github.com/kimor79/packer@v0.8.7-0.20151221212622-d507b18eb4cf/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 33 ctx interpolate.Context 34 } 35 36 type Builder struct { 37 config Config 38 runner multistep.Runner 39 } 40 41 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 42 b.config.ctx.Funcs = awscommon.TemplateFuncs 43 err := config.Decode(&b.config, &config.DecodeOpts{ 44 Interpolate: true, 45 InterpolateContext: &b.config.ctx, 46 }, raws...) 47 if err != nil { 48 return nil, err 49 } 50 51 // Accumulate any errors 52 var errs *packer.MultiError 53 errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) 54 errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(&b.config.ctx)...) 55 errs = packer.MultiErrorAppend(errs, b.config.AMIConfig.Prepare(&b.config.ctx)...) 56 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) 57 58 if errs != nil && len(errs.Errors) > 0 { 59 return nil, errs 60 } 61 62 log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey)) 63 return nil, nil 64 } 65 66 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 67 config, err := b.config.Config() 68 if err != nil { 69 return nil, err 70 } 71 72 session := session.New(config) 73 ec2conn := ec2.New(session) 74 75 // Setup the state bag and initial state for the steps 76 state := new(multistep.BasicStateBag) 77 state.Put("config", b.config) 78 state.Put("ec2", ec2conn) 79 state.Put("hook", hook) 80 state.Put("ui", ui) 81 82 // Build the steps 83 steps := []multistep.Step{ 84 &awscommon.StepPreValidate{ 85 DestAmiName: b.config.AMIName, 86 ForceDeregister: b.config.AMIForceDeregister, 87 }, 88 &awscommon.StepSourceAMIInfo{ 89 SourceAmi: b.config.SourceAmi, 90 EnhancedNetworking: b.config.AMIEnhancedNetworking, 91 }, 92 &awscommon.StepKeyPair{ 93 Debug: b.config.PackerDebug, 94 DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName), 95 KeyPairName: b.config.SSHKeyPairName, 96 TemporaryKeyPairName: b.config.TemporaryKeyPairName, 97 PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey, 98 }, 99 &awscommon.StepSecurityGroup{ 100 SecurityGroupIds: b.config.SecurityGroupIds, 101 CommConfig: &b.config.RunConfig.Comm, 102 VpcId: b.config.VpcId, 103 }, 104 &stepCleanupVolumes{ 105 BlockDevices: b.config.BlockDevices, 106 }, 107 &awscommon.StepRunSourceInstance{ 108 Debug: b.config.PackerDebug, 109 ExpectedRootDevice: "ebs", 110 SpotPrice: b.config.SpotPrice, 111 SpotPriceProduct: b.config.SpotPriceAutoProduct, 112 InstanceType: b.config.InstanceType, 113 UserData: b.config.UserData, 114 UserDataFile: b.config.UserDataFile, 115 SourceAMI: b.config.SourceAmi, 116 IamInstanceProfile: b.config.IamInstanceProfile, 117 SubnetId: b.config.SubnetId, 118 AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, 119 EbsOptimized: b.config.EbsOptimized, 120 AvailabilityZone: b.config.AvailabilityZone, 121 BlockDevices: b.config.BlockDevices, 122 Tags: b.config.RunTags, 123 }, 124 &awscommon.StepGetPassword{ 125 Debug: b.config.PackerDebug, 126 Comm: &b.config.RunConfig.Comm, 127 Timeout: b.config.WindowsPasswordTimeout, 128 }, 129 &communicator.StepConnect{ 130 Config: &b.config.RunConfig.Comm, 131 Host: awscommon.SSHHost( 132 ec2conn, 133 b.config.SSHPrivateIp), 134 SSHConfig: awscommon.SSHConfig( 135 b.config.RunConfig.Comm.SSHUsername), 136 }, 137 &common.StepProvision{}, 138 &stepStopInstance{SpotPrice: b.config.SpotPrice}, 139 // TODO(mitchellh): verify works with spots 140 &stepModifyInstance{}, 141 &awscommon.StepDeregisterAMI{ 142 ForceDeregister: b.config.AMIForceDeregister, 143 AMIName: b.config.AMIName, 144 }, 145 &stepCreateAMI{}, 146 &awscommon.StepAMIRegionCopy{ 147 AccessConfig: &b.config.AccessConfig, 148 Regions: b.config.AMIRegions, 149 Name: b.config.AMIName, 150 }, 151 &awscommon.StepModifyAMIAttributes{ 152 Description: b.config.AMIDescription, 153 Users: b.config.AMIUsers, 154 Groups: b.config.AMIGroups, 155 }, 156 &awscommon.StepCreateTags{ 157 Tags: b.config.AMITags, 158 }, 159 } 160 161 // Run! 162 if b.config.PackerDebug { 163 b.runner = &multistep.DebugRunner{ 164 Steps: steps, 165 PauseFn: common.MultistepDebugFn(ui), 166 } 167 } else { 168 b.runner = &multistep.BasicRunner{Steps: steps} 169 } 170 171 b.runner.Run(state) 172 173 // If there was an error, return that 174 if rawErr, ok := state.GetOk("error"); ok { 175 return nil, rawErr.(error) 176 } 177 178 // If there are no AMIs, then just return 179 if _, ok := state.GetOk("amis"); !ok { 180 return nil, nil 181 } 182 183 // Build the artifact and return it 184 artifact := &awscommon.Artifact{ 185 Amis: state.Get("amis").(map[string]string), 186 BuilderIdValue: BuilderId, 187 Conn: ec2conn, 188 } 189 190 return artifact, nil 191 } 192 193 func (b *Builder) Cancel() { 194 if b.runner != nil { 195 log.Println("Cancelling the step runner...") 196 b.runner.Cancel() 197 } 198 }