octocatalog-diff 1.5.2 → 2.1.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 +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'
|