github.com/rahart/packer@v0.12.2-0.20161229105310-282bb6ad370f/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 9 "github.com/aws/aws-sdk-go/aws/session" 10 "github.com/aws/aws-sdk-go/service/ec2" 11 "github.com/hashicorp/errwrap" 12 "github.com/mitchellh/multistep" 13 awscommon "github.com/mitchellh/packer/builder/amazon/common" 14 "github.com/mitchellh/packer/common" 15 "github.com/mitchellh/packer/helper/communicator" 16 "github.com/mitchellh/packer/helper/config" 17 "github.com/mitchellh/packer/packer" 18 "github.com/mitchellh/packer/template/interpolate" 19 ) 20 21 const BuilderId = "mitchellh.amazon.ebsvolume" 22 23 type Config struct { 24 common.PackerConfig `mapstructure:",squash"` 25 awscommon.AccessConfig `mapstructure:",squash"` 26 awscommon.RunConfig `mapstructure:",squash"` 27 28 VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"` 29 AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"` 30 31 ctx interpolate.Context 32 } 33 34 type Builder struct { 35 config Config 36 runner multistep.Runner 37 } 38 39 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 40 b.config.ctx.Funcs = awscommon.TemplateFuncs 41 err := config.Decode(&b.config, &config.DecodeOpts{ 42 Interpolate: true, 43 InterpolateContext: &b.config.ctx, 44 }, raws...) 45 if err != nil { 46 return nil, err 47 } 48 49 // Accumulate any errors 50 var errs *packer.MultiError 51 errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) 52 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) 53 54 if errs != nil && len(errs.Errors) > 0 { 55 return nil, errs 56 } 57 58 log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey)) 59 return nil, nil 60 } 61 62 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 63 config, err := b.config.Config() 64 if err != nil { 65 return nil, err 66 } 67 68 session, err := session.NewSession(config) 69 if err != nil { 70 return nil, errwrap.Wrapf("Error creating AWS Session: {{err}}", err) 71 } 72 73 ec2conn := ec2.New(session) 74 75 // If the subnet is specified but not the AZ, try to determine the AZ automatically 76 if b.config.SubnetId != "" && b.config.AvailabilityZone == "" { 77 log.Printf("[INFO] Finding AZ for the given subnet '%s'", b.config.SubnetId) 78 resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}}) 79 if err != nil { 80 return nil, err 81 } 82 b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone 83 log.Printf("[INFO] AZ found: '%s'", b.config.AvailabilityZone) 84 } 85 86 // Setup the state bag and initial state for the steps 87 state := new(multistep.BasicStateBag) 88 state.Put("config", b.config) 89 state.Put("ec2", ec2conn) 90 state.Put("hook", hook) 91 state.Put("ui", ui) 92 93 launchBlockDevices := commonBlockDevices(b.config.VolumeMappings) 94 95 // Build the steps 96 steps := []multistep.Step{ 97 &awscommon.StepSourceAMIInfo{ 98 SourceAmi: b.config.SourceAmi, 99 EnhancedNetworking: b.config.AMIEnhancedNetworking, 100 AmiFilters: b.config.SourceAmiFilter, 101 }, 102 &awscommon.StepKeyPair{ 103 Debug: b.config.PackerDebug, 104 SSHAgentAuth: b.config.Comm.SSHAgentAuth, 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 }, 136 &awscommon.StepGetPassword{ 137 Debug: b.config.PackerDebug, 138 Comm: &b.config.RunConfig.Comm, 139 Timeout: b.config.WindowsPasswordTimeout, 140 }, 141 &communicator.StepConnect{ 142 Config: &b.config.RunConfig.Comm, 143 Host: awscommon.SSHHost( 144 ec2conn, 145 b.config.SSHPrivateIp), 146 SSHConfig: awscommon.SSHConfig( 147 b.config.RunConfig.Comm.SSHAgentAuth, 148 b.config.RunConfig.Comm.SSHUsername, 149 b.config.RunConfig.Comm.SSHPassword), 150 }, 151 &common.StepProvision{}, 152 &awscommon.StepStopEBSBackedInstance{ 153 SpotPrice: b.config.SpotPrice, 154 DisableStopInstance: b.config.DisableStopInstance, 155 }, 156 &awscommon.StepModifyEBSBackedInstance{ 157 EnableEnhancedNetworking: b.config.AMIEnhancedNetworking, 158 }, 159 } 160 161 // Run! 162 b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) 163 b.runner.Run(state) 164 165 // If there was an error, return that 166 if rawErr, ok := state.GetOk("error"); ok { 167 return nil, rawErr.(error) 168 } 169 170 // Build the artifact and return it 171 artifact := &Artifact{ 172 Volumes: state.Get("ebsvolumes").(EbsVolumes), 173 BuilderIdValue: BuilderId, 174 Conn: ec2conn, 175 } 176 ui.Say(fmt.Sprintf("Created Volumes: %s", artifact)) 177 return artifact, nil 178 } 179 180 func (b *Builder) Cancel() { 181 if b.runner != nil { 182 log.Println("Cancelling the step runner...") 183 b.runner.Cancel() 184 } 185 }