provisioner/chef-solo: installChef fixed up to use tpl and StartWithUi

This commit is contained in:
Mitchell Hashimoto 2013-08-25 21:07:37 -07:00
parent c0d02b1593
commit 4a5a8c6006
2 changed files with 58 additions and 42 deletions

View File

@ -1,6 +1,7 @@
// This package implements a provisioner for Packer that executes
// shell scripts within the remote machine.
package chefSolo
// This package implements a provisioner for Packer that uses
// Chef to provision the remote machine, specifically with chef-solo (that is,
// without a Chef server).
package chefsolo
import (
"bufio"
@ -8,7 +9,7 @@ import (
"encoding/json"
"fmt"
"github.com/mitchellh/iochan"
"github.com/mitchellh/mapstructure"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
"io"
"io/ioutil"
@ -28,26 +29,21 @@ const (
var Ui packer.Ui
type config struct {
// An array of local paths of cookbooks to upload.
type Config struct {
common.PackerConfig `mapstructure:",squash"`
CookbooksPaths []string `mapstructure:"cookbooks_paths"`
InstallCommand string `mapstructure:"install_command"`
Recipes []string
Json map[string]interface{}
PreventSudo bool `mapstructure:"prevent_sudo"`
SkipInstall bool `mapstructure:"skip_install"`
// An array of recipes to run.
Recipes []string
// A string of JSON that will be used as the JSON attributes for the
// Chef run.
Json map[string]interface{}
// Option to avoid sudo use when executing commands. Defaults to false.
PreventSudo bool `mapstructure:"prevent_sudo"`
// If true, skips installing Chef. Defaults to false.
SkipInstall bool `mapstructure:"skip_install"`
tpl *packer.ConfigTemplate
}
type Provisioner struct {
config config
config Config
}
type ExecuteRecipeTemplate struct {
@ -56,29 +52,41 @@ type ExecuteRecipeTemplate struct {
Sudo bool
}
type ExecuteInstallChefTemplate struct {
PreventSudo bool
type InstallChefTemplate struct {
Sudo bool
}
func (p *Provisioner) Prepare(raws ...interface{}) error {
errs := make([]error, 0)
for _, raw := range raws {
if err := mapstructure.Decode(raw, &p.config); err != nil {
return err
}
md, err := common.DecodeConfig(&p.config, raws...)
if err != nil {
return err
}
p.config.tpl, err = packer.NewConfigTemplate()
if err != nil {
return err
}
p.config.tpl.UserVars = p.config.PackerUserVars
// Accumulate any errors
errs := common.CheckUnusedConfig(md)
if p.config.CookbooksPaths == nil {
p.config.CookbooksPaths = []string{DefaultCookbooksPath}
}
if p.config.InstallCommand == "" {
p.config.InstallCommand = "curl -L https://www.opscode.com/chef/install.sh | {{if .Sudo}}sudo {{end}}bash"
}
if p.config.Recipes == nil {
p.config.Recipes = make([]string, 0)
}
if p.config.Json != nil {
if _, err := json.Marshal(p.config.Json); err != nil {
errs = append(errs, fmt.Errorf("Bad JSON: %s", err))
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Bad JSON: %s", err))
}
} else {
p.config.Json = make(map[string]interface{})
@ -88,12 +96,13 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
pFileInfo, err := os.Stat(path)
if err != nil || !pFileInfo.IsDir() {
errs = append(errs, fmt.Errorf("Bad cookbook path '%s': %s", path, err))
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Bad cookbook path '%s': %s", path, err))
}
}
if len(errs) > 0 {
return &packer.MultiError{errs}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
@ -104,9 +113,8 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
Ui = ui
if !p.config.SkipInstall {
err = InstallChefSolo(p.config.PreventSudo, comm)
if err != nil {
return fmt.Errorf("Error installing Chef Solo: %s", err)
if err := p.installChef(ui, comm); err != nil {
return fmt.Errorf("Error installing Chef: %s", err)
}
}
@ -315,16 +323,24 @@ func CreateAttributesJson(jsonAttrs map[string]interface{}, recipes []string, co
return remotePath, nil
}
func InstallChefSolo(preventSudo bool, comm packer.Communicator) (err error) {
Ui.Say("Installing Chef Solo")
func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator) error {
ui.Message("Installing Chef...")
var command bytes.Buffer
t := template.Must(template.New("install-chef").Parse("curl -L https://www.opscode.com/chef/install.sh | {{if .sudo}}sudo {{end}}bash"))
t.Execute(&command, map[string]bool{"sudo": !preventSudo})
err = executeCommand(command.String(), comm)
command, err := p.config.tpl.Process(p.config.InstallCommand, &InstallChefTemplate{
Sudo: !p.config.PreventSudo,
})
if err != nil {
return fmt.Errorf("Unable to install Chef Solo: %d", err)
return err
}
cmd := &packer.RemoteCmd{Command: command}
if err := cmd.StartWithUi(comm, ui); err != nil {
return err
}
if cmd.ExitStatus != 0 {
return fmt.Errorf(
"Install script exited with non-zero exit status %d", cmd.ExitStatus)
}
return nil

View File

@ -1,4 +1,4 @@
package chefSolo
package chefsolo
import (
"github.com/mitchellh/packer/packer"