diff --git a/README.md b/README.md
index ccff08f3..6d6f8b7e 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,12 @@ platforms:
driver:
box: opscode-ubuntu-12.04
box_url: https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_ubuntu-12.04_provisionerless.box
+- name: windows2012r2_cloud
+ driver:
+ box: windows2012r2_cloud
+ box_url: https://s3.amazonaws.com/box-cutter-us-east-1-cloudtrail/windows/virtualbox4.3.12/win2012r2-datacenter-chef11.12.8.box
+ transport:
+ name: winrm
- name: ubuntu-12.10
driver:
box: opscode-ubuntu-12.10
@@ -119,6 +125,17 @@ By default the value is unset, or `nil`. In this case the driver will use the
Vagrant [default provider][vagrant_default_provider] which at this current time
is `virtualbox` unless set by `VAGRANT_DEFAULT_PROVIDER` environment variable.
+### gui
+
+This is the vm console gui configuration and is used to make the console
+visible/hidden following a create action.
+
+If this value is `nil` or `false` then the console will be hidden, if `true`
+the console will be visble.
+
+This is particularly helpful for debugging local Virtualbox/VMware Fusion/VMware Workstation guests along with
+adding `clipboard: bidirectional` to the customize section.
+
### customize
A **Hash** of customizations to a Vagrant virtual machine. Each key/value
@@ -130,6 +147,7 @@ driver:
customize:
memory: 1024
cpuexecutioncap: 50
+ clipboard: bidirectional
```
will generate a Vagrantfile configuration similar to:
diff --git a/Rakefile b/Rakefile
index 0cb701cf..30d36bc7 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,6 +1,9 @@
require "bundler/gem_tasks"
require 'cane/rake_task'
require 'tailor/rake_task'
+require 'rspec/core/rake_task'
+
+RSpec::Core::RakeTask.new(:test)
desc "Run cane to check quality metrics"
Cane::RakeTask.new
@@ -16,6 +19,6 @@ task :stats do
end
desc "Run all quality tasks"
-task :quality => [:cane, :tailor, :stats]
+task :quality => [:cane, :tailor, :stats, :test]
task :default => [ :quality ]
diff --git a/kitchen-vagrant.gemspec b/kitchen-vagrant.gemspec
index aba61b9e..fd5ffa53 100644
--- a/kitchen-vagrant.gemspec
+++ b/kitchen-vagrant.gemspec
@@ -23,4 +23,5 @@ Gem::Specification.new do |gem|
gem.add_development_dependency 'cane'
gem.add_development_dependency 'tailor'
gem.add_development_dependency 'countloc'
+ gem.add_development_dependency 'rspec'
end
diff --git a/lib/kitchen/driver/vagrant.rb b/lib/kitchen/driver/vagrant.rb
index 32c1403e..be277bdf 100644
--- a/lib/kitchen/driver/vagrant.rb
+++ b/lib/kitchen/driver/vagrant.rb
@@ -21,6 +21,9 @@
require 'kitchen'
+# Useful VBox Machine Class
+require 'kitchen/provider/machine'
+
module Kitchen
module Driver
@@ -31,7 +34,7 @@ module Driver
#
# @todo Vagrant installation check and version will be placed into any
# dependency hook checks when feature is released
- class Vagrant < Kitchen::Driver::SSHBase
+ class Vagrant < Kitchen::Driver::Base
default_config :customize, {}
default_config :network, []
@@ -44,12 +47,16 @@ class Vagrant < Kitchen::Driver::SSHBase
default_config :provider,
ENV.fetch('VAGRANT_DEFAULT_PROVIDER', "virtualbox")
+ default_config :box do |driver|
+ "opscode-#{driver.instance.platform.name}"
+ end
+
default_config :vm_hostname do |driver|
- "#{driver.instance.name}.vagrantup.com"
+ driver.instance.name
end
- default_config :box do |driver|
- "opscode-#{driver.instance.platform.name}"
+ default_config :communicator do |driver|
+ driver.instance.transport.class.name.split('::').last.downcase
end
default_config :box_url do |driver|
@@ -66,7 +73,7 @@ def create(state)
cmd = "vagrant up --no-provision"
cmd += " --provider=#{config[:provider]}" if config[:provider]
run cmd
- set_ssh_state(state)
+ set_state(state)
info("Vagrant instance #{instance.to_str} created.")
end
@@ -178,15 +185,6 @@ def template
File.expand_path(config[:vagrantfile_erb], config[:kitchen_root])
end
- def set_ssh_state(state)
- hash = vagrant_ssh_config
-
- state[:hostname] = hash["HostName"]
- state[:username] = hash["User"]
- state[:ssh_key] = hash["IdentityFile"]
- state[:port] = hash["Port"]
- end
-
def vagrant_ssh_config
output = run("vagrant ssh-config", :live_stream => nil)
lines = output.split("\n").map do |line|
@@ -230,6 +228,48 @@ def check_vagrant_version
" Please upgrade to version #{MIN_VER} or higher from #{WEBSITE}."
end
end
+
+ def set_state(state)
+ hash = vagrant_ssh_config
+
+ state[:hostname] = hash["HostName"]
+ state[:username] = config[:username] || hash["User"]
+ state[:password] = config[:password] || 'vagrant'
+ state[:ssh_key] = hash["IdentityFile"]
+ state[:port] = port_from_config
+ if config[:communicator] == "ssh"
+ state[:port] = hash["Port"]
+ end
+ refresh_forwarded_port(state)
+ end
+
+ def port_from_config
+ port = config[:port]
+ if port.nil?
+ guest_port = config[:guest_port] || instance.transport.default_port
+ if !config[:network].empty?
+ forwards = config[:network].select do |net|
+ net[0] == "forwarded_port" && net[1][:guest] == guest_port
+ end
+ forwards.each do |forward|
+ port = forward[1][:host]
+ end
+ end
+ end
+ port || instance.transport.default_port
+ end
+
+ # Get forwarded_port from Provider
+ #
+ # Working with: VirtualBox
+ def refresh_forwarded_port(state)
+ case config[:provider]
+ when "virtualbox"
+ Provider::VirtualBox::Machine.new(vagrant_root) do |machine|
+ state[:port] = machine.host_port(default_port)
+ end
+ end
+ end
end
end
end
diff --git a/lib/kitchen/driver/vagrant_version.rb b/lib/kitchen/driver/vagrant_version.rb
index 37e26fcc..960cb828 100644
--- a/lib/kitchen/driver/vagrant_version.rb
+++ b/lib/kitchen/driver/vagrant_version.rb
@@ -21,6 +21,6 @@ module Kitchen
module Driver
# Version string for Vagrant Kitchen driver
- VAGRANT_VERSION = "0.15.0"
+ VAGRANT_VERSION = '0.16.0'
end
end
diff --git a/lib/kitchen/provider/machine.rb b/lib/kitchen/provider/machine.rb
new file mode 100644
index 00000000..f2deac0d
--- /dev/null
+++ b/lib/kitchen/provider/machine.rb
@@ -0,0 +1,155 @@
+# -*- encoding: utf-8 -*-
+#
+# Author:: Salim Afiune ()
+#
+# Copyright (C) 2014, Salim Afiune
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'mixlib/shellout'
+
+module Kitchen
+
+ module Provider
+
+ module VirtualBox
+ # This class will simulate one Virtual Box Machine
+ # It will give us the latest Forwarded Ports list
+ # where we can check if the port has changed
+ #
+ # Supported Virtual Box
+ # => version 4.2.x
+ # => version 4.3.x
+ class Machine
+ # The UUID of the virtual machine
+ attr_reader :uuid
+
+ # The forwarded_ports of the virtual machine
+ attr_reader :forwarded_ports
+
+
+ def initialize(root_path, uuid=nil)
+ # Did we get the UUID
+ @uuid = uuid
+
+ # The VBoxID that Vagrant spun up
+ id = File.expand_path(File.join(root_path,
+ %w{.vagrant machines default virtualbox id}))
+
+ # If UUID was not given but the VBoxID exist.
+ @uuid = File.read(id) if File.exists?(id) && @uuid.nil?
+
+ # Report that the VM is missing.
+ vm_notfound unless exists?
+
+ yield self if block_given?
+ end
+
+ def execute(*command)
+ # Easy shellout method to execute something! :D
+ command.insert(0, vboxmanage)
+ c = Mixlib::ShellOut.new(*command.join(' '), :timeout => 10800)
+ c.run_command
+ c.error!
+ c
+ end
+
+ # Conveniente method for executing a method.
+ def self.host_port(guest_port, root_path)
+ new(root_path).host_port(guest_port)
+ end
+
+ def get_vboxmanage_path
+ # Set the path to VBoxManage
+ vboxmanage_path = 'VBoxManage'
+
+ if RUBY_PLATFORM =~ /cygwin|mswin|mingw|bccwin|wince|emx/
+ # On Windows, search for VBOX_INSTALL_PATH environmental
+ # variable to find VBoxManage.
+ if ENV.has_key?('VBOX_INSTALL_PATH')
+ # Get the path.
+ path = ENV['VBOX_INSTALL_PATH']
+
+ # There can actually be multiple paths in here, so we need to
+ # split by the separator ";" and see which is a good one.
+ path.split(";").each do |single|
+ # Make sure it ends with a \
+ single += '\\' if !single.end_with?('\\')
+
+ # If the executable exists, then set it as the main path
+ # and break out
+ vboxmanage = "#{path}VBoxManage.exe"
+ if File.file?(vboxmanage)
+ vboxmanage_path = "\"#{vboxmanage}\""
+ break
+ end
+ end
+ end
+ end
+ @vboxmanage = vboxmanage_path
+ end
+
+ def read_forwarded_ports(uuid=nil)
+ uuid ||= @uuid
+
+ results = []
+ current_nic = nil
+ info = execute('showvminfo', uuid, '--machinereadable')
+ info.stdout.split("\n").each do |line|
+ # This is how we find the nic that a FP is attached to,
+ # since this comes first.
+ current_nic = $1.to_i if line =~ /^nic(\d+)=".+?"/
+
+ # Parse out the forwarded port information
+ if line =~ /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"/
+ result = [current_nic, $1.to_s, $2.to_i, $3.to_i]
+ results << result
+ end
+ end
+ @forwarded_ports = results
+ end
+
+ def host_port(guest_port)
+ port=nil
+ # Verify forwarded_ports
+ read_forwarded_ports.each do |forwarded_port|
+ # Checking that this is the WinRM port
+ if forwarded_port.include?(guest_port)
+ # For Virtual Box 4.2 the host port position is (2)
+ port=forwarded_port[2]
+ break
+ end
+ end
+ # This is the actual port that the VM is using.
+ port
+ end
+
+ def exists?(uuid=nil)
+ uuid ||= @uuid
+ execute('showvminfo', uuid).exitstatus == 0
+ end
+
+ # Command to manage Virtual Box `vboxmanage`
+ def vboxmanage
+ get_vboxmanage_path if @vboxmanage.nil?
+ @vboxmanage
+ end
+
+ def vm_notfound
+ raise 'Missing Virtual Box Machine. Run `kitchen create` and retry.'
+ end
+
+ end # => Machine
+ end # => VirtualBox
+ end # => Provider
+end # => Kitchen
diff --git a/spec/kitchen/driver/vagrant_spec.rb b/spec/kitchen/driver/vagrant_spec.rb
new file mode 100644
index 00000000..0dc171d7
--- /dev/null
+++ b/spec/kitchen/driver/vagrant_spec.rb
@@ -0,0 +1,173 @@
+require 'kitchen/driver/vagrant'
+require 'yaml'
+
+module Kitchen
+
+ module Driver
+
+ class Testable < Kitchen::Driver::Vagrant
+
+ def initialize(config, ssh_config = {})
+ super(config)
+ @ssh_config = ssh_config
+ end
+
+ def create(state)
+ set_state(state)
+ end
+
+ protected
+
+ def vagrant_ssh_config
+ @ssh_config
+ end
+
+ def refresh_forwarded_port(state)
+ end
+ end
+ end
+end
+
+describe Kitchen::Driver::Vagrant do
+ let(:vagrant_ssh_config) {{
+ 'Port' => 22,
+ 'HostName' => "hostname",
+ 'User' => "user",
+ 'IdentityFile' => "key"
+ }}
+
+ subject do
+ parsed = Kitchen::Util.symbolized_hash(YAML.load(yaml))
+ transport = parsed[:transport][:name]
+ instance = double(
+ transport: Kitchen::Transport.for_plugin(transport, parsed),
+ logger: Logger.new(STDOUT)
+ )
+ driver = Kitchen::Driver::Testable.new(parsed[:driver], vagrant_ssh_config)
+ driver.instance = instance
+ driver
+ end
+
+ context "using ssh" do
+ let(:vagrant_ssh_config) {{
+ 'Port' => 8888,
+ 'HostName' => "hostname",
+ 'User' => "user",
+ 'IdentityFile' => "key"
+ }}
+ let(:yaml) do
+ <<-EOS
+ transport:
+ name: ssh
+ driver:
+ box: mybox
+ EOS
+ end
+
+ it "uses ssh_config from vagrant" do
+ state = {}
+ subject.create(state)
+
+ expect(state[:port]).to eq 8888
+ end
+ end
+
+ context "using winrm" do
+ context "forwarding to default guest port" do
+ let(:yaml) do
+ <<-EOS
+ transport:
+ name: winrm
+ driver:
+ box: mybox
+ network:
+ - ["forwarded_port", {guest: 5985, host: 55985}]
+ EOS
+ end
+
+ it "sets the port to the forwarded port" do
+ state = {}
+ subject.create(state)
+
+ expect(state[:port]).to eq 55985
+ end
+ end
+
+ context "using a custom port" do
+ let(:yaml) do
+ <<-EOS
+ transport:
+ name: winrm
+ driver:
+ box: mybox
+ port: 9999
+ EOS
+ end
+
+ it "sets the port to the custom port" do
+ state = {}
+ subject.create(state)
+
+ expect(state[:port]).to eq 9999
+ end
+ end
+
+ context "forwarding to a custom guest_port" do
+ let(:yaml) do
+ <<-EOS
+ transport:
+ name: winrm
+ driver:
+ box: mybox
+ guest_port: 7777
+ network:
+ - ["forwarded_port", {guest: 7777, host: 33333}]
+ EOS
+ end
+
+ it "sets the port to the forwarded port" do
+ state = {}
+ subject.create(state)
+
+ expect(state[:port]).to eq 33333
+ end
+ end
+
+ context "specifying a custom guest_port with no forwarding" do
+ let(:yaml) do
+ <<-EOS
+ transport:
+ name: winrm
+ driver:
+ box: mybox
+ guest_port: 7777
+ EOS
+ end
+
+ it "sets the port to the default port" do
+ state = {}
+ subject.create(state)
+
+ expect(state[:port]).to eq 5985
+ end
+ end
+
+ context "no network config" do
+ let(:yaml) do
+ <<-EOS
+ transport:
+ name: winrm
+ driver:
+ box: mybox
+ EOS
+ end
+
+ it "sets the port to the default port" do
+ state = {}
+ subject.create(state)
+
+ expect(state[:port]).to eq 5985
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/templates/Vagrantfile.erb b/templates/Vagrantfile.erb
index 9c714fc4..54409501 100644
--- a/templates/Vagrantfile.erb
+++ b/templates/Vagrantfile.erb
@@ -5,11 +5,21 @@ Vagrant.configure("2") do |c|
<% if config[:vm_hostname] %>
c.vm.hostname = "<%= config[:vm_hostname] %>"
<% end %>
+c.vm.communicator = "<%= config[:communicator] %>"
+<% if config[:port] %>
+ c.<%= config[:communicator] %>.port = <%= config[:port] %>
+<% end %>
+<% if config[:guest_port] %>
+ c.<%= config[:communicator] %>.guest_port = <%= config[:guest_port] %>
+<% end %>
<% if config[:guest] %>
c.vm.guest = <%= config[:guest] %>
<% end %>
<% if config[:username] %>
- c.ssh.username = "<%= config[:username] %>"
+ c.<%= config[:communicator] %>.username = "<%= config[:username] %>"
+<% end %>
+<% if config[:password] %>
+ c.<%= config[:communicator] %>.password = "<%= config[:password] %>"
<% end %>
<% if config[:ssh_key] %>
c.ssh.private_key_path = "<%= config[:ssh_key] %>"
@@ -25,22 +35,29 @@ Vagrant.configure("2") do |c|
<% end %>
c.vm.provider :<%= config[:provider] %> do |p|
-<% config[:customize].each do |key, value| %>
- <% case config[:provider]
- when "virtualbox" %>
- p.customize ["modifyvm", :id, "--<%= key %>", "<%= value %>"]
- <% when "rackspace", "softlayer" %>
- p.<%= key %> = "<%= value%>"
- <% when /^vmware_/ %>
- <% if key == :memory %>
- <% unless config[:customize].include?(:memsize) %>
- p.vmx["memsize"] = "<%= value %>"
+ <% case config[:provider]
+ when "virtualbox", "vmware_fusion", "vmware_workstation" %>
+ <% if config[:gui] %>
+ p.gui = <%= config[:gui] %>
+ <% end %>
+ <% end %>
+
+ <% config[:customize].each do |key, value| %>
+ <% case config[:provider]
+ when "virtualbox" %>
+ p.customize ["modifyvm", :id, "--<%= key %>", "<%= value %>"]
+ <% when "rackspace", "softlayer" %>
+ p.<%= key %> = "<%= value%>"
+ <% when /^vmware_/ %>
+ <% if key == :memory %>
+ <% unless config[:customize].include?(:memsize) %>
+ p.vmx["memsize"] = "<%= value %>"
+ <% end %>
+ <% else %>
+ p.vmx["<%= key %>"] = "<%= value %>"
<% end %>
- <% else %>
- p.vmx["<%= key %>"] = "<%= value %>"
<% end %>
<% end %>
-<% end %>
end
end