chef-metal 0.2.1 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3002d430afde46610d70663bf72b42f7740b6b3b
4
- data.tar.gz: b8f8311dc26ae7c0fa756704479169f170c6e47d
3
+ metadata.gz: b141298163e702535479301b6c88044960e65cc0
4
+ data.tar.gz: 64383c31616aa7fce1f7785d41134b6516d9b51d
5
5
  SHA512:
6
- metadata.gz: 4f2e703168cdc5f97e35270a432f1ba88544f87cb0e5e4863bde5f81dff0ab5df96c5bfc790ec5f53743c616bb928ea73e7c0f29df48816f66028706089bb8e6
7
- data.tar.gz: 04318115c38fe30f91fa386f0e5cc43791c474a4b442e0ad5e54f29780b338bde865c66f4d17ff0a2842ac65024e2599c197a1fcd2f4a9038d89272cd88c0951
6
+ metadata.gz: b86349e657a31e3594823b7036aac2c6fd2e5cf2e629da6d1db58a4069502a494bade45142d19db91533cfa66b698600c78f910955cf18a9d8ea0d5754c0e812
7
+ data.tar.gz: 2dbcb57bdfac12bea0e67c0da47b05a6f9cdc77415d14169820244f4c1ec3172ddc227e19a91f686ae39cc134394fbe11d82d66db9501a3ddbe5ee2b0b3fcba3
data/README.md CHANGED
@@ -120,10 +120,10 @@ chef-metal also comes with a [Fog](http://fog.io/) provisioner that handles prov
120
120
  Once your credentials are in, basic usage looks like this:
121
121
 
122
122
  ```
123
- chef-client -z -o myapp::vagrant,myapp::small
123
+ chef-client -z -o myapp::ec2,myapp::small
124
124
  ```
125
125
 
126
- The provisioner definition in `myapp::vagrant` looks like this:
126
+ The provisioner definition in `myapp::ec2` looks like this:
127
127
 
128
128
  ```ruby
129
129
  ec2testdir = File.expand_path('~/ec2test')
@@ -0,0 +1,121 @@
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
+ def initialize(options = {})
11
+ @client_rb_path ||= '/etc/chef/client.rb'
12
+ @client_pem_path ||= '/etc/chef/client.pem'
13
+ @chef_version ||= options[:chef_version]
14
+ @prerelease ||= options[:prerelease]
15
+ @package_cache_path ||= options[:package_cache_path] || "#{ENV['HOME']}/.chef/package_cache"
16
+ @package_cache = {}
17
+ @tmp_dir = '/tmp'
18
+ FileUtils.mkdir_p(@package_cache_path)
19
+ @download_lock = Mutex.new
20
+ end
21
+
22
+ def setup_convergence(provider, machine, machine_resource)
23
+ super
24
+
25
+ # Install chef-client. TODO check and update version if not latest / not desired
26
+ if machine.execute_always('chef-client -v').exitstatus != 0
27
+ platform, platform_version, machine_architecture = machine.detect_os(provider)
28
+ package_file = download_package_for_platform(provider, machine, platform, platform_version, machine_architecture)
29
+ remote_package_file = "#{@tmp_dir}/#{File.basename(package_file)}"
30
+ machine.upload_file(provider, package_file, remote_package_file)
31
+ install_package(provider, machine, remote_package_file)
32
+ end
33
+ end
34
+
35
+ def converge(provider, machine)
36
+ machine.execute(provider, 'chef-client')
37
+ end
38
+
39
+ private
40
+
41
+ def download_package_for_platform(provider, machine, platform, platform_version, machine_architecture)
42
+ @package_cache_lock.synchronize do
43
+ @package_cache ||= {}
44
+ @package_cache[platform] ||= {}
45
+ @package_cache[platform][platform_version] ||= {}
46
+ @package_cache[platform][platform_version][machine_architecture] ||= { :lock => Mutex.new }
47
+ end
48
+ @package_cache[platform][platform_version][machine_architecture][:lock].synchronize do
49
+ if !@package_cache[platform][platform_version][machine_architecture][:file]
50
+ #
51
+ # Grab metadata
52
+ #
53
+ metadata = download_metadata_for_platform(machine, platform, platform_version, machine_architecture)
54
+
55
+ # Download actual package desired by metadata
56
+ package_file = "#{@package_cache_path}/#{URI(metadata['url']).path.split('/')[-1]}"
57
+
58
+ ChefMetal.inline_resource(provider) do
59
+ remote_file package_file do
60
+ source metadata['url']
61
+ checksum metadata['sha256']
62
+ end
63
+ end
64
+
65
+ @package_cache[platform][platform_version][machine_architecture][:file] = package_file
66
+ end
67
+ end
68
+ @package_cache[platform][platform_version][machine_architecture][:file]
69
+ end
70
+
71
+ def download_metadata_for_platform(machine, platform, platform_version, machine_architecture)
72
+ #
73
+ # Figure out the URL to the metadata
74
+ #
75
+ metadata_url="https://www.opscode.com/chef/metadata"
76
+ metadata_url << "?v=#{@chef_version}"
77
+ metadata_url << "&prerelease=#{@prerelease ? 'true' : 'false'}"
78
+ metadata_url << "&p=#{platform}"
79
+ metadata_url << "&pv=#{platform_version}"
80
+ metadata_url << "&m=#{machine_architecture}"
81
+
82
+ # solaris 9 lacks openssl, solaris 10 lacks recent enough credentials - your base O/S is completely insecure, please upgrade
83
+ if platform == 'solaris2' && (platform_version == '5.9' || platform_version == '5.10')
84
+ metadata_url.sub(/^https/, 'http')
85
+ end
86
+
87
+ # Download and parse the metadata
88
+ Chef::Log.debug("Getting metadata for machine #{machine.node['name']}: #{metadata_url}")
89
+ metadata_str = Net::HTTP.get(URI(metadata_url))
90
+ metadata = {}
91
+ metadata_str.each_line do |line|
92
+ key, value = line.split("\t", 2)
93
+ metadata[key] = value
94
+ end
95
+ metadata
96
+ end
97
+
98
+ def install_package(provider, machine, remote_package_file)
99
+ extension = File.extname(remote_package_file)
100
+ result = case extension
101
+ when '.rpm'
102
+ machine.execute(provider, "rpm -Uvh --oldpackage --replacepkgs \"#{remote_package_file}\"")
103
+ when '.deb'
104
+ machine.execute(provider, "dpkg -i \"#{remote_package_file}\"")
105
+ when '.solaris'
106
+ machine.write_file(provider, "#{@tmp_dir}/nocheck", <<EOM)
107
+ conflict=nocheck
108
+ action=nocheck
109
+ mail=
110
+ EOM
111
+ machine.execute(provider, "pkgrm -a \"#{@tmp_dir}/nocheck\" -n chef")
112
+ machine.execute(provider, "pkgadd -n -d \"#{remote_package_file}\" -a \"#{@tmp_dir}/nocheck\" chef")
113
+ when '.sh'
114
+ machine.execute(provider, "sh \"#{remote_package_file}\"")
115
+ else
116
+ raise "Unknown package extension '#{extension}' for file #{remote_package_file}"
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -68,7 +68,7 @@ module ChefMetal
68
68
  end
69
69
 
70
70
  else
71
-
71
+
72
72
  # If the server does not already have keys, create them and upload
73
73
  Cheffish.inline_resource(provider) do
74
74
  private_key 'in_memory' do
@@ -109,13 +109,6 @@ module ChefMetal
109
109
  def create_chef_objects(provider, machine, machine_resource, public_key)
110
110
  # Save the node and create the client keys and client.
111
111
  ChefMetal.inline_resource(provider) do
112
- # Create node
113
- # TODO strip automatic attributes first so we don't race with "current state"
114
- chef_node machine.node['name'] do
115
- chef_server machine_resource.chef_server
116
- raw_json machine.node
117
- end
118
-
119
112
  # Create client
120
113
  chef_client machine.node['name'] do
121
114
  chef_server machine_resource.chef_server
@@ -125,6 +118,15 @@ module ChefMetal
125
118
  admin machine_resource.admin
126
119
  validator machine_resource.validator
127
120
  end
121
+
122
+ # Create node
123
+ # TODO strip automatic attributes first so we don't race with "current state"
124
+ chef_node machine.node['name'] do
125
+ chef_server machine_resource.chef_server
126
+ raw_json machine.node
127
+ end
128
+
129
+
128
130
  end
129
131
  end
130
132
 
@@ -2,3 +2,15 @@ require 'chef_metal'
2
2
  require 'chef/resource/fog_key_pair'
3
3
  require 'chef/provider/fog_key_pair'
4
4
  require 'chef_metal/provisioner/fog_provisioner'
5
+
6
+ class Chef
7
+ class Recipe
8
+ def with_fog_provisioner(options = {}, &block)
9
+ ChefMetal.with_provisioner(ChefMetal::Provisioner::FogProvisioner.new(options, &block))
10
+ end
11
+
12
+ def with_fog_ec2_provisioner(options = {}, &block)
13
+ with_fog_provisioner({ :provider => 'AWS' }.merge(options), &block)
14
+ end
15
+ end
16
+ end
@@ -75,5 +75,15 @@ module ChefMetal
75
75
  def disconnect
76
76
  raise "disconnect not overridden on #{self.class}"
77
77
  end
78
+
79
+ # TODO get rid of the provider attribute, that is ridiculous
80
+ # Detect the OS on the machine (assumes the machine is up)
81
+ # Returns a triplet:
82
+ # platform, platform_version, machine_architecture = machine.detect_os(provider)
83
+ # This triplet is suitable for passing to the Chef metadata API:
84
+ # https://www.opscode.com/chef/metadata?p=#{platform}&pv=#{platform_version}&m=#{machine_architecture}
85
+ def detect_os(provider)
86
+ raise "detect_os not overridden on #{self.class}"
87
+ end
78
88
  end
79
89
  end
@@ -76,4 +76,4 @@ module ChefMetal
76
76
  end
77
77
  end
78
78
  end
79
- end
79
+ end
@@ -6,6 +6,8 @@ module ChefMetal
6
6
  class UnixMachine < BasicMachine
7
7
  def initialize(node, transport, convergence_strategy)
8
8
  super
9
+
10
+ @tmp_dir = '/tmp'
9
11
  end
10
12
 
11
13
  # Options include:
@@ -104,5 +106,169 @@ module ChefMetal
104
106
  path.split('/')[0..-2].join('/')
105
107
  end
106
108
  end
109
+
110
+ def detect_os(provider)
111
+ #
112
+ # Use detect.sh to detect the operating system of the remote machine
113
+ #
114
+ # TODO do this in terms of commands rather than writing a shell script
115
+ self.write_file(provider, "#{@tmp_dir}/detect.sh", detect_sh)
116
+ detected = self.execute_always("sh #{@tmp_dir}/detect.sh")
117
+ if detected.exitstatus != 0
118
+ raise "detect.sh exited with nonzero exit status: #{detected.exitstatus}"
119
+ end
120
+ platform = nil
121
+ platform_version = nil
122
+ machine_architecture = nil
123
+ detected.stdout.each_line do |line|
124
+ if line =~ /^PLATFORM: (.+)/
125
+ platform = $1
126
+ elsif line =~ /^PLATFORM_VERSION: (.+)/
127
+ platform_version = $1
128
+ elsif line =~ /^MACHINE: (.+)/
129
+ machine_architecture = $1
130
+ end
131
+ end
132
+ [ platform, platform_version, machine_architecture ]
133
+ end
134
+
135
+ private
136
+
137
+ def detect_sh
138
+ result = <<EOM
139
+ prerelease="false"
140
+
141
+ project="chef"
142
+
143
+ report_bug() {
144
+ echo "Please file a bug report at http://tickets.opscode.com"
145
+ echo "Project: Chef"
146
+ echo "Component: Packages"
147
+ echo "Label: Omnibus"
148
+ echo "Version: $version"
149
+ echo " "
150
+ echo "Please detail your operating system type, version and any other relevant details"
151
+ }
152
+
153
+
154
+ machine=`uname -m`
155
+ os=`uname -s`
156
+
157
+ # Retrieve Platform and Platform Version
158
+ if test -f "/etc/lsb-release" && grep -q DISTRIB_ID /etc/lsb-release; then
159
+ platform=`grep DISTRIB_ID /etc/lsb-release | cut -d "=" -f 2 | tr '[A-Z]' '[a-z]'`
160
+ platform_version=`grep DISTRIB_RELEASE /etc/lsb-release | cut -d "=" -f 2`
161
+ elif test -f "/etc/debian_version"; then
162
+ platform="debian"
163
+ platform_version=`cat /etc/debian_version`
164
+ elif test -f "/etc/redhat-release"; then
165
+ platform=`sed 's/^\(.\+\) release.*/\1/' /etc/redhat-release | tr '[A-Z]' '[a-z]'`
166
+ platform_version=`sed 's/^.\+ release \([.0-9]\+\).*/\1/' /etc/redhat-release`
167
+
168
+ # If /etc/redhat-release exists, we act like RHEL by default
169
+ if test "$platform" = "fedora"; then
170
+ # Change platform version for use below.
171
+ platform_version="6.0"
172
+ fi
173
+ platform="el"
174
+ elif test -f "/etc/system-release"; then
175
+ platform=`sed 's/^\(.\+\) release.\+/\1/' /etc/system-release | tr '[A-Z]' '[a-z]'`
176
+ platform_version=`sed 's/^.\+ release \([.0-9]\+\).*/\1/' /etc/system-release | tr '[A-Z]' '[a-z]'`
177
+ # amazon is built off of fedora, so act like RHEL
178
+ if test "$platform" = "amazon linux ami"; then
179
+ platform="el"
180
+ platform_version="6.0"
181
+ fi
182
+ # Apple OS X
183
+ elif test -f "/usr/bin/sw_vers"; then
184
+ platform="mac_os_x"
185
+ # Matching the tab-space with sed is error-prone
186
+ platform_version=`sw_vers | awk '/^ProductVersion:/ { print $2 }'`
187
+
188
+ major_version=`echo $platform_version | cut -d. -f1,2`
189
+ case $major_version in
190
+ "10.6") platform_version="10.6" ;;
191
+ "10.7"|"10.8"|"10.9") platform_version="10.7" ;;
192
+ *) echo "No builds for platform: $major_version"
193
+ report_bug
194
+ exit 1
195
+ ;;
196
+ esac
197
+
198
+ # x86_64 Apple hardware often runs 32-bit kernels (see OHAI-63)
199
+ x86_64=`sysctl -n hw.optional.x86_64`
200
+ if test $x86_64 -eq 1; then
201
+ machine="x86_64"
202
+ fi
203
+ elif test -f "/etc/release"; then
204
+ platform="solaris2"
205
+ machine=`/usr/bin/uname -p`
206
+ platform_version=`/usr/bin/uname -r`
207
+ elif test -f "/etc/SuSE-release"; then
208
+ if grep -q 'Enterprise' /etc/SuSE-release;
209
+ then
210
+ platform="sles"
211
+ platform_version=`awk '/^VERSION/ {V = $3}; /^PATCHLEVEL/ {P = $3}; END {print V "." P}' /etc/SuSE-release`
212
+ else
213
+ platform="suse"
214
+ platform_version=`awk '/^VERSION =/ { print $3 }' /etc/SuSE-release`
215
+ fi
216
+ elif test "x$os" = "xFreeBSD"; then
217
+ platform="freebsd"
218
+ platform_version=`uname -r | sed 's/-.*//'`
219
+ elif test "x$os" = "xAIX"; then
220
+ platform="aix"
221
+ platform_version=`uname -v`
222
+ machine="ppc"
223
+ fi
224
+
225
+ if test "x$platform" = "x"; then
226
+ echo "Unable to determine platform version!"
227
+ report_bug
228
+ exit 1
229
+ fi
230
+
231
+ # Mangle $platform_version to pull the correct build
232
+ # for various platforms
233
+ major_version=`echo $platform_version | cut -d. -f1`
234
+ case $platform in
235
+ "el")
236
+ platform_version=$major_version
237
+ ;;
238
+ "debian")
239
+ case $major_version in
240
+ "5") platform_version="6";;
241
+ "6") platform_version="6";;
242
+ "7") platform_version="6";;
243
+ esac
244
+ ;;
245
+ "freebsd")
246
+ platform_version=$major_version
247
+ ;;
248
+ "sles")
249
+ platform_version=$major_version
250
+ ;;
251
+ "suse")
252
+ platform_version=$major_version
253
+ ;;
254
+ esac
255
+
256
+ if test "x$platform_version" = "x"; then
257
+ echo "Unable to determine platform version!"
258
+ report_bug
259
+ exit 1
260
+ fi
261
+
262
+ if test "x$platform" = "xsolaris2"; then
263
+ # hack up the path on Solaris to find wget
264
+ PATH=/usr/sfw/bin:$PATH
265
+ export PATH
266
+ fi
267
+
268
+ echo "PLATFORM: $platform"
269
+ echo "PLATFORM_VERSION: $platform_version"
270
+ echo "MACHINE: $machine"
271
+ EOM
272
+ end
107
273
  end
108
- end
274
+ end
@@ -213,7 +213,7 @@ module ChefMetal
213
213
  def delete_machine(provider, node)
214
214
  if node['normal']['provisioner_output'] && node['normal']['provisioner_output']['server_id']
215
215
  server = compute.servers.get(node['normal']['provisioner_output']['server_id'])
216
- provider.converge_by "destroy machine #{node['name']} (#{server.id} at #{provisioner_url}" do
216
+ provider.converge_by "destroy machine #{node['name']} (#{node['normal']['provisioner_output']['server_id']} at #{provisioner_url})" do
217
217
  server.destroy
218
218
  end
219
219
  convergence_strategy_for(node).delete_chef_objects(provider, node)
@@ -345,11 +345,15 @@ module ChefMetal
345
345
 
346
346
  def convergence_strategy_for(node)
347
347
  if node['normal']['provisioner_options'] && node['normal']['provisioner_options']['is_windows']
348
- require 'chef_metal/convergence_strategy/install_msi'
349
- ChefMetal::ConvergenceStrategy::InstallMsi.new
348
+ @windows_convergence_strategy ||= begin
349
+ require 'chef_metal/convergence_strategy/install_msi'
350
+ ChefMetal::ConvergenceStrategy::InstallMsi.new
351
+ end
350
352
  else
351
- require 'chef_metal/convergence_strategy/install_sh'
352
- ChefMetal::ConvergenceStrategy::InstallSh.new
353
+ @unix_convergence_strategy ||= begin
354
+ require 'chef_metal/convergence_strategy/install_cached'
355
+ ChefMetal::ConvergenceStrategy::InstallCached.new
356
+ end
353
357
  end
354
358
  end
355
359
 
@@ -377,7 +381,7 @@ module ChefMetal
377
381
  def create_ssh_transport(server)
378
382
  require 'chef_metal/transport/ssh'
379
383
 
380
- ssh_options, options = ssh_options_for(server)
384
+ ssh_options = ssh_options_for(server)
381
385
  # If we're on AWS, the default is to use ubuntu, not root
382
386
  if compute_options[:provider] == 'AWS'
383
387
  username = compute_options[:ssh_username] || 'ubuntu'
@@ -221,11 +221,15 @@ module ChefMetal
221
221
 
222
222
  def convergence_strategy_for(node)
223
223
  if vagrant_option(node, 'vm.guest').to_s == 'windows'
224
- require 'chef_metal/convergence_strategy/install_msi'
225
- ChefMetal::ConvergenceStrategy::InstallMsi.new
224
+ @windows_convergence_strategy ||= begin
225
+ require 'chef_metal/convergence_strategy/install_msi'
226
+ ChefMetal::ConvergenceStrategy::InstallMsi.new
227
+ end
226
228
  else
227
- require 'chef_metal/convergence_strategy/install_sh'
228
- ChefMetal::ConvergenceStrategy::InstallSh.new
229
+ @unix_convergence_strategy ||= begin
230
+ require 'chef_metal/convergence_strategy/install_cached'
231
+ ChefMetal::ConvergenceStrategy::InstallCached.new
232
+ end
229
233
  end
230
234
  end
231
235
 
@@ -324,4 +328,4 @@ module ChefMetal
324
328
  end
325
329
  end
326
330
  end
327
- end
331
+ end
@@ -1,5 +1,4 @@
1
1
  require 'chef_metal'
2
- require 'chef_metal/provisioner/fog_provisioner'
3
2
 
4
3
  class Chef
5
4
  class Recipe
@@ -10,21 +9,5 @@ class Chef
10
9
  def with_provisioner_options(provisioner_options, &block)
11
10
  ChefMetal.with_provisioner_options(provisioner_options, &block)
12
11
  end
13
-
14
- def with_vagrant_cluster(cluster_path, &block)
15
- ChefMetal.with_vagrant_cluster(cluster_path, &block)
16
- end
17
-
18
- def with_vagrant_box(box_name, vagrant_options = {}, &block)
19
- ChefMetal.with_vagrant_box(box_name, vagrant_options, &block)
20
- end
21
-
22
- def with_fog_provisioner(options = {}, &block)
23
- ChefMetal.with_provisioner(ChefMetal::Provisioner::FogProvisioner.new(options, &block))
24
- end
25
-
26
- def with_fog_ec2_provisioner(options = {}, &block)
27
- with_fog_provisioner({ :provider => 'AWS' }.merge(options), &block)
28
- end
29
12
  end
30
13
  end
@@ -25,3 +25,15 @@ module ChefMetal
25
25
  with_provisioner_options(provisioner_options, &block)
26
26
  end
27
27
  end
28
+
29
+ class Chef
30
+ class Recipe
31
+ def with_vagrant_cluster(cluster_path, &block)
32
+ ChefMetal.with_vagrant_cluster(cluster_path, &block)
33
+ end
34
+
35
+ def with_vagrant_box(box_name, vagrant_options = {}, &block)
36
+ ChefMetal.with_vagrant_box(box_name, vagrant_options, &block)
37
+ end
38
+ end
39
+ end
@@ -1,3 +1,3 @@
1
1
  module ChefMetal
2
- VERSION = '0.2.1'
2
+ VERSION = '0.3'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-metal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: '0.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Keiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-07 00:00:00.000000000 Z
11
+ date: 2014-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef
@@ -130,6 +130,7 @@ files:
130
130
  - lib/chef/resource/vagrant_box.rb
131
131
  - lib/chef/resource/vagrant_cluster.rb
132
132
  - lib/chef_metal/aws_credentials.rb
133
+ - lib/chef_metal/convergence_strategy/install_cached.rb
133
134
  - lib/chef_metal/convergence_strategy/install_msi.rb
134
135
  - lib/chef_metal/convergence_strategy/install_sh.rb
135
136
  - lib/chef_metal/convergence_strategy/precreate_chef_objects.rb
@@ -141,11 +142,9 @@ files:
141
142
  - lib/chef_metal/machine/windows_machine.rb
142
143
  - lib/chef_metal/machine.rb
143
144
  - lib/chef_metal/provisioner/fog_provisioner.rb
144
- - lib/chef_metal/provisioner/lxc_provisioner.rb
145
145
  - lib/chef_metal/provisioner/vagrant_provisioner.rb
146
146
  - lib/chef_metal/provisioner.rb
147
147
  - lib/chef_metal/recipe_dsl.rb
148
- - lib/chef_metal/transport/lxc.rb
149
148
  - lib/chef_metal/transport/ssh.rb
150
149
  - lib/chef_metal/transport/winrm.rb
151
150
  - lib/chef_metal/transport.rb
@@ -1,135 +0,0 @@
1
- require 'chef/mixin/shell_out'
2
- require 'chef_metal/provisioner'
3
- require 'lxc'
4
-
5
- module ChefMetal
6
- class Provisioner
7
-
8
- # Provisions machines in lxc.
9
- class LXCProvisioner < Provisioner
10
-
11
- include Chef::Mixin::ShellOut
12
-
13
- #
14
- # Acquire a machine, generally by provisioning it. Returns a Machine
15
- # object pointing at the machine, allowing useful actions like setup,
16
- # converge, execute, file and directory. The Machine object will have a
17
- # "node" property which must be saved to the server (if it is any
18
- # different from the original node object).
19
- #
20
- # ## Parameters
21
- # provider - the provider object that is calling this method.
22
- # node - node object (deserialized json) representing this machine. If
23
- # the node has a provisioner_options hash in it, these will be used
24
- # instead of options provided by the provisioner. TODO compare and
25
- # fail if different?
26
- # node will have node['normal']['provisioner_options'] in it with any options.
27
- # It is a hash with this format:
28
- #
29
- # -- provisioner_url: lxc:<lxc_path>
30
- # -- template: template name
31
- # -- template_options: additional arguments for templates
32
- # -- backingstore: backing storage (lvm, thinpools, btrfs etc)
33
- #
34
- # node['normal']['provisioner_output'] will be populated with information
35
- # about the created machine. For lxc, it is a hash with this
36
- # format:
37
- #
38
- # -- provisioner_url: lxc://<lxc_path>
39
- # -- lxc_path: path to lxc root
40
- # -- name: container name
41
- #
42
- def acquire_machine(provider, node)
43
- # TODO verify that the existing provisioner_url in the node is the same as ours
44
-
45
- # Set up the modified node data
46
- provisioner_options = node['normal']['provisioner_options']
47
- provisioner_output = node['normal']['provisioner_output'] || {
48
- 'provisioner_url' => "lxc://#{lxc_path_for(node)}",
49
- 'name' => node['name']
50
- }
51
-
52
-
53
- # Create the container if it does not exist
54
- ct = LXC::Container.new(provisioner_output['name'], lxc_path_for(node))
55
- unless ct.defined?
56
- provider.converge_by "create lxc container #{provisioner_output['name']}" do
57
- ct.create(provisioner_options['template'], provisioner_options['backingstore'], 0, provisioner_options['template_options'])
58
- end
59
- end
60
- unless ct.running?
61
- provider.converge_by "start lxc container #{provisioner_output['name']}" do
62
- ct.start
63
- while ct.ip_addresses.empty?
64
- sleep 1 # wait till dhcp ip allocation is done
65
- end
66
- end
67
- end
68
-
69
- if true # do a check on whether sshd is installed. This is idempotency!
70
- provider.converge_by "install ssh into container #{provisioner_output['name']}" do
71
- end
72
- end
73
-
74
- node['normal']['provisioner_output'] = provisioner_output
75
-
76
- # Create machine object for callers to use
77
- machine_for(node)
78
- end
79
-
80
- # Connect to machine without acquiring it
81
- def connect_to_machine(node)
82
- machine_for(node)
83
- end
84
-
85
- def delete_machine(provider, node)
86
- if node['normal'] && node['normal']['provisioner_output']
87
- provisioner_output = node['normal']['provisioner_output']
88
- ct = LXC::Container.new(provisioner_output['name'], lxc_path_for(node))
89
- if ct.defined?
90
- provider.converge_by "delete lxc container #{provisioner_output['name']}" do
91
- ct.destroy
92
- end
93
- end
94
- end
95
- convergence_strategy_for(node).delete_chef_objects(provider, node)
96
- end
97
-
98
- def stop_machine(provider, node)
99
- provisioner_options = node['normal']['provisioner_options']
100
- if node['normal'] && node['normal']['provisioner_output']
101
- provisioner_output = node['normal']['provisioner_output']
102
- ct = LXC::Container.new(provisioner_output['name'], lxc_path_for(node))
103
- if ct.running?
104
- provider.converge_by "delete lxc container #{provisioner_output['name']}" do
105
- ct.stop
106
- end
107
- end
108
- end
109
- end
110
-
111
- protected
112
-
113
- def lxc_path_for(node)
114
- provisioner_options = node['normal']['provisioner_options']
115
- provisioner_options['lxc_path'] || LXC.global_config_item('lxc.lxcpath')
116
- end
117
-
118
- def machine_for(node)
119
- require 'chef_metal/machine/unix_machine'
120
- ChefMetal::Machine::UnixMachine.new(node, transport_for(node), convergence_strategy_for(node))
121
- end
122
-
123
- def convergence_strategy_for(node)
124
- require 'chef_metal/convergence_strategy/install_sh'
125
- ChefMetal::ConvergenceStrategy::InstallSh.new
126
- end
127
-
128
- def transport_for(node)
129
- require 'chef_metal/transport/lxc'
130
- provisioner_output = node['normal']['provisioner_output']
131
- ChefMetal::Transport::LXCTransport.new(provisioner_output['name'], lxc_path_for(node))
132
- end
133
- end
134
- end
135
- end
@@ -1,90 +0,0 @@
1
- require 'chef_metal/transport'
2
- require 'lxc/extra'
3
- require 'chef/mixin/shell_out'
4
-
5
- module ChefMetal
6
- class Transport
7
- class LXCTransport < Transport
8
-
9
- class LXCExecuteResult < Struct.new(:stdout, :stderr, :exitstatus)
10
- def error!
11
- raise "Error: code #{exitstatus}.\nSTDOUT:#{stdout}\nSTDERR:#{stderr}" if exitstatus != 0
12
- end
13
- end
14
-
15
- attr_reader :name, :options, :lxc_path
16
-
17
- include Chef::Mixin::ShellOut
18
-
19
- def initialize(name, lxc_path, options={})
20
- @options = options
21
- @name = name
22
- @lxc_path = lxc_path
23
- end
24
-
25
- def ct
26
- @container ||= LXC::Container.new(name, lxc_path)
27
- end
28
-
29
- def rootfs
30
- ct.config_item('lxc.rootfs')
31
- end
32
-
33
- def ct_path(path)
34
- File.join(rootfs, path)
35
- end
36
-
37
- def execute(command)
38
- Chef::Log.info("Executing #{command} on #{name}")
39
- res = ct.execute do
40
- begin
41
- out = shell_out(command)
42
- LXCExecuteResult.new(out.stdout,out.stderr, out.exitstatus)
43
- rescue Exception => e
44
- LXCExecuteResult.new('', e.message, -1)
45
- end
46
- end
47
- res
48
- end
49
-
50
- def forward_remote_port_to_local(remote_port, local_port)
51
- warn 'Port forwarding is not implemented in lxc transport'
52
- warn "You can do this on host using:"
53
- warn " 'iptables -t nat -A PREROUTING -p tcp --dport #{remote_port} -j DNAT --to #{ct.ip_addresses.first}:#{local_port}'"
54
- end
55
-
56
- def read_file(path)
57
- if File.exists?(ct_path(path))
58
- File.read(ct_path(path))
59
- end
60
- end
61
-
62
- def download_file(path, local_path)
63
- Chef::Log.debug("Copying file #{path} from #{name} to local #{local_path}")
64
- FileUtils.cp_r(ct_path(path), local_path)
65
- end
66
-
67
- def write_file(path, content)
68
- File.open(ct_path(path), 'w') do |f|
69
- f.write(content)
70
- end
71
- end
72
-
73
- def upload_file(local_path, path)
74
- FileUtils.cp_r(local_path, ct_path(path))
75
- end
76
-
77
- def disconnect
78
- end
79
-
80
- def available?
81
- begin
82
- execute('pwd')
83
- true
84
- rescue Exception =>e
85
- false
86
- end
87
- end
88
- end
89
- end
90
- end