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

Support for test run labels/selectors #483

Merged
merged 6 commits into from
Aug 31, 2023

Conversation

porridge
Copy link
Member

@porridge porridge commented Aug 9, 2023

What this PR does / why we need it:

Make it possible to associate label sets with test runs and put label selectors for individual files as discussed on Slack. The purpose is conditional ignoring of selected files with the goal of having a single test directory but with some variation to allow for testing on different platforms.

Why a new resource type?

I was not able to find any appropriate place.

  • A TestStep object applies to all files in a given step, and only single such object is permitted per step. This might be too coarse granularity, since for example in a given step we might only want to vary some assertions and keep the apply files constant.
  • Similarly, only a single TestAssert is used in a given step (although unlike for TestStep, additional TestAssert objects seem to be silently ignored rather than complained about). Also, TestAssert objects are only active if they appear in an NN-assert*.yaml file - they seem to be ignored in all other files, including errors files and varying the apply files is a goal.

Simple usage example

Setup

[mowsiany@mowsiany x]$ head -n 99 tt/*
==> tt/01-assert-cat.yaml <==
apiVersion: kuttl.dev/v1beta1
kind: TestFile
testRunSelector:
  matchLabels:
    flavor: cat
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: test
data:
  key: fluffy

==> tt/01-assert-dog.yaml <==
apiVersion: kuttl.dev/v1beta1
kind: TestFile
testRunSelector:
  matchLabels:
    flavor: dog
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: test
data:
  key: rex

==> tt/01-create.yaml <==
apiVersion: v1
kind: ConfigMap
metadata:
  name: test
data:
  key: fluffy
[mowsiany@mowsiany x]$ make -C .. cli
make: Entering directory '/home/mowsiany/go/src/github.com/kudobuilder/kuttl'
go build -ldflags "-X github.com/kudobuilder/kuttl/pkg/version.gitVersion=0.15.0 -X github.com/kudobuilder/kuttl/pkg/version.gitCommit=e7d8400e -X github.com/kudobuilder/kuttl/pkg/version.buildDate=2023-08-22T08:49:58Z" -o bin/kubectl-kuttl ./cmd/kubectl-kuttl
make: Leaving directory '/home/mowsiany/go/src/github.com/kudobuilder/kuttl'

A flavor=cat test run

[mowsiany@mowsiany x]$ ../bin/kubectl-kuttl test . --start-kind --timeout 15 --test-run-labels flavor=cat
2023/08/22 10:39:41 running without a 'kuttl-test.yaml' configuration
2023/08/22 10:39:41 kutt-test config testdirs is overridden with args: [ . ]
=== RUN   kuttl
    harness.go:465: starting setup
    harness.go:252: running tests with KIND.
    harness.go:176: temp folder created /tmp/nix-shell.mdjyec/nix-shell.UvjEuW/kuttl3988091570
    harness.go:158: Starting KIND cluster
    kind.go:66: Adding Containers to KIND...
    harness.go:278: Successful connection to cluster at: https://127.0.0.1:40829
    harness.go:363: running tests
    harness.go:75: going to run test suite with timeout of 15 seconds for each step
    harness.go:375: testsuite: . has 1 tests
=== RUN   kuttl/harness
=== RUN   kuttl/harness/tt
=== PAUSE kuttl/harness/tt
=== CONT  kuttl/harness/tt
Skipping file "/home/mowsiany/go/src/github.com/kudobuilder/kuttl/x/tt/01-assert-dog.yaml", label selector does not match test run labels.
    logger.go:42: 10:40:10 | tt | Creating namespace: kuttl-test-shining-cat
    logger.go:42: 10:40:10 | tt/1-create | starting test step 1-create
    logger.go:42: 10:40:10 | tt/1-create | ConfigMap:kuttl-test-shining-cat/test created
    logger.go:42: 10:40:10 | tt/1-create | test step completed 1-create
    logger.go:42: 10:40:10 | tt | tt events from ns kuttl-test-shining-cat:
    logger.go:42: 10:40:10 | tt | Deleting namespace: kuttl-test-shining-cat
=== NAME  kuttl
    harness.go:408: run tests finished
    harness.go:516: cleaning up
    harness.go:525: collecting cluster logs to kind-logs-1692693615
    harness.go:573: removing temp folder: "/tmp/nix-shell.mdjyec/nix-shell.UvjEuW/kuttl3988091570"
    harness.go:579: tearing down kind cluster
--- PASS: kuttl (36.29s)
    --- PASS: kuttl/harness (0.00s)
        --- PASS: kuttl/harness/tt (5.20s)
PASS

A flavor=dog run

[mowsiany@mowsiany x]$ ../bin/kubectl-kuttl test . --start-kind --timeout 15 --test-run-labels flavor=dog
2023/08/22 10:40:33 running without a 'kuttl-test.yaml' configuration
2023/08/22 10:40:33 kutt-test config testdirs is overridden with args: [ . ]
=== RUN   kuttl
    harness.go:465: starting setup
    harness.go:252: running tests with KIND.
    harness.go:176: temp folder created /tmp/nix-shell.mdjyec/nix-shell.UvjEuW/kuttl3909311779
    harness.go:158: Starting KIND cluster
    kind.go:66: Adding Containers to KIND...
    harness.go:278: Successful connection to cluster at: https://127.0.0.1:40901
    harness.go:363: running tests
    harness.go:75: going to run test suite with timeout of 15 seconds for each step
    harness.go:375: testsuite: . has 1 tests
=== RUN   kuttl/harness
=== RUN   kuttl/harness/tt
=== PAUSE kuttl/harness/tt
=== CONT  kuttl/harness/tt
Skipping file "/home/mowsiany/go/src/github.com/kudobuilder/kuttl/x/tt/01-assert-cat.yaml", label selector does not match test run labels.
    logger.go:42: 10:41:01 | tt | Creating namespace: kuttl-test-integral-porpoise
    logger.go:42: 10:41:01 | tt/1-create | starting test step 1-create
    logger.go:42: 10:41:01 | tt/1-create | ConfigMap:kuttl-test-integral-porpoise/test created
    logger.go:42: 10:41:17 | tt/1-create | test step failed 1-create
    case.go:366: failed in step 1-create
    case.go:368: --- ConfigMap:kuttl-test-integral-porpoise/test
        +++ ConfigMap:kuttl-test-integral-porpoise/test
        @@ -1,8 +1,18 @@
         apiVersion: v1
         data:
        -  key: rex
        +  key: fluffy
         kind: ConfigMap
         metadata:
        +  managedFields:
        +  - apiVersion: v1
        +    fieldsType: FieldsV1
        +    fieldsV1:
        +      f:data:
        +        .: {}
        +        f:key: {}
        +    manager: kubectl-kuttl
        +    operation: Update
        +    time: "2023-08-22T08:41:01Z"
           name: test
           namespace: kuttl-test-integral-porpoise
         
        
    case.go:368: resource ConfigMap:kuttl-test-integral-porpoise/test: .data.key: value mismatch, expected: rex != actual: fluffy
    logger.go:42: 10:41:17 | tt | tt events from ns kuttl-test-integral-porpoise:
    logger.go:42: 10:41:17 | tt | Deleting namespace: kuttl-test-integral-porpoise
=== NAME  kuttl
    harness.go:408: run tests finished
    harness.go:516: cleaning up
    harness.go:525: collecting cluster logs to kind-logs-1692693682
    harness.go:573: removing temp folder: "/tmp/nix-shell.mdjyec/nix-shell.UvjEuW/kuttl3909311779"
    harness.go:579: tearing down kind cluster
--- FAIL: kuttl (49.76s)
    --- FAIL: kuttl/harness (0.00s)
        --- PASS: kuttl/harness/kind-logs-1692693615 (5.20s)
        --- FAIL: kuttl/harness/tt (20.57s)
FAIL
[mowsiany@mowsiany x]$ 

Real-world usage example

See this test directory (see the files ending in (non-)openshift.yaml).

Fixes #379

Signed-off-by: Marcin Owsiany <porridge@redhat.com>
Signed-off-by: Marcin Owsiany <porridge@redhat.com>
Signed-off-by: Marcin Owsiany <porridge@redhat.com>
Signed-off-by: Marcin Owsiany <porridge@redhat.com>
@porridge porridge marked this pull request as ready for review August 22, 2023 08:50
@porridge porridge changed the title PoC for test run labels/selectors Support for test run labels/selectors Aug 22, 2023
porridge added a commit to porridge/kuttl that referenced this pull request Aug 22, 2023
@porridge
Copy link
Member Author

Copy link
Member

@kensipe kensipe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing really blocking on this... it would be great to have a conversation around comments but if you feel good about. Documentation is missing and we should create an issue to capture that. we have a number of ways to skip or isolate tests down... like using --test. I didn't think through it deeply... I suppose we can have awkward scenarios which is the responsibility of the user to understand.

something to be cautious of (going off intuition here). It seems likely that we want to "wait" for a running object with a label... or have an object started outside of kuttl that has a label. It seems we don't want to confuse users with different label context. This one is a selector for a test label (which isn't an object running in the cluster... it is a set of declarations to use during a test). Another type of selector is to select on running objects and I wonder if we need to delineate that... perhaps premature. However I've been thinking a while now about adding kuttl labels or annotations on objects it creates. We currently do not cleanup tests if we do not delete the namespace. I was thinking we could do clean up if we labeled or annotated the kuttl created objects. Great addition and thanks @porridge !

metav1.ObjectMeta `json:"metadata,omitempty"`

// Which test runs should this file be used in. Empty selector matches all test runs.
TestRunSelector *metav1.LabelSelector `json:"testRunSelector,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why we use this structure... this doesn't feel like a selector... the selector is on the command line of running kuttl. This feels more like a label (that a selector would select on) which makes me wonder why we wouldn't use the standard k8s model which would look more like:

==> tt/01-assert-cat.yaml <==
apiVersion: kuttl.dev/v1beta1
kind: TestFile
metadata:
    labels:
        flavor: cat

thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This particular arrangement of which side labels vs selectors go was @eddycharly 's idea, so maybe he can chime in on where it came from. Maybe it's following some pattern seen elsewhere?

I guess you can think about this in two ways:

  1. (this implementation): a "test run" (i.e. a kuttl invocation) comes with a set of labels attached. These labels represent some characteristics of this test invocation. In my use case, there would would be a single label with just two possible values:openshift=true or openshift=false, but you could imagine multiple labels and values for a more elaborate test matrix (such as the cartesian product of plaform={openshift,vanilla,eks},k8s-version={1.28,1.29,1.30}). Then individual files in the test suite provide an optional label selector, which needs to match the label set in order for the file to be considered for the test run. Obviously the selector can contain a subset of label keys that the test run has. For example a given file might only select one or more k8s-version values and be neutral w.r.t. platform. The notable edge case is a file that (be default) has no selector attached - an empty selector matches all possible label sets, meaning this feature is fully backwards-compatible - a file without a TestFile is included in all test invocations, just like before this change.
  2. (I haven't thought about this case earlier so I'm inventing things as I go.) Each test file comes with an optional label set (empty by default). Let's say that some file tests some API introduced in version 1.29 but also present in 1.30. Since a label set can only specify a single value for a given label key, we'd either have to allow multiple label sets per file (eek!) or introduce a copy of the file per k8s version (ouch) or instead of labeling by k8s version, label by feature availability feature-foo={true,false}. Then, each test run can have a label selector attached, and this selector would need to select the APIs supported. This could quickly create an explosion of labels and combinations. What's worse, files which are agnostic about a given API would have to somehow advertise that fact, in order to be selected by a selector which does mention a given API.

So it looks to me that only solution 1 is in fact realistic.

@@ -0,0 +1,30 @@
package cmd
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about name of file... values could be anything... seems like labels or something similar is more readable IMO

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I called it values because the type implements apflag.Value interface and I thought it might be a good name if we add more types that can be used via that interface. Maybe I should have inserted a comment about this...

But sure, this can be labels.go too if we go for one file per type. Let me know which one you prefer.

pkg/test/step.go Outdated Show resolved Hide resolved
pkg/test/step.go Outdated Show resolved Hide resolved
@porridge
Copy link
Member Author

nothing really blocking on this... it would be great to have a conversation around comments but if you feel good about.

Not sure what you mean here ☝️ ...

Documentation is missing and we should create an issue to capture that.

Do you mean docs for this change? Or on something that exists already?

we have a number of ways to skip or isolate tests down... like using --test.

Right. I corrected the description since what I had in mind is reuse of an individual test, rather than suite.

something to be cautious of (going off intuition here). It seems likely that we want to "wait" for a running object with a label... or have an object started outside of kuttl that has a label. It seems we don't want to confuse users with different label context. This one is a selector for a test label (which isn't an object running in the cluster... it is a set of declarations to use during a test). Another type of selector is to select on running objects and I wonder if we need to delineate that... perhaps premature.

I think we should be fine if we clearly qualify labels or selectors as e.g. "test labels" vs "resource labels" or "object labels" in the docs.

Signed-off-by: Marcin Owsiany <porridge@redhat.com>
Copy link
Member

@kensipe kensipe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

after a hangout conversation around this... this makes a lot of sense and it looks ready to go.

@kensipe kensipe merged commit 7e78376 into kudobuilder:main Aug 31, 2023
4 checks passed
porridge added a commit to stackrox/stackrox that referenced this pull request Sep 5, 2023
- Introduce a gke-operator e2e job.
- Use a modified kuttl with support for conditional inclusion of files, see Support for test run labels/selectors kudobuilder/kuttl#483
- Split openshift-specific parts/variants of tests into separate files to make the test suite pass on GKE.

The above broke validation of some files. Made the script skip them as a quick workaround. Proper solution tracked in a sister subtask.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Conditional execution of test steps
2 participants