From 608d874b452d15af767ae458ce13948e9e6521a6 Mon Sep 17 00:00:00 2001 From: James Van Dyke Date: Fri, 5 Jul 2013 21:57:56 -0400 Subject: [PATCH] Make JSON attributes work with real JSON! --- provisioner/chef-solo/provisioner.go | 101 ++++++++++++++++----------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/provisioner/chef-solo/provisioner.go b/provisioner/chef-solo/provisioner.go index b26e8a1cb..25936e1e0 100644 --- a/provisioner/chef-solo/provisioner.go +++ b/provisioner/chef-solo/provisioner.go @@ -17,6 +17,7 @@ import ( "strings" "text/template" "path/filepath" + "encoding/json" ) const RemoteStagingPath = "/tmp/provision/chef-solo" @@ -36,9 +37,14 @@ type config struct { // An array of recipes to run. RunList []string `mapstructure:"run_list"` - // An array of environment variables that will be injected before - // your command(s) are executed. - JsonFile string `mapstructure:"json_file"` + // A string of JSON that will be used as the JSON attributes for the + // Chef run. + Json map[string]interface{} + + UseSudo bool `mapstructure:"use_sudo"` + + // If true + SkipInstall bool `mapstructure:"skip_install"` } type Provisioner struct { @@ -48,7 +54,6 @@ type Provisioner struct { type ExecuteRecipeTemplate struct { SoloRbPath string JsonPath string - RunList string } func (p *Provisioner) Prepare(raws ...interface{}) error { @@ -79,10 +84,12 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { p.config.RunList = make([]string, 0) } - if p.config.JsonFile != "" { - if _, err := os.Stat(p.config.JsonFile); err != nil { - errs = append(errs, fmt.Errorf("Bad JSON attributes file '%s': %s", p.config.JsonFile, err)) + if p.config.Json != nil { + if _, err := json.Marshal(p.config.Json); err != nil { + errs = append(errs, fmt.Errorf("Bad JSON: %s", err)) } + } else { + p.config.Json = make(map[string]interface{}) } for _, path := range p.config.CookbookPaths { @@ -122,7 +129,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { return fmt.Errorf("Error creating Chef Solo configuration file: %s", err) } - jsonPath, err := CreateAttributesJson(p.config.JsonFile, comm) + jsonPath, err := CreateAttributesJson(p.config.Json, p.config.RunList, comm) if err != nil { return fmt.Errorf("Error uploading JSON attributes file: %s", err) } @@ -137,18 +144,19 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { } // Execute requested recipes - for _, recipe := range p.config.RunList { - ui.Say(fmt.Sprintf("chef-solo running recipe: %s", recipe)) - // Compile the command - var command bytes.Buffer - t := template.Must(template.New("chef-run").Parse("sudo chef-solo --no-color -c {{.SoloRbPath}} -j {{.JsonPath}} -o {{.RunList}}")) - t.Execute(&command, &ExecuteRecipeTemplate{soloRbPath, jsonPath, recipe}) - - err = executeCommand(command.String(), comm) - if err != nil { - return fmt.Errorf("Error running recipe %s: %s", recipe, err) - } + ui.Say("Beginning Chef Solo run") + + // Compile the command + var command bytes.Buffer + t := template.Must(template.New("chef-run").Parse("sudo chef-solo --no-color -c {{.SoloRbPath}} -j {{.JsonPath}}")) + t.Execute(&command, &ExecuteRecipeTemplate{soloRbPath, jsonPath}) + + err = executeCommand(command.String(), comm) + if err != nil { + return fmt.Errorf("Error running Chef Solo: %s", err) } + + return fmt.Errorf("Die") return nil } @@ -246,32 +254,43 @@ func CreateSoloRb(cookbookPaths []string, comm packer.Communicator) (str string, return remotePath, nil } -func CreateAttributesJson(jsonFile string, comm packer.Communicator) (str string, err error) { - Ui.Say(fmt.Sprintf("Uploading Chef attributes file %s", jsonFile)) +func CreateAttributesJson(jsonAttrs map[string]interface{}, runList []string, comm packer.Communicator) (str string, err error) { + Ui.Say(fmt.Sprintf("Creating and uploading Chef attributes file")) remotePath := RemoteStagingPath + "/node.json" - // Create an empty JSON file if none given - if jsonFile == "" { - tf, err := ioutil.TempFile("", "packer-chef-solo-json") - if err != nil { - return "", fmt.Errorf("Error preparing Chef attributes file: %s", err) - } - defer os.Remove(tf.Name()) - - // Write our contents to it - writer := bufio.NewWriter(tf) - if _, err := writer.WriteString("{}"); err != nil { - return "", fmt.Errorf("Error preparing Chef attributes file: %s", err) - } - - if err := writer.Flush(); err != nil { - return "", fmt.Errorf("Error preparing Chef attributes file: %s", err) - } - - jsonFile = tf.Name() - tf.Close() + var formattedRunList []string + for _, value := range runList { + formattedRunList = append(formattedRunList, "recipe[" + value + "]") } + // Add RunList to JSON + jsonAttrs["run_list"] = formattedRunList + + // Convert to JSON string + jsonString, err := json.MarshalIndent(jsonAttrs, "", " ") + if err != nil { + return "", fmt.Errorf("Error parsing JSON attributes: %s", err) + } + + tf, err := ioutil.TempFile("", "packer-chef-solo-json") + if err != nil { + return "", fmt.Errorf("Error preparing Chef attributes file: %s", err) + } + defer os.Remove(tf.Name()) + + // Write our contents to it + writer := bufio.NewWriter(tf) + if _, err := writer.WriteString(string(jsonString)); err != nil { + return "", fmt.Errorf("Error preparing Chef attributes file: %s", err) + } + + if err := writer.Flush(); err != nil { + return "", fmt.Errorf("Error preparing Chef attributes file: %s", err) + } + + jsonFile := tf.Name() + tf.Close() + log.Printf("Opening %s for reading", jsonFile) f, err := os.Open(jsonFile) if err != nil {