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,110 @@
1
+ module ChefMetal
2
+ class Machine
3
+ def initialize(machine_spec)
4
+ @machine_spec = machine_spec
5
+ end
6
+
7
+ attr_reader :machine_spec
8
+
9
+ def name
10
+ machine_spec.name
11
+ end
12
+
13
+ def node
14
+ machine_spec.node
15
+ end
16
+
17
+ # Sets up everything necessary for convergence to happen on the machine.
18
+ # The node MUST be saved as part of this procedure. Other than that,
19
+ # nothing is guaranteed except that converge() will work when this is done.
20
+ def setup_convergence(action_handler)
21
+ raise "setup_convergence not overridden on #{self.class}"
22
+ end
23
+
24
+ def converge(action_handler)
25
+ raise "converge not overridden on #{self.class}"
26
+ end
27
+
28
+ def cleanup_convergence(action_handler)
29
+ raise "cleanup_convergence not overridden on #{self.class}"
30
+ end
31
+
32
+ def execute(action_handler, command, options = {})
33
+ raise "execute not overridden on #{self.class}"
34
+ end
35
+
36
+ def execute_always(command, options = {})
37
+ raise "execute_always not overridden on #{self.class}"
38
+ end
39
+
40
+ def read_file(path)
41
+ raise "read_file not overridden on #{self.class}"
42
+ end
43
+
44
+ def download_file(action_handler, path, local_path)
45
+ raise "read_file not overridden on #{self.class}"
46
+ end
47
+
48
+ def write_file(action_handler, path, content)
49
+ raise "write_file not overridden on #{self.class}"
50
+ end
51
+
52
+ def upload_file(action_handler, local_path, path)
53
+ raise "write_file not overridden on #{self.class}"
54
+ end
55
+
56
+ def create_dir(action_handler, path)
57
+ raise "create_dir not overridden on #{self.class}"
58
+ end
59
+
60
+ # Delete file
61
+ def delete_file(action_handler, path)
62
+ raise "delete_file not overridden on #{self.class}"
63
+ end
64
+
65
+ # Return true if directory, false/nil if not
66
+ def is_directory?(path)
67
+ raise "is_directory? not overridden on #{self.class}"
68
+ end
69
+
70
+ # Return true or false depending on whether file exists
71
+ def file_exists?(path)
72
+ raise "file_exists? not overridden on #{self.class}"
73
+ end
74
+
75
+ # Return true or false depending on whether remote file differs from local path or content
76
+ def files_different?(path, local_path, content=nil)
77
+ raise "file_different? not overridden on #{self.class}"
78
+ end
79
+
80
+ # Set file attributes { mode, :owner, :group }
81
+ def set_attributes(action_handler, path, attributes)
82
+ raise "set_attributes not overridden on #{self.class}"
83
+ end
84
+
85
+ # Get file attributes { :mode, :owner, :group }
86
+ def get_attributes(path)
87
+ raise "get_attributes not overridden on #{self.class}"
88
+ end
89
+
90
+ # Ensure the given URL can be reached by the remote side (possibly by port forwarding)
91
+ # Must return the URL that the remote side can use to reach the local_url
92
+ def make_url_available_to_remote(local_url)
93
+ raise "make_url_available_to_remote not overridden on #{self.class}"
94
+ end
95
+
96
+ def disconnect
97
+ raise "disconnect not overridden on #{self.class}"
98
+ end
99
+
100
+ # TODO get rid of the action_handler attribute, that is ridiculous
101
+ # Detect the OS on the machine (assumes the machine is up)
102
+ # Returns a triplet:
103
+ # platform, platform_version, machine_architecture = machine.detect_os(action_handler)
104
+ # This triplet is suitable for passing to the Chef metadata API:
105
+ # https://www.opscode.com/chef/metadata?p=#{platform}&pv=#{platform_version}&m=#{machine_architecture}
106
+ def detect_os(action_handler)
107
+ raise "detect_os not overridden on #{self.class}"
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,82 @@
1
+ require 'chef_metal/machine'
2
+
3
+ module ChefMetal
4
+ class Machine
5
+ class BasicMachine < Machine
6
+ def initialize(machine_spec, transport, convergence_strategy)
7
+ super(machine_spec)
8
+ @transport = transport
9
+ @convergence_strategy = convergence_strategy
10
+ end
11
+
12
+ attr_reader :transport
13
+ attr_reader :convergence_strategy
14
+
15
+ def setup_convergence(action_handler)
16
+ convergence_strategy.setup_convergence(action_handler, self)
17
+ end
18
+
19
+ def converge(action_handler)
20
+ convergence_strategy.converge(action_handler, self)
21
+ end
22
+
23
+ def cleanup_convergence(action_handler)
24
+ convergence_strategy.cleanup_convergence(action_handler, machine_spec)
25
+ end
26
+
27
+ def execute(action_handler, command, options = {})
28
+ action_handler.perform_action "run '#{command}' on #{machine_spec.name}" do
29
+ result = transport.execute(command, options)
30
+ result.error!
31
+ result
32
+ end
33
+ end
34
+
35
+ def execute_always(command, options = {})
36
+ transport.execute(command, options)
37
+ end
38
+
39
+ def read_file(path)
40
+ transport.read_file(path)
41
+ end
42
+
43
+ def download_file(action_handler, path, local_path)
44
+ if files_different?(path, local_path)
45
+ action_handler.perform_action "download file #{path} on #{machine_spec.name} to #{local_path}" do
46
+ transport.download_file(path, local_path)
47
+ end
48
+ end
49
+ end
50
+
51
+ def write_file(action_handler, path, content, options = {})
52
+ if files_different?(path, nil, content)
53
+ if options[:ensure_dir]
54
+ create_dir(action_handler, dirname_on_machine(path))
55
+ end
56
+ action_handler.perform_action "write file #{path} on #{machine_spec.name}" do
57
+ transport.write_file(path, content)
58
+ end
59
+ end
60
+ end
61
+
62
+ def upload_file(action_handler, local_path, path, options = {})
63
+ if files_different?(path, local_path)
64
+ if options[:ensure_dir]
65
+ create_dir(action_handler, dirname_on_machine(path))
66
+ end
67
+ action_handler.perform_action "upload file #{local_path} to #{path} on #{machine_spec.name}" do
68
+ transport.upload_file(local_path, path)
69
+ end
70
+ end
71
+ end
72
+
73
+ def make_url_available_to_remote(local_url)
74
+ transport.make_url_available_to_remote(local_url)
75
+ end
76
+
77
+ def disconnect
78
+ transport.disconnect
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,276 @@
1
+ require 'chef_metal/machine/basic_machine'
2
+ require 'digest'
3
+
4
+ module ChefMetal
5
+ class Machine
6
+ class UnixMachine < BasicMachine
7
+ def initialize(machine_spec, transport, convergence_strategy)
8
+ super
9
+
10
+ @tmp_dir = '/tmp'
11
+ end
12
+
13
+ # Options include:
14
+ #
15
+ # command_prefix - prefix to put in front of any command, e.g. sudo
16
+ attr_reader :options
17
+
18
+ # Delete file
19
+ def delete_file(action_handler, path)
20
+ if file_exists?(path)
21
+ action_handler.perform_action "delete file #{path} on #{machine_spec.name}" do
22
+ transport.execute("rm -f #{path}").error!
23
+ end
24
+ end
25
+ end
26
+
27
+ def is_directory?(path)
28
+ result = transport.execute("stat -c '%F' #{path}", :read_only => true)
29
+ return nil if result.exitstatus != 0
30
+ result.stdout.chomp == 'directory'
31
+ end
32
+
33
+ # Return true or false depending on whether file exists
34
+ def file_exists?(path)
35
+ result = transport.execute("ls -d #{path}", :read_only => true)
36
+ result.exitstatus == 0 && result.stdout != ''
37
+ end
38
+
39
+ def files_different?(path, local_path, content=nil)
40
+ if !file_exists?(path) || (local_path && !File.exists?(local_path))
41
+ return true
42
+ end
43
+
44
+ # Get remote checksum of file
45
+ result = transport.execute("md5sum -b #{path}", :read_only => true)
46
+ result.error!
47
+ remote_sum = result.stdout.split(' ')[0]
48
+
49
+ digest = Digest::MD5.new
50
+ if content
51
+ digest.update(content)
52
+ else
53
+ File.open(local_path, 'rb') do |io|
54
+ while (buf = io.read(4096)) && buf.length > 0
55
+ digest.update(buf)
56
+ end
57
+ end
58
+ end
59
+ remote_sum != digest.hexdigest
60
+ end
61
+
62
+ def create_dir(action_handler, path)
63
+ if !file_exists?(path)
64
+ action_handler.perform_action "create directory #{path} on #{machine_spec.name}" do
65
+ transport.execute("mkdir -p #{path}").error!
66
+ end
67
+ end
68
+ end
69
+
70
+ # Set file attributes { mode, :owner, :group }
71
+ def set_attributes(action_handler, path, attributes)
72
+ if attributes[:mode] || attributes[:owner] || attributes[:group]
73
+ current_attributes = get_attributes(path)
74
+ if attributes[:mode] && current_attributes[:mode].to_i != attributes[:mode].to_i
75
+ action_handler.perform_action "change mode of #{path} on #{machine_spec.name} from #{current_attributes[:mode].to_i} to #{attributes[:mode].to_i}" do
76
+ transport.execute("chmod #{attributes[:mode].to_i} #{path}").error!
77
+ end
78
+ end
79
+ if attributes[:owner] && current_attributes[:owner] != attributes[:owner]
80
+ action_handler.perform_action "change group of #{path} on #{machine_spec.name} from #{current_attributes[:owner]} to #{attributes[:owner]}" do
81
+ transport.execute("chown #{attributes[:owner]} #{path}").error!
82
+ end
83
+ end
84
+ if attributes[:group] && current_attributes[:group] != attributes[:group]
85
+ action_handler.perform_action "change group of #{path} on #{machine_spec.name} from #{current_attributes[:group]} to #{attributes[:group]}" do
86
+ transport.execute("chgrp #{attributes[:group]} #{path}").error!
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ # Get file attributes { :mode, :owner, :group }
93
+ def get_attributes(path)
94
+ result = transport.execute("stat -c '%a %U %G %n' #{path}", :read_only => true)
95
+ return nil if result.exitstatus != 0
96
+ file_info = result.stdout.split(/\s+/)
97
+ if file_info.size <= 1
98
+ raise "#{path} does not exist in set_attributes()"
99
+ end
100
+ result = {
101
+ :mode => file_info[0],
102
+ :owner => file_info[1],
103
+ :group => file_info[2]
104
+ }
105
+ end
106
+
107
+ def dirname_on_machine(path)
108
+ path.split('/')[0..-2].join('/')
109
+ end
110
+ end
111
+
112
+ def detect_os(action_handler)
113
+ #
114
+ # Use detect.sh to detect the operating system of the remote machine
115
+ #
116
+ # TODO do this in terms of commands rather than writing a shell script
117
+ self.write_file(action_handler, "#{@tmp_dir}/detect.sh", detect_sh)
118
+ detected = self.execute_always("sh #{@tmp_dir}/detect.sh")
119
+ if detected.exitstatus != 0
120
+ raise "detect.sh exited with nonzero exit status: #{detected.exitstatus}"
121
+ end
122
+ platform = nil
123
+ platform_version = nil
124
+ machine_architecture = nil
125
+ detected.stdout.each_line do |line|
126
+ if line =~ /^PLATFORM: (.+)/
127
+ platform = $1
128
+ elsif line =~ /^PLATFORM_VERSION: (.+)/
129
+ platform_version = $1
130
+ elsif line =~ /^MACHINE: (.+)/
131
+ machine_architecture = $1
132
+ end
133
+ end
134
+ [ platform, platform_version, machine_architecture ]
135
+ end
136
+
137
+ private
138
+
139
+ def detect_sh
140
+ result = <<EOM
141
+ prerelease="false"
142
+
143
+ project="chef"
144
+
145
+ report_bug() {
146
+ echo "Please file a bug report at http://tickets.opscode.com"
147
+ echo "Project: Chef"
148
+ echo "Component: Packages"
149
+ echo "Label: Omnibus"
150
+ echo "Version: $version"
151
+ echo " "
152
+ echo "Please detail your operating system type, version and any other relevant details"
153
+ }
154
+
155
+
156
+ machine=`uname -m`
157
+ os=`uname -s`
158
+
159
+ # Retrieve Platform and Platform Version
160
+ if test -f "/etc/lsb-release" && grep -q DISTRIB_ID /etc/lsb-release; then
161
+ platform=`grep DISTRIB_ID /etc/lsb-release | cut -d "=" -f 2 | tr '[A-Z]' '[a-z]'`
162
+ platform_version=`grep DISTRIB_RELEASE /etc/lsb-release | cut -d "=" -f 2`
163
+ elif test -f "/etc/debian_version"; then
164
+ platform="debian"
165
+ platform_version=`cat /etc/debian_version`
166
+ elif test -f "/etc/redhat-release"; then
167
+ platform=`sed 's/^\\(.\\+\\) release.*/\\1/' /etc/redhat-release | tr '[A-Z]' '[a-z]'`
168
+ platform_version=`sed 's/^.\\+ release \\([.0-9]\\+\\).*/\\1/' /etc/redhat-release`
169
+
170
+ # If /etc/redhat-release exists, we act like RHEL by default
171
+ if test "$platform" = "fedora"; then
172
+ # Change platform version for use below.
173
+ platform_version="6.0"
174
+ fi
175
+ platform="el"
176
+ elif test -f "/etc/system-release"; then
177
+ platform=`sed 's/^\\(.\\+\\) release.\\+/\\1/' /etc/system-release | tr '[A-Z]' '[a-z]'`
178
+ platform_version=`sed 's/^.\\+ release \\([.0-9]\\+\\).*/\\1/' /etc/system-release | tr '[A-Z]' '[a-z]'`
179
+ # amazon is built off of fedora, so act like RHEL
180
+ if test "$platform" = "amazon linux ami"; then
181
+ platform="el"
182
+ platform_version="6.0"
183
+ fi
184
+ # Apple OS X
185
+ elif test -f "/usr/bin/sw_vers"; then
186
+ platform="mac_os_x"
187
+ # Matching the tab-space with sed is error-prone
188
+ platform_version=`sw_vers | awk '/^ProductVersion:/ { print $2 }'`
189
+
190
+ major_version=`echo $platform_version | cut -d. -f1,2`
191
+ case $major_version in
192
+ "10.6") platform_version="10.6" ;;
193
+ "10.7"|"10.8"|"10.9") platform_version="10.7" ;;
194
+ *) echo "No builds for platform: $major_version"
195
+ report_bug
196
+ exit 1
197
+ ;;
198
+ esac
199
+
200
+ # x86_64 Apple hardware often runs 32-bit kernels (see OHAI-63)
201
+ x86_64=`sysctl -n hw.optional.x86_64`
202
+ if test $x86_64 -eq 1; then
203
+ machine="x86_64"
204
+ fi
205
+ elif test -f "/etc/release"; then
206
+ platform="solaris2"
207
+ machine=`/usr/bin/uname -p`
208
+ platform_version=`/usr/bin/uname -r`
209
+ elif test -f "/etc/SuSE-release"; then
210
+ if grep -q 'Enterprise' /etc/SuSE-release;
211
+ then
212
+ platform="sles"
213
+ platform_version=`awk '/^VERSION/ {V = $3}; /^PATCHLEVEL/ {P = $3}; END {print V "." P}' /etc/SuSE-release`
214
+ else
215
+ platform="suse"
216
+ platform_version=`awk '/^VERSION =/ { print $3 }' /etc/SuSE-release`
217
+ fi
218
+ elif test "x$os" = "xFreeBSD"; then
219
+ platform="freebsd"
220
+ platform_version=`uname -r | sed 's/-.*//'`
221
+ elif test "x$os" = "xAIX"; then
222
+ platform="aix"
223
+ platform_version=`uname -v`
224
+ machine="ppc"
225
+ fi
226
+
227
+ if test "x$platform" = "x"; then
228
+ echo "Unable to determine platform version!"
229
+ report_bug
230
+ exit 1
231
+ fi
232
+
233
+ # Mangle $platform_version to pull the correct build
234
+ # for various platforms
235
+ major_version=`echo $platform_version | cut -d. -f1`
236
+ case $platform in
237
+ "el")
238
+ platform_version=$major_version
239
+ ;;
240
+ "debian")
241
+ case $major_version in
242
+ "5") platform_version="6";;
243
+ "6") platform_version="6";;
244
+ "7") platform_version="6";;
245
+ esac
246
+ ;;
247
+ "freebsd")
248
+ platform_version=$major_version
249
+ ;;
250
+ "sles")
251
+ platform_version=$major_version
252
+ ;;
253
+ "suse")
254
+ platform_version=$major_version
255
+ ;;
256
+ esac
257
+
258
+ if test "x$platform_version" = "x"; then
259
+ echo "Unable to determine platform version!"
260
+ report_bug
261
+ exit 1
262
+ fi
263
+
264
+ if test "x$platform" = "xsolaris2"; then
265
+ # hack up the path on Solaris to find wget
266
+ PATH=/usr/sfw/bin:$PATH
267
+ export PATH
268
+ fi
269
+
270
+ echo "PLATFORM: $platform"
271
+ echo "PLATFORM_VERSION: $platform_version"
272
+ echo "MACHINE: $machine"
273
+ EOM
274
+ end
275
+ end
276
+ end