octocatalog-diff 1.5.1 → 2.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 +5 -5
- data/.version +1 -1
- data/README.md +4 -4
- data/doc/CHANGELOG.md +49 -0
- data/doc/advanced-filter.md +23 -0
- data/doc/advanced-ignores.md +10 -0
- data/doc/advanced-puppet-master.md +23 -5
- data/doc/configuration-puppetdb.md +11 -0
- data/doc/dev/api/v1/calls/catalog-diff.md +6 -2
- data/doc/dev/api/v1/objects/diff.md +3 -3
- data/doc/dev/releasing.md +1 -1
- data/doc/limitations.md +9 -9
- data/doc/optionsref.md +167 -11
- data/doc/requirements.md +6 -2
- data/lib/octocatalog-diff/catalog-diff/differ.rb +29 -4
- data/lib/octocatalog-diff/catalog-diff/filter.rb +2 -1
- data/lib/octocatalog-diff/catalog-diff/filter/compilation_dir.rb +29 -25
- data/lib/octocatalog-diff/catalog-diff/filter/single_item_array.rb +44 -0
- data/lib/octocatalog-diff/catalog-util/builddir.rb +3 -3
- data/lib/octocatalog-diff/catalog-util/command.rb +25 -3
- data/lib/octocatalog-diff/catalog-util/fileresources.rb +1 -1
- data/lib/octocatalog-diff/catalog.rb +22 -4
- data/lib/octocatalog-diff/catalog/computed.rb +2 -1
- data/lib/octocatalog-diff/catalog/puppetmaster.rb +43 -5
- data/lib/octocatalog-diff/cli.rb +36 -5
- data/lib/octocatalog-diff/cli/options.rb +39 -3
- data/lib/octocatalog-diff/cli/options/hostname.rb +13 -2
- data/lib/octocatalog-diff/cli/options/pe_enc_token_file.rb +1 -1
- data/lib/octocatalog-diff/cli/options/puppet_master_api_version.rb +2 -2
- data/lib/octocatalog-diff/cli/options/puppet_master_token.rb +20 -0
- data/lib/octocatalog-diff/cli/options/puppet_master_token_file.rb +35 -0
- data/lib/octocatalog-diff/cli/options/puppet_master_update_catalog.rb +20 -0
- data/lib/octocatalog-diff/cli/options/puppet_master_update_facts.rb +20 -0
- data/lib/octocatalog-diff/cli/options/puppetdb_package_inventory.rb +18 -0
- data/lib/octocatalog-diff/cli/options/puppetdb_token.rb +17 -0
- data/lib/octocatalog-diff/cli/options/puppetdb_token_file.rb +21 -0
- data/lib/octocatalog-diff/facts/puppetdb.rb +43 -2
- data/lib/octocatalog-diff/puppetdb.rb +5 -1
- data/lib/octocatalog-diff/util/parallel.rb +20 -16
- data/lib/octocatalog-diff/util/util.rb +2 -0
- data/scripts/env/env.sh +1 -1
- data/scripts/git-extract/git-extract.sh +1 -1
- data/scripts/puppet/puppet.sh +1 -1
- metadata +37 -30
data/lib/octocatalog-diff/cli.rb
CHANGED
@@ -11,6 +11,7 @@ require_relative 'util/util'
|
|
11
11
|
require_relative 'version'
|
12
12
|
|
13
13
|
require 'logger'
|
14
|
+
require 'parallel'
|
14
15
|
require 'socket'
|
15
16
|
|
16
17
|
module OctocatalogDiff
|
@@ -116,16 +117,46 @@ module OctocatalogDiff
|
|
116
117
|
end
|
117
118
|
|
118
119
|
# Compile catalogs and do catalog-diff
|
119
|
-
|
120
|
+
node_set = options.delete(:node)
|
121
|
+
node_set = [node_set] unless node_set.is_a?(Array)
|
122
|
+
|
123
|
+
# run multiple node diffs in parallel
|
124
|
+
catalog_diffs = if node_set.size == 1
|
125
|
+
[run_octocatalog_diff(node_set.first, options, logger)]
|
126
|
+
else
|
127
|
+
::Parallel.map(node_set, in_threads: 4) { |node| run_octocatalog_diff(node, options, logger) }
|
128
|
+
end
|
129
|
+
|
130
|
+
# Return the resulting diff object if requested (generally for testing)
|
131
|
+
# or otherwise return exit code
|
132
|
+
return catalog_diffs.first if opts[:INTEGRATION]
|
133
|
+
|
134
|
+
all_diffs = catalog_diffs.map(&:diffs)
|
135
|
+
|
136
|
+
all_diffs.each do |diff|
|
137
|
+
next unless diff.any?
|
138
|
+
return EXITCODE_SUCCESS_WITH_DIFFS
|
139
|
+
end
|
140
|
+
|
141
|
+
EXITCODE_SUCCESS_NO_DIFFS
|
142
|
+
end
|
143
|
+
|
144
|
+
# Run the octocatalog-diff process for a given node. Return the diffs for a contribution to
|
145
|
+
# the final exit status.
|
146
|
+
# node - String with the node
|
147
|
+
# options - All of the currently defined options
|
148
|
+
# logger - Logger object
|
149
|
+
def self.run_octocatalog_diff(node, options, logger)
|
150
|
+
options_copy = options.merge(node: node)
|
151
|
+
catalog_diff = OctocatalogDiff::API::V1.catalog_diff(options_copy.merge(logger: logger))
|
120
152
|
diffs = catalog_diff.diffs
|
121
153
|
|
122
154
|
# Display diffs
|
123
|
-
printer_obj = OctocatalogDiff::Cli::Printer.new(
|
155
|
+
printer_obj = OctocatalogDiff::Cli::Printer.new(options_copy, logger)
|
124
156
|
printer_obj.printer(diffs, catalog_diff.from.compilation_dir, catalog_diff.to.compilation_dir)
|
125
157
|
|
126
|
-
# Return
|
127
|
-
|
128
|
-
diffs.any? ? EXITCODE_SUCCESS_WITH_DIFFS : EXITCODE_SUCCESS_NO_DIFFS
|
158
|
+
# Return catalog-diff object.
|
159
|
+
catalog_diff
|
129
160
|
end
|
130
161
|
|
131
162
|
# Parse command line options with 'optparse'. Returns a hash with the parsed arguments.
|
@@ -11,7 +11,7 @@ module OctocatalogDiff
|
|
11
11
|
# This class contains the option parser. 'parse_options' is the external entry point.
|
12
12
|
class Options
|
13
13
|
# The usage banner.
|
14
|
-
BANNER = 'Usage: catalog-diff -n <hostname> [-f <from environment>] [-t <to environment>]'.freeze
|
14
|
+
BANNER = 'Usage: catalog-diff -n <hostname>[,<hostname>...] [-f <from environment>] [-t <to environment>]'.freeze
|
15
15
|
|
16
16
|
# An error class specifically for passing information to the document build task.
|
17
17
|
class DocBuildError < RuntimeError; end
|
@@ -23,7 +23,6 @@ module OctocatalogDiff
|
|
23
23
|
|
24
24
|
# Define the Option class and newoption() method for use by cli/options/*.rb files
|
25
25
|
class Option
|
26
|
-
DEFAULT_WEIGHT = 999
|
27
26
|
def self.has_weight(w) # rubocop:disable Style/PredicateName
|
28
27
|
@weight = w
|
29
28
|
end
|
@@ -38,7 +37,9 @@ module OctocatalogDiff
|
|
38
37
|
elsif @weight
|
39
38
|
@weight
|
40
39
|
else
|
41
|
-
|
40
|
+
# :nocov:
|
41
|
+
raise ArgumentError, "Option #{name} does not have a weight specified. Add 'has_weight NNN' to control ordering."
|
42
|
+
# :nocov:
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
@@ -102,6 +103,7 @@ module OctocatalogDiff
|
|
102
103
|
datatype = opts.fetch(:datatype, '')
|
103
104
|
return option_globally_or_per_branch_string(opts) if datatype.is_a?(String)
|
104
105
|
return option_globally_or_per_branch_array(opts) if datatype.is_a?(Array)
|
106
|
+
return option_globally_or_per_branch_boolean(opts) if datatype.is_a?(TrueClass) || datatype.is_a?(FalseClass)
|
105
107
|
raise ArgumentError, "option_globally_or_per_branch not equipped to handle #{datatype.class}"
|
106
108
|
end
|
107
109
|
|
@@ -176,6 +178,40 @@ module OctocatalogDiff
|
|
176
178
|
end
|
177
179
|
end
|
178
180
|
|
181
|
+
# See description of `option_globally_or_per_branch`. This implements the logic for a boolean value.
|
182
|
+
# @param :parser [OptionParser object] The OptionParser argument
|
183
|
+
# @param :options [Hash] Options hash being constructed; this is modified in this method.
|
184
|
+
# @param :cli_name [String] Name of option on command line (e.g. puppet-binary)
|
185
|
+
# @param :option_name [Symbol] Name of option in the options hash (e.g. :puppet_binary)
|
186
|
+
# @param :desc [String] Description of option on the command line; will have "for the XX branch" appended
|
187
|
+
def self.option_globally_or_per_branch_boolean(opts)
|
188
|
+
parser = opts.fetch(:parser)
|
189
|
+
options = opts.fetch(:options)
|
190
|
+
cli_name = opts.fetch(:cli_name)
|
191
|
+
option_name = opts.fetch(:option_name)
|
192
|
+
desc = opts.fetch(:desc)
|
193
|
+
|
194
|
+
flag = cli_name
|
195
|
+
from_option = "from_#{option_name}".to_sym
|
196
|
+
to_option = "to_#{option_name}".to_sym
|
197
|
+
parser.on("--[no-]#{flag}", "#{desc} globally") do |x|
|
198
|
+
translated = translate_option(opts[:translator], x)
|
199
|
+
options[to_option] = translated
|
200
|
+
options[from_option] = translated
|
201
|
+
post_process(opts[:post_process], options)
|
202
|
+
end
|
203
|
+
parser.on("--[no-]to-#{flag}", "#{desc} for the to branch") do |x|
|
204
|
+
translated = translate_option(opts[:translator], x)
|
205
|
+
options[to_option] = translated
|
206
|
+
post_process(opts[:post_process], options)
|
207
|
+
end
|
208
|
+
parser.on("--[no-]from-#{flag}", "#{desc} for the from branch") do |x|
|
209
|
+
translated = translate_option(opts[:translator], x)
|
210
|
+
options[from_option] = translated
|
211
|
+
post_process(opts[:post_process], options)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
179
215
|
# If a validator was provided, run the validator on the supplied value. The validator is expected to
|
180
216
|
# throw an error if there is a problem. Note that the validator runs *before* the translator if both
|
181
217
|
# a validator and translator are supplied.
|
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Set hostname, which is used to look up facts in PuppetDB, and in the header of diff display.
|
4
|
+
# This option can recieve a single hostname, or a comma separated list of
|
5
|
+
# multiple hostnames, which are split into an Array. Multiple hostnames do not
|
6
|
+
# work with the `catalog-only` or `bootstrap-then-exit` options.
|
4
7
|
# @param parser [OptionParser object] The OptionParser argument
|
5
8
|
# @param options [Hash] Options hash being constructed; this is modified in this method.
|
6
9
|
|
@@ -8,8 +11,16 @@ OctocatalogDiff::Cli::Options::Option.newoption(:hostname) do
|
|
8
11
|
has_weight 1
|
9
12
|
|
10
13
|
def parse(parser, options)
|
11
|
-
parser.on(
|
12
|
-
|
14
|
+
parser.on(
|
15
|
+
'--hostname HOSTNAME1[,HOSTNAME2[,...]]',
|
16
|
+
'-n',
|
17
|
+
'Use PuppetDB facts from last run of a hostname or a comma separated list of multiple hostnames'
|
18
|
+
) do |hostname|
|
19
|
+
options[:node] = if hostname.include?(',')
|
20
|
+
hostname.split(',')
|
21
|
+
else
|
22
|
+
hostname
|
23
|
+
end
|
13
24
|
end
|
14
25
|
end
|
15
26
|
end
|
@@ -12,7 +12,7 @@ OctocatalogDiff::Cli::Options::Option.newoption(:pe_enc_token_file) do
|
|
12
12
|
def parse(parser, options)
|
13
13
|
parser.on('--pe-enc-token-file PATH', 'Path containing token for PE node classifier, relative or absolute') do |x|
|
14
14
|
proposed_token_path = x.start_with?('/') ? x : File.join(options[:basedir], x)
|
15
|
-
raise Errno::ENOENT, "Provided token (#{proposed_token_path}) does not exist" unless File.file?(proposed_token_path)
|
15
|
+
raise Errno::ENOENT, "Provided PE ENC token (#{proposed_token_path}) does not exist" unless File.file?(proposed_token_path)
|
16
16
|
options[:pe_enc_token] = File.read(proposed_token_path)
|
17
17
|
end
|
18
18
|
end
|
@@ -14,8 +14,8 @@ OctocatalogDiff::Cli::Options::Option.newoption(:puppet_master_api_version) do
|
|
14
14
|
options: options,
|
15
15
|
cli_name: 'puppet-master-api-version',
|
16
16
|
option_name: 'puppet_master_api_version',
|
17
|
-
desc: 'Puppet Master API version (2 for Puppet 3.x, 3 for Puppet 4.x)',
|
18
|
-
validator: ->(x) { x =~ /^[
|
17
|
+
desc: 'Puppet Master API version (2 for Puppet 3.x, 3 for Puppet 4.x, 4 for Puppet Server >= 6.3.0)',
|
18
|
+
validator: ->(x) { x =~ /^[234]$/ || raise(ArgumentError, 'Only API versions 2, 3, and 4 are supported') },
|
19
19
|
translator: ->(x) { x.to_i }
|
20
20
|
)
|
21
21
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Specify a PE RBAC token used to authenticate to Puppetserver for v4
|
4
|
+
# catalog API calls.
|
5
|
+
# @param parser [OptionParser object] The OptionParser argument
|
6
|
+
# @param options [Hash] Options hash being constructed; this is modified in this method.
|
7
|
+
OctocatalogDiff::Cli::Options::Option.newoption(:puppet_master_token) do
|
8
|
+
has_weight 310
|
9
|
+
|
10
|
+
def parse(parser, options)
|
11
|
+
OctocatalogDiff::Cli::Options.option_globally_or_per_branch(
|
12
|
+
parser: parser,
|
13
|
+
options: options,
|
14
|
+
datatype: '',
|
15
|
+
cli_name: 'puppet-master-token',
|
16
|
+
option_name: 'puppet_master_token',
|
17
|
+
desc: 'PE RBAC token to authenticate to the Puppetserver API v4'
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Specify a path to a file containing a PE RBAC token used to authenticate to the
|
4
|
+
# Puppetserver for a v4 catalog API call.
|
5
|
+
# @param parser [OptionParser object] The OptionParser argument
|
6
|
+
# @param options [Hash] Options hash being constructed; this is modified in this method.
|
7
|
+
OctocatalogDiff::Cli::Options::Option.newoption(:puppet_master_token_file) do
|
8
|
+
has_weight 300
|
9
|
+
|
10
|
+
def parse(parser, options)
|
11
|
+
OctocatalogDiff::Cli::Options.option_globally_or_per_branch(
|
12
|
+
parser: parser,
|
13
|
+
options: options,
|
14
|
+
datatype: '',
|
15
|
+
cli_name: 'puppet-master-token-file',
|
16
|
+
option_name: 'puppet_master_token_file',
|
17
|
+
desc: 'File containing PE RBAC token to authenticate to the Puppetserver API v4',
|
18
|
+
translator: ->(x) { x.start_with?('/', '~') ? x : File.join(options[:basedir], x) },
|
19
|
+
post_process: lambda do |opts|
|
20
|
+
%w(to from).each do |prefix|
|
21
|
+
fileopt = "#{prefix}_puppet_master_token_file".to_sym
|
22
|
+
tokenopt = "#{prefix}_puppet_master_token".to_sym
|
23
|
+
|
24
|
+
tokenfile = opts[fileopt]
|
25
|
+
next if tokenfile.nil?
|
26
|
+
|
27
|
+
raise(Errno::ENOENT, "Token file #{tokenfile} is not readable") unless File.readable?(tokenfile)
|
28
|
+
|
29
|
+
token = File.read(tokenfile).strip
|
30
|
+
opts[tokenopt] ||= token
|
31
|
+
end
|
32
|
+
end
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Specify if, when using the Puppetserver v4 catalog API, the Puppetserver should
|
4
|
+
# update the catalog in PuppetDB.
|
5
|
+
# @param parser [OptionParser object] The OptionParser argument
|
6
|
+
# @param options [Hash] Options hash being constructed; this is modified in this method.
|
7
|
+
OctocatalogDiff::Cli::Options::Option.newoption(:puppet_master_update_catalog) do
|
8
|
+
has_weight 320
|
9
|
+
|
10
|
+
def parse(parser, options)
|
11
|
+
OctocatalogDiff::Cli::Options.option_globally_or_per_branch(
|
12
|
+
parser: parser,
|
13
|
+
options: options,
|
14
|
+
datatype: false,
|
15
|
+
cli_name: 'puppet-master-update-catalog',
|
16
|
+
option_name: 'puppet_master_update_catalog',
|
17
|
+
desc: 'Update catalog in PuppetDB when using Puppetmaster API version 4'
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Specify if, when using the Puppetserver v4 catalog API, the Puppetserver should
|
4
|
+
# update the facts in PuppetDB.
|
5
|
+
# @param parser [OptionParser object] The OptionParser argument
|
6
|
+
# @param options [Hash] Options hash being constructed; this is modified in this method.
|
7
|
+
OctocatalogDiff::Cli::Options::Option.newoption(:puppet_master_update_facts) do
|
8
|
+
has_weight 320
|
9
|
+
|
10
|
+
def parse(parser, options)
|
11
|
+
OctocatalogDiff::Cli::Options.option_globally_or_per_branch(
|
12
|
+
parser: parser,
|
13
|
+
options: options,
|
14
|
+
datatype: false,
|
15
|
+
cli_name: 'puppet-master-update-facts',
|
16
|
+
option_name: 'puppet_master_update_facts',
|
17
|
+
desc: 'Update facts in PuppetDB when using Puppetmaster API version 4'
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# When pulling facts from PuppetDB in a Puppet Enterprise environment, also include
|
4
|
+
# the Puppet Enterprise Package Inventory data in the fact results, if available.
|
5
|
+
# Generally you should not need to specify this, but including the package inventory
|
6
|
+
# data will produce a more accurate set of input facts for environments using
|
7
|
+
# package inventory.
|
8
|
+
# @param parser [OptionParser object] The OptionParser argument
|
9
|
+
# @param options [Hash] Options hash being constructed; this is modified in this method.
|
10
|
+
OctocatalogDiff::Cli::Options::Option.newoption(:puppetdb_package_inventory) do
|
11
|
+
has_weight 150
|
12
|
+
|
13
|
+
def parse(parser, options)
|
14
|
+
parser.on('--[no-]puppetdb-package-inventory', 'Include Puppet Enterprise package inventory data, if found') do |x|
|
15
|
+
options[:puppetdb_package_inventory] = x
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Specify the PE RBAC token to access the PuppetDB API. Refer to
|
4
|
+
# https://puppet.com/docs/pe/latest/rbac/rbac_token_auth_intro.html#generate-a-token-using-puppet-access
|
5
|
+
# for details on generating and obtaining a token. Use this option to specify the text
|
6
|
+
# of the token. (Use --puppetdb-token-file to read the content of the token from a file.)
|
7
|
+
# @param parser [OptionParser object] The OptionParser argument
|
8
|
+
# @param options [Hash] Options hash being constructed; this is modified in this method.
|
9
|
+
OctocatalogDiff::Cli::Options::Option.newoption(:puppetdb_token) do
|
10
|
+
has_weight 310
|
11
|
+
|
12
|
+
def parse(parser, options)
|
13
|
+
parser.on('--puppetdb-token TOKEN', 'Token to access the PuppetDB API') do |token|
|
14
|
+
options[:puppetdb_token] = token
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Specify the PE RBAC token to access the PuppetDB API. Refer to
|
4
|
+
# https://puppet.com/docs/pe/latest/rbac/rbac_token_auth_intro.html#generate-a-token-using-puppet-access
|
5
|
+
# for details on generating and obtaining a token. Use this option to specify the text
|
6
|
+
# in a file, to read the content of the token from the file.
|
7
|
+
# @param parser [OptionParser object] The OptionParser argument
|
8
|
+
# @param options [Hash] Options hash being constructed; this is modified in this method.
|
9
|
+
OctocatalogDiff::Cli::Options::Option.newoption(:puppetdb_token_file) do
|
10
|
+
has_weight 310
|
11
|
+
|
12
|
+
def parse(parser, options)
|
13
|
+
parser.on('--puppetdb-token-file PATH', 'Path containing token for PuppetDB API, relative or absolute') do |x|
|
14
|
+
proposed_token_path = x.start_with?('/') ? x : File.join(options[:basedir], x)
|
15
|
+
unless File.file?(proposed_token_path)
|
16
|
+
raise Errno::ENOENT, "Provided PuppetDB API token (#{proposed_token_path}) does not exist"
|
17
|
+
end
|
18
|
+
options[:puppetdb_token] = File.read(proposed_token_path)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -36,6 +36,7 @@ module OctocatalogDiff
|
|
36
36
|
exception_class = nil
|
37
37
|
exception_message = nil
|
38
38
|
obj_to_return = nil
|
39
|
+
packages = nil
|
39
40
|
(retries + 1).times do
|
40
41
|
begin
|
41
42
|
result = puppetdb.get(uri)
|
@@ -61,8 +62,48 @@ module OctocatalogDiff
|
|
61
62
|
exception_message = "Fact retrieval failed for node #{node} from PuppetDB (#{exc.message})"
|
62
63
|
end
|
63
64
|
end
|
64
|
-
|
65
|
-
raise exception_class, exception_message
|
65
|
+
|
66
|
+
raise exception_class, exception_message if obj_to_return.nil?
|
67
|
+
|
68
|
+
return obj_to_return if puppetdb_api_version < 4 || (!options[:puppetdb_package_inventory])
|
69
|
+
|
70
|
+
(retries + 1).times do
|
71
|
+
begin
|
72
|
+
result = puppetdb.get("/pdb/query/v4/package-inventory/#{node}")
|
73
|
+
packages = {}
|
74
|
+
result.each do |pkg|
|
75
|
+
key = "#{pkg['package_name']}+#{pkg['provider']}"
|
76
|
+
# Need to handle the situation where a package has multiple versions installed.
|
77
|
+
# The _puppet_inventory_1 hash lists them separated by "; ".
|
78
|
+
if packages.key?(key)
|
79
|
+
packages[key]['version'] += "; #{pkg['version']}"
|
80
|
+
else
|
81
|
+
packages[key] = pkg
|
82
|
+
end
|
83
|
+
end
|
84
|
+
break
|
85
|
+
rescue OctocatalogDiff::Errors::PuppetDBConnectionError => exc
|
86
|
+
exception_class = OctocatalogDiff::Errors::FactSourceError
|
87
|
+
exception_message = "Package inventory retrieval failed (#{exc.class}) (#{exc.message})"
|
88
|
+
# This is not expected to occur, but we'll leave it just in case. A query to package-inventory
|
89
|
+
# for a non-existant node returns a 200 OK with an empty list of packages:
|
90
|
+
rescue OctocatalogDiff::Errors::PuppetDBNodeNotFoundError
|
91
|
+
packages = {}
|
92
|
+
rescue OctocatalogDiff::Errors::PuppetDBGenericError => exc
|
93
|
+
exception_class = OctocatalogDiff::Errors::FactRetrievalError
|
94
|
+
exception_message = "Package inventory retrieval failed for node #{node} from PuppetDB (#{exc.message})"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
raise exception_class, exception_message if packages.nil?
|
99
|
+
|
100
|
+
unless packages.empty?
|
101
|
+
obj_to_return['values']['_puppet_inventory_1'] = {
|
102
|
+
'packages' => packages.values.map { |pkg| [pkg['package_name'], pkg['version'], pkg['provider']] }
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
obj_to_return
|
66
107
|
end
|
67
108
|
end
|
68
109
|
end
|
@@ -42,6 +42,7 @@ module OctocatalogDiff
|
|
42
42
|
# @param :puppetdb_ssl_client_p12 [String] pkcs12-encoded client key and certificate
|
43
43
|
# @param :puppetdb_ssl_client_password [String] Path to file containing password for SSL client key (any format)
|
44
44
|
# @param :puppetdb_ssl_client_auth [Boolean] Override the client-auth that is guessed from parameters
|
45
|
+
# @param :puppetdb_token [String] PE RBAC token to authenticate to PuppetDB API
|
45
46
|
# @param :timeout [Integer] Connection timeout for PuppetDB (default=10)
|
46
47
|
def initialize(options = {})
|
47
48
|
@connections =
|
@@ -107,7 +108,10 @@ module OctocatalogDiff
|
|
107
108
|
].join('')
|
108
109
|
|
109
110
|
begin
|
110
|
-
|
111
|
+
headers = { 'Accept' => 'application/json' }
|
112
|
+
headers['X-Authentication'] = @options[:puppetdb_token] if @options[:puppetdb_token]
|
113
|
+
more_options = { headers: headers, timeout: @timeout }
|
114
|
+
|
111
115
|
if connection[:username] || connection[:password]
|
112
116
|
more_options[:basic_auth] = { username: connection[:username], password: connection[:password] }
|
113
117
|
end
|
@@ -129,22 +129,26 @@ module OctocatalogDiff
|
|
129
129
|
|
130
130
|
# Waiting for children and handling results
|
131
131
|
while pidmap.any?
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
132
|
+
pidmap.each do |pid|
|
133
|
+
status = Process.waitpid2(pid[0], Process::WNOHANG)
|
134
|
+
next if status.nil?
|
135
|
+
this_pid, exit_obj = status
|
136
|
+
next unless this_pid && pidmap.key?(this_pid)
|
137
|
+
index = pidmap[this_pid][:index]
|
138
|
+
exitstatus = exit_obj.exitstatus
|
139
|
+
raise "PID=#{this_pid} exited abnormally: #{exit_obj.inspect}" if exitstatus.nil?
|
140
|
+
raise "PID=#{this_pid} exited with status #{exitstatus}" unless exitstatus.zero?
|
141
|
+
|
142
|
+
input = File.read(File.join(ipc_tempdir, "#{this_pid}.dat"))
|
143
|
+
result[index] = Marshal.load(input) # rubocop:disable Security/MarshalLoad
|
144
|
+
time_delta = Time.now - pidmap[this_pid][:start_time]
|
145
|
+
pidmap.delete(this_pid)
|
146
|
+
|
147
|
+
logger.debug "PID=#{this_pid} completed in #{time_delta} seconds, #{input.length} bytes"
|
148
|
+
|
149
|
+
next if result[index].status
|
150
|
+
return result[index].exception
|
151
|
+
end
|
148
152
|
end
|
149
153
|
|
150
154
|
logger.debug 'All child processes completed with no exceptions raised'
|