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
@@ -1,15 +1,73 @@
1
+ require 'forwardable'
2
+ require_relative './intrinsic_functions'
3
+
1
4
  module Convection
2
5
  module DSL
3
6
  ##
4
- # Template DSL
7
+ # Methods for defining DSL/Models
8
+ ##
9
+ module ClassHelpers
10
+ def attribute(attribute_name)
11
+ attr_writer attribute_name
12
+ define_method(attribute_name) do |value = nil|
13
+ instance_variable_set("@#{ attribute_name }", value) unless value.nil?
14
+ instance_variable_get("@#{ attribute_name }")
15
+ end
16
+ end
17
+
18
+ def list(attribute_name)
19
+ define_method(attribute_name) do |value = nil|
20
+ collection = instance_variable_get("@#{ attribute_name }")
21
+ collection << value unless value.nil?
22
+ collection
23
+ end
24
+ end
25
+ end
26
+
27
+ ##
28
+ # Helper methods for creating templates
5
29
  ##
6
30
  module Helpers
7
- def attribute(name)
8
- define_method(name) do |value = nil|
9
- instance_variable_set("@#{ name }", value) unless value.nil?
10
- instance_variable_get("@#{ name }")
31
+ extend Forwardable
32
+ include DSL::IntrinsicFunctions
33
+
34
+ def_delegators :@template, :stack, :parameters, :mappings, :resources, :outputs
35
+
36
+ class << self
37
+ def included(mod)
38
+ mod.extend(DSL::ClassHelpers)
39
+ end
40
+
41
+ def method_name(cf_type)
42
+ nodes = cf_type.split('::')
43
+ nodes.shift # Remove AWS::
44
+
45
+ ## Cammel-case to snake-case
46
+ nodes.map! do |n|
47
+ n.split(/([A-Z0-9])(?![A-Z0-9])(?<!$)/)
48
+ .reject(&:empty?)
49
+ .reduce('') { |a, e| (e.length == 1 && !a.empty?) ? a + "_#{e}" : a + e }
50
+ .downcase
51
+ end
52
+
53
+ nodes.join('_').downcase
11
54
  end
12
55
  end
56
+
57
+ ## Convert various casings to CamelCase
58
+ def camel_case(str)
59
+ str.downcase.split(/[\.\-_]/).map(&:capitalize).join
60
+ end
61
+
62
+ ## Convert various casings to snake_case
63
+ def snake_case(str)
64
+ str.downcase.split(/[\.\-_]/).join('_')
65
+ end
66
+
67
+ ## Convert various casings to SCREAMING_SNAKE_CASE
68
+ def screaming_snake_case(str)
69
+ str.upcase.split(/[\.\-_]/).join('_')
70
+ end
13
71
  end
14
72
  end
15
73
  end
