humidifier 1.8.0 → 1.9.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -31,7 +31,8 @@ module Humidifier
31
31
  # Upload a CFN stack to S3 so that it can be referenced via template_url
32
32
  def upload(payload)
33
33
  Humidifier.config.ensure_upload_configured!(payload)
34
- upload_object(payload, "#{Humidifier.config.s3_prefix}#{payload.identifier}.json")
34
+ filename = "#{Humidifier.config.s3_prefix}#{payload.identifier}.json"
35
+ upload_object(payload, filename)
35
36
  end
36
37
 
37
38
  # Validate a template in CFN
@@ -48,7 +49,8 @@ module Humidifier
48
49
  private
49
50
 
50
51
  def client
51
- @client ||= base_module::CloudFormation::Client.new(region: AwsShim::REGION)
52
+ @client ||=
53
+ base_module::CloudFormation::Client.new(region: AwsShim::REGION)
52
54
  end
53
55
 
54
56
  def try_valid
@@ -2,7 +2,8 @@ module Humidifier
2
2
  module AwsAdapters
3
3
  # An adapter used when neither SDK is loaded
4
4
  class Noop
5
- # Capture all STACK_METHODS method calls and warn that they will not be run
5
+ # Capture all STACK_METHODS method calls and warn that they will not be
6
+ # run
6
7
  def method_missing(method, *)
7
8
  if AwsShim::STACK_METHODS.include?(method)
8
9
  puts "WARNING: Cannot run #{method} because aws-sdk not loaded."
@@ -23,12 +23,20 @@ module Humidifier
23
23
  AWS
24
24
  end
25
25
 
26
+ def failure_event_filtered?(event)
27
+ !event.resource_status.include?('FAILED') ||
28
+ !event.key?(:resource_status_reason)
29
+ end
30
+
26
31
  def handle_failure(payload)
27
32
  reasons = []
28
- client.describe_stack_events(stack_name: payload.identifier).stack_events.each do |event|
29
- next unless event.resource_status.include?('FAILED') && event.key?(:resource_status_reason)
33
+ response = client.describe_stack_events(stack_name: payload.identifier)
34
+
35
+ response.stack_events.each do |event|
36
+ next if failure_event_filtered?(event)
30
37
  reasons.unshift(event.resource_status_reason)
31
38
  end
39
+
32
40
  raise "#{payload.name} stack failed:\n#{reasons.join("\n")}"
33
41
  end
34
42
 
@@ -37,7 +45,8 @@ module Humidifier
37
45
 
38
46
  aws_stack = nil
39
47
  Sleeper.new(payload.max_wait) do
40
- aws_stack = client.describe_stacks(stack_name: payload.identifier).stacks.first
48
+ response = client.describe_stacks(stack_name: payload.identifier)
49
+ aws_stack = response.stacks.first
41
50
  !aws_stack.stack_status.end_with?('IN_PROGRESS')
42
51
  end
43
52
 
@@ -48,11 +57,14 @@ module Humidifier
48
57
  def upload_object(payload, key)
49
58
  base_module.config(region: AwsShim::REGION)
50
59
  @s3 ||= base_module::S3.new
51
- @s3.buckets[Humidifier.config.s3_bucket].objects.create(key, payload.template_body).url_for(:read)
60
+
61
+ objects = @s3.buckets[Humidifier.config.s3_bucket].objects
62
+ objects.create(key, payload.template_body).url_for(:read)
52
63
  end
53
64
 
54
65
  def unsupported(method)
55
- puts "WARNING: Cannot #{method} because that functionality is not supported in V1 of aws-sdk."
66
+ puts "WARNING: Cannot #{method} because that functionality is not " \
67
+ 'supported in V1 of aws-sdk.'
56
68
  false
57
69
  end
58
70
  end
@@ -2,9 +2,13 @@ module Humidifier
2
2
  module AwsAdapters
3
3
  # The adapter for v2 of aws-sdk
4
4
  class SDKV2 < Base
