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
@@ -4,12 +4,6 @@ require 'highline'
4
4
  module ForemanMaintain
5
5
  class Reporter
6
6
  class CLIReporter < Reporter
7
- DECISION_MAPPER = {
8
- %w[y yes] => :yes,
9
- %w[n next no] => :no,
10
- %w[q quit] => :quit
11
- }.freeze
12
-
13
7
  # Simple spinner able to keep updating current line
14
8
  class Spinner
15
9
  def initialize(reporter, interval = 0.1)
@@ -87,7 +81,7 @@ module ForemanMaintain
87
81
  end
88
82
 
89
83
  def before_scenario_starts(scenario)
90
- puts "\nRunning #{scenario.description || scenario.class}"
84
+ puts "Running #{scenario.description || scenario.class}"
91
85
  hline('=')
92
86
  end
93
87
 
@@ -147,33 +141,31 @@ module ForemanMaintain
147
141
  def after_execution_finishes(execution)
148
142
  puts_status(execution.status)
149
143
  puts(execution.output) unless execution.output.empty?
144
+ if execution.status == :already_run
145
+ puts(<<-MESSAGE.strip_heredoc)
146
+ The step was skipped as it was already run and it is marked
147
+ as run_once. Use --force to enforce the execution.
148
+ MESSAGE
149
+ end
150
150
  hline
151
151
  new_line_if_needed
152
152
  end
153
153
 
154
- def after_scenario_finishes(_scenario); end
155
-
156
- def on_next_steps(steps)
157
- return if steps.empty?
158
- if steps.size > 1
159
- multiple_steps_decision(steps)
160
- else
161
- single_step_decision(steps.first)
162
- end
154
+ def after_scenario_finishes(scenario)
155
+ scenario_failure_message(scenario)
156
+ puts "\n"
163
157
  end
164
158
 
165
159
  def clear_line
166
160
  print "\r" + ' ' * @max_length + "\r"
167
161
  end
168
162
 
169
- private
170
-
171
163
  def assumeyes?
172
164
  @assumeyes
173
165
  end
174
166
 
175
167
  def single_step_decision(step)
176
- answer = ask_decision("Continue with step [#{step.description}]?")
168
+ answer = ask_decision("Continue with step [#{step.runtime_message}]?")
177
169
  if answer == :yes
178
170
  step
179
171
  else
@@ -184,9 +176,9 @@ module ForemanMaintain
184
176
  def multiple_steps_decision(steps)
185
177
  puts 'There are multiple steps to proceed:'
186
178
  steps.each_with_index do |step, index|
187
- puts "#{index + 1}) #{step.description}"
179
+ puts "#{index + 1}) #{step.runtime_message}"
188
180
  end
189
- ask_to_select('Select step to continue', steps, &:description)
181
+ ask_to_select('Select step to continue', steps, &:runtime_message)
190
182
  end
191
183
 
192
184
  def ask_decision(message)
@@ -260,6 +252,7 @@ module ForemanMaintain
260
252
  :fail => { :label => '[FAIL]', :color => :red },
261
253
  :running => { :label => '[RUNNING]', :color => :blue },
262
254
  :skipped => { :label => '[SKIPPED]', :color => :yellow },
255
+ :already_run => { :label => '[ALREADY RUN]', :color => :yellow },
263
256
  :warning => { :label => '[WARNING]', :color => :yellow } }
264
257
  properties = mapping[status]
265
258
  @hl.color(properties[:label], properties[:color], :bold)
@@ -272,6 +265,49 @@ module ForemanMaintain
272
265
  def record_last_line(string)
273
266
  @last_line = string.lines.to_a.last
274
267
  end
268
+
269
+ private
270
+
271
+ def scenario_failure_message(scenario)
272
+ return if scenario.passed?
273
+ message = []
274
+ message << <<-MESSAGE.strip_heredoc
275
+ Scenario [#{scenario.description}] failed.
276
+ MESSAGE
277
+ recommend = []
278
+ steps_with_error = scenario.steps_with_error(:whitelisted => false)
279
+ unless steps_with_error.empty?
280
+ message << <<-MESSAGE.strip_heredoc
281
+ The following steps ended up in failing state:
282
+
283
+ #{format_steps(steps_with_error, "\n", 2)}
284
+ MESSAGE
285
+ recommend << <<-MESSAGE.strip_heredoc
286
+ Resolve the failed steps and rerun
287
+ the command. In case the failures are false positives,
288
+ use --whitelist="#{steps_with_error.map(&:label_dashed).join(',')}"
289
+ MESSAGE
290
+ end
291
+
292
+ steps_with_warning = scenario.steps_with_warning(:whitelisted => false)
293
+ unless steps_with_warning.empty?
294
+ message << <<-MESSAGE.strip_heredoc
295
+ The following steps ended up in warning state:
296
+
297
+ #{format_steps(steps_with_warning, "\n", 2)}
298
+ MESSAGE
299
+
300
+ recommend << <<-MESSAGE.strip_heredoc
301
+ The steps in warning state itself might not mean there is an error,
302
+ but it should be reviews to ensure the behavior is expected
303
+ MESSAGE
304
+ end
305
+ puts((message + recommend).join("\n"))
306
+ end
307
+
308
+ def format_steps(steps, join_with = ', ', indent = 0)
309
+ steps.map { |s| "#{' ' * indent}[#{s.label_dashed}]" }.join(join_with)
310
+ end
275
311
  end
276
312
  end
277
313
  end
@@ -1,48 +1,73 @@
1
1
  module ForemanMaintain
2
2
  # Class responsible for running the scenario
3
3
  class Runner
4
+ attr_reader :reporter, :exit_code
5
+
4
6
  require 'foreman_maintain/runner/execution'
7
+ require 'foreman_maintain/runner/stored_execution'
5
8
  def initialize(reporter, scenarios, options = {})
6
- options.validate_options!(:assumeyes)
9
+ options.validate_options!(:assumeyes, :whitelist, :force)
7
10
  @assumeyes = options.fetch(:assumeyes, false)
11
+ @whitelist = options.fetch(:whitelist, [])
12
+ @force = options.fetch(:force, false)
8
13
  @reporter = reporter
9
14
  @scenarios = Array(scenarios)
10
- @scenarios_with_dependencies = scenarios_with_dependencies
11
15
  @quit = false
16
+ @last_scenario = nil
17
+ @exit_code = 0
12
18
  end
13
19
 
14
- def assumeyes?
15
- @assumeyes
20
+ def quit?
21
+ @quit
16
22
  end
17
23
 
18
- def scenarios_with_dependencies
19
- @scenarios.map do |scenario|
20
- scenario.before_scenarios + [scenario]
21
- end.flatten
24
+ def assumeyes?
25
+ @assumeyes
22
26
  end
23
27
 
24
28
  def run
25
- scenarios_with_dependencies.each do |scenario|
29
+ @scenarios.each do |scenario|
26
30
  run_scenario(scenario)
31
+ break if @quit
27
32
  end
28
33
  end
29
34
 
30
- def run_scenario(scenario)
31
- @steps_to_run = scenario.steps.dup
32
- @steps_to_run = ForemanMaintain::DependencyGraph.sort(@steps_to_run)
33
- @reporter.before_scenario_starts(scenario)
34
- while !@quit && !@steps_to_run.empty?
35
- step = @steps_to_run.shift
36
- @reporter.puts('Rerunning the check after fix procedure') if rerun_check?(step)
37
- execution = Execution.new(step, @reporter)
38
- execution.run
39
- ask_about_offered_steps(step)
35
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
36
+ def run_scenario(scenario, confirm = true)
37
+ return if scenario.steps.empty?
38
+ raise 'The runner is already in quit state' if quit?
39
+ if confirm
40
+ confirm_scenario(scenario)
41
+ return if quit?
40
42
  end
43
+ scenario.before_scenarios.flatten.each { |before_scenario| run_scenario(before_scenario) }
44
+ return if quit? # the before scenarios caused the stop of the execution
45
+ @reporter.before_scenario_starts(scenario)
46
+ run_steps(scenario, scenario.steps)
41
47
  @reporter.after_scenario_finishes(scenario)
48
+ ensure
49
+ @last_scenario = scenario unless scenario.steps.empty?
50
+ end
51
+
52
+ def whitelisted_step?(step)
53
+ @whitelist.include?(step.label_dashed.to_s)
54
+ end
55
+
56
+ def confirm_scenario(scenario)
57
+ return unless @last_scenario
58
+ decision = if @last_scenario.steps_with_error(:whitelisted => false).any?
59
+ :quit
60
+ elsif @last_scenario.steps_with_warning(:whitelisted => false).any?
61
+ reporter.ask_decision("Continue with [#{scenario.description}]")
62
+ end
63
+
64
+ ask_to_quit if [:quit, :no].include?(decision)
65
+ decision
42
66
  end
43
67
 
44
- def ask_to_quit(_step = nil)
68
+ def ask_to_quit(exit_code = 1)
45
69
  @quit = true
70
+ @exit_code = exit_code
46
71
  end
47
72
 
48
73
  def add_steps(*steps)
@@ -53,9 +78,42 @@ module ForemanMaintain
53
78
  end
54
79
  end
55
80
 
81
+ def storage
82
+ ForemanMaintain.storage(:default)
83
+ end
84
+
56
85
  private
57
86
 
58
- # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity
87
+ def run_steps(scenario, steps)
88
+ @steps_to_run = ForemanMaintain::DependencyGraph.sort(steps)
89
+ while !@quit && !@steps_to_run.empty?
90
+ execution = run_step(@steps_to_run.shift)
91
+ post_step_decisions(scenario, execution)
92
+ end
93
+ end
94
+
95
+ def run_step(step)
96
+ @reporter.puts('Rerunning the check after fix procedure') if rerun_check?(step)
97
+ execution = Execution.new(step, @reporter,
98
+ :whitelisted => whitelisted_step?(step),
99
+ :storage => storage,
100
+ :force => @force)
101
+ execution.run
102
+ execution
103
+ ensure
104
+ storage.save
105
+ end
106
+
107
+ def post_step_decisions(scenario, execution)
108
+ step = execution.step
109
+ next_steps_decision = ask_about_offered_steps(step)
110
+ if next_steps_decision != :yes &&
111
+ execution.fail? && !execution.whitelisted? &&
112
+ scenario.run_strategy == :fail_fast
113
+ ask_to_quit
114
+ end
115
+ end
116
+
59
117
  def ask_about_offered_steps(step)
60
118
  if assumeyes? && rerun_check?(step)
61
119
  @reporter.puts 'Check still failing after attempt to fix. Skipping'
@@ -72,6 +130,7 @@ module ForemanMaintain
72
130
  chosen_steps = [decision]
73
131
  chosen_steps << step if step.is_a?(Check)
74
132
  add_steps(*chosen_steps)
133
+ :yes
75
134
  end
76
135
  end
77
136
  end
@@ -4,7 +4,7 @@ module ForemanMaintain
4
4
  class Execution
5
5
  include Concerns::Logger
6
6
  extend Forwardable
7
- def_delegators :@reporter, :with_spinner, :puts, :print, :ask, :assumeyes?
7
+ def_delegators :reporter, :with_spinner, :puts, :print, :ask, :assumeyes?
8
8
 
9
9
  # Step performed as part of the execution
10
10
  attr_reader :step
@@ -20,32 +20,57 @@ module ForemanMaintain
20
20
 
21
21
  attr_reader :reporter
22
22
 
23
- def initialize(step, reporter)
23
+ def initialize(step, reporter, options = {})
24
+ options.validate_options!(:whitelisted, :storage, :force)
24
25
  @step = step
25
26
  @reporter = reporter
26
27
  @status = :pending
27
28
  @output = ''
29
+ @whitelisted = options[:whitelisted]
30
+ @storage = options[:storage]
31
+ @force = options[:force]
28
32
  end
29
33
 
30
34
  def name
31
35
  @step.description
32
36
  end
33
37
 
38
+ def whitelisted?
39
+ @whitelisted
40
+ end
41
+
34
42
  def success?
35
- @status == :success
43
+ [:success, :already_run, :skipped].include?(@status)
36
44
  end
37
45
 
38
46
  def fail?
39
47
  @status == :fail
40
48
  end
41
49
 
50
+ def skipped?
51
+ @status == :skipped
52
+ end
53
+
54
+ def skip?
55
+ !@force && step.run_once? && step.executed? && step.success?
56
+ end
57
+
42
58
  def warning?
43
59
  @status == :warning
44
60
  end
45
61
 
62
+ # yaml storage to preserve key/value pairs between runs.
63
+ def storage
64
+ @storage || ForemanMaintain.storage(:default)
65
+ end
66
+
46
67
  def run
47
- @status = :running
48
68
  @reporter.before_execution_starts(self)
69
+ if skip?
70
+ @status = :already_run
71
+ return
72
+ end
73
+ @status = :running
49
74
  with_metadata_calculation do
50
75
  capture_errors do
51
76
  step.__run__(self)
@@ -0,0 +1,23 @@
1
+ module ForemanMaintain
2
+ class Runner
3
+ # Class representing an execution of a single step in scenario
4
+ class StoredExecution < Execution
5
+ include Concerns::Logger
6
+ extend Forwardable
7
+
8
+ def initialize(step, hash)
9
+ @step = step
10
+ @status = hash[:status]
11
+ @output = hash[:output]
12
+ end
13
+
14
+ def reporter
15
+ raise 'Can not access reporter from stored execution'
16
+ end
17
+
18
+ def run
19
+ raise 'Can not run stored execution'
20
+ end
21
+ end
22
+ end
23
+ end
@@ -2,33 +2,48 @@ module ForemanMaintain
2
2
  class Scenario
3
3
  include Concerns::Logger
4
4
  include Concerns::SystemHelpers
5
- include Concerns::Metadata
5
+ include Concerns::ScenarioMetadata
6
6
  include Concerns::Finders
7
+ extend Concerns::Finders
7
8
 
8
9
  attr_reader :steps
9
10
 
10
11
  class FilteredScenario < Scenario
11
- metadata { manual_detection }
12
+ metadata do
13
+ manual_detection
14
+ run_strategy :fail_slow
15
+ end
12
16
 
13
17
  attr_reader :filter_label, :filter_tags
14
18
 
15
- def initialize(filter)
19
+ def initialize(filter, definition_kinds = [:check])
16
20
  @filter_tags = filter[:tags]
17
21
  @filter_label = filter[:label]
18
- @steps = ForemanMaintain.available_checks(filter).map(&:ensure_instance)
22
+ @definition_kinds = definition_kinds
23
+ @steps = []
24
+ @steps += checks(filter) if definition_kinds.include?(:check)
25
+ @steps += procedures(filter) if definition_kinds.include?(:procedure)
19
26
  @steps = DependencyGraph.sort(@steps)
20
27
  end
21
28
 
22
- def description
29
+ def runtime_message
23
30
  if @filter_label
24
- "check with label [#{dashize(@filter_label)}]"
31
+ "#{kind_list} with label [#{dashize(@filter_label)}]"
25
32
  else
26
- "checks with tags #{tag_string(@filter_tags)}"
33
+ "#{kinds_list} with tags #{tag_string(@filter_tags)}"
27
34
  end
28
35
  end
29
36
 
30
37
  private
31
38
 
39
+ def kinds_list
40
+ @definition_kinds.map { |kind| kind.to_s + 's' }.join(' and ')
41
+ end
42
+
43
+ def kind_list
44
+ @definition_kinds.map(&:to_s).join(' or ')
45
+ end
46
+
32
47
  def tag_string(tags)
33
48
  tags.map { |tag| dashize("[#{tag}]") }.join(' ')
34
49
  end
@@ -36,12 +51,21 @@ module ForemanMaintain
36
51
  def dashize(string)
37
52
  string.to_s.tr('_', '-')
38
53
  end
54
+
55
+ def checks(filter)
56
+ ForemanMaintain.available_checks(filter).map(&:ensure_instance)
57
+ end
58
+
59
+ def procedures(filter)
60
+ ForemanMaintain.available_procedures(filter).map(&:ensure_instance)
61
+ end
39
62
  end
40
63
 
41
64
  class PreparationScenario < Scenario
42
65
  metadata do
43
66
  manual_detection
44
67
  description 'preparation steps required to run the next scenarios'
68
+ run_strategy :fail_slow
45
69
  end
46
70
 
47
71
  attr_reader :main_scenario
@@ -71,6 +95,37 @@ module ForemanMaintain
71
95
  end.uniq
72
96
  end
73
97
 
98
+ def executed_steps
99
+ steps.find_all(&:executed?)
100
+ end
101
+
102
+ def steps_with_error(options = {})
103
+ filter_whitelisted(executed_steps.find_all(&:fail?), options)
104
+ end
105
+
106
+ def steps_with_warning(options = {})
107
+ filter_whitelisted(executed_steps.find_all(&:warning?), options)
108
+ end
109
+
110
+ def filter_whitelisted(steps, options)
111
+ options.validate_options!(:whitelisted)
112
+ if options.key?(:whitelisted)
113
+ steps.select do |step|
114
+ options[:whitelisted] ? step.whitelisted? : !step.whitelisted?
115
+ end
116
+ else
117
+ steps
118
+ end
119
+ end
120
+
121
+ def passed?
122
+ (steps_with_error(:whitelisted => false) + steps_with_warning(:whitelisted => false)).empty?
123
+ end
124
+
125
+ def failed?
126
+ !passed?
127
+ end
128
+
74
129
  # scenarios to be run before this scenario
75
130
  def before_scenarios
76
131
  scenarios = []
@@ -96,5 +151,33 @@ module ForemanMaintain
96
151
  def inspect
97
152
  "#{self.class.metadata[:description]}<#{self.class.name}>"
98
153
  end
154
+
155
+ def to_hash
156
+ { :label => label,
157
+ :steps => steps.map(&:to_hash) }
158
+ end
159
+
160
+ def self.new_from_hash(hash)
161
+ scenarios = find_scenarios(:label => hash[:label])
162
+ unless scenarios.size == 1
163
+ raise "Could not find scenario #{hash[:label]}, found #{scenarios.size} scenarios"
164
+ end
165
+ scenario = scenarios.first
166
+ scenario.load_step_states(hash[:steps])
167
+ scenario
168
+ end
169
+
170
+ def load_step_states(steps_hash)
171
+ steps = self.steps.dup
172
+ steps_hash.each do |step_hash|
173
+ until steps.empty?
174
+ step = steps.shift
175
+ if step.matches_hash?(step_hash)
176
+ step.update_from_hash(step_hash)
177
+ break
178
+ end
179
+ end
180
+ end
181
+ end
99
182
  end
100
183
  end