From d2201dfddddc99216d0122c5e9949cc9a36c99ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= Date: Mon, 19 Feb 2024 14:10:43 +0100 Subject: [PATCH] feat: use temporary KUBECONFIG (#924) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Charles-Edouard Brétéché --- pkg/runner/operations/command/operation.go | 46 ++++++++++-- .../operations/command/operation_test.go | 1 + pkg/runner/operations/script/operation.go | 39 ++++++++-- .../operations/script/operation_test.go | 1 + pkg/runner/processors/clusters.go | 26 +++++-- pkg/runner/processors/step.go | 32 ++++---- pkg/runner/processors/step_test.go | 4 +- pkg/runner/processors/test.go | 5 +- pkg/runner/processors/test_test.go | 4 +- pkg/runner/processors/tests.go | 5 +- pkg/runner/processors/tests_test.go | 8 +- pkg/utils/rest/config.go | 74 +++++++++++++++++++ testdata/e2e/examples/basic/README.md | 3 +- .../e2e/examples/basic/chainsaw-test.yaml | 2 + 14 files changed, 208 insertions(+), 42 deletions(-) diff --git a/pkg/runner/operations/command/operation.go b/pkg/runner/operations/command/operation.go index a394f67e2..a6c0e04a4 100644 --- a/pkg/runner/operations/command/operation.go +++ b/pkg/runner/operations/command/operation.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "github.com/jmespath-community/go-jmespath/pkg/binding" "github.com/kyverno/chainsaw/pkg/apis/v1alpha1" @@ -13,7 +14,9 @@ import ( "github.com/kyverno/chainsaw/pkg/runner/logging" "github.com/kyverno/chainsaw/pkg/runner/operations" "github.com/kyverno/chainsaw/pkg/runner/operations/internal" + restutils "github.com/kyverno/chainsaw/pkg/utils/rest" "github.com/kyverno/kyverno/ext/output/color" + "k8s.io/client-go/rest" ) type operation struct { @@ -21,9 +24,16 @@ type operation struct { basePath string namespace string bindings binding.Bindings + cfg *rest.Config } -func New(command v1alpha1.Command, basePath string, namespace string, bindings binding.Bindings) operations.Operation { +func New( + command v1alpha1.Command, + basePath string, + namespace string, + bindings binding.Bindings, + cfg *rest.Config, +) operations.Operation { if bindings == nil { bindings = binding.NewBindings() } @@ -32,6 +42,7 @@ func New(command v1alpha1.Command, basePath string, namespace string, bindings b basePath: basePath, namespace: namespace, bindings: bindings, + cfg: cfg, } } @@ -40,7 +51,10 @@ func (o *operation) Exec(ctx context.Context) (_err error) { defer func() { internal.LogEnd(logger, logging.Command, _err) }() - cmd, err := o.createCommand(ctx) + cmd, cancel, err := o.createCommand(ctx) + if cancel != nil { + defer cancel() + } if err != nil { return err } @@ -48,21 +62,39 @@ func (o *operation) Exec(ctx context.Context) (_err error) { return o.execute(ctx, cmd) } -func (o *operation) createCommand(ctx context.Context) (*exec.Cmd, error) { +func (o *operation) createCommand(ctx context.Context) (*exec.Cmd, context.CancelFunc, error) { + var cancel context.CancelFunc args := env.Expand(map[string]string{"NAMESPACE": o.namespace}, o.command.Args...) cmd := exec.CommandContext(ctx, o.command.Entrypoint, args...) //nolint:gosec env := os.Environ() if cwd, err := os.Getwd(); err != nil { - return nil, fmt.Errorf("failed to get current working directory (%w)", err) + return nil, nil, fmt.Errorf("failed to get current working directory (%w)", err) } else { env = append(env, fmt.Sprintf("PATH=%s/bin/:%s", cwd, os.Getenv("PATH"))) } env = append(env, fmt.Sprintf("NAMESPACE=%s", o.namespace)) - // TODO - // env = append(env, fmt.Sprintf("KUBECONFIG=%s/bin/:%s", cwd, os.Getenv("PATH"))) + if o.cfg != nil { + f, err := os.CreateTemp(o.basePath, "chainsaw-kubeconfig-") + if err != nil { + return nil, nil, err + } + path := f.Name() + cancel = func() { + err := os.Remove(path) + if err != nil { + logger := internal.GetLogger(ctx, nil) + logger.Log(logging.Script, logging.ErrorStatus, color.BoldYellow, logging.ErrSection(err)) + } + } + defer f.Close() + if err := restutils.Save(o.cfg, f); err != nil { + return nil, cancel, err + } + env = append(env, fmt.Sprintf("KUBECONFIG=%s", filepath.Base(path))) + } cmd.Env = env cmd.Dir = o.basePath - return cmd, nil + return cmd, cancel, nil } func (o *operation) execute(ctx context.Context, cmd *exec.Cmd) error { diff --git a/pkg/runner/operations/command/operation_test.go b/pkg/runner/operations/command/operation_test.go index ac7963663..bf24a03fd 100644 --- a/pkg/runner/operations/command/operation_test.go +++ b/pkg/runner/operations/command/operation_test.go @@ -107,6 +107,7 @@ func Test_operationCommand(t *testing.T) { tt.basePath, tt.namespace, nil, + nil, ) err := operation.Exec(ctx) if tt.wantErr { diff --git a/pkg/runner/operations/script/operation.go b/pkg/runner/operations/script/operation.go index a6e8e3e2e..100a4832e 100644 --- a/pkg/runner/operations/script/operation.go +++ b/pkg/runner/operations/script/operation.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "github.com/jmespath-community/go-jmespath/pkg/binding" "github.com/kyverno/chainsaw/pkg/apis/v1alpha1" @@ -12,7 +13,9 @@ import ( "github.com/kyverno/chainsaw/pkg/runner/logging" "github.com/kyverno/chainsaw/pkg/runner/operations" "github.com/kyverno/chainsaw/pkg/runner/operations/internal" + restutils "github.com/kyverno/chainsaw/pkg/utils/rest" "github.com/kyverno/kyverno/ext/output/color" + "k8s.io/client-go/rest" ) type operation struct { @@ -20,6 +23,7 @@ type operation struct { basePath string namespace string bindings binding.Bindings + cfg *rest.Config } func New( @@ -27,6 +31,7 @@ func New( basePath string, namespace string, bindings binding.Bindings, + cfg *rest.Config, ) operations.Operation { if bindings == nil { bindings = binding.NewBindings() @@ -36,6 +41,7 @@ func New( basePath: basePath, namespace: namespace, bindings: bindings, + cfg: cfg, } } @@ -44,7 +50,10 @@ func (o *operation) Exec(ctx context.Context) (err error) { defer func() { internal.LogEnd(logger, logging.Script, err) }() - cmd, err := o.createCommand(ctx) + cmd, cancel, err := o.createCommand(ctx) + if cancel != nil { + defer cancel() + } if err != nil { return err } @@ -52,20 +61,38 @@ func (o *operation) Exec(ctx context.Context) (err error) { return o.execute(ctx, cmd) } -func (o *operation) createCommand(ctx context.Context) (*exec.Cmd, error) { +func (o *operation) createCommand(ctx context.Context) (*exec.Cmd, context.CancelFunc, error) { + var cancel context.CancelFunc cmd := exec.CommandContext(ctx, "sh", "-c", o.script.Content) //nolint:gosec env := os.Environ() if cwd, err := os.Getwd(); err != nil { - return nil, fmt.Errorf("failed to get current working directory (%w)", err) + return nil, nil, fmt.Errorf("failed to get current working directory (%w)", err) } else { env = append(env, fmt.Sprintf("PATH=%s/bin/:%s", cwd, os.Getenv("PATH"))) } env = append(env, fmt.Sprintf("NAMESPACE=%s", o.namespace)) - // TODO - // env = append(env, fmt.Sprintf("KUBECONFIG=%s/bin/:%s", cwd, os.Getenv("PATH"))) + if o.cfg != nil { + f, err := os.CreateTemp(o.basePath, "chainsaw-kubeconfig-") + if err != nil { + return nil, nil, err + } + path := f.Name() + cancel = func() { + err := os.Remove(path) + if err != nil { + logger := internal.GetLogger(ctx, nil) + logger.Log(logging.Script, logging.ErrorStatus, color.BoldYellow, logging.ErrSection(err)) + } + } + defer f.Close() + if err := restutils.Save(o.cfg, f); err != nil { + return nil, cancel, err + } + env = append(env, fmt.Sprintf("KUBECONFIG=%s", filepath.Base(path))) + } cmd.Env = env cmd.Dir = o.basePath - return cmd, nil + return cmd, cancel, nil } func (o *operation) execute(ctx context.Context, cmd *exec.Cmd) error { diff --git a/pkg/runner/operations/script/operation_test.go b/pkg/runner/operations/script/operation_test.go index 8d9b85f18..ede090eb1 100644 --- a/pkg/runner/operations/script/operation_test.go +++ b/pkg/runner/operations/script/operation_test.go @@ -101,6 +101,7 @@ func Test_operationScript(t *testing.T) { tt.basePath, tt.namespace, nil, + nil, ) err := operation.Exec(ctx) if tt.wantErr { diff --git a/pkg/runner/processors/clusters.go b/pkg/runner/processors/clusters.go index a4f53eb10..76c7fee4e 100644 --- a/pkg/runner/processors/clusters.go +++ b/pkg/runner/processors/clusters.go @@ -8,30 +8,40 @@ import ( const DefaultClient = "" +type cluster struct { + config *rest.Config + client client.Client +} + type clusters struct { - clients map[string]client.Client + clients map[string]cluster } func NewClusters() clusters { return clusters{ - clients: map[string]client.Client{}, + clients: map[string]cluster{}, } } -func (c *clusters) Register(name string, cfg *rest.Config) error { - client, err := client.New(cfg) +func (c *clusters) Register(name string, config *rest.Config) error { + client, err := client.New(config) if err != nil { return err } - c.clients[DefaultClient] = runnerclient.New(client) + c.clients[DefaultClient] = cluster{ + config: config, + client: runnerclient.New(client), + } return nil } -func (c *clusters) client(names ...string) client.Client { +func (c *clusters) client(names ...string) (*rest.Config, client.Client) { for _, name := range names { if name != "" { - return c.clients[name] + cluster := c.clients[name] + return cluster.config, cluster.client } } - return c.clients[DefaultClient] + cluster := c.clients[DefaultClient] + return cluster.config, cluster.client } diff --git a/pkg/runner/processors/step.go b/pkg/runner/processors/step.go index fe2b78918..0dbcd613f 100644 --- a/pkg/runner/processors/step.go +++ b/pkg/runner/processors/step.go @@ -30,6 +30,7 @@ import ( "github.com/kyverno/chainsaw/pkg/testing" "github.com/kyverno/kyverno/ext/output/color" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/rest" "k8s.io/utils/clock" ) @@ -51,6 +52,9 @@ func NewStepProcessor( cleaner *cleaner, bindings binding.Bindings, ) StepProcessor { + if bindings == nil { + bindings = binding.NewBindings() + } return &stepProcessor{ config: config, clusters: clusters, @@ -318,7 +322,7 @@ func (p *stepProcessor) applyOperation(ctx context.Context, op v1alpha1.Apply) ( } dryRun := op.DryRun != nil && *op.DryRun template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template) - cluster := p.getClient(op.Cluster, dryRun) + _, cluster := p.getClient(op.Cluster, dryRun) bindings := p.bindings.Register("$client", binding.NewBinding(cluster)) for _, resource := range resources { if err := p.prepareResource(resource); err != nil { @@ -343,7 +347,7 @@ func (p *stepProcessor) assertOperation(ctx context.Context, op v1alpha1.Assert) p.stepReport.AddOperation(operationReport) } template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template) - cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster) + _, cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster) bindings := p.bindings.Register("$client", binding.NewBinding(cluster)) for _, resource := range resources { ops = append(ops, operation{ @@ -364,11 +368,11 @@ func (p *stepProcessor) commandOperation(ctx context.Context, op v1alpha1.Comman if p.namespacer != nil { ns = p.namespacer.GetNamespace() } - cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster) + config, cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster) bindings := p.bindings.Register("$client", binding.NewBinding(cluster)) return operation{ timeout: timeout.Get(op.Timeout, p.timeouts.ExecDuration()), - operation: opcommand.New(op, p.test.BasePath, ns, bindings), + operation: opcommand.New(op, p.test.BasePath, ns, bindings, config), operationReport: operationReport, } } @@ -385,7 +389,7 @@ func (p *stepProcessor) createOperation(ctx context.Context, op v1alpha1.Create) } dryRun := op.DryRun != nil && *op.DryRun template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template) - cluster := p.getClient(op.Cluster, dryRun) + _, cluster := p.getClient(op.Cluster, dryRun) bindings := p.bindings.Register("$client", binding.NewBinding(cluster)) for _, resource := range resources { if err := p.prepareResource(resource); err != nil { @@ -411,7 +415,7 @@ func (p *stepProcessor) deleteOperation(ctx context.Context, op v1alpha1.Delete) p.stepReport.AddOperation(operationReport) } template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template) - cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster) + _, cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster) bindings := p.bindings.Register("$client", binding.NewBinding(cluster)) return &operation{ timeout: timeout.Get(op.Timeout, p.timeouts.DeleteDuration()), @@ -431,7 +435,7 @@ func (p *stepProcessor) errorOperation(ctx context.Context, op v1alpha1.Error) ( p.stepReport.AddOperation(operationReport) } template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template) - cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster) + _, cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster) bindings := p.bindings.Register("$client", binding.NewBinding(cluster)) for _, resource := range resources { ops = append(ops, operation{ @@ -455,7 +459,7 @@ func (p *stepProcessor) patchOperation(ctx context.Context, op v1alpha1.Patch) ( } dryRun := op.DryRun != nil && *op.DryRun template := template.Get(op.Template, p.step.Template, p.test.Spec.Template, p.config.Template) - cluster := p.getClient(op.Cluster, dryRun) + _, cluster := p.getClient(op.Cluster, dryRun) bindings := p.bindings.Register("$client", binding.NewBinding(cluster)) for _, resource := range resources { if err := p.prepareResource(resource); err != nil { @@ -478,11 +482,11 @@ func (p *stepProcessor) scriptOperation(ctx context.Context, op v1alpha1.Script) if p.namespacer != nil { ns = p.namespacer.GetNamespace() } - cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster) + config, cluster := p.clusters.client(op.Cluster, p.step.Cluster, p.test.Spec.Cluster) bindings := p.bindings.Register("$client", binding.NewBinding(cluster)) return operation{ timeout: timeout.Get(op.Timeout, p.timeouts.ExecDuration()), - operation: opscript.New(op, p.test.BasePath, ns, bindings), + operation: opscript.New(op, p.test.BasePath, ns, bindings, config), operationReport: operationReport, } } @@ -559,12 +563,12 @@ func (p *stepProcessor) prepareResource(resource unstructured.Unstructured) erro return nil } -func (p *stepProcessor) getClient(opCluster string, dryRun bool) client.Client { - cluster := p.clusters.client(opCluster, p.step.Cluster, p.test.Spec.Cluster) +func (p *stepProcessor) getClient(opCluster string, dryRun bool) (*rest.Config, client.Client) { + config, cluster := p.clusters.client(opCluster, p.step.Cluster, p.test.Spec.Cluster) if !dryRun { - return cluster + return config, cluster } - return client.DryRun(cluster) + return config, client.DryRun(cluster) } func (p *stepProcessor) getCleaner(ctx context.Context, dryRun bool) cleanup.Cleaner { diff --git a/pkg/runner/processors/step_test.go b/pkg/runner/processors/step_test.go index c662aaa85..a8e213d95 100644 --- a/pkg/runner/processors/step_test.go +++ b/pkg/runner/processors/step_test.go @@ -1025,7 +1025,9 @@ func TestStepProcessor_Run(t *testing.T) { t.Run(tc.name, func(t *testing.T) { clusters := NewClusters() if tc.client != nil { - clusters.clients[DefaultClient] = tc.client + clusters.clients[DefaultClient] = cluster{ + client: tc.client, + } } stepProcessor := NewStepProcessor( tc.config, diff --git a/pkg/runner/processors/test.go b/pkg/runner/processors/test.go index 33d90ab91..de8ae21fe 100644 --- a/pkg/runner/processors/test.go +++ b/pkg/runner/processors/test.go @@ -39,6 +39,9 @@ func NewTestProcessor( shouldFailFast *atomic.Bool, bindings binding.Bindings, ) TestProcessor { + if bindings == nil { + bindings = binding.NewBindings() + } return &testProcessor{ config: config, clusters: clusters, @@ -112,7 +115,7 @@ func (p *testProcessor) Run(ctx context.Context, nspacer namespacer.Namespacer) cleanupLogger := logging.NewLogger(t, p.clock, p.test.Name, fmt.Sprintf("%-*s", size, "@cleanup")) var namespace *corev1.Namespace bindings := p.bindings - cluster := p.clusters.client(p.test.Spec.Cluster) + _, cluster := p.clusters.client(p.test.Spec.Cluster) if cluster != nil { bindings = bindings.Register("$client", binding.NewBinding(cluster)) if nspacer == nil || p.test.Spec.Namespace != "" { diff --git a/pkg/runner/processors/test_test.go b/pkg/runner/processors/test_test.go index 528a6b418..cc5474f6b 100644 --- a/pkg/runner/processors/test_test.go +++ b/pkg/runner/processors/test_test.go @@ -390,7 +390,9 @@ func TestTestProcessor_Run(t *testing.T) { shouldFailVar.Store(tc.shouldFailFast) clusters := NewClusters() if tc.client != nil { - clusters.clients[DefaultClient] = tc.client + clusters.clients[DefaultClient] = cluster{ + client: tc.client, + } } processor := NewTestProcessor( tc.config, diff --git a/pkg/runner/processors/tests.go b/pkg/runner/processors/tests.go index 61b75e28a..9217dfea0 100644 --- a/pkg/runner/processors/tests.go +++ b/pkg/runner/processors/tests.go @@ -37,6 +37,9 @@ func NewTestsProcessor( bindings binding.Bindings, tests ...discovery.Test, ) TestsProcessor { + if bindings == nil { + bindings = binding.NewBindings() + } return &testsProcessor{ config: config, clusters: clusters, @@ -69,7 +72,7 @@ func (p *testsProcessor) Run(ctx context.Context) { }) var nspacer namespacer.Namespacer bindings := p.bindings - cluster := p.clusters.client() + _, cluster := p.clusters.client() if cluster != nil { bindings = bindings.Register("$client", binding.NewBinding(cluster)) if p.config.Namespace != "" { diff --git a/pkg/runner/processors/tests_test.go b/pkg/runner/processors/tests_test.go index e2db6ce2a..4a3087fdf 100644 --- a/pkg/runner/processors/tests_test.go +++ b/pkg/runner/processors/tests_test.go @@ -164,7 +164,9 @@ func TestTestsProcessor_Run(t *testing.T) { t.Run(tc.name, func(t *testing.T) { clusters := NewClusters() if tc.client != nil { - clusters.clients[DefaultClient] = tc.client + clusters.clients[DefaultClient] = cluster{ + client: tc.client, + } } processor := NewTestsProcessor( tc.config, @@ -223,7 +225,9 @@ func TestCreateTestProcessor(t *testing.T) { localTC := tc clusters := NewClusters() if localTC.client != nil { - clusters.clients[DefaultClient] = localTC.client + clusters.clients[DefaultClient] = cluster{ + client: localTC.client, + } } processor := testsProcessor{ config: localTC.config, diff --git a/pkg/utils/rest/config.go b/pkg/utils/rest/config.go index cedbe8b28..5a75d877b 100644 --- a/pkg/utils/rest/config.go +++ b/pkg/utils/rest/config.go @@ -1,8 +1,12 @@ package rest import ( + "io" + + "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + api "k8s.io/client-go/tools/clientcmd/api/v1" ) func DefaultConfig(overrides clientcmd.ConfigOverrides) (*rest.Config, error) { @@ -24,3 +28,73 @@ func load(loader clientcmd.ClientConfigLoader, overrides clientcmd.ConfigOverrid config.Burst = 300 return config, nil } + +func Save(cfg *rest.Config, w io.Writer) error { + var authProvider *api.AuthProviderConfig + var execConfig *api.ExecConfig + if cfg.AuthProvider != nil { + authProvider = &api.AuthProviderConfig{ + Name: cfg.AuthProvider.Name, + Config: cfg.AuthProvider.Config, + } + } + + if cfg.ExecProvider != nil { + execConfig = &api.ExecConfig{ + Command: cfg.ExecProvider.Command, + Args: cfg.ExecProvider.Args, + APIVersion: cfg.ExecProvider.APIVersion, + Env: []api.ExecEnvVar{}, + } + + for _, envVar := range cfg.ExecProvider.Env { + execConfig.Env = append(execConfig.Env, api.ExecEnvVar{ + Name: envVar.Name, + Value: envVar.Value, + }) + } + } + err := rest.LoadTLSFiles(cfg) + if err != nil { + return err + } + return json.NewYAMLSerializer(json.DefaultMetaFactory, nil, nil).Encode(&api.Config{ + CurrentContext: "chainsaw", + Clusters: []api.NamedCluster{ + { + Name: "chainsaw", + Cluster: api.Cluster{ + Server: cfg.Host, + CertificateAuthorityData: cfg.TLSClientConfig.CAData, + InsecureSkipTLSVerify: cfg.TLSClientConfig.Insecure, + }, + }, + }, + Contexts: []api.NamedContext{ + { + Name: "chainsaw", + Context: api.Context{ + Cluster: "chainsaw", + AuthInfo: "chainsaw", + }, + }, + }, + AuthInfos: []api.NamedAuthInfo{ + { + Name: "chainsaw", + AuthInfo: api.AuthInfo{ + ClientCertificateData: cfg.TLSClientConfig.CertData, + ClientKeyData: cfg.TLSClientConfig.KeyData, + Token: cfg.BearerToken, + Username: cfg.Username, + Password: cfg.Password, + Impersonate: cfg.Impersonate.UserName, + ImpersonateGroups: cfg.Impersonate.Groups, + ImpersonateUserExtra: cfg.Impersonate.Extra, + AuthProvider: authProvider, + Exec: execConfig, + }, + }, + }, + }, w) +} diff --git a/testdata/e2e/examples/basic/README.md b/testdata/e2e/examples/basic/README.md index fe21537d6..10462c229 100644 --- a/testdata/e2e/examples/basic/README.md +++ b/testdata/e2e/examples/basic/README.md @@ -6,7 +6,7 @@ This is a very simple test that creates a configmap and checks the content is as | # | Name | Try | Catch | Finally | |:-:|---|:-:|:-:|:-:| -| 1 | [step-1](#step-step-1) | 2 | 0 | 0 | +| 1 | [step-1](#step-step-1) | 3 | 0 | 0 | ## Step: `step-1` @@ -18,3 +18,4 @@ This steps applies the configmap in the cluster and checks the configmap content |:-:|---|---| | 1 | `apply` | Create the configmap. | | 2 | `assert` | Check the configmap content. | +| 3 | `script` | *No description* | diff --git a/testdata/e2e/examples/basic/chainsaw-test.yaml b/testdata/e2e/examples/basic/chainsaw-test.yaml index c7d78dd9f..942a811be 100644 --- a/testdata/e2e/examples/basic/chainsaw-test.yaml +++ b/testdata/e2e/examples/basic/chainsaw-test.yaml @@ -14,3 +14,5 @@ spec: - description: Check the configmap content. assert: file: configmap-assert.yaml + - script: + content: kubectl get pod -A \ No newline at end of file