hybrid_platforms_conductor 32.17.1 → 32.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +3 -0
  4. data/bin/last_deploys +4 -1
  5. data/bin/nodes_to_deploy +5 -5
  6. data/docs/config_dsl.md +22 -0
  7. data/docs/gen/mermaid/README.md-0.png +0 -0
  8. data/docs/gen/mermaid/docs/executables/check-node.md-0.png +0 -0
  9. data/docs/gen/mermaid/docs/executables/deploy.md-0.png +0 -0
  10. data/docs/gen/mermaid/docs/executables/free_ips.md-0.png +0 -0
  11. data/docs/gen/mermaid/docs/executables/get_impacted_nodes.md-0.png +0 -0
  12. data/docs/gen/mermaid/docs/executables/last_deploys.md-0.png +0 -0
  13. data/docs/gen/mermaid/docs/executables/nodes_to_deploy.md-0.png +0 -0
  14. data/docs/gen/mermaid/docs/executables/report.md-0.png +0 -0
  15. data/docs/gen/mermaid/docs/executables/run.md-0.png +0 -0
  16. data/docs/gen/mermaid/docs/executables/ssh_config.md-0.png +0 -0
  17. data/docs/gen/mermaid/docs/executables/test.md-0.png +0 -0
  18. data/docs/plugins.md +25 -0
  19. data/docs/plugins/log/remote_fs.md +26 -0
  20. data/lib/hybrid_platforms_conductor/actions_executor.rb +8 -1
  21. data/lib/hybrid_platforms_conductor/deployer.rb +96 -104
  22. data/lib/hybrid_platforms_conductor/hpc_plugins/log/my_log_plugin.rb.sample +100 -0
  23. data/lib/hybrid_platforms_conductor/hpc_plugins/log/remote_fs.rb +179 -0
  24. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +7 -20
  25. data/lib/hybrid_platforms_conductor/log.rb +31 -0
  26. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  27. data/spec/hybrid_platforms_conductor_test.rb +22 -6
  28. data/spec/hybrid_platforms_conductor_test/api/deployer/config_dsl_spec.rb +24 -4
  29. data/spec/hybrid_platforms_conductor_test/api/deployer/deploy_spec.rb +187 -212
  30. data/spec/hybrid_platforms_conductor_test/api/deployer/log_plugins/remote_fs_spec.rb +223 -0
  31. data/spec/hybrid_platforms_conductor_test/api/tests_runner/global_spec.rb +1 -1
  32. data/spec/hybrid_platforms_conductor_test/executables/last_deploys_spec.rb +146 -98
  33. data/spec/hybrid_platforms_conductor_test/executables/nodes_to_deploy_spec.rb +240 -83
  34. data/spec/hybrid_platforms_conductor_test/executables/options/common_spec.rb +2 -1
  35. data/spec/hybrid_platforms_conductor_test/helpers/deployer_helpers.rb +40 -53
  36. data/spec/hybrid_platforms_conductor_test/helpers/deployer_test_helpers.rb +2 -2
  37. data/spec/hybrid_platforms_conductor_test/test_log_no_read_plugin.rb +82 -0
  38. data/spec/hybrid_platforms_conductor_test/test_log_plugin.rb +103 -0
  39. metadata +10 -2
