diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index 1347376f0..c7672a68d 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -6,6 +6,7 @@ import ( "github.com/mitchellh/packer/packer" "os" "path/filepath" + "strings" ) const DefaultStagingDir = "/tmp/packer-provisioner-ansible-local" @@ -23,9 +24,21 @@ type Config struct { // An array of local paths of roles to upload. RolePaths []string `mapstructure:"role_paths"` + // Path to group_vars directory + GroupVars string `mapstructure:"group_vars"` + + // Path to host_vars directory + HostVars string `mapstructure:"host_vars"` + // The directory where files will be uploaded. Packer requires write // permissions in this directory. StagingDir string `mapstructure:"staging_directory"` + + // The command to run ansible + Command string + + // Extra options to pass to the ansible command + ExtraArguments []string `mapstructure:"extra_arguments"` } type Provisioner struct { @@ -48,14 +61,22 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { // Accumulate any errors errs := common.CheckUnusedConfig(md) + // Defaults if p.config.StagingDir == "" { p.config.StagingDir = DefaultStagingDir } + if p.config.Command == "" { + p.config.Command = "ansible-playbook" + } + // Templates templates := map[string]*string{ "playbook_file": &p.config.PlaybookFile, "staging_dir": &p.config.StagingDir, + "command": &p.config.Command, + "group_vars": &p.config.GroupVars, + "host_vars": &p.config.HostVars, } for n, ptr := range templates { @@ -68,8 +89,9 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } sliceTemplates := map[string][]string{ - "playbook_paths": p.config.PlaybookPaths, - "role_paths": p.config.RolePaths, + "playbook_paths": p.config.PlaybookPaths, + "role_paths": p.config.RolePaths, + "extra_arguments": p.config.ExtraArguments, } for n, slice := range sliceTemplates { @@ -99,6 +121,20 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { errs = packer.MultiErrorAppend(errs, err) } } + + // Check that the group_vars directory exists, if configured + if len(p.config.GroupVars) > 0 { + if err := validateDirConfig(p.config.GroupVars, "group_vars"); err != nil { + errs = packer.MultiErrorAppend(errs, err) + } + } + + // Check that the host_vars directory exists, if configured + if len(p.config.HostVars) > 0 { + if err := validateDirConfig(p.config.HostVars, "host_vars"); err != nil { + errs = packer.MultiErrorAppend(errs, err) + } + } if errs != nil && len(errs.Errors) > 0 { return errs } @@ -120,6 +156,26 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { return fmt.Errorf("Error uploading main playbook: %s", err) } + // Upload group_vars + if len(p.config.GroupVars) > 0 { + ui.Message("Uploading group_vars directory...") + src := p.config.GroupVars + dst := filepath.Join(p.config.StagingDir, "group_vars") + if err := p.uploadDir(ui, comm, dst, src); err != nil { + return fmt.Errorf("Error uploading group_vars directory: %s", err) + } + } + + // Upload host_vars + if len(p.config.HostVars) > 0 { + ui.Message("Uploading host_vars directory...") + src := p.config.HostVars + dst := filepath.Join(p.config.StagingDir, "host_vars") + if err := p.uploadDir(ui, comm, dst, src); err != nil { + return fmt.Errorf("Error uploading host_vars directory: %s", err) + } + } + if len(p.config.RolePaths) > 0 { ui.Message("Uploading role directories...") for _, src := range p.config.RolePaths { @@ -161,7 +217,13 @@ func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator) err // The inventory must be set to "127.0.0.1,". The comma is important // as its the only way to override the ansible inventory when dealing // with a single host. - command := fmt.Sprintf("ansible-playbook %s -c local -i %s", playbook, `"127.0.0.1,"`) + var command string + if len(p.config.ExtraArguments) > 0 { + command = fmt.Sprintf("%s %s %s -c local -i \"127.0.0.1,\"", p.config.Command, + playbook, strings.Join(p.config.ExtraArguments, " ")) + } else { + command = fmt.Sprintf("%s %s -c local -i \"127.0.0.1,\"", p.config.Command, playbook) + } ui.Message(fmt.Sprintf("Executing Ansible: %s", command)) cmd := &packer.RemoteCmd{ diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index 21f83affd..2504cb4b1 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -120,4 +120,28 @@ func TestProvisionerPrepare_Dirs(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } + + config["group_vars"] = playbook_file.Name() + err = p.Prepare(config) + if err == nil { + t.Fatalf("should error if group_vars path is not a dir") + } + + config["group_vars"] = os.TempDir() + err = p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } + + config["host_vars"] = playbook_file.Name() + err = p.Prepare(config) + if err == nil { + t.Fatalf("should error if host_vars path is not a dir") + } + + config["host_vars"] = os.TempDir() + err = p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } } diff --git a/website/source/docs/provisioners/ansible-local.html.markdown b/website/source/docs/provisioners/ansible-local.html.markdown index 6c309d7ba..a5e423509 100644 --- a/website/source/docs/provisioners/ansible-local.html.markdown +++ b/website/source/docs/provisioners/ansible-local.html.markdown @@ -34,6 +34,11 @@ Required: Optional: +* `command` (string) - The command to invoke ansible. Defaults to "ansible-playbook". + +* `extra_arguments` (array of strings) - An array of extra arguments to pass to the + ansible command. By default, this is empty. + * `playbook_paths` (array of strings) - An array of paths to playbook files on your local system. These will be uploaded to the remote machine under `staging_directory`/playbooks. By default, this is empty.