diff --git a/packer/core.go b/packer/core.go index a3f33a816..16780d086 100644 --- a/packer/core.go +++ b/packer/core.go @@ -2,6 +2,7 @@ package packer import ( "fmt" + "regexp" "sort" "strings" @@ -346,13 +347,24 @@ func (c *Core) validate() error { return err } -func (c *Core) init() error { - if c.variables == nil { - c.variables = make(map[string]string) +func isDoneInterpolating(v string) (bool, error) { + // Check for whether the var contains any more references to `user`, wrapped + // in interpolation syntax. + filter := "{{\\s*|user\\s*|\\x60?.*\\x60?}}" + matched, err := regexp.MatchString(filter, v) + if err != nil { + return false, fmt.Errorf("Can't tell if interpolation is done: %s", err) } - // Go through the variables and interpolate the environment and - // user variables + if matched { + // not done interpolating; there's still a call to "user" in a template + // engine + return false, nil + } + // No more calls to "user" as a template engine, so we're done. + return true, nil +} +func (c *Core) renderVarsRecursively() (*interpolate.Context, error) { ctx := c.Context() ctx.EnableEnv = true ctx.UserVariables = make(map[string]string) @@ -386,15 +398,15 @@ func (c *Core) init() error { // We need to read the keys from both, then loop over all of them to figure // out the appropriate interpolations. - allVariables := make(map[string]string) + repeatMap := make(map[string]string) // load in template variables for k, v := range c.Template.Variables { - allVariables[k] = v.Default + repeatMap[k] = v.Default } // overwrite template variables with command-line-read variables for k, v := range c.variables { - allVariables[k] = v + repeatMap[k] = v } // Regex to exclude any build function variable or template variable @@ -405,7 +417,7 @@ func (c *Core) init() error { for i := 0; i < 100; i++ { shouldRetry = false // First, loop over the variables in the template - for k, v := range allVariables { + for k, v := range repeatMap { // Interpolate the default renderedV, err := interpolate.RenderRegex(v, ctx, renderFilter) switch err.(type) { @@ -415,16 +427,27 @@ func (c *Core) init() error { changed = true c.variables[k] = renderedV ctx.UserVariables = c.variables + // Remove fully-interpolated variables from the map, and flag + // variables that still need interpolating for a repeat. + done, err := isDoneInterpolating(v) + if err != nil { + return ctx, err + } + if done { + delete(repeatMap, k) + } else { + shouldRetry = true + } case ttmp.ExecError: castError := err.(ttmp.ExecError) if strings.Contains(castError.Error(), interpolate.ErrVariableNotSetString) { shouldRetry = true failedInterpolation = fmt.Sprintf(`"%s": "%s"; error: %s`, k, v, err) } else { - return err + return ctx, err } default: - return fmt.Errorf( + return ctx, fmt.Errorf( // unexpected interpolation error: abort the run "error interpolating default value for '%s': %s", k, err) @@ -436,12 +459,25 @@ func (c *Core) init() error { } if !changed && shouldRetry { - return fmt.Errorf("Failed to interpolate %s: Please make sure that "+ + return ctx, fmt.Errorf("Failed to interpolate %s: Please make sure that "+ "the variable you're referencing has been defined; Packer treats "+ "all variables used to interpolate other user varaibles as "+ "required.", failedInterpolation) } + return ctx, nil +} + +func (c *Core) init() error { + if c.variables == nil { + c.variables = make(map[string]string) + } + // Go through the variables and interpolate the environment and + // user variables + ctx, err := c.renderVarsRecursively() + if err != nil { + return err + } for _, v := range c.Template.SensitiveVariables { secret := ctx.UserVariables[v.Key] c.secrets = append(c.secrets, secret)