Skip to content

Commit

Permalink
Merge pull request #224 from ashiqueps/CHEF-2616-license-command
Browse files Browse the repository at this point in the history
Implementation of the chef license commands
  • Loading branch information
ashiqueps authored Oct 23, 2024
2 parents 86243c3 + 2d0c47c commit b3630a0
Show file tree
Hide file tree
Showing 8 changed files with 344 additions and 0 deletions.
1 change: 1 addition & 0 deletions chef-cli.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ Gem::Specification.new do |gem|
gem.add_dependency "diff-lcs", ">= 1.0", "< 1.4" # 1.4 changes the output
gem.add_dependency "pastel", "~> 0.7" # used for policyfile differ
gem.add_dependency "license-acceptance", ">= 1.0.11", "< 3"
gem.add_dependency "chef-licensing", "~> 1.0"
end
2 changes: 2 additions & 0 deletions lib/chef-cli/builtin_commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,6 @@

c.builtin "describe-cookbook", :DescribeCookbook, require_path: "chef-cli/command/describe_cookbook",
desc: "Prints cookbook checksum information used for cookbook identifier"
c.builtin "license", :License, require_path: "chef-cli/command/license",
desc: "Create & install a new license on the system or view installed license(s)."
end
108 changes: 108 additions & 0 deletions lib/chef-cli/command/license.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# frozen_string_literal: true

# Copyright:: Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require_relative "base"
require "chef-cli/licensing/base"
require_relative "../configurable"

module ChefCLI
module Command

# This class will manage the license command in the chef-cli
class License < Base

include Configurable

MAIN_COMMAND_HELP = <<~HELP.freeze
Usage: #{ChefCLI::Dist::EXEC} license [SUBCOMMAND]
`#{ChefCLI::Dist::EXEC} license` command will validate the existing license
or will help you interactively generate new free/trial license and activate the
commercial license the chef team has sent you through email.
HELP

SUB_COMMANDS = [
{ name: "list", description: "List details of the license(s) installed on the system." },
{ name: "add", description: "Create & install a Free/ Trial license or install a Commercial license on the system." },
].freeze

option :chef_license_key,
long: "--chef-license-key LICENSE",
description: "New license key to accept and store in the system"

attr_accessor :ui

def self.banner
<<~BANNER
#{MAIN_COMMAND_HELP}
Subcommands:
#{SUB_COMMANDS.map do |c|
" #{c[:name].ljust(7)}#{c[:description]}"
end.join("\n") }
Options:
BANNER
end

def initialize
super

@ui = UI.new
end

def run(params)
config_license_debug if debug?
remaining_args = parse_options(params)
return 1 unless validate_params!(remaining_args)

if remaining_args.blank?
ChefCLI::Licensing::Base.validate
else
ChefCLI::Licensing::Base.send(remaining_args[0])
end
end

def debug?
!!config[:debug]
end

def validate_params!(args)
if args.length > 1
ui.err("Too many arguments")
return false
end

valid_subcommands = SUB_COMMANDS.collect { |c| c[:name] }
args.each do |arg|
next if valid_subcommands.include?(arg)

ui.err("Invalid option: #{arg}")
return false
end

true
end

private

def config_license_debug
ChefLicensing.output = ui
end
end
end
end
42 changes: 42 additions & 0 deletions lib/chef-cli/licensing/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

# Copyright:: Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require "chef-licensing"
require_relative "config"

module ChefCLI
module Licensing
class Base
class << self
def validate
ChefLicensing.fetch_and_persist.each do |license_key|
puts "License Key: #{license_key}"
end
end

def list
ChefLicensing.list_license_keys_info
end

def add
ChefLicensing.add_license
end
end
end
end
end
27 changes: 27 additions & 0 deletions lib/chef-cli/licensing/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

# Copyright:: Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require "chef-licensing"

ChefLicensing.configure do |config|
config.chef_product_name = "workstation"
config.chef_entitlement_id = "x6f3bc76-a94f-4b6c-bc97-4b7ed2b045c0"
config.chef_executable_name = "chef"
# config.license_server_url = "https://services.chef.io/licensing"
config.license_server_url = "https://licensing-acceptance.chef.co/License"
end
2 changes: 2 additions & 0 deletions spec/unit/cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ def mock_shell_out(exitstatus, stdout, stderr)

commands_map.builtin "example", :TestCommand, require_path: "unit/fixtures/command/cli_test_command",
desc: "Example subcommand for testing"

