Giter VIP home page Giter VIP logo

helm-chartsnap's Introduction

helm-chartsnap ๐Ÿ“ธ

Go Reference Go Report Card codecov Workflow Status CI GitHub Marketplace

Snapshot testing tool for Helm charts.

Bring powerful UI testing capabilities for Helm chart with minimal configuration just within values.yaml file.

screenshot

Overview

Features ๐Ÿ”‘

  • Easy & Intuitive Snapshot testing for Helm developers

    • No tool-specific TestSuite file format: Unlike some tools that require the creation of separate TestSuites with custom file formats, this tool doesn't require any additional file formats. Instead, you can prepare variations of the standard values.yaml file for each test case. This eliminates the need to learn new formats and simplifies testing.
  • Optimized for Helm chart

    • Handling dynamic output generated by helm functions: You can specify additional properties within the values.yaml file for each test case to provide flexibility in adjusting test behavior. For example, you can control the output dynamically generated by helm functions like randAlphaNum.
  • Continuous Snapshot testing

    • GitHub Action is ready: GitHub action which enables to do continuous snapshot testing for local or remote Helm charts in your CI is ready ๐Ÿš€ -> helm-chart-action

Why Snapshot Testing? ๐Ÿ“ธ

I believe that snapshot testing, a popular testing approach in Web or UI testing with tools like Jest, is the best way to test Helm charts. Snapshot testing provides several benefits:

  • Visual comparisons: Snapshot tests create a baseline output and then compare it to subsequent test results. This allows for easy visual inspection of changes and regressions in Helm chart rendering.

  • Enhanced maintainability: Snapshot testing simplifies the test setup process, making it more accessible and maintainable, especially when dealing with complex Helm charts. The input for the Helm chart is a combination of values. We need only maintain the assumed input combinations(=values.yaml), and their snapshots.

  • Continuous testing: By continuously taking snapshots of the Helm chart in your CI process, you can quickly notice the changes in your whole chart and avoid regressions.

These advantages are not limited to the Helm chart you created, but also the third-party charts you have installed on your Kubernetes if you are a Kubernetes administrator. When upgrading a cluster, you can notice any changes in the output manifests produced by your values, no matter how minor.

Motivation

There are existing Helm testing tools that support snapshot tests.

The official Helm test tool ct is not for unit tests or snapshot tests but for lint and integration test

While they are valuable tools for testing Helm charts, it has certain limitations for snapshot testing.

  • You need to learn and prepare the Test Suite files of the tools even if only for snapshot testing.

  • Not snapshot the whole chart by default, but designed to test on a per-file basis within the Helm chart.

  • Helm function's dynamic outputs are not considered.

helm-unittest is designed that conducts tests on a per-file basis within the Helm chart. This means that it evaluates each file independently, and when Helm chart values or conditions lead to an entire file being conditionally included or excluded, it can result in tests being unable to cover important scenarios.

Here is an example of a test spec file for snapshot testing in helm-unittest.

https://github.com/helm-unittest/helm-unittest#snapshot-testing

templates:
  - templates/deployment.yaml
tests:
  - it: pod spec should match snapshot
    asserts:
      - matchSnapshot:
          path: spec.template.spec
  # or you can snapshot the whole manifest
  - it: manifest should match snapshot
    asserts:
      - matchSnapshot: {}

For instance, consider a scenario where the Helm chart may create a Certificate resource of cert-manager when both TLS is enabled and cert-manager is enabled, but create a self-signed TLS Secret resource using helm function genSelfSignedCert when cert-manager is not enabled. Furthermore, whether TLS is enabled or not affects Ingress's TLS properties, Deployment's app-specific TLS options, etc.

In such cases, helm-unittest may not be able to adequately test the entire chart's behavior, as the output of these conditional files may be empty, leading to incomplete tests.

However, all you have to do with helm-chartsnap is just to prepare some values.yaml files of the patterns you expect ๐Ÿ˜Š

Installation ๐Ÿš€

You can install as Helm plugin.

helm plugin install https://github.com/jlandowner/helm-chartsnap

Other options:

Usage

Just pass your chart for a quick start.

helm chartsnap -c YOUR_CHART

__snapshot__ directory is created in the chart directory and default.snap is created.

Also, you can create test cases as a variation of the Values files of your chart.

Here is the full usage of the command.

Usage:
  chartsnap -c CHART [flags]

