diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index f08c2985f..89d3cd05b 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -39,6 +39,9 @@ type Config struct { // The directory where files will be uploaded. Packer requires write // permissions in this directory. StagingDir string `mapstructure:"staging_directory"` + + // The optional inventory file + InventoryFile string `mapstructure:"inventory_file"` } type Provisioner struct { @@ -72,11 +75,12 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { // Templates templates := map[string]*string{ - "command": &p.config.Command, - "group_vars": &p.config.GroupVars, - "host_vars": &p.config.HostVars, - "playbook_file": &p.config.PlaybookFile, - "staging_dir": &p.config.StagingDir, + "command": &p.config.Command, + "group_vars": &p.config.GroupVars, + "host_vars": &p.config.HostVars, + "playbook_file": &p.config.PlaybookFile, + "staging_dir": &p.config.StagingDir, + "inventory_file": &p.config.InventoryFile, } for n, ptr := range templates { @@ -111,6 +115,14 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { errs = packer.MultiErrorAppend(errs, err) } + // Check that the inventory file exists, if configured + if len(p.config.InventoryFile) > 0 { + err = validateFileConfig(p.config.InventoryFile, "inventory_file", true) + if err != nil { + 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 { @@ -158,6 +170,15 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { return fmt.Errorf("Error uploading main playbook: %s", err) } + if len(p.config.InventoryFile) > 0 { + ui.Message("Uploading inventory file...") + src := p.config.InventoryFile + dst := filepath.Join(p.config.StagingDir, filepath.Base(src)) + if err := p.uploadFile(ui, comm, dst, src); err != nil { + return fmt.Errorf("Error uploading inventory file: %s", err) + } + } + if len(p.config.GroupVars) > 0 { ui.Message("Uploading group_vars directory...") src := p.config.GroupVars @@ -217,13 +238,18 @@ 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. + inventory := "\"127.0.0.1,\"" + if len(p.config.InventoryFile) > 0 { + inventory = filepath.Join(p.config.StagingDir, filepath.Base(p.config.InventoryFile)) + } + extraArgs := "" if len(p.config.ExtraArguments) > 0 { extraArgs = " " + strings.Join(p.config.ExtraArguments, " ") } - command := fmt.Sprintf("%s %s%s -c local -i \"127.0.0.1,\"", - p.config.Command, playbook, extraArgs) + command := fmt.Sprintf("%s %s%s -c local -i %s", + p.config.Command, playbook, extraArgs, inventory) ui.Message(fmt.Sprintf("Executing Ansible: %s", command)) cmd := &packer.RemoteCmd{ Command: command, diff --git a/provisioner/ansible-local/provisioner_test.go b/provisioner/ansible-local/provisioner_test.go index 2504cb4b1..45ee1c6e3 100644 --- a/provisioner/ansible-local/provisioner_test.go +++ b/provisioner/ansible-local/provisioner_test.go @@ -70,6 +70,46 @@ func TestProvisionerPrepare_PlaybookFile(t *testing.T) { } } +func TestProvisionerPrepare_InventoryFile(t *testing.T) { + var p Provisioner + config := testConfig() + + err := p.Prepare(config) + if err == nil { + t.Fatal("should have error") + } + + config["playbook_file"] = "" + err = p.Prepare(config) + if err == nil { + t.Fatal("should have error") + } + + playbook_file, err := ioutil.TempFile("", "playbook") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.Remove(playbook_file.Name()) + + config["playbook_file"] = playbook_file.Name() + err = p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } + + inventory_file, err := ioutil.TempFile("", "inventory") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.Remove(inventory_file.Name()) + + config["inventory_file"] = inventory_file.Name() + err = p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } +} + func TestProvisionerPrepare_Dirs(t *testing.T) { var p Provisioner config := testConfig() diff --git a/website/source/docs/provisioners/ansible-local.html.markdown b/website/source/docs/provisioners/ansible-local.html.markdown index bcc9a0962..40b883280 100644 --- a/website/source/docs/provisioners/ansible-local.html.markdown +++ b/website/source/docs/provisioners/ansible-local.html.markdown @@ -39,6 +39,35 @@ Optional: * `extra_arguments` (array of strings) - An array of extra arguments to pass to the ansible command. By default, this is empty. +* `inventory_file` (string) - The inventory file to be used by ansible. + This file must exist on your local system and will be uploaded to the + remote machine. + + When using an inventory file, it's also required to `--limit` the hosts to + the specified host you're buiding. The `--limit` argument can be provided in + the `extra_arguments` option. + + An example inventory file may look like: +
+  [chi-dbservers]
+  db-01 ansible_connection=local
+  db-02 ansible_connection=local
+
+  [chi-appservers]
+  app-01 ansible_connection=local
+  app-02 ansible_connection=local
+
+  [chi:children]
+  chi-dbservers
+  chi-appservers
+
+  [dbservers:children]
+  chi-dbservers
+
+  [appservers:children]
+  chi-appservers
+  
+ * `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.