provisioner/chef-solo: properly escape user vars for JSON
/cc @mwhooker - I moved the processing up into Prepare so that any errors will be shown during a validate pass. Also, I escape some stuff in keys. Tests moved as well.
This commit is contained in:
parent
6914baa473
commit
662e74d070
|
@ -1,11 +1,11 @@
|
|||
package packer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func TestConfigTemplateProcess_timestamp(t *testing.T) {
|
||||
|
@ -48,39 +48,6 @@ func TestConfigTemplateProcess_user(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestJsonTemplateProcess_user(t *testing.T) {
|
||||
tpl, err := NewConfigTemplate()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
tpl.UserVars["foo"] = "bar"
|
||||
jsonData := make(map[string]interface{})
|
||||
jsonData["key"] = map[string]string{
|
||||
"key1": "{{user `foo`}}",
|
||||
}
|
||||
jsonBytes, err := json.MarshalIndent(jsonData, "", " ")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
var jsonString = string(jsonBytes)
|
||||
|
||||
jsonString, err = tpl.Process(jsonString, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
var dat map[string]map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(jsonString), &dat); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if dat["key"]["key1"] != "bar" {
|
||||
t.Fatalf("found %s instead", dat["key"]["key1"])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
func TestConfigTemplateValidate(t *testing.T) {
|
||||
tpl, err := NewConfigTemplate()
|
||||
if err != nil {
|
||||
|
|
|
@ -95,6 +95,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||
sliceTemplates := map[string][]string{
|
||||
"cookbook_paths": p.config.CookbookPaths,
|
||||
"remote_cookbook_paths": p.config.RemoteCookbookPaths,
|
||||
"run_list": p.config.RunList,
|
||||
}
|
||||
|
||||
for n, slice := range sliceTemplates {
|
||||
|
@ -129,6 +130,14 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Process the user variables within the JSON and set the JSON.
|
||||
// Do this early so that we can validate and show errors.
|
||||
p.config.Json, err = p.processJsonUserVars()
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Error processing user variables in JSON: %s", err))
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
@ -136,6 +145,46 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) processJsonUserVars() (map[string]interface{}, error) {
|
||||
jsonBytes, err := json.Marshal(p.config.Json)
|
||||
if err != nil {
|
||||
// This really shouldn't happen since we literally just unmarshalled
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Copy the user variables so that we can restore them later, and
|
||||
// make sure we make the quotes JSON-friendly in the user variables.
|
||||
originalUserVars := make(map[string]string)
|
||||
for k, v := range p.config.tpl.UserVars {
|
||||
originalUserVars[k] = v
|
||||
}
|
||||
|
||||
// Make sure we reset them no matter what
|
||||
defer func() {
|
||||
p.config.tpl.UserVars = originalUserVars
|
||||
}()
|
||||
|
||||
// Make the current user variables JSON string safe.
|
||||
for k, v := range p.config.tpl.UserVars {
|
||||
v = strings.Replace(v, `\`, `\\`, -1)
|
||||
v = strings.Replace(v, `"`, `\"`, -1)
|
||||
p.config.tpl.UserVars[k] = v
|
||||
}
|
||||
|
||||
// Process the bytes with the template processor
|
||||
jsonBytesProcessed, err := p.config.tpl.Process(string(jsonBytes), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(jsonBytesProcessed), &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||
if !p.config.SkipInstall {
|
||||
if err := p.installChef(ui, comm); err != nil {
|
||||
|
@ -236,14 +285,9 @@ func (p *Provisioner) createJson(ui packer.Ui, comm packer.Communicator) (string
|
|||
return "", err
|
||||
}
|
||||
|
||||
jsonBytesProcessed, err := p.config.tpl.Process(string(jsonBytes), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Upload the bytes
|
||||
remotePath := filepath.Join(p.config.StagingDir, "node.json")
|
||||
if err := comm.Upload(remotePath, bytes.NewReader([]byte(jsonBytesProcessed))); err != nil {
|
||||
if err := comm.Upload(remotePath, bytes.NewReader(jsonBytes)); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
|
|
@ -51,3 +51,24 @@ func TestProvisionerPrepare_cookbookPaths(t *testing.T) {
|
|||
t.Fatalf("unexpected: %#v", p.config.CookbookPaths)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionerPrepare_json(t *testing.T) {
|
||||
config := testConfig()
|
||||
config["json"] = map[string]interface{}{
|
||||
"foo": "{{ user `foo` }}",
|
||||
}
|
||||
|
||||
config[packer.UserVariablesConfigKey] = map[string]string{
|
||||
"foo": `"bar\baz"`,
|
||||
}
|
||||
|
||||
var p Provisioner
|
||||
err := p.Prepare(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if p.config.Json["foo"] != `"bar\baz"` {
|
||||
t.Fatalf("bad: %#v", p.config.Json)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue