class OpenShift::Runtime::CartridgeRepository

Singleton to provide management of cartridges installed on the system

Constants

CARTRIDGE_REPO_DIR

FIXME: move to node.conf

Attributes

path[R]

Filesystem path to where cartridges are installed

Public Class Methods

instantiate_cartridge(cartridge, path) → nil click to toggle source

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_cartridge(cartridge, path) → nil click to toggle source

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

Private Class Methods

extract(method, source, target) click to toggle source
# 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
filesystem_copy(entries, target, black_list) click to toggle source
# 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
uri_copy(uri, temporary, md5 = nil) click to toggle source
# 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
valid_cartridge_home(cartridge, path) click to toggle source
# 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

Public Instance Methods

[](cartridge_name, version, cartridge_version = '_')
Alias for: select
instance.clear → nil click to toggle source

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
instance.each → Cartridge click to toggle source

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
instance.erase(cartridge_name, version, cartridge_version) → Cartridge click to toggle source

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
instance.exist?(cartridge_name, cartridge_version, version) → true or false click to toggle source

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
inspect() click to toggle source

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
instance.install(directory) → Cartridge click to toggle source

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
installed_in_base_path?(cartridge_name, version, cartridge_version) click to toggle source
# 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
latest_cartridge_version?(cartridge_name, version, cartridge_version) click to toggle source

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
latest_in_slice(index_slice) click to toggle source

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
latest_in_slice?(index_slice, version) click to toggle source

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
instance.each_latest → Cartridge click to toggle source

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
instance.load([directory path]) → Fixnum click to toggle source

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
instance.select(cartridge_name, version) → Cartridge click to toggle source
instance.select(cartridge_name, version, cartridge_version) → Cartridge
instance[cartridge_name, version] → Cartridge
instance[cartridge_name, version, cartridge_version] → Cartridge

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
Also aliased as: []
to_s() click to toggle source

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