Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ _testmain.go
*.cov
bin
pkg

#IntelliJ Go Project Files
.idea
*.iml
57 changes: 40 additions & 17 deletions builder/amazon-windows/common/run_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"

"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/packer"
)

Expand All @@ -13,19 +14,24 @@ import (
type RunConfig struct {
AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"`
AvailabilityZone string `mapstructure:"availability_zone"`
ConfigureSecureWinRM bool `mapstructure:"winrm_autoconfigure"`
IamInstanceProfile string `mapstructure:"iam_instance_profile"`
InstanceType string `mapstructure:"instance_type"`
KeyPairPrivateKeyFile string `mapstructure:"key_pair_private_key_file"`
NewAdministratorPassword string `mapstructure:"new_administrator_password"`
RunTags map[string]string `mapstructure:"run_tags"`
SourceAmi string `mapstructure:"source_ami"`
SpotPrice string `mapstructure:"spot_price"`
SpotPriceAutoProduct string `mapstructure:"spot_price_auto_product"`
WinRMPrivateIp bool `mapstructure:"winrm_private_ip"`
SecurityGroupId string `mapstructure:"security_group_id"`
SecurityGroupIds []string `mapstructure:"security_group_ids"`
SubnetId string `mapstructure:"subnet_id"`
TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
UserData string `mapstructure:"user_data"`
UserDataFile string `mapstructure:"user_data_file"`
VpcId string `mapstructure:"vpc_id"`
WinRMPrivateIp bool `mapstructure:"winrm_private_ip"`
WinRMCertificateFile string `mapstructure:"winrm_certificate_file"`
}

func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
Expand All @@ -38,17 +44,19 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
}

templates := map[string]*string{
"iam_instance_profile": &c.IamInstanceProfile,
"instance_type": &c.InstanceType,
"spot_price": &c.SpotPrice,
"spot_price_auto_product": &c.SpotPriceAutoProduct,
"source_ami": &c.SourceAmi,
"subnet_id": &c.SubnetId,
"vpc_id": &c.VpcId,
"availability_zone": &c.AvailabilityZone,
"user_data": &c.UserData,
"user_data_file": &c.UserDataFile,
"security_group_id": &c.SecurityGroupId,
"iam_instance_profile": &c.IamInstanceProfile,
"instance_type": &c.InstanceType,
"key_pair_private_key_file": &c.KeyPairPrivateKeyFile,
"spot_price": &c.SpotPrice,
"spot_price_auto_product": &c.SpotPriceAutoProduct,
"source_ami": &c.SourceAmi,
"subnet_id": &c.SubnetId,
"temporary_key_pair_name": &c.TemporaryKeyPairName,
"vpc_id": &c.VpcId,
"availability_zone": &c.AvailabilityZone,
"user_data": &c.UserData,
"user_data_file": &c.UserDataFile,
"security_group_id": &c.SecurityGroupId,
}

errs := make([]error, 0)
Expand All @@ -70,18 +78,33 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
errs = append(errs, errors.New("An instance_type must be specified"))
}

if c.TemporaryKeyPairName == "" {
c.TemporaryKeyPairName = fmt.Sprintf(
"packer %s", uuid.TimeOrderedUUID())
}

if c.SpotPrice == "auto" {
if c.SpotPriceAutoProduct == "" {
errs = append(errs, errors.New(
"spot_price_auto_product must be specified when spot_price is auto"))
}
}

