From ff68fd07a9fbcd5a7de89b30d3669819c4af8ed7 Mon Sep 17 00:00:00 2001 From: Jason Barnett Date: Tue, 5 Oct 2021 11:29:13 -0600 Subject: Add support for default secret service Signed-off-by: Jason Barnett --- spec/unit/dsl/secret_spec.rb | 150 +++++++++++++++++++++++++++++++++++------- spec/unit/run_context_spec.rb | 16 +++++ 2 files changed, 143 insertions(+), 23 deletions(-) (limited to 'spec') diff --git a/spec/unit/dsl/secret_spec.rb b/spec/unit/dsl/secret_spec.rb index 96a915c43d..9952980697 100644 --- a/spec/unit/dsl/secret_spec.rb +++ b/spec/unit/dsl/secret_spec.rb @@ -17,11 +17,14 @@ # require "spec_helper" +require "chef/exceptions" require "chef/dsl/secret" require "chef/secret_fetcher/base" + class SecretDSLTester include Chef::DSL::Secret - # Because DSL is invoked in the context of a recipe, + + # Because DSL is invoked in the context of a recipe or attribute file # we expect run_context to always be available when SecretFetcher::Base # requests it - making it safe to mock here def run_context @@ -37,35 +40,136 @@ end describe Chef::DSL::Secret do let(:dsl) { SecretDSLTester.new } - it "responds to 'secret'" do - expect(dsl.respond_to?(:secret)).to eq true + let(:run_context) { Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new) } + + before do + allow(dsl).to receive(:run_context).and_return(run_context) end - it "uses SecretFetcher.for_service to find the fetcher" do - substitute_fetcher = SecretFetcherImpl.new({}, nil) - expect(Chef::SecretFetcher).to receive(:for_service).with(:example, {}, nil).and_return(substitute_fetcher) - expect(substitute_fetcher).to receive(:fetch).with("key1", nil) - dsl.secret(name: "key1", service: :example, config: {}) + %w{ + secret + default_secret_service + default_secret_config + with_secret_service + with_secret_config + }.each do |m| + it "responds to ##{m}" do + expect(dsl.respond_to?(m)).to eq true + end + end + + describe "#default_secret_service" do + let(:service) { :hashi_vault } + + it "persists the service passed in as an argument" do + expect(dsl.default_secret_service).to eq(nil) + dsl.default_secret_service(service) + expect(dsl.default_secret_service).to eq(service) + end + + it "returns run_context.default_secret_service value when no argument is given" do + run_context.default_secret_service = :my_thing + expect(dsl.default_secret_service).to eq(:my_thing) + end + + it "raises exception when service given is not valid" do + stub_const("Chef::SecretFetcher::SECRET_FETCHERS", %i{service_a service_b}) + expect { dsl.default_secret_service(:unknown_service) }.to raise_error(Chef::Exceptions::Secret::InvalidFetcherService) + end end - it "resolves a secret when using the example fetcher" do - secret_value = dsl.secret(name: "test1", service: :example, config: { "test1" => "secret value" }) - expect(secret_value).to eq "secret value" + describe "#with_secret_config" do + let(:service) { :hashi_vault } + + it "sets the service for the scope of the block only" do + expect(dsl.default_secret_service).to eq(nil) + dsl.with_secret_service(service) do + expect(dsl.default_secret_service).to eq(service) + end + expect(dsl.default_secret_service).to eq(nil) + end + + it "raises exception when block is not given" do + expect { dsl.with_secret_service(service) }.to raise_error(ArgumentError) + end end - context "when used within a resource" do - let(:run_context) { - Chef::RunContext.new(Chef::Node.new, - Chef::CookbookCollection.new(Chef::CookbookLoader.new(File.join(CHEF_SPEC_DATA, "cookbooks"))), - Chef::EventDispatch::Dispatcher.new) - } - - it "marks that resource as 'sensitive'" do - recipe = Chef::Recipe.new("secrets", "test", run_context) - recipe.zen_master "secret_test" do - peace secret(name: "test1", service: :example, config: { "test1" => true }) + describe "#default_secret_config" do + let(:config) { { my_key: "value" } } + + it "persists the config passed in as argument" do + expect(dsl.default_secret_config).to eq({}) + dsl.default_secret_config(**config) + expect(dsl.default_secret_config).to eq(config) + end + + it "returns run_context.default_secret_config value when no argument is given" do + run_context.default_secret_config = { my_thing: "that" } + expect(dsl.default_secret_config).to eq({ my_thing: "that" }) + end + end + + describe "#with_secret_config" do + let(:config) { { my_key: "value" } } + + it "sets the config for the scope of the block only" do + expect(dsl.default_secret_config).to eq({}) + dsl.with_secret_config(**config) do + expect(dsl.default_secret_config).to eq(config) end - expect(run_context.resource_collection.lookup("zen_master[secret_test]").sensitive).to eql(true) + expect(dsl.default_secret_config).to eq({}) + end + + it "raises exception when block is not given" do + expect { dsl.with_secret_config(**config) }.to raise_error(ArgumentError) + end + end + + describe "#secret" do + it "uses SecretFetcher.for_service to find the fetcher" do + substitute_fetcher = SecretFetcherImpl.new({}, nil) + expect(Chef::SecretFetcher).to receive(:for_service).with(:example, {}, run_context).and_return(substitute_fetcher) + expect(substitute_fetcher).to receive(:fetch).with("key1", nil) + dsl.secret(name: "key1", service: :example, config: {}) + end + + it "resolves a secret when using the example fetcher" do + secret_value = dsl.secret(name: "test1", service: :example, config: { "test1" => "secret value" }) + expect(secret_value).to eq "secret value" + end + + context "when used within a resource" do + let(:run_context) { + Chef::RunContext.new(Chef::Node.new, + Chef::CookbookCollection.new(Chef::CookbookLoader.new(File.join(CHEF_SPEC_DATA, "cookbooks"))), + Chef::EventDispatch::Dispatcher.new) + } + + it "marks that resource as 'sensitive'" do + recipe = Chef::Recipe.new("secrets", "test", run_context) + recipe.zen_master "secret_test" do + peace secret(name: "test1", service: :example, config: { "test1" => true }) + end + expect(run_context.resource_collection.lookup("zen_master[secret_test]").sensitive).to eql(true) + end + end + + it "passes default service to SecretFetcher.for_service" do + service = :example + dsl.default_secret_service(service) + substitute_fetcher = SecretFetcherImpl.new({}, nil) + expect(Chef::SecretFetcher).to receive(:for_service).with(service, {}, run_context).and_return(substitute_fetcher) + allow(substitute_fetcher).to receive(:fetch).with("key1", nil) + dsl.secret(name: "key1") + end + + it "passes default config to SecretFetcher.for_service" do + config = { my_config: "value" } + dsl.default_secret_config(**config) + substitute_fetcher = SecretFetcherImpl.new({}, nil) + expect(Chef::SecretFetcher).to receive(:for_service).with(:example, config, run_context).and_return(substitute_fetcher) + allow(substitute_fetcher).to receive(:fetch).with("key1", nil) + dsl.secret(name: "key1", service: :example) end end end diff --git a/spec/unit/run_context_spec.rb b/spec/unit/run_context_spec.rb index 5bd7a360fb..3ec8eeed61 100644 --- a/spec/unit/run_context_spec.rb +++ b/spec/unit/run_context_spec.rb @@ -53,6 +53,22 @@ describe Chef::RunContext do expect(run_context.node).to eq(node) end + it "responds to #default_secret_service" do + expect(run_context).to respond_to(:default_secret_service) + end + + it "responds to #default_secret_config" do + expect(run_context).to respond_to(:default_secret_config) + end + + it "#default_secret_service defaults to nil" do + expect(run_context.default_secret_service).to eq(nil) + end + + it "#default_secret_config defaults to {}" do + expect(run_context.default_secret_config).to eq({}) + end + it "loads up node[:cookbooks]" do expect(run_context.node[:cookbooks]).to eql( { -- cgit v1.2.1