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

     1  package ebssurrogate
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/service/ec2"
    10  	multierror "github.com/hashicorp/go-multierror"
    11  	awscommon "github.com/hashicorp/packer/builder/amazon/common"
    12  	"github.com/hashicorp/packer/helper/multistep"
    13  	"github.com/hashicorp/packer/packer"
    14  )
    15  
    16  // StepSnapshotVolumes creates snapshots of the created volumes.
    17  //
    18  // Produces:
    19  //   snapshot_ids map[string]string - IDs of the created snapshots
    20  type StepSnapshotVolumes struct {
    21  	LaunchDevices []*ec2.BlockDeviceMapping
    22  	snapshotIds   map[string]string
    23  }
    24  
    25  func (s *StepSnapshotVolumes) snapshotVolume(ctx context.Context, deviceName string, state multistep.StateBag) error {
    26  	ec2conn := state.Get("ec2").(*ec2.EC2)
    27  	ui := state.Get("ui").(packer.Ui)
    28  	instance := state.Get("instance").(*ec2.Instance)
    29  
    30  	var volumeId string
    31  	for _, volume := range instance.BlockDeviceMappings {
    32  		if *volume.DeviceName == deviceName {
    33  			volumeId = *volume.Ebs.VolumeId
    34  		}
    35  	}
    36  	if volumeId == "" {
    37  		return fmt.Errorf("Volume ID for device %s not found", deviceName)
    38  	}
    39  
    40  	ui.Say(fmt.Sprintf("Creating snapshot of EBS Volume %s...", volumeId))
    41  	description := fmt.Sprintf("Packer: %s", time.Now().String())
    42  
    43  	createSnapResp, err := ec2conn.CreateSnapshot(&ec2.CreateSnapshotInput{
    44  		VolumeId:    &volumeId,
    45  		Description: &description,
    46  	})
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	// Set the snapshot ID so we can delete it later
    52  	s.snapshotIds[deviceName] = *createSnapResp.SnapshotId
    53  
    54  	// Wait for snapshot to be created
    55  	err = awscommon.WaitUntilSnapshotDone(ctx, ec2conn, *createSnapResp.SnapshotId)
    56  	return err
    57  }
    58  
    59  func (s *StepSnapshotVolumes) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
    60  	ui := state.Get("ui").(packer.Ui)
    61  
    62  	s.snapshotIds = map[string]string{}
    63  
    64  	var wg sync.WaitGroup
    65  	var errs *multierror.Error
    66  	for _, device := range s.LaunchDevices {
    67  		wg.Add(1)
    68  		go func(device *ec2.BlockDeviceMapping) {
    69  			defer wg.Done()
    70  			if err := s.snapshotVolume(ctx, *device.DeviceName, state); err != nil {
    71  				errs = multierror.Append(errs, err)
    72  			}
    73  		}(device)
    74  	}
    75  
    76  	wg.Wait()
    77  
    78  	if errs != nil {
    79  		state.Put("error", errs)
    80  		ui.Error(errs.Error())
    81  		return multistep.ActionHalt
    82  	}
    83  
    84  	state.Put("snapshot_ids", s.snapshotIds)
    85  	return multistep.ActionContinue
    86  }
    87  
    88  func (s *StepSnapshotVolumes) Cleanup(state multistep.StateBag) {
    89  	if len(s.snapshotIds) == 0 {
    90  		return
    91  	}
    92  
    93  	_, cancelled := state.GetOk(multistep.StateCancelled)
    94  	_, halted := state.GetOk(multistep.StateHalted)
    95  
    96  	if cancelled || halted {
    97  		ec2conn := state.Get("ec2").(*ec2.EC2)
    98  		ui := state.Get("ui").(packer.Ui)
    99  		ui.Say("Removing snapshots since we cancelled or halted...")
   100  		for _, snapshotId := range s.snapshotIds {
   101  			_, err := ec2conn.DeleteSnapshot(&ec2.DeleteSnapshotInput{SnapshotId: &snapshotId})
   102  			if err != nil {
   103  				ui.Error(fmt.Sprintf("Error: %s", err))
   104  			}
   105  		}
   106  	}
   107  }