lib/puppet_library/forge/multi.rb
# -*- encoding: utf-8 -*-
# Puppet Library
# Copyright (C) 2014 drrb
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
require 'puppet_library/forge/forge'
require 'puppet_library/forge/search_result'
require 'puppet_library/util/patches'
require 'ostruct'
module PuppetLibrary::Forge
# A forge that delegates to multiple other forges.
#
# For queries, all subforges are queried. The results are merged, giving
# preference to earlier ones. That is, if the same version of a module is
# found in two different subforges, the one contained in the earlier
# subforge is kept in the query results.
#
# For downloads, subforges are queried sequentially. The first module found
# is returned.
#
# <b>Usage:</b>
#
# # A forge that serves modules from disk, and proxies a remote forge
# multi_forge = Multi.new
# multi_forge.add_forge(Directory.new("/var/modules"))
# multi_forge.add_forge(Proxy.new("http://forge.puppetlabs.com"))
class Multi < Forge
def initialize
@forges = []
end
def prime
@forges.each_in_parallel &:prime
end
def clear_cache
@forges.each_in_parallel &:clear_cache
end
# Add another forge to delegate to.
def add_forge(forge)
@forges << forge
end
def search_modules(query)
all_results = @forges.map do |forge|
forge.search_modules(query)
end.flatten
SearchResult.merge_by_full_name(all_results)
end
def get_module_buffer(author, name, version)
@forges.each do |forge|
begin
return forge.get_module_buffer(author, name, version)
rescue ModuleNotFound
# Try the next one
end
end
raise ModuleNotFound
end
def get_module_metadata(author, name)
metadata_list = @forges.inject([]) do |metadata_list, forge|
begin
metadata_list << forge.get_module_metadata(author, name)
rescue ModuleNotFound
metadata_list
end
end
raise ModuleNotFound if metadata_list.empty?
metadata_list.deep_merge.tap do |metadata|
metadata["releases"] = metadata["releases"].unique_by { |release| release["version"] }
end
end
def get_module_metadata_with_dependencies(author, name, version)
modules_to_search = [ OpenStruct.new(:author => author, :name => name, :version => version) ]
already_searched_modules = []
metadata_list = []
while spec = modules_to_search.shift
if already_searched_modules.include? spec
next
else
already_searched_modules << spec
end
@forges.each do |forge|
begin
metadata = forge.get_module_metadata_with_dependencies(spec.author, spec.name, spec.version)
# Search all subforges for all versions of the dependencies too
modules_to_search += metadata.keys.map do |dep_full_name|
dep_author, dep_name = dep_full_name.split("/")
OpenStruct.new(:author => dep_author, :name => dep_name, :version => nil)
end
metadata_list << metadata
rescue ModuleNotFound
# Try the next one
end
end
end
raise ModuleNotFound if metadata_list.empty?
metadata_list.deep_merge.tap do |metadata|
metadata.each do |module_name, releases|
metadata[module_name] = releases.unique_by { |release| release["version"] }
end
end
end
end
end