# frozen_string_literal: true require 'log4r' require 'fileutils' require 'digest/md5' require 'io/console' require 'ruby_expect' require 'netaddr' require 'ipaddr' require 'vagrant/util/numeric' require 'pty' require 'expect' require 'vagrant' require 'resolv' require 'open3' require 'vagrant-local/util/timer' require 'vagrant-local/util/subprocess' require 'vagrant/util/retryable' module VagrantPlugins module ProviderLocal # This class does the heavy lifting of the local provider class Driver include Vagrant::Util::Retryable attr_accessor :executor def initialize(machine) @logger = Log4r::Logger.new('vagrant_local::driver') @machine = machine @executor = Executor::Exec.new @pfexec = if Process.uid.zero? '' elsif system('sudo -v') 'sudo' else 'pfexec' end end def state data = [{ 'name' => nil, 'ec2_id' => nil, 'state' => 'not_created', 'public_ip' => nil }] vmstate_file = "#{@machine.name}.vmstate" File.write(vmstate_file, data.to_yaml) unless File.exist?(vmstate_file) vm_data = YAML.load_file(vmstate_file) return :not_created unless vm_data.is_a?(Array) && !vm_data.empty? && vm_data.first.is_a?(Hash) vm_state = vm_data.first['state'] case vm_state when 'running' :running when 'installed', 'stopped' :stopped else :not_created end end # Execute System commands def execute(...) @executor.execute(...) end ## Begin installation def install(_uii) config = @machine.config.vm.provisioners[1].config.extra_vars.to_json command = "#{@pfexec} ansible-playbook ./providers/ansible/playbook.yml --tags 'install' -e '#{config}'" _stdin, stdout, _stderr, wait_thr = Open3.popen3(command) puts stdout.readline until stdout.eof? wait_thr.value end ## Run commands over SSH instead of ZLogin def ssh_run_command(uii, command) config = @machine.provider_config ip = get_ip_address user = user(@machine) key = userprivatekeypath(@machine).to_s port = sshport(@machine).to_s port = 22 if sshport(@machine).to_s.nil? execute_return = '' Util::Timer.time do retryable(on: Errors::TimeoutError, tries: 60) do # If we're interrupted don't worry about waiting ssh_string = "#{@pfexec} ssh -o 'StrictHostKeyChecking=no' -p" execute_return = execute(false, %(#{ssh_string} #{port} -i #{key} #{user}@#{ip} "#{command}")) uii.info(I18n.t('vagrant_local.ssh_run_command')) if config.debug uii.info(I18n.t('vagrant_local.ssh_run_command') + command) if config.debug loop do break if @machine.communicate.ready? end end end execute_return end ## Boot the Machine def boot(uii) config = @machine.config.vm.provisioners[1].config.extra_vars.to_json command = "#{@pfexec} ansible-playbook ./providers/ansible/playbook.yml --tags 'boot' -e '#{config}'" uii.info(I18n.t('vagrant_local.start')) _stdin, stdout, _stderr, wait_thr = Open3.popen3(command) puts stdout.readline until stdout.eof? wait_thr.value end # This ensures the host is ready for provisioning def check_support(uii) uii.info(I18n.t('vagrant_local.preflight_checks')) uii.info(I18n.t('vagrant_local.ansible_playbook_check')) playbook_result = execute(false, "#{@pfexec} which ansible-playbook") raise Errors::AnsiblePlaybookNotDetected if playbook_result.empty? uii.info(I18n.t('vagrant_local.ansible_check')) result = execute(false, "#{@pfexec} which ansible") raise Errors::AnsibleNotDetected if result.empty? end def get_ip_address vm_data = YAML.load_file("#{@machine.name}.vmstate") return nil unless vm_data.is_a?(Array) && !vm_data.empty? && vm_data.first.is_a?(Hash) vm_data.first['public_ip'] end # This filters the vagrantuser def user(machine) config = machine.provider_config user = config.vagrant_user unless config.vagrant_user.nil? user = 'vagrant' if config.vagrant_user.nil? user end # This filters the userprivatekeypath def userprivatekeypath(machine) config = machine.provider_config userkey = config.vagrant_user_private_key_path.to_s if config.vagrant_user_private_key_path.to_s.nil? id_rsa = 'https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant' file = './id_rsa' command = "#{@pfexec} curl #{id_rsa} -O #{file}" Util::Subprocess.new command do |_stdout, stderr, _thread| uii.rewriting do |uipkp| uipkp.clear_line uipkp.info(I18n.t('vagrant_local.importing_vagrant_key'), new_line: false) uipkp.report_progress(stderr, 100, false) end end uii.clear_line userkey = './id_rsa' end userkey end # This filters the sshport def sshport(machine) config = machine.provider_config sshport = '22' sshport = config.sshport.to_s unless config.sshport.to_s.nil? || config.sshport.to_i.zero? # uii.info(I18n.t('vagrant_local.sshport')) if config.debug sshport end # This filters the vagrantuserpass def vagrantuserpass(machine) config = machine.provider_config # uii.info(I18n.t('vagrant_local.vagrantuserpass')) if config.debug config.vagrant_user_pass unless config.vagrant_user_pass.to_s.nil? end # Halts the instance, first via shutdown command, then a halt. def halt(uii) provider_config = @machine.provider_config config = @machine.config.vm.provisioners[1].config.extra_vars.to_json uii.info(I18n.t('vagrant_local.graceful_shutdown')) begin Timeout.timeout(provider_config.clean_shutdown_time) do command = "#{@pfexec} ansible-playbook ./providers/ansible/playbook.yml --tags 'halt' -e '#{config}' " _stdin, stdout, _stderr, wait_thr = Open3.popen3(command) puts stdout.readline until stdout.eof? wait_thr.value end rescue Timeout::Error uii.info(I18n.t('vagrant_local.graceful_shutdown_failed') + provider_config.clean_shutdown_time.to_s) begin Timeout.timeout(provider_config.clean_shutdown_time) do command = "#{@pfexec} ansible-playbook ./providers/ansible/playbook.yml --tags 'halt' -e '#{config}' " _stdin, stdout, _stderr, wait_thr = Open3.popen3(command) puts stdout.readline until stdout.eof? wait_thr.value end rescue Timeout::Error raise Errors::TimeoutHalt end end end # Destroys the instance def destroy(id) # byebug config = @machine.config.vm.provisioners[1].config.extra_vars.to_json if state == :stopped id.info(I18n.t('vagrant_local.destroy')) command = "#{@pfexec} ansible-playbook ./providers/ansible/playbook.yml --tags 'destroy' -e '#{config}' " _stdin, stdout, _stderr, wait_thr = Open3.popen3(command) puts stdout.readline until stdout.eof? wait_thr.value end id.info(I18n.t('vagrant_local.destroyed')) end end end end