humidifier 2.15.0 → 3.0.0
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 +24305 -18109
- data/LICENSE +1 -1
- data/README.md +55 -33
- data/lib/humidifier/condition.rb +0 -2
- data/lib/humidifier/config.rb +46 -0
- data/lib/humidifier/fn.rb +7 -8
- data/lib/humidifier/loader.rb +37 -45
- data/lib/humidifier/mapping.rb +0 -2
- data/lib/humidifier/output.rb +4 -6
- data/lib/humidifier/parameter.rb +9 -10
- data/lib/humidifier/props.rb +177 -2
- data/lib/humidifier/resource.rb +13 -14
- data/lib/humidifier/stack.rb +166 -21
- data/lib/humidifier/version.rb +1 -1
- data/lib/humidifier.rb +31 -24
- metadata +67 -31
- data/lib/humidifier/aws_adapters/base.rb +0 -67
- data/lib/humidifier/aws_adapters/noop.rb +0 -25
- data/lib/humidifier/aws_adapters/sdkv1.rb +0 -75
- data/lib/humidifier/aws_adapters/sdkv2.rb +0 -61
- data/lib/humidifier/aws_adapters/sdkv3.rb +0 -31
- data/lib/humidifier/aws_shim.rb +0 -83
- data/lib/humidifier/configuration.rb +0 -69
- data/lib/humidifier/props/base.rb +0 -47
- data/lib/humidifier/props/boolean_prop.rb +0 -25
- data/lib/humidifier/props/double_prop.rb +0 -23
- data/lib/humidifier/props/integer_prop.rb +0 -23
- data/lib/humidifier/props/json_prop.rb +0 -31
- data/lib/humidifier/props/list_prop.rb +0 -42
- data/lib/humidifier/props/map_prop.rb +0 -46
- data/lib/humidifier/props/string_prop.rb +0 -23
- data/lib/humidifier/props/structure_prop.rb +0 -71
- data/lib/humidifier/props/timestamp_prop.rb +0 -23
- data/lib/humidifier/sdk_payload.rb +0 -122
- data/lib/humidifier/sleeper.rb +0 -25
- data/lib/humidifier/utils.rb +0 -19
@@ -1,75 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module AwsAdapters
|
5
|
-
# The adapter for v1 of aws-sdk
|
6
|
-
class SDKV1 < Base
|
7
|
-
# Cannot create change sets in V1
|
8
|
-
def create_change_set(*)
|
9
|
-
unsupported('create change set')
|
10
|
-
end
|
11
|
-
|
12
|
-
# Cannot deploy change sets in V1
|
13
|
-
def deploy_change_set(*)
|
14
|
-
unsupported('deploy change set')
|
15
|
-
end
|
16
|
-
|
17
|
-
# True if the stack exists in CFN
|
18
|
-
def exists?(payload)
|
19
|
-
base_module::CloudFormation::Stack.new(payload.identifier).exists?
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def base_module
|
25
|
-
AWS
|
26
|
-
end
|
27
|
-
|
28
|
-
def failure_event_filtered?(event)
|
29
|
-
!event.resource_status.include?('FAILED') ||
|
30
|
-
!event.key?(:resource_status_reason)
|
31
|
-
end
|
32
|
-
|
33
|
-
def handle_failure(payload)
|
34
|
-
reasons = []
|
35
|
-
response = client.describe_stack_events(stack_name: payload.identifier)
|
36
|
-
|
37
|
-
response.stack_events.each do |event|
|
38
|
-
next if failure_event_filtered?(event)
|
39
|
-
|
40
|
-
reasons.unshift(event.resource_status_reason)
|
41
|
-
end
|
42
|
-
|
43
|
-
raise "#{payload.name} stack failed:\n#{reasons.join("\n")}"
|
44
|
-
end
|
45
|
-
|
46
|
-
def perform_and_wait(method, payload)
|
47
|
-
response = public_send(method, payload)
|
48
|
-
|
49
|
-
aws_stack = nil
|
50
|
-
Sleeper.new(payload.max_wait) do
|
51
|
-
response = client.describe_stacks(stack_name: payload.identifier)
|
52
|
-
aws_stack = response.stacks.first
|
53
|
-
!aws_stack.stack_status.end_with?('IN_PROGRESS')
|
54
|
-
end
|
55
|
-
|
56
|
-
handle_failure(payload) if aws_stack.stack_status =~ /(FAILED|ROLLBACK)/
|
57
|
-
response
|
58
|
-
end
|
59
|
-
|
60
|
-
def upload_object(payload, key)
|
61
|
-
base_module.config(region: AwsShim::REGION)
|
62
|
-
@s3 ||= base_module::S3.new
|
63
|
-
|
64
|
-
objects = @s3.buckets[Humidifier.config.s3_bucket].objects
|
65
|
-
objects.create(key, payload.template_body).url_for(:read)
|
66
|
-
end
|
67
|
-
|
68
|
-
def unsupported(method)
|
69
|
-
puts "WARNING: Cannot #{method} because that functionality is not " \
|
70
|
-
'supported in V1 of aws-sdk.'
|
71
|
-
false
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module AwsAdapters
|
5
|
-
# The adapter for v2 of aws-sdk
|
6
|
-
class SDKV2 < Base
|
7
|
-
# Format of the timestamp used in changeset naming
|
8
|
-
TIME_FORMAT = '%Y-%m-%d-%H-%M-%S'
|
9
|
-
|
10
|
-
# Create a change set in CFN
|
11
|
-
def create_change_set(payload)
|
12
|
-
change_set_name = "changeset-#{Time.now.strftime(TIME_FORMAT)}"
|
13
|
-
payload.merge(change_set_name: change_set_name)
|
14
|
-
try_valid { client.create_change_set(payload.create_change_set_params) }
|
15
|
-
end
|
16
|
-
|
17
|
-
# Create a change set if the stack exists, otherwise create the stack
|
18
|
-
def deploy_change_set(payload)
|
19
|
-
exists?(payload) ? create_change_set(payload) : create(payload)
|
20
|
-
end
|
21
|
-
|
22
|
-
# True if the stack exists in CFN
|
23
|
-
def exists?(payload)
|
24
|
-
base_module::CloudFormation::Stack.new(name: payload.identifier).exists?
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def base_module
|
30
|
-
Aws
|
31
|
-
end
|
32
|
-
|
33
|
-
def perform_and_wait(method, payload)
|
34
|
-
method = exists?(payload) ? :update : :create if method == :deploy
|
35
|
-
response = public_send(method, payload)
|
36
|
-
signal = :"stack_#{method}_complete"
|
37
|
-
|
38
|
-
client.wait_until(signal, stack_name: payload.identifier) do |waiter|
|
39
|
-
waiter.max_attempts = payload.max_wait / 5
|
40
|
-
waiter.delay = 5
|
41
|
-
end
|
42
|
-
|
43
|
-
response
|
44
|
-
end
|
45
|
-
|
46
|
-
def upload_object(payload, key)
|
47
|
-
base_module.config.update(region: AwsShim::REGION)
|
48
|
-
@s3_client ||= base_module::S3::Client.new
|
49
|
-
|
50
|
-
@s3_client.put_object(
|
51
|
-
body: payload.template_body,
|
52
|
-
bucket: Humidifier.config.s3_bucket,
|
53
|
-
key: key
|
54
|
-
)
|
55
|
-
|
56
|
-
object = base_module::S3::Object.new(Humidifier.config.s3_bucket, key)
|
57
|
-
object.presigned_url(:get)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module AwsAdapters
|
5
|
-
# The adapter for v3 of aws-sdk
|
6
|
-
class SDKV3 < SDKV2
|
7
|
-
# The notice to add the `aws-sdk-s3` gem when it is needed.
|
8
|
-
S3_SDK_MESSAGE = <<-MSG
|
9
|
-
The AWS SDK for versions 3+ have broken out individual AWS modules into their
|
10
|
-
own gems. Since the stack that you're attempting to use is large enough that it
|
11
|
-
needs to be uploaded to S3, humidifier needs to load the S3 SDK. Please make
|
12
|
-
sure that the 'aws-sdk-s3' gem is available in your load path.
|
13
|
-
MSG
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def upload_object(payload, key)
|
18
|
-
raise S3_SDK_MESSAGE unless s3_sdk_loaded?
|
19
|
-
|
20
|
-
super
|
21
|
-
end
|
22
|
-
|
23
|
-
def s3_sdk_loaded?
|
24
|
-
require 'aws-sdk-s3'
|
25
|
-
true
|
26
|
-
rescue LoadError
|
27
|
-
false
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
data/lib/humidifier/aws_shim.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'humidifier/aws_adapters/base'
|
4
|
-
require 'humidifier/aws_adapters/noop'
|
5
|
-
require 'humidifier/aws_adapters/sdkv1'
|
6
|
-
require 'humidifier/aws_adapters/sdkv2'
|
7
|
-
require 'humidifier/aws_adapters/sdkv3'
|
8
|
-
|
9
|
-
module Humidifier
|
10
|
-
# Optionally provides aws-sdk functionality if the gem is loaded
|
11
|
-
class AwsShim
|
12
|
-
# The AWS region, can be set through the environment, defaults to us-east-1
|
13
|
-
REGION = ENV['AWS_REGION'] || 'us-east-1'
|
14
|
-
|
15
|
-
# Methods that are sent over to the aws adapter from the stack
|
16
|
-
STACK_METHODS = %i[
|
17
|
-
create delete deploy exists? update upload valid?
|
18
|
-
create_and_wait delete_and_wait deploy_and_wait update_and_wait
|
19
|
-
create_change_set deploy_change_set
|
20
|
-
].freeze
|
21
|
-
|
22
|
-
attr_reader :shim
|
23
|
-
|
24
|
-
# Either set the SDK based on the configured option or guess the SDK
|
25
|
-
# version by attempting to require both aws-sdk-v1 and aws-sdk, then setting
|
26
|
-
# the shim based on what successfully loaded
|
27
|
-
def initialize
|
28
|
-
@shim =
|
29
|
-
if Humidifier.config.sdk_version_1?
|
30
|
-
AwsAdapters::SDKV1.new
|
31
|
-
elsif Humidifier.config.sdk_version_2?
|
32
|
-
AwsAdapters::SDKV2.new
|
33
|
-
elsif Humidifier.config.sdk_version_3?
|
34
|
-
AwsAdapters::SDKV3.new
|
35
|
-
else
|
36
|
-
guess_sdk
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class << self
|
41
|
-
extend Forwardable
|
42
|
-
def_delegators :shim, *STACK_METHODS
|
43
|
-
|
44
|
-
# The shim singleton
|
45
|
-
def instance
|
46
|
-
@instance ||= new
|
47
|
-
end
|
48
|
-
|
49
|
-
# The target of all of the forwarding
|
50
|
-
def shim
|
51
|
-
instance.shim
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
def guess_sdk
|
58
|
-
try_requiring_sdks
|
59
|
-
|
60
|
-
if defined?(Aws) && Aws::CORE_GEM_VERSION[0] == '3'
|
61
|
-
AwsAdapters::SDKV3.new
|
62
|
-
elsif defined?(Aws)
|
63
|
-
AwsAdapters::SDKV2.new
|
64
|
-
elsif Object.const_defined?(:AWS)
|
65
|
-
AwsAdapters::SDKV1.new
|
66
|
-
else
|
67
|
-
AwsAdapters::Noop.new
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def try_require_sdk(name)
|
72
|
-
require name || true
|
73
|
-
rescue LoadError
|
74
|
-
false
|
75
|
-
end
|
76
|
-
|
77
|
-
def try_requiring_sdks
|
78
|
-
try_require_sdk('aws-sdk-cloudformation') ||
|
79
|
-
try_require_sdk('aws-sdk') ||
|
80
|
-
try_require_sdk('aws-sdk-v1')
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
# a container for user params
|
5
|
-
class Configuration
|
6
|
-
# The message that gets displayed when the stack body is too large to use
|
7
|
-
# the template_body option
|
8
|
-
UPLOAD_MESSAGE = <<-MSG
|
9
|
-
The %<identifier>s stack's body is too large to be use the template_body option,
|
10
|
-
and therefore must use the template_url option instead. You can configure
|
11
|
-
Humidifier to do this automatically by setting up the s3 config on the top-level
|
12
|
-
Humidifier object like so:
|
13
|
-
|
14
|
-
Humidifier.configure do |config|
|
15
|
-
config.s3_bucket = 'my.s3.bucket'
|
16
|
-
config.s3_prefix = 'my-prefix/' # optional
|
17
|
-
end
|
18
|
-
MSG
|
19
|
-
|
20
|
-
# If true, always upload the CloudFormation template to the configured S3
|
21
|
-
# destination. A useful option if you're going to be deploying multiple
|
22
|
-
# copies of a template or you just generally want a backup.
|
23
|
-
attr_accessor :force_upload
|
24
|
-
|
25
|
-
# The S3 bucket to which to deploy CloudFormation templates when
|
26
|
-
# `always_upload` is set to true or the template is too big for a string
|
27
|
-
# literal.
|
28
|
-
attr_accessor :s3_bucket
|
29
|
-
|
30
|
-
# An optional prefix for the stack names.
|
31
|
-
attr_accessor :s3_prefix
|
32
|
-
|
33
|
-
# By default, `humidifier` will attempt to determine which SDK you have
|
34
|
-
# loaded. (There's not really a story for peer dependencies with bundler).
|
35
|
-
# If you want to enforce a specific version (for instance if you have both
|
36
|
-
# `aws-sdk-v1` and `aws-sdk` but want to use the former) you can set this
|
37
|
-
# variable to `1`.
|
38
|
-
attr_accessor :sdk_version
|
39
|
-
|
40
|
-
def initialize(opts = {})
|
41
|
-
@force_upload = opts[:force_upload]
|
42
|
-
@s3_bucket = opts[:s3_bucket]
|
43
|
-
@s3_prefix = opts[:s3_prefix]
|
44
|
-
@sdk_version = opts[:sdk_version]
|
45
|
-
end
|
46
|
-
|
47
|
-
# raise an error unless the s3_bucket field is set
|
48
|
-
def ensure_upload_configured!(payload)
|
49
|
-
return if s3_bucket
|
50
|
-
|
51
|
-
raise UPLOAD_MESSAGE.gsub('%<identifier>s', payload.identifier)
|
52
|
-
end
|
53
|
-
|
54
|
-
# true if the sdk_version option is set to 1 or '1'
|
55
|
-
def sdk_version_1?
|
56
|
-
sdk_version.to_s == '1'
|
57
|
-
end
|
58
|
-
|
59
|
-
# true if the sdk_version option is set to 2 or '2'
|
60
|
-
def sdk_version_2?
|
61
|
-
sdk_version.to_s == '2'
|
62
|
-
end
|
63
|
-
|
64
|
-
# true if the sdk_version option is set to 3 or '3'
|
65
|
-
def sdk_version_3?
|
66
|
-
sdk_version.to_s == '3'
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module Props
|
5
|
-
# Superclass for all CFN properties
|
6
|
-
class Base
|
7
|
-
# The list of classes that are valid beyond the normal values for each
|
8
|
-
# prop
|
9
|
-
WHITELIST = [Fn, Ref].freeze
|
10
|
-
|
11
|
-
attr_reader :key, :name, :spec, :substructs
|
12
|
-
|
13
|
-
def initialize(key, spec = {}, substructs = {})
|
14
|
-
@key = key
|
15
|
-
@name = Utils.underscore(key)
|
16
|
-
@spec = spec
|
17
|
-
after_initialize(substructs) if respond_to?(:after_initialize, true)
|
18
|
-
end
|
19
|
-
|
20
|
-
# the link to the AWS docs
|
21
|
-
def documentation
|
22
|
-
spec['Documentation']
|
23
|
-
end
|
24
|
-
|
25
|
-
# true if this property is required by the resource
|
26
|
-
def required?
|
27
|
-
spec['Required']
|
28
|
-
end
|
29
|
-
|
30
|
-
# CFN stack syntax
|
31
|
-
def to_cf(value)
|
32
|
-
[key, Serializer.dump(value)]
|
33
|
-
end
|
34
|
-
|
35
|
-
# the type of update that occurs when this property is updated on its
|
36
|
-
# associated resource
|
37
|
-
def update_type
|
38
|
-
spec['UpdateType']
|
39
|
-
end
|
40
|
-
|
41
|
-
# true if the given value is of a type contained in the whitelist
|
42
|
-
def whitelisted_value?(value)
|
43
|
-
WHITELIST.any? { |clazz| value.is_a?(clazz) }
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module Props
|
5
|
-
# A boolean property
|
6
|
-
class BooleanProp < Base
|
7
|
-
# converts the value through `value == 'true'` unless it is valid
|
8
|
-
def convert(value)
|
9
|
-
if valid?(value) || !%w[true false].include?(value)
|
10
|
-
value
|
11
|
-
else
|
12
|
-
puts "WARNING: Property #{name} should be a boolean, not a string"
|
13
|
-
value == 'true'
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# true if it is a boolean
|
18
|
-
def valid?(value)
|
19
|
-
return true if whitelisted_value?(value)
|
20
|
-
|
21
|
-
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module Props
|
5
|
-
# A double property
|
6
|
-
class DoubleProp < Base
|
7
|
-
# converts the value through #to_f unless it is valid
|
8
|
-
def convert(value)
|
9
|
-
if valid?(value) || !value.respond_to?(:to_f)
|
10
|
-
value
|
11
|
-
else
|
12
|
-
puts "WARNING: Property #{name} should be a double"
|
13
|
-
value.to_f
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# true if it is whitelisted, an Integer, or a Float
|
18
|
-
def valid?(value)
|
19
|
-
whitelisted_value?(value) || value.is_a?(Integer) || value.is_a?(Float)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module Props
|
5
|
-
# An integer property
|
6
|
-
class IntegerProp < Base
|
7
|
-
# converts the value through #to_i unless it is valid
|
8
|
-
def convert(value)
|
9
|
-
if valid?(value) || !value.respond_to?(:to_i)
|
10
|
-
value
|
11
|
-
else
|
12
|
-
puts "WARNING: Property #{name} should be an integer"
|
13
|
-
value.to_i
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# true if it is whitelisted or a Integer
|
18
|
-
def valid?(value)
|
19
|
-
whitelisted_value?(value) || value.is_a?(Integer)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module Props
|
5
|
-
# A Json property
|
6
|
-
class JsonProp < Base
|
7
|
-
# converts the value through `Hash[value]` unless it is valid
|
8
|
-
def convert(value)
|
9
|
-
if valid?(value) || !convertable?(value)
|
10
|
-
value
|
11
|
-
else
|
12
|
-
puts "WARNING: Property #{name} should be a Hash"
|
13
|
-
Hash[value]
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# true if the value is whitelisted, a Hash, or an Array
|
18
|
-
def valid?(value)
|
19
|
-
whitelisted_value?(value) || value.is_a?(Hash)
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def convertable?(value)
|
25
|
-
Hash[value]
|
26
|
-
rescue ArgumentError
|
27
|
-
false
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module Props
|
5
|
-
# A property that is contained in a list
|
6
|
-
class ListProp < Base
|
7
|
-
attr_reader :subprop
|
8
|
-
|
9
|
-
# converts the value through mapping using the subprop unless it is valid
|
10
|
-
def convert(list)
|
11
|
-
valid?(list) ? list : list.map { |value| subprop.convert(value) }
|
12
|
-
end
|
13
|
-
|
14
|
-
# CFN stack syntax
|
15
|
-
def to_cf(list)
|
16
|
-
cf_value =
|
17
|
-
if list.respond_to?(:to_cf)
|
18
|
-
list.to_cf
|
19
|
-
else
|
20
|
-
list.map { |value| subprop.to_cf(value).last }
|
21
|
-
end
|
22
|
-
|
23
|
-
[key, cf_value]
|
24
|
-
end
|
25
|
-
|
26
|
-
# Valid if the value is whitelisted or every value in the list is valid
|
27
|
-
# on the subprop
|
28
|
-
def valid?(list)
|
29
|
-
return true if whitelisted_value?(list)
|
30
|
-
|
31
|
-
list.is_a?(Enumerable) && list.all? { |value| subprop.valid?(value) }
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
# Finds the subprop that's specified in the spec
|
37
|
-
def after_initialize(substructs)
|
38
|
-
@subprop = Props.singular_from(key, spec, substructs)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module Props
|
5
|
-
# A property that is contained in a Map
|
6
|
-
class MapProp < Base
|
7
|
-
attr_reader :subprop
|
8
|
-
|
9
|
-
# converts the value through mapping using the subprop unless it is valid
|
10
|
-
def convert(map)
|
11
|
-
return map if valid?(map)
|
12
|
-
|
13
|
-
map.map { |key, value| [key, subprop.convert(value)] }.to_h
|
14
|
-
end
|
15
|
-
|
16
|
-
# CFN stack syntax
|
17
|
-
def to_cf(map)
|
18
|
-
cf_value =
|
19
|
-
if map.respond_to?(:to_cf)
|
20
|
-
map.to_cf
|
21
|
-
else
|
22
|
-
map.each_with_object({}) do |(subkey, subvalue), serialized|
|
23
|
-
serialized[subkey] = subprop.to_cf(subvalue).last
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
[key, cf_value]
|
28
|
-
end
|
29
|
-
|
30
|
-
# Valid if the value is whitelisted or every value in the map is valid on
|
31
|
-
# the subprop
|
32
|
-
def valid?(map)
|
33
|
-
return true if whitelisted_value?(map)
|
34
|
-
|
35
|
-
map.is_a?(Hash) && map.values.all? { |value| subprop.valid?(value) }
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
# Finds the subprop that's specified in the spec
|
41
|
-
def after_initialize(substructs)
|
42
|
-
@subprop = Props.singular_from(key, spec, substructs)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module Props
|
5
|
-
# A string property
|
6
|
-
class StringProp < Base
|
7
|
-
# converts the value through #to_s unless it is valid
|
8
|
-
def convert(value)
|
9
|
-
if valid?(value)
|
10
|
-
value
|
11
|
-
else
|
12
|
-
puts "WARNING: Property #{name} should be a string"
|
13
|
-
value.to_s
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# true if it is whitelisted or a String
|
18
|
-
def valid?(value)
|
19
|
-
whitelisted_value?(value) || value.is_a?(String)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module Props
|
5
|
-
# A structure property that references a structure from the specification
|
6
|
-
class StructureProp < Base
|
7
|
-
attr_reader :subprops
|
8
|
-
|
9
|
-
# converts the value through mapping using the subprop unless it is valid
|
10
|
-
def convert(struct)
|
11
|
-
if valid?(struct)
|
12
|
-
struct
|
13
|
-
else
|
14
|
-
struct.map do |subkey, subvalue|
|
15
|
-
subkey = Utils.underscore(subkey.to_s)
|
16
|
-
[subkey.to_sym, subprops[subkey].convert(subvalue)]
|
17
|
-
end.to_h
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# CFN stack syntax
|
22
|
-
def to_cf(struct)
|
23
|
-
cf_value =
|
24
|
-
if struct.respond_to?(:to_cf)
|
25
|
-
struct.to_cf
|
26
|
-
else
|
27
|
-
struct.map do |subkey, subvalue|
|
28
|
-
subprops[subkey.to_s].to_cf(subvalue)
|
29
|
-
end.to_h
|
30
|
-
end
|
31
|
-
|
32
|
-
[key, cf_value]
|
33
|
-
end
|
34
|
-
|
35
|
-
# true if the value is whitelisted or Hash and all keys are valid for
|
36
|
-
# their corresponding props
|
37
|
-
def valid?(struct)
|
38
|
-
return true if whitelisted_value?(struct)
|
39
|
-
|
40
|
-
struct.is_a?(Hash) && valid_struct?(struct)
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def after_initialize(substructs)
|
46
|
-
@subprops = subprops_from(substructs, spec['ItemType'] || spec['Type'])
|
47
|
-
end
|
48
|
-
|
49
|
-
def subprops_from(substructs, type)
|
50
|
-
subprop_names = substructs.fetch(type, {}).fetch('Properties', {})
|
51
|
-
|
52
|
-
subprop_names.each_with_object({}) do |(key, config), subprops|
|
53
|
-
subprop =
|
54
|
-
if config['ItemType'] == type
|
55
|
-
self
|
56
|
-
else
|
57
|
-
Props.from(key, config, substructs)
|
58
|
-
end
|
59
|
-
|
60
|
-
subprops[Utils.underscore(key)] = subprop
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def valid_struct?(struct)
|
65
|
-
struct.all? do |key, value|
|
66
|
-
subprops.key?(key.to_s) && subprops[key.to_s].valid?(value)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Humidifier
|
4
|
-
module Props
|
5
|
-
# A timestamp (ISO 8601) property
|
6
|
-
class TimestampProp < Base
|
7
|
-
# converts the value through DateTime.parse(value) unless it is valid
|
8
|
-
def convert(value)
|
9
|
-
if valid?(value) || !value.is_a?(String)
|
10
|
-
value
|
11
|
-
else
|
12
|
-
puts "WARNING: Property #{name} should be a Date or Time"
|
13
|
-
DateTime.parse(value)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# true if it is whitelisted, a Time, or a Date
|
18
|
-
def valid?(value)
|
19
|
-
whitelisted_value?(value) || value.is_a?(Time) || value.is_a?(Date)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|