ChefSpec - issues when mocking encrypted_data_bag_secret

I have got an error that it actually got the encrypted_data_bag_secret and failed as I tried to mock it.

I specified this in the solo file when I run the Chef recipe:

# Chef/solo.rb
current_dir = File.expand_path(File.dirname(__FILE__))
encrypted_data_bag_secret "#{current_dir}/encrypted_data_bag_secret"

How could I mock this encrypted_data_bag_secret so it wont complain when I run the ChefSpec test in order to mock the secret to encrypt the databag and assign the value of the databag (key_vault) to the variable (key_vault_service_principal)?
The real encrypted_data_bag_secret file is in this directory: Chef/encrypted_data_bag_secret.

Could anyone please help me have a look to see what I have missed out? thanks so much

This is my recipe to retrieve the secret from encrypted data bag.

# Chef/cookbooks/myCookbook/recipes/certificates.rb
key_vault = Chef::EncryptedDataBagItem.load("key_vault", node['key_vault_data_bag'])

key_vault_service_principal = {
  'tenant_id' => key_vault["tenant_id"],
  'client_id' => key_vault["client_id"],
  'secret' => key_vault["key_vault_secret"]
}

node['certificates'].each do |cert_name,cert_properties|
  # Download the cert from KeyVault
  download_azure_key_vault_certificate cert_name do
    action                    :create
    sensitive                 true
    file_download_directory   temp_cert_directory
    certificate_file_name     "#{cert_properties['key_vault_secret_name']}.#{cert_properties['cert_type']}"
    azure_service_principal   key_vault_service_principal
    azure_key_vault_name      key_vault["key_vault_name"]
    azure_certificate_name    cert_properties['key_vault_secret_name']
    expected_thumbprint       cert_properties['thumbprint']
  end

end

This is my ChefSpec test for the above recipe:

# Chef/cookbooks/myCookbook/spec/certificates_spec.rb
require 'spec_helper'

certificates = {
    "Test Certificate": {
        key_vault_secret_name: "test-cert",
        cert_type: "pfx",
        thumbprint: "6C286860F0A79FD0b27343b464F84517AAC5F757"
    }
}

key_vault_service_principal = {
  'tenant_id' => '123456',
  'client_id' => '123456',
  'secret' => '123456'
}

describe 'enett_common::certificates' do

    let(:chef_run) do
        ChefSpec::SoloRunner.new do |node|
            node.default['certificates'] = certificates
            node.default['key_vault_data_bag'] = 'test'   
        end.converge(described_recipe)
    end

    before do
        secret = Chef::EncryptedDataBagItem.load_secret("../../encrypted_data_bag_secret")
        allow(Chef::EncryptedDataBagItem).to receive(:load).with('key_vault', 'test', secret).and_return({
            'test' => {
                'client_id' => '123456',
                'key_vault_name' => 'test',
                'key_vault_secret' => '123456',
                'tenant_id' => '123456'
              }
            })
    end

    certificates.each do |cert_name,cert_properties|
        it 'downloads the cert from KeyVault' do
            expect(chef_run).to create_download_azure_key_vault_certificate(cert_name).with(
                sensitive:                 true,
                file_download_directory:   temp_cert_directory,
                certificate_file_name:     "#{cert_properties[:key_vault_secret_name]}.#{cert_properties[:cert_type]}",
                azure_service_principal:   key_vault_service_principal,
                azure_key_vault_name:      "test_key_vault",
                azure_certificate_name:    cert_properties[:key_vault_secret_name],
                expected_thumbprint:       cert_properties[:thumbprint]
            )
        end
    end
end

What error are you getting?

At a glance, you are providing three arguments to your "allow" but the load method only takes two:

allow(Chef::EncryptedDataBagItem).to receive(:load).with('key_vault', 'test', secret).and_return('something')

should just be

allow(Chef::EncryptedDataBagItem).to receive(:load).with('key_vault', 'test').and_return('something')

If your allow does not match the call you are actually making, it will not be triggered.

1 Like

Thanks for your reply, I realised and fixed that earlier but I now got another weirld error without much information returned for dignostic.

The error I got is this. It just didnt give me much info like which specific part it failed, it's so hard to figure out what's wrong, but I am sure that all the arguments are filled as in its recipe. Thanks!

expected "download_azure_key_vault_certificate[Dev Certificate test]" with action :create to be in Chef run. Other download_azure_key_vault_certificate resources:

download_azure_key_vault_certificate[Dev Certificate test]

I changed my code to:

require 'spec_helper'

certificates = {
    'Dev Certificate test': {
        key_vault_secret_name: "dev-cert-test",
        cert_type: "pfx",
        thumbprint: "64A79C64F84b4686A5FD0bF7573"
    }
}

key_vault_service_principal = {
  tenant_id: '123456',
  client_id: '123456',
  secret: '123456'
}

describe 'enett_common::certificates' do

    let(:chef_run) do
        ChefSpec::SoloRunner.new do |node|
            node.default['certificates'] = certificates
            node.default['key_vault_data_bag'] = 'test'         
        end.converge(described_recipe)
    end

    before do
        allow(Chef::EncryptedDataBagItem).to receive(:load).with('key_vault', 'test').and_return({
            'client_id' => '123456',
            'key_vault_name' => 'test',
            'key_vault_secret' => '123456',
            'tenant_id' => '123456'
        })
    end

    certificates.each do |cert_name,cert_properties|
        it "creates and downloads the cert from KeyVault" do
            expect(chef_run).to create_download_azure_key_vault_certificate(cert_name).with(
                sensitive:                 true,
                file_download_directory:   temp_cert_directory,
                certificate_file_name:     "#{cert_properties[:key_vault_secret_name]}.#{cert_properties[:cert_type]}",
                azure_service_principal:   key_vault_service_principal,
                azure_key_vault_name:      "test",
                azure_certificate_name:    cert_properties[:key_vault_secret_name],
                expected_thumbprint:       cert_properties[:thumbprint]
            )
        end
    end
end