if c.UserData != "" && c.UserDataFile != "" {
errs = append(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified."))
} else if c.UserDataFile != "" {
if _, err := os.Stat(c.UserDataFile); err != nil {
errs = append(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
if c.ConfigureSecureWinRM {
if c.UserData != "" || c.UserDataFile != "" {
errs = append(errs, fmt.Errorf("winrm_autoconfigure cannot be used in conjunction with user_data or user_data_file"))
}

if c.WinRMCertificateFile == "" {
errs = append(errs, fmt.Errorf("winrm_certificate_file must be set to the path of a PFX container holding the certificate to be used for WinRM."))
}
} else {
if c.UserData != "" && c.UserDataFile != "" {
errs = append(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified."))
} else if c.UserDataFile != "" {
if _, err := os.Stat(c.UserDataFile); err != nil {
errs = append(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
}
}
}

Expand Down
42 changes: 27 additions & 15 deletions builder/amazon-windows/common/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package common
import (
"errors"
"fmt"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/multistep"
"log"
"net"
"os"
"strconv"
"time"

"github.com/awslabs/aws-sdk-go/aws"
"github.com/awslabs/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
)

// StateRefreshFunc is a function type used for StateChangeConf that is
Expand All @@ -36,9 +38,13 @@ type StateChangeConf struct {
// an AMI for state changes.
func AMIStateRefreshFunc(conn *ec2.EC2, imageId string) StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := conn.Images([]string{imageId}, ec2.NewFilter())
input := &ec2.DescribeImagesInput{
ImageIDs: []*string{&imageId},
}
resp, err := conn.DescribeImages(input)

if err != nil {
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidAMIID.NotFound" {
if ec2err, ok := err.(*aws.APIError); ok && ec2err.Code == "InvalidAMIID.NotFound" {
// Set this to nil as if we didn't find anything.
resp = nil
} else if isTransientNetworkError(err) {
Expand All @@ -57,17 +63,20 @@ func AMIStateRefreshFunc(conn *ec2.EC2, imageId string) StateRefreshFunc {
}

i := resp.Images[0]
return i, i.State, nil
return i, *i.State, nil
}
}

// InstanceStateRefreshFunc returns a StateRefreshFunc that is used to watch
// an EC2 instance.
func InstanceStateRefreshFunc(conn *ec2.EC2, instanceId string) StateRefreshFunc {
func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := conn.Instances([]string{instanceId}, ec2.NewFilter())
input := &ec2.DescribeInstancesInput{
InstanceIDs: []*string{i.InstanceID},
}
resp, err := conn.DescribeInstances(input)
if err != nil {
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
if ec2err, ok := err.(*aws.APIError); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
// Set this to nil as if we didn't find anything.
resp = nil
} else if isTransientNetworkError(err) {
Expand All @@ -85,18 +94,21 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, instanceId string) StateRefreshFunc
return nil, "", nil
}

i := &resp.Reservations[0].Instances[0]
return i, i.State.Name, nil
i := resp.Reservations[0].Instances[0]
return i, *i.State.Name, nil
}
}

// SpotRequestStateRefreshFunc returns a StateRefreshFunc that is used to watch
// a spot request for state changes.
func SpotRequestStateRefreshFunc(conn *ec2.EC2, spotRequestId string) StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := conn.DescribeSpotRequests([]string{spotRequestId}, ec2.NewFilter())
input := &ec2.DescribeSpotInstanceRequestsInput{
SpotInstanceRequestIDs: []*string{&spotRequestId},
}
resp, err := conn.DescribeSpotInstanceRequests(input)
if err != nil {
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidSpotInstanceRequestID.NotFound" {
if ec2err, ok := err.(*aws.APIError); ok && ec2err.Code == "InvalidSpotInstanceRequestID.NotFound" {
// Set this to nil as if we didn't find anything.
resp = nil
} else if isTransientNetworkError(err) {
Expand All @@ -108,14 +120,14 @@ func SpotRequestStateRefreshFunc(conn *ec2.EC2, spotRequestId string) StateRefre
}
}

if resp == nil || len(resp.SpotRequestResults) == 0 {
if resp == nil || len(resp.SpotInstanceRequests) == 0 {
// Sometimes AWS has consistency issues and doesn't see the
// SpotRequest. Return an empty state.
return nil, "", nil
}

i := resp.SpotRequestResults[0]
return i, i.State, nil
i := resp.SpotInstanceRequests[0]
return i, *i.State, nil
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package common

import (
"fmt"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/multistep"
//"github.com/mitchellh/packer/common"
"errors"
wincommon "github.com/packer-community/packer-windows-plugins/common"
"fmt"
"log"
"time"

"github.com/awslabs/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
wincommon "github.com/packer-community/packer-windows-plugins/common"
)

// Returns an Endpoint suitable for the WinRM communicator
Expand All @@ -17,13 +17,13 @@ func WinRMAddress(e *ec2.EC2, port uint, private bool) func(multistep.StateBag)
for j := 0; j < 2; j++ {
var host string
i := state.Get("instance").(*ec2.Instance)
if i.DNSName != "" {
host = i.DNSName
} else if i.VpcId != "" {
if i.PublicIpAddress != "" && !private {
host = i.PublicIpAddress
if *i.PublicDNSName != "" {
host = *i.PublicDNSName
} else if *i.VPCID != "" {
if *i.PublicIPAddress != "" && !private {
host = *i.PublicIPAddress
} else {
host = i.PrivateIpAddress
host = *i.PrivateIPAddress
}
}

Expand All @@ -32,13 +32,16 @@ func WinRMAddress(e *ec2.EC2, port uint, private bool) func(multistep.StateBag)
return fmt.Sprintf("%s:%d", host, port), nil
}

r, err := e.Instances([]string{i.InstanceId}, ec2.NewFilter())
input := &ec2.DescribeInstancesInput{
InstanceIDs: []*string{i.InstanceID},
}
r, err := e.DescribeInstances(input)
if err != nil {
return "", err
}

if len(r.Reservations) == 0 || len(r.Reservations[0].Instances) == 0 {
return "", fmt.Errorf("instance not found: %s", i.InstanceId)
return "", fmt.Errorf("instance not found: %s", i.InstanceID)
}

state.Put("instance", &r.Reservations[0].Instances[0])
Expand All @@ -50,11 +53,10 @@ func WinRMAddress(e *ec2.EC2, port uint, private bool) func(multistep.StateBag)
}

// Creates a WinRM connect step for an EC2 instance
func NewConnectStep(ec2 *ec2.EC2, private bool, winrmConfig wincommon.WinRMConfig) multistep.Step {
func NewConnectStep(ec2 *ec2.EC2, private bool, winrmConfig *wincommon.WinRMConfig) multistep.Step {
return &wincommon.StepConnectWinRM{
WinRMAddress: WinRMAddress(ec2, winrmConfig.WinRMPort, private),
WinRMUser: winrmConfig.WinRMUser,
WinRMPassword: winrmConfig.WinRMPassword,
WinRMConfig: winrmConfig,
WinRMWaitTimeout: winrmConfig.WinRMWaitTimeout,
}
}
Loading