github.com/amanya/packer@v0.12.1-0.20161117214323-902ac5ab2eb6/builder/amazon/ebsvolume/builder.go (about) 1 // The ebsvolume package contains a packer.Builder implementation that 2 // builds EBS volumes for Amazon EC2 using an ephemeral instance, 3 package ebsvolume 4 5 import ( 6 "fmt" 7 "log" 8 "strings" 9 10 "github.com/aws/aws-sdk-go/aws/session" 11 "github.com/aws/aws-sdk-go/service/ec2" 12 "github.com/hashicorp/errwrap" 13 "github.com/mitchellh/multistep" 14 awscommon "github.com/mitchellh/packer/builder/amazon/common" 15 "github.com/mitchellh/packer/common" 16 "github.com/mitchellh/packer/helper/communicator" 17 "github.com/mitchellh/packer/helper/config" 18 "github.com/mitchellh/packer/packer" 19 "github.com/mitchellh/packer/template/interpolate" 20 ) 21 22 type Config struct { 23 common.PackerConfig `mapstructure:",squash"` 24 awscommon.AccessConfig `mapstructure:",squash"` 25 awscommon.RunConfig `mapstructure:",squash"` 26 27 VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"` 28 AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"` 29 30 ctx interpolate.Context 31 } 32 33 type Builder struct { 34 config Config 35 runner multistep.Runner 36 } 37 38 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 39 b.config.ctx.Funcs = awscommon.TemplateFuncs 40 err := config.Decode(&b.config, &config.DecodeOpts{ 41 Interpolate: true, 42 InterpolateContext: &b.config.ctx, 43 }, raws...) 44 if err != nil { 45 return nil, err 46 } 47 48 // Accumulate any errors 49 var errs *packer.MultiError 50 errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) 51 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) 52 53 if errs != nil && len(errs.Errors) > 0 { 54 return nil, errs 55 } 56 57 log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey)) 58 return nil, nil 59 } 60 61 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 62 config, err := b.config.Config() 63 if err != nil { 64 return nil, err 65 } 66 67 session, err := session.NewSession(config) 68 if err != nil { 69 return nil, errwrap.Wrapf("Error creating AWS Session: {{err}}", err) 70 } 71 72 ec2conn := ec2.New(session) 73 74 // If the subnet is specified but not the AZ, try to determine the AZ automatically 75 if b.config.SubnetId != "" && b.config.AvailabilityZone == "" { 76 log.Printf("[INFO] Finding AZ for the given subnet '%s'", b.config.SubnetId) 77 resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}}) 78 if err != nil { 79 return nil, err 80 } 81 b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone 82 log.Printf("[INFO] AZ found: '%s'", b.config.AvailabilityZone) 83 } 84 85 // Setup the state bag and initial state for the steps 86 state := new(multistep.BasicStateBag) 87 state.Put("config", b.config) 88 state.Put("ec2", ec2conn) 89 state.Put("hook", hook) 90 state.Put("ui", ui) 91 92 launchBlockDevices := commonBlockDevices(b.config.VolumeMappings) 93 94 var volumeIds *[]string 95 96 // Build the steps 97 steps := []multistep.Step{ 98 &awscommon.StepSourceAMIInfo{ 99 SourceAmi: b.config.SourceAmi, 100 EnhancedNetworking: b.config.AMIEnhancedNetworking, 101 AmiFilters: b.config.SourceAmiFilter, 102 }, 103 &awscommon.StepKeyPair{ 104 Debug: b.config.PackerDebug, 105 DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName), 106 KeyPairName: b.config.SSHKeyPairName, 107 TemporaryKeyPairName: b.config.TemporaryKeyPairName, 108 PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey, 109 }, 110 &awscommon.StepSecurityGroup{ 111 SecurityGroupIds: b.config.SecurityGroupIds, 112 CommConfig: &b.config.RunConfig.Comm, 113 VpcId: b.config.VpcId, 114 }, 115 &awscommon.StepRunSourceInstance{ 116 Debug: b.config.PackerDebug, 117 ExpectedRootDevice: "ebs", 118 SpotPrice: b.config.SpotPrice, 119 SpotPriceProduct: b.config.SpotPriceAutoProduct, 120 InstanceType: b.config.InstanceType, 121 UserData: b.config.UserData, 122 UserDataFile: b.config.UserDataFile, 123 SourceAMI: b.config.SourceAmi, 124 IamInstanceProfile: b.config.IamInstanceProfile, 125 SubnetId: b.config.SubnetId, 126 AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, 127 EbsOptimized: b.config.EbsOptimized, 128 AvailabilityZone: b.config.AvailabilityZone, 129 BlockDevices: launchBlockDevices, 130 Tags: b.config.RunTags, 131 InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, 132 }, 133 &stepTagEBSVolumes{ 134 VolumeMapping: b.config.VolumeMappings, 135 VolumeIDs: &volumeIds, 136 }, 137 &awscommon.StepGetPassword{ 138 Debug: b.config.PackerDebug, 139 Comm: &b.config.RunConfig.Comm, 140 Timeout: b.config.WindowsPasswordTimeout, 141 }, 142 &communicator.StepConnect{ 143 Config: &b.config.RunConfig.Comm, 144 Host: awscommon.SSHHost( 145 ec2conn, 146 b.config.SSHPrivateIp), 147 SSHConfig: awscommon.SSHConfig( 148 b.config.RunConfig.Comm.SSHAgentAuth, 149 b.config.RunConfig.Comm.SSHUsername, 150 b.config.RunConfig.Comm.SSHPassword), 151 }, 152 &common.StepProvision{}, 153 &awscommon.StepStopEBSBackedInstance{ 154 SpotPrice: b.config.SpotPrice, 155 DisableStopInstance: b.config.DisableStopInstance, 156 }, 157 &awscommon.StepModifyEBSBackedInstance{ 158 EnableEnhancedNetworking: b.config.AMIEnhancedNetworking, 159 }, 160 } 161 162 // Run! 163 b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) 164 b.runner.Run(state) 165 166 // If there was an error, return that 167 if rawErr, ok := state.GetOk("error"); ok { 168 return nil, rawErr.(error) 169 } 170 171 ui.Say(fmt.Sprintf("Created Volumes: %s", strings.Join(*volumeIds, ", "))) 172 return nil, nil 173 } 174 175 func (b *Builder) Cancel() { 176 if b.runner != nil { 177 log.Println("Cancelling the step runner...") 178 b.runner.Cancel() 179 } 180 }