From ed3847ae2c7330f98325241d270085e5d1442cb4 Mon Sep 17 00:00:00 2001 From: dileepng <114957777+dileepng@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:47:42 -0700 Subject: [PATCH] adding sidecarEnvJson annotation (#726) * adding customSidecarEnvJson annotation --- pkg/inject/constants.go | 8 ++++ pkg/inject/envoy.go | 32 ++++++++++++++ pkg/inject/envoy_test.go | 93 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) diff --git a/pkg/inject/constants.go b/pkg/inject/constants.go index 1c5973d0..449c32c4 100644 --- a/pkg/inject/constants.go +++ b/pkg/inject/constants.go @@ -56,6 +56,14 @@ const ( // AppMeshEnvAnnotation = "appmesh.k8s.aws/sidecarEnv" + // AppMeshEnvJsonAnnotation which is similar AppMeshEnvAnnotation, but it is used to specify the list Jsons of environment variables that need to be programmed on Envoy sidecars + // Here's how a sample annotations will be like + // + // e.g. appmesh.k8s.aws/sidecarEnvJson: '[{"DD_ENV":"prod","TEST_ENV":"env_val"}]' + // e.g. appmesh.k8s.aws/sidecarEnvJson: '[{"DD_ENV":"prod"}]' + // + AppMeshEnvJsonAnnotation = "appmesh.k8s.aws/sidecarEnvJson" + // === begin xray daemon annotations === // AppMeshXrayAgentConfigAnnotation specifies the mount path for the Xray daemon's configuration file. diff --git a/pkg/inject/envoy.go b/pkg/inject/envoy.go index 9be8c01f..e56bc226 100644 --- a/pkg/inject/envoy.go +++ b/pkg/inject/envoy.go @@ -2,6 +2,7 @@ package inject import ( "fmt" + "k8s.io/apimachinery/pkg/util/json" "strconv" "strings" @@ -92,6 +93,15 @@ func (m *envoyMutator) mutate(pod *corev1.Pod) error { return err } + customEnvJson, err := m.getCustomEnvJson(pod) + if err != nil { + return err + } + if customEnvJson != nil { + for k, v := range customEnvJson { + customEnv[k] = v + } + } container, err := buildEnvoySidecar(variables, customEnv) if err != nil { return err @@ -249,6 +259,28 @@ func (m *envoyMutator) getCustomEnv(pod *corev1.Pod) (map[string]string, error) return customEnv, nil } +func (m *envoyMutator) getCustomEnvJson(pod *corev1.Pod) (map[string]string, error) { + var temp []map[string]interface{} + customEnvJson := make(map[string]string) + if v, ok := pod.ObjectMeta.Annotations[AppMeshEnvJsonAnnotation]; ok { + err := json.Unmarshal([]byte(v), &temp) + for _, item := range temp { + for key, value := range item { + if strValue, isString := value.(string); isString { + customEnvJson[key] = strValue + } else { + return nil, errors.Errorf("nested json isn't supported with this annotation %s, expected format: %s", AppMeshEnvJsonAnnotation, `[{"DD_ENV":"prod","TEST_ENV":"env_val"}]`) + } + } + } + if err != nil { + err = errors.Errorf("malformed annotation %s, expected format: %s", AppMeshEnvJsonAnnotation, `[{"DD_ENV":"prod","TEST_ENV":"env_val"}]`) + return nil, err + } + } + return customEnvJson, nil +} + func (m *envoyMutator) mutateVolumeMounts(pod *corev1.Pod, envoyContainer *corev1.Container, volumeMounts map[string]string) { for volumeName, mountPath := range volumeMounts { volumeMount := corev1.VolumeMount{ diff --git a/pkg/inject/envoy_test.go b/pkg/inject/envoy_test.go index b3925797..ea135a12 100644 --- a/pkg/inject/envoy_test.go +++ b/pkg/inject/envoy_test.go @@ -3510,6 +3510,99 @@ func Test_envoyMutator_getCustomEnv(t *testing.T) { }) } } +func Test_envoyMutator_getCustomEnvJson(t *testing.T) { + type args struct { + pod *corev1.Pod + } + tests := []struct { + name string + args args + want map[string]string + wantErr error + }{ + { + name: "pods with valid appmesh.k8s.aws/sidecarEnvJson annotation", + args: args{ + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "appmesh.k8s.aws/sidecarEnvJson": `[{"DD_ENV":"prod","TEST_ENV":"env_val"},{"PROD_ENV":"prod_env"}]`, + }, + }, + }, + }, + want: map[string]string{ + "DD_ENV": "prod", + "TEST_ENV": "env_val", + "PROD_ENV": "prod_env", + }, + wantErr: nil, + }, + { + name: "pods with no appmesh.k8s.aws/sidecarEnvJson annotation", + args: args{ + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{}, + }, + }, + }, + want: map[string]string{}, + wantErr: nil, + }, + { + name: "pods with invalid appmesh.k8s.aws/sidecarEnvJson annotation", + args: args{ + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "appmesh.k8s.aws/sidecarEnvJson": `[{"DD_ENV":"{PROD_ENV: "prod"}","TEST_ENV":"env_val"}]`, + }, + }, + }, + }, + wantErr: errors.New("malformed annotation appmesh.k8s.aws/sidecarEnvJson, expected format: [{\"DD_ENV\":\"prod\",\"TEST_ENV\":\"env_val\"}]"), + }, + { + name: "pods with Nested Json appmesh.k8s.aws/sidecarEnvJson annotation", + args: args{ + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "appmesh.k8s.aws/sidecarEnvJson": `[{"DD_ENV":{"PROD_ENV": "prod"},"TEST_ENV":"env_val"}]`, + }, + }, + }, + }, + wantErr: errors.New("nested json isn't supported with this annotation appmesh.k8s.aws/sidecarEnvJson, expected format: [{\"DD_ENV\":\"prod\",\"TEST_ENV\":\"env_val\"}]"), + }, + { + name: "pods with no_input appmesh.k8s.aws/sidecarEnvJson annotation", + args: args{ + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "appmesh.k8s.aws/sidecarEnvJson": ``, + }, + }, + }, + }, + wantErr: errors.New("malformed annotation appmesh.k8s.aws/sidecarEnvJson, expected format: [{\"DD_ENV\":\"prod\",\"TEST_ENV\":\"env_val\"}]"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &envoyMutator{} + got, err := m.getCustomEnvJson(tt.args.pod) + if tt.wantErr != nil { + assert.EqualError(t, err, tt.wantErr.Error()) + } else { + assert.NoError(t, err) + assert.Equal(t, got, tt.want) + } + }) + } +} func Test_envoyMutator_getAugmentedMeshName(t *testing.T) { type fields struct {