github.com/emate/packer@v0.8.1-0.20150625195101-fe0fde195dc6/provisioner/windows-restart/provisioner_test.go (about) 1 package restart 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "testing" 8 "time" 9 10 "github.com/mitchellh/packer/packer" 11 ) 12 13 func testConfig() map[string]interface{} { 14 return map[string]interface{}{} 15 } 16 17 func TestProvisioner_Impl(t *testing.T) { 18 var raw interface{} 19 raw = &Provisioner{} 20 if _, ok := raw.(packer.Provisioner); !ok { 21 t.Fatalf("must be a Provisioner") 22 } 23 } 24 25 func TestProvisionerPrepare_Defaults(t *testing.T) { 26 var p Provisioner 27 config := testConfig() 28 29 err := p.Prepare(config) 30 if err != nil { 31 t.Fatalf("err: %s", err) 32 } 33 34 if p.config.RestartTimeout != 5*time.Minute { 35 t.Errorf("unexpected remote path: %s", p.config.RestartTimeout) 36 } 37 38 if p.config.RestartCommand != "powershell \"& {Restart-Computer -force }\"" { 39 t.Errorf("unexpected remote path: %s", p.config.RestartCommand) 40 } 41 } 42 43 func TestProvisionerPrepare_ConfigRetryTimeout(t *testing.T) { 44 var p Provisioner 45 config := testConfig() 46 config["restart_timeout"] = "1m" 47 48 err := p.Prepare(config) 49 if err != nil { 50 t.Fatalf("err: %s", err) 51 } 52 53 if p.config.RestartTimeout != 1*time.Minute { 54 t.Errorf("unexpected remote path: %s", p.config.RestartTimeout) 55 } 56 } 57 58 func TestProvisionerPrepare_ConfigErrors(t *testing.T) { 59 var p Provisioner 60 config := testConfig() 61 config["restart_timeout"] = "m" 62 63 err := p.Prepare(config) 64 if err == nil { 65 t.Fatal("Expected error parsing restart_timeout but did not receive one.") 66 } 67 } 68 69 func TestProvisionerPrepare_InvalidKey(t *testing.T) { 70 var p Provisioner 71 config := testConfig() 72 73 // Add a random key 74 config["i_should_not_be_valid"] = true 75 err := p.Prepare(config) 76 if err == nil { 77 t.Fatal("should have error") 78 } 79 } 80 81 func testUi() *packer.BasicUi { 82 return &packer.BasicUi{ 83 Reader: new(bytes.Buffer), 84 Writer: new(bytes.Buffer), 85 ErrorWriter: new(bytes.Buffer), 86 } 87 } 88 89 func TestProvisionerProvision_Success(t *testing.T) { 90 config := testConfig() 91 92 // Defaults provided by Packer 93 ui := testUi() 94 p := new(Provisioner) 95 96 // Defaults provided by Packer 97 comm := new(packer.MockCommunicator) 98 p.Prepare(config) 99 waitForCommunicatorOld := waitForCommunicator 100 waitForCommunicator = func(p *Provisioner) error { 101 return nil 102 } 103 err := p.Provision(ui, comm) 104 if err != nil { 105 t.Fatal("should not have error") 106 } 107 108 expectedCommand := DefaultRestartCommand 109 110 // Should run the command without alteration 111 if comm.StartCmd.Command != expectedCommand { 112 t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) 113 } 114 // Set this back! 115 waitForCommunicator = waitForCommunicatorOld 116 } 117 118 func TestProvisionerProvision_CustomCommand(t *testing.T) { 119 config := testConfig() 120 121 // Defaults provided by Packer 122 ui := testUi() 123 p := new(Provisioner) 124 expectedCommand := "specialrestart.exe -NOW" 125 config["restart_command"] = expectedCommand 126 127 // Defaults provided by Packer 128 comm := new(packer.MockCommunicator) 129 p.Prepare(config) 130 waitForCommunicatorOld := waitForCommunicator 131 waitForCommunicator = func(p *Provisioner) error { 132 return nil 133 } 134 err := p.Provision(ui, comm) 135 if err != nil { 136 t.Fatal("should not have error") 137 } 138 139 // Should run the command without alteration 140 if comm.StartCmd.Command != expectedCommand { 141 t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) 142 } 143 // Set this back! 144 waitForCommunicator = waitForCommunicatorOld 145 } 146 147 func TestProvisionerProvision_RestartCommandFail(t *testing.T) { 148 config := testConfig() 149 ui := testUi() 150 p := new(Provisioner) 151 comm := new(packer.MockCommunicator) 152 comm.StartStderr = "WinRM terminated" 153 comm.StartExitStatus = 1 154 155 p.Prepare(config) 156 err := p.Provision(ui, comm) 157 if err == nil { 158 t.Fatal("should have error") 159 } 160 } 161 func TestProvisionerProvision_WaitForRestartFail(t *testing.T) { 162 config := testConfig() 163 164 // Defaults provided by Packer 165 ui := testUi() 166 p := new(Provisioner) 167 168 // Defaults provided by Packer 169 comm := new(packer.MockCommunicator) 170 p.Prepare(config) 171 waitForCommunicatorOld := waitForCommunicator 172 waitForCommunicator = func(p *Provisioner) error { 173 return fmt.Errorf("Machine did not restart properly") 174 } 175 err := p.Provision(ui, comm) 176 if err == nil { 177 t.Fatal("should have error") 178 } 179 180 // Set this back! 181 waitForCommunicator = waitForCommunicatorOld 182 } 183 184 func TestProvision_waitForRestartTimeout(t *testing.T) { 185 retryableSleep = 10 * time.Millisecond 186 config := testConfig() 187 config["restart_timeout"] = "1ms" 188 ui := testUi() 189 p := new(Provisioner) 190 comm := new(packer.MockCommunicator) 191 var err error 192 193 p.Prepare(config) 194 waitForCommunicatorOld := waitForCommunicator 195 waitDone := make(chan bool) 196 197 // Block until cancel comes through 198 waitForCommunicator = func(p *Provisioner) error { 199 for { 200 select { 201 case <-waitDone: 202 } 203 } 204 } 205 206 go func() { 207 err = p.Provision(ui, comm) 208 waitDone <- true 209 }() 210 <-waitDone 211 212 if err == nil { 213 t.Fatal("should not have error") 214 } 215 216 // Set this back! 217 waitForCommunicator = waitForCommunicatorOld 218 219 } 220 221 func TestProvision_waitForCommunicator(t *testing.T) { 222 config := testConfig() 223 224 // Defaults provided by Packer 225 ui := testUi() 226 p := new(Provisioner) 227 228 // Defaults provided by Packer 229 comm := new(packer.MockCommunicator) 230 p.comm = comm 231 p.ui = ui 232 comm.StartStderr = "WinRM terminated" 233 comm.StartExitStatus = 1 234 p.Prepare(config) 235 err := waitForCommunicator(p) 236 237 if err != nil { 238 t.Fatalf("should not have error, got: %s", err.Error()) 239 } 240 241 expectedCommand := DefaultRestartCheckCommand 242 243 // Should run the command without alteration 244 if comm.StartCmd.Command != expectedCommand { 245 t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) 246 } 247 } 248 249 func TestProvision_waitForCommunicatorWithCancel(t *testing.T) { 250 config := testConfig() 251 252 // Defaults provided by Packer 253 ui := testUi() 254 p := new(Provisioner) 255 256 // Defaults provided by Packer 257 comm := new(packer.MockCommunicator) 258 p.comm = comm 259 p.ui = ui 260 retryableSleep = 5 * time.Second 261 p.cancel = make(chan struct{}) 262 var err error 263 264 comm.StartStderr = "WinRM terminated" 265 comm.StartExitStatus = 1 // Always fail 266 p.Prepare(config) 267 268 // Run 2 goroutines; 269 // 1st to call waitForCommunicator (that will always fail) 270 // 2nd to cancel the operation 271 waitDone := make(chan bool) 272 go func() { 273 err = waitForCommunicator(p) 274 }() 275 276 go func() { 277 time.Sleep(10 * time.Millisecond) 278 p.Cancel() 279 waitDone <- true 280 }() 281 <-waitDone 282 283 // Expect a Cancel error 284 if err == nil { 285 t.Fatalf("Should have err") 286 } 287 } 288 289 func TestRetryable(t *testing.T) { 290 config := testConfig() 291 292 count := 0 293 retryMe := func() error { 294 t.Logf("RetryMe, attempt number %d", count) 295 if count == 2 { 296 return nil 297 } 298 count++ 299 return errors.New(fmt.Sprintf("Still waiting %d more times...", 2-count)) 300 } 301 retryableSleep = 50 * time.Millisecond 302 p := new(Provisioner) 303 p.config.RestartTimeout = 155 * time.Millisecond 304 err := p.Prepare(config) 305 err = p.retryable(retryMe) 306 if err != nil { 307 t.Fatalf("should not have error retrying funuction") 308 } 309 310 count = 0 311 p.config.RestartTimeout = 10 * time.Millisecond 312 err = p.Prepare(config) 313 err = p.retryable(retryMe) 314 if err == nil { 315 t.Fatalf("should have error retrying funuction") 316 } 317 } 318 319 func TestProvision_Cancel(t *testing.T) { 320 config := testConfig() 321 322 // Defaults provided by Packer 323 ui := testUi() 324 p := new(Provisioner) 325 326 var err error 327 328 comm := new(packer.MockCommunicator) 329 p.Prepare(config) 330 waitDone := make(chan bool) 331 332 // Block until cancel comes through 333 waitForCommunicator = func(p *Provisioner) error { 334 for { 335 select { 336 case <-waitDone: 337 } 338 } 339 } 340 341 // Create two go routines to provision and cancel in parallel 342 // Provision will block until cancel happens 343 go func() { 344 err = p.Provision(ui, comm) 345 waitDone <- true 346 }() 347 348 go func() { 349 p.Cancel() 350 }() 351 <-waitDone 352 353 // Expect interupt error 354 if err == nil { 355 t.Fatal("should have error") 356 } 357 }