cem_acpt 0.2.5 → 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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/spec.yml +30 -0
  3. data/Gemfile +4 -3
  4. data/Gemfile.lock +95 -43
  5. data/README.md +144 -83
  6. data/cem_acpt.gemspec +12 -7
  7. data/exe/cem_acpt +41 -7
  8. data/lib/cem_acpt/config.rb +340 -0
  9. data/lib/cem_acpt/core_extensions.rb +17 -61
  10. data/lib/cem_acpt/goss/api/action_response.rb +175 -0
  11. data/lib/cem_acpt/goss/api.rb +83 -0
  12. data/lib/cem_acpt/goss.rb +8 -0
  13. data/lib/cem_acpt/image_name_builder.rb +0 -9
  14. data/lib/cem_acpt/logging/formatter.rb +97 -0
  15. data/lib/cem_acpt/logging.rb +168 -142
  16. data/lib/cem_acpt/platform/base.rb +26 -37
  17. data/lib/cem_acpt/platform/gcp.rb +48 -62
  18. data/lib/cem_acpt/platform.rb +30 -28
  19. data/lib/cem_acpt/provision/terraform/linux.rb +47 -0
  20. data/lib/cem_acpt/provision/terraform/os_data.rb +72 -0
  21. data/lib/cem_acpt/provision/terraform/windows.rb +22 -0
  22. data/lib/cem_acpt/provision/terraform.rb +193 -0
  23. data/lib/cem_acpt/provision.rb +20 -0
  24. data/lib/cem_acpt/puppet_helpers.rb +0 -1
  25. data/lib/cem_acpt/test_data.rb +23 -13
  26. data/lib/cem_acpt/test_runner/log_formatter/goss_action_response.rb +104 -0
  27. data/lib/cem_acpt/test_runner/log_formatter.rb +10 -0
  28. data/lib/cem_acpt/test_runner.rb +170 -3
  29. data/lib/cem_acpt/utils/puppet.rb +29 -0
  30. data/lib/cem_acpt/utils/ssh.rb +197 -0
  31. data/lib/cem_acpt/utils/terminal.rb +27 -0
  32. data/lib/cem_acpt/utils.rb +4 -138
  33. data/lib/cem_acpt/version.rb +1 -1
  34. data/lib/cem_acpt.rb +73 -23
  35. data/lib/terraform/gcp/linux/goss/puppet_idempotent.yaml +10 -0
  36. data/lib/terraform/gcp/linux/goss/puppet_noop.yaml +12 -0
  37. data/lib/terraform/gcp/linux/main.tf +191 -0
  38. data/lib/terraform/gcp/linux/systemd/goss-acpt.service +8 -0
  39. data/lib/terraform/gcp/linux/systemd/goss-idempotent.service +8 -0
  40. data/lib/terraform/gcp/linux/systemd/goss-noop.service +8 -0
  41. data/lib/terraform/gcp/windows/.keep +0 -0
  42. data/sample_config.yaml +22 -21
  43. metadata +151 -51
  44. data/lib/cem_acpt/bootstrap/bootstrapper.rb +0 -206
  45. data/lib/cem_acpt/bootstrap/operating_system/rhel_family.rb +0 -129
  46. data/lib/cem_acpt/bootstrap/operating_system.rb +0 -17
  47. data/lib/cem_acpt/bootstrap.rb +0 -12
  48. data/lib/cem_acpt/context.rb +0 -153
  49. data/lib/cem_acpt/platform/base/cmd.rb +0 -71
  50. data/lib/cem_acpt/platform/gcp/cmd.rb +0 -345
  51. data/lib/cem_acpt/platform/gcp/compute.rb +0 -332
  52. data/lib/cem_acpt/platform/vmpooler.rb +0 -24
  53. data/lib/cem_acpt/rspec_utils.rb +0 -242
  54. data/lib/cem_acpt/shared_objects.rb +0 -537
  55. data/lib/cem_acpt/spec_helper_acceptance.rb +0 -184
  56. data/lib/cem_acpt/test_runner/run_handler.rb +0 -187
  57. data/lib/cem_acpt/test_runner/runner.rb +0 -210
  58. data/lib/cem_acpt/test_runner/runner_result.rb +0 -103
