class Inspec::ProfileVendor
Attributes
Public Class Methods
# File lib/inspec/profile_vendor.rb, line 8 def initialize(path) @profile_path = Pathname.new(File.expand_path(path)) end
Public Instance Methods
# File lib/inspec/profile_vendor.rb, line 33 def cache_path profile_path.join("vendor") end
# File lib/inspec/profile_vendor.rb, line 37 def lockfile profile_path.join("inspec.lock") end
The URL fetcher uses a Tempfile to retrieve the vendored profile, which creates a file that is only readable by the current user. In most circumstances, this is likely OK. However, in environments like a Habitat package, these files need to be readable by all users or the Habitat Supervisor may not be able to start InSpec correctly.
This method makes sure all vendored files are mode 644 for this use case. This method is not called by default - the caller vendoring the profile must make the decision as to whether this is necessary.
# File lib/inspec/profile_vendor.rb, line 27 def make_readable Dir.glob("#{cache_path}/**/*") do |e| FileUtils.chmod(0644, e) if File.file?(e) end end
# File lib/inspec/profile_vendor.rb, line 12 def vendor!(opts) vendor_dependencies!(opts) end
Private Instance Methods
# File lib/inspec/profile_vendor.rb, line 62 def delete_vendored_data FileUtils.rm_rf(cache_path) if cache_path.exist? File.delete(lockfile) if lockfile.exist? end
# File lib/inspec/profile_vendor.rb, line 67 def extract_archives Dir.glob(File.join(cache_path, "*")).each do |filepath| # Get SHA without extension # We use split since '.' is not valid in a SHA checksum destination_dir_name = File.basename(filepath).split(".")[0] destination_path = File.join(cache_path, destination_dir_name) provider = FileProvider.for_path(filepath) next unless provider.is_a?(ZipProvider) || provider.is_a?(TarProvider) Inspec::Log.debug("Extracting '#{filepath}' to '#{destination_path}'") provider.extract(destination_path) Inspec::Log.debug("Deleting archive '#{filepath}'") File.delete(filepath) end end
# File lib/inspec/profile_vendor.rb, line 43 def profile @profile ||= Inspec::Profile.for_target(profile_path.to_s, profile_opts) end
# File lib/inspec/profile_vendor.rb, line 47 def profile_opts { vendor_cache: Inspec::Cache.new(cache_path.to_s), backend: Inspec::Backend.create(Inspec::Config.mock), } end
# File lib/inspec/profile_vendor.rb, line 54 def vendor_dependencies!(opts) # This deletes any existing vendor/ directory delete_vendored_data warm_vendor_cache_from_archives if opts[:airgap] File.write(lockfile, profile.generate_lockfile.to_yaml) extract_archives end
Check top-level profile metadata for any local archives if present, go ahead and inflate it into the vendor cache. This will cause subsequent fetchers to hit on the vendor cache, avoiding a possibly failing fetch under airgapped conditions.
# File lib/inspec/profile_vendor.rb, line 90 def warm_vendor_cache_from_archives # We need to determine the cwd of the profile, to expand the (likely) # relative path of the dependencies. There is no non-invasive way of doing that. profile_cwd = profile.source_reader.target.parent.path # list dependencies in profile profile.metadata.dependencies.each do |dep_info| next unless dep_info.key?(:path) # "Local"-type fetchers are identified by the :path key # Locate local dep dep_profile_path = File.expand_path(dep_info[:path], profile_cwd) raise(Inspec::FetcherFailure, "Profile dependency local path '#{dep_profile_path}' does not exist") unless File.exist?(dep_profile_path) # Determine if it is an archive file_provider = FileProvider.for_path(dep_profile_path) next unless file_provider.is_a?(ZipProvider) || file_provider.is_a?(TarProvider) # Place the local dependency in the vendor cache. # Fetchers are in charge of calculating cache keys. fetcher = Inspec::Fetcher::Local.resolve(dep_profile_path) # Use a shorter name here in hopes of dodging windows filesystems path length restrictions actual_dep_profile_cache_dir = "#{cache_path}/#{fetcher.cache_key}" short_dep_profile_cache_dir = "#{cache_path}/short" file_provider.extract(short_dep_profile_cache_dir) # The archive (probably) contained a vendor cache of its own. # Since we are warming the fetcher cache, and fetcher caches are one-level # while archive caches are potentially deep, we must flatten the archive cache, # so that all vendor cache entries are top-level. Dir["#{short_dep_profile_cache_dir}/vendor/*"].each do |sub_dep_cache_dir| FileUtils.mv(sub_dep_cache_dir, cache_path) end # And finally correctly name the dependency itself. FileUtils.mv(short_dep_profile_cache_dir, actual_dep_profile_cache_dir) end end