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

Sign up to get free protection for your applications and to get access to all the features.
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