license-acceptance 0.2.10 → 0.2.13

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa15a0ed3a76f4f1bd60469de0b9f836f73f7db824da061e876bd95fb78a919b
4
- data.tar.gz: d31a667d3f45ed4455e613d6091f3635aeb6f76cfd40589f0a35e6108d022847
3
+ metadata.gz: a4e838f450a28b5543cbf2ebf0a620b4274567cc1f42067d2ebd859a5e122005
4
+ data.tar.gz: 6257b8dd5964e8058e7614c219ebbe60234f9f060dff4c52cdd4df3a295daecb
5
5
  SHA512:
6
- metadata.gz: 0a91aee2c874863a1bb217da59b9cf9f8345cc90f2f9e6e9420ca9bb792fbca3808a23131702b434a64321ae915c4973b640dd71d3b0da65002c456c56e14f1d
7
- data.tar.gz: 76433d31718bffa392e817f7a350aab605142dc5d2248c25f0349adde3ac23c0ce36f83130296900dd84b24a600049df04e4c3e8a325154446540dae0e7fbb0c
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.10)
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.2)
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/file_acceptance"
7
- require "license_acceptance/arg_acceptance"
8
- require "license_acceptance/prompt_acceptance"
9
- require "license_acceptance/env_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, :env_acceptance, :file_acceptance, :arg_acceptance, :prompt_acceptance
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
- @env_acceptance = EnvAcceptance.new
23
- @file_acceptance = FileAcceptance.new(config)
24
- @arg_acceptance = ArgAcceptance.new
25
- @prompt_acceptance = PromptAcceptance.new(config)
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 = file_acceptance.accepted?(product_relationship)
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 = file_acceptance.persist(product_relationship, missing_licenses)
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 && prompt_acceptance.request(missing_licenses) do
69
+ elsif config.output.isatty && prompt_strategy.request(missing_licenses) do
68
70
  if config.persist
69
- file_acceptance.persist(product_relationship, missing_licenses)
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
- env_acceptance.accepted?(ENV) || arg_acceptance.accepted?(ARGV)
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
- env_acceptance.no_persist?(ENV) || arg_acceptance.no_persist?(ARGV)
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
- env_acceptance.silent?(ENV) || arg_acceptance.silent?(ARGV)
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
- #{PromptAcceptance::BORDER}
108
- #{PromptAcceptance::CHECK} #{count} product license#{s} accepted.
109
- #{PromptAcceptance::BORDER}
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
- #{PromptAcceptance::BORDER}
116
- #{PromptAcceptance::CHECK} Product license accepted.
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
- #{PromptAcceptance::BORDER}
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,11 @@
1
+ module LicenseAcceptance
2
+ module Strategy
3
+ class Base
4
+
5
+ ACCEPT = "accept"
6
+ ACCEPT_SILENT = "accept-silent"
7
+ ACCEPT_NO_PERSIST = "accept-no-persist"
8
+
9
+ end
10
+ end
11
+ 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