foreman_maintain 0.0.6 → 0.0.7

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +75 -3
  3. data/config/foreman_maintain.yml.example +6 -0
  4. data/config/foreman_maintain.yml.packaging +6 -0
  5. data/definitions/checks/disk_speed_minimal.rb +1 -1
  6. data/definitions/checks/foreman_proxy/verify_dhcp_config_syntax.rb +17 -0
  7. data/definitions/checks/system_registration.rb +1 -1
  8. data/definitions/features/downstream.rb +31 -0
  9. data/definitions/features/foreman_1_7_x.rb +33 -0
  10. data/definitions/features/foreman_proxy.rb +72 -0
  11. data/definitions/features/sync_plans.rb +14 -20
  12. data/definitions/features/upstream.rb +4 -0
  13. data/definitions/procedures/foreman_tasks/delete.rb +2 -1
  14. data/definitions/procedures/hammer_setup.rb +4 -4
  15. data/definitions/procedures/installer/upgrade.rb +19 -0
  16. data/definitions/procedures/maintenance_mode/disable.rb +13 -0
  17. data/definitions/procedures/maintenance_mode/enable.rb +13 -0
  18. data/definitions/procedures/packages/install.rb +20 -0
  19. data/definitions/procedures/packages/update.rb +20 -0
  20. data/definitions/procedures/repositories/setup.rb +18 -0
  21. data/definitions/procedures/sync_plans/disable.rb +4 -0
  22. data/definitions/procedures/sync_plans/enable.rb +5 -0
  23. data/definitions/scenarios/upgrade_to_satellite_6_2.rb +77 -0
  24. data/lib/foreman_maintain.rb +5 -2
  25. data/lib/foreman_maintain/check.rb +11 -4
  26. data/lib/foreman_maintain/cli.rb +23 -0
  27. data/lib/foreman_maintain/cli/advanced/procedure/abstract_by_tag_command.rb +38 -0
  28. data/lib/foreman_maintain/cli/advanced/procedure/abstract_procedure_command.rb +17 -0
  29. data/lib/foreman_maintain/cli/advanced/procedure/by_tag_command.rb +32 -0
  30. data/lib/foreman_maintain/cli/advanced/procedure/run_command.rb +17 -0
  31. data/lib/foreman_maintain/cli/advanced/procedure_command.rb +11 -0
  32. data/lib/foreman_maintain/cli/advanced_command.rb +9 -0
  33. data/lib/foreman_maintain/cli/base.rb +52 -7
  34. data/lib/foreman_maintain/cli/health_command.rb +0 -12
  35. data/lib/foreman_maintain/cli/transform_clamp_options.rb +66 -0
  36. data/lib/foreman_maintain/cli/upgrade_command.rb +45 -33
  37. data/lib/foreman_maintain/concerns/metadata.rb +28 -2
  38. data/lib/foreman_maintain/concerns/scenario_metadata.rb +44 -0
  39. data/lib/foreman_maintain/concerns/system_helpers.rb +27 -5
  40. data/lib/foreman_maintain/config.rb +10 -5
  41. data/lib/foreman_maintain/core_ext.rb +5 -1
  42. data/lib/foreman_maintain/csv_parser.rb +81 -0
  43. data/lib/foreman_maintain/dependency_graph.rb +10 -48
  44. data/lib/foreman_maintain/error.rb +4 -0
  45. data/lib/foreman_maintain/executable.rb +64 -13
  46. data/lib/foreman_maintain/param.rb +1 -0
  47. data/lib/foreman_maintain/reporter.rb +84 -3
  48. data/lib/foreman_maintain/reporter/cli_reporter.rb +57 -21
  49. data/lib/foreman_maintain/runner.rb +80 -21
  50. data/lib/foreman_maintain/runner/execution.rb +29 -4
  51. data/lib/foreman_maintain/runner/stored_execution.rb +23 -0
  52. data/lib/foreman_maintain/scenario.rb +90 -7
  53. data/lib/foreman_maintain/upgrade_runner.rb +194 -0
  54. data/lib/foreman_maintain/utils.rb +1 -0
  55. data/lib/foreman_maintain/utils/curl_response.rb +21 -0
  56. data/lib/foreman_maintain/version.rb +1 -1
  57. metadata +24 -14
  58. data/definitions/checks/sync_plans/with_disabled_status.rb +0 -18
  59. data/definitions/checks/sync_plans/with_enabled_status.rb +0 -19
  60. data/definitions/procedures/install_package.rb +0 -17
  61. data/definitions/scenarios/pre_upgrade_check_foreman_1_14.rb +0 -13
  62. data/definitions/scenarios/pre_upgrade_check_satellite_6_0_z.rb +0 -14
  63. data/definitions/scenarios/pre_upgrade_check_satellite_6_1.rb +0 -14
  64. data/definitions/scenarios/pre_upgrade_check_satellite_6_1_z.rb +0 -14
  65. data/definitions/scenarios/pre_upgrade_check_satellite_6_2.rb +0 -14
  66. data/definitions/scenarios/pre_upgrade_check_satellite_6_2_z.rb +0 -14
  67. data/definitions/scenarios/pre_upgrade_check_satellite_6_3.rb +0 -14
  68. data/lib/foreman_maintain/object_cache.rb +0 -34
