github.com/angdraug/packer@v1.3.2/builder/digitalocean/step_snapshot.go (about)

     1  package digitalocean
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"time"
     9  
    10  	"github.com/digitalocean/godo"
    11  	"github.com/hashicorp/packer/helper/multistep"
    12  	"github.com/hashicorp/packer/packer"
    13  )
    14  
    15  type stepSnapshot struct{}
    16  
    17  func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
    18  	client := state.Get("client").(*godo.Client)
    19  	ui := state.Get("ui").(packer.Ui)
    20  	c := state.Get("config").(*Config)
    21  	dropletId := state.Get("droplet_id").(int)
    22  	var snapshotRegions []string
    23  
    24  	ui.Say(fmt.Sprintf("Creating snapshot: %v", c.SnapshotName))
    25  	action, _, err := client.DropletActions.Snapshot(context.TODO(), dropletId, c.SnapshotName)
    26  	if err != nil {
    27  		err := fmt.Errorf("Error creating snapshot: %s", err)
    28  		state.Put("error", err)
    29  		ui.Error(err.Error())
    30  		return multistep.ActionHalt
    31  	}
    32  
    33  	// With the pending state over, verify that we're in the active state
    34  	ui.Say("Waiting for snapshot to complete...")
    35  	if err := waitForActionState(godo.ActionCompleted, dropletId, action.ID,
    36  		client, 20*time.Minute); err != nil {
    37  		// If we get an error the first time, actually report it
    38  		err := fmt.Errorf("Error waiting for snapshot: %s", err)
    39  		state.Put("error", err)
    40  		ui.Error(err.Error())
    41  		return multistep.ActionHalt
    42  	}
    43  
    44  	// Wait for the droplet to become unlocked first. For snapshots
    45  	// this can end up taking quite a long time, so we hardcode this to
    46  	// 20 minutes.
    47  	if err := waitForDropletUnlocked(client, dropletId, 20*time.Minute); err != nil {
    48  		// If we get an error the first time, actually report it
    49  		err := fmt.Errorf("Error shutting down droplet: %s", err)
    50  		state.Put("error", err)
    51  		ui.Error(err.Error())
    52  		return multistep.ActionHalt
    53  	}
    54  
    55  	log.Printf("Looking up snapshot ID for snapshot: %s", c.SnapshotName)
    56  	images, _, err := client.Droplets.Snapshots(context.TODO(), dropletId, nil)
    57  	if err != nil {
    58  		err := fmt.Errorf("Error looking up snapshot ID: %s", err)
    59  		state.Put("error", err)
    60  		ui.Error(err.Error())
    61  		return multistep.ActionHalt
    62  	}
    63  
    64  	if len(c.SnapshotRegions) > 0 {
    65  		regionSet := make(map[string]struct{})
    66  		regions := make([]string, 0, len(c.SnapshotRegions))
    67  		regionSet[c.Region] = struct{}{}
    68  		for _, region := range c.SnapshotRegions {
    69  			// If we already saw the region, then don't look again
    70  			if _, ok := regionSet[region]; ok {
    71  				continue
    72  			}
    73  
    74  			// Mark that we saw the region
    75  			regionSet[region] = struct{}{}
    76  
    77  			regions = append(regions, region)
    78  		}
    79  		snapshotRegions = regions
    80  
    81  		for transfer := range snapshotRegions {
    82  			transferRequest := &godo.ActionRequest{
    83  				"type":   "transfer",
    84  				"region": snapshotRegions[transfer],
    85  			}
    86  			imageTransfer, _, err := client.ImageActions.Transfer(context.TODO(), images[0].ID, transferRequest)
    87  			if err != nil {
    88  				err := fmt.Errorf("Error transferring snapshot: %s", err)
    89  				state.Put("error", err)
    90  				ui.Error(err.Error())
    91  				return multistep.ActionHalt
    92  			}
    93  			ui.Say(fmt.Sprintf("transferring Snapshot ID: %d", imageTransfer.ID))
    94  			if err := waitForImageState(godo.ActionCompleted, imageTransfer.ID, action.ID,
    95  				client, 20*time.Minute); err != nil {
    96  				// If we get an error the first time, actually report it
    97  				err := fmt.Errorf("Error waiting for snapshot transfer: %s", err)
    98  				state.Put("error", err)
    99  				ui.Error(err.Error())
   100  				return multistep.ActionHalt
   101  			}
   102  		}
   103  	}
   104  
   105  	var imageId int
   106  	if len(images) == 1 {
   107  		imageId = images[0].ID
   108  	} else {
   109  		err := errors.New("Couldn't find snapshot to get the image ID. Bug?")
   110  		state.Put("error", err)
   111  		ui.Error(err.Error())
   112  		return multistep.ActionHalt
   113  	}
   114  	snapshotRegions = append(snapshotRegions, c.Region)
   115  
   116  	log.Printf("Snapshot image ID: %d", imageId)
   117  	state.Put("snapshot_image_id", imageId)
   118  	state.Put("snapshot_name", c.SnapshotName)
   119  	state.Put("regions", snapshotRegions)
   120  
   121  	return multistep.ActionContinue
   122  }
   123  
   124  func (s *stepSnapshot) Cleanup(state multistep.StateBag) {
   125  	// no cleanup
   126  }