chef-metal 0.13 → 0.14

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f7a30676d9791f178e7448693969b295a85d5f4
4
- data.tar.gz: cac264ccd9b787d7ff1a799f3b0994973eb06a1a
3
+ metadata.gz: 3a913392bf4f33acc38f2c62be960b979affc698
4
+ data.tar.gz: 7980f8eed3169aed7d50eda6a5c0836825b324ae
5
5
  SHA512:
6
- metadata.gz: 31858346827c13c25a8fb8a5a6e222a424db9c610a86e643971e5a4620fb9b863eb8418c3235830d0d519870e07a519359e548a6e94099c6cc17b50251cd2255
7
- data.tar.gz: fec942a369b4d2e82a33090bcf47caa51cda90346c9f867ac00d5633d95f9c633e98a786ddb997d43466623ca184342845e1e1ad7756d1f3444b49ef713f6c31
6
+ metadata.gz: 50d27c172313e6c3d53bcc392f9d09cab8e9e4282ef77d46c65e854718e6f036415dd77570c1fc014c784768dd18b4962a0fd7f29a5045f7590b3e7d4f32d172
7
+ data.tar.gz: 147d41779a646160b1b93021ca116991d3d5740edd6284d248100b2d022e9366cec4453fd916bc6f4ed34daeeabe41dcef9ee4795e43a923d41b954d2fd805a6
@@ -1,9 +1,56 @@
1
1
  # Chef Metal Changelog
2
2
 
3
+ ## 0.14 (8/18/2013)
4
+
5
+
6
+ - FEATURE: Add the machine_image resource (@jkeiser, @johnewart):
7
+ ```ruby
8
+ machine_image 'base' do
9
+ machine_options :bootstrap_options => { :image_id => 'ami-1234798123431', :ssh_username => 'root' }
10
+ recipe 'secure_base'
11
+ recipe 'corp_users'
12
+ end
13
+ # Build an image based on 'base' that has apache
14
+ machine_image 'apache' do
15
+ # All bootstrap options, like ssh_username, are carried forward
16
+ from_image 'base'
17
+ recipe 'apache2'
18
+ end
19
+ # Build an image with my web app based on the apache base image
20
+ machine_image 'myapp' do
21
+ from_image 'apache'
22
+ recipe 'mywebapp'
23
+ end
24
+ # Build an image with mysql and my schema based on the corporate base image
25
+ machine_image 'mydb' do
26
+ from_image 'base'
27
+ recipe 'mysql'
28
+ recipe 'myschema'
29
+ end
30
+ # Build a DB machine from mydb. Does not reinstall stuff! :)
31
+ machine 'db' do
32
+ from_image 'mydb'
33
+ end
34
+ # Build a web app machine from myapp. Does not reinstall stuff! :)
35
+ machine 'myapp1' do
36
+ from_image 'myapp'
37
+ end
38
+ ```
39
+ - Creates a node with the name of the machine_image, which contains metadata
40
+ like the username of the image. This makes things like AWS image registries
41
+ possible.
42
+ - Fix the no_converge convergence strategy (@johnewart)
43
+ - SSH port forwarding improvements:
44
+ - Detects *any* IP on the localhost and forwards it--not just 127.0.0.1
45
+ - Binds to localhost on the remote side instead of 127.0.0.1, allowing for IPv6 communication
46
+ - Tries multiple ports--if the origin port is already taken, tries "0" (ephemeral).
47
+ - Fix SSH race condition causing port forwarding to happen twice (and fail miserably)
48
+ - Add ChefMetal.connect_to_machine('mario')
49
+
3
50
  ## 0.13 (6/17/2014)
4
51
 
5
52
  - make winrm work again (@mwrock)
6
- - add bootstrap_proxy as a convergence_option for when target machines require a proxy
53
+ - add bootstrap_proxy as a convergence_option for when target machines require a proxy (@MrMMorris)
7
54
 
8
55
  ## 0.12.1 (6/18/2014)
9
56
 
data/README.md CHANGED
@@ -234,9 +234,14 @@ knife edit /groups/admin.json -e <editor>
234
234
  ```
235
235
  Then add:
236
236
  ```
