class R10K::Forge::ModuleRelease

Download, unpack, and install modules from the Puppet Forge

Attributes

download_path[RW]

@!attribute [rw] download_path

@return [Pathname] Where the module tarball will be downloaded to.
forge_release[R]

@!attribute [r] forge_release

@api private
@return [PuppetForge::V3::ModuleRelease] The Forge V3 API module
  release object used for downloading and verifying the module
  release.
md5_file_path[RW]

@!attribute [rw] md5_file_path

@return [Pathname] Where the md5 of the cached tarball is stored.
sha256_file_path[RW]

@!attribute [rw] sha256_file_path

@return [Pathname] Where the SHA256 of the cached tarball is stored.
tarball_cache_path[RW]

@!attribute [rw] tarball_cache_path

@return [Pathname] Where the module tarball will be cached to.
tarball_cache_root[RW]

@!attribute [rw] tarball_cache_root

@return [Pathname] Directory where the module tarball will be cached to.
unpack_path[RW]

@!attribute [rw] unpack_path

@return [Pathname] Where the module will be unpacked to.

Public Class Methods

new(full_name, version) click to toggle source

@param full_name [String] The hyphen separated name of the module @param version [String] The version of the module

# File lib/r10k/forge/module_release.rb, line 54
def initialize(full_name, version)
  @full_name = PuppetForge::V3.normalize_name(full_name)
  @version   = version

  # Copy the PuppetForge base connection to the release class; the connection
  # objects are created in the class instances and thus are not shared with
  # subclasses.
  PuppetForge::V3::Release.conn = PuppetForge::V3::Base.conn

  @forge_release = PuppetForge::V3::Release.new({ :name => @full_name, :version => @version, :slug => "#{@full_name}-#{@version}" })

  tarball_name = @forge_release.slug + '.tar.gz'
  @download_path = Pathname.new(Dir.mktmpdir) + (tarball_name)
  @tarball_cache_root = Pathname.new(settings[:cache_root]) + (@forge_release.slug + "/tarball/")
  @tarball_cache_path = @tarball_cache_root + tarball_name

  md5_filename = @forge_release.slug + '.md5'
  @md5_file_path = @tarball_cache_root + md5_filename

  sha256_filename = @forge_release.slug + '.sha256'
  @sha256_file_path = @tarball_cache_root + sha256_filename

  @unpack_path   = Pathname.new(Dir.mktmpdir) + @forge_release.slug
end

Public Instance Methods

cleanup() click to toggle source

Remove all files created while downloading and unpacking the module.

# File lib/r10k/forge/module_release.rb, line 209
def cleanup
  cleanup_unpack_path
  cleanup_download_path
end
cleanup_download_path() click to toggle source

Remove the downloaded module release.

# File lib/r10k/forge/module_release.rb, line 222
def cleanup_download_path
  if download_path.parent.exist?
    download_path.parent.rmtree
  end
end
cleanup_unpack_path() click to toggle source

Remove the temporary directory used for unpacking the module.

# File lib/r10k/forge/module_release.rb, line 215
def cleanup_unpack_path
  if unpack_path.parent.exist?
    unpack_path.parent.rmtree
  end
end
download() click to toggle source

