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.
- checksums.yaml +4 -4
- data/CloudFormationResourceSpecification.json +2324 -2025
- data/lib/humidifier/aws_adapters/base.rb +4 -2
- data/lib/humidifier/aws_adapters/noop.rb +2 -1
- data/lib/humidifier/aws_adapters/sdkv1.rb +17 -5
- data/lib/humidifier/aws_adapters/sdkv2.rb +16 -4
- data/lib/humidifier/aws_shim.rb +2 -2
- data/lib/humidifier/condition.rb +2 -2
- data/lib/humidifier/configuration.rb +6 -4
- data/lib/humidifier/fn.rb +7 -5
- data/lib/humidifier/loader.rb +11 -5
- data/lib/humidifier/mapping.rb +2 -2
- data/lib/humidifier/parameter.rb +10 -5
- data/lib/humidifier/props/base.rb +4 -2
- data/lib/humidifier/props/boolean_prop.rb +2 -1
- data/lib/humidifier/props/list_prop.rb +12 -3
- data/lib/humidifier/props/map_prop.rb +14 -3
- data/lib/humidifier/props/structure_prop.rb +33 -10
- data/lib/humidifier/props.rb +2 -1
- data/lib/humidifier/ref.rb +2 -2
- data/lib/humidifier/resource.rb +26 -7
- data/lib/humidifier/sdk_payload.rb +21 -16
- data/lib/humidifier/sleeper.rb +3 -3
- data/lib/humidifier/stack.rb +25 -12
- data/lib/humidifier/version.rb +1 -1
- data/lib/humidifier.rb +3 -3
- metadata +4 -4
@@ -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
|
-
|
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 ||=
|
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
|
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)
|
29
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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(
|
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
|
-
|
43
|
-
|
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
|
data/lib/humidifier/aws_shim.rb
CHANGED
@@ -17,13 +17,13 @@ module Humidifier
|
|
17
17
|
create_change_set deploy_change_set
|
18
18
|
].freeze
|
19
19
|
|
20
|
-
|
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
|
-
|
26
|
+
@shim =
|
27
27
|
if Humidifier.config.sdk_version_1?
|
28
28
|
AwsAdapters::SDKV1.new
|
29
29
|
elsif Humidifier.config.sdk_version_2?
|
data/lib/humidifier/condition.rb
CHANGED
@@ -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,
|
8
|
-
template_url option instead. You can configure
|
9
|
-
on the top-level
|
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
|
-
|
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
|
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
|
-
|
10
|
+
attr_reader :name, :value
|
10
11
|
|
11
12
|
def initialize(name, value)
|
12
|
-
|
13
|
-
|
13
|
+
@name = "Fn::#{name}"
|
14
|
+
@value = value
|
14
15
|
end
|
15
16
|
|
16
17
|
# CFN stack syntax
|
17
18
|
def to_cf
|
18
|
-
|
19
|
+
cf_value = value.respond_to?(:cf) ? value.to_cf : value
|
20
|
+
{ name => cf_value }
|
19
21
|
end
|
20
22
|
|
21
23
|
class << self
|
data/lib/humidifier/loader.rb
CHANGED
@@ -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('..', '..', '..',
|
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
|
-
|
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
|
41
|
-
structs
|
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
|
-
|
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
|
data/lib/humidifier/mapping.rb
CHANGED
data/lib/humidifier/parameter.rb
CHANGED
@@ -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 =
|
6
|
-
|
5
|
+
PROPERTIES =
|
6
|
+
Utils.underscored(%w[AllowedPattern AllowedValues ConstraintDescription
|
7
|
+
Default Description MaxLength MaxValue MinLength
|
8
|
+
MinValue NoEcho])
|
7
9
|
|
8
|
-
|
10
|
+
attr_reader :type, *PROPERTIES.values
|
9
11
|
|
10
12
|
def initialize(opts = {})
|
11
|
-
PROPERTIES.each_value
|
12
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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?
|
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
|
data/lib/humidifier/props.rb
CHANGED
@@ -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
|
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
|
|
data/lib/humidifier/ref.rb
CHANGED
data/lib/humidifier/resource.rb
CHANGED
@@ -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
|
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
|
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 =
|
37
|
-
|
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
|
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
|
-
|
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,
|
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
|
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
|
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 =
|
17
|
-
Cannot use a template > #{MAX_TEMPLATE_URL_SIZE} bytes
|
18
|
-
(
|
19
|
-
|
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
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
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
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
data/lib/humidifier/sleeper.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
18
|
+
@attempts -= 1
|
19
19
|
sleep 1
|
20
20
|
hibernate_until(&block)
|
21
21
|
end
|