Skip to content

Commit

Permalink
Support 'clear_cache'
Browse files Browse the repository at this point in the history
Helps with Customer Bug 236 (see there for repro)

Currently, when we run `inspec compliance upload my_profile` it is
cached locally in inspec when run. If we update the version in the core
code and run another upload, `inspec compliance upload my_profile` again
it will run the old cached version instead of running a new copy
from automate.

The current workaround is to specify the desired version with
`inspec exec compliance://my_profile/admin#0.1.1`.

The caching happens before we have forward sight into the profile's
contents and only the target name. So the text used to generate the
cache would be `compliance://my_profile/admin` which does not change
version to version.

A fix here could simply identify when we are doing a local `inspec exec
compliance://` (hitting local profiles does not generate a cache) and
skips the cache if there's no version specified. That would eliminate
the unexpected behavior. However, it is a breaking change for customers
as some current caching taking place would no longer take place.

Instead, we have included a `clear_cache` cli method for InSpec,
which should assist the core team and other developers in the future
when debugging edge case issues in InSpec.

Signed-off-by: Nick Schwaderer <nschwaderer@chef.io>
  • Loading branch information
Nick Schwaderer committed Nov 24, 2020
1 parent 38971c7 commit 6157dd6
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 7 deletions.
14 changes: 14 additions & 0 deletions lib/inspec/cli.rb
Expand Up @@ -393,6 +393,20 @@ def version
end
map %w{-v --version} => :version

desc "clear_cache", "clears the InSpec cache. Useful for debugging."
option :vendor_cache, type: :string,
desc: "Use the given path for caching dependencies. (default: ~/.inspec/cache)"
def clear_cache
o = config
configure_logger(o)
cache_path = o[:vendor_cache] || "~/.inspec/cache"
FileUtils.rm_r Dir.glob(File.expand_path(cache_path))

o[:logger] = Logger.new($stdout)
o[:logger].level = get_log_level(o[:log_level])
o[:logger].info "== InSpec cache cleared successfully =="
end

private

def run_command(opts)
Expand Down
2 changes: 1 addition & 1 deletion lib/inspec/runner_rspec.rb
Expand Up @@ -5,7 +5,7 @@
require "inspec/rspec_extensions"

# There be dragons!! Or borgs, or something...
# This file and all its contents cannot be unit-tested. both test-suits
# This file and all its contents cannot be unit-tested. both test-suites
# collide and disable all unit tests that have been added.

module Inspec
Expand Down
27 changes: 27 additions & 0 deletions test/functional/inspec_clear_cache_test.rb
@@ -0,0 +1,27 @@
require "functional/helper"
require "securerandom"

describe "inspec check" do
include FunctionalHelper

parallelize_me!

describe "inspec clear_cache" do
it "clears any existing cache" do
dirname = File.expand_path("~/.inspec/cache")
unless File.directory?(dirname)
FileUtils.mkdir_p(dirname)
end
newfile = "#{dirname}/#{SecureRandom.hex(10)}.txt"
File.write(newfile, SecureRandom.hex(100))

assert !Dir.glob(newfile).empty?

out = inspec("clear_cache")

assert_empty Dir.glob(newfile)
assert_exit_code 0, out
_(out.stdout).must_include "== InSpec cache cleared successfully ==\n"
end
end
end
36 changes: 30 additions & 6 deletions test/unit/cached_fetcher_test.rb
Expand Up @@ -25,8 +25,22 @@
"inputs" => [],
"latest_version" => "" }]
end

before do
InspecPlugins::Compliance::Configuration.expects(:new).returns({ "token" => "123abc", "server" => "https://a2.instance.com" })

@stub_get =
stub_request(
:get,
"https://a2.instance.com/owners/admin/compliance/ssh-baseline/tar"
).with(
headers: {
"Accept" => "*/*",
"Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"Authorization" => "Bearer 123abc",
"User-Agent" => "Ruby",
}
).to_return(status: 200, body: "", headers: {})
end

it "downloads the profile from the compliance service when sha256 not in the cache" do
Expand All @@ -44,19 +58,29 @@
mock_fetch.verify
end

it "does not download the profile when the sha256 exists in the inspec cache" do
it "does not download the profile when the sha256 exists in the inspec cache if version is specified" do
prof = profiles_result[0]
InspecPlugins::Compliance::API.stubs(:profiles).returns(["success", profiles_result])
cache = Inspec::Cache.new
entry_path = cache.base_path_for(prof["sha256"])
mock_prefered_entry_for = Minitest::Mock.new
mock_prefered_entry_for.expect :call, entry_path, [prof["sha256"]]
cf = Inspec::CachedFetcher.new("compliance://#{prof["owner"]}/#{prof["name"]}", cache)
mock_preferred_entry_for = Minitest::Mock.new
mock_preferred_entry_for.expect :call, entry_path, [prof["sha256"]]
cf = Inspec::CachedFetcher.new("compliance://#{prof["owner"]}/#{prof["name"]}#0.1.1", cache)
cache.stubs(:exists?).with(prof["sha256"]).returns(true)
cache.stub(:prefered_entry_for, mock_prefered_entry_for) do
cache.stub(:preferred_entry_for, mock_preferred_entry_for) do
cf.fetch
end
mock_prefered_entry_for.verify
mock_preferred_entry_for.verify
assert_not_requested(@stub_get)
end

it "skips caching on compliance if version unspecified" do
prof = profiles_result[0]
InspecPlugins::Compliance::API.stubs(:profiles).returns(["success", profiles_result])
cache = Inspec::Cache.new
cf = Inspec::CachedFetcher.new("compliance://#{prof["owner"]}/#{prof["name"]}", cache)
cf.fetch
assert_requested(@stub_get)
end
end
end

0 comments on commit 6157dd6

Please sign in to comment.