agentless-catalog-executor 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.dependency_decisions.yml +118 -0
  3. data/.gitignore +15 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +97 -0
  6. data/.travis.yml +7 -0
  7. data/CHANGELOG.md +44 -0
  8. data/Dockerfile +42 -0
  9. data/Gemfile +22 -0
  10. data/LICENSE +201 -0
  11. data/README.md +23 -0
  12. data/Rakefile +52 -0
  13. data/acceptance/.gitignore +10 -0
  14. data/acceptance/Gemfile +28 -0
  15. data/acceptance/Rakefile +16 -0
  16. data/acceptance/config/gem/options.rb +6 -0
  17. data/acceptance/config/git/options.rb +7 -0
  18. data/acceptance/config/package/options.rb +6 -0
  19. data/acceptance/lib/acceptance/ace_command_helper.rb +49 -0
  20. data/acceptance/lib/acceptance/ace_setup_helper.rb +45 -0
  21. data/acceptance/setup/common/pre-suite/.gitkeep +0 -0
  22. data/acceptance/setup/gem/pre-suite/.gitkeep +0 -0
  23. data/acceptance/setup/git/pre-suite/.gitkeep +0 -0
  24. data/acceptance/setup/package/pre-suite/.gitkeep +0 -0
  25. data/acceptance/tests/.gitkeep +0 -0
  26. data/agentless-catalog-executor.gemspec +38 -0
  27. data/config/docker.conf +9 -0
  28. data/config/local.conf +9 -0
  29. data/config/transport_tasks_config.rb +49 -0
  30. data/developer-docs/api.md +139 -0
  31. data/developer-docs/docker.md +170 -0
  32. data/docker-compose.yml +12 -0
  33. data/lib/ace/config.rb +73 -0
  34. data/lib/ace/configurer.rb +17 -0
  35. data/lib/ace/error.rb +31 -0
  36. data/lib/ace/fork_util.rb +39 -0
  37. data/lib/ace/plugin_cache.rb +79 -0
  38. data/lib/ace/puppet_util.rb +57 -0
  39. data/lib/ace/schemas/ace-execute_catalog.json +48 -0
  40. data/lib/ace/schemas/ace-run_task.json +30 -0
  41. data/lib/ace/schemas/task.json +74 -0
  42. data/lib/ace/transport_app.rb +252 -0
  43. data/lib/ace/version.rb +5 -0
  44. data/lib/puppet/indirector/catalog/certless.rb +83 -0
  45. metadata +254 -0
