chef-metal 0.1
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 +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
|