diff --git a/examples/app/templates/deployment.yaml b/examples/app/templates/deployment.yaml index 3567b1e5..e591c525 100644 --- a/examples/app/templates/deployment.yaml +++ b/examples/app/templates/deployment.yaml @@ -18,6 +18,7 @@ spec: app: myapp {{- include "app.selectorLabels" . | nindent 8 }} spec: + affinity: {{- toYaml .Values.myapp.affinity | nindent 8 }} containers: - args: {{- toYaml .Values.myapp.app.args | nindent 8 }} command: diff --git a/examples/app/values.yaml b/examples/app/values.yaml index 54f03b4b..3c1edaed 100644 --- a/examples/app/values.yaml +++ b/examples/app/values.yaml @@ -64,6 +64,13 @@ mySecretVars: var1: "" var2: "" myapp: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: Exists app: args: - --health-probe-bind-address=:8081 diff --git a/pkg/processor/pod/pod.go b/pkg/processor/pod/pod.go index 08d6bd4e..4d6ae275 100644 --- a/pkg/processor/pod/pod.go +++ b/pkg/processor/pod/pod.go @@ -99,6 +99,22 @@ func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpe } } + // process affinity if presented: + if spec.Affinity != nil { + err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%s.affinity | nindent %d }}`, objName, nindent), "affinity") + if err != nil { + return nil, nil, err + } + affinityMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(spec.Affinity) + if err != nil { + return nil, nil, err + } + err = unstructured.SetNestedField(values, affinityMap, objName, "affinity") + if err != nil { + return nil, nil, err + } + } + // process tolerations if presented: err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%s.tolerations | nindent %d }}`, objName, nindent), "tolerations") if err != nil { diff --git a/pkg/processor/pod/pod_test.go b/pkg/processor/pod/pod_test.go index 5ac49c01..dfe14529 100644 --- a/pkg/processor/pod/pod_test.go +++ b/pkg/processor/pod/pod_test.go @@ -137,6 +137,35 @@ spec: runAsNonRoot: true runAsUser: 65532 +` + strDeploymentWithAffinity = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: localhost:6001/my_project:latest + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: Exists + ` ) @@ -376,5 +405,61 @@ func Test_pod_Process(t *testing.T) { }, }, tmpl) }) + t.Run("deployment with affinity", func(t *testing.T) { + var deploy appsv1.Deployment + obj := internal.GenerateObj(strDeploymentWithAffinity) + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) + specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec, 0) + assert.NoError(t, err) + assert.Equal(t, map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "env": []interface{}{ + map[string]interface{}{ + "name": "KUBERNETES_CLUSTER_DOMAIN", + "value": "{{ quote .Values.kubernetesClusterDomain }}", + }, + }, + "image": "{{ .Values.nginx.nginx.image.repository }}:{{ .Values.nginx.nginx.image.tag | default .Chart.AppVersion }}", + "name": "nginx", + "resources": map[string]interface{}{}, + }, + }, + "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", + "serviceAccountName": `{{ include ".serviceAccountName" . }}`, + "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", + "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", + "affinity": "{{- toYaml .Values.nginx.affinity | nindent 8 }}", + }, specMap) + assert.Equal(t, helmify.Values{ + "nginx": map[string]interface{}{ + "affinity": map[string]interface{}{ + "nodeAffinity": map[string]interface{}{ + "requiredDuringSchedulingIgnoredDuringExecution": map[string]interface{}{ + "nodeSelectorTerms": []interface{}{ + map[string]interface{}{ + "matchExpressions": []interface{}{ + map[string]interface{}{ + "key": "node-role.kubernetes.io/control-plane", + "operator": "Exists", + }, + }, + }, + }, + }, + }, + }, + "nginx": map[string]interface{}{ + "image": map[string]interface{}{ + "repository": "localhost:6001/my_project", + "tag": "latest", + }, + }, + "nodeSelector": map[string]interface{}{}, + "tolerations": []interface{}{}, + "topologySpreadConstraints": []interface{}{}, + }, + }, tmpl) + }) } diff --git a/test_data/sample-app.yaml b/test_data/sample-app.yaml index e3e13197..d13bb8da 100644 --- a/test_data/sample-app.yaml +++ b/test_data/sample-app.yaml @@ -94,6 +94,13 @@ spec: nodeSelector: region: east type: user-node + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: Exists terminationGracePeriodSeconds: 10 volumes: - configMap: