hybrid_platforms_conductor 33.0.3 → 33.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eada2aa4c440c2fdfc69fc7f6ec20d2175d4d52d7405bc192ae32cf7a4850f38
4
- data.tar.gz: a720caa3b3c584a5c354cb787a5374866bfa40f7740ecf727d14db9a271e09d5
3
+ metadata.gz: f311db8004c9c9bce51821d58c9cee66e36b73565a70e990d5d56269a758ef07
4
+ data.tar.gz: 232be58c9d26f1821afea89a55421514c8f97becd2b316acf62c2e51c5e29b8c
5
5
  SHA512:
6
- metadata.gz: 5369c44dd7647622a49f8db8c01722ca0ba96ff7c5e7439fb3c4b208faea076513de0ec47fe3f5a6612807680ecff7b7e2962baf3da569c67c3fd8632dfb9a20
7
- data.tar.gz: 882ca15df05a0a209a7de8f723cc5ddda0a3ea89ce3b5655d6b5c4968e01adc9aa07437bbd17dafe6363331f373776c84fca307e150a53c69ef800926e54ee92
6
+ metadata.gz: d2c2fe3c9d0f5fccceb5a2a2b9759316730c536a5de99e63b5ecc32ef857b4f33a5aef5c4494c4b42f54a1b17a620af7a06526a953a095a8ac3e8fdc7d6fcd8e
7
+ data.tar.gz: 6d15f57eae1683425b64871b0234a33a6a98cf3300a3c481d98b88a525809c8ad277836a228565675b5bcd76bc23a130b5392431a045bbe18ec6cd032eeea27d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,57 @@
1
+ # [v33.2.1](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.2.0...v33.2.1) (2021-06-21 10:23:51)
2
+
3
+ ## Global changes
4
+ ### Patches
5
+
6
+ * [[Hotfix(platform_handler_serverless_chef)] Corrected dry-run mode not working](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/4800a0f4255c1999eed33651c1e66c445acd17bb)
7
+
8
+ ## Changes for platform_handler_serverless_chef
9
+ ### Patches
10
+
11
+ * [[Hotfix(platform_handler_serverless_chef)] Corrected dry-run mode not working](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/4800a0f4255c1999eed33651c1e66c445acd17bb)
12
+
13
+ # [v33.2.0](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.1.1...v33.2.0) (2021-06-18 23:22:21)
14
+
15
+ ## Global changes
16
+ ### Patches
17
+
18
+ * [[Feature(platform_handler_serverless_chef)] [#70] Install dependency gems from cookbook metadata before calling chef-client](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/6dfe7aa053db63489f3d0a236433304606051ecd)
19
+
20
+ ## Changes for platform_handler_serverless_chef
21
+ ### Features
22
+
23
+ * [[Feature(platform_handler_serverless_chef)] [#70] Install dependency gems from cookbook metadata before calling chef-client](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/6dfe7aa053db63489f3d0a236433304606051ecd)
24
+
25
+ # [v33.1.1](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.1.0...v33.1.1) (2021-06-18 13:19:39)
26
+
27
+ ### Patches
28
+
29
+ * [[Fix] Corrected bugs on log remote_fs plugin not working in dry-run and topographer](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/90dcde919b6fc99b8f79fc642fef8b4150a5a9d6)
30
+
31
+ # [v33.1.0](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.0.4...v33.1.0) (2021-06-18 11:37:28)
32
+
33
+ ## Global changes
34
+ ### Patches
35
+
36
+ * [[Feature(connector_local)] [#68] Add sudo support when copying files using the local connector](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/7725beca4429de0e81bcac0c0ac4fe149e625da2)
37
+
38
+ ## Changes for connector_local
39
+ ### Features
40
+
41
+ * [[Feature(connector_local)] [#68] Add sudo support when copying files using the local connector](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/7725beca4429de0e81bcac0c0ac4fe149e625da2)
42
+
43
+ # [v33.0.4](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.0.3...v33.0.4) (2021-06-18 10:09:57)
44
+
45
+ ## Global changes
46
+ ### Patches
47
+
48
+ * [[Fix(cmd_runner)] [#69] Make sure commands are run in unbundled env](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/0795c1ffdcf1b355c9dc91423bb6b7d88918fcf7)
49
+
50
+ ## Changes for cmd_runner
51
+ ### Patches
52
+
53
+ * [[Fix(cmd_runner)] [#69] Make sure commands are run in unbundled env](https://github.com/sweet-delights/hybrid-platforms-conductor/commit/0795c1ffdcf1b355c9dc91423bb6b7d88918fcf7)
54
+
1
55
  # [v33.0.3](https://github.com/sweet-delights/hybrid-platforms-conductor/compare/v33.0.2...v33.0.3) (2021-06-17 12:42:32)
2
56
 
3
57
  ### Patches
@@ -130,18 +130,20 @@ module HybridPlatformsConductor
130
130
  (log_to_stdout ? [@logger_stderr] : []) +
131
131
  (file_output.nil? ? [] : [file_output])
132
132
  ) do
133
- cmd_result = TTY::Command.new(
134
- printer: :null,
135
- pty: true,
136
- timeout: timeout,
137
- uuid: false
138
- ).run!(cmd) do |stdout, stderr|
139
- stdout_queue << stdout if stdout
140
- stderr_queue << stderr if stderr
133
+ Bundler.with_unbundled_env do
134
+ cmd_result = TTY::Command.new(
135
+ printer: :null,
136
+ pty: true,
137
+ timeout: timeout,
138
+ uuid: false
139
+ ).run!(cmd) do |stdout, stderr|
140
+ stdout_queue << stdout if stdout
141
+ stderr_queue << stderr if stderr
142
+ end
143
+ exit_status = cmd_result.exit_status
144
+ cmd_stdout = cmd_result.out
145
+ cmd_stderr = cmd_result.err
141
146
  end
142
- exit_status = cmd_result.exit_status
143
- cmd_stdout = cmd_result.out
144
- cmd_stderr = cmd_result.err
145
147
  end
146
148
  rescue TTY::Command::TimeoutExceeded
147
149
  exit_status = :timeout
@@ -146,6 +146,7 @@ module HybridPlatformsConductor
146
146
  logger: @logger,
147
147
  logger_stderr: @logger_stderr,
148
148
  config: @config,
149
+ cmd_runner: @cmd_runner,
149
150
  nodes_handler: @nodes_handler,
150
151
  actions_executor: @actions_executor
151
152
  )
@@ -50,7 +50,9 @@ module HybridPlatformsConductor
50
50
  # [API] - @stdout_io can be used to send stdout output
51
51
  # [API] - @stderr_io can be used to send stderr output
52
52
  def remote_interactive
53
- system "cd #{workspace_for(@node)} ; /bin/bash"
53
+ Bundler.with_unbundled_env do
54
+ system "cd #{workspace_for(@node)} ; /bin/bash"
55
+ end
54
56
  end
55
57
 
56
58
  # rubocop:disable Lint/UnusedMethodArgument
@@ -73,7 +75,11 @@ module HybridPlatformsConductor
73
75
  def remote_copy(from, to, sudo: false, owner: nil, group: nil)
74
76
  # If the destination is a relative path, prepend the workspace dir to it.
75
77
  to = "#{workspace_for(@node)}/#{to}" unless to.start_with?('/')
76
- FileUtils.cp_r from, to
78
+ if sudo
79
+ run_cmd "#{@nodes_handler.sudo_on(@node)} cp -r \"#{from}\" \"#{to}\""
80
+ else
81
+ FileUtils.cp_r from, to unless @cmd_runner.dry_run
82
+ end
77
83
  end
78
84
  # rubocop:enable Lint/UnusedMethodArgument
79
85
 
@@ -121,7 +121,9 @@ module HybridPlatformsConductor
121
121
  # ...
122
122
  # ===== STDERR =====
123
123
  # ...
124
- if exit_status.is_a?(Symbol)
124
+ if @cmd_runner.dry_run
125
+ { error: 'No log to show in dry-run mode' }
126
+ elsif exit_status.is_a?(Symbol)
125
127
  { error: "Error: #{exit_status}\n#{stderr}" }
126
128
  else
127
129
  stdout_lines = stdout.split("\n")
@@ -74,18 +74,16 @@ module HybridPlatformsConductor
74
74
  # [API] - @cmd_runner is accessible.
75
75
  def setup
76
76
  required_version = YAML.load_file("#{@repository_path}/chef_versions.yml")['workstation']
77
- Bundler.with_unbundled_env do
78
- exit_status, stdout, _stderr = @cmd_runner.run_cmd '/opt/chef-workstation/bin/chef --version', expected_code: [0, :command_error]
79
- existing_version =
80
- if exit_status == :command_error
81
- 'not installed'
82
- else
83
- expected_match = stdout.match(/^Chef Workstation version: (.+)\.\d+$/)
84
- expected_match.nil? ? 'unreadable' : expected_match[1]
85
- end
86
- log_debug "Current Chef version: #{existing_version}. Required 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
88
- end
77
+ exit_status, stdout, _stderr = @cmd_runner.run_cmd '/opt/chef-workstation/bin/chef --version', expected_code: [0, :command_error]
78
+ existing_version =
79
+ if exit_status == :command_error
80
+ 'not installed'
81
+ else
82
+ expected_match = stdout.match(/^Chef Workstation version: (.+)\.\d+$/)
83
+ expected_match.nil? ? 'unreadable' : expected_match[1]
84
+ end
85
+ log_debug "Current Chef version: #{existing_version}. Required version: #{required_version}"
86
+ @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
89
87
  end
90
88
 
91
89
  # Get the list of known nodes.
@@ -160,29 +158,34 @@ module HybridPlatformsConductor
160
158
  current_package_info = File.exist?(package_info_file) ? JSON.parse(File.read(package_info_file)).transform_keys(&:to_sym) : {}
161
159
  next if current_package_info == package_info
162
160
 
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"
177
- # If the policy lock file does not exist, generate it
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}")
179
- extra_cp_data_bags = File.exist?("#{@repository_path}/data_bags") ? " && cp -ar data_bags/ #{package_dir}/" : ''
180
- @cmd_runner.run_cmd "cd #{@repository_path} && \
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}"
161
+ policy_file = "policyfiles/#{service}.rb"
162
+ if local_environment
163
+ local_policy_file = "policyfiles/#{service}.local.rb"
164
+ # In local mode, we always regenerate the lock file as we may modify the run list
165
+ run_list = known_cookbook_paths.any? { |cookbook_path| File.exist?("#{@repository_path}/#{cookbook_path}/hpc_test/recipes/before_run.rb") } ? ['hpc_test::before_run'] : []
166
+ dsl_parser = DslParser.new
167
+ dsl_parser.parse("#{@repository_path}/#{policy_file}")
168
+ run_list.concat dsl_parser.calls.find { |call_info| call_info[:method] == :run_list }[:args].flatten
169
+ 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") }
170
+ File.write("#{@repository_path}/#{local_policy_file}", File.read("#{@repository_path}/#{policy_file}") + "\nrun_list #{run_list.map { |recipe| "'#{recipe}'" }.join(', ')}\n")
171
+ policy_file = local_policy_file
183
172
  end
173
+ lock_file = "#{File.dirname(policy_file)}/#{File.basename(policy_file, '.rb')}.lock.json"
174
+ # If the policy lock file does not exist, generate it
175
+ @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}")
176
+ extra_cp_data_bags = File.exist?("#{@repository_path}/data_bags") ? " && cp -ar data_bags/ #{package_dir}/" : ''
177
+ @cmd_runner.run_cmd "cd #{@repository_path} && \
178
+ #{@cmd_runner.root? ? '' : 'sudo '}rm -rf #{package_dir} && \
179
+ /opt/chef-workstation/bin/chef export #{policy_file} #{package_dir} --chef-license accept#{extra_cp_data_bags}"
184
180
  next if @cmd_runner.dry_run
185
181
 
182
+ # Write the list of gems to be installed for this package
183
+ File.write(
184
+ "#{@repository_path}/#{package_dir}/gems.json",
185
+ Dir.glob("#{@repository_path}/#{package_dir}/cookbook_artifacts/*/metadata.json").map do |metadata|
186
+ JSON.parse(File.read(metadata))['gems']
187
+ end.flatten(1).to_json
188
+ )
186
189
  # Create secrets file
187
190
  secrets_file = "#{@repository_path}/#{package_dir}/data_bags/hpc_secrets/hpc_secrets.json"
188
191
  FileUtils.mkdir_p(File.dirname(secrets_file))
@@ -224,10 +227,13 @@ module HybridPlatformsConductor
224
227
  # * Array< Hash<Symbol,Object> >: List of actions to be done
225
228
  def actions_to_deploy_on(node, service, use_why_run: true)
226
229
  package_dir = "#{@repository_path}/dist/#{@local_env ? 'local' : 'prod'}/#{service}"
230
+ gems_to_install = []
227
231
  # Generate the nodes attributes file
228
232
  unless @cmd_runner.dry_run
229
233
  FileUtils.mkdir_p "#{package_dir}/nodes"
230
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"))
231
237
  end
232
238
  client_options = [
233
239
  '--local-mode',
@@ -237,7 +243,19 @@ module HybridPlatformsConductor
237
243
  client_options << '--why-run' if use_why_run
238
244
  if @nodes_handler.get_use_local_chef_of(node)
239
245
  # Just run the chef-client directly from the packaged repository
240
- [{ bash: "cd #{package_dir} && #{@cmd_runner.root? ? '' : 'sudo '}SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client #{client_options.join(' ')}" }]
246
+ sudo_prefix = @cmd_runner.root? ? '' : 'sudo '
247
+ [
248
+ {
249
+ bash: [
250
+ 'set -e',
251
+ "cd #{package_dir}"
252
+ ] +
253
+ gems_to_install.map { |(gem_name, gem_version)| "#{sudo_prefix}SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef gem install #{gem_name} --version \"#{gem_version}\"" } +
254
+ [
255
+ "#{sudo_prefix}SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client #{client_options.join(' ')}"
256
+ ]
257
+ }
258
+ ]
241
259
  else
242
260
  # Upload the package and run it from the node
243
261
  package_name = File.basename(package_dir)
@@ -265,10 +283,14 @@ module HybridPlatformsConductor
265
283
  scp: { package_dir => './hpc_deploy' },
266
284
  remote_bash: [
267
285
  'set -e',
268
- "cd ./hpc_deploy/#{package_name}",
269
- "#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/bin/chef-client #{client_options.join(' ')}",
270
- 'cd ..'
271
- ] + (log_debug? ? [] : ["#{sudo}rm -rf ./hpc_deploy/#{package_name}"])
286
+ "cd ./hpc_deploy/#{package_name}"
287
+ ] +
288
+ gems_to_install.map { |(gem_name, gem_version)| "#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/embedded/bin/gem install #{gem_name} --version \"#{gem_version}\"" } +
289
+ [
290
+ "#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/bin/chef-client #{client_options.join(' ')}",
291
+ 'cd ..'
292
+ ] +
293
+ (log_debug? ? [] : ["#{sudo}rm -rf ./hpc_deploy/#{package_name}"])
272
294
  }
273
295
  ]
274
296
  end
@@ -12,16 +12,19 @@ module HybridPlatformsConductor
12
12
  # * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
13
13
  # * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
14
14
  # * *config* (Config): Config to be used. [default: Config.new]
15
+ # * *cmd_runner* (CmdRunner): CmdRunner to be used [default: CmdRunner.new]
15
16
  # * *nodes_handler* (NodesHandler): Nodes handler to be used. [default: NodesHandler.new]
16
17
  # * *actions_executor* (ActionsExecutor): Actions executor to be used. [default: ActionsExecutor.new]
17
18
  def initialize(
18
19
  logger: Logger.new($stdout),
19
20
  logger_stderr: Logger.new($stderr),
20
21
  config: Config.new,
22
+ cmd_runner: CmdRunner.new,
21
23
  nodes_handler: NodesHandler.new,
22
24
  actions_executor: ActionsExecutor.new
23
25
  )
24
26
  super(logger: logger, logger_stderr: logger_stderr, config: config)
27
+ @cmd_runner = cmd_runner
25
28
  @nodes_handler = nodes_handler
26
29
  @actions_executor = actions_executor
27
30
  end
@@ -304,7 +304,8 @@ module HybridPlatformsConductor
304
304
 
305
305
  # Define clusters of ips with 24 bits ranges.
306
306
  def define_clusters_ip_24
307
- @nodes_graph.each_key do |node_name|
307
+ # Clone keys as we modify the hash in the loop
308
+ @nodes_graph.keys.clone.each do |node_name|
308
309
  next unless @nodes_graph[node_name][:type] == :node && !@node_metadata[node_name][:private_ips].nil? && !@node_metadata[node_name][:private_ips].empty?
309
310
 
310
311
  ip_24 = "#{@node_metadata[node_name][:private_ips].first.split('.')[0..2].join('.')}.0/24"
@@ -1,5 +1,5 @@
1
1
  module HybridPlatformsConductor
2
2
 
3
- VERSION = '33.0.3'
3
+ VERSION = '33.2.1'
4
4
 
5
5
  end
@@ -87,6 +87,43 @@ describe HybridPlatformsConductor::ActionsExecutor do
87
87
  end
88
88
  end
89
89
 
90
+ it 'does not copy files remotely in dry-run mode' do
91
+ with_test_platform_for_remote_testing do
92
+ test_cmd_runner.dry_run = true
93
+ expect(FileUtils).not_to receive(:cp_r)
94
+ test_connector.remote_copy('/path/to/src.file', '/remote_path/to/dst.dir')
95
+ end
96
+ end
97
+
98
+ it 'copies files remotely with sudo' do
99
+ with_test_platform_for_remote_testing(
100
+ expected_cmds: [
101
+ [
102
+ 'sudo -u root cp -r "/path/to/src.file" "/remote_path/to/dst.dir"',
103
+ proc { [0, '', ''] }
104
+ ]
105
+ ]
106
+ ) do
107
+ test_connector.remote_copy('/path/to/src.file', '/remote_path/to/dst.dir', sudo: true)
108
+ end
109
+ end
110
+
111
+ it 'copies files remotely with a different sudo' do
112
+ with_test_platform_for_remote_testing(
113
+ expected_cmds: [
114
+ [
115
+ 'other_sudo --user root cp -r "/path/to/src.file" "/remote_path/to/dst.dir"',
116
+ proc { [0, '', ''] }
117
+ ]
118
+ ],
119
+ additional_config: <<~'EO_CONFIG'
120
+ sudo_for { |user| "other_sudo --user #{user}" }
121
+ EO_CONFIG
122
+ ) do
123
+ test_connector.remote_copy('/path/to/src.file', '/remote_path/to/dst.dir', sudo: true)
124
+ end
125
+ end
126
+
90
127
  it 'copies files remotely with timeout' do
91
128
  with_test_platform_for_remote_testing(
92
129
  timeout: 5
@@ -103,6 +140,35 @@ describe HybridPlatformsConductor::ActionsExecutor do
103
140
  end
104
141
  end
105
142
 
143
+ it 'copies relative files remotely with sudo' do
144
+ with_test_platform_for_remote_testing(
145
+ expected_cmds: [
146
+ [
147
+ 'sudo -u root cp -r "/path/to/src.file" "/tmp/hpc_local_workspaces/node/to/dst.dir"',
148
+ proc { [0, '', ''] }
149
+ ]
150
+ ]
151
+ ) do
152
+ test_connector.remote_copy('/path/to/src.file', 'to/dst.dir', sudo: true)
153
+ end
154
+ end
155
+
156
+ it 'copies relative files remotely with a different sudo' do
157
+ with_test_platform_for_remote_testing(
158
+ expected_cmds: [
159
+ [
160
+ 'other_sudo --user root cp -r "/path/to/src.file" "/tmp/hpc_local_workspaces/node/to/dst.dir"',
161
+ proc { [0, '', ''] }
162
+ ]
163
+ ],
164
+ additional_config: <<~'EO_CONFIG'
165
+ sudo_for { |user| "other_sudo --user #{user}" }
166
+ EO_CONFIG
167
+ ) do
168
+ test_connector.remote_copy('/path/to/src.file', 'to/dst.dir', sudo: true)
169
+ end
170
+ end
171
+
106
172
  end
107
173
 
108
174
  end
@@ -45,6 +45,12 @@ describe HybridPlatformsConductor::CmdRunner do
45
45
  end
46
46
  end
47
47
 
48
+ it 'runs a command in an un-bundled environment' do
49
+ with_repository do
50
+ expect(test_cmd_runner.run_cmd('echo "${BUNDLE_GEMFILE}"')).to eq [0, "\n", '']
51
+ end
52
+ end
53
+
48
54
  it 'fails when the command does not exit 0' do
49
55
  with_repository do
50
56
  expect { test_cmd_runner.run_cmd 'exit 1' }.to raise_error(HybridPlatformsConductor::CmdRunner::UnexpectedExitCodeError, 'Command \'exit 1\' returned error code 1 (expected 0).')
@@ -12,6 +12,7 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
12
12
  # * *export* (Boolean): Are we expecting the chef export stage? [default: true]
13
13
  # * *data_bags* (Boolean): Do we expect data bags copy? [default: false]
14
14
  # * *env* (String): Expected environment being packaged [default: 'prod']
15
+ # * *cookbook_metadata* (Hash<String, Hash>): JSON metadata to generate for packaged cookbooks [default: {}]
15
16
  # * *block* (Proc): Code called with mock in place
16
17
  def with_packaging_mocked(
17
18
  repository,
@@ -21,6 +22,7 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
21
22
  export: true,
22
23
  data_bags: false,
23
24
  env: 'prod',
25
+ cookbook_metadata: {},
24
26
  &block
25
27
  )
26
28
  with_cmd_runner_mocked(
@@ -49,8 +51,14 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
49
51
  [
50
52
  %r{^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)}/" : ''}$},
51
53
  proc do
52
- FileUtils.mkdir_p "#{repository}/dist/#{env}/#{policy}"
53
- FileUtils.cp_r("#{repository}/data_bags", "#{repository}/dist/#{env}/#{policy}/") if data_bags
54
+ package_dir = "#{repository}/dist/#{env}/#{policy}"
55
+ FileUtils.mkdir_p package_dir
56
+ FileUtils.cp_r("#{repository}/data_bags", "#{package_dir}/") if data_bags
57
+ cookbook_metadata.each do |cookbook, metadata|
58
+ metadata_file = "#{package_dir}/cookbook_artifacts/#{cookbook}/metadata.json"
59
+ FileUtils.mkdir_p File.dirname(metadata_file)
60
+ File.write(metadata_file, metadata.to_json)
61
+ end
54
62
  [0, 'Chef export done', '']
55
63
  end
56
64
  ]
@@ -80,6 +88,9 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
80
88
  with_serverless_chef_platforms('1_node') do |platform, repository|
81
89
  with_packaging_mocked(repository) do
82
90
  platform.package(services: { 'node' => %w[test_policy] }, secrets: {}, local_environment: false)
91
+ gems_file = "#{repository}/dist/prod/test_policy/gems.json"
92
+ expect(File.exist?(gems_file)).to eq true
93
+ expect(JSON.parse(File.read(gems_file))).to eq []
83
94
  end
84
95
  end
85
96
  end
@@ -212,6 +223,41 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
212
223
 
213
224
  end
214
225
 
226
+ context 'with a platform having several cookbooks' do
227
+
228
+ it 'generates the gems info to be installed' do
229
+ with_serverless_chef_platforms('several_cookbooks') do |platform, repository|
230
+ with_packaging_mocked(
231
+ repository,
232
+ policy: 'test_policy_1',
233
+ cookbook_metadata: {
234
+ 'test_cookbook_1' => {
235
+ gems: [
236
+ ['my_gem_1', '0.0.1'],
237
+ ['my_gem_2', '0.0.2']
238
+ ]
239
+ },
240
+ 'dependency_cookbook' => {
241
+ gems: [
242
+ ['my_gem_3', '~> 1.3']
243
+ ]
244
+ }
245
+ }
246
+ ) do
247
+ platform.package(services: { 'node1' => %w[test_policy_1] }, secrets: {}, local_environment: false)
248
+ gems_file = "#{repository}/dist/prod/test_policy_1/gems.json"
249
+ expect(File.exist?(gems_file)).to eq true
250
+ expect(JSON.parse(File.read(gems_file)).sort).to eq [
251
+ ['my_gem_1', '0.0.1'],
252
+ ['my_gem_2', '0.0.2'],
253
+ ['my_gem_3', '~> 1.3']
254
+ ].sort
255
+ end
256
+ end
257
+ end
258
+
259
+ end
260
+
215
261
  context 'with a platform having data bags' do
216
262
 
217
263
  it 'packages data bags' do
@@ -6,9 +6,18 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
6
6
  #
7
7
  # Parameters::
8
8
  # * *repository* (String): The repository we package
9
+ # * *env* (String): The environment for which this repository is packaged [default: 'prod']
9
10
  # * *service* (String): The service being packaged in this repository [default: 'test_policy']
10
- def mock_package(repository, service: 'test_policy')
11
- FileUtils.mkdir_p "#{repository}/dist/prod/#{service}"
11
+ # * *gems* (Array<String, String>): The [<name>, <version>] gems info to be installed as generated by the packaging [default: []]
12
+ def mock_package(
13
+ repository,
14
+ env: 'prod',
15
+ service: 'test_policy',
16
+ gems: []
17
+ )
18
+ package_dir = "#{repository}/dist/#{env}/#{service}"
19
+ FileUtils.mkdir_p package_dir
20
+ File.write("#{package_dir}/gems.json", gems.to_json)
12
21
  end
13
22
 
14
23
  # Get expected actions to deploy a service on a given node
@@ -20,6 +29,7 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
20
29
  # * *env* (String): Environment expected to be packaged [default: 'prod']
21
30
  # * *policy* (String): Expected policy to be packaged [default: 'test_policy']
22
31
  # * *node* (String): Expected node to be deployed [default: 'node']
32
+ # * *gems_install_cmds* (Array<String>): Expected gem install commands [default: []]
23
33
  # Result::
24
34
  # * Array: Expected actions
25
35
  def expected_actions_to_deploy_chef(
@@ -28,7 +38,8 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
28
38
  sudo: 'sudo -u root ',
29
39
  env: 'prod',
30
40
  policy: 'test_policy',
31
- node: 'node'
41
+ node: 'node',
42
+ gems_install_cmds: []
32
43
  )
33
44
  [
34
45
  {
@@ -48,11 +59,14 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
48
59
  scp: { "#{repository}/dist/#{env}/#{policy}" => './hpc_deploy' },
49
60
  remote_bash: [
50
61
  'set -e',
51
- "cd ./hpc_deploy/#{policy}",
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' : ''}",
53
- 'cd ..',
54
- "#{sudo}rm -rf ./hpc_deploy/#{policy}"
55
- ]
62
+ "cd ./hpc_deploy/#{policy}"
63
+ ] +
64
+ gems_install_cmds.map { |gem_install_cmd| "#{sudo}SSL_CERT_DIR=/etc/ssl/certs /opt/chef/embedded/bin/#{gem_install_cmd}" } +
65
+ [
66
+ "#{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' : ''}",
67
+ 'cd ..',
68
+ "#{sudo}rm -rf ./hpc_deploy/#{policy}"
69
+ ]
56
70
  }
57
71
  ]
58
72
  end
@@ -109,6 +123,33 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
109
123
  end
110
124
  end
111
125
 
126
+ it 'returns actions to deploy on this node with gems to be installed' do
127
+ with_serverless_chef_platforms('1_node') do |platform, repository|
128
+ mock_package(
129
+ repository,
130
+ gems: [
131
+ ['my_gem_1', '0.0.1'],
132
+ ['my_gem_2', '0.0.2'],
133
+ ['my_gem_3', '~> 1.3']
134
+ ]
135
+ )
136
+ platform.prepare_for_deploy(
137
+ services: { 'node' => %w[test_policy] },
138
+ secrets: {},
139
+ local_environment: false,
140
+ why_run: false
141
+ )
142
+ expect(platform.actions_to_deploy_on('node', 'test_policy', use_why_run: false)).to eq expected_actions_to_deploy_chef(
143
+ repository,
144
+ gems_install_cmds: [
145
+ 'gem install my_gem_1 --version "0.0.1"',
146
+ 'gem install my_gem_2 --version "0.0.2"',
147
+ 'gem install my_gem_3 --version "~> 1.3"'
148
+ ]
149
+ )
150
+ end
151
+ end
152
+
112
153
  it 'returns actions to deploy on this node with node attributes setup from metadata' do
113
154
  with_serverless_chef_platforms('1_node') do |platform, repository|
114
155
  test_nodes_handler.override_metadata_of 'node', :new_metadata, 'new_value'
@@ -161,7 +202,7 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
161
202
 
162
203
  it 'returns actions to deploy on this node using local mode' do
163
204
  with_serverless_chef_platforms('1_node') do |platform, repository|
164
- mock_package(repository)
205
+ mock_package(repository, env: 'local')
165
206
  platform.prepare_for_deploy(
166
207
  services: { 'node' => %w[test_policy] },
167
208
  secrets: {},
@@ -174,7 +215,7 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
174
215
 
175
216
  it 'returns actions to deploy on this node in why-run mode and local mode' do
176
217
  with_serverless_chef_platforms('1_node') do |platform, repository|
177
- mock_package(repository)
218
+ mock_package(repository, env: 'local')
178
219
  platform.prepare_for_deploy(
179
220
  services: { 'node' => %w[test_policy] },
180
221
  secrets: {},
@@ -219,7 +260,7 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
219
260
 
220
261
  it 'deploys services declared on 1 node on another node if asked' do
221
262
  with_serverless_chef_platforms('several_nodes') do |platform, repository|
222
- mock_package(repository)
263
+ mock_package(repository, service: 'test_policy_1')
223
264
  platform.prepare_for_deploy(
224
265
  services: { 'node2' => %w[test_policy_1] },
225
266
  secrets: {},
@@ -230,9 +271,38 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
230
271
  end
231
272
  end
232
273
 
233
- it 'deploys local nodes' do
274
+ it 'deploys using the local chef workstation when use_local_chef is set' do
234
275
  with_serverless_chef_platforms('several_nodes') do |platform, repository|
235
- mock_package(repository)
276
+ mock_package(repository, service: 'test_policy_1')
277
+ platform.prepare_for_deploy(
278
+ services: { 'local' => %w[test_policy_1] },
279
+ secrets: {},
280
+ local_environment: false,
281
+ why_run: false
282
+ )
283
+ expect(platform.actions_to_deploy_on('local', 'test_policy_1', use_why_run: false)).to eq [
284
+ {
285
+ bash: [
286
+ 'set -e',
287
+ "cd #{repository}/dist/prod/test_policy_1",
288
+ 'sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client --local-mode --chef-license accept --json-attributes nodes/local.json'
289
+ ]
290
+ }
291
+ ]
292
+ end
293
+ end
294
+
295
+ it 'deploys using the local chef workstation with gems to be installed when use_local_chef is set' do
296
+ with_serverless_chef_platforms('several_nodes') do |platform, repository|
297
+ mock_package(
298
+ repository,
299
+ service: 'test_policy_1',
300
+ gems: [
301
+ ['my_gem_1', '0.0.1'],
302
+ ['my_gem_2', '0.0.2'],
303
+ ['my_gem_3', '~> 1.3']
304
+ ]
305
+ )
236
306
  platform.prepare_for_deploy(
237
307
  services: { 'local' => %w[test_policy_1] },
238
308
  secrets: {},
@@ -241,7 +311,14 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
241
311
  )
242
312
  expect(platform.actions_to_deploy_on('local', 'test_policy_1', use_why_run: false)).to eq [
243
313
  {
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"
314
+ bash: [
315
+ 'set -e',
316
+ "cd #{repository}/dist/prod/test_policy_1",
317
+ 'sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef gem install my_gem_1 --version "0.0.1"',
318
+ 'sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef gem install my_gem_2 --version "0.0.2"',
319
+ 'sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef gem install my_gem_3 --version "~> 1.3"',
320
+ 'sudo SSL_CERT_DIR=/etc/ssl/certs /opt/chef-workstation/bin/chef-client --local-mode --chef-license accept --json-attributes nodes/local.json'
321
+ ]
245
322
  }
246
323
  ]
247
324
  end
@@ -254,7 +331,7 @@ describe HybridPlatformsConductor::HpcPlugins::PlatformHandler::ServerlessChef d
254
331
  it 'deploys a service on a node belonging to another platform' do
255
332
  with_serverless_chef_platforms({ 'p1' => '1_node', 'p2' => 'several_nodes' }) do |repositories|
256
333
  platform_1, repository_1 = repositories.find { |platform, _repository| platform.name == 'p1' }
257
- mock_package(repository_1)
334
+ mock_package(repository_1, service: 'test_policy_1')
258
335
  platform_1.prepare_for_deploy(
259
336
  services: { 'node2' => %w[test_policy_1] },
260
337
  secrets: {},
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: 33.0.3
4
+ version: 33.2.1
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-06-17 00:00:00.000000000 Z
11
+ date: 2021-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: range_operators