cem_acpt 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8b8813ddfec58349a3ce7c587dbab4a4eae04d7fa24c0ed87896de41ba3d160
4
- data.tar.gz: '099f6ed6a7570e2283a740d89bd3864638ff470c8d9a8456bd3eb64ca8d61217'
3
+ metadata.gz: a7ce3a10380b032387479fc85590e5a9ca59c2dc5cec33d71cb9d79eaf6938a7
4
+ data.tar.gz: d3d12c621ee070af7cab276e6887c45343519c51e9260a1c6e6fd0fc4ca64e80
5
5
  SHA512:
6
- metadata.gz: ce3f4d2ec04e379cea66c728da7d9a0c8b54a339b4023d40bf47e787e96aaf91c4f6fcd4c5513e38b3c3459de32bf575ad2efbfd8c4e440fd60a16fee0a4ced7
7
- data.tar.gz: 3f442bebaab6df3e75925ccfd4324a9b38d84c649805ef59a9af407ad097ca0154797d916c7039ae810f5e9db182e58ccd345049e3ad6c41b9fda97405e828c1
6
+ metadata.gz: d394053e259fb6d502a44d219b8dd056e1777d61a67a1a3da34360288ee6b623eadb842381900fe1d56d4944f0b9823c10c59f136f6ac486a8c0c47722dbfddf
7
+ data.tar.gz: c73ede8616dc75ab9da65573f5ad3a77c4ec604089183ede90e2a97a9fa2f5699ae012ba157935c0f7bdec9e2744f996d154958b40fde9271c545900f884de77
data/.gitignore CHANGED
@@ -9,3 +9,10 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+ test.log
13
+
14
+ # profiling
15
+ profile.txt
16
+
17
+ # local dev
18
+ run_script.rb
data/Gemfile.lock CHANGED
@@ -1,33 +1,39 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cem_acpt (0.1.0)
4
+ cem_acpt (0.2.0)
5
5
  concurrent-ruby (~> 1.1.9)
6
6
  deep_merge (~> 1.2.2)
7
- net-scp (~> 3.0)
8
- net-ssh (~> 6.1)
7
+ net-ssh (~> 7.0.0.beta1)
9
8
  puppet-modulebuilder (> 0.0.1)
10
- rake (> 0.8)
11
- rspec (> 0.1)
12
- serverspec (> 0.1)
9
+ rake
10
+ rspec
11
+ serverspec-cem-acpt
13
12
 
14
13
  GEM
15
14
  remote: https://rubygems.org/
16
15
  specs:
17
- concurrent-ruby (1.1.9)
16
+ ast (2.4.2)
17
+ concurrent-ruby (1.1.10)
18
18
  deep_merge (1.2.2)
19
19
  diff-lcs (1.5.0)
20
20
  minitar (0.9)
21
21
  multi_json (1.15.0)
22
22
  net-scp (3.0.0)
23
23
  net-ssh (>= 2.6.5, < 7.0.0)
24
- net-ssh (6.1.0)
24
+ net-ssh (7.0.0.beta1)
25
25
  net-telnet (0.1.1)
26
- pathspec (1.1.3)
26
+ parallel (1.22.1)
27
+ parser (3.1.2.0)
28
+ ast (~> 2.4.1)
29
+ pathspec (1.0.0)
27
30
  puppet-modulebuilder (0.3.0)
28
31
  minitar (~> 0.9)
29
32
  pathspec (>= 0.2.1, < 2.0.0)
33
+ rainbow (3.1.1)
30
34
  rake (12.3.3)
35
+ regexp_parser (2.5.0)
36
+ rexml (3.2.5)
31
37
  rspec (3.11.0)
32
38
  rspec-core (~> 3.11.0)
33
39
  rspec-expectations (~> 3.11.0)
@@ -40,29 +46,44 @@ GEM
40
46
  rspec-its (1.3.0)
41
47
  rspec-core (>= 3.0.0)
42
48
  rspec-expectations (>= 3.0.0)
43
- rspec-mocks (3.11.0)
49
+ rspec-mocks (3.11.1)
44
50
  diff-lcs (>= 1.2.0, < 2.0)
45
51
  rspec-support (~> 3.11.0)
46
52
  rspec-support (3.11.0)
47
- serverspec (2.41.8)
53
+ rubocop (1.30.1)
54
+ parallel (~> 1.10)
55
+ parser (>= 3.1.0.0)
56
+ rainbow (>= 2.2.2, < 4.0)
57
+ regexp_parser (>= 1.8, < 3.0)
58
+ rexml (>= 3.2.5, < 4.0)
59
+ rubocop-ast (>= 1.18.0, < 2.0)
60
+ ruby-progressbar (~> 1.7)
61
+ unicode-display_width (>= 1.4.0, < 3.0)
62
+ rubocop-ast (1.18.0)
63
+ parser (>= 3.1.1.0)
64
+ ruby-progressbar (1.11.0)
65
+ serverspec-cem-acpt (2.42.0)
48
66
  multi_json
49
67
  rspec (~> 3.0)
50
68
  rspec-its
51
- specinfra (~> 2.72)
69
+ specinfra (~> 2.83.1)
52
70
  sfl (2.3)
