chef-metal 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +160 -0
- data/Rakefile +6 -0
- data/lib/chef/provider/fog_key_pair.rb +106 -0
- data/lib/chef/provider/machine.rb +60 -0
- data/lib/chef/provider/machine_file.rb +39 -0
- data/lib/chef/provider/vagrant_box.rb +44 -0
- data/lib/chef/provider/vagrant_cluster.rb +39 -0
- data/lib/chef/resource/fog_key_pair.rb +34 -0
- data/lib/chef/resource/machine.rb +56 -0
- data/lib/chef/resource/machine_file.rb +25 -0
- data/lib/chef/resource/vagrant_box.rb +18 -0
- data/lib/chef/resource/vagrant_cluster.rb +16 -0
- data/lib/chef_metal.rb +84 -0
- data/lib/chef_metal/aws_credentials.rb +55 -0
- data/lib/chef_metal/convergence_strategy.rb +15 -0
- data/lib/chef_metal/convergence_strategy/install_msi.rb +41 -0
- data/lib/chef_metal/convergence_strategy/install_sh.rb +36 -0
- data/lib/chef_metal/convergence_strategy/precreate_chef_objects.rb +140 -0
- data/lib/chef_metal/inline_resource.rb +88 -0
- data/lib/chef_metal/machine.rb +79 -0
- data/lib/chef_metal/machine/basic_machine.rb +79 -0
- data/lib/chef_metal/machine/unix_machine.rb +108 -0
- data/lib/chef_metal/machine/windows_machine.rb +94 -0
- data/lib/chef_metal/provisioner.rb +71 -0
- data/lib/chef_metal/provisioner/fog_provisioner.rb +378 -0
- data/lib/chef_metal/provisioner/vagrant_provisioner.rb +327 -0
- data/lib/chef_metal/recipe_dsl.rb +26 -0
- data/lib/chef_metal/transport.rb +36 -0
- data/lib/chef_metal/transport/ssh.rb +157 -0
- data/lib/chef_metal/transport/winrm.rb +91 -0
- data/lib/chef_metal/version.rb +3 -0
- metadata +175 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'chef/provider/lwrp_base'
|
2
|
+
require 'chef/provider/chef_node'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
class Chef::Provider::Machine < Chef::Provider::LWRPBase
|
6
|
+
|
7
|
+
use_inline_resources
|
8
|
+
|
9
|
+
def whyrun_supported?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
action :create do
|
14
|
+
node_json = node_provider.new_json
|
15
|
+
node_json['normal']['provisioner_options'] = new_resource.provisioner_options
|
16
|
+
machine = new_resource.provisioner.acquire_machine(self, node_json)
|
17
|
+
begin
|
18
|
+
machine.setup_convergence(self, new_resource)
|
19
|
+
# If we were asked to converge, or anything changed, or if a converge has never succeeded, converge.
|
20
|
+
if new_resource.converge || (new_resource.converge.nil? && resource_updated?) ||
|
21
|
+
!node_json['automatic'] || node_json['automatic'].size == 0
|
22
|
+
machine.converge(self)
|
23
|
+
end
|
24
|
+
ensure
|
25
|
+
machine.disconnect
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
action :converge do
|
30
|
+
node_json = node_provider.new_json
|
31
|
+
node_json['normal']['provisioner_options'] = new_resource.provisioner_options
|
32
|
+
machine = new_resource.provisioner.connect_to_machine(node_json)
|
33
|
+
begin
|
34
|
+
machine.converge(self)
|
35
|
+
ensure
|
36
|
+
machine.disconnect
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
action :stop do
|
41
|
+
node_json = node_provider.new_json
|
42
|
+
node_json['normal']['provisioner_options'] = new_resource.provisioner_options
|
43
|
+
new_resource.provisioner.stop_machine(self, node_json)
|
44
|
+
end
|
45
|
+
|
46
|
+
action :delete do
|
47
|
+
# Grab the node json by asking the provider for it
|
48
|
+
node_data = node_provider.current_json
|
49
|
+
|
50
|
+
# Destroy the machine
|
51
|
+
new_resource.provisioner.delete_machine(self, node_data)
|
52
|
+
end
|
53
|
+
|
54
|
+
attr_reader :node_provider
|
55
|
+
|
56
|
+
def load_current_resource
|
57
|
+
@node_provider = Chef::Provider::ChefNode.new(new_resource, nil)
|
58
|
+
@node_provider.load_current_resource
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'chef/provider/lwrp_base'
|
2
|
+
require 'cheffish/cheffish_server_api'
|
3
|
+
|
4
|
+
class Chef::Provider::MachineFile < Chef::Provider::LWRPBase
|
5
|
+
|
6
|
+
use_inline_resources
|
7
|
+
|
8
|
+
def whyrun_supported?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def machine
|
13
|
+
@machine ||= begin
|
14
|
+
if new_resource.machine.kind_of?(ChefMetal::Machine)
|
15
|
+
new_resource.machine
|
16
|
+
else
|
17
|
+
# TODO this is inefficient, can we cache or something?
|
18
|
+
node = Cheffish::CheffishServerAPI.new(new_resource.chef_server).get("/nodes/#{new_resource.machine}")
|
19
|
+
new_resource.provisioner.connect_to_machine(node)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
action :upload do
|
25
|
+
if new_resource.content
|
26
|
+
machine.write_file(self, new_resource.path, new_resource.content)
|
27
|
+
else
|
28
|
+
machine.upload_file(self, new_resource.local_path, new_resource.path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
action :download do
|
33
|
+
machine.download_file(provider, new_resource.path, new_resource.local_path)
|
34
|
+
end
|
35
|
+
|
36
|
+
action :delete do
|
37
|
+
machine.delete_file(self, new_resource.path)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'chef/provider/lwrp_base'
|
2
|
+
require 'chef/mixin/shell_out'
|
3
|
+
|
4
|
+
class Chef::Provider::VagrantBox < Chef::Provider::LWRPBase
|
5
|
+
|
6
|
+
use_inline_resources
|
7
|
+
|
8
|
+
include Chef::Mixin::ShellOut
|
9
|
+
|
10
|
+
def whyrun_supported?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
action :create do
|
15
|
+
if !list_boxes.has_key?(new_resource.name)
|
16
|
+
if new_resource.url
|
17
|
+
converge_by "run 'vagrant box add #{new_resource.name} #{new_resource.url}'" do
|
18
|
+
shell_out("vagrant box add #{new_resource.name} #{new_resource.url}").error!
|
19
|
+
end
|
20
|
+
else
|
21
|
+
raise "Box #{new_resource.name} does not exist"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
action :delete do
|
27
|
+
if list_boxes.has_key?(new_resource.name)
|
28
|
+
converge_by "run 'vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]}'" do
|
29
|
+
shell_out("vagrant box remove #{new_resource.name} #{list_boxes[new_resource.name]}").error!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def list_boxes
|
35
|
+
@list_boxes ||= shell_out("vagrant box list").stdout.lines.inject({}) do |result, line|
|
36
|
+
line =~ /^(\S+)\s+\((.+)\)\s*$/
|
37
|
+
result[$1] = $2
|
38
|
+
result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_current_resource
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'chef/provider/lwrp_base'
|
2
|
+
|
3
|
+
class Chef::Provider::VagrantCluster < Chef::Provider::LWRPBase
|
4
|
+
|
5
|
+
use_inline_resources
|
6
|
+
|
7
|
+
def whyrun_supported?
|
8
|
+
true
|
9
|
+
end
|
10
|
+
|
11
|
+
action :create do
|
12
|
+
the_base_path = new_resource.path
|
13
|
+
ChefMetal.inline_resource(self) do
|
14
|
+
directory the_base_path
|
15
|
+
file ::File.join(the_base_path, 'Vagrantfile') do
|
16
|
+
content <<EOM
|
17
|
+
Dir.glob('#{::File.join(the_base_path, '*.vm')}') do |vm_file|
|
18
|
+
eval(IO.read(vm_file), nil, vm_file)
|
19
|
+
end
|
20
|
+
EOM
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
action :delete do
|
26
|
+
the_base_path = new_resource.path
|
27
|
+
ChefMetal.inline_resource(self) do
|
28
|
+
file ::File.join(the_base_path, 'Vagrantfile') do
|
29
|
+
action :delete
|
30
|
+
end
|
31
|
+
directory the_base_path do
|
32
|
+
action :delete
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_current_resource
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'chef_metal'
|
2
|
+
|
3
|
+
class Chef::Resource::FogKeyPair < Chef::Resource::LWRPBase
|
4
|
+
self.resource_name = 'fog_key_pair'
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
@provisioner = ChefMetal.enclosing_provisioner
|
9
|
+
end
|
10
|
+
|
11
|
+
def after_created
|
12
|
+
# Make the credentials usable
|
13
|
+
provisioner.key_pairs[name] = self
|
14
|
+
end
|
15
|
+
|
16
|
+
actions :create, :delete, :nothing
|
17
|
+
default_action :create
|
18
|
+
|
19
|
+
attribute :provisioner
|
20
|
+
# Private key to use as input (will be generated if it does not exist)
|
21
|
+
attribute :private_key_path, :kind_of => String
|
22
|
+
# Public key to use as input (will be generated if it does not exist)
|
23
|
+
attribute :public_key_path, :kind_of => String
|
24
|
+
# List of parameters to the private_key resource used for generation of the key
|
25
|
+
attribute :private_key_options, :kind_of => Hash
|
26
|
+
|
27
|
+
# TODO what is the right default for this?
|
28
|
+
attribute :allow_overwrite, :kind_of => [TrueClass, FalseClass], :default => false
|
29
|
+
|
30
|
+
# Proc that runs after the resource completes. Called with (resource, private_key, public_key)
|
31
|
+
def after(&block)
|
32
|
+
block ? @after = block : @after
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'chef/resource/lwrp_base'
|
2
|
+
require 'cheffish'
|
3
|
+
require 'chef_metal'
|
4
|
+
|
5
|
+
class Chef::Resource::Machine < Chef::Resource::LWRPBase
|
6
|
+
self.resource_name = 'machine'
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
@chef_environment = Cheffish.enclosing_environment
|
11
|
+
@chef_server = Cheffish.enclosing_chef_server
|
12
|
+
@provisioner = ChefMetal.enclosing_provisioner
|
13
|
+
@provisioner_options = ChefMetal.enclosing_provisioner_options
|
14
|
+
end
|
15
|
+
|
16
|
+
def after_created
|
17
|
+
# Notify the provisioner of this machine's creation
|
18
|
+
@provisioner.resource_created(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
actions :create, :delete, :stop, :converge, :nothing
|
22
|
+
default_action :create
|
23
|
+
|
24
|
+
# Provisioner attributes
|
25
|
+
attribute :provisioner
|
26
|
+
attribute :provisioner_options
|
27
|
+
|
28
|
+
# Node attributes
|
29
|
+
Cheffish.node_attributes(self)
|
30
|
+
|
31
|
+
# Client keys
|
32
|
+
# Options to generate private key (size, type, etc.) when the server doesn't have it
|
33
|
+
attribute :private_key_options, :kind_of => String
|
34
|
+
|
35
|
+
# Optionally pull the public key out to a file
|
36
|
+
attribute :public_key_path, :kind_of => String
|
37
|
+
attribute :public_key_format, :kind_of => String
|
38
|
+
|
39
|
+
# If you really want to force the private key to be a certain key, pass these
|
40
|
+
attribute :source_key
|
41
|
+
attribute :source_key_path, :kind_of => String
|
42
|
+
attribute :source_key_pass_phrase
|
43
|
+
|
44
|
+
# Client attributes
|
45
|
+
attribute :admin, :kind_of => [TrueClass, FalseClass]
|
46
|
+
attribute :validator, :kind_of => [TrueClass, FalseClass]
|
47
|
+
|
48
|
+
# Allows you to turn convergence off in the :create action by writing "converge false"
|
49
|
+
# or force it with "true"
|
50
|
+
attribute :converge, :kind_of => [TrueClass, FalseClass]
|
51
|
+
|
52
|
+
# chef client version and omnibus
|
53
|
+
# chef-zero boot method?
|
54
|
+
# chef-client -z boot method?
|
55
|
+
# pushy boot method?
|
56
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'chef/resource/lwrp_base'
|
2
|
+
require 'chef_metal'
|
3
|
+
require 'chef_metal/machine'
|
4
|
+
require 'chef_metal/provisioner'
|
5
|
+
|
6
|
+
class Chef::Resource::MachineFile < Chef::Resource::LWRPBase
|
7
|
+
self.resource_name = 'machine_file'
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
super
|
11
|
+
@chef_server = Cheffish.enclosing_chef_server
|
12
|
+
@provisioner = ChefMetal.enclosing_provisioner
|
13
|
+
end
|
14
|
+
|
15
|
+
actions :upload, :download, :delete, :nothing
|
16
|
+
default_action :upload
|
17
|
+
|
18
|
+
attribute :path, :kind_of => String, :name_attribute => true
|
19
|
+
attribute :machine, :kind_of => [String, ChefMetal::Machine]
|
20
|
+
attribute :local_path, :kind_of => String
|
21
|
+
attribute :content
|
22
|
+
|
23
|
+
attribute :chef_server, :kind_of => Hash
|
24
|
+
attribute :provisioner, :kind_of => ChefMetal::Provisioner
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'chef/resource/lwrp_base'
|
2
|
+
require 'chef_metal/provisioner/vagrant_provisioner'
|
3
|
+
|
4
|
+
class Chef::Resource::VagrantBox < Chef::Resource::LWRPBase
|
5
|
+
self.resource_name = 'vagrant_box'
|
6
|
+
|
7
|
+
actions :create, :delete, :nothing
|
8
|
+
default_action :create
|
9
|
+
|
10
|
+
attribute :name, :kind_of => String, :name_attribute => true
|
11
|
+
attribute :url, :kind_of => String
|
12
|
+
attribute :provisioner_options, :kind_of => Hash
|
13
|
+
|
14
|
+
def after_created
|
15
|
+
super
|
16
|
+
ChefMetal.with_vagrant_box self
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'chef/resource/lwrp_base'
|
2
|
+
require 'chef_metal'
|
3
|
+
|
4
|
+
class Chef::Resource::VagrantCluster < Chef::Resource::LWRPBase
|
5
|
+
self.resource_name = 'vagrant_cluster'
|
6
|
+
|
7
|
+
actions :create, :delete, :nothing
|
8
|
+
default_action :create
|
9
|
+
|
10
|
+
attribute :path, :kind_of => String, :name_attribute => true
|
11
|
+
|
12
|
+
def after_created
|
13
|
+
super
|
14
|
+
ChefMetal.with_vagrant_cluster path
|
15
|
+
end
|
16
|
+
end
|
data/lib/chef_metal.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# Include recipe basics so require 'chef_metal' will load everything
|
2
|
+
require 'chef_metal/recipe_dsl'
|
3
|
+
require 'chef/resource/machine'
|
4
|
+
require 'chef/provider/machine'
|
5
|
+
require 'chef/resource/machine_file'
|
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
|
+
|
15
|
+
require 'chef_metal/inline_resource'
|
16
|
+
|
17
|
+
module ChefMetal
|
18
|
+
def self.with_provisioner(provisioner)
|
19
|
+
old_provisioner = ChefMetal.enclosing_provisioner
|
20
|
+
ChefMetal.enclosing_provisioner = provisioner
|
21
|
+
if block_given?
|
22
|
+
begin
|
23
|
+
yield
|
24
|
+
ensure
|
25
|
+
ChefMetal.enclosing_provisioner = old_provisioner
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.with_provisioner_options(provisioner_options)
|
31
|
+
old_provisioner_options = ChefMetal.enclosing_provisioner_options
|
32
|
+
ChefMetal.enclosing_provisioner_options = provisioner_options
|
33
|
+
if block_given?
|
34
|
+
begin
|
35
|
+
yield
|
36
|
+
ensure
|
37
|
+
ChefMetal.enclosing_provisioner_options = old_provisioner_options
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
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
|
+
def self.inline_resource(provider, &block)
|
66
|
+
InlineResource.new(provider).instance_eval(&block)
|
67
|
+
end
|
68
|
+
|
69
|
+
@@enclosing_provisioner = nil
|
70
|
+
def self.enclosing_provisioner
|
71
|
+
@@enclosing_provisioner
|
72
|
+
end
|
73
|
+
def self.enclosing_provisioner=(provisioner)
|
74
|
+
@@enclosing_provisioner = provisioner
|
75
|
+
end
|
76
|
+
|
77
|
+
@@enclosing_provisioner_options = nil
|
78
|
+
def self.enclosing_provisioner_options
|
79
|
+
@@enclosing_provisioner_options
|
80
|
+
end
|
81
|
+
def self.enclosing_provisioner_options=(provisioner_options)
|
82
|
+
@@enclosing_provisioner_options = provisioner_options
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ChefMetal
|
2
|
+
# Reads in a credentials file in Amazon's download format and presents the credentials to you
|
3
|
+
class AWSCredentials
|
4
|
+
def initialize
|
5
|
+
@credentials = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def default
|
9
|
+
@credentials['default'] || @credentials.first[1]
|
10
|
+
end
|
11
|
+
|
12
|
+
def keys
|
13
|
+
@credentials.keys
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](name)
|
17
|
+
@credentials[name]
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_ini(credentials_ini_file)
|
21
|
+
require 'inifile'
|
22
|
+
inifile = IniFile.load(File.expand_path(credentials_ini_file))
|
23
|
+
inifile.each_section do |section|
|
24
|
+
@credentials[section] = {
|
25
|
+
:access_key_id => inifile[section]['aws_access_key_id'],
|
26
|
+
:secret_access_key => inifile[section]['aws_secret_access_key'],
|
27
|
+
:region => inifile[section]['region']
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_csv(credentials_csv_file)
|
33
|
+
require 'csv'
|
34
|
+
CSV.new(File.open(credentials_csv_file), :headers => :first_row).each do |row|
|
35
|
+
@credentials[row['User Name']] = {
|
36
|
+
:user_name => row['User Name'],
|
37
|
+
:access_key_id => row['Access Key Id'],
|
38
|
+
:secret_access_key => row['Secret Access Key']
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_default
|
44
|
+
load_ini('~/.aws/config')
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.method_missing(name, *args, &block)
|
48
|
+
singleton.send(name, *args, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.singleton
|
52
|
+
@aws_credentials ||= AWSCredentials.new
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|