class Chef::Cookbook::Metadata
Chef::Cookbook::Metadata
¶ ↑
Chef::Cookbook::Metadata
provides a convenient DSL
for declaring metadata about Chef
Cookbooks.
Constants
- CHEF_VERSIONS
- COMPARISON_FIELDS
- DEPENDENCIES
- DESCRIPTION
- GEMS
- ISSUES_URL
- LICENSE
- LONG_DESCRIPTION
- MAINTAINER
- MAINTAINER_EMAIL
- NAME
- OHAI_VERSIONS
- PLATFORMS
- PRIVACY
- PROVIDING
- RECIPES
- SOURCE_URL
- VERSION
- VERSION_CONSTRAINTS
Attributes
@return [Array<Gem::Dependency>] Array of supported Chef
versions
@return [Array<Array>] Array of gems to install with *args as an Array
@return [Array<Gem::Dependency>] Array of supported Ohai versions
Public Class Methods
# File lib/chef/cookbook/metadata.rb, line 446 def self.from_hash(o) cm = new() cm.from_hash(o) cm end
# File lib/chef/cookbook/metadata.rb, line 473 def self.from_json(string) o = Chef::JSONCompat.from_json(string) from_hash(o) end
Builds a new Chef::Cookbook::Metadata
object.
Parameters¶ ↑
- cookbook<String>
-
An optional cookbook object
- maintainer<String>
-
An optional maintainer
maintainer_email
<String>-
An optional maintainer email
license<String>::An optional license. Default is Apache v2.0
Returns¶ ↑
metadata<Chef::Cookbook::Metadata>
# File lib/chef/cookbook/metadata.rb, line 91 def initialize @name = nil @description = "" @long_description = "" @license = "All rights reserved" @maintainer = "" @maintainer_email = "" @platforms = Mash.new @dependencies = Mash.new @providing = Mash.new @recipes = Mash.new @version = Version.new("0.0.0") @source_url = "" @issues_url = "" @privacy = false @chef_versions = [] @ohai_versions = [] @gems = [] @errors = [] end
# File lib/chef/cookbook/metadata.rb, line 478 def self.validate_json(json_str) o = Chef::JSONCompat.from_json(json_str) metadata = new() VERSION_CONSTRAINTS.each do |dependency_type, hash_key| if dependency_group = o[hash_key] dependency_group.each do |cb_name, constraints| if metadata.respond_to?(dependency_type) metadata.public_send(dependency_type, cb_name, *Array(constraints)) end end end end true end
Public Instance Methods
# File lib/chef/cookbook/metadata.rb, line 116 def ==(other) COMPARISON_FIELDS.inject(true) do |equal_so_far, field| equal_so_far && other.respond_to?(field) && (other.send(field) == send(field)) end 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
# File lib/chef/cookbook/metadata.rb, line 319 def chef_version(*version_args) @chef_versions << Gem::Dependency.new("chef", *version_args) unless version_args.empty? @chef_versions end
Adds a dependency on another cookbook, with version checking strings.
Parameters¶ ↑
- cookbook<String>
-
The cookbook
- version<String>
-
A version constraint of the form “OP VERSION”,
where OP is one of < <= = > >= ~> and VERSION
has the form x.y.z or x.y.
Returns¶ ↑
- versions<Array>
-
Returns the list of versions for the platform
# File lib/chef/cookbook/metadata.rb, line 281 def depends(cookbook, *version_args) if cookbook == name raise "Cookbook depends on itself in cookbook #{name}, please remove the this unnecessary self-dependency" else version = new_args_format(:depends, cookbook, version_args) constraint = validate_version_constraint(:depends, cookbook, version) @dependencies[cookbook] = constraint.to_s end @dependencies[cookbook] end
Sets the current description, or returns it. Should be short - one line only!
Parameters¶ ↑
- description<String>
-
The new description
Returns¶ ↑
- description<String>
-
Returns the description
# File lib/chef/cookbook/metadata.rb, line 200 def description(arg = nil) set_or_return( :description, arg, kind_of: [ String ] ) end
A list of validation errors for this metadata object. See valid?
for comments about the validation criteria.
If there are any validation errors, one or more error strings will be returned. Otherwise an empty array is returned.
Returns¶ ↑
- error messages<Array>
-
Whether this metadata object is valid
# File lib/chef/cookbook/metadata.rb, line 143 def errors run_validation @errors end
# File lib/chef/cookbook/metadata.rb, line 452 def from_hash(o) @name = o[NAME] if o.key?(NAME) @description = o[DESCRIPTION] if o.key?(DESCRIPTION) @long_description = o[LONG_DESCRIPTION] if o.key?(LONG_DESCRIPTION) @maintainer = o[MAINTAINER] if o.key?(MAINTAINER) @maintainer_email = o[MAINTAINER_EMAIL] if o.key?(MAINTAINER_EMAIL) @license = o[LICENSE] if o.key?(LICENSE) @platforms = o[PLATFORMS] if o.key?(PLATFORMS) @dependencies = handle_deprecated_constraints(o[DEPENDENCIES]) if o.key?(DEPENDENCIES) @providing = o[PROVIDING] if o.key?(PROVIDING) @recipes = o[RECIPES] if o.key?(RECIPES) @version = o[VERSION] if o.key?(VERSION) @source_url = o[SOURCE_URL] if o.key?(SOURCE_URL) @issues_url = o[ISSUES_URL] if o.key?(ISSUES_URL) @privacy = o[PRIVACY] if o.key?(PRIVACY) @chef_versions = gem_requirements_from_array("chef", o[CHEF_VERSIONS]) if o.key?(CHEF_VERSIONS) @ohai_versions = gem_requirements_from_array("ohai", o[OHAI_VERSIONS]) if o.key?(OHAI_VERSIONS) @gems = o[GEMS] if o.key?(GEMS) self end
# File lib/chef/cookbook/metadata.rb, line 493 def from_json(string) o = Chef::JSONCompat.from_json(string) from_hash(o) end
Metadata
DSL
to set a gem to install from the cookbook metadata. May be declared multiple times. All the gems from all the cookbooks are combined into one Gemfile and depsolved together. Uses Bundler's DSL
for its implementation.
@param args [Array<String>] Gem name and options to pass to Bundler's DSL
@return [Array<Array>] Array of gem statements as args
# File lib/chef/cookbook/metadata.rb, line 341 def gem(*args) @gems << args unless args.empty? @gems 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
# File lib/chef/cookbook/metadata.rb, line 412 def gem_requirements_from_array(what, array) array.map do |dep| Gem::Dependency.new(what, *dep) end 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)
# File lib/chef/cookbook/metadata.rb, line 397 def gem_requirements_to_array(*deps) deps.map do |dep| dep.requirement.requirements.map do |op, version| "#{op} #{version}" end.sort end end
Sets the cookbook's issues URL, or returns it.
Parameters¶ ↑
issues_url
<String>-
The issues URL
Returns¶ ↑
issues_url
<String>-
Returns the current issues URL.
# File lib/chef/cookbook/metadata.rb, line 520 def issues_url(arg = nil) set_or_return( :issues_url, arg, kind_of: [ String ] ) end
Sets the current long description, or returns it. Might come from a README, say.
Parameters¶ ↑
long_description
<String>-
The new long description
Returns¶ ↑
long_description
<String>-
Returns the long description
# File lib/chef/cookbook/metadata.rb, line 215 def long_description(arg = nil) set_or_return( :long_description, arg, kind_of: [ String ] ) end
Sets the maintainers email address, or returns it.
Parameters¶ ↑
maintainer_email
<String>-
The maintainers email address
Returns¶ ↑
maintainer_email
<String>-
Returns the current maintainer email.
# File lib/chef/cookbook/metadata.rb, line 170 def maintainer_email(arg = nil) set_or_return( :maintainer_email, arg, kind_of: [ String ] ) end
# File lib/chef/cookbook/metadata.rb, line 565 def method_missing(method, *args, &block) if block_given? super else Chef::Log.trace "ignoring method #{method} on cookbook with name #{name}, possible typo or the ghosts of metadata past or future?" end 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
# File lib/chef/cookbook/metadata.rb, line 330 def ohai_version(*version_args) @ohai_versions << Gem::Dependency.new("ohai", *version_args) unless version_args.empty? @ohai_versions end
Sets the cookbook's privacy flag, or returns it.
Parameters¶ ↑
- privacy<TrueClass,FalseClass>
-
Whether this cookbook is private or not
Returns¶ ↑
- privacy<TrueClass,FalseClass>
-
Whether this cookbook is private or not
# File lib/chef/cookbook/metadata.rb, line 537 def privacy(arg = nil) set_or_return( :privacy, arg, kind_of: [ TrueClass, FalseClass ] ) end
Adds a recipe, definition, or resource provided by this cookbook.
Recipes are specified as normal Definitions are followed by (), and can include :params for prototyping Resources are the stringified version (service)
Parameters¶ ↑
- recipe, definition, resource<String>
-
The thing we provide
- version<String>
-
A version constraint of the form “OP VERSION”,
where OP is one of < <= = > >= ~> and VERSION
has the form x.y.z or x.y.
Returns¶ ↑
- versions<Array>
-
Returns the list of versions for the platform
# File lib/chef/cookbook/metadata.rb, line 306 def provides(cookbook, *version_args) version = new_args_format(:provides, cookbook, version_args) constraint = validate_version_constraint(:provides, cookbook, version) @providing[cookbook] = constraint.to_s @providing[cookbook] end
Sets the cookbook's recipes to the list of recipes in the given cookbook
. Any recipe that already has a description (if set by the recipe
method) will not be updated.
Parameters¶ ↑
- cookbook<CookbookVersion>
-
CookbookVersion
object representing the cookbook - description<String>
-
The description of the recipe
Returns¶ ↑
- recipe_unqualified_names<Array>
-
An array of the recipe names given by the cookbook
# File lib/chef/cookbook/metadata.rb, line 368 def recipes_from_cookbook_version(cookbook) cookbook.fully_qualified_recipe_names.map do |recipe_name| unqualified_name = if recipe_name =~ /::default$/ name.to_s else recipe_name end @recipes[unqualified_name] ||= "" provides(unqualified_name) unqualified_name end end
Sets the cookbook's source URL, or returns it.
Parameters¶ ↑
- maintainer<String>
-
The source URL
Returns¶ ↑
source_url
<String>-
Returns the current source URL.
# File lib/chef/cookbook/metadata.rb, line 505 def source_url(arg = nil) set_or_return( :source_url, arg, kind_of: [ String ] ) end
Adds a supported platform, with version checking strings.
Parameters¶ ↑
- platform<String>,<Symbol>
-
The platform (like :ubuntu or :mac_os_x)
- version<String>
-
A version constraint of the form “OP VERSION”,
where OP is one of < <= = > >= ~> and VERSION
has the form x.y.z or x.y.
Returns¶ ↑
- versions<Array>
-
Returns the list of versions for the platform
# File lib/chef/cookbook/metadata.rb, line 264 def supports(platform, *version_args) version = new_args_format(:supports, platform, version_args) constraint = validate_version_constraint(:supports, platform, version) @platforms[platform] = constraint.to_s @platforms[platform] end
# File lib/chef/cookbook/metadata.rb, line 418 def to_h { NAME => name, DESCRIPTION => description, LONG_DESCRIPTION => long_description, MAINTAINER => maintainer, MAINTAINER_EMAIL => maintainer_email, LICENSE => license, PLATFORMS => platforms, DEPENDENCIES => dependencies, PROVIDING => providing, RECIPES => recipes, VERSION => version, SOURCE_URL => source_url, ISSUES_URL => issues_url, PRIVACY => privacy, CHEF_VERSIONS => gem_requirements_to_array(*chef_versions), OHAI_VERSIONS => gem_requirements_to_array(*ohai_versions), GEMS => gems, } end
# File lib/chef/cookbook/metadata.rb, line 442 def to_json(*a) Chef::JSONCompat.to_json(to_h, *a) end
Whether this metadata is valid. In order to be valid, all required fields must be set. Chef's validation implementation checks the content of a given field when setting (and raises an error if the content does not meet the criteria), so the content of the fields is not considered when checking validity.
Returns¶ ↑
- valid<Boolean>
-
Whether this metadata object is valid
# File lib/chef/cookbook/metadata.rb, line 130 def valid? run_validation @errors.empty? end
Validates that the Chef::VERSION of the running chef-client matches one of the configured chef_version
statements in this cookbooks metadata.
@raise [Chef::Exceptions::CookbookChefVersionMismatch] if the cookbook fails validation
# File lib/chef/cookbook/metadata.rb, line 559 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
Validates that the Ohai::VERSION of the running chef-client matches one of the configured ohai_version
statements in this cookbooks metadata.
@raise [Chef::Exceptions::CookbookOhaiVersionMismatch] if the cookbook fails validation
# File lib/chef/cookbook/metadata.rb, line 549 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
Sets the current cookbook version, or returns it. Can be two or three digits, separated by dots. ie: '2.1', '1.5.4' or '0.9'.
Parameters¶ ↑
- version<String>
-
The current version, as a string
Returns¶ ↑
- version<String>
-
Returns the current version
# File lib/chef/cookbook/metadata.rb, line 231 def version(arg = nil) if arg @version = Chef::Version.new(arg) end @version.to_s end
Private Instance Methods
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
# File lib/chef/cookbook/metadata.rb, line 583 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
This method translates version constraint strings from cookbooks with the old format.
Before we began respecting version constraints, we allowed multiple constraints to be placed on cookbooks, as well as the << and >> operators, which are now just < and >. For specifications with more than one constraint, we return an empty array (otherwise, we're silently abiding only part of the contract they have specified to us). If there is only one constraint, we are replacing the old << and >> with the new < and >.
# File lib/chef/cookbook/metadata.rb, line 726 def handle_deprecated_constraints(specification) specification.inject(Mash.new) do |acc, (cb, constraints)| constraints = Array(constraints) acc[cb] = (constraints.empty? || constraints.size > 1) ? [] : constraints.first.gsub(/>>/, ">").gsub(/<</, "<") acc end end
# File lib/chef/cookbook/metadata.rb, line 596 def new_args_format(caller_name, dep_name, version_constraints) if version_constraints.empty? ">= 0.0.0" elsif version_constraints.size == 1 version_constraints.first else msg = <<~OBSOLETED The dependency specification syntax you are using is no longer valid. You may not specify more than one version constraint for a particular cookbook. Consult https://docs.chef.io/config_rb_metadata.html for the updated syntax. Called by: #{caller_name} '#{dep_name}', #{version_constraints.map { |vc| vc.inspect }.join(", ")} Called from: #{caller[0...5].map { |line| " " + line }.join("\n")} OBSOLETED raise Exceptions::ObsoleteDependencySyntax, msg end end
For backwards compatibility, remap Boolean values to String
true is mapped to "required" false is mapped to "optional"
Parameters¶ ↑
- required_attr<String><Boolean>
-
The value of options
Returns¶ ↑
- required_attr<String>
-
“required”, “recommended”, or “optional”
# File lib/chef/cookbook/metadata.rb, line 684 def remap_required_attribute(value) case value when true value = "required" when false value = "optional" end value end
# File lib/chef/cookbook/metadata.rb, line 590 def run_validation if name.nil? @errors = ["The `name' attribute is required in cookbook metadata"] end end
# File lib/chef/cookbook/metadata.rb, line 694 def validate_calculated_default_rule(options) calculated_conflict = ((options[:default].is_a?(Array) && !options[:default].empty?) || (options[:default].is_a?(String) && !options[:default] != "")) && options[:calculated] == true raise ArgumentError, "Default cannot be specified if calculated is true!" if calculated_conflict end
Validate the choice of the options hash
Raise an exception if the members of the array do not match the defaults
Parameters¶ ↑
- opts<Hash>
-
The options hash
# File lib/chef/cookbook/metadata.rb, line 652 def validate_choice_array(opts) if opts[:choice].kind_of?(Array) case opts[:type] when "string" validator = [ String ] when "array" validator = [ Array ] when "hash" validator = [ Hash ] when "symbol" validator = [ Symbol ] when "boolean" validator = [ TrueClass, FalseClass ] when "numeric" validator = [ Numeric ] end opts[:choice].each do |choice| validate( { choice: choice }, { choice: { kind_of: validator } } ) end end end
# File lib/chef/cookbook/metadata.rb, line 701 def validate_choice_default_rule(options) return if !options[:choice].is_a?(Array) || options[:choice].empty? if options[:default].is_a?(String) && options[:default] != "" raise ArgumentError, "Default must be one of your choice values!" if options[:choice].index(options[:default]).nil? end if options[:default].is_a?(Array) && !options[:default].empty? options[:default].each do |val| raise ArgumentError, "Default values must be a subset of your choice values!" if options[:choice].index(val).nil? end end end
Verify that the given array is an array of strings
Raise an exception if the members of the array are not Strings
Parameters¶ ↑
- arry<Array>
-
An array to be validated
# File lib/chef/cookbook/metadata.rb, line 639 def validate_string_array(arry) if arry.kind_of?(Array) arry.each do |choice| validate( { choice: choice }, { choice: { kind_of: String } } ) end end end
# File lib/chef/cookbook/metadata.rb, line 615 def validate_version_constraint(caller_name, dep_name, constraint_str) Chef::VersionConstraint::Platform.new(constraint_str) rescue Chef::Exceptions::InvalidVersionConstraint => e Log.debug(e) msg = <<~INVALID The version constraint syntax you are using is not valid. If you recently upgraded to Chef 0.10.0, be aware that you no may longer use "<<" and ">>" for 'less than' and 'greater than'; use '<' and '>' instead. Consult https://docs.chef.io/config_rb_metadata.html for more information. Called by: #{caller_name} '#{dep_name}', '#{constraint_str}' Called from: #{caller[0...5].map { |line| " " + line }.join("\n")} INVALID raise Exceptions::InvalidVersionConstraint, msg end