github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/post-processor/vagrant-cloud/post-processor.go (about)

     1  // vagrant_cloud implements the packer.PostProcessor interface and adds a
     2  // post-processor that uploads artifacts from the vagrant post-processor
     3  // to Vagrant Cloud (vagrantcloud.com)
     4  package vagrantcloud
     5  
     6  import (
     7  	"fmt"
     8  	"github.com/mitchellh/multistep"
     9  	"github.com/mitchellh/packer/common"
    10  	"github.com/mitchellh/packer/packer"
    11  	"log"
    12  	"strings"
    13  )
    14  
    15  const VAGRANT_CLOUD_URL = "https://vagrantcloud.com/api/v1"
    16  
    17  type Config struct {
    18  	common.PackerConfig `mapstructure:",squash"`
    19  
    20  	Tag                string `mapstructure:"box_tag"`
    21  	Version            string `mapstructure:"version"`
    22  	VersionDescription string `mapstructure:"version_description"`
    23  	NoRelease          bool   `mapstructure:"no_release"`
    24  
    25  	AccessToken     string `mapstructure:"access_token"`
    26  	VagrantCloudUrl string `mapstructure:"vagrant_cloud_url"`
    27  
    28  	tpl *packer.ConfigTemplate
    29  }
    30  
    31  type PostProcessor struct {
    32  	config Config
    33  	client *VagrantCloudClient
    34  	runner multistep.Runner
    35  }
    36  
    37  func (p *PostProcessor) Configure(raws ...interface{}) error {
    38  	_, err := common.DecodeConfig(&p.config, raws...)
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	p.config.tpl, err = packer.NewConfigTemplate()
    44  	if err != nil {
    45  		return err
    46  	}
    47  	p.config.tpl.UserVars = p.config.PackerUserVars
    48  
    49  	// Default configuration
    50  	if p.config.VagrantCloudUrl == "" {
    51  		p.config.VagrantCloudUrl = VAGRANT_CLOUD_URL
    52  	}
    53  
    54  	// Accumulate any errors
    55  	errs := new(packer.MultiError)
    56  
    57  	// required configuration
    58  	templates := map[string]*string{
    59  		"box_tag":      &p.config.Tag,
    60  		"version":      &p.config.Version,
    61  		"access_token": &p.config.AccessToken,
    62  	}
    63  
    64  	for key, ptr := range templates {
    65  		if *ptr == "" {
    66  			errs = packer.MultiErrorAppend(
    67  				errs, fmt.Errorf("%s must be set", key))
    68  		}
    69  	}
    70  
    71  	// Template process
    72  	for key, ptr := range templates {
    73  		*ptr, err = p.config.tpl.Process(*ptr, nil)
    74  		if err != nil {
    75  			errs = packer.MultiErrorAppend(
    76  				errs, fmt.Errorf("Error processing %s: %s", key, err))
    77  		}
    78  	}
    79  
    80  	if len(errs.Errors) > 0 {
    81  		return errs
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
    88  	// Only accepts input from the vagrant post-processor
    89  	if artifact.BuilderId() != "mitchellh.post-processor.vagrant" {
    90  		return nil, false, fmt.Errorf(
    91  			"Unknown artifact type, requires box from vagrant post-processor: %s", artifact.BuilderId())
    92  	}
    93  
    94  	// We assume that there is only one .box file to upload
    95  	if !strings.HasSuffix(artifact.Files()[0], ".box") {
    96  		return nil, false, fmt.Errorf(
    97  			"Unknown files in artifact from vagrant post-processor: %s", artifact.Files())
    98  	}
    99  
   100  	// create the HTTP client
   101  	p.client = VagrantCloudClient{}.New(p.config.VagrantCloudUrl, p.config.AccessToken)
   102  
   103  	// The name of the provider for vagrant cloud, and vagrant
   104  	providerName := providerFromBuilderName(artifact.Id())
   105  
   106  	// Set up the state
   107  	state := new(multistep.BasicStateBag)
   108  	state.Put("config", p.config)
   109  	state.Put("client", p.client)
   110  	state.Put("artifact", artifact)
   111  	state.Put("artifactFilePath", artifact.Files()[0])
   112  	state.Put("ui", ui)
   113  	state.Put("providerName", providerName)
   114  
   115  	// Build the steps
   116  	steps := []multistep.Step{
   117  		new(stepVerifyBox),
   118  		new(stepCreateVersion),
   119  		new(stepCreateProvider),
   120  		new(stepPrepareUpload),
   121  		new(stepUpload),
   122  		new(stepVerifyUpload),
   123  		new(stepReleaseVersion),
   124  	}
   125  
   126  	// Run the steps
   127  	if p.config.PackerDebug {
   128  		p.runner = &multistep.DebugRunner{
   129  			Steps:   steps,
   130  			PauseFn: common.MultistepDebugFn(ui),
   131  		}
   132  	} else {
   133  		p.runner = &multistep.BasicRunner{Steps: steps}
   134  	}
   135  
   136  	p.runner.Run(state)
   137  
   138  	// If there was an error, return that
   139  	if rawErr, ok := state.GetOk("error"); ok {
   140  		return nil, false, rawErr.(error)
   141  	}
   142  
   143  	return NewArtifact(providerName, p.config.Tag), true, nil
   144  }
   145  
   146  // Runs a cleanup if the post processor fails to upload
   147  func (p *PostProcessor) Cancel() {
   148  	if p.runner != nil {
   149  		log.Println("Cancelling the step runner...")
   150  		p.runner.Cancel()
   151  	}
   152  }
   153  
   154  // converts a packer builder name to the corresponding vagrant
   155  // provider
   156  func providerFromBuilderName(name string) string {
   157  	switch name {
   158  	case "aws":
   159  		return "aws"
   160  	case "digitalocean":
   161  		return "digitalocean"
   162  	case "virtualbox":
   163  		return "virtualbox"
   164  	case "vmware":
   165  		return "vmware_desktop"
   166  	case "parallels":
   167  		return "parallels"
   168  	default:
   169  		return name
   170  	}
   171  }