func (c *PushCommand) Run(args []string) int { var token string var message string var name string var create bool f := c.Meta.FlagSet("push", FlagSetVars) f.Usage = func() { c.Ui.Error(c.Help()) } f.StringVar(&token, "token", "", "token") f.StringVar(&message, "m", "", "message") f.StringVar(&message, "message", "", "message") f.StringVar(&name, "name", "", "name") f.BoolVar(&create, "create", false, "create (deprecated)") if err := f.Parse(args); err != nil { return 1 } if message != "" { c.Ui.Say("[DEPRECATED] -m/-message is deprecated and will be removed in a future Packer release") } args = f.Args() if len(args) != 1 { f.Usage() 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.")) } // Parse the template tpl, err := template.ParseFile(args[0]) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err)) return 1 } // Get the core core, err := c.Meta.Core(tpl) if err != nil { c.Ui.Error(err.Error()) return 1 } push := core.Template.Push // If we didn't pass name from the CLI, use the template if name == "" { name = push.Name } // Validate some things if name == "" { c.Ui.Error(fmt.Sprintf( "The 'push' section must be specified in the template with\n" + "at least the 'name' option set. Alternatively, you can pass the\n" + "name parameter from the CLI.")) return 1 } if !reName.MatchString(name) { c.Ui.Error(errInvalidName.Error()) return 1 } // Determine our token if token == "" { token = push.Token } // Build our client defer func() { c.client = nil }() c.client = atlas.DefaultClient() if push.Address != "" { c.client, err = atlas.NewClient(push.Address) if err != nil { c.Ui.Error(fmt.Sprintf( "Error setting up API client: %s", err)) return 1 } } if token != "" { c.client.Token = token } // Build the archiving options var opts archive.ArchiveOpts opts.Include = push.Include opts.Exclude = push.Exclude opts.VCS = push.VCS opts.Extra = map[string]string{ archiveTemplateEntry: args[0], } // Determine the path we're archiving. This logic is a bit complicated // as there are three possibilities: // // 1.) BaseDir is an absolute path, just use that. // // 2.) BaseDir is empty, so we use the directory of the template. // // 3.) BaseDir is relative, so we use the path relative to the directory // of the template. // path := push.BaseDir if path == "" || !filepath.IsAbs(path) { tplPath, err := filepath.Abs(args[0]) if err != nil { c.Ui.Error(fmt.Sprintf("Error determining path to archive: %s", err)) return 1 } tplPath = filepath.Dir(tplPath) if path != "" { tplPath = filepath.Join(tplPath, path) } path, err = filepath.Abs(tplPath) if err != nil { c.Ui.Error(fmt.Sprintf("Error determining path to archive: %s", err)) return 1 } } // Find the Atlas post-processors, if possible var atlasPPs []*template.PostProcessor for _, list := range tpl.PostProcessors { for _, pp := range list { if pp.Type == "atlas" { atlasPPs = append(atlasPPs, pp) } } } // Build the upload options var uploadOpts uploadOpts uploadOpts.Slug = name uploadOpts.Builds = make(map[string]*uploadBuildInfo) for _, b := range tpl.Builders { info := &uploadBuildInfo{Type: b.Type} // Determine if we're artifacting this build for _, pp := range atlasPPs { if !pp.Skip(b.Name) { info.Artifact = true break } } 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 { if b.Artifact { continue } badBuilds = append(badBuilds, name) } if len(badBuilds) > 0 { c.Ui.Error(fmt.Sprintf( "Warning! One or more of the builds in this template does not\n"+ "have an Atlas post-processor. Artifacts from this template will\n"+ "not appear in the Atlas artifact registry.\n\n"+ "This is just a warning. Atlas will still build your template\n"+ "and assume other post-processors are sending the artifacts where\n"+ "they need to go.\n\n"+ "Builds: %s\n\n", strings.Join(badBuilds, ", "))) } // Start the archiving process r, err := archive.CreateArchive(path, &opts) if err != nil { c.Ui.Error(fmt.Sprintf("Error archiving: %s", err)) return 1 } defer r.Close() // Start the upload process doneCh, uploadErrCh, err := c.upload(r, &uploadOpts) if err != nil { c.Ui.Error(fmt.Sprintf("Error starting upload: %s", err)) return 1 } // Make a ctrl-C channel sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) defer signal.Stop(sigCh) err = nil select { case err = <-uploadErrCh: err = fmt.Errorf("Error uploading: %s", err) case <-sigCh: err = fmt.Errorf("Push cancelled from Ctrl-C") case <-doneCh: } if err != nil { c.Ui.Error(err.Error()) return 1 } c.Ui.Say(fmt.Sprintf("Push successful to '%s'", name)) return 0 }
func (c *PushCommand) Run(args []string) int { var create bool var token string 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") if err := f.Parse(args); err != nil { return 1 } args = f.Args() if len(args) != 1 { f.Usage() return 1 } // Read the template tpl, err := packer.ParseTemplateFile(args[0], nil) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err)) return 1 } // Validate some things if tpl.Push.Name == "" { c.Ui.Error(fmt.Sprintf( "The 'push' section must be specified in the template with\n" + "at least the 'name' option set.")) return 1 } // Determine our token if token == "" { token = tpl.Push.Token } // Build our client defer func() { c.client = nil }() c.client = atlas.DefaultClient() if tpl.Push.Address != "" { c.client, err = atlas.NewClient(tpl.Push.Address) if err != nil { c.Ui.Error(fmt.Sprintf( "Error setting up API client: %s", err)) return 1 } } if token != "" { c.client.Token = token } // Build the archiving options var opts archive.ArchiveOpts opts.Include = tpl.Push.Include opts.Exclude = tpl.Push.Exclude opts.VCS = tpl.Push.VCS opts.Extra = map[string]string{ archiveTemplateEntry: args[0], } // Determine the path we're archiving. This logic is a bit complicated // as there are three possibilities: // // 1.) BaseDir is an absolute path, just use that. // // 2.) BaseDir is empty, so we use the directory of the template. // // 3.) BaseDir is relative, so we use the path relative to the directory // of the template. // path := tpl.Push.BaseDir if path == "" || !filepath.IsAbs(path) { tplPath, err := filepath.Abs(args[0]) if err != nil { c.Ui.Error(fmt.Sprintf("Error determining path to archive: %s", err)) return 1 } tplPath = filepath.Dir(tplPath) if path != "" { tplPath = filepath.Join(tplPath, path) } path, err = filepath.Abs(tplPath) if err != nil { c.Ui.Error(fmt.Sprintf("Error determining path to archive: %s", err)) return 1 } } // Find the Atlas post-processors, if possible var atlasPPs []packer.RawPostProcessorConfig for _, list := range tpl.PostProcessors { for _, pp := range list { if pp.Type == "atlas" { atlasPPs = append(atlasPPs, pp) } } } // Build the upload options var uploadOpts uploadOpts uploadOpts.Slug = tpl.Push.Name uploadOpts.Builds = make(map[string]*uploadBuildInfo) for _, b := range tpl.Builders { info := &uploadBuildInfo{Type: b.Type} // Determine if we're artifacting this build for _, pp := range atlasPPs { if !pp.Skip(b.Name) { info.Artifact = true break } } uploadOpts.Builds[b.Name] = info } // Warn about builds not having post-processors. var badBuilds []string for name, b := range uploadOpts.Builds { if b.Artifact { continue } badBuilds = append(badBuilds, name) } if len(badBuilds) > 0 { c.Ui.Error(fmt.Sprintf( "Warning! One or more of the builds in this template does not\n"+ "have an Atlas post-processor. Artifacts from this template will\n"+ "not appear in the Atlas artifact registry.\n\n"+ "This is just a warning. Atlas will still build your template\n"+ "and assume other post-processors are sending the artifacts where\n"+ "they need to go.\n\n"+ "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 { c.Ui.Error(fmt.Sprintf("Error archiving: %s", err)) return 1 } defer r.Close() // Start the upload process doneCh, uploadErrCh, err := c.upload(r, &uploadOpts) if err != nil { c.Ui.Error(fmt.Sprintf("Error starting upload: %s", err)) return 1 } // Make a ctrl-C channel sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) defer signal.Stop(sigCh) err = nil select { case err = <-uploadErrCh: err = fmt.Errorf("Error uploading: %s", err) case <-sigCh: err = fmt.Errorf("Push cancelled from Ctrl-C") case <-doneCh: } if err != nil { c.Ui.Error(err.Error()) return 1 } c.Ui.Output(fmt.Sprintf("Push successful to '%s'", tpl.Push.Name)) return 0 }
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) { if _, err := p.client.Artifact(p.config.user, p.config.name); err != nil { if err != atlas.ErrNotFound { return nil, false, fmt.Errorf( "Error finding artifact: %s", err) } // Artifact doesn't exist, create it ui.Message(fmt.Sprintf("Creating artifact: %s", p.config.Artifact)) _, err = p.client.CreateArtifact(p.config.user, p.config.name) if err != nil { return nil, false, fmt.Errorf( "Error creating artifact: %s", err) } } opts := &atlas.UploadArtifactOpts{ User: p.config.user, Name: p.config.name, Type: p.config.Type, ID: artifact.Id(), Metadata: p.metadata(artifact), BuildId: p.config.buildId, } if fs := artifact.Files(); len(fs) > 0 { var archiveOpts archive.ArchiveOpts // We have files. We want to compress/upload them. If we have just // one file, then we use it as-is. Otherwise, we compress all of // them into a single file. var path string if len(fs) == 1 { path = fs[0] } else { path = longestCommonPrefix(fs) if path == "" { return nil, false, fmt.Errorf( "No common prefix for achiving files: %v", fs) } // Modify the archive options to only include the files // that are in our file list. include := make([]string, 0, len(fs)) for i, f := range fs { include[i] = strings.Replace(f, path, "", 1) } archiveOpts.Include = include } r, err := archive.CreateArchive(path, &archiveOpts) if err != nil { return nil, false, fmt.Errorf( "Error archiving artifact: %s", err) } defer r.Close() opts.File = r opts.FileSize = r.Size } ui.Message("Uploading artifact version...") var av *atlas.ArtifactVersion doneCh := make(chan struct{}) errCh := make(chan error, 1) go func() { var err error av, err = p.client.UploadArtifact(opts) if err != nil { errCh <- err return } close(doneCh) }() select { case err := <-errCh: return nil, false, fmt.Errorf("Error uploading: %s", err) case <-doneCh: } return &Artifact{ Name: p.config.Artifact, Type: p.config.Type, Version: av.Version, }, true, nil }