convection 0.0.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +26 -8
  4. data/.rubocop_todo.yml +77 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +10 -0
  7. data/Gemfile +9 -0
  8. data/README.md +27 -2
  9. data/Rakefile +11 -1
  10. data/bin/convection +49 -0
  11. data/convection.gemspec +5 -7
  12. data/example/.ruby-version +1 -0
  13. data/example/Cloudfile +13 -0
  14. data/example/deprecated/elb.rb +27 -0
  15. data/example/deprecated/iam_access_key.rb +18 -0
  16. data/example/deprecated/iam_group.rb +31 -0
  17. data/example/{iam_role.rb → deprecated/iam_role.rb} +21 -32
  18. data/example/deprecated/iam_user.rb +31 -0
  19. data/example/deprecated/rds.rb +70 -0
  20. data/example/{s3.rb → deprecated/s3.rb} +0 -0
  21. data/example/deprecated/sqs.rb +32 -0
  22. data/example/deprecated/vpc.rb +85 -0
  23. data/example/foobar.rb +22 -0
  24. data/example/output/vpc.json +335 -0
  25. data/example/security-groups.rb +40 -0
  26. data/example/trust_cloudtrail.rb +24 -0
  27. data/example/vpc.rb +63 -81
  28. data/ext/resource_generator.sh +21 -0
  29. data/lib/convection.rb +5 -4
  30. data/lib/convection/control/cloud.rb +59 -0
  31. data/lib/convection/control/stack.rb +261 -60
  32. data/lib/convection/dsl/helpers.rb +63 -5
  33. data/lib/convection/model/attributes.rb +60 -0
  34. data/lib/convection/model/cloudfile.rb +58 -0
  35. data/lib/convection/model/diff.rb +39 -0
  36. data/lib/convection/model/event.rb +62 -0
  37. data/lib/convection/model/exceptions.rb +18 -0
  38. data/lib/convection/model/mixin/cidr_block.rb +4 -4
  39. data/lib/convection/model/mixin/colorize.rb +20 -0
  40. data/lib/convection/model/mixin/conditional.rb +1 -3
  41. data/lib/convection/model/mixin/policy.rb +89 -0
  42. data/lib/convection/model/mixin/protocol.rb +29 -0
  43. data/lib/convection/model/mixin/taggable.rb +2 -2
  44. data/lib/convection/model/template.rb +248 -21
  45. data/lib/convection/model/template/condition.rb +56 -0
  46. data/lib/convection/model/template/mapping.rb +4 -3
  47. data/lib/convection/model/template/output.rb +9 -7
  48. data/lib/convection/model/template/parameter.rb +19 -4
  49. data/lib/convection/model/template/resource.rb +317 -23
  50. data/lib/convection/model/template/resource/aws_auto_scaling_auto_scaling_group.rb +39 -0
  51. data/lib/convection/model/template/resource/aws_auto_scaling_launch_configuration.rb +30 -0
  52. data/lib/convection/model/template/resource/aws_auto_scaling_scaling_policy.rb +20 -0
  53. data/lib/convection/model/template/resource/aws_cloud_watch_alarm.rb +31 -0
  54. data/lib/convection/model/template/resource/aws_ec2_instance.rb +10 -46
  55. data/lib/convection/model/template/resource/aws_ec2_internet_gateway.rb +3 -14
  56. data/lib/convection/model/template/resource/aws_ec2_network_acl.rb +45 -0
  57. data/lib/convection/model/template/resource/aws_ec2_network_acl_entry.rb +27 -0
  58. data/lib/convection/model/template/resource/aws_ec2_route.rb +7 -40
  59. data/lib/convection/model/template/resource/aws_ec2_route_table.rb +2 -17
  60. data/lib/convection/model/template/resource/aws_ec2_security_group.rb +24 -30
  61. data/lib/convection/model/template/resource/aws_ec2_security_group_ingres.rb +25 -0
  62. data/lib/convection/model/template/resource/aws_ec2_subnet.rb +21 -28
  63. data/lib/convection/model/template/resource/aws_ec2_subnet_network_acl_association.rb +18 -0
  64. data/lib/convection/model/template/resource/aws_ec2_subnet_route_table_association.rb +3 -24
  65. data/lib/convection/model/template/resource/aws_ec2_vpc.rb +20 -22
  66. data/lib/convection/model/template/resource/aws_ec2_vpc_gateway_attachment.rb +4 -28
  67. data/lib/convection/model/template/resource/aws_elasticache_cluster.rb +24 -0
  68. data/lib/convection/model/template/resource/aws_elasticache_parameter_group.rb +19 -0
  69. data/lib/convection/model/template/resource/aws_elasticache_security_group.rb +17 -0
  70. data/lib/convection/model/template/resource/aws_elasticache_security_group_ingress.rb +19 -0
  71. data/lib/convection/model/template/resource/aws_elb.rb +39 -0
  72. data/lib/convection/model/template/resource/aws_iam_access_key.rb +19 -0
  73. data/lib/convection/model/template/resource/aws_iam_group.rb +18 -0
  74. data/lib/convection/model/template/resource/aws_iam_instance_profile.rb +21 -0
  75. data/lib/convection/model/template/resource/aws_iam_policy.rb +28 -24
  76. data/lib/convection/model/template/resource/aws_iam_role.rb +88 -19
  77. data/lib/convection/model/template/resource/aws_iam_user.rb +53 -0
  78. data/lib/convection/model/template/resource/aws_logs_loggroup.rb +33 -0
  79. data/lib/convection/model/template/resource/aws_rds_db_instance.rb +59 -0
  80. data/lib/convection/model/template/resource/aws_rds_db_parameter_group.rb +27 -0
  81. data/lib/convection/model/template/resource/aws_rds_db_security_group.rb +40 -0
  82. data/lib/convection/model/template/resource/aws_rds_db_subnet_group.rb +26 -0
  83. data/lib/convection/model/template/resource/aws_route53_health_check.rb +17 -0
  84. data/lib/convection/model/template/resource/aws_route53_recordset.rb +30 -0
  85. data/lib/convection/model/template/resource/aws_s3_bucket.rb +8 -44
  86. data/lib/convection/model/template/resource/aws_s3_bucket_policy.rb +14 -19
  87. data/lib/convection/model/template/resource/aws_sns_topic.rb +19 -0
  88. data/lib/convection/model/template/resource/aws_sqs_queue.rb +31 -0
  89. data/lib/convection/model/template/resource/aws_sqs_queue_policy.rb +18 -0
  90. data/test/convection/model/test_conditions.rb +121 -0
  91. data/test/convection/model/test_elasticache.rb +97 -0
  92. data/test/convection/model/test_loggroups.rb +25 -0
  93. data/test/convection/model/test_rds.rb +76 -0
  94. data/test/convection/model/test_template.rb +64 -0
  95. data/test/convection/model/test_validation.rb +216 -0
  96. data/test/test_helper.rb +17 -0
  97. metadata +131 -50
@@ -0,0 +1,17 @@
1
+ require_relative '../resource'
2
+
3
+ module Convection
4
+ module Model
5
+ class Template
6
+ class Resource
7
+ ##
8
+ # AWS::Route53::HealthCheck
9
+ ##
10
+ class Route53HealthCheck < Resource
11
+ type 'AWS::Route53::HealthCheck', :route53_healthcheck
12
+ property :health_check_config, 'HealthCheckConfig'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ require_relative '../resource'
2
+
3
+ module Convection
4
+ module Model
5
+ class Template
6
+ class Resource
7
+ ##
8
+ # AWS::EC2::Instance
9
+ ##
10
+ class Route53RecordSet < Resource
11
+ type 'AWS::Route53::RecordSet', :route53_recordset
12
+ property :alias_target, 'AliasTarget'
13
+ property :comment, 'Comment'
14
+ property :failover, 'Failover'
15
+ property :geo_location, 'GeoLocation', :array
16
+ property :health_check_id, 'HealthCheckId'
17
+ property :zone, 'HostedZoneId'
18
+ property :zone_name, 'HostedZoneName'
19
+ property :record_name, 'Name'
20
+ property :region, 'Region'
21
+ property :record, 'ResourceRecords', :array
22
+ property :set_identifier, 'SetIdentifier'
23
+ property :ttl, 'TTL'
24
+ property :record_type, 'Type'
25
+ property :weight, 'Weight'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -10,38 +10,14 @@ module Convection
10
10
  class S3Bucket < Resource
11
11
  include Model::Mixin::Taggable
12
12
 
