agentless-catalog-executor 0.10.0 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92395f0079ad1725aee845a780da2c613c02b6709053288e60ff374bf9f1260c
4
- data.tar.gz: a8e35c7eaca53dc0e294779122a50f85b325b312c51bcdca192bcfd4b00d3b18
3
+ metadata.gz: 603248d8cb9b344650b57553ed3f415e94964b5c49fb1adcfa38b3e14adbfc8a
4
+ data.tar.gz: dedfb986987cb291c4a5277b269a25da626f875854ba44b5a7935aabc3851f67
5
5
  SHA512:
6
- metadata.gz: 4eeeacc1233b2541287c3965409f9b8e155de490e430ee0eb927af210484fa42d81f4bc475bc88f5475464639489d41bac550943751f38a3e493af64e8caa8a5
7
- data.tar.gz: a8e6988f55d193eb286e1139aa5b7f8dbc37849ff77d8664a27140878de2ad1715d0ca3438f787ceaf842107d01890bb287d53c1e3a2e782ea1f5a11b8ea6d09
6
+ metadata.gz: e408493f6327f58cc447a38e7de50cc39bee52d99dd98bbfa4ae66c24744e0c27a5d384bd2d04448c0004915367028f927d4c7587fdcfcfa03b326f0bb5d281d
7
+ data.tar.gz: cd98abcf1b810d881dddcf1c0207f8ec0a3c3cf62e7da76fa9cd87bd1416e379da5a9ffbb094a0d27dea4ba5ae3477b40006ab909a6391179e949a24e741c6de
data/.rspec CHANGED
@@ -1,3 +1,4 @@
1
1
  --format documentation
2
2
  --color
3
3
  --require spec_helper
4
+ --exclude-pattern 'spec/volumes/**/*'
@@ -98,3 +98,8 @@ RSpec/ExampleLength:
98
98
  Lint/AmbiguousBlockAssociation:
99
99
  Exclude:
100
100
  - "spec/**/*"
101
+
102
+ # complex code is complex :-(
103
+ RSpec/NestedGroups:
104
+ Enabled: true
105
+ Max: 5
@@ -1,5 +1,6 @@
1
1
  dist: xenial
2
2
  language: ruby
3
+ cache: bundler
3
4
  rvm:
4
5
  - 2.5.1
5
6
  env:
@@ -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
 
@@ -0,0 +1,2 @@
1
+ # Set the networking team as the main owners of the module
2
+ * @puppetlabs/networking
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 the ACE service to ensure that the certs and keys are valid. For more information, see the [docker documentation](developer-docs/docker.md).
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.15"
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"
@@ -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
 
@@ -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
- `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`
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
@@ -28,7 +28,7 @@ module ACE
28
28
  }
29
29
  }.to_json)
30
30
  success = false
31
- rescue StandardError => e
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.read
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
@@ -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 = Concurrent::ReadWriteLock.new
24
+ @cache_dir_mutex = cache_dir_mutex
20
25
 
21
- @purge = Concurrent::TimerTask.new(execution_interval: PURGE_INTERVAL,
22
- timeout_interval: PURGE_TIMEOUT,
23
- run_now: true) { expire(PURGE_TTL) }
24
- @purge.execute
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(certname, environment)
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
@@ -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, 'conf')
22
- Puppet.settings[:rundir] = File.join(cachedir, 'run')
23
- Puppet.settings[:logdir] = File.join(cachedir, 'log')
24
- Puppet.settings[:codedir] = File.join(cachedir, 'code')
25
- Puppet.settings[:plugindest] = File.join(cachedir, 'plugins')
26
- Puppet.push_context(Puppet.base_context(Puppet.settings), "Puppet Initialization")
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
- Puppet.push_context({
39
- ssl_context: ssl_context,
40
- server: uri.host,
41
- serverport: uri.port
42
- }, "PuppetServer connection information to be used")
43
- Puppet.settings.use :main, :agent, :ssl
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
  },
@@ -17,8 +17,7 @@
17
17
  "description": ""
18
18
  }
19
19
  },
20
- "additionalProperties": true,
21
- "required": ["remote-transport"]
20
+ "additionalProperties": true
22
21
  },
23
22
  "task": { "$ref": "file:task"},
24
23
  "parameters": {
@@ -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
- @file_cache = BoltServer::FileCache.new(@config.data.merge('cache-dir' => tasks_cache_dir)).setup
25
- environments_cache_dir = File.join(@config['cache-dir'], 'environments')
26
- @plugins = ACE::PluginCache.new(environments_cache_dir).setup
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
- Puppet::Util::NetworkDevice.init(device_struct.new(transport,
89
- url,
90
- certname,
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 = { _error: e.to_h }
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 StandardError => e
228
+ rescue JSON::ParserError => e
163
229
  request_error = {
164
- _error: ACE::Error.to_h(e.message,
165
- 'puppetlabs/ace/request_exception',
166
- class: e.class, backtrace: e.backtrace)
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
- opts = body['target'].merge('protocol' => 'remote')
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
- target.first.inventory = inventory
261
+ target.first.inventory = inventory
181
262
 
182
- task = Bolt::Task::PuppetServer.new(body['task'], @file_cache)
263
+ task = Bolt::Task::PuppetServer.new(body['task'], @file_cache)
183
264
 
184
- parameters = body['parameters'] || {}
265
+ parameters = body['parameters'] || {}
185
266
 
186
- result = ForkUtil.isolate do
187
- # Since this will only be on one node we can just return the first result
188
- results = @executor.run_task(target, task, parameters)
189
- scrub_stack_trace(results.first.status_hash)
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
- configurer.run(transport_name: certname,
230
- environment: environment,
231
- network_device: true,
232
- pluginsync: false,
233
- trusted_facts: ACE::TransportApp.trusted_facts(certname))
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: 'report_generated',
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ACE
4
- VERSION = "0.10.0"
4
+ VERSION = "1.0.0"
5
5
  end
@@ -41,7 +41,7 @@ module Puppet
41
41
  "transaction_uuid": request.options[:transaction_uuid],
42
42
  "job_id": request.options[:job_id],
43
43
  "options": {
44
- "prefer_requested_environment": true,
44
+ "prefer_requested_environment": false,
45
45
  "capture_logs": false
46
46
  }
47
47
  }
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.10.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-07-25 00:00:00.000000000 Z
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.15'
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.15'
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
- rubyforge_project:
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.