chef-licensing 0.4.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/chef-licensing.gemspec +35 -0
  4. data/lib/chef-licensing/api/client.rb +39 -0
  5. data/lib/chef-licensing/api/describe.rb +62 -0
  6. data/lib/chef-licensing/api/license_feature_entitlement.rb +55 -0
  7. data/lib/chef-licensing/api/license_software_entitlement.rb +53 -0
  8. data/lib/chef-licensing/api/list_licenses.rb +30 -0
  9. data/lib/chef-licensing/api/parser/client.rb +100 -0
  10. data/lib/chef-licensing/api/parser/describe.rb +118 -0
  11. data/lib/chef-licensing/cli_flags/mixlib_cli.rb +28 -0
  12. data/lib/chef-licensing/cli_flags/thor.rb +21 -0
  13. data/lib/chef-licensing/config.rb +44 -0
  14. data/lib/chef-licensing/config_fetcher/arg_fetcher.rb +38 -0
  15. data/lib/chef-licensing/config_fetcher/env_fetcher.rb +21 -0
  16. data/lib/chef-licensing/context.rb +98 -0
  17. data/lib/chef-licensing/exceptions/client_error.rb +9 -0
  18. data/lib/chef-licensing/exceptions/describe_error.rb +9 -0
  19. data/lib/chef-licensing/exceptions/error.rb +4 -0
  20. data/lib/chef-licensing/exceptions/feature_not_entitled.rb +9 -0
  21. data/lib/chef-licensing/exceptions/invalid_license.rb +10 -0
  22. data/lib/chef-licensing/exceptions/license_generation_failed.rb +9 -0
  23. data/lib/chef-licensing/exceptions/license_generation_rejected.rb +7 -0
  24. data/lib/chef-licensing/exceptions/list_licenses_error.rb +12 -0
  25. data/lib/chef-licensing/exceptions/missing_api_credentials_error.rb +7 -0
  26. data/lib/chef-licensing/exceptions/restful_client_connection_error.rb +9 -0
  27. data/lib/chef-licensing/exceptions/restful_client_error.rb +9 -0
  28. data/lib/chef-licensing/exceptions/software_not_entitled.rb +9 -0
  29. data/lib/chef-licensing/license.rb +151 -0
  30. data/lib/chef-licensing/license_key_fetcher/base.rb +28 -0
  31. data/lib/chef-licensing/license_key_fetcher/chef_licensing_interactions.yaml +534 -0
  32. data/lib/chef-licensing/license_key_fetcher/file.rb +275 -0
  33. data/lib/chef-licensing/license_key_fetcher/prompt.rb +43 -0
  34. data/lib/chef-licensing/license_key_fetcher.rb +314 -0
  35. data/lib/chef-licensing/license_key_generator.rb +47 -0
  36. data/lib/chef-licensing/license_key_validator.rb +24 -0
  37. data/lib/chef-licensing/licensing_service/local.rb +29 -0
  38. data/lib/chef-licensing/list_license_keys.rb +142 -0
  39. data/lib/chef-licensing/restful_client/base.rb +139 -0
  40. data/lib/chef-licensing/restful_client/middleware/exceptions_handler.rb +16 -0
  41. data/lib/chef-licensing/restful_client/v1.rb +17 -0
  42. data/lib/chef-licensing/tui_engine/tui_actions.rb +238 -0
  43. data/lib/chef-licensing/tui_engine/tui_engine.rb +174 -0
  44. data/lib/chef-licensing/tui_engine/tui_engine_state.rb +62 -0
  45. data/lib/chef-licensing/tui_engine/tui_exceptions.rb +17 -0
  46. data/lib/chef-licensing/tui_engine/tui_interaction.rb +17 -0
  47. data/lib/chef-licensing/tui_engine/tui_prompt.rb +117 -0
  48. data/lib/chef-licensing/tui_engine.rb +2 -0
  49. data/lib/chef-licensing/version.rb +3 -0
  50. data/lib/chef-licensing.rb +70 -0
  51. metadata +191 -0
