diff options
author | Lamont Granquist <lamont@scriptkiddie.org> | 2015-10-28 10:33:14 -0700 |
---|---|---|
committer | Lamont Granquist <lamont@scriptkiddie.org> | 2015-10-28 10:33:14 -0700 |
commit | 6a306c0847d2738dcc3656318ebccff5888fe25c (patch) | |
tree | 890ef90159b4d5262314d5111fe05e1c5afd088d /lib | |
parent | cc51b2f0504a32d7e55d2c445f22baf9ed30b031 (diff) | |
parent | a75ce7ce4c5ee9565f536430244a37bc33cd4e59 (diff) | |
download | chef-6a306c0847d2738dcc3656318ebccff5888fe25c.tar.gz |
Merge pull request #4081 from chef/lcg/chef-version
RFC-037: add chef_version and ohai_version metadata
Diffstat (limited to 'lib')
-rw-r--r-- | lib/chef/cookbook/cookbook_collection.rb | 15 | ||||
-rw-r--r-- | lib/chef/cookbook/metadata.rb | 124 | ||||
-rw-r--r-- | lib/chef/exceptions.rb | 16 | ||||
-rw-r--r-- | lib/chef/policy_builder/expand_node_object.rb | 2 | ||||
-rw-r--r-- | lib/chef/policy_builder/policyfile.rb | 1 |
5 files changed, 147 insertions, 11 deletions
diff --git a/lib/chef/cookbook/cookbook_collection.rb b/lib/chef/cookbook/cookbook_collection.rb index ae63abfc93..38784c22fa 100644 --- a/lib/chef/cookbook/cookbook_collection.rb +++ b/lib/chef/cookbook/cookbook_collection.rb @@ -1,7 +1,7 @@ #-- # Author:: Tim Hinderliter (<tim@opscode.com>) # Author:: Christopher Walters (<cw@opscode.com>) -# Copyright:: Copyright (c) 2010 Opscode, Inc. +# Copyright:: Copyright (c) 2010-2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,5 +41,18 @@ class Chef cookbook_versions.each{ |cookbook_name, cookbook_version| self[cookbook_name] = cookbook_version } end + # Validates that the cookbook metadata allows it to run on this instance. + # + # Currently checks chef_version and ohai_version in the cookbook metadata + # against the running Chef::VERSION and Ohai::VERSION. + # + # @raises [Chef::Exceptions::CookbookChefVersionMismatch] if the Chef::VERSION fails validation + # @raises [Chef::Exceptions::CookbookOhaiVersionMismatch] if the Ohai::VERSION fails validation + def validate! + each do |cookbook_name, cookbook_version| + cookbook_version.metadata.validate_chef_version! + cookbook_version.metadata.validate_ohai_version! + end + end end end diff --git a/lib/chef/cookbook/metadata.rb b/lib/chef/cookbook/metadata.rb index 9822920a7d..e9509be38c 100644 --- a/lib/chef/cookbook/metadata.rb +++ b/lib/chef/cookbook/metadata.rb @@ -2,7 +2,7 @@ # Author:: Adam Jacob (<adam@opscode.com>) # Author:: AJ Christensen (<aj@opscode.com>) # Author:: Seth Falcon (<seth@opscode.com>) -# Copyright:: Copyright 2008-2010 Opscode, Inc. +# Copyright:: Copyright 2008-2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -55,19 +55,23 @@ class Chef SOURCE_URL = 'source_url'.freeze ISSUES_URL = 'issues_url'.freeze PRIVACY = 'privacy'.freeze + CHEF_VERSIONS = 'chef_versions'.freeze + OHAI_VERSIONS = 'ohai_versions'.freeze COMPARISON_FIELDS = [ :name, :description, :long_description, :maintainer, :maintainer_email, :license, :platforms, :dependencies, :recommendations, :suggestions, :conflicting, :providing, :replacing, :attributes, :groupings, :recipes, :version, - :source_url, :issues_url, :privacy ] + :source_url, :issues_url, :privacy, :chef_versions, :ohai_versions ] - VERSION_CONSTRAINTS = {:depends => DEPENDENCIES, - :recommends => RECOMMENDATIONS, - :suggests => SUGGESTIONS, - :conflicts => CONFLICTING, - :provides => PROVIDING, - :replaces => REPLACING } + VERSION_CONSTRAINTS = {:depends => DEPENDENCIES, + :recommends => RECOMMENDATIONS, + :suggests => SUGGESTIONS, + :conflicts => CONFLICTING, + :provides => PROVIDING, + :replaces => REPLACING, + :chef_version => CHEF_VERSIONS, + :ohai_version => OHAI_VERSIONS } include Chef::Mixin::ParamsValidate include Chef::Mixin::FromFile @@ -84,6 +88,11 @@ class Chef attr_reader :recipes attr_reader :version + # @return [Array<Gem::Dependency>] Array of supported Chef versions + attr_reader :chef_versions + # @return [Array<Gem::Dependency>] Array of supported Ohai versions + attr_reader :ohai_versions + # Builds a new Chef::Cookbook::Metadata object. # # === Parameters @@ -118,6 +127,8 @@ class Chef @source_url = '' @issues_url = '' @privacy = false + @chef_versions = [] + @ohai_versions = [] @errors = [] end @@ -386,6 +397,28 @@ class Chef @replacing[cookbook] end + # Metadata DSL to set a valid chef_version. May be declared multiple times + # with the result being 'OR'd such that if any statements match, the version + # is considered supported. Uses Gem::Requirement for its implementation. + # + # @param version_args [Array<String>] Version constraint in String form + # @return [Array<Gem::Dependency>] Current chef_versions array + def chef_version(*version_args) + @chef_versions << Gem::Dependency.new('chef', *version_args) unless version_args.empty? + @chef_versions + end + + # Metadata DSL to set a valid ohai_version. May be declared multiple times + # with the result being 'OR'd such that if any statements match, the version + # is considered supported. Uses Gem::Requirement for its implementation. + # + # @param version_args [Array<String>] Version constraint in String form + # @return [Array<Gem::Dependency>] Current ohai_versions array + def ohai_version(*version_args) + @ohai_versions << Gem::Dependency.new('ohai', *version_args) unless version_args.empty? + @ohai_versions + end + # Adds a description for a recipe. # # === Parameters @@ -481,6 +514,40 @@ class Chef @groupings[name] end + # Convert an Array of Gem::Dependency objects (chef_version/ohai_version) to an Array. + # + # Gem::Dependencey#to_s is not useful, and there is no #to_json defined on it or its component + # objets, so we have to write our own rendering method. + # + # [ Gem::Dependency.new(">= 12.5"), Gem::Dependency.new(">= 11.18.0", "< 12.0") ] + # + # results in: + # + # [ [ ">= 12.5" ], [ ">= 11.18.0", "< 12.0" ] ] + # + # @param deps [Array<Gem::Dependency>] Multiple Gem-style version constraints + # @return [Array<Array<String>]] Simple object representation of version constraints (for json) + def gem_requirements_to_array(*deps) + deps.map do |dep| + dep.requirement.requirements.map do |op, version| + "#{op} #{version}" + end.sort + end + end + + # Convert an Array of Gem::Dependency objects (chef_version/ohai_version) to a hash. + # + # This is the inverse of #gem_requirements_to_array + # + # @param what [String] What version constraint we are constructing ('chef' or 'ohai' presently) + # @param array [Array<Array<String>]] Simple object representation of version constraints (from json) + # @return [Array<Gem::Dependency>] Multiple Gem-style version constraints + def gem_requirements_from_array(what, array) + array.map do |dep| + Gem::Dependency.new(what, *dep) + end + end + def to_hash { NAME => self.name, @@ -502,7 +569,9 @@ class Chef VERSION => self.version, SOURCE_URL => self.source_url, ISSUES_URL => self.issues_url, - PRIVACY => self.privacy + PRIVACY => self.privacy, + CHEF_VERSIONS => gem_requirements_to_array(*self.chef_versions), + OHAI_VERSIONS => gem_requirements_to_array(*self.ohai_versions) } end @@ -537,6 +606,8 @@ class Chef @source_url = o[SOURCE_URL] if o.has_key?(SOURCE_URL) @issues_url = o[ISSUES_URL] if o.has_key?(ISSUES_URL) @privacy = o[PRIVACY] if o.has_key?(PRIVACY) + @chef_versions = gem_requirements_from_array("chef", o[CHEF_VERSIONS]) if o.has_key?(CHEF_VERSIONS) + @ohai_versions = gem_requirements_from_array("ohai", o[OHAI_VERSIONS]) if o.has_key?(OHAI_VERSIONS) self end @@ -612,8 +683,43 @@ class Chef ) end + # Validates that the Ohai::VERSION of the running chef-client matches one of the + # configured ohai_version statements in this cookbooks metadata. + # + # @raises [Chef::Exceptions::CookbookOhaiVersionMismatch] if the cookbook fails validation + def validate_ohai_version! + unless gem_dep_matches?("ohai", Gem::Version.new(Ohai::VERSION), *ohai_versions) + raise Exceptions::CookbookOhaiVersionMismatch.new(Ohai::VERSION, name, version, *ohai_versions) + end + end + + # Validates that the Chef::VERSION of the running chef-client matches one of the + # configured chef_version statements in this cookbooks metadata. + # + # @raises [Chef::Exceptions::CookbookChefVersionMismatch] if the cookbook fails validation + def validate_chef_version! + unless gem_dep_matches?("chef", Gem::Version.new(Chef::VERSION), *chef_versions) + raise Exceptions::CookbookChefVersionMismatch.new(Chef::VERSION, name, version, *chef_versions) + end + end + private + # Helper to match a gem style version (ohai_version/chef_version) against a set of + # Gem::Dependency version constraints. If none are present, it always matches. if + # multiple are present, one must match. Returns false if none matches. + # + # @param what [String] the name of the constraint (e.g. 'chef' or 'ohai') + # @param version [String] the version to compare against the constraints + # @param deps [Array<Gem::Dependency>] Multiple Gem-style version constraints + # @return [Boolean] true if no constraints or a match, false if no match + def gem_dep_matches?(what, version, *deps) + # always match if we have no chef_version at all + return true unless deps.length > 0 + # match if we match any of the chef_version lines + deps.any? { |dep| dep.match?(what, version) } + end + def run_validation if name.nil? @errors = ["The `name' attribute is required in cookbook metadata"] diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index 855c86d9cc..8172311dd6 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -2,7 +2,7 @@ # Author:: Adam Jacob (<adam@opscode.com>) # Author:: Seth Falcon (<seth@opscode.com>) # Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>) -# Copyright:: Copyright 2008-2010 Opscode, Inc. +# Copyright:: Copyright 2008-2015 Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -482,6 +482,20 @@ class Chef end end + class CookbookChefVersionMismatch < RuntimeError + def initialize(chef_version, cookbook_name, cookbook_version, *constraints) + constraint_str = constraints.map { |c| c.requirement.as_list.to_s }.join(', ') + super "Cookbook '#{cookbook_name}' version '#{cookbook_version}' depends on chef version #{constraint_str}, but the running chef version is #{chef_version}" + end + end + + class CookbookOhaiVersionMismatch < RuntimeError + def initialize(ohai_version, cookbook_name, cookbook_version, *constraints) + constraint_str = constraints.map { |c| c.requirement.as_list.to_s }.join(', ') + super "Cookbook '#{cookbook_name}' version '#{cookbook_version}' depends on ohai version #{constraint_str}, but the running ohai version is #{ohai_version}" + end + end + class MultipleDscResourcesFound < RuntimeError attr_reader :resources_found def initialize(resources_found) diff --git a/lib/chef/policy_builder/expand_node_object.rb b/lib/chef/policy_builder/expand_node_object.rb index 2c6d644e42..848dd00684 100644 --- a/lib/chef/policy_builder/expand_node_object.rb +++ b/lib/chef/policy_builder/expand_node_object.rb @@ -74,11 +74,13 @@ class Chef cl = Chef::CookbookLoader.new(Chef::Config[:cookbook_path]) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) + cookbook_collection.validate! run_context = Chef::RunContext.new(node, cookbook_collection, @events) else Chef::Cookbook::FileVendor.fetch_from_remote(api_service) cookbook_hash = sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbook_hash) + cookbook_collection.validate! run_context = Chef::RunContext.new(node, cookbook_collection, @events) end diff --git a/lib/chef/policy_builder/policyfile.rb b/lib/chef/policy_builder/policyfile.rb index d6dcdf67b2..3633110d6c 100644 --- a/lib/chef/policy_builder/policyfile.rb +++ b/lib/chef/policy_builder/policyfile.rb @@ -153,6 +153,7 @@ class Chef Chef::Cookbook::FileVendor.fetch_from_remote(http_api) sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbooks_to_sync) + cookbook_collection.validate! run_context = Chef::RunContext.new(node, cookbook_collection, events) setup_chef_class(run_context) |