github.phpd.cn/hashicorp/packer@v1.3.2/builder/amazon/ebssurrogate/builder.go (about) 1 // The ebssurrogate package contains a packer.Builder implementation that 2 // builds a new EBS-backed AMI using an ephemeral instance. 3 package ebssurrogate 4 5 import ( 6 "errors" 7 "fmt" 8 "log" 9 10 "github.com/aws/aws-sdk-go/service/ec2" 11 awscommon "github.com/hashicorp/packer/builder/amazon/common" 12 "github.com/hashicorp/packer/common" 13 "github.com/hashicorp/packer/helper/communicator" 14 "github.com/hashicorp/packer/helper/config" 15 "github.com/hashicorp/packer/helper/multistep" 16 "github.com/hashicorp/packer/packer" 17 "github.com/hashicorp/packer/template/interpolate" 18 ) 19 20 const BuilderId = "mitchellh.amazon.ebssurrogate" 21 22 type Config struct { 23 common.PackerConfig `mapstructure:",squash"` 24 awscommon.AccessConfig `mapstructure:",squash"` 25 awscommon.RunConfig `mapstructure:",squash"` 26 awscommon.BlockDevices `mapstructure:",squash"` 27 awscommon.AMIConfig `mapstructure:",squash"` 28 29 RootDevice RootBlockDevice `mapstructure:"ami_root_device"` 30 VolumeRunTags awscommon.TagMap `mapstructure:"run_volume_tags"` 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 InterpolateFilter: &interpolate.RenderFilter{ 46 Exclude: []string{ 47 "ami_description", 48 "run_tags", 49 "run_volume_tags", 50 "snapshot_tags", 51 "spot_tags", 52 "tags", 53 }, 54 }, 55 }, raws...) 56 if err != nil { 57 return nil, err 58 } 59 60 if b.config.PackerConfig.PackerForce { 61 b.config.AMIForceDeregister = true 62 } 63 64 // Accumulate any errors 65 var errs *packer.MultiError 66 errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...) 67 errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) 68 errs = packer.MultiErrorAppend(errs, 69 b.config.AMIConfig.Prepare(&b.config.AccessConfig, &b.config.ctx)...) 70 errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(&b.config.ctx)...) 71 errs = packer.MultiErrorAppend(errs, b.config.RootDevice.Prepare(&b.config.ctx)...) 72 73 if b.config.AMIVirtType == "" { 74 errs = packer.MultiErrorAppend(errs, errors.New("ami_virtualization_type is required.")) 75 } 76 77 foundRootVolume := false 78 for _, launchDevice := range b.config.BlockDevices.LaunchMappings { 79 if launchDevice.DeviceName == b.config.RootDevice.SourceDeviceName { 80 foundRootVolume = true 81 } 82 } 83 84 if !foundRootVolume { 85 errs = packer.MultiErrorAppend(errs, fmt.Errorf("no volume with name '%s' is found", b.config.RootDevice.SourceDeviceName)) 86 } 87 88 if b.config.IsSpotInstance() && ((b.config.AMIENASupport != nil && *b.config.AMIENASupport) || b.config.AMISriovNetSupport) { 89 errs = packer.MultiErrorAppend(errs, 90 fmt.Errorf("Spot instances do not support modification, which is required "+ 91 "when either `ena_support` or `sriov_support` are set. Please ensure "+ 92 "you use an AMI that already has either SR-IOV or ENA enabled.")) 93 } 94 95 if errs != nil && len(errs.Errors) > 0 { 96 return nil, errs 97 } 98 99 packer.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey, b.config.Token) 100 return nil, nil 101 } 102 103 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 104 session, err := b.config.Session() 105 if err != nil { 106 return nil, err 107 } 108 ec2conn := ec2.New(session) 109 110 // Setup the state bag and initial state for the steps 111 state := new(multistep.BasicStateBag) 112 state.Put("config", &b.config) 113 state.Put("ec2", ec2conn) 114 state.Put("awsSession", session) 115 state.Put("hook", hook) 116 state.Put("ui", ui) 117 118 var instanceStep multistep.Step 119 120 if b.config.IsSpotInstance() { 121 instanceStep = &awscommon.StepRunSpotInstance{ 122 AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, 123 BlockDevices: b.config.BlockDevices, 124 BlockDurationMinutes: b.config.BlockDurationMinutes, 125 Ctx: b.config.ctx, 126 Comm: &b.config.RunConfig.Comm, 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 Tags: b.config.RunTags, 138 UserData: b.config.UserData, 139 UserDataFile: b.config.UserDataFile, 140 VolumeTags: b.config.VolumeRunTags, 141 } 142 } else { 143 instanceStep = &awscommon.StepRunSourceInstance{ 144 AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, 145 BlockDevices: b.config.BlockDevices, 146 Comm: &b.config.RunConfig.Comm, 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 Tags: b.config.RunTags, 158 UserData: b.config.UserData, 159 UserDataFile: b.config.UserDataFile, 160 VolumeTags: b.config.VolumeRunTags, 161 } 162 } 163 164 amiDevices := b.config.BuildAMIDevices() 165 launchDevices := b.config.BuildLaunchDevices() 166 167 // Build the steps 168 steps := []multistep.Step{ 169 &awscommon.StepPreValidate{ 170 DestAmiName: b.config.AMIName, 171 ForceDeregister: b.config.AMIForceDeregister, 172 }, 173 &awscommon.StepSourceAMIInfo{ 174 SourceAmi: b.config.SourceAmi, 175 EnableAMISriovNetSupport: b.config.AMISriovNetSupport, 176 EnableAMIENASupport: b.config.AMIENASupport, 177 AmiFilters: b.config.SourceAmiFilter, 178 AMIVirtType: b.config.AMIVirtType, 179 }, 180 &awscommon.StepNetworkInfo{ 181 VpcId: b.config.VpcId, 182 VpcFilter: b.config.VpcFilter, 183 SecurityGroupIds: b.config.SecurityGroupIds, 184 SecurityGroupFilter: b.config.SecurityGroupFilter, 185 SubnetId: b.config.SubnetId, 186 SubnetFilter: b.config.SubnetFilter, 187 AvailabilityZone: b.config.AvailabilityZone, 188 }, 189 &awscommon.StepKeyPair{ 190 Debug: b.config.PackerDebug, 191 Comm: &b.config.RunConfig.Comm, 192 DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName), 193 }, 194 &awscommon.StepSecurityGroup{ 195 SecurityGroupFilter: b.config.SecurityGroupFilter, 196 SecurityGroupIds: b.config.SecurityGroupIds, 197 CommConfig: &b.config.RunConfig.Comm, 198 TemporarySGSourceCidr: b.config.TemporarySGSourceCidr, 199 }, 200 &awscommon.StepCleanupVolumes{ 201 BlockDevices: b.config.BlockDevices, 202 }, 203 instanceStep, 204 &awscommon.StepGetPassword{ 205 Debug: b.config.PackerDebug, 206 Comm: &b.config.RunConfig.Comm, 207 Timeout: b.config.WindowsPasswordTimeout, 208 BuildName: b.config.PackerBuildName, 209 }, 210 &communicator.StepConnect{ 211 Config: &b.config.RunConfig.Comm, 212 Host: awscommon.SSHHost( 213 ec2conn, 214 b.config.Comm.SSHInterface), 215 SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(), 216 }, 217 &common.StepProvision{}, 218 &common.StepCleanupTempKeys{ 219 Comm: &b.config.RunConfig.Comm, 220 }, 221 &awscommon.StepStopEBSBackedInstance{ 222 Skip: b.config.IsSpotInstance(), 223 DisableStopInstance: b.config.DisableStopInstance, 224 }, 225 &awscommon.StepModifyEBSBackedInstance{ 226 EnableAMISriovNetSupport: b.config.AMISriovNetSupport, 227 EnableAMIENASupport: b.config.AMIENASupport, 228 }, 229 &StepSnapshotVolumes{ 230 LaunchDevices: launchDevices, 231 }, 232 &awscommon.StepDeregisterAMI{ 233 AccessConfig: &b.config.AccessConfig, 234 ForceDeregister: b.config.AMIForceDeregister, 235 ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot, 236 AMIName: b.config.AMIName, 237 Regions: b.config.AMIRegions, 238 }, 239 &StepRegisterAMI{ 240 RootDevice: b.config.RootDevice, 241 AMIDevices: amiDevices, 242 LaunchDevices: launchDevices, 243 EnableAMISriovNetSupport: b.config.AMISriovNetSupport, 244 EnableAMIENASupport: b.config.AMIENASupport, 245 }, 246 &awscommon.StepCreateEncryptedAMICopy{ 247 KeyID: b.config.AMIKmsKeyId, 248 EncryptBootVolume: b.config.AMIEncryptBootVolume, 249 Name: b.config.AMIName, 250 }, 251 &awscommon.StepAMIRegionCopy{ 252 AccessConfig: &b.config.AccessConfig, 253 Regions: b.config.AMIRegions, 254 RegionKeyIds: b.config.AMIRegionKMSKeyIDs, 255 EncryptBootVolume: b.config.AMIEncryptBootVolume, 256 Name: b.config.AMIName, 257 }, 258 &awscommon.StepModifyAMIAttributes{ 259 Description: b.config.AMIDescription, 260 Users: b.config.AMIUsers, 261 Groups: b.config.AMIGroups, 262 ProductCodes: b.config.AMIProductCodes, 263 SnapshotUsers: b.config.SnapshotUsers, 264 SnapshotGroups: b.config.SnapshotGroups, 265 Ctx: b.config.ctx, 266 }, 267 &awscommon.StepCreateTags{ 268 Tags: b.config.AMITags, 269 SnapshotTags: b.config.SnapshotTags, 270 Ctx: b.config.ctx, 271 }, 272 } 273 274 // Run! 275 b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) 276 b.runner.Run(state) 277 278 // If there was an error, return that 279 if rawErr, ok := state.GetOk("error"); ok { 280 return nil, rawErr.(error) 281 } 282 283 if amis, ok := state.GetOk("amis"); ok { 284 // Build the artifact and return it 285 artifact := &awscommon.Artifact{ 286 Amis: amis.(map[string]string), 287 BuilderIdValue: BuilderId, 288 Session: session, 289 } 290 291 return artifact, nil 292 } 293 294 return nil, nil 295 } 296 297 func (b *Builder) Cancel() { 298 if b.runner != nil { 299 log.Println("Cancelling the step runner...") 300 b.runner.Cancel() 301 } 302 }