Class: Chef::Cookbook::Metadata

Inherits:
Object
  • Object
show all
Includes:
Mixin::FromFile, Mixin::ParamsValidate
Defined in:
lib/chef/cookbook/metadata.rb

Overview

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

Direct Known Subclasses

MinimalMetadata

Constant Summary collapse

NAME =
"name".freeze
DESCRIPTION =
"description".freeze
LONG_DESCRIPTION =
"long_description".freeze
MAINTAINER =
"maintainer".freeze
MAINTAINER_EMAIL =
"maintainer_email".freeze
LICENSE =
"license".freeze
PLATFORMS =
"platforms".freeze
DEPENDENCIES =
"dependencies".freeze
PROVIDING =
"providing".freeze
RECIPES =
"recipes".freeze
VERSION =
"version".freeze
SOURCE_URL =
"source_url".freeze
ISSUES_URL =
"issues_url".freeze
PRIVACY =
"privacy".freeze
CHEF_VERSIONS =
"chef_versions".freeze
OHAI_VERSIONS =
"ohai_versions".freeze
GEMS =
"gems".freeze
EAGER_LOAD_LIBRARIES =
"eager_load_libraries".freeze
COMPARISON_FIELDS =
%i{name description long_description maintainer
maintainer_email license platforms dependencies
providing recipes version source_url issues_url
privacy chef_versions ohai_versions gems
eager_load_libraries}.freeze
VERSION_CONSTRAINTS =
{ depends: DEPENDENCIES,
provides: PROVIDING,
chef_version: CHEF_VERSIONS,
ohai_version: OHAI_VERSIONS }.freeze

Instance Attribute Summary collapse

Attributes included from Mixin::FromFile

#source_file

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mixin::FromFile

#class_from_file, #from_file

Methods included from Mixin::ParamsValidate

#lazy, #set_or_return, #validate

Constructor Details

#initializeMetadata

Builds a new Chef::Cookbook::Metadata object.

=== Parameters cookbook:: An optional cookbook object maintainer:: An optional maintainer maintainer_email:: An optional maintainer email license::An optional license. Default is Apache v2.0

=== Returns metadataChef::Cookbook::Metadata


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/chef/cookbook/metadata.rb', line 93

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 = []
  @eager_load_libraries = true

  @errors = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object


609
610
611
612
613
614
615
# File 'lib/chef/cookbook/metadata.rb', line 609

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

Instance Attribute Details

#chef_versionsArray<Gem::Dependency> (readonly)

Returns Array of supported Chef versions.

Returns:

  • (Array<Gem::Dependency>)

    Array of supported Chef versions


77
78
79
# File 'lib/chef/cookbook/metadata.rb', line 77

def chef_versions
  @chef_versions
end

#dependenciesObject (readonly)

Returns the value of attribute dependencies


72
73
74
# File 'lib/chef/cookbook/metadata.rb', line 72

def dependencies
  @dependencies
end

#gemsArray<Array> (readonly)

Returns Array of gems to install with *args as an Array.

Returns:

  • (Array<Array>)

    Array of gems to install with *args as an Array


81
82
83
# File 'lib/chef/cookbook/metadata.rb', line 81

def gems
  @gems
end

#ohai_versionsArray<Gem::Dependency> (readonly)

Returns Array of supported Ohai versions.

Returns:

  • (Array<Gem::Dependency>)

    Array of supported Ohai versions


79
80
81
# File 'lib/chef/cookbook/metadata.rb', line 79

def ohai_versions
  @ohai_versions
end

#platformsObject (readonly)

Returns the value of attribute platforms


71
72
73
# File 'lib/chef/cookbook/metadata.rb', line 71

def platforms
  @platforms
end

#providingObject (readonly)

Returns the value of attribute providing


73
74
75
# File 'lib/chef/cookbook/metadata.rb', line 73

def providing
  @providing
end

#recipesObject (readonly)

Returns the value of attribute recipes


74
75
76
# File 'lib/chef/cookbook/metadata.rb', line 74

def recipes
  @recipes
end

Class Method Details

.from_hash(o) ⇒ Object


470
471
472
473
474
# File 'lib/chef/cookbook/metadata.rb', line 470

def self.from_hash(o)
  cm = new
  cm.from_hash(o)
  cm
