Skip to content

Commit e1ffcbf

Browse files
committed
Merge pull request 'Make compatible with shinyproxy-operator' (#7) from feature/23003 into develop
Good point!
2 parents 55e24ab + 4636e04 commit e1ffcbf

4 files changed

Lines changed: 77 additions & 3 deletions

File tree

.settings/org.eclipse.jdt.core.prefs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ org.eclipse.jdt.core.compiler.debug.lineNumber=generate
88
org.eclipse.jdt.core.compiler.debug.localVariable=generate
99
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
1010
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
11+
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
1112
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
1213
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
14+
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
1315
org.eclipse.jdt.core.compiler.release=disabled
1416
org.eclipse.jdt.core.compiler.source=1.8

pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@
132132
<groupId>org.springframework.boot</groupId>
133133
<artifactId>spring-boot-starter-actuator</artifactId>
134134
</dependency>
135+
<dependency>
136+
<groupId>org.springframework.session</groupId>
137+
<artifactId>spring-session-data-redis</artifactId>
138+
</dependency>
139+
<dependency>
140+
<groupId>io.lettuce</groupId>
141+
<artifactId>lettuce-core</artifactId>
142+
</dependency>
135143

136144
<!-- Spring social security components -->
137145
<!-- Note: also includes an embedded datasource (H2) to remember social logins -->

src/main/java/eu/openanalytics/containerproxy/ContainerProxyApplication.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@
4848
@SpringBootApplication
4949
@ComponentScan("eu.openanalytics")
5050
public class ContainerProxyApplication {
51-
private static final String CONFIG_FILENAME = "application.yml";
52-
private static final String CONFIG_DEMO_PROFILE = "demo";
51+
public static final String CONFIG_FILENAME = "application.yml";
52+
public static final String CONFIG_DEMO_PROFILE = "demo";
5353

5454
@Inject
5555
private Environment environment;
@@ -123,6 +123,9 @@ private static void setDefaultProperties(SpringApplication app ) {
123123
Properties properties = new Properties();
124124
properties.put("management.health.ldap.enabled", false);
125125
properties.put("management.endpoint.health.probes.enabled", true);
126+
127+
// use in-memory session storage by default. Can be overwritten in application.yml
128+
properties.put("spring.session.store-type", "none");
126129
app.setDefaultProperties(properties);
127130
}
128131

src/main/java/eu/openanalytics/containerproxy/backend/kubernetes/KubernetesBackend.java

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121
package eu.openanalytics.containerproxy.backend.kubernetes;
2222

2323
import java.io.ByteArrayInputStream;
24+
import java.io.File;
2425
import java.io.IOException;
2526
import java.io.OutputStream;
27+
import java.math.BigInteger;
2628
import java.net.URI;
2729
import java.nio.file.Files;
2830
import java.nio.file.Path;
2931
import java.nio.file.Paths;
32+
import java.security.MessageDigest;
33+
import java.security.NoSuchAlgorithmException;
3034
import java.util.ArrayList;
3135
import java.util.Arrays;
3236
import java.util.Collections;
@@ -40,8 +44,16 @@
4044

4145
import org.apache.commons.io.IOUtils;
4246

47+
import com.fasterxml.jackson.core.JsonParseException;
48+
import com.fasterxml.jackson.databind.JsonMappingException;
49+
import com.fasterxml.jackson.databind.MapperFeature;
50+
import com.fasterxml.jackson.databind.ObjectMapper;
51+
import com.fasterxml.jackson.databind.SerializationFeature;
52+
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
53+
import com.google.common.base.Charsets;
4354
import com.google.common.base.Splitter;
4455

56+
import eu.openanalytics.containerproxy.ContainerProxyApplication;
4557
import eu.openanalytics.containerproxy.ContainerProxyException;
4658
import eu.openanalytics.containerproxy.backend.AbstractContainerBackend;
4759
import eu.openanalytics.containerproxy.model.runtime.Container;
@@ -97,6 +109,10 @@ public class KubernetesBackend extends AbstractContainerBackend {
97109

98110
private static final String SECRET_KEY_REF = "secretKeyRef";
99111

112+
private static final String LABEL_PROXIED_APP = "openanalytics.eu/containerproxy-proxied-app";
113+
private static final String LABEL_INSTANCE = "openanalytics.eu/sp-instance";
114+
115+
100116
@Inject
101117
private PodPatcher podPatcher;
102118

@@ -122,6 +138,11 @@ public void initialize() throws ContainerProxyException {
122138
}
123139

124140
kubeClient = new DefaultKubernetesClient(configBuilder.build());
141+
try {
142+
log.info("Hash of config is: " + getInstanceId());
143+
} catch(Exception e) {
144+
throw new RuntimeException("Cannot compute hash of config", e);
145+
}
125146
}
126147

127148
public void initialize(KubernetesClient client) {
@@ -222,7 +243,9 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
222243
.withName("sp-pod-" + container.getId())
223244
.addToLabels(spec.getLabels())
224245
.addToLabels("app", container.getId())
225-
.endMetadata();
246+
.addToLabels(LABEL_INSTANCE, getInstanceId())
247+
.addToLabels(LABEL_PROXIED_APP, "true")
248+
.endMetadata();
226249

227250
PodSpec podSpec = new PodSpec();
228251
podSpec.setContainers(Collections.singletonList(containerBuilder.build()));
@@ -262,6 +285,8 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
262285
.withKind("Service")
263286
.withNewMetadata()
264287
.withName("sp-service-" + container.getId())
288+
.addToLabels(LABEL_INSTANCE, getInstanceId())
289+
.addToLabels(LABEL_PROXIED_APP, "true")
265290
.endMetadata()
266291
.withNewSpec()
267292
.addToSelector("app", container.getId())
@@ -406,6 +431,42 @@ public BiConsumer<OutputStream, OutputStream> getOutputAttacher(Proxy proxy) {
406431
protected String getPropertyPrefix() {
407432
return PROPERTY_PREFIX;
408433
}
434+
435+
private String instanceId = null;
436+
437+
/**
438+
* Calculates a hash of the config file (i.e. application.yaml).
439+
*/
440+
private String getInstanceId() throws JsonParseException, JsonMappingException, IOException, NoSuchAlgorithmException {
441+
if (instanceId != null) {
442+
return instanceId;
443+
}
444+
445+
/**
446+
* We need a hash of some "canonical" version of the config file.
447+
* The hash should not change when e.g. comments are added to the file.
448+
* Therefore we read the application.yml file into an Object and then
449+
* dump it again into YAML. We also sort the keys of maps and properties so that
450+
* the order does not matter for the resulting hash.
451+
*/
452+
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
453+
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
454+
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
455+
456+
File file = Paths.get(ContainerProxyApplication.CONFIG_FILENAME).toFile();
457+
if (!file.exists()) {
458+
file = Paths.get(ContainerProxyApplication.CONFIG_DEMO_PROFILE).toFile();
459+
}
460+
461+
Object parsedConfig = objectMapper.readValue(file, Object.class);
462+
String canonicalConfigFile = objectMapper.writeValueAsString(parsedConfig);
463+
464+
MessageDigest digest = MessageDigest.getInstance("SHA-1");
465+
digest.reset();
466+
digest.update(canonicalConfigFile.getBytes(Charsets.UTF_8));
467+
instanceId = String.format("%040x", new BigInteger(1, digest.digest()));
468+
return instanceId;
469+
}
409470

410471

411472
}

0 commit comments

Comments
 (0)