chef 0.9.0.a91 → 0.9.0.a92

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of chef might be problematic. Click here for more details.

@@ -66,6 +66,12 @@ class Chef::Application
66
66
  def configure_chef
67
67
  parse_options
68
68
 
69
+ unless config[:config_file] && File.file?(config[:config_file])
70
+ Chef::Log.warn("*****************************************")
71
+ Chef::Log.warn("Can not find config file: #{config[:config_file]}, using defaults.")
72
+ Chef::Log.warn("*****************************************")
73
+ end
74
+
69
75
  Chef::Config.from_file(config[:config_file]) if !config[:config_file].nil? && File.exists?(config[:config_file]) && File.readable?(config[:config_file])
70
76
  Chef::Config.merge!(config)
71
77
  end
@@ -31,6 +31,7 @@ require 'chef/runner'
31
31
  require 'chef/cookbook/cookbook_collection'
32
32
  require 'chef/cookbook/file_vendor'
33
33
  require 'chef/cookbook/file_system_file_vendor'
34
+ require 'chef/cookbook/remote_file_vendor'
34
35
  require 'ohai'
35
36
 
36
37
  class Chef
@@ -82,22 +83,29 @@ class Chef
82
83
  assert_cookbook_path_not_empty(run_context)
83
84
  converge(run_context)
84
85
  else
86
+ # Keep track of the filenames that we use in both eager cookbook
87
+ # downloading (during sync_cookbooks) and lazy (during the run
88
+ # itself, through FileVendor). After the run is over, clean up the
89
+ # cache.
90
+ valid_cache_entries = Hash.new
91
+
85
92
  save_node
93
+
94
+ # Sync_cookbooks eagerly loads all files except files and templates.
95
+ # It returns the cookbook_hash -- the return result from
96
+ # /nodes/#{nodename}/cookbooks -- which we will use for our
97
+ # run_context.
98
+ Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::RemoteFileVendor.new(manifest, rest, valid_cache_entries) }
99
+ cookbook_hash = sync_cookbooks(valid_cache_entries)
100
+ run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new(cookbook_hash))
86
101
 
87
- # Note: When we move to lazily loading all cookbook files,
88
- # replace sync_cookbooks with a method that simply gets the
89
- # cookbook manifests from the remote server (and their
90
- # download URLs) from the server and feeds them to
91
- # RemoteFileVendors. [cw/tim-5/11/2010, 5/23/2010]
92
- Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest) }
93
- # Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::RemoteFileVendor.new(manifest) }
94
- sync_cookbooks
95
- run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new(Chef::CookbookLoader.new))
96
102
  assert_cookbook_path_not_empty(run_context)
97
103
  save_node
98
104
 
99
105
  converge(run_context)
100
106
  save_node
107
+
108
+ cleanup_file_cache(valid_cache_entries)
101
109
  end
102
110
 
103
111
  end_time = Time.now
@@ -146,9 +154,9 @@ class Chef
146
154
  def determine_node_name
147
155
  unless node_name
148
156
  if Chef::Config[:node_name]
149
- self.node_name = Chef::Config[:node_name]
157
+ @node_name = Chef::Config[:node_name]
150
158
  else
151
- self.node_name = ohai[:fqdn] ? ohai[:fqdn] : ohai[:hostname]
159
+ @node_name = ohai[:fqdn] ? ohai[:fqdn] : ohai[:hostname]
152
160
  Chef::Config[:node_name] = node_name
153
161
  end
154
162
 
@@ -163,37 +171,29 @@ class Chef
163
171
  # === Returns
164
172
  # node<Chef::Node>:: Returns the created node object, also stored in @node
165
173
  def build_node
166
- Chef::Log.debug("Building node object for #{node_name}")
174
+ Chef::Log.debug("Building node object for #{@node_name}")
167
175
 
168
176
  unless Chef::Config[:solo]
169
- self.node = begin
170
- rest.get_rest("nodes/#{node_name}")
171
- rescue Net::HTTPServerException => e
172
- raise unless e.message =~ /^404/
173
- end
177
+ begin
178
+ @node = rest.get_rest("nodes/#{@node_name}")
179
+ rescue Net::HTTPServerException => e
180
+ raise unless e.message =~ /^404/
181
+ end
174
182
  end
175
183
 
176
184
  unless node
177
185
  @node_exists = false
178
- self.node = Chef::Node.new
179
- node.name(node_name)
186
+ @node = Chef::Node.new
187
+ @node.name(node_name)
180
188
  end
181
189
 
182
- node.consume_attributes(json_attribs)
183
-
184
- node.automatic_attrs = ohai.data
190
+ @node.prepare_for_run(ohai.data, @json_attribs)
191
+ # Need to nil-ify the json attribs so they are not applied on subsequent runs
192
+ @json_attribs = nil
185
193
 
186
- platform, version = Chef::Platform.find_platform_and_version(node)
187
- Chef::Log.debug("Platform is #{platform} version #{version}")
188
- @node.automatic_attrs[:platform] = platform
189
- @node.automatic_attrs[:platform_version] = version
190
- # We clear defaults and overrides, so that any deleted attributes between runs are
191
- # still gone.
192
- @node.default_attrs = Mash.new
193
- @node.override_attrs = Mash.new
194
194
  @node
195
195
  end
196
-
196
+
197
197
  #
198
198
  # === Returns
199
199
  # rest<Chef::REST>:: returns Chef::REST connection object
@@ -208,19 +208,54 @@ class Chef
208
208
  self.rest = Chef::REST.new(Chef::Config[:chef_server_url], node_name, Chef::Config[:client_key])
209
209
  end
210
210
 
211
+ # Synchronizes all the cookbooks from the chef-server.
212
+ #
213
+ # === Returns
214
+ # true:: Always returns true
215
+ def sync_cookbooks(valid_cache_entries)
216
+ Chef::Log.debug("Synchronizing cookbooks")
217
+ cookbook_hash = rest.get_rest("nodes/#{node_name}/cookbooks")
218
+ Chef::Log.debug("Cookbooks to load: #{cookbook_hash.inspect}")
219
+
220
+ # Remove all cookbooks no longer relevant to this node
221
+ Chef::FileCache.find(File.join(%w{cookbooks ** *})).each do |cache_file|
222
+ cache_file =~ /^cookbooks\/([^\/]+)\//
223
+ unless cookbook_hash.has_key?($1)
224
+ Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.")
225
+ Chef::FileCache.delete(cache_file)
226
+ end
227
+ end
228
+
229
+ # Synchronize each of the node's cookbooks, and add to the
230
+ # valid_cache_entries hash.
231
+ cookbook_hash.values.each do |cookbook|
232
+ sync_cookbook_file_cache(cookbook, valid_cache_entries)
233
+ end
234
+
235
+ # register the file cache path in the cookbook path so that CookbookLoader actually picks up the synced cookbooks
236
+ Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks")
237
+
238
+ cookbook_hash
239
+ end
240
+
211
241
  # Update the file caches for a given cache segment. Takes a segment name
