From ec1adec0296ee34f16bf2236ff136caa0ca316cc Mon Sep 17 00:00:00 2001 From: jamie brim Date: Wed, 22 Jan 2014 04:40:06 -0800 Subject: [PATCH 1/2] provisioner/ansible: configurable command / args allow users to provide a replacement command / extra args to the ansible-local provisioner. this is useful, for example, to unbuffer output when using the docker builder, or to modify ansible behavior, like enabling verbose mode, injecting variables, or changing paths. --- provisioner/ansible-local/provisioner.go | 26 ++++++++++++++++--- .../provisioners/ansible-local.html.markdown | 5 ++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index 1347376f0..ff4e0649c 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" @@ -26,6 +27,12 @@ type Config struct { // 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 +55,20 @@ 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, } for n, ptr := range templates { @@ -68,8 +81,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 { @@ -161,7 +175,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/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. From 458d90c71d51089514d5959e45ba8f531783da96 Mon Sep 17 00:00:00 2001 From: Bernard McKeever Date: Sun, 19 Jan 2014 02:48:12 +0000 Subject: [PATCH 2/2] provisioner/ansible: support {group,host}_vars ansible looks for group and host vars directories as well as playbooks this revision to the provisioner uploads those as well. squashed to a single commit by strcrzy. --- provisioner/ansible-local/provisioner.go | 42 +++++++++++++++++++ provisioner/ansible-local/provisioner_test.go | 24 +++++++++++ 2 files changed, 66 insertions(+) diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index ff4e0649c..c7672a68d 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -24,6 +24,12 @@ 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"` @@ -69,6 +75,8 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { "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 { @@ -113,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 } @@ -134,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 { 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) + } }