github.phpd.cn/hashicorp/packer@v1.3.2/builder/amazon/chroot/step_create_volume.go (about)

     1  package chroot
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/service/ec2"
    11  	awscommon "github.com/hashicorp/packer/builder/amazon/common"
    12  	"github.com/hashicorp/packer/helper/multistep"
    13  	"github.com/hashicorp/packer/packer"
    14  	"github.com/hashicorp/packer/template/interpolate"
    15  )
    16  
    17  // StepCreateVolume creates a new volume from the snapshot of the root
    18  // device of the AMI.
    19  //
    20  // Produces:
    21  //   volume_id string - The ID of the created volume
    22  type StepCreateVolume struct {
    23  	volumeId       string
    24  	RootVolumeSize int64
    25  	RootVolumeType string
    26  	RootVolumeTags awscommon.TagMap
    27  	Ctx            interpolate.Context
    28  }
    29  
    30  func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
    31  	config := state.Get("config").(*Config)
    32  	ec2conn := state.Get("ec2").(*ec2.EC2)
    33  	instance := state.Get("instance").(*ec2.Instance)
    34  	ui := state.Get("ui").(packer.Ui)
    35  
    36  	volTags, err := s.RootVolumeTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
    37  	if err != nil {
    38  		err := fmt.Errorf("Error tagging volumes: %s", err)
    39  		state.Put("error", err)
    40  		ui.Error(err.Error())
    41  		return multistep.ActionHalt
    42  	}
    43  
    44  	// Collect tags for tagging on resource creation
    45  	var tagSpecs []*ec2.TagSpecification
    46  
    47  	if len(volTags) > 0 {
    48  		runVolTags := &ec2.TagSpecification{
    49  			ResourceType: aws.String("volume"),
    50  			Tags:         volTags,
    51  		}
    52  
    53  		tagSpecs = append(tagSpecs, runVolTags)
    54  	}
    55  
    56  	var createVolume *ec2.CreateVolumeInput
    57  	if config.FromScratch {
    58  		rootVolumeType := ec2.VolumeTypeGp2
    59  		if s.RootVolumeType == "io1" {
    60  			err := errors.New("Cannot use io1 volume when building from scratch")
    61  			state.Put("error", err)
    62  			ui.Error(err.Error())
    63  			return multistep.ActionHalt
    64  		} else if s.RootVolumeType != "" {
    65  			rootVolumeType = s.RootVolumeType
    66  		}
    67  		createVolume = &ec2.CreateVolumeInput{
    68  			AvailabilityZone: instance.Placement.AvailabilityZone,
    69  			Size:             aws.Int64(s.RootVolumeSize),
    70  			VolumeType:       aws.String(rootVolumeType),
    71  		}
    72  
    73  	} else {
    74  		// Determine the root device snapshot
    75  		image := state.Get("source_image").(*ec2.Image)
    76  		log.Printf("Searching for root device of the image (%s)", *image.RootDeviceName)
    77  		var rootDevice *ec2.BlockDeviceMapping
    78  		for _, device := range image.BlockDeviceMappings {
    79  			if *device.DeviceName == *image.RootDeviceName {
    80  				rootDevice = device
    81  				break
    82  			}
    83  		}
    84  
    85  		ui.Say("Creating the root volume...")
    86  		createVolume, err = s.buildCreateVolumeInput(*instance.Placement.AvailabilityZone, rootDevice)
    87  		if err != nil {
    88  			state.Put("error", err)
    89  			ui.Error(err.Error())
    90  			return multistep.ActionHalt
    91  		}
    92  	}
    93  
    94  	if len(tagSpecs) > 0 {
    95  		createVolume.SetTagSpecifications(tagSpecs)
    96  		volTags.Report(ui)
    97  	}
    98  	log.Printf("Create args: %+v", createVolume)
    99  
   100  	createVolumeResp, err := ec2conn.CreateVolume(createVolume)
   101  	if err != nil {
   102  		err := fmt.Errorf("Error creating root volume: %s", err)
   103  		state.Put("error", err)
   104  		ui.Error(err.Error())
   105  		return multistep.ActionHalt
   106  	}
   107  
   108  	// Set the volume ID so we remember to delete it later
   109  	s.volumeId = *createVolumeResp.VolumeId
   110  	log.Printf("Volume ID: %s", s.volumeId)
   111  
   112  	// Wait for the volume to become ready
   113  	err = awscommon.WaitUntilVolumeAvailable(ctx, ec2conn, s.volumeId)
   114  	if err != nil {
   115  		err := fmt.Errorf("Error waiting for volume: %s", err)
   116  		state.Put("error", err)
   117  		ui.Error(err.Error())
   118  		return multistep.ActionHalt
   119  	}
   120  
   121  	state.Put("volume_id", s.volumeId)
   122  	return multistep.ActionContinue
   123  }
   124  
   125  func (s *StepCreateVolume) Cleanup(state multistep.StateBag) {
   126  	if s.volumeId == "" {
   127  		return
   128  	}
   129  
   130  	ec2conn := state.Get("ec2").(*ec2.EC2)
   131  	ui := state.Get("ui").(packer.Ui)
   132  
   133  	ui.Say("Deleting the created EBS volume...")
   134  	_, err := ec2conn.DeleteVolume(&ec2.DeleteVolumeInput{VolumeId: &s.volumeId})
   135  	if err != nil {
   136  		ui.Error(fmt.Sprintf("Error deleting EBS volume: %s", err))
   137  	}
   138  }
   139  
   140  func (s *StepCreateVolume) buildCreateVolumeInput(az string, rootDevice *ec2.BlockDeviceMapping) (*ec2.CreateVolumeInput, error) {
   141  	if rootDevice == nil {
   142  		return nil, fmt.Errorf("Couldn't find root device!")
   143  	}
   144  	createVolumeInput := &ec2.CreateVolumeInput{
   145  		AvailabilityZone: aws.String(az),
   146  		Size:             rootDevice.Ebs.VolumeSize,
   147  		SnapshotId:       rootDevice.Ebs.SnapshotId,
   148  		VolumeType:       rootDevice.Ebs.VolumeType,
   149  		Iops:             rootDevice.Ebs.Iops,
   150  	}
   151  	if s.RootVolumeSize > *rootDevice.Ebs.VolumeSize {
   152  		createVolumeInput.Size = aws.Int64(s.RootVolumeSize)
   153  	}
   154  
   155  	if s.RootVolumeType == "" || s.RootVolumeType == *rootDevice.Ebs.VolumeType {
   156  		return createVolumeInput, nil
   157  	}
   158  
   159  	if s.RootVolumeType == "io1" {
   160  		return nil, fmt.Errorf("Root volume type cannot be io1, because existing root volume type was %s", *rootDevice.Ebs.VolumeType)
   161  	}
   162  
   163  	createVolumeInput.VolumeType = aws.String(s.RootVolumeType)
   164  	// non io1 cannot set iops
   165  	createVolumeInput.Iops = nil
   166  
   167  	return createVolumeInput, nil
   168  }