Examples:

  # Snapshot with default values:
  chartsnap -c YOUR_CHART
  
  # Update snapshot files:
  chartsnap -c YOUR_CHART -u

  # Snapshot with test case values:
  chartsnap -c YOUR_CHART -f YOUR_TEST_VALUES_FILE
  
  # Snapshot all test cases:
  chartsnap -c YOUR_CHART -f YOUR_TEST_VALUES_FILES_DIRECTOY
  
  # Set additional args or flags for the 'helm template' command:
  chartsnap -c YOUR_CHART -f YOUR_TEST_VALUES_FILE -- --skip-tests

  # Snapshot remote chart in Helm repository:
  chartsnap -c CHART_NAME -f YOUR_VALUES_FILE -- --repo HELM_REPO_URL

  # Snapshot ingress-nginx (https://kubernetes.github.io/ingress-nginx/) helm chart for a specific version with your value file:
  chartsnap -c ingress-nginx -f YOUR_VALUES_FILE -- --repo https://kubernetes.github.io/ingress-nginx --namespace kube-system --version 4.8.3

  # Snapshot cilium (https://cilium.io) helm chart with default value and set flags:
  chartsnap -c cilium -- --repo https://helm.cilium.io --namespace kube-system --set hubble.relay.enabled=true --set hubble.ui.enabled=true

  # Snapshot charts in OCI registry
  chartsnap -c oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric -n nginx-gateway

  # Output with no colors:
  NO_COLOR=1 chartsnap -c YOUR_CHART

Flags:
  -c, --chart string              path to the chart directory. this flag is passed to 'helm template RELEASE_NAME CHART --values VALUES' as 'CHART'
      --config-file string        config file name or path, which defines snapshot behavior e.g. dynamic fields (default ".chartsnap.yaml")
  -N, --ctx-lines int             number of lines to show in diff output. 0 for full output (default 3)
      --debug                     debug mode
      --fail-helm-error           fail if 'helm template' command failed
      --failfast                  fail once any test case failed
  -h, --help                      help for chartsnap
  -n, --namespace string          namespace. this flag is passed to 'helm template RELEASE_NAME CHART --values VALUES --namespace NAMESPACE' as 'NAMESPACE' (default "default")
  -o, --output-dir string         directory which is __snapshot__ directory is created. (default: values file directory if --values is set; chart directory if chart is local; else current directory)
      --parallelism int           test concurrency if taking multiple snapshots for a test value file directory. default is unlimited (default -1)
      --release-name string       release name. this flag is passed to 'helm template RELEASE_NAME CHART --values VALUES' as 'RELEASE_NAME' (default "chartsnap")
      --snapshot-version string   use a specific snapshot format version. v1, v2, v3 are supported. (default: latest)
  -u, --update-snapshot           update snapshot mode
  -f, --values string             path to a test values file or directory. if the directory is set, all test files are tested. if empty, default values are used. this flag is passed to 'helm template RELEASE_NAME CHART --values VALUES' as 'VALUES'
  -v, --version                   version for chartsnap

Handling dynamic values ๐Ÿ’ช

Helm has a feature called "Helm functions" that generates random values or self-signed certificates like randAlphaNum and genCA, genPrivateKey, genSelfSignedCert

When using these functions, the output will vary each time Helm is executed. For this case, chartsnap has a feature that prevents mismatched snapshots by substituting them with a fixed value, ###DYNAMIC_FIELD###.

You can specify the paths of dynamic values in the generated YAML using JSONPath.

dynamicFields:
  - apiVersion: v1
    kind: Secret
    name: cosmo-auth-env
    jsonPath:
      - /data/COOKIE_HASHKEY
      - /data/COOKIE_BLOCKKEY
      - /data/COOKIE_HASHKEY
      - /data/COOKIE_SESSION_NAME
    base64: true

NOTE: In JSONPath, escaping the / character is documented in RFC6901, section 3

   Because the characters '~' (%x7E) and '/' (%x2F) have special
   meanings in JSON Pointer, '~' needs to be encoded as '~0' and '/'
   needs to be encoded as '~1' when these characters appear in a
   reference token.

So if you handle a manifest like the following, you need to specify the json path as /metadata/annotaions/checksum~1values.

metadata:
  annotations:
    checksum/values: RANDOM_VALUES

If you have a test values file directory, place it as a .chartsnap.yaml file within that directory. It is a common behavior in all snapshot tests in the directory.

For more examples, see example/app1.

Alternatively, you can directly add the config in the test values file as a testSpec property. In this case, you don't need to prepare .chartsnap.yaml. It is for taking snapshots of remote or third-party charts with your single values file.

