You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@heron.apache.org by ni...@apache.org on 2021/07/09 03:14:37 UTC

[incubator-heron] branch nicknezis/k8s-secrets created (now 7604fcb)

This is an automated email from the ASF dual-hosted git repository.

nicknezis pushed a change to branch nicknezis/k8s-secrets
in repository https://gitbox.apache.org/repos/asf/incubator-heron.git.


      at 7604fcb  Added support for Secrets and SecretKeyRefs

This branch includes the following new commits:

     new 7604fcb  Added support for Secrets and SecretKeyRefs

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[incubator-heron] 01/01: Added support for Secrets and SecretKeyRefs

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nicknezis pushed a commit to branch nicknezis/k8s-secrets
in repository https://gitbox.apache.org/repos/asf/incubator-heron.git

commit 7604fcbe8a079c731856c32a6ddb05f7c2f4f82f
Author: Nicholas Nezis <ni...@gmail.com>
AuthorDate: Thu Jul 8 23:14:25 2021 -0400

    Added support for Secrets and SecretKeyRefs
---
 .../scheduler/kubernetes/KubernetesContext.java    | 71 +++++++++++++---------
 .../heron/scheduler/kubernetes/V1Controller.java   | 47 ++++++++++++++
 .../heron/scheduler/kubernetes/VolumesTests.java   | 16 ++---
 3 files changed, 97 insertions(+), 37 deletions(-)

