octocatalog-diff 1.3.0 → 1.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 67cc184b1ece4c4982d29bd714a674cc118f649d
4
- data.tar.gz: b2f18b550db7705f998cc2facab1525ded036399
3
+ metadata.gz: 5f0b3862bc04c59a30e41e90cf689f508cb68dd6
4
+ data.tar.gz: 4e67157b3a0ba173d2d1099d1e0aec79027a8f3b
5
5
  SHA512:
6
- metadata.gz: a22fdaab74c77e8e195bb7de464330f441444da4abe76729d98ea74a2c2958cc299ec030644f7804e99972ae162272b191062857d19fe47409877a05222ce2f3
7
- data.tar.gz: 08f100f0cec01c5821154341ee4bf4c3abe2d2b1350a41391716a605b966cf11948179a3f0b5452ddfd8782a937841bc6e9d53962e6178f3559027ed2239ce4d
6
+ metadata.gz: aa208efdde5e60a3bfdd1ced747632bc00d0fec7f916fed1765f9c8e7fe8de99160a7ef673e694fa043a90713af3df2c6eec620f9e792d2c0332864d04baf694
7
+ data.tar.gz: 228113ef4f77899dfe3185b839b9d67f41db4ae9db0901f9aa7aa3307d0c67d8983982118c8e2dff433f37f428cccc8c1ca755f83c41e9a8a9965a6027bd4952
data/.version CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.4.0
@@ -8,6 +8,15 @@
8
8
  </tr>
9
9
  </thead><tbody>
10
10
  <tr valign=top>
11
+ <td>1.4.0</td>
12
+ <td>2017-08-03</td>
13
+ <td>
14
+ <li><a href="https://github.com/github/octocatalog-diff/pull/135">#135</a>: (Enhancement) Puppet 5 compatibility</li>
15
+ <li><a href="https://github.com/github/octocatalog-diff/pull/140">#140</a>: (Internal) Prefix tmpdirs with ocd-</li>
16
+ <li><a href="https://github.com/github/octocatalog-diff/pull/138">#138</a>: (Internal) Refactor catalog class with proper inheritance</li>
17
+ </td>
18
+ </tr>
19
+ <tr valign=top>
11
20
  <td>1.3.0</td>
12
21
  <td>2017-06-09</td>
13
22
  <td>
@@ -4,12 +4,14 @@
4
4
 
5
5
  Catalog validation features include:
6
6
 
7
- - Validate references: Ensure resources targeted by `before`, `notify`, `require`, and/or `subscribe` exist in the catalog
7
+ - Validate references: Ensure resources targeted by `before`, `notify`, `require`, and/or `subscribe` exist in the catalog for Puppet 4 and below.
8
8
 
9
9
  ## Validate references
10
10
 
11
11
  `octocatalog-diff` includes the ability to validate references by ensuring resources targeted by `before`, `notify`, `require`, and/or `subscribe` parameters also exist in the catalog.
12
12
 
13
+ Puppet 5 already has this checking built in, so the `--validate-references` option described in this section will be ignored if Puppet 5 is being used. The same exception (`OctocatalogDiff::Errors::CatalogError`) is raised for a missing reference, whether the problem was detected by octocatalog-diff or Puppet 5.
14
+
13
15
  Consider the following Puppet code:
14
16
 
15
17
  ```
@@ -194,7 +194,7 @@ The following exceptions may occur during the compilation of a catalog within th
194
194
 
195
195
  - `OctocatalogDiff::Errors::CatalogError`
196
196
 
197
- Catalog failed to compile. Please note that whenever possible, a `OctocatalogDiff::API::V1::Catalog` object is still constructed for a failed catalog, with `#valid?` returning false.
197
+ Catalog failed to compile. Please note that whenever possible, a `OctocatalogDiff::API::V1::Catalog` object is still constructed for a failed catalog, with `#valid?` returning false. It's also possible that the catalog contained broken references -- see [Catalog validation](/doc/advanced-catalog-validation.md).
198
198
 
199
199
  - `OctocatalogDiff::Errors::GitCheckoutError`
200
200
 
@@ -203,7 +203,3 @@ The following exceptions may occur during the compilation of a catalog within th
203
203
  - `OctocatalogDiff::Errors::PuppetVersionError`
204
204
 
205
205
  The version of Puppet could not be determined, generally because the Puppet binary was not found, or does not respond as expected to `puppet version`.
206
-
207
- - `OctocatalogDiff::Errors::ReferenceValidationError`
208
-
209
- See [Catalog validation](/doc/advanced-catalog-validation.md).
@@ -100,7 +100,7 @@ The following exceptions may occur during the compilation of a catalog:
100
100
 
101
101
  - `OctocatalogDiff::Errors::CatalogError`
102
102
 
103
- Catalog failed to compile. Please note that whenever possible, a `OctocatalogDiff::API::V1::Catalog` object is still constructed for a failed catalog, with `#valid?` returning false.
103
+ Catalog failed to compile. Please note that whenever possible, a `OctocatalogDiff::API::V1::Catalog` object is still constructed for a failed catalog, with `#valid?` returning false. It's also possible that the catalog contained broken references -- see [Catalog validation](/doc/advanced-catalog-validation.md).
104
104
 
105
105
  - `OctocatalogDiff::Errors::GitCheckoutError`
106
106
 
@@ -109,7 +109,3 @@ The following exceptions may occur during the compilation of a catalog:
109
109
  - `OctocatalogDiff::Errors::PuppetVersionError`
110
110
 
111
111
  The version of Puppet could not be determined, generally because the Puppet binary was not found, or does not respond as expected to `puppet version`.
112
-
113
- - `OctocatalogDiff::Errors::ReferenceValidationError`
114
-
115
- See [Catalog validation](/doc/advanced-catalog-validation.md).
@@ -38,7 +38,7 @@ module OctocatalogDiff
38
38
  # @param options [Hash] Options for class; see above description
39
39
  def initialize(options = {}, logger = nil)
40
40
  @options = options.dup
41
- @tempdir = Dir.mktmpdir
41
+ @tempdir = Dir.mktmpdir('ocd-builddir-')
42
42
  at_exit { FileUtils.rm_rf(@tempdir) if File.directory?(@tempdir) }
43
43
 
44
44
  @factdir = nil
@@ -12,14 +12,42 @@ require_relative 'catalog-util/fileresources'
12
12
  require_relative 'errors'
13
13
 
14
14
  module OctocatalogDiff
15
- # This class represents a catalog. Generation of the catalog is handled via one of the
15
+ # Basic methods for interacting with a catalog. Generation of the catalog is handled via one of the
16
16
  # supported backends listed above as 'require_relative'. Usually, the 'computed' backend
17
17
  # will build the catalog from the Puppet command.
18
18
  class Catalog
19
- # Readable
20
- attr_reader :built, :catalog, :catalog_json
19
+ attr_accessor :node
20
+ attr_reader :built, :catalog, :catalog_json, :options
21
21
 
22
22
  # Constructor
23
+ def initialize(options = {})
24
+ unless options.is_a?(Hash)
25
+ raise ArgumentError, "#{self.class}.initialize requires hash argument, not #{options.class}"
26
+ end
27
+ @options = options
28
+
29
+ # Basic settings
30
+ @node = options[:node]
31
+ @error_message = nil
32
+ @catalog = nil
33
+ @catalog_json = nil
34
+ @retries = nil
35
+
36
+ # The compilation directory can be overridden, e.g. when testing
37
+ @override_compilation_dir = options[:compilation_dir]
38
+
39
+ # Keep track of whether references have been validated yet. Allow this to be fudged for when we do
40
+ # not desire reference validation to happen (e.g., for the "from" catalog that is otherwise valid).
41
+ @references_validated = options[:references_validated] || false
42
+
43
+ # Keep track of whether file resources have been converted.
44
+ @file_resources_converted = false
45
+
46
+ # Keep track of whether it's built yet
47
+ @built = false
48
+ end
49
+
50
+ # Guess the backend from the input and return the appropriate catalog object.
23
51
  # @param :backend [Symbol] If set, this will force a backend
24
52
  # @param :json [String] JSON catalog content (will avoid running Puppet to compile catalog)
25
53
  # @param :puppetdb [Object] If set, pull the catalog from PuppetDB rather than building
@@ -30,18 +58,25 @@ module OctocatalogDiff
30
58
  # @param :pass_env_vars [Array<String>] OPTIONAL: Additional environment vars to pass
31
59
  # @param :convert_file_resources [Boolean] OPTIONAL: Convert file resource source to content
32
60
  # @param :storeconfigs [Boolean] OPTIONAL: Pass the '-s' flag, for puppetdb (storeconfigs) integration
33
- def initialize(options = {})
34
- @options = options
35
-
36
- # Call appropriate backend for catalog generation
37
- @catalog_obj = backend(options)
61
+ # @return [OctocatalogDiff::Catalog::<?>] Catalog object from guessed backend
62
+ def self.create(options = {})
63
+ # Hard-coded backend
64
+ if options[:backend]
65
+ return OctocatalogDiff::Catalog::JSON.new(options) if options[:backend] == :json
66
+ return OctocatalogDiff::Catalog::PuppetDB.new(options) if options[:backend] == :puppetdb
67
+ return OctocatalogDiff::Catalog::PuppetMaster.new(options) if options[:backend] == :puppetmaster
68
+ return OctocatalogDiff::Catalog::Computed.new(options) if options[:backend] == :computed
69
+ return OctocatalogDiff::Catalog::Noop.new(options) if options[:backend] == :noop
70
+ raise ArgumentError, "Unknown backend :#{options[:backend]}"
71
+ end
38
72
 
39
- # The catalog is not built yet, except if the backend has no build method
40
- @built = false
41
- build unless @catalog_obj.respond_to?(:build)
73
+ # Determine backend based on arguments
74
+ return OctocatalogDiff::Catalog::JSON.new(options) if options[:json]
75
+ return OctocatalogDiff::Catalog::PuppetDB.new(options) if options[:puppetdb]
76
+ return OctocatalogDiff::Catalog::PuppetMaster.new(options) if options[:puppet_master]
42
77
 
43
- # The compilation directory can be overridden, e.g. when testing
44
- @override_compilation_dir = nil
78
+ # Default is to build catalog ourselves
79
+ OctocatalogDiff::Catalog::Computed.new(options)
45
80
  end
46
81
 
47
82
  # Build catalog - this method needs to be called to build the catalog. It is separate due to
@@ -49,43 +84,42 @@ module OctocatalogDiff
49
84
  # object so it cannot be part of any object that is passed around.
50
85
  # @param logger [Logger] Logger object, initialized to a default throwaway value
51
86
  def build(logger = Logger.new(StringIO.new))
52
- # Only build once
87
+ # If already built, don't build again
53
88
  return if @built
54
89
  @built = true
55
90
 
56
- # Call catalog's build method.
57
- if @catalog_obj.respond_to?(:build)
58
- logger.debug "Calling build for object #{@catalog_obj.class}"
59
- @catalog_obj.build(logger)
60
- end
61
-
62
- # These methods must exist in all backends
63
- @catalog = @catalog_obj.catalog
64
- @catalog_json = @catalog_obj.catalog_json
65
- @error_message = @catalog_obj.error_message
66
-
67
91
  # The resource hash is computed the first time it's needed. For now initialize it as nil.
68
92
  @resource_hash = nil
69
93
 
94
+ # Invoke the backend's build method, if there is one. There's a stub below in case there's not.
95
+ logger.debug "Calling build for object #{self.class}"
96
+ build_catalog(logger)
97
+
70
98
  # Perform post-generation processing of the catalog
71
99
  return unless valid?
72
- unless @catalog_obj.respond_to?(:convert_file_resources) && @catalog_obj.convert_file_resources == false
73
- if @options.fetch(:compare_file_text, false)
74
- OctocatalogDiff::CatalogUtil::FileResources.convert_file_resources(self, environment)
75
- end
76
- end
100
+
101
+ validate_references
102
+ return unless valid?
103
+
104
+ convert_file_resources if @options[:compare_file_text]
105
+
106
+ true
107
+ end
108
+
109
+ # Stub method if the backend does not contain a build method.
110
+ def build_catalog(_logger)
77
111
  end
78
112
 
79
113
  # Compilation environment
80
114
  # @return [String] Compilation environment (if set), else 'production' by default
81
115
  def environment
82
- @catalog_obj.respond_to?(:environment) ? @catalog_obj.environment : 'production'
116
+ @environment ||= 'production'
83
117
  end
84
118
 
85
119
  # For logging we may wish to know the backend being used
86
120
  # @return [String] Class of backend used
87
121
  def builder
88
- @catalog_obj.class.to_s
122
+ self.class.to_s
89
123
  end
90
124
 
91
125
  # Set the catalog JSON
@@ -98,8 +132,7 @@ module OctocatalogDiff
98
132
  # This retrieves the compilation directory from the catalog, or otherwise the passed-in directory.
99
133
  # @return [String] Compilation directory
100
134
  def compilation_dir
101
- return @override_compilation_dir if @override_compilation_dir
102
- @catalog_obj.respond_to?(:compilation_dir) ? @catalog_obj.compilation_dir : @options[:basedir]
135
+ @override_compilation_dir || @options[:basedir]
103
136
  end
104
137
 
105
138
  # The compilation directory can be overridden, e.g. during testing.
@@ -108,16 +141,16 @@ module OctocatalogDiff
108
141
  @override_compilation_dir = dir
109
142
  end
110
143
 
111
- # Determine whether the underlying catalog object supports :compare_file_text
112
- # @return [Boolean] Whether underlying catalog object supports :compare_file_text
113
- def convert_file_resources
114
- return true unless @catalog_obj.respond_to?(:convert_file_resources)
115
- @catalog_obj.convert_file_resources
144
+ # Stub method for "convert_file_resources" -- returns false because if the underlying class does
145
+ # not implement this method, it's not supported.
146
+ def convert_file_resources(_dry_run = false)
147
+ false
116
148
  end
117
149
 
118
150
  # Retrieve the error message.
119
151
  # @return [String] Error message (maximum 20,000 characters) - nil if no error.
120
152
  def error_message
153
+ build
121
154
  return nil if @error_message.nil? || !@error_message.is_a?(String)
122
155
  @error_message[0, 20_000]
123
156
  end
@@ -133,12 +166,11 @@ module OctocatalogDiff
133
166
  @resource_hash = nil
134
167
  end
135
168
 
136
- # This retrieves the version of Puppet used to compile a catalog. If the underlying catalog was not
137
- # compiled by running Puppet (e.g., it was read in from JSON or puppetdb), then this returns the
138
- # puppet version optionally passed in to the constructor. This can also be nil.
169
+ # Stub method to return the puppet version if the back end doesn't support this.
139
170
  # @return [String] Puppet version
140
171
  def puppet_version
141
- @catalog_obj.respond_to?(:puppet_version) ? @catalog_obj.puppet_version : @options[:puppet_version]
172
+ build
173
+ @options[:puppet_version]
142
174
  end
143
175
 
144
176
  # This allows retrieving a resource by type and title. This is intended for use when a O(1) lookup is required.
@@ -147,6 +179,7 @@ module OctocatalogDiff
147
179
  # @return [Hash] Resource item
148
180
  def resource(opts = {})
149
181
  raise ArgumentError, ':type and :title are required' unless opts[:type] && opts[:title]
182
+ build
150
183
  build_resource_hash if @resource_hash.nil?
151
184
  return nil unless @resource_hash[opts[:type]].is_a?(Hash)
152
185
  @resource_hash[opts[:type]][opts[:title]]
@@ -155,6 +188,7 @@ module OctocatalogDiff
155
188
  # This is a compatibility layer for the resources, which are in a different place in Puppet 3.x and Puppet 4.x
156
189
  # @return [Array] Resource array
157
190
  def resources
191
+ build
158
192
  raise OctocatalogDiff::Errors::CatalogError, 'Catalog does not appear to have been built' if !valid? && error_message.nil?
159
193
  raise OctocatalogDiff::Errors::CatalogError, error_message unless valid?
160
194
  return @catalog['data']['resources'] if @catalog['data'].is_a?(Hash) && @catalog['data']['resources'].is_a?(Array)
@@ -165,16 +199,17 @@ module OctocatalogDiff
165
199
  # :nocov:
166
200
  end
167
201
 
168
- # This retrieves the number of retries necessary to compile the catalog. If the underlying catalog
202
+ # Stub method of the the number of retries necessary to compile the catalog. If the underlying catalog
169
203
  # generation backend does not support retries, nil is returned.
170
204
  # @return [Integer] Retry count
171
205
  def retries
172
- @retries = @catalog_obj.respond_to?(:retries) ? @catalog_obj.retries : nil
206
+ nil
173
207
  end
174
208
 
175
209
  # Determine if the catalog build was successful.
176
210
  # @return [Boolean] Whether the catalog is valid
177
211
  def valid?
212
+ build
178
213
  !@catalog.nil?
179
214
  end
180
215
 
@@ -182,11 +217,19 @@ module OctocatalogDiff
182
217
  # Raise a OctocatalogDiff::Errors::ReferenceValidationError for any found to be missing.
183
218
  # Uses @options[:validate_references] to influence which references are checked.
184
219
  def validate_references
220
+ # If we've already done the validation, don't do it again
221
+ return if @references_validated
222
+ @references_validated = true
223
+
185
224
  # Skip out early if no reference validation has been requested.
186
225
  unless @options[:validate_references].is_a?(Array) && @options[:validate_references].any?
187
226
  return
188
227
  end
189
228
 
229
+ # Puppet 5 has reference validation built-in and enabled, so there won't even be a valid catalog if
230
+ # there were invalid references. It's pointless to perform validation of our own.
231
+ return if puppet_version && puppet_version >= '5.0.0'
232
+
190
233
  # Iterate over all the resources and check each one that has one of the attributes being checked.
191
234
  # Keep track of all references that are missing for ultimate inclusion in the error message.
192
235
  missing = []
@@ -204,7 +247,7 @@ module OctocatalogDiff
204
247
  # At this point there is at least one broken/missing reference. Format an error message and raise.
205
248
  errors = format_missing_references(missing)
206
249
  plural = errors =~ /;/ ? 's' : ''
207
- raise OctocatalogDiff::Errors::ReferenceValidationError, "Catalog has broken reference#{plural}: #{errors}"
250
+ self.error_message = "Catalog has broken reference#{plural}: #{errors}"
208
251
  end
209
252
 
210
253
  private
@@ -265,29 +308,6 @@ module OctocatalogDiff
265
308
  end
266
309
  end
267
310
 
268
- # Private method: Choose backend based on passed-in options
269
- # @param options [Hash] Options passed into constructor
270
- # @return [Object] OctocatalogDiff::Catalog::<whatever> object
271
- def backend(options)
272
- # Hard-coded backend
273
- if options[:backend]
274
- return OctocatalogDiff::Catalog::JSON.new(options) if options[:backend] == :json
275
- return OctocatalogDiff::Catalog::PuppetDB.new(options) if options[:backend] == :puppetdb
276
- return OctocatalogDiff::Catalog::PuppetMaster.new(options) if options[:backend] == :puppetmaster
277
- return OctocatalogDiff::Catalog::Computed.new(options) if options[:backend] == :computed
278
- return OctocatalogDiff::Catalog::Noop.new(options) if options[:backend] == :noop
279
- raise ArgumentError, "Unknown backend :#{options[:backend]}"
280
- end
281
-
282
- # Determine backend based on arguments
283
- return OctocatalogDiff::Catalog::JSON.new(options) if options[:json]
284
- return OctocatalogDiff::Catalog::PuppetDB.new(options) if options[:puppetdb]
285
- return OctocatalogDiff::Catalog::PuppetMaster.new(options) if options[:puppet_master]
286
-
287
- # Default is to build catalog ourselves
288
- OctocatalogDiff::Catalog::Computed.new(options)
289
- end
290
-
291
311
  # Private method: Build the resource hash to be used used for O(1) lookups by type and title.
292
312
  # This method is called the first time the resource hash is accessed.
293
313
  def build_resource_hash
@@ -4,6 +4,7 @@ require 'fileutils'
4
4
  require 'json'
5
5
  require 'stringio'
6
6
 
7
+ require_relative '../catalog'
7
8
  require_relative '../catalog-util/bootstrap'
8
9
  require_relative '../catalog-util/builddir'
9
10
  require_relative '../catalog-util/command'
@@ -15,9 +16,7 @@ module OctocatalogDiff
15
16
  class Catalog
16
17
  # Represents a Puppet catalog that is computed (via `puppet master --compile ...`)
17
18
  # By instantiating this class, the catalog is computed.
18
- class Computed
19
- attr_reader :node, :error_message, :catalog, :catalog_json, :retries
20
-
19
+ class Computed < OctocatalogDiff::Catalog
21
20
  # Constructor
22
21
  # @param :node [String] REQUIRED: Node name
23
22
  # @param :basedir [String] Directory in which to compile the catalog
@@ -28,14 +27,10 @@ module OctocatalogDiff
28
27
  # @param :puppet_version [String] Puppet version (optional; if not supplied, it is calculated)
29
28
  # @param :puppet_command [String] Full command to run Puppet (optional; if not supplied, it is calculated)
30
29
  def initialize(options)
31
- raise ArgumentError, 'Usage: OctocatalogDiff::Catalog::Computed.initialize(options_hash)' unless options.is_a?(Hash)
32
- raise ArgumentError, 'Node name must be passed to OctocatalogDiff::Catalog::Computed' unless options[:node].is_a?(String)
30
+ super
33
31
 
34
- # Standard readable variables
35
- @node = options[:node]
36
- @error_message = nil
37
- @catalog = nil
38
- @catalog_json = nil
32
+ raise ArgumentError, 'Node name must be passed to OctocatalogDiff::Catalog::Computed' unless options[:node].is_a?(String)
33
+ raise ArgumentError, 'Branch is undefined' unless options[:branch]
39
34
 
40
35
  # Additional class variables
41
36
  @pass_env_vars = options.fetch(:pass_env_vars, [])
@@ -44,31 +39,15 @@ module OctocatalogDiff
44
39
  @puppet_binary = options[:puppet_binary]
45
40
  @puppet_version = options[:puppet_version]
46
41
  @puppet_command = options[:puppet_command]
47
- @retries = nil
48
42
  @builddir = nil
49
43
  @facts_terminus = options.fetch(:facts_terminus, 'yaml')
50
-
51
- # Pass through the input for other access
52
- @opts = options
53
- raise ArgumentError, 'Branch is undefined' unless @opts[:branch]
54
- end
55
-
56
- # Actually build the catalog (populate @error_message, @catalog, @catalog_json)
57
- def build(logger = Logger.new(StringIO.new))
58
- if @facts_terminus != 'facter'
59
- facts_obj = OctocatalogDiff::CatalogUtil::Facts.new(@opts, logger)
60
- logger.debug "Start retrieving facts for #{@node} from #{self.class}"
61
- @opts[:facts] = facts_obj.facts
62
- logger.debug "Success retrieving facts for #{@node} from #{self.class}"
63
- end
64
- build_catalog(logger)
65
44
  end
66
45
 
67
46
  # Get the Puppet version
68
47
  # @return [String] Puppet version
69
48
  def puppet_version
70
49
  raise ArgumentError, '"puppet_binary" was not passed to OctocatalogDiff::Catalog::Computed' unless @puppet_binary
71
- @puppet_version ||= OctocatalogDiff::Util::PuppetVersion.puppet_version(@puppet_binary, @opts)
50
+ @puppet_version ||= OctocatalogDiff::Util::PuppetVersion.puppet_version(@puppet_binary, @options)
72
51
  end
73
52
 
74
53
  # Compilation directory
@@ -80,7 +59,14 @@ module OctocatalogDiff
80
59
 
81
60
  # Environment used to compile catalog
82
61
  def environment
83
- @opts.fetch(:environment, 'production')
62
+ @options.fetch(:environment, 'production')
63
+ end
64
+
65
+ # Convert file resources source => "puppet:///..." to content => "actual content of file".
66
+ def convert_file_resources(dry_run = false)
67
+ return @options.key?(:basedir) if dry_run
68
+ return false unless @options[:basedir]
69
+ OctocatalogDiff::CatalogUtil::FileResources.convert_file_resources(self, environment)
84
70
  end
85
71
 
86
72
  private
@@ -105,29 +91,29 @@ module OctocatalogDiff
105
91
  return if @builddir
106
92
 
107
93
  # Fill options for creating and populating the temporary directory
108
- tmphash = @opts.dup
94
+ tmphash = @options.dup
109
95
 
110
96
  # Bootstrap directory if needed
111
- if !@opts[:bootstrapped_dir].nil?
112
- raise Errno::ENOENT, "Invalid dir #{@opts[:bootstrapped_dir]}" unless File.directory?(@opts[:bootstrapped_dir])
113
- tmphash[:basedir] = @opts[:bootstrapped_dir]
114
- elsif @opts[:branch] == '.'
115
- if @opts[:bootstrap_current]
116
- tmphash[:basedir] = Dir.mktmpdir
97
+ if !@options[:bootstrapped_dir].nil?
98
+ raise Errno::ENOENT, "Invalid dir #{@options[:bootstrapped_dir]}" unless File.directory?(@options[:bootstrapped_dir])
99
+ tmphash[:basedir] = @options[:bootstrapped_dir]
100
+ elsif @options[:branch] == '.'
101
+ if @options[:bootstrap_current]
102
+ tmphash[:basedir] = Dir.mktmpdir('ocd-bootstrap-basedir-')
117
103
  at_exit { cleanup_checkout_dir(tmphash[:basedir], logger) }
118
104
 
119
- FileUtils.cp_r File.join(@opts[:basedir], '.'), tmphash[:basedir]
105
+ FileUtils.cp_r File.join(@options[:basedir], '.'), tmphash[:basedir]
120
106
 
121
- o = @opts.reject { |k, _v| k == :branch }.merge(path: tmphash[:basedir])
107
+ o = @options.reject { |k, _v| k == :branch }.merge(path: tmphash[:basedir])
122
108
  OctocatalogDiff::CatalogUtil::Bootstrap.bootstrap_directory(o, logger)
123
109
  else
124
- tmphash[:basedir] = @opts[:basedir]
110
+ tmphash[:basedir] = @options[:basedir]
125
111
  end
126
112
  else
127
- checkout_dir = Dir.mktmpdir
113
+ checkout_dir = Dir.mktmpdir('ocd-bootstrap-checkout-')
128
114
  at_exit { cleanup_checkout_dir(checkout_dir, logger) }
129
115
  tmphash[:basedir] = checkout_dir
130
- OctocatalogDiff::CatalogUtil::Bootstrap.bootstrap_directory(@opts.merge(path: checkout_dir), logger)
116
+ OctocatalogDiff::CatalogUtil::Bootstrap.bootstrap_directory(@options.merge(path: checkout_dir), logger)
131
117
  end
132
118
 
133
119
  # Create and populate the temporary directory
@@ -136,8 +122,14 @@ module OctocatalogDiff
136
122
 
137
123
  # Private method: Build catalog by running Puppet
138
124
  # @param logger [Logger] Logger object
139
- def build_catalog(logger = nil)
140
- return nil unless @catalog.nil? && @error_message.nil?
125
+ def build_catalog(logger)
126
+ if @facts_terminus != 'facter'
127
+ facts_obj = OctocatalogDiff::CatalogUtil::Facts.new(@options, logger)
128
+ logger.debug "Start retrieving facts for #{@node} from #{self.class}"
129
+ @options[:facts] = facts_obj.facts
130
+ logger.debug "Success retrieving facts for #{@node} from #{self.class}"
131
+ end
132
+
141
133
  bootstrap(logger)
142
134
  result = run_puppet(logger)
143
135
  @retries = result[:retries]
@@ -168,10 +160,10 @@ module OctocatalogDiff
168
160
  @puppet_command_obj ||= begin
169
161
  raise ArgumentError, '"puppet_binary" was not passed to OctocatalogDiff::Catalog::Computed' unless @puppet_binary
170
162
 
171
- command_opts = @opts.merge(
163
+ command_opts = @options.merge(
172
164
  node: @node,
173
165
  compilation_dir: @builddir.tempdir,
174
- parser: @opts.fetch(:parser, :default),
166
+ parser: @options.fetch(:parser, :default),
175
167
  puppet_binary: @puppet_binary,
176
168
  fact_file: @builddir.fact_file,
177
169
  dir: @builddir.tempdir,
@@ -201,7 +193,7 @@ module OctocatalogDiff
201
193
  # Set up the ScriptRunner
202
194
  scriptrunner = OctocatalogDiff::Util::ScriptRunner.new(
203
195
  default_script: 'puppet/puppet.sh',
204
- override_script_path: @opts[:override_script_path]
196
+ override_script_path: @options[:override_script_path]
205
197
  )
206
198
 
207
199
  begin
@@ -1,21 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../catalog'
4
+ require_relative '../catalog-util/fileresources'
5
+
3
6
  require 'json'
4
7
 
5
8
  module OctocatalogDiff
6
9
  class Catalog
7
10
  # Represents a Puppet catalog that is read in directly from a JSON file.
8
- class JSON
9
- attr_accessor :node
10
- attr_reader :error_message, :catalog, :catalog_json
11
-
11
+ class JSON < OctocatalogDiff::Catalog
12
12
  # Constructor
13
13
  # @param :json [String] REQUIRED: Content of catalog, will be parsed as JSON
14
14
  # @param :node [String] Node name (if not supplied, will be determined from catalog)
15
15
  def initialize(options)
16
- raise ArgumentError, 'Usage: OctocatalogDiff::Catalog::JSON.initialize(options_hash)' unless options.is_a?(Hash)
17
- raise ArgumentError, "Must supply :json as string in options: #{options[:json].class}" unless options[:json].is_a?(String)
18
- @catalog_json = options[:json]
16
+ super
17
+
18
+ unless options[:json].is_a?(String)
19
+ raise ArgumentError, "Must supply :json as string in options: #{options[:json].class}"
20
+ end
21
+
22
+ @catalog_json = options.fetch(:json)
19
23
  begin
20
24
  @catalog = ::JSON.parse(@catalog_json)
21
25
  @error_message = nil
@@ -27,6 +31,13 @@ module OctocatalogDiff
27
31
  @catalog_json = nil
28
32
  end
29
33
  end
34
+
35
+ # Convert file resources source => "puppet:///..." to content => "actual content of file".
36
+ def convert_file_resources(dry_run = false)
37
+ return @options.key?(:basedir) if dry_run
38
+ return false unless @options[:basedir]
39
+ OctocatalogDiff::CatalogUtil::FileResources.convert_file_resources(self, environment)
40
+ end
30
41
  end
31
42
  end
32
43
  end
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../catalog'
4
+
3
5
  require 'json'
4
6
 
5
7
  module OctocatalogDiff
6
8
  class Catalog
7
9
  # Represents a null Puppet catalog.
8
- class Noop
9
- attr_accessor :node
10
- attr_reader :error_message, :catalog, :catalog_json
11
-
12
- # Constructor
10
+ class Noop < OctocatalogDiff::Catalog
13
11
  def initialize(options)
12
+ super
13
+
14
14
  @catalog_json = '{"resources":[]}'
15
15
  @catalog = { 'resources' => [] }
16
16
  @error_message = nil
@@ -3,43 +3,30 @@
3
3
  require 'json'
4
4
  require 'stringio'
5
5
 
6
+ require_relative '../catalog'
6
7
  require_relative '../errors'
7
8
  require_relative '../puppetdb'
8
9
 
9
10
  module OctocatalogDiff
10
11
  class Catalog
11
12
  # Represents a Puppet catalog that is read from PuppetDB.
12
- class PuppetDB
13
- attr_accessor :node
14
- attr_reader :error_message, :catalog, :catalog_json, :retries, :convert_file_resources
15
-
13
+ class PuppetDB < OctocatalogDiff::Catalog
16
14
  # Constructor - See OctocatalogDiff::PuppetDB for additional parameters
17
15
  # @param :node [String] Node name
18
16
  # @param :retry [Integer] Number of retries, if fetch fails
19
17
  def initialize(options)
20
- raise ArgumentError, 'Hash of options must be passed to OctocatalogDiff::Catalog::PuppetDB' unless options.is_a?(Hash)
21
- raise ArgumentError, 'node must be a non-empty string' unless options[:node].is_a?(String) && options[:node] != ''
22
- @node = options[:node]
23
- @catalog = nil
24
- @error_message = nil
25
- @retries = nil
26
-
27
- # Cannot convert file resources from this type of catalog
28
- @convert_file_resources = false
18
+ super
29
19
 
30
- # Save options
31
- @options = options
32
- end
33
-
34
- def build(logger = Logger.new(StringIO.new))
35
- fetch_catalog(logger)
20
+ unless @options[:node].is_a?(String) && @options[:node] != ''
21
+ raise ArgumentError, 'node must be a non-empty string'
22
+ end
36
23
  end
37
24
 
38
25
  private
39
26
 
40
27
  # Private method: Get catalog from PuppetDB. Sets @catalog / @catalog_json or @error_message
41
28
  # @param logger [Logger object] Logger object
42
- def fetch_catalog(logger)
29
+ def build_catalog(logger)
43
30
  # Use OctocatalogDiff::PuppetDB to interact with puppetdb
44
31
  puppetdb_obj = OctocatalogDiff::PuppetDB.new(@options)
45
32
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../catalog'
3
4
  require_relative '../catalog-util/facts'
4
5
  require_relative '../external/pson/pure'
5
6
  require_relative '../util/httparty'
@@ -11,10 +12,7 @@ require 'stringio'
11
12
  module OctocatalogDiff
12
13
  class Catalog
13
14
  # Represents a Puppet catalog that is obtained by contacting the Puppet Master.
14
- class PuppetMaster
15
- attr_accessor :node
16
- attr_reader :error_message, :catalog, :catalog_json, :convert_file_resources, :options, :retries
17
-
15
+ class PuppetMaster < OctocatalogDiff::Catalog
18
16
  # Defaults
19
17
  DEFAULT_PUPPET_PORT_NUMBER = 8140
20
18
  DEFAULT_PUPPET_SERVER_API = 3
@@ -34,32 +32,29 @@ module OctocatalogDiff
34
32
  # @param :puppet_master_ssl_client_auth [Boolean] Override the client-auth that is guessed from parameters
35
33
  # @param :timeout [Integer] Connection timeout for Puppet master (default=PUPPET_MASTER_TIMEOUT seconds)
36
34
  def initialize(options)
37
- raise ArgumentError, 'Hash of options must be passed to OctocatalogDiff::Catalog::PuppetMaster' unless options.is_a?(Hash)
38
- raise ArgumentError, 'node must be a non-empty string' unless options[:node].is_a?(String) && options[:node] != ''
39
- unless options[:branch].is_a?(String) && options[:branch] != ''
35
+ super
36
+
37
+ unless @options[:node].is_a?(String) && @options[:node] != ''
38
+ raise ArgumentError, 'node must be a non-empty string'
39
+ end
40
+
41
+ unless @options[:branch].is_a?(String) && @options[:branch] != ''
40
42
  raise ArgumentError, 'Environment must be a non-empty string'
41
43
  end
42
- unless options[:puppet_master].is_a?(String) && options[:puppet_master] != ''
44
+
45
+ unless @options[:puppet_master].is_a?(String) && @options[:puppet_master] != ''
43
46
  raise ArgumentError, 'Puppet Master must be a non-empty string'
44
47
  end
45
48
 
46
- @node = options[:node]
47
- @catalog = nil
48
- @error_message = nil
49
- @retries = nil
50
49
  @timeout = options.fetch(:puppet_master_timeout, options.fetch(:timeout, PUPPET_MASTER_TIMEOUT))
51
50
  @retry_failed_catalog = options.fetch(:retry_failed_catalog, 0)
52
-
53
- # Cannot convert file resources from this type of catalog right now.
54
- # FIXME: This is possible with additional API calls but is current unimplemented.
55
- @convert_file_resources = false
56
-
57
- options[:puppet_master] += ":#{DEFAULT_PUPPET_PORT_NUMBER}" unless options[:puppet_master] =~ /\:\d+$/
58
- @options = options
51
+ @options[:puppet_master] += ":#{DEFAULT_PUPPET_PORT_NUMBER}" unless @options[:puppet_master] =~ /\:\d+$/
59
52
  end
60
53
 
54
+ private
55
+
61
56
  # Build method
62
- def build(logger = Logger.new(StringIO.new))
57
+ def build_catalog(logger = Logger.new(StringIO.new))
63
58
  facts_obj = OctocatalogDiff::CatalogUtil::Facts.new(@options, logger)
64
59
  logger.debug "Start retrieving facts for #{@node} from #{self.class}"
65
60
  @facts = facts_obj.facts
@@ -67,8 +62,6 @@ module OctocatalogDiff
67
62
  fetch_catalog(logger)
68
63
  end
69
64
 
70
- private
71
-
72
65
  # Returns a hash of parameters for each supported version of the Puppet Server Catalog API.
73
66
  # @return [Hash] Hash of parameters
74
67
  #
@@ -11,7 +11,6 @@ module OctocatalogDiff
11
11
  class BootstrapError < RuntimeError; end
12
12
  class CatalogError < RuntimeError; end
13
13
  class PuppetVersionError < RuntimeError; end
14
- class ReferenceValidationError < RuntimeError; end
15
14
  class GitCheckoutError < RuntimeError; end
16
15
 
17
16
  # Error classes for retrieving facts
@@ -63,15 +63,15 @@ module OctocatalogDiff
63
63
  # build the catalog.
64
64
  result = {}
65
65
  catalog_tasks.each do |x|
66
- result[x[0]] = OctocatalogDiff::Catalog.new(x[1].args)
66
+ result[x[0]] = OctocatalogDiff::Catalog.create(x[1].args)
67
67
  @logger.debug "Initialized #{result[x[0]].builder} for #{x[0]}-catalog"
68
68
  end
69
69
 
70
70
  # Disable --compare-file-text if either (or both) of the chosen backends do not support it
71
71
  if @options.fetch(:compare_file_text, false)
72
- result.each do |_key, val|
73
- next unless val.convert_file_resources == false
74
- @logger.debug "Disabling --compare-file-text; not supported by #{val.builder}"
72
+ result.each do |_key, builder_obj|
73
+ next if builder_obj.convert_file_resources(true)
74
+ @logger.debug "Disabling --compare-file-text; not supported by #{builder_obj.builder}"
75
75
  @options[:compare_file_text] = false
76
76
  catalog_tasks.map! do |x|
77
77
  x[1].args[:compare_file_text] = false
@@ -147,6 +147,7 @@ module OctocatalogDiff
147
147
  retry_failed_catalog: @options.fetch(:retry_failed_catalog, 0),
148
148
  parser: @options["parser_#{key}".to_sym]
149
149
  )
150
+ args[:basedir] ||= args[:bootstrapped_dir]
150
151
 
151
152
  # If any options are in the form of 'to_SOMETHING' or 'from_SOMETHING', this sets the option to
152
153
  # 'SOMETHING' for the catalog if it matches this key. For example, when compiling the 'to' catalog
@@ -157,6 +158,9 @@ module OctocatalogDiff
157
158
  args.delete(opt_key)
158
159
  end
159
160
 
161
+ # Skip reference validation in the from-catalog by saying we already performed it.
162
+ args[:references_validated] = (key == :from)
163
+
160
164
  # The task is a OctocatalogDiff::Util::Parallel::Task object that contains the method to execute,
161
165
  # validator method, text description, and arguments to provide when calling the method.
162
166
  task = OctocatalogDiff::Util::Parallel::Task.new(
@@ -250,10 +254,9 @@ module OctocatalogDiff
250
254
  # @param logger [Logger] Logger object (presently unused)
251
255
  # @param args [Hash] Additional arguments set specifically for validator
252
256
  # @return [Boolean] Return true if catalog is valid, false otherwise
253
- def catalog_validator(catalog = nil, _logger = @logger, args = {})
257
+ def catalog_validator(catalog = nil, _logger = @logger, _args = {})
254
258
  raise ArgumentError, "Expects a catalog, got #{catalog.class}" unless catalog.is_a?(OctocatalogDiff::Catalog)
255
259
  raise OctocatalogDiff::Errors::CatalogError, "Catalog failed: #{catalog.error_message}" unless catalog.valid?
256
- catalog.validate_references if args[:task] == :to # Raises exception for broken references
257
260
  true
258
261
  end
259
262
  end
@@ -107,7 +107,7 @@ module OctocatalogDiff
107
107
  # @return [Exception] First exception encountered by a child process; returns nil if no exceptions encountered.
108
108
  def self.run_tasks_parallel(result, task_array, logger)
109
109
  pidmap = {}
110
- ipc_tempdir = Dir.mktmpdir
110
+ ipc_tempdir = Dir.mktmpdir('ocd-ipc-')
111
111
 
112
112
  # Child process forking
113
113
  task_array.each_with_index do |task, index|
@@ -91,7 +91,7 @@ module OctocatalogDiff
91
91
  # @return [String] Path to tempfile containing script
92
92
  def temp_script(script)
93
93
  raise Errno::ENOENT, "Script '#{script}' not found" unless File.file?(script)
94
- temp_dir = Dir.mktmpdir
94
+ temp_dir = Dir.mktmpdir('ocd-scriptrunner-')
95
95
  at_exit do
96
96
  begin
97
97
  FileUtils.remove_entry_secure temp_dir
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: octocatalog-diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub, Inc.
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-06-09 00:00:00.000000000 Z
12
+ date: 2017-08-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: diffy
@@ -137,32 +137,18 @@ dependencies:
137
137
  - - '='
138
138
  - !ruby/object:Gem::Version
139
139
  version: 0.48.1
140
- - !ruby/object:Gem::Dependency
141
- name: puppetdb-terminus
142
- requirement: !ruby/object:Gem::Requirement
143
- requirements:
144
- - - '='
145
- - !ruby/object:Gem::Version
146
- version: 3.2.4
147
- type: :development
148
- prerelease: false
149
- version_requirements: !ruby/object:Gem::Requirement
150
- requirements:
151
- - - '='
152
- - !ruby/object:Gem::Version
153
- version: 3.2.4
154
140
  - !ruby/object:Gem::Dependency
155
141
  name: simplecov
156
142
  requirement: !ruby/object:Gem::Requirement
157
143
  requirements:
158
- - - ">="
144
+ - - "~>"
159
145
  - !ruby/object:Gem::Version
160
146
  version: 0.14.1
161
147
  type: :development
162
148
  prerelease: false
163
149
  version_requirements: !ruby/object:Gem::Requirement
164
150
  requirements:
165
- - - ">="
151
+ - - "~>"
166
152
  - !ruby/object:Gem::Version
167
153
  version: 0.14.1
168
154
  - !ruby/object:Gem::Dependency
@@ -193,6 +179,20 @@ dependencies:
193
179
  - - "~>"
194
180
  - !ruby/object:Gem::Version
195
181
  version: 4.10.0
182
+ - !ruby/object:Gem::Dependency
183
+ name: puppetdb-terminus
184
+ requirement: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - '='
187
+ - !ruby/object:Gem::Version
188
+ version: 3.2.4
189
+ type: :development
190
+ prerelease: false
191
+ version_requirements: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - '='
194
+ - !ruby/object:Gem::Version
195
+ version: 3.2.4
196
196
  description: |
197
197
  Octocatalog-Diff assists with Puppet development and testing by enabling the user to
198
198
  compile 2 Puppet catalogs and compare them. It is possible to compare different