command/push: interpolate
This commit is contained in:
parent
3946346614
commit
579264bb5b
|
@ -28,6 +28,7 @@ func testMeta(t *testing.T) Meta {
|
|||
var out, err bytes.Buffer
|
||||
|
||||
return Meta{
|
||||
CoreConfig: packer.TestCoreConfig(t),
|
||||
Ui: &packer.BasicUi{
|
||||
Writer: &out,
|
||||
ErrorWriter: &err,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -12,6 +11,7 @@ import (
|
|||
"github.com/hashicorp/atlas-go/archive"
|
||||
"github.com/hashicorp/atlas-go/v1"
|
||||
"github.com/mitchellh/packer/template"
|
||||
"github.com/mitchellh/packer/template/interpolate"
|
||||
)
|
||||
|
||||
// archiveTemplateEntry is the name the template always takes within the slug.
|
||||
|
@ -37,7 +37,7 @@ func (c *PushCommand) Run(args []string) int {
|
|||
var name string
|
||||
var create bool
|
||||
|
||||
f := flag.NewFlagSet("push", flag.ContinueOnError)
|
||||
f := c.Meta.FlagSet("push", FlagSetVars)
|
||||
f.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
f.StringVar(&token, "token", "", "token")
|
||||
f.StringVar(&message, "m", "", "message")
|
||||
|
@ -67,9 +67,23 @@ func (c *PushCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
// Get the core
|
||||
core, err := c.Meta.Core(tpl)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
push := tpl.Push
|
||||
pushRaw, err := interpolate.RenderInterface(&push, core.Context())
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
push = *pushRaw.(*template.Push)
|
||||
|
||||
// If we didn't pass name from the CLI, use the template
|
||||
if name == "" {
|
||||
name = tpl.Push.Name
|
||||
name = push.Name
|
||||
}
|
||||
|
||||
// Validate some things
|
||||
|
@ -83,14 +97,14 @@ func (c *PushCommand) Run(args []string) int {
|
|||
|
||||
// Determine our token
|
||||
if token == "" {
|
||||
token = tpl.Push.Token
|
||||
token = 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 push.Address != "" {
|
||||
c.client, err = atlas.NewClient(push.Address)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"Error setting up API client: %s", err))
|
||||
|
@ -103,9 +117,9 @@ func (c *PushCommand) Run(args []string) int {
|
|||
|
||||
// Build the archiving options
|
||||
var opts archive.ArchiveOpts
|
||||
opts.Include = tpl.Push.Include
|
||||
opts.Exclude = tpl.Push.Exclude
|
||||
opts.VCS = tpl.Push.VCS
|
||||
opts.Include = push.Include
|
||||
opts.Exclude = push.Exclude
|
||||
opts.VCS = push.VCS
|
||||
opts.Extra = map[string]string{
|
||||
archiveTemplateEntry: args[0],
|
||||
}
|
||||
|
@ -120,7 +134,7 @@ func (c *PushCommand) Run(args []string) int {
|
|||
// 3.) BaseDir is relative, so we use the path relative to the directory
|
||||
// of the template.
|
||||
//
|
||||
path := tpl.Push.BaseDir
|
||||
path := push.BaseDir
|
||||
if path == "" || !filepath.IsAbs(path) {
|
||||
tplPath, err := filepath.Abs(args[0])
|
||||
if err != nil {
|
||||
|
@ -150,7 +164,7 @@ func (c *PushCommand) Run(args []string) int {
|
|||
|
||||
// Build the upload options
|
||||
var uploadOpts uploadOpts
|
||||
uploadOpts.Slug = tpl.Push.Name
|
||||
uploadOpts.Slug = push.Name
|
||||
uploadOpts.Builds = make(map[string]*uploadBuildInfo)
|
||||
for _, b := range tpl.Builders {
|
||||
info := &uploadBuildInfo{Type: b.Type}
|
||||
|
@ -229,7 +243,7 @@ func (c *PushCommand) Run(args []string) int {
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -257,6 +271,10 @@ Options:
|
|||
"username/name".
|
||||
|
||||
-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)
|
||||
|
|
|
@ -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 {
|
||||
// Finish the archiving process in-memory
|
||||
var buf bytes.Buffer
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"variables": {
|
||||
"name": null
|
||||
},
|
||||
|
||||
"builders": [{"type": "dummy"}],
|
||||
|
||||
"push": {
|
||||
"name": "{{user `name`}}"
|
||||
}
|
||||
}
|
|
@ -66,7 +66,7 @@ func NewCore(c *CoreConfig) (*Core, error) {
|
|||
// to do this at this point with the variables.
|
||||
result.builds = make(map[string]*template.Builder)
|
||||
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 {
|
||||
return nil, fmt.Errorf(
|
||||
"Error interpolating builder '%s': %s",
|
||||
|
@ -206,6 +206,14 @@ func (c *Core) Build(n string) (Build, error) {
|
|||
}, 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.
|
||||
//
|
||||
// This will automatically call template.validate() in addition to doing
|
||||
|
@ -241,7 +249,7 @@ func (c *Core) init() error {
|
|||
}
|
||||
|
||||
// Go through the variables and interpolate the environment variables
|
||||
ctx := c.context()
|
||||
ctx := c.Context()
|
||||
ctx.EnableEnv = true
|
||||
ctx.UserVariables = nil
|
||||
for k, v := range c.template.Variables {
|
||||
|
@ -268,10 +276,3 @@ func (c *Core) init() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) context() *interpolate.Context {
|
||||
return &interpolate.Context{
|
||||
TemplatePath: c.template.Path,
|
||||
UserVariables: c.variables,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,47 @@ import (
|
|||
"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) {
|
||||
cases := map[string]struct {
|
||||
Input interface{}
|
||||
|
|
Loading…
Reference in New Issue