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 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