Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CHEF-3954 Extended config block usage - Enabling kitchen usage with chef licensing #153

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions components/ruby/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,16 @@ ChefLicensing.configure do |config|
config.chef_product_name = "chef"
config.chef_executable_name = "inspec"
config.chef_entitlement_id = "chef123"
config.chef_license_key="free-t0832est-1111-1te1-b1e5-011t182test3-111"
config.license_server_url_in_config_file = true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can use set_license_server_url_fron_config_file or 'use_license_server_url_from_config_file`

config.logger = Logger.new($stdout)
end

```
where:

- `chef_license_key`: the unique License key which validates software entitlement to the Chef's Software.
- `license_server_url_in_config_file`: a boolean flag set when license server url to be used from config block.

<!-- Usage section contains all the methods that the client would invoke while using the Chef Licensing Library -->
## Usage
Expand Down
2 changes: 2 additions & 0 deletions components/ruby/lib/chef-licensing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ def check_feature_entitlement!(feature_name: nil, feature_id: nil)
end

def check_software_entitlement!
raise LicenseKeyFetcher::LicenseKeyNotFetchedError.new("Unable to obtain a License Key.") if license_keys.empty?

# If API call is not breaking that means license is entitled.
client(license_keys: license_keys)
true
Expand Down
2 changes: 1 addition & 1 deletion components/ruby/lib/chef-licensing/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class << self
attr_writer :license_server_url, :logger, :output, :license_server_url_check_in_file

# is_local_license_service is used by context class
attr_accessor :is_local_license_service, :chef_entitlement_id, :chef_product_name, :chef_executable_name
attr_accessor :is_local_license_service, :chef_entitlement_id, :chef_product_name, :chef_executable_name, :chef_license_key, :license_server_url_in_config_file

def license_server_url(opts = {})
return @license_server_url if @license_server_url && @license_server_url_check_in_file
Expand Down
18 changes: 16 additions & 2 deletions components/ruby/lib/chef-licensing/license_key_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ def perform_on_prem_operations
end

def perform_global_operations
logger.debug "License Key fetcher examining config values"
new_keys = fetch_license_key_from_config
license_type = validate_and_fetch_license_type(new_keys)
if license_type && !unrestricted_license_added?(new_keys, license_type)
# break the flow after the prompt if there is a restriction in adding license
# and return the license keys persisted in the file or @license_keys if any
return license_keys
end

logger.debug "License Key fetcher examining CLI arg checks"
new_keys = fetch_license_key_from_arg
license_type = validate_and_fetch_license_type(new_keys)
Expand Down Expand Up @@ -161,11 +170,11 @@ def add_license
end
end

# Note: Fetching from arg and env as well, to be able to fetch license when disk is non-writable
# Note: Fetching from arg, env and config as well, to be able to fetch license when disk is non-writable
def fetch
# While using on-prem licensing service, @license_keys have been fetched from API
# While using global licensing service, @license_keys have been fetched from file
(fetch_license_key_from_arg << fetch_license_key_from_env << @license_keys).flatten.uniq
(fetch_license_key_from_arg << fetch_license_key_from_env << fetch_license_key_from_config << @license_keys).flatten.uniq
end

def self.fetch_and_persist(opts = {})
Expand Down Expand Up @@ -259,6 +268,11 @@ def fetch_license_key_from_env
validate_license_key_format(new_key)
end

def fetch_license_key_from_config
new_key = ChefLicensing::Config.chef_license_key
validate_license_key_format(new_key)
end

def validate_license_key_format(license_key)
return [] if license_key.nil?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ def fetch_or_persist_url(license_server_url_from_config, license_server_url_from
load_basic_license_data_to_contents(url, [])
elsif @contents && license_server_url_from_system && license_server_url_from_system != @contents[:license_server_url]
@contents[:license_server_url] = license_server_url_from_system
elsif @contents && ChefLicensing::Config.license_server_url_in_config_file && (license_server_url_from_config && license_server_url_from_config != @contents[:license_server_url])
@contents[:license_server_url] = license_server_url_from_config
end

# Ensure the license server URL is returned to the caller in all cases
Expand Down
29 changes: 29 additions & 0 deletions components/ruby/spec/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,34 @@
ARGV.clear
end
end

context "updating values in licenses.yaml file for license server url via config" do
let(:temp_dir) { Dir.mktmpdir }
let(:original_file) { "spec/fixtures/license_file_with_server_url/licenses.yaml" }

before do
FileUtils.cp(original_file, "#{temp_dir}/licenses.yaml")
ChefLicensing.configure do |config|
config.license_server_url_check_in_file = false
config.license_server_url_in_config_file = true
config.license_server_url = "https://custom-licensing-server-2.com/License"
end
end

let(:opts) {
{
dir: "#{temp_dir}",
}
}

it "updates the value in licenses.yaml file" do
# load the original file first and check the value
expect(YAML.load_file("#{temp_dir}/licenses.yaml")[:license_server_url]).to eq("https://custom-licensing-server.com/License")
# this will update the value in licenses.yaml file
expect(ChefLicensing::Config.license_server_url(opts)).to eq("https://custom-licensing-server-2.com/License")
# load the file again and check the value
expect(YAML.load_file("#{temp_dir}/licenses.yaml")[:license_server_url]).to eq("https://custom-licensing-server-2.com/License")
end
end
end
end
160 changes: 160 additions & 0 deletions components/ruby/spec/license_key_fetcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,163 @@
end
end

context "the license is set via config" do
let(:license_keys) { ["tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150"] }
let(:argv) { [] }
let(:env) { {} }
before do
ChefLicensing.configure do |config|
config.chef_license_key = "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150"
end
ChefLicensing::Context.current_context = nil
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/listLicenses")
.to_return(body: { data: [], status_code: 404 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/validate")
.with(query: { licenseId: "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150", version: api_version })
.to_return(body: { data: true, message: "License Id is valid", status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/client")
.with(query: { licenseId: "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150", entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: client_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/client")
.with(query: { licenseId: license_keys.join(","), entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: client_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/desc")
.with(query: { licenseId: "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150", entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: describe_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
end
Dir.mktmpdir do |tmpdir|
let(:opts) {
{
logger: logger,
argv: argv,
env: env,
output: output,
dir: tmpdir,
}
}

let(:license_key_fetcher) { described_class.new(opts) }
it "returns license key set using config" do
expect(license_key_fetcher.fetch_and_persist).to eq(["tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150"])
end
end
end

context "the license is set via env and config" do
let(:argv) { [] }
let(:env) { { "CHEF_LICENSE_KEY" => "free-c0832d2d-1111-1ec1-b1e5-011d182dc341-111" } }
let(:license_keys) {
%w{tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150 free-c0832d2d-1111-1ec1-b1e5-011d182dc341-111}
}
before do
ChefLicensing.configure do |config|
config.is_local_license_service = nil
config.chef_license_key = "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150"
end
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/validate")
.with(query: { licenseId: "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150", version: api_version })
.to_return(body: { data: true, message: "License Id is valid", status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/validate")
.with(query: { licenseId: "free-c0832d2d-1111-1ec1-b1e5-011d182dc341-111", version: api_version })
.to_return(body: { data: true, message: "License Id is valid", status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/client")
.with(query: { licenseId: "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150", entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: client_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/client")
.with(query: { licenseId: "free-c0832d2d-1111-1ec1-b1e5-011d182dc341-111", entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: client_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/client")
.with(query: { licenseId: license_keys.join(","), entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: client_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/desc")
.with(query: { licenseId: "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150", entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: describe_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
end

Dir.mktmpdir do |tmpdir|
let(:opts) {
{
logger: logger,
argv: argv,
env: env,
output: output,
dir: tmpdir,
}
}

let(:license_key_fetcher) { described_class.new(opts) }
it "returns only trial set in config and not free due to active trial restriction" do
expect(license_key_fetcher.fetch_and_persist).to eq(%w{tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150})
end
end
end

context "the license is set via argument and config" do
let(:env) { {} }
let(:argv) { ["--chef-license-key=free-c0832d2d-1111-1ec1-b1e5-011d182dc341-111"] }
let(:license_keys) {
%w{tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150 free-c0832d2d-1111-1ec1-b1e5-011d182dc341-111}
}
before do
ChefLicensing.configure do |config|
config.is_local_license_service = nil
config.chef_license_key = "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150"
end
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/validate")
.with(query: { licenseId: "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150", version: api_version })
.to_return(body: { data: true, message: "License Id is valid", status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/validate")
.with(query: { licenseId: "free-c0832d2d-1111-1ec1-b1e5-011d182dc341-111", version: api_version })
.to_return(body: { data: true, message: "License Id is valid", status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/client")
.with(query: { licenseId: "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150", entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: client_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/client")
.with(query: { licenseId: "free-c0832d2d-1111-1ec1-b1e5-011d182dc341-111", entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: client_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/client")
.with(query: { licenseId: license_keys.join(","), entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: client_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/desc")
.with(query: { licenseId: "tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150", entitlementId: ChefLicensing::Config.chef_entitlement_id })
.to_return(body: { data: describe_api_data, status_code: 200 }.to_json,
headers: { content_type: "application/json" })
end

Dir.mktmpdir do |tmpdir|
let(:opts) {
{
logger: logger,
argv: argv,
env: env,
output: output,
dir: tmpdir,
}
}

let(:license_key_fetcher) { described_class.new(opts) }
it "returns only trial set in config and not free due to active trial restriction" do
expect(license_key_fetcher.fetch_and_persist).to eq(%w{tmns-0f76efaf-b45b-4d92-86b2-2d144ce73dfa-150})
end
end
end

context "multiple free licenses are restricted and can only add one free license in file" do
let(:argv) { ["--chef-license-key=free-c0832d2d-1111-1ec1-b1e5-011d182dc341-112"] }

Expand Down Expand Up @@ -343,6 +500,9 @@
}

before do
ChefLicensing.configure do |config|
config.chef_license_key = nil
end
stub_request(:get, "#{ChefLicensing::Config.license_server_url}/v1/validate")
.with(query: { licenseId: "free-c0832d2d-1111-1ec1-b1e5-011d182dc341-111", version: api_version })
.to_return(body: { data: true, message: "License Id is valid", status_code: 200 }.to_json,
Expand Down