From d247329599497f500efea2a0f6edad03ede697b9 Mon Sep 17 00:00:00 2001 From: Ruben Tsirunyan Date: Fri, 26 Jul 2019 20:44:40 +0400 Subject: [PATCH 1/3] Adding support for installing roles with ansible-galaxy for ansible provisioner --- provisioner/ansible/provisioner.go | 83 ++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 6aa0b07e2..9640e1b05 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -61,6 +61,10 @@ type Config struct { UseSFTP bool `mapstructure:"use_sftp"` InventoryDirectory string `mapstructure:"inventory_directory"` InventoryFile string `mapstructure:"inventory_file"` + GalaxyFile string `mapstructure:"galaxy_file"` + GalaxyCommand string `mapstructure:"galaxy_command"` + GalaxyForceInstall bool `mapstructure:"galaxy_force_install"` + RolesDir string `mapstructure:"roles_dir"` } type Provisioner struct { @@ -100,6 +104,14 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { p.config.Command = "ansible-playbook" } + if p.config.GalaxyCommand == "" { + p.config.GalaxyCommand = "ansible-galaxy" + } + + if p.config.RolesDir == "" { + p.config.RolesDir = "~/.ansible/roles" + } + if p.config.HostAlias == "" { p.config.HostAlias = "default" } @@ -110,6 +122,14 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { errs = packer.MultiErrorAppend(errs, err) } + // Check that the galaxy file exists, if configured + if len(p.config.GalaxyFile) > 0 { + err = validateFileConfig(p.config.GalaxyFile, "galaxy_file", true) + if err != nil { + errs = packer.MultiErrorAppend(errs, err) + } + } + // Check that the authorized key file exists if len(p.config.SSHAuthorizedKeyFile) > 0 { err = validateFileConfig(p.config.SSHAuthorizedKeyFile, "ssh_authorized_key_file", true) @@ -343,12 +363,75 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C return nil } +func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) error { + rolesDir := filepath.ToSlash(p.config.RolesDir) + galaxyFile := filepath.ToSlash(p.config.GalaxyFile) + + // ansible-galaxy install -r requirements.yml -p roles/ + args := []string{"install", "-r", galaxyFile, "-p", rolesDir} + // Add force to arguments + if p.config.GalaxyForceInstall { + args = append(args, "-f") + } + + ui.Message(fmt.Sprintf("Executing Ansible Galaxy")) + cmd := exec.Command(p.config.GalaxyCommand, args...) + + stdout, err := cmd.StdoutPipe() + if err != nil { + return err + } + stderr, err := cmd.StderrPipe() + if err != nil { + return err + } + wg := sync.WaitGroup{} + repeat := func(r io.ReadCloser) { + reader := bufio.NewReader(r) + for { + line, err := reader.ReadString('\n') + if line != "" { + line = strings.TrimRightFunc(line, unicode.IsSpace) + ui.Message(line) + } + if err != nil { + if err == io.EOF { + break + } else { + ui.Error(err.Error()) + break + } + } + } + wg.Done() + } + wg.Add(2) + go repeat(stdout) + go repeat(stderr) + + if err := cmd.Start(); err != nil { + return err + } + wg.Wait() + err = cmd.Wait() + if err != nil { + return fmt.Errorf("Non-zero exit status: %s", err) + } + return nil +} + func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator, privKeyFile string) error { playbook, _ := filepath.Abs(p.config.PlaybookFile) inventory := p.config.InventoryFile var envvars []string + // Fetch external dependencies + if len(p.config.GalaxyFile) > 0 { + if err := p.executeGalaxy(ui, comm); err != nil { + return fmt.Errorf("Error executing Ansible Galaxy: %s", err) + } + } args := []string{"--extra-vars", fmt.Sprintf("packer_build_name=%s packer_builder_type=%s -o IdentitiesOnly=yes", p.config.PackerBuildName, p.config.PackerBuilderType), "-i", inventory, playbook} From 1b35c586ce557103af35dbf887662ed886ac7830 Mon Sep 17 00:00:00 2001 From: Ruben Tsirunyan Date: Sat, 27 Jul 2019 15:56:14 +0400 Subject: [PATCH 2/3] Changes to roles_path option --- provisioner/ansible/provisioner.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 9640e1b05..d61d4d44e 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -64,7 +64,7 @@ type Config struct { GalaxyFile string `mapstructure:"galaxy_file"` GalaxyCommand string `mapstructure:"galaxy_command"` GalaxyForceInstall bool `mapstructure:"galaxy_force_install"` - RolesDir string `mapstructure:"roles_dir"` + RolesPath string `mapstructure:"roles_path"` } type Provisioner struct { @@ -108,10 +108,6 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { p.config.GalaxyCommand = "ansible-galaxy" } - if p.config.RolesDir == "" { - p.config.RolesDir = "~/.ansible/roles" - } - if p.config.HostAlias == "" { p.config.HostAlias = "default" } @@ -364,15 +360,18 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C } func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) error { - rolesDir := filepath.ToSlash(p.config.RolesDir) galaxyFile := filepath.ToSlash(p.config.GalaxyFile) - // ansible-galaxy install -r requirements.yml -p roles/ - args := []string{"install", "-r", galaxyFile, "-p", rolesDir} + // ansible-galaxy install -r requirements.yml + args := []string{"install", "-r", galaxyFile} // Add force to arguments if p.config.GalaxyForceInstall { args = append(args, "-f") } + // Add roles_path argument if specified + if p.config.RolesPath != "" { + args = append(args, "-p", filepath.ToSlash(p.config.RolesPath)) + } ui.Message(fmt.Sprintf("Executing Ansible Galaxy")) cmd := exec.Command(p.config.GalaxyCommand, args...) From f9d79addace4126152459fac461dafe64f2d25f6 Mon Sep 17 00:00:00 2001 From: Ruben Tsirunyan Date: Sat, 27 Jul 2019 16:26:20 +0400 Subject: [PATCH 3/3] Adding documentation for ansible-galaxy related arguments --- .../source/docs/provisioners/ansible.html.md.erb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/website/source/docs/provisioners/ansible.html.md.erb b/website/source/docs/provisioners/ansible.html.md.erb index 0e98a7601..e212080f1 100644 --- a/website/source/docs/provisioners/ansible.html.md.erb +++ b/website/source/docs/provisioners/ansible.html.md.erb @@ -96,6 +96,17 @@ Optional Parameters: ] ``` +- `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) + 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 `ansible-galaxy`. + +- `galaxy_force_install` (string) - Force overwriting an existing role. + Adds `--force` option to `ansible-galaxy` command. By default, this is empty. + - `groups` (array of strings) - The groups into which the Ansible host should be placed. When unspecified, the host is not associated with any groups. @@ -121,6 +132,11 @@ Optional Parameters: `local_port`. A system-chosen port is used when `local_port` is missing or empty. +- `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 + `ansible-galaxy` command. By default, this is empty, and thus `--roles-path` + option is not added to the command. + - `sftp_command` (string) - The command to run on the machine being provisioned by Packer to handle the SFTP protocol that Ansible will use to transfer files. The command should read and write on stdin and stdout,