chef-provisioning 0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +207 -0
- data/LICENSE +201 -0
- data/README.md +260 -0
- data/Rakefile +6 -0
- data/lib/chef/provider/load_balancer.rb +77 -0
- data/lib/chef/provider/machine.rb +176 -0
- data/lib/chef/provider/machine_batch.rb +191 -0
- data/lib/chef/provider/machine_execute.rb +35 -0
- data/lib/chef/provider/machine_file.rb +54 -0
- data/lib/chef/provider/machine_image.rb +60 -0
- data/lib/chef/provisioning.rb +95 -0
- data/lib/chef/provisioning/action_handler.rb +68 -0
- data/lib/chef/provisioning/add_prefix_action_handler.rb +31 -0
- data/lib/chef/provisioning/chef_image_spec.rb +108 -0
- data/lib/chef/provisioning/chef_load_balancer_spec.rb +108 -0
- data/lib/chef/provisioning/chef_machine_spec.rb +84 -0
- data/lib/chef/provisioning/chef_provider_action_handler.rb +74 -0
- data/lib/chef/provisioning/chef_run_data.rb +139 -0
- data/lib/chef/provisioning/convergence_strategy.rb +28 -0
- data/lib/chef/provisioning/convergence_strategy/install_cached.rb +156 -0
- data/lib/chef/provisioning/convergence_strategy/install_msi.rb +58 -0
- data/lib/chef/provisioning/convergence_strategy/install_sh.rb +55 -0
- data/lib/chef/provisioning/convergence_strategy/no_converge.rb +39 -0
- data/lib/chef/provisioning/convergence_strategy/precreate_chef_objects.rb +183 -0
- data/lib/chef/provisioning/driver.rb +304 -0
- data/lib/chef/provisioning/image_spec.rb +72 -0
- data/lib/chef/provisioning/load_balancer_spec.rb +86 -0
- data/lib/chef/provisioning/machine.rb +112 -0
- data/lib/chef/provisioning/machine/basic_machine.rb +84 -0
- data/lib/chef/provisioning/machine/unix_machine.rb +278 -0
- data/lib/chef/provisioning/machine/windows_machine.rb +104 -0
- data/lib/chef/provisioning/machine_spec.rb +82 -0
- data/lib/chef/provisioning/recipe_dsl.rb +103 -0
- data/lib/chef/provisioning/transport.rb +95 -0
- data/lib/chef/provisioning/transport/ssh.rb +343 -0
- data/lib/chef/provisioning/transport/winrm.rb +151 -0
- data/lib/chef/provisioning/version.rb +5 -0
- data/lib/chef/resource/chef_data_bag_resource.rb +148 -0
- data/lib/chef/resource/load_balancer.rb +57 -0
- data/lib/chef/resource/machine.rb +124 -0
- data/lib/chef/resource/machine_batch.rb +78 -0
- data/lib/chef/resource/machine_execute.rb +28 -0
- data/lib/chef/resource/machine_file.rb +34 -0
- data/lib/chef/resource/machine_image.rb +35 -0
- data/lib/chef_metal.rb +1 -0
- metadata +217 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'chef/provisioning/convergence_strategy/precreate_chef_objects'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
class Chef
|
5
|
+
module Provisioning
|
6
|
+
class ConvergenceStrategy
|
7
|
+
class InstallMsi < PrecreateChefObjects
|
8
|
+
@@install_msi_cache = {}
|
9
|
+
|
10
|
+
def initialize(convergence_options, config)
|
11
|
+
super
|
12
|
+
@install_msi_url = convergence_options[:install_msi_url] || 'http://www.opscode.com/chef/install.msi'
|
13
|
+
@install_msi_path = convergence_options[:install_msi_path] || "$env:TEMP\\#{File.basename(@install_msi_url)}"
|
14
|
+
@chef_client_timeout = convergence_options.has_key?(:chef_client_timeout) ? convergence_options[:chef_client_timeout] : 120*60 # Default: 2 hours
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :install_msi_url
|
18
|
+
attr_reader :install_msi_path
|
19
|
+
|
20
|
+
def setup_convergence(action_handler, machine)
|
21
|
+
if !convergence_options.has_key?(:client_rb_path) || !convergence_options.has_key?(:client_pem_path)
|
22
|
+
system_drive = machine.execute_always('$env:SystemDrive').stdout.strip
|
23
|
+
@convergence_options = Cheffish::MergedConfig.new(convergence_options, {
|
24
|
+
:client_rb_path => "#{system_drive}\\chef\\client.rb",
|
25
|
+
:client_pem_path => "#{system_drive}\\chef\\client.pem"
|
26
|
+
})
|
27
|
+
end
|
28
|
+
|
29
|
+
super
|
30
|
+
|
31
|
+
# Install chef-client. TODO check and update version if not latest / not desired
|
32
|
+
if machine.execute_always('chef-client -v').exitstatus != 0
|
33
|
+
# TODO ssh verification of install.msi before running arbtrary code would be nice?
|
34
|
+
# TODO find a way to cache this on the host like with the Unix stuff.
|
35
|
+
# Limiter is we don't know how to efficiently upload large files to
|
36
|
+
# the remote machine with WMI.
|
37
|
+
machine.execute(action_handler, "(New-Object System.Net.WebClient).DownloadFile(#{machine.escape(install_msi_url)}, #{machine.escape(install_msi_path)})")
|
38
|
+
machine.execute(action_handler, "msiexec /qn /i #{machine.escape(install_msi_path)}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def converge(action_handler, machine)
|
43
|
+
super
|
44
|
+
|
45
|
+
# TODO For some reason I get a 500 back if I don't do -l debug
|
46
|
+
action_handler.open_stream(machine.node['name']) do |stdout|
|
47
|
+
action_handler.open_stream(machine.node['name']) do |stderr|
|
48
|
+
machine.execute(action_handler, "chef-client -l debug",
|
49
|
+
:stream_stdout => stdout,
|
50
|
+
:stream_stderr => stderr,
|
51
|
+
:timeout => @chef_client_timeout)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'chef/provisioning/convergence_strategy/precreate_chef_objects'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
class Chef
|
5
|
+
module Provisioning
|
6
|
+
class ConvergenceStrategy
|
7
|
+
class InstallSh < PrecreateChefObjects
|
8
|
+
@@install_sh_cache = {}
|
9
|
+
|
10
|
+
def initialize(convergence_options, config)
|
11
|
+
convergence_options = Cheffish::MergedConfig.new(convergence_options, {
|
12
|
+
:client_rb_path => '/etc/chef/client.rb',
|
13
|
+
:client_pem_path => '/etc/chef/client.pem'
|
14
|
+
})
|
15
|
+
super(convergence_options, config)
|
16
|
+
@install_sh_url = convergence_options[:install_sh_url] || 'http://www.opscode.com/chef/install.sh'
|
17
|
+
@install_sh_path = convergence_options[:install_sh_path] || '/tmp/chef-install.sh'
|
18
|
+
@bootstrap_env = convergence_options[:bootstrap_proxy] ? "http_proxy=#{convergence_options[:bootstrap_proxy]}" : ""
|
19
|
+
@chef_client_timeout = convergence_options.has_key?(:chef_client_timeout) ? convergence_options[:chef_client_timeout] : 120*60 # Default: 2 hours
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :install_sh_url
|
23
|
+
attr_reader :install_sh_path
|
24
|
+
attr_reader :bootstrap_env
|
25
|
+
|
26
|
+
def setup_convergence(action_handler, machine)
|
27
|
+
super
|
28
|
+
|
29
|
+
# Install chef-client. TODO check and update version if not latest / not desired
|
30
|
+
if machine.execute_always('chef-client -v').exitstatus != 0
|
31
|
+
# TODO ssh verification of install.sh before running arbtrary code would be nice?
|
32
|
+
@@install_sh_cache[install_sh_url] ||= Net::HTTP.get(URI(install_sh_url))
|
33
|
+
machine.write_file(action_handler, install_sh_path, @@install_sh_cache[install_sh_url], :ensure_dir => true)
|
34
|
+
machine.execute(action_handler, "bash -c '#{bootstrap_env} bash #{install_sh_path}'")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def converge(action_handler, machine)
|
39
|
+
super
|
40
|
+
|
41
|
+
action_handler.open_stream(machine.node['name']) do |stdout|
|
42
|
+
action_handler.open_stream(machine.node['name']) do |stderr|
|
43
|
+
command_line = "chef-client"
|
44
|
+
command_line << " -l #{config[:log_level].to_s}" if config[:log_level]
|
45
|
+
machine.execute(action_handler, command_line,
|
46
|
+
:stream_stdout => stdout,
|
47
|
+
:stream_stderr => stderr,
|
48
|
+
:timeout => @chef_client_timeout)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'chef/provisioning/convergence_strategy'
|
2
|
+
require 'pathname'
|
3
|
+
require 'cheffish'
|
4
|
+
|
5
|
+
class Chef
|
6
|
+
module Provisioning
|
7
|
+
class ConvergenceStrategy
|
8
|
+
class NoConverge < ConvergenceStrategy
|
9
|
+
def initialize(convergence_options, config)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def chef_server
|
14
|
+
@chef_server ||= convergence_options[:chef_server] || Cheffish.default_chef_server(config)
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_convergence(action_handler, machine)
|
18
|
+
end
|
19
|
+
|
20
|
+
def converge(action_handler, machine)
|
21
|
+
end
|
22
|
+
|
23
|
+
def cleanup_convergence(action_handler, machine_spec)
|
24
|
+
_self = self
|
25
|
+
Chef::Provisioning.inline_resource(action_handler) do
|
26
|
+
chef_node machine_spec.name do
|
27
|
+
chef_server _self.chef_server
|
28
|
+
action :delete
|
29
|
+
end
|
30
|
+
chef_client machine_spec.name do
|
31
|
+
chef_server _self.chef_server
|
32
|
+
action :delete
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'chef/provisioning/convergence_strategy'
|
2
|
+
require 'pathname'
|
3
|
+
require 'cheffish'
|
4
|
+
|
5
|
+
class Chef
|
6
|
+
module Provisioning
|
7
|
+
class ConvergenceStrategy
|
8
|
+
class PrecreateChefObjects < ConvergenceStrategy
|
9
|
+
def initialize(convergence_options, config)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def chef_server
|
14
|
+
@chef_server ||= convergence_options[:chef_server] || Cheffish.default_chef_server(config)
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_convergence(action_handler, machine)
|
18
|
+
# Create keys on machine
|
19
|
+
public_key = create_keys(action_handler, machine)
|
20
|
+
# Create node and client on chef server
|
21
|
+
create_chef_objects(action_handler, machine, public_key)
|
22
|
+
|
23
|
+
# If the chef server lives on localhost, tunnel the port through to the guest
|
24
|
+
# (we need to know what got tunneled!)
|
25
|
+
chef_server_url = chef_server[:chef_server_url]
|
26
|
+
chef_server_url = machine.make_url_available_to_remote(chef_server_url)
|
27
|
+
|
28
|
+
# Support for multiple ohai hints, required on some platforms
|
29
|
+
create_ohai_files(action_handler, machine)
|
30
|
+
|
31
|
+
# Create client.rb and client.pem on machine
|
32
|
+
content = client_rb_content(chef_server_url, machine.node['name'])
|
33
|
+
machine.write_file(action_handler, convergence_options[:client_rb_path], content, :ensure_dir => true)
|
34
|
+
end
|
35
|
+
|
36
|
+
def converge(action_handler, machine)
|
37
|
+
machine.make_url_available_to_remote(chef_server[:chef_server_url])
|
38
|
+
end
|
39
|
+
|
40
|
+
def cleanup_convergence(action_handler, machine_spec)
|
41
|
+
_self = self
|
42
|
+
Chef::Provisioning.inline_resource(action_handler) do
|
43
|
+
chef_node machine_spec.name do
|
44
|
+
chef_server _self.chef_server
|
45
|
+
action :delete
|
46
|
+
end
|
47
|
+
chef_client machine_spec.name do
|
48
|
+
chef_server _self.chef_server
|
49
|
+
action :delete
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def create_keys(action_handler, machine)
|
57
|
+
server_private_key = machine.read_file(convergence_options[:client_pem_path])
|
58
|
+
if server_private_key
|
59
|
+
begin
|
60
|
+
server_private_key, format = Cheffish::KeyFormatter.decode(server_private_key)
|
61
|
+
rescue
|
62
|
+
server_private_key = nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
if server_private_key
|
67
|
+
if source_key && server_private_key.to_pem != source_key.to_pem
|
68
|
+
# If the server private key does not match our source key, overwrite it
|
69
|
+
server_private_key = source_key
|
70
|
+
if convergence_options[:allow_overwrite_keys]
|
71
|
+
machine.write_file(action_handler, convergence_options[:client_pem_path], server_private_key.to_pem, :ensure_dir => true)
|
72
|
+
else
|
73
|
+
raise "Private key on machine #{machine.name} does not match desired input key."
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
else
|
78
|
+
|
79
|
+
# If the server does not already have keys, create them and upload
|
80
|
+
_convergence_options = convergence_options
|
81
|
+
Chef::Provisioning.inline_resource(action_handler) do
|
82
|
+
private_key 'in_memory' do
|
83
|
+
path :none
|
84
|
+
if _convergence_options[:private_key_options]
|
85
|
+
_convergence_options[:private_key_options].each_pair do |key,value|
|
86
|
+
send(key, value)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
after { |resource, private_key| server_private_key = private_key }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
machine.write_file(action_handler, convergence_options[:client_pem_path], server_private_key.to_pem, :ensure_dir => true)
|
94
|
+
end
|
95
|
+
|
96
|
+
server_private_key.public_key
|
97
|
+
end
|
98
|
+
|
99
|
+
def is_localhost(host)
|
100
|
+
host == '127.0.0.1' || host == 'localhost' || host == '[::1]'
|
101
|
+
end
|
102
|
+
|
103
|
+
def source_key
|
104
|
+
if convergence_options[:source_key].is_a?(String)
|
105
|
+
key, format = Cheffish::KeyFormatter.decode(convergence_options[:source_key], convergence_options[:source_key_pass_phrase])
|
106
|
+
key
|
107
|
+
elsif convergence_options[:source_key]
|
108
|
+
convergence_options[:source_key]
|
109
|
+
elsif convergence_options[:source_key_path]
|
110
|
+
key, format = Cheffish::KeyFormatter.decode(IO.read(convergence_options[:source_key_path]), convergence_options[:source_key_pass_phrase], convergence_options[:source_key_path])
|
111
|
+
key
|
112
|
+
else
|
113
|
+
nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Create the ohai file(s)
|
118
|
+
def create_ohai_files(action_handler, machine)
|
119
|
+
if convergence_options[:ohai_hints]
|
120
|
+
convergence_options[:ohai_hints].each_pair do |hint, data|
|
121
|
+
# The location of the ohai hint
|
122
|
+
ohai_hint = "/etc/chef/ohai/hints/#{hint}.json"
|
123
|
+
machine.write_file(action_handler, ohai_hint, data.to_json, :ensure_dir => true)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def create_chef_objects(action_handler, machine, public_key)
|
129
|
+
_convergence_options = convergence_options
|
130
|
+
_chef_server = chef_server
|
131
|
+
# Save the node and create the client keys and client.
|
132
|
+
Chef::Provisioning.inline_resource(action_handler) do
|
133
|
+
# Create client
|
134
|
+
chef_client machine.name do
|
135
|
+
chef_server _chef_server
|
136
|
+
source_key public_key
|
137
|
+
output_key_path _convergence_options[:public_key_path]
|
138
|
+
output_key_format _convergence_options[:public_key_format]
|
139
|
+
admin _convergence_options[:admin]
|
140
|
+
validator _convergence_options[:validator]
|
141
|
+
end
|
142
|
+
|
143
|
+
# Create node
|
144
|
+
# TODO strip automatic attributes first so we don't race with "current state"
|
145
|
+
chef_node machine.name do
|
146
|
+
chef_server _chef_server
|
147
|
+
raw_json machine.node
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# If using enterprise/hosted chef, fix acls
|
152
|
+
if chef_server[:chef_server_url] =~ /\/+organizations\/.+/
|
153
|
+
grant_client_node_permissions(action_handler, chef_server, machine.name, ["read", "update"])
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Grant the client permissions to the node
|
158
|
+
# This procedure assumes that the client name and node name are the same
|
159
|
+
def grant_client_node_permissions(action_handler, chef_server, node_name, perms)
|
160
|
+
api = Cheffish.chef_server_api(chef_server)
|
161
|
+
node_perms = api.get("/nodes/#{node_name}/_acl")
|
162
|
+
perms.each do |p|
|
163
|
+
if !node_perms[p]['actors'].include?(node_name)
|
164
|
+
action_handler.perform_action "Add #{node_name} to client #{p} ACLs" do
|
165
|
+
node_perms[p]['actors'] << node_name
|
166
|
+
api.put("/nodes/#{node_name}/_acl/#{p}", p => node_perms[p])
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def client_rb_content(chef_server_url, node_name)
|
173
|
+
<<EOM
|
174
|
+
chef_server_url #{chef_server_url.inspect}
|
175
|
+
node_name #{node_name.inspect}
|
176
|
+
client_key #{convergence_options[:client_pem_path].inspect}
|
177
|
+
ssl_verify_mode :verify_peer
|
178
|
+
EOM
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
class Chef
|
2
|
+
module Provisioning
|
3
|
+
#
|
4
|
+
# A Driver instance represents a place where machines can be created and found,
|
5
|
+
# and contains methods to create, delete, start, stop, and find them.
|
6
|
+
#
|
7
|
+
# For AWS, a Driver instance corresponds to a single account.
|
8
|
+
# For Vagrant, it is a directory where VM files are found.
|
9
|
+
#
|
10
|
+
# = How to Make a Driver
|
11
|
+
#
|
12
|
+
# To implement a Driver, you must implement the following methods:
|
13
|
+
#
|
14
|
+
# * initialize(driver_url) - create a new driver with the given URL
|
15
|
+
# * driver_url - a URL representing everything unique about your driver. (NOT credentials)
|
16
|
+
# * allocate_machine - ask the driver to allocate a machine to you.
|
17
|
+
# * ready_machine - get the machine "ready" - wait for it to be booted and accessible (for example, accessible via SSH transport).
|
18
|
+
# * stop_machine - stop the machine.
|
19
|
+
# * destroy_machine - delete the machine.
|
20
|
+
# * connect_to_machine - connect to the given machine.
|
21
|
+
#
|
22
|
+
# Optionally, you can also implement:
|
23
|
+
# * allocate_machines - allocate an entire group of machines.
|
24
|
+
# * ready_machines - get a group of machines warm and booted.
|
25
|
+
# * stop_machines - stop a group of machines.
|
26
|
+
# * destroy_machines - delete a group of machines.
|
27
|
+
#
|
28
|
+
# Additionally, you must create a file named `chef/provisioning/driver_init/<scheme>.rb`,
|
29
|
+
# where <scheme> is the name of the scheme you chose for your driver_url. This
|
30
|
+
# file, when required, must call Chef::Provisioning.add_registered_driver(<scheme>, <class>).
|
31
|
+
# The given <class>.from_url(url, config) will be called with a driver_url and
|
32
|
+
# configuration.
|
33
|
+
#
|
34
|
+
# All of these methods must be idempotent - if the work is already done, they
|
35
|
+
# just don't do anything.
|
36
|
+
#
|
37
|
+
class Driver
|
38
|
+
#
|
39
|
+
# Inflate a driver from a driver URL.
|
40
|
+
#
|
41
|
+
#
|
42
|
+
# @param [String] driver_url the URL to inflate the driver
|
43
|
+
# config - a configuration hash. See "config" for a list of known keys.
|
44
|
+
#
|
45
|
+
# == Returns
|
46
|
+
# A Driver representing the given driver_url.
|
47
|
+
#
|
48
|
+
def initialize(driver_url, config)
|
49
|
+
@driver_url = driver_url
|
50
|
+
@config = config
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Override this on specific driver classes
|
55
|
+
#
|
56
|
+
def self.from_url(driver_url, config)
|
57
|
+
Chef::Provisioning.from_url(driver_url, config)
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# A URL representing the driver and the place where machines come from.
|
62
|
+
# This will be stuffed in machine_spec.location['driver_url'] so that the
|
63
|
+
# machine can be re-inflated. URLs must have a unique scheme identifying the
|
64
|
+
# driver class, and enough information to identify the place where created
|
65
|
+
# machines can be found. For AWS, this is the account number; for lxc and
|
66
|
+
# vagrant, it is the directory in which VMs and containers are.
|
67
|
+
#
|
68
|
+
# For example:
|
69
|
+
# - fog:AWS:123456789012
|
70
|
+
# - vagrant:/var/vms
|
71
|
+
# - lxc:
|
72
|
+
# - docker:
|
73
|
+
#
|
74
|
+
attr_reader :driver_url
|
75
|
+
|
76
|
+
# A configuration hash. These keys may be present:
|
77
|
+
# - :driver_options: a driver-defined object containing driver config.
|
78
|
+
# - :private_keys: a hash of private keys, with a "name" and a "value". Values are either strings (paths) or PrivateKey objects.
|
79
|
+
# - :private_key_paths: a list of paths to directories containing private keys.
|
80
|
+
# - :write_private_key_path: the path to which we write new keys by default.
|
81
|
+
# - :log_level: :debug/:info/:warn/:error/:fatal
|
82
|
+
# - :chef_server_url: url to chef server
|
83
|
+
# - :node_name: username to talk to chef server
|
84
|
+
# - :client_key: path to key used to talk to chef server
|
85
|
+
attr_reader :config
|
86
|
+
|
87
|
+
#
|
88
|
+
# Driver configuration. Equivalent to config[:driver_options] || {}
|
89
|
+
#
|
90
|
+
def driver_options
|
91
|
+
config[:driver_options] || {}
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# Allocate a machine from the underlying service. This method
|
96
|
+
# does not need to wait for the machine to boot or have an IP, but it must
|
97
|
+
# store enough information in machine_spec.location to find the machine
|
98
|
+
# later in ready_machine.
|
99
|
+
#
|
100
|
+
# If a machine is powered off or otherwise unusable, this method may start
|
101
|
+
# it, but does not need to wait until it is started. The idea is to get the
|
102
|
+
# gears moving, but the job doesn't need to be done :)
|
103
|
+
#
|
104
|
+
# @param [Chef::Provisioning::ActionHandler] action_handler The action_handler object that is calling this method
|
105
|
+
# @param [Chef::Provisioning::MachineSpec] machine_spec A machine specification representing this machine.
|
106
|
+
# @param [Hash] machine_options A set of options representing the desired options when
|
107
|
+
# constructing the machine
|
108
|
+
#
|
109
|
+
# @return [Chef::Provisioning::MachineSpec] Modifies the passed-in machine_spec. Anything in here will be saved
|
110
|
+
# back after allocate_machine completes.
|
111
|
+
#
|
112
|
+
def allocate_machine(action_handler, machine_spec, machine_options)
|
113
|
+
raise "#{self.class} does not implement allocate_machine"
|
114
|
+
end
|
115
|
+
|
116
|
+
# Ready a machine, to the point where it is running and accessible via a
|
117
|
+
# transport. This will NOT allocate a machine, but may kick it if it is down.
|
118
|
+
# This method waits for the machine to be usable, returning a Machine object
|
119
|
+
# pointing at the machine, allowing useful actions like setup, converge,
|
120
|
+
# execute, file and directory.
|
121
|
+
#
|
122
|
+
#
|
123
|
+
# @param [Chef::Provisioning::ActionHandler] action_handler The action_handler object that is calling this method
|
124
|
+
# @param [Chef::Provisioning::MachineSpec] machine_spec A machine specification representing this machine.
|
125
|
+
# @param [Hash] machine_options A set of options representing the desired state of the machine
|
126
|
+
#
|
127
|
+
# @return [Machine] A machine object pointing at the machine, allowing useful actions like setup,
|
128
|
+
# converge, execute, file and directory.
|
129
|
+
#
|
130
|
+
def ready_machine(action_handler, machine_spec, machine_options)
|
131
|
+
raise "#{self.class} does not implement ready_machine"
|
132
|
+
end
|
133
|
+
|
134
|
+
# Connect to a machine without allocating or readying it. This method will
|
135
|
+
# NOT make any changes to anything, or attempt to wait.
|
136
|
+
#
|
137
|
+
# @param [Chef::Provisioning::MachineSpec] machine_spec MachineSpec representing this machine.
|
138
|
+
# @param [Hash] machine_options
|
139
|
+
# @return [Machine] A machine object pointing at the machine, allowing useful actions like setup,
|
140
|
+
# converge, execute, file and directory.
|
141
|
+
#
|
142
|
+
def connect_to_machine(machine_spec, machine_options)
|
143
|
+
raise "#{self.class} does not implement connect_to_machine"
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
# Delete the given machine -- destroy the machine,
|
148
|
+
# returning things to the state before allocate_machine was called.
|
149
|
+
#
|
150
|
+
# @param [Chef::Provisioning::ActionHandler] action_handler The action_handler object that is calling this method
|
151
|
+
# @param [Chef::Provisioning::MachineSpec] machine_spec A machine specification representing this machine.
|
152
|
+
# @param [Hash] machine_options A set of options representing the desired state of the machine
|
153
|
+
def destroy_machine(action_handler, machine_spec, machine_options)
|
154
|
+
raise "#{self.class} does not implement destroy_machine"
|
155
|
+
end
|
156
|
+
|
157
|
+
# Stop the given machine.
|
158
|
+
#
|
159
|
+
# @param [Chef::Provisioning::ActionHandler] action_handler The action_handler object that is calling this method
|
160
|
+
# @param [Chef::Provisioning::MachineSpec] machine_spec A machine specification representing this machine.
|
161
|
+
# @param [Hash] machine_options A set of options representing the desired state of the machine
|
162
|
+
def stop_machine(action_handler, machine_spec, machine_options)
|
163
|
+
raise "#{self.class} does not implement stop_machine"
|
164
|
+
end
|
165
|
+
|
166
|
+
# Allocate an image. Returns quickly with an ID that tracks the image.
|
167
|
+
#
|
168
|
+
# @param [Chef::Provisioning::ActionHandler] action_handler The action_handler object that is calling this method
|
169
|
+
# @param [Chef::Provisioning::ImageSpec] image_spec A machine specification representing this machine.
|
170
|
+
# @param [Hash] image_options A set of options representing the desired state of the machine
|
171
|
+
def allocate_image(action_handler, image_spec, image_options, machine_spec)
|
172
|
+
raise "#{self.class} does not implement create_image"
|
173
|
+
end
|
174
|
+
|
175
|
+
# Ready an image, waiting till the point where it is ready to be used.
|
176
|
+
#
|
177
|
+
# @param [Chef::Provisioning::ActionHandler] action_handler The action_handler object that is calling this method
|
178
|
+
# @param [Chef::Provisioning::ImageSpec] image_spec A machine specification representing this machine.
|
179
|
+
# @param [Hash] image_options A set of options representing the desired state of the machine
|
180
|
+
def ready_image(action_handler, image_spec, image_options)
|
181
|
+
raise "#{self.class} does not implement ready_image"
|
182
|
+
end
|
183
|
+
|
184
|
+
# Destroy an image using this service.
|
185
|
+
#
|
186
|
+
# @param [Chef::Provisioning::ActionHandler] action_handler The action_handler object that is calling this method
|
187
|
+
# @param [Chef::Provisioning::ImageSpec] image_spec A machine specification representing this machine.
|
188
|
+
# @param [Hash] image_options A set of options representing the desired state of the machine
|
189
|
+
def destroy_image(action_handler, image_spec, image_options)
|
190
|
+
raise "#{self.class} does not implement destroy_image"
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# Optional interface methods
|
195
|
+
#
|
196
|
+
|
197
|
+
#
|
198
|
+
# Allocate a set of machines. This should have the same effect as running
|
199
|
+
# allocate_machine on all machine_specs.
|
200
|
+
#
|
201
|
+
# Drivers do not need to implement this; the default implementation
|
202
|
+
# calls acquire_machine in parallel.
|
203
|
+
#
|
204
|
+
# == Parallelizing
|
205
|
+
#
|
206
|
+
# The parallelizer must implement #parallelize
|
207
|
+
# @example Example parallelizer
|
208
|
+
# parallelizer.parallelize(specs_and_options) do |machine_spec|
|
209
|
+
# allocate_machine(action_handler, machine_spec)
|
210
|
+
# end.to_a
|
211
|
+
# # The to_a at the end causes you to wait until the parallelization is done
|
212
|
+
#
|
213
|
+
# This object is shared among other chef-provisioning actions, ensuring that you do
|
214
|
+
# not go over parallelization limits set by the user. Use of the parallelizer
|
215
|
+
# to parallelizer machines is not required.
|
216
|
+
#
|
217
|
+
# == Passing a block
|
218
|
+
#
|
219
|
+
# If you pass a block to this function, each machine will be yielded to you
|
220
|
+
# as it completes, and then the function will return when all machines are
|
221
|
+
# yielded.
|
222
|
+
#
|
223
|
+
# @example Passing a block
|
224
|
+
# allocate_machines(action_handler, specs_and_options, parallelizer) do |machine_spec|
|
225
|
+
# ...
|
226
|
+
# end
|
227
|
+
#
|
228
|
+
# @param [Chef::Provisioning::ActionHandler] action_handler The action_handler object that is calling this method; this
|
229
|
+
# is generally a driver, but could be anything that can support the
|
230
|
+
# interface (i.e., in the case of the test kitchen provisioning driver for
|
231
|
+
# acquiring and destroying VMs).
|
232
|
+
# @param [Hash] specs_and_options A hash of machine_spec -> machine_options representing the
|
233
|
+
# machines to allocate.
|
234
|
+
# @param [Parallelizer] parallelizer an object with a parallelize() method that works like this:
|
235
|
+
# @return [Array<Machine>] An array of machine objects created
|
236
|
+
def allocate_machines(action_handler, specs_and_options, parallelizer)
|
237
|
+
parallelizer.parallelize(specs_and_options) do |machine_spec, machine_options|
|
238
|
+
allocate_machine(add_prefix(machine_spec, action_handler), machine_spec, machine_options)
|
239
|
+
yield machine_spec if block_given?
|
240
|
+
machine_spec
|
241
|
+
end.to_a
|
242
|
+
end
|
243
|
+
|
244
|
+
# Ready machines in batch, in parallel if possible.
|
245
|
+
def ready_machines(action_handler, specs_and_options, parallelizer)
|
246
|
+
parallelizer.parallelize(specs_and_options) do |machine_spec, machine_options|
|
247
|
+
machine = ready_machine(add_prefix(machine_spec, action_handler), machine_spec, machine_options)
|
248
|
+
yield machine if block_given?
|
249
|
+
machine
|
250
|
+
end.to_a
|
251
|
+
end
|
252
|
+
|
253
|
+
# Stop machines in batch, in parallel if possible.
|
254
|
+
def stop_machines(action_handler, specs_and_options, parallelizer)
|
255
|
+
parallelizer.parallelize(specs_and_options) do |machine_spec, machine_options|
|
256
|
+
stop_machine(add_prefix(machine_spec, action_handler), machine_spec, machine_options)
|
257
|
+
yield machine_spec if block_given?
|
258
|
+
end.to_a
|
259
|
+
end
|
260
|
+
|
261
|
+
# Delete machines in batch, in parallel if possible.
|
262
|
+
def destroy_machines(action_handler, specs_and_options, parallelizer)
|
263
|
+
parallelizer.parallelize(specs_and_options) do |machine_spec, machine_options|
|
264
|
+
destroy_machine(add_prefix(machine_spec, action_handler), machine_spec, machine_options)
|
265
|
+
yield machine_spec if block_given?
|
266
|
+
end.to_a
|
267
|
+
end
|
268
|
+
|
269
|
+
# Allocate a load balancer
|
270
|
+
# @param [ChefMetal::ActionHandler] action_handler The action handler
|
271
|
+
# @param [ChefMetal::LoadBalancerSpec] lb_spec Frozen LB specification
|
272
|
+
# @param [Hash] lb_options A hash of options to pass the LB
|
273
|
+
def allocate_load_balancer(action_handler, lb_spec, lb_options)
|
274
|
+
end
|
275
|
+
|
276
|
+
# Make the load balancer ready
|
277
|
+
# @param [ChefMetal::ActionHandler] action_handler The action handler
|
278
|
+
# @param [ChefMetal::LoadBalancerSpec] lb_spec Frozen LB specification
|
279
|
+
# @param [Hash] lb_options A hash of options to pass the LB
|
280
|
+
def ready_load_balancer(action_handler, lb_spec, lb_options)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Destroy the load balancer
|
284
|
+
# @param [ChefMetal::ActionHandler] action_handler The action handler
|
285
|
+
# @param [ChefMetal::LoadBalancerSpec] lb_spec Frozen LB specification
|
286
|
+
# @param [Hash] lb_options A hash of options to pass the LB
|
287
|
+
def destroy_load_balancer(action_handler, lb_spec, lb_options)
|
288
|
+
end
|
289
|
+
|
290
|
+
def update_load_balancer(action_handler, lb_spec, lb_options, opts = {})
|
291
|
+
end
|
292
|
+
|
293
|
+
protected
|
294
|
+
|
295
|
+
def add_prefix(machine_spec, action_handler)
|
296
|
+
AddPrefixActionHandler.new(action_handler, "[#{machine_spec.name}] ")
|
297
|
+
end
|
298
|
+
|
299
|
+
def get_private_key(name)
|
300
|
+
Cheffish.get_private_key(name, config)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|