class SparkleFormation

Unicorns and rainbows

Unicorns and rainbows

Formation container

Unicorns and rainbows

Constants

ALLOWED_GENERATION_PARAMETERS

Attributes allowed for generation parameter definitions

DEFAULT_STACK_RESOURCE

@return [String] default stack resource name

IGNORE_DIRECTORIES

@return [Array<String>] directory names to ignore

PROVIDER_MAPPINGS

@return [Hash] name mappings for providers used in lookups (resources/sparkles)

SPARKLEFORMATION_ROOT_LIBRARY

@return [String] path to root of this library

SparklePack

Independent collection of SparkleFormation items

VALID_GENERATION_PARAMETER_TYPES

Allowed data types for parameters

VALID_STACK_RESOURCES

@return [Array<String>] collection of valid stack resource types

VERSION

Current library version

Attributes

audit_log[RW]

@return [AuditLog] records of template composition

blacklisted_templates[R]

@return [Array<String>] black listed templates

compile_state[RW]

@return [Hash] state hash for compile time parameters

components_directory[R]

@return [String] components path

composition[R]

@return [Composition]

dynamics_directory[R]

@return [String] dynamics path

name[RW]

@return [Symbol] name of formation

parameters[R]

@return [Hash] parameters for stack generation

parent[R]

@return [SparkleFormation] parent stack

provider[R]

@return [Symbol] target provider

provider_resources[R]

@return [Class] Provider resources

registry_directory[R]

@return [String] registry path

sparkle[R]

@return [Sparkle] parts store

sparkle_path[R]

@return [String] base path

stack_resource_types[R]

@return [Array<String>] valid stack resource types

template_path[RW]

@return [String] local path to template

Public Class Methods

build(base = nil, &block) click to toggle source

Execute given block within struct context

@param base [SparkleStruct] context for block @yield block to execute @return [SparkleStruct] provided base or new struct

# File lib/sparkle_formation/sparkle_formation.rb, line 125
def build(base = nil, &block)
  if base || block.nil?
    struct = base || SparkleStruct.new
    struct.instance_exec(&block)
    struct
  else
    block
  end
end
builtin_insert(dynamic_name, struct, *args, &block) click to toggle source

Insert a builtin dynamic into a context

@param dynamic_name [String, Symbol] dynamic name @param struct [SparkleStruct] context for insertion @param args [Object] parameters for dynamic @return [SparkleStruct]

# File lib/sparkle_formation/sparkle_formation.rb, line 377
def builtin_insert(dynamic_name, struct, *args, &block)
  if struct._self.provider_resources && lookup_key = struct._self.provider_resources.registry_key(dynamic_name)
    _name, _config = *args
    _config ||= {}
    __t_hashish(_config)
    unless _name.is_a?(SparkleFormation::FunctionStruct)
      __t_stringish(_name)
      resource_name = [
        _name,
        _config.fetch(:resource_name_suffix, dynamic_name),
      ].compact.join("_").to_sym
    else
      resource_name = _name._root
    end
    _config.delete(:resource_name_suffix)
    new_resource = struct.resources.set!(resource_name)
    new_resource.type lookup_key
    properties = new_resource.properties
    config_keys = _config.keys.zip(_config.keys.map { |k| snake(k).to_s.tr("_", "") })
    struct._self.provider_resources.resource(dynamic_name, :properties).each do |prop_name|
      key = (config_keys.detect { |k| k.last == snake(prop_name).to_s.tr("_", "") } || []).first
      value = _config[key] if key
      if value
        if value.is_a?(Proc)
          properties.set!(prop_name, &value)
        else
          properties.set!(prop_name, value)
        end
      end
    end
    new_resource.instance_exec(&block) if block
    struct._self.provider_resources.resource_customizer(new_resource, lookup_key)
    new_resource
  end
end
compile(path, *args) click to toggle source

Compile file

@param path [String] path to file @param args [Object] use :sparkle to return struct. provide Hash

to pass through when compiling ({:state => {}})

@return [Hashish, SparkleStruct]

# File lib/sparkle_formation/sparkle_formation.rb, line 103
def compile(path, *args)
  opts = args.detect { |i| i.is_a?(Hash) } || {}
  unless path.is_a?(String) && File.file?(path.to_s)
    if spath = (opts.delete(:sparkle_path) || SparkleFormation.custom_paths[:sparkle_path])
      container = Sparkle.new(:root => spath)
      path = container.get(:template, path)[:path]
    end
  end
  formation = instance_eval(IO.read(path), path, 1)
  formation.template_path = path
  if args.delete(:sparkle)
    formation
  else
    formation.compile(opts)._dump
  end
end
components_path(path = nil)
Alias for: components_path=
components_path=(path = nil) click to toggle source

Get/set path to component files

@param path [String] path to component files @return [String] path to component files

# File lib/sparkle_formation/sparkle_formation.rb, line 62
def components_path=(path = nil)
  if path
    custom_paths[:components_directory] = path
  end
  custom_paths[:components_directory]
end
Also aliased as: components_path
custom_paths() click to toggle source

@return [Hashish] custom paths

# File lib/sparkle_formation/sparkle_formation.rb, line 37
def custom_paths
  @_paths ||= SparkleStruct.hashish.new
  @_paths
end
dynamic(name, args = {}, &block) click to toggle source

Define and register new dynamic

@param name [String, Symbol] name of dynamic @param args [Hash] dynamic metadata @option args [Hash] :parameters description of _config parameters @example

metadata describes dynamic parameters for _config hash:
:item_name => {:description => 'Defines item name', :type => 'String'}

@yield dynamic block @return [TrueClass]

# File lib/sparkle_formation/sparkle_formation.rb, line 182
def dynamic(name, args = {}, &block)
  @dynamics ||= SparkleStruct.hashish.new
  dynamics[name] = SparkleStruct.hashish[
    :block, block, :args, SparkleStruct.hashish[args.map(&:to_a)]
  ]
  true
end
dynamic_info(name) click to toggle source

Metadata for dynamic

@param name [String, Symbol] dynamic name @return [Hashish] metadata information

# File lib/sparkle_formation/sparkle_formation.rb, line 194
def dynamic_info(name)
  if dynamics[name]
    dynamics[name][:args] ||= SparkleStruct.hashish.new
  else
    raise KeyError.new("No dynamic registered with provided name (#{name})")
  end
end
Also aliased as: dynamic_information
dynamic_information(name)
Alias for: dynamic_info
dynamics() click to toggle source

@return [Hashish] loaded dynamics

# File lib/sparkle_formation/sparkle_formation.rb, line 32
def dynamics
  @dynamics ||= SparkleStruct.hashish.new
end
dynamics_path(path = nil)
Alias for: dynamics_path=
dynamics_path=(path = nil) click to toggle source

Get/set path to dynamic files

@param path [String] path to dynamic files @return [String] path to dynamic files

# File lib/sparkle_formation/sparkle_formation.rb, line 75
def dynamics_path=(path = nil)
  if path
    custom_paths[:dynamics_directory] = path
  end
  custom_paths[:dynamics_directory]
end
Also aliased as: dynamics_path
extract_caller(cinfo) click to toggle source

Extract the origin of the caller outside this library

@param cinfo [Array<String>] caller info @return [Array<String, Integer>] caller path and line number

# File lib/sparkle_formation/sparkle_formation.rb, line 208
def extract_caller(cinfo)
  res = cinfo.detect { |c|
    !c.include?(SPARKLEFORMATION_ROOT_LIBRARY)
  }.to_s.split(":")[0, 2]
  if res.size != 2
    line = res.last
    if line.to_i.to_s != line
      res << 0
    end
    if res != 2
      res.unshift(:unknown)
    end
  end
  res
end
from_hash(hash) click to toggle source

Convert hash to SparkleStruct instance

@param hash [Hashish] @return [SparkleStruct] @note will do best effort on camel key auto discovery

# File lib/sparkle_formation/sparkle_formation.rb, line 418
def from_hash(hash)
  struct = SparkleStruct.new
  struct._camel_keys_set(:auto_discovery)
  struct._load(hash)
  struct._camel_keys_set(nil)
  struct
end
insert(dynamic_name, struct, *args, &block) click to toggle source

Insert a dynamic into a context

@param dynamic_name [String, Symbol] dynamic name @param struct [SparkleStruct] context for insertion @param args [Object] parameters for dynamic @return [SparkleStruct]

# File lib/sparkle_formation/sparkle_formation.rb, line 251
def insert(dynamic_name, struct, *args, &block)
  __t_stringish(dynamic_name)
  result = false
  begin
    opts = args.detect { |i| i.is_a?(Hash) } || {}
    dyn = struct._self.sparkle.get(:dynamic, dynamic_name, opts[:provider])
    opts = nil
    raise dyn if dyn.is_a?(Exception)
    result = nil
    dyn.monochrome.each do |dynamic_item|
      record = struct._self.audit_log.push(
        type: :dynamic,
        name: dynamic_name,
        caller: extract_caller(caller),
        location: dynamic_item[:block].source_location,
      )
      struct._self.wrapped_audit(record) do
        if result
          opts = args.detect { |i| i.is_a?(Hash) }
          if opts
            opts[:previous_layer_result] = result
          else
            args.push(:previous_layer_result => result)
          end
        end
        result = struct.instance_exec(*args, &dynamic_item[:block])
      end
    end
    if block_given?
      result.instance_exec(&block)
    end
  rescue Error::NotFound::Dynamic
    record = struct._self.audit_log.push(
      type: :dynamic,
      name: dynamic_name,
      location: [:builtin, 0],
      caller: extract_caller(caller),
    )
    result = struct._self.wrapped_audit(record) do
      builtin_insert(dynamic_name, struct, *args, &block)
    end
    unless result
      message = "Failed to locate requested dynamic block for insertion: #{dynamic_name} " \
      "(valid: #{struct._self.sparkle.dynamics.fetch(struct._self.sparkle.provider, {}).keys.sort.join(", ")})"
      if struct._self.provider_resources && struct._self.provider_resources.registry.keys.size > 1
        t_name = struct._self.provider_resources.registry.keys.first
        valid_t_name = Bogo::Utility.snake(
          t_name.split(
            struct._self.provider_resources.resource_type_splitter
          ).join("_")
        )
        message << "\nBuiltin dynamics pattern `#{t_name}` -> `:#{Bogo::Utility.snake(valid_t_name)}`"
      end
      raise message
    end
  end
  result
