From c1456137bd97c361daa4aba3c9658bdf04175611 Mon Sep 17 00:00:00 2001 From: Fletcher Nichol Date: Tue, 10 Mar 2015 21:40:20 -0600 Subject: [PATCH 1/4] Support WinRM transport and Windows-based instances, yay! --- lib/kitchen/driver/vagrant.rb | 67 +++++++++++++-- spec/kitchen/driver/vagrant_spec.rb | 129 ++++++++++++++++++++++------ 2 files changed, 164 insertions(+), 32 deletions(-) diff --git a/lib/kitchen/driver/vagrant.rb b/lib/kitchen/driver/vagrant.rb index 38969ed9..4560b225 100644 --- a/lib/kitchen/driver/vagrant.rb +++ b/lib/kitchen/driver/vagrant.rb @@ -29,7 +29,9 @@ module Driver # Vagrant driver for Kitchen. It communicates to Vagrant via the CLI. # # @author Fletcher Nichol - class Vagrant < Kitchen::Driver::SSHBase + class Vagrant < Kitchen::Driver::Base + + include ShellOut default_config(:box) { |driver| driver.default_box } required_config :box @@ -46,6 +48,10 @@ class Vagrant < Kitchen::Driver::SSHBase default_config :network, [] + default_config :password do |driver| + "vagrant" if driver.winrm_transport? + end + default_config :pre_create_command, nil default_config :provision, false @@ -58,11 +64,16 @@ class Vagrant < Kitchen::Driver::SSHBase default_config :synced_folders, [] + default_config :username do |driver| + "vagrant" if driver.winrm_transport? + end + default_config :vagrantfile_erb, File.join(File.dirname(__FILE__), "../../../templates/Vagrantfile.erb") expand_path_for :vagrantfile_erb default_config :vagrantfiles, [] + expand_path_for :vagrantfiles default_config(:vm_hostname) { |driver| driver.instance.name } @@ -77,6 +88,7 @@ def create(state) run_pre_create_command run_vagrant_up update_state(state) + instance.transport.connection(state).wait_until_ready info("Vagrant instance #{instance.to_str} created.") end @@ -128,9 +140,7 @@ def destroy(state) # @raise [ClientError] if instance parameter is nil def finalize_config!(instance) super - config[:vagrantfiles] = config[:vagrantfiles].map do |path| - File.expand_path(path, config[:kitchen_root]) - end + finalize_vm_hostname! finalize_pre_create_command! finalize_synced_folders! self @@ -152,6 +162,13 @@ def verify_dependencies end end + # @return [TrueClass,FalseClass] whether or not the transport's name + # implies a WinRM-based transport + # @api private + def winrm_transport? + instance.transport.name.downcase =~ /win_?rm/ + end + protected WEBSITE = "http://www.vagrantup.com/downloads.html".freeze @@ -223,6 +240,18 @@ def finalize_synced_folders! end end + # Truncates the length of `:vm_hostname` to 12 characters for + # Windows-based operating systems. + # + # @api private + def finalize_vm_hostname! + string = config[:vm_hostname] + + if windows_os? && string.is_a?(String) && string.size >= 12 + config[:vm_hostname] = "#{string[0...10]}-#{string[-1]}" + end + end + # Renders the Vagrantfile ERb template. # # @return [String] the contents for a Vagrantfile @@ -250,6 +279,22 @@ def run(cmd, options = {}) run_command(cmd, { :cwd => vagrant_root }.merge(options)) end + # Delegates to Kitchen::ShellOut.run_command, overriding some default + # options: + # + # * `:use_sudo` defaults to the value of `config[:use_sudo]` in the + # Driver object + # * `:log_subject` defaults to a String representation of the Driver's + # class name + # + # @see Kitchen::ShellOut#run_command + def run_command(cmd, options = {}) + merged = { + :use_sudo => config[:use_sudo], :log_subject => name + }.merge(options) + super(cmd, merged) + end + # Runs a local command before `vagrant up` has been called. # # @api private @@ -288,10 +333,16 @@ def update_state(state) hash = vagrant_ssh_config state[:hostname] = hash["HostName"] - state[:username] = hash["User"] - state[:ssh_key] = hash["IdentityFile"] - state[:port] = hash["Port"] - state[:proxy_command] = hash["ProxyCommand"] if hash["ProxyCommand"] + + if winrm_transport? + state[:username] = config[:username] + state[:password] = config[:password] + else + state[:username] = hash["User"] + state[:ssh_key] = hash["IdentityFile"] + state[:port] = hash["Port"] + state[:proxy_command] = hash["ProxyCommand"] if hash["ProxyCommand"] + end end # @return [String] full local path to the directory containing the diff --git a/spec/kitchen/driver/vagrant_spec.rb b/spec/kitchen/driver/vagrant_spec.rb index fe109d2c..a8308149 100644 --- a/spec/kitchen/driver/vagrant_spec.rb +++ b/spec/kitchen/driver/vagrant_spec.rb @@ -23,6 +23,7 @@ require "kitchen/driver/vagrant" require "kitchen/provisioner/dummy" +require "kitchen/transport/dummy" describe Kitchen::Driver::Vagrant do @@ -33,6 +34,7 @@ let(:suite) { Kitchen::Suite.new(:name => "suitey") } let(:busser) { double("busser") } let(:provisioner) { Kitchen::Provisioner::Dummy.new } + let(:transport) { Kitchen::Transport::Dummy.new } let(:state_file) { double("state_file") } let(:state) { Hash.new } let(:env) { Hash.new } @@ -53,6 +55,7 @@ :suite => suite, :platform => platform, :provisioner => provisioner, + :transport => transport, :state_file => state_file ) end @@ -295,14 +298,60 @@ ) end - it "sets :vm_hostname to the instance name by default" do - expect(driver[:vm_hostname]).to eq("suitey-fooos-99") + context "for unix os_types" do + + before { allow(platform).to receive(:os_type).and_return("unix") } + + it "sets :vm_hostname to the instance name by default" do + expect(driver[:vm_hostname]).to eq("suitey-fooos-99") + end + + it "sets :vm_hostname to a custom value" do + config[:vm_hostname] = "okay" + + expect(driver[:vm_hostname]).to eq("okay") + end + end + + context "for windows os_types" do + + before { allow(platform).to receive(:os_type).and_return("windows") } + + it "sets :vm_hostname to a truncated 12-char instance name by default" do + expect(driver[:vm_hostname]).to eq("suitey-foo-9") + end + + it "sets :vm_hostname to a custom value, truncated to 12 chars" do + config[:vm_hostname] = "this-is-a-pretty-long-name-ya-think" + + expect(driver[:vm_hostname]).to eq("this-is-a--k") + end end - it "sets :vm_hostname to a custom value" do - config[:vm_hostname] = "okay" + context "for non-WinRM-based transports" do + + before { allow(transport).to receive(:name).and_return("Coolness") } + + it "sets :username to nil by default" do + expect(driver[:username]).to eq(nil) + end - expect(driver[:vm_hostname]).to eq("okay") + it "sets :password to nil by default" do + expect(driver[:password]).to eq(nil) + end + end + + context "for WinRM-based transports" do + + before { allow(transport).to receive(:name).and_return("WinRM") } + + it "sets :username to vagrant by default" do + expect(driver[:username]).to eq("vagrant") + end + + it "sets :password to vagrant by default" do + expect(driver[:password]).to eq("vagrant") + end end end @@ -369,6 +418,14 @@ expect(File.exist?(File.join(vagrant_root, "Vagrantfile"))).to eq(true) end + it "calls Transport's #wait_until_ready" do + conn = double("connection") + allow(transport).to receive(:connection).with(state).and_return(conn) + expect(conn).to receive(:wait_until_ready) + + cmd + end + it "logs the Vagrantfile contents on debug level" do cmd @@ -448,35 +505,59 @@ expect(state).to include(:hostname => "192.168.32.64") end - it "sets :username from ssh-config" do - cmd + context "for non-WinRM-based transports" do - expect(state).to include(:username => "vagrant") - end + before { allow(transport).to receive(:name).and_return("Coolness") } - it "sets :ssh_key from ssh-config" do - cmd + it "sets :username from ssh-config" do + cmd - expect(state).to include(:ssh_key => "/path/to/private_key") - end + expect(state).to include(:username => "vagrant") + end - it "sets :port from ssh-config" do - cmd + it "sets :ssh_key from ssh-config" do + cmd - expect(state).to include(:port => "2022") - end + expect(state).to include(:ssh_key => "/path/to/private_key") + end - it "does not set :proxy_command by default" do - cmd + it "sets :port from ssh-config" do + cmd + + expect(state).to include(:port => "2022") + end + + it "does not set :proxy_command by default" do + cmd + + expect(state.keys).to_not include(:proxy_command) + end - expect(state.keys).to_not include(:proxy_command) + it "sets :proxy_command if ProxyCommand is in ssh-config" do + output.concat(" ProxyCommand echo proxy\n") + cmd + + expect(state).to include(:proxy_command => "echo proxy") + end end - it "sets :proxy_command if ProxyCommand is in ssh-config" do - output.concat(" ProxyCommand echo proxy\n") - cmd + context "for WinRM-based transports" do - expect(state).to include(:proxy_command => "echo proxy") + before { allow(transport).to receive(:name).and_return("WinRM") } + + it "sets :username from config" do + config[:username] = "winuser" + cmd + + expect(state).to include(:username => "winuser") + end + + it "sets :password from config" do + config[:password] = "mysecret" + cmd + + expect(state).to include(:password => "mysecret") + end end end From bd1ecc034b21f46e721871f5cf093a11e2317789 Mon Sep 17 00:00:00 2001 From: Fletcher Nichol Date: Wed, 11 Mar 2015 12:36:01 -0600 Subject: [PATCH 2/4] Default :vm_hostname to nil for Windows-based platforms. --- README.md | 7 ++++--- lib/kitchen/driver/vagrant.rb | 4 +++- spec/kitchen/driver/vagrant_spec.rb | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 52167280..a79d4840 100644 --- a/README.md +++ b/README.md @@ -396,9 +396,10 @@ section of the Vagrant documentation. To prevent this value from being rendered in the default Vagrantfile, you can set this value to `false`. -The default will be computed from the name of the instance. For -example, the instance was called "default-fuzz-9" will produce a default -`vm_hostname` value of `"default-fuzz-9.vagrantup.com"`. +The default will be computed from the name of the instance. For example, the +instance was called "default-fuzz-9" will produce a default `vm_hostname` value +of `"default-fuzz-9"`. For Windows-based platforms, a default of `nil` is used +to save on boot time and potential rebooting. ## Development diff --git a/lib/kitchen/driver/vagrant.rb b/lib/kitchen/driver/vagrant.rb index 4560b225..aa4fc727 100644 --- a/lib/kitchen/driver/vagrant.rb +++ b/lib/kitchen/driver/vagrant.rb @@ -75,7 +75,9 @@ class Vagrant < Kitchen::Driver::Base default_config :vagrantfiles, [] expand_path_for :vagrantfiles - default_config(:vm_hostname) { |driver| driver.instance.name } + default_config(:vm_hostname) do |driver| + driver.windows_os? ? nil : driver.instance.name + end no_parallel_for :create, :destroy diff --git a/spec/kitchen/driver/vagrant_spec.rb b/spec/kitchen/driver/vagrant_spec.rb index a8308149..d54cbf46 100644 --- a/spec/kitchen/driver/vagrant_spec.rb +++ b/spec/kitchen/driver/vagrant_spec.rb @@ -317,8 +317,8 @@ before { allow(platform).to receive(:os_type).and_return("windows") } - it "sets :vm_hostname to a truncated 12-char instance name by default" do - expect(driver[:vm_hostname]).to eq("suitey-foo-9") + it "sets :vm_hostname to nil by default" do + expect(driver[:vm_hostname]).to eq(nil) end it "sets :vm_hostname to a custom value, truncated to 12 chars" do From 46eded6a3f6980ed0761c99c9663a2ad5f641900 Mon Sep 17 00:00:00 2001 From: Fletcher Nichol Date: Thu, 19 Mar 2015 11:58:10 -0600 Subject: [PATCH 3/4] Fix failing specs for Verifier introduction. --- spec/kitchen/driver/vagrant_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/kitchen/driver/vagrant_spec.rb b/spec/kitchen/driver/vagrant_spec.rb index d54cbf46..dd03c9fc 100644 --- a/spec/kitchen/driver/vagrant_spec.rb +++ b/spec/kitchen/driver/vagrant_spec.rb @@ -24,6 +24,7 @@ require "kitchen/driver/vagrant" require "kitchen/provisioner/dummy" require "kitchen/transport/dummy" +require "kitchen/verifier/dummy" describe Kitchen::Driver::Vagrant do @@ -32,7 +33,7 @@ let(:config) { { :kitchen_root => "/kroot" } } let(:platform) { Kitchen::Platform.new(:name => "fooos-99") } let(:suite) { Kitchen::Suite.new(:name => "suitey") } - let(:busser) { double("busser") } + let(:verifier) { Kitchen::Verifier::Dummy.new } let(:provisioner) { Kitchen::Provisioner::Dummy.new } let(:transport) { Kitchen::Transport::Dummy.new } let(:state_file) { double("state_file") } @@ -49,7 +50,7 @@ let(:instance) do Kitchen::Instance.new( - :busser => busser, + :verifier => verifier, :driver => driver_object, :logger => logger, :suite => suite, From 758bc98e42239763a95e60ae864f585c29178eab Mon Sep 17 00:00:00 2001 From: Fletcher Nichol Date: Tue, 24 Mar 2015 14:58:25 -0600 Subject: [PATCH 4/4] Update test-kitchen dependency, pinning to 1.4.0.beta.1 pre-release. --- kitchen-vagrant.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kitchen-vagrant.gemspec b/kitchen-vagrant.gemspec index 64733c73..a6939cc6 100644 --- a/kitchen-vagrant.gemspec +++ b/kitchen-vagrant.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |gem| gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ["lib"] - gem.add_dependency "test-kitchen", "~> 1.0" + gem.add_dependency "test-kitchen", "= 1.4.0.beta.1" gem.add_development_dependency "countloc", "~> 0.4" gem.add_development_dependency "rake"