Skip to content

Commit

Permalink
Merge pull request 'Refactor label and annotations containg metadata' (
Browse files Browse the repository at this point in the history
…#22) from feature/cleanup_labels into develop
  • Loading branch information
fmichielssen committed Feb 17, 2021
2 parents 8a11f5f + 5db2f7e commit 9051682
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 149 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>



<!-- Kubernetes -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,12 @@
*/
package eu.openanalytics.containerproxy.backend;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.inject.Inject;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.common.base.Charsets;
import eu.openanalytics.containerproxy.ContainerProxyApplication;
import eu.openanalytics.containerproxy.ContainerProxyException;
import eu.openanalytics.containerproxy.auth.IAuthenticationBackend;
import eu.openanalytics.containerproxy.backend.strategy.IProxyTargetMappingStrategy;
Expand All @@ -54,6 +37,26 @@
import eu.openanalytics.containerproxy.service.UserService;
import eu.openanalytics.containerproxy.spec.expression.ExpressionAwareContainerSpec;
import eu.openanalytics.containerproxy.spec.expression.SpecExpressionResolver;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;

import javax.inject.Inject;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public abstract class AbstractContainerBackend implements IContainerBackend {

Expand All @@ -70,9 +73,14 @@ public abstract class AbstractContainerBackend implements IContainerBackend {
protected static final String ENV_VAR_USER_GROUPS = "SHINYPROXY_USERGROUPS";
protected static final String ENV_VAR_REALM_ID = "SHINYPROXY_REALM_ID";

protected static final String LABEL_PROXY_ID = "openanalytics.eu/sp-proxy-id";
protected static final String LABEL_PROXY_SPEC_ID = "openanalytics.eu/sp-spec-id";
protected static final String LABEL_STARTUP_TIMESTAMP = "openanalytics.eu/sp-proxy-startup-timestamp";
protected static final String RUNTIME_LABEL_PROXY_ID = "openanalytics.eu/sp-proxy-id";
protected static final String RUNTIME_LABEL_USER_ID = "openanalytics.eu/sp-user-id";
protected static final String RUNTIME_LABEL_USER_GROUPS = "openanalytics.eu/sp-user-groups";
protected static final String RUNTIME_LABEL_REALM_ID = "openanalytics.eu/sp-realm-id";
protected static final String RUNTIME_LABEL_PROXY_SPEC_ID = "openanalytics.eu/sp-spec-id";
protected static final String RUNTIME_LABEL_STARTUP_TIMESTAMP = "openanalytics.eu/sp-proxy-startup-timestamp";
protected static final String RUNTIME_LABEL_PROXIED_APP = "openanalytics.eu/sp-proxied-app";
protected static final String RUNTIME_LABEL_INSTANCE = "openanalytics.eu/sp-instance";

protected final Logger log = LogManager.getLogger(getClass());

Expand All @@ -98,19 +106,31 @@ public abstract class AbstractContainerBackend implements IContainerBackend {
@Lazy
// Note: lazy needed to work around early initialization conflict
protected IAuthenticationBackend authBackend;


protected String realmId;

protected String instanceId = null;

@Override
public void initialize() throws ContainerProxyException {
// If this application runs as a container itself, things like port publishing can be omitted.
useInternalNetwork = Boolean.valueOf(getProperty(PROPERTY_INTERNAL_NETWORKING, "false"));
privileged = Boolean.valueOf(getProperty(PROPERTY_PRIVILEGED, "false"));
realmId = environment.getProperty("proxy.realm-id");
try {
instanceId = calculateInstanceId();
log.info("Hash of config is: " + instanceId);
} catch(Exception e) {
throw new RuntimeException("Cannot compute hash of config", e);
}
}

@Override
public void startProxy(Proxy proxy) throws ContainerProxyException {
proxy.setId(UUID.randomUUID().toString());
proxy.setStatus(ProxyStatus.Starting);

proxy.setStartupTimestamp(System.currentTimeMillis());

try {
doStartProxy(proxy);
} catch (Throwable t) {
Expand All @@ -123,7 +143,6 @@ public void startProxy(Proxy proxy) throws ContainerProxyException {
throw new ContainerProxyException("Container did not respond in time");
}

proxy.setStartupTimestamp(System.currentTimeMillis());
proxy.setStatus(ProxyStatus.Up);
}

Expand All @@ -132,19 +151,23 @@ protected void doStartProxy(Proxy proxy) throws Exception {
if (authBackend != null) authBackend.customizeContainer(spec);

// add labels need for App Recovery and maintenance
spec.addLabel(LABEL_PROXY_ID, proxy.getId());
spec.addLabel(LABEL_PROXY_SPEC_ID, proxy.getSpec().getId());
spec.addLabel(LABEL_STARTUP_TIMESTAMP, String.valueOf(proxy.getStartupTimestamp()));
spec.addRuntimeLabel(RUNTIME_LABEL_PROXIED_APP, true, "true");
spec.addRuntimeLabel(RUNTIME_LABEL_INSTANCE, true, instanceId);

spec.addRuntimeLabel(RUNTIME_LABEL_PROXY_ID, false, proxy.getId());
spec.addRuntimeLabel(RUNTIME_LABEL_PROXY_SPEC_ID, false, proxy.getSpec().getId());
if (realmId != null) {
spec.addRuntimeLabel(RUNTIME_LABEL_REALM_ID, false, realmId);
}
spec.addRuntimeLabel(RUNTIME_LABEL_USER_ID, false, proxy.getUserId());
String[] groups = userService.getGroups(userService.getCurrentAuth());
spec.addRuntimeLabel(RUNTIME_LABEL_USER_GROUPS, false, String.join(",", groups));
spec.addRuntimeLabel(RUNTIME_LABEL_STARTUP_TIMESTAMP, false, String.valueOf(proxy.getStartupTimestamp()));

ExpressionAwareContainerSpec eSpec = new ExpressionAwareContainerSpec(spec, proxy, expressionResolver);
Container c = startContainer(eSpec, proxy);
c.setSpec(spec);

// remove labels needed for App Recovery since they do not really belong to the spec
spec.removeLabel(LABEL_PROXY_ID);
spec.removeLabel(LABEL_PROXY_SPEC_ID);
spec.removeLabel(LABEL_STARTUP_TIMESTAMP);


proxy.getContainers().add(c);
}
}
Expand Down Expand Up @@ -241,4 +264,40 @@ protected boolean isUseInternalNetwork() {
protected boolean isPrivileged() {
return privileged;
}


/**
* Calculates a hash of the config file (i.e. application.yaml).
*/
private String calculateInstanceId() throws IOException, NoSuchAlgorithmException {
/**
* We need a hash of some "canonical" version of the config file.
* The hash should not change when e.g. comments are added to the file.
* Therefore we read the application.yml file into an Object and then
* dump it again into YAML. We also sort the keys of maps and properties so that
* the order does not matter for the resulting hash.
*/
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);

File file = Paths.get(ContainerProxyApplication.CONFIG_FILENAME).toFile();
if (!file.exists()) {
file = Paths.get(ContainerProxyApplication.CONFIG_DEMO_PROFILE).toFile();
}
if (!file.exists()) {
// this should only happen in tests
instanceId = "unknown-instance-id";
return instanceId;
}

Object parsedConfig = objectMapper.readValue(file, Object.class);
String canonicalConfigFile = objectMapper.writeValueAsString(parsedConfig);

MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
digest.update(canonicalConfigFile.getBytes(Charsets.UTF_8));
instanceId = String.format("%040x", new BigInteger(1, digest.digest()));
return instanceId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,14 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
Optional.ofNullable(spec.getDns()).ifPresent(dns -> hostConfigBuilder.dns(dns));
Optional.ofNullable(spec.getVolumes()).ifPresent(v -> hostConfigBuilder.binds(v));
hostConfigBuilder.privileged(isPrivileged() || spec.isPrivileged());


Map<String, String> labels = spec.getLabels();
spec.getRuntimeLabels().forEach((key, value) -> labels.put(key, value.getSecond()));

ContainerConfig containerConfig = ContainerConfig.builder()
.hostConfig(hostConfigBuilder.build())
.image(spec.getImage())
.labels(spec.getLabels())
.labels(labels)
.exposedPorts(portBindings.keySet())
.cmd(spec.getCmd())
.env(buildEnv(spec, proxy))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@

import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.*;

import com.spotify.docker.client.messages.mount.Mount;
import com.spotify.docker.client.messages.swarm.DnsConfig;
Expand Down Expand Up @@ -67,11 +63,13 @@ protected Container startContainer(ContainerSpec spec, Proxy proxy) throws Excep
.map(b -> b.split(":"))
.map(fromTo -> Mount.builder().source(fromTo[0]).target(fromTo[1]).type("bind").build())
.toArray(i -> new Mount[i]);
Map<String, String> labels = spec.getLabels();
spec.getRuntimeLabels().forEach((key, value) -> labels.put(key, value.getSecond()));

com.spotify.docker.client.messages.swarm.ContainerSpec containerSpec =
com.spotify.docker.client.messages.swarm.ContainerSpec.builder()
.image(spec.getImage())
.labels(spec.getLabels())
.labels(labels)
.command(spec.getCmd())
.env(buildEnv(spec, proxy))
.dnsConfig(DnsConfig.builder().nameServers(spec.getDns()).build())
Expand Down
Loading

0 comments on commit 9051682

Please sign in to comment.