end
load_component(path) click to toggle source

Load component

@param path [String] path to component @return [SparkleStruct] resulting struct

# File lib/sparkle_formation/sparkle_formation.rb, line 139
def load_component(path)
  instance_eval(IO.read(path), path, 1)
  @_struct
end
load_dynamics!(directory) click to toggle source

Load all dynamics within a directory

@param directory [String] @return [TrueClass]

# File lib/sparkle_formation/sparkle_formation.rb, line 148
def load_dynamics!(directory)
  @loaded_dynamics ||= []
  Dir.glob(File.join(directory, "*.rb")).each do |dyn|
    dyn = File.expand_path(dyn)
    next if @loaded_dynamics.include?(dyn)
    instance_eval(IO.read(dyn), dyn, 1)
    @loaded_dynamics << dyn
  end
  @loaded_dynamics.uniq!
  true
end
load_registry!(directory) click to toggle source

Load all registry entries within a directory

@param directory [String] @return [TrueClass]

# File lib/sparkle_formation/sparkle_formation.rb, line 164
def load_registry!(directory)
  Dir.glob(File.join(directory, "*.rb")).each do |reg|
    reg = File.expand_path(reg)
    require reg
  end
  true
end
nest(template, struct, *args, &block) click to toggle source

Nest a template into a context

@param template [String, Symbol] template to nest @param struct [SparkleStruct] context for nesting @param args [String, Symbol] stringified and underscore joined for name @return [SparkleStruct] @note if symbol is provided for template, double underscores

will be used for directory separator and dashes will match underscores
# File lib/sparkle_formation/sparkle_formation.rb, line 318
def nest(template, struct, *args, &block)
  options = args.detect { |i| i.is_a?(Hash) }
  if options
    args.delete(options)
  else
    options = {}
  end
  [template, *args].compact.each do |item|
    __t_stringish(item)
  end
  to_nest = struct._self.sparkle.get(:template, template, options[:provider])
  resource_name = template.to_s.gsub("__", "_")
  unless args.empty?
    resource_name = [
      options.delete(:overwrite_name) ? nil : resource_name,
      args.map { |a| Bogo::Utility.snake(a) }.join("_"),
    ].flatten.compact.join("_").to_sym
  end
  record = struct._self.audit_log.push(
    type: :template,
    caller: extract_caller(caller),
    name: resource_name,
    location: to_nest[:path],
  )
  struct._self.wrapped_audit(record) do
    resource_name = struct._process_key(resource_name.to_sym)
    nested_template = compile(to_nest[:path], :sparkle)
    nested_template.parent = struct._self
    nested_template.audit_log = record.audit_log
    nested_template.name = resource_name
    if options[:parameters]
      nested_template.compile_state = options[:parameters]
    end
    unless struct._self.sparkle.empty?
      nested_template.sparkle.apply(struct._self.sparkle)
    end
    nested_resource = struct.dynamic!(
      struct._self.stack_resource_type,
      resource_name,
      {:resource_name_suffix => nil},
      &block
    )
    # Ignore the stack resource that we generated in the
    # audit log since the wrapping template record will
    # provide the context with actual information
    record.audit_log.list.pop
    nested_resource.properties.stack nested_template
    # Force compilation to properly record audit log times
    nested_template.compile
    nested_resource
  end
end
new(name, options = {}, &base_block) click to toggle source

Create new instance

@param name [String, Symbol] name of formation @param options [Hash] options @option options [String] :sparkle_path custom base path @option options [String] :components_directory custom components path @option options [String] :dynamics_directory custom dynamics path @option options [String] :registry_directory custom registry path @option options [Hash] :parameters parameters for stack generation @option options [Truthy, Falsey] :disable_aws_builtins do not load builtins @yield base context

