Class: Berkshelf::Berksfile
- Inherits:
-
Object
- Object
- Berkshelf::Berksfile
- Extended by:
- Forwardable
- Includes:
- Mixin::DSLEval, Mixin::Logging
- Defined in:
- lib/berkshelf/berksfile.rb
Constant Summary collapse
- @@active_group =
nil
Instance Attribute Summary collapse
- #cached_cookbooks ⇒ Array<Berkshelf::CachedCookbook> readonly
- #downloader ⇒ Berkshelf::Downloader readonly
-
#filepath ⇒ String
readonly
The path on disk to the file representing this instance of Berksfile.
Class Method Summary collapse
- .from_file(file) ⇒ Berksfile
-
.vendor(cookbooks, path) ⇒ String
Copy all cached_cookbooks to the given directory.
Instance Method Summary collapse
- #[](name) ⇒ Berkshelf::CookbookSource (also: #get_source)
-
#add_source(name, constraint = nil, options = {}) ⇒ Array<Berkshelf::CookbookSource]
Add a source of the given name and constraint to the array of sources.
-
#apply(environment_name, options = {}) ⇒ Object
Resolve this Berksfile and apply the locks found in the generated Berksfile.lock to the target Chef environment.
-
#chef_api(value, options = {}) ⇒ Hash
Add a ‘Chef API’ default location which will be used to resolve cookbook sources that do not contain an explicit location.
-
#cookbook(*args) ⇒ Object
Add a cookbook dependency to the Berksfile to be retrieved and have it’s dependencies recursively retrieved and resolved.
-
#find(name) ⇒ Berkshelf::CookbookSource?
Find a source defined in this berksfile by name.
- #group(*args) ⇒ Object
-
#groups ⇒ Hash
A hash containing group names as keys and an array of CookbookSources that are a member of that group as values.
- #has_source?(source) ⇒ Boolean
-
#initialize(path) ⇒ Berksfile
constructor
A new instance of Berksfile.
-
#install(options = {}) ⇒ Array<Berkshelf::CachedCookbook>
Install the sources listed in the Berksfile, respecting the locked versions in the Berksfile.lock.
-
#lockfile ⇒ Berkshelf::Lockfile
Get the lockfile corresponding to this Berksfile.
-
#metadata(options = {}) ⇒ Object
Use a Cookbook metadata file to determine additional cookbook sources to retrieve.
-
#outdated(options = {}) ⇒ Hash
Get a list of all the cookbooks which have newer versions found on the community site versus what your current constraints allow.
-
#package(name = nil, options = {}) ⇒ String
Package the given cookbook for distribution outside of berkshelf.
- #remove_source(source) ⇒ Berkshelf::CookbookSource
-
#resolve(sources = [], options = {}) ⇒ Array<Berkshelf::CachedCookbooks>
Finds a solution for the Berksfile and returns an array of CachedCookbooks.
-
#site(value) ⇒ Hash
Add a ‘Site’ default location which will be used to resolve cookbook sources that do not contain an explicit location.
-
#sources(options = {}) ⇒ Array<Berkshelf::CookbookSource>
The list of cookbook sources specified in this Berksfile.
- #update(options = {}) ⇒ Object
-
#upload(options = {}) ⇒ Object
Upload the cookbooks installed by this Berksfile.
Methods included from Mixin::DSLEval
#dsl_eval, #dsl_eval_file, included
Methods included from Mixin::Logging
Constructor Details
#initialize(path) ⇒ Berksfile
Returns a new instance of Berksfile.
87 88 89 90 91 92 |
# File 'lib/berkshelf/berksfile.rb', line 87 def initialize(path) @filepath = path @sources = Hash.new @downloader = Downloader.new(Berkshelf.cookbook_store) @cached_cookbooks = nil end |
Instance Attribute Details
#cached_cookbooks ⇒ Array<Berkshelf::CachedCookbook> (readonly)
80 81 82 |
# File 'lib/berkshelf/berksfile.rb', line 80 def cached_cookbooks @cached_cookbooks end |
#downloader ⇒ Berkshelf::Downloader (readonly)
77 78 79 |
# File 'lib/berkshelf/berksfile.rb', line 77 def downloader @downloader end |
#filepath ⇒ String (readonly)
Returns The path on disk to the file representing this instance of Berksfile.
74 75 76 |
# File 'lib/berkshelf/berksfile.rb', line 74 def filepath @filepath end |
Class Method Details
.from_file(file) ⇒ Berksfile
8 9 10 11 12 13 14 15 16 |
# File 'lib/berkshelf/berksfile.rb', line 8 def from_file(file) raise BerksfileNotFound.new(file) unless File.exist?(file) begin new(file).dsl_eval_file(file) rescue => ex raise BerksfileReadError.new(ex) end end |
.vendor(cookbooks, path) ⇒ String
Copy all cached_cookbooks to the given directory. Each cookbook will be contained in a directory named after the name of the cookbook.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/berkshelf/berksfile.rb', line 28 def vendor(cookbooks, path) chefignore = nil path = File.(path) scratch = Berkshelf.mktmpdir FileUtils.mkdir_p(path) unless (ignore_file = Berkshelf::Chef::Cookbook::Chefignore.find_relative_to(Dir.pwd)).nil? chefignore = Berkshelf::Chef::Cookbook::Chefignore.new(ignore_file) end cookbooks.each do |cb| dest = File.join(scratch, cb.cookbook_name, '/') FileUtils.mkdir_p(dest) # Dir.glob does not support backslash as a File separator src = cb.path.to_s.gsub('\\', '/') files = Dir.glob(File.join(src, '*')) # Filter out files using chefignore files = chefignore.remove_ignores_from(files) if chefignore FileUtils.cp_r(files, dest) end FileUtils.remove_dir(path, force: true) FileUtils.mv(scratch, path) path end |
Instance Method Details
#[](name) ⇒ Berkshelf::CookbookSource Also known as: get_source
370 371 372 |
# File 'lib/berkshelf/berksfile.rb', line 370 def [](name) @sources[name] end |
#add_source(name, constraint = nil, options = {}) ⇒ Array<Berkshelf::CookbookSource]
Add a source of the given name and constraint to the array of sources.
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/berkshelf/berksfile.rb', line 253 def add_source(name, constraint = nil, = {}) if has_source?(name) # Only raise an exception if the source is a true duplicate groups = ([:group].nil? || [:group].empty?) ? [:default] : [:group] if !(@sources[name].groups & groups).empty? raise DuplicateSourceDefined, "Berksfile contains multiple sources named '#{name}'. Use only one, or put them in different groups." end end if [:path] = File.join([:path], 'metadata.rb') end [:constraint] = constraint @sources[name] = CookbookSource.new(self, name, ) end |
#apply(environment_name, options = {}) ⇒ Object
Resolve this Berksfile and apply the locks found in the generated Berksfile.lock to the target Chef environment
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 |
# File 'lib/berkshelf/berksfile.rb', line 567 def apply(environment_name, = {}) conn = ridley_connection() environment = conn.environment.find(environment_name) if environment install environment.cookbook_versions = {}.tap do |cookbook_versions| lockfile.sources.each { |source| cookbook_versions[source.name] = source.locked_version.to_s } end environment.save else raise EnvironmentNotFound.new(environment_name) end rescue Ridley::Errors::RidleyError => ex raise ChefConnectionError, ex ensure conn.terminate if conn && conn.alive? end |
#chef_api(value, options = {}) ⇒ Hash
specifying the symbol :config as the value of the chef_api default location will attempt to use the contents of your Berkshelf configuration to find the Chef API to interact with.
Add a ‘Chef API’ default location which will be used to resolve cookbook sources that do not contain an explicit location.
237 238 239 |
# File 'lib/berkshelf/berksfile.rb', line 237 def chef_api(value, = {}) add_location(:chef_api, value, ) end |
#cookbook(name, version_constraint, options = {}) ⇒ Object #cookbook(name, options = {}) ⇒ Object
Add a cookbook dependency to the Berksfile to be retrieved and have it’s dependencies recursively retrieved and resolved.
162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/berkshelf/berksfile.rb', line 162 def cookbook(*args) = args.last.is_a?(Hash) ? args.pop : Hash.new name, constraint = args [:path] &&= File.([:path], File.dirname(filepath)) [:group] = Array([:group]) if @@active_group [:group] += @@active_group end add_source(name, constraint, ) end |
#find(name) ⇒ Berkshelf::CookbookSource?
Find a source defined in this berksfile by name.
337 338 339 |
# File 'lib/berkshelf/berksfile.rb', line 337 def find(name) @sources[name] end |
#group(*args) ⇒ Object
176 177 178 179 180 |
# File 'lib/berkshelf/berksfile.rb', line 176 def group(*args) @@active_group = args yield @@active_group = nil end |
#groups ⇒ Hash
Returns a hash containing group names as keys and an array of CookbookSources that are a member of that group as values
Example:
{
nautilus: [
#<Berkshelf::CookbookSource: nginx (~> 1.0.0)>,
#<Berkshelf::CookbookSource: mysql (~> 1.2.4)>
],
skarner: [
#<Berkshelf::CookbookSource: nginx (~> 1.0.0)>
]
}.
355 356 357 358 359 360 361 362 363 364 |
# File 'lib/berkshelf/berksfile.rb', line 355 def groups {}.tap do |groups| sources.each do |source| source.groups.each do |group| groups[group] ||= [] groups[group] << source end end end end |
#has_source?(source) ⇒ Boolean
284 285 286 |
# File 'lib/berkshelf/berksfile.rb', line 284 def has_source?(source) @sources.has_key?(source.to_s) end |
#install(options = {}) ⇒ Array<Berkshelf::CachedCookbook>
Install the sources listed in the Berksfile, respecting the locked versions in the Berksfile.lock.
-
Check that a lockfile exists. If a lockfile does not exist, all sources are considered to be “unlocked”. If a lockfile is specified, a definition is created via the following algorithm:
-
For each source, see if there exists a locked version that still satisfies the version constraint in the Berksfile. If there exists such a source, remove it from the list of unlocked sources. If not, then either a version constraint has changed, or a new source has been added to the Berksfile. In the event that a locked_source exists, but it no longer satisfies the constraint, this method will raise a OutdatedCookbookSource, and inform the user to run
berks update COOKBOOK
to remedy the issue. -
Remove any locked sources that no longer exist in the Berksfile (i.e. a cookbook source was removed from the Berksfile).
-
-
Resolve the collection of locked and unlocked sources.
-
Write out a new lockfile.
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'lib/berkshelf/berksfile.rb', line 415 def install( = {}) local_sources = apply_lockfile(sources()) resolver = resolve(local_sources) @cached_cookbooks = resolver[:solution] local_sources = resolver[:sources] verify_licenses! self.class.vendor(@cached_cookbooks, [:path]) if [:path] lockfile.update(local_sources) unless [:update_lockfile] == false self.cached_cookbooks end |
#lockfile ⇒ Berkshelf::Lockfile
Get the lockfile corresponding to this Berksfile. This is necessary because the user can specify a different path to the Berksfile. So assuming the lockfile is named “Berksfile.lock” is a poor assumption.
681 682 683 |
# File 'lib/berkshelf/berksfile.rb', line 681 def lockfile @lockfile ||= Berkshelf::Lockfile.new(self) end |
#metadata(options = {}) ⇒ Object
Use a Cookbook metadata file to determine additional cookbook sources to retrieve. All sources found in the metadata will use the default locations set in the Berksfile (if any are set) or the default locations defined by Berkshelf.
190 191 192 193 194 195 196 197 198 199 |
# File 'lib/berkshelf/berksfile.rb', line 190 def ( = {}) path = [:path] || File.dirname(filepath) = File.(File.join(path, 'metadata.rb')) = Ridley::Chef::Cookbook::Metadata.from_file() name = .name.presence || File.basename(File.(path)) add_source(name, nil, { path: path, metadata: true }) end |
#outdated(options = {}) ⇒ Hash
Get a list of all the cookbooks which have newer versions found on the community site versus what your current constraints allow
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/berkshelf/berksfile.rb', line 469 def outdated( = {}) outdated = Hash.new sources().each do |cookbook| location = cookbook.location || Location.init(cookbook.name, cookbook.version_constraint, site: :opscode) if location.is_a?(SiteLocation) latest_version = location.latest_version unless cookbook.version_constraint.satisfies?(latest_version) outdated[cookbook] = latest_version end end end outdated end |
#package(name = nil, options = {}) ⇒ String
Package the given cookbook for distribution outside of berkshelf. If the name attribute is not given, all cookbooks in the Berksfile will be packaged.
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 |
# File 'lib/berkshelf/berksfile.rb', line 606 def package(name = nil, = {}) tar_name = "#{name || 'package'}.tar.gz" output = File.(File.join([:output], tar_name)) unless name.nil? source = self.find(name) raise CookbookNotFound, "Cookbook '#{name}' is not in your Berksfile" unless source package = Berkshelf.ui.mute { self.resolve(source, )[:solution] } else package = Berkshelf.ui.mute { self.resolve(sources, )[:solution] } end package.each do |cookbook| validate_files!(cookbook) end Dir.mktmpdir do |tmp| package.each do |cached_cookbook| path = cached_cookbook.path.to_s destination = File.join(tmp, cached_cookbook.cookbook_name) FileUtils.cp_r(path, destination) unless [:ignore_chefignore] if ignore_file = Berkshelf::Chef::Cookbook::Chefignore.find_relative_to(path) chefignore = Berkshelf::Chef::Cookbook::Chefignore.new(ignore_file) chefignore.remove_ignores_from(destination) if chefignore end end end FileUtils.mkdir_p([:output]) Dir.chdir(tmp) do |dir| tgz = Zlib::GzipWriter.new(File.open(output, 'wb')) Archive::Tar::Minitar.pack('.', tgz) end end Berkshelf.formatter.package(name, output) output end |
#remove_source(source) ⇒ Berkshelf::CookbookSource
276 277 278 |
# File 'lib/berkshelf/berksfile.rb', line 276 def remove_source(source) @sources.delete(source.to_s) end |
#resolve(sources = [], options = {}) ⇒ Array<Berkshelf::CachedCookbooks>
Finds a solution for the Berksfile and returns an array of CachedCookbooks.
664 665 666 667 668 669 670 671 672 |
# File 'lib/berkshelf/berksfile.rb', line 664 def resolve(sources = [], = {}) resolver = Resolver.new( self, sources: sources, skip_dependencies: [:skip_dependencies] ) { solution: resolver.resolve, sources: resolver.sources } end |
#site(value) ⇒ Hash
specifying the symbol :opscode as the value of the site default location is an alias for the latest API of the Opscode Community Site.
Add a ‘Site’ default location which will be used to resolve cookbook sources that do not contain an explicit location.
215 216 217 |
# File 'lib/berkshelf/berksfile.rb', line 215 def site(value) add_location(:site, value) end |
#sources(options = {}) ⇒ Array<Berkshelf::CookbookSource>
The list of cookbook sources specified in this Berksfile
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/berkshelf/berksfile.rb', line 307 def sources( = {}) l_sources = @sources.values cookbooks = Array([:cookbooks]) except = Array([:except]).collect(&:to_sym) only = Array([:only]).collect(&:to_sym) case when !except.empty? && !only.empty? raise Berkshelf::ArgumentError, 'Cannot specify both :except and :only' when !cookbooks.empty? if !except.empty? && !only.empty? Berkshelf.ui.warn 'Cookbooks were specified, ignoring :except and :only' end l_sources.select { |source| cookbooks.include?(source.name) } when !except.empty? l_sources.select { |source| (except & source.groups).empty? } when !only.empty? l_sources.select { |source| !(only & source.groups).empty? } else l_sources end end |
#update(options = {}) ⇒ Object
439 440 441 442 443 444 445 446 447 |
# File 'lib/berkshelf/berksfile.rb', line 439 def update( = {}) validate_cookbook_names!() # Unlock any/all specified cookbooks sources().each { |source| lockfile.unlock(source) } # NOTE: We intentionally do NOT pass options to the installer self.install end |
#upload(options = {}) ⇒ Object
Upload the cookbooks installed by this Berksfile
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 |
# File 'lib/berkshelf/berksfile.rb', line 521 def upload( = {}) = .reverse_merge(force: false, freeze: true, skip_dependencies: false, halt_on_frozen: false, update_lockfile: false) cached_cookbooks = install() upload_opts = .slice(:force, :freeze) conn = ridley_connection() cached_cookbooks.each do |cookbook| Berkshelf.formatter.upload(cookbook.cookbook_name, cookbook.version, conn.server_url) validate_files!(cookbook) begin conn.cookbook.upload(cookbook.path, upload_opts.merge(name: cookbook.cookbook_name)) rescue Ridley::Errors::FrozenCookbook => ex if [:halt_on_frozen] raise Berkshelf::FrozenCookbook, ex end end end if [:skip_dependencies] missing_cookbooks = .fetch(:cookbooks, nil) - cached_cookbooks.map(&:cookbook_name) unless missing_cookbooks.empty? msg = "Unable to upload cookbooks: #{missing_cookbooks.sort.join(', ')}\n" msg << "Specified cookbooks must be defined within the Berkshelf file when using the" msg << " `--skip-dependencies` option" raise ExplicitCookbookNotFound.new(msg) end end rescue Ridley::Errors::RidleyError => ex log_exception(ex) raise ChefConnectionError, ex # todo implement ensure conn.terminate if conn && conn.alive? end |