Merge pull request #2153 from mitchellh/f-interpolate-push
Interpolate the "push" config
This commit is contained in:
commit
b507173f27
|
@ -28,6 +28,7 @@ func testMeta(t *testing.T) Meta {
|
||||||
var out, err bytes.Buffer
|
var out, err bytes.Buffer
|
||||||
|
|
||||||
return Meta{
|
return Meta{
|
||||||
|
CoreConfig: packer.TestCoreConfig(t),
|
||||||
Ui: &packer.BasicUi{
|
Ui: &packer.BasicUi{
|
||||||
Writer: &out,
|
Writer: &out,
|
||||||
ErrorWriter: &err,
|
ErrorWriter: &err,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
@ -37,7 +36,7 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
var name string
|
var name string
|
||||||
var create bool
|
var create bool
|
||||||
|
|
||||||
f := flag.NewFlagSet("push", flag.ContinueOnError)
|
f := c.Meta.FlagSet("push", FlagSetVars)
|
||||||
f.Usage = func() { c.Ui.Error(c.Help()) }
|
f.Usage = func() { c.Ui.Error(c.Help()) }
|
||||||
f.StringVar(&token, "token", "", "token")
|
f.StringVar(&token, "token", "", "token")
|
||||||
f.StringVar(&message, "m", "", "message")
|
f.StringVar(&message, "m", "", "message")
|
||||||
|
@ -67,9 +66,17 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
return 1
|
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 we didn't pass name from the CLI, use the template
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = tpl.Push.Name
|
name = push.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate some things
|
// Validate some things
|
||||||
|
@ -83,14 +90,14 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
|
|
||||||
// Determine our token
|
// Determine our token
|
||||||
if token == "" {
|
if token == "" {
|
||||||
token = tpl.Push.Token
|
token = push.Token
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build our client
|
// Build our client
|
||||||
defer func() { c.client = nil }()
|
defer func() { c.client = nil }()
|
||||||
c.client = atlas.DefaultClient()
|
c.client = atlas.DefaultClient()
|
||||||
if tpl.Push.Address != "" {
|
if push.Address != "" {
|
||||||
c.client, err = atlas.NewClient(tpl.Push.Address)
|
c.client, err = atlas.NewClient(push.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(
|
c.Ui.Error(fmt.Sprintf(
|
||||||
"Error setting up API client: %s", err))
|
"Error setting up API client: %s", err))
|
||||||
|
@ -103,9 +110,9 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
|
|
||||||
// Build the archiving options
|
// Build the archiving options
|
||||||
var opts archive.ArchiveOpts
|
var opts archive.ArchiveOpts
|
||||||
opts.Include = tpl.Push.Include
|
opts.Include = push.Include
|
||||||
opts.Exclude = tpl.Push.Exclude
|
opts.Exclude = push.Exclude
|
||||||
opts.VCS = tpl.Push.VCS
|
opts.VCS = push.VCS
|
||||||
opts.Extra = map[string]string{
|
opts.Extra = map[string]string{
|
||||||
archiveTemplateEntry: args[0],
|
archiveTemplateEntry: args[0],
|
||||||
}
|
}
|
||||||
|
@ -120,7 +127,7 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
// 3.) BaseDir is relative, so we use the path relative to the directory
|
// 3.) BaseDir is relative, so we use the path relative to the directory
|
||||||
// of the template.
|
// of the template.
|
||||||
//
|
//
|
||||||
path := tpl.Push.BaseDir
|
path := push.BaseDir
|
||||||
if path == "" || !filepath.IsAbs(path) {
|
if path == "" || !filepath.IsAbs(path) {
|
||||||
tplPath, err := filepath.Abs(args[0])
|
tplPath, err := filepath.Abs(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -150,7 +157,7 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
|
|
||||||
// Build the upload options
|
// Build the upload options
|
||||||
var uploadOpts uploadOpts
|
var uploadOpts uploadOpts
|
||||||
uploadOpts.Slug = tpl.Push.Name
|
uploadOpts.Slug = push.Name
|
||||||
uploadOpts.Builds = make(map[string]*uploadBuildInfo)
|
uploadOpts.Builds = make(map[string]*uploadBuildInfo)
|
||||||
for _, b := range tpl.Builders {
|
for _, b := range tpl.Builders {
|
||||||
info := &uploadBuildInfo{Type: b.Type}
|
info := &uploadBuildInfo{Type: b.Type}
|
||||||
|
@ -229,7 +236,7 @@ func (c *PushCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Ui.Say(fmt.Sprintf("Push successful to '%s'", tpl.Push.Name))
|
c.Ui.Say(fmt.Sprintf("Push successful to '%s'", push.Name))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,6 +264,10 @@ Options:
|
||||||
"username/name".
|
"username/name".
|
||||||
|
|
||||||
-token=<token> The access token to use to when uploading
|
-token=<token> The access token to use to when uploading
|
||||||
|
|
||||||
|
-var 'key=value' Variable for templates, can be used multiple times.
|
||||||
|
|
||||||
|
-var-file=path JSON file containing user variables.
|
||||||
`
|
`
|
||||||
|
|
||||||
return strings.TrimSpace(helpText)
|
return strings.TrimSpace(helpText)
|
||||||
|
|
|
@ -190,6 +190,35 @@ func TestPush_uploadErrorCh(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPush_vars(t *testing.T) {
|
||||||
|
var actualOpts *uploadOpts
|
||||||
|
uploadFn := func(r io.Reader, opts *uploadOpts) (<-chan struct{}, <-chan error, error) {
|
||||||
|
actualOpts = opts
|
||||||
|
|
||||||
|
doneCh := make(chan struct{})
|
||||||
|
close(doneCh)
|
||||||
|
return doneCh, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &PushCommand{
|
||||||
|
Meta: testMeta(t),
|
||||||
|
uploadFn: uploadFn,
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"-var", "name=foo/bar",
|
||||||
|
filepath.Join(testFixture("push-vars"), "template.json"),
|
||||||
|
}
|
||||||
|
if code := c.Run(args); code != 0 {
|
||||||
|
fatalCommand(t, c.Meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := "foo/bar"
|
||||||
|
if actualOpts.Slug != expected {
|
||||||
|
t.Fatalf("bad: %#v", actualOpts.Slug)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testArchive(t *testing.T, r io.Reader) []string {
|
func testArchive(t *testing.T, r io.Reader) []string {
|
||||||
// Finish the archiving process in-memory
|
// Finish the archiving process in-memory
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"variables": {
|
||||||
|
"name": null
|
||||||
|
},
|
||||||
|
|
||||||
|
"builders": [{"type": "dummy"}],
|
||||||
|
|
||||||
|
"push": {
|
||||||
|
"name": "{{user `name`}}"
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,8 +12,9 @@ import (
|
||||||
// Core is the main executor of Packer. If Packer is being used as a
|
// Core is the main executor of Packer. If Packer is being used as a
|
||||||
// library, this is the struct you'll want to instantiate to get anything done.
|
// library, this is the struct you'll want to instantiate to get anything done.
|
||||||
type Core struct {
|
type Core struct {
|
||||||
|
Template *template.Template
|
||||||
|
|
||||||
components ComponentFinder
|
components ComponentFinder
|
||||||
template *template.Template
|
|
||||||
variables map[string]string
|
variables map[string]string
|
||||||
builds map[string]*template.Builder
|
builds map[string]*template.Builder
|
||||||
}
|
}
|
||||||
|
@ -51,8 +52,8 @@ type ComponentFinder struct {
|
||||||
// NewCore creates a new Core.
|
// NewCore creates a new Core.
|
||||||
func NewCore(c *CoreConfig) (*Core, error) {
|
func NewCore(c *CoreConfig) (*Core, error) {
|
||||||
result := &Core{
|
result := &Core{
|
||||||
|
Template: c.Template,
|
||||||
components: c.Components,
|
components: c.Components,
|
||||||
template: c.Template,
|
|
||||||
variables: c.Variables,
|
variables: c.Variables,
|
||||||
}
|
}
|
||||||
if err := result.validate(); err != nil {
|
if err := result.validate(); err != nil {
|
||||||
|
@ -66,7 +67,7 @@ func NewCore(c *CoreConfig) (*Core, error) {
|
||||||
// to do this at this point with the variables.
|
// to do this at this point with the variables.
|
||||||
result.builds = make(map[string]*template.Builder)
|
result.builds = make(map[string]*template.Builder)
|
||||||
for _, b := range c.Template.Builders {
|
for _, b := range c.Template.Builders {
|
||||||
v, err := interpolate.Render(b.Name, result.context())
|
v, err := interpolate.Render(b.Name, result.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"Error interpolating builder '%s': %s",
|
"Error interpolating builder '%s': %s",
|
||||||
|
@ -112,8 +113,8 @@ func (c *Core) Build(n string) (Build, error) {
|
||||||
rawName := configBuilder.Name
|
rawName := configBuilder.Name
|
||||||
|
|
||||||
// Setup the provisioners for this build
|
// Setup the provisioners for this build
|
||||||
provisioners := make([]coreBuildProvisioner, 0, len(c.template.Provisioners))
|
provisioners := make([]coreBuildProvisioner, 0, len(c.Template.Provisioners))
|
||||||
for _, rawP := range c.template.Provisioners {
|
for _, rawP := range c.Template.Provisioners {
|
||||||
// If we're skipping this, then ignore it
|
// If we're skipping this, then ignore it
|
||||||
if rawP.Skip(rawName) {
|
if rawP.Skip(rawName) {
|
||||||
continue
|
continue
|
||||||
|
@ -155,8 +156,8 @@ func (c *Core) Build(n string) (Build, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the post-processors
|
// Setup the post-processors
|
||||||
postProcessors := make([][]coreBuildPostProcessor, 0, len(c.template.PostProcessors))
|
postProcessors := make([][]coreBuildPostProcessor, 0, len(c.Template.PostProcessors))
|
||||||
for _, rawPs := range c.template.PostProcessors {
|
for _, rawPs := range c.Template.PostProcessors {
|
||||||
current := make([]coreBuildPostProcessor, 0, len(rawPs))
|
current := make([]coreBuildPostProcessor, 0, len(rawPs))
|
||||||
for _, rawP := range rawPs {
|
for _, rawP := range rawPs {
|
||||||
// If we skip, ignore
|
// If we skip, ignore
|
||||||
|
@ -201,11 +202,19 @@ func (c *Core) Build(n string) (Build, error) {
|
||||||
builderType: configBuilder.Type,
|
builderType: configBuilder.Type,
|
||||||
postProcessors: postProcessors,
|
postProcessors: postProcessors,
|
||||||
provisioners: provisioners,
|
provisioners: provisioners,
|
||||||
templatePath: c.template.Path,
|
templatePath: c.Template.Path,
|
||||||
variables: c.variables,
|
variables: c.variables,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Context returns an interpolation context.
|
||||||
|
func (c *Core) Context() *interpolate.Context {
|
||||||
|
return &interpolate.Context{
|
||||||
|
TemplatePath: c.Template.Path,
|
||||||
|
UserVariables: c.variables,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// validate does a full validation of the template.
|
// validate does a full validation of the template.
|
||||||
//
|
//
|
||||||
// This will automatically call template.validate() in addition to doing
|
// This will automatically call template.validate() in addition to doing
|
||||||
|
@ -213,13 +222,13 @@ func (c *Core) Build(n string) (Build, error) {
|
||||||
func (c *Core) validate() error {
|
func (c *Core) validate() error {
|
||||||
// First validate the template in general, we can't do anything else
|
// First validate the template in general, we can't do anything else
|
||||||
// unless the template itself is valid.
|
// unless the template itself is valid.
|
||||||
if err := c.template.Validate(); err != nil {
|
if err := c.Template.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate variables are set
|
// Validate variables are set
|
||||||
var err error
|
var err error
|
||||||
for n, v := range c.template.Variables {
|
for n, v := range c.Template.Variables {
|
||||||
if v.Required {
|
if v.Required {
|
||||||
if _, ok := c.variables[n]; !ok {
|
if _, ok := c.variables[n]; !ok {
|
||||||
err = multierror.Append(err, fmt.Errorf(
|
err = multierror.Append(err, fmt.Errorf(
|
||||||
|
@ -241,10 +250,10 @@ func (c *Core) init() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go through the variables and interpolate the environment variables
|
// Go through the variables and interpolate the environment variables
|
||||||
ctx := c.context()
|
ctx := c.Context()
|
||||||
ctx.EnableEnv = true
|
ctx.EnableEnv = true
|
||||||
ctx.UserVariables = nil
|
ctx.UserVariables = nil
|
||||||
for k, v := range c.template.Variables {
|
for k, v := range c.Template.Variables {
|
||||||
// Ignore variables that are required
|
// Ignore variables that are required
|
||||||
if v.Required {
|
if v.Required {
|
||||||
continue
|
continue
|
||||||
|
@ -266,12 +275,10 @@ func (c *Core) init() error {
|
||||||
c.variables[k] = def
|
c.variables[k] = def
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interpolate the push configuration
|
||||||
|
if _, err := interpolate.RenderInterface(&c.Template.Push, c.Context()); err != nil {
|
||||||
|
return fmt.Errorf("Error interpolating 'push': %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) context() *interpolate.Context {
|
|
||||||
return &interpolate.Context{
|
|
||||||
TemplatePath: c.template.Path,
|
|
||||||
UserVariables: c.variables,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -368,6 +368,40 @@ func TestCoreBuild_templatePath(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCore_pushInterpolate(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
File string
|
||||||
|
Vars map[string]string
|
||||||
|
Result template.Push
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"push-vars.json",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
template.Push{Name: "bar"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
tpl, err := template.ParseFile(fixtureDir(tc.File))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s\n\n%s", tc.File, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
core, err := NewCore(&CoreConfig{
|
||||||
|
Template: tpl,
|
||||||
|
Variables: tc.Vars,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s\n\n%s", tc.File, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := core.Template.Push
|
||||||
|
if !reflect.DeepEqual(expected, tc.Result) {
|
||||||
|
t.Fatalf("err: %s\n\n%#v", tc.File, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCoreValidate(t *testing.T) {
|
func TestCoreValidate(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
File string
|
File string
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"variables": {
|
||||||
|
"foo": null
|
||||||
|
},
|
||||||
|
|
||||||
|
"builders": [{"type": "test"}],
|
||||||
|
|
||||||
|
"push": {
|
||||||
|
"name": "{{user `foo`}}"
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,47 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestRenderInterface(t *testing.T) {
|
||||||
|
type Test struct {
|
||||||
|
Foo string
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]struct {
|
||||||
|
Input interface{}
|
||||||
|
Output interface{}
|
||||||
|
}{
|
||||||
|
"basic": {
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "{{upper `bar`}}",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"foo": "BAR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"struct": {
|
||||||
|
&Test{
|
||||||
|
Foo: "{{upper `bar`}}",
|
||||||
|
},
|
||||||
|
&Test{
|
||||||
|
Foo: "BAR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := &Context{}
|
||||||
|
for k, tc := range cases {
|
||||||
|
actual, err := RenderInterface(tc.Input, ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s\n\n%s", k, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(actual, tc.Output) {
|
||||||
|
t.Fatalf("err: %s\n\n%#v\n\n%#v", k, actual, tc.Output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRenderMap(t *testing.T) {
|
func TestRenderMap(t *testing.T) {
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
Input interface{}
|
Input interface{}
|
||||||
|
|
Loading…
Reference in New Issue