class Berkshelf::Installer

Attributes

berksfile[R]
downloader[R]
lockfile[R]
pool[R]
worker[R]

Public Class Methods

new(berksfile) click to toggle source

@param [Berkshelf::Berksfile] berksfile

# File lib/berkshelf/installer.rb, line 12
def initialize(berksfile)
  @berksfile  = berksfile
  @lockfile   = berksfile.lockfile
  @pool       = Concurrent::FixedThreadPool.new([Concurrent.processor_count - 1, 2].max)
  @worker     = Worker.new(berksfile)
end

Public Instance Methods

build_universe() click to toggle source
# File lib/berkshelf/installer.rb, line 19
def build_universe
  berksfile.sources.collect do |source|
    Thread.new do

      Berkshelf.formatter.msg("Fetching cookbook index from #{source}...")
      source.build_universe
    rescue Berkshelf::APIClientError => ex
      Berkshelf.formatter.warn "Error retrieving universe from source: #{source}"
      Berkshelf.formatter.warn "  * [#{ex.class}] #{ex}"

    end
  end.map(&:join)
end
run() click to toggle source

@return [Array<Berkshelf::CachedCookbook>]

# File lib/berkshelf/installer.rb, line 34
def run
  lockfile.reduce!

  Berkshelf.formatter.msg("Resolving cookbook dependencies...")

  dependencies, cookbooks =
    if lockfile.trusted?
      install_from_lockfile
    else
      install_from_universe
    end

  Berkshelf.log.debug "  Finished resolving, calculating locks"

  to_lock = dependencies.select do |dependency|
    berksfile.has_dependency?(dependency)
  end

  Berkshelf.log.debug "  New locks"
  to_lock.each do |lock|
    Berkshelf.log.debug "    #{lock}"
  end

  lockfile.graph.update(cookbooks)
  lockfile.update(to_lock)
  lockfile.save

  cookbooks
end

Private Instance Methods

download_locations(dependencies) click to toggle source
# File lib/berkshelf/installer.rb, line 189
def download_locations(dependencies)
  dependencies.select(&:location).each do |dependency|
    unless dependency.location.installed?
      Berkshelf.formatter.fetch(dependency)
      dependency.location.install
    end
  end
end
install_from_lockfile() click to toggle source

Install all the dependencies from the lockfile graph.

@return [Array<Array<Dependency> Array<CachedCookbook>>]

the list of installed dependencies and cookbooks
# File lib/berkshelf/installer.rb, line 119
def install_from_lockfile
  Berkshelf.log.info "Installing from lockfile"

  dependencies = lockfile.graph.locks.values

  Berkshelf.log.debug "  Dependencies"
  dependencies.map do |dependency|
    Berkshelf.log.debug "    #{dependency}"
  end

  download_locations(dependencies)

  # Only construct the universe if we are going to install things
  unless dependencies.all?(&:installed?)
    Berkshelf.log.debug "  Not all dependencies are installed"
    build_universe
  end

  futures = dependencies.sort.map { |dependency| Concurrent::Future.execute(executor: pool) { worker.install(dependency) } }
  cookbooks = futures.map(&:value)
  rejects = futures.select(&:rejected?)
  raise rejects.first.reason unless rejects.empty?

  [dependencies, cookbooks]
end
install_from_universe() click to toggle source

Resolve and install the dependencies from the “universe”, updating the lockfile appropiately.

@return [Array<Array<Dependency> Array<CachedCookbook>>]

the list of installed dependencies and cookbooks
# File lib/berkshelf/installer.rb, line 150
def install_from_universe
  Berkshelf.log.info "Installing from universe"

  dependencies = lockfile.graph.locks.values + berksfile.dependencies
  dependencies = dependencies.inject({}) do |hash, dependency|
    # Fancy way of ensuring no duplicate dependencies are used...
    hash[dependency.name] ||= dependency
    hash
  end.values

  download_locations(dependencies)

  Berkshelf.log.debug "  Creating a resolver"
  resolver = Resolver.new(berksfile, dependencies)

  # Unlike when installing from the lockfile, we _always_ need to build
  # the universe when installing from the universe... duh
  build_universe

  # Add any explicit dependencies for already-downloaded cookbooks (like
  # path locations)
  dependencies.each do |dependency|
    if dependency.location
      cookbook = dependency.cached_cookbook
      Berkshelf.log.debug "  Adding explicit dependency on #{cookbook}"
      resolver.add_explicit_dependencies(cookbook)
    end
  end

  Berkshelf.log.debug "  Starting resolution..."

  futures = resolver.resolve.sort.map { |dependency| Concurrent::Future.execute(executor: pool) { worker.install(dependency) } }
  cookbooks = futures.map(&:value)
  rejects = futures.select(&:rejected?)
  raise rejects.first.reason unless rejects.empty?

  [dependencies, cookbooks]
end