@@ -0,0 +1,60 @@
1
+ module Convection
2
+ module Model
3
+ ##
4
+ # Manage parameters and attributes across stacks
5
+ ##
6
+ class Attributes
7
+ class Stack
8
+ attr_accessor :resources
9
+ attr_accessor :outputs
10
+ attr_reader :parameters
11
+
12
+ def initialize
13
+ @resources = {}
14
+ @outputs = {}
15
+ @parameters = {}
16
+ end
17
+
18
+ def include?(key)
19
+ @parameters.include?(key) || @outputs.include?(key) || @resources.include?(key)
20
+ end
21
+
22
+ def [](key)
23
+ @parameters[key.to_s] || @outputs[key.to_s] || @resources[key.to_s]
24
+ end
25
+
26
+ def []=(key, value)
27
+ @parameters[key.to_s] = value
28
+ end
29
+ end
30
+
31
+ attr_reader :stacks
32
+
33
+ def initialize
34
+ @stacks = Hash.new do |hash, key|
35
+ hash[key] = Stack.new
36
+ end
37
+ end
38
+
39
+ def include?(stack, key)
40
+ @stacks.include?(stack) && @stacks[stack].include?(key)
41
+ end
42
+
43
+ def get(stack, key, default = nil)
44
+ include?(stack, key) ? @stacks[stack.to_s][key.to_s] : default
45
+ end
46
+
47
+ def set(stack, key, value)
48
+ @stacks[stack.to_s][key.to_s] = value
49
+ end
50
+
51
+ def load_outputs(stack)
52
+ @stacks[stack.name.to_s].outputs = stack.outputs
53
+ end
54
+
55
+ def load_resources(stack)
56
+ @stacks[stack.name.to_s].resources = stack.attribute_mapping_values
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,58 @@
1
+ require_relative '../control/stack'
2
+ require_relative '../dsl/helpers'
3
+ require_relative '../model/attributes'
4
+ require_relative '../model/template'
5
+
6
+ module Convection
7
+ module DSL
8
+ ##
9
+ # DSL for Cloudfile
10
+ ##
11
+ module Cloudfile
12
+ include DSL::Helpers
13
+
14
+ attribute :name
15
+ attribute :region
16
+ attribute :splay
17
+
18
+ ## Helper to define a template in-line
19
+ def template(*args, &block)
20
+ Model::Template.new(*args, &block)
21
+ end
22
+
23
+ def attribute(stack, key, value)
24
+ @attributes.set(stack, key, value)
25
+ end
26
+
27
+ def stack(stack_name, template, options = {})
28
+ options[:region] ||= region
29
+ options[:cloud] = name
30
+ options[:attributes] = attributes
31
+
32
+ @stacks[stack_name] = Control::Stack.new(stack_name, template, options)
33
+ @deck << @stacks[stack_name]
34
+ end
35
+ end
36
+ end
37
+
38
+ module Model
39
+ ##
40
+ # Define your Clouds
41
+ ##
42
+ class Cloudfile
43
+ include DSL::Cloudfile
44
+
45
+ attr_reader :attributes
46
+ attr_reader :stacks
47
+ attr_reader :deck
48
+
49
+ def initialize(cloudfile)
50
+ @attributes = Model::Attributes.new
51
+ @stacks = {}
52
+ @deck = []
53
+
54
+ instance_eval(IO.read(cloudfile), cloudfile, 1)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,39 @@
1
+ require_relative './mixin/colorize'
2
+
3
+ module Convection
4
+ module Model
5
+ ##
6
+ # Difference between an item in two templates
7
+ ##
8
+ class Diff
9
+ extend Mixin::Colorize
10
+
11
+ attr_reader :key
12
+ attr_reader :action
13
+ attr_reader :ours
14
+ attr_reader :theirs
15
+ colorize :action, :green => [:create], :yellow => [:update], :red => [:delete]
16
+
17
+ def initialize(key, ours, theirs)
18
+ @key = key
19
+ @ours = ours
20
+ @theirs = theirs
21
+
22
+ @action = if ours && theirs then :update
23
+ elsif ours then :create
24
+ else :delete
25
+ end
26
+ end
27
+
28
+ def to_thor
29
+ message = case action
30
+ when :create then "#{ key }: #{ ours }"
31
+ when :update then "#{ key }: #{ theirs } => #{ ours }"
32
+ when :delete then key
33
+ end
34
+
35
+ [action, message, color]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,62 @@
1
+ require_relative './mixin/colorize'
2
+ require_relative '../control/stack'
3
+
4
+ module Convection
5
+ module Model
6
+ ##
7
+ # Wrap events with some smarts
8
+ ##
9
+ class Event
10
+ extend Mixin::Colorize
11
+
12
+ attr_accessor :name
13
+ attr_accessor :message
14
+ attr_accessor :level
15
+ attr_accessor :timestamp
16
+ colorize :level,
17
+ :green => [:info, :success, Control::Stack::CREATE_COMPLETE, Control::Stack::UPDATE_COMPLETE, Control::Stack::UPDATE_ROLLBACK_COMPLETE],
18
+ :red => [:error, :fail, Control::Stack::CREATE_FAILED, Control::Stack::ROLLBACK_FAILED,
19
+ Control::Stack::DELETE_FAILED, Control::Stack::UPDATE_FAILED,
20
+ Control::Stack::UPDATE_ROLLBACK_IN_PROGRESS, Control::Stack::UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS,
21
+ Control::Stack::UPDATE_ROLLBACK_FAILED],
22
+ :default => :yellow
23
+
24
+ class << self
25
+ def from_cf(event)
26
+ Event.new(event.resource_status.downcase,
27
+ "#{ event.logical_resource_id }: (#{ event.resource_type }/"\
28
+ "#{ event.physical_resource_id}) #{ event.resource_status_reason }",
29
+ event.resource_status,
30
+ :event_id => event.event_id,
31
+ :logical_resource_id => event.logical_resource_id,
32
+ :physical_resource_id => event.physical_resource_id,
33
+ :resource_properties => event.resource_properties,
34
+ :resource_status_reason => event.resource_status_reason,
35
+ :resource_type => event.resource_type,
36
+ :stack_id => event.stack_id,
37
+ :stack_name => event.stack_name,
38
+ :timestamp => event.timestamp)
39
+ end
40
+ end
41
+
42
+ def initialize(name, message, level = :info, attributes = {})
43
+ @name = name
44
+ @message = message
45
+ @level = level
46
+ @attributes = attributes
47
+ end
48
+
49
+ def [](attr)
50
+ @attributes[attr]
51
+ end
52
+
53
+ def []=(attr, value)
54
+ @attributes[attr] = value
55
+ end
56
+
57
+ def to_thor
58
+ [name.downcase, message, color]
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,18 @@
1
+ class ValidationError < StandardError; end
2
+ class ExcessiveResourcesError < ValidationError; end
3
+ class ExcessiveResourceNameError < ValidationError; end
4
+ class ExcessiveMappingsError < ValidationError; end
5
+ class ExcessiveMappingAttributesError < ValidationError; end
6
+ class ExcessiveMappingNameError < ValidationError; end
7
+ class ExcessiveMappingAttributeNameError < ValidationError; end
8
+ class ExcessiveParametersError < ValidationError; end
9
+ class ExcessiveParameterNameError < ValidationError; end
10
+ class ExcessiveParameterBytesizeError < ValidationError; end
11
+ class ExcessiveOutputsError < ValidationError; end
12
+ class ExcessiveOutputNameError < ValidationError; end
13
+ class ExcessiveDescriptionError < ValidationError; end
14
+ class ExcessiveTemplateSizeError < ValidationError; end
15
+
16
+ def limit_exceeded_error(value, limit, error_class)
17
+ fail error_class, "Value #{value} exceeds Limit #{limit}"
18
+ end
@@ -4,12 +4,12 @@ module Convection
4
4
  module Model
