humidifier 1.8.0 → 1.9.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|