From bb9dba56aca7e4bd3546ad1fa3256bfc8955c47e Mon Sep 17 00:00:00 2001 From: Gilles Cornu Date: Wed, 8 Jun 2016 22:59:47 +0200 Subject: [PATCH] provisioners/ansible_local: add "pip" install_mode These changes have been validated against the following guest systems: - Debian 7 and 8 - Ubuntu 12.04, 14.04 and 16.04 - Fedora 21 and 23 - CentOS 7 - OracleLinux 7 - Scientific Linux 7 At the moment, the pip setup (via get-pip.py script) is not working for RHEL6-like systems (CentOS 6.6, OracleLinux 6.5, Scientific Linux 6), because Python 2.6 has been deprecated and is no longer supported by Python core team. I consider this limitation with low priority in Vagrant context. The `:pip` install_mode is currently not implemented for the following platforms: - OpenSUSE - ArchLinux - FreeBSD Known Issue: By using get-pip.py script, any previous pip installation will be most probably overrided. This could be an issue for Python developers who would prefer to keep their base box setup untouched. In future iteration, it could be possible to choose to reinstall/upgrade pip or not. issue for Python developers who would prefer to keep their base box setup untouched. In future iteration, it could be possible to choose to reinstall/upgrade pip or not. Resolve GH-6654 Resolve GH-7167 as the `version` option is now considered to select the version of Ansible to be installed. --- CHANGELOG.md | 2 ++ .../ansible/cap/guest/arch/ansible_install.rb | 11 +++++-- .../cap/guest/debian/ansible_install.rb | 24 ++++++++++++++- .../provisioners/ansible/cap/guest/facts.rb | 24 +++++++++++++++ .../cap/guest/fedora/ansible_install.rb | 18 +++++++---- .../cap/guest/freebsd/ansible_install.rb | 9 ++++-- .../provisioners/ansible/cap/guest/pip/pip.rb | 28 +++++++++++++++++ .../cap/guest/redhat/ansible_install.rb | 30 ++++++++++++++----- .../ansible/cap/guest/suse/ansible_install.rb | 8 +++-- .../cap/guest/ubuntu/ansible_install.rb | 23 ++++++++++---- plugins/provisioners/ansible/config/guest.rb | 9 ++++++ plugins/provisioners/ansible/errors.rb | 4 +++ .../provisioners/ansible/provisioner/guest.rb | 2 +- templates/locales/en.yml | 10 +++++++ .../provisioners/ansible/config/guest_test.rb | 18 +++++++++++ .../docs/provisioning/ansible_local.html.md | 10 +++++++ 16 files changed, 201 insertions(+), 29 deletions(-) create mode 100644 plugins/provisioners/ansible/cap/guest/facts.rb create mode 100644 plugins/provisioners/ansible/cap/guest/pip/pip.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index bb2a1b199..dad1b4a4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,8 @@ IMPROVEMENTS: - commands/login: Print a warning with both the environment variable and local login token are present [GH-7206, GH-7219] - communicators/winrm: Upgrade to latest WinRM gems [GH-6922] + - provisioners/ansible_local: Allow to install Ansible from pip, + with version selection capability [GH-6654, GH-7167] - provisioners/ansible_local: Use `provisioning_path` as working directory for `ansible-galaxy` execution - provisioners/ansible(both provisioners): Add basic config diff --git a/plugins/provisioners/ansible/cap/guest/arch/ansible_install.rb b/plugins/provisioners/ansible/cap/guest/arch/ansible_install.rb index 62e1e2141..062bea01c 100644 --- a/plugins/provisioners/ansible/cap/guest/arch/ansible_install.rb +++ b/plugins/provisioners/ansible/cap/guest/arch/ansible_install.rb @@ -1,3 +1,4 @@ +require_relative "../../../errors" module VagrantPlugins module Ansible @@ -6,9 +7,13 @@ module VagrantPlugins module Arch module AnsibleInstall - def self.ansible_install(machine) - machine.communicate.sudo("pacman -Syy --noconfirm") - machine.communicate.sudo("pacman -S --noconfirm ansible") + def self.ansible_install(machine, install_mode, ansible_version) + if install_mode == :pip + raise Ansible::Errors::AnsiblePipInstallIsNotSupported + else + machine.communicate.sudo("pacman -Syy --noconfirm") + machine.communicate.sudo("pacman -S --noconfirm ansible") + end end end diff --git a/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb b/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb index 05308d03d..3ce214bf3 100644 --- a/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb +++ b/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb @@ -1,3 +1,4 @@ +require_relative "../pip/pip" module VagrantPlugins module Ansible @@ -6,8 +7,23 @@ module VagrantPlugins module Debian module AnsibleInstall - def self.ansible_install(machine) + def self.ansible_install(machine, install_mode, ansible_version) + if (install_mode == :pip) + ansible_pip_install machine, ansible_version + else + ansible_apt_install machine + end + end + + def self.ansible_pip_install(machine, ansible_version) + pip_setup machine + Pip::pip_install machine, "ansible", ansible_version + end + + private + + def self.ansible_apt_install(machine) install_backports_if_wheezy_release = < false) - if epel != 0 - machine.communicate.sudo('sudo rpm -i https://dl.fedoraproject.org/pub/epel/epel-release-latest-`rpm -E %dist | sed -n \'s/.*el\([0-9]\).*/\1/p\'`.noarch.rpm') + def self.ansible_install(machine, install_mode, ansible_version) + if install_mode == :pip + pip_setup machine + Pip::pip_install machine, "ansible", ansible_version + else + ansible_rpm_install machine end - - machine.communicate.sudo("#{yum_dnf(machine)} -y --enablerepo=epel install ansible") end - def self.yum_dnf(machine) - machine.communicate.test("/usr/bin/which -s dnf") ? "dnf" : "yum" + private + + def self.ansible_rpm_install(machine) + epel = machine.communicate.execute "#{yum_dnf(machine)} repolist epel | grep -q epel", error_check: false + if epel != 0 + machine.communicate.sudo 'sudo rpm -i https://dl.fedoraproject.org/pub/epel/epel-release-latest-`rpm -E %dist | sed -n \'s/.*el\([0-9]\).*/\1/p\'`.noarch.rpm' + end + + machine.communicate.sudo "#{Facts::rpm_package_manager} -y --enablerepo=epel install ansible" + end + + def self.pip_setup(machine) + machine.communicate.sudo("#{Facts::rpm_package_manager(machine)} install -y curl gcc libffi-devel openssl-devel python-crypto python-devel python-setuptools") + Pip::get_pip machine end end diff --git a/plugins/provisioners/ansible/cap/guest/suse/ansible_install.rb b/plugins/provisioners/ansible/cap/guest/suse/ansible_install.rb index 64f3cd41c..08e7a96f4 100644 --- a/plugins/provisioners/ansible/cap/guest/suse/ansible_install.rb +++ b/plugins/provisioners/ansible/cap/guest/suse/ansible_install.rb @@ -6,8 +6,12 @@ module VagrantPlugins module SUSE module AnsibleInstall - def self.ansible_install(machine) - machine.communicate.sudo("zypper --non-interactive --quiet install ansible") + def self.ansible_install(machine, install_mode, ansible_version) + if install_mode == :pip + raise Ansible::Errors::AnsiblePipInstallIsNotSupported + else + machine.communicate.sudo("zypper --non-interactive --quiet install ansible") + end end end diff --git a/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb b/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb index 92182fe06..bd9f22295 100644 --- a/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb +++ b/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb @@ -1,3 +1,4 @@ +require_relative "../debian/ansible_install" module VagrantPlugins module Ansible @@ -6,12 +7,22 @@ module VagrantPlugins module Ubuntu module AnsibleInstall - def self.ansible_install(machine) - machine.communicate.sudo("apt-get update -y -qq") - machine.communicate.sudo("apt-get install -y -qq software-properties-common python-software-properties") - machine.communicate.sudo("add-apt-repository ppa:ansible/ansible -y") - machine.communicate.sudo("apt-get update -y -qq") - machine.communicate.sudo("apt-get install -y -qq ansible") + def self.ansible_install(machine, install_mode, ansible_version) + if install_mode == :pip + Debian::AnsibleInstall::ansible_pip_install machine, ansible_version + else + ansible_apt_install machine + end + end + + private + + def self.ansible_apt_install(machine) + machine.communicate.sudo "apt-get update -y -qq" + machine.communicate.sudo "apt-get install -y -qq software-properties-common python-software-properties" + machine.communicate.sudo "add-apt-repository ppa:ansible/ansible -y" + machine.communicate.sudo "apt-get update -y -qq" + machine.communicate.sudo "apt-get install -y -qq ansible" end end diff --git a/plugins/provisioners/ansible/config/guest.rb b/plugins/provisioners/ansible/config/guest.rb index 5f0086c4a..c0cbb92d7 100644 --- a/plugins/provisioners/ansible/config/guest.rb +++ b/plugins/provisioners/ansible/config/guest.rb @@ -9,12 +9,14 @@ module VagrantPlugins attr_accessor :provisioning_path attr_accessor :tmp_path attr_accessor :install + attr_accessor :install_mode attr_accessor :version def initialize super @install = UNSET_VALUE + @install_mode = UNSET_VALUE @provisioning_path = UNSET_VALUE @tmp_path = UNSET_VALUE @version = UNSET_VALUE @@ -24,6 +26,7 @@ module VagrantPlugins super @install = true if @install == UNSET_VALUE + @install_mode = :default if @install_mode == UNSET_VALUE @provisioning_path = "/vagrant" if provisioning_path == UNSET_VALUE @tmp_path = "/tmp/vagrant-ansible" if tmp_path == UNSET_VALUE @version = "" if @version == UNSET_VALUE @@ -32,6 +35,12 @@ module VagrantPlugins def validate(machine) super + if @install_mode.to_s.to_sym == :pip + @install_mode = :pip + else + @install_mode = :default + end + { "ansible local provisioner" => @errors } end diff --git a/plugins/provisioners/ansible/errors.rb b/plugins/provisioners/ansible/errors.rb index 06a1063cf..be9e55fb9 100644 --- a/plugins/provisioners/ansible/errors.rb +++ b/plugins/provisioners/ansible/errors.rb @@ -19,6 +19,10 @@ module VagrantPlugins error_key(:ansible_not_found_on_guest) end + class AnsiblePipInstallIsNotSupported < AnsibleError + error_key(:cannot_support_pip_install) + end + class AnsibleVersionNotFoundOnGuest < AnsibleError error_key(:ansible_version_not_found_on_guest) end diff --git a/plugins/provisioners/ansible/provisioner/guest.rb b/plugins/provisioners/ansible/provisioner/guest.rb index 298f9cef3..147e212ce 100644 --- a/plugins/provisioners/ansible/provisioner/guest.rb +++ b/plugins/provisioners/ansible/provisioner/guest.rb @@ -49,7 +49,7 @@ module VagrantPlugins (config.version.to_s.to_sym == :latest || !@machine.guest.capability(:ansible_installed, config.version)) @machine.ui.detail I18n.t("vagrant.provisioners.ansible.installing") - @machine.guest.capability(:ansible_install) + @machine.guest.capability(:ansible_install, config.install_mode, config.version) end # Check that ansible binaries are well installed on the guest, diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 1514c7a1f..112ef37d8 100755 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -2122,6 +2122,15 @@ en: on your host system. Vagrant can't do this for you in a safe and automated way. Please check https://docs.ansible.com for more information. + cannot_support_pip_install: |- + Unfortunately Vagrant does not support yet installing Ansible + from pip for the guest OS running in the machine. + + If you'd like this provisioner to be improved, please + take a look at the Vagrant source code linked below and try + to contribute back support. Thank you! + + https://github.com/mitchellh/vagrant ansible_version_not_found_on_guest: |- The requested Ansible version (%{required_version}) was not found on the guest. Please check the ansible installation on your guest system, @@ -2139,6 +2148,7 @@ en: raw_ssh_args_invalid: |- `raw_ssh_args` must be an array of strings. Received: %{value} (as %{type}) installing: "Installing Ansible..." + installing_pip: "Installing pip... (for Ansible installation)" running_galaxy: "Running ansible-galaxy..." running_playbook: "Running ansible-playbook..." windows_not_supported_for_control_machine: |- diff --git a/test/unit/plugins/provisioners/ansible/config/guest_test.rb b/test/unit/plugins/provisioners/ansible/config/guest_test.rb index 4368c7032..c1c389686 100644 --- a/test/unit/plugins/provisioners/ansible/config/guest_test.rb +++ b/test/unit/plugins/provisioners/ansible/config/guest_test.rb @@ -23,6 +23,7 @@ describe VagrantPlugins::Ansible::Config::Guest do groups host_vars install + install_mode inventory_path limit playbook @@ -48,6 +49,7 @@ describe VagrantPlugins::Ansible::Config::Guest do subject.finalize! expect(subject.install).to be_true + expect(subject.install_mode).to eql(:default) expect(subject.provisioning_path).to eql("/vagrant") expect(subject.tmp_path).to eql("/tmp/vagrant-ansible") expect(subject.version).to be_empty @@ -60,6 +62,22 @@ describe VagrantPlugins::Ansible::Config::Guest do end it_behaves_like "an Ansible provisioner", "/vagrant", "local" + + it "falls back to :default install_mode for any invalid setting" do + subject.install_mode = "from_source" + subject.finalize! + + result = subject.validate(machine) + expect(subject.install_mode).to eql(:default) + end + + it "supports :pip install_mode" do + subject.install_mode = "pip" + subject.finalize! + + result = subject.validate(machine) + expect(subject.install_mode).to eql(:pip) + end end end diff --git a/website/source/docs/provisioning/ansible_local.html.md b/website/source/docs/provisioning/ansible_local.html.md index ba93cc881..a630107bb 100644 --- a/website/source/docs/provisioning/ansible_local.html.md +++ b/website/source/docs/provisioning/ansible_local.html.md @@ -71,6 +71,16 @@ This section lists the specific options for the Ansible Local provisioner. In ad **Attention:** There is no guarantee that this automated installation will replace a custom Ansible setup, that might be already present on the Vagrant box. +- `install_mode` (`:default` or `:pip`) - Select the way to automatically install Ansible on the guest system. + + - `:default`: Ansible is installed from the operating system package manager. This mode doesn't support `version` selection. For many platforms (e.g Debian, FreeBSD, OpenSUSE) the official package repository is used, except for the following Linux distributions: + - On Ubuntu-like systems, the latest Ansible release is installed from the `ppa:ansible/ansible` repository. + - On RedHat-like systems, the latest Ansible release is installed from the [EPEL](http://fedoraproject.org/wiki/EPEL) repository. + + - `:pip`: Ansible is installed from [PyPI](https://pypi.python.org/pypi) with [pip](https://pip.pypa.io) package installer. With this mode, Vagrant will systematically try to [install the latest pip version](https://pip.pypa.io/en/stable/installing/#installing-with-get-pip-py). The `:pip` mode can install a specific version of Ansible if such information is specified with the `version` option described below. + + The default value is `:default`, and any invalid value for this option will silently fall back to the default value. + - `provisioning_path` (string) - An absolute path on the guest machine where the Ansible files are stored. The `ansible-galaxy` and `ansible-playbook` commands are executed from this directory. This is the location to place an [ansible.cfg](http://docs.ansible.com/ansible/intro_configuration.html) file, in case you need it. The default value is `/vagrant`.