Class: Enscalator::RichTemplateDSL

Inherits:
TemplateDSL
  • Object
show all
Includes:
Core::CfParameters, Core::CfResources, Helpers, Plugins::Route53
Defined in:
lib/enscalator/rich_template_dsl.rb

Overview

DSL specific for enscalator

Constant Summary collapse

TEMPLATE_BODY_LIMIT =

Cloudformation limit when sending template body directly

51_200

Constants included from Plugins::Route53

Plugins::Route53::HEALTH_CHECK_TYPE, Plugins::Route53::RECORD_TYPE

Instance Method Summary collapse

Methods included from Plugins::Route53

#create_healthcheck, #create_multiple_dns_records, #create_private_hosted_zone, #create_public_hosted_zone, #create_single_dns_record

Methods included from Helpers

#cfn_call_script, #create_ssh_key, #find_ami, #flatten_hash, #gen_ssh_key_name, #init_assets_dir, #init_aws_config, #read_user_data, #run_cmd

Methods included from Helpers::Dns

#get_dns_records, #upsert_dns_record

Methods included from Helpers::Stack

#cfn_create_stack, #generate_parameters, #get_resource, #get_resources, #wait_stack

Methods included from Helpers::Wrappers

#cfn_client, #cfn_resource, #ec2_client, #route53_client

Methods included from Core::CfResources

#iam_instance_profile_with_full_access, #instance_vpc, #instance_with_network, #security_group, #security_group_vpc, #subnet, #vpc

Methods included from Core::CfParameters

#parameter_allocated_storage, #parameter_ami, #parameter_ec2_instance_type, #parameter_instance_type, #parameter_key_name, #parameter_name, #parameter_password, #parameter_rds_instance_type, #parameter_username

Constructor Details

#initialize(options = {}) ⇒ RichTemplateDSL

Create new RichTemplateDSL instance

Parameters:

  • options (Hash) (defaults to: {})

    command-line arguments



17
18
19
20
21
# File 'lib/enscalator/rich_template_dsl.rb', line 17

def initialize(options = {})
  @options = options
  init_aws_config(@options[:region], profile_name: @options[:profile])
  super(parse_params(@options[:parameters]), @options[:stack_name], @options[:region], false, &proc { tpl })
end

Instance Method Details

#availability_zonesObject

Availability zones accessor



108
109
110
# File 'lib/enscalator/rich_template_dsl.rb', line 108

def availability_zones
  @availability_zones ||= read_availability_zones
end

#creating?Truthy

Helper method to check if the current command is to create the stack

Returns:

  • (Truthy)

    truthful if currently creating a stack



34
35
36
# File 'lib/enscalator/rich_template_dsl.rb', line 34

def creating?
  @options[:create_stack]
end

#deploy(template) ⇒ Object

Pass generated template to underlying cloudformation client to actually create/update stack

Parameters:

  • template (TemplateDSL)

    instance of template

Raises:

  • (RuntimeError)

    when generated template exceeds 51200 size limit



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/enscalator/rich_template_dsl.rb', line 193

def deploy(template)
  template_body = template.to_json
  if template_body.bytesize > TEMPLATE_BODY_LIMIT
    fail("Unable to deploy template exceeding #{TEMPLATE_BODY_LIMIT} limit: #{template_body.bytesize}")
  end
  options = {
    stack_name: stack_name,
    capabilities: [@options[:capabilities]],
    template_body: template_body
  }
  options[:parameters] = parameters.map { |k, v| { parameter_key: k, parameter_value: v } } unless parameters.empty?
  action = @options[:update_stack] ? :update_stack : :create_stack
  resp = cfn_client(region).send(action, options)
  resp.stack_id
end

#deployment_envObject



219
220
221
# File 'lib/enscalator/rich_template_dsl.rb', line 219

def deployment_env
  parameters[@deployment_env_param]
end

#description(desc) ⇒ Object

Template description

Parameters:

  • desc (String)

    template description



137
138
139
# File 'lib/enscalator/rich_template_dsl.rb', line 137

def description(desc)
  value(Description: desc)
end

#enqueue(items) ⇒ Object

Adds block to the run queue

Parameters:

  • items (Array)

    list of blocks



177
178
179
# File 'lib/enscalator/rich_template_dsl.rb', line 177

def enqueue(items)
  (@run_queue ||= []).concat(items || [])
end

#exec!Object

Determine content of run queue and execute each block in queue in sequence



182
183
184
185
186
187
188
# File 'lib/enscalator/rich_template_dsl.rb', line 182

def exec!
  init_assets_dir
  enqueue(@pre_run_blocks) if @options[:pre_run]
  enqueue([@options[:expand] ? proc { STDOUT.puts(JSON.pretty_generate(self)) } : proc { STDOUT.puts(deploy(self)) }])
  enqueue(@post_run_blocks) if @options[:post_run]
  @run_queue.each(&:call) if @run_queue
end

#handle_trailing_dot(str) ⇒ String

Adds trailing dot to make it proper FQDN

Parameters:

  • str (String)

    fqdn string

Returns:

  • (String)

    fqdn with trailing dot



57
58
59
# File 'lib/enscalator/rich_template_dsl.rb', line 57

def handle_trailing_dot(str)
  str.end_with?('.') ? str : str + '.'
end

#has_multiple_envsObject



209
210
211
212
213
214
215
216
217
# File 'lib/enscalator/rich_template_dsl.rb', line 209

def has_multiple_envs
  @deployment_env_param = 'DeploymentEnv'
  parameter @deployment_env_param, {
    Description: 'Environment parameters to configure stack resources (production, staging or development)',
    Type: 'String',
    AllowedValues: %w(production staging development),
    Default: 'staging'
  }
