diff --git a/pkg/coordinator/tasks/run_task_options/README.md b/pkg/coordinator/tasks/run_task_options/README.md index 84e3552..4753bba 100644 --- a/pkg/coordinator/tasks/run_task_options/README.md +++ b/pkg/coordinator/tasks/run_task_options/README.md @@ -20,6 +20,12 @@ The `run_task_options` task is designed to execute a single task with configurab - **`ignoreFailure`**:\ When `true`, any failure result from the child task is ignored, and the `run_task_options` task will return a success result instead. This is useful for cases where the child task's failure is an acceptable outcome. +- **`retryOnFailure`**:\ + If set to `true`, the task will retry the execution of the child task if it fails, up to the maximum number of retries specified by `maxRetryCount`. + +- **`maxRetryCount`**:\ + The maximum number of times the child task will be retried if it fails and `retryOnFailure` is true. A value of 0 means no retries. + - **`newVariableScope`**:\ Determines whether to create a new variable scope for the child task. If `false`, the current scope is passed through, allowing the child task to share the same variable context as the `run_task_options` task. @@ -35,5 +41,7 @@ Default settings for the `run_task_options` task: invertResult: false expectFailure: false ignoreFailure: false + retryOnFailure: false + maxRetryCount: 0 newVariableScope: false ``` diff --git a/pkg/coordinator/tasks/run_task_options/config.go b/pkg/coordinator/tasks/run_task_options/config.go index e18b90d..c272ceb 100644 --- a/pkg/coordinator/tasks/run_task_options/config.go +++ b/pkg/coordinator/tasks/run_task_options/config.go @@ -12,6 +12,8 @@ type Config struct { InvertResult bool `yaml:"invertResult" json:"invertResult"` ExpectFailure bool `yaml:"expectFailure" json:"expectFailure"` IgnoreFailure bool `yaml:"ignoreFailure" json:"ignoreFailure"` + RetryOnFailure bool `yaml:"retryOnFailure" json:"retryOnFailure"` + MaxRetryCount uint `yaml:"maxRetryCount" json:"maxRetryCount"` NewVariableScope bool `yaml:"newVariableScope" json:"newVariableScope"` } diff --git a/pkg/coordinator/tasks/run_task_options/task.go b/pkg/coordinator/tasks/run_task_options/task.go index af21605..1032bcf 100644 --- a/pkg/coordinator/tasks/run_task_options/task.go +++ b/pkg/coordinator/tasks/run_task_options/task.go @@ -80,45 +80,60 @@ func (t *Task) LoadConfig() error { return err2 } - // init child task - taskOpts, err := t.ctx.Scheduler.ParseTaskOptions(config.Task) - if err != nil { - return fmt.Errorf("failed parsing child task config: %w", err) - } - - taskVars := t.ctx.Vars - if config.NewVariableScope { - taskVars = taskVars.NewScope() - } - - t.task, err = t.ctx.NewTask(taskOpts, taskVars) - if err != nil { - return fmt.Errorf("failed initializing child task: %w", err) - } - t.config = config return nil } func (t *Task) Execute(ctx context.Context) error { - err := t.ctx.Scheduler.ExecuteTask(ctx, t.task, func(ctx context.Context, cancelFn context.CancelFunc, _ types.Task) { - t.watchTaskResult(ctx, cancelFn) - }) - - switch { - case t.config.ExpectFailure: - if err == nil { - return fmt.Errorf("child task succeeded, but should have failed") - } - case t.config.IgnoreFailure: + retryCount := uint(0) + + for { + // init child task + taskOpts, err := t.ctx.Scheduler.ParseTaskOptions(t.config.Task) if err != nil { - t.logger.Warnf("child task failed: %w", err) + return fmt.Errorf("failed parsing child task config: %w", err) + } + + taskVars := t.ctx.Vars + if t.config.NewVariableScope { + taskVars = taskVars.NewScope() } - default: + + t.task, err = t.ctx.NewTask(taskOpts, taskVars) if err != nil { - return fmt.Errorf("child task failed: %w", err) + return fmt.Errorf("failed initializing child task: %w", err) + } + + // execute task + err = t.ctx.Scheduler.ExecuteTask(ctx, t.task, func(ctx context.Context, cancelFn context.CancelFunc, _ types.Task) { + t.watchTaskResult(ctx, cancelFn) + }) + + switch { + case t.config.RetryOnFailure && retryCount < t.config.MaxRetryCount: + if err != nil { + retryCount++ + + t.logger.Warnf("child task failed: %w (retrying)", err) + + continue + } + case t.config.ExpectFailure: + if err == nil { + return fmt.Errorf("child task succeeded, but should have failed") + } + case t.config.IgnoreFailure: + if err != nil { + t.logger.Warnf("child task failed: %w", err) + } + default: + if err != nil { + return fmt.Errorf("child task failed: %w", err) + } } + + break } return nil