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