skywriter 1.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.
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