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
@@ -0,0 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require_relative '../logging'
|
5
|
+
|
6
|
+
module CemAcpt
|
7
|
+
module Utils
|
8
|
+
# SSH-related utilities
|
9
|
+
module SSH
|
10
|
+
# Class for generating SSH keys
|
11
|
+
class Keygen
|
12
|
+
include CemAcpt::Logging
|
13
|
+
|
14
|
+
DEFAULT_TYPE = 'ed25519'
|
15
|
+
DEFAULT_PASSPHRASE = ''
|
16
|
+
DEFAULT_ROUNDS = 100
|
17
|
+
DEFAULT_BITS = 4096
|
18
|
+
|
19
|
+
def initialize(key_dir: ::File.join(ENV['HOME'], '.ssh'))
|
20
|
+
@key_dir = key_dir
|
21
|
+
@bin_path = find_bin_path
|
22
|
+
end
|
23
|
+
|
24
|
+
def exist?(key_name)
|
25
|
+
key_paths(key_name, chmod: false)
|
26
|
+
true
|
27
|
+
rescue StandardError
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def create(key_name, **options)
|
32
|
+
delete(key_name) # Delete existing keys with same name
|
33
|
+
cmd = new_keygen_cmd(key_name, **options)
|
34
|
+
logger.debug("Creating SSH key with command: #{cmd}")
|
35
|
+
_stdout, stderr, status = Open3.capture3(cmd)
|
36
|
+
raise "Failed to create SSH key! #{stderr}" unless status.success?
|
37
|
+
|
38
|
+
key_paths(key_name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(key_name)
|
42
|
+
priv_key = key_path(key_name)
|
43
|
+
pub_key = key_path(key_name, public_key: true)
|
44
|
+
if ::File.file?(priv_key)
|
45
|
+
logger.debug("Deleting private key: #{priv_key}")
|
46
|
+
::File.delete(priv_key)
|
47
|
+
end
|
48
|
+
if ::File.file?(pub_key)
|
49
|
+
logger.debug("Deleting public key: #{pub_key}")
|
50
|
+
::File.delete(pub_key)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
FIND_BIN_PATH_COMMANDS = [
|
57
|
+
"#{ENV['SHELL']} -c 'command -v ssh-keygen'",
|
58
|
+
"#{ENV['SHELL']} -c 'which ssh-keygen'",
|
59
|
+
].freeze
|
60
|
+
|
61
|
+
def find_bin_path(find_cmd = FIND_BIN_PATH_COMMANDS.first)
|
62
|
+
bin_path, stderr, status = Open3.capture3(find_cmd)
|
63
|
+
raise "Cannot find ssh-keygen with command #{find_cmd}: #{stderr}" unless status.success?
|
64
|
+
|
65
|
+
bin_path.chomp
|
66
|
+
rescue StandardError => e
|
67
|
+
return find_bin_path(FIND_BIN_PATH_COMMANDS.last) unless FIND_BIN_PATH_COMMANDS.last == find_cmd
|
68
|
+
|
69
|
+
raise e
|
70
|
+
end
|
71
|
+
|
72
|
+
def new_keygen_cmd(key_name, **options)
|
73
|
+
[
|
74
|
+
@bin_path,
|
75
|
+
'-o',
|
76
|
+
"-t #{options[:type] || DEFAULT_TYPE}",
|
77
|
+
"-C '#{options[:comment] || "cem_acpt_#{key_name}"}'",
|
78
|
+
"-f #{::File.join(@key_dir, key_name)}",
|
79
|
+
"-N '#{options[:passphrase] || DEFAULT_PASSPHRASE}'",
|
80
|
+
"-a #{options[:rounds] || DEFAULT_ROUNDS}",
|
81
|
+
"-b #{options[:bits] || DEFAULT_BITS}",
|
82
|
+
].join(' ')
|
83
|
+
end
|
84
|
+
|
85
|
+
def key_path(file_name, public_key: false)
|
86
|
+
key = ::File.join(@key_dir, file_name)
|
87
|
+
public_key ? "#{key}.pub" : key
|
88
|
+
end
|
89
|
+
|
90
|
+
def key_paths(file_name, chmod: true)
|
91
|
+
priv_key = key_path(file_name)
|
92
|
+
pub_key = key_path(file_name, public_key: true)
|
93
|
+
raise "Private key file #{priv_key} does not exist" unless ::File.file?(priv_key)
|
94
|
+
raise "Public key file #{pub_key} does not exist" unless ::File.file?(pub_key)
|
95
|
+
|
96
|
+
::File.chmod(0o600, priv_key) if chmod
|
97
|
+
::File.chmod(0o600, pub_key) if chmod
|
98
|
+
[priv_key, pub_key]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.ssh_keygen
|
103
|
+
bin_path = `#{ENV['SHELL']} -c 'command -v ssh-keygen'`.chomp
|
104
|
+
raise 'Cannot find ssh-keygen! Install it and verify PATH' unless bin_path
|
105
|
+
|
106
|
+
bin_path
|
107
|
+
rescue StandardError => e
|
108
|
+
raise "Cannot find ssh-keygen! Install it and verify PATH. Orignal error: #{e}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.default_keydir
|
112
|
+
ssh_dir = ::File.join(ENV['HOME'], '.ssh')
|
113
|
+
raise "SSH directory at #{ssh_dir} does not exist" unless ::File.directory?(ssh_dir)
|
114
|
+
|
115
|
+
ssh_dir
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.file_path(file_name, keydir: default_keydir)
|
119
|
+
::File.join(keydir, file_name)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Takes a file name (not path) and optional SSH key directory and returns the paths
|
123
|
+
# to the private key and public key based on the file name given.
|
124
|
+
# @param file_name [String] The base name for the keys
|
125
|
+
# @param keydir [String] An optional SSH key directory
|
126
|
+
def self.key_paths(file_name, keydir: default_keydir)
|
127
|
+
[file_path(file_name, keydir: keydir), file_path("#{file_name}.pub", keydir: keydir)]
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.create(key_name, **options)
|
131
|
+
keygen = Keygen.new
|
132
|
+
keys = keygen.create(key_name, **options)
|
133
|
+
keys + ['/dev/null']
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.create_known_hosts(known_hosts, overwrite: true, keydir: default_keydir)
|
137
|
+
return nil unless known_hosts
|
138
|
+
|
139
|
+
kh_file = file_path(known_hosts, keydir: keydir)
|
140
|
+
::File.open(kh_file, 'w') { |f| f.write("\n") } unless ::File.exist?(kh_file) && !overwrite
|
141
|
+
kh_file
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.set_ssh_file_permissions(*files)
|
145
|
+
files.uniq.compact.map { |p| ::File.chmod(0o600, p) }
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.ephemeral_ssh_key(keydir: default_keydir)
|
149
|
+
CemAcpt::Utils::SSH::Ephemeral.create(keydir: keydir)
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.clean_ephemeral_keys
|
153
|
+
CemAcpt::Utils::SSH::Ephemeral.clean
|
154
|
+
end
|
155
|
+
|
156
|
+
# Ephemeral SSH key generation and cleanup
|
157
|
+
module Ephemeral
|
158
|
+
PRIV_KEY = 'acpt_test_key'
|
159
|
+
CREATE_OPTS = {
|
160
|
+
type: 'ed25519',
|
161
|
+
bits: '4096',
|
162
|
+
rounds: '100',
|
163
|
+
comment: 'Ephemeral for cem_acpt',
|
164
|
+
password: '',
|
165
|
+
known_hosts: 'acpt_known_hosts',
|
166
|
+
overwrite_known_hosts: true,
|
167
|
+
}.freeze
|
168
|
+
|
169
|
+
class << self
|
170
|
+
attr_accessor :ephemeral_keydir
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.create(keydir: CemAcpt::Utils::SSH.default_keydir)
|
174
|
+
return [false, false, false] if ENV['CEM_ACPT_SSH_PRI_KEY'] # Don't create ephemeral keys if this is set
|
175
|
+
|
176
|
+
self.ephemeral_keydir = keydir
|
177
|
+
@priv_key, @pub_key, @known_hosts = CemAcpt::Utils::SSH.create(PRIV_KEY, keydir: ephemeral_keydir, **CREATE_OPTS)
|
178
|
+
[@priv_key, @pub_key, @known_hosts]
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.clean
|
182
|
+
return if ENV['CEM_ACPT_SSH_PRI_KEY']
|
183
|
+
|
184
|
+
[@priv_key, @pub_key, @known_hosts].each_with_object([]) do |f, arr|
|
185
|
+
next unless f
|
186
|
+
|
187
|
+
path = CemAcpt::Utils::SSH.file_path(f, keydir: ephemeral_keydir)
|
188
|
+
if ::File.exist?(path)
|
189
|
+
::File.delete(path)
|
190
|
+
arr << path
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent-ruby'
|
4
|
+
|
5
|
+
module CemAcpt
|
6
|
+
module Utils
|
7
|
+
# Terminal-related utilities
|
8
|
+
module Terminal
|
9
|
+
def self.keep_terminal_alive
|
10
|
+
executor = Concurrent::SingleThreadExecutor.new
|
11
|
+
executor.post do
|
12
|
+
loop do
|
13
|
+
$stdout.print(".\r")
|
14
|
+
sleep(1)
|
15
|
+
$stdout.print("..\r")
|
16
|
+
sleep(1)
|
17
|
+
$stdout.print("...\r")
|
18
|
+
sleep(1)
|
19
|
+
$stdout.print(" \r")
|
20
|
+
sleep(1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
executor
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/cem_acpt/utils.rb
CHANGED
@@ -1,144 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative 'utils/puppet'
|
4
|
+
require_relative 'utils/ssh'
|
5
|
+
require_relative 'utils/terminal'
|
5
6
|
|
6
7
|
module CemAcpt
|
7
8
|
# Utility methods and modules for CemAcpt.
|
8
|
-
module Utils
|
9
|
-
def self.os
|
10
|
-
case RbConfig::CONFIG['host_os']
|
11
|
-
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
12
|
-
:windows
|
13
|
-
else
|
14
|
-
:nix
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# File-related utilities
|
19
|
-
module File
|
20
|
-
def self.set_permissions(permission, *file_paths)
|
21
|
-
file_paths.map { |p| ::File.chmod(permission, p) }
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# Puppet-related utilities
|
26
|
-
module Puppet
|
27
|
-
DEFAULT_PUPPET_PATH = {
|
28
|
-
nix: '/opt/puppetlabs/bin/puppet',
|
29
|
-
windows: 'C:/Program Files/Puppet Labs/Puppet/bin/puppet.bat',
|
30
|
-
}.freeze
|
31
|
-
|
32
|
-
# Finds and returns the Puppet executable
|
33
|
-
def self.puppet_executable
|
34
|
-
this_os = CemAcpt::Utils.os
|
35
|
-
if ::File.file?(DEFAULT_PUPPET_PATH[this_os]) && ::File.executable?(DEFAULT_PUPPET_PATH[this_os])
|
36
|
-
return DEFAULT_PUPPET_PATH[this_os]
|
37
|
-
end
|
38
|
-
|
39
|
-
file_name = 'puppet'
|
40
|
-
if this_os == :windows
|
41
|
-
exts = ENV['PATHEXT'] ? ".{#{ENV['PATHEXT'].tr(';', ',').tr('.', '').downcase}}" : '.{exe,com,bat}'
|
42
|
-
file_name = "#{file_name}#{exts}"
|
43
|
-
end
|
44
|
-
ENV['PATH'].split(::File::PATH_SEPARATOR).each do |path|
|
45
|
-
if ::File.file?(::File.join(path, file_name)) && ::File.executable?(::File.join(path, file_name))
|
46
|
-
return ::File.join(path, file_name)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
raise 'Could not find Puppet executable! Is Puppet installed?'
|
50
|
-
end
|
51
|
-
|
52
|
-
# Builds a Puppet module package.
|
53
|
-
# @param module_dir [String] Path to the module directory. If target_dir
|
54
|
-
# is specified as a relative path, it will be relative to the module dir.
|
55
|
-
# @param target_dir [String] Path to the target directory where the package
|
56
|
-
# will be built. This defaults to the relative path 'pkg/'.
|
57
|
-
# @param should_log [Boolean] Whether or not to log the build process.
|
58
|
-
# @return [String] Path to the built package.
|
59
|
-
def self.build_module_package(module_dir, target_dir = nil, should_log: false)
|
60
|
-
require 'puppet/modulebuilder'
|
61
|
-
require 'fileutils'
|
62
|
-
|
63
|
-
builder_logger = should_log ? logger : nil
|
64
|
-
builder = ::Puppet::Modulebuilder::Builder.new(::File.expand_path(module_dir), target_dir, builder_logger)
|
65
|
-
|
66
|
-
# Validates module metadata by raising exception if invalid
|
67
|
-
_metadata = builder.metadata
|
68
|
-
|
69
|
-
# Builds the module package
|
70
|
-
builder.build
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
# Node-related utilities
|
75
|
-
module Node
|
76
|
-
def self.random_instance_name(prefix: 'cem-acpt-', length: 24)
|
77
|
-
rand_length = length - prefix.length
|
78
|
-
"#{prefix}#{::SecureRandom.hex(rand_length)}"
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# SSH-related utilities
|
83
|
-
module SSH
|
84
|
-
def self.ssh_keygen
|
85
|
-
bin_path = `#{ENV['SHELL']} -c 'command -v ssh-keygen'`.chomp
|
86
|
-
raise 'Cannot find ssh-keygen! Install it and verify PATH' unless bin_path
|
87
|
-
|
88
|
-
bin_path
|
89
|
-
rescue StandardError => e
|
90
|
-
raise "Cannot find ssh-keygen! Install it and verify PATH. Orignal error: #{e}"
|
91
|
-
end
|
92
|
-
|
93
|
-
def self.default_keydir
|
94
|
-
ssh_dir = ::File.join(ENV['HOME'], '.ssh')
|
95
|
-
raise "SSH directory at #{ssh_dir} does not exist" unless ::File.directory?(ssh_dir)
|
96
|
-
|
97
|
-
ssh_dir
|
98
|
-
end
|
99
|
-
|
100
|
-
def self.ephemeral_ssh_key(type: 'rsa', bits: '4096', comment: nil, keydir: default_keydir)
|
101
|
-
raise ArgumentError, 'keydir does not exist' unless ::File.directory?(keydir)
|
102
|
-
|
103
|
-
keyfile = ::File.join(keydir, 'acpt_test_key')
|
104
|
-
keygen_cmd = [ssh_keygen, "-t #{type}", "-b #{bits}", "-f #{keyfile}", '-N ""']
|
105
|
-
keygen_cmd << "-C \"#{comment}\"" if comment
|
106
|
-
_, stderr, status = Open3.capture3(keygen_cmd.join(' '))
|
107
|
-
raise "Failed to generate ephemeral SSH key: #{stderr}" unless status.success?
|
108
|
-
|
109
|
-
[keyfile, "#{keyfile}.pub"]
|
110
|
-
end
|
111
|
-
|
112
|
-
def self.acpt_known_hosts(keydir: default_keydir, file_name: 'acpt_known_hosts', overwrite: true)
|
113
|
-
kh_file = ::File.join(keydir, file_name)
|
114
|
-
::File.open(kh_file, 'w') { |f| f.write("\n") } unless ::File.exist?(kh_file) && !overwrite
|
115
|
-
kh_file
|
116
|
-
end
|
117
|
-
|
118
|
-
def self.set_ssh_file_permissions(priv_key, pub_key, known_hosts)
|
119
|
-
CemAcpt::Utils::File.set_permissions(0o600, priv_key, pub_key, known_hosts)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
# Terminal-related utilities
|
124
|
-
module Terminal
|
125
|
-
def self.keep_terminal_alive
|
126
|
-
require 'concurrent-ruby'
|
127
|
-
executor = Concurrent::SingleThreadExecutor.new
|
128
|
-
executor.post do
|
129
|
-
loop do
|
130
|
-
$stdout.print(".\r")
|
131
|
-
sleep(1)
|
132
|
-
$stdout.print("..\r")
|
133
|
-
sleep(1)
|
134
|
-
$stdout.print("...\r")
|
135
|
-
sleep(1)
|
136
|
-
$stdout.print(" \r")
|
137
|
-
sleep(1)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
executor
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
9
|
+
module Utils; end
|
144
10
|
end
|
data/lib/cem_acpt/version.rb
CHANGED
data/lib/cem_acpt.rb
CHANGED
@@ -1,34 +1,84 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
require_relative 'cem_acpt/config'
|
4
|
+
require_relative 'cem_acpt/logging'
|
5
|
+
require_relative 'cem_acpt/test_runner'
|
6
|
+
require_relative 'cem_acpt/version'
|
7
7
|
|
8
|
+
module CemAcpt
|
8
9
|
class << self
|
9
10
|
include CemAcpt::Logging
|
10
|
-
end
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
attr_reader :config
|
13
|
+
|
14
|
+
def version(as_str: false)
|
15
|
+
return VERSION unless as_str
|
16
|
+
|
17
|
+
"cem_acpt v#{VERSION}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def print_config(options, format: :yaml)
|
21
|
+
config = new_config(options)
|
22
|
+
if format == :explain
|
23
|
+
puts config.explain
|
24
|
+
return
|
25
|
+
end
|
26
|
+
puts config.send("to_#{format}".to_sym)
|
27
|
+
end
|
28
|
+
|
29
|
+
def run(options)
|
30
|
+
# Set up config, logger, and helper
|
31
|
+
@config = new_config(options)
|
32
|
+
initialize_logger!
|
33
|
+
runner = new_runner
|
14
34
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
35
|
+
# Set up signal handlers
|
36
|
+
Signal.trap('INT') do
|
37
|
+
@trap_context = true
|
38
|
+
logger.trap_context = @trap_context
|
39
|
+
logger.fatal('Signal Handler') { 'Received interrupt signal. Cleaning up test suite...' }
|
40
|
+
runner.clean_up(@trap_context)
|
41
|
+
logger.fatal('Signal Handler') { 'Exiting due to interrupt signal' }
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
|
45
|
+
# Run the test suite
|
46
|
+
runner.run
|
47
|
+
|
48
|
+
exit runner.exit_code
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def new_config(options)
|
54
|
+
CemAcpt::Config.new(opts: options, config_file: options[:config_file])
|
22
55
|
end
|
23
|
-
|
24
|
-
|
56
|
+
|
57
|
+
def new_runner
|
58
|
+
CemAcpt::TestRunner::Runner.new(@config)
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize_logger!
|
62
|
+
raise 'Config must be loaded before logger can be initialized' if config.nil? || config.empty?
|
63
|
+
|
64
|
+
log_formatter = config.get('log_format')&.to_sym || :text
|
65
|
+
logdevs = [$stdout]
|
66
|
+
# If log_file is set, and quiet is set, only log to the file
|
67
|
+
if config.get('log_file') && config.get('quiet')
|
68
|
+
logdevs = [config.get('log_file')]
|
69
|
+
elsif config.get('log_file') && !config.get('quiet')
|
70
|
+
logdevs << config.get('log_file')
|
71
|
+
end
|
72
|
+
if config.ci_mode? && !logdevs.include?($stdout)
|
73
|
+
logdevs << $stdout
|
74
|
+
end
|
75
|
+
new_log = new_logger(
|
76
|
+
*logdevs,
|
77
|
+
level: config.get('log_level'),
|
78
|
+
formatter: log_formatter,
|
79
|
+
)
|
80
|
+
new_log.set_verbose(!!config.get('verbose'))
|
81
|
+
new_log
|
25
82
|
end
|
26
|
-
new_logger(
|
27
|
-
*logdevs,
|
28
|
-
level: log_level,
|
29
|
-
formatter: log_formatter,
|
30
|
-
)
|
31
|
-
exit_code = CemAcpt::Context.with(params, &:run)
|
32
|
-
exit exit_code
|
33
83
|
end
|
34
84
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
command:
|
2
|
+
puppet_noop:
|
3
|
+
exec: 'sudo /opt/puppetlabs/puppet/bin/puppet apply --verbose --detailed-exitcodes --noop /opt/cem_acpt/manifest.pp'
|
4
|
+
timeout: 300000 # 5 mins in miliseconds
|
5
|
+
stdout:
|
6
|
+
- "Applied catalog in"
|
7
|
+
stderr:
|
8
|
+
- ""
|
9
|
+
exit-status:
|
10
|
+
or:
|
11
|
+
- 0
|
12
|
+
- 2
|
@@ -0,0 +1,191 @@
|
|
1
|
+
terraform {
|
2
|
+
required_providers {
|
3
|
+
google = {
|
4
|
+
source = "hashicorp/google"
|
5
|
+
version = "4.59.0"
|
6
|
+
}
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
variable "credentials_file" {
|
11
|
+
type = string
|
12
|
+
}
|
13
|
+
|
14
|
+
variable "project" {
|
15
|
+
type = string
|
16
|
+
}
|
17
|
+
|
18
|
+
variable "region" {
|
19
|
+
type = string
|
20
|
+
}
|
21
|
+
|
22
|
+
variable "zone" {
|
23
|
+
type = string
|
24
|
+
}
|
25
|
+
|
26
|
+
variable "subnetwork" {
|
27
|
+
type = string
|
28
|
+
}
|
29
|
+
|
30
|
+
variable "username" {
|
31
|
+
type = string
|
32
|
+
}
|
33
|
+
|
34
|
+
variable "private_key" {
|
35
|
+
type = string
|
36
|
+
sensitive = true
|
37
|
+
}
|
38
|
+
|
39
|
+
variable "public_key" {
|
40
|
+
type = string
|
41
|
+
}
|
42
|
+
|
43
|
+
variable "puppet_module_package" {
|
44
|
+
type = string
|
45
|
+
}
|
46
|
+
|
47
|
+
variable "node_data" {
|
48
|
+
type = map(object({
|
49
|
+
machine_type = string
|
50
|
+
image = string
|
51
|
+
disk_size = number
|
52
|
+
test_name = string
|
53
|
+
goss_file = string
|
54
|
+
puppet_manifest = string
|
55
|
+
provision_dir_source = string
|
56
|
+
provision_dir_dest = string
|
57
|
+
provision_commands = list(string)
|
58
|
+
}))
|
59
|
+
}
|
60
|
+
|
61
|
+
provider "google" {
|
62
|
+
credentials = file(var.credentials_file)
|
63
|
+
project = var.project
|
64
|
+
region = var.region
|
65
|
+
zone = var.zone
|
66
|
+
}
|
67
|
+
|
68
|
+
resource "google_compute_instance" "acpt-test-node" {
|
69
|
+
provider = google
|
70
|
+
for_each = var.node_data
|
71
|
+
name = each.key
|
72
|
+
machine_type = each.value.machine_type
|
73
|
+
|
74
|
+
boot_disk {
|
75
|
+
initialize_params {
|
76
|
+
image = each.value.image
|
77
|
+
size = each.value.disk_size
|
78
|
+
type = "pd-standard"
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
scheduling {
|
83
|
+
preemptible = true
|
84
|
+
automatic_restart = false
|
85
|
+
provisioning_model = "SPOT"
|
86
|
+
instance_termination_action = "DELETE"
|
87
|
+
}
|
88
|
+
|
89
|
+
network_interface {
|
90
|
+
subnetwork = var.subnetwork
|
91
|
+
access_config {
|
92
|
+
network_tier = "STANDARD"
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
provisioner "remote-exec" {
|
97
|
+
connection {
|
98
|
+
type = "ssh"
|
99
|
+
user = "${var.username}"
|
100
|
+
timeout = "5m"
|
101
|
+
host = self.network_interface.0.access_config.0.nat_ip
|
102
|
+
private_key = "${file(var.private_key)}"
|
103
|
+
agent = false
|
104
|
+
}
|
105
|
+
inline = [
|
106
|
+
"sudo mkdir -p ${each.value.provision_dir_dest}",
|
107
|
+
"sudo chown -R ${var.username}:${var.username} ${each.value.provision_dir_dest}"
|
108
|
+
]
|
109
|
+
}
|
110
|
+
|
111
|
+
provisioner "file" {
|
112
|
+
source = "${each.value.provision_dir_source}/"
|
113
|
+
destination = each.value.provision_dir_dest
|
114
|
+
connection {
|
115
|
+
type = "ssh"
|
116
|
+
user = "${var.username}"
|
117
|
+
timeout = "2m"
|
118
|
+
host = self.network_interface.0.access_config.0.nat_ip
|
119
|
+
private_key = "${file(var.private_key)}"
|
120
|
+
agent = false
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
provisioner "file" {
|
125
|
+
source = var.puppet_module_package
|
126
|
+
destination = "${each.value.provision_dir_dest}/puppet-module.tar.gz"
|
127
|
+
connection {
|
128
|
+
type = "ssh"
|
129
|
+
user = "${var.username}"
|
130
|
+
timeout = "2m"
|
131
|
+
host = self.network_interface.0.access_config.0.nat_ip
|
132
|
+
private_key = "${file(var.private_key)}"
|
133
|
+
agent = false
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
provisioner "file" {
|
138
|
+
source = each.value.goss_file
|
139
|
+
destination = "${each.value.provision_dir_dest}/goss.yaml"
|
140
|
+
connection {
|
141
|
+
type = "ssh"
|
142
|
+
user = "${var.username}"
|
143
|
+
timeout = "2m"
|
144
|
+
host = self.network_interface.0.access_config.0.nat_ip
|
145
|
+
private_key = "${file(var.private_key)}"
|
146
|
+
agent = false
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
provisioner "file" {
|
151
|
+
source = each.value.puppet_manifest
|
152
|
+
destination = "${each.value.provision_dir_dest}/manifest.pp"
|
153
|
+
connection {
|
154
|
+
type = "ssh"
|
155
|
+
user = "${var.username}"
|
156
|
+
timeout = "2m"
|
157
|
+
host = self.network_interface.0.access_config.0.nat_ip
|
158
|
+
private_key = "${file(var.private_key)}"
|
159
|
+
agent = false
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
provisioner "remote-exec" {
|
164
|
+
connection {
|
165
|
+
type = "ssh"
|
166
|
+
user = "${var.username}"
|
167
|
+
timeout = "5m"
|
168
|
+
host = self.network_interface.0.access_config.0.nat_ip
|
169
|
+
private_key = "${file(var.private_key)}"
|
170
|
+
agent = false
|
171
|
+
}
|
172
|
+
inline = each.value.provision_commands
|
173
|
+
}
|
174
|
+
|
175
|
+
metadata = {
|
176
|
+
oslogin = "TRUE"
|
177
|
+
ssh-keys = "${var.username}:${file(var.public_key)}"
|
178
|
+
cem-acpt-test = each.value.test_name
|
179
|
+
}
|
180
|
+
|
181
|
+
tags = [ "cem-acpt-test-node" ]
|
182
|
+
}
|
183
|
+
|
184
|
+
output "instance_name_ip" {
|
185
|
+
value = {
|
186
|
+
for k, v in google_compute_instance.acpt-test-node : v.name => {
|
187
|
+
ip = v.network_interface.0.access_config.0.nat_ip,
|
188
|
+
test_name = v.metadata.cem-acpt-test,
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|