foreman_maintain 0.0.1

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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +193 -0
  4. data/bin/foreman-maintain +9 -0
  5. data/definitions/checks/foreman_tasks_not_paused.rb +14 -0
  6. data/definitions/checks/foreman_tasks_not_running.rb +10 -0
  7. data/definitions/features/downstream.rb +17 -0
  8. data/definitions/features/foreman_1_11_x.rb +7 -0
  9. data/definitions/features/foreman_1_7_x.rb +7 -0
  10. data/definitions/features/foreman_database.rb +15 -0
  11. data/definitions/features/foreman_tasks.rb +22 -0
  12. data/definitions/features/upstream.rb +7 -0
  13. data/definitions/procedures/foreman_tasks_resume.rb +13 -0
  14. data/definitions/scenarios/pre_upgrade_check_foreman_1_14.rb +12 -0
  15. data/definitions/scenarios/pre_upgrade_check_satellite_6_0_z.rb +12 -0
  16. data/definitions/scenarios/pre_upgrade_check_satellite_6_1.rb +12 -0
  17. data/definitions/scenarios/pre_upgrade_check_satellite_6_1_z.rb +12 -0
  18. data/definitions/scenarios/pre_upgrade_check_satellite_6_2.rb +12 -0
  19. data/definitions/scenarios/pre_upgrade_check_satellite_6_2_z.rb +12 -0
  20. data/definitions/scenarios/pre_upgrade_check_satellite_6_3.rb +12 -0
  21. data/lib/foreman_maintain.rb +54 -0
  22. data/lib/foreman_maintain/check.rb +34 -0
  23. data/lib/foreman_maintain/cli.rb +14 -0
  24. data/lib/foreman_maintain/cli/base.rb +47 -0
  25. data/lib/foreman_maintain/cli/health_command.rb +39 -0
  26. data/lib/foreman_maintain/cli/upgrade_command.rb +57 -0
  27. data/lib/foreman_maintain/concerns/finders.rb +69 -0
  28. data/lib/foreman_maintain/concerns/logger.rb +13 -0
  29. data/lib/foreman_maintain/concerns/metadata.rb +124 -0
  30. data/lib/foreman_maintain/concerns/system_helpers.rb +93 -0
  31. data/lib/foreman_maintain/config.rb +17 -0
  32. data/lib/foreman_maintain/detector.rb +146 -0
  33. data/lib/foreman_maintain/executable.rb +44 -0
  34. data/lib/foreman_maintain/feature.rb +22 -0
  35. data/lib/foreman_maintain/logger.rb +11 -0
  36. data/lib/foreman_maintain/procedure.rb +8 -0
  37. data/lib/foreman_maintain/reporter.rb +18 -0
  38. data/lib/foreman_maintain/reporter/cli_reporter.rb +177 -0
  39. data/lib/foreman_maintain/runner.rb +39 -0
  40. data/lib/foreman_maintain/runner/execution.rb +74 -0
  41. data/lib/foreman_maintain/scenario.rb +46 -0
  42. data/lib/foreman_maintain/top_level_modules.rb +13 -0
  43. data/lib/foreman_maintain/version.rb +3 -0
  44. metadata +173 -0
