Class: R10K::Forge::ModuleRelease

Inherits:
Object
  • Object
show all
Includes:
Logging, Settings::Mixin
Defined in:
lib/r10k/forge/module_release.rb

Overview

Download, unpack, and install modules from the Puppet Forge

Constant Summary

Constants included from Logging

Logging::LOG_LEVELS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

debug_formatter, default_formatter, default_outputter, #logger, #logger_name, parse_level

Methods included from Settings::Mixin

included

Constructor Details

#initialize(full_name, version) ⇒ ModuleRelease

Returns a new instance of ModuleRelease.

Parameters:

  • full_name (String)

    The hyphen separated name of the module

  • version (String)

    The version of the module



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/r10k/forge/module_release.rb', line 53

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

Instance Attribute Details

#download_pathPathname

Returns Where the module tarball will be downloaded to.

Returns:

  • (Pathname)

    Where the module tarball will be downloaded to.



29
30
31
# File 'lib/r10k/forge/module_release.rb', line 29

def download_path
  @download_path
end

#forge_releaseObject (readonly)

Returns the value of attribute forge_release.



25
26
27
# File 'lib/r10k/forge/module_release.rb', line 25

def forge_release
  @forge_release
end

#md5_file_pathPathname

Returns Where the md5 of the cached tarball is stored.

Returns:

  • (Pathname)

    Where the md5 of the cached tarball is stored.



41
42
43
# File 'lib/r10k/forge/module_release.rb', line 41

def md5_file_path
  @md5_file_path
end

#sha256_file_pathPathname

Returns Where the SHA256 of the cached tarball is stored.

Returns:

  • (Pathname)

    Where the SHA256 of the cached tarball is stored.



45
46
47
# File 'lib/r10k/forge/module_release.rb', line 45

def sha256_file_path
  @sha256_file_path
end

#tarball_cache_pathPathname

Returns Where the module tarball will be cached to.

Returns:

  • (Pathname)

    Where the module tarball will be cached to.



33
34
35
# File 'lib/r10k/forge/module_release.rb', line 33

def tarball_cache_path
  @tarball_cache_path
end

#tarball_cache_rootPathname

Returns Directory where the module tarball will be cached to.

Returns:

  • (Pathname)

    Directory where the module tarball will be cached to.



37
38
39
# File 'lib/r10k/forge/module_release.rb', line 37

def tarball_cache_root
  @tarball_cache_root
end

#unpack_pathPathname

Returns Where the module will be unpacked to.

Returns:

  • (Pathname)

    Where the module will be unpacked to.



49
50
51
# File 'lib/r10k/forge/module_release.rb', line 49

def unpack_path
  @unpack_path
end

Instance Method Details

#cleanupObject

Remove all files created while downloading and unpacking the module.



208
209
210
211
# File 'lib/r10k/forge/module_release.rb', line 208

def cleanup
  cleanup_unpack_path
  cleanup_download_path
end

#cleanup_download_pathObject

Remove the downloaded module release.



221
222
223
224
225
# File 'lib/r10k/forge/module_release.rb', line 221

def cleanup_download_path
  if download_path.parent.exist?
    download_path.parent.rmtree
  end
end

#cleanup_unpack_pathObject

Remove the temporary directory used for unpacking the module.



214
215
216
217
218
# File 'lib/r10k/forge/module_release.rb', line 214

def cleanup_unpack_path
  if unpack_path.parent.exist?
    unpack_path.parent.rmtree
  end
end

#downloadvoid

This method returns an undefined value.

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



99
100
101
102
103
104
105
106
107
108
# File 'lib/r10k/forge/module_release.rb', line 99

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) ⇒ void

This method returns an undefined value.

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

Examples:

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)

Parameters:

  • target_dir (Pathname)

    The full path to where the module should be installed.



88
89
90
91
92
93
94
# File 'lib/r10k/forge/module_release.rb', line 88

def install(target_dir)
  download
  verify
  unpack(target_dir)
ensure
  cleanup
end

#unpack(target_dir) ⇒ void

This method returns an undefined value.

Unpack the module release at #tarball_cache_path into the given target_dir

Parameters:

  • target_dir (Pathname)

    The final path where the module release should be unpacked/installed into.



195
196
197
198
199
200
201
202
203
204
205
# File 'lib/r10k/forge/module_release.rb', line 195

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

#verifyvoid

This method returns an undefined value.

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.

Raises:

  • (R10K::Error)

    when no SHA256 is available and FIPS mode is on



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/r10k/forge/module_release.rb', line 116

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) ⇒ void

This method returns an undefined value.

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.

Parameters:

  • tarball_checksum (String)

    the checksum (either md5 or SHA256) of the downloaded module tarball

  • file (Pathname)

    the file containing the checksum as downloaded previously from the forge

  • digest_class (Digest::SHA256, Digest::MD5)

    which checksum type to verify with

Raises:

  • (PuppetForge::V3::Release::ChecksumMismatch)

    The cached module release checksum doesn’t match the cached checksum.



157
158
159
160
161
162
163
164
165
# File 'lib/r10k/forge/module_release.rb', line 157

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) ⇒ void

This method returns an undefined value.

Verify the checksum of the cached tarball against the module release checksum from the forge. On mismatch, remove the cached copy of the tarball.

Parameters:

  • tarball_checksum (String)

    the checksum (either md5 or SHA256) of the downloaded module tarball

  • forge_checksum (String)

    the checksum downloaded from the Forge

  • checksum_file_path (Pathname)

    the path to write the verified checksum to

Raises:

  • (PuppetForge::V3::Release::ChecksumMismatch)

    The cached module release checksum doesn’t match the forge checksum.



180
181
182
183
184
185
186
187
188
# File 'lib/r10k/forge/module_release.rb', line 180

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