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
- # @param :backend [Symbol] If set, this will force a backend
24
- # @param :json [String] JSON catalog content (will avoid running Puppet to compile catalog)
25
- # @param :puppetdb [Object] If set, pull the catalog from PuppetDB rather than building
26
- # @param :node [String] Name of node whose catalog is being built
27
- # @param :fact_file [String] OPTIONAL: Path to fact file (if not provided, look up in PuppetDB)
28
- # @param :hiera_config [String] OPTIONAL: Path to hiera config file (munge temp. copy if not provided)
29
- # @param :basedir [String] OPTIONAL: Base directory for catalog (default base directory of this checkout)
30
- # @param :pass_env_vars [Array<String>] OPTIONAL: Additional environment vars to pass
31
- # @param :convert_file_resources [Boolean] OPTIONAL: Convert file resource source to content
32
- # @param :storeconfigs [Boolean] OPTIONAL: Pass the '-s' flag, for puppetdb (storeconfigs) integration
33
23
def initialize ( options = { } )
24
+ unless options . is_a? ( Hash )
25
+ raise ArgumentError , "#{ self . class } .initialize requires hash argument, not #{ options . class } "
26
+ end
34
27
@options = options
35
28
29
+ # Basic settings
30
+ @node = options [ :node ]
31
+ @error_message = nil
32
+ @catalog = nil
33
+ @catalog_json = nil
34
+
36
35
# The compilation directory can be overridden, e.g. when testing
37
36
@override_compilation_dir = options [ :compilation_dir ]
38
37
39
38
# Keep track of whether references have been validated yet. Allow this to be fudged for when we do
40
39
# not desire reference validation to happen (e.g., for the "from" catalog that is otherwise valid).
41
40
@references_validated = options [ :references_validated ] || false
42
41
43
- # Call appropriate backend for catalog generation
44
- @catalog_obj = backend ( options )
42
+ # Keep track of whether file resources have been converted.
43
+ @file_resources_converted = false
45
44
46
- # The catalog is not built yet, except if the backend has no build method
45
+ # Keep track of whether it's built yet
47
46
@built = false
48
- build unless @catalog_obj . respond_to? ( :build )
47
+ end
48
+
49
+ # Guess the backend from the input and return the appropriate catalog object.
50
+ # @param :backend [Symbol] If set, this will force a backend
51
+ # @param :json [String] JSON catalog content (will avoid running Puppet to compile catalog)
52
+ # @param :puppetdb [Object] If set, pull the catalog from PuppetDB rather than building
53
+ # @param :node [String] Name of node whose catalog is being built
54
+ # @param :fact_file [String] OPTIONAL: Path to fact file (if not provided, look up in PuppetDB)
55
+ # @param :hiera_config [String] OPTIONAL: Path to hiera config file (munge temp. copy if not provided)
56
+ # @param :basedir [String] OPTIONAL: Base directory for catalog (default base directory of this checkout)
57
+ # @param :pass_env_vars [Array<String>] OPTIONAL: Additional environment vars to pass
58
+ # @param :convert_file_resources [Boolean] OPTIONAL: Convert file resource source to content
59
+ # @param :storeconfigs [Boolean] OPTIONAL: Pass the '-s' flag, for puppetdb (storeconfigs) integration
60
+ # @return [OctocatalogDiff::Catalog::<?>] Catalog object from guessed backend
61
+ def self . create ( options = { } )
62
+ # Hard-coded backend
63
+ if options [ :backend ]
64
+ return OctocatalogDiff ::Catalog ::JSON . new ( options ) if options [ :backend ] == :json
65
+ return OctocatalogDiff ::Catalog ::PuppetDB . new ( options ) if options [ :backend ] == :puppetdb
66
+ return OctocatalogDiff ::Catalog ::PuppetMaster . new ( options ) if options [ :backend ] == :puppetmaster
67
+ return OctocatalogDiff ::Catalog ::Computed . new ( options ) if options [ :backend ] == :computed
68
+ return OctocatalogDiff ::Catalog ::Noop . new ( options ) if options [ :backend ] == :noop
69
+ raise ArgumentError , "Unknown backend :#{ options [ :backend ] } "
70
+ end
71
+
72
+ # Determine backend based on arguments
73
+ return OctocatalogDiff ::Catalog ::JSON . new ( options ) if options [ :json ]
74
+ return OctocatalogDiff ::Catalog ::PuppetDB . new ( options ) if options [ :puppetdb ]
75
+ return OctocatalogDiff ::Catalog ::PuppetMaster . new ( options ) if options [ :puppet_master ]
76
+
77
+ # Default is to build catalog ourselves
78
+ OctocatalogDiff ::Catalog ::Computed . new ( options )
49
79
end
50
80
51
81
# Build catalog - this method needs to be called to build the catalog. It is separate due to
52
82
# the serialization of the logger object -- the parallel gem cannot serialize/deserialize a logger
53
83
# object so it cannot be part of any object that is passed around.
54
84
# @param logger [Logger] Logger object, initialized to a default throwaway value
55
85
def build ( logger = Logger . new ( StringIO . new ) )
56
- # Only build once
86
+ # If already built, don't build again
57
87
return if @built
58
88
@built = true
59
89
60
- # Call catalog's build method.
61
- if @catalog_obj . respond_to? ( :build )
62
- logger . debug "Calling build for object #{ @catalog_obj . class } "
63
- @catalog_obj . build ( logger )
64
- end
65
-
66
- # These methods must exist in all backends
67
- @catalog = @catalog_obj . catalog
68
- @catalog_json = @catalog_obj . catalog_json
69
- @error_message = @catalog_obj . error_message
70
-
71
90
# The resource hash is computed the first time it's needed. For now initialize it as nil.
72
91
@resource_hash = nil
73
92
93
+ # Invoke the backend's build method, if there is one. There's a stub below in case there's not.
94
+ build_catalog ( logger )
95
+
74
96
# Perform post-generation processing of the catalog
75
97
return unless valid?
76
98
77
99
validate_references
78
100
return unless valid?
79
101
80
- unless @catalog_obj . respond_to? ( : convert_file_resources) && @catalog_obj . convert_file_resources == false
81
- if @options . fetch ( :compare_file_text , false )
82
- OctocatalogDiff :: CatalogUtil :: FileResources . convert_file_resources ( self , environment )
83
- end
84
- end
102
+ convert_file_resources ( logger )
103
+ end
104
+
105
+ # Stub method if the backend does not contain a build method.
106
+ def build_catalog ( _logger )
85
107
end
86
108
87
109
# Compilation environment
88
110
# @return [String] Compilation environment (if set), else 'production' by default
89
111
def environment
90
- @catalog_obj . respond_to? ( : environment) ? @catalog_obj . environment : 'production'
112
+ @environment ||= 'production'
91
113
end
92
114
93
115
# For logging we may wish to know the backend being used
94
116
# @return [String] Class of backend used
95
117
def builder
96
- @catalog_obj . class . to_s
118
+ self . class . to_s
97
119
end
98
120
99
121
# Set the catalog JSON
@@ -106,8 +128,7 @@ def catalog_json=(str)
106
128
# This retrieves the compilation directory from the catalog, or otherwise the passed-in directory.
107
129
# @return [String] Compilation directory
108
130
def compilation_dir
109
- return @override_compilation_dir if @override_compilation_dir
110
- @catalog_obj . respond_to? ( :compilation_dir ) ? @catalog_obj . compilation_dir : @options [ :basedir ]
131
+ @override_compilation_dir || @options [ :basedir ]
111
132
end
112
133
113
134
# The compilation directory can be overridden, e.g. during testing.
@@ -116,16 +137,16 @@ def compilation_dir=(dir)
116
137
@override_compilation_dir = dir
117
138
end
118
139
119
- # Determine whether the underlying catalog object supports :compare_file_text
120
- # @return [Boolean] Whether underlying catalog object supports :compare_file_text
121
- def convert_file_resources
122
- return true unless @catalog_obj . respond_to? ( :convert_file_resources )
123
- @catalog_obj . convert_file_resources
140
+ # Stub method for "convert_file_resources" to avoid errors if the backend doesn't support this.
141
+ # @return [Boolean] True
142
+ def convert_file_resources ( logger = Logger . new ( StringIO . new ) )
143
+ logger . debug "Disabling --compare-file-text; not supported by #{ self . class } "
124
144
end
125
145
126
146
# Retrieve the error message.
127
147
# @return [String] Error message (maximum 20,000 characters) - nil if no error.
128
148
def error_message
149
+ build
129
150
return nil if @error_message . nil? || !@error_message . is_a? ( String )
130
151
@error_message [ 0 , 20_000 ]
131
152
end
@@ -141,12 +162,11 @@ def error_message=(error)
141
162
@resource_hash = nil
142
163
end
143
164
144
- # This retrieves the version of Puppet used to compile a catalog. If the underlying catalog was not
145
- # compiled by running Puppet (e.g., it was read in from JSON or puppetdb), then this returns the
146
- # puppet version optionally passed in to the constructor. This can also be nil.
165
+ # Stub method to return the puppet version if the back end doesn't support this.
147
166
# @return [String] Puppet version
148
167
def puppet_version
149
- @catalog_obj . respond_to? ( :puppet_version ) ? @catalog_obj . puppet_version : @options [ :puppet_version ]
168
+ build
169
+ @options [ :puppet_version ]
150
170
end
151
171
152
172
# This allows retrieving a resource by type and title. This is intended for use when a O(1) lookup is required.
@@ -155,6 +175,7 @@ def puppet_version
155
175
# @return [Hash] Resource item
156
176
def resource ( opts = { } )
157
177
raise ArgumentError , ':type and :title are required' unless opts [ :type ] && opts [ :title ]
178
+ build
158
179
build_resource_hash if @resource_hash . nil?
159
180
return nil unless @resource_hash [ opts [ :type ] ] . is_a? ( Hash )
160
181
@resource_hash [ opts [ :type ] ] [ opts [ :title ] ]
@@ -163,6 +184,7 @@ def resource(opts = {})
163
184
# This is a compatibility layer for the resources, which are in a different place in Puppet 3.x and Puppet 4.x
164
185
# @return [Array] Resource array
165
186
def resources
187
+ build
166
188
raise OctocatalogDiff ::Errors ::CatalogError , 'Catalog does not appear to have been built' if !valid? && error_message . nil?
167
189
raise OctocatalogDiff ::Errors ::CatalogError , error_message unless valid?
168
190
return @catalog [ 'data' ] [ 'resources' ] if @catalog [ 'data' ] . is_a? ( Hash ) && @catalog [ 'data' ] [ 'resources' ] . is_a? ( Array )
@@ -173,16 +195,17 @@ def resources
173
195
# :nocov:
174
196
end
175
197
176
- # This retrieves the number of retries necessary to compile the catalog. If the underlying catalog
198
+ # Stub method of the the number of retries necessary to compile the catalog. If the underlying catalog
177
199
# generation backend does not support retries, nil is returned.
178
200
# @return [Integer] Retry count
179
201
def retries
180
- @retries = @catalog_obj . respond_to? ( :retries ) ? @catalog_obj . retries : nil
202
+ nil
181
203
end
182
204
183
205
# Determine if the catalog build was successful.
184
206
# @return [Boolean] Whether the catalog is valid
185
207
def valid?
208
+ build
186
209
!@catalog . nil?
187
210
end
188
211
@@ -281,29 +304,6 @@ def resources_missing_from_catalog(resources_to_check)
281
304
end
282
305
end
283
306
284
- # Private method: Choose backend based on passed-in options
285
- # @param options [Hash] Options passed into constructor
286
- # @return [Object] OctocatalogDiff::Catalog::<whatever> object
287
- def backend ( options )
288
- # Hard-coded backend
289
- if options [ :backend ]
290
- return OctocatalogDiff ::Catalog ::JSON . new ( options ) if options [ :backend ] == :json
291
- return OctocatalogDiff ::Catalog ::PuppetDB . new ( options ) if options [ :backend ] == :puppetdb
292
- return OctocatalogDiff ::Catalog ::PuppetMaster . new ( options ) if options [ :backend ] == :puppetmaster
293
- return OctocatalogDiff ::Catalog ::Computed . new ( options ) if options [ :backend ] == :computed
294
- return OctocatalogDiff ::Catalog ::Noop . new ( options ) if options [ :backend ] == :noop
295
- raise ArgumentError , "Unknown backend :#{ options [ :backend ] } "
296
- end
297
-
298
- # Determine backend based on arguments
299
- return OctocatalogDiff ::Catalog ::JSON . new ( options ) if options [ :json ]
300
- return OctocatalogDiff ::Catalog ::PuppetDB . new ( options ) if options [ :puppetdb ]
301
- return OctocatalogDiff ::Catalog ::PuppetMaster . new ( options ) if options [ :puppet_master ]
302
-
303
- # Default is to build catalog ourselves
304
- OctocatalogDiff ::Catalog ::Computed . new ( options )
305
- end
306
-
307
307
# Private method: Build the resource hash to be used used for O(1) lookups by type and title.
308
308
# This method is called the first time the resource hash is accessed.
309
309
def build_resource_hash
@@ -317,5 +317,16 @@ def build_resource_hash
317
317
end
318
318
end
319
319
end
320
+
321
+ # Private method: A common way of running convert_file_resources for backends that allow it.
322
+ def convert_file_resources_real ( logger = Logger . new ( StringIO . new ) )
323
+ return false unless @options [ :compare_file_text ]
324
+ if @options [ :basedir ]
325
+ OctocatalogDiff ::CatalogUtil ::FileResources . convert_file_resources ( self , environment )
326
+ else
327
+ logger . debug "Disabling --compare-file-text; not supported by #{ self . class } without basedir"
328
+ false
329
+ end
330
+ end
320
331
end
321
332
end
0 commit comments