post-processor/vagrant-cloud: initial commit
This commit is contained in:
parent
e2acb8e988
commit
7d4efdc236
|
@ -47,7 +47,8 @@ const defaultConfig = `
|
||||||
"vagrant": "packer-post-processor-vagrant",
|
"vagrant": "packer-post-processor-vagrant",
|
||||||
"vsphere": "packer-post-processor-vsphere",
|
"vsphere": "packer-post-processor-vsphere",
|
||||||
"docker-push": "packer-post-processor-docker-push",
|
"docker-push": "packer-post-processor-docker-push",
|
||||||
"docker-import": "packer-post-processor-docker-import"
|
"docker-import": "packer-post-processor-docker-import",
|
||||||
|
"vagrant-cloud": "packer-post-processor-vagrant-cloud"
|
||||||
},
|
},
|
||||||
|
|
||||||
"provisioners": {
|
"provisioners": {
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/packer/packer/plugin"
|
||||||
|
"github.com/mitchellh/packer/post-processor/vagrant-cloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
server, err := plugin.Server()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
server.RegisterPostProcessor(new(vagrantcloud.PostProcessor))
|
||||||
|
server.Serve()
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package main
|
|
@ -0,0 +1,97 @@
|
||||||
|
package vagrantcloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Box struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type VagrantCloudClient struct {
|
||||||
|
// The http client for communicating
|
||||||
|
client *http.Client
|
||||||
|
|
||||||
|
// The base URL of the API
|
||||||
|
BaseURL string
|
||||||
|
|
||||||
|
// Access token
|
||||||
|
AccessToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VagrantCloudClient) New(baseUrl string, token string) *VagrantCloudClient {
|
||||||
|
c := &VagrantCloudClient{
|
||||||
|
client: &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BaseURL: baseUrl,
|
||||||
|
AccessToken: token,
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeBody(resp *http.Response, out interface{}) error {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
dec := json.NewDecoder(resp.Body)
|
||||||
|
return dec.Decode(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeBody is used to encode a request body
|
||||||
|
func encodeBody(obj interface{}) (io.Reader, error) {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
enc := json.NewEncoder(buf)
|
||||||
|
if err := enc.Encode(obj); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VagrantCloudClient) Box(tag string) (*Box, error) {
|
||||||
|
resp, err := v.Get(tag)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error retrieving box: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
box := &Box{}
|
||||||
|
|
||||||
|
if err = decodeBody(resp, box); err != nil {
|
||||||
|
return nil, fmt.Errorf("Error parsing box response: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return box, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VagrantCloudClient) Get(path string) (*http.Response, error) {
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("access_token", v.AccessToken)
|
||||||
|
reqUrl := fmt.Sprintf("%s/%s%s", v.BaseURL, path, params.Encode())
|
||||||
|
|
||||||
|
// Scrub API key for logs
|
||||||
|
scrubbedUrl := strings.Replace(reqUrl, v.AccessToken, "ACCESS_TOKEN", -1)
|
||||||
|
log.Printf("Post-Processor Vagrant Cloud API GET: %s", scrubbedUrl)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", reqUrl, nil)
|
||||||
|
resp, err := v.client.Do(req)
|
||||||
|
|
||||||
|
log.Printf("Post-Processor Vagrant Cloud API Response: \n\n%s", resp)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VagrantCloudClient) Post(path string, body map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
|
||||||
|
// Scrub API key for logs
|
||||||
|
scrubbedUrl := strings.Replace(path, v.AccessToken, "ACCESS_TOKEN", -1)
|
||||||
|
log.Printf("Post-Processor Vagrant Cloud API POST: %s. \n\n Body: %s", scrubbedUrl, body)
|
||||||
|
return nil, nil
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package vagrantcloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const BuilderId = "pearkes.post-processor.vagrant-cloud"
|
||||||
|
|
||||||
|
type Artifact struct {
|
||||||
|
Path string
|
||||||
|
Provider string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewArtifact(provider, path string) *Artifact {
|
||||||
|
return &Artifact{
|
||||||
|
Path: path,
|
||||||
|
Provider: provider,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Artifact) BuilderId() string {
|
||||||
|
return BuilderId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) Files() []string {
|
||||||
|
return []string{a.Path}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) Id() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) String() string {
|
||||||
|
return fmt.Sprintf("'%s' provider box: %s", a.Provider, a.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) Destroy() error {
|
||||||
|
return os.Remove(a.Path)
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package vagrantcloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestArtifact_ImplementsArtifact(t *testing.T) {
|
||||||
|
var raw interface{}
|
||||||
|
raw = &Artifact{}
|
||||||
|
if _, ok := raw.(packer.Artifact); !ok {
|
||||||
|
t.Fatalf("Artifact should be a Artifact")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
// 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/packer/common"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
const VAGRANT_CLOUD_URL = "https://vagrantcloud.com"
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
Tag string `mapstructure:"box_tag"`
|
||||||
|
Version string `mapstructure:"version"`
|
||||||
|
|
||||||
|
AccessToken string `mapstructure:"access_token"`
|
||||||
|
VagrantCloudUrl string `mapstructure:"vagrant_cloud_url"`
|
||||||
|
|
||||||
|
tpl *packer.ConfigTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostProcessor struct {
|
||||||
|
config Config
|
||||||
|
client *VagrantCloudClient
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
config := p.config
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
// The name of the provider for vagrant cloud, and vagrant
|
||||||
|
provider := providerFromBuilderName(artifact.Id())
|
||||||
|
version := p.config.Version
|
||||||
|
tag := p.config.Tag
|
||||||
|
|
||||||
|
// create the HTTP client
|
||||||
|
p.client = VagrantCloudClient{}.New(p.config.VagrantCloudUrl, p.config.AccessToken)
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Verifying box is accessible: %s", tag))
|
||||||
|
|
||||||
|
box, err := p.client.Box(tag)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if box.Tag != tag {
|
||||||
|
ui.Say(fmt.Sprintf("Could not verify box is correct: %s", tag))
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Creating Version %s", version))
|
||||||
|
ui.Say(fmt.Sprintf("Creating Provider %s", version))
|
||||||
|
ui.Say(fmt.Sprintf("Uploading Box %s", version))
|
||||||
|
ui.Say(fmt.Sprintf("Verifying upload %s", version))
|
||||||
|
ui.Say(fmt.Sprintf("Releasing version %s", version))
|
||||||
|
|
||||||
|
return NewArtifact(provider, config.Tag), true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs a cleanup if the post processor fails to upload
|
||||||
|
func (p *PostProcessor) Cleanup() {
|
||||||
|
// Delete the version
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package vagrantcloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testConfig() map[string]interface{} {
|
||||||
|
return map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPP(t *testing.T) *PostProcessor {
|
||||||
|
var p PostProcessor
|
||||||
|
if err := p.Configure(testConfig()); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUi() *packer.BasicUi {
|
||||||
|
return &packer.BasicUi{
|
||||||
|
Reader: new(bytes.Buffer),
|
||||||
|
Writer: new(bytes.Buffer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
|
||||||
|
var _ packer.PostProcessor = new(PostProcessor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestproviderFromBuilderName(t *testing.T) {
|
||||||
|
if providerFromBuilderName("foobar") != "foobar" {
|
||||||
|
t.Fatal("should copy unknown provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
if providerFromBuilderName("vmware") != "vmware_desktop" {
|
||||||
|
t.Fatal("should convert provider")
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ func (a *Artifact) Files() []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Artifact) Id() string {
|
func (a *Artifact) Id() string {
|
||||||
return ""
|
return a.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Artifact) String() string {
|
func (a *Artifact) String() string {
|
||||||
|
|
|
@ -12,3 +12,10 @@ func TestArtifact_ImplementsArtifact(t *testing.T) {
|
||||||
t.Fatalf("Artifact should be a Artifact")
|
t.Fatalf("Artifact should be a Artifact")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestArtifact_Id(t *testing.T) {
|
||||||
|
artifact := NewArtifact("vmware", "./")
|
||||||
|
if artifact.Id() != "vmware" {
|
||||||
|
t.Fatalf("should return name as Id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue