diff --git a/cmd/workflow-controller/main.go b/cmd/workflow-controller/main.go index ad24683d01ce..c25ed50d500b 100644 --- a/cmd/workflow-controller/main.go +++ b/cmd/workflow-controller/main.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "log" "net/http" "os" "strings" @@ -12,7 +13,6 @@ import ( "github.com/argoproj/pkg/cli" kubecli "github.com/argoproj/pkg/kube/cli" "github.com/argoproj/pkg/stats" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -31,6 +31,7 @@ import ( wfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" cmdutil "github.com/argoproj/argo-workflows/v3/util/cmd" "github.com/argoproj/argo-workflows/v3/util/env" + "github.com/argoproj/argo-workflows/v3/util/logging" "github.com/argoproj/argo-workflows/v3/util/logs" pprofutil "github.com/argoproj/argo-workflows/v3/util/pprof" "github.com/argoproj/argo-workflows/v3/workflow/common" @@ -72,6 +73,8 @@ func NewRootCommand() *cobra.Command { RunE: func(c *cobra.Command, args []string) error { defer runtimeutil.HandleCrashWithContext(context.Background(), runtimeutil.PanicHandlers...) + log := logging.NewSlogLogger() + cli.SetLogLevel(logLevel) cmdutil.SetGLogLevel(glogLevel) cmdutil.SetLogFormatter(logFormat) @@ -104,7 +107,7 @@ func NewRootCommand() *cobra.Command { wfclientset := wfclientset.NewForConfigOrDie(config) if !namespaced && managedNamespace != "" { - log.Warn("ignoring --managed-namespace because --namespaced is false") + log.Warn(ctx, "ignoring --managed-namespace because --namespaced is false") managedNamespace = "" } if namespaced && managedNamespace == "" { @@ -118,15 +121,15 @@ func NewRootCommand() *cobra.Command { leaderElectionOff := os.Getenv("LEADER_ELECTION_DISABLE") if leaderElectionOff == "true" { - log.Info("Leader election is turned off. Running in single-instance mode") - log.WithField("id", "single-instance").Info("starting leading") + log.Info(ctx, "Leader election is turned off. Running in single-instance mode") + log.WithField(ctx, "id", "single-instance").Info(ctx, "starting leading") go wfController.Run(ctx, workflowWorkers, workflowTTLWorkers, podCleanupWorkers, cronWorkflowWorkers, workflowArchiveWorkers) go wfController.RunPrometheusServer(ctx, false) } else { nodeID, ok := os.LookupEnv("LEADER_ELECTION_IDENTITY") if !ok { - log.Fatal("LEADER_ELECTION_IDENTITY must be set so that the workflow controllers can elect a leader") + log.Fatal(ctx, "LEADER_ELECTION_IDENTITY must be set so that the workflow controllers can elect a leader") } leaderName := "workflow-controller" @@ -166,13 +169,13 @@ func NewRootCommand() *cobra.Command { }() }, OnStoppedLeading: func() { - log.WithField("id", nodeID).Info("stopped leading") + log.WithField(ctx, "id", nodeID).Info(ctx, "stopped leading") cancel() wg.Wait() go wfController.RunPrometheusServer(dummyCtx, true) }, OnNewLeader: func(identity string) { - log.WithField("leader", identity).Info("new leader") + log.WithField(ctx, "leader", identity).Info(ctx, "new leader") }, }, }) @@ -181,7 +184,7 @@ func NewRootCommand() *cobra.Command { http.HandleFunc("/healthz", wfController.Healthz) go func() { - log.Println(http.ListenAndServe(":6060", nil)) + log.Println(ctx, http.ListenAndServe(":6060", nil).Error()) }() <-ctx.Done() @@ -234,10 +237,10 @@ func init() { } func initConfig() { - log.SetFormatter(&log.TextFormatter{ + /* log.SetFormatter(&log.TextFormatter{ TimestampFormat: "2006-01-02T15:04:05.000Z", FullTimestamp: true, - }) + }) */ } func main() { diff --git a/util/logging/logging.go b/util/logging/logging.go new file mode 100644 index 000000000000..bec6e77cc7e9 --- /dev/null +++ b/util/logging/logging.go @@ -0,0 +1,40 @@ +package logging + +import "context" + +const ( + ErrorField string = "error" +) + +type Fields map[string]interface{} + +// Logger exports a logging interface +type Logger interface { + WithFields(ctx context.Context, fields Fields) Logger + WithField(ctx context.Context, name string, value interface{}) Logger + WithError(ctx context.Context, err error) Logger + + Info(ctx context.Context, msg string) + Infof(ctx context.Context, format string, args ...interface{}) + + Warn(ctx context.Context, msg string) + Warnf(ctx context.Context, format string, args ...interface{}) + + Fatal(ctx context.Context, msg string) + Fatalf(ctx context.Context, format string, args ...interface{}) + + Error(ctx context.Context, msg string) + Errorf(ctx context.Context, format string, args ...interface{}) + + Debug(ctx context.Context, msg string) + Debugf(ctx context.Context, format string, args ...interface{}) + + Warning(ctx context.Context, msg string) + Warningf(ctx context.Context, format string, args ...interface{}) + + Println(ctx context.Context, msg string) + Printf(ctx context.Context, format string, args ...interface{}) + + Panic(ctx context.Context, msg string) + Panicf(ctx context.Context, format string, args ...interface{}) +} diff --git a/util/logging/slog.go b/util/logging/slog.go new file mode 100644 index 000000000000..9e21ce1bc5eb --- /dev/null +++ b/util/logging/slog.go @@ -0,0 +1,141 @@ +package logging + +import ( + "context" + "fmt" + "log/slog" + "os" +) + +type slogLogger struct { + fields Fields + logger *slog.Logger +} + +func (s *slogLogger) WithFields(_ context.Context, fields Fields) Logger { + logger := s.logger + + newFields := make(Fields) + for k, v := range s.fields { + newFields[k] = v + logger = logger.With(k, v) + } + for k, v := range fields { + newFields[k] = v + logger = logger.With(k, v) + } + + return &slogLogger{ + fields: newFields, + logger: logger, + } +} + +func (s *slogLogger) WithField(_ context.Context, name string, value interface{}) Logger { + newFields := make(Fields) + + logger := s.logger + for k, v := range s.fields { + newFields[k] = v + logger = s.logger.With(k, v) + } + + logger = logger.With(name, value) + newFields[name] = value + return &slogLogger{ + fields: newFields, + logger: logger, + } +} + +func (s *slogLogger) WithError(ctx context.Context, err error) Logger { + return s.WithField(ctx, ErrorField, err) +} + +func (s *slogLogger) Info(ctx context.Context, msg string) { + s.logger.InfoContext(ctx, msg) +} + +func (s *slogLogger) Infof(ctx context.Context, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + s.logger.InfoContext(ctx, msg) +} + +func (s *slogLogger) Warn(ctx context.Context, msg string) { + s.logger.WarnContext(ctx, msg) +} + +func (s *slogLogger) Warnf(ctx context.Context, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + s.logger.WarnContext(ctx, msg) +} + +func (s *slogLogger) Fatal(ctx context.Context, msg string) { + s.logger.ErrorContext(ctx, msg) + os.Exit(1) +} + +func (s *slogLogger) Fatalf(ctx context.Context, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + s.logger.ErrorContext(ctx, msg) + os.Exit(1) +} + +func (s *slogLogger) Error(ctx context.Context, msg string) { + s.logger.ErrorContext(ctx, msg) +} + +func (s *slogLogger) Errorf(ctx context.Context, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + s.logger.ErrorContext(ctx, msg) +} + +func (s *slogLogger) Debug(ctx context.Context, msg string) { + s.logger.DebugContext(ctx, msg) +} + +func (s *slogLogger) Debugf(ctx context.Context, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + s.logger.DebugContext(ctx, msg) +} + +func (s *slogLogger) Warning(ctx context.Context, msg string) { + s.logger.WarnContext(ctx, msg) +} + +func (s *slogLogger) Warningf(ctx context.Context, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + s.logger.WarnContext(ctx, msg) +} + +func (s *slogLogger) Println(ctx context.Context, msg string) { + s.logger.InfoContext(ctx, msg) +} + +func (s *slogLogger) Printf(ctx context.Context, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + s.logger.InfoContext(ctx, msg) +} + +func (s *slogLogger) Panic(ctx context.Context, msg string) { + s.logger.ErrorContext(ctx, msg) + panic(msg) +} + +func (s *slogLogger) Panicf(ctx context.Context, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + s.logger.ErrorContext(ctx, msg) + panic(msg) +} + +// NewSlogLogger returns a slog based logger +func NewSlogLogger() Logger { + f := make(Fields) + l := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})) + // l := slog.Default() + s := slogLogger{ + fields: f, + logger: l, + } + return &s +} diff --git a/util/runtime/panic.go b/util/runtime/panic.go index e66a4cf52869..563fa658f4b7 100644 --- a/util/runtime/panic.go +++ b/util/runtime/panic.go @@ -1,12 +1,14 @@ package runtime import ( + "context" "runtime" - log "github.com/sirupsen/logrus" + "github.com/argoproj/argo-workflows/v3/util/logging" ) -func RecoverFromPanic(log *log.Entry) { +// RecoverFromPanic recovers from a panic and logs the panic and call stack +func RecoverFromPanic(ctx context.Context, log logging.Logger) { if r := recover(); r != nil { // Same as stdlib http server code. Manually allocate stack trace buffer size // to prevent excessively large logs @@ -15,7 +17,7 @@ func RecoverFromPanic(log *log.Entry) { stackSize := runtime.Stack(stackTraceBuffer, false) // Free up the unused spaces stackTraceBuffer = stackTraceBuffer[:stackSize] - log.Errorf("recovered from panic %q. Call stack:\n%s", + log.Errorf(ctx, "recovered from panic %q. Call stack:\n%s", r, stackTraceBuffer) } diff --git a/workflow/common/ancestry.go b/workflow/common/ancestry.go index e91f7aac006c..76e64bbc8a25 100644 --- a/workflow/common/ancestry.go +++ b/workflow/common/ancestry.go @@ -1,6 +1,7 @@ package common import ( + "context" "fmt" "regexp" "sort" @@ -11,9 +12,9 @@ import ( ) type DagContext interface { - GetTask(taskName string) *wfv1.DAGTask - GetTaskDependencies(taskName string) []string - GetTaskFinishedAtTime(taskName string) time.Time + GetTask(ctx context.Context, taskName string) *wfv1.DAGTask + GetTaskDependencies(ctx context.Context, taskName string) []string + GetTaskFinishedAtTime(ctx context.Context, taskName string) time.Time } type TaskResult string @@ -48,8 +49,8 @@ const ( DependencyTypeItems ) -func GetTaskDependencies(task *wfv1.DAGTask, ctx DagContext) (map[string]DependencyType, string) { - depends := getTaskDependsLogic(task, ctx) +func GetTaskDependencies(ctx context.Context, task *wfv1.DAGTask, dctx DagContext) (map[string]DependencyType, string) { + depends := getTaskDependsLogic(ctx, task, dctx) matches := taskNameRegex.FindAllStringSubmatchIndex(depends, -1) var expansionMatches []expansionMatch dependencies := make(map[string]DependencyType) @@ -79,7 +80,7 @@ func GetTaskDependencies(task *wfv1.DAGTask, ctx DagContext) (map[string]Depende return expansionMatches[i].start > expansionMatches[j].start }) for _, match := range expansionMatches { - matchTask := ctx.GetTask(match.taskName) + matchTask := dctx.GetTask(ctx, match.taskName) depends = depends[:match.start] + expandDependency(match.taskName, matchTask) + depends[match.end:] } @@ -106,7 +107,7 @@ func ValidateTaskResults(dagTask *wfv1.DAGTask) error { return nil } -func getTaskDependsLogic(dagTask *wfv1.DAGTask, ctx DagContext) string { +func getTaskDependsLogic(ctx context.Context, dagTask *wfv1.DAGTask, dctx DagContext) string { if dagTask.Depends != "" { return dagTask.Depends } @@ -114,7 +115,7 @@ func getTaskDependsLogic(dagTask *wfv1.DAGTask, ctx DagContext) string { // For backwards compatibility, "dependencies: [A, B]" is equivalent to "depends: (A.Successful || A.Skipped || A.Daemoned)) && (B.Successful || B.Skipped || B.Daemoned)" var dependencies []string for _, dependency := range dagTask.Dependencies { - depTask := ctx.GetTask(dependency) + depTask := dctx.GetTask(ctx, dependency) dependencies = append(dependencies, expandDependency(dependency, depTask)) } return strings.Join(dependencies, " && ") @@ -137,7 +138,7 @@ func expandDependency(depName string, depTask *wfv1.DAGTask) string { // GetTaskAncestry returns a list of taskNames which are ancestors of this task. // The list is ordered by the tasks finished time. -func GetTaskAncestry(ctx DagContext, taskName string) []string { +func GetTaskAncestry(ctx context.Context, dctx DagContext, taskName string) []string { visited := make(map[string]time.Time) var getAncestry func(currTask string) @@ -145,12 +146,12 @@ func GetTaskAncestry(ctx DagContext, taskName string) []string { if _, seen := visited[currTask]; seen { return } - for _, depTask := range ctx.GetTaskDependencies(currTask) { + for _, depTask := range dctx.GetTaskDependencies(ctx, currTask) { getAncestry(depTask) } if currTask != taskName { if _, ok := visited[currTask]; !ok { - visited[currTask] = ctx.GetTaskFinishedAtTime(currTask) + visited[currTask] = dctx.GetTaskFinishedAtTime(ctx, currTask) } } } diff --git a/workflow/common/ancestry_test.go b/workflow/common/ancestry_test.go index a666a9697fb3..60557fee886b 100644 --- a/workflow/common/ancestry_test.go +++ b/workflow/common/ancestry_test.go @@ -1,6 +1,7 @@ package common import ( + "context" "testing" "time" @@ -23,12 +24,13 @@ func TestGetTaskDependenciesFromDepends(t *testing.T) { }, } - ctx := &testContext{ + dctx := &testContext{ testTasks: testTasks, } + ctx := context.Background() task := &wfv1.DAGTask{Depends: "(task-1 || task-2.Succeeded) && !task-3.Succeeded"} - deps, logic := GetTaskDependencies(task, ctx) + deps, logic := GetTaskDependencies(ctx, task, dctx) assert.Len(t, deps, 3) for _, dep := range []string{"task-1", "task-2", "task-3"} { assert.Contains(t, deps, dep) @@ -36,7 +38,7 @@ func TestGetTaskDependenciesFromDepends(t *testing.T) { assert.Equal(t, "((task-1.Succeeded || task-1.Skipped || task-1.Daemoned) || task-2.Succeeded) && !task-3.Succeeded", logic) task = &wfv1.DAGTask{Depends: "(task-1 || task-2.AnySucceeded) && !task-3.Succeeded"} - deps, logic = GetTaskDependencies(task, ctx) + deps, logic = GetTaskDependencies(ctx, task, dctx) assert.Len(t, deps, 3) for _, dep := range []string{"task-1", "task-2", "task-3"} { assert.Contains(t, deps, dep) @@ -44,7 +46,7 @@ func TestGetTaskDependenciesFromDepends(t *testing.T) { assert.Equal(t, "((task-1.Succeeded || task-1.Skipped || task-1.Daemoned) || task-2.AnySucceeded) && !task-3.Succeeded", logic) task = &wfv1.DAGTask{Depends: "(task-1||(task-2.Succeeded || task-2.Failed))&&!task-3.Failed"} - deps, logic = GetTaskDependencies(task, ctx) + deps, logic = GetTaskDependencies(ctx, task, dctx) assert.Len(t, deps, 3) for _, dep := range []string{"task-1", "task-2", "task-3"} { assert.Contains(t, deps, dep) @@ -52,30 +54,30 @@ func TestGetTaskDependenciesFromDepends(t *testing.T) { assert.Equal(t, "((task-1.Succeeded || task-1.Skipped || task-1.Daemoned)||(task-2.Succeeded || task-2.Failed))&&!task-3.Failed", logic) task = &wfv1.DAGTask{Depends: "(task-1 || task-1.Succeeded) && !task-1.Failed"} - deps, logic = GetTaskDependencies(task, ctx) + deps, logic = GetTaskDependencies(ctx, task, dctx) assert.Equal(t, map[string]DependencyType{"task-1": DependencyTypeTask}, deps) assert.Equal(t, "((task-1.Succeeded || task-1.Skipped || task-1.Daemoned) || task-1.Succeeded) && !task-1.Failed", logic) task = &wfv1.DAGTask{Depends: "task-1.Succeeded && task-1.AnySucceeded"} - deps, logic = GetTaskDependencies(task, ctx) + deps, logic = GetTaskDependencies(ctx, task, dctx) assert.Equal(t, map[string]DependencyType{"task-1": DependencyTypeItems}, deps) assert.Equal(t, "task-1.Succeeded && task-1.AnySucceeded", logic) - ctx.testTasks[0].ContinueOn = &wfv1.ContinueOn{Failed: true} + dctx.testTasks[0].ContinueOn = &wfv1.ContinueOn{Failed: true} task = &wfv1.DAGTask{Depends: "task-1"} - deps, logic = GetTaskDependencies(task, ctx) + deps, logic = GetTaskDependencies(ctx, task, dctx) assert.Equal(t, map[string]DependencyType{"task-1": DependencyTypeTask}, deps) assert.Equal(t, "(task-1.Succeeded || task-1.Skipped || task-1.Daemoned || task-1.Failed)", logic) - ctx.testTasks[0].ContinueOn = &wfv1.ContinueOn{Error: true} + dctx.testTasks[0].ContinueOn = &wfv1.ContinueOn{Error: true} task = &wfv1.DAGTask{Depends: "task-1"} - deps, logic = GetTaskDependencies(task, ctx) + deps, logic = GetTaskDependencies(ctx, task, dctx) assert.Equal(t, map[string]DependencyType{"task-1": DependencyTypeTask}, deps) assert.Equal(t, "(task-1.Succeeded || task-1.Skipped || task-1.Daemoned || task-1.Errored)", logic) - ctx.testTasks[0].ContinueOn = &wfv1.ContinueOn{Failed: true, Error: true} + dctx.testTasks[0].ContinueOn = &wfv1.ContinueOn{Failed: true, Error: true} task = &wfv1.DAGTask{Depends: "task-1"} - deps, logic = GetTaskDependencies(task, ctx) + deps, logic = GetTaskDependencies(ctx, task, dctx) assert.Equal(t, map[string]DependencyType{"task-1": DependencyTypeTask}, deps) assert.Equal(t, "(task-1.Succeeded || task-1.Skipped || task-1.Daemoned || task-1.Errored || task-1.Failed)", logic) } @@ -111,15 +113,16 @@ func TestGetTaskDependsLogic(t *testing.T) { }, } - ctx := &testContext{ + dctx := &testContext{ testTasks: testTasks, } + ctx := context.Background() task := &wfv1.DAGTask{Depends: "(task-1 || task-2.Succeeded) && !task-3"} - depends := getTaskDependsLogic(task, ctx) + depends := getTaskDependsLogic(ctx, task, dctx) assert.Equal(t, "(task-1 || task-2.Succeeded) && !task-3", depends) task = &wfv1.DAGTask{Dependencies: []string{"task-1", "task-2"}} - depends = getTaskDependsLogic(task, ctx) + depends = getTaskDependsLogic(ctx, task, dctx) assert.Equal(t, "(task-1.Succeeded || task-1.Skipped || task-1.Daemoned) && (task-2.Succeeded || task-2.Skipped || task-2.Daemoned)", depends) } @@ -128,7 +131,7 @@ type testContext struct { testTasks []*wfv1.DAGTask } -func (d *testContext) GetTask(taskName string) *wfv1.DAGTask { +func (d *testContext) GetTask(ctx context.Context, taskName string) *wfv1.DAGTask { for _, task := range d.testTasks { if task.Name == taskName { return task @@ -137,11 +140,11 @@ func (d *testContext) GetTask(taskName string) *wfv1.DAGTask { return nil } -func (d *testContext) GetTaskDependencies(taskName string) []string { - return d.GetTask(taskName).Dependencies +func (d *testContext) GetTaskDependencies(ctx context.Context, taskName string) []string { + return d.GetTask(ctx, taskName).Dependencies } -func (d *testContext) GetTaskFinishedAtTime(taskName string) time.Time { +func (d *testContext) GetTaskFinishedAtTime(ctx context.Context, taskName string) time.Time { if finished, ok := d.status[taskName]; ok { return finished } @@ -174,7 +177,7 @@ func TestGetTaskAncestryForValidation(t *testing.T) { } now := time.Now() - ctx := &testContext{ + dctx := &testContext{ testTasks: testTasks, status: map[string]time.Time{ "task1": now.Add(1 * time.Minute), @@ -192,7 +195,7 @@ func TestGetTaskAncestryForValidation(t *testing.T) { { name: "one task", args: args{ - ctx: ctx, + ctx: dctx, taskName: "task2", }, want: []string{"task1"}, @@ -200,14 +203,15 @@ func TestGetTaskAncestryForValidation(t *testing.T) { { name: "multiple tasks", args: args{ - ctx: ctx, + ctx: dctx, taskName: "task4", }, want: []string{"task1", "task2", "task3"}, }, } + ctx := context.Background() for _, tt := range tests { - res := GetTaskAncestry(tt.args.ctx, tt.args.taskName) + res := GetTaskAncestry(ctx, tt.args.ctx, tt.args.taskName) assert.Equal(t, tt.want, res) } } @@ -237,7 +241,7 @@ func TestGetTaskAncestryForGlobalArtifacts(t *testing.T) { }, } - ctx := &testContext{ + dctx := &testContext{ testTasks: testTasks, status: map[string]time.Time{ "task1": time.Now().Add(1 * time.Minute), @@ -255,7 +259,7 @@ func TestGetTaskAncestryForGlobalArtifacts(t *testing.T) { { name: "one task", args: args{ - ctx: ctx, + ctx: dctx, taskName: "task2", }, want: []string{"task1"}, @@ -263,14 +267,15 @@ func TestGetTaskAncestryForGlobalArtifacts(t *testing.T) { { name: "multiple tasks", args: args{ - ctx: ctx, + ctx: dctx, taskName: "task4", }, want: []string{"task1", "task3", "task2"}, }, } + ctx := context.Background() for _, tt := range tests { - res := GetTaskAncestry(tt.args.ctx, tt.args.taskName) + res := GetTaskAncestry(ctx, tt.args.ctx, tt.args.taskName) assert.Equal(t, tt.want, res) } } diff --git a/workflow/controller/agent.go b/workflow/controller/agent.go index de484272e802..0272672e1780 100644 --- a/workflow/controller/agent.go +++ b/workflow/controller/agent.go @@ -31,7 +31,7 @@ func (woc *wfOperationCtx) isAgentPod(pod *apiv1.Pod) bool { } func (woc *wfOperationCtx) reconcileAgentPod(ctx context.Context) error { - woc.log.Infof("reconcileAgentPod") + woc.log.Info(ctx, "reconcileAgentPod") if len(woc.taskSet) == 0 { return nil } @@ -41,16 +41,16 @@ func (woc *wfOperationCtx) reconcileAgentPod(ctx context.Context) error { } // Check Pod is just created if pod != nil && pod.Status.Phase != "" { - woc.updateAgentPodStatus(pod) + woc.updateAgentPodStatus(ctx, pod) } return nil } -func (woc *wfOperationCtx) updateAgentPodStatus(pod *apiv1.Pod) { - woc.log.Info("updateAgentPodStatus") +func (woc *wfOperationCtx) updateAgentPodStatus(ctx context.Context, pod *apiv1.Pod) { + woc.log.Info(ctx, "updateAgentPodStatus") newPhase, message := assessAgentPodStatus(pod) if newPhase == wfv1.NodeFailed || newPhase == wfv1.NodeError { - woc.markTaskSetNodesError(fmt.Errorf(`agent pod failed with reason:"%s"`, message)) + woc.markTaskSetNodesError(ctx, fmt.Errorf(`agent pod failed with reason:"%s"`, message)) } } @@ -111,7 +111,7 @@ func (woc *wfOperationCtx) getCertVolumeMount(ctx context.Context, name string) func (woc *wfOperationCtx) createAgentPod(ctx context.Context) (*apiv1.Pod, error) { podName := woc.getAgentPodName() - log := woc.log.WithField("podName", podName) + log := woc.log.WithField(ctx, "podName", podName) obj, exists, err := woc.controller.podInformer.GetStore().Get(cache.ExplicitKey(woc.wf.Namespace + "/" + podName)) if err != nil { @@ -120,7 +120,7 @@ func (woc *wfOperationCtx) createAgentPod(ctx context.Context) (*apiv1.Pod, erro if exists { existing, ok := obj.(*apiv1.Pod) if ok { - log.WithField("podPhase", existing.Status.Phase).Debug("Skipped pod creation: already exists") + log.WithField(ctx, "podPhase", existing.Status.Phase).Debugf(ctx, "Skipped pod creation: already exists") return existing, nil } } @@ -247,11 +247,11 @@ func (woc *wfOperationCtx) createAgentPod(ctx context.Context) (*apiv1.Pod, erro pod.ObjectMeta.Labels[common.LabelKeyControllerInstanceID] = woc.controller.Config.InstanceID } - log.Debug("Creating Agent pod") + log.Debugf(ctx, "Creating Agent pod") created, err := woc.controller.kubeclientset.CoreV1().Pods(woc.wf.ObjectMeta.Namespace).Create(ctx, pod, metav1.CreateOptions{}) if err != nil { - log.WithError(err).Info("Failed to create Agent pod") + log.WithError(ctx, err).Info(ctx, "Failed to create Agent pod") if apierr.IsAlreadyExists(err) { // get a reference to the currently existing Pod since the created pod returned before was nil. if existing, err := woc.controller.kubeclientset.CoreV1().Pods(woc.wf.ObjectMeta.Namespace).Get(ctx, pod.Name, metav1.GetOptions{}); err == nil { @@ -264,7 +264,7 @@ func (woc *wfOperationCtx) createAgentPod(ctx context.Context) (*apiv1.Pod, erro } return nil, errors.InternalWrapError(fmt.Errorf("failed to create Agent pod. Reason: %v", err)) } - log.Info("Created Agent pod") + log.Info(ctx, "Created Agent pod") return created, nil } diff --git a/workflow/controller/agent_test.go b/workflow/controller/agent_test.go index f816c772dbcc..df353b52e166 100644 --- a/workflow/controller/agent_test.go +++ b/workflow/controller/agent_test.go @@ -46,7 +46,6 @@ spec: url: "{{inputs.parameters.url}}" `) - ctx := context.Background() var ts wfv1.WorkflowTaskSet wfv1.MustUnmarshal(`apiVersion: argoproj.io/v1alpha1 kind: WorkflowTaskSet @@ -91,7 +90,8 @@ status: t.Run("CreateTaskSet", func(t *testing.T) { cancel, controller := newController(wf, ts, defaultServiceAccount) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) tslist, err := woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets("default").List(ctx, v1.ListOptions{}) require.NoError(t, err) @@ -117,7 +117,8 @@ status: cancel, controller := newController(wf, ts, defaultServiceAccount) defer cancel() controller.Config.InstanceID = "testID" - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) tslist, err := woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets("default").List(ctx, v1.ListOptions{}) require.NoError(t, err) diff --git a/workflow/controller/artifact_gc.go b/workflow/controller/artifact_gc.go index c485458a8fec..6f7da876ec6b 100644 --- a/workflow/controller/artifact_gc.go +++ b/workflow/controller/artifact_gc.go @@ -27,7 +27,7 @@ const artifactGCComponent = "artifact-gc" // artifactGCEnabled is a feature flag to globally disabled artifact GC in case of emergency var artifactGCEnabled, _ = env.GetBool("ARGO_ARTIFACT_GC_ENABLED", true) -func (woc *wfOperationCtx) addArtifactGCFinalizer() { +func (woc *wfOperationCtx) addArtifactGCFinalizer(ctx context.Context) { if !artifactGCEnabled { return } @@ -43,7 +43,7 @@ func (woc *wfOperationCtx) addArtifactGCFinalizer() { return // we already verified it's not required for this workflow } if woc.HasArtifactGC() { - woc.log.Info("adding artifact GC finalizer") + woc.log.Info(ctx, "adding artifact GC finalizer") finalizers := append(woc.wf.GetFinalizers(), common.FinalizerArtifactGC) woc.wf.SetFinalizers(finalizers) woc.wf.Status.ArtifactGCStatus.NotSpecified = false @@ -62,7 +62,7 @@ func (woc *wfOperationCtx) garbageCollectArtifacts(ctx context.Context) error { // based on current state of Workflow, which Artifact GC Strategies can be processed now? strategies := woc.artifactGCStrategiesReady() for strategy := range strategies { - woc.log.Debugf("processing Artifact GC Strategy %s", strategy) + woc.log.Debugf(ctx, "processing Artifact GC Strategy %s", strategy) err := woc.processArtifactGCStrategy(ctx, strategy) if err != nil { return err @@ -134,12 +134,12 @@ func (woc *wfOperationCtx) processArtifactGCStrategy(ctx context.Context, strate var err error - woc.log.Debugf("processing Artifact GC Strategy %s", strategy) + woc.log.Debugf(ctx, "processing Artifact GC Strategy %s", strategy) // Search for artifacts artifactSearchResults := woc.findArtifactsToGC(strategy) if len(artifactSearchResults) == 0 { - woc.log.Debugf("No Artifact Search Results returned from strategy %s", strategy) + woc.log.Debugf(ctx, "No Artifact Search Results returned from strategy %s", strategy) return nil } @@ -176,7 +176,7 @@ func (woc *wfOperationCtx) processArtifactGCStrategy(ctx context.Context, strate // get the Template for the Artifact node, err := woc.wf.Status.Nodes.Get(artifactSearchResult.NodeID) if err != nil { - woc.log.Errorf("Was unable to obtain node for %s", artifactSearchResult.NodeID) + woc.log.Errorf(ctx, "Was unable to obtain node for %s", artifactSearchResult.NodeID) return fmt.Errorf("can't process Artifact GC Strategy %s: node ID %q not found in Status??", strategy, artifactSearchResult.NodeID) } templateName := util.GetTemplateFromNode(*node) @@ -205,7 +205,7 @@ func (woc *wfOperationCtx) processArtifactGCStrategy(ctx context.Context, strate for templateName, artifacts := range templatesToArtList { template := templatesByName[templateName] - woc.addTemplateArtifactsToTasks(podName, &tasks, template, artifacts) + woc.addTemplateArtifactsToTasks(ctx, podName, &tasks, template, artifacts) } if len(tasks) > 0 { @@ -284,7 +284,7 @@ func (woc *wfOperationCtx) artifactGCPodLabel(podName string) string { return fmt.Sprintf("%d", hashedPod.Sum32()) } -func (woc *wfOperationCtx) addTemplateArtifactsToTasks(podName string, tasks *[]*wfv1.WorkflowArtifactGCTask, template *wfv1.Template, artifactSearchResults wfv1.ArtifactSearchResults) { +func (woc *wfOperationCtx) addTemplateArtifactsToTasks(ctx context.Context, podName string, tasks *[]*wfv1.WorkflowArtifactGCTask, template *wfv1.Template, artifactSearchResults wfv1.ArtifactSearchResults) { if len(artifactSearchResults) == 0 { return } @@ -343,7 +343,7 @@ func (woc *wfOperationCtx) addTemplateArtifactsToTasks(podName string, tasks *[] artifactNodeSpec.Artifacts[artifactSearchResult.Name] = artifactSearchResult.Artifact } - woc.log.Debugf("list of artifacts pertaining to template %s to WorkflowArtifactGCTask %q: %+v", template.Name, currentTask.Name, artifactsByNode) + woc.log.Debugf(ctx, "list of artifacts pertaining to template %s to WorkflowArtifactGCTask %q: %+v", template.Name, currentTask.Name, artifactsByNode) } @@ -369,9 +369,9 @@ func (woc *wfOperationCtx) createWorkflowArtifactGCTask(ctx context.Context, tas return nil, err } if foundTask != nil { - woc.log.Debugf("Artifact GC Task %s already exists", task.Name) + woc.log.Debugf(ctx, "Artifact GC Task %s already exists", task.Name) } else { - woc.log.Infof("Creating Artifact GC Task %s", task.Name) + woc.log.Infof(ctx, "Creating Artifact GC Task %s", task.Name) task, err = woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowArtifactGCTasks(woc.wf.Namespace).Create(ctx, task, metav1.CreateOptions{}) if err != nil { @@ -386,8 +386,8 @@ func (woc *wfOperationCtx) createArtifactGCPod(ctx context.Context, strategy wfv podInfo podInfo, podName string, templatesToArtList templatesToArtifacts, templatesByName map[string]*wfv1.Template) (*corev1.Pod, error) { woc.log. - WithField("strategy", strategy). - Infof("creating pod to delete artifacts: %s", podName) + WithField(ctx, "strategy", strategy). + Infof(ctx, "creating pod to delete artifacts: %s", podName) // Pod is owned by WorkflowArtifactGCTasks, so it will die automatically when all of them have died ownerReferences := make([]metav1.OwnerReference, len(tasks)) @@ -492,7 +492,7 @@ func (woc *wfOperationCtx) createArtifactGCPod(ctx context.Context, strategy wfv if err != nil { if apierr.IsAlreadyExists(err) { - woc.log.Warningf("Artifact GC Pod %s already exists?", pod.Name) + woc.log.Warningf(ctx, "Artifact GC Pod %s already exists?", pod.Name) } else { return nil, fmt.Errorf("failed to create pod: %w", err) } @@ -524,10 +524,10 @@ func (woc *wfOperationCtx) processArtifactGCCompletion(ctx context.Context) erro // if Pod is done processing the results if phase == corev1.PodSucceeded || phase == corev1.PodFailed { - woc.log.WithField("pod", pod.Name). - WithField("phase", phase). - WithField("message", pod.Status.Message). - Info("reconciling artifact-gc pod") + woc.log.WithField(ctx, "pod", pod.Name). + WithField(ctx, "phase", phase). + WithField(ctx, "message", pod.Status.Message). + Infof(ctx, "reconciling artifact-gc pod") err = woc.processCompletedArtifactGCPod(ctx, pod) if err != nil { @@ -548,7 +548,7 @@ func (woc *wfOperationCtx) processArtifactGCCompletion(ctx context.Context) erro removeFinalizer = woc.allArtifactsDeleted() } if removeFinalizer { - woc.log.Infof("no remaining artifacts to GC, removing artifact GC finalizer (forceFinalizerRemoval=%v)", forceFinalizerRemoval) + woc.log.Infof(ctx, "no remaining artifacts to GC, removing artifact GC finalizer (forceFinalizerRemoval=%v)", forceFinalizerRemoval) woc.wf.Finalizers = slices.DeleteFunc(woc.wf.Finalizers, func(x string) bool { return x == common.FinalizerArtifactGC }) woc.updated = true @@ -593,7 +593,7 @@ func (woc *wfOperationCtx) findArtifactsToGC(strategy wfv1.ArtifactGCStrategy) w } func (woc *wfOperationCtx) processCompletedArtifactGCPod(ctx context.Context, pod *corev1.Pod) error { - woc.log.Infof("processing completed Artifact GC Pod %q", pod.Name) + woc.log.Infof(ctx, "processing completed Artifact GC Pod %q", pod.Name) strategyStr, found := pod.Annotations[common.AnnotationKeyArtifactGCStrategy] if !found { @@ -615,16 +615,16 @@ func (woc *wfOperationCtx) processCompletedArtifactGCPod(ctx context.Context, po } for _, task := range taskList.Items { - allArtifactsSucceeded, err := woc.processCompletedWorkflowArtifactGCTask(&task, strategy) + allArtifactsSucceeded, err := woc.processCompletedWorkflowArtifactGCTask(ctx, &task, strategy) if err != nil { return err } if allArtifactsSucceeded && pod.Status.Phase == corev1.PodSucceeded { // now we can delete it, if it succeeded (otherwise we leave it up to be inspected) - woc.log.Debugf("deleting WorkflowArtifactGCTask: %s", task.Name) + woc.log.Debugf(ctx, "deleting WorkflowArtifactGCTask: %s", task.Name) err := woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowArtifactGCTasks(woc.wf.Namespace).Delete(ctx, task.Name, metav1.DeleteOptions{}) if err != nil { - woc.log.Errorf("error deleting WorkflowArtifactGCTask: %s: %v", task.Name, err) + woc.log.Errorf(ctx, "error deleting WorkflowArtifactGCTask: %s: %v", task.Name, err) } } @@ -634,15 +634,15 @@ func (woc *wfOperationCtx) processCompletedArtifactGCPod(ctx context.Context, po // process the Status in the WorkflowArtifactGCTask which was completed and reflect it in Workflow Status; then delete the Task CRD Object // return true if all artifacts succeeded, else false -func (woc *wfOperationCtx) processCompletedWorkflowArtifactGCTask(artifactGCTask *wfv1.WorkflowArtifactGCTask, strategy wfv1.ArtifactGCStrategy) (bool, error) { - woc.log.Debugf("processing WorkflowArtifactGCTask %s", artifactGCTask.Name) +func (woc *wfOperationCtx) processCompletedWorkflowArtifactGCTask(ctx context.Context, artifactGCTask *wfv1.WorkflowArtifactGCTask, strategy wfv1.ArtifactGCStrategy) (bool, error) { + woc.log.Debugf(ctx, "processing WorkflowArtifactGCTask %s", artifactGCTask.Name) foundGCFailure := false for nodeName, nodeResult := range artifactGCTask.Status.ArtifactResultsByNode { // find this node result in the Workflow Status wfNode, err := woc.wf.Status.Nodes.Get(nodeName) if err != nil { - woc.log.Errorf("Was unable to obtain node for %s", nodeName) + woc.log.Errorf(ctx, "Was unable to obtain node for %s", nodeName) return false, fmt.Errorf("node named %q returned by WorkflowArtifactGCTask %q wasn't found in Workflow %q Status", nodeName, artifactGCTask.Name, woc.wf.Name) } if wfNode.Outputs == nil { diff --git a/workflow/controller/artifact_gc_test.go b/workflow/controller/artifact_gc_test.go index 7adc52187abf..7bca2c638162 100644 --- a/workflow/controller/artifact_gc_test.go +++ b/workflow/controller/artifact_gc_test.go @@ -345,7 +345,7 @@ func TestProcessArtifactGCStrategy(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.wf.Status.ArtifactGCStatus = &wfv1.ArtGCStatus{} err := woc.processArtifactGCStrategy(ctx, wfv1.ArtifactGCOnWorkflowCompletion) @@ -564,14 +564,15 @@ func TestProcessCompletedWorkflowArtifactGCTask(t *testing.T) { cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.wf.Status.ArtifactGCStatus = &wfv1.ArtGCStatus{} // verify that we update these Status fields: // - Artifact.Deleted // - Conditions - _, err := woc.processCompletedWorkflowArtifactGCTask(wfat, "OnWorkflowCompletion") + _, err := woc.processCompletedWorkflowArtifactGCTask(ctx, wfat, "OnWorkflowCompletion") require.NoError(t, err) for _, expectedArtifact := range []struct { @@ -712,7 +713,8 @@ func TestWorkflowHasArtifactGC(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(workflowSpec) cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) hasArtifact := woc.HasArtifactGC() diff --git a/workflow/controller/container_set_template.go b/workflow/controller/container_set_template.go index 6905c82452f0..d7b9efd62231 100644 --- a/workflow/controller/container_set_template.go +++ b/workflow/controller/container_set_template.go @@ -10,9 +10,9 @@ import ( func (woc *wfOperationCtx) executeContainerSet(ctx context.Context, nodeName string, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) (*wfv1.NodeStatus, error) { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - node = woc.initializeExecutableNode(nodeName, wfv1.NodeTypePod, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) + node = woc.initializeExecutableNode(ctx, nodeName, wfv1.NodeTypePod, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) } - includeScriptOutput, err := woc.includeScriptOutput(nodeName, opts.boundaryID) + includeScriptOutput, err := woc.includeScriptOutput(ctx, nodeName, opts.boundaryID) if err != nil { return node, err } @@ -23,7 +23,7 @@ func (woc *wfOperationCtx) executeContainerSet(ctx context.Context, nodeName str executionDeadline: opts.executionDeadline, }) if err != nil { - return woc.requeueIfTransientErr(err, node.Name) + return woc.requeueIfTransientErr(ctx, err, node.Name) } // we only complete the graph if we actually managed to create the pod, @@ -32,16 +32,16 @@ func (woc *wfOperationCtx) executeContainerSet(ctx context.Context, nodeName str ctxNodeName := fmt.Sprintf("%s.%s", nodeName, c.Name) _, err := woc.wf.GetNodeByName(ctxNodeName) if err != nil { - _ = woc.initializeNode(ctxNodeName, wfv1.NodeTypeContainer, templateScope, orgTmpl, node.ID, wfv1.NodePending, opts.nodeFlag) + _ = woc.initializeNode(ctx, ctxNodeName, wfv1.NodeTypeContainer, templateScope, orgTmpl, node.ID, wfv1.NodePending, opts.nodeFlag) } } for _, c := range tmpl.ContainerSet.GetGraph() { ctrNodeName := fmt.Sprintf("%s.%s", nodeName, c.Name) if len(c.Dependencies) == 0 { - woc.addChildNode(nodeName, ctrNodeName) + woc.addChildNode(ctx, nodeName, ctrNodeName) } for _, v := range c.Dependencies { - woc.addChildNode(fmt.Sprintf("%s.%s", nodeName, v), ctrNodeName) + woc.addChildNode(ctx, fmt.Sprintf("%s.%s", nodeName, v), ctrNodeName) } } diff --git a/workflow/controller/container_set_template_test.go b/workflow/controller/container_set_template_test.go index c83dbdcb1a01..7504efdd957f 100644 --- a/workflow/controller/container_set_template_test.go +++ b/workflow/controller/container_set_template_test.go @@ -33,9 +33,9 @@ spec: `) cancel, controller := newController(wf) defer cancel() - - woc := newWorkflowOperationCtx(wf, controller) - woc.operate(context.Background()) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) + woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) assert.Len(t, woc.wf.Status.Nodes, 2) @@ -101,9 +101,9 @@ spec: `) cancel, controller := newController(wf) defer cancel() - - woc := newWorkflowOperationCtx(wf, controller) - woc.operate(context.Background()) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) + woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) assert.Len(t, woc.wf.Status.Nodes, 2) @@ -180,9 +180,9 @@ spec: `) cancel, controller := newController(wf) defer cancel() - - woc := newWorkflowOperationCtx(wf, controller) - woc.operate(context.Background()) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) + woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) assert.Len(t, woc.wf.Status.Nodes, 2) diff --git a/workflow/controller/controller.go b/workflow/controller/controller.go index d396115a582a..9d5a2c778b12 100644 --- a/workflow/controller/controller.go +++ b/workflow/controller/controller.go @@ -852,7 +852,7 @@ func (wfc *WorkflowController) processNextItem(ctx context.Context) bool { wf, err := util.FromUnstructured(un) if err != nil { log.WithFields(log.Fields{"key": key, "error": err}).Warn("Failed to unmarshal key to workflow object") - woc := newWorkflowOperationCtx(wf, wfc) + woc := newWorkflowOperationCtx(ctx, wf, wfc) woc.markWorkflowFailed(ctx, fmt.Sprintf("cannot unmarshall spec: %s", err.Error())) woc.persistUpdates(ctx) return true @@ -866,7 +866,7 @@ func (wfc *WorkflowController) processNextItem(ctx context.Context) bool { // this will ensure we process every incomplete workflow once every 20m wfc.wfQueue.AddAfter(key, workflowResyncPeriod) - woc := newWorkflowOperationCtx(wf, wfc) + woc := newWorkflowOperationCtx(ctx, wf, wfc) if !(woc.GetShutdownStrategy().Enabled() && woc.GetShutdownStrategy() == wfv1.ShutdownStrategyTerminate) && !wfc.throttler.Admit(key) { log.WithField("key", key).Info("Workflow processing has been postponed due to max parallelism limit") @@ -887,7 +887,7 @@ func (wfc *WorkflowController) processNextItem(ctx context.Context) bool { err = wfc.hydrator.Hydrate(woc.wf) if err != nil { - woc.log.Errorf("hydration failed: %v", err) + woc.log.Errorf(ctx, "hydration failed: %v", err) woc.markWorkflowError(ctx, err) woc.persistUpdates(ctx) return true diff --git a/workflow/controller/controller_test.go b/workflow/controller/controller_test.go index d9756765b858..26329d2deef9 100644 --- a/workflow/controller/controller_test.go +++ b/workflow/controller/controller_test.go @@ -802,8 +802,9 @@ func TestCheckAndInitWorkflowTmplRef(t *testing.T) { wftmpl := wfv1.MustUnmarshalWorkflowTemplate(wfTmpl) cancel, controller := newController(wf, wftmpl) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) - err := woc.setExecWorkflow(context.Background()) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) + err := woc.setExecWorkflow(ctx) require.NoError(t, err) assert.Equal(t, wftmpl.Spec.Templates, woc.execWf.Spec.Templates) } @@ -853,15 +854,16 @@ func TestInvalidWorkflowMetadata(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(wfWithInvalidMetadataLabelsFrom) cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) - err := woc.setExecWorkflow(context.Background()) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) + err := woc.setExecWorkflow(ctx) require.ErrorContains(t, err, "invalid label value") wf = wfv1.MustUnmarshalWorkflow(wfWithInvalidMetadataLabels) cancel, controller = newController(wf) defer cancel() - woc = newWorkflowOperationCtx(wf, controller) - err = woc.setExecWorkflow(context.Background()) + woc = newWorkflowOperationCtx(ctx, wf, controller) + err = woc.setExecWorkflow(ctx) require.ErrorContains(t, err, "invalid label value") } @@ -1146,7 +1148,7 @@ spec: ctx := context.Background() assert.True(t, controller.processNextItem(ctx)) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) makePodsPhase(ctx, woc, apiv1.PodSucceeded) @@ -1176,7 +1178,7 @@ spec: ctx := context.Background() assert.True(t, controller.processNextItem(ctx)) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) makePodsPhase(ctx, woc, apiv1.PodPending) @@ -1247,7 +1249,7 @@ func TestPendingPodWhenTerminate(t *testing.T) { ctx := context.Background() assert.True(t, controller.processNextItem(ctx)) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) for _, node := range woc.wf.Status.Nodes { @@ -1263,7 +1265,7 @@ func TestWorkflowReferItselfFromExpression(t *testing.T) { ctx := context.Background() assert.True(t, controller.processNextItem(ctx)) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) makePodsPhase(ctx, woc, apiv1.PodSucceeded) @@ -1281,7 +1283,7 @@ func TestWorkflowWithLongArguments(t *testing.T) { ctx := context.Background() assert.True(t, controller.processNextItem(ctx)) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) diff --git a/workflow/controller/dag.go b/workflow/controller/dag.go index 8f708eb02d05..e4fc60d40dc5 100644 --- a/workflow/controller/dag.go +++ b/workflow/controller/dag.go @@ -8,11 +8,10 @@ import ( "strings" "time" - log "github.com/sirupsen/logrus" - "github.com/argoproj/argo-workflows/v3/errors" wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" "github.com/argoproj/argo-workflows/v3/util/expr/argoexpr" + "github.com/argoproj/argo-workflows/v3/util/logging" "github.com/argoproj/argo-workflows/v3/util/template" "github.com/argoproj/argo-workflows/v3/workflow/common" controllercache "github.com/argoproj/argo-workflows/v3/workflow/controller/cache" @@ -56,18 +55,21 @@ type dagContext struct { // Because this resolved "depends" is computed using regex and regex is expensive, we cache the results so that they // are only computed once per operation dependsLogic map[string]string + + // used for logging in the dag + log logging.Logger } -func (d *dagContext) GetTaskDependencies(taskName string) []string { +func (d *dagContext) GetTaskDependencies(ctx context.Context, taskName string) []string { if dependencies, ok := d.dependencies[taskName]; ok { return dependencies } - d.resolveDependencies(taskName) + d.resolveDependencies(ctx, taskName) return d.dependencies[taskName] } -func (d *dagContext) GetTaskFinishedAtTime(taskName string) time.Time { - node := d.getTaskNode(taskName) +func (d *dagContext) GetTaskFinishedAtTime(ctx context.Context, taskName string) time.Time { + node := d.getTaskNode(ctx, taskName) if node == nil { return time.Time{} } @@ -77,7 +79,7 @@ func (d *dagContext) GetTaskFinishedAtTime(taskName string) time.Time { return node.StartedAt.Time } -func (d *dagContext) GetTask(taskName string) *wfv1.DAGTask { +func (d *dagContext) GetTask(ctx context.Context, taskName string) *wfv1.DAGTask { for _, task := range d.tasks { if task.Name == taskName { return &task @@ -86,16 +88,16 @@ func (d *dagContext) GetTask(taskName string) *wfv1.DAGTask { panic("target " + taskName + " does not exist") } -func (d *dagContext) GetTaskDependsLogic(taskName string) string { +func (d *dagContext) GetTaskDependsLogic(ctx context.Context, taskName string) string { if logic, ok := d.dependsLogic[taskName]; ok { return logic } - d.resolveDependencies(taskName) + d.resolveDependencies(ctx, taskName) return d.dependsLogic[taskName] } -func (d *dagContext) resolveDependencies(taskName string) { - dependencies, resolvedDependsLogic := common.GetTaskDependencies(d.GetTask(taskName), d) +func (d *dagContext) resolveDependencies(ctx context.Context, taskName string) { + dependencies, resolvedDependsLogic := common.GetTaskDependencies(ctx, d.GetTask(ctx, taskName), d) var dependencyTasks []string for dep := range dependencies { dependencyTasks = append(dependencyTasks, dep) @@ -117,18 +119,18 @@ func (d *dagContext) taskNodeID(taskName string) string { } // getTaskNode returns the node status of a task. -func (d *dagContext) getTaskNode(taskName string) *wfv1.NodeStatus { +func (d *dagContext) getTaskNode(ctx context.Context, taskName string) *wfv1.NodeStatus { nodeID := d.taskNodeID(taskName) node, err := d.wf.Status.Nodes.Get(nodeID) if err != nil { - log.Warnf("was unable to obtain the node for %s, taskName %s", nodeID, taskName) + d.log.Warnf(ctx, "was unable to obtain the node for %s, taskName %s", nodeID, taskName) return nil } return node } // assessDAGPhase assesses the overall DAG status -func (d *dagContext) assessDAGPhase(targetTasks []string, nodes wfv1.Nodes, isShutdown bool) (wfv1.NodePhase, error) { +func (d *dagContext) assessDAGPhase(ctx context.Context, targetTasks []string, nodes wfv1.Nodes, isShutdown bool) (wfv1.NodePhase, error) { // We cannot only rely on the DAG traversal. Conditionals, self-references, // and ContinuesOn (every one of those features in unison) make this an undecidable problem. // However, we can just use isShutdown to automatically fail the DAG. @@ -208,7 +210,7 @@ func (d *dagContext) assessDAGPhase(targetTasks []string, nodes wfv1.Nodes, isSh // For non-leaf tasks, this is done by setting all of its dependents to allow for their failure or error in // their "depends" clause during their respective "dependencies" to "depends" conversion. See "expandDependency" // in ancestry.go - if task := d.GetTask(depName); task.ContinuesOn(branchPhase) { + if task := d.GetTask(ctx, depName); task.ContinuesOn(branchPhase) { continue } @@ -227,7 +229,7 @@ func (woc *wfOperationCtx) executeDAG(ctx context.Context, nodeName string, tmpl node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - node = woc.initializeExecutableNode(nodeName, wfv1.NodeTypeDAG, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodeRunning, opts.nodeFlag) + node = woc.initializeExecutableNode(ctx, nodeName, wfv1.NodeTypeDAG, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodeRunning, opts.nodeFlag) } defer func() { @@ -237,7 +239,7 @@ func (woc *wfOperationCtx) executeDAG(ctx context.Context, nodeName string, tmpl panic(fmt.Sprintf("expected node for %s due to preceded initializeExecutableNode but couldn't find it", node.ID)) } if node.Fulfilled() { - woc.killDaemonedChildren(node.ID) + woc.killDaemonedChildren(ctx, node.ID) } }() @@ -252,13 +254,14 @@ func (woc *wfOperationCtx) executeDAG(ctx context.Context, nodeName string, tmpl onExitTemplate: opts.onExitTemplate, dependencies: make(map[string][]string), dependsLogic: make(map[string]string), + log: woc.log, } // Identify our target tasks. If user did not specify any, then we choose all tasks which have // no dependants. var targetTasks []string if tmpl.DAG.Target == "" { - targetTasks = dagCtx.findLeafTaskNames(tmpl.DAG.Tasks) + targetTasks = dagCtx.findLeafTaskNames(ctx, tmpl.DAG.Tasks) } else { targetTasks = strings.Split(tmpl.DAG.Target, " ") } @@ -271,24 +274,24 @@ func (woc *wfOperationCtx) executeDAG(ctx context.Context, nodeName string, tmpl // It is possible that target tasks are not reconsidered (i.e. executeDAGTask is not called on them) once they are // complete (since the DAG itself will have succeeded). To ensure that their exit handlers are run we also run them here. Note that // calls to runOnExitNode are idempotent: it is fine if they are called more than once for the same task. - taskNode := dagCtx.getTaskNode(taskName) + taskNode := dagCtx.getTaskNode(ctx, taskName) if taskNode != nil { - task := dagCtx.GetTask(taskName) - scope, err := woc.buildLocalScopeFromTask(dagCtx, task) + task := dagCtx.GetTask(ctx, taskName) + scope, err := woc.buildLocalScopeFromTask(ctx, dagCtx, task) if err != nil { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) return node, err } scope.addParamToScope(fmt.Sprintf("tasks.%s.status", task.Name), string(taskNode.Phase)) - _, err = woc.executeTmplLifeCycleHook(ctx, scope, dagCtx.GetTask(taskName).Hooks, taskNode, dagCtx.boundaryID, dagCtx.tmplCtx, "tasks."+taskName) + _, err = woc.executeTmplLifeCycleHook(ctx, scope, dagCtx.GetTask(ctx, taskName).Hooks, taskNode, dagCtx.boundaryID, dagCtx.tmplCtx, "tasks."+taskName) if err != nil { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) return node, err } if taskNode.Fulfilled() { if taskNode.Completed() { - hasOnExitNode, onExitNode, err := woc.runOnExitNode(ctx, dagCtx.GetTask(taskName).GetExitHook(woc.execWf.Spec.Arguments), taskNode, dagCtx.boundaryID, dagCtx.tmplCtx, "tasks."+taskName, scope) + hasOnExitNode, onExitNode, err := woc.runOnExitNode(ctx, dagCtx.GetTask(ctx, taskName).GetExitHook(woc.execWf.Spec.Arguments), taskNode, dagCtx.boundaryID, dagCtx.tmplCtx, "tasks."+taskName, scope) if err != nil { return node, err } @@ -302,7 +305,7 @@ func (woc *wfOperationCtx) executeDAG(ctx context.Context, nodeName string, tmpl // Check if we are still running any tasks in this dag and return early if we do // We should wait for onExit nodes even if ShutdownStrategy is enabled. - dagPhase, err := dagCtx.assessDAGPhase(targetTasks, woc.wf.Status.Nodes, woc.GetShutdownStrategy().Enabled() && onExitCompleted) + dagPhase, err := dagCtx.assessDAGPhase(ctx, targetTasks, woc.wf.Status.Nodes, woc.GetShutdownStrategy().Enabled() && onExitCompleted) if err != nil { return nil, err } @@ -311,18 +314,18 @@ func (woc *wfOperationCtx) executeDAG(ctx context.Context, nodeName string, tmpl case wfv1.NodeRunning: return node, nil case wfv1.NodeError, wfv1.NodeFailed: - err = woc.updateOutboundNodesForTargetTasks(dagCtx, targetTasks, nodeName) + err = woc.updateOutboundNodesForTargetTasks(ctx, dagCtx, targetTasks, nodeName) if err != nil { return nil, err } - _ = woc.markNodePhase(nodeName, dagPhase) + _ = woc.markNodePhase(ctx, nodeName, dagPhase) return node, nil } // set outputs from tasks in order for DAG templates to support outputs scope := createScope(tmpl) for _, task := range tmpl.DAG.Tasks { - taskNode := dagCtx.getTaskNode(task.Name) + taskNode := dagCtx.getTaskNode(ctx, task.Name) if taskNode == nil { // Can happen when dag.target was specified continue @@ -334,29 +337,29 @@ func (woc *wfOperationCtx) executeDAG(ctx context.Context, nodeName string, tmpl for i, childID := range taskNode.Children { childNode, err := woc.wf.Status.Nodes.Get(childID) if err != nil { - woc.log.Errorf("was unable to obtain node for %s", childID) + woc.log.Errorf(ctx, "was unable to obtain node for %s", childID) return nil, fmt.Errorf("Critical error, unable to find %s", childID) } childNodes[i] = *childNode } err := woc.processAggregateNodeOutputs(scope, prefix, childNodes) if err != nil { - woc.log.Errorf("unable to processAggregateNodeOutputs") + woc.log.Errorf(ctx, "unable to processAggregateNodeOutputs") return nil, errors.InternalWrapError(err) } } woc.buildLocalScope(scope, prefix, taskNode) - woc.addOutputsToGlobalScope(taskNode.Outputs) + woc.addOutputsToGlobalScope(ctx, taskNode.Outputs) } outputs, err := getTemplateOutputsFromScope(tmpl, scope) if err != nil { - woc.log.Errorf("unable to get outputs") + woc.log.Errorf(ctx, "unable to get outputs") return node, err } if outputs != nil { node, err = woc.wf.GetNodeByName(nodeName) if err != nil { - woc.log.Errorf("unable to get node by name for %s", nodeName) + woc.log.Errorf(ctx, "unable to get node by name for %s", nodeName) return nil, err } node.Outputs = outputs @@ -366,38 +369,38 @@ func (woc *wfOperationCtx) executeDAG(ctx context.Context, nodeName string, tmpl c := woc.controller.cacheFactory.GetCache(controllercache.ConfigMapCache, node.MemoizationStatus.CacheName) err := c.Save(ctx, node.MemoizationStatus.Key, node.ID, node.Outputs) if err != nil { - woc.log.WithFields(log.Fields{"nodeID": node.ID}).WithError(err).Error("Failed to save node outputs to cache") + woc.log.WithFields(ctx, logging.Fields{"nodeID": node.ID}).WithError(ctx, err).Errorf(ctx, "Failed to save node outputs to cache") node.Phase = wfv1.NodeError } } - err = woc.updateOutboundNodesForTargetTasks(dagCtx, targetTasks, nodeName) + err = woc.updateOutboundNodesForTargetTasks(ctx, dagCtx, targetTasks, nodeName) if err != nil { return nil, err } - return woc.markNodePhase(nodeName, wfv1.NodeSucceeded), nil + return woc.markNodePhase(ctx, nodeName, wfv1.NodeSucceeded), nil } -func (woc *wfOperationCtx) updateOutboundNodesForTargetTasks(dagCtx *dagContext, targetTasks []string, nodeName string) error { +func (woc *wfOperationCtx) updateOutboundNodesForTargetTasks(ctx context.Context, dagCtx *dagContext, targetTasks []string, nodeName string) error { // set the outbound nodes from the target tasks outbound := make([]string, 0) for _, depName := range targetTasks { - depNode := dagCtx.getTaskNode(depName) + depNode := dagCtx.getTaskNode(ctx, depName) if depNode == nil { - woc.log.Println(depName) + woc.log.Println(ctx, depName) continue } - outboundNodeIDs := woc.getOutboundNodes(depNode.ID) + outboundNodeIDs := woc.getOutboundNodes(ctx, depNode.ID) outbound = append(outbound, outboundNodeIDs...) } node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - woc.log.Warnf("was unable to obtain node by name for %s", nodeName) + woc.log.Warnf(ctx, "was unable to obtain node by name for %s", nodeName) return err } node.OutboundNodes = outbound woc.wf.Status.Nodes.Set(node.ID, *node) - woc.log.Infof("Outbound nodes of %s set to %s", node.ID, outbound) + woc.log.Infof(ctx, "Outbound nodes of %s set to %s", node.ID, outbound) return nil } @@ -408,20 +411,20 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex } dagCtx.visited[taskName] = true - node := dagCtx.getTaskNode(taskName) - task := dagCtx.GetTask(taskName) - log := woc.log.WithField("taskName", taskName) + node := dagCtx.getTaskNode(ctx, taskName) + task := dagCtx.GetTask(ctx, taskName) + log := woc.log.WithField(ctx, "taskName", taskName) if node != nil && (node.Fulfilled() || node.Phase == wfv1.NodeRunning) { - scope, err := woc.buildLocalScopeFromTask(dagCtx, task) + scope, err := woc.buildLocalScopeFromTask(ctx, dagCtx, task) if err != nil { - log.Error("Failed to build local scope from task") - woc.markNodeError(node.Name, err) + log.Errorf(ctx, "Failed to build local scope from task") + woc.markNodeError(ctx, node.Name, err) return } scope.addParamToScope(fmt.Sprintf("tasks.%s.status", task.Name), string(node.Phase)) - hookCompleted, err := woc.executeTmplLifeCycleHook(ctx, scope, dagCtx.GetTask(taskName).Hooks, node, dagCtx.boundaryID, dagCtx.tmplCtx, "tasks."+taskName) + hookCompleted, err := woc.executeTmplLifeCycleHook(ctx, scope, dagCtx.GetTask(ctx, taskName).Hooks, node, dagCtx.boundaryID, dagCtx.tmplCtx, "tasks."+taskName) if err != nil { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) } // Check all hooks are completes if !hookCompleted { @@ -431,13 +434,13 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex if node != nil && node.Fulfilled() { // Collect the completed task metrics - _, tmpl, _, tmplErr := dagCtx.tmplCtx.ResolveTemplate(task) + _, tmpl, _, tmplErr := dagCtx.tmplCtx.ResolveTemplate(ctx, task) if tmplErr != nil { - woc.markNodeError(node.Name, tmplErr) + woc.markNodeError(ctx, node.Name, tmplErr) return } if err := woc.mergedTemplateDefaultsInto(tmpl); err != nil { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) return } if tmpl != nil && tmpl.Metrics != nil { @@ -449,7 +452,7 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex processedTmpl, err := common.ProcessArgs(tmpl, &task.Arguments, woc.globalParams, map[string]string{}, true, woc.wf.Namespace, woc.controller.configMapInformer.GetIndexer()) if err != nil { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) } // Release acquired lock completed task. @@ -457,10 +460,10 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex woc.controller.syncManager.Release(ctx, woc.wf, node.ID, processedTmpl.Synchronization) } - scope, err := woc.buildLocalScopeFromTask(dagCtx, task) + scope, err := woc.buildLocalScopeFromTask(ctx, dagCtx, task) if err != nil { - woc.markNodeError(node.Name, err) - log.Error("Failed to build local scope from task") + woc.markNodeError(ctx, node.Name, err) + log.Errorf(ctx, "Failed to build local scope from task") return } scope.addParamToScope(fmt.Sprintf("tasks.%s.status", task.Name), string(node.Phase)) @@ -481,7 +484,7 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex // Check if our dependencies completed. If not, recurse our parents executing them if necessary nodeName := dagCtx.taskNodeName(taskName) - taskDependencies := dagCtx.GetTaskDependencies(taskName) + taskDependencies := dagCtx.GetTaskDependencies(ctx, taskName) // error condition taken care of via a nil check taskGroupNode, _ := woc.wf.GetNodeByName(nodeName) @@ -494,35 +497,35 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex // if we had no dependencies, then we are a root task, and we should connect the // boundary node as our parent if taskGroupNode == nil { - woc.addChildNode(dagCtx.boundaryName, taskNodeName) + woc.addChildNode(ctx, dagCtx.boundaryName, taskNodeName) } else { - woc.addChildNode(taskGroupNode.Name, taskNodeName) + woc.addChildNode(ctx, taskGroupNode.Name, taskNodeName) } } else { // Otherwise, add all outbound nodes of our dependencies as parents to this node for _, depName := range taskDependencies { - depNode := dagCtx.getTaskNode(depName) - outboundNodeIDs := woc.getOutboundNodes(depNode.ID) + depNode := dagCtx.getTaskNode(ctx, depName) + outboundNodeIDs := woc.getOutboundNodes(ctx, depNode.ID) for _, outNodeID := range outboundNodeIDs { nodeName, err := woc.wf.Status.Nodes.GetName(outNodeID) if err != nil { - woc.log.Errorf("was unable to obtain node for %s", outNodeID) + woc.log.Errorf(ctx, "was unable to obtain node for %s", outNodeID) return } - woc.addChildNode(nodeName, taskNodeName) + woc.addChildNode(ctx, nodeName, taskNodeName) } } } } - if dagCtx.GetTaskDependsLogic(taskName) != "" { + if dagCtx.GetTaskDependsLogic(ctx, taskName) != "" { // Recurse into all of this node's dependencies for _, dep := range taskDependencies { woc.executeDAGTask(ctx, dagCtx, dep) } - execute, proceed, err := dagCtx.evaluateDependsLogic(taskName) + execute, proceed, err := dagCtx.evaluateDependsLogic(ctx, taskName) if err != nil { - woc.initializeNode(nodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, err.Error()) + woc.initializeNode(ctx, nodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, err.Error()) connectDependencies(nodeName) return } @@ -532,7 +535,7 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex } if !execute { // Given the results of this node's dependencies, this node should not be executed. Mark it omitted - woc.initializeNode(nodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeOmitted, &wfv1.NodeFlag{}, "omitted: depends condition not met") + woc.initializeNode(ctx, nodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeOmitted, &wfv1.NodeFlag{}, "omitted: depends condition not met") connectDependencies(nodeName) return } @@ -540,9 +543,9 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex // All our dependencies were satisfied and successful. It's our turn to run // First resolve/substitute params/artifacts from our dependencies - newTask, err := woc.resolveDependencyReferences(dagCtx, task) + newTask, err := woc.resolveDependencyReferences(ctx, dagCtx, task) if err != nil { - woc.initializeNode(nodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, err.Error()) + woc.initializeNode(ctx, nodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, err.Error()) connectDependencies(nodeName) return } @@ -551,7 +554,7 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex // expandedTasks will be a single element list of the same task expandedTasks, err := expandTask(*newTask) if err != nil { - woc.initializeNode(nodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, err.Error()) + woc.initializeNode(ctx, nodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, err.Error()) connectDependencies(nodeName) return } @@ -563,31 +566,31 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex // DAG task with empty withParams list should be skipped if len(expandedTasks) == 0 { skipReason := "Skipped, empty params" - woc.initializeNode(nodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeSkipped, &wfv1.NodeFlag{}, skipReason) + woc.initializeNode(ctx, nodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeSkipped, &wfv1.NodeFlag{}, skipReason) connectDependencies(nodeName) } else if taskGroupNode == nil { connectDependencies(nodeName) - taskGroupNode = woc.initializeNode(nodeName, wfv1.NodeTypeTaskGroup, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeRunning, &wfv1.NodeFlag{}, "") + taskGroupNode = woc.initializeNode(ctx, nodeName, wfv1.NodeTypeTaskGroup, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeRunning, &wfv1.NodeFlag{}, "") } } for _, t := range expandedTasks { taskNodeName := dagCtx.taskNodeName(t.Name) - node = dagCtx.getTaskNode(t.Name) + node = dagCtx.getTaskNode(ctx, t.Name) if node == nil { - woc.log.Infof("All of node %s dependencies %v completed", taskNodeName, taskDependencies) + woc.log.Infof(ctx, "All of node %s dependencies %v completed", taskNodeName, taskDependencies) // Add the child relationship from our dependency's outbound nodes to this node. connectDependencies(taskNodeName) // Check the task's when clause to decide if it should execute proceed, err := shouldExecute(t.When) if err != nil { - woc.initializeNode(taskNodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, err.Error()) + woc.initializeNode(ctx, taskNodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, err.Error()) continue } if !proceed { skipReason := fmt.Sprintf("when '%s' evaluated false", t.When) - woc.initializeNode(taskNodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeSkipped, &wfv1.NodeFlag{}, skipReason) + woc.initializeNode(ctx, taskNodeName, wfv1.NodeTypeSkipped, dagTemplateScope, task, dagCtx.boundaryID, wfv1.NodeSkipped, &wfv1.NodeFlag{}, skipReason) continue } } @@ -601,10 +604,10 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex case ErrParallelismReached: case ErrMaxDepthExceeded: case ErrTimeout: - _ = woc.markNodePhase(taskNodeName, wfv1.NodeFailed, err.Error()) + _ = woc.markNodePhase(ctx, taskNodeName, wfv1.NodeFailed, err.Error()) return default: - _ = woc.markNodeError(taskNodeName, fmt.Errorf("task '%s' errored: %v", taskNodeName, err)) + _ = woc.markNodeError(ctx, taskNodeName, fmt.Errorf("task '%s' errored: %v", taskNodeName, err)) return } } @@ -613,9 +616,9 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex return } if node.Completed() { - scope, err := woc.buildLocalScopeFromTask(dagCtx, task) + scope, err := woc.buildLocalScopeFromTask(ctx, dagCtx, task) if err != nil { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) } scope.addParamToScope(fmt.Sprintf("tasks.%s.status", task.Name), string(node.Phase)) // if the node type is NodeTypeRetry, and its last child is completed, it will be completed after woc.executeTemplate; @@ -631,7 +634,7 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex groupPhase := wfv1.NodeSucceeded for _, t := range expandedTasks { // Add the child relationship from our dependency's outbound nodes to this node. - node := dagCtx.getTaskNode(t.Name) + node := dagCtx.getTaskNode(ctx, t.Name) if node == nil || !node.Fulfilled() { return } @@ -639,18 +642,18 @@ func (woc *wfOperationCtx) executeDAGTask(ctx context.Context, dagCtx *dagContex groupPhase = node.Phase } } - woc.markNodePhase(taskGroupNode.Name, groupPhase) + woc.markNodePhase(ctx, taskGroupNode.Name, groupPhase) } } -func (woc *wfOperationCtx) buildLocalScopeFromTask(dagCtx *dagContext, task *wfv1.DAGTask) (*wfScope, error) { +func (woc *wfOperationCtx) buildLocalScopeFromTask(ctx context.Context, dagCtx *dagContext, task *wfv1.DAGTask) (*wfScope, error) { // build up the scope scope := createScope(dagCtx.tmpl) woc.addOutputsToLocalScope("workflow", woc.wf.Status.Outputs, scope) - ancestors := common.GetTaskAncestry(dagCtx, task.Name) + ancestors := common.GetTaskAncestry(ctx, dagCtx, task.Name) for _, ancestor := range ancestors { - ancestorNode := dagCtx.getTaskNode(ancestor) + ancestorNode := dagCtx.getTaskNode(ctx, ancestor) if ancestorNode == nil { return nil, errors.InternalErrorf("Ancestor task node %s not found", ancestor) } @@ -662,7 +665,7 @@ func (woc *wfOperationCtx) buildLocalScopeFromTask(dagCtx *dagContext, task *wfv ancestorNodes = append(ancestorNodes, node) } } - _, _, templateStored, err := dagCtx.tmplCtx.ResolveTemplate(ancestorNode) + _, _, templateStored, err := dagCtx.tmplCtx.ResolveTemplate(ctx, ancestorNode) if err != nil { return nil, errors.InternalWrapError(err) } @@ -684,8 +687,8 @@ func (woc *wfOperationCtx) buildLocalScopeFromTask(dagCtx *dagContext, task *wfv // resolveDependencyReferences replaces any references to outputs of task dependencies, or artifacts in the inputs // NOTE: by now, input parameters should have been substituted throughout the template -func (woc *wfOperationCtx) resolveDependencyReferences(dagCtx *dagContext, task *wfv1.DAGTask) (*wfv1.DAGTask, error) { - scope, err := woc.buildLocalScopeFromTask(dagCtx, task) +func (woc *wfOperationCtx) resolveDependencyReferences(ctx context.Context, dagCtx *dagContext, task *wfv1.DAGTask) (*wfv1.DAGTask, error) { + scope, err := woc.buildLocalScopeFromTask(ctx, dagCtx, task) if err != nil { return nil, err } @@ -738,7 +741,7 @@ func (woc *wfOperationCtx) resolveDependencyReferences(dagCtx *dagContext, task resolvedArt, err := scope.resolveArtifact(&art) if err != nil { if strings.Contains(err.Error(), "Unable to resolve") && art.Optional { - woc.log.Warnf("Optional artifact '%s' was not found; it won't be available as an input", art.Name) + woc.log.Warnf(ctx, "Optional artifact '%s' was not found; it won't be available as an input", art.Name) continue } return nil, err @@ -751,13 +754,13 @@ func (woc *wfOperationCtx) resolveDependencyReferences(dagCtx *dagContext, task // findLeafTaskNames finds the names of all tasks whom no other nodes depend on. // This list of tasks is used as the default list of targets when dag.targets is omitted. -func (d *dagContext) findLeafTaskNames(tasks []wfv1.DAGTask) []string { +func (d *dagContext) findLeafTaskNames(ctx context.Context, tasks []wfv1.DAGTask) []string { taskIsLeaf := make(map[string]bool) for _, task := range tasks { if _, ok := taskIsLeaf[task.Name]; !ok { taskIsLeaf[task.Name] = true } - for _, dependency := range d.GetTaskDependencies(task.Name) { + for _, dependency := range d.GetTaskDependencies(ctx, task.Name) { taskIsLeaf[dependency] = false } } @@ -842,18 +845,18 @@ type TaskResults struct { // evaluateDependsLogic returns whether a node should execute and proceed. proceed means that all of its dependencies are // completed and execute means that given the results of its dependencies, this node should execute. -func (d *dagContext) evaluateDependsLogic(taskName string) (bool, bool, error) { - node := d.getTaskNode(taskName) +func (d *dagContext) evaluateDependsLogic(ctx context.Context, taskName string) (bool, bool, error) { + node := d.getTaskNode(ctx, taskName) if node != nil { return true, true, nil } evalScope := make(map[string]TaskResults) - for _, taskName := range d.GetTaskDependencies(taskName) { + for _, taskName := range d.GetTaskDependencies(ctx, taskName) { // If the task is still running, we should not proceed. - depNode := d.getTaskNode(taskName) + depNode := d.getTaskNode(ctx, taskName) if depNode == nil || !depNode.Fulfilled() || !common.CheckAllHooksFullfilled(depNode, d.wf.Status.Nodes) { return false, false, nil } @@ -873,7 +876,7 @@ func (d *dagContext) evaluateDependsLogic(taskName string) (bool, bool, error) { for _, childNodeID := range depNode.Children { childNodePhase, err := d.wf.Status.Nodes.GetPhase(childNodeID) if err != nil { - log.Warnf("was unable to obtain node for %s", childNodeID) + d.log.Warnf(ctx, "was unable to obtain node for %s", childNodeID) allFailed = false // we don't know if all failed continue } @@ -894,7 +897,7 @@ func (d *dagContext) evaluateDependsLogic(taskName string) (bool, bool, error) { } } - evalLogic := strings.Replace(d.GetTaskDependsLogic(taskName), "-", "_", -1) + evalLogic := strings.Replace(d.GetTaskDependsLogic(ctx, taskName), "-", "_", -1) execute, err := argoexpr.EvalBool(evalLogic, evalScope) if err != nil { return false, false, fmt.Errorf("unable to evaluate expression '%s': %s", evalLogic, err) diff --git a/workflow/controller/dag_test.go b/workflow/controller/dag_test.go index dee2ef413f64..9a377ba2b37d 100644 --- a/workflow/controller/dag_test.go +++ b/workflow/controller/dag_test.go @@ -12,14 +12,15 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + "github.com/argoproj/argo-workflows/v3/util/logging" "github.com/argoproj/argo-workflows/v3/workflow/common" ) // TestDagXfail verifies a DAG can fail properly func TestDagXfail(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow("@testdata/dag_xfail.yaml") - woc := newWoc(*wf) ctx := context.Background() + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) } @@ -27,8 +28,8 @@ func TestDagXfail(t *testing.T) { // TestDagRetrySucceeded verifies a DAG will be marked Succeeded if retry was successful func TestDagRetrySucceeded(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow("@testdata/dag_retry_succeeded.yaml") - woc := newWoc(*wf) ctx := context.Background() + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) } @@ -36,8 +37,8 @@ func TestDagRetrySucceeded(t *testing.T) { // TestDagRetryExhaustedXfail verifies we fail properly when we exhaust our retries func TestDagRetryExhaustedXfail(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow("@testdata/dag-exhausted-retries-xfail.yaml") - woc := newWoc(*wf) ctx := context.Background() + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) } @@ -45,8 +46,8 @@ func TestDagRetryExhaustedXfail(t *testing.T) { // TestDagDisableFailFast test disable fail fast function func TestDagDisableFailFast(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow("@testdata/dag-disable-fail-fast.yaml") - woc := newWoc(*wf) ctx := context.Background() + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) } @@ -108,7 +109,7 @@ func TestSingleDependency(t *testing.T) { require.NoError(t, err) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) // Mark the status of the pod according to the test @@ -118,7 +119,7 @@ func TestSingleDependency(t *testing.T) { makePodsPhase(ctx, woc, v1.PodPending) } - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) found := false for _, node := range woc.wf.Status.Nodes { @@ -199,11 +200,11 @@ func TestArtifactResolutionWhenSkippedDAG(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(artifactResolutionWhenSkippedDAG) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) } @@ -245,6 +246,7 @@ func TestEvaluateDependsLogic(t *testing.T) { wf: &wfv1.Workflow{ObjectMeta: metav1.ObjectMeta{Name: "test-wf"}}, dependencies: make(map[string][]string), dependsLogic: make(map[string]string), + log: logging.NewSlogLogger(), } // Task A is running @@ -256,9 +258,10 @@ func TestEvaluateDependsLogic(t *testing.T) { }, }, } + ctx := context.Background() // Task B should not proceed, task A is still running - execute, proceed, err := d.evaluateDependsLogic("B") + execute, proceed, err := d.evaluateDependsLogic(ctx, "B") require.NoError(t, err) assert.False(t, proceed) assert.False(t, execute) @@ -267,16 +270,16 @@ func TestEvaluateDependsLogic(t *testing.T) { d.wf.Status.Nodes[d.taskNodeID("A")] = wfv1.NodeStatus{Phase: wfv1.NodeSucceeded} // Task B and C should proceed and execute - execute, proceed, err = d.evaluateDependsLogic("B") + execute, proceed, err = d.evaluateDependsLogic(ctx, "B") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) - execute, proceed, err = d.evaluateDependsLogic("C") + execute, proceed, err = d.evaluateDependsLogic(ctx, "C") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) // Other tasks should not - execute, proceed, err = d.evaluateDependsLogic("should-execute-1") + execute, proceed, err = d.evaluateDependsLogic(ctx, "should-execute-1") require.NoError(t, err) assert.False(t, proceed) assert.False(t, execute) @@ -286,16 +289,16 @@ func TestEvaluateDependsLogic(t *testing.T) { d.wf.Status.Nodes[d.taskNodeID("C")] = wfv1.NodeStatus{Phase: wfv1.NodeFailed} // Tasks should-execute-1 and should-execute-2 should proceed and execute - execute, proceed, err = d.evaluateDependsLogic("should-execute-1") + execute, proceed, err = d.evaluateDependsLogic(ctx, "should-execute-1") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) - execute, proceed, err = d.evaluateDependsLogic("should-execute-2") + execute, proceed, err = d.evaluateDependsLogic(ctx, "should-execute-2") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) // Task should-not-execute should proceed, but not execute - execute, proceed, err = d.evaluateDependsLogic("should-not-execute") + execute, proceed, err = d.evaluateDependsLogic(ctx, "should-not-execute") require.NoError(t, err) assert.True(t, proceed) assert.False(t, execute) @@ -306,7 +309,7 @@ func TestEvaluateDependsLogic(t *testing.T) { d.wf.Status.Nodes[d.taskNodeID("should-not-execute")] = wfv1.NodeStatus{Phase: wfv1.NodeSkipped} // Tasks should-execute-3 should proceed and execute - execute, proceed, err = d.evaluateDependsLogic("should-execute-3") + execute, proceed, err = d.evaluateDependsLogic(ctx, "should-execute-3") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) @@ -345,6 +348,7 @@ func TestEvaluateAnyAllDependsLogic(t *testing.T) { wf: &wfv1.Workflow{ObjectMeta: metav1.ObjectMeta{Name: "test-wf"}}, dependencies: make(map[string][]string), dependsLogic: make(map[string]string), + log: logging.NewSlogLogger(), } // Task A is still running, A-1 succeeded but A-2 failed @@ -362,9 +366,10 @@ func TestEvaluateAnyAllDependsLogic(t *testing.T) { }, }, } + ctx := context.Background() // Task B should not proceed as task A is still running - execute, proceed, err := d.evaluateDependsLogic("B") + execute, proceed, err := d.evaluateDependsLogic(ctx, "B") require.NoError(t, err) assert.False(t, proceed) assert.False(t, execute) @@ -377,7 +382,7 @@ func TestEvaluateAnyAllDependsLogic(t *testing.T) { } // Task B should proceed, but not execute as none of the children have succeeded yet - execute, proceed, err = d.evaluateDependsLogic("B") + execute, proceed, err = d.evaluateDependsLogic(ctx, "B") require.NoError(t, err) assert.True(t, proceed) assert.False(t, execute) @@ -386,7 +391,7 @@ func TestEvaluateAnyAllDependsLogic(t *testing.T) { d.wf.Status.Nodes[d.taskNodeID("A-2")] = wfv1.NodeStatus{Phase: wfv1.NodeSucceeded} // Task B should now proceed and execute - execute, proceed, err = d.evaluateDependsLogic("B") + execute, proceed, err = d.evaluateDependsLogic(ctx, "B") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) @@ -400,7 +405,7 @@ func TestEvaluateAnyAllDependsLogic(t *testing.T) { d.wf.Status.Nodes[d.taskNodeID("B-1")] = wfv1.NodeStatus{Phase: wfv1.NodeFailed} // Task C should proceed, but not execute as not all of B's children have failed yet - execute, proceed, err = d.evaluateDependsLogic("C") + execute, proceed, err = d.evaluateDependsLogic(ctx, "C") require.NoError(t, err) assert.True(t, proceed) assert.False(t, execute) @@ -408,7 +413,7 @@ func TestEvaluateAnyAllDependsLogic(t *testing.T) { d.wf.Status.Nodes[d.taskNodeID("B-2")] = wfv1.NodeStatus{Phase: wfv1.NodeFailed} // Task C should now proceed and execute as all of B's children have failed - execute, proceed, err = d.evaluateDependsLogic("C") + execute, proceed, err = d.evaluateDependsLogic(ctx, "C") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) @@ -431,6 +436,7 @@ func TestEvaluateDependsLogicWhenDaemonFailed(t *testing.T) { wf: &wfv1.Workflow{ObjectMeta: metav1.ObjectMeta{Name: "test-wf"}}, dependencies: make(map[string][]string), dependsLogic: make(map[string]string), + log: logging.NewSlogLogger(), } // Task A is running @@ -443,9 +449,10 @@ func TestEvaluateDependsLogicWhenDaemonFailed(t *testing.T) { }, }, } + ctx := context.Background() // Task B should proceed and execute - execute, proceed, err := d.evaluateDependsLogic("B") + execute, proceed, err := d.evaluateDependsLogic(ctx, "B") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) @@ -457,7 +464,7 @@ func TestEvaluateDependsLogicWhenDaemonFailed(t *testing.T) { d.wf.Status.Nodes[d.taskNodeID("A")] = wfv1.NodeStatus{Phase: wfv1.NodeFailed} // Task B should proceed and execute - execute, proceed, err = d.evaluateDependsLogic("B") + execute, proceed, err = d.evaluateDependsLogic(ctx, "B") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) @@ -480,6 +487,7 @@ func TestEvaluateDependsLogicWhenTaskOmitted(t *testing.T) { wf: &wfv1.Workflow{ObjectMeta: metav1.ObjectMeta{Name: "test-wf"}}, dependencies: make(map[string][]string), dependsLogic: make(map[string]string), + log: logging.NewSlogLogger(), } // Task A is running @@ -492,8 +500,10 @@ func TestEvaluateDependsLogicWhenTaskOmitted(t *testing.T) { }, } + ctx := context.Background() + // Task B should proceed and execute - execute, proceed, err := d.evaluateDependsLogic("B") + execute, proceed, err := d.evaluateDependsLogic(ctx, "B") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) @@ -526,6 +536,7 @@ func TestAllEvaluateDependsLogic(t *testing.T) { wf: &wfv1.Workflow{ObjectMeta: metav1.ObjectMeta{Name: "test-wf"}}, dependencies: make(map[string][]string), dependsLogic: make(map[string]string), + log: logging.NewSlogLogger(), } // Task A is running @@ -537,12 +548,13 @@ func TestAllEvaluateDependsLogic(t *testing.T) { }, }, } + ctx := context.Background() - execute, proceed, err := d.evaluateDependsLogic("Run") + execute, proceed, err := d.evaluateDependsLogic(ctx, "Run") require.NoError(t, err) assert.True(t, proceed) assert.True(t, execute) - execute, proceed, err = d.evaluateDependsLogic("NotRun") + execute, proceed, err = d.evaluateDependsLogic(ctx, "NotRun") require.NoError(t, err) assert.True(t, proceed) assert.False(t, execute) @@ -817,10 +829,10 @@ func TestDagAssessPhaseContinueOnExpandedTaskVariables(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(dagAssessPhaseContinueOnExpandedTaskVariables) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) } @@ -1040,10 +1052,10 @@ func TestDagAssessPhaseContinueOnExpandedTask(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(dagAssessPhaseContinueOnExpandedTask) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) } @@ -1089,7 +1101,7 @@ func TestDAGWithParamAndGlobalParam(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(dagWithParamAndGlobalParam) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) @@ -1327,7 +1339,7 @@ func TestTerminatingDAGWithRetryStrategyNodes(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(terminatingDAGWithRetryStrategyNodes) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) @@ -1487,7 +1499,7 @@ func TestTerminateDAGWithMaxDurationLimitExpiredAndMoreAttempts(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(terminateDAGWithMaxDurationLimitExpiredAndMoreAttempts) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -1497,7 +1509,7 @@ func TestTerminateDAGWithMaxDurationLimitExpiredAndMoreAttempts(t *testing.T) { assert.Equal(t, wfv1.NodeFailed, retryNode.Phase) assert.Contains(t, retryNode.Message, "Max duration limit exceeded") - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) // This is the crucial part of the test @@ -1676,7 +1688,7 @@ func TestRetryStrategyNodes(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(testRetryStrategyNodes) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) retryNode, err := woc.wf.GetNodeByName("wf-retry-pol") @@ -1842,7 +1854,7 @@ func TestOnExitDAGPhase(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(testOnExitNodeDAGPhase) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) retryNode, err := woc.wf.GetNodeByName("dag-diamond-88trp") @@ -1973,7 +1985,7 @@ func TestOnExitNonLeaf(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(testOnExitNonLeaf) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) retryNode, err := woc.wf.GetNodeByName("exit-handler-bug-example.step-2.onExit") @@ -1987,7 +1999,7 @@ func TestOnExitNonLeaf(t *testing.T) { retryNode.Phase = wfv1.NodeSucceeded woc.wf.Status.Nodes[retryNode.ID] = *retryNode - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) retryNode, err = woc.wf.GetNodeByName("exit-handler-bug-example.step-3") require.NoError(t, err) @@ -2086,8 +2098,8 @@ func TestDagOptionalInputArtifacts(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(testDagOptionalInputArtifacts) cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) optionalInputArtifactsNode, err := woc.wf.GetNodeByName("dag-optional-inputartifacts.B") @@ -2243,7 +2255,7 @@ func TestDagTargetTaskOnExit(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(testDagTargetTaskOnExit) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) onExitNode, err := woc.wf.GetNodeByName("dag-primay-branch-6bnnl.A.onExit") @@ -2396,7 +2408,7 @@ func TestEmptyWithParamDAG(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(testEmptyWithParamDAG) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) @@ -3073,7 +3085,7 @@ func TestFailsWithParamDAG(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(testFailsWithParamDAG) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) @@ -3197,9 +3209,9 @@ func TestLeafContinueOn(t *testing.T) { cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) - ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) + woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) } @@ -3361,7 +3373,7 @@ func TestDAGReferTaskAggregatedOutputs(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) dagNode := woc.wf.Status.Nodes.FindByDisplayName("parameter-aggregation-dag-h8b82") @@ -3440,7 +3452,7 @@ func TestDagHttpChildrenAssigned(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) dagNode := woc.wf.Status.Nodes.FindByDisplayName("good2") @@ -3570,7 +3582,7 @@ func TestRetryTypeDagTaskRunExitNodeAfterCompleted(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) // retryTypeDAGTask completed printAChild := woc.wf.Status.Nodes.FindByDisplayName("printA(0)") assert.Equal(t, wfv1.NodeSucceeded, printAChild.Phase) @@ -3635,18 +3647,19 @@ spec: image: docker/whalesay:latest command: [cowsay] args: ["I have a {{inputs.parameters.thing}}"]`) - woc := newWoc(*wf) + ctx := context.Background() + woc := newWoc(ctx, *wf) woc.operate(ctx) - woc1 := newWoc(*woc.wf) + woc1 := newWoc(ctx, *woc.wf) woc1.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) } func TestDagWftmplHookWithRetry(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow("@testdata/dag_wftmpl_hook_with_retry.yaml") - woc := newWoc(*wf) ctx := context.Background() + woc := newWoc(ctx, *wf) woc.operate(ctx) // assert task kicked diff --git a/workflow/controller/exec_control.go b/workflow/controller/exec_control.go index 9866aedd6bbe..b39f398b7d95 100644 --- a/workflow/controller/exec_control.go +++ b/workflow/controller/exec_control.go @@ -1,6 +1,7 @@ package controller import ( + "context" "fmt" "sync" "time" @@ -14,7 +15,7 @@ import ( // applyExecutionControl will ensure a pod's execution control annotation is up-to-date // kills any pending and running pods (except agent pod) when workflow has reached its deadline -func (woc *wfOperationCtx) applyExecutionControl(pod *apiv1.Pod, wfNodesLock *sync.RWMutex) { +func (woc *wfOperationCtx) applyExecutionControl(ctx context.Context, pod *apiv1.Pod, wfNodesLock *sync.RWMutex) { if pod == nil || woc.isAgentPod(pod) { return } @@ -24,7 +25,7 @@ func (woc *wfOperationCtx) applyExecutionControl(pod *apiv1.Pod, wfNodesLock *sy node, err := woc.wf.Status.Nodes.Get(nodeID) wfNodesLock.RUnlock() if err != nil { - woc.log.Errorf("was unable to obtain node for %s", nodeID) + woc.log.Errorf(ctx, "was unable to obtain node for %s", nodeID) return } // node is already completed @@ -42,12 +43,12 @@ func (woc *wfOperationCtx) applyExecutionControl(pod *apiv1.Pod, wfNodesLock *sy _, onExitPod := pod.Labels[common.LabelKeyOnExit] if !woc.GetShutdownStrategy().ShouldExecute(onExitPod) { - woc.log.WithField("podName", pod.Name). - WithField("shutdownStrategy", woc.GetShutdownStrategy()). - Info("Terminating pod as part of workflow shutdown") + woc.log.WithField(ctx, "podName", pod.Name). + WithField(ctx, "shutdownStrategy", woc.GetShutdownStrategy()). + Info(ctx, "Terminating pod as part of workflow shutdown") woc.controller.queuePodForCleanup(pod.Namespace, pod.Name, terminateContainers) msg := fmt.Sprintf("workflow shutdown with strategy: %s", woc.GetShutdownStrategy()) - woc.handleExecutionControlError(nodeID, wfNodesLock, msg) + woc.handleExecutionControlError(ctx, nodeID, wfNodesLock, msg) return } } @@ -57,39 +58,39 @@ func (woc *wfOperationCtx) applyExecutionControl(pod *apiv1.Pod, wfNodesLock *sy // pods that are part of an onExit handler aren't subject to the deadline _, onExitPod := pod.Labels[common.LabelKeyOnExit] if !onExitPod { - woc.log.WithField("podName", pod.Name). - WithField(" workflowDeadline", woc.workflowDeadline). - Info("Terminating pod which has exceeded workflow deadline") + woc.log.WithField(ctx, "podName", pod.Name). + WithField(ctx, " workflowDeadline", woc.workflowDeadline). + Info(ctx, "Terminating pod which has exceeded workflow deadline") woc.controller.queuePodForCleanup(pod.Namespace, pod.Name, terminateContainers) - woc.handleExecutionControlError(nodeID, wfNodesLock, "Step exceeded its deadline") + woc.handleExecutionControlError(ctx, nodeID, wfNodesLock, "Step exceeded its deadline") return } } } if woc.GetShutdownStrategy().Enabled() { if _, onExitPod := pod.Labels[common.LabelKeyOnExit]; !woc.GetShutdownStrategy().ShouldExecute(onExitPod) { - woc.log.WithField("podName", pod.Name). - Info("Terminating on-exit pod") + woc.log.WithField(ctx, "podName", pod.Name). + Info(ctx, "Terminating on-exit pod") woc.controller.queuePodForCleanup(woc.wf.Namespace, pod.Name, terminateContainers) } } } // handleExecutionControlError marks a node as failed with an error message -func (woc *wfOperationCtx) handleExecutionControlError(nodeID string, wfNodesLock *sync.RWMutex, errorMsg string) { +func (woc *wfOperationCtx) handleExecutionControlError(ctx context.Context, nodeID string, wfNodesLock *sync.RWMutex, errorMsg string) { wfNodesLock.Lock() defer wfNodesLock.Unlock() node, err := woc.wf.Status.Nodes.Get(nodeID) if err != nil { - woc.log.Errorf("was not abble to obtain node for %s", nodeID) + woc.log.Errorf(ctx, "was not abble to obtain node for %s", nodeID) return } - woc.markNodePhase(node.Name, wfv1.NodeFailed, errorMsg) + woc.markNodePhase(ctx, node.Name, wfv1.NodeFailed, errorMsg) children, err := woc.wf.Status.Nodes.NestedChildrenStatus(nodeID) if err != nil { - woc.log.Errorf("was not able to obtain children: %s", err) + woc.log.Errorf(ctx, "was not able to obtain children: %s", err) return } @@ -97,15 +98,15 @@ func (woc *wfOperationCtx) handleExecutionControlError(nodeID string, wfNodesLoc // then need to fail child nodes so they will not hang in Pending after pod deletion for _, child := range children { if !child.Fulfilled() { - woc.markNodePhase(child.Name, wfv1.NodeFailed, errorMsg) + woc.markNodePhase(ctx, child.Name, wfv1.NodeFailed, errorMsg) } } } // killDaemonedChildren kill any daemoned pods of a steps or DAG template node. -func (woc *wfOperationCtx) killDaemonedChildren(nodeID string) { +func (woc *wfOperationCtx) killDaemonedChildren(ctx context.Context, nodeID string) { if nodeID != "" { - woc.log.Debugf("Checking daemoned children of %s", nodeID) + woc.log.Debugf(ctx, "Checking daemoned children of %s", nodeID) } for _, childNode := range woc.wf.Status.Nodes { if childNode.BoundaryID != nodeID { diff --git a/workflow/controller/exec_control_test.go b/workflow/controller/exec_control_test.go index 6b3d082aa4b4..43bf5bb85453 100644 --- a/workflow/controller/exec_control_test.go +++ b/workflow/controller/exec_control_test.go @@ -1,6 +1,7 @@ package controller import ( + "context" "sync" "testing" @@ -13,8 +14,9 @@ import ( func TestKillDaemonChildrenUnmarkPod(t *testing.T) { cancel, controller := newController() defer cancel() + ctx := context.Background() - woc := newWorkflowOperationCtx(&v1alpha1.Workflow{ + woc := newWorkflowOperationCtx(ctx, &v1alpha1.Workflow{ Status: v1alpha1.WorkflowStatus{ Nodes: v1alpha1.Nodes{ "a": v1alpha1.NodeStatus{ @@ -28,7 +30,7 @@ func TestKillDaemonChildrenUnmarkPod(t *testing.T) { assert.NotNil(t, woc.wf.Status.Nodes["a"].Daemoned) // Error will be that it cannot find the pod, but we only care about the node status for this test - woc.killDaemonedChildren("a") + woc.killDaemonedChildren(ctx, "a") assert.Nil(t, woc.wf.Status.Nodes["a"].Daemoned) } @@ -135,14 +137,15 @@ func TestHandleExecutionControlErrorMarksProvidedNode(t *testing.T) { defer cancel() workflow := v1alpha1.MustUnmarshalWorkflow(workflowWithContainerSetPodInPending) + ctx := context.Background() - woc := newWorkflowOperationCtx(workflow, controller) + woc := newWorkflowOperationCtx(ctx, workflow, controller) containerSetNodeName := "container-set-termination-demopw5vv-842041608" assert.Equal(t, v1alpha1.NodePending, woc.wf.Status.Nodes[containerSetNodeName].Phase) - woc.handleExecutionControlError(containerSetNodeName, &sync.RWMutex{}, "terminated") + woc.handleExecutionControlError(ctx, containerSetNodeName, &sync.RWMutex{}, "terminated") assert.Equal(t, v1alpha1.NodeFailed, woc.wf.Status.Nodes[containerSetNodeName].Phase) } @@ -152,8 +155,9 @@ func TestHandleExecutionControlErrorMarksChildNodes(t *testing.T) { defer cancel() workflow := v1alpha1.MustUnmarshalWorkflow(workflowWithContainerSetPodInPending) + ctx := context.Background() - woc := newWorkflowOperationCtx(workflow, controller) + woc := newWorkflowOperationCtx(ctx, workflow, controller) containerSetNodeName := "container-set-termination-demopw5vv-842041608" step1NodeName := "container-set-termination-demopw5vv-893664226" @@ -162,7 +166,7 @@ func TestHandleExecutionControlErrorMarksChildNodes(t *testing.T) { assert.Equal(t, v1alpha1.NodePending, woc.wf.Status.Nodes[step1NodeName].Phase) assert.Equal(t, v1alpha1.NodePending, woc.wf.Status.Nodes[step2NodeName].Phase) - woc.handleExecutionControlError(containerSetNodeName, &sync.RWMutex{}, "terminated") + woc.handleExecutionControlError(ctx, containerSetNodeName, &sync.RWMutex{}, "terminated") assert.Equal(t, v1alpha1.NodeFailed, woc.wf.Status.Nodes[step1NodeName].Phase) assert.Equal(t, v1alpha1.NodeFailed, woc.wf.Status.Nodes[step2NodeName].Phase) diff --git a/workflow/controller/exit_handler.go b/workflow/controller/exit_handler.go index cd4725cb355d..1eea57bf10f4 100644 --- a/workflow/controller/exit_handler.go +++ b/workflow/controller/exit_handler.go @@ -29,7 +29,7 @@ func (woc *wfOperationCtx) runOnExitNode(ctx context.Context, exitHook *wfv1.Lif } } if execute { - woc.log.WithField("lifeCycleHook", exitHook).Infof("Running OnExit handler") + woc.log.WithField(ctx, "lifeCycleHook", exitHook).Infof(ctx, "Running OnExit handler") onExitNodeName := common.GenerateOnExitNodeName(parentNode.Name) resolvedArgs := exitHook.Arguments if !resolvedArgs.IsEmpty() { @@ -44,7 +44,7 @@ func (woc *wfOperationCtx) runOnExitNode(ctx context.Context, exitHook *wfv1.Lif onExitTemplate: true, nodeFlag: &wfv1.NodeFlag{Hooked: true}, }) - woc.addChildNode(parentNode.Name, onExitNodeName) + woc.addChildNode(ctx, parentNode.Name, onExitNodeName) return true, onExitNode, err } } diff --git a/workflow/controller/exit_handler_test.go b/workflow/controller/exit_handler_test.go index 5091fae33a4c..d2e972f27fc0 100644 --- a/workflow/controller/exit_handler_test.go +++ b/workflow/controller/exit_handler_test.go @@ -65,10 +65,10 @@ func TestStepsOnExitTmpl(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) onExitNodeIsPresent := false for _, node := range woc.wf.Status.Nodes { @@ -133,10 +133,10 @@ func TestDAGOnExitTmpl(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) onExitNodeIsPresent := false for _, node := range woc.wf.Status.Nodes { @@ -193,7 +193,7 @@ func TestStepsOnExitTmplWithArt(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) for idx, node := range woc.wf.Status.Nodes { @@ -212,7 +212,7 @@ func TestStepsOnExitTmplWithArt(t *testing.T) { woc.wf.Status.MarkTaskResultComplete(node.ID) } } - woc1 := newWorkflowOperationCtx(woc.wf, controller) + woc1 := newWorkflowOperationCtx(ctx, woc.wf, controller) woc1.operate(ctx) onExitNodeIsPresent := false for _, node := range woc1.wf.Status.Nodes { @@ -268,7 +268,7 @@ func TestDAGOnExitTmplWithArt(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) for idx, node := range woc.wf.Status.Nodes { @@ -287,7 +287,7 @@ func TestDAGOnExitTmplWithArt(t *testing.T) { woc.wf.Status.MarkTaskResultComplete(node.ID) } } - woc1 := newWorkflowOperationCtx(woc.wf, controller) + woc1 := newWorkflowOperationCtx(ctx, woc.wf, controller) woc1.operate(ctx) onExitNodeIsPresent := false for _, node := range woc1.wf.Status.Nodes { @@ -353,11 +353,11 @@ func TestStepsTmplOnExit(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc1 := newWorkflowOperationCtx(woc.wf, controller) + woc1 := newWorkflowOperationCtx(ctx, woc.wf, controller) woc1.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc1.wf.Status.Phase) onExitNodeIsPresent := false @@ -370,7 +370,7 @@ func TestStepsTmplOnExit(t *testing.T) { assert.True(t, onExitNodeIsPresent) makePodsPhase(ctx, woc1, apiv1.PodSucceeded) - woc2 := newWorkflowOperationCtx(woc1.wf, controller) + woc2 := newWorkflowOperationCtx(ctx, woc1.wf, controller) woc2.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc2.wf.Status.Phase) makePodsPhase(ctx, woc2, apiv1.PodSucceeded) @@ -389,7 +389,7 @@ func TestStepsTmplOnExit(t *testing.T) { } } - woc3 := newWorkflowOperationCtx(woc2.wf, controller) + woc3 := newWorkflowOperationCtx(ctx, woc2.wf, controller) woc3.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc3.wf.Status.Phase) onExitNodeIsPresent = false @@ -458,11 +458,11 @@ func TestDAGOnExit(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc1 := newWorkflowOperationCtx(woc.wf, controller) + woc1 := newWorkflowOperationCtx(ctx, woc.wf, controller) woc1.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc1.wf.Status.Phase) onExitNodeIsPresent := false @@ -475,7 +475,7 @@ func TestDAGOnExit(t *testing.T) { assert.True(t, onExitNodeIsPresent) makePodsPhase(ctx, woc1, apiv1.PodSucceeded) - woc2 := newWorkflowOperationCtx(woc1.wf, controller) + woc2 := newWorkflowOperationCtx(ctx, woc1.wf, controller) woc2.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc2.wf.Status.Phase) makePodsPhase(ctx, woc2, apiv1.PodSucceeded) @@ -493,7 +493,7 @@ func TestDAGOnExit(t *testing.T) { woc.wf.Status.MarkTaskResultComplete(node.ID) } } - woc3 := newWorkflowOperationCtx(woc2.wf, controller) + woc3 := newWorkflowOperationCtx(ctx, woc2.wf, controller) woc3.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc3.wf.Status.Phase) onExitNodeIsPresent = false @@ -695,7 +695,7 @@ func TestDagOnExitAndRetryStrategy(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -747,7 +747,7 @@ func TestWorkflowOnExitHttpReconciliation(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) taskSets, err := woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets("").List(ctx, v1.ListOptions{}) require.NoError(t, err) @@ -845,7 +845,7 @@ func TestWorkflowOnExitStepsHttpReconciliation(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) taskSets, err := woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets("").List(ctx, v1.ListOptions{}) require.NoError(t, err) @@ -988,7 +988,7 @@ status: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) taskSets, err := woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets("").List(ctx, v1.ListOptions{}) require.NoError(t, err) @@ -1037,12 +1037,12 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) var hasExitNode bool diff --git a/workflow/controller/hooks.go b/workflow/controller/hooks.go index 8e86d61c5393..163519bb0283 100644 --- a/workflow/controller/hooks.go +++ b/workflow/controller/hooks.go @@ -31,17 +31,17 @@ func (woc *wfOperationCtx) executeWfLifeCycleHook(ctx context.Context, tmplCtx * } // executeTemplated should be invoked when hookedNode != nil, because we should reexecute the function to check mutex condition, etc. if execute || hookedNode != nil { - woc.log.WithField("lifeCycleHook", hookName).WithField("node", hookNodeName).Infof("Running workflow level hooks") + woc.log.WithField(ctx, "lifeCycleHook", hookName).WithField(ctx, "node", hookNodeName).Info(ctx, "Running workflow level hooks") hookNode, err := woc.executeTemplate(ctx, hookNodeName, &wfv1.WorkflowStep{Template: hook.Template, TemplateRef: hook.TemplateRef}, tmplCtx, hook.Arguments, &executeTemplateOpts{nodeFlag: &wfv1.NodeFlag{Hooked: true}}, ) if err != nil { return true, err } - woc.addChildNode(woc.wf.Name, hookNodeName) + woc.addChildNode(ctx, woc.wf.Name, hookNodeName) hookNodes = append(hookNodes, hookNode) // If the hookNode node is HTTP template, it requires HTTP reconciliation, do it here - if hookNode != nil && woc.nodeRequiresTaskSetReconciliation(hookNode.Name) { + if hookNode != nil && woc.nodeRequiresTaskSetReconciliation(ctx, hookNode.Name) { woc.taskSetReconciliation(ctx) } } @@ -78,7 +78,7 @@ func (woc *wfOperationCtx) executeTmplLifeCycleHook(ctx context.Context, scope * if lastChildNode := woc.possiblyGetRetryChildNode(parentNode); lastChildNode != nil { outputs = lastChildNode.Outputs } - woc.log.WithField("lifeCycleHook", hookName).WithField("node", hookNodeName).WithField("hookName", hookName).Info("Running hooks") + woc.log.WithField(ctx, "lifeCycleHook", hookName).WithField(ctx, "node", hookNodeName).WithField(ctx, "hookName", hookName).Info(ctx, "Running hooks") resolvedArgs := hook.Arguments var err error if !resolvedArgs.IsEmpty() && outputs != nil { @@ -94,7 +94,7 @@ func (woc *wfOperationCtx) executeTmplLifeCycleHook(ctx context.Context, scope * if err != nil { return false, err } - woc.addChildNode(parentNode.Name, hookNodeName) + woc.addChildNode(ctx, parentNode.Name, hookNodeName) hookNodes = append(hookNodes, hookNode) } } diff --git a/workflow/controller/hooks_test.go b/workflow/controller/hooks_test.go index 40de205e9069..35f23009d054 100644 --- a/workflow/controller/hooks_test.go +++ b/workflow/controller/hooks_test.go @@ -142,7 +142,7 @@ status: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("lifecycle-hook-bgsf6.hooks.error") assert.NotNil(t, node) @@ -262,7 +262,7 @@ status: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("step1.hooks.error") assert.NotNil(t, node) @@ -549,7 +549,7 @@ status: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("step-1.hooks.error") assert.NotNil(t, node) @@ -774,7 +774,7 @@ status: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { fmt.Println(node.DisplayName, node.Phase) @@ -937,7 +937,7 @@ status: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("lifecycle-hook-fh7t4.hooks.Failed") assert.NotNil(t, node) @@ -983,11 +983,11 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("hook-failures.hooks.failure") assert.NotNil(t, node) @@ -997,7 +997,7 @@ spec: ) assert.Equal(t, wfv1.NodePending, node.Phase) makePodsPhase(ctx, woc, apiv1.PodFailed) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) err, _ := woc.podReconciliation(ctx) require.NoError(t, err) node = woc.wf.Status.Nodes.FindByDisplayName("hook-failures.hooks.failure") @@ -1031,11 +1031,11 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) assert.Equal(t, "invalid spec: hooks.failure Expression required", woc.wf.Status.Message) @@ -1070,11 +1070,11 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) assert.Equal(t, "invalid spec: templates.main.steps[0].step-1.foo Expression required", woc.wf.Status.Message) @@ -1116,12 +1116,12 @@ spec: cancel, controller := newController(wf) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodRunning) // Check if running hook is triggered - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("hook-running.hooks.running") assert.NotNil(t, node) @@ -1130,7 +1130,7 @@ spec: // Make all pods running makePodsPhase(ctx, woc, apiv1.PodRunning) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) node = woc.wf.Status.Nodes.FindByDisplayName("hook-running.hooks.running") assert.Equal(t, wfv1.NodeRunning, node.Phase) @@ -1143,7 +1143,7 @@ spec: updatedPod, _ := podcs.Update(ctx, pod, metav1.UpdateOptions{}) woc.wf.Status.MarkTaskResultComplete(woc.nodeID(pod)) _ = woc.controller.podInformer.GetStore().Update(updatedPod) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.Progress("1/2"), woc.wf.Status.Progress) node = woc.wf.Status.Nodes.FindByDisplayName("hook-running") @@ -1156,7 +1156,7 @@ spec: // Make all pod completed makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.Progress("2/2"), woc.wf.Status.Progress) node = woc.wf.Status.Nodes.FindByDisplayName("hook-running.hooks.running") @@ -1206,12 +1206,12 @@ spec: cancel, controller := newController(wf) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodRunning) // Check if running hook is triggered - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("job.hooks.running") assert.NotNil(t, node) @@ -1220,7 +1220,7 @@ spec: // Make all pods running makePodsPhase(ctx, woc, apiv1.PodRunning) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) node = woc.wf.Status.Nodes.FindByDisplayName("job.hooks.running") assert.Equal(t, wfv1.NodeRunning, node.Phase) @@ -1234,7 +1234,7 @@ spec: updatedPod, _ := podcs.Update(ctx, &pod, metav1.UpdateOptions{}) _ = woc.controller.podInformer.GetStore().Update(updatedPod) woc.wf.Status.MarkTaskResultComplete(woc.nodeID(&pod)) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.Progress("1/2"), woc.wf.Status.Progress) node = woc.wf.Status.Nodes.FindByDisplayName("job") @@ -1247,7 +1247,7 @@ spec: // Make all pod completed makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.Progress("2/2"), woc.wf.Status.Progress) node = woc.wf.Status.Nodes.FindByDisplayName("job.hooks.running") diff --git a/workflow/controller/http_template.go b/workflow/controller/http_template.go index de6a7048c1c6..299ef337647a 100644 --- a/workflow/controller/http_template.go +++ b/workflow/controller/http_template.go @@ -1,13 +1,15 @@ package controller import ( + "context" + wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" ) -func (woc *wfOperationCtx) executeHTTPTemplate(nodeName string, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) *wfv1.NodeStatus { +func (woc *wfOperationCtx) executeHTTPTemplate(ctx context.Context, nodeName string, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) *wfv1.NodeStatus { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - node = woc.initializeExecutableNode(nodeName, wfv1.NodeTypeHTTP, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) + node = woc.initializeExecutableNode(ctx, nodeName, wfv1.NodeTypeHTTP, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) } if !node.Fulfilled() { woc.taskSet[node.ID] = *tmpl diff --git a/workflow/controller/http_template_test.go b/workflow/controller/http_template_test.go index 05c016526739..b6f6654ebc5a 100644 --- a/workflow/controller/http_template_test.go +++ b/workflow/controller/http_template_test.go @@ -1,6 +1,7 @@ package controller import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -10,6 +11,7 @@ import ( ) func TestNodeRequiresHttpReconciliation(t *testing.T) { + ctx := context.Background() woc := &wfOperationCtx{ wf: &v1alpha1.Workflow{ ObjectMeta: v1.ObjectMeta{ @@ -35,7 +37,7 @@ func TestNodeRequiresHttpReconciliation(t *testing.T) { }, } - assert.False(t, woc.nodeRequiresTaskSetReconciliation("not-needed")) - assert.True(t, woc.nodeRequiresTaskSetReconciliation("child-http")) - assert.True(t, woc.nodeRequiresTaskSetReconciliation("parent")) + assert.False(t, woc.nodeRequiresTaskSetReconciliation(ctx, "not-needed")) + assert.True(t, woc.nodeRequiresTaskSetReconciliation(ctx, "child-http")) + assert.True(t, woc.nodeRequiresTaskSetReconciliation(ctx, "parent")) } diff --git a/workflow/controller/inline_test.go b/workflow/controller/inline_test.go index 85ebcf516bb3..b061da8e7ab4 100644 --- a/workflow/controller/inline_test.go +++ b/workflow/controller/inline_test.go @@ -37,8 +37,9 @@ spec: `) cancel, wfc := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, wfc) - woc.operate(context.Background()) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, wfc) + woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) } @@ -68,8 +69,9 @@ spec: `) cancel, wfc := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, wfc) - woc.operate(context.Background()) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, wfc) + woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) node := woc.wf.Status.Nodes.FindByDisplayName("a") @@ -165,7 +167,7 @@ func TestCallTemplateWithInlineSteps(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -267,7 +269,7 @@ func TestCallTemplateWithInlineDAG(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) diff --git a/workflow/controller/operator.go b/workflow/controller/operator.go index f87526b54493..ad5c62f7c61b 100644 --- a/workflow/controller/operator.go +++ b/workflow/controller/operator.go @@ -47,6 +47,7 @@ import ( "github.com/argoproj/argo-workflows/v3/util/expr/env" "github.com/argoproj/argo-workflows/v3/util/help" "github.com/argoproj/argo-workflows/v3/util/intstr" + "github.com/argoproj/argo-workflows/v3/util/logging" "github.com/argoproj/argo-workflows/v3/util/resource" "github.com/argoproj/argo-workflows/v3/util/retry" argoruntime "github.com/argoproj/argo-workflows/v3/util/runtime" @@ -73,8 +74,8 @@ type wfOperationCtx struct { // updated indicates whether or not the workflow object itself was updated // and needs to be persisted back to kubernetes updated bool - // log is an logrus logging context to correlate logs with a workflow - log *log.Entry + // log is a logging interfacg to correlate logs with a workflow + log logging.Logger // controller reference to workflow controller controller *WorkflowController // estimate duration @@ -144,18 +145,19 @@ type failedNodeStatus struct { } // newWorkflowOperationCtx creates and initializes a new wfOperationCtx object. -func newWorkflowOperationCtx(wf *wfv1.Workflow, wfc *WorkflowController) *wfOperationCtx { +func newWorkflowOperationCtx(ctx context.Context, wf *wfv1.Workflow, wfc *WorkflowController) *wfOperationCtx { // NEVER modify objects from the store. It's a read-only, local cache. // You can use DeepCopy() to make a deep copy of original object and modify this copy // Or create a copy manually for better performance wfCopy := wf.DeepCopyObject().(*wfv1.Workflow) + slogger := logging.NewSlogLogger() woc := wfOperationCtx{ wf: wfCopy, orig: wf, execWf: wfCopy, updated: false, - log: log.WithFields(log.Fields{ + log: slogger.WithFields(ctx, logging.Fields{ "workflow": wf.ObjectMeta.Name, "namespace": wf.ObjectMeta.Namespace, }), @@ -185,14 +187,14 @@ func newWorkflowOperationCtx(wf *wfv1.Workflow, wfc *WorkflowController) *wfOper // later time // As you must not call `persistUpdates` twice, you must not call `operate` twice. func (woc *wfOperationCtx) operate(ctx context.Context) { - defer argoruntime.RecoverFromPanic(woc.log) + defer argoruntime.RecoverFromPanic(ctx, woc.log) defer func() { woc.persistUpdates(ctx) }() defer func() { if r := recover(); r != nil { - woc.log.WithFields(log.Fields{"stack": string(debug.Stack()), "r": r}).Errorf("Recovered from panic") + woc.log.WithFields(ctx, logging.Fields{"stack": string(debug.Stack()), "r": r}).Error(ctx, "Recovered from panic") if rerr, ok := r.(error); ok { woc.markWorkflowError(ctx, rerr) } else { @@ -202,13 +204,13 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { } }() - woc.log.WithFields(log.Fields{"Phase": woc.wf.Status.Phase, "ResourceVersion": woc.wf.ObjectMeta.ResourceVersion}).Info("Processing workflow") + woc.log.WithFields(ctx, logging.Fields{"Phase": woc.wf.Status.Phase, "ResourceVersion": woc.wf.ObjectMeta.ResourceVersion}).Info(ctx, "Processing workflow") // Set the Execute workflow spec for execution // ExecWF is a runtime execution spec which merged from Wf, WFT and Wfdefault err := woc.setExecWorkflow(ctx) if err != nil { - woc.log.WithError(err).Errorf("Unable to set ExecWorkflow") + woc.log.WithError(ctx, err).Errorf(ctx, "Unable to set ExecWorkflow") return } @@ -229,19 +231,19 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { } woc.artifactRepository = repo - woc.addArtifactGCFinalizer() + woc.addArtifactGCFinalizer(ctx) // Reconciliation of Outputs (Artifacts). See ReportOutputs() of executor.go. - woc.taskResultReconciliation() + woc.taskResultReconciliation(ctx) // Do artifact GC if task result reconciliation is complete. if woc.wf.Status.Fulfilled() { if err := woc.garbageCollectArtifacts(ctx); err != nil { - woc.log.WithError(err).Error("failed to GC artifacts") + woc.log.WithError(ctx, err).Error(ctx, "failed to GC artifacts") return } } else { - woc.log.Debug("Skipping artifact GC") + woc.log.Debug(ctx, "Skipping artifact GC") } if woc.wf.Labels[common.LabelKeyCompleted] == "true" { // abort now, we do not want to perform any more processing on a complete workflow because we could corrupt it @@ -252,14 +254,14 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { if woc.execWf.Spec.Synchronization != nil { acquired, wfUpdate, msg, failedLockName, err := woc.controller.syncManager.TryAcquire(ctx, woc.wf, "", woc.execWf.Spec.Synchronization) if err != nil { - woc.log.Warnf("Failed to acquire the lock %s", failedLockName) + woc.log.Warnf(ctx, "Failed to acquire the lock %s", failedLockName) woc.markWorkflowFailed(ctx, fmt.Sprintf("Failed to acquire the synchronization lock. %s", err.Error())) return } woc.updated = wfUpdate if !acquired { if !woc.releaseLocksForPendingShuttingdownWfs(ctx) { - woc.log.Warn("Workflow processing has been postponed due to concurrency limit") + woc.log.Warn(ctx, "Workflow processing has been postponed due to concurrency limit") phase := woc.wf.Status.Phase if phase == wfv1.WorkflowUnknown { phase = wfv1.WorkflowPending @@ -283,7 +285,7 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { if woc.wf.Status.Phase == wfv1.WorkflowUnknown { err := woc.createPDBResource(ctx) if err != nil { - woc.log.WithError(err).WithField("workflow", woc.wf.Name).Error("PDB creation failed") + woc.log.WithError(ctx, err).WithField(ctx, "workflow", woc.wf.Name).Error(ctx, "PDB creation failed") woc.requeue() return } @@ -307,25 +309,25 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { // Execution control has been applied to the nodes with created pods after pod reconciliation. // However, pending and suspended nodes do not have created pods, and taskset nodes use the agent pod. // Apply execution control to these nodes now since pod reconciliation does not take effect on them. - woc.failNodesWithoutCreatedPodsAfterDeadlineOrShutdown() + woc.failNodesWithoutCreatedPodsAfterDeadlineOrShutdown(ctx) } if err != nil { - woc.log.WithError(err).WithField("workflow", woc.wf.ObjectMeta.Name).Error("workflow timeout") + woc.log.WithError(ctx, err).WithField(ctx, "workflow", woc.wf.ObjectMeta.Name).Error(ctx, "workflow timeout") woc.eventRecorder.Event(woc.wf, apiv1.EventTypeWarning, "WorkflowTimedOut", "Workflow timed out") // TODO: we need to re-add to the workqueue, but should happen in caller return } if !podReconciliationCompleted { - woc.log.WithField("workflow", woc.wf.ObjectMeta.Name).Info("pod reconciliation didn't complete, will retry") + woc.log.WithField(ctx, "workflow", woc.wf.ObjectMeta.Name).Info(ctx, "pod reconciliation didn't complete, will retry") woc.requeue() return } } if woc.ShouldSuspend() { - woc.log.Info("workflow suspended") + woc.log.Info(ctx, "workflow suspended") return } if woc.execWf.Spec.Parallelism != nil { @@ -335,14 +337,14 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { // Create a starting template context. tmplCtx, err := woc.createTemplateContext(wfv1.ResourceScopeLocal, "") if err != nil { - woc.log.WithError(err).Error("Failed to create a template context") + woc.log.WithError(ctx, err).Error(ctx, "Failed to create a template context") woc.markWorkflowError(ctx, err) return } err = woc.substituteParamsInVolumes(woc.globalParams) if err != nil { - woc.log.WithError(err).Error("volumes global param substitution error") + woc.log.WithError(ctx, err).Error(ctx, "volumes global param substitution error") woc.markWorkflowError(ctx, err) return } @@ -357,7 +359,7 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { return } err = fmt.Errorf("pvc create error: %w", err) - woc.log.WithError(err).Error("pvc create error") + woc.log.WithError(ctx, err).Error(ctx, "pvc create error") woc.markWorkflowError(ctx, err) return } else if woc.wf.Status.Phase == wfv1.WorkflowPending { @@ -367,7 +369,7 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { node, err := woc.executeTemplate(ctx, woc.wf.ObjectMeta.Name, &wfv1.WorkflowStep{Template: woc.execWf.Spec.Entrypoint}, tmplCtx, woc.execWf.Spec.Arguments, &executeTemplateOpts{}) if err != nil { - woc.log.WithError(err).Error("error in entry template execution") + woc.log.WithError(ctx, err).Error(ctx, "error in entry template execution") // we wrap this error up to report a clear message x := fmt.Errorf("error in entry template execution: %w", err) switch err { @@ -380,7 +382,7 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { // Garbage collect PVCs if Entrypoint template execution returns error if err := woc.deletePVCs(ctx); err != nil { - woc.log.WithError(err).Warn("failed to delete PVCs") + woc.log.WithError(ctx, err).Warn(ctx, "failed to delete PVCs") } } } @@ -415,7 +417,7 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { } failedNodeBytes, err := json.Marshal(failures) if err != nil { - woc.log.Errorf("Error marshalling failed nodes list: %+v", err) + woc.log.Errorf(ctx, "Error marshalling failed nodes list: %+v", err) // No need to return here } // This strconv.Quote is necessary so that the escaped quotes are not removed during parameter substitution @@ -423,7 +425,7 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { hookCompleted, err := woc.executeWfLifeCycleHook(ctx, tmplCtx) if err != nil { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) } // Reconcile TaskSet and Agent for HTTP/Plugin templates when is not shutdown if !woc.execWf.Spec.Shutdown.Enabled() { @@ -442,7 +444,7 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { var onExitNode *wfv1.NodeStatus if woc.execWf.Spec.HasExitHook() { - woc.log.Infof("Running OnExit handler: %s", woc.execWf.Spec.OnExit) + woc.log.Infof(ctx, "Running OnExit handler: %s", woc.execWf.Spec.OnExit) onExitNodeName := common.GenerateOnExitNodeName(woc.wf.ObjectMeta.Name) onExitNode, _ = woc.execWf.GetNodeByName(onExitNodeName) if onExitNode != nil || woc.GetShutdownStrategy().ShouldExecute(true) { @@ -462,7 +464,7 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { // Garbage collect PVCs if Onexit template execution returns error if err := woc.deletePVCs(ctx); err != nil { - woc.log.WithError(err).Warn("failed to delete PVCs") + woc.log.WithError(ctx, err).Warn(ctx, "failed to delete PVCs") } } } @@ -470,7 +472,7 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { } // If the onExit node (or any child of the onExit node) requires HTTP reconciliation, do it here - if onExitNode != nil && woc.nodeRequiresTaskSetReconciliation(onExitNode.Name) { + if onExitNode != nil && woc.nodeRequiresTaskSetReconciliation(ctx, onExitNode.Name) { woc.taskSetReconciliation(ctx) } @@ -526,14 +528,14 @@ func (woc *wfOperationCtx) operate(ctx context.Context) { } if err := woc.deletePVCs(ctx); err != nil { - woc.log.WithError(err).Warn("failed to delete PVCs") + woc.log.WithError(ctx, err).Warn(ctx, "failed to delete PVCs") } } func (woc *wfOperationCtx) releaseLocksForPendingShuttingdownWfs(ctx context.Context) bool { if woc.GetShutdownStrategy().Enabled() && woc.wf.Status.Phase == wfv1.WorkflowPending && woc.GetShutdownStrategy() == wfv1.ShutdownStrategyTerminate { if woc.controller.syncManager.ReleaseAll(woc.execWf) { - woc.log.WithFields(log.Fields{"key": woc.execWf.Name}).Info("Released all locks since this pending workflow is being shutdown") + woc.log.WithFields(ctx, logging.Fields{"key": woc.execWf.Name}).Info(ctx, "Released all locks since this pending workflow is being shutdown") woc.markWorkflowSuccess(ctx) return true } @@ -734,33 +736,33 @@ func (woc *wfOperationCtx) persistUpdates(ctx context.Context) { // * Fails the `reapplyUpdate` cannot work unless resource versions are different. // * It will double the number of Kubernetes API requests. if woc.orig.ResourceVersion != woc.wf.ResourceVersion { - woc.log.Panic("cannot persist updates with mismatched resource versions") + woc.log.Panic(ctx, "cannot persist updates with mismatched resource versions") } wfClient := woc.controller.wfclientset.ArgoprojV1alpha1().Workflows(woc.wf.ObjectMeta.Namespace) // try and compress nodes if needed nodes := woc.wf.Status.Nodes err := woc.controller.hydrator.Dehydrate(woc.wf) if err != nil { - woc.log.Warnf("Failed to dehydrate: %v", err) + woc.log.Warnf(ctx, "Failed to dehydrate: %v", err) woc.markWorkflowError(ctx, err) } // Release all acquired lock for completed workflow if woc.wf.Status.Synchronization != nil && woc.wf.Status.Fulfilled() { if woc.controller.syncManager.ReleaseAll(woc.wf) { - woc.log.WithFields(log.Fields{"key": woc.wf.Name}).Info("Released all acquired locks") + woc.log.WithFields(ctx, logging.Fields{"key": woc.wf.Name}).Info(ctx, "Released all acquired locks") } } // Remove completed taskset status before update workflow. err = woc.removeCompletedTaskSetStatus(ctx) if err != nil { - woc.log.WithError(err).Warn("error updating taskset") + woc.log.WithError(ctx, err).Warn(ctx, "error updating taskset") } wf, err := wfClient.Update(ctx, woc.wf, metav1.UpdateOptions{}) if err != nil { - woc.log.Warnf("Error updating workflow: %v %s", err, apierr.ReasonForError(err)) + woc.log.Warnf(ctx, "Error updating workflow: %v %s", err, apierr.ReasonForError(err)) if argokubeerr.IsRequestEntityTooLargeErr(err) { woc.persistWorkflowSizeLimitErr(ctx, wfClient, err) return @@ -768,10 +770,10 @@ func (woc *wfOperationCtx) persistUpdates(ctx context.Context) { if !apierr.IsConflict(err) { return } - woc.log.Info("Re-applying updates on latest version and retrying update") + woc.log.Info(ctx, "Re-applying updates on latest version and retrying update") wf, err := woc.reapplyUpdate(ctx, wfClient, nodes) if err != nil { - woc.log.WithError(err).Info("Failed to re-apply update") + woc.log.WithError(ctx, err).Info(ctx, "Failed to re-apply update") return } woc.wf = wf @@ -785,13 +787,13 @@ func (woc *wfOperationCtx) persistUpdates(ctx context.Context) { woc.wf.TypeMeta = woc.orig.TypeMeta // Create WorkflowNode* events for nodes that have changed phase - woc.recordNodePhaseChangeEvents(woc.orig.Status.Nodes, woc.wf.Status.Nodes) + woc.recordNodePhaseChangeEvents(ctx, woc.orig.Status.Nodes, woc.wf.Status.Nodes) if !woc.controller.hydrator.IsHydrated(woc.wf) { panic("workflow should be hydrated") } - woc.log.WithFields(log.Fields{"resourceVersion": woc.wf.ResourceVersion, "phase": woc.wf.Status.Phase}).Info("Workflow update successful") + woc.log.WithFields(ctx, logging.Fields{"resourceVersion": woc.wf.ResourceVersion, "phase": woc.wf.Status.Phase}).Info(ctx, "Workflow update successful") switch os.Getenv("INFORMER_WRITE_BACK") { // By default we write back (as per v2.11), this does not reduce errors, but does reduce @@ -809,7 +811,7 @@ func (woc *wfOperationCtx) persistUpdates(ctx context.Context) { if woc.wf.Status.Fulfilled() { woc.controller.metrics.StopRealtimeMetricsForWfUID(string(woc.wf.GetUID())) if err := woc.deleteTaskResults(ctx); err != nil { - woc.log.WithError(err).Warn("failed to delete task-results") + woc.log.WithError(ctx, err).Warn(ctx, "failed to delete task-results") } } // If Finalizer exists, requeue to make sure Finalizer can be removed. @@ -820,11 +822,11 @@ func (woc *wfOperationCtx) persistUpdates(ctx context.Context) { // It is important that we *never* label pods as completed until we successfully updated the workflow // Failing to do so means we can have inconsistent state. // Pods may be labeled multiple times. - woc.queuePodsForCleanup() + woc.queuePodsForCleanup(ctx) } -func (woc *wfOperationCtx) checkTaskResultsInProgress() bool { - woc.log.Debugf("Task results completion status: %v", woc.wf.Status.TaskResultsCompletionStatus) +func (woc *wfOperationCtx) checkTaskResultsInProgress(ctx context.Context) bool { + woc.log.Debugf(ctx, "Task results completion status: %v", woc.wf.Status.TaskResultsCompletionStatus) return woc.wf.Status.TaskResultsInProgress() } @@ -857,7 +859,7 @@ func (woc *wfOperationCtx) persistWorkflowSizeLimitErr(ctx context.Context, wfCl woc.markWorkflowError(ctx, err) _, err = wfClient.Update(ctx, woc.wf, metav1.UpdateOptions{}) if err != nil { - woc.log.Warnf("Error updating workflow with size error: %v", err) + woc.log.Warnf(ctx, "Error updating workflow with size error: %v", err) } } @@ -867,7 +869,7 @@ func (woc *wfOperationCtx) persistWorkflowSizeLimitErr(ctx context.Context, wfCl func (woc *wfOperationCtx) reapplyUpdate(ctx context.Context, wfClient v1alpha1.WorkflowInterface, nodes wfv1.Nodes) (*wfv1.Workflow, error) { // if this condition is true, then this func will always error if woc.orig.ResourceVersion != woc.wf.ResourceVersion { - woc.log.Panic("cannot re-apply update with mismatched resource versions") + woc.log.Panic(ctx, "cannot re-apply update with mismatched resource versions") } err := woc.controller.hydrator.Hydrate(woc.orig) if err != nil { @@ -930,12 +932,12 @@ func (woc *wfOperationCtx) reapplyUpdate(ctx context.Context, wfClient v1alpha1. } wf, err := wfClient.Update(ctx, &newWf, metav1.UpdateOptions{}) if err == nil { - woc.log.Infof("Update retry attempt %d successful", attempt) + woc.log.Infof(ctx, "Update retry attempt %d successful", attempt) woc.controller.hydrator.HydrateWithNodes(wf, nodes) return wf, nil } attempt++ - woc.log.Warnf("Update retry attempt %d failed: %v", attempt, err) + woc.log.Warnf(ctx, "Update retry attempt %d failed: %v", attempt, err) if attempt > 5 { return nil, err } @@ -954,7 +956,7 @@ func (woc *wfOperationCtx) requeue() { } // processNodeRetries updates the retry node state based on the child node state and the retry strategy and returns the node. -func (woc *wfOperationCtx) processNodeRetries(node *wfv1.NodeStatus, retryStrategy wfv1.RetryStrategy, opts *executeTemplateOpts) (*wfv1.NodeStatus, bool, error) { +func (woc *wfOperationCtx) processNodeRetries(ctx context.Context, node *wfv1.NodeStatus, retryStrategy wfv1.RetryStrategy, opts *executeTemplateOpts) (*wfv1.NodeStatus, bool, error) { if node.Fulfilled() { return node, true, nil } @@ -976,7 +978,7 @@ func (woc *wfOperationCtx) processNodeRetries(node *wfv1.NodeStatus, retryStrate if !lastChildNode.FailedOrError() { node.Outputs = lastChildNode.Outputs.DeepCopy() woc.wf.Status.Nodes.Set(node.ID, *node) - return woc.markNodePhase(node.Name, wfv1.NodeSucceeded), true, nil + return woc.markNodePhase(ctx, node.Name, wfv1.NodeSucceeded), true, nil } if woc.GetShutdownStrategy().Enabled() || (woc.workflowDeadline != nil && time.Now().UTC().After(*woc.workflowDeadline)) { @@ -986,8 +988,8 @@ func (woc *wfOperationCtx) processNodeRetries(node *wfv1.NodeStatus, retryStrate } else { message = fmt.Sprintf("retry exceeded workflow deadline %s", *woc.workflowDeadline) } - woc.log.Infoln(message) - return woc.markNodePhase(node.Name, lastChildNode.Phase, message), true, nil + woc.log.Info(ctx, message) + return woc.markNodePhase(ctx, node.Name, lastChildNode.Phase, message), true, nil } if retryStrategy.Backoff != nil { @@ -1004,8 +1006,8 @@ func (woc *wfOperationCtx) processNodeRetries(node *wfv1.NodeStatus, retryStrate } maxDurationDeadline = firstChildNode.StartedAt.Add(maxDuration) if time.Now().After(maxDurationDeadline) { - woc.log.Infoln("Max duration limit exceeded. Failing...") - return woc.markNodePhase(node.Name, lastChildNode.Phase, "Max duration limit exceeded"), true, nil + woc.log.Info(ctx, "Max duration limit exceeded. Failing...") + return woc.markNodePhase(ctx, node.Name, lastChildNode.Phase, "Max duration limit exceeded"), true, nil } } @@ -1033,21 +1035,21 @@ func (woc *wfOperationCtx) processNodeRetries(node *wfv1.NodeStatus, retryStrate // If the waiting deadline is after the max duration deadline, then it's futile to wait until then. Stop early if !maxDurationDeadline.IsZero() && waitingDeadline.After(maxDurationDeadline) { - woc.log.Infoln("Backoff would exceed max duration limit. Failing...") - return woc.markNodePhase(node.Name, lastChildNode.Phase, "Backoff would exceed max duration limit"), true, nil + woc.log.Info(ctx, "Backoff would exceed max duration limit. Failing...") + return woc.markNodePhase(ctx, node.Name, lastChildNode.Phase, "Backoff would exceed max duration limit"), true, nil } // See if we have waited past the deadline if time.Now().Before(waitingDeadline) && retryStrategy.Limit != nil && int32(len(childNodeIds)) <= int32(retryStrategy.Limit.IntValue()) { woc.requeueAfter(timeToWait) retryMessage := fmt.Sprintf("Backoff for %s", humanize.Duration(timeToWait)) - return woc.markNodePhase(node.Name, node.Phase, retryMessage), false, nil + return woc.markNodePhase(ctx, node.Name, node.Phase, retryMessage), false, nil } - woc.log.WithField("node", node.Name).Infof("node has maxDuration set, setting executionDeadline to: %s", humanize.Timestamp(maxDurationDeadline)) + woc.log.WithField(ctx, "node", node.Name).Infof(ctx, "node has maxDuration set, setting executionDeadline to: %s", humanize.Timestamp(maxDurationDeadline)) opts.executionDeadline = maxDurationDeadline - node = woc.markNodePhase(node.Name, node.Phase, "") + node = woc.markNodePhase(ctx, node.Name, node.Phase, "") } var retryOnFailed bool @@ -1070,16 +1072,16 @@ func (woc *wfOperationCtx) processNodeRetries(node *wfv1.NodeStatus, retryStrate default: return nil, false, fmt.Errorf("%s is not a valid RetryPolicy", retryStrategy.RetryPolicyActual()) } - woc.log.Infof("Retry Policy: %s (onFailed: %v, onError %v)", retryStrategy.RetryPolicyActual(), retryOnFailed, retryOnError) + woc.log.Infof(ctx, "Retry Policy: %s (onFailed: %v, onError %v)", retryStrategy.RetryPolicyActual(), retryOnFailed, retryOnError) if (lastChildNode.Phase == wfv1.NodeFailed && !retryOnFailed) || (lastChildNode.Phase == wfv1.NodeError && !retryOnError) { - woc.log.Infof("Node not set to be retried after status: %s", lastChildNode.Phase) - return woc.markNodePhase(node.Name, lastChildNode.Phase, lastChildNode.Message), true, nil + woc.log.Infof(ctx, "Node not set to be retried after status: %s", lastChildNode.Phase) + return woc.markNodePhase(ctx, node.Name, lastChildNode.Phase, lastChildNode.Message), true, nil } if !lastChildNode.CanRetry() { - woc.log.Infof("Node cannot be retried. Marking it failed") - return woc.markNodePhase(node.Name, lastChildNode.Phase, lastChildNode.Message), true, nil + woc.log.Infof(ctx, "Node cannot be retried. Marking it failed") + return woc.markNodePhase(ctx, node.Name, lastChildNode.Phase, lastChildNode.Message), true, nil } limit, err := intstr.Int32(retryStrategy.Limit) @@ -1087,8 +1089,8 @@ func (woc *wfOperationCtx) processNodeRetries(node *wfv1.NodeStatus, retryStrate return nil, false, err } if retryStrategy.Limit != nil && limit != nil && int32(len(childNodeIds)) > *limit { - woc.log.Infoln("No more retries left. Failing...") - return woc.markNodePhase(node.Name, lastChildNode.Phase, "No more retries left"), true, nil + woc.log.Info(ctx, "No more retries left. Failing...") + return woc.markNodePhase(ctx, node.Name, lastChildNode.Phase, "No more retries left"), true, nil } if retryStrategy.Expression != "" && len(childNodeIds) > 0 { @@ -1099,11 +1101,11 @@ func (woc *wfOperationCtx) processNodeRetries(node *wfv1.NodeStatus, retryStrate return nil, false, err } if !shouldContinue && lastChildNode.Fulfilled() { - return woc.markNodePhase(node.Name, lastChildNode.Phase, "retryStrategy.expression evaluated to false"), true, nil + return woc.markNodePhase(ctx, node.Name, lastChildNode.Phase, "retryStrategy.expression evaluated to false"), true, nil } } - woc.log.Infof("%d child nodes of %s failed. Trying again...", len(childNodeIds), node.Name) + woc.log.Infof(ctx, "%d child nodes of %s failed. Trying again...", len(childNodeIds), node.Name) return node, true, nil } @@ -1127,7 +1129,7 @@ func (woc *wfOperationCtx) podReconciliation(ctx context.Context) (error, bool) return } if woc.isAgentPod(pod) { - woc.updateAgentPodStatus(pod) + woc.updateAgentPodStatus(ctx, pod) return } nodeID := woc.nodeID(pod) @@ -1142,17 +1144,17 @@ func (woc *wfOperationCtx) podReconciliation(ctx context.Context) (error, bool) if newState := woc.assessNodeStatus(ctx, pod, node); newState != nil { // Check whether its taskresult is in an incompleted state. if newState.Succeeded() && woc.wf.Status.IsTaskResultIncomplete(node.ID) { - woc.log.WithFields(log.Fields{"nodeID": newState.ID}).Debug("Taskresult of the node not yet completed") + woc.log.WithFields(ctx, logging.Fields{"nodeID": newState.ID}).Debug(ctx, "Taskresult of the node not yet completed") taskResultIncomplete = true return } - woc.addOutputsToGlobalScope(newState.Outputs) + woc.addOutputsToGlobalScope(ctx, newState.Outputs) if newState.MemoizationStatus != nil { if newState.Succeeded() { c := woc.controller.cacheFactory.GetCache(controllercache.ConfigMapCache, newState.MemoizationStatus.CacheName) err := c.Save(ctx, newState.MemoizationStatus.Key, newState.ID, newState.Outputs) if err != nil { - woc.log.WithFields(log.Fields{"nodeID": newState.ID}).WithError(err).Error("Failed to save node outputs to cache") + woc.log.WithFields(ctx, logging.Fields{"nodeID": newState.ID}).WithError(ctx, err).Error(ctx, "Failed to save node outputs to cache") newState.Phase = wfv1.NodeError newState.Message = err.Error() } @@ -1166,7 +1168,7 @@ func (woc *wfOperationCtx) podReconciliation(ctx context.Context) (error, bool) // warning! when the node completes, the daemoned flag will be unset, so we must check the old node if !node.IsDaemoned() && !node.Completed() && newState.Completed() { if woc.shouldPrintPodSpec(newState) { - printPodSpecLog(pod, woc.wf.Name) + woc.printPodSpecLog(ctx, pod, woc.wf.Name) } } } @@ -1182,7 +1184,7 @@ func (woc *wfOperationCtx) podReconciliation(ctx context.Context) (error, bool) go func(pod *apiv1.Pod) { defer wg.Done() performAssessment(pod) - woc.applyExecutionControl(pod, wfNodesLock) + woc.applyExecutionControl(ctx, pod, wfNodesLock) <-parallelPodNum }(pod) } @@ -1212,7 +1214,7 @@ func (woc *wfOperationCtx) podReconciliation(ctx context.Context) (error, bool) if _, ok := seenPods[nodeID]; !ok { // grace-period to allow informer sync - woc.log.WithFields(log.Fields{"nodeName": node.Name, "nodePhase": node.Phase, "recentlyStarted": recentlyStarted}).Info("Workflow pod is missing") + woc.log.WithFields(ctx, logging.Fields{"nodeName": node.Name, "nodePhase": node.Phase, "recentlyStarted": recentlyStarted}).Info(ctx, "Workflow pod is missing") woc.controller.metrics.PodMissingInc(ctx, recentlyStarted, string(node.Phase)) // If the node is pending and the pod does not exist, it could be the case that we want to try to submit it @@ -1233,9 +1235,9 @@ func (woc *wfOperationCtx) podReconciliation(ctx context.Context) (error, bool) node.Daemoned = nil woc.updated = true } - woc.markNodeError(node.Name, errors.New("", "pod deleted")) + woc.markNodeError(ctx, node.Name, errors.New("", "pod deleted")) // Mark all its children(container) as deleted if pod deleted - woc.markAllContainersDeleted(node.ID) + woc.markAllContainersDeleted(ctx, node.ID) } } return nil, !taskResultIncomplete @@ -1254,23 +1256,23 @@ func recentlyStarted(node wfv1.NodeStatus) bool { } // markAllContainersDeleted mark all its children(container) as deleted -func (woc *wfOperationCtx) markAllContainersDeleted(nodeID string) { +func (woc *wfOperationCtx) markAllContainersDeleted(ctx context.Context, nodeID string) { node, err := woc.wf.Status.Nodes.Get(nodeID) if err != nil { - woc.log.Errorf("was unable to obtain node for %s", nodeID) + woc.log.Errorf(ctx, "was unable to obtain node for %s", nodeID) return } for _, childNodeID := range node.Children { childNode, err := woc.wf.Status.Nodes.Get(childNodeID) if err != nil { - woc.log.Errorf("was unable to obtain node for %s", childNodeID) + woc.log.Errorf(ctx, "was unable to obtain node for %s", childNodeID) continue } if childNode.Type == wfv1.NodeTypeContainer { - woc.markNodeError(childNode.Name, errors.New("", "container deleted")) + woc.markNodeError(ctx, childNode.Name, errors.New("", "container deleted")) // Recursively mark successor node(container) as deleted - woc.markAllContainersDeleted(childNodeID) + woc.markAllContainersDeleted(ctx, childNodeID) } } } @@ -1282,7 +1284,7 @@ func (woc *wfOperationCtx) shouldPrintPodSpec(node *wfv1.NodeStatus) bool { } // failNodesWithoutCreatedPodsAfterDeadlineOrShutdown mark the nodes without created pods failed when shutting down or exceeding deadline. -func (woc *wfOperationCtx) failNodesWithoutCreatedPodsAfterDeadlineOrShutdown() { +func (woc *wfOperationCtx) failNodesWithoutCreatedPodsAfterDeadlineOrShutdown(ctx context.Context) { nodes := woc.wf.Status.Nodes for _, node := range nodes { if node.Fulfilled() { @@ -1293,7 +1295,7 @@ func (woc *wfOperationCtx) failNodesWithoutCreatedPodsAfterDeadlineOrShutdown() // fail suspended nodes or taskset nodes when shutting down if node.IsActiveSuspendNode() || node.IsTaskSetNode() { message := fmt.Sprintf("Stopped with strategy '%s'", woc.GetShutdownStrategy()) - woc.markNodePhase(node.Name, wfv1.NodeFailed, message) + woc.markNodePhase(ctx, node.Name, wfv1.NodeFailed, message) continue } } @@ -1302,7 +1304,7 @@ func (woc *wfOperationCtx) failNodesWithoutCreatedPodsAfterDeadlineOrShutdown() deadlineExceeded := woc.workflowDeadline != nil && time.Now().UTC().After(*woc.workflowDeadline) if deadlineExceeded && !node.IsPartOfExitHandler(nodes) && (node.Phase == wfv1.NodePending || node.IsActiveSuspendNode()) { message := "Step exceeded its deadline" - woc.markNodePhase(node.Name, wfv1.NodeFailed, message) + woc.markNodePhase(ctx, node.Name, wfv1.NodeFailed, message) continue } } @@ -1325,20 +1327,20 @@ func (woc *wfOperationCtx) getAllWorkflowPods() ([]*apiv1.Pod, error) { return pods, nil } -func printPodSpecLog(pod *apiv1.Pod, wfName string) { +func (woc *wfOperationCtx) printPodSpecLog(ctx context.Context, pod *apiv1.Pod, wfName string) { podSpecByte, err := json.Marshal(pod) - log := log.WithField("workflow", wfName). - WithField("podName", pod.Name). - WithField("nodeID", pod.Annotations[common.AnnotationKeyNodeID]). - WithField("namespace", pod.Namespace) + log := woc.log.WithField(ctx, "workflow", wfName). + WithField(ctx, "podName", pod.Name). + WithField(ctx, "nodeID", pod.Annotations[common.AnnotationKeyNodeID]). + WithField(ctx, "namespace", pod.Namespace) if err != nil { log. - WithError(err). - Warn("Unable to marshal pod spec.") + WithError(ctx, err). + Warn(ctx, "Unable to marshal pod spec.") } else { log. - WithField("spec", string(podSpecByte)). - Info("Pod Spec") + WithField(ctx, "spec", string(podSpecByte)). + Info(ctx, "Pod Spec") } } @@ -1346,9 +1348,9 @@ func printPodSpecLog(pod *apiv1.Pod, wfName string) { // and returns the new node status if something changed func (woc *wfOperationCtx) assessNodeStatus(ctx context.Context, pod *apiv1.Pod, old *wfv1.NodeStatus) *wfv1.NodeStatus { new := old.DeepCopy() - tmpl, err := woc.GetNodeTemplate(old) + tmpl, err := woc.GetNodeTemplate(ctx, old) if err != nil { - woc.log.Error(err) + woc.log.Error(ctx, err.Error()) return nil } switch pod.Status.Phase { @@ -1368,8 +1370,8 @@ func (woc *wfOperationCtx) assessNodeStatus(ctx context.Context, pod *apiv1.Pod, new.Phase = wfv1.NodeSucceeded } else { new.Phase, new.Message = woc.inferFailedReason(pod, tmpl) - woc.log.WithField("displayName", old.DisplayName).WithField("templateName", wfutil.GetTemplateFromNode(*old)). - WithField("pod", pod.Name).Infof("Pod failed: %s", new.Message) + woc.log.WithField(ctx, "displayName", old.DisplayName).WithField(ctx, "templateName", wfutil.GetTemplateFromNode(*old)). + WithField(ctx, "pod", pod.Name).Infof(ctx, "Pod failed: %s", new.Message) } new.Daemoned = nil case apiv1.PodRunning: @@ -1388,7 +1390,7 @@ func (woc *wfOperationCtx) assessNodeStatus(ctx context.Context, pod *apiv1.Pod, new.Phase = wfv1.NodeRunning new.Daemoned = ptr.To(true) if !old.IsDaemoned() { - woc.log.WithField("nodeId", old.ID).Info("Node became daemoned") + woc.log.WithField(ctx, "nodeId", old.ID).Info(ctx, "Node became daemoned") } } } else { @@ -1414,21 +1416,21 @@ func (woc *wfOperationCtx) assessNodeStatus(ctx context.Context, pod *apiv1.Pod, } switch { case c.State.Waiting != nil: - woc.markNodePhase(ctrNodeName, wfv1.NodePending) + woc.markNodePhase(ctx, ctrNodeName, wfv1.NodePending) case c.State.Running != nil: - woc.markNodePhase(ctrNodeName, wfv1.NodeRunning) + woc.markNodePhase(ctx, ctrNodeName, wfv1.NodeRunning) case c.State.Terminated != nil: exitCode := int(c.State.Terminated.ExitCode) message := fmt.Sprintf("%s: %s (exit code %d): %s", c.Name, c.State.Terminated.Reason, exitCode, c.State.Terminated.Message) switch exitCode { case 0: - woc.markNodePhase(ctrNodeName, wfv1.NodeSucceeded) + woc.markNodePhase(ctx, ctrNodeName, wfv1.NodeSucceeded) case 64: // special emissary exit code indicating the emissary errors, rather than the sub-process failure, // (unless the sub-process coincidentally exits with code 64 of course) - woc.markNodePhase(ctrNodeName, wfv1.NodeError, message) + woc.markNodePhase(ctx, ctrNodeName, wfv1.NodeError, message) default: - woc.markNodePhase(ctrNodeName, wfv1.NodeFailed, message) + woc.markNodePhase(ctx, ctrNodeName, wfv1.NodeFailed, message) } } } @@ -1457,7 +1459,7 @@ func (woc *wfOperationCtx) assessNodeStatus(ctx context.Context, pod *apiv1.Pod, for _, c := range pod.Status.InitContainerStatuses { if c.State.Terminated != nil && int(c.State.Terminated.ExitCode) != 0 { new.Phase = wfv1.NodeFailed - woc.log.WithField("new.phase", new.Phase).Info("marking node as failed since init container has non-zero exit code") + woc.log.WithField(ctx, "new.phase", new.Phase).Info(ctx, "marking node as failed since init container has non-zero exit code") break } } @@ -1470,14 +1472,14 @@ func (woc *wfOperationCtx) assessNodeStatus(ctx context.Context, pod *apiv1.Pod, waitContainerCleanedUp = false switch { case c.State.Running != nil && new.Phase.Completed(): - woc.log.WithField("new.phase", new.Phase).Info("leaving phase un-changed: wait container is not yet terminated ") + woc.log.WithField(ctx, "new.phase", new.Phase).Info(ctx, "leaving phase un-changed: wait container is not yet terminated ") new.Phase = old.Phase case c.State.Terminated != nil && c.State.Terminated.ExitCode != 0: // Mark its taskResult as completed directly since wait container did not exit normally, // and it will never have a chance to report taskResult correctly. nodeID := woc.nodeID(pod) - woc.log.WithFields(log.Fields{"nodeID": nodeID, "exitCode": c.State.Terminated.ExitCode, "reason": c.State.Terminated.Reason}). - Warn("marking its taskResult as completed since wait container did not exit normally") + woc.log.WithFields(ctx, logging.Fields{"nodeID": nodeID, "exitCode": c.State.Terminated.ExitCode, "reason": c.State.Terminated.Reason}). + Warn(ctx, "marking its taskResult as completed since wait container did not exit normally") woc.wf.Status.MarkTaskResultComplete(nodeID) } } @@ -1486,8 +1488,8 @@ func (woc *wfOperationCtx) assessNodeStatus(ctx context.Context, pod *apiv1.Pod, // Mark its taskResult as completed directly since wait container has been cleaned up because of pod evicted, // and it will never have a chance to report taskResult correctly. nodeID := woc.nodeID(pod) - woc.log.WithFields(log.Fields{"nodeID": nodeID}). - Warn("marking its taskResult as completed since wait container has been cleaned up.") + woc.log.WithFields(ctx, logging.Fields{"nodeID": nodeID}). + Warn(ctx, "marking its taskResult as completed since wait container has been cleaned up.") woc.wf.Status.MarkTaskResultComplete(nodeID) } @@ -1502,18 +1504,18 @@ func (woc *wfOperationCtx) assessNodeStatus(ctx context.Context, pod *apiv1.Pod, } if !reflect.DeepEqual(old, new) { - woc.log.WithField("nodeID", old.ID). - WithField("old.phase", old.Phase). - WithField("new.phase", new.Phase). - WithField("old.message", old.Message). - WithField("new.message", new.Message). - WithField("old.progress", old.Progress). - WithField("new.progress", new.Progress). - Debug("node changed") + woc.log.WithField(ctx, "nodeID", old.ID). + WithField(ctx, "old.phase", old.Phase). + WithField(ctx, "new.phase", new.Phase). + WithField(ctx, "old.message", old.Message). + WithField(ctx, "new.message", new.Message). + WithField(ctx, "old.progress", old.Progress). + WithField(ctx, "new.progress", new.Progress). + Debug(ctx, "node changed") return new } - woc.log.WithField("nodeID", old.ID). - Debug("node unchanged") + woc.log.WithField(ctx, "nodeID", old.ID). + Debug(ctx, "node unchanged") return nil } @@ -1690,7 +1692,7 @@ func (woc *wfOperationCtx) createPVCs(ctx context.Context) error { // PVC name will be - refName := pvcTmpl.ObjectMeta.Name pvcName := fmt.Sprintf("%s-%s", woc.wf.ObjectMeta.Name, pvcTmpl.ObjectMeta.Name) - woc.log.Infof("Creating pvc %s", pvcName) + woc.log.Infof(ctx, "Creating pvc %s", pvcName) pvcTmpl.ObjectMeta.Name = pvcName if pvcTmpl.ObjectMeta.Labels == nil { pvcTmpl.ObjectMeta.Labels = make(map[string]string) @@ -1701,7 +1703,7 @@ func (woc *wfOperationCtx) createPVCs(ctx context.Context) error { } pvc, err := pvcClient.Create(ctx, &pvcTmpl, metav1.CreateOptions{}) if err != nil && apierr.IsAlreadyExists(err) { - woc.log.WithField("pvc", pvcTmpl.Name).Info("pvc already exists. Workflow is re-using it") + woc.log.WithField(ctx, "pvc", pvcTmpl.Name).Info(ctx, "pvc already exists. Workflow is re-using it") pvc, err = pvcClient.Get(ctx, pvcTmpl.Name, metav1.GetOptions{}) if err != nil { return err @@ -1763,11 +1765,11 @@ func (woc *wfOperationCtx) deletePVCs(ctx context.Context) error { // Attempt to delete all PVCs. Record first error encountered var firstErr error for _, pvc := range woc.wf.Status.PersistentVolumeClaims { - woc.log.Infof("Deleting PVC %s", pvc.PersistentVolumeClaim.ClaimName) + woc.log.Infof(ctx, "Deleting PVC %s", pvc.PersistentVolumeClaim.ClaimName) err := pvcClient.Delete(ctx, pvc.PersistentVolumeClaim.ClaimName, metav1.DeleteOptions{}) if err != nil { if !apierr.IsNotFound(err) { - woc.log.Errorf("Failed to delete pvc %s: %v", pvc.PersistentVolumeClaim.ClaimName, err) + woc.log.Errorf(ctx, "Failed to delete pvc %s: %v", pvc.PersistentVolumeClaim.ClaimName, err) newPVClist = append(newPVClist, pvc) if firstErr == nil { firstErr = err @@ -1777,8 +1779,8 @@ func (woc *wfOperationCtx) deletePVCs(ctx context.Context) error { } if os.Getenv("ARGO_REMOVE_PVC_PROTECTION_FINALIZER") != "false" { for _, pvc := range woc.wf.Status.PersistentVolumeClaims { - woc.log.WithField("claimName", pvc.PersistentVolumeClaim.ClaimName). - Info("Removing PVC \"kubernetes.io/pvc-protection\" finalizer") + woc.log.WithField(ctx, "claimName", pvc.PersistentVolumeClaim.ClaimName). + Info(ctx, "Removing PVC \"kubernetes.io/pvc-protection\" finalizer") x, err := pvcClient.Get(ctx, pvc.PersistentVolumeClaim.ClaimName, metav1.GetOptions{}) if err != nil { return err @@ -1793,7 +1795,7 @@ func (woc *wfOperationCtx) deletePVCs(ctx context.Context) error { } if len(newPVClist) != totalPVCs { // we were successful in deleting one ore more PVCs - woc.log.Infof("Deleted %d/%d PVCs", totalPVCs-len(newPVClist), totalPVCs) + woc.log.Infof(ctx, "Deleted %d/%d PVCs", totalPVCs-len(newPVClist), totalPVCs) woc.wf.Status.PersistentVolumeClaims = newPVClist woc.updated = true } @@ -1898,7 +1900,7 @@ type executeTemplateOpts struct { // nodeName is the name to be used as the name of the node, and boundaryID indicates which template // boundary this node belongs to. func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, orgTmpl wfv1.TemplateReferenceHolder, tmplCtx *templateresolution.Context, args wfv1.Arguments, opts *executeTemplateOpts) (*wfv1.NodeStatus, error) { - woc.log.Debugf("Evaluating node %s: template: %s, boundaryID: %s", nodeName, common.GetTemplateHolderString(orgTmpl), opts.boundaryID) + woc.log.Debugf(ctx, "Evaluating node %s: template: %s, boundaryID: %s", nodeName, common.GetTemplateHolderString(orgTmpl), opts.boundaryID) // Set templateScope from which the template resolution starts. templateScope := tmplCtx.GetTemplateScope() @@ -1906,19 +1908,19 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, node, err := woc.wf.GetNodeByName(nodeName) if err != nil { // Will be initialized via woc.initializeNodeOrMarkError - woc.log.Warnf("Node was nil, will be initialized as type Skipped") + woc.log.Warnf(ctx, "Node was nil, will be initialized as type Skipped") } woc.currentStackDepth++ defer func() { woc.currentStackDepth-- }() if woc.currentStackDepth >= woc.controller.maxStackDepth && os.Getenv("DISABLE_MAX_RECURSION") != "true" { - return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, ErrMaxDepthExceeded), ErrMaxDepthExceeded + return woc.initializeNodeOrMarkError(ctx, node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, ErrMaxDepthExceeded), ErrMaxDepthExceeded } - newTmplCtx, resolvedTmpl, templateStored, err := tmplCtx.ResolveTemplate(orgTmpl) + newTmplCtx, resolvedTmpl, templateStored, err := tmplCtx.ResolveTemplate(ctx, orgTmpl) if err != nil { - return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err + return woc.initializeNodeOrMarkError(ctx, node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err } // A new template was stored during resolution, persist it if templateStored { @@ -1943,13 +1945,13 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, // Merge Template defaults to template err = woc.mergedTemplateDefaultsInto(resolvedTmpl) if err != nil { - return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err + return woc.initializeNodeOrMarkError(ctx, node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err } // Inputs has been processed with arguments already, so pass empty arguments. processedTmpl, err := common.ProcessArgs(resolvedTmpl, &args, woc.globalParams, localParams, false, woc.wf.Namespace, woc.controller.configMapInformer.GetIndexer()) if err != nil { - return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err + return woc.initializeNodeOrMarkError(ctx, node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err } // Check if this is a fulfilled node for synchronization. @@ -1960,12 +1962,12 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, woc.controller.syncManager.Release(ctx, woc.wf, node.ID, processedTmpl.Synchronization) return fulfilledNode, nil } - woc.log.Debugf("Executing node %s of %s is %s", nodeName, node.Type, node.Phase) + woc.log.Debugf(ctx, "Executing node %s of %s is %s", nodeName, node.Type, node.Phase) } // Check if we took too long operating on this workflow and immediately return if we did if time.Now().UTC().After(woc.deadline) { - woc.log.Warnf("Deadline exceeded") + woc.log.Warnf(ctx, "Deadline exceeded") woc.requeue() return node, ErrDeadlineExceeded } @@ -1975,12 +1977,12 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, // In above scenario, only Node will be created in pending state _, err = woc.checkTemplateTimeout(processedTmpl, node) if err != nil { - woc.log.Warnf("Template %s exceeded its deadline", processedTmpl.Name) - return woc.markNodePhase(nodeName, wfv1.NodeFailed, err.Error()), err + woc.log.Warnf(ctx, "Template %s exceeded its deadline", processedTmpl.Name) + return woc.markNodePhase(ctx, nodeName, wfv1.NodeFailed, err.Error()), err } // Check if we exceeded template or workflow parallelism and immediately return if we did - if err := woc.checkParallelism(processedTmpl, node, opts.boundaryID); err != nil { + if err := woc.checkParallelism(ctx, processedTmpl, node, opts.boundaryID); err != nil { return node, err } @@ -1989,20 +1991,20 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, if processedTmpl.Synchronization != nil { lockAcquired, wfUpdated, msg, failedLockName, err := woc.controller.syncManager.TryAcquire(ctx, woc.wf, woc.wf.NodeID(nodeName), processedTmpl.Synchronization) if err != nil { - return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err + return woc.initializeNodeOrMarkError(ctx, node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err } if !lockAcquired { if node == nil { - node = woc.initializeExecutableNode(nodeName, wfutil.GetNodeType(processedTmpl), templateScope, processedTmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag, msg) + node = woc.initializeExecutableNode(ctx, nodeName, wfutil.GetNodeType(processedTmpl), templateScope, processedTmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag, msg) } - woc.log.Infof("Could not acquire lock named: %s", failedLockName) + woc.log.Infof(ctx, "Could not acquire lock named: %s", failedLockName) return woc.markNodeWaitingForLock(node.Name, failedLockName, msg) } else { - woc.log.Infof("Node %s acquired synchronization lock", nodeName) + woc.log.Infof(ctx, "Node %s acquired synchronization lock", nodeName) if node != nil { node, err = woc.markNodeWaitingForLock(node.Name, "", "") if err != nil { - woc.log.WithField("node.Name", node.Name).WithField("lockName", "").Error("markNodeWaitingForLock returned err") + woc.log.WithField(ctx, "node.Name", node.Name).WithField(ctx, "lockName", "").Error(ctx, "markNodeWaitingForLock returned err") return nil, err } } @@ -2019,13 +2021,13 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, memoizationCache := woc.controller.cacheFactory.GetCache(controllercache.ConfigMapCache, processedTmpl.Memoize.Cache.ConfigMap.Name) if memoizationCache == nil { err := fmt.Errorf("cache could not be found or created") - woc.log.WithFields(log.Fields{"cacheName": processedTmpl.Memoize.Cache.ConfigMap.Name}).WithError(err) - return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err + woc.log.WithFields(ctx, logging.Fields{"cacheName": processedTmpl.Memoize.Cache.ConfigMap.Name}).WithError(ctx, err) + return woc.initializeNodeOrMarkError(ctx, node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err } entry, err := memoizationCache.Load(ctx, processedTmpl.Memoize.Key) if err != nil { - return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err + return woc.initializeNodeOrMarkError(ctx, node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err } hit := entry.Hit() @@ -2034,7 +2036,7 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, maxAge, err := time.ParseDuration(processedTmpl.Memoize.MaxAge) if err != nil { err := fmt.Errorf("invalid maxAge: %s", err) - return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err + return woc.initializeNodeOrMarkError(ctx, node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err } maxAgeOutputs, ok := entry.GetOutputsWithMaxAge(maxAge) if !ok { @@ -2053,16 +2055,16 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, } if hit { if node == nil { - node = woc.initializeCacheHitNode(nodeName, processedTmpl, templateScope, orgTmpl, opts.boundaryID, outputs, memoizationStatus, opts.nodeFlag) + node = woc.initializeCacheHitNode(ctx, nodeName, processedTmpl, templateScope, orgTmpl, opts.boundaryID, outputs, memoizationStatus, opts.nodeFlag) } else { - woc.log.Infof("Node %s is using mutex with memoize. Cache is hit.", nodeName) - woc.updateAsCacheHitNode(node, outputs, memoizationStatus) + woc.log.Infof(ctx, "Node %s is using mutex with memoize. Cache is hit.", nodeName) + woc.updateAsCacheHitNode(ctx, node, outputs, memoizationStatus) } } else { if node == nil { - node = woc.initializeCacheNode(nodeName, processedTmpl, templateScope, orgTmpl, opts.boundaryID, memoizationStatus, opts.nodeFlag) + node = woc.initializeCacheNode(ctx, nodeName, processedTmpl, templateScope, orgTmpl, opts.boundaryID, memoizationStatus, opts.nodeFlag) } else { - woc.log.Infof("Node %s is using mutex with memoize. Cache is NOT hit", nodeName) + woc.log.Infof(ctx, "Node %s is using mutex with memoize. Cache is NOT hit", nodeName) woc.updateAsCacheNode(node, memoizationStatus) } } @@ -2099,16 +2101,16 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, retryNodeName = nodeName retryParentNode := node if retryParentNode == nil { - woc.log.Debugf("Inject a retry node for node %s", retryNodeName) - retryParentNode = woc.initializeExecutableNode(retryNodeName, wfv1.NodeTypeRetry, templateScope, processedTmpl, orgTmpl, opts.boundaryID, wfv1.NodeRunning, opts.nodeFlag) + woc.log.Debugf(ctx, "Inject a retry node for node %s", retryNodeName) + retryParentNode = woc.initializeExecutableNode(ctx, retryNodeName, wfv1.NodeTypeRetry, templateScope, processedTmpl, orgTmpl, opts.boundaryID, wfv1.NodeRunning, opts.nodeFlag) } if opts.nodeFlag == nil { opts.nodeFlag = &wfv1.NodeFlag{} } opts.nodeFlag.Retried = true - processedRetryParentNode, continueExecution, err := woc.processNodeRetries(retryParentNode, *woc.retryStrategy(processedTmpl), opts) + processedRetryParentNode, continueExecution, err := woc.processNodeRetries(ctx, retryParentNode, *woc.retryStrategy(processedTmpl), opts) if err != nil { - return woc.markNodeError(retryNodeName, err), err + return woc.markNodeError(ctx, retryNodeName, err), err } else if !continueExecution { // We are still waiting for a retry delay to finish return retryParentNode, nil @@ -2160,7 +2162,7 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, // Create a new child node and append it to the retry node. retryNum = len(childNodeIDs) nodeName = fmt.Sprintf("%s(%d)", retryNodeName, retryNum) - woc.addChildNode(retryNodeName, nodeName) + woc.addChildNode(ctx, retryNodeName, nodeName) node = nil } @@ -2177,7 +2179,7 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, return node, err } if err != nil { - return woc.initializeNodeOrMarkError(node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err + return woc.initializeNodeOrMarkError(ctx, node, nodeName, templateScope, orgTmpl, opts.boundaryID, opts.nodeFlag, err), err } } @@ -2195,20 +2197,20 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, case wfv1.TemplateTypeDAG: node, err = woc.executeDAG(ctx, nodeName, newTmplCtx, templateScope, processedTmpl, orgTmpl, opts) case wfv1.TemplateTypeSuspend: - node, err = woc.executeSuspend(nodeName, templateScope, processedTmpl, orgTmpl, opts) + node, err = woc.executeSuspend(ctx, nodeName, templateScope, processedTmpl, orgTmpl, opts) case wfv1.TemplateTypeData: node, err = woc.executeData(ctx, nodeName, templateScope, processedTmpl, orgTmpl, opts) case wfv1.TemplateTypeHTTP: - node = woc.executeHTTPTemplate(nodeName, templateScope, processedTmpl, orgTmpl, opts) + node = woc.executeHTTPTemplate(ctx, nodeName, templateScope, processedTmpl, orgTmpl, opts) case wfv1.TemplateTypePlugin: - node = woc.executePluginTemplate(nodeName, templateScope, processedTmpl, orgTmpl, opts) + node = woc.executePluginTemplate(ctx, nodeName, templateScope, processedTmpl, orgTmpl, opts) default: err = errors.Errorf(errors.CodeBadRequest, "Template '%s' missing specification", processedTmpl.Name) - return woc.initializeNode(nodeName, wfv1.NodeTypeSkipped, templateScope, orgTmpl, opts.boundaryID, wfv1.NodeError, opts.nodeFlag, err.Error()), err + return woc.initializeNode(ctx, nodeName, wfv1.NodeTypeSkipped, templateScope, orgTmpl, opts.boundaryID, wfv1.NodeError, opts.nodeFlag, err.Error()), err } if err != nil { - node = woc.markNodeError(nodeName, err) + node = woc.markNodeError(ctx, nodeName, err) // If retry policy is not set, or if it is not set to Always or OnError, we won't attempt to retry an errored container // and we return instead. @@ -2237,7 +2239,7 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, retrieveNode, err := woc.wf.GetNodeByName(node.Name) if err != nil { err := fmt.Errorf("no Node found by the name of %s; wf.Status.Nodes=%+v", node.Name, woc.wf.Status.Nodes) - woc.log.Error(err) + woc.log.Error(ctx, err.Error()) woc.markWorkflowError(ctx, err) return node, err } @@ -2248,7 +2250,7 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, retryNode, err := woc.wf.GetNodeByName(retryNodeName) if err != nil { err := fmt.Errorf("no Retry Node found by the name of %s; wf.Status.Nodes=%+v", retryNodeName, woc.wf.Status.Nodes) - woc.log.Error(err) + woc.log.Error(ctx, err.Error()) woc.markWorkflowError(ctx, err) return node, err } @@ -2256,7 +2258,7 @@ func (woc *wfOperationCtx) executeTemplate(ctx context.Context, nodeName string, if !retryNode.Fulfilled() && node.Fulfilled() { // if the retry child has completed we need to update outself retryNode, err = woc.executeTemplate(ctx, retryNodeName, orgTmpl, tmplCtx, args, opts) if err != nil { - return woc.markNodeError(node.Name, err), err + return woc.markNodeError(ctx, node.Name, err), err } } node = retryNode @@ -2292,7 +2294,7 @@ func (woc *wfOperationCtx) handleNodeFulfilled(ctx context.Context, nodeName str return nil } - woc.log.Debugf("Node %s already completed", nodeName) + woc.log.Debugf(ctx, "Node %s already completed", nodeName) if processedTmpl.Metrics != nil { // Check if this node completed between executions. If it did, emit metrics. @@ -2347,19 +2349,19 @@ func (woc *wfOperationCtx) recordWorkflowPhaseChange(ctx context.Context) { // optionally marks the workflow completed, which sets the finishedAt timestamp and completed label func (woc *wfOperationCtx) markWorkflowPhase(ctx context.Context, phase wfv1.WorkflowPhase, message string) { // Check whether or not the workflow needs to continue processing when it is completed - if phase.Completed() && (woc.checkTaskResultsInProgress() || woc.hasDaemonNodes()) { - woc.log.WithFields(log.Fields{"fromPhase": woc.wf.Status.Phase, "toPhase": phase}). - Debug("taskresults of workflow are incomplete or still have daemon nodes, so can't mark workflow completed") - woc.killDaemonedChildren("") + if phase.Completed() && (woc.checkTaskResultsInProgress(ctx) || woc.hasDaemonNodes()) { + woc.log.WithFields(ctx, logging.Fields{"fromPhase": woc.wf.Status.Phase, "toPhase": phase}). + Debug(ctx, "taskresults of workflow are incomplete or still have daemon nodes, so can't mark workflow completed") + woc.killDaemonedChildren(ctx, "") return } if woc.wf.Status.Phase != phase { if woc.wf.Status.Fulfilled() { - woc.log.WithFields(log.Fields{"fromPhase": woc.wf.Status.Phase, "toPhase": phase}). - Panic("workflow is already fulfilled") + woc.log.WithFields(ctx, logging.Fields{"fromPhase": woc.wf.Status.Phase, "toPhase": phase}). + Panic(ctx, "workflow is already fulfilled") } - woc.log.Infof("Updated phase %s -> %s", woc.wf.Status.Phase, phase) + woc.log.Infof(ctx, "Updated phase %s -> %s", woc.wf.Status.Phase, phase) woc.updated = true woc.wf.Status.Phase = phase woc.recordWorkflowPhaseChange(ctx) @@ -2387,7 +2389,7 @@ func (woc *wfOperationCtx) markWorkflowPhase(ctx context.Context, phase wfv1.Wor woc.wf.Status.EstimatedDuration = woc.estimateWorkflowDuration() } if woc.wf.Status.Message != message { - woc.log.Infof("Updated message %s -> %s", woc.wf.Status.Message, message) + woc.log.Infof(ctx, "Updated message %s -> %s", woc.wf.Status.Message, message) woc.updated = true woc.wf.Status.Message = message } @@ -2395,7 +2397,7 @@ func (woc *wfOperationCtx) markWorkflowPhase(ctx context.Context, phase wfv1.Wor if phase == wfv1.WorkflowError { entryNode, err := woc.wf.Status.Nodes.Get(woc.wf.ObjectMeta.Name) if err != nil { - woc.log.Errorf("was unable to obtain node for %s", woc.wf.ObjectMeta.Name) + woc.log.Errorf(ctx, "was unable to obtain node for %s", woc.wf.ObjectMeta.Name) } if (err == nil) && entryNode.Phase == wfv1.NodeRunning { entryNode.Phase = wfv1.NodeError @@ -2407,7 +2409,7 @@ func (woc *wfOperationCtx) markWorkflowPhase(ctx context.Context, phase wfv1.Wor switch phase { case wfv1.WorkflowSucceeded, wfv1.WorkflowFailed, wfv1.WorkflowError: - woc.log.Info("Marking workflow completed") + woc.log.Info(ctx, "Marking workflow completed") woc.wf.Status.FinishedAt = metav1.Time{Time: time.Now().UTC()} woc.globalParams[common.GlobalVarWorkflowDuration] = fmt.Sprintf("%f", woc.wf.Status.FinishedAt.Sub(woc.wf.Status.StartedAt.Time).Seconds()) if woc.wf.ObjectMeta.Labels == nil { @@ -2424,10 +2426,10 @@ func (woc *wfOperationCtx) markWorkflowPhase(ctx context.Context, phase wfv1.Wor } if woc.controller.wfArchive.IsEnabled() { if woc.controller.isArchivable(woc.wf) { - woc.log.Info("Marking workflow as pending archiving") + woc.log.Info(ctx, "Marking workflow as pending archiving") woc.wf.Labels[common.LabelKeyWorkflowArchivingStatus] = "Pending" } else { - woc.log.Info("Doesn't match with archive label selector. Skipping Archive") + woc.log.Info(ctx, "Doesn't match with archive label selector. Skipping Archive") } } woc.updated = true @@ -2462,16 +2464,16 @@ func (woc *wfOperationCtx) hasDaemonNodes() bool { return false } -func (woc *wfOperationCtx) GetNodeTemplate(node *wfv1.NodeStatus) (*wfv1.Template, error) { +func (woc *wfOperationCtx) GetNodeTemplate(ctx context.Context, node *wfv1.NodeStatus) (*wfv1.Template, error) { if node.TemplateRef != nil { tmplCtx, err := woc.createTemplateContext(node.GetTemplateScope()) if err != nil { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) return nil, err } - tmpl, err := tmplCtx.GetTemplateFromRef(node.TemplateRef) + tmpl, err := tmplCtx.GetTemplateFromRef(ctx, node.TemplateRef) if err != nil { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) return tmpl, err } return tmpl, nil @@ -2500,8 +2502,8 @@ func (woc *wfOperationCtx) markWorkflowError(ctx context.Context, err error) { var stepsOrDagSeparator = regexp.MustCompile(`^(\[\d+\])?\.`) // initializeExecutableNode initializes a node and stores the template. -func (woc *wfOperationCtx) initializeExecutableNode(nodeName string, nodeType wfv1.NodeType, templateScope string, executeTmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, phase wfv1.NodePhase, nodeFlag *wfv1.NodeFlag, messages ...string) *wfv1.NodeStatus { - node := woc.initializeNode(nodeName, nodeType, templateScope, orgTmpl, boundaryID, phase, nodeFlag) +func (woc *wfOperationCtx) initializeExecutableNode(ctx context.Context, nodeName string, nodeType wfv1.NodeType, templateScope string, executeTmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, phase wfv1.NodePhase, nodeFlag *wfv1.NodeFlag, messages ...string) *wfv1.NodeStatus { + node := woc.initializeNode(ctx, nodeName, nodeType, templateScope, orgTmpl, boundaryID, phase, nodeFlag) // Set the input values to the node. if executeTmpl.Inputs.HasInputs() { @@ -2534,39 +2536,44 @@ func (woc *wfOperationCtx) initializeExecutableNode(nodeName string, nodeType wf } // initializeNodeOrMarkError initializes an error node or mark a node if it already exists. -func (woc *wfOperationCtx) initializeNodeOrMarkError(node *wfv1.NodeStatus, nodeName string, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, nodeFlag *wfv1.NodeFlag, err error) *wfv1.NodeStatus { +func (woc *wfOperationCtx) initializeNodeOrMarkError(ctx context.Context, node *wfv1.NodeStatus, nodeName string, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, nodeFlag *wfv1.NodeFlag, err error) *wfv1.NodeStatus { if node != nil { - return woc.markNodeError(nodeName, err) + return woc.markNodeError(ctx, nodeName, err) } - return woc.initializeNode(nodeName, wfv1.NodeTypeSkipped, templateScope, orgTmpl, boundaryID, wfv1.NodeError, nodeFlag, err.Error()) + return woc.initializeNode(ctx, nodeName, wfv1.NodeTypeSkipped, templateScope, orgTmpl, boundaryID, wfv1.NodeError, nodeFlag, err.Error()) } // Creates a node status that is or will be cached -func (woc *wfOperationCtx) initializeCacheNode(nodeName string, resolvedTmpl *wfv1.Template, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, memStat *wfv1.MemoizationStatus, nodeFlag *wfv1.NodeFlag, messages ...string) *wfv1.NodeStatus { +func (woc *wfOperationCtx) initializeCacheNode(ctx context.Context, nodeName string, resolvedTmpl *wfv1.Template, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, memStat *wfv1.MemoizationStatus, nodeFlag *wfv1.NodeFlag, messages ...string) *wfv1.NodeStatus { if resolvedTmpl.Memoize == nil { err := fmt.Errorf("cannot initialize a cached node from a non-memoized template") - woc.log.WithFields(log.Fields{"namespace": woc.wf.Namespace, "wfName": woc.wf.Name}).WithError(err) + woc.log.WithFields(ctx, logging.Fields{"namespace": woc.wf.Namespace, "wfName": woc.wf.Name}).WithError(ctx, err) panic(err) } - woc.log.Debug("Initializing cached node ", nodeName, common.GetTemplateHolderString(orgTmpl), boundaryID) + woc.log.WithFields(ctx, logging.Fields{ + "nodeName": nodeName, + "templateHolder": common.GetTemplateHolderString(orgTmpl), + "boundaryID": boundaryID, + }, + ).Debugf(ctx, "Initializing cached node") - node := woc.initializeExecutableNode(nodeName, wfutil.GetNodeType(resolvedTmpl), templateScope, resolvedTmpl, orgTmpl, boundaryID, wfv1.NodePending, nodeFlag, messages...) + node := woc.initializeExecutableNode(ctx, nodeName, wfutil.GetNodeType(resolvedTmpl), templateScope, resolvedTmpl, orgTmpl, boundaryID, wfv1.NodePending, nodeFlag, messages...) node.MemoizationStatus = memStat return node } // Creates a node status that has been cached, completely initialized, and marked as finished -func (woc *wfOperationCtx) initializeCacheHitNode(nodeName string, resolvedTmpl *wfv1.Template, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, outputs *wfv1.Outputs, memStat *wfv1.MemoizationStatus, nodeFlag *wfv1.NodeFlag, messages ...string) *wfv1.NodeStatus { - node := woc.initializeCacheNode(nodeName, resolvedTmpl, templateScope, orgTmpl, boundaryID, memStat, nodeFlag, messages...) +func (woc *wfOperationCtx) initializeCacheHitNode(ctx context.Context, nodeName string, resolvedTmpl *wfv1.Template, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, outputs *wfv1.Outputs, memStat *wfv1.MemoizationStatus, nodeFlag *wfv1.NodeFlag, messages ...string) *wfv1.NodeStatus { + node := woc.initializeCacheNode(ctx, nodeName, resolvedTmpl, templateScope, orgTmpl, boundaryID, memStat, nodeFlag, messages...) node.Phase = wfv1.NodeSucceeded node.Outputs = outputs node.FinishedAt = metav1.Time{Time: time.Now().UTC()} return node } -func (woc *wfOperationCtx) initializeNode(nodeName string, nodeType wfv1.NodeType, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, phase wfv1.NodePhase, nodeFlag *wfv1.NodeFlag, messages ...string) *wfv1.NodeStatus { - woc.log.Debugf("Initializing node %s: template: %s, boundaryID: %s", nodeName, common.GetTemplateHolderString(orgTmpl), boundaryID) +func (woc *wfOperationCtx) initializeNode(ctx context.Context, nodeName string, nodeType wfv1.NodeType, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, phase wfv1.NodePhase, nodeFlag *wfv1.NodeFlag, messages ...string) *wfv1.NodeStatus { + woc.log.Debugf(ctx, "Initializing node %s: template: %s, boundaryID: %s", nodeName, common.GetTemplateHolderString(orgTmpl), boundaryID) nodeID := woc.wf.NodeID(nodeName) ok := woc.wf.Status.Nodes.Has(nodeID) @@ -2594,7 +2601,7 @@ func (woc *wfOperationCtx) initializeNode(nodeName string, nodeType wfv1.NodeTyp node.DisplayName = stepsOrDagSeparator.ReplaceAllString(node.DisplayName, "") } } else { - woc.log.Infof("was unable to obtain node for %s, letting display name to be nodeName", boundaryID) + woc.log.Infof(ctx, "was unable to obtain node for %s, letting display name to be nodeName", boundaryID) node.DisplayName = nodeName } @@ -2607,7 +2614,7 @@ func (woc *wfOperationCtx) initializeNode(nodeName string, nodeType wfv1.NodeTyp node.Message = messages[0] } woc.wf.Status.Nodes.Set(nodeID, node) - woc.log.Infof("%s node %v initialized %s%s", node.Type, node.ID, node.Phase, message) + woc.log.Infof(ctx, "%s node %v initialized %s%s", node.Type, node.ID, node.Phase, message) woc.updated = true return &node } @@ -2621,41 +2628,41 @@ func (woc *wfOperationCtx) updateAsCacheNode(node *wfv1.NodeStatus, memStat *wfv } // Update a node status that has been cached and marked as finished -func (woc *wfOperationCtx) updateAsCacheHitNode(node *wfv1.NodeStatus, outputs *wfv1.Outputs, memStat *wfv1.MemoizationStatus, message ...string) { +func (woc *wfOperationCtx) updateAsCacheHitNode(ctx context.Context, node *wfv1.NodeStatus, outputs *wfv1.Outputs, memStat *wfv1.MemoizationStatus, message ...string) { node.Phase = wfv1.NodeSucceeded node.Outputs = outputs node.FinishedAt = metav1.Time{Time: time.Now().UTC()} woc.updateAsCacheNode(node, memStat) - woc.log.Infof("%s node %v updated %s%s", node.Type, node.ID, node.Phase, message) + woc.log.Infof(ctx, "%s node %v updated %s%s", node.Type, node.ID, node.Phase, message) } // markNodePhase marks a node with the given phase, creating the node if necessary and handles timestamps -func (woc *wfOperationCtx) markNodePhase(nodeName string, phase wfv1.NodePhase, message ...string) *wfv1.NodeStatus { +func (woc *wfOperationCtx) markNodePhase(ctx context.Context, nodeName string, phase wfv1.NodePhase, message ...string) *wfv1.NodeStatus { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - woc.log.Warningf("workflow '%s' node '%s' uninitialized when marking as %v: %s", woc.wf.Name, nodeName, phase, message) + woc.log.Warningf(ctx, "workflow '%s' node '%s' uninitialized when marking as %v: %s", woc.wf.Name, nodeName, phase, message) node = &wfv1.NodeStatus{} } if node.Phase != phase { if node.Phase.Fulfilled() { - woc.log.WithFields(log.Fields{"nodeName": node.Name, "fromPhase": node.Phase, "toPhase": phase}). - Error("node is already fulfilled") + woc.log.WithFields(ctx, logging.Fields{"nodeName": node.Name, "fromPhase": node.Phase, "toPhase": phase}). + Error(ctx, "node is already fulfilled") } - woc.log.Infof("node %s phase %s -> %s", node.ID, node.Phase, phase) + woc.log.Infof(ctx, "node %s phase %s -> %s", node.ID, node.Phase, phase) node.Phase = phase woc.updated = true } if len(message) > 0 { if message[0] != node.Message { - woc.log.Infof("node %s message: %s", node.ID, message[0]) + woc.log.Infof(ctx, "node %s message: %s", node.ID, message[0]) node.Message = message[0] woc.updated = true } } if node.Fulfilled() && node.FinishedAt.IsZero() { node.FinishedAt = metav1.Time{Time: time.Now().UTC()} - woc.log.Infof("node %s finished: %s", node.ID, node.FinishedAt) + woc.log.Infof(ctx, "node %s finished: %s", node.ID, node.FinishedAt) woc.updated = true } woc.wf.Status.Nodes.Set(node.ID, *node) @@ -2671,7 +2678,7 @@ func (woc *wfOperationCtx) getPodByNode(node *wfv1.NodeStatus) (*apiv1.Pod, erro return woc.controller.getPod(woc.wf.GetNamespace(), podName) } -func (woc *wfOperationCtx) recordNodePhaseEvent(node *wfv1.NodeStatus) { +func (woc *wfOperationCtx) recordNodePhaseEvent(ctx context.Context, node *wfv1.NodeStatus) { message := fmt.Sprintf("%v node %s", node.Phase, node.Name) if node.Message != "" { message = message + ": " + node.Message @@ -2694,7 +2701,7 @@ func (woc *wfOperationCtx) recordNodePhaseEvent(node *wfv1.NodeStatus) { if eventConfig.SendAsPod { pod, err := woc.getPodByNode(node) if err != nil { - woc.log.WithError(err).Info("Error getting pod from workflow node") + woc.log.WithError(ctx, err).Info(ctx, "Error getting pod from workflow node") } if pod != nil { involvedObject = pod @@ -2713,7 +2720,7 @@ func (woc *wfOperationCtx) recordNodePhaseEvent(node *wfv1.NodeStatus) { // recordNodePhaseChangeEvents creates WorkflowNode Kubernetes events for each node // that has changes logged during this execution of the operator loop. -func (woc *wfOperationCtx) recordNodePhaseChangeEvents(old wfv1.Nodes, new wfv1.Nodes) { +func (woc *wfOperationCtx) recordNodePhaseChangeEvents(ctx context.Context, old wfv1.Nodes, new wfv1.Nodes) { if !woc.controller.Config.NodeEvents.IsEnabled() { return } @@ -2728,32 +2735,32 @@ func (woc *wfOperationCtx) recordNodePhaseChangeEvents(old wfv1.Nodes, new wfv1. if oldNode.Phase == wfv1.NodePending && newNode.Completed() { ephemeralNode := newNode.DeepCopy() ephemeralNode.Phase = wfv1.NodeRunning - woc.recordNodePhaseEvent(ephemeralNode) + woc.recordNodePhaseEvent(ctx, ephemeralNode) } - woc.recordNodePhaseEvent(&newNode) + woc.recordNodePhaseEvent(ctx, &newNode) } else { if newNode.Phase == wfv1.NodeRunning { - woc.recordNodePhaseEvent(&newNode) + woc.recordNodePhaseEvent(ctx, &newNode) } else if newNode.Completed() { ephemeralNode := newNode.DeepCopy() ephemeralNode.Phase = wfv1.NodeRunning - woc.recordNodePhaseEvent(ephemeralNode) - woc.recordNodePhaseEvent(&newNode) + woc.recordNodePhaseEvent(ctx, ephemeralNode) + woc.recordNodePhaseEvent(ctx, &newNode) } } } } // markNodeError is a convenience method to mark a node with an error and set the message from the error -func (woc *wfOperationCtx) markNodeError(nodeName string, err error) *wfv1.NodeStatus { - woc.log.WithError(err).WithField("nodeName", nodeName).Error("Mark error node") - return woc.markNodePhase(nodeName, wfv1.NodeError, err.Error()) +func (woc *wfOperationCtx) markNodeError(ctx context.Context, nodeName string, err error) *wfv1.NodeStatus { + woc.log.WithError(ctx, err).WithField(ctx, "nodeName", nodeName).Error(ctx, "Mark error node") + return woc.markNodePhase(ctx, nodeName, wfv1.NodeError, err.Error()) } // markNodePending is a convenience method to mark a node and set the message from the error -func (woc *wfOperationCtx) markNodePending(nodeName string, err error) *wfv1.NodeStatus { - woc.log.Infof("Mark node %s as Pending, due to: %v", nodeName, err) - return woc.markNodePhase(nodeName, wfv1.NodePending, err.Error()) // this error message will not change often +func (woc *wfOperationCtx) markNodePending(ctx context.Context, nodeName string, err error) *wfv1.NodeStatus { + woc.log.Infof(ctx, "Mark node %s as Pending, due to: %v", nodeName, err) + return woc.markNodePhase(ctx, nodeName, wfv1.NodePending, err.Error()) // this error message will not change often } // markNodeWaitingForLock is a convenience method to mark that a node is waiting for a lock @@ -2784,13 +2791,13 @@ func (woc *wfOperationCtx) markNodeWaitingForLock(nodeName string, lockName stri return node, nil } -func (woc *wfOperationCtx) findLeafNodeWithType(boundaryID string, nodeType wfv1.NodeType) *wfv1.NodeStatus { +func (woc *wfOperationCtx) findLeafNodeWithType(ctx context.Context, boundaryID string, nodeType wfv1.NodeType) *wfv1.NodeStatus { var leafNode *wfv1.NodeStatus var dfs func(nodeID string) dfs = func(nodeID string) { node, err := woc.wf.Status.Nodes.Get(nodeID) if err != nil { - woc.log.Errorf("was unable to obtain node for %s", nodeID) + woc.log.Errorf(ctx, "was unable to obtain node for %s", nodeID) return } if node.Type == nodeType { @@ -2805,9 +2812,9 @@ func (woc *wfOperationCtx) findLeafNodeWithType(boundaryID string, nodeType wfv1 } // checkParallelism checks if the given template is able to be executed, considering the current active pods and workflow/template parallelism -func (woc *wfOperationCtx) checkParallelism(tmpl *wfv1.Template, node *wfv1.NodeStatus, boundaryID string) error { +func (woc *wfOperationCtx) checkParallelism(ctx context.Context, tmpl *wfv1.Template, node *wfv1.NodeStatus, boundaryID string) error { if woc.execWf.Spec.Parallelism != nil && woc.activePods >= *woc.execWf.Spec.Parallelism { - woc.log.Infof("workflow active pod spec parallelism reached %d/%d", woc.activePods, *woc.execWf.Spec.Parallelism) + woc.log.Infof(ctx, "workflow active pod spec parallelism reached %d/%d", woc.activePods, *woc.execWf.Spec.Parallelism) return ErrParallelismReached } @@ -2817,18 +2824,18 @@ func (woc *wfOperationCtx) checkParallelism(tmpl *wfv1.Template, node *wfv1.Node if tmpl.IsFailFast() && woc.getUnsuccessfulChildren(node.ID) > 0 { if woc.getActivePods(node.ID) == 0 { if tmpl.GetType() == wfv1.TemplateTypeSteps { - if leafStepGroupNode := woc.findLeafNodeWithType(node.ID, wfv1.NodeTypeStepGroup); leafStepGroupNode != nil { - woc.markNodePhase(leafStepGroupNode.Name, wfv1.NodeFailed, "template has failed or errored children and failFast enabled") + if leafStepGroupNode := woc.findLeafNodeWithType(ctx, node.ID, wfv1.NodeTypeStepGroup); leafStepGroupNode != nil { + woc.markNodePhase(ctx, leafStepGroupNode.Name, wfv1.NodeFailed, "template has failed or errored children and failFast enabled") } } - woc.markNodePhase(node.Name, wfv1.NodeFailed, "template has failed or errored children and failFast enabled") + woc.markNodePhase(ctx, node.Name, wfv1.NodeFailed, "template has failed or errored children and failFast enabled") } return ErrParallelismReached } // Check parallelism if tmpl.HasParallelism() && woc.getActivePods(node.ID) >= *tmpl.Parallelism { - woc.log.Infof("template (node %s) active children parallelism exceeded %d", node.ID, *tmpl.Parallelism) + woc.log.Infof(ctx, "template (node %s) active children parallelism exceeded %d", node.ID, *tmpl.Parallelism) return ErrParallelismReached } } @@ -2840,7 +2847,7 @@ func (woc *wfOperationCtx) checkParallelism(tmpl *wfv1.Template, node *wfv1.Node return err } - boundaryTemplate, templateStored, err := woc.GetTemplateByBoundaryID(boundaryID) + boundaryTemplate, templateStored, err := woc.GetTemplateByBoundaryID(ctx, boundaryID) if err != nil { return err } @@ -2853,18 +2860,18 @@ func (woc *wfOperationCtx) checkParallelism(tmpl *wfv1.Template, node *wfv1.Node if boundaryTemplate.IsFailFast() && woc.getUnsuccessfulChildren(boundaryID) > 0 { if woc.getActivePods(boundaryID) == 0 { if boundaryTemplate.GetType() == wfv1.TemplateTypeSteps { - if leafStepGroupNode := woc.findLeafNodeWithType(boundaryID, wfv1.NodeTypeStepGroup); leafStepGroupNode != nil { - woc.markNodePhase(leafStepGroupNode.Name, wfv1.NodeFailed, "template has failed or errored children and failFast enabled") + if leafStepGroupNode := woc.findLeafNodeWithType(ctx, boundaryID, wfv1.NodeTypeStepGroup); leafStepGroupNode != nil { + woc.markNodePhase(ctx, leafStepGroupNode.Name, wfv1.NodeFailed, "template has failed or errored children and failFast enabled") } } - woc.markNodePhase(boundaryNode.Name, wfv1.NodeFailed, "template has failed or errored children and failFast enabled") + woc.markNodePhase(ctx, boundaryNode.Name, wfv1.NodeFailed, "template has failed or errored children and failFast enabled") } return ErrParallelismReached } // Check parallelism if boundaryTemplate.HasParallelism() && woc.getActiveChildren(boundaryID) >= *boundaryTemplate.Parallelism { - woc.log.Infof("template (node %s) active children parallelism exceeded %d", boundaryID, *boundaryTemplate.Parallelism) + woc.log.Infof(ctx, "template (node %s) active children parallelism exceeded %d", boundaryID, *boundaryTemplate.Parallelism) return ErrParallelismReached } } @@ -2874,17 +2881,17 @@ func (woc *wfOperationCtx) checkParallelism(tmpl *wfv1.Template, node *wfv1.Node func (woc *wfOperationCtx) executeContainer(ctx context.Context, nodeName string, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) (*wfv1.NodeStatus, error) { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - node = woc.initializeExecutableNode(nodeName, wfv1.NodeTypePod, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) + node = woc.initializeExecutableNode(ctx, nodeName, wfv1.NodeTypePod, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) } // Check if the output of this container is referenced elsewhere in the Workflow. If so, make sure to include it during // execution. - includeScriptOutput, err := woc.includeScriptOutput(nodeName, opts.boundaryID) + includeScriptOutput, err := woc.includeScriptOutput(ctx, nodeName, opts.boundaryID) if err != nil { return node, err } - woc.log.Debugf("Executing node %s with container template: %v\n", nodeName, tmpl.Name) + woc.log.Debugf(ctx, "Executing node %s with container template: %v\n", nodeName, tmpl.Name) _, err = woc.createWorkflowPod(ctx, nodeName, []apiv1.Container{*tmpl.Container}, tmpl, &createWorkflowPodOpts{ includeScriptOutput: includeScriptOutput, onExitPod: opts.onExitTemplate, @@ -2892,16 +2899,16 @@ func (woc *wfOperationCtx) executeContainer(ctx context.Context, nodeName string }) if err != nil { - return woc.requeueIfTransientErr(err, node.Name) + return woc.requeueIfTransientErr(ctx, err, node.Name) } return node, err } -func (woc *wfOperationCtx) getOutboundNodes(nodeID string) []string { +func (woc *wfOperationCtx) getOutboundNodes(ctx context.Context, nodeID string) []string { node, err := woc.wf.Status.Nodes.Get(nodeID) if err != nil { - woc.log.Panicf("was unable to obtain node for %s", nodeID) + woc.log.Panicf(ctx, "was unable to obtain node for %s", nodeID) } switch node.Type { case wfv1.NodeTypeSkipped, wfv1.NodeTypeSuspend, wfv1.NodeTypeHTTP, wfv1.NodeTypePlugin: @@ -2913,7 +2920,7 @@ func (woc *wfOperationCtx) getOutboundNodes(nodeID string) []string { if err != nil { return []string{node.ID} } - _, parentTemplate, _, err := tmplCtx.ResolveTemplate(node) + _, parentTemplate, _, err := tmplCtx.ResolveTemplate(ctx, node) if err != nil { return []string{node.ID} } @@ -2933,13 +2940,13 @@ func (woc *wfOperationCtx) getOutboundNodes(nodeID string) []string { for _, child := range node.Children { childNode, err := woc.wf.Status.Nodes.Get(child) if err != nil { - woc.log.Panicf("was unable to obtain child node for %s", child) + woc.log.Panicf(ctx, "was unable to obtain child node for %s", child) } // child node has different boundaryID meaning current node is the deepest outbound node if node.Type == wfv1.NodeTypeContainer && node.BoundaryID != childNode.BoundaryID { outboundNodes = append(outboundNodes, node.ID) } else { - outboundNodes = append(outboundNodes, woc.getOutboundNodes(child)...) + outboundNodes = append(outboundNodes, woc.getOutboundNodes(ctx, child)...) } } return outboundNodes @@ -2955,7 +2962,7 @@ func (woc *wfOperationCtx) getOutboundNodes(nodeID string) []string { } outbound := make([]string, 0) for _, outboundNodeID := range node.OutboundNodes { - outbound = append(outbound, woc.getOutboundNodes(outboundNodeID)...) + outbound = append(outbound, woc.getOutboundNodes(ctx, outboundNodeID)...) } return outbound } @@ -3090,21 +3097,21 @@ loop: func (woc *wfOperationCtx) executeScript(ctx context.Context, nodeName string, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) (*wfv1.NodeStatus, error) { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - node = woc.initializeExecutableNode(nodeName, wfv1.NodeTypePod, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) + node = woc.initializeExecutableNode(ctx, nodeName, wfv1.NodeTypePod, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) } else if !node.Pending() { return node, nil } // Check if the output of this script is referenced elsewhere in the Workflow. If so, make sure to include it during // execution. - includeScriptOutput, err := woc.includeScriptOutput(nodeName, opts.boundaryID) + includeScriptOutput, err := woc.includeScriptOutput(ctx, nodeName, opts.boundaryID) if err != nil { return node, err } mainCtr := tmpl.Script.Container if len(tmpl.Script.Source) == 0 { - woc.log.Warn("'script.source' is empty, suggest change template into 'container'") + woc.log.Warn(ctx, "'script.source' is empty, suggest change template into 'container'") } else { mainCtr.Args = append(mainCtr.Args, common.ExecutorScriptSourcePath) } @@ -3114,16 +3121,16 @@ func (woc *wfOperationCtx) executeScript(ctx context.Context, nodeName string, t executionDeadline: opts.executionDeadline, }) if err != nil { - return woc.requeueIfTransientErr(err, node.Name) + return woc.requeueIfTransientErr(ctx, err, node.Name) } return node, err } -func (woc *wfOperationCtx) requeueIfTransientErr(err error, nodeName string) (*wfv1.NodeStatus, error) { +func (woc *wfOperationCtx) requeueIfTransientErr(ctx context.Context, err error, nodeName string) (*wfv1.NodeStatus, error) { if errorsutil.IsTransientErr(err) || err == ErrResourceRateLimitReached { // Our error was most likely caused by a lack of resources. woc.requeue() - return woc.markNodePending(nodeName, err), nil + return woc.markNodePending(ctx, nodeName, err), nil } return nil, err } @@ -3187,15 +3194,15 @@ func (woc *wfOperationCtx) addOutputsToLocalScope(prefix string, outputs *wfv1.O } } -func (woc *wfOperationCtx) addOutputsToGlobalScope(outputs *wfv1.Outputs) { +func (woc *wfOperationCtx) addOutputsToGlobalScope(ctx context.Context, outputs *wfv1.Outputs) { if outputs == nil { return } for _, param := range outputs.Parameters { - woc.addParamToGlobalScope(param) + woc.addParamToGlobalScope(ctx, param) } for _, art := range outputs.Artifacts { - woc.addArtifactToGlobalScope(art) + woc.addArtifactToGlobalScope(ctx, art) } } @@ -3348,7 +3355,7 @@ func aggregatedJsonValueList(valueList []string) (string, error) { } // addParamToGlobalScope exports any desired node outputs to the global scope, and adds it to the global outputs. -func (woc *wfOperationCtx) addParamToGlobalScope(param wfv1.Parameter) { +func (woc *wfOperationCtx) addParamToGlobalScope(ctx context.Context, param wfv1.Parameter) { if param.GlobalName == "" { return } @@ -3356,7 +3363,7 @@ func (woc *wfOperationCtx) addParamToGlobalScope(param wfv1.Parameter) { if param.HasValue() { woc.globalParams[paramName] = param.GetValue() } - wfUpdated := wfutil.AddParamToGlobalScope(woc.wf, woc.log, param) + wfUpdated := wfutil.AddParamToGlobalScope(ctx, woc.wf, woc.log, param) if wfUpdated { woc.updated = true } @@ -3364,7 +3371,7 @@ func (woc *wfOperationCtx) addParamToGlobalScope(param wfv1.Parameter) { // addArtifactToGlobalScope exports any desired node outputs to the global scope // Optionally adds to a local scope if supplied -func (woc *wfOperationCtx) addArtifactToGlobalScope(art wfv1.Artifact) { +func (woc *wfOperationCtx) addArtifactToGlobalScope(ctx context.Context, art wfv1.Artifact) { if art.GlobalName == "" { return } @@ -3378,7 +3385,7 @@ func (woc *wfOperationCtx) addArtifactToGlobalScope(art wfv1.Artifact) { art.Path = "" if !reflect.DeepEqual(woc.wf.Status.Outputs.Artifacts[i], art) { woc.wf.Status.Outputs.Artifacts[i] = art - woc.log.Infof("overwriting %s: %v", globalArtName, art) + woc.log.Infof(ctx, "overwriting %s: %v", globalArtName, art) woc.updated = true } return @@ -3391,19 +3398,19 @@ func (woc *wfOperationCtx) addArtifactToGlobalScope(art wfv1.Artifact) { art.Name = art.GlobalName art.GlobalName = "" art.Path = "" - woc.log.Infof("setting %s: %v", globalArtName, art) + woc.log.Infof(ctx, "setting %s: %v", globalArtName, art) woc.wf.Status.Outputs.Artifacts = append(woc.wf.Status.Outputs.Artifacts, art) woc.updated = true } // addChildNode adds a nodeID as a child to a parent // parent and child are both node names -func (woc *wfOperationCtx) addChildNode(parent string, child string) { +func (woc *wfOperationCtx) addChildNode(ctx context.Context, parent string, child string) { parentID := woc.wf.NodeID(parent) childID := woc.wf.NodeID(child) node, err := woc.wf.Status.Nodes.Get(parentID) if err != nil { - woc.log.Panicf("was unable to obtain node for %s", parentID) + woc.log.Panicf(ctx, "was unable to obtain node for %s", parentID) } for _, nodeID := range node.Children { if childID == nodeID { @@ -3421,7 +3428,7 @@ func (woc *wfOperationCtx) executeResource(ctx context.Context, nodeName string, node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - node = woc.initializeExecutableNode(nodeName, wfv1.NodeTypePod, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) + node = woc.initializeExecutableNode(ctx, nodeName, wfv1.NodeTypePod, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) } else if !node.Pending() { return node, nil } @@ -3448,7 +3455,7 @@ func (woc *wfOperationCtx) executeResource(ctx context.Context, nodeName string, mainCtr.Command = append([]string{"argoexec", "resource", tmpl.Resource.Action}, woc.getExecutorLogOpts()...) _, err = woc.createWorkflowPod(ctx, nodeName, []apiv1.Container{*mainCtr}, tmpl, &createWorkflowPodOpts{onExitPod: opts.onExitTemplate, executionDeadline: opts.executionDeadline}) if err != nil { - return woc.requeueIfTransientErr(err, node.Name) + return woc.requeueIfTransientErr(ctx, err, node.Name) } return node, err @@ -3457,7 +3464,7 @@ func (woc *wfOperationCtx) executeResource(ctx context.Context, nodeName string, func (woc *wfOperationCtx) executeData(ctx context.Context, nodeName string, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) (*wfv1.NodeStatus, error) { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - node = woc.initializeExecutableNode(nodeName, wfv1.NodeTypePod, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) + node = woc.initializeExecutableNode(ctx, nodeName, wfv1.NodeTypePod, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) } else if !node.Pending() { return node, nil } @@ -3471,19 +3478,19 @@ func (woc *wfOperationCtx) executeData(ctx context.Context, nodeName string, tem mainCtr.Command = append([]string{"argoexec", "data", string(dataTemplate)}, woc.getExecutorLogOpts()...) _, err = woc.createWorkflowPod(ctx, nodeName, []apiv1.Container{*mainCtr}, tmpl, &createWorkflowPodOpts{onExitPod: opts.onExitTemplate, executionDeadline: opts.executionDeadline, includeScriptOutput: true}) if err != nil { - return woc.requeueIfTransientErr(err, node.Name) + return woc.requeueIfTransientErr(ctx, err, node.Name) } return node, nil } -func (woc *wfOperationCtx) executeSuspend(nodeName string, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) (*wfv1.NodeStatus, error) { +func (woc *wfOperationCtx) executeSuspend(ctx context.Context, nodeName string, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) (*wfv1.NodeStatus, error) { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - node = woc.initializeExecutableNode(nodeName, wfv1.NodeTypeSuspend, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) - woc.resolveInputFieldsForSuspendNode(node) + node = woc.initializeExecutableNode(ctx, nodeName, wfv1.NodeTypeSuspend, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) + woc.resolveInputFieldsForSuspendNode(ctx, node) } - woc.log.Infof("node %s suspended", nodeName) + woc.log.Infof(ctx, "node %s suspended", nodeName) // If there is either an active workflow deadline, or if this node is suspended with a duration, then the workflow // will need to be requeued after a certain amount of time @@ -3502,11 +3509,11 @@ func (woc *wfOperationCtx) executeSuspend(nodeName string, templateScope string, requeueTime = &suspendDeadline if time.Now().UTC().After(suspendDeadline) { // Suspension is expired, node can be resumed - woc.log.Infof("auto resuming node %s", nodeName) + woc.log.Infof(ctx, "auto resuming node %s", nodeName) if err := wfutil.OverrideOutputParametersWithDefault(node.Outputs); err != nil { return node, err } - _ = woc.markNodePhase(nodeName, wfv1.NodeSucceeded) + _ = woc.markNodePhase(ctx, nodeName, wfv1.NodeSucceeded) return node, nil } } @@ -3524,11 +3531,11 @@ func (woc *wfOperationCtx) executeSuspend(nodeName string, templateScope string, woc.requeueAfter(time.Until(*requeueTime)) } - _ = woc.markNodePhase(nodeName, wfv1.NodeRunning) + _ = woc.markNodePhase(ctx, nodeName, wfv1.NodeRunning) return node, nil } -func (woc *wfOperationCtx) resolveInputFieldsForSuspendNode(node *wfv1.NodeStatus) { +func (woc *wfOperationCtx) resolveInputFieldsForSuspendNode(ctx context.Context, node *wfv1.NodeStatus) { if node.Inputs == nil { return } @@ -3540,7 +3547,7 @@ func (woc *wfOperationCtx) resolveInputFieldsForSuspendNode(node *wfv1.NodeStatu tempParameter := wfv1.Parameter{} if err := json.Unmarshal([]byte(value), &tempParameter); err != nil { - woc.log.Debugf("Unable to parse input string %s to Parameter %s, %v", value, parameter.Name, err) + woc.log.Debugf(ctx, "Unable to parse input string %s to Parameter %s, %v", value, parameter.Name, err) continue } @@ -3724,7 +3731,7 @@ func (woc *wfOperationCtx) createTemplateContext(scope wfv1.ResourceScope, resou } else { clusterWorkflowTemplateGetter = &templateresolution.NullClusterWorkflowTemplateGetter{} } - ctx := templateresolution.NewContext(woc.controller.wftmplInformer.Lister().WorkflowTemplates(woc.wf.Namespace), clusterWorkflowTemplateGetter, woc.execWf, woc.wf) + ctx := templateresolution.NewContext(woc.controller.wftmplInformer.Lister().WorkflowTemplates(woc.wf.Namespace), clusterWorkflowTemplateGetter, woc.execWf, woc.wf, woc.log) switch scope { case wfv1.ResourceScopeNamespaced: @@ -3745,7 +3752,7 @@ func (woc *wfOperationCtx) computeMetrics(ctx context.Context, metricList []*wfv } if metricTmpl.Help == "" { - woc.reportMetricEmissionError(fmt.Sprintf("metric '%s' must contain a help string under 'help: ' field", metricTmpl.Name)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("metric '%s' must contain a help string under 'help: ' field", metricTmpl.Name)) continue } @@ -3754,19 +3761,19 @@ func (woc *wfOperationCtx) computeMetrics(ctx context.Context, metricList []*wfv // might be realtime ({{workflow.duration}} will not be substituted the same way if it's realtime or if it isn't). metricTmplBytes, err := json.Marshal(metricTmpl) if err != nil { - woc.reportMetricEmissionError(fmt.Sprintf("unable to substitute parameters for metric '%s' (marshal): %s", metricTmpl.Name, err)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("unable to substitute parameters for metric '%s' (marshal): %s", metricTmpl.Name, err)) continue } replacedValue, err := template.Replace(string(metricTmplBytes), localScope, false) if err != nil { - woc.reportMetricEmissionError(fmt.Sprintf("unable to substitute parameters for metric '%s': %s", metricTmpl.Name, err)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("unable to substitute parameters for metric '%s': %s", metricTmpl.Name, err)) continue } var metricTmplSubstituted wfv1.Prometheus err = json.Unmarshal([]byte(replacedValue), &metricTmplSubstituted) if err != nil { - woc.reportMetricEmissionError(fmt.Sprintf("unable to substitute parameters for metric '%s' (unmarshal): %s", metricTmpl.Name, err)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("unable to substitute parameters for metric '%s' (unmarshal): %s", metricTmpl.Name, err)) continue } // Only substitute non-value fields here. Value field substitution happens below @@ -3777,7 +3784,7 @@ func (woc *wfOperationCtx) computeMetrics(ctx context.Context, metricList []*wfv proceed, err := shouldExecute(metricTmpl.When) if err != nil { - woc.reportMetricEmissionError(fmt.Sprintf("unable to compute 'when' clause for metric '%s': %s", woc.wf.ObjectMeta.Name, err)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("unable to compute 'when' clause for metric '%s': %s", woc.wf.ObjectMeta.Name, err)) continue } if !proceed { @@ -3788,18 +3795,18 @@ func (woc *wfOperationCtx) computeMetrics(ctx context.Context, metricList []*wfv // Finally substitute value parameters value := metricTmpl.Gauge.Value if !(strings.HasPrefix(value, "{{") && strings.HasSuffix(value, "}}")) { - woc.reportMetricEmissionError("real time metrics can only be used with metric variables") + woc.reportMetricEmissionError(ctx, "real time metrics can only be used with metric variables") continue } value = strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(value, "{{"), "}}")) valueFunc, ok := realTimeScope[value] if !ok { - woc.reportMetricEmissionError(fmt.Sprintf("'%s' is not available as a real time metric", value)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("'%s' is not available as a real time metric", value)) continue } err = woc.controller.metrics.UpsertCustomMetric(ctx, metricTmpl, string(woc.wf.UID), valueFunc) if err != nil { - woc.reportMetricEmissionError(fmt.Sprintf("could not construct metric '%s': %s", metricTmpl.Name, err)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("could not construct metric '%s': %s", metricTmpl.Name, err)) continue } continue @@ -3811,20 +3818,20 @@ func (woc *wfOperationCtx) computeMetrics(ctx context.Context, metricList []*wfv metricValueStringJson, err := json.Marshal(metricValueString) if err != nil { - woc.reportMetricEmissionError(fmt.Sprintf("unable to marshal metric to JSON for templating '%s': %s", metricSpec.Name, err)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("unable to marshal metric to JSON for templating '%s': %s", metricSpec.Name, err)) continue } replacedValueJson, err := template.Replace(string(metricValueStringJson), localScope, false) if err != nil { - woc.reportMetricEmissionError(fmt.Sprintf("unable to substitute parameters for metric '%s': %s", metricSpec.Name, err)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("unable to substitute parameters for metric '%s': %s", metricSpec.Name, err)) continue } var replacedStringJson string err = json.Unmarshal([]byte(replacedValueJson), &replacedStringJson) if err != nil { - woc.reportMetricEmissionError(fmt.Sprintf("unable to unmarshal templated metric JSON '%s': %s", metricSpec.Name, err)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("unable to unmarshal templated metric JSON '%s': %s", metricSpec.Name, err)) continue } @@ -3832,7 +3839,7 @@ func (woc *wfOperationCtx) computeMetrics(ctx context.Context, metricList []*wfv err = woc.controller.metrics.UpsertCustomMetric(ctx, metricSpec, string(woc.wf.UID), nil) if err != nil { - woc.reportMetricEmissionError(fmt.Sprintf("could not construct metric '%s': %s", metricSpec.Name, err)) + woc.reportMetricEmissionError(ctx, fmt.Sprintf("could not construct metric '%s': %s", metricSpec.Name, err)) continue } continue @@ -3840,7 +3847,7 @@ func (woc *wfOperationCtx) computeMetrics(ctx context.Context, metricList []*wfv } } -func (woc *wfOperationCtx) reportMetricEmissionError(errorString string) { +func (woc *wfOperationCtx) reportMetricEmissionError(ctx context.Context, errorString string) { woc.wf.Status.Conditions.UpsertConditionMessage( wfv1.Condition{ Status: metav1.ConditionTrue, @@ -3848,7 +3855,7 @@ func (woc *wfOperationCtx) reportMetricEmissionError(errorString string) { Message: errorString, }) woc.updated = true - woc.log.Error(errorString) + woc.log.Error(ctx, errorString) } func (woc *wfOperationCtx) createPDBResource(ctx context.Context) error { @@ -3877,12 +3884,12 @@ func (woc *wfOperationCtx) createPDBResource(ctx context.Context) error { _, err := woc.controller.kubeclientset.PolicyV1().PodDisruptionBudgets(woc.wf.Namespace).Create(ctx, &newPDB, metav1.CreateOptions{}) if err != nil { if apierr.IsAlreadyExists(err) { - woc.log.Info("PDB resource already exists for workflow.") + woc.log.Info(ctx, "PDB resource already exists for workflow.") return nil } return err } - woc.log.Info("Created PDB resource for workflow.") + woc.log.Info(ctx, "Created PDB resource for workflow.") woc.updated = true return nil } @@ -3899,21 +3906,21 @@ func (woc *wfOperationCtx) deletePDBResource(ctx context.Context) error { return !errorsutil.IsTransientErr(err), err }) if err != nil { - woc.log.WithField("err", err).Error("Unable to delete PDB resource for workflow.") + woc.log.WithField(ctx, "err", err).Error(ctx, "Unable to delete PDB resource for workflow.") return err } - woc.log.Info("Deleted PDB resource for workflow.") + woc.log.Info(ctx, "Deleted PDB resource for workflow.") return nil } // Check if the output of this node is referenced elsewhere in the Workflow. If so, make sure to include it during // execution. -func (woc *wfOperationCtx) includeScriptOutput(nodeName, boundaryID string) (bool, error) { +func (woc *wfOperationCtx) includeScriptOutput(ctx context.Context, nodeName, boundaryID string) (bool, error) { if boundaryID == "" { return false, nil } - parentTemplate, templateStored, err := woc.GetTemplateByBoundaryID(boundaryID) + parentTemplate, templateStored, err := woc.GetTemplateByBoundaryID(ctx, boundaryID) if err != nil { return false, err } @@ -3936,7 +3943,7 @@ func (woc *wfOperationCtx) fetchWorkflowSpec(ctx context.Context) (wfv1.Workflow // Logic for workflow refers Workflow template if woc.wf.Spec.WorkflowTemplateRef.ClusterScope { // not-woc-misuse if woc.controller.cwftmplInformer == nil { - woc.log.WithError(err).Error("clusterWorkflowTemplate RBAC is missing") + woc.log.WithError(ctx, err).Error(ctx, "clusterWorkflowTemplate RBAC is missing") return nil, fmt.Errorf("cannot get resource clusterWorkflowTemplate at cluster scope") } woc.controller.metrics.CountWorkflowTemplate(ctx, metrics.WorkflowNew, woc.wf.Spec.WorkflowTemplateRef.Name, woc.wf.Namespace, true) // not-woc-misuse diff --git a/workflow/controller/operator_agent_test.go b/workflow/controller/operator_agent_test.go index 399ebcf594b3..606ec1a2a0d0 100644 --- a/workflow/controller/operator_agent_test.go +++ b/workflow/controller/operator_agent_test.go @@ -33,7 +33,7 @@ func TestHTTPTemplate(t *testing.T) { t.Run("ExecuteHTTPTemplate", func(t *testing.T) { ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pod, err := controller.kubeclientset.CoreV1().Pods(woc.wf.Namespace).Get(ctx, woc.getAgentPodName(), metav1.GetOptions{}) require.NoError(t, err) @@ -72,7 +72,7 @@ func TestHTTPTemplateWithoutServiceAccount(t *testing.T) { t.Run("ExecuteHTTPTemplateWithoutServiceAccount", func(t *testing.T) { ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) _, err := controller.kubeclientset.CoreV1().Pods(woc.wf.Namespace).Get(ctx, woc.getAgentPodName(), metav1.GetOptions{}) require.Error(t, err, `pods "%s" not found`, woc.getAgentPodName()) diff --git a/workflow/controller/operator_concurrency_test.go b/workflow/controller/operator_concurrency_test.go index d73461dc77fa..478033d01e98 100644 --- a/workflow/controller/operator_concurrency_test.go +++ b/workflow/controller/operator_concurrency_test.go @@ -175,7 +175,7 @@ func TestSemaphoreTmplLevel(t *testing.T) { wf.Name = "one" wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) // acquired the lock woc.operate(ctx) @@ -192,7 +192,7 @@ func TestSemaphoreTmplLevel(t *testing.T) { wf_Two.Name = "two" wf_Two, err = controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf_Two, metav1.CreateOptions{}) require.NoError(t, err) - woc_two := newWorkflowOperationCtx(wf_Two, controller) + woc_two := newWorkflowOperationCtx(ctx, wf_Two, controller) // Try Acquire the lock woc_two.operate(ctx) @@ -207,12 +207,12 @@ func TestSemaphoreTmplLevel(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodFailed) // Release the lock - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Nil(t, woc.wf.Status.Synchronization) // Try to acquired the lock - woc_two = newWorkflowOperationCtx(woc_two.wf, controller) + woc_two = newWorkflowOperationCtx(ctx, woc_two.wf, controller) woc_two.operate(ctx) assert.NotNil(t, woc_two.wf.Status.Synchronization) assert.NotNil(t, woc_two.wf.Status.Synchronization.Semaphore) @@ -236,7 +236,7 @@ func TestSemaphoreScriptTmplLevel(t *testing.T) { wf.Name = "one" wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) // acquired the lock woc.operate(ctx) @@ -253,7 +253,7 @@ func TestSemaphoreScriptTmplLevel(t *testing.T) { wf_Two.Name = "two" wf_Two, err = controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf_Two, metav1.CreateOptions{}) require.NoError(t, err) - woc_two := newWorkflowOperationCtx(wf_Two, controller) + woc_two := newWorkflowOperationCtx(ctx, wf_Two, controller) // Try Acquire the lock woc_two.operate(ctx) @@ -267,12 +267,12 @@ func TestSemaphoreScriptTmplLevel(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodFailed) // Release the lock - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Nil(t, woc.wf.Status.Synchronization) // Try to acquired the lock - woc_two = newWorkflowOperationCtx(woc_two.wf, controller) + woc_two = newWorkflowOperationCtx(ctx, woc_two.wf, controller) woc_two.operate(ctx) assert.NotNil(t, woc_two.wf.Status.Synchronization) assert.NotNil(t, woc_two.wf.Status.Synchronization.Semaphore) @@ -297,7 +297,7 @@ func TestSemaphoreScriptConfigMapInDifferentNamespace(t *testing.T) { wf.Namespace = "namespace-one" wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) // acquired the lock woc.operate(ctx) @@ -315,7 +315,7 @@ func TestSemaphoreScriptConfigMapInDifferentNamespace(t *testing.T) { wf_Two.Namespace = "namespace-two" wf_Two, err = controller.wfclientset.ArgoprojV1alpha1().Workflows(wf_Two.Namespace).Create(ctx, wf_Two, metav1.CreateOptions{}) require.NoError(t, err) - woc_two := newWorkflowOperationCtx(wf_Two, controller) + woc_two := newWorkflowOperationCtx(ctx, wf_Two, controller) // Try Acquire the lock woc_two.operate(ctx) @@ -329,12 +329,12 @@ func TestSemaphoreScriptConfigMapInDifferentNamespace(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodFailed) // Release the lock - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Nil(t, woc.wf.Status.Synchronization) // Try to acquired the lock - woc_two = newWorkflowOperationCtx(woc_two.wf, controller) + woc_two = newWorkflowOperationCtx(ctx, woc_two.wf, controller) woc_two.operate(ctx) assert.NotNil(t, woc_two.wf.Status.Synchronization) assert.NotNil(t, woc_two.wf.Status.Synchronization.Semaphore) @@ -358,7 +358,7 @@ func TestSemaphoreResourceTmplLevel(t *testing.T) { wf.Name = "one" wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) // acquired the lock woc.operate(ctx) @@ -375,7 +375,7 @@ func TestSemaphoreResourceTmplLevel(t *testing.T) { wf_Two.Name = "two" wf_Two, err = controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf_Two, metav1.CreateOptions{}) require.NoError(t, err) - woc_two := newWorkflowOperationCtx(wf_Two, controller) + woc_two := newWorkflowOperationCtx(ctx, wf_Two, controller) // Try Acquire the lock woc_two.operate(ctx) @@ -390,12 +390,12 @@ func TestSemaphoreResourceTmplLevel(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodFailed) // Release the lock - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Nil(t, woc.wf.Status.Synchronization) // Try to acquired the lock - woc_two = newWorkflowOperationCtx(woc_two.wf, controller) + woc_two = newWorkflowOperationCtx(ctx, woc_two.wf, controller) woc_two.operate(ctx) assert.NotNil(t, woc_two.wf.Status.Synchronization) assert.NotNil(t, woc_two.wf.Status.Synchronization.Semaphore) @@ -416,7 +416,7 @@ func TestSemaphoreWithOutConfigMap(t *testing.T) { wf.Name = "one" wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err, _ = woc.podReconciliation(ctx) require.NoError(t, err) for _, node := range woc.wf.Status.Nodes { @@ -470,7 +470,7 @@ func TestMutexInDAG(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(DAGWithMutex) wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { if node.Name == "dag-mutex.A" { @@ -480,7 +480,7 @@ func TestMutexInDAG(t *testing.T) { assert.Equal(wfv1.WorkflowRunning, woc.wf.Status.Phase) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc1 := newWorkflowOperationCtx(woc.wf, controller) + woc1 := newWorkflowOperationCtx(ctx, woc.wf, controller) woc1.operate(ctx) for _, node := range woc1.wf.Status.Nodes { if node.Name == "dag-mutex.B" { @@ -542,7 +542,7 @@ func TestMutexInDAGWithInterpolation(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(DAGWithInterpolatedMutex) wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { if node.Name == "dag-mutex.A" { @@ -552,7 +552,7 @@ func TestMutexInDAGWithInterpolation(t *testing.T) { assert.Equal(wfv1.WorkflowRunning, woc.wf.Status.Phase) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc1 := newWorkflowOperationCtx(woc.wf, controller) + woc1 := newWorkflowOperationCtx(ctx, woc.wf, controller) woc1.operate(ctx) for _, node := range woc1.wf.Status.Nodes { assert.NotEqual(wfv1.NodeError, node.Phase) @@ -611,7 +611,7 @@ func TestSynchronizationWithRetry(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(RetryWfWithSemaphore) wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { if node.Name == "hello1" { @@ -623,7 +623,7 @@ func TestSynchronizationWithRetry(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodSucceeded) // Release the lock from hello1 - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { if node.Name == "hello1" { @@ -637,7 +637,7 @@ func TestSynchronizationWithRetry(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodSucceeded) // Release the lock from hello2 - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) // Nobody is waiting for the lock assert.Nil(woc.wf.Status.Synchronization) @@ -821,7 +821,7 @@ func TestSynchronizationWithStep(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(StepWithSync) wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows("default").Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.NotNil(woc.wf.Status.Synchronization) assert.NotNil(woc.wf.Status.Synchronization.Semaphore) @@ -832,7 +832,7 @@ func TestSynchronizationWithStep(t *testing.T) { wf1.Name = "step2" wf1, err = controller.wfclientset.ArgoprojV1alpha1().Workflows("default").Create(ctx, wf1, metav1.CreateOptions{}) require.NoError(t, err) - woc1 := newWorkflowOperationCtx(wf1, controller) + woc1 := newWorkflowOperationCtx(ctx, wf1, controller) woc1.operate(ctx) assert.NotNil(woc1.wf.Status.Synchronization) assert.NotNil(woc1.wf.Status.Synchronization.Semaphore) @@ -841,12 +841,12 @@ func TestSynchronizationWithStep(t *testing.T) { // Finished all StepGroup in step wf = wfv1.MustUnmarshalWorkflow(StepWithSyncStatus) - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Nil(woc.wf.Status.Synchronization) // Second workflow acquire the lock - woc1 = newWorkflowOperationCtx(woc1.wf, controller) + woc1 = newWorkflowOperationCtx(ctx, woc1.wf, controller) woc1.operate(ctx) assert.NotNil(woc1.wf.Status.Synchronization) assert.NotNil(woc1.wf.Status.Synchronization.Semaphore) @@ -898,7 +898,7 @@ func TestSynchronizationWithStepRetry(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(wfWithStepRetry) wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows("default").Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) for _, n := range woc.wf.Status.Nodes { if n.Name == "[0].step1(0)" { @@ -960,7 +960,7 @@ func TestSynchronizationForPendingShuttingdownWfs(t *testing.T) { wf.Spec.Synchronization.Mutex.Name = "terminating-test" wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.NotNil(t, woc.wf.Status.Synchronization) assert.NotNil(t, woc.wf.Status.Synchronization.Mutex) @@ -972,7 +972,7 @@ func TestSynchronizationForPendingShuttingdownWfs(t *testing.T) { wfTwo, err = controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wfTwo, metav1.CreateOptions{}) require.NoError(t, err) // This workflow should be pending since the first workflow still holds the lock. - wocTwo := newWorkflowOperationCtx(wfTwo, controller) + wocTwo := newWorkflowOperationCtx(ctx, wfTwo, controller) wocTwo.operate(ctx) assert.Equal(t, wfv1.WorkflowPending, wocTwo.wf.Status.Phase) @@ -988,7 +988,7 @@ func TestSynchronizationForPendingShuttingdownWfs(t *testing.T) { require.NoError(t, err) // The pending workflow that's being shutdown should have succeeded and released the lock. - wocTwo = newWorkflowOperationCtx(wfTwo, controller) + wocTwo = newWorkflowOperationCtx(ctx, wfTwo, controller) wocTwo.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, wocTwo.execWf.Status.Phase) assert.Nil(t, wocTwo.wf.Status.Synchronization) @@ -1004,7 +1004,7 @@ func TestSynchronizationForPendingShuttingdownWfs(t *testing.T) { wf.Spec.Synchronization.Mutex.Name = "stopping-test" wf, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.NotNil(t, woc.wf.Status.Synchronization) assert.NotNil(t, woc.wf.Status.Synchronization.Mutex) @@ -1016,7 +1016,7 @@ func TestSynchronizationForPendingShuttingdownWfs(t *testing.T) { wfTwo, err = controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.Namespace).Create(ctx, wfTwo, metav1.CreateOptions{}) require.NoError(t, err) // This workflow should be pending since the first workflow still holds the lock. - wocTwo := newWorkflowOperationCtx(wfTwo, controller) + wocTwo := newWorkflowOperationCtx(ctx, wfTwo, controller) wocTwo.operate(ctx) assert.Equal(t, wfv1.WorkflowPending, wocTwo.wf.Status.Phase) @@ -1032,7 +1032,7 @@ func TestSynchronizationForPendingShuttingdownWfs(t *testing.T) { require.NoError(t, err) // The pending workflow that's being shutdown should still be pending and waiting to acquire the lock. - wocTwo = newWorkflowOperationCtx(wfTwo, controller) + wocTwo = newWorkflowOperationCtx(ctx, wfTwo, controller) wocTwo.operate(ctx) assert.Equal(t, wfv1.WorkflowPending, wocTwo.execWf.Status.Phase) assert.NotNil(t, wocTwo.wf.Status.Synchronization) @@ -1097,7 +1097,7 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) holdingJobs := make(map[string]string) diff --git a/workflow/controller/operator_data_test.go b/workflow/controller/operator_data_test.go index 019a65704543..8cfc120847f9 100644 --- a/workflow/controller/operator_data_test.go +++ b/workflow/controller/operator_data_test.go @@ -233,7 +233,7 @@ func TestDataTemplateCreatesPod(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("collect-artifact") diff --git a/workflow/controller/operator_metrics_test.go b/workflow/controller/operator_metrics_test.go index 1aa031df627b..999888b4a332 100644 --- a/workflow/controller/operator_metrics_test.go +++ b/workflow/controller/operator_metrics_test.go @@ -52,16 +52,16 @@ func TestBasicMetric(t *testing.T) { ctx := context.Background() _, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) // Schedule first pod and mark completed - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) // Process first metrics - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) metricName := wf.Spec.Templates[0].Metrics.Prometheus[0].Name @@ -126,12 +126,12 @@ func TestGaugeMetric(t *testing.T) { // Schedule first pod and mark completed ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) // Process first metrics - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) attribs := attribute.NewSet(attribute.String("name", "random-int")) @@ -192,12 +192,12 @@ func TestCounterMetric(t *testing.T) { // Schedule first pod and mark completed ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) // Process first metrics - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) attribs := attribute.NewSet(attribute.String("name", "flakey")) @@ -272,7 +272,7 @@ func TestMetricEmissionSameOperationCreationAndFailure(t *testing.T) { wf := v1alpha1.MustUnmarshalWorkflow(testMetricEmissionSameOperationCreationAndFailure) _, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -336,8 +336,8 @@ func TestRetryStrategyMetric(t *testing.T) { wf := v1alpha1.MustUnmarshalWorkflow(testRetryStrategyMetric) cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) // Ensure no metrics have been emitted yet @@ -350,7 +350,7 @@ func TestRetryStrategyMetric(t *testing.T) { podNode := woc.wf.Status.Nodes["workflow-template-whalesay-9pk8f-1966833540"] podNode.Phase = v1alpha1.NodeSucceeded woc.wf.Status.Nodes["workflow-template-whalesay-9pk8f-1966833540"] = podNode - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) attribs := attribute.NewSet() @@ -452,7 +452,7 @@ func TestDAGTmplMetrics(t *testing.T) { wf := v1alpha1.MustUnmarshalWorkflow(dagTmplMetrics) _, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) @@ -512,7 +512,7 @@ func TestRealtimeWorkflowMetric(t *testing.T) { wf := v1alpha1.MustUnmarshalWorkflow(testRealtimeWorkflowMetric) _, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -575,7 +575,7 @@ func TestRealtimeWorkflowMetricWithGlobalParameters(t *testing.T) { wf := v1alpha1.MustUnmarshalWorkflow(testRealtimeWorkflowMetricWithGlobalParameters) _, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -676,7 +676,7 @@ func TestProcessedRetryNode(t *testing.T) { wf := v1alpha1.MustUnmarshalWorkflow(testProcessedRetryNode) _, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -822,7 +822,7 @@ func TestControllerRestartWithRunningWorkflow(t *testing.T) { wf := v1alpha1.MustUnmarshalWorkflow(suspendWfWithMetrics) _, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) attribs := attribute.NewSet(attribute.String("name", "model_a")) @@ -868,11 +868,11 @@ func TestRuntimeMetrics(t *testing.T) { ctx := context.Background() _, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) // create step node makePodsPhase(ctx, woc, apiv1.PodSucceeded) // pod is successful - manually workflow is succeeded - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) // node status of previous context attribs := attribute.NewSet(attribute.String("playground_id_workflow_counter", "test"), attribute.String("status", "Succeeded")) diff --git a/workflow/controller/operator_persist_test.go b/workflow/controller/operator_persist_test.go index 603b3743482d..0a9b42f52ce3 100644 --- a/workflow/controller/operator_persist_test.go +++ b/workflow/controller/operator_persist_test.go @@ -60,7 +60,7 @@ func TestPersistWithoutLargeWfSupport(t *testing.T) { require.NoError(t, err) controller.offloadNodeStatusRepo, controller.hydrator = getMockDBCtx(fmt.Errorf("not found"), false) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -81,7 +81,7 @@ func TestPersistErrorWithoutLargeWfSupport(t *testing.T) { require.NoError(t, err) controller.offloadNodeStatusRepo, controller.hydrator = getMockDBCtx(errors.New("23324", "test"), false) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -101,7 +101,7 @@ func TestPersistWithLargeWfSupport(t *testing.T) { require.NoError(t, err) controller.offloadNodeStatusRepo, controller.hydrator = getMockDBCtx(nil, true) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -129,7 +129,7 @@ func TestPersistErrorWithLargeWfSupport(t *testing.T) { require.NoError(t, err) controller.offloadNodeStatusRepo, controller.hydrator = getMockDBCtx(errors.New("23324", "test"), true) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) require.NoError(t, err) diff --git a/workflow/controller/operator_template_scope_test.go b/workflow/controller/operator_template_scope_test.go index 65088519b1fa..048dde46538a 100644 --- a/workflow/controller/operator_template_scope_test.go +++ b/workflow/controller/operator_template_scope_test.go @@ -80,8 +80,8 @@ func TestTemplateScope(t *testing.T) { cancel, controller := newController(wf, wftmpl1, wftmpl2) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf = woc.wf @@ -170,7 +170,7 @@ func TestTemplateScopeWithParam(t *testing.T) { wfcset := controller.wfclientset.ArgoprojV1alpha1().Workflows("default") ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err := wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) @@ -259,7 +259,7 @@ func TestTemplateScopeNestedStepsWithParams(t *testing.T) { wfcset := controller.wfclientset.ArgoprojV1alpha1().Workflows("default") ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err := wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) @@ -361,7 +361,7 @@ func TestTemplateScopeDAG(t *testing.T) { wfcset := controller.wfclientset.ArgoprojV1alpha1().Workflows("default") ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err := wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) @@ -458,7 +458,7 @@ func TestTemplateClusterScope(t *testing.T) { wfcset := controller.wfclientset.ArgoprojV1alpha1().Workflows("default") ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err := wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) require.NoError(t, err) diff --git a/workflow/controller/operator_test.go b/workflow/controller/operator_test.go index 2216ba204bb2..e452e331cb7d 100644 --- a/workflow/controller/operator_test.go +++ b/workflow/controller/operator_test.go @@ -59,7 +59,7 @@ func TestOperateWorkflowPanicRecover(t *testing.T) { _, err := controller.wfclientset.ArgoprojV1alpha1().Workflows("").Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) } @@ -73,7 +73,7 @@ func Test_wfOperationCtx_reapplyUpdate(t *testing.T) { cancel, controller := newController(wf) defer cancel() controller.hydrator = hydratorfake.Always - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) // fake the behaviour woc.operate() require.NoError(t, controller.hydrator.Hydrate(wf)) @@ -97,7 +97,7 @@ func Test_wfOperationCtx_reapplyUpdate(t *testing.T) { currWf.Status.Phase = wfv1.WorkflowSucceeded cancel, controller := newController(currWf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) _, err := woc.reapplyUpdate(ctx, controller.wfclientset.ArgoprojV1alpha1().Workflows(""), wfv1.Nodes{}) require.EqualError(t, err, "must never update completed workflows") }) @@ -110,7 +110,7 @@ func Test_wfOperationCtx_reapplyUpdate(t *testing.T) { currWf.Status.Nodes = wfv1.Nodes{"my-node": wfv1.NodeStatus{Phase: wfv1.NodeSucceeded}} cancel, controller := newController(currWf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) _, err := woc.reapplyUpdate(ctx, controller.wfclientset.ArgoprojV1alpha1().Workflows(""), wf.Status.Nodes) require.EqualError(t, err, "must never update completed node my-node") }) @@ -137,11 +137,11 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.NotEmpty(t, woc.wf.Status.ResourcesDuration, "workflow duration not empty") @@ -171,12 +171,12 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, "0.000000", woc.globalParams[common.GlobalVarWorkflowDuration]) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Greater(t, woc.globalParams[common.GlobalVarWorkflowDuration], "0.000000") } @@ -215,11 +215,11 @@ status: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) @@ -249,13 +249,13 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, "false", woc.wf.Labels[common.LabelKeyCompleted]) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) @@ -289,11 +289,11 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodRunning) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) @@ -303,7 +303,7 @@ spec: assert.Equal(t, wfv1.Progress("0/100"), pod.Progress) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) @@ -358,7 +358,7 @@ func TestGlobalParams(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) require.Contains(t, woc.globalParams, "workflow.creationTimestamp") assert.NotContains(t, woc.globalParams["workflow.creationTimestamp"], "UTC") @@ -390,7 +390,7 @@ func TestSidecarWithVolume(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) pods, err := listPods(woc) @@ -506,7 +506,7 @@ func TestVolumeGCStrategy(t *testing.T) { ctx := context.Background() wfcset := controller.wfclientset.ArgoprojV1alpha1().Workflows("") - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err := wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -522,7 +522,8 @@ func TestProcessNodeRetries(t *testing.T) { assert.NotNil(t, controller) wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) assert.NotNil(t, wf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) // Verify that there are no nodes in the wf status. assert.Empty(t, woc.wf.Status.Nodes) @@ -530,7 +531,7 @@ func TestProcessNodeRetries(t *testing.T) { // Add the parent node for retries. nodeName := "test-node" nodeID := woc.wf.NodeID(nodeName) - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) retries := wfv1.RetryStrategy{} retries.Limit = intstrutil.ParsePtr("2") woc.wf.Status.Nodes[nodeID] = *node @@ -544,8 +545,8 @@ func TestProcessNodeRetries(t *testing.T) { // Add child nodes. for i := 0; i < 2; i++ { childNode := fmt.Sprintf("%s(%d)", nodeName, i) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) } n, err := woc.wf.GetNodeByName(nodeName) @@ -555,21 +556,21 @@ func TestProcessNodeRetries(t *testing.T) { // Last child is still running. processNodeRetries() should return false since // there should be no retries at this point. - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeRunning, n.Phase) // Mark lastChild as successful. - woc.markNodePhase(lastChild.Name, wfv1.NodeSucceeded) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeSucceeded) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) // The parent node also gets marked as Succeeded. assert.Equal(t, wfv1.NodeSucceeded, n.Phase) // Mark the parent node as running again and the lastChild as failed. - woc.markNodePhase(n.Name, wfv1.NodeRunning) - woc.markNodePhase(lastChild.Name, wfv1.NodeFailed) - _, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeFailed) + _, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -577,22 +578,22 @@ func TestProcessNodeRetries(t *testing.T) { // Add a hook node that has Succeeded childHookedNode := "child-node.hooks.running" - woc.initializeNode(childHookedNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeSucceeded, &wfv1.NodeFlag{Hooked: true}) - woc.addChildNode(nodeName, childHookedNode) + woc.initializeNode(ctx, childHookedNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeSucceeded, &wfv1.NodeFlag{Hooked: true}) + woc.addChildNode(ctx, nodeName, childHookedNode) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeRunning, n.Phase) // Add a third node that has failed. childNode := fmt.Sprintf("%s(%d)", nodeName, 3) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeFailed, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeFailed, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeFailed, n.Phase) } @@ -604,7 +605,8 @@ func TestProcessNodeRetriesOnErrors(t *testing.T) { assert.NotNil(t, controller) wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) assert.NotNil(t, wf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) // Verify that there are no nodes in the wf status. assert.Empty(t, woc.wf.Status.Nodes) @@ -612,7 +614,7 @@ func TestProcessNodeRetriesOnErrors(t *testing.T) { // Add the parent node for retries. nodeName := "test-node" nodeID := woc.wf.NodeID(nodeName) - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) retries := wfv1.RetryStrategy{} retries.Limit = intstrutil.ParsePtr("2") retries.RetryPolicy = wfv1.RetryPolicyAlways @@ -627,8 +629,8 @@ func TestProcessNodeRetriesOnErrors(t *testing.T) { // Add child nodes. for i := 0; i < 2; i++ { childNode := fmt.Sprintf("%s(%d)", nodeName, i) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) } n, err := woc.wf.GetNodeByName(nodeName) @@ -638,21 +640,21 @@ func TestProcessNodeRetriesOnErrors(t *testing.T) { // Last child is still running. processNodeRetries() should return false since // there should be no retries at this point. - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeRunning, n.Phase) // Mark lastChild as successful. - woc.markNodePhase(lastChild.Name, wfv1.NodeSucceeded) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeSucceeded) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) // The parent node also gets marked as Succeeded. assert.Equal(t, wfv1.NodeSucceeded, n.Phase) // Mark the parent node as running again and the lastChild as errored. - n = woc.markNodePhase(n.Name, wfv1.NodeRunning) - woc.markNodePhase(lastChild.Name, wfv1.NodeError) - _, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n = woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeError) + _, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -660,11 +662,11 @@ func TestProcessNodeRetriesOnErrors(t *testing.T) { // Add a third node that has errored. childNode := fmt.Sprintf("%s(%d)", nodeName, 3) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeError, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeError, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeError, n.Phase) } @@ -676,7 +678,8 @@ func TestProcessNodeRetriesOnTransientErrors(t *testing.T) { assert.NotNil(t, controller) wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) assert.NotNil(t, wf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) // Verify that there are no nodes in the wf status. assert.Empty(t, woc.wf.Status.Nodes) @@ -684,7 +687,7 @@ func TestProcessNodeRetriesOnTransientErrors(t *testing.T) { // Add the parent node for retries. nodeName := "test-node" nodeID := woc.wf.NodeID(nodeName) - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) retries := wfv1.RetryStrategy{} retries.Limit = intstrutil.ParsePtr("2") retries.RetryPolicy = wfv1.RetryPolicyOnTransientError @@ -699,8 +702,8 @@ func TestProcessNodeRetriesOnTransientErrors(t *testing.T) { // Add child nodes. for i := 0; i < 2; i++ { childNode := fmt.Sprintf("%s(%d)", nodeName, i) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) } n, err := woc.wf.GetNodeByName(nodeName) @@ -710,25 +713,25 @@ func TestProcessNodeRetriesOnTransientErrors(t *testing.T) { // Last child is still running. processNodeRetries() should return false since // there should be no retries at this point. - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeRunning, n.Phase) // Mark lastChild as successful. - woc.markNodePhase(lastChild.Name, wfv1.NodeSucceeded) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeSucceeded) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) // The parent node also gets marked as Succeeded. assert.Equal(t, wfv1.NodeSucceeded, n.Phase) // Mark the parent node as running again and the lastChild as errored with a message that indicates the error // is transient. - n = woc.markNodePhase(n.Name, wfv1.NodeRunning) + n = woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) transientEnvVarKey := "TRANSIENT_ERROR_PATTERN" transientErrMsg := "This error is transient" - woc.markNodePhase(lastChild.Name, wfv1.NodeError, transientErrMsg) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeError, transientErrMsg) t.Setenv(transientEnvVarKey, transientErrMsg) - _, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + _, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -736,11 +739,11 @@ func TestProcessNodeRetriesOnTransientErrors(t *testing.T) { // Add a third node that has errored. childNode := fmt.Sprintf("%s(%d)", nodeName, 3) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeError, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeError, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeError, n.Phase) } @@ -752,7 +755,8 @@ func TestProcessNodeRetriesWithBackoff(t *testing.T) { assert.NotNil(t, controller) wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) assert.NotNil(t, wf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) // Verify that there are no nodes in the wf status. assert.Empty(t, woc.wf.Status.Nodes) @@ -760,7 +764,7 @@ func TestProcessNodeRetriesWithBackoff(t *testing.T) { // Add the parent node for retries. nodeName := "test-node" nodeID := woc.wf.NodeID(nodeName) - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) retries := wfv1.RetryStrategy{} retries.Limit = intstrutil.ParsePtr("2") retries.Backoff = &wfv1.Backoff{ @@ -777,8 +781,8 @@ func TestProcessNodeRetriesWithBackoff(t *testing.T) { lastChild := getChildNodeIndex(node, woc.wf.Status.Nodes, -1) assert.Nil(t, lastChild) - woc.initializeNode(nodeName+"(0)", wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, nodeName+"(0)") + woc.initializeNode(ctx, nodeName+"(0)", wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, nodeName+"(0)") n, err := woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -787,13 +791,13 @@ func TestProcessNodeRetriesWithBackoff(t *testing.T) { // Last child is still running. processNodeRetries() should return false since // there should be no retries at this point. - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeRunning, n.Phase) // Mark lastChild as successful. - woc.markNodePhase(lastChild.Name, wfv1.NodeSucceeded) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeSucceeded) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) // The parent node also gets marked as Succeeded. assert.Equal(t, wfv1.NodeSucceeded, n.Phase) @@ -807,7 +811,8 @@ func TestProcessNodeRetriesWithExponentialBackoff(t *testing.T) { require.NotNil(controller) wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) require.NotNil(wf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) require.NotNil(woc) // Verify that there are no nodes in the wf status. @@ -816,7 +821,7 @@ func TestProcessNodeRetriesWithExponentialBackoff(t *testing.T) { // Add the parent node for retries. nodeName := "test-node" nodeID := woc.wf.NodeID(nodeName) - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) retries := wfv1.RetryStrategy{} retries.Limit = intstrutil.ParsePtr("2") retries.RetryPolicy = wfv1.RetryPolicyAlways @@ -832,14 +837,14 @@ func TestProcessNodeRetriesWithExponentialBackoff(t *testing.T) { lastChild := getChildNodeIndex(node, woc.wf.Status.Nodes, -1) require.Nil(lastChild) - woc.initializeNode(nodeName+"(0)", wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeFailed, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, nodeName+"(0)") + woc.initializeNode(ctx, nodeName+"(0)", wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeFailed, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, nodeName+"(0)") n, err := woc.wf.GetNodeByName(nodeName) require.NoError(err) // Last child has failed. processNodesWithRetries() should return false due to the default backoff. - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(err) require.Equal(wfv1.NodeRunning, n.Phase) @@ -849,12 +854,12 @@ func TestProcessNodeRetriesWithExponentialBackoff(t *testing.T) { require.LessOrEqual(backoff, 300) require.Less(295, backoff) - woc.initializeNode(nodeName+"(1)", wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeError, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, nodeName+"(1)") + woc.initializeNode(ctx, nodeName+"(1)", wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeError, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, nodeName+"(1)") n, err = woc.wf.GetNodeByName(nodeName) require.NoError(err) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(err) require.Equal(wfv1.NodeRunning, n.Phase) @@ -867,8 +872,8 @@ func TestProcessNodeRetriesWithExponentialBackoff(t *testing.T) { // Mark lastChild as successful. lastChild = getChildNodeIndex(n, woc.wf.Status.Nodes, -1) require.NotNil(lastChild) - woc.markNodePhase(lastChild.Name, wfv1.NodeSucceeded) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeSucceeded) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(err) // The parent node also gets marked as Succeeded. require.Equal(wfv1.NodeSucceeded, n.Phase) @@ -881,7 +886,8 @@ func TestProcessNodeRetriesWithExpression(t *testing.T) { assert.NotNil(t, controller) wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) assert.NotNil(t, wf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) // Verify that there are no nodes in the wf status. assert.Empty(t, woc.wf.Status.Nodes) @@ -889,7 +895,7 @@ func TestProcessNodeRetriesWithExpression(t *testing.T) { // Add the parent node for retries. nodeName := "test-node" nodeID := woc.wf.NodeID(nodeName) - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) retries := wfv1.RetryStrategy{} retries.Expression = "false" retries.Limit = intstrutil.ParsePtr("2") @@ -905,8 +911,8 @@ func TestProcessNodeRetriesWithExpression(t *testing.T) { // Add child nodes. for i := 0; i < 2; i++ { childNode := fmt.Sprintf("%s(%d)", nodeName, i) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) } n, err := woc.wf.GetNodeByName(nodeName) @@ -916,28 +922,28 @@ func TestProcessNodeRetriesWithExpression(t *testing.T) { // Last child is still running. processNodeRetries() should return false since // there should be no retries at this point. - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeRunning, n.Phase) // Mark lastChild Pending. - woc.markNodePhase(lastChild.Name, wfv1.NodePending) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodePending) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeRunning, n.Phase) // Mark lastChild as successful. - woc.markNodePhase(lastChild.Name, wfv1.NodeSucceeded) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeSucceeded) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) // The parent node also gets marked as Succeeded. assert.Equal(t, wfv1.NodeSucceeded, n.Phase) assert.Equal(t, "", n.Message) // Mark the parent node as running again and the lastChild as errored. - n = woc.markNodePhase(n.Name, wfv1.NodeRunning) - woc.markNodePhase(lastChild.Name, wfv1.NodeError) - _, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n = woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeError) + _, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -945,13 +951,13 @@ func TestProcessNodeRetriesWithExpression(t *testing.T) { assert.Equal(t, "retryStrategy.expression evaluated to false", n.Message) // Add a third node that has failed. - woc.markNodePhase(n.Name, wfv1.NodeRunning) + woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) childNode := fmt.Sprintf("%s(%d)", nodeName, 3) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeFailed, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeFailed, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeFailed, n.Phase) assert.Equal(t, "No more retries left", n.Message) @@ -963,7 +969,8 @@ func TestProcessNodeRetriesMessageOrder(t *testing.T) { assert.NotNil(t, controller) wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) assert.NotNil(t, wf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) // Verify that there are no nodes in the wf status. assert.Empty(t, woc.wf.Status.Nodes) @@ -971,7 +978,7 @@ func TestProcessNodeRetriesMessageOrder(t *testing.T) { // Add the parent node for retries. nodeName := "test-node" nodeID := woc.wf.NodeID(nodeName) - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) retries := wfv1.RetryStrategy{} retries.Expression = "false" retries.Limit = intstrutil.ParsePtr("1") @@ -987,8 +994,8 @@ func TestProcessNodeRetriesMessageOrder(t *testing.T) { // Add child nodes. for i := 0; i < 1; i++ { childNode := fmt.Sprintf("%s(%d)", nodeName, i) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) } n, err := woc.wf.GetNodeByName(nodeName) @@ -997,29 +1004,29 @@ func TestProcessNodeRetriesMessageOrder(t *testing.T) { assert.NotNil(t, lastChild) // No retry related message for running node - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeRunning, n.Phase) // No retry related message for pending node - woc.markNodePhase(lastChild.Name, wfv1.NodePending) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodePending) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeRunning, n.Phase) assert.Equal(t, "", n.Message) // No retry related message for succeeded node - woc.markNodePhase(lastChild.Name, wfv1.NodeSucceeded) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeSucceeded) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeSucceeded, n.Phase) assert.Equal(t, "", n.Message) // workflow mark shutdown, no retry is evaluated woc.wf.Spec.Shutdown = wfv1.ShutdownStrategyStop - n = woc.markNodePhase(n.Name, wfv1.NodeRunning) - woc.markNodePhase(lastChild.Name, wfv1.NodeError) - _, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n = woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeError) + _, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -1029,16 +1036,16 @@ func TestProcessNodeRetriesMessageOrder(t *testing.T) { // Invalid retry policy, shouldn't evaluate expression retries.RetryPolicy = "noExist" - n = woc.markNodePhase(n.Name, wfv1.NodeRunning) - woc.markNodePhase(lastChild.Name, wfv1.NodeError) - _, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n = woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeError) + _, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) assert.Equal(t, "noExist is not a valid RetryPolicy", err.Error()) // Node status doesn't with retrypolicy, shouldn't evaluate expression retries.RetryPolicy = wfv1.RetryPolicyOnFailure - n = woc.markNodePhase(n.Name, wfv1.NodeRunning) - woc.markNodePhase(lastChild.Name, wfv1.NodeError) - _, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n = woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeError) + _, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -1047,9 +1054,9 @@ func TestProcessNodeRetriesMessageOrder(t *testing.T) { // Node status aligns with retrypolicy, should evaluate expression retries.RetryPolicy = wfv1.RetryPolicyOnFailure - n = woc.markNodePhase(n.Name, wfv1.NodeRunning) - woc.markNodePhase(lastChild.Name, wfv1.NodeFailed) - _, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n = woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeFailed) + _, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -1057,13 +1064,13 @@ func TestProcessNodeRetriesMessageOrder(t *testing.T) { assert.Equal(t, "retryStrategy.expression evaluated to false", n.Message) // Node status aligns with retrypolicy but reach max retry limit, shouldn't evaluate expression - woc.markNodePhase(n.Name, wfv1.NodeRunning) + woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) childNode := fmt.Sprintf("%s(%d)", nodeName, 1) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeFailed, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeFailed, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeFailed, n.Phase) assert.Equal(t, "No more retries left", n.Message) @@ -1097,7 +1104,8 @@ func TestProcessNodesNoRetryWithError(t *testing.T) { assert.NotNil(t, controller) wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) assert.NotNil(t, wf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) // Verify that there are no nodes in the wf status. assert.Empty(t, woc.wf.Status.Nodes) @@ -1105,7 +1113,7 @@ func TestProcessNodesNoRetryWithError(t *testing.T) { // Add the parent node for retries. nodeName := "test-node" nodeID := woc.wf.NodeID(nodeName) - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) retries := wfv1.RetryStrategy{} retries.Limit = intstrutil.ParsePtr("2") retries.RetryPolicy = wfv1.RetryPolicyOnFailure @@ -1120,8 +1128,8 @@ func TestProcessNodesNoRetryWithError(t *testing.T) { // Add child nodes. for i := 0; i < 2; i++ { childNode := fmt.Sprintf("%s(%d)", nodeName, i) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) } n, err := woc.wf.GetNodeByName(nodeName) @@ -1131,22 +1139,22 @@ func TestProcessNodesNoRetryWithError(t *testing.T) { // Last child is still running. processNodeRetries() should return false since // there should be no retries at this point. - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) assert.Equal(t, wfv1.NodeRunning, n.Phase) // Mark lastChild as successful. - woc.markNodePhase(lastChild.Name, wfv1.NodeSucceeded) - n, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeSucceeded) + n, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) // The parent node also gets marked as Succeeded. assert.Equal(t, wfv1.NodeSucceeded, n.Phase) // Mark the parent node as running again and the lastChild as errored. // Parent node should also be errored because retry on error is disabled - n = woc.markNodePhase(n.Name, wfv1.NodeRunning) - woc.markNodePhase(lastChild.Name, wfv1.NodeError) - _, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + n = woc.markNodePhase(ctx, n.Name, wfv1.NodeRunning) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeError) + _, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -1285,7 +1293,8 @@ func TestBackoffMessage(t *testing.T) { assert.NotNil(t, controller) wf := wfv1.MustUnmarshalWorkflow(backoffMessage) assert.NotNil(t, wf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) retryNode, err := woc.wf.GetNodeByName("retry-backoff-s69z6") require.NoError(t, err) @@ -1300,7 +1309,7 @@ func TestBackoffMessage(t *testing.T) { lastNode.FinishedAt = metav1.Time{Time: time.Now().Add(-1 * time.Second)} woc.wf.Status.Nodes[lastNode.ID] = *lastNode - newRetryNode, proceed, err := woc.processNodeRetries(retryNode, *woc.wf.Spec.Templates[0].RetryStrategy, &executeTemplateOpts{}) + newRetryNode, proceed, err := woc.processNodeRetries(ctx, retryNode, *woc.wf.Spec.Templates[0].RetryStrategy, &executeTemplateOpts{}) require.NoError(t, err) assert.False(t, proceed) assert.Equal(t, "Backoff for 4 seconds", newRetryNode.Message) @@ -1313,7 +1322,7 @@ func TestBackoffMessage(t *testing.T) { lastNode.FinishedAt = metav1.Time{Time: time.Now().Add(-2 * time.Second)} woc.wf.Status.Nodes[lastNode.ID] = *lastNode - newRetryNode, proceed, err = woc.processNodeRetries(retryNode, *woc.wf.Spec.Templates[0].RetryStrategy, &executeTemplateOpts{}) + newRetryNode, proceed, err = woc.processNodeRetries(ctx, retryNode, *woc.wf.Spec.Templates[0].RetryStrategy, &executeTemplateOpts{}) require.NoError(t, err) assert.False(t, proceed) // Message should not change @@ -1327,7 +1336,7 @@ func TestBackoffMessage(t *testing.T) { lastNode.FinishedAt = metav1.Time{Time: time.Now().Add(-5 * time.Second)} woc.wf.Status.Nodes[lastNode.ID] = *lastNode - newRetryNode, proceed, err = woc.processNodeRetries(retryNode, *woc.wf.Spec.Templates[0].RetryStrategy, &executeTemplateOpts{}) + newRetryNode, proceed, err = woc.processNodeRetries(ctx, retryNode, *woc.wf.Spec.Templates[0].RetryStrategy, &executeTemplateOpts{}) require.NoError(t, err) assert.True(t, proceed) // New node is started, message should be clear @@ -1359,7 +1368,7 @@ func TestRetriesVariable(t *testing.T) { iterations := 5 var woc *wfOperationCtx for i := 1; i <= iterations; i++ { - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) if i != 1 { makePodsPhase(ctx, woc, apiv1.PodFailed) } @@ -1413,7 +1422,7 @@ func TestRetriesVariableInPodSpecPatch(t *testing.T) { iterations := 5 var woc *wfOperationCtx for i := 1; i <= iterations; i++ { - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) if i != 1 { makePodsPhase(ctx, woc, apiv1.PodFailed) } @@ -1470,7 +1479,7 @@ func TestRetriesVariableWithGlobalVariableInPodSpecPatch(t *testing.T) { iterations := 5 var woc *wfOperationCtx for i := 1; i <= iterations; i++ { - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) if i != 1 { makePodsPhase(ctx, woc, apiv1.PodFailed) } @@ -1529,7 +1538,7 @@ func TestStepsRetriesVariable(t *testing.T) { iterations := 5 var woc *wfOperationCtx for i := 1; i <= iterations; i++ { - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) if i != 1 { makePodsPhase(ctx, woc, apiv1.PodFailed) } @@ -1769,6 +1778,7 @@ func TestAssessNodeStatus(t *testing.T) { nonDaemonWf := wfv1.MustUnmarshalWorkflow(helloWorldWf) daemonWf := wfv1.MustUnmarshalWorkflow(helloDaemonWf) + ctx := context.Background() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { wf := nonDaemonWf @@ -1778,7 +1788,7 @@ func TestAssessNodeStatus(t *testing.T) { assert.Equal(t, tt.daemon, wf.GetTemplateByName(tt.node.TemplateName).IsDaemon(), "check the test case is valid") cancel, controller := newController() defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) got := woc.assessNodeStatus(context.TODO(), tt.pod, tt.node) assert.Equal(t, tt.wantPhase, got.Phase) assert.Equal(t, tt.wantMessage, got.Message) @@ -1979,7 +1989,7 @@ func TestWorkflowStepRetry(t *testing.T) { require.NoError(t, err) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -1989,7 +1999,7 @@ func TestWorkflowStepRetry(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodSucceeded) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) nodeID := woc.nodeID(&pods.Items[0]) woc.wf.Status.MarkTaskResultComplete(nodeID) woc.operate(ctx) @@ -1998,7 +2008,7 @@ func TestWorkflowStepRetry(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodFailed) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -2048,7 +2058,7 @@ func TestWorkflowParallelismLimit(t *testing.T) { cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -2057,7 +2067,7 @@ func TestWorkflowParallelismLimit(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodRunning) // operate again and make sure we don't schedule any more pods - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -2108,7 +2118,7 @@ func TestStepsTemplateParallelismLimit(t *testing.T) { wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -2120,7 +2130,7 @@ func TestStepsTemplateParallelismLimit(t *testing.T) { require.NoError(t, err) // wfBytes, _ := json.MarshalIndent(wf, "", " ") // log.Printf("%s", wfBytes) - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -2161,7 +2171,7 @@ func TestDAGTemplateParallelismLimit(t *testing.T) { cancel, controller := newController(wf) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -2169,7 +2179,7 @@ func TestDAGTemplateParallelismLimit(t *testing.T) { // operate again and make sure we don't schedule any more pods makePodsPhase(ctx, woc, apiv1.PodRunning) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -2252,7 +2262,7 @@ func TestNestedTemplateParallelismLimit(t *testing.T) { require.NoError(t, err) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -2279,7 +2289,7 @@ func TestSidecarResourceLimits(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) _, err := controller.wfclientset.ArgoprojV1alpha1().Workflows("").Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pod, err := getPod(woc, "hello-world") require.NoError(t, err) @@ -2312,7 +2322,7 @@ func TestSuspendResume(t *testing.T) { assert.True(t, *wf.Spec.Suspend) // operate should not result in no workflows being created since it is suspended - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -2324,7 +2334,7 @@ func TestSuspendResume(t *testing.T) { wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) assert.Nil(t, wf.Spec.Suspend) - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -2354,14 +2364,14 @@ func TestSuspendWithDeadline(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(suspendTemplateWithDeadline) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) assert.True(t, util.IsWorkflowSuspended(wf)) // operate again and verify no pods were scheduled - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) updatedWf, err := wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -2401,7 +2411,7 @@ func TestSuspendInputsResolution(t *testing.T) { ctx := context.Background() wf := wfv1.MustUnmarshalWorkflow(suspendTemplateInputResolution) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("suspend-template") @@ -2458,7 +2468,7 @@ func TestSequence(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(sequence) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) updatedWf, err := wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -2521,7 +2531,7 @@ func TestInputParametersAsJson(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(inputParametersAsJson) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) updatedWf, err := wfcset.Get(ctx, wf.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -2579,7 +2589,7 @@ func TestExpandWithItems(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(expandWithItems) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) newSteps, err := woc.expandStep(wf.Spec.Templates[0].Steps[0].Steps[0]) require.NoError(t, err) assert.Len(t, newSteps, 5) @@ -2629,7 +2639,7 @@ func TestExpandWithItemsMap(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(expandWithItemsMap) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) newSteps, err := woc.expandStep(wf.Spec.Templates[0].Steps[0].Steps[0]) require.NoError(t, err) assert.Len(t, newSteps, 3) @@ -2678,14 +2688,14 @@ func TestSuspendTemplate(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(suspendTemplate) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) assert.True(t, util.IsWorkflowSuspended(wf)) // operate again and verify no pods were scheduled - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -2699,7 +2709,7 @@ func TestSuspendTemplate(t *testing.T) { assert.False(t, util.IsWorkflowSuspended(wf)) // operate the workflow. it should reach the second step - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -2716,14 +2726,14 @@ func TestSuspendTemplateWithFailedResume(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(suspendTemplate) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) assert.True(t, util.IsWorkflowSuspended(wf)) // operate again and verify no pods were scheduled - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -2737,7 +2747,7 @@ func TestSuspendTemplateWithFailedResume(t *testing.T) { assert.False(t, util.IsWorkflowSuspended(wf)) // operate the workflow. it should be failed and not reach the second step - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) pods, err = listPods(woc) @@ -2755,14 +2765,14 @@ func TestSuspendTemplateWithFilteredResume(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(suspendTemplate) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) assert.True(t, util.IsWorkflowSuspended(wf)) // operate again and verify no pods were scheduled - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -2773,7 +2783,7 @@ func TestSuspendTemplateWithFilteredResume(t *testing.T) { require.Error(t, err) // operate the workflow. nothing should have happened - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -2788,7 +2798,7 @@ func TestSuspendTemplateWithFilteredResume(t *testing.T) { assert.False(t, util.IsWorkflowSuspended(wf)) // operate the workflow. it should reach the second step - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -2831,14 +2841,14 @@ func TestSuspendResumeAfterTemplate(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(suspendResumeAfterTemplate) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) assert.True(t, util.IsWorkflowSuspended(wf)) // operate again and verify no pods were scheduled - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -2848,7 +2858,7 @@ func TestSuspendResumeAfterTemplate(t *testing.T) { time.Sleep(4 * time.Second) // operate the workflow. it should reach the second step - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -2865,14 +2875,14 @@ func TestSuspendResumeAfterTemplateNoWait(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(suspendResumeAfterTemplate) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) assert.True(t, util.IsWorkflowSuspended(wf)) // operate again and verify no pods were scheduled - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -2881,7 +2891,7 @@ func TestSuspendResumeAfterTemplateNoWait(t *testing.T) { // don't wait // operate the workflow. it should have not reached the second step since not enough time passed - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -2931,7 +2941,7 @@ func TestWorkflowSpecParam(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(volumeWithParam) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pod, err := getPod(woc, wf.Name) @@ -3090,7 +3100,7 @@ func TestWokflowSchedulingConstraintsDAG(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -3120,7 +3130,7 @@ func TestWokflowSchedulingConstraintsSteps(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -3144,7 +3154,8 @@ func TestWokflowSchedulingConstraintsSteps(t *testing.T) { } func TestAddGlobalParamToScope(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) woc.globalParams = make(map[string]string) testVal := wfv1.AnyStringPtr("test-value") param := wfv1.Parameter{ @@ -3152,12 +3163,12 @@ func TestAddGlobalParamToScope(t *testing.T) { Value: testVal, } // Make sure if the param is not global, don't add to scope - woc.addParamToGlobalScope(param) + woc.addParamToGlobalScope(ctx, param) assert.Nil(t, woc.wf.Status.Outputs) // Now set it as global. Verify it is added to workflow outputs param.GlobalName = "global-param" - woc.addParamToGlobalScope(param) + woc.addParamToGlobalScope(ctx, param) assert.Len(t, woc.wf.Status.Outputs.Parameters, 1) assert.Equal(t, param.GlobalName, woc.wf.Status.Outputs.Parameters[0].Name) assert.Equal(t, testVal, woc.wf.Status.Outputs.Parameters[0].Value) @@ -3166,7 +3177,7 @@ func TestAddGlobalParamToScope(t *testing.T) { // Change the value and verify it is reflected in workflow outputs newValue := wfv1.AnyStringPtr("new-value") param.Value = newValue - woc.addParamToGlobalScope(param) + woc.addParamToGlobalScope(ctx, param) assert.Len(t, woc.wf.Status.Outputs.Parameters, 1) assert.Equal(t, param.GlobalName, woc.wf.Status.Outputs.Parameters[0].Name) assert.Equal(t, newValue, woc.wf.Status.Outputs.Parameters[0].Value) @@ -3174,7 +3185,7 @@ func TestAddGlobalParamToScope(t *testing.T) { // Add a new global parameter param.GlobalName = "global-param2" - woc.addParamToGlobalScope(param) + woc.addParamToGlobalScope(ctx, param) assert.Len(t, woc.wf.Status.Outputs.Parameters, 2) assert.Equal(t, param.GlobalName, woc.wf.Status.Outputs.Parameters[1].Name) assert.Equal(t, newValue, woc.wf.Status.Outputs.Parameters[1].Value) @@ -3182,7 +3193,8 @@ func TestAddGlobalParamToScope(t *testing.T) { } func TestAddGlobalArtifactToScope(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) art := wfv1.Artifact{ Name: "test-art", ArtifactLocation: wfv1.ArtifactLocation{ @@ -3195,19 +3207,19 @@ func TestAddGlobalArtifactToScope(t *testing.T) { }, } // Make sure if the artifact is not global, don't add to scope - woc.addArtifactToGlobalScope(art) + woc.addArtifactToGlobalScope(ctx, art) assert.Nil(t, woc.wf.Status.Outputs) // Now mark it as global. Verify it is added to workflow outputs art.GlobalName = "global-art" - woc.addArtifactToGlobalScope(art) + woc.addArtifactToGlobalScope(ctx, art) assert.Len(t, woc.wf.Status.Outputs.Artifacts, 1) assert.Equal(t, art.GlobalName, woc.wf.Status.Outputs.Artifacts[0].Name) assert.Equal(t, "some/key", woc.wf.Status.Outputs.Artifacts[0].S3.Key) // Change the value and verify update is reflected art.S3.Key = "new/key" - woc.addArtifactToGlobalScope(art) + woc.addArtifactToGlobalScope(ctx, art) assert.Len(t, woc.wf.Status.Outputs.Artifacts, 1) assert.Equal(t, art.GlobalName, woc.wf.Status.Outputs.Artifacts[0].Name) assert.Equal(t, "new/key", woc.wf.Status.Outputs.Artifacts[0].S3.Key) @@ -3215,7 +3227,7 @@ func TestAddGlobalArtifactToScope(t *testing.T) { // Add a new global artifact art.GlobalName = "global-art2" art.S3.Key = "new/new/key" - woc.addArtifactToGlobalScope(art) + woc.addArtifactToGlobalScope(ctx, art) assert.Len(t, woc.wf.Status.Outputs.Artifacts, 2) assert.Equal(t, art.GlobalName, woc.wf.Status.Outputs.Artifacts[1].Name) assert.Equal(t, "new/new/key", woc.wf.Status.Outputs.Artifacts[1].S3.Key) @@ -3223,8 +3235,8 @@ func TestAddGlobalArtifactToScope(t *testing.T) { func TestParamSubstitutionWithArtifact(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow("@../../test/e2e/functional/param-sub-with-artifacts.yaml") - woc := newWoc(*wf) ctx := context.Background() + woc := newWoc(ctx, *wf) woc.operate(ctx) wf, err := woc.controller.wfclientset.ArgoprojV1alpha1().Workflows("").Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -3236,8 +3248,8 @@ func TestParamSubstitutionWithArtifact(t *testing.T) { func TestGlobalParamSubstitutionWithArtifact(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow("@../../test/e2e/functional/global-param-sub-with-artifacts.yaml") - woc := newWoc(*wf) ctx := context.Background() + woc := newWoc(ctx, *wf) woc.operate(ctx) wf, err := woc.controller.wfclientset.ArgoprojV1alpha1().Workflows("").Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -3354,7 +3366,7 @@ func TestMetadataPassing(t *testing.T) { require.NoError(t, err) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) pods, err := listPods(woc) @@ -3426,7 +3438,7 @@ spec: func TestResolveIOPathPlaceholders(t *testing.T) { ctx := context.Background() wf := wfv1.MustUnmarshalWorkflow(ioPathPlaceholders) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) pods, err := listPods(woc) @@ -3458,7 +3470,7 @@ spec: func TestResolvePlaceholdersInOutputValues(t *testing.T) { ctx := context.Background() wf := wfv1.MustUnmarshalWorkflow(outputValuePlaceholders) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) pods, err := listPods(woc) @@ -3503,7 +3515,7 @@ func TestResolvePodNameInRetries(t *testing.T) { t.Setenv("POD_NAMES", tt.podNameVersion) ctx := context.Background() wf := wfv1.MustUnmarshalWorkflow(podNameInRetries) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) pods, err := woc.controller.kubeclientset.CoreV1().Pods(wf.ObjectMeta.Namespace).List(ctx, metav1.ListOptions{}) @@ -3601,7 +3613,7 @@ func TestResourceTemplate(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(resourceTemplate) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -3684,7 +3696,7 @@ func TestResourceWithOwnerReferenceTemplate(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(resourceWithOwnerReferenceTemplate) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -3796,7 +3808,7 @@ func TestStepWFGetNodeName(t *testing.T) { require.NoError(t, err) assert.True(t, hasOutputResultRef("generate", &wf.Spec.Templates[0])) assert.False(t, hasOutputResultRef("print-message", &wf.Spec.Templates[0])) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -3821,7 +3833,7 @@ func TestDAGWFGetNodeName(t *testing.T) { require.NoError(t, err) assert.True(t, hasOutputResultRef("A", &wf.Spec.Templates[0])) assert.False(t, hasOutputResultRef("B", &wf.Spec.Templates[0])) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -3876,7 +3888,7 @@ func TestWithParamAsJsonList(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(withParamAsJsonList) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -3919,10 +3931,10 @@ func TestStepsOnExit(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("leafA.onExit") @@ -3961,10 +3973,10 @@ func TestStepsOnExitFailures(t *testing.T) { // Test list expansion ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Contains(t, woc.globalParams[common.GlobalVarWorkflowFailures], `[{\"displayName\":\"exit-handlers\",\"message\":\"Pod failed\",\"templateName\":\"intentional-fail\",\"phase\":\"Failed\",\"podName\":\"exit-handlers\"`) @@ -4003,11 +4015,11 @@ func TestStepsOnExitTimeout(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(onExitTimeout) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("exit-handlers.onExit") @@ -4100,10 +4112,10 @@ spec: t.Run(wf.Name, func(t *testing.T) { cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.ElementsMatch(t, want, getEventsWithoutAnnotations(controller, len(want))) }) @@ -4195,11 +4207,11 @@ spec: cancel, controller := newController(wf) defer cancel() controller.Config.NodeEvents = config.NodeEvents{SendAsPod: true} - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) createRunningPods(ctx, woc) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.ElementsMatch(t, want, getEventsWithoutAnnotations(controller, len(want))) }) @@ -4245,7 +4257,7 @@ spec: ctx := context.Background() cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) createRunningPods(ctx, woc) woc.operate(ctx) time.Sleep(time.Second) @@ -4289,7 +4301,7 @@ func TestPDBCreation(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pdb, _ := controller.kubeclientset.PolicyV1().PodDisruptionBudgets("").Get(ctx, woc.wf.Name, metav1.GetOptions{}) assert.Equal(t, pdb.Name, wf.Name) @@ -4313,7 +4325,7 @@ func TestPDBCreation(t *testing.T) { _, err = controller.kubeclientset.PolicyV1().PodDisruptionBudgets(wf.Namespace).Create(ctx, &newPDB, metav1.CreateOptions{}) require.NoError(t, err) - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pdb, _ = controller.kubeclientset.PolicyV1().PodDisruptionBudgets("").Get(ctx, woc.wf.Name, metav1.GetOptions{}) assert.Equal(t, pdb.Name, wf.Name) @@ -4325,12 +4337,12 @@ func TestPDBCreationRaceDelete(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) err := controller.kubeclientset.PolicyV1().PodDisruptionBudgets("").Delete(ctx, woc.wf.Name, metav1.DeleteOptions{}) require.NoError(t, err) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) } @@ -4341,7 +4353,7 @@ func TestStatusConditions(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Empty(t, woc.wf.Status.Conditions) woc.markWorkflowSuccess(ctx) @@ -4400,11 +4412,11 @@ func TestNestedOptionalOutputArtifacts(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(nestedOptionalOutputArtifacts) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) @@ -4418,10 +4430,10 @@ func TestPodSpecLogForFailedPods(t *testing.T) { ctx := context.Background() controller.Config.PodSpecLogStrategy.FailedPod = true - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodFailed) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { assert.True(t, woc.shouldPrintPodSpec(&node)) @@ -4440,10 +4452,10 @@ func TestPodSpecLogForAllPods(t *testing.T) { wfcset := controller.wfclientset.ArgoprojV1alpha1().Workflows("") wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) woc.operate(ctx) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { assert.True(t, woc.shouldPrintPodSpec(&node)) @@ -4556,7 +4568,7 @@ func TestRetryNodeOutputs(t *testing.T) { require.NoError(t, err) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) retryNode, err := woc.wf.GetNodeByName("daemon-step-dvbnn[0].influx") require.NoError(t, err) @@ -4637,12 +4649,11 @@ func TestDeletePVCDoesNotDeletePVCOnFailedWorkflow(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(workflowWithPVCAndFailingStep) cancel, controller := newController(wf) defer cancel() - - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.Len(woc.wf.Status.PersistentVolumeClaims, 1, "1 PVC before operating") - ctx := context.Background() woc.operate(ctx) node1, err := woc.wf.GetNodeByName("wf-with-pvc(0)[0].succeed") @@ -4701,7 +4712,7 @@ func TestContainerOutputsResult(t *testing.T) { assert.True(t, hasOutputResultRef("hello1", &wf.Spec.Templates[0])) assert.False(t, hasOutputResultRef("hello2", &wf.Spec.Templates[0])) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) for _, node := range wf.Status.Nodes { @@ -4847,7 +4858,7 @@ func TestNestedStepGroupGlobalParams(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("generate") @@ -4886,7 +4897,7 @@ spec: func TestResolvePlaceholdersInGlobalVariables(t *testing.T) { ctx := context.Background() wf := wfv1.MustUnmarshalWorkflow(globalVariablePlaceholders) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) pods, err := listPods(woc) @@ -4935,7 +4946,7 @@ func TestUnsuppliedArgValue(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, woc.wf.Status.Conditions[0].Status, metav1.ConditionStatus("True")) assert.Equal(t, "invalid spec: spec.arguments.missing.value or spec.arguments.missing.valueFrom is required", woc.wf.Status.Message) @@ -4969,7 +4980,7 @@ func TestSuppliedArgValue(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) assert.Equal(t, "argo", woc.globalParams["workflow.parameters.message"]) @@ -5057,7 +5068,7 @@ func TestMaxDurationOnErroredFirstNode(t *testing.T) { wf.Status.Nodes["echo-wngc4-1641470511"] = node ctx := context.Background() - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) } @@ -5155,7 +5166,7 @@ func TestBackoffExceedsMaxDuration(t *testing.T) { wf.Status.Nodes["echo-r6v49-3721138751"] = node ctx := context.Background() - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) assert.Equal(t, "Backoff would exceed max duration limit", woc.wf.Status.Nodes["echo-r6v49"].Message) @@ -5323,7 +5334,7 @@ func TestNoOnExitWhenSkipped(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(noOnExitWhenSkipped) ctx := context.Background() - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.operate(ctx) _, err := woc.wf.GetNodeByName("B.onExit") require.Error(t, err) @@ -5343,10 +5354,10 @@ func TestGenerateNodeName(t *testing.T) { // This tests that we don't wait a backoff if it would exceed the maxDuration anyway. func TestPanicMetric(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(noOnExitWhenSkipped) - woc := newWoc(*wf) + ctx := context.Background() + woc := newWoc(ctx, *wf) // This should make the call to "operate" panic - ctx := context.Background() woc.preExecutionNodePhases = nil woc.operate(ctx) @@ -5365,19 +5376,19 @@ func TestControllerReferenceMode(t *testing.T) { ctx := context.Background() controller.Config.WorkflowRestrictions = &config.WorkflowRestrictions{} controller.Config.WorkflowRestrictions.TemplateReferencing = config.TemplateReferencingStrict - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowError, woc.wf.Status.Phase) assert.Equal(t, "workflows must use workflowTemplateRef to be executed when the controller is in reference mode", woc.wf.Status.Message) controller.Config.WorkflowRestrictions.TemplateReferencing = config.TemplateReferencingSecure - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowError, woc.wf.Status.Phase) assert.Equal(t, "workflows must use workflowTemplateRef to be executed when the controller is in reference mode", woc.wf.Status.Message) controller.Config.WorkflowRestrictions = nil - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) } @@ -5391,25 +5402,25 @@ func TestValidReferenceMode(t *testing.T) { ctx := context.Background() controller.Config.WorkflowRestrictions = &config.WorkflowRestrictions{} controller.Config.WorkflowRestrictions.TemplateReferencing = config.TemplateReferencingSecure - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) // Change stored Workflow Spec woc.wf.Status.StoredWorkflowSpec.Entrypoint = "different" - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowError, woc.wf.Status.Phase) assert.Equal(t, "WorkflowSpec may not change during execution when the controller is set `templateReferencing: Secure`", woc.wf.Status.Message) controller.Config.WorkflowRestrictions.TemplateReferencing = config.TemplateReferencingStrict - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) // Change stored Workflow Spec woc.wf.Status.StoredWorkflowSpec.Entrypoint = "different" - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowError, woc.wf.Status.Phase) } @@ -5517,7 +5528,7 @@ status: func TestWorkflowStatusMetric(t *testing.T) { ctx := context.Background() wf := wfv1.MustUnmarshalWorkflow(workflowStatusMetric) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.operate(ctx) // Must only be two (completed: true), (podRunning: true) assert.Len(t, woc.wf.Status.Conditions, 2) @@ -5539,27 +5550,27 @@ spec: cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) assert.Nil(t, woc.wf.Status.Conditions, "zero conditions on first reconciliation") makePodsPhase(ctx, woc, apiv1.PodPending) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) assert.Equal(t, wfv1.Conditions{{Type: wfv1.ConditionTypePodRunning, Status: metav1.ConditionFalse}}, woc.wf.Status.Conditions) makePodsPhase(ctx, woc, apiv1.PodRunning) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) assert.Equal(t, wfv1.Conditions{{Type: wfv1.ConditionTypePodRunning, Status: metav1.ConditionTrue}}, woc.wf.Status.Conditions) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) @@ -5629,7 +5640,7 @@ func TestConfigMapCacheLoadOperate(t *testing.T) { _, err = controller.kubeclientset.CoreV1().ConfigMaps("default").Create(ctx, &sampleConfigMapCacheEntry, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) require.Len(t, woc.wf.Status.Nodes, 1) @@ -5701,7 +5712,7 @@ func TestConfigMapCacheLoadOperateNoOutputs(t *testing.T) { _, err = controller.kubeclientset.CoreV1().ConfigMaps("default").Create(ctx, &sampleConfigMapCacheEntry, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) require.Len(t, woc.wf.Status.Nodes, 1) @@ -5816,7 +5827,7 @@ func TestGetOutboundNodesFromCacheHitSteps(t *testing.T) { _, err = controller.kubeclientset.CoreV1().ConfigMaps("default").Create(ctx, &myConfigMapCacheEntry, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) woc.operate(ctx) @@ -5863,7 +5874,7 @@ func TestGetOutboundNodesFromCacheHitDAG(t *testing.T) { _, err = controller.kubeclientset.CoreV1().ConfigMaps("default").Create(ctx, &myConfigMapCacheEntry, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) woc.operate(ctx) @@ -5946,7 +5957,7 @@ func TestConfigMapCacheLoadOperateMaxAge(t *testing.T) { _, err = controller.kubeclientset.CoreV1().ConfigMaps("default").Create(ctx, &nonExpiredEntry, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) require.Len(t, woc.wf.Status.Nodes, 1) @@ -5965,7 +5976,7 @@ func TestConfigMapCacheLoadOperateMaxAge(t *testing.T) { _, err = controller.kubeclientset.CoreV1().ConfigMaps("default").Create(ctx, &expiredEntry, metav1.CreateOptions{}) require.NoError(t, err) - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) require.Len(t, woc.wf.Status.Nodes, 1) @@ -6153,7 +6164,7 @@ func TestStepConfigMapCacheCreateWhenHaveRetryStrategy(t *testing.T) { _, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.ObjectMeta.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) woc.operate(ctx) @@ -6173,7 +6184,7 @@ func TestDAGConfigMapCacheCreateWhenHaveRetryStrategy(t *testing.T) { _, err := controller.wfclientset.ArgoprojV1alpha1().Workflows(wf.ObjectMeta.Namespace).Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) woc.operate(ctx) @@ -6208,7 +6219,7 @@ func TestConfigMapCacheLoadNoLabels(t *testing.T) { _, err = controller.kubeclientset.CoreV1().ConfigMaps("default").Create(ctx, &sampleConfigMapCacheEntry, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) fn := func() { woc.operate(ctx) } @@ -6249,7 +6260,7 @@ func TestConfigMapCacheLoadNilOutputs(t *testing.T) { _, err = controller.kubeclientset.CoreV1().ConfigMaps("default").Create(ctx, &sampleConfigMapCacheEntry, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) fn := func() { woc.operate(ctx) } @@ -6268,7 +6279,8 @@ func TestConfigMapCacheSaveOperate(t *testing.T) { cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) sampleOutputs := wfv1.Outputs{ Parameters: []wfv1.Parameter{ {Name: "hello", Value: wfv1.AnyStringPtr("foobar")}, @@ -6276,10 +6288,9 @@ func TestConfigMapCacheSaveOperate(t *testing.T) { ExitCode: ptr.To("0"), } - ctx := context.Background() woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded, withExitCode(0), withOutputs(sampleOutputs)) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) cm, err := controller.kubeclientset.CoreV1().ConfigMaps("default").Get(ctx, "whalesay-cache", metav1.GetOptions{}) @@ -6322,7 +6333,8 @@ func TestPropagateMaxDurationProcess(t *testing.T) { assert.NotNil(t, controller) wf := wfv1.MustUnmarshalWorkflow(propagate) assert.NotNil(t, wf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) err := woc.setExecWorkflow(context.Background()) require.NoError(t, err) @@ -6330,7 +6342,7 @@ func TestPropagateMaxDurationProcess(t *testing.T) { // Add the parent node for retries. nodeName := "test-node" - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) retries := wfv1.RetryStrategy{ Limit: intstrutil.ParsePtr("2"), Backoff: &wfv1.Backoff{ @@ -6342,13 +6354,13 @@ func TestPropagateMaxDurationProcess(t *testing.T) { woc.wf.Status.Nodes[woc.wf.NodeID(nodeName)] = *node childNode := fmt.Sprintf("%s(%d)", nodeName, 0) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeFailed, &wfv1.NodeFlag{Retried: true}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeFailed, &wfv1.NodeFlag{Retried: true}) + woc.addChildNode(ctx, nodeName, childNode) var opts executeTemplateOpts n, err := woc.wf.GetNodeByName(nodeName) require.NoError(t, err) - _, _, err = woc.processNodeRetries(n, retries, &opts) + _, _, err = woc.processNodeRetries(ctx, n, retries, &opts) require.NoError(t, err) assert.Equal(t, n.StartedAt.Add(20*time.Second).Round(time.Second).String(), opts.executionDeadline.Round(time.Second).String()) } @@ -6400,18 +6412,19 @@ func TestCheckForbiddenErrorAndResbmitAllowed(t *testing.T) { cancel, controller := newController() defer cancel() wf := wfv1.MustUnmarshalWorkflow(resubmitPendingWf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) forbiddenErr := apierr.NewForbidden(schema.GroupResource{Group: "test", Resource: "test1"}, "test", errors.New("exceeded quota")) nonForbiddenErr := apierr.NewBadRequest("badrequest") t.Run("ForbiddenError", func(t *testing.T) { - node, err := woc.requeueIfTransientErr(forbiddenErr, "resubmit-pending-wf") + node, err := woc.requeueIfTransientErr(ctx, forbiddenErr, "resubmit-pending-wf") assert.NotNil(t, node) require.NoError(t, err) assert.Equal(t, wfv1.NodePending, node.Phase) }) t.Run("NonForbiddenError", func(t *testing.T) { - node, err := woc.requeueIfTransientErr(nonForbiddenErr, "resubmit-pending-wf") + node, err := woc.requeueIfTransientErr(ctx, nonForbiddenErr, "resubmit-pending-wf") require.Error(t, err) assert.Nil(t, node) }) @@ -6441,7 +6454,7 @@ status: cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) for _, node := range woc.wf.Status.Nodes { @@ -6488,7 +6501,7 @@ status: cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) assert.Equal(t, "modified", wf.Spec.Arguments.Parameters[0].Value.String()) @@ -6525,7 +6538,7 @@ status: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, "modified", wf.Spec.Arguments.Parameters[0].Value.String()) } @@ -6554,10 +6567,9 @@ spec: `) cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) - - // reconcile ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) + woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) @@ -6565,7 +6577,7 @@ spec: makePodsPhase(ctx, woc, apiv1.PodSucceeded) // reconcile - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) } @@ -6713,9 +6725,9 @@ func TestGlobalVarsOnExit(t *testing.T) { wftmpl := wfv1.MustUnmarshalWorkflowTemplate(wftmplGlobalVarsOnExit) cancel, controller := newController(wf, wftmpl) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) - ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) + woc.operate(ctx) node := woc.wf.Status.Nodes["hello-world-6gphm-8n22g-3224262006"] @@ -6803,14 +6815,14 @@ func TestFailSuspendedAndPendingNodesAfterDeadline(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) t.Run("Before Deadline", func(t *testing.T) { woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) }) time.Sleep(3 * time.Second) t.Run("After Deadline", func(t *testing.T) { - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) for _, node := range woc.wf.Status.Nodes { @@ -6826,7 +6838,7 @@ func TestFailSuspendedAndPendingNodesAfterShutdown(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) t.Run("After Shutdown", func(t *testing.T) { woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) @@ -6914,7 +6926,7 @@ func TestTemplateTimeoutDuration(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) time.Sleep(6 * time.Second) @@ -6929,12 +6941,12 @@ func TestTemplateTimeoutDuration(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) time.Sleep(6 * time.Second) makePodsPhase(ctx, woc, apiv1.PodPending) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) @@ -6949,7 +6961,7 @@ func TestTemplateTimeoutDuration(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) jsonByte, err := json.Marshal(woc.wf) @@ -6966,7 +6978,7 @@ func TestTemplateTimeoutDuration(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) jsonByte, err := json.Marshal(woc.wf) @@ -7008,7 +7020,7 @@ func TestStorageQuota(t *testing.T) { }) ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowPending, woc.wf.Status.Phase) assert.Contains(t, woc.wf.Status.Message, "Waiting for a PVC to be created.") @@ -7069,7 +7081,8 @@ func TestPodFailureWithContainerWaitingState(t *testing.T) { var pod apiv1.Pod wfv1.MustUnmarshal(podWithFailed, &pod) assert.NotNil(t, pod) - nodeStatus, msg := newWoc().inferFailedReason(&pod, nil) + ctx := context.Background() + nodeStatus, msg := newWoc(ctx).inferFailedReason(&pod, nil) assert.Equal(t, wfv1.NodeError, nodeStatus) assert.Equal(t, "Pod failed before main container starts due to ContainerCreating: Container is creating", msg) } @@ -7183,7 +7196,8 @@ func TestPodFailureWithContainerOOM(t *testing.T) { for _, tt := range tests { wfv1.MustUnmarshal(tt.podDetail, &pod) assert.NotNil(t, pod) - nodeStatus, msg := newWoc().inferFailedReason(&pod, nil) + ctx := context.Background() + nodeStatus, msg := newWoc(ctx).inferFailedReason(&pod, nil) assert.Equal(t, tt.phase, nodeStatus) assert.Contains(t, msg, "OOMKilled") } @@ -7208,7 +7222,7 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) @@ -7219,7 +7233,7 @@ spec: time.Sleep(time.Second) deletePods(ctx, woc) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) @@ -7230,7 +7244,7 @@ spec: time.Sleep(time.Second) makePodsPhase(ctx, woc, apiv1.PodSucceeded) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) @@ -7301,7 +7315,7 @@ func TestWFWithRetryAndWithParam(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -7502,7 +7516,7 @@ func TestParamAggregation(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) evenNode := woc.wf.Status.Nodes.FindByDisplayName("print-evenness") @@ -7595,14 +7609,16 @@ func TestRetryOnDiffHost(t *testing.T) { cancel, controller := newController() defer cancel() wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) // Verify that there are no nodes in the wf status. assert.Empty(t, woc.wf.Status.Nodes) // Add the parent node for retries. nodeName := "test-node" + nodeID := woc.wf.NodeID(nodeName) - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) hostSelector := "kubernetes.io/hostname" retries := wfv1.RetryStrategy{} @@ -7620,16 +7636,16 @@ func TestRetryOnDiffHost(t *testing.T) { // Add child node. childNode := fmt.Sprintf("%s(%d)", nodeName, 0) - woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) - woc.addChildNode(nodeName, childNode) + woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + woc.addChildNode(ctx, nodeName, childNode) n, err := woc.wf.GetNodeByName(nodeName) require.NoError(t, err) lastChild = getChildNodeIndex(n, woc.wf.Status.Nodes, -1) assert.NotNil(t, lastChild) - woc.markNodePhase(lastChild.Name, wfv1.NodeFailed) - _, _, err = woc.processNodeRetries(n, retries, &executeTemplateOpts{}) + woc.markNodePhase(ctx, lastChild.Name, wfv1.NodeFailed) + _, _, err = woc.processNodeRetries(ctx, n, retries, &executeTemplateOpts{}) require.NoError(t, err) n, err = woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -7694,7 +7710,7 @@ func TestRetryOnNodeAntiAffinity(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) @@ -7788,7 +7804,7 @@ func TestNoPodsWhenShutdown(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) node := woc.wf.Status.Nodes.FindByDisplayName("hello-world") @@ -7821,7 +7837,7 @@ func TestWorkflowScheduledTimeVariable(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, "2006-01-02T15:04:05-07:00", woc.globalParams[common.GlobalVarWorkflowCronScheduleTime]) } @@ -7848,7 +7864,7 @@ func TestWorkflowMainEntrypointVariable(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, "whalesay", woc.globalParams[common.GlobalVarWorkflowMainEntrypoint]) } @@ -7890,7 +7906,7 @@ func TestWorkflowInterpolatesNodeNameField(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) foundPod := false @@ -7926,7 +7942,7 @@ spec: defer cancel() t.Run("StopStrategy", func(t *testing.T) { ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { @@ -7937,7 +7953,7 @@ spec: // Simulate the Stop command wf1 := woc.wf wf1.Spec.Shutdown = wfv1.ShutdownStrategyStop - woc1 := newWorkflowOperationCtx(wf1, controller) + woc1 := newWorkflowOperationCtx(ctx, wf1, controller) woc1.operate(ctx) node := woc1.wf.Status.Nodes.FindByDisplayName("whalesay") @@ -7948,7 +7964,7 @@ spec: t.Run("TerminateStrategy", func(t *testing.T) { ctx := context.Background() - woc := newWorkflowOperationCtx(wf1, controller) + woc := newWorkflowOperationCtx(ctx, wf1, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { @@ -7959,7 +7975,7 @@ spec: // Simulate the Terminate command wfOut := woc.wf wfOut.Spec.Shutdown = wfv1.ShutdownStrategyTerminate - woc1 := newWorkflowOperationCtx(wfOut, controller) + woc1 := newWorkflowOperationCtx(ctx, wfOut, controller) woc1.operate(ctx) for _, node := range woc1.wf.Status.Nodes { require.NotNil(t, node) @@ -8201,7 +8217,7 @@ func TestStepsFailFast(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) @@ -8294,7 +8310,7 @@ func TestRootRetryStrategyCompletes(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) @@ -8331,9 +8347,9 @@ func TestSubstituteGlobalVariables(t *testing.T) { cancel, controller := newController(wf) defer cancel() - // ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) - err := woc.setExecWorkflow(context.Background()) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) + err := woc.setExecWorkflow(ctx) require.NoError(t, err) assert.NotNil(t, woc.execWf) assert.Equal(t, "mutex1", woc.execWf.Spec.Synchronization.Mutex.Name) @@ -8414,8 +8430,9 @@ func TestSubstituteGlobalVariablesLabelsAnnotations(t *testing.T) { wftmpl := wfv1.MustUnmarshalWorkflowTemplate(tt.workflowTemplate) cancel, controller := newController(wf, wftmpl) defer cancel() + ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(context.Background()) require.NoError(t, err) @@ -8476,7 +8493,7 @@ func TestWfPendingWithNoPod(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) pods, err := listPods(woc) @@ -8544,7 +8561,7 @@ func TestMutexWfPendingWithNoPod(t *testing.T) { // preempt lock _, _, _, _, err := controller.syncManager.TryAcquire(ctx, wf, "test", &wfv1.Synchronization{Mutex: &wfv1.Mutex{Name: "welcome"}}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) @@ -8631,7 +8648,7 @@ func TestWFGlobalArtifactNil(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodRunning) woc.operate(ctx) @@ -8750,7 +8767,7 @@ func TestDagTwoChildrenWithNonExpectedNodeType(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -8809,7 +8826,7 @@ func TestDagTwoChildrenContainerSet(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) woc.operate(ctx) @@ -8985,7 +9002,7 @@ func TestOperatorRetryExpression(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -9176,7 +9193,7 @@ func TestOperatorRetryExpressionError(t *testing.T) { cancel, controller := newController(wf) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -9349,7 +9366,7 @@ func TestOperatorRetryExpressionErrorNoExpr(t *testing.T) { cancel, controller := newController(wf) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -9529,7 +9546,7 @@ func TestExitHandlerWithRetryNodeParam(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) retryStepNode, err := woc.wf.GetNodeByName("exit-handler-with-param-xbh52[0].step-1") @@ -9564,7 +9581,7 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotPanics(t, func() { woc.operate(ctx) }) } @@ -9584,7 +9601,7 @@ func TestSetWFPodNamesAnnotation(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) annotations := woc.wf.ObjectMeta.GetAnnotations() @@ -10031,7 +10048,7 @@ func TestRetryLoopWithOutputParam(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) @@ -10137,10 +10154,10 @@ status: func TestFailNodesWithoutCreatedPodsAfterDeadlineOrShutdown(t *testing.T) { cancel, controller := newController() defer cancel() - + ctx := context.Background() t.Run("Shutdown", func(t *testing.T) { workflow := wfv1.MustUnmarshalWorkflow(workflowShuttingDownWithNodesInPendingAfterReconciliation) - woc := newWorkflowOperationCtx(workflow, controller) + woc := newWorkflowOperationCtx(ctx, workflow, controller) woc.execWf.Spec.Shutdown = "Terminate" woc.execWf.Spec.ActiveDeadlineSeconds = nil @@ -10167,7 +10184,7 @@ func TestFailNodesWithoutCreatedPodsAfterDeadlineOrShutdown(t *testing.T) { assert.Equal(t, wfv1.NodeRunning, woc.wf.Status.Nodes[step1NodeName].Phase) assert.Equal(t, wfv1.NodeRunning, woc.wf.Status.Nodes[step2NodeName].Phase) - woc.failNodesWithoutCreatedPodsAfterDeadlineOrShutdown() + woc.failNodesWithoutCreatedPodsAfterDeadlineOrShutdown(ctx) assert.Equal(t, wfv1.NodeRunning, woc.wf.Status.Nodes[step1NodeName].Phase) assert.Equal(t, wfv1.NodeFailed, woc.wf.Status.Nodes[step2NodeName].Phase) @@ -10175,7 +10192,7 @@ func TestFailNodesWithoutCreatedPodsAfterDeadlineOrShutdown(t *testing.T) { t.Run("Deadline", func(t *testing.T) { workflow := wfv1.MustUnmarshalWorkflow(workflowShuttingDownWithNodesInPendingAfterReconciliation) - woc := newWorkflowOperationCtx(workflow, controller) + woc := newWorkflowOperationCtx(ctx, workflow, controller) woc.execWf.Spec.Shutdown = "" @@ -10202,7 +10219,7 @@ func TestFailNodesWithoutCreatedPodsAfterDeadlineOrShutdown(t *testing.T) { assert.Equal(t, wfv1.NodeRunning, woc.wf.Status.Nodes[step1NodeName].Phase) assert.Equal(t, wfv1.NodePending, woc.wf.Status.Nodes[step2NodeName].Phase) - woc.failNodesWithoutCreatedPodsAfterDeadlineOrShutdown() + woc.failNodesWithoutCreatedPodsAfterDeadlineOrShutdown(ctx) assert.Equal(t, wfv1.NodeRunning, woc.wf.Status.Nodes[step1NodeName].Phase) assert.Equal(t, wfv1.NodeFailed, woc.wf.Status.Nodes[step2NodeName].Phase) @@ -10225,7 +10242,7 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) t.Log(woc.wf) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) @@ -10248,7 +10265,7 @@ spec: defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) t.Log(woc.wf) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) @@ -10300,7 +10317,7 @@ func TestMemoizationTemplateLevelCacheWithStepWithoutCache(t *testing.T) { ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) @@ -10349,7 +10366,7 @@ func TestMemoizationTemplateLevelCacheWithStepWithCache(t *testing.T) { _, err := controller.kubeclientset.CoreV1().ConfigMaps("default").Create(ctx, &sampleConfigMapCacheEntry, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) @@ -10415,7 +10432,7 @@ func TestMemoizationTemplateLevelCacheWithDagWithoutCache(t *testing.T) { ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) @@ -10464,7 +10481,7 @@ func TestMemoizationTemplateLevelCacheWithDagWithCache(t *testing.T) { _, err := controller.kubeclientset.CoreV1().ConfigMaps("default").Create(ctx, &sampleConfigMapCacheEntry, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) makePodsPhase(ctx, woc, apiv1.PodSucceeded) @@ -10533,7 +10550,7 @@ func TestMaxDepth(t *testing.T) { // Max depth is too small, error expected controller.maxStackDepth = 2 ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -10545,7 +10562,7 @@ func TestMaxDepth(t *testing.T) { // Max depth is enabled, but not too small, no error expected controller.maxStackDepth = 3 - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) @@ -10571,7 +10588,7 @@ func TestMaxDepthEnvVariable(t *testing.T) { // Max depth is disabled, no error expected controller.maxStackDepth = 2 ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) t.Setenv("DISABLE_MAX_RECURSION", "true") woc.operate(ctx) @@ -10592,20 +10609,21 @@ func TestMaxDepthEnvVariable(t *testing.T) { func TestGetChildNodeIdsAndLastRetriedNode(t *testing.T) { nodeName := "test-node" + ctx := context.Background() setup := func() *wfOperationCtx { cancel, controller := newController() defer cancel() assert.NotNil(t, controller) wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) assert.NotNil(t, wf) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotNil(t, woc) // Verify that there are no nodes in the wf status. assert.Empty(t, woc.wf.Status.Nodes) // Add the parent node for retries. nodeID := woc.wf.NodeID(nodeName) - node := woc.initializeNode(nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) + node := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeRetry, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{}) woc.wf.Status.Nodes[nodeID] = *node // Ensure there are no child nodes yet. @@ -10619,8 +10637,8 @@ func TestGetChildNodeIdsAndLastRetriedNode(t *testing.T) { // Add child nodes. for i := 0; i < 2; i++ { childNode := fmt.Sprintf("%s(%d)", nodeName, i) - childNodes = append(childNodes, woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true})) - woc.addChildNode(nodeName, childNode) + childNodes = append(childNodes, woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true})) + woc.addChildNode(ctx, nodeName, childNode) } node, err := woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -10636,14 +10654,14 @@ func TestGetChildNodeIdsAndLastRetriedNode(t *testing.T) { // Add child nodes. for i := 0; i < 2; i++ { childNode := fmt.Sprintf("%s(%d)", nodeName, i) - childNodes = append(childNodes, woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true})) - woc.addChildNode(nodeName, childNode) + childNodes = append(childNodes, woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true})) + woc.addChildNode(ctx, nodeName, childNode) } // Add child hooked nodes childNode := fmt.Sprintf("%s.hook.running", nodeName) - childNodes = append(childNodes, woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Hooked: true})) - woc.addChildNode(nodeName, childNode) + childNodes = append(childNodes, woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Hooked: true})) + woc.addChildNode(ctx, nodeName, childNode) node, err := woc.wf.GetNodeByName(nodeName) require.NoError(t, err) @@ -10660,8 +10678,8 @@ func TestGetChildNodeIdsAndLastRetriedNode(t *testing.T) { // Add child hooked noes for i := 0; i < 2; i++ { childNode := fmt.Sprintf("%s(%d)", nodeName, i) - childNodes = append(childNodes, woc.initializeNode(childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true, Hooked: true})) - woc.addChildNode(nodeName, childNode) + childNodes = append(childNodes, woc.initializeNode(ctx, childNode, wfv1.NodeTypePod, "", &wfv1.WorkflowStep{}, "", wfv1.NodeRunning, &wfv1.NodeFlag{Retried: true, Hooked: true})) + woc.addChildNode(ctx, nodeName, childNode) } node, err := woc.wf.GetNodeByName(nodeName) @@ -10795,7 +10813,7 @@ status: return true, pod, nil }) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) } @@ -10866,7 +10884,7 @@ func TestWorkflowNeedReconcile(t *testing.T) { require.NoError(t, err) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -10876,7 +10894,7 @@ func TestWorkflowNeedReconcile(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodSucceeded) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) for _, node := range woc.wf.Status.Nodes { woc.wf.Status.MarkTaskResultIncomplete(node.ID) } @@ -10907,7 +10925,7 @@ func TestWorkflowNeedReconcile(t *testing.T) { makePodsPhase(ctx, woc, apiv1.PodSucceeded) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err = listPods(woc) require.NoError(t, err) @@ -11126,7 +11144,7 @@ status: assert.False(t, reconceilNeeded) delete(wf.Labels, common.LabelKeyCompleted) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) assert.NotEmpty(t, woc.wf.Status.Nodes) nodeId := "wf-retry-stopped-pn6mm-1672493720" @@ -11139,8 +11157,8 @@ status: assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) delete(wf.Labels, common.LabelKeyCompleted) - woc = newWorkflowOperationCtx(wf, controller) - n := woc.markNodePhase(wf.Name, wfv1.NodeError) + woc = newWorkflowOperationCtx(ctx, wf, controller) + n := woc.markNodePhase(ctx, wf.Name, wfv1.NodeError) assert.Equal(t, wfv1.NodeError, n.Phase) woc.wf.Status.MarkTaskResultIncomplete(nodeId) woc.operate(ctx) @@ -11151,8 +11169,8 @@ status: assert.Equal(t, wfv1.WorkflowError, woc.wf.Status.Phase) delete(wf.Labels, common.LabelKeyCompleted) - woc = newWorkflowOperationCtx(wf, controller) - n = woc.markNodePhase(wf.Name, wfv1.NodeSucceeded) + woc = newWorkflowOperationCtx(ctx, wf, controller) + n = woc.markNodePhase(ctx, wf.Name, wfv1.NodeSucceeded) assert.Equal(t, wfv1.NodeSucceeded, n.Phase) woc.wf.Status.MarkTaskResultIncomplete(nodeId) woc.operate(ctx) @@ -11208,7 +11226,7 @@ func TestContainerSetWhenPodDeleted(t *testing.T) { require.NoError(t, err) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -11216,7 +11234,7 @@ func TestContainerSetWhenPodDeleted(t *testing.T) { // mark pod Running makePodsPhase(ctx, woc, apiv1.PodRunning) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { if node.Type == wfv1.NodeTypePod { @@ -11231,7 +11249,7 @@ func TestContainerSetWhenPodDeleted(t *testing.T) { assert.Empty(t, pods.Items) // reconcile - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowError, woc.wf.Status.Phase) for _, node := range woc.wf.Status.Nodes { @@ -11292,7 +11310,7 @@ func TestContainerSetWithDependenciesWhenPodDeleted(t *testing.T) { require.NoError(t, err) wf, err = wfcset.Get(ctx, wf.ObjectMeta.Name, metav1.GetOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pods, err := listPods(woc) require.NoError(t, err) @@ -11300,7 +11318,7 @@ func TestContainerSetWithDependenciesWhenPodDeleted(t *testing.T) { // mark pod Running makePodsPhase(ctx, woc, apiv1.PodRunning) - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) for _, node := range woc.wf.Status.Nodes { if node.Type == wfv1.NodeTypePod { @@ -11315,7 +11333,7 @@ func TestContainerSetWithDependenciesWhenPodDeleted(t *testing.T) { assert.Empty(t, pods.Items) // reconcile - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowError, woc.wf.Status.Phase) for _, node := range woc.wf.Status.Nodes { @@ -11462,7 +11480,7 @@ func TestGetOutboundNodesFromDAGContainerset(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) found := false diff --git a/workflow/controller/operator_tmpl_default_test.go b/workflow/controller/operator_tmpl_default_test.go index 65a8ffaebaa1..3907efc296d0 100644 --- a/workflow/controller/operator_tmpl_default_test.go +++ b/workflow/controller/operator_tmpl_default_test.go @@ -115,6 +115,7 @@ spec: func TestSetTemplateDefault(t *testing.T) { cancel, controller := newController() defer cancel() + ctx := context.Background() controller.Config.WorkflowDefaults = &wfv1.Workflow{ Spec: wfv1.WorkflowSpec{ TemplateDefaults: &wfv1.Template{ @@ -127,7 +128,7 @@ func TestSetTemplateDefault(t *testing.T) { } t.Run("tmplDefaultInConfig", func(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(defaultWf) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(context.Background()) require.NoError(t, err) tmpl := woc.execWf.Spec.Templates[0] @@ -154,7 +155,7 @@ func TestSetTemplateDefault(t *testing.T) { Env: envs, }, } - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(context.Background()) require.NoError(t, err) tmpl := woc.execWf.Spec.Templates[0] @@ -184,7 +185,7 @@ func TestSetTemplateDefault(t *testing.T) { Env: envs, }, } - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(context.Background()) require.NoError(t, err) tmpl := woc.execWf.Spec.Templates[0] @@ -225,7 +226,7 @@ func TestSetTemplateDefault(t *testing.T) { Env: envs, }, } - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(context.Background()) require.NoError(t, err) tmpl := woc.execWf.Spec.Templates[0] diff --git a/workflow/controller/operator_wfdefault_test.go b/workflow/controller/operator_wfdefault_test.go index f44729100e7c..2b80719dff5c 100644 --- a/workflow/controller/operator_wfdefault_test.go +++ b/workflow/controller/operator_wfdefault_test.go @@ -205,14 +205,14 @@ func TestWFDefaultsWithWorkflow(t *testing.T) { ctx := context.Background() controller.Config.WorkflowDefaults = wfDefault - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(woc.wf.Spec, wfResult.Spec) assert.Contains(woc.wf.Labels, "testLabel") assert.Contains(woc.wf.Annotations, "testAnnotation") wf1.Spec.Entrypoint = "" - woc = newWorkflowOperationCtx(wf1, controller) + woc = newWorkflowOperationCtx(ctx, wf1, controller) woc.operate(ctx) assert.Equal(woc.wf.Spec, wfResult.Spec) assert.Contains(woc.wf.Labels, "testLabel") @@ -233,7 +233,7 @@ func TestWFDefaultWithWFTAndWf(t *testing.T) { controller.Config.WorkflowDefaults = wfDefault wf := wfv1.Workflow{ObjectMeta: metav1.ObjectMeta{Namespace: "default"}, Spec: wfv1.WorkflowSpec{WorkflowTemplateRef: &wfv1.WorkflowTemplateRef{Name: "workflow-template-submittable"}}} - woc := newWorkflowOperationCtx(&wf, controller) + woc := newWorkflowOperationCtx(ctx, &wf, controller) woc.operate(ctx) resultSpec.WorkflowTemplateRef = &wfv1.WorkflowTemplateRef{Name: "workflow-template-submittable"} assert.Equal(resultSpec, woc.execWf.Spec) @@ -261,7 +261,7 @@ func TestWFDefaultWithWFTAndWf(t *testing.T) { resultSpec.TTLStrategy = &ttlStrategy resultSpec.WorkflowTemplateRef = &wfv1.WorkflowTemplateRef{Name: "workflow-template-submittable"} - woc := newWorkflowOperationCtx(&wf, controller) + woc := newWorkflowOperationCtx(ctx, &wf, controller) woc.operate(ctx) assert.Equal(resultSpec, woc.execWf.Spec) assert.Equal(&resultSpec, woc.wf.Status.StoredWorkflowSpec) @@ -304,7 +304,7 @@ func TestWFDefaultWithWFTAndWf(t *testing.T) { resultSpec.Arguments.Parameters = append(resultSpec.Arguments.Parameters, param) resultSpec.Arguments.Artifacts = append(resultSpec.Arguments.Artifacts, art) - woc := newWorkflowOperationCtx(&wf, controller) + woc := newWorkflowOperationCtx(ctx, &wf, controller) woc.operate(ctx) assert.Contains(woc.execWf.Spec.Arguments.Parameters, param) assert.Contains(woc.wf.Status.StoredWorkflowSpec.Arguments.Artifacts, art) diff --git a/workflow/controller/operator_workflow_template_ref_test.go b/workflow/controller/operator_workflow_template_ref_test.go index 8fbf54d85f7d..5d52b44ff1e4 100644 --- a/workflow/controller/operator_workflow_template_ref_test.go +++ b/workflow/controller/operator_workflow_template_ref_test.go @@ -19,7 +19,7 @@ func TestWorkflowTemplateRef(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wfv1.MustUnmarshalWorkflow(wfWithTmplRef), controller) + woc := newWorkflowOperationCtx(ctx, wfv1.MustUnmarshalWorkflow(wfWithTmplRef), controller) woc.operate(ctx) assert.Equal(t, wfv1.MustUnmarshalWorkflowTemplate(wfTmpl).Spec.Templates, woc.execWf.Spec.Templates) assert.Equal(t, woc.wf.Spec.Entrypoint, woc.execWf.Spec.Entrypoint) @@ -45,7 +45,7 @@ func TestWorkflowTemplateRefWithArgs(t *testing.T) { wf.Spec.Arguments.Parameters = util.MergeParameters(wf.Spec.Arguments.Parameters, args) cancel, controller := newController(wf, wftmpl) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, "test", woc.globalParams["workflow.parameters.param1"]) }) @@ -66,7 +66,7 @@ func TestWorkflowTemplateRefWithWorkflowTemplateArgs(t *testing.T) { wftmpl.Spec.Arguments.Parameters = util.MergeParameters(wf.Spec.Arguments.Parameters, args) cancel, controller := newController(wf, wftmpl) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, "test", woc.globalParams["workflow.parameters.param1"]) }) @@ -80,7 +80,7 @@ func TestWorkflowTemplateRefWithWorkflowTemplateArgs(t *testing.T) { ActiveDeadlineSeconds: &wfDefaultActiveS, }, } - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfDefaultActiveS, *woc.execWf.Spec.ActiveDeadlineSeconds) }) @@ -98,12 +98,12 @@ func TestWorkflowTemplateRefWithWorkflowTemplateArgs(t *testing.T) { }, } wf.Spec.ActiveDeadlineSeconds = &wfActiveS - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfActiveS, *woc.execWf.Spec.ActiveDeadlineSeconds) wf.Spec.ActiveDeadlineSeconds = nil - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wftActiveS, *woc.execWf.Spec.ActiveDeadlineSeconds) }) @@ -127,7 +127,7 @@ func TestWorkflowTemplateRefInvalidWF(t *testing.T) { cancel, controller := newController(wf) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowError, woc.wf.Status.Phase) }) @@ -210,7 +210,7 @@ func TestWorkflowTemplateRefParamMerge(t *testing.T) { cancel, controller := newController(wf, wftmpl) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wf.Spec.Arguments.Parameters, woc.wf.Spec.Arguments.Parameters) }) @@ -278,7 +278,7 @@ func TestWorkflowTemplateRefGetArtifactsFromTemplate(t *testing.T) { cancel, controller := newController(wf, wftmpl) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Len(t, woc.execWf.Spec.Arguments.Artifacts, 3) @@ -294,14 +294,14 @@ func TestWorkflowTemplateRefWithShutdownAndSuspend(t *testing.T) { cancel, controller := newController(wf, wfv1.MustUnmarshalWorkflowTemplate(wfTmpl)) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Nil(t, woc.wf.Status.StoredWorkflowSpec.Suspend) wf1 := woc.wf.DeepCopy() // Updating Pod state makePodsPhase(ctx, woc, apiv1.PodPending) wf1.Status.StoredWorkflowSpec.Entrypoint = "" - woc1 := newWorkflowOperationCtx(wf1, controller) + woc1 := newWorkflowOperationCtx(ctx, wf1, controller) woc1.operate(ctx) assert.NotNil(t, woc1.wf.Status.StoredWorkflowSpec.Entrypoint) assert.Equal(t, woc.wf.Spec.Entrypoint, woc1.wf.Status.StoredWorkflowSpec.Entrypoint) @@ -312,14 +312,14 @@ func TestWorkflowTemplateRefWithShutdownAndSuspend(t *testing.T) { cancel, controller := newController(wf, wfv1.MustUnmarshalWorkflowTemplate(wfTmpl)) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Nil(t, woc.wf.Status.StoredWorkflowSpec.Suspend) wf1 := woc.wf.DeepCopy() // Updating Pod state makePodsPhase(ctx, woc, apiv1.PodPending) wf1.Spec.Suspend = ptr.To(true) - woc1 := newWorkflowOperationCtx(wf1, controller) + woc1 := newWorkflowOperationCtx(ctx, wf1, controller) woc1.operate(ctx) assert.NotNil(t, woc1.wf.Status.StoredWorkflowSpec.Suspend) assert.True(t, *woc1.wf.Status.StoredWorkflowSpec.Suspend) @@ -329,14 +329,14 @@ func TestWorkflowTemplateRefWithShutdownAndSuspend(t *testing.T) { cancel, controller := newController(wf, wfv1.MustUnmarshalWorkflowTemplate(wfTmpl)) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Empty(t, woc.wf.Status.StoredWorkflowSpec.Shutdown) wf1 := woc.wf.DeepCopy() // Updating Pod state makePodsPhase(ctx, woc, apiv1.PodPending) wf1.Spec.Shutdown = wfv1.ShutdownStrategyTerminate - woc1 := newWorkflowOperationCtx(wf1, controller) + woc1 := newWorkflowOperationCtx(ctx, wf1, controller) woc1.operate(ctx) assert.NotEmpty(t, woc1.wf.Status.StoredWorkflowSpec.Shutdown) assert.Equal(t, wfv1.ShutdownStrategyTerminate, woc1.wf.Status.StoredWorkflowSpec.Shutdown) @@ -351,14 +351,14 @@ func TestWorkflowTemplateRefWithShutdownAndSuspend(t *testing.T) { cancel, controller := newController(wf, wfv1.MustUnmarshalWorkflowTemplate(wfTmpl)) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Empty(t, woc.wf.Status.StoredWorkflowSpec.Shutdown) wf1 := woc.wf.DeepCopy() // Updating Pod state makePodsPhase(ctx, woc, apiv1.PodPending) wf1.Spec.Shutdown = wfv1.ShutdownStrategyStop - woc1 := newWorkflowOperationCtx(wf1, controller) + woc1 := newWorkflowOperationCtx(ctx, wf1, controller) woc1.operate(ctx) assert.NotEmpty(t, woc1.wf.Status.StoredWorkflowSpec.Shutdown) assert.Equal(t, wfv1.ShutdownStrategyStop, woc1.wf.Status.StoredWorkflowSpec.Shutdown) @@ -428,11 +428,11 @@ func TestSuspendResumeWorkflowTemplateRef(t *testing.T) { cancel, controller := newController(wf, wfv1.MustUnmarshalWorkflowTemplate(wfTmpl)) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.True(t, *woc.wf.Status.StoredWorkflowSpec.Suspend) woc.wf.Spec.Suspend = nil - woc = newWorkflowOperationCtx(woc.wf, controller) + woc = newWorkflowOperationCtx(ctx, woc.wf, controller) woc.operate(ctx) assert.Nil(t, woc.wf.Status.StoredWorkflowSpec.Suspend) } @@ -476,7 +476,7 @@ func TestWorkflowTemplateUpdateScenario(t *testing.T) { cancel, controller := newController(wf, wfv1.MustUnmarshalWorkflowTemplate(wfTmpl)) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.NotEmpty(t, woc.wf.Status.StoredWorkflowSpec) assert.NotEmpty(t, woc.wf.Status.StoredWorkflowSpec.Templates[0].Container) @@ -484,7 +484,7 @@ func TestWorkflowTemplateUpdateScenario(t *testing.T) { cancel, controller = newController(woc.wf, wfv1.MustUnmarshalWorkflowTemplate(wfTmplUpt)) defer cancel() ctx = context.Background() - woc1 := newWorkflowOperationCtx(woc.wf, controller) + woc1 := newWorkflowOperationCtx(ctx, woc.wf, controller) woc1.operate(ctx) assert.NotEmpty(t, woc1.wf.Status.StoredWorkflowSpec) assert.Equal(t, woc.wf.Status.StoredWorkflowSpec, woc1.wf.Status.StoredWorkflowSpec) @@ -521,7 +521,7 @@ func TestWFTWithVol(t *testing.T) { cancel, controller := newController(wf, wfv1.MustUnmarshalWorkflowTemplate(wfTmpl)) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) pvc, err := controller.kubeclientset.CoreV1().PersistentVolumeClaims("default").List(ctx, metav1.ListOptions{}) require.NoError(t, err) @@ -554,7 +554,7 @@ func TestSubmitWorkflowTemplateRefWithoutRBAC(t *testing.T) { cancel, controller := newController(wf, wfv1.MustUnmarshalWorkflowTemplate(wfTmpl)) defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.controller.cwftmplInformer = nil woc.operate(ctx) assert.Equal(t, wfv1.WorkflowError, woc.wf.Status.Phase) @@ -615,7 +615,7 @@ func TestWorkflowTemplateWithDynamicRef(t *testing.T) { defer cancel() ctx := context.Background() - woc := newWorkflowOperationCtx(wfv1.MustUnmarshalWorkflow(wfWithDynamicRef), controller) + woc := newWorkflowOperationCtx(ctx, wfv1.MustUnmarshalWorkflow(wfWithDynamicRef), controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) pods, err := listPods(woc) diff --git a/workflow/controller/plugin_template.go b/workflow/controller/plugin_template.go index 6aebe8bb100e..f83d4230f45f 100644 --- a/workflow/controller/plugin_template.go +++ b/workflow/controller/plugin_template.go @@ -1,16 +1,18 @@ package controller import ( + "context" + wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" ) -func (woc *wfOperationCtx) executePluginTemplate(nodeName string, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) *wfv1.NodeStatus { +func (woc *wfOperationCtx) executePluginTemplate(ctx context.Context, nodeName string, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) *wfv1.NodeStatus { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { if opts.boundaryID == "" { - woc.log.Warnf("[DEBUG] boundaryID was nil") + woc.log.Warnf(ctx, "[DEBUG] boundaryID was nil") } - node = woc.initializeExecutableNode(nodeName, wfv1.NodeTypePlugin, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) + node = woc.initializeExecutableNode(ctx, nodeName, wfv1.NodeTypePlugin, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodePending, opts.nodeFlag) } if !node.Fulfilled() { woc.taskSet[node.ID] = *tmpl diff --git a/workflow/controller/pod_cleanup.go b/workflow/controller/pod_cleanup.go index 12787ae77d22..bbe8b8958a2d 100644 --- a/workflow/controller/pod_cleanup.go +++ b/workflow/controller/pod_cleanup.go @@ -1,6 +1,8 @@ package controller import ( + "context" + apiv1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" @@ -10,12 +12,12 @@ import ( "github.com/argoproj/argo-workflows/v3/workflow/controller/indexes" ) -func (woc *wfOperationCtx) queuePodsForCleanup() { +func (woc *wfOperationCtx) queuePodsForCleanup(ctx context.Context) { delay := woc.controller.Config.GetPodGCDeleteDelayDuration() podGC := woc.execWf.Spec.PodGC podGCDelay, err := podGC.GetDeleteDelayDuration() if err != nil { - woc.log.WithError(err).Warn("failed to parse podGC.deleteDelayDuration") + woc.log.WithError(ctx, err).Warn(ctx, "failed to parse podGC.deleteDelayDuration") } else if podGCDelay >= 0 { delay = podGCDelay } @@ -31,7 +33,7 @@ func (woc *wfOperationCtx) queuePodsForCleanup() { nodeID := woc.nodeID(pod) nodePhase, err := woc.wf.Status.Nodes.GetPhase(nodeID) if err != nil { - woc.log.Errorf("was unable to obtain node for %s", nodeID) + woc.log.Errorf(ctx, "was unable to obtain node for %s", nodeID) continue } if !nodePhase.Fulfilled() { diff --git a/workflow/controller/steps.go b/workflow/controller/steps.go index 7926dce77abc..c03ad841bbdb 100644 --- a/workflow/controller/steps.go +++ b/workflow/controller/steps.go @@ -10,11 +10,11 @@ import ( "time" "github.com/Knetic/govaluate" - log "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" "github.com/argoproj/argo-workflows/v3/errors" wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + "github.com/argoproj/argo-workflows/v3/util/logging" "github.com/argoproj/argo-workflows/v3/util/template" "github.com/argoproj/argo-workflows/v3/workflow/common" controllercache "github.com/argoproj/argo-workflows/v3/workflow/controller/cache" @@ -40,17 +40,17 @@ type stepsContext struct { func (woc *wfOperationCtx) executeSteps(ctx context.Context, nodeName string, tmplCtx *templateresolution.Context, templateScope string, tmpl *wfv1.Template, orgTmpl wfv1.TemplateReferenceHolder, opts *executeTemplateOpts) (*wfv1.NodeStatus, error) { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - node = woc.initializeExecutableNode(nodeName, wfv1.NodeTypeSteps, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodeRunning, opts.nodeFlag) + node = woc.initializeExecutableNode(ctx, nodeName, wfv1.NodeTypeSteps, templateScope, tmpl, orgTmpl, opts.boundaryID, wfv1.NodeRunning, opts.nodeFlag) } defer func() { nodePhase, err := woc.wf.Status.Nodes.GetPhase(node.ID) if err != nil { - woc.log.Fatalf("was unable to obtain nodePhase for %s", node.ID) + woc.log.Fatalf(ctx, "was unable to obtain nodePhase for %s", node.ID) panic(fmt.Sprintf("unable to obtain nodePhase for %s", node.ID)) } if nodePhase.Fulfilled() { - woc.killDaemonedChildren(node.ID) + woc.killDaemonedChildren(ctx, node.ID) } }() @@ -70,15 +70,15 @@ func (woc *wfOperationCtx) executeSteps(ctx context.Context, nodeName string, tm { sgNode, err := woc.wf.GetNodeByName(sgNodeName) if err != nil { - _ = woc.initializeNode(sgNodeName, wfv1.NodeTypeStepGroup, stepTemplateScope, &wfv1.WorkflowStep{}, stepsCtx.boundaryID, wfv1.NodeRunning, &wfv1.NodeFlag{}) + _ = woc.initializeNode(ctx, sgNodeName, wfv1.NodeTypeStepGroup, stepTemplateScope, &wfv1.WorkflowStep{}, stepsCtx.boundaryID, wfv1.NodeRunning, &wfv1.NodeFlag{}) } else if !sgNode.Fulfilled() { - _ = woc.markNodePhase(sgNodeName, wfv1.NodeRunning) + _ = woc.markNodePhase(ctx, sgNodeName, wfv1.NodeRunning) } } // The following will connect the step group node to its parents. if i == 0 { // If this is the first step group, the boundary node is the parent - woc.addChildNode(nodeName, sgNodeName) + woc.addChildNode(ctx, nodeName, sgNodeName) } else { // Otherwise connect all the outbound nodes of the previous step group as parents to // the current step group node. @@ -90,18 +90,18 @@ func (woc *wfOperationCtx) executeSteps(ctx context.Context, nodeName string, tm if len(prevStepGroupNode.Children) == 0 { // corner case which connects an empty StepGroup (e.g. due to empty withParams) to // the previous StepGroup node - woc.addChildNode(prevStepGroupName, sgNodeName) + woc.addChildNode(ctx, prevStepGroupName, sgNodeName) } else { for _, childID := range prevStepGroupNode.Children { - outboundNodeIDs := woc.getOutboundNodes(childID) - woc.log.Infof("SG Outbound nodes of %s are %s", childID, outboundNodeIDs) + outboundNodeIDs := woc.getOutboundNodes(ctx, childID) + woc.log.Infof(ctx, "SG Outbound nodes of %s are %s", childID, outboundNodeIDs) for _, outNodeID := range outboundNodeIDs { outNodeName, err := woc.wf.Status.Nodes.GetName(outNodeID) if err != nil { - woc.log.Fatalf("was not able to obtain node name for %s", outNodeID) + woc.log.Fatalf(ctx, "was not able to obtain node name for %s", outNodeID) panic(fmt.Sprintf("could not obtain the out noden name for %s", outNodeID)) } - woc.addChildNode(outNodeName, sgNodeName) + woc.addChildNode(ctx, outNodeName, sgNodeName) } } } @@ -109,20 +109,20 @@ func (woc *wfOperationCtx) executeSteps(ctx context.Context, nodeName string, tm sgNode, err := woc.executeStepGroup(ctx, stepGroup.Steps, sgNodeName, &stepsCtx) if err != nil { - return woc.markNodeError(sgNodeName, err), nil + return woc.markNodeError(ctx, sgNodeName, err), nil } if !sgNode.Fulfilled() { - woc.log.Infof("Workflow step group node %s not yet completed", sgNode.ID) + woc.log.Infof(ctx, "Workflow step group node %s not yet completed", sgNode.ID) return node, nil } if sgNode.FailedOrError() { failMessage := fmt.Sprintf("step group %s was unsuccessful: %s", sgNode.ID, sgNode.Message) - woc.log.Info(failMessage) - if err = woc.updateOutboundNodes(nodeName, tmpl); err != nil { + woc.log.Info(ctx, failMessage) + if err = woc.updateOutboundNodes(ctx, nodeName, tmpl); err != nil { return nil, err } - return woc.markNodePhase(nodeName, wfv1.NodeFailed, sgNode.Message), nil + return woc.markNodePhase(ctx, nodeName, wfv1.NodeFailed, sgNode.Message), nil } // Add all outputs of each step in the group to the scope @@ -141,7 +141,7 @@ func (woc *wfOperationCtx) executeSteps(ctx context.Context, nodeName string, tm } if len(childNodes) > 0 { // Expanded child nodes should be created from the same template. - _, _, templateStored, err := stepsCtx.tmplCtx.ResolveTemplate(&childNodes[0]) + _, _, templateStored, err := stepsCtx.tmplCtx.ResolveTemplate(ctx, &childNodes[0]) if err != nil { return node, err } @@ -155,7 +155,7 @@ func (woc *wfOperationCtx) executeSteps(ctx context.Context, nodeName string, tm return node, err } } else { - woc.log.Infof("Step '%s' has no expanded child nodes", childNode) + woc.log.Infof(ctx, "Step '%s' has no expanded child nodes", childNode) } } else { woc.buildLocalScope(stepsCtx.scope, prefix, childNode) @@ -163,7 +163,7 @@ func (woc *wfOperationCtx) executeSteps(ctx context.Context, nodeName string, tm } } - err = woc.updateOutboundNodes(nodeName, tmpl) + err = woc.updateOutboundNodes(ctx, nodeName, tmpl) if err != nil { return nil, err } @@ -179,7 +179,7 @@ func (woc *wfOperationCtx) executeSteps(ctx context.Context, nodeName string, tm return nil, err } node.Outputs = outputs - woc.addOutputsToGlobalScope(node.Outputs) + woc.addOutputsToGlobalScope(ctx, node.Outputs) woc.wf.Status.Nodes.Set(node.ID, *node) } @@ -187,15 +187,15 @@ func (woc *wfOperationCtx) executeSteps(ctx context.Context, nodeName string, tm c := woc.controller.cacheFactory.GetCache(controllercache.ConfigMapCache, node.MemoizationStatus.CacheName) err := c.Save(ctx, node.MemoizationStatus.Key, node.ID, node.Outputs) if err != nil { - woc.log.WithFields(log.Fields{"nodeID": node.ID}).WithError(err).Error("Failed to save node outputs to cache") + woc.log.WithFields(ctx, logging.Fields{"nodeID": node.ID}).WithError(ctx, err).Error(ctx, "Failed to save node outputs to cache") node.Phase = wfv1.NodeError } } - return woc.markNodePhase(nodeName, wfv1.NodeSucceeded), nil + return woc.markNodePhase(ctx, nodeName, wfv1.NodeSucceeded), nil } // updateOutboundNodes set the outbound nodes from the last step group -func (woc *wfOperationCtx) updateOutboundNodes(nodeName string, tmpl *wfv1.Template) error { +func (woc *wfOperationCtx) updateOutboundNodes(ctx context.Context, nodeName string, tmpl *wfv1.Template) error { outbound := make([]string, 0) // Find the last, initialized stepgroup node var lastSGNode *wfv1.NodeStatus @@ -209,19 +209,19 @@ func (woc *wfOperationCtx) updateOutboundNodes(nodeName string, tmpl *wfv1.Templ } } if lastSGNode == nil { - woc.log.Warnf("node '%s' had no initialized StepGroup nodes", nodeName) + woc.log.Warnf(ctx, "node '%s' had no initialized StepGroup nodes", nodeName) return err } for _, childID := range lastSGNode.Children { - outboundNodeIDs := woc.getOutboundNodes(childID) - woc.log.Infof("Outbound nodes of %s is %s", childID, outboundNodeIDs) + outboundNodeIDs := woc.getOutboundNodes(ctx, childID) + woc.log.Infof(ctx, "Outbound nodes of %s is %s", childID, outboundNodeIDs) outbound = append(outbound, outboundNodeIDs...) } node, err := woc.wf.GetNodeByName(nodeName) if err != nil { return err } - woc.log.Infof("Outbound nodes of %s is %s", node.ID, outbound) + woc.log.Infof(ctx, "Outbound nodes of %s is %s", node.ID, outbound) node.OutboundNodes = outbound woc.wf.Status.Nodes.Set(node.ID, *node) return nil @@ -235,20 +235,20 @@ func (woc *wfOperationCtx) executeStepGroup(ctx context.Context, stepGroup []wfv return nil, err } if node.Fulfilled() { - woc.log.Debugf("Step group node %v already marked completed", node) + woc.log.Debugf(ctx, "Step group node %v already marked completed", node) return node, nil } // First, resolve any references to outputs from previous steps, and perform substitution - stepGroup, err = woc.resolveReferences(stepGroup, stepsCtx.scope) + stepGroup, err = woc.resolveReferences(ctx, stepGroup, stepsCtx.scope) if err != nil { - return woc.markNodeError(sgNodeName, err), nil + return woc.markNodeError(ctx, sgNodeName, err), nil } // Next, expand the step's withItems (if any) - stepGroup, err = woc.expandStepGroup(sgNodeName, stepGroup, stepsCtx) + stepGroup, err = woc.expandStepGroup(ctx, sgNodeName, stepGroup, stepsCtx) if err != nil { - return woc.markNodeError(sgNodeName, err), nil + return woc.markNodeError(ctx, sgNodeName, err), nil } // Maps nodes to their steps @@ -264,23 +264,23 @@ func (woc *wfOperationCtx) executeStepGroup(ctx context.Context, stepGroup []wfv // Check the step's when clause to decide if it should execute proceed, err := shouldExecute(step.When) if err != nil { - woc.initializeNode(childNodeName, wfv1.NodeTypeSkipped, stepTemplateScope, &step, stepsCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, err.Error()) - woc.addChildNode(sgNodeName, childNodeName) - woc.markNodeError(childNodeName, err) - return woc.markNodeError(sgNodeName, err), nil + woc.initializeNode(ctx, childNodeName, wfv1.NodeTypeSkipped, stepTemplateScope, &step, stepsCtx.boundaryID, wfv1.NodeError, &wfv1.NodeFlag{}, err.Error()) + woc.addChildNode(ctx, sgNodeName, childNodeName) + woc.markNodeError(ctx, childNodeName, err) + return woc.markNodeError(ctx, sgNodeName, err), nil } if !proceed { if _, err := woc.wf.GetNodeByName(childNodeName); err != nil { skipReason := fmt.Sprintf("when '%s' evaluated false", step.When) - woc.log.Infof("Skipping %s: %s", childNodeName, skipReason) - woc.initializeNode(childNodeName, wfv1.NodeTypeSkipped, stepTemplateScope, &step, stepsCtx.boundaryID, wfv1.NodeSkipped, &wfv1.NodeFlag{}, skipReason) - woc.addChildNode(sgNodeName, childNodeName) + woc.log.Infof(ctx, "Skipping %s: %s", childNodeName, skipReason) + woc.initializeNode(ctx, childNodeName, wfv1.NodeTypeSkipped, stepTemplateScope, &step, stepsCtx.boundaryID, wfv1.NodeSkipped, &wfv1.NodeFlag{}, skipReason) + woc.addChildNode(ctx, sgNodeName, childNodeName) } continue } if stepsCtx.boundaryID == "" { - woc.log.Warnf("boundaryID was nil") + woc.log.Warnf(ctx, "boundaryID was nil") } childNode, err := woc.executeTemplate(ctx, childNodeName, &step, stepsCtx.tmplCtx, step.Arguments, &executeTemplateOpts{boundaryID: stepsCtx.boundaryID, onExitTemplate: stepsCtx.onExitTemplate}) if err != nil { @@ -290,15 +290,15 @@ func (woc *wfOperationCtx) executeStepGroup(ctx context.Context, stepGroup []wfv case ErrParallelismReached: case ErrMaxDepthExceeded: case ErrTimeout: - return woc.markNodePhase(node.Name, wfv1.NodeFailed, err.Error()), nil + return woc.markNodePhase(ctx, node.Name, wfv1.NodeFailed, err.Error()), nil default: - woc.addChildNode(sgNodeName, childNodeName) - return woc.markNodeError(node.Name, fmt.Errorf("step group deemed errored due to child %s error: %w", childNodeName, err)), nil + woc.addChildNode(ctx, sgNodeName, childNodeName) + return woc.markNodeError(ctx, node.Name, fmt.Errorf("step group deemed errored due to child %s error: %w", childNodeName, err)), nil } } if childNode != nil { nodeSteps[childNodeName] = step - woc.addChildNode(sgNodeName, childNodeName) + woc.addChildNode(ctx, sgNodeName, childNodeName) } } @@ -312,14 +312,14 @@ func (woc *wfOperationCtx) executeStepGroup(ctx context.Context, stepGroup []wfv childNode, err := woc.wf.Status.Nodes.Get(childNodeID) if err != nil { errorMsg := fmt.Sprintf("was unable to obtain childNode for %s", childNodeID) - woc.log.Error(errorMsg) + woc.log.Error(ctx, errorMsg) return nil, fmt.Errorf("%s", errorMsg) } step := nodeSteps[childNode.Name] stepsCtx.scope.addParamToScope(fmt.Sprintf("steps.%s.status", childNode.DisplayName), string(childNode.Phase)) hookCompleted, err := woc.executeTmplLifeCycleHook(ctx, stepsCtx.scope, step.Hooks, childNode, stepsCtx.boundaryID, stepsCtx.tmplCtx, "steps."+step.Name) if err != nil { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) } // Check all hooks are completed if !hookCompleted { @@ -344,23 +344,23 @@ func (woc *wfOperationCtx) executeStepGroup(ctx context.Context, stepGroup []wfv return node, nil } - woc.addOutputsToGlobalScope(node.Outputs) + woc.addOutputsToGlobalScope(ctx, node.Outputs) // All children completed. Determine step group status as a whole for _, childNodeID := range node.Children { childNode, err := woc.wf.Status.Nodes.Get(childNodeID) if err != nil { - woc.log.Panicf("Coudn't obtain child for %s, panicking", childNodeID) + woc.log.Panicf(ctx, "Coudn't obtain child for %s, panicking", childNodeID) } step := nodeSteps[childNode.Name] if childNode.FailedOrError() && !step.ContinuesOn(childNode.Phase) { failMessage := fmt.Sprintf("child '%s' failed", childNodeID) - woc.log.Infof("Step group node %s deemed failed: %s", node.ID, failMessage) - return woc.markNodePhase(node.Name, wfv1.NodeFailed, failMessage), nil + woc.log.Infof(ctx, "Step group node %s deemed failed: %s", node.ID, failMessage) + return woc.markNodePhase(ctx, node.Name, wfv1.NodeFailed, failMessage), nil } } - woc.log.Infof("Step group node %v successful", node.ID) - return woc.markNodePhase(node.Name, wfv1.NodeSucceeded), nil + woc.log.Infof(ctx, "Step group node %v successful", node.ID) + return woc.markNodePhase(ctx, node.Name, wfv1.NodeSucceeded), nil } // shouldExecute evaluates a already substituted when expression to decide whether or not a step should execute @@ -419,7 +419,7 @@ func errorFromChannel(errCh <-chan error) error { // 3) dereferencing output.exitCode from previous steps // 4) dereferencing artifacts from previous steps // 5) dereferencing artifacts from inputs -func (woc *wfOperationCtx) resolveReferences(stepGroup []wfv1.WorkflowStep, scope *wfScope) ([]wfv1.WorkflowStep, error) { +func (woc *wfOperationCtx) resolveReferences(ctx context.Context, stepGroup []wfv1.WorkflowStep, scope *wfScope) ([]wfv1.WorkflowStep, error) { newStepGroup := make([]wfv1.WorkflowStep, len(stepGroup)) // Step 0: replace all parameter scope references for volumes @@ -495,7 +495,7 @@ func (woc *wfOperationCtx) resolveReferences(stepGroup []wfv1.WorkflowStep, scop go func(i int, step wfv1.WorkflowStep) { defer wg.Done() if err := resolveStepReferences(i, step, newStepGroup); err != nil { - woc.log.WithFields(log.Fields{"stepName": step.Name}).WithError(err).Error("Failed to resolve references") + woc.log.WithFields(ctx, logging.Fields{"stepName": step.Name}).WithError(ctx, err).Error(ctx, "Failed to resolve references") errCh <- err } <-parallelStepNum @@ -511,7 +511,7 @@ func (woc *wfOperationCtx) resolveReferences(stepGroup []wfv1.WorkflowStep, scop } // expandStepGroup looks at each step in a collection of parallel steps, and expands all steps using withItems/withParam -func (woc *wfOperationCtx) expandStepGroup(sgNodeName string, stepGroup []wfv1.WorkflowStep, stepsCtx *stepsContext) ([]wfv1.WorkflowStep, error) { +func (woc *wfOperationCtx) expandStepGroup(ctx context.Context, sgNodeName string, stepGroup []wfv1.WorkflowStep, stepsCtx *stepsContext) ([]wfv1.WorkflowStep, error) { newStepGroup := make([]wfv1.WorkflowStep, 0) for _, step := range stepGroup { if !step.ShouldExpand() { @@ -528,9 +528,9 @@ func (woc *wfOperationCtx) expandStepGroup(sgNodeName string, stepGroup []wfv1.W if _, err := woc.wf.GetNodeByName(childNodeName); err != nil { stepTemplateScope := stepsCtx.tmplCtx.GetTemplateScope() skipReason := "Skipped, empty params" - woc.log.Infof("Skipping %s: %s", childNodeName, skipReason) - woc.initializeNode(childNodeName, wfv1.NodeTypeSkipped, stepTemplateScope, &step, stepsCtx.boundaryID, wfv1.NodeSkipped, &wfv1.NodeFlag{}, skipReason) - woc.addChildNode(sgNodeName, childNodeName) + woc.log.Infof(ctx, "Skipping %s: %s", childNodeName, skipReason) + woc.initializeNode(ctx, childNodeName, wfv1.NodeTypeSkipped, stepTemplateScope, &step, stepsCtx.boundaryID, wfv1.NodeSkipped, &wfv1.NodeFlag{}, skipReason) + woc.addChildNode(ctx, sgNodeName, childNodeName) } } newStepGroup = append(newStepGroup, expandedStep...) diff --git a/workflow/controller/steps_test.go b/workflow/controller/steps_test.go index dae2fa623f2f..5385fe4f9790 100644 --- a/workflow/controller/steps_test.go +++ b/workflow/controller/steps_test.go @@ -17,7 +17,7 @@ import ( func TestStepsFailedRetries(t *testing.T) { ctx := context.Background() wf := wfv1.MustUnmarshalWorkflow("@testdata/steps-failed-retries.yaml") - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowFailed, woc.wf.Status.Phase) } @@ -75,7 +75,7 @@ func TestArtifactResolutionWhenSkipped(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(artifactResolutionWhenSkipped) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowSucceeded, woc.wf.Status.Phase) @@ -121,7 +121,7 @@ func TestStepsWithParamAndGlobalParam(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(stepsWithParamAndGlobalParam) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) @@ -310,7 +310,7 @@ func TestOptionalArgumentAndParameter(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(optionalArgumentAndParameter) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) @@ -440,7 +440,7 @@ func TestOptionalArgumentUseSubPathInLoop(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(artifactResolutionWhenOptionalAndSubpath) wf, err := wfcset.Create(ctx, wf, metav1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) diff --git a/workflow/controller/taskresult.go b/workflow/controller/taskresult.go index 49995a9e6baf..65884d4f4000 100644 --- a/workflow/controller/taskresult.go +++ b/workflow/controller/taskresult.go @@ -1,6 +1,7 @@ package controller import ( + "context" "reflect" "time" @@ -58,24 +59,24 @@ func recentlyDeleted(node *wfv1.NodeStatus) bool { return time.Since(node.FinishedAt.Time) <= envutil.LookupEnvDurationOr("RECENTLY_DELETED_POD_DURATION", 2*time.Minute) } -func (woc *wfOperationCtx) taskResultReconciliation() { +func (woc *wfOperationCtx) taskResultReconciliation(ctx context.Context) { objs, _ := woc.controller.taskResultInformer.GetIndexer().ByIndex(indexes.WorkflowIndex, woc.wf.Namespace+"/"+woc.wf.Name) - woc.log.WithField("numObjs", len(objs)).Info("Task-result reconciliation") + woc.log.WithField(ctx, "numObjs", len(objs)).Info(ctx, "Task-result reconciliation") for _, obj := range objs { result := obj.(*wfv1.WorkflowTaskResult) resultName := result.GetName() - woc.log.Debugf("task result:\n%+v", result) - woc.log.Debugf("task result name:\n%+v", resultName) + woc.log.Debugf(ctx, "task result:\n%+v", result) + woc.log.Debugf(ctx, "task result name:\n%+v", resultName) label := result.Labels[common.LabelKeyReportOutputsCompleted] // If the task result is completed, set the state to true. if label == "true" { - woc.log.Debugf("Marking task result complete %s", resultName) + woc.log.Debugf(ctx, "Marking task result complete %s", resultName) woc.wf.Status.MarkTaskResultComplete(resultName) } else if label == "false" { - woc.log.Debugf("Marking task result incomplete %s", resultName) + woc.log.Debugf(ctx, "Marking task result incomplete %s", resultName) woc.wf.Status.MarkTaskResultIncomplete(resultName) } @@ -88,14 +89,14 @@ func (woc *wfOperationCtx) taskResultReconciliation() { // Mark task result as completed if it has no chance to be completed. if label == "false" && old.Completed() && !woc.nodePodExist(*old) { if recentlyDeleted(old) { - woc.log.WithField("nodeID", nodeID).Debug("Wait for marking task result as completed because pod is recently deleted.") + woc.log.WithField(ctx, "nodeID", nodeID).Debug(ctx, "Wait for marking task result as completed because pod is recently deleted.") // If the pod was deleted, then it is possible that the controller never get another informer message about it. // In this case, the workflow will only be requeued after the resync period (20m). This means // workflow will not update for 20m. Requeuing here prevents that happening. woc.requeue() continue } - woc.log.WithField("nodeID", nodeID).Info("Marking task result as completed because pod has been deleted for a while.") + woc.log.WithField(ctx, "nodeID", nodeID).Info(ctx, "Marking task result as completed because pod has been deleted for a while.") woc.wf.Status.MarkTaskResultComplete(nodeID) } newNode := old.DeepCopy() @@ -113,8 +114,8 @@ func (woc *wfOperationCtx) taskResultReconciliation() { } if !reflect.DeepEqual(old, newNode) { woc.log. - WithField("nodeID", nodeID). - Debug("task-result changed") + WithField(ctx, "nodeID", nodeID). + Debug(ctx, "task-result changed") woc.wf.Status.Nodes.Set(nodeID, *newNode) woc.updated = true } diff --git a/workflow/controller/taskset.go b/workflow/controller/taskset.go index de244c139f47..cecf5961f4f1 100644 --- a/workflow/controller/taskset.go +++ b/workflow/controller/taskset.go @@ -10,11 +10,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - log "github.com/sirupsen/logrus" - "github.com/argoproj/argo-workflows/v3/errors" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow" wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + "github.com/argoproj/argo-workflows/v3/util/logging" "github.com/argoproj/argo-workflows/v3/workflow/common" controllercache "github.com/argoproj/argo-workflows/v3/workflow/controller/cache" ) @@ -53,10 +52,10 @@ func (woc *wfOperationCtx) getDeleteTaskAndNodePatch() (tasksPatch map[string]in return } -func (woc *wfOperationCtx) markTaskSetNodesError(err error) { +func (woc *wfOperationCtx) markTaskSetNodesError(ctx context.Context, err error) { for _, node := range woc.wf.Status.Nodes { if node.IsTaskSetNode() && !node.Fulfilled() { - woc.markNodeError(node.Name, err) + woc.markNodeError(ctx, node.Name, err) } } } @@ -98,17 +97,17 @@ func (woc *wfOperationCtx) getWorkflowTaskSet() (*wfv1.WorkflowTaskSet, error) { func (woc *wfOperationCtx) taskSetReconciliation(ctx context.Context) { if err := woc.reconcileTaskSet(ctx); err != nil { - woc.log.WithError(err).Error("error in workflowtaskset reconciliation") + woc.log.WithError(ctx, err).Error(ctx, "error in workflowtaskset reconciliation") return } if err := woc.reconcileAgentPod(ctx); err != nil { - woc.log.WithError(err).Error("error in agent pod reconciliation") - woc.markTaskSetNodesError(fmt.Errorf(`create agent pod failed with reason:"%s"`, err)) + woc.log.WithError(ctx, err).Error(ctx, "error in agent pod reconciliation") + woc.markTaskSetNodesError(ctx, fmt.Errorf(`create agent pod failed with reason:"%s"`, err)) return } } -func (woc *wfOperationCtx) nodeRequiresTaskSetReconciliation(nodeName string) bool { +func (woc *wfOperationCtx) nodeRequiresTaskSetReconciliation(ctx context.Context, nodeName string) bool { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { return false @@ -121,10 +120,10 @@ func (woc *wfOperationCtx) nodeRequiresTaskSetReconciliation(nodeName string) bo // If any of the node's children need an HTTP reconciliation, the parent node will also need one childNodeName, err := woc.wf.Status.Nodes.GetName(child) if err != nil { - woc.log.Fatalf("was unable to get child node name for %s", child) + woc.log.Fatalf(ctx, "was unable to get child node name for %s", child) panic("unable to obtain child node name") } - if woc.nodeRequiresTaskSetReconciliation(childNodeName) { + if woc.nodeRequiresTaskSetReconciliation(ctx, childNodeName) { return true } } @@ -138,13 +137,13 @@ func (woc *wfOperationCtx) reconcileTaskSet(ctx context.Context) error { return err } - woc.log.Info("TaskSet Reconciliation") + woc.log.Info(ctx, "TaskSet Reconciliation") if workflowTaskSet != nil && len(workflowTaskSet.Status.Nodes) > 0 { for nodeID, taskResult := range workflowTaskSet.Status.Nodes { node, err := woc.wf.Status.Nodes.Get(nodeID) if err != nil { - woc.log.Warnf("[SPECIAL][DEBUG] returning but assumed validity before") - woc.log.Errorf("[DEBUG] Was unable to obtain node for %s", nodeID) + woc.log.Warnf(ctx, "returning but assumed validity before") + woc.log.Errorf(ctx, "was unable to obtain node for %s", nodeID) return err } @@ -158,7 +157,7 @@ func (woc *wfOperationCtx) reconcileTaskSet(ctx context.Context) error { c := woc.controller.cacheFactory.GetCache(controllercache.ConfigMapCache, node.MemoizationStatus.CacheName) err := c.Save(ctx, node.MemoizationStatus.Key, node.ID, node.Outputs) if err != nil { - woc.log.WithFields(log.Fields{"nodeID": node.ID}).WithError(err).Error("Failed to save node outputs to cache") + woc.log.WithFields(ctx, logging.Fields{"nodeID": node.ID}).WithError(ctx, err).Error(ctx, "Failed to save node outputs to cache") } } woc.updated = true @@ -172,7 +171,7 @@ func (woc *wfOperationCtx) createTaskSet(ctx context.Context) error { return nil } - woc.log.Info("Creating TaskSet") + woc.log.Info(ctx, "Creating TaskSet") taskSet := wfv1.WorkflowTaskSet{ TypeMeta: metav1.TypeMeta{ Kind: workflow.WorkflowTaskSetKind, @@ -194,12 +193,12 @@ func (woc *wfOperationCtx) createTaskSet(ctx context.Context) error { Tasks: woc.taskSet, }, } - woc.log.Debug("creating new taskset") + woc.log.Debug(ctx, "creating new taskset") _, err := woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets(woc.wf.Namespace).Create(ctx, &taskSet, metav1.CreateOptions{}) if apierr.IsConflict(err) || apierr.IsAlreadyExists(err) { - woc.log.Debug("patching the exiting taskset") + woc.log.Debug(ctx, "patching the exiting taskset") spec := map[string]interface{}{ "metadata": metav1.ObjectMeta{ Labels: map[string]string{ @@ -211,11 +210,11 @@ func (woc *wfOperationCtx) createTaskSet(ctx context.Context) error { // patch the new templates into taskset err = woc.mergePatchTaskSet(ctx, spec) if err != nil { - woc.log.WithError(err).Error("Failed to patch WorkflowTaskSet") + woc.log.WithError(ctx, err).Error(ctx, "Failed to patch WorkflowTaskSet") return fmt.Errorf("failed to patch TaskSet. %v", err) } } else if err != nil { - woc.log.WithError(err).Error("Failed to create WorkflowTaskSet") + woc.log.WithError(ctx, err).Error(ctx, "Failed to create WorkflowTaskSet") return err } return nil diff --git a/workflow/controller/taskset_test.go b/workflow/controller/taskset_test.go index dd54ced0a8de..7e122acd97a5 100644 --- a/workflow/controller/taskset_test.go +++ b/workflow/controller/taskset_test.go @@ -66,7 +66,7 @@ spec: t.Run("CreateTaskSet", func(t *testing.T) { cancel, controller := newController(wf, ts, defaultServiceAccount) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) tslist, err := woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets("default").List(ctx, v1.ListOptions{}) require.NoError(t, err) @@ -91,7 +91,7 @@ spec: cancel, controller := newController(wf, ts, defaultServiceAccount) defer cancel() controller.Config.InstanceID = "testID" - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) woc.operate(ctx) tslist, err := woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets("default").List(ctx, v1.ListOptions{}) require.NoError(t, err) @@ -294,7 +294,7 @@ status: defer cancel() _, err := controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets("default").Create(ctx, &ts, v1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err = woc.removeCompletedTaskSetStatus(ctx) require.NoError(t, err) tslist, err := woc.controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets("default").List(ctx, v1.ListOptions{}) @@ -317,8 +317,8 @@ func TestNonHTTPTemplateScenario(t *testing.T) { cancel, controller := newController() defer cancel() wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) - woc := newWorkflowOperationCtx(wf, controller) ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) t.Run("reconcileTaskSet", func(t *testing.T) { woc.operate(ctx) err := woc.reconcileTaskSet(ctx) @@ -398,7 +398,7 @@ status: defer cancel() _, err := controller.wfclientset.ArgoprojV1alpha1().WorkflowTaskSets("default").Create(ctx, &ts, v1.CreateOptions{}) require.NoError(t, err) - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) time.Sleep(1 * time.Second) err = woc.reconcileTaskSet(ctx) require.NoError(t, err) diff --git a/workflow/controller/workflowpod.go b/workflow/controller/workflowpod.go index e5296c17bb11..292a91d0442c 100644 --- a/workflow/controller/workflowpod.go +++ b/workflow/controller/workflowpod.go @@ -54,7 +54,7 @@ var ( // scheduleOnDifferentHost adds affinity to prevent retry on the same host when // retryStrategy.affinity.nodeAntiAffinity{} is specified -func (woc *wfOperationCtx) scheduleOnDifferentHost(node *wfv1.NodeStatus, pod *apiv1.Pod) error { +func (woc *wfOperationCtx) scheduleOnDifferentHost(ctx context.Context, node *wfv1.NodeStatus, pod *apiv1.Pod) error { if node != nil && pod != nil { if retryNode := FindRetryNode(woc.wf.Status.Nodes, node.ID); retryNode != nil { // recover template for the retry node @@ -62,7 +62,7 @@ func (woc *wfOperationCtx) scheduleOnDifferentHost(node *wfv1.NodeStatus, pod *a if err != nil { return err } - _, retryTmpl, _, err := tmplCtx.ResolveTemplate(retryNode) + _, retryTmpl, _, err := tmplCtx.ResolveTemplate(ctx, retryNode) if err != nil { return err } @@ -92,13 +92,13 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin } if exists { - woc.log.WithField("podPhase", existing.Status.Phase).Debugf("Skipped pod %s (%s) creation: already exists", nodeName, nodeID) + woc.log.WithField(ctx, "podPhase", existing.Status.Phase).Debugf(ctx, "Skipped pod %s (%s) creation: already exists", nodeName, nodeID) return existing, nil } if !woc.GetShutdownStrategy().ShouldExecute(opts.onExitPod) { // Do not create pods if we are shutting down - woc.markNodePhase(nodeName, wfv1.NodeFailed, fmt.Sprintf("workflow shutdown with strategy: %s", woc.GetShutdownStrategy())) + woc.markNodePhase(ctx, nodeName, wfv1.NodeFailed, fmt.Sprintf("workflow shutdown with strategy: %s", woc.GetShutdownStrategy())) return nil, nil } @@ -205,7 +205,7 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin pod.ObjectMeta.Labels[common.LabelKeyControllerInstanceID] = woc.controller.Config.InstanceID } - woc.addArchiveLocation(tmpl) + woc.addArchiveLocation(ctx, tmpl) err = woc.setupServiceAccount(ctx, pod, tmpl) if err != nil { @@ -250,7 +250,7 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin if p, ok := wfv1.ParseProgress(x); ok { node, err := woc.wf.Status.Nodes.Get(nodeID) if err != nil { - woc.log.Panicf("was unable to obtain node for %s", nodeID) + woc.log.Panicf(ctx, "was unable to obtain node for %s", nodeID) } node.Progress = p woc.wf.Status.Nodes.Set(nodeID, *node) @@ -262,7 +262,7 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin return nil, err } - err = woc.addInputArtifactsVolumes(pod, tmpl) + err = woc.addInputArtifactsVolumes(ctx, pod, tmpl) if err != nil { return nil, err } @@ -454,9 +454,9 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin if !apierr.IsAlreadyExists(err) { return nil, err } - woc.log.Infof("Configmap already exists: %s", cm.Name) + woc.log.Infof(ctx, "Configmap already exists: %s", cm.Name) } else { - woc.log.Infof("Created configmap: %s", created.Name) + woc.log.Infof(ctx, "Created configmap: %s", created.Name) } volumeConfig := apiv1.Volume{ @@ -490,14 +490,14 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin // Check if the template has exceeded its timeout duration. If it hasn't set the applicable activeDeadlineSeconds node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - woc.log.Warnf("couldn't retrieve node for nodeName %s, will get nil templateDeadline", nodeName) + woc.log.Warnf(ctx, "couldn't retrieve node for nodeName %s, will get nil templateDeadline", nodeName) } templateDeadline, err := woc.checkTemplateTimeout(tmpl, node) if err != nil { return nil, err } - if err := woc.scheduleOnDifferentHost(node, pod); err != nil { + if err := woc.scheduleOnDifferentHost(ctx, node, pod); err != nil { return nil, err } @@ -506,7 +506,7 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin if newActiveDeadlineSeconds <= 1 { return nil, fmt.Errorf("%s exceeded its deadline", nodeName) } - woc.log.Debugf("Setting new activeDeadlineSeconds %d for pod %s/%s due to templateDeadline", newActiveDeadlineSeconds, pod.Namespace, pod.Name) + woc.log.Debugf(ctx, "Setting new activeDeadlineSeconds %d for pod %s/%s due to templateDeadline", newActiveDeadlineSeconds, pod.Namespace, pod.Name) pod.Spec.ActiveDeadlineSeconds = &newActiveDeadlineSeconds } @@ -514,14 +514,14 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin return nil, ErrResourceRateLimitReached } - woc.log.Debugf("Creating Pod: %s (%s)", nodeName, pod.Name) + woc.log.Debugf(ctx, "Creating Pod: %s (%s)", nodeName, pod.Name) created, err := woc.controller.kubeclientset.CoreV1().Pods(woc.wf.ObjectMeta.Namespace).Create(ctx, pod, metav1.CreateOptions{}) if err != nil { if apierr.IsAlreadyExists(err) { // workflow pod names are deterministic. We can get here if the // controller fails to persist the workflow after creating the pod. - woc.log.Infof("Failed pod %s (%s) creation: already exists", nodeName, pod.Name) + woc.log.Infof(ctx, "Failed pod %s (%s) creation: already exists", nodeName, pod.Name) // get a reference to the currently existing Pod since the created pod returned before was nil. if existing, err = woc.controller.kubeclientset.CoreV1().Pods(woc.wf.ObjectMeta.Namespace).Get(ctx, pod.Name, metav1.GetOptions{}); err == nil { return existing, nil @@ -530,10 +530,10 @@ func (woc *wfOperationCtx) createWorkflowPod(ctx context.Context, nodeName strin if errorsutil.IsTransientErr(err) { return nil, err } - woc.log.Infof("Failed to create pod %s (%s): %v", nodeName, pod.Name, err) + woc.log.Infof(ctx, "Failed to create pod %s (%s): %v", nodeName, pod.Name, err) return nil, errors.InternalWrapError(err) } - woc.log.Infof("Created pod: %s (%s)", nodeName, created.Name) + woc.log.Infof(ctx, "Created pod: %s (%s)", nodeName, created.Name) woc.activePods++ return created, nil } @@ -762,9 +762,9 @@ func (woc *wfOperationCtx) addDNSConfig(pod *apiv1.Pod) { // addSchedulingConstraints applies any node selectors or affinity rules to the pod, either set in the workflow or the template func (woc *wfOperationCtx) addSchedulingConstraints(ctx context.Context, pod *apiv1.Pod, wfSpec *wfv1.WorkflowSpec, tmpl *wfv1.Template, nodeName string) { // Get boundaryNode Template (if specified) - boundaryTemplate, err := woc.GetBoundaryTemplate(nodeName) + boundaryTemplate, err := woc.GetBoundaryTemplate(ctx, nodeName) if err != nil { - woc.log.Warnf("couldn't get boundaryTemplate through nodeName %s", nodeName) + woc.log.Warnf(ctx, "couldn't get boundaryTemplate through nodeName %s", nodeName) } // Set nodeSelector (if specified) if len(tmpl.NodeSelector) > 0 { @@ -825,13 +825,13 @@ func (woc *wfOperationCtx) addSchedulingConstraints(ctx context.Context, pod *ap } // GetBoundaryTemplate get a template through the nodeName -func (woc *wfOperationCtx) GetBoundaryTemplate(nodeName string) (*wfv1.Template, error) { +func (woc *wfOperationCtx) GetBoundaryTemplate(ctx context.Context, nodeName string) (*wfv1.Template, error) { node, err := woc.wf.GetNodeByName(nodeName) if err != nil { - woc.log.Warnf("couldn't retrieve node for nodeName %s, will get nil templateDeadline", nodeName) + woc.log.Warnf(ctx, "couldn't retrieve node for nodeName %s, will get nil templateDeadline", nodeName) return nil, err } - boundaryTmpl, _, err := woc.GetTemplateByBoundaryID(node.BoundaryID) + boundaryTmpl, _, err := woc.GetTemplateByBoundaryID(ctx, node.BoundaryID) if err != nil { return nil, err } @@ -839,7 +839,7 @@ func (woc *wfOperationCtx) GetBoundaryTemplate(nodeName string) (*wfv1.Template, } // GetTemplateByBoundaryID get a template through the node's BoundaryID. -func (woc *wfOperationCtx) GetTemplateByBoundaryID(boundaryID string) (*wfv1.Template, bool, error) { +func (woc *wfOperationCtx) GetTemplateByBoundaryID(ctx context.Context, boundaryID string) (*wfv1.Template, bool, error) { boundaryNode, err := woc.wf.Status.Nodes.Get(boundaryID) if err != nil { return nil, false, err @@ -848,7 +848,7 @@ func (woc *wfOperationCtx) GetTemplateByBoundaryID(boundaryID string) (*wfv1.Tem if err != nil { return nil, false, err } - _, boundaryTmpl, templateStored, err := tmplCtx.ResolveTemplate(boundaryNode) + _, boundaryTmpl, templateStored, err := tmplCtx.ResolveTemplate(ctx, boundaryNode) if err != nil { return nil, templateStored, err } @@ -971,7 +971,7 @@ func addVolumeReferences(pod *apiv1.Pod, vols []apiv1.Volume, tmpl *wfv1.Templat // the explicit volume mount and the artifact emptydir and prevent all uses of the emptydir for purposes of // loading data. The controller will omit mounting the emptydir to the artifact path, and the executor // will load the artifact in the in user's volume (as opposed to the emptydir) -func (woc *wfOperationCtx) addInputArtifactsVolumes(pod *apiv1.Pod, tmpl *wfv1.Template) error { +func (woc *wfOperationCtx) addInputArtifactsVolumes(ctx context.Context, pod *apiv1.Pod, tmpl *wfv1.Template) error { if len(tmpl.Inputs.Artifacts) == 0 { return nil } @@ -1017,7 +1017,7 @@ func (woc *wfOperationCtx) addInputArtifactsVolumes(pod *apiv1.Pod, tmpl *wfv1.T return errors.Errorf(errors.CodeBadRequest, "error in inputs.artifacts.%s: %s", art.Name, err.Error()) } if !art.HasLocationOrKey() && art.Optional { - woc.log.Infof("skip volume mount of %s (%s): optional artifact was not provided", + woc.log.Infof(ctx, "skip volume mount of %s (%s): optional artifact was not provided", art.Name, art.Path) continue } @@ -1026,7 +1026,7 @@ func (woc *wfOperationCtx) addInputArtifactsVolumes(pod *apiv1.Pod, tmpl *wfv1.T // artifact path overlaps with a mounted volume. do not mount the // artifacts emptydir to the main container. init would have copied // the artifact to the user's volume instead - woc.log.Debugf("skip volume mount of %s (%s): overlaps with mount %s at %s", + woc.log.Debugf(ctx, "skip volume mount of %s (%s): overlaps with mount %s at %s", art.Name, art.Path, overlap.Name, overlap.MountPath) continue } @@ -1080,7 +1080,7 @@ func addOutputArtifactsVolumes(pod *apiv1.Pod, tmpl *wfv1.Template) { // information configured in the controller, for the purposes of archiving outputs. This is skipped // for templates which do not need to archive anything, or have explicitly set an archive location // in the template. -func (woc *wfOperationCtx) addArchiveLocation(tmpl *wfv1.Template) { +func (woc *wfOperationCtx) addArchiveLocation(ctx context.Context, tmpl *wfv1.Template) { if tmpl.ArchiveLocation.HasLocation() { // User explicitly set the location. nothing else to do. return @@ -1092,7 +1092,7 @@ func (woc *wfOperationCtx) addArchiveLocation(tmpl *wfv1.Template) { needLocation = true } } - woc.log.WithField("needLocation", needLocation).Debug() + woc.log.WithField(ctx, "needLocation", needLocation).Debug(ctx, "") if !needLocation { return } diff --git a/workflow/controller/workflowpod_test.go b/workflow/controller/workflowpod_test.go index 42b5b359a54a..aee0fe014a40 100644 --- a/workflow/controller/workflowpod_test.go +++ b/workflow/controller/workflowpod_test.go @@ -32,7 +32,7 @@ func unmarshalTemplate(yamlStr string) *wfv1.Template { } // newWoc a new operation context suitable for testing -func newWoc(wfs ...wfv1.Workflow) *wfOperationCtx { +func newWoc(ctx context.Context, wfs ...wfv1.Workflow) *wfOperationCtx { var wf *wfv1.Workflow if len(wfs) == 0 { wf = wfv1.MustUnmarshalWorkflow(helloWorldWf) @@ -41,7 +41,7 @@ func newWoc(wfs ...wfv1.Workflow) *wfOperationCtx { } cancel, controller := newController(wf, defaultServiceAccount) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) return woc } @@ -86,7 +86,7 @@ script: func TestScriptTemplateWithVolume(t *testing.T) { ctx := context.Background() tmpl := unmarshalTemplate(scriptTemplateWithInputArtifact) - woc := newWoc() + woc := newWoc(ctx) _, err := woc.executeScript(ctx, tmpl.Name, "", tmpl, &wfv1.WorkflowStep{}, &executeTemplateOpts{}) require.NoError(t, err) } @@ -157,10 +157,10 @@ func TestScriptTemplateWithoutVolumeOptionalArtifact(t *testing.T) { // Ensure that volume mount is added when artifact is provided tmpl := unmarshalTemplate(scriptTemplateWithOptionalInputArtifactProvided) - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) mainCtr := tmpl.Script.Container mainCtr.Args = append(mainCtr.Args, common.ExecutorScriptSourcePath) - ctx := context.Background() pod, err := woc.createWorkflowPod(ctx, tmpl.Name, []apiv1.Container{mainCtr}, tmpl, &createWorkflowPodOpts{}) require.NoError(t, err) // Note: pod.Spec.Containers[0] is wait @@ -173,7 +173,7 @@ func TestScriptTemplateWithoutVolumeOptionalArtifact(t *testing.T) { tmpl = unmarshalTemplate(scriptTemplateWithOptionalInputArtifactProvidedAndOverlappedPath) wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) wf.Spec.Volumes = append(wf.Spec.Volumes, apiv1.Volume{Name: "my-mount"}) - woc = newWoc(*wf) + woc = newWoc(ctx, *wf) mainCtr = tmpl.Script.Container mainCtr.Args = append(mainCtr.Args, common.ExecutorScriptSourcePath) pod, err = woc.createWorkflowPod(ctx, tmpl.Name, []apiv1.Container{mainCtr}, tmpl, &createWorkflowPodOpts{includeScriptOutput: true}) @@ -186,12 +186,12 @@ func TestScriptTemplateWithoutVolumeOptionalArtifact(t *testing.T) { // TestWFLevelServiceAccount verifies the ability to carry forward the service account name // for the pod from workflow.spec.serviceAccountName. func TestWFLevelServiceAccount(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) woc.execWf.Spec.ServiceAccountName = "foo" tmplCtx, err := woc.createTemplateContext(wfv1.ResourceScopeLocal, "") require.NoError(t, err) - ctx := context.Background() _, err = woc.executeContainer(ctx, woc.execWf.Spec.Entrypoint, tmplCtx.GetTemplateScope(), &woc.execWf.Spec.Templates[0], &wfv1.WorkflowStep{}, &executeTemplateOpts{}) require.NoError(t, err) pods, err := listPods(woc) @@ -204,13 +204,13 @@ func TestWFLevelServiceAccount(t *testing.T) { // TestTmplServiceAccount verifies the ability to carry forward the Template level service account name // for the pod from workflow.spec.serviceAccountName. func TestTmplServiceAccount(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) woc.execWf.Spec.ServiceAccountName = "foo" woc.execWf.Spec.Templates[0].ServiceAccountName = "tmpl" tmplCtx, err := woc.createTemplateContext(wfv1.ResourceScopeLocal, "") require.NoError(t, err) - ctx := context.Background() _, err = woc.executeContainer(ctx, woc.execWf.Spec.Entrypoint, tmplCtx.GetTemplateScope(), &woc.execWf.Spec.Templates[0], &wfv1.WorkflowStep{}, &executeTemplateOpts{}) require.NoError(t, err) @@ -223,8 +223,8 @@ func TestTmplServiceAccount(t *testing.T) { // TestWFLevelAutomountServiceAccountToken verifies the ability to carry forward workflow level AutomountServiceAccountToken to Podspec. func TestWFLevelAutomountServiceAccountToken(t *testing.T) { - woc := newWoc() ctx := context.Background() + woc := newWoc(ctx) _, err := util.CreateServiceAccountWithToken(ctx, woc.controller.kubeclientset, "", "foo") require.NoError(t, err) @@ -245,8 +245,8 @@ func TestWFLevelAutomountServiceAccountToken(t *testing.T) { // TestTmplLevelAutomountServiceAccountToken verifies the ability to carry forward template level AutomountServiceAccountToken to Podspec. func TestTmplLevelAutomountServiceAccountToken(t *testing.T) { - woc := newWoc() ctx := context.Background() + woc := newWoc(ctx) _, err := util.CreateServiceAccountWithToken(ctx, woc.controller.kubeclientset, "", "foo") require.NoError(t, err) @@ -279,8 +279,8 @@ func verifyServiceAccountTokenVolumeMount(t *testing.T, ctr apiv1.Container, vol // TestWFLevelExecutorServiceAccountName verifies the ability to carry forward workflow level AutomountServiceAccountToken to Podspec. func TestWFLevelExecutorServiceAccountName(t *testing.T) { - woc := newWoc() ctx := context.Background() + woc := newWoc(ctx) _, err := util.CreateServiceAccountWithToken(ctx, woc.controller.kubeclientset, "", "foo") require.NoError(t, err) @@ -302,8 +302,8 @@ func TestWFLevelExecutorServiceAccountName(t *testing.T) { // TestTmplLevelExecutorServiceAccountName verifies the ability to carry forward template level AutomountServiceAccountToken to Podspec. func TestTmplLevelExecutorServiceAccountName(t *testing.T) { - woc := newWoc() ctx := context.Background() + woc := newWoc(ctx) _, err := util.CreateServiceAccountWithToken(ctx, woc.controller.kubeclientset, "", "foo") require.NoError(t, err) _, err = util.CreateServiceAccountWithToken(ctx, woc.controller.kubeclientset, "", "tmpl") @@ -330,7 +330,7 @@ func TestTmplLevelExecutorServiceAccountName(t *testing.T) { func TestCtrlLevelExecutorSecurityContext(t *testing.T) { var user int64 = 1000 ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) _, err := util.CreateServiceAccountWithToken(ctx, woc.controller.kubeclientset, "", "foo") require.NoError(t, err) _, err = util.CreateServiceAccountWithToken(ctx, woc.controller.kubeclientset, "", "tmpl") @@ -359,7 +359,8 @@ func TestCtrlLevelExecutorSecurityContext(t *testing.T) { // TestImagePullSecrets verifies the ability to carry forward imagePullSecrets from workflow.spec func TestImagePullSecrets(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) woc.execWf.Spec.ImagePullSecrets = []apiv1.LocalObjectReference{ { Name: "secret-name", @@ -368,7 +369,6 @@ func TestImagePullSecrets(t *testing.T) { tmplCtx, err := woc.createTemplateContext(wfv1.ResourceScopeLocal, "") require.NoError(t, err) - ctx := context.Background() _, err = woc.executeContainer(ctx, woc.execWf.Spec.Entrypoint, tmplCtx.GetTemplateScope(), &woc.execWf.Spec.Templates[0], &wfv1.WorkflowStep{}, &executeTemplateOpts{}) require.NoError(t, err) pods, err := woc.controller.kubeclientset.CoreV1().Pods("").List(ctx, metav1.ListOptions{}) @@ -380,7 +380,8 @@ func TestImagePullSecrets(t *testing.T) { // TestAffinity verifies the ability to carry forward affinity rules func TestAffinity(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) woc.execWf.Spec.Affinity = &apiv1.Affinity{ NodeAffinity: &apiv1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &apiv1.NodeSelector{ @@ -404,7 +405,6 @@ func TestAffinity(t *testing.T) { tmplCtx, err := woc.createTemplateContext(wfv1.ResourceScopeLocal, "") require.NoError(t, err) - ctx := context.Background() _, err = woc.executeContainer(ctx, woc.execWf.Spec.Entrypoint, tmplCtx.GetTemplateScope(), &woc.execWf.Spec.Templates[0], &wfv1.WorkflowStep{}, &executeTemplateOpts{}) require.NoError(t, err) pods, err := listPods(woc) @@ -416,7 +416,8 @@ func TestAffinity(t *testing.T) { // TestTolerations verifies the ability to carry forward tolerations. func TestTolerations(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) woc.execWf.Spec.Templates[0].Tolerations = []apiv1.Toleration{{ Key: "nvidia.com/gpu", Operator: "Exists", @@ -425,7 +426,6 @@ func TestTolerations(t *testing.T) { tmplCtx, err := woc.createTemplateContext(wfv1.ResourceScopeLocal, "") require.NoError(t, err) - ctx := context.Background() _, err = woc.executeContainer(ctx, woc.execWf.Spec.Entrypoint, tmplCtx.GetTemplateScope(), &woc.execWf.Spec.Templates[0], &wfv1.WorkflowStep{}, &executeTemplateOpts{}) require.NoError(t, err) pods, err := listPods(woc) @@ -438,11 +438,11 @@ func TestTolerations(t *testing.T) { // TestMetadata verifies ability to carry forward annotations and labels func TestMetadata(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) tmplCtx, err := woc.createTemplateContext(wfv1.ResourceScopeLocal, "") require.NoError(t, err) - ctx := context.Background() _, err = woc.executeContainer(ctx, woc.execWf.Spec.Entrypoint, tmplCtx.GetTemplateScope(), &woc.execWf.Spec.Templates[0], &wfv1.WorkflowStep{}, &executeTemplateOpts{}) require.NoError(t, err) pods, err := listPods(woc) @@ -463,7 +463,7 @@ func TestMetadata(t *testing.T) { // TestWorkflowControllerArchiveConfig verifies archive location substitution of workflow func TestWorkflowControllerArchiveConfig(t *testing.T) { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) setArtifactRepository(woc.controller, &wfv1.ArtifactRepository{S3: &wfv1.S3ArtifactRepository{ S3Bucket: wfv1.S3Bucket{ Bucket: "foo", @@ -483,7 +483,7 @@ func setArtifactRepository(controller *WorkflowController, repo *wfv1.ArtifactRe // TestConditionalNoAddArchiveLocation verifies we do not add archive location if it is not needed func TestConditionalNoAddArchiveLocation(t *testing.T) { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) setArtifactRepository(woc.controller, &wfv1.ArtifactRepository{S3: &wfv1.S3ArtifactRepository{ S3Bucket: wfv1.S3Bucket{ Bucket: "foo", @@ -503,7 +503,7 @@ func TestConditionalNoAddArchiveLocation(t *testing.T) { // TestConditionalAddArchiveLocationArchiveLogs verifies we do add archive location if it is needed for logs func TestConditionalAddArchiveLocationArchiveLogs(t *testing.T) { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) setArtifactRepository(woc.controller, &wfv1.ArtifactRepository{ S3: &wfv1.S3ArtifactRepository{ S3Bucket: wfv1.S3Bucket{ @@ -536,7 +536,7 @@ func TestConditionalArchiveLocation(t *testing.T) { }, }, } - woc := newWoc() + woc := newWoc(ctx) setArtifactRepository(woc.controller, &wfv1.ArtifactRepository{S3: &wfv1.S3ArtifactRepository{ S3Bucket: wfv1.S3Bucket{ Bucket: "foo", @@ -593,7 +593,8 @@ func TestConditionalAddArchiveLocationTemplateArchiveLogs(t *testing.T) { } cancel, controller := newController(wf) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) setArtifactRepository(woc.controller, &wfv1.ArtifactRepository{ ArchiveLogs: ptr.To(tt.controllerArchiveLog), S3: &wfv1.S3ArtifactRepository{ @@ -628,8 +629,9 @@ func Test_createWorkflowPod_rateLimited(t *testing.T) { c.Config.ResourceRateLimit = &limit }) defer cancel() - woc := newWorkflowOperationCtx(wf, controller) - woc.operate(context.Background()) + ctx := context.Background() + woc := newWorkflowOperationCtx(ctx, wf, controller) + woc.operate(ctx) x := woc.wf.Status.Nodes[woc.wf.Name] assert.Equal(t, wfv1.NodePending, x.Phase) if limited { @@ -642,7 +644,8 @@ func Test_createWorkflowPod_rateLimited(t *testing.T) { } func Test_createWorkflowPod_containerName(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) pod, err := woc.createWorkflowPod(context.Background(), "", []apiv1.Container{{Name: "invalid", Command: []string{""}}}, &wfv1.Template{}, &createWorkflowPodOpts{}) require.NoError(t, err) assert.Equal(t, common.MainContainerName, pod.Spec.Containers[1].Name) @@ -651,28 +654,33 @@ func Test_createWorkflowPod_containerName(t *testing.T) { var emissaryCmd = []string{"/var/run/argo/argoexec", "emissary"} func Test_createWorkflowPod_emissary(t *testing.T) { + t.Run("NoCommand", func(t *testing.T) { - woc := newWoc() - _, err := woc.createWorkflowPod(context.Background(), "", []apiv1.Container{{Image: "docker/whalesay:nope"}}, &wfv1.Template{Name: "my-tmpl"}, &createWorkflowPodOpts{}) + ctx := context.Background() + woc := newWoc(ctx) + _, err := woc.createWorkflowPod(ctx, "", []apiv1.Container{{Image: "docker/whalesay:nope"}}, &wfv1.Template{Name: "my-tmpl"}, &createWorkflowPodOpts{}) require.EqualError(t, err, "failed to look-up entrypoint/cmd for image \"docker/whalesay:nope\", you must either explicitly specify the command, or list the image's command in the index: https://argo-workflows.readthedocs.io/en/latest/workflow-executors/#emissary-emissary: GET https://index.docker.io/v2/docker/whalesay/manifests/nope: MANIFEST_UNKNOWN: manifest unknown; unknown tag=nope") }) t.Run("CommandNoArgs", func(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) pod, err := woc.createWorkflowPod(context.Background(), "", []apiv1.Container{{Command: []string{"foo"}}}, &wfv1.Template{}, &createWorkflowPodOpts{}) require.NoError(t, err) cmd := append(append(emissaryCmd, woc.getExecutorLogOpts()...), "--", "foo") assert.Equal(t, cmd, pod.Spec.Containers[1].Command) }) t.Run("NoCommandWithImageIndex", func(t *testing.T) { - woc := newWoc() - pod, err := woc.createWorkflowPod(context.Background(), "", []apiv1.Container{{Image: "my-image"}}, &wfv1.Template{}, &createWorkflowPodOpts{}) + ctx := context.Background() + woc := newWoc(ctx) + pod, err := woc.createWorkflowPod(ctx, "", []apiv1.Container{{Image: "my-image"}}, &wfv1.Template{}, &createWorkflowPodOpts{}) require.NoError(t, err) cmd := append(append(emissaryCmd, woc.getExecutorLogOpts()...), "--", "my-entrypoint") assert.Equal(t, cmd, pod.Spec.Containers[1].Command) assert.Equal(t, []string{"my-cmd"}, pod.Spec.Containers[1].Args) }) t.Run("NoCommandWithArgsWithImageIndex", func(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) pod, err := woc.createWorkflowPod(context.Background(), "", []apiv1.Container{{Image: "my-image", Args: []string{"foo"}}}, &wfv1.Template{}, &createWorkflowPodOpts{}) require.NoError(t, err) cmd := append(append(emissaryCmd, woc.getExecutorLogOpts()...), "--", "my-entrypoint") @@ -680,7 +688,8 @@ func Test_createWorkflowPod_emissary(t *testing.T) { assert.Equal(t, []string{"foo"}, pod.Spec.Containers[1].Args) }) t.Run("CommandFromPodSpecPatch", func(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) podSpec := &apiv1.PodSpec{} podSpec.Containers = []apiv1.Container{{ Name: "main", @@ -715,7 +724,7 @@ func TestVolumeAndVolumeMounts(t *testing.T) { // For emissary executor t.Run("Emissary", func(t *testing.T) { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.volumes = volumes woc.execWf.Spec.Templates[0].Container.VolumeMounts = volumeMounts @@ -778,7 +787,7 @@ func TestVolumesPodSubstitution(t *testing.T) { } ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.volumes = volumes woc.execWf.Spec.Templates[0].Container.VolumeMounts = volumeMounts woc.execWf.Spec.Templates[0].Inputs.Parameters = inputParameters @@ -815,7 +824,7 @@ func TestOutOfCluster(t *testing.T) { // default mount path & volume name { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.controller.Config.KubeConfig = &config.KubeConfig{ SecretName: "foo", SecretKey: "bar", @@ -839,7 +848,7 @@ func TestOutOfCluster(t *testing.T) { // custom mount path & volume name, in case name collision { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.controller.Config.KubeConfig = &config.KubeConfig{ SecretName: "foo", SecretKey: "bar", @@ -868,7 +877,7 @@ func TestOutOfCluster(t *testing.T) { func TestPriority(t *testing.T) { priority := int32(15) ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.execWf.Spec.Templates[0].PriorityClassName = "foo" woc.execWf.Spec.Templates[0].Priority = &priority tmplCtx, err := woc.createTemplateContext(wfv1.ResourceScopeLocal, "") @@ -886,7 +895,7 @@ func TestPriority(t *testing.T) { // TestSchedulerName verifies the ability to carry forward schedulerName. func TestSchedulerName(t *testing.T) { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.execWf.Spec.Templates[0].SchedulerName = "foo" tmplCtx, err := woc.createTemplateContext(wfv1.ResourceScopeLocal, "") require.NoError(t, err) @@ -930,7 +939,7 @@ func TestInitContainers(t *testing.T) { mirrorVolumeMounts := true ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.volumes = volumes woc.execWf.Spec.Templates[0].Container.VolumeMounts = volumeMounts woc.execWf.Spec.Templates[0].InitContainers = []wfv1.UserContainer{ @@ -994,7 +1003,7 @@ func TestSidecars(t *testing.T) { mirrorVolumeMounts := true ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.volumes = volumes woc.execWf.Spec.Templates[0].Container.VolumeMounts = volumeMounts woc.execWf.Spec.Templates[0].Sidecars = []wfv1.UserContainer{ @@ -1057,7 +1066,7 @@ func TestTemplateLocalVolumes(t *testing.T) { } ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.volumes = volumes woc.execWf.Spec.Templates[0].Container.VolumeMounts = volumeMounts woc.execWf.Spec.Templates[0].Volumes = localVolumes @@ -1081,7 +1090,7 @@ func TestTemplateLocalVolumes(t *testing.T) { // TestWFLevelHostAliases verifies the ability to carry forward workflow level HostAliases to Podspec func TestWFLevelHostAliases(t *testing.T) { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.execWf.Spec.HostAliases = []apiv1.HostAlias{ {IP: "127.0.0.1"}, {IP: "127.0.0.1"}, @@ -1100,7 +1109,7 @@ func TestWFLevelHostAliases(t *testing.T) { // TestTmplLevelHostAliases verifies the ability to carry forward template level HostAliases to Podspec func TestTmplLevelHostAliases(t *testing.T) { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) woc.execWf.Spec.Templates[0].HostAliases = []apiv1.HostAlias{ {IP: "127.0.0.1"}, {IP: "127.0.0.1"}, @@ -1119,7 +1128,7 @@ func TestTmplLevelHostAliases(t *testing.T) { // TestWFLevelSecurityContext verifies the ability to carry forward workflow level SecurityContext to Podspec func TestWFLevelSecurityContext(t *testing.T) { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) runAsUser := int64(1234) woc.execWf.Spec.SecurityContext = &apiv1.PodSecurityContext{ RunAsUser: &runAsUser, @@ -1139,7 +1148,7 @@ func TestWFLevelSecurityContext(t *testing.T) { // TestTmplLevelSecurityContext verifies the ability to carry forward template level SecurityContext to Podspec func TestTmplLevelSecurityContext(t *testing.T) { ctx := context.Background() - woc := newWoc() + woc := newWoc(ctx) runAsUser := int64(1234) woc.execWf.Spec.Templates[0].SecurityContext = &apiv1.PodSecurityContext{ RunAsUser: &runAsUser, @@ -1179,7 +1188,7 @@ func Test_createSecretVolumesFromArtifactLocations_SSECUsed(t *testing.T) { }, }, } - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) setArtifactRepository(woc.controller, &wfv1.ArtifactRepository{ S3: &wfv1.S3ArtifactRepository{ @@ -1270,7 +1279,7 @@ func TestCreateSecretVolumesFromArtifactLocationsSessionToken(t *testing.T) { }, }, } - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) setArtifactRepository(woc.controller, &wfv1.ArtifactRepository{ S3: &wfv1.S3ArtifactRepository{ @@ -1436,26 +1445,26 @@ spec: func TestPodSpecPatch(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWfWithPatch) ctx := context.Background() - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) mainCtr := woc.execWf.Spec.Templates[0].Container pod, _ := woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{}) assert.Equal(t, "0.800", pod.Spec.Containers[1].Resources.Limits.Cpu().AsDec().String()) wf = wfv1.MustUnmarshalWorkflow(helloWorldWfWithWFPatch) - woc = newWoc(*wf) + woc = newWoc(ctx, *wf) mainCtr = woc.execWf.Spec.Templates[0].Container pod, _ = woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{}) assert.Equal(t, "0.800", pod.Spec.Containers[1].Resources.Limits.Cpu().AsDec().String()) wf = wfv1.MustUnmarshalWorkflow(helloWorldWfWithWFYAMLPatch) - woc = newWoc(*wf) + woc = newWoc(ctx, *wf) mainCtr = woc.execWf.Spec.Templates[0].Container pod, _ = woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{}) assert.Equal(t, "0.800", pod.Spec.Containers[1].Resources.Limits.Cpu().AsDec().String()) assert.Equal(t, "104857600", pod.Spec.Containers[1].Resources.Limits.Memory().AsDec().String()) wf = wfv1.MustUnmarshalWorkflow(helloWorldWfWithTmplAndWFPatch) - woc = newWoc(*wf) + woc = newWoc(ctx, *wf) mainCtr = woc.execWf.Spec.Templates[0].Container pod, _ = woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{}) assert.Equal(t, ptr.To(true), pod.Spec.Containers[1].SecurityContext.RunAsNonRoot) @@ -1463,7 +1472,7 @@ func TestPodSpecPatch(t *testing.T) { assert.Equal(t, []apiv1.Capability(nil), pod.Spec.Containers[1].SecurityContext.Capabilities.Drop) wf = wfv1.MustUnmarshalWorkflow(helloWorldWfWithInvalidPatchFormat) - woc = newWoc(*wf) + woc = newWoc(ctx, *wf) mainCtr = woc.execWf.Spec.Templates[0].Container _, err := woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{}) require.EqualError(t, err, "Error applying PodSpecPatch") @@ -1509,7 +1518,7 @@ func TestPodSpecPatchPodName(t *testing.T) { t.Setenv("POD_NAMES", tt.podNameVersion) ctx := context.Background() wf := wfv1.MustUnmarshalWorkflow(tt.workflowYaml) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.operate(ctx) assert.Equal(t, wfv1.WorkflowRunning, woc.wf.Status.Phase) pods, err := listPods(woc) @@ -1539,7 +1548,7 @@ func TestMainContainerCustomization(t *testing.T) { // configuration in controller so here we respect what's specified in podSpecPatch. t.Run("PodSpecPatchPrecedence", func(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWfWithPatch) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.controller.Config.MainContainer = mainCtrSpec mainCtr := woc.execWf.Spec.Templates[0].Container pod, _ := woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{}) @@ -1549,7 +1558,7 @@ func TestMainContainerCustomization(t *testing.T) { // container's resources are not specified. t.Run("Default", func(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.controller.Config.MainContainer = mainCtrSpec mainCtr := woc.execWf.Spec.Templates[0].Container mainCtr.Resources = apiv1.ResourceRequirements{Limits: apiv1.ResourceList{}} @@ -1565,7 +1574,7 @@ func TestMainContainerCustomization(t *testing.T) { // so here the main container resources remain unchanged. t.Run("ContainerPrecedence", func(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.controller.Config.MainContainer = mainCtrSpec mainCtr := wf.Spec.Templates[0].Container mainCtr.Name = common.MainContainerName @@ -1582,7 +1591,7 @@ func TestMainContainerCustomization(t *testing.T) { // If script template has limits then they take precedence over config in controller t.Run("ScriptPrecedence", func(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(scriptWf) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) woc.controller.Config.MainContainer = mainCtrSpec mainCtr := &woc.execWf.Spec.Templates[0].Script.Container mainCtr.Resources = apiv1.ResourceRequirements{ @@ -1598,7 +1607,8 @@ func TestMainContainerCustomization(t *testing.T) { } func TestExecutorContainerCustomization(t *testing.T) { - woc := newWoc() + ctx := context.Background() + woc := newWoc(ctx) woc.controller.Config.Executor = &apiv1.Container{ Args: []string{"foo"}, Resources: apiv1.ResourceRequirements{ @@ -1609,7 +1619,7 @@ func TestExecutorContainerCustomization(t *testing.T) { }, } - pod, err := woc.createWorkflowPod(context.Background(), "", nil, &wfv1.Template{}, &createWorkflowPodOpts{}) + pod, err := woc.createWorkflowPod(ctx, "", nil, &wfv1.Template{}, &createWorkflowPodOpts{}) require.NoError(t, err) waitCtr := pod.Spec.Containers[0] assert.Equal(t, []string{"foo"}, waitCtr.Args) @@ -1636,6 +1646,7 @@ spec: func TestWindowsUNCPathsAreRemoved(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWindowsWf) + ctx := context.Background() uncVolume := apiv1.Volume{ Name: "unc", VolumeSource: apiv1.VolumeSource{ @@ -1660,9 +1671,8 @@ func TestWindowsUNCPathsAreRemoved(t *testing.T) { wf.Spec.Volumes = append(wf.Spec.Volumes, uncVolume) wf.Spec.Templates[0].Container.VolumeMounts = append(wf.Spec.Templates[0].Container.VolumeMounts, uncMount) wf.Spec.Templates[0].Inputs.Artifacts = append(wf.Spec.Templates[0].Inputs.Artifacts, inp) - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) - ctx := context.Background() mainCtr := woc.execWf.Spec.Templates[0].Container pod, _ := woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{}) waitCtrIdx, err := wfutil.FindWaitCtrIndex(pod) @@ -1698,11 +1708,11 @@ container: ` func TestPropagateMaxDuration(t *testing.T) { + ctx := context.Background() // Ensure that volume mount is added when artifact is provided tmpl := unmarshalTemplate(propagateMaxDuration) - woc := newWoc() + woc := newWoc(ctx) deadline := time.Time{}.Add(time.Second) - ctx := context.Background() pod, err := woc.createWorkflowPod(ctx, tmpl.Name, []apiv1.Container{*tmpl.Container}, tmpl, &createWorkflowPodOpts{executionDeadline: deadline}) require.NoError(t, err) v, err := getPodDeadline(pod) @@ -1760,14 +1770,14 @@ spec: func TestPodMetadata(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(wfWithPodMetadata) ctx := context.Background() - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) mainCtr := woc.execWf.Spec.Templates[0].Container pod, _ := woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{}) assert.Equal(t, "foo", pod.ObjectMeta.Annotations["workflow-level-pod-annotation"]) assert.Equal(t, "bar", pod.ObjectMeta.Labels["workflow-level-pod-label"]) wf = wfv1.MustUnmarshalWorkflow(wfWithPodMetadataAndTemplateMetadata) - woc = newWoc(*wf) + woc = newWoc(ctx, *wf) mainCtr = woc.execWf.Spec.Templates[0].Container pod, _ = woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{}) assert.Equal(t, "fizz", pod.ObjectMeta.Annotations["workflow-level-pod-annotation"]) @@ -1802,13 +1812,13 @@ func TestPodDefaultContainer(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(wfWithContainerSet) // change first container name to main wf.Spec.Templates[0].ContainerSet.Containers[0].Name = common.MainContainerName - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) template := woc.execWf.Spec.Templates[0] pod, _ := woc.createWorkflowPod(ctx, wf.Name, template.ContainerSet.GetContainers(), &wf.Spec.Templates[0], &createWorkflowPodOpts{}) assert.Equal(t, common.MainContainerName, pod.ObjectMeta.Annotations[common.AnnotationKeyDefaultContainer]) wf = wfv1.MustUnmarshalWorkflow(wfWithContainerSet) - woc = newWoc(*wf) + woc = newWoc(ctx, *wf) template = woc.execWf.Spec.Templates[0] pod, _ = woc.createWorkflowPod(ctx, wf.Name, template.ContainerSet.GetContainers(), &template, &createWorkflowPodOpts{}) assert.Equal(t, "b", pod.ObjectMeta.Annotations[common.AnnotationKeyDefaultContainer]) @@ -1817,7 +1827,7 @@ func TestPodDefaultContainer(t *testing.T) { func TestGetDeadline(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) ctx := context.Background() - woc := newWoc(*wf) + woc := newWoc(ctx, *wf) mainCtr := woc.execWf.Spec.Templates[0].Container pod, _ := woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{}) deadline, _ := getPodDeadline(pod) @@ -1826,7 +1836,7 @@ func TestGetDeadline(t *testing.T) { executionDeadline := time.Now().Add(5 * time.Minute) wf = wfv1.MustUnmarshalWorkflow(helloWorldWf) ctx = context.Background() - woc = newWoc(*wf) + woc = newWoc(ctx, *wf) mainCtr = woc.execWf.Spec.Templates[0].Container pod, _ = woc.createWorkflowPod(ctx, wf.Name, []apiv1.Container{*mainCtr}, &wf.Spec.Templates[0], &createWorkflowPodOpts{executionDeadline: executionDeadline}) deadline, _ = getPodDeadline(pod) @@ -1854,7 +1864,7 @@ func TestPodMetadataWithWorkflowDefaults(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(ctx) require.NoError(t, err) mainCtr := woc.execWf.Spec.Templates[0].Container @@ -1877,7 +1887,7 @@ func TestPodMetadataWithWorkflowDefaults(t *testing.T) { } wf = wfv1.MustUnmarshalWorkflow(wfWithPodMetadata) ctx = context.Background() - woc = newWorkflowOperationCtx(wf, controller) + woc = newWorkflowOperationCtx(ctx, wf, controller) err = woc.setExecWorkflow(ctx) require.NoError(t, err) mainCtr = woc.execWf.Spec.Templates[0].Container @@ -1895,7 +1905,7 @@ func TestPodExists(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(ctx) require.NoError(t, err) mainCtr := woc.execWf.Spec.Templates[0].Container @@ -1923,7 +1933,7 @@ func TestPodFinalizerExits(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(ctx) require.NoError(t, err) mainCtr := woc.execWf.Spec.Templates[0].Container @@ -1941,7 +1951,7 @@ func TestPodFinalizerDoesNotExist(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(ctx) require.NoError(t, err) mainCtr := woc.execWf.Spec.Templates[0].Container @@ -1958,7 +1968,7 @@ func TestProgressEnvVars(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWf) ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(ctx) require.NoError(t, err) mainCtr := woc.execWf.Spec.Templates[0].Container @@ -2082,7 +2092,7 @@ func TestMergeEnvVars(t *testing.T) { wf := wfv1.MustUnmarshalWorkflow(helloWorldWfWithEnvReferSecret) ctx := context.Background() - woc := newWorkflowOperationCtx(wf, controller) + woc := newWorkflowOperationCtx(ctx, wf, controller) err := woc.setExecWorkflow(ctx) require.NoError(t, err) mainCtrSpec := &apiv1.Container{ diff --git a/workflow/cron/controller.go b/workflow/cron/controller.go index 04b50e110040..3d1c0c3da167 100644 --- a/workflow/cron/controller.go +++ b/workflow/cron/controller.go @@ -7,7 +7,6 @@ import ( "time" "github.com/argoproj/pkg/sync" - log "github.com/sirupsen/logrus" apiv1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -24,6 +23,9 @@ import ( "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" + "github.com/argoproj/argo-workflows/v3/util/logging" + log "github.com/argoproj/argo-workflows/v3/util/logging" + "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow" "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" @@ -54,6 +56,7 @@ type Controller struct { metrics *metrics.Metrics eventRecorderManager events.EventRecorderManager cronWorkflowWorkers int + logger logging.Logger } const ( @@ -65,14 +68,17 @@ var ( ) func init() { + slog := log.NewSlogLogger() + ctx := context.Background() // this make sure we support timezones _, err := time.Parse(time.RFC822, "17 Oct 07 14:03 PST") if err != nil { - log.Fatal(err) + slog.Fatal(ctx, err.Error()) } - log.WithField("cronSyncPeriod", cronSyncPeriod).Info("cron config") + slog.WithField(ctx, "cronSyncPeriod", cronSyncPeriod).Info(ctx, "cron config") } +// NewCronController creates a new cron controller func NewCronController(ctx context.Context, wfclientset versioned.Interface, dynamicInterface dynamic.Interface, namespace string, managedNamespace string, instanceId string, metrics *metrics.Metrics, eventRecorderManager events.EventRecorderManager, cronWorkflowWorkers int, wftmplInformer wfextvv1alpha1.WorkflowTemplateInformer, cwftmplInformer wfextvv1alpha1.ClusterWorkflowTemplateInformer, wfDefaults *v1alpha1.Workflow) *Controller { return &Controller{ @@ -90,15 +96,17 @@ func NewCronController(ctx context.Context, wfclientset versioned.Interface, dyn wftmplInformer: wftmplInformer, cwftmplInformer: cwftmplInformer, cronWorkflowWorkers: cronWorkflowWorkers, + logger: log.NewSlogLogger(), } } +// Run start the cron controller func (cc *Controller) Run(ctx context.Context) { defer runtimeutil.HandleCrashWithContext(ctx, runtimeutil.PanicHandlers...) defer cc.cronWfQueue.ShutDown() - log.Infof("Starting CronWorkflow controller") + cc.logger.Infof(ctx, "Starting CronWorkflow controller") if cc.instanceId != "" { - log.Infof("...with InstanceID: %s", cc.instanceId) + cc.logger.Infof(ctx, "...with InstanceID: %s", cc.instanceId) } cc.cronWfInformer = dynamicinformer.NewFilteredDynamicSharedInformerFactory(cc.dynamicInterface, cronWorkflowResyncPeriod, cc.managedNamespace, func(options *v1.ListOptions) { @@ -106,7 +114,7 @@ func (cc *Controller) Run(ctx context.Context) { }).ForResource(schema.GroupVersionResource{Group: workflow.Group, Version: workflow.Version, Resource: workflow.CronWorkflowPlural}) err := cc.addCronWorkflowInformerHandler() if err != nil { - log.Fatal(err) + cc.logger.Fatal(ctx, err.Error()) } wfInformer := util.NewWorkflowInformer(cc.dynamicInterface, cc.managedNamespace, cronWorkflowResyncPeriod, @@ -149,30 +157,30 @@ func (cc *Controller) processNextCronItem(ctx context.Context) bool { cc.keyLock.Lock(key) defer cc.keyLock.Unlock(key) - logCtx := log.WithField("cronWorkflow", key) - logCtx.Infof("Processing %s", key) + logger := cc.logger.WithField(ctx, "cronWorkflow", key) + logger.Infof(ctx, "Processing %s", key) obj, exists, err := cc.cronWfInformer.Informer().GetIndexer().GetByKey(key) if err != nil { - logCtx.WithError(err).Error(fmt.Sprintf("Failed to get CronWorkflow '%s' from informer index", key)) + logger.WithError(ctx, err).Error(ctx, fmt.Sprintf("Failed to get CronWorkflow '%s' from informer index", key)) return true } if !exists { - logCtx.Infof("Deleting '%s'", key) + logger.Infof(ctx, "Deleting '%s'", key) cc.cron.Delete(key) return true } un, ok := obj.(*unstructured.Unstructured) if !ok { - logCtx.Errorf("malformed cluster workflow template: expected *unstructured.Unstructured, got %s", reflect.TypeOf(obj).Name()) + logger.Errorf(ctx, "malformed cluster workflow template: expected *unstructured.Unstructured, got %s", reflect.TypeOf(obj).Name()) return true } cronWf := &v1alpha1.CronWorkflow{} err = util.FromUnstructuredObj(un, cronWf) if err != nil { cc.eventRecorderManager.Get(un.GetNamespace()).Event(un, apiv1.EventTypeWarning, "Malformed", err.Error()) - logCtx.WithError(err).Error("malformed cron workflow: could not convert from unstructured") + logger.WithError(ctx, err).Error(ctx, "malformed cron workflow: could not convert from unstructured") return true } ctx = wfctx.InjectObjectMeta(ctx, &cronWf.ObjectMeta) @@ -181,13 +189,13 @@ func (cc *Controller) processNextCronItem(ctx context.Context) bool { err = cronWorkflowOperationCtx.validateCronWorkflow(ctx) if err != nil { - logCtx.WithError(err).Error("invalid cron workflow") + logger.WithError(ctx, err).Error(ctx, "invalid cron workflow") return true } wfWasRun, err := cronWorkflowOperationCtx.runOutstandingWorkflows(ctx) if err != nil { - logCtx.WithError(err).Error("could not run outstanding Workflow") + logger.WithError(ctx, err).Error(ctx, "could not run outstanding Workflow") return true } else if wfWasRun { // A workflow was run, so the cron workflow will be requeued. Return here to avoid duplicating work @@ -200,24 +208,26 @@ func (cc *Controller) processNextCronItem(ctx context.Context) bool { for _, schedule := range cronWf.Spec.GetSchedulesWithTimezone(ctx) { lastScheduledTimeFunc, err := cc.cron.AddJob(key, schedule, cronWorkflowOperationCtx) if err != nil { - logCtx.WithError(err).Error("could not schedule CronWorkflow") + logger.WithError(ctx, err).Error(ctx, "could not schedule CronWorkflow") return true } cronWorkflowOperationCtx.scheduledTimeFunc = lastScheduledTimeFunc } - logCtx.Infof("CronWorkflow %s added", key) + logger.Infof(ctx, "CronWorkflow %s added", key) return true } func (cc *Controller) addCronWorkflowInformerHandler() error { + ctx := context.Background() + log := log.NewSlogLogger() _, err := cc.cronWfInformer.Informer().AddEventHandler( cache.FilteringResourceEventHandler{ FilterFunc: func(obj interface{}) bool { un, ok := obj.(*unstructured.Unstructured) if !ok { - log.Warnf("Cron Workflow FilterFunc: '%v' is not an unstructured", obj) + log.Warnf(ctx, "Cron Workflow FilterFunc: '%v' is not an unstructured", obj) return false } return !isCompleted(un) @@ -260,7 +270,7 @@ func isCompleted(wf v1.Object) bool { func (cc *Controller) syncAll(ctx context.Context) { defer runtimeutil.HandleCrashWithContext(ctx, runtimeutil.PanicHandlers...) - log.Debug("Syncing all CronWorkflows") + cc.logger.Debug(ctx, "Syncing all CronWorkflows") workflows, err := cc.wfLister.List() if err != nil { @@ -272,19 +282,19 @@ func (cc *Controller) syncAll(ctx context.Context) { for _, obj := range cronWorkflows { un, ok := obj.(*unstructured.Unstructured) if !ok { - log.Error("Unable to convert object to unstructured when syncing CronWorkflows") + cc.logger.Error(ctx, "Unable to convert object to unstructured when syncing CronWorkflows") continue } cronWf := &v1alpha1.CronWorkflow{} err := util.FromUnstructuredObj(un, cronWf) if err != nil { - log.WithError(err).Error("Unable to convert unstructured to CronWorkflow when syncing CronWorkflows") + cc.logger.WithError(ctx, err).Error(ctx, "Unable to convert unstructured to CronWorkflow when syncing CronWorkflows") continue } err = cc.syncCronWorkflow(ctx, cronWf, groupedWorkflows[cronWf.UID]) if err != nil { - log.WithError(err).Error("Unable to sync CronWorkflow") + cc.logger.WithError(ctx, err).Error(ctx, "Unable to sync CronWorkflow") continue } } diff --git a/workflow/templateresolution/context.go b/workflow/templateresolution/context.go index 131864685887..02b9f04208ca 100644 --- a/workflow/templateresolution/context.go +++ b/workflow/templateresolution/context.go @@ -4,13 +4,13 @@ import ( "context" "fmt" - log "github.com/sirupsen/logrus" apierr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/argoproj/argo-workflows/v3/errors" wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" typed "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned/typed/workflow/v1alpha1" + "github.com/argoproj/argo-workflows/v3/util/logging" "github.com/argoproj/argo-workflows/v3/workflow/common" ) @@ -73,35 +73,35 @@ type Context struct { tmplBase wfv1.TemplateHolder // workflow is the Workflow where templates will be stored workflow *wfv1.Workflow - // log is a logrus entry. - log *log.Entry + // log is a logging entry. + log logging.Logger } // NewContext returns new Context. -func NewContext(wftmplGetter WorkflowTemplateNamespacedGetter, cwftmplGetter ClusterWorkflowTemplateGetter, tmplBase wfv1.TemplateHolder, workflow *wfv1.Workflow) *Context { +func NewContext(wftmplGetter WorkflowTemplateNamespacedGetter, cwftmplGetter ClusterWorkflowTemplateGetter, tmplBase wfv1.TemplateHolder, workflow *wfv1.Workflow, log logging.Logger) *Context { return &Context{ wftmplGetter: wftmplGetter, cwftmplGetter: cwftmplGetter, tmplBase: tmplBase, workflow: workflow, - log: log.WithFields(log.Fields{}), + log: log, } } // NewContextFromClientSet returns new Context. -func NewContextFromClientSet(wftmplClientset typed.WorkflowTemplateInterface, clusterWftmplClient typed.ClusterWorkflowTemplateInterface, tmplBase wfv1.TemplateHolder, workflow *wfv1.Workflow) *Context { +func NewContextFromClientSet(wftmplClientset typed.WorkflowTemplateInterface, clusterWftmplClient typed.ClusterWorkflowTemplateInterface, tmplBase wfv1.TemplateHolder, workflow *wfv1.Workflow, log logging.Logger) *Context { return &Context{ wftmplGetter: WrapWorkflowTemplateInterface(wftmplClientset), cwftmplGetter: WrapClusterWorkflowTemplateInterface(clusterWftmplClient), tmplBase: tmplBase, workflow: workflow, - log: log.WithFields(log.Fields{}), + log: log, } } // GetTemplateByName returns a template by name in the context. -func (ctx *Context) GetTemplateByName(name string) (*wfv1.Template, error) { - ctx.log.Debugf("Getting the template by name: %s", name) +func (ctx *Context) GetTemplateByName(c context.Context, name string) (*wfv1.Template, error) { + ctx.log.Debugf(c, "Getting the template by name: %s", name) tmpl := ctx.tmplBase.GetTemplateByName(name) if tmpl == nil { @@ -118,8 +118,8 @@ func (ctx *Context) GetTemplateGetterFromRef(tmplRef *wfv1.TemplateRef) (wfv1.Te } // GetTemplateFromRef returns a template found by a given template ref. -func (ctx *Context) GetTemplateFromRef(tmplRef *wfv1.TemplateRef) (*wfv1.Template, error) { - ctx.log.Debug("Getting the template from ref") +func (ctx *Context) GetTemplateFromRef(c context.Context, tmplRef *wfv1.TemplateRef) (*wfv1.Template, error) { + ctx.log.Debug(c, "Getting the template from ref") var template *wfv1.Template var wftmpl wfv1.TemplateHolder var err error @@ -145,14 +145,14 @@ func (ctx *Context) GetTemplateFromRef(tmplRef *wfv1.TemplateRef) (*wfv1.Templat } // GetTemplate returns a template found by template name or template ref. -func (ctx *Context) GetTemplate(h wfv1.TemplateReferenceHolder) (*wfv1.Template, error) { - ctx.log.Debug("Getting the template") +func (ctx *Context) GetTemplate(c context.Context, h wfv1.TemplateReferenceHolder) (*wfv1.Template, error) { + ctx.log.Debug(c, "Getting the template") if x := h.GetTemplate(); x != nil { return x, nil } else if x := h.GetTemplateRef(); x != nil { - return ctx.GetTemplateFromRef(x) + return ctx.GetTemplateFromRef(c, x) } else if x := h.GetTemplateName(); x != "" { - return ctx.GetTemplateByName(x) + return ctx.GetTemplateByName(c, x) } return nil, errors.Errorf(errors.CodeInternal, "failed to get a template") } @@ -168,20 +168,20 @@ func (ctx *Context) GetTemplateScope() string { // ResolveTemplate digs into referenes and returns a merged template. // This method is the public start point of template resolution. -func (ctx *Context) ResolveTemplate(tmplHolder wfv1.TemplateReferenceHolder) (*Context, *wfv1.Template, bool, error) { - return ctx.resolveTemplateImpl(tmplHolder) +func (ctx *Context) ResolveTemplate(c context.Context, tmplHolder wfv1.TemplateReferenceHolder) (*Context, *wfv1.Template, bool, error) { + return ctx.resolveTemplateImpl(c, tmplHolder) } // resolveTemplateImpl digs into references and returns a merged template. // This method processes inputs and arguments so the inputs of the final // resolved template include intermediate parameter passing. // The other fields are just merged and shallower templates overwrite deeper. -func (ctx *Context) resolveTemplateImpl(tmplHolder wfv1.TemplateReferenceHolder) (*Context, *wfv1.Template, bool, error) { - ctx.log = ctx.log.WithFields(log.Fields{ +func (ctx *Context) resolveTemplateImpl(c context.Context, tmplHolder wfv1.TemplateReferenceHolder) (*Context, *wfv1.Template, bool, error) { + ctx.log = ctx.log.WithFields(c, logging.Fields{ "base": common.GetTemplateGetterString(ctx.tmplBase), "tmpl": common.GetTemplateHolderString(tmplHolder), }) - ctx.log.Debug("Resolving the template") + ctx.log.Debug(c, "Resolving the template") templateStored := false var tmpl *wfv1.Template @@ -192,10 +192,10 @@ func (ctx *Context) resolveTemplateImpl(tmplHolder wfv1.TemplateReferenceHolder) tmpl = ctx.workflow.GetStoredTemplate(scope, resourceName, tmplHolder) } if tmpl != nil { - ctx.log.Debug("Found stored template") + ctx.log.Debug(c, "Found stored template") } else { // Find newly appeared template. - newTmpl, err := ctx.GetTemplate(tmplHolder) + newTmpl, err := ctx.GetTemplate(c, tmplHolder) if err != nil { return nil, nil, false, err } @@ -208,12 +208,12 @@ func (ctx *Context) resolveTemplateImpl(tmplHolder wfv1.TemplateReferenceHolder) return nil, nil, false, err } if stored { - ctx.log.Debug("Stored the template") + ctx.log.Debug(c, "Stored the template") templateStored = true } err = ctx.workflow.SetStoredInlineTemplate(scope, resourceName, newTmpl) if err != nil { - ctx.log.Errorf("Failed to store the inline template: %v", err) + ctx.log.Errorf(c, "Failed to store the inline template: %v", err) } } tmpl = newTmpl @@ -248,7 +248,7 @@ func (ctx *Context) WithTemplateHolder(tmplHolder wfv1.TemplateReferenceHolder) // WithTemplateBase creates new context with a wfv1.TemplateHolder. func (ctx *Context) WithTemplateBase(tmplBase wfv1.TemplateHolder) *Context { - return NewContext(ctx.wftmplGetter, ctx.cwftmplGetter, tmplBase, ctx.workflow) + return NewContext(ctx.wftmplGetter, ctx.cwftmplGetter, tmplBase, ctx.workflow, ctx.log) } // WithWorkflowTemplate creates new context with a wfv1.TemplateHolder. diff --git a/workflow/templateresolution/context_test.go b/workflow/templateresolution/context_test.go index 70abade41c04..9fe2148c7fe3 100644 --- a/workflow/templateresolution/context_test.go +++ b/workflow/templateresolution/context_test.go @@ -12,6 +12,7 @@ import ( wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" wfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" fakewfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned/fake" + "github.com/argoproj/argo-workflows/v3/util/logging" ) func createWorkflowTemplate(wfClientset wfclientset.Interface, yamlStr string) error { @@ -103,14 +104,16 @@ spec: func TestGetTemplateByName(t *testing.T) { wfClientset := fakewfclientset.NewSimpleClientset() wftmpl := unmarshalWftmpl(baseWorkflowTemplateYaml) - ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil) + log := logging.NewSlogLogger() + c := context.Background() + ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil, log) - tmpl, err := ctx.GetTemplateByName("whalesay") + tmpl, err := ctx.GetTemplateByName(c, "whalesay") require.NoError(t, err) assert.Equal(t, "whalesay", tmpl.Name) assert.NotNil(t, tmpl.Container) - _, err = ctx.GetTemplateByName("unknown") + _, err = ctx.GetTemplateByName(c, "unknown") require.EqualError(t, err, "template unknown not found") } @@ -125,23 +128,25 @@ func TestGetTemplateFromRef(t *testing.T) { t.Fatal(err) } wftmpl := unmarshalWftmpl(baseWorkflowTemplateYaml) - ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil) + log := logging.NewSlogLogger() + c := context.Background() + ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil, log) // Get the template of existing template reference. tmplRef := wfv1.TemplateRef{Name: "some-workflow-template", Template: "whalesay"} - tmpl, err := ctx.GetTemplateFromRef(&tmplRef) + tmpl, err := ctx.GetTemplateFromRef(c, &tmplRef) require.NoError(t, err) assert.Equal(t, "whalesay", tmpl.Name) assert.NotNil(t, tmpl.Container) // Get the template of unexisting template reference. tmplRef = wfv1.TemplateRef{Name: "unknown-workflow-template", Template: "whalesay"} - _, err = ctx.GetTemplateFromRef(&tmplRef) + _, err = ctx.GetTemplateFromRef(c, &tmplRef) require.EqualError(t, err, "workflow template unknown-workflow-template not found") // Get the template of unexisting template name of existing template reference. tmplRef = wfv1.TemplateRef{Name: "some-workflow-template", Template: "unknown"} - _, err = ctx.GetTemplateFromRef(&tmplRef) + _, err = ctx.GetTemplateFromRef(c, &tmplRef) require.EqualError(t, err, "template unknown not found in workflow template some-workflow-template") } @@ -156,37 +161,40 @@ func TestGetTemplate(t *testing.T) { t.Fatal(err) } wftmpl := unmarshalWftmpl(baseWorkflowTemplateYaml) - ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil) + log := logging.NewSlogLogger() + c := context.Background() + ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil, log) // Get the template of existing template name. tmplHolder := wfv1.WorkflowStep{Template: "whalesay"} - tmpl, err := ctx.GetTemplate(&tmplHolder) + tmpl, err := ctx.GetTemplate(c, &tmplHolder) require.NoError(t, err) assert.Equal(t, "whalesay", tmpl.Name) assert.NotNil(t, tmpl.Container) // Get the template of unexisting template name. tmplHolder = wfv1.WorkflowStep{Template: "unexisting"} - _, err = ctx.GetTemplate(&tmplHolder) + _, err = ctx.GetTemplate(c, &tmplHolder) require.EqualError(t, err, "template unexisting not found") // Get the template of existing template reference. tmplHolder = wfv1.WorkflowStep{TemplateRef: &wfv1.TemplateRef{Name: "some-workflow-template", Template: "whalesay"}} - tmpl, err = ctx.GetTemplate(&tmplHolder) + tmpl, err = ctx.GetTemplate(c, &tmplHolder) require.NoError(t, err) assert.Equal(t, "whalesay", tmpl.Name) assert.NotNil(t, tmpl.Container) // Get the template of unexisting template reference. tmplHolder = wfv1.WorkflowStep{TemplateRef: &wfv1.TemplateRef{Name: "unknown-workflow-template", Template: "whalesay"}} - _, err = ctx.GetTemplate(&tmplHolder) + _, err = ctx.GetTemplate(c, &tmplHolder) require.EqualError(t, err, "workflow template unknown-workflow-template not found") } func TestGetCurrentTemplateBase(t *testing.T) { wfClientset := fakewfclientset.NewSimpleClientset() wftmpl := unmarshalWftmpl(baseWorkflowTemplateYaml) - ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil) + log := logging.NewSlogLogger() + ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil, log) // Get the template base of existing template name. tmplBase := ctx.GetCurrentTemplateBase() @@ -206,7 +214,8 @@ func TestWithTemplateHolder(t *testing.T) { t.Fatal(err) } wftmpl := unmarshalWftmpl(baseWorkflowTemplateYaml) - ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil) + log := logging.NewSlogLogger() + ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil, log) var tmplGetter wfv1.TemplateHolder // Get the template base of existing template name. @@ -248,11 +257,13 @@ func TestResolveTemplate(t *testing.T) { require.NoError(t, err) wftmpl := unmarshalWftmpl(baseWorkflowTemplateYaml) - ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil) + log := logging.NewSlogLogger() + c := context.Background() + ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil, log) // Get the template of template name. tmplHolder := wfv1.WorkflowStep{Template: "whalesay"} - ctx, tmpl, _, err := ctx.ResolveTemplate(&tmplHolder) + ctx, tmpl, _, err := ctx.ResolveTemplate(c, &tmplHolder) require.NoError(t, err) wftmpl, ok := ctx.tmplBase.(*wfv1.WorkflowTemplate) require.True(t, ok, "tmplBase is not a WorkflowTemplate") @@ -262,7 +273,7 @@ func TestResolveTemplate(t *testing.T) { var tmplGetter wfv1.TemplateHolder // Get the template of template reference. tmplHolder = wfv1.WorkflowStep{TemplateRef: &wfv1.TemplateRef{Name: "some-workflow-template", Template: "whalesay"}} - ctx, tmpl, _, err = ctx.ResolveTemplate(&tmplHolder) + ctx, tmpl, _, err = ctx.ResolveTemplate(c, &tmplHolder) require.NoError(t, err) tmplGetter, ok = ctx.tmplBase.(*wfv1.WorkflowTemplate) @@ -273,7 +284,7 @@ func TestResolveTemplate(t *testing.T) { // Get the template of local nested template reference. tmplHolder = wfv1.WorkflowStep{TemplateRef: &wfv1.TemplateRef{Name: "some-workflow-template", Template: "local-whalesay"}} - ctx, tmpl, _, err = ctx.ResolveTemplate(&tmplHolder) + ctx, tmpl, _, err = ctx.ResolveTemplate(c, &tmplHolder) require.NoError(t, err) tmplGetter, ok = ctx.tmplBase.(*wfv1.WorkflowTemplate) @@ -284,7 +295,7 @@ func TestResolveTemplate(t *testing.T) { // Get the template of nested template reference. tmplHolder = wfv1.WorkflowStep{TemplateRef: &wfv1.TemplateRef{Name: "some-workflow-template", Template: "another-whalesay"}} - ctx, tmpl, _, err = ctx.ResolveTemplate(&tmplHolder) + ctx, tmpl, _, err = ctx.ResolveTemplate(c, &tmplHolder) require.NoError(t, err) tmplGetter, ok = ctx.tmplBase.(*wfv1.WorkflowTemplate) @@ -297,7 +308,7 @@ func TestResolveTemplate(t *testing.T) { tmplHolder = wfv1.WorkflowStep{ TemplateRef: &wfv1.TemplateRef{Name: "some-workflow-template", Template: "whalesay-with-arguments"}, } - ctx, tmpl, _, err = ctx.ResolveTemplate(&tmplHolder) + ctx, tmpl, _, err = ctx.ResolveTemplate(c, &tmplHolder) require.NoError(t, err) tmplGetter, ok = ctx.tmplBase.(*wfv1.WorkflowTemplate) @@ -309,7 +320,7 @@ func TestResolveTemplate(t *testing.T) { tmplHolder = wfv1.WorkflowStep{ TemplateRef: &wfv1.TemplateRef{Name: "some-workflow-template", Template: "nested-whalesay-with-arguments"}, } - ctx, tmpl, _, err = ctx.ResolveTemplate(&tmplHolder) + ctx, tmpl, _, err = ctx.ResolveTemplate(c, &tmplHolder) require.NoError(t, err) tmplGetter, ok = ctx.tmplBase.(*wfv1.WorkflowTemplate) @@ -321,7 +332,8 @@ func TestResolveTemplate(t *testing.T) { func TestWithTemplateBase(t *testing.T) { wfClientset := fakewfclientset.NewSimpleClientset() wftmpl := unmarshalWftmpl(baseWorkflowTemplateYaml) - ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil) + log := logging.NewSlogLogger() + ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil, log) anotherWftmpl := unmarshalWftmpl(anotherWorkflowTemplateYaml) @@ -335,7 +347,8 @@ func TestWithTemplateBase(t *testing.T) { func TestOnWorkflowTemplate(t *testing.T) { wfClientset := fakewfclientset.NewSimpleClientset() wftmpl := unmarshalWftmpl(baseWorkflowTemplateYaml) - ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil) + log := logging.NewSlogLogger() + ctx := NewContextFromClientSet(wfClientset.ArgoprojV1alpha1().WorkflowTemplates(metav1.NamespaceDefault), wfClientset.ArgoprojV1alpha1().ClusterWorkflowTemplates(), wftmpl, nil, log) err := createWorkflowTemplate(wfClientset, anotherWorkflowTemplateYaml) require.NoError(t, err) diff --git a/workflow/util/util.go b/workflow/util/util.go index 1813bf631ae2..dbf57b9003e4 100644 --- a/workflow/util/util.go +++ b/workflow/util/util.go @@ -46,6 +46,7 @@ import ( "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned/typed/workflow/v1alpha1" cmdutil "github.com/argoproj/argo-workflows/v3/util/cmd" errorsutil "github.com/argoproj/argo-workflows/v3/util/errors" + "github.com/argoproj/argo-workflows/v3/util/logging" "github.com/argoproj/argo-workflows/v3/util/retry" unstructutil "github.com/argoproj/argo-workflows/v3/util/unstructured" waitutil "github.com/argoproj/argo-workflows/v3/util/wait" @@ -494,7 +495,7 @@ type SetOperationValues struct { OutputParameters map[string]string } -func AddParamToGlobalScope(wf *wfv1.Workflow, log *log.Entry, param wfv1.Parameter) bool { +func AddParamToGlobalScope(ctx context.Context, wf *wfv1.Workflow, log logging.Logger, param wfv1.Parameter) bool { wfUpdated := false if param.GlobalName == "" { return wfUpdated @@ -512,14 +513,14 @@ func AddParamToGlobalScope(wf *wfv1.Workflow, log *log.Entry, param wfv1.Paramet } paramName := fmt.Sprintf("workflow.outputs.parameters.%s", param.GlobalName) if index == -1 { - log.Infof("setting %s: '%s'", paramName, param.Value) + log.Infof(ctx, "setting %s: '%s'", paramName, param.Value) gParam := wfv1.Parameter{Name: param.GlobalName, Value: param.Value} wf.Status.Outputs.Parameters = append(wf.Status.Outputs.Parameters, gParam) wfUpdated = true } else { prevVal := wf.Status.Outputs.Parameters[index].Value if prevVal == nil || (param.Value != nil && *prevVal != *param.Value) { - log.Infof("overwriting %s: '%s' -> '%s'", paramName, wf.Status.Outputs.Parameters[index].Value, param.Value) + log.Infof(ctx, "overwriting %s: '%s' -> '%s'", paramName, wf.Status.Outputs.Parameters[index].Value, param.Value) wf.Status.Outputs.Parameters[index].Value = param.Value wfUpdated = true } @@ -579,7 +580,8 @@ func updateSuspendedNode(ctx context.Context, wfIf v1alpha1.WorkflowInterface, h node.Outputs.Parameters[i].ValueFrom = nil nodeUpdated = true hit = true - AddParamToGlobalScope(wf, log.NewEntry(log.StandardLogger()), node.Outputs.Parameters[i]) + log := logging.NewSlogLogger() + AddParamToGlobalScope(ctx, wf, log, node.Outputs.Parameters[i]) break } } diff --git a/workflow/util/util_test.go b/workflow/util/util_test.go index aad12f74227c..389392708d16 100644 --- a/workflow/util/util_test.go +++ b/workflow/util/util_test.go @@ -349,8 +349,8 @@ func TestAddParamToGlobalScopeValueNil(t *testing.T) { }, }, } - - p := AddParamToGlobalScope(&wf, nil, wfv1.Parameter{ + ctx := context.Background() + p := AddParamToGlobalScope(ctx, &wf, nil, wfv1.Parameter{ Name: "test", Value: nil, GlobalName: "test", diff --git a/workflow/validate/validate.go b/workflow/validate/validate.go index 9b5e51fd4549..cf86d6e63b29 100644 --- a/workflow/validate/validate.go +++ b/workflow/validate/validate.go @@ -22,6 +22,7 @@ import ( wfv1 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" "github.com/argoproj/argo-workflows/v3/util" "github.com/argoproj/argo-workflows/v3/util/intstr" + "github.com/argoproj/argo-workflows/v3/util/logging" "github.com/argoproj/argo-workflows/v3/util/sorting" "github.com/argoproj/argo-workflows/v3/util/template" "github.com/argoproj/argo-workflows/v3/workflow/artifacts/hdfs" @@ -140,7 +141,8 @@ func validateHooks(hooks wfv1.LifecycleHooks, hookBaseName string) error { // ValidateWorkflow accepts a workflow and performs validation against it. func ValidateWorkflow(wftmplGetter templateresolution.WorkflowTemplateNamespacedGetter, cwftmplGetter templateresolution.ClusterWorkflowTemplateGetter, wf *wfv1.Workflow, wfDefaults *wfv1.Workflow, opts ValidateOpts) error { ctx := newTemplateValidationCtx(wf, opts) - tmplCtx := templateresolution.NewContext(wftmplGetter, cwftmplGetter, wf, wf) + log := logging.NewSlogLogger() + tmplCtx := templateresolution.NewContext(wftmplGetter, cwftmplGetter, wf, wf, log) var wfSpecHolder wfv1.WorkflowSpecHolder var wfTmplRef *wfv1.TemplateRef var err error @@ -408,7 +410,7 @@ func ValidateCronWorkflow(ctx context.Context, wftmplGetter templateresolution.W return nil } -func (ctx *templateValidationCtx) validateInitContainers(containers []wfv1.UserContainer) error { +func (tctx *templateValidationCtx) validateInitContainers(containers []wfv1.UserContainer) error { for _, container := range containers { if len(container.Container.Name) == 0 { return errors.Errorf(errors.CodeBadRequest, "initContainers must all have container name") @@ -417,8 +419,7 @@ func (ctx *templateValidationCtx) validateInitContainers(containers []wfv1.UserC return nil } -func (ctx *templateValidationCtx) validateTemplate(tmpl *wfv1.Template, tmplCtx *templateresolution.Context, args wfv1.ArgumentsProvider, workflowTemplateValidation bool) error { - +func (tctx *templateValidationCtx) validateTemplate(ctx context.Context, tmpl *wfv1.Template, tmplCtx *templateresolution.Context, args wfv1.ArgumentsProvider, workflowTemplateValidation bool) error { if err := validateTemplateType(tmpl); err != nil { return err } @@ -428,7 +429,7 @@ func (ctx *templateValidationCtx) validateTemplate(tmpl *wfv1.Template, tmplCtx return err } - if err := ctx.validateInitContainers(tmpl.InitContainers); err != nil { + if err := tctx.validateInitContainers(tmpl.InitContainers); err != nil { return err } @@ -462,7 +463,7 @@ func (ctx *templateValidationCtx) validateTemplate(tmpl *wfv1.Template, tmplCtx } } - newTmpl, err := common.ProcessArgs(tmpl, args, ctx.globalParams, localParams, true, "", nil) + newTmpl, err := common.ProcessArgs(tmpl, args, tctx.globalParams, localParams, true, "", nil) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s %s", tmpl.Name, err) } @@ -480,28 +481,28 @@ func (ctx *templateValidationCtx) validateTemplate(tmpl *wfv1.Template, tmplCtx } tmplID := getTemplateID(tmpl) - _, ok := ctx.results[tmplID] + _, ok := tctx.results[tmplID] if ok { // we can skip the rest since it has been validated. return nil } - ctx.results[tmplID] = true + tctx.results[tmplID] = true - for globalVar, val := range ctx.globalParams { + for globalVar, val := range tctx.globalParams { scope[globalVar] = val } switch newTmpl.GetType() { case wfv1.TemplateTypeSteps: - err = ctx.validateSteps(scope, tmplCtx, newTmpl, workflowTemplateValidation) + err = tctx.validateSteps(scope, tmplCtx, newTmpl, workflowTemplateValidation) case wfv1.TemplateTypeDAG: - err = ctx.validateDAG(scope, tmplCtx, newTmpl, workflowTemplateValidation) + err = tctx.validateDAG(ctx, scope, tmplCtx, newTmpl, workflowTemplateValidation) default: - err = ctx.validateLeaf(scope, tmplCtx, newTmpl, workflowTemplateValidation) + err = tctx.validateLeaf(scope, tmplCtx, newTmpl, workflowTemplateValidation) } if err != nil { return err } - err = validateOutputs(scope, ctx.globalParams, newTmpl, workflowTemplateValidation) + err = validateOutputs(scope, tctx.globalParams, newTmpl, workflowTemplateValidation) if err != nil { return err } @@ -543,7 +544,8 @@ func VerifyResolvedVariables(obj interface{}) error { } // validateTemplateHolder validates a template holder and returns the validated template. -func (ctx *templateValidationCtx) validateTemplateHolder(tmplHolder wfv1.TemplateReferenceHolder, tmplCtx *templateresolution.Context, args wfv1.ArgumentsProvider, workflowTemplateValidation bool) (*wfv1.Template, error) { +func (tctx *templateValidationCtx) validateTemplateHolder(tmplHolder wfv1.TemplateReferenceHolder, tmplCtx *templateresolution.Context, args wfv1.ArgumentsProvider, workflowTemplateValidation bool) (*wfv1.Template, error) { + ctx := context.Background() tmplRef := tmplHolder.GetTemplateRef() tmplName := tmplHolder.GetTemplateName() if tmplRef != nil { @@ -561,7 +563,7 @@ func (ctx *templateValidationCtx) validateTemplateHolder(tmplHolder wfv1.Templat return nil, nil } } else if tmplName != "" { - _, err := tmplCtx.GetTemplateByName(tmplName) + _, err := tmplCtx.GetTemplateByName(ctx, tmplName) if err != nil { if argoerr, ok := err.(errors.ArgoError); ok && argoerr.Code() == errors.CodeNotFound { return nil, errors.Errorf(errors.CodeBadRequest, "template name '%s' undefined", tmplName) @@ -570,7 +572,7 @@ func (ctx *templateValidationCtx) validateTemplateHolder(tmplHolder wfv1.Templat } } - tmplCtx, resolvedTmpl, _, err := tmplCtx.ResolveTemplate(tmplHolder) + tmplCtx, resolvedTmpl, _, err := tmplCtx.ResolveTemplate(ctx, tmplHolder) if err != nil { if argoerr, ok := err.(errors.ArgoError); ok && argoerr.Code() == errors.CodeNotFound { if tmplRef != nil { @@ -592,7 +594,7 @@ func (ctx *templateValidationCtx) validateTemplateHolder(tmplHolder wfv1.Templat } } - return resolvedTmpl, ctx.validateTemplate(resolvedTmpl, tmplCtx, args, workflowTemplateValidation) + return resolvedTmpl, tctx.validateTemplate(ctx, resolvedTmpl, tmplCtx, args, workflowTemplateValidation) } // validateTemplateType validates that only one template type is defined @@ -731,12 +733,12 @@ func validateNonLeaf(tmpl *wfv1.Template) error { return nil } -func (ctx *templateValidationCtx) validateLeaf(scope map[string]interface{}, tmplCtx *templateresolution.Context, tmpl *wfv1.Template, workflowTemplateValidation bool) error { +func (tctx *templateValidationCtx) validateLeaf(scope map[string]interface{}, tmplCtx *templateresolution.Context, tmpl *wfv1.Template, workflowTemplateValidation bool) error { tmplBytes, err := json.Marshal(tmpl) if err != nil { return errors.InternalWrapError(err) } - err = resolveAllVariables(scope, ctx.globalParams, string(tmplBytes), workflowTemplateValidation) + err = resolveAllVariables(scope, tctx.globalParams, string(tmplBytes), workflowTemplateValidation) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s: %s", tmpl.Name, err.Error()) } @@ -932,7 +934,7 @@ func validateArgumentsValues(prefix string, arguments wfv1.Arguments, allowEmpty return nil } -func (ctx *templateValidationCtx) validateSteps(scope map[string]interface{}, tmplCtx *templateresolution.Context, tmpl *wfv1.Template, workflowTemplateValidation bool) error { +func (tctx *templateValidationCtx) validateSteps(scope map[string]interface{}, tmplCtx *templateresolution.Context, tmpl *wfv1.Template, workflowTemplateValidation bool) error { err := validateNonLeaf(tmpl) if err != nil { return err @@ -962,13 +964,13 @@ func (ctx *templateValidationCtx) validateSteps(scope map[string]interface{}, tm if err != nil { return err } - resolvedTmpl, err := ctx.validateTemplateHolder(&step, tmplCtx, &FakeArguments{}, workflowTemplateValidation) + resolvedTmpl, err := tctx.validateTemplateHolder(&step, tmplCtx, &FakeArguments{}, workflowTemplateValidation) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.steps[%d].%s %s", tmpl.Name, i, step.Name, err.Error()) } if step.HasExitHook() { - ctx.addOutputsToScope(resolvedTmpl, fmt.Sprintf("steps.%s", step.Name), scope, false, false) + tctx.addOutputsToScope(resolvedTmpl, fmt.Sprintf("steps.%s", step.Name), scope, false, false) } resolvedTemplates[step.Name] = resolvedTmpl @@ -993,17 +995,17 @@ func (ctx *templateValidationCtx) validateSteps(scope map[string]interface{}, tm } } - err = resolveAllVariables(stepScope, ctx.globalParams, string(stepBytes), workflowTemplateValidation) + err = resolveAllVariables(stepScope, tctx.globalParams, string(stepBytes), workflowTemplateValidation) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.steps %s", tmpl.Name, err.Error()) } aggregate := len(step.WithItems) > 0 || step.WithParam != "" - ctx.addOutputsToScope(resolvedTmpl, fmt.Sprintf("steps.%s", step.Name), scope, aggregate, false) + tctx.addOutputsToScope(resolvedTmpl, fmt.Sprintf("steps.%s", step.Name), scope, aggregate, false) // Validate the template again with actual arguments. - _, err = ctx.validateTemplateHolder(&step, tmplCtx, &step.Arguments, workflowTemplateValidation) + _, err = tctx.validateTemplateHolder(&step, tmplCtx, &step.Arguments, workflowTemplateValidation) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.steps[%d].%s %s", tmpl.Name, i, step.Name, err.Error()) } @@ -1058,7 +1060,7 @@ func addItemsToScope(withItems []wfv1.Item, withParam string, withSequence *wfv1 return nil } -func (ctx *templateValidationCtx) addOutputsToScope(tmpl *wfv1.Template, prefix string, scope map[string]interface{}, aggregate bool, isAncestor bool) { +func (tctx *templateValidationCtx) addOutputsToScope(tmpl *wfv1.Template, prefix string, scope map[string]interface{}, aggregate bool, isAncestor bool) { scope[fmt.Sprintf("%s.id", prefix)] = true scope[fmt.Sprintf("%s.startedAt", prefix)] = true scope[fmt.Sprintf("%s.finishedAt", prefix)] = true @@ -1079,7 +1081,7 @@ func (ctx *templateValidationCtx) addOutputsToScope(tmpl *wfv1.Template, prefix if !isParameter(param.GlobalName) { globalParamName := fmt.Sprintf("workflow.outputs.parameters.%s", param.GlobalName) scope[globalParamName] = true - ctx.globalParams[globalParamName] = placeholderGenerator.NextPlaceholder() + tctx.globalParams[globalParamName] = placeholderGenerator.NextPlaceholder() } else { logrus.Warnf("GlobalName '%s' is a parameter and won't be validated until runtime", param.GlobalName) scope[anyWorkflowOutputParameterMagicValue] = true @@ -1092,7 +1094,7 @@ func (ctx *templateValidationCtx) addOutputsToScope(tmpl *wfv1.Template, prefix if !isParameter(art.GlobalName) { globalArtName := fmt.Sprintf("workflow.outputs.artifacts.%s", art.GlobalName) scope[globalArtName] = true - ctx.globalParams[globalArtName] = placeholderGenerator.NextPlaceholder() + tctx.globalParams[globalArtName] = placeholderGenerator.NextPlaceholder() } else { logrus.Warnf("GlobalName '%s' is a parameter and won't be validated until runtime", art.GlobalName) scope[anyWorkflowOutputArtifactMagicValue] = true @@ -1277,13 +1279,13 @@ type dagValidationContext struct { dependencies map[string]map[string]common.DependencyType // map of DAG tasks, each one containing a map of [task it's dependent on] -> [dependency type] } -func (d *dagValidationContext) GetTask(taskName string) *wfv1.DAGTask { +func (d *dagValidationContext) GetTask(ctx context.Context, taskName string) *wfv1.DAGTask { task := d.tasks[taskName] return &task } -func (d *dagValidationContext) GetTaskDependencies(taskName string) []string { - dependencies := d.GetTaskDependenciesWithDependencyTypes(taskName) +func (d *dagValidationContext) GetTaskDependencies(ctx context.Context, taskName string) []string { + dependencies := d.GetTaskDependenciesWithDependencyTypes(ctx, taskName) var dependencyTasks []string for task := range dependencies { @@ -1293,21 +1295,21 @@ func (d *dagValidationContext) GetTaskDependencies(taskName string) []string { return dependencyTasks } -func (d *dagValidationContext) GetTaskDependenciesWithDependencyTypes(taskName string) map[string]common.DependencyType { +func (d *dagValidationContext) GetTaskDependenciesWithDependencyTypes(ctx context.Context, taskName string) map[string]common.DependencyType { if dependencies, ok := d.dependencies[taskName]; ok { return dependencies } - task := d.GetTask(taskName) - dependencies, _ := common.GetTaskDependencies(task, d) + task := d.GetTask(ctx, taskName) + dependencies, _ := common.GetTaskDependencies(ctx, task, d) d.dependencies[taskName] = dependencies return d.dependencies[taskName] } -func (d *dagValidationContext) GetTaskFinishedAtTime(taskName string) time.Time { +func (d *dagValidationContext) GetTaskFinishedAtTime(ctx context.Context, taskName string) time.Time { return time.Now() } -func (ctx *templateValidationCtx) validateDAG(scope map[string]interface{}, tmplCtx *templateresolution.Context, tmpl *wfv1.Template, workflowTemplateValidation bool) error { +func (tctx *templateValidationCtx) validateDAG(ctx context.Context, scope map[string]interface{}, tmplCtx *templateresolution.Context, tmpl *wfv1.Template, workflowTemplateValidation bool) error { err := validateNonLeaf(tmpl) if err != nil { return err @@ -1355,7 +1357,7 @@ func (ctx *templateValidationCtx) validateDAG(scope map[string]interface{}, tmpl return errors.Errorf(errors.CodeBadRequest, "templates.%s cannot use 'continueOn' when using 'depends'. Instead use 'dep-task.Failed'/'dep-task.Errored'", tmpl.Name) } - resolvedTmpl, err := ctx.validateTemplateHolder(&task, tmplCtx, &FakeArguments{}, workflowTemplateValidation) + resolvedTmpl, err := tctx.validateTemplateHolder(&task, tmplCtx, &FakeArguments{}, workflowTemplateValidation) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.tasks.%s %s", tmpl.Name, task.Name, err.Error()) @@ -1365,14 +1367,14 @@ func (ctx *templateValidationCtx) validateDAG(scope map[string]interface{}, tmpl prefix := fmt.Sprintf("tasks.%s", task.Name) aggregate := len(task.WithItems) > 0 || task.WithParam != "" - ctx.addOutputsToScope(resolvedTmpl, prefix, scope, aggregate, false) + tctx.addOutputsToScope(resolvedTmpl, prefix, scope, aggregate, false) err = common.ValidateTaskResults(&task) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.tasks.%s %s", tmpl.Name, task.Name, err.Error()) } - for depName, depType := range dagValidationCtx.GetTaskDependenciesWithDependencyTypes(task.Name) { + for depName, depType := range dagValidationCtx.GetTaskDependenciesWithDependencyTypes(ctx, task.Name) { task, ok := dagValidationCtx.tasks[depName] if !ok { return errors.Errorf(errors.CodeBadRequest, @@ -1386,10 +1388,10 @@ func (ctx *templateValidationCtx) validateDAG(scope map[string]interface{}, tmpl } } - if err = verifyNoCycles(tmpl, dagValidationCtx); err != nil { + if err = verifyNoCycles(ctx, tmpl, dagValidationCtx); err != nil { return err } - err = resolveAllVariables(scope, ctx.globalParams, tmpl.DAG.Target, workflowTemplateValidation) + err = resolveAllVariables(scope, tctx.globalParams, tmpl.DAG.Target, workflowTemplateValidation) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.targets %s", tmpl.Name, err.Error()) } @@ -1405,9 +1407,9 @@ func (ctx *templateValidationCtx) validateDAG(scope map[string]interface{}, tmpl if task.Hooks != nil { scope[fmt.Sprintf("%s.status", prefix)] = true } - ctx.addOutputsToScope(resolvedTmpl, prefix, scope, false, false) + tctx.addOutputsToScope(resolvedTmpl, prefix, scope, false, false) if task.HasExitHook() { - ctx.addOutputsToScope(resolvedTmpl, prefix, scope, false, false) + tctx.addOutputsToScope(resolvedTmpl, prefix, scope, false, false) } taskBytes, err := json.Marshal(task) if err != nil { @@ -1417,13 +1419,13 @@ func (ctx *templateValidationCtx) validateDAG(scope map[string]interface{}, tmpl for k, v := range scope { taskScope[k] = v } - ancestry := common.GetTaskAncestry(dagValidationCtx, task.Name) + ancestry := common.GetTaskAncestry(ctx, dagValidationCtx, task.Name) for _, ancestor := range ancestry { - ancestorTask := dagValidationCtx.GetTask(ancestor) + ancestorTask := dagValidationCtx.GetTask(ctx, ancestor) resolvedTmpl := resolvedTemplates[ancestor] ancestorPrefix := fmt.Sprintf("tasks.%s", ancestor) aggregate := len(ancestorTask.WithItems) > 0 || ancestorTask.WithParam != "" - ctx.addOutputsToScope(resolvedTmpl, ancestorPrefix, taskScope, aggregate, true) + tctx.addOutputsToScope(resolvedTmpl, ancestorPrefix, taskScope, aggregate, true) } if i := task.Inline; i != nil { for _, p := range i.Inputs.Parameters { @@ -1435,7 +1437,7 @@ func (ctx *templateValidationCtx) validateDAG(scope map[string]interface{}, tmpl if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.tasks.%s %s", tmpl.Name, task.Name, err.Error()) } - err = resolveAllVariables(taskScope, ctx.globalParams, string(taskBytes), workflowTemplateValidation) + err = resolveAllVariables(taskScope, tctx.globalParams, string(taskBytes), workflowTemplateValidation) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.tasks.%s %s", tmpl.Name, task.Name, err.Error()) } @@ -1448,7 +1450,7 @@ func (ctx *templateValidationCtx) validateDAG(scope map[string]interface{}, tmpl return errors.Errorf(errors.CodeBadRequest, "templates.%s.tasks.%s %s", tmpl.Name, task.Name, err.Error()) } // Validate the template again with actual arguments. - _, err = ctx.validateTemplateHolder(&task, tmplCtx, &task.Arguments, workflowTemplateValidation) + _, err = tctx.validateTemplateHolder(&task, tmplCtx, &task.Arguments, workflowTemplateValidation) if err != nil { return errors.Errorf(errors.CodeBadRequest, "templates.%s.tasks.%s %s", tmpl.Name, task.Name, err.Error()) } @@ -1505,15 +1507,15 @@ func validateDAGTargets(tmpl *wfv1.Template, nameToTask map[string]wfv1.DAGTask) } // verifyNoCycles verifies there are no cycles in the DAG graph -func verifyNoCycles(tmpl *wfv1.Template, ctx *dagValidationContext) error { +func verifyNoCycles(ctx context.Context, tmpl *wfv1.Template, dctx *dagValidationContext) error { visited := make(map[string]bool) var noCyclesHelper func(taskName string, cycle []string) error noCyclesHelper = func(taskName string, cycle []string) error { if _, ok := visited[taskName]; ok { return nil } - task := ctx.GetTask(taskName) - for _, depName := range ctx.GetTaskDependencies(task.Name) { + task := dctx.GetTask(ctx, taskName) + for _, depName := range dctx.GetTaskDependencies(ctx, task.Name) { for _, name := range cycle { if name == depName { return errors.Errorf(errors.CodeBadRequest, @@ -1541,13 +1543,14 @@ func verifyNoCycles(tmpl *wfv1.Template, ctx *dagValidationContext) error { return nil } -func sortDAGTasks(tmpl *wfv1.Template, ctx *dagValidationContext) error { +func sortDAGTasks(tmpl *wfv1.Template, tctx *dagValidationContext) error { taskMap := make(map[string]*wfv1.DAGTask, len(tmpl.DAG.Tasks)) sortingGraph := make([]*sorting.TopologicalSortingNode, len(tmpl.DAG.Tasks)) + ctx := context.Background() for index := range tmpl.DAG.Tasks { task := tmpl.DAG.Tasks[index] taskMap[task.Name] = &task - dependenciesMap, _ := common.GetTaskDependencies(&task, ctx) + dependenciesMap, _ := common.GetTaskDependencies(ctx, &task, tctx) var dependencies []string for taskName := range dependenciesMap { dependencies = append(dependencies, taskName)