cem_acpt 0.10.3 → 0.10.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/cem_acpt/actions.rb +7 -2
- data/lib/cem_acpt/bolt/cmd/output.rb +1 -0
- data/lib/cem_acpt/bolt/tests.rb +3 -3
- data/lib/cem_acpt/bolt/yaml_file.rb +6 -0
- data/lib/cem_acpt/goss/api/action_response.rb +5 -3
- data/lib/cem_acpt/goss/api.rb +22 -52
- data/lib/cem_acpt/logging.rb +2 -2
- data/lib/cem_acpt/provision/terraform/linux.rb +7 -0
- data/lib/cem_acpt/test_runner/log_formatter/bolt_summary_results_formatter.rb +36 -19
- data/lib/cem_acpt/test_runner/test_results.rb +8 -8
- data/lib/cem_acpt/test_runner.rb +28 -30
- data/lib/cem_acpt/utils/puppet.rb +50 -16
- data/lib/cem_acpt/utils.rb +2 -1
- data/lib/cem_acpt/version.rb +1 -1
- data/lib/terraform/gcp/linux/log_service/log_service.rb +62 -0
- data/lib/terraform/gcp/linux/systemd/goss-acpt.service +1 -1
- data/lib/terraform/gcp/linux/systemd/goss-idempotent.service +1 -1
- data/lib/terraform/gcp/linux/systemd/goss-noop.service +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc299de4494abd1da48700f76b43223cb05aae1cb8c26235757e31e909bd6c2f
|
4
|
+
data.tar.gz: 32948e52808bd436695d69cf0c0bf2c5ccd31ada90ed401341c9c73e00653c6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a916fec7be6263243deeb60eac717ac097e0c477359b8abe71c374ef68880ab1608ff48859339074d97e8ae4592b9da491b5ff76b61f842a60712a9efcd1f75
|
7
|
+
data.tar.gz: e4e3b465b224f8f1431867ae19b6bee5161a91248168fd8e0c8c221c6ffa2c6caf3e2bf45a2a54e139e02069e45bc1dc7beacbddbe7fa11c15bf269811c98c54
|
data/Gemfile.lock
CHANGED
data/lib/cem_acpt/actions.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'async'
|
4
4
|
require 'async/barrier'
|
5
5
|
require 'async/http/internet'
|
6
|
+
require_relative 'logging'
|
6
7
|
|
7
8
|
module CemAcpt
|
8
9
|
# Provides a way to register actions to be executed if the registered
|
@@ -12,7 +13,7 @@ module CemAcpt
|
|
12
13
|
class Action
|
13
14
|
attr_reader :name, :order
|
14
15
|
|
15
|
-
def initialize(name, order
|
16
|
+
def initialize(name, order: 0, &block)
|
16
17
|
@name = name.to_sym
|
17
18
|
@order = order
|
18
19
|
@block = block
|
@@ -42,7 +43,7 @@ module CemAcpt
|
|
42
43
|
# order of the associated action groups' execution order. Defaults to 0.
|
43
44
|
# @param block [Proc] the block to be executed
|
44
45
|
def register_action(name, order: 0, &block)
|
45
|
-
new_action = Action.new(name, order, &block)
|
46
|
+
new_action = Action.new(name, order: order, &block)
|
46
47
|
@actions << new_action
|
47
48
|
sort!
|
48
49
|
self # return self to allow chaining
|
@@ -119,6 +120,8 @@ module CemAcpt
|
|
119
120
|
class << self
|
120
121
|
attr_reader :config
|
121
122
|
|
123
|
+
include CemAcpt::Logging
|
124
|
+
|
122
125
|
# Configures the Actions module.
|
123
126
|
# @param world_config [CemAcpt::Config::Base] the current config for the "world"
|
124
127
|
# @yield [CemAcpt::Actions::ActionConfig] the config object for the Actions module
|
@@ -134,12 +137,14 @@ module CemAcpt
|
|
134
137
|
# @option opts [Hash] :context the context to be passed to the actions
|
135
138
|
# @return [Array] the results of the actions
|
136
139
|
def execute(**opts)
|
140
|
+
logger.debug('CemAcpt::Actions') { "Executing registered actions with opts: #{opts}" }
|
137
141
|
context = opts[:context] || {}
|
138
142
|
ordered_groups = config.groups.values.sort_by(&:order)
|
139
143
|
ordered_groups.each_with_object([]) do |group, results|
|
140
144
|
actions = group.filter_actions
|
141
145
|
next if actions.empty?
|
142
146
|
|
147
|
+
logger.debug('CemAcpt::Actions') { "Executing action group: #{group.name}" }
|
143
148
|
context[:group] = group.name
|
144
149
|
context[:actions] = actions.map(&:name)
|
145
150
|
if group.async
|
@@ -127,6 +127,7 @@ module CemAcpt
|
|
127
127
|
end
|
128
128
|
ensure
|
129
129
|
@items = (@cmd_output['items'] || []).map { |item| OutputItem.new(item) }
|
130
|
+
@error_obj ||= @items.find(&:error?)&.error
|
130
131
|
if @items.empty? && @error_obj.nil? && @strict
|
131
132
|
err = RuntimeError.new("Cannot set results, no error or items found for cmd_output:\n#{cmd_output}")
|
132
133
|
@error_obj = err
|
data/lib/cem_acpt/bolt/tests.rb
CHANGED
@@ -158,11 +158,11 @@ module CemAcpt
|
|
158
158
|
end
|
159
159
|
|
160
160
|
def error?
|
161
|
-
@command_result.
|
161
|
+
@command_result.error?
|
162
162
|
end
|
163
163
|
|
164
164
|
def error
|
165
|
-
@command_result if error?
|
165
|
+
@command_result.error if error?
|
166
166
|
end
|
167
167
|
|
168
168
|
def to_h
|
@@ -378,7 +378,7 @@ module CemAcpt
|
|
378
378
|
def load_test_data(run_data)
|
379
379
|
run_data[:test_data].each_with_object({}) do |tdata, h|
|
380
380
|
test_name = tdata[:test_name]
|
381
|
-
next if h.key?(test_name)
|
381
|
+
next if h.key?(test_name)
|
382
382
|
|
383
383
|
logger.debug('CemAcpt::Bolt::Tests') { "Loading test data for test #{test_name}" }
|
384
384
|
begin
|
@@ -53,6 +53,8 @@ module CemAcpt
|
|
53
53
|
return false unless File.exist?(path)
|
54
54
|
|
55
55
|
disk_contents = CemAcpt::Utils::Files.read(path, log_prefix: self.class.to_s)
|
56
|
+
return false unless disk_contents.is_a?(Hash)
|
57
|
+
|
56
58
|
@hash.sort.to_h == disk_contents.sort.to_h
|
57
59
|
end
|
58
60
|
|
@@ -60,6 +62,8 @@ module CemAcpt
|
|
60
62
|
return false unless File.exist?(path)
|
61
63
|
|
62
64
|
disk_contents = CemAcpt::Utils::Files.read(path, log_prefix: self.class.to_s)
|
65
|
+
return false unless disk_contents.is_a?(Hash)
|
66
|
+
|
63
67
|
@hash.sort.to_h >= disk_contents.sort.to_h
|
64
68
|
end
|
65
69
|
|
@@ -67,6 +71,8 @@ module CemAcpt
|
|
67
71
|
return false unless File.exist?(path)
|
68
72
|
|
69
73
|
disk_contents = CemAcpt::Utils::Files.read(path, log_prefix: self.class.to_s)
|
74
|
+
return false unless disk_contents.is_a?(Hash)
|
75
|
+
|
70
76
|
@hash.sort.to_h <= disk_contents.sort.to_h
|
71
77
|
end
|
72
78
|
end
|
@@ -4,13 +4,14 @@ module CemAcpt
|
|
4
4
|
module Goss
|
5
5
|
module Api
|
6
6
|
class ActionResponse
|
7
|
-
attr_reader :host, :action, :body
|
7
|
+
attr_reader :host, :action, :body, :metadata
|
8
8
|
|
9
9
|
def initialize(host, action, status, body)
|
10
10
|
@host = host
|
11
11
|
@action = action
|
12
12
|
@status = status
|
13
13
|
@body = body
|
14
|
+
@metadata = {}
|
14
15
|
end
|
15
16
|
|
16
17
|
def to_s
|
@@ -27,6 +28,7 @@ module CemAcpt
|
|
27
28
|
action: action,
|
28
29
|
status: @status,
|
29
30
|
body: @body,
|
31
|
+
metadata: @metadata,
|
30
32
|
}
|
31
33
|
end
|
32
34
|
|
@@ -40,11 +42,11 @@ module CemAcpt
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def error
|
43
|
-
results.find(&:error)
|
45
|
+
results.find(&:error)
|
44
46
|
end
|
45
47
|
|
46
48
|
def error?
|
47
|
-
!success?
|
49
|
+
!success? && !error.nil?
|
48
50
|
end
|
49
51
|
|
50
52
|
def results
|
data/lib/cem_acpt/goss/api.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'async'
|
4
|
-
require 'async/barrier'
|
5
|
-
require 'async/http/internet'
|
6
3
|
require 'json'
|
7
4
|
require_relative 'api/action_response'
|
8
5
|
require_relative '../logging'
|
@@ -31,64 +28,37 @@ module CemAcpt
|
|
31
28
|
URI("http://#{host}:#{ACTIONS[action.to_sym]}")
|
32
29
|
end
|
33
30
|
|
34
|
-
#
|
35
|
-
# @param
|
36
|
-
# @
|
37
|
-
#
|
38
|
-
# @
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
raise ArgumentError, 'actions must be an Array' unless actions.is_a?(Array)
|
48
|
-
|
49
|
-
actions.map!(&:to_sym)
|
50
|
-
actions.select! { |action| ACTIONS.key?(action) }
|
51
|
-
logger.info('CemAcpt::Goss::Api') do
|
52
|
-
"Running test actions #{actions.join(', ')} against #{hosts.size} host(s)..."
|
53
|
-
end
|
54
|
-
task = Async do
|
55
|
-
internet = Async::HTTP::Internet.new
|
56
|
-
barrier = Async::Barrier.new
|
57
|
-
barrier.async do
|
58
|
-
hosts.each do |host|
|
59
|
-
actions.each do |action|
|
60
|
-
results << run_action(internet, host, action)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
barrier.wait
|
65
|
-
ensure
|
66
|
-
internet&.close
|
31
|
+
# Get the JSON response from the specified URI.
|
32
|
+
# @param uri [URI] The URI to get the JSON response from.
|
33
|
+
# @param internet [nil, Async::HTTP::Internet] The object to use for the request. If nil,
|
34
|
+
# Net::HTTP will be used.
|
35
|
+
# @return [Array] The status code and the parsed JSON response body.
|
36
|
+
def get(uri, internet = nil)
|
37
|
+
if internet.nil?
|
38
|
+
require 'net/http'
|
39
|
+
response = Net::HTTP.get_response(uri)
|
40
|
+
[response.code, JSON.parse(response.body)]
|
41
|
+
else
|
42
|
+
response = internet.get(uri.to_s)
|
43
|
+
[response.status, JSON.parse(response.read)]
|
67
44
|
end
|
68
|
-
task.wait
|
69
|
-
logger.info('CemAcpt::Goss::Api') { 'Finished running test actions, returning results...' }
|
70
|
-
results
|
71
|
-
end
|
72
|
-
|
73
|
-
def run_action_async(context = {})
|
74
|
-
context[:results] ||= Queue.new
|
75
|
-
context[:hosts].each do |host|
|
76
|
-
context[:results] << run_action(context[:internet], host, context[:action])
|
77
|
-
end
|
78
|
-
context[:results]
|
79
45
|
end
|
80
46
|
|
81
47
|
# Run the specified action against the specified host.
|
82
|
-
# @param internet [Async::HTTP::Internet] The Async::HTTP::Internet object to use for the request.
|
83
48
|
# @param host [String] The host to run the action against. This should be
|
84
49
|
# a public IP address or a DNS-resolvable name.
|
85
50
|
# @param action [Symbol] The action to run.
|
51
|
+
# @param internet [nil, Async::HTTP::Internet] The object to use for the request.
|
86
52
|
# @return [ActionResponse] The response from the action.
|
87
|
-
def run_action(
|
53
|
+
def run_action(host, action, internet = nil)
|
88
54
|
uri = action_uri(host, action)
|
89
|
-
|
90
|
-
|
91
|
-
|
55
|
+
status_code, body = get(uri, internet)
|
56
|
+
ActionResponse.new(host, action, status_code, body)
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_run_logs(host, internet = nil)
|
60
|
+
status_code, body = get(URI("http://#{host}:8083/run-logs"), internet)
|
61
|
+
ActionResponse.new(host, :run_logs, status_code, body)
|
92
62
|
end
|
93
63
|
end
|
94
64
|
end
|
data/lib/cem_acpt/logging.rb
CHANGED
@@ -198,7 +198,7 @@ module CemAcpt
|
|
198
198
|
else
|
199
199
|
severity
|
200
200
|
end
|
201
|
-
block_message =
|
201
|
+
block_message = block ? yield : nil
|
202
202
|
base = [progname, block_message].compact.join(': ')
|
203
203
|
return if base.empty?
|
204
204
|
|
@@ -272,7 +272,7 @@ module CemAcpt
|
|
272
272
|
|
273
273
|
@log_config = {
|
274
274
|
logdev: $stdout,
|
275
|
-
shift_age:
|
275
|
+
shift_age: 0,
|
276
276
|
shift_size: 1_048_576,
|
277
277
|
level: ::Logger::INFO,
|
278
278
|
progname: 'CemAcpt',
|
@@ -30,6 +30,9 @@ module CemAcpt
|
|
30
30
|
commands = [
|
31
31
|
"sudo /opt/puppetlabs/puppet/bin/puppet module install #{destination_provision_directory}/#{remote_module_package_name}",
|
32
32
|
'curl -fsSL https://goss.rocks/install | sudo sh',
|
33
|
+
'sudo /opt/puppetlabs/puppet/bin/gem install webrick',
|
34
|
+
'sudo chmod +x /opt/cem_acpt/log_service/log_service.rb',
|
35
|
+
'sudo /opt/cem_acpt/log_service/log_service.rb',
|
33
36
|
]
|
34
37
|
unless systemd_files.empty?
|
35
38
|
systemd_files.each do |file|
|
@@ -54,6 +57,10 @@ module CemAcpt
|
|
54
57
|
]
|
55
58
|
commands = (commands << provision_commands).flatten
|
56
59
|
commands
|
60
|
+
elsif image_name.include?('ubuntu')
|
61
|
+
commands = ['sudo apt purge -y unattended-upgrades', 'sudo apt-get update -y']
|
62
|
+
commands = (commands << provision_commands).flatten
|
63
|
+
commands
|
57
64
|
else
|
58
65
|
provision_commands
|
59
66
|
end
|
@@ -26,25 +26,7 @@ module CemAcpt
|
|
26
26
|
super(response)
|
27
27
|
log_subject.each_with_object([]) do |res, ary|
|
28
28
|
res.results.each do |r|
|
29
|
-
|
30
|
-
"#{success_str(r.success?).capitalize}: #{r.name}",
|
31
|
-
"action: #{r.action}",
|
32
|
-
"target: #{name_from_ip(r.target)}",
|
33
|
-
"object: #{r.object}",
|
34
|
-
"status: #{r.status}",
|
35
|
-
]
|
36
|
-
parts = [
|
37
|
-
header.join(', '),
|
38
|
-
"validation results:\n#{JSON.pretty_generate(r.failed_validation_results)}",
|
39
|
-
]
|
40
|
-
if CemAcpt::Logging.verbose?
|
41
|
-
parts << "command result:\n#{JSON.pretty_generate(r.command_result.to_h)}"
|
42
|
-
end
|
43
|
-
parts << r.error if r.error?
|
44
|
-
if r.respond_to?(:details) && !r.details&.empty?
|
45
|
-
parts << "details:\n#{JSON.pretty_generate(r.details)}\n"
|
46
|
-
end
|
47
|
-
ary << parts.join("\n")
|
29
|
+
ary << (r.error? ? format_error_result(r) : format_result(r))
|
48
30
|
end
|
49
31
|
end
|
50
32
|
end
|
@@ -58,6 +40,41 @@ module CemAcpt
|
|
58
40
|
super(response)
|
59
41
|
'Bolt tests'
|
60
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def result_header(r)
|
47
|
+
[
|
48
|
+
"#{success_str(r.success?).capitalize}: #{r.name}",
|
49
|
+
"action: #{r.action}",
|
50
|
+
"target: #{name_from_ip(r.target)}",
|
51
|
+
"object: #{r.object}",
|
52
|
+
"status: #{r.status}",
|
53
|
+
].join(', ')
|
54
|
+
end
|
55
|
+
|
56
|
+
def format_result(r)
|
57
|
+
parts = [
|
58
|
+
result_header(r),
|
59
|
+
"validation results:\n#{JSON.pretty_generate(r.failed_validation_results)}",
|
60
|
+
]
|
61
|
+
if CemAcpt::Logging.verbose?
|
62
|
+
parts << "command result:\n#{JSON.pretty_generate(r.command_result.to_h)}"
|
63
|
+
end
|
64
|
+
if r.respond_to?(:details) && !r.details&.empty?
|
65
|
+
parts << "details:\n#{JSON.pretty_generate(r.details)}\n"
|
66
|
+
end
|
67
|
+
parts.join("\n")
|
68
|
+
end
|
69
|
+
|
70
|
+
def format_error_result(r)
|
71
|
+
[
|
72
|
+
result_header(r),
|
73
|
+
"error: #{r.error.kind}",
|
74
|
+
"issue code: #{r.error.issue_code}",
|
75
|
+
r.error.msg,
|
76
|
+
].join("\n")
|
77
|
+
end
|
61
78
|
end
|
62
79
|
end
|
63
80
|
end
|
@@ -28,14 +28,14 @@ module CemAcpt
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def <<(result)
|
31
|
-
case result
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
@results_queue << case result
|
32
|
+
when TestActionResult, TestErrorActionResult
|
33
|
+
result
|
34
|
+
when StandardError
|
35
|
+
TestErrorActionResult.new(result, new_formatter(result))
|
36
|
+
else
|
37
|
+
TestActionResult.new(result, new_formatter(result))
|
38
|
+
end
|
39
39
|
end
|
40
40
|
|
41
41
|
def to_a
|
data/lib/cem_acpt/test_runner.rb
CHANGED
@@ -28,6 +28,7 @@ module CemAcpt
|
|
28
28
|
|
29
29
|
def initialize(config)
|
30
30
|
@config = config
|
31
|
+
@module_builder = CemAcpt::Utils::Puppet::ModulePackageBuilder.new(config.get('module_dir'))
|
31
32
|
@run_data = {}
|
32
33
|
@duration = 0
|
33
34
|
@exit_code = 0
|
@@ -101,8 +102,7 @@ module CemAcpt
|
|
101
102
|
destroy_test_nodes
|
102
103
|
end
|
103
104
|
rescue StandardError => e
|
104
|
-
logger.verbose('CemAcpt::TestRunner') { "Error cleaning up: #{e}" }
|
105
|
-
logger.verbose('CemAcpt::TestRunner') { e.backtrace.join("\n") }
|
105
|
+
logger.verbose('CemAcpt::TestRunner') { "Error cleaning up: #{e}\n#{e.backtrace.join("\n")}" }
|
106
106
|
ensure
|
107
107
|
logger.end_ci_group
|
108
108
|
end
|
@@ -118,16 +118,20 @@ module CemAcpt
|
|
118
118
|
c.register_group(:goss, order: 0, async: true)
|
119
119
|
CemAcpt::Goss::Api::ACTIONS.each_key do |a|
|
120
120
|
c[:goss].register_action(a) do |context|
|
121
|
-
|
121
|
+
context[:hosts] = @hosts
|
122
|
+
context[:results] = @results
|
123
|
+
context[:hosts].each do |host|
|
124
|
+
action_res = CemAcpt::Goss::Api.run_action(host, context[:action], context[:internet])
|
125
|
+
action_res.metadata[:run_logs] = CemAcpt::Goss::Api.get_run_logs(host, context[:internet]).body
|
126
|
+
context[:results] << action_res
|
127
|
+
end
|
128
|
+
context[:results]
|
122
129
|
end
|
123
130
|
end
|
124
131
|
c.register_group(:bolt, order: 1).register_action(:bolt) do |context|
|
125
132
|
run_bolt_tests(context)
|
126
133
|
end
|
127
134
|
end
|
128
|
-
logger.debug('CemAcpt::TestRunner') { "All actions #{CemAcpt::Actions.config.action_names}" }
|
129
|
-
logger.debug('CemAcpt::TestRunner') { "Only actions: #{CemAcpt::Actions.config.only}" }
|
130
|
-
logger.debug('CemAcpt::TestRunner') { "Except actions: #{CemAcpt::Actions.config.except}" }
|
131
135
|
logger.info('CemAcpt::TestRunner') do
|
132
136
|
"Configured and registered actions, will run actions: #{CemAcpt::Actions.config.action_names.join(', ')}"
|
133
137
|
end
|
@@ -135,13 +139,7 @@ module CemAcpt
|
|
135
139
|
|
136
140
|
# @return [String] The path to the module package
|
137
141
|
def build_module_package
|
138
|
-
|
139
|
-
CemAcpt::Utils.package_win_module(config.get('module_dir'))
|
140
|
-
else
|
141
|
-
CemAcpt::Utils::Puppet.build_module_package(config.get('module_dir'))
|
142
|
-
end
|
143
|
-
logger.info('CemAcpt::TestRunner') { "Created module package: #{pkg_path}..." }
|
144
|
-
pkg_path
|
142
|
+
@module_builder.build
|
145
143
|
end
|
146
144
|
|
147
145
|
# @return [Array<String>] The paths to the ssh private key, public key, and known hosts file
|
@@ -188,7 +186,7 @@ module CemAcpt
|
|
188
186
|
@run_data[:nodes] = new_node_data
|
189
187
|
logger.verbose('CemAcpt::TestRunner') { "Initial run data:\n#{@run_data}" }
|
190
188
|
logger.info('CemAcpt::TestRunner') { 'Created initial run data...' }
|
191
|
-
setup_bolt if CemAcpt::Actions.config.action_names.include?(
|
189
|
+
setup_bolt if CemAcpt::Actions.config.action_names.include?(:bolt)
|
192
190
|
end
|
193
191
|
|
194
192
|
def setup_bolt
|
@@ -199,12 +197,12 @@ module CemAcpt
|
|
199
197
|
rescue CemAcpt::ShellCommandNotFoundError => e
|
200
198
|
logger.warning('CemAcpt::TestRunner') { e.message }
|
201
199
|
logger.warning('CemAcpt::TestRunner') { 'Adding Bolt action to ignore list...' }
|
202
|
-
CemAcpt::Actions.config.ignore <<
|
200
|
+
CemAcpt::Actions.config.ignore << :bolt
|
203
201
|
return
|
204
202
|
end
|
205
203
|
return unless @bolt_test_runner.tests.to_a.empty?
|
206
204
|
|
207
|
-
if !CemAcpt::Actions.config.only.empty? && CemAcpt::Actions.config.only.include?(
|
205
|
+
if !CemAcpt::Actions.config.only.empty? && CemAcpt::Actions.config.only.include?(:bolt)
|
208
206
|
raise 'No Bolt tests to run and only bolt action was specified'
|
209
207
|
end
|
210
208
|
|
@@ -261,20 +259,6 @@ module CemAcpt
|
|
261
259
|
CemAcpt::Actions.execute
|
262
260
|
end
|
263
261
|
|
264
|
-
def run_goss_tests(context = {})
|
265
|
-
logger.info('CemAcpt::TestRunner') { 'Running goss tests...' }
|
266
|
-
context[:hosts] = @hosts
|
267
|
-
context[:results] = @results
|
268
|
-
CemAcpt::Goss::Api.run_actions_async(context)
|
269
|
-
end
|
270
|
-
|
271
|
-
def run_goss_test(context = {})
|
272
|
-
logger.info('CemAcpt::TestRunner') { "Running Goss test for action #{context[:action]}..." }
|
273
|
-
context[:hosts] = @hosts
|
274
|
-
context[:results] = @results
|
275
|
-
CemAcpt::Goss::Api.run_action_async(context)
|
276
|
-
end
|
277
|
-
|
278
262
|
def run_bolt_tests(_context = {})
|
279
263
|
logger.info('CemAcpt::TestRunner') { 'Running Bolt tests...' }
|
280
264
|
# If the Bolt config has tests:only or tests:ignore lists, we need to filter the hosts
|
@@ -364,6 +348,20 @@ module CemAcpt
|
|
364
348
|
logger.info { r }
|
365
349
|
else
|
366
350
|
logger.error { r }
|
351
|
+
next unless result.respond_to?(:metadata) && result.metadata.key?(:run_logs)
|
352
|
+
|
353
|
+
logs = if config.debug? && !config.get('puppet.no_debug')
|
354
|
+
result.metadata[:run_logs]&.dup
|
355
|
+
else
|
356
|
+
filtered = result.metadata[:run_logs]&.dup
|
357
|
+
filtered&.each { |_, v| v.reject! { |l| l.include?('(debug)') } }
|
358
|
+
filtered
|
359
|
+
end
|
360
|
+
unless logs.nil? || logs.empty?
|
361
|
+
logger.debug { "Logs for provision apply:\n#{logs['provision'].join}" } if logs['provision']
|
362
|
+
logger.error { "Logs for idempotent apply:\n#{logs['idempotent'].join}" } if logs['idempotent']
|
363
|
+
logger.error { "Logs for noop apply:\n#{logs['noop'].join}" } if logs['noop']
|
364
|
+
end
|
367
365
|
end
|
368
366
|
end
|
369
367
|
end
|
@@ -2,27 +2,61 @@
|
|
2
2
|
|
3
3
|
require 'puppet/modulebuilder'
|
4
4
|
require 'fileutils'
|
5
|
+
require_relative '../logging'
|
5
6
|
|
6
7
|
module CemAcpt
|
7
8
|
module Utils
|
8
9
|
# Puppet-related utilities
|
9
10
|
module Puppet
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
11
|
+
class ModulePackageBuilder
|
12
|
+
include CemAcpt::Logging
|
13
|
+
|
14
|
+
attr_reader :module_dir, :target_dir, :should_log, :metadata, :package_path
|
15
|
+
|
16
|
+
def initialize(module_dir, target_dir: nil, should_log: false, builder: ::Puppet::Modulebuilder::Builder)
|
17
|
+
@module_dir = ::File.expand_path(module_dir)
|
18
|
+
@target_dir = target_dir
|
19
|
+
@should_log = should_log
|
20
|
+
@builder = new_builder(builder)
|
21
|
+
@metadata = @builder.metadata # Validates metadata
|
22
|
+
@package_built = false
|
23
|
+
@package_path = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Builds a Puppet module package.
|
27
|
+
# @return [String] Path to the built package.
|
28
|
+
def build
|
29
|
+
@package_path = @metadata['name'].include?('windows') ? build_windows_package : @builder.build
|
30
|
+
@package_built = true
|
31
|
+
logger.info('CemAcpt::Utils::Puppet::ModulePackageBuilder') { "Built module package: #{@package_path}" }
|
32
|
+
@package_path
|
33
|
+
end
|
34
|
+
|
35
|
+
# Determines if a package has been built.
|
36
|
+
def package_built?
|
37
|
+
@package_built
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# This method is used to create a new builder instance.
|
43
|
+
# It is used to allow for dependency injection in tests.
|
44
|
+
def new_builder(builder)
|
45
|
+
builder.new(@module_dir, @target_dir, @should_log ? logger : nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
# This method is used to build a Windows module package.
|
49
|
+
# It's unknown exactly why we need to package the Windows module differently from the Linux module.
|
50
|
+
# When we find out, we should add an explanation here.
|
51
|
+
def build_windows_module
|
52
|
+
package_name = "#{@metadata['name']}.tar.gz"
|
53
|
+
package_file = ::File.join(@module_dir, package_name)
|
54
|
+
::FileUtils.rm_f(package_file) # Remove the old package file if it exists
|
55
|
+
Dir.chdir(@module_dir) do
|
56
|
+
`touch #{package_name} && tar -czf #{package_name} --exclude=#{package_name} *`
|
57
|
+
end
|
58
|
+
package_file
|
59
|
+
end
|
26
60
|
end
|
27
61
|
end
|
28
62
|
end
|
data/lib/cem_acpt/utils.rb
CHANGED
@@ -13,6 +13,7 @@ module CemAcpt
|
|
13
13
|
class << self
|
14
14
|
include CemAcpt::Logging
|
15
15
|
|
16
|
+
# This is method currently unused, see lib/cem_acpt/utils/puppet.rb for details.
|
16
17
|
def package_win_module(module_dir)
|
17
18
|
# Path to the package file
|
18
19
|
package_file = File.join(module_dir, 'puppetlabs-cem_windows.tar.gz')
|
@@ -53,7 +54,7 @@ module CemAcpt
|
|
53
54
|
def get_windows_login_info(instance_name, hash_of_instance)
|
54
55
|
password_and_username = {}
|
55
56
|
password_and_username[instance_name] = {}
|
56
|
-
info = reset_password_readiness_polling(instance_name).split(
|
57
|
+
info = reset_password_readiness_polling(instance_name).split(%r{\r?\n})[1..2]
|
57
58
|
info.each do |line|
|
58
59
|
key_val = line.split(' ')
|
59
60
|
password_and_username[instance_name][key_val[0].strip.delete(':')] = key_val[1].strip
|
data/lib/cem_acpt/version.rb
CHANGED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/opt/puppetlabs/puppet/bin/ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'webrick'
|
6
|
+
|
7
|
+
# Implements a simple HTTP server that serves the various run logs
|
8
|
+
class RunLogs < WEBrick::HTTPServlet::AbstractServlet
|
9
|
+
# rubocop:disable Naming/MethodName
|
10
|
+
def do_GET(_request, response)
|
11
|
+
run_logs = {}
|
12
|
+
p_logs = read_log_file('/opt/cem_acpt/provision_apply.log')
|
13
|
+
i_logs = read_log_file('/opt/cem_acpt/idempotent_apply.log')
|
14
|
+
n_logs = read_log_file('/opt/cem_acpt/noop_apply.log')
|
15
|
+
run_logs['provision'] = p_logs unless p_logs.empty?
|
16
|
+
run_logs['idempotent'] = i_logs unless i_logs.empty?
|
17
|
+
run_logs['noop'] = n_logs unless n_logs.empty?
|
18
|
+
response.status = run_logs.empty? ? 404 : 200
|
19
|
+
response['Content-Type'] = 'application/json'
|
20
|
+
response.body = run_logs.to_json
|
21
|
+
end
|
22
|
+
# rubocop:enable Naming/MethodName
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def read_log_file(file)
|
27
|
+
if File.exist?(file)
|
28
|
+
format_log(File.readlines(file))
|
29
|
+
else
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
rescue StandardError => e
|
33
|
+
['Error reading log file', e.message, e.backtrace.join("\n")]
|
34
|
+
end
|
35
|
+
|
36
|
+
def format_log(log)
|
37
|
+
# Force UTF-8 on each line so they aren't mangled by the UTF to ASCII conversion
|
38
|
+
encoded = log.map { |l| l.force_encoding(Encoding::UTF_8) }
|
39
|
+
# Now we need to condense the lines into logical groups based on log entry. This
|
40
|
+
# allows us to filter out debug logs that span multiple lines.
|
41
|
+
memo = nil
|
42
|
+
encoded.each_with_object([]) do |line, formatted|
|
43
|
+
if memo.nil?
|
44
|
+
memo = line
|
45
|
+
elsif line.match?(%r{^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \+\d{4}.*$})
|
46
|
+
formatted << memo
|
47
|
+
memo = line
|
48
|
+
else
|
49
|
+
memo += line
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
server = WEBrick::HTTPServer.new(Port: 8083)
|
56
|
+
server.mount '/run-logs', RunLogs
|
57
|
+
|
58
|
+
trap 'INT' do
|
59
|
+
server.shutdown
|
60
|
+
end
|
61
|
+
WEBrick::Daemon.start
|
62
|
+
server.start
|
@@ -2,7 +2,7 @@
|
|
2
2
|
Description=unit file for goss server acpt endpoint service
|
3
3
|
|
4
4
|
[Service]
|
5
|
-
ExecStart=/usr/local/bin/goss -g /opt/cem_acpt/goss.yaml serve -f json --endpoint "/acpt" --cache
|
5
|
+
ExecStart=/usr/local/bin/goss -g /opt/cem_acpt/goss.yaml serve -f json -o pretty --endpoint "/acpt" --cache '0s'
|
6
6
|
|
7
7
|
[Install]
|
8
8
|
WantedBy=multi-user.target
|
@@ -2,7 +2,7 @@
|
|
2
2
|
Description=unit file for goss server acpt endpoint service
|
3
3
|
|
4
4
|
[Service]
|
5
|
-
ExecStart=/usr/local/bin/goss -g /opt/cem_acpt/goss/puppet_idempotent.yaml serve -f json -l ":8081" --endpoint "/idempotent" --cache
|
5
|
+
ExecStart=/usr/local/bin/goss -g /opt/cem_acpt/goss/puppet_idempotent.yaml serve -f json -o pretty -l ":8081" --endpoint "/idempotent" --cache '0s'
|
6
6
|
|
7
7
|
[Install]
|
8
8
|
WantedBy=multi-user.target
|
@@ -2,7 +2,7 @@
|
|
2
2
|
Description=unit file for goss server acpt endpoint service
|
3
3
|
|
4
4
|
[Service]
|
5
|
-
ExecStart=/usr/local/bin/goss -g /opt/cem_acpt/goss/puppet_noop.yaml serve -f json -l ":8082" --endpoint "/noop" --cache
|
5
|
+
ExecStart=/usr/local/bin/goss -g /opt/cem_acpt/goss/puppet_noop.yaml serve -f json -o pretty -l ":8082" --endpoint "/noop" --cache '0s'
|
6
6
|
|
7
7
|
[Install]
|
8
8
|
WantedBy=multi-user.target
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cem_acpt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- puppetlabs
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async-http
|
@@ -280,6 +280,7 @@ files:
|
|
280
280
|
- lib/cem_acpt/version.rb
|
281
281
|
- lib/terraform/gcp/linux/goss/puppet_idempotent.yaml
|
282
282
|
- lib/terraform/gcp/linux/goss/puppet_noop.yaml
|
283
|
+
- lib/terraform/gcp/linux/log_service/log_service.rb
|
283
284
|
- lib/terraform/gcp/linux/main.tf
|
284
285
|
- lib/terraform/gcp/linux/systemd/goss-acpt.service
|
285
286
|
- lib/terraform/gcp/linux/systemd/goss-idempotent.service
|
@@ -313,7 +314,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
313
314
|
- !ruby/object:Gem::Version
|
314
315
|
version: '0'
|
315
316
|
requirements: []
|
316
|
-
rubygems_version: 3.
|
317
|
+
rubygems_version: 3.5.18
|
317
318
|
signing_key:
|
318
319
|
specification_version: 4
|
319
320
|
summary: CEM Acceptance Tests
|