license-acceptance 0.2.10 → 0.2.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/lib/license_acceptance/acceptor.rb +24 -22
- data/lib/license_acceptance/strategy/argument.rb +44 -0
- data/lib/license_acceptance/strategy/base.rb +11 -0
- data/lib/license_acceptance/strategy/environment.rb +38 -0
- data/lib/license_acceptance/strategy/file.rb +102 -0
- data/lib/license_acceptance/strategy/prompt.rb +111 -0
- data/lib/license_acceptance/strategy/provided_value.rb +28 -0
- data/lib/license_acceptance/version.rb +1 -1
- data/spec/license_acceptance/acceptor_spec.rb +37 -73
- data/spec/license_acceptance/strategy/argument_spec.rb +82 -0
- data/spec/license_acceptance/strategy/environment_spec.rb +76 -0
- data/spec/license_acceptance/{file_acceptance_spec.rb → strategy/file_spec.rb} +3 -3
- data/spec/license_acceptance/{prompt_acceptance_spec.rb → strategy/prompt_spec.rb} +4 -4
- data/spec/license_acceptance/strategy/provided_value_spec.rb +55 -0
- metadata +14 -11
- data/lib/license_acceptance/arg_acceptance.rb +0 -32
- data/lib/license_acceptance/env_acceptance.rb +0 -26
- data/lib/license_acceptance/file_acceptance.rb +0 -97
- data/lib/license_acceptance/prompt_acceptance.rb +0 -106
- data/spec/license_acceptance/arg_acceptance_spec.rb +0 -57
- data/spec/license_acceptance/env_acceptance_spec.rb +0 -65
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4e838f450a28b5543cbf2ebf0a620b4274567cc1f42067d2ebd859a5e122005
|
4
|
+
data.tar.gz: 6257b8dd5964e8058e7614c219ebbe60234f9f060dff4c52cdd4df3a295daecb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81e308d0664299c9797fa0b06470f550aac3087f9d154ee0f8b3737aff4cfc09fca669a80f81d4be726d01e4166124ea3747630720f32d8115bc1c5d5fb38722
|
7
|
+
data.tar.gz: 3677568ac06e369235ecba67c6ae38cd4f271a9ca19f495c8d2e789e32e18eea559fe40aea9a8f1b60cdef659828f5b892eacf1c70099ea83cae1b846bd27cf1
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
license-acceptance (0.2.
|
4
|
+
license-acceptance (0.2.13)
|
5
5
|
pastel (~> 0.7)
|
6
6
|
tomlrb (~> 1.2)
|
7
7
|
tty-box (~> 0.3)
|
@@ -40,7 +40,7 @@ GEM
|
|
40
40
|
rspec-mocks (~> 3.8.0)
|
41
41
|
rspec-core (3.8.0)
|
42
42
|
rspec-support (~> 3.8.0)
|
43
|
-
rspec-expectations (3.8.
|
43
|
+
rspec-expectations (3.8.3)
|
44
44
|
diff-lcs (>= 1.2.0, < 2.0)
|
45
45
|
rspec-support (~> 3.8.0)
|
46
46
|
rspec-mocks (3.8.0)
|
@@ -3,26 +3,28 @@ require "license_acceptance/config"
|
|
3
3
|
require "license_acceptance/logger"
|
4
4
|
require "license_acceptance/product_reader"
|
5
5
|
require "license_acceptance/product_relationship"
|
6
|
-
require "license_acceptance/
|
7
|
-
require "license_acceptance/
|
8
|
-
require "license_acceptance/
|
9
|
-
require "license_acceptance/
|
6
|
+
require "license_acceptance/strategy/environment"
|
7
|
+
require "license_acceptance/strategy/file"
|
8
|
+
require "license_acceptance/strategy/argument"
|
9
|
+
require "license_acceptance/strategy/prompt"
|
10
|
+
require "license_acceptance/strategy/provided_value"
|
10
11
|
|
11
12
|
module LicenseAcceptance
|
12
13
|
class Acceptor
|
13
14
|
extend Forwardable
|
14
15
|
include Logger
|
15
16
|
|
16
|
-
attr_reader :config, :product_reader, :
|
17
|
+
attr_reader :config, :product_reader, :env_strategy, :file_strategy, :arg_strategy, :prompt_strategy, :provided_strategy
|
17
18
|
|
18
19
|
def initialize(opts={})
|
19
20
|
@config = Config.new(opts)
|
20
21
|
Logger.initialize(config.logger)
|
21
22
|
@product_reader = ProductReader.new
|
22
|
-
@
|
23
|
-
@
|
24
|
-
@
|
25
|
-
@
|
23
|
+
@env_strategy = Strategy::Environment.new(ENV)
|
24
|
+
@file_strategy = Strategy::File.new(config)
|
25
|
+
@arg_strategy = Strategy::Argument.new(ARGV)
|
26
|
+
@prompt_strategy = Strategy::Prompt.new(config)
|
27
|
+
@provided_strategy = Strategy::ProvidedValue.new(opts.fetch(:provided, nil))
|
26
28
|
end
|
27
29
|
|
28
30
|
def_delegator :@config, :output
|
@@ -46,7 +48,7 @@ module LicenseAcceptance
|
|
46
48
|
product_reader.read
|
47
49
|
product_relationship = product_reader.lookup(product_name, version)
|
48
50
|
|
49
|
-
missing_licenses =
|
51
|
+
missing_licenses = file_strategy.accepted?(product_relationship)
|
50
52
|
|
51
53
|
# They have already accepted all licenses and stored their acceptance in the persistent files
|
52
54
|
if missing_licenses.empty?
|
@@ -56,7 +58,7 @@ module LicenseAcceptance
|
|
56
58
|
|
57
59
|
if accepted? || accepted_silent?
|
58
60
|
if config.persist
|
59
|
-
errs =
|
61
|
+
errs = file_strategy.persist(product_relationship, missing_licenses)
|
60
62
|
if errs.empty?
|
61
63
|
output_num_persisted(missing_licenses.size) unless accepted_silent?
|
62
64
|
else
|
@@ -64,9 +66,9 @@ module LicenseAcceptance
|
|
64
66
|
end
|
65
67
|
end
|
66
68
|
return true
|
67
|
-
elsif config.output.isatty &&
|
69
|
+
elsif config.output.isatty && prompt_strategy.request(missing_licenses) do
|
68
70
|
if config.persist
|
69
|
-
|
71
|
+
file_strategy.persist(product_relationship, missing_licenses)
|
70
72
|
else
|
71
73
|
[]
|
72
74
|
end
|
@@ -86,17 +88,17 @@ module LicenseAcceptance
|
|
86
88
|
end
|
87
89
|
|
88
90
|
def accepted?
|
89
|
-
|
91
|
+
provided_strategy.accepted? || env_strategy.accepted? || arg_strategy.accepted?
|
90
92
|
end
|
91
93
|
|
92
94
|
# no-persist is silent too
|
93
95
|
def accepted_no_persist?
|
94
|
-
|
96
|
+
provided_strategy.no_persist? || env_strategy.no_persist? || arg_strategy.no_persist?
|
95
97
|
end
|
96
98
|
|
97
99
|
# persist but be silent like no-persist
|
98
100
|
def accepted_silent?
|
99
|
-
|
101
|
+
provided_strategy.silent? || env_strategy.silent? || arg_strategy.silent?
|
100
102
|
end
|
101
103
|
|
102
104
|
# In the case where users accept with a command line argument or environment variable
|
@@ -104,18 +106,18 @@ module LicenseAcceptance
|
|
104
106
|
def output_num_persisted(count)
|
105
107
|
s = count > 1 ? "s": ""
|
106
108
|
output.puts <<~EOM
|
107
|
-
#{
|
108
|
-
#{
|
109
|
-
#{
|
109
|
+
#{Strategy::Prompt::BORDER}
|
110
|
+
#{Strategy::Prompt::CHECK} #{count} product license#{s} accepted.
|
111
|
+
#{Strategy::Prompt::BORDER}
|
110
112
|
EOM
|
111
113
|
end
|
112
114
|
|
113
115
|
def output_persist_failed(errs)
|
114
116
|
output.puts <<~EOM
|
115
|
-
#{
|
116
|
-
#{
|
117
|
+
#{Strategy::Prompt::BORDER}
|
118
|
+
#{Strategy::Prompt::CHECK} Product license accepted.
|
117
119
|
Could not persist acceptance:\n\t* #{errs.map(&:message).join("\n\t* ")}
|
118
|
-
#{
|
120
|
+
#{Strategy::Prompt::BORDER}
|
119
121
|
EOM
|
120
122
|
end
|
121
123
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "license_acceptance/strategy/base"
|
2
|
+
|
3
|
+
module LicenseAcceptance
|
4
|
+
module Strategy
|
5
|
+
|
6
|
+
# Look for acceptance values in the ARGV
|
7
|
+
class Argument < Base
|
8
|
+
|
9
|
+
attr_reader :argv
|
10
|
+
|
11
|
+
def initialize(argv)
|
12
|
+
@argv = argv
|
13
|
+
end
|
14
|
+
|
15
|
+
def accepted?
|
16
|
+
look_for_value(ACCEPT)
|
17
|
+
end
|
18
|
+
|
19
|
+
def silent?
|
20
|
+
look_for_value(ACCEPT_SILENT)
|
21
|
+
end
|
22
|
+
|
23
|
+
def no_persist?
|
24
|
+
look_for_value(ACCEPT_NO_PERSIST)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def look_for_value(sought)
|
30
|
+
if argv.include?("--chef-license=#{sought}")
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
i = argv.index("--chef-license")
|
34
|
+
unless i.nil?
|
35
|
+
val = argv[i+1]
|
36
|
+
if val != nil && val.downcase == sought
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "license_acceptance/strategy/base"
|
2
|
+
|
3
|
+
module LicenseAcceptance
|
4
|
+
module Strategy
|
5
|
+
|
6
|
+
# Look for acceptance values in the environment
|
7
|
+
class Environment < Base
|
8
|
+
|
9
|
+
attr_reader :env
|
10
|
+
|
11
|
+
def initialize(env)
|
12
|
+
@env = env
|
13
|
+
end
|
14
|
+
|
15
|
+
def accepted?
|
16
|
+
look_for_value(ACCEPT)
|
17
|
+
end
|
18
|
+
|
19
|
+
def silent?
|
20
|
+
look_for_value(ACCEPT_SILENT)
|
21
|
+
end
|
22
|
+
|
23
|
+
def no_persist?
|
24
|
+
look_for_value(ACCEPT_NO_PERSIST)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def look_for_value(sought)
|
30
|
+
if env['CHEF_LICENSE'] && env['CHEF_LICENSE'].downcase == sought
|
31
|
+
return true
|
32
|
+
end
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'yaml'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'etc'
|
5
|
+
require "license_acceptance/logger"
|
6
|
+
require "license_acceptance/strategy/base"
|
7
|
+
|
8
|
+
module LicenseAcceptance
|
9
|
+
module Strategy
|
10
|
+
|
11
|
+
# Read and write marker files that show acceptance.
|
12
|
+
class File < Base
|
13
|
+
include Logger
|
14
|
+
|
15
|
+
attr_reader :config
|
16
|
+
|
17
|
+
def initialize(config)
|
18
|
+
@config = config
|
19
|
+
end
|
20
|
+
|
21
|
+
INVOCATION_TIME = DateTime.now.freeze
|
22
|
+
|
23
|
+
# For all the given products in the product set, search all possible locations for the
|
24
|
+
# license acceptance files.
|
25
|
+
def accepted?(product_relationship)
|
26
|
+
searching = [product_relationship.parent] + product_relationship.children
|
27
|
+
missing_licenses = searching.clone
|
28
|
+
logger.debug("Searching for the following licenses: #{missing_licenses.map(&:name)}")
|
29
|
+
|
30
|
+
searching.each do |product|
|
31
|
+
found = false
|
32
|
+
config.license_locations.each do |loc|
|
33
|
+
f = ::File.join(loc, product.filename)
|
34
|
+
if ::File.exist?(f)
|
35
|
+
found = true
|
36
|
+
logger.debug("Found license #{product.filename} at #{f}")
|
37
|
+
missing_licenses.delete(product)
|
38
|
+
break
|
39
|
+
end
|
40
|
+
end
|
41
|
+
break if missing_licenses.empty?
|
42
|
+
end
|
43
|
+
logger.debug("Missing licenses remaining: #{missing_licenses.map(&:name)}")
|
44
|
+
missing_licenses
|
45
|
+
end
|
46
|
+
|
47
|
+
def persist(product_relationship, missing_licenses)
|
48
|
+
parent = product_relationship.parent
|
49
|
+
parent_version = product_relationship.parent_version
|
50
|
+
root_dir = config.persist_location
|
51
|
+
|
52
|
+
if !Dir.exist?(root_dir)
|
53
|
+
begin
|
54
|
+
FileUtils.mkdir_p(root_dir)
|
55
|
+
rescue StandardError => e
|
56
|
+
msg = "Could not create license directory #{root_dir}"
|
57
|
+
logger.info "#{msg}\n\t#{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
58
|
+
return [e]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
errs = []
|
63
|
+
if missing_licenses.include?(parent)
|
64
|
+
err = persist_license(root_dir, parent, parent, parent_version)
|
65
|
+
errs << err unless err.nil?
|
66
|
+
end
|
67
|
+
product_relationship.children.each do |child|
|
68
|
+
if missing_licenses.include?(child)
|
69
|
+
err = persist_license(root_dir, child, parent, parent_version)
|
70
|
+
errs << err unless err.nil?
|
71
|
+
end
|
72
|
+
end
|
73
|
+
return errs
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def persist_license(folder_path, product, parent, parent_version)
|
79
|
+
path = ::File.join(folder_path, product.filename)
|
80
|
+
logger.info("Persisting a license for #{product.name} at path #{path}")
|
81
|
+
::File.open(path, ::File::WRONLY | ::File::CREAT | ::File::EXCL) do |license_file|
|
82
|
+
contents = {
|
83
|
+
name: product.name,
|
84
|
+
date_accepted: INVOCATION_TIME.iso8601,
|
85
|
+
accepting_product: parent.name,
|
86
|
+
accepting_product_version: parent_version,
|
87
|
+
user: Etc.getlogin,
|
88
|
+
file_format: 1,
|
89
|
+
}
|
90
|
+
contents = Hash[contents.map { |k, v| [k.to_s, v] }]
|
91
|
+
license_file << YAML.dump(contents)
|
92
|
+
end
|
93
|
+
return nil
|
94
|
+
rescue StandardError => e
|
95
|
+
msg = "Could not persist license to #{path}"
|
96
|
+
logger.info "#{msg}\n\t#{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
97
|
+
return e
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'tty-prompt'
|
2
|
+
require 'pastel'
|
3
|
+
require "license_acceptance/logger"
|
4
|
+
require "license_acceptance/strategy/base"
|
5
|
+
require "timeout"
|
6
|
+
|
7
|
+
module LicenseAcceptance
|
8
|
+
module Strategy
|
9
|
+
|
10
|
+
# Interactive prompt for accepting and persistnce license acceptance, or failing with custom exit code
|
11
|
+
class Prompt < Base
|
12
|
+
include Logger
|
13
|
+
|
14
|
+
attr_reader :output
|
15
|
+
|
16
|
+
def initialize(config)
|
17
|
+
@output = config.output
|
18
|
+
end
|
19
|
+
|
20
|
+
WIDTH = 50.freeze
|
21
|
+
PASTEL = Pastel.new
|
22
|
+
BORDER = "+---------------------------------------------+".freeze
|
23
|
+
YES = PASTEL.green.bold("yes")
|
24
|
+
CHECK = PASTEL.green("✔")
|
25
|
+
|
26
|
+
def request(missing_licenses, &persist_callback)
|
27
|
+
logger.debug("Requesting a license for #{missing_licenses.map(&:name)}")
|
28
|
+
c = missing_licenses.size
|
29
|
+
s = c > 1 ? "s": ""
|
30
|
+
|
31
|
+
acceptance_question = "Do you accept the #{c} product license#{s} (#{YES}/no)?"
|
32
|
+
output.puts <<~EOM
|
33
|
+
#{BORDER}
|
34
|
+
Chef License Acceptance
|
35
|
+
|
36
|
+
Before you can continue, #{c} product license#{s}
|
37
|
+
must be accepted. View the license at
|
38
|
+
https://www.chef.io/end-user-license-agreement/
|
39
|
+
|
40
|
+
License#{s} that need accepting:
|
41
|
+
* #{missing_licenses.map(&:pretty_name).join("\n * ")}
|
42
|
+
|
43
|
+
#{acceptance_question}
|
44
|
+
|
45
|
+
EOM
|
46
|
+
|
47
|
+
if ask(output, c, s, persist_callback)
|
48
|
+
output.puts BORDER
|
49
|
+
return true
|
50
|
+
end
|
51
|
+
|
52
|
+
output.puts <<~EOM
|
53
|
+
|
54
|
+
If you do not accept this license you will
|
55
|
+
not be able to use Chef products.
|
56
|
+
|
57
|
+
#{acceptance_question}
|
58
|
+
|
59
|
+
EOM
|
60
|
+
|
61
|
+
answer = ask(output, c, s, persist_callback)
|
62
|
+
if answer != "yes"
|
63
|
+
output.puts BORDER
|
64
|
+
end
|
65
|
+
return answer
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def ask(output, c, s, persist_callback)
|
71
|
+
logger.debug("Attempting to request interactive prompt on TTY")
|
72
|
+
prompt = TTY::Prompt.new(track_history: false, active_color: :bold, interrupt: :exit, output: output)
|
73
|
+
|
74
|
+
answer = "no"
|
75
|
+
begin
|
76
|
+
Timeout::timeout(60, PromptTimeout) do
|
77
|
+
answer = prompt.ask(">") do |q|
|
78
|
+
q.modify :down, :trim
|
79
|
+
q.required true
|
80
|
+
q.messages[:required?] = "You must enter 'yes' or 'no'"
|
81
|
+
q.validate /^\s*(yes|no)\s*$/i
|
82
|
+
q.messages[:valid?] = "You must enter 'yes' or 'no'"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
rescue PromptTimeout
|
86
|
+
prompt.unsubscribe(prompt.reader)
|
87
|
+
output.puts "Prompt timed out. Use non-interactive flags or enter an answer within 60 seconds."
|
88
|
+
end
|
89
|
+
|
90
|
+
if answer == "yes"
|
91
|
+
output.puts
|
92
|
+
output.puts "Persisting #{c} product license#{s}..."
|
93
|
+
errs = persist_callback.call
|
94
|
+
if errs.empty?
|
95
|
+
output.puts "#{CHECK} #{c} product license#{s} persisted.\n\n"
|
96
|
+
else
|
97
|
+
output.puts <<~EOM
|
98
|
+
#{CHECK} #{c} product license#{s} accepted.
|
99
|
+
Could not persist acceptance:\n\t* #{errs.map(&:message).join("\n\t* ")}
|
100
|
+
EOM
|
101
|
+
end
|
102
|
+
return true
|
103
|
+
end
|
104
|
+
return false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class PromptTimeout < StandardError; end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|