Skip to content

Commit 5c6ed94

Browse files
committed
Embed Go example and make targets
Not all components have the same make targets yet
1 parent 5c94a50 commit 5c6ed94

3 files changed

Lines changed: 74 additions & 60 deletions

File tree

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@ docker_opts ?= --rm --tty --user "$$(id -u)"
66
asciidoctor_pdf_cmd ?= $(docker_cmd) run $(docker_opts) --volume "$${PWD}":/documents/ vshn/asciidoctor-pdf:1.4
77
asciidoctor_opts ?= --destination-dir=$(out_dir)
88

9+
.PHONY: all
910
all: pdf
1011

12+
.PHONY: pdf
1113
pdf: build/tutorial.pdf
1214

13-
.PHONY: build/tutorial.pdf
1415
build/tutorial.pdf: tutorial.adoc
1516
$(asciidoctor_pdf_cmd) $(asciidoctor_opts) $<
1617

18+
.PHONY: clean
1719
clean:
1820
rm -rf build
1921

22+
.PHONY: setup
2023
setup:
2124
./0_requirements.sh; \
2225
./1_lieutenant_on_minikube.sh; \

docs/modules/ROOT/examples/defaults_test.go

Lines changed: 0 additions & 34 deletions
This file was deleted.

docs/modules/ROOT/pages/testing.adoc

Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
ifndef::backend-pdf[]
2-
:examplesdir: example$
3-
endif::[]
4-
51
= Tutorial: Writing Commodore Component Tests
62

73
This tutorial covers the topic of writing tests for your new or existing Commodore Component.
@@ -10,8 +6,10 @@ If not, see link:index.adoc[Writing your First Commodore Component].
106

117
Currently, we can test components with two approaches:
128

13-
. Unit tests with Go. Easy to understand and write if you are already a Go developer.
14-
. Policy tests with Conftest. Uses the https://www.openpolicyagent.org/docs/latest/policy-language/[Rego] syntax from https://www.openpolicyagent.org/[OpenPolicyAgent].
9+
. Unit tests with Go.
10+
Easy to understand and write if you are already a Go developer.
11+
. Policy tests with Conftest.
12+
Uses the https://www.openpolicyagent.org/docs/latest/policy-language/[Rego] syntax from https://www.openpolicyagent.org/[OpenPolicyAgent].
1513

1614
It is up to you to decide which test framework you want to use.
1715
Some tests are simpler to do in Go, some are simpler in Rego.
@@ -24,7 +22,6 @@ NOTE: The policy tests run with the Conftest tool, but for the purpose of this t
2422
NOTE: This tutorial *was written on a Linux system*.
2523

2624
. `Go` version 1.15, developer environment with Go modules enabled.
27-
. `make` version 4
2825
. `docker` version 19
2926

3027
== Setting up test infrastructure with Go
@@ -54,7 +51,44 @@ If you have `component-somename`, then leave out `component-`.
5451

5552
[source,go]
5653
----
57-
include::{examplesdir}defaults_test.go[]
54+
package main
55+
56+
import (
57+
"github.com/stretchr/testify/assert"
58+
"github.com/stretchr/testify/require"
59+
"testing"
60+
)
61+
62+
var (
63+
testPath = "../../compiled/espejo/espejo"
64+
)
65+
66+
func Test_Deployment_DefaultParameters(t *testing.T) {
67+
68+
subject := DecodeDeployment(t, testPath+"/10_deployment.yaml")
69+
require.NotEmpty(t, subject.Spec.Template.Spec.Containers)
70+
container := subject.Spec.Template.Spec.Containers[0]
71+
72+
assert.Equal(t, "espejo", container.Name)
73+
assert.Contains(t, container.Args, "--verbose=false")
74+
assert.Contains(t, container.Args, "--reconcile-interval=10m")
75+
assert.Contains(t, container.Args, "--metrics-addr=:8080")
76+
assert.Contains(t, container.Args, "--enable-leader-election=true")
77+
78+
require.NotEmpty(t, container.Env)
79+
env := container.Env[0]
80+
assert.Equal(t, "WATCH_NAMESPACE", env.Name)
81+
assert.Equal(t, "metadata.namespace", env.ValueFrom.FieldRef.FieldPath)
82+
}
83+
84+
func Test_Namespace(t *testing.T) {
85+
86+
subject := DecodeNamespace(t, testPath+"/01_namespace.yaml")
87+
88+
assert.Equal(t, "syn-espejo", subject.Name)
89+
assert.Contains(t, subject.Labels, "name")
90+
}
91+
5892
----
5993

6094
CAUTION: We have not yet built a library to host the boilerplate code and common functions.
@@ -64,17 +98,19 @@ As you can see, it's pretty straight forward:
6498
. First, load the pre-compiled YAML file into a Go K8s struct that we all know and love
6599
. Then, we verify if the values were parsed correctly, using any assertion library of your choice.
66100