237
- "groups": [
238
- "clients"
237
+ {
238
+ "users": [
239
+ "pivotal" # This is an internal superuser for Hosted/Enterprise Chef
240
+ ],
241
+ "groups": [
242
+ "clients" # This is what you need to add
239
243
  ]
244
+ }
240
245
  ```
241
246
 
242
247
  This can also be done through the Chef Server web UI (Administration tab > Groups > select admins Group > Add 'clients'
@@ -94,18 +94,31 @@ class Chef::Provider::Machine < Chef::Provider::LWRPBase
94
94
  end
95
95
  end
96
96
 
97
+ def from_image_spec
98
+ @from_image_spec ||= begin
99
+ if new_resource.from_image
100
+ ChefMetal::ChefImageSpec.get(new_resource.from_image, new_resource.chef_server)
101
+ else
102
+ nil
103
+ end
104
+ end
105
+ end
106
+
97
107
  def new_machine_options
98
108
  machine_options(new_driver)
99
109
  end
100
110
 
101
111
  def current_machine_options
102
- if current_driver
103
- machine_options(current_driver)
104
- end
112
+ machine_options(current_driver)
105
113
  end
106
114
 
107
115
  def machine_options(driver)
108
116
  configs = []
117
+
118
+ if from_image_spec && from_image_spec.machine_options
119
+ configs << from_image_spec.machine_options
120
+ end
121
+
109
122
  configs << {
110
123
  :convergence_options =>
111
124
  [ :chef_server,
@@ -0,0 +1,54 @@
1
+ require 'chef/provider/lwrp_base'
2
+ require 'chef/provider/chef_node'
3
+ require 'openssl'
4
+ require 'chef_metal/chef_provider_action_handler'
5
+ require 'chef_metal/chef_image_spec'
6
+
7
+ class Chef::Provider::MachineImage < Chef::Provider::LWRPBase
8
+ def action_handler
9
+ @action_handler ||= ChefMetal::ChefProviderActionHandler.new(self)
10
+ end
11
+
12
+ def load_current_resource
13
+ end
14
+
15
+ # Get the driver specified in the resource
16
+ def new_driver
17
+ @new_driver ||= run_context.chef_metal.driver_for(new_resource.driver)
18
+ end
19
+
20
+ action :create do
21
+ # Get the image mapping on the server (from name to image-id)
22
+ image_spec = ChefMetal::ChefImageSpec.get(new_resource.name, new_resource.chef_server) ||
23
+ ChefMetal::ChefImageSpec.empty(new_resource.name, new_resource.chef_server)
24
+ if image_spec.location
25
+ # TODO check for real existence and maybe update
26
+ else
27
+ #
28
+ # Create a new image
29
+ #
30
+ image_spec.machine_options = new_resource.machine_options
31
+ create_image(image_spec)
32
+ end
33
+ end
34
+
35
+ action :destroy do
36
+ end
37
+
38
+ def create_image(image_spec)
39
+ # 1. Create the exemplar machine
40
+ machine_provider = Chef::Provider::Machine.new(new_resource, run_context)
41
+ machine_provider.load_current_resource
42
+ machine_provider.action_converge
43
+
44
+ # 2. Create the image
45
+ new_driver.allocate_image(action_handler, image_spec, new_resource.image_options,
46
+ machine_provider.machine_spec)
47
+
48
+ # 3. Save the linkage from name -> image id
49
+ image_spec.save(action_handler)
50
+
51
+ # 4. Wait for image to be ready
52
+ new_driver.ready_image(action_handler, image_spec, new_resource.image_options)
53
+ end
54
+ end
@@ -57,6 +57,12 @@ class Chef::Resource::Machine < Chef::Resource::LWRPBase
57
57
  # files '/remote/path.txt' => { :content => 'woo' }
58
58
  attribute :files, :kind_of => Hash
59
59
 
60
+ # The named machine_image to start from. Specify the name of a machine_image
61
+ # object and the default machine_options will be set to use that image.
62
+ # == Examples
63
+ # from_image 'company_base_image'
64
+ attribute :from_image, :kind_of => String
65
+
60
66
  # A single file to upload, in the format REMOTE_PATH, LOCAL_PATH|HASH.
61
67
  # This directive may be passed multiple times, and multiple files will be uploaded.
62
68
  # == Examples
@@ -0,0 +1,29 @@
1
+ require 'chef/resource/lwrp_base'
2
+ require 'cheffish'
3
+ require 'chef_metal'
4
+ require 'cheffish/merged_config'
5
+
6
+ class Chef::Resource::MachineImage < Chef::Resource::Machine
7
+ self.resource_name = 'machine_image'
8
+
9
+ def initialize(*args)
10
+ super
11
+ @image_options = run_context.chef_metal.current_image_options
12
+ @machine_options = run_context.chef_metal.current_machine_options
13
+ end
14
+
15
+ # TODO check if we still have all the actions from machine
16
+ actions :create, :destroy, :archive
17
+ default_action :create
18
+
19
+ attribute :image_options, :kind_of => Hash
20
+
21
+ # TODO
22
+ # - policy for old version cleanup
23
+ # - when attributes or recipes of the machine_image changes, or when the actual
24
+ # recipes therein change, we will regenerate the image
25
+ # - attribute to force or turn off image updates
26
+ # - ability to rebuild new image based on the previous image
27
+ # - ability to specify whether and how to keep the image-building machine around
28
+ # - ability to specify whether to leave the chef config around or not
29
+ end
@@ -71,9 +71,13 @@ module ChefMetal
71
71
  end
72
72
 
73
73
  def self.connect_to_machine(machine_spec, config = Cheffish.profiled_config)
74
+ chef_server = Cheffish.default_chef_server(config)
75
+ if machine_spec.is_a?(String)
76
+ machine_spec = ChefMachineSpec.get(machine_spec, chef_server)
77
+ end
74
78
  driver = driver_for_url(machine_spec.driver_url, config)
75
79
  if driver
76
- machine_options = { :convergence_options => { :chef_server => Cheffish.default_chef_server(config) } }
80
+ machine_options = { :convergence_options => { :chef_server => chef_server } }
77
81
  machine_options = Cheffish::MergedConfig.new(config[:machine_options], machine_options) if config[:machine_options]
78
82
  driver.connect_to_machine(machine_spec, machine_options)
79
83
  else
@@ -0,0 +1,106 @@
1
+ require 'chef_metal'
2
+ require 'cheffish'
3
+ require 'chef_metal/image_spec'
4
+
5
+ module ChefMetal
6
+ #
7
+ # Specification for a image. Sufficient information to find and contact it
8
+ # after it has been set up.
9
+ #
10
+ class ChefImageSpec < ImageSpec
11
+ def initialize(node, chef_server)
12
+ super(node)
13
+ @chef_server = chef_server
14
+ end
15
+
16
+ #
17
+ # Get a ImageSpec from the chef server. If the node does not exist on the
18
+ # server, it returns nil.
19
+ #
20
+ def self.get(name, chef_server = Cheffish.default_chef_server)
21
+ chef_api = Cheffish.chef_server_api(chef_server)
22
+ begin
23
+ data = chef_api.get("/data/images/#{name}")
24
+ data['machine_options'] = strings_to_symbols(data['machine_options'])
25
+ ChefImageSpec.new(data, chef_server)
26
+ rescue Net::HTTPServerException => e
27
+ if e.response.code == '404'
28
+ nil
29
+ else
30
+ raise
31
+ end
32
+ end
33
+ end
34
+
35
+ # Creates a new empty ImageSpec with the given name.
36
+ def self.empty(id, chef_server = Cheffish.default_chef_server)
37
+ ChefImageSpec.new({ 'id' => id }, chef_server)
38
+ end
39
+
40
+ #
41
+ # Globally unique identifier for this image. Does not depend on the image's
42
+ # location or existence.
43
+ #
44
+ def id
45
+ ChefImageSpec.id_from(chef_server, name)
46
+ end
47
+
48
+ def self.id_from(chef_server, name)
49
+ "#{chef_server[:chef_server_url]}/data/images/#{name}"
50
+ end
51
+
52
+ #
53
+ # Save this node to the server. If you have significant information that
54
+ # could be lost, you should do this as quickly as possible. Data will be
55
+ # saved automatically for you after allocate_image and ready_image.
56
+ #
57
+ def save(action_handler)
58
+ # Save the node to the server.
59
+ _self = self
60
+ _chef_server = _self.chef_server
61
+ ChefMetal.inline_resource(action_handler) do
62
+ chef_data_bag_item _self.name do
63
+ data_bag 'images'
64
+ chef_server _chef_server
65
+ raw_data _self.image_data
66
+ end
67
+ end
68
+ end
69
+
70
+ def delete(action_handler)
71
+ # Save the node to the server.
72
+ _self = self
73
+ _chef_server = _self.chef_server
74
+ ChefMetal.inline_resource(action_handler) do
75
+ chef_data_bag_item _self.name do
76
+ data_bag 'images'
77
+ chef_server _chef_server
78
+ action :destroy
79
+ end
80
+ end
81
+ end
82
+
83
+ protected
84
+
85
+ attr_reader :chef_server
86
+
87
+ #
88
+ # Chef API object for the given Chef server
89
+ #
90
+ def chef_api
91
+ Cheffish.server_api_for(chef_server)
92
+ end
93
+
94
+ def self.strings_to_symbols(data)
95
+ if data.is_a?(Hash)
96
+ result = {}
97
+ data.each_pair do |key, value|
98
+ result[key.to_sym] = strings_to_symbols(value)
99
+ end
100
+ result
101
+ else
102
+ data
103
+ end
104
+ end
105
+ end
106
+ end
@@ -1,3 +1,4 @@
1
+ require 'pry'
1
2
  require 'chef_metal'
2
3
  require 'cheffish'
3
4
  require 'chef_metal/machine_spec'
@@ -16,6 +16,7 @@ module ChefMetal
16
16
  attr_reader :current_driver
17
17
 
18
18
  with :machine_options
19
+ with :image_options
19
20
 
20
21
  def with_driver(driver, options = nil, &block)
21
22
  if drivers[driver] && options
@@ -49,6 +50,14 @@ module ChefMetal
49
50
  end
50
51
  end
51
52
 
53
+ def current_image_options
54
+ if @current_image_options
55
+ @current_image_options
56
+ else
57
+ {}
58
+ end
59
+ end
60
+
52
61
  def add_machine_options(options, &block)
53
62
  with_machine_options(Chef::Mixin::DeepMerge.hash_only_merge(current_machine_options, options), &block)
54
63
  end
@@ -14,7 +14,6 @@ module ChefMetal
14
14
  end
15
15
 
16
16
  def setup_convergence(action_handler, machine)
17
- machine_spec.save(action_handler)
18
17
  end
19
18
 
20
19
  def converge(action_handler, machine)
@@ -182,6 +182,27 @@ module ChefMetal
182
182
  raise "#{self.class} does not implement stop_machine"
183
183
  end
184
184
 
185
+ #
186
+ # Allocate an image. Returns quickly with an ID that tracks the image.
187
+ #
188
+ def allocate_image(action_handler, image_spec, image_options, machine_spec)
189
+ raise "#{self.class} does not implement create_image"
190
+ end
191
+
192
+ #
193
+ # Ready an image, waiting till the point where it is ready to be used.
194
+ #
195
+ def ready_image(action_handler, image_spec, image_options)
196
+ raise "#{self.class} does not implement ready_image"
197
+ end
198
+
199
+ #
200
+ # Destroy an image.
201
+ #
202
+ def destroy_image(action_handler, image_spec, image_options)
203
+ raise "#{self.class} does not implement destroy_image"
204
+ end
205
+
185
206
  #
186
207
  # Optional interface methods
187
208
  #
@@ -0,0 +1,70 @@
1
+ module ChefMetal
2
+ #
3
+ # Specification for a image. Sufficient information to find and contact it
4
+ # after it has been set up.
5
+ #
6
+ class ImageSpec
7
+ def initialize(image_data)
8
+ @image_data = image_data
9
+ end
10
+
11
+ attr_reader :image_data
12
+
13
+ #
14
+ # Globally unique identifier for this image. Does not depend on the image's
15
+ # location or existence.
16
+ #
17
+ def id
18
+ raise "id unimplemented"
19
+ end
20
+
21
+ #
22
+ # Name of the image. Corresponds to the name in "image 'name' do" ...
23
+ #
24
+ def name
25
+ image_data['id']
26
+ end
27
+
28
+ #
29
+ # Location of this image. This should be a freeform hash, with enough
30
+ # information for the driver to look it up and create a image object to
31
+ # access it.
32
+ #
33
+ # This MUST include a 'driver_url' attribute with the driver's URL in it.
34
+ #
35
+ # chef-metal will do its darnedest to not lose this information.
36
+ #
37
+ def location
38
+ image_data['location']
39
+ end
40
+
41
+ #
42
+ # Set the location for this image.
43
+ #
44
+ def location=(value)
45
+ image_data['location'] = value
46
+ end
47
+
48
+ def machine_options
49
+ image_data['machine_options']
50
+ end
51
+
52
+ def machine_options=(value)
53
+ image_data['machine_options'] = value
54
+ end
55
+
56
+ # URL to the driver. Convenience for location['driver_url']
57
+ def driver_url
58
+ location ? location['driver_url'] : nil
59
+ end
60
+
61
+ #
62
+ # Save this image_data to the server. If you have significant information that
63
+ # could be lost, you should do this as quickly as possible. image_data will be
64
+ # saved automatically for you after allocate_image and ready_image.
65
+ #
66
+ def save(action_handler)
67
+ raise "save unimplemented"
68
+ end
69
+ end
70
+ end
@@ -9,6 +9,8 @@ require 'chef/resource/machine_file'
9
9
  require 'chef/provider/machine_file'
10
10
  require 'chef/resource/machine_execute'
11
11
  require 'chef/provider/machine_execute'
12
+ require 'chef/resource/machine_image'
13
+ require 'chef/provider/machine_image'
12
14
 
13
15
  class Chef
14
16
  module DSL
@@ -29,6 +31,14 @@ class Chef
29
31
  run_context.chef_metal.add_machine_options(options, &block)
30
32
  end
31
33
 
34
+ def with_image_options(image_options, &block)
35
+ run_context.chef_metal.with_image_options(image_options, &block)
36
+ end
37
+
38
+ def current_image_options
39
+ run_context.chef_metal.current_image_options
40
+ end
41
+
32
42
  NOT_PASSED = Object.new
33
43
 
34
44
  def auto_batch_machines(value = NOT_PASSED)
@@ -1,4 +1,5 @@
1
1
  require 'chef_metal/transport'
2
+ require 'chef/log'
2
3
  require 'uri'
3
4
  require 'socket'
4
5
  require 'timeout'
@@ -108,14 +109,20 @@ module ChefMetal
108
109
 
109
110
  def make_url_available_to_remote(local_url)
110
111
  uri = URI(local_url)
111
- host = Socket.getaddrinfo(uri.host, uri.scheme, nil, :STREAM)[0][3]
112
- if host == '127.0.0.1' || host == '::1'
113
- unless session.forward.active_remotes.any? { |port, bind| port == uri.port && bind == uri.host }
114
- Chef::Log.debug("Forwarding local server #{uri.host}:#{uri.port} to port #{uri.port} on #{username}@#{self.host}")
115
- session.forward.remote(uri.port, uri.host, uri.port)
112
+ if is_local_machine(uri.host)
113
+ port, host = forward_port(uri.port, uri.host, uri.port, 'localhost')
114
+ if !port
115
+ # Try harder if the port is already taken
116
+ port, host = forward_port(uri.port, uri.host, 0, 'localhost')
117
+ if !port
118
+ raise "Error forwarding port: could not forward #{uri.port} or 0"
119
+ end
116
120
  end
121
+ uri.host = host
122
+ uri.port = port
117
123
  end
118
- local_url
124
+ Chef::Log.info("Port forwarded: local URL #{local_url} is available to #{self.host} as #{uri.to_s} for the duration of this SSH connection.")
125
+ uri.to_s
119
126
  end
120
127
 
121
128
  def disconnect
@@ -232,6 +239,50 @@ module ChefMetal
232
239
  raise InitialConnectTimeout.new($!)
233
240
  end
234
241
  end
242
+
243
+ def is_local_machine(host)
244
+ local_addrs = Socket.ip_address_list
245
+ host_addrs = Addrinfo.getaddrinfo(host, nil)
246
+ local_addrs.any? do |local_addr|
247
+ host_addrs.any? do |host_addr|
248
+ local_addr.ip_address == host_addr.ip_address
249
+ end
250
+ end
251
+ end
252
+
253
+ # Forwards a port over the connection, and returns the
254
+ def forward_port(local_port, local_host, remote_port, remote_host)
255
+ # This bit is from the documentation.
256
+ if session.forward.respond_to?(:active_remote_destinations)
257
+ got_remote_port, remote_host = session.forward.active_remote_destinations[[local_port, local_host]]
258
+ if !got_remote_port
259
+ Chef::Log.debug("Forwarding local server #{local_host}:#{local_port} to #{username}@#{self.host}")
260
+
261
+ session.forward.remote(local_port, local_host, remote_port, remote_host) do |actual_remote_port|
262
+ got_remote_port = actual_remote_port || :error
263
+ :no_exception # I'll take care of it myself, thanks
264
+ end
265
+ # Kick SSH until we get a response
266
+ session.loop { !got_remote_port }
267
+ if got_remote_port == :error
268
+ return nil
269
+ end
270
+ end
271
+ [ got_remote_port, remote_host ]
272
+ else
273
+ @forwarded_ports ||= {}
274
+ remote_port, remote_host = @forwarded_ports[[local_port, local_host]]
275
+ if !remote_port
276
+ Chef::Log.debug("Forwarding local server #{local_host}:#{local_port} to #{username}@#{self.host}")
277
+ old_active_remotes = session.forward.active_remotes
278
+ session.forward.remote(local_port, local_host, local_port)
279
+ session.loop { !(session.forward.active_remotes.length > old_active_remotes.length) }
280
+ remote_port, remote_host = (session.forward.active_remotes - old_active_remotes).first
281
+ @forwarded_ports[[local_port, local_host]] = [ remote_port, remote_host ]
282
+ end
283
+ [ remote_port, remote_host ]
284
+ end
285
+ end
235
286
  end
236
287
  end
237
288
  end
@@ -1,3 +1,3 @@
1
1
  module ChefMetal
2
- VERSION = '0.13'
2
+ VERSION = '0.14'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-metal
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.13'
4
+ version: '0.14'
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Keiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-15 00:00:00.000000000 Z
11
+ date: 2014-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef
@@ -155,13 +155,16 @@ files:
155
155
  - lib/chef/provider/machine_batch.rb
156
156
  - lib/chef/provider/machine_execute.rb
157
157
  - lib/chef/provider/machine_file.rb
158
+ - lib/chef/provider/machine_image.rb
158
159
  - lib/chef/resource/machine.rb
159
160
  - lib/chef/resource/machine_batch.rb
160
161
  - lib/chef/resource/machine_execute.rb
161
162
  - lib/chef/resource/machine_file.rb
163
+ - lib/chef/resource/machine_image.rb
162
164
  - lib/chef_metal.rb
163
165
  - lib/chef_metal/action_handler.rb
164
166
  - lib/chef_metal/add_prefix_action_handler.rb
167
+ - lib/chef_metal/chef_image_spec.rb
165
168
  - lib/chef_metal/chef_machine_spec.rb
166
169
  - lib/chef_metal/chef_provider_action_handler.rb
167
170
  - lib/chef_metal/chef_run_data.rb
@@ -172,6 +175,7 @@ files:
172
175
  - lib/chef_metal/convergence_strategy/no_converge.rb
173
176
  - lib/chef_metal/convergence_strategy/precreate_chef_objects.rb
174
177
  - lib/chef_metal/driver.rb
178
+ - lib/chef_metal/image_spec.rb
175
179
  - lib/chef_metal/machine.rb
176
180
  - lib/chef_metal/machine/basic_machine.rb
177
181
  - lib/chef_metal/machine/unix_machine.rb