diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a731915..0750868 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -11,6 +11,7 @@ on: jobs: release: + if: github.repository == 'splunk/synthetics-helm-charts' permissions: contents: write runs-on: ubuntu-latest @@ -45,6 +46,8 @@ jobs: - name: Run chart-releaser uses: helm/chart-releaser-action@v1.6.0 + with: + config: cr.yaml env: CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" if: ${{ steps.check_update_chart.outputs.VALID_UPDATE == 1 }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f379f21..87dcd00 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -33,25 +33,31 @@ jobs: echo "changed=true" >> "$GITHUB_OUTPUT" fi - - name: Linting charts - run: helm lint charts/* - - name: Run chart-testing (lint) if: steps.list-changed.outputs.changed == 'true' run: ct lint --config=ct.yaml --target-branch ${{ github.event.repository.default_branch }} + - name: Run helm-docs + id: helm-docs + run: | + make docs + if [[ $(git diff --stat) != '' ]]; then + echo -e '\033[0;31mREADME outdated! Please run "make docs".\033[0m ❌' + git diff --color + exit 1 + else + echo -e '\033[0;32mREADME is up-to-date\033[0m ✔' + fi + - name: Run helm unit tests run: | helm plugin install https://github.com/helm-unittest/helm-unittest make unittest + - name: Run chart-testing (install) + run: ct install --config=ct.yaml + # - name: Create kind cluster # uses: helm/kind-action@v1.10.0 # # Only build a kind cluster if there are chart changes to test. # if: steps.list-changed.outputs.changed == 'true' - - # - name: Run chart-testing (install) - # run: ct install --config=ct.yaml - - - # e2e-test: [] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6655399..d956ef6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,16 @@ repos: exclude: ^charts/.*/templates/ args: [ --allow-multiple-documents ] - id: check-added-large-files - - repo: https://github.com/gruntwork-io/pre-commit - rev: v0.1.23 + - repo: https://github.com/norwoodj/helm-docs + rev: v1.14.2 + hooks: + - id: helm-docs + args: + - --chart-search-root=charts + - repo: local hooks: - id: helmlint + name: Lint the helm chart + entry: make lint + files: "charts/*" + language: system diff --git a/Makefile b/Makefile index e798a17..ec3bdce 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ # The general settings and variables for the project SHELL := /bin/bash -# TODO: Move CHART_FILE_PATH and VALUES_FILE_PATH here, currently set in multiple places # The version of the chart VERSION := $(shell grep "^version:" charts/splunk-synthetics-runner/Chart.yaml | awk '{print $$2}') @@ -15,7 +14,7 @@ lint: ## Lint the Helm chart with ct ct lint --config=ct.yaml || exit 1 .PHONY: pre-commit -pre-commit: render ## Test the Helm chart with pre-commit +pre-commit: ## Test the Helm chart with pre-commit @echo "Checking the Helm chart with pre-commit..." pre-commit run --all-files || exit 1 @@ -28,3 +27,7 @@ unittest: ## Run unittests on the Helm chart docs: ## Run unittests on the Helm chart @echo "Update docs for helm chart..." cd charts/splunk-synthetics-runner && helm-docs || exit 1 + +.PHONY: install-tools +install-tools: ## Install tools (macOS) + LOCALBIN=$(LOCALBIN) scripts/install-tools.sh || exit 1 diff --git a/charts/splunk-synthetics-runner/Chart.yaml b/charts/splunk-synthetics-runner/Chart.yaml index 0cf8ec3..ee76a4d 100644 --- a/charts/splunk-synthetics-runner/Chart.yaml +++ b/charts/splunk-synthetics-runner/Chart.yaml @@ -3,4 +3,11 @@ name: splunk-synthetics-runner description: Private location runners for Splunk Synthetic Monitoring type: application version: 0.0.1 -appVersion: "0.16.7" +appVersion: "0.19.3" +keywords: + - Splunk + - Observability + - Synthetic Monitoring + - Private Location +maintainers: + - name: Splunk diff --git a/charts/splunk-synthetics-runner/README.md b/charts/splunk-synthetics-runner/README.md index 72dbf0c..a28fd5d 100644 --- a/charts/splunk-synthetics-runner/README.md +++ b/charts/splunk-synthetics-runner/README.md @@ -1,6 +1,6 @@ ## Splunk Synthetic Monitoring - Kubernetes Private Locations -![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.16.7](https://img.shields.io/badge/AppVersion-0.16.7-informational?style=flat-square) +![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.19.3](https://img.shields.io/badge/AppVersion-0.19.3-informational?style=flat-square) Helm chart to deploy [private location runners](https://docs.splunk.com/observability/en/synthetics/test-config/private-locations.html) for [Splunk Synthetic Monitoring](https://www.splunk.com/en_us/products/synthetic-monitoring.html). @@ -19,12 +19,6 @@ $ helm install my-splunk-synthetics-runner release synthetics-helm-charts/splunk |-----|------|---------|-------------| | affinity | object | `{}` | Inter-pod and node affinity/anti-affinity rules. | | automountServiceAccountToken | bool | `true` | Indicates whether a service account token should be automatically mounted to the runner pod. | -| autoscaling | object | `{"enabled":false,"maxReplicas":6,"minReplicas":1,"targetCPUUtilizationPercentage":95,"targetMemoryUtilizationPercentage":95}` | Configuration for HPA | -| autoscaling.enabled | bool | `false` | Enable HPA | -| autoscaling.maxReplicas | int | `6` | Maximum replicas of runner | -| autoscaling.minReplicas | int | `1` | Minimum replicas of runner | -| autoscaling.targetCPUUtilizationPercentage | int | `95` | Target CPU utilization | -| autoscaling.targetMemoryUtilizationPercentage | int | `95` | Target Memory utilization | | commonLabels | object | `{}` | Additional labels which will be included on all objects and as selectors. | | containerSecurityContext | object | `{}` | Container security context for runner container. | | dnsConfig | object | `{}` | Specify additional DNS parameters for the runner pods. | @@ -55,10 +49,10 @@ $ helm install my-splunk-synthetics-runner release synthetics-helm-charts/splunk | serviceAccount.create | bool | `true` | If true, service account will be created. | | serviceAccount.name | string | `""` | The name of the service account to use. If not set, the release's fullname will be used when create is true. Set this variable to add user created service account to pod. | | synthetics | object | `{"additionalCaCerts":{},"enableNetworkShaping":true,"logLevel":"info","secret":{"create":false,"name":"","runnerToken":""}}` | Splunk Synthetics Runner configurations | -| synthetics.additionalCaCerts | object | `{}` | Add custom CA certs to use in API/HTTP tests. Requires privilege escalation. | +| synthetics.additionalCaCerts | object | `{}` | Add custom CA certs (should be in PEM format) to use in API/HTTP tests. Requires privilege escalation in an init container which adds these certs to the runner's system cacerts. | | synthetics.enableNetworkShaping | bool | `true` | Enable netwrok shapping capabilities which allows runner to simulate different device's throughputs. Needs privilege escalation and CAP_NET_ADMIN. | | synthetics.logLevel | string | `"info"` | logLevel is to set log level of the Splunk Synthetics runner. Available values are: debug, info, warn, error | -| synthetics.secret | object | `{"create":false,"name":"","runnerToken":""}` | Private location token configuration. Rotating the runner token requires an explicit rollout/restart of the deployment. | +| synthetics.secret | object | `{"create":false,"name":"","runnerToken":""}` | Private location token configuration. | | synthetics.secret.create | bool | `false` | Option for creating a new secret or using an existing one. When true, a new kubernetes secret will be created by the chart that will contain value from runnerToken. When false, the user must set secret.name to the name of the k8s secret the user created with the runner's token. | | synthetics.secret.name | string | `""` | The name of the secret created by chart (if name is empty the default name is used) or the name of a secret that the user created. If secret is created outside of the helm chart, make sure the key for token is 'runner_token' in the secret. The chart references this key when passing token as env variable. | | synthetics.secret.runnerToken | string | `""` | Used when sythentics.secret.create=true. The runner's token available in Splunk Observability when Private Location was created. | diff --git a/charts/splunk-synthetics-runner/templates/NOTES.txt b/charts/splunk-synthetics-runner/templates/NOTES.txt index 87f9be8..86ba60f 100644 --- a/charts/splunk-synthetics-runner/templates/NOTES.txt +++ b/charts/splunk-synthetics-runner/templates/NOTES.txt @@ -1,3 +1,14 @@ +{{/* Throws an error when the token is not provided. Not an exhaustive check of +all the different ways a user could add an env variable to container, +so might need to be removed later if requested by users. +If none of the below checks pass, we throw an error - +1. Check if token var has value and secret creation is enabled +2. Check is a custom secret name is provided with in chart secret creation disabled +3. Check if token is set as an env var directly */}} +{{- if not (or (and .Values.synthetics.secret.create .Values.synthetics.secret.runnerToken) (and (not .Values.synthetics.secret.create) (or .Values.synthetics.secret.name (hasKey .Values.env "RUNNER_TOKEN")))) }} +{{ fail "[ERROR] Splunk Synthetic Private Location Runner requires token to be supplied in the environment variable RUNNER_TOKEN. Users can provide this as value to the variable synthetics.secret.runnerToken or supply the secret name which contains the token in the variable synthetics.secret.name" }} +{{- end }} + Check the status of Splunk Synthetic Private Location deployment by running this comamnd: kubectl get deployments --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "splunk-synthetics-runner.fullname" . }},app.kubernetes.io/instance={{ .Release.Name }}" diff --git a/charts/splunk-synthetics-runner/templates/_helpers.tpl b/charts/splunk-synthetics-runner/templates/_helpers.tpl index fd152ca..e8fa3e3 100644 --- a/charts/splunk-synthetics-runner/templates/_helpers.tpl +++ b/charts/splunk-synthetics-runner/templates/_helpers.tpl @@ -101,7 +101,33 @@ Render security context Render names compliant with DNS label standard as defined in RFC 1123 */}} {{- define "cleanupNames" -}} - {{- $name := regexReplaceAll "\\W+" . "-" | lower -}} + {{- $name := regexReplaceAll "[^A-Za-z0-9\\-]" . "-" | lower -}} {{- $name = regexReplaceAll "^-+|-+$" $name "" | trunc 63 | trimSuffix "-" -}} {{- $name -}} {{- end -}} + + +{{/* +Render pod annotations. +Checksums are calculated for secret and additionalCaCerts if they exist. This +checksum is further used to trigger a rolling update when the secret/configmap +changes. The checksum is stored in the pod annotation `checksum/config`. +*/}} +{{- define "splunk-synthetics-runner.podAnnotations" -}} +{{- $checksums := list -}} +{{- if and .Values.synthetics.secret.create .Values.synthetics.token -}} +{{- $checksums = append $checksums (include (print .Template.BasePath "/secret.yaml") . | sha256sum) }} +{{- end -}} +{{- if .Values.synthetics.additionalCaCerts -}} +{{- $checksums = append $checksums (include (print .Template.BasePath "/configmap-ca.yaml" ) . | sha256sum) }} +{{- end -}} +{{- if or $checksums .Values.podAnnotations -}} +annotations: +{{- if $checksums }} + checksum/config: {{ (join "" $checksums) | sha256sum }} +{{- end -}} +{{- if .Values.podAnnotations -}} +{{- toYaml .Values.podAnnotations | nindent 2 }} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/splunk-synthetics-runner/templates/deployment.yaml b/charts/splunk-synthetics-runner/templates/deployment.yaml index b5bb579..a2bd0b5 100644 --- a/charts/splunk-synthetics-runner/templates/deployment.yaml +++ b/charts/splunk-synthetics-runner/templates/deployment.yaml @@ -5,9 +5,7 @@ metadata: labels: {{- include "splunk-synthetics-runner.labels" . | nindent 4 }} spec: - {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} - {{- end }} {{- if .Values.updateStrategy }} strategy: {{- toYaml .Values.updateStrategy | nindent 4 }} @@ -17,18 +15,17 @@ spec: {{- include "splunk-synthetics-runner.selectorLabels" . | nindent 6 }} template: metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} labels: {{- include "splunk-synthetics-runner.podLabels" . | nindent 8 }} + {{- include "splunk-synthetics-runner.podAnnotations" . | nindent 6 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} + {{- if or .Values.serviceAccount.create .Values.serviceAccount.name }} serviceAccountName: {{ include "splunk-synthetics-runner.serviceAccountName" . }} + {{- end}} {{- if .Values.automountServiceAccountToken }} automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} {{- end }} @@ -50,12 +47,31 @@ spec: hostAliases: {{- toYaml . | nindent 8 }} {{- end }} - containers: - - name: {{ .Chart.Name }} - {{- if .Values.synthetics.additionalCaCerts }} + {{- if .Values.synthetics.additionalCaCerts }} + initContainers: + - name: update-ca-certificates + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} command: ["/usr/bin/sh", "-c", "--"] - args: ["sudo update-ca-certificates; exec /usr/bin/tini -s -- bundle exec bin/start_runner"] + args: ["sudo update-ca-certificates && cp -r /etc/ssl/certs/* /cacerts/"] + securityContext: + allowPrivilegeEscalation: true + volumeMounts: + {{- if .Values.synthetics.additionalCaCerts }} + {{- range $cert := keys .Values.synthetics.additionalCaCerts | sortAlpha }} + {{- $crtFile := regexReplaceAll "^(.*)\\.(\\w+)$" $cert "${1}.crt" }} + - name: {{ include "cleanupNames" $cert }} + mountPath: {{ printf "/usr/local/share/ca-certificates/%s" $crtFile }} + subPath: {{ $crtFile }} + readOnly: false + {{- end }} {{- end }} + - name: cacerts + mountPath: /cacerts/ + readOnly: false + {{- end }} + containers: + - name: {{ .Chart.Name }} env: {{- if not .Values.synthetics.enableNetworkShaping }} - name: DISABLE_NETWORK_SHAPING @@ -63,11 +79,13 @@ spec: {{- end }} - name: LOG_LEVEL value: {{ .Values.synthetics.logLevel | upper }} + {{- if or .Values.synthetics.secret.create .Values.synthetics.secret.name }} - name: RUNNER_TOKEN valueFrom: secretKeyRef: name: {{ include "splunk-synthetics-runner.secretName" . }} key: runner_token + {{- end }} {{- range $key, $value := .Values.env }} - name: {{ $key }} value: {{ $value | quote }} @@ -92,12 +110,9 @@ spec: {{- if or .Values.synthetics.additionalCaCerts .Values.volumeMounts }} volumeMounts: {{- if .Values.synthetics.additionalCaCerts }} - {{- range $cert := keys .Values.synthetics.additionalCaCerts | sortAlpha }} - - name: {{ include "cleanupNames" $cert }} - mountPath: {{ printf "/usr/local/share/ca-certificates/%s" $cert }} - subPath: {{ $cert }} + - name: cacerts + mountPath: /etc/ssl/certs readOnly: false - {{- end }} {{- end }} {{- with .Values.volumeMounts }} {{- toYaml . | nindent 12 }} @@ -108,13 +123,16 @@ spec: volumes: {{- if .Values.synthetics.additionalCaCerts }} {{- range $cert := keys .Values.synthetics.additionalCaCerts | sortAlpha }} + {{- $crtFile := regexReplaceAll "^(.*)\\.(\\w+)$" $cert "${1}.crt" }} - name: {{ include "cleanupNames" $cert }} configMap: name: {{ include "splunk-synthetics-runner.fullname" $ }} items: - key: {{ $cert }} - path: {{ $cert }} + path: {{ $crtFile }} {{- end }} + - name: cacerts + emptyDir: {} {{- end }} {{- with .Values.volumes }} {{- toYaml . | nindent 8 }} diff --git a/charts/splunk-synthetics-runner/templates/hpa.yaml b/charts/splunk-synthetics-runner/templates/hpa.yaml deleted file mode 100644 index c361231..0000000 --- a/charts/splunk-synthetics-runner/templates/hpa.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "splunk-synthetics-runner.fullname" . }} - labels: - {{- include "splunk-synthetics-runner.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "splunk-synthetics-runner.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/splunk-synthetics-runner/templates/secret.yaml b/charts/splunk-synthetics-runner/templates/secret.yaml index 9c00919..79361ee 100644 --- a/charts/splunk-synthetics-runner/templates/secret.yaml +++ b/charts/splunk-synthetics-runner/templates/secret.yaml @@ -7,5 +7,5 @@ metadata: {{- include "splunk-synthetics-runner.labels" . | nindent 4 }} type: Opaque data: - runner_token: {{ .Values.synthetics.secret.runnerToken }} + runner_token: {{ .Values.synthetics.secret.runnerToken | b64enc | quote }} {{- end -}} diff --git a/charts/splunk-synthetics-runner/values.schema.json b/charts/splunk-synthetics-runner/values.schema.json index d5b5555..407ae2b 100644 --- a/charts/splunk-synthetics-runner/values.schema.json +++ b/charts/splunk-synthetics-runner/values.schema.json @@ -21,7 +21,6 @@ "livenessProbe", "automountServiceAccountToken", "serviceAccount", - "autoscaling", "terminationGracePeriodSeconds", "env", "volumes", @@ -194,7 +193,7 @@ "default": {}, "title": "The additionalCaCerts Schema", "examples": [ - {"example.com.cert": "-----BEGIN CERTIFICATE-----\nMIIBIjANBgkqAQ8AMIIBCgKCAQEAwJw..."} + {"example.com.crt": "-----BEGIN CERTIFICATE-----\nMIIBIjANBgkqAQ8AMIIBCgKCAQEAwJw..."} ] }, "secret": { @@ -254,26 +253,20 @@ "type": "object", "default": {}, "title": "The resources Schema", - "required": [ - "limits", - "requests" - ], "properties": { "limits": { "type": "object", "default": {}, "title": "The limits Schema", - "required": [ - "cpu", - "memory" - ], "properties": { "cpu": { - "type": "integer", - "default": 0, + "type": [ + "string", + "integer" + ], "title": "The cpu Schema", "examples": [ - 2 + 2, "500m" ] }, "memory": { @@ -294,17 +287,15 @@ "type": "object", "default": {}, "title": "The requests Schema", - "required": [ - "cpu", - "memory" - ], "properties": { "cpu": { - "type": "integer", - "default": 0, + "type": [ + "string", + "integer" + ], "title": "The cpu Schema", "examples": [ - 2 + 2, "500m" ] }, "memory": { @@ -443,67 +434,6 @@ "name": "" }] }, - "autoscaling": { - "type": "object", - "default": {}, - "title": "The autoscaling Schema", - "required": [ - "enabled", - "minReplicas", - "maxReplicas", - "targetCPUUtilizationPercentage", - "targetMemoryUtilizationPercentage" - ], - "properties": { - "enabled": { - "type": "boolean", - "default": false, - "title": "The enabled Schema", - "examples": [ - false - ] - }, - "minReplicas": { - "type": "integer", - "default": 0, - "title": "The minReplicas Schema", - "examples": [ - 1 - ] - }, - "maxReplicas": { - "type": "integer", - "default": 0, - "title": "The maxReplicas Schema", - "examples": [ - 6 - ] - }, - "targetCPUUtilizationPercentage": { - "type": "integer", - "default": 0, - "title": "The targetCPUUtilizationPercentage Schema", - "examples": [ - 95 - ] - }, - "targetMemoryUtilizationPercentage": { - "type": "integer", - "default": 0, - "title": "The targetMemoryUtilizationPercentage Schema", - "examples": [ - 95 - ] - } - }, - "examples": [{ - "enabled": false, - "minReplicas": 1, - "maxReplicas": 6, - "targetCPUUtilizationPercentage": 95, - "targetMemoryUtilizationPercentage": 95 - }] - }, "terminationGracePeriodSeconds": { "type": "integer", "default": 0, @@ -601,8 +531,7 @@ "default": {}, "title": "The podDisruptionBudget Schema", "required": [ - "enabled", - "minAvailable" + "enabled" ], "properties": { "enabled": { @@ -614,11 +543,17 @@ ] }, "minAvailable": { - "type": "integer", - "default": 0, + "type": ["integer", "string"], "title": "The minAvailable Schema", "examples": [ - 1 + 1, "25%" + ] + }, + "maxUnavailable": { + "type": ["integer", "string"], + "title": "The maxUnavailable Schema", + "examples": [ + 1, "25%" ] } }, @@ -677,13 +612,6 @@ "create": true, "name": "" }, - "autoscaling": { - "enabled": false, - "minReplicas": 1, - "maxReplicas": 6, - "targetCPUUtilizationPercentage": 95, - "targetMemoryUtilizationPercentage": 95 - }, "terminationGracePeriodSeconds": 10, "env": {}, "volumes": [], diff --git a/charts/splunk-synthetics-runner/values.yaml b/charts/splunk-synthetics-runner/values.yaml index 83809be..b247d43 100644 --- a/charts/splunk-synthetics-runner/values.yaml +++ b/charts/splunk-synthetics-runner/values.yaml @@ -54,11 +54,13 @@ synthetics: # Available values are: debug, info, warn, error logLevel: info - # -- Add custom CA certs to use in API/HTTP tests. Requires privilege escalation. + # -- Add custom CA certs (should be in PEM format) to use in API/HTTP tests. + # Requires privilege escalation in an init container which adds these certs + # to the runner's system cacerts. additionalCaCerts: {} + # -- Private location token configuration. - # Rotating the runner token requires an explicit rollout/restart of the deployment. secret: # -- The name of the secret created by chart (if name is empty the default name is used) # or the name of a secret that the user created. @@ -113,19 +115,6 @@ serviceAccount: # -- Annotations to add to service account annotations: {} -# -- Configuration for HPA -autoscaling: - # -- Enable HPA - enabled: false - # -- Minimum replicas of runner - minReplicas: 1 - # -- Maximum replicas of runner - maxReplicas: 6 - # -- Target CPU utilization - targetCPUUtilizationPercentage: 95 - # -- Target Memory utilization - targetMemoryUtilizationPercentage: 95 - # -- Duration in seconds the pod needs to terminate gracefully. terminationGracePeriodSeconds: 10 diff --git a/cr.yaml b/cr.yaml new file mode 100644 index 0000000..ce73740 --- /dev/null +++ b/cr.yaml @@ -0,0 +1 @@ +generate-release-notes: true diff --git a/ct.yaml b/ct.yaml index 848a68d..4fcc139 100644 --- a/ct.yaml +++ b/ct.yaml @@ -1,8 +1,8 @@ # See https://github.com/helm/chart-testing#configuration remote: origin +target-branch: main chart-dirs: - charts -helm-extra-args: --timeout 300s +helm-extra-args: '--values=tests/ci_values.yaml timeout=600s' +helm-lint-extra-args: '--values=tests/ci_values.yaml' check-version-increment: false -validate-chart-schema: false -target-branch: main diff --git a/scripts/install-tools.sh b/scripts/install-tools.sh new file mode 100755 index 0000000..24edbc2 --- /dev/null +++ b/scripts/install-tools.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# Purpose: Installs or upgrades essential development tools. +# Notes: +# - Should be executed via the `make install-tools` command. +# - Supports macOS for installations via `brew install` +# Include the base utility functions for setting and debugging variables +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Function to install a tool +install() { + local tool=$1 + local type=$2 + + case $type in + brew) + install_brew "$tool" + ;; + helm_plugin) + install_helm_plugin "$tool" + ;; + *) + echo "Unsupported tool type: $type" + exit 1 + ;; + esac +} + +# Function to install a Homebrew-based tool +install_brew() { + if ! command -v brew &> /dev/null + then + echo "Homebrew could not be found. Please install Homebrew and try again." + return + fi + + local tool=$1 + local installed_version=$(brew list $tool --versions | awk '{print $2}') + local latest_version=$(brew info --json=v1 "$tool" | jq -r '.[0].versions.stable') + + if [ "$installed_version" == "$latest_version" ]; then + echo "$tool is already up to date (version $installed_version)." + return + elif [ ! -z "$installed_version" ] && [ "$installed_version" != "$latest_version" ]; then + echo "$tool $installed_version is installed. A new version $latest_version is available. Continuing for now..." + return + fi + echo "$tool (version $latest_version) is not installed, installing now..." + brew install $tool || echo "Failed to install $tool. Continuing..." +} + +# Function to install a helm plugin +install_helm_plugin() { + if ! command -v helm &> /dev/null + then + echo "Helm could not be found. Please install Helm and try again." + return + fi + local plugin="${1%%=*}" + local repo="${1#*=}" + local installed_version=$(helm plugin list | grep ${plugin} | awk '{print $2}') + + if [ -z "$installed_version" ]; then + echo "Helm plugin ${plugin} is not installed, installing now..." + helm plugin install ${repo} || echo "Failed to install plugin ${plugin}. Continuing..." + else + echo "Helm plugin ${plugin} (version: ${installed_version}) already installed." + fi +} + +# install brew-based tools +for tool in yamllint chart-testing helm kubectl pre-commit norwoodj/tap/helm-docs; do + install "$tool" brew +done + +# install helm plugin +install "unittest=https://github.com/helm-unittest/helm-unittest.git" helm_plugin + +echo "Tool installation process completed!" +exit 0 diff --git a/tests/ci_values.yaml b/tests/ci_values.yaml new file mode 100644 index 0000000..c1ecb33 --- /dev/null +++ b/tests/ci_values.yaml @@ -0,0 +1,4 @@ +synthetics: + secret: + create: true + runnerToken: "faketoken" \ No newline at end of file diff --git a/tests/unittests/configmap-ca_tests.yaml b/tests/unittests/configmap-ca_tests.yaml index 9f6aa32..75dc94f 100644 --- a/tests/unittests/configmap-ca_tests.yaml +++ b/tests/unittests/configmap-ca_tests.yaml @@ -2,8 +2,19 @@ suite: Test ConfigMap templates: - templates/configmap-ca.yaml - templates/deployment.yaml -values: - - ./values/configmap-ca_tests.yaml +set: + synthetics: + additionalCaCerts: + customer_test.com.cert: | + -----BEGIN CERTIFICATE----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7 + -----END CERTIFICATE----- + another.test.com.crt: | + -----BEGIN CERTIFICATE----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8 + -----END CERTIFICATE----- + podAnnotations: + key1: value1 tests: - it: should create a ConfigMap with the correct name templates: @@ -19,56 +30,98 @@ tests: - templates/configmap-ca.yaml asserts: - equal: - path: data["customer.test.com.cert"] + path: data["customer_test.com.cert"] value: | -----BEGIN CERTIFICATE----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7 -----END CERTIFICATE----- - equal: - path: data["another.test.com.cert"] - value: |- + path: data["another.test.com.crt"] + value: | -----BEGIN CERTIFICATE----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8 -----END CERTIFICATE----- - - it: should add correct volumeMounts to deployment + - it: should add initContainer to pod spec templates: - templates/deployment.yaml asserts: - equal: - path: spec.template.spec.containers[0].volumeMounts + path: spec.template.spec.initContainers[0].name + value: update-ca-certificates + - it: should add correct volumeMounts to initContainer, should not add extra volumeMounts to init containers + templates: + - templates/deployment.yaml + set: + volumeMounts: + - mountPath: /app/tmp + name: app-volume + asserts: + - equal: + path: spec.template.spec.initContainers[0].volumeMounts value: - - name: another-test-com-cert - mountPath: /usr/local/share/ca-certificates/another.test.com.cert - subPath: another.test.com.cert + - name: another-test-com-crt + mountPath: /usr/local/share/ca-certificates/another.test.com.crt + subPath: another.test.com.crt readOnly: false - name: customer-test-com-cert - mountPath: /usr/local/share/ca-certificates/customer.test.com.cert - subPath: customer.test.com.cert + mountPath: /usr/local/share/ca-certificates/customer_test.com.crt + subPath: customer_test.com.crt + readOnly: false + - name: cacerts + mountPath: /cacerts/ + readOnly: false + - it: should add correct volumeMounts to containers + templates: + - templates/deployment.yaml + set: + volumeMounts: + - mountPath: /app/tmp + name: app-volume + asserts: + - equal: + path: spec.template.spec.containers[0].volumeMounts + value: + - name: cacerts + mountPath: /etc/ssl/certs readOnly: false + - mountPath: /app/tmp + name: app-volume - it: should add correct volumes to deployment templates: - templates/deployment.yaml + set: + volumes: + - name: app-volume + emptyDir: + sizeLimit: 500Mi asserts: - equal: path: spec.template.spec.volumes value: - - name: another-test-com-cert + - name: another-test-com-crt configMap: name: RELEASE-NAME-splunk-synthetics-runner items: - - key: another.test.com.cert - path: another.test.com.cert + - key: another.test.com.crt + path: another.test.com.crt - name: customer-test-com-cert configMap: name: RELEASE-NAME-splunk-synthetics-runner items: - - key: customer.test.com.cert - path: customer.test.com.cert - - it: should add cmd/args to pod spec + - key: customer_test.com.cert + path: customer_test.com.crt + - name: cacerts + emptyDir: {} + - name: app-volume + emptyDir: + sizeLimit: 500Mi + - it: should add checksum and any additional annotation templates: - templates/deployment.yaml asserts: - - exists: - path: spec.template.spec.containers[0].args - - exists: - path: spec.template.spec.containers[0].command + - matchRegex: + path: spec.template.metadata.annotations["checksum/config"] + pattern: "^[A-Fa-f0-9]{64}$" + - equal: + path: spec.template.metadata.annotations.key1 + value: value1 diff --git a/tests/unittests/deployment_tests.yaml b/tests/unittests/deployment_tests.yaml new file mode 100644 index 0000000..f99f4ca --- /dev/null +++ b/tests/unittests/deployment_tests.yaml @@ -0,0 +1,208 @@ +suite: Test Deployment +templates: + - templates/deployment.yaml +tests: + - it: should render deployment with correct metadata + asserts: + - isKind: + of: Deployment + - matchRegex: + path: metadata.name + pattern: ".*splunk-synthetics-runner$" + - it: should render deployment with security context when traffic shaping is enabled + set: + sythenetics.enableNetworkShaping: true + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext + value: + capabilities: + add: + - NET_ADMIN + allowPrivilegeEscalation: true + - it: should render deployment when traffic shaping is disabled + set: + synthetics.enableNetworkShaping: false + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext + value: {} + - contains: + path: spec.template.spec.containers[0].env + content: + name: DISABLE_NETWORK_SHAPING + value: "true" + any: true + - it: should render deployment with correct log level + set: + synthetics.logLevel: debug + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: LOG_LEVEL + value: DEBUG + any: true + - it: should render deployment with correct selectors and replicas + set: + replicaCount: 10 + commonLabels: + key1: value1 + asserts: + - equal: + path: spec.replicas + value: 10 + - equal: + path: spec.selector.matchLabels + value: + app.kubernetes.io/name: splunk-synthetics-runner + app.kubernetes.io/instance: RELEASE-NAME + key1: value1 + - it: should render deployment with correct pod labels and annotations + set: + commonLabels: + key1: value1 + podLabels: + key2: value2 + podAnnotations: + key3: value3 + asserts: + - equal: + path: spec.template.metadata.labels.key1 + value: value1 + - equal: + path: spec.template.metadata.labels.key2 + value: value2 + - equal: + path: spec.template.metadata.annotations + value: + key3: value3 + - it: should render deployment with correct container image + set: + image: + tag: 1.0.0 + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: quay.io/signalfx/splunk-synthetics-runner:1.0.0 + - it: should render deployment with correct container resources + set: + resources: &valResources + limits: + cpu: 1 + memory: 4Gi + requests: + cpu: 500m + memory: 2Gi + asserts: + - equal: + path: spec.template.spec.containers[0].resources + value: *valResources + - it: should render deployment with correct strategy + set: + updateStrategy: &valStrategy + type: RollingUpdate + rollingUpdate: + maxSurge: 25% + maxUnavailable: 1 + asserts: + - equal: + path: spec.strategy + value: *valStrategy + - it: should render deployment with nodeSelector, tolerations and affinity + set: + nodeSelector: + key: value + affinity: &valAffinity + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: key + operator: In + values: + - value + tolerations: &valTolerations + - key: key + operator: Equal + value: value + effect: NoSchedule + asserts: + - equal: + path: spec.template.spec.tolerations + value: *valTolerations + - equal: + path: spec.template.spec.nodeSelector + value: + key: value + - equal: + path: spec.template.spec.affinity + value: *valAffinity + - it: should render deployment with other customizable configs + set: + imagePullSecrets: + - name: secret1 + automountServiceAccountToken: true + dnsPolicy: ClusterFirst + dnsConfig: &valDnsConfig + nameservers: + - 192.0.2.1 + searches: + - ns1.svc.cluster-domain.example + priorityClassName: high + podSecurityContext: &valPodSecurityContext + fsGroup: 2000 + containerSecurityContext: + capabilities: + add: + - NET_RAW + hostAliases: &valHostAliases + - ip: "1.2.3.4" + hostnames: + - "private.domain.com" + volumes: &valVolumes + - name: app-volume + emptyDir: + sizeLimit: 500Mi + volumeMounts: &valVolumeMounts + - mountPath: /app/tmp + name: app-volume + asserts: + - equal: + path: spec.template.spec.imagePullSecrets + value: + - name: secret1 + - equal: + path: spec.template.spec.serviceAccountName + value: RELEASE-NAME-splunk-synthetics-runner + - equal: + path: spec.template.spec.automountServiceAccountToken + value: true + - equal: + path: spec.template.spec.dnsPolicy + value: ClusterFirst + - equal: + path: spec.template.spec.dnsConfig + value: *valDnsConfig + - equal: + path: spec.template.spec.priorityClassName + value: high + - equal: + path: spec.template.spec.securityContext + value: *valPodSecurityContext + - equal: + path: spec.template.spec.containers[0].securityContext + value: + capabilities: + add: + - NET_RAW + - NET_ADMIN + - equal: + path: spec.template.spec.hostAliases + value: *valHostAliases + - equal: + path: spec.template.spec.volumes + value: *valVolumes + - equal: + path: spec.template.spec.containers[0].volumeMounts + value: *valVolumeMounts diff --git a/tests/unittests/notes_tests.yaml b/tests/unittests/notes_tests.yaml new file mode 100644 index 0000000..c1d6842 --- /dev/null +++ b/tests/unittests/notes_tests.yaml @@ -0,0 +1,29 @@ +suite: Test Notes file +templates: + - templates/NOTES.txt +tests: + - it: should fail the notes file with invalid combination of token/secret variables + set: + synthetics.secret.create: false + asserts: + - failedTemplate: + errorPattern: "[ERROR]" + - it: should not fail the notes when token is provided + set: + synthetics.secret.create: true + synthetics.secret.runnerToken: "faketoken" + asserts: + - notFailedTemplate: {} + - it: should not fail the notes when custom secret name is provided + set: + synthetics.secret.create: false + synthetics.secret.name: "secretname" + asserts: + - notFailedTemplate: {} + - it: should not fail the notes when RUNNER_TOKEN is given as an env var + set: + env: + RUNNER_TOKEN: "faketoken" + synthetics.secret.create: false + asserts: + - notFailedTemplate: {} \ No newline at end of file diff --git a/tests/unittests/pdb_tests.yaml b/tests/unittests/pdb_tests.yaml new file mode 100644 index 0000000..8ad8a8b --- /dev/null +++ b/tests/unittests/pdb_tests.yaml @@ -0,0 +1,37 @@ +suite: Test PodDisruptionBudget +templates: + - templates/pdb.yaml +tests: + - it: should render PodDisruptionBudget with correct metadata and spec + set: + commonLabels: + key1: value1 + podDisruptionBudget: + enabled: true + minAvailable: 1 + asserts: + - isKind: + of: PodDisruptionBudget + - matchRegex: + path: metadata.name + pattern: ".*splunk-synthetics-runner$" + - equal: + path: spec.minAvailable + value: 1 + - equal: + path: spec.selector.matchLabels + value: + app.kubernetes.io/name: splunk-synthetics-runner + app.kubernetes.io/instance: RELEASE-NAME + key1: value1 + - equal: + path: metadata.labels.key1 + value: value1 + - it: should not render PodDisruptionBudget + set: + podDisruptionBudget: + enabled: false + asserts: + - hasDocuments: + count: 0 + template: templates/pdb.yaml \ No newline at end of file diff --git a/tests/unittests/secret_tests.yaml b/tests/unittests/secret_tests.yaml new file mode 100644 index 0000000..5ad253d --- /dev/null +++ b/tests/unittests/secret_tests.yaml @@ -0,0 +1,56 @@ +suite: Test Secret +templates: + - templates/secret.yaml + - templates/deployment.yaml +tests: + - it: should render secret and reference token in env var in deployment + set: + synthetics: + secret: + create: true + runnerToken: "faketoken" + templates: + - templates/secret.yaml + asserts: + - isKind: + of: Secret + - equal: + path: metadata.name + value: RELEASE-NAME-splunk-synthetics-runner + - equal: + path: data.runner_token + value: "ZmFrZXRva2Vu" + - contains: + path: spec.template.spec.containers[0].env + content: + name: RUNNER_TOKEN + valueFrom: + secretKeyRef: + name: RELEASE-NAME-splunk-synthetics-runner + key: runner_token + any: true + template: templates/deployment.yaml + - matchRegex: + path: spec.template.metadata.annotations["checksum/config"] + pattern: "^[A-Fa-f0-9]{64}$" + template: templates/deployment.yaml + - it: should not render secret but reference provided secret name in deployment + set: + synthetics: + secret: + create: false + name: custom-secret + asserts: + - hasDocuments: + count: 0 + template: templates/secret.yaml + - contains: + path: spec.template.spec.containers[0].env + content: + name: RUNNER_TOKEN + valueFrom: + secretKeyRef: + name: custom-secret + key: runner_token + any: true + template: templates/deployment.yaml diff --git a/tests/unittests/serviceaccount_tests.yaml b/tests/unittests/serviceaccount_tests.yaml new file mode 100644 index 0000000..4266775 --- /dev/null +++ b/tests/unittests/serviceaccount_tests.yaml @@ -0,0 +1,54 @@ +suite: Test ServiceAccount +templates: + - templates/serviceaccount.yaml + - templates/deployment.yaml +tests: + - it: should render serviceaccount + templates: + - templates/serviceaccount.yaml + set: + commonLabels: + key1: value1 + serviceAccount: + create: true + annotations: + key1: value1 + asserts: + - isKind: + of: ServiceAccount + - matchRegex: + path: metadata.name + pattern: ".*splunk-synthetics-runner$" + - equal: + path: metadata.annotations + value: + key1: value1 + - matchRegex: + path: spec.template.spec.serviceAccountName + pattern: ".*splunk-synthetics-runner$" + template: templates/deployment.yaml + - it: should not render serviceaccount + set: + serviceAccount: + create: false + asserts: + - hasDocuments: + count: 0 + template: templates/serviceaccount.yaml + - notExists: + path: spec.template.spec.serviceAccountName + template: templates/deployment.yaml + - it: should not render serviceaccount and use provided service account name in deployment + set: + serviceAccount: + create: false + name: custom-service-account + asserts: + - hasDocuments: + count: 0 + template: templates/serviceaccount.yaml + - equal: + path: spec.template.spec.serviceAccountName + value: custom-service-account + template: templates/deployment.yaml + diff --git a/tests/unittests/values/configmap-ca_tests.yaml b/tests/unittests/values/configmap-ca_tests.yaml deleted file mode 100644 index df41e94..0000000 --- a/tests/unittests/values/configmap-ca_tests.yaml +++ /dev/null @@ -1,10 +0,0 @@ -synthetics: - additionalCaCerts: - customer.test.com.cert: | - -----BEGIN CERTIFICATE----- - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7 - -----END CERTIFICATE----- - another.test.com.cert: | - -----BEGIN CERTIFICATE----- - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8 - -----END CERTIFICATE----- \ No newline at end of file