chef-provisioning 0.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +207 -0
  3. data/LICENSE +201 -0
  4. data/README.md +260 -0
  5. data/Rakefile +6 -0
  6. data/lib/chef/provider/load_balancer.rb +77 -0
  7. data/lib/chef/provider/machine.rb +176 -0
  8. data/lib/chef/provider/machine_batch.rb +191 -0
  9. data/lib/chef/provider/machine_execute.rb +35 -0
  10. data/lib/chef/provider/machine_file.rb +54 -0
  11. data/lib/chef/provider/machine_image.rb +60 -0
  12. data/lib/chef/provisioning.rb +95 -0
  13. data/lib/chef/provisioning/action_handler.rb +68 -0
  14. data/lib/chef/provisioning/add_prefix_action_handler.rb +31 -0
  15. data/lib/chef/provisioning/chef_image_spec.rb +108 -0
  16. data/lib/chef/provisioning/chef_load_balancer_spec.rb +108 -0
  17. data/lib/chef/provisioning/chef_machine_spec.rb +84 -0
  18. data/lib/chef/provisioning/chef_provider_action_handler.rb +74 -0
  19. data/lib/chef/provisioning/chef_run_data.rb +139 -0
  20. data/lib/chef/provisioning/convergence_strategy.rb +28 -0
  21. data/lib/chef/provisioning/convergence_strategy/install_cached.rb +156 -0
  22. data/lib/chef/provisioning/convergence_strategy/install_msi.rb +58 -0
  23. data/lib/chef/provisioning/convergence_strategy/install_sh.rb +55 -0
  24. data/lib/chef/provisioning/convergence_strategy/no_converge.rb +39 -0
  25. data/lib/chef/provisioning/convergence_strategy/precreate_chef_objects.rb +183 -0
  26. data/lib/chef/provisioning/driver.rb +304 -0
  27. data/lib/chef/provisioning/image_spec.rb +72 -0
  28. data/lib/chef/provisioning/load_balancer_spec.rb +86 -0
  29. data/lib/chef/provisioning/machine.rb +112 -0
  30. data/lib/chef/provisioning/machine/basic_machine.rb +84 -0
  31. data/lib/chef/provisioning/machine/unix_machine.rb +278 -0
  32. data/lib/chef/provisioning/machine/windows_machine.rb +104 -0
  33. data/lib/chef/provisioning/machine_spec.rb +82 -0
  34. data/lib/chef/provisioning/recipe_dsl.rb +103 -0
  35. data/lib/chef/provisioning/transport.rb +95 -0
  36. data/lib/chef/provisioning/transport/ssh.rb +343 -0
  37. data/lib/chef/provisioning/transport/winrm.rb +151 -0
  38. data/lib/chef/provisioning/version.rb +5 -0
  39. data/lib/chef/resource/chef_data_bag_resource.rb +148 -0
  40. data/lib/chef/resource/load_balancer.rb +57 -0
  41. data/lib/chef/resource/machine.rb +124 -0
  42. data/lib/chef/resource/machine_batch.rb +78 -0
  43. data/lib/chef/resource/machine_execute.rb +28 -0
  44. data/lib/chef/resource/machine_file.rb +34 -0
  45. data/lib/chef/resource/machine_image.rb +35 -0
  46. data/lib/chef_metal.rb +1 -0
  47. 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