hybrid_platforms_conductor 33.2.0 → 33.3.0
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 +4 -4
- data/CHANGELOG.md +61 -0
- data/docs/plugins.md +1 -0
- data/docs/plugins/secrets_reader/keepass.md +62 -0
- data/lib/hybrid_platforms_conductor/cmd_runner.rb +1 -1
- data/lib/hybrid_platforms_conductor/config.rb +0 -35
- data/lib/hybrid_platforms_conductor/core_extensions/bundler/without_bundled_env.rb +54 -0
- data/lib/hybrid_platforms_conductor/deployer.rb +35 -28
- data/lib/hybrid_platforms_conductor/executable.rb +2 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +4 -3
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/reserve_proxmox_container +1 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/keepass.rb +173 -0
- data/lib/hybrid_platforms_conductor/plugins.rb +1 -0
- data/lib/hybrid_platforms_conductor/safe_merge.rb +37 -0
- data/lib/hybrid_platforms_conductor/topographer/plugins/graphviz.rb +5 -3
- data/lib/hybrid_platforms_conductor/version.rb +1 -1
- data/spec/hybrid_platforms_conductor_test.rb +4 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +8 -6
- data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +15 -1
- data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +48 -72
- data/spec/hybrid_platforms_conductor_test/api/deployer/config_dsl_spec.rb +36 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/keepass_spec.rb +719 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs_plugins_api_spec.rb +2 -2
- data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +1 -1
- data/spec/hybrid_platforms_conductor_test/executables/nodes_to_deploy_spec.rb +21 -15
- metadata +159 -139
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c0707386d30d5e671d5ceac5e1fec9d971cdccd2b7057c6ed3a75cb5d8bb6d85
|
|
4
|
+
data.tar.gz: e9e5c023662e7aa9283905f4ae54f4b8995d14f61bea1d844ef4fef32cba68dc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b70dedf6605d48081ead37771b8f94e29d26334fb1c0f7f15ed8ed70cabc24809b145bd681af9e829cd0c968accd18cf87886987391709a7252908a00104096
|
|
7
|
+
data.tar.gz: cbe9033432aae4b83b4153501efad2330651696ba0fc8b487d21f5927430d481661e5d3bf2c3617ffb20c2ba7e254f82328d4225aeba4f98295873c55fe4cca9
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,64 @@
|
|
|
1
|
+
# [v33.3.0](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.2.4...v33.3.0) (2021-07-02 17:20:58)
|
|
2
|
+
|
|
3
|
+
## Global changes
|
|
4
|
+
### Patches
|
|
5
|
+
|
|
6
|
+
* [[Feature(secrets_reader_keepass)] [#79] Add Keepass secrets reader plugin to get secrets from KeePass databases](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/7ace48653a74f03ed535ee6b41b21242a6454ff3)
|
|
7
|
+
|
|
8
|
+
## Changes for secrets_reader_keepass
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* [[Feature(secrets_reader_keepass)] [#79] Add Keepass secrets reader plugin to get secrets from KeePass databases](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/7ace48653a74f03ed535ee6b41b21242a6454ff3)
|
|
12
|
+
|
|
13
|
+
# [v33.2.4](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.2.3...v33.2.4) (2021-06-23 15:14:20)
|
|
14
|
+
|
|
15
|
+
## Global changes
|
|
16
|
+
### Patches
|
|
17
|
+
|
|
18
|
+
* [[Hotfix(platform_handler_serverless_chef)] Forward environment in sudo commands](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/fd3b58875665c29dd071b5af2055eab0c45c0974)
|
|
19
|
+
* [[Hotfix] Fixed unbundled environment not cleaned + Moved deployer config DSL in deployer.rb](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/2bce6dbc31d98b27f196ed646eb9aa669b0f9a86)
|
|
20
|
+
|
|
21
|
+
## Changes for platform_handler_serverless_chef
|
|
22
|
+
### Patches
|
|
23
|
+
|
|
24
|
+
* [[Hotfix(platform_handler_serverless_chef)] Forward environment in sudo commands](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/fd3b58875665c29dd071b5af2055eab0c45c0974)
|
|
25
|
+
|
|
26
|
+
# [v33.2.3](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.2.2...v33.2.3) (2021-06-23 13:45:56)
|
|
27
|
+
|
|
28
|
+
## Global changes
|
|
29
|
+
### Patches
|
|
30
|
+
|
|
31
|
+
* [[Hotfix(provisioner_proxmox)] Add missing require in synchronization script](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/7a6c71595789c9180e1d95323fc5cf5051f2e2cd)
|
|
32
|
+
|
|
33
|
+
## Changes for provisioner_proxmox
|
|
34
|
+
### Patches
|
|
35
|
+
|
|
36
|
+
* [[Hotfix(provisioner_proxmox)] Add missing require in synchronization script](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/7a6c71595789c9180e1d95323fc5cf5051f2e2cd)
|
|
37
|
+
|
|
38
|
+
# [v33.2.2](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.2.1...v33.2.2) (2021-06-21 12:41:35)
|
|
39
|
+
|
|
40
|
+
## Global changes
|
|
41
|
+
### Patches
|
|
42
|
+
|
|
43
|
+
* [[Hotfix(cmd_runner)] Retain dynamically set environment while executing commands](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/d709d5d2871e43196cc1f5f9eaf5b2155b34ed4e)
|
|
44
|
+
|
|
45
|
+
## Changes for cmd_runner
|
|
46
|
+
### Patches
|
|
47
|
+
|
|
48
|
+
* [[Hotfix(cmd_runner)] Retain dynamically set environment while executing commands](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/d709d5d2871e43196cc1f5f9eaf5b2155b34ed4e)
|
|
49
|
+
|
|
50
|
+
# [v33.2.1](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.2.0...v33.2.1) (2021-06-21 10:23:51)
|
|
51
|
+
|
|
52
|
+
## Global changes
|
|
53
|
+
### Patches
|
|
54
|
+
|
|
55
|
+
* [[Hotfix(platform_handler_serverless_chef)] Corrected dry-run mode not working](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/4800a0f4255c1999eed33651c1e66c445acd17bb)
|
|
56
|
+
|
|
57
|
+
## Changes for platform_handler_serverless_chef
|
|
58
|
+
### Patches
|
|
59
|
+
|
|
60
|
+
* [[Hotfix(platform_handler_serverless_chef)] Corrected dry-run mode not working](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/4800a0f4255c1999eed33651c1e66c445acd17bb)
|
|
61
|
+
|
|
1
62
|
# [v33.2.0](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.1.1...v33.2.0) (2021-06-18 23:22:21)
|
|
2
63
|
|
|
3
64
|
## Global changes
|
data/docs/plugins.md
CHANGED
|
@@ -196,6 +196,7 @@ Check the [sample plugin file](../lib/hybrid_platforms_conductor/hpc_plugins/sec
|
|
|
196
196
|
|
|
197
197
|
Plugins shipped by default:
|
|
198
198
|
* [`cli`](plugins/secrets_reader/cli.md)
|
|
199
|
+
* [`keepass`](plugins/secrets_reader/keepass.md)
|
|
199
200
|
* [`thycotic`](plugins/secrets_reader/thycotic.md)
|
|
200
201
|
|
|
201
202
|
<a name="test"></a>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Secrets reader plugin: `keepass`
|
|
2
|
+
|
|
3
|
+
The `keepass` secrets reader plugin retrieves secrets from [KeePass](https://keepass.info/) databases, using an actual KeePass installation with the [KPScript plugin](https://keepass.info/plugins.html#kpscript).
|
|
4
|
+
|
|
5
|
+
It is configured by giving the KPScript command-line (using `use_kpscript_from` config DSL method), the KeePass databases to be read (using `secrets_from_keepass` config DSL method) and uses the `keepass` credential ID to authenticate along with extra environment variables for eventual key files or encrypted passwords.
|
|
6
|
+
|
|
7
|
+
## Config DSL extension
|
|
8
|
+
|
|
9
|
+
### `use_kpscript_from`
|
|
10
|
+
|
|
11
|
+
Provide the KPScript command-line to be used. If KPScript is already in your path, using `KPScript.exe` or `kpscript` should be enough, otherwise the full path to the command-line will be needed. On Windows it is needed to also include double quotes if the path contains spaces (like `"C:\Program Files\KeePass\KPScript.exe"`).
|
|
12
|
+
|
|
13
|
+
It takes a simple `String` as parameter to get the command line.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
```ruby
|
|
17
|
+
use_kpscript_from '/path/to/kpscript'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### `secrets_from_keepass`
|
|
21
|
+
|
|
22
|
+
Define a KeePass database to read secrets from.
|
|
23
|
+
A base group path of the KeePass database can also be specified to only read secrets from this group path.
|
|
24
|
+
|
|
25
|
+
All entries, attachments and sub-groups from the base group will be read as secrets.
|
|
26
|
+
|
|
27
|
+
Can be applied to subset of nodes using the [`for_nodes` DSL method](/docs/config_dsl.md#for_nodes).
|
|
28
|
+
|
|
29
|
+
It takes the following parameters:
|
|
30
|
+
* **database** (`String`): Database file path.
|
|
31
|
+
* **group_path** (`Array<String>`): Group path to extract from [default: `[]`].
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
```ruby
|
|
35
|
+
secrets_from_keepass(
|
|
36
|
+
database: '/path/to/database.kdbx',
|
|
37
|
+
group_path: %w[Secrets Automation]
|
|
38
|
+
)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Used credentials
|
|
42
|
+
|
|
43
|
+
| Credential | Usage
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `keepass` | Used to get the password to the database. No need to be set if the database opens without password. |
|
|
46
|
+
|
|
47
|
+
## Used Metadata
|
|
48
|
+
|
|
49
|
+
| Metadata | Type | Usage
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
|
|
52
|
+
## Used environment variables
|
|
53
|
+
|
|
54
|
+
| Variable | Usage
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `hpc_key_file_for_keepass` | Optional path to the key file needed to open the database |
|
|
57
|
+
| `hpc_password_enc_for_keepass` | Optional encrypted password needed to open the database |
|
|
58
|
+
|
|
59
|
+
## External tools dependencies
|
|
60
|
+
|
|
61
|
+
* [KeePass](https://keepass.info/) to open databases.
|
|
62
|
+
* [KPScript KeePass plugin](https://keepass.info/plugins.html#kpscript) to query KeePass API.
|
|
@@ -130,7 +130,7 @@ module HybridPlatformsConductor
|
|
|
130
130
|
(log_to_stdout ? [@logger_stderr] : []) +
|
|
131
131
|
(file_output.nil? ? [] : [file_output])
|
|
132
132
|
) do
|
|
133
|
-
Bundler.
|
|
133
|
+
Bundler.without_bundled_env do
|
|
134
134
|
cmd_result = TTY::Command.new(
|
|
135
135
|
printer: :null,
|
|
136
136
|
pty: true,
|
|
@@ -47,12 +47,6 @@ module HybridPlatformsConductor
|
|
|
47
47
|
# Array<Hash,Symbol,Object>
|
|
48
48
|
attr_reader :expected_failures
|
|
49
49
|
|
|
50
|
-
# List of retriable errors. Each info has the following properties:
|
|
51
|
-
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by those errors
|
|
52
|
-
# * *errors_on_stdout* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stdout
|
|
53
|
-
# * *errors_on_stderr* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stderr
|
|
54
|
-
attr_reader :retriable_errors
|
|
55
|
-
|
|
56
50
|
# List of deployment schedules. Each info has the following properties:
|
|
57
51
|
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
|
|
58
52
|
# * *schedule* (IceCube::Schedule): The deployment schedule
|
|
@@ -81,11 +75,6 @@ module HybridPlatformsConductor
|
|
|
81
75
|
# * *reason* (String): Reason for this expected failure
|
|
82
76
|
# Array<Hash,Symbol,Object>
|
|
83
77
|
@expected_failures = []
|
|
84
|
-
# List of retriable errors. Each info has the following properties:
|
|
85
|
-
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by those errors
|
|
86
|
-
# * *errors_on_stdout* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stdout
|
|
87
|
-
# * *errors_on_stderr* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stderr
|
|
88
|
-
@retriable_errors = []
|
|
89
78
|
# List of deployment schedules. Each info has the following properties:
|
|
90
79
|
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
|
|
91
80
|
# * *schedule* (IceCube::Schedule): The deployment schedule
|
|
@@ -162,30 +151,6 @@ module HybridPlatformsConductor
|
|
|
162
151
|
end
|
|
163
152
|
expose :expect_tests_to_fail
|
|
164
153
|
|
|
165
|
-
# Mark some errors on stdout to be retriable during a deploy
|
|
166
|
-
#
|
|
167
|
-
# Parameters::
|
|
168
|
-
# * *errors* (String, Regexp or Array<String or Regexp>): Single (or list of) errors matching pattern (either as exact string match or using a regexp).
|
|
169
|
-
def retry_deploy_for_errors_on_stdout(errors)
|
|
170
|
-
@retriable_errors << {
|
|
171
|
-
errors_on_stdout: errors.is_a?(Array) ? errors : [errors],
|
|
172
|
-
nodes_selectors_stack: current_nodes_selectors_stack
|
|
173
|
-
}
|
|
174
|
-
end
|
|
175
|
-
expose :retry_deploy_for_errors_on_stdout
|
|
176
|
-
|
|
177
|
-
# Mark some errors on stderr to be retriable during a deploy
|
|
178
|
-
#
|
|
179
|
-
# Parameters::
|
|
180
|
-
# * *errors* (String, Regexp or Array<String or Regexp>): Single (or list of) errors matching pattern (either as exact string match or using a regexp).
|
|
181
|
-
def retry_deploy_for_errors_on_stderr(errors)
|
|
182
|
-
@retriable_errors << {
|
|
183
|
-
errors_on_stderr: errors.is_a?(Array) ? errors : [errors],
|
|
184
|
-
nodes_selectors_stack: current_nodes_selectors_stack
|
|
185
|
-
}
|
|
186
|
-
end
|
|
187
|
-
expose :retry_deploy_for_errors_on_stderr
|
|
188
|
-
|
|
189
154
|
# Set a deployment schedule
|
|
190
155
|
#
|
|
191
156
|
# Parameters::
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Add a way to clean the current env from Bundler variables
|
|
2
|
+
module Bundler
|
|
3
|
+
|
|
4
|
+
class << self
|
|
5
|
+
|
|
6
|
+
# Run block with all bundler-related variables removed from the current environment
|
|
7
|
+
def without_bundled_env(&block)
|
|
8
|
+
with_env(current_unbundled_env, &block)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @return [Hash] Environment with all bundler-related variables removed
|
|
12
|
+
def current_unbundled_env
|
|
13
|
+
env = ENV.clone.to_hash
|
|
14
|
+
%w[
|
|
15
|
+
PATH
|
|
16
|
+
RUBYLIB
|
|
17
|
+
RUBYOPT
|
|
18
|
+
].each do |env_name|
|
|
19
|
+
if original_env.key?(env_name)
|
|
20
|
+
env[env_name] = original_env[env_name]
|
|
21
|
+
else
|
|
22
|
+
env.delete(env_name)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
env['MANPATH'] = env['BUNDLER_ORIG_MANPATH'] if env.key?('BUNDLER_ORIG_MANPATH')
|
|
27
|
+
|
|
28
|
+
env.delete_if do |k, _|
|
|
29
|
+
%w[
|
|
30
|
+
GEM_
|
|
31
|
+
BUNDLE_
|
|
32
|
+
BUNDLER_
|
|
33
|
+
].any? { |prefix| k.start_with?(prefix) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if env.key?('RUBYOPT')
|
|
37
|
+
rubyopt = env['RUBYOPT'].split
|
|
38
|
+
rubyopt.delete("-r#{File.expand_path('bundler/setup', __dir__)}")
|
|
39
|
+
rubyopt.delete('-rbundler/setup')
|
|
40
|
+
env['RUBYOPT'] = rubyopt.join(' ')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if env.key?('RUBYLIB')
|
|
44
|
+
rubylib = env['RUBYLIB'].split(File::PATH_SEPARATOR)
|
|
45
|
+
rubylib.delete(File.expand_path(__dir__))
|
|
46
|
+
env['RUBYLIB'] = rubylib.join(File::PATH_SEPARATOR)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
env
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
@@ -9,6 +9,7 @@ require 'hybrid_platforms_conductor/logger_helpers'
|
|
|
9
9
|
require 'hybrid_platforms_conductor/nodes_handler'
|
|
10
10
|
require 'hybrid_platforms_conductor/services_handler'
|
|
11
11
|
require 'hybrid_platforms_conductor/plugins'
|
|
12
|
+
require 'hybrid_platforms_conductor/safe_merge'
|
|
12
13
|
|
|
13
14
|
module HybridPlatformsConductor
|
|
14
15
|
|
|
@@ -18,6 +19,12 @@ module HybridPlatformsConductor
|
|
|
18
19
|
# Extend the Config DSL
|
|
19
20
|
module ConfigDSLExtension
|
|
20
21
|
|
|
22
|
+
# List of retriable errors. Each info has the following properties:
|
|
23
|
+
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by those errors
|
|
24
|
+
# * *errors_on_stdout* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stdout
|
|
25
|
+
# * *errors_on_stderr* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stderr
|
|
26
|
+
attr_reader :retriable_errors
|
|
27
|
+
|
|
21
28
|
# List of log plugins. Each info has the following properties:
|
|
22
29
|
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule.
|
|
23
30
|
# * *log_plugins* (Array<Symbol>): List of log plugins to be used to store deployment logs.
|
|
@@ -36,6 +43,11 @@ module HybridPlatformsConductor
|
|
|
36
43
|
# Mixin initializer
|
|
37
44
|
def init_deployer_config
|
|
38
45
|
@packaging_timeout_secs = 60
|
|
46
|
+
# List of retriable errors. Each info has the following properties:
|
|
47
|
+
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by those errors
|
|
48
|
+
# * *errors_on_stdout* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stdout
|
|
49
|
+
# * *errors_on_stderr* (Array<String or Regexp>): List of errors match (as exact string match or using a regexp) to check against stderr
|
|
50
|
+
@retriable_errors = []
|
|
39
51
|
@deployment_logs = []
|
|
40
52
|
@secrets_readers = []
|
|
41
53
|
end
|
|
@@ -48,6 +60,28 @@ module HybridPlatformsConductor
|
|
|
48
60
|
@packaging_timeout_secs = packaging_timeout_secs
|
|
49
61
|
end
|
|
50
62
|
|
|
63
|
+
# Mark some errors on stdout to be retriable during a deploy
|
|
64
|
+
#
|
|
65
|
+
# Parameters::
|
|
66
|
+
# * *errors* (String, Regexp or Array<String or Regexp>): Single (or list of) errors matching pattern (either as exact string match or using a regexp).
|
|
67
|
+
def retry_deploy_for_errors_on_stdout(errors)
|
|
68
|
+
@retriable_errors << {
|
|
69
|
+
errors_on_stdout: errors.is_a?(Array) ? errors : [errors],
|
|
70
|
+
nodes_selectors_stack: current_nodes_selectors_stack
|
|
71
|
+
}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Mark some errors on stderr to be retriable during a deploy
|
|
75
|
+
#
|
|
76
|
+
# Parameters::
|
|
77
|
+
# * *errors* (String, Regexp or Array<String or Regexp>): Single (or list of) errors matching pattern (either as exact string match or using a regexp).
|
|
78
|
+
def retry_deploy_for_errors_on_stderr(errors)
|
|
79
|
+
@retriable_errors << {
|
|
80
|
+
errors_on_stderr: errors.is_a?(Array) ? errors : [errors],
|
|
81
|
+
nodes_selectors_stack: current_nodes_selectors_stack
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
51
85
|
# Set the deployment log plugins to be used
|
|
52
86
|
#
|
|
53
87
|
# Parameters::
|
|
@@ -467,34 +501,7 @@ module HybridPlatformsConductor
|
|
|
467
501
|
|
|
468
502
|
private
|
|
469
503
|
|
|
470
|
-
|
|
471
|
-
# Safe-merging is done by:
|
|
472
|
-
# * Merging values that are hashes.
|
|
473
|
-
# * Reporting errors when values conflict.
|
|
474
|
-
# When values are conflicting, the initial hash won't modify those conflicting values and will stop the merge.
|
|
475
|
-
#
|
|
476
|
-
# Parameters::
|
|
477
|
-
# * *hash* (Hash): Hash to be modified merging hash_to_merge
|
|
478
|
-
# * *hash_to_merge* (Hash): Hash to be merged into hash
|
|
479
|
-
# Result::
|
|
480
|
-
# * nil or Array<Object>: nil in case of success, or the keys path leading to a conflicting value in case of error
|
|
481
|
-
def safe_merge(hash, hash_to_merge)
|
|
482
|
-
conflicting_path = nil
|
|
483
|
-
hash_to_merge.each do |key, value_to_merge|
|
|
484
|
-
if hash.key?(key)
|
|
485
|
-
if hash[key].is_a?(Hash) && value_to_merge.is_a?(Hash)
|
|
486
|
-
sub_conflicting_path = safe_merge(hash[key], value_to_merge)
|
|
487
|
-
conflicting_path = [key] + sub_conflicting_path unless sub_conflicting_path.nil?
|
|
488
|
-
elsif hash[key] != value_to_merge
|
|
489
|
-
conflicting_path = [key]
|
|
490
|
-
end
|
|
491
|
-
else
|
|
492
|
-
hash[key] = value_to_merge
|
|
493
|
-
end
|
|
494
|
-
break unless conflicting_path.nil?
|
|
495
|
-
end
|
|
496
|
-
conflicting_path
|
|
497
|
-
end
|
|
504
|
+
include SafeMerge
|
|
498
505
|
|
|
499
506
|
# Get the list of retriable errors a node got from deployment logs.
|
|
500
507
|
# Useful to know if an error is non-deterministic (due to external and temporary factors).
|
|
@@ -227,13 +227,14 @@ module HybridPlatformsConductor
|
|
|
227
227
|
# * Array< Hash<Symbol,Object> >: List of actions to be done
|
|
228
228
|
def actions_to_deploy_on(node, service, use_why_run: true)
|
|
229
229
|
package_dir = "#{@repository_path}/dist/#{@local_env ? 'local' : 'prod'}/#{service}"
|
|
230
|
+
gems_to_install = []
|
|
230
231
|
# Generate the nodes attributes file
|
|
231
232
|
unless @cmd_runner.dry_run
|
|
232
233
|
FileUtils.mkdir_p "#{package_dir}/nodes"
|
|
233
234
|
File.write("#{package_dir}/nodes/#{node}.json", (known_nodes.include?(node) ? metadata_for(node) : {}).merge(@nodes_handler.metadata_of(node)).to_json)
|
|
235
|
+
# Get the gems to be installed
|
|
236
|
+
gems_to_install = JSON.parse(File.read("#{package_dir}/gems.json"))
|
|
234
237
|
end
|
|
235
|
-
# Get the gems to be installed
|
|
236
|
-
gems_to_install = JSON.parse(File.read("#{package_dir}/gems.json"))
|
|
237
238
|
client_options = [
|
|
238
239
|
'--local-mode',
|
|
239
240
|
'--chef-license', 'accept',
|
|
@@ -262,7 +263,7 @@ module HybridPlatformsConductor
|
|
|
262
263
|
raise "Missing file #{chef_versions_file} specifying the Chef Infra Client version to be deployed" unless File.exist?(chef_versions_file)
|
|
263
264
|
|
|
264
265
|
required_chef_client_version = YAML.load_file(chef_versions_file)['client']
|
|
265
|
-
sudo = (@actions_executor.connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} ")
|
|
266
|
+
sudo = (@actions_executor.connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} -E ")
|
|
266
267
|
[
|
|
267
268
|
{
|
|
268
269
|
# Install dependencies
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
require 'nokogiri'
|
|
3
|
+
require 'tempfile'
|
|
4
|
+
require 'keepass_kpscript'
|
|
5
|
+
require 'zlib'
|
|
6
|
+
require 'hybrid_platforms_conductor/credentials'
|
|
7
|
+
require 'hybrid_platforms_conductor/safe_merge'
|
|
8
|
+
require 'hybrid_platforms_conductor/secrets_reader'
|
|
9
|
+
|
|
10
|
+
module HybridPlatformsConductor
|
|
11
|
+
|
|
12
|
+
module HpcPlugins
|
|
13
|
+
|
|
14
|
+
module SecretsReader
|
|
15
|
+
|
|
16
|
+
# Get secrets from a KeePass database
|
|
17
|
+
class Keepass < HybridPlatformsConductor::SecretsReader
|
|
18
|
+
|
|
19
|
+
include SafeMerge
|
|
20
|
+
|
|
21
|
+
# Extend the Config DSL
|
|
22
|
+
module ConfigDSLExtension
|
|
23
|
+
|
|
24
|
+
# List of defined KeePass secrets. Each info has the following properties:
|
|
25
|
+
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule.
|
|
26
|
+
# * *database* (String): Database file path.
|
|
27
|
+
# * *group_path* (Array<String>): Group path to extract from.
|
|
28
|
+
# Array< Hash<Symbol, Object> >
|
|
29
|
+
attr_reader :keepass_secrets
|
|
30
|
+
|
|
31
|
+
# String: The KPScript command line
|
|
32
|
+
attr_reader :kpscript
|
|
33
|
+
|
|
34
|
+
# Mixin initializer
|
|
35
|
+
def init_keepass_config
|
|
36
|
+
@keepass_secrets = []
|
|
37
|
+
@kpscript = nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Set the KPScript command line
|
|
41
|
+
#
|
|
42
|
+
# Parameters::
|
|
43
|
+
# * *cmd* (String): KPScript command line
|
|
44
|
+
def use_kpscript_from(cmd)
|
|
45
|
+
@kpscript = cmd
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Set a KeePass database configuration
|
|
49
|
+
#
|
|
50
|
+
# Parameters::
|
|
51
|
+
# * *database* (String): Database file path.
|
|
52
|
+
# * *group_path* (Array<String>): Group path to extract from [default: []].
|
|
53
|
+
def secrets_from_keepass(database:, group_path: [])
|
|
54
|
+
@keepass_secrets << {
|
|
55
|
+
nodes_selectors_stack: current_nodes_selectors_stack,
|
|
56
|
+
database: database,
|
|
57
|
+
group_path: group_path
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
Config.extend_config_dsl_with ConfigDSLExtension, :init_keepass_config
|
|
64
|
+
|
|
65
|
+
# Return secrets for a given service to be deployed on a node.
|
|
66
|
+
# [API] - This method is mandatory
|
|
67
|
+
# [API] - The following API components are accessible:
|
|
68
|
+
# * *@config* (Config): Main configuration API.
|
|
69
|
+
# * *@cmd_runner* (CmdRunner): Command Runner API.
|
|
70
|
+
# * *@nodes_handler* (NodesHandler): Nodes handler API.
|
|
71
|
+
#
|
|
72
|
+
# Parameters::
|
|
73
|
+
# * *node* (String): Node to be deployed
|
|
74
|
+
# * *service* (String): Service to be deployed
|
|
75
|
+
# Result::
|
|
76
|
+
# * Hash: The secrets
|
|
77
|
+
def secrets_for(node, service)
|
|
78
|
+
secrets = {}
|
|
79
|
+
# As we are dealing with global secrets, cache the reading for performance between nodes and services.
|
|
80
|
+
# Keep secrets cache grouped by URL/ID
|
|
81
|
+
@secrets = {} unless defined?(@secrets)
|
|
82
|
+
@nodes_handler.select_confs_for_node(node, @config.keepass_secrets).each do |keepass_secrets_info|
|
|
83
|
+
secret_id = "#{keepass_secrets_info[:database]}:#{keepass_secrets_info[:group_path].join('/')}"
|
|
84
|
+
unless @secrets.key?(secret_id)
|
|
85
|
+
raise 'Missing KPScript configuration. Please use use_kpscript_from to set it.' if @config.kpscript.nil?
|
|
86
|
+
|
|
87
|
+
Credentials.with_credentials_for(:keepass, @logger, @logger_stderr) do |_user, password|
|
|
88
|
+
Tempfile.create('hpc_keepass') do |xml_file|
|
|
89
|
+
key_file = ENV['hpc_key_file_for_keepass']
|
|
90
|
+
password_enc = ENV['hpc_password_enc_for_keepass']
|
|
91
|
+
keepass_credentials = {}
|
|
92
|
+
keepass_credentials[:password] = password if password
|
|
93
|
+
keepass_credentials[:password_enc] = password_enc if password_enc
|
|
94
|
+
keepass_credentials[:key_file] = key_file if key_file
|
|
95
|
+
KeepassKpscript.
|
|
96
|
+
use(@config.kpscript, debug: log_debug?).
|
|
97
|
+
open(keepass_secrets_info[:database], **keepass_credentials).
|
|
98
|
+
export('KeePass XML (2.x)', xml_file.path, group_path: keepass_secrets_info[:group_path].empty? ? nil : keepass_secrets_info[:group_path])
|
|
99
|
+
@secrets[secret_id] = parse_xml_secrets(Nokogiri::XML(xml_file).at_xpath('KeePassFile/Root/Group'))
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
conflicting_path = safe_merge(secrets, @secrets[secret_id])
|
|
104
|
+
raise "Secret set at path #{conflicting_path.join('->')} by #{keepass_secrets_info[:database]}#{keepass_secrets_info[:group_path].empty? ? '' : " from group #{keepass_secrets_info[:group_path].join('/')}"} for service #{service} on node #{node} has conflicting values (#{log_debug? ? "#{@secrets[secret_id].dig(*conflicting_path)} != #{secrets.dig(*conflicting_path)}" : 'set debug for value details'})." unless conflicting_path.nil?
|
|
105
|
+
end
|
|
106
|
+
secrets
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
# List of fields to include in the secrets and their corresponding XML name
|
|
112
|
+
FIELDS = {
|
|
113
|
+
notes: 'Notes',
|
|
114
|
+
password: 'Password',
|
|
115
|
+
url: 'URL',
|
|
116
|
+
user_name: 'UserName'
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# Parse XML secrets from a Nokogiri XML group node
|
|
120
|
+
#
|
|
121
|
+
# Parameters::
|
|
122
|
+
# * *group* (Nokogiri::XML::Element): The group to parse
|
|
123
|
+
# Result::
|
|
124
|
+
# * Hash: The JSON secrets parsed from this group
|
|
125
|
+
def parse_xml_secrets(group)
|
|
126
|
+
# Parse all entries
|
|
127
|
+
group.xpath('Entry').map do |entry|
|
|
128
|
+
[
|
|
129
|
+
entry.at_xpath('String/Key[contains(.,"Title")]/../Value').text,
|
|
130
|
+
FIELDS.map do |property, field|
|
|
131
|
+
value = entry.at_xpath("String/Key[contains(.,\"#{field}\")]/../Value")&.text
|
|
132
|
+
if value.nil? || value.empty?
|
|
133
|
+
nil
|
|
134
|
+
else
|
|
135
|
+
[
|
|
136
|
+
property.to_s,
|
|
137
|
+
value
|
|
138
|
+
]
|
|
139
|
+
end
|
|
140
|
+
end.compact.to_h.merge(
|
|
141
|
+
entry.xpath('Binary').map do |binary|
|
|
142
|
+
binary_meta = group.document.at_xpath("KeePassFile/Meta/Binaries/Binary[@ID=#{Integer(binary.xpath('Value').attr('Ref').value)}]")
|
|
143
|
+
binary_content = Base64.decode64(binary_meta.text)
|
|
144
|
+
if binary_meta.attr('Compressed') == 'True'
|
|
145
|
+
gz = Zlib::GzipReader.new(StringIO.new(binary_content))
|
|
146
|
+
binary_content = gz.read
|
|
147
|
+
gz.close
|
|
148
|
+
end
|
|
149
|
+
[
|
|
150
|
+
binary.xpath('Key').text,
|
|
151
|
+
binary_content
|
|
152
|
+
]
|
|
153
|
+
end.to_h
|
|
154
|
+
)
|
|
155
|
+
]
|
|
156
|
+
end.to_h.merge(
|
|
157
|
+
# Add children groups
|
|
158
|
+
group.xpath('Group').map do |sub_group|
|
|
159
|
+
[
|
|
160
|
+
sub_group.at_xpath('Name').text,
|
|
161
|
+
parse_xml_secrets(sub_group)
|
|
162
|
+
]
|
|
163
|
+
end.to_h
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
end
|