github.com/daniellockard/packer@v0.7.6-0.20141210173435-5a9390934716/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/mitchellh/goamz/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/packer" 17 ) 18 19 // The unique ID for this builder 20 const BuilderId = "mitchellh.amazonebs" 21 22 type config struct { 23 common.PackerConfig `mapstructure:",squash"` 24 awscommon.AccessConfig `mapstructure:",squash"` 25 awscommon.AMIConfig `mapstructure:",squash"` 26 awscommon.BlockDevices `mapstructure:",squash"` 27 awscommon.RunConfig `mapstructure:",squash"` 28 29 tpl *packer.ConfigTemplate 30 } 31 32 type Builder struct { 33 config config 34 runner multistep.Runner 35 } 36 37 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 38 md, err := common.DecodeConfig(&b.config, raws...) 39 if err != nil { 40 return nil, err 41 } 42 43 b.config.tpl, err = packer.NewConfigTemplate() 44 if err != nil { 45 return nil, err 46 } 47 b.config.tpl.UserVars = b.config.PackerUserVars 48 b.config.tpl.Funcs(awscommon.TemplateFuncs) 49 50 // Accumulate any errors 51 errs := common.CheckUnusedConfig(md) 52 errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(b.config.tpl)...) 53 errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(b.config.tpl)...) 54 errs = packer.MultiErrorAppend(errs, b.config.AMIConfig.Prepare(b.config.tpl)...) 55 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(b.config.tpl)...) 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 region, err := b.config.Region() 67 if err != nil { 68 return nil, err 69 } 70 71 auth, err := b.config.AccessConfig.Auth() 72 if err != nil { 73 return nil, err 74 } 75 76 ec2conn := ec2.New(auth, region) 77 78 // Setup the state bag and initial state for the steps 79 state := new(multistep.BasicStateBag) 80 state.Put("config", b.config) 81 state.Put("ec2", ec2conn) 82 state.Put("hook", hook) 83 state.Put("ui", ui) 84 85 // Build the steps 86 steps := []multistep.Step{ 87 &awscommon.StepSourceAMIInfo{ 88 SourceAmi: b.config.SourceAmi, 89 EnhancedNetworking: b.config.AMIEnhancedNetworking, 90 }, 91 &awscommon.StepKeyPair{ 92 Debug: b.config.PackerDebug, 93 DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName), 94 KeyPairName: b.config.TemporaryKeyPairName, 95 PrivateKeyFile: b.config.SSHPrivateKeyFile, 96 }, 97 &awscommon.StepSecurityGroup{ 98 SecurityGroupIds: b.config.SecurityGroupIds, 99 SSHPort: b.config.SSHPort, 100 VpcId: b.config.VpcId, 101 }, 102 &awscommon.StepRunSourceInstance{ 103 Debug: b.config.PackerDebug, 104 ExpectedRootDevice: "ebs", 105 SpotPrice: b.config.SpotPrice, 106 SpotPriceProduct: b.config.SpotPriceAutoProduct, 107 InstanceType: b.config.InstanceType, 108 UserData: b.config.UserData, 109 UserDataFile: b.config.UserDataFile, 110 SourceAMI: b.config.SourceAmi, 111 IamInstanceProfile: b.config.IamInstanceProfile, 112 SubnetId: b.config.SubnetId, 113 AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, 114 AvailabilityZone: b.config.AvailabilityZone, 115 BlockDevices: b.config.BlockDevices, 116 Tags: b.config.RunTags, 117 }, 118 &common.StepConnectSSH{ 119 SSHAddress: awscommon.SSHAddress( 120 ec2conn, b.config.SSHPort, b.config.SSHPrivateIp), 121 SSHConfig: awscommon.SSHConfig(b.config.SSHUsername), 122 SSHWaitTimeout: b.config.SSHTimeout(), 123 }, 124 &common.StepProvision{}, 125 &stepStopInstance{SpotPrice: b.config.SpotPrice}, 126 // TODO(mitchellh): verify works with spots 127 &stepModifyInstance{}, 128 &stepCreateAMI{}, 129 &awscommon.StepAMIRegionCopy{ 130 Regions: b.config.AMIRegions, 131 }, 132 &awscommon.StepModifyAMIAttributes{ 133 Description: b.config.AMIDescription, 134 Users: b.config.AMIUsers, 135 Groups: b.config.AMIGroups, 136 }, 137 &awscommon.StepCreateTags{ 138 Tags: b.config.AMITags, 139 }, 140 } 141 142 // Run! 143 if b.config.PackerDebug { 144 b.runner = &multistep.DebugRunner{ 145 Steps: steps, 146 PauseFn: common.MultistepDebugFn(ui), 147 } 148 } else { 149 b.runner = &multistep.BasicRunner{Steps: steps} 150 } 151 152 b.runner.Run(state) 153 154 // If there was an error, return that 155 if rawErr, ok := state.GetOk("error"); ok { 156 return nil, rawErr.(error) 157 } 158 159 // If there are no AMIs, then just return 160 if _, ok := state.GetOk("amis"); !ok { 161 return nil, nil 162 } 163 164 // Build the artifact and return it 165 artifact := &awscommon.Artifact{ 166 Amis: state.Get("amis").(map[string]string), 167 BuilderIdValue: BuilderId, 168 Conn: ec2conn, 169 } 170 171 return artifact, nil 172 } 173 174 func (b *Builder) Cancel() { 175 if b.runner != nil { 176 log.Println("Cancelling the step runner...") 177 b.runner.Cancel() 178 } 179 }