hybrid_platforms_conductor 33.0.3 → 33.2.1

Sign up to get free protection for your applications and to get access to all the features.
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