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