From 4028cbd6683d80daf2a3d353999576df74812825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= Date: Mon, 16 Dec 2024 13:37:58 +0100 Subject: [PATCH] chore: add unit tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Charles-Edouard Brétéché --- pkg/commands/assert/command_test.go | 11 +- pkg/runner/context.go | 74 --- pkg/runner/context/legacy.go | 73 +++ pkg/runner/{step.go => operations/factory.go} | 574 ++++++------------ pkg/runner/{ => operations}/operation.go | 15 +- pkg/runner/{ => operations}/operation_test.go | 36 +- pkg/runner/runner.go | 246 +++++++- pkg/runner/runner_test.go | 2 +- pkg/runner/step_test.go | 15 +- pkg/testing/context.go | 35 -- pkg/testing/err_reader.go | 11 - pkg/testing/mock.go | 86 --- 12 files changed, 525 insertions(+), 653 deletions(-) delete mode 100644 pkg/runner/context.go create mode 100644 pkg/runner/context/legacy.go rename pkg/runner/{step.go => operations/factory.go} (55%) rename pkg/runner/{ => operations}/operation.go (80%) rename pkg/runner/{ => operations}/operation_test.go (67%) delete mode 100644 pkg/testing/context.go delete mode 100644 pkg/testing/err_reader.go delete mode 100644 pkg/testing/mock.go diff --git a/pkg/commands/assert/command_test.go b/pkg/commands/assert/command_test.go index 204db80db..bd8120cce 100644 --- a/pkg/commands/assert/command_test.go +++ b/pkg/commands/assert/command_test.go @@ -3,23 +3,30 @@ package assert import ( "bytes" "context" + "errors" "io" "os" "path" "path/filepath" + "testing" "time" "github.com/kyverno/chainsaw/pkg/client" tclient "github.com/kyverno/chainsaw/pkg/client/testing" "github.com/kyverno/chainsaw/pkg/commands/root" fakeNamespacer "github.com/kyverno/chainsaw/pkg/engine/namespacer/testing" - "github.com/kyverno/chainsaw/pkg/testing" "github.com/spf13/cobra" testify "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +type errReader struct{} + +func (e *errReader) Read(p []byte) (n int, err error) { + return 0, errors.New("error reading from stdin") +} + func Test_Execute(t *testing.T) { basePath := path.Join("..", "..", "..", "testdata", "commands", "assert") tests := []struct { @@ -320,7 +327,7 @@ data: cmd.Args = cobra.RangeArgs(0, 1) cmd.SilenceUsage = true cmd.SetOut(bytes.NewBufferString("")) - cmd.SetIn(&testing.ErrReader{}) + cmd.SetIn(&errReader{}) return cmd }, opts: options{ diff --git a/pkg/runner/context.go b/pkg/runner/context.go deleted file mode 100644 index 720e8a68b..000000000 --- a/pkg/runner/context.go +++ /dev/null @@ -1,74 +0,0 @@ -package runner - -import ( - "github.com/kyverno/chainsaw/pkg/apis/v1alpha1" - enginecontext "github.com/kyverno/chainsaw/pkg/runner/context" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type contextData struct { - basePath string - catch []v1alpha1.CatchFinally - cluster *string - clusters v1alpha1.Clusters - delayBeforeCleanup *metav1.Duration - deletionPropagation *metav1.DeletionPropagation - dryRun *bool - skipDelete *bool - templating *bool - terminationGrace *metav1.Duration - timeouts *v1alpha1.Timeouts -} - -func setupContext(tc enginecontext.TestContext, data contextData) (enginecontext.TestContext, error) { - if len(data.catch) > 0 { - tc = tc.WithCatch(data.catch...) - } - if data.dryRun != nil { - tc = tc.WithDryRun(*data.dryRun) - } - if data.delayBeforeCleanup != nil { - tc = tc.WithDelayBeforeCleanup(&data.delayBeforeCleanup.Duration) - } - if data.deletionPropagation != nil { - tc = tc.WithDeletionPropagation(*data.deletionPropagation) - } - if data.skipDelete != nil { - tc = tc.WithSkipDelete(*data.skipDelete) - } - if data.templating != nil { - tc = tc.WithTemplating(*data.templating) - } - if data.terminationGrace != nil { - tc = tc.WithTerminationGrace(&data.terminationGrace.Duration) - } - if data.timeouts != nil { - tc = tc.WithTimeouts(*data.timeouts) - } - tc = enginecontext.WithClusters(tc, data.basePath, data.clusters) - if data.cluster != nil { - if _tc, err := enginecontext.WithCurrentCluster(tc, *data.cluster); err != nil { - return tc, err - } else { - tc = _tc - } - } - return tc, nil -} - -func setupBindings(tc enginecontext.TestContext, bindings ...v1alpha1.Binding) (enginecontext.TestContext, error) { - if _tc, err := enginecontext.WithBindings(tc, bindings...); err != nil { - return tc, err - } else { - tc = _tc - } - return tc, nil -} - -func setupContextAndBindings(tc enginecontext.TestContext, data contextData, bindings ...v1alpha1.Binding) (enginecontext.TestContext, error) { - if tc, err := setupContext(tc, data); err != nil { - return tc, err - } else { - return setupBindings(tc, bindings...) - } -} diff --git a/pkg/runner/context/legacy.go b/pkg/runner/context/legacy.go new file mode 100644 index 000000000..63aa7be08 --- /dev/null +++ b/pkg/runner/context/legacy.go @@ -0,0 +1,73 @@ +package context + +import ( + "github.com/kyverno/chainsaw/pkg/apis/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ContextData struct { + BasePath string + Catch []v1alpha1.CatchFinally + Cluster *string + Clusters v1alpha1.Clusters + DelayBeforeCleanup *metav1.Duration + DeletionPropagation *metav1.DeletionPropagation + DryRun *bool + SkipDelete *bool + Templating *bool + TerminationGrace *metav1.Duration + Timeouts *v1alpha1.Timeouts +} + +func SetupContext(tc TestContext, data ContextData) (TestContext, error) { + if len(data.Catch) > 0 { + tc = tc.WithCatch(data.Catch...) + } + if data.DryRun != nil { + tc = tc.WithDryRun(*data.DryRun) + } + if data.DelayBeforeCleanup != nil { + tc = tc.WithDelayBeforeCleanup(&data.DelayBeforeCleanup.Duration) + } + if data.DeletionPropagation != nil { + tc = tc.WithDeletionPropagation(*data.DeletionPropagation) + } + if data.SkipDelete != nil { + tc = tc.WithSkipDelete(*data.SkipDelete) + } + if data.Templating != nil { + tc = tc.WithTemplating(*data.Templating) + } + if data.TerminationGrace != nil { + tc = tc.WithTerminationGrace(&data.TerminationGrace.Duration) + } + if data.Timeouts != nil { + tc = tc.WithTimeouts(*data.Timeouts) + } + tc = WithClusters(tc, data.BasePath, data.Clusters) + if data.Cluster != nil { + if _tc, err := WithCurrentCluster(tc, *data.Cluster); err != nil { + return tc, err + } else { + tc = _tc + } + } + return tc, nil +} + +func SetupBindings(tc TestContext, bindings ...v1alpha1.Binding) (TestContext, error) { + if _tc, err := WithBindings(tc, bindings...); err != nil { + return tc, err + } else { + tc = _tc + } + return tc, nil +} + +func SetupContextAndBindings(tc TestContext, data ContextData, bindings ...v1alpha1.Binding) (TestContext, error) { + if tc, err := SetupContext(tc, data); err != nil { + return tc, err + } else { + return SetupBindings(tc, bindings...) + } +} diff --git a/pkg/runner/step.go b/pkg/runner/operations/factory.go similarity index 55% rename from pkg/runner/step.go rename to pkg/runner/operations/factory.go index d2ec5f21a..4c2f87db2 100644 --- a/pkg/runner/step.go +++ b/pkg/runner/operations/factory.go @@ -1,9 +1,8 @@ -package runner +package operations import ( "context" "errors" - "fmt" "net/url" "path/filepath" "time" @@ -25,221 +24,46 @@ import ( opsleep "github.com/kyverno/chainsaw/pkg/engine/operations/sleep" opupdate "github.com/kyverno/chainsaw/pkg/engine/operations/update" "github.com/kyverno/chainsaw/pkg/loaders/resource" - "github.com/kyverno/chainsaw/pkg/logging" "github.com/kyverno/chainsaw/pkg/model" enginecontext "github.com/kyverno/chainsaw/pkg/runner/context" - "github.com/kyverno/chainsaw/pkg/testing" "github.com/kyverno/kyverno-json/pkg/core/compilers" - "github.com/kyverno/pkg/ext/output/color" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/utils/ptr" ) -func (r *runner) runStep( - ctx context.Context, - t testing.TTest, - basePath string, - namespacer namespacer.Namespacer, - tc enginecontext.TestContext, - step v1alpha1.TestStep, - testReport *model.TestReport, -) bool { - report := &model.StepReport{ - Name: step.Name, - StartTime: time.Now(), - } - defer func() { - report.EndTime = time.Now() - testReport.Add(report) - }() - if step.Compiler != nil { - tc = tc.WithDefaultCompiler(string(*step.Compiler)) - } - contextData := contextData{ - basePath: basePath, - catch: step.Catch, - cluster: step.Cluster, - clusters: step.Clusters, - deletionPropagation: step.DeletionPropagationPolicy, - skipDelete: step.SkipDelete, - templating: step.Template, - timeouts: step.Timeouts, - } - tc, err := setupContextAndBindings(tc, contextData, step.Bindings...) - if err != nil { - t.Fail() - logging.Log(ctx, logging.Internal, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) - r.onFail() - return true - } - cleaner := cleaner.New(tc.Timeouts().Cleanup.Duration, tc.DelayBeforeCleanup(), tc.DeletionPropagation()) - t.Cleanup(func() { - if !cleaner.Empty() || len(step.Cleanup) != 0 { - report := &model.StepReport{ - Name: fmt.Sprintf("cleanup (%s)", report.Name), - StartTime: time.Now(), - } - defer func() { - report.EndTime = time.Now() - testReport.Add(report) - }() - logging.Log(ctx, logging.Cleanup, logging.BeginStatus, nil, color.BoldFgCyan) - defer func() { - logging.Log(ctx, logging.Cleanup, logging.EndStatus, nil, color.BoldFgCyan) - }() - if !cleaner.Empty() { - if errs := cleaner.Run(ctx, report); len(errs) != 0 { - t.Fail() - for _, err := range errs { - logging.Log(ctx, logging.Cleanup, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) - } - r.onFail() - } - } - for i, operation := range step.Cleanup { - operationTc := tc - if operation.Compiler != nil { - operationTc = operationTc.WithDefaultCompiler(string(*operation.Compiler)) - } - operations, err := finallyOperation(operationTc.Compilers(), basePath, i, namespacer, operationTc.Bindings(), operation) - if err != nil { - t.Fail() - logging.Log(ctx, logging.Cleanup, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) - r.onFail() - } - for _, operation := range operations { - _, err := operation.execute(ctx, operationTc, report) - if err != nil { - t.Fail() - r.onFail() - } - } - } - } - }) - if len(step.Finally) != 0 { - defer func() { - logging.Log(ctx, logging.Finally, logging.BeginStatus, nil, color.BoldFgCyan) - defer func() { - logging.Log(ctx, logging.Finally, logging.EndStatus, nil, color.BoldFgCyan) - }() - for i, operation := range step.Finally { - operationTc := tc - if operation.Compiler != nil { - operationTc = operationTc.WithDefaultCompiler(string(*operation.Compiler)) - } - operations, err := finallyOperation(operationTc.Compilers(), basePath, i, namespacer, operationTc.Bindings(), operation) - if err != nil { - t.Fail() - logging.Log(ctx, logging.Finally, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) - r.onFail() - } - for _, operation := range operations { - _, err := operation.execute(ctx, operationTc, report) - if err != nil { - t.Fail() - r.onFail() - } - } - } - }() - } - if catch := tc.Catch(); len(catch) != 0 { - defer func() { - if t.Failed() { - logging.Log(ctx, logging.Catch, logging.BeginStatus, nil, color.BoldFgCyan) - defer func() { - logging.Log(ctx, logging.Catch, logging.EndStatus, nil, color.BoldFgCyan) - }() - for i, operation := range catch { - operationTc := tc - if operation.Compiler != nil { - operationTc = operationTc.WithDefaultCompiler(string(*operation.Compiler)) - } - operations, err := catchOperation(operationTc.Compilers(), basePath, i, namespacer, operationTc.Bindings(), operation) - if err != nil { - t.Fail() - logging.Log(ctx, logging.Catch, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) - r.onFail() - } - for _, operation := range operations { - _, err := operation.execute(ctx, operationTc, report) - if err != nil { - t.Fail() - r.onFail() - } - } - } - } - }() - } - logging.Log(ctx, logging.Try, logging.BeginStatus, nil, color.BoldFgCyan) - defer func() { - logging.Log(ctx, logging.Try, logging.EndStatus, nil, color.BoldFgCyan) - }() - for i, operation := range step.Try { - operationTc := tc - if operation.Compiler != nil { - operationTc = operationTc.WithDefaultCompiler(string(*operation.Compiler)) - } - continueOnError := operation.ContinueOnError != nil && *operation.ContinueOnError - operations, err := tryOperation(operationTc.Compilers(), basePath, i, namespacer, operationTc.Bindings(), operation, cleaner) - if err != nil { - t.Fail() - logging.Log(ctx, logging.Try, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) - r.onFail() - return true - } - for _, operation := range operations { - outputs, err := operation.execute(ctx, operationTc, report) - if err != nil { - t.Fail() - r.onFail() - if !continueOnError { - return true - } - } - for k, v := range outputs { - tc = tc.WithBinding(k, v) - } - } - } - return false -} - -func tryOperation(compilers compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, bindings apis.Bindings, handler v1alpha1.Operation, cleaner cleaner.CleanerCollector) ([]operation, error) { - var ops []operation +func TryOperation(tc enginecontext.TestContext, basePath string, namespacer namespacer.Namespacer, handler v1alpha1.Operation, cleaner cleaner.CleanerCollector) ([]Operation, error) { + var ops []Operation if handler.Apply != nil { - loaded, err := applyOperation(compilers, basePath, id+1, namespacer, cleaner, bindings, *handler.Apply) + loaded, err := applyOperation(tc.Compilers(), basePath, namespacer, cleaner, tc.Bindings(), *handler.Apply) if err != nil { return nil, err } ops = append(ops, loaded...) } else if handler.Assert != nil { - loaded, err := assertOperation(compilers, basePath, id+1, namespacer, bindings, *handler.Assert) + loaded, err := assertOperation(tc.Compilers(), basePath, namespacer, tc.Bindings(), *handler.Assert) if err != nil { return nil, err } ops = append(ops, loaded...) } else if handler.Command != nil { - ops = append(ops, commandOperation(compilers, basePath, id+1, namespacer, *handler.Command)) + ops = append(ops, commandOperation(tc.Compilers(), basePath, namespacer, *handler.Command)) } else if handler.Create != nil { - loaded, err := createOperation(compilers, basePath, id+1, namespacer, cleaner, bindings, *handler.Create) + loaded, err := createOperation(tc.Compilers(), basePath, namespacer, cleaner, tc.Bindings(), *handler.Create) if err != nil { return nil, err } ops = append(ops, loaded...) } else if handler.Delete != nil { - loaded, err := deleteOperation(compilers, basePath, id+1, namespacer, bindings, *handler.Delete) + loaded, err := deleteOperation(tc.Compilers(), basePath, namespacer, tc.Bindings(), *handler.Delete) if err != nil { return nil, err } ops = append(ops, loaded...) } else if handler.Describe != nil { - ops = append(ops, describeOperation(compilers, basePath, id+1, namespacer, *handler.Describe)) + ops = append(ops, describeOperation(tc.Compilers(), basePath, namespacer, *handler.Describe)) } else if handler.Error != nil { - loaded, err := errorOperation(compilers, basePath, id+1, namespacer, bindings, *handler.Error) + loaded, err := errorOperation(tc.Compilers(), basePath, namespacer, tc.Bindings(), *handler.Error) if err != nil { return nil, err } @@ -257,41 +81,41 @@ func tryOperation(compilers compilers.Compilers, basePath string, id int, namesp ActionObjectSelector: handler.Events.ActionObjectSelector, }, } - ops = append(ops, getOperation(compilers, basePath, id+1, namespacer, get)) + ops = append(ops, getOperation(tc.Compilers(), basePath, namespacer, get)) } else if handler.Get != nil { - ops = append(ops, getOperation(compilers, basePath, id+1, namespacer, *handler.Get)) + ops = append(ops, getOperation(tc.Compilers(), basePath, namespacer, *handler.Get)) } else if handler.Patch != nil { - loaded, err := patchOperation(compilers, basePath, id+1, namespacer, bindings, *handler.Patch) + loaded, err := patchOperation(tc.Compilers(), basePath, namespacer, tc.Bindings(), *handler.Patch) if err != nil { return nil, err } ops = append(ops, loaded...) } else if handler.PodLogs != nil { - ops = append(ops, logsOperation(compilers, basePath, id+1, namespacer, *handler.PodLogs)) + ops = append(ops, logsOperation(tc.Compilers(), basePath, namespacer, *handler.PodLogs)) } else if handler.Proxy != nil { - ops = append(ops, proxyOperation(compilers, basePath, id+1, namespacer, *handler.Proxy)) + ops = append(ops, proxyOperation(tc.Compilers(), basePath, namespacer, *handler.Proxy)) } else if handler.Script != nil { - ops = append(ops, scriptOperation(compilers, basePath, id+1, namespacer, *handler.Script)) + ops = append(ops, scriptOperation(tc.Compilers(), basePath, namespacer, *handler.Script)) } else if handler.Sleep != nil { - ops = append(ops, sleepOperation(compilers, id+1, *handler.Sleep)) + ops = append(ops, sleepOperation(tc.Compilers(), *handler.Sleep)) } else if handler.Update != nil { - loaded, err := updateOperation(compilers, basePath, id+1, namespacer, bindings, *handler.Update) + loaded, err := updateOperation(tc.Compilers(), basePath, namespacer, tc.Bindings(), *handler.Update) if err != nil { return nil, err } ops = append(ops, loaded...) } else if handler.Wait != nil { - ops = append(ops, waitOperation(compilers, basePath, id+1, namespacer, *handler.Wait)) + ops = append(ops, waitOperation(tc.Compilers(), basePath, namespacer, *handler.Wait)) } else { return nil, errors.New("no operation found") } return ops, nil } -func catchOperation(compilers compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, bindings apis.Bindings, handler v1alpha1.CatchFinally) ([]operation, error) { - var ops []operation +func CatchOperation(compilers compilers.Compilers, basePath string, namespacer namespacer.Namespacer, bindings apis.Bindings, handler v1alpha1.CatchFinally) ([]Operation, error) { + var ops []Operation if handler.PodLogs != nil { - ops = append(ops, logsOperation(compilers, basePath, id+1, namespacer, *handler.PodLogs)) + ops = append(ops, logsOperation(compilers, basePath, namespacer, *handler.PodLogs)) } else if handler.Events != nil { get := v1alpha1.Get{ ActionClusters: handler.Events.ActionClusters, @@ -305,35 +129,35 @@ func catchOperation(compilers compilers.Compilers, basePath string, id int, name ActionObjectSelector: handler.Events.ActionObjectSelector, }, } - ops = append(ops, getOperation(compilers, basePath, id+1, namespacer, get)) + ops = append(ops, getOperation(compilers, basePath, namespacer, get)) } else if handler.Describe != nil { - ops = append(ops, describeOperation(compilers, basePath, id+1, namespacer, *handler.Describe)) + ops = append(ops, describeOperation(compilers, basePath, namespacer, *handler.Describe)) } else if handler.Get != nil { - ops = append(ops, getOperation(compilers, basePath, id+1, namespacer, *handler.Get)) + ops = append(ops, getOperation(compilers, basePath, namespacer, *handler.Get)) } else if handler.Delete != nil { - loaded, err := deleteOperation(compilers, basePath, id+1, namespacer, bindings, *handler.Delete) + loaded, err := deleteOperation(compilers, basePath, namespacer, bindings, *handler.Delete) if err != nil { return nil, err } ops = append(ops, loaded...) } else if handler.Command != nil { - ops = append(ops, commandOperation(compilers, basePath, id+1, namespacer, *handler.Command)) + ops = append(ops, commandOperation(compilers, basePath, namespacer, *handler.Command)) } else if handler.Script != nil { - ops = append(ops, scriptOperation(compilers, basePath, id+1, namespacer, *handler.Script)) + ops = append(ops, scriptOperation(compilers, basePath, namespacer, *handler.Script)) } else if handler.Sleep != nil { - ops = append(ops, sleepOperation(compilers, id+1, *handler.Sleep)) + ops = append(ops, sleepOperation(compilers, *handler.Sleep)) } else if handler.Wait != nil { - ops = append(ops, waitOperation(compilers, basePath, id+1, namespacer, *handler.Wait)) + ops = append(ops, waitOperation(compilers, basePath, namespacer, *handler.Wait)) } else { return nil, errors.New("no operation found") } return ops, nil } -func finallyOperation(compilers compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, bindings apis.Bindings, handler v1alpha1.CatchFinally) ([]operation, error) { - var ops []operation +func FinallyOperation(compilers compilers.Compilers, basePath string, namespacer namespacer.Namespacer, bindings apis.Bindings, handler v1alpha1.CatchFinally) ([]Operation, error) { + var ops []Operation if handler.PodLogs != nil { - ops = append(ops, logsOperation(compilers, basePath, id+1, namespacer, *handler.PodLogs)) + ops = append(ops, logsOperation(compilers, basePath, namespacer, *handler.PodLogs)) } else if handler.Events != nil { get := v1alpha1.Get{ ActionClusters: handler.Events.ActionClusters, @@ -347,55 +171,51 @@ func finallyOperation(compilers compilers.Compilers, basePath string, id int, na ActionObjectSelector: handler.Events.ActionObjectSelector, }, } - ops = append(ops, getOperation(compilers, basePath, id+1, namespacer, get)) + ops = append(ops, getOperation(compilers, basePath, namespacer, get)) } else if handler.Describe != nil { - ops = append(ops, describeOperation(compilers, basePath, id+1, namespacer, *handler.Describe)) + ops = append(ops, describeOperation(compilers, basePath, namespacer, *handler.Describe)) } else if handler.Get != nil { - ops = append(ops, getOperation(compilers, basePath, id+1, namespacer, *handler.Get)) + ops = append(ops, getOperation(compilers, basePath, namespacer, *handler.Get)) } else if handler.Delete != nil { - loaded, err := deleteOperation(compilers, basePath, id+1, namespacer, bindings, *handler.Delete) + loaded, err := deleteOperation(compilers, basePath, namespacer, bindings, *handler.Delete) if err != nil { return nil, err } ops = append(ops, loaded...) } else if handler.Command != nil { - ops = append(ops, commandOperation(compilers, basePath, id+1, namespacer, *handler.Command)) + ops = append(ops, commandOperation(compilers, basePath, namespacer, *handler.Command)) } else if handler.Script != nil { - ops = append(ops, scriptOperation(compilers, basePath, id+1, namespacer, *handler.Script)) + ops = append(ops, scriptOperation(compilers, basePath, namespacer, *handler.Script)) } else if handler.Sleep != nil { - ops = append(ops, sleepOperation(compilers, id+1, *handler.Sleep)) + ops = append(ops, sleepOperation(compilers, *handler.Sleep)) } else if handler.Wait != nil { - ops = append(ops, waitOperation(compilers, basePath, id+1, namespacer, *handler.Wait)) + ops = append(ops, waitOperation(compilers, basePath, namespacer, *handler.Wait)) } else { return nil, errors.New("no operation found") } return ops, nil } -func applyOperation(compilers compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, cleaner cleaner.CleanerCollector, bindings apis.Bindings, op v1alpha1.Apply) ([]operation, error) { +func applyOperation(compilers compilers.Compilers, basePath string, namespacer namespacer.Namespacer, cleaner cleaner.CleanerCollector, bindings apis.Bindings, op v1alpha1.Apply) ([]Operation, error) { resources, err := fileRefOrResource(context.TODO(), op.ActionResourceRef, basePath, compilers, bindings) if err != nil { return nil, err } - var ops []operation + var ops []Operation for i := range resources { resource := resources[i] ops = append(ops, newOperation( - OperationInfo{ - Id: id, - ResourceId: i + 1, - }, model.OperationTypeApply, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - dryRun: op.DryRun, - templating: op.Template, - timeouts: &v1alpha1.Timeouts{Apply: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + DryRun: op.DryRun, + Templating: op.Template, + Timeouts: &v1alpha1.Timeouts{Apply: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData, op.Bindings...); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData, op.Bindings...); err != nil { return nil, nil, tc, err } else if err := prepareResource(resource, tc); err != nil { return nil, nil, tc, err @@ -420,29 +240,25 @@ func applyOperation(compilers compilers.Compilers, basePath string, id int, name return ops, nil } -func assertOperation(compilers compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, bindings apis.Bindings, op v1alpha1.Assert) ([]operation, error) { +func assertOperation(compilers compilers.Compilers, basePath string, namespacer namespacer.Namespacer, bindings apis.Bindings, op v1alpha1.Assert) ([]Operation, error) { resources, err := fileRefOrCheck(context.TODO(), op.ActionCheckRef, basePath, compilers, bindings) if err != nil { return nil, err } - var ops []operation + var ops []Operation for i := range resources { resource := resources[i] ops = append(ops, newOperation( - OperationInfo{ - Id: id, - ResourceId: i + 1, - }, model.OperationTypeAssert, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - templating: op.Template, - timeouts: &v1alpha1.Timeouts{Assert: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + Templating: op.Template, + Timeouts: &v1alpha1.Timeouts{Assert: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData, op.Bindings...); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData, op.Bindings...); err != nil { return nil, nil, tc, err } else if _, client, err := tc.CurrentClusterClient(); err != nil { return nil, nil, tc, err @@ -462,24 +278,21 @@ func assertOperation(compilers compilers.Compilers, basePath string, id int, nam return ops, nil } -func commandOperation(_ compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, op v1alpha1.Command) operation { +func commandOperation(_ compilers.Compilers, basePath string, namespacer namespacer.Namespacer, op v1alpha1.Command) Operation { ns := "" if namespacer != nil { ns = namespacer.GetNamespace() } return newOperation( - OperationInfo{ - Id: id, - }, model.OperationTypeCommand, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + Timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData, op.Bindings...); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData, op.Bindings...); err != nil { return nil, nil, tc, err } else if config, _, err := tc.CurrentClusterClient(); err != nil { return nil, nil, tc, err @@ -497,30 +310,26 @@ func commandOperation(_ compilers.Compilers, basePath string, id int, namespacer ) } -func createOperation(compilers compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, cleaner cleaner.CleanerCollector, bindings apis.Bindings, op v1alpha1.Create) ([]operation, error) { +func createOperation(compilers compilers.Compilers, basePath string, namespacer namespacer.Namespacer, cleaner cleaner.CleanerCollector, bindings apis.Bindings, op v1alpha1.Create) ([]Operation, error) { resources, err := fileRefOrResource(context.TODO(), op.ActionResourceRef, basePath, compilers, bindings) if err != nil { return nil, err } - var ops []operation + var ops []Operation for i := range resources { resource := resources[i] ops = append(ops, newOperation( - OperationInfo{ - Id: id, - ResourceId: i + 1, - }, model.OperationTypeCreate, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - dryRun: op.DryRun, - templating: op.Template, - timeouts: &v1alpha1.Timeouts{Apply: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + DryRun: op.DryRun, + Templating: op.Template, + Timeouts: &v1alpha1.Timeouts{Apply: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData, op.Bindings...); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData, op.Bindings...); err != nil { return nil, nil, tc, err } else if err := prepareResource(resource, tc); err != nil { return nil, nil, tc, err @@ -545,7 +354,7 @@ func createOperation(compilers compilers.Compilers, basePath string, id int, nam return ops, nil } -func deleteOperation(compilers compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, bindings apis.Bindings, op v1alpha1.Delete) ([]operation, error) { +func deleteOperation(compilers compilers.Compilers, basePath string, namespacer namespacer.Namespacer, bindings apis.Bindings, op v1alpha1.Delete) ([]Operation, error) { ref := v1alpha1.ActionResourceRef{ FileRef: v1alpha1.FileRef{ File: op.File, @@ -564,25 +373,21 @@ func deleteOperation(compilers compilers.Compilers, basePath string, id int, nam if err != nil { return nil, err } - var ops []operation + var ops []Operation for i := range resources { resource := resources[i] ops = append(ops, newOperation( - OperationInfo{ - Id: id, - ResourceId: i + 1, - }, model.OperationTypeDelete, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - deletionPropagation: op.DeletionPropagationPolicy, - templating: op.Template, - timeouts: &v1alpha1.Timeouts{Delete: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + DeletionPropagation: op.DeletionPropagationPolicy, + Templating: op.Template, + Timeouts: &v1alpha1.Timeouts{Delete: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData, op.Bindings...); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData, op.Bindings...); err != nil { return nil, nil, tc, err } else if _, client, err := tc.CurrentClusterClient(); err != nil { return nil, nil, tc, err @@ -604,24 +409,21 @@ func deleteOperation(compilers compilers.Compilers, basePath string, id int, nam return ops, nil } -func describeOperation(_ compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, op v1alpha1.Describe) operation { +func describeOperation(_ compilers.Compilers, basePath string, namespacer namespacer.Namespacer, op v1alpha1.Describe) Operation { ns := "" if namespacer != nil { ns = namespacer.GetNamespace() } return newOperation( - OperationInfo{ - Id: id, - }, model.OperationTypeCommand, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + Timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData); err != nil { return nil, nil, tc, err } else if config, client, err := tc.CurrentClusterClient(); err != nil { return nil, nil, tc, err @@ -648,29 +450,25 @@ func describeOperation(_ compilers.Compilers, basePath string, id int, namespace ) } -func errorOperation(compilers compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, bindings apis.Bindings, op v1alpha1.Error) ([]operation, error) { +func errorOperation(compilers compilers.Compilers, basePath string, namespacer namespacer.Namespacer, bindings apis.Bindings, op v1alpha1.Error) ([]Operation, error) { resources, err := fileRefOrCheck(context.TODO(), op.ActionCheckRef, basePath, compilers, bindings) if err != nil { return nil, err } - var ops []operation + var ops []Operation for i := range resources { resource := resources[i] ops = append(ops, newOperation( - OperationInfo{ - Id: id, - ResourceId: i + 1, - }, model.OperationTypeError, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - templating: op.Template, - timeouts: &v1alpha1.Timeouts{Error: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + Templating: op.Template, + Timeouts: &v1alpha1.Timeouts{Error: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData, op.Bindings...); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData, op.Bindings...); err != nil { return nil, nil, tc, err } else if _, client, err := tc.CurrentClusterClient(); err != nil { return nil, nil, tc, err @@ -690,24 +488,21 @@ func errorOperation(compilers compilers.Compilers, basePath string, id int, name return ops, nil } -func getOperation(_ compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, op v1alpha1.Get) operation { +func getOperation(_ compilers.Compilers, basePath string, namespacer namespacer.Namespacer, op v1alpha1.Get) Operation { ns := "" if namespacer != nil { ns = namespacer.GetNamespace() } return newOperation( - OperationInfo{ - Id: id, - }, model.OperationTypeCommand, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + Timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData); err != nil { return nil, nil, tc, err } else if config, client, err := tc.CurrentClusterClient(); err != nil { return nil, nil, tc, err @@ -734,24 +529,21 @@ func getOperation(_ compilers.Compilers, basePath string, id int, namespacer nam ) } -func logsOperation(_ compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, op v1alpha1.PodLogs) operation { +func logsOperation(_ compilers.Compilers, basePath string, namespacer namespacer.Namespacer, op v1alpha1.PodLogs) Operation { ns := "" if namespacer != nil { ns = namespacer.GetNamespace() } return newOperation( - OperationInfo{ - Id: id, - }, model.OperationTypeCommand, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + Timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData); err != nil { return nil, nil, tc, err } else if config, _, err := tc.CurrentClusterClient(); err != nil { return nil, nil, tc, err @@ -778,30 +570,26 @@ func logsOperation(_ compilers.Compilers, basePath string, id int, namespacer na ) } -func patchOperation(compilers compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, bindings apis.Bindings, op v1alpha1.Patch) ([]operation, error) { +func patchOperation(compilers compilers.Compilers, basePath string, namespacer namespacer.Namespacer, bindings apis.Bindings, op v1alpha1.Patch) ([]Operation, error) { resources, err := fileRefOrResource(context.TODO(), op.ActionResourceRef, basePath, compilers, bindings) if err != nil { return nil, err } - var ops []operation + var ops []Operation for i := range resources { resource := resources[i] ops = append(ops, newOperation( - OperationInfo{ - Id: id, - ResourceId: i + 1, - }, model.OperationTypePatch, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - dryRun: op.DryRun, - templating: op.Template, - timeouts: &v1alpha1.Timeouts{Apply: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + DryRun: op.DryRun, + Templating: op.Template, + Timeouts: &v1alpha1.Timeouts{Apply: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData, op.Bindings...); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData, op.Bindings...); err != nil { return nil, nil, tc, err } else if err := prepareResource(resource, tc); err != nil { return nil, nil, tc, err @@ -825,24 +613,21 @@ func patchOperation(compilers compilers.Compilers, basePath string, id int, name return ops, nil } -func proxyOperation(_ compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, op v1alpha1.Proxy) operation { +func proxyOperation(_ compilers.Compilers, basePath string, namespacer namespacer.Namespacer, op v1alpha1.Proxy) Operation { ns := "" if namespacer != nil { ns = namespacer.GetNamespace() } return newOperation( - OperationInfo{ - Id: id, - }, model.OperationTypeCommand, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + Timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData); err != nil { return nil, nil, tc, err } else if config, client, err := tc.CurrentClusterClient(); err != nil { return nil, nil, tc, err @@ -869,24 +654,21 @@ func proxyOperation(_ compilers.Compilers, basePath string, id int, namespacer n ) } -func scriptOperation(_ compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, op v1alpha1.Script) operation { +func scriptOperation(_ compilers.Compilers, basePath string, namespacer namespacer.Namespacer, op v1alpha1.Script) Operation { ns := "" if namespacer != nil { ns = namespacer.GetNamespace() } return newOperation( - OperationInfo{ - Id: id, - }, model.OperationTypeScript, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + Timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData, op.Bindings...); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData, op.Bindings...); err != nil { return nil, nil, tc, err } else if config, _, err := tc.CurrentClusterClient(); err != nil { return nil, nil, tc, err @@ -904,11 +686,8 @@ func scriptOperation(_ compilers.Compilers, basePath string, id int, namespacer ) } -func sleepOperation(_ compilers.Compilers, id int, op v1alpha1.Sleep) operation { +func sleepOperation(_ compilers.Compilers, op v1alpha1.Sleep) Operation { return newOperation( - OperationInfo{ - Id: id, - }, model.OperationTypeSleep, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { return opsleep.New(op), nil, tc, nil @@ -916,30 +695,26 @@ func sleepOperation(_ compilers.Compilers, id int, op v1alpha1.Sleep) operation ) } -func updateOperation(compilers compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, bindings apis.Bindings, op v1alpha1.Update) ([]operation, error) { +func updateOperation(compilers compilers.Compilers, basePath string, namespacer namespacer.Namespacer, bindings apis.Bindings, op v1alpha1.Update) ([]Operation, error) { resources, err := fileRefOrResource(context.TODO(), op.ActionResourceRef, basePath, compilers, bindings) if err != nil { return nil, err } - var ops []operation + var ops []Operation for i := range resources { resource := resources[i] ops = append(ops, newOperation( - OperationInfo{ - Id: id, - ResourceId: i + 1, - }, model.OperationTypeUpdate, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - dryRun: op.DryRun, - templating: op.Template, - timeouts: &v1alpha1.Timeouts{Apply: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + DryRun: op.DryRun, + Templating: op.Template, + Timeouts: &v1alpha1.Timeouts{Apply: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData, op.Bindings...); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData, op.Bindings...); err != nil { return nil, nil, tc, err } else if err := prepareResource(resource, tc); err != nil { return nil, nil, tc, err @@ -963,24 +738,21 @@ func updateOperation(compilers compilers.Compilers, basePath string, id int, nam return ops, nil } -func waitOperation(_ compilers.Compilers, basePath string, id int, namespacer namespacer.Namespacer, op v1alpha1.Wait) operation { +func waitOperation(_ compilers.Compilers, basePath string, namespacer namespacer.Namespacer, op v1alpha1.Wait) Operation { ns := "" if namespacer != nil { ns = namespacer.GetNamespace() } return newOperation( - OperationInfo{ - Id: id, - }, model.OperationTypeCommand, func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - contextData := contextData{ - basePath: basePath, - cluster: op.Cluster, - clusters: op.Clusters, - timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, + contextData := enginecontext.ContextData{ + BasePath: basePath, + Cluster: op.Cluster, + Clusters: op.Clusters, + Timeouts: &v1alpha1.Timeouts{Exec: op.Timeout}, } - if tc, err := setupContextAndBindings(tc, contextData); err != nil { + if tc, err := enginecontext.SetupContextAndBindings(tc, contextData); err != nil { return nil, nil, tc, err } else if config, client, err := tc.CurrentClusterClient(); err != nil { return nil, nil, tc, err @@ -1012,13 +784,9 @@ func waitOperation(_ compilers.Compilers, basePath string, id int, namespacer na ) } -func fileRefOrCheck(ctx context.Context, ref v1alpha1.ActionCheckRef, basePath string, compilers compilers.Compilers, bindings apis.Bindings) ([]unstructured.Unstructured, error) { - if ref.Check != nil && ref.Check.Value() != nil { - if object, ok := ref.Check.Value().(map[string]any); !ok { - return nil, errors.New("resource must be an object") - } else { - return []unstructured.Unstructured{{Object: object}}, nil - } +func fileRefOrResource(ctx context.Context, ref v1alpha1.ActionResourceRef, basePath string, compilers compilers.Compilers, bindings apis.Bindings) ([]unstructured.Unstructured, error) { + if ref.Resource != nil { + return []unstructured.Unstructured{*ref.Resource}, nil } if ref.File != "" { ref, err := ref.File.Value(ctx, compilers, bindings) @@ -1027,17 +795,21 @@ func fileRefOrCheck(ctx context.Context, ref v1alpha1.ActionCheckRef, basePath s } url, err := url.ParseRequestURI(ref) if err != nil { - return resource.Load(filepath.Join(basePath, ref), false) + return resource.Load(filepath.Join(basePath, ref), true) } else { - return resource.LoadFromURI(url, false) + return resource.LoadFromURI(url, true) } } return nil, errors.New("file or resource must be set") } -func fileRefOrResource(ctx context.Context, ref v1alpha1.ActionResourceRef, basePath string, compilers compilers.Compilers, bindings apis.Bindings) ([]unstructured.Unstructured, error) { - if ref.Resource != nil { - return []unstructured.Unstructured{*ref.Resource}, nil +func fileRefOrCheck(ctx context.Context, ref v1alpha1.ActionCheckRef, basePath string, compilers compilers.Compilers, bindings apis.Bindings) ([]unstructured.Unstructured, error) { + if ref.Check != nil && ref.Check.Value() != nil { + if object, ok := ref.Check.Value().(map[string]any); !ok { + return nil, errors.New("resource must be an object") + } else { + return []unstructured.Unstructured{{Object: object}}, nil + } } if ref.File != "" { ref, err := ref.File.Value(ctx, compilers, bindings) @@ -1046,9 +818,9 @@ func fileRefOrResource(ctx context.Context, ref v1alpha1.ActionResourceRef, base } url, err := url.ParseRequestURI(ref) if err != nil { - return resource.Load(filepath.Join(basePath, ref), true) + return resource.Load(filepath.Join(basePath, ref), false) } else { - return resource.LoadFromURI(url, true) + return resource.LoadFromURI(url, false) } } return nil, errors.New("file or resource must be set") diff --git a/pkg/runner/operation.go b/pkg/runner/operations/operation.go similarity index 80% rename from pkg/runner/operation.go rename to pkg/runner/operations/operation.go index 0197c517f..f28c647c8 100644 --- a/pkg/runner/operation.go +++ b/pkg/runner/operations/operation.go @@ -1,4 +1,4 @@ -package runner +package operations import ( "context" @@ -14,25 +14,22 @@ import ( type operationFactory = func(context.Context, enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) -type operation struct { - info OperationInfo +type Operation struct { opType model.OperationType operation operationFactory } func newOperation( - info OperationInfo, opType model.OperationType, op operationFactory, -) operation { - return operation{ - info: info, +) Operation { + return Operation{ opType: opType, operation: op, } } -func (o operation) execute(ctx context.Context, tc enginecontext.TestContext, stepReport *model.StepReport) (_ outputs.Outputs, err error) { +func (o Operation) Execute(ctx context.Context, tc enginecontext.TestContext, stepReport *model.StepReport) (_ outputs.Outputs, err error) { report := &model.OperationReport{ Type: o.opType, StartTime: time.Now(), @@ -42,7 +39,7 @@ func (o operation) execute(ctx context.Context, tc enginecontext.TestContext, st report.Err = err stepReport.Add(report) }() - if operation, timeout, tc, err := o.operation(ctx, tc.WithBinding("operation", o.info)); err != nil { + if operation, timeout, tc, err := o.operation(ctx, tc); err != nil { logging.Log(ctx, logging.Internal, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) return nil, err } else { diff --git a/pkg/runner/operation_test.go b/pkg/runner/operations/operation_test.go similarity index 67% rename from pkg/runner/operation_test.go rename to pkg/runner/operations/operation_test.go index bbfb4b2ed..980ccb296 100644 --- a/pkg/runner/operation_test.go +++ b/pkg/runner/operations/operation_test.go @@ -1,8 +1,9 @@ -package runner +package operations import ( "context" "errors" + "testing" "time" "github.com/kyverno/chainsaw/pkg/apis" @@ -11,7 +12,6 @@ import ( "github.com/kyverno/chainsaw/pkg/engine/outputs" "github.com/kyverno/chainsaw/pkg/model" enginecontext "github.com/kyverno/chainsaw/pkg/runner/context" - "github.com/kyverno/chainsaw/pkg/testing" "github.com/stretchr/testify/assert" "k8s.io/utils/clock" ) @@ -47,25 +47,45 @@ func TestOperation_Execute(t *testing.T) { }, }, timeout: 1 * time.Second, + }, { + name: "operation succeeds", + operation: mock.MockOperation{ + ExecFn: func(_ context.Context, _ apis.Bindings) (outputs.Outputs, error) { + return nil, nil + }, + }, + timeout: 1 * time.Second, }} for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - localTC := tc op := newOperation( - OperationInfo{}, model.OperationTypeApply, - func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { - return localTC.operation, &localTC.timeout, tc, nil + func(ctx context.Context, _tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { + return tc.operation, &tc.timeout, _tc, nil }, ) tcontext := enginecontext.EmptyContext(clock.RealClock{}) ctx := context.Background() - _, err := op.execute(ctx, tcontext, &model.StepReport{}) - if localTC.expectedFail { + _, err := op.Execute(ctx, tcontext, &model.StepReport{}) + if tc.expectedFail { assert.Error(t, err) } else { assert.NoError(t, err) } }) } + { + t.Run("factory failure", func(t *testing.T) { + op := newOperation( + model.OperationTypeApply, + func(ctx context.Context, tc enginecontext.TestContext) (operations.Operation, *time.Duration, enginecontext.TestContext, error) { + return nil, nil, tc, errors.New("dummy") + }, + ) + tcontext := enginecontext.EmptyContext(clock.RealClock{}) + ctx := context.Background() + _, err := op.Execute(ctx, tcontext, &model.StepReport{}) + assert.Error(t, err) + }) + } } diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index d6d330e4c..6d1d40009 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -3,6 +3,7 @@ package runner import ( "context" "fmt" + "testing" "time" petname "github.com/dustinkirkland/golang-petname" @@ -17,7 +18,7 @@ import ( enginecontext "github.com/kyverno/chainsaw/pkg/runner/context" "github.com/kyverno/chainsaw/pkg/runner/internal" "github.com/kyverno/chainsaw/pkg/runner/names" - "github.com/kyverno/chainsaw/pkg/testing" + "github.com/kyverno/chainsaw/pkg/runner/operations" "github.com/kyverno/pkg/ext/output/color" "go.uber.org/multierr" corev1 "k8s.io/api/core/v1" @@ -105,7 +106,7 @@ func (r *runner) run(ctx context.Context, m mainstart, nsOptions v1alpha2.Namesp } ctx := logging.WithLogger(ctx, logging.NewLogger(test.Test.Name, fmt.Sprintf("%-*s", size, "@chainsaw"))) // helper to run test - runTest := func(ctx context.Context, t *testing.T, testId int, scenarioId int, bindings ...v1alpha1.Binding) { + runTest := func(ctx context.Context, t *testing.T, testId int, scenarioId int, tc enginecontext.TestContext, bindings ...v1alpha1.Binding) { t.Helper() // setup logger sink ctx = logging.WithSink(ctx, newSink(r.clock, t.Log)) @@ -174,7 +175,7 @@ func (r *runner) run(ctx context.Context, m mainstart, nsOptions v1alpha2.Namesp return } // setup bindings - tc, err = setupBindings(tc, test.Test.Spec.Bindings...) + tc, err = enginecontext.SetupBindings(tc, test.Test.Spec.Bindings...) if err != nil { t.Fail() logging.Log(ctx, logging.Internal, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) @@ -188,7 +189,7 @@ func (r *runner) run(ctx context.Context, m mainstart, nsOptions v1alpha2.Namesp Id: i + 1, } tc := tc.WithBinding("step", info) - if stop := r.runStep(ctx, t, test.BasePath, nspacer, tc, step, report); stop { + if stop := r.runStep(ctx, t.Cleanup, t.Fail, t.Failed, test.BasePath, nspacer, tc, step, report); stop { return } } @@ -198,14 +199,14 @@ func (r *runner) run(ctx context.Context, m mainstart, nsOptions v1alpha2.Namesp if len(test.Test.Spec.Scenarios) == 0 { t.Run(name, func(t *testing.T) { t.Helper() - runTest(ctx, t, testId, 0) + runTest(ctx, t, testId, 0, tc) }) } else { for s := range test.Test.Spec.Scenarios { scenarioId := s + 1 t.Run(name, func(t *testing.T) { t.Helper() - runTest(ctx, t, testId, scenarioId, test.Test.Spec.Scenarios[s].Bindings...) + runTest(ctx, t, testId, scenarioId, tc, test.Test.Spec.Scenarios[s].Bindings...) }) } } @@ -232,6 +233,215 @@ func (r *runner) run(ctx context.Context, m mainstart, nsOptions v1alpha2.Namesp return nil } +func (r *runner) runStep( + ctx context.Context, + cleanup func(func()), + fail func(), + failed func() bool, + basePath string, + namespacer namespacer.Namespacer, + tc enginecontext.TestContext, + step v1alpha1.TestStep, + testReport *model.TestReport, +) bool { + report := &model.StepReport{ + Name: step.Name, + StartTime: time.Now(), + } + defer func() { + report.EndTime = time.Now() + testReport.Add(report) + }() + if step.Compiler != nil { + tc = tc.WithDefaultCompiler(string(*step.Compiler)) + } + contextData := enginecontext.ContextData{ + BasePath: basePath, + Catch: step.Catch, + Cluster: step.Cluster, + Clusters: step.Clusters, + DeletionPropagation: step.DeletionPropagationPolicy, + SkipDelete: step.SkipDelete, + Templating: step.Template, + Timeouts: step.Timeouts, + } + tc, err := enginecontext.SetupContextAndBindings(tc, contextData, step.Bindings...) + if err != nil { + fail() + logging.Log(ctx, logging.Internal, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) + r.onFail() + return true + } + cleaner := cleaner.New(tc.Timeouts().Cleanup.Duration, tc.DelayBeforeCleanup(), tc.DeletionPropagation()) + cleanup(func() { + if !cleaner.Empty() || len(step.Cleanup) != 0 { + report := &model.StepReport{ + Name: fmt.Sprintf("cleanup (%s)", report.Name), + StartTime: time.Now(), + } + defer func() { + report.EndTime = time.Now() + testReport.Add(report) + }() + logging.Log(ctx, logging.Cleanup, logging.BeginStatus, nil, color.BoldFgCyan) + defer func() { + logging.Log(ctx, logging.Cleanup, logging.EndStatus, nil, color.BoldFgCyan) + }() + if !cleaner.Empty() { + if errs := cleaner.Run(ctx, report); len(errs) != 0 { + fail() + for _, err := range errs { + logging.Log(ctx, logging.Cleanup, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) + } + r.onFail() + } + } + for _, operation := range step.Cleanup { + operationTc := tc + if operation.Compiler != nil { + operationTc = operationTc.WithDefaultCompiler(string(*operation.Compiler)) + } + operations, err := operations.FinallyOperation(operationTc.Compilers(), basePath, namespacer, operationTc.Bindings(), operation) + if err != nil { + fail() + logging.Log(ctx, logging.Cleanup, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) + r.onFail() + } + for _, operation := range operations { + _, err := operation.Execute(ctx, operationTc, report) + if err != nil { + fail() + r.onFail() + } + } + } + } + }) + if len(step.Finally) != 0 { + defer func() { + logging.Log(ctx, logging.Finally, logging.BeginStatus, nil, color.BoldFgCyan) + defer func() { + logging.Log(ctx, logging.Finally, logging.EndStatus, nil, color.BoldFgCyan) + }() + for _, operation := range step.Finally { + operationTc := tc + if operation.Compiler != nil { + operationTc = operationTc.WithDefaultCompiler(string(*operation.Compiler)) + } + operations, err := operations.FinallyOperation(operationTc.Compilers(), basePath, namespacer, operationTc.Bindings(), operation) + if err != nil { + fail() + logging.Log(ctx, logging.Finally, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) + r.onFail() + } + for _, operation := range operations { + _, err := operation.Execute(ctx, operationTc, report) + if err != nil { + fail() + r.onFail() + } + } + } + }() + } + if catch := tc.Catch(); len(catch) != 0 { + defer func() { + if failed() { + logging.Log(ctx, logging.Catch, logging.BeginStatus, nil, color.BoldFgCyan) + defer func() { + logging.Log(ctx, logging.Catch, logging.EndStatus, nil, color.BoldFgCyan) + }() + for _, operation := range catch { + operationTc := tc + if operation.Compiler != nil { + operationTc = operationTc.WithDefaultCompiler(string(*operation.Compiler)) + } + operations, err := operations.CatchOperation(operationTc.Compilers(), basePath, namespacer, operationTc.Bindings(), operation) + if err != nil { + fail() + logging.Log(ctx, logging.Catch, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) + r.onFail() + } + for _, operation := range operations { + _, err := operation.Execute(ctx, operationTc, report) + if err != nil { + fail() + r.onFail() + } + } + } + } + }() + } + { + logging.Log(ctx, logging.Try, logging.BeginStatus, nil, color.BoldFgCyan) + defer func() { + logging.Log(ctx, logging.Try, logging.EndStatus, nil, color.BoldFgCyan) + }() + tc := tc + for i, operation := range step.Try { + continueOnError, outputsTc, err := r.runOperation(ctx, tc, operation, basePath, i, namespacer, cleaner, report) + if err != nil { + fail() + if !continueOnError { + return true + } + } + tc = outputsTc + } + } + return false +} + +func (r *runner) runOperation( + ctx context.Context, + tc enginecontext.TestContext, + operation v1alpha1.Operation, + basePath string, + operationId int, + namespacer namespacer.Namespacer, + cleaner cleaner.Cleaner, + report *model.StepReport, +) (bool, enginecontext.TestContext, error) { + if operation.Compiler != nil { + tc = tc.WithDefaultCompiler(string(*operation.Compiler)) + } + actions, err := operations.TryOperation(tc, basePath, namespacer, operation, cleaner) + if err != nil { + logging.Log(ctx, logging.Try, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) + r.onFail() + return false, tc, err + } + continueOnError := operation.ContinueOnError != nil && *operation.ContinueOnError + var errs []error + for i, action := range actions { + outputs, err := r.runAction(ctx, action, operationId, i, tc, report) + if err != nil { + errs = append(errs, err) + r.onFail() + } + for k, v := range outputs { + tc = tc.WithBinding(k, v) + } + } + return continueOnError, tc, multierr.Combine(errs...) +} + +func (*runner) runAction( + ctx context.Context, + action operations.Operation, + operationId int, + actionId int, + tc enginecontext.TestContext, + stepReport *model.StepReport, +) (outputs map[string]any, err error) { + tc = tc.WithBinding("operation", OperationInfo{ + Id: operationId + 1, + ResourceId: actionId + 1, + }) + return action.Execute(ctx, tc, stepReport) +} + func (r *runner) onFail() { if r.onFailure != nil { r.onFailure() @@ -309,19 +519,19 @@ func (r *runner) setupTestContext(ctx context.Context, testId int, scenarioId in r.onFail() return tc, err } - contextData := contextData{ - basePath: test.BasePath, - catch: test.Test.Spec.Catch, - cluster: test.Test.Spec.Cluster, - clusters: test.Test.Spec.Clusters, - delayBeforeCleanup: test.Test.Spec.DelayBeforeCleanup, - deletionPropagation: test.Test.Spec.DeletionPropagationPolicy, - skipDelete: test.Test.Spec.SkipDelete, - templating: test.Test.Spec.Template, - terminationGrace: test.Test.Spec.ForceTerminationGracePeriod, - timeouts: test.Test.Spec.Timeouts, + contextData := enginecontext.ContextData{ + BasePath: test.BasePath, + Catch: test.Test.Spec.Catch, + Cluster: test.Test.Spec.Cluster, + Clusters: test.Test.Spec.Clusters, + DelayBeforeCleanup: test.Test.Spec.DelayBeforeCleanup, + DeletionPropagation: test.Test.Spec.DeletionPropagationPolicy, + SkipDelete: test.Test.Spec.SkipDelete, + Templating: test.Test.Spec.Template, + TerminationGrace: test.Test.Spec.ForceTerminationGracePeriod, + Timeouts: test.Test.Spec.Timeouts, } - tc, err = setupContext(tc, contextData) + tc, err = enginecontext.SetupContext(tc, contextData) if err != nil { logging.Log(ctx, logging.Internal, logging.ErrorStatus, nil, color.BoldRed, logging.ErrSection(err)) r.onFail() diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 0c8639c6c..992c18a3d 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "flag" + "testing" "github.com/kyverno/chainsaw/pkg/apis" "github.com/kyverno/chainsaw/pkg/apis/v1alpha1" @@ -17,7 +18,6 @@ import ( "github.com/kyverno/chainsaw/pkg/runner/flags" "github.com/kyverno/chainsaw/pkg/runner/internal" "github.com/kyverno/chainsaw/pkg/runner/mocks" - "github.com/kyverno/chainsaw/pkg/testing" "github.com/stretchr/testify/assert" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/runner/step_test.go b/pkg/runner/step_test.go index 802043282..a9101b351 100644 --- a/pkg/runner/step_test.go +++ b/pkg/runner/step_test.go @@ -3,6 +3,7 @@ package runner import ( "context" "path/filepath" + "testing" "time" "github.com/kyverno/chainsaw/pkg/apis" @@ -16,7 +17,6 @@ import ( "github.com/kyverno/chainsaw/pkg/model" enginecontext "github.com/kyverno/chainsaw/pkg/runner/context" "github.com/kyverno/chainsaw/pkg/runner/mocks" - "github.com/kyverno/chainsaw/pkg/testing" "github.com/stretchr/testify/assert" kerror "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -784,7 +784,10 @@ func TestStepProcessor_Run(t *testing.T) { if tc.client != nil { registry.Client = tc.client } - nt := &testing.MockT{} + _failed := false + fail := func() { _failed = true } + failed := func() bool { return _failed } + cleanup := func(func()) {} ctx := context.Background() ctx = logging.WithLogger(ctx, &fakeLogger.Logger{}) tcontext := enginecontext.MakeContext(clock.RealClock{}, apis.NewBindings(), registry).WithTimeouts(v1alpha1.Timeouts{ @@ -796,13 +799,9 @@ func TestStepProcessor_Run(t *testing.T) { Exec: &config.Spec.Timeouts.Exec, }) runner := runner{} - got := runner.runStep(ctx, nt, tc.basePath, tc.namespacer, tcontext, tc.stepSpec, &model.TestReport{}) + got := runner.runStep(ctx, cleanup, fail, failed, tc.basePath, tc.namespacer, tcontext, tc.stepSpec, &model.TestReport{}) assert.Equal(t, tc.want, got) - if tc.expectedFail { - assert.True(t, nt.FailedVar, "expected an error but got none") - } else { - assert.False(t, nt.FailedVar, "expected no error but got one") - } + assert.Equal(t, tc.expectedFail, _failed) }) } } diff --git a/pkg/testing/context.go b/pkg/testing/context.go deleted file mode 100644 index 490861041..000000000 --- a/pkg/testing/context.go +++ /dev/null @@ -1,35 +0,0 @@ -package testing - -import ( - "testing" - "time" -) - -var MainStart = testing.MainStart - -type ( - InternalTest = testing.InternalTest - T = testing.T -) - -type TTest interface { - Cleanup(func()) - Deadline() (deadline time.Time, ok bool) - Error(args ...any) - Errorf(format string, args ...any) - Fail() - Failed() bool - Fatal(args ...any) - Fatalf(format string, args ...any) - Helper() - Log(args ...any) - Logf(format string, args ...any) - Name() string - Parallel() - Run(name string, f func(t *T)) bool - Setenv(key, value string) - Skip(args ...any) - Skipf(format string, args ...any) - Skipped() bool - TempDir() string -} diff --git a/pkg/testing/err_reader.go b/pkg/testing/err_reader.go deleted file mode 100644 index fcbe6f1d1..000000000 --- a/pkg/testing/err_reader.go +++ /dev/null @@ -1,11 +0,0 @@ -package testing - -import ( - "errors" -) - -type ErrReader struct{} - -func (e *ErrReader) Read(p []byte) (n int, err error) { - return 0, errors.New("error reading from stdin") -} diff --git a/pkg/testing/mock.go b/pkg/testing/mock.go deleted file mode 100644 index 66e0174c1..000000000 --- a/pkg/testing/mock.go +++ /dev/null @@ -1,86 +0,0 @@ -package testing - -import ( - "time" -) - -type MockT struct { - NameVar string - FailedVar bool - ImmeditateFailVar bool - SkippedVar bool -} - -func (c *MockT) Cleanup(f func()) { -} - -func (t *MockT) Deadline() (deadline time.Time, ok bool) { - return time.Time{}, false -} - -func (c *MockT) Error(args ...any) { -} - -func (c *MockT) Errorf(format string, args ...any) { -} - -func (c *MockT) Fail() { - c.FailedVar = true -} - -func (c *MockT) FailNow() { - c.ImmeditateFailVar = true - c.FailedVar = true -} - -func (c *MockT) Failed() bool { - return c.FailedVar -} - -func (c *MockT) Fatal(args ...any) { -} - -func (c *MockT) Fatalf(format string, args ...any) { -} - -func (c *MockT) Helper() { -} - -func (c *MockT) Log(args ...any) { -} - -func (c *MockT) Logf(format string, args ...any) { -} - -func (c *MockT) Name() string { - return c.NameVar -} - -func (t *MockT) Parallel() { -} - -func (nt *MockT) Run(name string, f func(t *T)) bool { - nt.NameVar = name - return true -} - -func (t *MockT) Setenv(key, value string) { -} - -func (c *MockT) Skip(args ...any) { -} - -func (c *MockT) SkipNow() { - c.SkippedVar = true -} - -func (c *MockT) Skipf(format string, args ...any) { -} - -func (c *MockT) Skipped() bool { - return c.SkippedVar -} - -func (c *MockT) TempDir() string { - return "" -}