chef-provisioning 0.15

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.
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