@@ -2,8 +2,8 @@ require 'fileutils'
2
2
  module ForemanMaintain
3
3
  class Config
4
4
  attr_accessor :pre_setup_log_messages,
5
- :config_file, :definitions_dirs, :log_level, :log_dir, :storage_file,
6
- :backup_dir
5
+ :config_file, :definitions_dirs, :log_level, :log_dir, :log_file_size,
6
+ :storage_file, :backup_dir, :foreman_proxy_cert_path
7
7
 
8
8
  def initialize(options)
9
9
  @pre_setup_log_messages = []
@@ -11,17 +11,22 @@ module ForemanMaintain
11
11
  @options = load_config
12
12
  @definitions_dirs = @options.fetch(:definitions_dirs,
13
13
  [File.join(source_path, 'definitions')])
14
-
15
- @log_level = @options.fetch(:log_level, ::Logger::DEBUG)
16
- @log_dir = find_dir_path(@options.fetch(:log_dir, 'log'))
14
+ load_log_configs
17
15
  @storage_file = @options.fetch(:storage_file, 'data.yml')
18
16
  @backup_dir = find_dir_path(
19
17
  @options.fetch(:backup_dir, '/var/lib/foreman-maintain')
20
18
  )
19
+ @foreman_proxy_cert_path = @options.fetch(:foreman_proxy_cert_path, '/etc/foreman')
21
20
  end
22
21
 
23
22
  private
24
23
 
24
+ def load_log_configs
25
+ @log_level = @options.fetch(:log_level, ::Logger::DEBUG)
26
+ @log_dir = find_dir_path(@options.fetch(:log_dir, 'log'))
27
+ @log_file_size = @options.fetch(:log_file_size, 10_000)
28
+ end
29
+
25
30
  def load_config
26
31
  if File.exist?(config_file)
27
32
  YAML.load(File.open(config_file)) || {}
@@ -2,7 +2,11 @@ module ForemanMaintain
2
2
  module CoreExt
3
3
  module StripHeredoc
4
4
  def strip_heredoc
5
- indent = scan(/^[ \t]*(?=\S)/).min.size || 0
5
+ indent = 0
6
+ indented_lines = scan(/^[ \t]+(?=\S)/)
7
+ unless indented_lines.empty?
8
+ indent = indented_lines.min.size
9
+ end
6
10
  gsub(/^[ \t]{#{indent}}/, '')
7
11
  end
8
12
  end
@@ -0,0 +1,81 @@
1
+ module ForemanMaintain
2
+ class CSVParser
3
+ def initialize
4
+ reset_parser
5
+ end
6
+
7
+ def parse(data)
8
+ return [] if data.nil?
9
+ reset_parser
10
+ data.each_char do |char|
11
+ handle_escape(char) || handle_quoting(char) || handle_comma(char) || add_to_buffer(char)
12
+ end
13
+ unless @last_quote.nil?
14
+ raise(ArgumentError, format('Illegal quoting in %s', @raw_buffer))
15
+ end
16
+ clean_buffer
17
+ @value
18
+ end
19
+
20
+ private
21
+
22
+ def handle_comma(char)
23
+ if char == ','
24
+ clean_buffer
25
+ true
26
+ else
27
+ false
28
+ end
29
+ end
30
+
31
+ def handle_quoting(char)
32
+ if @last_quote.nil? && ["'", '"'].include?(char)
33
+ @last_quote = char
34
+ @raw_buffer += char
35
+ true
36
+ elsif @last_quote == char
37
+ @last_quote = nil
38
+ @raw_buffer += char
39
+ true
40
+ elsif @last_quote
41
+ add_to_buffer(char)
42
+ true
43
+ else
44
+ false
45
+ end
46
+ end
47
+
48
+ def handle_escape(char)
49
+ if @escape
50
+ add_to_buffer(char)
51
+ @escape = false
52
+ true
53
+ elsif char == '\\'
54
+ @escape = true
55
+ @raw_buffer += char
56
+ true
57
+ else
58
+ false
59
+ end
60
+ end
61
+
62
+ def add_to_buffer(char)
63
+ @buffer += char
64
+ @raw_buffer += char
65
+ end
66
+
67
+ def reset_parser
68
+ @value = []
69
+ @buffer = ''
70
+ @raw_buffer = ''
71
+ @escape = false
72
+ @last_quote = nil
73
+ end
74
+
75
+ def clean_buffer
76
+ @value << @buffer
77
+ @raw_buffer = ''
78
+ @buffer = ''
79
+ end
80
+ end
81
+ end
@@ -13,24 +13,18 @@ module ForemanMaintain
13
13
  def initialize(collection)
14
14
  @graph = Hash.new([])
15
15
  @collection = collection
16
- @labels = extract_labels
16
+ @steps_by_labels = @collection.group_by(&:label)
17
17
  generate_label_graph
18
- convert_label_graph_to_object_graph
19
18
  end
20
19
 
21
20
  def add_to_graph(key, dependencies = [])
22
21
  return unless key
23
22
 
24
- dependencies = dependencies.map do |dep|
25
- next unless labels.include?(dep)
26
- dep
27
- end.compact
28
-
29
23
  graph[key] = dependencies
30
24
  end
31
25
 
32
26
  def tsort_each_node(&block)
33
- graph.each_key(&block)
27
+ @collection.each(&block)
34
28
  end
35
29
 
36
30
  def tsort_each_child(node, &block)
@@ -39,51 +33,19 @@ module ForemanMaintain
39
33
 
40
34
  private
41
35
 
42
- def cache
43
- ForemanMaintain.cache
44
- end
45
-
46
- def convert_label_graph_to_object_graph
47
- graph.keys.each do |key|
48
- graph[cache.fetch(key)] = graph[key].map { |dep| cache.fetch(dep) }
49
- graph.delete(key)
50
- end
51
-
52
- graph
53
- end
54
-
55
- def extract_labels
56
- collection.map(&:label)
57
- end
58
-
59
- def find_class(dep)
60
- case dep
61
- when Class
62
- dep
63
- when String
64
- if dep.include?('::')
65
- dep.split('::').reduce(Object) { |o, e| o.const_get(e) }
66
- else
67
- cache.fetch(dep)
68
- end
69
- else
70
- cache.fetch(dep)
71
- end
72
- end
73
-
74
36
  def generate_label_graph
75
37
  collection.each do |object|
76
- klass = object.class
77
- key = object.label
78
-
79
- add_to_graph(klass.before.first, [key])
80
- add_to_graph(key, klass.after)
81
- map_label_to_object(object)
38
+ klass = object.is_a?(Class) ? object : object.class
39
+ klass.before.each do |label|
40
+ add_to_graph(labels_to_objects(label).first, [object])
41
+ end
42
+ add_to_graph(object, labels_to_objects(klass.after))
82
43
  end
83
44
  end
84
45
 
85
- def map_label_to_object(object)
86
- cache.fetch(object.label, object)
46
+ def labels_to_objects(labels)
47
+ labels = Array(labels)
48
+ labels.map { |label| @steps_by_labels[label] }.compact.flatten
87
49
  end
88
50
  end
89
51
  end
@@ -34,5 +34,9 @@ module ForemanMaintain
34
34
  ret
35
35
  end
36
36
  end
37
+
38
+ # Error caused by incorrect usage, usually connected to passed CLI options
39
+ class UsageError < StandardError
40
+ end
37
41
  end
38
42
  end
@@ -1,9 +1,12 @@
1
1
  module ForemanMaintain
2
2
  class Executable
3
3
  extend Forwardable
4
+ extend Concerns::Finders
4
5
  attr_reader :options
5
- def_delegators :execution, :success?, :fail?, :output, :assumeyes?
6
- def_delegators :execution, :puts, :print, :with_spinner, :ask
6
+ def_delegators :execution,
7
+ :success?, :skipped?, :fail?, :warning?, :output,
8
+ :assumeyes?, :whitelisted?,
9
+ :execution, :puts, :print, :with_spinner, :ask, :storage
7
10
 
8
11
  attr_accessor :associated_feature
9
12
 
@@ -56,11 +59,41 @@ module ForemanMaintain
56
59
  @next_steps ||= []
57
60
  end
58
61
 
62
+ # make the step to fail: the failure is considered significant and
63
+ # the next steps should not continue. The specific behaviour depends
64
+ # on the scenario it's being used on. In check-sets scenario, the next
65
+ # steps of the same scenario might continue, while the following scenarios
66
+ # would be aborted.
67
+ def fail!(message)
68
+ raise Error::Fail, message
69
+ end
70
+
71
+ # make the step a warning: this doesn't indicate the whole scenario should
72
+ # not continue, but the user will be warned before proceeding
73
+ def warn!(message)
74
+ raise Error::Warn, message
75
+ end
76
+
77
+ # rubocop:disable Style/AccessorMethodName
78
+ def set_fail(message)
79
+ execution.status = :fail
80
+ execution.output << message
81
+ end
82
+
83
+ def set_warn(message)
84
+ execution.status = :warning
85
+ execution.output << message
86
+ end
87
+
59
88
  # public method to be overriden
60
89
  def run
61
90
  raise NotImplementedError
62
91
  end
63
92
 
93
+ def executed?
94
+ @_execution ? true : false
95
+ end
96
+
64
97
  def execution
65
98
  if @_execution
66
99
  @_execution
@@ -70,22 +103,12 @@ module ForemanMaintain
70
103
  end
71
104
 
72
105
  # public method to be overriden: it can perform additional checks
73
- # to say, if the step is actually necessary to run. For example an `InstallPackage`
106
+ # to say, if the step is actually necessary to run. For example an `Packages::Install`
74
107
  # procedure would not be necessary when the package is already installed.
75
108
  def necessary?
76
109
  true
77
110
  end
78
111
 
79
- def fail!(message)
80
- execution.status = :fail
81
- execution.output << message
82
- end
83
-
84
- def warn!(message)
85
- execution.status = :warning
86
- execution.output << message
87
- end
88
-
89
112
  # update reporter about the current message
90
113
  def say(message)
91
114
  execution.update(message)
@@ -110,6 +133,34 @@ module ForemanMaintain
110
133
  @next_steps = []
111
134
  end
112
135
 
136
+ # serialization methods
137
+ def to_hash
138
+ ret = { :label => label, :param_values => @param_values }
139
+ if @_execution
140
+ ret[:status] = @_execution.status
141
+ ret[:output] = @_execution.output
142
+ end
143
+ ret
144
+ end
145
+
146
+ def matches_hash?(hash)
147
+ label == hash[:label] && @param_values == hash[:param_values]
148
+ end
149
+
150
+ def update_from_hash(hash)
151
+ raise "The step is not matching the hash #{hash.inspect}" unless matches_hash?(hash)
152
+ raise "Can't update step that was already executed" if @_execution
153
+ @_execution = Runner::StoredExecution.new(self, :status => hash[:status],
154
+ :output => hash[:output])
155
+ end
156
+
157
+ def inspect
158
+ ret = "#{self.class.name} label:#{label}"
159
+ ret << " params: #{@param_values.inspect}" unless @param_values.empty?
160
+ ret << " status: #{execution.status}" if executed?
161
+ ret
162
+ end
163
+
113
164
  class << self
114
165
  def ensure_instance
115
166
  new
@@ -1,6 +1,7 @@
1
1
  module ForemanMaintain
2
2
  class Param
3
3
  attr_reader :name, :description, :options
4
+
4
5
  def initialize(name, description, options, &block)
5
6
  options.validate_options!(:description, :required, :flag, :array)
6
7
  @name = name
@@ -7,8 +7,14 @@ module ForemanMaintain
7
7
  end
8
8
  require 'foreman_maintain/reporter/cli_reporter'
9
9
 
10
+ DECISION_MAPPER = {
11
+ %w[y yes] => :yes,
12
+ %w[n next no] => :no,
13
+ %w[q quit] => :quit
14
+ }.freeze
15
+
10
16
  # Each public method is a hook called by executor at the specific point
11
- def before_scenario_starts(_scenario); end
17
+ def before_scenario_starts(_scenario, _last_scenario = nil); end
12
18
 
13
19
  def before_execution_starts(_execution); end
14
20
 
@@ -16,7 +22,14 @@ module ForemanMaintain
16
22
 
17
23
  def after_scenario_finishes(_scenario); end
18
24
 
19
- def on_next_steps(_steps); end
25
+ def on_next_steps(steps)
26
+ return if steps.empty?
27
+ if steps.size > 1
28
+ multiple_steps_decision(steps)
29
+ else
30
+ single_step_decision(steps.first)
31
+ end
32
+ end
20
33
 
21
34
  def with_spinner(_message, &_block)
22
35
  yield DummySpinner.new
@@ -29,7 +42,75 @@ module ForemanMaintain
29
42
  def ask(_message); end
30
43
 
31
44
  def assumeyes?
32
- false
45
+ @assumeyes
46
+ end
47
+
48
+ # simple yes/no question, returns :yes, :no or :quit
49
+ def ask_decision(message, options = {})
50
+ options.validate_options!(:assumeyes)
51
+ assumeyes = options.fetch(:assumeyes, assumeyes?)
52
+ if assumeyes
53
+ print("#{message} (assuming yes)")
54
+ return :yes
55
+ end
56
+ until_valid_decision do
57
+ filter_decision(ask("#{message}, [y(yes), n(no), q(quit)]"))
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def single_step_decision(step)
64
+ answer = ask_decision("Continue with step [#{step.description}]?")
65
+ if answer == :yes
66
+ step
67
+ else
68
+ answer
69
+ end
70
+ end
71
+
72
+ def multiple_steps_decision(steps)
73
+ puts 'There are multiple steps to proceed:'
74
+ steps.each_with_index do |step, index|
75
+ puts "#{index + 1}) #{step.description}"
76
+ end
77
+ ask_to_select('Select step to continue', steps, &:description)
78
+ end
79
+
80
+ def filter_decision(answer)
81
+ decision = nil
82
+ DECISION_MAPPER.each do |options, decision_label|
83
+ decision = decision_label if options.include?(answer)
84
+ end
85
+ decision
86
+ end
87
+
88
+ # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
89
+ def ask_to_select(message, steps)
90
+ if assumeyes?
91
+ puts('(assuming first option)')
92
+ return steps.first
93
+ end
94
+ until_valid_decision do
95
+ answer = ask("#{message}, [n(next), q(quit)]")
96
+ if answer =~ /^\d+$/ && (answer.to_i - 1) < steps.size
97
+ steps[answer.to_i - 1]
98
+ else
99
+ decision = filter_decision(answer)
100
+ if decision == :yes
101
+ steps.first
102
+ else
103
+ decision
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ # loop over the block until it returns some non-false value
110
+ def until_valid_decision
111
+ decision = nil
112
+ decision = yield until decision
113
+ decision
33
114
  end
34
115
  end
35
116
  end