skywriter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +8 -0
  5. data/CHANGES.md +5 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.md +22 -0
  8. data/README.md +139 -0
  9. data/Rakefile +7 -0
  10. data/lib/skywriter/function.rb +133 -0
  11. data/lib/skywriter/resource/auto_scaling/auto_scaling_group.rb +28 -0
  12. data/lib/skywriter/resource/auto_scaling/launch_configuration.rb +28 -0
  13. data/lib/skywriter/resource/auto_scaling/scaling_policy.rb +18 -0
  14. data/lib/skywriter/resource/auto_scaling/scheduled_action.rb +21 -0
  15. data/lib/skywriter/resource/auto_scaling/trigger.rb +26 -0
  16. data/lib/skywriter/resource/cloud_formation/authentication.rb +46 -0
  17. data/lib/skywriter/resource/cloud_formation/custom_resource.rb +17 -0
  18. data/lib/skywriter/resource/cloud_formation/stack.rb +15 -0
  19. data/lib/skywriter/resource/cloud_formation/wait_condition.rb +15 -0
  20. data/lib/skywriter/resource/cloud_formation/wait_condition_handle.rb +12 -0
  21. data/lib/skywriter/resource/cloud_front/distribution.rb +13 -0
  22. data/lib/skywriter/resource/cloud_watch/alarm.rb +27 -0
  23. data/lib/skywriter/resource/dynamo_db/table.rb +18 -0
  24. data/lib/skywriter/resource/ec2/customer_gateway.rb +16 -0
  25. data/lib/skywriter/resource/ec2/dhcp_options.rb +18 -0
  26. data/lib/skywriter/resource/ec2/eip.rb +14 -0
  27. data/lib/skywriter/resource/ec2/eip_association.rb +17 -0
  28. data/lib/skywriter/resource/ec2/instance.rb +34 -0
  29. data/lib/skywriter/resource/ec2/internet_gateway.rb +13 -0
  30. data/lib/skywriter/resource/ec2/network_acl.rb +14 -0
  31. data/lib/skywriter/resource/ec2/network_acl_entry.rb +20 -0
  32. data/lib/skywriter/resource/ec2/network_interface.rb +20 -0
  33. data/lib/skywriter/resource/ec2/network_interface_attachment.rb +16 -0
  34. data/lib/skywriter/resource/ec2/route.rb +17 -0
  35. data/lib/skywriter/resource/ec2/route_table.rb +14 -0
  36. data/lib/skywriter/resource/ec2/security_group.rb +17 -0
  37. data/lib/skywriter/resource/ec2/security_group_egress.rb +23 -0
  38. data/lib/skywriter/resource/ec2/security_group_ingress.rb +26 -0
  39. data/lib/skywriter/resource/ec2/subnet.rb +16 -0
  40. data/lib/skywriter/resource/ec2/subnet_network_acl_association.rb +14 -0
  41. data/lib/skywriter/resource/ec2/subnet_route_table_association.rb +14 -0
  42. data/lib/skywriter/resource/ec2/volume.rb +18 -0
  43. data/lib/skywriter/resource/ec2/volume_attachment.rb +15 -0
  44. data/lib/skywriter/resource/ec2/vpc.rb +17 -0
  45. data/lib/skywriter/resource/ec2/vpc_dhcp_options_association.rb +14 -0
  46. data/lib/skywriter/resource/ec2/vpc_gateway_attachment.rb +15 -0
  47. data/lib/skywriter/resource/ec2/vpn_connection.rb +17 -0
  48. data/lib/skywriter/resource/ec2/vpn_connection_route.rb +14 -0
  49. data/lib/skywriter/resource/ec2/vpn_gateway.rb +14 -0
  50. data/lib/skywriter/resource/ec2/vpn_gateway_route_propagation.rb +14 -0
  51. data/lib/skywriter/resource/elasti_cache/cache_cluster.rb +27 -0
  52. data/lib/skywriter/resource/elastic_beanstalk/application.rb +14 -0
  53. data/lib/skywriter/resource/elastic_beanstalk/application_version.rb +15 -0
  54. data/lib/skywriter/resource/elastic_beanstalk/configuration_template.rb +18 -0
  55. data/lib/skywriter/resource/elastic_beanstalk/environment.rb +21 -0
  56. data/lib/skywriter/resource/elastic_load_balancing/load_balancer.rb +25 -0
  57. data/lib/skywriter/resource/elasticache/parameter_group.rb +15 -0
  58. data/lib/skywriter/resource/elasticache/security_group.rb +13 -0
  59. data/lib/skywriter/resource/elasticache/security_group_ingress.rb +15 -0
  60. data/lib/skywriter/resource/elasticache/subnet_group.rb +14 -0
  61. data/lib/skywriter/resource/iam/access_key.rb +16 -0
  62. data/lib/skywriter/resource/iam/group.rb +15 -0
  63. data/lib/skywriter/resource/iam/instance_profile.rb +15 -0
  64. data/lib/skywriter/resource/iam/policy.rb +18 -0
  65. data/lib/skywriter/resource/iam/role.rb +13 -0
  66. data/lib/skywriter/resource/iam/user.rb +17 -0
  67. data/lib/skywriter/resource/iam/user_to_group_addition.rb +15 -0
  68. data/lib/skywriter/resource/logical_name_pointer.rb +11 -0
  69. data/lib/skywriter/resource/pointer.rb +23 -0
  70. data/lib/skywriter/resource/rds/db_instance.rb +37 -0
  71. data/lib/skywriter/resource/rds/db_parameter_group.rb +17 -0
  72. data/lib/skywriter/resource/rds/db_security_group.rb +17 -0
  73. data/lib/skywriter/resource/rds/db_security_group_ingress.rb +18 -0
  74. data/lib/skywriter/resource/rds/db_subnet_group.rb +16 -0
  75. data/lib/skywriter/resource/redshift/cluster.rb +37 -0
  76. data/lib/skywriter/resource/redshift/cluster_parameter_group.rb +16 -0
  77. data/lib/skywriter/resource/redshift/cluster_security_group.rb +14 -0
  78. data/lib/skywriter/resource/redshift/cluster_security_group_ingress.rb +17 -0
  79. data/lib/skywriter/resource/redshift/cluster_subnet_group.rb +15 -0
  80. data/lib/skywriter/resource/ref_pointer.rb +11 -0
  81. data/lib/skywriter/resource/route_53/record_set.rb +24 -0
  82. data/lib/skywriter/resource/route_53/record_set_group.rb +17 -0
  83. data/lib/skywriter/resource/s3/bucket.rb +17 -0
  84. data/lib/skywriter/resource/s3/bucket_policy.rb +15 -0
  85. data/lib/skywriter/resource/sdb/domain.rb +13 -0
  86. data/lib/skywriter/resource/sns/topic.rb +16 -0
  87. data/lib/skywriter/resource/sns/topic_policy.rb +15 -0
  88. data/lib/skywriter/resource/sqs/queue.rb +18 -0
  89. data/lib/skywriter/resource/sqs/queue_policy.rb +15 -0
  90. data/lib/skywriter/resource.rb +165 -0
  91. data/lib/skywriter/resource_property/README.md +64 -0
  92. data/lib/skywriter/resource_property/auto_scaling/block_device_mapping.rb +12 -0
  93. data/lib/skywriter/resource_property/auto_scaling/ebs_block_device.rb +17 -0
  94. data/lib/skywriter/resource_property/auto_scaling/notification_configuration.rb +11 -0
  95. data/lib/skywriter/resource_property/auto_scaling/tag.rb +15 -0
  96. data/lib/skywriter/resource_property/cloud_formation/resource_tag.rb +12 -0
  97. data/lib/skywriter/resource_property/cloud_front/cache_behavior.rb +14 -0
  98. data/lib/skywriter/resource_property/cloud_front/custom_origin.rb +11 -0
  99. data/lib/skywriter/resource_property/cloud_front/default_cache_behavior.rb +13 -0
  100. data/lib/skywriter/resource_property/cloud_front/distribution_config.rb +16 -0
  101. data/lib/skywriter/resource_property/cloud_front/forwarded_value.rb +13 -0
  102. data/lib/skywriter/resource_property/cloud_front/logging.rb +10 -0
  103. data/lib/skywriter/resource_property/cloud_front/origin.rb +12 -0
  104. data/lib/skywriter/resource_property/cloud_front/s3_origin.rb +9 -0
  105. data/lib/skywriter/resource_property/cloud_watch/metric_dimension.rb +10 -0
  106. data/lib/skywriter/resource_property/dynamo_db/attribute_definition.rb +12 -0
  107. data/lib/skywriter/resource_property/dynamo_db/global_secondary_index.rb +14 -0
  108. data/lib/skywriter/resource_property/dynamo_db/key_schema.rb +10 -0
  109. data/lib/skywriter/resource_property/dynamo_db/local_secondary_index.rb +13 -0
  110. data/lib/skywriter/resource_property/dynamo_db/projection.rb +12 -0
  111. data/lib/skywriter/resource_property/dynamo_db/provisioned_throughput.rb +10 -0
  112. data/lib/skywriter/resource_property/ec2/block_device_mapping.rb +12 -0
  113. data/lib/skywriter/resource_property/ec2/ebs_block_device.rb +15 -0
  114. data/lib/skywriter/resource_property/ec2/icmp.rb +10 -0
  115. data/lib/skywriter/resource_property/ec2/mount_point.rb +10 -0
  116. data/lib/skywriter/resource_property/ec2/network_interface.rb +18 -0
  117. data/lib/skywriter/resource_property/ec2/network_interface_association.rb +12 -0
  118. data/lib/skywriter/resource_property/ec2/network_interface_attachment.rb +10 -0
  119. data/lib/skywriter/resource_property/ec2/network_interface_group_item.rb +10 -0
  120. data/lib/skywriter/resource_property/ec2/network_interface_private_ip_specification.rb +10 -0
  121. data/lib/skywriter/resource_property/ec2/port_range.rb +10 -0
  122. data/lib/skywriter/resource_property/ec2/security_group_rule.rb +12 -0
  123. data/lib/skywriter/resource_property/ec2/tag.rb +10 -0
  124. data/lib/skywriter/resource_property/elastic_beanstalk/environment_tier.rb +11 -0
  125. data/lib/skywriter/resource_property/elastic_beanstalk/option_setting.rb +13 -0
  126. data/lib/skywriter/resource_property/elastic_beanstalk/source_bundle.rb +10 -0
  127. data/lib/skywriter/resource_property/elastic_beanstalk/source_configuration.rb +10 -0
  128. data/lib/skywriter/resource_property/elastic_load_balancing/app_cookie_stickiness_policy.rb +10 -0
  129. data/lib/skywriter/resource_property/elastic_load_balancing/health_check.rb +13 -0
  130. data/lib/skywriter/resource_property/elastic_load_balancing/lb_cookie_stickiness_policy.rb +10 -0
  131. data/lib/skywriter/resource_property/elastic_load_balancing/listener.rb +14 -0
  132. data/lib/skywriter/resource_property/elastic_load_balancing/policy.rb +13 -0
  133. data/lib/skywriter/resource_property/rds/security_group_rule.rb +12 -0
  134. data/lib/skywriter/resource_property/redshift/parameter.rb +10 -0
  135. data/lib/skywriter/resource_property/route53/alias_target.rb +10 -0
  136. data/lib/skywriter/resource_property/s3/website_configuration_policy.rb +16 -0
  137. data/lib/skywriter/resource_property/sns/subscription.rb +10 -0
  138. data/lib/skywriter/resource_property/sqs/redrive_policy.rb +10 -0
  139. data/lib/skywriter/resource_property.rb +57 -0
  140. data/lib/skywriter/template.rb +143 -0
  141. data/lib/skywriter/version.rb +3 -0
  142. data/lib/skywriter.rb +8 -0
  143. data/skywriter.gemspec +29 -0
  144. data/spec/skywriter/function_spec.rb +99 -0
  145. data/spec/skywriter/resource/cloud_formation/authentication_spec.rb +36 -0
  146. data/spec/skywriter/resource/cloud_formation/custom_resource_spec.rb +30 -0
  147. data/spec/skywriter/resource/logical_name_pointer_spec.rb +15 -0
  148. data/spec/skywriter/resource/ref_pointer_spec.rb +15 -0
  149. data/spec/skywriter/resource_property/ec2/security_group_rule_spec.rb +7 -0
  150. data/spec/skywriter/resource_property_spec.rb +153 -0
  151. data/spec/skywriter/resource_spec.rb +236 -0
  152. data/spec/skywriter/template_spec.rb +246 -0
  153. data/spec/spec_helper.rb +29 -0
  154. metadata +306 -0
