diff options
112 files changed, 1381 insertions, 998 deletions
diff --git a/.travis.yml b/.travis.yml index 5c45a016b6..f1146a4aaa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,6 @@ before_install: matrix: include: - - rvm: 1.8.7-p374 - rvm: 1.9.3 - rvm: 2.0.0 - rvm: 2.1.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 387ef8912a..ce47b3e752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,31 @@ # Chef Client Changelog ## Unreleased: + +* Fix a bug in the experimental Policyfile mode that caused errors when + using templates. +* Disable JSON encoding of request body when non-JSON content type is + specified. +* Clean up FileVendor and CookbookUploader internal APIs * [**Vasiliy Tolstov**](https://github.com/vtolstov): Reload systemd service only if it's running, otherwise start. +* [**Chris Jerdonek**](https://github.com/cjerdonek): + knife diagnostic messages sent to stdout instead of stderr +* [**Xabier de Zuazo**](https://github.com/zuazo): + Remove the unused StreamingCookbookUploader class (CHEF-4586) +* http_request no longer appends "?message=" query string to GET and HEAD requests +* added shell_out commands directly to the recipe DSL +* cookbook synchronizer deletes old files from cookbooks +* do not clear file cache when override run list is set (CHEF-3684) +* ruby 1.8.7/1.9.1/1.9.2 support is dropped +* set no_lazy_load to true (CHEF-4961) +* set file_stating_uses_destdir config option default to true (CHEF-5040) * remove dependency on rest-client gem * Add method shell_out_with_systems_locale to ShellOut. * Fix knife cookbook site share on windows (CHEF-4994) +* chef-repo rake tasks are deprecated; print relevant information for + each one. ## Last Release: 11.14.0 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f05b99e09e..b1da138bd8 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,54 @@ # Chef Client Release Notes: +## http_request resource no longer appends query string + +Previously the http_request GET and HEAD requests appended a hard-coded "?message=resource_name" +query parameter that could not be overridden. That feature has been dropped. Cookbooks that +actually relied on that should manually add the message query string to the URL they pass to +the resource. + +## Added Chef::Mixin::ShellOut methods to Recipe DSL + +Added the ability to use shell_out, shell_out! and shell_out_with_systems_locale in the Recipe +DSL without needing to explicitly extend/include the mixin. + +## Cookbook Synchronizer Cleans Deleted Files + +At the start of the Chef client run any files which are in active cookbooks, but are no longer in the +manifest for the cookbook will be deleted from the cookbook file cache. + +## When given an override run list Chef does not clean the file_cache + +In order to avoid redownloading the file_cache for all the cookbooks and files that are skipped when an +override run list is used, when an override run list is set the file cache is not cleaned at all. + +## Dropped Support For Ruby 1.8.7/1.9.1/1.9.2 + +Ruby 1.8.7, 1.9.1 and 1.9.2 are no longer supported. + +## Changed no_lazy_load config default to True + +Previously the default behavior of chef-client was lazily synchronize cookbook files and templates as +they were actually used. With this setting being true, all the files and templates in a cookbook will +be synchronized at the beginning of the chef-client run. This avoids the problem where time-sensitive +URLs in the cookbook manifest may timeout before the `cookbook_file` or `template` resource is actually +converged. Many users find the lazy behavior confusing as well and expect that the cookbook should +be fully synchronized at the start. + +Some users who distribute large files via cookbooks may see performance issues with this turned on. They +should disable the setting and go back to the old lazy behavior, or else refactor how they are doing +file distribution (using `remote_file` to download artifacts from S3 or a similar service is usually a +better approach, or individual large artifacts could be encapsulated into individual different cookbooks). + +## Changed file_staging_uses_destdir config default to True + +Staging into the system's tempdir (usually /tmp or /var/tmp) rather than the destination directory can +cause issues with permissions or available space. It can also become problematic when doing cross-devices +renames which turn move operations into copy operations (using mv uses a new inode on Unix which avoids +ETXTBSY exceptions, while cp reuses the inode and can raise that error). Staging the tempfile for the +Chef file providers into the destination directory solve these problems for users. Windows ACLs on the +directory will also be inherited correctly. + ## Removed Rest-Client dependency - cookbooks that previously were able to use rest-client directly will now need to install it via `chef_gem "rest-client"`. @@ -10,3 +59,26 @@ - to avoid crashes, by default, Chef will now scan a port range and take the first available port from 8889-9999. - to change this behavior, you can pass --chef-zero-port=PORT_RANGE (for example, 10,20,30 or 10000-20000) or modify Chef::Config.chef_zero.port to be a port string, an enumerable of ports, or a single port number. + +## Knife now logs to stderr + +Informational messages from knife are now sent to stderr, allowing you to pipe the output of knife to other commands without having to filter these messages out. + +# Internal API Changes in this Release + +These changes do not impact any cookbook code, but may impact tools that +use the code base as a library. Authors of tools that rely on Chef +internals should review these changes carefully and update their +applications. + +## Changes to CookbookUpload + +`Chef::CookbookUpload.new` previously took a path as the second +argument, but due to internal changes, this parameter was not used, and +it has been removed. See: https://github.com/opscode/chef/commit/12c9bed3a5a7ab86ff78cb660d96f8b77ad6395d + +## Changes to FileVendor + +`Chef::Cookbook::FileVendor` was previously configured by passing a +block to the `on_create` method; it is now configured by calling either +`fetch_from_remote` or `fetch_from_disk`. See: https://github.com/opscode/chef/commit/3b2b4de8e7f0d55524f2a0ccaf3e1aa9f2d371eb diff --git a/chef.gemspec b/chef.gemspec index c171bda880..2bbabbc3f2 100644 --- a/chef.gemspec +++ b/chef.gemspec @@ -12,12 +12,14 @@ Gem::Specification.new do |s| s.email = "adam@getchef.com" s.homepage = "http://www.getchef.com" + s.required_ruby_version = ">= 1.9.3" + s.add_dependency "mixlib-config", "~> 2.0" s.add_dependency "mixlib-cli", "~> 1.4" s.add_dependency "mixlib-log", "~> 1.3" s.add_dependency "mixlib-authentication", "~> 1.3" s.add_dependency "mixlib-shellout", "~> 1.4" - s.add_dependency "ohai", "= 7.2.0.rc.1" + s.add_dependency "ohai", "~> 7.2" s.add_dependency "ffi-yajl", "~> 1.0" s.add_dependency "net-ssh", "~> 2.6" diff --git a/lib/chef/chef_fs/file_system/cookbooks_dir.rb b/lib/chef/chef_fs/file_system/cookbooks_dir.rb index a58bfdd1f2..d4857cdabd 100644 --- a/lib/chef/chef_fs/file_system/cookbooks_dir.rb +++ b/lib/chef/chef_fs/file_system/cookbooks_dir.rb @@ -106,7 +106,7 @@ class Chef cookbook_to_upload.freeze_version if options[:freeze] # Instantiate a new uploader based on the proxy loader - uploader = Chef::CookbookUploader.new(cookbook_to_upload, proxy_cookbook_path, :force => options[:force], :rest => root.chef_rest) + uploader = Chef::CookbookUploader.new(cookbook_to_upload, :force => options[:force], :rest => root.chef_rest) with_actual_cookbooks_dir(temp_cookbooks_path) do upload_cookbook!(uploader) @@ -128,7 +128,7 @@ class Chef def upload_unversioned_cookbook(other, options) cookbook_to_upload = other.chef_object cookbook_to_upload.freeze_version if options[:freeze] - uploader = Chef::CookbookUploader.new(cookbook_to_upload, other.parent.file_path, :force => options[:force], :rest => root.chef_rest) + uploader = Chef::CookbookUploader.new(cookbook_to_upload, :force => options[:force], :rest => root.chef_rest) with_actual_cookbooks_dir(other.parent.file_path) do upload_cookbook!(uploader) diff --git a/lib/chef/config.rb b/lib/chef/config.rb index 65952b8cf7..93cd05d0ef 100644 --- a/lib/chef/config.rb +++ b/lib/chef/config.rb @@ -567,7 +567,7 @@ class Chef # If false file staging is will be done via tempfiles that are # created under ENV['TMP'] otherwise tempfiles will be created in # the directory that files are going to reside. - default :file_staging_uses_destdir, false + default :file_staging_uses_destdir, true # Exit if another run is in progress and the chef-client is unable to # get the lock before time expires. If nil, no timeout is enforced. (Exits @@ -580,6 +580,30 @@ class Chef # the number of threads will help. default :cookbook_sync_threads, 10 + # At the beginning of the Chef Client run, the cookbook manifests are downloaded which + # contain URLs for every file in every relevant cookbook. Most of the files + # (recipes, resources, providers, libraries, etc) are immediately synchronized + # at the start of the run. The handling of "files" and "templates" directories, + # however, have two modes of operation. They can either all be downloaded immediately + # at the start of the run (no_lazy_load==true) or else they can be lazily loaded as + # cookbook_file or template resources are converged which require them (no_lazy_load==false). + # + # The advantage of lazily loading these files is that unnecessary files are not + # synchronized. This may be useful to users with large files checked into cookbooks which + # are only selectively downloaded to a subset of clients which use the cookbook. However, + # better solutions are to either isolate large files into individual cookbooks and only + # include those cookbooks in the run lists of the servers that need them -- or move to + # using remote_file and a more appropriate backing store like S3 for large file + # distribution. + # + # The disadvantages of lazily loading files are that users some time find it + # confusing that their cookbooks are not fully synchronzied to the cache initially, + # and more importantly the time-sensitive URLs which are in the manifest may time + # out on long Chef runs before the resource that uses the file is converged + # (leading to many confusing 403 errors on template/cookbook_file resources). + # + default :no_lazy_load, true + # A whitelisted array of attributes you want sent over the wire when node # data is saved. # The default setting is nil, which collects all data. Setting to [] will not diff --git a/lib/chef/cookbook/file_system_file_vendor.rb b/lib/chef/cookbook/file_system_file_vendor.rb index 8896e3ed30..e351ec4702 100644 --- a/lib/chef/cookbook/file_system_file_vendor.rb +++ b/lib/chef/cookbook/file_system_file_vendor.rb @@ -31,6 +31,9 @@ class Chef # non-sensical. class FileSystemFileVendor < FileVendor + attr_reader :cookbook_name + attr_reader :repo_paths + def initialize(manifest, *repo_paths) @cookbook_name = manifest[:cookbook_name] @repo_paths = repo_paths.flatten diff --git a/lib/chef/cookbook/file_vendor.rb b/lib/chef/cookbook/file_vendor.rb index 406f23ca25..b82b52f90c 100644 --- a/lib/chef/cookbook/file_vendor.rb +++ b/lib/chef/cookbook/file_vendor.rb @@ -24,15 +24,39 @@ class Chef # This class handles fetching of cookbook files based on specificity. class FileVendor - def self.on_create(&block) - @instance_creator = block + @vendor_class = nil + @initialization_options = nil + + # Configures FileVendor to use the RemoteFileVendor implementation. After + # calling this, subsequent calls to create_from_manifest will return a + # RemoteFileVendor object initialized with the given http_client + def self.fetch_from_remote(http_client) + @vendor_class = RemoteFileVendor + @initialization_options = http_client + end + + def self.fetch_from_disk(cookbook_paths) + @vendor_class = FileSystemFileVendor + @initialization_options = cookbook_paths + end + + # Returns the implementation class that is currently configured, or `nil` + # if one has not been configured yet. + def self.vendor_class + @vendor_class + end + + def self.initialization_options + @initialization_options end # Factory method that creates the appropriate kind of # Cookbook::FileVendor to serve the contents of the manifest def self.create_from_manifest(manifest) - raise "Must call Chef::Cookbook::FileVendor.on_create before calling create_from_manifest factory" unless defined?(@instance_creator) - @instance_creator.call(manifest) + if @vendor_class.nil? + raise "Must configure FileVendor to use a specific implementation before creating an instance" + end + @vendor_class.new(manifest, @initialization_options) end # Gets the on-disk location for the given cookbook file. diff --git a/lib/chef/cookbook/remote_file_vendor.rb b/lib/chef/cookbook/remote_file_vendor.rb index 49de62cf65..2ddce31001 100644 --- a/lib/chef/cookbook/remote_file_vendor.rb +++ b/lib/chef/cookbook/remote_file_vendor.rb @@ -25,6 +25,9 @@ class Chef # if not available, loading them from the remote server. class RemoteFileVendor < FileVendor + attr_reader :rest + attr_reader :cookbook_name + def initialize(manifest, rest) @manifest = manifest @cookbook_name = @manifest[:cookbook_name] diff --git a/lib/chef/cookbook/synchronizer.rb b/lib/chef/cookbook/synchronizer.rb index 7df3f554bb..1b96d0510b 100644 --- a/lib/chef/cookbook/synchronizer.rb +++ b/lib/chef/cookbook/synchronizer.rb @@ -10,6 +10,8 @@ class Chef # cache. class CookbookCacheCleaner + attr_accessor :skip_removal + # Setup a notification to clear the valid_cache_entries when a Chef client # run starts Chef::Client.when_run_starts do |run_status| @@ -40,15 +42,17 @@ class Chef end def cleanup_file_cache - unless Chef::Config[:solo] + unless Chef::Config[:solo] || skip_removal # Delete each file in the cache that we didn't encounter in the # manifest. - cache.find(File.join(%w{cookbooks ** *})).each do |cache_filename| + cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_filename| unless @valid_cache_entries[cache_filename] Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer needed by chef-client.") cache.delete(cache_filename) end end + else + Chef::Log.info("Skipping removal of unused files from the cache") end end @@ -59,6 +63,8 @@ class Chef class CookbookSynchronizer CookbookFile = Struct.new(:cookbook, :segment, :manifest_record) + attr_accessor :remove_obsoleted_files + def initialize(cookbooks_by_name, events) @eager_segments = Chef::CookbookVersion::COOKBOOK_SEGMENTS.dup unless Chef::Config[:no_lazy_load] @@ -70,6 +76,7 @@ class Chef @cookbooks_by_name, @events = cookbooks_by_name, events @cookbook_full_file_paths = {} + @remove_obsoleted_files = true end def cache @@ -92,6 +99,10 @@ class Chef @cookbooks_by_name.key?(cookbook_name) end + def cookbook_segment(cookbook_name, segment) + @cookbooks_by_name[cookbook_name].manifest[segment] + end + def files @files ||= cookbooks.inject([]) do |memo, cookbook| @eager_segments.each do |segment| @@ -169,12 +180,10 @@ class Chef @cookbook_full_file_paths[file.cookbook][file.segment] << full_path end - # Iterates over cached cookbooks' files, removing files belonging to - # cookbooks that don't appear in +cookbook_hash+ - def clear_obsoleted_cookbooks - @events.cookbook_clean_start - # Remove all cookbooks no longer relevant to this node - cache.find(File.join(%w{cookbooks ** *})).each do |cache_file| + # remove cookbooks that are not referenced in the expanded run_list at all + # (if we have an override run_list we may not want to do this) + def remove_old_cookbooks + cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_file| cache_file =~ /^cookbooks\/([^\/]+)\// unless have_cookbook?($1) Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.") @@ -182,6 +191,39 @@ class Chef @events.removed_cookbook_file(cache_file) end end + end + + # remove deleted files in cookbooks that are being used on the node + def remove_deleted_files + cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_file| + md = cache_file.match(/^cookbooks\/([^\/]+)\/([^\/]+)\/(.*)/) + next unless md + ( cookbook_name, segment, file ) = md[1..3] + if have_cookbook?(cookbook_name) + manifest_segment = cookbook_segment(cookbook_name, segment) + if manifest_segment.select { |manifest_record| manifest_record["path"] == "#{segment}/#{file}" }.empty? + Chef::Log.info("Removing #{cache_file} from the cache; its is no longer in the cookbook manifest.") + cache.delete(cache_file) + @events.removed_cookbook_file(cache_file) + end + end + end + end + + # Iterates over cached cookbooks' files, removing files belonging to + # cookbooks that don't appear in +cookbook_hash+ + def clear_obsoleted_cookbooks + @events.cookbook_clean_start + + if remove_obsoleted_files + remove_old_cookbooks + else + Chef::Log.info("Skipping removal of obsoleted cookbooks from the cache") + CookbookCacheCleaner.instance.skip_removal = true + end + + remove_deleted_files + @events.cookbook_clean_complete end diff --git a/lib/chef/cookbook_uploader.rb b/lib/chef/cookbook_uploader.rb index adad55b4ca..2d8bf5bc7e 100644 --- a/lib/chef/cookbook_uploader.rb +++ b/lib/chef/cookbook_uploader.rb @@ -35,8 +35,8 @@ class Chef # in Chef::Config. # * :concurrency An integer that decided how many threads will be used to # perform concurrent uploads - def initialize(cookbooks, path, opts={}) - @path, @opts = path, opts + def initialize(cookbooks, opts={}) + @opts = opts @cookbooks = Array(cookbooks) @rest = opts[:rest] || Chef::REST.new(Chef::Config[:chef_server_url]) @concurrency = opts[:concurrency] || 10 @@ -53,7 +53,7 @@ class Chef end checksums = checksum_files.inject({}){|memo,elt| memo[elt.first]=nil ; memo} - new_sandbox = rest.post_rest("sandboxes", { :checksums => checksums }) + new_sandbox = rest.post("sandboxes", { :checksums => checksums }) Chef::Log.info("Uploading files") @@ -80,7 +80,7 @@ class Chef # in eventual consistency) retries = 0 begin - rest.put_rest(sandbox_url, {:is_completed => true}) + rest.put(sandbox_url, {:is_completed => true}) rescue Net::HTTPServerException => e if e.message =~ /^400/ && (retries += 1) <= 5 sleep 2 @@ -94,7 +94,7 @@ class Chef cookbooks.each do |cb| save_url = opts[:force] ? cb.force_save_url : cb.save_url begin - rest.put_rest(save_url, cb) + rest.put(save_url, cb) rescue Net::HTTPServerException => e case e.response.code when "409" @@ -108,32 +108,19 @@ class Chef Chef::Log.info("Upload complete!") end - def worker_thread(work_queue) - end - def uploader_function_for(file, checksum, url, checksums_to_upload) lambda do # Checksum is the hexadecimal representation of the md5, # but we need the base64 encoding for the content-md5 # header checksum64 = Base64.encode64([checksum].pack("H*")).strip - timestamp = Time.now.utc.iso8601 file_contents = File.open(file, "rb") {|f| f.read} - # TODO - 5/28/2010, cw: make signing and sending the request streaming + + # Custom headers. 'content-type' disables JSON serialization of the request body. headers = { 'content-type' => 'application/x-binary', 'content-md5' => checksum64, "accept" => 'application/json' } - if rest.signing_key - sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object( - :http_method => :put, - :path => URI.parse(url).path, - :body => file_contents, - :timestamp => timestamp, - :user_id => rest.client_name - ) - headers.merge!(sign_obj.sign(OpenSSL::PKey::RSA.new(rest.signing_key))) - end begin - Chef::HTTP::Simple.new(url, :headers=>headers).put(url, file_contents) + rest.put(url, file_contents, headers) checksums_to_upload.delete(checksum) rescue Net::HTTPServerException, Net::HTTPFatalError, Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError => e error_message = "Failed to upload #{file} (#{checksum}) to #{url} : #{e.message}" @@ -146,7 +133,7 @@ class Chef def validate_cookbooks cookbooks.each do |cb| - syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cb.name, @user_cookbook_path) + syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cb.name) Chef::Log.info("Validating ruby files") exit(1) unless syntax_checker.validate_ruby_files Chef::Log.info("Validating templates") diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb index 3d8b9fb908..778a6043bf 100644 --- a/lib/chef/cookbook_version.rb +++ b/lib/chef/cookbook_version.rb @@ -25,6 +25,7 @@ require 'chef/cookbook/metadata' require 'chef/version_class' require 'pathname' require 'chef/monkey_patches/pathname' +require 'chef/digester' class Chef diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb index 6846703f09..23cfbd558c 100644 --- a/lib/chef/dsl/recipe.rb +++ b/lib/chef/dsl/recipe.rb @@ -28,6 +28,7 @@ class Chef # objects via method calls. module Recipe + include Chef::Mixin::ShellOut include Chef::Mixin::ConvertToClassName def method_missing(method_symbol, *args, &block) diff --git a/lib/chef/file_content_management/tempfile.rb b/lib/chef/file_content_management/tempfile.rb index 0bb7f3a6fa..61a5ce2a7c 100644 --- a/lib/chef/file_content_management/tempfile.rb +++ b/lib/chef/file_content_management/tempfile.rb @@ -54,7 +54,14 @@ class Chef end def tempfile_dirname - Chef::Config[:file_staging_uses_destdir] ? ::File.dirname(@new_resource.path) : Dir::tmpdir + # in why-run mode we need to create a Tempfile to compare against, which we will never + # wind up deploying, but our enclosing directory for the destdir may not exist yet, so + # instead we can reliably always create a Tempfile to compare against in Dir::tmpdir + if Chef::Config[:file_staging_uses_destdir] && !Chef::Config[:why_run] + ::File.dirname(@new_resource.path) + else + Dir::tmpdir + end end end end diff --git a/lib/chef/formatters/base.rb b/lib/chef/formatters/base.rb index 636ba9c83f..c901068aa0 100644 --- a/lib/chef/formatters/base.rb +++ b/lib/chef/formatters/base.rb @@ -93,6 +93,13 @@ class Chef def indent_by(amount) @output.indent += amount + if @output.indent < 0 + # This is left commented out for now. We need to uncomment it and fix at least one bug in + # the formatter, and then leave this line uncommented in the future. + #Chef::Log.warn "Internal Formatter Error -- Attempt to indent by negative number of spaces" + @output.indent = 0 + end + @output.indent end # Input: a Formatters::ErrorDescription object. diff --git a/lib/chef/http/json_input.rb b/lib/chef/http/json_input.rb index 1e4e736030..23ccc3a8a7 100644 --- a/lib/chef/http/json_input.rb +++ b/lib/chef/http/json_input.rb @@ -29,7 +29,8 @@ class Chef end def handle_request(method, url, headers={}, data=false) - if data + if data && should_encode_as_json?(headers) + headers.delete_if { |key, _value| key.downcase == 'content-type' } headers["Content-Type"] = 'application/json' data = Chef::JSONCompat.to_json(data) # Force encoding to binary to fix SSL related EOFErrors @@ -52,6 +53,16 @@ class Chef [http_response, rest_request, return_value] end + private + + def should_encode_as_json?(headers) + # ruby/Net::HTTP don't enforce capitalized headers (it normalizes them + # for you before sending the request), so we have to account for all + # the variations we might find + requested_content_type = headers.find {|k, v| k.downcase == "content-type" } + requested_content_type.nil? || requested_content_type.last.include?("json") + end + end end end diff --git a/lib/chef/knife/cookbook_site_share.rb b/lib/chef/knife/cookbook_site_share.rb index 4dcce42d7f..330f3cb229 100644 --- a/lib/chef/knife/cookbook_site_share.rb +++ b/lib/chef/knife/cookbook_site_share.rb @@ -54,7 +54,7 @@ class Chef cl = Chef::CookbookLoader.new(config[:cookbook_path]) if cl.cookbook_exists?(cookbook_name) cookbook = cl[cookbook_name] - Chef::CookbookUploader.new(cookbook,config[:cookbook_path]).validate_cookbooks + Chef::CookbookUploader.new(cookbook).validate_cookbooks tmp_cookbook_dir = Chef::CookbookSiteStreamingUploader.create_build_dir(cookbook) begin Chef::Log.debug("Temp cookbook directory is #{tmp_cookbook_dir.inspect}") diff --git a/lib/chef/knife/cookbook_upload.rb b/lib/chef/knife/cookbook_upload.rb index 9d6e0d438d..f5002be3a7 100644 --- a/lib/chef/knife/cookbook_upload.rb +++ b/lib/chef/knife/cookbook_upload.rb @@ -184,7 +184,7 @@ class Chef def cookbook_repo @cookbook_loader ||= begin - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, config[:cookbook_path]) } + Chef::Cookbook::FileVendor.fetch_from_disk(config[:cookbook_path]) Chef::CookbookLoader.new(config[:cookbook_path]) end end @@ -240,7 +240,7 @@ WARNING check_for_broken_links!(cb) check_for_dependencies!(cb) end - Chef::CookbookUploader.new(cookbooks, config[:cookbook_path], :force => config[:force], :concurrency => config[:concurrency]).upload_cookbooks + Chef::CookbookUploader.new(cookbooks, :force => config[:force], :concurrency => config[:concurrency]).upload_cookbooks rescue Chef::Exceptions::CookbookFrozen => e ui.error e raise diff --git a/lib/chef/knife/core/ui.rb b/lib/chef/knife/core/ui.rb index ff2545cfed..c4d7d73b00 100644 --- a/lib/chef/knife/core/ui.rb +++ b/lib/chef/knife/core/ui.rb @@ -73,10 +73,8 @@ class Chef end end - alias :info :msg - - # Prints a msg to stderr. Used for warn, error, and fatal. - def err(message) + # Prints a msg to stderr. Used for info, warn, error, and fatal. + def log(message) begin stderr.puts message rescue Errno::EPIPE => e @@ -85,19 +83,22 @@ class Chef end end + alias :info :log + alias :err :log + # Print a warning message def warn(message) - err("#{color('WARNING:', :yellow, :bold)} #{message}") + log("#{color('WARNING:', :yellow, :bold)} #{message}") end # Print an error message def error(message) - err("#{color('ERROR:', :red, :bold)} #{message}") + log("#{color('ERROR:', :red, :bold)} #{message}") end # Print a message describing a fatal error. def fatal(message) - err("#{color('FATAL:', :red, :bold)} #{message}") + log("#{color('FATAL:', :red, :bold)} #{message}") end def color(string, *colors) diff --git a/lib/chef/policy_builder/expand_node_object.rb b/lib/chef/policy_builder/expand_node_object.rb index 161e14ca38..2ee8a23258 100644 --- a/lib/chef/policy_builder/expand_node_object.rb +++ b/lib/chef/policy_builder/expand_node_object.rb @@ -56,13 +56,13 @@ class Chef def setup_run_context(specific_recipes=nil) if Chef::Config[:solo] - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, Chef::Config[:cookbook_path]) } + Chef::Cookbook::FileVendor.fetch_from_disk(Chef::Config[:cookbook_path]) cl = Chef::CookbookLoader.new(Chef::Config[:cookbook_path]) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) run_context = Chef::RunContext.new(node, cookbook_collection, @events) else - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::RemoteFileVendor.new(manifest, api_service) } + Chef::Cookbook::FileVendor.fetch_from_remote(api_service) cookbook_hash = sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbook_hash) run_context = Chef::RunContext.new(node, cookbook_collection, @events) @@ -176,6 +176,9 @@ class Chef end synchronizer = Chef::CookbookSynchronizer.new(cookbook_hash, events) + if temporary_policy? + synchronizer.remove_obsoleted_files = false + end synchronizer.sync_cookbooks # register the file cache path in the cookbook path so that CookbookLoader actually picks up the synced cookbooks diff --git a/lib/chef/policy_builder/policyfile.rb b/lib/chef/policy_builder/policyfile.rb index 4baa6340d4..0df3dd5dd2 100644 --- a/lib/chef/policy_builder/policyfile.rb +++ b/lib/chef/policy_builder/policyfile.rb @@ -154,10 +154,7 @@ class Chef end def setup_run_context(specific_recipes=nil) - # TODO: This file vendor stuff is duplicated and initializing it with a - # block traps a reference to this object in a global context which will - # prevent it from getting GC'd. Simplify it. - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::RemoteFileVendor.new(manifest, api_service) } + Chef::Cookbook::FileVendor.fetch_from_remote(http_api) sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbooks_to_sync) run_context = Chef::RunContext.new(node, cookbook_collection, events) diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb index 90b6ead716..db7629dbcb 100644 --- a/lib/chef/provider.rb +++ b/lib/chef/provider.rb @@ -22,11 +22,13 @@ require 'chef/mixin/convert_to_class_name' require 'chef/dsl/recipe' require 'chef/mixin/enforce_ownership_and_permissions' require 'chef/mixin/why_run' +require 'chef/mixin/shell_out' class Chef class Provider include Chef::DSL::Recipe include Chef::Mixin::WhyRun + include Chef::Mixin::ShellOut attr_accessor :new_resource attr_accessor :current_resource diff --git a/lib/chef/provider/cookbook_file.rb b/lib/chef/provider/cookbook_file.rb index 18af70d415..26d6ebf1d9 100644 --- a/lib/chef/provider/cookbook_file.rb +++ b/lib/chef/provider/cookbook_file.rb @@ -49,4 +49,3 @@ class Chef end end end - diff --git a/lib/chef/provider/deploy.rb b/lib/chef/provider/deploy.rb index 18f396f89e..541cc9e846 100644 --- a/lib/chef/provider/deploy.rb +++ b/lib/chef/provider/deploy.rb @@ -175,7 +175,6 @@ class Chef restart end - def callback(what, callback_code=nil) @collection = Chef::ResourceCollection.new case callback_code diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb index 2907688e88..d469bea769 100644 --- a/lib/chef/provider/execute.rb +++ b/lib/chef/provider/execute.rb @@ -16,7 +16,6 @@ # limitations under the License. # -require 'chef/mixin/shell_out' require 'chef/log' require 'chef/provider' @@ -24,8 +23,6 @@ class Chef class Provider class Execute < Chef::Provider - include Chef::Mixin::ShellOut - def load_current_resource true end diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb index 2dffcc6ddc..031d0fb005 100644 --- a/lib/chef/provider/file.rb +++ b/lib/chef/provider/file.rb @@ -25,7 +25,6 @@ require 'etc' require 'fileutils' require 'chef/scan_access_control' require 'chef/mixin/checksum' -require 'chef/mixin/shell_out' require 'chef/mixin/file_class' require 'chef/util/backup' require 'chef/util/diff' @@ -48,7 +47,6 @@ class Chef class File < Chef::Provider include Chef::Mixin::EnforceOwnershipAndPermissions include Chef::Mixin::Checksum - include Chef::Mixin::ShellOut include Chef::Util::Selinux include Chef::Mixin::FileClass @@ -247,7 +245,6 @@ class Chef "Assuming symlink source would be created by a previous resource" ] end - def content @content ||= begin load_current_resource if @current_resource.nil? diff --git a/lib/chef/provider/git.rb b/lib/chef/provider/git.rb index 3999edc16a..263014d229 100644 --- a/lib/chef/provider/git.rb +++ b/lib/chef/provider/git.rb @@ -16,19 +16,15 @@ # limitations under the License. # - require 'chef/exceptions' require 'chef/log' require 'chef/provider' -require 'chef/mixin/shell_out' require 'fileutils' class Chef class Provider class Git < Chef::Provider - include Chef::Mixin::ShellOut - def whyrun_supported? true end @@ -51,7 +47,6 @@ class Chef "Cannot clone #{@new_resource} to #{@new_resource.destination}, the enclosing directory #{dirname} does not exist") end - requirements.assert(:all_actions) do |a| a.assertion { !(@new_resource.revision =~ /^origin\//) } a.failure_message Chef::Exceptions::InvalidRemoteGitReference, @@ -107,7 +102,6 @@ class Chef end end - def existing_git_clone? ::File.exist?(::File.join(@new_resource.destination, ".git")) end diff --git a/lib/chef/provider/group/gpasswd.rb b/lib/chef/provider/group/gpasswd.rb index a65a7ffd53..521affac11 100644 --- a/lib/chef/provider/group/gpasswd.rb +++ b/lib/chef/provider/group/gpasswd.rb @@ -17,15 +17,12 @@ # require 'chef/provider/group/groupadd' -require 'chef/mixin/shell_out' class Chef class Provider class Group class Gpasswd < Chef::Provider::Group::Groupadd - include Chef::Mixin::ShellOut - def load_current_resource super end diff --git a/lib/chef/provider/group/groupmod.rb b/lib/chef/provider/group/groupmod.rb index c8b6458db0..7ad762af8d 100644 --- a/lib/chef/provider/group/groupmod.rb +++ b/lib/chef/provider/group/groupmod.rb @@ -16,15 +16,11 @@ # limitations under the License. # -require 'chef/mixin/shell_out' - class Chef class Provider class Group class Groupmod < Chef::Provider::Group - include Chef::Mixin::ShellOut - def load_current_resource super [ "group", "user" ].each do |binary| diff --git a/lib/chef/provider/group/suse.rb b/lib/chef/provider/group/suse.rb index 14380f1705..7ac2831d02 100644 --- a/lib/chef/provider/group/suse.rb +++ b/lib/chef/provider/group/suse.rb @@ -17,15 +17,12 @@ # require 'chef/provider/group/groupadd' -require 'chef/mixin/shell_out' class Chef class Provider class Group class Suse < Chef::Provider::Group::Groupadd - include Chef::Mixin::ShellOut - def load_current_resource super end diff --git a/lib/chef/provider/group/usermod.rb b/lib/chef/provider/group/usermod.rb index e6f3fc9e0f..e9dcc38b43 100644 --- a/lib/chef/provider/group/usermod.rb +++ b/lib/chef/provider/group/usermod.rb @@ -17,15 +17,12 @@ # require 'chef/provider/group/groupadd' -require 'chef/mixin/shell_out' class Chef class Provider class Group class Usermod < Chef::Provider::Group::Groupadd - include Chef::Mixin::ShellOut - def load_current_resource super end diff --git a/lib/chef/provider/http_request.rb b/lib/chef/provider/http_request.rb index 2bbd20aa2c..ba54b10195 100644 --- a/lib/chef/provider/http_request.rb +++ b/lib/chef/provider/http_request.rb @@ -33,13 +33,13 @@ class Chef @http = Chef::HTTP::Simple.new(@new_resource.url) end - # Send a HEAD request to @new_resource.url, with ?message=@new_resource.message + # Send a HEAD request to @new_resource.url def action_head message = check_message(@new_resource.message) # CHEF-4762: we expect a nil return value from Chef::HTTP for a "200 Success" response # and false for a "304 Not Modified" response modified = @http.head( - "#{@new_resource.url}?message=#{message}", + "#{@new_resource.url}", @new_resource.headers ) Chef::Log.info("#{@new_resource} HEAD to #{@new_resource.url} successful") @@ -50,13 +50,13 @@ class Chef end end - # Send a GET request to @new_resource.url, with ?message=@new_resource.message + # Send a GET request to @new_resource.url def action_get converge_by("#{@new_resource} GET to #{@new_resource.url}") do message = check_message(@new_resource.message) body = @http.get( - "#{@new_resource.url}?message=#{message}", + "#{@new_resource.url}", @new_resource.headers ) Chef::Log.info("#{@new_resource} GET to #{@new_resource.url} successful") diff --git a/lib/chef/provider/link.rb b/lib/chef/provider/link.rb index c41fbcad95..d6602c2e03 100644 --- a/lib/chef/provider/link.rb +++ b/lib/chef/provider/link.rb @@ -18,7 +18,6 @@ require 'chef/config' require 'chef/log' -require 'chef/mixin/shell_out' require 'chef/mixin/file_class' require 'chef/resource/link' require 'chef/provider' @@ -29,7 +28,6 @@ class Chef class Link < Chef::Provider include Chef::Mixin::EnforceOwnershipAndPermissions - include Chef::Mixin::ShellOut include Chef::Mixin::FileClass def negative_complement(big) diff --git a/lib/chef/provider/log.rb b/lib/chef/provider/log.rb index e65f01e69a..0aee349705 100644 --- a/lib/chef/provider/log.rb +++ b/lib/chef/provider/log.rb @@ -25,9 +25,6 @@ class Chef # Chef log provider, allows logging to chef's logs from recipes class ChefLog < Chef::Provider - # ordered array of the log levels - @@levels = [ :debug, :info, :warn, :error, :fatal ] - # No concept of a 'current' resource for logs, this is a no-op # # === Return @@ -42,18 +39,7 @@ class Chef # true:: Always return true def action_write Chef::Log.send(@new_resource.level, @new_resource.message) - - # resolve the integers for the current log levels - global_level = Mixlib::Log::LEVELS.fetch(Chef::Log.level) - resource_level = Mixlib::Log::LEVELS.fetch(@new_resource.level) - - # If the resource level is greater than or the same os the global - # level, then it should have been written to the log. Mark the - # resource as updated. - if resource_level >= global_level - @new_resource.updated_by_last_action(true) - end - + @new_resource.updated_by_last_action(true) end end @@ -63,6 +49,3 @@ class Chef end end - - - diff --git a/lib/chef/provider/mdadm.rb b/lib/chef/provider/mdadm.rb index 51c9b8d3c6..d156e49d48 100644 --- a/lib/chef/provider/mdadm.rb +++ b/lib/chef/provider/mdadm.rb @@ -17,15 +17,12 @@ # require 'chef/log' -require 'chef/mixin/shell_out' require 'chef/provider' class Chef class Provider class Mdadm < Chef::Provider - include Chef::Mixin::ShellOut - def popen4 raise Exception, "deprecated" end diff --git a/lib/chef/provider/mount/mount.rb b/lib/chef/provider/mount/mount.rb index 22d61a9236..bb1b796290 100644 --- a/lib/chef/provider/mount/mount.rb +++ b/lib/chef/provider/mount/mount.rb @@ -18,13 +18,11 @@ require 'chef/provider/mount' require 'chef/log' -require 'chef/mixin/shell_out' class Chef class Provider class Mount class Mount < Chef::Provider::Mount - include Chef::Mixin::ShellOut def initialize(new_resource, run_context) super diff --git a/lib/chef/provider/mount/solaris.rb b/lib/chef/provider/mount/solaris.rb index 85158eb564..462fa32b71 100644 --- a/lib/chef/provider/mount/solaris.rb +++ b/lib/chef/provider/mount/solaris.rb @@ -20,14 +20,12 @@ require 'chef/provider/mount' require 'chef/log' -require 'chef/mixin/shell_out' require 'forwardable' class Chef class Provider class Mount class Solaris < Chef::Provider::Mount - include Chef::Mixin::ShellOut extend Forwardable VFSTAB = "/etc/vfstab".freeze diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb index adae880b7f..a4a056dfec 100644 --- a/lib/chef/provider/package.rb +++ b/lib/chef/provider/package.rb @@ -198,7 +198,6 @@ class Chef Chef::Log.debug("#{@new_resource} fetching preseed file to #{cache_seed_to}") - if template_available?(@new_resource.response_file) Chef::Log.debug("#{@new_resource} fetching preseed file via Template") remote_file = Chef::Resource::Template.new(cache_seed_to, run_context) diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb index 76d90a5a97..0d91d0d1f0 100644 --- a/lib/chef/provider/package/apt.rb +++ b/lib/chef/provider/package/apt.rb @@ -19,15 +19,12 @@ require 'chef/provider/package' require 'chef/mixin/command' require 'chef/resource/package' -require 'chef/mixin/shell_out' - class Chef class Provider class Package class Apt < Chef::Provider::Package - include Chef::Mixin::ShellOut attr_accessor :is_virtual_package def load_current_resource diff --git a/lib/chef/provider/package/easy_install.rb b/lib/chef/provider/package/easy_install.rb index 6c9dacc55d..2af8a72e61 100644 --- a/lib/chef/provider/package/easy_install.rb +++ b/lib/chef/provider/package/easy_install.rb @@ -18,17 +18,13 @@ require 'chef/provider/package' require 'chef/mixin/command' -require 'chef/mixin/shell_out' require 'chef/resource/package' -require 'chef/mixin/shell_out' class Chef class Provider class Package class EasyInstall < Chef::Provider::Package - include Chef::Mixin::ShellOut - def install_check(name) check = false diff --git a/lib/chef/provider/package/freebsd/base.rb b/lib/chef/provider/package/freebsd/base.rb index 24f79484f0..b0f05667ff 100644 --- a/lib/chef/provider/package/freebsd/base.rb +++ b/lib/chef/provider/package/freebsd/base.rb @@ -21,7 +21,6 @@ require 'chef/resource/package' require 'chef/provider/package' -require 'chef/mixin/shell_out' require 'chef/mixin/get_source_from_package' class Chef @@ -63,9 +62,7 @@ class Chef end end - class Base < Chef::Provider::Package - include Chef::Mixin::ShellOut include Chef::Mixin::GetSourceFromPackage def initialize(*args) diff --git a/lib/chef/provider/package/freebsd/pkgng.rb b/lib/chef/provider/package/freebsd/pkgng.rb index da531facc1..0741a4d95f 100644 --- a/lib/chef/provider/package/freebsd/pkgng.rb +++ b/lib/chef/provider/package/freebsd/pkgng.rb @@ -52,8 +52,6 @@ class Chef @new_resource.source ? file_candidate_version : repo_candidate_version end - - private def file_candidate_version diff --git a/lib/chef/provider/package/freebsd/port.rb b/lib/chef/provider/package/freebsd/port.rb index 6f4471a6f7..4b3510f0e9 100644 --- a/lib/chef/provider/package/freebsd/port.rb +++ b/lib/chef/provider/package/freebsd/port.rb @@ -54,8 +54,6 @@ class Chef super(@new_resource.package_name) end - - private def supports_pkgng? diff --git a/lib/chef/provider/package/ips.rb b/lib/chef/provider/package/ips.rb index 2c6d98d81a..fed3e34124 100644 --- a/lib/chef/provider/package/ips.rb +++ b/lib/chef/provider/package/ips.rb @@ -21,14 +21,12 @@ require 'open3' require 'chef/provider/package' require 'chef/mixin/command' require 'chef/resource/package' -require 'chef/mixin/shell_out' class Chef class Provider class Package class Ips < Chef::Provider::Package - include Chef::Mixin::ShellOut attr_accessor :virtual def define_resource_requirements @@ -98,4 +96,3 @@ class Chef end end end - diff --git a/lib/chef/provider/package/paludis.rb b/lib/chef/provider/package/paludis.rb index e304cd3b8e..f40962d74d 100644 --- a/lib/chef/provider/package/paludis.rb +++ b/lib/chef/provider/package/paludis.rb @@ -18,15 +18,12 @@ require 'chef/provider/package' require 'chef/resource/package' -require 'chef/mixin/shell_out' class Chef class Provider class Package class Paludis < Chef::Provider::Package - include Chef::Mixin::ShellOut - def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.package_name) @current_resource.package_name(@new_resource.package_name) @@ -87,5 +84,3 @@ class Chef end end end - - diff --git a/lib/chef/provider/package/portage.rb b/lib/chef/provider/package/portage.rb index ea8e37e5d4..6a3587558a 100644 --- a/lib/chef/provider/package/portage.rb +++ b/lib/chef/provider/package/portage.rb @@ -55,7 +55,6 @@ class Chef @current_resource end - def parse_emerge(package, txt) availables = {} found_package_name = nil @@ -103,7 +102,6 @@ class Chef end - def install_package(name, version) pkg = "=#{name}-#{version}" diff --git a/lib/chef/provider/package/rpm.rb b/lib/chef/provider/package/rpm.rb index 74baddb062..9cfd6bb010 100644 --- a/lib/chef/provider/package/rpm.rb +++ b/lib/chef/provider/package/rpm.rb @@ -85,7 +85,6 @@ class Chef end end - @current_resource end @@ -119,4 +118,3 @@ class Chef end end end - diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb index b423c199a0..be0022f4aa 100644 --- a/lib/chef/provider/package/rubygems.rb +++ b/lib/chef/provider/package/rubygems.rb @@ -352,8 +352,6 @@ class Chef end - include Chef::Mixin::ShellOut - attr_reader :gem_env attr_reader :cleanup_gem_env diff --git a/lib/chef/provider/package/smartos.rb b/lib/chef/provider/package/smartos.rb index 28d56ddc2c..19a6b9efef 100644 --- a/lib/chef/provider/package/smartos.rb +++ b/lib/chef/provider/package/smartos.rb @@ -20,7 +20,6 @@ # require 'chef/provider/package' -require 'chef/mixin/shell_out' require 'chef/resource/package' require 'chef/mixin/get_source_from_package' @@ -28,10 +27,8 @@ class Chef class Provider class Package class SmartOS < Chef::Provider::Package - include Chef::Mixin::ShellOut attr_accessor :is_virtual_package - def load_current_resource Chef::Log.debug("#{@new_resource} loading current resource") @current_resource = Chef::Resource::Package.new(@new_resource.name) diff --git a/lib/chef/provider/package/windows/msi.rb b/lib/chef/provider/package/windows/msi.rb index a342600678..cc07909d8e 100644 --- a/lib/chef/provider/package/windows/msi.rb +++ b/lib/chef/provider/package/windows/msi.rb @@ -18,7 +18,6 @@ # TODO: Allow @new_resource.source to be a Product Code as a GUID for uninstall / network install -require 'chef/mixin/shell_out' require 'chef/win32/api/installer' if RUBY_PLATFORM =~ /mswin|mingw32|windows/ class Chef @@ -27,7 +26,6 @@ class Chef class Windows class MSI include Chef::ReservedNames::Win32::API::Installer if RUBY_PLATFORM =~ /mswin|mingw32|windows/ - include Chef::Mixin::ShellOut def initialize(resource) @new_resource = resource diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb index c241c7fd6d..d15f22ae16 100644 --- a/lib/chef/provider/package/yum.rb +++ b/lib/chef/provider/package/yum.rb @@ -19,12 +19,10 @@ require 'chef/config' require 'chef/provider/package' require 'chef/mixin/command' -require 'chef/mixin/shell_out' require 'chef/resource/package' require 'singleton' require 'chef/mixin/get_source_from_package' - class Chef class Provider class Package @@ -647,7 +645,6 @@ class Chef # Cache for our installed and available packages, pulled in from yum-dump.py class YumCache include Chef::Mixin::Command - include Chef::Mixin::ShellOut include Singleton def initialize @@ -949,7 +946,6 @@ class Chef end # YumCache include Chef::Mixin::GetSourceFromPackage - include Chef::Mixin::ShellOut def initialize(new_resource, run_context) super diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb index b288d5d954..2639f18deb 100644 --- a/lib/chef/provider/package/zypper.rb +++ b/lib/chef/provider/package/zypper.rb @@ -22,7 +22,6 @@ require 'chef/provider/package' require 'chef/mixin/command' require 'chef/resource/package' -require 'chef/mixin/shell_out' require 'singleton' class Chef @@ -30,8 +29,6 @@ class Chef class Package class Zypper < Chef::Provider::Package - include Chef::Mixin::ShellOut - def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) diff --git a/lib/chef/provider/registry_key.rb b/lib/chef/provider/registry_key.rb index 3b5be93ba1..01ee57895e 100644 --- a/lib/chef/provider/registry_key.rb +++ b/lib/chef/provider/registry_key.rb @@ -25,7 +25,6 @@ require 'chef/provider' require 'etc' require 'fileutils' require 'chef/scan_access_control' -require 'chef/mixin/shell_out' require 'chef/win32/registry' class Chef @@ -33,7 +32,6 @@ class Chef class Provider class RegistryKey < Chef::Provider include Chef::Mixin::Checksum - include Chef::Mixin::ShellOut def whyrun_supported? true diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb index 129c5208f0..77e2754b08 100644 --- a/lib/chef/provider/remote_directory.rb +++ b/lib/chef/provider/remote_directory.rb @@ -40,7 +40,6 @@ class Chef name !~ /(?:^|#{Regexp.escape(::File::SEPARATOR)})\.\.?$/ end) - files_to_transfer.each do |cookbook_file_relative_path| create_cookbook_file(cookbook_file_relative_path) # the file is removed from the purge list diff --git a/lib/chef/provider/remote_file.rb b/lib/chef/provider/remote_file.rb index ed99c0bb84..da2573dacb 100644 --- a/lib/chef/provider/remote_file.rb +++ b/lib/chef/provider/remote_file.rb @@ -50,4 +50,3 @@ class Chef end end end - diff --git a/lib/chef/provider/remote_file/cache_control_data.rb b/lib/chef/provider/remote_file/cache_control_data.rb index 0add74f50a..8331f3d31a 100644 --- a/lib/chef/provider/remote_file/cache_control_data.rb +++ b/lib/chef/provider/remote_file/cache_control_data.rb @@ -161,5 +161,3 @@ class Chef end end end - - diff --git a/lib/chef/provider/remote_file/fetcher.rb b/lib/chef/provider/remote_file/fetcher.rb index c3a098ed35..249b29186f 100644 --- a/lib/chef/provider/remote_file/fetcher.rb +++ b/lib/chef/provider/remote_file/fetcher.rb @@ -17,7 +17,6 @@ # limitations under the License. # - class Chef class Provider class RemoteFile @@ -40,4 +39,3 @@ class Chef end end end - diff --git a/lib/chef/provider/remote_file/ftp.rb b/lib/chef/provider/remote_file/ftp.rb index 7f3fdbf383..3f78286aa3 100644 --- a/lib/chef/provider/remote_file/ftp.rb +++ b/lib/chef/provider/remote_file/ftp.rb @@ -81,7 +81,6 @@ class Chef @filename end - def fetch with_connection do get diff --git a/lib/chef/provider/resource_update.rb b/lib/chef/provider/resource_update.rb index 54f25738ed..e069a8201c 100644 --- a/lib/chef/provider/resource_update.rb +++ b/lib/chef/provider/resource_update.rb @@ -50,6 +50,3 @@ class Chef end end end - - - diff --git a/lib/chef/provider/service/freebsd.rb b/lib/chef/provider/service/freebsd.rb index 7da29851c2..800cd4ec13 100644 --- a/lib/chef/provider/service/freebsd.rb +++ b/lib/chef/provider/service/freebsd.rb @@ -16,7 +16,6 @@ # limitations under the License. # -require 'chef/mixin/shell_out' require 'chef/resource/service' require 'chef/provider/service/init' require 'chef/mixin/command' @@ -26,8 +25,6 @@ class Chef class Service class Freebsd < Chef::Provider::Service::Init - include Chef::Mixin::ShellOut - def load_current_resource @current_resource = Chef::Resource::Service.new(@new_resource.name) @current_resource.service_name(@new_resource.service_name) diff --git a/lib/chef/provider/service/init.rb b/lib/chef/provider/service/init.rb index 64f4074d8b..cf5da852c3 100644 --- a/lib/chef/provider/service/init.rb +++ b/lib/chef/provider/service/init.rb @@ -16,7 +16,6 @@ # limitations under the License. # -require 'chef/mixin/shell_out' require 'chef/provider/service/simple' require 'chef/mixin/command' @@ -25,8 +24,6 @@ class Chef class Service class Init < Chef::Provider::Service::Simple - include Chef::Mixin::ShellOut - def initialize(new_resource, run_context) super @init_command = "/etc/init.d/#{@new_resource.service_name}" diff --git a/lib/chef/provider/service/macosx.rb b/lib/chef/provider/service/macosx.rb index 2642a7e204..36930ee4ac 100644 --- a/lib/chef/provider/service/macosx.rb +++ b/lib/chef/provider/service/macosx.rb @@ -24,7 +24,6 @@ class Chef class Provider class Service class Macosx < Chef::Provider::Service::Simple - include Chef::Mixin::ShellOut def self.gather_plist_dirs locations = %w{/Library/LaunchAgents diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb index 5275bcac29..7a7b2a1c40 100644 --- a/lib/chef/provider/service/redhat.rb +++ b/lib/chef/provider/service/redhat.rb @@ -17,13 +17,11 @@ # require 'chef/provider/service/init' -require 'chef/mixin/shell_out' class Chef class Provider class Service class Redhat < Chef::Provider::Service::Init - include Chef::Mixin::ShellOut CHKCONFIG_ON = /\d:on/ CHKCONFIG_MISSING = /No such/ diff --git a/lib/chef/provider/service/simple.rb b/lib/chef/provider/service/simple.rb index 3b3e539d40..f03b8a18a1 100644 --- a/lib/chef/provider/service/simple.rb +++ b/lib/chef/provider/service/simple.rb @@ -16,7 +16,6 @@ # limitations under the License. # -require 'chef/mixin/shell_out' require 'chef/provider/service' require 'chef/resource/service' require 'chef/mixin/command' @@ -26,8 +25,6 @@ class Chef class Service class Simple < Chef::Provider::Service - include Chef::Mixin::ShellOut - def load_current_resource @current_resource = Chef::Resource::Service.new(@new_resource.name) @current_resource.service_name(@new_resource.service_name) diff --git a/lib/chef/provider/service/solaris.rb b/lib/chef/provider/service/solaris.rb index 69a79e6226..0c47a3462b 100644 --- a/lib/chef/provider/service/solaris.rb +++ b/lib/chef/provider/service/solaris.rb @@ -16,7 +16,6 @@ # limitations under the License. # -require 'chef/mixin/shell_out' require 'chef/provider/service' require 'chef/resource/service' require 'chef/mixin/command' @@ -25,7 +24,6 @@ class Chef class Provider class Service class Solaris < Chef::Provider::Service - include Chef::Mixin::ShellOut attr_reader :maintenance def initialize(new_resource, run_context=nil) @@ -35,7 +33,6 @@ class Chef @maintenace = false end - def load_current_resource @current_resource = Chef::Resource::Service.new(@new_resource.name) @current_resource.service_name(@new_resource.service_name) diff --git a/lib/chef/provider/service/systemd.rb b/lib/chef/provider/service/systemd.rb index ea43e658e4..6231603d03 100644 --- a/lib/chef/provider/service/systemd.rb +++ b/lib/chef/provider/service/systemd.rb @@ -18,7 +18,6 @@ require 'chef/resource/service' require 'chef/provider/service/simple' -require 'chef/mixin/shell_out' class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple def load_current_resource diff --git a/lib/chef/provider/service/windows.rb b/lib/chef/provider/service/windows.rb index 2cfa9d44c1..2d478fa9fe 100644 --- a/lib/chef/provider/service/windows.rb +++ b/lib/chef/provider/service/windows.rb @@ -18,7 +18,6 @@ # limitations under the License. # -require 'chef/mixin/shell_out' require 'chef/provider/service/simple' if RUBY_PLATFORM =~ /mswin|mingw32|windows/ require 'win32/service' @@ -26,8 +25,6 @@ end class Chef::Provider::Service::Windows < Chef::Provider::Service - include Chef::Mixin::ShellOut - #Win32::Service.get_start_type AUTO_START = 'auto start' DISABLED = 'disabled' diff --git a/lib/chef/provider/subversion.rb b/lib/chef/provider/subversion.rb index 6ceb3e592a..81ed639c53 100644 --- a/lib/chef/provider/subversion.rb +++ b/lib/chef/provider/subversion.rb @@ -16,7 +16,6 @@ # limitations under the License. # - #TODO subversion and git should both extend from a base SCM provider. require 'chef/log' @@ -199,7 +198,6 @@ class Chef ['svn', *args].compact.join(" ") end - def target_dir_non_existent_or_empty? !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..'] end diff --git a/lib/chef/provider/template.rb b/lib/chef/provider/template.rb index 555f4f14f0..48cc45f3a8 100644 --- a/lib/chef/provider/template.rb +++ b/lib/chef/provider/template.rb @@ -22,7 +22,6 @@ require 'chef/provider/file' require 'chef/deprecation/provider/template' require 'chef/deprecation/warnings' - class Chef class Provider class Template < Chef::Provider::File @@ -63,4 +62,3 @@ class Chef end end end - diff --git a/lib/chef/provider/template/content.rb b/lib/chef/provider/template/content.rb index 472ea331a3..7fc680ec85 100644 --- a/lib/chef/provider/template/content.rb +++ b/lib/chef/provider/template/content.rb @@ -58,4 +58,3 @@ class Chef end end end - diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb index b01931609e..96b5db24ba 100644 --- a/lib/chef/provider/user/dscl.rb +++ b/lib/chef/provider/user/dscl.rb @@ -16,7 +16,6 @@ # limitations under the License. # -require 'chef/mixin/shell_out' require 'chef/provider/user' require 'openssl' @@ -24,7 +23,6 @@ class Chef class Provider class User class Dscl < Chef::Provider::User - include Chef::Mixin::ShellOut NFS_HOME_DIRECTORY = %r{^NFSHomeDirectory: (.*)$} AUTHENTICATION_AUTHORITY = %r{^AuthenticationAuthority: (.*)$} diff --git a/lib/chef/provider/user/solaris.rb b/lib/chef/provider/user/solaris.rb index df491ac697..d480acaced 100644 --- a/lib/chef/provider/user/solaris.rb +++ b/lib/chef/provider/user/solaris.rb @@ -89,4 +89,3 @@ class Chef end end end - diff --git a/lib/chef/provider/user/useradd.rb b/lib/chef/provider/user/useradd.rb index 201bcbe542..cad9d58e7e 100644 --- a/lib/chef/provider/user/useradd.rb +++ b/lib/chef/provider/user/useradd.rb @@ -17,7 +17,6 @@ # require 'pathname' -require 'chef/mixin/shell_out' require 'chef/provider/user' class Chef @@ -25,8 +24,6 @@ class Chef class User class Useradd < Chef::Provider::User - include Chef::Mixin::ShellOut - UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:password, "-p"], [:shell, "-s"], [:uid, "-u"]] def create_user diff --git a/lib/chef/resource/lwrp_base.rb b/lib/chef/resource/lwrp_base.rb index b293b85944..5b67941a8b 100644 --- a/lib/chef/resource/lwrp_base.rb +++ b/lib/chef/resource/lwrp_base.rb @@ -92,9 +92,16 @@ class Chef # Sets the default action def self.default_action(action_name=NULL_ARG) unless action_name.equal?(NULL_ARG) - action = action_name.to_sym - actions.push(action) unless actions.include?(action) - @default_action = action + @actions ||= [] + if action_name.is_a?(Array) + action = action_name.map { |arg| arg.to_sym } + @actions = actions | action + @default_action = action + else + action = action_name.to_sym + @actions.push(action) unless @actions.include?(action) + @default_action = action + end end @default_action ||= from_superclass(:default_action) diff --git a/lib/chef/shell/shell_session.rb b/lib/chef/shell/shell_session.rb index a158020116..73e6c34ebb 100644 --- a/lib/chef/shell/shell_session.rb +++ b/lib/chef/shell/shell_session.rb @@ -169,7 +169,7 @@ module Shell def rebuild_context @run_status = Chef::RunStatus.new(@node, @events) - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, Chef::Config[:cookbook_path]) } + Chef::Cookbook::FileVendor.fetch_from_disk(Chef::Config[:cookbook_path]) cl = Chef::CookbookLoader.new(Chef::Config[:cookbook_path]) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) @@ -201,7 +201,7 @@ module Shell def rebuild_context @run_status = Chef::RunStatus.new(@node, @events) - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::RemoteFileVendor.new(manifest, Chef::REST.new(Chef::Config[:server_url])) } + Chef::Cookbook::FileVendor.fetch_from_remote(Chef::REST.new(Chef::Config[:chef_server_url])) cookbook_hash = @client.sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbook_hash) @run_context = Chef::RunContext.new(node, cookbook_collection, @events) diff --git a/lib/chef/streaming_cookbook_uploader.rb b/lib/chef/streaming_cookbook_uploader.rb deleted file mode 100644 index 9e638f6367..0000000000 --- a/lib/chef/streaming_cookbook_uploader.rb +++ /dev/null @@ -1,205 +0,0 @@ -# inspired by/cargo-culted from http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html -# On Apr 6, 2010, at 3:00 PM, Stanislav Vitvitskiy wrote: -# -# It's free to use / modify / distribute. No need to mention anything. Just copy/paste and use. -# -# Regards, -# Stan - -require 'net/http' -require 'mixlib/authentication/signedheaderauth' -require 'openssl' -require 'chef/version' - -class Chef - class StreamingCookbookUploader - - DefaultHeaders = { 'accept' => 'application/json', 'x-chef-version' => ::Chef::VERSION } - - class << self - - def post(to_url, user_id, secret_key_filename, params = {}, headers = {}) - make_request(:post, to_url, user_id, secret_key_filename, params, headers) - end - - def put(to_url, user_id, secret_key_filename, params = {}, headers = {}) - make_request(:put, to_url, user_id, secret_key_filename, params, headers) - end - - def make_request(http_verb, to_url, user_id, secret_key_filename, params = {}, headers = {}) - Chef::Log.warn('[DEPRECATED] StreamingCookbookUploader class is deprecated. It will be removed in Chef 12. Please use CookbookSiteStreamingUploader instead.') - boundary = '----RubyMultipartClient' + rand(1000000).to_s + 'ZZZZZ' - parts = [] - content_file = nil - - timestamp = Time.now.utc.iso8601 - secret_key = OpenSSL::PKey::RSA.new(File.read(secret_key_filename)) - - unless params.nil? || params.empty? - params.each do |key, value| - if value.kind_of?(File) - content_file = value - filepath = value.path - filename = File.basename(filepath) - parts << StringPart.new( "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"" + key.to_s + "\"; filename=\"" + filename + "\"\r\n" + - "Content-Type: application/octet-stream\r\n\r\n") - parts << StreamPart.new(value, File.size(filepath)) - parts << StringPart.new("\r\n") - else - parts << StringPart.new( "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"" + key.to_s + "\"\r\n\r\n") - parts << StringPart.new(value.to_s + "\r\n") - end - end - parts << StringPart.new("--" + boundary + "--\r\n") - end - - body_stream = MultipartStream.new(parts) - - timestamp = Time.now.utc.iso8601 - - url = URI.parse(to_url) - - Chef::Log.logger.debug("Signing: method: #{http_verb}, path: #{url.path}, file: #{content_file}, User-id: #{user_id}, Timestamp: #{timestamp}") - - # We use the body for signing the request if the file parameter - # wasn't a valid file or wasn't included. Extract the body (with - # multi-part delimiters intact) to sign the request. - # TODO: tim: 2009-12-28: It'd be nice to remove this special case, and - # always hash the entire request body. In the file case it would just be - # expanded multipart text - the entire body of the POST. - content_body = parts.inject("") { |result,part| result + part.read(0, part.size) } - content_file.rewind if content_file # we consumed the file for the above operation, so rewind it. - - signing_options = { - :http_method=>http_verb, - :path=>url.path, - :user_id=>user_id, - :timestamp=>timestamp} - (content_file && signing_options[:file] = content_file) || (signing_options[:body] = (content_body || "")) - - headers.merge!(Mixlib::Authentication::SignedHeaderAuth.signing_object(signing_options).sign(secret_key)) - - content_file.rewind if content_file - - # net/http doesn't like symbols for header keys, so we'll to_s each one just in case - headers = DefaultHeaders.merge(Hash[*headers.map{ |k,v| [k.to_s, v] }.flatten]) - - req = case http_verb - when :put - Net::HTTP::Put.new(url.path, headers) - when :post - Net::HTTP::Post.new(url.path, headers) - end - req.content_length = body_stream.size - req.content_type = 'multipart/form-data; boundary=' + boundary unless parts.empty? - req.body_stream = body_stream - - http = Net::HTTP.new(url.host, url.port) - if url.scheme == "https" - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - end - res = http.request(req) - #res = http.start {|http_proc| http_proc.request(req) } - - # alias status to code and to_s to body for test purposes - # TODO: stop the following madness! - class << res - alias :to_s :body - - # BUGBUG this makes the response compatible with what respsonse_steps expects to test headers (response.headers[] -> response[]) - def headers - self - end - - def status - code.to_i - end - end - res - end - - end - - class StreamPart - def initialize(stream, size) - Chef::Log.warn('[DEPRECATED] StreamingCookbookUploader::StreamPart class is deprecated. It will be removed in Chef 12. Please use CookbookSiteStreamingUploader::StreamPart instead.') - @stream, @size = stream, size - end - - def size - @size - end - - # read the specified amount from the stream - def read(offset, how_much) - @stream.read(how_much) - end - end - - class StringPart - def initialize(str) - Chef::Log.warn('[DEPRECATED] StreamingCookbookUploader::StringPart class is deprecated. It will be removed in Chef 12. Please use CookbookSiteStreamingUploader::StringPart instead.') - @str = str - end - - def size - @str.length - end - - # read the specified amount from the string startiung at the offset - def read(offset, how_much) - @str[offset, how_much] - end - end - - class MultipartStream - def initialize(parts) - Chef::Log.warn('[DEPRECATED] StreamingCookbookUploader::MultipartStream class is deprecated. It will be removed in Chef 12. Please use CookbookSiteStreamingUploader::MultipartStream instead.') - @parts = parts - @part_no = 0 - @part_offset = 0 - end - - def size - @parts.inject(0) {|size, part| size + part.size} - end - - def read(how_much) - return nil if @part_no >= @parts.size - - how_much_current_part = @parts[@part_no].size - @part_offset - - how_much_current_part = if how_much_current_part > how_much - how_much - else - how_much_current_part - end - - how_much_next_part = how_much - how_much_current_part - - current_part = @parts[@part_no].read(@part_offset, how_much_current_part) - - # recurse into the next part if the current one was not large enough - if how_much_next_part > 0 - @part_no += 1 - @part_offset = 0 - next_part = read(how_much_next_part) - current_part + if next_part - next_part - else - '' - end - else - @part_offset += how_much_current_part - current_part - end - end - end - - end - - -end diff --git a/lib/chef/tasks/chef_repo.rake b/lib/chef/tasks/chef_repo.rake index 704557ebb3..14a5bcc0c1 100644 --- a/lib/chef/tasks/chef_repo.rake +++ b/lib/chef/tasks/chef_repo.rake @@ -1,6 +1,7 @@ # # Author:: Adam Jacob (<adam@opscode.com>) # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc. +# Copyright:: Copyright (c) 2014, Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,320 +16,186 @@ # See the License for the specific language governing permissions and # limitations under the License. # - -require 'rubygems' -require 'chef/json_compat' -require 'chef' -require 'chef/role' -require 'chef/cookbook/metadata' -require 'tempfile' +TOPDIR = '.' require 'rake' -# Allow REMOTE options to be overridden on the command line -REMOTE_HOST = ENV["REMOTE_HOST"] if ENV["REMOTE_HOST"] != nil -REMOTE_SUDO = ENV["REMOTE_SUDO"] if ENV["REMOTE_SUDO"] != nil -if defined? REMOTE_HOST - REMOTE_PATH_PREFIX = "#{REMOTE_HOST}:" - REMOTE_EXEC_PREFIX = "ssh #{REMOTE_HOST}" - REMOTE_EXEC_PREFIX += " sudo" if defined? REMOTE_SUDO - LOCAL_EXEC_PREFIX = "" -else - REMOTE_PATH_PREFIX = "" - REMOTE_EXEC_PREFIX = "" - LOCAL_EXEC_PREFIX = "sudo" -end - -desc "Update your repository from source control" -task :update do - puts "** Updating your repository" - - case $vcs - when :svn - sh %{svn up} - when :git - pull = false - IO.foreach(File.join(TOPDIR, ".git", "config")) do |line| - pull = true if line =~ /\[remote "origin"\]/ - end - if pull - sh %{git pull} - else - puts "* Skipping git pull, no origin specified" - end - else - puts "* No SCM configured, skipping update" - end +desc "By default, print deprecation notice" +task :default do + puts deprecation_notice end desc "Install the latest copy of the repository on this Chef Server" -task :install => [ :update, :roles, :upload_cookbooks ] do - if File.exists?(File.join(TOPDIR, "config", "server.rb")) - puts "* Installing new Chef Server Config" - sh "#{LOCAL_EXEC_PREFIX} rsync -rlt --delete --exclude '.svn' --exclude '.git*' config/server.rb #{REMOTE_PATH_PREFIX}#{CHEF_SERVER_CONFIG}" - end - if File.exists?(File.join(TOPDIR, "config", "client.rb")) - puts "* Installing new Chef Client Config" - sh "#{LOCAL_EXEC_PREFIX} rsync -rlt --delete --exclude '.svn' --exclude '.git*' config/client.rb #{REMOTE_PATH_PREFIX}#{CHEF_CLIENT_CONFIG}" - end +task :install do + puts deprecation_notice + puts 'The `install` rake task, which included the `update`, `roles`, and' + puts '`upload_cookbooks` rake tasks is replaced by the `knife upload`' + puts 'sub-command. The notion of "installing" the chef-repo to the Chef' + puts 'Server. Previously the `install` task would manage server and' + puts 'client configuration. This will not work at all on Chef Server 11+' + puts 'and client configuration should be managed with the `chef-client`' + puts 'cookbook.' end -desc "By default, run rake test_cookbooks" -task :default => [ :test_cookbooks ] +desc "Update your repository from source control" +task :update do + puts deprecation_notice + puts 'The `update` rake task previously updated the chef-repo from' + puts 'the detected version control system, either svn or git. However,' + puts 'it has not been recommended for users for years. Most users in' + puts 'the community use `git`, so the Subversion functionality is not' + puts 'required, and `git pull` is sufficient for many workflows. The' + puts 'world of git workflows is rather different now than it was when' + puts 'this rake task was created.' +end desc "Create a new cookbook (with COOKBOOK=name, optional CB_PREFIX=site-)" task :new_cookbook do - puts "***WARN: rake new_cookbook is deprecated. Please use 'knife cookbook create COOKBOOK' command.***" - create_cookbook(File.join(TOPDIR, "#{ENV["CB_PREFIX"]}cookbooks")) - create_readme(File.join(TOPDIR, "#{ENV["CB_PREFIX"]}cookbooks")) - create_metadata(File.join(TOPDIR, "#{ENV["CB_PREFIX"]}cookbooks")) -end - -def create_cookbook(dir) - raise "Must provide a COOKBOOK=" unless ENV["COOKBOOK"] - puts "** Creating cookbook #{ENV["COOKBOOK"]}" - sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "attributes")}" - sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "recipes")}" - sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "definitions")}" - sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "libraries")}" - sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "resources")}" - sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "providers")}" - sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "files", "default")}" - sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "templates", "default")}" - unless File.exists?(File.join(dir, ENV["COOKBOOK"], "recipes", "default.rb")) - open(File.join(dir, ENV["COOKBOOK"], "recipes", "default.rb"), "w") do |file| - file.puts <<-EOH -# -# Cookbook Name:: #{ENV["COOKBOOK"]} -# Recipe:: default -# -# Copyright #{Time.now.year}, #{COMPANY_NAME} -# -EOH - case NEW_COOKBOOK_LICENSE - when :apachev2 - file.puts <<-EOH -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -EOH - when :none - file.puts <<-EOH -# All rights reserved - Do Not Redistribute -# -EOH - end - end - end -end - -def create_readme(dir) - raise "Must provide a COOKBOOK=" unless ENV["COOKBOOK"] - puts "** Creating README for cookbook: #{ENV["COOKBOOK"]}" - unless File.exists?(File.join(dir, ENV["COOKBOOK"], "README.rdoc")) - open(File.join(dir, ENV["COOKBOOK"], "README.md"), "w") do |file| - file.puts <<-EOH -Description -=========== - -Requirements -============ - -Attributes -========== - -Usage -===== - -EOH - end - end -end - -def create_metadata(dir) - raise "Must provide a COOKBOOK=" unless ENV["COOKBOOK"] - puts "** Creating metadata for cookbook: #{ENV["COOKBOOK"]}" - - case NEW_COOKBOOK_LICENSE - when :apachev2 - license = "Apache 2.0" - when :none - license = "All rights reserved" - end - - unless File.exists?(File.join(dir, ENV["COOKBOOK"], "metadata.rb")) - open(File.join(dir, ENV["COOKBOOK"], "metadata.rb"), "w") do |file| - if File.exists?(File.join(dir, ENV["COOKBOOK"], 'README.rdoc')) - long_description = "long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))" - end - file.puts <<-EOH -maintainer "#{COMPANY_NAME}" -maintainer_email "#{SSL_EMAIL_ADDRESS}" -license "#{license}" -description "Installs/Configures #{ENV["COOKBOOK"]}" -#{long_description} -version "0.1" -EOH - end - end + cb = ENV['COOKBOOK'] || 'my_cookbook_name' + puts deprecation_notice + puts 'The `new_cookbook` rake task is replaced by the ChefDK cookbook' + puts 'generator. To generate a new cookbook run:' + puts + puts "chef generate cookbook #{ENV['COOKBOOK']}" + puts + puts 'Or, if you are not using ChefDK, use `knife cookbook create`:' + puts + puts "knife cookbook create #{ENV['COOKBOOK']}" end desc "Create a new self-signed SSL certificate for FQDN=foo.example.com" task :ssl_cert do - $expect_verbose = true - fqdn = ENV["FQDN"] - fqdn =~ /^(.+?)\.(.+)$/ - hostname = $1 - domain = $2 - raise "Must provide FQDN!" unless fqdn && hostname && domain - keyfile = fqdn.gsub("*", "wildcard") - puts "** Creating self signed SSL Certificate for #{fqdn}" - sh("(cd #{CADIR} && openssl genrsa 2048 > #{keyfile}.key)") - sh("(cd #{CADIR} && chmod 644 #{keyfile}.key)") - puts "* Generating Self Signed Certificate Request" - tf = Tempfile.new("#{keyfile}.ssl-conf") - ssl_config = <<EOH -[ req ] -distinguished_name = req_distinguished_name -prompt = no - -[ req_distinguished_name ] -C = #{SSL_COUNTRY_NAME} -ST = #{SSL_STATE_NAME} -L = #{SSL_LOCALITY_NAME} -O = #{COMPANY_NAME} -OU = #{SSL_ORGANIZATIONAL_UNIT_NAME} -CN = #{fqdn} -emailAddress = #{SSL_EMAIL_ADDRESS} -EOH - tf.puts(ssl_config) - tf.close - sh("(cd #{CADIR} && openssl req -config '#{tf.path}' -new -x509 -nodes -sha1 -days 3650 -key #{keyfile}.key > #{keyfile}.crt)") - sh("(cd #{CADIR} && openssl x509 -noout -fingerprint -text < #{keyfile}.crt > #{keyfile}.info)") - sh("(cd #{CADIR} && cat #{keyfile}.crt #{keyfile}.key > #{keyfile}.pem)") - sh("(cd #{CADIR} && chmod 644 #{keyfile}.pem)") -end - -rule(%r{\b(?:site-)?cookbooks/[^/]+/metadata\.json\Z} => [ proc { |task_name| task_name.sub(/\.[^.]+$/, '.rb') } ]) do |t| - system("knife cookbook metadata from file #{t.source}") + puts deprecation_notice + puts 'The `ssl_cert` rake task is superseded by using the CHEF-maintained' + puts '`openssl` cookbook\'s `openssl_x509` resource which can generate' + puts 'self-signed certificate chains as convergent resources.' + puts + puts 'https://supermarket.getchef.com/cookbooks/openssl' end desc "Build cookbook metadata.json from metadata.rb" -task :metadata => FileList[File.join(TOPDIR, '*cookbooks', ENV['COOKBOOK'] || '*', 'metadata.rb')].pathmap('%X.json') - -rule(%r{\broles/\S+\.json\Z} => [ proc { |task_name| task_name.sub(/\.[^.]+$/, '.rb') } ]) do |t| - system("knife role from file #{t.source}") +task :metadata do + puts deprecation_notice + puts 'The `metadata` rake task is not recommended. Cookbook' + puts '`metadata.json` is automatically generated from `metadata.rb`' + puts 'by `knife` when uploading cookbooks to the Chef Server.' end desc "Update roles" -task :roles => FileList[File.join(TOPDIR, 'roles', '**', '*.rb')].pathmap('%X.json') +task :roles do + puts deprecation_notice + puts 'The `roles` rake task is not recommended. If you are using Ruby' + puts 'role files (roles/*.rb), you can upload them all with:' + puts + puts 'knife role from file roles/*' + puts + puts 'If you are using JSON role files (roles/*.json), you can upload' + puts 'them all with:' + puts + puts 'knife upload roles/*.json' +end desc "Update a specific role" -task :role, :role_name do |t, args| - system("knife role from file #{File.join(TOPDIR, 'roles', args.role_name)}.rb") +task :role do + puts deprecation_notice + puts 'The `role` rake task is not recommended. If you are using Ruby' + puts 'role files, you can upload a single role with:' + puts + puts 'knife role from file rolename.rb' + puts + puts 'If you are using JSON role files, you can upload a single role with' + puts + puts 'knife upload roles/rolename.json' end desc "Upload all cookbooks" -task :upload_cookbooks => [ :metadata ] task :upload_cookbooks do - system("knife cookbook upload --all") + puts deprecation_notice + puts deprecated_cookbook_upload end desc "Upload a single cookbook" -task :upload_cookbook => [ :metadata ] -task :upload_cookbook, :cookbook do |t, args| - system("knife cookbook upload #{args.cookbook}") +task :upload_cookbook do + puts deprecation_notice + puts deprecated_cookbook_upload end desc "Test all cookbooks" task :test_cookbooks do - system("knife cookbook test --all") + puts deprecation_notice + puts 'The `test_cookbooks` rake task is no longer recommended. Previously' + puts 'it only performed a syntax check, and did no other kind of testing,' + puts 'and the Chef Community has a rich ecosystem of testing tools for' + puts 'various purposes:' + puts + puts '- knife cookbook test will perform a syntax check, as this task did' + puts ' before.' + puts '- rubocop and foodcritic will perform lint checking for Ruby and' + puts ' Chef cookbook style according to community standards.' + puts '- ChefSpec will perform unit testing' + puts '- Test Kitchen will perform convergence and post-convergence' + puts ' testing on virtual machines.' end desc "Test a single cookbook" -task :test_cookbook, :cookbook do |t, args| - system("knife cookbook test #{args.cookbook}") -end +task :test_cookbook => [:test_cookbooks] namespace :databag do - path = "data_bags" - desc "Upload a single databag" - task :upload, :databag do |t, args| - input_databag = args[:databag] || 'none_specified' - databag = File.join(path, input_databag) - - if File.exists?(databag) && File.directory?(databag) - system "knife data bag create #{input_databag}" - Dir.foreach(databag) do |item| - name, type = item.split('.') - if type == 'json' && name.length > 0 - system "knife data bag from file #{input_databag} " + File.join(databag, item) - end - end - else - puts "ERROR: Could not find the databag in your databag path (" + File.join(path, input_databag) + "), skipping it" - end + task :upload do + puts deprecation_notice + puts 'The `data_bags:upload` task is not recommended. You should use' + puts 'the `knife upload` sub-command for uploading data bag items.' + puts + puts 'knife upload data_bags/bagname/itemname.json' end desc "Upload all databags" task :upload_all do - if File.exists?(path) && File.directory?(path) - Dir.foreach(path) do |databag| - if databag == databag[/^[\-[:alnum:]_]+$/] - Rake::Task['databag:upload'].execute( { :databag => databag } ) - end - end - else - puts "ERROR: Could not find any databags, skipping it" - end + puts deprecation_notice + puts 'The `data_bags:upload_all` task is not recommended. You should' + puts 'use the `knife upload` sub-command for uploading data bag items.' + puts + puts 'knife upload data_bags/*' end desc "Create a databag" - task :create, :databag do |t, args| - input_databag = args[:databag] || 'none_specified' - - FileUtils.mkdir(path) unless File.exists?(path) - databag = File.join(path, input_databag) - FileUtils.mkdir(databag) unless File.exists?(databag) + task :create do + puts deprecation_notice + puts deprecated_data_bag_creation end desc "Create a databag item stub" - task :create_item, :databag, :item do |t, args| - input_databag = args[:databag] || 'none_specified' - input_item = args[:item] || false - - databag = File.join(path, input_databag) - if File.exists?(databag) && File.directory?(databag) - if input_item - json_filename = File.join(databag, "#{input_item}.json") - if !File.exists?(json_filename) - stub = <<EOH -{ - "id" : "#{input_item}" -} -EOH - json_file = File.new(json_filename, "w") - json_file.write(stub) - json_file.close - else - puts "ERROR: databag item already exists (#{json_filename}), skipping it" - end - else - puts "ERROR: No item id specified, skipping it" - end - else - puts "ERROR: Could not find your databag (#{databag}), skipping it" - end + task :create_item do + puts deprecation_notice + puts deprecated_data_bag_creation end +end + +def deprecation_notice + %Q[************************************************* +NOTICE: Chef Repository Rake Tasks Are Deprecated +************************************************* +] end +def deprecated_cookbook_upload + %Q[ +The `upload_cookbook` and `upload_cookbooks` rake tasks are not +recommended. These tasks are replaced by other, better workflow +tools, such as `knife cookbook upload`, `knife upload`, or `berks` +] +end + +def deprecated_data_bag_creation + %Q[ +The `data_bags:create` and `data_bags:create_item` tasks are not +recommended. You should create data bag items as JSON files in the data_bags +directory, with a sub-directory for each bag, and use `knife upload` to +upload them. For example, if you have a data bags named `users`, with +`finn`, and `jake` items, you would have: + +./data_bags/users/finn.json +./data-bags/users/jake.json +] +end diff --git a/pedant.gemfile b/pedant.gemfile index 8f223defde..2ce3ed27fb 100644 --- a/pedant.gemfile +++ b/pedant.gemfile @@ -1,8 +1,8 @@ source "https://rubygems.org" gemspec :name => "chef" -gem 'rest-client', :github => 'opscode/rest-client' -gem 'chef-pedant', :github => 'opscode/chef-pedant', :ref => '4744d7f318b629ff60a0d22cf02296df36936397' +gem 'rest-client', :github => 'opscode/rest-client', :branch => 'lcg/1.6.7-version-lying' +gem 'chef-pedant', :github => 'opscode/chef-pedant', :ref => 'd12864964a18994b467908daa91811eee1f1bf68' # TODO figure out how to grab this stuff from the main Gemfile gem "activesupport", "< 4.0.0", :group => :compat_testing, :platform => "ruby" diff --git a/spec/functional/resource/base.rb b/spec/functional/resource/base.rb index 056db39877..cdb52fbc1b 100644 --- a/spec/functional/resource/base.rb +++ b/spec/functional/resource/base.rb @@ -16,16 +16,6 @@ # limitations under the License. # -require 'spec_helper' - -def ohai - # provider is platform-dependent, we need platform ohai data: - @OHAI_SYSTEM ||= begin - ohai = Ohai::System.new - ohai.all_plugins("platform") - ohai - end -end def run_context @run_context ||= begin diff --git a/spec/functional/resource/cookbook_file_spec.rb b/spec/functional/resource/cookbook_file_spec.rb index 173dac8268..7797ed0041 100644 --- a/spec/functional/resource/cookbook_file_spec.rb +++ b/spec/functional/resource/cookbook_file_spec.rb @@ -40,7 +40,7 @@ describe Chef::Resource::CookbookFile do # set up cookbook collection for this run to use, based on our # spec data. cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, 'cookbooks')) - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, cookbook_repo) } + Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_repo) loader = Chef::CookbookLoader.new(cookbook_repo) loader.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(loader) diff --git a/spec/functional/resource/package_spec.rb b/spec/functional/resource/package_spec.rb index 797f308cf9..548db40e79 100644 --- a/spec/functional/resource/package_spec.rb +++ b/spec/functional/resource/package_spec.rb @@ -135,9 +135,7 @@ describe Chef::Resource::Package, metadata do cookbook_path = File.join(CHEF_SPEC_DATA, "cookbooks") cl = Chef::CookbookLoader.new(cookbook_path) cl.load_cookbooks - Chef::Cookbook::FileVendor.on_create do |manifest| - Chef::Cookbook::FileSystemFileVendor.new(manifest, cookbook_path) - end + Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_path) Chef::CookbookCollection.new(cl) end diff --git a/spec/functional/resource/remote_directory_spec.rb b/spec/functional/resource/remote_directory_spec.rb index 70e575abb0..f9eb20711e 100644 --- a/spec/functional/resource/remote_directory_spec.rb +++ b/spec/functional/resource/remote_directory_spec.rb @@ -26,7 +26,7 @@ describe Chef::Resource::RemoteDirectory do def create_resource cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, cookbook_repo) } + Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_repo) node = Chef::Node.new cl = Chef::CookbookLoader.new(cookbook_repo) cl.load_cookbooks diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb index 7816d6357b..fefd995743 100644 --- a/spec/functional/resource/template_spec.rb +++ b/spec/functional/resource/template_spec.rb @@ -37,7 +37,7 @@ describe Chef::Resource::Template do def create_resource cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, cookbook_repo) } + Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_repo) cl = Chef::CookbookLoader.new(cookbook_repo) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) diff --git a/spec/integration/knife/chef_fs_data_store_spec.rb b/spec/integration/knife/chef_fs_data_store_spec.rb index e8a3b3dde5..7e55aa7319 100644 --- a/spec/integration/knife/chef_fs_data_store_spec.rb +++ b/spec/integration/knife/chef_fs_data_store_spec.rb @@ -141,7 +141,7 @@ EOM end it 'knife cookbook upload works' do - knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} x").should_succeed <<EOM + knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} x").should_succeed :stderr => <<EOM Uploading x [1.0.0] Uploaded 1 cookbook. EOM @@ -199,7 +199,7 @@ EOM end it 'knife cookbook upload works' do - knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} z").should_succeed <<EOM + knife("cookbook upload -z --cookbook-path #{path_to('cookbooks_to_upload')} z").should_succeed :stderr => <<EOM Uploading z [1.0.0] Uploaded 1 cookbook. EOM diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb index 75ab0c9cde..ad7104d170 100644 --- a/spec/support/platform_helpers.rb +++ b/spec/support/platform_helpers.rb @@ -27,6 +27,11 @@ def windows? !!(RUBY_PLATFORM =~ /mswin|mingw|windows/) end +def ohai + # This is defined in spec_helper; it has the `platform` populated. + OHAI_SYSTEM +end + require 'wmi-lite/wmi' if windows? def windows_domain_joined? diff --git a/spec/unit/cookbook/file_vendor_spec.rb b/spec/unit/cookbook/file_vendor_spec.rb new file mode 100644 index 0000000000..4fad7d5808 --- /dev/null +++ b/spec/unit/cookbook/file_vendor_spec.rb @@ -0,0 +1,78 @@ +#-- +# Author:: Daniel DeLeo (<dan@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require 'spec_helper' + +describe Chef::Cookbook::FileVendor do + + let(:file_vendor_class) { Class.new(described_class) } + + # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest + let(:manifest) { {:cookbook_name => "bob"} } + + context "when configured to fetch files over http" do + + let(:http) { double("Chef::REST") } + + before do + file_vendor_class.fetch_from_remote(http) + end + + it "sets the vendor class to RemoteFileVendor" do + expect(file_vendor_class.vendor_class).to eq(Chef::Cookbook::RemoteFileVendor) + end + + it "sets the initialization options to the given http object" do + expect(file_vendor_class.initialization_options).to eq(http) + end + + it "creates a RemoteFileVendor for a given manifest" do + file_vendor = file_vendor_class.create_from_manifest(manifest) + expect(file_vendor).to be_a_kind_of(Chef::Cookbook::RemoteFileVendor) + expect(file_vendor.rest).to eq(http) + expect(file_vendor.cookbook_name).to eq("bob") + end + + end + + context "when configured to load files from disk" do + + let(:cookbook_path) { %w[/var/chef/cookbooks /var/chef/other_cookbooks] } + + before do + file_vendor_class.fetch_from_disk(cookbook_path) + end + + it "sets the vendor class to FileSystemFileVendor" do + expect(file_vendor_class.vendor_class).to eq(Chef::Cookbook::FileSystemFileVendor) + end + + it "sets the initialization options to the given cookbook paths" do + expect(file_vendor_class.initialization_options).to eq(cookbook_path) + end + + it "creates a FileSystemFileVendor for a given manifest" do + file_vendor = file_vendor_class.create_from_manifest(manifest) + expect(file_vendor).to be_a_kind_of(Chef::Cookbook::FileSystemFileVendor) + expect(file_vendor.cookbook_name).to eq("bob") + expect(file_vendor.repo_paths).to eq(cookbook_path) + end + + end + +end + diff --git a/spec/unit/cookbook/synchronizer_spec.rb b/spec/unit/cookbook/synchronizer_spec.rb index c52593287a..2b040f3c95 100644 --- a/spec/unit/cookbook/synchronizer_spec.rb +++ b/spec/unit/cookbook/synchronizer_spec.rb @@ -5,302 +5,517 @@ require 'chef/cookbook_version' describe Chef::CookbookCacheCleaner do describe "when cleaning up unused cookbook components" do - before do - @cleaner = Chef::CookbookCacheCleaner.instance - @cleaner.reset! + let(:cleaner) do + cleaner = Chef::CookbookCacheCleaner.instance + cleaner.reset! + cleaner end - it "removes all files that belong to unused cookbooks" do - end + let(:file_cache) { double("Chef::FileCache with files from unused cookbooks") } - it "removes all files not validated during the chef run" do - file_cache = double("Chef::FileCache with files from unused cookbooks") - unused_template_files = %w{cookbooks/unused/templates/default/foo.conf.erb cookbooks/unused/tempaltes/default/bar.conf.erb} - valid_cached_cb_files = %w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb} - @cleaner.mark_file_as_valid('cookbooks/valid1/recipes/default.rb') - @cleaner.mark_file_as_valid('cookbooks/valid2/recipes/default.rb') - file_cache.should_receive(:find).with(File.join(%w{cookbooks ** *})).and_return(valid_cached_cb_files + unused_template_files) - file_cache.should_receive(:delete).with('cookbooks/unused/templates/default/foo.conf.erb') - file_cache.should_receive(:delete).with('cookbooks/unused/tempaltes/default/bar.conf.erb') - cookbook_hash = {"valid1"=> {}, "valid2" => {}} - @cleaner.stub(:cache).and_return(file_cache) - @cleaner.cleanup_file_cache + let(:unused_template_files) do + %w{ + cookbooks/unused/templates/default/foo.conf.erb + cookbooks/unused/tempaltes/default/bar.conf.erb + } end - describe "on chef-solo" do - before do - Chef::Config[:solo] = true - end + let(:valid_cached_cb_files) do + %w{ + cookbooks/valid1/recipes/default.rb + cookbooks/valid2/recipes/default.rb + } + end - after do - Chef::Config[:solo] = false + before do + valid_cached_cb_files.each do |cbf| + cleaner.mark_file_as_valid(cbf) end + end - it "does not remove anything" do - @cleaner.cache.stub(:find).and_return(%w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb}) - @cleaner.cache.should_not_receive(:delete) - @cleaner.cleanup_file_cache + it "removes all files not validated during the chef run" do + expect(file_cache).to receive(:find).with(File.join(%w{cookbooks ** {*,.*}})).and_return(valid_cached_cb_files + unused_template_files) + unused_template_files.each do |cbf| + expect(file_cache).to receive(:delete).with(cbf) end + cookbook_hash = {"valid1"=> {}, "valid2" => {}} + allow(cleaner).to receive(:cache).and_return(file_cache) + cleaner.cleanup_file_cache + end + it "does not remove anything when skip_removal is true" do + cleaner.skip_removal = true + allow(cleaner.cache).to receive(:find).and_return(%w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb}) + expect(cleaner.cache).not_to receive(:delete) + cleaner.cleanup_file_cache end + it "does not remove anything on chef-solo" do + Chef::Config[:solo] = true + allow(cleaner.cache).to receive(:find).and_return(%w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb}) + expect(cleaner.cache).not_to receive(:delete) + cleaner.cleanup_file_cache + end end end describe Chef::CookbookSynchronizer do - before do + let(:cookbook_a_default_recipe) do + { + "path" => "recipes/default.rb", + "url" => "http://chef.example.com/abc123", + "checksum" => "abc123", + } + end + + let(:cookbook_a_default_attrs) do + { + "path" => "attributes/default.rb", + "url" => "http://chef.example.com/abc456", + "checksum" => "abc456", + } + end + + let(:cookbook_a_template) do + { + "path" => "templates/default/apache2.conf.erb", + "url" => "http://chef.example.com/ffffff", + "checksum" => "abc125", + } + end + + let(:cookbook_a_file) do + { + "path" => "files/default/megaman.conf", + "url" => "http://chef.example.com/megaman.conf", + "checksum" => "abc124", + } + end + + let(:cookbook_a_manifest) do segments = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ] - @cookbook_manifest = {} - @cookbook_a = Chef::CookbookVersion.new("cookbook_a") - @cookbook_a_manifest = segments.inject({}) {|h, segment| h[segment.to_s] = []; h} - @cookbook_a_default_recipe = { "path" => "recipes/default.rb", - "url" => "http://chef.example.com/abc123", - "checksum" => "abc123" } - @cookbook_a_manifest["recipes"] = [ @cookbook_a_default_recipe ] - - @cookbook_a_default_attrs = { "path" => "attributes/default.rb", - "url" => "http://chef.example.com/abc456", - "checksum" => "abc456" } - @cookbook_a_manifest["attributes"] = [ @cookbook_a_default_attrs ] - @cookbook_a_manifest["templates"] = [{"path" => "templates/default/apache2.conf.erb", "url" => "http://chef.example.com/ffffff"}] - @cookbook_a_manifest["files"] = [{"path" => "files/default/megaman.conf", "url" => "http://chef.example.com/megaman.conf"}] - @cookbook_a.manifest = @cookbook_a_manifest - @cookbook_manifest["cookbook_a"] = @cookbook_a - - @events = Chef::EventDispatch::Dispatcher.new - @synchronizer = Chef::CookbookSynchronizer.new(@cookbook_manifest, @events) + cookbook_a_manifest = segments.inject({}) {|h, segment| h[segment.to_s] = []; h} + cookbook_a_manifest["recipes"] = [ cookbook_a_default_recipe ] + cookbook_a_manifest["attributes"] = [ cookbook_a_default_attrs ] + cookbook_a_manifest["templates"] = [ cookbook_a_template ] + cookbook_a_manifest["files"] = [ cookbook_a_file ] + cookbook_a_manifest + end + + let(:cookbook_a) do + cookbook_a = Chef::CookbookVersion.new("cookbook_a") + cookbook_a.manifest = cookbook_a_manifest + cookbook_a + end + + let(:cookbook_manifest) do + { + "cookbook_a" => cookbook_a + } + end + + let(:events) { Chef::EventDispatch::Dispatcher.new } + + let(:no_lazy_load) { true } + + let(:synchronizer) do + Chef::Config[:no_lazy_load] = no_lazy_load + Chef::CookbookSynchronizer.new(cookbook_manifest, events) end it "lists the cookbook names" do - @synchronizer.cookbook_names.should == %w[cookbook_a] + expect(synchronizer.cookbook_names).to eq(%w[cookbook_a]) end it "lists the cookbook manifests" do - @synchronizer.cookbooks.should == [@cookbook_a] + expect(synchronizer.cookbooks).to eq([cookbook_a]) end - context "when the cache contains unneeded cookbooks" do - before do - @file_cache = double("Chef::FileCache with files from unused cookbooks") - @valid_cached_cb_files = %w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb} - @obsolete_cb_files = %w{cookbooks/old1/recipes/default.rb cookbooks/old2/recipes/default.rb} + context "#clear_obsoleted_cookbooks" do + after do + # Singletons == Global State == Bad + Chef::CookbookCacheCleaner.instance.skip_removal = nil + end - @cookbook_hash = {"valid1"=> {}, "valid2" => {}} + it "behaves correctly when remove_obsoleted_files is false" do + synchronizer.remove_obsoleted_files = false + expect(synchronizer).not_to receive(:remove_old_cookbooks) + expect(synchronizer).to receive(:remove_deleted_files) + synchronizer.clear_obsoleted_cookbooks + expect(Chef::CookbookCacheCleaner.instance.skip_removal).to be true + end + + it "behaves correctly when remove_obsoleted_files is true" do + synchronizer.remove_obsoleted_files = true + expect(synchronizer).to receive(:remove_old_cookbooks) + expect(synchronizer).to receive(:remove_deleted_files) + synchronizer.clear_obsoleted_cookbooks + expect(Chef::CookbookCacheCleaner.instance.skip_removal).to be nil + end + end + + context "#remove_old_cookbooks" do + let(:file_cache) { double("Chef::FileCache with files from unused cookbooks") } - @synchronizer = Chef::CookbookSynchronizer.new(@cookbook_hash, @events) + let(:cookbook_manifest) do + {"valid1"=> {}, "valid2" => {}} end it "removes unneeded cookbooks" do - @file_cache.should_receive(:find).with(File.join(%w{cookbooks ** *})).and_return(@valid_cached_cb_files + @obsolete_cb_files) - @file_cache.should_receive(:delete).with('cookbooks/old1/recipes/default.rb') - @file_cache.should_receive(:delete).with('cookbooks/old2/recipes/default.rb') - @synchronizer.stub(:cache).and_return(@file_cache) - @synchronizer.clear_obsoleted_cookbooks + valid_cached_cb_files = %w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb} + obsolete_cb_files = %w{cookbooks/old1/recipes/default.rb cookbooks/old2/recipes/default.rb} + expect(file_cache).to receive(:find).with(File.join(%w{cookbooks ** {*,.*}})).and_return(valid_cached_cb_files + obsolete_cb_files) + expect(file_cache).to receive(:delete).with('cookbooks/old1/recipes/default.rb') + expect(file_cache).to receive(:delete).with('cookbooks/old2/recipes/default.rb') + allow(synchronizer).to receive(:cache).and_return(file_cache) + synchronizer.remove_old_cookbooks end end - describe "when syncing cookbooks with the server" do - before do - # Would rather not stub out methods on the test subject, but setting up - # the state is a PITA and tests for this behavior are above. - @synchronizer.stub(:clear_obsoleted_cookbooks) + context "#remove_deleted_files" do + let(:file_cache) { double("Chef::FileCache with files from unused cookbooks") } + + let(:cookbook_manifest) do + {"valid1"=> {}, "valid2" => {}} + end + + it "removes only deleted files" do + valid_cached_cb_files = %w{cookbooks/valid1/recipes/default.rb cookbooks/valid2/recipes/default.rb} + obsolete_cb_files = %w{cookbooks/valid1/recipes/deleted.rb cookbooks/valid2/recipes/deleted.rb} + expect(file_cache).to receive(:find).with(File.join(%w{cookbooks ** {*,.*}})).and_return(valid_cached_cb_files + obsolete_cb_files) + # valid1 is a cookbook in our run_list + expect(synchronizer).to receive(:have_cookbook?).with("valid1").at_least(:once).and_return(true) + # valid2 is a cookbook not in our run_list (we're simulating an override run_list where valid2 needs to be preserved) + expect(synchronizer).to receive(:have_cookbook?).with("valid2").at_least(:once).and_return(false) + expect(file_cache).to receive(:delete).with('cookbooks/valid1/recipes/deleted.rb') + expect(synchronizer).to receive(:cookbook_segment).with("valid1", "recipes").at_least(:once).and_return([ { "path" => "recipes/default.rb" }]) + allow(synchronizer).to receive(:cache).and_return(file_cache) + synchronizer.remove_deleted_files + end + end + + let(:cookbook_a_default_recipe_tempfile) do + double("Tempfile for cookbook_a default.rb recipe", + :path => "/tmp/cookbook_a_recipes_default_rb") + end - @server_api = double("Chef::REST (mock)") - @file_cache = double("Chef::FileCache (mock)") - @synchronizer.stub(:server_api).and_return(@server_api) - @synchronizer.stub(:cache).and_return(@file_cache) + let(:cookbook_a_default_attribute_tempfile) do + double("Tempfile for cookbook_a default.rb attr file", + :path => "/tmp/cookbook_a_attributes_default_rb") + end + let(:cookbook_a_file_default_tempfile) do + double("Tempfile for cookbook_a megaman.conf file", + :path => "/tmp/cookbook_a_file_default_tempfile") + end - @cookbook_a_default_recipe_tempfile = double("Tempfile for cookbook_a default.rb recipe", - :path => "/tmp/cookbook_a_recipes_default_rb") + let(:cookbook_a_template_default_tempfile) do + double("Tempfile for cookbook_a apache.conf.erb template", + :path => "/tmp/cookbook_a_template_default_tempfile") + end - @cookbook_a_default_attribute_tempfile = double("Tempfile for cookbook_a default.rb attr file", - :path => "/tmp/cookbook_a_attributes_default_rb") + def setup_common_files_missing_expectations + # Files are not in the cache: + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/recipes/default.rb"). + and_return(false) + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/attributes/default.rb"). + and_return(false) + + # Fetch and copy default.rb recipe + expect(server_api).to receive(:get_rest). + with('http://chef.example.com/abc123', true). + and_return(cookbook_a_default_recipe_tempfile) + expect(file_cache).to receive(:move_to). + with("/tmp/cookbook_a_recipes_default_rb", "cookbooks/cookbook_a/recipes/default.rb") + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/recipes/default.rb", false). + and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb") + + # Fetch and copy default.rb attribute file + expect(server_api).to receive(:get_rest). + with('http://chef.example.com/abc456', true). + and_return(cookbook_a_default_attribute_tempfile) + expect(file_cache).to receive(:move_to). + with("/tmp/cookbook_a_attributes_default_rb", "cookbooks/cookbook_a/attributes/default.rb") + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/attributes/default.rb", false). + and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb") + end + def setup_no_lazy_files_and_templates_missing_expectations + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/files/default/megaman.conf"). + and_return(false) + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/templates/default/apache2.conf.erb"). + and_return(false) + + expect(server_api).to receive(:get_rest). + with('http://chef.example.com/megaman.conf', true). + and_return(cookbook_a_file_default_tempfile) + expect(file_cache).to receive(:move_to). + with("/tmp/cookbook_a_file_default_tempfile", "cookbooks/cookbook_a/files/default/megaman.conf") + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/files/default/megaman.conf", false). + and_return("/file-cache/cookbooks/cookbook_a/default/megaman.conf") + + expect(server_api).to receive(:get_rest). + with('http://chef.example.com/ffffff', true). + and_return(cookbook_a_template_default_tempfile) + expect(file_cache).to receive(:move_to). + with("/tmp/cookbook_a_template_default_tempfile", "cookbooks/cookbook_a/templates/default/apache2.conf.erb") + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false). + and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb") + end + + def setup_common_files_chksum_mismatch_expectations + # Files are in the cache: + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/recipes/default.rb"). + and_return(true) + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/attributes/default.rb"). + and_return(true) + + # Fetch and copy default.rb recipe + expect(server_api).to receive(:get_rest). + with('http://chef.example.com/abc123', true). + and_return(cookbook_a_default_recipe_tempfile) + expect(file_cache).to receive(:move_to). + with("/tmp/cookbook_a_recipes_default_rb", "cookbooks/cookbook_a/recipes/default.rb") + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/recipes/default.rb", false). + twice. + and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb") + + # Current file has fff000, want abc123 + expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). + with("/file-cache/cookbooks/cookbook_a/recipes/default.rb"). + and_return("fff000") + + # Fetch and copy default.rb attribute file + expect(server_api).to receive(:get_rest). + with('http://chef.example.com/abc456', true). + and_return(cookbook_a_default_attribute_tempfile) + expect(file_cache).to receive(:move_to). + with("/tmp/cookbook_a_attributes_default_rb", "cookbooks/cookbook_a/attributes/default.rb") + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/attributes/default.rb", false). + twice. + and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb") + + # Current file has fff000, want abc456 + expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). + with("/file-cache/cookbooks/cookbook_a/attributes/default.rb"). + and_return("fff000") + end + + def setup_no_lazy_files_and_templates_chksum_mismatch_expectations + # Files are in the cache: + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/files/default/megaman.conf"). + and_return(true) + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/templates/default/apache2.conf.erb"). + and_return(true) + + # Fetch and copy megaman.conf + expect(server_api).to receive(:get_rest). + with('http://chef.example.com/megaman.conf', true). + and_return(cookbook_a_file_default_tempfile) + expect(file_cache).to receive(:move_to). + with("/tmp/cookbook_a_file_default_tempfile", "cookbooks/cookbook_a/files/default/megaman.conf") + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/files/default/megaman.conf", false). + twice. + and_return("/file-cache/cookbooks/cookbook_a/default/megaman.conf") + + # Fetch and copy apache2.conf template + expect(server_api).to receive(:get_rest). + with('http://chef.example.com/ffffff', true). + and_return(cookbook_a_template_default_tempfile) + expect(file_cache).to receive(:move_to). + with("/tmp/cookbook_a_template_default_tempfile", "cookbooks/cookbook_a/templates/default/apache2.conf.erb") + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false). + twice. + and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb") + + # Current file has fff000 + expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). + with("/file-cache/cookbooks/cookbook_a/default/megaman.conf"). + and_return("fff000") + + # Current file has fff000 + expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). + with("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb"). + and_return("fff000") + end + + def setup_common_files_present_expectations + # Files are in the cache: + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/recipes/default.rb"). + and_return(true) + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/attributes/default.rb"). + and_return(true) + + # Current file has abc123, want abc123 + expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). + with("/file-cache/cookbooks/cookbook_a/recipes/default.rb"). + and_return("abc123") + + # Current file has abc456, want abc456 + expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). + with("/file-cache/cookbooks/cookbook_a/attributes/default.rb"). + and_return("abc456") + + # :load called twice + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/recipes/default.rb", false). + twice. + and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb") + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/attributes/default.rb", false). + twice. + and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb") + end + + def setup_no_lazy_files_and_templates_present_expectations + # Files are in the cache: + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/files/default/megaman.conf"). + and_return(true) + expect(file_cache).to receive(:has_key?). + with("cookbooks/cookbook_a/templates/default/apache2.conf.erb"). + and_return(true) + + # Current file has abc124, want abc124 + expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). + with("/file-cache/cookbooks/cookbook_a/default/megaman.conf"). + and_return("abc124") + + # Current file has abc125, want abc125 + expect(Chef::CookbookVersion).to receive(:checksum_cookbook_file). + with("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb"). + and_return("abc125") + + # :load called twice + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/files/default/megaman.conf", false). + twice. + and_return("/file-cache/cookbooks/cookbook_a/default/megaman.conf") + expect(file_cache).to receive(:load). + with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false). + twice. + and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb") + end + + describe "when syncing cookbooks with the server" do + let(:server_api) { double("Chef::REST (mock)") } + + let(:file_cache) { double("Chef::FileCache (mock)") } + + before do + # Would rather not stub out methods on the test subject, but setting up + # the state is a PITA and tests for this behavior are above. + allow(synchronizer).to receive(:clear_obsoleted_cookbooks) + allow(synchronizer).to receive(:server_api).and_return(server_api) + allow(synchronizer).to receive(:cache).and_return(file_cache) end context "when the cache does not contain the desired files" do before do - - # Files are not in the cache: - @file_cache.should_receive(:has_key?). - with("cookbooks/cookbook_a/recipes/default.rb"). - and_return(false) - @file_cache.should_receive(:has_key?). - with("cookbooks/cookbook_a/attributes/default.rb"). - and_return(false) - - # Fetch and copy default.rb recipe - @server_api.should_receive(:get_rest). - with('http://chef.example.com/abc123', true). - and_return(@cookbook_a_default_recipe_tempfile) - @file_cache.should_receive(:move_to). - with("/tmp/cookbook_a_recipes_default_rb", "cookbooks/cookbook_a/recipes/default.rb") - @file_cache.should_receive(:load). - with("cookbooks/cookbook_a/recipes/default.rb", false). - and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb") - - # Fetch and copy default.rb attribute file - @server_api.should_receive(:get_rest). - with('http://chef.example.com/abc456', true). - and_return(@cookbook_a_default_attribute_tempfile) - @file_cache.should_receive(:move_to). - with("/tmp/cookbook_a_attributes_default_rb", "cookbooks/cookbook_a/attributes/default.rb") - @file_cache.should_receive(:load). - with("cookbooks/cookbook_a/attributes/default.rb", false). - and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb") + setup_common_files_missing_expectations end - it "fetches eagerly loaded files" do - @synchronizer.sync_cookbooks - end + context "Chef::Config[:no_lazy_load] is false" do + let(:no_lazy_load) { false } - it "does not fetch templates or cookbook files" do - # Implicitly tested in previous test; this test is just for behavior specification. - @server_api.should_not_receive(:get_rest). - with('http://chef.example.com/ffffff', true) + it "fetches eagerly loaded files" do + synchronizer.sync_cookbooks + end + + it "does not fetch templates or cookbook files" do + # Implicitly tested in previous test; this test is just for behavior specification. + expect(server_api).not_to receive(:get_rest). + with('http://chef.example.com/ffffff', true) - @synchronizer.sync_cookbooks + synchronizer.sync_cookbooks + end end context "Chef::Config[:no_lazy_load] is true" do - before do - Chef::Config[:no_lazy_load] = true - @synchronizer = Chef::CookbookSynchronizer.new(@cookbook_manifest, @events) - @synchronizer.stub(:server_api).and_return(@server_api) - @synchronizer.stub(:cache).and_return(@file_cache) - @synchronizer.stub(:clear_obsoleted_cookbooks) - - @cookbook_a_file_default_tempfile = double("Tempfile for cookbook_a megaman.conf file", - :path => "/tmp/cookbook_a_file_default_tempfile") - @cookbook_a_template_default_tempfile = double("Tempfile for cookbook_a apache.conf.erb template", - :path => "/tmp/cookbook_a_template_default_tempfile") - end + let(:no_lazy_load) { true } - after do - Chef::Config[:no_lazy_load] = false + before do + setup_no_lazy_files_and_templates_missing_expectations end it "fetches templates and cookbook files" do - @file_cache.should_receive(:has_key?). - with("cookbooks/cookbook_a/files/default/megaman.conf"). - and_return(false) - @file_cache.should_receive(:has_key?). - with("cookbooks/cookbook_a/templates/default/apache2.conf.erb"). - and_return(false) - - @server_api.should_receive(:get_rest). - with('http://chef.example.com/megaman.conf', true). - and_return(@cookbook_a_file_default_tempfile) - @file_cache.should_receive(:move_to). - with("/tmp/cookbook_a_file_default_tempfile", "cookbooks/cookbook_a/files/default/megaman.conf") - @file_cache.should_receive(:load). - with("cookbooks/cookbook_a/files/default/megaman.conf", false). - and_return("/file-cache/cookbooks/cookbook_a/default/megaman.conf") - - @server_api.should_receive(:get_rest). - with('http://chef.example.com/ffffff', true). - and_return(@cookbook_a_template_default_tempfile) - @file_cache.should_receive(:move_to). - with("/tmp/cookbook_a_template_default_tempfile", "cookbooks/cookbook_a/templates/default/apache2.conf.erb") - @file_cache.should_receive(:load). - with("cookbooks/cookbook_a/templates/default/apache2.conf.erb", false). - and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb") - - @synchronizer.sync_cookbooks + synchronizer.sync_cookbooks end end end context "when the cache contains outdated files" do before do - # Files are in the cache: - @file_cache.should_receive(:has_key?). - with("cookbooks/cookbook_a/recipes/default.rb"). - and_return(true) - @file_cache.should_receive(:has_key?). - with("cookbooks/cookbook_a/attributes/default.rb"). - and_return(true) - - - # Fetch and copy default.rb recipe - @server_api.should_receive(:get_rest). - with('http://chef.example.com/abc123', true). - and_return(@cookbook_a_default_recipe_tempfile) - @file_cache.should_receive(:move_to). - with("/tmp/cookbook_a_recipes_default_rb", "cookbooks/cookbook_a/recipes/default.rb") - @file_cache.should_receive(:load). - with("cookbooks/cookbook_a/recipes/default.rb", false). - twice. - and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb") - - # Current file has fff000, want abc123 - Chef::CookbookVersion.should_receive(:checksum_cookbook_file). - with("/file-cache/cookbooks/cookbook_a/recipes/default.rb"). - and_return("fff000") - - # Fetch and copy default.rb attribute file - @server_api.should_receive(:get_rest). - with('http://chef.example.com/abc456', true). - and_return(@cookbook_a_default_attribute_tempfile) - @file_cache.should_receive(:move_to). - with("/tmp/cookbook_a_attributes_default_rb", "cookbooks/cookbook_a/attributes/default.rb") - @file_cache.should_receive(:load). - with("cookbooks/cookbook_a/attributes/default.rb", false). - twice. - and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb") - - # Current file has fff000, want abc456 - Chef::CookbookVersion.should_receive(:checksum_cookbook_file). - with("/file-cache/cookbooks/cookbook_a/attributes/default.rb"). - and_return("fff000") + setup_common_files_chksum_mismatch_expectations end - it "updates the outdated files" do - @synchronizer.sync_cookbooks + context "Chef::Config[:no_lazy_load] is true" do + let(:no_lazy_load) { true } + + before do + setup_no_lazy_files_and_templates_chksum_mismatch_expectations + end + + it "updates the outdated files" do + synchronizer.sync_cookbooks + end + end + + context "Chef::Config[:no_lazy_load] is false" do + let(:no_lazy_load) { false } + + it "updates the outdated files" do + synchronizer.sync_cookbooks + end end end context "when the cache is up to date" do before do - # Files are in the cache: - @file_cache.should_receive(:has_key?). - with("cookbooks/cookbook_a/recipes/default.rb"). - and_return(true) - @file_cache.should_receive(:has_key?). - with("cookbooks/cookbook_a/attributes/default.rb"). - and_return(true) - - # Current file has abc123, want abc123 - Chef::CookbookVersion.should_receive(:checksum_cookbook_file). - with("/file-cache/cookbooks/cookbook_a/recipes/default.rb"). - and_return("abc123") - - # Current file has abc456, want abc456 - Chef::CookbookVersion.should_receive(:checksum_cookbook_file). - with("/file-cache/cookbooks/cookbook_a/attributes/default.rb"). - and_return("abc456") - - @file_cache.should_receive(:load). - with("cookbooks/cookbook_a/recipes/default.rb", false). - twice. - and_return("/file-cache/cookbooks/cookbook_a/recipes/default.rb") - - @file_cache.should_receive(:load). - with("cookbooks/cookbook_a/attributes/default.rb", false). - twice. - and_return("/file-cache/cookbooks/cookbook_a/attributes/default.rb") + setup_common_files_present_expectations end - it "does not update files" do - @file_cache.should_not_receive(:move_to) - @server_api.should_not_receive(:get_rest) - @synchronizer.sync_cookbooks + context "Chef::Config[:no_lazy_load] is true" do + let(:no_lazy_load) { true } + + before do + setup_no_lazy_files_and_templates_present_expectations + end + + it "does not update files" do + expect(file_cache).not_to receive(:move_to) + expect(server_api).not_to receive(:get_rest) + synchronizer.sync_cookbooks + end end - end + context "Chef::Config[:no_lazy_load] is false" do + let(:no_lazy_load) { false } + it "does not update files" do + expect(file_cache).not_to receive(:move_to) + expect(server_api).not_to receive(:get_rest) + synchronizer.sync_cookbooks + end + end + end end - end - diff --git a/spec/unit/cookbook_uploader_spec.rb b/spec/unit/cookbook_uploader_spec.rb new file mode 100644 index 0000000000..af25baff13 --- /dev/null +++ b/spec/unit/cookbook_uploader_spec.rb @@ -0,0 +1,160 @@ +# +# Author:: Daniel DeLeo (<dan@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::CookbookUploader do + + let(:http_client) { double("Chef::REST") } + + let(:cookbook_loader) do + loader = Chef::CookbookLoader.new(File.join(CHEF_SPEC_DATA, "cookbooks")) + loader.load_cookbooks + loader + end + + let(:apache2_cookbook) { cookbook_loader.cookbooks_by_name["apache2"] } + + let(:java_cookbook) { cookbook_loader.cookbooks_by_name["java"] } + + let(:cookbooks_to_upload) { [apache2_cookbook, java_cookbook] } + + let(:checksums_of_cookbook_files) { apache2_cookbook.checksums.merge(java_cookbook.checksums) } + + let(:checksums_set) do + checksums_of_cookbook_files.keys.inject({}) do |set, cksum| + set[cksum] = nil + set + end + end + + let(:sandbox_commit_uri) { "https://chef.example.org/sandboxes/abc123" } + + let(:uploader) { described_class.new(cookbooks_to_upload, :rest => http_client) } + + it "has a list of cookbooks to upload" do + expect(uploader.cookbooks).to eq(cookbooks_to_upload) + end + + it "creates an HTTP client with default configuration when not initialized with one" do + default_http_client = double("Chef::REST") + expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(default_http_client) + uploader = described_class.new(cookbooks_to_upload) + expect(uploader.rest).to eq(default_http_client) + end + + describe "uploading cookbooks" do + + def url_for(cksum) + "https://storage.example.com/#{cksum}" + end + + let(:sandbox_response) do + sandbox_checksums = cksums_not_on_remote.inject({}) do |cksum_map, cksum| + cksum_map[cksum] = { "needs_upload" => true, "url" => url_for(cksum)} + cksum_map + end + { "checksums" => sandbox_checksums, "uri" => sandbox_commit_uri } + end + + def expect_sandbox_create + expect(http_client).to receive(:post). + with("sandboxes", {:checksums => checksums_set}). + and_return(sandbox_response) + end + + def expect_checksum_upload + checksums_of_cookbook_files.each do |md5, file_path| + next unless cksums_not_on_remote.include?(md5) + + upload_headers = { + "content-type" => "application/x-binary", + "content-md5" => an_instance_of(String), + "accept" => "application/json" + } + + expect(http_client).to receive(:put). + with(url_for(md5), IO.binread(file_path), upload_headers) + + end + end + + def expect_sandbox_commit + expect(http_client).to receive(:put).with(sandbox_commit_uri, {:is_completed => true}) + end + + def expect_cookbook_create + cookbooks_to_upload.each do |cookbook| + + expect(http_client).to receive(:put). + with(cookbook.save_url, cookbook) + + end + end + + context "when no files exist on the server" do + + let(:cksums_not_on_remote) do + checksums_of_cookbook_files.keys + end + + it "uploads all files in a sandbox transaction, then creates cookbooks on the server" do + expect_sandbox_create + expect_checksum_upload + expect_sandbox_commit + expect_cookbook_create + + uploader.upload_cookbooks + end + + end + + context "when some files exist on the server" do + + let(:cksums_not_on_remote) do + checksums_of_cookbook_files.keys[0, 1] + end + + it "uploads all files in a sandbox transaction, then creates cookbooks on the server" do + expect_sandbox_create + expect_checksum_upload + expect_sandbox_commit + expect_cookbook_create + + uploader.upload_cookbooks + end + + end + + context "when all files already exist on the server" do + + let(:cksums_not_on_remote) { [] } + + it "uploads all files in a sandbox transaction, then creates cookbooks on the server" do + expect_sandbox_create + expect_checksum_upload + expect_sandbox_commit + expect_cookbook_create + + uploader.upload_cookbooks + end + + end + end + +end diff --git a/spec/unit/dsl/recipe_spec.rb b/spec/unit/dsl/recipe_spec.rb index 14ee30be44..dfaad0b73e 100644 --- a/spec/unit/dsl/recipe_spec.rb +++ b/spec/unit/dsl/recipe_spec.rb @@ -46,6 +46,18 @@ describe Chef::DSL::Recipe do it "responds to recipe_name" do expect(recipe.recipe_name).to eq(recipe_name) end + + it "responds to shell_out" do + expect(recipe.respond_to?(:shell_out)).to be true + end + + it "responds to shell_out" do + expect(recipe.respond_to?(:shell_out!)).to be true + end + + it "responds to shell_out" do + expect(recipe.respond_to?(:shell_out_with_systems_locale)).to be true + end end context "when included in a class that defines the required interface directly" do diff --git a/spec/unit/formatters/base_spec.rb b/spec/unit/formatters/base_spec.rb new file mode 100644 index 0000000000..6a843ea775 --- /dev/null +++ b/spec/unit/formatters/base_spec.rb @@ -0,0 +1,48 @@ +# +# Author:: Lamont Granquist (<lamont@getchef.com>) +# +# Copyright:: Copyright (c) 2012 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' + +describe Chef::Formatters::Base do + let(:out) { double("out") } + let(:err) { double("err") } + let(:formatter) { Chef::Formatters::Base.new(out, err) } + + it "starts with an indentation of zero" do + expect(formatter.output.indent).to eql(0) + end + + it "increments it to two correctly" do + formatter.indent_by(2) + expect(formatter.output.indent).to eql(2) + end + + it "increments it and then decrements it corectly" do + formatter.indent_by(2) + formatter.indent_by(-2) + expect(formatter.output.indent).to eql(0) + end + + it "does not allow negative indentation" do + formatter.indent_by(-2) + expect(formatter.output.indent).to eql(0) + end +end + + diff --git a/spec/unit/http/json_input_spec.rb b/spec/unit/http/json_input_spec.rb new file mode 100644 index 0000000000..fbf8f22503 --- /dev/null +++ b/spec/unit/http/json_input_spec.rb @@ -0,0 +1,128 @@ +#-- +# Author:: Daniel DeLeo (<dan@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'spec_helper' +require 'chef/http/json_input' + +describe Chef::HTTP::JSONInput do + + let(:json_encoder) { described_class.new() } + + let(:url) { URI.parse("http://example.com") } + let(:headers) { {} } + + def handle_request + json_encoder.handle_request(http_method, url, headers, data) + end + + it "passes the response unmodified" do + http_response = double("Net::HTTPSuccess") + request = double("Chef::HTTP::HTTPRequest") + return_value = "response body" + + result = json_encoder.handle_response(http_response, request, return_value) + expect(result).to eq([http_response, request, return_value]) + end + + it "doesn't handle streaming responses" do + http_response = double("Net::HTTPSuccess") + expect(json_encoder.stream_response_handler(http_response)).to be nil + end + + it "does nothing for stream completion" do + http_response = double("Net::HTTPSuccess") + request = double("Chef::HTTP::HTTPRequest") + return_value = "response body" + + result = json_encoder.handle_response(http_response, request, return_value) + expect(result).to eq([http_response, request, return_value]) + end + + context "when handling a request with no body" do + + let(:http_method) { :get } + let(:data) { nil } + + it "passes the request unmodified" do + expect(handle_request).to eq([http_method, url, headers, data]) + end + end + + context "when the request should be serialized" do + + let(:http_method) { :put } + let(:data) { {foo: "bar"} } + let(:expected_data) { %q[{"foo":"bar"}] } + + context "and the request has a ruby object as the body and no explicit content-type" do + + it "serializes the body to json" do + # Headers Hash get mutated, so we start by asserting it's empty: + expect(headers).to be_empty + + expect(handle_request).to eq([http_method, url, headers, expected_data]) + + # Now the headers Hash should have json content type: + expect(headers).to have_key("Content-Type") + expect(headers["Content-Type"]).to eq("application/json") + end + end + + context "ant the request has an explicit content type of json" do + + it "serializes the body to json when content-type is all lowercase" do + headers["content-type"] = "application/json" + + expect(handle_request).to eq([http_method, url, headers, expected_data]) + + # Content-Type header should be normalized: + expect(headers.size).to eq(1) + expect(headers).to have_key("Content-Type") + expect(headers["Content-Type"]).to eq("application/json") + end + + end + + end + + context "when handling a request with an explicit content type other than json" do + + let(:http_method) { :put } + let(:data) { "some arbitrary bytes" } + + it "does not serialize the body to json when content type is given as lowercase" do + headers["content-type"] = "application/x-binary" + + expect(handle_request).to eq([http_method, url, headers, data]) + + # not normalized + expect(headers).to eq({"content-type" => "application/x-binary"}) + end + + it "does not serialize the body to json when content type is given in capitalized form" do + headers["Content-Type"] = "application/x-binary" + + expect(handle_request).to eq([http_method, url, headers, data]) + + # not normalized + expect(headers).to eq({"Content-Type" => "application/x-binary"}) + end + + end + +end diff --git a/spec/unit/knife/client_create_spec.rb b/spec/unit/knife/client_create_spec.rb index 897cee8974..59238d69ec 100644 --- a/spec/unit/knife/client_create_spec.rb +++ b/spec/unit/knife/client_create_spec.rb @@ -35,15 +35,15 @@ describe Chef::Knife::ClientCreate do @knife.stub(:edit_data).and_return(@client) @knife.stub(:puts) Chef::ApiClient.stub(:new).and_return(@client) - @stdout = StringIO.new - @knife.ui.stub(:stdout).and_return(@stdout) + @stderr = StringIO.new + @knife.ui.stub(:stderr).and_return(@stderr) end describe "run" do it "should create a new Client" do Chef::ApiClient.should_receive(:new).and_return(@client) @knife.run - @stdout.string.should match /created client.+adam/i + @stderr.string.should match /created client.+adam/i end it "should set the Client name" do diff --git a/spec/unit/knife/configure_client_spec.rb b/spec/unit/knife/configure_client_spec.rb index d101780317..de2a5a41e5 100644 --- a/spec/unit/knife/configure_client_spec.rb +++ b/spec/unit/knife/configure_client_spec.rb @@ -25,8 +25,8 @@ describe Chef::Knife::ConfigureClient do Chef::Config[:validation_client_name] = 'chef-validator' Chef::Config[:validation_key] = '/etc/chef/validation.pem' - @stdout = StringIO.new - @knife.ui.stub(:stdout).and_return(@stdout) + @stderr = StringIO.new + @knife.ui.stub(:stderr).and_return(@stderr) end describe 'run' do @@ -73,11 +73,12 @@ describe Chef::Knife::ConfigureClient do it 'should print information on what is being configured' do FileUtils.stub(:mkdir_p) @knife.run - @stdout.string.should match /creating client configuration/i - @stdout.string.should match /writing client\.rb/i - @stdout.string.should match /writing validation\.pem/i + @stderr.string.should match /creating client configuration/i + @stderr.string.should match /writing client\.rb/i + @stderr.string.should match /writing validation\.pem/i end end end end + diff --git a/spec/unit/knife/cookbook_delete_spec.rb b/spec/unit/knife/cookbook_delete_spec.rb index f4ba46f513..53b120be71 100644 --- a/spec/unit/knife/cookbook_delete_spec.rb +++ b/spec/unit/knife/cookbook_delete_spec.rb @@ -204,7 +204,7 @@ describe Chef::Knife::CookbookDelete do it 'should output that the cookbook was deleted' do @knife.stub(:delete_request) @knife.delete_version_without_confirmation('1.0.0') - @stdout.string.should match /deleted cookbook\[foobar\]\[1.0.0\]/im + @stderr.string.should match /deleted cookbook\[foobar\]\[1.0.0\]/im end describe 'with --print-after' do diff --git a/spec/unit/knife/cookbook_download_spec.rb b/spec/unit/knife/cookbook_download_spec.rb index efab98f646..6f40a3396b 100644 --- a/spec/unit/knife/cookbook_download_spec.rb +++ b/spec/unit/knife/cookbook_download_spec.rb @@ -21,8 +21,8 @@ require 'spec_helper' describe Chef::Knife::CookbookDownload do before(:each) do @knife = Chef::Knife::CookbookDownload.new - @stdout = StringIO.new - @knife.ui.stub(:stdout).and_return(@stdout) + @stderr = StringIO.new + @knife.ui.stub(:stderr).and_return(@stderr) end describe 'run' do @@ -121,10 +121,10 @@ describe Chef::Knife::CookbookDownload do File.should_receive(:exists?).with('/var/tmp/chef/foobar-1.0.0').and_return(false) @knife.run ['attributes', 'recipes', 'templates'].each do |segment| - @stdout.string.should match /downloading #{segment}/im + @stderr.string.should match /downloading #{segment}/im end - @stdout.string.should match /downloading foobar cookbook version 1\.0\.0/im - @stdout.string.should match /cookbook downloaded to \/var\/tmp\/chef\/foobar-1\.0\.0/im + @stderr.string.should match /downloading foobar cookbook version 1\.0\.0/im + @stderr.string.should match /cookbook downloaded to \/var\/tmp\/chef\/foobar-1\.0\.0/im end describe 'with -f or --force' do diff --git a/spec/unit/knife/cookbook_metadata_spec.rb b/spec/unit/knife/cookbook_metadata_spec.rb index 26ff43829c..1d6568739c 100644 --- a/spec/unit/knife/cookbook_metadata_spec.rb +++ b/spec/unit/knife/cookbook_metadata_spec.rb @@ -119,7 +119,7 @@ describe Chef::Knife::CookbookMetadata do Chef::JSONCompat.should_receive(:to_json_pretty).with(@metadata_mock). and_return(@json_data) @knife.generate_metadata_from_file('foobar', "#{@cookbook_dir}/foobar/metadata.rb") - @stdout.string.should match /generating metadata for foobar from #{@cookbook_dir}\/foobar\/metadata\.rb/im + @stderr.string.should match /generating metadata for foobar from #{@cookbook_dir}\/foobar\/metadata\.rb/im end { Chef::Exceptions::ObsoleteDependencySyntax => 'obsolote dependency', diff --git a/spec/unit/knife/cookbook_site_download_spec.rb b/spec/unit/knife/cookbook_site_download_spec.rb index faf58f7bc7..7cd71c2211 100644 --- a/spec/unit/knife/cookbook_site_download_spec.rb +++ b/spec/unit/knife/cookbook_site_download_spec.rb @@ -25,7 +25,7 @@ describe Chef::Knife::CookbookSiteDownload do @knife = Chef::Knife::CookbookSiteDownload.new @knife.name_args = ['apache2'] @noauth_rest = double('no auth rest') - @stdout = StringIO.new + @stderr = StringIO.new @cookbook_api_url = 'http://cookbooks.opscode.com/api/v1/cookbooks' @version = '1.0.2' @version_us = @version.gsub '.', '_' @@ -33,7 +33,7 @@ describe Chef::Knife::CookbookSiteDownload do 'latest_version' => "#{@cookbook_api_url}/apache2/versions/#{@version_us}", 'replacement' => 'other_apache2' } - @knife.ui.stub(:stdout).and_return(@stdout) + @knife.ui.stub(:stderr).and_return(@stderr) @knife.stub(:noauth_rest).and_return(@noauth_rest) @noauth_rest.should_receive(:get_rest). with("#{@cookbook_api_url}/apache2"). @@ -85,8 +85,8 @@ describe Chef::Knife::CookbookSiteDownload do with(/.+deprecated.+replaced by other_apache2.+/i) FileUtils.should_receive(:cp).with(@temp_file.path, @file) @knife.run - @stdout.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i - @stdout.string.should match /cookbook save.+#{Regexp.escape(@file)}/i + @stderr.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i + @stderr.string.should match /cookbook save.+#{Regexp.escape(@file)}/i end end @@ -94,8 +94,8 @@ describe Chef::Knife::CookbookSiteDownload do it 'should download the latest version' do FileUtils.should_receive(:cp).with(@temp_file.path, @file) @knife.run - @stdout.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i - @stdout.string.should match /cookbook save.+#{Regexp.escape(@file)}/i + @stderr.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i + @stderr.string.should match /cookbook save.+#{Regexp.escape(@file)}/i end context 'with -f or --file' do @@ -107,8 +107,8 @@ describe Chef::Knife::CookbookSiteDownload do it 'should download the cookbook to the desired file' do @knife.run - @stdout.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i - @stdout.string.should match /cookbook save.+#{Regexp.escape(@file)}/i + @stderr.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i + @stderr.string.should match /cookbook save.+#{Regexp.escape(@file)}/i end end @@ -139,8 +139,8 @@ describe Chef::Knife::CookbookSiteDownload do and_return(@temp_file) FileUtils.should_receive(:cp).with(@temp_file.path, @file) @knife.run - @stdout.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i - @stdout.string.should match /cookbook save.+#{Regexp.escape(@file)}/i + @stderr.string.should match /downloading apache2.+version.+#{Regexp.escape(@version)}/i + @stderr.string.should match /cookbook save.+#{Regexp.escape(@file)}/i end end diff --git a/spec/unit/knife/cookbook_site_share_spec.rb b/spec/unit/knife/cookbook_site_share_spec.rb index 28b2a509fd..a46783274b 100644 --- a/spec/unit/knife/cookbook_site_share_spec.rb +++ b/spec/unit/knife/cookbook_site_share_spec.rb @@ -34,7 +34,7 @@ describe Chef::Knife::CookbookSiteShare do @cookbook_loader.stub(:[]).and_return(@cookbook) Chef::CookbookLoader.stub(:new).and_return(@cookbook_loader) - @cookbook_uploader = Chef::CookbookUploader.new('herpderp', File.join(CHEF_SPEC_DATA, 'cookbooks'), :rest => "norest") + @cookbook_uploader = Chef::CookbookUploader.new('herpderp', :rest => "norest") Chef::CookbookUploader.stub(:new).and_return(@cookbook_uploader) @cookbook_uploader.stub(:validate_cookbooks).and_return(true) Chef::CookbookSiteStreamingUploader.stub(:create_build_dir).and_return(Dir.mktmpdir) @@ -77,7 +77,7 @@ describe Chef::Knife::CookbookSiteShare do it 'should make a tarball of the cookbook' do @knife.should_receive(:shell_out!) do |args| - args.to_s.should match /tar -czf/ + args.to_s.should match(/tar -czf/) end @knife.run end diff --git a/spec/unit/knife/cookbook_upload_spec.rb b/spec/unit/knife/cookbook_upload_spec.rb index 6a99a80459..e5c9a40cf1 100644 --- a/spec/unit/knife/cookbook_upload_spec.rb +++ b/spec/unit/knife/cookbook_upload_spec.rb @@ -61,8 +61,9 @@ describe Chef::Knife::CookbookUpload do test_cookbook = Chef::CookbookVersion.new('test_cookbook', '/tmp/blah') cookbook_loader.stub(:each).and_yield("test_cookbook", test_cookbook) cookbook_loader.stub(:cookbook_names).and_return(["test_cookbook"]) - Chef::CookbookUploader.should_receive(:new).with( kind_of(Array), kind_of(Array), - {:force=>nil, :concurrency => 3}).and_return(double("Chef::CookbookUploader", :upload_cookbooks=> true)) + Chef::CookbookUploader.should_receive(:new). + with( kind_of(Array), { :force => nil, :concurrency => 3}). + and_return(double("Chef::CookbookUploader", :upload_cookbooks=> true)) knife.run end end diff --git a/spec/unit/knife/tag_create_spec.rb b/spec/unit/knife/tag_create_spec.rb index 82e3ad68cd..bafea8d268 100644 --- a/spec/unit/knife/tag_create_spec.rb +++ b/spec/unit/knife/tag_create_spec.rb @@ -9,15 +9,15 @@ describe Chef::Knife::TagCreate do @node = Chef::Node.new @node.stub :save Chef::Node.stub(:load).and_return @node - @stdout = StringIO.new - @knife.ui.stub(:stdout).and_return(@stdout) + @stderr = StringIO.new + @knife.ui.stub(:stderr).and_return(@stderr) end describe "run" do it "can create tags on a node" do @knife.run @node.tags.should == ["happytag"] - @stdout.string.should match /created tags happytag.+node webmonkey.example.com/i + @stderr.string.should match /created tags happytag.+node webmonkey.example.com/i end end end diff --git a/spec/unit/knife/tag_delete_spec.rb b/spec/unit/knife/tag_delete_spec.rb index 67e3dd3ff0..514228f0a2 100644 --- a/spec/unit/knife/tag_delete_spec.rb +++ b/spec/unit/knife/tag_delete_spec.rb @@ -10,8 +10,8 @@ describe Chef::Knife::TagDelete do @node.stub :save @node.tags << "sadtag" << "happytag" Chef::Node.stub(:load).and_return @node - @stdout = StringIO.new - @knife.ui.stub(:stdout).and_return(@stdout) + @stderr = StringIO.new + @knife.ui.stub(:stderr).and_return(@stderr) end describe "run" do @@ -19,7 +19,7 @@ describe Chef::Knife::TagDelete do @node.tags.should == ["sadtag", "happytag"] @knife.run @node.tags.should == ["happytag"] - @stdout.string.should match /deleted.+sadtag/i + @stderr.string.should match /deleted.+sadtag/i end end end diff --git a/spec/unit/knife/user_create_spec.rb b/spec/unit/knife/user_create_spec.rb index 039d4659b0..58ef868053 100644 --- a/spec/unit/knife/user_create_spec.rb +++ b/spec/unit/knife/user_create_spec.rb @@ -46,7 +46,7 @@ describe Chef::Knife::UserCreate do Chef::User.should_receive(:new).and_return(@user) @user.should_receive(:create) @knife.run - @stdout.string.should match /created user.+a_user/i + @stderr.string.should match /created user.+a_user/i end it "sets the password" do diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb index 52784741d7..70b60a2f96 100644 --- a/spec/unit/knife_spec.rb +++ b/spec/unit/knife_spec.rb @@ -41,7 +41,7 @@ describe Chef::Knife do Chef::Log.stub(level_sym) end Chef::Knife.stub(:puts) - @stdout = StringIO.new + @stderr = StringIO.new end describe "selecting a config file" do @@ -241,7 +241,7 @@ describe Chef::Knife do end it "exits if no subcommand matches the CLI args" do - Chef::Knife.ui.stub(:stdout).and_return(@stdout) + Chef::Knife.ui.stub(:stderr).and_return(@stderr) Chef::Knife.ui.should_receive(:fatal) lambda {Chef::Knife.run(%w{fuuu uuuu fuuuu})}.should raise_error(SystemExit) { |e| e.status.should_not == 0 } end @@ -326,7 +326,7 @@ describe Chef::Knife do @knife.stub(:run).and_raise(Net::HTTPServerException.new("401 Unauthorized", response)) @knife.run_with_pretty_exceptions @stderr.string.should match(/ERROR: Failed to authenticate to/) - @stdout.string.should match(/Response: y u no syncronize your clock\?/) + @stderr.string.should match(/Response: y u no syncronize your clock\?/) end it "formats 403s nicely" do @@ -337,7 +337,7 @@ describe Chef::Knife do @knife.stub(:username).and_return("sadpanda") @knife.run_with_pretty_exceptions @stderr.string.should match(%r[ERROR: You authenticated successfully to http.+ as sadpanda but you are not authorized for this action]) - @stdout.string.should match(%r[Response: y u no administrator]) + @stderr.string.should match(%r[Response: y u no administrator]) end it "formats 400s nicely" do @@ -347,7 +347,7 @@ describe Chef::Knife do @knife.stub(:run).and_raise(Net::HTTPServerException.new("400 Bad Request", response)) @knife.run_with_pretty_exceptions @stderr.string.should match(%r[ERROR: The data in your request was invalid]) - @stdout.string.should match(%r[Response: y u search wrong]) + @stderr.string.should match(%r[Response: y u search wrong]) end it "formats 404s nicely" do @@ -357,7 +357,7 @@ describe Chef::Knife do @knife.stub(:run).and_raise(Net::HTTPServerException.new("404 Not Found", response)) @knife.run_with_pretty_exceptions @stderr.string.should match(%r[ERROR: The object you are looking for could not be found]) - @stdout.string.should match(%r[Response: nothing to see here]) + @stderr.string.should match(%r[Response: nothing to see here]) end it "formats 500s nicely" do @@ -367,7 +367,7 @@ describe Chef::Knife do @knife.stub(:run).and_raise(Net::HTTPFatalError.new("500 Internal Server Error", response)) @knife.run_with_pretty_exceptions @stderr.string.should match(%r[ERROR: internal server error]) - @stdout.string.should match(%r[Response: sad trombone]) + @stderr.string.should match(%r[Response: sad trombone]) end it "formats 502s nicely" do @@ -377,7 +377,7 @@ describe Chef::Knife do @knife.stub(:run).and_raise(Net::HTTPFatalError.new("502 Bad Gateway", response)) @knife.run_with_pretty_exceptions @stderr.string.should match(%r[ERROR: bad gateway]) - @stdout.string.should match(%r[Response: sadder trombone]) + @stderr.string.should match(%r[Response: sadder trombone]) end it "formats 503s nicely" do @@ -387,7 +387,7 @@ describe Chef::Knife do @knife.stub(:run).and_raise(Net::HTTPFatalError.new("503 Service Unavailable", response)) @knife.run_with_pretty_exceptions @stderr.string.should match(%r[ERROR: Service temporarily unavailable]) - @stdout.string.should match(%r[Response: saddest trombone]) + @stderr.string.should match(%r[Response: saddest trombone]) end it "formats other HTTP errors nicely" do @@ -397,15 +397,15 @@ describe Chef::Knife do @knife.stub(:run).and_raise(Net::HTTPServerException.new("402 Payment Required", response)) @knife.run_with_pretty_exceptions @stderr.string.should match(%r[ERROR: Payment Required]) - @stdout.string.should match(%r[Response: nobugfixtillyoubuy]) + @stderr.string.should match(%r[Response: nobugfixtillyoubuy]) end it "formats NameError and NoMethodError nicely" do @knife.stub(:run).and_raise(NameError.new("Undefined constant FUUU")) @knife.run_with_pretty_exceptions @stderr.string.should match(%r[ERROR: knife encountered an unexpected error]) - @stdout.string.should match(%r[This may be a bug in the 'knife' knife command or plugin]) - @stdout.string.should match(%r[Exception: NameError: Undefined constant FUUU]) + @stderr.string.should match(%r[This may be a bug in the 'knife' knife command or plugin]) + @stderr.string.should match(%r[Exception: NameError: Undefined constant FUUU]) end it "formats missing private key errors nicely" do @@ -413,7 +413,7 @@ describe Chef::Knife do @knife.stub(:api_key).and_return("/home/root/.chef/no-key-here.pem") @knife.run_with_pretty_exceptions @stderr.string.should match(%r[ERROR: Your private key could not be loaded from /home/root/.chef/no-key-here.pem]) - @stdout.string.should match(%r[Check your configuration file and ensure that your private key is readable]) + @stderr.string.should match(%r[Check your configuration file and ensure that your private key is readable]) end it "formats connection refused errors nicely" do @@ -423,7 +423,7 @@ describe Chef::Knife do # *nix = Errno::ECONNREFUSED: Connection refused # win32: Errno::ECONNREFUSED: No connection could be made because the target machine actively refused it. @stderr.string.should match(%r[ERROR: Network Error: .* - y u no shut up]) - @stdout.string.should match(%r[Check your knife configuration and network settings]) + @stderr.string.should match(%r[Check your knife configuration and network settings]) end end diff --git a/spec/unit/lwrp_spec.rb b/spec/unit/lwrp_spec.rb index 9a775f2dff..960aff3c36 100644 --- a/spec/unit/lwrp_spec.rb +++ b/spec/unit/lwrp_spec.rb @@ -180,6 +180,19 @@ describe "LWRP" do end end + describe "when #default_action is an array" do + let(:lwrp) do + Class.new(Chef::Resource::LWRPBase) do + actions :eat, :sleep + default_action [:eat, :sleep] + end + end + + it "returns the array of default actions" do + expect(lwrp.default_action).to eq([:eat, :sleep]) + end + end + describe "when inheriting from LWRPBase" do let(:parent) do Class.new(Chef::Resource::LWRPBase) do diff --git a/spec/unit/mixin/template_spec.rb b/spec/unit/mixin/template_spec.rb index 3aa0b9ba22..63fa81782e 100644 --- a/spec/unit/mixin/template_spec.rb +++ b/spec/unit/mixin/template_spec.rb @@ -76,7 +76,7 @@ describe Chef::Mixin::Template, "render_template" do describe "with a template resource" do before :each do @cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, @cookbook_repo) } + Chef::Cookbook::FileVendor.fetch_from_disk(@cookbook_repo) @node = Chef::Node.new cl = Chef::CookbookLoader.new(@cookbook_repo) diff --git a/spec/unit/provider/http_request_spec.rb b/spec/unit/provider/http_request_spec.rb index 605287fcc3..3077685b97 100644 --- a/spec/unit/provider/http_request_spec.rb +++ b/spec/unit/provider/http_request_spec.rb @@ -53,13 +53,13 @@ describe Chef::Provider::HttpRequest do it "should inflate a message block at runtime" do @new_resource.message { "return" } - @http.should_receive(:get).with("http://www.opscode.com/?message=return", {}) + @http.should_receive(:get).with("http://www.opscode.com/", {}) @provider.run_action(:get) @new_resource.should be_updated end it "should run a GET request" do - @http.should_receive(:get).with("http://www.opscode.com/?message=is cool", {}) + @http.should_receive(:get).with("http://www.opscode.com/", {}) @provider.run_action(:get) @new_resource.should be_updated end @@ -112,25 +112,25 @@ describe Chef::Provider::HttpRequest do it "should inflate a message block at runtime" do @new_resource.message { "return" } - @http.should_receive(:head).with("http://www.opscode.com/?message=return", {}).and_return(nil) + @http.should_receive(:head).with("http://www.opscode.com/", {}).and_return(nil) @provider.run_action(:head) @new_resource.should be_updated end it "should run a HEAD request" do - @http.should_receive(:head).with("http://www.opscode.com/?message=is cool", {}).and_return(nil) + @http.should_receive(:head).with("http://www.opscode.com/", {}).and_return(nil) @provider.run_action(:head) @new_resource.should be_updated end it "should update a HEAD request with empty string response body (CHEF-4762)" do - @http.should_receive(:head).with("http://www.opscode.com/?message=is cool", {}).and_return("") + @http.should_receive(:head).with("http://www.opscode.com/", {}).and_return("") @provider.run_action(:head) @new_resource.should be_updated end it "should update a HEAD request with nil response body (CHEF-4762)" do - @http.should_receive(:head).with("http://www.opscode.com/?message=is cool", {}).and_return(nil) + @http.should_receive(:head).with("http://www.opscode.com/", {}).and_return(nil) @provider.run_action(:head) @new_resource.should be_updated end @@ -138,14 +138,14 @@ describe Chef::Provider::HttpRequest do it "should not update a HEAD request if a not modified response (CHEF-4762)" do if_modified_since = File.mtime(__FILE__).httpdate @new_resource.headers "If-Modified-Since" => if_modified_since - @http.should_receive(:head).with("http://www.opscode.com/?message=is cool", {"If-Modified-Since" => if_modified_since}).and_return(false) + @http.should_receive(:head).with("http://www.opscode.com/", {"If-Modified-Since" => if_modified_since}).and_return(false) @provider.run_action(:head) @new_resource.should_not be_updated end it "should run a HEAD request with If-Modified-Since header" do @new_resource.headers "If-Modified-Since" => File.mtime(__FILE__).httpdate - @http.should_receive(:head).with("http://www.opscode.com/?message=is cool", @new_resource.headers) + @http.should_receive(:head).with("http://www.opscode.com/", @new_resource.headers) @provider.run_action(:head) end diff --git a/spec/unit/provider/log_spec.rb b/spec/unit/provider/log_spec.rb index 8f411e461a..f6ff526dd4 100644 --- a/spec/unit/provider/log_spec.rb +++ b/spec/unit/provider/log_spec.rb @@ -78,22 +78,4 @@ describe Chef::Provider::Log::ChefLog do @provider.action_write end - it "should not update the resource if the message was not written to the log" do - Chef::Log.level = :fatal - @new_resource = Chef::Resource::Log.new(@log_str) - @new_resource.level :info - @provider = Chef::Provider::Log::ChefLog.new(@new_resource, @run_context) - @provider.action_write - @new_resource.updated.should be_false - end - - it "should update the resource if the message has been written to the log" do - Chef::Log.level = :debug - @new_resource = Chef::Resource::Log.new(@log_str) - @new_resource.level :info - @provider = Chef::Provider::Log::ChefLog.new(@new_resource, @run_context) - @provider.action_write - @new_resource.updated.should be_true - end - end diff --git a/spec/unit/provider/package_spec.rb b/spec/unit/provider/package_spec.rb index 33b4f8186a..375a0d0646 100644 --- a/spec/unit/provider/package_spec.rb +++ b/spec/unit/provider/package_spec.rb @@ -339,7 +339,7 @@ describe Chef::Provider::Package do describe "when given a response file" do before(:each) do @cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, @cookbook_repo) } + Chef::Cookbook::FileVendor.fetch_from_disk(@cookbook_repo) @node = Chef::Node.new cl = Chef::CookbookLoader.new(@cookbook_repo) diff --git a/spec/unit/provider/remote_directory_spec.rb b/spec/unit/provider/remote_directory_spec.rb index d3f1438f0f..b986e2c8ad 100644 --- a/spec/unit/provider/remote_directory_spec.rb +++ b/spec/unit/provider/remote_directory_spec.rb @@ -35,7 +35,7 @@ describe Chef::Provider::RemoteDirectory do @resource.cookbook('openldap') @cookbook_repo = ::File.expand_path(::File.join(CHEF_SPEC_DATA, "cookbooks")) - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, @cookbook_repo) } + Chef::Cookbook::FileVendor.fetch_from_disk(@cookbook_repo) @node = Chef::Node.new cl = Chef::CookbookLoader.new(@cookbook_repo) diff --git a/spec/unit/provider/template/content_spec.rb b/spec/unit/provider/template/content_spec.rb index dfc5d21c2a..b419e70519 100644 --- a/spec/unit/provider/template/content_spec.rb +++ b/spec/unit/provider/template/content_spec.rb @@ -36,7 +36,7 @@ describe Chef::Provider::Template::Content do let(:run_context) do cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) - Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, cookbook_repo) } + Chef::Cookbook::FileVendor.fetch_from_disk(cookbook_repo) cl = Chef::CookbookLoader.new(cookbook_repo) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb index 363649ec46..9b89fc1888 100644 --- a/spec/unit/provider_spec.rb +++ b/spec/unit/provider_spec.rb @@ -61,6 +61,18 @@ describe Chef::Provider do @provider = Chef::Provider.new(@resource, @run_context) end + it "should mixin shell_out" do + expect(@provider.respond_to?(:shell_out)).to be true + end + + it "should mixin shell_out!" do + expect(@provider.respond_to?(:shell_out!)).to be true + end + + it "should mixin shell_out_with_systems_locale" do + expect(@provider.respond_to?(:shell_out_with_systems_locale)).to be true + end + it "should store the resource passed to new as new_resource" do @provider.new_resource.should eql(@resource) end diff --git a/spec/unit/shell/shell_session_spec.rb b/spec/unit/shell/shell_session_spec.rb index 92a2e5d538..f49c9fc805 100644 --- a/spec/unit/shell/shell_session_spec.rb +++ b/spec/unit/shell/shell_session_spec.rb @@ -50,6 +50,7 @@ end describe Shell::ClientSession do before do Chef::Config[:shell_config] = { :override_runlist => [Chef::RunList::RunListItem.new('shell::override')] } + @chef_rest = double("Chef::REST") @session = Shell::ClientSession.instance @node = Chef::Node.build("foo") @session.node = @node @@ -66,6 +67,7 @@ describe Shell::ClientSession do @expansion = Chef::RunList::RunListExpansion.new(@node.chef_environment, []) @node.run_list.should_receive(:expand).with(@node.chef_environment).and_return(@expansion) + Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(@chef_rest) @session.rebuild_context end |