mirror of
https://github.com/inspec/inspec
synced 2024-11-10 15:14:23 +00:00
Support vendored profiles in Habitat-packaged profiles
This change adds support in Habitat-packaged profiles for profiles that depend on other profiles. When `inspec habitat profile create` or `inspec habitat profile upload` is run, it will see if the profile's dependencies have been vendored yet, and if not, it will vendor them before creating the habitat artifact. For the git and URL fetchers, more explicit creation of the target directories for the vendored profiles is done. This is implicitly done via normal CLI interactions a user may go through, but in our case, we want to ensure those directories are there before the fetchers try to write out content. By adding this support, we also fix a bug experienced in Habitat where a profile that was packaged before an `inspec exec` was run for the profile would cause a failure in Habitat. This is caused by `inspec exec` doing a vendor of the dependencies if necessary and generating the inspec.lock file. In Habitat, the package dir is not writable by the hab user and InSpec would fail to run due to an inability to write out an inspec.lock. Signed-off-by: Adam Leff <adam@leff.co>
This commit is contained in:
parent
279f07cb1e
commit
8269d0da9e
4 changed files with 101 additions and 2 deletions
|
@ -37,6 +37,8 @@ module Habitat
|
|||
validate_habitat_installed
|
||||
validate_habitat_origin
|
||||
create_profile_object
|
||||
verify_profile
|
||||
vendor_profile_dependencies
|
||||
copy_profile_to_work_dir
|
||||
create_plan
|
||||
create_run_hook
|
||||
|
@ -79,7 +81,19 @@ module Habitat
|
|||
private
|
||||
|
||||
def create_profile_object
|
||||
@profile = Inspec::Profile.for_target(path, backend: Inspec::Backend.create(target: 'mock://'))
|
||||
@profile = Inspec::Profile.for_target(
|
||||
path,
|
||||
cache: Inspec::Cache.new(cache_path.to_s),
|
||||
backend: Inspec::Backend.create(target: 'mock://'),
|
||||
)
|
||||
end
|
||||
|
||||
def cache_path
|
||||
File.join(path, 'vendor')
|
||||
end
|
||||
|
||||
def inspec_lockfile
|
||||
File.join(path, 'inspec.lock')
|
||||
end
|
||||
|
||||
def verify_profile
|
||||
|
@ -92,6 +106,20 @@ module Habitat
|
|||
Habitat::Log.info('Profile is valid.')
|
||||
end
|
||||
|
||||
def vendor_profile_dependencies
|
||||
if File.exist?(inspec_lockfile) && Dir.exist?(cache_path)
|
||||
Habitat::Log.info("Profile's dependencies are already vendored, skipping vendor process.")
|
||||
else
|
||||
Habitat::Log.info("Vendoring the profile's dependencies...")
|
||||
FileUtils.rm_rf(cache_path)
|
||||
File.delete(inspec_lockfile) if File.exist?(inspec_lockfile)
|
||||
File.write(inspec_lockfile, profile.generate_lockfile.to_yaml)
|
||||
|
||||
# refresh the profile object since the profile now has new files
|
||||
create_profile_object
|
||||
end
|
||||
end
|
||||
|
||||
def validate_habitat_installed
|
||||
Habitat::Log.info('Checking to see if Habitat is installed...')
|
||||
cmd = Mixlib::ShellOut.new('hab --version')
|
||||
|
|
|
@ -24,7 +24,7 @@ module Fetchers
|
|||
# you got to this file during debugging, you may want to look at the
|
||||
# omnibus source for hints.
|
||||
#
|
||||
class Git < Inspec.fetcher(1)
|
||||
class Git < Inspec.fetcher(1) # rubocop:disable ClassLength
|
||||
name 'git'
|
||||
priority 200
|
||||
|
||||
|
@ -44,6 +44,8 @@ module Fetchers
|
|||
|
||||
def fetch(dir)
|
||||
@repo_directory = dir
|
||||
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
|
||||
|
||||
if cloned?
|
||||
checkout
|
||||
else
|
||||
|
|
|
@ -153,7 +153,9 @@ module Fetchers
|
|||
def download_archive(path)
|
||||
download_archive_to_temp
|
||||
final_path = "#{path}#{@archive_type}"
|
||||
FileUtils.mkdir_p(File.dirname(final_path))
|
||||
FileUtils.mv(temp_archive_path, final_path)
|
||||
FileUtils.chmod(0644, final_path)
|
||||
Inspec::Log.debug("Fetched archive moved to: #{final_path}")
|
||||
@temp_archive_path = nil
|
||||
final_path
|
||||
|
|
|
@ -17,6 +17,73 @@ describe Habitat::Profile do
|
|||
Habitat::Log.level(:fatal)
|
||||
end
|
||||
|
||||
describe '#verify_profile' do
|
||||
it 'exits if the profile is not valid' do
|
||||
profile = mock
|
||||
profile.stubs(:check).returns(summary: { valid: false })
|
||||
subject.expects(:profile).returns(profile)
|
||||
proc { subject.send(:verify_profile) }.must_raise SystemExit
|
||||
end
|
||||
|
||||
it 'does not exist if the profile is valid' do
|
||||
profile = mock
|
||||
profile.stubs(:check).returns(summary: { valid: true })
|
||||
subject.expects(:profile).returns(profile)
|
||||
subject.send(:verify_profile)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#vendor_profile_dependencies' do
|
||||
let(:cache) { '/path/to/cache' }
|
||||
let(:lockfile) { '/path/to/lock' }
|
||||
let(:profile) do
|
||||
profile = mock
|
||||
profile.stubs(:generate_lockfile).returns(foo: 'bar')
|
||||
profile
|
||||
end
|
||||
|
||||
before do
|
||||
subject.expects(:cache_path).at_least_once.returns(cache)
|
||||
subject.expects(:inspec_lockfile).at_least_once.returns(lockfile)
|
||||
end
|
||||
|
||||
describe 'when lockfile exists and cache dir exists' do
|
||||
it 'does not generate the lockfile' do
|
||||
File.expects(:exist?).with(lockfile).returns(true)
|
||||
Dir.expects(:exist?).with(cache).returns(true)
|
||||
subject.expects(:profile).returns(profile).never
|
||||
profile.expects(:generate_lockfile).never
|
||||
subject.send(:vendor_profile_dependencies)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when the lockfile exists but the cache dir does not' do
|
||||
it 'deletes the lockfile, generates the lockfile, and refreshes the profile object' do
|
||||
File.expects(:exist?).with(lockfile).returns(true).at_least_once
|
||||
Dir.expects(:exist?).with(cache).returns(false).at_least_once
|
||||
FileUtils.expects(:rm_rf).with(cache)
|
||||
File.expects(:delete).with(lockfile)
|
||||
subject.expects(:profile).returns(profile)
|
||||
File.expects(:write).with(lockfile, "---\n:foo: bar\n")
|
||||
subject.expects(:create_profile_object)
|
||||
|
||||
subject.send(:vendor_profile_dependencies)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when the lockfile does not exist' do
|
||||
it 'generates the lockfile, and refreshes the profile object' do
|
||||
File.expects(:exist?).with(lockfile).returns(false).at_least_once
|
||||
FileUtils.expects(:rm_rf).with(cache)
|
||||
subject.expects(:profile).returns(profile)
|
||||
File.expects(:write).with(lockfile, "---\n:foo: bar\n")
|
||||
subject.expects(:create_profile_object)
|
||||
|
||||
subject.send(:vendor_profile_dependencies)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#validate_habitat_installed' do
|
||||
it 'exits if hab --version fails' do
|
||||
cmd = mock
|
||||
|
|
Loading…
Reference in a new issue