cem_acpt 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/CODEOWNERS +1 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +68 -0
- data/README.md +146 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/cem_acpt.gemspec +37 -0
- data/exe/cem_acpt +58 -0
- data/lib/cem_acpt/bootstrap/bootstrapper.rb +206 -0
- data/lib/cem_acpt/bootstrap/operating_system/rhel_family.rb +129 -0
- data/lib/cem_acpt/bootstrap/operating_system.rb +17 -0
- data/lib/cem_acpt/bootstrap.rb +12 -0
- data/lib/cem_acpt/context.rb +60 -0
- data/lib/cem_acpt/core_extensions.rb +111 -0
- data/lib/cem_acpt/image_name_builder.rb +104 -0
- data/lib/cem_acpt/logging.rb +193 -0
- data/lib/cem_acpt/platform/base/cmd.rb +65 -0
- data/lib/cem_acpt/platform/base.rb +78 -0
- data/lib/cem_acpt/platform/gcp/cmd.rb +313 -0
- data/lib/cem_acpt/platform/gcp/compute.rb +327 -0
- data/lib/cem_acpt/platform/gcp.rb +85 -0
- data/lib/cem_acpt/platform/vmpooler.rb +24 -0
- data/lib/cem_acpt/platform.rb +103 -0
- data/lib/cem_acpt/puppet_helpers.rb +38 -0
- data/lib/cem_acpt/runner.rb +304 -0
- data/lib/cem_acpt/shared_objects.rb +416 -0
- data/lib/cem_acpt/spec_helper_acceptance.rb +176 -0
- data/lib/cem_acpt/test_data.rb +157 -0
- data/lib/cem_acpt/utils.rb +70 -0
- data/lib/cem_acpt/version.rb +5 -0
- data/lib/cem_acpt.rb +27 -0
- data/sample_config.yaml +58 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d8b8813ddfec58349a3ce7c587dbab4a4eae04d7fa24c0ed87896de41ba3d160
|
4
|
+
data.tar.gz: '099f6ed6a7570e2283a740d89bd3864638ff470c8d9a8456bd3eb64ca8d61217'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ce3f4d2ec04e379cea66c728da7d9a0c8b54a339b4023d40bf47e787e96aaf91c4f6fcd4c5513e38b3c3459de32bf575ad2efbfd8c4e440fd60a16fee0a4ced7
|
7
|
+
data.tar.gz: 3f442bebaab6df3e75925ccfd4324a9b38d84c649805ef59a9af407ad097ca0154797d916c7039ae810f5e9db182e58ccd345049e3ad6c41b9fda97405e828c1
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODEOWNERS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* @puppetlabs/abide-team
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cem_acpt (0.1.0)
|
5
|
+
concurrent-ruby (~> 1.1.9)
|
6
|
+
deep_merge (~> 1.2.2)
|
7
|
+
net-scp (~> 3.0)
|
8
|
+
net-ssh (~> 6.1)
|
9
|
+
puppet-modulebuilder (> 0.0.1)
|
10
|
+
rake (> 0.8)
|
11
|
+
rspec (> 0.1)
|
12
|
+
serverspec (> 0.1)
|
13
|
+
|
14
|
+
GEM
|
15
|
+
remote: https://rubygems.org/
|
16
|
+
specs:
|
17
|
+
concurrent-ruby (1.1.9)
|
18
|
+
deep_merge (1.2.2)
|
19
|
+
diff-lcs (1.5.0)
|
20
|
+
minitar (0.9)
|
21
|
+
multi_json (1.15.0)
|
22
|
+
net-scp (3.0.0)
|
23
|
+
net-ssh (>= 2.6.5, < 7.0.0)
|
24
|
+
net-ssh (6.1.0)
|
25
|
+
net-telnet (0.1.1)
|
26
|
+
pathspec (1.1.3)
|
27
|
+
puppet-modulebuilder (0.3.0)
|
28
|
+
minitar (~> 0.9)
|
29
|
+
pathspec (>= 0.2.1, < 2.0.0)
|
30
|
+
rake (12.3.3)
|
31
|
+
rspec (3.11.0)
|
32
|
+
rspec-core (~> 3.11.0)
|
33
|
+
rspec-expectations (~> 3.11.0)
|
34
|
+
rspec-mocks (~> 3.11.0)
|
35
|
+
rspec-core (3.11.0)
|
36
|
+
rspec-support (~> 3.11.0)
|
37
|
+
rspec-expectations (3.11.0)
|
38
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
39
|
+
rspec-support (~> 3.11.0)
|
40
|
+
rspec-its (1.3.0)
|
41
|
+
rspec-core (>= 3.0.0)
|
42
|
+
rspec-expectations (>= 3.0.0)
|
43
|
+
rspec-mocks (3.11.0)
|
44
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
45
|
+
rspec-support (~> 3.11.0)
|
46
|
+
rspec-support (3.11.0)
|
47
|
+
serverspec (2.41.8)
|
48
|
+
multi_json
|
49
|
+
rspec (~> 3.0)
|
50
|
+
rspec-its
|
51
|
+
specinfra (~> 2.72)
|
52
|
+
sfl (2.3)
|
53
|
+
specinfra (2.82.2)
|
54
|
+
net-scp
|
55
|
+
net-ssh (>= 2.7)
|
56
|
+
net-telnet (= 0.1.1)
|
57
|
+
sfl
|
58
|
+
|
59
|
+
PLATFORMS
|
60
|
+
ruby
|
61
|
+
|
62
|
+
DEPENDENCIES
|
63
|
+
cem_acpt!
|
64
|
+
rake (~> 12.0)
|
65
|
+
rspec (~> 3.0)
|
66
|
+
|
67
|
+
BUNDLED WITH
|
68
|
+
2.1.4
|
data/README.md
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
# CemAcpt
|
2
|
+
|
3
|
+
CemAcpt is an acceptance testing library / command line application for running acceptance tests against the CEM modules. It is heavily inspired by Puppet Litmus. CemAcpt is fully compatible with Puppet Litmus acceptance tests.
|
4
|
+
|
5
|
+
CemAcpt was written to facilitate running a single acceptance test file against one or more test nodes concurrently. This is useful when your module supports a lot of different operating systems and modifies low-level components of those operating systems. For CEM, we manage things such as firewall and bootloader configurations which requires us to test against multiple base images of a single operating system (i.e. RHEL 8 with `iptables` and RHEL 8 with `firewalld`). Additionally, we test our module using both Puppet 6 and Puppet 7. As you can see, we now need to test against four test nodes that are all RHEL 8 with slightly different baseline configs. CemAcpt allows us to do so quickly in parallel.
|
6
|
+
|
7
|
+
Major differences between Puppet Litmus and CemAcpt are:
|
8
|
+
|
9
|
+
- CemAcpt revolves around mapping a single acceptance test file to one or more test hosts.
|
10
|
+
- Test node image names can be dynamically defined by configurable parsing of the acceptance test name.
|
11
|
+
- This allows your acceptance test names to define the node it will run against.
|
12
|
+
- CemAcpt has it's own CLI for running provisioning of hosts and the test suite and does not use `rake`.
|
13
|
+
- CemAcpt runs everything, including provisioning the test node, creating a temporary manifest on the test node, and running the acceptance test on the test node, in parallel. No matter how many test / host combinations there are, running the test suite will only take as long as the longest running single test / host combo.
|
14
|
+
- CemAcpt is configurable via a YAML file.
|
15
|
+
|
16
|
+
## Concepts
|
17
|
+
|
18
|
+
CemAcpt is underpinned with a few core concepts. These concepts shape the architecture and functionality of CemAcpt.
|
19
|
+
|
20
|
+
### Platforms
|
21
|
+
|
22
|
+
Platforms are backends that handle provisioning, interacting with, and destroying test nodes. Platforms typically represent something like a cloud provider or hypervisor, but as long as a platform implements the required methods it could do anything.
|
23
|
+
|
24
|
+
Platforms are created by creating a file in `lib/cem_acpt/platform`. The name of the platform file is significant as it will be capitalized and used as a classname. PLATFORMS MUST implement the methods stipulated by `lib/cem_acpt/platform/base.rb` in a single module called `Platform` because the platform system will dynamically create the platform class from this module. How the methods are implemented is irrelevant as long as they take the correct input and give the correct output.
|
25
|
+
|
26
|
+
Platforms will be passed all configuration options under the top-level key `node_data`. Node configuration should be generic and should apply to all test nodes regardless of the test being ran against them. For test-specific configurations, use `test data`.
|
27
|
+
|
28
|
+
See [lib/cem_acpt/platform/gcp.rb](lib/cem_acpt/platform/gcp.rb) for an example of a platform file.
|
29
|
+
|
30
|
+
### Tests
|
31
|
+
|
32
|
+
Each acceptance test should be specified in the config under the top-level key `tests`. These tests SHOULD NOT be paths and should not contain the suffix `_spec.rb`. For example, if you have an acceptance test `spec/acceptance/our_acceptance_test_spec.rb`, the tests config would look like this:
|
33
|
+
|
34
|
+
```yaml
|
35
|
+
tests:
|
36
|
+
- our_acceptance_test
|
37
|
+
```
|
38
|
+
|
39
|
+
Aside from ways of manipulating test data outlined below, tests are typically matched one-to-one with a test node.
|
40
|
+
|
41
|
+
### Test data
|
42
|
+
|
43
|
+
Test data is a collection of data about a specific test that persists through the entire acceptance test suite lifecycle. Specifically, test data is implemented as an array of hashes with each hash representing a single run through the acceptance test suite lifecycle. What this means is that each item in the test data array is couple with a single test node that is provisioned and a single test run against that node. Test data is used to store values that are test-specific, as opposed to node data which is generic.
|
44
|
+
|
45
|
+
Test data can be configured using the top-level key `test_data`. There are several supported options for manipulating test data:
|
46
|
+
|
47
|
+
#### for_each
|
48
|
+
|
49
|
+
When specified, should be a hash of `key -> Array` pairs. For each of these pairs, a copy of the test data is made for each item in the array, with that item becoming the value of a variable `key`.
|
50
|
+
|
51
|
+
Example:
|
52
|
+
|
53
|
+
```yaml
|
54
|
+
test_data:
|
55
|
+
for_each:
|
56
|
+
collection:
|
57
|
+
- puppet6
|
58
|
+
- puppet7
|
59
|
+
|
60
|
+
tests:
|
61
|
+
- our_acceptance_test
|
62
|
+
```
|
63
|
+
|
64
|
+
In the above config, instead of test data consisting of a single hash, it will have two hashes that have the `collection` variable set to `puppet6` and `puppet7` respectively. This means that there will be two test nodes provisioned and two runs of the test `our_acceptance_test`, one run against each node.
|
65
|
+
|
66
|
+
#### vars
|
67
|
+
|
68
|
+
Aribitrary key-value pairs that are injected into the test data hashes. Think of these as constants.
|
69
|
+
|
70
|
+
#### name_pattern_vars
|
71
|
+
|
72
|
+
A Ruby regex pattern that is matched against the test name with the intent of creating variables from named capture groups. See [sample_config.yaml](sample_config.yaml) for an example.
|
73
|
+
|
74
|
+
#### vars_post_processing
|
75
|
+
|
76
|
+
Rules that allow for processing variables after all other test data rules are ran. See [sample_config.yaml](sample_config.yaml) for an example.
|
77
|
+
|
78
|
+
### Image name builder
|
79
|
+
|
80
|
+
Much like `name_pattern_vars`, specifying the `image_name_builder` top-level key in the config allows you to manipulate acceptance test names to create a special test data variable called `image_name`. This is helpful for when you have multiple platform base images and want to use the correct image with the correct test. See [sample_config.yaml](sample_config.yaml) for an example.
|
81
|
+
|
82
|
+
## Installation
|
83
|
+
|
84
|
+
Add this line to your module's Gemfile (usually under `group :development`):
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
gem 'cem_acpt', require: false
|
88
|
+
```
|
89
|
+
|
90
|
+
Then, add the following to your `spec_helper_acceptance.rb` (replacing the Puppet Litmus configuration lines):
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
require 'cem_acpt'
|
94
|
+
|
95
|
+
CemAcpt.configure_spec_helper!
|
96
|
+
```
|
97
|
+
|
98
|
+
To use CemAcpt in your acceptance tests, you must call the method `initialize_test_environment!` in your acceptance test's `describe` block before doing anything else:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
describe 'cem_linux CIS Level 1' do
|
102
|
+
initialize_test_environment!
|
103
|
+
...
|
104
|
+
```
|
105
|
+
|
106
|
+
## Usage
|
107
|
+
|
108
|
+
### RSpec methods
|
109
|
+
|
110
|
+
CemAcpt enables you to use all ServerSpec methods in your acceptance tests, and adds three new methods that can be used (these methods are used the same as in Litmus):
|
111
|
+
|
112
|
+
- `apply_manifest(manifest, opts = {})`: Applies a Puppet manifest given as a string.
|
113
|
+
- `idempotent_apply(manifest, opts = {})`: Applies a Puppet manifest given as a string twice and fails if the second apply reports changes.
|
114
|
+
- `run_shell(command, opts = {})`: Runs a shell command against the test node.
|
115
|
+
|
116
|
+
### Config file
|
117
|
+
|
118
|
+
CemAcpt can use a config file (default path is `./cem_acpt_config.yaml`) to configure it's settings. Below are some of the options available in the config file:
|
119
|
+
|
120
|
+
- `tests`: An array of test names you want to run. These are assumed to exist in the path `spec/acceptance`. You should leave off the suffix `_spec.rb` from the test file names.
|
121
|
+
- `platform`: The backend platform that acceptance tests will run on. Currently, only `gcp` is supported.
|
122
|
+
- `test_data`: Configurations for test data. Allows assigning variables to test data either dynamically or statically.
|
123
|
+
- `node_data`: Configurations for nodes created by the platform. The available options are platform-dependent.
|
124
|
+
- `image_name_builder`: If this key is specified, platforms will be passed a dynamically generated image name based on the configurations under this key.
|
125
|
+
|
126
|
+
### Semantic acceptance test names
|
127
|
+
|
128
|
+
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
|
+
|
130
|
+
#### Test data
|
131
|
+
|
132
|
+
Test data can
|
133
|
+
|
134
|
+
## Development
|
135
|
+
|
136
|
+
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.
|
137
|
+
|
138
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
139
|
+
|
140
|
+
## Contributing
|
141
|
+
|
142
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/cem_acpt.
|
143
|
+
|
144
|
+
## License
|
145
|
+
|
146
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "cem_acpt"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/cem_acpt.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/cem_acpt/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'cem_acpt'
|
7
|
+
spec.version = CemAcpt::VERSION
|
8
|
+
spec.authors = ['Heston Snodgrass']
|
9
|
+
spec.email = ['hsnodgrass3@gmail.com']
|
10
|
+
|
11
|
+
spec.summary = 'CEM Acceptance Tests'
|
12
|
+
spec.description = 'Litmus-like library focusing on CEM Acceptance Tests'
|
13
|
+
spec.homepage = 'https://github.com/puppetlabs/cem_acpt'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
16
|
+
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/puppetlabs/cem_acpt'
|
19
|
+
spec.metadata['changelog_uri'] = 'https://github.com/puppetlabs/cem_acpt'
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
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
|
24
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
end
|
26
|
+
spec.bindir = 'exe'
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
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'
|
37
|
+
end
|
data/exe/cem_acpt
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'deep_merge'
|
5
|
+
require 'json'
|
6
|
+
require 'optparse'
|
7
|
+
require 'yaml'
|
8
|
+
require 'cem_acpt'
|
9
|
+
|
10
|
+
options = {}
|
11
|
+
parser = OptionParser.new do |opts|
|
12
|
+
opts.banner = 'Usage: cem_acpt [options]'
|
13
|
+
|
14
|
+
opts.on('-D', '--debug', 'Enable debug logging') do
|
15
|
+
options[:log_level] = 'debug'
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on('-L', '--log-file FILE', 'Log to FILE') do |file|
|
19
|
+
options[:log_file] = file
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on('-O', '--options OPTS', 'Set options. Example: -P "param1=value1,param2=value2"') do |o|
|
23
|
+
params = o.split(',').map { |s| s.split('=') }.to_h
|
24
|
+
params.transform_keys(&:to_sym).each do |k, v|
|
25
|
+
options[k] = v
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-p', '--platform PLATFORM', 'Set platform. Example: -p "gcp"') do |p|
|
30
|
+
options[:platform] = p
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-m', '--module-dir DIR', 'Set module directory. Example: -m "/tmp/module"') do |m|
|
34
|
+
options[:module_dir] = m
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on('-c', '--config-file FILE', 'Set config file. Example: -c "/tmp/config.yaml"') do |c|
|
38
|
+
options[:config_file] = c
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on('-I', '--CI', 'Run in CI mode') do
|
42
|
+
options[:CI] = true
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on('-E', '--no-destroy-nodes', 'Do not destroy nodes') do
|
46
|
+
options[:no_destroy_nodes] = true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
parser.parse!
|
51
|
+
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'
|
54
|
+
puts '#################### RUNNING ACCEPTANCE TEST SUITE ####################'
|
55
|
+
puts "Using options from command line: #{options}"
|
56
|
+
puts '#######################################################################'
|
57
|
+
end
|
58
|
+
CemAcpt.run(options)
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'operating_system'
|
4
|
+
|
5
|
+
# Currently unused in this implementation.
|
6
|
+
module CemAcpt::Bootstrap
|
7
|
+
# This class runs bootstrapping commands on the VM after it
|
8
|
+
# is provisioned. This can include installing repos,
|
9
|
+
# installing Puppet, installing other programs,
|
10
|
+
# starting services, and running other commands.
|
11
|
+
class Bootstrapper
|
12
|
+
STATE_ORDER = [:repo, :package, :service, :command, :complete].freeze
|
13
|
+
STATE_ACTIONS = {
|
14
|
+
repo: :run_repo_installs,
|
15
|
+
package: :run_package_installs,
|
16
|
+
service: :run_service_starts,
|
17
|
+
command: :run_commands,
|
18
|
+
}.freeze
|
19
|
+
UNIVERSAL_DEPS = [
|
20
|
+
'git',
|
21
|
+
'curl',
|
22
|
+
'puppet-agent',
|
23
|
+
].freeze
|
24
|
+
include CemAcpt::Bootstrap::OperatingSystem
|
25
|
+
|
26
|
+
def initialize(instance_name, instance_image, cmd, collection: 'puppet7', repos: [], packages: [], services: [], commands: [])
|
27
|
+
os, major_version = instance_image.split('-')
|
28
|
+
use_os(os)
|
29
|
+
@instance_name = instance_name.split('.').first
|
30
|
+
@cmd_provider = cmd
|
31
|
+
@collection = collection
|
32
|
+
@os = os
|
33
|
+
@major_version = major_version
|
34
|
+
@repos = repos
|
35
|
+
@packages = packages
|
36
|
+
@services = services
|
37
|
+
@commands = commands
|
38
|
+
@logfile = '/var/log/bootstrap.log'
|
39
|
+
@state = 0
|
40
|
+
@retries = 0
|
41
|
+
@results = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def ruby_version
|
45
|
+
return @ruby_version if defined?(@ruby_version)
|
46
|
+
|
47
|
+
@ruby_version = determine_ruby_version(@collection)
|
48
|
+
@ruby_version
|
49
|
+
end
|
50
|
+
|
51
|
+
def repos
|
52
|
+
@repos.push(puppet_agent_repo).uniq
|
53
|
+
end
|
54
|
+
|
55
|
+
def packages
|
56
|
+
(@packages + UNIVERSAL_DEPS).uniq
|
57
|
+
end
|
58
|
+
|
59
|
+
def services
|
60
|
+
@services.uniq
|
61
|
+
end
|
62
|
+
|
63
|
+
def commands
|
64
|
+
@commands + ruby_install_commands
|
65
|
+
end
|
66
|
+
|
67
|
+
def instance_available?
|
68
|
+
@instance_available ||= test_connection
|
69
|
+
end
|
70
|
+
|
71
|
+
def run
|
72
|
+
raise 'Instance is not available' unless instance_available?
|
73
|
+
|
74
|
+
@results = {}
|
75
|
+
transition_state
|
76
|
+
@results
|
77
|
+
rescue StandardError => e
|
78
|
+
return_results_with_error(e)
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def return_results_with_error(error)
|
84
|
+
@results[:_error] = {
|
85
|
+
error: error,
|
86
|
+
details: {
|
87
|
+
instance_name: @instance_name,
|
88
|
+
instance_image: @instance_image,
|
89
|
+
collection: @collection,
|
90
|
+
os: @os,
|
91
|
+
major_version: @major_version,
|
92
|
+
repos: repos,
|
93
|
+
packages: packages,
|
94
|
+
services: services,
|
95
|
+
commands: commands,
|
96
|
+
state: @state,
|
97
|
+
retries: @retries,
|
98
|
+
},
|
99
|
+
}
|
100
|
+
@results
|
101
|
+
end
|
102
|
+
|
103
|
+
def execute_state_action
|
104
|
+
state_sym = STATE_ORDER[@state]
|
105
|
+
send(STATE_ACTIONS[state_sym])
|
106
|
+
end
|
107
|
+
|
108
|
+
def transition_state
|
109
|
+
raise @last_error if @results.key?(:_error)
|
110
|
+
return if @state == STATE_ORDER.length - 1
|
111
|
+
|
112
|
+
begin
|
113
|
+
execute_state_action
|
114
|
+
rescue StandardError => e
|
115
|
+
@last_error_time = Time.now
|
116
|
+
@last_errored_state = @state
|
117
|
+
@last_error = e
|
118
|
+
handle_state_error
|
119
|
+
end
|
120
|
+
@state += 1
|
121
|
+
transition_state
|
122
|
+
end
|
123
|
+
|
124
|
+
def run_repo_installs
|
125
|
+
raise 'Instance is not available' unless instance_available?
|
126
|
+
|
127
|
+
run_ssh_command_with_formatting(repo_install_cmd(repos), :repo_installs)
|
128
|
+
end
|
129
|
+
|
130
|
+
def run_package_installs
|
131
|
+
raise 'Instance is not available' unless instance_available?
|
132
|
+
|
133
|
+
run_ssh_command_with_formatting(package_install_cmd(packages), :package_installs)
|
134
|
+
end
|
135
|
+
|
136
|
+
def run_service_starts
|
137
|
+
raise 'Instance is not available' unless instance_available?
|
138
|
+
|
139
|
+
run_ssh_command_with_formatting(service_start_cmd(services), :service_starts)
|
140
|
+
end
|
141
|
+
|
142
|
+
def run_commands
|
143
|
+
raise 'Instance is not available' unless instance_available?
|
144
|
+
|
145
|
+
run_ssh_command_with_formatting(commands.join(';'), :command_runs)
|
146
|
+
end
|
147
|
+
|
148
|
+
def handle_state_error
|
149
|
+
@retries = 0 unless @last_errored_state == @state
|
150
|
+
|
151
|
+
raise @last_error unless @retries < 3
|
152
|
+
|
153
|
+
@retries += 1
|
154
|
+
@state = @last_errored_state
|
155
|
+
transition_state
|
156
|
+
end
|
157
|
+
|
158
|
+
def run_ssh_command_with_formatting(cmd, results_key, log_file: '/tmp/puppet-bootstrap.log')
|
159
|
+
key = results_key.to_sym
|
160
|
+
@results[key] = {}
|
161
|
+
@results[key][:started] = Time.now
|
162
|
+
if cmd.nil? || cmd.empty?
|
163
|
+
@results[key][:results] = 'No command(s) to run'
|
164
|
+
@results[key][:finished] = Time.now
|
165
|
+
return
|
166
|
+
end
|
167
|
+
cmd = "#{cmd} | tee -a #{log_file}"
|
168
|
+
ssh_results = @cmd_provider.ssh(@instance_name, cmd)
|
169
|
+
@results[key][:results] = ssh_results.gsub(%r{\s+}, ' ').split('\n')
|
170
|
+
@results[key][:finished] = Time.now
|
171
|
+
end
|
172
|
+
|
173
|
+
def verify_command(results_key)
|
174
|
+
send("verify_#{results_key}".to_sym)
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_connection
|
178
|
+
return true if @cmd_provider.ssh_ready?(@instance_name)
|
179
|
+
|
180
|
+
raise "Connection test failed for #{@instance_name}"
|
181
|
+
end
|
182
|
+
|
183
|
+
def determine_ruby_version(collection)
|
184
|
+
case collection
|
185
|
+
when 'puppet7'
|
186
|
+
'2.7'
|
187
|
+
when 'puppet6'
|
188
|
+
'2.5'
|
189
|
+
else
|
190
|
+
raise "Cannot determine Ruby version, unsupported collection: #{collection}"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def ruby_install_commands
|
195
|
+
[
|
196
|
+
'curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer -o rvm-installer',
|
197
|
+
'curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer.asc -o rvm-installer.asc',
|
198
|
+
'chmod +x rvm-installer',
|
199
|
+
'gpg --verify rvm-installer.asc rvm-installer && sudo ./rvm-installer',
|
200
|
+
"sudo rvm install #{ruby_version} && sudo rvm use #{ruby_version} --default",
|
201
|
+
'gem install bundler',
|
202
|
+
'[[ -f ./Gemfile ]] && bundle install',
|
203
|
+
]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|