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.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +193 -0
- data/bin/foreman-maintain +9 -0
- data/definitions/checks/foreman_tasks_not_paused.rb +14 -0
- data/definitions/checks/foreman_tasks_not_running.rb +10 -0
- data/definitions/features/downstream.rb +17 -0
- data/definitions/features/foreman_1_11_x.rb +7 -0
- data/definitions/features/foreman_1_7_x.rb +7 -0
- data/definitions/features/foreman_database.rb +15 -0
- data/definitions/features/foreman_tasks.rb +22 -0
- data/definitions/features/upstream.rb +7 -0
- data/definitions/procedures/foreman_tasks_resume.rb +13 -0
- data/definitions/scenarios/pre_upgrade_check_foreman_1_14.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_0_z.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_1.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_1_z.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_2.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_2_z.rb +12 -0
- data/definitions/scenarios/pre_upgrade_check_satellite_6_3.rb +12 -0
- data/lib/foreman_maintain.rb +54 -0
- data/lib/foreman_maintain/check.rb +34 -0
- data/lib/foreman_maintain/cli.rb +14 -0
- data/lib/foreman_maintain/cli/base.rb +47 -0
- data/lib/foreman_maintain/cli/health_command.rb +39 -0
- data/lib/foreman_maintain/cli/upgrade_command.rb +57 -0
- data/lib/foreman_maintain/concerns/finders.rb +69 -0
- data/lib/foreman_maintain/concerns/logger.rb +13 -0
- data/lib/foreman_maintain/concerns/metadata.rb +124 -0
- data/lib/foreman_maintain/concerns/system_helpers.rb +93 -0
- data/lib/foreman_maintain/config.rb +17 -0
- data/lib/foreman_maintain/detector.rb +146 -0
- data/lib/foreman_maintain/executable.rb +44 -0
- data/lib/foreman_maintain/feature.rb +22 -0
- data/lib/foreman_maintain/logger.rb +11 -0
- data/lib/foreman_maintain/procedure.rb +8 -0
- data/lib/foreman_maintain/reporter.rb +18 -0
- data/lib/foreman_maintain/reporter/cli_reporter.rb +177 -0
- data/lib/foreman_maintain/runner.rb +39 -0
- data/lib/foreman_maintain/runner/execution.rb +74 -0
- data/lib/foreman_maintain/scenario.rb +46 -0
- data/lib/foreman_maintain/top_level_modules.rb +13 -0
- data/lib/foreman_maintain/version.rb +3 -0
- metadata +173 -0
data/README.md
ADDED
@@ -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,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,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,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
|