diff --git a/.github/workflows/go-test-datadog-csi-driver.yaml b/.github/workflows/go-test-datadog-csi-driver.yaml new file mode 100644 index 000000000..e25ebac22 --- /dev/null +++ b/.github/workflows/go-test-datadog-csi-driver.yaml @@ -0,0 +1,40 @@ +name: Go Test Datadog CSI Driver +on: + push: + paths: + - 'test/datadog-csi-driver/**' + - 'charts/datadog-csi-driver/**' + pull_request: + paths: + - 'test/datadog-csi-driver/**' + - 'charts/datadog-csi-driver/**' + +# Permission forced by repo-level setting; only elevate on job-level +permissions: + contents: read + # packages: read + +env: + GO111MODULE: "on" + PROJECTNAME: "helm-charts" +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Set up Go + uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 + with: + go-version: 1.24 + id: go + - name: Set up Helm + uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3.5.0 + with: + version: v3.10.1 + - name: Add Datadog Helm repo + run: helm repo add datadog https://helm.datadoghq.com && helm repo update + - name: Check out code into the Go module directory + uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 + - name: run Go tests + run: | + helm dependency build ./charts/datadog-csi-driver + make unit-test-datadog-csi-driver diff --git a/Makefile b/Makefile index 29fa9339f..bd6c9d01e 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ vet: go vet -C test ./... .PHONY: unit-test -unit-test: unit-test-datadog unit-test-operator unit-test-private-action-runner +unit-test: unit-test-datadog unit-test-operator unit-test-private-action-runner unit-test-datadog-csi-driver .PHONY: unit-test-datadog unit-test-datadog: @@ -60,12 +60,17 @@ unit-test-operator: helm dependency update ./charts/datadog-operator 2>/dev/null go test -C test ./datadog-operator -count=1 +.PHONY: unit-test-datadog-csi-driver +unit-test-datadog-csi-driver: + helm dependency update ./charts/datadog-csi-driver 2>/dev/null + go test -C test ./datadog-csi-driver -count=1 + .PHONY: unit-test-private-action-runner unit-test-private-action-runner: go test -C test ./private-action-runner -count=1 .PHONY: update-test-baselines -update-test-baselines: update-test-baselines-datadog-agent update-test-baselines-operator update-test-baselines-private-action-runner +update-test-baselines: update-test-baselines-datadog-agent update-test-baselines-operator update-test-baselines-private-action-runner update-test-baselines-datadog-csi-driver .PHONY: update-test-baselines-private-action-runner update-test-baselines-private-action-runner: @@ -81,6 +86,11 @@ update-test-baselines-datadog-agent: helm dependency update ./charts/datadog 2>/dev/null go test -C test ./datadog -count=1 -args -updateBaselines=true +.PHONY: update-test-baselines-datadog-csi-driver +update-test-baselines-datadog-csi-driver: + helm dependency update ./charts/datadog-csi-driver 2>/dev/null + go test -C test ./datadog-csi-driver -count=1 -args -updateBaselines=true + .PHONY: integration-test integration-test: go test -C test/integ --tags=integration -count=1 -v diff --git a/charts/datadog-csi-driver/CHANGELOG.md b/charts/datadog-csi-driver/CHANGELOG.md index 8e72f77c5..d22bcdd83 100644 --- a/charts/datadog-csi-driver/CHANGELOG.md +++ b/charts/datadog-csi-driver/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.5.0 + +* [CONTP-719] Expose security context and annotation configurations ([#2317](https://github.com/DataDog/helm-charts/pull/2317)). + ## 0.4.4 * Support the definition of tolerations diff --git a/charts/datadog-csi-driver/Chart.yaml b/charts/datadog-csi-driver/Chart.yaml index 6ddacd6cd..cd83131f3 100644 --- a/charts/datadog-csi-driver/Chart.yaml +++ b/charts/datadog-csi-driver/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: datadog-csi-driver description: Datadog CSI Driver helm chart type: application -version: 0.4.4 +version: 0.5.0 appVersion: "0.1.0" maintainers: - name: Datadog diff --git a/charts/datadog-csi-driver/README.md b/charts/datadog-csi-driver/README.md index 432b8aefb..8582e5353 100644 --- a/charts/datadog-csi-driver/README.md +++ b/charts/datadog-csi-driver/README.md @@ -1,6 +1,6 @@ # datadog-csi-driver -![Version: 0.4.4](https://img.shields.io/badge/Version-0.4.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.0](https://img.shields.io/badge/AppVersion-0.1.0-informational?style=flat-square) +![Version: 0.5.0](https://img.shields.io/badge/Version-0.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.1.0](https://img.shields.io/badge/AppVersion-0.1.0-informational?style=flat-square) Datadog CSI Driver helm chart @@ -14,6 +14,8 @@ Datadog CSI Driver helm chart | Key | Type | Default | Description | |-----|------|---------|-------------| +| annotations | object | `{}` | Configure the annotations for the csi driver daemonset pods. | +| driver.securityContext | object | `{"privileged":true,"readOnlyRootFilesystem":true}` | CSI driver securityContext | | fullnameOverride | string | `""` | Allows overriding the full name of resources created by the chart. If set, this value completely replaces the generated name, ignoring the standard naming convention. | | image.pullPolicy | string | `"IfNotPresent"` | CSI driver image pullPolicy | | image.pullSecrets | list | `[]` | CSI driver repository pullSecret (for example: specify Docker registry credentials) | @@ -23,6 +25,8 @@ Datadog CSI Driver helm chart | registrar.image.pullPolicy | string | `"IfNotPresent"` | CSI registrar image pullPolicy | | registrar.image.repository | string | `"k8s.gcr.io/sig-storage/csi-node-driver-registrar"` | Override default registry + image.name for the registrar | | registrar.image.tag | string | `"v2.0.1"` | CSI registrar image tag to use | +| registrar.securityContext | object | `{}` | CSI registrar securityContext | +| securityContext | object | `{}` | Configure the security context for the csi driver daemonset pods. | | sockets.apmHostSocketPath | string | `"/var/run/datadog/apm.socket"` | | | sockets.dsdHostSocketPath | string | `"/var/run/datadog/dsd.socket"` | | | tolerations | list | `[]` | Allow scheduling the csi driver daemonset pods on tainted nodes. | diff --git a/charts/datadog-csi-driver/templates/daemonset.yaml b/charts/datadog-csi-driver/templates/daemonset.yaml index 6dcb1fcab..32ed7dc73 100644 --- a/charts/datadog-csi-driver/templates/daemonset.yaml +++ b/charts/datadog-csi-driver/templates/daemonset.yaml @@ -16,7 +16,15 @@ spec: labels: app: {{ include "datadog-csi-driver.daemonsetName" . }} admission.datadoghq.com/enabled: "false" + {{- with .Values.annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} spec: + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} {{- if .Values.image.pullSecrets }} imagePullSecrets: {{ toYaml .Values.image.pullSecrets | indent 8 }} @@ -25,8 +33,10 @@ spec: - name: csi-node-driver image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.driver.securityContext }} securityContext: - privileged: true + {{- toYaml . | nindent 12 }} + {{- end }} ports: - containerPort: 5000 protocol: TCP @@ -59,6 +69,10 @@ spec: - name: csi-node-driver-registrar image: "{{ .Values.registrar.image.repository }}:{{ .Values.registrar.image.tag }}" imagePullPolicy: {{ .Values.registrar.image.pullPolicy }} + {{- with .Values.registrar.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} args: - "--csi-address=$(ADDRESS)" - "--kubelet-registration-path=$(DRIVER_REG_SOCK_PATH)" diff --git a/charts/datadog-csi-driver/values.yaml b/charts/datadog-csi-driver/values.yaml index fe9a4046e..da7067eb8 100644 --- a/charts/datadog-csi-driver/values.yaml +++ b/charts/datadog-csi-driver/values.yaml @@ -41,12 +41,21 @@ registrar: # registrar.image.pullPolicy -- CSI registrar image pullPolicy pullPolicy: IfNotPresent + # registrar.securityContext -- CSI registrar securityContext + securityContext: {} + +driver: + # driver.securityContext -- CSI driver securityContext + securityContext: + readOnlyRootFilesystem: true + privileged: true + sockets: # apmHostSocketPath -- Host path of the apm socket. # Should correspond to `datadog.apm.hostSocketPath` apmHostSocketPath: /var/run/datadog/apm.socket - # dsdHostSocketPath -- Host path of the apm socket. + # dsdHostSocketPath -- Host path of the dsd socket. # Should correspond to `datadog.dsd.hostSocketPath` dsdHostSocketPath: /var/run/datadog/dsd.socket @@ -54,3 +63,9 @@ sockets: ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ tolerations: [] + +# securityContext -- Configure the security context for the csi driver daemonset pods. +securityContext: {} + +# annotations -- Configure the annotations for the csi driver daemonset pods. +annotations: {} diff --git a/test/datadog-csi-driver/baseline/CSI_Driver_annotation_and_securitycontext.yaml b/test/datadog-csi-driver/baseline/CSI_Driver_annotation_and_securitycontext.yaml new file mode 100644 index 000000000..ffb601fb5 --- /dev/null +++ b/test/datadog-csi-driver/baseline/CSI_Driver_annotation_and_securitycontext.yaml @@ -0,0 +1,114 @@ +--- +# Source: datadog-csi-driver/templates/daemonset.yaml +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: datadog-csi-driver-node-server + namespace: datadog-agent +spec: + selector: + matchLabels: + app: datadog-csi-driver-node-server + template: + metadata: + labels: + app: datadog-csi-driver-node-server + admission.datadoghq.com/enabled: "false" + annotations: + ad.datadoghq.com/csi-node-driver.checks: | + { + "openmetrics": { + "init_config": {}, + "instances": [ + { + "openmetrics_endpoint": "http://%%host%%:5000/metrics", + "metrics": [{ + "datadog_csi_driver_node_publish_volume_attempts": "datadog_csi.driver_node_publish_volume_attempts", + "datadog_csi_driver_node_unpublish_volume_attempts": "datadog_csi.driver_node_unpublish_volume_attempts" + }] + } + ] + } + } + spec: + securityContext: + fsGroup: 0 + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + containers: + - name: csi-node-driver + image: "gcr.io/datadoghq/csi-driver:1.0.0" + imagePullPolicy: IfNotPresent + securityContext: + privileged: true + readOnlyRootFilesystem: true + ports: + - containerPort: 5000 + protocol: TCP + args: + - --apm-host-socket-path=/var/run/datadog/apm.socket + - --dsd-host-socket-path=/var/run/datadog/dsd.socket + volumeMounts: + # plugin-dir stores the socket on which CSI node server service is exposed. + # it is created by the node server and needs to be writeable. + - name: plugin-dir + mountPath: /csi + - name: apm-socket + mountPath: /var/run/datadog + readOnly: true + # write mode is required to perform a volume mount + # csi driver has to create a subdirectory under /var/lib/kubelet/pods//volumes/kubernetes.io~csi/datadog/mount. + - mountPath: /var/lib/kubelet/pods + mountPropagation: Bidirectional + name: mountpoint-dir + env: + - name: NODE_ID + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: csi-node-driver-registrar + image: "k8s.gcr.io/sig-storage/csi-node-driver-registrar:v2.0.1" + imagePullPolicy: IfNotPresent + securityContext: + privileged: true + readOnlyRootFilesystem: false + args: + - "--csi-address=$(ADDRESS)" + - "--kubelet-registration-path=$(DRIVER_REG_SOCK_PATH)" + env: + - name: ADDRESS + value: /csi/csi.sock + - name: DRIVER_REG_SOCK_PATH + value: /var/lib/kubelet/plugins/datadog.csi/driver/csi.sock + volumeMounts: + # plugin-dir stores the socket created by the CSI driver node server. + # it is needed by the registrar to fetch the driver name from the driver contain (via the CSI GetPluginInfo() call). + - name: plugin-dir + mountPath: /csi # Match this to ADDRESS + readOnly: true + # registration-dir is used to store the registration information and register the driver with kubelet. + # it needs to be writeable + - name: registration-dir + mountPath: /registration # This is where the registrar writes the registration information + volumes: + - name: plugin-dir + hostPath: + path: /var/lib/kubelet/plugins/datadog.csi/driver + type: DirectoryOrCreate + - name: registration-dir + hostPath: + path: /var/lib/kubelet/plugins_registry + type: Directory + - hostPath: + path: /var/lib/kubelet/pods + type: DirectoryOrCreate + name: mountpoint-dir + - hostPath: + path: /var/run/datadog + type: DirectoryOrCreate + name: apm-socket + - hostPath: + path: /var/run/datadog + type: DirectoryOrCreate + name: dsd-socket \ No newline at end of file diff --git a/test/datadog-csi-driver/baseline/CSI_Driver_default.yaml b/test/datadog-csi-driver/baseline/CSI_Driver_default.yaml new file mode 100644 index 000000000..16f60aad8 --- /dev/null +++ b/test/datadog-csi-driver/baseline/CSI_Driver_default.yaml @@ -0,0 +1,90 @@ +--- +# Source: datadog-csi-driver/templates/daemonset.yaml +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: datadog-csi-driver-node-server + namespace: datadog-agent +spec: + selector: + matchLabels: + app: datadog-csi-driver-node-server + template: + metadata: + labels: + app: datadog-csi-driver-node-server + admission.datadoghq.com/enabled: "false" + spec: + containers: + - name: csi-node-driver + image: "gcr.io/datadoghq/csi-driver:1.0.0" + imagePullPolicy: IfNotPresent + securityContext: + privileged: true + readOnlyRootFilesystem: true + ports: + - containerPort: 5000 + protocol: TCP + args: + - --apm-host-socket-path=/var/run/datadog/apm.socket + - --dsd-host-socket-path=/var/run/datadog/dsd.socket + volumeMounts: + # plugin-dir stores the socket on which CSI node server service is exposed. + # it is created by the node server and needs to be writeable. + - name: plugin-dir + mountPath: /csi + - name: apm-socket + mountPath: /var/run/datadog + readOnly: true + # write mode is required to perform a volume mount + # csi driver has to create a subdirectory under /var/lib/kubelet/pods//volumes/kubernetes.io~csi/datadog/mount. + - mountPath: /var/lib/kubelet/pods + mountPropagation: Bidirectional + name: mountpoint-dir + env: + - name: NODE_ID + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: csi-node-driver-registrar + image: "k8s.gcr.io/sig-storage/csi-node-driver-registrar:v2.0.1" + imagePullPolicy: IfNotPresent + args: + - "--csi-address=$(ADDRESS)" + - "--kubelet-registration-path=$(DRIVER_REG_SOCK_PATH)" + env: + - name: ADDRESS + value: /csi/csi.sock + - name: DRIVER_REG_SOCK_PATH + value: /var/lib/kubelet/plugins/datadog.csi/driver/csi.sock + volumeMounts: + # plugin-dir stores the socket created by the CSI driver node server. + # it is needed by the registrar to fetch the driver name from the driver contain (via the CSI GetPluginInfo() call). + - name: plugin-dir + mountPath: /csi # Match this to ADDRESS + readOnly: true + # registration-dir is used to store the registration information and register the driver with kubelet. + # it needs to be writeable + - name: registration-dir + mountPath: /registration # This is where the registrar writes the registration information + volumes: + - name: plugin-dir + hostPath: + path: /var/lib/kubelet/plugins/datadog.csi/driver + type: DirectoryOrCreate + - name: registration-dir + hostPath: + path: /var/lib/kubelet/plugins_registry + type: Directory + - hostPath: + path: /var/lib/kubelet/pods + type: DirectoryOrCreate + name: mountpoint-dir + - hostPath: + path: /var/run/datadog + type: DirectoryOrCreate + name: apm-socket + - hostPath: + path: /var/run/datadog + type: DirectoryOrCreate + name: dsd-socket \ No newline at end of file diff --git a/test/datadog-csi-driver/baseline_test.go b/test/datadog-csi-driver/baseline_test.go new file mode 100644 index 000000000..a8c907e5a --- /dev/null +++ b/test/datadog-csi-driver/baseline_test.go @@ -0,0 +1,64 @@ +package datadog_csi_driver + +import ( + "testing" + + "github.com/DataDog/helm-charts/test/common" + "github.com/DataDog/helm-charts/test/utils" + "github.com/stretchr/testify/assert" + appsv1 "k8s.io/api/apps/v1" +) + +func Test_baseline_manifests(t *testing.T) { + tests := []struct { + name string + command common.HelmCommand + baselineManifestPath string + assertions func(t *testing.T, baselineManifestPath, manifest string) + }{ + { + name: "CSI Driver DaemonSet default", + command: common.HelmCommand{ + ReleaseName: "datadog-csi-driver", + ChartPath: "../../charts/datadog-csi-driver", + ShowOnly: []string{"templates/daemonset.yaml"}, + Values: []string{"../../charts/datadog-csi-driver/values.yaml"}, + Overrides: map[string]string{}, + }, + baselineManifestPath: "./baseline/CSI_Driver_default.yaml", + assertions: verifyCSIDriverDaemonSet, + }, + { + name: "CSI Driver with annotations and security context set", + command: common.HelmCommand{ + ReleaseName: "datadog-csi-driver", + ChartPath: "../../charts/datadog-csi-driver", + ShowOnly: []string{"templates/daemonset.yaml"}, + Values: []string{ + "../../charts/datadog-csi-driver/values.yaml", + "./manifests/added_annotation_and_securitycontext.yaml", + }, + Overrides: map[string]string{}, + }, + baselineManifestPath: "./baseline/CSI_Driver_annotation_and_securitycontext.yaml", + assertions: verifyCSIDriverDaemonSet, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + manifest, err := common.RenderChart(t, tt.command) + assert.Nil(t, err, "couldn't render template") + t.Log("update baselines", common.UpdateBaselines) + if common.UpdateBaselines { + common.WriteToFile(t, tt.baselineManifestPath, manifest) + } + + tt.assertions(t, tt.baselineManifestPath, manifest) + }) + } +} + +func verifyCSIDriverDaemonSet(t *testing.T, baselineManifestPath, manifest string) { + utils.VerifyBaseline(t, baselineManifestPath, manifest, appsv1.DaemonSet{}, appsv1.DaemonSet{}) +} diff --git a/test/datadog-csi-driver/manifests/added_annotation_and_securitycontext.yaml b/test/datadog-csi-driver/manifests/added_annotation_and_securitycontext.yaml new file mode 100644 index 000000000..c8a1fb59d --- /dev/null +++ b/test/datadog-csi-driver/manifests/added_annotation_and_securitycontext.yaml @@ -0,0 +1,27 @@ +annotations: + ad.datadoghq.com/csi-node-driver.checks: | + { + "openmetrics": { + "init_config": {}, + "instances": [ + { + "openmetrics_endpoint": "http://%%host%%:5000/metrics", + "metrics": [{ + "datadog_csi_driver_node_publish_volume_attempts": "datadog_csi.driver_node_publish_volume_attempts", + "datadog_csi_driver_node_unpublish_volume_attempts": "datadog_csi.driver_node_unpublish_volume_attempts" + }] + } + ] + } + } + +registrar: + securityContext: + readOnlyRootFilesystem: false + privileged: true + +securityContext: + runAsNonRoot: false + runAsUser: 0 + runAsGroup: 0 + fsGroup: 0 \ No newline at end of file diff --git a/test/datadog-csi-driver/testcsi_test.go b/test/datadog-csi-driver/testcsi_test.go new file mode 100644 index 000000000..392086b7d --- /dev/null +++ b/test/datadog-csi-driver/testcsi_test.go @@ -0,0 +1,13 @@ +package datadog_csi_driver + +import ( + "os" + "testing" + + "github.com/DataDog/helm-charts/test/common" +) + +func TestMain(m *testing.M) { + common.ParseArgs() + os.Exit(m.Run()) +} diff --git a/test/datadog-operator/baseline_test.go b/test/datadog-operator/baseline_test.go index 46e863dbd..798d03f1e 100644 --- a/test/datadog-operator/baseline_test.go +++ b/test/datadog-operator/baseline_test.go @@ -4,8 +4,7 @@ import ( "testing" "github.com/DataDog/helm-charts/test/common" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" + "github.com/DataDog/helm-charts/test/utils" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -66,23 +65,9 @@ func Test_baseline_manifests(t *testing.T) { } func verifyOperatorDeployment(t *testing.T, baselineManifestPath, manifest string) { - verifyBaseline(t, baselineManifestPath, manifest, appsv1.Deployment{}, appsv1.Deployment{}) + utils.VerifyBaseline(t, baselineManifestPath, manifest, appsv1.Deployment{}, appsv1.Deployment{}) } func verifyDatadogAgent(t *testing.T, baselineManifestPath, manifest string) { - verifyBaseline(t, baselineManifestPath, manifest, v1.CustomResourceDefinition{}, v1.CustomResourceDefinition{}) -} - -func verifyBaseline[T any](t *testing.T, baselineManifestPath, manifest string, baseline, actual T) { - common.Unmarshal(t, manifest, &actual) - common.LoadFromFile(t, baselineManifestPath, &baseline) - - // Exclude "helm.sh/chart" label from comparison to avoid - // updating baselines on every unrelated chart changes. - ops := make(cmp.Options, 0) - ops = append(ops, cmpopts.IgnoreMapEntries(func(k, v string) bool { - return k == "helm.sh/chart" - })) - - assert.True(t, cmp.Equal(baseline, actual, ops), cmp.Diff(baseline, actual)) + utils.VerifyBaseline(t, baselineManifestPath, manifest, v1.CustomResourceDefinition{}, v1.CustomResourceDefinition{}) } diff --git a/test/utils/verify_baseline.go b/test/utils/verify_baseline.go new file mode 100644 index 000000000..803c052aa --- /dev/null +++ b/test/utils/verify_baseline.go @@ -0,0 +1,24 @@ +package utils + +import ( + "testing" + + "github.com/DataDog/helm-charts/test/common" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/assert" +) + +func VerifyBaseline[T any](t *testing.T, baselineManifestPath, manifest string, baseline, actual T) { + common.Unmarshal(t, manifest, &actual) + common.LoadFromFile(t, baselineManifestPath, &baseline) + + // Exclude "helm.sh/chart" label from comparison to avoid + // updating baselines on every unrelated chart changes. + ops := make(cmp.Options, 0) + ops = append(ops, cmpopts.IgnoreMapEntries(func(k, v string) bool { + return k == "helm.sh/chart" + })) + + assert.True(t, cmp.Equal(baseline, actual, ops), cmp.Diff(baseline, actual)) +}