diff --git a/plugins/guests/smartos/cap/change_host_name.rb b/plugins/guests/smartos/cap/change_host_name.rb new file mode 100644 index 000000000..3e9313dc8 --- /dev/null +++ b/plugins/guests/smartos/cap/change_host_name.rb @@ -0,0 +1,17 @@ +module VagrantPlugins + module GuestSmartos + module Cap + class ChangeHostName + def self.change_host_name(machine, name) + su_cmd = machine.config.smartos.suexec_cmd + + # Only do this if the hostname is not already set + if !machine.communicate.test("hostname | grep '#{name}'") + machine.communicate.execute("#{su_cmd} sh -c \"echo '#{name}' > /etc/nodename\"") + machine.communicate.execute("#{su_cmd} hostname #{name}") + end + end + end + end + end +end diff --git a/plugins/guests/smartos/cap/configure_networks.rb b/plugins/guests/smartos/cap/configure_networks.rb new file mode 100644 index 000000000..dac941dab --- /dev/null +++ b/plugins/guests/smartos/cap/configure_networks.rb @@ -0,0 +1,27 @@ +module VagrantPlugins + module GuestSmartos + module Cap + class ConfigureNetworks + def self.configure_networks(machine, networks) + su_cmd = machine.config.smartos.suexec_cmd + + networks.each do |network| + device = "#{machine.config.smartos.device}#{network[:interface]}" + ifconfig_cmd = "#{su_cmd} /sbin/ifconfig #{device}" + + machine.communicate.execute("#{ifconfig_cmd} plumb") + + if network[:type].to_sym == :static + machine.communicate.execute("#{ifconfig_cmd} inet #{network[:ip]} netmask #{network[:netmask]}") + machine.communicate.execute("#{ifconfig_cmd} up") + machine.communicate.execute("#{su_cmd} sh -c \"echo '#{network[:ip]}' > /etc/hostname.#{device}\"") + elsif network[:type].to_sym == :dhcp + machine.communicate.execute("#{ifconfig_cmd} dhcp start") + end + end + end + end + end + end +end + diff --git a/plugins/guests/smartos/cap/halt.rb b/plugins/guests/smartos/cap/halt.rb new file mode 100644 index 000000000..c0178e388 --- /dev/null +++ b/plugins/guests/smartos/cap/halt.rb @@ -0,0 +1,22 @@ +module VagrantPlugins + module GuestSmartos + module Cap + class Halt + def self.halt(machine) + # There should be an exception raised if the line + # + # vagrant::::profiles=Primary Administrator + # + # does not exist in /etc/user_attr. TODO + begin + machine.communicate.execute( + "#{machine.config.smartos.suexec_cmd} /usr/sbin/shutdown -y -i5 -g0") + rescue IOError + # Ignore, this probably means connection closed because it + # shut down. + end + end + end + end + end +end diff --git a/plugins/guests/smartos/cap/mount_nfs.rb b/plugins/guests/smartos/cap/mount_nfs.rb new file mode 100644 index 000000000..14fa0bdfa --- /dev/null +++ b/plugins/guests/smartos/cap/mount_nfs.rb @@ -0,0 +1,19 @@ +module VagrantPlugins + module GuestSmartos + module Cap + class MountNFS + def self.mount_nfs_folder(machine, ip, folders) + sudo = machine.config.smartos.suexec_cmd + + folders.each do |name, opts| + machine.communicate.tap do |comm| + comm.execute("#{sudo} mkdir -p #{opts[:guestpath]}", {:shell => "sh"}) + comm.execute("#{sudo} /usr/sbin/mount -F nfs '#{ip}:#{opts[:hostpath]}' '#{opts[:guestpath]}'", {:shell => "sh"}) + end + end + end + end + end + end +end + diff --git a/plugins/guests/smartos/cap/rsync.rb b/plugins/guests/smartos/cap/rsync.rb new file mode 100644 index 000000000..0217c4dcc --- /dev/null +++ b/plugins/guests/smartos/cap/rsync.rb @@ -0,0 +1,21 @@ +module VagrantPlugins + module GuestSmartos + module Cap + class RSync + def self.rsync_installed(machine) + machine.communicate.test("which rsync") + end + + def self.rsync_pre(machine, folder_opts) + username = machine.ssh_info[:username] + sudo = machine.config.smartos.suexec_cmd + + machine.communicate.tap do |comm| + comm.execute("#{sudo} mkdir -p '#{folder_opts[:guestpath]}'") + comm.execute("#{sudo} chown -R #{username} '#{folder_opts[:guestpath]}'") + end + end + end + end + end +end diff --git a/plugins/guests/smartos/config.rb b/plugins/guests/smartos/config.rb new file mode 100644 index 000000000..65e9f8ea2 --- /dev/null +++ b/plugins/guests/smartos/config.rb @@ -0,0 +1,18 @@ +module VagrantPlugins + module GuestSmartos + class Config < Vagrant.plugin("2", :config) + attr_accessor :halt_timeout + attr_accessor :halt_check_interval + # This sets the command to use to execute items as a superuser. sudo is default + attr_accessor :suexec_cmd + attr_accessor :device + + def initialize + @halt_timeout = 30 + @halt_check_interval = 1 + @suexec_cmd = 'pfexec' + @device = "e1000g" + end + end + end +end diff --git a/plugins/guests/smartos/guest.rb b/plugins/guests/smartos/guest.rb new file mode 100644 index 000000000..1d2a18f02 --- /dev/null +++ b/plugins/guests/smartos/guest.rb @@ -0,0 +1,11 @@ +require "vagrant" + +module VagrantPlugins + module GuestSmartos + class Guest < Vagrant.plugin("2", :guest) + def detect?(machine) + machine.communicate.test("cat /etc/release | grep -i SmartOS") + end + end + end +end diff --git a/plugins/guests/smartos/plugin.rb b/plugins/guests/smartos/plugin.rb new file mode 100644 index 000000000..f99f7e498 --- /dev/null +++ b/plugins/guests/smartos/plugin.rb @@ -0,0 +1,50 @@ +require "vagrant" + +module VagrantPlugins + module GuestSmartos + class Plugin < Vagrant.plugin("2") + name "SmartOS guest." + description "SmartOS guest support." + + config("smartos") do + require File.expand_path("../config", __FILE__) + Config + end + + guest("smartos") do + require File.expand_path("../guest", __FILE__) + Guest + end + + guest_capability("smartos", "change_host_name") do + require_relative "cap/change_host_name" + Cap::ChangeHostName + end + + guest_capability("smartos", "configure_networks") do + require_relative "cap/configure_networks" + Cap::ConfigureNetworks + end + + guest_capability("smartos", "halt") do + require_relative "cap/halt" + Cap::Halt + end + + guest_capability("smartos", "mount_nfs_folder") do + require_relative "cap/mount_nfs" + Cap::MountNFS + end + + guest_capability("smartos", "rsync_installed") do + require_relative "cap/rsync" + Cap::RSync + end + + guest_capability("smartos", "rsync_pre") do + require_relative "cap/rsync" + Cap::RSync + end + end + end +end diff --git a/test/unit/plugins/guests/smartos/cap/change_host_name_test.rb b/test/unit/plugins/guests/smartos/cap/change_host_name_test.rb new file mode 100644 index 000000000..1d9a84692 --- /dev/null +++ b/test/unit/plugins/guests/smartos/cap/change_host_name_test.rb @@ -0,0 +1,33 @@ +require File.expand_path("../../../../../base", __FILE__) + +describe "VagrantPlugins::VagrantPlugins::Cap::ChangeHostName" do + let(:plugin) { VagrantPlugins::GuestSmartos::Plugin.components.guest_capabilities[:smartos].get(:change_host_name) } + let(:machine) { double("machine") } + let(:config) { double("config", smartos: VagrantPlugins::GuestSmartos::Config.new) } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:old_hostname) { 'oldhostname.olddomain.tld' } + let(:new_hostname) { 'newhostname.olddomain.tld' } + + before do + machine.stub(:communicate).and_return(communicator) + machine.stub(:config).and_return(config) + communicator.stub_command("hostname | grep '#{old_hostname}'", stdout: old_hostname) + end + + after do + communicator.verify_expectations! + end + + describe ".change_host_name" do + it "refreshes the hostname service with the hostname command" do + communicator.expect_command(%Q(pfexec hostname #{new_hostname})) + plugin.change_host_name(machine, new_hostname) + end + + it "writes the hostname into /etc/nodename" do + communicator.expect_command(%Q(pfexec sh -c "echo '#{new_hostname}' > /etc/nodename")) + plugin.change_host_name(machine, new_hostname) + end + end +end + diff --git a/test/unit/plugins/guests/smartos/cap/configure_networks_test.rb b/test/unit/plugins/guests/smartos/cap/configure_networks_test.rb new file mode 100644 index 000000000..711875573 --- /dev/null +++ b/test/unit/plugins/guests/smartos/cap/configure_networks_test.rb @@ -0,0 +1,61 @@ +require File.expand_path("../../../../../base", __FILE__) + +describe "VagrantPlugins::VagrantPlugins::Cap::ConfigureNetworks" do + let(:plugin) { VagrantPlugins::GuestSmartos::Plugin.components.guest_capabilities[:smartos].get(:configure_networks) } + let(:machine) { double("machine") } + let(:config) { double("config", smartos: VagrantPlugins::GuestSmartos::Config.new) } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + + before do + machine.stub(:communicate).and_return(communicator) + machine.stub(:config).and_return(config) + end + + after do + communicator.verify_expectations! + end + + describe ".configure_networks" do + let(:interface) { "eth0" } + let(:device) { "e1000g#{interface}" } + + describe 'dhcp' do + let(:network) { {:interface => interface, :type => :dhcp} } + + it "plumbs the device" do + communicator.expect_command(%Q(pfexec /sbin/ifconfig #{device} plumb)) + plugin.configure_networks(machine, [network]) + end + + it "starts dhcp for the device" do + communicator.expect_command(%Q(pfexec /sbin/ifconfig #{device} dhcp start)) + plugin.configure_networks(machine, [network]) + end + end + + describe 'static' do + let(:network) { {:interface => interface, :type => :static, :ip => '1.1.1.1', :netmask => '255.255.255.0'} } + + it "plumbs the network" do + communicator.expect_command(%Q(pfexec /sbin/ifconfig #{device} plumb)) + plugin.configure_networks(machine, [network]) + end + + it "starts sets netmask and IP for the device" do + communicator.expect_command(%Q(pfexec /sbin/ifconfig #{device} inet 1.1.1.1 netmask 255.255.255.0)) + plugin.configure_networks(machine, [network]) + end + + it "starts enables the device" do + communicator.expect_command(%Q(pfexec /sbin/ifconfig #{device} up)) + plugin.configure_networks(machine, [network]) + end + + it "starts writes out a hostname file" do + communicator.expect_command(%Q(pfexec sh -c "echo '1.1.1.1' > /etc/hostname.#{device}")) + plugin.configure_networks(machine, [network]) + end + end + end +end + diff --git a/test/unit/plugins/guests/smartos/cap/halt_test.rb b/test/unit/plugins/guests/smartos/cap/halt_test.rb new file mode 100644 index 000000000..782fc6b09 --- /dev/null +++ b/test/unit/plugins/guests/smartos/cap/halt_test.rb @@ -0,0 +1,25 @@ +require File.expand_path("../../../../../base", __FILE__) + +describe "VagrantPlugins::VagrantPlugins::Cap::Halt" do + let(:plugin) { VagrantPlugins::GuestSmartos::Plugin.components.guest_capabilities[:smartos].get(:halt) } + let(:machine) { double("machine") } + let(:config) { double("config", smartos: VagrantPlugins::GuestSmartos::Config.new) } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + + before do + machine.stub(:communicate).and_return(communicator) + machine.stub(:config).and_return(config) + end + + after do + communicator.verify_expectations! + end + + describe ".halt" do + it "sends a shutdown signal" do + communicator.expect_command(%Q(pfexec /usr/sbin/shutdown -y -i5 -g0)) + plugin.halt(machine) + end + end +end + diff --git a/test/unit/plugins/guests/smartos/cap/mount_nfs_test.rb b/test/unit/plugins/guests/smartos/cap/mount_nfs_test.rb new file mode 100644 index 000000000..cbf0b875a --- /dev/null +++ b/test/unit/plugins/guests/smartos/cap/mount_nfs_test.rb @@ -0,0 +1,30 @@ +require File.expand_path("../../../../../base", __FILE__) + +describe "VagrantPlugins::VagrantPlugins::Cap::MountNFS" do + let(:plugin) { VagrantPlugins::GuestSmartos::Plugin.components.guest_capabilities[:smartos].get(:mount_nfs_folder) } + let(:machine) { double("machine") } + let(:config) { double("config", smartos: VagrantPlugins::GuestSmartos::Config.new) } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + + before do + machine.stub(:communicate).and_return(communicator) + machine.stub(:config).and_return(config) + end + + after do + communicator.verify_expectations! + end + + describe ".mount_nfs_folder" do + it "creates the directory mount point" do + communicator.expect_command(%Q(pfexec mkdir -p /mountpoint)) + plugin.mount_nfs_folder(machine, '1.1.1.1', {'nfs' => {:guestpath => '/mountpoint'}}) + end + + it "mounts the NFS share" do + communicator.expect_command(%Q(pfexec /usr/sbin/mount -F nfs '1.1.1.1:/some/share' '/mountpoint')) + plugin.mount_nfs_folder(machine, '1.1.1.1', {'nfs' => {:guestpath => '/mountpoint', :hostpath => '/some/share'}}) + end + end +end + diff --git a/test/unit/plugins/guests/smartos/cap/rsync_test.rb b/test/unit/plugins/guests/smartos/cap/rsync_test.rb new file mode 100644 index 000000000..d95ae49a7 --- /dev/null +++ b/test/unit/plugins/guests/smartos/cap/rsync_test.rb @@ -0,0 +1,52 @@ +require File.expand_path("../../../../../base", __FILE__) + +describe "VagrantPlugins::VagrantPlugins::Cap::Rsync" do + let(:plugin) { VagrantPlugins::GuestSmartos::Plugin.components.guest_capabilities[:smartos].get(:rsync_pre) } + let(:machine) { double("machine") } + let(:config) { double("config", smartos: VagrantPlugins::GuestSmartos::Config.new) } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + + before do + machine.stub(:communicate).and_return(communicator) + machine.stub(:config).and_return(config) + end + + after do + communicator.verify_expectations! + end + + describe ".rsync_installed" do + describe "when rsync is in the path" do + it "is true" do + communicator.stub_command("which rsync", stdout: '/usr/bin/rsync', exit_code: 0) + expect(plugin.rsync_installed(machine)).to be true + end + end + + describe "when rsync is not in the path" do + it "is false" do + communicator.stub_command("which rsync", stdout: '', exit_code: 1) + expect(plugin.rsync_installed(machine)).to be false + end + end + end + + describe ".rsync_pre" do + let(:username) { "some_user" } + + before do + machine.stub(:ssh_info).and_return({username: username}) + end + + it "creates a local directory" do + communicator.expect_command(%Q(pfexec mkdir -p '/mountpoint')) + plugin.rsync_pre(machine, {guestpath: '/mountpoint'}) + end + + it "chowns local directory to ssh user" do + communicator.expect_command(%Q(pfexec chown -R #{username} '/mountpoint')) + plugin.rsync_pre(machine, {guestpath: '/mountpoint'}) + end + end +end + diff --git a/test/unit/support/dummy_communicator.rb b/test/unit/support/dummy_communicator.rb index fa401d47b..4f7ffe914 100644 --- a/test/unit/support/dummy_communicator.rb +++ b/test/unit/support/dummy_communicator.rb @@ -73,6 +73,10 @@ module VagrantTests def sudo(command, opts=nil, &block) execute(command, opts, &block) end + + def test(command, opts=nil) + execute(command, opts) == 0 + end end end end