clc-fork-chef-metal 0.11.beta.5

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 (38) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +106 -0
  3. data/LICENSE +201 -0
  4. data/README.md +201 -0
  5. data/Rakefile +6 -0
  6. data/bin/metal +276 -0
  7. data/lib/chef/provider/machine.rb +147 -0
  8. data/lib/chef/provider/machine_batch.rb +130 -0
  9. data/lib/chef/provider/machine_execute.rb +30 -0
  10. data/lib/chef/provider/machine_file.rb +49 -0
  11. data/lib/chef/resource/machine.rb +95 -0
  12. data/lib/chef/resource/machine_batch.rb +20 -0
  13. data/lib/chef/resource/machine_execute.rb +22 -0
  14. data/lib/chef/resource/machine_file.rb +28 -0
  15. data/lib/chef_metal.rb +62 -0
  16. data/lib/chef_metal/action_handler.rb +63 -0
  17. data/lib/chef_metal/add_prefix_action_handler.rb +29 -0
  18. data/lib/chef_metal/chef_machine_spec.rb +64 -0
  19. data/lib/chef_metal/chef_provider_action_handler.rb +72 -0
  20. data/lib/chef_metal/chef_run_data.rb +80 -0
  21. data/lib/chef_metal/convergence_strategy.rb +26 -0
  22. data/lib/chef_metal/convergence_strategy/install_cached.rb +157 -0
  23. data/lib/chef_metal/convergence_strategy/install_msi.rb +56 -0
  24. data/lib/chef_metal/convergence_strategy/install_sh.rb +51 -0
  25. data/lib/chef_metal/convergence_strategy/no_converge.rb +38 -0
  26. data/lib/chef_metal/convergence_strategy/precreate_chef_objects.rb +180 -0
  27. data/lib/chef_metal/driver.rb +267 -0
  28. data/lib/chef_metal/machine.rb +110 -0
  29. data/lib/chef_metal/machine/basic_machine.rb +82 -0
  30. data/lib/chef_metal/machine/unix_machine.rb +276 -0
  31. data/lib/chef_metal/machine/windows_machine.rb +102 -0
  32. data/lib/chef_metal/machine_spec.rb +78 -0
  33. data/lib/chef_metal/recipe_dsl.rb +84 -0
  34. data/lib/chef_metal/transport.rb +87 -0
  35. data/lib/chef_metal/transport/ssh.rb +235 -0
  36. data/lib/chef_metal/transport/winrm.rb +109 -0
  37. data/lib/chef_metal/version.rb +3 -0
  38. metadata +223 -0
