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  }