realityforge-vagrant-windows 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ *.rbc
3
+ Gemfile.lock
4
+ pkg
5
+ rdoc
6
+ # IntelliJ project files
7
+ /*.iml
8
+ /*.ipr
9
+ /*.iws
@@ -0,0 +1 @@
1
+ 1.9.3-p327
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in my_gem.gemspec
4
+ gemspec
5
+
@@ -0,0 +1,115 @@
1
+ TODO:
2
+ vagrant rdp && vagrant ps to connect to VM
3
+
4
+
5
+ Installing
6
+ ==========
7
+
8
+ ```
9
+ gem install vagrant-windows
10
+ ```
11
+
12
+ Building a Base Box
13
+ ===================
14
+
15
+ All Windows Machines
16
+ --------------------
17
+ -Enable WinRM
18
+
19
+ ```
20
+ winrm quickconfig -q
21
+ winrm set winrm/config/winrs @{MaxMemoryPerShellMB="512"}
22
+ winrm set winrm/config @{MaxTimeoutms="1800000"}
23
+ winrm set winrm/config/service @{AllowUnencrypted="true"}
24
+ winrm set winrm/config/service/auth @{Basic="true"}
25
+ ```
26
+ - Create a vagrant user
27
+ - For things to work out of the box, username and password should both be vagrant
28
+
29
+ - Turn off UAC (Msconfig)
30
+ - Disable complex passwords
31
+
32
+ Servers
33
+ --------
34
+ - Disable Shutdown Tracker (http://www.jppinto.com/2010/01/how-to-disable-the-shutdown-event-tracker-in-server-20032008/)
35
+ - Disable "Server Manager" Starting at login (http://www.elmajdal.net/win2k8/How_to_Turn_Off_The_Automatic_Display_of_Server_Manager_At_logon.aspx)
36
+
37
+ The Vagrant File
38
+ ================
39
+
40
+ Add the following to your Vagrantfile
41
+
42
+ ```ruby
43
+ config.vm.guest = :windows
44
+
45
+ config.vm.forward_port 3389, 3390, :name => "rdp", :auto => true
46
+ config.vm.forward_port 5985, 5985, :name => "winrm", :auto => true
47
+ ```
48
+
49
+ Example:
50
+ ```ruby
51
+ Vagrant::Config.run do |config|
52
+
53
+ #The following timeout configuration is option, however if have
54
+ #any large remote_file resources in your chef recipes, you may
55
+ #experience timeouts (reported as 500 responses)
56
+ config.winrm.timeout = 1800 #Set WinRM Timeout in seconds (Default 30)
57
+
58
+ # Configure base box parameters
59
+ config.vm.box = "windows2008r2"
60
+ config.vm.box_url = "./windows-2008-r2.box"
61
+ config.vm.guest = :windows
62
+
63
+ config.vm.forward_port 3389, 3390, :name => "rdp", :auto => true
64
+ config.vm.forward_port 5985, 5985, :name => "winrm", :auto => true
65
+
66
+ config.vm.provision :chef_solo do |chef|
67
+ chef.add_recipe("your::recipe")
68
+ end
69
+
70
+ end
71
+ ````
72
+
73
+ What Works?
74
+ ===========
75
+ - vagrant up|hault|reload|provision
76
+ - Chef Vagrant Provisioner
77
+ - Puppet standalone provisioning
78
+
79
+ What has not been tested
80
+ ========================
81
+ - Everything Else!!!
82
+ - Shell provisioning
83
+ - Shell should work, though I have not vetted it yet.
84
+
85
+ What does not work
86
+ ==================
87
+ - <s>Complex networking setups</s> - Fixed in 0.0.3
88
+ - Note that I have not tested the Virtual Box 4.0 Driver, all _should_ work. Can someone please confirm?
89
+
90
+ What Can I do to help?
91
+ ======================
92
+ 1. Contribute Code (See Below)
93
+ 2. Test Various Scenarios and file bugs for things that dont work
94
+
95
+ Contributing
96
+ ============
97
+ 1. Fork it.
98
+ 2. Create a branch (git checkout -b my_feature_branch)
99
+ 3. Commit your changes (git commit -am "Added a sweet feature")
100
+ 4. Push to the branch (git push origin my_feature_branch)
101
+ 5. Create a pull requst from your branch into master (Please be sure to provide enough detail for us to cipher what this change is doing)
102
+
103
+
104
+ References and Shout Outs
105
+ =========================
106
+ - Chris McClimans - Vagrant Branch (https://github.com/hh/vagrant/blob/feature/winrm/)
107
+ -Dan Wanek - WinRM GEM (https://github.com/zenchild/WinRM)
108
+ - +1 For being super responsive to pull requests.
109
+
110
+
111
+ Changelog
112
+ =========
113
+ 0.1.1 - Remove extra debug information from command output.
114
+ 0.1.2 - added virtual box 4.2 support
115
+ 0.1.3 - added puppet provisioner
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,185 @@
1
+ require 'timeout'
2
+
3
+ require 'log4r'
4
+ #require 'em-winrm'
5
+ require 'winrm'
6
+ require 'highline'
7
+
8
+ require 'vagrant/util/ansi_escape_code_remover'
9
+ require 'vagrant/util/file_mode'
10
+ require 'vagrant/util/platform'
11
+ require 'vagrant/util/retryable'
12
+
13
+ module Vagrant
14
+ module Communication
15
+ # Provides communication with the VM via WinRM.
16
+ class WinRM < Base
17
+
18
+ include Util::ANSIEscapeCodeRemover
19
+ include Util::Retryable
20
+
21
+ attr_reader :logger
22
+ attr_reader :vm
23
+
24
+ def initialize(vm)
25
+ @vm = vm
26
+ @logger = Log4r::Logger.new("vagrant::communication::winrm")
27
+ @co = nil
28
+ end
29
+
30
+ def ready?
31
+ logger.debug("Checking whether WinRM is ready...")
32
+
33
+ Timeout.timeout(@vm.config.winrm.timeout) do
34
+ execute "hostname"
35
+ end
36
+
37
+ # If we reached this point then we successfully connected
38
+ logger.info("WinRM is ready!")
39
+ true
40
+ rescue Timeout::Error, HTTPClient::KeepAliveDisconnected => e
41
+ #, Errors::SSHConnectionRefused, Net::SSH::Disconnect => e
42
+ # The above errors represent various reasons that WinRM may not be
43
+ # ready yet. Return false.
44
+ logger.info("WinRM not up yet: #{e.inspect}")
45
+
46
+ return false
47
+ end
48
+
49
+ # Wrap Sudo in execute.... One day we could integrate with UAC, but Icky
50
+ def sudo(command, opts=nil, &block)
51
+ execute(command,opts,&block)
52
+ end
53
+
54
+ def execute(command, opts=nil, &block)
55
+
56
+ # Connect to WinRM, giving it a few tries
57
+ logger.info("Connecting to WinRM: #{@vm.winrm.info[:host]}:#{@vm.winrm.info[:port]}")
58
+
59
+ opts = {
60
+ :error_check => true,
61
+ :error_class => Errors::VagrantError,
62
+ :error_key => :winrm_bad_exit_status,
63
+ :command => command,
64
+ :sudo => false,
65
+ :shell => :powershell
66
+ }.merge(opts || {})
67
+
68
+ # Connect via WinRM and execute the command in the shell.
69
+ exceptions = [HTTPClient::KeepAliveDisconnected]
70
+ exit_status = retryable(:tries => @vm.config.winrm.max_tries, :on => exceptions, :sleep => 10) do
71
+ logger.debug "WinRM Trying to connect"
72
+ shell_execute(command,opts[:shell], &block)
73
+ end
74
+
75
+ logger.debug("#{command} EXIT STATUS #{exit_status.inspect}")
76
+
77
+ # Check for any errors
78
+ if opts[:error_check] && exit_status != 0
79
+ # The error classes expect the translation key to be _key,
80
+ # but that makes for an ugly configuration parameter, so we
81
+ # set it here from `error_key`
82
+ error_opts = opts.merge(:_key => opts[:error_key])
83
+ raise opts[:error_class], error_opts
84
+ end
85
+
86
+ # Return the exit status
87
+ exit_status
88
+ end
89
+
90
+ def new_session
91
+ opts = {
92
+ :user => vm.config.winrm.username,
93
+ :pass => vm.config.winrm.password,
94
+ :host => vm.config.winrm.host,
95
+ :port => vm.winrm.info[:port],
96
+ :operation_timeout => vm.config.winrm.timeout,
97
+ :basic_auth_only => true
98
+ }.merge ({})
99
+
100
+ # create a session
101
+ logger.info("Attempting WinRM session with options: #{opts}")
102
+ begin
103
+ endpoint = "http://#{opts[:host]}:#{opts[:port]}/wsman"
104
+ client = ::WinRM::WinRMWebService.new(endpoint, :plaintext, opts)
105
+ client.set_timeout(opts[:operation_timeout]) if opts[:operation_timeout]
106
+ rescue ::WinRM::WinRMAuthorizationError => error
107
+ raise ::WinRM::WinRMAuthorizationError.new("#{error.message}@#{opts[:host]}")
108
+ end
109
+
110
+ client
111
+ end
112
+
113
+ def session
114
+ @session ||= new_session
115
+ end
116
+
117
+ def h
118
+ @highline ||= HighLine.new
119
+ end
120
+
121
+ def print_data(data, color = :green)
122
+ if data =~ /\n/
123
+ data.split(/\n/).each { |d| print_data(d, color) }
124
+ else
125
+ puts h.color(data.chomp, color)
126
+ end
127
+ end
128
+
129
+ def upload(from, to)
130
+ file = "winrm-upload-#{rand()}"
131
+ file_name = (session.cmd("echo %TEMP%\\#{file}"))[:data][0][:stdout].chomp
132
+ session.powershell <<-EOH
133
+ if(Test-Path #{to})
134
+ {
135
+ rm #{to}
136
+ }
137
+ EOH
138
+ Base64.encode64(IO.binread(from)).gsub("\n",'').chars.to_a.each_slice(8000-file_name.size) do |chunk|
139
+ out = session.cmd( "echo #{chunk.join} >> \"#{file_name}\"" )
140
+ end
141
+ execute "mkdir [System.IO.Path]::GetDirectoryName(\"#{to}\")"
142
+ execute <<-EOH
143
+ $base64_string = Get-Content \"#{file_name}\"
144
+ $bytes = [System.Convert]::FromBase64String($base64_string)
145
+ $new_file = [System.IO.Path]::GetFullPath(\"#{to}\")
146
+ [System.IO.File]::WriteAllBytes($new_file,$bytes)
147
+ EOH
148
+ end
149
+
150
+ protected
151
+
152
+ # Executes the command on an SSH connection within a login shell.
153
+ def shell_execute(command,shell = :powershell)
154
+ logger.info("Execute: #{command}")
155
+ exit_status = nil
156
+
157
+ if shell.eql? :cmd
158
+ output = session.cmd(command) do |out,error|
159
+ print_data(out) if out
160
+ print_data(error, :red) if error
161
+ end
162
+ elsif shell.eql? :powershell
163
+ new_command = File.read(File.expand_path("#{File.dirname(__FILE__)}/../scripts/command_alias.ps1"))
164
+ new_command << "\r\n"
165
+ new_command << command
166
+ output = session.powershell(new_command) do |out,error|
167
+ print_data(out) if out
168
+ print_data(error, :red) if error
169
+ end
170
+ else
171
+ raise Vagrant::Errors::WinRMInvalidShell, "#{shell} is not a valid type of shell"
172
+ end
173
+
174
+
175
+ exit_status = output[:exitcode]
176
+ logger.debug exit_status.inspect
177
+
178
+ # Return the final exit status
179
+ return exit_status
180
+ rescue ::WinRM::WinRMHTTPTransportError => e
181
+ raise Vagrant::Errors::WinRMTimeout, e.message
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,27 @@
1
+ module Vagrant
2
+ module Config
3
+ class Windows < Vagrant::Config::Base
4
+ attr_accessor :winrm_user
5
+ attr_accessor :winrm_password
6
+ attr_accessor :halt_timeout
7
+ attr_accessor :halt_check_interval
8
+ attr_accessor :device
9
+
10
+ def initialize
11
+ @winrm_user = 'vagrant'
12
+ @winrm_password = 'vagrant'
13
+ @halt_timeout = 30
14
+ @halt_check_interval = 1
15
+ @device = "e1000g"
16
+ end
17
+
18
+ def validate(env, errors)
19
+ [ :winrm_user, :winrm_password].each do |field|
20
+ errors.add(I18n.t("vagrant.config.common.error_empty", :field => field)) if !instance_variable_get("@#{field}".to_sym)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ Vagrant.config_keys.register(:windows) { Vagrant::Config::Windows }
@@ -0,0 +1,31 @@
1
+ module Vagrant
2
+ module Config
3
+ class WinRM < Vagrant::Config::Base
4
+ attr_accessor :username
5
+ attr_accessor :password
6
+ attr_accessor :host
7
+ attr_accessor :port
8
+ attr_accessor :guest_port
9
+ attr_accessor :max_tries
10
+ attr_accessor :timeout
11
+
12
+ def initialize
13
+ @username = "vagrant"
14
+ @password = "vagrant"
15
+ @guest_port = 5985
16
+ @host = "localhost"
17
+ @max_tries = 12
18
+ @timeout = 1800
19
+ end
20
+
21
+ def validate(env, errors)
22
+ [:username, :password, :host, :max_tries, :timeout].each do |field|
23
+ errors.add(I18n.t("vagrant.config.common.error_empty", :field => field)) if !instance_variable_get("@#{field}".to_sym)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ Vagrant.config_keys.register(:winrm) { Vagrant::Config::WinRM }
@@ -0,0 +1,22 @@
1
+ module Vagrant
2
+ module Errors
3
+ class WinRMPortNotDetected < VagrantError
4
+ status_code(550)
5
+ error_key(:winrm_port_not_detected)
6
+ end
7
+
8
+ class WinRMInvalidShell < VagrantError
9
+ status_code(551)
10
+ error_key(:winrm_invalid_shell)
11
+ end
12
+ class WinRMTransferError < VagrantError
13
+ status_code(552)
14
+ error_key(:winrm_upload_error)
15
+ end
16
+ class WinRMTimeout < VagrantError
17
+ status_code(553)
18
+ error_key(:winrm_timeout)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,116 @@
1
+ module Vagrant
2
+ module Guest
3
+ # A general Vagrant system implementation for "windows".
4
+ #
5
+ # Contributed by Chris McClimans <chris@hippiehacker.org>
6
+ class Windows < Base
7
+ # A custom config class which will be made accessible via `config.windows`
8
+ # Here for whenever it may be used.
9
+ class WindowsError < Errors::VagrantError
10
+ error_namespace("vagrant.guest.windows")
11
+ end
12
+
13
+ def change_host_name(name)
14
+ #### on windows, renaming a computer seems to require a reboot
15
+ @vm.channel.session.cmd("netdom renamecomputer %COMPUTERNAME% /force /reb:2 /newname:#{name}")
16
+ sleep 5
17
+ wait_for_state :running, @vm.config.windows.halt_timeout, @vm.config.windows.halt_check_interval
18
+ end
19
+
20
+ # TODO: I am sure that ciphering windows versions will be important at some point
21
+ def distro_dispatch
22
+ :windows
23
+ end
24
+
25
+ def halt
26
+ @vm.channel.execute("shutdown /s /t 1 /c \"Vagrant Halt\" /f /d p:4:1")
27
+ # Wait until the VM's state is actually powered off. If this doesn't
28
+ # occur within a reasonable amount of time (15 seconds by default),
29
+ # then simply return and allow Vagrant to kill the machine.
30
+ wait_for_state :poweroff, @vm.config.windows.halt_timeout, @vm.config.windows.halt_check_interval
31
+ end
32
+
33
+ def wait_for_state(state, timeout, interval)
34
+ count = 0
35
+ while @vm.state != state
36
+ count += 1
37
+ return if count > timeout
38
+ sleep interval
39
+ end
40
+ end
41
+
42
+ def mount_shared_folder(name, guestpath, options)
43
+ mount_script = TemplateRenderer.render(File.expand_path("#{File.dirname(__FILE__)}/../scripts/mount_volume.ps1"),
44
+ :options => {:mount_point => guestpath, :name => name})
45
+ @vm.channel.execute(mount_script,{:shell => :powershell})
46
+ end
47
+
48
+ def mount_nfs(ip, folders)
49
+ raise NotImplementedError, "Mounting NFS Shares on windows is not implemented"
50
+ # TODO: Maybe check for nfs support on the guest, since its often
51
+ # not installed by default
52
+ #folders.each do |name, opts|
53
+ # # Expand the guestpath, so we can handle things like "~/vagrant"
54
+ # real_guestpath = expanded_guest_path(opts[:guestpath])
55
+
56
+ # Do the actual creating and mounting
57
+ # @vm.channel.sudo("mkdir -p #{real_guestpath}")
58
+ # @vm.channel.sudo("mount -o vers=#{opts[:nfs_version]} #{ip}:'#{opts[:hostpath]}' #{real_guestpath}",
59
+ # :error_class => LinuxError,
60
+ # :error_key => :mount_nfs_fail)
61
+ #end
62
+ end
63
+
64
+ def configure_networks(networks)
65
+ ### HACK!!!!!
66
+ Nori.advanced_typecasting = false
67
+ if driver_mac_address = @vm.driver.read_mac_addresses
68
+ driver_mac_address = driver_mac_address.invert
69
+ end
70
+
71
+ vm_interface_map = {}
72
+ @vm.channel.session.wql("SELECT * FROM Win32_NetworkAdapter WHERE NetConnectionStatus=2")[:win32_network_adapter].each do |nic|
73
+ naked_mac = nic[:mac_address].gsub(':','')
74
+
75
+ if driver_mac_address[naked_mac]
76
+ vm_interface_map[driver_mac_address[naked_mac]] = {
77
+ :name => nic[:net_connection_id],
78
+ :mac_address => naked_mac,
79
+ :index => nic[:interface_index]
80
+ }
81
+ end
82
+ end
83
+ networks.each do |network|
84
+ interface_name = vm_interface_map[network[:interface]+1][:name]
85
+ interface_index = vm_interface_map[network[:interface]+1][:index]
86
+
87
+ if network[:type].to_sym == :static
88
+ @vm.channel.execute("netsh interface ip set address \"#{interface_name}\" static #{network[:ip]} #{network[:netmask]}")
89
+
90
+ elsif network[:type].to_sym == :dhcp
91
+
92
+ if !vm.channel.test("if (-not (netsh interface ip show address \"#{interface_name}\" | where \{$_ -match \"DHCP enabled:\s+Yes\"\})) \{exit 1\} ")
93
+ vm.channel.execute("netsh interface ip set address \"#{interface_name}\" dhcp")
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+
100
+ def windows_path(path)
101
+ p = ''
102
+ if path =~ /^\//
103
+ p << 'C:\\'
104
+ end
105
+ p << path
106
+ p.gsub! /\//, "\\"
107
+ p.gsub /\\\\{0,}/, "\\"
108
+ end
109
+
110
+
111
+
112
+ end
113
+ end
114
+ end
115
+
116
+ Vagrant.guests.register(:windows) { Vagrant::Guest::Windows }
@@ -0,0 +1,57 @@
1
+ require 'vagrant/driver/virtualbox_base'
2
+ require 'vagrant/driver/virtualbox'
3
+
4
+
5
+ module Vagrant
6
+ module Driver
7
+
8
+ class VirtualBox_4_2 < VirtualBoxBase
9
+ def read_mac_addresses
10
+ macs = {}
11
+ info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
12
+ info.split("\n").each do |line|
13
+ if matcher = /^macaddress(\d+)="(.+?)"$/.match(line)
14
+ adapter = matcher[1].to_i
15
+ mac = matcher[2].to_s
16
+ macs[adapter] = mac
17
+ end
18
+ end
19
+ macs
20
+ end
21
+ end
22
+
23
+ class VirtualBox_4_1 < VirtualBoxBase
24
+ def read_mac_addresses
25
+ macs = {}
26
+ info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
27
+ info.split("\n").each do |line|
28
+ if matcher = /^macaddress(\d+)="(.+?)"$/.match(line)
29
+ adapter = matcher[1].to_i
30
+ mac = matcher[2].to_s
31
+ macs[adapter] = mac
32
+ end
33
+ end
34
+ macs
35
+ end
36
+ end
37
+
38
+ class VirtualBox_4_0 < VirtualBoxBase
39
+ def read_mac_addresses
40
+ macs = {}
41
+ info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true)
42
+ info.split("\n").each do |line|
43
+ if matcher = /^macaddress(\d+)="(.+?)"$/.match(line)
44
+ adapter = matcher[1].to_i
45
+ mac = matcher[2].to_s
46
+ macs[adapter] = mac
47
+ end
48
+ end
49
+ macs
50
+ end
51
+ end
52
+
53
+ class VirtualBox < VirtualBoxBase
54
+ def_delegator :@driver, :read_mac_addresses
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,16 @@
1
+ module Vagrant
2
+ class VM
3
+ def winrm
4
+ @winrm ||= WinRM.new(self)
5
+ end
6
+
7
+ def channel
8
+ if @guest.class.eql? Vagrant::Guest::Windows
9
+ @channel ||= Communication::WinRM.new(self)
10
+ else
11
+ @channel ||= Communication::SSH.new(self)
12
+ end
13
+ @channel
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,72 @@
1
+ module Vagrant
2
+ module Provisioners
3
+ class Puppet < Base
4
+ def run_puppet_client
5
+ options = [config.options].flatten
6
+ if env[:vm].config.vm.guest == :windows
7
+ options << "--modulepath '#{@module_paths.values.join(';')}'" if !@module_paths.empty?
8
+ else
9
+ options << "--modulepath '#{@module_paths.values.join(':')}'" if !@module_paths.empty?
10
+ end
11
+
12
+ options << @manifest_file
13
+ options = options.join(" ")
14
+
15
+ # Build up the custom facts if we have any
16
+ facter = ""
17
+ if !config.facter.empty?
18
+ facts = []
19
+ config.facter.each do |key, value|
20
+ if env[:vm].config.vm.guest == :windows
21
+ facts << "$env:FACTER_#{key}='#{value}';"
22
+ else
23
+ facts << "FACTER_#{key}='#{value}'"
24
+ end
25
+ end
26
+
27
+ facter = "#{facts.join(" ")} "
28
+ end
29
+ if env[:vm].config.vm.guest == :windows
30
+ command = "cd #{manifests_guest_path}; if($?) \{ #{facter} puppet apply #{options} \}"
31
+ else
32
+ command = "cd #{manifests_guest_path} && #{facter}puppet apply #{options}"
33
+ end
34
+
35
+ env[:ui].info I18n.t("vagrant.provisioners.puppet.running_puppet",
36
+ :manifest => @manifest_file)
37
+
38
+ env[:vm].channel.sudo(command) do |type, data|
39
+ env[:ui].info(data.chomp, :prefix => false)
40
+ end
41
+ end
42
+ def verify_binary(binary)
43
+ if env[:vm].config.vm.guest == :windows
44
+ command = "command #{binary}"
45
+ else
46
+ command = "which #{binary}"
47
+ end
48
+ env[:vm].channel.sudo(command,
49
+ :error_class => PuppetError,
50
+ :error_key => :not_detected,
51
+ :binary => binary)
52
+ end
53
+ def verify_shared_folders(folders)
54
+ folders.each do |folder|
55
+ @logger.debug("Checking for shared folder: #{folder}")
56
+
57
+
58
+ if env[:vm].config.vm.guest == :windows
59
+ if !env[:vm].channel.test("if(-not (test-path #{folder})) \{exit 1\} ")
60
+ raise PuppetError, :missing_shared_folders
61
+ end
62
+ else
63
+ if !env[:vm].channel.test("test -d #{folder}")
64
+ raise PuppetError, :missing_shared_folders
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,35 @@
1
+ require 'tempfile'
2
+
3
+ module Vagrant
4
+ module Provisioners
5
+ class Winrm < Base
6
+ class Config < Vagrant::Config::Base
7
+ attr_accessor :inline
8
+
9
+ def validate(env, errors)
10
+ # Validate that the parameters are properly set
11
+ if !inline
12
+ errors.add("No inline set")
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.config_class
18
+ Config
19
+ end
20
+
21
+ def provision!
22
+ env[:vm].channel.sudo(config.inline) do |type, data|
23
+ if [:stderr, :stdout].include?(type)
24
+ # Output the data with the proper color based on the stream.
25
+ color = type == :stdout ? :green : :red
26
+
27
+ # Note: Be sure to chomp the data to avoid the newlines that the
28
+ # Chef outputs.
29
+ env[:ui].info(data.chomp, :color => color, :prefix => false)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ function which {
2
+ $command = [Array](Get-Command $args[0] -errorAction continue)
3
+ write-host $command[0].Definition
4
+ }
5
+
6
+ function test ([Switch] $d, [String] $path) {
7
+ Resolve-Path $path| Out-Null;
8
+ }
9
+
10
+ function chown {
11
+ exit 0
12
+ }
13
+
14
+ function mkdir ([Switch] $p, [String] $path)
15
+ {
16
+ if(Test-Path $path)
17
+ {
18
+ exit 0
19
+ } else {
20
+ New-Item $p -Type Directory -Force | Out-Null
21
+ }
22
+ }
23
+
@@ -0,0 +1,49 @@
1
+
2
+ function Test-ReparsePoint([string]$path) {
3
+ $file = Get-Item $path -Force -ea 0
4
+ return [bool]($file.Attributes -band [IO.FileAttributes]::ReparsePoint)
5
+ }
6
+
7
+
8
+ $MountPoint = [System.IO.Path]::GetFullPath("<%= options[:mount_point] %>")
9
+ $ShareName = "<%= options[:name] %>"
10
+
11
+ # https://github.com/BIAINC/vagrant-windows/issues/4
12
+ # Not sure why this works, but it does.
13
+
14
+ & net use $ShareName 2>&1 | Out-Null
15
+
16
+ Write-Debug "Attempting to mount $ShareName to $MountPoint"
17
+ if( (Test-Path "$MountPoint") -and (Test-ReparsePoint "$MountPoint") )
18
+ {
19
+ Write-Debug "Junction already exists, so I will delete it"
20
+ # Powershell refuses to delete junctions, oh well use cmd
21
+ cmd /c rd "$MountPoint"
22
+
23
+ if ( $LASTEXITCODE -ne 0 )
24
+ {
25
+ Write-Error "Failed to delete symbolic link at $MountPoint"
26
+ exit 1
27
+ }
28
+
29
+ }
30
+ elseif(Test-Path $MountPoint)
31
+ {
32
+ Write-Debug "Mount point already exists and is not a symbolic link"
33
+ exit 1
34
+ }
35
+
36
+ $BaseDirectory = [System.IO.Path]::GetDirectoryName($MountPoint)
37
+
38
+ if (-not (Test-Path $BaseDirectory))
39
+ {
40
+ Write-Debug "Creating parent directory for mount point $BaseDirectory"
41
+ New-Item $BaseDirectory -Type Directory -Force | Out-Null
42
+ }
43
+
44
+ cmd /c mklink /D "$MountPoint" "\\vboxsrv\$ShareName" | out-null
45
+
46
+ if ( $LASTEXITCODE -ne 0 )
47
+ {
48
+ exit 1
49
+ }
@@ -0,0 +1,3 @@
1
+ module VagrantWindows
2
+ VERSION = "0.1.4"
3
+ end
@@ -0,0 +1,82 @@
1
+ require 'log4r'
2
+ #require 'em-winrm'
3
+
4
+ module Vagrant
5
+ # Manages WINRM access to a specific environment. Allows an environment to
6
+ # run commands, upload files, and check if a host is up.
7
+ class WinRM
8
+ include Util::SafeExec
9
+
10
+ def initialize(vm)
11
+ @vm = vm
12
+ @logger = Log4r::Logger.new("vagrant::winrm")
13
+ end
14
+
15
+ # Returns a hash of information necessary for accessing this
16
+ # virtual machine via WINRM.
17
+ #
18
+ # @return [Hash]
19
+ def info
20
+ results = {
21
+ :host => @vm.config.winrm.host,
22
+ :port => @vm.config.winrm.port || @vm.driver.ssh_port(@vm.config.winrm.guest_port),
23
+ :username => @vm.config.winrm.username
24
+ }
25
+
26
+ # This can happen if no port is set and for some reason Vagrant
27
+ # can't detect an SSH port.
28
+ raise Errors::WinRMPortNotDetected if !results[:port]
29
+
30
+ # Return the results
31
+ return results
32
+ end
33
+
34
+ # Checks if this environment's machine is up (i.e. responding to WINRM).
35
+ #
36
+ # @return [Boolean]
37
+ def up?
38
+ # We have to determine the port outside of the block since it uses
39
+ # API calls which can only be used from the main thread in JRuby on
40
+ # Windows
41
+ ssh_port = port
42
+
43
+ require 'timeout'
44
+ Timeout.timeout(@env.config.ssh.timeout) do
45
+ execute 'hostname'
46
+ end
47
+
48
+ true
49
+ rescue Timeout::Error, Errno::ECONNREFUSED
50
+ return false
51
+ end
52
+
53
+ # Returns the port which is either given in the options hash or taken from
54
+ # the config by finding it in the forwarded ports hash based on the
55
+ # `config.ssh.forwarded_port_key`.
56
+ def port(opts={})
57
+ # Check if port was specified in options hash
58
+ return opts[:port] if opts[:port]
59
+
60
+ # Check if a port was specified in the config
61
+ return @env.config.winrm.port if @env.config.winrm.port
62
+
63
+ # Check if we have an SSH forwarded port
64
+ pnum_by_name = nil
65
+ pnum_by_destination = nil
66
+ @logger.info("Looking for winrm port: #{opts}")
67
+ @logger.info("Looking for winrm port: #{env.config.winrm.inspect}")
68
+
69
+ env.vm.vm.network_adapters.each do |na|
70
+ # Look for the port number by destination...
71
+ pnum_by_destination = na.nat_driver.forwarded_ports.detect do |fp|
72
+ fp.guestport == env.config.winrm.guest_port
73
+ end
74
+ end
75
+
76
+ return pnum_by_destination.hostport if pnum_by_destination
77
+
78
+ # This should NEVER happen.
79
+ raise Errors::WinRMPortNotDetected
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,24 @@
1
+ #Add Windows Guest Defintion
2
+ require 'vagrant-windows/guest/windows'
3
+
4
+ #Add Configuration Items
5
+ require 'vagrant-windows/config/windows'
6
+ require 'vagrant-windows/config/winrm'
7
+
8
+ # Add WinRM Communication Channel
9
+ require 'vagrant-windows/communication/winrm'
10
+
11
+ # Add Provisioner
12
+ require 'vagrant-windows/provisioners/puppet'
13
+
14
+ #Monkey Patch the VM object to support multiple channels
15
+ require 'vagrant-windows/monkey_patches/vm'
16
+
17
+ #Monkey Patch the driver to support returning a mapping of mac addresses to nics
18
+ require 'vagrant-windows/monkey_patches/driver'
19
+
20
+ require 'vagrant-windows/winrm'
21
+
22
+ #Errors are good
23
+ require 'vagrant-windows/errors'
24
+
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/vagrant-windows/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Paul Morton"]
6
+ gem.email = ["pmorton@biaprotect.com"]
7
+ gem.description = %q{Windows Guest Support for Vagrant}
8
+ gem.summary = %q{A small gem that adds windows guest support to vagrant, uses WinRM as the Communication Channel}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "realityforge-vagrant-windows"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = VagrantWindows::VERSION
17
+
18
+ gem.add_runtime_dependency "winrm", "~> 1.1.1"
19
+ gem.add_runtime_dependency 'vagrant', "~> 1.0"
20
+ gem.add_runtime_dependency 'highline'
21
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: realityforge-vagrant-windows
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Paul Morton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: winrm
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.1.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.1.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: vagrant
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: highline
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Windows Guest Support for Vagrant
63
+ email:
64
+ - pmorton@biaprotect.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - .ruby-version
71
+ - Gemfile
72
+ - README.md
73
+ - Rakefile
74
+ - lib/vagrant-windows/communication/winrm.rb
75
+ - lib/vagrant-windows/config/windows.rb
76
+ - lib/vagrant-windows/config/winrm.rb
77
+ - lib/vagrant-windows/errors.rb
78
+ - lib/vagrant-windows/guest/windows.rb
79
+ - lib/vagrant-windows/monkey_patches/driver.rb
80
+ - lib/vagrant-windows/monkey_patches/vm.rb
81
+ - lib/vagrant-windows/provisioners/puppet.rb
82
+ - lib/vagrant-windows/provisioners/winrm.rb
83
+ - lib/vagrant-windows/scripts/command_alias.ps1
84
+ - lib/vagrant-windows/scripts/mount_volume.ps1.erb
85
+ - lib/vagrant-windows/version.rb
86
+ - lib/vagrant-windows/winrm.rb
87
+ - lib/vagrant_init.rb
88
+ - vagrant-windows.gemspec
89
+ homepage: ''
90
+ licenses: []
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 1.8.23
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: A small gem that adds windows guest support to vagrant, uses WinRM as the
113
+ Communication Channel
114
+ test_files: []
115
+ has_rdoc: