octocatalog-diff 1.5.2 → 2.1.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 +50 -0
- data/doc/advanced-compare-file-text.md +79 -0
- data/doc/advanced-environments.md +5 -5
- data/doc/advanced-ignores.md +10 -0
- data/doc/advanced-puppet-master.md +25 -7
- data/doc/configuration-puppet.md +19 -19
- data/doc/configuration-puppetdb.md +8 -0
- data/doc/configuration.md +31 -31
- data/doc/dev/api/v1/calls/catalog-diff.md +6 -2
- data/doc/dev/api/v1/objects/diff.md +3 -3
- data/doc/dev/integration-tests.md +2 -2
- data/doc/dev/releasing.md +41 -41
- data/doc/dev/run-from-branch.md +23 -23
- data/doc/installation.md +14 -14
- data/doc/limitations.md +9 -9
- data/doc/optionsref.md +166 -11
- data/doc/requirements.md +6 -2
- data/lib/octocatalog-diff/catalog-diff/differ.rb +32 -5
- data/lib/octocatalog-diff/catalog-diff/filter/compilation_dir.rb +29 -25
- data/lib/octocatalog-diff/catalog-util/command.rb +25 -3
- data/lib/octocatalog-diff/catalog-util/fileresources.rb +39 -16
- 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 +38 -6
- data/lib/octocatalog-diff/cli/options.rb +36 -1
- data/lib/octocatalog-diff/cli/options/compare_file_text.rb +18 -0
- data/lib/octocatalog-diff/cli/options/hostname.rb +13 -2
- 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/use_lcs.rb +14 -0
- data/lib/octocatalog-diff/facts/json.rb +13 -2
- data/lib/octocatalog-diff/facts/puppetdb.rb +43 -2
- 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 +36 -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
|
@@ -43,7 +44,8 @@ module OctocatalogDiff
|
|
43
44
|
display_datatype_changes: true,
|
44
45
|
parallel: true,
|
45
46
|
suppress_absent_file_details: true,
|
46
|
-
hiera_path: 'hieradata'
|
47
|
+
hiera_path: 'hieradata',
|
48
|
+
use_lcs: true
|
47
49
|
}.freeze
|
48
50
|
|
49
51
|
# This method is the one to call externally. It is possible to specify alternate
|
@@ -116,16 +118,46 @@ module OctocatalogDiff
|
|
116
118
|
end
|
117
119
|
|
118
120
|
# Compile catalogs and do catalog-diff
|
119
|
-
|
121
|
+
node_set = options.delete(:node)
|
122
|
+
node_set = [node_set] unless node_set.is_a?(Array)
|
123
|
+
|
124
|
+
# run multiple node diffs in parallel
|
125
|
+
catalog_diffs = if node_set.size == 1
|
126
|
+
[run_octocatalog_diff(node_set.first, options, logger)]
|
127
|
+
else
|
128
|
+
::Parallel.map(node_set, in_threads: 4) { |node| run_octocatalog_diff(node, options, logger) }
|
129
|
+
end
|
130
|
+
|
131
|
+
# Return the resulting diff object if requested (generally for testing)
|
132
|
+
# or otherwise return exit code
|
133
|
+
return catalog_diffs.first if opts[:INTEGRATION]
|
134
|
+
|
135
|
+
all_diffs = catalog_diffs.map(&:diffs)
|
136
|
+
|
137
|
+
all_diffs.each do |diff|
|
138
|
+
next unless diff.any?
|
139
|
+
return EXITCODE_SUCCESS_WITH_DIFFS
|
140
|
+
end
|
141
|
+
|
142
|
+
EXITCODE_SUCCESS_NO_DIFFS
|
143
|
+
end
|
144
|
+
|
145
|
+
# Run the octocatalog-diff process for a given node. Return the diffs for a contribution to
|
146
|
+
# the final exit status.
|
147
|
+
# node - String with the node
|
148
|
+
# options - All of the currently defined options
|
149
|
+
# logger - Logger object
|
150
|
+
def self.run_octocatalog_diff(node, options, logger)
|
151
|
+
options_copy = options.merge(node: node)
|
152
|
+
catalog_diff = OctocatalogDiff::API::V1.catalog_diff(options_copy.merge(logger: logger))
|
120
153
|
diffs = catalog_diff.diffs
|
121
154
|
|
122
155
|
# Display diffs
|
123
|
-
printer_obj = OctocatalogDiff::Cli::Printer.new(
|
156
|
+
printer_obj = OctocatalogDiff::Cli::Printer.new(options_copy, logger)
|
124
157
|
printer_obj.printer(diffs, catalog_diff.from.compilation_dir, catalog_diff.to.compilation_dir)
|
125
158
|
|
126
|
-
# Return
|
127
|
-
|
128
|
-
diffs.any? ? EXITCODE_SUCCESS_WITH_DIFFS : EXITCODE_SUCCESS_NO_DIFFS
|
159
|
+
# Return catalog-diff object.
|
160
|
+
catalog_diff
|
129
161
|
end
|
130
162
|
|
131
163
|
# 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
|
@@ -103,6 +103,7 @@ module OctocatalogDiff
|
|
103
103
|
datatype = opts.fetch(:datatype, '')
|
104
104
|
return option_globally_or_per_branch_string(opts) if datatype.is_a?(String)
|
105
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)
|
106
107
|
raise ArgumentError, "option_globally_or_per_branch not equipped to handle #{datatype.class}"
|
107
108
|
end
|
108
109
|
|
@@ -177,6 +178,40 @@ module OctocatalogDiff
|
|
177
178
|
end
|
178
179
|
end
|
179
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
|
+
|
180
215
|
# If a validator was provided, run the validator on the supplied value. The validator is expected to
|
181
216
|
# throw an error if there is a problem. Note that the validator runs *before* the translator if both
|
182
217
|
# a validator and translator are supplied.
|
@@ -15,3 +15,21 @@ OctocatalogDiff::Cli::Options::Option.newoption(:compare_file_text) do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
# Sometimes there is a particular file resource for which the file text
|
20
|
+
# comparison is not desired, while not disabling that option globally. Similar
|
21
|
+
# to --ignore_tags, it's possible to tag the file resource and exempt it from
|
22
|
+
# the --compare_file_text checks.
|
23
|
+
# @param parser [OptionParser object] The OptionParser argument
|
24
|
+
# @param options [Hash] Options hash being constructed; this is modified in this method.
|
25
|
+
OctocatalogDiff::Cli::Options::Option.newoption(:compare_file_text_ignore_tags) do
|
26
|
+
has_weight 415
|
27
|
+
|
28
|
+
def parse(parser, options)
|
29
|
+
description = 'Tags that exclude a file resource from text comparison'
|
30
|
+
parser.on('--compare-file-text-ignore-tags STRING1[,STRING2[,...]]', Array, description) do |x|
|
31
|
+
options[:compare_file_text_ignore_tags] ||= []
|
32
|
+
options[:compare_file_text_ignore_tags].concat x
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -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
|
@@ -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,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Configures using the Longest common subsequence (LCS) algorithm to determine differences in arrays
|
4
|
+
# @param parser [OptionParser object] The OptionParser argument
|
5
|
+
# @param options [Hash] Options hash being constructed; this is modified in this method.
|
6
|
+
OctocatalogDiff::Cli::Options::Option.newoption(:use_lcs) do
|
7
|
+
has_weight 250
|
8
|
+
|
9
|
+
def parse(parser, options)
|
10
|
+
parser.on('--[no-]use-lcs', 'Use the LCS algorithm to determine differences in arrays') do |x|
|
11
|
+
options[:use_lcs] = x
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -14,8 +14,19 @@ module OctocatalogDiff
|
|
14
14
|
# @return [Hash] Facts
|
15
15
|
def self.fact_retriever(options = {}, node = '')
|
16
16
|
facts = ::JSON.parse(options.fetch(:fact_file_string))
|
17
|
-
|
18
|
-
|
17
|
+
|
18
|
+
if facts.keys.include?('name') && facts.keys.include?('values') && facts['values'].is_a?(Hash)
|
19
|
+
# If you saved the output of something like
|
20
|
+
# `puppet facts find $(hostname)` the structure will already be a
|
21
|
+
# {'name' => <fqdn>, 'values' => <hash of facts>}. We do nothing
|
22
|
+
# here because we don't want to double-encode.
|
23
|
+
else
|
24
|
+
facts = { 'name' => node, 'values' => facts }
|
25
|
+
end
|
26
|
+
|
27
|
+
facts['name'] = node unless node.empty?
|
28
|
+
facts['name'] = facts['values'].fetch('fqdn', 'unknown.node') if facts['name'].empty?
|
29
|
+
facts
|
19
30
|
end
|
20
31
|
end
|
21
32
|
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
|
@@ -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'
|