class Humidifier::Stack
Represents a CFN stack
Constants
- AWS_REGION
The AWS region, can be set through the environment, defaults to us-east-1
- ENUMERABLE_RESOURCES
Lists of objects linked to the stack
- MAX_TEMPLATE_BODY_SIZE
The maximum size a template body can be before it has to be put somewhere and referenced through a URL
- MAX_TEMPLATE_URL_SIZE
The maximum size a template body can be inside of an S3 bucket
- MAX_WAIT
The maximum amount of time that
Humidifier
should wait for a stack to complete a CRUD operation- STATIC_RESOURCES
Single settings on the stack
Attributes
client[W]
default_identifier[R]
id[RW]
name[R]
Public Class Methods
new(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 68 def initialize(opts = {}) @name = opts[:name] @id = opts[:id] @default_identifier = self.class.next_default_identifier ENUMERABLE_RESOURCES.each_value do |property| instance_variable_set(:"@#{property}", opts.fetch(property, {})) end STATIC_RESOURCES.each_value do |property| instance_variable_set(:"@#{property}", opts[property]) end end
next_default_identifier()
click to toggle source
# File lib/humidifier/stack.rb, line 212 def self.next_default_identifier @count ||= 0 @count += 1 "humidifier-stack-template-#{@count}" end
Public Instance Methods
add(name, resource, attributes = {})
click to toggle source
# File lib/humidifier/stack.rb, line 82 def add(name, resource, attributes = {}) resources[name] = resource resource.update_attributes(attributes) if attributes.any? resource end
add_condition(name, opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 88 def add_condition(name, opts = {}) conditions[name] = Condition.new(opts) end
add_mapping(name, opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 92 def add_mapping(name, opts = {}) mappings[name] = Mapping.new(opts) end
add_output(name, opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 96 def add_output(name, opts = {}) outputs[name] = Output.new(opts) end
add_parameter(name, opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 100 def add_parameter(name, opts = {}) parameters[name] = Parameter.new(opts) end
client()
click to toggle source
# File lib/humidifier/stack.rb, line 104 def client @client ||= Aws::CloudFormation::Client.new(region: AWS_REGION) end
create(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 121 def create(opts = {}) params = { stack_name: name }.merge!(template_for(opts)).merge!(opts) try_valid do client.create_stack(params).tap { |response| @id = response.stack_id } end end
create_and_wait(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 129 def create_and_wait(opts = {}) perform_and_wait(:create, opts) end
create_change_set(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 133 def create_change_set(opts = {}) raise NoResourcesError.new(self, :change) unless resources.any? params = { stack_name: identifier, change_set_name: "changeset-#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}" } params.merge!(template_for(opts)).merge!(opts) try_valid { client.create_change_set(params) } end
delete(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 145 def delete(opts = {}) client.delete_stack({ stack_name: identifier }.merge!(opts)) true end
delete_and_wait(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 150 def delete_and_wait(opts = {}) perform_and_wait(:delete, opts) end
deploy(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 154 def deploy(opts = {}) raise NoResourcesError.new(self, :deploy) unless resources.any? exists? ? update(opts) : create(opts) end
deploy_and_wait(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 160 def deploy_and_wait(opts = {}) perform_and_wait(exists? ? :update : :create, opts) end
deploy_change_set(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 164 def deploy_change_set(opts = {}) exists? ? create_change_set(opts) : create(opts) end
exists?()
click to toggle source
# File lib/humidifier/stack.rb, line 168 def exists? Aws::CloudFormation::Stack.new(name: identifier).exists? end
identifier()
click to toggle source
# File lib/humidifier/stack.rb, line 108 def identifier id || name || default_identifier end
to_cf(serializer = :json)
click to toggle source
# File lib/humidifier/stack.rb, line 112 def to_cf(serializer = :json) resources = static_resources.merge!(enumerable_resources) case serializer when :json then JSON.pretty_generate(resources) when :yaml then YAML.dump(resources) end end
update(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 172 def update(opts = {}) params = { capabilities: %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM], stack_name: identifier } params.merge!(template_for(opts)).merge!(opts) try_valid { client.update_stack(params) } end
update_and_wait(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 183 def update_and_wait(opts = {}) perform_and_wait(:update, opts) end
upload()
click to toggle source
# File lib/humidifier/stack.rb, line 187 def upload raise NoResourcesError.new(self, :upload) unless resources.any? bucket = Humidifier.config.s3_bucket raise UploadNotConfiguredError, identifier unless bucket Aws.config.update(region: AWS_REGION) key = "#{Humidifier.config.s3_prefix}#{identifier}.json" Aws::S3::Client.new.put_object(body: to_cf, bucket: bucket, key: key) Aws::S3::Object.new(bucket, key).presigned_url(:get) end
valid?(opts = {})
click to toggle source
# File lib/humidifier/stack.rb, line 200 def valid?(opts = {}) params = template_for(opts).merge!(opts) try_valid { client.validate_template(params) } rescue Aws::CloudFormation::Errors::AccessDenied raise Error, <<~MSG The authenticated AWS profile does not have the requisite permissions to run this command. Ensure the profile has the "cloudformation:ValidateTemplate" IAM permission. MSG end
Private Instance Methods
bytesize()
click to toggle source
# File lib/humidifier/stack.rb, line 222 def bytesize to_cf.bytesize.tap do |size| raise TemplateTooLargeError, size if size > MAX_TEMPLATE_URL_SIZE end end
enumerable_resources()
click to toggle source
# File lib/humidifier/stack.rb, line 228 def enumerable_resources ENUMERABLE_RESOURCES.each_with_object({}) do |(name, prop), list| resources = public_send(prop) next if resources.empty? list[name] = resources.to_h do |resource_name, resource| [resource_name, resource.to_cf] end end end
perform_and_wait(method, opts)
click to toggle source
# File lib/humidifier/stack.rb, line 240 def perform_and_wait(method, opts) public_send(method, opts).tap do signal = :"stack_#{method}_complete" client.wait_until(signal, stack_name: identifier) do |waiter| waiter.max_attempts = (opts.delete(:max_wait) || MAX_WAIT) / 5 waiter.delay = 5 end end end
static_resources()
click to toggle source
# File lib/humidifier/stack.rb, line 251 def static_resources STATIC_RESOURCES.each_with_object({}) do |(name, prop), list| resource = public_send(prop) list[name] = resource if resource end end
template_for(opts)
click to toggle source
# File lib/humidifier/stack.rb, line 258 def template_for(opts) @template ||= if opts.delete(:force_upload) || Humidifier.config.force_upload || bytesize > MAX_TEMPLATE_BODY_SIZE { template_url: upload } else { template_body: to_cf } end end
try_valid() { ||| true| ... }
click to toggle source
# File lib/humidifier/stack.rb, line 270 def try_valid yield || true rescue Aws::CloudFormation::Errors::ValidationError => error warn(error.message) warn(error.backtrace) false end