67-
To actually run our unit test case, we need to run a Commodore Component compilation first.
68-
Luckiliy, we have already a `make` target for that.
69-
So simply run `make compile test_go` and see whether they pass.
70-
Alternatively, `go test ./...` inside `tests/go` or inside your IDE work too after compilation.
101+
To actually run our unit test case, we need to run a Commodore Component compilation first:
102+
[source,bash]
103+
----
104+
COMPONENT_NAME=$(basename ${PWD} | sed s/component-//)
105+
DOCKER_CMD() {docker run --rm --user "$(id -u)" -v "${PWD}:/${COMPONENT_NAME}" --workdir /${COMPONENT_NAME} $*}
106+
DOCKER_CMD --entrypoint /usr/local/bin/jb projectsyn/commodore:latest install
107+
DOCKER_CMD projectsyn/commodore:latest component compile . -f tests/test.yml
108+
----
71109

72110
Running the tests could look like this:
73111
[source,bash]
74112
----
75-
$ make test_go
76-
===> Running Go unit tests
77-
cd tests/unit && go test -v ./...
113+
$ pushd tests/unit > /dev/null && go test -v ./... && popd > /dev/null
78114
=== RUN Test_Deployment_DefaultParameters
79115
--- PASS: Test_Deployment_DefaultParameters (0.01s)
80116
=== RUN Test_Namespace
@@ -113,21 +149,29 @@ warn_labels[msg] {
113149
Let's break down the structure:
114150

115151
. `recommended_labels` is an object that verifies that `.metadata.labels` contain the desired label keys.
116-
. `warn_labels[msg]` is a Rule. If all expressions in the brackets match (including `msg`), this Rule is considered `true`.
117-
. Since the prefix of the rule is `warn_`, it will only print a Warning message if there is an object that matches the rule. With `deny_`, it would fail the test.
152+
. `warn_labels[msg]` is a Rule.
153+
If all expressions in the brackets match (including `msg`), this Rule is considered `true`.
154+
. Since the prefix of the rule is `warn_`, it will only print a Warning message if there is an object that matches the rule.
155+
With `deny_`, it would fail the test.
118156

119157
IMPORTANT: Rego (like Datalog and its ancestor Prolog) is declarative.
120158
The lines within a rule are not evaluated imperatively.
121159
It is important to keep that in mind when writing rules, as it can cause many headaches.
122160

123161
Let's translate the example to English:
124162

125-
. In `recommended_labels`, we will test whether the Kubernetes object (named `input`) contains "app.kubernetes.io/managed-by" in the `.metadata.labels` dictionary. We ignore the actual value here. Since `recommended_labels` is not a rule, it's not yet used.
163+
. In `recommended_labels`, we will test whether the Kubernetes object (named `input`) contains "app.kubernetes.io/managed-by" in the `.metadata.labels` dictionary.
164+
We ignore the actual value here.
165+
Since `recommended_labels` is not a rule, it's not yet used.
126166
. When conftest matches an Object against the rule `warn_labels`, all expressions in the rule have to evaluate `True`.
127167
. If we pass a CRD, the result of the rule is `False` because of `input.kind != "CustomResourceDefinition"`, thus the rule does not match, and the test passes.
128168
. If we pass a `Deployment`, we have at least `input.kind != "CustomResourceDefinition"` that equals to `True`, but remember, all expressions have to be evaluated.
129-
. The other expression, `not recommended_labels` checks if the object is missing the desired labels. If the given Deployment has the labels, it will fail the rule and pass the test. A Deployment that doesn't have the labels would match the rule, and thus fail the test.
130-
. By now the rule would already match with a Deployment without the labels, and thus fail the test, but we want to give a reason why. As the final expression, we will assign the `msg` variable a human readable message why the rule matches. Remember, this line can also be the first one since the execution order is determined by Rego and not line by line.
169+
. The other expression, `not recommended_labels` checks if the object is missing the desired labels.
170+
If the given Deployment has the labels, it will fail the rule and pass the test.
171+
A Deployment that doesn't have the labels would match the rule, and thus fail the test.
172+
. By now the rule would already match with a Deployment without the labels, and thus fail the test, but we want to give a reason why.
173+
As the final expression, we will assign the `msg` variable a human readable message why the rule matches.
174+
Remember, this line can also be the first one since the execution order is determined by Rego and not line by line.
131175

132176
If we now also pass a `Namespace` or `Service` objects, the same rules can be applied, since all these objects share the common property `.metadata.labels`.
133177

@@ -149,8 +193,7 @@ The expression `not input.metadata.name = "syn-espejo"` is equivalent, but we wa
149193
Running the policies could look like this:
150194
[source,bash]
151195
----
152-
$ make test_conftest
153-
===> Running Conftest policies
196+
$ DOCKER_CMD --volume "${PWD}/tests/policies:/policy" openpolicyagent/conftest:latest test --policy /policy $(find . -type f -wholename "./compiled/${COMPONENT_NAME}/*.yaml")
154197
WARN - ./compiled/espejo/espejo/05_rbac.yaml - ClusterRole/syn-espejo has not recommended labels
155198
WARN - ./compiled/espejo/espejo/05_rbac.yaml - ServiceAccount/espejo has not recommended labels
156199
WARN - ./compiled/espejo/espejo/05_rbac.yaml - ClusterRoleBinding/syn-espejo has not recommended labels
@@ -161,11 +204,13 @@ WARN - ./compiled/espejo/espejo/01_namespace.yaml - Namespace/syn-espejo has not
161204

162205
== Run all tests
163206

164-
At the time of writing, this is done simply with `make test`.
165-
This is also applicable in any CI/CD pipelines, such as GitHub Actions.
207+
You could declare all the test commands in the `Makefile`.
208+
Have a look at https://github.com/projectsyn/component-espejo/blob/master/Makefile[Component-Espejo] for an example.
209+
This should also help running tests in any CI/CD pipelines, such as GitHub Actions.
166210

167211
== Conclusion
168212

169213
I hope this guide has shown how we can test our component without having to compile a whole catalog and applying it to a cluster.
170214

171-
At the moment, we are limited to only have tests against a single compilation (e.g. the default parameters). Later on, we want to enable testing different parameter sets.
215+
At the moment, we are limited to only have tests against a single compilation (e.g. the default parameters).
216+
Later on, we want to enable testing different parameter sets.

0 commit comments

Comments
 (0)