Skip to content

Commit

Permalink
(PUP-2093) Migrate PMT to /v3 API.
Browse files Browse the repository at this point in the history
Prior to this commit, the PMT managed all communication with the Forge via the
(now legacy) /v1 API. That API has been deprecated (primarily due to scalability
concerns), which is less of an issue since it was never officially a public API.

This commit migrates all communication to the Forge to the new /v3 API, which is
expected to be launched as our official public API "soon". This commit also
integrates a standalone dependency resolver, as the /v3 API contains no implicit
dependency resolution (as the /v1 API did); consequently, large portions of the
PMT code have been changed, and others have become unreachable.

This commit also changes the default host that the PMT communicates with, from
https://forge.puppetlabs.com to https://forgeapi.puppetlabs.com (the natural
URL for the Forge API service). Since the tool now expects to communicate with
a completely different service, it will be unable to communicate with services
that do not implement the same /v3 API.
  • Loading branch information
pvande committed Apr 4, 2014
1 parent 6fa68b5 commit 3f05274
Show file tree
Hide file tree
Showing 49 changed files with 2,513 additions and 1,171 deletions.
2 changes: 1 addition & 1 deletion lib/puppet/defaults.rb
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ def self.default_diffargs
)
Puppet.define_settings(:module_tool,
:module_repository => {
:default => 'https://forge.puppetlabs.com',
:default => 'https://forgeapi.puppetlabs.com',
:desc => "The module repository",
},
:module_working_dir => {
Expand Down
21 changes: 11 additions & 10 deletions lib/puppet/face/module/install.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
Installs a module from the Puppet Forge or from a release archive file.
The specified module will be installed into the directory
specified with the `--target-dir` option, which defaults to
#{Puppet.lookup(:current_environment).full_modulepath.first}.
specified with the `--target-dir` option, which defaults to the first
directory in the modulepath.
EOT

returns "Pathname object representing the path to the installed module."
Expand Down Expand Up @@ -106,7 +106,7 @@
option "--ignore-dependencies" do
summary "Do not attempt to install dependencies"
description <<-EOT
Do not attempt to install dependencies.
Do not attempt to install dependencies. (Implied by --force.)
EOT
end

Expand All @@ -122,20 +122,21 @@
Puppet::ModuleTool.set_option_defaults options
Puppet.notice "Preparing to install into #{options[:target_dir]} ..."

forge = Puppet::Forge.new("PMT", self.version)
install_dir = Puppet::ModuleTool::InstallDirectory.new(Pathname.new(options[:target_dir]))
installer = Puppet::ModuleTool::Applications::Installer.new(name, forge, install_dir, options)

installer.run
Puppet::ModuleTool::Applications::Installer.run(name, install_dir, options)
end

when_rendering :console do |return_value, name, options|
if return_value[:result] == :failure
if return_value[:result] == :noop
Puppet.notice "Module #{name} #{return_value[:version]} is already installed."
exit 0
elsif return_value[:result] == :failure
Puppet.err(return_value[:error][:multiline])
exit 1
else
tree = Puppet::ModuleTool.build_tree(return_value[:installed_modules], return_value[:install_dir])
return_value[:install_dir] + "\n" +
tree = Puppet::ModuleTool.build_tree(return_value[:graph], return_value[:install_dir])

"#{return_value[:install_dir]}\n" +
Puppet::ModuleTool.format_tree(tree)
end
end
Expand Down
165 changes: 83 additions & 82 deletions lib/puppet/face/module/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,85 +58,22 @@
EOT

when_invoked do |options|
environment_from_options(options).modules_by_path
end
Puppet::ModuleTool.set_option_defaults(options)
environment = options[:environment_instance]

when_rendering :console do |modules_by_path, options|
output = ''
environment = environment_from_options(options)

error_types = {
:non_semantic_version => {
:title => "Non semantic version dependency"
},
:missing => {
:title => "Missing dependency"
},
:version_mismatch => {
:title => "Module '%s' (v%s) fails to meet some dependencies:"
}
{
:environment => environment,
:modules_by_path => environment.modules_by_path,
}
end

@unmet_deps = {}
error_types.each_key do |type|
@unmet_deps[type] = Hash.new do |hash, key|
hash[key] = { :errors => [], :parent => nil }
end
end
when_rendering :console do |result, options|
environment = result[:environment]
modules_by_path = result[:modules_by_path]

# Prepare the unmet dependencies for display on the console.
environment.modules.sort_by {|mod| mod.name}.each do |mod|
unmet_grouped = Hash.new { |h,k| h[k] = [] }
unmet_grouped = mod.unmet_dependencies.inject(unmet_grouped) do |acc, dep|
acc[dep[:reason]] << dep
acc
end
unmet_grouped.each do |type, deps|
unless deps.empty?
unmet_grouped[type].sort_by { |dep| dep[:name] }.each do |dep|
dep_name = dep[:name].gsub('/', '-')
installed_version = dep[:mod_details][:installed_version]
version_constraint = dep[:version_constraint]
parent_name = dep[:parent][:name].gsub('/', '-')
parent_version = dep[:parent][:version]

msg = "'#{parent_name}' (#{parent_version})"
msg << " requires '#{dep_name}' (#{version_constraint})"
@unmet_deps[type][dep[:name]][:errors] << msg
@unmet_deps[type][dep[:name]][:parent] = {
:name => dep[:parent][:name],
:version => parent_version
}
@unmet_deps[type][dep[:name]][:version] = installed_version
end
end
end
end
output = ''

# Display unmet dependencies by category.
error_display_order = [:non_semantic_version, :version_mismatch, :missing]
error_display_order.each do |type|
unless @unmet_deps[type].empty?
@unmet_deps[type].keys.sort_by {|dep| dep }.each do |dep|
name = dep.gsub('/', '-')
title = error_types[type][:title]
errors = @unmet_deps[type][dep][:errors]
version = @unmet_deps[type][dep][:version]

msg = case type
when :version_mismatch
title % [name, version] + "\n"
when :non_semantic_version
title + " '#{name}' (v#{version}):\n"
else
title + " '#{name}':\n"
end

errors.each { |error_string| msg << " #{error_string}\n" }
Puppet.warning msg.chomp
end
end
end
warn_unmet_dependencies(environment)

environment.modulepath.each do |path|
modules = modules_by_path[path]
Expand Down Expand Up @@ -166,14 +103,78 @@
end
end

def environment_from_options(options)
environments = Puppet.lookup(:environments)
if options[:modulepath]
environments.for(options[:modulepath], '')
elsif options[:environment]
environments.get(options[:environment])
else
environments.get(Puppet[:environment])
def warn_unmet_dependencies(environment)
error_types = {
:non_semantic_version => {
:title => "Non semantic version dependency"
},
:missing => {
:title => "Missing dependency"
},
:version_mismatch => {
:title => "Module '%s' (v%s) fails to meet some dependencies:"
}
}

@unmet_deps = {}
error_types.each_key do |type|
@unmet_deps[type] = Hash.new do |hash, key|
hash[key] = { :errors => [], :parent => nil }
end
end

# Prepare the unmet dependencies for display on the console.
environment.modules.sort_by {|mod| mod.name}.each do |mod|
unmet_grouped = Hash.new { |h,k| h[k] = [] }
unmet_grouped = mod.unmet_dependencies.inject(unmet_grouped) do |acc, dep|
acc[dep[:reason]] << dep
acc
end
unmet_grouped.each do |type, deps|
unless deps.empty?
unmet_grouped[type].sort_by { |dep| dep[:name] }.each do |dep|
dep_name = dep[:name].gsub('/', '-')
installed_version = dep[:mod_details][:installed_version]
version_constraint = dep[:version_constraint]
parent_name = dep[:parent][:name].gsub('/', '-')
parent_version = dep[:parent][:version]

msg = "'#{parent_name}' (#{parent_version})"
msg << " requires '#{dep_name}' (#{version_constraint})"
@unmet_deps[type][dep[:name]][:errors] << msg
@unmet_deps[type][dep[:name]][:parent] = {
:name => dep[:parent][:name],
:version => parent_version
}
@unmet_deps[type][dep[:name]][:version] = installed_version
end
end
end
end

# Display unmet dependencies by category.
error_display_order = [:non_semantic_version, :version_mismatch, :missing]
error_display_order.each do |type|
unless @unmet_deps[type].empty?
@unmet_deps[type].keys.sort_by {|dep| dep }.each do |dep|
name = dep.gsub('/', '-')
title = error_types[type][:title]
errors = @unmet_deps[type][dep][:errors]
version = @unmet_deps[type][dep][:version]

msg = case type
when :version_mismatch
title % [name, version] + "\n"
when :non_semantic_version
title + " '#{name}' (v#{version}):\n"
else
title + " '#{name}':\n"
end

errors.each { |error_string| msg << " #{error_string}\n" }
Puppet.warning msg.chomp
end
end
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/puppet/face/module/search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

when_invoked do |term, options|
Puppet::ModuleTool.set_option_defaults options
Puppet::ModuleTool::Applications::Searcher.new(term, Puppet::Forge.new("PMT", self.version), options).run
Puppet::ModuleTool::Applications::Searcher.new(term, Puppet::Forge.new, options).run
end

when_rendering :console do |results, term, options|
Expand Down
19 changes: 10 additions & 9 deletions lib/puppet/face/module/upgrade.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
end

option "--ignore-dependencies" do
summary "Do not attempt to install dependencies."
summary "Do not attempt to install dependencies"
description <<-EOT
Do not attempt to install dependencies
Do not attempt to install dependencies. (Implied by --force.)
EOT
end

Expand All @@ -57,19 +57,20 @@
name = name.gsub('/', '-')
Puppet.notice "Preparing to upgrade '#{name}' ..."
Puppet::ModuleTool.set_option_defaults options
Puppet::ModuleTool::Applications::Upgrader.new(name, Puppet::Forge.new("PMT", self.version), options).run
Puppet::ModuleTool::Applications::Upgrader.new(name, options).run
end

when_rendering :console do |return_value|
if return_value[:result] == :failure
if return_value[:result] == :noop
Puppet.notice return_value[:error][:multiline]
exit 0
elsif return_value[:result] == :failure
Puppet.err(return_value[:error][:multiline])
exit 1
elsif return_value[:result] == :noop
Puppet.err(return_value[:error][:multiline])
exit 0
else
tree = Puppet::ModuleTool.build_tree(return_value[:affected_modules], return_value[:base_dir])
return_value[:base_dir] + "\n" +
tree = Puppet::ModuleTool.build_tree(return_value[:graph], return_value[:base_dir])

"#{return_value[:base_dir]}\n" +
Puppet::ModuleTool.format_tree(tree)
end
end
Expand Down
Loading

0 comments on commit 3f05274

Please sign in to comment.