summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordanielsdeleo <dan@getchef.com>2015-02-11 14:05:57 -0800
committerdanielsdeleo <dan@getchef.com>2015-02-11 14:05:57 -0800
commit3ed9b877442a1b735504ac0b3571f3de1b87dcb4 (patch)
tree4b481ec66d24e1b919f148df9dea9c8921db4006
parent5a78c483d2a40b0de04609f7f6c829763fa9938b (diff)
parent9c6488d6939d57d35301edbf51f42d760b2add2c (diff)
downloadchef-3ed9b877442a1b735504ac0b3571f3de1b87dcb4.tar.gz
Merge branch 'upload-cookbooks-as-artifacts'
-rw-r--r--lib/chef/chef_fs/chef_fs_data_store.rb3
-rw-r--r--lib/chef/cookbook_manifest.rb275
-rw-r--r--lib/chef/cookbook_uploader.rb13
-rw-r--r--lib/chef/cookbook_version.rb259
-rw-r--r--spec/unit/cookbook_loader_spec.rb14
-rw-r--r--spec/unit/cookbook_manifest_spec.rb609
-rw-r--r--spec/unit/cookbook_uploader_spec.rb40
-rw-r--r--spec/unit/cookbook_version_file_specificity_spec.rb554
-rw-r--r--spec/unit/cookbook_version_spec.rb256
9 files changed, 1153 insertions, 870 deletions
diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb
index 9b4f7320b8..4084fb80d3 100644
--- a/lib/chef/chef_fs/chef_fs_data_store.rb
+++ b/lib/chef/chef_fs/chef_fs_data_store.rb
@@ -16,6 +16,7 @@
# limitations under the License.
#
+require 'chef/cookbook_manifest'
require 'chef_zero/data_store/memory_store'
require 'chef_zero/data_store/data_already_exists_error'
require 'chef_zero/data_store/data_not_found_error'
@@ -147,7 +148,7 @@ class Chef
# get /cookbooks/NAME/version
result = nil
begin
- result = entry.chef_object.to_hash
+ result = Chef::CookbookManifest.new(entry.chef_object).to_hash
rescue Chef::ChefFS::FileSystem::NotFoundError => e
raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
end
diff --git a/lib/chef/cookbook_manifest.rb b/lib/chef/cookbook_manifest.rb
new file mode 100644
index 0000000000..0d21e9725c
--- /dev/null
+++ b/lib/chef/cookbook_manifest.rb
@@ -0,0 +1,275 @@
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright 2015 Opscode, 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 'forwardable'
+require 'chef/util/path_helper'
+require 'chef/log'
+
+class Chef
+
+ # Handles the details of representing a cookbook in JSON form for uploading
+ # to a Chef Server.
+ class CookbookManifest
+
+ # Duplicates the same constant in CookbookVersion. We cannot remove it
+ # there because it is treated by other code as part of CookbookVersion's
+ # public API (also used in some deprecated methods).
+ COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ].freeze
+
+ extend Forwardable
+
+ attr_reader :cookbook_version
+
+ def_delegator :@cookbook_version, :root_paths
+ def_delegator :@cookbook_version, :segment_filenames
+ def_delegator :@cookbook_version, :name
+ def_delegator :@cookbook_version, :metadata
+ def_delegator :@cookbook_version, :full_name
+ def_delegator :@cookbook_version, :version
+ def_delegator :@cookbook_version, :frozen_version?
+
+ # Create a new CookbookManifest object for the given `cookbook_version`.
+ # You can subsequently call #to_hash to get a Hash representation of the
+ # cookbook_version in the "manifest" format, or #to_json to get a JSON
+ # representation of the cookbook_version.
+ #
+ # The inferface for this behavior is expected to change as we implement new
+ # manifest formats. The entire class should be considered a private API for
+ # now.
+ #
+ # @api private
+ # @param policy_mode [Boolean] whether to convert cookbooks to Hash/JSON in
+ # the format used by the `cookbook_artifacts` endpoint (for policyfiles).
+ # Setting this option also changes the behavior of #save_url and
+ # #force_save_url such that CookbookVersions will be uploaded to the new
+ # `cookbook_artifacts` API. This endpoint is currently under active
+ # development and the format is expected to change frequently, therefore
+ # the result of #manifest, #to_hash, and #to_json will not be stable when
+ # `policy_mode` is enabled.
+ def initialize(cookbook_version, policy_mode: false)
+ @cookbook_version = cookbook_version
+ @policy_mode = !!policy_mode
+
+ reset!
+ end
+
+ # Resets all lazily computed values.
+ def reset!
+ @manifest = nil
+ @checksums = nil
+ @manifest_records_by_path = nil
+ true
+ end
+
+ # Returns a 'manifest' data structure that can be uploaded to a Chef
+ # Server.
+ #
+ # The format is as follows:
+ #
+ # {
+ # :cookbook_name => name, # String
+ # :metadata => metadata, # Chef::Cookbook::Metadata
+ # :version => version, # Chef::Version
+ # :name => full_name, # String of "#{name}-#{version}"
+ #
+ # :recipes => Array<FileSpec>,
+ # :definitions => Array<FileSpec>,
+ # :libraries => Array<FileSpec>,
+ # :attributes => Array<FileSpec>,
+ # :files => Array<FileSpec>,
+ # :templates => Array<FileSpec>,
+ # :resources => Array<FileSpec>,
+ # :providers => Array<FileSpec>,
+ # :root_files => Array<FileSpec>
+ # }
+ #
+ # Where a `FileSpec` is a Hash of the form:
+ #
+ # {
+ # :name => file_name,
+ # :path => path,
+ # :checksum => csum,
+ # :specificity => specificity
+ # }
+ #
+ def manifest
+ @manifest || generate_manifest
+ @manifest
+ end
+
+ def checksums
+ @manifest || generate_manifest
+ @checksums
+ end
+
+ def manifest_records_by_path
+ @manifest || generate_manifest
+ @manifest_records_by_path
+ end
+
+ def policy_mode?
+ @policy_mode
+ end
+
+ def to_hash
+ result = manifest.dup
+ result['frozen?'] = frozen_version?
+ result['chef_type'] = 'cookbook_version'
+ result.to_hash
+ end
+
+ def to_json(*a)
+ result = to_hash
+ result['json_class'] = "Chef::CookbookVersion"
+ Chef::JSONCompat.to_json(result, *a)
+ end
+
+ # Return the URL to save (PUT) this object to the server via the
+ # REST api. If there is an existing document on the server and it
+ # is marked frozen, a PUT will result in a 409 Conflict.
+ def save_url
+ "#{cookbook_url_path}/#{name}/#{version}"
+ end
+
+ # Adds the `force=true` parameter to the upload URL. This allows
+ # the user to overwrite a frozen cookbook (a PUT against the
+ # normal #save_url raises a 409 Conflict in this case).
+ def force_save_url
+ "#{save_url}?force=true"
+ end
+
+ # Update this CookbookManifest from the contents of another manifest, and
+ # make the corresponding changes to the cookbook_version object. Required
+ # to provide backward compatibility with CookbookVersion#manifest= method.
+ def update_from(new_manifest)
+ @manifest = Mash.new new_manifest
+ @checksums = extract_checksums_from_manifest(@manifest)
+ @manifest_records_by_path = extract_manifest_records_by_path(@manifest)
+
+ COOKBOOK_SEGMENTS.each do |segment|
+ next unless @manifest.has_key?(segment)
+ filenames = @manifest[segment].map{|manifest_record| manifest_record['name']}
+
+ cookbook_version.replace_segment_filenames(segment, filenames)
+ end
+ end
+
+ private
+
+ def cookbook_url_path
+ policy_mode? ? "cookbook_artifacts" : "cookbooks"
+ end
+
+ # See #manifest for a description of the manifest return value.
+ # See #preferred_manifest_record for a description an individual manifest record.
+ def generate_manifest
+ manifest = Mash.new({
+ :recipes => Array.new,
+ :definitions => Array.new,
+ :libraries => Array.new,
+ :attributes => Array.new,
+ :files => Array.new,
+ :templates => Array.new,
+ :resources => Array.new,
+ :providers => Array.new,
+ :root_files => Array.new
+ })
+ @checksums = {}
+
+ if !root_paths || root_paths.size == 0
+ Chef::Log.error("Cookbook #{name} does not have root_paths! Cannot generate manifest.")
+ raise "Cookbook #{name} does not have root_paths! Cannot generate manifest."
+ end
+
+ COOKBOOK_SEGMENTS.each do |segment|
+ segment_filenames(segment).each do |segment_file|
+ next if File.directory?(segment_file)
+
+ path, specificity = parse_segment_file_from_root_paths(segment, segment_file)
+ file_name = File.basename(path)
+
+ csum = checksum_cookbook_file(segment_file)
+ @checksums[csum] = segment_file
+ rs = Mash.new({
+ :name => file_name,
+ :path => path,
+ :checksum => csum,
+ :specificity => specificity
+ })
+
+ manifest[segment] << rs
+ end
+ end
+
+ manifest[:cookbook_name] = name.to_s
+ manifest[:metadata] = metadata
+ manifest[:version] = metadata.version
+ manifest[:name] = full_name
+
+ @manifest_records_by_path = extract_manifest_records_by_path(manifest)
+ @manifest = manifest
+ end
+
+ def parse_segment_file_from_root_paths(segment, segment_file)
+ root_paths.each do |root_path|
+ pathname = Chef::Util::PathHelper.relative_path_from(root_path, segment_file)
+
+ parts = pathname.each_filename.take(2)
+ # Check if path is actually under root_path
+ next if parts[0] == '..'
+ if segment == :templates || segment == :files
+ # Check if pathname looks like files/foo or templates/foo (unscoped)
+ if pathname.each_filename.to_a.length == 2
+ # Use root_default in case the same path exists at root_default and default
+ return [ pathname.to_s, 'root_default' ]
+ else
+ return [ pathname.to_s, parts[1] ]
+ end
+ else
+ return [ pathname.to_s, 'default' ]
+ end
+ end
+ Chef::Log.error("Cookbook file #{segment_file} not under cookbook root paths #{root_paths.inspect}.")
+ raise "Cookbook file #{segment_file} not under cookbook root paths #{root_paths.inspect}."
+ end
+
+ def extract_checksums_from_manifest(manifest)
+ checksums = {}
+ COOKBOOK_SEGMENTS.each do |segment|
+ next unless manifest.has_key?(segment)
+ manifest[segment].each do |manifest_record|
+ checksums[manifest_record[:checksum]] = nil
+ end
+ end
+ checksums
+ end
+
+ def checksum_cookbook_file(filepath)
+ CookbookVersion.checksum_cookbook_file(filepath)
+ end
+
+ def extract_manifest_records_by_path(manifest)
+ manifest_records_by_path = {}
+ COOKBOOK_SEGMENTS.each do |segment|
+ next unless manifest.has_key?(segment)
+ manifest[segment].each do |manifest_record|
+ manifest_records_by_path[manifest_record[:path]] = manifest_record
+ end
+ end
+ manifest_records_by_path
+ end
+ end
+end
diff --git a/lib/chef/cookbook_uploader.rb b/lib/chef/cookbook_uploader.rb
index 2d8bf5bc7e..f24ce2cd56 100644
--- a/lib/chef/cookbook_uploader.rb
+++ b/lib/chef/cookbook_uploader.rb
@@ -3,6 +3,7 @@ require 'set'
require 'chef/exceptions'
require 'chef/knife/cookbook_metadata'
require 'chef/digester'
+require 'chef/cookbook_manifest'
require 'chef/cookbook_version'
require 'chef/cookbook/syntax_check'
require 'chef/cookbook/file_system_file_vendor'
@@ -40,6 +41,7 @@ class Chef
@cookbooks = Array(cookbooks)
@rest = opts[:rest] || Chef::REST.new(Chef::Config[:chef_server_url])
@concurrency = opts[:concurrency] || 10
+ @policy_mode = opts[:policy_mode] || false
end
def upload_cookbooks
@@ -92,9 +94,12 @@ class Chef
# files are uploaded, so save the manifest
cookbooks.each do |cb|
- save_url = opts[:force] ? cb.force_save_url : cb.save_url
+
+ manifest = Chef::CookbookManifest.new(cb, policy_mode: policy_mode?)
+
+ save_url = opts[:force] ? manifest.force_save_url : manifest.save_url
begin
- rest.put(save_url, cb)
+ rest.put(save_url, manifest)
rescue Net::HTTPServerException => e
case e.response.code
when "409"
@@ -143,5 +148,9 @@ class Chef
end
end
+ def policy_mode?
+ @policy_mode
+ end
+
end
end
diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb
index 3bf19296c9..c51d5798c5 100644
--- a/lib/chef/cookbook_version.rb
+++ b/lib/chef/cookbook_version.rb
@@ -23,8 +23,8 @@ require 'chef/log'
require 'chef/cookbook/file_vendor'
require 'chef/cookbook/metadata'
require 'chef/version_class'
-require 'pathname'
require 'chef/digester'
+require 'chef/cookbook_manifest'
class Chef
@@ -33,10 +33,8 @@ class Chef
# cookbook. Chef supports maintaining multiple versions of a cookbook on a
# single server; each version is represented by a distinct instance of this
# class.
- #--
- # TODO: timh/cw: 5-24-2010: mutators for files (e.g., recipe_filenames=,
- # recipe_filenames.insert) should dirty the manifest so it gets regenerated.
class CookbookVersion
+
include Comparable
COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ]
@@ -51,7 +49,16 @@ class Chef
attr_accessor :root_filenames
attr_accessor :name
attr_accessor :metadata_filenames
- attr_accessor :status
+
+ def status=(new_status)
+ Chef::Log.deprecation("Deprecated method `status' called from #{caller(1).first}. This method will be removed")
+ @status = new_status
+ end
+
+ def status
+ Chef::Log.deprecation("Deprecated method `status' called from #{caller(1).first}. This method will be removed")
+ @status
+ end
# A Chef::Cookbook::Metadata object. It has a setter that fixes up the
# metadata to add descriptions of the recipes contained in this
@@ -95,6 +102,7 @@ class Chef
@name = name
@root_paths = root_paths
@frozen = false
+
@attribute_filenames = Array.new
@definition_filenames = Array.new
@template_filenames = Array.new
@@ -106,8 +114,9 @@ class Chef
@provider_filenames = Array.new
@metadata_filenames = Array.new
@root_filenames = Array.new
+
+ # deprecated
@status = :ready
- @manifest = nil
@file_vendor = nil
@metadata = Chef::Cookbook::Metadata.new
end
@@ -128,66 +137,10 @@ class Chef
end
def version=(new_version)
- manifest["version"] = new_version
+ cookbook_manifest.reset!
metadata.version(new_version)
end
- # A manifest is a Mash that maps segment names to arrays of manifest
- # records (see #preferred_manifest_record for format of manifest records),
- # as well as describing cookbook metadata. The manifest follows a form
- # like the following:
- #
- # {
- # :cookbook_name = "apache2",
- # :version = "1.0",
- # :name = "Apache 2"
- # :metadata = ???TODO: timh/cw: 5-24-2010: describe this format,
- #
- # :files => [
- # {
- # :name => "afile.rb",
- # :path => "files/ubuntu-9.10/afile.rb",
- # :checksum => "2222",
- # :specificity => "ubuntu-9.10"
- # },
- # ],
- # :templates => [ manifest_record1, ... ],
- # ...
- # }
- def manifest
- unless @manifest
- generate_manifest
- end
- @manifest
- end
-
- def manifest=(new_manifest)
- @manifest = Mash.new new_manifest
- @checksums = extract_checksums_from_manifest(@manifest)
- @manifest_records_by_path = extract_manifest_records_by_path(@manifest)
-
- COOKBOOK_SEGMENTS.each do |segment|
- next unless @manifest.has_key?(segment)
- filenames = @manifest[segment].map{|manifest_record| manifest_record['name']}
-
- replace_segment_filenames(segment, filenames)
- end
- end
-
- # Returns a hash of checksums to either nil or the on disk path (which is
- # done by generate_manifest).
- def checksums
- unless @checksums
- generate_manifest
- end
- @checksums
- end
-
- def manifest_records_by_path
- @manifest_records_by_path || generate_manifest
- @manifest_records_by_path
- end
-
def full_name
"#{name}-#{version}"
end
@@ -208,6 +161,24 @@ class Chef
alias :attribute_files :attribute_filenames
alias :attribute_files= :attribute_filenames=
+ def manifest
+ cookbook_manifest.manifest
+ end
+
+ # Returns a hash of checksums to either nil or the on disk path (which is
+ # done by generate_manifest).
+ def checksums
+ cookbook_manifest.checksums
+ end
+
+ def manifest_records_by_path
+ cookbook_manifest.manifest_records_by_path
+ end
+
+ def manifest=(new_manifest)
+ cookbook_manifest.update_from(new_manifest)
+ end
+
# Return recipe names in the form of cookbook_name::recipe_name
def fully_qualified_recipe_names
results = Array.new
@@ -310,7 +281,7 @@ class Chef
def preferred_manifest_record(node, segment, filename)
found_pref = find_preferred_manifest_record(node, segment, filename)
if found_pref
- @manifest_records_by_path[found_pref]
+ manifest_records_by_path[found_pref]
else
if segment == :files || segment == :templates
error_message = "Cookbook '#{name}' (#{version}) does not contain a file at any of these locations:\n"
@@ -471,19 +442,6 @@ class Chef
end
private :preferences_for_path
- def to_hash
- result = manifest.dup
- result['frozen?'] = frozen_version?
- result['chef_type'] = 'cookbook_version'
- result.to_hash
- end
-
- def to_json(*a)
- result = to_hash
- result['json_class'] = self.class.name
- Chef::JSONCompat.to_json(result, *a)
- end
-
def self.json_create(o)
cookbook_version = new(o["cookbook_name"])
# We want the Chef::Cookbook::Metadata object to always be inflated
@@ -497,7 +455,11 @@ class Chef
cookbook_version
end
+ # @deprecated This method was used by the Ruby Chef Server and is no longer
+ # needed. There is no replacement.
def generate_manifest_with_urls(&url_generator)
+ Chef::Log.deprecation("Deprecated method #generate_manifest_with_urls called from #{caller(1).first}")
+
rendered_manifest = manifest.dup
COOKBOOK_SEGMENTS.each do |segment|
if rendered_manifest.has_key?(segment)
@@ -510,6 +472,18 @@ class Chef
rendered_manifest
end
+
+ def to_hash
+ # TODO: this should become deprecated when the API for CookbookManifest becomes stable
+ cookbook_manifest.to_hash
+ end
+
+ def to_json(*a)
+ # TODO: this should become deprecated when the API for CookbookManifest becomes stable
+ cookbook_manifest.to_json
+ end
+
+
def metadata_json_file
File.join(root_paths[0], "metadata.json")
end
@@ -527,26 +501,23 @@ class Chef
##
# REST API
##
- def self.chef_server_rest
- Chef::REST.new(Chef::Config[:chef_server_url])
+
+ def save_url
+ # TODO: this should become deprecated when the API for CookbookManifest becomes stable
+ cookbook_manifest.save_url
end
- def chef_server_rest
- self.class.chef_server_rest
+ def force_save_url
+ # TODO: this should become deprecated when the API for CookbookManifest becomes stable
+ cookbook_manifest.force_save_url
end
- # Return the URL to save (PUT) this object to the server via the
- # REST api. If there is an existing document on the server and it
- # is marked frozen, a PUT will result in a 409 Conflict.
- def save_url
- "cookbooks/#{name}/#{version}"
+ def self.chef_server_rest
+ Chef::REST.new(Chef::Config[:chef_server_url])
end
- # Adds the `force=true` parameter to the upload URL. This allows
- # the user to overwrite a frozen cookbook (a PUT against the
- # normal #save_url raises a 409 Conflict in this case).
- def force_save_url
- "cookbooks/#{name}/#{version}?force=true"
+ def chef_server_rest
+ self.class.chef_server_rest
end
def destroy
@@ -602,15 +573,15 @@ class Chef
private
+ def cookbook_manifest
+ @cookbook_manifest ||= CookbookManifest.new(self)
+ end
+
def find_preferred_manifest_record(node, segment, filename)
preferences = preferences_for_path(node, segment, filename)
- # ensure that we generate the manifest, which will also generate
- # @manifest_records_by_path
- manifest
-
# in order of prefernce, look for the filename in the manifest
- preferences.find {|preferred_filename| @manifest_records_by_path[preferred_filename] }
+ preferences.find {|preferred_filename| manifest_records_by_path[preferred_filename] }
end
# For each filename, produce a mapping of base filename (i.e. recipe name
@@ -619,80 +590,6 @@ class Chef
filenames.select{|filename| filename =~ /\.rb$/}.inject({}){|memo, filename| memo[File.basename(filename, '.rb')] = filename ; memo }
end
- # See #manifest for a description of the manifest return value.
- # See #preferred_manifest_record for a description an individual manifest record.
- def generate_manifest
- manifest = Mash.new({
- :recipes => Array.new,
- :definitions => Array.new,
- :libraries => Array.new,
- :attributes => Array.new,
- :files => Array.new,
- :templates => Array.new,
- :resources => Array.new,
- :providers => Array.new,
- :root_files => Array.new
- })
- checksums_to_on_disk_paths = {}
-
- if !root_paths || root_paths.size == 0
- Chef::Log.error("Cookbook #{name} does not have root_paths! Cannot generate manifest.")
- raise "Cookbook #{name} does not have root_paths! Cannot generate manifest."
- end
-
- COOKBOOK_SEGMENTS.each do |segment|
- segment_filenames(segment).each do |segment_file|
- next if File.directory?(segment_file)
-
- path, specificity = parse_segment_file_from_root_paths(segment, segment_file)
- file_name = File.basename(path)
-
- csum = self.class.checksum_cookbook_file(segment_file)
- checksums_to_on_disk_paths[csum] = segment_file
- rs = Mash.new({
- :name => file_name,
- :path => path,
- :checksum => csum,
- :specificity => specificity
- })
-
- manifest[segment] << rs
- end
- end
-
- manifest[:cookbook_name] = name.to_s
- manifest[:metadata] = metadata
- manifest[:version] = metadata.version
- manifest[:name] = full_name
-
- @checksums = checksums_to_on_disk_paths
- @manifest = manifest
- @manifest_records_by_path = extract_manifest_records_by_path(manifest)
- end
-
- def parse_segment_file_from_root_paths(segment, segment_file)
- root_paths.each do |root_path|
- pathname = Chef::Util::PathHelper.relative_path_from(root_path, segment_file)
-
- parts = pathname.each_filename.take(2)
- # Check if path is actually under root_path
- next if parts[0] == '..'
- if segment == :templates || segment == :files
- # Check if pathname looks like files/foo or templates/foo (unscoped)
- if pathname.each_filename.to_a.length == 2
- # Use root_default in case the same path exists at root_default and default
- return [ pathname.to_s, 'root_default' ]
- else
- return [ pathname.to_s, parts[1] ]
- end
- else
- return [ pathname.to_s, 'default' ]
- end
- end
- Chef::Log.error("Cookbook file #{segment_file} not under cookbook root paths #{root_paths.inspect}.")
- raise "Cookbook file #{segment_file} not under cookbook root paths #{root_paths.inspect}."
- end
-
def file_vendor
unless @file_vendor
@file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(manifest)
@@ -700,27 +597,5 @@ class Chef
@file_vendor
end
- def extract_checksums_from_manifest(manifest)
- checksums = {}
- COOKBOOK_SEGMENTS.each do |segment|
- next unless manifest.has_key?(segment)
- manifest[segment].each do |manifest_record|
- checksums[manifest_record[:checksum]] = nil
- end
- end
- checksums
- end
-
- def extract_manifest_records_by_path(manifest)
- manifest_records_by_path = {}
- COOKBOOK_SEGMENTS.each do |segment|
- next unless manifest.has_key?(segment)
- manifest[segment].each do |manifest_record|
- manifest_records_by_path[manifest_record[:path]] = manifest_record
- end
- end
- manifest_records_by_path
- end
-
end
end
diff --git a/spec/unit/cookbook_loader_spec.rb b/spec/unit/cookbook_loader_spec.rb
index 51532778e4..45a985bafd 100644
--- a/spec/unit/cookbook_loader_spec.rb
+++ b/spec/unit/cookbook_loader_spec.rb
@@ -190,6 +190,11 @@ describe Chef::CookbookLoader do
end
describe "loading only one cookbook" do
+
+ let(:openldap_cookbook) { cookbook_loader["openldap"] }
+
+ let(:cookbook_as_hash) { Chef::CookbookManifest.new(openldap_cookbook).to_hash }
+
before(:each) do
cookbook_loader.load_cookbook("openldap")
end
@@ -205,12 +210,11 @@ describe Chef::CookbookLoader do
it "should not duplicate keys when serialized to JSON" do
# Chef JSON serialization will generate duplicate keys if given
# a Hash containing matching string and symbol keys. See CHEF-4571.
- aa = cookbook_loader["openldap"]
- expect(aa.to_hash["metadata"].recipes.keys).not_to include(:openldap)
- expect(aa.to_hash["metadata"].recipes.keys).to include("openldap")
+ expect(cookbook_as_hash["metadata"].recipes.keys).not_to include(:openldap)
+ expect(cookbook_as_hash["metadata"].recipes.keys).to include("openldap")
expected_desc = "Main Open LDAP configuration"
- expect(aa.to_hash["metadata"].recipes["openldap"]).to eq(expected_desc)
- raw = Chef::JSONCompat.to_json(aa.to_hash["metadata"].recipes)
+ expect(cookbook_as_hash["metadata"].recipes["openldap"]).to eq(expected_desc)
+ raw = Chef::JSONCompat.to_json(cookbook_as_hash["metadata"].recipes)
search_str = "\"openldap\":\""
key_idx = raw.index(search_str)
expect(key_idx).to be > 0
diff --git a/spec/unit/cookbook_manifest_spec.rb b/spec/unit/cookbook_manifest_spec.rb
index 8b50b040c2..938f72c743 100644
--- a/spec/unit/cookbook_manifest_spec.rb
+++ b/spec/unit/cookbook_manifest_spec.rb
@@ -1,7 +1,6 @@
#
-# Author:: Tim Hinderliter (<tim@opscode.com>)
-# Author:: Christopher Walters (<cw@opscode.com>)
-# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright (c) 2015 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,540 +14,214 @@
# 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/cookbook_manifest'
+require 'chef/digester'
+require 'pathname'
-describe "Chef::CookbookVersion manifest" do
- before(:each) do
- @cookbook = Chef::CookbookVersion.new "test-cookbook"
- @cookbook.manifest = {
- "files" =>
- [
- # afile.rb
- {
- :name => "afile.rb",
- :path => "files/host-examplehost.example.org/afile.rb",
- :checksum => "csum-host",
- :specificity => "host-examplehost.example.org"
- },
- {
- :name => "afile.rb",
- :path => "files/ubuntu-9.10/afile.rb",
- :checksum => "csum-platver-full",
- :specificity => "ubuntu-9.10"
- },
- {
- :name => "afile.rb",
- :path => "files/newubuntu-9/afile.rb",
- :checksum => "csum-platver-partial",
- :specificity => "newubuntu-9"
- },
- {
- :name => "afile.rb",
- :path => "files/ubuntu/afile.rb",
- :checksum => "csum-plat",
- :specificity => "ubuntu"
- },
- {
- :name => "afile.rb",
- :path => "files/default/afile.rb",
- :checksum => "csum-default",
- :specificity => "default"
- },
-
- # for different/odd platform_versions
- {
- :name => "bfile.rb",
- :path => "files/fakeos-2.0.rc.1/bfile.rb",
- :checksum => "csum2-platver-full",
- :specificity => "fakeos-2.0.rc.1"
- },
- {
- :name => "bfile.rb",
- :path => "files/newfakeos-2.0.rc/bfile.rb",
- :checksum => "csum2-platver-partial",
- :specificity => "newfakeos-2.0.rc"
- },
- {
- :name => "bfile.rb",
- :path => "files/fakeos-maple tree/bfile.rb",
- :checksum => "csum3-platver-full",
- :specificity => "maple tree"
- },
- {
- :name => "bfile.rb",
- :path => "files/fakeos-1/bfile.rb",
- :checksum => "csum4-platver-full",
- :specificity => "fakeos-1"
- },
-
- # directory adirectory
- {
- :name => "anotherfile1.rb",
- :path => "files/host-examplehost.example.org/adirectory/anotherfile1.rb.host",
- :checksum => "csum-host-1",
- :specificity => "host-examplehost.example.org"
- },
- {
- :name => "anotherfile2.rb",
- :path => "files/host-examplehost.example.org/adirectory/anotherfile2.rb.host",
- :checksum => "csum-host-2",
- :specificity => "host-examplehost.example.org"
- },
-
- {
- :name => "anotherfile1.rb",
- :path => "files/ubuntu-9.10/adirectory/anotherfile1.rb.platform-full-version",
- :checksum => "csum-platver-full-1",
- :specificity => "ubuntu-9.10"
- },
- {
- :name => "anotherfile2.rb",
- :path => "files/ubuntu-9.10/adirectory/anotherfile2.rb.platform-full-version",
- :checksum => "csum-platver-full-2",
- :specificity => "ubuntu-9.10"
- },
-
- {
- :name => "anotherfile1.rb",
- :path => "files/newubuntu-9/adirectory/anotherfile1.rb.platform-partial-version",
- :checksum => "csum-platver-partial-1",
- :specificity => "newubuntu-9"
- },
- {
- :name => "anotherfile2.rb",
- :path => "files/newubuntu-9/adirectory/anotherfile2.rb.platform-partial-version",
- :checksum => "csum-platver-partial-2",
- :specificity => "nweubuntu-9"
- },
-
- {
- :name => "anotherfile1.rb",
- :path => "files/ubuntu/adirectory/anotherfile1.rb.platform",
- :checksum => "csum-plat-1",
- :specificity => "ubuntu"
- },
- {
- :name => "anotherfile2.rb",
- :path => "files/ubuntu/adirectory/anotherfile2.rb.platform",
- :checksum => "csum-plat-2",
- :specificity => "ubuntu"
- },
-
- {
- :name => "anotherfile1.rb",
- :path => "files/default/adirectory/anotherfile1.rb.default",
- :checksum => "csum-default-1",
- :specificity => "default"
- },
- {
- :name => "anotherfile2.rb",
- :path => "files/default/adirectory/anotherfile2.rb.default",
- :checksum => "csum-default-2",
- :specificity => "default"
- },
- # for different/odd platform_versions
- {
- :name => "anotherfile1.rb",
- :path => "files/fakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-full-version",
- :checksum => "csum2-platver-full-1",
- :specificity => "fakeos-2.0.rc.1"
- },
- {
- :name => "anotherfile2.rb",
- :path => "files/fakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-full-version",
- :checksum => "csum2-platver-full-2",
- :specificity => "fakeos-2.0.rc.1"
- },
- {
- :name => "anotherfile1.rb",
- :path => "files/newfakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-partial-version",
- :checksum => "csum2-platver-partial-1",
- :specificity => "newfakeos-2.0.rc"
- },
- {
- :name => "anotherfile2.rb",
- :path => "files/newfakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-partial-version",
- :checksum => "csum2-platver-partial-2",
- :specificity => "newfakeos-2.0.rc"
- },
- {
- :name => "anotherfile1.rb",
- :path => "files/fakeos-maple tree/adirectory/anotherfile1.rb.platform-full-version",
- :checksum => "csum3-platver-full-1",
- :specificity => "fakeos-maple tree"
- },
- {
- :name => "anotherfile2.rb",
- :path => "files/fakeos-maple tree/adirectory/anotherfile2.rb.platform-full-version",
- :checksum => "csum3-platver-full-2",
- :specificity => "fakeos-maple tree"
- },
- {
- :name => "anotherfile1.rb",
- :path => "files/fakeos-1/adirectory/anotherfile1.rb.platform-full-version",
- :checksum => "csum4-platver-full-1",
- :specificity => "fakeos-1"
- },
- {
- :name => "anotherfile2.rb",
- :path => "files/fakeos-1/adirectory/anotherfile2.rb.platform-full-version",
- :checksum => "csum4-platver-full-2",
- :specificity => "fakeos-1"
- },
- ]
- }
-
- end
-
-
- it "should return a manifest record based on priority preference: host" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "ubuntu"
- node.automatic_attrs[:platform_version] = "9.10"
- node.automatic_attrs[:fqdn] = "examplehost.example.org"
-
- manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb")
- expect(manifest_record).not_to be_nil
- expect(manifest_record[:checksum]).to eq("csum-host")
- end
-
- it "should return a manifest record based on priority preference: platform & full version" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "ubuntu"
- node.automatic_attrs[:platform_version] = "9.10"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb")
- expect(manifest_record).not_to be_nil
- expect(manifest_record[:checksum]).to eq("csum-platver-full")
- end
-
- it "should return a manifest record based on priority preference: platform & partial version" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "newubuntu"
- node.automatic_attrs[:platform_version] = "9.10"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb")
- expect(manifest_record).not_to be_nil
- expect(manifest_record[:checksum]).to eq("csum-platver-partial")
- end
+describe Chef::CookbookManifest do
- it "should return a manifest record based on priority preference: platform only" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "ubuntu"
- node.automatic_attrs[:platform_version] = "1.0"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ let(:version) { "1.2.3" }
- manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb")
- expect(manifest_record).not_to be_nil
- expect(manifest_record[:checksum]).to eq("csum-plat")
- end
-
- it "should return a manifest record based on priority preference: default" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "notubuntu"
- node.automatic_attrs[:platform_version] = "1.0"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb")
- expect(manifest_record).not_to be_nil
- expect(manifest_record[:checksum]).to eq("csum-default")
+ let(:metadata) do
+ Chef::Cookbook::Metadata.new.tap do |m|
+ m.version(version)
+ end
end
- it "should return a manifest record based on priority preference: platform & full version - platform_version variant 1" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "fakeos"
- node.automatic_attrs[:platform_version] = "2.0.rc.1"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ let(:cookbook_root) { '/tmp/blah' }
- manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb")
- expect(manifest_record).not_to be_nil
- expect(manifest_record[:checksum]).to eq("csum2-platver-full")
+ let(:cookbook_version) do
+ Chef::CookbookVersion.new("tatft", cookbook_root).tap do |c|
+ c.metadata = metadata
+ end
end
- it "should return a manifest record based on priority preference: platform & partial version - platform_version variant 1" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "newfakeos"
- node.automatic_attrs[:platform_version] = "2.0.rc.1"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ let(:policy_mode) { false }
- manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb")
- expect(manifest_record).not_to be_nil
- expect(manifest_record[:checksum]).to eq("csum2-platver-partial")
- end
+ subject(:cookbook_manifest) { Chef::CookbookManifest.new(cookbook_version, policy_mode: policy_mode) }
- it "should return a manifest record based on priority preference: platform & full version - platform_version variant 2" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "fakeos"
- node.automatic_attrs[:platform_version] = "maple tree"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ context "when policy mode is not specified" do
- manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb")
- expect(manifest_record).not_to be_nil
- expect(manifest_record[:checksum]).to eq("csum3-platver-full")
- end
+ subject(:cookbook_manifest) { Chef::CookbookManifest.new(cookbook_version) }
- it "should return a manifest record based on priority preference: platform & full version - platform_version variant 3" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "fakeos"
- node.automatic_attrs[:platform_version] = "1"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ it "defaults to policies disabled" do
+ expect(cookbook_manifest.policy_mode?).to be(false)
+ end
- manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb")
- expect(manifest_record).not_to be_nil
- expect(manifest_record[:checksum]).to eq("csum4-platver-full")
end
- describe "when fetching the contents of a directory by file specificity" do
-
- it "should return a directory of manifest records based on priority preference: host" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "ubuntu"
- node.automatic_attrs[:platform_version] = "9.10"
- node.automatic_attrs[:fqdn] = "examplehost.example.org"
+ describe "collecting cookbook data from the cookbook version object" do
- manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
- expect(manifest_records).not_to be_nil
- expect(manifest_records.size).to eq(2)
-
- checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum-host-1", "csum-host-2"])
+ it "delegates `name' to cookbook_version" do
+ expect(cookbook_manifest.name).to eq("tatft")
end
- it "should return a directory of manifest records based on priority preference: platform & full version" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "ubuntu"
- node.automatic_attrs[:platform_version] = "9.10"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
- expect(manifest_records).not_to be_nil
- expect(manifest_records.size).to eq(2)
-
- checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum-platver-full-1", "csum-platver-full-2"])
+ it "delegates `root_paths' to cookbook_version" do
+ expect(cookbook_manifest.root_paths).to eq(['/tmp/blah'])
end
- it "should return a directory of manifest records based on priority preference: platform & partial version" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "newubuntu"
- node.automatic_attrs[:platform_version] = "9.10"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
- expect(manifest_records).not_to be_nil
- expect(manifest_records.size).to eq(2)
-
- checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum-platver-partial-1", "csum-platver-partial-2"])
+ it "delegates `metadata' to cookbook_version" do
+ expect(cookbook_manifest.metadata).to eq(metadata)
end
- it "should return a directory of manifest records based on priority preference: platform only" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "ubuntu"
- node.automatic_attrs[:platform_version] = "1.0"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
- expect(manifest_records).not_to be_nil
- expect(manifest_records.size).to eq(2)
-
- checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum-plat-1", "csum-plat-2"])
+ it "delegates `full_name' to cookbook_version" do
+ expect(cookbook_manifest.full_name).to eq("tatft-1.2.3")
end
- it "should return a directory of manifest records based on priority preference: default" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "notubuntu"
- node.automatic_attrs[:platform_version] = "1.0"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
- expect(manifest_records).not_to be_nil
- expect(manifest_records.size).to eq(2)
-
- checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum-default-1", "csum-default-2"])
+ it "delegates `version' to cookbook_version" do
+ expect(cookbook_manifest.version).to eq(version)
end
- it "should return a manifest record based on priority preference: platform & full version - platform_version variant 1" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "fakeos"
- node.automatic_attrs[:platform_version] = "2.0.rc.1"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
- expect(manifest_records).not_to be_nil
- expect(manifest_records.size).to eq(2)
+ it "delegates `frozen_version?' to cookbook_version" do
+ expect(cookbook_manifest.frozen_version?).to be(false)
+ end
- checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum2-platver-full-1", "csum2-platver-full-2"])
+ it "delegates `segment_filenames' to cookbook_version" do
+ expect(cookbook_version).to receive(:segment_filenames).with(:recipes).and_return([])
+ expect(cookbook_manifest.segment_filenames(:recipes)).to eq([])
end
- it "should return a manifest record based on priority preference: platform & partial version - platform_version variant 1" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "newfakeos"
- node.automatic_attrs[:platform_version] = "2.0.rc.1"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ end
- manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
- expect(manifest_records).not_to be_nil
- expect(manifest_records.size).to eq(2)
+ context "when given an empty cookbook" do
- checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum2-platver-partial-1", "csum2-platver-partial-2"])
- end
+ let(:expected_hash) do
+ {
+ "chef_type" => "cookbook_version",
- it "should return a manifest record based on priority preference: platform & full version - platform_version variant 2" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "fakeos"
- node.automatic_attrs[:platform_version] = "maple tree"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ "name" => "tatft-1.2.3",
+ "version" => "1.2.3",
+ "cookbook_name" => "tatft",
+ "metadata" => metadata,
- manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
- expect(manifest_records).not_to be_nil
- expect(manifest_records.size).to eq(2)
+ "frozen?" => false,
- checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum3-platver-full-1", "csum3-platver-full-2"])
+ "recipes" =>[],
+ "definitions" =>[],
+ "libraries" =>[],
+ "attributes" =>[],
+ "files" =>[],
+ "templates" =>[],
+ "resources" =>[],
+ "providers" =>[],
+ "root_files" =>[],
+ }
end
- it "should return a manifest record based on priority preference: platform & full version - platform_version variant 3" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "fakeos"
- node.automatic_attrs[:platform_version] = "1"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
- expect(manifest_records).not_to be_nil
- expect(manifest_records.size).to eq(2)
-
- checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
- expect(checksums.sort).to eq(["csum4-platver-full-1", "csum4-platver-full-2"])
+ it "converts the CookbookVersion to a ruby Hash representation" do
+ expect(cookbook_manifest.to_hash).to eq(expected_hash)
end
+
end
- ## Globbing the relative paths out of the manifest records ##
+ context "when given a cookbook with files" do
- describe "when globbing for relative file paths based on filespecificity" do
- it "should return a list of relative paths based on priority preference: host" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "ubuntu"
- node.automatic_attrs[:platform_version] = "9.10"
- node.automatic_attrs[:fqdn] = "examplehost.example.org"
+ let(:cookbook_root) { File.join(CHEF_SPEC_DATA, 'cb_version_cookbooks', 'tatft') }
- filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
- expect(filenames).not_to be_nil
- expect(filenames.size).to eq(2)
+ let(:attribute_filenames) { Dir[File.join(cookbook_root, 'attributes', '**', '*.rb')] }
+ let(:definition_filenames) { Dir[File.join(cookbook_root, 'definitions', '**', '*.rb')] }
+ let(:file_filenames) { Dir[File.join(cookbook_root, 'files', '**', '*.tgz')] }
+ let(:recipe_filenames) { Dir[File.join(cookbook_root, 'recipes', '**', '*.rb')] }
+ let(:template_filenames) { Dir[File.join(cookbook_root, 'templates', '**', '*.erb')] }
+ let(:library_filenames) { Dir[File.join(cookbook_root, 'libraries', '**', '*.rb')] }
+ let(:resource_filenames) { Dir[File.join(cookbook_root, 'resources', '**', '*.rb')] }
+ let(:provider_filenames) { Dir[File.join(cookbook_root, 'providers', '**', '*.rb')] }
+ let(:root_filenames) { Array(File.join(cookbook_root, 'README.rdoc')) }
+ let(:metadata_filenames) { Array(File.join(cookbook_root, 'metadata.json')) }
- expect(filenames.sort).to eq(['anotherfile1.rb.host', 'anotherfile2.rb.host'])
- end
+ let(:match_md5) { /[0-9a-f]{32}/ }
- it "should return a list of relative paths based on priority preference: platform & full version" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "ubuntu"
- node.automatic_attrs[:platform_version] = "9.10"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ def map_to_file_specs(paths)
+ paths.map do |path|
- filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
- expect(filenames).not_to be_nil
- expect(filenames.size).to eq(2)
+ relative_path = Pathname.new(path).relative_path_from(Pathname.new(cookbook_root)).to_s
- expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version'])
+ {
+ "name" => File.basename(path),
+ "path" => relative_path,
+ "checksum" => Chef::Digester.generate_md5_checksum_for_file(path),
+ "specificity" => "default",
+ }
+ end
end
- it "should return a list of relative paths based on priority preference: platform & partial version" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "newubuntu"
- node.automatic_attrs[:platform_version] = "9.10"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
- expect(filenames).not_to be_nil
- expect(filenames.size).to eq(2)
+ let(:expected_hash) do
+ {
+ "chef_type" => "cookbook_version",
- expect(filenames.sort).to eq(['anotherfile1.rb.platform-partial-version', 'anotherfile2.rb.platform-partial-version'])
- end
-
- it "should return a list of relative paths based on priority preference: platform only" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "ubuntu"
- node.automatic_attrs[:platform_version] = "1.0"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ "name" => "tatft-1.2.3",
+ "version" => "1.2.3",
+ "cookbook_name" => "tatft",
+ "metadata" => metadata,
- filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
- expect(filenames).not_to be_nil
- expect(filenames.size).to eq(2)
+ "frozen?" => false,
- expect(filenames.sort).to eq(['anotherfile1.rb.platform', 'anotherfile2.rb.platform'])
+ "recipes" => map_to_file_specs(recipe_filenames),
+ "definitions" => map_to_file_specs(definition_filenames),
+ "libraries" => map_to_file_specs(library_filenames),
+ "attributes" => map_to_file_specs(attribute_filenames),
+ "files" => map_to_file_specs(file_filenames),
+ "templates" => map_to_file_specs(template_filenames),
+ "resources" => map_to_file_specs(resource_filenames),
+ "providers" => map_to_file_specs(provider_filenames),
+ "root_files" => map_to_file_specs(root_filenames),
+ }
end
- it "should return a list of relative paths based on priority preference: default" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "notubuntu"
- node.automatic_attrs[:platform_version] = "1.0"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ before do
+ cookbook_version.attribute_filenames = attribute_filenames
+ cookbook_version.definition_filenames = definition_filenames
+ cookbook_version.file_filenames = file_filenames
+ cookbook_version.recipe_filenames = recipe_filenames
+ cookbook_version.template_filenames = template_filenames
+ cookbook_version.library_filenames = library_filenames
+ cookbook_version.resource_filenames = resource_filenames
+ cookbook_version.provider_filenames = provider_filenames
+ cookbook_version.root_filenames = root_filenames
+ cookbook_version.metadata_filenames = metadata_filenames
+ end
- filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
- expect(filenames).not_to be_nil
- expect(filenames.size).to eq(2)
+ it "converts the CookbookVersion to a ruby Hash representation" do
+ cookbook_manifest_hash = cookbook_manifest.to_hash
- expect(filenames.sort).to eq(['anotherfile1.rb.default', 'anotherfile2.rb.default'])
+ expect(cookbook_manifest_hash.keys).to match_array(expected_hash.keys)
+ cookbook_manifest_hash.each do |key, value|
+ expect(cookbook_manifest_hash[key]).to eq(expected_hash[key])
+ end
end
- it "should return a list of relative paths based on priority preference: platform & full version - platform_version variant 1" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "fakeos"
- node.automatic_attrs[:platform_version] = "2.0.rc.1"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ end
- filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
- expect(filenames).not_to be_nil
- expect(filenames.size).to eq(2)
+ describe "providing upstream URLs for save" do
- expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version'])
- end
+ context "and policy mode is disabled" do
- it "should return a list of relative paths based on priority preference: platform & partial version - platform_version variant 1" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "newfakeos"
- node.automatic_attrs[:platform_version] = "2.0.rc.1"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ it "gives the save URL" do
+ expect(cookbook_manifest.save_url).to eq("cookbooks/tatft/1.2.3")
+ end
- filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
- expect(filenames).not_to be_nil
- expect(filenames.size).to eq(2)
+ it "gives the force save URL" do
+ expect(cookbook_manifest.force_save_url).to eq("cookbooks/tatft/1.2.3?force=true")
+ end
- expect(filenames.sort).to eq(['anotherfile1.rb.platform-partial-version', 'anotherfile2.rb.platform-partial-version'])
end
- it "should return a list of relative paths based on priority preference: platform & full version - platform_version variant 2" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "fakeos"
- node.automatic_attrs[:platform_version] = "maple tree"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
-
- filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
- expect(filenames).not_to be_nil
- expect(filenames.size).to eq(2)
+ context "and policy mode is enabled" do
- expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version'])
- end
+ let(:policy_mode) { true }
- it "should return a list of relative paths based on priority preference: platform & full version - platform_version variant 3" do
- node = Chef::Node.new
- node.automatic_attrs[:platform] = "fakeos"
- node.automatic_attrs[:platform_version] = "1"
- node.automatic_attrs[:fqdn] = "differenthost.example.org"
+ it "gives the save URL" do
+ expect(cookbook_manifest.save_url).to eq("cookbook_artifacts/tatft/1.2.3")
+ end
- filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
- expect(filenames).not_to be_nil
- expect(filenames.size).to eq(2)
+ it "gives the force save URL" do
+ expect(cookbook_manifest.force_save_url).to eq("cookbook_artifacts/tatft/1.2.3?force=true")
+ end
- expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version'])
end
end
+
end
+
diff --git a/spec/unit/cookbook_uploader_spec.rb b/spec/unit/cookbook_uploader_spec.rb
index af25baff13..152e5373f0 100644
--- a/spec/unit/cookbook_uploader_spec.rb
+++ b/spec/unit/cookbook_uploader_spec.rb
@@ -45,7 +45,13 @@ describe Chef::CookbookUploader do
let(:sandbox_commit_uri) { "https://chef.example.org/sandboxes/abc123" }
- let(:uploader) { described_class.new(cookbooks_to_upload, :rest => http_client) }
+ let(:policy_mode) { false }
+
+ let(:uploader) { described_class.new(cookbooks_to_upload, rest: http_client, policy_mode: policy_mode) }
+
+ it "defaults to not enabling policy mode" do
+ expect(described_class.new(cookbooks_to_upload, rest: http_client).policy_mode?).to be(false)
+ end
it "has a list of cookbooks to upload" do
expect(uploader.cookbooks).to eq(cookbooks_to_upload)
@@ -61,7 +67,7 @@ describe Chef::CookbookUploader do
describe "uploading cookbooks" do
def url_for(cksum)
- "https://storage.example.com/#{cksum}"
+ "https://storage.example.com/#{cksum}"
end
let(:sandbox_response) do
@@ -94,6 +100,10 @@ describe Chef::CookbookUploader do
end
end
+ def expected_save_url(cookbook)
+ "cookbooks/#{cookbook.name}/#{cookbook.version}"
+ end
+
def expect_sandbox_commit
expect(http_client).to receive(:put).with(sandbox_commit_uri, {:is_completed => true})
end
@@ -102,7 +112,7 @@ describe Chef::CookbookUploader do
cookbooks_to_upload.each do |cookbook|
expect(http_client).to receive(:put).
- with(cookbook.save_url, cookbook)
+ with(expected_save_url(cookbook), cookbook)
end
end
@@ -155,6 +165,30 @@ describe Chef::CookbookUploader do
end
end
+
+ context "when policy_mode is specified" do
+
+ let(:cksums_not_on_remote) do
+ checksums_of_cookbook_files.keys
+ end
+
+ let(:policy_mode) { true }
+
+ def expected_save_url(cookbook)
+ "cookbook_artifacts/#{cookbook.name}/#{cookbook.version}"
+ end
+
+ it "uploads all files in a sandbox transaction, then creates cookbooks on the server using cookbook_artifacts API" 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/cookbook_version_file_specificity_spec.rb b/spec/unit/cookbook_version_file_specificity_spec.rb
new file mode 100644
index 0000000000..73b10899d4
--- /dev/null
+++ b/spec/unit/cookbook_version_file_specificity_spec.rb
@@ -0,0 +1,554 @@
+#
+# Author:: Tim Hinderliter (<tim@opscode.com>)
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, 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::CookbookVersion, "file specificity" do
+ before(:each) do
+ @cookbook = Chef::CookbookVersion.new "test-cookbook"
+ @cookbook.manifest = {
+ "files" =>
+ [
+ # afile.rb
+ {
+ :name => "afile.rb",
+ :path => "files/host-examplehost.example.org/afile.rb",
+ :checksum => "csum-host",
+ :specificity => "host-examplehost.example.org"
+ },
+ {
+ :name => "afile.rb",
+ :path => "files/ubuntu-9.10/afile.rb",
+ :checksum => "csum-platver-full",
+ :specificity => "ubuntu-9.10"
+ },
+ {
+ :name => "afile.rb",
+ :path => "files/newubuntu-9/afile.rb",
+ :checksum => "csum-platver-partial",
+ :specificity => "newubuntu-9"
+ },
+ {
+ :name => "afile.rb",
+ :path => "files/ubuntu/afile.rb",
+ :checksum => "csum-plat",
+ :specificity => "ubuntu"
+ },
+ {
+ :name => "afile.rb",
+ :path => "files/default/afile.rb",
+ :checksum => "csum-default",
+ :specificity => "default"
+ },
+
+ # for different/odd platform_versions
+ {
+ :name => "bfile.rb",
+ :path => "files/fakeos-2.0.rc.1/bfile.rb",
+ :checksum => "csum2-platver-full",
+ :specificity => "fakeos-2.0.rc.1"
+ },
+ {
+ :name => "bfile.rb",
+ :path => "files/newfakeos-2.0.rc/bfile.rb",
+ :checksum => "csum2-platver-partial",
+ :specificity => "newfakeos-2.0.rc"
+ },
+ {
+ :name => "bfile.rb",
+ :path => "files/fakeos-maple tree/bfile.rb",
+ :checksum => "csum3-platver-full",
+ :specificity => "maple tree"
+ },
+ {
+ :name => "bfile.rb",
+ :path => "files/fakeos-1/bfile.rb",
+ :checksum => "csum4-platver-full",
+ :specificity => "fakeos-1"
+ },
+
+ # directory adirectory
+ {
+ :name => "anotherfile1.rb",
+ :path => "files/host-examplehost.example.org/adirectory/anotherfile1.rb.host",
+ :checksum => "csum-host-1",
+ :specificity => "host-examplehost.example.org"
+ },
+ {
+ :name => "anotherfile2.rb",
+ :path => "files/host-examplehost.example.org/adirectory/anotherfile2.rb.host",
+ :checksum => "csum-host-2",
+ :specificity => "host-examplehost.example.org"
+ },
+
+ {
+ :name => "anotherfile1.rb",
+ :path => "files/ubuntu-9.10/adirectory/anotherfile1.rb.platform-full-version",
+ :checksum => "csum-platver-full-1",
+ :specificity => "ubuntu-9.10"
+ },
+ {
+ :name => "anotherfile2.rb",
+ :path => "files/ubuntu-9.10/adirectory/anotherfile2.rb.platform-full-version",
+ :checksum => "csum-platver-full-2",
+ :specificity => "ubuntu-9.10"
+ },
+
+ {
+ :name => "anotherfile1.rb",
+ :path => "files/newubuntu-9/adirectory/anotherfile1.rb.platform-partial-version",
+ :checksum => "csum-platver-partial-1",
+ :specificity => "newubuntu-9"
+ },
+ {
+ :name => "anotherfile2.rb",
+ :path => "files/newubuntu-9/adirectory/anotherfile2.rb.platform-partial-version",
+ :checksum => "csum-platver-partial-2",
+ :specificity => "nweubuntu-9"
+ },
+
+ {
+ :name => "anotherfile1.rb",
+ :path => "files/ubuntu/adirectory/anotherfile1.rb.platform",
+ :checksum => "csum-plat-1",
+ :specificity => "ubuntu"
+ },
+ {
+ :name => "anotherfile2.rb",
+ :path => "files/ubuntu/adirectory/anotherfile2.rb.platform",
+ :checksum => "csum-plat-2",
+ :specificity => "ubuntu"
+ },
+
+ {
+ :name => "anotherfile1.rb",
+ :path => "files/default/adirectory/anotherfile1.rb.default",
+ :checksum => "csum-default-1",
+ :specificity => "default"
+ },
+ {
+ :name => "anotherfile2.rb",
+ :path => "files/default/adirectory/anotherfile2.rb.default",
+ :checksum => "csum-default-2",
+ :specificity => "default"
+ },
+ # for different/odd platform_versions
+ {
+ :name => "anotherfile1.rb",
+ :path => "files/fakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-full-version",
+ :checksum => "csum2-platver-full-1",
+ :specificity => "fakeos-2.0.rc.1"
+ },
+ {
+ :name => "anotherfile2.rb",
+ :path => "files/fakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-full-version",
+ :checksum => "csum2-platver-full-2",
+ :specificity => "fakeos-2.0.rc.1"
+ },
+ {
+ :name => "anotherfile1.rb",
+ :path => "files/newfakeos-2.0.rc.1/adirectory/anotherfile1.rb.platform-partial-version",
+ :checksum => "csum2-platver-partial-1",
+ :specificity => "newfakeos-2.0.rc"
+ },
+ {
+ :name => "anotherfile2.rb",
+ :path => "files/newfakeos-2.0.rc.1/adirectory/anotherfile2.rb.platform-partial-version",
+ :checksum => "csum2-platver-partial-2",
+ :specificity => "newfakeos-2.0.rc"
+ },
+ {
+ :name => "anotherfile1.rb",
+ :path => "files/fakeos-maple tree/adirectory/anotherfile1.rb.platform-full-version",
+ :checksum => "csum3-platver-full-1",
+ :specificity => "fakeos-maple tree"
+ },
+ {
+ :name => "anotherfile2.rb",
+ :path => "files/fakeos-maple tree/adirectory/anotherfile2.rb.platform-full-version",
+ :checksum => "csum3-platver-full-2",
+ :specificity => "fakeos-maple tree"
+ },
+ {
+ :name => "anotherfile1.rb",
+ :path => "files/fakeos-1/adirectory/anotherfile1.rb.platform-full-version",
+ :checksum => "csum4-platver-full-1",
+ :specificity => "fakeos-1"
+ },
+ {
+ :name => "anotherfile2.rb",
+ :path => "files/fakeos-1/adirectory/anotherfile2.rb.platform-full-version",
+ :checksum => "csum4-platver-full-2",
+ :specificity => "fakeos-1"
+ },
+ ]
+ }
+
+ end
+
+
+ it "should return a manifest record based on priority preference: host" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = "9.10"
+ node.automatic_attrs[:fqdn] = "examplehost.example.org"
+
+ manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb")
+ expect(manifest_record).not_to be_nil
+ expect(manifest_record[:checksum]).to eq("csum-host")
+ end
+
+ it "should return a manifest record based on priority preference: platform & full version" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = "9.10"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb")
+ expect(manifest_record).not_to be_nil
+ expect(manifest_record[:checksum]).to eq("csum-platver-full")
+ end
+
+ it "should return a manifest record based on priority preference: platform & partial version" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "newubuntu"
+ node.automatic_attrs[:platform_version] = "9.10"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb")
+ expect(manifest_record).not_to be_nil
+ expect(manifest_record[:checksum]).to eq("csum-platver-partial")
+ end
+
+ it "should return a manifest record based on priority preference: platform only" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = "1.0"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb")
+ expect(manifest_record).not_to be_nil
+ expect(manifest_record[:checksum]).to eq("csum-plat")
+ end
+
+ it "should return a manifest record based on priority preference: default" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "notubuntu"
+ node.automatic_attrs[:platform_version] = "1.0"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_record = @cookbook.preferred_manifest_record(node, :files, "afile.rb")
+ expect(manifest_record).not_to be_nil
+ expect(manifest_record[:checksum]).to eq("csum-default")
+ end
+
+ it "should return a manifest record based on priority preference: platform & full version - platform_version variant 1" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "fakeos"
+ node.automatic_attrs[:platform_version] = "2.0.rc.1"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb")
+ expect(manifest_record).not_to be_nil
+ expect(manifest_record[:checksum]).to eq("csum2-platver-full")
+ end
+
+ it "should return a manifest record based on priority preference: platform & partial version - platform_version variant 1" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "newfakeos"
+ node.automatic_attrs[:platform_version] = "2.0.rc.1"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb")
+ expect(manifest_record).not_to be_nil
+ expect(manifest_record[:checksum]).to eq("csum2-platver-partial")
+ end
+
+ it "should return a manifest record based on priority preference: platform & full version - platform_version variant 2" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "fakeos"
+ node.automatic_attrs[:platform_version] = "maple tree"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb")
+ expect(manifest_record).not_to be_nil
+ expect(manifest_record[:checksum]).to eq("csum3-platver-full")
+ end
+
+ it "should return a manifest record based on priority preference: platform & full version - platform_version variant 3" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "fakeos"
+ node.automatic_attrs[:platform_version] = "1"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_record = @cookbook.preferred_manifest_record(node, :files, "bfile.rb")
+ expect(manifest_record).not_to be_nil
+ expect(manifest_record[:checksum]).to eq("csum4-platver-full")
+ end
+
+ describe "when fetching the contents of a directory by file specificity" do
+
+ it "should return a directory of manifest records based on priority preference: host" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = "9.10"
+ node.automatic_attrs[:fqdn] = "examplehost.example.org"
+
+ manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
+ expect(manifest_records).not_to be_nil
+ expect(manifest_records.size).to eq(2)
+
+ checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
+ expect(checksums.sort).to eq(["csum-host-1", "csum-host-2"])
+ end
+
+ it "should return a directory of manifest records based on priority preference: platform & full version" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = "9.10"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
+ expect(manifest_records).not_to be_nil
+ expect(manifest_records.size).to eq(2)
+
+ checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
+ expect(checksums.sort).to eq(["csum-platver-full-1", "csum-platver-full-2"])
+ end
+
+ it "should return a directory of manifest records based on priority preference: platform & partial version" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "newubuntu"
+ node.automatic_attrs[:platform_version] = "9.10"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
+ expect(manifest_records).not_to be_nil
+ expect(manifest_records.size).to eq(2)
+
+ checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
+ expect(checksums.sort).to eq(["csum-platver-partial-1", "csum-platver-partial-2"])
+ end
+
+ it "should return a directory of manifest records based on priority preference: platform only" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = "1.0"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
+ expect(manifest_records).not_to be_nil
+ expect(manifest_records.size).to eq(2)
+
+ checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
+ expect(checksums.sort).to eq(["csum-plat-1", "csum-plat-2"])
+ end
+
+ it "should return a directory of manifest records based on priority preference: default" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "notubuntu"
+ node.automatic_attrs[:platform_version] = "1.0"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
+ expect(manifest_records).not_to be_nil
+ expect(manifest_records.size).to eq(2)
+
+ checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
+ expect(checksums.sort).to eq(["csum-default-1", "csum-default-2"])
+ end
+
+ it "should return a manifest record based on priority preference: platform & full version - platform_version variant 1" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "fakeos"
+ node.automatic_attrs[:platform_version] = "2.0.rc.1"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
+ expect(manifest_records).not_to be_nil
+ expect(manifest_records.size).to eq(2)
+
+ checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
+ expect(checksums.sort).to eq(["csum2-platver-full-1", "csum2-platver-full-2"])
+ end
+
+ it "should return a manifest record based on priority preference: platform & partial version - platform_version variant 1" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "newfakeos"
+ node.automatic_attrs[:platform_version] = "2.0.rc.1"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
+ expect(manifest_records).not_to be_nil
+ expect(manifest_records.size).to eq(2)
+
+ checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
+ expect(checksums.sort).to eq(["csum2-platver-partial-1", "csum2-platver-partial-2"])
+ end
+
+ it "should return a manifest record based on priority preference: platform & full version - platform_version variant 2" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "fakeos"
+ node.automatic_attrs[:platform_version] = "maple tree"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
+ expect(manifest_records).not_to be_nil
+ expect(manifest_records.size).to eq(2)
+
+ checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
+ expect(checksums.sort).to eq(["csum3-platver-full-1", "csum3-platver-full-2"])
+ end
+
+ it "should return a manifest record based on priority preference: platform & full version - platform_version variant 3" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "fakeos"
+ node.automatic_attrs[:platform_version] = "1"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ manifest_records = @cookbook.preferred_manifest_records_for_directory(node, :files, "adirectory")
+ expect(manifest_records).not_to be_nil
+ expect(manifest_records.size).to eq(2)
+
+ checksums = manifest_records.map{ |manifest_record| manifest_record[:checksum] }
+ expect(checksums.sort).to eq(["csum4-platver-full-1", "csum4-platver-full-2"])
+ end
+ end
+
+ ## Globbing the relative paths out of the manifest records ##
+
+ describe "when globbing for relative file paths based on filespecificity" do
+ it "should return a list of relative paths based on priority preference: host" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = "9.10"
+ node.automatic_attrs[:fqdn] = "examplehost.example.org"
+
+ filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
+ expect(filenames).not_to be_nil
+ expect(filenames.size).to eq(2)
+
+ expect(filenames.sort).to eq(['anotherfile1.rb.host', 'anotherfile2.rb.host'])
+ end
+
+ it "should return a list of relative paths based on priority preference: platform & full version" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = "9.10"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
+ expect(filenames).not_to be_nil
+ expect(filenames.size).to eq(2)
+
+ expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version'])
+ end
+
+ it "should return a list of relative paths based on priority preference: platform & partial version" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "newubuntu"
+ node.automatic_attrs[:platform_version] = "9.10"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
+ expect(filenames).not_to be_nil
+ expect(filenames.size).to eq(2)
+
+ expect(filenames.sort).to eq(['anotherfile1.rb.platform-partial-version', 'anotherfile2.rb.platform-partial-version'])
+ end
+
+ it "should return a list of relative paths based on priority preference: platform only" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "ubuntu"
+ node.automatic_attrs[:platform_version] = "1.0"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
+ expect(filenames).not_to be_nil
+ expect(filenames.size).to eq(2)
+
+ expect(filenames.sort).to eq(['anotherfile1.rb.platform', 'anotherfile2.rb.platform'])
+ end
+
+ it "should return a list of relative paths based on priority preference: default" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "notubuntu"
+ node.automatic_attrs[:platform_version] = "1.0"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
+ expect(filenames).not_to be_nil
+ expect(filenames.size).to eq(2)
+
+ expect(filenames.sort).to eq(['anotherfile1.rb.default', 'anotherfile2.rb.default'])
+ end
+
+ it "should return a list of relative paths based on priority preference: platform & full version - platform_version variant 1" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "fakeos"
+ node.automatic_attrs[:platform_version] = "2.0.rc.1"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
+ expect(filenames).not_to be_nil
+ expect(filenames.size).to eq(2)
+
+ expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version'])
+ end
+
+ it "should return a list of relative paths based on priority preference: platform & partial version - platform_version variant 1" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "newfakeos"
+ node.automatic_attrs[:platform_version] = "2.0.rc.1"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
+ expect(filenames).not_to be_nil
+ expect(filenames.size).to eq(2)
+
+ expect(filenames.sort).to eq(['anotherfile1.rb.platform-partial-version', 'anotherfile2.rb.platform-partial-version'])
+ end
+
+ it "should return a list of relative paths based on priority preference: platform & full version - platform_version variant 2" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "fakeos"
+ node.automatic_attrs[:platform_version] = "maple tree"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
+ expect(filenames).not_to be_nil
+ expect(filenames.size).to eq(2)
+
+ expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version'])
+ end
+
+ it "should return a list of relative paths based on priority preference: platform & full version - platform_version variant 3" do
+ node = Chef::Node.new
+ node.automatic_attrs[:platform] = "fakeos"
+ node.automatic_attrs[:platform_version] = "1"
+ node.automatic_attrs[:fqdn] = "differenthost.example.org"
+
+ filenames = @cookbook.relative_filenames_in_preferred_directory(node, :files, "adirectory")
+ expect(filenames).not_to be_nil
+ expect(filenames.size).to eq(2)
+
+ expect(filenames.sort).to eq(['anotherfile1.rb.platform-full-version', 'anotherfile2.rb.platform-full-version'])
+ end
+ end
+end
diff --git a/spec/unit/cookbook_version_spec.rb b/spec/unit/cookbook_version_spec.rb
index 6dd3429ffc..440dd9da6c 100644
--- a/spec/unit/cookbook_version_spec.rb
+++ b/spec/unit/cookbook_version_spec.rb
@@ -68,32 +68,10 @@ describe Chef::CookbookVersion do
expect(@cookbook_version).to be_frozen_version
end
- it "is \"ready\"" do
- # WTF is this? what are the valid states? and why aren't they set with encapsulating methods?
- # [Dan 15-Jul-2010]
- expect(@cookbook_version.status).to eq(:ready)
- end
-
it "has empty metadata" do
expect(@cookbook_version.metadata).to eq(Chef::Cookbook::Metadata.new)
end
- it "creates a manifest hash of its contents" do
- expected = {"recipes"=>[],
- "definitions"=>[],
- "libraries"=>[],
- "attributes"=>[],
- "files"=>[],
- "templates"=>[],
- "resources"=>[],
- "providers"=>[],
- "root_files"=>[],
- "cookbook_name"=>"tatft",
- "metadata"=>Chef::Cookbook::Metadata.new,
- "version"=>"0.0.0",
- "name"=>"tatft-0.0.0"}
- expect(@cookbook_version.manifest).to eq(expected)
- end
end
describe "with a cookbook directory named tatft" do
@@ -141,85 +119,6 @@ describe Chef::CookbookVersion do
@node.name("testing")
end
- it "generates a manifest containing the cookbook's files" do
- manifest = @cookbook_version.manifest
-
- expect(manifest["metadata"]).to eq(Chef::Cookbook::Metadata.new)
- expect(manifest["cookbook_name"]).to eq("tatft")
-
- expect(manifest["recipes"].size).to eq(1)
-
- recipe = manifest["recipes"].first
- expect(recipe["name"]).to eq("default.rb")
- expect(recipe["path"]).to eq("recipes/default.rb")
- expect(recipe["checksum"]).to match(MD5)
- expect(recipe["specificity"]).to eq("default")
-
- expect(manifest["definitions"].size).to eq(1)
-
- definition = manifest["definitions"].first
- expect(definition["name"]).to eq("runit_service.rb")
- expect(definition["path"]).to eq("definitions/runit_service.rb")
- expect(definition["checksum"]).to match(MD5)
- expect(definition["specificity"]).to eq("default")
-
- expect(manifest["libraries"].size).to eq(1)
-
- library = manifest["libraries"].first
- expect(library["name"]).to eq("ownage.rb")
- expect(library["path"]).to eq("libraries/ownage.rb")
- expect(library["checksum"]).to match(MD5)
- expect(library["specificity"]).to eq("default")
-
- expect(manifest["attributes"].size).to eq(1)
-
- attribute_file = manifest["attributes"].first
- expect(attribute_file["name"]).to eq("default.rb")
- expect(attribute_file["path"]).to eq("attributes/default.rb")
- expect(attribute_file["checksum"]).to match(MD5)
- expect(attribute_file["specificity"]).to eq("default")
-
- expect(manifest["files"].size).to eq(1)
-
- cookbook_file = manifest["files"].first
- expect(cookbook_file["name"]).to eq("giant_blob.tgz")
- expect(cookbook_file["path"]).to eq("files/default/giant_blob.tgz")
- expect(cookbook_file["checksum"]).to match(MD5)
- expect(cookbook_file["specificity"]).to eq("default")
-
- expect(manifest["templates"].size).to eq(1)
-
- template = manifest["templates"].first
- expect(template["name"]).to eq("configuration.erb")
- expect(template["path"]).to eq("templates/default/configuration.erb")
- expect(template["checksum"]).to match(MD5)
- expect(template["specificity"]).to eq("default")
-
- expect(manifest["resources"].size).to eq(1)
-
- lwr = manifest["resources"].first
- expect(lwr["name"]).to eq("lwr.rb")
- expect(lwr["path"]).to eq("resources/lwr.rb")
- expect(lwr["checksum"]).to match(MD5)
- expect(lwr["specificity"]).to eq("default")
-
- expect(manifest["providers"].size).to eq(1)
-
- lwp = manifest["providers"].first
- expect(lwp["name"]).to eq("lwp.rb")
- expect(lwp["path"]).to eq("providers/lwp.rb")
- expect(lwp["checksum"]).to match(MD5)
- expect(lwp["specificity"]).to eq("default")
-
- expect(manifest["root_files"].size).to eq(1)
-
- readme = manifest["root_files"].first
- expect(readme["name"]).to eq("README.rdoc")
- expect(readme["path"]).to eq("README.rdoc")
- expect(readme["checksum"]).to match(MD5)
- expect(readme["specificity"]).to eq("default")
- end
-
it "determines whether a template is available for a given node" do
expect(@cookbook_version).to have_template_for_node(@node, "configuration.erb")
expect(@cookbook_version).not_to have_template_for_node(@node, "missing.erb")
@@ -253,102 +152,6 @@ describe Chef::CookbookVersion do
end
end
- describe "and a cookbook_version with a different name" do
- before do
- # Currently the cookbook loader finds all the files then tells CookbookVersion
- # where they are.
- @cookbook_version = Chef::CookbookVersion.new("blarghle", @cookbook_root)
- @cookbook_version.attribute_filenames = @cookbook[:attribute_filenames]
- @cookbook_version.definition_filenames = @cookbook[:definition_filenames]
- @cookbook_version.recipe_filenames = @cookbook[:recipe_filenames]
- @cookbook_version.template_filenames = @cookbook[:template_filenames]
- @cookbook_version.file_filenames = @cookbook[:file_filenames]
- @cookbook_version.library_filenames = @cookbook[:library_filenames]
- @cookbook_version.resource_filenames = @cookbook[:resource_filenames]
- @cookbook_version.provider_filenames = @cookbook[:provider_filenames]
- @cookbook_version.root_filenames = @cookbook[:root_filenames]
- @cookbook_version.metadata_filenames = @cookbook[:metadata_filenames]
- end
-
- it "generates a manifest containing the cookbook's files" do
- manifest = @cookbook_version.manifest
-
- expect(manifest["metadata"]).to eq(Chef::Cookbook::Metadata.new)
- expect(manifest["cookbook_name"]).to eq("blarghle")
-
- expect(manifest["recipes"].size).to eq(1)
-
- recipe = manifest["recipes"].first
- expect(recipe["name"]).to eq("default.rb")
- expect(recipe["path"]).to eq("recipes/default.rb")
- expect(recipe["checksum"]).to match(MD5)
- expect(recipe["specificity"]).to eq("default")
-
- expect(manifest["definitions"].size).to eq(1)
-
- definition = manifest["definitions"].first
- expect(definition["name"]).to eq("runit_service.rb")
- expect(definition["path"]).to eq("definitions/runit_service.rb")
- expect(definition["checksum"]).to match(MD5)
- expect(definition["specificity"]).to eq("default")
-
- expect(manifest["libraries"].size).to eq(1)
-
- library = manifest["libraries"].first
- expect(library["name"]).to eq("ownage.rb")
- expect(library["path"]).to eq("libraries/ownage.rb")
- expect(library["checksum"]).to match(MD5)
- expect(library["specificity"]).to eq("default")
-
- expect(manifest["attributes"].size).to eq(1)
-
- attribute_file = manifest["attributes"].first
- expect(attribute_file["name"]).to eq("default.rb")
- expect(attribute_file["path"]).to eq("attributes/default.rb")
- expect(attribute_file["checksum"]).to match(MD5)
- expect(attribute_file["specificity"]).to eq("default")
-
- expect(manifest["files"].size).to eq(1)
-
- cookbook_file = manifest["files"].first
- expect(cookbook_file["name"]).to eq("giant_blob.tgz")
- expect(cookbook_file["path"]).to eq("files/default/giant_blob.tgz")
- expect(cookbook_file["checksum"]).to match(MD5)
- expect(cookbook_file["specificity"]).to eq("default")
-
- expect(manifest["templates"].size).to eq(1)
-
- template = manifest["templates"].first
- expect(template["name"]).to eq("configuration.erb")
- expect(template["path"]).to eq("templates/default/configuration.erb")
- expect(template["checksum"]).to match(MD5)
- expect(template["specificity"]).to eq("default")
-
- expect(manifest["resources"].size).to eq(1)
-
- lwr = manifest["resources"].first
- expect(lwr["name"]).to eq("lwr.rb")
- expect(lwr["path"]).to eq("resources/lwr.rb")
- expect(lwr["checksum"]).to match(MD5)
- expect(lwr["specificity"]).to eq("default")
-
- expect(manifest["providers"].size).to eq(1)
-
- lwp = manifest["providers"].first
- expect(lwp["name"]).to eq("lwp.rb")
- expect(lwp["path"]).to eq("providers/lwp.rb")
- expect(lwp["checksum"]).to match(MD5)
- expect(lwp["specificity"]).to eq("default")
-
- expect(manifest["root_files"].size).to eq(1)
-
- readme = manifest["root_files"].first
- expect(readme["name"]).to eq("README.rdoc")
- expect(readme["path"]).to eq("README.rdoc")
- expect(readme["checksum"]).to match(MD5)
- expect(readme["specificity"]).to eq("default")
- end
- end
end
describe 'with a cookbook directory named cookbook2 that has unscoped files' do
@@ -499,8 +302,63 @@ describe Chef::CookbookVersion do
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
- let(:jsonable) { Chef::CookbookVersion.new("tatft", '/tmp/blah') }
+ describe "when deprecation warnings are errors" do
+
+ subject(:cbv) { Chef::CookbookVersion.new("version validation", '/tmp/blah') }
+
+ describe "HTTP Resource behaviors", pending: "will be deprected when CookbookManifest API is stablized" do
+
+ it "errors on #save_url" do
+ expect { cbv.save_url }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ end
+
+ it "errors on #force_save_url" do
+ expect { cbv.force_save_url }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ end
+
+ it "errors on #to_hash" do
+ expect { cbv.to_hash }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ end
+
+ it "errors on #to_json" do
+ expect { cbv.to_json }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ end
+
+ end
+
+ it "errors on #status and #status=" do
+ expect { cbv.status = :wat }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ expect { cbv.status }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ end
+
end
+ describe "deprecated features" do
+
+ subject(:cbv) { Chef::CookbookVersion.new("tatft", '/tmp/blah').tap { |c| c.version = "1.2.3" } }
+
+ before do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
+
+ it "gives a save URL for the standard cookbook API" do
+ expect(cbv.save_url).to eq("cookbooks/tatft/1.2.3")
+ end
+
+ it "gives a force save URL for the standard cookbook API" do
+ expect(cbv.force_save_url).to eq("cookbooks/tatft/1.2.3?force=true")
+ end
+
+ it "is \"ready\"" do
+ # WTF is this? what are the valid states? and why aren't they set with encapsulating methods?
+ # [Dan 15-Jul-2010]
+ expect(cbv.status).to eq(:ready)
+ end
+
+
+ include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ let(:jsonable) { Chef::CookbookVersion.new("tatft", '/tmp/blah') }
+ end
+
+ end
end