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