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.
Files changed (20) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/docs/plugins/platform_handler/serverless_chef.md +6 -0
  4. data/lib/hybrid_platforms_conductor/cmd_runner.rb +13 -1
  5. data/lib/hybrid_platforms_conductor/connector.rb +4 -2
  6. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +1 -1
  7. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +108 -13
  8. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef/recipes_tree_builder.rb +4 -43
  9. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/podman.rb +1 -1
  10. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  11. data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +7 -0
  12. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/packaging_spec.rb +86 -6
  13. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +8 -4
  14. data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +1 -1
  15. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/chef_versions.yml +3 -0
  16. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/cookbooks/hpc_test/recipes/after_run.rb +1 -0
  17. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/cookbooks/hpc_test/recipes/before_run.rb +1 -0
  18. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/nodes/node.json +10 -0
  19. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/hpc_test/policyfiles/test_policy.rb +3 -0
  20. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99c918a62cd924ceaca95073e944df866b9b1ecce771dcb710122ec501cadf95
4
- data.tar.gz: 959326a99c32ac8eb120af28c8b3c34304e2efa61df64f43a99545d0d33f88e3
3
+ metadata.gz: 22f6bf2ee85b7db6649e517503f00dd71f0604d4afb3a12bee347820af4d90e2
4
+ data.tar.gz: 7804ffa80bb56697c98970b1d781a5e3be12b27bc8c38a3df9eb26b666791ed8
5
5
  SHA512:
6
- metadata.gz: 8e6c3a54a5c04f21aaeeba1a047366a7f427680077712742c0b2a7977cecfe14c4058fdd1e1c6c66842b603a7f7590bfea6eae0d975240e19a4ffa8214043c17
7
- data.tar.gz: e8dd3ef50cec1a0dd7e94161159a30f359fca736bd6e7ab14ab916e2d743d48889bf891c3ca2bf626d3f087e1472ff43eb1abc0370e3fe63226cb0a8595950ad
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, 127]
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 == 127
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 policyfiles/#{service}.rb" unless File.exist?("#{@repository_path}/policyfiles/#{service}.lock.json")
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 policyfiles/#{service}.rb #{package_dir}#{extra_cp_data_bags}"
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 --local-mode --json-attributes nodes/#{node}.json#{use_why_run ? ' --why-run' : ''}" }]
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
- "curl --location https://omnitruck.chef.io/install.sh | tac | tac | #{sudo}bash -s -- -d /opt/artefacts -v #{required_chef_client_version} -s once"
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 --local-mode --chef-license=accept --json-attributes nodes/#{node}.json#{use_why_run ? ' --why-run' : ''}",
223
- 'cd ..',
224
- "#{sudo}rm -rf #{package_name}"
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 = RecipesTreeBuilder.new(@config, self).full_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? ? 'podman' : 'sudo podman' unless defined?(@podman_cmd)
115
+ @podman_cmd = "#{@cmd_runner.root? ? '' : 'sudo '}podman" unless defined?(@podman_cmd)
116
116
  @podman_cmd
117
117
  end
118
118
 
@@ -1,5 +1,5 @@
1
1
  module HybridPlatformsConductor
2
2
 
3
- VERSION = '32.14.0'
3
+ VERSION = '32.16.3'
4
4
 
5
5
  end
@@ -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"
@@ -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 policyfiles/#{policy}.rb",
29
+ "cd #{repository} && /opt/chef-workstation/bin/chef install #{policy_file} --chef-license accept",
26
30
  proc do
27
- File.write("#{repository}/policyfiles/#{policy}.lock.json", '{}')
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\/prod\/#{Regexp.escape(policy)} &&\s+\/opt\/chef-workstation\/bin\/chef export policyfiles\/#{Regexp.escape(policy)}.rb dist\/prod\/#{Regexp.escape(policy)}#{data_bags ? " && cp -ar data_bags/ dist/prod/#{Regexp.escape(policy)}/" : ''}$/,
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/prod/#{policy}"
41
- FileUtils.cp_r("#{repository}/data_bags", "#{repository}/dist/prod/#{policy}/") if data_bags
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
- "curl --location https://omnitruck.chef.io/install.sh | tac | tac | #{sudo}bash -s -- -d /opt/artefacts -v 17.0 -s once"
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=accept --json-attributes nodes/#{node}.json#{check_mode ? ' --why-run' : ''}",
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 #{policy}"
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,3 @@
1
+ ---
2
+ workstation: '21.5'
3
+ client: '17.0'
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "node",
3
+ "normal": {
4
+ "description": "Single test node",
5
+ "image": "debian_9",
6
+ "private_ips": ["172.16.0.1"]
7
+ },
8
+ "policy_name": "test_policy",
9
+ "policy_group": "test_group"
10
+ }
@@ -0,0 +1,3 @@
1
+ name File.basename(__FILE__, '.rb')
2
+ default_source :supermarket
3
+ run_list 'recipe[test_cookbook]'
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.14.0
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-05-31 00:00:00.000000000 Z
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