212
242
  # and a hash that matches one of the cookbooks/_attribute_files style
213
243
  # remote file listings.
214
244
  #
215
245
  # === Parameters
216
- # segment<String>:: The cache segment to update
217
- # remote_list<Hash>:: A cookbooks/_attribute_files style remote file listing
218
- def sync_cookbook_file_cache(cookbook)
246
+ # cookbook<Chef::Cookbook>:: The cookbook to update
247
+ # valid_cache_entries<Hash>:: Out-param; Added to this hash are the files that
248
+ # were referred to by this cookbook
249
+ def sync_cookbook_file_cache(cookbook, valid_cache_entries)
219
250
  Chef::Log.debug("Synchronizing cookbook #{cookbook.name}")
220
251
 
221
- filenames_seen = Hash.new
222
-
223
- Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment|
252
+ # files and templates are lazily loaded, and will be done later.
253
+ eager_segments = Array(Chef::CookbookVersion::COOKBOOK_SEGMENTS)
254
+ eager_segments.delete(:files)
255
+ eager_segments.delete(:templates)
256
+
257
+ eager_segments.each do |segment|
258
+ segment_filenames = Array.new
224
259
  cookbook.manifest[segment].each do |manifest_record|
225
260
  # segment = cookbook segment
226
261
  # remote_list = list of file hashes
@@ -229,7 +264,7 @@ class Chef
229
264
  # just laying about.
230
265
 
231
266
  cache_filename = File.join("cookbooks", cookbook.name, manifest_record['path'])
232
- filenames_seen[cache_filename] = true
267
+ valid_cache_entries[cache_filename] = true
233
268
 
234
269
  current_checksum = nil
235
270
  if Chef::FileCache.has_key?(cache_filename)
@@ -245,17 +280,29 @@ class Chef
245
280
  Chef::Log.info("Storing updated #{cache_filename} in the cache.")
246
281
  Chef::FileCache.move_to(raw_file.path, cache_filename)
247
282
  else
283
+ Chef::Log.debug("Not storing #{cache_filename}, as the cache is up to date.")
248
284
  end
285
+
286
+ # make the segment filenames a full path.
287
+ full_path_cache_filename = Chef::FileCache.load(cache_filename, false)
288
+ segment_filenames << full_path_cache_filename
289
+ end
290
+
291
+ # replace segment filenames with a full-path one.
292
+ if segment.to_sym == :recipes
293
+ cookbook.recipe_filenames = segment_filenames
294
+ elsif segment.to_sym == :attributes
295
+ cookbook.attribute_filenames = segment_filenames
296
+ else
297
+ cookbook.segment_filenames(segment).replace(segment_filenames)
249
298
  end
250
299
  end
251
-
252
- filenames_seen
253
300
  end
254
301
 
255
302
  def cleanup_file_cache(valid_cache_entries)
256
303
  # Delete each file in the cache that we didn't encounter in the
257
304
  # manifest.
258
- Chef::FileCache.list.each do |cache_filename|
305
+ Chef::FileCache.find(File.join(%w{cookbooks ** *})).each do |cache_filename|
259
306
  unless valid_cache_entries[cache_filename]
260
307
  Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer on the server.")
261
308
  Chef::FileCache.delete(cache_filename)
@@ -263,50 +310,19 @@ class Chef
263
310
  end
264
311
  end
265
312
 
266
- # Synchronizes all the cookbooks from the chef-server.
267
- #
268
- # === Returns
269
- # true:: Always returns true
270
- def sync_cookbooks
271
- Chef::Log.debug("Synchronizing cookbooks")
272
- cookbook_hash = rest.get_rest("nodes/#{node_name}/cookbooks")
273
- Chef::Log.debug("Cookbooks to load: #{cookbook_hash.inspect}")
274
-
275
- # Remove all cookbooks no longer relevant to this node
276
- Chef::FileCache.list.each do |cache_file|
277
- if cache_file =~ /^cookbooks\/(.+?)\//
278
- unless cookbook_hash.has_key?($1)
279
- Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.")
280
- Chef::FileCache.delete(cache_file)
281
- end
282
- end
283
- end
284
-
285
- # Synchronize each of the node's cookbooks
286
- valid_cache_entries = cookbook_hash.values.inject({}) do |memo, cookbook|
287
- memo.merge!(sync_cookbook_file_cache(cookbook))
288
- memo
289
- end
290
-
291
- cleanup_file_cache(valid_cache_entries)
292
-
293
- # register the file cache path in the cookbook path so that CookbookLoader actually picks up the synced cookbooks
294
- Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks")
295
- end
296
-
297
313
  # Updates the current node configuration on the server.
298
314
  #
299
315
  # === Returns
300
- # true:: Always returns true
316
+ # Chef::Node - the current node
301
317
  def save_node
302
318
  Chef::Log.debug("Saving the current state of node #{node_name}")
303
- self.node = if node_exists
304
- rest.put_rest("nodes/#{node_name}", node)
305
- else
306
- result = rest.post_rest("nodes", node)
307
- @node_exists = true
308
- rest.get_rest(result['uri'])
309
- end
319
+ if node_exists
320
+ @node = @node.save
321
+ else
322
+ result = rest.post_rest("nodes", node)
323
+ @node_exists = true
324
+ @node = rest.get_rest(result['uri'])
325
+ end
310
326
  end
311
327
 
312
328
  # Converges the node.
