github.com/rsyabuta/packer@v1.1.4-0.20180119234903-5ef0c2280f0b/common/multistep_runner.go (about) 1 package common 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "reflect" 8 "strings" 9 "time" 10 11 "github.com/hashicorp/packer/packer" 12 "github.com/mitchellh/multistep" 13 ) 14 15 func newRunner(steps []multistep.Step, config PackerConfig, ui packer.Ui) (multistep.Runner, multistep.DebugPauseFn) { 16 switch config.PackerOnError { 17 case "", "cleanup": 18 case "abort": 19 for i, step := range steps { 20 steps[i] = abortStep{step, ui} 21 } 22 case "ask": 23 for i, step := range steps { 24 steps[i] = askStep{step, ui} 25 } 26 } 27 28 if config.PackerDebug { 29 pauseFn := MultistepDebugFn(ui) 30 return &multistep.DebugRunner{Steps: steps, PauseFn: pauseFn}, pauseFn 31 } else { 32 return &multistep.BasicRunner{Steps: steps}, nil 33 } 34 } 35 36 // NewRunner returns a multistep.Runner that runs steps augmented with support 37 // for -debug and -on-error command line arguments. 38 func NewRunner(steps []multistep.Step, config PackerConfig, ui packer.Ui) multistep.Runner { 39 runner, _ := newRunner(steps, config, ui) 40 return runner 41 } 42 43 // NewRunnerWithPauseFn returns a multistep.Runner that runs steps augmented 44 // with support for -debug and -on-error command line arguments. With -debug it 45 // puts the multistep.DebugPauseFn that will pause execution between steps into 46 // the state under the key "pauseFn". 47 func NewRunnerWithPauseFn(steps []multistep.Step, config PackerConfig, ui packer.Ui, state multistep.StateBag) multistep.Runner { 48 runner, pauseFn := newRunner(steps, config, ui) 49 if pauseFn != nil { 50 state.Put("pauseFn", pauseFn) 51 } 52 return runner 53 } 54 55 func typeName(i interface{}) string { 56 return reflect.Indirect(reflect.ValueOf(i)).Type().Name() 57 } 58 59 type abortStep struct { 60 step multistep.Step 61 ui packer.Ui 62 } 63 64 func (s abortStep) InnerStepName() string { 65 return typeName(s.step) 66 } 67 68 func (s abortStep) Run(state multistep.StateBag) multistep.StepAction { 69 return s.step.Run(state) 70 } 71 72 func (s abortStep) Cleanup(state multistep.StateBag) { 73 if _, ok := state.GetOk(multistep.StateCancelled); ok { 74 s.ui.Error("Interrupted, aborting...") 75 os.Exit(1) 76 } 77 if _, ok := state.GetOk(multistep.StateHalted); ok { 78 s.ui.Error(fmt.Sprintf("Step %q failed, aborting...", typeName(s.step))) 79 os.Exit(1) 80 } 81 s.step.Cleanup(state) 82 } 83 84 type askStep struct { 85 step multistep.Step 86 ui packer.Ui 87 } 88 89 func (s askStep) InnerStepName() string { 90 return typeName(s.step) 91 } 92 93 func (s askStep) Run(state multistep.StateBag) (action multistep.StepAction) { 94 for { 95 action = s.step.Run(state) 96 97 if action != multistep.ActionHalt { 98 return 99 } 100 101 switch ask(s.ui, typeName(s.step), state) { 102 case askCleanup: 103 return 104 case askAbort: 105 os.Exit(1) 106 case askRetry: 107 continue 108 } 109 } 110 } 111 112 func (s askStep) Cleanup(state multistep.StateBag) { 113 s.step.Cleanup(state) 114 } 115 116 type askResponse int 117 118 const ( 119 askCleanup askResponse = iota 120 askAbort 121 askRetry 122 ) 123 124 func ask(ui packer.Ui, name string, state multistep.StateBag) askResponse { 125 ui.Say(fmt.Sprintf("Step %q failed", name)) 126 127 result := make(chan askResponse) 128 go func() { 129 result <- askPrompt(ui) 130 }() 131 132 for { 133 select { 134 case response := <-result: 135 return response 136 case <-time.After(100 * time.Millisecond): 137 if _, ok := state.GetOk(multistep.StateCancelled); ok { 138 return askCleanup 139 } 140 } 141 } 142 } 143 144 func askPrompt(ui packer.Ui) askResponse { 145 for { 146 line, err := ui.Ask("[c] Clean up and exit, [a] abort without cleanup, or [r] retry step (build may fail even if retry succeeds)?") 147 if err != nil { 148 log.Printf("Error asking for input: %s", err) 149 } 150 151 input := strings.ToLower(line) + "c" 152 switch input[0] { 153 case 'c': 154 return askCleanup 155 case 'a': 156 return askAbort 157 case 'r': 158 return askRetry 159 } 160 ui.Say(fmt.Sprintf("Incorrect input: %#v", line)) 161 } 162 }