packer-cn/post-processor/vagrant-cloud/post-processor.go

172 lines
4.2 KiB
Go

// vagrant_cloud implements the packer.PostProcessor interface and adds a
// post-processor that uploads artifacts from the vagrant post-processor
// to Vagrant Cloud (vagrantcloud.com)
package vagrantcloud
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
"log"
"strings"
)
const VAGRANT_CLOUD_URL = "https://vagrantcloud.com/api/v1"
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Tag string `mapstructure:"box_tag"`
Version string `mapstructure:"version"`
VersionDescription string `mapstructure:"version_description"`
NoRelease bool `mapstructure:"no_release"`
AccessToken string `mapstructure:"access_token"`
VagrantCloudUrl string `mapstructure:"vagrant_cloud_url"`
tpl *packer.ConfigTemplate
}
type PostProcessor struct {
config Config
client *VagrantCloudClient
runner multistep.Runner
}
func (p *PostProcessor) Configure(raws ...interface{}) error {
_, err := common.DecodeConfig(&p.config, raws...)
if err != nil {
return err
}
p.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return err
}
p.config.tpl.UserVars = p.config.PackerUserVars
// Default configuration
if p.config.VagrantCloudUrl == "" {
p.config.VagrantCloudUrl = VAGRANT_CLOUD_URL
}
// Accumulate any errors
errs := new(packer.MultiError)
// required configuration
templates := map[string]*string{
"box_tag": &p.config.Tag,
"version": &p.config.Version,
"access_token": &p.config.AccessToken,
}
for key, ptr := range templates {
if *ptr == "" {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("%s must be set", key))
}
}
// Template process
for key, ptr := range templates {
*ptr, err = p.config.tpl.Process(*ptr, nil)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Error processing %s: %s", key, err))
}
}
if len(errs.Errors) > 0 {
return errs
}
return nil
}
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
// Only accepts input from the vagrant post-processor
if artifact.BuilderId() != "mitchellh.post-processor.vagrant" {
return nil, false, fmt.Errorf(
"Unknown artifact type, requires box from vagrant post-processor: %s", artifact.BuilderId())
}
// We assume that there is only one .box file to upload
if !strings.HasSuffix(artifact.Files()[0], ".box") {
return nil, false, fmt.Errorf(
"Unknown files in artifact from vagrant post-processor: %s", artifact.Files())
}
// create the HTTP client
p.client = VagrantCloudClient{}.New(p.config.VagrantCloudUrl, p.config.AccessToken)
// The name of the provider for vagrant cloud, and vagrant
providerName := providerFromBuilderName(artifact.Id())
// Set up the state
state := new(multistep.BasicStateBag)
state.Put("config", p.config)
state.Put("client", p.client)
state.Put("artifact", artifact)
state.Put("artifactFilePath", artifact.Files()[0])
state.Put("ui", ui)
state.Put("providerName", providerName)
// Build the steps
steps := []multistep.Step{
new(stepVerifyBox),
new(stepCreateVersion),
new(stepCreateProvider),
new(stepPrepareUpload),
new(stepUpload),
new(stepVerifyUpload),
new(stepReleaseVersion),
}
// Run the steps
if p.config.PackerDebug {
p.runner = &multistep.DebugRunner{
Steps: steps,
PauseFn: common.MultistepDebugFn(ui),
}
} else {
p.runner = &multistep.BasicRunner{Steps: steps}
}
p.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state.GetOk("error"); ok {
return nil, false, rawErr.(error)
}
return NewArtifact(providerName, p.config.Tag), true, nil
}
// Runs a cleanup if the post processor fails to upload
func (p *PostProcessor) Cancel() {
if p.runner != nil {
log.Println("Cancelling the step runner...")
p.runner.Cancel()
}
}
// converts a packer builder name to the corresponding vagrant
// provider
func providerFromBuilderName(name string) string {
switch name {
case "aws":
return "aws"
case "digitalocean":
return "digitalocean"
case "virtualbox":
return "virtualbox"
case "vmware":
return "vmware_desktop"
case "parallels":
return "parallels"
default:
return name
}
}