Your values file can be like this:

# Add this property to your values.
testSpec:
  dynamicFields:
    - apiVersion: v1
      kind: Secret
      name: cilium-ca
      jsonPath:
        - /data/ca.crt
        - /data/ca.key
      base64: true
    - apiVersion: v1
      kind: Secret
      name: hubble-server-certs
      jsonPath:
        - /data/ca.crt
        - /data/tls.crt
        - /data/tls.key
      base64: true
    - apiVersion: v1
      kind: Secret
      name: hubble-relay-client-certs
      jsonPath:
        - /data/ca.crt
        - /data/tls.crt
        - /data/tls.key
      base64: true

# Others can be any of your chart values.
# ...

For more examples, see example/remote.

Showcase & Users โœจ

Users / Projects Description URL
Example Example chart generated by helm create command example/app1
Example Example of snapshotting charts in remote Helm repositories example/remote
helm-chartsnap-action GitHub action to use helm chartsnap in CI https://github.com/jlandowner/helm-chartsnap-action
cosmo-workspace Open source WebIDE & DevEnvironment Platform on Kubernetes https://github.com/cosmo-workspace/cosmo/tree/main/charts/cosmo/test
Kong ๐Ÿฆ The Cloud-Native API Gateway and AI Gateway. https://github.com/Kong/charts

helm-chartsnap's People

Contributors

dependabot[bot] avatar florianrusch avatar github-actions[bot] avatar jlandowner avatar web-flow avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

helm-chartsnap's Issues

Add CLI options properties in`testSpec`

Support to run cmd without args such as chartsnap -f values.yaml by using the values file like:

testSpec:
  chart: cilium
  additionalArgs: |
    --repo https://helm.cilium.io
    --namespace kube-system
...

success even if chart is not found

After #110 chartsnap can take a error output snapshot with success exitcode. But also it unintentionally takes a error snapshot that chart is not found.

It should take only helm's rendering output(including rendering error) and it should exit with errors excluding rendering error.

Support user-provided fixed value for dynamic field

Option 1

dynamicFields:
  - apiVersion: v1
    kind: Secret
    name: cosmo-auth-env
    jsonPath:
      - /data/COOKIE_HASHKEY
      - /data/COOKIE_BLOCKKEY
      - /data/COOKIE_HASHKEY
      - /data/COOKIE_SESSION_NAME
    base64: true
    value: '###DYNAMIC_FIELD###' # Add

Option 2

dynamicFields:
  - apiVersion: v1
    kind: Secret
    name: cosmo-auth-env
    jsonPath:                         # Support both string and struct array
      - path: /data/COOKIE_HASHKEY    # Change as path property
        value: '###COOKIE_HASHKEY###'
      - path: /data/COOKIE_BLOCKKEY
        value: '###COOKIE_BLOCKKEY###' # This option can set values on each path.

Take an empty snapshot for empty Helm output

My default values for my chart do not produce any manifests. I have templates in there but they are all guarded by if statements.

helm chartsnap -c charts/project -u
 RUNS  Snapshot testing chart=charts/project values=
 FAIL  chart=charts/ace-project values= err=failed to encode manifests: failed to close encoder: yaml: expected STREAM-START
time=2024-04-09T14:12:24.545-04:00 level=ERROR msg="failed to get snapshot chart=charts/project values=: failed to encode manifests: failed to close encoder: yaml: expected STREAM-START"
Error: plugin "chartsnap" exited with error

dynamicFields with / in key

Hi,

I would like to use dynamicFields with kyes which has '/' characters.
Part of the templated yaml:

spec:
    template:
        metadata:
            annotations:
                checksum/values: 30da9af9da506b1fc3aa378d47e2d916f1d62e0e30dda84b25e855053233fd33

This is the .chartignore

dynamicFields:
  - apiVersion: apps/v1
    kind: Deployment
    name: cosmo-auth-env
    jsonPath:
      - /spec/template/metadata/annotaions/checksum/values

I got the following error:

chart=. values=test/test.yaml err=failed to replace json path: failed to replace json path: failed to patch JSON6902: replace operation does not apply: doc is missing path: /spec/template/metadata/annotaions/checksum/values: missing value:

I tried the following escape segments:
/spec/template/metadata/annotaions/"checksum/values"
/spec/template/metadata/annotaions/'checksum/values'
/spec/template/metadata/annotaions/checksum%2Fvalues

