class Chef::Cookbook::Metadata

Chef::Cookbook::Metadata

Chef::Cookbook::Metadata provides a convenient DSL for declaring metadata about Chef Cookbooks.

Constants

ATTRIBUTES
COMPARISON_FIELDS
CONFLICTING
DEPENDENCIES
DESCRIPTION
GROUPINGS
LICENSE
LONG_DESCRIPTION
MAINTAINER
MAINTAINER_EMAIL
NAME
PLATFORMS
PROVIDING
RECIPES
RECOMMENDATIONS
REPLACING
SUGGESTIONS
VERSION
VERSION_CONSTRAINTS

Attributes

attributes[R]
conflicting[R]
cookbook[R]
dependencies[R]
groupings[R]
platforms[R]
providing[R]
recipes[R]
recommendations[R]
replacing[R]
suggestions[R]
version[R]

Public Class Methods

from_hash(o) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 447
def self.from_hash(o)
  cm = self.new()
  cm.from_hash(o)
  cm
end
from_json(string) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 474
def self.from_json(string)
  o = Chef::JSONCompat.from_json(string)
  self.from_hash(o)
end
new(cookbook=nil, maintainer='YOUR_COMPANY_NAME', maintainer_email='YOUR_EMAIL', license='none') click to toggle source

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 92
def initialize(cookbook=nil, maintainer='YOUR_COMPANY_NAME', maintainer_email='YOUR_EMAIL', license='none')
  @cookbook = cookbook
  @name = cookbook ? cookbook.name : ""
  @long_description = ""
  self.maintainer(maintainer)
  self.maintainer_email(maintainer_email)
  self.license(license)
  self.description('A fabulous new cookbook')
  @platforms = Mash.new
  @dependencies = Mash.new
  @recommendations = Mash.new
  @suggestions = Mash.new
  @conflicting = Mash.new
  @providing = Mash.new
  @replacing = Mash.new
  @attributes = Mash.new
  @groupings = Mash.new
  @recipes = Mash.new
  @version = Version.new "0.0.0"
  if cookbook
    @recipes = cookbook.fully_qualified_recipe_names.inject({}) do |r, e|
      e = self.name.to_s if e =~ /::default$/
      r[e] ||= ""
      self.provides e
      r
    end
  end
end
validate_json(json_str) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 479
def self.validate_json(json_str)
  o = Chef::JSONCompat.from_json(json_str)
  metadata = new()
  VERSION_CONSTRAINTS.each do |method_name, hash_key|
    if constraints = o[hash_key]
     constraints.each do |cb_name, constraints|
       metadata.send(method_name, cb_name, *Array(constraints))
     end
    end
  end
  true
end

Public Instance Methods

==(other) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 121
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
attribute(name, options) click to toggle source

Adds an attribute )hat a user needs to configure for this cookbook. Takes a name (with the / notation for a nested attribute), followed by any of these options

display_name<String>:: What a UI should show for this attribute
description<String>:: A hint as to what this attr is for
choice<Array>:: An array of choices to present to the user.
calculated<Boolean>:: If true, the default value is calculated by the recipe and cannot be displayed.
type<String>:: "string" or "array" - default is "string"  ("hash" is supported for backwards compatibility)
required<String>:: Whether this attr is 'required', 'recommended' or 'optional' - default 'optional' (true/false values also supported for backwards compatibility)
recipes<Array>:: An array of recipes which need this attr set.
default<String>,<Array>,<Hash>:: The default value

Parameters

name<String>

The name of the attribute ('foo', or 'apache2/log_dir')

options<Hash>

The description of the options

Returns

options<Hash>

Returns the current options hash

# File lib/chef/cookbook/metadata.rb, line 386
def attribute(name, options)
  validate(
    options,
    {
      :display_name => { :kind_of => String },
      :description => { :kind_of => String },
      :choice => { :kind_of => [ Array ], :default => [] },
      :calculated => { :equal_to => [ true, false ], :default => false },
      :type => { :equal_to => [ "string", "array", "hash", "symbol", "boolean", "numeric" ], :default => "string" },
      :required => { :equal_to => [ "required", "recommended", "optional", true, false ], :default => "optional" },
      :recipes => { :kind_of => [ Array ], :default => [] },
      :default => { :kind_of => [ String, Array, Hash, Symbol, Numeric, TrueClass, FalseClass ] }
    }
  )
  options[:required] = remap_required_attribute(options[:required]) unless options[:required].nil?
  validate_choice_array(options)
  validate_calculated_default_rule(options)
  validate_choice_default_rule(options)

  @attributes[name] = options
  @attributes[name]
