diff --git a/pkg/testworkflows/testworkflowprocessor/action/actiontypes/lite/utils.go b/pkg/testworkflows/testworkflowprocessor/action/actiontypes/lite/utils.go index ede5389b61e..4d67756a5b5 100644 --- a/pkg/testworkflows/testworkflowprocessor/action/actiontypes/lite/utils.go +++ b/pkg/testworkflows/testworkflowprocessor/action/actiontypes/lite/utils.go @@ -34,8 +34,8 @@ func (a LiteActionList) Result(ref, expression string) LiteActionList { return append(a, LiteAction{Result: &ActionResult{Ref: ref, Value: expression}}) } -func (a LiteActionList) Execute(ref string, negative bool) LiteActionList { - return append(a, LiteAction{Execute: &ActionExecute{Ref: ref, Negative: negative}}) +func (a LiteActionList) Execute(ref string, negative, pure bool) LiteActionList { + return append(a, LiteAction{Execute: &ActionExecute{Ref: ref, Negative: negative, Pure: pure}}) } func (a LiteActionList) MutateContainer(config LiteContainerConfig) LiteActionList { diff --git a/pkg/testworkflows/testworkflowprocessor/action/actiontypes/utils.go b/pkg/testworkflows/testworkflowprocessor/action/actiontypes/utils.go index 84f7c163650..bf003acf60b 100644 --- a/pkg/testworkflows/testworkflowprocessor/action/actiontypes/utils.go +++ b/pkg/testworkflows/testworkflowprocessor/action/actiontypes/utils.go @@ -68,14 +68,23 @@ func (a ActionList) Result(ref, expression string) ActionList { return append(a, Action{Result: &lite.ActionResult{Ref: ref, Value: expression}}) } -func (a ActionList) Execute(ref string, negative bool) ActionList { - return append(a, Action{Execute: &lite.ActionExecute{Ref: ref, Negative: negative}}) +func (a ActionList) Execute(ref string, negative, pure bool) ActionList { + return append(a, Action{Execute: &lite.ActionExecute{Ref: ref, Negative: negative, Pure: pure}}) } func (a ActionList) MutateContainer(ref string, config testworkflowsv1.ContainerConfig) ActionList { return append(a, Action{Container: &ActionContainer{Ref: ref, Config: config}}) } +func (a ActionList) Image() string { + for i := range a { + if a[i].Container != nil { + return a[i].Container.Config.Image + } + } + return "" +} + type ActionGroups []ActionList func (a ActionGroups) Append(fn func(list ActionList) ActionList) ActionGroups { diff --git a/pkg/testworkflows/testworkflowprocessor/action/group.go b/pkg/testworkflows/testworkflowprocessor/action/group.go index 1702d39a951..44919462046 100644 --- a/pkg/testworkflows/testworkflowprocessor/action/group.go +++ b/pkg/testworkflows/testworkflowprocessor/action/group.go @@ -169,5 +169,15 @@ merging: i++ } + // Fix the auto-merged internal images + for i := range groups { + image := groups[i].Image() + // Re-use /.tktw/bin/sh when the internal image has been merged into different container + if image != constants.DefaultToolkitImage && image != constants.DefaultInitImage { + groups[i] = groups[i].RewireCommandDirectory(constants.DefaultInitImage, "/.tktw-bin", constants.InternalBinPath) + groups[i] = groups[i].RewireCommandDirectory(constants.DefaultToolkitImage, "/.tktw-bin", constants.InternalBinPath) + } + } + return groups } diff --git a/pkg/testworkflows/testworkflowprocessor/action/group_test.go b/pkg/testworkflows/testworkflowprocessor/action/group_test.go index 873c42cafcb..8717ebcbee8 100644 --- a/pkg/testworkflows/testworkflowprocessor/action/group_test.go +++ b/pkg/testworkflows/testworkflowprocessor/action/group_test.go @@ -33,7 +33,7 @@ func TestGroup_Basic(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). End("step2"). CurrentStatus("init"). @@ -43,7 +43,7 @@ func TestGroup_Basic(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step3"). - Execute("step3", false). + Execute("step3", false, false). End("step3"). End("init"). End("") diff --git a/pkg/testworkflows/testworkflowprocessor/action/process_test.go b/pkg/testworkflows/testworkflowprocessor/action/process_test.go index 1615b5a7c0a..2d2117ec140 100644 --- a/pkg/testworkflows/testworkflowprocessor/action/process_test.go +++ b/pkg/testworkflows/testworkflowprocessor/action/process_test.go @@ -8,6 +8,7 @@ import ( testworkflowsv1 "github.com/kubeshop/testkube-operator/api/testworkflows/v1" "github.com/kubeshop/testkube/internal/common" "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/action/actiontypes" + "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/constants" "github.com/kubeshop/testkube/pkg/testworkflows/testworkflowprocessor/stage" ) @@ -40,7 +41,7 @@ func TestProcess_BasicSteps(t *testing.T) { Command: common.Ptr([]string{"a", "b"}), }). Start("step1"). - Execute("step1", false). + Execute("step1", false, false). End("step1"). // Run the step 2 @@ -50,7 +51,7 @@ func TestProcess_BasicSteps(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). End("step2"). // Finish @@ -100,7 +101,7 @@ func TestProcess_Grouping(t *testing.T) { Command: common.Ptr([]string{"a", "b"}), }). Start("step1"). - Execute("step1", false). + Execute("step1", false, false). End("step1"). // Start the group 1 @@ -114,7 +115,7 @@ func TestProcess_Grouping(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). End("step2"). // Run the step 2 @@ -124,7 +125,7 @@ func TestProcess_Grouping(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step3"). - Execute("step3", false). + Execute("step3", false, false). End("step3"). // End the group 1 @@ -137,7 +138,7 @@ func TestProcess_Grouping(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step4"). - Execute("step4", false). + Execute("step4", false, false). End("step4"). // Finish @@ -184,7 +185,7 @@ func TestProcess_Pause(t *testing.T) { Command: common.Ptr([]string{"a", "b"}), }). Start("step1"). - Execute("step1", false). + Execute("step1", false, false). End("step1"). // Run the step 2 @@ -194,7 +195,7 @@ func TestProcess_Pause(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). End("step2"). // Finish @@ -238,7 +239,7 @@ func TestProcess_NegativeStep(t *testing.T) { Command: common.Ptr([]string{"a", "b"}), }). Start("step1"). - Execute("step1", true). + Execute("step1", true, false). End("step1"). // Run the step 2 @@ -248,7 +249,7 @@ func TestProcess_NegativeStep(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). End("step2"). // Finish @@ -291,7 +292,7 @@ func TestProcess_NegativeGroup(t *testing.T) { Command: common.Ptr([]string{"a", "b"}), }). Start("step1"). - Execute("step1", false). + Execute("step1", false, false). End("step1"). // Run the step 2 @@ -301,7 +302,7 @@ func TestProcess_NegativeGroup(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). End("step2"). // Finish @@ -345,7 +346,7 @@ func TestProcess_OptionalStep(t *testing.T) { Command: common.Ptr([]string{"a", "b"}), }). Start("step1"). - Execute("step1", false). + Execute("step1", false, false). End("step1"). // Run the step 2 @@ -355,7 +356,7 @@ func TestProcess_OptionalStep(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). End("step2"). // Finish @@ -404,7 +405,7 @@ func TestProcess_OptionalGroup(t *testing.T) { Command: common.Ptr([]string{"a", "b"}), }). Start("step1"). - Execute("step1", false). + Execute("step1", false, false). End("step1"). // Run the step 2 @@ -414,7 +415,7 @@ func TestProcess_OptionalGroup(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). End("step2"). // Finish @@ -464,7 +465,7 @@ func TestProcess_IgnoreExecutionOfStaticSkip(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). End("step2"). // Finish @@ -604,7 +605,7 @@ func TestProcess_IgnoreExecutionOfStaticSkip_PauseGroup(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). End("step2"). // Finish @@ -650,7 +651,7 @@ func TestProcess_ConsecutiveAlways(t *testing.T) { Command: common.Ptr([]string{"a", "b"}), }). Start("step1"). - Execute("step1", false). + Execute("step1", false, false). End("step1"). // Run the step 2 @@ -660,7 +661,64 @@ func TestProcess_ConsecutiveAlways(t *testing.T) { Command: common.Ptr([]string{"c", "d"}), }). Start("step2"). - Execute("step2", false). + Execute("step2", false, false). + End("step2"). + + // Finish + End("init"). + End("") + + // Assert + got, err := Process(root, nil) + assert.NoError(t, err) + assert.Equal(t, want, got) +} + +func TestProcess_PureShellAtTheEnd(t *testing.T) { + // Build the structure + root := stage.NewGroupStage("init", false) + step1 := stage.NewContainerStage("step1", stage.NewContainer().SetImage("image:1.2.3").SetCommand(constants.DefaultShellPath)) + step1.SetPure(true) + step1.SetCondition("always") + root.Add(step1) + step2 := stage.NewContainerStage("step2", stage.NewContainer().SetCommand(constants.DefaultShellPath)) + step2.SetPure(true) + step2.SetCondition("always") + root.Add(step2) + + // Build the expectations + want := actiontypes.NewActionList(). + // Declare stage conditions + Declare("init", "true"). + Declare("step1", "true", "init"). + Declare("step2", "true", "init"). + + // Declare group resolutions + Result("init", "step1&&step2"). + Result("", "init"). + + // Initialize + Start(""). + CurrentStatus("true"). + Start("init"). + + // Run the step 1 + CurrentStatus("init"). + MutateContainer("step1", testworkflowsv1.ContainerConfig{ + Image: "image:1.2.3", + Command: common.Ptr([]string{constants.DefaultShellPath}), + }). + Start("step1"). + Execute("step1", false, true). + End("step1"). + + // Run the step 2 + CurrentStatus("step1&&init"). + MutateContainer("step2", testworkflowsv1.ContainerConfig{ + Command: common.Ptr([]string{constants.DefaultShellPath}), + }). + Start("step2"). + Execute("step2", false, true). End("step2"). // Finish diff --git a/pkg/testworkflows/testworkflowprocessor/presets/processor_test.go b/pkg/testworkflows/testworkflowprocessor/presets/processor_test.go index 61f6bdc799c..b7f842085d7 100644 --- a/pkg/testworkflows/testworkflowprocessor/presets/processor_test.go +++ b/pkg/testworkflows/testworkflowprocessor/presets/processor_test.go @@ -152,7 +152,7 @@ func TestProcessBasic(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[0].Ref()). - Execute(sig[0].Ref(), false). + Execute(sig[0].Ref(), false, false). End(sig[0].Ref()). End(constants.RootOperationName). End("") @@ -272,7 +272,7 @@ func TestProcessShellWithNonStandardImage(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[0].Ref()). - Execute(sig[0].Ref(), false). + Execute(sig[0].Ref(), false, false). End(sig[0].Ref()). End(constants.RootOperationName). End("") @@ -406,7 +406,7 @@ func TestProcessBasicEnvReference(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[0].Ref()). - Execute(sig[0].Ref(), false). + Execute(sig[0].Ref(), false, false). End(sig[0].Ref()). End(constants.RootOperationName). End("") @@ -489,7 +489,7 @@ func TestProcessMultipleSteps(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[0].Ref()). - Execute(sig[0].Ref(), false). + Execute(sig[0].Ref(), false, false). End(sig[0].Ref()). CurrentStatus(and(sig[0].Ref(), constants.RootOperationName)) }). @@ -500,7 +500,7 @@ func TestProcessMultipleSteps(t *testing.T) { Args: cmdShell("shell-test-2"), }). Start(sig[1].Ref()). - Execute(sig[1].Ref(), false). + Execute(sig[1].Ref(), false, false). End(sig[1].Ref()). End(constants.RootOperationName). End("") @@ -603,7 +603,7 @@ func TestProcessNestedSteps(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[0].Ref()). - Execute(sig[0].Ref(), false). + Execute(sig[0].Ref(), false, false). End(sig[0].Ref()). CurrentStatus(and(sig[0].Ref(), constants.RootOperationName)). Start(sig[1].Ref()). @@ -616,7 +616,7 @@ func TestProcessNestedSteps(t *testing.T) { Args: cmdShell("shell-test-2"), }). Start(sig[1].Children()[0].Ref()). - Execute(sig[1].Children()[0].Ref(), false). + Execute(sig[1].Children()[0].Ref(), false, false). End(sig[1].Children()[0].Ref()). CurrentStatus(and(sig[1].Children()[0].Ref(), sig[1].Ref(), sig[0].Ref(), constants.RootOperationName)) }). @@ -627,7 +627,7 @@ func TestProcessNestedSteps(t *testing.T) { Args: cmdShell("shell-test-3"), }). Start(sig[1].Children()[1].Ref()). - Execute(sig[1].Children()[1].Ref(), false). + Execute(sig[1].Children()[1].Ref(), false, false). End(sig[1].Children()[1].Ref()). End(sig[1].Ref()). CurrentStatus(and(sig[1].Ref(), sig[0].Ref(), constants.RootOperationName)) @@ -639,7 +639,7 @@ func TestProcessNestedSteps(t *testing.T) { Args: cmdShell("shell-test-4"), }). Start(sig[2].Ref()). - Execute(sig[2].Ref(), false). + Execute(sig[2].Ref(), false, false). End(sig[2].Ref()). End(constants.RootOperationName). End("") @@ -936,7 +936,7 @@ func TestProcessShell(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[0].Ref()). - Execute(sig[0].Ref(), false). + Execute(sig[0].Ref(), false, false). End(sig[0].Ref()). End(constants.RootOperationName). End("") @@ -984,7 +984,7 @@ func TestProcessConsecutiveAlways(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[0].Ref()). - Execute(sig[0].Ref(), false). + Execute(sig[0].Ref(), false, false). End(sig[0].Ref()). CurrentStatus(and(sig[0].Ref(), constants.RootOperationName)) }).Append(func(list lite.LiteActionList) lite.LiteActionList { @@ -994,7 +994,7 @@ func TestProcessConsecutiveAlways(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[1].Ref()). - Execute(sig[1].Ref(), false). + Execute(sig[1].Ref(), false, false). End(sig[1].Ref()). End(constants.RootOperationName). End("") @@ -1044,7 +1044,7 @@ func TestProcessNestedCondition(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[0].Ref()). - Execute(sig[0].Ref(), false). + Execute(sig[0].Ref(), false, false). End(sig[0].Ref()). CurrentStatus(and(sig[0].Ref(), constants.RootOperationName)) }).Append(func(list lite.LiteActionList) lite.LiteActionList { @@ -1054,7 +1054,7 @@ func TestProcessNestedCondition(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[1].Ref()). - Execute(sig[1].Ref(), false). + Execute(sig[1].Ref(), false, false). End(sig[1].Ref()). End(constants.RootOperationName). End("") @@ -1109,7 +1109,7 @@ func TestProcessConditionWithMultipleOperations(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[0].Ref()). - Execute(sig[0].Ref(), false). + Execute(sig[0].Ref(), false, false). End(sig[0].Ref()). CurrentStatus(and(sig[0].Ref(), constants.RootOperationName)). Start(virtual.Ref()). @@ -1121,7 +1121,7 @@ func TestProcessConditionWithMultipleOperations(t *testing.T) { Args: cmdShell("shell-test"), }). Start(sig[1].Ref()). - Execute(sig[1].Ref(), false). + Execute(sig[1].Ref(), false, false). End(sig[1].Ref()). CurrentStatus(and(sig[1].Ref(), virtual.Ref(), sig[0].Ref(), constants.RootOperationName)) @@ -1132,7 +1132,7 @@ func TestProcessConditionWithMultipleOperations(t *testing.T) { Args: cmdShell("shell-test-2"), }). Start(sig[2].Ref()). - Execute(sig[2].Ref(), false). + Execute(sig[2].Ref(), false, false). End(sig[2].Ref()). End(virtual.Ref()). End(constants.RootOperationName). @@ -1249,7 +1249,7 @@ func TestProcess_ConditionAlways(t *testing.T) { Args: cmdShell("test-command-1"), }). Start(sig[0].Ref()). - Execute(sig[0].Ref(), false). + Execute(sig[0].Ref(), false, false). End(sig[0].Ref()). CurrentStatus(and(sig[0].Ref(), constants.RootOperationName)) }). @@ -1260,7 +1260,72 @@ func TestProcess_ConditionAlways(t *testing.T) { Args: cmdShell("echo $result"), }). Start(sig[1].Ref()). - Execute(sig[1].Ref(), false). + Execute(sig[1].Ref(), false, false). + End(sig[1].Ref()). + End(constants.RootOperationName). + End("") + }) + + assert.NoError(t, err) + assert.Equal(t, want, res.LiteActions()) +} + +func TestProcess_PureShellAtTheEnd(t *testing.T) { + wf := &testworkflowsv1.TestWorkflow{ + Spec: testworkflowsv1.TestWorkflowSpec{ + Steps: []testworkflowsv1.Step{ + { + StepMeta: testworkflowsv1.StepMeta{Pure: common.Ptr(true)}, + StepDefaults: testworkflowsv1.StepDefaults{Container: &testworkflowsv1.ContainerConfig{ + Image: "custom-image:1.2.3", + }}, + StepOperations: testworkflowsv1.StepOperations{Shell: "test-command-1"}, + }, + { + StepMeta: testworkflowsv1.StepMeta{Pure: common.Ptr(true)}, + StepOperations: testworkflowsv1.StepOperations{Shell: "test-command-2"}, + }, + }, + }, + } + + res, err := proc.Bundle(context.Background(), wf, testworkflowprocessor.BundleOptions{Config: testConfig}) + sig := res.Signature + + want := lite.NewLiteActionGroups(). + Append(func(list lite.LiteActionList) lite.LiteActionList { + return list. + // configure + Setup(true, false, true). + Declare(constants.RootOperationName, "true"). + Declare(sig[0].Ref(), "true", constants.RootOperationName). + Declare(sig[1].Ref(), sig[0].Ref(), constants.RootOperationName). + Result(constants.RootOperationName, and(sig[0].Ref(), sig[1].Ref())). + Result("", constants.RootOperationName). + + // initialize + Start(""). + CurrentStatus("true"). + Start(constants.RootOperationName). + CurrentStatus(constants.RootOperationName) + }). + Append(func(list lite.LiteActionList) lite.LiteActionList { + return list. + // start first container + MutateContainer(lite.LiteContainerConfig{ + Command: cmd("/.tktw/bin/sh"), + Args: cmdShell("test-command-1"), + }). + Start(sig[0].Ref()). + Execute(sig[0].Ref(), false, true). + End(sig[0].Ref()). + CurrentStatus(and(sig[0].Ref(), constants.RootOperationName)). + MutateContainer(lite.LiteContainerConfig{ + Command: cmd("/.tktw/bin/sh"), + Args: cmdShell("test-command-2"), + }). + Start(sig[1].Ref()). + Execute(sig[1].Ref(), false, true). End(sig[1].Ref()). End(constants.RootOperationName). End("")