diff --git a/heron/schedulers/src/java/org/apache/heron/scheduler/kubernetes/KubernetesContext.java b/heron/schedulers/src/java/org/apache/heron/scheduler/kubernetes/KubernetesContext.java
index 4074fbc..71a660a 100644
--- a/heron/schedulers/src/java/org/apache/heron/scheduler/kubernetes/KubernetesContext.java
+++ b/heron/schedulers/src/java/org/apache/heron/scheduler/kubernetes/KubernetesContext.java
@@ -59,44 +59,49 @@ public final class KubernetesContext extends Context {
   public static final String KUBERNETES_RESOURCE_REQUEST_MODE =
           "heron.kubernetes.resource.request.mode";
 
-  public static final String HERON_KUBERNETES_VOLUME_NAME = "heron.kubernetes.volume.name";
-  public static final String HERON_KUBERNETES_VOLUME_TYPE = "heron.kubernetes.volume.type";
+  public static final String KUBERNETES_VOLUME_NAME = "heron.kubernetes.volume.name";
+  public static final String KUBERNETES_VOLUME_TYPE = "heron.kubernetes.volume.type";
 
 
   // HostPath volume keys
   // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
-  public static final String HERON_KUBERNETES_VOLUME_HOSTPATH_PATH =
+  public static final String KUBERNETES_VOLUME_HOSTPATH_PATH =
       "heron.kubernetes.volume.hostPath.path";
 
   // nfs volume keys
   // https://kubernetes.io/docs/concepts/storage/volumes/#nfs
-  public static final String HERON_KUBERNETES_VOLUME_NFS_PATH =
+  public static final String KUBERNETES_VOLUME_NFS_PATH =
       "heron.kubernetes.volume.nfs.path";
-  public static final String HERON_KUBERNETES_VOLUME_NFS_SERVER =
+  public static final String KUBERNETES_VOLUME_NFS_SERVER =
       "heron.kubernetes.volume.nfs.server";
 
   // awsElasticBlockStore volume keys
   // https://kubernetes.io/docs/concepts/storage/volumes/#awselasticblockstore
-  public static final String HERON_KUBERNETES_VOLUME_AWS_EBS_VOLUME_ID =
+  public static final String KUBERNETES_VOLUME_AWS_EBS_VOLUME_ID =
       "heron.kubernetes.volume.awsElasticBlockStore.volumeID";
-  public static final String HERON_KUBERNETES_VOLUME_AWS_EBS_FS_TYPE =
+  public static final String KUBERNETES_VOLUME_AWS_EBS_FS_TYPE =
       "heron.kubernetes.volume.awsElasticBlockStore.fsType";
 
   // container mount volume mount keys
-  public static final String HERON_KUBERNETES_CONTAINER_VOLUME_MOUNT_NAME =
+  public static final String KUBERNETES_CONTAINER_VOLUME_MOUNT_NAME =
       "heron.kubernetes.container.volumeMount.name";
-  public static final String HERON_KUBERNETES_CONTAINER_VOLUME_MOUNT_PATH =
+  public static final String KUBERNETES_CONTAINER_VOLUME_MOUNT_PATH =
       "heron.kubernetes.container.volumeMount.path";
 
-  public static final String HERON_KUBERNETES_POD_ANNOTATION =
+  public static final String KUBERNETES_POD_ANNOTATION_PREFIX =
       "heron.kubernetes.pod.annotation.";
-  public static final String HERON_KUBERNETES_SERVICE_ANNOTATION =
+  public static final String KUBERNETES_SERVICE_ANNOTATION_PREFIX =
       "heron.kubernetes.service.annotation.";
-  public static final String HERON_KUBERNETES_POD_LABEL =
-          "heron.kubernetes.pod.label.";
-  public static final String HERON_KUBERNETES_SERVICE_LABEL =
-          "heron.kubernetes.service.label.";
-
+  public static final String KUBERNETES_POD_LABEL_PREFIX =
+      "heron.kubernetes.pod.label.";
+  public static final String KUBERNETES_SERVICE_LABEL_PREFIX =
+      "heron.kubernetes.service.label.";
+  // heron.kubernetes.pod.secret.heron-secret=/etc/secrets
+  public static final String KUBERNETES_POD_SECRET_PREFIX =
+      "heron.kubernetes.pod.secret.";
+  // heron.kubernetes.pod.secretKeyRef.ENV_NAME=name:key
+  public static final String KUBERNETES_POD_SECRET_KEY_REF_PREFIX =
+      "heron.kubernetes.pod.secretKeyRef.";
 
   private KubernetesContext() {
   }
@@ -128,31 +133,31 @@ public final class KubernetesContext extends Context {
   }
 
   static String getVolumeType(Config config) {
-    return config.getStringValue(HERON_KUBERNETES_VOLUME_TYPE);
+    return config.getStringValue(KUBERNETES_VOLUME_TYPE);
   }
 
   static String getVolumeName(Config config) {
-    return config.getStringValue(HERON_KUBERNETES_VOLUME_NAME);
+    return config.getStringValue(KUBERNETES_VOLUME_NAME);
   }
 
   static String getHostPathVolumePath(Config config) {
-    return config.getStringValue(HERON_KUBERNETES_VOLUME_HOSTPATH_PATH);
+    return config.getStringValue(KUBERNETES_VOLUME_HOSTPATH_PATH);
   }
 
   static String getNfsVolumePath(Config config) {
-    return config.getStringValue(HERON_KUBERNETES_VOLUME_NFS_PATH);
+    return config.getStringValue(KUBERNETES_VOLUME_NFS_PATH);
   }
 
   static String getNfsServer(Config config) {
-    return config.getStringValue(HERON_KUBERNETES_VOLUME_NFS_SERVER);
+    return config.getStringValue(KUBERNETES_VOLUME_NFS_SERVER);
   }
 
   static String getAwsEbsVolumeId(Config config) {
-    return config.getStringValue(HERON_KUBERNETES_VOLUME_AWS_EBS_VOLUME_ID);
+    return config.getStringValue(KUBERNETES_VOLUME_AWS_EBS_VOLUME_ID);
   }
 
   static String getAwsEbsFsType(Config config) {
-    return config.getStringValue(HERON_KUBERNETES_VOLUME_AWS_EBS_FS_TYPE);
+    return config.getStringValue(KUBERNETES_VOLUME_AWS_EBS_FS_TYPE);
   }
 
   static boolean hasVolume(Config config) {
@@ -160,27 +165,35 @@ public final class KubernetesContext extends Context {
   }
 
   static String getContainerVolumeName(Config config) {
-    return config.getStringValue(HERON_KUBERNETES_CONTAINER_VOLUME_MOUNT_NAME);
+    return config.getStringValue(KUBERNETES_CONTAINER_VOLUME_MOUNT_NAME);
   }
 
   static String getContainerVolumeMountPath(Config config) {
-    return config.getStringValue(HERON_KUBERNETES_CONTAINER_VOLUME_MOUNT_PATH);
+    return config.getStringValue(KUBERNETES_CONTAINER_VOLUME_MOUNT_PATH);
   }
 
   public static Map<String, String> getPodLabels(Config config) {
-    return getConfigItemsByPrefix(config, HERON_KUBERNETES_POD_LABEL);
+    return getConfigItemsByPrefix(config, KUBERNETES_POD_LABEL_PREFIX);
   }
 
   public static Map<String, String> getServiceLabels(Config config) {
-    return getConfigItemsByPrefix(config, HERON_KUBERNETES_SERVICE_LABEL);
+    return getConfigItemsByPrefix(config, KUBERNETES_SERVICE_LABEL_PREFIX);
   }
 
   public static Map<String, String> getPodAnnotations(Config config) {
-    return getConfigItemsByPrefix(config, HERON_KUBERNETES_POD_ANNOTATION);
+    return getConfigItemsByPrefix(config, KUBERNETES_POD_ANNOTATION_PREFIX);
   }
 
   public static Map<String, String> getServiceAnnotations(Config config) {
-    return getConfigItemsByPrefix(config, HERON_KUBERNETES_SERVICE_ANNOTATION);
+    return getConfigItemsByPrefix(config, KUBERNETES_SERVICE_ANNOTATION_PREFIX);
+  }
+
+  public static Map<String, String> getPodSecretsToMount(Config config) {
+    return getConfigItemsByPrefix(config, KUBERNETES_POD_SECRET_PREFIX);
+  }
+
+  public static Map<String, String> getPodSecretKeyRefs(Config config) {
+    return getConfigItemsByPrefix(config, KUBERNETES_POD_SECRET_KEY_REF_PREFIX);
   }
 
   static Set<String> getConfigKeys(Config config, String keyPrefix) {
diff --git a/heron/schedulers/src/java/org/apache/heron/scheduler/kubernetes/V1Controller.java b/heron/schedulers/src/java/org/apache/heron/scheduler/kubernetes/V1Controller.java
index 2bf815b..35564e7 100644
--- a/heron/schedulers/src/java/org/apache/heron/scheduler/kubernetes/V1Controller.java
+++ b/heron/schedulers/src/java/org/apache/heron/scheduler/kubernetes/V1Controller.java
@@ -52,6 +52,7 @@ import io.kubernetes.client.openapi.apis.CoreV1Api;
 import io.kubernetes.client.openapi.models.V1Container;
 import io.kubernetes.client.openapi.models.V1ContainerPort;
 import io.kubernetes.client.openapi.models.V1EnvVar;
+import io.kubernetes.client.openapi.models.V1EnvVarBuilder;
 import io.kubernetes.client.openapi.models.V1EnvVarSource;
 import io.kubernetes.client.openapi.models.V1LabelSelector;
 import io.kubernetes.client.openapi.models.V1ObjectFieldSelector;
@@ -59,6 +60,7 @@ import io.kubernetes.client.openapi.models.V1ObjectMeta;
 import io.kubernetes.client.openapi.models.V1PodSpec;
 import io.kubernetes.client.openapi.models.V1PodTemplateSpec;
 import io.kubernetes.client.openapi.models.V1ResourceRequirements;
+import io.kubernetes.client.openapi.models.V1SecretVolumeSourceBuilder;
 import io.kubernetes.client.openapi.models.V1Service;
 import io.kubernetes.client.openapi.models.V1ServiceSpec;
 import io.kubernetes.client.openapi.models.V1StatefulSet;
@@ -444,6 +446,8 @@ public class V1Controller extends KubernetesController {
 
     addVolumesIfPresent(podSpec);
 
+    mountSecretsAsVolumes(podSpec);
+
     return podSpec;
   }
 
@@ -473,6 +477,25 @@ public class V1Controller extends KubernetesController {
     }
   }
 
+  private void mountSecretsAsVolumes(V1PodSpec podSpec) {
+    final Config config = getConfiguration();
+    final Map<String, String> secrets = KubernetesContext.getPodSecretsToMount(config);
+    for (Map.Entry<String, String> secret : secrets.entrySet()) {
+      final V1VolumeMount mount = new V1VolumeMount()
+              .name(secret.getKey())
+              .mountPath(secret.getValue());
+      final V1Volume secretVolume = new V1Volume()
+              .name(secret.getKey())
+              .secret(new V1SecretVolumeSourceBuilder()
+                      .withSecretName(secret.getKey())
+                      .build());
+      podSpec.addVolumesItem(secretVolume);
+      for (V1Container container : podSpec.getContainers()) {
+        container.addVolumeMountsItem(mount);
+      }
+    }
+  }
+
   private V1Container getContainer(List<String> executorCommand, Resource resource,
       int numberOfInstances) {
     final Config configuration = getConfiguration();
@@ -502,6 +525,7 @@ public class V1Controller extends KubernetesController {
                 .fieldPath(KubernetesConstants.POD_NAME)));
     container.setEnv(Arrays.asList(envVarHost, envVarPodName));
 
+    setSecretKeyRefs(container);
 
     // set container resources
     final V1ResourceRequirements resourceRequirements = new V1ResourceRequirements();
@@ -573,6 +597,29 @@ public class V1Controller extends KubernetesController {
     }
   }
 
+  private void setSecretKeyRefs(V1Container container) {
+    final Config config = getConfiguration();
+    final Map<String, String> podSecretKeyRefs = KubernetesContext.getPodSecretKeyRefs(config);
+    for (Map.Entry<String, String> secret : podSecretKeyRefs.entrySet()) {
+      final String[] keyRefParts = secret.getValue().split(":");
+      if (keyRefParts.length != 2) {
+        LOG.log(Level.SEVERE, "SecretKeyRef must be in the form name:key. <" + keyRefParts + ">");
+        throw new TopologyRuntimeManagementException("SecretKeyRef must be in the form name:key. <" + keyRefParts + ">");
+      }
+      String name = keyRefParts[0];
+      String key = keyRefParts[1];
+      V1EnvVar envVar = new V1EnvVarBuilder()
+              .withName(secret.getKey())
+              .withNewValueFrom()
+                .withNewSecretKeyRef()
+                  .withKey(key)
+                  .withName(name)
+                .endSecretKeyRef()
+              .endValueFrom().build();
+      container.addEnvItem(envVar);
+    }
+  }
+
   public static double roundDecimal(double value, int places) {
     double scale = Math.pow(10, places);
     return Math.round(value * scale) / scale;
diff --git a/heron/schedulers/tests/java/org/apache/heron/scheduler/kubernetes/VolumesTests.java b/heron/schedulers/tests/java/org/apache/heron/scheduler/kubernetes/VolumesTests.java
index 95f82f4..401c97f 100644
--- a/heron/schedulers/tests/java/org/apache/heron/scheduler/kubernetes/VolumesTests.java
+++ b/heron/schedulers/tests/java/org/apache/heron/scheduler/kubernetes/VolumesTests.java
@@ -39,8 +39,8 @@ public class VolumesTests {
   public void testHostPathVolume() {
     final String path = "/test/dir1";
     final Config config = Config.newBuilder()
-        .put(KubernetesContext.HERON_KUBERNETES_VOLUME_TYPE, "hostPath")
-        .put(KubernetesContext.HERON_KUBERNETES_VOLUME_HOSTPATH_PATH, path)
+        .put(KubernetesContext.KUBERNETES_VOLUME_TYPE, "hostPath")
+        .put(KubernetesContext.KUBERNETES_VOLUME_HOSTPATH_PATH, path)
         .build();
 
     final V1Volume volume = Volumes.get().create(config);
@@ -54,9 +54,9 @@ public class VolumesTests {
     final String path = "/test/dir1";
     final String server = "10.10.10.10";
     final Config config = Config.newBuilder()
-        .put(KubernetesContext.HERON_KUBERNETES_VOLUME_TYPE, "nfs")
-        .put(KubernetesContext.HERON_KUBERNETES_VOLUME_NFS_PATH, path)
-        .put(KubernetesContext.HERON_KUBERNETES_VOLUME_NFS_SERVER, server)
+        .put(KubernetesContext.KUBERNETES_VOLUME_TYPE, "nfs")
+        .put(KubernetesContext.KUBERNETES_VOLUME_NFS_PATH, path)
+        .put(KubernetesContext.KUBERNETES_VOLUME_NFS_SERVER, server)
         .build();
 
     final V1Volume volume = Volumes.get().create(config);
@@ -71,9 +71,9 @@ public class VolumesTests {
     final String volumeId = "aws-ebs-1";
     final String fsType = "ext4";
     final Config config = Config.newBuilder()
-        .put(KubernetesContext.HERON_KUBERNETES_VOLUME_TYPE, "awsElasticBlockStore")
-        .put(KubernetesContext.HERON_KUBERNETES_VOLUME_AWS_EBS_VOLUME_ID, volumeId)
-        .put(KubernetesContext.HERON_KUBERNETES_VOLUME_AWS_EBS_FS_TYPE, fsType)
+        .put(KubernetesContext.KUBERNETES_VOLUME_TYPE, "awsElasticBlockStore")
+        .put(KubernetesContext.KUBERNETES_VOLUME_AWS_EBS_VOLUME_ID, volumeId)
+        .put(KubernetesContext.KUBERNETES_VOLUME_AWS_EBS_FS_TYPE, fsType)
         .build();
 
     final V1Volume volume = Volumes.get().create(config);