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.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +160 -0
  4. data/Rakefile +6 -0
  5. data/lib/chef/provider/fog_key_pair.rb +106 -0
  6. data/lib/chef/provider/machine.rb +60 -0
  7. data/lib/chef/provider/machine_file.rb +39 -0
  8. data/lib/chef/provider/vagrant_box.rb +44 -0
  9. data/lib/chef/provider/vagrant_cluster.rb +39 -0
  10. data/lib/chef/resource/fog_key_pair.rb +34 -0
  11. data/lib/chef/resource/machine.rb +56 -0
  12. data/lib/chef/resource/machine_file.rb +25 -0
  13. data/lib/chef/resource/vagrant_box.rb +18 -0
  14. data/lib/chef/resource/vagrant_cluster.rb +16 -0
  15. data/lib/chef_metal.rb +84 -0
  16. data/lib/chef_metal/aws_credentials.rb +55 -0
  17. data/lib/chef_metal/convergence_strategy.rb +15 -0
  18. data/lib/chef_metal/convergence_strategy/install_msi.rb +41 -0
  19. data/lib/chef_metal/convergence_strategy/install_sh.rb +36 -0
  20. data/lib/chef_metal/convergence_strategy/precreate_chef_objects.rb +140 -0
  21. data/lib/chef_metal/inline_resource.rb +88 -0
  22. data/lib/chef_metal/machine.rb +79 -0
  23. data/lib/chef_metal/machine/basic_machine.rb +79 -0
  24. data/lib/chef_metal/machine/unix_machine.rb +108 -0
  25. data/lib/chef_metal/machine/windows_machine.rb +94 -0
  26. data/lib/chef_metal/provisioner.rb +71 -0
  27. data/lib/chef_metal/provisioner/fog_provisioner.rb +378 -0
  28. data/lib/chef_metal/provisioner/vagrant_provisioner.rb +327 -0
  29. data/lib/chef_metal/recipe_dsl.rb +26 -0
  30. data/lib/chef_metal/transport.rb +36 -0
  31. data/lib/chef_metal/transport/ssh.rb +157 -0
  32. data/lib/chef_metal/transport/winrm.rb +91 -0
  33. data/lib/chef_metal/version.rb +3 -0
  34. 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
@@ -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