# File lib/sparkle_formation/sparkle_formation.rb, line 473
def initialize(name, options = {}, &base_block)
  @name = name.to_sym
  @component_paths = []
  if options[:sparkle_collection]
    @sparkle = options[:sparkle_collection]
    if options[:sparkle]
      @sparkle.add_sparkle(options[:sparkle])
    end
  else
    @sparkle = SparkleCollection.new
    if options[:sparkle]
      @sparkle.set_root(options[:sparkle])
    else
      @sparkle.set_root(
        Sparkle.new(
          Smash.new.tap { |h|
            s_path = options.fetch(:sparkle_path,
                                   self.class.custom_paths[:sparkle_path])
            if s_path
              h[:root] = s_path
            else
              h[:root] = :none
            end
          }
        )
      )
    end
  end
  self.provider = options.fetch(:provider, @parent ? @parent.provider : :aws)
  if provider == :aws || !options[:disable_aws_builtins]
    require "sparkle_formation/aws"
  end
  @parameters = set_generation_parameters!(
    options.fetch(:compile_time_parameters,
                  options.fetch(:parameters, {}))
  )
  @stack_resource_types = [
    stack_resource_type,
    *options.fetch(:stack_resource_types, []),
  ].compact.uniq
  @blacklisted_templates = [name]
  @composition = Composition.new(self)
  @parent = options[:parent]
  @seed = Smash.new(
    :inherit => options[:inherit],
    :layering => options[:layering],
  )
  if base_block
    load_block(base_block)
  end
  @audit_log = AuditLog.new
  @compiled = nil
end
registry(registry_name, struct, *args) click to toggle source

Insert a dynamic into a context

@param dynamic_name [String, Symbol] dynamic name @param struct [SparkleStruct] context for insertion @param args [Object] parameters for dynamic @return [SparkleStruct]

# File lib/sparkle_formation/sparkle_formation.rb, line 230
def registry(registry_name, struct, *args)
  __t_stringish(registry_name)
  opts = args.detect { |item| item.is_a?(Hash) } || {}
  reg = struct._self.sparkle.get(:registry, registry_name, opts[:provider])
  record = struct._self.audit_log.push(
    type: :registry,
    name: registry_name,
    location: reg[:block].source_location,
    caller: extract_caller(caller),
  )
  struct._self.wrapped_audit(record) do
    struct.instance_exec(*args, &reg[:block])
  end
end
registry_path(path = nil)
Alias for: registry_path=
registry_path=(path = nil) click to toggle source

Get/set path to registry files

@param path [String] path to registry files @return [String] path to registry files

# File lib/sparkle_formation/sparkle_formation.rb, line 88
def registry_path=(path = nil)
  if path
    custom_paths[:registry_directory] = path
  end
  custom_paths[:registry_directory]
end
Also aliased as: registry_path
sparkle_path(path = nil)
Alias for: sparkle_path=
sparkle_path=(path = nil) click to toggle source

Get/set path to sparkle directory

@param path [String] path to directory @return [String] path to directory

# File lib/sparkle_formation/sparkle_formation.rb, line 46
def sparkle_path=(path = nil)
  if path
    custom_paths[:sparkle_path] = path
    custom_paths[:components_directory] = File.join(path, "components")
    custom_paths[:dynamics_directory] = File.join(path, "dynamics")
    custom_paths[:registry_directory] = File.join(path, "registry")
  end
  custom_paths[:sparkle_path]
end
Also aliased as: sparkle_path

Public Instance Methods

apply_deep_nesting(*args, &block) click to toggle source

Apply deeply nested stacks. This is the new nesting approach and does not bubble parameters up to the root stack. Parameters are isolated to the stack resource itself and output mapping is automatically applied.

@yieldparam stack [SparkleFormation] stack instance @yieldparam resource [AttributeStruct] the stack resource @yieldparam s_name [String] stack resource name @yieldreturn [Hash] key/values to be merged into resource properties @return [SparkleFormation::SparkleStruct] compiled structure

# File lib/sparkle_formation/sparkle_formation.rb, line 1003
def apply_deep_nesting(*args, &block)
  compile
end
apply_nesting(*args, &block) click to toggle source

Apply nesting logic to stack

@param nest_type [Symbol] values: :shallow, :deep (default: :deep) @return [SparkleFormation::SparkleStruct] compiled structure @note see specific version for expected block parameters

# File lib/sparkle_formation/sparkle_formation.rb, line 985
def apply_nesting(*args, &block)
  if args.include?(:shallow)
    apply_shallow_nesting(&block)
  else
    apply_deep_nesting(&block)
  end
end
apply_shallow_nesting(*args, &block) click to toggle source

Apply shallow nesting. This style of nesting will bubble parameters up to the root stack. This type of nesting is the original and now deprecated, but remains for compat issues so any existing usage won't be automatically busted.

@yieldparam resource_name [String] name of stack resource @yieldparam stack [SparkleFormation] nested stack @yieldreturn [String] Remote URL storage for template @return [SparkleFormation::SparkleStruct] compiled structure

# File lib/sparkle_formation/sparkle_formation.rb, line 1064
def apply_shallow_nesting(*args, &block)
  compile
end
block(block) click to toggle source

Add block to load order

@param block [Proc] @return [TrueClass]

# File lib/sparkle_formation/sparkle_formation.rb, line 747
def block(block)
  composition.new_component(:__base__, &block)
  true
end
Also aliased as: load_block
collect_outputs(*args) click to toggle source

@return [Smash<output_name:SparkleFormation>]

# File lib/sparkle_formation/sparkle_formation.rb, line 1069
def collect_outputs(*args)
  if args.include?(:force) || root?
    if !compile.outputs.nil? && !root?
      outputs = Smash[
        compile.outputs.keys!.zip(
          [self] * compile.outputs.keys!.size
        )
      ]
    else
      outputs = Smash.new
    end
    nested_stacks.each do |nested_stack|
      if nested_stack.is_a?(self.class) === true
        n_stack = nested_stack
      else
        n_stack = nested_stack._self
      end
      outputs = n_stack.collect_outputs(:force).merge(outputs)
    end
    outputs
  else
    root.collect_outputs(:force)
  end
end
compile(args = {}) click to toggle source

Compile the formation

@param args [Hash] @option args [Hash] :state local state parameters @return [SparkleStruct]

# File lib/sparkle_formation/sparkle_formation.rb, line 789
def compile(args = {})
  if args.key?(:state) && args.is_a?(Hash)
    @compile_state = args[:state].to_smash
    unmemoize(:compile)
  end
  memoize(:compile) do
    # NOTE: this is where we inject inherit or layering
    seed_self

    set_compile_time_parameters!
    if provider && SparkleStruct.const_defined?(camel(provider))
      struct_class = SparkleStruct.const_get(camel(provider))
      struct_name = [SparkleStruct.name, camel(provider)].join("::")
      struct_class.define_singleton_method(:name) { struct_name }
      struct_class.define_singleton_method(:to_s) { struct_name }
    else
      struct_class = SparkleStruct
    end
    load_resources!
    compiled = struct_class.new
    compiled._set_self(self)
    compiled._struct_class = struct_class
    if struct_class.const_defined?(:CAMEL_KEYS)
      compiled._camel_keys = struct_class.const_get(:CAMEL_KEYS)
    end
    if struct_class.const_defined?(:CAMEL_STYLE)
      compiled._camel_style = struct_class.const_get(:CAMEL_STYLE)
    end
    if compile_state
      compiled.set_state!(compile_state)
    end
    composition.each do |item|
      case item
      when Composition::Component
        record = nil
        if item.block
          if item.key == "__base__"
            if parent.nil?
              record = audit_log.push(
                type: :template,
                name: name,
                location: item.block.source_location.first,
                caller: [:template, 0],
              )
              # NOTE: Move audit log records under new record's
              # audit log. This allows components to be listed
              # logically under the template record instead of
              # at the same level.
              record.audit_log.list.replace(audit_log.list)
              record.audit_log.list.delete(record)

              audit_log.list.clear
              audit_log.list << record
            end
          else
            record = audit_log.push(
              type: :component,
              name: item.key,
              location: item.block.source_location.first,
              caller: [:template, 0],
            )
          end
          wrapped_audit(record) do
            self.class.build(compiled, &item.block)
          end
        else
          sparkle.get(:component, item.key).monochrome.each do |component_block|
            record = audit_log.push(
              type: :component,
              name: item.key,
              location: component_block[:block].source_location.first,
              caller: [:template, 0],
            )
            wrapped_audit(record) do
              self.class.build(compiled, &component_block[:block])
            end
          end
        end
      when Composition::Override
        record = audit_log.push(
          type: :component,
          name: :override,
          location: item.block.source_location.first,
          caller: [:template, 0],
        )
        if item.args && !item.args.empty?
          compiled._set_state(item.args)
        end
        wrapped_audit(record) do
          self.class.build(compiled, &item.block)
        end
      end
    end
    if compile_state && !compile_state.empty?
      set_compiled_state(compiled)
    end
    compiled
  end
end
compile_time_parameter_setter(&block) click to toggle source

Get or set the compile time parameter setting block. If a get request the ancestor path will be searched to root

@yield block to set compile time parameters @yieldparam [SparkleFormation] @return [Proc, NilClass]

# File lib/sparkle_formation/sparkle_formation.rb, line 705
def compile_time_parameter_setter(&block)
  if block
    @compile_time_parameter_setter = block
  else
    if @compile_time_parameter_setter
      @compile_time_parameter_setter
    else
      parent.nil? ? nil : parent.compile_time_parameter_setter
    end
  end
end
dump() click to toggle source

@return [Hash] dumped hash

# File lib/sparkle_formation/sparkle_formation.rb, line 1113
def dump
  MultiJson.load(to_json)
end
extract_template_data(template) click to toggle source

Extract information from given template and integrate with current instance

@param template [SparkleFormation] @return [self]

