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/doc/requirements.md
CHANGED
@@ -2,10 +2,14 @@
|
|
2
2
|
|
3
3
|
To run `octocatalog-diff` you will need these basics:
|
4
4
|
|
5
|
-
-
|
5
|
+
- An appropriate Puppet version and [corresponding ruby version](https://puppet.com/docs/puppet/5.4/system_requirements.html)
|
6
|
+
- Puppet 5.x officially supports Ruby 2.4
|
7
|
+
- Puppet 4.x officially supports Ruby 2.1, but seems to work fine with later versions as well
|
8
|
+
- Puppet 3.8.7 -- we attempt to maintain compatibility in `octocatalog-diff` to facilitate upgrades even though this version is no longer supported by Puppet
|
9
|
+
- We don't officially support Puppet 3.8.6 or before
|
6
10
|
- Mac OS, Linux, or other Unix-line operating system (Windows is not supported)
|
7
11
|
- Ability to install gems, e.g. with [rbenv](https://github.com/rbenv/rbenv) or [rvm](https://rvm.io/), or root privileges to install into the system Ruby
|
8
|
-
- Puppet agent for [Linux](https://docs.puppet.com/puppet/latest/reference/install_linux.html) or [Mac OS X](https://docs.puppet.com/puppet/latest/reference/install_osx.html), or installed as a gem
|
12
|
+
- Puppet agent for [Linux](https://docs.puppet.com/puppet/latest/reference/install_linux.html) or [Mac OS X](https://docs.puppet.com/puppet/latest/reference/install_osx.html), or installed as a gem
|
9
13
|
|
10
14
|
We recommend that you also have the following to get the most out of `octocatalog-diff`, but these are not absolute requirements:
|
11
15
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'diffy'
|
4
|
+
require 'digest'
|
4
5
|
require 'hashdiff'
|
5
6
|
require 'json'
|
6
7
|
require 'set'
|
@@ -11,6 +12,8 @@ require_relative '../errors'
|
|
11
12
|
require_relative '../util/util'
|
12
13
|
require_relative 'filter'
|
13
14
|
|
15
|
+
HashDiff = Hashdiff unless defined? HashDiff
|
16
|
+
|
14
17
|
module OctocatalogDiff
|
15
18
|
module CatalogDiff
|
16
19
|
# Calculate the difference between two Puppet catalogs.
|
@@ -263,7 +266,7 @@ module OctocatalogDiff
|
|
263
266
|
|
264
267
|
# Handle parameters
|
265
268
|
if k == 'parameters'
|
266
|
-
cleansed_param = cleanse_parameters_hash(v)
|
269
|
+
cleansed_param = cleanse_parameters_hash(v, resource.fetch('sensitive_parameters', []))
|
267
270
|
hsh[k] = cleansed_param unless cleansed_param.nil? || cleansed_param.empty?
|
268
271
|
elsif k == 'tags'
|
269
272
|
# The order of tags is unimportant. Sort this array to avoid false diffs if order changes.
|
@@ -294,10 +297,13 @@ module OctocatalogDiff
|
|
294
297
|
# Use diffy to get only the lines that have changed in a text object.
|
295
298
|
# As we iterate through the diff, jump out if we have our answer: either
|
296
299
|
# true if '=~>' finds ANY match, or false if '=&>' fails to find a match.
|
297
|
-
Diffy::Diff.new(old_val, new_val, context: 0)
|
300
|
+
diffy_result = Diffy::Diff.new(old_val, new_val, context: 0)
|
301
|
+
newline_alerts = diffy_result.count { |line| line.strip == '\' }
|
302
|
+
diffy_result.each do |line|
|
298
303
|
if regex.match(line.strip)
|
299
304
|
return true if operator == '=~>'
|
300
305
|
elsif operator == '=&>'
|
306
|
+
next if line.strip == '\' && newline_alerts == 2
|
301
307
|
return false
|
302
308
|
end
|
303
309
|
end
|
@@ -334,7 +340,8 @@ module OctocatalogDiff
|
|
334
340
|
# =-> Attribute must have been removed and equal this
|
335
341
|
# =~> Change must match regexp (one line of change matching is sufficient)
|
336
342
|
# =&> Change must match regexp (all lines of change MUST match regexp)
|
337
|
-
|
343
|
+
# =s> Change must be array and contain identical elements, ignoring order
|
344
|
+
if rule_attr =~ /\A(.+?)(=[\-\+~&s]?>)(.+)/m
|
338
345
|
rule_attr = Regexp.last_match(1)
|
339
346
|
operator = Regexp.last_match(2)
|
340
347
|
value = Regexp.last_match(3)
|
@@ -355,6 +362,9 @@ module OctocatalogDiff
|
|
355
362
|
raise RegexpError, "Invalid ignore regexp for #{key}: #{exc.message}"
|
356
363
|
end
|
357
364
|
matcher = ->(x, y) { regexp_operator_match?(operator, my_regex, x, y) }
|
365
|
+
elsif operator == '=s>'
|
366
|
+
raise ArgumentError, "Invalid ignore option for =s>, must be '='" unless value == '='
|
367
|
+
matcher = ->(x, y) { x.is_a?(Array) && y.is_a?(Array) && Set.new(x) == Set.new(y) }
|
358
368
|
end
|
359
369
|
end
|
360
370
|
|
@@ -394,6 +404,13 @@ module OctocatalogDiff
|
|
394
404
|
return false unless rule[:title].casecmp(hsh[:title]).zero?
|
395
405
|
end
|
396
406
|
|
407
|
+
# If rule[:attr] is a regular expression, handle that case here.
|
408
|
+
if rule[:attr].is_a?(Regexp)
|
409
|
+
return false unless hsh[:attr].is_a?(String)
|
410
|
+
return false unless rule[:attr].match(hsh[:attr])
|
411
|
+
return ignore_match_true(hsh, rule)
|
412
|
+
end
|
413
|
+
|
397
414
|
# Special 'attributes': Ignore specific diff types (+ add, - remove, ~ and ! change)
|
398
415
|
if rule[:attr] =~ /\A[\-\+~!]+\Z/
|
399
416
|
return ignore_match_true(hsh, rule) if rule[:attr].include?(diff_type)
|
@@ -446,10 +463,18 @@ module OctocatalogDiff
|
|
446
463
|
|
447
464
|
# Cleanse parameters of filtered attributes.
|
448
465
|
# @param parameters_hash [Hash] Hash of parameters
|
466
|
+
# @param sensitive_parameters [Array] Array of sensitive parameters
|
449
467
|
# @return [Hash] Cleaned parameters hash (original input hash is not altered)
|
450
|
-
def cleanse_parameters_hash(parameters_hash)
|
468
|
+
def cleanse_parameters_hash(parameters_hash, sensitive_parameters)
|
451
469
|
result = parameters_hash.dup
|
452
470
|
|
471
|
+
# hides sensitive params. We still need to know if there's a going to
|
472
|
+
# be a diff, so we hash the value.
|
473
|
+
sensitive_parameters.each do |p|
|
474
|
+
md5 = Digest::MD5.hexdigest Marshal.dump(result[p])
|
475
|
+
result[p] = 'Sensitive [md5sum ' + md5 + ']'
|
476
|
+
end
|
477
|
+
|
453
478
|
# 'before' and 'require' handle internal Puppet ordering but do not affect what
|
454
479
|
# happens on the target machine. Don't consider these for the purpose of catalog diff.
|
455
480
|
result.delete('before')
|
@@ -2,6 +2,7 @@ require_relative '../api/v1/diff'
|
|
2
2
|
require_relative 'filter/absent_file'
|
3
3
|
require_relative 'filter/compilation_dir'
|
4
4
|
require_relative 'filter/json'
|
5
|
+
require_relative 'filter/single_item_array'
|
5
6
|
require_relative 'filter/yaml'
|
6
7
|
|
7
8
|
require 'stringio'
|
@@ -13,7 +14,7 @@ module OctocatalogDiff
|
|
13
14
|
attr_accessor :logger
|
14
15
|
|
15
16
|
# List the available filters here (by class name) for use in the validator method.
|
16
|
-
AVAILABLE_FILTERS = %w(AbsentFile CompilationDir JSON YAML).freeze
|
17
|
+
AVAILABLE_FILTERS = %w(AbsentFile CompilationDir JSON SingleItemArray YAML).freeze
|
17
18
|
|
18
19
|
# Public: Determine whether a particular filter exists. This can be used to validate
|
19
20
|
# a user-submitted filter.
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../filter'
|
4
|
+
require_relative '../../util/util'
|
4
5
|
|
5
6
|
module OctocatalogDiff
|
6
7
|
module CatalogDiff
|
@@ -35,43 +36,46 @@ module OctocatalogDiff
|
|
35
36
|
|
36
37
|
# Check for a change where the difference in a parameter exactly corresponds to the difference in the
|
37
38
|
# compilation directory.
|
38
|
-
if diff.change?
|
39
|
-
|
40
|
-
|
41
|
-
from_match = false
|
42
|
-
to_before = nil
|
43
|
-
to_after = nil
|
44
|
-
to_match = false
|
39
|
+
if diff.change?
|
40
|
+
o = remove_compilation_dir(diff.old_value, dir2)
|
41
|
+
n = remove_compilation_dir(diff.new_value, dir1)
|
45
42
|
|
46
|
-
if diff.old_value
|
47
|
-
from_before = Regexp.last_match(1) || ''
|
48
|
-
from_after = Regexp.last_match(2) || ''
|
49
|
-
from_match = true
|
50
|
-
end
|
51
|
-
|
52
|
-
if diff.new_value =~ /^(.*)#{dir1}(.*)$/m
|
53
|
-
to_before = Regexp.last_match(1) || ''
|
54
|
-
to_after = Regexp.last_match(2) || ''
|
55
|
-
to_match = true
|
56
|
-
end
|
57
|
-
|
58
|
-
if from_match && to_match && to_before == from_before && to_after == from_after
|
43
|
+
if o != diff.old_value || n != diff.new_value
|
59
44
|
message = "Resource key #{diff.type}[#{diff.title}] #{diff.structure.join(' => ')}"
|
60
|
-
message += '
|
45
|
+
message += ' may depend on catalog compilation directory, but there may be differences.'
|
46
|
+
message += ' This is included in results for now, but please verify.'
|
61
47
|
@logger.warn message
|
62
|
-
return true
|
63
48
|
end
|
64
49
|
|
65
|
-
if
|
50
|
+
if o == n
|
66
51
|
message = "Resource key #{diff.type}[#{diff.title}] #{diff.structure.join(' => ')}"
|
67
|
-
message += '
|
68
|
-
message += ' This is included in results for now, but please verify.'
|
52
|
+
message += ' appears to depend on catalog compilation directory. Suppressed from results.'
|
69
53
|
@logger.warn message
|
54
|
+
return true
|
70
55
|
end
|
71
56
|
end
|
72
57
|
|
73
58
|
false
|
74
59
|
end
|
60
|
+
|
61
|
+
def remove_compilation_dir(v, dir)
|
62
|
+
value = OctocatalogDiff::Util::Util.deep_dup(v)
|
63
|
+
traverse(value) do |e|
|
64
|
+
e.gsub!(dir, '') if e.respond_to?(:gsub!)
|
65
|
+
end
|
66
|
+
value
|
67
|
+
end
|
68
|
+
|
69
|
+
def traverse(a)
|
70
|
+
case a
|
71
|
+
when Array
|
72
|
+
a.map { |v| traverse(v, &Proc.new) }
|
73
|
+
when Hash
|
74
|
+
traverse(a.values, &Proc.new)
|
75
|
+
else
|
76
|
+
yield a
|
77
|
+
end
|
78
|
+
end
|
75
79
|
end
|
76
80
|
end
|
77
81
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../filter'
|
4
|
+
|
5
|
+
module OctocatalogDiff
|
6
|
+
module CatalogDiff
|
7
|
+
class Filter
|
8
|
+
# Filter out changes in parameters when one catalog has a parameter that's an object and
|
9
|
+
# the other catalog has that same parameter as an array containing the same object.
|
10
|
+
# For example, under this filter, the following is not a change:
|
11
|
+
# catalog1: notify => "Service[foo]"
|
12
|
+
# catalog2: notify => ["Service[foo]"]
|
13
|
+
class SingleItemArray < OctocatalogDiff::CatalogDiff::Filter
|
14
|
+
# Public: Implement the filter for single-item arrays whose item exactly matches the
|
15
|
+
# item that's not in an array in the other catalog.
|
16
|
+
#
|
17
|
+
# @param diff [OctocatalogDiff::API::V1::Diff] Difference
|
18
|
+
# @param _options [Hash] Additional options (there are none for this filter)
|
19
|
+
# @return [Boolean] true if this should be filtered out, false otherwise
|
20
|
+
def filtered?(diff, _options = {})
|
21
|
+
# Skip additions or removals - focus only on changes
|
22
|
+
return false unless diff.change?
|
23
|
+
old_value = diff.old_value
|
24
|
+
new_value = diff.new_value
|
25
|
+
|
26
|
+
# Skip unless there is a single-item array under consideration
|
27
|
+
return false unless
|
28
|
+
(old_value.is_a?(Array) && old_value.size == 1) ||
|
29
|
+
(new_value.is_a?(Array) && new_value.size == 1)
|
30
|
+
|
31
|
+
# Skip if both the old value and new value are arrays
|
32
|
+
return false if old_value.is_a?(Array) && new_value.is_a?(Array)
|
33
|
+
|
34
|
+
# Do comparison
|
35
|
+
if old_value.is_a?(Array)
|
36
|
+
old_value.first == new_value
|
37
|
+
else
|
38
|
+
new_value.first == old_value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -149,13 +149,13 @@ module OctocatalogDiff
|
|
149
149
|
raise ArgumentError, 'Called install_fact_file without node, or with an empty node'
|
150
150
|
end
|
151
151
|
|
152
|
-
facts = if options[:
|
152
|
+
facts = if options[:facts].is_a?(OctocatalogDiff::Facts)
|
153
|
+
options[:facts].dup
|
154
|
+
elsif options[:fact_file]
|
153
155
|
raise Errno::ENOENT, "Fact file #{options[:fact_file]} does not exist" unless File.file?(options[:fact_file])
|
154
156
|
fact_file_opts = { fact_file_string: File.read(options[:fact_file]) }
|
155
157
|
fact_file_opts[:backend] = Regexp.last_match(1).to_sym if options[:fact_file] =~ /.*\.(\w+)$/
|
156
158
|
OctocatalogDiff::Facts.new(fact_file_opts)
|
157
|
-
elsif options[:facts].is_a?(OctocatalogDiff::Facts)
|
158
|
-
options[:facts].dup
|
159
159
|
else
|
160
160
|
raise ArgumentError, 'No facts passed to "install_fact_file" method'
|
161
161
|
end
|
@@ -54,9 +54,21 @@ module OctocatalogDiff
|
|
54
54
|
raise ArgumentError, 'Puppet binary was not supplied' if @puppet_binary.nil?
|
55
55
|
raise Errno::ENOENT, "Puppet binary #{@puppet_binary} doesn't exist" unless File.file?(@puppet_binary)
|
56
56
|
|
57
|
+
puppet_version = Gem::Version.new(@options[:puppet_version])
|
58
|
+
|
57
59
|
# Node to compile
|
58
60
|
cmdline = []
|
59
|
-
|
61
|
+
# The 'puppet master --compile' command was removed in Puppet 6.x and replaced in
|
62
|
+
# Puppet 6.5 with an identically functioning 'puppet catalog compile' command.
|
63
|
+
# From versions 6.0.0 until 6.5.0 there is no compatible invocation method.
|
64
|
+
if puppet_version < Gem::Version.new('6.0.0')
|
65
|
+
cmdline.concat ['master', '--compile', Shellwords.escape(@node)]
|
66
|
+
elsif puppet_version < Gem::Version.new('6.5.0')
|
67
|
+
raise OctocatalogDiff::Errors::PuppetVersionError,
|
68
|
+
'Octocatalog-diff does not support Puppet versions >= 6.0.0 and < 6.5.0'
|
69
|
+
else
|
70
|
+
cmdline.concat ['catalog', 'compile', Shellwords.escape(@node)]
|
71
|
+
end
|
60
72
|
|
61
73
|
# storeconfigs?
|
62
74
|
if @options[:storeconfigs]
|
@@ -93,11 +105,21 @@ module OctocatalogDiff
|
|
93
105
|
# Some typical options for puppet
|
94
106
|
cmdline.concat %w(
|
95
107
|
--no-daemonize
|
96
|
-
--no-ca
|
97
108
|
--color=false
|
98
|
-
--config_version="/bin/echo catalogscript"
|
99
109
|
)
|
100
110
|
|
111
|
+
if puppet_version < Gem::Version.new('6.0.0')
|
112
|
+
# This config_version parameter causes an error when run with Puppet 6.x. Per
|
113
|
+
# the Puppet configuration settings docs, the below config_version argument
|
114
|
+
# may not actually be valid, but for backward compatibility's sake we'll keep it
|
115
|
+
# for the versions it has always worked with:
|
116
|
+
cmdline.concat ['--config_version="/bin/echo catalogscript"']
|
117
|
+
|
118
|
+
# The 'ca' configuration option was removed in Puppet 6, but we'll keep it
|
119
|
+
# for older versions:
|
120
|
+
cmdline.concat ['--no-ca']
|
121
|
+
end
|
122
|
+
|
101
123
|
# Add environment - only make this variable if preserve_environments is used.
|
102
124
|
# If preserve_environments is not used, the hard-coded 'production' here matches
|
103
125
|
# up with the symlink created under the temporary directory structure.
|
@@ -191,6 +191,8 @@ module OctocatalogDiff
|
|
191
191
|
build
|
192
192
|
raise OctocatalogDiff::Errors::CatalogError, 'Catalog does not appear to have been built' if !valid? && error_message.nil?
|
193
193
|
raise OctocatalogDiff::Errors::CatalogError, error_message unless valid?
|
194
|
+
# Handle the structure returned by the /puppet/v4/catalog Puppetserver endpoint:
|
195
|
+
return @catalog['catalog']['resources'] if @catalog['catalog'].is_a?(Hash) && @catalog['catalog']['resources'].is_a?(Array)
|
194
196
|
return @catalog['data']['resources'] if @catalog['data'].is_a?(Hash) && @catalog['data']['resources'].is_a?(Array)
|
195
197
|
return @catalog['resources'] if @catalog['resources'].is_a?(Array)
|
196
198
|
# This is a bug condition
|
@@ -304,20 +306,36 @@ module OctocatalogDiff
|
|
304
306
|
unless res =~ /\A([\w:]+)\[(.+)\]\z/
|
305
307
|
raise ArgumentError, "Resource #{res} is not in the expected format"
|
306
308
|
end
|
307
|
-
|
309
|
+
|
310
|
+
type = Regexp.last_match(1)
|
311
|
+
title = normalized_title(Regexp.last_match(2), type)
|
312
|
+
resource(type: type, title: title).nil?
|
308
313
|
end
|
309
314
|
end
|
310
315
|
|
316
|
+
# Private method: Given a title string, normalize it according to the rules
|
317
|
+
# used by puppet 4.10.x for file resource title normalization:
|
318
|
+
# https://github.com/puppetlabs/puppet/blob/4.10.x/lib/puppet/type/file.rb#L42
|
319
|
+
def normalized_title(title_string, type)
|
320
|
+
return title_string if type != 'File'
|
321
|
+
|
322
|
+
matches = title_string.match(%r{^(?<normalized_path>/|.+:/|.*[^/])/*\Z}m)
|
323
|
+
matches[:normalized_path] || title_string
|
324
|
+
end
|
325
|
+
|
311
326
|
# Private method: Build the resource hash to be used used for O(1) lookups by type and title.
|
312
327
|
# This method is called the first time the resource hash is accessed.
|
313
328
|
def build_resource_hash
|
314
329
|
@resource_hash = {}
|
315
330
|
resources.each do |resource|
|
316
331
|
@resource_hash[resource['type']] ||= {}
|
317
|
-
@resource_hash[resource['type']][resource['title']] = resource
|
318
332
|
|
319
|
-
|
320
|
-
|
333
|
+
title = normalized_title(resource['title'], resource['type'])
|
334
|
+
@resource_hash[resource['type']][title] = resource
|
335
|
+
|
336
|
+
if resource.key?('parameters')
|
337
|
+
@resource_hash[resource['type']][resource['parameters']['alias']] = resource if resource['parameters'].key?('alias')
|
338
|
+
@resource_hash[resource['type']][resource['parameters']['name']] = resource if resource['parameters'].key?('name')
|
321
339
|
end
|
322
340
|
end
|
323
341
|
end
|
@@ -147,7 +147,8 @@ module OctocatalogDiff
|
|
147
147
|
puppet_binary: @puppet_binary,
|
148
148
|
fact_file: @builddir.fact_file,
|
149
149
|
dir: @builddir.tempdir,
|
150
|
-
enc: @builddir.enc
|
150
|
+
enc: @builddir.enc,
|
151
|
+
puppet_version: puppet_version
|
151
152
|
)
|
152
153
|
OctocatalogDiff::CatalogUtil::Command.new(command_opts)
|
153
154
|
end
|
@@ -62,16 +62,19 @@ module OctocatalogDiff
|
|
62
62
|
fetch_catalog(logger)
|
63
63
|
end
|
64
64
|
|
65
|
-
# Returns a hash of parameters for
|
65
|
+
# Returns a hash of parameters for the requested version of the Puppet Server Catalog API.
|
66
66
|
# @return [Hash] Hash of parameters
|
67
67
|
#
|
68
68
|
# Note: The double escaping of the facts here is implemented to correspond to a long standing
|
69
69
|
# bug in the Puppet code. See https://github.com/puppetlabs/puppet/pull/1818 and
|
70
70
|
# https://docs.puppet.com/puppet/latest/http_api/http_catalog.html#parameters for explanation.
|
71
|
-
def puppet_catalog_api
|
72
|
-
{
|
71
|
+
def puppet_catalog_api(version)
|
72
|
+
api_style = {
|
73
73
|
2 => {
|
74
74
|
url: "https://#{@options[:puppet_master]}/#{@options[:branch]}/catalog/#{@node}",
|
75
|
+
headers: {
|
76
|
+
'Accept' => 'text/pson'
|
77
|
+
},
|
75
78
|
parameters: {
|
76
79
|
'facts_format' => 'pson',
|
77
80
|
'facts' => CGI.escape(@facts.fudge_timestamp.without('trusted').to_pson),
|
@@ -80,24 +83,59 @@ module OctocatalogDiff
|
|
80
83
|
},
|
81
84
|
3 => {
|
82
85
|
url: "https://#{@options[:puppet_master]}/puppet/v3/catalog/#{@node}",
|
86
|
+
headers: {
|
87
|
+
'Accept' => 'text/pson'
|
88
|
+
},
|
83
89
|
parameters: {
|
84
90
|
'environment' => @options[:branch],
|
85
91
|
'facts_format' => 'pson',
|
86
92
|
'facts' => CGI.escape(@facts.fudge_timestamp.without('trusted').to_pson),
|
87
93
|
'transaction_uuid' => SecureRandom.uuid
|
88
94
|
}
|
95
|
+
},
|
96
|
+
4 => {
|
97
|
+
url: "https://#{@options[:puppet_master]}/puppet/v4/catalog",
|
98
|
+
headers: {
|
99
|
+
'Content-Type' => 'application/json'
|
100
|
+
},
|
101
|
+
parameters: {
|
102
|
+
'certname' => @node,
|
103
|
+
'persistence' => {
|
104
|
+
'facts' => @options[:puppet_master_update_facts] || false,
|
105
|
+
'catalog' => @options[:puppet_master_update_catalog] || false
|
106
|
+
},
|
107
|
+
'environment' => @options[:branch],
|
108
|
+
'facts' => { 'values' => @facts.facts['values'] },
|
109
|
+
'options' => {
|
110
|
+
'prefer_requested_environment' => true,
|
111
|
+
'capture_logs' => false,
|
112
|
+
'log_level' => 'warning'
|
113
|
+
},
|
114
|
+
'transaction_uuid' => SecureRandom.uuid
|
115
|
+
}
|
89
116
|
}
|
90
117
|
}
|
118
|
+
|
119
|
+
params = api_style[version]
|
120
|
+
return nil if params.nil?
|
121
|
+
|
122
|
+
unless @options[:puppet_master_token].nil?
|
123
|
+
params[:headers]['X-Authentication'] = @options[:puppet_master_token]
|
124
|
+
end
|
125
|
+
|
126
|
+
params[:parameters] = params[:parameters].to_json if version >= 4
|
127
|
+
|
128
|
+
params
|
91
129
|
end
|
92
130
|
|
93
131
|
# Fetch catalog by contacting the Puppet master, sending the facts, and asking for the catalog. When the
|
94
132
|
# catalog is returned in PSON format, parse it to JSON and then set appropriate variables.
|
95
133
|
def fetch_catalog(logger)
|
96
134
|
api_version = @options[:puppet_master_api_version] || DEFAULT_PUPPET_SERVER_API
|
97
|
-
api = puppet_catalog_api
|
135
|
+
api = puppet_catalog_api(api_version)
|
98
136
|
raise ArgumentError, "Unsupported or invalid API version #{api_version}" unless api.is_a?(Hash)
|
99
137
|
|
100
|
-
more_options = { headers:
|
138
|
+
more_options = { headers: api[:headers], timeout: @timeout }
|
101
139
|
post_hash = api[:parameters]
|
102
140
|
|
103
141
|
response = nil
|