@@ -0,0 +1,79 @@
1
+ #
2
+ # Author:: Tim Hinderliter (<tim@opscode.com>)
3
+ # Copyright:: Copyright (c) 2010 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/cookbook/file_vendor'
20
+
21
+ # This FileVendor loads files by either fetching them from the local cache, or
22
+ # if not available, loading them from the remote server.
23
+ class Chef
24
+ class Cookbook
25
+ class RemoteFileVendor < FileVendor
26
+
27
+ def initialize(manifest, rest, valid_cache_entries)
28
+ @manifest = manifest
29
+ @cookbook_name = @manifest[:cookbook_name]
30
+ @rest = rest
31
+ @valid_cache_entries = valid_cache_entries
32
+ end
33
+
34
+ # Implements abstract base's requirement. It looks in the
35
+ # Chef::Config.cookbook_path file hierarchy for the requested
36
+ # file.
37
+ def get_filename(filename)
38
+ if filename =~ /([^\/]+)\/(.+)$/
39
+ segment = $1
40
+ else
41
+ raise "get_filename: Cannot determine segment/filename for incoming filename #{filename}"
42
+ end
43
+
44
+ raise "No such segment #{segment} in cookbook #{@cookbook_name}" unless @manifest[segment]
45
+ found_manifest_record = @manifest[segment].find {|manifest_record| manifest_record[:path] == filename }
46
+ raise "No such file #{filename} in #{@cookbook_name}" unless found_manifest_record
47
+
48
+ cache_filename = File.join("cookbooks", @cookbook_name, found_manifest_record['path'])
49
+
50
+ # update valid_cache_entries so the upstream cache cleaner knows what
51
+ # we've used.
52
+ @valid_cache_entries[cache_filename] = true
53
+
54
+ current_checksum = nil
55
+ if Chef::FileCache.has_key?(cache_filename)
56
+ current_checksum = Chef::CookbookVersion.checksum_cookbook_file(Chef::FileCache.load(cache_filename, false))
57
+ end
58
+
59
+ # If the checksums are different between on-disk (current) and on-server
60
+ # (remote, per manifest), do the update. This will also execute if there
61
+ # is no current checksum.
62
+ if current_checksum != found_manifest_record['checksum']
63
+ raw_file = @rest.get_rest(found_manifest_record[:url], true)
64
+
65
+ Chef::Log.info("Storing updated #{cache_filename} in the cache.")
66
+ Chef::FileCache.move_to(raw_file.path, cache_filename)
67
+ else
68
+ Chef::Log.debug("Not storing #{cache_filename}, as the cache is up to date.")
69
+ end
70
+
71
+ full_path_cache_filename = Chef::FileCache.load(cache_filename, false)
72
+
73
+ # return the filename, not the contents (second argument= false)
74
+ full_path_cache_filename
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -34,7 +34,6 @@ class Chef
34
34
  :library_filenames, :resource_filenames, :provider_filenames, :root_filenames, :name,
35
35
  :metadata, :metadata_filenames, :status, :couchdb_rev, :couchdb
36
36
  attr_reader :couchdb_id
37
- attr_reader :file_vendor
38
37
 
39
38
  # attribute_filenames also has a setter that has non-default
40
39
  # functionality.
@@ -380,7 +379,6 @@ class Chef
380
379
  end
381
380
  end
382
381
 
383
-
384
382
  def relative_filenames_in_preferred_directory(node, segment, dirname)
385
383
  preferences = preferences_for_path(node, segment, dirname)
386
384
  filenames_by_pref = Hash.new
@@ -802,10 +800,16 @@ class Chef
802
800
  manifest[:name] = full_name
803
801
 
804
802
  @checksums = checksums_to_on_disk_paths
805
- @file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(manifest)
806
803
  @manifest = manifest
807
804
  @manifest_records_by_path = extract_manifest_records_by_path(manifest)
808
805
  end
806
+
807
+ def file_vendor
808
+ unless @file_vendor
809
+ @file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(manifest)
810
+ end
811
+ @file_vendor
812
+ end
809
813
 
810
814
  def extract_checksums_from_manifest(manifest)
811
815
  checksums = {}
@@ -29,6 +29,7 @@ class Chef
29
29
  class Override < RuntimeError; end
30
30
  class UnsupportedAction < RuntimeError; end
31
31
  class MissingLibrary < RuntimeError; end
32
+ class MissingRole < RuntimeError; end
32
33
  class User < RuntimeError; end
33
34
  class Group < RuntimeError; end
34
35
  class Link < RuntimeError; end
@@ -31,7 +31,7 @@ class Chef
31
31
  #
32
32
  # === Parameters
33
33
  # path<String>:: The path to the file you want to put in the cache - should
34
- # be relative to Chef::Config[:file_cache_path]
34
+ # be relative to file_cache_path
35
35
  # contents<String>:: A string with the contents you want written to the file
36
36
  #
37
37
  # === Returns
@@ -51,9 +51,9 @@ class Chef
51
51
  file_path_array = File.split(path)
52
52
  file_name = file_path_array.pop
53
53
  cache_path = create_cache_path(File.join(file_path_array))
54
- io = File.open(File.join(cache_path, file_name), "w")
55
- io.print(contents)
56
- io.close
54
+ File.open(File.join(cache_path, file_name), "w") do |io|
55
+ io.print(contents)
56
+ end
57
57
  true
58
58
  end
59
59
 
@@ -90,7 +90,7 @@ class Chef
90
90
  #
91
91
  # === Parameters
92
92
  # path<String>:: The path to the file you want to load - should
93
- # be relative to Chef::Config[:file_cache_path]
93
+ # be relative to file_cache_path
94
94
  # read<True/False>:: Whether to return the file contents, or the path.
95
95
  # Defaults to true.
96
96
  #
@@ -121,7 +121,7 @@ class Chef
121
121
  #
122
122
  # === Parameters
123
123
  # path<String>:: The path to the file you want to delete - should
124
- # be relative to Chef::Config[:file_cache_path]
124
+ # be relative to file_cache_path
125
125
  #
126
126
  # === Returns
127
127
  # true
@@ -145,12 +145,19 @@ class Chef
145
145
  #
146
146
  # === Returns
147
147
  # Array:: An array of files in the cache, suitable for use with load, delete and store
148
- def list()
148
+ def list
149
+ find("**#{File::Separator}*")
150
+ end
151
+
152
+ ##
153
+ # Find files in the cache by +glob_pattern+
154
+ # === Returns
155
+ # [String] - An array of file cache keys matching the glob
156
+ def find(glob_pattern)
149
157
  keys = Array.new
150
- Dir[File.join(Chef::Config[:file_cache_path], '**', '*')].each do |f|
158
+ Dir[File.join(file_cache_path, glob_pattern)].each do |f|
151
159
  if File.file?(f)
