github.com/rahart/packer@v0.12.2-0.20161229105310-282bb6ad370f/builder/amazon/ebs/builder.go (about) 1 // The amazonebs package contains a packer.Builder implementation that 2 // builds AMIs for Amazon EC2. 3 // 4 // In general, there are two types of AMIs that can be created: ebs-backed or 5 // instance-store. This builder _only_ builds ebs-backed images. 6 package ebs 7 8 import ( 9 "fmt" 10 "log" 11 12 "github.com/aws/aws-sdk-go/aws/session" 13 "github.com/aws/aws-sdk-go/service/ec2" 14 "github.com/mitchellh/multistep" 15 awscommon "github.com/mitchellh/packer/builder/amazon/common" 16 "github.com/mitchellh/packer/common" 17 "github.com/mitchellh/packer/helper/communicator" 18 "github.com/mitchellh/packer/helper/config" 19 "github.com/mitchellh/packer/packer" 20 "github.com/mitchellh/packer/template/interpolate" 21 ) 22 23 // The unique ID for this builder 24 const BuilderId = "mitchellh.amazonebs" 25 26 type Config struct { 27 common.PackerConfig `mapstructure:",squash"` 28 awscommon.AccessConfig `mapstructure:",squash"` 29 awscommon.AMIConfig `mapstructure:",squash"` 30 awscommon.BlockDevices `mapstructure:",squash"` 31 awscommon.RunConfig `mapstructure:",squash"` 32 VolumeRunTags map[string]string `mapstructure:"run_volume_tags"` 33 34 ctx interpolate.Context 35 } 36 37 type Builder struct { 38 config Config 39 runner multistep.Runner 40 } 41 42 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 43 b.config.ctx.Funcs = awscommon.TemplateFuncs 44 err := config.Decode(&b.config, &config.DecodeOpts{ 45 Interpolate: true, 46 InterpolateContext: &b.config.ctx, 47 }, raws...) 48 if err != nil { 49 return nil, err 50 } 51 52 // Accumulate any errors 53 var errs *packer.MultiError 54 errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) 55 errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(&b.config.ctx)...) 56 errs = packer.MultiErrorAppend(errs, b.config.AMIConfig.Prepare(&b.config.ctx)...) 57 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) 58 59 if errs != nil && len(errs.Errors) > 0 { 60 return nil, errs 61 } 62 63 log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey)) 64 return nil, nil 65 } 66 67 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 68 config, err := b.config.Config() 69 if err != nil { 70 return nil, err 71 } 72 73 session, err := session.NewSession(config) 74 if err != nil { 75 return nil, err 76 } 77 ec2conn := ec2.New(session) 78 79 // If the subnet is specified but not the AZ, try to determine the AZ automatically 80 if b.config.SubnetId != "" && b.config.AvailabilityZone == "" { 81 log.Printf("[INFO] Finding AZ for the given subnet '%s'", b.config.SubnetId) 82 resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}}) 83 if err != nil { 84 return nil, err 85 } 86 b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone 87 log.Printf("[INFO] AZ found: '%s'", b.config.AvailabilityZone) 88 } 89 90 // Setup the state bag and initial state for the steps 91 state := new(multistep.BasicStateBag) 92 state.Put("config", b.config) 93 state.Put("ec2", ec2conn) 94 state.Put("hook", hook) 95 state.Put("ui", ui) 96 97 // Build the steps 98 steps := []multistep.Step{ 99 &awscommon.StepPreValidate{ 100 DestAmiName: b.config.AMIName, 101 ForceDeregister: b.config.AMIForceDeregister, 102 }, 103 &awscommon.StepSourceAMIInfo{ 104 SourceAmi: b.config.SourceAmi, 105 EnhancedNetworking: b.config.AMIEnhancedNetworking, 106 AmiFilters: b.config.SourceAmiFilter, 107 }, 108 &awscommon.StepKeyPair{ 109 Debug: b.config.PackerDebug, 110 SSHAgentAuth: b.config.Comm.SSHAgentAuth, 111 DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName), 112 KeyPairName: b.config.SSHKeyPairName, 113 TemporaryKeyPairName: b.config.TemporaryKeyPairName, 114 PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey, 115 }, 116 &awscommon.StepSecurityGroup{ 117 SecurityGroupIds: b.config.SecurityGroupIds, 118 CommConfig: &b.config.RunConfig.Comm, 119 VpcId: b.config.VpcId, 120 }, 121 &stepCleanupVolumes{ 122 BlockDevices: b.config.BlockDevices, 123 }, 124 &awscommon.StepRunSourceInstance{ 125 Debug: b.config.PackerDebug, 126 ExpectedRootDevice: "ebs", 127 SpotPrice: b.config.SpotPrice, 128 SpotPriceProduct: b.config.SpotPriceAutoProduct, 129 InstanceType: b.config.InstanceType, 130 UserData: b.config.UserData, 131 UserDataFile: b.config.UserDataFile, 132 SourceAMI: b.config.SourceAmi, 133 IamInstanceProfile: b.config.IamInstanceProfile, 134 SubnetId: b.config.SubnetId, 135 AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, 136 EbsOptimized: b.config.EbsOptimized, 137 AvailabilityZone: b.config.AvailabilityZone, 138 BlockDevices: b.config.BlockDevices, 139 Tags: b.config.RunTags, 140 InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, 141 }, 142 &stepTagEBSVolumes{ 143 VolumeRunTags: b.config.VolumeRunTags, 144 }, 145 &awscommon.StepGetPassword{ 146 Debug: b.config.PackerDebug, 147 Comm: &b.config.RunConfig.Comm, 148 Timeout: b.config.WindowsPasswordTimeout, 149 }, 150 &communicator.StepConnect{ 151 Config: &b.config.RunConfig.Comm, 152 Host: awscommon.SSHHost( 153 ec2conn, 154 b.config.SSHPrivateIp), 155 SSHConfig: awscommon.SSHConfig( 156 b.config.RunConfig.Comm.SSHAgentAuth, 157 b.config.RunConfig.Comm.SSHUsername, 158 b.config.RunConfig.Comm.SSHPassword), 159 }, 160 &common.StepProvision{}, 161 &awscommon.StepStopEBSBackedInstance{ 162 SpotPrice: b.config.SpotPrice, 163 DisableStopInstance: b.config.DisableStopInstance, 164 }, 165 &awscommon.StepModifyEBSBackedInstance{ 166 EnableEnhancedNetworking: b.config.AMIEnhancedNetworking, 167 }, 168 &awscommon.StepDeregisterAMI{ 169 ForceDeregister: b.config.AMIForceDeregister, 170 ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot, 171 AMIName: b.config.AMIName, 172 }, 173 &stepCreateAMI{}, 174 &stepCreateEncryptedAMICopy{}, 175 &awscommon.StepAMIRegionCopy{ 176 AccessConfig: &b.config.AccessConfig, 177 Regions: b.config.AMIRegions, 178 Name: b.config.AMIName, 179 }, 180 &awscommon.StepModifyAMIAttributes{ 181 Description: b.config.AMIDescription, 182 Users: b.config.AMIUsers, 183 Groups: b.config.AMIGroups, 184 ProductCodes: b.config.AMIProductCodes, 185 SnapshotUsers: b.config.SnapshotUsers, 186 SnapshotGroups: b.config.SnapshotGroups, 187 }, 188 &awscommon.StepCreateTags{ 189 Tags: b.config.AMITags, 190 SnapshotTags: b.config.SnapshotTags, 191 }, 192 } 193 194 // Run! 195 b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) 196 b.runner.Run(state) 197 198 // If there was an error, return that 199 if rawErr, ok := state.GetOk("error"); ok { 200 return nil, rawErr.(error) 201 } 202 203 // If there are no AMIs, then just return 204 if _, ok := state.GetOk("amis"); !ok { 205 return nil, nil 206 } 207 208 // Build the artifact and return it 209 artifact := &awscommon.Artifact{ 210 Amis: state.Get("amis").(map[string]string), 211 BuilderIdValue: BuilderId, 212 Conn: ec2conn, 213 } 214 215 return artifact, nil 216 } 217 218 func (b *Builder) Cancel() { 219 if b.runner != nil { 220 log.Println("Cancelling the step runner...") 221 b.runner.Cancel() 222 } 223 }