@@ -0,0 +1,72 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Douglas Triggs (<doug@getchef.com>)
4
+ #
5
+ # Copyright (C) 2014, Chef, Inc.
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require 'chef_metal/action_handler'
20
+
21
+ # This is included in the metal drivers to proxy from generic requests needed
22
+ # to specific driver actions
23
+ module ChefMetal
24
+ class ChefProviderActionHandler < ActionHandler
25
+ def initialize(provider)
26
+ @provider = provider
27
+ end
28
+
29
+ attr_reader :provider
30
+
31
+ def updated!
32
+ provider.new_resource.updated_by_last_action(true)
33
+ end
34
+
35
+ def should_perform_actions
36
+ !provider.run_context.config.why_run
37
+ end
38
+
39
+ def report_progress(description)
40
+ # TODO this seems wrong but Chef doesn't have another thing
41
+ provider.converge_by description do
42
+ # We already did the action, but we trust whoever told us that they did it.
43
+ end
44
+ end
45
+
46
+ def performed_action(description)
47
+ provider.converge_by description do
48
+ # We already did the action, but we trust whoever told us that they did it.
49
+ end
50
+ end
51
+
52
+ def perform_action(description, &block)
53
+ provider.converge_by(description, &block)
54
+ end
55
+
56
+ def open_stream(name, &block)
57
+ if provider.run_context.respond_to?(:open_stream)
58
+ provider.run_context.open_stream({ :name => name }, &block)
59
+ else
60
+ if block_given?
61
+ yield STDOUT
62
+ else
63
+ STDOUT
64
+ end
65
+ end
66
+ end
67
+
68
+ def host_node
69
+ "#{provider.run_context.config[:chef_server_url]}/nodes/#{provider.run_context.node['name']}"
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,80 @@
1
+ require 'chef/mixin/deep_merge'
2
+ require 'cheffish/with_pattern'
3
+ require 'cheffish/merged_config'
4
+ require 'chef_metal/chef_machine_spec'
5
+
6
+ module ChefMetal
7
+ class ChefRunData
8
+ extend Cheffish::WithPattern
9
+ def initialize(config)
10
+ @config = config
11
+ @drivers = {}
12
+ end
13
+
14
+ attr_reader :config
15
+ attr_reader :drivers
16
+
17
+ with :driver
18
+ with :machine_options
19
+ with :machine_batch
20
+
21
+ def current_driver
22
+ @current_driver || config[:driver]
23
+ end
24
+
25
+ def current_machine_options
26
+ if @current_machine_options
27
+ @current_machine_options
28
+ elsif config[:drivers] && driver_for(current_driver) && config[:drivers][driver_for(current_driver).driver_url]
29
+ Cheffish::MergedConfig.new(config[:drivers][driver_for(current_driver).driver_url], config)[:machine_options] || {}
30
+ else
31
+ config[:machine_options] || {}
32
+ end
33
+ end
34
+
35
+ def add_machine_options(options, &block)
36
+ with_machine_options(Chef::Mixin::DeepMerge.hash_only_merge(current_machine_options, options), &block)
37
+ end
38
+
39
+ def driver_for(driver)
40
+ driver.is_a?(String) ? driver_for_url(driver) : driver
41
+ end
42
+
43
+ def driver_config_for(driver)
44
+ ChefMetal.config_for_url(driver_for(driver).driver_url, config)
45
+ end
46
+
47
+ def driver_for_url(driver_url)
48
+ drivers[driver_url] ||= begin
49
+ driver = ChefMetal.driver_for_url(driver_url, config)
50
+ # Check the canonicalized driver_url from the driver
51
+ if driver.driver_url != driver_url
52
+ drivers[driver.driver_url] ||= driver
53
+ else
54
+ driver
55
+ end
56
+ end
57
+ end
58
+
59
+ def connect_to_machine(name, chef_server = nil)
60
+ if name.is_a?(MachineSpec)
61
+ machine_spec = name
62
+ else
63
+ machine_spec = ChefMetal::ChefMachineSpec.get(name, chef_server)
64
+ end
65
+ ChefMetal.connect_to_machine(machine_spec, config)
66
+ end
67
+
68
+ def keys
69
+ result = (config.keys || {}).dup
70
+ Array(config.key_path) do |key_path|
71
+ Dir.entries(key_path).each do |key|
72
+ if File.extname(key) == '.pem'
73
+ result[File.basename(key)[0..-5]] ||= key
74
+ end
75
+ end
76
+ end
77
+ result
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,26 @@
1
+ module ChefMetal
2
+ class ConvergenceStrategy
3
+ # convergence_options - a freeform hash of options to the converger.
4
+ # config - a Chef::Config-like object with global config like :log_level
5
+ def initialize(convergence_options, config)
6
+ @convergence_options = convergence_options || {}
7
+ @config = config
8
+ end
9
+
10
+ attr_reader :convergence_options
11
+ attr_reader :config
12
+
13
+ # Get the machine ready to converge, but do not converge.
14
+ def setup_convergence(action_handler, machine)
15
+ raise "setup_convergence not overridden on #{self.class}"
16
+ end
17
+
18
+ def converge(action_handler, machine)
19
+ raise "converge not overridden on #{self.class}"
20
+ end
21
+
22
+ def cleanup_convergence(action_handler, machine_spec)
23
+ raise "cleanup_convergence not overridden on #{self.class}"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,157 @@
1
+ require 'chef_metal/convergence_strategy/precreate_chef_objects'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+ require 'digest/md5'
5
+ require 'thread'
6
+
7
+ module ChefMetal
8
+ class ConvergenceStrategy
9
+ class InstallCached < PrecreateChefObjects
10
+ # convergence_options is a hash of setup convergence_options, including:
11
+ # - :chef_server
12
+ # - :allow_overwrite_keys
13
+ # - :source_key, :source_key_path, :source_key_pass_phrase
14
+ # - :private_key_options
15
+ # - :ohai_hints
16
+ # - :public_key_path, :public_key_format
17
+ # - :admin, :validator
18
+ # - :chef_client_timeout
19
+ # - :client_rb_path, :client_pem_path
20
+ # - :chef_version, :prerelease, :package_cache_path
21
+ def initialize(convergence_options, config)
22
+ convergence_options = Cheffish::MergedConfig.new(convergence_options, {
23
+ :client_rb_path => '/etc/chef/client.rb',
24
+ :client_pem_path => '/etc/chef/client.pem'
25
+ })
26
+ super(convergence_options, config)
27
+ @chef_version ||= convergence_options[:chef_version]
28
+ @prerelease ||= convergence_options[:prerelease]
29
+ @package_cache_path ||= convergence_options[:package_cache_path] || "#{ENV['HOME']}/.chef/package_cache"
30
+ @package_cache = {}
31
+ @tmp_dir = '/tmp'
32
+ @chef_client_timeout = convergence_options.has_key?(:chef_client_timeout) ? convergence_options[:chef_client_timeout] : 120*60 # Default: 2 hours
33
+ FileUtils.mkdir_p(@package_cache_path)
34
+ @package_cache_lock = Mutex.new
35
+ end
36
+
37
+ attr_reader :client_rb_path
38
+ attr_reader :client_pem_path
39
+
40
+ def setup_convergence(action_handler, machine)
41
+ super
42
+
43
+ # Install chef-client. TODO check and update version if not latest / not desired
44
+ if machine.execute_always('chef-client -v').exitstatus != 0
45
+ platform, platform_version, machine_architecture = machine.detect_os(action_handler)
46
+ package_file = download_package_for_platform(action_handler, machine, platform, platform_version, machine_architecture)
47
+ remote_package_file = "#{@tmp_dir}/#{File.basename(package_file)}"
48
+ machine.upload_file(action_handler, package_file, remote_package_file)
49
+ install_package(action_handler, machine, remote_package_file)
50
+ end
51
+ end
52
+
53
+ def converge(action_handler, machine)
54
+ super
55
+
56
+ action_handler.open_stream(machine.node['name']) do |stdout|
57
+ action_handler.open_stream(machine.node['name']) do |stderr|
58
+ command_line = "chef-client"
59
+ command_line << " -l #{config[:log_level].to_s}" if config[:log_level]
60
+ machine.execute(action_handler, command_line,
61
+ :stream_stdout => stdout,
62
+ :stream_stderr => stderr,
63
+ :timeout => @chef_client_timeout)
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def download_package_for_platform(action_handler, machine, platform, platform_version, machine_architecture)
71
+ @package_cache_lock.synchronize do
72
+ @package_cache ||= {}
73
+ @package_cache[platform] ||= {}
74
+ @package_cache[platform][platform_version] ||= {}
75
+ @package_cache[platform][platform_version][machine_architecture] ||= { :lock => Mutex.new }
76
+ end
77
+ @package_cache[platform][platform_version][machine_architecture][:lock].synchronize do
78
+ if !@package_cache[platform][platform_version][machine_architecture][:file]
79
+ #
80
+ # Grab metadata
81
+ #
82
+ metadata = download_metadata_for_platform(machine, platform, platform_version, machine_architecture)
83
+
84
+ # Download actual package desired by metadata
85
+ package_file = "#{@package_cache_path}/#{URI(metadata['url']).path.split('/')[-1]}"
86
+
87
+ ChefMetal.inline_resource(action_handler) do
88
+ remote_file package_file do
89
+ source metadata['url']
90
+ checksum metadata['sha256']
91
+ end
92
+ end
93
+
94
+ @package_cache[platform][platform_version][machine_architecture][:file] = package_file
95
+ end
96
+ end
97
+ @package_cache[platform][platform_version][machine_architecture][:file]
98
+ end
99
+
100
+ def download_metadata_for_platform(machine, platform, platform_version, machine_architecture)
101
+ #
102
+ # Figure out the URL to the metadata
103
+ #
104
+ metadata_url="https://www.opscode.com/chef/metadata"
105
+ metadata_url << "?v=#{@chef_version}"
106
+ metadata_url << "&prerelease=#{@prerelease ? 'true' : 'false'}"
107
+ metadata_url << "&p=#{platform.strip}"
108
+ metadata_url << "&pv=#{platform_version.strip}"
109
+ metadata_url << "&m=#{machine_architecture.strip}"
110
+ use_ssl = true
111
+
112
+ # solaris 9 lacks openssl, solaris 10 lacks recent enough credentials - your base O/S is completely insecure, please upgrade
113
+ if platform == 'solaris2' && (platform_version == '5.9' || platform_version == '5.10')
114
+ metadata_url.sub(/^https/, 'http')
115
+ use_ssl = false
116
+ end
117
+
118
+ # Download and parse the metadata
119
+ Chef::Log.debug("Getting metadata for machine #{machine.node['name']}: #{metadata_url}")
120
+ uri = URI(metadata_url)
121
+ http = Net::HTTP.new(uri.host, uri.port)
122
+ http.use_ssl = use_ssl
123
+ request = Net::HTTP::Get.new(uri.request_uri)
124
+ response = http.request(request)
125
+ metadata_str = response.body
126
+ metadata = {}
127
+ metadata_str.each_line do |line|
128
+ key, value = line.split("\t", 2)
129
+ metadata[key] = value
130
+ end
131
+ metadata
132
+ end
133
+
134
+ def install_package(action_handler, machine, remote_package_file)
135
+ extension = File.extname(remote_package_file)
136
+ result = case extension
137
+ when '.rpm'
138
+ machine.execute(action_handler, "rpm -Uvh --oldpackage --replacepkgs \"#{remote_package_file}\"")
139
+ when '.deb'
140
+ machine.execute(action_handler, "dpkg -i \"#{remote_package_file}\"")
141
+ when '.solaris'
142
+ machine.write_file(action_handler, "#{@tmp_dir}/nocheck", <<EOM)
143
+ conflict=nocheck
144
+ action=nocheck
145
+ mail=
146
+ EOM
147
+ machine.execute(action_handler, "pkgrm -a \"#{@tmp_dir}/nocheck\" -n chef")
148
+ machine.execute(action_handler, "pkgadd -n -d \"#{remote_package_file}\" -a \"#{@tmp_dir}/nocheck\" chef")
149
+ when '.sh'
150
+ machine.execute(action_handler, "sh \"#{remote_package_file}\"")
151
+ else
152
+ raise "Unknown package extension '#{extension}' for file #{remote_package_file}"
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,56 @@
1
+ require 'chef_metal/convergence_strategy/precreate_chef_objects'
2
+ require 'pathname'
3
+
4
+ module ChefMetal
5
+ class ConvergenceStrategy
6
+ class InstallMsi < PrecreateChefObjects
7
+ @@install_msi_cache = {}
8
+
9
+ def initialize(convergence_options, config)
10
+ super
11
+ @install_msi_url = convergence_options[:install_msi_url] || 'http://www.opscode.com/chef/install.msi'
12
+ @install_msi_path = convergence_options[:install_msi_path] || "%TEMP%\\#{File.basename(@install_msi_url)}"
13
+ @chef_client_timeout = convergence_options.has_key?(:chef_client_timeout) ? convergence_options[:chef_client_timeout] : 120*60 # Default: 2 hours
14
+ end
15
+
16
+ attr_reader :install_msi_url
17
+ attr_reader :install_msi_path
18
+
19
+ def setup_convergence(action_handler, machine)
20
+ if !convergence_options.has_key?(:client_rb_path) || !convergence_options.has_key?(:client_pem_path)
21
+ system_drive = machine.execute_always('$env:SystemDrive').stdout.strip
22
+ @convergence_options = Cheffish::MergedConfig.new(convergence_options, {
23
+ :client_rb_path => "#{system_drive}\\chef\\client.rb",
24
+ :client_pem_path => "#{system_drive}\\chef\\client.pem"
25
+ })
26
+ end
27
+
28
+ super
29
+
30
+ # Install chef-client. TODO check and update version if not latest / not desired
31
+ if machine.execute_always('chef-client -v').exitstatus != 0
32
+ # TODO ssh verification of install.msi before running arbtrary code would be nice?
33
+ # TODO find a way to cache this on the host like with the Unix stuff.
34
+ # Limiter is we don't know how to efficiently upload large files to
35
+ # the remote machine with WMI.
36
+ machine.execute(action_handler, "(New-Object System.Net.WebClient).DownloadFile(#{machine.escape(install_msi_url)}, #{machine.escape(install_msi_path)})")
37
+ machine.execute(action_handler, "msiexec /qn /i #{machine.escape(install_msi_path)}")
38
+ end
39
+ end
40
+
41
+ def converge(action_handler, machine)
42
+ super
43
+
44
+ # TODO For some reason I get a 500 back if I don't do -l debug
45
+ action_handler.open_stream(machine.node['name']) do |stdout|
46
+ action_handler.open_stream(machine.node['name']) do |stderr|
47
+ machine.execute(action_handler, "chef-client -l debug",
48
+ :stream_stdout => stdout,
49
+ :stream_stderr => stderr,
50
+ :timeout => @chef_client_timeout)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,51 @@
1
+ require 'chef_metal/convergence_strategy/precreate_chef_objects'
2
+ require 'pathname'
3
+
4
+ module ChefMetal
5
+ class ConvergenceStrategy
6
+ class InstallSh < PrecreateChefObjects
7
+ @@install_sh_cache = {}
8
+
9
+ def initialize(convergence_options, config)
10
+ convergence_options = Cheffish::MergedConfig.new(convergence_options, {
11
+ :client_rb_path => '/etc/chef/client.rb',
12
+ :client_pem_path => '/etc/chef/client.pem'
13
+ })
14
+ super(convergence_options, config)
15
+ @install_sh_url = convergence_options[:install_sh_url] || 'http://www.opscode.com/chef/install.sh'
16
+ @install_sh_path = convergence_options[:install_sh_path] || '/tmp/chef-install.sh'
17
+ @chef_client_timeout = convergence_options.has_key?(:chef_client_timeout) ? convergence_options[:chef_client_timeout] : 120*60 # Default: 2 hours
18
+ end
19
+
20
+ attr_reader :install_sh_url
21
+ attr_reader :install_sh_path
22
+
23
+ def setup_convergence(action_handler, machine)
24
+ super
25
+
26
+ # Install chef-client. TODO check and update version if not latest / not desired
27
+ if machine.execute_always('chef-client -v').exitstatus != 0
28
+ # TODO ssh verification of install.sh before running arbtrary code would be nice?
29
+ @@install_sh_cache[install_sh_url] ||= Net::HTTP.get(URI(install_sh_url))
30
+ machine.write_file(action_handler, install_sh_path, @@install_sh_cache[install_sh_url], :ensure_dir => true)
31
+ machine.execute(action_handler, "bash #{install_sh_path}")
32
+ end
33
+ end
34
+
35
+ def converge(action_handler, machine)
36
+ super
37
+
38
+ action_handler.open_stream(machine.node['name']) do |stdout|
39
+ action_handler.open_stream(machine.node['name']) do |stderr|
40
+ command_line = "chef-client"
41
+ command_line << " -l #{config[:log_level].to_s}" if config[:log_level]
42
+ machine.execute(action_handler, command_line,
43
+ :stream_stdout => stdout,
44
+ :stream_stderr => stderr,
45
+ :timeout => @chef_client_timeout)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end