152
- path = f.match("^#{Dir[Chef::Config[:file_cache_path]].first}\/(.+)")[1]
153
- keys << path
160
+ keys << f[/^#{Regexp.escape(Dir[file_cache_path].first) + File::Separator}(.+)/, 1]
154
161
  end
155
162
  end
156
163
  keys
@@ -160,7 +167,7 @@ class Chef
160
167
  #
161
168
  # === Parameters
162
169
  # path:: The path to the file you want to check - is relative
163
- # to Chef::Config[:file_cache_path]
170
+ # to file_cache_path
164
171
  #
165
172
  # === Returns
166
173
  # True:: If the file exists
@@ -186,13 +193,13 @@ class Chef
186
193
  # also creates the path if it does not exist.
187
194
  #
188
195
  # === Parameters
189
- # path:: The path to create, relative to Chef::Config[:file_cache_path]
196
+ # path:: The path to create, relative to file_cache_path
190
197
  # create_if_missing:: True by default - whether to create the path if it does not exist
191
198
  #
192
199
  # === Returns
193
200
  # String:: The fully expanded path
194
201
  def create_cache_path(path, create_if_missing=true)
195
- cache_dir = File.expand_path(File.join(Chef::Config[:file_cache_path], path))
202
+ cache_dir = File.expand_path(File.join(file_cache_path, path))
196
203
  if create_if_missing
197
204
  create_path(cache_dir)
198
205
  else
@@ -200,6 +207,12 @@ class Chef
200
207
  end
201
208
  end
202
209
 
210
+ private
211
+
212
+ def file_cache_path
213
+ Chef::Config[:file_cache_path]
214
+ end
215
+
203
216
  end
204
217
  end
205
218
  end
@@ -32,7 +32,12 @@ class Chef
32
32
  :description => "Grab dependencies automatically"
33
33
 
34
34
  def run
35
- vendor_path = File.join(Chef::Config[:cookbook_path].first)
35
+ if config[:cookbook_path]
36
+ vendor_path = File.expand_path(File.join(config[:cookbook_path].first))
37
+ else
38
+ Chef::Log.warn("cookbook path is not configured in your knife.rb, using the current directory for the cookbook repo")
39
+ vendor_path = Dir.pwd
40
+ end
36
41
  cookbook_path = File.join(vendor_path, name_args[0])
37
42
  upstream_file = File.join(vendor_path, "#{name_args[0]}.tar.gz")
38
43
  branch_name = "chef-vendor-#{name_args[0]}"
@@ -73,6 +73,10 @@ class Chef
73
73
  :description => "Your AWS API Secret Access Key",
74
74
  :proc => Proc.new { |key| Chef::Config[:knife][:aws_secret_access_key] = key }
75
75
 
76
+ option :prerelease,
77
+ :long => "--prerelease",
78
+ :description => "Install the pre-release chef gems"
79
+
76
80
  def h
77
81
  @highline ||= HighLine.new
78
82
  end
@@ -117,15 +121,22 @@ class Chef
117
121
  puts "#{h.color("Private DNS Name", :cyan)}: #{server.private_dns_name}"
118
122
  puts "#{h.color("Private IP Address", :cyan)}: #{server.private_ip_address}"
119
123
 
120
- puts "\nWaiting 15 seconds for SSH...\n"
121
-
122
- sleep 15
123
-
124
- bootstrap = Chef::Knife::Bootstrap.new
125
- bootstrap.name_args = [ server.ip_address, @name_args ].flatten
126
- bootstrap.config[:ssh_user] = config[:ssh_user]
127
- bootstrap.config[:chef_node_name] = server.id
128
- bootstrap.run
124
+ begin
125
+ bootstrap = Chef::Knife::Bootstrap.new
126
+ bootstrap.name_args = [ server.ip_address, @name_args ].flatten
127
+ bootstrap.config[:ssh_user] = config[:ssh_user]
128
+ bootstrap.config[:chef_node_name] = server.id
129
+ bootstrap.config[:prerelease] = config[:prerelease]
130
+ bootstrap.run
131
+ rescue Errno::ECONNREFUSED
132
+ puts h.color("Connection refused on SSH, retrying - CTRL-C to abort")
133
+ sleep 1
134
+ retry
135
+ rescue Errno::ETIMEDOUT
136
+ puts h.color("Connection timed out on SSH, retrying - CTRL-C to abort")
137
+ sleep 1
138
+ retry
139
+ end
129
140
 
130
141
  puts "\n"
131
142
  puts "#{h.color("Instance ID", :cyan)}: #{server.id}"
@@ -315,7 +315,6 @@ class Chef
315
315
  # as using the normal/default/override interface.
316
316
  def method_missing(symbol, *args)
317
317
  attrs = construct_attributes
318
- attrs.set_type = :normal
319
318
  attrs.send(symbol, *args)
320
319
  end
321
320
 
@@ -353,18 +352,24 @@ class Chef
353
352
  run_list.detect { |r| r == item } ? true : false
354
353
  end
355
354
 
356
- def consume_attributes(attrs)
357
- attrs ||= {}
358
- Chef::Log.debug("Adding JSON Attributes")
355
+ def consume_run_list(attrs)
356
+ attrs = attrs ? attrs.dup : {}
359
357
  if new_run_list = attrs.delete("recipes") || attrs.delete("run_list")
360
358
  if attrs.key?("recipes") || attrs.key?("run_list")
361
359
  raise Chef::Exceptions::AmbiguousRunlistSpecification, "please set the node's run list using the 'run_list' attribute only."
362
360
  end
363
- Chef::Log.info("Replacing the run_list with #{new_run_list.inspect} from JSON")
361
+ Chef::Log.info("Setting the run_list to #{new_run_list.inspect} from JSON")
364
362
  run_list(new_run_list)
365
363
  end
366
- Chef::Mixin::DeepMerge.merge(@normal_attrs, attrs)
364
+ attrs
365
+ end
367
366
 
367
+ # TODO: this code should be killed and removed, but it is used by shef and
368
+ # I don't have time to fix it right now. [Dan - 09/Jun/2010]
369
+ def consume_attributes(attrs)
370
+ attrs = consume_run_list(attrs)
371
+ Chef::Log.debug("Applying attributes from json file")
372
+ @normal_attrs = Chef::Mixin::DeepMerge.merge(@normal_attrs,attrs)
368
373
  self[:tags] = Array.new unless attribute?(:tags)
369
374
  end
370
375
 
@@ -531,8 +536,25 @@ class Chef
531
536
  self
532
537
  end
533
538
 
539
+ def prepare_for_run(ohai_data, json_cli_attrs)
540
+ Chef::Log.debug("Extracting run list from JSON attributes provided on command line")
541
+ @json_attrib_for_expansion = consume_run_list(json_cli_attrs)
542
+
543
+ node.automatic_attrs = ohai_data
544
+
545
+ platform, version = Chef::Platform.find_platform_and_version(self)
546
+ Chef::Log.debug("Platform is #{platform} version #{version}")
547
+ @automatic_attrs[:platform] = platform
548
+ @automatic_attrs[:platform_version] = version
549
+ # We clear defaults and overrides, so that any deleted attributes between runs are
550
+ # still gone.
551
+ @default_attrs = Mash.new
552
+ @override_attrs = Mash.new
553
+ end
554
+
534
555
  # Expands the node's run list and deep merges the default and
535
- # override attributes.
556
+ # override attributes. Also applies stored attributes (from json provided
557
+ # on the command line)
536
558
  #
537
559
  # Returns the fully-expanded list of recipes.
538
560
  #
@@ -541,12 +563,18 @@ class Chef
541
563
  # run_list is mutated? Or perhaps do something smarter like
542
564
  # on-demand generation of default_attrs and override_attrs,
543
565
  # invalidated only when run_list is mutated?
544
- def expand_node!
566
+ def expand!
545
567
  # This call should only be called on a chef-client run.
546
- recipes, expanded_default_attrs, expanded_override_attrs = run_list.expand('server')
547
- self.default_attrs = Chef::Mixin::DeepMerge.merge(default_attrs, expanded_default_attrs)
548
- self.override_attrs = Chef::Mixin::DeepMerge.merge(override_attrs, expanded_override_attrs)
549
- recipes
568
+ expansion = run_list.expand('server')
569
+ raise Chef::Exceptions::MissingRole if expansion.errors?
570
+
571
+ Chef::Log.debug("Applying attributes from json file")
572
+ @normal_attrs = Chef::Mixin::DeepMerge.merge(@normal_attrs, @json_attrib_for_expansion)
573
+ self[:tags] = Array.new unless attribute?(:tags)
574
+ @default_attrs = Chef::Mixin::DeepMerge.merge(default_attrs, expansion.default_attrs)
575
+ @override_attrs = Chef::Mixin::DeepMerge.merge(override_attrs, expansion.override_attrs)
576
+
577
+ expansion.recipes
550
578
  end
551
579
 
552
580
  end
@@ -51,7 +51,7 @@ class Chef
51
51
  @state = state
52
52
  @auto_vivifiy_on_read = false
53
53
  @set_unless_value_present = false
54
- @set_type = :normal
54
+ @set_type = nil
55
55
  @has_been_read = false
56
56
  end
57
57
 
@@ -93,12 +93,23 @@ class Chef
93
93
  # See the comments in []= for more details.
94
94
  @has_been_read = true
95
95
 
96
- a_value = value_or_descend(current_automatic, key, auto_vivifiy_on_read && @set_type == :automatic)
97
- o_value = value_or_descend(current_override, key, auto_vivifiy_on_read && @set_type == :override)
98
- n_value = value_or_descend(current_normal, key, auto_vivifiy_on_read && @set_type == :normal)
99
- d_value = value_or_descend(current_default, key, auto_vivifiy_on_read && @set_type == :default)
96
+ # If we have a set type, our destiny is to write
97
+ if @set_type
98
+ a_value = @set_type == :automatic ? value_or_descend(current_automatic, key, auto_vivifiy_on_read) : nil
99
+ o_value = @set_type == :override ? value_or_descend(current_override, key, auto_vivifiy_on_read) : nil
100
+ n_value = @set_type == :normal ? value_or_descend(current_normal, key, auto_vivifiy_on_read) : nil
101
+ d_value = @set_type == :default ? value_or_descend(current_default, key, auto_vivifiy_on_read) : nil
100
102
 
101
- determine_value(a_value, o_value, n_value, d_value)
103
+ determine_value(a_value, o_value, n_value, d_value)
104
+ # Our destiny is only to read, so we get the full list.
105
+ else
106
+ a_value = value_or_descend(current_automatic, key)
107
+ o_value = value_or_descend(current_override, key)
108
+ n_value = value_or_descend(current_normal, key)
109
+ d_value = value_or_descend(current_default, key)
110
+
111
+ determine_value(a_value, o_value, n_value, d_value)
112
+ end
102
113
  end
103
114
 
104
115
  def attribute?(key)
@@ -309,6 +320,9 @@ class Chef
309
320
  end
310
321
 
311
322
  def []=(key, value)
323
+ # If we don't have one, then we'll pretend we're normal
324
+ @set_type ||= :normal
325
+
312
326
  if set_unless_value_present
313
327
  if get_value(set_type_hash, key) != nil
314
328
  Chef::Log.debug("Not setting #{state.join("/")}/#{key} to #{value.inspect} because it has a #{@set_type} value already")
@@ -132,7 +132,8 @@ class Chef
132
132
  :env => Chef::Provider::Env::Windows,
133
133
  :service => Chef::Provider::Service::Windows,
134
134
  :user => Chef::Provider::User::Windows,
135
- :group => Chef::Provider::Group::Windows
135
+ :group => Chef::Provider::Group::Windows,
136
+ :mount => Chef::Provider::Mount::Windows
136
137
  }