end

.from_json(string) ⇒ Object


498
499
500
501
# File 'lib/chef/cookbook/metadata.rb', line 498

def self.from_json(string)
  o = Chef::JSONCompat.from_json(string)
  from_hash(o)
end

.validate_json(json_str) ⇒ Object


503
504
505
506
507
508
509
510
511
512
513
514
515
516
# File 'lib/chef/cookbook/metadata.rb', line 503

def self.validate_json(json_str)
  o = Chef::JSONCompat.from_json(json_str)
   = new
  VERSION_CONSTRAINTS.each do |dependency_type, hash_key|
    if dependency_group = o[hash_key]
      dependency_group.each do |cb_name, constraints|
        if .respond_to?(dependency_type)
          .public_send(dependency_type, cb_name, *Array(constraints))
        end
      end
    end
  end
  true
end

Instance Method Details

#==(other) ⇒ Object


119
120
121
122
123
# File 'lib/chef/cookbook/metadata.rb', line 119

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

#chef_version(*version_args) ⇒ Array<Gem::Dependency>

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.

Parameters:

  • version_args (Array<String>)

    Version constraint in String form

Returns:

  • (Array<Gem::Dependency>)

    Current chef_versions array


323
324
325
326
# File 'lib/chef/cookbook/metadata.rb', line 323

def chef_version(*version_args)
  @chef_versions << Gem::Dependency.new("chef", *version_args) unless version_args.empty?
  @chef_versions
end

#depends(cookbook, *version_args) ⇒ Object

Adds a dependency on another cookbook, with version checking strings.

=== Parameters cookbook:: The cookbook version:: 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:: Returns the list of versions for the platform


284
285
286
287
288
289
290
291
292
293
294
# File 'lib/chef/cookbook/metadata.rb', line 284

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

#description(arg = nil) ⇒ Object

Sets the current description, or returns it. Should be short - one line only!

=== Parameters description:: The new description

=== Returns description:: Returns the description


203
204
205
206
207
208
209
# File 'lib/chef/cookbook/metadata.rb', line 203

def description(arg = nil)
  set_or_return(
    :description,
    arg,
    kind_of: [ String ]
  )
end

#eager_load_libraries(arg = nil) ⇒ Object

Metadata DSL to control the behavior of library loading.

Can be set to:

true - libraries are eagerly loaded in alphabetical order (backcompat) false - libraries are not eagerly loaded, the libraries dir is added to the LOAD_PATH String - a file or glob pattern to eagerly load, otherwise it is treated like false Array - array of files or globs to eagerly load, otherwise it is treated like false


361
362
363
364
365
366
367
# File 'lib/chef/cookbook/metadata.rb', line 361

def eager_load_libraries(arg = nil)
  set_or_return(
    :eager_load_libraries,
    arg,
    kind_of: [ Array, String, TrueClass, FalseClass ]
  )
end

#errorsObject

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:: Whether this metadata object is valid


146
147
148
149
# File 'lib/chef/cookbook/metadata.rb', line 146

def errors
  run_validation
  @errors
end

#from_hash(o) ⇒ Object


476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'lib/chef/cookbook/metadata.rb', line 476

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_incorrect_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)
  @eager_load_libraries         = o[EAGER_LOAD_LIBRARIES] if o.key?(EAGER_LOAD_LIBRARIES)
  self
end

#from_json(string) ⇒ Object


518
519
520
521
# File 'lib/chef/cookbook/metadata.rb', line 518

def from_json(string)
  o = Chef::JSONCompat.from_json(string)
  from_hash(o)
end

#gem(*args) ⇒ Array<Array>

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.

Parameters:

  • args (Array<String>)

    Gem name and options to pass to Bundler's DSL

Returns:

  • (Array<Array>)

    Array of gem statements as args


345
346
347
348
# File 'lib/chef/cookbook/metadata.rb', line 345

def gem(*args)
  @gems << args unless args.empty?
  @gems
end

#gem_requirements_from_array(what, array) ⇒ Array<Gem::Dependency>

Convert an Array of Gem::Dependency objects (chef_version/ohai_version) to a hash.

This is the inverse of #gem_requirements_to_array