None of the worked for me.

How can I escape the / in the key character?

Thank you for your help!

Unknow warning

First of all, thank you so much for this amazing tool.
I'm creating this issue due since several days I'm facing this issue

WARN: failed to recognize a resource. snapshot as Unknown:
---
object:
    apiVersion: helm-chartsnap.jlandowner.dev/v1alpha1
    kind: Unknown
    raw: |
        coalesce.go:286: warning: cannot overwrite table with non table for certificate-exporter.hostPathsExporter.securityContext (map[capabilities:map[drop:[ALL]] readOnlyRootFilesystem:true runAsGroup:0 runAsUser:0])
        coalesce.go:286: warning: cannot overwrite table with non table for certificate-exporter.secretsExporter.securityContext (map[capabilities:map[drop:[ALL]] readOnlyRootFilesystem:true runAsGroup:65534 runAsUser:65534])

I don't know but maybe could be related with the array section
this is my values.yaml:

certificate-exporter:
  exposePerCertificateErrorMetrics: true
  exposeRelativeMetrics: true
  secretsExporter:
    securityContext: []    
  hostPathsExporter:
    securityContext: []

My helm version is 3.14.2

Failed to snapshot helm `required` error output

Hello again,

today I wanted to integrate helm-chartsnap into another project where I discovered an edge case that I'm not sure how to handle.

Let's imagine we have a template that contains the following:

testValue: {{ required "testValue is required" .Values.testValue }}

and a values.yaml that contains the following:

testValue:

Executing helm template and also helm chartsnap -c . would now result in the following "error" message:

Error: execution error at (<TEMPLATE_PATH>.yaml:1:3): testValue is required

The question now is, how should I handle this? I don't want to put a default value in the values.yaml because there is no "default" value as the value always depends on the environment the helm chart will applied to.

One idea would be to provide a separate "default" values.yaml file just for chartsnap. But this feels somehow ugly for me. Also I would need to provide testValue in each of my test values.yaml files which is very repetitive.

Another idea would be to somehow provide these kind of values within the .chartsnap.yaml, but I guess that's not supported at the moment, right?

Validator integration

@jlandowner thanks for this amazing tool!

I was looking for exactly something like this, because I work in a project with multiple different developers, which all have different levels of experiences with helm and how to write and modify helm charts.

I'm just wondering, if it's somehow possible to also integrate a validator tool to validate the generated yaml code against the kubernetes api. Did you or someone else already thought about something like that? It would be really easy, if the snapshots would be stored as plain yaml files.

Allow defining global dynamic fields rules

Context

I have a use case in which I would like to test multiple test values files located in a single directory (let's say, named tests). For every values file, my Helm chart generates Kubernetes Secrets with dynamically generated data. Secrets are always named the same. Now, to make my tests pass, I need to separately add the same testSpec.dynamicFields to every test values file which is redundant. I would like to be able to define global dynamicFields-like rules that would be used when generating snapshots for every values file in my tests directory.

Expected behavior

It's possible to set a global dynamic fields configuration that is used for every values file. Dynamic fields configuration is used to replace all dynamically generated content with ###DYNAMIC_FIELD### constant when the rule matches an object's field.

Sample command:

 helm chartsnap -c <chart> -f <directory-with-test-values-files> --dynamic-fields-config=./exclude-secrets-data.yaml 

Sample config file (exclude-secrets-data.yaml):

  dynamicFields:
    - apiVersion: v1
      kind: Secret
      name: chartsnap-postgresql
      jsonPath:
        - /data/postgres-password

Alternatively, this could be a config file used by the tool, e.g. .chartsnap.yaml in which the global configuration would be defined (I believe there could be more global settings to tune than just dynamicFields in the future).

[Feature] Warning message with indentation error

Hi,
I am writing to request a feature enhancement in your chartsnap plugin. Specifically, I would like the system to generate a warning when the indexing of YAML files is incorrect. For example, this example:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test
spec:
  replicas: 1
    spec:
      imagePullSecrets:
      - name: {{ .Values.imagePullSecrets }}

Currently, when there is an issue with the indexing of these files, it can lead to unexpected behavior in our application, which is difficult to debug due to the lack of explicit warnings or error messages.

Implementing this feature would greatly improve our ability to identify and resolve issues more efficiently, thereby improving the overall robustness of our application.

I look forward to your positive response. Because this approach, we could resolve a lot of issues in our debugging.

Thank you for all your help.
Best regards,
Sofรญa

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.