diff --git a/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb b/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb index fc8feeaad..0c77506ab 100644 --- a/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb +++ b/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb @@ -8,13 +8,13 @@ module VagrantPlugins module AnsibleInstall - def self.ansible_install(machine, install_mode, ansible_version, pip_args) + def self.ansible_install(machine, install_mode, ansible_version, pip_args, pip_install_cmd="") case install_mode when :pip - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "ansible", ansible_version, pip_args, true when :pip_args_only - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "", "", pip_args, false else ansible_apt_install machine @@ -36,10 +36,10 @@ INLINE_CRIPT machine.communicate.sudo "apt-get install -y -qq ansible" end - def self.pip_setup(machine) + def self.pip_setup(machine, pip_install_cmd="") machine.communicate.sudo "apt-get update -y -qq" machine.communicate.sudo "apt-get install -y -qq build-essential curl git libssl-dev libffi-dev python-dev" - Pip::get_pip machine + Pip::get_pip machine, pip_install_cmd end end diff --git a/plugins/provisioners/ansible/cap/guest/fedora/ansible_install.rb b/plugins/provisioners/ansible/cap/guest/fedora/ansible_install.rb index fe651f372..560a4dcc0 100644 --- a/plugins/provisioners/ansible/cap/guest/fedora/ansible_install.rb +++ b/plugins/provisioners/ansible/cap/guest/fedora/ansible_install.rb @@ -8,13 +8,13 @@ module VagrantPlugins module Fedora module AnsibleInstall - def self.ansible_install(machine, install_mode, ansible_version, pip_args) + def self.ansible_install(machine, install_mode, ansible_version, pip_args, pip_install_cmd="") case install_mode when :pip - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "ansible", ansible_version, pip_args, true when :pip_args_only - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "", "", pip_args, false else rpm_package_manager = Facts::rpm_package_manager(machine) @@ -25,11 +25,11 @@ module VagrantPlugins private - def self.pip_setup(machine) + def self.pip_setup(machine, pip_install_cmd="") rpm_package_manager = Facts::rpm_package_manager(machine) machine.communicate.sudo "#{rpm_package_manager} install -y curl gcc gmp-devel libffi-devel openssl-devel python-crypto python-devel python-dnf python-setuptools redhat-rpm-config" - Pip::get_pip machine + Pip::get_pip machine, pip_install_cmd end end diff --git a/plugins/provisioners/ansible/cap/guest/pip/pip.rb b/plugins/provisioners/ansible/cap/guest/pip/pip.rb index 370aabd32..88cb5b630 100644 --- a/plugins/provisioners/ansible/cap/guest/pip/pip.rb +++ b/plugins/provisioners/ansible/cap/guest/pip/pip.rb @@ -5,6 +5,8 @@ module VagrantPlugins module Guest module Pip + DEFAULT_PIP_INSTALL_CMD = "curl https://bootstrap.pypa.io/get-pip.py | sudo python".freeze + def self.pip_install(machine, package = "", version = "", pip_args = "", upgrade = true) upgrade_arg = "--upgrade" if upgrade version_arg = "" @@ -18,9 +20,20 @@ module VagrantPlugins machine.communicate.sudo "pip install #{args_array.join(' ')}" end - def self.get_pip(machine) + def self.get_pip(machine, pip_install_cmd=DEFAULT_PIP_INSTALL_CMD) + + # The objective here is to get pip either by default + # or by the argument passed in. The objective is not + # to circumvent the pip setup by passing in nothing. + # Thus, we stick with the default on an empty string. + # Typecast added in the check for safety. + + if pip_install_cmd.to_s.empty? + pip_install_cmd=DEFAULT_PIP_INSTALL_CMD + end + machine.ui.detail I18n.t("vagrant.provisioners.ansible.installing_pip") - machine.communicate.execute "curl https://bootstrap.pypa.io/get-pip.py | sudo python" + machine.communicate.execute pip_install_cmd end end diff --git a/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb b/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb index 3119cab90..05e858ddd 100644 --- a/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb +++ b/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb @@ -8,13 +8,13 @@ module VagrantPlugins module RedHat module AnsibleInstall - def self.ansible_install(machine, install_mode, ansible_version, pip_args) + def self.ansible_install(machine, install_mode, ansible_version, pip_args, pip_install_cmd="") case install_mode when :pip - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "ansible", ansible_version, pip_args, true when :pip_args_only - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "", "", pip_args, false else ansible_rpm_install machine @@ -33,11 +33,11 @@ module VagrantPlugins machine.communicate.sudo "#{rpm_package_manager} -y --enablerepo=epel install ansible" end - def self.pip_setup(machine) + def self.pip_setup(machine, pip_install_cmd="") rpm_package_manager = Facts::rpm_package_manager(machine) machine.communicate.sudo("#{rpm_package_manager} -y install curl gcc libffi-devel openssl-devel python-crypto python-devel python-setuptools") - Pip::get_pip machine + Pip::get_pip machine, pip_install_cmd 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 d319ac071..b08176997 100644 --- a/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb +++ b/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb @@ -7,9 +7,9 @@ module VagrantPlugins module Ubuntu module AnsibleInstall - def self.ansible_install(machine, install_mode, ansible_version, pip_args) + def self.ansible_install(machine, install_mode, ansible_version, pip_args, pip_install_cmd="") if install_mode != :default - Debian::AnsibleInstall::ansible_install machine, install_mode, ansible_version, pip_args + Debian::AnsibleInstall::ansible_install machine, install_mode, ansible_version, pip_args, pip_install_cmd else ansible_apt_install machine end diff --git a/plugins/provisioners/ansible/config/guest.rb b/plugins/provisioners/ansible/config/guest.rb index a869779ae..13d801ff8 100644 --- a/plugins/provisioners/ansible/config/guest.rb +++ b/plugins/provisioners/ansible/config/guest.rb @@ -11,6 +11,7 @@ module VagrantPlugins attr_accessor :install attr_accessor :install_mode attr_accessor :pip_args + attr_accessor :pip_install_cmd def initialize super @@ -18,6 +19,7 @@ module VagrantPlugins @install = UNSET_VALUE @install_mode = UNSET_VALUE @pip_args = UNSET_VALUE + @pip_install_cmd = UNSET_VALUE @provisioning_path = UNSET_VALUE @tmp_path = UNSET_VALUE end @@ -28,6 +30,7 @@ module VagrantPlugins @install = true if @install == UNSET_VALUE @install_mode = :default if @install_mode == UNSET_VALUE @pip_args = "" if @pip_args == UNSET_VALUE + @pip_install_cmd = "" if @pip_args == UNSET_VALUE @provisioning_path = "/vagrant" if provisioning_path == UNSET_VALUE @tmp_path = "/tmp/vagrant-ansible" if tmp_path == UNSET_VALUE end diff --git a/plugins/provisioners/ansible/provisioner/guest.rb b/plugins/provisioners/ansible/provisioner/guest.rb index ca4953b72..de101e051 100644 --- a/plugins/provisioners/ansible/provisioner/guest.rb +++ b/plugins/provisioners/ansible/provisioner/guest.rb @@ -32,7 +32,10 @@ module VagrantPlugins # # Current limitations: # - The installation of a specific Ansible version is only supported by - # the "pip" install_mode. + # the "pip" install_mode. Note that "pip" installation also takes place + # via a default command. If pip needs to be installed differently then + # the command can be overwritten by supplying "pip_install_cmd" in the + # config settings. # - There is no absolute guarantee that the automated installation will replace # a previous Ansible installation (although it works fine in many cases) # @@ -51,7 +54,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, config.install_mode, config.version, config.pip_args) + @machine.guest.capability(:ansible_install, config.install_mode, config.version, config.pip_args, config.pip_install_cmd) end # This step will also fetch the Ansible version data into related instance variables diff --git a/test/unit/plugins/provisioners/ansible/cap/guest/pip/pip_test.rb b/test/unit/plugins/provisioners/ansible/cap/guest/pip/pip_test.rb new file mode 100644 index 000000000..a4403b7df --- /dev/null +++ b/test/unit/plugins/provisioners/ansible/cap/guest/pip/pip_test.rb @@ -0,0 +1,52 @@ +require_relative "../../../../../../base" + +require Vagrant.source_root.join("plugins/provisioners/ansible/cap/guest/pip/pip") + +describe VagrantPlugins::Ansible::Cap::Guest::Pip do + include_context "unit" + + subject { VagrantPlugins::Ansible::Cap::Guest::Pip } + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + env = isolated_environment + env.vagrantfile("") + env.create_vagrant_env + end + + let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } + let(:communicator) { double("comm") } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + allow(communicator).to receive(:execute).and_return(true) + end + + describe "#get_pip" do + describe 'when no pip_install_command argument is provided' do + it "installs pip using the default command" do + expect(communicator).to receive(:execute).with("curl https://bootstrap.pypa.io/get-pip.py | sudo python") + subject.get_pip(machine) + end + end + + describe 'when pip_install_command argument is provided' do + it "runs the supplied argument instead of default" do + pip_install_command = "foo" + expect(communicator).to receive(:execute).with(pip_install_command) + subject.get_pip(machine,pip_install_command) + end + + it "installs pip using the default command if the argument is empty" do + pip_install_command = "" + expect(communicator).to receive(:execute).with("curl https://bootstrap.pypa.io/get-pip.py | sudo python") + subject.get_pip(machine,pip_install_command) + end + + it "installs pip using the default command if the argument is nil" do + expect(communicator).to receive(:execute).with("curl https://bootstrap.pypa.io/get-pip.py | sudo python") + subject.get_pip(machine, nil) + end + end + end +end \ No newline at end of file diff --git a/test/unit/plugins/provisioners/ansible/config/guest_test.rb b/test/unit/plugins/provisioners/ansible/config/guest_test.rb index 19010ba94..d3abc431c 100644 --- a/test/unit/plugins/provisioners/ansible/config/guest_test.rb +++ b/test/unit/plugins/provisioners/ansible/config/guest_test.rb @@ -32,6 +32,7 @@ describe VagrantPlugins::Ansible::Config::Guest do inventory_path limit pip_args + pip_install_cmd playbook playbook_command provisioning_path diff --git a/website/source/docs/provisioning/ansible_local.html.md b/website/source/docs/provisioning/ansible_local.html.md index 140f1473f..38fcb9331 100644 --- a/website/source/docs/provisioning/ansible_local.html.md +++ b/website/source/docs/provisioning/ansible_local.html.md @@ -93,6 +93,31 @@ This section lists the _specific_ options for the Ansible Local provisioner. In sudo pip install --upgrade ansible==2.2.1.0 ``` + As-is `pip` is installed if needed via a default command which looks like + + ```shell + curl https://bootstrap.pypa.io/get-pip.py | sudo python + ``` + + This can be problematic in certain scenarios, for example, when behind a proxy. It is possible to override this default command by providing an explicit command to run as part of the config using `pip_install_cmd`. For example: + + ```ruby + config.vm.provision "ansible_local" do |ansible| + ansible.playbook = "playbook.yml" + ansible.install_mode = "pip" + ansible.pip_install_cmd = "https_proxy=http://your.proxy.server:port curl -s https://bootstrap.pypa.io/get-pip.py | sudo https_proxy=http://your.proxy.server:port python" + ansible.version = "2.2.1.0" + end + ``` + + In this case case `pip` will be installed via the command: + + ```shell + https_proxy=http://your.proxy.server:port curl -s https://bootstrap.pypa.io/get-pip.py | sudo https_proxy=http://your.proxy.server:port python + ``` + + If `pip_install_cmd` is not provided in the config, then `pip` is installed via the default command. + - `:pip_args_only`: This mode is very similar to the `:pip` mode, with the difference that in this case no pip arguments will be automatically set by Vagrant. Example: