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