diff --git a/.github/workflows/chart-testing.yaml b/.github/workflows/chart-testing.yaml index eaeba0c..4db6975 100644 --- a/.github/workflows/chart-testing.yaml +++ b/.github/workflows/chart-testing.yaml @@ -3,6 +3,7 @@ name: Lint and Test Charts on: pull_request jobs: + lint-test: runs-on: ubuntu-latest steps: @@ -16,6 +17,38 @@ jobs: with: version: v3.14.0 + - name: Set up kubectl + uses: azure/setup-kubectl@v4 + with: + version: latest + id: install + + - name: Create kind cluster + uses: helm/kind-action@v1.9.0 + with: + node_image: kindest/node:v1.30.2 + + - name: Validate chart template with default values + run: | + helm repo add argo-helm-charts https://chanzuckerberg.github.io/argo-helm-charts/ + helm dependency update ./stack + helm dependency build ./stack + output=$(helm template ./stack --values ./test-fixtures/values.yaml | kubectl apply -f - --dry-run=client 2>&1 || :) + if [[ $output -ne "error: no objects passed to apply" ]]; then + exit 1 + fi + + - name: Validate chart template with invalid values + run: | + output=$(helm template ./stack --values ./test-fixtures/values1.yaml | kubectl apply -f - --dry-run=client 2>&1 || :) + if [[ $output -ne "Error: failed to parse ./test-fixtures/values1.yaml: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}" ]]; then + exit 1 + fi + + - name: Validate chart template with valid values + run: | + helm template ./stack --values ./test-fixtures/values2.yaml | kubectl apply -f - --dry-run=client 2>&1 + - uses: actions/setup-python@v4 with: python-version: '3.12' @@ -27,7 +60,7 @@ jobs: - name: Run chart-testing (list-changed) id: list-changed run: | - changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }}) + changed=$(ct list-changed --chart-dirs . --target-branch ${{ github.event.repository.default_branch }}) if [[ -n "$changed" ]]; then echo "changed=true" >> "$GITHUB_OUTPUT" fi @@ -36,10 +69,6 @@ jobs: if: steps.list-changed.outputs.changed == 'true' run: ct lint --target-branch ${{ github.event.repository.default_branch }} - - name: Create kind cluster - if: steps.list-changed.outputs.changed == 'true' - uses: helm/kind-action@v1.8.0 - - name: Run chart-testing (install) if: steps.list-changed.outputs.changed == 'true' run: ct install --target-branch ${{ github.event.repository.default_branch }} \ No newline at end of file diff --git a/test-fixtures/values.yaml b/test-fixtures/values.yaml new file mode 100644 index 0000000..8fe13e1 --- /dev/null +++ b/test-fixtures/values.yaml @@ -0,0 +1,184 @@ +# Service defaults +global: + replicaCount: 1 + + # Settings for the primary container + image: + repository: nginx + pullPolicy: IfNotPresent + tag: "latest" + + args: [] + command: [] + + imagePullSecrets: [] + nameOverride: "" + fullnameOverride: "" + + dnsPolicy: ClusterFirst + restartPolicy: Always + + # Probes for the primary container + livenessProbe: + failureThreshold: 3 + httpGet: + path: / + port: http + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + initialDelaySeconds: 30 + readinessProbe: + failureThreshold: 3 + httpGet: + path: / + port: http + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + initialDelaySeconds: 30 + startupProbe: + enabled: false + failureThreshold: 3 + successThreshold: 1 + initialDelaySeconds: 0 + timeoutSeconds: 1 + periodSeconds: 10 + exec: + command: + - ps + - '-ef' + + resources: + limits: + cpu: '1' + memory: '1Gi' + requests: + cpu: '100m' + memory: '128Mi' + + service: + type: ClusterIP + port: 80 + + initContainers: [] + sidecars: [] + + appConfig: + envContextConfigMapName: "" # App environment level configuration configmap name + stackContextConfigMapName: "" # Stack level configuration configmap name + envSecretName: "" # App environment level configuration secret name + stackSecretName: "" # Stack level configuration secret name + + # Global annotations to add to all resources + annotations: {} + # Annotations to add to pods + podAnnotations: {} + # Labels to add to pods + podLabels: {} + + serviceAccount: + # Specifies whether a service account should be created + create: false + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + + podSecurityContext: {} + # fsGroup: 2000 + + securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + + ingress: + enabled: true + className: "" + host: chart-example.local + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + + # Additional volumes on the output Deployment definition. + volumes: [] + # - name: foo + # secret: + # secretName: mysecret + # optional: false + + # Additional volumeMounts on the output Deployment definition. + volumeMounts: [] + # - name: foo + # mountPath: "/etc/foo" + # readOnly: true + + nodeSelector: + kubernetes.io/arch: arm64 + + tolerations: [] + + affinity: {} + + topologySpreadConstraints: [] + oidcProxy: + enabled: false + +# Service overrides +services: {} + # service1: + # args: ["arg1", "arg2"] + # command: ["command1", "command2"] + # autoscaling: + # enabled: true + # minReplicas: 2 + # maxReplicas: 10 + # maxUnavailable: 1 + # replicaCount: 2 + # sidecars: + # - name: sidecar1 + # image: "sidecar1:latest" + # - name: sidecar2 + # image: "sidecar2:latest" + # initContainers: + # - name: initContainer1 + # image: "alpine:latest" + # command: ["echo", "Hello World"] + # service2: + # startupProbe: + # enabled: true + # autoscaling: + # enabled: true + # minReplicas: 2 + # maxReplicas: 10 + # maxUnavailable: 1 + # replicaCount: 2 + # sidecars: + # - name: sidecar3 + # image: sidecar3:latest + # - name: sidecar4 + # image: sidecar4:latest \ No newline at end of file diff --git a/test-fixtures/values1.yaml b/test-fixtures/values1.yaml new file mode 100644 index 0000000..6f4f765 --- /dev/null +++ b/test-fixtures/values1.yaml @@ -0,0 +1 @@ +$ \ No newline at end of file diff --git a/test-fixtures/values2.yaml b/test-fixtures/values2.yaml new file mode 100644 index 0000000..5e14e39 --- /dev/null +++ b/test-fixtures/values2.yaml @@ -0,0 +1,168 @@ +# Service defaults +global: + replicaCount: 1 + + # Settings for the primary container + image: + repository: nginx + pullPolicy: IfNotPresent + tag: "latest" + + args: [] + command: [] + + imagePullSecrets: [] + nameOverride: "" + fullnameOverride: "" + + dnsPolicy: ClusterFirst + restartPolicy: Always + + # Probes for the primary container + livenessProbe: + failureThreshold: 3 + httpGet: + path: / + port: http + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + initialDelaySeconds: 30 + readinessProbe: + failureThreshold: 3 + httpGet: + path: / + port: http + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + initialDelaySeconds: 30 + startupProbe: + enabled: false + failureThreshold: 3 + successThreshold: 1 + initialDelaySeconds: 0 + timeoutSeconds: 1 + periodSeconds: 10 + exec: + command: + - ps + - '-ef' + + resources: + limits: + cpu: '1' + memory: '1Gi' + requests: + cpu: '100m' + memory: '128Mi' + + service: + type: ClusterIP + port: 80 + + initContainers: [] + sidecars: [] + + appConfig: + envContextConfigMapName: "" # App environment level configuration configmap name + stackContextConfigMapName: "" # Stack level configuration configmap name + envSecretName: "" # App environment level configuration secret name + stackSecretName: "" # Stack level configuration secret name + + # Global annotations to add to all resources + annotations: {} + # Annotations to add to pods + podAnnotations: {} + # Labels to add to pods + podLabels: {} + + serviceAccount: + # Specifies whether a service account should be created + create: false + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + + podSecurityContext: {} + # fsGroup: 2000 + + securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + + ingress: + enabled: true + className: "" + host: chart-example.local + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + + # Additional volumes on the output Deployment definition. + volumes: [] + # - name: foo + # secret: + # secretName: mysecret + # optional: false + + # Additional volumeMounts on the output Deployment definition. + volumeMounts: [] + # - name: foo + # mountPath: "/etc/foo" + # readOnly: true + + nodeSelector: + kubernetes.io/arch: arm64 + + tolerations: [] + + affinity: {} + + topologySpreadConstraints: [] + +# Service overrides +services: + service1: + args: ["arg1", "arg2"] + command: ["command1", "command2"] + autoscaling: + enabled: true + minReplicas: 2 + maxReplicas: 10 + maxUnavailable: 1 + replicaCount: 2 + sidecars: + - name: sidecar1 + image: "sidecar1:latest" + - name: sidecar2 + image: "sidecar2:latest" + initContainers: + - name: initContainer1 + image: "alpine:latest" + command: ["echo", "Hello World"] \ No newline at end of file