github.com/angdraug/packer@v1.3.2/builder/digitalocean/builder.go (about) 1 // The digitalocean package contains a packer.Builder implementation 2 // that builds DigitalOcean images (snapshots). 3 4 package digitalocean 5 6 import ( 7 "context" 8 "fmt" 9 "log" 10 "net/url" 11 12 "github.com/digitalocean/godo" 13 "github.com/hashicorp/packer/common" 14 "github.com/hashicorp/packer/helper/communicator" 15 "github.com/hashicorp/packer/helper/multistep" 16 "github.com/hashicorp/packer/packer" 17 "golang.org/x/oauth2" 18 ) 19 20 // The unique id for the builder 21 const BuilderId = "pearkes.digitalocean" 22 23 type Builder struct { 24 config Config 25 runner multistep.Runner 26 } 27 28 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 29 c, warnings, errs := NewConfig(raws...) 30 if errs != nil { 31 return warnings, errs 32 } 33 b.config = *c 34 35 return nil, nil 36 } 37 38 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 39 client := godo.NewClient(oauth2.NewClient(oauth2.NoContext, &apiTokenSource{ 40 AccessToken: b.config.APIToken, 41 })) 42 if b.config.APIURL != "" { 43 u, err := url.Parse(b.config.APIURL) 44 if err != nil { 45 return nil, fmt.Errorf("DigitalOcean: Invalid API URL, %s.", err) 46 } 47 client.BaseURL = u 48 } 49 50 if len(b.config.SnapshotRegions) > 0 { 51 opt := &godo.ListOptions{ 52 Page: 1, 53 PerPage: 200, 54 } 55 regions, _, err := client.Regions.List(context.TODO(), opt) 56 if err != nil { 57 return nil, fmt.Errorf("DigitalOcean: Unable to get regions, %s", err) 58 } 59 60 validRegions := make(map[string]struct{}) 61 for _, val := range regions { 62 validRegions[val.Slug] = struct{}{} 63 } 64 65 for _, region := range append(b.config.SnapshotRegions, b.config.Region) { 66 if _, ok := validRegions[region]; !ok { 67 return nil, fmt.Errorf("DigitalOcean: Invalid region, %s", region) 68 } 69 } 70 } 71 72 // Set up the state 73 state := new(multistep.BasicStateBag) 74 state.Put("config", &b.config) 75 state.Put("client", client) 76 state.Put("hook", hook) 77 state.Put("ui", ui) 78 79 // Build the steps 80 steps := []multistep.Step{ 81 &stepCreateSSHKey{ 82 Debug: b.config.PackerDebug, 83 DebugKeyPath: fmt.Sprintf("do_%s.pem", b.config.PackerBuildName), 84 }, 85 new(stepCreateDroplet), 86 new(stepDropletInfo), 87 &communicator.StepConnect{ 88 Config: &b.config.Comm, 89 Host: commHost, 90 SSHConfig: b.config.Comm.SSHConfigFunc(), 91 }, 92 new(common.StepProvision), 93 &common.StepCleanupTempKeys{ 94 Comm: &b.config.Comm, 95 }, 96 new(stepShutdown), 97 new(stepPowerOff), 98 new(stepSnapshot), 99 } 100 101 // Run the steps 102 b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) 103 b.runner.Run(state) 104 105 // If there was an error, return that 106 if rawErr, ok := state.GetOk("error"); ok { 107 return nil, rawErr.(error) 108 } 109 110 if _, ok := state.GetOk("snapshot_name"); !ok { 111 log.Println("Failed to find snapshot_name in state. Bug?") 112 return nil, nil 113 } 114 115 artifact := &Artifact{ 116 snapshotName: state.Get("snapshot_name").(string), 117 snapshotId: state.Get("snapshot_image_id").(int), 118 regionNames: state.Get("regions").([]string), 119 client: client, 120 } 121 122 return artifact, nil 123 } 124 125 func (b *Builder) Cancel() { 126 if b.runner != nil { 127 log.Println("Cancelling the step runner...") 128 b.runner.Cancel() 129 } 130 }