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.
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