Add --resume-flag to plan run subcommand

Allows to skip phases in plan execution.

Change-Id: Iee0e0da5ae9836f27ec1810ac0d333247a6ddb8f
Signed-off-by: Ruslan Aliev <raliev@mirantis.com>
Closes: #667
This commit is contained in:
Ruslan Aliev 2021-11-10 08:24:18 +00:00
parent 4f7a9dbf6d
commit 89bd08e102
9 changed files with 31 additions and 8 deletions

View File

@ -39,7 +39,7 @@ Perform a dry run of a plan
// NewRunCommand creates a command which execute a particular phase plan // NewRunCommand creates a command which execute a particular phase plan
func NewRunCommand(cfgFactory config.Factory) *cobra.Command { func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
r := &phase.PlanRunCommand{Factory: cfgFactory} r := &phase.PlanRunCommand{Factory: cfgFactory}
f := &phase.RunFlags{} f := &phase.PlanRunFlags{}
runCmd := &cobra.Command{ runCmd := &cobra.Command{
Use: "run PLAN_NAME", Use: "run PLAN_NAME",
@ -55,6 +55,8 @@ func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
r.Options.DryRun = f.DryRun r.Options.DryRun = f.DryRun
case "wait-timeout": case "wait-timeout":
r.Options.Timeout = &f.Timeout r.Options.Timeout = &f.Timeout
case "resume-from":
r.Options.ResumeFromPhase = f.ResumeFromPhase
} }
} }
cmd.Flags().Visit(fn) cmd.Flags().Visit(fn)
@ -63,6 +65,7 @@ func NewRunCommand(cfgFactory config.Factory) *cobra.Command {
} }
flags := runCmd.Flags() flags := runCmd.Flags()
flags.StringVar(&f.ResumeFromPhase, "resume-from", "", "skip all phases before the specified one")
flags.BoolVar(&f.DryRun, "dry-run", false, "simulate phase execution") flags.BoolVar(&f.DryRun, "dry-run", false, "simulate phase execution")
flags.DurationVar(&f.Timeout, "wait-timeout", 0, "wait timeout") flags.DurationVar(&f.Timeout, "wait-timeout", 0, "wait timeout")
return runCmd return runCmd

View File

@ -16,4 +16,5 @@ Perform a dry run of a plan
Flags: Flags:
--dry-run simulate phase execution --dry-run simulate phase execution
-h, --help help for run -h, --help help for run
--resume-from string skip all phases before the specified one
--wait-timeout duration wait timeout --wait-timeout duration wait timeout

View File

@ -37,6 +37,7 @@ Options
--dry-run simulate phase execution --dry-run simulate phase execution
-h, --help help for run -h, --help help for run
--resume-from string skip all phases before the specified one
--wait-timeout duration wait timeout --wait-timeout duration wait timeout
Options inherited from parent commands Options inherited from parent commands

View File

@ -270,7 +270,16 @@ func (p *plan) Validate() error {
} }
// Run function executes Run method for each phase // Run function executes Run method for each phase
func (p *plan) Run(ro ifc.RunOptions) error { func (p *plan) Run(ro ifc.PlanRunOptions) error {
if ro.ResumeFromPhase != "" {
for i, phase := range p.apiObj.Phases {
if phase.Name == ro.ResumeFromPhase {
p.apiObj.Phases = p.apiObj.Phases[i:]
break
}
}
}
for _, step := range p.apiObj.Phases { for _, step := range p.apiObj.Phases {
phaseRunner, err := p.phaseClient.PhaseByID(ifc.ID{Name: step.Name}) phaseRunner, err := p.phaseClient.PhaseByID(ifc.ID{Name: step.Name})
if err != nil { if err != nil {
@ -278,7 +287,7 @@ func (p *plan) Run(ro ifc.RunOptions) error {
} }
log.Printf("executing phase: %s\n", step.Name) log.Printf("executing phase: %s\n", step.Name)
if err = phaseRunner.Run(ro); err != nil { if err = phaseRunner.Run(ro.RunOptions); err != nil {
return err return err
} }
} }

View File

@ -436,7 +436,7 @@ func TestPlanRun(t *testing.T) {
require.NotNil(t, client) require.NotNil(t, client)
p, err := client.PlanByID(tt.planID) p, err := client.PlanByID(tt.planID)
require.NoError(t, err) require.NoError(t, err)
err = p.Run(ifc.RunOptions{DryRun: true}) err = p.Run(ifc.PlanRunOptions{RunOptions: ifc.RunOptions{DryRun: true}})
if tt.errContains != "" { if tt.errContains != "" {
require.Error(t, err) require.Error(t, err)
assert.Contains(t, err.Error(), tt.errContains) assert.Contains(t, err.Error(), tt.errContains)

View File

@ -202,12 +202,13 @@ func (c *PlanListCommand) RunE() error {
// PlanRunFlags options for phase run command // PlanRunFlags options for phase run command
type PlanRunFlags struct { type PlanRunFlags struct {
GenericRunFlags GenericRunFlags
ResumeFromPhase string
} }
// PlanRunCommand phase run command // PlanRunCommand phase run command
type PlanRunCommand struct { type PlanRunCommand struct {
PlanID ifc.ID PlanID ifc.ID
Options ifc.RunOptions Options ifc.PlanRunOptions
Factory config.Factory Factory config.Factory
} }

View File

@ -482,8 +482,10 @@ func TestPlanRunCommand(t *testing.T) {
tt := tc tt := tc
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
cmd := phase.PlanRunCommand{ cmd := phase.PlanRunCommand{
Options: ifc.RunOptions{ Options: ifc.PlanRunOptions{
DryRun: true, RunOptions: ifc.RunOptions{
DryRun: true,
},
}, },
Factory: tt.factory, Factory: tt.factory,
PlanID: tt.planID, PlanID: tt.planID,

View File

@ -43,6 +43,12 @@ type RunOptions struct {
Timeout *time.Duration Timeout *time.Duration
} }
// PlanRunOptions holds options for plan run method
type PlanRunOptions struct {
RunOptions
ResumeFromPhase string
}
// RenderOptions holds options for render method // RenderOptions holds options for render method
type RenderOptions struct { type RenderOptions struct {
FilterSelector document.Selector FilterSelector document.Selector

View File

@ -40,7 +40,7 @@ type PhaseStatus struct {
// Plan provides a way to interact with phase plans // Plan provides a way to interact with phase plans
type Plan interface { type Plan interface {
Validate() error Validate() error
Run(RunOptions) error Run(PlanRunOptions) error
Status(StatusOptions) (PlanStatus, error) Status(StatusOptions) (PlanStatus, error)
} }