From 19f01140354c341e35a26b0a023a458b45aacd3a Mon Sep 17 00:00:00 2001 From: Artis3n Date: Sat, 5 Sep 2020 16:10:02 -0400 Subject: [PATCH 1/7] Added support for Ansible Collections to the Galaxy install Fixes #8821 --- provisioner/ansible/provisioner.go | 41 +++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index a9bcdd344..84ed895c5 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -162,8 +162,8 @@ type Config struct { // test your playbook. this option is not used if you set an `inventory_file`. KeepInventoryFile bool `mapstructure:"keep_inventory_file"` // A requirements file which provides a way to - // install roles with the [ansible-galaxy - // cli](http://docs.ansible.com/ansible/galaxy.html#the-ansible-galaxy-command-line-tool) + // install roles or collections with the [ansible-galaxy + // cli](https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#the-ansible-galaxy-command-line-tool) // on the local machine before executing `ansible-playbook`. By default, this is empty. GalaxyFile string `mapstructure:"galaxy_file"` // The command to invoke ansible-galaxy. By default, this is @@ -173,11 +173,16 @@ type Config struct { // Adds `--force` option to `ansible-galaxy` command. By default, this is // `false`. GalaxyForceInstall bool `mapstructure:"galaxy_force_install"` - // The path to the directory on your local system to - // install the roles in. Adds `--roles-path /path/to/your/roles` to + // The path to the directory on your local system in which to + // install the roles. Adds `--roles-path /path/to/your/roles` to // `ansible-galaxy` command. By default, this is empty, and thus `--roles-path` // option is not added to the command. RolesPath string `mapstructure:"roles_path"` + // The path to the directory on your local system in which to + // install the collections. Adds `--collections-path /path/to/your/collections` to + // `ansible-galaxy` command. By default, this is empty, and thus `--collections-path` + // option is not added to the command. + CollectionsPath string `mapstructure:"collections_path"` // When `true`, set up a localhost proxy adapter // so that Ansible has an IP address to connect to, even if your guest does not // have an IP address. For example, the adapter is necessary for Docker builds @@ -624,16 +629,38 @@ func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) erro galaxyFile := filepath.ToSlash(p.config.GalaxyFile) // ansible-galaxy install -r requirements.yml - args := []string{"install", "-r", galaxyFile} + roleArgs := []string{"install", "-r", galaxyFile} + // Instead of modifying args depending on config values and removing or modifying values from + // the slice between role and collection installs, just use 2 slices and simplify everything + collectionArgs := []string{"collection", "install", "-r", galaxyFile} // Add force to arguments if p.config.GalaxyForceInstall { - args = append(args, "-f") + roleArgs = append(roleArgs, "-f") + collectionArgs = append(collectionArgs, "-f") } + // Add roles_path argument if specified if p.config.RolesPath != "" { - args = append(args, "-p", filepath.ToSlash(p.config.RolesPath)) + roleArgs = append(roleArgs, "-p", filepath.ToSlash(p.config.RolesPath)) + } + // Add collections_path argument if specified + if p.config.CollectionsPath != "" { + roleArgs = append(roleArgs, "-p", filepath.ToSlash(p.config.CollectionsPath)) } + roleInstallError := p.invokeGalaxyCommand(roleArgs, ui, comm) + // Return the error if the role installation failed before attempting the collection install + if roleInstallError != nil { + return roleInstallError + } + // If all is well, proceed with collections install + // This variable isn't strictly necessary but including for readability to match the role installation + collectionInstallError := p.invokeGalaxyCommand(roleArgs, ui, comm) + return collectionInstallError +} + +// Intended to be invoked from p.executeGalaxy depending on the Ansible Galaxy parameters passed to Packer +func (p *Provisioner) invokeGalaxyCommand(args []string, ui packer.Ui, comm packer.Communicator) error { ui.Message(fmt.Sprintf("Executing Ansible Galaxy")) cmd := exec.Command(p.config.GalaxyCommand, args...) From 3d2259a6d44bece359a39040ea5bc07752b5901c Mon Sep 17 00:00:00 2001 From: Artis3n Date: Sat, 5 Sep 2020 16:19:05 -0400 Subject: [PATCH 2/7] Typo fix in comment --- provisioner/ansible/provisioner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 84ed895c5..5879a1c40 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -653,7 +653,7 @@ func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) erro if roleInstallError != nil { return roleInstallError } - // If all is well, proceed with collections install + // If all is well, proceed with collection install // This variable isn't strictly necessary but including for readability to match the role installation collectionInstallError := p.invokeGalaxyCommand(roleArgs, ui, comm) return collectionInstallError From 41a8a96821008ec24dde77d70966fd51eace0f0f Mon Sep 17 00:00:00 2001 From: Artis3n Date: Sat, 5 Sep 2020 16:48:20 -0400 Subject: [PATCH 3/7] Added acceptance test for Galaxy Collection install --- .../provisioner-ansible/galaxy-playbook.yml | 33 +++++++++++++++++++ test/fixtures/provisioner-ansible/galaxy.json | 21 ++++++++++++ .../provisioner-ansible/requirements.yml | 2 ++ test/provisioner_ansible.bats | 8 +++++ 4 files changed, 64 insertions(+) create mode 100644 test/fixtures/provisioner-ansible/galaxy-playbook.yml create mode 100644 test/fixtures/provisioner-ansible/galaxy.json create mode 100644 test/fixtures/provisioner-ansible/requirements.yml diff --git a/test/fixtures/provisioner-ansible/galaxy-playbook.yml b/test/fixtures/provisioner-ansible/galaxy-playbook.yml new file mode 100644 index 000000000..b91a3dab4 --- /dev/null +++ b/test/fixtures/provisioner-ansible/galaxy-playbook.yml @@ -0,0 +1,33 @@ +--- +- hosts: default:packer-test + gather_facts: no + collections: + - artis3n.github + tasks: + - name: touch + raw: touch /tmp/ansible-raw-test + - name: raw test + raw: date + - name: command test + command: echo "the command module" + - name: prepare remote directory + command: mkdir /tmp/remote-dir + args: + creates: /tmp/remote-dir + - name: transfer file.txt + copy: src=dir/file.txt dest=/tmp/remote-dir/file.txt + - name: fetch file.text + fetch: src=/tmp/remote-dir/file.txt dest=fetched-dir validate=yes fail_on_missing=yes + - name: copy contents of directory + copy: src=dir/contents-only/ dest=/tmp/remote-dir + - name: fetch contents of directory + fetch: src=/tmp/remote-dir/file.txt dest="fetched-dir/{{ inventory_hostname }}/tmp/remote-dir/contents-only/" flat=yes validate=yes fail_on_missing=yes + - name: copy directory recursively + copy: src=dir/subdir dest=/tmp/remote-dir + - name: fetch recursively copied directory + fetch: src=/tmp/remote-dir/subdir/file.txt dest=fetched-dir validate=yes fail_on_missing=yes + - copy: src=largish-file.txt dest=/tmp/largish-file.txt + - name: test collection - fetch latest repo version + set_fact: + # Ansible will fail if collection is not installed + packer_version: "{{ lookup('artis3n.github.latest_release', 'hashicorp/packer' }}" diff --git a/test/fixtures/provisioner-ansible/galaxy.json b/test/fixtures/provisioner-ansible/galaxy.json new file mode 100644 index 000000000..e09a47cdc --- /dev/null +++ b/test/fixtures/provisioner-ansible/galaxy.json @@ -0,0 +1,21 @@ +{ + "variables": {}, + "provisioners": [ + { + "type": "ansible", + "playbook_file": "./playbook.yml", + "galaxy_file": "./requirements.yml" + } + ], + "builders": [ + { + "type": "googlecompute", + "account_file": "{{user `account_file`}}", + "project_id": "{{user `project_id`}}", + "image_name": "packerbats-galaxy-{{timestamp}}", + "source_image": "debian-8-jessie-v20161027", + "zone": "us-central1-a", + "ssh_username": "debian" + } + ] +} diff --git a/test/fixtures/provisioner-ansible/requirements.yml b/test/fixtures/provisioner-ansible/requirements.yml new file mode 100644 index 000000000..4658d9204 --- /dev/null +++ b/test/fixtures/provisioner-ansible/requirements.yml @@ -0,0 +1,2 @@ +collections: + - name: artis3n.github diff --git a/test/provisioner_ansible.bats b/test/provisioner_ansible.bats index c9dc9abd9..2cc45b522 100755 --- a/test/provisioner_ansible.bats +++ b/test/provisioner_ansible.bats @@ -65,6 +65,14 @@ teardown() { diff -r dir fetched-dir/packer-test/tmp/remote-dir > /dev/null } +@test "ansible provisioner: build galaxy.json" { + cd $FIXTURE_ROOT + run packer build ${USER_VARS} $FIXTURE_ROOT/galaxy.json + [ "$status" -eq 0 ] + [ "$(gc_has_image "packerbats-galaxy")" -eq 1 ] + diff -r dir fetched-dir/default/tmp/remote-dir > /dev/null +} + @test "ansible provisioner: build scp.json" { cd $FIXTURE_ROOT run packer build ${USER_VARS} $FIXTURE_ROOT/scp.json From 611899f7b2ee78ec17f32e8d578e0f05e5b092f3 Mon Sep 17 00:00:00 2001 From: Artis3n Date: Sat, 5 Sep 2020 16:51:38 -0400 Subject: [PATCH 4/7] Actually use the collectionArgs, that would be helpful --- provisioner/ansible/provisioner.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 5879a1c40..53cfd63ff 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -645,7 +645,7 @@ func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) erro } // Add collections_path argument if specified if p.config.CollectionsPath != "" { - roleArgs = append(roleArgs, "-p", filepath.ToSlash(p.config.CollectionsPath)) + collectionArgs = append(roleArgs, "-p", filepath.ToSlash(p.config.CollectionsPath)) } roleInstallError := p.invokeGalaxyCommand(roleArgs, ui, comm) @@ -655,7 +655,7 @@ func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) erro } // If all is well, proceed with collection install // This variable isn't strictly necessary but including for readability to match the role installation - collectionInstallError := p.invokeGalaxyCommand(roleArgs, ui, comm) + collectionInstallError := p.invokeGalaxyCommand(collectionArgs, ui, comm) return collectionInstallError } From 9a2908d1d961dc78d9f9d25db7d0fe9379ff213d Mon Sep 17 00:00:00 2001 From: Artis3n Date: Sat, 5 Sep 2020 18:15:42 -0400 Subject: [PATCH 5/7] Use the galaxy playbook in the galaxy packer file --- test/fixtures/provisioner-ansible/galaxy.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/provisioner-ansible/galaxy.json b/test/fixtures/provisioner-ansible/galaxy.json index e09a47cdc..879c925f6 100644 --- a/test/fixtures/provisioner-ansible/galaxy.json +++ b/test/fixtures/provisioner-ansible/galaxy.json @@ -3,7 +3,7 @@ "provisioners": [ { "type": "ansible", - "playbook_file": "./playbook.yml", + "playbook_file": "./galaxy-playbook.yml", "galaxy_file": "./requirements.yml" } ], From 39183d1b76e276e90bcab52e9a8ea198eab3f5b3 Mon Sep 17 00:00:00 2001 From: Artis3n Date: Sat, 5 Sep 2020 18:29:52 -0400 Subject: [PATCH 6/7] Updated documentation with `make generate` --- provisioner/ansible/provisioner.hcl2spec.go | 2 ++ .../provisioner/ansible/Config-not-required.mdx | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/provisioner/ansible/provisioner.hcl2spec.go b/provisioner/ansible/provisioner.hcl2spec.go index 91b7fce71..61217c677 100644 --- a/provisioner/ansible/provisioner.hcl2spec.go +++ b/provisioner/ansible/provisioner.hcl2spec.go @@ -39,6 +39,7 @@ type FlatConfig struct { GalaxyCommand *string `mapstructure:"galaxy_command" cty:"galaxy_command" hcl:"galaxy_command"` GalaxyForceInstall *bool `mapstructure:"galaxy_force_install" cty:"galaxy_force_install" hcl:"galaxy_force_install"` RolesPath *string `mapstructure:"roles_path" cty:"roles_path" hcl:"roles_path"` + CollectionsPath *string `mapstructure:"collections_path" cty:"collections_path" hcl:"collections_path"` UseProxy *bool `mapstructure:"use_proxy" cty:"use_proxy" hcl:"use_proxy"` } @@ -84,6 +85,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "galaxy_command": &hcldec.AttrSpec{Name: "galaxy_command", Type: cty.String, Required: false}, "galaxy_force_install": &hcldec.AttrSpec{Name: "galaxy_force_install", Type: cty.Bool, Required: false}, "roles_path": &hcldec.AttrSpec{Name: "roles_path", Type: cty.String, Required: false}, + "collections_path": &hcldec.AttrSpec{Name: "collections_path", Type: cty.String, Required: false}, "use_proxy": &hcldec.AttrSpec{Name: "use_proxy", Type: cty.Bool, Required: false}, } return s diff --git a/website/pages/partials/provisioner/ansible/Config-not-required.mdx b/website/pages/partials/provisioner/ansible/Config-not-required.mdx index 751649e99..99d0df0f5 100644 --- a/website/pages/partials/provisioner/ansible/Config-not-required.mdx +++ b/website/pages/partials/provisioner/ansible/Config-not-required.mdx @@ -120,8 +120,8 @@ test your playbook. this option is not used if you set an `inventory_file`. - `galaxy_file` (string) - A requirements file which provides a way to - install roles with the [ansible-galaxy - cli](http://docs.ansible.com/ansible/galaxy.html#the-ansible-galaxy-command-line-tool) + install roles or collections with the [ansible-galaxy + cli](https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#the-ansible-galaxy-command-line-tool) on the local machine before executing `ansible-playbook`. By default, this is empty. - `galaxy_command` (string) - The command to invoke ansible-galaxy. By default, this is @@ -131,11 +131,16 @@ Adds `--force` option to `ansible-galaxy` command. By default, this is `false`. -- `roles_path` (string) - The path to the directory on your local system to - install the roles in. Adds `--roles-path /path/to/your/roles` to +- `roles_path` (string) - The path to the directory on your local system in which to + install the roles. Adds `--roles-path /path/to/your/roles` to `ansible-galaxy` command. By default, this is empty, and thus `--roles-path` option is not added to the command. +- `collections_path` (string) - The path to the directory on your local system in which to + install the collections. Adds `--collections-path /path/to/your/collections` to + `ansible-galaxy` command. By default, this is empty, and thus `--collections-path` + option is not added to the command. + - `use_proxy` (boolean) - When `true`, set up a localhost proxy adapter so that Ansible has an IP address to connect to, even if your guest does not have an IP address. For example, the adapter is necessary for Docker builds From 4ecade39086294979e195b52679fbcb5009c3eae Mon Sep 17 00:00:00 2001 From: Artis3n Date: Sat, 5 Sep 2020 18:42:40 -0400 Subject: [PATCH 7/7] collectionsPath appends to the correct slice Fixes what would have been a failure if roles+collections were tested at once --- provisioner/ansible/provisioner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 53cfd63ff..c6d0d6f1c 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -645,7 +645,7 @@ func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) erro } // Add collections_path argument if specified if p.config.CollectionsPath != "" { - collectionArgs = append(roleArgs, "-p", filepath.ToSlash(p.config.CollectionsPath)) + collectionArgs = append(collectionArgs, "-p", filepath.ToSlash(p.config.CollectionsPath)) } roleInstallError := p.invokeGalaxyCommand(roleArgs, ui, comm)