1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
# frozen_string_literal: true
module Puppet::ModuleTool::Shared
include Puppet::ModuleTool::Errors
def get_local_constraints
@local = Hash.new { |h, k| h[k] = {} }
@conditions = Hash.new { |h, k| h[k] = [] }
@installed = Hash.new { |h, k| h[k] = [] }
@environment.modules_by_path.values.flatten.each do |mod|
mod_name = (mod.forge_name || mod.name).tr('/', '-')
@installed[mod_name] << mod
d = @local["#{mod_name}@#{mod.version}"]
(mod.dependencies || []).each do |hash|
name = hash['name']
conditions = hash['version_requirement']
name = name.tr('/', '-')
d[name] = conditions
@conditions[name] << {
:module => mod_name,
:version => mod.version,
:dependency => conditions
}
end
end
end
def get_remote_constraints(forge)
@remote = Hash.new { |h, k| h[k] = {} }
@urls = {}
@versions = Hash.new { |h, k| h[k] = [] }
Puppet.notice _("Downloading from %{uri} ...") % { uri: forge.uri }
author, modname = Puppet::ModuleTool.username_and_modname_from(@module_name)
info = forge.remote_dependency_info(author, modname, @options[:version])
info.each do |pair|
mod_name, releases = pair
mod_name = mod_name.tr('/', '-')
releases.each do |rel|
semver = begin
SemanticPuppet::Version.parse(rel['version'])
rescue
SemanticPuppet::Version::MIN
end
@versions[mod_name] << { :vstring => rel['version'], :semver => semver }
@versions[mod_name].sort_by! { |a| a[:semver] }
@urls["#{mod_name}@#{rel['version']}"] = rel['file']
d = @remote["#{mod_name}@#{rel['version']}"]
(rel['dependencies'] || []).each do |name, conditions|
d[name.tr('/', '-')] = conditions
end
end
end
end
def implicit_version(mod)
return :latest if @conditions[mod].empty?
if @conditions[mod].all? { |c| c[:queued] || c[:module] == :you }
return :latest
end
:best
end
def annotated_version(mod, versions)
if versions.empty?
implicit_version(mod)
else
"#{implicit_version(mod)}: #{versions.last}"
end
end
def resolve_constraints(dependencies, source = [{ :name => :you }], seen = {}, action = @action)
dependencies = dependencies.filter_map do |mod, range|
source.last[:dependency] = range
@conditions[mod] << {
:module => source.last[:name],
:version => source.last[:version],
:dependency => range,
:queued => true
}
if forced?
range = begin
Puppet::Module.parse_range(@version)
rescue
Puppet::Module.parse_range('>= 0.0.0')
end
else
range = (@conditions[mod]).map do |r|
Puppet::Module.parse_range(r[:dependency])
rescue
Puppet::Module.parse_range('>= 0.0.0')
end.inject(&:&)
end
if @action == :install && seen.include?(mod)
next if range === seen[mod][:semver]
req_module = @module_name
req_versions = @versions[@module_name.to_s].map { |v| v[:semver] }
raise InvalidDependencyCycleError,
:module_name => mod,
:source => (source + [{ :name => mod, :version => source.last[:dependency] }]),
:requested_module => req_module,
:requested_version => @version || annotated_version(req_module, req_versions),
:conditions => @conditions
end
if !(forced? || @installed[mod].empty? || source.last[:name] == :you)
next if range === SemanticPuppet::Version.parse(@installed[mod].first.version)
action = :upgrade
elsif @installed[mod].empty?
action = :install
end
if action == :upgrade
@conditions.each { |_, conds| conds.delete_if { |c| c[:module] == mod } }
end
versions = @versions[mod.to_s].select { |h| range === h[:semver] }
valid_versions = versions.select { |x| x[:semver].special == '' }
valid_versions = versions if valid_versions.empty?
version = valid_versions.last
unless version
req_module = @module_name
req_versions = @versions[@module_name.to_s].map { |v| v[:semver] }
raise NoVersionsSatisfyError,
:requested_name => req_module,
:requested_version => @version || annotated_version(req_module, req_versions),
:installed_version => @installed[@module_name].empty? ? nil : @installed[@module_name].first.version,
:dependency_name => mod,
:conditions => @conditions[mod],
:action => @action
end
seen[mod] = version
{
:module => mod,
:version => version,
:action => action,
:previous_version => @installed[mod].empty? ? nil : @installed[mod].first.version,
:file => @urls["#{mod}@#{version[:vstring]}"],
:path => if action == :install
@options[:target_dir]
else
@installed[mod].empty? ? @options[:target_dir] : @installed[mod].first.modulepath
end,
:dependencies => []
}
end
dependencies.each do |mod|
deps = @remote["#{mod[:module]}@#{mod[:version][:vstring]}"].sort_by(&:first)
mod[:dependencies] = resolve_constraints(deps, source + [{ :name => mod[:module], :version => mod[:version][:vstring] }], seen, :install)
end unless @ignore_dependencies
dependencies
end
def download_tarballs(graph, default_path, forge)
graph.map do |release|
begin
if release[:tarball]
cache_path = Pathname(release[:tarball])
else
cache_path = forge.retrieve(release[:file])
end
rescue OpenURI::HTTPError => e
raise RuntimeError, _("Could not download module: %{message}") % { message: e.message }, e.backtrace
end
[
{ (release[:path] ||= default_path) => cache_path },
*download_tarballs(release[:dependencies], default_path, forge)
]
end.flatten
end
def forced?
options[:force]
end
def add_module_name_constraints_to_graph(graph)
# Puppet modules are installed by "module name", but resolved by
# "full name" (including namespace). So that we don't run into
# problems at install time, we should reject any solution that
# depends on multiple nodes with the same "module name".
graph.add_graph_constraint('PMT') do |nodes|
names = nodes.map { |x| x.dependency_names + [x.name] }.flatten
names = names.map { |x| x.tr('/', '-') }.uniq
names = names.map { |x| x[/-(.*)/, 1] }
names.length == names.uniq.length
end
end
end
|