chef-metal 0.1 → 0.2

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