Class: OctocatalogDiff::Catalog

Inherits:
Object
  • Object
show all
Defined in:
lib/octocatalog-diff/catalog.rb,
lib/octocatalog-diff/catalog/json.rb,
lib/octocatalog-diff/catalog/noop.rb,
lib/octocatalog-diff/catalog/computed.rb,
lib/octocatalog-diff/catalog/puppetdb.rb,
lib/octocatalog-diff/catalog/puppetmaster.rb

Overview

This class represents a catalog. Generation of the catalog is handled via one of the supported backends listed above as ‘require_relative’. Usually, the ‘computed’ backend will build the catalog from the Puppet command.

Defined Under Namespace

Classes: Computed, JSON, Noop, PuppetDB, PuppetMaster

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Catalog

Constructor

Parameters:

  • :backend (Symbol)

    If set, this will force a backend

  • :json (String)

    JSON catalog content (will avoid running Puppet to compile catalog)

  • :puppetdb (Object)

    If set, pull the catalog from PuppetDB rather than building

  • :node (String)

    Name of node whose catalog is being built

  • :fact_file (String)

    OPTIONAL: Path to fact file (if not provided, look up in PuppetDB)

  • :hiera_config (String)

    OPTIONAL: Path to hiera config file (munge temp. copy if not provided)

  • :basedir (String)

    OPTIONAL: Base directory for catalog (default base directory of this checkout)

  • :pass_env_vars (Array<String>)

    OPTIONAL: Additional environment vars to pass

  • :convert_file_resources (Boolean)

    OPTIONAL: Convert file resource source to content

  • :storeconfigs (Boolean)

    OPTIONAL: Pass the ‘-s’ flag, for puppetdb (storeconfigs) integration



33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/octocatalog-diff/catalog.rb', line 33

def initialize(options = {})
  @options = options

  # Call appropriate backend for catalog generation
  @catalog_obj = backend(options)

  # The catalog is not built yet, except if the backend has no build method
  @built = false
  build unless @catalog_obj.respond_to?(:build)

  # The compilation directory can be overridden, e.g. when testing
  @override_compilation_dir = nil
end

Instance Attribute Details

#builtObject (readonly)

Readable



20
21
22
# File 'lib/octocatalog-diff/catalog.rb', line 20

def built
  @built
end

#catalogObject (readonly)

Readable



20
21
22
# File 'lib/octocatalog-diff/catalog.rb', line 20

def catalog
  @catalog
end

#catalog_jsonObject

Readable



20
21
22
# File 'lib/octocatalog-diff/catalog.rb', line 20

def catalog_json
  @catalog_json
end

Instance Method Details

#build(logger = Logger.new(StringIO.new)) ⇒ Object

Build catalog - this method needs to be called to build the catalog. It is separate due to the serialization of the logger object – the parallel gem cannot serialize/deserialize a logger object so it cannot be part of any object that is passed around.

Parameters:

  • logger (Logger) (defaults to: Logger.new(StringIO.new))

    Logger object, initialized to a default throwaway value



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/octocatalog-diff/catalog.rb', line 51

def build(logger = Logger.new(StringIO.new))
  # Only build once
  return if @built
  @built = true

  # Call catalog's build method.
  if @catalog_obj.respond_to?(:build)
    logger.debug "Calling build for object #{@catalog_obj.class}"
    @catalog_obj.build(logger)
  end

  # These methods must exist in all backends
  @catalog = @catalog_obj.catalog
  @catalog_json = @catalog_obj.catalog_json
  @error_message = @catalog_obj.error_message

  # The resource hash is computed the first time it's needed. For now initialize it as nil.
  @resource_hash = nil

  # Perform post-generation processing of the catalog
  return unless valid?
  unless @catalog_obj.respond_to?(:convert_file_resources) && @catalog_obj.convert_file_resources == false
    if @options.fetch(:compare_file_text, false)
      OctocatalogDiff::CatalogUtil::FileResources.convert_file_resources(self, environment)
    end
  end
end

#builderString

For logging we may wish to know the backend being used

Returns:

  • (String)

    Class of backend used



87
88
89
# File 'lib/octocatalog-diff/catalog.rb', line 87

def builder
  @catalog_obj.class.to_s
end

#compilation_dirString

This retrieves the compilation directory from the catalog, or otherwise the passed-in directory.

Returns:

  • (String)

    Compilation directory



100
101
102
103
# File 'lib/octocatalog-diff/catalog.rb', line 100

def compilation_dir
  return @override_compilation_dir if @override_compilation_dir
  @catalog_obj.respond_to?(:compilation_dir) ? @catalog_obj.compilation_dir : @options[:basedir]
end

#compilation_dir=(dir) ⇒ Object

The compilation directory can be overridden, e.g. during testing.

Parameters:

  • dir (String)

    Compilation directory



107
108
109
# File 'lib/octocatalog-diff/catalog.rb', line 107

def compilation_dir=(dir)
  @override_compilation_dir = dir
end

#convert_file_resourcesBoolean

Determine whether the underlying catalog object supports :compare_file_text

Returns:

  • (Boolean)

    Whether underlying catalog object supports :compare_file_text



113
114
115
116
# File 'lib/octocatalog-diff/catalog.rb', line 113

