humidifier 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +88 -0
- data/ext/humidifier/extconf.rb +2 -0
- data/ext/humidifier/humidifier.c +93 -0
- data/ext/humidifier/humidifier.h +14 -0
- data/lib/humidifier/aws_adapters/base.rb +64 -0
- data/lib/humidifier/aws_adapters/noop.rb +18 -0
- data/lib/humidifier/aws_adapters/sdkv1.rb +56 -0
- data/lib/humidifier/aws_adapters/sdkv2.rb +43 -0
- data/lib/humidifier/aws_shim.rb +60 -0
- data/lib/humidifier/condition.rb +17 -0
- data/lib/humidifier/fn.rb +28 -0
- data/lib/humidifier/humidifier.bundle +0 -0
- data/lib/humidifier/loader.rb +53 -0
- data/lib/humidifier/mapping.rb +17 -0
- data/lib/humidifier/output.rb +20 -0
- data/lib/humidifier/parameter.rb +27 -0
- data/lib/humidifier/props.rb +127 -0
- data/lib/humidifier/ref.rb +17 -0
- data/lib/humidifier/resource.rb +108 -0
- data/lib/humidifier/sdk_payload.rb +30 -0
- data/lib/humidifier/serializer.rb +18 -0
- data/lib/humidifier/sleeper.rb +25 -0
- data/lib/humidifier/stack.rb +66 -0
- data/lib/humidifier/utils.rb +21 -0
- data/lib/humidifier/version.rb +4 -0
- data/lib/humidifier.rb +50 -0
- data/specs/ApiGateway-Account.cf +6 -0
- data/specs/ApiGateway-ApiKey.cf +9 -0
- data/specs/ApiGateway-Authorizer.cf +13 -0
- data/specs/ApiGateway-BasePathMapping.cf +9 -0
- data/specs/ApiGateway-ClientCertificate.cf +6 -0
- data/specs/ApiGateway-Deployment.cf +9 -0
- data/specs/ApiGateway-Method.cf +15 -0
- data/specs/ApiGateway-Model.cf +10 -0
- data/specs/ApiGateway-Resource.cf +8 -0
- data/specs/ApiGateway-RestApi.cf +12 -0
- data/specs/ApiGateway-Stage.cf +14 -0
- data/specs/AutoScaling-AutoScalingGroup.cf +21 -0
- data/specs/AutoScaling-LaunchConfiguration.cf +22 -0
- data/specs/AutoScaling-LifecycleHook.cf +12 -0
- data/specs/AutoScaling-ScalingPolicy.cf +14 -0
- data/specs/AutoScaling-ScheduledAction.cf +12 -0
- data/specs/CloudFormation-Authentication.cf +18 -0
- data/specs/CloudFormation-CustomResource.cf +8 -0
- data/specs/CloudFormation-Interface.cf +9 -0
- data/specs/CloudFormation-Stack.cf +10 -0
- data/specs/CloudFormation-WaitCondition.cf +8 -0
- data/specs/CloudFormation-WaitConditionHandle.cf +5 -0
- data/specs/CloudFront-Distribution.cf +6 -0
- data/specs/CloudTrail-Trail.cf +16 -0
- data/specs/CloudWatch-Alarm.cf +20 -0
- data/specs/CodeDeploy-Application.cf +6 -0
- data/specs/CodeDeploy-DeploymentConfig.cf +7 -0
- data/specs/CodeDeploy-DeploymentGroup.cf +13 -0
- data/specs/CodePipeline-CustomActionType.cf +12 -0
- data/specs/CodePipeline-Pipeline.cf +11 -0
- data/specs/Config-ConfigRule.cf +11 -0
- data/specs/Config-ConfigurationRecorder.cf +8 -0
- data/specs/Config-DeliveryChannel.cf +10 -0
- data/specs/DataPipeline-Pipeline.cf +12 -0
- data/specs/DirectoryService-MicrosoftAD.cf +11 -0
- data/specs/DirectoryService-SimpleAD.cf +13 -0
- data/specs/DynamoDB-Table.cf +12 -0
- data/specs/EC2-CustomerGateway.cf +9 -0
- data/specs/EC2-DHCPOptions.cf +11 -0
- data/specs/EC2-EIP.cf +7 -0
- data/specs/EC2-EIPAssociation.cf +10 -0
- data/specs/EC2-FlowLog.cf +10 -0
- data/specs/EC2-Host.cf +8 -0
- data/specs/EC2-Instance.cf +32 -0
- data/specs/EC2-InternetGateway.cf +6 -0
- data/specs/EC2-NatGateway.cf +7 -0
- data/specs/EC2-NetworkAcl.cf +7 -0
- data/specs/EC2-NetworkAclEntry.cf +13 -0
- data/specs/EC2-NetworkInterface.cf +13 -0
- data/specs/EC2-NetworkInterfaceAttachment.cf +9 -0
- data/specs/EC2-PlacementGroup.cf +6 -0
- data/specs/EC2-Route.cf +12 -0
- data/specs/EC2-RouteTable.cf +7 -0
- data/specs/EC2-SecurityGroup.cf +10 -0
- data/specs/EC2-SecurityGroupEgress.cf +11 -0
- data/specs/EC2-SecurityGroupIngress.cf +14 -0
- data/specs/EC2-SpotFleet.cf +6 -0
- data/specs/EC2-Subnet.cf +10 -0
- data/specs/EC2-SubnetNetworkAclAssociation.cf +5 -0
- data/specs/EC2-SubnetRouteTableAssociation.cf +7 -0
- data/specs/EC2-VPC.cf +10 -0
- data/specs/EC2-VPCDHCPOptionsAssociation.cf +7 -0
- data/specs/EC2-VPCEndpoint.cf +9 -0
- data/specs/EC2-VPCGatewayAttachment.cf +8 -0
- data/specs/EC2-VPCPeeringConnection.cf +8 -0
- data/specs/EC2-VPNConnection.cf +10 -0
- data/specs/EC2-VPNConnectionRoute.cf +7 -0
- data/specs/EC2-VPNGateway.cf +7 -0
- data/specs/EC2-VPNGatewayRoutePropagation.cf +7 -0
- data/specs/EC2-Volume.cf +14 -0
- data/specs/EC2-VolumeAttachment.cf +8 -0
- data/specs/ECR-Repository.cf +7 -0
- data/specs/ECS-Cluster.cf +6 -0
- data/specs/ECS-Service.cf +11 -0
- data/specs/ECS-TaskDefinition.cf +7 -0
- data/specs/EFS-FileSystem.cf +6 -0
- data/specs/EFS-MountTarget.cf +9 -0
- data/specs/EMR-Cluster.cf +17 -0
- data/specs/EMR-InstanceGroupConfig.cf +14 -0
- data/specs/EMR-Step.cf +9 -0
- data/specs/ElastiCache-CacheCluster.cf +27 -0
- data/specs/ElastiCache-ParameterGroup.cf +8 -0
- data/specs/ElastiCache-ReplicationGroup.cf +23 -0
- data/specs/ElastiCache-SecurityGroup.cf +7 -0
- data/specs/ElastiCache-SecurityGroupIngress.cf +9 -0
- data/specs/ElastiCache-SubnetGroup.cf +7 -0
- data/specs/ElasticBeanstalk-Application.cf +7 -0
- data/specs/ElasticBeanstalk-ApplicationVersion.cf +8 -0
- data/specs/ElasticBeanstalk-ConfigurationTemplate.cf +11 -0
- data/specs/ElasticBeanstalk-Environment.cf +15 -0
- data/specs/ElasticLoadBalancing-LoadBalancer.cf +21 -0
- data/specs/Elasticsearch-Domain.cf +12 -0
- data/specs/Events-Rule.cf +12 -0
- data/specs/GameLift-Alias.cf +8 -0
- data/specs/GameLift-Build.cf +8 -0
- data/specs/GameLift-Fleet.cf +16 -0
- data/specs/IAM-AccessKey.cf +8 -0
- data/specs/IAM-Group.cf +8 -0
- data/specs/IAM-InstanceProfile.cf +7 -0
- data/specs/IAM-ManagedPolicy.cf +11 -0
- data/specs/IAM-Policy.cf +10 -0
- data/specs/IAM-Role.cf +9 -0
- data/specs/IAM-User.cf +10 -0
- data/specs/IAM-UserToGroupAddition.cf +7 -0
- data/specs/KMS-Key.cf +9 -0
- data/specs/Kinesis-Stream.cf +8 -0
- data/specs/KinesisFirehose-DeliveryStream.cf +9 -0
- data/specs/Lambda-Alias.cf +9 -0
- data/specs/Lambda-EventSourceMapping.cf +10 -0
- data/specs/Lambda-Function.cf +14 -0
- data/specs/Lambda-Permission.cf +10 -0
- data/specs/Lambda-Version.cf +8 -0
- data/specs/Logs-Destination.cf +9 -0
- data/specs/Logs-LogGroup.cf +6 -0
- data/specs/Logs-LogStream.cf +7 -0
- data/specs/Logs-MetricFilter.cf +8 -0
- data/specs/Logs-SubscriptionFilter.cf +9 -0
- data/specs/OpsWorks-App.cf +16 -0
- data/specs/OpsWorks-ElasticLoadBalancerAttachment.cf +7 -0
- data/specs/OpsWorks-Instance.cf +19 -0
- data/specs/OpsWorks-Layer.cf +21 -0
- data/specs/OpsWorks-Stack.cf +23 -0
- data/specs/RDS-DBCluster.cf +23 -0
- data/specs/RDS-DBClusterParameterGroup.cf +9 -0
- data/specs/RDS-DBInstance.cf +38 -0
- data/specs/RDS-DBParameterGroup.cf +9 -0
- data/specs/RDS-DBSecurityGroup.cf +10 -0
- data/specs/RDS-DBSecurityGroupIngress.cf +10 -0
- data/specs/RDS-DBSubnetGroup.cf +8 -0
- data/specs/RDS-EventSubscription.cf +10 -0
- data/specs/RDS-OptionGroup.cf +10 -0
- data/specs/Redshift-Cluster.cf +30 -0
- data/specs/Redshift-ClusterParameterGroup.cf +8 -0
- data/specs/Redshift-ClusterSecurityGroup.cf +6 -0
- data/specs/Redshift-ClusterSecurityGroupIngress.cf +9 -0
- data/specs/Redshift-ClusterSubnetGroup.cf +7 -0
- data/specs/Route53-HealthCheck.cf +7 -0
- data/specs/Route53-HostedZone.cf +9 -0
- data/specs/Route53-RecordSet.cf +19 -0
- data/specs/Route53-RecordSetGroup.cf +9 -0
- data/specs/S3-Bucket.cf +15 -0
- data/specs/S3-BucketPolicy.cf +7 -0
- data/specs/SDB-Domain.cf +6 -0
- data/specs/SNS-Topic.cf +8 -0
- data/specs/SNS-TopicPolicy.cf +8 -0
- data/specs/SQS-Queue.cf +12 -0
- data/specs/SQS-QueuePolicy.cf +7 -0
- data/specs/SSM-Document.cf +6 -0
- data/specs/WAF-ByteMatchSet.cf +7 -0
- data/specs/WAF-IPSet.cf +7 -0
- data/specs/WAF-Rule.cf +8 -0
- data/specs/WAF-SizeConstraintSet.cf +7 -0
- data/specs/WAF-SqlInjectionMatchSet.cf +7 -0
- data/specs/WAF-WebACL.cf +9 -0
- data/specs/WAF-XssMatchSet.cf +7 -0
- data/specs/WorkSpaces-Workspace.cf +11 -0
- metadata +356 -0
@@ -0,0 +1,127 @@
|
|
1
|
+
module Humidifier
|
2
|
+
|
3
|
+
# Container for property of CFN resources
|
4
|
+
module Props
|
5
|
+
|
6
|
+
# Superclass for all CFN properties
|
7
|
+
class Base
|
8
|
+
|
9
|
+
# The list of classes that are valid beyond the normal values for each prop
|
10
|
+
WHITELIST = [Fn, Ref].freeze
|
11
|
+
|
12
|
+
attr_accessor :key
|
13
|
+
attr_reader :value
|
14
|
+
|
15
|
+
def initialize(key = nil)
|
16
|
+
self.key = key
|
17
|
+
end
|
18
|
+
|
19
|
+
# true if the property type knows how to convert its values
|
20
|
+
def convertable?
|
21
|
+
respond_to?(:convert)
|
22
|
+
end
|
23
|
+
|
24
|
+
# the name of the property
|
25
|
+
def name
|
26
|
+
@name ||= Utils.underscore(key)
|
27
|
+
end
|
28
|
+
|
29
|
+
# CFN stack syntax
|
30
|
+
def to_cf(value)
|
31
|
+
[key, Serializer.dump(value)]
|
32
|
+
end
|
33
|
+
|
34
|
+
# true if the given value is of a type contained in the whitelist
|
35
|
+
def whitelisted_value?(value)
|
36
|
+
WHITELIST.any? { |clazz| value.is_a?(clazz) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# An array property
|
41
|
+
class ArrayProp < Base
|
42
|
+
def valid?(value)
|
43
|
+
value.is_a?(Array)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# A boolean property
|
48
|
+
class BooleanProp < Base
|
49
|
+
# converts through value == 'true'
|
50
|
+
def convert(value)
|
51
|
+
if %w[true false].include?(value)
|
52
|
+
puts "WARNING: Property #{name} should be a boolean, not a string"
|
53
|
+
value == 'true'
|
54
|
+
else
|
55
|
+
value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# true if it is a boolean
|
60
|
+
def valid?(value)
|
61
|
+
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# A JSON property (and the default)
|
66
|
+
class JSONProp < Base
|
67
|
+
def valid?(value)
|
68
|
+
whitelisted_value?(value) || value.is_a?(Hash) || value.is_a?(Array)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# An integer property
|
73
|
+
class IntegerProp < Base
|
74
|
+
# converts the value through #to_i unless it is whitelisted
|
75
|
+
def convert(value)
|
76
|
+
puts "WARNING: Property #{name} should be an integer" unless valid?(value)
|
77
|
+
value.to_i
|
78
|
+
end
|
79
|
+
|
80
|
+
# true if it is whitelisted or a Fixnum
|
81
|
+
def valid?(value)
|
82
|
+
whitelisted_value?(value) || value.is_a?(Fixnum)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# A string property
|
87
|
+
class StringProp < Base
|
88
|
+
# converts the value through #to_s unless it is whitelisted
|
89
|
+
def convert(value)
|
90
|
+
puts "WARNING: Property #{name} should be a string" unless valid?(value)
|
91
|
+
whitelisted_value?(value) ? value : value.to_s
|
92
|
+
end
|
93
|
+
|
94
|
+
# true if it is whitelisted or a String
|
95
|
+
def valid?(value)
|
96
|
+
whitelisted_value?(value) || value.is_a?(String)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class << self
|
101
|
+
# builds the appropriate prop object from the given spec line
|
102
|
+
def from(spec_line)
|
103
|
+
key, type = parse(spec_line)
|
104
|
+
|
105
|
+
case type
|
106
|
+
when 'Boolean' then BooleanProp.new(key)
|
107
|
+
when 'Integer' then IntegerProp.new(key)
|
108
|
+
when 'String' then StringProp.new(key)
|
109
|
+
when /\[.*?\]/ then ArrayProp.new(key)
|
110
|
+
else JSONProp.new(key)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# parses a spec line to return a key and type
|
115
|
+
def parse(spec_line)
|
116
|
+
spec_line.strip!
|
117
|
+
|
118
|
+
if spec_line.include?(':')
|
119
|
+
key, type = spec_line.gsub(/,\z/, '').split(': ').map(&:strip)
|
120
|
+
[key[1..-2], type]
|
121
|
+
else
|
122
|
+
[nil, spec_line]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Humidifier
|
2
|
+
|
3
|
+
# Superclass for all AWS resources
|
4
|
+
class Resource
|
5
|
+
|
6
|
+
# Attributes that are available to every stack
|
7
|
+
COMMON_ATTRIBUTES =
|
8
|
+
Utils.underscored(%w[Condition CreationPolicy DeletionPolicy DependsOn Metadata UpdatePolicy])
|
9
|
+
|
10
|
+
attr_accessor :properties, *COMMON_ATTRIBUTES.values
|
11
|
+
|
12
|
+
def initialize(properties = {}, raw = false)
|
13
|
+
self.properties = {}
|
14
|
+
update(properties, raw)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Patches method_missing to include property accessors
|
18
|
+
# After the first method call, builds the accessor methods to get a speed boost
|
19
|
+
def method_missing(name, *args)
|
20
|
+
if !valid_accessor?(name)
|
21
|
+
super
|
22
|
+
elsif self.class.prop?(name.to_s)
|
23
|
+
self.class.build_property_reader(name)
|
24
|
+
send(name)
|
25
|
+
else
|
26
|
+
self.class.build_property_writer(name)
|
27
|
+
send(name, args.first)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Patches respond_to_missing? to include property accessors
|
32
|
+
def respond_to_missing?(name, *)
|
33
|
+
valid_accessor?(name) || super
|
34
|
+
end
|
35
|
+
|
36
|
+
# CFN stack syntax
|
37
|
+
def to_cf
|
38
|
+
props_cf = Utils.enumerable_to_h(properties) { |(key, value)| self.class.props[key].to_cf(value) }
|
39
|
+
{ 'Type' => self.class.aws_name, 'Properties' => props_cf }.merge(common_attributes)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Update a set of properties defined by the given properties hash
|
43
|
+
def update(properties, raw = false)
|
44
|
+
properties.each { |property, value| update_property(property, value, raw) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Update an individual property on this resource
|
48
|
+
def update_property(property, value, raw = false)
|
49
|
+
property = property.to_s
|
50
|
+
property = validate_property(property, raw)
|
51
|
+
value = validate_value(property, value, raw)
|
52
|
+
properties[property] = value
|
53
|
+
end
|
54
|
+
|
55
|
+
class << self
|
56
|
+
attr_accessor :aws_name, :props
|
57
|
+
|
58
|
+
# @private builds a cached method for a property reader
|
59
|
+
def build_property_reader(name)
|
60
|
+
define_method(name) do
|
61
|
+
properties[name.to_s]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @private builds a cached method for a property writer
|
66
|
+
def build_property_writer(name)
|
67
|
+
define_method(name) do |value|
|
68
|
+
update_property(name.to_s[0..-2], value)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# true if this resource has the given property
|
73
|
+
def prop?(prop)
|
74
|
+
props.key?(prop)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def common_attributes
|
81
|
+
COMMON_ATTRIBUTES.each_with_object({}) do |(name, prop), result|
|
82
|
+
value = send(prop)
|
83
|
+
result[name] = value if value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def valid_accessor?(method)
|
88
|
+
(self.class.props.keys & [method.to_s, method.to_s[0..-2]]).any?
|
89
|
+
end
|
90
|
+
|
91
|
+
def validate_property(property, raw)
|
92
|
+
property = Utils.underscore(property) if raw
|
93
|
+
unless self.class.prop?(property)
|
94
|
+
raise ArgumentError, "Attempting to set invalid property for #{self.class.name}: #{property}"
|
95
|
+
end
|
96
|
+
property
|
97
|
+
end
|
98
|
+
|
99
|
+
def validate_value(property, value, raw)
|
100
|
+
prop = self.class.props[property]
|
101
|
+
value = prop.convert(value) if raw && prop.convertable?
|
102
|
+
unless prop.valid?(value)
|
103
|
+
raise ArgumentError, "Invalid value for #{property}: #{value.inspect}"
|
104
|
+
end
|
105
|
+
value
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Humidifier
|
2
|
+
|
3
|
+
# The payload sent to the shim methods, representing the stack and the options
|
4
|
+
class SdkPayload
|
5
|
+
|
6
|
+
# The maximum amount of time that Humidifier should wait for a stack to complete a CRUD operation
|
7
|
+
MAX_WAIT = 180
|
8
|
+
|
9
|
+
attr_accessor :stack, :options, :max_wait
|
10
|
+
|
11
|
+
extend Forwardable
|
12
|
+
def_delegators :stack, :id=, :identifier, :name, :to_cf
|
13
|
+
|
14
|
+
def initialize(stack, options)
|
15
|
+
self.stack = stack
|
16
|
+
self.options = options
|
17
|
+
self.max_wait = options.delete(:max_wait) || MAX_WAIT
|
18
|
+
end
|
19
|
+
|
20
|
+
# True if the stack and options are the same as the other (used for testing)
|
21
|
+
def ==(other)
|
22
|
+
stack == other.stack && options == other.options
|
23
|
+
end
|
24
|
+
|
25
|
+
# Merge in options
|
26
|
+
def merge(new_options)
|
27
|
+
self.options = new_options.merge(options)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Humidifier
|
2
|
+
|
3
|
+
# Dumps an object to CFN syntax
|
4
|
+
class Serializer
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# dumps the given object out to CFN syntax recursively
|
8
|
+
def dump(node)
|
9
|
+
case node
|
10
|
+
when Hash then Utils.enumerable_to_h(node) { |(key, value)| [key, dump(value)] }
|
11
|
+
when Array then node.map { |value| dump(value) }
|
12
|
+
when Ref, Fn then dump(node.to_cf)
|
13
|
+
else node
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Humidifier
|
2
|
+
|
3
|
+
# Manages waiting for stack events for v1 of the SDK
|
4
|
+
class Sleeper
|
5
|
+
|
6
|
+
attr_accessor :attempts
|
7
|
+
|
8
|
+
# Store attempts and wait for the block to return true
|
9
|
+
def initialize(attempts, &block)
|
10
|
+
self.attempts = attempts
|
11
|
+
hibernate_until(&block)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def hibernate_until(&block)
|
17
|
+
raise 'Waited too long, giving up' if attempts.zero?
|
18
|
+
return if yield
|
19
|
+
|
20
|
+
self.attempts -= 1
|
21
|
+
sleep 1
|
22
|
+
hibernate_until(&block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Humidifier
|
2
|
+
|
3
|
+
# Represents a CFN stack
|
4
|
+
class Stack
|
5
|
+
|
6
|
+
# Single settings on the stack
|
7
|
+
STATIC_RESOURCES = Utils.underscored(%w[AWSTemplateFormatVersion Description Metadata])
|
8
|
+
|
9
|
+
# Lists of objects linked to the stack
|
10
|
+
ENUMERABLE_RESOURCES = Utils.underscored(%w[Conditions Mappings Outputs Parameters Resources])
|
11
|
+
|
12
|
+
attr_accessor :id, :name, *ENUMERABLE_RESOURCES.values, *STATIC_RESOURCES.values
|
13
|
+
|
14
|
+
def initialize(opts = {})
|
15
|
+
self.name = opts[:name]
|
16
|
+
self.id = opts[:id]
|
17
|
+
|
18
|
+
ENUMERABLE_RESOURCES.values.each { |prop| send(:"#{prop}=", opts.fetch(prop, {})) }
|
19
|
+
STATIC_RESOURCES.values.each { |prop| send(:"#{prop}=", opts[prop]) }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Add a resource to the stack
|
23
|
+
def add(name, resource)
|
24
|
+
resources[name] = resource
|
25
|
+
end
|
26
|
+
|
27
|
+
# The identifier used by the shim to find the stack in CFN, prefers id to name
|
28
|
+
def identifier
|
29
|
+
id || name
|
30
|
+
end
|
31
|
+
|
32
|
+
# A string representation of the stack that's valid for CFN
|
33
|
+
def to_cf
|
34
|
+
JSON.pretty_generate(enumerable_resources.merge(static_resources))
|
35
|
+
end
|
36
|
+
|
37
|
+
%i[condition mapping output parameter].each do |resource_type|
|
38
|
+
define_method(:"add_#{resource_type}") do |name, opts = {}|
|
39
|
+
send(:"#{resource_type}s")[name] = Humidifier.const_get(resource_type.capitalize).new(opts)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
AwsShim::STACK_METHODS.each do |method|
|
44
|
+
define_method(method) { |opts = {}| AwsShim.send(method, SdkPayload.new(self, opts)) }
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def enumerable_resources
|
50
|
+
ENUMERABLE_RESOURCES.each_with_object({}) do |(name, prop), list|
|
51
|
+
resources = send(prop)
|
52
|
+
next if resources.empty?
|
53
|
+
list[name] = Utils.enumerable_to_h(resources) do |resource_name, resource|
|
54
|
+
[resource_name, resource.to_cf]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def static_resources
|
60
|
+
STATIC_RESOURCES.each_with_object({}) do |(name, prop), list|
|
61
|
+
resource = send(prop)
|
62
|
+
list[name] = resource if resource
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Humidifier
|
2
|
+
|
3
|
+
# Dumps an object to CFN syntax
|
4
|
+
module Utils
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# back-supporting ruby 2.0's lack of Enumerable#to_h
|
8
|
+
def enumerable_to_h(enumerable)
|
9
|
+
enumerable.each_with_object({}) do |item, result|
|
10
|
+
key, value = yield item
|
11
|
+
result[key] = value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# a frozen hash of the given names mapped to their underscored version
|
16
|
+
def underscored(names)
|
17
|
+
names.each_with_object({}) { |name, map| map[name] = underscore(name).to_sym }.freeze
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/humidifier.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'json'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
require 'humidifier/humidifier'
|
6
|
+
require 'humidifier/utils'
|
7
|
+
|
8
|
+
require 'humidifier/fn'
|
9
|
+
require 'humidifier/ref'
|
10
|
+
require 'humidifier/props'
|
11
|
+
|
12
|
+
require 'humidifier/aws_shim'
|
13
|
+
require 'humidifier/condition'
|
14
|
+
require 'humidifier/loader'
|
15
|
+
require 'humidifier/mapping'
|
16
|
+
require 'humidifier/output'
|
17
|
+
require 'humidifier/parameter'
|
18
|
+
require 'humidifier/resource'
|
19
|
+
require 'humidifier/sdk_payload'
|
20
|
+
require 'humidifier/serializer'
|
21
|
+
require 'humidifier/sleeper'
|
22
|
+
require 'humidifier/stack'
|
23
|
+
require 'humidifier/version'
|
24
|
+
|
25
|
+
# container module for all gem classes
|
26
|
+
module Humidifier
|
27
|
+
class << self
|
28
|
+
# convenience method for calling cloudformation functions
|
29
|
+
def fn
|
30
|
+
Fn
|
31
|
+
end
|
32
|
+
|
33
|
+
# convenience method for creating references
|
34
|
+
def ref(reference)
|
35
|
+
Ref.new(reference)
|
36
|
+
end
|
37
|
+
|
38
|
+
# the list of all registered resources
|
39
|
+
def registry
|
40
|
+
@registry ||= {}
|
41
|
+
end
|
42
|
+
|
43
|
+
# convenience method for finding classes by AWS name
|
44
|
+
def [](aws_name)
|
45
|
+
registry[aws_name]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Humidifier::Loader.load
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{
|
2
|
+
"Type" : "AWS::ApiGateway::Authorizer",
|
3
|
+
"Properties" : {
|
4
|
+
"AuthorizerCredentials" : String,
|
5
|
+
"AuthorizerResultTtlInSeconds" : Integer,
|
6
|
+
"AuthorizerUri" : String,
|
7
|
+
"IdentitySource" : String,
|
8
|
+
"IdentityValidationExpression" : String,
|
9
|
+
"Name" : String,
|
10
|
+
"RestApiId" : String,
|
11
|
+
"Type" : String
|
12
|
+
}
|
13
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
"Type" : "AWS::ApiGateway::Method",
|
3
|
+
"Properties" : {
|
4
|
+
"ApiKeyRequired" : Boolean,
|
5
|
+
"AuthorizationType" : String,
|
6
|
+
"AuthorizerId" : String,
|
7
|
+
"HttpMethod" : String,
|
8
|
+
"Integration" : Integration,
|
9
|
+
"MethodResponses" : [ MethodResponse, ... ],
|
10
|
+
"RequestModels" : { String:String, ... },
|
11
|
+
"RequestParameters" : { String:Boolean, ... },
|
12
|
+
"ResourceId" : String,
|
13
|
+
"RestApiId" : String
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"Type" : "AWS::ApiGateway::Stage",
|
3
|
+
"Properties" : {
|
4
|
+
"CacheClusterEnabled" : Boolean,
|
5
|
+
"CacheClusterSize" : String,
|
6
|
+
"ClientCertificateId" : String,
|
7
|
+
"DeploymentId" : String,
|
8
|
+
"Description" : String,
|
9
|
+
"MethodSettings" : [ MethodSetting ],
|
10
|
+
"RestApiId" : String,
|
11
|
+
"StageName" : String,
|
12
|
+
"Variables" : { String:String, ... }
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"Type" : "AWS::AutoScaling::AutoScalingGroup",
|
3
|
+
"Properties" : {
|
4
|
+
"AvailabilityZones" : [ String, ... ],
|
5
|
+
"Cooldown" : String,
|
6
|
+
"DesiredCapacity" : String,
|
7
|
+
"HealthCheckGracePeriod" : Integer,
|
8
|
+
"HealthCheckType" : String,
|
9
|
+
"InstanceId" : String,
|
10
|
+
"LaunchConfigurationName" : String,
|
11
|
+
"LoadBalancerNames" : [ String, ... ],
|
12
|
+
"MaxSize" : String,
|
13
|
+
"MetricsCollection" : [ MetricsCollection, ... ]
|
14
|
+
"MinSize" : String,
|
15
|
+
"NotificationConfigurations" : [ NotificationConfigurations, ... ],
|
16
|
+
"PlacementGroup" : String,
|
17
|
+
"Tags" : [ Auto Scaling Tag, ..., ],
|
18
|
+
"TerminationPolicies" : [ String, ..., ],
|
19
|
+
"VPCZoneIdentifier" : [ String, ... ]
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"Type" : "AWS::AutoScaling::LaunchConfiguration",
|
3
|
+
"Properties" : {
|
4
|
+
"AssociatePublicIpAddress" : Boolean,
|
5
|
+
"BlockDeviceMappings" : [ BlockDeviceMapping, ... ],
|
6
|
+
"ClassicLinkVPCId" : String,
|
7
|
+
"ClassicLinkVPCSecurityGroups" : [ String, ... ],
|
8
|
+
"EbsOptimized" : Boolean,
|
9
|
+
"IamInstanceProfile" : String,
|
10
|
+
"ImageId" : String,
|
11
|
+
"InstanceId" : String,
|
12
|
+
"InstanceMonitoring" : Boolean,
|
13
|
+
"InstanceType" : String,
|
14
|
+
"KernelId" : String,
|
15
|
+
"KeyName" : String,
|
16
|
+
"PlacementTenancy" : String,
|
17
|
+
"RamDiskId" : String,
|
18
|
+
"SecurityGroups" : [ SecurityGroup, ... ],
|
19
|
+
"SpotPrice" : String,
|
20
|
+
"UserData" : String
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
{
|
2
|
+
"Type" : "AWS::AutoScaling::LifecycleHook",
|
3
|
+
"Properties" : {
|
4
|
+
"AutoScalingGroupName" : String,
|
5
|
+
"DefaultResult" : String,
|
6
|
+
"HeartbeatTimeout" : Integer,
|
7
|
+
"LifecycleTransition" : String,
|
8
|
+
"NotificationMetadata" : String,
|
9
|
+
"NotificationTargetARN" : String,
|
10
|
+
"RoleARN" : String
|
11
|
+
}
|
12
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"Type" : "AWS::AutoScaling::ScalingPolicy",
|
3
|
+
"Properties" : {
|
4
|
+
"AdjustmentType" : String,
|
5
|
+
"AutoScalingGroupName" : String,
|
6
|
+
"Cooldown" : String,
|
7
|
+
"EstimatedInstanceWarmup" : Integer,
|
8
|
+
"MetricAggregationType" : String,
|
9
|
+
"MinAdjustmentMagnitude" : Integer,
|
10
|
+
"PolicyType" : String,
|
11
|
+
"ScalingAdjustment" : Integer,
|
12
|
+
"StepAdjustments" : [ StepAdjustments, ... ]
|
13
|
+
}
|
14
|
+
}
|