Create a password for windows instances when using WinRM communicator and no password is provided.
This commit is contained in:
parent
44493a3820
commit
5db1c1f503
|
@ -58,6 +58,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
&StepCreateInstance{
|
&StepCreateInstance{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
},
|
},
|
||||||
|
&StepCreateWindowsPassword{
|
||||||
|
Debug: b.config.PackerDebug,
|
||||||
|
DebugKeyPath: fmt.Sprintf("gce_windows_%s.pem", b.config.PackerBuildName),
|
||||||
|
},
|
||||||
&StepInstanceInfo{
|
&StepInstanceInfo{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
},
|
},
|
||||||
|
@ -65,6 +69,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
Config: &b.config.Comm,
|
Config: &b.config.Comm,
|
||||||
Host: commHost,
|
Host: commHost,
|
||||||
SSHConfig: sshConfig,
|
SSHConfig: sshConfig,
|
||||||
|
WinRMConfig: winrmConfig,
|
||||||
},
|
},
|
||||||
new(common.StepProvision),
|
new(common.StepProvision),
|
||||||
new(StepWaitInstanceStartup),
|
new(StepWaitInstanceStartup),
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package googlecompute
|
package googlecompute
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// Driver is the interface that has to be implemented to communicate
|
// Driver is the interface that has to be implemented to communicate
|
||||||
// with GCE. The Driver interface exists mostly to allow a mock implementation
|
// with GCE. The Driver interface exists mostly to allow a mock implementation
|
||||||
// to be used to test the steps.
|
// to be used to test the steps.
|
||||||
|
@ -44,6 +49,9 @@ type Driver interface {
|
||||||
|
|
||||||
// WaitForInstance waits for an instance to reach the given state.
|
// WaitForInstance waits for an instance to reach the given state.
|
||||||
WaitForInstance(state, zone, name string) <-chan error
|
WaitForInstance(state, zone, name string) <-chan error
|
||||||
|
|
||||||
|
// CreateOrResetWindowsPassword creates or resets the password for a user on an Windows instance.
|
||||||
|
CreateOrResetWindowsPassword(zone, name string, config *WindowsPasswordConfig) (<-chan error, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstanceConfig struct {
|
type InstanceConfig struct {
|
||||||
|
@ -64,3 +72,24 @@ type InstanceConfig struct {
|
||||||
Tags []string
|
Tags []string
|
||||||
Zone string
|
Zone string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WindowsPasswordConfig is the data structue that GCE needs to encrypt the created
|
||||||
|
// windows password.
|
||||||
|
type WindowsPasswordConfig struct {
|
||||||
|
key *rsa.PrivateKey
|
||||||
|
password string
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
Modulus string `json:"modulus"`
|
||||||
|
Exponent string `json:"exponent"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
ExpireOn time.Time `json:"expireOn"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type windowsPasswordResponse struct {
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
PasswordFound bool `json:"passwordFound"`
|
||||||
|
EncryptedPassword string `json:"encryptedPassword"`
|
||||||
|
Modulus string `json:"modulus"`
|
||||||
|
Exponent string `json:"exponent"`
|
||||||
|
ErrorMessage string `json:"errorMessage"`
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
package googlecompute
|
package googlecompute
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
"github.com/mitchellh/packer/version"
|
"github.com/mitchellh/packer/version"
|
||||||
|
@ -393,6 +400,112 @@ func (d *driverGCE) RunInstance(c *InstanceConfig) (<-chan error, error) {
|
||||||
return errCh, nil
|
return errCh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *driverGCE) CreateOrResetWindowsPassword(instance, zone string, c *WindowsPasswordConfig) (<-chan error, error) {
|
||||||
|
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
go d.createWindowsPassword(errCh, instance, zone, c)
|
||||||
|
|
||||||
|
return errCh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driverGCE) createWindowsPassword(errCh chan<- error, name, zone string, c *WindowsPasswordConfig) {
|
||||||
|
|
||||||
|
data, err := json.Marshal(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dCopy := string(data)
|
||||||
|
|
||||||
|
instance, err := d.service.Instances.Get(d.projectId, zone, name).Do()
|
||||||
|
instance.Metadata.Items = append(instance.Metadata.Items, &compute.MetadataItems{Key: "windows-keys", Value: &dCopy})
|
||||||
|
|
||||||
|
op, err := d.service.Instances.SetMetadata(d.projectId, zone, name, &compute.Metadata{
|
||||||
|
Fingerprint: instance.Metadata.Fingerprint,
|
||||||
|
Items: instance.Metadata.Items,
|
||||||
|
}).Do()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newErrCh := make(chan error, 1)
|
||||||
|
go waitForState(newErrCh, "DONE", d.refreshZoneOp(zone, op))
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err = <-newErrCh:
|
||||||
|
case <-time.After(time.Second * 30):
|
||||||
|
err = errors.New("time out while waiting for instance to create")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout := time.Now().Add(time.Minute * 3)
|
||||||
|
hash := sha1.New()
|
||||||
|
random := rand.Reader
|
||||||
|
|
||||||
|
for time.Now().Before(timeout) {
|
||||||
|
if passwordResponses, err := d.getPasswordResponses(zone, name); err == nil {
|
||||||
|
for _, response := range passwordResponses {
|
||||||
|
if response.Modulus == c.Modulus {
|
||||||
|
|
||||||
|
decodedPassword, err := base64.StdEncoding.DecodeString(response.EncryptedPassword)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
password, err := rsa.DecryptOAEP(hash, random, c.key, decodedPassword, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.password = string(password)
|
||||||
|
errCh <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
err = errors.New("Could not retrieve password. Timed out.")
|
||||||
|
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driverGCE) getPasswordResponses(zone, instance string) ([]windowsPasswordResponse, error) {
|
||||||
|
output, err := d.service.Instances.GetSerialPortOutput(d.projectId, zone, instance).Port(4).Do()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := strings.Split(output.Contents, "\n")
|
||||||
|
|
||||||
|
passwordResponses := make([]windowsPasswordResponse, 0, len(responses))
|
||||||
|
|
||||||
|
for _, response := range responses {
|
||||||
|
var passwordResponse windowsPasswordResponse
|
||||||
|
if err := json.Unmarshal([]byte(response), &passwordResponse); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
passwordResponses = append(passwordResponses, passwordResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return passwordResponses, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *driverGCE) WaitForInstance(state, zone, name string) <-chan error {
|
func (d *driverGCE) WaitForInstance(state, zone, name string) <-chan error {
|
||||||
errCh := make(chan error, 1)
|
errCh := make(chan error, 1)
|
||||||
go waitForState(errCh, state, d.refreshInstanceState(zone, name))
|
go waitForState(errCh, state, d.refreshInstanceState(zone, name))
|
||||||
|
|
|
@ -67,6 +67,12 @@ type DriverMock struct {
|
||||||
RunInstanceErrCh <-chan error
|
RunInstanceErrCh <-chan error
|
||||||
RunInstanceErr error
|
RunInstanceErr error
|
||||||
|
|
||||||
|
CreateOrResetWindowsPasswordZone string
|
||||||
|
CreateOrResetWindowsPasswordInstance string
|
||||||
|
CreateOrResetWindowsPasswordConfig *WindowsPasswordConfig
|
||||||
|
CreateOrResetWindowsPasswordErr error
|
||||||
|
CreateOrResetWindowsPasswordErrCh <-chan error
|
||||||
|
|
||||||
WaitForInstanceState string
|
WaitForInstanceState string
|
||||||
WaitForInstanceZone string
|
WaitForInstanceZone string
|
||||||
WaitForInstanceName string
|
WaitForInstanceName string
|
||||||
|
@ -224,3 +230,25 @@ func (d *DriverMock) WaitForInstance(state, zone, name string) <-chan error {
|
||||||
|
|
||||||
return resultCh
|
return resultCh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DriverMock) GetWindowsPassword() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DriverMock) CreateOrResetWindowsPassword(instance, zone string, c *WindowsPasswordConfig) (<-chan error, error) {
|
||||||
|
|
||||||
|
d.CreateOrResetWindowsPasswordInstance = instance
|
||||||
|
d.CreateOrResetWindowsPasswordZone = zone
|
||||||
|
d.CreateOrResetWindowsPasswordConfig = c
|
||||||
|
|
||||||
|
c.password = "MOCK_PASSWORD"
|
||||||
|
|
||||||
|
resultCh := d.CreateOrResetWindowsPasswordErrCh
|
||||||
|
if resultCh == nil {
|
||||||
|
ch := make(chan error)
|
||||||
|
close(ch)
|
||||||
|
resultCh = ch
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultCh, d.CreateOrResetWindowsPasswordErr
|
||||||
|
}
|
||||||
|
|
|
@ -76,6 +76,10 @@ func (s *StepCreateInstance) Run(state multistep.StateBag) multistep.StepAction
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sourceImage.IsWindows() && c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" {
|
||||||
|
state.Put("create_windows_password", true)
|
||||||
|
}
|
||||||
|
|
||||||
ui.Say("Creating instance...")
|
ui.Say("Creating instance...")
|
||||||
name := c.InstanceName
|
name := c.InstanceName
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,101 @@ func TestStepCreateInstance(t *testing.T) {
|
||||||
assert.Equal(t, d.DeleteDiskZone, c.Zone, "Incorrect disk zone passed to driver.")
|
assert.Equal(t, d.DeleteDiskZone, c.Zone, "Incorrect disk zone passed to driver.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStepCreateInstance_windowsNeedsPassword(t *testing.T) {
|
||||||
|
|
||||||
|
state := testState(t)
|
||||||
|
step := new(StepCreateInstance)
|
||||||
|
defer step.Cleanup(state)
|
||||||
|
|
||||||
|
state.Put("ssh_public_key", "key")
|
||||||
|
c := state.Get("config").(*Config)
|
||||||
|
d := state.Get("driver").(*DriverMock)
|
||||||
|
d.GetImageResult = StubImage("test-image", "test-project", []string{"windows"}, 100)
|
||||||
|
c.Comm.Type = "winrm"
|
||||||
|
// run the step
|
||||||
|
if action := step.Run(state); action != multistep.ActionContinue {
|
||||||
|
t.Fatalf("bad action: %#v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify state
|
||||||
|
nameRaw, ok := state.GetOk("instance_name")
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("should have instance name")
|
||||||
|
}
|
||||||
|
|
||||||
|
createPassword, ok := state.GetOk("create_windows_password")
|
||||||
|
|
||||||
|
if !ok || !createPassword.(bool) {
|
||||||
|
t.Fatal("should need to create a windows password")
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
step.Cleanup(state)
|
||||||
|
|
||||||
|
if d.DeleteInstanceName != nameRaw.(string) {
|
||||||
|
t.Fatal("should've deleted instance")
|
||||||
|
}
|
||||||
|
if d.DeleteInstanceZone != c.Zone {
|
||||||
|
t.Fatalf("bad instance zone: %#v", d.DeleteInstanceZone)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.DeleteDiskName != c.InstanceName {
|
||||||
|
t.Fatal("should've deleted disk")
|
||||||
|
}
|
||||||
|
if d.DeleteDiskZone != c.Zone {
|
||||||
|
t.Fatalf("bad disk zone: %#v", d.DeleteDiskZone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepCreateInstance_windowsPasswordSet(t *testing.T) {
|
||||||
|
|
||||||
|
state := testState(t)
|
||||||
|
step := new(StepCreateInstance)
|
||||||
|
defer step.Cleanup(state)
|
||||||
|
|
||||||
|
state.Put("ssh_public_key", "key")
|
||||||
|
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
driver := state.Get("driver").(*DriverMock)
|
||||||
|
driver.GetImageResult = StubImage("test-image", "test-project", []string{"windows"}, 100)
|
||||||
|
config.Comm.Type = "winrm"
|
||||||
|
config.Comm.WinRMPassword = "password"
|
||||||
|
|
||||||
|
// run the step
|
||||||
|
if action := step.Run(state); action != multistep.ActionContinue {
|
||||||
|
t.Fatalf("bad action: %#v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify state
|
||||||
|
nameRaw, ok := state.GetOk("instance_name")
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("should have instance name")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok = state.GetOk("create_windows_password")
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
t.Fatal("should not need to create windows password")
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
step.Cleanup(state)
|
||||||
|
|
||||||
|
if driver.DeleteInstanceName != nameRaw.(string) {
|
||||||
|
t.Fatal("should've deleted instance")
|
||||||
|
}
|
||||||
|
if driver.DeleteInstanceZone != config.Zone {
|
||||||
|
t.Fatalf("bad instance zone: %#v", driver.DeleteInstanceZone)
|
||||||
|
}
|
||||||
|
|
||||||
|
if driver.DeleteDiskName != config.InstanceName {
|
||||||
|
t.Fatal("should've deleted disk")
|
||||||
|
}
|
||||||
|
if driver.DeleteDiskZone != config.Zone {
|
||||||
|
t.Fatalf("bad disk zone: %#v", driver.DeleteDiskZone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStepCreateInstance_error(t *testing.T) {
|
func TestStepCreateInstance_error(t *testing.T) {
|
||||||
state := testState(t)
|
state := testState(t)
|
||||||
step := new(StepCreateInstance)
|
step := new(StepCreateInstance)
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
package googlecompute
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StepCreateWindowsPassword represents a Packer build step that sets the windows password on a Windows GCE instance.
|
||||||
|
type StepCreateWindowsPassword struct {
|
||||||
|
Debug bool
|
||||||
|
DebugKeyPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run executes the Packer build step that sets the windows password on a Windows GCE instance.
|
||||||
|
func (s *StepCreateWindowsPassword) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
d := state.Get("driver").(Driver)
|
||||||
|
c := state.Get("config").(*Config)
|
||||||
|
name := state.Get("instance_name").(string)
|
||||||
|
|
||||||
|
if c.Comm.WinRMPassword != "" {
|
||||||
|
state.Put("winrm_password", c.Comm.WinRMPassword)
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
create, ok := state.GetOk("create_windows_password")
|
||||||
|
|
||||||
|
if !ok || !create.(bool) {
|
||||||
|
return multistep.ActionContinue
|
||||||
|
|
||||||
|
}
|
||||||
|
ui.Say("Creating windows user for instance...")
|
||||||
|
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error creating temporary key: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(buf, uint32(priv.E))
|
||||||
|
|
||||||
|
data := WindowsPasswordConfig{
|
||||||
|
key: priv,
|
||||||
|
UserName: c.Comm.WinRMUser,
|
||||||
|
Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()),
|
||||||
|
Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
|
||||||
|
Email: c.Account.ClientEmail,
|
||||||
|
ExpireOn: time.Now().Add(time.Minute * 5),
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Debug {
|
||||||
|
|
||||||
|
priv_blk := pem.Block{
|
||||||
|
Type: "RSA PRIVATE KEY",
|
||||||
|
Headers: nil,
|
||||||
|
Bytes: x509.MarshalPKCS1PrivateKey(priv),
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
|
||||||
|
f, err := os.Create(s.DebugKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out the key
|
||||||
|
err = pem.Encode(f, &priv_blk)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errCh, err := d.CreateOrResetWindowsPassword(name, c.Zone, &data)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
ui.Message("Waiting for windows password to complete...")
|
||||||
|
select {
|
||||||
|
case err = <-errCh:
|
||||||
|
case <-time.After(c.stateTimeout):
|
||||||
|
err = errors.New("time out while waiting for the password to be created")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error creating windows password: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Message("Created password.")
|
||||||
|
|
||||||
|
state.Put("winrm_password", data.password)
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing to clean up. The windows password is only created on the single instance.
|
||||||
|
func (s *StepCreateWindowsPassword) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,162 @@
|
||||||
|
package googlecompute
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStepCreateOrResetWindowsPassword(t *testing.T) {
|
||||||
|
state := testState(t)
|
||||||
|
|
||||||
|
// Step is run after the instance is created so we will have an instance name set
|
||||||
|
state.Put("instance_name", "mock_instance")
|
||||||
|
state.Put("create_windows_password", true)
|
||||||
|
|
||||||
|
step := new(StepCreateWindowsPassword)
|
||||||
|
defer step.Cleanup(state)
|
||||||
|
|
||||||
|
// run the step
|
||||||
|
if action := step.Run(state); action != multistep.ActionContinue {
|
||||||
|
t.Fatalf("bad action: %#v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
if password, ok := state.GetOk("winrm_password"); !ok || password.(string) != "MOCK_PASSWORD" {
|
||||||
|
t.Fatal("should have a password", password, ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepCreateOrResetWindowsPassword_passwordSet(t *testing.T) {
|
||||||
|
state := testState(t)
|
||||||
|
|
||||||
|
// Step is run after the instance is created so we will have an instance name set
|
||||||
|
state.Put("instance_name", "mock_instance")
|
||||||
|
|
||||||
|
c := state.Get("config").(*Config)
|
||||||
|
|
||||||
|
c.Comm.WinRMPassword = "password"
|
||||||
|
|
||||||
|
step := new(StepCreateWindowsPassword)
|
||||||
|
defer step.Cleanup(state)
|
||||||
|
|
||||||
|
// run the step
|
||||||
|
if action := step.Run(state); action != multistep.ActionContinue {
|
||||||
|
t.Fatalf("bad action: %#v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
if password, ok := state.GetOk("winrm_password"); !ok || password.(string) != "password" {
|
||||||
|
t.Fatal("should have used existing password", password, ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepCreateOrResetWindowsPassword_dontNeedPassword(t *testing.T) {
|
||||||
|
state := testState(t)
|
||||||
|
|
||||||
|
// Step is run after the instance is created so we will have an instance name set
|
||||||
|
state.Put("instance_name", "mock_instance")
|
||||||
|
|
||||||
|
step := new(StepCreateWindowsPassword)
|
||||||
|
defer step.Cleanup(state)
|
||||||
|
|
||||||
|
// run the step
|
||||||
|
if action := step.Run(state); action != multistep.ActionContinue {
|
||||||
|
t.Fatalf("bad action: %#v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepCreateOrResetWindowsPassword_debug(t *testing.T) {
|
||||||
|
tf, err := ioutil.TempFile("", "packer")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
tf.Close()
|
||||||
|
|
||||||
|
state := testState(t)
|
||||||
|
// Step is run after the instance is created so we will have an instance name set
|
||||||
|
state.Put("instance_name", "mock_instance")
|
||||||
|
state.Put("create_windows_password", true)
|
||||||
|
|
||||||
|
step := new(StepCreateWindowsPassword)
|
||||||
|
|
||||||
|
step.Debug = true
|
||||||
|
step.DebugKeyPath = tf.Name()
|
||||||
|
|
||||||
|
defer step.Cleanup(state)
|
||||||
|
|
||||||
|
// run the step
|
||||||
|
if action := step.Run(state); action != multistep.ActionContinue {
|
||||||
|
t.Fatalf("bad action: %#v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
if password, ok := state.GetOk("winrm_password"); !ok || password.(string) != "MOCK_PASSWORD" {
|
||||||
|
t.Fatal("should have a password", password, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(tf.Name()); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepCreateOrResetWindowsPassword_error(t *testing.T) {
|
||||||
|
state := testState(t)
|
||||||
|
|
||||||
|
// Step is run after the instance is created so we will have an instance name set
|
||||||
|
state.Put("instance_name", "mock_instance")
|
||||||
|
state.Put("create_windows_password", true)
|
||||||
|
|
||||||
|
step := new(StepCreateWindowsPassword)
|
||||||
|
defer step.Cleanup(state)
|
||||||
|
|
||||||
|
driver := state.Get("driver").(*DriverMock)
|
||||||
|
driver.CreateOrResetWindowsPasswordErr = errors.New("error")
|
||||||
|
|
||||||
|
// run the step
|
||||||
|
if action := step.Run(state); action != multistep.ActionHalt {
|
||||||
|
t.Fatalf("bad action: %#v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify state
|
||||||
|
if _, ok := state.GetOk("error"); !ok {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := state.GetOk("winrm_password"); ok {
|
||||||
|
t.Fatal("should NOT have instance name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepCreateOrResetWindowsPassword_errorOnChannel(t *testing.T) {
|
||||||
|
state := testState(t)
|
||||||
|
|
||||||
|
// Step is run after the instance is created so we will have an instance name set
|
||||||
|
state.Put("instance_name", "mock_instance")
|
||||||
|
state.Put("create_windows_password", true)
|
||||||
|
|
||||||
|
step := new(StepCreateWindowsPassword)
|
||||||
|
defer step.Cleanup(state)
|
||||||
|
|
||||||
|
driver := state.Get("driver").(*DriverMock)
|
||||||
|
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
errCh <- errors.New("error")
|
||||||
|
|
||||||
|
driver.CreateOrResetWindowsPasswordErrCh = errCh
|
||||||
|
|
||||||
|
// run the step
|
||||||
|
if action := step.Run(state); action != multistep.ActionHalt {
|
||||||
|
t.Fatalf("bad action: %#v", action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify state
|
||||||
|
if _, ok := state.GetOk("error"); !ok {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
if _, ok := state.GetOk("winrm_password"); ok {
|
||||||
|
t.Fatal("should NOT have instance name")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package googlecompute
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/helper/communicator"
|
||||||
|
)
|
||||||
|
|
||||||
|
// winrmConfig returns the WinRM configuration.
|
||||||
|
func winrmConfig(state multistep.StateBag) (*communicator.WinRMConfig, error) {
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
password := state.Get("winrm_password").(string)
|
||||||
|
|
||||||
|
return &communicator.WinRMConfig{
|
||||||
|
Username: config.Comm.WinRMUser,
|
||||||
|
Password: password,
|
||||||
|
}, nil
|
||||||
|
}
|
Loading…
Reference in New Issue