def convert_file_resources
  return true unless @catalog_obj.respond_to?(:convert_file_resources)
  @catalog_obj.convert_file_resources
end

#environmentString

Compilation environment

Returns:

  • (String)

    Compilation environment (if set), else ‘production’ by default



81
82
83
# File 'lib/octocatalog-diff/catalog.rb', line 81

def environment
  @catalog_obj.respond_to?(:environment) ? @catalog_obj.environment : 'production'
end

#error_messageString

Retrieve the error message.

Returns:

  • (String)

    Error message (maximum 20,000 characters) - nil if no error.



120
121
122
123
# File 'lib/octocatalog-diff/catalog.rb', line 120

def error_message
  return nil if @error_message.nil? || !@error_message.is_a?(String)
  @error_message[0, 20_000]
end

#error_message=(error) ⇒ Object

Allow setting the error message. If the error message is set to a string, the catalog and catalog JSON are set to nil.

Parameters:

  • error (String)

    Error message

Raises:

  • (ArgumentError)


128
129
130
131
132
133
134
# File 'lib/octocatalog-diff/catalog.rb', line 128

def error_message=(error)
  raise ArgumentError, 'Error message must be a string' unless error.is_a?(String)
  @error_message = error
  @catalog = nil
  @catalog_json = nil
  @resource_hash = nil
end

#puppet_versionString

This retrieves the version of Puppet used to compile a catalog. If the underlying catalog was not compiled by running Puppet (e.g., it was read in from JSON or puppetdb), then this returns the puppet version optionally passed in to the constructor. This can also be nil.

Returns:



140
141
142
# File 'lib/octocatalog-diff/catalog.rb', line 140

def puppet_version
  @catalog_obj.respond_to?(:puppet_version) ? @catalog_obj.puppet_version : @options[:puppet_version]
end

#resource(opts = {}) ⇒ Hash

This allows retrieving a resource by type and title. This is intended for use when a O(1) lookup is required.

Parameters:

  • :type (String)

    Type of resource

  • :title (String)

    Title of resource

Returns:

  • (Hash)

    Resource item

Raises:

  • (ArgumentError)


148
149
150
151
152
153
# File 'lib/octocatalog-diff/catalog.rb', line 148

def resource(opts = {})
  raise ArgumentError, ':type and :title are required' unless opts[:type] && opts[:title]
  build_resource_hash if @resource_hash.nil?
  return nil unless @resource_hash[opts[:type]].is_a?(Hash)
  @resource_hash[opts[:type]][opts[:title]]
end

#resourcesArray

This is a compatibility layer for the resources, which are in a different place in Puppet 3.x and Puppet 4.x

Returns:

  • (Array)

    Resource array

Raises:



157
158
159
160
161
162
163
164
165
166
# File 'lib/octocatalog-diff/catalog.rb', line 157

def resources
  raise OctocatalogDiff::Errors::CatalogError, 'Catalog does not appear to have been built' if !valid? && error_message.nil?
  raise OctocatalogDiff::Errors::CatalogError, error_message unless valid?
  return @catalog['data']['resources'] if @catalog['data'].is_a?(Hash) && @catalog['data']['resources'].is_a?(Array)
  return @catalog['resources'] if @catalog['resources'].is_a?(Array)
  # This is a bug condition
  # :nocov:
  raise "BUG: catalog has no data::resources or ::resources array. Please report this. #{@catalog.inspect}"
  # :nocov:
end

#retriesInteger

This retrieves the number of retries necessary to compile the catalog. If the underlying catalog generation backend does not support retries, nil is returned.

Returns:

  • (Integer)

    Retry count



171
172
173
# File 'lib/octocatalog-diff/catalog.rb', line 171

def retries
  @retries = @catalog_obj.respond_to?(:retries) ? @catalog_obj.retries : nil
end

#valid?Boolean

Determine if the catalog build was successful.

Returns:

  • (Boolean)

    Whether the catalog is valid



177
178
179
# File 'lib/octocatalog-diff/catalog.rb', line 177

def valid?
  !@catalog.nil?
end

#validate_referencesObject

Determine if all of the (before, notify, require, subscribe) targets are actually in the catalog. Raise a OctocatalogDiff::Errors::ReferenceValidationError for any found to be missing. Uses @options to influence which references are checked.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/octocatalog-diff/catalog.rb', line 184

def validate_references
  # Skip out early if no reference validation has been requested.
  unless @options[:validate_references].is_a?(Array) && @options[:validate_references].any?
    return
  end

  # Iterate over all the resources and check each one that has one of the attributes being checked.
  # Keep track of all references that are missing for ultimate inclusion in the error message.
  missing = []
  resources.each do |x|
    @options[:validate_references].each do |r|
      next unless x.key?('parameters')
      next unless x['parameters'].key?(r)
      missing_resources = resources_missing_from_catalog(x['parameters'][r])
      next unless missing_resources.any?
      missing.concat missing_resources.map { |missing_target| { source: x, target_type: r, target_value: missing_target } }
    end
  end
  return if missing.empty?

  # At this point there is at least one broken/missing reference. Format an error message and raise.
  errors = format_missing_references(missing)
  plural = errors =~ /;/ ? 's' : ''
  raise OctocatalogDiff::Errors::ReferenceValidationError, "Catalog has broken reference#{plural}: #{errors}"
end