agentless-catalog-executor 0.10.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +37 -2
- data/CODEOWNERS +2 -0
- data/Dockerfile +1 -1
- data/README.md +1 -1
- data/agentless-catalog-executor.gemspec +1 -1
- data/developer-docs/api.md +7 -0
- data/developer-docs/docker.md +3 -1
- data/lib/ace/file_mutex.rb +29 -0
- data/lib/ace/fork_util.rb +5 -2
- data/lib/ace/plugin_cache.rb +21 -15
- data/lib/ace/puppet_util.rb +35 -14
- data/lib/ace/schemas/ace-execute_catalog.json +26 -1
- data/lib/ace/schemas/ace-run_task.json +1 -2
- data/lib/ace/transport_app.rb +166 -39
- data/lib/ace/version.rb +1 -1
- data/lib/puppet/indirector/catalog/certless.rb +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 603248d8cb9b344650b57553ed3f415e94964b5c49fb1adcfa38b3e14adbfc8a
|
4
|
+
data.tar.gz: dedfb986987cb291c4a5277b269a25da626f875854ba44b5a7935aabc3851f67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e408493f6327f58cc447a38e7de50cc39bee52d99dd98bbfa4ae66c24744e0c27a5d384bd2d04448c0004915367028f927d4c7587fdcfcfa03b326f0bb5d281d
|
7
|
+
data.tar.gz: cd98abcf1b810d881dddcf1c0207f8ec0a3c3cf62e7da76fa9cd87bd1416e379da5a9ffbb094a0d27dea4ba5ae3477b40006ab909a6391179e949a24e741c6de
|
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,32 @@
|
|
3
3
|
All significant changes to this repo will be summarized in this file.
|
4
4
|
|
5
5
|
|
6
|
+
## [v1.0.0](https://github.com/puppetlabs/ace/tree/v1.0.0) (2019-10-08)
|
7
|
+
[Full Changelog](https://github.com/puppetlabs/ace/compare/v0.10.0...v1.0.0)
|
8
|
+
|
9
|
+
**Implemented enhancements:**
|
10
|
+
|
11
|
+
- \(FM-8503\) implement transport loading if there is no device.rb shim [\#55](https://github.com/puppetlabs/ace/pull/55) ([Lavinia-Dan](https://github.com/Lavinia-Dan))
|
12
|
+
- \(FM-8485\) - Addition of CODEOWNERS file [\#53](https://github.com/puppetlabs/ace/pull/53) ([david22swan](https://github.com/david22swan))
|
13
|
+
- \(FM-8446\) remove remote-transport requirement [\#50](https://github.com/puppetlabs/ace/pull/50) ([DavidS](https://github.com/DavidS))
|
14
|
+
- \(PE-27029\) introduce enforce\_environment to support strict mode [\#49](https://github.com/puppetlabs/ace/pull/49) ([DavidS](https://github.com/DavidS))
|
15
|
+
- \(PE-27024\) return detailed results from `/execute\_catalog` [\#48](https://github.com/puppetlabs/ace/pull/48) ([DavidS](https://github.com/DavidS))
|
16
|
+
|
17
|
+
**Fixed bugs:**
|
18
|
+
|
19
|
+
- \(FM-8566\) Add additional error handling for /run\_task [\#60](https://github.com/puppetlabs/ace/pull/60) ([da-ar](https://github.com/da-ar))
|
20
|
+
- \(FM-8497\) Ensure cross-process mutexing [\#59](https://github.com/puppetlabs/ace/pull/59) ([da-ar](https://github.com/da-ar))
|
21
|
+
- \(FM-8481\) Add missing headers for native extensions [\#51](https://github.com/puppetlabs/ace/pull/51) ([da-ar](https://github.com/da-ar))
|
22
|
+
- \\(PE-27024\\) return detailed results from `/execute\\_catalog` [\#48](https://github.com/puppetlabs/ace/pull/48) ([DavidS](https://github.com/DavidS))
|
23
|
+
|
24
|
+
**Merged pull requests:**
|
25
|
+
|
26
|
+
- \(maint\) rubocop fixes for RSpec/EmptyLineAfterExample [\#61](https://github.com/puppetlabs/ace/pull/61) ([da-ar](https://github.com/da-ar))
|
27
|
+
- \(FM-8496\) Add support for Puppet debug flags during /execute\_catalog [\#58](https://github.com/puppetlabs/ace/pull/58) ([david22swan](https://github.com/david22swan))
|
28
|
+
- \(maint\) Do not follow spec test found in `Volumes` [\#56](https://github.com/puppetlabs/ace/pull/56) ([da-ar](https://github.com/da-ar))
|
29
|
+
- \(maint\) various cleanups [\#52](https://github.com/puppetlabs/ace/pull/52) ([DavidS](https://github.com/DavidS))
|
30
|
+
- \(maint\) using the CA\_ALLOW\_SUBJECT\_ALT\_NAMES env variable for new doc… [\#47](https://github.com/puppetlabs/ace/pull/47) ([Thomas-Franklin](https://github.com/Thomas-Franklin))
|
31
|
+
|
6
32
|
## [v0.10.0](https://github.com/puppetlabs/ace/tree/v0.10.0) (2019-07-25)
|
7
33
|
[Full Changelog](https://github.com/puppetlabs/ace/compare/v0.9.1...v0.10.0)
|
8
34
|
|
@@ -31,6 +57,8 @@ All significant changes to this repo will be summarized in this file.
|
|
31
57
|
- \(FM-7927\) Docs review [\#35](https://github.com/puppetlabs/ace/pull/35) ([clairecadman](https://github.com/clairecadman))
|
32
58
|
|
33
59
|
## [v0.9.0](https://github.com/puppetlabs/ace/tree/v0.9.0) (2019-04-16)
|
60
|
+
[Full Changelog](https://github.com/puppetlabs/ace/compare/0.1.0...v0.9.0)
|
61
|
+
|
34
62
|
**Implemented enhancements:**
|
35
63
|
|
36
64
|
- \(FM-7922\) running the configuration to apply catalog to transport [\#30](https://github.com/puppetlabs/ace/pull/30) ([Thomas-Franklin](https://github.com/Thomas-Franklin))
|
@@ -39,8 +67,6 @@ All significant changes to this repo will be summarized in this file.
|
|
39
67
|
- \(FM-7883\) execute plugin sync from a puppetserver [\#20](https://github.com/puppetlabs/ace/pull/20) ([Thomas-Franklin](https://github.com/Thomas-Franklin))
|
40
68
|
- \(FM-7826\) first pass of execute catalog docs and mock API endpoint [\#16](https://github.com/puppetlabs/ace/pull/16) ([DavidS](https://github.com/DavidS))
|
41
69
|
- Utilities for environment isolation per request [\#12](https://github.com/puppetlabs/ace/pull/12) ([willmeek](https://github.com/willmeek))
|
42
|
-
- \(PE-25514\) Add docker support for ACE [\#3](https://github.com/puppetlabs/ace/pull/3) ([da-ar](https://github.com/da-ar))
|
43
|
-
- \(PE-25508\) Add JSON Schema and validation example [\#2](https://github.com/puppetlabs/ace/pull/2) ([da-ar](https://github.com/da-ar))
|
44
70
|
|
45
71
|
**Fixed bugs:**
|
46
72
|
|
@@ -64,6 +90,15 @@ All significant changes to this repo will be summarized in this file.
|
|
64
90
|
- Update README.md [\#10](https://github.com/puppetlabs/ace/pull/10) ([willmeek](https://github.com/willmeek))
|
65
91
|
- \(maint\) reworking of the configuration [\#5](https://github.com/puppetlabs/ace/pull/5) ([Thomas-Franklin](https://github.com/Thomas-Franklin))
|
66
92
|
- Update JSONSchema, and mock endpoint [\#4](https://github.com/puppetlabs/ace/pull/4) ([da-ar](https://github.com/da-ar))
|
93
|
+
|
94
|
+
## [0.1.0](https://github.com/puppetlabs/ace/tree/0.1.0) (2018-11-30)
|
95
|
+
**Implemented enhancements:**
|
96
|
+
|
97
|
+
- \(PE-25514\) Add docker support for ACE [\#3](https://github.com/puppetlabs/ace/pull/3) ([da-ar](https://github.com/da-ar))
|
98
|
+
- \(PE-25508\) Add JSON Schema and validation example [\#2](https://github.com/puppetlabs/ace/pull/2) ([da-ar](https://github.com/da-ar))
|
99
|
+
|
100
|
+
**Merged pull requests:**
|
101
|
+
|
67
102
|
- \(PE-25509\) first fake endpoint; rubocop [\#1](https://github.com/puppetlabs/ace/pull/1) ([DavidS](https://github.com/DavidS))
|
68
103
|
|
69
104
|
|
data/CODEOWNERS
ADDED
data/Dockerfile
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
FROM puppet/puppet-agent-alpine:6.4.2 as build
|
3
3
|
|
4
4
|
RUN \
|
5
|
-
apk --no-cache add build-base ruby-dev ruby-bundler ruby-json ruby-bigdecimal git openssl-dev && \
|
5
|
+
apk --no-cache add build-base ruby-dev ruby-bundler ruby-json ruby-bigdecimal git openssl-dev linux-headers && \
|
6
6
|
echo 'gem: --no-document' > /etc/gemrc && \
|
7
7
|
bundle config --global silence_root_warning 1
|
8
8
|
|
data/README.md
CHANGED
@@ -18,7 +18,7 @@ ACE is built-in to PE as pe-ace-server.
|
|
18
18
|
|
19
19
|
## Development
|
20
20
|
|
21
|
-
As ACE is dependent on Puppet Server, there is a docker-compose file in the `spec/` directory which we advise you run before
|
21
|
+
As ACE is dependent on Puppet Server, there is a docker-compose file in the `spec/` directory which we advise you run before the ACE service to ensure that the certs and keys are valid. For more information, see the [docker documentation](developer-docs/docker.md).
|
22
22
|
|
23
23
|
To release a new version, update the version number in `version.rb`, generate a new changelog with `bundle exec rake changelog`, commit the results and run `bundle exec rake release`, which creates a git tag for the version, pushes git commits and tags, and pushes the `.gem` file to [rubygems.org](https://rubygems.org). Released gems are eventually consumed by [ace-vanagon](https://github.com/puppetlabs/ace-vanagon) and promoted into PE.
|
24
24
|
|
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
-
spec.add_dependency "bolt", "~> 1.
|
23
|
+
spec.add_dependency "bolt", "~> 1.31"
|
24
24
|
|
25
25
|
# server-side dependencies cargo culted from https://github.com/puppetlabs/bolt/blob/4418da408643aa7eb5ed64f4c9704b941ea878dc/Gemfile#L10-L16
|
26
26
|
spec.add_dependency "hocon", ">= 1.2.5"
|
data/developer-docs/api.md
CHANGED
@@ -106,6 +106,13 @@ The `compiler` is a JSON object which contains parameters regarding the compilat
|
|
106
106
|
* `transaction_uuid`
|
107
107
|
* `job_id`
|
108
108
|
|
109
|
+
The compiler also contains several parameters that can be used in order to help to debug your request, these being:
|
110
|
+
|
111
|
+
* `noop`
|
112
|
+
* `debug`
|
113
|
+
* `trace`
|
114
|
+
* `evaltrace`
|
115
|
+
|
109
116
|
### Task Object
|
110
117
|
This is a copy of [bolt's task object](https://github.com/puppetlabs/bolt/blob/master/developer-docs/bolt-api-servers.md#task-object)
|
111
118
|
|
data/developer-docs/docker.md
CHANGED
@@ -31,7 +31,9 @@ sudo chmod a+rx -R volumes/
|
|
31
31
|
|
32
32
|
At this point it is required to generate certs for the `aceserver`, this can be achieved though:
|
33
33
|
|
34
|
-
|
34
|
+
```
|
35
|
+
docker exec spec_puppet_1 puppetserver ca generate --certname aceserver --subject-alt-names localhost,aceserver,ace_aceserver_1,spec_puppetserver_1,ace_server,puppet_server,spec_aceserver_1,puppetdb,spec_puppetdb_1,0.0.0.0,puppet,spec_puppet_1,ace_aceserver_1
|
36
|
+
```
|
35
37
|
|
36
38
|
On Linux, ensure that you have access to the newly created files:
|
37
39
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
module ACE
|
6
|
+
class FileMutex
|
7
|
+
def initialize(lock_file)
|
8
|
+
@lock_file = lock_file
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_read_lock
|
12
|
+
fh = File.open(@lock_file, File::CREAT)
|
13
|
+
fh.flock(File::LOCK_SH)
|
14
|
+
yield
|
15
|
+
ensure
|
16
|
+
fh.flock(File::LOCK_UN)
|
17
|
+
fh.close
|
18
|
+
end
|
19
|
+
|
20
|
+
def with_write_lock
|
21
|
+
fh = File.open(@lock_file, File::CREAT)
|
22
|
+
fh.flock(File::LOCK_EX)
|
23
|
+
yield
|
24
|
+
ensure
|
25
|
+
fh.flock(File::LOCK_UN)
|
26
|
+
fh.close
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/ace/fork_util.rb
CHANGED
@@ -28,7 +28,7 @@ module ACE
|
|
28
28
|
}
|
29
29
|
}.to_json)
|
30
30
|
success = false
|
31
|
-
rescue
|
31
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
32
32
|
writer.puts({
|
33
33
|
msg: e.message,
|
34
34
|
kind: e.class,
|
@@ -39,6 +39,7 @@ module ACE
|
|
39
39
|
}.to_json)
|
40
40
|
success = false
|
41
41
|
ensure
|
42
|
+
writer.flush
|
42
43
|
Process.exit! success
|
43
44
|
end
|
44
45
|
# :nocov:
|
@@ -48,11 +49,13 @@ module ACE
|
|
48
49
|
exit 1
|
49
50
|
end
|
50
51
|
writer.close
|
51
|
-
output = reader.
|
52
|
+
output = reader.readlines('')[0]
|
52
53
|
Process.wait(pid)
|
53
54
|
if $CHILD_STATUS != 0
|
54
55
|
error = JSON.parse(output)
|
55
56
|
raise ACE::Error.new(error['msg'], error['kind'], error['details'])
|
57
|
+
elsif output.nil?
|
58
|
+
raise ACE::Error.new('spawned process returned no result', 'puppetlabs/ace/fork_util', 'no details')
|
56
59
|
else
|
57
60
|
JSON.parse(output)
|
58
61
|
end
|
data/lib/ace/plugin_cache.rb
CHANGED
@@ -14,14 +14,21 @@ module ACE
|
|
14
14
|
PURGE_INTERVAL = 24 * PURGE_TIMEOUT
|
15
15
|
PURGE_TTL = 7 * PURGE_INTERVAL
|
16
16
|
|
17
|
-
def initialize(environments_cache_dir
|
17
|
+
def initialize(environments_cache_dir,
|
18
|
+
purge_interval: PURGE_INTERVAL,
|
19
|
+
purge_timeout: PURGE_TIMEOUT,
|
20
|
+
purge_ttl: PURGE_TTL,
|
21
|
+
cache_dir_mutex: Concurrent::ReadWriteLock.new,
|
22
|
+
do_purge: true)
|
18
23
|
@cache_dir = environments_cache_dir
|
19
|
-
@cache_dir_mutex =
|
24
|
+
@cache_dir_mutex = cache_dir_mutex
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
26
|
+
if do_purge
|
27
|
+
@purge = Concurrent::TimerTask.new(execution_interval: purge_interval,
|
28
|
+
timeout_interval: purge_timeout,
|
29
|
+
run_now: true) { expire(purge_ttl) }
|
30
|
+
@purge.execute
|
31
|
+
end
|
25
32
|
end
|
26
33
|
|
27
34
|
def setup
|
@@ -29,9 +36,14 @@ module ACE
|
|
29
36
|
self
|
30
37
|
end
|
31
38
|
|
32
|
-
def with_synced_libdir(environment, certname, &block)
|
39
|
+
def with_synced_libdir(environment, enforce_environment, certname, &block)
|
33
40
|
ForkUtil.isolate do
|
34
|
-
ACE::PuppetUtil.isolated_puppet_settings(
|
41
|
+
ACE::PuppetUtil.isolated_puppet_settings(
|
42
|
+
certname,
|
43
|
+
environment,
|
44
|
+
enforce_environment,
|
45
|
+
environment_dir(environment)
|
46
|
+
)
|
35
47
|
with_synced_libdir_core(environment, &block)
|
36
48
|
end
|
37
49
|
end
|
@@ -65,7 +77,7 @@ module ACE
|
|
65
77
|
def environment_dir(environment)
|
66
78
|
environment_dir = File.join(cache_dir, environment)
|
67
79
|
cache_dir_mutex.with_write_lock do
|
68
|
-
FileUtils.mkdir_p(environment_dir)
|
80
|
+
FileUtils.mkdir_p(File.join(environment_dir, 'code', 'environments', environment))
|
69
81
|
FileUtils.touch(environment_dir)
|
70
82
|
end
|
71
83
|
environment_dir
|
@@ -76,12 +88,6 @@ module ACE
|
|
76
88
|
def sync_core(environment)
|
77
89
|
env = Puppet::Node::Environment.remote(environment)
|
78
90
|
environments_dir = environment_dir(environment)
|
79
|
-
Puppet[:vardir] = File.join(environments_dir)
|
80
|
-
Puppet[:confdir] = File.join(environments_dir, 'conf')
|
81
|
-
Puppet[:rundir] = File.join(environments_dir, 'run')
|
82
|
-
Puppet[:logdir] = File.join(environments_dir, 'log')
|
83
|
-
Puppet[:codedir] = File.join(environments_dir, 'code')
|
84
|
-
Puppet[:plugindest] = File.join(environments_dir, 'plugins')
|
85
91
|
Puppet::Configurer::PluginHandler.new.download_plugins(env)
|
86
92
|
libdir(File.join(environments_dir, 'plugins'))
|
87
93
|
end
|
data/lib/ace/puppet_util.rb
CHANGED
@@ -18,12 +18,12 @@ module ACE
|
|
18
18
|
# as per request settings will be set later on
|
19
19
|
# to satisfy multi-environments
|
20
20
|
Puppet.settings[:vardir] = cachedir
|
21
|
-
Puppet.settings[:confdir] = File.join(cachedir, '
|
22
|
-
Puppet.settings[:rundir] = File.join(cachedir, '
|
23
|
-
Puppet.settings[:logdir] = File.join(cachedir, '
|
24
|
-
Puppet.settings[:codedir] = File.join(cachedir, '
|
25
|
-
Puppet.settings[:plugindest] = File.join(cachedir, '
|
26
|
-
|
21
|
+
Puppet.settings[:confdir] = File.join(cachedir, 'conf_x')
|
22
|
+
Puppet.settings[:rundir] = File.join(cachedir, 'run_x')
|
23
|
+
Puppet.settings[:logdir] = File.join(cachedir, 'log_x')
|
24
|
+
Puppet.settings[:codedir] = File.join(cachedir, 'code_x')
|
25
|
+
Puppet.settings[:plugindest] = File.join(cachedir, 'plugin_x')
|
26
|
+
|
27
27
|
# ssl_context will be a persistent context
|
28
28
|
cert_provider = Puppet::X509::CertProvider.new(
|
29
29
|
capath: ca_cert_path,
|
@@ -35,18 +35,39 @@ module ACE
|
|
35
35
|
private_key: OpenSSL::PKey::RSA.new(File.read(private_key_path, encoding: 'utf-8')),
|
36
36
|
client_cert: OpenSSL::X509::Certificate.new(File.read(client_cert_path, encoding: 'utf-8'))
|
37
37
|
)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
Puppet::Transaction::Report.indirection.terminus_class = :rest
|
38
|
+
# Store SSL settings for reuse in isolated process
|
39
|
+
@ssl_settings = {
|
40
|
+
ssl_context: ssl_context,
|
41
|
+
server: uri.host,
|
42
|
+
serverport: uri.port
|
43
|
+
}
|
45
44
|
end
|
46
45
|
|
47
|
-
def self.isolated_puppet_settings(certname, environment)
|
46
|
+
def self.isolated_puppet_settings(certname, environment, enforce_environment, environment_dir)
|
48
47
|
Puppet.settings[:certname] = certname
|
49
48
|
Puppet.settings[:environment] = environment
|
49
|
+
Puppet.settings[:strict_environment_mode] = enforce_environment
|
50
|
+
|
51
|
+
Puppet.settings[:vardir] = File.join(environment_dir)
|
52
|
+
Puppet.settings[:confdir] = File.join(environment_dir, 'conf')
|
53
|
+
Puppet.settings[:rundir] = File.join(environment_dir, 'run')
|
54
|
+
Puppet.settings[:logdir] = File.join(environment_dir, 'log')
|
55
|
+
Puppet.settings[:codedir] = File.join(environment_dir, 'code')
|
56
|
+
Puppet.settings[:plugindest] = File.join(environment_dir, 'plugins')
|
57
|
+
|
58
|
+
# establish a base_context. This needs to be the first context on the stack, but must not be created
|
59
|
+
# before all settings have been set. For example, this will create a Puppet::Environments::Directories
|
60
|
+
# instance copying the :environmentpath setting and never updating this.
|
61
|
+
Puppet.push_context(Puppet.base_context(Puppet.settings), "Puppet Initialization")
|
62
|
+
Puppet.push_context(@ssl_settings, "PuppetServer connection information to be used")
|
63
|
+
|
64
|
+
# finalise settings initialisation
|
65
|
+
Puppet.settings.use :main, :agent, :ssl
|
66
|
+
|
67
|
+
# special override
|
68
|
+
Puppet::Transaction::Report.indirection.terminus_class = :rest
|
69
|
+
|
70
|
+
# configure the requested environment, and deploy new loaders
|
50
71
|
env = Puppet::Node::Environment.remote(environment)
|
51
72
|
Puppet.push_context({
|
52
73
|
configured_environment: environment,
|
@@ -28,6 +28,10 @@
|
|
28
28
|
"type": "string",
|
29
29
|
"description": "The name of the environment for which to compile the catalog."
|
30
30
|
},
|
31
|
+
"enforce_environment": {
|
32
|
+
"type": "boolean",
|
33
|
+
"description": "Whether to force agents to run in the same environment in which their assigned applications are defined. (This key is required to be false if `environment` is an empty string)."
|
34
|
+
},
|
31
35
|
"transaction_uuid": {
|
32
36
|
"type": "string",
|
33
37
|
"description": "The id for tracking the catalog compilation and report submission."
|
@@ -35,12 +39,33 @@
|
|
35
39
|
"job_id": {
|
36
40
|
"type": "string",
|
37
41
|
"description": "The id of the orchestrator job that triggered this run."
|
42
|
+
},
|
43
|
+
"noop": {
|
44
|
+
"type": "boolean",
|
45
|
+
"description": "The operation should not be applied",
|
46
|
+
"default": false
|
47
|
+
},
|
48
|
+
"debug": {
|
49
|
+
"type": "boolean",
|
50
|
+
"description": "Show up to debug level messages",
|
51
|
+
"default": false
|
52
|
+
},
|
53
|
+
"trace": {
|
54
|
+
"type": "boolean",
|
55
|
+
"description": "Allows for a backtrace to be returned in the event of an exception",
|
56
|
+
"default": false
|
57
|
+
},
|
58
|
+
"evaltrace": {
|
59
|
+
"type": "boolean",
|
60
|
+
"description": "Reports on each step of the Puppet process",
|
61
|
+
"default": false
|
38
62
|
}
|
39
63
|
},
|
40
64
|
"additionalProperties": true,
|
41
65
|
"required": [
|
42
66
|
"certname",
|
43
|
-
"environment"
|
67
|
+
"environment",
|
68
|
+
"enforce_environment"
|
44
69
|
]
|
45
70
|
}
|
46
71
|
},
|
data/lib/ace/transport_app.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'ace/file_mutex'
|
3
4
|
require 'ace/error'
|
4
5
|
require 'ace/fork_util'
|
5
6
|
require 'ace/puppet_util'
|
@@ -18,12 +19,17 @@ require 'puppet/util/network_device/base'
|
|
18
19
|
module ACE
|
19
20
|
class TransportApp < Sinatra::Base
|
20
21
|
def initialize(config = nil)
|
22
|
+
@logger = Logging.logger[self]
|
21
23
|
@config = config
|
22
24
|
@executor = Bolt::Executor.new(0)
|
23
25
|
tasks_cache_dir = File.join(@config['cache-dir'], 'tasks')
|
24
|
-
@
|
25
|
-
|
26
|
-
|
26
|
+
@mutex = ACE::FileMutex.new(Tempfile.new('ace.lock'))
|
27
|
+
@file_cache = BoltServer::FileCache.new(@config.data.merge('cache-dir' => tasks_cache_dir),
|
28
|
+
cache_dir_mutex: @mutex, do_purge: false).setup
|
29
|
+
environments_cache_dir = File.join(@config['cache-dir'], 'environment_cache')
|
30
|
+
@plugins_mutex = ACE::FileMutex.new(Tempfile.new('ace.plugins.lock'))
|
31
|
+
@plugins = ACE::PluginCache.new(environments_cache_dir,
|
32
|
+
cache_dir_mutex: @plugins_mutex, do_purge: false).setup
|
27
33
|
|
28
34
|
@schemas = {
|
29
35
|
"run_task" => JSON.parse(File.read(File.join(__dir__, 'schemas', 'ace-run_task.json'))),
|
@@ -40,6 +46,36 @@ module ACE
|
|
40
46
|
config['cache-dir'],
|
41
47
|
URI.parse(config['puppet-server-uri']))
|
42
48
|
|
49
|
+
ace_pid = Process.pid
|
50
|
+
@logger.info "ACE started: #{ace_pid}"
|
51
|
+
fork do
|
52
|
+
# :nocov:
|
53
|
+
# FileCache and PluginCache cleanup timer started in a seperate fork
|
54
|
+
# so that there is only a single timer responsible for purging old files
|
55
|
+
@logger.info "FileCache process started: #{Process.pid}"
|
56
|
+
@fc_purge = BoltServer::FileCache.new(@config.data.merge('cache-dir' => tasks_cache_dir),
|
57
|
+
cache_dir_mutex: @mutex,
|
58
|
+
do_purge: true)
|
59
|
+
|
60
|
+
@pc_purge = ACE::PluginCache.new(environments_cache_dir,
|
61
|
+
cache_dir_mutex: @plugins_mutex, do_purge: true)
|
62
|
+
loop do
|
63
|
+
begin
|
64
|
+
# is the parent process alibve
|
65
|
+
Process.getpgid(ace_pid)
|
66
|
+
sleep 10 # how often to check if parent process is alive
|
67
|
+
rescue Interrupt
|
68
|
+
# handle ctrl-c event
|
69
|
+
break
|
70
|
+
rescue StandardError
|
71
|
+
# parent is no longer alive
|
72
|
+
break
|
73
|
+
end
|
74
|
+
end
|
75
|
+
@logger.info "FileCache process ended"
|
76
|
+
# :nocov:
|
77
|
+
end
|
78
|
+
|
43
79
|
super(nil)
|
44
80
|
end
|
45
81
|
|
@@ -84,11 +120,20 @@ module ACE
|
|
84
120
|
end
|
85
121
|
|
86
122
|
device_struct = Struct.new(:provider, :url, :name, :options)
|
123
|
+
type = target['remote-transport']
|
87
124
|
# Return device
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
125
|
+
begin
|
126
|
+
require 'puppet/resource_api/transport'
|
127
|
+
transport = Puppet::ResourceApi::Transport.connect(type, url)
|
128
|
+
Puppet::ResourceApi::Transport.inject_device(type, transport)
|
129
|
+
rescue Puppet::DevError, LoadError => e
|
130
|
+
raise e unless e.message.include?("Transport for `#{type}` not registered with") || e.class == LoadError
|
131
|
+
# fallback to puppet device if there's no transport
|
132
|
+
Puppet::Util::NetworkDevice.init(device_struct.new(transport,
|
133
|
+
url,
|
134
|
+
certname,
|
135
|
+
{}))
|
136
|
+
end
|
92
137
|
end
|
93
138
|
|
94
139
|
def scrub_stack_trace(result)
|
@@ -110,6 +155,12 @@ module ACE
|
|
110
155
|
end
|
111
156
|
end
|
112
157
|
|
158
|
+
def nest_metrics(metrics)
|
159
|
+
Hash[metrics.fetch('resources', {}).values.map do |name, _human_name, value|
|
160
|
+
[name, value]
|
161
|
+
end]
|
162
|
+
end
|
163
|
+
|
113
164
|
# returns a hash of trusted facts that will be used
|
114
165
|
# to request a catalog for the target
|
115
166
|
def self.trusted_facts(certname)
|
@@ -156,39 +207,85 @@ module ACE
|
|
156
207
|
begin
|
157
208
|
body = JSON.parse(request.body.read)
|
158
209
|
validate_schema(@schemas["run_task"], body)
|
210
|
+
opts = body['target'].merge('protocol' => 'remote')
|
211
|
+
|
212
|
+
# This is a workaround for Bolt due to the way it expects to receive the target info
|
213
|
+
# see: https://github.com/puppetlabs/bolt/pull/915#discussion_r268280535
|
214
|
+
# Systems calling into ACE will need to determine the nodename/certname and pass this as `name`
|
215
|
+
target = [Bolt::Target.new(body['target']['host'] || body['target']['name'], opts)]
|
159
216
|
rescue ACE::Error => e
|
160
|
-
request_error = {
|
217
|
+
request_error = {
|
218
|
+
"node": target,
|
219
|
+
"target": target,
|
220
|
+
"action": nil,
|
221
|
+
"object": nil,
|
222
|
+
"status": "failure",
|
223
|
+
"result": {
|
224
|
+
"_error": e.to_h
|
225
|
+
}
|
226
|
+
}
|
161
227
|
return [400, request_error.to_json]
|
162
|
-
rescue
|
228
|
+
rescue JSON::ParserError => e
|
163
229
|
request_error = {
|
164
|
-
|
165
|
-
|
166
|
-
|
230
|
+
"node": target,
|
231
|
+
"target": target,
|
232
|
+
"action": nil,
|
233
|
+
"object": nil,
|
234
|
+
"status": "failure",
|
235
|
+
"result": {
|
236
|
+
"_error": ACE::Error.to_h(e.message,
|
237
|
+
'puppetlabs/ace/request_exception',
|
238
|
+
class: e.class, backtrace: e.backtrace).to_h
|
239
|
+
}
|
167
240
|
}
|
168
241
|
return [400, request_error.to_json]
|
242
|
+
rescue StandardError => e
|
243
|
+
request_error = {
|
244
|
+
"node": target,
|
245
|
+
"target": target,
|
246
|
+
"action": nil,
|
247
|
+
"object": nil,
|
248
|
+
"status": "failure",
|
249
|
+
"result": {
|
250
|
+
"_error": ACE::Error.to_h(e.message,
|
251
|
+
'puppetlabs/ace/execution_exception',
|
252
|
+
class: e.class, backtrace: e.backtrace).to_h
|
253
|
+
}
|
254
|
+
}
|
255
|
+
return [500, request_error.to_json]
|
169
256
|
end
|
170
257
|
|
171
|
-
|
172
|
-
|
173
|
-
# This is a workaround for Bolt due to the way it expects to receive the target info
|
174
|
-
# see: https://github.com/puppetlabs/bolt/pull/915#discussion_r268280535
|
175
|
-
# Systems calling into ACE will need to determine the nodename/certname and pass this as `name`
|
176
|
-
target = [Bolt::Target.new(body['target']['host'] || body['target']['name'], opts)]
|
177
|
-
|
178
|
-
inventory = Bolt::Inventory.new(nil)
|
258
|
+
begin
|
259
|
+
inventory = Bolt::Inventory.new(nil)
|
179
260
|
|
180
|
-
|
261
|
+
target.first.inventory = inventory
|
181
262
|
|
182
|
-
|
263
|
+
task = Bolt::Task::PuppetServer.new(body['task'], @file_cache)
|
183
264
|
|
184
|
-
|
265
|
+
parameters = body['parameters'] || {}
|
185
266
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
267
|
+
result = ForkUtil.isolate do
|
268
|
+
# Since this will only be on one node we can just return the first result
|
269
|
+
results = @executor.run_task(target, task, parameters)
|
270
|
+
scrub_stack_trace(results.first.status_hash)
|
271
|
+
end
|
272
|
+
[200, result.to_json]
|
273
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
274
|
+
# handle all the things and make it obvious what happened
|
275
|
+
process_error = {
|
276
|
+
"node": target,
|
277
|
+
"target": target,
|
278
|
+
"action": nil,
|
279
|
+
"object": nil,
|
280
|
+
"status": "failure",
|
281
|
+
"result": {
|
282
|
+
"_error": ACE::Error.to_h(e.message,
|
283
|
+
'puppetlabs/ace/processing_exception',
|
284
|
+
class: e.class, backtrace: e.backtrace).to_h
|
285
|
+
}
|
286
|
+
}
|
287
|
+
return [500, process_error.to_json]
|
190
288
|
end
|
191
|
-
[200, result.to_json]
|
192
289
|
end
|
193
290
|
|
194
291
|
post '/execute_catalog' do
|
@@ -199,6 +296,13 @@ module ACE
|
|
199
296
|
validate_schema(@schemas["execute_catalog"], body)
|
200
297
|
|
201
298
|
environment = body['compiler']['environment']
|
299
|
+
enforce_environment = body['compiler']['enforce_environment']
|
300
|
+
if environment == '' && !enforce_environment
|
301
|
+
environment = 'production'
|
302
|
+
elsif environment == '' && enforce_environment
|
303
|
+
raise ACE::Error.new('You MUST provide an `environment` when `enforce_environment` is set to true',
|
304
|
+
'puppetlabs/ace/execute_catalog')
|
305
|
+
end
|
202
306
|
certname = body['compiler']['certname']
|
203
307
|
trans_id = body['compiler']['transaction_uuid']
|
204
308
|
job_id = body['compiler']['job_id']
|
@@ -223,14 +327,40 @@ module ACE
|
|
223
327
|
end
|
224
328
|
|
225
329
|
begin
|
226
|
-
@plugins.with_synced_libdir(environment, certname) do
|
330
|
+
run_result = @plugins.with_synced_libdir(environment, enforce_environment, certname) do
|
227
331
|
ACE::TransportApp.init_puppet_target(certname, body['target']['remote-transport'], body['target'])
|
332
|
+
|
333
|
+
# Apply compiler flags for Configurer
|
334
|
+
Puppet.settings[:noop] = body['compiler']['noop'] || false
|
335
|
+
# grab the current debug level
|
336
|
+
current_log_level = Puppet.settings[:log_level] if body['compiler']['debug']
|
337
|
+
# apply debug level if its specified
|
338
|
+
Puppet.settings[:log_level] = :debug if body['compiler']['debug']
|
339
|
+
Puppet.settings[:trace] = body['compiler']['trace'] || false
|
340
|
+
Puppet.settings[:evaltrace] = body['compiler']['evaltrace'] || false
|
341
|
+
|
228
342
|
configurer = ACE::Configurer.new(body['compiler']['transaction_uuid'], body['compiler']['job_id'])
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
343
|
+
options = { transport_name: certname,
|
344
|
+
environment: environment,
|
345
|
+
network_device: true,
|
346
|
+
pluginsync: false,
|
347
|
+
trusted_facts: ACE::TransportApp.trusted_facts(certname) }
|
348
|
+
configurer.run(options)
|
349
|
+
# return logging level back to original
|
350
|
+
Puppet.settings[:log_level] = current_log_level if body['compiler']['debug']
|
351
|
+
# `options[:report]` gets populated by configurer.run with the report of the run with a
|
352
|
+
# Puppet::Transaction::Report instance
|
353
|
+
# see https://github.com/puppetlabs/puppet/blob/c956ad95fcdd9aabb28e196b55d1f112b5944777/lib/puppet/configurer.rb#L211
|
354
|
+
report = options[:report]
|
355
|
+
# remember that this hash gets munged by fork's json serialising
|
356
|
+
{
|
357
|
+
'time' => report.time,
|
358
|
+
'transaction_uuid' => trans_id,
|
359
|
+
'environment' => report.environment,
|
360
|
+
'status' => report.status,
|
361
|
+
'metrics' => nest_metrics(report.metrics),
|
362
|
+
'job_id' => job_id
|
363
|
+
}
|
234
364
|
end
|
235
365
|
rescue ACE::Error => e
|
236
366
|
process_error = {
|
@@ -255,11 +385,8 @@ module ACE
|
|
255
385
|
else
|
256
386
|
result = {
|
257
387
|
certname: certname,
|
258
|
-
status: '
|
259
|
-
result:
|
260
|
-
transaction_uuid: trans_id,
|
261
|
-
job_id: job_id
|
262
|
-
}
|
388
|
+
status: run_result.delete('status'),
|
389
|
+
result: run_result
|
263
390
|
}
|
264
391
|
[200, result.to_json]
|
265
392
|
end
|
data/lib/ace/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: agentless-catalog-executor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Schmitt
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bolt
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.31'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.31'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: hocon
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -211,6 +211,7 @@ files:
|
|
211
211
|
- ".rubocop.yml"
|
212
212
|
- ".travis.yml"
|
213
213
|
- CHANGELOG.md
|
214
|
+
- CODEOWNERS
|
214
215
|
- Dockerfile
|
215
216
|
- Gemfile
|
216
217
|
- LICENSE
|
@@ -239,6 +240,7 @@ files:
|
|
239
240
|
- lib/ace/config.rb
|
240
241
|
- lib/ace/configurer.rb
|
241
242
|
- lib/ace/error.rb
|
243
|
+
- lib/ace/file_mutex.rb
|
242
244
|
- lib/ace/fork_util.rb
|
243
245
|
- lib/ace/plugin_cache.rb
|
244
246
|
- lib/ace/puppet_util.rb
|
@@ -266,8 +268,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
266
268
|
- !ruby/object:Gem::Version
|
267
269
|
version: '0'
|
268
270
|
requirements: []
|
269
|
-
|
270
|
-
rubygems_version: 2.7.6
|
271
|
+
rubygems_version: 3.0.4
|
271
272
|
signing_key:
|
272
273
|
specification_version: 4
|
273
274
|
summary: ACE lets you run remote tasks and catalogs using puppet and bolt.
|