Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 0 additions & 137 deletions components/ruby/Gemfile.lock

This file was deleted.

2 changes: 1 addition & 1 deletion components/ruby/chef-licensing.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
spec.add_dependency "tty-prompt", "~> 0.23"
spec.add_dependency "faraday", ">= 1", "< 2"
spec.add_dependency "faraday-http-cache"
spec.add_dependency "activesupport", "~> 7.2", ">= 7.2.2.1"
spec.add_dependency "activesupport", "> 7.1.3.2", "< 7.2"
spec.add_dependency "tty-spinner", "~> 0.9.3"
spec.add_dependency "mixlib-log", "~> 3.0"

Expand Down
30 changes: 23 additions & 7 deletions components/ruby/lib/chef-licensing/license_key_fetcher/file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,35 @@ def fetch_or_persist_url(license_server_url_from_config, license_server_url_from

@contents = load_license_file(license_key_file_path)

# Three possible cases:
# 1. If contents is nil or an error occurred while loading, load basic license data with the latest structure.
# 2. If contents is not nil and valid, but the license server URL in contents is different from the system's,
# update the license server URL in contents and licenses.yaml file.
# 3. If contents is valid and no update is needed, return the existing license server URL.
# Allow developers to opt-out of using system-provided license server URLs
# (for example CI/developer envs that set CHEF_LICENSE_SERVER). When the
# env var CHEF_LICENSE_IGNORE_SYSTEM_LICENSE_SERVER is set we will not
# consider the system value for persisting into the licenses.yaml file.
if ENV["CHEF_LICENSE_IGNORE_SYSTEM_LICENSE_SERVER"]
logger.debug("Ignoring system-provided license server URL due to CHEF_LICENSE_IGNORE_SYSTEM_LICENSE_SERVER") if logger
license_server_url_from_system = nil
end

# Decide when to update the license_server_url persisted in licenses.yaml.
# Priority (highest -> lowest):
# 1. Explicit config/CLI value (license_server_url_from_config)
# 2. Existing value in the file (do not let system/env override it)
# 3. System/env value (license_server_url_from_system) only if file is absent
# Handle error cases first - when file loading failed or contents is nil
if @contents.is_a?(StandardError) || @contents.nil?
# No existing file or failed to load: prefer system value but fall back to config
url = license_server_url_from_system || license_server_url_from_config
load_basic_license_data_to_contents(url, [])
elsif @contents && license_server_url_from_system && license_server_url_from_system != @contents[:license_server_url]
elsif license_server_url_from_config && license_server_url_from_config != @contents[:license_server_url]
# An explicit config/arg should overwrite the persisted value
@contents[:license_server_url] = license_server_url_from_config
elsif license_server_url_from_system && license_server_url_from_system != @contents[:license_server_url]
# If a system-provided value (ENV/CLI) is present and differs from the
# persisted value, treat it as an explicit override unless the caller
# has intentionally set CHEF_LICENSE_IGNORE_SYSTEM_LICENSE_SERVER.
@contents[:license_server_url] = license_server_url_from_system
else
# Nothing to change in the file
# Nothing to change in the file: return the existing persisted value
@license_server_url = @contents[:license_server_url]
return @license_server_url
end
Expand Down
1 change: 1 addition & 0 deletions components/ruby/spec/config_spec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require "spec_helper"
require "chef-licensing/config"
require "logger"
require "stringio"
Expand Down
81 changes: 80 additions & 1 deletion components/ruby/spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,92 @@
require "bundler/setup"
require "chef-licensing"
require "tmpdir"
require "fileutils"

# Create a suite-level temporary HOME early so any code executed at
# require-time that consults the user's HOME doesn't accidentally pick up
# a real developer/CI HOME. We use a lightweight suite-level HOME to
# avoid unnecessary temp-dir churn during startup. Some specs still need
# a clean HOME per-example (they create or expect files under HOME); to
# support those we provide a per-example around hook below which swaps
# in a fresh HOME for the duration of the example.
TMP_TEST_HOME = Dir.mktmpdir("chef_licensing_spec_home")
ENV["HOME"] = TMP_TEST_HOME
# On Windows set USERPROFILE as well for early initialization
ENV["USERPROFILE"] = TMP_TEST_HOME if Gem.win_platform?

# Explicitly set the license server used by tests to a stable, test-only
# URL. This prevents tests from accidentally using a developer/CI
# provided value or reading an on-disk persisted value that points at a
# production server. Keep this deterministic to make tests hermetic.
ENV["CHEF_LICENSE_SERVER"] = "https://custom-licensing-server.com/License"
ENV["LICENSE_SERVER"] = "https://custom-licensing-server.com/License"

# Clear any real license key from the environment so tests don't pick up
# developer/CI credentials by accident. This avoids flaky tests where a
# real key would change behavior or reach out to real endpoints.
ENV.delete("CHEF_LICENSE_KEY")

# Require WebMock before requiring the library so that any HTTP client
# initialization that runs during require-time (for example middleware
# setup that probes a host) is intercepted. Requiring WebMock early is
# the simplest way to guarantee no accidental real HTTP connections
# happen while the test harness initializes.
require "webmock/rspec"
require "chef-licensing"

RSpec.configure do |config|
# We set a suite-level TMP_TEST_HOME above to keep startup fast and to
# prevent require-time code from touching a real HOME. Some specs,
# however, depend on a clean HOME per-example to test file persistence
# and migrations under the user's home directory. To keep those specs
# hermetic we swap in a fresh HOME for each example using this around
# hook. This balances performance (single suite-level HOME) with test
# correctness for file-based scenarios.
config.around(:each) do |example|
original_home = ENV["HOME"]
original_userprofile = ENV["USERPROFILE"]
tmp_home = Dir.mktmpdir("chef_licensing_spec_example_home")
ENV["HOME"] = tmp_home
ENV["USERPROFILE"] = tmp_home if Gem.win_platform?
begin
example.run
ensure
ENV["HOME"] = original_home
ENV["USERPROFILE"] = original_userprofile
FileUtils.remove_entry_secure(tmp_home) if ::File.exist?(tmp_home)
end
end

# Clear cached configuration in the library before each example to avoid
# state leaking between tests. Many specs mutate `ChefLicensing::Config`
# (for example set a license_server_url or change logging/output) and
# rely on a clean slate. Reset only known, writable attributes here as
# a best-effort approach; this keeps examples deterministic without
# tightly coupling to private internals.
config.before(:each) do
if defined?(ChefLicensing::Config)
begin
ChefLicensing::Config.license_server_url = nil
ChefLicensing::Config.license_server_url_check_in_file = false
ChefLicensing::Config.logger = nil
ChefLicensing::Config.output = nil
ChefLicensing::Config.make_licensing_optional = false
ChefLicensing::Config.is_local_license_service = nil
ChefLicensing::Config.chef_entitlement_id = nil
ChefLicensing::Config.chef_product_name = nil
ChefLicensing::Config.chef_executable_name = nil
rescue StandardError
# best-effort reset; if some writers are missing ignore and continue
end
end
end

# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = ".rspec_status"

# Disable RSpec exposing methods globally on `Module` and `main`
config.disable_monkey_patching!
require "fileutils"

config.expect_with :rspec do |c|
c.syntax = :expect
Expand Down
Loading