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 }