# File lib/sparkle_formation/sparkle_formation.rb, line 606
def extract_template_data(template)
  # TODO: Should allow forced override here for cases like: openstack -> rackspace
  if provider != template.provider
    raise TypeError.new "This template `#{name}` cannot inherit template `#{template.name}`! Provider mismatch: `#{provider}` != `#{template.provider}`" # rubocop:disable Metrics/LineLength
  end
  template.sparkle.apply(sparkle)
  template.seed_self
  blacklisted_templates.replace(
    (blacklisted_templates + template.blacklisted_templates).map(&:to_s).uniq
  )
  @parameters = template.parameters.to_smash.deep_merge(parameters.to_smash)
  new_composition = Composition.new(self,
                                    :components => template.composition.composite,
                                    :overrides => composition.overrides)
  composition.components.each do |item|
    if item.respond_to?(:key) && item.key == "__base__"
      item.key = Smash.new(
        :template => name,
        :component => :__base__,
        :object_id => object_id,
      ).checksum.to_s
    end
    new_composition.add_component(item)
  end
  @composition = new_composition
  self
end
extract_templates(&block) click to toggle source

Extract and process nested stacks

@yieldparam stack [SparkleFormation] stack instance @yieldparam resource [AttributeStruct] the stack resource @yieldparam s_name [String] stack resource name @yieldreturn [Hash] key/values to be merged into resource properties

# File lib/sparkle_formation/sparkle_formation.rb, line 1035
def extract_templates(&block)
  stack_template_extractor(nested_stacks(:with_resource, :with_name), &block)
end
generate_policy() click to toggle source

Generate policy for stack

@return [Hash]

# File lib/sparkle_formation/sparkle_formation.rb, line 976
def generate_policy
  Smash.new
end
includes_policies?(stack_hash = nil) click to toggle source

@return [TrueClass, FalseClass] policies defined

# File lib/sparkle_formation/sparkle_formation.rb, line 962
def includes_policies?(stack_hash = nil)
  if stack_hash
    raise Error::Deprecated.new "Hash parameter no longer valid for this method (`#{self.class}##{__callee__}`)"
  end
  unless compile.resources.nil?
    compile.resources._data.any? do |r_name, r_value|
      !r_value.policy.nil?
    end
  end
end
inherit_from(template_name) click to toggle source

Inhert template structure

@param template_name [String] name of template to inherit @return [self]

# File lib/sparkle_formation/sparkle_formation.rb, line 591
def inherit_from(template_name)
  if blacklisted_templates.map(&:to_s).include?(template_name.to_s)
    raise Error::CircularInheritance.new "Circular inheritance detected between templates `#{template_name}` and `#{name}`" # rubocop:disable Metrics/LineLength
  end
  template = self.class.compile(sparkle.get(:template, template_name)[:path], :sparkle)
  template.blacklisted_templates.replace(
    (template.blacklisted_templates + blacklisted_templates).map(&:to_s).uniq
  )
  extract_template_data(template)
end
isolated_nests?(stack_hash = nil) click to toggle source

@return [TrueClass, FalseClass] includes only nested stacks

# File lib/sparkle_formation/sparkle_formation.rb, line 950
def isolated_nests?(stack_hash = nil)
  if stack_hash
    raise Error::Deprecated.new "Hash parameter no longer valid for this method (`#{self.class}##{__callee__}`)"
  end
  unless compile.resources.nil?
    compile.resources._data.all? do |r_name, r_value|
      stack_resource_type?(r_value.type)
    end
  end
end
load(*args, &user_block) click to toggle source

Load components into instance

@param args [String, Symbol] Symbol component names or String paths @return [self]

# File lib/sparkle_formation/sparkle_formation.rb, line 758
def load(*args, &user_block)
  args.each do |thing|
    if thing.is_a?(String)
      # NOTE: This needs to be deprecated and removed
      # TODO: deprecate
      key = File.basename(thing.to_s).sub(".rb", "")
      composition.new_component(key, &self.class.load_component(thing))
    else
      composition.new_component(thing)
    end
  end
  if block_given?
    block(user_block)
  end
  self
end
load_block(block)
Alias for: block
make_output_available(output_name, outputs) click to toggle source

Extract output to make available for stack parameter usage at the current depth

@param output_name [String] name of output @param outputs [Hash] listing of outputs @return [Hash] reference to output value (used for setting parameter)

# File lib/sparkle_formation/sparkle_formation.rb, line 1025
def make_output_available(output_name, outputs)
  {}
end
merge_previous!() click to toggle source

Merge previous layer template structure

@return [self]

# File lib/sparkle_formation/sparkle_formation.rb, line 579
def merge_previous!
  my_index = sparkle.get(:template, name).spectrum.find_index do |item|
    item[:path] == template_path
  end
  template = self.class.compile(sparkle.get(:template, name).layer_at(my_index - 1)[:path], :sparkle)
  extract_template_data(template)
