Search code examples
ansiblevagrantvagrant-pluginrhel8

How to extend a vagrant plugin through Vagrantfile without changing the .rb file?


I have this scenario in Vagrant where I wanted to patch how the ansible is being installed on RHEL 8 (because of certain issues with absence of packages missing due to repository setup) using ansible_local plugin. So here's the thing. Instead, I wanted to use pip3 (though I know I can use pip using ansible_local module but still it errs after that due to absence of certain repository so I figure out a way to fix it).

In my Vagrantfile, I have these lines as such,

.....
      node.vm.provision "ansible_local" do |ansible|
        ansible.playbook = ansible_playbook
        ansible.verbose = true
        ansible.install = true
        ## Actually this line doesn't suffice my problem since it still errs as pip requires other packages. Please check the *.rb file below
        if i == 100
            ansible.install_mode = "pip"
            ansible.version = "2.9"
            #ansible.ansible_rpm_install = Foo
        end
      end
...
.....

But because using pip still fails, so I end up changing the file /opt/vagrant/embedded/gems/2.2.9/gems/vagrant-2.2.9/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb and what I did is this way,


cap/guest/redhat/ansible_install.rb
require_relative "../facts"
require_relative "../pip/pip"

module VagrantPlugins
  module Ansible
    module Cap
      module Guest
        module RedHat
          module AnsibleInstall

            def self.ansible_install(machine, install_mode, ansible_version, pip_args, pip_install_cmd = "")
              case install_mode
              when :pip
                pip_setup machine, pip_install_cmd
                Pip::pip_install machine, "ansible", ansible_version, pip_args, true
              when :pip_args_only
                pip_setup machine, pip_install_cmd
                Pip::pip_install machine, "", "", pip_args, false
              else
                
                // My added part as a quick fix/solution
                if machine.config.vm.box == "generic/rhel8"
                   ansible_rpm_install_rhel8 machine
                elsif
                   ansible_rpm_install machine
                end
                
              end
            end

            private

            def self.ansible_rpm_install(machine)
              rpm_package_manager = Facts::rpm_package_manager(machine)

              epel = machine.communicate.execute "#{rpm_package_manager} 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 "#{rpm_package_manager} -y --enablerepo=epel install ansible"
            end

            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_install_cmd
            end

            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_install_cmd
            end

            
            // My added part as a quick fix/solution
            def self.ansible_rpm_install_rhel8(machine)
              rpm_package_manager = Facts::rpm_package_manager(machine)

              epel = machine.communicate.execute "#{rpm_package_manager} 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 "dnf -y update; dnf -y install python3 python3-pip; pip3 install ansible"
            end
            
            
          end
        end
      end
    end
  end
end

So I added a method self.ansible_rpm_install_rhel8(machine) and did an if..elsif as a solution when box name is "generic/rhel8". This works perfect actually on my end. However, I don't like this approach i.e. changing the file /opt/vagrant/embedded/gems/2.2.9/gems/vagrant-2.2.9/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb because once it changes or version upgrades, this will be gone. Is there any better way that I can just do this only inside Vagrantfile such that perhaps overriding or extending the class itself? I have no idea how to do this.

Any ideas are welcome.

Thank you!


Solution

  • You can use multiple provisioning sections next one another which will be executed in the order as you defined them in your Vagrantfile.

    Instead of patching a plugin, try to prepare your ansible execution accordingly.

    Before executing the provisioning part for ansible, execute a shell provisioning as follows:

    Vagrant.configure("2") do |node|
    
        # ...
    
        node.vm.provision :shell, path: "fix_repo_and_add_packages.sh"
    
        node.vm.provision "ansible_local" do |ansible|
            ansible.playbook = ansible_playbook
            ansible.verbose = true
            ansible.install = true
            ## Actually this line doesn't suffice my problem since it still errs as pip requires other packages. Please check the *.rb file below
            if i == 100
                ansible.install_mode = "pip"
                ansible.version = "2.9"
                #ansible.ansible_rpm_install = Foo
            end
        end
    
    end
    

    Your fix_repo_and_add_packages.sh contains the setup of the missing repos and here you can add packages as well.

    For more information about shell provisioning, you can find in the doc