5
5
  module Mixin
6
6
  ##
7
- # Add condition helpers
7
+ # Sanitized CIDR IP notation
8
8
  ##
9
9
  module CIDRBlock
10
- def network(*args)
11
- @network = NetAddr::CIDR.create(*args) unless args.empty?
12
- property('CidrBlock', @network.to_s)
10
+ def cidr_property(name = :network, property_name = 'CidrBlock')
11
+ property(name, property_name,
12
+ :transform => proc { |*args| NetAddr::CIDR.create(*args) })
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,20 @@
1
+ module Convection
2
+ module Model
3
+ module Mixin
4
+ module Colorize
5
+ def colorize(param, options = {})
6
+ define_method(:color) do
7
+ case instance_variable_get("@#{ param }")
8
+ when *options.fetch(:white, [:status]) then :white
9
+ when *options.fetch(:cyan, [:debug, :trace]) then :cyan
10
+ when *options.fetch(:green, [:info, :success, :create]) then :green
11
+ when *options.fetch(:yellow, [:warn, :update]) then :yellow
12
+ when *options.fetch(:red, [:error, :fail, :delete]) then :red
13
+ else options.fetch(:default, :green)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -11,9 +11,7 @@ module Convection
11
11
  end
12
12
 
13
13
  def render_condition(resource)
14
- resource.tap do |r|
15
- r['Condition'] = condition unless condition.nil?
16
- end
14
+ resource['Condition'] = condition unless condition.nil?
17
15
  end
18
16
  end
19
17
  end
@@ -0,0 +1,89 @@
1
+ module Convection
2
+ module Model
3
+ module Mixin
4
+ ##
5
+ # Add definition helpers for entities with policies
6
+ ##
7
+ class Policy
8
+ include DSL::Helpers
9
+
10
+ DEFAULT_VERSION = '2012-10-17'
11
+
12
+ attribute :name
13
+ attribute :id
14
+ attribute :version
15
+ list :statement
16
+
17
+ def initialize(options = {})
18
+ @name = options.fetch(:name) { SecureRandom.uuid }
19
+ @version = DEFAULT_VERSION
20
+ @statement = []
21
+
22
+ @template = options[:template]
23
+ end
24
+
25
+ def allow(sid = nil, &block)
26
+ add_statement = Statement.new('Allow', @template)
27
+ add_statement.sid = sid unless sid.nil?
28
+ add_statement.instance_exec(&block) if block
29
+
30
+ statement(add_statement)
31
+ end
32
+
33
+ def document
34
+ {
35
+ 'Version' => version,
36
+ 'Statement' => statement.map(&:render)
37
+ }
38
+ end
39
+
40
+ def render(parent = {})
41
+ parent.tap do |resource|
42
+ resource['PolicyName'] = name unless name.is_a?(FalseClass)
43
+ resource['PolicyDocument'] = document
44
+ end
45
+ end
46
+
47
+ ##
48
+ # An IAM policy statement
49
+ ##
50
+ class Statement
51
+ include DSL::Helpers
52
+
53
+ attribute :sid
54
+ attribute :effect
55
+ attribute :principal
56
+ attribute :condition
57
+ list :action
58
+ list :resource
59
+
60
+ def s3_resource(bucket, path = nil)
61
+ return resource "arn:aws:s3:::#{ bucket }/#{ path }" unless path.nil?
62
+ resource "arn:aws:s3:::#{ bucket }"
63
+ end
64
+
65
+ def initialize(effect = 'Allow', template = nil)
66
+ @effect = effect
67
+
68
+ @action = []
69
+ @resource = []
70
+
71
+ @template = template
72
+ end
73
+
74
+ def render
75
+ {
76
+ 'Effect' => effect,
77
+ 'Action' => action,
78
+ 'Resource' => resource
79
+ }.tap do |statemant|
80
+ statemant['Sid'] = sid unless sid.nil?
81
+ statemant['Condition'] = condition unless condition.nil?
82
+ statemant['Principal'] = principal unless principal.nil?
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end