137
138
  },
138
139
  :mingw32 => {
@@ -140,7 +141,8 @@ class Chef
140
141
  :env => Chef::Provider::Env::Windows,
141
142
  :service => Chef::Provider::Service::Windows,
142
143
  :user => Chef::Provider::User::Windows,
143
- :group => Chef::Provider::Group::Windows
144
+ :group => Chef::Provider::Group::Windows,
145
+ :mount => Chef::Provider::Mount::Windows
144
146
  }
145
147
  },
146
148
  :windows => {
@@ -148,7 +150,8 @@ class Chef
148
150
  :env => Chef::Provider::Env::Windows,
149
151
  :service => Chef::Provider::Service::Windows,
150
152
  :user => Chef::Provider::User::Windows,
151
- :group => Chef::Provider::Group::Windows
153
+ :group => Chef::Provider::Group::Windows,
154
+ :mount => Chef::Provider::Mount::Windows
152
155
  }
153
156
  },
154
157
  :solaris => {},
@@ -84,6 +84,11 @@ class Chef
84
84
  def build_from_file(cookbook_name, filename)
85
85
  pname = filename_to_qualified_string(cookbook_name, filename)
86
86
 
87
+ # Add log entry if we override an existing light-weight provider.
88
+ class_name = convert_to_class_name(pname)
89
+ overriding = Chef::Provider.const_defined?(class_name)
90
+ Chef::Log.info("#{class_name} light-weight provider already initialized -- overriding!") if overriding
91
+
87
92
  new_provider_class = Class.new self do |cls|