53
- specinfra (2.82.2)
71
+ specinfra (2.83.2)
54
72
  net-scp
55
73
  net-ssh (>= 2.7)
56
74
  net-telnet (= 0.1.1)
57
75
  sfl
76
+ unicode-display_width (2.1.0)
58
77
 
59
78
  PLATFORMS
60
- ruby
79
+ universal-java-17
80
+ x86_64-darwin-19
61
81
 
62
82
  DEPENDENCIES
63
83
  cem_acpt!
64
84
  rake (~> 12.0)
65
85
  rspec (~> 3.0)
86
+ rubocop
66
87
 
67
88
  BUNDLED WITH
68
- 2.1.4
89
+ 2.3.9
data/README.md CHANGED
@@ -81,6 +81,14 @@ Much like `name_pattern_vars`, specifying the `image_name_builder` top-level key
81
81
 
82
82
  ## Installation
83
83
 
84
+ ### Installing from RubyGems
85
+
86
+ To install the gem locally to use as a CLI tool:
87
+
88
+ `gem install cem_acpt --prerelease`
89
+
90
+ ### Installing into a Puppet module
91
+
84
92
  Add this line to your module's Gemfile (usually under `group :development`):
85
93
 
86
94
  ```ruby
@@ -127,10 +135,6 @@ CemAcpt can use a config file (default path is `./cem_acpt_config.yaml`) to conf
127
135
 
128
136
  With CemAcpt, acceptance test names can have semantic meaning and influence how your acceptance test nodes are provisioned and how test data is created. The semantic meaning of the test names can be configured in the config file.
129
137
 
130
- #### Test data
131
-
132
- Test data can
133
-
134
138
  ## Development
135
139
 
136
140
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/cem_acpt.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.summary = 'CEM Acceptance Tests'
12
12
  spec.description = 'Litmus-like library focusing on CEM Acceptance Tests'
13
13
  spec.homepage = 'https://github.com/puppetlabs/cem_acpt'
14
- spec.license = 'MIT'
14
+ spec.license = 'proprietary'
15
15
  spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
16
16
 
17
17
  spec.metadata['homepage_uri'] = spec.homepage
@@ -20,18 +20,18 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  # Specify which files should be added to the gem when it is released.
22
22
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
24
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
25
  end
26
26
  spec.bindir = 'exe'
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ['lib']
29
- spec.add_dependency 'concurrent-ruby', '~> 1.1.9'
30
- spec.add_dependency 'deep_merge', '~> 1.2.2'
31
- spec.add_dependency 'net-scp', '~> 3.0'
32
- spec.add_dependency 'net-ssh', '~> 6.1'
33
- spec.add_dependency 'puppet-modulebuilder', '> 0.0.1'
34
- spec.add_dependency 'rake', '> 0.8'
35
- spec.add_dependency 'rspec', '> 0.1'
36
- spec.add_dependency 'serverspec', '> 0.1'
29
+ spec.add_runtime_dependency 'concurrent-ruby', '~> 1.1.9'
30
+ spec.add_runtime_dependency 'deep_merge', '~> 1.2.2'
31
+ spec.add_runtime_dependency 'net-ssh', '~> 7.0.0.beta1'
32
+ spec.add_runtime_dependency 'puppet-modulebuilder', '> 0.0.1'
33
+ spec.add_runtime_dependency 'rake'
34
+ spec.add_runtime_dependency 'rspec'
35
+ spec.add_runtime_dependency 'serverspec-cem-acpt'
36
+ spec.add_development_dependency 'rubocop'
37
37
  end
data/exe/cem_acpt CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ #!/usr/bin/env jruby
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'deep_merge'
@@ -40,17 +40,43 @@ parser = OptionParser.new do |opts|
40
40
 
41
41
  opts.on('-I', '--CI', 'Run in CI mode') do
42
42
  options[:CI] = true
43
+ options[:log_format] = 'github_action'
43
44
  end
44
45
 
45
46
  opts.on('-E', '--no-destroy-nodes', 'Do not destroy nodes') do
46
47
  options[:no_destroy_nodes] = true
47
48
  end
49
+
50
+ opts.on('-q', '--quiet', 'Do not log to stdout') do
51
+ options[:quiet] = true
52
+ end
53
+
54
+ opts.on('-v', '--verbose', 'Enables verbose logging mode') do
55
+ options[:verbose] = true
56
+ end
57
+
58
+ opts.on('-V', '--version', 'Show the cem_acpt version') do
59
+ options[:show_version] = true
60
+ end
61
+
62
+ opts.on('-S', '--no-epehemeral-ssh-key', 'Do not generate an ephemeral SSH key for test suites') do
63
+ options[:no_ephemeral_ssh_key] = true
64
+ end
65
+
66
+ # NOT IMPLEMENTED
67
+ # opts.on('-R', '--reuse-nodes', 'Reuses test nodes if compatible node inventory exists') do
68
+ # options[:reuse_nodes] = true
69
+ # end
48
70
  end
49
71
 
50
72
  parser.parse!
73
+ if options[:show_version]
74
+ puts CemAcpt::VERSION
75
+ exit 0
76
+ end
51
77
  options[:module_dir] = Dir.pwd unless options[:module_dir]
52
- options[:platforms] = ['gcp', 'vmpooler'] unless options[:platform]
53
- if options.key?(:log_level) && options[:log_level] == 'debug'
78
+ options[:platforms] = %w[gcp vmpooler] unless options[:platform]
79
+ if (options[:log_level] == 'debug' || options[:verbose]) && !options[:quiet]
54
80
  puts '#################### RUNNING ACCEPTANCE TEST SUITE ####################'
55
81
  puts "Using options from command line: #{options}"
56
82
  puts '#######################################################################'
@@ -1,60 +1,153 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent-ruby'
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
+
4
10
  module CemAcpt
5
11
  # Context provides the context in which the RunHandler creates and starts Runners.
6
12
  module Context
7
- require_relative 'platform'
8
- require_relative 'shared_objects'
9
- require_relative 'test_data'
10
- require_relative 'logging'
13
+ class ContextError < StandardError; end
11
14
 
12
15
  class << self
13
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
14
71
  end
15
72
 
16
- # Creates a context for the RunHandler to create and start Runners.
73
+ # Creates a context (CemAcpt::Context::Ctx) object for the RunHandler to create and start Runners.
17
74
  # Provides the following objects for the Runners: a config object,
18
75
  # the test data hash, the node inventory, and the local port allocator.
19
76
  # Additionally, it creates the platform-specific node objects for each
20
77
  # test suite in the test data. It then calls the provided block with
21
78
  # the context objects nodes, config, test_data, and node_inventory.
22
79
  # @param config_opts [Hash] the config options
23
- # @param config_file [String] the config file
24
- def self.with(config_opts: nil, config_file: nil, &block)
25
- logger.debug('In context with:')
26
- logger.debug("config_opts: #{config_opts}")
27
- logger.debug("config_file: #{config_file}")
28
-
29
- config = CemAcpt::Config.new.load(opts: config_opts, config_file: config_file)
30
- logger.debug("Loaded config: #{config.to_h}")
31
-
32
- test_data = CemAcpt::TestData.acceptance_test_data(config)
33
- logger.debug("Loaded test data: #{test_data}")
34
-
35
- node_inventory = CemAcpt::NodeInventory.new
36
- logger.debug("Initialized new node inventory: #{node_inventory}")
37
-
38
- local_port_allocator = CemAcpt::LocalPortAllocator.new
39
- logger.debug("Initialized new local port allocator: #{local_port_allocator}")
40
-
41
- if config.has?('platform')
42
- logger.debug("Using platform: #{config.get('platform')}")
43
- nodes = CemAcpt::Platform.use(config.get('platform'), config, test_data, local_port_allocator)
44
- block.call(nodes, config, test_data, node_inventory)
45
- elsif config.has?('platforms')
46
- config.get('platforms').each do |platform|
47
- logger.debug("Using platform: #{platform}")
48
- nodes = CemAcpt::Platform.use(platform, config, test_data, local_port_allocator)
49
- block.call(nodes, config, test_data, node_inventory)
50
- end
51
- else
52
- raise CemAcpt::Error, 'No platform(s) specified'
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
53
99
  end
100
+ @exit_code = @run_handler.exit_code
54
101
  rescue StandardError => e
55
- logger.fatal("Fatal error: #{e.message}")
56
- logger.debug(e.backtrace.join('; '))
57
- exit(1)
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
58
151
  end
59
152
  end
60
153
  end
@@ -45,14 +45,14 @@ module CemAcpt::CoreExtensions
45
45
  # Holds deep_freeze extensions to OpenStruct
46
46
  module OpenStruct
47
47
  def deep_freeze
48
- return if deep_frozen?
49
-
50
- @table.reduce({}) do |h, (key, value)|
51
- fkey = key.respond_to?(:deep_freeze) ? key.deep_freeze : key
52
- fval = value.respond_to?(:deep_freeze) ? value.deep_freeze : value
53
- h.merge(fkey => fval)
54
- end.freeze
55
- @deep_frozen = true
48
+ unless deep_frozen?
49
+ @table.reduce({}) do |h, (key, value)|
50
+ fkey = key.respond_to?(:deep_freeze) ? key.deep_freeze : key
51
+ fval = value.respond_to?(:deep_freeze) ? value.deep_freeze : value
52
+ h.merge(fkey => fval)
53
+ end.freeze
54
+ @deep_frozen = true
55
+ end
56
56
  end
57
57
 
58
58
  def deep_frozen?
@@ -90,10 +90,7 @@ module CemAcpt::CoreExtensions
90
90
  # hash = {a: {b: {c: 1}}}
91
91
  # hash.dot_dig('a.b.c') # => 1
92
92
  def dot_dig(path)
93
- result = dig(*path.split('.').map(&:to_sym))
94
- return result unless result.nil?
95
-
96
- dig(*path.split('.'))
93
+ dig(*path.split('.').map(&:to_sym)) || dig(*path.split('.'))
97
94
  end
98
95
 
99
96
  # Stores a value in a nested Hash using a dot-separated path