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