88
93
 
89
94
  def load_current_resource
@@ -0,0 +1,80 @@
1
+ #
2
+ # Author:: Doug MacEachern (<dougm@vmware.com>)
3
+ # Copyright:: Copyright (c) 2010 VMware, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/provider/mount'
20
+ if RUBY_PLATFORM =~ /mswin|mingw32|windows/
21
+ require 'chef/util/windows/net_use'
22
+ require 'chef/util/windows/volume'
23
+ end
24
+
25
+ class Chef
26
+ class Provider
27
+ class Mount
28
+ class Windows < Chef::Provider::Mount
29
+
30
+ def is_volume(name)
31
+ name =~ /^\\\\\?\\Volume\{[\w-]+\}\\$/ ? true : false
32
+ end
33
+
34
+ def initialize(new_resource, run_context)
35
+ super
36
+ @mount = nil
37
+ end
38
+
39
+ def load_current_resource
40
+ if is_volume(@new_resource.device)
41
+ @mount = Chef::Util::Windows::Volume.new(@new_resource.name)
42
+ else #assume network drive
43
+ @mount = Chef::Util::Windows::NetUse.new(@new_resource.name)
44
+ end
45
+
46
+ @current_resource = Chef::Resource::Mount.new(@new_resource.name)
47
+ @current_resource.mount_point(@new_resource.mount_point)
48
+ Chef::Log.debug("Checking for mount point #{@current_resource.mount_point}")
49
+
50
+ begin
51
+ @current_resource.device(@mount.device)
52
+ Chef::Log.debug("#{@current_resource.device} mounted on #{@new_resource.mount_point}")
53
+ @current_resource.mounted(true)
54
+ rescue ArgumentError => e
55
+ @current_resource.mounted(false)
56
+ Chef::Log.debug("#{@new_resource.mount_point} is not mounted: #{e.message}")
57
+ end
58
+ end
59
+
60
+ def mount_fs
61
+ unless @current_resource.mounted
62
+ @mount.add(@new_resource.device)
63
+ else
64
+ Chef::Log.debug("#{@new_resource.mount_point} is already mounted.")
65
+ end
66
+ end
67
+
68
+ def umount_fs
69
+ if @current_resource.mounted
70
+ @mount.delete
71
+ Chef::Log.info("Unmounted #{@new_resource.mount_point}")
72
+ else
73
+ Chef::Log.debug("#{@new_resource.mount_point} is not mounted.")
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
80
+ end
@@ -348,7 +348,7 @@ class Chef
348
348
 
349
349
  def all_installed_versions
350
350
  @all_installed_versions ||= begin
351
- @gem_env.installed_versions(Gem::Dependency.new(gem_dependency.name))
351
+ @gem_env.installed_versions(Gem::Dependency.new(gem_dependency.name, '>= 0'))
352
352
  end
353
353
  end
354
354
 
@@ -82,6 +82,7 @@ require 'chef/provider/group/usermod'
82
82
  require 'chef/provider/group/windows'
83
83
 
84
84
  require 'chef/provider/mount/mount'
85
+ require 'chef/provider/mount/windows'
85
86
 
86
87
  require 'chef/provider/deploy/revision'
87
88
  require 'chef/provider/deploy/timestamped'
@@ -295,6 +295,11 @@ class Chef
295
295
 
296
296
  def build_from_file(cookbook_name, filename)
297
297
  rname = filename_to_qualified_string(cookbook_name, filename)
298
+
299
+ # Add log entry if we override an existing light-weight resource.
300
+ class_name = convert_to_class_name(rname)
301
+ overriding = Chef::Resource.const_defined?(class_name)
302
+ Chef::Log.info("#{class_name} light-weight resource already initialized -- overriding!") if overriding
298
303
 
299
304
  new_resource_class = Class.new self do |cls|
300
305
 
@@ -209,25 +209,11 @@ class Chef
209
209
  # Remove this role from the CouchDB
210
210
  def cdb_destroy
211
211
  couchdb.delete("role", @name, couchdb_rev)
212
-
213
- rs = couchdb.get_view("nodes", "by_run_list", :startkey => "role[#{@name}]", :endkey => "role[#{@name}]", :include_docs => true)
214
- rs["rows"].each do |row|
215
- node = row["doc"]
216
- node.run_list.remove("role[#{@name}]")
217
- node.cdb_save
218
- end
219
212
  end
220
213
 
221
214
  # Remove this role via the REST API
222
215
  def destroy
223
216
  chef_server_rest.delete_rest("roles/#{@name}")
224
-
225
- Chef::Node.list.each do |node|
226
- n = Chef::Node.load(node[0])
227
- n.run_list.remove("role[#{@name}]")
228
- n.save
229
- end
230
-
231
217
  end
232
218
 
233
219
  # Save this role to the CouchDB
@@ -85,7 +85,8 @@ class Chef
85
85
  # Retrieve the fully expanded list of recipes for the node by
86
86
  # resolving roles; this step also merges attributes into the
87
87
  # node from the roles/recipes included.
88
- recipe_names = node.expand_node!
88
+ recipe_names = node.expand!
89
+
89
90
  recipe_names.each do |recipe_name|
90
91
  # TODO: timh/cw, 5-14-2010: It's distasteful to be including
91
92
  # the DSL in a class outside the context of the DSL
@@ -66,8 +66,8 @@ class Chef
66
66
  if other.kind_of?(Chef::RunList)
67
67
  other.run_list_items == @run_list_items
68
68
  else
69
- other_run_list_items = other
70
- return false unless other_run_list_items.size == @run_list_items.size
69
+ return false unless other.respond_to?(:size) && (other.size == @run_list_items.size)
70
+ other_run_list_items = other.dup
71
71
 
72
72
  other_run_list_items.map! { |item| item.kind_of?(RunListItem) ? item : RunListItem.new(item) }
73
73
  other_run_list_items == @run_list_items
@@ -124,7 +124,7 @@ class Chef
124
124
 
125
125
  expansion = expansion_for_data_source(data_source, :couchdb => couchdb, :rest => rest)
126
126
  expansion.expand
127
- return expansion.recipes, expansion.default_attrs, expansion.override_attrs
127
+ expansion
128
128
  end
129
129
 
130
130
  # Converts a string run list entry to a RunListItem object.
@@ -37,12 +37,16 @@ class Chef
37
37
 
38
38
  attr_reader :override_attrs
39
39
 
40
+ attr_reader :errors
41
+
40
42
  # The data source passed to the constructor. Not used in this class.
