chef-metal 0.1 → 0.2

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: ff00a9a5e38faa2766f8b863f1d1e4f388835d9a
4
- data.tar.gz: f9c11bf6e500bd3ce30c1584f737e9c325b39ae3
3
+ metadata.gz: a47fa35091e77e37a5ee21d98a91aa77438f2193
4
+ data.tar.gz: fee0d677dd4bb0503179fa71784a75069ab2928b
5
5
  SHA512:
6
- metadata.gz: 65d59f9815c642ef61f3c105ee4b29f65ab4aa13b9995e91600fce8efd6eab67d9b2a00960d74be740b52aa4bb5099dbf44f524811cbbfcb08f93d3e5c5114af
7
- data.tar.gz: bd1be86b6d3a40d0d1d4217d8aafc54f797338469546cf1ac18841bfbcc8cc200aa6fabed214f42ee228cd2ea5c77f1c2909fdf88b9611d93efa284732cc6323
6
+ metadata.gz: d02701018d7e62a14f9ac0abcbbf4869ca0cacab94e543945e4830dfce67336ea7be7e8055e754d07e56abd8a634821955acabced9e1f8ea467b63bfef33dbf6
7
+ data.tar.gz: a271c5856f5b695ba8be63ba7ae803d9fd58f18768a8794f34a320e9933d79952df7ade8151cf9b9798aa479ca3ace0b3fcfff4869b7fdb041cf3c501eb5a4a1
data/README.md CHANGED
@@ -10,12 +10,12 @@ Try It Out
10
10
 
11
11
  To give it a spin, get chef 11.8 or greater try this:
12
12
 
13
- git clone https://github.com/jkeiser/cheffish.git
13
+ git clone https://github.com/opscode/cheffish.git
14
14
  cd cheffish
15
15
  rake install
16
16
  cd ..
17
17
 
18
- git clone https://github.com/jkeiser/chef-metal.git
18
+ git clone https://github.com/opscode/chef-metal.git
19
19
  cd chef-metal
20
20
  rake install
21
21
 
@@ -31,9 +31,9 @@ Chef Metal has two major abstractions: the machine resource, and provisioners.
31
31
 
32
32
  ### The `machine` resource
33
33
 
34
- You declare what your machines do (recipes, tags, etc.) with the `machine` resource, the fundamental unit of Chef Metal. You will typically declare `machine` resources in a separate, OS/priovisioning-independent file that declares the *topology* of your app--your machines and the recipes that will run on them.
34
+ You declare what your machines do (recipes, tags, etc.) with the `machine` resource, the fundamental unit of Chef Metal. You will typically declare `machine` resources in a separate, OS/provisioning-independent file that declares the *topology* of your app--your machines and the recipes that will run on them.
35
35
 