Parameters:

  • what (String)

    What version constraint we are constructing ('chef' or 'ohai' presently)

  • array (Array<Array<String>])

    Simple object representation of version constraints (from json)

Returns:

  • (Array<Gem::Dependency>)

    Multiple Gem-style version constraints


435
436
437
438
439
# File 'lib/chef/cookbook/metadata.rb', line 435

def gem_requirements_from_array(what, array)
  array.map do |dep|
    Gem::Dependency.new(what, *dep)
  end
end

#gem_requirements_to_array(*deps) ⇒ Array<Array<String>]

Convert an Array of Gem::Dependency objects (chef_version/ohai_version) to an Array.

Gem::Dependency#to_s is not useful, and there is no #to_json defined on it or its component objects, 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" ] ]

Parameters:

  • deps (Array<Gem::Dependency>)

    Multiple Gem-style version constraints

Returns:

  • (Array<Array<String>])

    Simple object representation of version constraints (for json)


420
421
422
423
424
425
426
# File 'lib/chef/cookbook/metadata.rb', line 420

def gem_requirements_to_array(*deps)
  deps.map do |dep|
    dep.requirement.requirements.map do |op, version|
      "#{op} #{version}"
    end.sort
  end
end

#handle_incorrect_constraints(specification) ⇒ Object

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 >.


549
550
551
552
553
554
555
# File 'lib/chef/cookbook/metadata.rb', line 549

def handle_incorrect_constraints(specification)
  specification.inject(Mash.new) do |acc, (cb, constraints)|
    constraints = Array(constraints)
    acc[cb] = (constraints.empty? || constraints.size > 1) ? [] : constraints.first
    acc
  end
end

#issues_url(arg = nil) ⇒ Object

Sets the cookbook's issues URL, or returns it.

=== Parameters issues_url:: The issues URL

=== Returns issues_url:: Returns the current issues URL.


564
565
566
567
568
569
570
# File 'lib/chef/cookbook/metadata.rb', line 564

def issues_url(arg = nil)
  set_or_return(
    :issues_url,
    arg,
    kind_of: [ String ]
  )
end

#license(arg = nil) ⇒ Object

Sets the current license, or returns it.

=== Parameters license:: The current license.

=== Returns license:: Returns the current license


188
189
190
191
192
193
194
# File 'lib/chef/cookbook/metadata.rb', line 188

def license(arg = nil)
  set_or_return(
    :license,
    arg,
    kind_of: [ String ]
  )
end

#long_description(arg = nil) ⇒ Object

Sets the current long description, or returns it. Might come from a README, say.

=== Parameters long_description:: The new long description

=== Returns long_description:: Returns the long description


218
219
220
221
222
223
224
# File 'lib/chef/cookbook/metadata.rb', line 218

def long_description(arg = nil)
  set_or_return(
    :long_description,
    arg,
    kind_of: [ String ]
  )
end

#maintainer(arg = nil) ⇒ Object

Sets the cookbooks maintainer, or returns it.

=== Parameters maintainer:: The maintainers name

=== Returns maintainer:: Returns the current maintainer.


158
159
160
161
162
163
164
# File 'lib/chef/cookbook/metadata.rb', line 158

def maintainer(arg = nil)
  set_or_return(
    :maintainer,
    arg,
    kind_of: [ String ]
  )
end

#maintainer_email(arg = nil) ⇒ Object

Sets the maintainers email address, or returns it.

=== Parameters maintainer_email:: The maintainers email address

=== Returns maintainer_email:: Returns the current maintainer email.


173
174
175
176
177
178
179
# File 'lib/chef/cookbook/metadata.rb', line 173

def maintainer_email(arg = nil)
  set_or_return(
    :maintainer_email,
    arg,
    kind_of: [ String ]
  )
end

#name(arg = nil) ⇒ Object

Sets the name of the cookbook, or returns it.

=== Parameters name:: The current cookbook name.

=== Returns name:: Returns the current cookbook name.


249
250
251
252
253
254
255
# File 'lib/chef/cookbook/metadata.rb', line 249

def name(arg = nil)
  set_or_return(
    :name,
    arg,
    kind_of: [ String ]
  )
end

#ohai_version(*version_args) ⇒ Array<Gem::Dependency>

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.

Parameters:

  • version_args (Array<String>)

    Version constraint in String form