13
- def initialize(*args)
14
- super
15
- type 'AWS::S3::Bucket'
16
- end
17
-
18
- def access_control(value)
19
- property('AccessControl', value)
20
- end
21
-
22
- def bucket_name(value)
23
- property('BucketName', value)
24
- end
25
-
26
- def cors_configuration(value)
27
- property('CorsConfiguration', value)
28
- end
29
-
30
- def lifecycle_configuration(value)
31
- property('LifecycleConfiguration', value)
32
- end
33
-
34
- def logging_configuration(value)
35
- property('LoggingConfiguration', value)
36
- end
37
-
38
- def notification_configuration(value)
39
- property('NotificationConfiguration', value)
40
- end
41
-
42
- def version_configuration(value)
43
- property('VersionConfiguration', value)
44
- end
13
+ type 'AWS::S3::Bucket'
14
+ property :bucket_name, 'BucketName'
15
+ property :access_control, 'AccessControl'
16
+ property :cors_configurationm, 'CorsConfiguration'
17
+ property :lifecycle_configuration, 'LifecycleConfiguration'
18
+ property :logging_configuration, 'LoggingConfiguration'
19
+ property :notification_configuration, 'NotificationConfiguration'
20
+ property :version_configuration, 'VersionConfiguration'
45
21
 
46
22
  def render(*args)
47
23
  super.tap do |resource|
@@ -52,16 +28,4 @@ module Convection
52
28
  end
53
29
  end
54
30
  end
55
-
56
- module DSL
57
- ## Add DSL method to template namespace
58
- module Template
59
- def s3_bucket(name, &block)
60
- r = Model::Template::Resource::S3Bucket.new(name, self)
61
-
62
- r.instance_exec(&block) if block
63
- resources[name] = r
64
- end
65
- end
66
- end
67
31
  end
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require_relative '../resource'
2
3
 
3
4
  module Convection
@@ -8,33 +9,27 @@ module Convection
8
9
  # AWS::S3::BucketPolicy
9
10
  ##
10
11
  class S3BucketPolicy < Resource
12
+ extend Forwardable
13
+
14
+ type 'AWS::S3::BucketPolicy'
15
+ property :bucket, 'Bucket'
16
+ attr_reader :document # , 'PolicyDocument'
17
+
18
+ def_delegators :@document, :allow, :id, :version, :statement
19
+ def_delegator :@document, :name, :policy_name
11
20
 
12
21
  def initialize(*args)
13
22
  super
14
- type 'AWS::S3::BucketPolicy'
23
+ @document = Model::Mixin::Policy.new(:name => false, :template => @template)
15
24
  end
16
25
 
17
- def bucket(value)
18
- property('Bucket', value)
19
- end
20
-
21
- def policy_document(value)
22
- property('PolicyDocument', value)
26
+ def render
27
+ super.tap do |r|
28
+ document.render(r['Properties'])
29
+ end
23
30
  end
24
31
  end
25
32
  end
26
33
  end
27
34
  end
28
-
29
- module DSL
30
- ## Add DSL method to template namespace
31
- module Template
32
- def s3_bucket_policy(name, &block)
33
- r = Model::Template::Resource::S3BucketPolicy.new(name, self)
34
-
35
- r.instance_exec(&block) if block
36
- resources[name] = r
37
- end
38
- end
39
- end
40
35
  end
