summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordanielsdeleo <dan@getchef.com>2014-07-30 15:22:22 -0700
committerdanielsdeleo <dan@getchef.com>2014-07-30 15:22:22 -0700
commitbfe0ac206c5723637772d8b9b3a48a4d94f5c21f (patch)
tree221462e43a423bfc2d4539a98f93cc24b237517a
parentc8a54df7ca24f1482a701d5f39879cdc6c836f8a (diff)
parent031a09221d8d2d1b2c22bba1eca31abcb3d665d9 (diff)
downloadchef-bfe0ac206c5723637772d8b9b3a48a4d94f5c21f.tar.gz
Merge branch 'policyfile-fixes'12.0.0.alpha.0
-rw-r--r--CHANGELOG.md6
-rw-r--r--RELEASE_NOTES.md19
-rw-r--r--lib/chef/chef_fs/file_system/cookbooks_dir.rb4
-rw-r--r--lib/chef/cookbook/file_system_file_vendor.rb3
-rw-r--r--lib/chef/cookbook/file_vendor.rb32
-rw-r--r--lib/chef/cookbook/remote_file_vendor.rb3
-rw-r--r--lib/chef/cookbook_uploader.rb31
-rw-r--r--lib/chef/cookbook_version.rb1
-rw-r--r--lib/chef/http/json_input.rb13
-rw-r--r--lib/chef/knife/cookbook_site_share.rb2
-rw-r--r--lib/chef/knife/cookbook_upload.rb4
-rw-r--r--lib/chef/policy_builder/expand_node_object.rb4
-rw-r--r--lib/chef/policy_builder/policyfile.rb5
-rw-r--r--lib/chef/shell/shell_session.rb4
-rw-r--r--spec/functional/resource/base.rb10
-rw-r--r--spec/functional/resource/cookbook_file_spec.rb2
-rw-r--r--spec/functional/resource/package_spec.rb4
-rw-r--r--spec/functional/resource/remote_directory_spec.rb2
-rw-r--r--spec/functional/resource/template_spec.rb2
-rw-r--r--spec/support/platform_helpers.rb5
-rw-r--r--spec/unit/cookbook/file_vendor_spec.rb78
-rw-r--r--spec/unit/cookbook_uploader_spec.rb160
-rw-r--r--spec/unit/http/json_input_spec.rb128
-rw-r--r--spec/unit/knife/cookbook_site_share_spec.rb4
-rw-r--r--spec/unit/knife/cookbook_upload_spec.rb5
-rw-r--r--spec/unit/mixin/template_spec.rb2
-rw-r--r--spec/unit/provider/package_spec.rb2
-rw-r--r--spec/unit/provider/remote_directory_spec.rb2
-rw-r--r--spec/unit/provider/template/content_spec.rb2
-rw-r--r--spec/unit/shell/shell_session_spec.rb2
30 files changed, 477 insertions, 64 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 748bb86c30..13d66ac932 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
# 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):
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 0f2b86be1b..c3b78c9f86 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -56,3 +56,22 @@ directory will also be inherited correctly.
## 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/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/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_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/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/policy_builder/expand_node_object.rb b/lib/chef/policy_builder/expand_node_object.rb
index 63964a2a0d..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)
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/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/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/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_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/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/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/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/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/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