Returns:

  • (Array<Gem::Dependency>)

    Current ohai_versions array


334
335
336
337
# File 'lib/chef/cookbook/metadata.rb', line 334

def ohai_version(*version_args)
  @ohai_versions << Gem::Dependency.new("ohai", *version_args) unless version_args.empty?
  @ohai_versions
end

#privacy(arg = nil) ⇒ Object

Sets the cookbook's privacy flag, or returns it.

=== Parameters privacy:: Whether this cookbook is private or not

=== Returns privacy:: Whether this cookbook is private or not


581
582
583
584
585
586
587
# File 'lib/chef/cookbook/metadata.rb', line 581

def privacy(arg = nil)
  set_or_return(
    :privacy,
    arg,
    kind_of: [ TrueClass, FalseClass ]
  )
end

#provides(cookbook, *version_args) ⇒ Object

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[apache2])

=== Parameters recipe, definition, resource:: The thing we provide version:: 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:: Returns the list of versions for the platform


310
311
312
313
314
315
# File 'lib/chef/cookbook/metadata.rb', line 310

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

#recipe(name, description) ⇒ Object

Adds a description for a recipe.

=== Parameters recipe:: The recipe description:: The description of the recipe

=== Returns description:: Returns the current description


377
378
379
# File 'lib/chef/cookbook/metadata.rb', line 377

def recipe(name, description)
  @recipes[name] = description
end

#recipes_from_cookbook_version(cookbook) ⇒ Object

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 object representing the cookbook description:: The description of the recipe

=== Returns recipe_unqualified_names:: An array of the recipe names given by the cookbook


391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/chef/cookbook/metadata.rb', line 391

def recipes_from_cookbook_version(cookbook)
  cookbook.fully_qualified_recipe_names.map do |recipe_name|
    unqualified_name =
      if /::default$/.match?(recipe_name)
        name.to_s
      else
        recipe_name
      end

    @recipes[unqualified_name] ||= ""
    provides(unqualified_name)

    unqualified_name
  end
end

#source_url(arg = nil) ⇒ Object

Sets the cookbook's source URL, or returns it.

=== Parameters maintainer:: The source URL

=== Returns source_url:: Returns the current source URL.


530
531
532
533
534
535
536
# File 'lib/chef/cookbook/metadata.rb', line 530

def source_url(arg = nil)
  set_or_return(
    :source_url,
    arg,
    kind_of: [ String ]
  )
end

#supports(platform, *version_args) ⇒ Object

Adds a supported platform, with version checking strings.

=== Parameters platform,:: The platform (like :ubuntu or :mac_os_x) version:: 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:: Returns the list of versions for the platform


267
268
269
270
271
272
# File 'lib/chef/cookbook/metadata.rb', line 267

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

#to_hObject Also known as: to_hash


441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/chef/cookbook/metadata.rb', line 441

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,
    EAGER_LOAD_LIBRARIES => eager_load_libraries,
  }
end

#to_json(*a) ⇒ Object


466
467
468
# File 'lib/chef/cookbook/metadata.rb', line 466

def to_json(*a)
  Chef::JSONCompat.to_json(to_h, *a)
end

#valid?Boolean

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:: Whether this metadata object is valid

Returns:

  • (Boolean)

133
134
135
136
# File 'lib/chef/cookbook/metadata.rb', line 133

def valid?
  run_validation
  @errors.empty?
end

#validate_chef_version!Object

Validates that the Chef::VERSION of the running chef-client matches one of the configured chef_version statements in this cookbooks metadata.

Raises:


603
604
605
606
607
# File 'lib/chef/cookbook/metadata.rb', line 603

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

#validate_ohai_version!Object

Validates that the Ohai::VERSION of the running chef-client matches one of the configured ohai_version statements in this cookbooks metadata.

Raises:


593
594
595
596
597
# File 'lib/chef/cookbook/metadata.rb', line 593

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

#version(arg = nil) ⇒ Object

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:: The current version, as a string

=== Returns version:: Returns the current version


234
235
236
237
238
239
240
# File 'lib/chef/cookbook/metadata.rb', line 234

def version(arg = nil)
  if arg
    @version = Chef::Version.new(arg)
  end

  @version.to_s
end