Example #1
0
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
}
Example #2
0
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
}
Example #3
0
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
}
Example #4
0
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
}
Example #5
0
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
}
Example #6
0
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
}
Example #7
0
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
}
Example #8
0
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)
	}
}
Example #10
0
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
}
Example #11
0
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
}
Example #12
0
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
}
Example #13
0
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
}
Example #15
0
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
}
Example #16
0
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
}
Example #17
0
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
}
Example #22
0
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
}
Example #23
0
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
}