provisioner/powershell
This commit is contained in:
parent
cf570a71dc
commit
040ff0706d
|
@ -0,0 +1,15 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/packer/packer/plugin"
|
||||
"github.com/mitchellh/packer/provisioner/powershell"
|
||||
)
|
||||
|
||||
func main() {
|
||||
server, err := plugin.Server()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
server.RegisterProvisioner(new(powershell.Provisioner))
|
||||
server.Serve()
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package powershell
|
||||
|
||||
import (
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type elevatedOptions struct {
|
||||
User string
|
||||
Password string
|
||||
TaskName string
|
||||
TaskDescription string
|
||||
EncodedCommand string
|
||||
}
|
||||
|
||||
var elevatedTemplate = template.Must(template.New("ElevatedCommand").Parse(`
|
||||
$name = "{{.TaskName}}"
|
||||
$log = "$env:TEMP\$name.out"
|
||||
$s = New-Object -ComObject "Schedule.Service"
|
||||
$s.Connect()
|
||||
$t = $s.NewTask($null)
|
||||
$t.XmlText = @'
|
||||
<?xml version="1.0" encoding="UTF-16"?>
|
||||
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
||||
<RegistrationInfo>
|
||||
<Description>{{.TaskDescription}}</Description>
|
||||
</RegistrationInfo>
|
||||
<Principals>
|
||||
<Principal id="Author">
|
||||
<UserId>{{.User}}</UserId>
|
||||
<LogonType>Password</LogonType>
|
||||
<RunLevel>HighestAvailable</RunLevel>
|
||||
</Principal>
|
||||
</Principals>
|
||||
<Settings>
|
||||
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
||||
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
||||
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
||||
<AllowHardTerminate>true</AllowHardTerminate>
|
||||
<StartWhenAvailable>false</StartWhenAvailable>
|
||||
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
||||
<IdleSettings>
|
||||
<StopOnIdleEnd>false</StopOnIdleEnd>
|
||||
<RestartOnIdle>false</RestartOnIdle>
|
||||
</IdleSettings>
|
||||
<AllowStartOnDemand>true</AllowStartOnDemand>
|
||||
<Enabled>true</Enabled>
|
||||
<Hidden>false</Hidden>
|
||||
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
||||
<WakeToRun>false</WakeToRun>
|
||||
<ExecutionTimeLimit>PT24H</ExecutionTimeLimit>
|
||||
<Priority>4</Priority>
|
||||
</Settings>
|
||||
<Actions Context="Author">
|
||||
<Exec>
|
||||
<Command>cmd</Command>
|
||||
<Arguments>/c powershell.exe -EncodedCommand {{.EncodedCommand}} > %TEMP%\{{.TaskName}}.out 2>&1</Arguments>
|
||||
</Exec>
|
||||
</Actions>
|
||||
</Task>
|
||||
'@
|
||||
$f = $s.GetFolder("\")
|
||||
$f.RegisterTaskDefinition($name, $t, 6, "{{.User}}", "{{.Password}}", 1, $null) | Out-Null
|
||||
$t = $f.GetTask("\$name")
|
||||
$t.Run($null) | Out-Null
|
||||
$timeout = 10
|
||||
$sec = 0
|
||||
while ((!($t.state -eq 4)) -and ($sec -lt $timeout)) {
|
||||
Start-Sleep -s 1
|
||||
$sec++
|
||||
}
|
||||
function SlurpOutput($l) {
|
||||
if (Test-Path $log) {
|
||||
Get-Content $log | select -skip $l | ForEach {
|
||||
$l += 1
|
||||
Write-Host "$_"
|
||||
}
|
||||
}
|
||||
return $l
|
||||
}
|
||||
$line = 0
|
||||
do {
|
||||
Start-Sleep -m 100
|
||||
$line = SlurpOutput $line
|
||||
} while (!($t.state -eq 3))
|
||||
$result = $t.LastTaskResult
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($s) | Out-Null
|
||||
exit $result`))
|
|
@ -0,0 +1,17 @@
|
|||
package powershell
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
func powershellEncode(buffer []byte) string {
|
||||
// 2 byte chars to make PowerShell happy
|
||||
wideCmd := ""
|
||||
for _, b := range buffer {
|
||||
wideCmd += string(b) + "\x00"
|
||||
}
|
||||
|
||||
// Base64 encode the command
|
||||
input := []uint8(wideCmd)
|
||||
return base64.StdEncoding.EncodeToString(input)
|
||||
}
|
|
@ -0,0 +1,459 @@
|
|||
// This package implements a provisioner for Packer that executes
|
||||
// shell scripts within the remote machine.
|
||||
package powershell
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/common/uuid"
|
||||
"github.com/mitchellh/packer/helper/config"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/template/interpolate"
|
||||
)
|
||||
|
||||
const DefaultRemotePath = "c:/Windows/Temp/script.ps1"
|
||||
|
||||
var retryableSleep = 2 * time.Second
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
// If true, the script contains binary and line endings will not be
|
||||
// converted from Windows to Unix-style.
|
||||
Binary bool
|
||||
|
||||
// An inline script to execute. Multiple strings are all executed
|
||||
// in the context of a single shell.
|
||||
Inline []string
|
||||
|
||||
// The local path of the shell script to upload and execute.
|
||||
Script string
|
||||
|
||||
// An array of multiple scripts to run.
|
||||
Scripts []string
|
||||
|
||||
// An array of environment variables that will be injected before
|
||||
// your command(s) are executed.
|
||||
Vars []string `mapstructure:"environment_vars"`
|
||||
|
||||
// The remote path where the local shell script will be uploaded to.
|
||||
// This should be set to a writable file that is in a pre-existing directory.
|
||||
RemotePath string `mapstructure:"remote_path"`
|
||||
|
||||
// The command used to execute the script. The '{{ .Path }}' variable
|
||||
// should be used to specify where the script goes, {{ .Vars }}
|
||||
// can be used to inject the environment_vars into the environment.
|
||||
ExecuteCommand string `mapstructure:"execute_command"`
|
||||
|
||||
// The command used to execute the elevated script. The '{{ .Path }}' variable
|
||||
// should be used to specify where the script goes, {{ .Vars }}
|
||||
// can be used to inject the environment_vars into the environment.
|
||||
ElevatedExecuteCommand string `mapstructure:"elevated_execute_command"`
|
||||
|
||||
// The timeout for retrying to start the process. Until this timeout
|
||||
// is reached, if the provisioner can't start a process, it retries.
|
||||
// This can be set high to allow for reboots.
|
||||
StartRetryTimeout time.Duration `mapstructure:"start_retry_timeout"`
|
||||
|
||||
// This is used in the template generation to format environment variables
|
||||
// inside the `ExecuteCommand` template.
|
||||
EnvVarFormat string
|
||||
|
||||
// This is used in the template generation to format environment variables
|
||||
// inside the `ElevatedExecuteCommand` template.
|
||||
ElevatedEnvVarFormat string `mapstructure:"elevated_env_var_format"`
|
||||
|
||||
// Instructs the communicator to run the remote script as a
|
||||
// Windows scheduled task, effectively elevating the remote
|
||||
// user by impersonating a logged-in user
|
||||
ElevatedUser string `mapstructure:"elevated_user"`
|
||||
ElevatedPassword string `mapstructure:"elevated_password"`
|
||||
|
||||
// Valid Exit Codes - 0 is not always the only valid error code!
|
||||
// See http://www.symantec.com/connect/articles/windows-system-error-codes-exit-codes-description for examples
|
||||
// such as 3010 - "The requested operation is successful. Changes will not be effective until the system is rebooted."
|
||||
ValidExitCodes []int `mapstructure:"valid_exit_codes"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
type Provisioner struct {
|
||||
config Config
|
||||
communicator packer.Communicator
|
||||
}
|
||||
|
||||
type ExecuteCommandTemplate struct {
|
||||
Vars string
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
err := config.Decode(&p.config, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"execute_command",
|
||||
},
|
||||
},
|
||||
}, raws...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.config.EnvVarFormat == "" {
|
||||
p.config.EnvVarFormat = `$env:%s=\"%s\"; `
|
||||
}
|
||||
|
||||
if p.config.ElevatedEnvVarFormat == "" {
|
||||
p.config.ElevatedEnvVarFormat = `$env:%s="%s"; `
|
||||
}
|
||||
|
||||
if p.config.ExecuteCommand == "" {
|
||||
p.config.ExecuteCommand = `powershell "& { {{.Vars}}{{.Path}}; exit $LastExitCode}"`
|
||||
}
|
||||
|
||||
if p.config.ElevatedExecuteCommand == "" {
|
||||
p.config.ElevatedExecuteCommand = `{{.Vars}}{{.Path}}`
|
||||
}
|
||||
|
||||
if p.config.Inline != nil && len(p.config.Inline) == 0 {
|
||||
p.config.Inline = nil
|
||||
}
|
||||
|
||||
if p.config.StartRetryTimeout == 0 {
|
||||
p.config.StartRetryTimeout = 5 * time.Minute
|
||||
}
|
||||
|
||||
if p.config.RemotePath == "" {
|
||||
p.config.RemotePath = DefaultRemotePath
|
||||
}
|
||||
|
||||
if p.config.Scripts == nil {
|
||||
p.config.Scripts = make([]string, 0)
|
||||
}
|
||||
|
||||
if p.config.Vars == nil {
|
||||
p.config.Vars = make([]string, 0)
|
||||
}
|
||||
|
||||
if p.config.ValidExitCodes == nil {
|
||||
p.config.ValidExitCodes = []int{0}
|
||||
}
|
||||
|
||||
var errs error
|
||||
if p.config.Script != "" && len(p.config.Scripts) > 0 {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
errors.New("Only one of script or scripts can be specified."))
|
||||
}
|
||||
|
||||
if p.config.ElevatedUser != "" && p.config.ElevatedPassword == "" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
errors.New("Must supply an 'elevated_password' if 'elevated_user' provided"))
|
||||
}
|
||||
|
||||
if p.config.ElevatedUser == "" && p.config.ElevatedPassword != "" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
errors.New("Must supply an 'elevated_user' if 'elevated_password' provided"))
|
||||
}
|
||||
|
||||
if p.config.Script != "" {
|
||||
p.config.Scripts = []string{p.config.Script}
|
||||
}
|
||||
|
||||
if len(p.config.Scripts) == 0 && p.config.Inline == nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
errors.New("Either a script file or inline script must be specified."))
|
||||
} else if len(p.config.Scripts) > 0 && p.config.Inline != nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
errors.New("Only a script file or an inline script can be specified, not both."))
|
||||
}
|
||||
|
||||
for _, path := range p.config.Scripts {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("Bad script '%s': %s", path, err))
|
||||
}
|
||||
}
|
||||
|
||||
// Do a check for bad environment variables, such as '=foo', 'foobar'
|
||||
for _, kv := range p.config.Vars {
|
||||
vs := strings.SplitN(kv, "=", 2)
|
||||
if len(vs) != 2 || vs[0] == "" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("Environment variable not in format 'key=value': %s", kv))
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Takes the inline scripts, concatenates them
|
||||
// into a temporary file and returns a string containing the location
|
||||
// of said file.
|
||||
func extractScript(p *Provisioner) (string, error) {
|
||||
temp, err := ioutil.TempFile(os.TempDir(), "packer-powershell-provisioner")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer temp.Close()
|
||||
writer := bufio.NewWriter(temp)
|
||||
for _, command := range p.config.Inline {
|
||||
log.Printf("Found command: %s", command)
|
||||
if _, err := writer.WriteString(command + "\n"); err != nil {
|
||||
return "", fmt.Errorf("Error preparing shell script: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := writer.Flush(); err != nil {
|
||||
return "", fmt.Errorf("Error preparing shell script: %s", err)
|
||||
}
|
||||
|
||||
return temp.Name(), nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||
ui.Say(fmt.Sprintf("Provisioning with Powershell..."))
|
||||
p.communicator = comm
|
||||
|
||||
scripts := make([]string, len(p.config.Scripts))
|
||||
copy(scripts, p.config.Scripts)
|
||||
|
||||
// Build our variables up by adding in the build name and builder type
|
||||
envVars := make([]string, len(p.config.Vars)+2)
|
||||
envVars[0] = "PACKER_BUILD_NAME=" + p.config.PackerBuildName
|
||||
envVars[1] = "PACKER_BUILDER_TYPE=" + p.config.PackerBuilderType
|
||||
copy(envVars, p.config.Vars)
|
||||
|
||||
if p.config.Inline != nil {
|
||||
temp, err := extractScript(p)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to extract inline scripts into a file: %s", err))
|
||||
}
|
||||
scripts = append(scripts, temp)
|
||||
}
|
||||
|
||||
for _, path := range scripts {
|
||||
ui.Say(fmt.Sprintf("Provisioning with shell script: %s", path))
|
||||
|
||||
log.Printf("Opening %s for reading", path)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error opening shell script: %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
command, err := p.createCommandText()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error processing command: %s", err)
|
||||
}
|
||||
|
||||
// Upload the file and run the command. Do this in the context of
|
||||
// a single retryable function so that we don't end up with
|
||||
// the case that the upload succeeded, a restart is initiated,
|
||||
// and then the command is executed but the file doesn't exist
|
||||
// any longer.
|
||||
var cmd *packer.RemoteCmd
|
||||
err = p.retryable(func() error {
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := comm.Upload(p.config.RemotePath, f, nil); err != nil {
|
||||
return fmt.Errorf("Error uploading script: %s", err)
|
||||
}
|
||||
|
||||
cmd = &packer.RemoteCmd{Command: command}
|
||||
return cmd.StartWithUi(comm, ui)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Close the original file since we copied it
|
||||
f.Close()
|
||||
|
||||
// Check exit code against allowed codes (likely just 0)
|
||||
validExitCode := false
|
||||
for _, v := range p.config.ValidExitCodes {
|
||||
if cmd.ExitStatus == v {
|
||||
validExitCode = true
|
||||
}
|
||||
}
|
||||
if !validExitCode {
|
||||
return fmt.Errorf("Script exited with non-zero exit status: %d. Allowed exit codes are: %s", cmd.ExitStatus, p.config.ValidExitCodes)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) Cancel() {
|
||||
// Just hard quit. It isn't a big deal if what we're doing keeps
|
||||
// running on the other side.
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// retryable will retry the given function over and over until a
|
||||
// non-error is returned.
|
||||
func (p *Provisioner) retryable(f func() error) error {
|
||||
startTimeout := time.After(p.config.StartRetryTimeout)
|
||||
for {
|
||||
var err error
|
||||
if err = f(); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create an error and log it
|
||||
err = fmt.Errorf("Retryable error: %s", err)
|
||||
log.Printf(err.Error())
|
||||
|
||||
// Check if we timed out, otherwise we retry. It is safe to
|
||||
// retry since the only error case above is if the command
|
||||
// failed to START.
|
||||
select {
|
||||
case <-startTimeout:
|
||||
return err
|
||||
default:
|
||||
time.Sleep(retryableSleep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provisioner) createFlattenedEnvVars(elevated bool) (flattened string, err error) {
|
||||
flattened = ""
|
||||
envVars := make(map[string]string)
|
||||
|
||||
// Always available Packer provided env vars
|
||||
envVars["PACKER_BUILD_NAME"] = p.config.PackerBuildName
|
||||
envVars["PACKER_BUILDER_TYPE"] = p.config.PackerBuilderType
|
||||
|
||||
// Split vars into key/value components
|
||||
for _, envVar := range p.config.Vars {
|
||||
keyValue := strings.Split(envVar, "=")
|
||||
if len(keyValue) != 2 {
|
||||
err = errors.New("Shell provisioner environment variables must be in key=value format")
|
||||
return
|
||||
}
|
||||
envVars[keyValue[0]] = keyValue[1]
|
||||
}
|
||||
|
||||
// Create a list of env var keys in sorted order
|
||||
var keys []string
|
||||
for k := range envVars {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
format := p.config.EnvVarFormat
|
||||
if elevated {
|
||||
format = p.config.ElevatedEnvVarFormat
|
||||
}
|
||||
|
||||
// Re-assemble vars using OS specific format pattern and flatten
|
||||
for _, key := range keys {
|
||||
flattened += fmt.Sprintf(format, key, envVars[key])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Provisioner) createCommandText() (command string, err error) {
|
||||
// Create environment variables to set before executing the command
|
||||
flattenedEnvVars, err := p.createFlattenedEnvVars(false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
p.config.ctx.Data = &ExecuteCommandTemplate{
|
||||
Vars: flattenedEnvVars,
|
||||
Path: p.config.RemotePath,
|
||||
}
|
||||
command, err = interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error processing command: %s", err)
|
||||
}
|
||||
|
||||
// Return the interpolated command
|
||||
if p.config.ElevatedUser == "" {
|
||||
return command, nil
|
||||
}
|
||||
|
||||
// Can't double escape the env vars, lets create shiny new ones
|
||||
flattenedEnvVars, err = p.createFlattenedEnvVars(true)
|
||||
p.config.ctx.Data = &ExecuteCommandTemplate{
|
||||
Vars: flattenedEnvVars,
|
||||
Path: p.config.RemotePath,
|
||||
}
|
||||
command, err = interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error processing command: %s", err)
|
||||
}
|
||||
|
||||
// OK so we need an elevated shell runner to wrap our command, this is going to have its own path
|
||||
// generate the script and update the command runner in the process
|
||||
path, err := p.generateElevatedRunner(command)
|
||||
|
||||
// Return the path to the elevated shell wrapper
|
||||
command = fmt.Sprintf("powershell -executionpolicy bypass -file \"%s\"", path)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath string, err error) {
|
||||
log.Printf("Building elevated command wrapper for: %s", command)
|
||||
|
||||
// generate command
|
||||
var buffer bytes.Buffer
|
||||
err = elevatedTemplate.Execute(&buffer, elevatedOptions{
|
||||
User: p.config.ElevatedUser,
|
||||
Password: p.config.ElevatedPassword,
|
||||
TaskDescription: "Packer elevated task",
|
||||
TaskName: fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()),
|
||||
EncodedCommand: powershellEncode([]byte(command + "; exit $LASTEXITCODE")),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating elevated template: %s", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
tmpFile, err := ioutil.TempFile(os.TempDir(), "packer-elevated-shell.ps1")
|
||||
writer := bufio.NewWriter(tmpFile)
|
||||
if _, err := writer.WriteString(string(buffer.Bytes())); err != nil {
|
||||
return "", fmt.Errorf("Error preparing elevated shell script: %s", err)
|
||||
}
|
||||
|
||||
if err := writer.Flush(); err != nil {
|
||||
return "", fmt.Errorf("Error preparing elevated shell script: %s", err)
|
||||
}
|
||||
tmpFile.Close()
|
||||
f, err := os.Open(tmpFile.Name())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error opening temporary elevated shell script: %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
uuid := uuid.TimeOrderedUUID()
|
||||
path := fmt.Sprintf(`${env:TEMP}\packer-elevated-shell-%s.ps1`, uuid)
|
||||
log.Printf("Uploading elevated shell wrapper for command [%s] to [%s] from [%s]", command, path, tmpFile.Name())
|
||||
err = p.communicator.Upload(path, f, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error preparing elevated shell script: %s", err)
|
||||
}
|
||||
|
||||
// CMD formatted Path required for this op
|
||||
path = fmt.Sprintf("%s-%s.ps1", "%TEMP%\\packer-elevated-shell", uuid)
|
||||
return path, err
|
||||
}
|
|
@ -0,0 +1,656 @@
|
|||
package powershell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
//"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"inline": []interface{}{"foo", "bar"},
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
//log.SetOutput(ioutil.Discard)
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_extractScript(t *testing.T) {
|
||||
config := testConfig()
|
||||
p := new(Provisioner)
|
||||
_ = p.Prepare(config)
|
||||
file, err := extractScript(p)
|
||||
if err != nil {
|
||||
t.Fatalf("Should not be error: %s", err)
|
||||
}
|
||||
t.Logf("File: %s", file)
|
||||
if strings.Index(file, os.TempDir()) != 0 {
|
||||
t.Fatalf("Temp file should reside in %s. File location: %s", os.TempDir(), file)
|
||||
}
|
||||
|
||||
// File contents should contain 2 lines concatenated by newlines: foo\nbar
|
||||
readFile, err := ioutil.ReadFile(file)
|
||||
expectedContents := "foo\nbar\n"
|
||||
s := string(readFile[:])
|
||||
if s != expectedContents {
|
||||
t.Fatalf("Expected generated inlineScript to equal '%s', got '%s'", expectedContents, s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisioner_Impl(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = &Provisioner{}
|
||||
if _, ok := raw.(packer.Provisioner); !ok {
|
||||
t.Fatalf("must be a Provisioner")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_Defaults(t *testing.T) {
|
||||
var p Provisioner
|
||||
config := testConfig()
|
||||
|
||||
err := p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if p.config.RemotePath != DefaultRemotePath {
|
||||
t.Errorf("unexpected remote path: %s", p.config.RemotePath)
|
||||
}
|
||||
|
||||
if p.config.ElevatedUser != "" {
|
||||
t.Error("expected elevated_user to be empty")
|
||||
}
|
||||
if p.config.ElevatedPassword != "" {
|
||||
t.Error("expected elevated_password to be empty")
|
||||
}
|
||||
|
||||
if p.config.ExecuteCommand != "powershell \"& { {{.Vars}}{{.Path}}; exit $LastExitCode}\"" {
|
||||
t.Fatalf("Default command should be powershell \"& { {{.Vars}}{{.Path}}; exit $LastExitCode}\", but got %s", p.config.ExecuteCommand)
|
||||
}
|
||||
|
||||
if p.config.ElevatedExecuteCommand != "{{.Vars}}{{.Path}}" {
|
||||
t.Fatalf("Default command should be powershell {{.Vars}}{{.Path}}, but got %s", p.config.ElevatedExecuteCommand)
|
||||
}
|
||||
|
||||
if p.config.ValidExitCodes == nil {
|
||||
t.Fatalf("ValidExitCodes should not be nil")
|
||||
}
|
||||
if p.config.ValidExitCodes != nil {
|
||||
expCodes := []int{0}
|
||||
for i, v := range p.config.ValidExitCodes {
|
||||
if v != expCodes[i] {
|
||||
t.Fatalf("Expected ValidExitCodes don't match actual")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p.config.ElevatedEnvVarFormat != `$env:%s="%s"; ` {
|
||||
t.Fatalf("Default command should be powershell \"{{.Vars}}{{.Path}}\", but got %s", p.config.ElevatedEnvVarFormat)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_Config(t *testing.T) {
|
||||
config := testConfig()
|
||||
config["elevated_user"] = "{{user `user`}}"
|
||||
config["elevated_password"] = "{{user `password`}}"
|
||||
config[packer.UserVariablesConfigKey] = map[string]string{
|
||||
"user": "myusername",
|
||||
"password": "mypassword",
|
||||
}
|
||||
|
||||
var p Provisioner
|
||||
err := p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if p.config.ElevatedUser != "myusername" {
|
||||
t.Fatalf("Expected 'myusername' for key `elevated_user`: %s", p.config.ElevatedUser)
|
||||
}
|
||||
if p.config.ElevatedPassword != "mypassword" {
|
||||
t.Fatalf("Expected 'mypassword' for key `elevated_password`: %s", p.config.ElevatedPassword)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_InvalidKey(t *testing.T) {
|
||||
var p Provisioner
|
||||
config := testConfig()
|
||||
|
||||
// Add a random key
|
||||
config["i_should_not_be_valid"] = true
|
||||
err := p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_Elevated(t *testing.T) {
|
||||
var p Provisioner
|
||||
config := testConfig()
|
||||
|
||||
// Add a random key
|
||||
config["elevated_user"] = "vagrant"
|
||||
err := p.Prepare(config)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("should have error (only provided elevated_user)")
|
||||
}
|
||||
|
||||
config["elevated_password"] = "vagrant"
|
||||
err = p.Prepare(config)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("should not have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_Script(t *testing.T) {
|
||||
config := testConfig()
|
||||
delete(config, "inline")
|
||||
|
||||
config["script"] = "/this/should/not/exist"
|
||||
p := new(Provisioner)
|
||||
err := p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("error tempfile: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config["script"] = tf.Name()
|
||||
p = new(Provisioner)
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_ScriptAndInline(t *testing.T) {
|
||||
var p Provisioner
|
||||
config := testConfig()
|
||||
|
||||
delete(config, "inline")
|
||||
delete(config, "script")
|
||||
err := p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with both
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("error tempfile: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config["inline"] = []interface{}{"foo"}
|
||||
config["script"] = tf.Name()
|
||||
err = p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_ScriptAndScripts(t *testing.T) {
|
||||
var p Provisioner
|
||||
config := testConfig()
|
||||
|
||||
// Test with both
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("error tempfile: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config["inline"] = []interface{}{"foo"}
|
||||
config["scripts"] = []string{tf.Name()}
|
||||
err = p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_Scripts(t *testing.T) {
|
||||
config := testConfig()
|
||||
delete(config, "inline")
|
||||
|
||||
config["scripts"] = []string{}
|
||||
p := new(Provisioner)
|
||||
err := p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("error tempfile: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config["scripts"] = []string{tf.Name()}
|
||||
p = new(Provisioner)
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_EnvironmentVars(t *testing.T) {
|
||||
config := testConfig()
|
||||
|
||||
// Test with a bad case
|
||||
config["environment_vars"] = []string{"badvar", "good=var"}
|
||||
p := new(Provisioner)
|
||||
err := p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with a trickier case
|
||||
config["environment_vars"] = []string{"=bad"}
|
||||
p = new(Provisioner)
|
||||
err = p.Prepare(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with a good case
|
||||
// Note: baz= is a real env variable, just empty
|
||||
config["environment_vars"] = []string{"FOO=bar", "baz="}
|
||||
p = new(Provisioner)
|
||||
err = p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerQuote_EnvironmentVars(t *testing.T) {
|
||||
config := testConfig()
|
||||
|
||||
config["environment_vars"] = []string{"keyone=valueone", "keytwo=value\ntwo", "keythree='valuethree'", "keyfour='value\nfour'"}
|
||||
p := new(Provisioner)
|
||||
p.Prepare(config)
|
||||
|
||||
expectedValue := "keyone=valueone"
|
||||
if p.config.Vars[0] != expectedValue {
|
||||
t.Fatalf("%s should be equal to %s", p.config.Vars[0], expectedValue)
|
||||
}
|
||||
|
||||
expectedValue = "keytwo=value\ntwo"
|
||||
if p.config.Vars[1] != expectedValue {
|
||||
t.Fatalf("%s should be equal to %s", p.config.Vars[1], expectedValue)
|
||||
}
|
||||
|
||||
expectedValue = "keythree='valuethree'"
|
||||
if p.config.Vars[2] != expectedValue {
|
||||
t.Fatalf("%s should be equal to %s", p.config.Vars[2], expectedValue)
|
||||
}
|
||||
|
||||
expectedValue = "keyfour='value\nfour'"
|
||||
if p.config.Vars[3] != expectedValue {
|
||||
t.Fatalf("%s should be equal to %s", p.config.Vars[3], expectedValue)
|
||||
}
|
||||
}
|
||||
|
||||
func testUi() *packer.BasicUi {
|
||||
return &packer.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
ErrorWriter: new(bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
func testObjects() (packer.Ui, packer.Communicator) {
|
||||
ui := testUi()
|
||||
return ui, new(packer.MockCommunicator)
|
||||
}
|
||||
|
||||
func TestProvisionerProvision_ValidExitCodes(t *testing.T) {
|
||||
config := testConfig()
|
||||
delete(config, "inline")
|
||||
|
||||
// Defaults provided by Packer
|
||||
config["remote_path"] = "c:/Windows/Temp/inlineScript.bat"
|
||||
config["inline"] = []string{"whoami"}
|
||||
ui := testUi()
|
||||
p := new(Provisioner)
|
||||
|
||||
// Defaults provided by Packer
|
||||
p.config.PackerBuildName = "vmware"
|
||||
p.config.PackerBuilderType = "iso"
|
||||
p.config.ValidExitCodes = []int{0, 200}
|
||||
comm := new(packer.MockCommunicator)
|
||||
comm.StartExitStatus = 200
|
||||
p.Prepare(config)
|
||||
err := p.Provision(ui, comm)
|
||||
if err != nil {
|
||||
t.Fatal("should not have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerProvision_InvalidExitCodes(t *testing.T) {
|
||||
config := testConfig()
|
||||
delete(config, "inline")
|
||||
|
||||
// Defaults provided by Packer
|
||||
config["remote_path"] = "c:/Windows/Temp/inlineScript.bat"
|
||||
config["inline"] = []string{"whoami"}
|
||||
ui := testUi()
|
||||
p := new(Provisioner)
|
||||
|
||||
// Defaults provided by Packer
|
||||
p.config.PackerBuildName = "vmware"
|
||||
p.config.PackerBuilderType = "iso"
|
||||
p.config.ValidExitCodes = []int{0, 200}
|
||||
comm := new(packer.MockCommunicator)
|
||||
comm.StartExitStatus = 201 // Invalid!
|
||||
p.Prepare(config)
|
||||
err := p.Provision(ui, comm)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerProvision_Inline(t *testing.T) {
|
||||
config := testConfig()
|
||||
delete(config, "inline")
|
||||
|
||||
// Defaults provided by Packer
|
||||
config["remote_path"] = "c:/Windows/Temp/inlineScript.bat"
|
||||
config["inline"] = []string{"whoami"}
|
||||
ui := testUi()
|
||||
p := new(Provisioner)
|
||||
|
||||
// Defaults provided by Packer
|
||||
p.config.PackerBuildName = "vmware"
|
||||
p.config.PackerBuilderType = "iso"
|
||||
comm := new(packer.MockCommunicator)
|
||||
p.Prepare(config)
|
||||
err := p.Provision(ui, comm)
|
||||
if err != nil {
|
||||
t.Fatal("should not have error")
|
||||
}
|
||||
|
||||
expectedCommand := `powershell "& { $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; c:/Windows/Temp/inlineScript.bat; exit $LastExitCode}"`
|
||||
|
||||
// Should run the command without alteration
|
||||
if comm.StartCmd.Command != expectedCommand {
|
||||
t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command)
|
||||
}
|
||||
|
||||
envVars := make([]string, 2)
|
||||
envVars[0] = "FOO=BAR"
|
||||
envVars[1] = "BAR=BAZ"
|
||||
config["environment_vars"] = envVars
|
||||
config["remote_path"] = "c:/Windows/Temp/inlineScript.bat"
|
||||
|
||||
p.Prepare(config)
|
||||
err = p.Provision(ui, comm)
|
||||
if err != nil {
|
||||
t.Fatal("should not have error")
|
||||
}
|
||||
|
||||
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}"`
|
||||
|
||||
// Should run the command without alteration
|
||||
if comm.StartCmd.Command != expectedCommand {
|
||||
t.Fatalf("Expect command to be: %s, got: %s", expectedCommand, comm.StartCmd.Command)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerProvision_Scripts(t *testing.T) {
|
||||
tempFile, _ := ioutil.TempFile("", "packer")
|
||||
defer os.Remove(tempFile.Name())
|
||||
config := testConfig()
|
||||
delete(config, "inline")
|
||||
config["scripts"] = []string{tempFile.Name()}
|
||||
config["packer_build_name"] = "foobuild"
|
||||
config["packer_builder_type"] = "footype"
|
||||
ui := testUi()
|
||||
|
||||
p := new(Provisioner)
|
||||
comm := new(packer.MockCommunicator)
|
||||
p.Prepare(config)
|
||||
err := p.Provision(ui, comm)
|
||||
if err != nil {
|
||||
t.Fatal("should not have error")
|
||||
}
|
||||
|
||||
//powershell -Command "$env:PACKER_BUILDER_TYPE=''"; powershell -Command "$env:PACKER_BUILD_NAME='foobuild'"; powershell -Command c:/Windows/Temp/script.ps1
|
||||
expectedCommand := `powershell "& { $env:PACKER_BUILDER_TYPE=\"footype\"; $env:PACKER_BUILD_NAME=\"foobuild\"; c:/Windows/Temp/script.ps1; exit $LastExitCode}"`
|
||||
|
||||
// Should run the command without alteration
|
||||
if comm.StartCmd.Command != expectedCommand {
|
||||
t.Fatalf("Expect command to be %s NOT %s", expectedCommand, comm.StartCmd.Command)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) {
|
||||
tempFile, _ := ioutil.TempFile("", "packer")
|
||||
config := testConfig()
|
||||
ui := testUi()
|
||||
defer os.Remove(tempFile.Name())
|
||||
delete(config, "inline")
|
||||
|
||||
config["scripts"] = []string{tempFile.Name()}
|
||||
config["packer_build_name"] = "foobuild"
|
||||
config["packer_builder_type"] = "footype"
|
||||
|
||||
// Env vars - currently should not effect them
|
||||
envVars := make([]string, 2)
|
||||
envVars[0] = "FOO=BAR"
|
||||
envVars[1] = "BAR=BAZ"
|
||||
config["environment_vars"] = envVars
|
||||
|
||||
p := new(Provisioner)
|
||||
comm := new(packer.MockCommunicator)
|
||||
p.Prepare(config)
|
||||
err := p.Provision(ui, comm)
|
||||
if err != nil {
|
||||
t.Fatal("should not have error")
|
||||
}
|
||||
|
||||
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}"`
|
||||
|
||||
// Should run the command without alteration
|
||||
if comm.StartCmd.Command != expectedCommand {
|
||||
t.Fatalf("Expect command to be %s NOT %s", expectedCommand, comm.StartCmd.Command)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerProvision_UISlurp(t *testing.T) {
|
||||
// UI should be called n times
|
||||
|
||||
// UI should receive following messages / output
|
||||
}
|
||||
|
||||
func TestProvisioner_createFlattenedElevatedEnvVars_windows(t *testing.T) {
|
||||
config := testConfig()
|
||||
|
||||
p := new(Provisioner)
|
||||
err := p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error preparing config: %s", err)
|
||||
}
|
||||
|
||||
// Defaults provided by Packer
|
||||
p.config.PackerBuildName = "vmware"
|
||||
p.config.PackerBuilderType = "iso"
|
||||
|
||||
// no user env var
|
||||
flattenedEnvVars, err := p.createFlattenedEnvVars(true)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error creating flattened env vars: %s", err)
|
||||
}
|
||||
if flattenedEnvVars != "$env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; " {
|
||||
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
|
||||
}
|
||||
|
||||
// single user env var
|
||||
p.config.Vars = []string{"FOO=bar"}
|
||||
|
||||
flattenedEnvVars, err = p.createFlattenedEnvVars(true)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error creating flattened env vars: %s", err)
|
||||
}
|
||||
if flattenedEnvVars != "$env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; " {
|
||||
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
|
||||
}
|
||||
|
||||
// multiple user env vars
|
||||
p.config.Vars = []string{"FOO=bar", "BAZ=qux"}
|
||||
|
||||
flattenedEnvVars, err = p.createFlattenedEnvVars(true)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error creating flattened env vars: %s", err)
|
||||
}
|
||||
if flattenedEnvVars != "$env:BAZ=\"qux\"; $env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; " {
|
||||
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) {
|
||||
config := testConfig()
|
||||
|
||||
p := new(Provisioner)
|
||||
err := p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error preparing config: %s", err)
|
||||
}
|
||||
|
||||
// Defaults provided by Packer
|
||||
p.config.PackerBuildName = "vmware"
|
||||
p.config.PackerBuilderType = "iso"
|
||||
|
||||
// no user env var
|
||||
flattenedEnvVars, err := p.createFlattenedEnvVars(false)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error creating flattened env vars: %s", err)
|
||||
}
|
||||
if flattenedEnvVars != "$env:PACKER_BUILDER_TYPE=\\\"iso\\\"; $env:PACKER_BUILD_NAME=\\\"vmware\\\"; " {
|
||||
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
|
||||
}
|
||||
|
||||
// single user env var
|
||||
p.config.Vars = []string{"FOO=bar"}
|
||||
|
||||
flattenedEnvVars, err = p.createFlattenedEnvVars(false)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error creating flattened env vars: %s", err)
|
||||
}
|
||||
if flattenedEnvVars != "$env:FOO=\\\"bar\\\"; $env:PACKER_BUILDER_TYPE=\\\"iso\\\"; $env:PACKER_BUILD_NAME=\\\"vmware\\\"; " {
|
||||
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
|
||||
}
|
||||
|
||||
// multiple user env vars
|
||||
p.config.Vars = []string{"FOO=bar", "BAZ=qux"}
|
||||
|
||||
flattenedEnvVars, err = p.createFlattenedEnvVars(false)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error creating flattened env vars: %s", err)
|
||||
}
|
||||
if flattenedEnvVars != "$env:BAZ=\\\"qux\\\"; $env:FOO=\\\"bar\\\"; $env:PACKER_BUILDER_TYPE=\\\"iso\\\"; $env:PACKER_BUILD_NAME=\\\"vmware\\\"; " {
|
||||
t.Fatalf("unexpected flattened env vars: %s", flattenedEnvVars)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvision_createCommandText(t *testing.T) {
|
||||
|
||||
config := testConfig()
|
||||
p := new(Provisioner)
|
||||
comm := new(packer.MockCommunicator)
|
||||
p.communicator = comm
|
||||
_ = p.Prepare(config)
|
||||
|
||||
// Non-elevated
|
||||
cmd, _ := p.createCommandText()
|
||||
if cmd != "powershell \"& { $env:PACKER_BUILDER_TYPE=\\\"\\\"; $env:PACKER_BUILD_NAME=\\\"\\\"; c:/Windows/Temp/script.ps1; exit $LastExitCode}\"" {
|
||||
t.Fatalf("Got unexpected non-elevated command: %s", cmd)
|
||||
}
|
||||
|
||||
// Elevated
|
||||
p.config.ElevatedUser = "vagrant"
|
||||
p.config.ElevatedPassword = "vagrant"
|
||||
cmd, _ = p.createCommandText()
|
||||
matched, _ := regexp.MatchString("powershell -executionpolicy bypass -file \"%TEMP%(.{1})packer-elevated-shell.*", cmd)
|
||||
if !matched {
|
||||
t.Fatalf("Got unexpected elevated command: %s", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvision_generateElevatedShellRunner(t *testing.T) {
|
||||
|
||||
// Non-elevated
|
||||
config := testConfig()
|
||||
p := new(Provisioner)
|
||||
p.Prepare(config)
|
||||
comm := new(packer.MockCommunicator)
|
||||
p.communicator = comm
|
||||
path, err := p.generateElevatedRunner("whoami")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Did not expect error: %s", err.Error())
|
||||
}
|
||||
|
||||
if comm.UploadCalled != true {
|
||||
t.Fatalf("Should have uploaded file")
|
||||
}
|
||||
|
||||
matched, _ := regexp.MatchString("%TEMP%(.{1})packer-elevated-shell.*", path)
|
||||
if !matched {
|
||||
t.Fatalf("Got unexpected file: %s", path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetryable(t *testing.T) {
|
||||
config := testConfig()
|
||||
|
||||
count := 0
|
||||
retryMe := func() error {
|
||||
t.Logf("RetryMe, attempt number %d", count)
|
||||
if count == 2 {
|
||||
return nil
|
||||
}
|
||||
count++
|
||||
return errors.New(fmt.Sprintf("Still waiting %d more times...", 2-count))
|
||||
}
|
||||
retryableSleep = 50 * time.Millisecond
|
||||
p := new(Provisioner)
|
||||
p.config.StartRetryTimeout = 155 * time.Millisecond
|
||||
err := p.Prepare(config)
|
||||
err = p.retryable(retryMe)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error retrying funuction")
|
||||
}
|
||||
|
||||
count = 0
|
||||
p.config.StartRetryTimeout = 10 * time.Millisecond
|
||||
err = p.Prepare(config)
|
||||
err = p.retryable(retryMe)
|
||||
if err == nil {
|
||||
t.Fatalf("should have error retrying funuction")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancel(t *testing.T) {
|
||||
// Don't actually call Cancel() as it performs an os.Exit(0)
|
||||
// which kills the 'go test' tool
|
||||
}
|
Loading…
Reference in New Issue