octocatalog-diff 0.5.1
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 +7 -0
- data/.version +1 -0
- data/LICENSE +20 -0
- data/README.md +82 -0
- data/bin/octocatalog-diff +75 -0
- data/doc/advanced-bootstrap.md +33 -0
- data/doc/advanced-cache-dir.md +24 -0
- data/doc/advanced-catalog-only.md +37 -0
- data/doc/advanced-ci.md +13 -0
- data/doc/advanced-dynamic-ignores.md +123 -0
- data/doc/advanced-future-parser.md +11 -0
- data/doc/advanced-ignores.md +224 -0
- data/doc/advanced-output-formats.md +96 -0
- data/doc/advanced-output-hacks.md +45 -0
- data/doc/advanced-override-facts.md +67 -0
- data/doc/advanced-pe-enc.md +52 -0
- data/doc/advanced-puppet-master.md +50 -0
- data/doc/advanced-puppet-versions.md +9 -0
- data/doc/advanced-storeconfigs.md +72 -0
- data/doc/advanced-using-without-git.md +15 -0
- data/doc/advanced.md +43 -0
- data/doc/basic.md +70 -0
- data/doc/configuration-enc.md +69 -0
- data/doc/configuration-hiera.md +103 -0
- data/doc/configuration-puppetdb.md +49 -0
- data/doc/configuration.md +51 -0
- data/doc/dev/README.md +1 -0
- data/doc/dev/coverage.md +34 -0
- data/doc/dev/how-to-add-options.md +83 -0
- data/doc/dev/integration-tests.md +63 -0
- data/doc/dev/releasing.md +19 -0
- data/doc/installation.md +49 -0
- data/doc/limitations.md +34 -0
- data/doc/optionsref.md +947 -0
- data/doc/requirements.md +16 -0
- data/doc/roadmap.md +26 -0
- data/doc/similar.md +17 -0
- data/doc/troubleshooting.md +54 -0
- data/lib/octocatalog-diff.rb +12 -0
- data/lib/octocatalog-diff/bootstrap.rb +53 -0
- data/lib/octocatalog-diff/catalog-diff/cli.rb +205 -0
- data/lib/octocatalog-diff/catalog-diff/cli/catalogs.rb +240 -0
- data/lib/octocatalog-diff/catalog-diff/cli/diffs.rb +145 -0
- data/lib/octocatalog-diff/catalog-diff/cli/helpers/fact_override.rb +99 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options.rb +173 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/basedir.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/bootstrap_environment.rb +18 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/bootstrap_script.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/bootstrap_then_exit.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/bootstrapped_dirs.rb +18 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/cached_master_dir.rb +21 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/catalog_only.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/color.rb +13 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/compare_file_text.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/debug.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/display_datatype_changes.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/display_detail_add.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/display_source_file_line.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/enc.rb +31 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/existing_catalogs.rb +25 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/fact_file.rb +23 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/fact_override.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/facts_terminus.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/from_puppetdb.rb +13 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/header.rb +24 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/hiera_config.rb +18 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/hiera_path_strip.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/hostname.rb +13 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/ignore.rb +24 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/ignore_attr.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/ignore_tags.rb +23 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/include_tags.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/master_cache_branch.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/output_file.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/output_format.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/parallel.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/parser.rb +48 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pass_env_vars.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_ssl_ca.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_ssl_client_cert.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_ssl_client_key.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_token.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_token_file.rb +17 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/pe_enc_url.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_binary.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_master.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_master_api_version.rb +20 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_master_ssl_ca.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_master_ssl_client_cert.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppet_master_ssl_client_key.rb +19 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_ssl_ca.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_ssl_client_cert.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_ssl_client_key.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_ssl_client_password.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_ssl_client_password_file.rb +13 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/puppetdb_url.rb +18 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/quiet.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/retry_failed_catalog.rb +13 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/safe_to_delete_cached_master_dir.rb +15 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/storeconfigs.rb +12 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/suppress_absent_file_details.rb +14 -0
- data/lib/octocatalog-diff/catalog-diff/cli/options/to_from_branch.rb +16 -0
- data/lib/octocatalog-diff/catalog-diff/cli/printer.rb +52 -0
- data/lib/octocatalog-diff/catalog-diff/differ.rb +615 -0
- data/lib/octocatalog-diff/catalog-diff/display.rb +125 -0
- data/lib/octocatalog-diff/catalog-diff/display/json.rb +25 -0
- data/lib/octocatalog-diff/catalog-diff/display/text.rb +452 -0
- data/lib/octocatalog-diff/catalog-util/bootstrap.rb +145 -0
- data/lib/octocatalog-diff/catalog-util/builddir.rb +289 -0
- data/lib/octocatalog-diff/catalog-util/cached_master_directory.rb +169 -0
- data/lib/octocatalog-diff/catalog-util/command.rb +96 -0
- data/lib/octocatalog-diff/catalog-util/enc.rb +77 -0
- data/lib/octocatalog-diff/catalog-util/enc/noop.rb +22 -0
- data/lib/octocatalog-diff/catalog-util/enc/pe.rb +99 -0
- data/lib/octocatalog-diff/catalog-util/enc/pe/v1.rb +61 -0
- data/lib/octocatalog-diff/catalog-util/enc/script.rb +88 -0
- data/lib/octocatalog-diff/catalog-util/facts.rb +89 -0
- data/lib/octocatalog-diff/catalog-util/fileresources.rb +83 -0
- data/lib/octocatalog-diff/catalog-util/git.rb +65 -0
- data/lib/octocatalog-diff/catalog.rb +209 -0
- data/lib/octocatalog-diff/catalog/computed.rb +205 -0
- data/lib/octocatalog-diff/catalog/json.rb +30 -0
- data/lib/octocatalog-diff/catalog/noop.rb +19 -0
- data/lib/octocatalog-diff/catalog/puppetdb.rb +82 -0
- data/lib/octocatalog-diff/catalog/puppetmaster.rb +121 -0
- data/lib/octocatalog-diff/external/pson/LICENSE +17 -0
- data/lib/octocatalog-diff/external/pson/README.md +20 -0
- data/lib/octocatalog-diff/external/pson/common.rb +370 -0
- data/lib/octocatalog-diff/external/pson/pure.rb +15 -0
- data/lib/octocatalog-diff/external/pson/pure/generator.rb +395 -0
- data/lib/octocatalog-diff/external/pson/pure/parser.rb +307 -0
- data/lib/octocatalog-diff/external/pson/version.rb +8 -0
- data/lib/octocatalog-diff/facts.rb +125 -0
- data/lib/octocatalog-diff/facts/json.rb +20 -0
- data/lib/octocatalog-diff/facts/puppetdb.rb +59 -0
- data/lib/octocatalog-diff/facts/yaml.rb +29 -0
- data/lib/octocatalog-diff/puppetdb.rb +163 -0
- data/lib/octocatalog-diff/util/colored.rb +20 -0
- data/lib/octocatalog-diff/util/httparty.rb +158 -0
- data/lib/octocatalog-diff/util/parallel.rb +170 -0
- data/lib/octocatalog-diff/util/puppetversion.rb +24 -0
- data/lib/octocatalog-diff/version.rb +7 -0
- metadata +386 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'open3'
|
|
3
|
+
require 'shellwords'
|
|
4
|
+
|
|
5
|
+
module OctocatalogDiff
|
|
6
|
+
module CatalogUtil
|
|
7
|
+
# Used to construct the command to run 'puppet' to construct the catalog.
|
|
8
|
+
class Command
|
|
9
|
+
# Constructor
|
|
10
|
+
def initialize(options = {}, logger = nil)
|
|
11
|
+
@options = options
|
|
12
|
+
@logger = logger
|
|
13
|
+
|
|
14
|
+
# Required parameters
|
|
15
|
+
@compilation_dir = options[:compilation_dir]
|
|
16
|
+
raise ArgumentError, 'Compile dir (:compilation_dir) must be a string' unless @compilation_dir.is_a?(String)
|
|
17
|
+
raise Errno::ENOENT, "Compile dir #{@compilation_dir} doesn't exist" unless File.exist?(@compilation_dir)
|
|
18
|
+
raise ArgumentError, "Compile dir #{@compilation_dir} not a directory" unless File.directory?(@compilation_dir)
|
|
19
|
+
|
|
20
|
+
@node = options[:node]
|
|
21
|
+
raise ArgumentError, 'Node must be specified to compile catalog' if @node.nil? || !@node.is_a?(String)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Build up the command line to run Puppet
|
|
25
|
+
def puppet_command
|
|
26
|
+
cmdline = []
|
|
27
|
+
|
|
28
|
+
# Where is the puppet binary?
|
|
29
|
+
puppet = @options[:puppet_binary]
|
|
30
|
+
raise ArgumentError, 'Puppet binary was not supplied' if puppet.nil?
|
|
31
|
+
raise Errno::ENOENT, "Puppet binary #{puppet} doesn't exist" unless File.file?(puppet)
|
|
32
|
+
cmdline << puppet
|
|
33
|
+
|
|
34
|
+
# Node to compile
|
|
35
|
+
cmdline.concat ['master', '--compile', Shellwords.escape(@node)]
|
|
36
|
+
|
|
37
|
+
# storeconfigs?
|
|
38
|
+
if @options[:storeconfigs]
|
|
39
|
+
cmdline.concat %w(--storeconfigs --storeconfigs_backend=puppetdb)
|
|
40
|
+
else
|
|
41
|
+
cmdline << '--no-storeconfigs'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# enc?
|
|
45
|
+
if @options[:enc]
|
|
46
|
+
raise Errno::ENOENT, "Did not find ENC as expected at #{@options[:enc]}" unless File.file?(@options[:enc])
|
|
47
|
+
cmdline << "--node_terminus=exec --external_nodes=#{Shellwords.escape(@options[:enc])}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Future parser?
|
|
51
|
+
cmdline << '--parser=future' if @options[:parser] == :future
|
|
52
|
+
|
|
53
|
+
# Path to facts, or a specific fact file?
|
|
54
|
+
facts_terminus = @options.fetch(:facts_terminus, 'yaml')
|
|
55
|
+
if facts_terminus == 'yaml'
|
|
56
|
+
cmdline << "--factpath=#{Shellwords.escape(File.join(@compilation_dir, 'var', 'yaml', 'facts'))}"
|
|
57
|
+
if @options[:fact_file].is_a?(String) && @options[:fact_file] =~ /.*\.(\w+)$/
|
|
58
|
+
fact_file = File.join(@compilation_dir, 'var', 'yaml', 'facts', "#{@node}.#{Regexp.last_match(1)}")
|
|
59
|
+
FileUtils.cp @options[:fact_file], fact_file unless File.file?(fact_file) || @options[:fact_file] == fact_file
|
|
60
|
+
end
|
|
61
|
+
cmdline << '--facts_terminus=yaml'
|
|
62
|
+
elsif facts_terminus == 'facter'
|
|
63
|
+
cmdline << '--facts_terminus=facter'
|
|
64
|
+
else
|
|
65
|
+
raise ArgumentError, "Unrecognized facts_terminus setting: '#{facts_terminus}'"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Some typical options for puppet
|
|
69
|
+
cmdline.concat %w(
|
|
70
|
+
--no-daemonize
|
|
71
|
+
--no-ca
|
|
72
|
+
--color=false
|
|
73
|
+
--config_version="/bin/echo catalogscript"
|
|
74
|
+
--environment=production
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# For people who aren't running hiera, a hiera-config will not be generated when @options[:hiera_config]
|
|
78
|
+
# is nil. For everyone else, the hiera config was generated/copied/munged in the 'builddir' class
|
|
79
|
+
# and was installed into the compile directory and named hiera.yaml.
|
|
80
|
+
unless @options[:hiera_config].nil?
|
|
81
|
+
cmdline << "--hiera_config=#{Shellwords.escape(File.join(@compilation_dir, 'hiera.yaml'))}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Options with parameters
|
|
85
|
+
cmdline << "--environmentpath=#{Shellwords.escape(File.join(@compilation_dir, 'environments'))}"
|
|
86
|
+
cmdline << "--vardir=#{Shellwords.escape(File.join(@compilation_dir, 'var'))}"
|
|
87
|
+
cmdline << "--logdir=#{Shellwords.escape(File.join(@compilation_dir, 'var'))}"
|
|
88
|
+
cmdline << "--ssldir=#{Shellwords.escape(File.join(@compilation_dir, 'var', 'ssl'))}"
|
|
89
|
+
cmdline << "--confdir=#{Shellwords.escape(@compilation_dir)}"
|
|
90
|
+
|
|
91
|
+
# Return full command
|
|
92
|
+
cmdline.join(' ')
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require_relative 'enc/noop'
|
|
2
|
+
require_relative 'enc/pe'
|
|
3
|
+
require_relative 'enc/script'
|
|
4
|
+
|
|
5
|
+
require 'stringio'
|
|
6
|
+
|
|
7
|
+
module OctocatalogDiff
|
|
8
|
+
module CatalogUtil
|
|
9
|
+
# Support a generic ENC. It must use one of the supported backends found in the
|
|
10
|
+
# 'enc' subdirectory.
|
|
11
|
+
class ENC
|
|
12
|
+
attr_reader :builder
|
|
13
|
+
|
|
14
|
+
# Constructor
|
|
15
|
+
# @param :backend [Symbol] If set, this will force a backend
|
|
16
|
+
# @param :enc [String] Path to ENC script (node_terminus = exec)
|
|
17
|
+
# @param # FIXME: Add support for PE's ENC endpoint API
|
|
18
|
+
def initialize(options = {})
|
|
19
|
+
@options = options
|
|
20
|
+
|
|
21
|
+
# Determine appropriate backend based on options supplied
|
|
22
|
+
@enc_obj = backend
|
|
23
|
+
|
|
24
|
+
# Initialize instance variables for content and error message.
|
|
25
|
+
@builder = @enc_obj.class.to_s
|
|
26
|
+
|
|
27
|
+
# Set the executed flag to false, so that it can be executed when something is retrieved.
|
|
28
|
+
@executed = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Retrieve content
|
|
32
|
+
# @return [String] ENC content, or nil if there was an error
|
|
33
|
+
def content
|
|
34
|
+
execute
|
|
35
|
+
@content ||= @enc_obj.content
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Retrieve error message
|
|
39
|
+
# @return [String] Error message, or nil if there was no error
|
|
40
|
+
def error_message
|
|
41
|
+
execute
|
|
42
|
+
@error_message ||= @enc_obj.error_message
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Backend - given options, choose an appropriate backend and construct the corresponding object.
|
|
48
|
+
# @return [?] Backend object
|
|
49
|
+
def backend
|
|
50
|
+
# Hard-coded backend
|
|
51
|
+
if @options[:backend]
|
|
52
|
+
return OctocatalogDiff::CatalogUtil::ENC::Noop.new(@options) if @options[:backend] == :noop
|
|
53
|
+
return OctocatalogDiff::CatalogUtil::ENC::PE.new(@options) if @options[:backend] == :pe
|
|
54
|
+
return OctocatalogDiff::CatalogUtil::ENC::Script.new(@options) if @options[:backend] == :script
|
|
55
|
+
raise ArgumentError, "Unknown backend :#{@options[:backend]}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Determine backend based on arguments
|
|
59
|
+
return OctocatalogDiff::CatalogUtil::ENC::PE.new(@options) if @options[:pe_enc_url]
|
|
60
|
+
return OctocatalogDiff::CatalogUtil::ENC::Script.new(@options) if @options[:enc]
|
|
61
|
+
|
|
62
|
+
# At this point we do not know what backend to use for the ENC
|
|
63
|
+
raise ArgumentError, 'Unable to determine ENC backend to use'
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Execute the 'execute' method of the object, but only once
|
|
67
|
+
# @param [Logger] Logger (optional) - if not supplied any logger messages will be discarded
|
|
68
|
+
def execute(logger = nil)
|
|
69
|
+
return if @executed
|
|
70
|
+
logger ||= @options[:logger]
|
|
71
|
+
logger ||= Logger.new(StringIO.new)
|
|
72
|
+
@enc_obj.execute(logger) if @enc_obj.respond_to?(:execute)
|
|
73
|
+
@executed = true
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module OctocatalogDiff
|
|
2
|
+
module CatalogUtil
|
|
3
|
+
class ENC
|
|
4
|
+
# No-op ENC.
|
|
5
|
+
class Noop
|
|
6
|
+
# Constructor
|
|
7
|
+
def initialize(_options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Retrieve content
|
|
11
|
+
def content
|
|
12
|
+
''
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Error message
|
|
16
|
+
def error_message
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require_relative 'pe/v1'
|
|
2
|
+
require_relative '../../util/httparty'
|
|
3
|
+
require_relative '../facts'
|
|
4
|
+
|
|
5
|
+
module OctocatalogDiff
|
|
6
|
+
module CatalogUtil
|
|
7
|
+
class ENC
|
|
8
|
+
# Support the Puppet Enterprise classification API.
|
|
9
|
+
# Documentation: https://docs.puppet.com/pe/latest/nc_index.html
|
|
10
|
+
class PE
|
|
11
|
+
# Error class that can be caught
|
|
12
|
+
class ClassificationError < RuntimeError; end
|
|
13
|
+
|
|
14
|
+
# Allow the main ENC object to retrieve these values
|
|
15
|
+
attr_reader :content, :error_message
|
|
16
|
+
|
|
17
|
+
# Constructor
|
|
18
|
+
# @param options [Hash] Options - must contain the Puppet Enterprise URL and the node
|
|
19
|
+
def initialize(options)
|
|
20
|
+
# Make sure the node is in the options
|
|
21
|
+
raise ArgumentError, 'OctocatalogDiff::CatalogUtil::ENC::PE#new requires :node' unless options.key?(:node)
|
|
22
|
+
@node = options[:node]
|
|
23
|
+
|
|
24
|
+
# Retrieve the base URL for the Puppet Enterprise ENC service
|
|
25
|
+
raise ArgumentError, 'OctocatalogDiff::CatalogUtil::ENC::PE#new requires :pe_enc_url' unless options.key?(:pe_enc_url)
|
|
26
|
+
|
|
27
|
+
# Save options
|
|
28
|
+
@options = options
|
|
29
|
+
|
|
30
|
+
# Get the object corresponding to the version of the API in use.
|
|
31
|
+
# (Right now this is hard-coded at V1 because that is the only version there is. In the future
|
|
32
|
+
# if there are different versions, this will need to be parameterized.)
|
|
33
|
+
@api = OctocatalogDiff::CatalogUtil::ENC::PE::V1.new(@options)
|
|
34
|
+
|
|
35
|
+
# Initialize the content and error message
|
|
36
|
+
@content = nil
|
|
37
|
+
@error_message = 'The execute method was never run'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Executor
|
|
41
|
+
# @param logger [Logger] Logger object
|
|
42
|
+
def execute(logger)
|
|
43
|
+
logger.debug "Beginning OctocatalogDiff::CatalogUtil::ENC::PE#execute for #{@node}"
|
|
44
|
+
|
|
45
|
+
@options[:facts] ||= facts(logger)
|
|
46
|
+
return unless @options[:facts]
|
|
47
|
+
|
|
48
|
+
more_options = { headers: @api.headers, timeout: @options[:timeout] || 10 }
|
|
49
|
+
post_hash = @api.body
|
|
50
|
+
url = @api.url
|
|
51
|
+
response = OctocatalogDiff::Util::HTTParty.post(url, @options.merge(more_options), post_hash, 'pe_enc')
|
|
52
|
+
|
|
53
|
+
unless response[:code] == 200
|
|
54
|
+
logger.debug "PE ENC failed: #{response.inspect}"
|
|
55
|
+
logger.error "PE ENC failed: Response from #{url} was #{response[:code]}"
|
|
56
|
+
@error_message = "Response from #{url} was #{response[:code]}"
|
|
57
|
+
return
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
logger.debug "Response from #{url} was #{response[:code]}"
|
|
61
|
+
unless response[:parsed].is_a?(Hash)
|
|
62
|
+
logger.error "PE ENC failed: Response from #{url} was not a hash! #{response[:parsed].inspect}"
|
|
63
|
+
@error_message = "PE ENC failed: Response from #{url} was not a hash! #{response[:parsed].class}"
|
|
64
|
+
return
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
begin
|
|
68
|
+
@content = @api.result(response[:parsed], logger)
|
|
69
|
+
@error_message = nil
|
|
70
|
+
rescue OctocatalogDiff::CatalogUtil::ENC::PE::ClassificationError => exc
|
|
71
|
+
@error_message = exc.message
|
|
72
|
+
logger.error "PE ENC failed: #{exc.message}"
|
|
73
|
+
return
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
logger.debug "Completed OctocatalogDiff::CatalogUtil::ENC::PE#execute for #{@node}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Facts
|
|
80
|
+
# @param logger [Logger] Logger object
|
|
81
|
+
# @return [OctocatalogDiff::Facts] Facts object
|
|
82
|
+
def facts(logger)
|
|
83
|
+
facts_obj = OctocatalogDiff::CatalogUtil::Facts.new(@options, logger)
|
|
84
|
+
logger.debug "Start retrieving facts for #{@node} from #{self.class}"
|
|
85
|
+
begin
|
|
86
|
+
result = facts_obj.facts
|
|
87
|
+
logger.debug "Success retrieving facts for #{@node} from #{self.class}"
|
|
88
|
+
rescue OctocatalogDiff::Facts::FactRetrievalError, OctocatalogDiff::Facts::FactSourceError => exc
|
|
89
|
+
@content = nil
|
|
90
|
+
@error_message = "Fact retrieval failed: #{exc.class} - #{exc.message}"
|
|
91
|
+
logger.error @error_message
|
|
92
|
+
result = nil
|
|
93
|
+
end
|
|
94
|
+
result
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'uri'
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module OctocatalogDiff
|
|
6
|
+
module CatalogUtil
|
|
7
|
+
class ENC
|
|
8
|
+
class PE
|
|
9
|
+
# Support the Puppet Enterprise classification API.
|
|
10
|
+
# Documentation: https://docs.puppet.com/pe/latest/nc_index.html
|
|
11
|
+
# This is version 1 of the API
|
|
12
|
+
class V1
|
|
13
|
+
# Constructor
|
|
14
|
+
# @param options [Hash] All input options
|
|
15
|
+
def initialize(options)
|
|
16
|
+
@options = options
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Return the URL to the API
|
|
20
|
+
# @return [String] API URL
|
|
21
|
+
def url
|
|
22
|
+
"#{@options[:pe_enc_url]}/v1/classified/nodes/#{@options[:node]}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Headers
|
|
26
|
+
# @return [Hash] Headers for request
|
|
27
|
+
def headers
|
|
28
|
+
result = {
|
|
29
|
+
'Accept' => 'application/json',
|
|
30
|
+
'Content-Type' => 'application/json'
|
|
31
|
+
}
|
|
32
|
+
result['X-Authentication'] = @options[:pe_enc_token] if @options[:pe_enc_token]
|
|
33
|
+
result
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# POST body
|
|
37
|
+
# @return [String] POST body data
|
|
38
|
+
def body
|
|
39
|
+
raise ":facts required (got #{@options[:facts].class})" unless @options[:facts].is_a?(OctocatalogDiff::Facts)
|
|
40
|
+
{ 'fact' => @options[:facts].facts }.to_json
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Parse response from ENC and return the final ENC data
|
|
44
|
+
# @param parsed [Parsed response] Parsed response from ENC
|
|
45
|
+
# @param logger [Logger] Logger.object
|
|
46
|
+
# @return [String] ENC data as text
|
|
47
|
+
def result(parsed, logger)
|
|
48
|
+
%w(classes parameters).each do |required_key|
|
|
49
|
+
next if parsed[required_key]
|
|
50
|
+
logger.debug parsed.keys.inspect
|
|
51
|
+
raise OctocatalogDiff::CatalogUtil::ENC::PE::ClassificationError, "Response missing: #{required_key}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
obj = { 'classes' => parsed['classes'], 'parameters' => parsed['parameters'] }
|
|
55
|
+
obj.to_yaml
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'open3'
|
|
3
|
+
require 'shellwords'
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
|
|
6
|
+
module OctocatalogDiff
|
|
7
|
+
module CatalogUtil
|
|
8
|
+
class ENC
|
|
9
|
+
# Support an ENC that executes a script on this system which returns the ENC data on STDOUT.
|
|
10
|
+
class Script
|
|
11
|
+
attr_reader :content, :error_message, :script
|
|
12
|
+
|
|
13
|
+
# Constructor
|
|
14
|
+
# @param options [Hash] Options - must contain script name and node name, plus tempdir if it's a relative path
|
|
15
|
+
def initialize(options)
|
|
16
|
+
# Make sure the node is in the options
|
|
17
|
+
raise ArgumentError, 'OctocatalogDiff::CatalogUtil::ENC::Script#new requires :node' unless options.key?(:node)
|
|
18
|
+
@node = options[:node]
|
|
19
|
+
|
|
20
|
+
# Determine path to ENC and make sure it exists
|
|
21
|
+
raise ArgumentError, 'OctocatalogDiff::CatalogUtil::ENC::Script#new requires :enc' unless options.key?(:enc)
|
|
22
|
+
@script = script_path(options[:enc], options[:tempdir])
|
|
23
|
+
|
|
24
|
+
# Other options we may recognize
|
|
25
|
+
@pass_env_vars = options.fetch(:pass_env_vars, [])
|
|
26
|
+
|
|
27
|
+
# Initialize the content and error message
|
|
28
|
+
@content = nil
|
|
29
|
+
@error_message = 'The execute method was never run'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Executor
|
|
33
|
+
# @param logger [Logger] Logger object
|
|
34
|
+
def execute(logger)
|
|
35
|
+
logger.debug "Beginning OctocatalogDiff::CatalogUtil::ENC::Script#execute for #{@node} with #{@script}"
|
|
36
|
+
logger.debug "Passing these extra environment variables: #{@pass_env_vars}" if @pass_env_vars.any?
|
|
37
|
+
|
|
38
|
+
# Copy the script and make it executable
|
|
39
|
+
# Then run the command in the restricted environment
|
|
40
|
+
raise Errno::ENOENT, "ENC #{@script} wasn't found" unless File.file?(@script)
|
|
41
|
+
file = Tempfile.open('enc.sh')
|
|
42
|
+
file.close
|
|
43
|
+
begin
|
|
44
|
+
FileUtils.cp @script, file.path
|
|
45
|
+
FileUtils.chmod 0o755, file.path
|
|
46
|
+
env = {
|
|
47
|
+
'HOME' => ENV['HOME'],
|
|
48
|
+
'PATH' => ENV['PATH'],
|
|
49
|
+
'PWD' => File.dirname(@script)
|
|
50
|
+
}
|
|
51
|
+
@pass_env_vars.each { |var| env[var] ||= ENV[var] }
|
|
52
|
+
command = [file.path, @node].map { |x| Shellwords.escape(x) }.join(' ')
|
|
53
|
+
out, err, status = Open3.capture3(env, command, unsetenv_others: true, chdir: File.dirname(@script))
|
|
54
|
+
logger.debug "ENC exited #{status.exitstatus}: #{out.length} bytes to STDOUT, #{err.length} bytes to STDERR"
|
|
55
|
+
ensure
|
|
56
|
+
file.unlink
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Analyze the output
|
|
60
|
+
if status.exitstatus.zero?
|
|
61
|
+
@content = out
|
|
62
|
+
@error_message = nil
|
|
63
|
+
logger.warn "ENC STDERR: #{err}" unless err.empty?
|
|
64
|
+
else
|
|
65
|
+
@content = nil
|
|
66
|
+
@error_message = "ENC failed with status #{status.exitstatus}: #{out} #{err}"
|
|
67
|
+
logger.error "ENC failed - Status #{status.exitstatus}"
|
|
68
|
+
logger.error "Failed ENC printed this to STDOUT: #{out}" unless out.empty?
|
|
69
|
+
logger.error "Failed ENC printed this to STDERR: #{err}" unless err.empty?
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
# Determine the script path for the incoming file -- absolute or relative
|
|
76
|
+
# @param enc [String] Path to ENC supplied by user/config
|
|
77
|
+
# @param tempdir [String]
|
|
78
|
+
# @return [String] Full path to file on system
|
|
79
|
+
def script_path(enc, tempdir)
|
|
80
|
+
return enc if enc.start_with? '/'
|
|
81
|
+
raise ArgumentError, 'OctocatalogDiff::CatalogUtil::ENC::Script#new requires :tempdir' unless tempdir.is_a?(String)
|
|
82
|
+
return File.join(tempdir, enc) if enc =~ %r{^environments/production/}
|
|
83
|
+
File.join(tempdir, 'environments', 'production', enc)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|