From 87d56a3345c64260643149e590e6a39ac8fb622a Mon Sep 17 00:00:00 2001 From: Young Bu Park Date: Wed, 6 Sep 2023 05:26:06 -0700 Subject: [PATCH 1/2] Add more unit-tests for basemanifest --- pkg/corerp/renderers/container/manifest.go | 10 - pkg/corerp/renderers/container/render.go | 11 +- pkg/corerp/renderers/container/render_test.go | 85 +++++++++ .../basemanifest-input-addcontainer.yaml | 25 +++ .../testdata/basemanifest-input-merge.yaml | 95 ++++++++++ .../basemanifest-output-addcontainer.json | 84 +++++++++ .../testdata/basemanifest-output-merge.json | 118 ++++++++++++ pkg/corerp/renderers/container/util.go | 55 ++++++ pkg/corerp/renderers/container/util_test.go | 178 ++++++++++++++++++ 9 files changed, 645 insertions(+), 16 deletions(-) create mode 100644 pkg/corerp/renderers/container/testdata/basemanifest-input-addcontainer.yaml create mode 100644 pkg/corerp/renderers/container/testdata/basemanifest-input-merge.yaml create mode 100644 pkg/corerp/renderers/container/testdata/basemanifest-output-addcontainer.json create mode 100644 pkg/corerp/renderers/container/testdata/basemanifest-output-merge.json create mode 100644 pkg/corerp/renderers/container/util.go create mode 100644 pkg/corerp/renderers/container/util_test.go diff --git a/pkg/corerp/renderers/container/manifest.go b/pkg/corerp/renderers/container/manifest.go index 538832bdc4..de2d7aca55 100644 --- a/pkg/corerp/renderers/container/manifest.go +++ b/pkg/corerp/renderers/container/manifest.go @@ -33,7 +33,6 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/strategicpatch" ) @@ -161,15 +160,6 @@ func getServiceAccountBase(manifest kubeutil.ObjectManifest, appName string, r * return defaultAccount } -func getObjectMeta(metaObj metav1.ObjectMeta, appName, resourceName, resourceType string, options renderers.RenderOptions) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Name: kubernetes.NormalizeResourceName(resourceName), - Namespace: options.Environment.Namespace, - Labels: labels.Merge(metaObj.Labels, renderers.GetLabels(options, appName, resourceName, resourceType)), - Annotations: labels.Merge(metaObj.Annotations, renderers.GetAnnotations(options)), - } -} - // populateAllBaseResources populates all remaining resources from manifest into outputResources. // These resources must be deployed before Deployment resource by adding them as a dependency. func populateAllBaseResources(ctx context.Context, base kubeutil.ObjectManifest, outputResources []rpv1.OutputResource, options renderers.RenderOptions) []rpv1.OutputResource { diff --git a/pkg/corerp/renderers/container/render.go b/pkg/corerp/renderers/container/render.go index cfac671c5f..2d4aa49647 100644 --- a/pkg/corerp/renderers/container/render.go +++ b/pkg/corerp/renderers/container/render.go @@ -618,14 +618,13 @@ func (r Renderer) makeDeployment( outputResources = append(outputResources, *roleBinding) deps = append(deps, rpv1.LocalIDKubernetesRoleBinding) - deployment.Spec.Template.ObjectMeta = metav1.ObjectMeta{ - Labels: podLabels, - Annotations: map[string]string{}, - } + deployment.Spec.Template.ObjectMeta = mergeObjectMeta(deployment.Spec.Template.ObjectMeta, metav1.ObjectMeta{ + Labels: podLabels, + }) - deployment.Spec.Selector = &metav1.LabelSelector{ + deployment.Spec.Selector = mergeLabelSelector(deployment.Spec.Selector, &metav1.LabelSelector{ MatchLabels: kubernetes.MakeSelectorLabels(applicationName, resource.Name), - } + }) podSpec.Volumes = append(podSpec.Volumes, volumes...) diff --git a/pkg/corerp/renderers/container/render_test.go b/pkg/corerp/renderers/container/render_test.go index 688151386e..0300a849dc 100644 --- a/pkg/corerp/renderers/container/render_test.go +++ b/pkg/corerp/renderers/container/render_test.go @@ -17,6 +17,7 @@ limitations under the License. package container import ( + "encoding/json" "fmt" "testing" @@ -34,6 +35,7 @@ import ( resources_azure "github.com/radius-project/radius/pkg/ucp/resources/azure" resources_kubernetes "github.com/radius-project/radius/pkg/ucp/resources/kubernetes" "github.com/radius-project/radius/test/testcontext" + "github.com/radius-project/radius/test/testutil" "github.com/google/uuid" "github.com/stretchr/testify/require" @@ -1679,6 +1681,89 @@ func Test_Render_StrategicPatchMerge(t *testing.T) { require.ElementsMatch(t, expectedContainers, deployment.Spec.Template.Spec.Containers) } +func Test_Render_BaseManifest(t *testing.T) { + manifestTests := []struct { + name string + inFile string + container datamodel.ContainerProperties + outFile string + }{ + { + name: "merge container, envvars, and volumes", + inFile: "basemanifest-input-merge.yaml", + container: datamodel.ContainerProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Application: applicationResourceID, + }, + Container: datamodel.Container{ + Image: "someimage:latest", + Env: map[string]string{ + envVarName1: envVarValue1, + envVarName2: envVarValue2, + }, + Volumes: map[string]datamodel.VolumeProperties{ + "ephemeralVolume": { + Kind: datamodel.Ephemeral, + Ephemeral: &datamodel.EphemeralVolume{ + VolumeBase: datamodel.VolumeBase{ + MountPath: "/mnt/ephemeral", + }, + ManagedStore: datamodel.ManagedStoreMemory, + }, + }, + }, + }, + }, + outFile: "basemanifest-output-merge.json", + }, + { + name: "inject new sidecar", + inFile: "basemanifest-input-addcontainer.yaml", + container: datamodel.ContainerProperties{ + BasicResourceProperties: rpv1.BasicResourceProperties{ + Application: applicationResourceID, + }, + Container: datamodel.Container{ + Image: "someimage:latest", + Env: map[string]string{ + envVarName1: envVarValue1, + envVarName2: envVarValue2, + }, + }, + }, + outFile: "basemanifest-output-addcontainer.json", + }, + } + + for _, tc := range manifestTests { + t.Run(tc.name, func(t *testing.T) { + inYAML := testutil.ReadFixture(tc.inFile) + tc.container.Runtimes = &datamodel.RuntimeProperties{ + Kubernetes: &datamodel.KubernetesRuntime{ + Base: string(inYAML), + }, + } + + resource := makeResource(t, tc.container) + dependencies := map[string]renderers.RendererDependency{} + + ctx := testcontext.New(t) + renderer := Renderer{} + output, err := renderer.Render(ctx, resource, renderers.RenderOptions{Dependencies: dependencies}) + require.NoError(t, err) + + deployment, _ := kubernetes.FindDeployment(output.Resources) + require.NotNil(t, deployment) + + actual, err := json.MarshalIndent(deployment, "", " ") + require.NoError(t, err) + + outputYaml := testutil.ReadFixture(tc.outFile) + require.Equal(t, string(outputYaml), string(actual), "actual output: %s", string(actual)) + }) + } +} + func renderOptionsEnvAndAppKubeMetadata() renderers.RenderOptions { dependencies := map[string]renderers.RendererDependency{} option := renderers.RenderOptions{Dependencies: dependencies} diff --git a/pkg/corerp/renderers/container/testdata/basemanifest-input-addcontainer.yaml b/pkg/corerp/renderers/container/testdata/basemanifest-input-addcontainer.yaml new file mode 100644 index 0000000000..54c8196aa9 --- /dev/null +++ b/pkg/corerp/renderers/container/testdata/basemanifest-input-addcontainer.yaml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-container + labels: + app: test-container + annotations: + source: base-manifest-test +spec: + replicas: 3 + selector: + matchLabels: + app: test-container + basemanifest: default + template: + spec: + containers: + - name: sidecar + image: "sidecar:latest" + ports: + - containerPort: 80 + protocol: TCP + env: + - name: KEY + value: VALUE diff --git a/pkg/corerp/renderers/container/testdata/basemanifest-input-merge.yaml b/pkg/corerp/renderers/container/testdata/basemanifest-input-merge.yaml new file mode 100644 index 0000000000..21fe452104 --- /dev/null +++ b/pkg/corerp/renderers/container/testdata/basemanifest-input-merge.yaml @@ -0,0 +1,95 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-container + labels: + app: test-container + annotations: + source: base-manifest-test +spec: + replicas: 3 + selector: + matchLabels: + app: test-container + basemanifest: default + template: + metadata: + labels: + app: test-container + basemanifest: default + spec: + serviceAccountName: test-container + volumes: + - name: secret-vol + secret: + secretName: test-container-secret0 + containers: + - name: test-container + ports: + - containerPort: 80 + protocol: TCP + volumeMounts: + - name: secret-vol + readOnly: true + mountPath: /etc/secret-vol + env: + - name: TEST_SECRET_KEY + valueFrom: + secretKeyRef: + name: test-container-secret1 + key: secret1 + - name: TEST_CONFIGMAP_KEY + valueFrom: + configMapKeyRef: + name: test-container-config + key: TEST_CONFIGMAP +--- +apiVersion: v1 +kind: Service +metadata: + name: test-container + annotations: + source: base-manifest-test +spec: + selector: + app.kubernetes.io/name: test-container + ports: + - protocol: TCP + port: 3000 + targetPort: 3000 +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-container + annotations: + source: base-manifest-test +--- +apiVersion: v1 +kind: Secret +metadata: + name: test-container-secret0 + annotations: + source: base-manifest-test +type: Opaque +stringData: + 'secret0': test-secret-0 +--- +apiVersion: v1 +kind: Secret +metadata: + name: test-container-secret1 + annotations: + source: base-manifest-test +type: Opaque +stringData: + 'secret1': test-secret-1 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-container-config + annotations: + source: base-manifest-test +data: + TEST_CONFIGMAP: test-configmap diff --git a/pkg/corerp/renderers/container/testdata/basemanifest-output-addcontainer.json b/pkg/corerp/renderers/container/testdata/basemanifest-output-addcontainer.json new file mode 100644 index 0000000000..68a684b94d --- /dev/null +++ b/pkg/corerp/renderers/container/testdata/basemanifest-output-addcontainer.json @@ -0,0 +1,84 @@ +{ + "kind": "Deployment", + "apiVersion": "apps/v1", + "metadata": { + "name": "test-container", + "creationTimestamp": null, + "labels": { + "app": "test-container", + "app.kubernetes.io/managed-by": "radius-rp", + "app.kubernetes.io/name": "test-container", + "app.kubernetes.io/part-of": "test-app", + "radius.dev/application": "test-app", + "radius.dev/resource": "test-container", + "radius.dev/resource-type": "applications.core-containers" + }, + "annotations": { + "source": "base-manifest-test" + } + }, + "spec": { + "replicas": 3, + "selector": { + "matchLabels": { + "app": "test-container", + "basemanifest": "default", + "radius.dev/application": "test-app", + "radius.dev/resource": "test-container" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app.kubernetes.io/managed-by": "radius-rp", + "app.kubernetes.io/name": "test-container", + "app.kubernetes.io/part-of": "test-app", + "radius.dev/application": "test-app", + "radius.dev/resource": "test-container", + "radius.dev/resource-type": "applications.core-containers" + } + }, + "spec": { + "containers": [ + { + "name": "sidecar", + "image": "sidecar:latest", + "ports": [ + { + "containerPort": 80, + "protocol": "TCP" + } + ], + "env": [ + { + "name": "KEY", + "value": "VALUE" + } + ], + "resources": {} + }, + { + "name": "test-container", + "image": "someimage:latest", + "env": [ + { + "name": "TEST_VAR_1", + "value": "TEST_VALUE_1" + }, + { + "name": "TEST_VAR_2", + "value": "81" + } + ], + "resources": {} + } + ], + "serviceAccountName": "test-container", + "enableServiceLinks": false + } + }, + "strategy": {} + }, + "status": {} +} \ No newline at end of file diff --git a/pkg/corerp/renderers/container/testdata/basemanifest-output-merge.json b/pkg/corerp/renderers/container/testdata/basemanifest-output-merge.json new file mode 100644 index 0000000000..2fdd583332 --- /dev/null +++ b/pkg/corerp/renderers/container/testdata/basemanifest-output-merge.json @@ -0,0 +1,118 @@ +{ + "kind": "Deployment", + "apiVersion": "apps/v1", + "metadata": { + "name": "test-container", + "creationTimestamp": null, + "labels": { + "app": "test-container", + "app.kubernetes.io/managed-by": "radius-rp", + "app.kubernetes.io/name": "test-container", + "app.kubernetes.io/part-of": "test-app", + "radius.dev/application": "test-app", + "radius.dev/resource": "test-container", + "radius.dev/resource-type": "applications.core-containers" + }, + "annotations": { + "source": "base-manifest-test" + } + }, + "spec": { + "replicas": 3, + "selector": { + "matchLabels": { + "app": "test-container", + "basemanifest": "default", + "radius.dev/application": "test-app", + "radius.dev/resource": "test-container" + } + }, + "template": { + "metadata": { + "creationTimestamp": null, + "labels": { + "app": "test-container", + "app.kubernetes.io/managed-by": "radius-rp", + "app.kubernetes.io/name": "test-container", + "app.kubernetes.io/part-of": "test-app", + "basemanifest": "default", + "radius.dev/application": "test-app", + "radius.dev/resource": "test-container", + "radius.dev/resource-type": "applications.core-containers" + } + }, + "spec": { + "volumes": [ + { + "name": "secret-vol", + "secret": { + "secretName": "test-container-secret0" + } + }, + { + "name": "ephemeralVolume", + "emptyDir": { + "medium": "Memory" + } + } + ], + "containers": [ + { + "name": "test-container", + "image": "someimage:latest", + "ports": [ + { + "containerPort": 80, + "protocol": "TCP" + } + ], + "env": [ + { + "name": "TEST_SECRET_KEY", + "valueFrom": { + "secretKeyRef": { + "name": "test-container-secret1", + "key": "secret1" + } + } + }, + { + "name": "TEST_CONFIGMAP_KEY", + "valueFrom": { + "configMapKeyRef": { + "name": "test-container-config", + "key": "TEST_CONFIGMAP" + } + } + }, + { + "name": "TEST_VAR_1", + "value": "TEST_VALUE_1" + }, + { + "name": "TEST_VAR_2", + "value": "81" + } + ], + "resources": {}, + "volumeMounts": [ + { + "name": "secret-vol", + "readOnly": true, + "mountPath": "/etc/secret-vol" + }, + { + "name": "ephemeralVolume", + "mountPath": "/mnt/ephemeral" + } + ] + } + ], + "serviceAccountName": "test-container", + "enableServiceLinks": false + } + }, + "strategy": {} + }, + "status": {} +} \ No newline at end of file diff --git a/pkg/corerp/renderers/container/util.go b/pkg/corerp/renderers/container/util.go new file mode 100644 index 0000000000..e8dba2ab7d --- /dev/null +++ b/pkg/corerp/renderers/container/util.go @@ -0,0 +1,55 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package container + +import ( + "github.com/radius-project/radius/pkg/corerp/renderers" + "github.com/radius-project/radius/pkg/kubernetes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" +) + +func mergeLabelSelector(base *metav1.LabelSelector, cur *metav1.LabelSelector) *metav1.LabelSelector { + if base == nil { + base = &metav1.LabelSelector{} + } + + return &metav1.LabelSelector{ + MatchLabels: labels.Merge(base.MatchLabels, cur.MatchLabels), + MatchExpressions: append(base.MatchExpressions, cur.MatchExpressions...), + } +} + +func mergeObjectMeta(base metav1.ObjectMeta, cur metav1.ObjectMeta) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Name: cur.Name, + Namespace: cur.Namespace, + Labels: labels.Merge(base.Labels, cur.Labels), + Annotations: labels.Merge(base.Annotations, cur.Annotations), + } +} + +func getObjectMeta(base metav1.ObjectMeta, appName, resourceName, resourceType string, options renderers.RenderOptions) metav1.ObjectMeta { + cur := metav1.ObjectMeta{ + Name: kubernetes.NormalizeResourceName(resourceName), + Namespace: options.Environment.Namespace, + Labels: renderers.GetLabels(options, appName, resourceName, resourceType), + Annotations: renderers.GetAnnotations(options), + } + + return mergeObjectMeta(base, cur) +} diff --git a/pkg/corerp/renderers/container/util_test.go b/pkg/corerp/renderers/container/util_test.go new file mode 100644 index 0000000000..50ab74734a --- /dev/null +++ b/pkg/corerp/renderers/container/util_test.go @@ -0,0 +1,178 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package container + +import ( + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestMergeLabelSelector(t *testing.T) { + labelMergeTests := []struct { + name string + base *metav1.LabelSelector + cur *metav1.LabelSelector + expected *metav1.LabelSelector + }{ + { + name: "base is nil", + base: nil, + cur: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "key1": "value1", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "key2", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"value2"}, + }, + }, + }, + expected: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "key1": "value1", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "key2", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"value2"}, + }, + }, + }, + }, + { + name: "base includes matchLabels", + base: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "key1": "value1", + }, + }, + cur: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "key2": "value2", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "key2", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"value2"}, + }, + }, + }, + expected: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "key2", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"value2"}, + }, + }, + }, + }, + } + + for _, tc := range labelMergeTests { + t.Run(tc.name, func(t *testing.T) { + actual := mergeLabelSelector(tc.base, tc.cur) + require.Equal(t, tc.expected, actual) + }) + } +} + +func TestMergeObjectMeta(t *testing.T) { + mergeObjectMetaTests := []struct { + name string + base metav1.ObjectMeta + cur metav1.ObjectMeta + expected metav1.ObjectMeta + }{ + { + name: "base is empty", + base: metav1.ObjectMeta{}, + cur: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{ + "key1": "value1", + }, + Annotations: map[string]string{ + "key2": "value2", + }, + }, + expected: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{ + "key1": "value1", + }, + Annotations: map[string]string{ + "key2": "value2", + }, + }, + }, + { + name: "override name and namespace", + base: metav1.ObjectMeta{ + Name: "base", + Namespace: "base namespace", + Labels: map[string]string{ + "key1": "value1", + }, + Annotations: map[string]string{ + "key1": "value1", + }, + }, + cur: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{ + "key2": "value2", + }, + Annotations: map[string]string{ + "key2": "value2", + }, + }, + expected: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + Annotations: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + }, + }, + } + + for _, tc := range mergeObjectMetaTests { + t.Run(tc.name, func(t *testing.T) { + actual := mergeObjectMeta(tc.base, tc.cur) + require.Equal(t, tc.expected, actual) + }) + } +} From b9b9e29c209858931bfe3e524dd64325233f7b10 Mon Sep 17 00:00:00 2001 From: Young Bu Park Date: Sun, 10 Sep 2023 21:12:41 -0700 Subject: [PATCH 2/2] merge --- pkg/corerp/renderers/container/manifest.go | 32 ++++ .../renderers/container/manifest_test.go | 154 +++++++++++++++ pkg/corerp/renderers/container/util.go | 55 ------ pkg/corerp/renderers/container/util_test.go | 178 ------------------ 4 files changed, 186 insertions(+), 233 deletions(-) delete mode 100644 pkg/corerp/renderers/container/util.go delete mode 100644 pkg/corerp/renderers/container/util_test.go diff --git a/pkg/corerp/renderers/container/manifest.go b/pkg/corerp/renderers/container/manifest.go index de2d7aca55..6061cce165 100644 --- a/pkg/corerp/renderers/container/manifest.go +++ b/pkg/corerp/renderers/container/manifest.go @@ -33,6 +33,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/strategicpatch" ) @@ -228,3 +229,34 @@ func patchPodSpec(sourceSpec *corev1.PodSpec, patchSpec []byte) (*corev1.PodSpec return patched, nil } + +func mergeLabelSelector(base *metav1.LabelSelector, cur *metav1.LabelSelector) *metav1.LabelSelector { + if base == nil { + base = &metav1.LabelSelector{} + } + + return &metav1.LabelSelector{ + MatchLabels: labels.Merge(base.MatchLabels, cur.MatchLabels), + MatchExpressions: append(base.MatchExpressions, cur.MatchExpressions...), + } +} + +func mergeObjectMeta(base metav1.ObjectMeta, cur metav1.ObjectMeta) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Name: cur.Name, + Namespace: cur.Namespace, + Labels: labels.Merge(base.Labels, cur.Labels), + Annotations: labels.Merge(base.Annotations, cur.Annotations), + } +} + +func getObjectMeta(base metav1.ObjectMeta, appName, resourceName, resourceType string, options renderers.RenderOptions) metav1.ObjectMeta { + cur := metav1.ObjectMeta{ + Name: kubernetes.NormalizeResourceName(resourceName), + Namespace: options.Environment.Namespace, + Labels: renderers.GetLabels(options, appName, resourceName, resourceType), + Annotations: renderers.GetAnnotations(options), + } + + return mergeObjectMeta(base, cur) +} diff --git a/pkg/corerp/renderers/container/manifest_test.go b/pkg/corerp/renderers/container/manifest_test.go index 12684ddf97..f2637754ec 100644 --- a/pkg/corerp/renderers/container/manifest_test.go +++ b/pkg/corerp/renderers/container/manifest_test.go @@ -49,6 +49,160 @@ var ( testOptions = &renderers.RenderOptions{Environment: renderers.EnvironmentOptions{Namespace: "test-ns"}} ) +func TestMergeLabelSelector(t *testing.T) { + labelMergeTests := []struct { + name string + base *metav1.LabelSelector + cur *metav1.LabelSelector + expected *metav1.LabelSelector + }{ + { + name: "base is nil", + base: nil, + cur: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "key1": "value1", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "key2", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"value2"}, + }, + }, + }, + expected: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "key1": "value1", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "key2", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"value2"}, + }, + }, + }, + }, + { + name: "base includes matchLabels", + base: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "key1": "value1", + }, + }, + cur: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "key2": "value2", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "key2", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"value2"}, + }, + }, + }, + expected: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "key2", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"value2"}, + }, + }, + }, + }, + } + + for _, tc := range labelMergeTests { + t.Run(tc.name, func(t *testing.T) { + actual := mergeLabelSelector(tc.base, tc.cur) + require.Equal(t, tc.expected, actual) + }) + } +} + +func TestMergeObjectMeta(t *testing.T) { + mergeObjectMetaTests := []struct { + name string + base metav1.ObjectMeta + cur metav1.ObjectMeta + expected metav1.ObjectMeta + }{ + { + name: "base is empty", + base: metav1.ObjectMeta{}, + cur: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{ + "key1": "value1", + }, + Annotations: map[string]string{ + "key2": "value2", + }, + }, + expected: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{ + "key1": "value1", + }, + Annotations: map[string]string{ + "key2": "value2", + }, + }, + }, + { + name: "override name and namespace", + base: metav1.ObjectMeta{ + Name: "base", + Namespace: "base namespace", + Labels: map[string]string{ + "key1": "value1", + }, + Annotations: map[string]string{ + "key1": "value1", + }, + }, + cur: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{ + "key2": "value2", + }, + Annotations: map[string]string{ + "key2": "value2", + }, + }, + expected: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + Labels: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + Annotations: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + }, + }, + } + + for _, tc := range mergeObjectMetaTests { + t.Run(tc.name, func(t *testing.T) { + actual := mergeObjectMeta(tc.base, tc.cur) + require.Equal(t, tc.expected, actual) + }) + } +} + func TestFetchBaseManifest(t *testing.T) { manifestTests := []struct { name string diff --git a/pkg/corerp/renderers/container/util.go b/pkg/corerp/renderers/container/util.go deleted file mode 100644 index e8dba2ab7d..0000000000 --- a/pkg/corerp/renderers/container/util.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2023 The Radius Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package container - -import ( - "github.com/radius-project/radius/pkg/corerp/renderers" - "github.com/radius-project/radius/pkg/kubernetes" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" -) - -func mergeLabelSelector(base *metav1.LabelSelector, cur *metav1.LabelSelector) *metav1.LabelSelector { - if base == nil { - base = &metav1.LabelSelector{} - } - - return &metav1.LabelSelector{ - MatchLabels: labels.Merge(base.MatchLabels, cur.MatchLabels), - MatchExpressions: append(base.MatchExpressions, cur.MatchExpressions...), - } -} - -func mergeObjectMeta(base metav1.ObjectMeta, cur metav1.ObjectMeta) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Name: cur.Name, - Namespace: cur.Namespace, - Labels: labels.Merge(base.Labels, cur.Labels), - Annotations: labels.Merge(base.Annotations, cur.Annotations), - } -} - -func getObjectMeta(base metav1.ObjectMeta, appName, resourceName, resourceType string, options renderers.RenderOptions) metav1.ObjectMeta { - cur := metav1.ObjectMeta{ - Name: kubernetes.NormalizeResourceName(resourceName), - Namespace: options.Environment.Namespace, - Labels: renderers.GetLabels(options, appName, resourceName, resourceType), - Annotations: renderers.GetAnnotations(options), - } - - return mergeObjectMeta(base, cur) -} diff --git a/pkg/corerp/renderers/container/util_test.go b/pkg/corerp/renderers/container/util_test.go deleted file mode 100644 index 50ab74734a..0000000000 --- a/pkg/corerp/renderers/container/util_test.go +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright 2023 The Radius Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package container - -import ( - "testing" - - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestMergeLabelSelector(t *testing.T) { - labelMergeTests := []struct { - name string - base *metav1.LabelSelector - cur *metav1.LabelSelector - expected *metav1.LabelSelector - }{ - { - name: "base is nil", - base: nil, - cur: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "key1": "value1", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "key2", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"value2"}, - }, - }, - }, - expected: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "key1": "value1", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "key2", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"value2"}, - }, - }, - }, - }, - { - name: "base includes matchLabels", - base: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "key1": "value1", - }, - }, - cur: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "key2": "value2", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "key2", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"value2"}, - }, - }, - }, - expected: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "key2", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"value2"}, - }, - }, - }, - }, - } - - for _, tc := range labelMergeTests { - t.Run(tc.name, func(t *testing.T) { - actual := mergeLabelSelector(tc.base, tc.cur) - require.Equal(t, tc.expected, actual) - }) - } -} - -func TestMergeObjectMeta(t *testing.T) { - mergeObjectMetaTests := []struct { - name string - base metav1.ObjectMeta - cur metav1.ObjectMeta - expected metav1.ObjectMeta - }{ - { - name: "base is empty", - base: metav1.ObjectMeta{}, - cur: metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - Labels: map[string]string{ - "key1": "value1", - }, - Annotations: map[string]string{ - "key2": "value2", - }, - }, - expected: metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - Labels: map[string]string{ - "key1": "value1", - }, - Annotations: map[string]string{ - "key2": "value2", - }, - }, - }, - { - name: "override name and namespace", - base: metav1.ObjectMeta{ - Name: "base", - Namespace: "base namespace", - Labels: map[string]string{ - "key1": "value1", - }, - Annotations: map[string]string{ - "key1": "value1", - }, - }, - cur: metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - Labels: map[string]string{ - "key2": "value2", - }, - Annotations: map[string]string{ - "key2": "value2", - }, - }, - expected: metav1.ObjectMeta{ - Name: "name", - Namespace: "namespace", - Labels: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - Annotations: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - }, - }, - } - - for _, tc := range mergeObjectMetaTests { - t.Run(tc.name, func(t *testing.T) { - actual := mergeObjectMeta(tc.base, tc.cur) - require.Equal(t, tc.expected, actual) - }) - } -}