Class: Cfer::Core::Stack

Inherits:
Block
  • Object
show all
Includes:
Functions, Hooks
Defined in:
lib/cfer/core/stack.rb

Overview

Defines the structure of a CloudFormation stack

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Hooks

included, #post_block, #pre_block

Methods included from Functions

#and, #cidr, #equals, #find_in_map, #get_att, #get_azs, #if, #join, #not, #notification_arns, #or, #ref, #select, #split, #sub

Methods inherited from Block

#build_from_block, #build_from_file, #build_from_string, #include_file, #post_block, #pre_block

Constructor Details

#initialize(options = {}) ⇒ Stack

Returns a new instance of Stack.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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
# File 'lib/cfer/core/stack.rb', line 30

def initialize(options = {})
  self[:AWSTemplateFormatVersion] = '2010-09-09'
  self[:Description] = ''

  @options = options

  self[:Metadata] = {
    :Cfer => {
      :Version => Cfer::SEMANTIC_VERSION.to_h.delete_if { |k, v| v === nil }
    }
  }

  self[:Parameters] = {}
  self[:Mappings] = {}
  self[:Conditions] = {}
  self[:Resources] = {}
  self[:Outputs] = {}

  if options[:client] && git = options[:client].git
    begin
      @git_state = git.object('HEAD^')
      self[:Metadata][:Cfer][:Git] = {
        Rev: git_state.sha,
        Clean: git.status.changed.empty?
      }
    rescue => e
      Cfer::LOGGER.warn("Unable to add Git information to CloudFormation Metadata. #{e}")
    end
  end

  @parameters = HashWithIndifferentAccess.new
  @input_parameters = HashWithIndifferentAccess.new

  if options[:client]
    begin
      @parameters.merge! options[:client].fetch_parameters
    rescue Cfer::Util::StackDoesNotExistError
      Cfer::LOGGER.debug "Can't include current stack parameters because the stack doesn't exist yet."
    end
  end

  if options[:parameters]
    options[:parameters].each do |key, val|
      @input_parameters[key] = @parameters[key] = val
    end
  end
end

Instance Attribute Details

#git_stateObject (readonly)

Returns the value of attribute git_state.



16
17
18
# File 'lib/cfer/core/stack.rb', line 16

def git_state
  @git_state
end

#input_parametersObject (readonly)

The parameters strictly as passed via command line



9
10
11
# File 'lib/cfer/core/stack.rb', line 9

def input_parameters
  @input_parameters
end

#optionsObject (readonly)

Returns the value of attribute options.



14
15
16
# File 'lib/cfer/core/stack.rb', line 14

def options
  @options
end

#parametersObject (readonly)

The fully resolved parameters, including defaults and parameters fetched from an existing stack during an update



12
13
14
# File 'lib/cfer/core/stack.rb', line 12

def parameters
  @parameters
end

Class Method Details

.extend_stack(&block) ⇒ Object



203
204
205
# File 'lib/cfer/core/stack.rb', line 203

def extend_stack(&block)
  class_eval(&block)
end

Instance Method Details

#clientObject

Gets the Cfn client, if one exists, or throws an error if one does not



174
175
176
# File 'lib/cfer/core/stack.rb', line 174

def client
  @options[:client] || raise('No client set on this stack')
end

#condition(name, expr) ⇒ Object

Adds a condition to the template.

Parameters:



133
134
135
# File 'lib/cfer/core/stack.rb', line 133

def condition(name, expr)
  self[:Conditions][name] = expr
end

#converge!(options = {}) ⇒ Object



22
23
24
# File 'lib/cfer/core/stack.rb', line 22

def converge!(options = {})
  client.converge self, options
end

#description(desc) ⇒ Object

Sets the description for this CloudFormation stack



79
80
81
# File 'lib/cfer/core/stack.rb', line 79

def description(desc)
  self[:Description] = desc
end

#include_template(*files) ⇒ Object

Includes template code from one or more files, and evals it in the context of this stack. Filenames are relative to the file containing the invocation of this method.



180
181
182
183
184
185
186
# File 'lib/cfer/core/stack.rb', line 180

def include_template(*files)
  include_base = options[:include_base] || File.dirname(caller.first.split(/:\d/,2).first)
  files.each do |file|
    path = File.join(include_base, file)
    include_file(path)
  end
end

#lookup_output(stack, out) ⇒ Object

Looks up a specific output of another CloudFormation stack in the same region.

Parameters:

  • stack (String)

    The name of the stack to fetch an output from

  • out (String)

    The name of the output to fetch from the stack



191
192
193
# File 'lib/cfer/core/stack.rb', line 191

def lookup_output(stack, out)
  lookup_outputs(stack).fetch(out)
end

#lookup_outputs(stack) ⇒ Object

Looks up a hash of all outputs from another CloudFormation stack in the same region.

