cem_acpt 0.2.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/spec.yml +30 -0
- data/Gemfile +4 -3
- data/Gemfile.lock +95 -43
- data/README.md +144 -83
- data/cem_acpt.gemspec +12 -7
- data/exe/cem_acpt +41 -7
- data/lib/cem_acpt/config.rb +340 -0
- data/lib/cem_acpt/core_extensions.rb +17 -61
- data/lib/cem_acpt/goss/api/action_response.rb +175 -0
- data/lib/cem_acpt/goss/api.rb +83 -0
- data/lib/cem_acpt/goss.rb +8 -0
- data/lib/cem_acpt/image_name_builder.rb +0 -9
- data/lib/cem_acpt/logging/formatter.rb +97 -0
- data/lib/cem_acpt/logging.rb +168 -142
- data/lib/cem_acpt/platform/base.rb +26 -37
- data/lib/cem_acpt/platform/gcp.rb +48 -62
- data/lib/cem_acpt/platform.rb +30 -28
- data/lib/cem_acpt/provision/terraform/linux.rb +47 -0
- data/lib/cem_acpt/provision/terraform/os_data.rb +72 -0
- data/lib/cem_acpt/provision/terraform/windows.rb +22 -0
- data/lib/cem_acpt/provision/terraform.rb +193 -0
- data/lib/cem_acpt/provision.rb +20 -0
- data/lib/cem_acpt/puppet_helpers.rb +0 -1
- data/lib/cem_acpt/test_data.rb +23 -13
- data/lib/cem_acpt/test_runner/log_formatter/goss_action_response.rb +104 -0
- data/lib/cem_acpt/test_runner/log_formatter.rb +10 -0
- data/lib/cem_acpt/test_runner.rb +170 -3
- data/lib/cem_acpt/utils/puppet.rb +29 -0
- data/lib/cem_acpt/utils/ssh.rb +197 -0
- data/lib/cem_acpt/utils/terminal.rb +27 -0
- data/lib/cem_acpt/utils.rb +4 -138
- data/lib/cem_acpt/version.rb +1 -1
- data/lib/cem_acpt.rb +73 -23
- data/lib/terraform/gcp/linux/goss/puppet_idempotent.yaml +10 -0
- data/lib/terraform/gcp/linux/goss/puppet_noop.yaml +12 -0
- data/lib/terraform/gcp/linux/main.tf +191 -0
- data/lib/terraform/gcp/linux/systemd/goss-acpt.service +8 -0
- data/lib/terraform/gcp/linux/systemd/goss-idempotent.service +8 -0
- data/lib/terraform/gcp/linux/systemd/goss-noop.service +8 -0
- data/lib/terraform/gcp/windows/.keep +0 -0
- data/sample_config.yaml +22 -21
- metadata +151 -51
- data/lib/cem_acpt/bootstrap/bootstrapper.rb +0 -206
- data/lib/cem_acpt/bootstrap/operating_system/rhel_family.rb +0 -129
- data/lib/cem_acpt/bootstrap/operating_system.rb +0 -17
- data/lib/cem_acpt/bootstrap.rb +0 -12
- data/lib/cem_acpt/context.rb +0 -153
- data/lib/cem_acpt/platform/base/cmd.rb +0 -71
- data/lib/cem_acpt/platform/gcp/cmd.rb +0 -353
- data/lib/cem_acpt/platform/gcp/compute.rb +0 -332
- data/lib/cem_acpt/platform/vmpooler.rb +0 -24
- data/lib/cem_acpt/rspec_utils.rb +0 -242
- data/lib/cem_acpt/shared_objects.rb +0 -537
- data/lib/cem_acpt/spec_helper_acceptance.rb +0 -184
- data/lib/cem_acpt/test_runner/run_handler.rb +0 -187
- data/lib/cem_acpt/test_runner/runner.rb +0 -210
- data/lib/cem_acpt/test_runner/runner_result.rb +0 -103
@@ -1,129 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Currently unused in this implementation.
|
4
|
-
module CemAcpt::Bootstrap::OperatingSystem
|
5
|
-
# This module holds methods used by Bootstrap for RHEL-family operating systems
|
6
|
-
module RhelFamily
|
7
|
-
def puppet_agent_repo
|
8
|
-
"https://yum.puppet.com/#{@collection}-release-el-#{@major_version}.noarch.rpm"
|
9
|
-
end
|
10
|
-
|
11
|
-
def package_manager
|
12
|
-
return 'dnf' if @major_version.to_i >= 8
|
13
|
-
|
14
|
-
'yum'
|
15
|
-
end
|
16
|
-
|
17
|
-
def rvm_deps
|
18
|
-
[
|
19
|
-
'kernel-devel',
|
20
|
-
'gcc',
|
21
|
-
'gcc-c++',
|
22
|
-
'make',
|
23
|
-
'augeas',
|
24
|
-
'augeas-devel',
|
25
|
-
'patch',
|
26
|
-
'readline',
|
27
|
-
'readline-devel',
|
28
|
-
'zlib',
|
29
|
-
'zlib-devel',
|
30
|
-
'libffi-devel',
|
31
|
-
'openssl-devel',
|
32
|
-
'bzip2',
|
33
|
-
'autoconf',
|
34
|
-
'automake',
|
35
|
-
'libtool',
|
36
|
-
'bison',
|
37
|
-
'sqlite-devel',
|
38
|
-
]
|
39
|
-
end
|
40
|
-
|
41
|
-
def repo_installs(repos, sudo: true)
|
42
|
-
return if repos.nil? || repos.empty?
|
43
|
-
|
44
|
-
cmd = [
|
45
|
-
repo_install_cmd(repos, sudo: sudo),
|
46
|
-
repo_install_verify_cmd(repos, sudo: sudo),
|
47
|
-
].join(' && ')
|
48
|
-
"#{cmd} | sudo tee -ai #{@log_file}"
|
49
|
-
end
|
50
|
-
|
51
|
-
def package_installs(packages, sudo: true)
|
52
|
-
return if packages.nil? || packages.empty?
|
53
|
-
|
54
|
-
packages += rvm_deps
|
55
|
-
cmd = [
|
56
|
-
package_install_cmd(packages, sudo: sudo),
|
57
|
-
package_install_verify_cmd(packages, sudo: sudo),
|
58
|
-
].join(' && ')
|
59
|
-
"#{cmd} | sudo tee -ai #{@log_file}"
|
60
|
-
end
|
61
|
-
|
62
|
-
def service_starts(services, sudo: true)
|
63
|
-
return if services.nil? || services.empty?
|
64
|
-
|
65
|
-
cmd = [
|
66
|
-
#service_start_verify_existance_cmd(services, sudo: sudo),
|
67
|
-
service_start_cmd(services, sudo: sudo),
|
68
|
-
].join(' && ')
|
69
|
-
"#{cmd} | sudo tee -ai #{@log_file}"
|
70
|
-
end
|
71
|
-
|
72
|
-
def repo_install_cmd(repos, sudo: true)
|
73
|
-
cmd = []
|
74
|
-
cmd << 'sudo' if sudo
|
75
|
-
cmd << 'rpm -Uv'
|
76
|
-
repos.each { |r| cmd << r }
|
77
|
-
cmd.join(' ')
|
78
|
-
end
|
79
|
-
|
80
|
-
def repo_install_verify_cmd(repos, sudo: true)
|
81
|
-
cmd = []
|
82
|
-
cmd << 'sudo' if sudo
|
83
|
-
cmd << 'rpm -q'
|
84
|
-
repos.each { |r| cmd << r }
|
85
|
-
cmd.join(' ')
|
86
|
-
end
|
87
|
-
|
88
|
-
def package_install_cmd(packages, sudo: true)
|
89
|
-
cmd = []
|
90
|
-
cmd << 'sudo' if sudo
|
91
|
-
cmd << "#{package_manager} install #{package_install_cmd_opts}"
|
92
|
-
packages.each { |p| cmd << p }
|
93
|
-
cmd << package_install_cmd_output_format
|
94
|
-
cmd.join(' ')
|
95
|
-
end
|
96
|
-
|
97
|
-
def package_install_verify_cmd(packages, sudo: true)
|
98
|
-
cmd = []
|
99
|
-
cmd << 'sudo' if sudo
|
100
|
-
cmd << "#{package_manager} list installed"
|
101
|
-
packages.each { |p| cmd << p }
|
102
|
-
cmd.join(' ')
|
103
|
-
end
|
104
|
-
|
105
|
-
def package_install_cmd_opts
|
106
|
-
'-y'
|
107
|
-
end
|
108
|
-
|
109
|
-
def package_install_cmd_output_format
|
110
|
-
'| tr "\n" "#" | sed -e \'s/# / /g\' | tr "#" "\n"'
|
111
|
-
end
|
112
|
-
|
113
|
-
def service_start_cmd(services, sudo: true)
|
114
|
-
cmd = []
|
115
|
-
cmd << 'sudo' if sudo
|
116
|
-
cmd << 'systemctl start'
|
117
|
-
services.each { |s| cmd << s }
|
118
|
-
cmd.join(' ')
|
119
|
-
end
|
120
|
-
|
121
|
-
def service_start_verify_existance_cmd(services, sudo: true)
|
122
|
-
cmd = []
|
123
|
-
cmd << 'sudo' if sudo
|
124
|
-
cmd << 'systemctl list-units --type=service'
|
125
|
-
services.each { |s| cmd << s }
|
126
|
-
cmd.join(' ')
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Currently unused in this implementation.
|
4
|
-
module CemAcpt::Bootstrap::OperatingSystem
|
5
|
-
class Error < StandardError; end
|
6
|
-
|
7
|
-
# Currently unused in this implementation.
|
8
|
-
def use_os(os)
|
9
|
-
case os
|
10
|
-
when %r{^(centos|rhel)$}
|
11
|
-
require_relative 'operating_system/rhel_family'
|
12
|
-
self.class.include CemAcpt::Bootstrap::OperatingSystem::RhelFamily
|
13
|
-
else
|
14
|
-
raise Error, "Operating system #{os} is not supported"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/cem_acpt/bootstrap.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Bootstrap provides a method for bootstrapping test nodes.
|
4
|
-
# Currently unused in this implementation.
|
5
|
-
module CemAcpt::Bootstrap
|
6
|
-
require_relative 'bootstrap/bootstrapper'
|
7
|
-
|
8
|
-
def self.run(instance_name, instance_image, cmd_provider, collection: 'puppet7', repos: [], packages: [], services: [], commands: [], cmd_provider_args: {})
|
9
|
-
bootstrapper = CemAcpt::Bootstrap::Bootstrapper.new(instance_name, instance_image, cmd_provider, collection: 'puppet7', repos: [], packages: [], services: [], commands: [], cmd_provider_args: {})
|
10
|
-
bootstrapper.run
|
11
|
-
end
|
12
|
-
end
|
data/lib/cem_acpt/context.rb
DELETED
@@ -1,153 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'platform'
|
4
|
-
require_relative 'utils'
|
5
|
-
require_relative 'shared_objects'
|
6
|
-
require_relative 'test_data'
|
7
|
-
require_relative 'test_runner/run_handler'
|
8
|
-
require_relative 'logging'
|
9
|
-
|
10
|
-
module CemAcpt
|
11
|
-
# Context provides the context in which the RunHandler creates and starts Runners.
|
12
|
-
module Context
|
13
|
-
class ContextError < StandardError; end
|
14
|
-
|
15
|
-
class << self
|
16
|
-
include CemAcpt::Logging
|
17
|
-
|
18
|
-
KEY_PATH = File.join([ENV['HOME'], '.ssh', 'acpt_test_key']).freeze
|
19
|
-
KH_PATH = File.join([ENV['HOME'], '.ssh', 'acpt_test_known_hosts']).freeze
|
20
|
-
|
21
|
-
def log(msg, level = :info)
|
22
|
-
real_msg = "CONTEXT: #{msg}"
|
23
|
-
logger.send(level, real_msg)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Builds the Puppet module package
|
27
|
-
# @param opts [Hash] config opts
|
28
|
-
# @return [String] The path to the Puppet module package
|
29
|
-
def build_module_package(opts = {})
|
30
|
-
module_dir = opts[:module_dir] || __dir__
|
31
|
-
pkg_path = CemAcpt::Utils::Puppet.build_module_package(module_dir)
|
32
|
-
log("Module package built at #{pkg_path}")
|
33
|
-
pkg_path
|
34
|
-
end
|
35
|
-
|
36
|
-
# Creates a SSH key and a SSH known hosts file for the acceptance test suite
|
37
|
-
def new_test_ssh_key
|
38
|
-
log('Creating ephemeral SSH key and known hosts file for acceptance test suites...')
|
39
|
-
@ssh_priv_key, @ssh_pub_key = CemAcpt::Utils::SSH.ephemeral_ssh_key
|
40
|
-
@ssh_known_hosts = CemAcpt::Utils::SSH.acpt_known_hosts
|
41
|
-
CemAcpt::Utils::SSH.set_ssh_file_permissions(@ssh_priv_key, @ssh_pub_key, @ssh_known_hosts)
|
42
|
-
log('Successfully created SSH files...')
|
43
|
-
log("SSH private key: #{@ssh_priv_key}", :debug)
|
44
|
-
log("SSH public key: #{@ssh_pub_key}", :debug)
|
45
|
-
log("SSH known hosts: #{@ssh_known_hosts}", :debug)
|
46
|
-
end
|
47
|
-
|
48
|
-
# Deletes acceptance test suite SSH files
|
49
|
-
def clean_test_ssh_key
|
50
|
-
log('Deleting ephemeral ssh keys and acpt_known_hosts if they exist...')
|
51
|
-
[@ssh_priv_key, @ssh_pub_key, @ssh_known_hosts].map { |f| File.delete(f) if File.exist?(f) }
|
52
|
-
end
|
53
|
-
|
54
|
-
# Prints a period to the terminal every 5 seconds in a single line to keep the terminal
|
55
|
-
# alive. This is used when running in CI mode. Does nothing unless the option `:CI` is
|
56
|
-
# `true`, or the environment variables CI or GITHUB_ACTION are set to a truthy value.
|
57
|
-
# @param opts [Hash] config opts
|
58
|
-
def keep_terminal_alive(opts = {})
|
59
|
-
@keep_terminal_alive = if opts[:CI] || ENV['CI'] || ENV['GITHUB_ACTION']
|
60
|
-
CemAcpt::Utils::Terminal.keep_terminal_alive
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def clean_up_test_suite(opts)
|
65
|
-
@ctx.node_inventory.clear!
|
66
|
-
@ctx.node_inventory.clean_local_files
|
67
|
-
clean_test_ssh_key unless opts[:no_ephemeral_ssh_key]
|
68
|
-
@run_handler&.destroy_test_nodes
|
69
|
-
@keep_terminal_alive&.kill
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# Creates a context (CemAcpt::Context::Ctx) object for the RunHandler to create and start Runners.
|
74
|
-
# Provides the following objects for the Runners: a config object,
|
75
|
-
# the test data hash, the node inventory, and the local port allocator.
|
76
|
-
# Additionally, it creates the platform-specific node objects for each
|
77
|
-
# test suite in the test data. It then calls the provided block with
|
78
|
-
# the context objects nodes, config, test_data, and node_inventory.
|
79
|
-
# @param config_opts [Hash] the config options
|
80
|
-
def self.with(**opts)
|
81
|
-
@opts = opts
|
82
|
-
@start_time = Time.now
|
83
|
-
raise CemAcpt::Context::ContextError, 'CemAcpt::Context.with requires a block' unless block_given?
|
84
|
-
|
85
|
-
config_file = @opts[:config_file] || File.expand_path('./cem_acpt_config.yaml')
|
86
|
-
logger.info("Running acceptance test suite at #{@start_time}")
|
87
|
-
logger.debug("Config opts: #{@opts}")
|
88
|
-
logger.debug("Config file: #{config_file}")
|
89
|
-
logger.info("Using module directory: #{@opts[:module_dir]}")
|
90
|
-
keep_terminal_alive(@opts)
|
91
|
-
Dir.chdir(opts[:module_dir]) do
|
92
|
-
new_test_ssh_key unless @opts[:no_ephemeral_ssh_key]
|
93
|
-
pkg_path = build_module_package(@opts)
|
94
|
-
@ctx = CemAcpt::Context::Ctx.new(opts: @opts, config_file: config_file, module_package_path: pkg_path)
|
95
|
-
logger.debug("Created Ctx object #{@ctx}")
|
96
|
-
@run_handler = CemAcpt::TestRunner::RunHandler.new(@ctx)
|
97
|
-
logger.debug("Created RunHandler object #{@run_handler}")
|
98
|
-
yield @run_handler
|
99
|
-
end
|
100
|
-
@exit_code = @run_handler.exit_code
|
101
|
-
rescue StandardError => e
|
102
|
-
logger.fatal("Acceptance test suite encountered an error: #{e.message}")
|
103
|
-
logger.fatal(e.backtrace.join("\n"))
|
104
|
-
@exit_code = 1
|
105
|
-
ensure
|
106
|
-
clean_up_test_suite(@opts)
|
107
|
-
total_minutes = ((Time.now - @start_time) / 60).round
|
108
|
-
logger.info("Test suite finished in ~#{total_minutes} minutes")
|
109
|
-
@exit_code || 1
|
110
|
-
end
|
111
|
-
|
112
|
-
# Ctx holds the context objects for the RunHandler to create and start Runners.
|
113
|
-
class Ctx
|
114
|
-
attr_reader :config, :test_data, :module_package_path, :node_inventory, :local_port_allocator
|
115
|
-
|
116
|
-
def initialize(opts: nil, config_file: nil, module_package_path: nil)
|
117
|
-
@config = CemAcpt::Config.new.load(opts: opts, config_file: config_file)
|
118
|
-
@test_data = CemAcpt::TestData.acceptance_test_data(@config)
|
119
|
-
@module_package_path = module_package_path
|
120
|
-
@node_inventory = CemAcpt::NodeInventory.new
|
121
|
-
@local_port_allocator = CemAcpt::LocalPortAllocator.new
|
122
|
-
prep_environment
|
123
|
-
end
|
124
|
-
|
125
|
-
def nodes
|
126
|
-
return @nodes if defined?(@nodes)
|
127
|
-
|
128
|
-
raise CemAcpt::Error, 'No platform(s) specified' unless @config.has?('platform') || @config.has?('platforms')
|
129
|
-
|
130
|
-
@nodes = nodes_from_platforms
|
131
|
-
@nodes
|
132
|
-
end
|
133
|
-
|
134
|
-
private
|
135
|
-
|
136
|
-
def prep_environment
|
137
|
-
@node_inventory.clean_local_files
|
138
|
-
end
|
139
|
-
|
140
|
-
def nodes_from_platforms
|
141
|
-
nodes = {}
|
142
|
-
if @config.has?('platform')
|
143
|
-
nodes[@config.get('platform')] = CemAcpt::Platform.use(@config.get('platform'), @config, @test_data, @local_port_allocator)
|
144
|
-
elsif @config.has?('platforms')
|
145
|
-
config.get('platforms').each do |platform|
|
146
|
-
nodes[platform] = CemAcpt::Platform.use(platform, @config, @test_data, @local_port_allocator)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
nodes
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module CemAcpt::Platform
|
4
|
-
require_relative File.join(__dir__, '..', '..', 'logging.rb')
|
5
|
-
|
6
|
-
class CmdError < StandardError; end
|
7
|
-
|
8
|
-
# Base class for command providers. Provides an API for subclasses to implement.
|
9
|
-
class CmdBase
|
10
|
-
include CemAcpt::Logging
|
11
|
-
|
12
|
-
attr_reader :env
|
13
|
-
|
14
|
-
def initialize(*_args, env: {}, **_kwargs)
|
15
|
-
@env = env
|
16
|
-
end
|
17
|
-
|
18
|
-
def local_exec(*_args, **_kwargs)
|
19
|
-
raise NotImplementedError, '#local_exec must be implemented by a subclass'
|
20
|
-
end
|
21
|
-
|
22
|
-
def ssh(_instance_name, _command, _ignore_command_errors: false, _opts: {})
|
23
|
-
raise NotImplementedError, '#ssh must be implemented by a subclass'
|
24
|
-
end
|
25
|
-
|
26
|
-
def scp_upload(_instance_name, _local, _remote, _scp_opts: {}, _opts: {})
|
27
|
-
raise NotImplementedError, '#scp_upload must be implemented by a subclass'
|
28
|
-
end
|
29
|
-
|
30
|
-
def scp_download(_instance_name, _local, _remote, _scp_opts: {}, _opts: {})
|
31
|
-
raise NotImplementedError, '#scp_download must be implemented by a subclass'
|
32
|
-
end
|
33
|
-
|
34
|
-
def ssh_ready?(_instance_name, _timeout = 300, _opts: {})
|
35
|
-
raise NotImplementedError, '#ssh_ready? must be implemented by a subclass'
|
36
|
-
end
|
37
|
-
|
38
|
-
def apply_manifest(_instance_name, _manifest, _opts: {})
|
39
|
-
raise NotImplementedError, '#create_manifest_on_node must be implemented by a subclass'
|
40
|
-
end
|
41
|
-
|
42
|
-
def run_shell(_instance_name, _command, _opts: {})
|
43
|
-
raise NotImplementedError, '#run_shell must be implemented by a subclass'
|
44
|
-
end
|
45
|
-
|
46
|
-
def trim_output(output)
|
47
|
-
if output.include?("\n")
|
48
|
-
output.split("\n").map(&:strip).reject(&:empty?).join("\n")
|
49
|
-
else
|
50
|
-
output.strip
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def with_timed_retry(timeout = 300)
|
55
|
-
return unless block_given?
|
56
|
-
|
57
|
-
last_error = nil
|
58
|
-
start_time = Time.now
|
59
|
-
while Time.now - start_time < timeout
|
60
|
-
begin
|
61
|
-
output = yield
|
62
|
-
return output
|
63
|
-
rescue StandardError => e
|
64
|
-
last_error = e
|
65
|
-
sleep(10)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
raise last_error
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|