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 +4 -4
- data/README.md +8 -8
- data/lib/chef/provider/fog_key_pair.rb +53 -9
- data/lib/chef_metal.rb +0 -30
- data/lib/chef_metal/fog.rb +4 -0
- data/lib/chef_metal/provisioner/fog_provisioner.rb +55 -22
- data/lib/chef_metal/provisioner/lxc_provisioner.rb +135 -0
- data/lib/chef_metal/recipe_dsl.rb +5 -1
- data/lib/chef_metal/transport/lxc.rb +90 -0
- data/lib/chef_metal/vagrant.rb +27 -0
- data/lib/chef_metal/version.rb +1 -1
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a47fa35091e77e37a5ee21d98a91aa77438f2193
|
4
|
+
data.tar.gz: fee0d677dd4bb0503179fa71784a75069ab2928b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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/
|
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/
|
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/
|
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/
|
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/
|
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/
|
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/
|
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
|
-
|
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
|
-
|
41
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
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
|
@@ -54,14 +54,12 @@ module ChefMetal
|
|
54
54
|
|
55
55
|
def current_base_bootstrap_options
|
56
56
|
result = @base_bootstrap_options.dup
|
57
|
-
if
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
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[
|
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 =>
|
360
|
+
# :paranoid => true,
|
337
361
|
:auth_methods => [ 'publickey' ],
|
338
|
-
:
|
339
|
-
:
|
362
|
+
:keys_only => true,
|
363
|
+
:host_key_alias => "#{server.id}.#{compute_options[:provider]}"
|
340
364
|
}
|
341
|
-
if
|
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]
|
344
|
-
result[:host_key_alias] = "#{server.id}.ec2"
|
369
|
+
result[:keys] ||= [ key_pairs[server.key_name].private_key_path ]
|
345
370
|
else
|
346
|
-
|
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
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
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
|
-
|
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
|
data/lib/chef_metal/version.rb
CHANGED
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.
|
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:
|
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
|