octocatalog-diff 1.5.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|