@@ -1,242 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'English'
4
- require 'open3'
5
- require 'pty'
6
- require 'shellwords'
7
- require_relative 'logging'
8
-
9
- module CemAcpt
10
- # Module that provides methods and objects for running and managing RSpec
11
- module RSpecUtils
12
- class BundlerNotFoundError < StandardError; end
13
- class RSpecNotFoundError < StandardError; end
14
-
15
- # Holds and formats a RSpec command
16
- class Command
17
- include CemAcpt::LoggingAsync
18
- attr_reader :debug, :format, :test_path, :use_bundler, :pty_pid
19
-
20
- # @param opts [Hash] options hash for the RSpec command
21
- # @option opts [String] :test_path The path (or glob path) to the test file(s) to run. If blank, runs all.
22
- # @option opts [Hash] :format Format options for rspec where the key is the format (documentation, json, etc)
23
- # and the value is the out-file path. If you do not want to save the results of a format to a file, the
24
- # value should be `nil`.
25
- # @option opts [Boolean] :debug True if RSpec should run in debug mode, false if not. Mutually exclusive with
26
- # `:quiet`. Default is `false`.
27
- # @option opts [Boolean] :quiet True if no output should be logged from RSpec command, false if output should
28
- # be logged. Mutually exclusive with `:debug`. Default is `false`.
29
- # @option opts [Boolean] :use_bundler Whether or not `bundle exec` should be used to run the RSpec command.
30
- # Default is `true`.
31
- # @option opts [Boolean] :bundle_install Whether or not to run `bundle install` before the RSpec command
32
- # if `use_bundler` is `true`.
33
- # @option opts [Boolean] :use_shell Whether or not to add `$SHELL` as a prefix to the command
34
- # @option opts [Hash] :env Environment variables to prepend to the command
35
- def initialize(opts = {})
36
- @test_path = opts[:test_path]&.shellescape
37
- @format = opts.fetch(:format, {})
38
- @debug = opts.fetch(:debug, false)
39
- @quiet = @debug ? false : opts.fetch(:quiet, false)
40
- @use_bundler = opts.fetch(:use_bundler, false)
41
- @bundle_install = opts.fetch(:bundle_install, false)
42
- @env = opts.fetch(:env, {})
43
- @pty_pid = nil
44
- validate_and_set_bin_paths(opts)
45
- end
46
-
47
- # Sets debug mode to `true`
48
- def set_debug
49
- @debug = true
50
- if @quiet
51
- async_debug('Setting :quiet to false because :debug is now true.')
52
- @quiet = false
53
- end
54
- end
55
-
56
- # Sets debug mode to `false`
57
- def unset_debug
58
- @debug = false
59
- end
60
-
61
- def quiet
62
- @quiet && !debug
63
- end
64
-
65
- # Adds a new format to the RSpec command
66
- # @param fmt [String] The name of the format (i.e. "documentation", "json", etc.)
67
- # @param out [String] If specified, saves the specified format to a file at this path
68
- def with_format(fmt, out: nil)
69
- @format[fmt.to_sym] = out
70
- end
71
-
72
- # Environment variables that will be used for the RSpec command
73
- # @return [Hash] A Hash of environment variables with each key pair being: <var name> => <var value>
74
- def env
75
- @debug ? @env.merge({ 'RSPEC_DEBUG' => 'true' }) : @env
76
- end
77
-
78
- # Returns an array representation of the RSpec command
79
- def to_a
80
- cmd = cmd_base.dup
81
- cmd << test_path if test_path
82
- format.each do |fmt, out|
83
- cmd += ['--format', fmt.to_s.shellescape]
84
- cmd += ['--out', out.to_s.shellescape] if out
85
- end
86
- cmd.compact
87
- end
88
-
89
- # Returns a string representation of the RSpec command
90
- def to_s
91
- to_a.join(' ')
92
- end
93
-
94
- # Executes the RSpec command on the current machine
95
- # @param pty [Boolean] If true, execute command in a PTY. If false, execute command directly.
96
- # @param log_prefix [String] A prefix to add to log messages generated while the command is running.
97
- def execute(pty: true, log_prefix: 'RSPEC')
98
- if pty
99
- execute_pty(log_prefix: log_prefix)
100
- else
101
- execute_no_pty(log_prefix: log_prefix)
102
- end
103
- end
104
-
105
- # Executes the RSpec command in a psuedo-terminal (PTY). First, it spawns a process
106
- # for $SHELL, sets environment variables `export_envs`, then calls the current RSpec
107
- # command in the shell and exits with the last exit code `$?`. Output is read from the
108
- # RSpec command in near real-time in a blocking manner unless the `:quiet` option has
109
- # been specified.
110
- # @param log_prefix [String] A prefix to add to the log messages that are output from
111
- # the RSpec command.
112
- # @return [Integer] The exit code of the RSpec command
113
- def execute_pty(log_prefix: 'RSPEC')
114
- async_debug("Executing RSpec command '#{self}' in PTY...", log_prefix)
115
- PTY.spawn(env, ENV['SHELL']) do |r, w, pid|
116
- @pty_pid = pid
117
- async_debug("Spawned RSpec PTY with PID #{@pty_pid}", log_prefix)
118
- export_envs(w)
119
- w.puts "#{self}; exit $?"
120
- quiet ? wait_io(r) : read_io(r, log_prefix: log_prefix)
121
- end
122
- $CHILD_STATUS
123
- end
124
-
125
- # Executes the RSpec command using Open3.popen2e(). The output stream, which is both
126
- # stderr and stdout, is read in real-time in a non-blocking manner.
127
- # @param log_prefix [String] A prefix to add to the log messages that are output from
128
- # the RSpec command.
129
- # @return [Integer] The exit code of the RSpec command
130
- def execute_no_pty(log_prefix: 'RSPEC')
131
- async_info("Executing RSpec command '#{self}' with Open3.popen2e()...", log_prefix)
132
- exit_status = nil
133
- Open3.popen2e(env, to_s) do |stdin, std_out_err, wait_thr|
134
- stdin.close
135
- quiet ? wait_io(std_out_err) : read_io(std_out_err, log_prefix: log_prefix)
136
- exit_status = wait_thr.value
137
- end
138
- exit_status
139
- end
140
-
141
- # Kills the PTY process with `SIGKILL` if the process exists
142
- def kill_pty
143
- Process.kill('KILL', @pty_pid) unless @pty_pid.nil?
144
- rescue Errno::ESRCH
145
- true
146
- end
147
-
148
- private
149
-
150
- # Detects if the current Ruby context is JRuby
151
- def jruby?
152
- File.basename(RbConfig.ruby) == 'jruby'
153
- end
154
-
155
- # The base RSpec command
156
- def cmd_base
157
- use_bundler ? cmd_base_bundler : cmd_base_rspec
158
- end
159
-
160
- # The base RSpec command if `:use_bundler` is `true`.
161
- def cmd_base_bundler
162
- base = [@bundle, 'exec', 'rspec']
163
- base.unshift("#{@bundle} install;") if @bundle_install
164
- base
165
- end
166
-
167
- # The base RSpec command if `:use_bundler` is `false`
168
- def cmd_base_rspec
169
- [@rspec]
170
- end
171
-
172
- # Puts export statements for each key-value pair in `env` to the given writer.
173
- # Writer is the write pipe of a PTY session, or a similar IO object that can
174
- # pass the statements to a shell.
175
- # @param writer [IO] An IO object that supprts `puts` and can send statements to a shell
176
- def export_envs(writer)
177
- env.each do |key, val|
178
- writer.puts "export #{key}=#{val}"
179
- end
180
- end
181
-
182
- # Finds and sets the paths to the `bundle` and `rspec` binaries. The paths can
183
- # be either passed in as options in the `opts` Hash or interrogated from the
184
- # system.
185
- # @param opts [Hash] The options hash
186
- # @option opts [String] :bundle An absolute path on the system to the `bundle` binary.
187
- # @option opts [String] :rspec An absolute path on the system to the `rspec` binary.
188
- # @raise [CemAcpt::RSpecUtils::BundlerNotFoundError] if `@use_bundler` is true and
189
- # `bundle` binary is not found.
190
- # @raise [CemAcpt::RSpecUtils::RSpecNotFoundError] if `rspec` binary is not found.
191
- def validate_and_set_bin_paths(opts = {})
192
- %i[bundle rspec].each do |bin|
193
- bin_path = opts[bin] || `command -v #{bin}`.strip
194
- bin_not_found(bin, bin_path) unless bin_path && File.exist?(bin_path)
195
- instance_variable_set("@#{bin}", bin_path)
196
- end
197
- end
198
-
199
- # Handles binary paths which are not found
200
- # @param bin [Symbol] The binary that was not found, either :bundle or :rspec.
201
- # @param bin_path [String] The path to the binary that was checked.
202
- # @raise [CemAcpt::RSpecUtils::BundlerNotFoundError] if `@use_bundler` is true and
203
- # `bundle` binary is not found.
204
- # @raise [CemAcpt::RSpecUtils::RSpecNotFoundError] if `rspec` binary is not found.
205
- # @raise [RuntimeError] if `bin` is not :bundle or :rspec.
206
- def bin_not_found(bin, bin_path)
207
- msg_base = "#{bin} not found."
208
- msg = bin_path.nil? ? "#{msg_base} Path is nil." : "#{msg_base} Path: #{bin_path}"
209
- case bin
210
- when :bundle
211
- raise BundlerNotFoundError, msg if @use_bundler
212
- when :rspec
213
- raise RSpecNotFoundError, msg
214
- else
215
- raise "bin #{bin} not recognized!"
216
- end
217
- end
218
-
219
- # Blocking wait on an IO stream. Wait stops once the IO stream has reached
220
- # end of file.
221
- # @param stdout [IO] An IO stream with output that can be read from.
222
- def wait_io(stdout)
223
- sleep(0.01) until stdout.eof?
224
- end
225
-
226
- # Reads and logs data from `stdout` in a near real-time, blocking manner.
227
- # @param stdout [IO] An IO stream with output that can be read from.
228
- # @param log_prefix [String] A string prefix that is added to log messages.
229
- def read_io(stdout, log_prefix: 'RSPEC')
230
- loop do
231
- chunk = stdout.read_nonblock(4096).strip
232
- async_info(chunk, log_prefix) unless chunk.nil? || chunk.empty?
233
- rescue IO::WaitReadable
234
- IO.select([stdout])
235
- retry
236
- rescue EOFError
237
- break
238
- end
239
- end
240
- end
241
- end
242
- end