@@ -0,0 +1,23 @@
1
+ # Agentless::Catalog::Executor
2
+
3
+ ## Installation
4
+
5
+ ACE is installed as pe-ace-server as part of Puppet Enterprise.
6
+
7
+ ## Usage
8
+
9
+ For development or experimenting, run the puma server to get an instance started:
10
+
11
+ ```
12
+ ACE_CONF=config/local.conf bundle exec puma -C config/transport_tasks_config.rb
13
+ ```
14
+
15
+ ## Development
16
+
17
+ As ACE is dependent on a PuppetServer, there is a docker-compose file within the `spec/` directory which is advisable to run first to ensure that the certs and keys are valid before running the ACE service, more information can be found in the [docker documentation](developer-docs/docker).
18
+
19
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). Released gems will eventually be consumed by [ace-vanagon](https://github.com/puppetlabs/ace-vanagon) and promoted into PE.
20
+
21
+ ## Contributing
22
+
23
+ Bug reports and pull requests are welcome on GitHub at https://github.com/puppetlabs/ace. See the `.travis.yml` file for checks to run on your code before submitting. Please always include tests with your changes.
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require 'rubocop/rake_task'
5
+
6
+ RuboCop::RakeTask.new(:rubocop) do |t|
7
+ t.options = ['--display-cop-names']
8
+ end
9
+
10
+ task default: %i[rubocop spec license_finder]
11
+
12
+ #### RSPEC ####
13
+ require 'rspec/core/rake_task'
14
+
15
+ RSpec::Core::RakeTask.new(:spec) do |t|
16
+ t.exclude_pattern = 'spec/acceptance/**/*_spec.rb'
17
+ end
18
+
19
+ namespace :spec do
20
+ desc 'Run RSpec code examples with coverage collection'
21
+ task :coverage do
22
+ ENV['COVERAGE'] = 'yes'
23
+ Rake::Task['spec'].execute
24
+ end
25
+ end
26
+
27
+ #### LICENSE_FINDER ####
28
+ desc 'Check for unapproved licenses in dependencies'
29
+ task(:license_finder) do
30
+ unless system('license_finder --decisions-file=.dependency_decisions.yml')
31
+ raise(StandardError, 'Unapproved license(s) found on dependencies')
32
+ end
33
+ end
34
+
35
+ #### CHANGELOG ####
36
+ begin
37
+ require 'github_changelog_generator/task'
38
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
39
+ require 'ace/version'
40
+ config.future_release = "v#{ACE::VERSION}"
41
+ config.header = "# Changelog\n\n" \
42
+ "All significant changes to this repo will be summarized in this file.\n"
43
+ # config.include_labels = %w[enhancement bug]
44
+ config.user = 'puppetlabs'
45
+ config.project = 'ace'
46
+ end
47
+ rescue LoadError
48
+ desc 'Install github_changelog_generator to get access to automatic changelog generation'
49
+ task :changelog do
50
+ raise 'Install github_changelog_generator to get access to automatic changelog generation'
51
+ end
52
+ end
@@ -0,0 +1,10 @@
1
+ .vagrant
2
+ .bundle
3
+ Gemfile.lock
4
+ local_options.rb
5
+ log
6
+ junit
7
+ id_rsa-acceptance
8
+ id_rsa-acceptance.pub
9
+ merged_options.rb
10
+ tmp
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ source ENV['GEM_SOURCE'] || "https://rubygems.org"
4
+
5
+ def location_for(place, fake_version = nil)
6
+ git_match = place.match(/^(git:[^#]*)#(.*)/)
7
+ file_match = place.match(%r{^file://(.*)})
8
+ if git_match
9
+ git, branch = git_match.captures
10
+ [fake_version, { git: git, branch: branch, require: false }].compact
11
+ elsif file_match
12
+ file_path = file_match[1]
13
+ ['>= 0', { path: File.expand_path(file_path), require: false }]
14
+ else
15
+ [place, { require: false }]
16
+ end
17
+ end
18
+
19
+ gem "beaker", *location_for(ENV['BEAKER_VERSION'] || "~> 3.10")
20
+ gem "beaker-abs", *location_for(ENV['BEAKER_ABS_VERSION'] || "~> 0.2")
21
+ gem "beaker-hostgenerator", *location_for(ENV['BEAKER_HOSTGENERATOR_VERSION'] || "~> 1.0")
22
+ gem 'beaker-puppet', *location_for(ENV['BEAKER_PUPPET_VERSION'] || '~> 0.15')
23
+ gem 'rake', "~> 12.1"
24
+ gem 'rototiller'
25
+
26
+ if File.exist? "Gemfile.local"
27
+ eval_gemfile "Gemfile.local"
28
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rototiller'
4
+ require_relative 'lib/acceptance/ace_setup_helper'
5
+
6
+ # rubocop:disable Style/MixinUsage
7
+ extend Acceptance::AceSetupHelper
8
+ # rubocop:enable Style/MixinUsage
9
+
10
+ namespace :test do
11
+ desc "Run ace acceptance tests against a published gem"
12
+
13
+ desc "Run ace acceptance tests against a git repo"
14
+
15
+ desc "Run ace acceptance tests against a package"
16
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ {
4
+ pre_suite: [],
5
+ load_path: './lib/acceptance'
6
+ }
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ {
4
+ pre_suite: [],
5
+ load_path: './lib/acceptance',
6
+ ssh: { forward_agent: false }
7
+ }
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ {
4
+ pre_suite: [],
5
+ load_path: './lib/acceptance'
6
+ }
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Acceptance
4
+ module AceCommandHelper
5
+ # A helper to build a bolt command used in acceptance testing
6
+ # @param [Beaker::Host] host the host to execute the command on
7
+ # @param [String] command the command to execute on the bolt SUT
8
+ # @param [Hash] flags the command flags to append to the command
9
+ # @option flags [String] '--nodes' the nodes to run on
10
+ # @option flags [String] '--user' the user to run the command as
11
+ # @option flags [String] '--password' the password for the user
12
+ # @option flags [nil] '--no-host-key-check' specify nil to use
13
+ # @option flags [nil] '--no-ssl' specify nil to use
14
+ # @param [Hash] opts the options hash for this method
15
+ def bolt_command_on(host, command, flags = {}, opts = {})
16
+ bolt_command = command.dup
17
+ flags.each { |k, v| bolt_command << " #{k} #{v}" }
18
+
19
+ case host['platform']
20
+ when /windows/
21
+ execute_powershell_script_on(host, bolt_command, opts)
22
+ when /osx/
23
+ # Ensure Bolt runs with UTF-8 under macOS. Otherwise we get issues with
24
+ # UTF-8 content in task results.
25
+ env = 'source /etc/profile ~/.bash_profile ~/.bash_login ~/.profile && env LANG=en_US.UTF-8'
26
+ on(host, env + ' ' + bolt_command)
27
+ else
28
+ on(host, bolt_command, opts)
29
+ end
30
+ end
31
+
32
+ def default_boltdir
33
+ @default_boltdir ||= begin
34
+ query = bolt['platform'] =~ /windows/ ? 'cygpath -m $(printenv HOME)' : 'printenv HOME'
35
+ home = on(bolt, query).stdout.chomp
36
+ File.join(home, '.puppetlabs/bolt')
37
+ end
38
+ end
39
+
40
+ def modulepath(extra)
41
+ case bolt['platform']
42
+ when /windows/
43
+ "\"#{default_boltdir}/modules;#{extra}\""
44
+ else
45
+ "#{default_boltdir}/modules:#{extra}"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Acceptance
4
+ module AceSetupHelper
5
+ def ssh_user
6
+ ENV['SSH_USER'] || 'root'
7
+ end
8
+
9
+ def ssh_password
10
+ ENV['SSH_PASSWORD'] || 'bolt_secret_password'
11
+ end
12
+
13
+ def winrm_user
14
+ ENV['WINRM_USER'] || 'Administrator'
15
+ end
16
+
17
+ def winrm_password
18
+ ENV['WINRM_PASSWORD'] || 'bolt_secret_password'
19
+ end
20
+
21
+ def gem_version
22
+ ENV['GEM_VERSION'] || '> 0.1.0'
23
+ end
24
+
25
+ def gem_source
26
+ ENV['GEM_SOURCE'] || 'https://rubygems.org'
27
+ end
28
+
29
+ def git_server
30
+ ENV['GIT_SERVER'] || 'https://github.com'
31
+ end
32
+
33
+ def git_fork
34
+ ENV['GIT_FORK'] || 'puppetlabs/bolt'
35
+ end
36
+
37
+ def git_branch
38
+ ENV['GIT_BRANCH'] || 'master'
39
+ end
40
+
41
+ def git_sha
42
+ ENV['GIT_SHA'] || ''
43
+ end
44
+ end
45
+ end
File without changes
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "ace/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "agentless-catalog-executor"
9
+ spec.version = ACE::VERSION
10
+ spec.authors = ["David Schmitt"]
11
+ spec.email = ["david.schmitt@puppet.com"]
12
+
13
+ spec.summary = 'ACE lets you run remote tasks and catalogs using puppet and bolt.'
14
+ spec.homepage = "https://github.com/puppetlabs/agentless-catalog-executor"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency "bolt", "~> 1.15"
24
+
25
+ # server-side dependencies cargo culted from https://github.com/puppetlabs/bolt/blob/4418da408643aa7eb5ed64f4c9704b941ea878dc/Gemfile#L10-L16
26
+ spec.add_dependency "hocon", ">= 1.2.5"
27
+ spec.add_dependency "json-schema", ">= 2.8.0"
28
+ spec.add_dependency "puma", ">= 3.12.0"
29
+ spec.add_dependency "rack", ">= 2.0.5"
30
+ spec.add_dependency "rails-auth", ">= 2.1.4"
31
+ spec.add_dependency "sinatra", ">= 2.0.4"
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.16"
34
+ spec.add_development_dependency "rack-test", "~> 1.0"
35
+ spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "rspec", "~> 3.0"
37
+ spec.add_development_dependency "rubocop", "~> 0.50"
38
+ end
@@ -0,0 +1,9 @@
1
+ ace-server: {
2
+ ssl-cert: "spec/volumes/puppet/ssl/certs/aceserver.pem"
3
+ ssl-key: "spec/volumes/puppet/ssl/private_keys/aceserver.pem"
4
+ ssl-ca-cert: "spec/volumes/puppet/ssl/certs/ca.pem"
5
+ ssl-ca-crls: "spec/volumes/puppet/ssl/certs/crl.pem"
6
+ puppet-server-uri: "https://spec_puppet_1:8140"
7
+ loglevel: debug
8
+ host: "0.0.0.0"
9
+ }
@@ -0,0 +1,9 @@
1
+ ace-server: {
2
+ ssl-cert: "spec/volumes/puppet/ssl/certs/aceserver.pem"
3
+ ssl-key: "spec/volumes/puppet/ssl/private_keys/aceserver.pem"
4
+ ssl-ca-cert: "spec/volumes/puppet/ssl/certs/ca.pem"
5
+ ssl-ca-crls: "spec/volumes/puppet/ssl/ca/ca_crl.pem"
6
+ puppet-server-uri: "https://0.0.0.0:8140"
7
+ cache-dir: "tmp/"
8
+ loglevel: "debug"
9
+ }
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ ####################################################
4
+ ## DO NOT EDIT THIS FILE ##
5
+ ## Use /etc/puppetlabs/ace/conf.d/ace-server.conf ##
6
+ ## to configure the sinatra server ##
7
+ ####################################################
8
+
9
+ require 'ace/transport_app'
10
+ require 'ace/config'
11
+ require 'bolt_server/acl'
12
+ require 'bolt/logger'
13
+
14
+ Bolt::Logger.initialize_logging
15
+
16
+ config_path = ENV['ACE_CONF'] || '/etc/puppetlabs/ace-server/conf.d/ace-server.conf'
17
+
18
+ config = ACE::Config.new
19
+ config.load_file_config(config_path)
20
+ config.load_env_config
21
+ config.make_compatible
22
+ config.validate
23
+
24
+ Logging.logger[:root].add_appenders Logging.appenders.stderr(
25
+ 'console',
26
+ layout: Bolt::Logger.default_layout,
27
+ level: config['loglevel']
28
+ )
29
+
30
+ if config['logfile']
31
+ stdout_redirect config['logfile'], config['logfile'], true
32
+ end
33
+
34
+ bind_addr = +"ssl://#{config['host']}:#{config['port']}?"
35
+ bind_addr << "cert=#{config['ssl-cert']}"
36
+ bind_addr << "&key=#{config['ssl-key']}"
37
+ bind_addr << "&ca=#{config['ssl-ca-cert']}"
38
+ bind_addr << "&verify_mode=force_peer"
39
+ bind_addr << "&ssl_cipher_filter=#{config['ssl-cipher-suites'].join(':')}"
40
+ bind bind_addr
41
+
42
+ threads 0, config['concurrency']
43
+
44
+ impl = ACE::TransportApp.new(config)
45
+ unless config['whitelist'].nil?
46
+ impl = BoltServer::ACL.new(impl, config['whitelist'])
47
+ end
48
+
49
+ app impl
@@ -0,0 +1,139 @@
1
+ # ACE API
2
+
3
+ ## Overview
4
+ ACE provides 2 APIs to enable the execution of Tasks and Catalog compilation for a remote target.
5
+
6
+ ## API Endpoints
7
+ Each API endpoint accepts a request as described below. The request body must be a JSON object.
8
+
9
+ ### POST /run_task
10
+ - `target`: [RSAPI Transport Object](#rsapi-transport-object), *required* - Target information to run task on.
11
+ - `task`: [Task Object](#task-object), *required* - Task to run on target.
12
+ - `parameters`: Object, *optional* - JSON formatted parameters to be provided to task.
13
+
14
+ For example, the following runs the 'commit' task on `fw.example.net`:
15
+ ```
16
+ {
17
+ "target":{
18
+ "remote-transport":"panos",
19
+ "host":"fw.example.net",
20
+ "user":"foo",
21
+ "password":"wibble"
22
+ },
23
+ "task":{
24
+ "metadata":{},
25
+ "name":"panos::commit",
26
+ "files":[
27
+ {
28
+ "filename":"commit.rb",
29
+ "sha256":"c5abefbdecee006bd65ef6f625e73f0ebdd1ef3f1b8802f22a1b9644a516ce40",
30
+ "size_bytes":640,
31
+ "uri":{
32
+ "path":"/puppet/v3/file_content/tasks/panos/commit.rb",
33
+ "params":{
34
+ "environment":"production"
35
+ }
36
+ }
37
+ }
38
+ ]
39
+ },
40
+ "parameters":{
41
+ "message":"Hello world"
42
+ }
43
+ }
44
+ ```
45
+
46
+ #### Response
47
+ If the task runs the response will have status 200.
48
+ The response will be a standard bolt Result JSON object.
49
+
50
+
51
+ ### POST /execute_catalog
52
+ - `target`: [RSAPI Transport Object](#rsapi-transport-object), *required* - Target information to execute the catalog on.
53
+ - `compiler`: [Compiler Request Object](#compiler-request-object), *required* - Details on the requested compile.
54
+
55
+ For example, the following will compile and execute a catalog on fw.example.net:
56
+ ```
57
+ {
58
+ "target":{
59
+ "remote-transport":"panos",
60
+ "host":"fw.example.net",
61
+ "user":"foo",
62
+ "password":"wibble"
63
+ },
64
+ "compiler":{
65
+ "certname":"fw.example.net",
66
+ "environment":"development",
67
+ "transaction_uuid":"<uuid string>",
68
+ "job_id":"<id string>"
69
+ }
70
+ }
71
+ ```
72
+
73
+ For pre-Transport devices (like F5), a uri can be sent:
74
+
75
+ ```
76
+ {
77
+ "target":{
78
+ "remote-transport":"f5",
79
+ "uri":"https://foo:wibble@f5.example.net/"
80
+ },
81
+ "compiler":{
82
+ "certname":"f5.example.net",
83
+ "environment":"development",
84
+ "transaction_uuid":"<uuid string>",
85
+ "job_id":"<id string>"
86
+ }
87
+ }
88
+ ```
89
+
90
+ #### Response
91
+ TBD based on orchestrator's needs for feedback.
92
+
93
+ ## Data Object Definitions
94
+
95
+ ### RSAPI Transport Object
96
+ The `target` is a JSON object which reflects the schema of the `remote-transport`.
97
+ e.g. If `remote-transport` is `panos`, the object should validate against the panos transport schema.
98
+
99
+ Read more about [Transports](https://github.com/puppetlabs/puppet-resource_api#remote-resources) in the Resource API README. The `target` will contain both connection info and bolt's keywords for connection management.
100
+
101
+ ### Compiler Request Object
102
+ The `compiler` is a JSON object which contains parameters regarding the compilation of the catalog for this request. It contains four attributes that have the same definition as the attributes of the same name in the [puppet server catalog API](https://github.com/puppetlabs/puppetserver/blob/master/documentation/puppet-api/v4/catalog.markdown):
103
+
104
+ * `certname`
105
+ * `environment`
106
+ * `transaction_uuid`
107
+ * `job_id`
108
+
109
+ ### Task Object
110
+ 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
+
112
+
113
+ ## Running ACE in a container
114
+ *Recommended*
115
+
116
+ From your checkout of ACE start the docker-compose to run ACE
117
+
118
+ ```
119
+ docker-compose up -d --build
120
+ ```
121
+
122
+ You can now make a curl request to ACE, which should respond with 'OK':
123
+
124
+ ```
125
+ curl -X POST http://0.0.0.0:44633/check
126
+ ```
127
+
128
+ ## Running from source
129
+
130
+ From your checkout of ACE run
131
+
132
+ ```
133
+ bundle exec puma -p 44633 -C puma_config.rb
134
+ ```
135
+
136
+ You can now make a curl request to ACE, which should respond with 'OK':
137
+ ```
138
+ curl -X POST http://0.0.0.0:44633/check
139
+ ```