diff --git a/sermant-injector/src/main/java/com/huaweicloud/sermant/injection/controller/SermantInjectorController.java b/sermant-injector/src/main/java/com/huaweicloud/sermant/injection/controller/SermantInjectorController.java index f5c3a36634..83b45fb2ef 100644 --- a/sermant-injector/src/main/java/com/huaweicloud/sermant/injection/controller/SermantInjectorController.java +++ b/sermant-injector/src/main/java/com/huaweicloud/sermant/injection/controller/SermantInjectorController.java @@ -70,6 +70,10 @@ public class SermantInjectorController { private static final String VOLUMES_PATH = "volumes"; + private static final String METADATA_PATH = "metadata"; + + private static final String ANNOTATION_PATH = "annotations"; + private static final String VOLUMES_INJECT_PATH = PATH_SEPARATOR + SPEC_PATH + PATH_SEPARATOR + VOLUMES_PATH; private static final String REQUEST_PATH = "request"; @@ -174,6 +178,8 @@ public class SermantInjectorController { private static final String SERMANT_SERVICE_CENTER_TYPE_KEY = "REGISTER_SERVICE_REGISTERTYPE"; + private static final String SERMANT_ENV_PREFIX = "env.sermant.io/"; + @Autowired private ObjectMapper om; @@ -240,6 +246,11 @@ private Optional inject(ObjectNode body) { // 缓存容器的env setEnv(containersNode, containerEnv); + // 根据labels计算需要新增env + Map annotationEnv = new HashMap<>(); + JsonNode annotationNode = body.path(REQUEST_PATH).path(OBJECT_PATH).path(METADATA_PATH).path(ANNOTATION_PATH); + setEnvByMap(annotationNode, annotationEnv); + // 建一个json节点 ArrayNode arrayNode = om.createArrayNode(); @@ -265,7 +276,7 @@ private Optional inject(ObjectNode body) { injectEnvFrom(arrayNode, env, containerNode, containerPath); // 向容器新增env节点 - injectEnv(arrayNode, env, containerNode, containerPath); + injectEnv(arrayNode, env, containerNode, containerPath, annotationEnv); // 向容器新增volumeMounts节点 injectVolumeMounts(arrayNode, env, containerNode, containerPath); @@ -274,6 +285,27 @@ private Optional inject(ObjectNode body) { return Optional.of(arrayNode.toString()); } + /** + * 根据labels/annotations计算需要增加的环境变量,如下以labels为例: + * labels: + * env.sermant.io/[key1]:[value1] + * env.sermant.io/[key2]:[value2] + * 则在环境变量map中添加 key1:value1 和 key2:value2,共两个环境变量 + * 本函数本真不强制是否使用labels还是annotations,只需要srcNode代表label/annotations下的map即可。 + */ + private void setEnvByMap(JsonNode srcNode, Map tgtEnv) { + Iterator labelIter = srcNode.fieldNames(); + int prefexLength = SERMANT_ENV_PREFIX.length(); + while (labelIter.hasNext()) { + String labelName = labelIter.next(); + if (labelName.startsWith(SERMANT_ENV_PREFIX)) { + String envKey = labelName.substring(prefexLength); + String envValue = srcNode.findValue(labelName).textValue(); + tgtEnv.put(envKey, envValue); + } + } + } + private void setEnv(JsonNode containersPath, Map> containerEnv) { Iterator containerIterator = containersPath.elements(); int index = 0; @@ -416,7 +448,8 @@ private void injectEnvFrom(ArrayNode arrayNode, Map env, JsonNod configMapRefNode.put(NAME_KEY, configMap); } - private void injectEnv(ArrayNode arrayNode, Map env, JsonNode containerNode, String containerPath) { + private void injectEnv(ArrayNode arrayNode, Map env, JsonNode containerNode, String containerPath, + Map annotationEnv) { // 覆盖容器的env节点 ObjectNode envNode = arrayNode.addObject(); envNode.put(JSON_OPERATION_KEY, JSON_OPERATION_ADD); @@ -456,6 +489,12 @@ private void injectEnv(ArrayNode arrayNode, Map env, JsonNode co if (!StringUtils.hasText(env.get(SERMANT_SERVICE_CENTER_TYPE_KEY))) { addEnv(envArray, SERMANT_SERVICE_CENTER_TYPE_KEY, serviceType); } + + // 增加通过在 /metadata/annotations 上指定的env + // 这里默认,如果annotations中有新的设置,则覆盖原有的env设置。 + for (Map.Entry entry : annotationEnv.entrySet()) { + addEnv(envArray, entry.getKey(), entry.getValue()); + } } private void injectVolumeMounts(ArrayNode arrayNode, Map env, JsonNode containerNode, diff --git a/sermant-injector/src/test/java/com/huaweicloud/sermant/injection/controller/SermantInjectorControllerTest.java b/sermant-injector/src/test/java/com/huaweicloud/sermant/injection/controller/SermantInjectorControllerTest.java index 0e276993d4..5e8f85798e 100644 --- a/sermant-injector/src/test/java/com/huaweicloud/sermant/injection/controller/SermantInjectorControllerTest.java +++ b/sermant-injector/src/test/java/com/huaweicloud/sermant/injection/controller/SermantInjectorControllerTest.java @@ -12,6 +12,9 @@ import org.springframework.boot.test.context.SpringBootTest; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Iterator; /** * controller单元测试 @@ -33,5 +36,25 @@ public void injectSermant() throws IOException { Assertions.assertNotNull(response); Assertions.assertNotNull(response.getResponse()); Assertions.assertTrue(response.getResponse().isAllowed()); + + // Test Annotations environment feature. + byte[] decodedBytes = Base64.getDecoder().decode(response.getResponse().getPatch()); + String decodedString = new String(decodedBytes); + JsonNode rs = mapper.readTree(decodedString); + Iterator iter = rs.elements(); + while ( iter.hasNext() ) + { + JsonNode node = iter.next(); + if (node.get("path").asText().equals("/spec/containers/0/env")) + { + JsonNode jn = node.findValue("value"); + Assertions.assertTrue(jn.toString().indexOf("key1") > 0); + break; + } + if ( iter.hasNext() == false ) + { + Assertions.fail("should not get here"); + } + } } } \ No newline at end of file diff --git a/sermant-injector/src/test/resources/test.json b/sermant-injector/src/test/resources/test.json index 55f3e597f3..1682919751 100644 --- a/sermant-injector/src/test/resources/test.json +++ b/sermant-injector/src/test/resources/test.json @@ -2,7 +2,7 @@ "kind": "AdmissionReview", "apiVersion": "admission.k8s.io/v1beta1", "request": { - "uid": "57d4d575-4a5d-444a-8b21-b325d3d11b45", + "uid": "c57327fb-27c5-4d26-bc49-4732569f3c1d", "kind": { "group": "", "version": "v1", @@ -23,108 +23,202 @@ "version": "v1", "resource": "pods" }, - "namespace": "mesh", + "namespace": "default", "operation": "CREATE", "userInfo": { - "username": "foo", - "uid": "f8b49463-63cd-434e-865d-1a2f8679ccec", + "username": "system:serviceaccount:kube-system:replicaset-controller", + "uid": "584907d5-656e-4d6f-969c-90569b29b56e", "groups": [ - "system:bar", - "system:foo", - "system:bar" + "system:serviceaccounts", + "system:serviceaccounts:kube-system", + "system:authenticated" ] }, "object": { "kind": "Pod", "apiVersion": "v1", "metadata": { - "generateName": "bar-test-f8b49463-", + "generateName": "testapp-7c8756758c-", "creationTimestamp": null, "labels": { - "app": "bar-test", - "pod-template-hash": "f8b49463", + "app": "testapp", + "pod-template-hash": "7c8756758c", "sermant-injection": "enabled" }, "annotations": { - "kubernetes.io/psp": "psp-global" + "kubernetes.io/psp": "psp-global", + "env.sermant.io/key1": "value1", + "env.sermant.io/key2": "value2" }, "ownerReferences": [ { "apiVersion": "apps/v1", "kind": "ReplicaSet", - "name": "bar-test-f8b49463", - "uid": "4a28cea0-2a40-4e22-95a5-f61d2a5878bd", + "name": "testapp-7c8756758c", + "uid": "aa70b547-02ac-4d23-ae49-0305aad0c7a2", "controller": true, "blockOwnerDeletion": true } + ], + "managedFields": [ + { + "manager": "kube-controller-manager", + "operation": "Update", + "apiVersion": "v1", + "time": "2022-12-22T12:28:22Z", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:generateName": {}, + "f:labels": { + ".": {}, + "f:app": {}, + "f:env.sermant.io/key1": {}, + "f:env.sermant.io/key2": {}, + "f:pod-template-hash": {}, + "f:sermant-injection": {} + }, + "f:ownerReferences": { + ".": {}, + "k:{\"uid\":\"aa70b547-02ac-4d23-ae49-0305aad0c7a2\"}": { + ".": {}, + "f:apiVersion": {}, + "f:blockOwnerDeletion": {}, + "f:controller": {}, + "f:kind": {}, + "f:name": {}, + "f:uid": {} + } + } + }, + "f:spec": { + "f:containers": { + "k:{\"name\":\"testapp\"}": { + ".": {}, + "f:env": { + ".": {}, + "k:{\"name\":\"az\"}": { + ".": {}, + "f:name": {}, + "f:value": {} + }, + "k:{\"name\":\"dubbo.registry.address\"}": { + ".": {}, + "f:name": {}, + "f:value": {} + }, + "k:{\"name\":\"sermant_springboot_registry_lb_registryAddress\"}": { + ".": {}, + "f:name": {}, + "f:value": {} + } + }, + "f:image": {}, + "f:imagePullPolicy": {}, + "f:name": {}, + "f:ports": { + ".": {}, + "k:{\"containerPort\":8104,\"protocol\":\"TCP\"}": { + ".": {}, + "f:containerPort": {}, + "f:protocol": {} + } + }, + "f:resources": {}, + "f:terminationMessagePath": {}, + "f:terminationMessagePolicy": {} + } + }, + "f:dnsPolicy": {}, + "f:enableServiceLinks": {}, + "f:imagePullSecrets": { + ".": {}, + "k:{\"name\":\"foo-secret\"}": { + ".": {}, + "f:name": {} + } + }, + "f:restartPolicy": {}, + "f:schedulerName": {}, + "f:securityContext": {}, + "f:terminationGracePeriodSeconds": {} + } + } + } ] }, "spec": { "volumes": [ { - "name": "foo", - "secret": { - "secretName": "bar" + "name": "kube-api-access-sd2sx", + "projected": { + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "name": "kube-root-ca.crt", + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ] + } + }, + { + "downwardAPI": { + "items": [ + { + "path": "namespace", + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + ] + } + } + ] } } ], "containers": [ { - "name": "foo-test", - "image": "bar-test:1.0.1", + "name": "testapp", + "image": "swr.cn-north-5.myhuaweicloud.com/pengyuyi/testapp:3.10.0", "ports": [ { - "containerPort": 100, - "protocol": "TCP" - }, - { - "containerPort": 101, - "protocol": "TCP" - }, - { - "containerPort": 102, + "containerPort": 8104, "protocol": "TCP" } ], "env": [ { - "name": "SERVICECOMB.SERVICE.ENABLEDUBBOREGISTER", - "value": "true" - }, - { - "name": "SERVICECOMB.SERVICE.ENABLESPRINGREGISTER", - "value": "false" - }, - { - "name": "SERVICECOMB.SERVICE.SSLENABLED", - "value": "false" + "name": "dubbo.registry.address", + "value": "zookeeper://192.168.1.1:1111" }, { - "name": "SERVICECOMB.SERVICE.OPENMIGRATION", - "value": "false" + "name": "az", + "value": "sz" }, { - "name": "DYNAMIC.CONFIG.PLUGIN.ENABLEDYNAMICCONFIG", - "value": "false" + "name": "sermant_springboot_registry_lb_registryAddress", + "value": "192.168.1.1:1111" } ], "resources": {}, "volumeMounts": [ { - "name": "bar", + "name": "kube-api-access-sd2sx", "readOnly": true, - "mountPath": "foo" + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" } ], - "lifecycle": { - "preStop": { - "exec": { - "command": [ - "echo \"1111111111111111111111111111\"" - ] - } - } - }, "terminationMessagePath": "/dev/termination-log", "terminationMessagePolicy": "File", "imagePullPolicy": "IfNotPresent" @@ -133,31 +227,44 @@ "restartPolicy": "Always", "terminationGracePeriodSeconds": 30, "dnsPolicy": "ClusterFirst", - "serviceAccountName": "foo", - "serviceAccount": "bar", + "serviceAccountName": "default", + "serviceAccount": "default", "securityContext": {}, "imagePullSecrets": [ { - "name": "foo" + "name": "foo-secret" } ], - "schedulerName": "bar", + "schedulerName": "default-scheduler", "tolerations": [ { - "key": "bar", + "key": "node.kubernetes.io/not-ready", "operator": "Exists", "effect": "NoExecute", "tolerationSeconds": 300 }, { - "key": "foo", + "key": "node.kubernetes.io/unreachable", "operator": "Exists", "effect": "NoExecute", "tolerationSeconds": 300 } ], "priority": 0, - "enableServiceLinks": true + "dnsConfig": { + "options": [ + { + "name": "single-request-reopen", + "value": "" + }, + { + "name": "timeout", + "value": "2" + } + ] + }, + "enableServiceLinks": true, + "preemptionPolicy": "PreemptLowerPriority" }, "status": {} },