github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/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/helper/multistep" 15 "github.com/hashicorp/packer/packer" 16 "github.com/hashicorp/packer/template/interpolate" 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 "spot_tags", 48 "ebs_volumes", 49 }, 50 }, 51 }, raws...) 52 if err != nil { 53 return nil, err 54 } 55 56 // Accumulate any errors 57 var errs *packer.MultiError 58 errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) 59 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) 60 errs = packer.MultiErrorAppend(errs, b.config.launchBlockDevices.Prepare(&b.config.ctx)...) 61 62 for _, d := range b.config.VolumeMappings { 63 if err := d.Prepare(&b.config.ctx); err != nil { 64 errs = packer.MultiErrorAppend(errs, fmt.Errorf("AMIMapping: %s", err.Error())) 65 } 66 } 67 68 b.config.launchBlockDevices, err = commonBlockDevices(b.config.VolumeMappings, &b.config.ctx) 69 if err != nil { 70 errs = packer.MultiErrorAppend(errs, err) 71 } 72 73 if b.config.IsSpotInstance() && (b.config.AMIENASupport || b.config.AMISriovNetSupport) { 74 errs = packer.MultiErrorAppend(errs, 75 fmt.Errorf("Spot instances do not support modification, which is required "+ 76 "when either `ena_support` or `sriov_support` are set. Please ensure "+ 77 "you use an AMI that already has either SR-IOV or ENA enabled.")) 78 } 79 80 if errs != nil && len(errs.Errors) > 0 { 81 return nil, errs 82 } 83 84 log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey, b.config.Token)) 85 return nil, nil 86 } 87 88 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 89 session, err := b.config.Session() 90 if err != nil { 91 return nil, err 92 } 93 ec2conn := ec2.New(session) 94 95 // If the subnet is specified but not the VpcId or AZ, try to determine them automatically 96 if b.config.SubnetId != "" && (b.config.AvailabilityZone == "" || b.config.VpcId == "") { 97 log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", b.config.SubnetId) 98 resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}}) 99 if err != nil { 100 return nil, err 101 } 102 if b.config.AvailabilityZone == "" { 103 b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone 104 log.Printf("[INFO] AvailabilityZone found: '%s'", b.config.AvailabilityZone) 105 } 106 if b.config.VpcId == "" { 107 b.config.VpcId = *resp.Subnets[0].VpcId 108 log.Printf("[INFO] VpcId found: '%s'", b.config.VpcId) 109 } 110 } 111 112 // Setup the state bag and initial state for the steps 113 state := new(multistep.BasicStateBag) 114 state.Put("config", b.config) 115 state.Put("ec2", ec2conn) 116 state.Put("hook", hook) 117 state.Put("ui", ui) 118 119 var instanceStep multistep.Step 120 121 if b.config.IsSpotInstance() { 122 instanceStep = &awscommon.StepRunSpotInstance{ 123 AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, 124 AvailabilityZone: b.config.AvailabilityZone, 125 BlockDevices: b.config.launchBlockDevices, 126 Ctx: b.config.ctx, 127 Debug: b.config.PackerDebug, 128 EbsOptimized: b.config.EbsOptimized, 129 ExpectedRootDevice: "ebs", 130 IamInstanceProfile: b.config.IamInstanceProfile, 131 InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, 132 InstanceType: b.config.InstanceType, 133 SourceAMI: b.config.SourceAmi, 134 SpotPrice: b.config.SpotPrice, 135 SpotPriceProduct: b.config.SpotPriceAutoProduct, 136 SpotTags: b.config.SpotTags, 137 SubnetId: b.config.SubnetId, 138 Tags: b.config.RunTags, 139 UserData: b.config.UserData, 140 UserDataFile: b.config.UserDataFile, 141 } 142 } else { 143 instanceStep = &awscommon.StepRunSourceInstance{ 144 AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, 145 AvailabilityZone: b.config.AvailabilityZone, 146 BlockDevices: b.config.launchBlockDevices, 147 Ctx: b.config.ctx, 148 Debug: b.config.PackerDebug, 149 EbsOptimized: b.config.EbsOptimized, 150 EnableT2Unlimited: b.config.EnableT2Unlimited, 151 ExpectedRootDevice: "ebs", 152 IamInstanceProfile: b.config.IamInstanceProfile, 153 InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, 154 InstanceType: b.config.InstanceType, 155 IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(), 156 SourceAMI: b.config.SourceAmi, 157 SubnetId: b.config.SubnetId, 158 Tags: b.config.RunTags, 159 UserData: b.config.UserData, 160 UserDataFile: b.config.UserDataFile, 161 } 162 } 163 164 // Build the steps 165 steps := []multistep.Step{ 166 &awscommon.StepSourceAMIInfo{ 167 SourceAmi: b.config.SourceAmi, 168 EnableAMISriovNetSupport: b.config.AMISriovNetSupport, 169 EnableAMIENASupport: b.config.AMIENASupport, 170 AmiFilters: b.config.SourceAmiFilter, 171 }, 172 &awscommon.StepKeyPair{ 173 Debug: b.config.PackerDebug, 174 SSHAgentAuth: b.config.Comm.SSHAgentAuth, 175 DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName), 176 KeyPairName: b.config.SSHKeyPairName, 177 TemporaryKeyPairName: b.config.TemporaryKeyPairName, 178 PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey, 179 }, 180 &awscommon.StepSecurityGroup{ 181 SecurityGroupIds: b.config.SecurityGroupIds, 182 CommConfig: &b.config.RunConfig.Comm, 183 VpcId: b.config.VpcId, 184 TemporarySGSourceCidr: b.config.TemporarySGSourceCidr, 185 }, 186 instanceStep, 187 &stepTagEBSVolumes{ 188 VolumeMapping: b.config.VolumeMappings, 189 Ctx: b.config.ctx, 190 }, 191 &awscommon.StepGetPassword{ 192 Debug: b.config.PackerDebug, 193 Comm: &b.config.RunConfig.Comm, 194 Timeout: b.config.WindowsPasswordTimeout, 195 BuildName: b.config.PackerBuildName, 196 }, 197 &communicator.StepConnect{ 198 Config: &b.config.RunConfig.Comm, 199 Host: awscommon.SSHHost( 200 ec2conn, 201 b.config.SSHInterface), 202 SSHConfig: awscommon.SSHConfig( 203 b.config.RunConfig.Comm.SSHAgentAuth, 204 b.config.RunConfig.Comm.SSHUsername, 205 b.config.RunConfig.Comm.SSHPassword), 206 }, 207 &common.StepProvision{}, 208 &awscommon.StepStopEBSBackedInstance{ 209 Skip: b.config.IsSpotInstance(), 210 DisableStopInstance: b.config.DisableStopInstance, 211 }, 212 &awscommon.StepModifyEBSBackedInstance{ 213 EnableAMISriovNetSupport: b.config.AMISriovNetSupport, 214 EnableAMIENASupport: b.config.AMIENASupport, 215 }, 216 } 217 218 // Run! 219 b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) 220 b.runner.Run(state) 221 222 // If there was an error, return that 223 if rawErr, ok := state.GetOk("error"); ok { 224 return nil, rawErr.(error) 225 } 226 227 // Build the artifact and return it 228 artifact := &Artifact{ 229 Volumes: state.Get("ebsvolumes").(EbsVolumes), 230 BuilderIdValue: BuilderId, 231 Conn: ec2conn, 232 } 233 ui.Say(fmt.Sprintf("Created Volumes: %s", artifact)) 234 return artifact, nil 235 } 236 237 func (b *Builder) Cancel() { 238 if b.runner != nil { 239 log.Println("Cancelling the step runner...") 240 b.runner.Cancel() 241 } 242 }