@@ -0,0 +1,98 @@
1
+ # Context defines the interface for state management in chef-licensing
2
+ # Different states : local or global
3
+
4
+ # Licensing service detection
5
+ require_relative "licensing_service/local"
6
+
7
+ module ChefLicensing
8
+ class Context
9
+
10
+ attr_accessor :state
11
+ attr_reader :logger, :options
12
+
13
+ class << self
14
+ attr_writer :current_context
15
+
16
+ def local_licensing_service?
17
+ ChefLicensing::Config.is_local_license_service ||= LicensingService::Local.detected?
18
+ end
19
+
20
+ # Implement methods on current context
21
+ # Current context changes the state determined using LicensingService module
22
+
23
+ # Return license keys from current context
24
+ def license_keys(options = {})
25
+ current_context(options).license_keys
26
+ end
27
+
28
+ private
29
+
30
+ def current_context(options)
31
+ return @current_context if @current_context
32
+
33
+ @current_context = context_based_on_state(options)
34
+ end
35
+
36
+ def context_based_on_state(options)
37
+ if local_licensing_service?
38
+ new(Local.new, options)
39
+ else
40
+ new(Global.new, options)
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ # @param [State] state
47
+ def initialize(state, options = {})
48
+ @options = options
49
+ @logger = ChefLicensing::Config.logger
50
+ transition_to(state)
51
+ end
52
+
53
+ # The Context allows changing the State object
54
+ def transition_to(state)
55
+ logger.debug "Chef Licensing Context: Transition to #{state.class}"
56
+ @state = state
57
+ @state.context = self
58
+ @state.options = options
59
+ end
60
+
61
+ # The Context delegates part of its behavior to the current State object.
62
+ def license_keys
63
+ @state.license_keys
64
+ end
65
+
66
+ class State
67
+ attr_accessor :context, :options
68
+
69
+ # @abstract
70
+ def license_keys
71
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
72
+ end
73
+ end
74
+
75
+ # Implement various behaviors, associated with a state of the Context.
76
+
77
+ class Local < State
78
+ def license_keys
79
+ @license_keys ||= ChefLicensing::Api::ListLicenses.info || []
80
+ end
81
+ end
82
+
83
+ class Global < State
84
+ def license_keys
85
+ @license_keys ||= fetch_license_keys_from_file || []
86
+ end
87
+
88
+ def fetch_license_keys_from_file
89
+ file_fetcher = LicenseKeyFetcher::File.new(options)
90
+ if file_fetcher.persisted?
91
+ # This could be useful if the file was writable in past but is not writable in current scenario and new keys are not persisted in the file
92
+ file_fetcher.fetch
93
+ end
94
+ end
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "error"
2
+
3
+ module ChefLicensing
4
+ class ClientError < Error
5
+ def message
6
+ super || "License Client API failure"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "error"
2
+
3
+ module ChefLicensing
4
+ class DescribeError < Error
5
+ def message
6
+ super || "License Describe API failure"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module ChefLicensing
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "error"
2
+
3
+ module ChefLicensing
4
+ class FeatureNotEntitled < Error
5
+ def message
6
+ super || "Feature not entitled"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ require_relative "error"
2
+
3
+ module ChefLicensing
4
+ class InvalidLicense < Error
5
+ def message
6
+ super || "Invalid License"
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,9 @@
1
+ require_relative "error"
2
+
3
+ module ChefLicensing
4
+ class LicenseGenerationFailed < Error
5
+ def message
6
+ super || "License Generation Failed"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module ChefLicensing
2
+ class LicenseGenerationRejected < Error
3
+ def message
4
+ super || "License Generation Rejected"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ require_relative "error"
2
+
3
+ module ChefLicensing
4
+ class ListLicensesError < Error
5
+ attr_reader :status_code
6
+
7
+ def initialize(message, status_code)
8
+ @status_code = status_code
9
+ super(message)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module ChefLicensing
2
+ class MissingAPICredentialsError < Error
3
+ def message
4
+ super || "Missing API credentials. Check README for more details."
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "error"
2
+
3
+ module ChefLicensing
4
+ class RestfulClientConnectionError < Error
5
+ def message
6
+ super || "Restful Client Connection Error"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "error"
2
+
3
+ module ChefLicensing
4
+ class RestfulClientError < Error
5
+ def message
6
+ super || "License Server Error"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "error"
2
+
3
+ module ChefLicensing
4
+ class SoftwareNotEntitled < Error
5
+ def message
6
+ super || "Software not entitled"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,151 @@
1
+ require_relative "api/parser/client" unless defined?(ChefLicensing::Api::Parser::Client)
2
+ require_relative "api/parser/describe" unless defined?(ChefLicensing::Api::Parser::Describe)
3
+ require_relative "config"
4
+
5
+ # License data model has an id, status, type of license, entitlements and limits.
6
+
7
+ module ChefLicensing
8
+ class License
9
+
10
+ attr_reader :id, :license_type , :status, :expiration_date, :expiration_status
11
+ attr_accessor :number_of_days_in_expiration
12
+
13
+ def initialize(opts = {})
14
+ # API parser based on the API call
15
+ @parser = opts[:api_parser].new(opts[:data])
16
+ @product_name = ChefLicensing::Config.chef_product_name
17
+
18
+ @id = @parser.parse_id
19
+ @status = @parser.parse_status
20
+ @license_type = @parser.parse_license_type
21
+
22
+ # expiration details
23
+ @expiration_date = @parser.parse_expiration_date
24
+ @expiration_status = @parser.parse_license_expiration_status
25
+
26
+ # usage details
27
+ @limits = []
28
+
29
+ # Entitlements
30
+ @feature_entitlements = []
31
+ @software_entitlements = []
32
+ @asset_entitlements = []
33
+ end
34
+
35
+ def feature_entitlements
36
+ return @feature_entitlements unless @feature_entitlements.empty?
37
+
38
+ feat_entitlements = []
39
+ feat_entitlements_data = @parser.parse_feature_entitlements
40
+ feat_entitlements_data.each do |data|
41
+ feat_entitlements << FeatureEntitlement.new(data)
42
+ end
43
+ @feature_entitlements = feat_entitlements
44
+ end
45
+
46
+ def software_entitlements
47
+ return @software_entitlements unless @software_entitlements.empty?
48
+
49
+ sw_entitlements = []
50
+ sw_entitlements_data = @parser.parse_software_entitlements
51
+ sw_entitlements_data.each do |data|
52
+ sw_entitlements << SoftwareEntitlement.new(data)
53
+ end
54
+ @software_entitlements = sw_entitlements
55
+ end
56
+
57
+ def asset_entitlements
58
+ return @asset_entitlements unless @asset_entitlements.empty?
59
+
60
+ asset_entitlements = []
61
+ asset_entitlements_data = @parser.parse_asset_entitlements
62
+ asset_entitlements_data.each do |data|
63
+ asset_entitlements << AssetEntitlement.new(data)
64
+ end
65
+ @asset_entitlements = asset_entitlements
66
+ end
67
+
68
+ def limits
69
+ return @limits unless @limits.empty?
70
+
71
+ limits = []
72
+ limits_data = @parser.parse_limits
73
+ limits_data.each do |data|
74
+ limits << Limit.new(data, { product_name: @product_name })
75
+ end
76
+ @limits = limits
77
+ end
78
+
79
+ # License has list of limits for different softwares which includes usage details.
80
+ class Limit
81
+ attr_reader :usage_status, :usage_limit, :usage_measure, :used, :software
82
+
83
+ def initialize(limit_data, opts = {})
84
+ @usage_status = limit_data["usage_status"]
85
+ @usage_limit = limit_data["usage_limit"]
86
+ @usage_measure = limit_data["usage_measure"]
87
+ @used = limit_data["used"]
88
+ @software = limit_data["software"] || opts[:product_name]
89
+ end
90
+ end
91
+
92
+ # License can be entitled to list of features.
93
+ class FeatureEntitlement
94
+ attr_reader :id, :name, :entitled, :status
95
+
96
+ def initialize(entitlement_data)
97
+ @id = entitlement_data["id"]
98
+ @name = entitlement_data["name"]
99
+ @entitled = entitlement_data["entitled"]
100
+ @status = entitlement_data["status"]
101
+ end
102
+ end
103
+
104
+ # License can be entitled to list of softwares.
105
+ class SoftwareEntitlement
106
+ attr_reader :id, :name, :entitled, :status
107
+
108
+ def initialize(entitlement_data)
109
+ @id = entitlement_data["id"]
110
+ @name = entitlement_data["name"]
111
+ @entitled = entitlement_data["entitled"]
112
+ @status = entitlement_data["status"]
113
+ end
114
+ end
115
+
116
+ # License can be entitled to list of assets.
117
+ class AssetEntitlement
118
+ attr_reader :id, :name, :entitled, :status
119
+
120
+ def initialize(entitlement_data)
121
+ @id = entitlement_data["id"]
122
+ @name = entitlement_data["name"]
123
+ @entitled = entitlement_data["entitled"]
124
+ @status = entitlement_data["status"]
125
+ end
126
+ end
127
+
128
+ def have_grace?
129
+ status.eql?("Grace")
130
+ end
131
+
132
+ def expired?
133
+ status.eql?("Expired")
134
+ end
135
+
136
+ def active?
137
+ status.eql?("Active")
138
+ end
139
+
140
+ def expiring_or_expired?
141
+ have_grace? || expired? || about_to_expire?
142
+ end
143
+
144
+ def about_to_expire?
145
+ require "Date" unless defined?(Date)
146
+ self.number_of_days_in_expiration = (Date.parse(expiration_date) - Date.today).to_i
147
+ # starts to nag before a week when about to expire
148
+ status.eql?("Active") && expiration_status.eql?("Expired") && ((1..7).include? number_of_days_in_expiration)
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,28 @@
1
+ require_relative "../exceptions/invalid_license"
2
+ module ChefLicensing
3
+ class LicenseKeyFetcher
4
+ class Base
5
+ # @example LICENSE UUID: tmns-58555821-925e-4a27-8fdc-e79dae5a425b-9763
6
+ # @example SERIAL NUMBER: A8BCD1XS2B4F6FYBWG8TE0N490
7
+ # @example COMMERCIAL KEY: e0b8f309-6abd-4668-909b-ef2d5fc043a1
8
+ # 4[license type]-(8-4-4-4-12)[GUID]-4[timestamp]
9
+ LICENSE_KEY_REGEX = "([a-z]{4}-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-[0-9]{1,4})".freeze
10
+ LICENSE_KEY_PATTERN_DESC = "Hexadecimal".freeze
11
+ # Serial number is a 26 character alphanumeric string
12
+ SERIAL_KEY_REGEX = "([A-Z0-9]{26})".freeze
13
+ SERIAL_KEY_PATTERN_DESC = "26 character alphanumeric string".freeze
14
+ COMMERCIAL_KEY_REGEX = "([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})".freeze
15
+ QUIT_KEY_REGEX = "(q|Q)".freeze
16
+
17
+ def self.verify_and_extract_license(license_key)
18
+ if license_key && (match = license_key.match(/^#{LICENSE_KEY_REGEX}$/) || license_key.match(/^#{SERIAL_KEY_REGEX}$/) || license_key.match(/^#{COMMERCIAL_KEY_REGEX}$/))
19
+ match[1]
20
+ else
21
+ raise InvalidLicenseKeyFormat, "Malformed License Key passed on command line - should be #{LICENSE_KEY_PATTERN_DESC} or #{SERIAL_KEY_PATTERN_DESC}"
22
+ end
23
+ end
24
+
25
+ class InvalidLicenseKeyFormat < InvalidLicense; end
26
+ end
27
+ end
28
+ end