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