hybrid_platforms_conductor 32.14.0 → 32.16.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +60 -0
- data/docs/plugins/platform_handler/serverless_chef.md +6 -0
- data/lib/hybrid_platforms_conductor/cmd_runner.rb +13 -1
- data/lib/hybrid_platforms_conductor/connector.rb +4 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +108 -13
- data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef/recipes_tree_builder.rb +4 -43
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/podman.rb +1 -1
- data/lib/hybrid_platforms_conductor/version.rb +1 -1
- data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +7 -0
- data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/packaging_spec.rb +86 -6
- data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +8 -4
- data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +1 -1
- data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/chef_versions.yml +3 -0
- data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/cookbooks/hpc_test/recipes/after_run.rb +1 -0
- data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/cookbooks/hpc_test/recipes/before_run.rb +1 -0
- data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/nodes/node.json +10 -0
- data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/policyfiles/test_policy.rb +3 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22f6bf2ee85b7db6649e517503f00dd71f0604d4afb3a12bee347820af4d90e2
|
4
|
+
data.tar.gz: 7804ffa80bb56697c98970b1d781a5e3be12b27bc8c38a3df9eb26b666791ed8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a89d17b28222ca1e32a132d42167a9b7b78e59b8ddb281f2805200e2a6a356e6547ee316fdacedc2c00d53ee14956027ea17b0a0424bd7fd8ad3260ba071d873
|
7
|
+
data.tar.gz: b29e79b9d21b8b9019307918c05c73c8d422c5fb942e6f3dc0c9fd317c293430e7a4658ed5551503f9bbf9fdf7bd17394904e0e7aabcb664101b555309c6b912
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,63 @@
|
|
1
|
+
# [v32.16.3](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v32.16.2...v32.16.3) (2021-06-01 11:19:50)
|
2
|
+
|
3
|
+
## Global changes
|
4
|
+
### Patches
|
5
|
+
|
6
|
+
* [[Hotfix(platform_handler_serverless_chef)] Fix Chef packaging missing licenses accept](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/69eafc1b23f77de65ba0dd68e5adb7fafb58157e)
|
7
|
+
|
8
|
+
## Changes for platform_handler_serverless_chef
|
9
|
+
### Patches
|
10
|
+
|
11
|
+
* [[Hotfix(platform_handler_serverless_chef)] Fix Chef packaging missing licenses accept](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/69eafc1b23f77de65ba0dd68e5adb7fafb58157e)
|
12
|
+
|
13
|
+
# [v32.16.2](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v32.16.1...v32.16.2) (2021-06-01 09:52:22)
|
14
|
+
|
15
|
+
## Global changes
|
16
|
+
### Patches
|
17
|
+
|
18
|
+
* [[Hotfix(platform_handler_serverless_chef)] Fix Chef Workstation bash steps when run by root](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/1d99d1faa9cf66cd607eacc00205e312fd589715)
|
19
|
+
|
20
|
+
## Changes for platform_handler_serverless_chef
|
21
|
+
### Patches
|
22
|
+
|
23
|
+
* [[Hotfix(platform_handler_serverless_chef)] Fix Chef Workstation bash steps when run by root](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/1d99d1faa9cf66cd607eacc00205e312fd589715)
|
24
|
+
|
25
|
+
# [v32.16.1](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v32.16.0...v32.16.1) (2021-06-01 09:21:47)
|
26
|
+
|
27
|
+
## Global changes
|
28
|
+
### Patches
|
29
|
+
|
30
|
+
* [[Hotfix(platform_handler_serverless_chef)] Fix Chef Workstation installation step](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/58320d5394fec3bca9e1d0ffaf06ade42f1fd160)
|
31
|
+
|
32
|
+
## Changes for platform_handler_serverless_chef
|
33
|
+
### Patches
|
34
|
+
|
35
|
+
* [[Hotfix(platform_handler_serverless_chef)] Fix Chef Workstation installation step](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/58320d5394fec3bca9e1d0ffaf06ade42f1fd160)
|
36
|
+
|
37
|
+
# [v32.16.0](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v32.15.0...v32.16.0) (2021-05-31 17:55:49)
|
38
|
+
|
39
|
+
## Global changes
|
40
|
+
### Patches
|
41
|
+
|
42
|
+
* [[Feature(cmd_runner)] Add option to force bash usage in run_cmd](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/0711f1f33fc8f282b925726e0631ae78bf1337b5)
|
43
|
+
|
44
|
+
## Changes for cmd_runner
|
45
|
+
### Features
|
46
|
+
|
47
|
+
* [[Feature(cmd_runner)] Add option to force bash usage in run_cmd](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/0711f1f33fc8f282b925726e0631ae78bf1337b5)
|
48
|
+
|
49
|
+
# [v32.15.0](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v32.14.0...v32.15.0) (2021-05-31 14:43:32)
|
50
|
+
|
51
|
+
## Global changes
|
52
|
+
### Patches
|
53
|
+
|
54
|
+
* [[Feature(platform_handler_serverless_chef)] Use user-defined cookbook hpc_test to tune chef-client runs in test environments](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/2cfa010997b562a599308ceaaf62a8740ff51468)
|
55
|
+
|
56
|
+
## Changes for platform_handler_serverless_chef
|
57
|
+
### Features
|
58
|
+
|
59
|
+
* [[Feature(platform_handler_serverless_chef)] Use user-defined cookbook hpc_test to tune chef-client runs in test environments](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/2cfa010997b562a599308ceaaf62a8740ff51468)
|
60
|
+
|
1
61
|
# [v32.14.0](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v32.13.4...v32.14.0) (2021-05-31 09:05:45)
|
2
62
|
|
3
63
|
## Global changes
|
@@ -64,6 +64,12 @@ Services are being deployed by packaging the policy using [`chef install`](https
|
|
64
64
|
|
65
65
|
Then packaged services are uploaded on the node to configured and deployment is done remotely using [Chef Infra Client in local mode](https://docs.chef.io/ctl_chef_client/#run-in-local-mode) on the remote node.
|
66
66
|
|
67
|
+
## Test-wrapping cookbook `hpc_test`
|
68
|
+
|
69
|
+
If you have a cookbook named `hpc_test` in your Chef repository, then this plugin will wrap any run-list run in a local environment mode (used in tests) with the recipes `hpc_test::before_run` and `hpc_test::after_run`.
|
70
|
+
|
71
|
+
This way you can implement such recipes to adapt your Chef client runs to your test environment (like lack of connectivity, failing resources to be ignored...).
|
72
|
+
|
67
73
|
## Config DSL extension
|
68
74
|
|
69
75
|
### `helpers_including_recipes`
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'logger'
|
2
|
+
require 'tempfile'
|
2
3
|
require 'tty-command'
|
3
4
|
require 'hybrid_platforms_conductor/logger_helpers'
|
4
5
|
require 'hybrid_platforms_conductor/io_router'
|
@@ -66,6 +67,7 @@ module HybridPlatformsConductor
|
|
66
67
|
# * *timeout*: The command ended in timeout
|
67
68
|
# * *timeout* (Integer or nil): Timeout to apply for the command to be run, or nil for no timeout [default: nil]
|
68
69
|
# * *no_exception* (Boolean): If true, don't throw exception in case of error [default: false]
|
70
|
+
# * *force_bash* (Boolean): If true, then make sure command is invoked with bash instead of sh [default: false]
|
69
71
|
# Result::
|
70
72
|
# * Integer or Symbol: Exit status of the command, or Symbol in case of error. In case of dry-run mode the expected code is returned without executing anything.
|
71
73
|
# * String: Standard output of the command
|
@@ -78,7 +80,8 @@ module HybridPlatformsConductor
|
|
78
80
|
log_stderr_to_io: nil,
|
79
81
|
expected_code: 0,
|
80
82
|
timeout: nil,
|
81
|
-
no_exception: false
|
83
|
+
no_exception: false,
|
84
|
+
force_bash: false
|
82
85
|
)
|
83
86
|
expected_code = [expected_code] unless expected_code.is_a?(Array)
|
84
87
|
if @dry_run
|
@@ -101,6 +104,14 @@ module HybridPlatformsConductor
|
|
101
104
|
nil
|
102
105
|
end
|
103
106
|
start_time = Time.now if log_debug?
|
107
|
+
bash_file = nil
|
108
|
+
if force_bash
|
109
|
+
bash_file = Tempfile.new('hpc_bash')
|
110
|
+
bash_file.write(cmd)
|
111
|
+
bash_file.chmod 0700
|
112
|
+
bash_file.close
|
113
|
+
cmd = "/bin/bash -c #{bash_file.path}"
|
114
|
+
end
|
104
115
|
begin
|
105
116
|
# Make sure we keep a trace of stdout and stderr, even if it was not asked, just to use it in case of exceptions raised
|
106
117
|
cmd_result_stdout = ''
|
@@ -141,6 +152,7 @@ module HybridPlatformsConductor
|
|
141
152
|
cmd_stderr = "#{cmd_result_stderr.empty? ? '' : "#{cmd_result_stderr}\n"}#{$!}\n#{$!.backtrace.join("\n")}"
|
142
153
|
ensure
|
143
154
|
file_output.close unless file_output.nil?
|
155
|
+
bash_file.unlink unless bash_file.nil?
|
144
156
|
end
|
145
157
|
if log_debug?
|
146
158
|
elapsed = Time.now - start_time
|
@@ -65,17 +65,19 @@ module HybridPlatformsConductor
|
|
65
65
|
#
|
66
66
|
# Parameters::
|
67
67
|
# * *cmd* (String): The command to be run
|
68
|
+
# * *force_bash* (Boolean): If true, then make sure command is invoked with bash instead of sh [default: false]
|
68
69
|
# Result::
|
69
70
|
# * Integer: Exit code
|
70
71
|
# * String: Standard output
|
71
72
|
# * String: Error output
|
72
|
-
def run_cmd(cmd)
|
73
|
+
def run_cmd(cmd, force_bash: false)
|
73
74
|
@cmd_runner.run_cmd(
|
74
75
|
cmd,
|
75
76
|
timeout: @timeout,
|
76
77
|
log_to_stdout: false,
|
77
78
|
log_stdout_to_io: @stdout_io,
|
78
|
-
log_stderr_to_io: @stderr_io
|
79
|
+
log_stderr_to_io: @stderr_io,
|
80
|
+
force_bash: force_bash
|
79
81
|
)
|
80
82
|
end
|
81
83
|
|
@@ -37,7 +37,7 @@ module HybridPlatformsConductor
|
|
37
37
|
# Parameters::
|
38
38
|
# * *bash_cmds* (String): Bash commands to execute
|
39
39
|
def remote_bash(bash_cmds)
|
40
|
-
run_cmd "cd #{workspace_for(@node)} ; #{bash_cmds}"
|
40
|
+
run_cmd "cd #{workspace_for(@node)} ; #{bash_cmds}", force_bash: true
|
41
41
|
end
|
42
42
|
|
43
43
|
# Execute an interactive shell on the remote node
|
@@ -47,22 +47,44 @@ module HybridPlatformsConductor
|
|
47
47
|
end
|
48
48
|
self.extend_config_dsl_with MyDSLExtension, :init_serverless_chef
|
49
49
|
|
50
|
+
# Constructor
|
51
|
+
#
|
52
|
+
# Parameters::
|
53
|
+
# * *platform_type* (Symbol): Platform type
|
54
|
+
# * *repository_path* (String): Repository path
|
55
|
+
# * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
|
56
|
+
# * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
|
57
|
+
# * *config* (Config): Config to be used. [default: Config.new]
|
58
|
+
# * *cmd_runner* (CmdRunner): Command executor to be used. [default: CmdRunner.new]
|
59
|
+
def initialize(
|
60
|
+
platform_type,
|
61
|
+
repository_path,
|
62
|
+
logger: Logger.new(STDOUT),
|
63
|
+
logger_stderr: Logger.new(STDERR),
|
64
|
+
config: Config.new,
|
65
|
+
cmd_runner: CmdRunner.new
|
66
|
+
)
|
67
|
+
super
|
68
|
+
# Mutex for getting the full recipes tree
|
69
|
+
@recipes_tree_mutex = Mutex.new
|
70
|
+
end
|
71
|
+
|
50
72
|
# Setup the platform, install dependencies...
|
51
73
|
# [API] - This method is optional.
|
52
74
|
# [API] - @cmd_runner is accessible.
|
53
75
|
def setup
|
54
76
|
required_version = YAML.load_file("#{@repository_path}/chef_versions.yml")['workstation']
|
55
77
|
Bundler.with_unbundled_env do
|
56
|
-
exit_status, stdout, _stderr = @cmd_runner.run_cmd '/opt/chef-workstation/bin/chef --version', expected_code: [0,
|
78
|
+
exit_status, stdout, _stderr = @cmd_runner.run_cmd '/opt/chef-workstation/bin/chef --version', expected_code: [0, :command_error]
|
57
79
|
existing_version =
|
58
|
-
if exit_status ==
|
80
|
+
if exit_status == :command_error
|
59
81
|
'not installed'
|
60
82
|
else
|
61
83
|
expected_match = stdout.match(/^Chef Workstation version: (.+)\.\d+$/)
|
62
84
|
expected_match.nil? ? 'unreadable' : expected_match[1]
|
63
85
|
end
|
64
86
|
log_debug "Current Chef version: #{existing_version}. Required version: #{required_version}"
|
65
|
-
@cmd_runner.run_cmd "curl -L https://omnitruck.chef.io/install.sh | sudo bash -s -- -P chef-workstation -v #{required_version}" unless existing_version == required_version
|
87
|
+
@cmd_runner.run_cmd "curl -L https://omnitruck.chef.io/install.sh | #{@cmd_runner.root? ? '' : 'sudo '}bash -s -- -P chef-workstation -v #{required_version}" unless existing_version == required_version
|
66
88
|
end
|
67
89
|
end
|
68
90
|
|
@@ -139,12 +161,25 @@ module HybridPlatformsConductor
|
|
139
161
|
current_package_info = File.exist?(package_info_file) ? JSON.parse(File.read(package_info_file)).transform_keys(&:to_sym) : {}
|
140
162
|
unless current_package_info == package_info
|
141
163
|
Bundler.with_unbundled_env do
|
164
|
+
policy_file = "policyfiles/#{service}.rb"
|
165
|
+
if local_environment
|
166
|
+
local_policy_file = "policyfiles/#{service}.local.rb"
|
167
|
+
# In local mode, we always regenerate the lock file as we may modify the run list
|
168
|
+
run_list = known_cookbook_paths.any? { |cookbook_path| File.exist?("#{@repository_path}/#{cookbook_path}/hpc_test/recipes/before_run.rb") } ? ['hpc_test::before_run'] : []
|
169
|
+
dsl_parser = DslParser.new
|
170
|
+
dsl_parser.parse("#{@repository_path}/#{policy_file}")
|
171
|
+
run_list.concat dsl_parser.calls.find { |call_info| call_info[:method] == :run_list }[:args].flatten
|
172
|
+
run_list << 'hpc_test::after_run' if known_cookbook_paths.any? { |cookbook_path| File.exist?("#{@repository_path}/#{cookbook_path}/hpc_test/recipes/after_run.rb") }
|
173
|
+
File.write("#{@repository_path}/#{local_policy_file}", File.read("#{@repository_path}/#{policy_file}") + "\nrun_list #{run_list.map { |recipe| "'#{recipe}'" }.join(', ')}\n")
|
174
|
+
policy_file = local_policy_file
|
175
|
+
end
|
176
|
+
lock_file = "#{File.dirname(policy_file)}/#{File.basename(policy_file, '.rb')}.lock.json"
|
142
177
|
# If the policy lock file does not exist, generate it
|
143
|
-
@cmd_runner.run_cmd "cd #{@repository_path} && /opt/chef-workstation/bin/chef install
|
178
|
+
@cmd_runner.run_cmd "cd #{@repository_path} && /opt/chef-workstation/bin/chef install #{policy_file} --chef-license accept" unless File.exist?("#{@repository_path}/#{lock_file}")
|
144
179
|
extra_cp_data_bags = File.exist?("#{@repository_path}/data_bags") ? " && cp -ar data_bags/ #{package_dir}/" : ''
|
145
180
|
@cmd_runner.run_cmd "cd #{@repository_path} && \
|
146
|
-
sudo rm -rf #{package_dir} && \
|
147
|
-
/opt/chef-workstation/bin/chef export
|
181
|
+
#{@cmd_runner.root? ? '' : 'sudo '}rm -rf #{package_dir} && \
|
182
|
+
/opt/chef-workstation/bin/chef export #{policy_file} #{package_dir} --chef-license accept#{extra_cp_data_bags}"
|
148
183
|
end
|
149
184
|
unless @cmd_runner.dry_run
|
150
185
|
# Create secrets file
|
@@ -193,9 +228,15 @@ module HybridPlatformsConductor
|
|
193
228
|
FileUtils.mkdir_p "#{package_dir}/nodes"
|
194
229
|
File.write("#{package_dir}/nodes/#{node}.json", (known_nodes.include?(node) ? metadata_for(node) : {}).merge(@nodes_handler.metadata_of(node)).to_json)
|
195
230
|
end
|
231
|
+
client_options = [
|
232
|
+
'--local-mode',
|
233
|
+
'--chef-license', 'accept',
|
234
|
+
'--json-attributes', "nodes/#{node}.json"
|
235
|
+
]
|
236
|
+
client_options << '--why-run' if use_why_run
|
196
237
|
if @nodes_handler.get_use_local_chef_of(node)
|
197
238
|
# Just run the chef-client directly from the packaged repository
|
198
|
-
[{ bash: "cd #{package_dir} && sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client
|
239
|
+
[{ bash: "cd #{package_dir} && #{@cmd_runner.root? ? '' : 'sudo '}SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client #{client_options.join(' ')}" }]
|
199
240
|
else
|
200
241
|
# Upload the package and run it from the node
|
201
242
|
package_name = File.basename(package_dir)
|
@@ -211,7 +252,11 @@ module HybridPlatformsConductor
|
|
211
252
|
'set -o pipefail',
|
212
253
|
"if [ -n \"$(command -v apt)\" ]; then #{sudo}apt update && #{sudo}apt install -y curl build-essential ; else #{sudo}yum groupinstall 'Development Tools' && #{sudo}yum install -y curl ; fi",
|
213
254
|
'mkdir -p ./hpc_deploy',
|
214
|
-
|
255
|
+
'rm -rf ./hpc_deploy/tmp',
|
256
|
+
'mkdir -p ./hpc_deploy/tmp',
|
257
|
+
'curl --location https://omnitruck.chef.io/install.sh --output ./hpc_deploy/install.sh',
|
258
|
+
'chmod a+x ./hpc_deploy/install.sh',
|
259
|
+
"#{sudo}TMPDIR=./hpc_deploy/tmp ./hpc_deploy/install.sh -d /opt/artefacts -v #{required_chef_client_version} -s once"
|
215
260
|
]
|
216
261
|
},
|
217
262
|
{
|
@@ -219,10 +264,9 @@ module HybridPlatformsConductor
|
|
219
264
|
remote_bash: [
|
220
265
|
'set -e',
|
221
266
|
"cd ./hpc_deploy/#{package_name}",
|
222
|
-
"#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/bin/chef-client
|
223
|
-
'cd ..'
|
224
|
-
|
225
|
-
]
|
267
|
+
"#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/bin/chef-client #{client_options.join(' ')}",
|
268
|
+
'cd ..'
|
269
|
+
] + (log_debug? ? [] : ["#{sudo}rm -rf ./hpc_deploy/#{package_name}"])
|
226
270
|
}
|
227
271
|
]
|
228
272
|
end
|
@@ -371,7 +415,7 @@ module HybridPlatformsConductor
|
|
371
415
|
impacted_recipes.uniq!
|
372
416
|
log_debug "* #{impacted_recipes.size} impacted recipes:\n#{impacted_recipes.map { |(cookbook, recipe)| "#{cookbook}::#{recipe}" }.sort.join("\n")}"
|
373
417
|
|
374
|
-
recipes_tree =
|
418
|
+
recipes_tree = full_recipes_tree
|
375
419
|
[
|
376
420
|
impacted_nodes,
|
377
421
|
(
|
@@ -419,6 +463,45 @@ module HybridPlatformsConductor
|
|
419
463
|
@cookbook_paths
|
420
464
|
end
|
421
465
|
|
466
|
+
# Get the run list of a given policy
|
467
|
+
#
|
468
|
+
# Parameters::
|
469
|
+
# * *policy* (String): Policy to get the run list from
|
470
|
+
# Result::
|
471
|
+
# * Array<[String or nil, Symbol, Symbol]>: Run list of the given policy, as [cookbook_dir, cookbook, recipe]
|
472
|
+
def policy_run_list(policy)
|
473
|
+
# Read the policy file
|
474
|
+
dsl_parser = DslParser.new
|
475
|
+
policy_file = "#{@repository_path}/policyfiles/#{policy}.rb"
|
476
|
+
dsl_parser.parse(policy_file)
|
477
|
+
run_list_call = dsl_parser.calls.find { |call_info| call_info[:method] == :run_list }
|
478
|
+
raise "Policy #{policy} has no run list defined in #{policy_file}" if run_list_call.nil?
|
479
|
+
run_list_call[:args].map { |recipe_def| decode_recipe(recipe_def) }
|
480
|
+
end
|
481
|
+
|
482
|
+
# Return the cookbook directory, cookbook name and recipe name from which a recipe definition is found.
|
483
|
+
# The following forms are handled:
|
484
|
+
# * cookbook
|
485
|
+
# * cookbook::recipe
|
486
|
+
# * recipe[cookbook]
|
487
|
+
# * recipe[cookbook::recipe]
|
488
|
+
#
|
489
|
+
# Parameters::
|
490
|
+
# * *recipe_def* (String): Recipe definition (cookbook or cookbook::recipe).
|
491
|
+
# Result::
|
492
|
+
# * String: The cookbook directory, or nil if unknown
|
493
|
+
# * Symbol: The cookbook name
|
494
|
+
# * Symbol: The recipe name
|
495
|
+
def decode_recipe(recipe_def)
|
496
|
+
recipe_def = $1 if recipe_def =~ /^recipe\[(.+)\]$/
|
497
|
+
cookbook, recipe = recipe_def.split('::').map(&:to_sym)
|
498
|
+
recipe = :default if recipe.nil?
|
499
|
+
# Find the cookbook it belongs to
|
500
|
+
cookbook_dir = known_cookbook_paths.find { |cookbook_path| File.exist?("#{@repository_path}/#{cookbook_path}/#{cookbook}") }
|
501
|
+
raise "Unknown recipe #{cookbook}::#{recipe} from cookbook #{@repository_path}/#{cookbook_dir}/#{cookbook}." if !cookbook_dir.nil? && !File.exist?("#{@repository_path}/#{cookbook_dir}/#{cookbook}/recipes/#{recipe}.rb")
|
502
|
+
return cookbook_dir, cookbook, recipe
|
503
|
+
end
|
504
|
+
|
422
505
|
private
|
423
506
|
|
424
507
|
# Return the JSON associated to a node
|
@@ -431,6 +514,18 @@ module HybridPlatformsConductor
|
|
431
514
|
JSON.parse(File.read("#{@repository_path}/nodes/#{node}.json"))
|
432
515
|
end
|
433
516
|
|
517
|
+
# Get the full recipes tree.
|
518
|
+
# Keep it in a cache for performance.
|
519
|
+
#
|
520
|
+
# Result::
|
521
|
+
# * Hash: The recipes tree. See RecipesTreeBuilder#full_recipes_tree for the detailed signature
|
522
|
+
def full_recipes_tree
|
523
|
+
@recipes_tree_mutex.synchronize do
|
524
|
+
@recipes_tree = RecipesTreeBuilder.new(@config, self).full_recipes_tree unless defined?(@recipes_tree)
|
525
|
+
end
|
526
|
+
@recipes_tree
|
527
|
+
end
|
528
|
+
|
434
529
|
end
|
435
530
|
|
436
531
|
end
|
@@ -36,12 +36,12 @@ module HybridPlatformsConductor
|
|
36
36
|
def full_recipes_tree
|
37
37
|
@recipes_tree = {}
|
38
38
|
@platform.deployable_services.each do |service|
|
39
|
-
policy_run_list(service).each do |(cookbook_dir, cookbook, recipe)|
|
39
|
+
@platform.policy_run_list(service).each do |(cookbook_dir, cookbook, recipe)|
|
40
40
|
add_recipe_in_tree(cookbook_dir, cookbook, recipe)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
@platform.deployable_services.each do |service|
|
44
|
-
policy_run_list(service).each do |(cookbook_dir, cookbook, recipe)|
|
44
|
+
@platform.policy_run_list(service).each do |(cookbook_dir, cookbook, recipe)|
|
45
45
|
mark_recipe_used_by_policy(cookbook, recipe, service)
|
46
46
|
end
|
47
47
|
end
|
@@ -50,45 +50,6 @@ module HybridPlatformsConductor
|
|
50
50
|
|
51
51
|
private
|
52
52
|
|
53
|
-
# Get the run list of a given policy
|
54
|
-
#
|
55
|
-
# Parameters::
|
56
|
-
# * *policy* (String): Policy to get the run list from
|
57
|
-
# Result::
|
58
|
-
# * Array<[String or nil, Symbol,Symbol]>: Run list of the given policy, as [cookbook_dir, cookbook, recipe]
|
59
|
-
def policy_run_list(policy)
|
60
|
-
# Read the policy file
|
61
|
-
dsl_parser = DslParser.new
|
62
|
-
policy_file = "#{@platform.repository_path}/policyfiles/#{policy}.rb"
|
63
|
-
dsl_parser.parse(policy_file)
|
64
|
-
run_list_call = dsl_parser.calls.find { |call_info| call_info[:method] == :run_list }
|
65
|
-
raise "Policy #{policy} has no run list defined in #{policy_file}" if run_list_call.nil?
|
66
|
-
run_list_call[:args].map { |recipe_def| decode_recipe(recipe_def) }
|
67
|
-
end
|
68
|
-
|
69
|
-
# Return the cookbook directory, cookbook name and recipe name from which a recipe definition is found.
|
70
|
-
# The following forms are handled:
|
71
|
-
# * cookbook
|
72
|
-
# * cookbook::recipe
|
73
|
-
# * recipe[cookbook]
|
74
|
-
# * recipe[cookbook::recipe]
|
75
|
-
#
|
76
|
-
# Parameters::
|
77
|
-
# * *recipe_def* (String): Recipe definition (cookbook or cookbook::recipe).
|
78
|
-
# Result::
|
79
|
-
# * String: The cookbook directory, or nil if unknown
|
80
|
-
# * Symbol: The cookbook name
|
81
|
-
# * Symbol: The recipe name
|
82
|
-
def decode_recipe(recipe_def)
|
83
|
-
recipe_def = $1 if recipe_def =~ /^recipe\[(.+)\]$/
|
84
|
-
cookbook, recipe = recipe_def.split('::').map(&:to_sym)
|
85
|
-
recipe = :default if recipe.nil?
|
86
|
-
# Find the cookbook it belongs to
|
87
|
-
cookbook_dir = @platform.known_cookbook_paths.find { |cookbook_path| File.exist?("#{@platform.repository_path}/#{cookbook_path}/#{cookbook}") }
|
88
|
-
raise "Unknown recipe #{cookbook}::#{recipe} from cookbook #{@platform.repository_path}/#{cookbook_dir}/#{cookbook}." if !cookbook_dir.nil? && !File.exist?("#{@platform.repository_path}/#{cookbook_dir}/#{cookbook}/recipes/#{recipe}.rb")
|
89
|
-
return cookbook_dir, cookbook, recipe
|
90
|
-
end
|
91
|
-
|
92
53
|
# Fill the tree with a recipe and all its dependencies
|
93
54
|
#
|
94
55
|
# Parameters::
|
@@ -142,11 +103,11 @@ module HybridPlatformsConductor
|
|
142
103
|
# Check for include_recipe
|
143
104
|
used_recipes = recipe_content.
|
144
105
|
scan(/include_recipe\s+["'](\w+(::\w+)?)["']/).
|
145
|
-
map { |(recipe_def, _sub_grp)| decode_recipe(recipe_def) }
|
106
|
+
map { |(recipe_def, _sub_grp)| @platform.decode_recipe(recipe_def) }
|
146
107
|
# Check for some helpers we know include some recipes
|
147
108
|
@config.known_helpers_including_recipes.each do |helper_name, used_recipes_by_helper|
|
148
109
|
if recipe_content =~ Regexp.new(/(\W|^)#{Regexp.escape(helper_name)}(\W|$)/)
|
149
|
-
used_recipes.concat(used_recipes_by_helper.map { |recipe_def| decode_recipe(recipe_def) })
|
110
|
+
used_recipes.concat(used_recipes_by_helper.map { |recipe_def| @platform.decode_recipe(recipe_def) })
|
150
111
|
used_recipes.uniq!
|
151
112
|
end
|
152
113
|
end
|
@@ -112,7 +112,7 @@ module HybridPlatformsConductor
|
|
112
112
|
# Result::
|
113
113
|
# * String: The Podman command
|
114
114
|
def podman_cmd
|
115
|
-
@podman_cmd = @cmd_runner.root? ? '
|
115
|
+
@podman_cmd = "#{@cmd_runner.root? ? '' : 'sudo '}podman" unless defined?(@podman_cmd)
|
116
116
|
@podman_cmd
|
117
117
|
end
|
118
118
|
|
@@ -13,6 +13,13 @@ describe HybridPlatformsConductor::CmdRunner do
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
it 'runs a simple bash command and forces usage of bash' do
|
17
|
+
with_repository do |repository|
|
18
|
+
# Use set -o pipefail that does not work in /bin/sh
|
19
|
+
expect(test_cmd_runner.run_cmd "set -o pipefail ; echo TestStderr 1>&2 ; echo TestStdout", force_bash: true).to eq [0, "TestStdout\n", "TestStderr\n"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
16
23
|
it 'runs a simple bash command and logs stdout and stderr to a file' do
|
17
24
|
with_repository do |repository|
|
18
25
|
test_cmd_runner.run_cmd "echo TestStderr 1>&2 ; sleep 1 ; echo TestStdout", log_to_file: "#{repository}/test_file"
|
data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/packaging_spec.rb
CHANGED
@@ -7,24 +7,34 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
|
|
7
7
|
# Parameters::
|
8
8
|
# * *repository* (String): Repository to be packaged
|
9
9
|
# * *policy* (String): Expected policy to be packaged [default: 'test_policy']
|
10
|
+
# * *policy_file* (String): Expected policy file used [default: "policyfiles/#{policy}.rb"]
|
10
11
|
# * *install* (Boolean): Are we expecting the chef install stage? [default: true]
|
11
12
|
# * *export* (Boolean): Are we expecting the chef export stage? [default: true]
|
12
13
|
# * *data_bags* (Boolean): Do we expect data bags copy? [default: false]
|
14
|
+
# * *env* (String): Expected environment being packaged [default: 'prod']
|
13
15
|
# * Proc: Code called with mock in place
|
14
16
|
def with_packaging_mocked(
|
15
17
|
repository,
|
16
18
|
policy: 'test_policy',
|
19
|
+
policy_file: "policyfiles/#{policy}.rb",
|
17
20
|
install: true,
|
18
21
|
export: true,
|
19
|
-
data_bags: false
|
22
|
+
data_bags: false,
|
23
|
+
env: 'prod'
|
20
24
|
)
|
21
25
|
with_cmd_runner_mocked(
|
22
26
|
if install
|
23
27
|
[
|
24
28
|
[
|
25
|
-
"cd #{repository} && /opt/chef-workstation/bin/chef install
|
29
|
+
"cd #{repository} && /opt/chef-workstation/bin/chef install #{policy_file} --chef-license accept",
|
26
30
|
proc do
|
27
|
-
|
31
|
+
# Mock the run_list stored in the lock file
|
32
|
+
File.write(
|
33
|
+
"#{repository}/#{policy_file.gsub(/.rb$/, '.lock.json')}",
|
34
|
+
{
|
35
|
+
run_list: eval("[#{File.read("#{repository}/#{policy_file}").split("\n").select { |line| line =~ /^run_list.+$/ }.last.match(/^run_list(.+)$/)[1]}]").flatten
|
36
|
+
}.to_json
|
37
|
+
)
|
28
38
|
[0, 'Chef install done', '']
|
29
39
|
end
|
30
40
|
]
|
@@ -34,11 +44,12 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
|
|
34
44
|
end +
|
35
45
|
if export
|
36
46
|
[
|
47
|
+
['whoami', proc { [0, 'test_user', ''] }, { optional: true }],
|
37
48
|
[
|
38
|
-
/^cd #{Regexp.escape(repository)} &&\s+sudo rm -rf dist
|
49
|
+
/^cd #{Regexp.escape(repository)} &&\s+sudo rm -rf dist\/#{Regexp.escape(env)}\/#{Regexp.escape(policy)} &&\s+\/opt\/chef-workstation\/bin\/chef export #{Regexp.escape(policy_file)} dist\/#{Regexp.escape(env)}\/#{Regexp.escape(policy)} --chef-license accept#{data_bags ? " && cp -ar data_bags/ dist/#{Regexp.escape(env)}/#{Regexp.escape(policy)}/" : ''}$/,
|
39
50
|
proc do
|
40
|
-
FileUtils.mkdir_p "#{repository}/dist
|
41
|
-
FileUtils.cp_r("#{repository}/data_bags", "#{repository}/dist
|
51
|
+
FileUtils.mkdir_p "#{repository}/dist/#{env}/#{policy}"
|
52
|
+
FileUtils.cp_r("#{repository}/data_bags", "#{repository}/dist/#{env}/#{policy}/") if data_bags
|
42
53
|
[0, 'Chef export done', '']
|
43
54
|
end
|
44
55
|
]
|
@@ -96,6 +107,26 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
|
|
96
107
|
end
|
97
108
|
end
|
98
109
|
|
110
|
+
it 'packages the repository for a given node and service in local mode' do
|
111
|
+
with_serverless_chef_platforms('1_node') do |platform, repository|
|
112
|
+
with_packaging_mocked(repository, policy_file: 'policyfiles/test_policy.local.rb', env: 'local') do
|
113
|
+
platform.package(services: { 'node' => %w[test_policy] }, secrets: {}, local_environment: true)
|
114
|
+
local_policy_file = "#{repository}/policyfiles/test_policy.local.lock.json"
|
115
|
+
expect(File.exist?(local_policy_file)).to eq true
|
116
|
+
expect(JSON.parse(File.read(local_policy_file))).to eq('run_list' => ['recipe[test_cookbook]'])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'packages the repository without resolving dependencies when the lock file already exists' do
|
122
|
+
with_serverless_chef_platforms('1_node') do |platform, repository|
|
123
|
+
File.write("#{repository}/policyfiles/test_policy.lock.json", '{}')
|
124
|
+
with_packaging_mocked(repository, install: false) do
|
125
|
+
platform.package(services: { 'node' => %w[test_policy] }, secrets: {}, local_environment: false)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
99
130
|
it 'does not package the repository twice for the same config' do
|
100
131
|
with_serverless_chef_platforms('1_node', as_git: true) do |platform, repository|
|
101
132
|
with_packaging_mocked(repository) do
|
@@ -208,6 +239,55 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
|
|
208
239
|
|
209
240
|
end
|
210
241
|
|
242
|
+
context 'with a platform having hpc_test cookbook' do
|
243
|
+
|
244
|
+
it 'packages the repository with before_run and after_run recipes wrapping the run list' do
|
245
|
+
with_serverless_chef_platforms('hpc_test') do |platform, repository|
|
246
|
+
with_packaging_mocked(repository, policy_file: 'policyfiles/test_policy.local.rb', env: 'local') do
|
247
|
+
platform.package(services: { 'node' => %w[test_policy] }, secrets: {}, local_environment: true)
|
248
|
+
local_policy_file = "#{repository}/policyfiles/test_policy.local.lock.json"
|
249
|
+
expect(File.exist?(local_policy_file)).to eq true
|
250
|
+
expect(JSON.parse(File.read(local_policy_file))).to eq('run_list' => [
|
251
|
+
'hpc_test::before_run',
|
252
|
+
'recipe[test_cookbook]',
|
253
|
+
'hpc_test::after_run'
|
254
|
+
])
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'packages the repository with before_run only recipe' do
|
260
|
+
with_serverless_chef_platforms('hpc_test') do |platform, repository|
|
261
|
+
File.unlink "#{repository}/cookbooks/hpc_test/recipes/after_run.rb"
|
262
|
+
with_packaging_mocked(repository, policy_file: 'policyfiles/test_policy.local.rb', env: 'local') do
|
263
|
+
platform.package(services: { 'node' => %w[test_policy] }, secrets: {}, local_environment: true)
|
264
|
+
local_policy_file = "#{repository}/policyfiles/test_policy.local.lock.json"
|
265
|
+
expect(File.exist?(local_policy_file)).to eq true
|
266
|
+
expect(JSON.parse(File.read(local_policy_file))).to eq('run_list' => [
|
267
|
+
'hpc_test::before_run',
|
268
|
+
'recipe[test_cookbook]'
|
269
|
+
])
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'packages the repository with after_run only recipe' do
|
275
|
+
with_serverless_chef_platforms('hpc_test') do |platform, repository|
|
276
|
+
File.unlink "#{repository}/cookbooks/hpc_test/recipes/before_run.rb"
|
277
|
+
with_packaging_mocked(repository, policy_file: 'policyfiles/test_policy.local.rb', env: 'local') do
|
278
|
+
platform.package(services: { 'node' => %w[test_policy] }, secrets: {}, local_environment: true)
|
279
|
+
local_policy_file = "#{repository}/policyfiles/test_policy.local.lock.json"
|
280
|
+
expect(File.exist?(local_policy_file)).to eq true
|
281
|
+
expect(JSON.parse(File.read(local_policy_file))).to eq('run_list' => [
|
282
|
+
'recipe[test_cookbook]',
|
283
|
+
'hpc_test::after_run'
|
284
|
+
])
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
|
211
291
|
end
|
212
292
|
|
213
293
|
end
|
@@ -37,7 +37,11 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
|
|
37
37
|
'set -o pipefail',
|
38
38
|
"if [ -n \"$(command -v apt)\" ]; then #{sudo}apt update && #{sudo}apt install -y curl build-essential ; else #{sudo}yum groupinstall 'Development Tools' && #{sudo}yum install -y curl ; fi",
|
39
39
|
'mkdir -p ./hpc_deploy',
|
40
|
-
|
40
|
+
'rm -rf ./hpc_deploy/tmp',
|
41
|
+
'mkdir -p ./hpc_deploy/tmp',
|
42
|
+
'curl --location https://omnitruck.chef.io/install.sh --output ./hpc_deploy/install.sh',
|
43
|
+
'chmod a+x ./hpc_deploy/install.sh',
|
44
|
+
"#{sudo}TMPDIR=./hpc_deploy/tmp ./hpc_deploy/install.sh -d /opt/artefacts -v 17.0 -s once"
|
41
45
|
]
|
42
46
|
},
|
43
47
|
{
|
@@ -45,9 +49,9 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
|
|
45
49
|
remote_bash: [
|
46
50
|
'set -e',
|
47
51
|
"cd ./hpc_deploy/#{policy}",
|
48
|
-
"#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/bin/chef-client --local-mode --chef-license
|
52
|
+
"#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/bin/chef-client --local-mode --chef-license accept --json-attributes nodes/#{node}.json#{check_mode ? ' --why-run' : ''}",
|
49
53
|
'cd ..',
|
50
|
-
"#{sudo}rm -rf
|
54
|
+
"#{sudo}rm -rf ./hpc_deploy/#{policy}"
|
51
55
|
]
|
52
56
|
}
|
53
57
|
]
|
@@ -237,7 +241,7 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
|
|
237
241
|
)
|
238
242
|
expect(platform.actions_to_deploy_on('local', 'test_policy_1', use_why_run: false)).to eq [
|
239
243
|
{
|
240
|
-
bash: "cd #{repository}/dist/prod/test_policy_1 && sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client --local-mode --json-attributes nodes/local.json"
|
244
|
+
bash: "cd #{repository}/dist/prod/test_policy_1 && sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client --local-mode --chef-license accept --json-attributes nodes/local.json"
|
241
245
|
}
|
242
246
|
]
|
243
247
|
end
|
@@ -31,7 +31,7 @@ module HybridPlatformsConductorTest
|
|
31
31
|
end
|
32
32
|
# We need to protect the access to this array as the mocked commands can be called by competing threads
|
33
33
|
remaining_expected_commands_mutex = Mutex.new
|
34
|
-
allow(cmd_runner).to receive(:run_cmd) do |cmd, log_to_file: nil, log_to_stdout: true, log_stdout_to_io: nil, log_stderr_to_io: nil, expected_code: 0, timeout: nil, no_exception: false|
|
34
|
+
allow(cmd_runner).to receive(:run_cmd) do |cmd, log_to_file: nil, log_to_stdout: true, log_stdout_to_io: nil, log_stderr_to_io: nil, expected_code: 0, timeout: nil, no_exception: false, force_bash: false|
|
35
35
|
# Check the remaining expected commands
|
36
36
|
found_command = nil
|
37
37
|
found_command_code = nil
|
@@ -0,0 +1 @@
|
|
1
|
+
# Recipe
|
@@ -0,0 +1 @@
|
|
1
|
+
# Recipe
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hybrid_platforms_conductor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 32.
|
4
|
+
version: 32.16.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Muriel Salvan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: range_operators
|
@@ -873,6 +873,11 @@ files:
|
|
873
873
|
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/data_bags/data_bags/my_bag/my_item.json
|
874
874
|
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/data_bags/nodes/node.json
|
875
875
|
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/data_bags/policyfiles/test_policy.rb
|
876
|
+
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/chef_versions.yml
|
877
|
+
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/cookbooks/hpc_test/recipes/after_run.rb
|
878
|
+
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/cookbooks/hpc_test/recipes/before_run.rb
|
879
|
+
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/nodes/node.json
|
880
|
+
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/policyfiles/test_policy.rb
|
876
881
|
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/recipes/cookbooks/test_cookbook_1/recipes/default.rb
|
877
882
|
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/recipes/cookbooks/test_cookbook_2/libraries/default.rb
|
878
883
|
- spec/hybrid_platforms_conductor_test/serverless_chef_repositories/recipes/cookbooks/test_cookbook_2/recipes/default.rb
|