end
conflicts(cookbook, *version_args) click to toggle source

Adds a conflict for 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 311
def conflicts(cookbook, *version_args)
  version = new_args_format(:conflicts, cookbook, version_args)
  normalized_version = normalize_version_constraint(:conflicts, cookbook, version)
  @conflicting[cookbook] = normalized_version
  @conflicting[cookbook]
end
depends(cookbook, *version_args) click to toggle source

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 260
def depends(cookbook, *version_args)
  version = new_args_format(:depends, cookbook, version_args)
  normalized_version = normalize_version_constraint(:depends, cookbook, version)
  @dependencies[cookbook] = normalized_version
  @dependencies[cookbook]
end
description(arg=nil) click to toggle source

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 179
def description(arg=nil)
  set_or_return(
    :description,
    arg,
    :kind_of => [ String ]
  )
end
from_hash(o) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 453
def from_hash(o)
  @name                         = o[NAME] if o.has_key?(NAME)
  @description                  = o[DESCRIPTION] if o.has_key?(DESCRIPTION)
  @long_description             = o[LONG_DESCRIPTION] if o.has_key?(LONG_DESCRIPTION)
  @maintainer                   = o[MAINTAINER] if o.has_key?(MAINTAINER)
  @maintainer_email             = o[MAINTAINER_EMAIL] if o.has_key?(MAINTAINER_EMAIL)
  @license                      = o[LICENSE] if o.has_key?(LICENSE)
  @platforms                    = o[PLATFORMS] if o.has_key?(PLATFORMS)
  @dependencies                 = handle_deprecated_constraints(o[DEPENDENCIES]) if o.has_key?(DEPENDENCIES)
  @recommendations              = handle_deprecated_constraints(o[RECOMMENDATIONS]) if o.has_key?(RECOMMENDATIONS)
  @suggestions                  = handle_deprecated_constraints(o[SUGGESTIONS]) if o.has_key?(SUGGESTIONS)
  @conflicting                  = handle_deprecated_constraints(o[CONFLICTING]) if o.has_key?(CONFLICTING)
  @providing                    = o[PROVIDING] if o.has_key?(PROVIDING)
  @replacing                    = handle_deprecated_constraints(o[REPLACING]) if o.has_key?(REPLACING)
  @attributes                   = o[ATTRIBUTES] if o.has_key?(ATTRIBUTES)
  @groupings                    = o[GROUPINGS] if o.has_key?(GROUPINGS)
  @recipes                      = o[RECIPES] if o.has_key?(RECIPES)
  @version                      = o[VERSION] if o.has_key?(VERSION)
  self
end
from_json(string) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 492
def from_json(string)
  o = Chef::JSONCompat.from_json(string)
  from_hash(o)
end
grouping(name, options) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 409
def grouping(name, options)
  validate(
    options,
    {
      :title => { :kind_of => String },
      :description => { :kind_of => String }
    }
  )
  @groupings[name] = options
  @groupings[name]
end
license(arg=nil) click to toggle source

Sets the current license, or returns it.

Parameters

license<String>

The current license.

Returns

license<String>

Returns the current license

# File lib/chef/cookbook/metadata.rb, line 164
def license(arg=nil)
  set_or_return(
    :license,
    arg,
    :kind_of => [ String ]
  )
end
long_description(arg=nil) click to toggle source

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 194
def long_description(arg=nil)
  set_or_return(
    :long_description,
    arg,
    :kind_of => [ String ]
  )
end
maintainer(arg=nil) click to toggle source

Sets the cookbooks maintainer, or returns it.

Parameters

maintainer<String>

The maintainers name

Returns

maintainer<String>

Returns the current maintainer.

# File lib/chef/cookbook/metadata.rb, line 134
def maintainer(arg=nil)
  set_or_return(
    :maintainer,
    arg,
    :kind_of => [ String ]
  )
end
maintainer_email(arg=nil) click to toggle source

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 149
def maintainer_email(arg=nil)
  set_or_return(
    :maintainer_email,
    arg,
    :kind_of => [ String ]
  )
end
name(arg=nil) click to toggle source

Sets the name of the cookbook, or returns it.

Parameters

name<String>

The curent cookbook name.

Returns

name<String>

Returns the current cookbook name.

# File lib/chef/cookbook/metadata.rb, line 225
def name(arg=nil)
  set_or_return(
    :name,
    arg,
    :kind_of => [ String ]
  )
