lib/kitchen/driver/lxd_cli.rb in kitchen-lxd_cli-0.1.0.beta vs lib/kitchen/driver/lxd_cli.rb in kitchen-lxd_cli-0.1.0

- old
+ new

@@ -23,88 +23,92 @@ module Driver # LxdCli driver for Kitchen. # # @author Braden Wright <braden.m.wright@gmail.com> - class LxdCli < Kitchen::Driver::SSHBase + class LxdCli < Kitchen::Driver::Base + kitchen_driver_api_version 2 + default_config :public_key_path do [ File.expand_path('~/.ssh/id_rsa.pub'), File.expand_path('~/.ssh/id_dsa.pub'), File.expand_path('~/.ssh/identity.pub'), File.expand_path('~/.ssh/id_ecdsa.pub') ].find { |path| File.exist?(path) } end + default_config :stop_instead_of_destroy, false + + required_config :public_key_path def create(state) - if exists? - if running? - debug("#{instance.name} already exists, and is already running. Nothing to do") - else - debug("#{instance.name} already exists, starting instead") - run_command("lxc start #{instance.name}") - end - else - create_image_if_missing - run_command("lxc launch #{instance.platform.name} #{instance.name}") + unless exists? + image_name = create_image_if_missing + profile = "-p #{config[:profile]}" if config[:profile] + lxc_config = "-c #{config[:config]}" if config[:config] + info("Initializing container #{instance.name}") + run_lxc_command("init #{image_name} #{instance.name} #{profile} #{lxc_config}") end - ip_address(state) + + config_and_start_container unless running? + configure_dns + lxc_ip = wait_for_ip_address + state[:hostname] = lxc_ip setup_ssh_access end def destroy(state) if exists? if running? - run_command("lxc stop #{instance.name}") - else - debug("#{instance.name} isn't running, just destroying instead") + info("Stopping container #{instance.name}") + run_lxc_command("stop #{instance.name}") end - run_command("lxc delete #{instance.name}") - else - debug("#{instance.name} doesn't exist. Nothing to do") + info("Deleting container #{instance.name}") + run_lxc_command("delete #{instance.name}") unless config[:stop_instead_of_destroy] end state.delete(:hostname) end private def exists? - status = `lxc info #{instance.name} > /dev/null 2>&1 && echo $?`.chomp - if "#{status}" == "0" - debug("#{instance.name} exists") + `lxc info #{instance.name} > /dev/null 2>&1` + if $?.to_i == 0 + debug("Container #{instance.name} exists") return true else - debug("#{instance.name} doesn't exist") + debug("Container #{instance.name} doesn't exist") return false end end def running? status = `lxc info #{instance.name}`.match(/Status: ([a-zA-Z]+)[\n]/).captures[0].upcase if status == "RUNNING" - debug("#{instance.name} is running") + debug("Container #{instance.name} is running") return true else - debug("#{instance.name} isn't running") + debug("Container #{instance.name} isn't running") return false end end def create_image_if_missing - status = `lxc image show #{instance.name} > /dev/null 2>&1 && echo $?`.chomp - if "#{status}" == "0" - debug("Image #{instance.name} exists") - return false + image_name = config[:image_name] ||= instance.platform.name + `lxc image show #{image_name} > /dev/null 2>&1` + if $?.to_i == 0 + debug("Image #{image_name} exists") else - debug("Image #{instance.name} doesn't exist, creating now.") - image = get_ubuntu_image_info - debug("lxd-images import #{image[:os]} #{image[:release]} --alias #{instance.platform.name}") - run_command("lxd-images import #{image[:os]} #{image[:release]} --alias #{instance.platform.name}") - return true + info("Image #{image_name} doesn't exist, creating now. May take a few minutes.") + image = get_image_info + image_os = config[:image_os] ||= image[:os] + image_release = config[:image_release] ||= image[:release] + run_local_command("lxd-images import #{image_os} #{image_release} --alias #{image_name}") end + return image_name end - def get_ubuntu_image_info + def get_image_info platform, release = instance.platform.name.split('-') if platform.downcase == "ubuntu" case release.downcase when "14.04", "1404", "trusty", "", nil image = { :os => platform, :release => "trusty" } @@ -117,28 +121,166 @@ when "16.04", "1604", "xenial" image = { :os => platform, :release => "xenial" } else image = { :os => platform, :release => release } end - return image + else + image = { :os => platform, :release => release } end + return image end - def ip_address(state) + def config_and_start_container + config[:ip_gateway] ||= "auto" + arg_disable_dhcp = "" + if config[:ipv4] + IO.popen("bash", "r+") do |p| + p.puts("echo -e \"lxc.network.type = veth\nlxc.network.name = eth0\nlxc.network.link = lxcbr0\nlxc.network.ipv4 = #{config[:ipv4]}\nlxc.network.ipv4.gateway = #{config[:ip_gateway]}\nlxc.network.flags = up\" | lxc config set #{instance.name} raw.lxc -") + p.puts("exit") + end + arg_disable_dhcp = "&& lxc exec #{instance.name} -- sed -i 's/dhcp/manual/g' /etc/network/interfaces.d/eth0.cfg" + end + # TODO: loop over/run all lxc config settings passed in or figure out how to use multiple with lxc init + info("Starting container #{instance.name}") + run_lxc_command("start #{instance.name} #{arg_disable_dhcp}") + end + + def configure_dns + IO.popen("lxc exec #{instance.name} bash", "r+") do |p| + dns_servers = "" + config[:dns_servers].each do |dns_server| + dns_servers += "nameserver #{dns_server}\n" + end if config[:dns_servers] + + case config[:ip_gateway] + when "auto", "" + dns_servers = "nameserver 8.8.8.8\nnameserver 8.8.4.4" + dns_servers = "nameserver 8.8.8.8\nnameserver 8.8.4.4" + else + dns_servers = "nameserver #{config[:ip_gateway]}\nnameserver 8.8.8.8\nnameserver 8.8.4.4" + end if config[:ipv4] && dns_servers.length == 0 + + if dns_servers.length > 0 + wait_for_path("/etc/resolvconf/resolv.conf.d/base") + debug("Setting up the following dns servers via /etc/resolvconf/resolv.conf.d/base:") + debug(dns_servers.gsub("\n", ' ')) + p.puts(" echo \"#{dns_servers.chomp}\" > /etc/resolvconf/resolv.conf.d/base") + p.puts("resolvconf -u") + end + + debug("Setting up /etc/hosts") + if config[:domain_name] +# p.puts("echo -e \" dns-search #{config[:domain_name]}\" >> /etc/network/interfaces.d/eth0.cfg") + args_host = "#{instance.name}.#{config[:domain_name]} #{instance.name}" + end + args_host ||= "#{instance.name}" + wait_for_path("/etc/hosts") + p.puts("if grep -iq '127.0.1.1' /etc/hosts; then") + p.puts("sed -i 's/^127.0.1.1.*$/127.0.1.1\t#{args_host}/' /etc/hosts") + p.puts("else echo '#***** Setup by Kitchen-LxdCli driver *****#' >> /etc/hosts") + p.puts("echo -e '127.0.1.1\t#{args_host}' >> /etc/hosts; fi") + p.puts("exit") + end + end +=begin + def configure_ip_via_lxc_restart + debug("Configuring new ip address on eth0") + + IO.popen("lxc exec #{instance.name} bash", "r+") do |p| + p.puts('echo -e "#############################################" > /etc/network/interfaces.d/eth0.cfg') + p.puts('echo -e "# DO NOT EDIT CONTROLLED BY KITCHEN-LXC_CLI #" >> /etc/network/interfaces.d/eth0.cfg') + p.puts('echo -e "#############################################" >> /etc/network/interfaces.d/eth0.cfg') + p.puts('echo -e "auto eth0" >> /etc/network/interfaces.d/eth0.cfg') + if config[:ipv4] + config[:ip_gateway] ||= "10.0.3.1" + config[:dns_servers] ||= [ "8.8.8.8", "8.8.4.4" ] + p.puts('echo -e " iface eth0 inet static" >> /etc/network/interfaces.d/eth0.cfg') + p.puts("echo -e \" address #{config[:ipv4]}\" >> /etc/network/interfaces.d/eth0.cfg") + else + p.puts('echo -e " iface eth0 inet dhcp" >> /etc/network/interfaces.d/eth0.cfg') + end + p.puts("echo -e \" gateway #{config[:ip_gateway]}\" >> /etc/network/interfaces.d/eth0.cfg") if config[:ip_gateway] + config[:dns_servers].each do |dns_server| + p.puts("echo -e \" dns-nameserver #{dns_server}\" >> /etc/network/interfaces.d/eth0.cfg") + end if config[:dns_servers] + if config[:domain_name] + p.puts("echo -e \" dns-search #{config[:domain_name]}\" >> /etc/network/interfaces.d/eth0.cfg") + end + p.puts("exit") + end + debug("Finished configuring new ip address, restarting #{instance.name} for settings to take effect") + debug_note_about_configuring_ip + wait_for_ip_address + sleep 3 # Was hanging more often than not whenever I lowered the sleep + debug("Restarting #{instance.name}") + run_lxc_command("restart #{instance.name}") + debug("Finished restarting #{instance.name} ip address should be configured") + end +=end + + def setup_ssh_access + info("Setting up public key #{config[:public_key_path]} on #{instance.name}") + wait_for_path("/root/.ssh") + begin - lxc_info = `lxc info #{instance.name}` - end while (!lxc_info.match(/eth0:[\t]IPV[46][\t]([0-9.]+)[\n]/)) - lxc_ip = lxc_info.match(/eth0:[\t]IPV[46][\t]([0-9.]+)[\n]/).captures[0].to_s - state[:hostname] = lxc_ip + debug("Uploading public key...") + `lxc file push #{config[:public_key_path]} #{instance.name}/root/.ssh/authorized_keys 2> /dev/null` + break if $?.to_i == 0 + sleep 0.3 + end while true + + debug("Finished Copying public key from #{config[:public_key_path]} to #{instance.name}") + end + + def wait_for_ip_address + info("Waiting for network to become ready") + begin + lxc_info = `lxc info #{instance.name}`.match(/^[ ]+eth0:[\t]IPV4[\t]([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*$/) + debug("Still waiting for IP Address...") + lxc_ip = lxc_info.captures[0].to_s if lxc_info && lxc_info.captures + break if lxc_ip && lxc_ip.length > 7 + sleep 0.3 + end while true + debug("Found Ip Address #{lxc_ip}") return lxc_ip end + def wait_for_path(path) + begin + debug("Waiting for #{path} to become available...") + run_lxc_command("exec #{instance.name} -- ls #{path}") + break if $?.to_i == 0 + sleep 0.3 + end while true + debug("Found #{path}") + end + def setup_ssh_access - info("Copying public key from #{config[:public_key_path]} to #{instance.name}") + info("Setting up public key #{config[:public_key_path]} on #{instance.name}") + wait_for_path("/root/.ssh") + begin - sleep 1 - status = `lxc file push #{config[:public_key_path]} #{instance.name}/root/.ssh/authorized_keys 2> /dev/null && echo $?`.chomp - end while ("#{status}" != "0") + debug("Uploading public key...") + `lxc file push #{config[:public_key_path]} #{instance.name}/root/.ssh/authorized_keys 2> /dev/null` + break if $?.to_i == 0 + sleep 0.3 + end while true + + debug("Finished Copying public key from #{config[:public_key_path]} to #{instance.name}") + end + + def run_lxc_command(cmd) + run_local_command("lxc #{cmd}") if cmd + end + + def run_local_command(cmd) + debug("run_local_command ran: #{cmd}") + `#{cmd}` if cmd + debug("Command finished: #{$?.to_s}") + end + + def debug_note_about_configuring_ip + debug("NOTE: Restarting seemed to be the only way I could get things to work. Tried lxc profiles, config options. Tried restart networking service but it didn't work, also tried passing command like ifconfig 10.0.3.x/24 eth0 up. Which set the ip but after container ran for a while, it would reset to dhcp address that had been assigned. Restarting container seems to be working, and is really fast. Open to better alternatives.") end end end end