2121package eu .openanalytics .containerproxy .backend .kubernetes ;
2222
2323import java .io .ByteArrayInputStream ;
24+ import java .io .File ;
2425import java .io .IOException ;
2526import java .io .OutputStream ;
27+ import java .math .BigInteger ;
2628import java .net .URI ;
2729import java .nio .file .Files ;
2830import java .nio .file .Path ;
2931import java .nio .file .Paths ;
32+ import java .security .MessageDigest ;
33+ import java .security .NoSuchAlgorithmException ;
3034import java .util .ArrayList ;
3135import java .util .Arrays ;
3236import java .util .Collections ;
4044
4145import 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 ;
4354import com .google .common .base .Splitter ;
4455
56+ import eu .openanalytics .containerproxy .ContainerProxyApplication ;
4557import eu .openanalytics .containerproxy .ContainerProxyException ;
4658import eu .openanalytics .containerproxy .backend .AbstractContainerBackend ;
4759import 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+ e .printStackTrace ();
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