end
provides(cookbook, *version_args) click to toggle source

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 332
def provides(cookbook, *version_args)
  version = new_args_format(:provides, cookbook, version_args)
  normalized_version = normalize_version_constraint(:provides, cookbook, version)
  @providing[cookbook] = normalized_version
  @providing[cookbook]
end
recipe(name, description) click to toggle source

Adds a description for a recipe.

Parameters

recipe<String>

The recipe

description<String>

The description of the recipe

Returns

description<String>

Returns the current description

# File lib/chef/cookbook/metadata.rb, line 363
def recipe(name, description)
  @recipes[name] = description
end
recommends(cookbook, *version_args) click to toggle source

Adds a recommendation for 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 277
def recommends(cookbook, *version_args)
  version = new_args_format(:recommends, cookbook,  version_args)
  normalized_version = normalize_version_constraint(:recommends, cookbook, version)
  @recommendations[cookbook] = normalized_version
  @recommendations[cookbook]
end
replaces(cookbook, *version_args) click to toggle source

Adds a cookbook that is replaced by this one, with version checking strings.

Parameters

cookbook<String>

The cookbook we replace

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 348
def replaces(cookbook, *version_args)
  version = new_args_format(:replaces, cookbook, version_args)
  normalized_version = normalize_version_constraint(:replaces, cookbook, version)
  @replacing[cookbook] = normalized_version
  @replacing[cookbook]
end
suggests(cookbook, *version_args) click to toggle source

Adds a suggestion for 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 formx.y.z or x.y.

Returns

versions<Array>

Returns the list of versions for the platform

# File lib/chef/cookbook/metadata.rb, line 294
def suggests(cookbook, *version_args)
  version = new_args_format(:suggests, cookbook, version_args)
  normalized_version = normalize_version_constraint(:suggests, cookbook, version)
  @suggestions[cookbook] = normalized_version
  @suggestions[cookbook]
end
supports(platform, *version_args) click to toggle source

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 243
def supports(platform, *version_args)
  version = new_args_format(:supports, platform, version_args)
  normalized_version = normalize_version_constraint(:supports, platform, version)
  @platforms[platform] = normalized_version
  @platforms[platform]
end
to_hash() click to toggle source
# File lib/chef/cookbook/metadata.rb, line 421
def to_hash
  {
    NAME             => self.name,
    DESCRIPTION      => self.description,
    LONG_DESCRIPTION => self.long_description,
    MAINTAINER       => self.maintainer,
    MAINTAINER_EMAIL => self.maintainer_email,
    LICENSE          => self.license,
    PLATFORMS        => self.platforms,
    DEPENDENCIES     => self.dependencies,
    RECOMMENDATIONS  => self.recommendations,
    SUGGESTIONS      => self.suggestions,
    CONFLICTING      => self.conflicting,
    PROVIDING        => self.providing,
    REPLACING        => self.replacing,
    ATTRIBUTES       => self.attributes,
    GROUPINGS        => self.groupings,
    RECIPES          => self.recipes,
    VERSION          => self.version
  }
end
to_json(*a) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 443
def to_json(*a)
  self.to_hash.to_json(*a)
end

Private Instance Methods

handle_deprecated_constraints(specification) click to toggle source

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 634
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
new_args_format(caller_name, dep_name, version_constraints) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 499
      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 http://wiki.opscode.com/display/chef/Metadata 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
normalize_version_constraint(caller_name, dep_name, constraint_str) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 536
def normalize_version_constraint(caller_name, dep_name, constraint_str)
  version_constraint = validate_version_constraint(caller_name, dep_name, constraint_str)
  "#{version_constraint.op} #{version_constraint.raw_version}"
end
remap_required_attribute(value) click to toggle source

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 592
def remap_required_attribute(value)
  case value
  when true
    value = "required"
  when false
    value = "optional"
  end
  value
end
validate_calculated_default_rule(options) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 602
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_choice_array(opts) click to toggle source

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 560
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
validate_choice_default_rule(options) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 609
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
validate_string_array(arry) click to toggle source

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 547
def validate_string_array(arry)
  if arry.kind_of?(Array)
    arry.each do |choice|
      validate( {:choice => choice}, {:choice => {:kind_of => String}} )
    end
  end
end
validate_version_constraint(caller_name, dep_name, constraint_str) click to toggle source
# File lib/chef/cookbook/metadata.rb, line 518
      def validate_version_constraint(caller_name, dep_name, constraint_str)
        Chef::VersionConstraint.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 http://wiki.opscode.com/display/chef/Metadata 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