class Puppet::ModuleTool::Applications::Upgrader
Public Class Methods
new(name, options)
click to toggle source
Calls superclass method
Puppet::ModuleTool::Applications::Application::new
# File lib/puppet/module_tool/applications/upgrader.rb 16 def initialize(name, options) 17 super(options) 18 19 @action = :upgrade 20 @environment = options[:environment_instance] 21 @name = name 22 @ignore_changes = forced? || options[:ignore_changes] 23 @ignore_dependencies = forced? || options[:ignore_dependencies] 24 25 SemanticPuppet::Dependency.add_source(installed_modules_source) 26 SemanticPuppet::Dependency.add_source(module_repository) 27 end
Public Instance Methods
run()
click to toggle source
# File lib/puppet/module_tool/applications/upgrader.rb 29 def run 30 # Disallow anything that invokes md5 to avoid un-friendly termination due to FIPS 31 raise _("Module upgrade is prohibited in FIPS mode.") if Puppet.runtime[:facter].value(:fips_enabled) 32 33 name = @name.tr('/', '-') 34 version = options[:version] || '>= 0.0.0' 35 36 results = { 37 :action => :upgrade, 38 :requested_version => options[:version] || :latest, 39 } 40 41 begin 42 all_modules = @environment.modules_by_path.values.flatten 43 matching_modules = all_modules.select do |x| 44 x.forge_name && x.forge_name.tr('/', '-') == name 45 end 46 47 if matching_modules.empty? 48 raise NotInstalledError, results.merge(:module_name => name) 49 elsif matching_modules.length > 1 50 raise MultipleInstalledError, results.merge(:module_name => name, :installed_modules => matching_modules) 51 end 52 53 installed_release = installed_modules[name] 54 55 # `priority` is an attribute of a `SemanticPuppet::Dependency::Source`, 56 # which is delegated through `ModuleRelease` instances for the sake of 57 # comparison (sorting). By default, the `InstalledModules` source has 58 # a priority of 10 (making it the most preferable source, so that 59 # already installed versions of modules are selected in preference to 60 # modules from e.g. the Forge). Since we are specifically looking to 61 # upgrade this module, we don't want the installed version of this 62 # module to be chosen in preference to those with higher versions. 63 # 64 # This implementation is suboptimal, and since we can expect this sort 65 # of behavior to be reasonably common in Semantic, we should probably 66 # see about implementing a `ModuleRelease#override_priority` method 67 # (or something similar). 68 def installed_release.priority 69 0 70 end 71 72 mod = installed_release.mod 73 results[:installed_version] = SemanticPuppet::Version.parse(mod.version) 74 dir = Pathname.new(mod.modulepath) 75 76 vstring = mod.version ? "v#{mod.version}" : '???' 77 Puppet.notice _("Found '%{name}' (%{version}) in %{dir} ...") % { name: name, version: colorize(:cyan, vstring), dir: dir } 78 unless @ignore_changes 79 changes = Checksummer.run(mod.path) rescue [] 80 if mod.has_metadata? && !changes.empty? 81 raise LocalChangesError, 82 :action => :upgrade, 83 :module_name => name, 84 :requested_version => results[:requested_version], 85 :installed_version => mod.version 86 end 87 end 88 89 Puppet::Forge::Cache.clean 90 91 # Ensure that there is at least one candidate release available 92 # for the target package. 93 available_versions = module_repository.fetch(name) 94 if available_versions.empty? 95 raise NoCandidateReleasesError, results.merge(:module_name => name, :source => module_repository.host) 96 elsif results[:requested_version] != :latest 97 requested = Puppet::Module.parse_range(results[:requested_version]) 98 unless available_versions.any? {|m| requested.include? m.version} 99 raise NoCandidateReleasesError, results.merge(:module_name => name, :source => module_repository.host) 100 end 101 end 102 103 Puppet.notice _("Downloading from %{host} ...") % { host: module_repository.host } 104 if @ignore_dependencies 105 graph = build_single_module_graph(name, version) 106 else 107 graph = build_dependency_graph(name, version) 108 end 109 110 unless forced? 111 add_module_name_constraints_to_graph(graph) 112 end 113 114 installed_modules.each do |installed_module, release| 115 installed_module = installed_module.tr('/', '-') 116 next if installed_module == name 117 118 version = release.version 119 120 unless forced? 121 # Since upgrading already installed modules can be troublesome, 122 # we'll place constraints on the graph for each installed 123 # module, locking it to upgrades within the same major version. 124 installed_range = ">=#{version} #{version.major}.x" 125 graph.add_constraint('installed', installed_module, installed_range) do |node| 126 Puppet::Module.parse_range(installed_range).include? node.version 127 end 128 129 release.mod.dependencies.each do |dep| 130 dep_name = dep['name'].tr('/', '-') 131 132 range = dep['version_requirement'] 133 graph.add_constraint("#{installed_module} constraint", dep_name, range) do |node| 134 Puppet::Module.parse_range(range).include? node.version 135 end 136 end 137 end 138 end 139 140 begin 141 Puppet.info _("Resolving dependencies ...") 142 releases = SemanticPuppet::Dependency.resolve(graph) 143 rescue SemanticPuppet::Dependency::UnsatisfiableGraph 144 raise NoVersionsSatisfyError, results.merge(:requested_name => name) 145 end 146 147 releases.each do |rel| 148 mod = installed_modules_source.by_name[rel.name.split('-').last] 149 if mod 150 next if mod.has_metadata? && mod.forge_name.tr('/', '-') == rel.name 151 152 if rel.name != name 153 dependency = { 154 :name => rel.name, 155 :version => rel.version 156 } 157 end 158 159 raise InstallConflictError, 160 :requested_module => name, 161 :requested_version => options[:version] || 'latest', 162 :dependency => dependency, 163 :directory => mod.path, 164 :metadata => mod.metadata 165 end 166 end 167 168 child = releases.find { |x| x.name == name } 169 170 unless forced? 171 if child.version == results[:installed_version] 172 versions = graph.dependencies[name].map { |r| r.version } 173 newer_versions = versions.select { |v| v > results[:installed_version] } 174 175 raise VersionAlreadyInstalledError, 176 :module_name => name, 177 :requested_version => results[:requested_version], 178 :installed_version => results[:installed_version], 179 :newer_versions => newer_versions, 180 :possible_culprits => installed_modules_source.fetched.reject { |x| x == name } 181 elsif child.version < results[:installed_version] 182 raise DowngradingUnsupportedError, 183 :module_name => name, 184 :requested_version => results[:requested_version], 185 :installed_version => results[:installed_version] 186 end 187 end 188 189 Puppet.info _("Preparing to upgrade ...") 190 releases.each { |release| release.prepare } 191 192 Puppet.notice _('Upgrading -- do not interrupt ...') 193 releases.each do |release| 194 installed = installed_modules[release.name] 195 if installed 196 release.install(Pathname.new(installed.mod.modulepath)) 197 else 198 release.install(dir) 199 end 200 end 201 202 results[:result] = :success 203 results[:base_dir] = releases.first.install_dir 204 results[:affected_modules] = releases 205 results[:graph] = [ build_install_graph(releases.first, releases) ] 206 207 rescue VersionAlreadyInstalledError => e 208 results[:result] = (e.newer_versions.empty? ? :noop : :failure) 209 results[:error] = { :oneline => e.message, :multiline => e.multiline } 210 rescue => e 211 results[:error] = { 212 :oneline => e.message, 213 :multiline => e.respond_to?(:multiline) ? e.multiline : [e.to_s, e.backtrace].join("\n") 214 } 215 ensure 216 results[:result] ||= :failure 217 end 218 219 results 220 end
Private Instance Methods
build_dependency_graph(name, version)
click to toggle source
# File lib/puppet/module_tool/applications/upgrader.rb 243 def build_dependency_graph(name, version) 244 SemanticPuppet::Dependency.query(name => version) 245 end
build_install_graph(release, installed, graphed = [])
click to toggle source
# File lib/puppet/module_tool/applications/upgrader.rb 247 def build_install_graph(release, installed, graphed = []) 248 previous = installed_modules[release.name] 249 previous = previous.version if previous 250 251 action = :upgrade 252 unless previous && previous != release.version 253 action = :install 254 end 255 256 graphed << release 257 258 dependencies = release.dependencies.values.map do |deps| 259 dep = (deps & installed).first 260 if dep == installed_modules[dep.name] 261 next 262 end 263 264 if dep && !graphed.include?(dep) 265 build_install_graph(dep, installed, graphed) 266 end 267 end.compact 268 269 return { 270 :release => release, 271 :name => release.name, 272 :path => release.install_dir, 273 :dependencies => dependencies.compact, 274 :version => release.version, 275 :previous_version => previous, 276 :action => action, 277 } 278 end
build_single_module_graph(name, version)
click to toggle source
# File lib/puppet/module_tool/applications/upgrader.rb 235 def build_single_module_graph(name, version) 236 range = Puppet::Module.parse_range(version) 237 graph = SemanticPuppet::Dependency::Graph.new(name => range) 238 releases = SemanticPuppet::Dependency.fetch_releases(name) 239 releases.each { |release| release.dependencies.clear } 240 graph << releases 241 end
installed_modules()
click to toggle source
# File lib/puppet/module_tool/applications/upgrader.rb 231 def installed_modules 232 installed_modules_source.modules 233 end
installed_modules_source()
click to toggle source
# File lib/puppet/module_tool/applications/upgrader.rb 227 def installed_modules_source 228 @installed ||= Puppet::ModuleTool::InstalledModules.new(@environment) 229 end
module_repository()
click to toggle source
# File lib/puppet/module_tool/applications/upgrader.rb 223 def module_repository 224 @repo ||= Puppet::Forge.new(Puppet[:module_repository]) 225 end