@@ -0,0 +1,19 @@
1
+ require_relative '../resource'
2
+
3
+ module Convection
4
+ module Model
5
+ class Template
6
+ class Resource
7
+ ##
8
+ # AWS::SNS::Topic
9
+ ##
10
+ class SNSTopic < Resource
11
+ type 'AWS::SNS::Topic'
12
+ property :display_name, 'DisplayName'
13
+ property :subscription, 'Subscription', :type => :list
14
+ property :topic_name, 'TopicName'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ require_relative '../resource'
2
+
3
+ module Convection
4
+ module Model
5
+ class Template
6
+ class Resource
7
+ ##
8
+ # AWS::SQS::Queue
9
+ ##
10
+ class SQSQueue < Resource
11
+ include Model::Mixin::Taggable
12
+
13
+ type 'AWS::SQS::Queue'
14
+ property :delay_seconds, 'DelaySeconds'
15
+ property :maximum_message_size, 'MaximumMessageSize'
16
+ property :message_retention_period, 'MessageRetentionPeriod'
17
+ property :queue_name, 'QueueName'
18
+ property :receive_message_wait_time_seconds, 'ReceiveMessageWaitTimeSeconds'
19
+ property :redrive_policy, 'RedrivePolicy'
20
+ property :visibility_timeout, 'VisibilityTimeout'
21
+
22
+ def render(*args)
23
+ super.tap do |resource|
24
+ render_tags(resource)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../resource'
2
+
3
+ module Convection
4
+ module Model
5
+ class Template
6
+ class Resource
7
+ ##
8
+ # AWS::SQS::QueuePolicy
9
+ ##
10
+ class SQSQueuePolicy < Resource
11
+ type 'AWS::SQS::QueuePolicy'
12
+ property :queue, 'Queues', :type => :list
13
+ property :policy_document, 'PolicyDocument'
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,121 @@
1
+ require 'test_helper'
2
+ require 'json'
3
+ require 'pp'
4
+
5
+ class TestConditions < Minitest::Test
6
+ def setup
7
+ # Inspired by http://www.unixdaemon.net/cloud/intro-to-cloudformations-conditionals.html
8
+ @template = ::Convection.template do
9
+ description 'Conditions Test Template'
10
+
11
+ parameter 'DeploymentEnvironment' do
12
+ type 'String'
13
+ description 'The environment this stack is being deployed to.'
14
+ default 'dev'
15
+
16
+ allow 'dev'
17
+ allow 'stage'
18
+ allow 'prod'
19
+ end
20
+
21
+ condition 'InProd' do
22
+ fn_equals(fn_ref('DeploymentEnvironment'), 'prod')
23
+ end
24
+
25
+ condition 'NotInProd' do
26
+ fn_or(
27
+ fn_equals(fn_ref('DeploymentEnvironment'), 'stage'),
28
+ fn_equals(fn_ref('DeploymentEnvironment'), 'dev')
29
+ )
30
+ end
31
+
32
+ resource 'SomeSG' do
33
+ type 'AWS::EC2::SecurityGroup'
34
+ condition 'NotInProd'
35
+ end
36
+
37
+ resource 'SQLDB' do
38
+ type 'AWS::RDS::DBInstance'
39
+ property 'Iops', fn_if('InProd', '1000', fn_ref('AWS::NoValue'))
40
+ end
41
+ end
42
+ end
43
+
44
+ def from_json
45
+ JSON.parse(@template.to_json)
46
+ end
47
+
48
+ def test_inprod_condition
49
+ # Expected JSON:
50
+ # "InProd": { "Fn::Equals" : [ { "Ref" : "DeploymentEnvironment" }, "prod" ] },
51
+ json = from_json['Conditions']['InProd']
52
+ func_args = json['Fn::Equals']
53
+
54
+ assert func_args.is_a? Array
55
+ assert_equal 2, func_args.size
56
+
57
+ perform_parameter_ref_comparison func_args, 'DeploymentEnvironment', 'prod'
58
+ end
59
+
60
+ def test_notinprod_condition
61
+ # Expected JSON:
62
+ # "NotInProd": {
63
+ # "Fn::Or" : [
64
+ # { "Fn::Equals" : [ { "Ref" : "DeploymentEnvironment"}, "stage" ] },
65
+ # { "Fn::Equals" : [ { "Ref" : "DeploymentEnvironment"}, "dev" ] }
66
+ # ]
67
+ # }
68
+ json = from_json['Conditions']['NotInProd']
69
+ func_args = json['Fn::Or']
70
+
71
+ assert func_args.is_a? Array
72
+ assert_equal 2, func_args.size
73
+
74
+ subfunc1 = func_args[0]['Fn::Equals'] # check for 'stage'
75
+ subfunc2 = func_args[1]['Fn::Equals'] # check for 'dev'
76
+
77
+ perform_parameter_ref_comparison subfunc1, 'DeploymentEnvironment', 'stage'
78
+ perform_parameter_ref_comparison subfunc2, 'DeploymentEnvironment', 'dev'
79
+ end
80
+
81
+ def test_resource_uses_condition
82
+ json = from_json['Resources']['SomeSG']
83
+
84
+ assert json.is_a? Hash
85
+ assert json.key? 'Condition'
86
+ assert_equal 'NotInProd', json['Condition']
87
+ end
88
+
89
+ def test_property_uses_condition
90
+ iops_property = from_json['Resources']['SQLDB']['Properties']['Iops']
91
+ # Expected JSON:
92
+ # "Iops" : { "Fn::If" : [ "InProd", "1000", { "Ref" : "AWS::NoValue" }
93
+
94
+ # Check we have an IF function
95
+ assert iops_property.is_a? Hash
96
+ assert_equal 1, iops_property.size
97
+ assert iops_property.key? 'Fn::If'
98
+
99
+ # Check the 3 arguments to the IF function: (condition, true_value, false_value)
100
+ if_cond = iops_property['Fn::If']
101
+ assert if_cond.is_a? Array
102
+ assert_equal 3, if_cond.size
103
+
104
+ assert_equal 'InProd', if_cond[0]
105
+ assert_equal '1000', if_cond[1]
106
+ assert if_cond[2].is_a? Hash
107
+ assert_equal 'AWS::NoValue', if_cond[2]['Ref']
108
+ end
109
+
110
+ private
111
+
112
+ def perform_parameter_ref_comparison(comparison_array, parameter_name, expected_value)
113
+ parameter_ref = comparison_array[0]
114
+ assert parameter_ref.is_a? Hash
115
+ assert_equal 1, parameter_ref.size
116
+ assert parameter_ref.key? 'Ref'
117
+ assert parameter_ref.value? parameter_name
118
+
119
+ assert_equal expected_value, comparison_array[1]
120
+ end
121
+ end
@@ -0,0 +1,97 @@
1
+ require 'test_helper'
2
+ require 'json'
3
+ require 'pp'
4
+
5
+ class TestElasticache < Minitest::Test
6
+ def setup
7
+ # Inspired by http://www.unixdaemon.net/cloud/intro-to-cloudformations-conditionals.html
8
+ @template = ::Convection.template do
9
+ description 'Elasticache Test Template'
10
+
11
+ ec2_security_group 'MyEC2SecGroup' do
12
+ ingress_rule(:tcp, 22, 'my.ip.address')
13
+ end
14
+
15
+ elasticache_security_group 'MyRedisSecGroup' do
16
+ description 'Redis cache security group'
17
+ end
18
+
19
+ elasticache_security_group_ingress 'MyRedisSecGroupIngress' do
20
+ cache_security_group_name fn_ref('MyRedisSecGroup')
21
+ ec2_security_group_name(fn_ref('MyEC2SecGroup'))
22
+ ec2_security_group_owner_id('123456789012')
23
+ end
24
+
25
+ elasticache_parameter_group 'MyRedisParmGroup' do
26
+ cache_parameter_group_family 'redis2.8'
27
+ description 'Redis cache parameter group'
28
+ parameter 'my_parm_key', 'my_parm_value'
29
+ end
30
+
31
+ elasticache_cache_cluster 'MyRedisCluster' do
32
+ cluster_name 'demo'
33
+
34
+ auto_minor_version_upgrade true
35
+ cache_node_type 'cache.m3.medium'
36
+ cache_parameter_group_name fn_ref('MyRedisParmGroup')
37
+ cache_security_group_names [fn_ref('MyRedisSecGroup')]
38
+ engine 'redis'
39
+ engine_version '2.8.6'
40
+ num_cache_nodes 1
41
+ end
42
+ end
43
+ end
44
+
45
+ def from_json
46
+ JSON.parse(@template.to_json)
47
+ end
48
+
49
+ def test_elasticache_instance
50
+ json = from_json['Resources']['MyRedisCluster']
51
+ secgroups = json['Properties']['CacheSecurityGroupNames']
52
+
53
+ assert secgroups.is_a? Array
54
+ assert_equal 1, secgroups.size
55
+
56
+ perform_parameter_ref_comparison secgroups, 'MyRedisSecGroup', nil
57
+ end
58
+
59
+ # XXX Which method is correct implementation? @aburke
60
+ # def test_elasticache_secgroup_ingress
61
+ # json = from_json['Resources']['MyRedisSecGroupIngress']
62
+ # cachesecgroup = json['Properties']['CacheSecurityGroupName']
63
+ #
64
+ # assert cachesecgroup.is_a? Hash
65
+ # assert_equal 1, cachesecgroup.size
66
+ # assert cachesecgroup.key? 'Ref'
67
+ # assert cachesecgroup.value? 'MyRedisSecGroup'
68
+ #
69
+ # ec2_secgroup = json['Properties']['EC2SecurityGroupName']
70
+ # assert ec2_secgroup.is_a? Hash
71
+ # assert_equal 1, ec2_secgroup.size
72
+ # assert ec2_secgroup.key? 'Ref'
73
+ # assert ec2_secgroup.value? 'MyEC2SecGroup'
74
+ # end
75
+
76
+ def test_elasticache_secgroup_ingress
77
+ json = from_json['Resources']['MyRedisParmGroup']
78
+ parms = json['Properties']['Properties']
79
+
80
+ assert parms.is_a? Hash
81
+ assert_equal 1, parms.size
82
+ assert parms.key? 'my_parm_key'
83
+ assert parms.value? 'my_parm_value'
84
+ end
85
+
86
+ private
87
+
88
+ def perform_parameter_ref_comparison(comparison_array, parameter_name, expected_value)
89
+ parameter_ref = comparison_array[0]
90
+ assert parameter_ref.is_a? Hash
91
+ assert_equal 1, parameter_ref.size
92
+ assert parameter_ref.key? 'Ref'
93
+ assert parameter_ref.value? parameter_name
94
+
95
+ assert_equal expected_value, comparison_array[1]
96
+ end
97
+ end