end

#hosted_zoneString

Deprecated.

Hosted zone accessor

Returns:

  • (String)

    hosted zone, and ensure ending with a ‘.’

Raises:

  • (RuntimeError)

    if hosted zone is accessed but it’s not configured



66
67
68
69
# File 'lib/enscalator/rich_template_dsl.rb', line 66

def hosted_zone
  ActiveSupport::Deprecation.warn('hosted_zone is deprecated (use private_hosted_zone instead)')
  private_hosted_zone
end

#network_interface(device_index, options: {}) ⇒ Object

Network interface

Parameters:

  • device_index (String)

    network interface device index

  • options (Hash) (defaults to: {})


145
146
147
148
# File 'lib/enscalator/rich_template_dsl.rb', line 145

def network_interface(device_index, options: {})
  options[:DeviceIndex] = device_index
  options
end

#parameter(name, options) ⇒ Object

Dynamically define methods to access related parameters

Parameters:

  • name (String)

    parameter key

  • options (Hash)

    options



166
167
168
169
170
171
172
# File 'lib/enscalator/rich_template_dsl.rb', line 166

def parameter(name, options)
  default(:Parameters, {})[name] = options
  @parameters[name] ||= options[:Default]
  self.class.send(:define_method, :"ref_#{name.underscore}") do
    ref(name)
  end
end

#parse_params(raw_parameters) ⇒ Hash

Parse semicolon separated parameter string

Parameters:

  • raw_parameters (String)

    raw parameter string

Returns:

  • (Hash)

    parameter hash



27
28
29
# File 'lib/enscalator/rich_template_dsl.rb', line 27

def parse_params(raw_parameters)
  Hash[(raw_parameters || '').split(/;/).map { |pair| pair.split(/=/, 2) }]
end

#post_run(&block) ⇒ Object

Post-run hook

Parameters:

  • block (Proc)

    hook body



122
123
124
# File 'lib/enscalator/rich_template_dsl.rb', line 122

def post_run(&block)
  (@post_run_blocks ||= []) << block if block_given?
end

#pre_run(&block) ⇒ Object

Pre-run hook

Parameters:

  • block (Proc)

    hook body



115
116
117
# File 'lib/enscalator/rich_template_dsl.rb', line 115

def pre_run(&block)
  (@pre_run_blocks ||= []) << block if block_given?
end

#private_hosted_zoneString

Private hosted zone accessor

Returns:

  • (String)

    private hosted zone

Raises:

  • (RuntimeError)

    if private hosted zone was accessed before it was configured



75
76
77
78
79
# File 'lib/enscalator/rich_template_dsl.rb', line 75

def private_hosted_zone
  # TODO: adjust other templates/plugins to use private_hosted_zone
  @options[:private_hosted_zone] || fail('Private hosted zone has to be configured')
  handle_trailing_dot(@options[:private_hosted_zone])
end

#public_hosted_zoneString

Public hosted zone accessor

Returns:

  • (String)

    public hosted zone

Raises:

  • (RuntimeError)

    if hosted zone was accessed before it was configured



85
86
87
88
# File 'lib/enscalator/rich_template_dsl.rb', line 85

def public_hosted_zone
  @options[:public_hosted_zone] || fail('Public hosted zone has to be configured')
  handle_trailing_dot(@options[:public_hosted_zone])
end

#read_availability_zonesObject

Get a list of availability zones for the given region



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/enscalator/rich_template_dsl.rb', line 91

def read_availability_zones
  az = @options[:availability_zone]
  supported_az = ec2_client(region).describe_availability_zones.availability_zones
  alive_az = supported_az.select { |zone| zone.state == 'available' }
  az_list = alive_az.collect(&:zone_name).map { |n| [n.last.to_sym, n] }.to_h

  # use all zones, specific one, or fail if zone is not supported in given region
  if az == 'all'
    az_list
  elsif az.split(',').map(&:to_sym).all?{|x| az_list.include?(x)}
    az_list.select { |k, _| az.split(',').map(&:to_sym).include?(k) }
  else
    fail("Requested zone #{az} is not supported in #{region}, supported ones are #{az_list.keys.join(',')}")
  end
end

#regionString

Helper method to provide accessor for ‘region`

Returns:

  • (String)

    region



41
42
43
# File 'lib/enscalator/rich_template_dsl.rb', line 41

def region
  aws_region
end

#resource(name, options) ⇒ Object

Resource

Parameters:

  • name (String)

    of the resource

  • options (Hash)

    options



154
155
156
157
158
159
160
# File 'lib/enscalator/rich_template_dsl.rb', line 154

def resource(name, options)
  super
  return nil unless options[:Type] && %w(AWS::EC2::Instance).include?(options[:Type])
  output "#{name}PrivateIpAddress",
         Description: "#{name} Private IP Address",
         Value: get_att(name, 'PrivateIp')
end

#tags_to_properties(tags) ⇒ Array

Convert tags to properties

Parameters:

  • tags (Hash)

    collection of tags

Returns:

  • (Array)

    list of properties



130
131
132
# File 'lib/enscalator/rich_template_dsl.rb', line 130

def tags_to_properties(tags)
  tags.map { |k, v| { Key: k, Value: v } }
end

#vpc_stack_nameString

Helper method to provide value accessor for ‘vpc_stack_name`

Returns:

  • (String)

    vpc_stack_name

Raises:

  • (RuntimeError)

    if vpc-stack-name was not given



49
50
51
# File 'lib/enscalator/rich_template_dsl.rb', line 49

def vpc_stack_name
  @options[:vpc_stack_name] || fail('Requires vpc-stack-name')
end