octocatalog-diff 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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