5
+ # Format of the timestamp used in changeset naming
6
+ TIME_FORMAT = '%Y-%m-%d-%H-%M-%S'.freeze
7
+
5
8
  # Create a change set in CFN
6
9
  def create_change_set(payload)
7
- payload.merge(change_set_name: "changeset-#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}")
10
+ change_set_name = "changeset-#{Time.now.strftime(TIME_FORMAT)}"
11
+ payload.merge(change_set_name: change_set_name)
8
12
  try_valid { client.create_change_set(payload.create_change_set_params) }
9
13
  end
10
14
 
@@ -27,8 +31,9 @@ module Humidifier
27
31
  def perform_and_wait(method, payload)
28
32
  method = exists?(payload) ? :update : :create if method == :deploy
29
33
  response = public_send(method, payload)
34
+ signal = :"stack_#{method}_complete"
30
35
 
31
- client.wait_until(:"stack_#{method}_complete", stack_name: payload.identifier) do |waiter|
36
+ client.wait_until(signal, stack_name: payload.identifier) do |waiter|
32
37
  waiter.max_attempts = payload.max_wait / 5
33
38
  waiter.delay = 5
34
39
  end
@@ -39,8 +44,15 @@ module Humidifier
39
44
  def upload_object(payload, key)
40
45
  base_module.config.update(region: AwsShim::REGION)
41
46
  @s3_client ||= base_module::S3::Client.new
42
- @s3_client.put_object(body: payload.template_body, bucket: Humidifier.config.s3_bucket, key: key)
43
- base_module::S3::Object.new(Humidifier.config.s3_bucket, key).presigned_url(:get)
47
+
48
+ @s3_client.put_object(
49
+ body: payload.template_body,
50
+ bucket: Humidifier.config.s3_bucket,
51
+ key: key
52
+ )
53
+
54
+ object = base_module::S3::Object.new(Humidifier.config.s3_bucket, key)
55
+ object.presigned_url(:get)
44
56
  end
45
57
  end
46
58
  end
@@ -17,13 +17,13 @@ module Humidifier
17
17
  create_change_set deploy_change_set
18
18
  ].freeze
19
19
 
20
- attr_accessor :shim
20
+ attr_reader :shim
21
21
 
22
22
  # Either set the SDK based on the configured option or guess the SDK
23
23
  # version by attempting to require both aws-sdk-v1 and aws-sdk, then setting
24
24
  # the shim based on what successfully loaded
25
25
  def initialize
26
- self.shim =
26
+ @shim =
27
27
  if Humidifier.config.sdk_version_1?
28
28
  AwsAdapters::SDKV1.new
29
29
  elsif Humidifier.config.sdk_version_2?
@@ -1,10 +1,10 @@
1
1
  module Humidifier
2
2
  # Represents a CFN stack condition
3
3
  class Condition
4
- attr_accessor :opts
4
+ attr_reader :opts
5
5
 
6
6
  def initialize(opts)
7
- self.opts = opts
7
+ @opts = opts
8
8
  end
9
9
 
10
10
  # CFN stack syntax
@@ -4,9 +4,10 @@ module Humidifier
4
4
  # The message that gets displayed when the stack body is too large to use
5
5
  # the template_body option
6
6
  UPLOAD_MESSAGE = <<-MSG.freeze
7
- The %<identifier>s stack's body is too large to be use the template_body option, and therefore must use the
8
- template_url option instead. You can configure Humidifier to do this automatically by setting up the s3 config
9
- on the top-level Humidifier object like so:
7
+ The %<identifier>s stack's body is too large to be use the template_body option,
8
+ and therefore must use the template_url option instead. You can configure
9
+ Humidifier to do this automatically by setting up the s3 config on the top-level
10
+ Humidifier object like so:
10
11
 
11
12
  Humidifier.configure do |config|
12
13
  config.s3_bucket = 'my.s3.bucket'
@@ -24,7 +25,8 @@ MSG
24
25
 
25
26
  # raise an error unless the s3_bucket field is set
26
27
  def ensure_upload_configured!(payload)
27
- raise UPLOAD_MESSAGE.gsub('%<identifier>s', payload.identifier) if s3_bucket.nil?
28
+ return if s3_bucket
29
+ raise UPLOAD_MESSAGE.gsub('%<identifier>s', payload.identifier)
28
30
  end
29
31
 
30
32
  # true if the sdk_version option is set to 1 or '1'
data/lib/humidifier/fn.rb CHANGED
@@ -2,20 +2,22 @@ module Humidifier
2
2
  # Builds CFN function calls
3
3
  class Fn
4
4
  # The list of all internal functions provided by AWS from
5
- # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
5
+ # http://docs.aws.amazon.com
6
+ # /AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
6
7
  FUNCTIONS = Utils.underscored(%w[And Base64 Equals FindInMap GetAtt GetAZs
7
8
  If ImportValue Join Not Or Select Sub])
8
9
 
9
- attr_accessor :name, :value
10
+ attr_reader :name, :value
10
11
 
11
12
  def initialize(name, value)
12
- self.name = "Fn::#{name}"
13
- self.value = value
13
+ @name = "Fn::#{name}"
14
+ @value = value
14
15
  end
15
16
 
16
17
  # CFN stack syntax
17
18
  def to_cf
18
- { name => value }
19
+ cf_value = value.respond_to?(:cf) ? value.to_cf : value
20
+ { name => cf_value }
19
21
  end
20
22
 
21
23
  class << self
@@ -10,8 +10,10 @@ module Humidifier
10
10
  # Reads the specs/CloudFormationResourceSpecification.json file and load each
11
11
  # resource as a class
12
12
  class Loader
13
+ filename = 'CloudFormationResourceSpecification.json'
14
+
13
15
  # The path to the specification file
14
- SPECPATH = File.expand_path(File.join('..', '..', '..', 'CloudFormationResourceSpecification.json'), __FILE__)
16
+ SPECPATH = File.expand_path(File.join('..', '..', '..', filename), __FILE__)
15
17
 
16
18
  # Handles searching the PropertyTypes specifications for a specific
17
19
  # resource type
@@ -25,7 +27,8 @@ module Humidifier
25
27
  # find the substructures necessary for the given resource key
26
28
  def search(key)
