From fa2f277c67f545b745c027211066c7eda4d805bf Mon Sep 17 00:00:00 2001 From: Jerry Clinesmith Date: Tue, 10 Sep 2013 22:00:29 -0500 Subject: [PATCH] #348: chef-solo provisioner: add support for data_bags and roles --- provisioner/chef-solo/provisioner.go | 64 +++++++++++++++++++++-- provisioner/chef-solo/provisioner_test.go | 22 ++++++++ 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/provisioner/chef-solo/provisioner.go b/provisioner/chef-solo/provisioner.go index fe96ad467..7aa4cfc59 100644 --- a/provisioner/chef-solo/provisioner.go +++ b/provisioner/chef-solo/provisioner.go @@ -18,6 +18,8 @@ type Config struct { common.PackerConfig `mapstructure:",squash"` CookbookPaths []string `mapstructure:"cookbook_paths"` + RolesPath string `mapstructure:"roles_path"` + DataBagsPath string `mapstructure:"data_bags_path"` ExecuteCommand string `mapstructure:"execute_command"` InstallCommand string `mapstructure:"install_command"` RemoteCookbookPaths []string `mapstructure:"remote_cookbook_paths"` @@ -35,7 +37,9 @@ type Provisioner struct { } type ConfigTemplate struct { - CookbookPaths string + CookbookPaths string + RolesPath string + DataBagsPath string } type ExecuteTemplate struct { @@ -129,6 +133,24 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { errs, fmt.Errorf("Bad cookbook path '%s': %s", path, err)) } } + + if p.config.RolesPath != "" { + pFileInfo, err := os.Stat(p.config.RolesPath) + + if err != nil || !pFileInfo.IsDir() { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Bad roles path '%s': %s", p.config.RolesPath, err)) + } + } + + if p.config.DataBagsPath != "" { + pFileInfo, err := os.Stat(p.config.DataBagsPath) + + if err != nil || !pFileInfo.IsDir() { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Bad data bags path '%s': %s", p.config.DataBagsPath, err)) + } + } // Process the user variables within the JSON and set the JSON. // Do this early so that we can validate and show errors. @@ -165,8 +187,24 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { cookbookPaths = append(cookbookPaths, targetPath) } + + rolesPath := "" + if p.config.RolesPath != "" { + rolesPath := fmt.Sprintf("%s/roles", p.config.StagingDir) + if err := p.uploadDirectory(ui, comm, rolesPath, p.config.RolesPath); err != nil { + return fmt.Errorf("Error uploading roles: %s", err) + } + } - configPath, err := p.createConfig(ui, comm, cookbookPaths) + dataBagsPath := "" + if p.config.DataBagsPath != "" { + dataBagsPath := fmt.Sprintf("%s/data_bags", p.config.StagingDir) + if err := p.uploadDirectory(ui, comm, dataBagsPath, p.config.DataBagsPath); err != nil { + return fmt.Errorf("Error uploading data bags: %s", err) + } + } + + configPath, err := p.createConfig(ui, comm, cookbookPaths, rolesPath, dataBagsPath) if err != nil { return fmt.Errorf("Error creating Chef config file: %s", err) } @@ -203,7 +241,7 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds return comm.UploadDir(dst, src, nil) } -func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, localCookbooks []string) (string, error) { +func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, localCookbooks []string, rolesPath string, dataBagsPath string) (string, error) { ui.Message("Creating configuration file 'solo.rb'") cookbook_paths := make([]string, len(p.config.RemoteCookbookPaths)+len(localCookbooks)) @@ -215,9 +253,21 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, local i = len(p.config.RemoteCookbookPaths) + i cookbook_paths[i] = fmt.Sprintf(`"%s"`, path) } + + roles_path := "" + if rolesPath != "" { + roles_path = fmt.Sprintf(`"%s"`, rolesPath) + } + + data_bags_path := "" + if dataBagsPath != "" { + data_bags_path = fmt.Sprintf(`"%s"`, dataBagsPath) + } configString, err := p.config.tpl.Process(DefaultConfigTemplate, &ConfigTemplate{ CookbookPaths: strings.Join(cookbook_paths, ","), + RolesPath: roles_path, + DataBagsPath: data_bags_path, }) if err != nil { return "", err @@ -368,5 +418,11 @@ func (p *Provisioner) processJsonUserVars() (map[string]interface{}, error) { } var DefaultConfigTemplate = ` -cookbook_path [{{.CookbookPaths}}] +cookbook_path [{{.CookbookPaths}}] +{{if .RolesPath != ""}} +role_path {{.RolesPath}} +{{end}} +{{if .DataBagsPath != ""}} +data_bag_path {{.DataBagsPath}} +{{end}} ` diff --git a/provisioner/chef-solo/provisioner_test.go b/provisioner/chef-solo/provisioner_test.go index 332718d01..73d882e95 100644 --- a/provisioner/chef-solo/provisioner_test.go +++ b/provisioner/chef-solo/provisioner_test.go @@ -32,11 +32,25 @@ func TestProvisionerPrepare_cookbookPaths(t *testing.T) { t.Fatalf("err: %s", err) } + rolesPath, err := ioutil.TempDir("", "roles") + if err != nil { + t.Fatalf("err: %s", err) + } + + dataBagsPath, err := ioutil.TempDir("", "data_bags") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.Remove(path1) defer os.Remove(path2) + defer os.Remove(rolesPath) + defer os.Remove(dataBagsPath) config := testConfig() config["cookbook_paths"] = []string{path1, path2} + config["roles_path"] = rolesPath + config["data_bags_path"] = dataBagsPath err = p.Prepare(config) if err != nil { @@ -50,6 +64,14 @@ func TestProvisionerPrepare_cookbookPaths(t *testing.T) { if p.config.CookbookPaths[0] != path1 || p.config.CookbookPaths[1] != path2 { t.Fatalf("unexpected: %#v", p.config.CookbookPaths) } + + if p.config.RolesPath != rolesPath { + t.Fatalf("unexpected: %#v", p.config.RolesPath) + } + + if p.config.DataBagsPath != dataBagsPath { + t.Fatalf("unexpected: %#v", p.config.DataBagsPath) + } } func TestProvisionerPrepare_json(t *testing.T) {