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

feat: add test scenarios support (cherry-pick #1576) #1577

Merged
merged 1 commit into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .crds/chainsaw.kyverno.io_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,31 @@ spec:
namespace.
type: object
x-kubernetes-preserve-unknown-fields: true
scenarios:
description: Scenarios defines test scenarios.
items:
description: Scenario defines per scenario bindings.
properties:
bindings:
description: Bindings defines binding key/values.
items:
description: Binding represents a key/value set as a binding
in an executing test.
properties:
name:
description: Name the name of the binding.
pattern: ^(?:\w+|\(.+\))$
type: string
value:
description: Value value of the binding.
x-kubernetes-preserve-unknown-fields: true
required:
- name
- value
type: object
type: array
type: object
type: array
skip:
description: Skip determines whether the test should skipped.
type: boolean
Expand Down
45 changes: 45 additions & 0 deletions .schemas/json/test-chainsaw-v1alpha1.json
Original file line number Diff line number Diff line change
Expand Up @@ -1464,6 +1464,51 @@
"description": "NamespaceTemplate defines a template to create the test namespace.",
"x-kubernetes-preserve-unknown-fields": true
},
"scenarios": {
"description": "Scenarios defines test scenarios.",
"type": [
"array",
"null"
],
"items": {
"description": "Scenario defines per scenario bindings.",
"type": [
"object",
"null"
],
"properties": {
"bindings": {
"description": "Bindings defines binding key/values.",
"type": [
"array",
"null"
],
"items": {
"description": "Binding represents a key/value set as a binding in an executing test.",
"type": [
"object",
"null"
],
"required": [
"name",
"value"
],
"properties": {
"name": {
"description": "Name the name of the binding.",
"type": "string",
"pattern": "^(?:\\w+|\\(.+\\))$"
},
"value": {
"description": "Value value of the binding.",
"x-kubernetes-preserve-unknown-fields": true
}
}
}
}
}
}
},
"skip": {
"description": "Skip determines whether the test should skipped.",
"type": [
Expand Down
11 changes: 11 additions & 0 deletions pkg/apis/v1alpha1/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ type TestSpec struct {
// +optional
NamespaceTemplate *Any `json:"namespaceTemplate,omitempty"`

// Scenarios defines test scenarios.
// +optional
Scenarios []Scenario `json:"scenarios,omitempty"`

// Bindings defines additional binding key/values.
// +optional
Bindings []Binding `json:"bindings,omitempty"`
Expand Down Expand Up @@ -91,3 +95,10 @@ type TestSpec struct {
// +kubebuilder:validation:Enum:=Orphan;Background;Foreground
DeletionPropagationPolicy *metav1.DeletionPropagation `json:"deletionPropagationPolicy,omitempty"`
}

// Scenario defines per scenario bindings.
type Scenario struct {
// Bindings defines binding key/values.
// +optional
Bindings []Binding `json:"bindings,omitempty"`
}
30 changes: 30 additions & 0 deletions pkg/apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions pkg/data/crds/chainsaw.kyverno.io_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,31 @@ spec:
namespace.
type: object
x-kubernetes-preserve-unknown-fields: true
scenarios:
description: Scenarios defines test scenarios.
items:
description: Scenario defines per scenario bindings.
properties:
bindings:
description: Bindings defines binding key/values.
items:
description: Binding represents a key/value set as a binding
in an executing test.
properties:
name:
description: Name the name of the binding.
pattern: ^(?:\w+|\(.+\))$
type: string
value:
description: Value value of the binding.
x-kubernetes-preserve-unknown-fields: true
required:
- name
- value
type: object
type: array
type: object
type: array
skip:
description: Skip determines whether the test should skipped.
type: boolean
Expand Down
45 changes: 45 additions & 0 deletions pkg/data/schemas/json/test-chainsaw-v1alpha1.json
Original file line number Diff line number Diff line change
Expand Up @@ -1464,6 +1464,51 @@
"description": "NamespaceTemplate defines a template to create the test namespace.",
"x-kubernetes-preserve-unknown-fields": true
},
"scenarios": {
"description": "Scenarios defines test scenarios.",
"type": [
"array",
"null"
],
"items": {
"description": "Scenario defines per scenario bindings.",
"type": [
"object",
"null"
],
"properties": {
"bindings": {
"description": "Bindings defines binding key/values.",
"type": [
"array",
"null"
],
"items": {
"description": "Binding represents a key/value set as a binding in an executing test.",
"type": [
"object",
"null"
],
"required": [
"name",
"value"
],
"properties": {
"name": {
"description": "Name the name of the binding.",
"type": "string",
"pattern": "^(?:\\w+|\\(.+\\))$"
},
"value": {
"description": "Value value of the binding.",
"x-kubernetes-preserve-unknown-fields": true
}
}
}
}
}
}
},
"skip": {
"description": "Skip determines whether the test should skipped.",
"type": [
Expand Down
5 changes: 3 additions & 2 deletions pkg/runner/processors/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
)

type TestInfo struct {
Id int
Metadata metav1.ObjectMeta
Id int
ScenarioId int
Metadata metav1.ObjectMeta
}

type StepInfo struct {
Expand Down
53 changes: 37 additions & 16 deletions pkg/runner/processors/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,24 +137,45 @@
logging.Log(ctx, logging.Internal, logging.ErrorStatus, color.BoldRed, logging.ErrSection(err))
failer.FailNow(ctx)
}
t.Run(name, func(t *testing.T) {
t.Helper()
t.Cleanup(func() {
if t.Failed() {
p.shouldFailFast.Store(true)
var scenarios []discovery.Test
if test.Test != nil {
if len(test.Spec.Scenarios) == 0 {
scenarios = append(scenarios, test)
} else {
for s := range test.Spec.Scenarios {
scenario := test.Spec.Scenarios[s]
test := test
test.Test = test.Test.DeepCopy()
test.Spec.Scenarios = nil
bindings := scenario.Bindings
bindings = append(bindings, test.Spec.Bindings...)
test.Spec.Bindings = bindings
scenarios = append(scenarios, test)

Check warning on line 153 in pkg/runner/processors/tests.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/processors/tests.go#L145-L153

Added lines #L145 - L153 were not covered by tests
}
})
processor := p.CreateTestProcessor(test)
info := TestInfo{
Id: i + 1,
Metadata: test.ObjectMeta,
}
processor.Run(
testing.IntoContext(ctx, t),
apibindings.RegisterNamedBinding(ctx, bindings, "test", info),
nspacer,
)
})
}
for s := range scenarios {
test := scenarios[s]
t.Run(name, func(t *testing.T) {
t.Helper()
t.Cleanup(func() {
if t.Failed() {
p.shouldFailFast.Store(true)

Check warning on line 163 in pkg/runner/processors/tests.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/processors/tests.go#L160-L163

Added lines #L160 - L163 were not covered by tests
}
})
processor := p.CreateTestProcessor(test)
info := TestInfo{
Id: i + 1,
ScenarioId: s + 1,
Metadata: test.ObjectMeta,

Check warning on line 170 in pkg/runner/processors/tests.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/processors/tests.go#L166-L170

Added lines #L166 - L170 were not covered by tests
}
processor.Run(
testing.IntoContext(ctx, t),
apibindings.RegisterNamedBinding(ctx, bindings, "test", info),
nspacer,
)

Check warning on line 176 in pkg/runner/processors/tests.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/processors/tests.go#L172-L176

Added lines #L172 - L176 were not covered by tests
})
}
}
}

Expand Down
1 change: 1 addition & 0 deletions testdata/e2e/examples/CATALOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- [outputs](outputs/README.md)
- [patch](patch/README.md)
- [quick-start](quick-start/README.md)
- [scenarios](scenarios/README.md)
- [script-env](script-env/README.md)
- [sleep](sleep/README.md)
- [template](template/README.md)
Expand Down
22 changes: 22 additions & 0 deletions testdata/e2e/examples/scenarios/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Test: `scenarios`

*No description*

## Steps

| # | Name | Bindings | Try | Catch | Finally | Cleanup |
|:-:|---|:-:|:-:|:-:|:-:|
| 1 | [step-1](#step-step-1) | 0 | 1 | 0 | 0 | 0 |

### Step: `step-1`

*No description*

#### Try

| # | Operation | Bindings | Outputs | Description |
|:-:|---|:-:|:-:|---|
| 1 | `script` | 0 | 0 | *No description* |

---

22 changes: 22 additions & 0 deletions testdata/e2e/examples/scenarios/chainsaw-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: scenarios
spec:
scenarios:
- bindings:
- name: message
value: hello
- bindings:
- name: message
value: goodbye
steps:
- try:
- script:
env:
- name: message
value: ($message)
content: echo $message
check:
($stdout): ((to_number($test.scenarioId) == `1` && 'hello') || 'goodbye')
15 changes: 15 additions & 0 deletions website/docs/reference/apis/chainsaw.v1alpha1.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ during the testing process.</p>
- [ActionBindings](#chainsaw-kyverno-io-v1alpha1-ActionBindings)
- [ActionEnv](#chainsaw-kyverno-io-v1alpha1-ActionEnv)
- [Output](#chainsaw-kyverno-io-v1alpha1-Output)
- [Scenario](#chainsaw-kyverno-io-v1alpha1-Scenario)
- [TestSpec](#chainsaw-kyverno-io-v1alpha1-TestSpec)
- [TestStepSpec](#chainsaw-kyverno-io-v1alpha1-TestStepSpec)

Expand Down Expand Up @@ -716,6 +717,19 @@ If a resource doesn't exist yet in the cluster it will fail.</p>

- [ConfigurationSpec](#chainsaw-kyverno-io-v1alpha1-ConfigurationSpec)

## Scenario {#chainsaw-kyverno-io-v1alpha1-Scenario}

**Appears in:**

- [TestSpec](#chainsaw-kyverno-io-v1alpha1-TestSpec)

<p>Scenario defines per scenario bindings.</p>


| Field | Type | Required | Inline | Description |
|---|---|---|---|---|
| `bindings` | [`[]Binding`](#chainsaw-kyverno-io-v1alpha1-Binding) | | | <p>Bindings defines binding key/values.</p> |

## Script {#chainsaw-kyverno-io-v1alpha1-Script}

**Appears in:**
Expand Down Expand Up @@ -771,6 +785,7 @@ If a resource doesn't exist yet in the cluster it will fail.</p>
| `template` | `bool` | | | <p>Template determines whether resources should be considered for templating.</p> |
| `namespace` | `string` | | | <p>Namespace determines whether the test should run in a random ephemeral namespace or not.</p> |
| `namespaceTemplate` | `policy/v1alpha1.Any` | | | <p>NamespaceTemplate defines a template to create the test namespace.</p> |
| `scenarios` | [`[]Scenario`](#chainsaw-kyverno-io-v1alpha1-Scenario) | | | <p>Scenarios defines test scenarios.</p> |
| `bindings` | [`[]Binding`](#chainsaw-kyverno-io-v1alpha1-Binding) | | | <p>Bindings defines additional binding key/values.</p> |
| `steps` | [`[]TestStep`](#chainsaw-kyverno-io-v1alpha1-TestStep) | :white_check_mark: | | <p>Steps defining the test.</p> |
| `catch` | [`[]CatchFinally`](#chainsaw-kyverno-io-v1alpha1-CatchFinally) | | | <p>Catch defines what the steps will execute when an error happens. This will be combined with catch handlers defined at the step level.</p> |
Expand Down
Loading