cem_acpt 0.2.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 -345
- 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
|
+
}
|