@@ -0,0 +1,100 @@
1
+ require 'hybrid_platforms_conductor/log'
2
+
3
+ module HybridPlatformsConductor
4
+
5
+ module HpcPlugins
6
+
7
+ module Log
8
+
9
+ # Save logs on the remote node's file system
10
+ class RemoteFs < HybridPlatformsConductor::Log
11
+
12
+ # Get actions to save logs
13
+ # [API] - This method is mandatory.
14
+ # [API] - The following API components are accessible:
15
+ # * *@config* (Config): Main configuration API.
16
+ # * *@nodes_handler* (NodesHandler): Nodes handler API.
17
+ # * *@actions_executor* (ActionsExecutor): Actions executor API.
18
+ #
19
+ # Parameters::
20
+ # * *node* (String): Node for which logs are being saved
21
+ # * *services* (Array<String>): The list of services that have been deployed on this node
22
+ # * *deployment_info* (Hash<Symbol,Object>): Additional information to attach to the logs
23
+ # * *exit_status* (Integer or Symbol): Exit status of the deployment
24
+ # * *stdout* (String): Deployment's stdout
25
+ # * *stderr* (String): Deployment's stderr
26
+ # Result::
27
+ # * Array< Hash<Symbol,Object> >: List of actions to be done
28
+ def actions_to_save_logs(node, services, deployment_info, exit_status, stdout, stderr)
29
+ # Return here all actions that are to be run to save those logs.
30
+ [
31
+ {
32
+ bash: <<~EOS
33
+ cat <<'EOLOGFILE' >>all_logs.txt
34
+ Deployment log on #{node}:
35
+ #{stdout}
36
+ EOLOGFILE
37
+ EOS
38
+ }
39
+ ]
40
+ end
41
+
42
+ # Get actions to read logs.
43
+ # If provided, this method can return some actions to be executed that will fetch logs from servers or remote nodes.
44
+ # By using this method to run actions instead of the synchronous method logs_from, such actions will be run in parallel which can greatly improve time-consuming operations when querying a lot of nodes.
45
+ # [API] - This method is optional.
46
+ # [API] - The following API components are accessible:
47
+ # * *@config* (Config): Main configuration API.
48
+ # * *@nodes_handler* (NodesHandler): Nodes handler API.
49
+ # * *@actions_executor* (ActionsExecutor): Actions executor API.
50
+ #
51
+ # Parameters::
52
+ # * *node* (String): Node for which deployment logs are being read
53
+ # Result::
54
+ # * Array< Hash<Symbol,Object> >: List of actions to be done
55
+ def actions_to_read_logs(node)
56
+ [
57
+ { bash: 'cat all_logs.txt' }
58
+ ]
59
+ end
60
+
61
+ # Get deployment logs from a node.
62
+ # This method can use the result of actions previously run to read logs, as returned by the actions_to_read_logs method.
63
+ # [API] - This method is mandatory.
64
+ # [API] - The following API components are accessible:
65
+ # * *@config* (Config): Main configuration API.
66
+ # * *@nodes_handler* (NodesHandler): Nodes handler API.
67
+ # * *@actions_executor* (ActionsExecutor): Actions executor API.
68
+ #
69
+ # Parameters::
70
+ # * *node* (String): The node we want deployment logs from
71
+ # * *exit_status* (Integer, Symbol or nil): Exit status of actions to read logs, or nil if no action was returned by actions_to_read_logs
72
+ # * *stdout* (String or nil): stdout of actions to read logs, or nil if no action was returned by actions_to_read_logs
73
+ # * *stderr* (String or nil): stderr of actions to read logs, or nil if no action was returned by actions_to_read_logs
74
+ # Result::
75
+ # * Hash<Symbol,Object>: Deployment log information:
76
+ # * *error* (String): Error string in case deployment logs could not be retrieved. If set then further properties will be ignored. [optional]
77
+ # * *services* (Array<String>): List of services deployed on the node
78
+ # * *deployment_info* (Hash<Symbol,Object>): Deployment metadata
79
+ # * *exit_status* (Integer or Symbol): Deployment exit status
80
+ # * *stdout* (String): Deployment stdout
81
+ # * *stderr* (String): Deployment stderr
82
+ def logs_for(node, exit_status, stdout, stderr)
83
+ # Here we should retrieve this info from somewhere.
84
+ # Based on this example, stdout is the value of the execution of the action 'cat all_logs.txt' as returned by actions_to_read_logs.
85
+ {
86
+ services: %w[unknown],
87
+ deployment_info: {},
88
+ exit_status: 0,
89
+ stdout: stdout,
90
+ stderr: ''
91
+ }
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+
100
+ end
@@ -0,0 +1,179 @@
1
+ require 'hybrid_platforms_conductor/log'
2
+
3
+ module HybridPlatformsConductor
4
+
5
+ module HpcPlugins
6
+
7
+ module Log
8
+
9
+ # Save logs on the remote node's file system
10
+ class RemoteFs < HybridPlatformsConductor::Log
11
+
12
+ MARKER_STDOUT = '===== STDOUT ====='
13
+ MARKER_STDERR = '===== STDERR ====='
14
+
15
+ # Get actions to save logs
16
+ # [API] - This method is mandatory.
17
+ # [API] - The following API components are accessible:
18
+ # * *@config* (Config): Main configuration API.
19
+ # * *@nodes_handler* (NodesHandler): Nodes handler API.
20
+ # * *@actions_executor* (ActionsExecutor): Actions executor API.
21
+ #
22
+ # Parameters::
23
+ # * *node* (String): Node for which logs are being saved
24
+ # * *services* (Array<String>): The list of services that have been deployed on this node
25
+ # * *deployment_info* (Hash<Symbol,Object>): Additional information to attach to the logs
26
+ # * *exit_status* (Integer or Symbol): Exit status of the deployment
27
+ # * *stdout* (String): Deployment's stdout
28
+ # * *stderr* (String): Deployment's stderr
29
+ # Result::
30
+ # * Array< Hash<Symbol,Object> >: List of actions to be done
31
+ def actions_to_save_logs(node, services, deployment_info, exit_status, stdout, stderr)
32
+ # Create a log file to be scp with all relevant info
33
+ ssh_user = @actions_executor.connector(:ssh).ssh_user
34
+ sudo_prefix = ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} "
35
+ log_file = "#{Dir.tmpdir}/hpc_deploy_logs/#{node}_#{Time.now.utc.strftime('%F_%H%M%S')}_#{ssh_user}"
36
+ [
37
+ {
38
+ ruby: proc do
39
+ FileUtils.mkdir_p File.dirname(log_file)
40
+ File.write(log_file, <<~EOS)
41
+ #{
42
+ deployment_info.merge(
43
+ debug: log_debug? ? 'Yes' : 'No',
44
+ services: services.join(', '),
45
+ exit_status: exit_status
46
+ ).map { |property, value| "#{property}: #{value}" }.join("\n")
47
+ }
48
+ #{MARKER_STDOUT}
49
+ #{stdout}
50
+ #{MARKER_STDERR}
51
+ #{stderr}
52
+ EOS
53
+ end,
54
+ remote_bash: "#{sudo_prefix}mkdir -p /var/log/deployments && #{sudo_prefix}chmod 600 /var/log/deployments"
55
+ },
56
+ {
57
+ scp: {
58
+ log_file => '/var/log/deployments',
59
+ :sudo => ssh_user != 'root',
60
+ :owner => 'root',
61
+ :group => 'root'
62
+ }
63
+ },
64
+ {
65
+ remote_bash: "#{sudo_prefix}chmod 600 /var/log/deployments/#{File.basename(log_file)}",
66
+ # Remove temporary files storing logs for security
67
+ ruby: proc do
68
+ File.unlink(log_file)
69
+ end
70
+ }
71
+ ]
72
+ end
73
+
74
+ # Get actions to read logs.
75
+ # If provided, this method can return some actions to be executed that will fetch logs from servers or remote nodes.
76
+ # By using this method to run actions instead of the synchronous method logs_from, such actions will be run in parallel which can greatly improve time-consuming operations when querying a lot of nodes.
77
+ # [API] - This method is optional.
78
+ # [API] - The following API components are accessible:
79
+ # * *@config* (Config): Main configuration API.
80
+ # * *@nodes_handler* (NodesHandler): Nodes handler API.
81
+ # * *@actions_executor* (ActionsExecutor): Actions executor API.
82
+ #
83
+ # Parameters::
84
+ # * *node* (String): Node for which deployment logs are being read
85
+ # Result::
86
+ # * Array< Hash<Symbol,Object> >: List of actions to be done
87
+ def actions_to_read_logs(node)
88
+ sudo_prefix = @actions_executor.connector(:ssh).ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} "
89
+ [
90
+ { remote_bash: "#{sudo_prefix}cat /var/log/deployments/`#{sudo_prefix}ls -t /var/log/deployments/ | head -1`" }
91
+ ]
92
+ end
93
+
94
+ # Get deployment logs from a node.
95
+ # This method can use the result of actions previously run to read logs, as returned by the actions_to_read_logs method.
96
+ # [API] - This method is mandatory.
97
+ # [API] - The following API components are accessible:
98
+ # * *@config* (Config): Main configuration API.
99
+ # * *@nodes_handler* (NodesHandler): Nodes handler API.
100
+ # * *@actions_executor* (ActionsExecutor): Actions executor API.
101
+ #
102
+ # Parameters::
103
+ # * *node* (String): The node we want deployment logs from
104
+ # * *exit_status* (Integer, Symbol or nil): Exit status of actions to read logs, or nil if no action was returned by actions_to_read_logs
105
+ # * *stdout* (String or nil): stdout of actions to read logs, or nil if no action was returned by actions_to_read_logs
106
+ # * *stderr* (String or nil): stderr of actions to read logs, or nil if no action was returned by actions_to_read_logs
107
+ # Result::
108
+ # * Hash<Symbol,Object>: Deployment log information:
109
+ # * *error* (String): Error string in case deployment logs could not be retrieved. If set then further properties will be ignored. [optional]
110
+ # * *services* (Array<String>): List of services deployed on the node
111
+ # * *deployment_info* (Hash<Symbol,Object>): Deployment metadata
112
+ # * *exit_status* (Integer or Symbol): Deployment exit status
113
+ # * *stdout* (String): Deployment stdout
114
+ # * *stderr* (String): Deployment stderr
115
+ def logs_for(node, exit_status, stdout, stderr)
116
+ # Expected format for stdout:
117
+ # Property1: Value1
118
+ # ...
119
+ # PropertyN: ValueN
120
+ # ===== STDOUT =====
121
+ # ...
122
+ # ===== STDERR =====
123
+ # ...
124
+ if exit_status.is_a?(Symbol)
125
+ { error: "Error: #{exit_status}\n#{stderr}" }
126
+ else
127
+ stdout_lines = stdout.split("\n")
128
+ if stdout_lines.first =~ /No such file or directory/
129
+ { error: '/var/log/deployments missing' }
130
+ else
131
+ stdout_idx = stdout_lines.index(MARKER_STDOUT)
132
+ stderr_idx = stdout_lines.index(MARKER_STDERR)
133
+ deploy_info = {}
134
+ stdout_lines[0..stdout_idx - 1].each do |line|
135
+ if line =~ /^([^:]+): (.+)$/
136
+ key_str, value = $1, $2
137
+ key = key_str.to_sym
138
+ # Type-cast some values
139
+ case key_str
140
+ when 'date'
141
+ # Date and time values
142
+ # Thu Nov 23 18:43:01 UTC 2017
143
+ deploy_info[key] = Time.parse("#{value} UTC")
144
+ when 'debug'
145
+ # Boolean values
146
+ # Yes
147
+ deploy_info[key] = (value == 'Yes')
148
+ when /^diff_files_.+$/, 'services'
149
+ # Array of strings
150
+ # my_file.txt, other_file.txt
151
+ deploy_info[key] = value.split(', ')
152
+ else
153
+ deploy_info[key] = value
154
+ end
155
+ else
156
+ deploy_info[:unknown_lines] = [] unless deploy_info.key?(:unknown_lines)
157
+ deploy_info[:unknown_lines] << line
158
+ end
159
+ end
160
+ services = deploy_info.delete(:services)
161
+ exit_status = deploy_info.delete(:exit_status)
162
+ {
163
+ services: services,
164
+ deployment_info: deploy_info,
165
+ exit_status: exit_status =~ /^\d+$/ ? Integer(exit_status) : exit_status.to_sym,
166
+ stdout: stdout_lines[stdout_idx + 1..stderr_idx - 1].join("\n"),
167
+ stderr: stdout_lines[stderr_idx + 1..-1].join("\n")
168
+ }
169
+ end
170
+ end
171
+ end
172
+
173
+ end
174
+
175
+ end
176
+
177
+ end
178
+
179
+ end
@@ -12,27 +12,14 @@ module HybridPlatformsConductor
12
12
  MAX_ACCEPTABLE_REFRESH_PERIOD_SECS = 3 * 31 * 24 * 60 * 60 # 3 months
