foreman_maintain 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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