41
43
  # In subclasses, this is a couchdb or Chef::REST object pre-configured
42
44
  # to fetch roles from their correct location.
43
45
  attr_reader :source
44
46
 
45
47
  def initialize(run_list_items, source=nil)
48
+ @errors = Array.new
49
+
46
50
  @run_list_items = run_list_items.dup
47
51
  @source = source
48
52
 
@@ -54,8 +58,15 @@ class Chef
54
58
  @applied_roles = {}
55
59
  end
56
60
 
61
+ # Did we find any errors (expanding roles)?
62
+ def errors?
63
+ @errors.length > 0
64
+ end
65
+
66
+ alias :invalid? :errors?
67
+
57
68
  # Iterates over the run list items, expanding roles. After this,
58
- # +recipes+ will the fully expanded recipe list
69
+ # +recipes+ will contain the fully expanded recipe list
59
70
  def expand
60
71
  @run_list_items.each_with_index do |entry, index|
61
72
  case entry.type
@@ -99,12 +110,13 @@ class Chef
99
110
  raise NotImplementedError
100
111
  end
101
112
 
102
- # When a role is not found, an error message should be logged, but no
103
- # exception should be raised
113
+ # When a role is not found, an error message is logged, but no
114
+ # exception is raised. We do add an entry in the errors collection.
104
115
  # === Returns
105
116
  # nil
106
117
  def role_not_found(name)
107
118
  Chef::Log.error("Role #{name} is in the runlist but does not exist. Skipping expand.")
119
+ @errors << name
108
120
  nil
109
121
  end
110
122
  end
@@ -153,4 +165,4 @@ class Chef
153
165
 
154
166
  end
155
167
  end
156
- end
168
+ end
@@ -31,7 +31,7 @@ class Chef
31
31
 
32
32
  # Search Solr for objects of a given type, for a given query. If you give
33
33
  # it a block, it will handle the paging for you dynamically.
34
- def search(type, query="*:*", sort=nil, start=0, rows=20, &block)
34
+ def search(type, query="*:*", sort='X_CHEF_id_CHEF_X asc', start=0, rows=1000, &block)
35
35
  raise ArgumentError, "Type must be a string or a symbol!" unless (type.kind_of?(String) || type.kind_of?(Symbol))
36
36
 
37
37
  response = @rest.get_rest("search/#{type}?q=#{escape(query)}&sort=#{escape(sort)}&start=#{escape(start)}&rows=#{escape(rows)}")
@@ -35,6 +35,7 @@ class Chef
35
35
  # or jruby.
36
36
  class ShellOut
37
37
  READ_WAIT_TIME = 0.01
38
+ READ_SIZE = 4096
38
39
  DEFAULT_READ_TIMEOUT = 60
39
40
  DEFAULT_ENVIRONMENT = {'LC_ALL' => 'C'}
40
41
 
@@ -129,7 +130,6 @@ class Chef
129
130
  # within +timeout+ seconds (default: 60s)
130
131
  def run_command
131
132
  Chef::Log.debug("sh(#{@command})")
132
-
133
133
  @child_pid = fork_subprocess
134
134
 
135
135
  configure_parent_process_file_descriptors
@@ -138,6 +138,11 @@ class Chef
138
138
  @result = nil
139
139
  read_time = 0
140
140
 
141
+ # Ruby 1.8.7 and 1.8.6 from mid 2009 try to allocate objects during GC
142
+ # when calling IO.select and IO#read. Some OS Vendors are not interested
143
+ # in updating their ruby packages (Apple, *cough*) and we *have to*
144
+ # make it work. So I give you this epic hack:
145
+ GC.disable
141
146
  until @status
142
147
  ready = IO.select(open_pipes, nil, nil, READ_WAIT_TIME)
143
148
  unless ready
@@ -169,6 +174,10 @@ class Chef
169
174
  Process.waitpid2(@child_pid, Process::WNOHANG) rescue nil
170
175
  raise
171
176
  ensure
177
+ # no matter what happens, turn the GC back on, and hope whatever busted
178
+ # version of ruby we're on doesn't allocate some objects during the next
179
+ # GC run.
180
+ GC.enable
172
181
  close_all_pipes
173
182
  end
174
183
 
@@ -303,9 +312,7 @@ class Chef
303
312
  # Get output as it happens rather than buffered
304
313
  child_stdout.sync = true
305
314
  child_stderr.sync = true