@@ -0,0 +1,193 @@
1
+ # Foreman Maintenance
2
+
3
+ `foreman_maintain` aims to provide various features that helps keeping the
4
+ Foreman/Satellite up and running. It supports multiple versions and subparts
5
+ of the Foreman infrastructure, including server or smart proxy and is smart
6
+ enough to provide the right tools for the specific version.
7
+
8
+ ## Usage
9
+
10
+ ```
11
+ Subcommands:
12
+ health Health related commands
13
+ list-checks List the checks based on criteria
14
+ list-tags List the tags to use for filtering checks
15
+ check Run the health checks against the system
16
+ --tags tags Limit only for specific set of tags
17
+
18
+ upgrade Upgrade related commands
19
+ list-versions List versions this system is upgradable to
20
+ check TARGET_VERSION Run pre-upgrade checks for upgradeing to specified version
21
+ ```
22
+
23
+ ## Implementation
24
+
25
+ `foreman_maintain` maps the CLI commands into definitions. This allows to keep the set
26
+ of the commands the user needs to know immutable from version-specific changes. The mapping
27
+ between the CLI commands and definitions is made by defining various metadata.
28
+
29
+ ## Definitions
30
+
31
+ There are various kinds of definitions possible:
32
+
33
+ * **Features** - aspects that can be present on the system. It can be
34
+ service (foreman, foreman-proxy), a feature (some Foreman plugin),
35
+ a link to external systems (e.g. registered foreman proxy, compute resource)
36
+ or another aspect that can be subject of health checks and maintenance procedures.
37
+ * **Checks** - definitions of health checks to indicate health of the system against the present features
38
+ * **Procedures** - steps for performing specific operations on the system
39
+ * **Scenarios** - combinations of checks and procedures to achieve some goal
40
+
41
+ The definitions for this components are present in `definitions` folder.
42
+
43
+ ### Features
44
+
45
+ Before `foreman_maintain` starts, it takes the set of `features` definition
46
+ and determines their pesence by running their `confine` blocks against
47
+ the system.
48
+
49
+ The `confine` block can run an external command to check if the feature
50
+ is there, or it can check present of other features.
51
+
52
+ A feature can define additional methods that can be used across other
53
+ definitions.
54
+
55
+ ```ruby
56
+ class Features::Foreman < ForemanMaintain::Feature
57
+ label :foreman
58
+
59
+ confine do
60
+ check_min_version('foreman', '1.7')
61
+ end
62
+
63
+ # helper method that can be used in other definitions like this:
64
+ #
65
+ # feature(:foreman).running?
66
+ def running?
67
+ execute?('systemctl foreman status')
68
+ end
69
+ end
70
+ ```
71
+
72
+ The features can inherit from each other, which allows overriding
73
+ methods for older versions, when newer version of the feature is present
74
+ in the system. This way, we shield the other definitions (checks, procedures,
75
+ scenarios) from version-specific nuances.
76
+
77
+ ### Checks
78
+
79
+ Checks define assertions to determine status of the system.
80
+
81
+ ```ruby
82
+ class Checks::ForemanIsRuning < ForemanMaintain::Check
83
+ for_feature :foreman
84
+
85
+ description 'check foreman service is running'
86
+
87
+ tags :basic
88
+
89
+ def run
90
+ # we are using methods of a feature.
91
+ assert(feature(:foreman).running?
92
+ 'There are currently paused tasks in the system')
93
+ end
94
+
95
+ # we can define additional steps to be executed after this check is finished
96
+ # based on the result
97
+ def next_steps
98
+ [procedure(Procedures::ForemanStart)] if fail?
99
+ end
100
+ end
101
+ ```
102
+
103
+ Similarly as features, also checks (and in fact all definitions) can used
104
+ `label`, `description` `confine` and `tags` keyword to describe themselves.
105
+
106
+ Every definition has a `label` (if not stated explicitly, it's
107
+ determined from the class name).
108
+
109
+ ### Procedures
110
+
111
+ Procedure defines some operation that can be performed against the system.
112
+ It can be part of a scenario or be linked from a check as a remediation step.
113
+
114
+ ```ruby
115
+ class Procedures::ForemanStart < ForemanMaintain::Procedure
116
+ for_feature :foreman
117
+
118
+ description 'start foreman service'
119
+
120
+ def run
121
+ feature(:foreman).start
122
+ end
123
+ end
124
+ ```
125
+
126
+ ### Scenarios
127
+
128
+ Scenarios represent a composition of various steps (checks and procedures) to
129
+ achieve some complex maintenance operation in the system (such as upgrade).
130
+
131
+
132
+ ```ruby
133
+ class Scenarios::PreUpgradeCheckForeman_1_14 < ForemanMaintain::Scenario
134
+ description 'checks before upgrading to Foreman 1.14'
135
+
136
+ confine do
137
+ feature(:upstream)
138
+ end
139
+
140
+ tags :pre_upgrade_check
141
+
142
+ # Method to be called when composing the steps of the scenario
143
+ def compose
144
+ # we can search for the checks by metadata
145
+ steps.concat(find_checks(:basic))
146
+ end
147
+ end
148
+ ```
149
+
150
+ ## Implementation components
151
+
152
+ In order to process the definitions, there are other components present in the `lib` directory.
153
+
154
+ * **Detector** - searches the checks/procedures/scenarios based on metadata & available features
155
+ * **Runner** - executes the scenario
156
+ * **Reporter** - reports the results of the run. It's possible to define
157
+ multiple reporters, based on the current use case (CLI, reporting to monitoring tool)
158
+ * **Cli** - Clamp-based command line infrastructure, mapping the definitions
159
+ to user commands.
160
+
161
+ ## Testing
162
+
163
+ Since a single version of `foreman_maintain` is meant to be used against multiple versions and
164
+ components combinations, the testing is a crucial part of the process.
165
+
166
+ There are multiple kind of tests `foreman_maintain`:
167
+
168
+ * unit tests for implementation components - can be found in `test/lib`
169
+ * this tests are independent of the real-world definitions and are focused
170
+ on the internal implementation (metadata definitions, features detection)
171
+ * unit tests for definitions - can be found in `test/definitions`
172
+ * this tests are focusing on testing of the code in definitions directory.
173
+ There is an infrastructure to simulate various combinations of features without
174
+ needing for actually having them present for development
175
+ * bats test - TBD
176
+ * to achieve stability, we also want to include bats tests as part of the infrastructure,
177
+ perhaps in combination with ansible playbooks to make the testing against real-world
178
+ instances as easy as possible.
179
+
180
+ Execute `rake` to run the tests.
181
+
182
+ ## Planned commands:
183
+
184
+ ```
185
+ foreman-maintain health [check|fix]
186
+ foreman-maintain upgrade [check|run|abort] [foreman_1_14, satellite_6_1, satellite_6_2]
187
+ foreman-maintain maintenance-mode [on|off]
188
+ foreman-maintain backup [save|restore]
189
+ foreman-maintain monitor [display|upload]
190
+ foreman-maintain debug [save|upload|tail]
191
+ foreman-maintain console
192
+ foreman-maintain config
193
+ ```
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
4
+
5
+ require 'foreman_maintain'
6
+ ForemanMaintain.setup
7
+
8
+ require 'foreman_maintain/cli'
9
+ ForemanMaintain::Cli::MainCommand.run
@@ -0,0 +1,14 @@
1
+ class Checks::ForemanTasksNotPaused < ForemanMaintain::Check
2
+ for_feature :foreman_tasks
3
+ description 'check for paused tasks'
4
+ tags :basic
5
+
6
+ def run
7
+ assert(feature(:foreman_tasks).paused_tasks_count == 0,
8
+ 'There are currently paused tasks in the system')
9
+ end
10
+
11
+ def next_steps
12
+ [procedure(Procedures::ForemanTasksResume)] if fail?
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ class Checks::ForemanTasksNotRunning < ForemanMaintain::Check
2
+ for_feature :foreman_tasks
3
+ description 'check for running tasks'
4
+ tags :pre_upgrade
5
+
6
+ def run
7
+ assert(feature(:foreman_tasks).running_tasks_count == 0,
8
+ 'There are actively running tasks in the system')
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ class Features::Downstream < ForemanMaintain::Feature
2
+ label :downstream
3
+
4
+ confine do
5
+ downstream_installation?
6
+ end
7
+
8
+ def current_version
9
+ @current_version ||= rpm_version('satellite') || version_from_source
10
+ end
11
+
12
+ private
13
+
14
+ def version_from_source
15
+ version(File.read('/usr/share/foreman/lib/satellite/version.rb')[/6\.\d\.\d/])
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ require 'features/foreman_1_7_x'
2
+
3
+ class Features::Foreman_1_11_x < Features::Foreman_1_7_x
4
+ confine do
5
+ check_min_version('foreman', '1.11')
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class Features::Foreman_1_7_x < ForemanMaintain::Feature
2
+ label :foreman
3
+
4
+ confine do
5
+ check_min_version('foreman', '1.7')
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ class Features::ForemanDatabase < ForemanMaintain::Feature
2
+ label :foreman_database
3
+
4
+ confine do
5
+ File.exist?('/etc/foreman/database.yml')
6
+ end
7
+
8
+ def query(sql)
9
+ parse_csv(psql(%{COPY (#{sql}) TO STDOUT WITH CSV HEADER)}))
10
+ end
11
+
12
+ def psql(query)
13
+ execute("su - postgres -c 'psql -d foreman'", query)
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ class Features::ForemanTasks < ForemanMaintain::Feature
2
+ label :foreman_tasks
3
+
4
+ confine do
5
+ check_min_version('ruby193-rubygem-foreman-tasks', '0.6') ||
6
+ check_min_version('tfm-rubygem-foreman-tasks', '0.7')
7
+ end
8
+
9
+ def running_tasks_count
10
+ # feature(:foreman_database).query(<<-SQL).first['count'].to_i
11
+ # SELECT count(*) AS count FROM foreman_tasks_tasks WHERE state in ('running', 'paused')
12
+ # SQL
13
+ 0
14
+ end
15
+
16
+ def paused_tasks_count
17
+ # feature(:foreman_database).query(<<-SQL).first['count'].to_i
18
+ # SELECT count(*) AS count FROM foreman_tasks_tasks WHERE state in ('running', 'paused')
19
+ # SQL
20
+ 5
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ class Features::Upstream < ForemanMaintain::Feature
2
+ label :upstream
3
+
4
+ confine do
5
+ !downstream_installation?
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ class Procedures::ForemanTasksResume < ForemanMaintain::Procedure
2
+ for_feature :foreman_tasks
3
+ description 'resume paused tasks'
4
+
5
+ def run
6
+ say 'resuming paused tasks'
7
+ sleep 2
8
+ say 'hold on'
9
+ sleep 2
10
+ say 'almost there'
11
+ sleep 2
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ class Scenarios::PreUpgradeCheckForeman_1_14 < ForemanMaintain::Scenario
2
+ description 'checks before upgrading to Foreman 1.14'
3
+ confine do
4
+ feature(:upstream)
5
+ end
6
+
7
+ tags :pre_upgrade_check
8
+
9
+ def compose
10
+ steps.concat(find_checks(:basic))
11
+ end
12
+ end
@@ -0,0 +1,12 @@
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.')
6
+ end
7
+
8
+ def compose
9
+ steps.concat(find_checks(:basic))
10
+ steps.concat(find_checks(:pre_upgrade))
11
+ end
12
+ end
@@ -0,0 +1,12 @@
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.')
6
+ end
7
+
8
+ def compose
9
+ steps.concat(find_checks(:basic))
10
+ steps.concat(find_checks(:pre_upgrade))
11
+ end
12
+ end
@@ -0,0 +1,12 @@
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.')
6
+ end
7
+
8
+ def compose
9
+ steps.concat(find_checks(:basic))
10
+ steps.concat(find_checks(:pre_upgrade))
11
+ end
12
+ end
@@ -0,0 +1,12 @@
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.')
6
+ end
7
+
8
+ def compose
9
+ steps.concat(find_checks(:basic))
10
+ steps.concat(find_checks(:pre_upgrade))
11
+ end
12
+ end
@@ -0,0 +1,12 @@
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.')
6
+ end
7
+
8
+ def compose
9
+ steps.concat(find_checks(:basic))
10
+ steps.concat(find_checks(:pre_upgrade))
11
+ end
12
+ end
@@ -0,0 +1,12 @@
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.')
6
+ end
7
+
8
+ def compose
9
+ steps.concat(find_checks(:basic))
10
+ steps.concat(find_checks(:pre_upgrade))
11
+ end
12
+ end
@@ -0,0 +1,54 @@
1
+ module ForemanMaintain
2
+ require 'foreman_maintain/concerns/logger'
3
+ require 'foreman_maintain/concerns/metadata'
4
+ require 'foreman_maintain/concerns/system_helpers'
5
+ require 'foreman_maintain/concerns/finders'
6
+ require 'foreman_maintain/top_level_modules'
7
+ require 'foreman_maintain/config'
8
+ require 'foreman_maintain/detector'
9
+ require 'foreman_maintain/feature'
10
+ require 'foreman_maintain/executable'
11
+ require 'foreman_maintain/check'
12
+ require 'foreman_maintain/procedure'
13
+ require 'foreman_maintain/scenario'
14
+ require 'foreman_maintain/runner'
15
+ require 'foreman_maintain/reporter'
16
+
17
+ class << self
18
+ attr_accessor :config
19
+
20
+ def setup(configuration = {})
21
+ self.config = Config.new(configuration)
22
+ load_definitions
23
+ end
24
+
25
+ def load_definitions
26
+ # we need to add the load paths first, in case there is crossreferencing
27
+ # between the definitions directories
28
+ $LOAD_PATH.concat(config.definitions_dirs)
29
+ config.definitions_dirs.each do |definitions_dir|
30
+ Dir.glob(File.join(definitions_dir, '**', '*.rb')).each { |f| require f }
31
+ end
32
+ end
33
+
34
+ def detector
35
+ @detector ||= Detector.new
36
+ end
37
+
38
+ def available_features(*args)
39
+ detector.available_features(*args)
40
+ end
41
+
42
+ def available_scenarios(*args)
43
+ detector.available_scenarios(*args)
44
+ end
45
+
46
+ def available_checks(*args)
47
+ detector.available_checks(*args)
48
+ end
49
+
50
+ def available_procedures(*args)
51
+ detector.available_procedures(*args)
52
+ end
53
+ end
54
+ end