github.com/dacamp/packer@v0.10.2/provisioner/powershell/provisioner_test.go (about) 1 package powershell 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 //"log" 9 "os" 10 "regexp" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/mitchellh/packer/packer" 16 ) 17 18 func testConfig() map[string]interface{} { 19 return map[string]interface{}{ 20 "inline": []interface{}{"foo", "bar"}, 21 } 22 } 23 24 func init() { 25 //log.SetOutput(ioutil.Discard) 26 } 27 28 func TestProvisionerPrepare_extractScript(t *testing.T) { 29 config := testConfig() 30 p := new(Provisioner) 31 _ = p.Prepare(config) 32 file, err := extractScript(p) 33 if err != nil { 34 t.Fatalf("Should not be error: %s", err) 35 } 36 t.Logf("File: %s", file) 37 if strings.Index(file, os.TempDir()) != 0 { 38 t.Fatalf("Temp file should reside in %s. File location: %s", os.TempDir(), file) 39 } 40 41 // File contents should contain 2 lines concatenated by newlines: foo\nbar 42 readFile, err := ioutil.ReadFile(file) 43 expectedContents := "foo\nbar\n" 44 s := string(readFile[:]) 45 if s != expectedContents { 46 t.Fatalf("Expected generated inlineScript to equal '%s', got '%s'", expectedContents, s) 47 } 48 } 49 50 func TestProvisioner_Impl(t *testing.T) { 51 var raw interface{} 52 raw = &Provisioner{} 53 if _, ok := raw.(packer.Provisioner); !ok { 54 t.Fatalf("must be a Provisioner") 55 } 56 } 57 58 func TestProvisionerPrepare_Defaults(t *testing.T) { 59 var p Provisioner 60 config := testConfig() 61 62 err := p.Prepare(config) 63 if err != nil { 64 t.Fatalf("err: %s", err) 65 } 66 67 if p.config.RemotePath != DefaultRemotePath { 68 t.Errorf("unexpected remote path: %s", p.config.RemotePath) 69 } 70 71 if p.config.ElevatedUser != "" { 72 t.Error("expected elevated_user to be empty") 73 } 74 if p.config.ElevatedPassword != "" { 75 t.Error("expected elevated_password to be empty") 76 } 77 78 if p.config.ExecuteCommand != "powershell \"& { {{.Vars}}{{.Path}}; exit $LastExitCode}\"" { 79 t.Fatalf("Default command should be powershell \"& { {{.Vars}}{{.Path}}; exit $LastExitCode}\", but got %s", p.config.ExecuteCommand) 80 } 81 82 if p.config.ElevatedExecuteCommand != "{{.Vars}}{{.Path}}" { 83 t.Fatalf("Default command should be powershell {{.Vars}}{{.Path}}, but got %s", p.config.ElevatedExecuteCommand) 84 } 85 86 if p.config.ValidExitCodes == nil { 87 t.Fatalf("ValidExitCodes should not be nil") 88 } 89 if p.config.ValidExitCodes != nil { 90 expCodes := []int{0} 91 for i, v := range p.config.ValidExitCodes { 92 if v != expCodes[i] { 93 t.Fatalf("Expected ValidExitCodes don't match actual") 94 } 95 } 96 } 97 98 if p.config.ElevatedEnvVarFormat != `$env:%s="%s"; ` { 99 t.Fatalf("Default command should be powershell \"{{.Vars}}{{.Path}}\", but got %s", p.config.ElevatedEnvVarFormat) 100 } 101 } 102 103 func TestProvisionerPrepare_Config(t *testing.T) { 104 config := testConfig() 105 config["elevated_user"] = "{{user `user`}}" 106 config["elevated_password"] = "{{user `password`}}" 107 config[packer.UserVariablesConfigKey] = map[string]string{ 108 "user": "myusername", 109 "password": "mypassword", 110 } 111 112 var p Provisioner 113 err := p.Prepare(config) 114 if err != nil { 115 t.Fatalf("err: %s", err) 116 } 117 118 if p.config.ElevatedUser != "myusername" { 119 t.Fatalf("Expected 'myusername' for key `elevated_user`: %s", p.config.ElevatedUser) 120 } 121 if p.config.ElevatedPassword != "mypassword" { 122 t.Fatalf("Expected 'mypassword' for key `elevated_password`: %s", p.config.ElevatedPassword) 123 } 124 125 } 126 127 func TestProvisionerPrepare_InvalidKey(t *testing.T) { 128 var p Provisioner 129 config := testConfig() 130 131 // Add a random key 132 config["i_should_not_be_valid"] = true 133 err := p.Prepare(config) 134 if err == nil { 135 t.Fatal("should have error") 136 } 137 } 138 139 func TestProvisionerPrepare_Elevated(t *testing.T) { 140 var p Provisioner 141 config := testConfig() 142 143 // Add a random key 144 config["elevated_user"] = "vagrant" 145 err := p.Prepare(config) 146 147 if err == nil { 148 t.Fatal("should have error (only provided elevated_user)") 149 } 150 151 config["elevated_password"] = "vagrant" 152 err = p.Prepare(config) 153 154 if err != nil { 155 t.Fatal("should not have error") 156 } 157 } 158 159 func TestProvisionerPrepare_Script(t *testing.T) { 160 config := testConfig() 161 delete(config, "inline") 162 163 config["script"] = "/this/should/not/exist" 164 p := new(Provisioner) 165 err := p.Prepare(config) 166 if err == nil { 167 t.Fatal("should have error") 168 } 169 170 // Test with a good one 171 tf, err := ioutil.TempFile("", "packer") 172 if err != nil { 173 t.Fatalf("error tempfile: %s", err) 174 } 175 defer os.Remove(tf.Name()) 176 177 config["script"] = tf.Name() 178 p = new(Provisioner) 179 err = p.Prepare(config) 180 if err != nil { 181 t.Fatalf("should not have error: %s", err) 182 } 183 } 184 185 func TestProvisionerPrepare_ScriptAndInline(t *testing.T) { 186 var p Provisioner 187 config := testConfig() 188 189 delete(config, "inline") 190 delete(config, "script") 191 err := p.Prepare(config) 192 if err == nil { 193 t.Fatal("should have error") 194 } 195 196 // Test with both 197 tf, err := ioutil.TempFile("", "packer") 198 if err != nil { 199 t.Fatalf("error tempfile: %s", err) 200 } 201 defer os.Remove(tf.Name()) 202 203 config["inline"] = []interface{}{"foo"} 204 config["script"] = tf.Name() 205 err = p.Prepare(config) 206 if err == nil { 207 t.Fatal("should have error") 208 } 209 } 210 211 func TestProvisionerPrepare_ScriptAndScripts(t *testing.T) { 212 var p Provisioner 213 config := testConfig() 214 215 // Test with both 216 tf, err := ioutil.TempFile("", "packer") 217 if err != nil { 218 t.Fatalf("error tempfile: %s", err) 219 } 220 defer os.Remove(tf.Name()) 221 222 config["inline"] = []interface{}{"foo"} 223 config["scripts"] = []string{tf.Name()} 224 err = p.Prepare(config) 225 if err == nil { 226 t.Fatal("should have error") 227 } 228 } 229 230 func TestProvisionerPrepare_Scripts(t *testing.T) { 231 config := testConfig() 232 delete(config, "inline") 233 234 config["scripts"] = []string{} 235 p := new(Provisioner) 236 err := p.Prepare(config) 237 if err == nil { 238 t.Fatal("should have error") 239 } 240 241 // Test with a good one 242 tf, err := ioutil.TempFile("", "packer") 243 if err != nil { 244 t.Fatalf("error tempfile: %s", err) 245 } 246 defer os.Remove(tf.Name()) 247 248 config["scripts"] = []string{tf.Name()} 249 p = new(Provisioner) 250 err = p.Prepare(config) 251 if err != nil { 252 t.Fatalf("should not have error: %s", err) 253 } 254 } 255 256 func TestProvisionerPrepare_EnvironmentVars(t *testing.T) { 257 config := testConfig() 258 259 // Test with a bad case 260 config["environment_vars"] = []string{"badvar", "good=var"} 261 p := new(Provisioner) 262 err := p.Prepare(config) 263 if err == nil { 264 t.Fatal("should have error") 265 } 266 267 // Test with a trickier case 268 config["environment_vars"] = []string{"=bad"} 269 p = new(Provisioner) 270 err = p.Prepare(config) 271 if err == nil { 272 t.Fatal("should have error") 273 } 274 275 // Test with a good case 276 // Note: baz= is a real env variable, just empty 277 config["environment_vars"] = []string{"FOO=bar", "baz="} 278 p = new(Provisioner) 279 err = p.Prepare(config) 280 if err != nil { 281 t.Fatalf("should not have error: %s", err) 282 } 283 } 284 285 func TestProvisionerQuote_EnvironmentVars(t *testing.T) { 286 config := testConfig() 287 288 config["environment_vars"] = []string{"keyone=valueone", "keytwo=value\ntwo", "keythree='valuethree'", "keyfour='value\nfour'"} 289 p := new(Provisioner) 290 p.Prepare(config) 291 292 expectedValue := "keyone=valueone" 293 if p.config.Vars[0] != expectedValue { 294 t.Fatalf("%s should be equal to %s", p.config.Vars[0], expectedValue) 295 } 296 297 expectedValue = "keytwo=value\ntwo" 298 if p.config.Vars[1] != expectedValue { 299 t.Fatalf("%s should be equal to %s", p.config.Vars[1], expectedValue) 300 } 301 302 expectedValue = "keythree='valuethree'" 303 if p.config.Vars[2] != expectedValue { 304 t.Fatalf("%s should be equal to %s", p.config.Vars[2], expectedValue) 305 } 306 307 expectedValue = "keyfour='value\nfour'" 308 if p.config.Vars[3] != expectedValue { 309 t.Fatalf("%s should be equal to %s", p.config.Vars[3], expectedValue) 310 } 311 } 312 313 func testUi() *packer.BasicUi { 314 return &packer.BasicUi{ 315 Reader: new(bytes.Buffer), 316 Writer: new(bytes.Buffer), 317 ErrorWriter: new(bytes.Buffer), 318 } 319 } 320 321 func testObjects() (packer.Ui, packer.Communicator) { 322 ui := testUi() 323 return ui, new(packer.MockCommunicator) 324 } 325 326 func TestProvisionerProvision_ValidExitCodes(t *testing.T) { 327 config := testConfig() 328 delete(config, "inline") 329 330 // Defaults provided by Packer 331 config["remote_path"] = "c:/Windows/Temp/inlineScript.bat" 332 config["inline"] = []string{"whoami"} 333 ui := testUi() 334 p := new(Provisioner) 335 336 // Defaults provided by Packer 337 p.config.PackerBuildName = "vmware" 338 p.config.PackerBuilderType = "iso" 339 p.config.ValidExitCodes = []int{0, 200} 340 comm := new(packer.MockCommunicator) 341 comm.StartExitStatus = 200 342 p.Prepare(config) 343 err := p.Provision(ui, comm) 344 if err != nil { 345 t.Fatal("should not have error") 346 } 347 } 348 349 func TestProvisionerProvision_InvalidExitCodes(t *testing.T) { 350 config := testConfig() 351 delete(config, "inline") 352 353 // Defaults provided by Packer 354 config["remote_path"] = "c:/Windows/Temp/inlineScript.bat" 355 config["inline"] = []string{"whoami"} 356 ui := testUi() 357 p := new(Provisioner) 358 359 // Defaults provided by Packer 360 p.config.PackerBuildName = "vmware" 361 p.config.PackerBuilderType = "iso" 362 p.config.ValidExitCodes = []int{0, 200} 363 comm := new(packer.MockCommunicator) 364 comm.StartExitStatus = 201 // Invalid! 365 p.Prepare(config) 366 err := p.Provision(ui, comm) 367 if err == nil { 368 t.Fatal("should have error") 369 } 370 } 371 372 func TestProvisionerProvision_Inline(t *testing.T) { 373 config := testConfig() 374 delete(config, "inline") 375 376 // Defaults provided by Packer 377 config["remote_path"] = "c:/Windows/Temp/inlineScript.bat" 378 config["inline"] = []string{"whoami"} 379 ui := testUi() 380 p := new(Provisioner) 381 382 // Defaults provided by Packer 383 p.config.PackerBuildName = "vmware" 384 p.config.PackerBuilderType = "iso" 385 comm := new(packer.MockCommunicator) 386 p.Prepare(config) 387 err := p.Provision(ui, comm) 388 if err != nil { 389 t.Fatal("should not have error") 390 } 391 392 expectedCommand := `powershell "& { $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; c:/Windows/Temp/inlineScript.bat; exit $LastExitCode}"` 393 394 // Should run the command without alteration 395 if comm.StartCmd.Command != expectedCommand { 396 t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) 397 } 398 399 envVars := make([]string, 2) 400 envVars[0] = "FOO=BAR" 401 envVars[1] = "BAR=BAZ" 402 config["environment_vars"] = envVars 403 config["remote_path"] = "c:/Windows/Temp/inlineScript.bat" 404 405 p.Prepare(config) 406 err = p.Provision(ui, comm) 407 if err != nil { 408 t.Fatal("should not have error") 409 } 410 411 expectedCommand = `powershell "& { $env:BAR=\"BAZ\"; $env:FOO=\"BAR\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; c:/Windows/Temp/inlineScript.bat; exit $LastExitCode}"` 412 413 // Should run the command without alteration 414 if comm.StartCmd.Command != expectedCommand { 415 t.Fatalf("Expect command to be: %s, got: %s", expectedCommand, comm.StartCmd.Command) 416 } 417 } 418 419 func TestProvisionerProvision_Scripts(t *testing.T) { 420 tempFile, _ := ioutil.TempFile("", "packer") 421 defer os.Remove(tempFile.Name()) 422 config := testConfig() 423 delete(config, "inline") 424 config["scripts"] = []string{tempFile.Name()} 425 config["packer_build_name"] = "foobuild" 426 config["packer_builder_type"] = "footype" 427 ui := testUi() 428 429 p := new(Provisioner) 430 comm := new(packer.MockCommunicator) 431 p.Prepare(config) 432 err := p.Provision(ui, comm) 433 if err != nil { 434 t.Fatal("should not have error") 435 } 436 437 //powershell -Command "$env:PACKER_BUILDER_TYPE=''"; powershell -Command "$env:PACKER_BUILD_NAME='foobuild'"; powershell -Command c:/Windows/Temp/script.ps1 438 expectedCommand := `powershell "& { $env:PACKER_BUILDER_TYPE=\"footype\"; $env:PACKER_BUILD_NAME=\"foobuild\"; c:/Windows/Temp/script.ps1; exit $LastExitCode}"` 439 440 // Should run the command without alteration 441 if comm.StartCmd.Command != expectedCommand { 442 t.Fatalf("Expect command to be %s NOT %s", expectedCommand, comm.StartCmd.Command) 443 } 444 } 445 446 func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { 447 tempFile, _ := ioutil.TempFile("", "packer") 448 config := testConfig() 449 ui := testUi() 450 defer os.Remove(tempFile.Name()) 451 delete(config, "inline") 452 453 config["scripts"] = []string{tempFile.Name()} 454 config["packer_build_name"] = "foobuild" 455 config["packer_builder_type"] = "footype" 456 457 // Env vars - currently should not effect them 458 envVars := make([]string, 2) 459 envVars[0] = "FOO=BAR" 460 envVars[1] = "BAR=BAZ" 461 config["environment_vars"] = envVars 462 463 p := new(Provisioner) 464 comm := new(packer.MockCommunicator) 465 p.Prepare(config) 466 err := p.Provision(ui, comm) 467 if err != nil { 468 t.Fatal("should not have error") 469 } 470 471 expectedCommand := `powershell "& { $env:BAR=\"BAZ\"; $env:FOO=\"BAR\"; $env:PACKER_BUILDER_TYPE=\"footype\"; $env:PACKER_BUILD_NAME=\"foobuild\"; c:/Windows/Temp/script.ps1; exit $LastExitCode}"` 472 473 // Should run the command without alteration 474 if comm.StartCmd.Command != expectedCommand { 475 t.Fatalf("Expect command to be %s NOT %s", expectedCommand, comm.StartCmd.Command) 476 } 477 } 478 479 func TestProvisionerProvision_UISlurp(t *testing.T) { 480 // UI should be called n times 481 482 // UI should receive following messages / output 483 } 484 485 func TestProvisioner_createFlattenedElevatedEnvVars_windows(t *testing.T) { 486 config := testConfig() 487 488 p := new(Provisioner) 489 err := p.Prepare(config) 490 if err != nil { 491 t.Fatalf("should not have error preparing config: %s", err) 492 } 493 494 // Defaults provided by Packer 495 p.config.PackerBuildName = "vmware" 496 p.config.PackerBuilderType = "iso" 497 498 // no user env var 499 flattenedEnvVars, err := p.createFlattenedEnvVars(true) 500 if err != nil { 501 t.Fatalf("should not have error creating flattened env vars: %s", err) 502 } 503 if flattenedEnvVars != "$env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; " { 504 t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars) 505 } 506 507 // single user env var 508 p.config.Vars = []string{"FOO=bar"} 509 510 flattenedEnvVars, err = p.createFlattenedEnvVars(true) 511 if err != nil { 512 t.Fatalf("should not have error creating flattened env vars: %s", err) 513 } 514 if flattenedEnvVars != "$env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; " { 515 t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars) 516 } 517 518 // multiple user env vars 519 p.config.Vars = []string{"FOO=bar", "BAZ=qux"} 520 521 flattenedEnvVars, err = p.createFlattenedEnvVars(true) 522 if err != nil { 523 t.Fatalf("should not have error creating flattened env vars: %s", err) 524 } 525 if flattenedEnvVars != "$env:BAZ=\"qux\"; $env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; " { 526 t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars) 527 } 528 } 529 530 func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) { 531 config := testConfig() 532 533 p := new(Provisioner) 534 err := p.Prepare(config) 535 if err != nil { 536 t.Fatalf("should not have error preparing config: %s", err) 537 } 538 539 // Defaults provided by Packer 540 p.config.PackerBuildName = "vmware" 541 p.config.PackerBuilderType = "iso" 542 543 // no user env var 544 flattenedEnvVars, err := p.createFlattenedEnvVars(false) 545 if err != nil { 546 t.Fatalf("should not have error creating flattened env vars: %s", err) 547 } 548 if flattenedEnvVars != "$env:PACKER_BUILDER_TYPE=\\\"iso\\\"; $env:PACKER_BUILD_NAME=\\\"vmware\\\"; " { 549 t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars) 550 } 551 552 // single user env var 553 p.config.Vars = []string{"FOO=bar"} 554 555 flattenedEnvVars, err = p.createFlattenedEnvVars(false) 556 if err != nil { 557 t.Fatalf("should not have error creating flattened env vars: %s", err) 558 } 559 if flattenedEnvVars != "$env:FOO=\\\"bar\\\"; $env:PACKER_BUILDER_TYPE=\\\"iso\\\"; $env:PACKER_BUILD_NAME=\\\"vmware\\\"; " { 560 t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars) 561 } 562 563 // multiple user env vars 564 p.config.Vars = []string{"FOO=bar", "BAZ=qux"} 565 566 flattenedEnvVars, err = p.createFlattenedEnvVars(false) 567 if err != nil { 568 t.Fatalf("should not have error creating flattened env vars: %s", err) 569 } 570 if flattenedEnvVars != "$env:BAZ=\\\"qux\\\"; $env:FOO=\\\"bar\\\"; $env:PACKER_BUILDER_TYPE=\\\"iso\\\"; $env:PACKER_BUILD_NAME=\\\"vmware\\\"; " { 571 t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars) 572 } 573 } 574 575 func TestProvision_createCommandText(t *testing.T) { 576 577 config := testConfig() 578 p := new(Provisioner) 579 comm := new(packer.MockCommunicator) 580 p.communicator = comm 581 _ = p.Prepare(config) 582 583 // Non-elevated 584 cmd, _ := p.createCommandText() 585 if cmd != "powershell \"& { $env:PACKER_BUILDER_TYPE=\\\"\\\"; $env:PACKER_BUILD_NAME=\\\"\\\"; c:/Windows/Temp/script.ps1; exit $LastExitCode}\"" { 586 t.Fatalf("Got unexpected non-elevated command: %s", cmd) 587 } 588 589 // Elevated 590 p.config.ElevatedUser = "vagrant" 591 p.config.ElevatedPassword = "vagrant" 592 cmd, _ = p.createCommandText() 593 matched, _ := regexp.MatchString("powershell -executionpolicy bypass -file \"%TEMP%(.{1})packer-elevated-shell.*", cmd) 594 if !matched { 595 t.Fatalf("Got unexpected elevated command: %s", cmd) 596 } 597 } 598 599 func TestProvision_generateElevatedShellRunner(t *testing.T) { 600 601 // Non-elevated 602 config := testConfig() 603 p := new(Provisioner) 604 p.Prepare(config) 605 comm := new(packer.MockCommunicator) 606 p.communicator = comm 607 path, err := p.generateElevatedRunner("whoami") 608 609 if err != nil { 610 t.Fatalf("Did not expect error: %s", err.Error()) 611 } 612 613 if comm.UploadCalled != true { 614 t.Fatalf("Should have uploaded file") 615 } 616 617 matched, _ := regexp.MatchString("%TEMP%(.{1})packer-elevated-shell.*", path) 618 if !matched { 619 t.Fatalf("Got unexpected file: %s", path) 620 } 621 } 622 623 func TestRetryable(t *testing.T) { 624 config := testConfig() 625 626 count := 0 627 retryMe := func() error { 628 t.Logf("RetryMe, attempt number %d", count) 629 if count == 2 { 630 return nil 631 } 632 count++ 633 return errors.New(fmt.Sprintf("Still waiting %d more times...", 2-count)) 634 } 635 retryableSleep = 50 * time.Millisecond 636 p := new(Provisioner) 637 p.config.StartRetryTimeout = 155 * time.Millisecond 638 err := p.Prepare(config) 639 err = p.retryable(retryMe) 640 if err != nil { 641 t.Fatalf("should not have error retrying funuction") 642 } 643 644 count = 0 645 p.config.StartRetryTimeout = 10 * time.Millisecond 646 err = p.Prepare(config) 647 err = p.retryable(retryMe) 648 if err == nil { 649 t.Fatalf("should have error retrying funuction") 650 } 651 } 652 653 func TestCancel(t *testing.T) { 654 // Don't actually call Cancel() as it performs an os.Exit(0) 655 // which kills the 'go test' tool 656 }