func (s *stepStopInstance) Run(state map[string]interface{}) multistep.StepAction { ec2conn := state["ec2"].(*ec2.EC2) instance := state["instance"].(*ec2.Instance) ui := state["ui"].(packer.Ui) // Stop the instance so we can create an AMI from it ui.Say("Stopping the source instance...") _, err := ec2conn.StopInstances(instance.InstanceId) if err != nil { err := fmt.Errorf("Error stopping instance: %s", err) state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } // Wait for the instance to actual stop ui.Say("Waiting for the instance to stop...") instance, err = awscommon.WaitForState(ec2conn, instance, []string{"running", "stopping"}, "stopped") if err != nil { err := fmt.Errorf("Error waiting for instance to stop: %s", err) state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } return multistep.ActionContinue }
func (s *stepStopInstance) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) // Stop the instance so we can create an AMI from it ui.Say("Stopping the source instance...") _, err := ec2conn.StopInstances(instance.InstanceId) if err != nil { err := fmt.Errorf("Error stopping instance: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Wait for the instance to actual stop ui.Say("Waiting for the instance to stop...") stateChange := awscommon.StateChangeConf{ Conn: ec2conn, Pending: []string{"running", "stopping"}, Target: "stopped", Refresh: awscommon.InstanceStateRefreshFunc(ec2conn, instance), StepState: state, } _, err = awscommon.WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for instance to stop: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } return multistep.ActionContinue }
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) ec2conn := state.Get("ec2").(*ec2.EC2) image := state.Get("source_image").(*ec2.Image) snapshotId := state.Get("snapshot_id").(string) ui := state.Get("ui").(packer.Ui) ui.Say("Registering the AMI...") blockDevices := make([]*ec2.BlockDeviceMapping, len(image.BlockDeviceMappings)) for i, device := range image.BlockDeviceMappings { newDevice := device if newDevice.DeviceName == image.RootDeviceName { if newDevice.EBS != nil { newDevice.EBS.SnapshotID = &snapshotId } else { newDevice.EBS = &ec2.EBSBlockDevice{SnapshotID: &snapshotId} } } blockDevices[i] = newDevice } registerOpts := buildRegisterOpts(config, image, blockDevices) // Set SriovNetSupport to "simple". See http://goo.gl/icuXh5 if config.AMIEnhancedNetworking { registerOpts.SRIOVNetSupport = aws.String("simple") } registerResp, err := ec2conn.RegisterImage(registerOpts) if err != nil { state.Put("error", fmt.Errorf("Error registering AMI: %s", err)) ui.Error(state.Get("error").(error).Error()) return multistep.ActionHalt } // Set the AMI ID in the state ui.Say(fmt.Sprintf("AMI: %s", *registerResp.ImageID)) amis := make(map[string]string) amis[ec2conn.Config.Region] = *registerResp.ImageID state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Pending: []string{"pending"}, Target: "available", Refresh: awscommon.AMIStateRefreshFunc(ec2conn, *registerResp.ImageID), StepState: state, } ui.Say("Waiting for AMI to become ready...") if _, err := awscommon.WaitForState(&stateChange); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } return multistep.ActionContinue }
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) ec2conn := state.Get("ec2").(ec2.EC2) image := state.Get("source_image").(*ec2.Image) snapshotId := state.Get("snapshot_id").(string) ui := state.Get("ui").(packer.Ui) ui.Say("Registering the AMI...") blockDevices := make([]ec2.BlockDeviceMapping, len(image.BlockDevices)) for i, device := range image.BlockDevices { newDevice := device if newDevice.DeviceName == image.RootDeviceName { newDevice.SnapshotId = snapshotId } blockDevices[i] = newDevice } registerOpts := &ec2.RegisterImage{ Name: config.AMIName, Architecture: image.Architecture, KernelId: image.KernelId, RamdiskId: image.RamdiskId, RootDeviceName: image.RootDeviceName, BlockDevices: blockDevices, } registerResp, err := ec2conn.RegisterImage(registerOpts) if err != nil { state.Put("error", fmt.Errorf("Error registering AMI: %s", err)) ui.Error(state.Get("error").(error).Error()) return multistep.ActionHalt } // Set the AMI ID in the state ui.Say(fmt.Sprintf("AMI: %s", registerResp.ImageId)) amis := make(map[string]string) amis[ec2conn.Region.Name] = registerResp.ImageId state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Conn: ec2conn, Pending: []string{"pending"}, Target: "available", Refresh: awscommon.AMIStateRefreshFunc(ec2conn, registerResp.ImageId), StepState: state, } ui.Say("Waiting for AMI to become ready...") if _, err := awscommon.WaitForState(&stateChange); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } return multistep.ActionContinue }
func (s *StepAttachVolume) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) device := state.Get("device").(string) instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) volumeId := state.Get("volume_id").(string) // For the API call, it expects "sd" prefixed devices. attachVolume := strings.Replace(device, "/xvd", "/sd", 1) ui.Say(fmt.Sprintf("Attaching the root volume to %s", attachVolume)) _, err := ec2conn.AttachVolume(volumeId, instance.InstanceId, attachVolume) if err != nil { err := fmt.Errorf("Error attaching volume: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Mark that we attached it so we can detach it later s.attached = true s.volumeId = volumeId // Wait for the volume to become attached stateChange := awscommon.StateChangeConf{ Conn: ec2conn, Pending: []string{"attaching"}, StepState: state, Target: "attached", Refresh: func() (interface{}, string, error) { resp, err := ec2conn.Volumes([]string{volumeId}, ec2.NewFilter()) if err != nil { return nil, "", err } if len(resp.Volumes[0].Attachments) == 0 { return nil, "", errors.New("No attachments on volume.") } a := resp.Volumes[0].Attachments[0] return a, a.Status, nil }, } _, err = awscommon.WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for volume: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } state.Put("attach_cleanup", s) return multistep.ActionContinue }
func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(Config) ec2conn := state.Get("ec2").(*ec2.EC2) instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) // Create the image ui.Say(fmt.Sprintf("Creating the AMI: %s", config.AMIName)) createOpts := &ec2.CreateImageInput{ InstanceID: instance.InstanceID, Name: &config.AMIName, BlockDeviceMappings: config.BlockDevices.BuildAMIDevices(), } createResp, err := ec2conn.CreateImage(createOpts) if err != nil { err := fmt.Errorf("Error creating AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Set the AMI ID in the state ui.Message(fmt.Sprintf("AMI: %s", *createResp.ImageID)) amis := make(map[string]string) amis[*ec2conn.Config.Region] = *createResp.ImageID state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Pending: []string{"pending"}, Target: "available", Refresh: awscommon.AMIStateRefreshFunc(ec2conn, *createResp.ImageID), StepState: state, } ui.Say("Waiting for AMI to become ready...") if _, err := awscommon.WaitForState(&stateChange); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIDs: []*string{createResp.ImageID}}) if err != nil { err := fmt.Errorf("Error searching for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } s.image = imagesResp.Images[0] return multistep.ActionContinue }
func (s *StepSnapshot) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) volumeId := state.Get("volume_id").(string) ui.Say("Creating snapshot...") description := fmt.Sprintf("Packer: %s", time.Now().String()) createSnapResp, err := ec2conn.CreateSnapshot(&ec2.CreateSnapshotInput{ VolumeId: &volumeId, Description: &description, }) if err != nil { err := fmt.Errorf("Error creating snapshot: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Set the snapshot ID so we can delete it later s.snapshotId = *createSnapResp.SnapshotId ui.Message(fmt.Sprintf("Snapshot ID: %s", s.snapshotId)) // Wait for the snapshot to be ready stateChange := awscommon.StateChangeConf{ Pending: []string{"pending"}, StepState: state, Target: "completed", Refresh: func() (interface{}, string, error) { resp, err := ec2conn.DescribeSnapshots(&ec2.DescribeSnapshotsInput{SnapshotIds: []*string{&s.snapshotId}}) if err != nil { return nil, "", err } if len(resp.Snapshots) == 0 { return nil, "", errors.New("No snapshots found.") } s := resp.Snapshots[0] return s, *s.State, nil }, } _, err = awscommon.WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for snapshot: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } state.Put("snapshot_id", s.snapshotId) return multistep.ActionContinue }
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) ec2conn := state.Get("ec2").(*ec2.EC2) manifestPath := state.Get("remote_manifest_path").(string) ui := state.Get("ui").(packer.Ui) ui.Say("Registering the AMI...") registerOpts := &ec2.RegisterImageInput{ ImageLocation: &manifestPath, Name: aws.String(config.AMIName), BlockDeviceMappings: config.BlockDevices.BuildAMIDevices(), } if config.AMIVirtType != "" { registerOpts.VirtualizationType = aws.String(config.AMIVirtType) } // Set SriovNetSupport to "simple". See http://goo.gl/icuXh5 if config.AMIEnhancedNetworking { registerOpts.SriovNetSupport = aws.String("simple") } registerResp, err := ec2conn.RegisterImage(registerOpts) if err != nil { state.Put("error", fmt.Errorf("Error registering AMI: %s", err)) ui.Error(state.Get("error").(error).Error()) return multistep.ActionHalt } // Set the AMI ID in the state ui.Say(fmt.Sprintf("AMI: %s", *registerResp.ImageId)) amis := make(map[string]string) amis[*ec2conn.Config.Region] = *registerResp.ImageId state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Pending: []string{"pending"}, Target: "available", Refresh: awscommon.AMIStateRefreshFunc(ec2conn, *registerResp.ImageId), StepState: state, } ui.Say("Waiting for AMI to become ready...") if _, err := awscommon.WaitForState(&stateChange); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } return multistep.ActionContinue }
func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) { ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) // Cancel the spot request if it exists if s.spotRequest != nil { ui.Say("Cancelling the spot request...") if _, err := ec2conn.CancelSpotRequests([]string{s.spotRequest.SpotRequestId}); err != nil { ui.Error(fmt.Sprintf("Error cancelling the spot request, may still be around: %s", err)) return } stateChange := awscommon.StateChangeConf{ Pending: []string{"active", "open"}, Refresh: awscommon.SpotRequestStateRefreshFunc(ec2conn, s.spotRequest.SpotRequestId), Target: "cancelled", } awscommon.WaitForState(&stateChange) } // Terminate the source instance if it exists if s.instance != nil { ui.Say("Terminating the source AWS instance...") if _, err := ec2conn.TerminateInstances([]string{s.instance.InstanceId}); err != nil { ui.Error(fmt.Sprintf("Error terminating instance, may still be around: %s", err)) return } stateChange := awscommon.StateChangeConf{ Pending: []string{"pending", "running", "shutting-down", "stopped", "stopping"}, Refresh: awscommon.InstanceStateRefreshFunc(ec2conn, s.instance), Target: "terminated", } awscommon.WaitForState(&stateChange) } }
func (s *StepSnapshot) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(ec2.EC2) ui := state.Get("ui").(packer.Ui) volumeId := state.Get("volume_id").(string) ui.Say("Creating snapshot...") createSnapResp, err := ec2conn.CreateSnapshot(volumeId, "") if err != nil { err := fmt.Errorf("Error creating snapshot: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Set the snapshot ID so we can delete it later s.snapshotId = createSnapResp.Id ui.Message(fmt.Sprintf("Snapshot ID: %s", s.snapshotId)) // Wait for the snapshot to be ready stateChange := awscommon.StateChangeConf{ Conn: ec2conn, Pending: []string{"pending"}, StepState: state, Target: "completed", Refresh: func() (interface{}, string, error) { resp, err := ec2conn.Snapshots([]string{s.snapshotId}, ec2.NewFilter()) if err != nil { return nil, "", err } if len(resp.Snapshots) == 0 { return nil, "", errors.New("No snapshots found.") } s := resp.Snapshots[0] return s, s.Status, nil }, } _, err = awscommon.WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for snapshot: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } state.Put("snapshot_id", s.snapshotId) return multistep.ActionContinue }
func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(config) ec2conn := state.Get("ec2").(*ec2.EC2) instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) // Create the image ui.Say(fmt.Sprintf("Creating the AMI: %s", config.AMIName)) createOpts := &ec2.CreateImage{ InstanceId: instance.InstanceId, Name: config.AMIName, BlockDevices: config.BlockDevices.BuildAMIDevices(), } createResp, err := ec2conn.CreateImage(createOpts) if err != nil { err := fmt.Errorf("Error creating AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Set the AMI ID in the state ui.Say(fmt.Sprintf("AMI: %s", createResp.ImageId)) amis := make(map[string]string) amis[ec2conn.Region.Name] = createResp.ImageId state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Conn: ec2conn, Pending: []string{"pending"}, Target: "available", Refresh: awscommon.AMIStateRefreshFunc(ec2conn, createResp.ImageId), StepState: state, } ui.Say("Waiting for AMI to become ready...") if _, err := awscommon.WaitForState(&stateChange); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } return multistep.ActionContinue }
func (s *stepStopInstance) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) // Skip when it is a spot instance if s.SpotPrice != "" && s.SpotPrice != "0" { return multistep.ActionContinue } var err error if !s.DisableStopInstance { // Stop the instance so we can create an AMI from it ui.Say("Stopping the source instance...") _, err = ec2conn.StopInstances(&ec2.StopInstancesInput{ InstanceIds: []*string{instance.InstanceId}, }) if err != nil { err := fmt.Errorf("Error stopping instance: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } } else { ui.Say("Automatic instance stop disabled. Please stop instance manually.") } // Wait for the instance to actual stop ui.Say("Waiting for the instance to stop...") stateChange := awscommon.StateChangeConf{ Pending: []string{"running", "stopping"}, Target: "stopped", Refresh: awscommon.InstanceStateRefreshFunc(ec2conn, *instance.InstanceId), StepState: state, } _, err = awscommon.WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for instance to stop: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } return multistep.ActionContinue }
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) ec2conn := state.Get("ec2").(*ec2.EC2) manifestPath := state.Get("remote_manifest_path").(string) ui := state.Get("ui").(packer.Ui) ui.Say("Registering the AMI...") registerOpts := &ec2.RegisterImage{ ImageLocation: manifestPath, Name: config.AMIName, BlockDevices: config.BlockDevices.BuildAMIDevices(), } registerResp, err := ec2conn.RegisterImage(registerOpts) if err != nil { state.Put("error", fmt.Errorf("Error registering AMI: %s", err)) ui.Error(state.Get("error").(error).Error()) return multistep.ActionHalt } // Set the AMI ID in the state ui.Say(fmt.Sprintf("AMI: %s", registerResp.ImageId)) amis := make(map[string]string) amis[ec2conn.Region.Name] = registerResp.ImageId state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Conn: ec2conn, Pending: []string{"pending"}, Target: "available", Refresh: awscommon.AMIStateRefreshFunc(ec2conn, registerResp.ImageId), StepState: state, } ui.Say("Waiting for AMI to become ready...") if _, err := awscommon.WaitForState(&stateChange); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } return multistep.ActionContinue }
func (s *StepAttachVolume) CleanupFunc(state map[string]interface{}) error { if !s.attached { return nil } ec2conn := state["ec2"].(*ec2.EC2) ui := state["ui"].(packer.Ui) ui.Say("Detaching EBS volume...") _, err := ec2conn.DetachVolume(s.volumeId) if err != nil { return fmt.Errorf("Error detaching EBS volume: %s", err) } s.attached = false // Wait for the volume to detach stateChange := awscommon.StateChangeConf{ Conn: ec2conn, Pending: []string{"attaching", "attached", "detaching"}, StepState: state, Target: "detached", Refresh: func() (interface{}, string, error) { resp, err := ec2conn.Volumes([]string{s.volumeId}, ec2.NewFilter()) if err != nil { return nil, "", err } state := "detached" if len(resp.Volumes[0].Attachments) > 0 { state = resp.Volumes[0].Attachments[0].Status } return nil, state, nil }, } _, err = awscommon.WaitForState(&stateChange) if err != nil { return fmt.Errorf("Error waiting for volume: %s", err) } return nil }
func (s *StepAttachVolume) CleanupFunc(state multistep.StateBag) error { if !s.attached { return nil } ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) ui.Say("Detaching EBS volume...") _, err := ec2conn.DetachVolume(&ec2.DetachVolumeInput{VolumeID: &s.volumeId}) if err != nil { return fmt.Errorf("Error detaching EBS volume: %s", err) } s.attached = false // Wait for the volume to detach stateChange := awscommon.StateChangeConf{ Pending: []string{"attaching", "attached", "detaching"}, StepState: state, Target: "detached", Refresh: func() (interface{}, string, error) { resp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIDs: []*string{&s.volumeId}}) if err != nil { return nil, "", err } v := resp.Volumes[0] if len(v.Attachments) > 0 { return v, *v.Attachments[0].State, nil } else { return v, "detached", nil } }, } _, err = awscommon.WaitForState(&stateChange) if err != nil { return fmt.Errorf("Error waiting for volume: %s", err) } return nil }
func (s *StepCreateVolume) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) image := state.Get("source_image").(*ec2.Image) instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) // Determine the root device snapshot log.Printf("Searching for root device of the image (%s)", *image.RootDeviceName) var rootDevice *ec2.BlockDeviceMapping for _, device := range image.BlockDeviceMappings { if *device.DeviceName == *image.RootDeviceName { rootDevice = device break } } if rootDevice == nil { err := fmt.Errorf("Couldn't find root device!") state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } ui.Say("Creating the root volume...") vs := *rootDevice.Ebs.VolumeSize if s.RootVolumeSize > *rootDevice.Ebs.VolumeSize { vs = s.RootVolumeSize } createVolume := &ec2.CreateVolumeInput{ AvailabilityZone: instance.Placement.AvailabilityZone, Size: aws.Int64(vs), SnapshotId: rootDevice.Ebs.SnapshotId, VolumeType: rootDevice.Ebs.VolumeType, Iops: rootDevice.Ebs.Iops, } log.Printf("Create args: %s", createVolume) createVolumeResp, err := ec2conn.CreateVolume(createVolume) if err != nil { err := fmt.Errorf("Error creating root volume: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Set the volume ID so we remember to delete it later s.volumeId = *createVolumeResp.VolumeId log.Printf("Volume ID: %s", s.volumeId) // Wait for the volume to become ready stateChange := awscommon.StateChangeConf{ Pending: []string{"creating"}, StepState: state, Target: "available", Refresh: func() (interface{}, string, error) { resp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIds: []*string{&s.volumeId}}) if err != nil { return nil, "", err } v := resp.Volumes[0] return v, *v.State, nil }, } _, err = awscommon.WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for volume: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } state.Put("volume_id", s.volumeId) return multistep.ActionContinue }
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) ec2conn := state.Get("ec2").(*ec2.EC2) image := state.Get("source_image").(*ec2.Image) snapshotId := state.Get("snapshot_id").(string) ui := state.Get("ui").(packer.Ui) ui.Say("Registering the AMI...") blockDevices := make([]*ec2.BlockDeviceMapping, len(image.BlockDeviceMappings)) for i, device := range image.BlockDeviceMappings { newDevice := device if *newDevice.DeviceName == *image.RootDeviceName { if newDevice.Ebs != nil { newDevice.Ebs.SnapshotId = aws.String(snapshotId) } else { newDevice.Ebs = &ec2.EbsBlockDevice{SnapshotId: aws.String(snapshotId)} } if s.RootVolumeSize > *newDevice.Ebs.VolumeSize { newDevice.Ebs.VolumeSize = aws.Int64(s.RootVolumeSize) } } // assume working from a snapshot, so we unset the Encrypted field if set, // otherwise AWS API will return InvalidParameter if newDevice.Ebs != nil && newDevice.Ebs.Encrypted != nil { newDevice.Ebs.Encrypted = nil } blockDevices[i] = newDevice } registerOpts := buildRegisterOpts(config, image, blockDevices) // Set SriovNetSupport to "simple". See http://goo.gl/icuXh5 if config.AMIEnhancedNetworking { registerOpts.SriovNetSupport = aws.String("simple") } registerResp, err := ec2conn.RegisterImage(registerOpts) if err != nil { state.Put("error", fmt.Errorf("Error registering AMI: %s", err)) ui.Error(state.Get("error").(error).Error()) return multistep.ActionHalt } // Set the AMI ID in the state ui.Say(fmt.Sprintf("AMI: %s", *registerResp.ImageId)) amis := make(map[string]string) amis[*ec2conn.Config.Region] = *registerResp.ImageId state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Pending: []string{"pending"}, Target: "available", Refresh: awscommon.AMIStateRefreshFunc(ec2conn, *registerResp.ImageId), StepState: state, } ui.Say("Waiting for AMI to become ready...") if _, err := awscommon.WaitForState(&stateChange); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } return multistep.ActionContinue }
func (s *StepCreateVolume) Run(state map[string]interface{}) multistep.StepAction { ec2conn := state["ec2"].(*ec2.EC2) image := state["source_image"].(*ec2.Image) instance := state["instance"].(*ec2.Instance) ui := state["ui"].(packer.Ui) // Determine the root device snapshot log.Printf("Searching for root device of the image (%s)", image.RootDeviceName) var rootDevice *ec2.BlockDeviceMapping for _, device := range image.BlockDevices { if device.DeviceName == image.RootDeviceName { rootDevice = &device break } } if rootDevice == nil { err := fmt.Errorf("Couldn't find root device!") state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } ui.Say("Creating the root volume...") createVolume := &ec2.CreateVolume{ AvailZone: instance.AvailZone, Size: rootDevice.VolumeSize, SnapshotId: rootDevice.SnapshotId, VolumeType: rootDevice.VolumeType, IOPS: rootDevice.IOPS, } log.Printf("Create args: %#v", createVolume) createVolumeResp, err := ec2conn.CreateVolume(createVolume) if err != nil { err := fmt.Errorf("Error creating root volume: %s", err) state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } // Set the volume ID so we remember to delete it later s.volumeId = createVolumeResp.VolumeId log.Printf("Volume ID: %s", s.volumeId) // Wait for the volume to become ready stateChange := awscommon.StateChangeConf{ Conn: ec2conn, Pending: []string{"creating"}, StepState: state, Target: "available", Refresh: func() (interface{}, string, error) { resp, err := ec2conn.Volumes([]string{s.volumeId}, ec2.NewFilter()) if err != nil { return nil, "", err } return nil, resp.Volumes[0].Status, nil }, } _, err = awscommon.WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for volume: %s", err) state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } state["volume_id"] = s.volumeId return multistep.ActionContinue }
func (s *stepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(Config) ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) // Encrypt boot not set, so skip step if !config.AMIConfig.AMIEncryptBootVolume { return multistep.ActionContinue } ui.Say("Creating Encrypted AMI Copy") amis := state.Get("amis").(map[string]string) var region, id string if amis != nil { for region, id = range amis { break // Only get the first } } ui.Say(fmt.Sprintf("Copying AMI: %s(%s)", region, id)) copyOpts := &ec2.CopyImageInput{ Name: &config.AMIName, // Try to overwrite existing AMI SourceImageId: aws.String(id), SourceRegion: aws.String(region), Encrypted: aws.Bool(true), } copyResp, err := ec2conn.CopyImage(copyOpts) if err != nil { err := fmt.Errorf("Error copying AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Wait for the copy to become ready stateChange := awscommon.StateChangeConf{ Pending: []string{"pending"}, Target: "available", Refresh: awscommon.AMIStateRefreshFunc(ec2conn, *copyResp.ImageId), StepState: state, } ui.Say("Waiting for AMI copy to become ready...") if _, err := awscommon.WaitForState(&stateChange); err != nil { err := fmt.Errorf("Error waiting for AMI Copy: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Get the unencrypted AMI image unencImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(id)}}) if err != nil { err := fmt.Errorf("Error searching for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } unencImage := unencImagesResp.Images[0] // Remove unencrypted AMI ui.Say("Deregistering unecrypted AMI") deregisterOpts := &ec2.DeregisterImageInput{ImageId: aws.String(id)} if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil { ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err)) return multistep.ActionHalt } // Remove associated unencrypted snapshot(s) ui.Say("Deleting unencrypted snapshots") for _, blockDevice := range unencImage.BlockDeviceMappings { if blockDevice.Ebs != nil { if blockDevice.Ebs.SnapshotId != nil { ui.Message(fmt.Sprintf("Snapshot ID: %s", *blockDevice.Ebs.SnapshotId)) deleteSnapOpts := &ec2.DeleteSnapshotInput{ SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId), } if _, err := ec2conn.DeleteSnapshot(deleteSnapOpts); err != nil { ui.Error(fmt.Sprintf("Error deleting snapshot, may still be around: %s", err)) return multistep.ActionHalt } } } } // Replace original AMI ID with Encrypted ID in state amis[region] = *copyResp.ImageId state.Put("amis", amis) imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{copyResp.ImageId}}) if err != nil { err := fmt.Errorf("Error searching for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } s.image = imagesResp.Images[0] return multistep.ActionContinue }
func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) keyName := state.Get("keyPair").(string) securityGroupIds := state.Get("securityGroupIds").([]string) ui := state.Get("ui").(packer.Ui) userData := s.UserData if s.UserDataFile != "" { contents, err := ioutil.ReadFile(s.UserDataFile) if err != nil { state.Put("error", fmt.Errorf("Problem reading user data file: %s", err)) return multistep.ActionHalt } userData = string(contents) } securityGroups := make([]ec2.SecurityGroup, len(securityGroupIds)) for n, securityGroupId := range securityGroupIds { securityGroups[n] = ec2.SecurityGroup{Id: securityGroupId} } ui.Say("Launching a source AWS instance...") imageResp, err := ec2conn.Images([]string{s.SourceAMI}, ec2.NewFilter()) if err != nil { state.Put("error", fmt.Errorf("There was a problem with the source AMI: %s", err)) return multistep.ActionHalt } if len(imageResp.Images) != 1 { state.Put("error", fmt.Errorf("The source AMI '%s' could not be found.", s.SourceAMI)) return multistep.ActionHalt } if s.ExpectedRootDevice != "" && imageResp.Images[0].RootDeviceType != s.ExpectedRootDevice { state.Put("error", fmt.Errorf( "The provided source AMI has an invalid root device type.\n"+ "Expected '%s', got '%s'.", s.ExpectedRootDevice, imageResp.Images[0].RootDeviceType)) return multistep.ActionHalt } spotPrice := s.SpotPrice if spotPrice == "auto" { ui.Message(fmt.Sprintf( "Finding spot price for %s %s...", s.SpotPriceProduct, s.InstanceType)) // Detect the spot price startTime := time.Now().Add(-1 * time.Hour) resp, err := ec2conn.DescribeSpotPriceHistory(&ec2.DescribeSpotPriceHistory{ InstanceType: []string{s.InstanceType}, ProductDescription: []string{s.SpotPriceProduct}, AvailabilityZone: s.AvailabilityZone, StartTime: startTime, }) if err != nil { err := fmt.Errorf("Error finding spot price: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } var price float64 for _, history := range resp.History { log.Printf("[INFO] Candidate spot price: %s", history.SpotPrice) current, err := strconv.ParseFloat(history.SpotPrice, 64) if err != nil { log.Printf("[ERR] Error parsing spot price: %s", err) continue } if price == 0 || current < price { price = current } } if price == 0 { err := fmt.Errorf("No candidate spot prices found!") state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } spotPrice = strconv.FormatFloat(price, 'f', -1, 64) } var instanceId string if spotPrice == "" { runOpts := &ec2.RunInstances{ KeyName: keyName, ImageId: s.SourceAMI, InstanceType: s.InstanceType, UserData: []byte(userData), MinCount: 0, MaxCount: 0, SecurityGroups: securityGroups, IamInstanceProfile: s.IamInstanceProfile, SubnetId: s.SubnetId, AssociatePublicIpAddress: s.AssociatePublicIpAddress, BlockDevices: s.BlockDevices.BuildLaunchDevices(), AvailZone: s.AvailabilityZone, } runResp, err := ec2conn.RunInstances(runOpts) if err != nil { err := fmt.Errorf("Error launching source instance: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } instanceId = runResp.Instances[0].InstanceId } else { ui.Message(fmt.Sprintf( "Requesting spot instance '%s' for: %s", s.InstanceType, spotPrice)) runOpts := &ec2.RequestSpotInstances{ SpotPrice: spotPrice, KeyName: keyName, ImageId: s.SourceAMI, InstanceType: s.InstanceType, UserData: []byte(userData), SecurityGroups: securityGroups, IamInstanceProfile: s.IamInstanceProfile, SubnetId: s.SubnetId, AssociatePublicIpAddress: s.AssociatePublicIpAddress, BlockDevices: s.BlockDevices.BuildLaunchDevices(), AvailZone: s.AvailabilityZone, } runSpotResp, err := ec2conn.RequestSpotInstances(runOpts) if err != nil { err := fmt.Errorf("Error launching source spot instance: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } s.spotRequest = &runSpotResp.SpotRequestResults[0] spotRequestId := s.spotRequest.SpotRequestId ui.Message(fmt.Sprintf("Waiting for spot request (%s) to become active...", spotRequestId)) stateChange := awscommon.StateChangeConf{ Pending: []string{"open"}, Target: "active", Refresh: awscommon.SpotRequestStateRefreshFunc(ec2conn, spotRequestId), StepState: state, } _, err = awscommon.WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for spot request (%s) to become ready: %s", spotRequestId, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } spotResp, err := ec2conn.DescribeSpotRequests([]string{spotRequestId}, nil) if err != nil { err := fmt.Errorf("Error finding spot request (%s): %s", spotRequestId, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } instanceId = spotResp.SpotRequestResults[0].InstanceId } ui.Message(fmt.Sprintf("Instance ID: %s", instanceId)) ui.Say(fmt.Sprintf("Waiting for instance (%v) to become ready...", instanceId)) stateChange := StateChangeConf{ Pending: []string{"pending"}, Target: "running", Refresh: InstanceStateRefreshFunc(ec2conn, instanceId), StepState: state, } latestInstance, err := WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", instanceId, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } s.instance = latestInstance.(*ec2.Instance) ec2Tags := make([]ec2.Tag, 1, len(s.Tags)+1) ec2Tags[0] = ec2.Tag{"Name", "Packer Builder"} for k, v := range s.Tags { ec2Tags = append(ec2Tags, ec2.Tag{k, v}) } _, err = ec2conn.CreateTags([]string{s.instance.InstanceId}, ec2Tags) if err != nil { ui.Message( fmt.Sprintf("Failed to tag a Name on the builder instance: %s", err)) } if s.Debug { if s.instance.DNSName != "" { ui.Message(fmt.Sprintf("Public DNS: %s", s.instance.DNSName)) } if s.instance.PublicIpAddress != "" { ui.Message(fmt.Sprintf("Public IP: %s", s.instance.PublicIpAddress)) } if s.instance.PrivateIpAddress != "" { ui.Message(fmt.Sprintf("Private IP: %s", s.instance.PrivateIpAddress)) } } state.Put("instance", s.instance) return multistep.ActionContinue }
func (step *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) ec2conn := state.Get("ec2").(*ec2.EC2) instance := state.Get("instance").(*ec2.Instance) snapshotId := state.Get("snapshot_id").(string) ui := state.Get("ui").(packer.Ui) step.ensureRootDeviceMapping(snapshotId, config) // Create the image ui.Say(fmt.Sprintf("Registering the AMI: %s", config.AMIName)) opts := &ec2.RegisterImage{ Name: config.AMIName, Description: config.AMIDescription, Architecture: instance.Architecture, BlockDevices: config.BlockDevices.BuildAMIDevices(), RootDeviceName: step.RootDeviceName, VirtType: instance.VirtType, } if config.AMIEnhancedNetworking { opts.SriovNetSupport = "simple" } registerResp, err := ec2conn.RegisterImage(opts) if err != nil { err := fmt.Errorf("Error registering AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Set the AMI ID in the state ui.Message(fmt.Sprintf("AMI: %s", registerResp.ImageId)) amis := make(map[string]string) amis[ec2conn.Region.Name] = registerResp.ImageId state.Put("amis", amis) // Wait for the image to become ready stateChange := awscommon.StateChangeConf{ Pending: []string{"pending"}, Target: "available", Refresh: awscommon.AMIStateRefreshFunc(ec2conn, registerResp.ImageId), StepState: state, } ui.Say("Waiting for AMI to become ready...") if _, err := awscommon.WaitForState(&stateChange); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } imagesResp, err := ec2conn.Images([]string{registerResp.ImageId}, nil) if err != nil { err := fmt.Errorf("Error searching for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } step.image = &imagesResp.Images[0] return multistep.ActionContinue }
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { var err error config, err := p.config.Config() if err != nil { return nil, false, err } // Render this key since we didn't in the configure phase p.config.S3Key, err = interpolate.Render(p.config.S3Key, &p.config.ctx) if err != nil { return nil, false, fmt.Errorf("Error rendering s3_key_name template: %s", err) } log.Printf("Rendered s3_key_name as %s", p.config.S3Key) log.Println("Looking for OVA in artifact") // Locate the files output from the builder source := "" for _, path := range artifact.Files() { if strings.HasSuffix(path, ".ova") { source = path break } } // Hope we found something useful if source == "" { return nil, false, fmt.Errorf("No OVA file found in artifact from builder") } // Set up the AWS session log.Println("Creating AWS session") session := session.New(config) // open the source file log.Printf("Opening file %s to upload", source) file, err := os.Open(source) if err != nil { return nil, false, fmt.Errorf("Failed to open %s: %s", source, err) } ui.Message(fmt.Sprintf("Uploading %s to s3://%s/%s", source, p.config.S3Bucket, p.config.S3Key)) // Copy the OVA file into the S3 bucket specified uploader := s3manager.NewUploader(session) _, err = uploader.Upload(&s3manager.UploadInput{ Body: file, Bucket: &p.config.S3Bucket, Key: &p.config.S3Key, }) if err != nil { return nil, false, fmt.Errorf("Failed to upload %s: %s", source, err) } // May as well stop holding this open now file.Close() ui.Message(fmt.Sprintf("Completed upload of %s to s3://%s/%s", source, p.config.S3Bucket, p.config.S3Key)) // Call EC2 image import process log.Printf("Calling EC2 to import from s3://%s/%s", p.config.S3Bucket, p.config.S3Key) ec2conn := ec2.New(session) import_start, err := ec2conn.ImportImage(&ec2.ImportImageInput{ DiskContainers: []*ec2.ImageDiskContainer{ { UserBucket: &ec2.UserBucket{ S3Bucket: &p.config.S3Bucket, S3Key: &p.config.S3Key, }, }, }, }) if err != nil { return nil, false, fmt.Errorf("Failed to start import from s3://%s/%s: %s", p.config.S3Bucket, p.config.S3Key, err) } ui.Message(fmt.Sprintf("Started import of s3://%s/%s, task id %s", p.config.S3Bucket, p.config.S3Key, *import_start.ImportTaskId)) // Wait for import process to complete, this takess a while ui.Message(fmt.Sprintf("Waiting for task %s to complete (may take a while)", *import_start.ImportTaskId)) stateChange := awscommon.StateChangeConf{ Pending: []string{"pending", "active"}, Refresh: awscommon.ImportImageRefreshFunc(ec2conn, *import_start.ImportTaskId), Target: "completed", } // Actually do the wait for state change // We ignore errors out of this and check job state in AWS API awscommon.WaitForState(&stateChange) // Retrieve what the outcome was for the import task import_result, err := ec2conn.DescribeImportImageTasks(&ec2.DescribeImportImageTasksInput{ ImportTaskIds: []*string{ import_start.ImportTaskId, }, }) if err != nil { return nil, false, fmt.Errorf("Failed to find import task %s: %s", *import_start.ImportTaskId, err) } // Check it was actually completed if *import_result.ImportImageTasks[0].Status != "completed" { // The most useful error message is from the job itself return nil, false, fmt.Errorf("Import task %s failed: %s", *import_start.ImportTaskId, *import_result.ImportImageTasks[0].StatusMessage) } ui.Message(fmt.Sprintf("Import task %s complete", *import_start.ImportTaskId)) // Pull AMI ID out of the completed job createdami := *import_result.ImportImageTasks[0].ImageId // If we have tags, then apply them now to both the AMI and snaps // created by the import if len(p.config.Tags) > 0 { var ec2Tags []*ec2.Tag log.Printf("Repacking tags into AWS format") for key, value := range p.config.Tags { ui.Message(fmt.Sprintf("Adding tag \"%s\": \"%s\"", key, value)) ec2Tags = append(ec2Tags, &ec2.Tag{ Key: aws.String(key), Value: aws.String(value), }) } resourceIds := []*string{&createdami} log.Printf("Getting details of %s", createdami) imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ ImageIds: resourceIds, }) if err != nil { return nil, false, fmt.Errorf("Failed to retrieve details for AMI %s: %s", createdami, err) } if len(imageResp.Images) == 0 { return nil, false, fmt.Errorf("AMI %s has no images", createdami) } image := imageResp.Images[0] log.Printf("Walking block device mappings for %s to find snapshots", createdami) for _, device := range image.BlockDeviceMappings { if device.Ebs != nil && device.Ebs.SnapshotId != nil { ui.Message(fmt.Sprintf("Tagging snapshot %s", *device.Ebs.SnapshotId)) resourceIds = append(resourceIds, device.Ebs.SnapshotId) } } ui.Message(fmt.Sprintf("Tagging AMI %s", createdami)) _, err = ec2conn.CreateTags(&ec2.CreateTagsInput{ Resources: resourceIds, Tags: ec2Tags, }) if err != nil { return nil, false, fmt.Errorf("Failed to add tags to resources %#v: %s", resourceIds, err) } } // Add the reported AMI ID to the artifact list log.Printf("Adding created AMI ID %s in region %s to output artifacts", createdami, *config.Region) artifact = &awscommon.Artifact{ Amis: map[string]string{ *config.Region: createdami, }, BuilderIdValue: BuilderId, Conn: ec2conn, } if !p.config.SkipClean { ui.Message(fmt.Sprintf("Deleting import source s3://%s/%s", p.config.S3Bucket, p.config.S3Key)) s3conn := s3.New(session) _, err = s3conn.DeleteObject(&s3.DeleteObjectInput{ Bucket: &p.config.S3Bucket, Key: &p.config.S3Key, }) if err != nil { return nil, false, fmt.Errorf("Failed to delete s3://%s/%s: %s", p.config.S3Bucket, p.config.S3Key, err) } } return artifact, false, nil }
func (s *StepAttachVolume) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) device := state.Get("device").(string) instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) volumeId := state.Get("volume_id").(string) // For the API call, it expects "sd" prefixed devices. attachVolume := strings.Replace(device, "/xvd", "/sd", 1) ui.Say(fmt.Sprintf("Attaching the root volume to %s", attachVolume)) _, err := ec2conn.AttachVolume(&ec2.AttachVolumeInput{ InstanceID: instance.InstanceID, VolumeID: &volumeId, Device: &attachVolume, }) if err != nil { err := fmt.Errorf("Error attaching volume: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } // Mark that we attached it so we can detach it later s.attached = true s.volumeId = volumeId // Wait for the volume to become attached stateChange := awscommon.StateChangeConf{ Pending: []string{"attaching"}, StepState: state, Target: "attached", Refresh: func() (interface{}, string, error) { attempts := 0 for attempts < 30 { resp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesInput{VolumeIDs: []*string{&volumeId}}) if err != nil { return nil, "", err } if len(resp.Volumes[0].Attachments) > 0 { a := resp.Volumes[0].Attachments[0] return a, *a.State, nil } // When Attachment on volume is not present sleep for 2s and retry attempts += 1 ui.Say(fmt.Sprintf( "Volume %s show no attachments. Attempt %d/30. Sleeping for 2s and will retry.", volumeId, attempts)) time.Sleep(2 * time.Second) } // Attachment on volume is not present after all attempts return nil, "", errors.New("No attachments on volume.") }, } _, err = awscommon.WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for volume: %s", err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } state.Put("attach_cleanup", s) return multistep.ActionContinue }