306
- # Set file descriptors to non-blocking IO. man(2) fcntl
307
- child_stdout.fcntl(Fcntl::F_SETFL, child_stdout.fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
308
- child_stderr.fcntl(Fcntl::F_SETFL, child_stderr.fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
315
+
309
316
  true
310
317
  end
311
318
 
@@ -316,7 +323,7 @@ class Chef
316
323
  end
317
324
 
318
325
  def read_stdout_to_buffer
319
- while chunk = child_stdout.read(16 * 1024)
326
+ while chunk = child_stdout.read_nonblock(READ_SIZE)
320
327
  @stdout << chunk
321
328
  end
322
329
  rescue Errno::EAGAIN
@@ -325,7 +332,7 @@ class Chef
325
332
  end
326
333
 
327
334
  def read_stderr_to_buffer
328
- while chunk = child_stderr.read(16 * 1024)
335
+ while chunk = child_stderr.read_nonblock(READ_SIZE)
329
336
  @stderr << chunk
330
337
  end
331
338
  rescue Errno::EAGAIN
@@ -0,0 +1,121 @@
1
+ #
2
+ # Author:: Doug MacEachern (<dougm@vmware.com>)
3
+ # Copyright:: Copyright (c) 2010 VMware, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ #the Win32 Volume APIs do not support mapping network drives. not supported by WMI either.
20
+ #see also: WNetAddConnection2 and WNetAddConnection3
21
+ #see also cmd.exe: net use /?
22
+
23
+ require 'chef/util/windows'
24
+
25
+ class Chef::Util::Windows::NetUse < Chef::Util::Windows
26
+
27
+ private
28
+
29
+ USE_NOFORCE = 0
30
+ USE_FORCE = 1
31
+ USE_LOTS_OF_FORCE = 2 #every windows API should support this flag
32
+
33
+ USE_INFO_2 = [
34
+ [:local, nil],
35
+ [:remote, nil],
36
+ [:password, nil],
37
+ [:status, 0],
38
+ [:asg_type, 0],
39
+ [:refcount, 0],
40
+ [:usecount, 0],
41
+ [:username, nil],
42
+ [:domainname, nil]
43
+ ]
44
+
45
+ USE_INFO_2_TEMPLATE =
46
+ USE_INFO_2.collect { |field| field[1].class == Fixnum ? 'i' : 'L' }.join
47
+
48
+ SIZEOF_USE_INFO_2 = #sizeof(USE_INFO_2)
49
+ USE_INFO_2.inject(0){|sum,item|
50
+ sum + (item[1].class == Fixnum ? 4 : PTR_SIZE)
51
+ }
52
+
53
+ def use_info_2(args)
54
+ USE_INFO_2.collect { |field|
55
+ args.include?(field[0]) ? args[field[0]] : field[1]
56
+ }
57
+ end
58
+
59
+ def use_info_2_pack(use)
60
+ use.collect { |v|
61
+ v.class == Fixnum ? v : str_to_ptr(multi_to_wide(v))
62
+ }.pack(USE_INFO_2_TEMPLATE)
63
+ end
64
+
65
+ def use_info_2_unpack(buffer)
66
+ use = Hash.new
67
+ USE_INFO_2.each_with_index do |field,offset|
68
+ use[field[0]] = field[1].class == Fixnum ?
69
+ dword_to_i(buffer, offset) : lpwstr_to_s(buffer, offset)
70
+ end
71
+ use
72
+ end
73
+
74
+ public
75
+
76
+ def initialize(localname)
77
+ @localname = localname
78
+ @name = multi_to_wide(localname)
79
+ end
80
+
81
+ def add(args)
82
+ if args.class == String
83
+ remote = args
84
+ args = Hash.new(USE_INFO_2)
85
+ args[:remote] = remote
86
+ end
87
+ args[:local] ||= @localname
88
+ use = use_info_2(args)
89
+ buffer = use_info_2_pack(use)
90
+ rc = NetUseAdd.call(nil, 2, buffer, nil)
91
+ if rc != NERR_Success
92
+ raise ArgumentError, get_last_error(rc)
93
+ end
94
+ end
95
+
96
+ def get_info
97
+ ptr = 0.chr * PTR_SIZE
98
+ rc = NetUseGetInfo.call(nil, @name, 2, ptr)
99
+
100
+ if rc != NERR_Success
101
+ raise ArgumentError, get_last_error(rc)
102
+ end
103
+
104
+ ptr = ptr.unpack('L')[0]
105
+ buffer = 0.chr * SIZEOF_USE_INFO_2
106
+ memcpy(buffer, ptr, buffer.size)
107
+ NetApiBufferFree(ptr)
108
+ use_info_2_unpack(buffer)
109
+ end
110
+
111
+ def device
112
+ get_info()[:remote]
113
+ end
114
+ #XXX should we use some FORCE here?
115
+ def delete
116
+ rc = NetUseDel.call(nil, @name, USE_NOFORCE)
117
+ if rc != NERR_Success
118
+ raise ArgumentError, get_last_error(rc)
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,59 @@
1
+ #
2
+ # Author:: Doug MacEachern (<dougm@vmware.com>)
3
+ # Copyright:: Copyright (c) 2010 VMware, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ #simple wrapper around Volume APIs. might be possible with WMI, but possibly more complex.
20
+
21
+ require 'chef/util/windows'
22
+ require 'windows/volume'
23
+
24
+ class Chef::Util::Windows::Volume < Chef::Util::Windows
25
+
26
+ private
27
+ include Windows::Volume
28
+ #XXX not defined in the current windows-pr release
29
+ DeleteVolumeMountPoint =
30
+ Windows::API.new('DeleteVolumeMountPoint', 'S', 'B') unless defined? DeleteVolumeMountPoint
31
+
32
+ public
33
+
34
+ def initialize(name)
35
+ name += "\\" unless name =~ /\\$/ #trailing slash required
36
+ @name = name
37
+ end
38
+
39
+ def device
40
+ buffer = 0.chr * 256
41
+ if GetVolumeNameForVolumeMountPoint(@name, buffer, buffer.size)
42
+ return buffer[0,buffer.size].unpack("Z*")[0]
43
+ else
44
+ raise ArgumentError, get_last_error
45
+ end
46
+ end
47
+
48
+ def delete
49
+ unless DeleteVolumeMountPoint.call(@name)
50
+ raise ArgumentError, get_last_error
51
+ end
52
+ end
53
+
54
+ def add(device)
55
+ unless SetVolumeMountPoint(@name, device)
56
+ raise ArgumentError, get_last_error
57
+ end
58
+ end
59
+ end
@@ -16,5 +16,5 @@
16
16
  # limitations under the License.
17
17
 
18
18
  class Chef
19
- VERSION = '0.9.0.a91'
19
+ VERSION = '0.9.0.a92'
20
20
  end
metadata CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  - 0
7
7
  - 9
8
8
  - 0
9
- - a91
10
- version: 0.9.0.a91
9
+ - a92
10
+ version: 0.9.0.a92
11
11
  platform: ruby
12
12
  authors:
13
13
  - Adam Jacob
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-08 00:00:00 -07:00
18
+ date: 2010-06-11 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -282,6 +282,7 @@ files:
282
282
  - lib/chef/cookbook/file_vendor.rb
283
283
  - lib/chef/cookbook/metadata/version.rb
284
284
  - lib/chef/cookbook/metadata.rb
285
+ - lib/chef/cookbook/remote_file_vendor.rb
285
286
  - lib/chef/cookbook_loader.rb
286
287
  - lib/chef/cookbook_version.rb
287
288
  - lib/chef/couchdb.rb
@@ -406,6 +407,7 @@ files:
406
407
  - lib/chef/provider/log.rb
407
408
  - lib/chef/provider/mdadm.rb
408
409
  - lib/chef/provider/mount/mount.rb
410
+ - lib/chef/provider/mount/windows.rb
409
411
  - lib/chef/provider/mount.rb
410
412
  - lib/chef/provider/package/apt.rb
411
413
  - lib/chef/provider/package/dpkg.rb
@@ -516,7 +518,9 @@ files:
516
518
  - lib/chef/tasks/chef_repo.rake
517
519
  - lib/chef/util/file_edit.rb
518
520
  - lib/chef/util/windows/net_group.rb
521
+ - lib/chef/util/windows/net_use.rb
519
522
  - lib/chef/util/windows/net_user.rb
523
+ - lib/chef/util/windows/volume.rb
520
524
  - lib/chef/util/windows.rb
521
525
  - lib/chef/version.rb
522
526
  - lib/chef/webui_user.rb