Download the module release to {#download_path} and cache to {#tarball_cache_path}

@return [void]

# File lib/r10k/forge/module_release.rb, line 100
def download
  if @tarball_cache_path.exist?
    logger.debug1 "Using cached copy of #{@forge_release.slug} tarball"
  else
    logger.debug1 "Downloading #{@forge_release.slug} from #{PuppetForge::Release.conn.url_prefix} to #{@download_path}"
    @forge_release.download(download_path)
    FileUtils::mkdir_p(@tarball_cache_root)
    FileUtils::mv(@download_path, @tarball_cache_path)
  end
end
install(target_dir) click to toggle source

Download, unpack, and install this module release to the target directory.

@example

environment_path = Pathname.new('/etc/puppetlabs/puppet/environments/production')
target_dir = environment_path + 'eight_hundred'
mod = R10K::Forge::ModuleRelease.new('branan-eight_hundred', '8.0.0')
mod.install(target_dir)

@param target_dir [Pathname] The full path to where the module should be installed. @return [void]

# File lib/r10k/forge/module_release.rb, line 89
def install(target_dir)
  download
  verify
  unpack(target_dir)
ensure
  cleanup
end
unpack(target_dir) click to toggle source

Unpack the module release at {#tarball_cache_path} into the given target_dir

@param target_dir [Pathname] The final path where the module release

should be unpacked/installed into.

@return [void]

# File lib/r10k/forge/module_release.rb, line 196
def unpack(target_dir)
  logger.debug1 _("Unpacking %{tarball_cache_path} to %{target_dir} (with tmpdir %{tmp_path})") % {tarball_cache_path: tarball_cache_path, target_dir: target_dir, tmp_path: unpack_path}
  file_lists = PuppetForge::Unpacker.unpack(tarball_cache_path.to_s, target_dir.to_s, unpack_path.to_s)
  logger.debug2 _("Valid files unpacked: %{valid_files}") % {valid_files: file_lists[:valid]}
  if !file_lists[:invalid].empty?
    logger.debug1 _("These files existed in the module's tar file, but are invalid filetypes and were not unpacked: %{invalid_files}") % {invalid_files: file_lists[:invalid]}
  end
  if !file_lists[:symlinks].empty?
    logger.warn _("Symlinks are unsupported and were not unpacked from the module tarball. %{release_slug} contained these ignored symlinks: %{symlinks}") % {release_slug: @forge_release.slug, symlinks: file_lists[:symlinks]}
  end
end
verify() click to toggle source

Verify the module release cached in {#tarball_cache_path} against the module release checksum given by the Puppet Forge. On mismatch, remove the cached copy.

@raise [R10K::Error] when no SHA256 is available and FIPS mode is on @return [void]

# File lib/r10k/forge/module_release.rb, line 117
def verify
  logger.debug1 "Verifying that #{@tarball_cache_path} matches checksum"

  sha256_of_tarball = Digest(:SHA256).file(@tarball_cache_path).hexdigest

  if @sha256_file_path.exist?
    verify_from_file(sha256_of_tarball, @sha256_file_path)
  else
    if @forge_release.respond_to?(:file_sha256) && !@forge_release.file_sha256.nil? && !@forge_release.file_sha256.size.zero?
      forge_256_checksum = @forge_release.file_sha256
      verify_from_forge(sha256_of_tarball, forge_256_checksum, @sha256_file_path)
    else
      if R10K::Util::Platform.fips?
        raise R10K::Error, "Could not verify module, no SHA256 checksum available, and MD5 checksums not allowed in FIPS mode"
      end

      logger.debug1 "No SHA256 checksum available, falling back to MD5"
      md5_of_tarball = Digest(:MD5).file(@tarball_cache_path).hexdigest
      if @md5_file_path.exist?
        verify_from_file(md5_of_tarball, @md5_file_path)
      else
        verify_from_forge(md5_of_tarball, @forge_release.file_md5, @md5_file_path)
      end
    end
  end
end
verify_from_file(tarball_checksum, checksum_file_path) click to toggle source

Verify the checksum of the cached tarball against the module release checksum stored in the cache as well. On mismatch, remove the cached copy of both files. @param tarball_checksum [String] the checksum (either md5 or SHA256)

of the downloaded module tarball

@param file [Pathname] the file containing the checksum as downloaded

previously from the forge

@param digest_class [Digest::SHA256, Digest::MD5] which checksum type

to verify with

@raise [PuppetForge::V3::Release::ChecksumMismatch] The

cached module release checksum doesn't match the cached checksum.

@return [void]

# File lib/r10k/forge/module_release.rb, line 158
def verify_from_file(tarball_checksum, checksum_file_path)
  checksum_from_file = File.read(checksum_file_path).strip
  if tarball_checksum != checksum_from_file
    logger.error "Checksum of #{@tarball_cache_path} (#{tarball_checksum}) does not match checksum #{checksum_from_file} in #{checksum_file_path}. Removing both files."
    @tarball_cache_path.delete
    checksum_file_path.delete
    raise PuppetForge::V3::Release::ChecksumMismatch.new
  end
end
verify_from_forge(tarball_checksum, forge_checksum, checksum_file_path) click to toggle source

Verify the checksum of the cached tarball against the module release checksum from the forge. On mismatch, remove the cached copy of the tarball. @param tarball_checksum [String] the checksum (either md5 or SHA256)

of the downloaded module tarball

@param forge_checksum [String] the checksum downloaded from the Forge @param checksum_file_path [Pathname] the path to write the verified

checksum to

@raise [PuppetForge::V3::Release::ChecksumMismatch] The

cached module release checksum doesn't match the forge checksum.

@return [void]

# File lib/r10k/forge/module_release.rb, line 181
def verify_from_forge(tarball_checksum, forge_checksum, checksum_file_path)
  if tarball_checksum != forge_checksum
    logger.debug1 "Checksum of #{@tarball_cache_path} (#{tarball_checksum}) does not match checksum #{forge_checksum} found on the forge. Removing tarball."
    @tarball_cache_path.delete
    raise PuppetForge::V3::Release::ChecksumMismatch.new
  else
    File.write(checksum_file_path, forge_checksum)
  end
end