36
- The machine resources from the example [myapp::small](https://github.com/jkeiser/chef-metal/blob/master/cookbooks/myapp/recipes/small.rb) are pretty straightforward. Here's a copy/paste:
36
+ The machine resources from the example [myapp::small](https://github.com/opscode/chef-metal/blob/master/cookbooks/myapp/recipes/small.rb) are pretty straightforward. Here's a copy/paste:
37
37
 
38
38
  ```ruby
39
39
  machine 'mario' do
@@ -75,7 +75,7 @@ chef-zero comes with a provisioner for Vagrant, an abstraction that covers Virtu
75
75
  chef-client -z -o myapp::vagrant,myapp::linux,myapp::small
76
76
  ```
77
77
 
78
- The provisioner specification is in myapp::vagrant and myapp::linux [sample recipes](https://github.com/jkeiser/chef-metal/tree/master/cookbooks/myapp/recipes), copy/pasted here for your convenience:
78
+ The provisioner specification is in myapp::vagrant and myapp::linux [sample recipes](https://github.com/opscode/chef-metal/tree/master/cookbooks/myapp/recipes), copy/pasted here for your convenience:
79
79
 
80
80
  ```ruby
81
81
  vagrant_cluster "#{ENV['HOME']}/machinetest"
@@ -90,7 +90,7 @@ end
90
90
 
91
91
  `vagrant_cluster` declares a directory where all the vagrant definitions will be stored, and uses `with_provisioner` internally to tell Chef Metal that this is the provisioner we want to use for machines.
92
92
 
93
- `vagrant_box` makes sure a particular vagrant box exists, and lets you specify `provisioner_options` for things like port forwarding, OS definitions, and any other vagrant-isms. A more complicated vagrant box, with provisioner options, can be found in [myapp::windows](https://github.com/jkeiser/chef-metal/blob/master/cookbooks/myapp/recipes/windows.rb).
93
+ `vagrant_box` makes sure a particular vagrant box exists, and lets you specify `provisioner_options` for things like port forwarding, OS definitions, and any other vagrant-isms. A more complicated vagrant box, with provisioner options, can be found in [myapp::windows](https://github.com/opscode/chef-metal/blob/master/cookbooks/myapp/recipes/windows.rb).
94
94
 
95
95
  `with_chef_local_server` is a generic directive that creates a chef-zero server pointed at the given repository. nodes, clients, data bags, and all data will be stored here on your provisioner machine if you do this. You can use `with_chef_server` instead if you want to point at OSS, Hosted or Enterprise Chef, and if you don't specify a Chef server at all, it will use the one you are running chef-client against.
96
96
 
@@ -140,7 +140,7 @@ fog_key_pair 'me' do
140
140
  end
141
141
  ```
142
142
 
143
- `with_fog_ec2_provisioner` tells chef-metal to use the Fog provisioner against EC2. If you specify your credentials in `~/.aws/config`, you don't *have* to specify anything else; it will use the Fog defaults. You may pass a hash of parameters to `with_fog_ec2_provisioner` that is described [here](https://github.com/jkeiser/chef-metal/blob/master/lib/chef_metal/provisioner/fog_provisioner.rb#L21-L32).
143
+ `with_fog_ec2_provisioner` tells chef-metal to use the Fog provisioner against EC2. If you specify your credentials in `~/.aws/config`, you don't *have* to specify anything else; it will use the Fog defaults. You may pass a hash of parameters to `with_fog_ec2_provisioner` that is described [here](https://github.com/opscode/chef-metal/blob/master/lib/chef_metal/provisioner/fog_provisioner.rb#L21-L32).
144
144
 
145
145
  `fog_key_pair` creates a new key pair (if the files do not already exist) and automatically tells the Provisioner to use it to bootstrap subsequent machines. The private/public key pair will be automatically authorized to log on to the instance on first boot.
146
146
 
@@ -155,6 +155,6 @@ You will notice that we are still using `myapp::small` here. Machine definition
155
155
  Bugs and The Plan
156
156
  -----------------
157
157
 
158
- It's early days. *Please* submit bugs at https://github.com/jkeiser/chef-metal/issues, contact jkeiser on Twitter at @jkeiser2, email at jkeiser@opscode.com
158
+ It's early days. *Please* submit bugs at https://github.com/opscode/chef-metal/issues, contact jkeiser on Twitter at @jkeiser2, email at jkeiser@opscode.com
159
159
 
160
160
  If you are interested in the Plan for Chef Metal, you can peruse our [Trello board](https://trello.com/b/GcSzW0GM/chef-metal)! Please add suggestions there, vote or comment on issues that are important to you, and feel free to contribute by picking up a card. Chat with me (jkeiser@opscode.com) if you would like some context on how to go about implementing a card, or just go hog wild and submit a PR :)
@@ -15,7 +15,12 @@ class Chef::Provider::FogKeyPair < Chef::Provider::LWRPBase
15
15
  action :delete do
16
16
  if current_resource_exists?
17
17
  converge_by "delete #{key_description}" do
18
- compute.key_pairs.delete(new_resource.name)
18
+ case new_resource.provisioner.compute_options[:provider]
19
+ when 'DigitalOcean'
20
+ compute.destroy_key_pair(@current_id)
21
+ else
22
+ compute.key_pairs.delete(new_resource.name)
23
+ end
19
24
  end
20
25
  end
21
26
  end
@@ -37,11 +42,22 @@ class Chef::Provider::FogKeyPair < Chef::Provider::LWRPBase
37
42
  ensure_keys
38
43
  end
39
44
 
40
- public_key, format = Cheffish::KeyFormatter.decode(IO.read(new_resource.public_key_path))
41
- if Cheffish::KeyFormatter.encode(public_key, :format => :fingerprint) != @current_fingerprint
45
+ new_fingerprint = case new_resource.provisioner.compute_options[:provider]
46
+ when 'DigitalOcean'
47
+ Cheffish::KeyFormatter.encode(desired_key, :format => :openssh)
48
+ else
49
+ Cheffish::KeyFormatter.encode(desired_key, :format => :fingerprint)
50
+ end
51
+
52
+ if new_fingerprint != @current_fingerprint
42
53
  if new_resource.allow_overwrite
43
54
  converge_by "update #{key_description} to match local key at #{new_resource.private_key_path}" do
44
- compute.import_key_pair(new_resource.name, Cheffish::KeyFormatter.encode(desired_key, :format => :openssh))
55
+ case new_resource.provisioner.compute_options[:provider]
56
+ when 'DigitalOcean'
57
+ compute.create_ssh_key(new_resource.name, Cheffish::KeyFormatter.encode(desired_key, :format => :openssh))
58
+ else
59
+ compute.import_key_pair(new_resource.name, Cheffish::KeyFormatter.encode(desired_key, :format => :openssh))
60
+ end
45
61
  end
46
62
  else
47
63
  raise "#{key_description} does not match local private key, and allow_overwrite is false!"
@@ -53,7 +69,12 @@ class Chef::Provider::FogKeyPair < Chef::Provider::LWRPBase
53
69
 
54
70
  # Create key
55
71
  converge_by "create #{key_description} from local key at #{new_resource.private_key_path}" do
56
- compute.import_key_pair(new_resource.name, Cheffish::KeyFormatter.encode(desired_key, :format => :openssh))
72
+ case new_resource.provisioner.compute_options[:provider]
73
+ when 'DigitalOcean'
74
+ compute.create_ssh_key(new_resource.name, Cheffish::KeyFormatter.encode(desired_key, :format => :openssh))
75
+ else
76
+ compute.import_key_pair(new_resource.name, Cheffish::KeyFormatter.encode(desired_key, :format => :openssh))
77
+ end
57
78
  end
58
79
  end
59
80
  end
@@ -72,6 +93,18 @@ class Chef::Provider::FogKeyPair < Chef::Provider::LWRPBase
72
93
  end
73
94
  end
74
95
 
96
+ def desired_key
97
+ @desired_key ||= begin
98
+ if new_resource.public_key_path
99
+ public_key, format = Cheffish::KeyFormatter.decode(IO.read(new_resource.public_key_path))
100
+ public_key
101
+ else
102
+ private_key, format = Cheffish::KeyFormatter.decode(IO.read(new_resource.private_key_path))
103
+ private_key.public_key
104
+ end
105
+ end
106
+ end
107
+
75
108
  def current_resource_exists?
76
109
  @current_resource.action != [ :delete ]
77
110
  end
@@ -89,11 +122,22 @@ class Chef::Provider::FogKeyPair < Chef::Provider::LWRPBase
89
122
  raise 'ec2_key_pair only works with fog_provisioner'
90
123
  end
91
124
  @current_resource = Chef::Resource::FogKeyPair.new(new_resource.name)
92
- current_key_pair = compute.key_pairs.get(new_resource.name)
93
- if current_key_pair
94
- @current_fingerprint = current_key_pair.fingerprint
125
+ case new_resource.provisioner.compute_options[:provider]
126
+ when 'DigitalOcean'
127
+ current_key_pair = compute.ssh_keys.select { |key| key.name == new_resource.name }.first
128
+ if current_key_pair
129
+ @current_id = current_key_pair.id
130
+ @current_fingerprint = current_key_pair ? compute.ssh_keys.get(@current_id).ssh_pub_key : nil
131
+ else
132
+ current_resource.action :delete
133
+ end
95
134
  else
96
- current_resource.action :delete
135
+ current_key_pair = compute.key_pairs.get(new_resource.name)
136
+ if current_key_pair
137
+ @current_fingerprint = current_key_pair ? current_key_pair.fingerprint : nil
138
+ else
139
+ current_resource.action :delete
140
+ end
97
141
  end
98
142
 
99
143
  if new_resource.private_key_path && ::File.exist?(new_resource.private_key_path)
data/lib/chef_metal.rb CHANGED
@@ -4,13 +4,6 @@ require 'chef/resource/machine'
4
4
  require 'chef/provider/machine'
5
5
  require 'chef/resource/machine_file'
6
6
  require 'chef/provider/machine_file'
7
- require 'chef/resource/vagrant_cluster'
8
- require 'chef/provider/vagrant_cluster'
9
- require 'chef/resource/vagrant_box'
10
- require 'chef/provider/vagrant_box'
11
- require 'chef/resource/fog_key_pair'
12
- require 'chef/provider/fog_key_pair'
13
- require 'chef_metal/provisioner/fog_provisioner'
14
7
 
15
8
  require 'chef_metal/inline_resource'
16
9
 
@@ -39,29 +32,6 @@ module ChefMetal
39
32
  end
40
33
  end
41
34
 
42
- def self.with_vagrant_cluster(cluster_path, &block)
43
- require 'chef_metal/provisioner/vagrant_provisioner'
44
-
45
- with_provisioner(ChefMetal::Provisioner::VagrantProvisioner.new(cluster_path), &block)
46
- end
47
-
48
- def self.with_vagrant_box(box_name, provisioner_options = nil, &block)
49
- require 'chef/resource/vagrant_box'
50
-
51
- if box_name.is_a?(Chef::Resource::VagrantBox)
52
- provisioner_options ||= box_name.provisioner_options || {}
53
- provisioner_options['vagrant_options'] ||= {}
54
- provisioner_options['vagrant_options']['vm.box'] = box_name.name
55
- provisioner_options['vagrant_options']['vm.box_url'] = box_name.url if box_name.url
56
- else
57
- provisioner_options ||= {}
58
- provisioner_options['vagrant_options'] ||= {}
59
- provisioner_options['vagrant_options']['vm.box'] = box_name
60
- end
61
-
62
- with_provisioner_options(provisioner_options, &block)
63
- end
64
-
65
35
  def self.inline_resource(provider, &block)
66
36
  InlineResource.new(provider).instance_eval(&block)
67
37
  end
@@ -0,0 +1,4 @@
1
+ require 'chef_metal'
2
+ require 'chef/resource/fog_key_pair'
3
+ require 'chef/provider/fog_key_pair'
4
+ require 'chef_metal/provisioner/fog_provisioner'
@@ -54,14 +54,12 @@ module ChefMetal
54
54
 
55
55
  def current_base_bootstrap_options
56
56
  result = @base_bootstrap_options.dup
57
- if compute_options[:provider] == 'AWS'
58
- if key_pairs.size > 0
59
- last_pair_name = key_pairs.keys.last
60
- last_pair = key_pairs[last_pair_name]
61
- result[:key_name] ||= last_pair_name
62
- result[:private_key_path] ||= last_pair.private_key_path
63
- result[:public_key_path] ||= last_pair.public_key_path
64
- end
57
+ if key_pairs.size > 0
58
+ last_pair_name = key_pairs.keys.last
59
+ last_pair = key_pairs[last_pair_name]
60
+ result[:key_name] ||= last_pair_name
61
+ result[:private_key_path] ||= last_pair.private_key_path
62
+ result[:public_key_path] ||= last_pair.public_key_path
65
63
  end
66
64
  result
67
65
  end
@@ -122,7 +120,7 @@ module ChefMetal
122
120
  if !server
123
121
  Chef::Log.warn "Machine #{node['name']} (#{provisioner_output['server_id']} on #{provisioner_url}) is not associated with the ec2 account. Recreating ..."
124
122
  need_to_create = true
125
- elsif server.state == 'terminated' # Can't come back from that
123
+ elsif %w(terminated archive).include?(server.state) # Can't come back from that
126
124
  Chef::Log.warn "Machine #{node['name']} (#{server.id} on #{provisioner_url}) is terminated. Recreating ..."
127
125
  need_to_create = true
128
126
  else
@@ -249,12 +247,15 @@ module ChefMetal
249
247
  provider_identifier = case compute_options[:provider]
250
248
  when 'AWS'
251
249
  compute_options[:aws_access_key_id]
250
+ when 'DigitalOcean'
251
+ compute_options[:digitalocean_client_id]
252
252
  else
253
253
  '???'
254
254
  end
255
- "fog:#{compute_options['provider']}:#{provider_identifier}"
255
+ "fog:#{compute_options[:provider]}:#{provider_identifier}"
256
256
  end
257
257
 
258
+ # Not meant to be part of public interface
258
259
  def transport_for(server)
259
260
  # TODO winrm
260
261
  create_ssh_transport(server)
@@ -303,6 +304,28 @@ module ChefMetal
303
304
  # User-defined tags override the ones we set
304
305
  tags.merge!(bootstrap_options[:tags]) if bootstrap_options[:tags]
305
306
  bootstrap_options.merge!({ :tags => tags })
307
+
308
+ # Provide reasonable defaults for DigitalOcean
309
+ if compute_options[:provider] == 'DigitalOcean'
310
+ if !bootstrap_options[:image_id]
311
+ bootstrap_options[:image_name] ||= 'CentOS 6.4 x32'
312
+ bootstrap_options[:image_id] = compute.images.select { |image| image.name == bootstrap_options[:image_name] }.first.id
313
+ end
314
+ if !bootstrap_options[:flavor_id]
315
+ bootstrap_options[:flavor_name] ||= '512MB'
316
+ bootstrap_options[:flavor_id] = compute.flavors.select { |flavor| flavor.name == bootstrap_options[:flavor_name] }.first.id
317
+ end
318
+ if !bootstrap_options[:region_id]
319
+ bootstrap_options[:region_name] ||= 'San Francisco 1'
320
+ bootstrap_options[:region_id] = compute.regions.select { |region| region.name == bootstrap_options[:region_name] }.first.id
321
+ end
322
+ bootstrap_options[:ssh_key_ids] ||= [ compute.ssh_keys.select { |k| k.name == bootstrap_options[:key_name] }.first.id ]
323
+
324
+ # You don't get to specify name yourself
325
+ bootstrap_options[:name] = node['name']
326
+ end
327
+
328
+ bootstrap_options
306
329
  end
307
330
 
308
331
  def machine_for(node, server = nil)
@@ -332,18 +355,21 @@ module ChefMetal
332
355
 
333
356
  def ssh_options_for(server)
334
357
  result = {
358
+ # TODO create a user known hosts file
335
359
  # :user_known_hosts_file => vagrant_ssh_config['UserKnownHostsFile'],
336
- # :paranoid => yes_or_no(vagrant_ssh_config['StrictHostKeyChecking']),
360
+ # :paranoid => true,
337
361
  :auth_methods => [ 'publickey' ],
338
- :keys => [ server.private_key ],
339
- :keys_only => true
362
+ :keys_only => true,
363
+ :host_key_alias => "#{server.id}.#{compute_options[:provider]}"
340
364
  }
341
- if compute_options[:provider] == 'AWS'
365
+ if server.respond_to?(:private_key) && server.private_key
366
+ result[:keys] = [ server.private_key ]
367
+ elsif server.respond_to?(:key_name) && key_pairs[server.key_name]
342
368
  # TODO generalize for others?
343
- result[:keys] = [ server.private_key || key_pairs[server.key_name].private_key_path ]
344
- result[:host_key_alias] = "#{server.id}.ec2"
369
+ result[:keys] ||= [ key_pairs[server.key_name].private_key_path ]
345
370
  else
346
- private_key_path = nil
371
+ # TODO need a way to know which key if there were multiple
372
+ result[:keys] = [ key_pairs.first[1].private_key_path ]
347
373
  end
348
374
  result
349
375
  end
@@ -351,11 +377,18 @@ module ChefMetal
351
377
  def create_ssh_transport(server)
352
378
  require 'chef_metal/transport/ssh'
353
379
 
354
- ssh_options = ssh_options_for(server)
355
- options = {
356
- :prefix => 'sudo '
357
- }
358
- ChefMetal::Transport::SSH.new(server.public_ip_address, 'ubuntu', ssh_options, options)
380
+ ssh_options, options = ssh_options_for(server)
381
+ # If we're on AWS, the default is to use ubuntu, not root
382
+ if compute_options[:provider] == 'AWS'
383
+ username = compute_options[:ssh_username] || 'ubuntu'
384
+ else
385
+ username = compute_options[:ssh_username] || 'root'
386
+ end
387
+ options = {}
388
+ if compute_options[:sudo] || (!compute_options.has_key?(:sudo) && username != 'root')
389
+ options[:prefix] = 'sudo '
390
+ end
391
+ ChefMetal::Transport::SSH.new(server.public_ip_address, username, ssh_options, options)
359
392
  end
360
393
 
361
394
  def wait_until_ready(server, timeout)
@@ -0,0 +1,135 @@
1
+ require 'chef/mixin/shell_out'
2
+ require 'chef_metal/provisioner'
3
+ require 'lxc'
4
+
5
+ module ChefMetal
6
+ class Provisioner
7
+
8
+ # Provisions machines in lxc.
9
+ class LXCProvisioner < Provisioner
10
+
11
+ include Chef::Mixin::ShellOut
12
+
13
+ #
14
+ # Acquire a machine, generally by provisioning it. Returns a Machine
15
+ # object pointing at the machine, allowing useful actions like setup,
16
+ # converge, execute, file and directory. The Machine object will have a
17
+ # "node" property which must be saved to the server (if it is any
18
+ # different from the original node object).
19
+ #
20
+ # ## Parameters
21
+ # provider - the provider object that is calling this method.
22
+ # node - node object (deserialized json) representing this machine. If
23
+ # the node has a provisioner_options hash in it, these will be used
24
+ # instead of options provided by the provisioner. TODO compare and
25
+ # fail if different?
26
+ # node will have node['normal']['provisioner_options'] in it with any options.
27
+ # It is a hash with this format:
28
+ #
29
+ # -- provisioner_url: lxc:<lxc_path>
30
+ # -- template: template name
31
+ # -- template_options: additional arguments for templates
32
+ # -- backingstore: backing storage (lvm, thinpools, btrfs etc)
33
+ #
34
+ # node['normal']['provisioner_output'] will be populated with information
35
+ # about the created machine. For lxc, it is a hash with this
36
+ # format:
37
+ #
38
+ # -- provisioner_url: lxc://<lxc_path>
39
+ # -- lxc_path: path to lxc root
40
+ # -- name: container name
41
+ #
42
+ def acquire_machine(provider, node)
43
+ # TODO verify that the existing provisioner_url in the node is the same as ours
44
+
45
+ # Set up the modified node data
46
+ provisioner_options = node['normal']['provisioner_options']
47
+ provisioner_output = node['normal']['provisioner_output'] || {
48
+ 'provisioner_url' => "lxc://#{lxc_path_for(node)}",
49
+ 'name' => node['name']
50
+ }
51
+
52
+
53
+ # Create the container if it does not exist
54
+ ct = LXC::Container.new(provisioner_output['name'], lxc_path_for(node))
55
+ unless ct.defined?
56
+ provider.converge_by "create lxc container #{provisioner_output['name']}" do
57
+ ct.create(provisioner_options['template'], provisioner_options['backingstore'], 0, provisioner_options['template_options'])
58
+ end
59
+ end
60
+ unless ct.running?
61
+ provider.converge_by "start lxc container #{provisioner_output['name']}" do
62
+ ct.start
63
+ while ct.ip_addresses.empty?
64
+ sleep 1 # wait till dhcp ip allocation is done
65
+ end
66
+ end
67
+ end
68
+
69
+ if true # do a check on whether sshd is installed. This is idempotency!
70
+ provider.converge_by "install ssh into container #{provisioner_output['name']}" do
71
+ end
72
+ end
73
+
74
+ node['normal']['provisioner_output'] = provisioner_output
75
+
76
+ # Create machine object for callers to use
77
+ machine_for(node)
78
+ end
79
+
80
+ # Connect to machine without acquiring it
81
+ def connect_to_machine(node)
82
+ machine_for(node)
83
+ end
84
+
85
+ def delete_machine(provider, node)
86
+ if node['normal'] && node['normal']['provisioner_output']
87
+ provisioner_output = node['normal']['provisioner_output']
88
+ ct = LXC::Container.new(provisioner_output['name'], lxc_path_for(node))
89
+ if ct.defined?
90
+ provider.converge_by "delete lxc container #{provisioner_output['name']}" do
91
+ ct.destroy
92
+ end
93
+ end
94
+ end
95
+ convergence_strategy_for(node).delete_chef_objects(provider, node)
96
+ end
97
+
98
+ def stop_machine(provider, node)
99
+ provisioner_options = node['normal']['provisioner_options']
100
+ if node['normal'] && node['normal']['provisioner_output']
101
+ provisioner_output = node['normal']['provisioner_output']
102
+ ct = LXC::Container.new(provisioner_output['name'], lxc_path_for(node))
103
+ if ct.running?
104
+ provider.converge_by "delete lxc container #{provisioner_output['name']}" do
105
+ ct.stop
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ protected
112
+
113
+ def lxc_path_for(node)
114
+ provisioner_options = node['normal']['provisioner_options']
115
+ provisioner_options['lxc_path'] || LXC.global_config_item('lxc.lxcpath')
116
+ end
117
+
118
+ def machine_for(node)
119
+ require 'chef_metal/machine/unix_machine'
120
+ ChefMetal::Machine::UnixMachine.new(node, transport_for(node), convergence_strategy_for(node))
121
+ end
122
+
123
+ def convergence_strategy_for(node)
124
+ require 'chef_metal/convergence_strategy/install_sh'
125
+ ChefMetal::ConvergenceStrategy::InstallSh.new
126
+ end
127
+
128
+ def transport_for(node)
129
+ require 'chef_metal/transport/lxc'
130
+ provisioner_output = node['normal']['provisioner_output']
131
+ ChefMetal::Transport::LXCTransport.new(provisioner_output['name'], lxc_path_for(node))
132
+ end
133
+ end
134
+ end
135
+ end
@@ -19,8 +19,12 @@ class Chef
19
19
  ChefMetal.with_vagrant_box(box_name, vagrant_options, &block)
20
20
  end
21
21
 
22
+ def with_fog_provisioner(options = {}, &block)
23
+ ChefMetal.with_provisioner(ChefMetal::Provisioner::FogProvisioner.new(options, &block))
24
+ end
25
+
22
26
  def with_fog_ec2_provisioner(options = {}, &block)
23
- ChefMetal.with_provisioner(ChefMetal::Provisioner::FogProvisioner.new({ :provider => 'AWS' }.merge(options)), &block)
27
+ with_fog_provisioner({ :provider => 'AWS' }.merge(options), &block)
24
28
  end
25
29
  end
26
30
  end
@@ -0,0 +1,90 @@
1
+ require 'chef_metal/transport'
2
+ require 'lxc/extra'
3
+ require 'chef/mixin/shell_out'
4
+
5
+ module ChefMetal
6
+ class Transport
7
+ class LXCTransport < Transport
8
+
9
+ class LXCExecuteResult < Struct.new(:stdout, :stderr, :exitstatus)
10
+ def error!
11
+ raise "Error: code #{exitstatus}.\nSTDOUT:#{stdout}\nSTDERR:#{stderr}" if exitstatus != 0
12
+ end
13
+ end
14
+
15
+ attr_reader :name, :options, :lxc_path
16
+
17
+ include Chef::Mixin::ShellOut
18
+
19
+ def initialize(name, lxc_path, options={})
20
+ @options = options
21
+ @name = name
22
+ @lxc_path = lxc_path
23
+ end
24
+
25
+ def ct
26
+ @container ||= LXC::Container.new(name, lxc_path)
27
+ end
28
+
29
+ def rootfs
30
+ ct.config_item('lxc.rootfs')
31
+ end
32
+
33
+ def ct_path(path)
34
+ File.join(rootfs, path)
35
+ end
36
+
37
+ def execute(command)
38
+ Chef::Log.info("Executing #{command} on #{name}")
39
+ res = ct.execute do
40
+ begin
41
+ out = shell_out(command)
42
+ LXCExecuteResult.new(out.stdout,out.stderr, out.exitstatus)
43
+ rescue Exception => e
44
+ LXCExecuteResult.new('', e.message, -1)
45
+ end
46
+ end
47
+ res
48
+ end
49
+
50
+ def forward_remote_port_to_local(remote_port, local_port)
51
+ warn 'Port forwarding is not implemented in lxc transport'
52
+ warn "You can do this on host using:"
53
+ warn " 'iptables -t nat -A PREROUTING -p tcp --dport #{remote_port} -j DNAT --to #{ct.ip_addresses.first}:#{local_port}'"
54
+ end
55
+
56
+ def read_file(path)
57
+ if File.exists?(ct_path(path))
58
+ File.read(ct_path(path))
59
+ end
60
+ end
61
+
62
+ def download_file(path, local_path)
63
+ Chef::Log.debug("Copying file #{path} from #{name} to local #{local_path}")
64
+ FileUtils.cp_r(ct_path(path), local_path)
65
+ end
66
+
67
+ def write_file(path, content)
68
+ File.open(ct_path(path), 'w') do |f|
69
+ f.write(content)
70
+ end
71
+ end
72
+
73
+ def upload_file(local_path, path)
74
+ FileUtils.cp_r(local_path, ct_path(path))
75
+ end
76
+
77
+ def disconnect
78
+ end
79
+
80
+ def available?
81
+ begin
82
+ execute('pwd')
83
+ true
84
+ rescue Exception =>e
85
+ false
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,27 @@
1
+ require 'chef_metal'
2
+ require 'chef/resource/vagrant_cluster'
3
+ require 'chef/provider/vagrant_cluster'
4
+ require 'chef/resource/vagrant_box'
5
+ require 'chef/provider/vagrant_box'
6
+ require 'chef_metal/provisioner/vagrant_provisioner'
7
+
8
+ module ChefMetal
9
+ def self.with_vagrant_cluster(cluster_path, &block)
10
+ with_provisioner(ChefMetal::Provisioner::VagrantProvisioner.new(cluster_path), &block)
11
+ end
12
+
13
+ def self.with_vagrant_box(box_name, provisioner_options = nil, &block)
14
+ if box_name.is_a?(Chef::Resource::VagrantBox)
15
+ provisioner_options ||= box_name.provisioner_options || {}
16
+ provisioner_options['vagrant_options'] ||= {}
17
+ provisioner_options['vagrant_options']['vm.box'] = box_name.name
18
+ provisioner_options['vagrant_options']['vm.box_url'] = box_name.url if box_name.url
19
+ else
20
+ provisioner_options ||= {}
21
+ provisioner_options['vagrant_options'] ||= {}
22
+ provisioner_options['vagrant_options']['vm.box'] = box_name
23
+ end
24
+
25
+ with_provisioner_options(provisioner_options, &block)
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module ChefMetal
2
- VERSION = '0.1'
2
+ VERSION = '0.2'
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.1'
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Keiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-21 00:00:00.000000000 Z
11
+ date: 2014-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ~>
81
81
  - !ruby/object:Gem::Version
82
82
  version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: ruby-lxc
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rspec
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -134,18 +148,22 @@ files:
134
148
  - lib/chef_metal/convergence_strategy/install_sh.rb
135
149
  - lib/chef_metal/convergence_strategy/precreate_chef_objects.rb
136
150
  - lib/chef_metal/convergence_strategy.rb
151
+ - lib/chef_metal/fog.rb
137
152
  - lib/chef_metal/inline_resource.rb
138
153
  - lib/chef_metal/machine/basic_machine.rb
139
154
  - lib/chef_metal/machine/unix_machine.rb
140
155
  - lib/chef_metal/machine/windows_machine.rb
141
156
  - lib/chef_metal/machine.rb
142
157
  - lib/chef_metal/provisioner/fog_provisioner.rb
158
+ - lib/chef_metal/provisioner/lxc_provisioner.rb
143
159
  - lib/chef_metal/provisioner/vagrant_provisioner.rb
144
160
  - lib/chef_metal/provisioner.rb
145
161
  - lib/chef_metal/recipe_dsl.rb
162
+ - lib/chef_metal/transport/lxc.rb
146
163
  - lib/chef_metal/transport/ssh.rb
147
164
  - lib/chef_metal/transport/winrm.rb
148
165
  - lib/chef_metal/transport.rb
166
+ - lib/chef_metal/vagrant.rb
149
167
  - lib/chef_metal/version.rb
150
168
  - lib/chef_metal.rb
151
169
  homepage: http://wiki.opscode.com/display/chef