diff --git a/command/push.go b/command/push.go index 42c1bc465..74915de3f 100644 --- a/command/push.go +++ b/command/push.go @@ -32,13 +32,16 @@ type pushUploadFn func( io.Reader, *uploadOpts) (<-chan struct{}, <-chan error, error) func (c *PushCommand) Run(args []string) int { - var create bool var token string + var message string + var create bool f := flag.NewFlagSet("push", flag.ContinueOnError) f.Usage = func() { c.Ui.Error(c.Help()) } - f.BoolVar(&create, "create", false, "create") f.StringVar(&token, "token", "", "token") + f.StringVar(&message, "m", "", "message") + f.StringVar(&message, "message", "", "message") + f.BoolVar(&create, "create", false, "create (deprecated)") if err := f.Parse(args); err != nil { return 1 } @@ -49,6 +52,12 @@ func (c *PushCommand) Run(args []string) int { return 1 } + // Print deprecations + if create { + c.Ui.Error(fmt.Sprintf("The '-create' option is now the default and is\n" + + "longer used. It will be removed in the next version.")) + } + // Read the template tpl, err := packer.ParseTemplateFile(args[0], nil) if err != nil { @@ -149,6 +158,15 @@ func (c *PushCommand) Run(args []string) int { uploadOpts.Builds[b.Name] = info } + // Add the upload metadata + metadata := make(map[string]interface{}) + if message != "" { + metadata["message"] = message + } + metadata["template"] = tpl.RawContents + metadata["template_name"] = filepath.Base(args[0]) + uploadOpts.Metadata = metadata + // Warn about builds not having post-processors. var badBuilds []string for name, b := range uploadOpts.Builds { @@ -169,12 +187,6 @@ func (c *PushCommand) Run(args []string) int { "Builds: %s\n\n", strings.Join(badBuilds, ", "))) } - // Create the build config if it doesn't currently exist. - if err := c.create(uploadOpts.Slug, create); err != nil { - c.Ui.Error(err.Error()) - return 1 - } - // Start the archiving process r, err := archive.CreateArchive(path, &opts) if err != nil { @@ -217,59 +229,30 @@ func (*PushCommand) Help() string { helpText := ` Usage: packer push [options] TEMPLATE - Push the template and the files it needs to a Packer build service. - This will not initiate any builds, it will only update the templates - used for builds. + Push the given template and supporting files to a Packer build service such as + Atlas. - The configuration about what is pushed is configured within the - template's "push" section. + If a build configuration for the given template does not exist, it will be + created automatically. If the build configuration already exists, a new + version will be created with this template and the supporting files. + + Additional configuration options (such as the Atlas server URL and files to + include) may be specified in the "push" section of the Packer template. Please + see the online documentation for more information about these configurables. Options: - -create Create the build configuration if it doesn't exist. + -m, -message= A message to identify the purpose or changes in this + Packer template much like a VCS commit message - -token= Access token to use to upload. If blank, the - ATLAS_TOKEN environmental variable will be used. + -token= The access token to use to when uploading ` return strings.TrimSpace(helpText) } func (*PushCommand) Synopsis() string { - return "push template files to a Packer build service" -} - -func (c *PushCommand) create(name string, create bool) error { - if c.uploadFn != nil { - return nil - } - - // Separate the slug into the user and name components - user, name, err := atlas.ParseSlug(name) - if err != nil { - return fmt.Errorf("Malformed push name: %s", err) - } - - // Check if it exists. If so, we're done. - if _, err := c.client.BuildConfig(user, name); err == nil { - return nil - } else if err != atlas.ErrNotFound { - return err - } - - // Otherwise, show an error if we're not creating. - if !create { - return fmt.Errorf( - "Push target doesn't exist: %s. Either create this online via\n"+ - "the website or pass the -create flag.", name) - } - - // Create it - if err := c.client.CreateBuildConfig(user, name); err != nil { - return err - } - - return nil + return "push a template and supporting files to a Packer build service" } func (c *PushCommand) upload( @@ -284,10 +267,17 @@ func (c *PushCommand) upload( return nil, nil, fmt.Errorf("upload: %s", err) } - // Get the app + // Get the build configuration bc, err := c.client.BuildConfig(user, name) if err != nil { - return nil, nil, fmt.Errorf("upload: %s", err) + if err == atlas.ErrNotFound { + // Build configuration doesn't exist, attempt to create it + bc, err = c.client.CreateBuildConfig(user, name) + } + + if err != nil { + return nil, nil, fmt.Errorf("upload: %s", err) + } } // Build the version to send up @@ -307,7 +297,7 @@ func (c *PushCommand) upload( // Start the upload doneCh, errCh := make(chan struct{}), make(chan error) go func() { - err := c.client.UploadBuildConfigVersion(&version, r, r.Size) + err := c.client.UploadBuildConfigVersion(&version, opts.Metadata, r, r.Size) if err != nil { errCh <- err return @@ -320,9 +310,10 @@ func (c *PushCommand) upload( } type uploadOpts struct { - URL string - Slug string - Builds map[string]*uploadBuildInfo + URL string + Slug string + Builds map[string]*uploadBuildInfo + Metadata map[string]interface{} } type uploadBuildInfo struct { diff --git a/packer/template.go b/packer/template.go index 5cbef4fac..717ae7682 100644 --- a/packer/template.go +++ b/packer/template.go @@ -3,15 +3,16 @@ package packer import ( "bytes" "fmt" - "github.com/hashicorp/go-version" - "github.com/mitchellh/mapstructure" - jsonutil "github.com/mitchellh/packer/common/json" "io" "io/ioutil" "os" "sort" "text/template" "time" + + "github.com/hashicorp/go-version" + "github.com/mitchellh/mapstructure" + jsonutil "github.com/mitchellh/packer/common/json" ) // The rawTemplate struct represents the structure of a template read @@ -33,6 +34,7 @@ type rawTemplate struct { // The Template struct represents a parsed template, parsed into the most // completed form it can be without additional processing by the caller. type Template struct { + RawContents []byte Description string Variables map[string]RawVariable Builders map[string]RawBuilderConfig @@ -163,6 +165,7 @@ func ParseTemplate(data []byte, vars map[string]string) (t *Template, err error) } t = &Template{} + t.RawContents = data t.Description = rawTpl.Description t.Variables = make(map[string]RawVariable) t.Builders = make(map[string]RawBuilderConfig) diff --git a/packer/template_test.go b/packer/template_test.go index d42895665..bf11fc7d0 100644 --- a/packer/template_test.go +++ b/packer/template_test.go @@ -58,6 +58,10 @@ func TestParseTemplateFile_basic(t *testing.T) { if len(result.Builders) != 1 { t.Fatalf("bad: %#v", result.Builders) } + + if string(result.RawContents) != data { + t.Fatalf("expected %q to be %q", result.RawContents, data) + } } func TestParseTemplateFile_minPackerVersionBad(t *testing.T) { diff --git a/post-processor/atlas/post-processor_test.go b/post-processor/atlas/post-processor_test.go index db35b8790..2f8c63c47 100644 --- a/post-processor/atlas/post-processor_test.go +++ b/post-processor/atlas/post-processor_test.go @@ -9,6 +9,10 @@ import ( ) func TestPostProcessorConfigure(t *testing.T) { + currentEnv := os.Getenv("ATLAS_TOKEN") + os.Setenv("ATLAS_TOKEN", "") + defer os.Setenv("ATLAS_TOKEN", currentEnv) + var p PostProcessor if err := p.Configure(validDefaults()); err != nil { t.Fatalf("err: %s", err) diff --git a/website/source/docs/command-line/push.html.markdown b/website/source/docs/command-line/push.html.markdown index 10165cc22..be4040527 100644 --- a/website/source/docs/command-line/push.html.markdown +++ b/website/source/docs/command-line/push.html.markdown @@ -7,27 +7,42 @@ description: |- # Command-Line: Push -The `packer push` Packer command takes a template and pushes it to a build -service. The build service will automatically build your Packer template and -expose the artifacts. +The `packer push` Packer command takes a template and pushes it to a Packer +build service such as [HashiCorp's Atlas](https://atlas.hashicorp.com). The +build service will automatically build your Packer template and expose the +artifacts. -This command currently only sends templates to -[Atlas](https://atlas.hashicorp.com) by HashiCorp, but the command will -be pluggable in the future with alternate implementations. - -External build services such as Atlas make it easy to iterate on Packer -templates, especially when the builder you're running may not be easily +External build services such as HashiCorp's Atlas make it easy to iterate on +Packer templates, especially when the builder you are running may not be easily accessable (such as developing `qemu` builders on Mac or Windows). -For the `push` command to work, the -[push configuration](/docs/templates/push.html) +!> The Packer build service will receive the raw copy of your Packer template +when you push. **If you have sensitive data in your Packer template, you should +move that data into Packer variables or environment variables!** + +For the `push` command to work, the [push configuration](/docs/templates/push.html) must be completed within the template. ## Options -* `-create=true` - If the build configuration matching the name of the push - doesn't exist, it will be created if this is true. This defaults to true. +* `-message` - A message to identify the purpose or changes in this Packer + template much like a VCS commit message. This message will be passed to the + Packer build service. This option is also available as a short option `-m`. + +* `-token` - An access token for authenticating the push to the Packer build + service such as Atlas. This can also be specified within the push + configuration in the template. + +## Examples + +Push a Packer template: + +```shell +$ packer push -m "Updating the apache version" template.json +``` + +Push a Packer template with a custom token: -* `-token=FOO` - An access token for authenticating the push. This can also - be specified within the push configuration in the template. By setting this - in the template, you can take advantage of user variables. +```shell +$ packer push -token ABCD1234 template.json +``` diff --git a/website/source/docs/templates/push.html.markdown b/website/source/docs/templates/push.html.markdown index 1f97a3048..691d4f34d 100644 --- a/website/source/docs/templates/push.html.markdown +++ b/website/source/docs/templates/push.html.markdown @@ -46,8 +46,8 @@ each category, the available configuration keys are alphabetized. this is `https://atlas.hashicorp.com`. * `base_dir` (string) - The base directory of the files to upload. This - will be the CWD when the build service executes your template. This - path is relative to the template. + will be the current working directory when the build service executes your + template. This path is relative to the template. * `include` (array of strings) - Glob patterns to include relative to the `base_dir`. If this is specified, only files that match the include @@ -57,9 +57,38 @@ each category, the available configuration keys are alphabetized. the `base_dir`. * `token` (string) - An access token to use to authenticate to the build - service. For Atlas, you can retrieve this access token in your account - section by clicking your account name in the upper right corner. + service. * `vcs` (bool) - If true, Packer will detect your VCS (if there is one) and only upload the files that are tracked by the VCS. This is useful - for automatically excluding ignored files. This defaults to true. + for automatically excluding ignored files. This defaults to false. + +## Examples + +A push configuration section with minimal options: + +```javascript +{ + "push": { + "name": "hashicorp/precise64" + } +} +``` + +A push configuration specifying Packer to inspect the VCS and list individual +files to include: + +```javascript +{ + "push": { + "name": "hashicorp/precise64", + "vcs": true, + "include": [ + "other_file/outside_of.vcs" + ] + } +} +``` + +~> **Variable interpolation** is not currently possible in Packer push +configurations. This will be fixed in an upcoming release.