github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/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/service/ec2" 10 awscommon "github.com/hashicorp/packer/builder/amazon/common" 11 "github.com/hashicorp/packer/common" 12 "github.com/hashicorp/packer/helper/communicator" 13 "github.com/hashicorp/packer/helper/config" 14 "github.com/hashicorp/packer/packer" 15 "github.com/hashicorp/packer/template/interpolate" 16 "github.com/mitchellh/multistep" 17 ) 18 19 const BuilderId = "mitchellh.amazon.ebsvolume" 20 21 type Config struct { 22 common.PackerConfig `mapstructure:",squash"` 23 awscommon.AccessConfig `mapstructure:",squash"` 24 awscommon.RunConfig `mapstructure:",squash"` 25 26 VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"` 27 AMIENASupport bool `mapstructure:"ena_support"` 28 AMISriovNetSupport bool `mapstructure:"sriov_support"` 29 30 launchBlockDevices awscommon.BlockDevices 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 InterpolateFilter: &interpolate.RenderFilter{ 45 Exclude: []string{ 46 "run_tags", 47 "ebs_volumes", 48 }, 49 }, 50 }, raws...) 51 if err != nil { 52 return nil, err 53 } 54 55 // Accumulate any errors 56 var errs *packer.MultiError 57 errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) 58 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) 59 60 b.config.launchBlockDevices, err = commonBlockDevices(b.config.VolumeMappings, &b.config.ctx) 61 if err != nil { 62 errs = packer.MultiErrorAppend(errs, err) 63 } 64 65 if errs != nil && len(errs.Errors) > 0 { 66 return nil, errs 67 } 68 69 log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey)) 70 return nil, nil 71 } 72 73 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 74 session, err := b.config.Session() 75 if err != nil { 76 return nil, err 77 } 78 ec2conn := ec2.New(session) 79 80 // If the subnet is specified but not the VpcId or AZ, try to determine them automatically 81 if b.config.SubnetId != "" && (b.config.AvailabilityZone == "" || b.config.VpcId == "") { 82 log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", b.config.SubnetId) 83 resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}}) 84 if err != nil { 85 return nil, err 86 } 87 if b.config.AvailabilityZone == "" { 88 b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone 89 log.Printf("[INFO] AvailabilityZone found: '%s'", b.config.AvailabilityZone) 90 } 91 if b.config.VpcId == "" { 92 b.config.VpcId = *resp.Subnets[0].VpcId 93 log.Printf("[INFO] VpcId found: '%s'", b.config.VpcId) 94 } 95 } 96 97 // Setup the state bag and initial state for the steps 98 state := new(multistep.BasicStateBag) 99 state.Put("config", b.config) 100 state.Put("ec2", ec2conn) 101 state.Put("hook", hook) 102 state.Put("ui", ui) 103 104 // Build the steps 105 steps := []multistep.Step{ 106 &awscommon.StepSourceAMIInfo{ 107 SourceAmi: b.config.SourceAmi, 108 EnableAMISriovNetSupport: b.config.AMISriovNetSupport, 109 EnableAMIENASupport: b.config.AMIENASupport, 110 AmiFilters: b.config.SourceAmiFilter, 111 }, 112 &awscommon.StepKeyPair{ 113 Debug: b.config.PackerDebug, 114 SSHAgentAuth: b.config.Comm.SSHAgentAuth, 115 DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName), 116 KeyPairName: b.config.SSHKeyPairName, 117 TemporaryKeyPairName: b.config.TemporaryKeyPairName, 118 PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey, 119 }, 120 &awscommon.StepSecurityGroup{ 121 SecurityGroupIds: b.config.SecurityGroupIds, 122 CommConfig: &b.config.RunConfig.Comm, 123 VpcId: b.config.VpcId, 124 }, 125 &awscommon.StepRunSourceInstance{ 126 Debug: b.config.PackerDebug, 127 ExpectedRootDevice: "ebs", 128 SpotPrice: b.config.SpotPrice, 129 SpotPriceProduct: b.config.SpotPriceAutoProduct, 130 InstanceType: b.config.InstanceType, 131 UserData: b.config.UserData, 132 UserDataFile: b.config.UserDataFile, 133 SourceAMI: b.config.SourceAmi, 134 IamInstanceProfile: b.config.IamInstanceProfile, 135 SubnetId: b.config.SubnetId, 136 AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, 137 EbsOptimized: b.config.EbsOptimized, 138 AvailabilityZone: b.config.AvailabilityZone, 139 BlockDevices: b.config.launchBlockDevices, 140 Tags: b.config.RunTags, 141 Ctx: b.config.ctx, 142 InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, 143 }, 144 &stepTagEBSVolumes{ 145 VolumeMapping: b.config.VolumeMappings, 146 Ctx: b.config.ctx, 147 }, 148 &awscommon.StepGetPassword{ 149 Debug: b.config.PackerDebug, 150 Comm: &b.config.RunConfig.Comm, 151 Timeout: b.config.WindowsPasswordTimeout, 152 }, 153 &communicator.StepConnect{ 154 Config: &b.config.RunConfig.Comm, 155 Host: awscommon.SSHHost( 156 ec2conn, 157 b.config.SSHPrivateIp), 158 SSHConfig: awscommon.SSHConfig( 159 b.config.RunConfig.Comm.SSHAgentAuth, 160 b.config.RunConfig.Comm.SSHUsername, 161 b.config.RunConfig.Comm.SSHPassword), 162 }, 163 &common.StepProvision{}, 164 &awscommon.StepStopEBSBackedInstance{ 165 SpotPrice: b.config.SpotPrice, 166 DisableStopInstance: b.config.DisableStopInstance, 167 }, 168 &awscommon.StepModifyEBSBackedInstance{ 169 EnableAMISriovNetSupport: b.config.AMISriovNetSupport, 170 EnableAMIENASupport: b.config.AMIENASupport, 171 }, 172 } 173 174 // Run! 175 b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) 176 b.runner.Run(state) 177 178 // If there was an error, return that 179 if rawErr, ok := state.GetOk("error"); ok { 180 return nil, rawErr.(error) 181 } 182 183 // Build the artifact and return it 184 artifact := &Artifact{ 185 Volumes: state.Get("ebsvolumes").(EbsVolumes), 186 BuilderIdValue: BuilderId, 187 Conn: ec2conn, 188 } 189 ui.Say(fmt.Sprintf("Created Volumes: %s", artifact)) 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 }