allow(ChefCLI::Licensing::Base).to receive(:validate).and_return(true)
end

context "given no arguments or options" do
Expand Down
162 changes: 162 additions & 0 deletions spec/unit/command/license_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#
# Copyright:: Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require "spec_helper"
require "chef-cli/command/license"
require "shared/command_with_ui_object"
require "chef-cli/licensing/base"

describe ChefCLI::Command::License do
it_behaves_like "a command with a UI object"

let(:params) { [] }
let(:ui) { TestHelpers::TestUI.new }

before do
# Disable the access of local licenses
allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:fetch_license_key_from_arg).and_return([])
allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:fetch_license_key_from_env).and_return([])
allow_any_instance_of(ChefLicensing::LicensingService::Local).to receive(:detected?).and_return(false)
end

let(:command) do
c = described_class.new
c.validate_params!(params)
c.ui = ui
c
end

it "disables debug by default" do
expect(command.debug?).to be(false)
end

context "invalid parameters passed" do
let(:multiple_params) { %w{add list} }
let(:invalid_command) { %w{not_a_subcommand} }

it "should fail with errors when multiple subcommands passed" do
expect(command.run(multiple_params)).to eq(1)
end

it "should fail for invalid argument" do
expect(command.run(invalid_command)).to eq(1)
end
end

context "license command" do
context "when pre-accepted license exists" do
let(:license_keys) { %w{tsmc-abcd} }

before(:each) do
allow(ChefLicensing).to receive(:fetch_and_persist).and_return(license_keys)
end

it "should be successful" do
expect { command.run(params) }.not_to raise_exception
end

it "should return the correct license key" do
expect(command.run(params)).to eq(license_keys)
end
end

context "when no licenses are accepted previously" do
let(:new_key) { ["tsmc-123456789"] }
before(:each) do
ChefLicensing.configure do |config|
config.license_server_url = "https://license.test"
config.chef_product_name = "chef"
config.chef_entitlement_id = "chef-entitled-id"
config.chef_executable_name = "chef"
end

# Disable the active license check
allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:licenses_active?).and_return(false)
# Disable the UI engine
allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:append_extra_info_to_tui_engine)
# Disable the API call to fetch the license type
allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:get_license_type).and_return("free")
# Disable the overwriting to the license.yml file
allow_any_instance_of(ChefLicensing::LicenseKeyFetcher::File).to receive(:persist)

# Mocks the user prompt to enter the license
allow_any_instance_of(ChefLicensing::LicenseKeyFetcher::Prompt).to receive(:fetch).and_return(new_key)
allow(ChefLicensing).to receive(:fetch_and_persist).and_return(new_key)
end

it "should create and stores the new license" do
expect { command.run(params) }.not_to raise_exception
end

it "should be same as the user entered license" do
expect(command.run(params)).to include(new_key.first)
end
end
end

context "chef license list command" do
let(:params) { %w{list} }
let(:license_key) { "tsmn-123123" }

before do
command.ui = ui
end

context "when no licenses are accepted" do
before do
allow_any_instance_of(ChefLicensing::ListLicenseKeys).to receive(:fetch_license_keys).and_return([])
allow_any_instance_of(ChefLicensing::ListLicenseKeys).to receive(:fetch_licenses_metadata).and_return([])
end

it "should return the correct error message" do
expect(command.run(params)).to eq([])
end
end

context "when there is a valid license" do
before do
allow(ChefLicensing).to receive(:list_license_keys_info).and_return(license_key)
end

it "should print the license details" do
expect(command.run(params)).to eq(license_key)
end
end
end

context "chef license add command" do
let(:params) { %w{add} }
let(:license_key) { ["tsmn-123123"] }

before do
# Disable the API call to fetch the license type
allow_any_instance_of(ChefLicensing::LicenseKeyFetcher).to receive(:get_license_type).and_return("free")
# Disable the overwriting to the license.yml file
allow_any_instance_of(ChefLicensing::LicenseKeyFetcher::File).to receive(:persist)
# Mocks the user prompt to enter the license
allow_any_instance_of(ChefLicensing::LicenseKeyFetcher::Prompt).to receive(:fetch).and_return(license_key)
end

it "should not raise any errors" do
expect { command.run(params) }.not_to raise_exception
end

it "should create and store the new license" do
expect(command.run(params)).to include(license_key.first)
end
end
end

0 comments on commit b3630a0

Please sign in to comment.