chef-provisioning-vagrant 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +201 -201
- data/README.md +63 -60
- data/Rakefile +6 -6
- data/lib/chef/provider/vagrant_box.rb +58 -58
- data/lib/chef/provider/vagrant_cluster.rb +40 -40
- data/lib/chef/provisioning/driver_init/vagrant.rb +3 -3
- data/lib/chef/provisioning/vagrant_driver.rb +36 -36
- data/lib/chef/provisioning/vagrant_driver/driver.rb +470 -470
- data/lib/chef/provisioning/vagrant_driver/version.rb +7 -7
- data/lib/chef/resource/vagrant_box.rb +24 -24
- data/lib/chef/resource/vagrant_cluster.rb +21 -21
- data/spec/spec_helper.rb +21 -21
- data/spec/vagrant_spec.rb +13 -13
- data/spec/vagrant_support.rb +53 -53
- metadata +6 -6
data/Rakefile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
require 'bundler'
|
2
|
-
require 'bundler/gem_tasks'
|
3
|
-
|
4
|
-
task :spec do
|
5
|
-
require File.expand_path('spec/run')
|
6
|
-
end
|
1
|
+
require 'bundler'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
task :spec do
|
5
|
+
require File.expand_path('spec/run')
|
6
|
+
end
|
@@ -1,58 +1,58 @@
|
|
1
|
-
require 'chef/provider/lwrp_base'
|
2
|
-
require 'chef/mixin/shell_out'
|
3
|
-
|
4
|
-
class Chef::Provider::VagrantBox < Chef::Provider::LWRPBase
|
5
|
-
|
6
|
-
use_inline_resources
|
7
|
-
|
8
|
-
include Chef::Mixin::ShellOut
|
9
|
-
|
10
|
-
def whyrun_supported?
|
11
|
-
true
|
12
|
-
end
|
13
|
-
|
14
|
-
action :create do
|
15
|
-
if !box_exists?(new_resource)
|
16
|
-
if new_resource.url
|
17
|
-
converge_by "run 'vagrant box add #{new_resource.name} #{new_resource.url} --provider #{new_resource.vagrant_provider}'" do
|
18
|
-
shell_out("vagrant box add #{new_resource.name} #{new_resource.url} --provider #{new_resource.vagrant_provider}").error!
|
19
|
-
end
|
20
|
-
else
|
21
|
-
raise "Box #{new_resource.name} does not exist"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
action :delete do
|
27
|
-
if box_exists?(new_resource.name)
|
28
|
-
converge_by "run 'vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]} --provider #{new_resource.vagrant_provider}'" do
|
29
|
-
shell_out("vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]} --provider #{new_resource.vagrant_provider}").error!
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# Since all box names must be unique for a particular vagrant provider, this hash now
|
35
|
-
# keys off the provider name, as opposed to the box name. The version is not currently
|
36
|
-
# used, but is collected as metadata for future consumption
|
37
|
-
def list_boxes
|
38
|
-
@list_boxes ||= shell_out("vagrant box list").stdout.lines.inject({}) do |result, line|
|
39
|
-
line =~ /^(\S+)\s+\((.+),(.+)\)\s*$/
|
40
|
-
if result.has_key?($2)
|
41
|
-
result[$2][$1] = $3
|
42
|
-
else
|
43
|
-
result[$2] = { $1 => $3 }
|
44
|
-
end
|
45
|
-
result
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# In some rather strained logic, we hook into the vagrant provider, then
|
50
|
-
# the box name to make sure we have the correct box already installed.
|
51
|
-
def box_exists?(new_resource)
|
52
|
-
boxes = list_boxes
|
53
|
-
boxes[new_resource.vagrant_provider].has_key?(new_resource.name)
|
54
|
-
end
|
55
|
-
|
56
|
-
def load_current_resource
|
57
|
-
end
|
58
|
-
end
|
1
|
+
require 'chef/provider/lwrp_base'
|
2
|
+
require 'chef/mixin/shell_out'
|
3
|
+
|
4
|
+
class Chef::Provider::VagrantBox < Chef::Provider::LWRPBase
|
5
|
+
|
6
|
+
use_inline_resources
|
7
|
+
|
8
|
+
include Chef::Mixin::ShellOut
|
9
|
+
|
10
|
+
def whyrun_supported?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
action :create do
|
15
|
+
if !box_exists?(new_resource)
|
16
|
+
if new_resource.url
|
17
|
+
converge_by "run 'vagrant box add #{new_resource.name} #{new_resource.url} --provider #{new_resource.vagrant_provider}'" do
|
18
|
+
shell_out("vagrant box add #{new_resource.name} #{new_resource.url} --provider #{new_resource.vagrant_provider}").error!
|
19
|
+
end
|
20
|
+
else
|
21
|
+
raise "Box #{new_resource.name} does not exist"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
action :delete do
|
27
|
+
if box_exists?(new_resource.name)
|
28
|
+
converge_by "run 'vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]} --provider #{new_resource.vagrant_provider}'" do
|
29
|
+
shell_out("vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]} --provider #{new_resource.vagrant_provider}").error!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Since all box names must be unique for a particular vagrant provider, this hash now
|
35
|
+
# keys off the provider name, as opposed to the box name. The version is not currently
|
36
|
+
# used, but is collected as metadata for future consumption
|
37
|
+
def list_boxes
|
38
|
+
@list_boxes ||= shell_out("vagrant box list").stdout.lines.inject({}) do |result, line|
|
39
|
+
line =~ /^(\S+)\s+\((.+),(.+)\)\s*$/
|
40
|
+
if result.has_key?($2)
|
41
|
+
result[$2][$1] = $3
|
42
|
+
else
|
43
|
+
result[$2] = { $1 => $3 }
|
44
|
+
end
|
45
|
+
result
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# In some rather strained logic, we hook into the vagrant provider, then
|
50
|
+
# the box name to make sure we have the correct box already installed.
|
51
|
+
def box_exists?(new_resource)
|
52
|
+
boxes = list_boxes
|
53
|
+
boxes[new_resource.vagrant_provider].has_key?(new_resource.name)
|
54
|
+
end
|
55
|
+
|
56
|
+
def load_current_resource
|
57
|
+
end
|
58
|
+
end
|
@@ -1,40 +1,40 @@
|
|
1
|
-
require 'chef/provider/lwrp_base'
|
2
|
-
require 'cheffish'
|
3
|
-
|
4
|
-
class Chef::Provider::VagrantCluster < Chef::Provider::LWRPBase
|
5
|
-
|
6
|
-
use_inline_resources
|
7
|
-
|
8
|
-
def whyrun_supported?
|
9
|
-
true
|
10
|
-
end
|
11
|
-
|
12
|
-
action :create do
|
13
|
-
the_base_path = new_resource.path
|
14
|
-
Cheffish.inline_resource(self, :create) do
|
15
|
-
directory the_base_path
|
16
|
-
file ::File.join(the_base_path, 'Vagrantfile') do
|
17
|
-
content <<EOM
|
18
|
-
Dir.glob('#{::File.join(the_base_path, '*.vm')}') do |vm_file|
|
19
|
-
eval(IO.read(vm_file), nil, vm_file)
|
20
|
-
end
|
21
|
-
EOM
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
action :delete do
|
27
|
-
the_base_path = new_resource.path
|
28
|
-
Cheffish.inline_resource(self, :delete) do
|
29
|
-
file ::File.join(the_base_path, 'Vagrantfile') do
|
30
|
-
action :delete
|
31
|
-
end
|
32
|
-
directory the_base_path do
|
33
|
-
action :delete
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def load_current_resource
|
39
|
-
end
|
40
|
-
end
|
1
|
+
require 'chef/provider/lwrp_base'
|
2
|
+
require 'cheffish'
|
3
|
+
|
4
|
+
class Chef::Provider::VagrantCluster < Chef::Provider::LWRPBase
|
5
|
+
|
6
|
+
use_inline_resources
|
7
|
+
|
8
|
+
def whyrun_supported?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
action :create do
|
13
|
+
the_base_path = new_resource.path
|
14
|
+
Cheffish.inline_resource(self, :create) do
|
15
|
+
directory the_base_path
|
16
|
+
file ::File.join(the_base_path, 'Vagrantfile') do
|
17
|
+
content <<EOM
|
18
|
+
Dir.glob('#{::File.join(the_base_path, '*.vm')}') do |vm_file|
|
19
|
+
eval(IO.read(vm_file), nil, vm_file)
|
20
|
+
end
|
21
|
+
EOM
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
action :delete do
|
27
|
+
the_base_path = new_resource.path
|
28
|
+
Cheffish.inline_resource(self, :delete) do
|
29
|
+
file ::File.join(the_base_path, 'Vagrantfile') do
|
30
|
+
action :delete
|
31
|
+
end
|
32
|
+
directory the_base_path do
|
33
|
+
action :delete
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_current_resource
|
39
|
+
end
|
40
|
+
end
|
@@ -1,3 +1,3 @@
|
|
1
|
-
require 'chef/provisioning/vagrant_driver/driver'
|
2
|
-
|
3
|
-
Chef::Provisioning.register_driver_class("vagrant", Chef::Provisioning::VagrantDriver::Driver)
|
1
|
+
require 'chef/provisioning/vagrant_driver/driver'
|
2
|
+
|
3
|
+
Chef::Provisioning.register_driver_class("vagrant", Chef::Provisioning::VagrantDriver::Driver)
|
@@ -1,36 +1,36 @@
|
|
1
|
-
require 'chef/provisioning'
|
2
|
-
require 'chef/resource/vagrant_cluster'
|
3
|
-
require 'chef/provider/vagrant_cluster'
|
4
|
-
require 'chef/resource/vagrant_box'
|
5
|
-
require 'chef/provider/vagrant_box'
|
6
|
-
require 'chef/provisioning/vagrant_driver/driver'
|
7
|
-
|
8
|
-
class Chef
|
9
|
-
module Provisioning
|
10
|
-
module VagrantDriver
|
11
|
-
def self.with_vagrant_box(run_context, box_name, vagrant_options = {}, &block)
|
12
|
-
if box_name.is_a?(Chef::Resource::VagrantBox)
|
13
|
-
new_options = { :vagrant_options => { 'vm.box' => box_name.name } }
|
14
|
-
new_options[:vagrant_options]['vm.box_url'] = box_name.url if box_name.url
|
15
|
-
new_options[:vagrant_provider] = box_name.vagrant_provider
|
16
|
-
else
|
17
|
-
new_options = { :vagrant_options => { 'vm.box' => box_name } }
|
18
|
-
end
|
19
|
-
|
20
|
-
run_context.chef_provisioning.add_machine_options(new_options, &block)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module DSL
|
26
|
-
module Recipe
|
27
|
-
def with_vagrant_cluster(cluster_path, &block)
|
28
|
-
with_driver("vagrant:#{cluster_path}", &block)
|
29
|
-
end
|
30
|
-
|
31
|
-
def with_vagrant_box(box_name, vagrant_options = {}, &block)
|
32
|
-
Chef::Provisioning::VagrantDriver.with_vagrant_box(run_context, box_name, vagrant_options, &block)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
1
|
+
require 'chef/provisioning'
|
2
|
+
require 'chef/resource/vagrant_cluster'
|
3
|
+
require 'chef/provider/vagrant_cluster'
|
4
|
+
require 'chef/resource/vagrant_box'
|
5
|
+
require 'chef/provider/vagrant_box'
|
6
|
+
require 'chef/provisioning/vagrant_driver/driver'
|
7
|
+
|
8
|
+
class Chef
|
9
|
+
module Provisioning
|
10
|
+
module VagrantDriver
|
11
|
+
def self.with_vagrant_box(run_context, box_name, vagrant_options = {}, &block)
|
12
|
+
if box_name.is_a?(Chef::Resource::VagrantBox)
|
13
|
+
new_options = { :vagrant_options => { 'vm.box' => box_name.name } }
|
14
|
+
new_options[:vagrant_options]['vm.box_url'] = box_name.url if box_name.url
|
15
|
+
new_options[:vagrant_provider] = box_name.vagrant_provider
|
16
|
+
else
|
17
|
+
new_options = { :vagrant_options => { 'vm.box' => box_name } }
|
18
|
+
end
|
19
|
+
|
20
|
+
run_context.chef_provisioning.add_machine_options(new_options, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module DSL
|
26
|
+
module Recipe
|
27
|
+
def with_vagrant_cluster(cluster_path, &block)
|
28
|
+
with_driver("vagrant:#{cluster_path}", &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def with_vagrant_box(box_name, vagrant_options = {}, &block)
|
32
|
+
Chef::Provisioning::VagrantDriver.with_vagrant_box(run_context, box_name, vagrant_options, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,470 +1,470 @@
|
|
1
|
-
require 'chef/mixin/shell_out'
|
2
|
-
require 'chef/provisioning/driver'
|
3
|
-
require 'chef/provisioning/machine/windows_machine'
|
4
|
-
require 'chef/provisioning/machine/unix_machine'
|
5
|
-
require 'chef/provisioning/convergence_strategy/install_msi'
|
6
|
-
require 'chef/provisioning/convergence_strategy/install_cached'
|
7
|
-
require 'chef/provisioning/transport/winrm'
|
8
|
-
require 'chef/provisioning/transport/ssh'
|
9
|
-
require 'chef/provisioning/vagrant_driver/version'
|
10
|
-
require 'chef/resource/vagrant_cluster'
|
11
|
-
require 'chef/provider/vagrant_cluster'
|
12
|
-
|
13
|
-
class Chef
|
14
|
-
module Provisioning
|
15
|
-
module VagrantDriver
|
16
|
-
# Provisions machines in vagrant.
|
17
|
-
class Driver < Chef::Provisioning::Driver
|
18
|
-
|
19
|
-
include Chef::Mixin::ShellOut
|
20
|
-
|
21
|
-
# Create a new vagrant driver.
|
22
|
-
#
|
23
|
-
# ## Parameters
|
24
|
-
# cluster_path - path to the directory containing the vagrant files, which
|
25
|
-
# should have been created with the vagrant_cluster resource.
|
26
|
-
def initialize(driver_url, config)
|
27
|
-
super
|
28
|
-
scheme, cluster_path = driver_url.split(':', 2)
|
29
|
-
@cluster_path = cluster_path
|
30
|
-
end
|
31
|
-
|
32
|
-
attr_reader :cluster_path
|
33
|
-
|
34
|
-
def self.from_url(driver_url, config)
|
35
|
-
Driver.new(driver_url, config)
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.canonicalize_url(driver_url, config)
|
39
|
-
scheme, cluster_path = driver_url.split(':', 2)
|
40
|
-
cluster_path = File.expand_path(cluster_path || File.join(Chef::Config.config_dir, 'vms'))
|
41
|
-
"vagrant:#{cluster_path}"
|
42
|
-
end
|
43
|
-
|
44
|
-
# Acquire a machine, generally by provisioning it. Returns a Machine
|
45
|
-
# object pointing at the machine, allowing useful actions like setup,
|
46
|
-
# converge, execute, file and directory.
|
47
|
-
def allocate_machine(action_handler, machine_spec, machine_options)
|
48
|
-
ensure_vagrant_cluster(action_handler)
|
49
|
-
vm_name = machine_spec.name
|
50
|
-
vm_file_path = File.join(cluster_path, "#{machine_spec.name}.vm")
|
51
|
-
vm_file_updated = create_vm_file(action_handler, vm_name, vm_file_path, machine_options)
|
52
|
-
if vm_file_updated || !machine_spec.location
|
53
|
-
old_location = machine_spec.location
|
54
|
-
machine_spec.location = {
|
55
|
-
'driver_url' => driver_url,
|
56
|
-
'driver_version' => Chef::Provisioning::VagrantDriver::VERSION,
|
57
|
-
'vm_name' => vm_name,
|
58
|
-
'vm_file_path' => vm_file_path,
|
59
|
-
'allocated_at' => Time.now.utc.to_s,
|
60
|
-
'host_node' => action_handler.host_node
|
61
|
-
}
|
62
|
-
machine_spec.location['needs_reload'] = true if vm_file_updated
|
63
|
-
if machine_options[:vagrant_options]
|
64
|
-
%w(vm.guest winrm.host winrm.port winrm.username winrm.password).each do |key|
|
65
|
-
machine_spec.location[key] = machine_options[:vagrant_options][key] if machine_options[:vagrant_options][key]
|
66
|
-
end
|
67
|
-
end
|
68
|
-
machine_spec.location['chef_client_timeout'] = machine_options[:chef_client_timeout] if machine_options[:chef_client_timeout]
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def ready_machine(action_handler, machine_spec, machine_options)
|
73
|
-
start_machine(action_handler, machine_spec, machine_options)
|
74
|
-
machine_for(machine_spec, machine_options)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Connect to machine without acquiring it
|
78
|
-
def connect_to_machine(machine_spec, machine_options)
|
79
|
-
machine_for(machine_spec, machine_options)
|
80
|
-
end
|
81
|
-
|
82
|
-
def destroy_machine(action_handler, machine_spec, machine_options)
|
83
|
-
if machine_spec.location
|
84
|
-
vm_name = machine_spec.location['vm_name']
|
85
|
-
current_status = vagrant_status(vm_name)
|
86
|
-
if current_status != 'not created'
|
87
|
-
action_handler.perform_action "run vagrant destroy -f #{vm_name} (status was '#{current_status}')" do
|
88
|
-
result = shell_out("vagrant destroy -f #{vm_name}", :cwd => cluster_path)
|
89
|
-
if result.exitstatus != 0
|
90
|
-
raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
convergence_strategy_for(machine_spec, machine_options).
|
96
|
-
cleanup_convergence(action_handler, machine_spec)
|
97
|
-
|
98
|
-
vm_file_path = machine_spec.location['vm_file_path']
|
99
|
-
Chef::Provisioning.inline_resource(action_handler) do
|
100
|
-
file vm_file_path do
|
101
|
-
action :delete
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def stop_machine(action_handler, machine_spec, machine_options)
|
108
|
-
if machine_spec.location
|
109
|
-
vm_name = machine_spec.location['vm_name']
|
110
|
-
current_status = vagrant_status(vm_name)
|
111
|
-
if current_status == 'running'
|
112
|
-
action_handler.perform_action "run vagrant halt #{vm_name} (status was '#{current_status}')" do
|
113
|
-
result = shell_out("vagrant halt #{vm_name}", :cwd => cluster_path)
|
114
|
-
if result.exitstatus != 0
|
115
|
-
raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def ready_machines(action_handler, specs_and_options, parallelizer)
|
123
|
-
start_machines(action_handler, specs_and_options)
|
124
|
-
machines = []
|
125
|
-
specs_and_options.each_pair do |spec, options|
|
126
|
-
machine = machine_for(spec, options)
|
127
|
-
machines << machine
|
128
|
-
yield machine if block_given?
|
129
|
-
end
|
130
|
-
machines
|
131
|
-
end
|
132
|
-
|
133
|
-
def destroy_machines(action_handler, specs_and_options, parallelizer)
|
134
|
-
all_names = []
|
135
|
-
all_status = []
|
136
|
-
all_outputs = {}
|
137
|
-
specs_and_options.each_key do |spec|
|
138
|
-
if spec.location
|
139
|
-
vm_name = spec.location['vm_name']
|
140
|
-
current_status = vagrant_status(vm_name)
|
141
|
-
if current_status != 'not created'
|
142
|
-
all_names.push(vm_name)
|
143
|
-
all_status.push(current_status)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
if all_names.length > 0
|
148
|
-
names = all_names.join(" ")
|
149
|
-
statuses = all_status.join(", ")
|
150
|
-
action_handler.perform_action "run vagrant destroy -f #{names} (status was '#{statuses}')" do
|
151
|
-
result = shell_out("vagrant destroy -f #{names}", :cwd => cluster_path)
|
152
|
-
if result.exitstatus != 0
|
153
|
-
raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
specs_and_options.each_pair do |spec, options|
|
158
|
-
convergence_strategy_for(spec, options).cleanup_convergence(action_handler, spec)
|
159
|
-
|
160
|
-
vm_file_path = spec.location['vm_file_path']
|
161
|
-
Chef::Provisioning.inline_resource(action_handler) do
|
162
|
-
file vm_file_path do
|
163
|
-
action :delete
|
164
|
-
end
|
165
|
-
end
|
166
|
-
yield spec if block_given?
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def stop_machines(action_handler, specs_and_options, parallelizer)
|
171
|
-
all_names = []
|
172
|
-
specs_and_options.each_key do |spec|
|
173
|
-
if spec.location
|
174
|
-
vm_name = spec.location['vm_name']
|
175
|
-
current_status = vagrant_status(vm_name)
|
176
|
-
if current_status == 'running'
|
177
|
-
all_names.push(vm_name)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
if all_names.length > 0
|
182
|
-
names = all_names.join(" ")
|
183
|
-
action_handler.perform_action "run vagrant halt #{names} (status was 'running')" do
|
184
|
-
result = shell_out("vagrant halt #{names}", :cwd => cluster_path)
|
185
|
-
if result.exitstatus != 0
|
186
|
-
raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
# Used by vagrant_cluster and machine to get the string used to configure vagrant
|
193
|
-
def self.vagrant_config_string(vagrant_config, variable, line_prefix)
|
194
|
-
hostname = name.gsub(/[^A-Za-z0-9\-]/, '-')
|
195
|
-
result = ''
|
196
|
-
vagrant_config.each_pair do |key, value|
|
197
|
-
result += "#{line_prefix}#{variable}.#{key} = #{value.inspect}\n"
|
198
|
-
end
|
199
|
-
result
|
200
|
-
end
|
201
|
-
|
202
|
-
def driver_url
|
203
|
-
"vagrant:#{cluster_path}"
|
204
|
-
end
|
205
|
-
|
206
|
-
protected
|
207
|
-
|
208
|
-
def ensure_vagrant_cluster(action_handler)
|
209
|
-
_cluster_path = cluster_path
|
210
|
-
Chef::Provisioning.inline_resource(action_handler) do
|
211
|
-
vagrant_cluster _cluster_path
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def create_vm_file(action_handler, vm_name, vm_file_path, machine_options)
|
216
|
-
# Determine contents of vm file
|
217
|
-
vm_file_content = "Vagrant.configure('2') do |outer_config|\n"
|
218
|
-
vm_file_content << " outer_config.vm.define #{vm_name.inspect} do |config|\n"
|
219
|
-
merged_vagrant_options = { 'vm.hostname' => vm_name }
|
220
|
-
if machine_options[:vagrant_options]
|
221
|
-
merged_vagrant_options = Cheffish::MergedConfig.new(machine_options[:vagrant_options], merged_vagrant_options)
|
222
|
-
end
|
223
|
-
merged_vagrant_options.each_pair do |key, value|
|
224
|
-
if key == 'vm.network'
|
225
|
-
vm_file_content << " config.#{key}(#{value})\n"
|
226
|
-
else
|
227
|
-
vm_file_content << " config.#{key} = #{value.inspect}\n"
|
228
|
-
end
|
229
|
-
end
|
230
|
-
vm_file_content << machine_options[:vagrant_config] if machine_options[:vagrant_config]
|
231
|
-
vm_file_content << " end\nend\n"
|
232
|
-
|
233
|
-
# Set up vagrant file
|
234
|
-
Chef::Provisioning.inline_resource(action_handler) do
|
235
|
-
file vm_file_path do
|
236
|
-
content vm_file_content
|
237
|
-
action :create
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
def start_machine(action_handler, machine_spec, machine_options)
|
243
|
-
vm_name = machine_spec.location['vm_name']
|
244
|
-
vm_provider = machine_options.has_key?(:vagrant_provider) ? machine_options[:vagrant_provider] : 'virtualbox'
|
245
|
-
up_timeout = machine_options[:up_timeout] || 10*60
|
246
|
-
current_status = vagrant_status(vm_name)
|
247
|
-
vm_file_updated = machine_spec.location['needs_reload']
|
248
|
-
machine_spec.location['needs_reload'] = false
|
249
|
-
if current_status != 'running'
|
250
|
-
# Run vagrant up if vm is not running
|
251
|
-
action_handler.perform_action "run vagrant up #{vm_name} --provider #{vm_provider} (status was '#{current_status}')" do
|
252
|
-
result = shell_out("vagrant up #{vm_name} --provider #{vm_provider}", :cwd => cluster_path,
|
253
|
-
:timeout => up_timeout)
|
254
|
-
if result.exitstatus != 0
|
255
|
-
raise "vagrant up #{vm_name} --provider #{vm_provider} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
256
|
-
end
|
257
|
-
parse_vagrant_up(result.stdout, machine_spec)
|
258
|
-
end
|
259
|
-
elsif vm_file_updated
|
260
|
-
# Run vagrant reload if vm is running and vm file changed
|
261
|
-
action_handler.perform_action "run vagrant reload #{vm_name}" do
|
262
|
-
result = shell_out("vagrant reload #{vm_name}", :cwd => cluster_path,
|
263
|
-
:timeout => up_timeout)
|
264
|
-
if result.exitstatus != 0
|
265
|
-
raise "vagrant reload #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
266
|
-
end
|
267
|
-
parse_vagrant_up(result.stdout, machine_spec)
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
def start_machines(action_handler, specs_and_options)
|
273
|
-
up_names = []
|
274
|
-
up_status = []
|
275
|
-
up_specs = {}
|
276
|
-
update_names = []
|
277
|
-
update_specs = {}
|
278
|
-
timeouts = []
|
279
|
-
specs_and_options.each_pair do |spec, options|
|
280
|
-
vm_name = spec.location['vm_name']
|
281
|
-
|
282
|
-
vm_file_updated = spec.location['needs_reload']
|
283
|
-
spec.location['needs_reload'] = false
|
284
|
-
|
285
|
-
current_status = vagrant_status(vm_name)
|
286
|
-
if current_status != 'running'
|
287
|
-
up_names.push(vm_name)
|
288
|
-
up_status.push(current_status)
|
289
|
-
up_specs[vm_name] = spec
|
290
|
-
elsif vm_file_updated
|
291
|
-
update_names.push(vm_name)
|
292
|
-
update_specs[vm_name] = spec
|
293
|
-
end
|
294
|
-
timeouts.push(options[:up_timeout])
|
295
|
-
end
|
296
|
-
# Use the highest timeout, if any exist
|
297
|
-
up_timeout = timeouts.compact.max
|
298
|
-
up_timeout ||= 10*60
|
299
|
-
if up_names.length > 0
|
300
|
-
# Run vagrant up if vm is not running
|
301
|
-
names = up_names.join(" ")
|
302
|
-
statuses = up_status.join(", ")
|
303
|
-
action_handler.perform_action "run vagrant up --parallel #{names} (status was '#{statuses}')" do
|
304
|
-
result = shell_out("vagrant up --parallel #{names}", :cwd => cluster_path,
|
305
|
-
:timeout => up_timeout)
|
306
|
-
if result.exitstatus != 0
|
307
|
-
raise "vagrant up #{names} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
308
|
-
end
|
309
|
-
parse_multi_vagrant_up(result.stdout, up_specs)
|
310
|
-
end
|
311
|
-
end
|
312
|
-
if update_names.length > 0
|
313
|
-
names = update_names.join(" ")
|
314
|
-
# Run vagrant reload if vm is running and vm file changed
|
315
|
-
action_handler.perform_action "run vagrant reload #{names}" do
|
316
|
-
result = shell_out("vagrant reload #{names}", :cwd => cluster_path,
|
317
|
-
:timeout => up_timeout)
|
318
|
-
if result.exitstatus != 0
|
319
|
-
raise "vagrant reload #{names} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
320
|
-
end
|
321
|
-
parse_multi_vagrant_up(result.stdout, update_specs)
|
322
|
-
end
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
def parse_vagrant_up(output, machine_spec)
|
327
|
-
# Grab forwarded port info
|
328
|
-
machine_spec.location['forwarded_ports'] = {}
|
329
|
-
in_forwarding_ports = false
|
330
|
-
output.lines.each do |line|
|
331
|
-
if in_forwarding_ports
|
332
|
-
if line =~ /-- (\d+) => (\d+)/
|
333
|
-
machine_spec.location['forwarded_ports'][$1] = $2
|
334
|
-
else
|
335
|
-
in_forwarding_ports = false
|
336
|
-
end
|
337
|
-
elsif line =~ /Forwarding ports...$/
|
338
|
-
in_forwarding_ports = true
|
339
|
-
end
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
def parse_multi_vagrant_up(output, all_machine_specs)
|
344
|
-
# Grab forwarded port info
|
345
|
-
in_forwarding_ports = {}
|
346
|
-
all_machine_specs.each_pair do |key, spec|
|
347
|
-
spec.location['forwarded_ports'] = {}
|
348
|
-
in_forwarding_ports[key] = false
|
349
|
-
end
|
350
|
-
output.lines.each do |line|
|
351
|
-
/^\[(.*?)\]/.match(line)
|
352
|
-
node_name = $1
|
353
|
-
if in_forwarding_ports[node_name]
|
354
|
-
if line =~ /-- (\d+) => (\d+)/
|
355
|
-
spec = all_machine_specs[node_name]
|
356
|
-
spec.location['forwarded_ports'][$1] = $2
|
357
|
-
else
|
358
|
-
in_forwarding_ports[node_name] = false
|
359
|
-
end
|
360
|
-
elsif line =~ /Forwarding ports...$/
|
361
|
-
in_forwarding_ports[node_name] = true
|
362
|
-
end
|
363
|
-
end
|
364
|
-
end
|
365
|
-
|
366
|
-
def machine_for(machine_spec, machine_options)
|
367
|
-
if machine_spec.location['vm.guest'].to_s == 'windows'
|
368
|
-
Chef::Provisioning::Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec),
|
369
|
-
convergence_strategy_for(machine_spec, machine_options))
|
370
|
-
else
|
371
|
-
Chef::Provisioning::Machine::UnixMachine.new(machine_spec, transport_for(machine_spec),
|
372
|
-
convergence_strategy_for(machine_spec, machine_options))
|
373
|
-
end
|
374
|
-
end
|
375
|
-
|
376
|
-
def convergence_strategy_for(machine_spec, machine_options)
|
377
|
-
if machine_spec.location['vm.guest'].to_s == 'windows'
|
378
|
-
Chef::Provisioning::ConvergenceStrategy::InstallMsi.
|
379
|
-
new(machine_options[:convergence_options], config)
|
380
|
-
else
|
381
|
-
Chef::Provisioning::ConvergenceStrategy::InstallCached.
|
382
|
-
new(machine_options[:convergence_options], config)
|
383
|
-
end
|
384
|
-
end
|
385
|
-
|
386
|
-
def transport_for(machine_spec)
|
387
|
-
if machine_spec.location['vm.guest'].to_s == 'windows'
|
388
|
-
create_winrm_transport(machine_spec)
|
389
|
-
else
|
390
|
-
create_ssh_transport(machine_spec)
|
391
|
-
end
|
392
|
-
end
|
393
|
-
|
394
|
-
def vagrant_status(name)
|
395
|
-
status_output = shell_out("vagrant status #{name}", :cwd => cluster_path).stdout
|
396
|
-
if status_output =~ /^#{name}\s+(.+)\s+\((.+)\)/
|
397
|
-
$1
|
398
|
-
else
|
399
|
-
'not created'
|
400
|
-
end
|
401
|
-
end
|
402
|
-
|
403
|
-
def create_winrm_transport(machine_spec)
|
404
|
-
forwarded_ports = machine_spec.location['forwarded_ports']
|
405
|
-
|
406
|
-
# TODO IPv6 loopback? What do we do for that?
|
407
|
-
hostname = machine_spec.location['winrm.host'] || '127.0.0.1'
|
408
|
-
port = machine_spec.location['winrm.port'] || 5985
|
409
|
-
port = forwarded_ports[port] if forwarded_ports[port]
|
410
|
-
endpoint = "http://#{hostname}:#{port}/wsman"
|
411
|
-
type = :plaintext
|
412
|
-
options = {
|
413
|
-
:user => machine_spec.location['winrm.username'] || 'vagrant',
|
414
|
-
:pass => machine_spec.location['winrm.password'] || 'vagrant',
|
415
|
-
:disable_sspi => true
|
416
|
-
}
|
417
|
-
|
418
|
-
Chef::Provisioning::Transport::WinRM.new(endpoint, type, options)
|
419
|
-
end
|
420
|
-
|
421
|
-
def create_ssh_transport(machine_spec)
|
422
|
-
vagrant_ssh_config = vagrant_ssh_config_for(machine_spec)
|
423
|
-
hostname = vagrant_ssh_config['HostName']
|
424
|
-
username = vagrant_ssh_config['User']
|
425
|
-
ssh_options = {
|
426
|
-
:port => vagrant_ssh_config['Port'],
|
427
|
-
:auth_methods => ['publickey'],
|
428
|
-
:user_known_hosts_file => vagrant_ssh_config['UserKnownHostsFile'],
|
429
|
-
:paranoid => yes_or_no(vagrant_ssh_config['StrictHostKeyChecking']),
|
430
|
-
:keys => [ strip_quotes(vagrant_ssh_config['IdentityFile']) ],
|
431
|
-
:keys_only => yes_or_no(vagrant_ssh_config['IdentitiesOnly'])
|
432
|
-
}
|
433
|
-
ssh_options[:auth_methods] = %w(password) if yes_or_no(vagrant_ssh_config['PasswordAuthentication'])
|
434
|
-
options = {
|
435
|
-
:prefix => 'sudo '
|
436
|
-
}
|
437
|
-
Chef::Provisioning::Transport::SSH.new(hostname, username, ssh_options, options, config)
|
438
|
-
end
|
439
|
-
|
440
|
-
def vagrant_ssh_config_for(machine_spec)
|
441
|
-
vagrant_ssh_config = {}
|
442
|
-
result = shell_out("vagrant ssh-config #{machine_spec.location['vm_name']}",
|
443
|
-
:cwd => cluster_path)
|
444
|
-
result.stdout.lines.inject({}) do |result, line|
|
445
|
-
line =~ /^\s*(\S+)\s+(.+?)(\r\n|\r|\n|\z)/
|
446
|
-
vagrant_ssh_config[$1] = $2
|
447
|
-
end
|
448
|
-
vagrant_ssh_config
|
449
|
-
end
|
450
|
-
|
451
|
-
def yes_or_no(str)
|
452
|
-
case str
|
453
|
-
when 'yes'
|
454
|
-
true
|
455
|
-
else
|
456
|
-
false
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
460
|
-
def strip_quotes(str)
|
461
|
-
if str[0] == '"' && str[-1] == '"' && str.size >= 2
|
462
|
-
str[1..-2]
|
463
|
-
else
|
464
|
-
str
|
465
|
-
end
|
466
|
-
end
|
467
|
-
end
|
468
|
-
end
|
469
|
-
end
|
470
|
-
end
|
1
|
+
require 'chef/mixin/shell_out'
|
2
|
+
require 'chef/provisioning/driver'
|
3
|
+
require 'chef/provisioning/machine/windows_machine'
|
4
|
+
require 'chef/provisioning/machine/unix_machine'
|
5
|
+
require 'chef/provisioning/convergence_strategy/install_msi'
|
6
|
+
require 'chef/provisioning/convergence_strategy/install_cached'
|
7
|
+
require 'chef/provisioning/transport/winrm'
|
8
|
+
require 'chef/provisioning/transport/ssh'
|
9
|
+
require 'chef/provisioning/vagrant_driver/version'
|
10
|
+
require 'chef/resource/vagrant_cluster'
|
11
|
+
require 'chef/provider/vagrant_cluster'
|
12
|
+
|
13
|
+
class Chef
|
14
|
+
module Provisioning
|
15
|
+
module VagrantDriver
|
16
|
+
# Provisions machines in vagrant.
|
17
|
+
class Driver < Chef::Provisioning::Driver
|
18
|
+
|
19
|
+
include Chef::Mixin::ShellOut
|
20
|
+
|
21
|
+
# Create a new vagrant driver.
|
22
|
+
#
|
23
|
+
# ## Parameters
|
24
|
+
# cluster_path - path to the directory containing the vagrant files, which
|
25
|
+
# should have been created with the vagrant_cluster resource.
|
26
|
+
def initialize(driver_url, config)
|
27
|
+
super
|
28
|
+
scheme, cluster_path = driver_url.split(':', 2)
|
29
|
+
@cluster_path = cluster_path
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :cluster_path
|
33
|
+
|
34
|
+
def self.from_url(driver_url, config)
|
35
|
+
Driver.new(driver_url, config)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.canonicalize_url(driver_url, config)
|
39
|
+
scheme, cluster_path = driver_url.split(':', 2)
|
40
|
+
cluster_path = File.expand_path(cluster_path || File.join(Chef::Config.config_dir, 'vms'))
|
41
|
+
"vagrant:#{cluster_path}"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Acquire a machine, generally by provisioning it. Returns a Machine
|
45
|
+
# object pointing at the machine, allowing useful actions like setup,
|
46
|
+
# converge, execute, file and directory.
|
47
|
+
def allocate_machine(action_handler, machine_spec, machine_options)
|
48
|
+
ensure_vagrant_cluster(action_handler)
|
49
|
+
vm_name = machine_spec.name
|
50
|
+
vm_file_path = File.join(cluster_path, "#{machine_spec.name}.vm")
|
51
|
+
vm_file_updated = create_vm_file(action_handler, vm_name, vm_file_path, machine_options)
|
52
|
+
if vm_file_updated || !machine_spec.location
|
53
|
+
old_location = machine_spec.location
|
54
|
+
machine_spec.location = {
|
55
|
+
'driver_url' => driver_url,
|
56
|
+
'driver_version' => Chef::Provisioning::VagrantDriver::VERSION,
|
57
|
+
'vm_name' => vm_name,
|
58
|
+
'vm_file_path' => vm_file_path,
|
59
|
+
'allocated_at' => Time.now.utc.to_s,
|
60
|
+
'host_node' => action_handler.host_node
|
61
|
+
}
|
62
|
+
machine_spec.location['needs_reload'] = true if vm_file_updated
|
63
|
+
if machine_options[:vagrant_options]
|
64
|
+
%w(vm.guest winrm.host winrm.port winrm.username winrm.password).each do |key|
|
65
|
+
machine_spec.location[key] = machine_options[:vagrant_options][key] if machine_options[:vagrant_options][key]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
machine_spec.location['chef_client_timeout'] = machine_options[:chef_client_timeout] if machine_options[:chef_client_timeout]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def ready_machine(action_handler, machine_spec, machine_options)
|
73
|
+
start_machine(action_handler, machine_spec, machine_options)
|
74
|
+
machine_for(machine_spec, machine_options)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Connect to machine without acquiring it
|
78
|
+
def connect_to_machine(machine_spec, machine_options)
|
79
|
+
machine_for(machine_spec, machine_options)
|
80
|
+
end
|
81
|
+
|
82
|
+
def destroy_machine(action_handler, machine_spec, machine_options)
|
83
|
+
if machine_spec.location
|
84
|
+
vm_name = machine_spec.location['vm_name']
|
85
|
+
current_status = vagrant_status(vm_name)
|
86
|
+
if current_status != 'not created'
|
87
|
+
action_handler.perform_action "run vagrant destroy -f #{vm_name} (status was '#{current_status}')" do
|
88
|
+
result = shell_out("vagrant destroy -f #{vm_name}", :cwd => cluster_path)
|
89
|
+
if result.exitstatus != 0
|
90
|
+
raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
convergence_strategy_for(machine_spec, machine_options).
|
96
|
+
cleanup_convergence(action_handler, machine_spec)
|
97
|
+
|
98
|
+
vm_file_path = machine_spec.location['vm_file_path']
|
99
|
+
Chef::Provisioning.inline_resource(action_handler) do
|
100
|
+
file vm_file_path do
|
101
|
+
action :delete
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def stop_machine(action_handler, machine_spec, machine_options)
|
108
|
+
if machine_spec.location
|
109
|
+
vm_name = machine_spec.location['vm_name']
|
110
|
+
current_status = vagrant_status(vm_name)
|
111
|
+
if current_status == 'running'
|
112
|
+
action_handler.perform_action "run vagrant halt #{vm_name} (status was '#{current_status}')" do
|
113
|
+
result = shell_out("vagrant halt #{vm_name}", :cwd => cluster_path)
|
114
|
+
if result.exitstatus != 0
|
115
|
+
raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def ready_machines(action_handler, specs_and_options, parallelizer)
|
123
|
+
start_machines(action_handler, specs_and_options)
|
124
|
+
machines = []
|
125
|
+
specs_and_options.each_pair do |spec, options|
|
126
|
+
machine = machine_for(spec, options)
|
127
|
+
machines << machine
|
128
|
+
yield machine if block_given?
|
129
|
+
end
|
130
|
+
machines
|
131
|
+
end
|
132
|
+
|
133
|
+
def destroy_machines(action_handler, specs_and_options, parallelizer)
|
134
|
+
all_names = []
|
135
|
+
all_status = []
|
136
|
+
all_outputs = {}
|
137
|
+
specs_and_options.each_key do |spec|
|
138
|
+
if spec.location
|
139
|
+
vm_name = spec.location['vm_name']
|
140
|
+
current_status = vagrant_status(vm_name)
|
141
|
+
if current_status != 'not created'
|
142
|
+
all_names.push(vm_name)
|
143
|
+
all_status.push(current_status)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
if all_names.length > 0
|
148
|
+
names = all_names.join(" ")
|
149
|
+
statuses = all_status.join(", ")
|
150
|
+
action_handler.perform_action "run vagrant destroy -f #{names} (status was '#{statuses}')" do
|
151
|
+
result = shell_out("vagrant destroy -f #{names}", :cwd => cluster_path)
|
152
|
+
if result.exitstatus != 0
|
153
|
+
raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
specs_and_options.each_pair do |spec, options|
|
158
|
+
convergence_strategy_for(spec, options).cleanup_convergence(action_handler, spec)
|
159
|
+
|
160
|
+
vm_file_path = spec.location['vm_file_path']
|
161
|
+
Chef::Provisioning.inline_resource(action_handler) do
|
162
|
+
file vm_file_path do
|
163
|
+
action :delete
|
164
|
+
end
|
165
|
+
end
|
166
|
+
yield spec if block_given?
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def stop_machines(action_handler, specs_and_options, parallelizer)
|
171
|
+
all_names = []
|
172
|
+
specs_and_options.each_key do |spec|
|
173
|
+
if spec.location
|
174
|
+
vm_name = spec.location['vm_name']
|
175
|
+
current_status = vagrant_status(vm_name)
|
176
|
+
if current_status == 'running'
|
177
|
+
all_names.push(vm_name)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
if all_names.length > 0
|
182
|
+
names = all_names.join(" ")
|
183
|
+
action_handler.perform_action "run vagrant halt #{names} (status was 'running')" do
|
184
|
+
result = shell_out("vagrant halt #{names}", :cwd => cluster_path)
|
185
|
+
if result.exitstatus != 0
|
186
|
+
raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Used by vagrant_cluster and machine to get the string used to configure vagrant
|
193
|
+
def self.vagrant_config_string(vagrant_config, variable, line_prefix)
|
194
|
+
hostname = name.gsub(/[^A-Za-z0-9\-]/, '-')
|
195
|
+
result = ''
|
196
|
+
vagrant_config.each_pair do |key, value|
|
197
|
+
result += "#{line_prefix}#{variable}.#{key} = #{value.inspect}\n"
|
198
|
+
end
|
199
|
+
result
|
200
|
+
end
|
201
|
+
|
202
|
+
def driver_url
|
203
|
+
"vagrant:#{cluster_path}"
|
204
|
+
end
|
205
|
+
|
206
|
+
protected
|
207
|
+
|
208
|
+
def ensure_vagrant_cluster(action_handler)
|
209
|
+
_cluster_path = cluster_path
|
210
|
+
Chef::Provisioning.inline_resource(action_handler) do
|
211
|
+
vagrant_cluster _cluster_path
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def create_vm_file(action_handler, vm_name, vm_file_path, machine_options)
|
216
|
+
# Determine contents of vm file
|
217
|
+
vm_file_content = "Vagrant.configure('2') do |outer_config|\n"
|
218
|
+
vm_file_content << " outer_config.vm.define #{vm_name.inspect} do |config|\n"
|
219
|
+
merged_vagrant_options = { 'vm.hostname' => vm_name }
|
220
|
+
if machine_options[:vagrant_options]
|
221
|
+
merged_vagrant_options = Cheffish::MergedConfig.new(machine_options[:vagrant_options], merged_vagrant_options)
|
222
|
+
end
|
223
|
+
merged_vagrant_options.each_pair do |key, value|
|
224
|
+
if key == 'vm.network'
|
225
|
+
vm_file_content << " config.#{key}(#{value})\n"
|
226
|
+
else
|
227
|
+
vm_file_content << " config.#{key} = #{value.inspect}\n"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
vm_file_content << machine_options[:vagrant_config] if machine_options[:vagrant_config]
|
231
|
+
vm_file_content << " end\nend\n"
|
232
|
+
|
233
|
+
# Set up vagrant file
|
234
|
+
Chef::Provisioning.inline_resource(action_handler) do
|
235
|
+
file vm_file_path do
|
236
|
+
content vm_file_content
|
237
|
+
action :create
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def start_machine(action_handler, machine_spec, machine_options)
|
243
|
+
vm_name = machine_spec.location['vm_name']
|
244
|
+
vm_provider = machine_options.has_key?(:vagrant_provider) ? machine_options[:vagrant_provider] : 'virtualbox'
|
245
|
+
up_timeout = machine_options[:up_timeout] || 10*60
|
246
|
+
current_status = vagrant_status(vm_name)
|
247
|
+
vm_file_updated = machine_spec.location['needs_reload']
|
248
|
+
machine_spec.location['needs_reload'] = false
|
249
|
+
if current_status != 'running'
|
250
|
+
# Run vagrant up if vm is not running
|
251
|
+
action_handler.perform_action "run vagrant up #{vm_name} --provider #{vm_provider} (status was '#{current_status}')" do
|
252
|
+
result = shell_out("vagrant up #{vm_name} --provider #{vm_provider}", :cwd => cluster_path,
|
253
|
+
:timeout => up_timeout)
|
254
|
+
if result.exitstatus != 0
|
255
|
+
raise "vagrant up #{vm_name} --provider #{vm_provider} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
256
|
+
end
|
257
|
+
parse_vagrant_up(result.stdout, machine_spec)
|
258
|
+
end
|
259
|
+
elsif vm_file_updated
|
260
|
+
# Run vagrant reload if vm is running and vm file changed
|
261
|
+
action_handler.perform_action "run vagrant reload #{vm_name}" do
|
262
|
+
result = shell_out("vagrant reload #{vm_name}", :cwd => cluster_path,
|
263
|
+
:timeout => up_timeout)
|
264
|
+
if result.exitstatus != 0
|
265
|
+
raise "vagrant reload #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
266
|
+
end
|
267
|
+
parse_vagrant_up(result.stdout, machine_spec)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def start_machines(action_handler, specs_and_options)
|
273
|
+
up_names = []
|
274
|
+
up_status = []
|
275
|
+
up_specs = {}
|
276
|
+
update_names = []
|
277
|
+
update_specs = {}
|
278
|
+
timeouts = []
|
279
|
+
specs_and_options.each_pair do |spec, options|
|
280
|
+
vm_name = spec.location['vm_name']
|
281
|
+
|
282
|
+
vm_file_updated = spec.location['needs_reload']
|
283
|
+
spec.location['needs_reload'] = false
|
284
|
+
|
285
|
+
current_status = vagrant_status(vm_name)
|
286
|
+
if current_status != 'running'
|
287
|
+
up_names.push(vm_name)
|
288
|
+
up_status.push(current_status)
|
289
|
+
up_specs[vm_name] = spec
|
290
|
+
elsif vm_file_updated
|
291
|
+
update_names.push(vm_name)
|
292
|
+
update_specs[vm_name] = spec
|
293
|
+
end
|
294
|
+
timeouts.push(options[:up_timeout])
|
295
|
+
end
|
296
|
+
# Use the highest timeout, if any exist
|
297
|
+
up_timeout = timeouts.compact.max
|
298
|
+
up_timeout ||= 10*60
|
299
|
+
if up_names.length > 0
|
300
|
+
# Run vagrant up if vm is not running
|
301
|
+
names = up_names.join(" ")
|
302
|
+
statuses = up_status.join(", ")
|
303
|
+
action_handler.perform_action "run vagrant up --parallel #{names} (status was '#{statuses}')" do
|
304
|
+
result = shell_out("vagrant up --parallel #{names}", :cwd => cluster_path,
|
305
|
+
:timeout => up_timeout)
|
306
|
+
if result.exitstatus != 0
|
307
|
+
raise "vagrant up #{names} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
308
|
+
end
|
309
|
+
parse_multi_vagrant_up(result.stdout, up_specs)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
if update_names.length > 0
|
313
|
+
names = update_names.join(" ")
|
314
|
+
# Run vagrant reload if vm is running and vm file changed
|
315
|
+
action_handler.perform_action "run vagrant reload #{names}" do
|
316
|
+
result = shell_out("vagrant reload #{names}", :cwd => cluster_path,
|
317
|
+
:timeout => up_timeout)
|
318
|
+
if result.exitstatus != 0
|
319
|
+
raise "vagrant reload #{names} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
|
320
|
+
end
|
321
|
+
parse_multi_vagrant_up(result.stdout, update_specs)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def parse_vagrant_up(output, machine_spec)
|
327
|
+
# Grab forwarded port info
|
328
|
+
machine_spec.location['forwarded_ports'] = {}
|
329
|
+
in_forwarding_ports = false
|
330
|
+
output.lines.each do |line|
|
331
|
+
if in_forwarding_ports
|
332
|
+
if line =~ /-- (\d+) => (\d+)/
|
333
|
+
machine_spec.location['forwarded_ports'][$1] = $2
|
334
|
+
else
|
335
|
+
in_forwarding_ports = false
|
336
|
+
end
|
337
|
+
elsif line =~ /Forwarding ports...$/
|
338
|
+
in_forwarding_ports = true
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def parse_multi_vagrant_up(output, all_machine_specs)
|
344
|
+
# Grab forwarded port info
|
345
|
+
in_forwarding_ports = {}
|
346
|
+
all_machine_specs.each_pair do |key, spec|
|
347
|
+
spec.location['forwarded_ports'] = {}
|
348
|
+
in_forwarding_ports[key] = false
|
349
|
+
end
|
350
|
+
output.lines.each do |line|
|
351
|
+
/^\[(.*?)\]/.match(line)
|
352
|
+
node_name = $1
|
353
|
+
if in_forwarding_ports[node_name]
|
354
|
+
if line =~ /-- (\d+) => (\d+)/
|
355
|
+
spec = all_machine_specs[node_name]
|
356
|
+
spec.location['forwarded_ports'][$1] = $2
|
357
|
+
else
|
358
|
+
in_forwarding_ports[node_name] = false
|
359
|
+
end
|
360
|
+
elsif line =~ /Forwarding ports...$/
|
361
|
+
in_forwarding_ports[node_name] = true
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def machine_for(machine_spec, machine_options)
|
367
|
+
if machine_spec.location['vm.guest'].to_s == 'windows'
|
368
|
+
Chef::Provisioning::Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec),
|
369
|
+
convergence_strategy_for(machine_spec, machine_options))
|
370
|
+
else
|
371
|
+
Chef::Provisioning::Machine::UnixMachine.new(machine_spec, transport_for(machine_spec),
|
372
|
+
convergence_strategy_for(machine_spec, machine_options))
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def convergence_strategy_for(machine_spec, machine_options)
|
377
|
+
if machine_spec.location['vm.guest'].to_s == 'windows'
|
378
|
+
Chef::Provisioning::ConvergenceStrategy::InstallMsi.
|
379
|
+
new(machine_options[:convergence_options], config)
|
380
|
+
else
|
381
|
+
Chef::Provisioning::ConvergenceStrategy::InstallCached.
|
382
|
+
new(machine_options[:convergence_options], config)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
def transport_for(machine_spec)
|
387
|
+
if machine_spec.location['vm.guest'].to_s == 'windows'
|
388
|
+
create_winrm_transport(machine_spec)
|
389
|
+
else
|
390
|
+
create_ssh_transport(machine_spec)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def vagrant_status(name)
|
395
|
+
status_output = shell_out("vagrant status #{name}", :cwd => cluster_path).stdout
|
396
|
+
if status_output =~ /^#{name}\s+(.+)\s+\((.+)\)/
|
397
|
+
$1
|
398
|
+
else
|
399
|
+
'not created'
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def create_winrm_transport(machine_spec)
|
404
|
+
forwarded_ports = machine_spec.location['forwarded_ports']
|
405
|
+
|
406
|
+
# TODO IPv6 loopback? What do we do for that?
|
407
|
+
hostname = machine_spec.location['winrm.host'] || '127.0.0.1'
|
408
|
+
port = machine_spec.location['winrm.port'] || 5985
|
409
|
+
port = forwarded_ports[port] if forwarded_ports[port]
|
410
|
+
endpoint = "http://#{hostname}:#{port}/wsman"
|
411
|
+
type = :plaintext
|
412
|
+
options = {
|
413
|
+
:user => machine_spec.location['winrm.username'] || 'vagrant',
|
414
|
+
:pass => machine_spec.location['winrm.password'] || 'vagrant',
|
415
|
+
:disable_sspi => true
|
416
|
+
}
|
417
|
+
|
418
|
+
Chef::Provisioning::Transport::WinRM.new(endpoint, type, options)
|
419
|
+
end
|
420
|
+
|
421
|
+
def create_ssh_transport(machine_spec)
|
422
|
+
vagrant_ssh_config = vagrant_ssh_config_for(machine_spec)
|
423
|
+
hostname = vagrant_ssh_config['HostName']
|
424
|
+
username = vagrant_ssh_config['User']
|
425
|
+
ssh_options = {
|
426
|
+
:port => vagrant_ssh_config['Port'],
|
427
|
+
:auth_methods => ['publickey'],
|
428
|
+
:user_known_hosts_file => vagrant_ssh_config['UserKnownHostsFile'],
|
429
|
+
:paranoid => yes_or_no(vagrant_ssh_config['StrictHostKeyChecking']),
|
430
|
+
:keys => [ strip_quotes(vagrant_ssh_config['IdentityFile']) ],
|
431
|
+
:keys_only => yes_or_no(vagrant_ssh_config['IdentitiesOnly'])
|
432
|
+
}
|
433
|
+
ssh_options[:auth_methods] = %w(password) if yes_or_no(vagrant_ssh_config['PasswordAuthentication'])
|
434
|
+
options = {
|
435
|
+
:prefix => 'sudo '
|
436
|
+
}
|
437
|
+
Chef::Provisioning::Transport::SSH.new(hostname, username, ssh_options, options, config)
|
438
|
+
end
|
439
|
+
|
440
|
+
def vagrant_ssh_config_for(machine_spec)
|
441
|
+
vagrant_ssh_config = {}
|
442
|
+
result = shell_out("vagrant ssh-config #{machine_spec.location['vm_name']}",
|
443
|
+
:cwd => cluster_path)
|
444
|
+
result.stdout.lines.inject({}) do |result, line|
|
445
|
+
line =~ /^\s*(\S+)\s+(.+?)(\r\n|\r|\n|\z)/
|
446
|
+
vagrant_ssh_config[$1] = $2
|
447
|
+
end
|
448
|
+
vagrant_ssh_config
|
449
|
+
end
|
450
|
+
|
451
|
+
def yes_or_no(str)
|
452
|
+
case str
|
453
|
+
when 'yes'
|
454
|
+
true
|
455
|
+
else
|
456
|
+
false
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def strip_quotes(str)
|
461
|
+
if str[0] == '"' && str[-1] == '"' && str.size >= 2
|
462
|
+
str[1..-2]
|
463
|
+
else
|
464
|
+
str
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|