Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EPIC]: Add chart tests #125

Closed
21 tasks done
Tracked by #123
Zelldon opened this issue Jan 24, 2022 · 7 comments
Closed
21 tasks done
Tracked by #123

[EPIC]: Add chart tests #125

Zelldon opened this issue Jan 24, 2022 · 7 comments

Comments

@Zelldon
Copy link
Member

Zelldon commented Jan 24, 2022

We need to test our charts, to verify and ensure functionality and to guarantee that we not break them on adding more features or fixing bugs.

@Zelldon
Copy link
Member Author

Zelldon commented Feb 8, 2022

Based on #133 (comment) we create the following decision matrix. Please be aware that I tried to be objective as possible, but there might be some points more subjective like I wanted to be, e.g understandability etc..

Property \ Tool Template Test: Terratest Template Test: Golden Files IT: Terratest IT: CT and Helm test
Effort to implement High Low Low Mid - separate project for tests + docker image etc.
Effort to maintain Mid Low Low Low
Usability (local run) Good Good Good Ok
Integration with CI Easy Easy Easy Easy
Debugging Easy Not possible Easy Hard
Understandability of failures (detection of the problem) Good Good Good it depends, see below

For further details please see the comments and field reports below.

Conclusion:

For the template / unit tests, see #125 (comment) we will go with a combination of both.

I think we could combine both golden files and Terratest, and if we want to verify specific properties (or conditions) we can use the direct property tests we saw above #125 (comment) So we would have all in one place, tests specific to a manifest (for each one test), can run and edit easily locally via ide etc.

For the integration tests, see #125 (comment) I think it would be a good choice for now to go with terratest as well.

One issue I see with the chart-testing tool is that we have not much possibility to configure the complete testing process (or have not much control at all). Available options you can find here https://github.com/helm/chart-testing/blob/main/doc/ct_install.md but they are quite limited.

We can configure the helm install itself, but it seems that the chart-testing times out after 3 minutes. Or at least I was not able to run one successful test with ct. For me it seems we have no control over it. This issue we have not with terratest, in general we have here more control what to do and install and for what to wait etc. Be aware that we have to deploy a lot of components and elastic and zeebe takes "ages" to become ready.

In general looking at the other failure cases (#125 (comment)) we need to test, I feel it will become much harder to find out the issues and reproduce it, if this fails in the CI. You have to run the ct tool locally and have the tests in a separate container, which might fail. They need to be run executed again (by ct) and I feel this is hard to debug and observe. You have good insights with all the logs printed etc, but it is a lot of information which might make it also not easier (due to the massive amount of data).

My conclusion out of this would be to use terratest also for the integration tests (for now). The benefit here is that we have one tool for both (unit and it tests) and more control over it. It makes it easy to run and debug the tests. In general the tests were also quite simple.

I think we can always revisit this decision, I don't expect that we will write a lot of integration tests here, and of course I'm always open for feedback and concerns which might be raised regarding this.

@Zelldon
Copy link
Member Author

Zelldon commented Feb 9, 2022

Template Test: Terratest

I reused the example from #133 provided by @npepinpe 🙇 I will share here my experience with Terratest.

Setup

In general the setup is quite simple and easy to understand, see #133.

Run Tests

If you have setup go on your machine then it is not a problem to run the test either via go test or via an IDE like goland. This also means it should be easy to integrate with CI, since you can just run a go container with go test. Be aware you need to resolve the helm dependencies before.

After migrating the tests to the ccsm-helm chart I had issues running the terratests, because it seems the charts are to heavy coupled with the parent chart.
It is not possible to run them in charts/zeebe/test, we get some issues with missing values and templates.

GOROOT=/usr/lib/go #gosetup
GOPATH=/home/zell/goPath #gosetup
/usr/lib/go/bin/go test -c -o /tmp/GoLand/___statefulset_test_go.test camunda-cloud-helm/charts/ccsm-helm/charts/zeebe/test #gosetup
/usr/lib/go/bin/go tool test2json -t /tmp/GoLand/___statefulset_test_go.test -test.v -test.paniconexit0 -test.run ^\QTestStatefulSetTemplate\E$
=== RUN   TestStatefulSetTemplate
=== PAUSE TestStatefulSetTemplate
=== CONT  TestStatefulSetTemplate
--- FAIL: TestStatefulSetTemplate (0.04s)
=== RUN   TestStatefulSetTemplate/TestContainerSpecImage
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:33:10+01:00 logger.go:66: Running command helm with args [template --namespace zeebe-gbwm26 --namespace zeebe-gbwm26 --set image.repository=helm/zeebe --set image.tag=a.b.c --show-only templates/statefulset.yaml ccsm-helm-test /home/zell/goPath/src/github.com/zeebe-io/camunda-cloud-helm/charts/ccsm-helm/charts/zeebe]
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:33:10+01:00 logger.go:66: Error: template: zeebe/templates/tests/test-connection.yaml:4:23: executing "zeebe/templates/tests/test-connection.yaml" at <.Values.global.zeebeClusterName>: wrong type for value; expected string; got interface {}
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:33:10+01:00 logger.go:66: 
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:33:10+01:00 logger.go:66: Use --debug flag to render out invalid YAML
    template.go:20: 
        	Error Trace:	template.go:20
        	            				statefulset_test.go:48
        	Error:      	Received unexpected error:
        	            	error while running command: exit status 1; Error: template: zeebe/templates/tests/test-connection.yaml:4:23: executing "zeebe/templates/tests/test-connection.yaml" at <.Values.global.zeebeClusterName>: wrong type for value; expected string; got interface {}
        	            	
        	            	Use --debug flag to render out invalid YAML
        	Test:       	TestStatefulSetTemplate/TestContainerSpecImage
    --- FAIL: TestStatefulSetTemplate/TestContainerSpecImage (0.04s)

After deleting the test-connection file:

/usr/lib/go/bin/go tool test2json -t /tmp/GoLand/___TestContainerSpecImage_in_camunda_cloud_helm_charts_ccsm_helm_charts_zeebe_test.test -test.v -test.paniconexit0 -test.run ^\QTestStatefulSetTemplate\E$/^\QTestContainerSpecImage\E$ -testify.m ^TestContainerSpecImage$
=== RUN   TestStatefulSetTemplate
=== PAUSE TestStatefulSetTemplate
=== CONT  TestStatefulSetTemplate
--- FAIL: TestStatefulSetTemplate (0.04s)
=== RUN   TestStatefulSetTemplate/TestContainerSpecImage
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:30:13+01:00 logger.go:66: Running command helm with args [template --namespace zeebe-pwquvl --namespace zeebe-pwquvl --set image.repository=helm/zeebe --set image.tag=a.b.c --show-only templates/statefulset.yaml ccsm-helm-test /home/zell/goPath/src/github.com/zeebe-io/camunda-cloud-helm/charts/ccsm-helm/charts/zeebe]
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:30:13+01:00 logger.go:66: Error: template: zeebe/templates/statefulset.yaml:5:14: executing "zeebe/templates/statefulset.yaml" at <include "zeebe.labels.broker" .>: error calling include: template: zeebe/templates/_helpers.tpl:49:13: executing "zeebe.labels.broker" at <{{template "ccsm.labels" .}}>: template "ccsm.labels" not defined
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:30:13+01:00 logger.go:66: 
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:30:13+01:00 logger.go:66: Use --debug flag to render out invalid YAML
    template.go:20: 
        	Error Trace:	template.go:20
        	            				statefulset_test.go:48
        	Error:      	Received unexpected error:
        	            	error while running command: exit status 1; Error: template: zeebe/templates/statefulset.yaml:5:14: executing "zeebe/templates/statefulset.yaml" at <include "zeebe.labels.broker" .>: error calling include: template: zeebe/templates/_helpers.tpl:49:13: executing "zeebe.labels.broker" at <{{template "ccsm.labels" .}}>: template "ccsm.labels" not defined
        	            	
        	            	Use --debug flag to render out invalid YAML
        	Test:       	TestStatefulSetTemplate/TestContainerSpecImage
    --- FAIL: TestStatefulSetTemplate/TestContainerSpecImage (0.04s)

The output: logger.go:66: Use --debug flag to render out invalid YAML is quite confusing since it doesn't help at all. I tried with ExtraArgs: map[string][]string{"template": {"--debug"}, "install": {"--debug"}}, but I see no extra output. But this seems to be an general helm issue helm/helm#8655

I move the tests to the root chart, here it seems to work! I had to change the paths obviously.

func TestStatefulSetTemplate(t *testing.T) {
	t.Parallel()

	chartPath, err := filepath.Abs("../")
	require.NoError(t, err)

	suite.Run(t, &statefulSetTemplateTest{
		chartPath: chartPath,
		release:   "ccsm-helm-test",
		namespace: "zeebe-" + strings.ToLower(random.UniqueId()),
		templates: []string{"charts/zeebe/templates/statefulset.yaml"},
	})
}

Failure Report

What I really like is that the complete rendered manifest is printed out so we can see what values are set etc.

Template
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66: Running command helm with args [template --namespace zeebe-jxgflc --namespace zeebe-jxgflc --set image.repository=helm/zeebe --set image.tag=a.b.c --show-only charts/zeebe/templates/statefulset.yaml ccsm-helm-test /home/zell/goPath/src/github.com/zeebe-io/camunda-cloud-helm/charts/ccsm-helm]
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66: ---
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66: # Source: ccsm-helm/charts/zeebe/templates/statefulset.yaml
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66: apiVersion: apps/v1
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66: kind: StatefulSet
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66: metadata:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   name: "ccsm-helm-test-zeebe"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   labels:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     app: camunda-cloud-self-managed
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     app.kubernetes.io/name: zeebe
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     helm.sh/chart: zeebe-0.0.11
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     app.kubernetes.io/instance: ccsm-helm-test
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     app.kubernetes.io/managed-by: Helm
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     app.kubernetes.io/version: 1.3.1
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     app.kubernetes.io/part-of: camunda-cloud-self-managed
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     app.kubernetes.io/component: zeebe-broker
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   annotations:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66: spec:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   replicas: 3
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   selector:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     matchLabels:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       app: camunda-cloud-self-managed
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       app.kubernetes.io/name: zeebe
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       helm.sh/chart: zeebe-0.0.11
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       app.kubernetes.io/instance: ccsm-helm-test
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       app.kubernetes.io/managed-by: Helm
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       app.kubernetes.io/version: 1.3.1
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       app.kubernetes.io/part-of: camunda-cloud-self-managed
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       app.kubernetes.io/component: zeebe-broker
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   serviceName: "ccsm-helm-test-zeebe"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   updateStrategy:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     type: RollingUpdate
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   podManagementPolicy: Parallel
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   template:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     metadata:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       labels:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         app: camunda-cloud-self-managed
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         app.kubernetes.io/name: zeebe
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         helm.sh/chart: zeebe-0.0.11
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         app.kubernetes.io/instance: ccsm-helm-test
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         app.kubernetes.io/managed-by: Helm
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         app.kubernetes.io/version: 1.3.1
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         app.kubernetes.io/part-of: camunda-cloud-self-managed
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         app.kubernetes.io/component: zeebe-broker
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       annotations:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     spec:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       initContainers:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       containers:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       - name: zeebe
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         image: "camunda/zeebe:1.3.1"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         imagePullPolicy: IfNotPresent
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         env:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: LC_ALL
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: C.UTF-8
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: K8S_NAME
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           valueFrom:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             fieldRef:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:               fieldPath: metadata.name
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: K8S_SERVICE_NAME
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "ccsm-helm-test-zeebe"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: K8S_NAMESPACE
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           valueFrom:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             fieldRef:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:               fieldPath: metadata.namespace
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_NETWORK_ADVERTISEDHOST
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "$(K8S_NAME).$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_CLUSTER_INITIALCONTACTPOINTS
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             $(K8S_SERVICE_NAME)-0.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502,
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             $(K8S_SERVICE_NAME)-1.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502,
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             $(K8S_SERVICE_NAME)-2.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502,
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_CLUSTER_CLUSTERNAME
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: ccsm-helm-test-zeebe
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_LOG_LEVEL
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "info"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "3"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_CLUSTER_CLUSTERSIZE
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "3"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "3"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_THREADS_CPUTHREADCOUNT
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "2"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_THREADS_IOTHREADCOUNT
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "2"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_GATEWAY_ENABLE
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "false"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_CLASSNAME
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "io.camunda.zeebe.exporter.ElasticsearchExporter"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_URL
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "http://elasticsearch-master:9200"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_NETWORK_COMMANDAPI_PORT
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "26501"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_NETWORK_INTERNALAPI_PORT
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "26502"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: ZEEBE_BROKER_NETWORK_MONITORINGAPI_PORT
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "9600"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: K8S_POD_NAME
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           valueFrom:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             fieldRef:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:               fieldPath: metadata.name
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: JAVA_TOOL_OPTIONS
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           value: "-XX:MaxRAMPercentage=25.0 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/zeebe/data -XX:ErrorFile=/usr/local/zeebe/data/zeebe_error%p.log -XX:+ExitOnOutOfMemoryError"
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         ports:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - containerPort: 9600
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           name: http
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - containerPort: 26501
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           name: command
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - containerPort: 26502
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           name: internal
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         readinessProbe:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           httpGet:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             path: /ready
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             port: 9600
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           periodSeconds: 10
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           successThreshold: 1
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           timeoutSeconds: 1
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         resources:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           limits:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             cpu: 1000m
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             memory: 4Gi
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           requests:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             cpu: 500m
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:             memory: 2Gi
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         volumeMounts:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: config
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           mountPath: /usr/local/bin/startup.sh
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           subPath: startup.sh
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: data
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           mountPath: /usr/local/zeebe/data
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         - name: exporters
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           mountPath: /exporters
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         securityContext:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           {}
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       volumes:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       - name: config
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         configMap:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           name: ccsm-helm-test-zeebe
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           defaultMode: 0744
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       - name: exporters
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         emptyDir: {}
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   volumeClaimTemplates:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:   - metadata:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       name: data
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:     spec:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       accessModes: [ReadWriteOnce]
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       storageClassName: 
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:       resources:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:         requests:
TestStatefulSetTemplate/TestContainerSpecImage 2022-02-09T06:34:57+01:00 logger.go:66:           storage: "10Gi"

The test failure output is easy understandable:

    statefulset_test.go:56: 
        	Error Trace:	statefulset_test.go:56
        	Error:      	Not equal: 
        	            	expected: "camunda/zeebe:1.3.1"
        	            	actual  : "helm/zeebe:a.b.c"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-camunda/zeebe:1.3.1
        	            	+helm/zeebe:a.b.c
        	Test:       	TestStatefulSetTemplate/TestContainerSpecImage
    --- FAIL: TestStatefulSetTemplate/TestContainerSpecImage (0.19s)


Expected :camunda/zeebe:1.3.1
Actual   :helm/zeebe:a.b.c

Due to the setup and using Goland debugging is also possible. One issue we still have is that it just calls helm at the end. If it fails in helm itself, like we see above (with the missing values), we have no benefit of the terratests because it can't give more insights. If the template can be rendered, then it is quite powerful because we can explore the rendered manifest as an object in go.

obj

Testing Defaults

I added another test to verify the defaults we set. The test you can see below:

func (s *statefulSetTemplateTest) TestContainerDefaults() {
	options := &helm.Options{
		KubectlOptions: k8s.NewKubectlOptions("", "", s.namespace),
		ExtraArgs: map[string][]string{"template": {"--debug"}, "install": {"--debug"}},
	}
	output := helm.RenderTemplate(s.T(), options, s.chartPath, s.release, s.templates)

	var statefulSet appsv1.StatefulSet
	helm.UnmarshalK8SYaml(s.T(), output, &statefulSet)

	expectedReplicas := int32(3)
	replicas := *statefulSet.Spec.Replicas
	s.Require().Equal(replicas, expectedReplicas)
}

One thing to note is that we need to be aware of the different go types. We need to dereference the pointers (replicas := *statefulSet.Spec.Replicas), we need to use the right types (expectedReplicas := int32(3)) for comparison etc. Furthermore it is a lot of code to write to verify the defaults.

It becomes more interesting if we also want to verify whether the right env vars are set. I expected that this would be really a pain. Because I experienced that in the zeebe controller, but it was not.

func (s *statefulSetTemplateTest) TestContainerDefaults() {
	options := &helm.Options{
		KubectlOptions: k8s.NewKubectlOptions("", "", s.namespace),
		ExtraArgs: map[string][]string{"template": {"--debug"}, "install": {"--debug"}},
	}
	output := helm.RenderTemplate(s.T(), options, s.chartPath, s.release, s.templates)

	var statefulSet appsv1.StatefulSet
	helm.UnmarshalK8SYaml(s.T(), output, &statefulSet)

	expectedEnv := v1.EnvVar{Name: "ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT", Value: "3"}
	envs := statefulSet.Spec.Template.Spec.Containers[0].Env
	s.Require().Contains(envs, expectedEnv)
}

Asserting the env var is quite easy.

What I wanted to verify how it looks like if I change the default now or the property is no longer used (e.g. env is not set or not equal value). The failure output is not so nice anymore if it doesn't contain the value, but sure I still can find it somehow and at least it fails :).

    statefulset_test.go:73: 
        	Error Trace:	statefulset_test.go:73
        	Error:      	[]v1.EnvVar{v1.EnvVar{Name:"LC_ALL", Value:"C.UTF-8", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"K8S_NAME", Value:"", ValueFrom:(*v1.EnvVarSource)(0xc0000887c0)}, v1.EnvVar{Name:"K8S_SERVICE_NAME", Value:"ccsm-helm-test-zeebe", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"K8S_NAMESPACE", Value:"", ValueFrom:(*v1.EnvVarSource)(0xc000088800)}, v1.EnvVar{Name:"ZEEBE_BROKER_NETWORK_ADVERTISEDHOST", Value:"$(K8S_NAME).$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_CLUSTER_INITIALCONTACTPOINTS", Value:"$(K8S_SERVICE_NAME)-0.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502, $(K8S_SERVICE_NAME)-1.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502, $(K8S_SERVICE_NAME)-2.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502,", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_CLUSTER_CLUSTERNAME", Value:"ccsm-helm-test-zeebe", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_LOG_LEVEL", Value:"info", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT", Value:"", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_CLUSTER_CLUSTERSIZE", Value:"3", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR", Value:"3", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_THREADS_CPUTHREADCOUNT", Value:"2", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_THREADS_IOTHREADCOUNT", Value:"2", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_GATEWAY_ENABLE", Value:"false", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_CLASSNAME", Value:"io.camunda.zeebe.exporter.ElasticsearchExporter", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_URL", Value:"http://elasticsearch-master:9200", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_NETWORK_COMMANDAPI_PORT", Value:"26501", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_NETWORK_INTERNALAPI_PORT", Value:"26502", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"ZEEBE_BROKER_NETWORK_MONITORINGAPI_PORT", Value:"9600", ValueFrom:(*v1.EnvVarSource)(nil)}, v1.EnvVar{Name:"K8S_POD_NAME", Value:"", ValueFrom:(*v1.EnvVarSource)(0xc000088840)}, v1.EnvVar{Name:"JAVA_TOOL_OPTIONS", Value:"-XX:MaxRAMPercentage=25.0 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/zeebe/data -XX:ErrorFile=/usr/local/zeebe/data/zeebe_error%p.log -XX:+ExitOnOutOfMemoryError", ValueFrom:(*v1.EnvVarSource)(nil)}} does not contain v1.EnvVar{Name:"ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT", Value:"3", ValueFrom:(*v1.EnvVarSource)(nil)}
        	Test:       	TestStatefulSetTemplate/TestContainerDefaults
    --- FAIL: TestStatefulSetTemplate/TestContainerDefaults (0.19s)


FAIL

I think for the for such test (like testing all defaults) it is worth to setup a golden file.

@Zelldon
Copy link
Member Author

Zelldon commented Feb 9, 2022

Template Test: Golden Files

Setup

I first thought about using my example https://github.com/camunda-community-hub/camunda-cloud-helm/commits/zell-test-goldenfile

Which just adds this scripts and golden file.

Script:

#!/bin/bash
set -euox pipefail

helm template test . --set elasticsearch.enabled=false > actual

diff goldenfile actual

rm actual

It is quite simple, doesn't need any go setup. But the golden file is rather larger, since it contains all manifests.

Alternative

It came to my mind to combine both. Terratest and the go files, also based on this blog post https://medium.com/@jarifibrahim/golden-files-why-you-should-use-them-47087ec994bf

func (s *statefulSetTemplateTest) TestContainerDefaultsStatefulsetGolden() {
	options := &helm.Options{
		KubectlOptions: k8s.NewKubectlOptions("", "", s.namespace),
	}
	output := helm.RenderTemplate(s.T(), options, s.chartPath, s.release, s.templates)
	actual := []byte(output)

	goldenFile := "statefulset.golden.json"

	update := false

	if (update) {
		err := ioutil.WriteFile(goldenFile, actual, 0644)
		if err != nil {
			return
		}
	}

	expected, err := ioutil.ReadFile(goldenFile)
	if err != nil {
		return
	}

	s.Require().Equal(string(expected), output)
}

The test is fairly simple. If necessary it also allows to generate new golden files, similar how we do it in the zeebe controller.
It allows to verify that rendering works and that all default values are set as expected, and doesn't change. Plus we can have a golden file per manifest, which might makes it easier to maintain them. We can always see what we actually deploy.

Run Tests

It can be easily also run by the CI and local, if helm is installed.

Alternative

As described in the Terratest comment above, we need helm and go installed. If both avail. running tests are also quite simple either via go test or via IDE.

Failure Report

See #133 (comment)

If running the script from above the following would be the output, which makes it quite clear what the difference is:

+ helm template test . --set elasticsearch.enabled=false
+ diff -c goldenfile actual
*** goldenfile	2022-02-08 06:56:45.808903557 +0100
--- actual	2022-02-08 07:05:24.245103770 +0100
***************
*** 639,647 ****
          - name: ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR
            value: "3"
          - name: ZEEBE_BROKER_THREADS_CPUTHREADCOUNT
!           value: "2"
          - name: ZEEBE_BROKER_THREADS_IOTHREADCOUNT
!           value: "2"
          - name: ZEEBE_BROKER_GATEWAY_ENABLE
            value: "false"
          - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_CLASSNAME
--- 639,647 ----
          - name: ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR
            value: "3"
          - name: ZEEBE_BROKER_THREADS_CPUTHREADCOUNT
!           value: "3"
          - name: ZEEBE_BROKER_THREADS_IOTHREADCOUNT
!           value: "3"
          - name: ZEEBE_BROKER_GATEWAY_ENABLE
            value: "false"
          - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_CLASSNAME

Alternative

If we run the Terratest with goldenfile the output on failure is also quite nice and useful:

TestStatefulSetTemplate/TestContainerDefaultsStatefulsetGolden 2022-02-09T15:55:00+01:00 logger.go:66:           storage: "10Gi"
    statefulset_test.go:100: 
        	Error Trace:	statefulset_test.go:100
        	Error:      	Not equal: 
        	            	expected: "---\n# Source: ccsm-helm/charts/zeebe/templates/statefulset.yaml\napiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n  name: \"ccsm-helm-test-zeebe\"\n  labels:\n    app: camunda-cloud-self-managed\n    app.kubernetes.io/name: zeebe\n    helm.sh/chart: zeebe-0.0.11\n    app.kubernetes.io/instance: ccsm-helm-test\n    app.kubernetes.io/managed-by: Helm\n    app.kubernetes.io/version: 1.3.1\n    app.kubernetes.io/part-of: camunda-cloud-self-managed\n    app.kubernetes.io/component: zeebe-broker\n  annotations:\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: camunda-cloud-self-managed\n      app.kubernetes.io/name: zeebe\n      helm.sh/chart: zeebe-0.0.11\n      app.kubernetes.io/instance: ccsm-helm-test\n      app.kubernetes.io/managed-by: Helm\n      app.kubernetes.io/version: 1.3.1\n      app.kubernetes.io/part-of: camunda-cloud-self-managed\n      app.kubernetes.io/component: zeebe-broker\n  serviceName: \"ccsm-helm-test-zeebe\"\n  updateStrategy:\n    type: RollingUpdate\n  podManagementPolicy: Parallel\n  template:\n    metadata:\n      labels:\n        app: camunda-cloud-self-managed\n        app.kubernetes.io/name: zeebe\n        helm.sh/chart: zeebe-0.0.11\n        app.kubernetes.io/instance: ccsm-helm-test\n        app.kubernetes.io/managed-by: Helm\n        app.kubernetes.io/version: 1.3.1\n        app.kubernetes.io/part-of: camunda-cloud-self-managed\n        app.kubernetes.io/component: zeebe-broker\n      annotations:\n    spec:\n      initContainers:\n      containers:\n      - name: zeebe\n        image: \"camunda/zeebe:1.3.1\"\n        imagePullPolicy: IfNotPresent\n        env:\n        - name: LC_ALL\n          value: C.UTF-8\n        - name: K8S_NAME\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.name\n        - name: K8S_SERVICE_NAME\n          value: \"ccsm-helm-test-zeebe\"\n        - name: K8S_NAMESPACE\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.namespace\n        - name: ZEEBE_BROKER_NETWORK_ADVERTISEDHOST\n          value: \"$(K8S_NAME).$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local\"\n        - name: ZEEBE_BROKER_CLUSTER_INITIALCONTACTPOINTS\n          value:\n            $(K8S_SERVICE_NAME)-0.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502,\n            $(K8S_SERVICE_NAME)-1.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502,\n            $(K8S_SERVICE_NAME)-2.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502,\n        - name: ZEEBE_BROKER_CLUSTER_CLUSTERNAME\n          value: ccsm-helm-test-zeebe\n        - name: ZEEBE_LOG_LEVEL\n          value: \"info\"\n        - name: ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT\n          value: \"3\"\n        - name: ZEEBE_BROKER_CLUSTER_CLUSTERSIZE\n          value: \"3\"\n        - name: ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR\n          value: \"3\"\n        - name: ZEEBE_BROKER_THREADS_CPUTHREADCOUNT\n          value: \"2\"\n        - name: ZEEBE_BROKER_THREADS_IOTHREADCOUNT\n          value: \"2\"\n        - name: ZEEBE_BROKER_GATEWAY_ENABLE\n          value: \"false\"\n        - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_CLASSNAME\n          value: \"io.camunda.zeebe.exporter.ElasticsearchExporter\"\n        - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_URL\n          value: \"http://elasticsearch-master:9200\"\n        - name: ZEEBE_BROKER_NETWORK_COMMANDAPI_PORT\n          value: \"26501\"\n        - name: ZEEBE_BROKER_NETWORK_INTERNALAPI_PORT\n          value: \"26502\"\n        - name: ZEEBE_BROKER_NETWORK_MONITORINGAPI_PORT\n          value: \"9600\"\n        - name: K8S_POD_NAME\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.name\n        - name: JAVA_TOOL_OPTIONS\n          value: \"-XX:MaxRAMPercentage=25.0 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/zeebe/data -XX:ErrorFile=/usr/local/zeebe/data/zeebe_error%p.log -XX:+ExitOnOutOfMemoryError\"\n        ports:\n        - containerPort: 9600\n          name: http\n        - containerPort: 26501\n          name: command\n        - containerPort: 26502\n          name: internal\n        readinessProbe:\n          httpGet:\n            path: /ready\n            port: 9600\n          periodSeconds: 10\n          successThreshold: 1\n          timeoutSeconds: 1\n        resources:\n          limits:\n            cpu: 1000m\n            memory: 4Gi\n          requests:\n            cpu: 500m\n            memory: 2Gi\n        volumeMounts:\n        - name: config\n          mountPath: /usr/local/bin/startup.sh\n          subPath: startup.sh\n        - name: data\n          mountPath: /usr/local/zeebe/data\n        - name: exporters\n          mountPath: /exporters\n        securityContext:\n          {}\n      volumes:\n      - name: config\n        configMap:\n          name: ccsm-helm-test-zeebe\n          defaultMode: 0744\n      - name: exporters\n        emptyDir: {}\n  volumeClaimTemplates:\n  - metadata:\n      name: data\n    spec:\n      accessModes: [ReadWriteOnce]\n      storageClassName: \n      resources:\n        requests:\n          storage: \"10Gi\""
        	            	actual  : "---\n# Source: ccsm-helm/charts/zeebe/templates/statefulset.yaml\napiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n  name: \"ccsm-helm-test-zeebe\"\n  labels:\n    app: camunda-cloud-self-managed\n    app.kubernetes.io/name: zeebe\n    helm.sh/chart: zeebe-0.0.11\n    app.kubernetes.io/instance: ccsm-helm-test\n    app.kubernetes.io/managed-by: Helm\n    app.kubernetes.io/version: 1.3.1\n    app.kubernetes.io/part-of: camunda-cloud-self-managed\n    app.kubernetes.io/component: zeebe-broker\n  annotations:\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: camunda-cloud-self-managed\n      app.kubernetes.io/name: zeebe\n      helm.sh/chart: zeebe-0.0.11\n      app.kubernetes.io/instance: ccsm-helm-test\n      app.kubernetes.io/managed-by: Helm\n      app.kubernetes.io/version: 1.3.1\n      app.kubernetes.io/part-of: camunda-cloud-self-managed\n      app.kubernetes.io/component: zeebe-broker\n  serviceName: \"ccsm-helm-test-zeebe\"\n  updateStrategy:\n    type: RollingUpdate\n  podManagementPolicy: Parallel\n  template:\n    metadata:\n      labels:\n        app: camunda-cloud-self-managed\n        app.kubernetes.io/name: zeebe\n        helm.sh/chart: zeebe-0.0.11\n        app.kubernetes.io/instance: ccsm-helm-test\n        app.kubernetes.io/managed-by: Helm\n        app.kubernetes.io/version: 1.3.1\n        app.kubernetes.io/part-of: camunda-cloud-self-managed\n        app.kubernetes.io/component: zeebe-broker\n      annotations:\n    spec:\n      initContainers:\n      containers:\n      - name: zeebe\n        image: \"camunda/zeebe:1.3.1\"\n        imagePullPolicy: IfNotPresent\n        env:\n        - name: LC_ALL\n          value: C.UTF-8\n        - name: K8S_NAME\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.name\n        - name: K8S_SERVICE_NAME\n          value: \"ccsm-helm-test-zeebe\"\n        - name: K8S_NAMESPACE\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.namespace\n        - name: ZEEBE_BROKER_NETWORK_ADVERTISEDHOST\n          value: \"$(K8S_NAME).$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local\"\n        - name: ZEEBE_BROKER_CLUSTER_INITIALCONTACTPOINTS\n          value:\n            $(K8S_SERVICE_NAME)-0.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502,\n            $(K8S_SERVICE_NAME)-1.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502,\n            $(K8S_SERVICE_NAME)-2.$(K8S_SERVICE_NAME).$(K8S_NAMESPACE).svc.cluster.local:26502,\n        - name: ZEEBE_BROKER_CLUSTER_CLUSTERNAME\n          value: ccsm-helm-test-zeebe\n        - name: ZEEBE_LOG_LEVEL\n          value: \"info\"\n        - name: ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT\n          value: \"3\"\n        - name: ZEEBE_BROKER_CLUSTER_CLUSTERSIZE\n          value: \"3\"\n        - name: ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR\n          value: \n        - name: ZEEBE_BROKER_THREADS_CPUTHREADCOUNT\n          value: \"2\"\n        - name: ZEEBE_BROKER_THREADS_IOTHREADCOUNT\n          value: \"2\"\n        - name: ZEEBE_BROKER_GATEWAY_ENABLE\n          value: \"false\"\n        - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_CLASSNAME\n          value: \"io.camunda.zeebe.exporter.ElasticsearchExporter\"\n        - name: ZEEBE_BROKER_EXPORTERS_ELASTICSEARCH_ARGS_URL\n          value: \"http://elasticsearch-master:9200\"\n        - name: ZEEBE_BROKER_NETWORK_COMMANDAPI_PORT\n          value: \"26501\"\n        - name: ZEEBE_BROKER_NETWORK_INTERNALAPI_PORT\n          value: \"26502\"\n        - name: ZEEBE_BROKER_NETWORK_MONITORINGAPI_PORT\n          value: \"9600\"\n        - name: K8S_POD_NAME\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.name\n        - name: JAVA_TOOL_OPTIONS\n          value: \"-XX:MaxRAMPercentage=25.0 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/zeebe/data -XX:ErrorFile=/usr/local/zeebe/data/zeebe_error%p.log -XX:+ExitOnOutOfMemoryError\"\n        ports:\n        - containerPort: 9600\n          name: http\n        - containerPort: 26501\n          name: command\n        - containerPort: 26502\n          name: internal\n        readinessProbe:\n          httpGet:\n            path: /ready\n            port: 9600\n          periodSeconds: 10\n          successThreshold: 1\n          timeoutSeconds: 1\n        resources:\n          limits:\n            cpu: 1000m\n            memory: 4Gi\n          requests:\n            cpu: 500m\n            memory: 2Gi\n        volumeMounts:\n        - name: config\n          mountPath: /usr/local/bin/startup.sh\n          subPath: startup.sh\n        - name: data\n          mountPath: /usr/local/zeebe/data\n        - name: exporters\n          mountPath: /exporters\n        securityContext:\n          {}\n      volumes:\n      - name: config\n        configMap:\n          name: ccsm-helm-test-zeebe\n          defaultMode: 0744\n      - name: exporters\n        emptyDir: {}\n  volumeClaimTemplates:\n  - metadata:\n      name: data\n    spec:\n      accessModes: [ReadWriteOnce]\n      storageClassName: \n      resources:\n        requests:\n          storage: \"10Gi\""
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -79,3 +79,3 @@
        	            	         - name: ZEEBE_BROKER_CLUSTER_REPLICATIONFACTOR
        	            	-          value: "3"
        	            	+          value: 
        	            	         - name: ZEEBE_BROKER_THREADS_CPUTHREADCOUNT
        	Test:       	TestStatefulSetTemplate/TestContainerDefaultsStatefulsetGolden
    --- FAIL: TestStatefulSetTemplate/TestContainerDefaultsStatefulsetGolden (0.22s)

Testing defaults

I think here lies the strength of the golden files, this is something which shouldn't change and we can verify with a previous generated manifest and don't need to verify the properties one by one.

Small note: Might make sense to compare the objects instead of the strings.

Conclusion

I think we could combine both golden files and Terratest, and if we want to verify specific properties (or conditions) we can use the direct property tests we saw above #125 (comment) So we would have all in one place, tests specific to a manifest (for each one test), can run and edit easily locally via ide etc.

@Zelldon
Copy link
Member Author

Zelldon commented Feb 9, 2022

Integration Tests

I first thought about what we actually want to test here. I think it is two things:

  1. the charts can be deployed to kubernetes, and are accepted by k8 api
  2. the services are running and work with each other

Other things like broken templates, values are not set correctly etc. are caught by the tests above.

So to turn it around, what are potential failure cases we can find.

  1. Specifications which are on the wrong place (look like valid yaml), but are not accepted by the k8 API.
  2. Services are not becoming ready because of configuration errors, can't reach each other etc.

The first case we could also solve with other tools, which validates manifests based on the k8 api, but not the second one.

For the second case, I could imagine as MVP one simple test which allows to ensure most things are running.

We deploy a process model with an user task, start an instance, verify via Operate API that an instance exist and verify via tasklist API that an user task exists.

This ensures that:

  1. Zeebe Gateway works
  2. Broker works
  3. Gateway - Broker communication works
  4. Exporting works
  5. Elastic works
  6. Operate and Tasklist importing works

This test should be run with the default values and with values which configures and enables everything, so we can be sure that all work together and all configurations/templates are accepted by k8.

For simplicity and test the tools I will concentrate one the first failure case, to provoke some failures and understand how test failures look like etc.

@Zelldon
Copy link
Member Author

Zelldon commented Feb 10, 2022

IT: Terratest

Setup

Setup is as in #133 described. I ported them to ccsm, similar I described above. I had to move them to the parent chart.

I think if we use for unit tests also terratest it is also quite easy if we have the same for integration tests, since it is the same setup etc.

Run Tests

Similar to described above, via go test -tags integration or via IDA (e.g. Goland). The output is quiet clear we see what happens, like templating, installing and port forwarding etc. The test run takes around ~50 second, which is quite a bit, but if we keep the IT's to a minimum I think this should be ok.

Failure Report

As described above I changed something in the templates to produce an error, which is only detectable via integration tests, but not with the template test.

Diff:

--- a/charts/ccsm-helm/charts/zeebe/templates/statefulset.yaml
+++ b/charts/ccsm-helm/charts/zeebe/templates/statefulset.yaml
@@ -28,6 +28,8 @@ spec:
         {{ $key }}: {{ $value | quote }}
         {{- end }}
     spec:
+      resources:
+      {{- toYaml .Values.resources | nindent 10 }}
       {{- if .Values.priorityClassName }}
       priorityClassName: {{ .Values.priorityClassName | quote }}
       {{- end }}
@@ -122,8 +124,6 @@ spec:
           periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
           successThreshold: {{ .Values.readinessProbe.successThreshold }}
           timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
-        resources:
-          {{- toYaml .Values.resources | nindent 10 }}
         volumeMounts:
         - name: config
           mountPath: /usr/local/bin/startup.sh

I move the resources under the spec, which is valid yaml but not valid for k8 api. Be aware this is just an example, sure normally we would catch this with the normal golden files, but if we have a property which is only enabled/rendered if certain properties are set then maybe not.

The test fails really fast, since the helm install immediately fails. The error message also clearly mentions what the issue is, resources is not known under podSpec.

=== RUN   TestIntegration/TestGatewayConnection
TestIntegration/TestGatewayConnection 2022-02-10T07:07:52+01:00 client.go:42: Configuring Kubernetes client using config file /home/zell/.kube/config with context gke_zeebe-io_europe-west1-b_zeebe-cluster
TestIntegration/TestGatewayConnection 2022-02-10T07:07:52+01:00 logger.go:66: Running command helm with args [install --kube-context gke_zeebe-io_europe-west1-b_zeebe-cluster --namespace zeebe-eaukyg --set image.tag=1.3.1 zeebe-cluster-helm-it /home/zell/goPath/src/github.com/zeebe-io/camunda-cloud-helm/charts/ccsm-helm]
TestIntegration/TestGatewayConnection 2022-02-10T07:07:53+01:00 logger.go:66: Error: INSTALLATION FAILED: unable to build kubernetes objects from release manifest: error validating "": error validating data: ValidationError(StatefulSet.spec.template.spec): unknown field "resources" in io.k8s.api.core.v1.PodSpec
    install.go:16: 
        	Error Trace:	install.go:16
        	            				integration_test.go:69
        	Error:      	Received unexpected error:
        	            	error while running command: exit status 1; Error: INSTALLATION FAILED: unable to build kubernetes objects from release manifest: error validating "": error validating data: ValidationError(StatefulSet.spec.template.spec): unknown field "resources" in io.k8s.api.core.v1.PodSpec
        	Test:       	TestIntegration/TestGatewayConnection
TestIntegration/TestGatewayConnection 2022-02-10T07:07:53+01:00 client.go:42: Configuring Kubernetes client using config file /home/zell/.kube/config with context gke_zeebe-io_europe-west1-b_zeebe-cluster
    --- FAIL: TestIntegration/TestGatewayConnection (1.20s)

@Zelldon
Copy link
Member Author

Zelldon commented Feb 10, 2022

IT: CT and Helm Test

Setup

Setup is fairly simple, locally you can download the binary from the release page of https://github.com/helm/chart-testing for CI you can use the corresponding github action https://github.com/helm/chart-testing-action

It doesn't need much else, just access to the kubeconfig to deploy the chart to (similar to terratest).

Run Tests

The run is simple as ct install --charts charts/ccsm-helm/ this will create an namespace in the target k8 cluster and install the helm chart.

Unfortunately on the first try it failed during awaiting to become ready. This caused to print all logs from all pods and deployments (k describe), quite nice and insightful.

Increasing the timeout hasn't helped, it seems to always fail with some timing issues. The namespace is always deleted after the test run.

Failure Report

Similar to the IT: Terratest, I changed something in the templates to produce an error, which is only detectable via integration tests, but not with the template test.

The test fails really fast, since the helm install immediately fails.

$ time ct install --charts charts/ccsm-helm/ --helm-extra-args "--timeout 1500s"
Installing charts...
Version increment checking disabled.

------------------------------------------------------------------------------------------------------------------------
 Charts to be processed:
------------------------------------------------------------------------------------------------------------------------
 ccsm-helm => (version: "0.0.11", path: "charts/ccsm-helm/")
------------------------------------------------------------------------------------------------------------------------

Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "zeebe" chart repository
...Successfully got an update from the "camunda-cloud" chart repository
...Successfully got an update from the "elastic" chart repository
...Successfully got an update from the "camundacloud" chart repository
...Successfully got an update from the "prometheus-community" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 7 charts
Dependency zeebe did not declare a repository. Assuming it exists in the charts directory
Dependency zeebe-gateway did not declare a repository. Assuming it exists in the charts directory
Dependency operate did not declare a repository. Assuming it exists in the charts directory
Dependency tasklist did not declare a repository. Assuming it exists in the charts directory
Downloading elasticsearch from repo https://helm.elastic.co
Downloading kibana from repo https://helm.elastic.co
Downloading kube-prometheus-stack from repo https://prometheus-community.github.io/helm-charts
Deleting outdated charts
Installing chart 'ccsm-helm => (version: "0.0.11", path: "charts/ccsm-helm/")'...
Creating namespace 'ccsm-helm-oikryzm84s'...
namespace/ccsm-helm-oikryzm84s created
Error: INSTALLATION FAILED: unable to build kubernetes objects from release manifest: error validating "": error validating data: ValidationError(StatefulSet.spec.template.spec): unknown field "resources" in io.k8s.api.core.v1.PodSpec
========================================================================================================================
........................................................................................................................
==> Events of namespace ccsm-helm-oikryzm84s
........................................................................................................................
No resources found in ccsm-helm-oikryzm84s namespace.
........................................................................................................................
<== Events of namespace ccsm-helm-oikryzm84s
........................................................................................................................
========================================================================================================================
Deleting release 'ccsm-helm-oikryzm84s'...
Error: uninstall: Release not loaded: ccsm-helm-oikryzm84s: release: not found
Error deleting Helm release: Error waiting for process: exit status 1
Deleting namespace 'ccsm-helm-oikryzm84s'...
namespace "ccsm-helm-oikryzm84s" deleted
Namespace 'ccsm-helm-oikryzm84s' terminated.
------------------------------------------------------------------------------------------------------------------------
 ✖︎ ccsm-helm => (version: "0.0.11", path: "charts/ccsm-helm/") > Error waiting for process: exit status 1
------------------------------------------------------------------------------------------------------------------------
Error: Error installing charts: Error processing charts
Error installing charts: Error processing charts

real	0m16.959s
user	0m12.127s
sys	0m1.262s

As we can see it shows the same message, as the Terratest. Since both just print the error message from the K8 API, it is quite clear what the issue is.

Conclusion

One issue I see with the chart-testing tool is that we have not much possibility to configure the complete testing process (or have not much control at all). Available options you can find here https://github.com/helm/chart-testing/blob/main/doc/ct_install.md but they are quite limited.

We can configure the helm install itself, but it seems that the chart-testing times out after 3 minutes. Or at least I was not able to run one successful test with ct. For me it seems we have no control over it. This issue we have not with terratest, in general we have here more control what to do and install and for what to wait etc. Be aware that we have to deploy a lot of components and elastic and zeebe takes "ages" to become ready.

In general looking at the other failure cases (#125 (comment)) we need to test, I feel it will become much harder to find out the issues and reproduce it, if this fails in the CI. You have to run the ct tool locally and have the tests in a separate container, which might fail. They need to be run executed again (by ct) and I feel this is hard to debug and observe. You have good insights with all the logs printed etc, but it is a lot of information which might make it also not easier (due to the massive amount of data).

My conclusion out of this would be to use terratest also for the integration tests (for now). The benefit here is that we have one tool for both (unit and it tests) and more control over it. It makes it easy to run and debug the tests. In general the tests were also quite simple.

I think we can always revisit this decision, I don't expect that we will write a lot of integration tests here, and of course I'm always open for feedback and concerns which might be raised regarding this.


P.S.: I tried it again and checked what happens in the K8, and I saw actually that ct is already running the tests:

ccsm-helm-5wr7otc8qq-drwrb-test                       0/1     Pending             0          0s
ccsm-helm-5wr7otc8qq-drwrb-test                       0/1     Pending             0          0s
ccsm-helm-5wr7otc8qq-drwrb-test                       0/1     ContainerCreating   0          0s
ccsm-helm-5wr7otc8qq-drwrb-test                       0/1     Completed           0          2s
ccsm-helm-5wr7otc8qq-operate-test-connection          0/1     Pending             0          0s
ccsm-helm-5wr7otc8qq-operate-test-connection          0/1     Pending             0          0s
ccsm-helm-5wr7otc8qq-operate-test-connection          0/1     ContainerCreating   0          0s
ccsm-helm-5wr7otc8qq-operate-test-connection          1/1     Running             0          3s
ccsm-helm-5wr7otc8qq-operate-test-connection          0/1     Completed           0          4s
ccsm-helm-5wr7otc8qq-tasklist-test-connection         0/1     Pending             0          0s
ccsm-helm-5wr7otc8qq-tasklist-test-connection         0/1     Pending             0          0s
ccsm-helm-5wr7otc8qq-tasklist-test-connection         0/1     ContainerCreating   0          0s
ccsm-helm-5wr7otc8qq-tasklist-test-connection         1/1     Running             0          2s
ccsm-helm-5wr7otc8qq-tasklist-test-connection         0/1     Completed           0          3s
ccsm-helm-5wr7otc8qq-zeebe-test-connection            0/1     Pending             0          0s
ccsm-helm-5wr7otc8qq-zeebe-test-connection            0/1     Pending             0          0s
ccsm-helm-5wr7otc8qq-zeebe-test-connection            0/1     ContainerCreating   0          0s
ccsm-helm-5wr7otc8qq-zeebe-test-connection            0/1     Error   

I forwared the output of the tool into a separate log file and found at the begin of the logging the following:

Default user and password: "demo/demo"
deployment "ccsm-helm-5wr7otc8qq-operate" successfully rolled out
deployment "ccsm-helm-5wr7otc8qq-tasklist" successfully rolled out
deployment "ccsm-helm-5wr7otc8qq-zeebe-gateway" successfully rolled out
NAME: ccsm-helm-5wr7otc8qq
LAST DEPLOYED: Thu Feb 10 16:29:58 2022
NAMESPACE: ccsm-helm-5wr7otc8qq
STATUS: deployed
REVISION: 1
TEST SUITE:     ccsm-helm-5wr7otc8qq-drwrb-test
Last Started:   Thu Feb 10 16:32:35 2022
Last Completed: Thu Feb 10 16:32:37 2022
Phase:          Succeeded
TEST SUITE:     ccsm-helm-5wr7otc8qq-operate-test-connection
Last Started:   Thu Feb 10 16:32:37 2022
Last Completed: Thu Feb 10 16:32:41 2022
Phase:          Succeeded
TEST SUITE:     ccsm-helm-5wr7otc8qq-tasklist-test-connection
Last Started:   Thu Feb 10 16:32:41 2022
Last Completed: Thu Feb 10 16:32:44 2022
Phase:          Succeeded
TEST SUITE:     ccsm-helm-5wr7otc8qq-zeebe-test-connection
Last Started:   Thu Feb 10 16:32:45 2022
Last Completed: Thu Feb 10 16:32:47 2022
Phase:          Failed

After that information it prints ~4800 lines of logs, for all pods etc. Which caused that I can't see it in my shell. Well this was not optimal I would say 😅

@Zelldon
Copy link
Member Author

Zelldon commented Mar 17, 2022

With #214 we implemented the full round trip in our services, this need to be extended if we add more services. Furthermore we added #239 as follow up to verify other properties.

@Zelldon Zelldon closed this as completed Mar 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant