foreman_maintain 0.0.2 → 0.0.3

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +107 -25
  3. data/bin/foreman-maintain +1 -0
  4. data/definitions/checks/disk_speed_minimal.rb +31 -19
  5. data/definitions/checks/foreman_tasks/invalid/check_old.rb +20 -0
  6. data/definitions/checks/foreman_tasks/invalid/check_pending_state.rb +20 -0
  7. data/definitions/checks/foreman_tasks/invalid/check_planning_state.rb +20 -0
  8. data/definitions/checks/foreman_tasks/not_paused.rb +29 -0
  9. data/definitions/checks/foreman_tasks/not_running.rb +14 -0
  10. data/definitions/checks/sync_plans/with_disabled_status.rb +18 -0
  11. data/definitions/checks/sync_plans/with_enabled_status.rb +18 -0
  12. data/definitions/checks/system_registration.rb +31 -0
  13. data/definitions/features/downstream.rb +5 -3
  14. data/definitions/features/foreman_1_11_x.rb +4 -2
  15. data/definitions/features/foreman_1_7_x.rb +5 -3
  16. data/definitions/features/foreman_database.rb +11 -5
  17. data/definitions/features/foreman_tasks.rb +118 -9
  18. data/definitions/features/sync_plans.rb +75 -0
  19. data/definitions/features/upstream.rb +5 -3
  20. data/definitions/procedures/foreman_tasks/delete.rb +33 -0
  21. data/definitions/procedures/foreman_tasks/resume.rb +14 -0
  22. data/definitions/procedures/foreman_tasks/ui_investigate.rb +19 -0
  23. data/definitions/procedures/hammer_setup.rb +50 -0
  24. data/definitions/procedures/install_package.rb +17 -0
  25. data/definitions/procedures/sync_plans/disable.rb +22 -0
  26. data/definitions/procedures/sync_plans/enable.rb +21 -0
  27. data/definitions/scenarios/pre_upgrade_check_foreman_1_14.rb +7 -6
  28. data/definitions/scenarios/pre_upgrade_check_satellite_6_0_z.rb +8 -6
  29. data/definitions/scenarios/pre_upgrade_check_satellite_6_1.rb +8 -6
  30. data/definitions/scenarios/pre_upgrade_check_satellite_6_1_z.rb +8 -6
  31. data/definitions/scenarios/pre_upgrade_check_satellite_6_2.rb +8 -6
  32. data/definitions/scenarios/pre_upgrade_check_satellite_6_2_z.rb +8 -6
  33. data/definitions/scenarios/pre_upgrade_check_satellite_6_3.rb +8 -6
  34. data/lib/foreman_maintain.rb +52 -5
  35. data/lib/foreman_maintain/check.rb +18 -12
  36. data/lib/foreman_maintain/cli/base.rb +9 -2
  37. data/lib/foreman_maintain/cli/health_command.rb +2 -1
  38. data/lib/foreman_maintain/cli/upgrade_command.rb +2 -0
  39. data/lib/foreman_maintain/concerns/hammer.rb +20 -0
  40. data/lib/foreman_maintain/concerns/logger.rb +1 -5
  41. data/lib/foreman_maintain/concerns/metadata.rb +138 -31
  42. data/lib/foreman_maintain/concerns/system_helpers.rb +36 -32
  43. data/lib/foreman_maintain/config.rb +40 -5
  44. data/lib/foreman_maintain/core_ext.rb +24 -0
  45. data/lib/foreman_maintain/detector.rb +12 -13
  46. data/lib/foreman_maintain/error.rb +28 -0
  47. data/lib/foreman_maintain/executable.rb +86 -11
  48. data/lib/foreman_maintain/feature.rb +1 -0
  49. data/lib/foreman_maintain/param.rb +47 -0
  50. data/lib/foreman_maintain/reporter.rb +20 -3
  51. data/lib/foreman_maintain/reporter/cli_reporter.rb +166 -66
  52. data/lib/foreman_maintain/runner.rb +56 -13
  53. data/lib/foreman_maintain/runner/execution.rb +8 -0
  54. data/lib/foreman_maintain/scenario.rb +46 -2
  55. data/lib/foreman_maintain/top_level_modules.rb +3 -0
  56. data/lib/foreman_maintain/utils.rb +2 -0
  57. data/lib/foreman_maintain/utils/command_runner.rb +101 -0
  58. data/lib/foreman_maintain/utils/disk/device.rb +5 -9
  59. data/lib/foreman_maintain/utils/hammer.rb +78 -0
  60. data/lib/foreman_maintain/version.rb +1 -1
  61. data/lib/foreman_maintain/yaml_storage.rb +48 -0
  62. metadata +27 -9
  63. data/definitions/checks/foreman_tasks_not_paused.rb +0 -14
  64. data/definitions/checks/foreman_tasks_not_running.rb +0 -10
  65. data/definitions/procedures/foreman_tasks_resume.rb +0 -13
  66. data/lib/foreman_maintain/logger.rb +0 -11
@@ -0,0 +1,21 @@
1
+ module Procedures::SyncPlans
2
+ class Enable < ForemanMaintain::Procedure
3
+ metadata do
4
+ for_feature :sync_plans
5
+ description 're-enable sync plans'
6
+ end
7
+
8
+ def run
9
+ enabled_sync_plans
10
+ end
11
+
12
+ private
13
+
14
+ def enabled_sync_plans
15
+ with_spinner('re-enabling sync plans') do |spinner|
16
+ record_ids = feature(:sync_plans).make_enable
17
+ spinner.update "Total #{record_ids.length} sync plans are now enabled."
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,12 +1,13 @@
1
1
  class Scenarios::PreUpgradeCheckForeman_1_14 < ForemanMaintain::Scenario
2
- description 'checks before upgrading to Foreman 1.14'
3
- confine do
4
- feature(:upstream)
2
+ metadata do
3
+ description 'checks before upgrading to Foreman 1.14'
4
+ tags :pre_upgrade_check
5
+ confine do
6
+ feature(:upstream)
7
+ end
5
8
  end
6
9
 
7
- tags :pre_upgrade_check
8
-
9
10
  def compose
10
- steps.concat(find_checks(:basic))
11
+ add_steps(find_checks(:default))
11
12
  end
12
13
  end
@@ -1,12 +1,14 @@
1
1
  class Scenarios::PreUpgradeCheckSatellite_6_0_z < ForemanMaintain::Scenario
2
- tags :pre_upgrade_check, :satellite_6_0_z
3
- description 'checks before upgrading to Satellite 6.0'
4
- confine do
5
- feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.0.')
2
+ metadata do
3
+ tags :pre_upgrade_check, :satellite_6_0_z
4
+ description 'checks before upgrading to Satellite 6.0'
5
+ confine do
6
+ feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.0.')
7
+ end
6
8
  end
7
9
 
8
10
  def compose
9
- steps.concat(find_checks(:basic))
10
- steps.concat(find_checks(:pre_upgrade))
11
+ add_steps(find_checks(:default))
12
+ add_steps(find_checks(:pre_upgrade))
11
13
  end
12
14
  end
@@ -1,12 +1,14 @@
1
1
  class Scenarios::PreUpgradeCheckSatellite_6_1 < ForemanMaintain::Scenario
2
- tags :pre_upgrade_check, :satellite_6_1
3
- description 'checks before upgrading to Satellite 6.1'
4
- confine do
5
- feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.0.')
2
+ metadata do
3
+ description 'checks before upgrading to Satellite 6.1'
4
+ tags :pre_upgrade_check, :satellite_6_1
5
+ confine do
6
+ feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.0.')
7
+ end
6
8
  end
7
9
 
8
10
  def compose
9
- steps.concat(find_checks(:basic))
10
- steps.concat(find_checks(:pre_upgrade))
11
+ add_steps(find_checks(:default))
12
+ add_steps(find_checks(:pre_upgrade))
11
13
  end
12
14
  end
@@ -1,12 +1,14 @@
1
1
  class Scenarios::PreUpgradeCheckSatellite_6_1_z < ForemanMaintain::Scenario
2
- tags :pre_upgrade_check, :satellite_6_1_z
3
- description 'checks before upgrading to Satellite 6.1.z'
4
- confine do
5
- feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.1.')
2
+ metadata do
3
+ description 'checks before upgrading to Satellite 6.1.z'
4
+ tags :pre_upgrade_check, :satellite_6_1_z
5
+ confine do
6
+ feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.1.')
7
+ end
6
8
  end
7
9
 
8
10
  def compose
9
- steps.concat(find_checks(:basic))
10
- steps.concat(find_checks(:pre_upgrade))
11
+ add_steps(find_checks(:default))
12
+ add_steps(find_checks(:pre_upgrade))
11
13
  end
12
14
  end
@@ -1,12 +1,14 @@
1
1
  class Scenarios::PreUpgradeCheckSatellite_6_2 < ForemanMaintain::Scenario
2
- tags :pre_upgrade_check, :satellite_6_2
3
- description 'checks before upgrading to Satellite 6.2'
4
- confine do
5
- feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.1.')
2
+ metadata do
3
+ description 'checks before upgrading to Satellite 6.2'
4
+ tags :pre_upgrade_check, :satellite_6_2
5
+ confine do
6
+ feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.1.')
7
+ end
6
8
  end
7
9
 
8
10
  def compose
9
- steps.concat(find_checks(:basic))
10
- steps.concat(find_checks(:pre_upgrade))
11
+ add_steps(find_checks(:default))
12
+ add_steps(find_checks(:pre_upgrade))
11
13
  end
12
14
  end
@@ -1,12 +1,14 @@
1
1
  class Scenarios::PreUpgradeCheckSatellite_6_2_z < ForemanMaintain::Scenario
2
- tags :pre_upgrade_check, :satellite_6_2_z
3
- description 'checks before upgrading to Satellite 6.2.z'
4
- confine do
5
- feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.2.')
2
+ metadata do
3
+ description 'checks before upgrading to Satellite 6.2.z'
4
+ tags :pre_upgrade_check, :satellite_6_2_z
5
+ confine do
6
+ feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.2.')
7
+ end
6
8
  end
7
9
 
8
10
  def compose
9
- steps.concat(find_checks(:basic))
10
- steps.concat(find_checks(:pre_upgrade))
11
+ add_steps(find_checks(:default))
12
+ add_steps(find_checks(:pre_upgrade))
11
13
  end
12
14
  end
@@ -1,12 +1,14 @@
1
1
  class Scenarios::PreUpgradeCheckSatellite_6_3 < ForemanMaintain::Scenario
2
- tags :pre_upgrade_check, :satellite_6_3
3
- description 'checks before upgrading to Satellite 6.3'
4
- confine do
5
- feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.2.')
2
+ metadata do
3
+ description 'checks before upgrading to Satellite 6.3'
4
+ tags :pre_upgrade_check, :satellite_6_3
5
+ confine do
6
+ feature(:downstream) && feature(:downstream).current_version.to_s.start_with?('6.2.')
7
+ end
6
8
  end
7
9
 
8
10
  def compose
9
- steps.concat(find_checks(:basic))
10
- steps.concat(find_checks(:pre_upgrade))
11
+ add_steps(find_checks(:default))
12
+ add_steps(find_checks(:pre_upgrade))
11
13
  end
12
14
  end
@@ -2,16 +2,23 @@ if RUBY_VERSION <= '1.8.7'
2
2
  require 'rubygems'
3
3
  end
4
4
 
5
+ require 'forwardable'
5
6
  require 'json'
7
+ require 'logger'
8
+ require 'yaml'
6
9
 
7
10
  module ForemanMaintain
11
+ require 'foreman_maintain/core_ext'
8
12
  require 'foreman_maintain/concerns/logger'
13
+ require 'foreman_maintain/concerns/finders'
9
14
  require 'foreman_maintain/concerns/metadata'
10
15
  require 'foreman_maintain/concerns/system_helpers'
11
- require 'foreman_maintain/concerns/finders'
16
+ require 'foreman_maintain/concerns/hammer'
12
17
  require 'foreman_maintain/top_level_modules'
18
+ require 'foreman_maintain/yaml_storage'
13
19
  require 'foreman_maintain/config'
14
20
  require 'foreman_maintain/detector'
21
+ require 'foreman_maintain/param'
15
22
  require 'foreman_maintain/feature'
16
23
  require 'foreman_maintain/executable'
17
24
  require 'foreman_maintain/check'
@@ -20,13 +27,29 @@ module ForemanMaintain
20
27
  require 'foreman_maintain/runner'
21
28
  require 'foreman_maintain/reporter'
22
29
  require 'foreman_maintain/utils'
30
+ require 'foreman_maintain/error'
23
31
 
24
32
  class << self
25
- attr_accessor :config
33
+ attr_accessor :config, :logger
34
+
35
+ LOGGER_LEVEL_MAPPING = {
36
+ 'debug' => ::Logger::DEBUG,
37
+ 'info' => ::Logger::INFO,
38
+ 'warn' => ::Logger::WARN,
39
+ 'error' => ::Logger::ERROR,
40
+ 'fatal' => ::Logger::FATAL,
41
+ 'unknown' => ::Logger::UNKNOWN
42
+ }.freeze
26
43
 
27
- def setup(configuration = {})
28
- self.config = Config.new(configuration)
44
+ def setup(options = {})
45
+ # using a queue, we can log the messages which are generated before initializing logger
46
+ self.config = Config.new(options)
29
47
  load_definitions
48
+ init_logger
49
+ end
50
+
51
+ def config_file
52
+ config.config_file
30
53
  end
31
54
 
32
55
  def load_definitions
@@ -34,7 +57,8 @@ module ForemanMaintain
34
57
  # between the definitions directories
35
58
  $LOAD_PATH.concat(config.definitions_dirs)
36
59
  config.definitions_dirs.each do |definitions_dir|
37
- Dir.glob(File.join(definitions_dir, '**', '*.rb')).each { |f| require f }
60
+ file_paths = File.expand_path(File.join(definitions_dir, '**', '*.rb'))
61
+ Dir.glob(file_paths).each { |f| require f }
38
62
  end
39
63
  end
40
64
 
@@ -57,5 +81,28 @@ module ForemanMaintain
57
81
  def available_procedures(*args)
58
82
  detector.available_procedures(*args)
59
83
  end
84
+
85
+ def init_logger
86
+ # Note - If timestamp added to filename then number of log files i.e second
87
+ # argument to Logger.new will not work as expected
88
+ filename = File.expand_path("#{config.log_dir}/foreman-maintain.log")
89
+ @logger = Logger.new(filename, 10, 10_240_000).tap do |logger|
90
+ logger.level = LOGGER_LEVEL_MAPPING[config.log_level] || Logger::DEBUG
91
+ logger.datetime_format = '%Y-%m-%d %H:%M:%S%z '
92
+ end
93
+ pickup_log_messages
94
+ end
95
+
96
+ def pickup_log_messages
97
+ return if config.pre_setup_log_messages.empty?
98
+ config.pre_setup_log_messages.each { |msg| logger.info msg }
99
+ config.pre_setup_log_messages.clear
100
+ end
101
+
102
+ def storage(label)
103
+ ForemanMaintain::YamlStorage.load(label)
104
+ rescue => e
105
+ logger.error "Invalid Storage label i.e #{label}. Error - #{e.message}"
106
+ end
60
107
  end
61
108
  end
@@ -7,15 +7,20 @@ module ForemanMaintain
7
7
 
8
8
  attr_accessor :associated_feature
9
9
 
10
- class Fail < StandardError
11
- end
12
-
13
- def initialize(associated_feature)
14
- @associated_feature = associated_feature
15
- end
16
-
17
- def assert(condition, error_message)
18
- raise Fail, error_message unless condition
10
+ # run condition and mark the check as failed when not passing
11
+ #
12
+ # ==== Options
13
+ #
14
+ # * +:next_steps* - one or more procedures that can be followed to address
15
+ # the failure, will be offered to the user when running
16
+ # in interactive mode
17
+ def assert(condition, error_message, options = {})
18
+ options = options.validate_options!(:next_steps)
19
+ unless condition
20
+ next_steps = Array(options.fetch(:next_steps, []))
21
+ self.next_steps.concat(next_steps)
22
+ raise Error::Fail, error_message
23
+ end
19
24
  end
20
25
 
21
26
  # public method to be overriden
@@ -26,9 +31,10 @@ module ForemanMaintain
26
31
  # internal method called by executor
27
32
  def __run__(execution)
28
33
  super
29
- rescue Fail => e
30
- execution.status = :fail
31
- execution.output << e.message
34
+ rescue Error::Fail => e
35
+ fail!(e.message)
36
+ rescue Error::Warn => e
37
+ warn!(e.message)
32
38
  end
33
39
  end
34
40
  end
@@ -20,11 +20,13 @@ module ForemanMaintain
20
20
  end
21
21
 
22
22
  def reporter
23
- @reporter ||= ForemanMaintain::Reporter::CLIReporter.new
23
+ @reporter ||= ForemanMaintain::Reporter::CLIReporter.new(STDOUT,
24
+ STDIN,
25
+ :assumeyes => assumeyes?)
24
26
  end
25
27
 
26
28
  def run_scenario(scenario)
27
- ForemanMaintain::Runner.new(reporter, scenario).run
29
+ ForemanMaintain::Runner.new(reporter, scenario, :assumeyes => assumeyes?).run
28
30
  end
29
31
 
30
32
  def available_checks
@@ -54,6 +56,11 @@ module ForemanMaintain
54
56
  tags.split(',').map(&:strip).map { |tag| underscorize(tag).to_sym }
55
57
  end
56
58
  end
59
+
60
+ def self.interactive_option
61
+ option ['-y', '--assumeyes'], :flag,
62
+ 'Automatically answer yes for all questions'
63
+ end
57
64
  end
58
65
  end
59
66
  end
@@ -30,6 +30,7 @@ module ForemanMaintain
30
30
  subcommand 'check', 'Run the health checks against the system' do
31
31
  label_option
32
32
  tags_option
33
+ interactive_option
33
34
 
34
35
  def execute
35
36
  scenario = Scenario::FilteredScenario.new(filter)
@@ -45,7 +46,7 @@ module ForemanMaintain
45
46
  if label
46
47
  { :label => label }
47
48
  else
48
- { :tags => tags || [:basic] }
49
+ { :tags => tags || [:default] }
49
50
  end
50
51
  end
51
52
 
@@ -40,6 +40,8 @@ module ForemanMaintain
40
40
 
41
41
  subcommand 'check', 'Run pre-upgrade checks for upgradeing to specified version' do
42
42
  parameter 'TARGET_VERSION', 'Target version of the upgrade', :required => false
43
+ interactive_option
44
+
43
45
  def execute
44
46
  versions_to_scenarios = available_target_versions(:pre_upgrade_check)
45
47
  scenario = versions_to_scenarios[target_version]
@@ -0,0 +1,20 @@
1
+ module ForemanMaintain
2
+ module Concerns
3
+ module Hammer
4
+ def self.included(base)
5
+ base.metadata do
6
+ preparation_steps { Procedures::HammerSetup.new }
7
+ end
8
+ end
9
+
10
+ # Run a hammer command, examples:
11
+ # hammer('host list')
12
+ def hammer(args)
13
+ Utils::Hammer.instance.run_command(args)
14
+ end
15
+
16
+ # TODO: method for specifying that the check that includes this method
17
+ # requires hammer to be setup
18
+ end
19
+ end
20
+ end
@@ -1,12 +1,8 @@
1
- require 'logger'
2
-
3
1
  module ForemanMaintain
4
2
  module Concerns
5
3
  module Logger
6
4
  def logger
7
- @logger ||= ::Logger.new($stderr).tap do |logger|
8
- logger.level = ForemanMaintain.config.log_level
9
- end
5
+ @logger ||= ForemanMaintain.logger
10
6
  end
11
7
  end
12
8
  end
@@ -1,21 +1,61 @@
1
1
  module ForemanMaintain
2
2
  module Concerns
3
3
  module Metadata
4
- module DSL
4
+ # limit of steps dependent on each other, to avoid endless recursion
5
+ MAX_PREPARATION_STEPS_DEPTH = 20
6
+
7
+ class << self
8
+ # modules not to be included in autogenerated labels
9
+ attr_accessor :top_level_modules
10
+ end
11
+
12
+ class DSL
13
+ attr_reader :data
14
+
15
+ def initialize(data = {})
16
+ @data = data
17
+ end
18
+
5
19
  def label(label)
6
- metadata[:label] = label
20
+ @data[:label] = label
7
21
  end
8
22
 
9
23
  def tags(*tags)
10
- metadata[:tags].concat(tags)
24
+ @data[:tags].concat(tags)
11
25
  end
12
26
 
13
27
  def description(description)
14
- metadata[:description] = description
28
+ @data[:description] = description
15
29
  end
16
30
 
17
31
  def confine(&block)
18
- metadata[:confine_blocks] << block
32
+ @data[:confine_blocks] << block
33
+ end
34
+
35
+ # Parametrize the definition.
36
+ #
37
+ # == Arguments
38
+ #
39
+ # +name+: Name (Symbol) of the attribute
40
+ # +description_or_options+: Description string or a Hash with options
41
+ # +options+: Hash with options (unless specified in +descriptions_or_options+)
42
+ # +&block+: block to be called when processing the data: can be used for validation
43
+ # and type-casing of the value: expected to return the value to be used
44
+ #
45
+ # == Options
46
+ #
47
+ # +:description+: String describing the parameter
48
+ # +:required+: true if required
49
+ # +:flag+: param is just a true/false value: not expecting other values
50
+ #
51
+ def param(name, descripiton_or_options = {}, options = {}, &block)
52
+ case descripiton_or_options
53
+ when String
54
+ description = descripiton_or_options
55
+ when Hash
56
+ options = options.merge(descripiton_or_options) if descripiton_or_options.is_a?(Hash)
57
+ end
58
+ @data[:params][name] = Param.new(name, description, options, &block)
19
59
  end
20
60
 
21
61
  # Mark the class as manual: this means the instance
@@ -24,23 +64,33 @@ module ForemanMaintain
24
64
  # The classes marked for manual detect need to be initialized
25
65
  # in from other places (such as `additional_features` in Feature)
26
66
  def manual_detection
27
- @autodetect = false
67
+ @data[:autodetect] = false
28
68
  end
29
69
 
30
- def autodetect?
31
- defined?(@autodetect) ? @autodetect : true
70
+ # in the block, define one or more preparation steps needed
71
+ # before executing this definition
72
+ def preparation_steps(&block)
73
+ @data[:preparation_steps_blocks] << block
32
74
  end
33
75
 
34
76
  # Specify what feature the definition related to.
35
77
  def for_feature(feature_label)
36
- metadata[:for_feature] = feature_label
78
+ @data[:for_feature] = feature_label
37
79
  confine do
38
80
  feature(feature_label)
39
81
  end
40
82
  end
83
+
84
+ def self.eval_dsl(metadata, &block)
85
+ new(metadata).tap do |dsl|
86
+ dsl.instance_eval(&block)
87
+ end.data
88
+ end
41
89
  end
42
90
 
43
91
  module ClassMethods
92
+ include Finders
93
+
44
94
  def inherited(klass)
45
95
  sub_classes << klass
46
96
  end
@@ -56,6 +106,10 @@ module ForemanMaintain
56
106
  @sub_classes ||= []
57
107
  end
58
108
 
109
+ def autodetect?
110
+ metadata.fetch(:autodetect, true)
111
+ end
112
+
59
113
  def all_sub_classes(ignore_abstract_classes = true)
60
114
  ret = []
61
115
  ret << self if !ignore_abstract_classes || !abstract_class
@@ -65,22 +119,87 @@ module ForemanMaintain
65
119
  ret
66
120
  end
67
121
 
68
- def metadata
122
+ def metadata(&block)
69
123
  @metadata ||= initialize_metadata
124
+ if block
125
+ DSL.eval_dsl(@metadata, &block)
126
+ end
127
+ @metadata
128
+ end
129
+
130
+ def label
131
+ metadata[:label] || generate_label
132
+ end
133
+
134
+ def description
135
+ metadata[:description] || to_s
136
+ end
137
+
138
+ def tags
139
+ metadata[:tags]
140
+ end
141
+
142
+ def params
143
+ metadata[:params] || []
70
144
  end
71
145
 
72
146
  def initialize_metadata
73
147
  { :tags => [],
74
- :confine_blocks => [] }.tap do |metadata|
148
+ :confine_blocks => [],
149
+ :params => {},
150
+ :preparation_steps_blocks => [] }.tap do |metadata|
75
151
  if superclass.respond_to?(:metadata)
76
152
  metadata[:label] = superclass.metadata[:label]
77
153
  end
78
154
  end
79
155
  end
156
+
157
+ def present?
158
+ evaluate_confines
159
+ end
160
+
161
+ def preparation_steps(recursion_depth = 0, trace = [])
162
+ raise "Too many dependent steps #{trace}" if recursion_depth > MAX_PREPARATION_STEPS_DEPTH
163
+ return @preparation_steps if defined?(@preparation_steps)
164
+ preparation_steps = metadata[:preparation_steps_blocks].map(&:call)
165
+ preparation_steps.each { |step| raise ArgumentError unless step.is_a?(Executable) }
166
+ all_preparation_steps = []
167
+ preparation_steps.each do |step|
168
+ all_preparation_steps.concat(
169
+ step.preparation_steps(recursion_depth + 1, trace + [step])
170
+ )
171
+ all_preparation_steps << step
172
+ end
173
+ @preparation_steps = all_preparation_steps
174
+ end
175
+
176
+ private
177
+
178
+ def evaluate_confines
179
+ raise 'Recursive confine block call detected' if @confines_evaluation_in_progress
180
+ @confines_evaluation_in_progress = true
181
+ metadata[:confine_blocks].all? do |block|
182
+ instance_exec(&block)
183
+ end
184
+ ensure
185
+ @confines_evaluation_in_progress = false
186
+ end
187
+
188
+ def generate_label
189
+ label_parts = []
190
+ name.split('::').reduce(Object) do |parent_constant, name|
191
+ constant = parent_constant.const_get(name)
192
+ unless Metadata.top_level_modules.include?(constant)
193
+ # CamelCase -> camel_case
194
+ label_parts << name.split(/(?=[A-Z])/).map(&:downcase)
195
+ end
196
+ constant
197
+ end
198
+ label_parts.join('_').to_sym
199
+ end
80
200
  end
81
201
 
82
202
  def self.included(klass)
83
- klass.extend(DSL)
84
203
  klass.extend(ClassMethods)
85
204
  end
86
205
 
@@ -89,35 +208,23 @@ module ForemanMaintain
89
208
  end
90
209
 
91
210
  def label
92
- metadata[:label] || generate_label
211
+ self.class.label
93
212
  end
94
213
 
95
214
  def description
96
- metadata[:description] || to_s
215
+ self.class.description
97
216
  end
98
217
 
99
218
  def tags
100
- metadata[:tags]
101
- end
102
-
103
- def present?
104
- @_present ||= evaluate_confines
219
+ self.class.tags
105
220
  end
106
221
 
107
- private
108
-
109
- def evaluate_confines
110
- raise 'Recursive dependency in confine blocks detected' if @confines_evaluation_in_progress
111
- @confines_evaluation_in_progress = true
112
- metadata[:confine_blocks].all? do |block|
113
- instance_exec(&block)
114
- end
115
- ensure
116
- @confines_evaluation_in_progress = false
222
+ def params
223
+ self.class.params
117
224
  end
118
225
 
119
- def generate_label
120
- self.class.name.split('::').last.split(/(?=[A-Z])/).map(&:downcase).join('_').to_sym
226
+ def preparation_steps(*args)
227
+ self.class.preparation_steps(*args)
121
228
  end
122
229
  end
123
230
  end