Commit dc023297 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

provisioner/chef-solo: ability to specify custom chef template

parent 9f52b786
## 0.3.8 (unreleased) ## 0.3.8 (unreleased)
FEATURES:
* provisioner/chef-solo: Ability to specify a custom Chef configuration
template.
IMPROVEMENTS: IMPROVEMENTS:
* builder/amazon/*: Interrupts work while waiting for AMI to be ready. * builder/amazon/*: Interrupts work while waiting for AMI to be ready.
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"github.com/mitchellh/packer/common" "github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
...@@ -17,6 +18,7 @@ import ( ...@@ -17,6 +18,7 @@ import (
type Config struct { type Config struct {
common.PackerConfig `mapstructure:",squash"` common.PackerConfig `mapstructure:",squash"`
ConfigTemplate string `mapstructure:"config_template"`
CookbookPaths []string `mapstructure:"cookbook_paths"` CookbookPaths []string `mapstructure:"cookbook_paths"`
ExecuteCommand string `mapstructure:"execute_command"` ExecuteCommand string `mapstructure:"execute_command"`
InstallCommand string `mapstructure:"install_command"` InstallCommand string `mapstructure:"install_command"`
...@@ -80,7 +82,8 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { ...@@ -80,7 +82,8 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
errs := common.CheckUnusedConfig(md) errs := common.CheckUnusedConfig(md)
templates := map[string]*string{ templates := map[string]*string{
"staging_dir": &p.config.StagingDir, "config_template": &p.config.ConfigTemplate,
"staging_dir": &p.config.StagingDir,
} }
for n, ptr := range templates { for n, ptr := range templates {
...@@ -121,6 +124,17 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { ...@@ -121,6 +124,17 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
} }
} }
if p.config.ConfigTemplate != "" {
fi, err := os.Stat(p.config.ConfigTemplate)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Bad config template path: %s", err))
} else if fi.IsDir() {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Config template path must be a file: %s", err))
}
}
for _, path := range p.config.CookbookPaths { for _, path := range p.config.CookbookPaths {
pFileInfo, err := os.Stat(path) pFileInfo, err := os.Stat(path)
...@@ -216,7 +230,24 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, local ...@@ -216,7 +230,24 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, local
cookbook_paths[i] = fmt.Sprintf(`"%s"`, path) cookbook_paths[i] = fmt.Sprintf(`"%s"`, path)
} }
configString, err := p.config.tpl.Process(DefaultConfigTemplate, &ConfigTemplate{ // Read the template
tpl := DefaultConfigTemplate
if p.config.ConfigTemplate != "" {
f, err := os.Open(p.config.ConfigTemplate)
if err != nil {
return "", err
}
defer f.Close()
tplBytes, err := ioutil.ReadAll(f)
if err != nil {
return "", err
}
tpl = string(tplBytes)
}
configString, err := p.config.tpl.Process(tpl, &ConfigTemplate{
CookbookPaths: strings.Join(cookbook_paths, ","), CookbookPaths: strings.Join(cookbook_paths, ","),
}) })
if err != nil { if err != nil {
......
...@@ -19,6 +19,49 @@ func TestProvisioner_Impl(t *testing.T) { ...@@ -19,6 +19,49 @@ func TestProvisioner_Impl(t *testing.T) {
} }
} }
func TestProvisionerPrepare_configTemplate(t *testing.T) {
var err error
var p Provisioner
// Test no config template
config := testConfig()
delete(config, "config_template")
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a file
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(tf.Name())
config = testConfig()
config["config_template"] = tf.Name()
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a directory
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config = testConfig()
config["config_template"] = td
p = Provisioner{}
err = p.Prepare(config)
if err == nil {
t.Fatal("should have err")
}
}
func TestProvisionerPrepare_cookbookPaths(t *testing.T) { func TestProvisionerPrepare_cookbookPaths(t *testing.T) {
var p Provisioner var p Provisioner
......
...@@ -32,6 +32,13 @@ The example below is fully functional and expects cookbooks in the ...@@ -32,6 +32,13 @@ The example below is fully functional and expects cookbooks in the
The reference of available configuration options is listed below. No The reference of available configuration options is listed below. No
configuration is actually required, but at least `run_list` is recommended. configuration is actually required, but at least `run_list` is recommended.
* `config_template` (string) - Path to a template that will be used for
the Chef configuration file. By default Packer only sets configuration
it needs to match the settings set in the provisioner configuration. If
you need to set configurations that the Packer provisioner doesn't support,
then you should use a custom configuration template. See the dedicated
"Chef Configuration" section below for more details.
* `cookbook_paths` (array of strings) - This is an array of paths to * `cookbook_paths` (array of strings) - This is an array of paths to
"cookbooks" directories on your local filesystem. These will be uploaded "cookbooks" directories on your local filesystem. These will be uploaded
to the remote machine in the directory specified by the `staging_directory`. to the remote machine in the directory specified by the `staging_directory`.
...@@ -70,6 +77,25 @@ configuration is actually required, but at least `run_list` is recommended. ...@@ -70,6 +77,25 @@ configuration is actually required, but at least `run_list` is recommended.
this folder. If the permissions are not correct, use a shell provisioner this folder. If the permissions are not correct, use a shell provisioner
prior to this to configure it properly. prior to this to configure it properly.
## Chef Configuration
By default, Packer uses a simple Chef configuration file in order to set
the options specified for the provisioner. But Chef is a complex tool that
supports many configuration options. Packer allows you to specify a custom
configuration template if you'd like to set custom configurations.
The default value for the configuration template is:
```
cookbook_path [{{.CookbookPaths}}]
```
This template is a [configuration template](/docs/templates/configuration-templates.html)
and has a set of variables available to use:
* `CookbookPaths` is the set of cookbook paths ready to embedded directly
into a Ruby array to configure Chef.
## Execute Command ## Execute Command
By default, Packer uses the following command (broken across multiple lines By default, Packer uses the following command (broken across multiple lines
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment