1212require_relative 'errors'
1313
1414module 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
1616 # supported backends listed above as 'require_relative'. Usually, the 'computed' backend
1717 # will build the catalog from the Puppet command.
1818 class Catalog
19- # Readable
20- attr_reader :built , :catalog , :catalog_json
19+ attr_accessor :node
20+ attr_reader :built , :catalog , :catalog_json , :options
2121
2222 # 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
3323 def initialize ( options = { } )
24+ unless options . is_a? ( Hash )
25+ raise ArgumentError , "#{ self . class } .initialize requires hash argument, not #{ options . class } "
26+ end
3427 @options = options
3528
29+ # Basic settings
30+ @node = options [ :node ]
31+ @error_message = nil
32+ @catalog = nil
33+ @catalog_json = nil
34+
3635 # The compilation directory can be overridden, e.g. when testing
3736 @override_compilation_dir = options [ :compilation_dir ]
3837
3938 # Keep track of whether references have been validated yet. Allow this to be fudged for when we do
4039 # not desire reference validation to happen (e.g., for the "from" catalog that is otherwise valid).
4140 @references_validated = options [ :references_validated ] || false
4241
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
4544
46- # The catalog is not built yet, except if the backend has no build method
45+ # Keep track of whether it's built yet
4746 @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 )
4979 end
5080
5181 # Build catalog - this method needs to be called to build the catalog. It is separate due to
5282 # the serialization of the logger object -- the parallel gem cannot serialize/deserialize a logger
5383 # object so it cannot be part of any object that is passed around.
5484 # @param logger [Logger] Logger object, initialized to a default throwaway value
5585 def build ( logger = Logger . new ( StringIO . new ) )
56- # Only build once
86+ # If already built, don't build again
5787 return if @built
5888 @built = true
5989
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-
7190 # The resource hash is computed the first time it's needed. For now initialize it as nil.
7291 @resource_hash = nil
7392
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+
7496 # Perform post-generation processing of the catalog
7597 return unless valid?
7698
7799 validate_references
78100 return unless valid?
79101
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 )
85107 end
86108
87109 # Compilation environment
88110 # @return [String] Compilation environment (if set), else 'production' by default
89111 def environment
90- @catalog_obj . respond_to? ( : environment) ? @catalog_obj . environment : 'production'
112+ @environment ||= 'production'
91113 end
92114
93115 # For logging we may wish to know the backend being used
94116 # @return [String] Class of backend used
95117 def builder
96- @catalog_obj . class . to_s
118+ self . class . to_s
97119 end
98120
99121 # Set the catalog JSON
@@ -106,8 +128,7 @@ def catalog_json=(str)
106128 # This retrieves the compilation directory from the catalog, or otherwise the passed-in directory.
107129 # @return [String] Compilation directory
108130 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 ]
111132 end
112133
113134 # The compilation directory can be overridden, e.g. during testing.
@@ -116,16 +137,16 @@ def compilation_dir=(dir)
116137 @override_compilation_dir = dir
117138 end
118139
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 } "
124144 end
125145
126146 # Retrieve the error message.
127147 # @return [String] Error message (maximum 20,000 characters) - nil if no error.
128148 def error_message
149+ build
129150 return nil if @error_message . nil? || !@error_message . is_a? ( String )
130151 @error_message [ 0 , 20_000 ]
131152 end
@@ -141,12 +162,11 @@ def error_message=(error)
141162 @resource_hash = nil
142163 end
143164
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.
147166 # @return [String] Puppet version
148167 def puppet_version
149- @catalog_obj . respond_to? ( :puppet_version ) ? @catalog_obj . puppet_version : @options [ :puppet_version ]
168+ build
169+ @options [ :puppet_version ]
150170 end
151171
152172 # 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
155175 # @return [Hash] Resource item
156176 def resource ( opts = { } )
157177 raise ArgumentError , ':type and :title are required' unless opts [ :type ] && opts [ :title ]
178+ build
158179 build_resource_hash if @resource_hash . nil?
159180 return nil unless @resource_hash [ opts [ :type ] ] . is_a? ( Hash )
160181 @resource_hash [ opts [ :type ] ] [ opts [ :title ] ]
@@ -163,6 +184,7 @@ def resource(opts = {})
163184 # This is a compatibility layer for the resources, which are in a different place in Puppet 3.x and Puppet 4.x
164185 # @return [Array] Resource array
165186 def resources
187+ build
166188 raise OctocatalogDiff ::Errors ::CatalogError , 'Catalog does not appear to have been built' if !valid? && error_message . nil?
167189 raise OctocatalogDiff ::Errors ::CatalogError , error_message unless valid?
168190 return @catalog [ 'data' ] [ 'resources' ] if @catalog [ 'data' ] . is_a? ( Hash ) && @catalog [ 'data' ] [ 'resources' ] . is_a? ( Array )
@@ -173,16 +195,17 @@ def resources
173195 # :nocov:
174196 end
175197
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
177199 # generation backend does not support retries, nil is returned.
178200 # @return [Integer] Retry count
179201 def retries
180- @retries = @catalog_obj . respond_to? ( :retries ) ? @catalog_obj . retries : nil
202+ nil
181203 end
182204
183205 # Determine if the catalog build was successful.
184206 # @return [Boolean] Whether the catalog is valid
185207 def valid?
208+ build
186209 !@catalog . nil?
187210 end
188211
@@ -281,29 +304,6 @@ def resources_missing_from_catalog(resources_to_check)
281304 end
282305 end
283306
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-
307307 # Private method: Build the resource hash to be used used for O(1) lookups by type and title.
308308 # This method is called the first time the resource hash is accessed.
309309 def build_resource_hash
@@ -317,5 +317,16 @@ def build_resource_hash
317317 end
318318 end
319319 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
320331 end
321332end
0 commit comments