diff --git a/cmd/argoexec/commands/emissary.go b/cmd/argoexec/commands/emissary.go index e85fe6320cca..3c0bc3c5c0e5 100644 --- a/cmd/argoexec/commands/emissary.go +++ b/cmd/argoexec/commands/emissary.go @@ -132,6 +132,18 @@ func NewEmissaryCommand() *cobra.Command { command.Stderr = io.MultiWriter(os.Stderr, stderr) } + if _, ok := os.LookupEnv("ARGO_DEBUG_PAUSE_BEFORE"); ok { + for { + // User can create the file: /ctr/NAME_OF_THE_CONTAINER/before + // in order to break out of the sleep and release the container from + // the debugging state. + if _, err := os.Stat(varRunArgo + "/ctr/" + containerName + "/before"); os.IsNotExist(err) { + time.Sleep(time.Second) + continue + } + break + } + } if err := command.Start(); err != nil { return err } @@ -150,6 +162,19 @@ func NewEmissaryCommand() *cobra.Command { cmdErr := command.Wait() + if _, ok := os.LookupEnv("ARGO_DEBUG_PAUSE_AFTER"); ok { + for { + // User can create the file: /ctr/NAME_OF_THE_CONTAINER/after + // in order to break out of the sleep and release the container from + // the debugging state. + if _, err := os.Stat(varRunArgo + "/ctr/" + containerName + "/after"); os.IsNotExist(err) { + time.Sleep(time.Second) + continue + } + break + } + } + if cmdErr == nil { exitCode = 0 } else if exitError, ok := cmdErr.(*exec.ExitError); ok { diff --git a/cmd/argoexec/commands/wait.go b/cmd/argoexec/commands/wait.go index 9504b3df4067..045580e060d4 100644 --- a/cmd/argoexec/commands/wait.go +++ b/cmd/argoexec/commands/wait.go @@ -31,9 +31,7 @@ func waitContainer(ctx context.Context) error { stats.StartStatsTicker(5 * time.Minute) defer func() { - // Killing sidecar containers - err := wfExecutor.KillSidecars(ctx) - if err != nil { + if err := wfExecutor.KillSidecars(ctx); err != nil { wfExecutor.AddError(err) } }() diff --git a/test/e2e/functional/pause-after.yaml b/test/e2e/functional/pause-after.yaml new file mode 100644 index 000000000000..a95a56ec5e47 --- /dev/null +++ b/test/e2e/functional/pause-after.yaml @@ -0,0 +1,13 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: pause-after- +spec: + entrypoint: whalesay + templates: + - name: whalesay + container: + image: argoproj/argosay:v2 + env: + - name: ARGO_DEBUG_PAUSE_AFTER + value: 'true' diff --git a/test/e2e/functional/pause-before-after.yaml b/test/e2e/functional/pause-before-after.yaml new file mode 100644 index 000000000000..26acdc117d3e --- /dev/null +++ b/test/e2e/functional/pause-before-after.yaml @@ -0,0 +1,15 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: pause-before-after +spec: + entrypoint: whalesay + templates: + - name: whalesay + container: + image: argoproj/argosay:v2 + env: + - name: ARGO_DEBUG_PAUSE_BEFORE + value: 'true' + - name: ARGO_DEBUG_PAUSE_AFTER + value: 'true' diff --git a/test/e2e/functional/pause-before.yaml b/test/e2e/functional/pause-before.yaml new file mode 100644 index 000000000000..0fa6b2c1ba1e --- /dev/null +++ b/test/e2e/functional/pause-before.yaml @@ -0,0 +1,13 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: pause-before- +spec: + entrypoint: whalesay + templates: + - name: whalesay + container: + image: argoproj/argosay:v2 + env: + - name: ARGO_DEBUG_PAUSE_BEFORE + value: 'true' diff --git a/test/e2e/functional_test.go b/test/e2e/functional_test.go index 71266f56f978..af77b97ac13f 100644 --- a/test/e2e/functional_test.go +++ b/test/e2e/functional_test.go @@ -841,6 +841,37 @@ spec: WaitForWorkflow(fixtures.ToBeSucceeded) } +func (s *FunctionalSuite) TestPauseBefore() { + s.Given(). + Workflow(`@functional/pause-before.yaml`). + When(). + SubmitWorkflow(). + WaitForWorkflow(fixtures.ToBeRunning). + Exec("bash", []string{"-c", "sleep 5 && kubectl exec -i $(kubectl get pods | awk '/pause-before/ {print $1;exit}') -c main -- bash -c 'touch /proc/1/root/run/argo/ctr/main/before'"}, fixtures.NoError). + WaitForWorkflow(fixtures.ToBeSucceeded) +} + +func (s *FunctionalSuite) TestPauseAfter() { + s.Given(). + Workflow(`@functional/pause-after.yaml`). + When(). + SubmitWorkflow(). + WaitForWorkflow(fixtures.ToBeRunning). + Exec("bash", []string{"-c", "sleep 5 && kubectl exec -i $(kubectl get pods -n argo | awk '/pause-after/ {print $1;exit}') -c main -- bash -c 'touch /proc/1/root/run/argo/ctr/main/after'"}, fixtures.NoError). + WaitForWorkflow(fixtures.ToBeSucceeded) +} + +func (s *FunctionalSuite) TestPauseAfterAndBefore() { + s.Given(). + Workflow(`@functional/pause-before-after.yaml`). + When(). + SubmitWorkflow(). + WaitForWorkflow(fixtures.ToBeRunning). + Exec("bash", []string{"-c", "sleep 5 && kubectl exec -i $(kubectl get pods | awk '/pause-before-after/ {print $1;exit}') -c main -- bash -c 'touch /proc/1/root/run/argo/ctr/main/before'"}, fixtures.NoError). + Exec("bash", []string{"-c", "kubectl exec -i $(kubectl get pods | awk '/pause-before-after/ {print $1;exit}') -c main -- bash -c 'touch /proc/1/root/run/argo/ctr/main/after'"}, fixtures.NoError). + WaitForWorkflow(fixtures.ToBeSucceeded) +} + func TestFunctionalSuite(t *testing.T) { suite.Run(t, new(FunctionalSuite)) } diff --git a/workflow/controller/workflowpod.go b/workflow/controller/workflowpod.go index 3380515ed7fa..ecb7cb34f6e5 100644 --- a/workflow/controller/workflowpod.go +++ b/workflow/controller/workflowpod.go @@ -170,6 +170,7 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin case wfv1.TemplateTypeContainerSet: } + mainCtrs[i] = c } @@ -191,6 +192,16 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin activeDeadlineSeconds = tmplActiveDeadlineSeconds } } + + // If the template is marked for debugging no deadline will be set + for _, c := range mainCtrs { + for _, env := range c.Env { + if env.Name == "ARGO_DEBUG_PAUSE_BEFORE" || env.Name == "ARGO_DEBUG_PAUSE_AFTER" { + activeDeadlineSeconds = nil + } + } + } + pod := &apiv1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: util.PodName(woc.wf.Name, nodeName, tmpl.Name, nodeID),