27
29
  results = structs.keys.grep(/#{key}/)
28
- Hash[results.map { |result| result.gsub("#{key}.", '') }.zip(structs.values_at(*results))].merge(global)
30
+ shortened_names = results.map { |result| result.gsub("#{key}.", '') }
31
+ shortened_names.zip(structs.values_at(*results)).to_h.merge(global)
29
32
  end
30
33
 
31
34
  private
@@ -37,8 +40,8 @@ module Humidifier
37
40
 
38
41
  # loop through the specs and register each class
39
42
  def load
40
- parsed = JSON.parse(File.read(SPECPATH))
41
- structs = StructureContainer.new(parsed['PropertyTypes'])
43
+ parsed = JSON.parse(File.read(SPECPATH))
44
+ structs = StructureContainer.new(parsed['PropertyTypes'])
42
45
 
43
46
  parsed['ResourceTypes'].each do |key, spec|
44
47
  match = key.match(/\AAWS::(\w+)::(\w+)\z/)
@@ -68,7 +71,10 @@ module Humidifier
68
71
  aws_name = "AWS::#{group}::#{resource}"
69
72
  resource_class = build_class(aws_name, spec, substructs)
70
73
 
71
- Humidifier.const_set(group, Module.new) unless Humidifier.const_defined?(group)
74
+ unless Humidifier.const_defined?(group)
75
+ Humidifier.const_set(group, Module.new)
76
+ end
77
+
72
78
  Humidifier.const_get(group).const_set(resource, resource_class)
73
79
  Humidifier.registry[aws_name] = resource_class
74
80
  end
@@ -1,10 +1,10 @@
1
1
  module Humidifier
2
2
  # Represents a CFN stack mapping
3
3
  class Mapping
4
- attr_accessor :opts
4
+ attr_reader :opts
5
5
 
6
6
  def initialize(opts = {})
7
- self.opts = opts
7
+ @opts = opts
8
8
  end
9
9
 
10
10
  # CFN stack syntax
@@ -2,14 +2,19 @@ module Humidifier
2
2
  # Represents a CFN stack parameter
3
3
  class Parameter
4
4
  # The allowed properties of all stack parameters
5
- PROPERTIES = Utils.underscored(%w[AllowedPattern AllowedValues ConstraintDescription Default Description
6
- MaxLength MaxValue MinLength MinValue NoEcho])
5
+ PROPERTIES =
6
+ Utils.underscored(%w[AllowedPattern AllowedValues ConstraintDescription
7
+ Default Description MaxLength MaxValue MinLength
8
+ MinValue NoEcho])
7
9
 
8
- attr_accessor :type, *PROPERTIES.values
10
+ attr_reader :type, *PROPERTIES.values
9
11
 
10
12
  def initialize(opts = {})
11
- PROPERTIES.each_value { |prop| send(:"#{prop}=", opts[prop]) }
12
- self.type = opts.fetch(:type, 'String')
13
+ PROPERTIES.each_value do |property|
14
+ instance_variable_set(:"@#{property}", opts[property])
15
+ end
16
+
17
+ @type = opts.fetch(:type, 'String')
13
18
  end
14
19
 
15
20
  # CFN stack syntax
@@ -2,7 +2,8 @@ module Humidifier
2
2
  module Props
3
3
  # Superclass for all CFN properties
4
4
  class Base
5
- # The list of classes that are valid beyond the normal values for each prop
5
+ # The list of classes that are valid beyond the normal values for each
6
+ # prop
6
7
  WHITELIST = [Fn, Ref].freeze
7
8
 
8
9
  attr_reader :key, :name, :spec, :substructs
@@ -29,7 +30,8 @@ module Humidifier
29
30
  [key, Serializer.dump(value)]
30
31
  end
31
32
 
32
- # the type of update that occurs when this property is updated on its associated resource
33
+ # the type of update that occurs when this property is updated on its
34
+ # associated resource
33
35
  def update_type
34
36
  spec['UpdateType']
35
37
  end
@@ -14,7 +14,8 @@ module Humidifier
14
14
 
15
15
  # true if it is a boolean
16
16
  def valid?(value)
17
- whitelisted_value?(value) || value.is_a?(TrueClass) || value.is_a?(FalseClass)
17
+ return true if whitelisted_value?(value)
18
+ value.is_a?(TrueClass) || value.is_a?(FalseClass)
18
19
  end
19
20
  end
20
21
  end
@@ -11,12 +11,21 @@ module Humidifier
11
11
 
12
12
  # CFN stack syntax
13
13
  def to_cf(list)
14
- [key, list.map { |value| subprop.to_cf(value).last }]
14
+ cf_value =
15
+ if list.respond_to?(:to_cf)
16
+ list.to_cf
17
+ else
18
+ list.map { |value| subprop.to_cf(value).last }
19
+ end
20
+
21
+ [key, cf_value]
15
22
  end
16
23
 
17
- # Valid if the value is whitelisted or every value in the list is valid on the subprop
24
+ # Valid if the value is whitelisted or every value in the list is valid
25
+ # on the subprop
18
26
  def valid?(list)
19
- whitelisted_value?(list) || (list.is_a?(Enumerable) && list.all? { |value| subprop.valid?(value) })
27
+ return true if whitelisted_value?(list)
28
+ list.is_a?(Enumerable) && list.all? { |value| subprop.valid?(value) }
20
29
  end
21
30
 
22
31
  private
@@ -6,18 +6,29 @@ module Humidifier
6
6
 
7
7
  # converts the value through mapping using the subprop unless it is valid
8
8
  def convert(map)
9
- valid?(map) ? map : map.map { |key, value| [key, subprop.convert(value)] }.to_h
9
+ return map if valid?(map)
10
+ map.map { |key, value| [key, subprop.convert(value)] }.to_h
10
11
  end
11
12
 
12
13
  # CFN stack syntax
13
14
  def to_cf(map)
14
- [key, map.map { |subkey, subvalue| [subkey, subprop.to_cf(subvalue).last] }.to_h]
15
+ cf_value =
16
+ if map.respond_to?(:to_cf)
17
+ map.to_cf
18
+ else
19
+ map.each_with_object({}) do |(subkey, subvalue), serialized|
20
+ serialized[subkey] = subprop.to_cf(subvalue).last
21
+ end
22
+ end
23
+
24
+ [key, cf_value]
15
25
  end
16
26
 
17
27
  # Valid if the value is whitelisted or every value in the map is valid on
18
28
  # the subprop
19
29
  def valid?(map)
20
- whitelisted_value?(map) || (map.is_a?(Hash) && map.values.all? { |value| subprop.valid?(value) })
30
+ return true if whitelisted_value?(map)
31
+ map.is_a?(Hash) && map.values.all? { |value| subprop.valid?(value) }
21
32
  end
22
33
 
23
34
  private
@@ -18,27 +18,50 @@ module Humidifier
18
18
 
19
19
  # CFN stack syntax
20
20
  def to_cf(struct)
21
- [key, struct.map { |subkey, subvalue| subprops[subkey.to_s].to_cf(subvalue) }.to_h]
21
+ cf_value =
22
+ if struct.respond_to?(:to_cf)
23
+ struct.to_cf
24
+ else
25
+ struct.map do |subkey, subvalue|
26
+ subprops[subkey.to_s].to_cf(subvalue)
27
+ end.to_h
28
+ end
29
+
30
+ [key, cf_value]
22
31
  end
23
32
 
24
- # true if the value is whitelisted or Hash and all keys are valid for their corresponding props
33
+ # true if the value is whitelisted or Hash and all keys are valid for
34
+ # their corresponding props
25
35
  def valid?(struct)
26
- whitelisted_value?(struct) || (struct.is_a?(Hash) && valid_struct?(struct))
36
+ return true if whitelisted_value?(struct)
37
+ struct.is_a?(Hash) && valid_struct?(struct)
27
38
  end
28
39
 
29
40
  private
30
41
 
31
42
  def after_initialize(substructs)
32
- type = spec['ItemType'] || spec['Type']
33
- @subprops =
34
- substructs.fetch(type, {}).fetch('Properties', {}).map do |key, config|
35
- subprop = config['ItemType'] == type ? self : Props.from(key, config, substructs)
36
- [Utils.underscore(key), subprop]
37
- end.to_h
43
+ @subprops = subprops_from(substructs, spec['ItemType'] || spec['Type'])
44
+ end
45
+
46
+ def subprops_from(substructs, type)
47
+ subprop_names = substructs.fetch(type, {}).fetch('Properties', {})
48
+
49
+ subprop_names.each_with_object({}) do |(key, config), subprops|
50
+ subprop =
51
+ if config['ItemType'] == type
52
+ self
53
+ else
54
+ Props.from(key, config, substructs)
55
+ end
56
+
57
+ subprops[Utils.underscore(key)] = subprop
58
+ end
38
59
  end
39
60
 
40
61
  def valid_struct?(struct)
41
- struct.all? { |key, value| subprops.key?(key.to_s) && subprops[key.to_s].valid?(value) }
62
+ struct.all? do |key, value|
63
+ subprops.key?(key.to_s) && subprops[key.to_s].valid?(value)
64
+ end
42
65
  end
43
66
  end
44
67
  end
@@ -12,7 +12,8 @@ module Humidifier
12
12
  end
13
13
 
14
14
  # builds a prop that is not a List or Map type
15
- # PrimitiveType is one of Boolean, Double, Integer, Json, String, or Timestamp
15
+ # PrimitiveType is one of Boolean, Double, Integer, Json, String, or
16
+ # Timestamp
16
17
  def singular_from(key, spec, substructs)
17
18
  primitive = spec['PrimitiveItemType'] || spec['PrimitiveType']
18
19
 
@@ -1,10 +1,10 @@
1
1
  module Humidifier
2
2
  # Builds CFN references
3
3
  class Ref
4
- attr_accessor :reference
4
+ attr_reader :reference
5
5
 
6
6
  def initialize(reference)
7
- self.reference = reference
7
+ @reference = reference
8
8
  end
9
9
 
10
10
  # Builds CFN syntax
@@ -3,7 +3,8 @@ module Humidifier
3
3
  class Resource
4
4
  # Attributes that are available to every stack
5
5
  COMMON_ATTRIBUTES =
6
- Utils.underscored(%w[Condition CreationPolicy DeletionPolicy DependsOn Metadata UpdatePolicy])
6
+ Utils.underscored(%w[Condition CreationPolicy DeletionPolicy DependsOn
7
+ Metadata UpdatePolicy])
7
8
 
8
9
  attr_accessor :properties, *COMMON_ATTRIBUTES.values
9
10
 
@@ -13,7 +14,8 @@ module Humidifier
13
14
  end
14
15
 
15
16
  # Patches method_missing to include property accessors
16
- # After the first method call, builds the accessor methods to get a speed boost
17
+ # After the first method call, builds the accessor methods to get a speed
18
+ # boost
17
19
  def method_missing(name, *args)
18
20
  if !valid_accessor?(name)
19
21
  super
@@ -33,19 +35,31 @@ module Humidifier
33
35
 
34
36
  # CFN stack syntax
35
37
  def to_cf
36
- props_cf = properties.map { |key, value| self.class.props[key].to_cf(value) }.to_h
37
- { 'Type' => self.class.aws_name, 'Properties' => props_cf }.merge(common_attributes)
38
+ props_cf =
39
+ properties.map do |key, value|
40
+ self.class.props[key].to_cf(value)
41
+ end
42
+
43
+ common_attributes.merge!(
44
+ 'Type' => self.class.aws_name,
45
+ 'Properties' => props_cf.to_h
46
+ )
38
47
  end
39
48
 
40
49
  # Update a set of properties defined by the given properties hash
41
50
  def update(properties, raw = false)
42
- properties.each { |property, value| update_property(property, value, raw) }
51
+ properties.each do |property, value|
52
+ update_property(property, value, raw)
53
+ end
43
54
  end
44
55
 
45
56
  # Update the attributes of the resource defined by COMMON_ATTRIBUTES
46
57
  def update_attributes(attributes)
47
58
  attributes.each do |attribute, value|
48
- raise ArgumentError, "Invalid attribute: #{attribute}" unless COMMON_ATTRIBUTES.value?(attribute)
59
+ unless COMMON_ATTRIBUTES.value?(attribute)
60
+ raise ArgumentError, "Invalid attribute: #{attribute}"
61
+ end
62
+
49
63
  public_send(:"#{attribute}=", value)
50
64
  end
51
65
  end
@@ -96,18 +110,23 @@ module Humidifier
96
110
 
97
111
  def validate_property(property, raw)
98
112
  property = Utils.underscore(property) if raw
113
+
99
114
  unless self.class.prop?(property)
100
- raise ArgumentError, "Attempting to set invalid property for #{self.class.name}: #{property}"
115
+ raise ArgumentError, 'Attempting to set invalid property for ' \
116
+ "#{self.class.name}: #{property}"
101
117
  end
118
+
102
119
  property
103
120
  end
104
121
 
105
122
  def validate_value(property, value, raw)
106
123
  prop = self.class.props[property]
107
124
  value = prop.convert(value) if raw
125
+
108
126
  unless prop.valid?(value)
109
127
  raise ArgumentError, "Invalid value for #{property}: #{value.inspect}"
110
128
  end
129
+
111
130
  value
112
131
  end
113
132
  end
@@ -1,35 +1,38 @@
1
1
  module Humidifier
2
2
  # The payload sent to the shim methods, representing the stack and the options
3
3
  class SdkPayload
4
- # The maximum size a template body can be before it has to be put somewhere and referenced through a URL
4
+ # The maximum size a template body can be before it has to be put somewhere
5
+ # and referenced through a URL
5
6
  MAX_TEMPLATE_BODY_SIZE = 51_200
6
7
 
7
8
  # The maximum size a template body can be inside of an S3 bucket
8
9
  MAX_TEMPLATE_URL_SIZE = 460_800
9
10
 
10
- # The maximum amount of time that Humidifier should wait for a stack to complete a CRUD operation
11
+ # The maximum amount of time that Humidifier should wait for a stack to
12
+ # complete a CRUD operation
11
13
  MAX_WAIT = 600
12
14
 
13
15
  # Thrown when a template is too large to use the template_url option
14
16
  class TemplateTooLargeError < StandardError
15
17
  def initialize(bytesize)
16
- message = <<-MSG
17
- Cannot use a template > #{MAX_TEMPLATE_URL_SIZE} bytes (currently #{bytesize} bytes), consider using nested stacks
18
- (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stack.html)
19
- MSG
18
+ message =
19
+ "Cannot use a template > #{MAX_TEMPLATE_URL_SIZE} bytes " \
20
+ "(currently #{bytesize} bytes), consider using nested stacks " \
21
+ '(http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide' \
22
+ '/aws-properties-stack.html)'
20
23
  super(message)
21
24
  end
22
25
  end
23
26
 
24
- attr_accessor :stack, :options, :max_wait
27
+ attr_reader :stack, :options, :max_wait
25
28
 
26
29
  extend Forwardable
27
30
  def_delegators :stack, :id=, :identifier, :name
28
31
 
29
32
  def initialize(stack, options)
30
- self.stack = stack
31
- self.options = options
32
- self.max_wait = options.delete(:max_wait) || MAX_WAIT
33
+ @stack = stack
34
+ @options = options
35
+ @max_wait = options.delete(:max_wait) || MAX_WAIT
33
36
  end
34
37
 
35
38
  # True if the stack and options are the same as the other (used for testing)
@@ -39,7 +42,7 @@ MSG
39
42
 
40
43
  # Merge in options
41
44
  def merge(new_options)
42
- self.options = new_options.merge(options)
45
+ @options = new_options.merge(options)
43
46
  end
44
47
 
45
48
  # The body of the template
@@ -78,17 +81,19 @@ MSG
78
81
 
79
82
  private
80
83
 
81
- def template_param
82
- @template_param ||= begin
83
- bytesize = template_body.bytesize
84
- raise TemplateTooLargeError, bytesize if bytesize > MAX_TEMPLATE_URL_SIZE
84
+ def bytesize
85
+ template_body.bytesize.tap do |size|
86
+ raise TemplateTooLargeError, size if size > MAX_TEMPLATE_URL_SIZE
87
+ end
88
+ end
85
89
 
90
+ def template_param
91
+ @template_param ||=
86
92
  if bytesize > MAX_TEMPLATE_BODY_SIZE
87
93
  { template_url: AwsShim.upload(self) }
88
94
  else
89
95
  { template_body: template_body }
90
96
  end
91
- end
92
97
  end
93
98
  end
94
99
  end
@@ -1,11 +1,11 @@
1
1
  module Humidifier
2
2
  # Manages waiting for stack events for v1 of the SDK
3
3
  class Sleeper
4
- attr_accessor :attempts
4
+ attr_reader :attempts
5
5
 
6
6
  # Store attempts and wait for the block to return true
7
7
  def initialize(attempts, &block)
8
- self.attempts = attempts
8
+ @attempts = attempts
9
9
  hibernate_until(&block)
10
10
  end
11
11
 
@@ -15,7 +15,7 @@ module Humidifier
15
15
  raise 'Waited too long, giving up' if attempts.zero?
16
16
  return if yield
17
17
 
18
- self.attempts -= 1
18
+ @attempts -= 1
19
19
  sleep 1
20
20
  hibernate_until(&block)
21
21
  end