13
13
 
14
14
  # Check my_test_plugin.rb.sample documentation for signature details.
15
- def test_on_node
15
+ def test_for_node
16
16
  now = Time.now
17
- {
18
- "#{@nodes_handler.sudo_on(@node)} ls -t /var/log/deployments" => proc do |stdout|
19
- if stdout.empty?
20
- error 'Node has never been deployed using deploy (/var/log/deployments is empty)'
21
- elsif stdout.first =~ /No such file or directory/
22
- error 'Node has never been deployed using deploy (/var/log/deployments does not exist)'
23
- else
24
- # Expecting following file names
25
- # node-name_2017-12-01_093418_user-name
26
- file_match = stdout.first.match(/^#{Regexp.escape(@node)}_(\d{4}-\d{2}-\d{2})_.+$/)
27
- if file_match.nil?
28
- error "Invalid chef deployment log file found: #{stdout.first}"
29
- else
30
- last_deploy_time = Time.parse(file_match[1])
31
- error "Last deployment has been done on #{last_deploy_time.strftime('%F')}. Should refresh it." if now - last_deploy_time > MAX_ACCEPTABLE_REFRESH_PERIOD_SECS
32
- end
33
- end
34
- end
35
- }
17
+ deploy_info = @deployer.deployment_info_from(@node)[@node]
18
+ if deploy_info.key?(:error)
19
+ error "Error while getting deployment info: #{deploy_info[:error]}"
20
+ elsif Time.now.utc - deploy_info[:deployment_info][:date] > MAX_ACCEPTABLE_REFRESH_PERIOD_SECS
21
+ error "Last deployment has been done on #{deploy_info[:deployment_info][:date].strftime('%F')}. Should refresh it."
22
+ end
36
23
  end
37
24
 
38
25
  end
@@ -0,0 +1,31 @@
1
+ require 'hybrid_platforms_conductor/logger_helpers'
2
+ require 'hybrid_platforms_conductor/plugin'
3
+
4
+ module HybridPlatformsConductor
5
+
6
+ # Ancestor of all log plugins
7
+ class Log < Plugin
8
+
9
+ # Constructor
10
+ #
11
+ # Parameters::
12
+ # * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
13
+ # * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
14
+ # * *config* (Config): Config to be used. [default: Config.new]
15
+ # * *nodes_handler* (NodesHandler): Nodes handler to be used. [default: NodesHandler.new]
16
+ # * *actions_executor* (ActionsExecutor): Actions executor to be used. [default: ActionsExecutor.new]
17
+ def initialize(
18
+ logger: Logger.new(STDOUT),
19
+ logger_stderr: Logger.new(STDERR),
20
+ config: Config.new,
21
+ nodes_handler: NodesHandler.new,
22
+ actions_executor: ActionsExecutor.new
23
+ )
24
+ super(logger: logger, logger_stderr: logger_stderr, config: config)
25
+ @nodes_handler = nodes_handler
26
+ @actions_executor = actions_executor
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -1,5 +1,5 @@
1
1
  module HybridPlatformsConductor
2
2
 
3
- VERSION = '32.17.1'
3
+ VERSION = '32.18.0'
4
4
 
5
5
  end
@@ -5,6 +5,7 @@ require 'hybrid_platforms_conductor/platforms_handler'
5
5
  require 'hybrid_platforms_conductor/actions_executor'
6
6
  require 'hybrid_platforms_conductor/cmd_runner'
7
7
  require 'hybrid_platforms_conductor/deployer'
8
+ require 'hybrid_platforms_conductor/log'
8
9
  require 'hybrid_platforms_conductor/nodes_handler'
9
10
  require 'hybrid_platforms_conductor/platform_handler'
10
11
  require 'hybrid_platforms_conductor/provisioner'
@@ -43,6 +44,8 @@ require 'hybrid_platforms_conductor_test/platform_handler_plugins/test2'
43
44
  require 'hybrid_platforms_conductor_test/report_plugin'
44
45
  require 'hybrid_platforms_conductor_test/test_action'
45
46
  require 'hybrid_platforms_conductor_test/test_connector'
47
+ require 'hybrid_platforms_conductor_test/test_log_plugin'
48
+ require 'hybrid_platforms_conductor_test/test_log_no_read_plugin'
46
49
  require 'hybrid_platforms_conductor_test/test_plugins/global'
47
50
  require 'hybrid_platforms_conductor_test/test_plugins/node'
48
51
  require 'hybrid_platforms_conductor_test/test_plugins/node_check'
@@ -128,16 +131,29 @@ module HybridPlatformsConductorTest
128
131
  HybridPlatformsConductorTest::TestPlugins::NodeCheck.only_on_platform_types = nil
129
132
  HybridPlatformsConductorTest::TestPlugins::NodeCheck.only_on_nodes = nil
130
133
  HybridPlatformsConductorTest::TestPlugins::SeveralChecks.runs = []
134
+ HybridPlatformsConductorTest::TestLogPlugin.calls = []
135
+ HybridPlatformsConductorTest::TestLogNoReadPlugin.calls = []
131
136
  FileUtils.rm_rf './run_logs'
132
137
  FileUtils.rm_rf './testadmin.key.pub'
133
138
  FileUtils.rm_rf '/tmp/hpc_ssh'
139
+ register_plugins(
140
+ :log,
141
+ {
142
+ test_log: HybridPlatformsConductorTest::TestLogPlugin,
143
+ test_log_no_read: HybridPlatformsConductorTest::TestLogNoReadPlugin
144
+ },
145
+ replace: false
146
+ )
134
147
  # Make sure CMDB plugin classes loaded by test framework are not added automatically
135
- register_plugins(:cmdb, {
136
- config: HybridPlatformsConductor::HpcPlugins::Cmdb::Config,
137
- host_ip: HybridPlatformsConductor::HpcPlugins::Cmdb::HostIp,
138
- host_keys: HybridPlatformsConductor::HpcPlugins::Cmdb::HostKeys,
139
- platform_handlers: HybridPlatformsConductor::HpcPlugins::Cmdb::PlatformHandlers
140
- })
148
+ register_plugins(
149
+ :cmdb,
150
+ {
151
+ config: HybridPlatformsConductor::HpcPlugins::Cmdb::Config,
152
+ host_ip: HybridPlatformsConductor::HpcPlugins::Cmdb::HostIp,
153
+ host_keys: HybridPlatformsConductor::HpcPlugins::Cmdb::HostKeys,
154
+ platform_handlers: HybridPlatformsConductor::HpcPlugins::Cmdb::PlatformHandlers
155
+ }
156
+ )
141
157
  end
142
158
  end
143
159
 
@@ -3,10 +3,30 @@ describe HybridPlatformsConductor::Deployer do
3
3
  context 'checking deployer specific config DSL' do
4
4
 
5
5
  it 'declares a packaging timeout' do
6
- with_repository do |repository|
7
- with_platforms('packaging_timeout 666') do
8
- expect(test_config.packaging_timeout_secs).to eq 666
9
- end
6
+ with_platforms('packaging_timeout 666') do
7
+ expect(test_config.packaging_timeout_secs).to eq 666
8
+ end
9
+ end
10
+
11
+ it 'declares log plugins to be used' do
12
+ with_test_platforms(
13
+ { nodes: { 'node1' => {}, 'node2' => {} } },
14
+ false,
15
+ <<~EOS
16
+ send_logs_to %i[log_plugin_1 log_plugin_2]
17
+ for_nodes('node2') { send_logs_to :log_plugin_3 }
18
+ EOS
19
+ ) do
20
+ expect(test_config.deployment_logs).to eq [
21
+ {
22
+ nodes_selectors_stack: [],
23
+ log_plugins: %i[log_plugin_1 log_plugin_2]
24
+ },
25
+ {
26
+ nodes_selectors_stack: %w[node2],
27
+ log_plugins: %i[log_plugin_3]
28
+ }
29
+ ]
10
30
  end
11
31
  end
12
32
 
@@ -4,238 +4,213 @@ describe HybridPlatformsConductor::Deployer do
4
4
 
5
5
  deploy_specs_for(check_mode: false)
6
6
 
7
- # Expect the test services handler to be called to deploy a given list of services
8
- #
9
- # Parameters::
10
- # * *services* (Hash<String, Array<String> >): List of services to be expected, per node name
11
- def expect_services_handler_to_deploy(services)
12
- expect(test_services_handler).to receive(:deploy_allowed?).with(
13
- services: services,
14
- secrets: {},
15
- local_environment: false
16
- ) do
17
- nil
18
- end
19
- expect(test_services_handler).to receive(:package).with(
20
- services: services,
21
- secrets: {},
22
- local_environment: false
23
- )
24
- expect(test_services_handler).to receive(:prepare_for_deploy).with(
25
- services: services,
26
- secrets: {},
27
- local_environment: false,
28
- why_run: false
29
- )
30
- services.each do |node, services|
31
- expect(test_services_handler).to receive(:actions_to_deploy_on).with(node, services, false) do
32
- [{ bash: "echo \"Deploying on #{node}\"" }]
7
+ context 'checking log plugins usage' do
8
+
9
+ # Prepare the test platform with test log plugins
10
+ #
11
+ # Parameters::
12
+ # * *platforms_info* (Hash): The platforms info [default = {}]
13
+ # * *as_git* (Boolean): As a git repository? [default = false]
14
+ # * *additional_config* (String): Additional config [default = 'send_logs_to :test_log']
15
+ def with_test_platform_for_deploy_tests(platforms_info = {}, as_git = false, additional_config = 'send_logs_to :test_log')
16
+ with_test_platform(platforms_info, false, additional_config) do
17
+ yield
33
18
  end
34
- expect(test_services_handler).to receive(:log_info_for).with(node, services) do
35
- {
36
- repo_name_0: 'platform',
37
- commit_id_0: '123456',
38
- commit_message_0: "Test commit for #{node}: #{services.join(', ')}"
39
- }
19
+ end
20
+
21
+ it 'deploys correct logs information on 1 node' do
22
+ with_test_platform_for_deploy_tests({ nodes: { 'node' => { services: %w[service1 service2] } } }, true) do
23
+ with_connections_mocked_on ['node'] do
24
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
25
+ expect_services_handler_to_deploy('node' => %w[service1 service2])
26
+ expect_actions_executor_runs([
27
+ # First run, we expect the mutex to be setup, and the deployment actions to be run
28
+ proc { |actions_per_nodes| expect_actions_to_deploy_on(actions_per_nodes, 'node') },
29
+ # Second run, we expect the mutex to be released
30
+ proc { |actions_per_nodes| expect_actions_to_unlock(actions_per_nodes, 'node') },
31
+ # Third run, we expect logs to be uploaded on the node
32
+ proc { |actions_per_nodes| expect(actions_per_nodes).to eq('node' => [{ bash: 'echo Save test logs to node' }]) }
33
+ ])
34
+ expect(test_deployer.deploy_on('node')).to eq('node' => [0, 'Deploy successful', ''])
35
+ expect(HybridPlatformsConductorTest::TestLogPlugin.calls).to eq [
36
+ {
37
+ method: :actions_to_save_logs,
38
+ node: 'node',
39
+ services: %w[service1 service2],
40
+ deployment_info: {
41
+ repo_name_0: 'platform',
42
+ commit_id_0: '123456',
43
+ commit_message_0: 'Test commit for node: service1, service2',
44
+ user: 'test_user'
45
+ },
46
+ exit_status: 0,
47
+ stdout: 'Deploy successful',
48
+ stderr: ''
49
+ }
50
+ ]
51
+ end
40
52
  end
41
53
  end
42
- end
43
54
 
44
- it 'deploys correct logs information on 1 node' do
45
- with_test_platform({ nodes: { 'node' => { services: %w[service1 service2] } } }, true) do
46
- with_connections_mocked_on ['node'] do
47
- test_actions_executor.connector(:ssh).ssh_user = 'test_user'
48
- expect_services_handler_to_deploy('node' => %w[service1 service2])
49
- expect_actions_executor_runs([
50
- # First run, we expect the mutex to be setup, and the deployment actions to be run
51
- proc { |actions_per_nodes| expect_actions_to_deploy_on(actions_per_nodes, 'node') },
52
- # Second run, we expect the mutex to be released
53
- proc { |actions_per_nodes| expect_actions_to_unlock(actions_per_nodes, 'node') },
54
- # Third run, we expect logs to be uploaded on the node
55
- proc do |actions_per_nodes|
56
- # Check logs content
57
- local_log_file = actions_per_nodes['node'][:scp].first[0]
58
- expect(File.exist?(local_log_file)).to eq true
59
- expect_logs_to_be(File.read(local_log_file), 'Deploy successful', '',
60
- date: /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/,
61
- user: 'test_user',
62
- debug: 'No',
63
- repo_name_0: 'platform',
64
- commit_id_0: '123456',
65
- commit_message_0: 'Test commit for node: service1, service2',
66
- services: 'service1, service2',
67
- exit_status: '0'
68
- )
69
- expect_actions_to_upload_logs(actions_per_nodes, 'node')
70
- end
71
- ])
72
- expect(test_deployer.deploy_on('node')).to eq('node' => [0, 'Deploy successful', ''])
55
+ it 'deploys correct logs information on several nodes' do
56
+ with_test_platform_for_deploy_tests({ nodes: {
57
+ 'node1' => { services: %w[service1] },
58
+ 'node2' => { services: %w[service2] }
59
+ } }, true) do
60
+ with_connections_mocked_on %w[node1 node2] do
61
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
62
+ expect_services_handler_to_deploy(
63
+ 'node1' => %w[service1],
64
+ 'node2' => %w[service2]
65
+ )
66
+ expect_actions_executor_runs([
67
+ # First run, we expect the mutex to be setup, and the deployment actions to be run
68
+ proc { |actions_per_nodes| expect_actions_to_deploy_on(actions_per_nodes, %w[node1 node2]) },
69
+ # Second run, we expect the mutex to be released
70
+ proc { |actions_per_nodes| expect_actions_to_unlock(actions_per_nodes, %w[node1 node2]) },
71
+ # Third run, we expect logs to be uploaded on the node
72
+ proc do |actions_per_nodes|
73
+ expect(actions_per_nodes).to eq(
74
+ 'node1' => [{ bash: 'echo Save test logs to node1' }],
75
+ 'node2' => [{ bash: 'echo Save test logs to node2' }]
76
+ )
77
+ end
78
+ ])
79
+ expect(test_deployer.deploy_on(%w[node1 node2])).to eq(
80
+ 'node1' => [0, 'Deploy successful', ''],
81
+ 'node2' => [0, 'Deploy successful', '']
82
+ )
83
+ expect(HybridPlatformsConductorTest::TestLogPlugin.calls).to eq [
84
+ {
85
+ method: :actions_to_save_logs,
86
+ node: 'node1',
87
+ services: %w[service1],
88
+ deployment_info: {
89
+ repo_name_0: 'platform',
90
+ commit_id_0: '123456',
91
+ commit_message_0: 'Test commit for node1: service1',
92
+ user: 'test_user'
93
+ },
94
+ exit_status: 0,
95
+ stdout: 'Deploy successful',
96
+ stderr: ''
97
+ },
98
+ {
99
+ method: :actions_to_save_logs,
100
+ node: 'node2',
101
+ services: %w[service2],
102
+ deployment_info: {
103
+ repo_name_0: 'platform',
104
+ commit_id_0: '123456',
105
+ commit_message_0: 'Test commit for node2: service2',
106
+ user: 'test_user'
107
+ },
108
+ exit_status: 0,
109
+ stdout: 'Deploy successful',
110
+ stderr: ''
111
+ }
112
+ ]
113
+ end
73
114
  end
74
115
  end
75
- end
76
116
 
77
- it 'deploys correct logs information on several nodes' do
78
- with_test_platform({ nodes: {
79
- 'node1' => { services: %w[service1] },
80
- 'node2' => { services: %w[service2] }
81
- } }, true) do
82
- with_connections_mocked_on %w[node1 node2] do
83
- test_actions_executor.connector(:ssh).ssh_user = 'test_user'
84
- expect_services_handler_to_deploy(
85
- 'node1' => %w[service1],
86
- 'node2' => %w[service2]
87
- )
88
- expect_actions_executor_runs([
89
- # First run, we expect the mutex to be setup, and the deployment actions to be run
90
- proc { |actions_per_nodes| expect_actions_to_deploy_on(actions_per_nodes, %w[node1 node2]) },
91
- # Second run, we expect the mutex to be released
92
- proc { |actions_per_nodes| expect_actions_to_unlock(actions_per_nodes, %w[node1 node2]) },
93
- # Third run, we expect logs to be uploaded on the node
94
- proc do |actions_per_nodes|
95
- # Check logs content
96
- local_log_file1 = actions_per_nodes['node1'][:scp].first[0]
97
- expect(File.exist?(local_log_file1)).to eq true
98
- expect_logs_to_be(File.read(local_log_file1), 'Deploy successful', '',
99
- date: /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/,
100
- user: 'test_user',
101
- debug: 'No',
102
- repo_name_0: 'platform',
103
- commit_id_0: '123456',
104
- commit_message_0: 'Test commit for node1: service1',
105
- services: 'service1',
106
- exit_status: '0'
107
- )
108
- local_log_file2 = actions_per_nodes['node2'][:scp].first[0]
109
- expect(File.exist?(local_log_file2)).to eq true
110
- expect_logs_to_be(File.read(local_log_file2), 'Deploy successful', '',
111
- date: /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/,
112
- user: 'test_user',
113
- debug: 'No',
114
- repo_name_0: 'platform',
115
- commit_id_0: '123456',
116
- commit_message_0: 'Test commit for node2: service2',
117
- services: 'service2',
118
- exit_status: '0'
119
- )
120
- expect_actions_to_upload_logs(actions_per_nodes, %w[node1 node2])
121
- end
122
- ])
123
- expect(test_deployer.deploy_on(%w[node1 node2])).to eq(
124
- 'node1' => [0, 'Deploy successful', ''],
125
- 'node2' => [0, 'Deploy successful', '']
126
- )
117
+ it 'deploys correct logs information on 1 node even when there is a failing deploy' do
118
+ with_test_platform_for_deploy_tests({ nodes: { 'node' => { services: %w[service1 service2] } } }, true) do
119
+ with_connections_mocked_on ['node'] do
120
+ test_actions_executor.connector(:ssh).ssh_user = 'test_user'
121
+ expect_services_handler_to_deploy('node' => %w[service1 service2])
122
+ expect_actions_executor_runs([
123
+ # First run, we expect the mutex to be setup, and the deployment actions to be run
124
+ proc do |actions_per_nodes|
125
+ expect_actions_to_deploy_on(
126
+ actions_per_nodes,
127
+ 'node',
128
+ mocked_result: { 'node' => [:failed_action, 'Failed deploy stdout', 'Failed deploy stderr'] }
129
+ )
130
+ end,
131
+ # Second run, we expect the mutex to be released
132
+ proc { |actions_per_nodes| expect_actions_to_unlock(actions_per_nodes, 'node') },
133
+ # Third run, we expect logs to be uploaded on the node
134
+ proc { |actions_per_nodes| expect(actions_per_nodes).to eq('node' => [{ bash: 'echo Save test logs to node' }]) }
135
+ ])
136
+ expect(test_deployer.deploy_on('node')).to eq('node' => [:failed_action, 'Failed deploy stdout', 'Failed deploy stderr'])
137
+ expect(HybridPlatformsConductorTest::TestLogPlugin.calls).to eq [
138
+ {
139
+ method: :actions_to_save_logs,
140
+ node: 'node',
141
+ services: %w[service1 service2],
142
+ deployment_info: {
143
+ repo_name_0: 'platform',
144
+ commit_id_0: '123456',
145
+ commit_message_0: 'Test commit for node: service1, service2',
146
+ user: 'test_user'
147
+ },
148
+ exit_status: :failed_action,
149
+ stdout: 'Failed deploy stdout',
150
+ stderr: 'Failed deploy stderr'
151
+ }
152
+ ]
153
+ end
127
154
  end
128
155
  end
129
- end
130
156
 
131
- it 'deploys correct logs information on 1 node even when there is a failing deploy' do
132
- with_test_platform({ nodes: { 'node' => { services: %w[service1 service2] } } }, true) do
133
- with_connections_mocked_on ['node'] do
134
- test_actions_executor.connector(:ssh).ssh_user = 'test_user'
135
- expect_services_handler_to_deploy('node' => %w[service1 service2])
157
+ it 'gets deployment info from log plugins' do
158
+ with_test_platform_for_deploy_tests({ nodes: { 'node' => {} } }) do |repository|
136
159
  expect_actions_executor_runs([
137
- # First run, we expect the mutex to be setup, and the deployment actions to be run
160
+ # Expect the actions to get log files
138
161
  proc do |actions_per_nodes|
139
- expect_actions_to_deploy_on(
140
- actions_per_nodes,
141
- 'node',
142
- mocked_result: { 'node' => [:failed_action, "Failed deploy stdout\n", "Failed deploy stderr\n"] }
143
- )
144
- end,
145
- # Second run, we expect the mutex to be released
146
- proc { |actions_per_nodes| expect_actions_to_unlock(actions_per_nodes, 'node') },
147
- # Third run, we expect logs to be uploaded on the node
148
- proc do |actions_per_nodes|
149
- # Check logs content
150
- local_log_file = actions_per_nodes['node'][:scp].first[0]
151
- expect(File.exist?(local_log_file)).to eq true
152
- expect_logs_to_be(File.read(local_log_file), "Failed deploy stdout\n", 'Failed deploy stderr',
153
- date: /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/,
154
- user: 'test_user',
155
- debug: 'No',
156
- repo_name_0: 'platform',
157
- commit_id_0: '123456',
158
- commit_message_0: 'Test commit for node: service1, service2',
159
- services: 'service1, service2',
160
- exit_status: 'failed_action'
161
- )
162
- expect_actions_to_upload_logs(actions_per_nodes, 'node')
162
+ expect(actions_per_nodes).to eq('node' => [{ bash: 'echo Read logs for node' }])
163
+ { 'node' => [42, 'Log files read stdout', 'Log files read stderr'] }
163
164
  end
164
165
  ])
165
- expect(test_deployer.deploy_on('node')).to eq('node' => [:failed_action, "Failed deploy stdout\n", "Failed deploy stderr\n"])
166
+ expect(test_deployer.deployment_info_from('node')).to eq(
167
+ 'node' => {
168
+ deployment_info: { user: 'test_user' },
169
+ exit_status: 666,
170
+ services: %w[unknown],
171
+ stderr: 'Deployment test stderr',
172
+ stdout: 'Deployment test stdout'
173
+ }
174
+ )
175
+ expect(HybridPlatformsConductorTest::TestLogPlugin.calls).to eq [
176
+ {
177
+ method: :actions_to_read_logs,
178
+ node: 'node'
179
+ },
180
+ {
181
+ method: :logs_for,
182
+ node: 'node',
183
+ exit_status: 42,
184
+ stdout: 'Log files read stdout',
185
+ stderr: 'Log files read stderr'
186
+ }
187
+ ]
166
188
  end
167
189
  end
168
- end
169
-
170
- it 'gets deployment info from log files' do
171
- with_test_platform({ nodes: { 'node' => {} } }) do |repository|
172
- expect_actions_executor_runs([
173
- # Expect the actions to get log files
174
- proc do |actions_per_nodes|
175
- expect(actions_per_nodes).to eq('node' => { remote_bash: 'cd /var/log/deployments && ls -t | head -1 | xargs sed \'/===== STDOUT =====/q\'' })
176
- { 'node' => [0, "Property1: Value1\nProperty2: Value2", ''] }
177
- end
178
- ])
179
- expect(test_deployer.deployment_info_from('node')).to eq(
180
- 'node' => {
181
- Property1: 'Value1',
182
- Property2: 'Value2'
183
- }
184
- )
185
- end
186
- end
187
190
 
188
- it 'gets deployment info with some properties converted from log files' do
189
- with_test_platform({ nodes: { 'node' => {} } }) do |repository|
190
- expect_actions_executor_runs([
191
- # Expect the actions to get log files
192
- proc do |actions_per_nodes|
193
- expect(actions_per_nodes).to eq('node' => { remote_bash: 'cd /var/log/deployments && ls -t | head -1 | xargs sed \'/===== STDOUT =====/q\'' })
194
- { 'node' => [0, <<~EOS, ''] }
195
- date: Thu Nov 23 18:43:01 UTC 2017
196
- debug: Yes
197
- diff_files_0: file1, file2, file3
198
- services: service1, service2, service3
199
- EOS
200
- end
201
- ])
202
- expect(test_deployer.deployment_info_from('node')).to eq(
203
- 'node' => {
204
- date: Time.parse('2017-11-23 18:43:01 UTC'),
205
- debug: true,
206
- diff_files_0: %w[file1 file2 file3],
207
- services: %w[service1 service2 service3]
208
- }
209
- )
210
- end
211
- end
212
-
213
- it 'gets deployment info from several log files' do
214
- with_test_platform({ nodes: { 'node1' => {}, 'node2' => {} } }) do |repository|
215
- expect_actions_executor_runs([
216
- # Expect the actions to get log files
217
- proc do |actions_per_nodes|
218
- expect(actions_per_nodes).to eq(
219
- 'node1' => { remote_bash: 'cd /var/log/deployments && ls -t | head -1 | xargs sed \'/===== STDOUT =====/q\'' },
220
- 'node2' => { remote_bash: 'cd /var/log/deployments && ls -t | head -1 | xargs sed \'/===== STDOUT =====/q\'' }
221
- )
191
+ it 'gets deployment info from log plugins not having actions_to_read_logs' do
192
+ with_test_platform_for_deploy_tests({ nodes: { 'node' => {} } }, false, 'send_logs_to :test_log_no_read') do |repository|
193
+ expect(test_deployer.deployment_info_from('node')).to eq(
194
+ 'node' => {
195
+ deployment_info: { user: 'test_user' },
196
+ exit_status: 666,
197
+ services: %w[unknown],
198
+ stderr: 'Deployment test stderr',
199
+ stdout: 'Deployment test stdout'
200
+ }
201
+ )
202
+ expect(HybridPlatformsConductorTest::TestLogNoReadPlugin.calls).to eq [
222
203
  {
223
- 'node1' => [0, "Property1: Value1\nProperty2: Value2", ''],
224
- 'node2' => [0, "Property3: Value3\nProperty4: Value4", '']
204
+ method: :logs_for,
205
+ node: 'node',
206
+ exit_status: nil,
207
+ stdout: nil,
208
+ stderr: nil
225
209
  }
226
- end
227
- ])
228
- expect(test_deployer.deployment_info_from(%w[node1 node2])).to eq(
229
- 'node1' => {
230
- Property1: 'Value1',
231
- Property2: 'Value2'
232
- },
233
- 'node2' => {
234
- Property3: 'Value3',
235
- Property4: 'Value4'
236
- }
237
- )
210
+ ]
211
+ end
238
212
  end
213
+
239
214
  end
240
215
 
241
216
  end