Parameters:

  • stack (String)

    The name of the stack to fetch outputs from



197
198
199
200
# File 'lib/cfer/core/stack.rb', line 197

def lookup_outputs(stack)
  client = @options[:client] || raise(Cfer::Util::CferError, "Can not fetch stack outputs without a client")
  client.fetch_outputs(stack)
end

#mappings(mappings) ⇒ Object

Sets the mappings block for this stack. See The CloudFormation Documentation for more details



126
127
128
# File 'lib/cfer/core/stack.rb', line 126

def mappings(mappings)
  self[:Mappings] = mappings
end

#output(name, value, **options) ⇒ Object

Adds an output to the CloudFormation stack.

Parameters:

  • name (String)

    The Logical ID of the output parameter

  • value (String)

    Value to return

  • options (Hash)

    Extra options for this output parameter

Options Hash (**options):

  • :description (String)

    Information about the value

  • :export (String)

    Name be exported for cross-stack reference



157
158
159
160
161
# File 'lib/cfer/core/stack.rb', line 157

def output(name, value, **options)
  opt = options.each_with_object({}) { |(k,v),h| h[k.to_s.capitalize] = v } # capitalize all keys
  export = opt.has_key?('Export') ? {'Name' => opt['Export']} : nil
  self[:Outputs][name] = opt.merge('Value' => value, 'Export' => export).compact
end

#parameter(name, **options) ⇒ Object

Declares a CloudFormation parameter

Parameters:

  • name (String)

    The parameter name

  • options (Hash)

Options Hash (**options):

  • :type (String)

    The type for the CloudFormation parameter

  • :default (String)

    A value of the appropriate type for the template to use if no value is specified when a stack is created. If you define constraints for the parameter, you must specify a value that adheres to those constraints.

  • :no_echo (String)

    Whether to mask the parameter value whenever anyone makes a call that describes the stack. If you set the value to true, the parameter value is masked with asterisks (*****).

  • :allowed_values (String)

    An array containing the list of values allowed for the parameter.

  • :allowed_pattern (String)

    A regular expression that represents the patterns you want to allow for String types.

  • :max_length (Number)

    An integer value that determines the largest number of characters you want to allow for String types.

  • :min_length (Number)

    An integer value that determines the smallest number of characters you want to allow for String types.

  • :max_value (Number)

    A numeric value that determines the largest numeric value you want to allow for Number types.

  • :min_value (Number)

    A numeric value that determines the smallest numeric value you want to allow for Number types.

  • :description (String)

    A string of up to 4000 characters that describes the parameter.

  • :constraint_description (String)

    A string that explains the constraint when the constraint is violated. For example, without a constraint description, a parameter that has an allowed pattern of [A-Za-z0-9]+ displays the following error message when the user specifies an invalid value:

    Malformed input-Parameter MyParameter must match pattern [A-Za-z0-9]+

    By adding a constraint description, such as must only contain upper- and lowercase letters, and numbers, you can display a customized error message:

    Malformed input-Parameter MyParameter must only contain upper and lower case letters and numbers



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/cfer/core/stack.rb', line 104

def parameter(name, **options)
  param = {}
  options.each do |key, v|
    next if v === nil

    k = key.to_s.camelize.to_sym
    param[k] =
      case k
      when :AllowedPattern
        if v.class == Regexp
          v.source
        end
      when :Default
        @parameters[name] ||= v
      end
    param[k] ||= v
  end
  param[:Type] ||= 'String'
  self[:Parameters][name] = param
end

#resource(name, type, **options, &block) ⇒ Object

Creates a CloudFormation resource

Parameters:

  • name (String)

    The name of the resource (must be alphanumeric)

  • type (String)

    The type of CloudFormation resource to create.

  • options (Hash)

    Additional attributes to add to the resource block (such as the UpdatePolicy for an AWS::AutoScaling::AutoScalingGroup)



141
142
143
144
145
146
147
148
149
# File 'lib/cfer/core/stack.rb', line 141

def resource(name, type, **options, &block)
  Preconditions.check_argument(/[[:alnum:]]+/ =~ name, "Resource name must be alphanumeric")

  clazz = Cfer::Core::Resource.resource_class(type)
  rc = clazz.new(name, type, self, options, &block)

  self[:Resources][name] = rc
  rc.handle
end

#tail!(options = {}, &block) ⇒ Object



26
27
28
# File 'lib/cfer/core/stack.rb', line 26

def tail!(options = {}, &block)
  client.tail self, options, &block
end

#to_cfnString

Renders the stack into a CloudFormation template.

Returns:

  • (String)

    The final template



165
166
167
168
169
170
171
# File 'lib/cfer/core/stack.rb', line 165

def to_cfn
  if @options[:pretty_print]
    JSON.pretty_generate(to_h)
  else
    to_h.to_json
  end
end