chef-metal 0.13 → 0.14

Sign up to get free protection for your applications and to get access to all the features.
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