end
nested?(stack_hash = nil) click to toggle source

@return [TrueClass, FalseClass] includes nested stacks

# File lib/sparkle_formation/sparkle_formation.rb, line 938
def nested?(stack_hash = nil)
  if stack_hash
    raise Error::Deprecated.new "Hash parameter no longer valid for this method (`#{self.class}##{__callee__}`)"
  end
  unless compile.resources.nil?
    compile.resources._data.any? do |r_name, r_value|
      stack_resource_type?(r_value.type)
    end
  end
end
nested_stacks(*args) click to toggle source

@return [Array<SparkleFormation>]

# File lib/sparkle_formation/sparkle_formation.rb, line 916
def nested_stacks(*args)
  if compile[:resources]
    compile.resources.keys!.map do |key|
      if stack_resource_type?(compile.resources[key].type)
        if !compile.resources[key].properties.stack.nil?
          result = [compile.resources[key].properties.stack]
          if args.include?(:with_resource)
            result.push(compile[:resources][key])
          end
          if args.include?(:with_name)
            result.push(key)
          end
          result.size == 1 ? result.first : result
        end
      end
    end.compact
  else
    []
  end
end
output_matched?(p_name, output_names) click to toggle source

Check if parameter name matches an output name

@param p_name [String, Symbol] parameter name @param output_names [Array<String>] list of available outputs @return [String, NilClass] matching output name @note will auto downcase name prior to comparison

# File lib/sparkle_formation/sparkle_formation.rb, line 1013
def output_matched?(p_name, output_names)
  output_names.detect do |o_name|
    Bogo::Utility.snake(o_name).tr("_", "") == Bogo::Utility.snake(p_name).tr("_", "")
  end
end
overrides(args = {}, &block) click to toggle source

Registers block into overrides

@param args [Hash] optional arguments to provide state @yield override block

# File lib/sparkle_formation/sparkle_formation.rb, line 779
def overrides(args = {}, &block)
  composition.new_override(args, &block)
  self
end
parent=(p) click to toggle source

Set the parent template

@param p [SparkleFormation] parent template @return [self]

# File lib/sparkle_formation/sparkle_formation.rb, line 531
def parent=(p)
  unless p.is_a?(SparkleFormation)
    raise TypeError, "Expected `SparkleFormation` but received `#{p.class.name}`"
  end
  @parent = p
  @audit_log = p.audit_log
  self
end
provider=(val) click to toggle source

Set remote API target for template to allow loading of provider specific helpers and data if available. Setting to a false-y value will disable helpers loading

@param val [String, Symbol, NilClass, FalseClass] remote provider @return [Symbol, NilClass]

# File lib/sparkle_formation/sparkle_formation.rb, line 645
def provider=(val)
  if val
    @provider = Bogo::Utility.snake(val).to_sym
    provider_klass = Bogo::Utility.camel(@provider.to_s)
    if Provider.const_defined?(provider_klass)
      extend Provider.const_get(provider_klass)
    end
    sparkle.provider = val
    @provider
  else
    @provider = nil
  end
end
recompile() click to toggle source

Clear compiled stack if cached and perform compilation again

@return [SparkleStruct]

# File lib/sparkle_formation/sparkle_formation.rb, line 910
def recompile
  unmemoize(:compile)
  compile
end
remap_nested_parameters(template, parameters, stack_name, stack_resource, output_map) click to toggle source

Extract parameters from nested stacks. Check for previous nested stack outputs that match parameter. If match, set parameter to use output. If no match, check container stack parameters for match. If match, set to use ref. If no match, add parameter to container stack parameters and set to use ref.

@param template [Hash] template being processed @param parameters [Hash] top level parameter set being built @param stack_name [String] name of stack resource @param stack_resource [Hash] duplicate of stack resource contents @param output_map [Hash] mapping of output names to required stack output access @return [TrueClass] @note if parameter has includes `StackUnique` a new parameter will

be added to container stack and it will not use outputs
# File lib/sparkle_formation/sparkle_formation.rb, line 1108
def remap_nested_parameters(template, parameters, stack_name, stack_resource, output_map)
  true
end
root() click to toggle source

@return [SparkleFormation] root stack

# File lib/sparkle_formation/sparkle_formation.rb, line 668
def root
  if parent
    parent.root
  else
    self
  end
end
root?() click to toggle source

@return [TrueClass, FalseClass] current stack is root

# File lib/sparkle_formation/sparkle_formation.rb, line 686
def root?
  root == self
end
root_path() click to toggle source

@return [Array<SparkleFormation] path to root

# File lib/sparkle_formation/sparkle_formation.rb, line 677
def root_path
  if parent
    [*parent.root_path, self].compact
  else
    [self]
  end
end
seed_self() click to toggle source

Update underlying data structures based on inherit or layering behavior if defined for this template

@param options [Hash] @return [self]

