Singleton to provide management of cartridges installed on the system
FIXME: move to node.conf
Filesystem path to where cartridges are installed
Instantiate a cartridge in a gear;
If the cartridge manifest_path is :url then source_url is used to obtain cartridge source Otherwise the cartridge source is copied from the cartridge_repository source_hash is used to ensure the download was successful. CartridgeRepository.instantiate_cartridge(perl_cartridge, '/var/lib/.../mock') => nil
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 467 def self.instantiate_cartridge(cartridge, target, failure_remove = true) FileUtils.mkpath target if :url == cartridge.manifest_path downloadable = true end if downloadable uri = URI(cartridge.source_url) temporary = PathUtils.join(File.dirname(target), File.basename(cartridge.source_url)) cartridge.validate_vendor_name cartridge.check_reserved_vendor_name cartridge.validate_cartridge_name case when 'git' == uri.scheme || cartridge.source_url.end_with?('.git') Utils::oo_spawn(%Q(set -xe; git clone #{cartridge.source_url} #{cartridge.name}; GIT_DIR=./#{cartridge.name}/.git git repack), chdir: Pathname.new(target).parent.to_path, expected_exitstatus: 0) when uri.scheme =~ /^https*/ && cartridge.source_url =~ /\.zip/ begin uri_copy(URI(cartridge.source_url), temporary, cartridge.source_md5) extract(:zip, temporary, target) ensure FileUtils.rm(temporary) end when uri.scheme =~ /^https*/ && cartridge.source_url =~ /(\.tar\.gz|\.tgz)$/ begin uri_copy(URI(cartridge.source_url), temporary, cartridge.source_md5) extract(:tgz, temporary, target) ensure FileUtils.rm(temporary) end when uri.scheme =~ /^https*/ && cartridge.source_url =~ /\.tar$/ begin uri_copy(URI(cartridge.source_url), temporary, cartridge.source_md5) extract(:tar, temporary, target) ensure FileUtils.rm(temporary) end when 'file' == uri.scheme entries = Dir.glob(PathUtils.join(uri.path, '*'), File::FNM_DOTMATCH) filesystem_copy(entries, target, %w(. ..)) else raise ArgumentError.new("CLIENT_ERROR: Unsupported URL(#{cartridge.source_url}) for downloading a private cartridge") end else entries = Dir.glob(PathUtils.join(cartridge.repository_path, '*'), File::FNM_DOTMATCH) filesystem_copy(entries, target, %w(. .. usr)) source_usr = PathUtils.join(cartridge.repository_path, 'usr') target_usr = PathUtils.join(target, 'usr') FileUtils.rm(target_usr) if File.symlink?(target_usr) FileUtils.symlink(source_usr, target_usr) if File.exist?(source_usr) && !File.exist?(target_usr) end valid_cartridge_home(cartridge, target) if downloadable manifest_on_disk = PathUtils.join(target, %w(metadata manifest.yml)) IO.write(manifest_on_disk, YAML.dump(cartridge.manifest)) end rescue => e FileUtils.rm_rf target if failure_remove raise e end
Overlay new code over existing cartridge in a gear;
If the cartridge manifest_path is :url then source_url is used to obtain cartridge source Otherwise the cartridge source is copied from the cartridge_repository source_hash is used to ensure the download was successful. CartridgeRepository.overlay_cartridge(perl_cartridge, '/var/lib/.../mock') => nil
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 453 def self.overlay_cartridge(cartridge, target) instantiate_cartridge(cartridge, target, false) end
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 591 def self.extract(method, source, target) case method when :zip Utils.oo_spawn("/usr/bin/unzip -d #{target} #{source}", expected_exitstatus: 0) when :tgz Utils.oo_spawn("/bin/tar -C #{target} -zxpf #{source}", expected_exitstatus: 0) when :tar Utils.oo_spawn("/bin/tar -C #{target} -xpf #{source}", expected_exitstatus: 0) else raise "Packaging method #{method} not yet supported." end files = Dir.glob(PathUtils.join(target, '*')) if 1 == files.size # A directory of one file is not legal move everyone up a level (github zip's are this way) to_delete = files.first + '.to_delete' File.rename(files.first, to_delete) entries = Dir.glob(PathUtils.join(to_delete, '*'), File::FNM_DOTMATCH).delete_if do |e| %w(. ..).include? File.basename(e) end FileUtils.move(entries, target) FileUtils.rm_rf(to_delete) end end
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 580 def self.filesystem_copy(entries, target, black_list) entries.delete_if do |e| black_list.include? File.basename(e) end raise ArgumentError.new('CLIENT_ERROR: No cartridge sources found to install.') if entries.empty? Utils.oo_spawn("/bin/cp -ad #{entries.join(' ')} #{target}", expected_exitstatus: 0) end
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 544 def self.uri_copy(uri, temporary, md5 = nil) content_length = nil File.open(temporary, 'w') do |output| uri.open(ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE, content_length_proc: lambda { |l| content_length = l } ) do |input| input.meta begin total = 0 while true partial = input.readpartial(4096) total += output.write partial if content_length && content_length < total raise Net::HTTPBadResponse.new("CLIENT_ERROR: Download of '#{uri}' exceeded Content-Length of #{content_length}. Download aborted.") end end rescue EOFError # we are done end end end if content_length && content_length != File.size(temporary) raise Net::HTTPBadResponse.new( "CLIENT_ERROR: Download of '#{uri}' failed, expected Content-Length of #{content_length} received #{File.size(temporary)}") end if md5 digest = Digest::MD5.file(temporary).hexdigest if digest != md5 raise IOError.new("CLIENT_ERROR: Failed to download cartridge, checksum failed: #{md5} expected, #{digest} actual") end end end
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 623 def self.valid_cartridge_home(cartridge, path) errors = [] [ [File, :directory?, %w(metadata)], [File, :directory?, %w(bin)], [File, :file?, %w(metadata manifest.yml)] ].each do |clazz, method, target| relative = PathUtils.join(target) absolute = PathUtils.join(path, relative) unless clazz.method(method).(absolute) errors << "#{relative} is not #{method}" end end unless errors.empty? raise MalformedCartridgeError.new( "CLIENT_ERROR: Malformed cartridge (#{cartridge.name}, #{cartridge.version}, #{cartridge.cartridge_version})", errors ) end end
Clear all entries from the memory index. Nothing is removed from the disk.
CartridgeRepository.instance.clear #=> nil
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 105 def clear @index = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) } end
Process each unique cartridge
CartridgeRepository.instance.each {|c| puts c.name}
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 386 def each return to_enum(:each) unless block_given? cartridges = Set.new @index.each_pair do |_, sw_hash| sw_hash.each_pair do |_, cart_hash| cart_hash.each_pair do |_, cartridge| cartridges.add(cartridge) end end end cartridges.each { |c| yield c } self end
Erase all software versions for the given cartridge version from the repository. This cannot be undone.
CartridgeRepository.instance.erase('php', '3.5', '1.0') #=> Cartridge
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 196 def erase(cartridge_name, version, cartridge_version, force = false) unless exist?(cartridge_name, version, cartridge_version) raise KeyError.new("key not found: (#{cartridge_name}, #{version}, #{cartridge_version})") end if !force && installed_in_base_path?(cartridge_name, version, cartridge_version) raise ArgumentError.new("Cannot erase cartridge installed in CARTRIDGE_BASE_PATH") end entry = nil $OpenShift_CartridgeRepository_SEMAPHORE.synchronize do # find a "template" entry entry = select(cartridge_name, version, cartridge_version) entry.versions.each do |software_version| remove(cartridge_name, software_version, cartridge_version) end FileUtils.rm_r(entry.repository_path) parent = Pathname.new(entry.repository_path).parent FileUtils.rm_r(parent) if 0 == parent.children.count end entry end
Is there an entry in the repository for this tuple?
CartridgeRepository.instance.exist?('cobol', '2002', '1.0') #=> false
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 256 def exist?(cartridge_name, version, cartridge_version) @index.key?(cartridge_name) && @index[cartridge_name].key?(version) && @index[cartridge_name][version].key?(cartridge_version) end
print out all index entries in a table
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 426 def inspect @index.inject("<CartridgeRepository:\n") do |memo, (name, sw_hash)| sw_hash.inject(memo) do |memo, (sw_ver, cart_hash)| cart_hash.inject(memo) do |memo, (cart_ver, cartridge)| memo << "(#{name}, #{sw_ver}, #{cart_ver}): " << cartridge.to_s << "\n" end end << '>' end end
Copies a cartridge's source into the cartridge repository from a
directory
. The Cartridge will additionally be indexed and
available from a CartridgeRepository.instance
CartridgeRepository.instance.install('/usr/libexec/openshift/cartridges/openshift-origin-php') #=> Cartridge
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 170 def install(directory) raise ArgumentError.new("Illegal path to cartridge source: '#{directory}'") unless directory && File.directory?(directory) raise ArgumentError.new("Source cannot be: '#{@path}'") if directory == @path manifest_path = PathUtils.join(directory, 'metadata', 'manifest.yml') raise ArgumentError.new("Cartridge manifest.yml missing: '#{manifest_path}'") unless File.file?(manifest_path) entry = nil $OpenShift_CartridgeRepository_SEMAPHORE.synchronize do entry = insert(Manifest.new(manifest_path, nil, :file, @path)) FileUtils.rm_r(entry.repository_path) if File.exist?(entry.repository_path) FileUtils.mkpath(entry.repository_path) Utils.oo_spawn("shopt -s dotglob; /bin/cp -ad #{directory}/* #{entry.repository_path}", expected_exitstatus: 0) end entry end
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 222 def installed_in_base_path?(cartridge_name, version, cartridge_version) config = OpenShift::Config.new cartridge_base_path = config.get('CARTRIDGE_BASE_PATH') cartridge_path = PathUtils.join(cartridge_base_path, cartridge_name) unless File.exists?(cartridge_path) return false end manifest_path = PathUtils.join(cartridge_path, %w(metadata manifest.yml)) unless File.exists?(manifest_path) return false end error = false begin manifest = Manifest.new(manifest_path, nil, :file) rescue OpenShift::InvalidElementError => e error = true rescue OpenShift::MissingElementError => e error = true end return (!error && manifest.versions.include?(version) && manifest.cartridge_version == cartridge_version) end
Determine whether the given cartridge version is the latest for (cartridge_name, version)
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 372 def latest_cartridge_version?(cartridge_name, version, cartridge_version) if !exist?(cartridge_name, version, cartridge_version) return false end return latest_in_slice?(@index[cartridge_name][version], cartridge_version) end
Determine the latest version present in a slice of the index
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 362 def latest_in_slice(index_slice) real_versions = index_slice.keys real_versions.delete_if { |v| v == '_' } Manifest.sort_versions(real_versions).last end
Determine whether the latest version present in the index slice is the latest one
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 355 def latest_in_slice?(index_slice, version) latest_in_slice(index_slice) == version end
Process each latest version of each software version of each cartridge
CartridgeRepository.instance.each_latest {|c| puts c.name}
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 408 def latest_versions cartridges = [] @index.each_key do |cart_name| @index[cart_name].keys.sort.reverse.each do |software_version| latest = @index[cart_name][software_version]['_'] cartridges << latest unless latest.instance_of?(Hash) end end if block_given? cartridges.each { |c| yield c } end cartridges end
Read cartridge manifests from the CARTRIDGE_REPO_DIR
or the provided directory
.
CartridgeRepository.instance.load("/var/lib/openshift/.cartridge_repository") #=> 24
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 115 def load(directory = nil) $OpenShift_CartridgeRepository_SEMAPHORE.synchronize do load_via_url = directory.nil? find_manifests(directory || @path) do |manifest_path| logger.debug { "Loading cartridge from #{manifest_path}" } if File.size(manifest_path) == 0 logger.warn("Skipping load of #{manifest_path} because manifest appears to be corrupted") next end c = insert(Manifest.new(manifest_path, nil, :file, @path, load_via_url)) logger.debug { "Loaded cartridge (#{c.name}, #{c.version}, #{c.cartridge_version})" } end end count end
Select a software version for a cartridge from the repository.
If you do not provide cartridge_version then the latest is assumed, for version and cartridge_name.
Latest is determined from the Version elements provided in the cartridge's manifest, when the cartridge is loaded.
Assuming PHP, 3.5 and 0.1 are all the latest each of these calls would return the same cartridge.
CartridgeRepository.instance.select('php', '3.5', '0.1') #=> Cartridge CartridgeRepository.instance.select('php', '3.5') #=> Cartridge CartridgeRepository.instance['php', '3.5', '0.1'] #=> Cartridge CartridgeRepository.instance['php', '3.5'] #=> Cartridge
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 153 def select(cartridge_name, version, cartridge_version = '_') unless exist?(cartridge_name, version, cartridge_version) raise KeyError.new("key not found: (#{cartridge_name}, #{version}, #{cartridge_version})") end @index[cartridge_name][version][cartridge_version] end
print out all indexed cartridges in a table
# File lib/openshift-origin-node/model/cartridge_repository.rb, line 437 def to_s each_with_object("") do |c, memo| memo << "(#{c.cartridge_vendor}, #{c.name}, #{c.version}, #{c.cartridge_version})\n" end end