@@ -0,0 +1,11 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module ElasticBeanstalk
4
+ class EnvironmentTier < Skywriter::ResourceProperty
5
+ property :Name
6
+ property :Type
7
+ property :Version
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module ElasticBeanstalk
4
+ class OptionSetting < Skywriter::ResourceProperty
5
+ property :Namespace
6
+ property :OptionName
7
+ property :Value
8
+ end
9
+
10
+ OptionSettings = OptionSetting
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module ElasticBeanstalk
4
+ class SourceBundle < Skywriter::ResourceProperty
5
+ property :S3Bucket
6
+ property :S3Key
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module ElasticBeanstalk
4
+ class SourceConfiguration < Skywriter::ResourceProperty
5
+ property :ApplicationName
6
+ property :TemplateName
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module ElasticLoadBalancing
4
+ class AppCookieStickinessPolicy < Skywriter::ResourceProperty
5
+ property :CookieName
6
+ property :PolicyName
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module ElasticLoadBalancing
4
+ class HealthCheck < Skywriter::ResourceProperty
5
+ property :HealthyThreshold
6
+ property :Interval
7
+ property :Target
8
+ property :Timeout
9
+ property :UnhealthyThreshold
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module ElasticLoadBalancing
4
+ class LBCookieStickinessPolicy < Skywriter::ResourceProperty
5
+ property :CookieExpirationPeriod
6
+ property :PolicyName
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module ElasticLoadBalancing
4
+ class Listener < Skywriter::ResourceProperty
5
+ property :InstancePort
6
+ property :InstanceProtocol
7
+ property :LoadBalancerPort
8
+ property :PolicyNames
9
+ property :Protocol
10
+ property :SSLCertificateId
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module ElasticLoadBalancing
4
+ class Policy < Skywriter::ResourceProperty
5
+ property :Attributes
6
+ property :InstancePorts
7
+ property :LoadBalancerPorts
8
+ property :PolicyName
9
+ property :PolicyType
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module RDS
4
+ class SecurityGroupRule < Skywriter::ResourceProperty
5
+ property :CIDRIP
6
+ property :EC2SecurityGroupId
7
+ property :EC2SecurityGroupName
8
+ property :EC2SecurityGroupOwnerId
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module Redshift
4
+ class Parameter < Skywriter::ResourceProperty
5
+ property :ParameterName
6
+ property :ParameterValue
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module Route53
4
+ class AliasTarget < Skywriter::ResourceProperty
5
+ property :HostedZoneId
6
+ property :DNSName
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module S3
4
+ class WebsiteConfiguration < Skywriter::ResourceProperty
5
+ property :IndexDocument
6
+ property :ErrorDocument
7
+ end
8
+ end
9
+
10
+ # This attribute looks like it should be in the 'CloudFormation' namespace, but it's actually
11
+ # and embedded property of a resource in the 'S3' namespace
12
+ module CloudFormation
13
+ WebsiteConfiguration = ::Skywriter::ResourceProperty::S3::WebsiteConfiguration
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module SNS
4
+ class Subscription < Skywriter::ResourceProperty
5
+ property :Endpoint
6
+ property :Protocol
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ module SQS
4
+ class RedrivePolicy < Skywriter::ResourceProperty
5
+ property :deadLetterTargetArn
6
+ property :maxReceiveCount
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,57 @@
1
+ module Skywriter
2
+ class ResourceProperty
3
+ # Declare a ResourceProperty property
4
+ #
5
+ # @param name [Symbol] The property name as it exists in the AWS documentation
6
+ def self.property(name)
7
+ property_definitions << PropertyDefinition.new(name)
8
+ end
9
+
10
+ # @param [Hash] options Options hash. Valid values depend on the implementing class - see the AWS documentation
11
+ # at http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-product-property-reference.html
12
+ # for details
13
+ def initialize(options = {})
14
+ @options = options.freeze
15
+ end
16
+
17
+ # Returns a JSON-able hash representing the Resource
18
+ #
19
+ # @return [Hash] A JSON-able hash
20
+ #
21
+ def as_json(*)
22
+ @as_json ||= property_definitions.each_with_object({}) do |property_definition, hash|
23
+ if (value = property_value(property_definition))
24
+ hash[property_definition.name] = value
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :options
32
+
33
+ def self.property_definitions
34
+ @property_definitions ||= []
35
+ end
36
+
37
+ def property_value(property_definition)
38
+ options[property_definition.key.to_sym] ||
39
+ options[property_definition.key.to_s] ||
40
+ options[property_definition.name.to_sym] ||
41
+ options[property_definition.name.to_s]
42
+ end
43
+
44
+ def property_definitions
45
+ self.class.property_definitions
46
+ end
47
+ end
48
+
49
+ class PropertyDefinition
50
+ attr_reader :name, :key
51
+
52
+ def initialize(name)
53
+ @name = name.to_s
54
+ @key = name.to_s.underscore.to_sym
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,143 @@
1
+ module Skywriter
2
+ # CloudFormation Template
3
+ #
4
+ # Represents a CloudFormation template.
5
+ #
6
+ # @example
7
+ # parameters = {"foo" => "bar"}
8
+ # webserver = Skywriter::Resource::EC2::Instance.new('MyWebserver')
9
+ # load_balancer = Skywriter::Resource::ElasticLoadBalancing::LoadBalancer.new('MyLoadBalancer')
10
+ #
11
+ # template = Skywriter::Template.new(
12
+ # description: "My Template",
13
+ # parameters: parameters,
14
+ # resources: [webserver, load_balancer]
15
+ # )
16
+ #
17
+ # AWS::CloudFormation.new.stacks.create('my-stack', template.to_json)
18
+ #
19
+ class Template
20
+ class MergeError < StandardError; end
21
+
22
+ attr_reader :format_version, :description
23
+ attr_reader :parameters, :mappings, :conditions, :resources, :outputs
24
+
25
+ # Constructor
26
+ #
27
+ # See the AWS Documentation for details on the meaning of these parameters:
28
+ # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/concept-template.html
29
+ #
30
+ # @param format_version [String] The AWS CloudFormation template version
31
+ # @param description [String] The stack description
32
+ # @param parameters [Hash] A hash of parameters
33
+ # @param mappings [Hash] A hash of mappings
34
+ # @param conditions [Hash] A hash of condition
35
+ # @param resources [Hash, Array<Skywriter::Resource>] Either a hash of
36
+ # resources or an array of objects whose classwhich include
37
+ # Skywriter::Resource, for example Skywriter::Resource::EC2::Instance.
38
+ # @param outputs [Hash] A hash of outputs
39
+ def initialize(options = {})
40
+ @format_version = (options[:format_version] || '2010-09-09').freeze
41
+ @description = options[:description].freeze
42
+
43
+ @parameters = (access_liberally(options, :parameters) || {}).freeze
44
+ @mappings = (access_liberally(options, :mappings) || {}).freeze
45
+ @conditions = (access_liberally(options, :conditions) || {}).freeze
46
+ @resources = resources_as_json(access_liberally(options, :resources)).freeze
47
+ @outputs = (access_liberally(options, :outputs) || {}).freeze
48
+ end
49
+
50
+ # Returns a JSON string representing the Template
51
+ #
52
+ # @return [String] A JSON string
53
+ #
54
+ def to_json(*)
55
+ JSON.pretty_generate(as_json)
56
+ end
57
+
58
+ # Returns a hash representing the Template
59
+ #
60
+ # @return [Hash] A JSON-able hash
61
+ #
62
+ def as_json(*)
63
+ {
64
+ 'FormatVersion' => format_version,
65
+ 'Description' => description,
66
+ 'Parameters' => parameters,
67
+ 'Mappings' => mappings,
68
+ 'Conditions' => conditions,
69
+ 'Resources' => resources,
70
+ 'Outputs' => outputs,
71
+ }.reject { |key, value| value.nil? }
72
+ end
73
+
74
+ def merge(other)
75
+ dup.merge!(other)
76
+ end
77
+
78
+ protected
79
+
80
+ def merge!(other)
81
+ raise ArgumentError, "Don't know how to merge with a #{other.class.name}" unless other.kind_of?(Template)
82
+
83
+ merge_value!(:format_version, other.format_version)
84
+ merge_value!(:description, other.description)
85
+
86
+ merge_hash!(:parameters, other.parameters)
87
+ merge_hash!(:mappings, other.mappings)
88
+ merge_hash!(:conditions, other.conditions)
89
+ merge_hash!(:resources, other.resources)
90
+ merge_hash!(:outputs, other.outputs)
91
+
92
+ self
93
+ end
94
+
95
+ private
96
+
97
+ def access_liberally(object, key)
98
+ object[key.to_sym] ||
99
+ object[key.to_s] ||
100
+ object[key.to_s.camelcase.to_sym] ||
101
+ object[key.to_s.camelcase.to_s]
102
+ end
103
+
104
+ def merge_value!(attribute, other_value)
105
+ self_value = send(attribute)
106
+
107
+ raise MergeError, "Duplicate attribute '#{attribute}' found" if self_value && (self_value != other_value)
108
+
109
+ instance_variable_set("@#{attribute}", other_value)
110
+ end
111
+
112
+ def merge_hash!(attribute, other_value)
113
+ self_value = send(attribute)
114
+
115
+ new_hash = self_value.merge(other_value) do |key, old, new|
116
+ raise MergeError, "Duplicate key '#{key}' found with different value in attribute '#{attribute}'" if old != new
117
+ old
118
+ end.freeze
119
+
120
+ instance_variable_set("@#{attribute}", new_hash)
121
+ end
122
+
123
+ def resources_as_json(resources_option = nil)
124
+ case resources_option
125
+ when Array
126
+ merge_disjoint_keys_and_duplicated_values(resources_option.map(&:as_json))
127
+ when Hash
128
+ resources_option.as_json
129
+ else
130
+ {}
131
+ end
132
+ end
133
+
134
+ def merge_disjoint_keys_and_duplicated_values(hashes)
135
+ hashes.reduce({}) do |memo, hash|
136
+ memo.merge(hash) do |key, old, new|
137
+ raise MergeError, "Duplicate key '#{key}' has different values" unless old == new
138
+ old
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,3 @@
1
+ module Skywriter
2
+ VERSION = "1.0.0"
3
+ end
data/lib/skywriter.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "require_all"
2
+ require "active_support/inflector"
3
+ require "active_support/json"
4
+
5
+ require_all File.expand_path('../../lib', __FILE__)
6
+
7
+ module Skywriter
8
+ end
data/skywriter.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'skywriter/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "skywriter"
8
+ spec.version = Skywriter::VERSION
9
+ spec.authors = ["Ryan Michael", "Ben Hamill"]
10
+ spec.email = ["ryanmichael@otherinbox.com", "ben@context.io"]
11
+ spec.summary = %q{Writes cloud formations}
12
+ spec.description = %q{Writes AWS CloudFormation templates.}
13
+ spec.homepage = "https://github.com/otherinbox/skywriter#readme"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "require_all", "~> 1.3"
22
+ spec.add_dependency "activesupport", "~> 4.0"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.5"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec", "~> 2.14"
27
+ spec.add_development_dependency "pry"
28
+ spec.add_development_dependency "fuubar"
29
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe Skywriter::Function do
4
+ describe "#ref" do
5
+ it "returns expected value" do
6
+ expect(Skywriter::Function.ref('value')).to eq("Ref" => 'value')
7
+ end
8
+ end
9
+
10
+ describe "#base64" do
11
+ it "returns expected value" do
12
+ expect(Skywriter::Function.base64('value')).to eq("Fn::Base64" => 'value')
13
+ end
14
+ end
15
+
16
+ describe "#find_in_map" do
17
+ it "returns expected value" do
18
+ expect(Skywriter::Function.find_in_map("MapName", "TopLevelKey", "SecondLevelKey")).to eq(
19
+ "Fn::FindInMap" => ["MapName", "TopLevelKey", "SecondLevelKey"]
20
+ )
21
+ end
22
+ end
23
+
24
+ describe "#get_att" do
25
+ it "returns expected value" do
26
+ expect(Skywriter::Function.get_att('resource', 'attribute')).to eq(
27
+ "Fn::GetAtt" => ['resource', 'attribute']
28
+ )
29
+ end
30
+ end
31
+
32
+ describe "#get_azs" do
33
+ it "returns expected value" do
34
+ expect(Skywriter::Function.get_azs('region')).to eq("Fn::GetAZs" => "region")
35
+ end
36
+ end
37
+
38
+ describe "#join" do
39
+ it "returns expected value" do
40
+ expect(Skywriter::Function.join('delimiter', 'value1', 'value2')).to eq(
41
+ "Fn::Join" => ["delimiter", ['value1', 'value2']]
42
+ )
43
+ end
44
+ end
45
+
46
+ describe "#select" do
47
+ it "returns expected value" do
48
+ expect(Skywriter::Function.select('1', 'apples', 'grapes', 'oranges', 'mangoes')).to eq(
49
+ "Fn::Select" => [ "1", [ "apples", "grapes", "oranges", "mangoes" ] ]
50
+ )
51
+ end
52
+
53
+ it "converts numeric index to string" do
54
+ expect(Skywriter::Function.select(1, 'apples', 'grapes', 'oranges', 'mangoes')).to eq(
55
+ "Fn::Select" => [ "1", [ "apples", "grapes", "oranges", "mangoes" ] ]
56
+ )
57
+ end
58
+ end
59
+
60
+ describe "#and" do
61
+ it "returns expected value" do
62
+ expect(Skywriter::Function.and('one', 'two')).to eq(
63
+ "Fn::And" => ['one', 'two']
64
+ )
65
+ end
66
+ end
67
+
68
+ describe "#equals" do
69
+ it "returns expected value" do
70
+ expect(Skywriter::Function.equals('value_1', 'value_2')).to eq(
71
+ "Fn::Equals" => ["value_1", "value_2"]
72
+ )
73
+ end
74
+ end
75
+
76
+ describe "#if" do
77
+ it "returns expected value" do
78
+ expect(Skywriter::Function.if('condition', 'when_true', 'when_false')).to eq(
79
+ "Fn::If" => ['condition', 'when_true', 'when_false']
80
+ )
81
+ end
82
+ end
83
+
84
+ describe "#not" do
85
+ it "returns expected value" do
86
+ expect(Skywriter::Function.not('condition')).to eq(
87
+ "Fn::Not" => ['condition']
88
+ )
89
+ end
90
+ end
91
+
92
+ describe "#or" do
93
+ it "returns expected value" do
94
+ expect(Skywriter::Function.or('condition 1', 'condition 2')).to eq(
95
+ "Fn::Or" => ['condition 1', 'condition 2']
96
+ )
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Skywriter::Resource::CloudFormation::Authentication do
4
+ it "is a Resource" do
5
+ expect(Skywriter::Resource::CloudFormation::Authentication.new('name')).to be_a(Skywriter::Resource)
6
+ end
7
+
8
+ describe "#as_json" do
9
+ it "allows the user to define the keyspace" do
10
+ resource = Skywriter::Resource::CloudFormation::Authentication.new(
11
+ 'name',
12
+ Foo: {
13
+ access_key_id: 'some kind of key'
14
+ },
15
+ Bar: {
16
+ access_key_id: 'some other kind of key'
17
+ }
18
+ )
19
+
20
+ expect(resource.as_json).to eq(
21
+ {
22
+ 'name' => {
23
+ 'Type' => "AWS::CloudFormation::Authentication",
24
+ 'Foo' => {
25
+ 'accessKeyId' => 'some kind of key',
26
+ },
27
+ 'Bar' => {
28
+ 'accessKeyId' => 'some other kind of key',
29
+ },
30
+ 'DependsOn' => [],
31
+ }
32
+ }
33
+ )
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Skywriter::Resource::CloudFormation::CustomResource do
4
+ it "is a Resource" do
5
+ expect(Skywriter::Resource::CloudFormation::CustomResource.new('name')).to be_a(Skywriter::Resource)
6
+ end
7
+
8
+ describe "#as_json" do
9
+ it "allows the user to define the keyspace" do
10
+ resource = Skywriter::Resource::CloudFormation::CustomResource.new(
11
+ 'name',
12
+ Foo: 'some foo value',
13
+ Bar: { 'SomeKey' => 'some value' },
14
+ )
15
+
16
+ expect(resource.as_json).to eq(
17
+ {
18
+ 'name' => {
19
+ 'Type' => "AWS::CloudFormation::CustomResource",
20
+ 'Properties' => {
21
+ 'Foo' => 'some foo value',
22
+ 'Bar' => { 'SomeKey' => 'some value' },
23
+ },
24
+ 'DependsOn' => [],
25
+ }
26
+ }
27
+ )
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Skywriter::Resource::LogicalNamePointer do
4
+ let(:resource) { double("Resource", logical_name: "logical name") }
5
+
6
+ let(:pointer) do
7
+ Skywriter::Resource::LogicalNamePointer.new(resource)
8
+ end
9
+
10
+ describe "#as_json" do
11
+ it "returns the logical name" do
12
+ expect(pointer.as_json).to eq("logical name")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Skywriter::Resource::RefPointer do
4
+ let(:resource) { double("Resource", logical_name: "logical name") }
5
+
6
+ let(:pointer) do
7
+ Skywriter::Resource::RefPointer.new(resource)
8
+ end
9
+
10
+ describe "#as_json" do
11
+ it "returns Ref" do
12
+ expect(pointer.as_json).to eq("Ref" => "logical name")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Skywriter::ResourceProperty::EC2::SecurityGroupRule do
4
+ it "is a ResourceProperty" do
5
+ expect(Skywriter::ResourceProperty::EC2::SecurityGroupRule.new).to be_a(Skywriter::ResourceProperty)
6
+ end
7
+ end