# File lib/sparkle_formation/sparkle_formation.rb, line 560
def seed_self
  memoize(:seed) do
    options = @seed
    if options[:inherit] && options[:layering].to_s == "merge"
      raise ArgumentError.new "Cannot merge and inherit!"
    end
    if options[:inherit]
      inherit_from(options[:inherit])
    elsif options[:layering].to_s == "merge"
      merge_previous!
    end
    true
  end
  self
end
set_compile_time_parameters!() click to toggle source

Set the compile time parameters for the stack if the setter proc is available

# File lib/sparkle_formation/sparkle_formation.rb, line 719
def set_compile_time_parameters!
  if compile_time_parameter_setter
    compile_time_parameter_setter.call(self)
  end
end
set_compiled_state(compiled) click to toggle source

Set the current compile_state into the compiled result

@param compiled [AttributeStruct] @return [AttributeStruct]

# File lib/sparkle_formation/sparkle_formation.rb, line 894
def set_compiled_state(compiled)
  storage_compile_state = Smash.new
  parameters.each do |param_key, param_config|
    if param_config.fetch(:type, "string").to_s.downcase.to_sym != :complex
      storage_compile_state[param_key] = compile_state[param_key]
    end
  end
  unless storage_compile_state.empty?
    compiled.outputs.compile_state.value MultiJson.dump(storage_compile_state)
  end
  compiled
end
set_generation_parameters!(params) click to toggle source

Validation parameters used for template generation to ensure they are in the expected format

@param params [Hash] parameter set @return [Hash] parameter set @raise [ArgumentError]

# File lib/sparkle_formation/sparkle_formation.rb, line 731
def set_generation_parameters!(params)
  params.each do |name, value|
    unless value.is_a?(Hash)
      raise TypeError.new("Expecting `Hash` type. Received `#{value.class}`")
    end
    if key = value.keys.detect { |k| !ALLOWED_GENERATION_PARAMETERS.include?(k.to_s) }
      raise ArgumentError.new("Invalid generation parameter key provided `#{key}`")
    end
  end
  params
end
sparkle_dump() click to toggle source

@return [Hash] dumped hash

# File lib/sparkle_formation/sparkle_formation.rb, line 1118
def sparkle_dump
  MultiJson.load(
    MultiJson.dump(
      compile.sparkle_dump!
    )
  )
end
stack_resource_type() click to toggle source

@return [String] provider stack resource type

# File lib/sparkle_formation/sparkle_formation.rb, line 635
def stack_resource_type
  DEFAULT_STACK_RESOURCE
end
stack_resource_type?(type) click to toggle source

Check if type is a registered stack type

@param type [String] @return [TrueClass, FalseClass]

# File lib/sparkle_formation/sparkle_formation.rb, line 663
def stack_resource_type?(type)
  stack_resource_types.include?(type)
end
stack_template_extractor(x_stacks, &block) click to toggle source

Run the stack extraction

@param x_stacks [Array<Array<SparkleFormation, SparkleStruct, String>>]

# File lib/sparkle_formation/sparkle_formation.rb, line 1042
def stack_template_extractor(x_stacks, &block)
  x_stacks.each do |stack, resource, s_name|
    unless stack.nested_stacks.empty?
      stack_template_extractor(stack.nested_stacks(:with_resource, :with_name), &block)
    end
    resource.properties._delete(:stack)
    s_parent = resource.properties.stack
    stack.compile._parent(s_parent)
    resource.properties.set!(:stack, stack.compile)
    block.call(s_name, stack, resource)
  end
end
to_json(*args) click to toggle source

@return [String] dumped hash JSON

# File lib/sparkle_formation/sparkle_formation.rb, line 1127
def to_json(*args)
  # NOTE: Ported in from batali
  # Prevent stupid conversion errors of
  # JSON::Ext::Generator::State into Hash
  args = args.map do |argument|
    argument.respond_to?(:to_h) ? argument.to_h : argument
  end
  MultiJson.dump(compile.dump!, *args)
end
wrapped_audit(record) { || ... } click to toggle source

Wrap given block within audit log of given record

@param record [AuditLog::Record] @return [Object] result of yield

# File lib/sparkle_formation/sparkle_formation.rb, line 544
def wrapped_audit(record)
  start_log = audit_log
  start_time = Time.now.to_f
  @audit_log = record ? record.audit_log : start_log
  result = yield if block_given?
  record.compile_duration = Time.now.to_f - start_time if record
  result
ensure
  @audit_log = start_log
end

Protected Instance Methods

load_resources!() click to toggle source

Load provider specific resources handling

# File lib/sparkle_formation/sparkle_formation.rb, line 1140
def load_resources!
  if provider && @provider_resources.nil?
    provider_name = camel(PROVIDER_MAPPINGS.fetch(provider, provider))
    if Resources.const_defined?(provider_name)
      @provider_resources = Resources.const_get(provider_name)
      provider_resources.load!
    end
  end
end