cfn-model 0.1.19 → 0.1.20

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 92eab96d3f067bc96c5745d85df911ed45ad9e3e
4
- data.tar.gz: 71ab75c2b46e1db93170f5b1144c717655d26196
3
+ metadata.gz: 652fe0c686fe75981f441077ed1514a520e98ec2
4
+ data.tar.gz: 0559f9931b7a2bea7a20ee103e555e7c941b4551
5
5
  SHA512:
6
- metadata.gz: 285309a40260fb8de1d2827c216436365ab2bddc815a59897b2f7a170622c685d58315ce1b6ef2b0a38c5cf951de4ccfa5549fb2663bdba92146cf118e6cd2ab
7
- data.tar.gz: cfee44802f748b9d6db24b17ba29143c7c460569dcc6504f9682e9a1d61cd297826836acf53b467cc8c4a2737258f19088ef068c509ed257656d768ae4fcdd52
6
+ metadata.gz: 8ce505301b69d93e0ba094f985352d7ca34315d469ef874c352280ed84f47d5902fb9b02c003aaa33cffcdaf8647b9f85e7979e27243ae8be2cd247d521ab001
7
+ data.tar.gz: 7f4ec766db340462ad877dbdfc210c7fae2702040d90fd163643e577062f8284919b152c1ac5b98ab2e8e4678b0fa7e126bd7b0b79cf434e9c34ea39e7c249b7
@@ -1,13 +1,11 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::S3::BucketPolicy < ModelElement
4
- # mapped from document
5
- attr_accessor :bucket, :policyDocument
6
-
7
4
  # PolicyDocument - objectified policyDocument
8
5
  attr_accessor :policy_document
9
6
 
10
- def initialize
7
+ def initialize(cfn_model)
8
+ super
11
9
  @resource_type = 'AWS::S3::BucketPolicy'
12
10
  end
13
11
  end
@@ -1,7 +1,7 @@
1
1
  require_relative 'references'
2
2
 
3
3
  class CfnModel
4
- attr_reader :resources
4
+ attr_reader :resources, :parameters
5
5
 
6
6
  ##
7
7
  # if you really want it, here it is - the raw Hash from YAML.load. you'll have to mess with structural nits of
@@ -10,6 +10,7 @@ class CfnModel
10
10
  attr_accessor :raw_model
11
11
 
12
12
  def initialize
13
+ @parameters = {}
13
14
  @resources = {}
14
15
  @raw_model = nil
15
16
  end
@@ -20,6 +21,9 @@ class CfnModel
20
21
  # the Hash is a clone
21
22
  def copy
22
23
  new_cfn_model = CfnModel.new
24
+ @parameters.each do |k,v|
25
+ new_cfn_model.parameters[k] = v
26
+ end
23
27
  @resources.each do |k, v|
24
28
  new_cfn_model.resources[k] = v
25
29
  end
@@ -1,12 +1,11 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::EC2::Instance < ModelElement
4
- attr_accessor :securityGroupIds, :networkInterfaces
5
-
6
4
  # SecurityGroup objects based upon securityGroupIds
7
5
  attr_accessor :security_groups
8
6
 
9
- def initialize
7
+ def initialize(cfn_model)
8
+ super
10
9
  @securityGroupIds = []
11
10
  @networkInterfaces = []
12
11
  @security_groups = []
@@ -1,13 +1,11 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::EC2::NetworkInterface < ModelElement
4
- attr_accessor :groupSet, :ipv6Addresses, :privateIpAddresses, :tags
5
- attr_accessor :description, :ipv6AddressCount, :privateIpAddress, :secondaryPrivateIpAddressCount, :sourceDestCheck, :subnetId
6
-
7
4
  # SecurityGroup objects based upon groupSet
8
5
  attr_accessor :security_groups
9
6
 
10
- def initialize
7
+ def initialize(cfn_model)
8
+ super
11
9
  @groupSet = []
12
10
  @ipv6Addresses = []
13
11
  @privateIpAddresses = []
@@ -1,12 +1,11 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::IAM::Group < ModelElement
4
- attr_accessor :groupName, :managedPolicyArns, :path, :policies
5
-
6
4
  # synthesized version of policies
7
5
  attr_accessor :policy_objects
8
6
 
9
- def initialize
7
+ def initialize(cfn_model)
8
+ super
10
9
  @managedPolicyArns = []
11
10
  @policies = []
12
11
  @policy_objects = []
@@ -1,11 +1,10 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::IAM::ManagedPolicy < ModelElement
4
- attr_accessor :description, :managedPolicyName, :policyDocument, :groups, :roles, :users, :path
5
-
6
4
  attr_accessor :policy_document
7
5
 
8
- def initialize
6
+ def initialize(cfn_model)
7
+ super
9
8
  @groups = []
10
9
  @roles = []
11
10
  @users = []
@@ -1,11 +1,10 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::IAM::Policy < ModelElement
4
- attr_accessor :policyName, :policyDocument, :groups, :roles, :users
5
-
6
4
  attr_accessor :policy_document
7
5
 
8
- def initialize
6
+ def initialize(cfn_model)
7
+ super
9
8
  @groups = []
10
9
  @roles = []
11
10
  @users = []
@@ -1,11 +1,10 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::IAM::Role < ModelElement
4
- attr_accessor :roleName, :assumeRolePolicyDocument, :policies, :path, :managedPolicyArns
5
-
6
4
  attr_accessor :policy_objects, :assume_role_policy_document
7
5
 
8
- def initialize
6
+ def initialize(cfn_model)
7
+ super
9
8
  @policies = []
10
9
  @managedPolicyArns = []
11
10
  @policy_objects = []
@@ -1,12 +1,11 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::IAM::User < ModelElement
4
- attr_accessor :groups, :loginProfile, :path, :policies, :userName
5
-
6
4
  # synthesized version of policies
7
5
  attr_accessor :policy_objects, :group_names
8
6
 
9
- def initialize
7
+ def initialize(cfn_model)
8
+ super
10
9
  @groups = []
11
10
  @policies = []
12
11
  @policy_objects = []
@@ -1,12 +1,10 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::ElasticLoadBalancing::LoadBalancer < ModelElement
4
- attr_accessor :securityGroups, :subnets, :tags, :scheme, :loadBalancerName, :crossZone, :availabilityZones, :connectionDrainingPolicy
5
- attr_accessor :connectionSettings, :accessLoggingPolicy, :instances, :appCookieStickinessPolicy, :lBCookieStickinessPolicy, :healthCheck, :policies, :listeners
6
-
7
4
  attr_accessor :security_groups
8
5
 
9
- def initialize
6
+ def initialize(cfn_model)
7
+ super
10
8
  @securityGroups = []
11
9
  @security_groups = []
12
10
  @subnets = []
@@ -22,11 +20,10 @@ class AWS::ElasticLoadBalancing::LoadBalancer < ModelElement
22
20
  end
23
21
 
24
22
  class AWS::ElasticLoadBalancingV2::LoadBalancer < ModelElement
25
- attr_accessor :securityGroups, :loadBalancerAttributes, :subnets, :tags, :scheme, :name, :ipAddressType
26
-
27
23
  attr_accessor :security_groups
28
24
 
29
- def initialize
25
+ def initialize(cfn_model)
26
+ super
30
27
  @securityGroups = []
31
28
  @security_groups = []
32
29
  @loadBalancerAttributes = []
@@ -1,3 +1,4 @@
1
+ require_relative 'references'
1
2
 
2
3
  module AWS
3
4
  module CloudFormation
@@ -45,9 +46,17 @@ module Custom
45
46
 
46
47
  end
47
48
 
49
+ #ModelElement is a bit of a misnomer I think.... this is really a Resource, and Parameter and Resource
50
+ #have a lot in common, but are different
48
51
  class ModelElement
49
52
  attr_accessor :logical_resource_id, :resource_type, :metadata
50
53
 
54
+ # the dreaded two way relationship
55
+ def initialize(cfn_model)
56
+ raise 'cfn_model must be specificed' if cfn_model.nil?
57
+ @cfn_model = cfn_model
58
+ end
59
+
51
60
  def to_s
52
61
  <<END
53
62
  {
@@ -59,7 +68,7 @@ END
59
68
  def ==(another_model_element)
60
69
  found_unequal_instance_var = false
61
70
  instance_variables_without_at_sign.each do |instance_variable|
62
- if instance_variable != :logical_resource_id
71
+ if instance_variable != :logical_resource_id && instance_variable != :cfn_model
63
72
  if self.send(instance_variable) != another_model_element.send(instance_variable)
64
73
  found_unequal_instance_var = true
65
74
  end
@@ -79,7 +88,7 @@ END
79
88
  if method_name =~ /^(\w+)=$/
80
89
  instance_variable_set "@#{$1}", args[0]
81
90
  else
82
- instance_variable_get "@#{method_name}"
91
+ References.resolve_value(@cfn_model, instance_variable_get("@#{method_name}"))
83
92
  end
84
93
  end
85
94
 
@@ -0,0 +1,39 @@
1
+ #copy-paste alert with ModelElement which should instead be Resource anyway
2
+ class Parameter
3
+ attr_accessor :id, :type
4
+
5
+ attr_accessor :synthesized_value
6
+
7
+ def is_no_echo?
8
+ !@noEcho.nil? && @noEcho.to_s.downcase == 'true'
9
+ end
10
+
11
+ def to_s
12
+ <<END
13
+ {
14
+ #{emit_instance_vars}
15
+ }
16
+ END
17
+ end
18
+
19
+ ##
20
+ # Treat any missing method as an instance variable get/set
21
+ #
22
+ # This will allow arbitrary elements in Resource/Properties definitions
23
+ # to map to instance variables without having to anticipate them in a schema
24
+ def method_missing(method_name, *args)
25
+ if method_name =~ /^(\w+)=$/
26
+ instance_variable_set "@#{$1}", args[0]
27
+ else
28
+ instance_variable_get "@#{method_name}"
29
+ end
30
+ end
31
+
32
+ def emit_instance_vars
33
+ instance_vars_str = ''
34
+ self.instance_variables.each do |instance_variable|
35
+ instance_vars_str += " #{instance_variable}=#{instance_variable_get(instance_variable)}\n"
36
+ end
37
+ instance_vars_str
38
+ end
39
+ end
@@ -1,12 +1,11 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::SQS::QueuePolicy < ModelElement
4
- attr_accessor :queues, :policyDocument
5
-
6
4
  # PolicyDocument - objectified policyDocument
7
5
  attr_accessor :policy_document
8
6
 
9
- def initialize
7
+ def initialize(cfn_model)
8
+ super
10
9
  @queues = []
11
10
  @resource_type = 'AWS::SQS::QueuePolicy'
12
11
  end
@@ -6,6 +6,28 @@
6
6
  # references yet... in the meantime pile things up here and hope a pattern becomes
7
7
  # clear
8
8
  module References
9
+ def self.resolve_value(cfn_model, value)
10
+ if value.is_a? Hash
11
+ if value.has_key?('Ref')
12
+ ref_id = value['Ref']
13
+ if ref_id.is_a? String
14
+ if cfn_model.parameters.has_key?(ref_id)
15
+ return value if cfn_model.parameters[ref_id].synthesized_value.nil?
16
+ return cfn_model.parameters[ref_id].synthesized_value
17
+ else
18
+ return value
19
+ end
20
+ else
21
+ return value
22
+ end
23
+ else
24
+ return value
25
+ end
26
+ else
27
+ return value
28
+ end
29
+ end
30
+
9
31
  def self.is_security_group_id_external(group_id)
10
32
  resolve_security_group_id(group_id).nil?
11
33
  end
@@ -1,13 +1,10 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::EC2::SecurityGroup < ModelElement
4
- attr_accessor :groupDescription, :vpcId
5
- attr_accessor :tags
6
- attr_accessor :securityGroupIngress, :securityGroupEgress
7
-
8
4
  attr_accessor :ingresses, :egresses
9
5
 
10
- def initialize
6
+ def initialize(cfn_model)
7
+ super
11
8
  @securityGroupIngress = []
12
9
  @securityGroupEgress = []
13
10
  @ingresses = []
@@ -4,26 +4,9 @@ require_relative 'model_element'
4
4
  # in latter case there would be a logical resource id
5
5
  # but i think we don't ever care?
6
6
  class AWS::EC2::SecurityGroupEgress < ModelElement
7
- # You must specify a destination security group (destinationPrefixListId or destinationSecurityGroupId) or a CIDR range (CidrIp or CidrIpv6).
8
- attr_accessor :cidrIp,
9
- :cidrIpv6,
10
- :destinationPrefixListId,
11
- :destinationSecurityGroupId
12
7
 
13
- # required
14
- attr_accessor :groupId,
15
- :fromPort,
16
- :toPort,
17
- :ipProtocol
18
-
19
- def initialize
8
+ def initialize(cfn_model)
9
+ super
20
10
  @resource_type = 'AWS::EC2::SecurityGroupEgress'
21
11
  end
22
-
23
- # def valid?
24
- # has_no_destination = @cidrIp.nil? && @cidrIpv6.nil? && @destinationPrefixListId.nil? && @destinationSecurityGroupId.nil?
25
- # if has_no_destination
26
- # raise "SG egress #{@logical_resource_id} has no destination specified"
27
- # end
28
- # end
29
12
  end
@@ -4,35 +4,8 @@ require_relative 'model_element'
4
4
  # in latter case there would be a logical resource id
5
5
  # but i think we don't ever care?
6
6
  class AWS::EC2::SecurityGroupIngress < ModelElement
7
- # You must specify a source security group (SourceSecurityGroupName or SourceSecurityGroupId) or a CIDR range (CidrIp or CidrIpv6).
8
- attr_accessor :cidrIp,
9
- :cidrIpv6,
10
- :sourceSecurityGroupName,
11
- :sourceSecurityGroupId
12
-
13
- # Required: Conditional. You must specify the GroupName property or the GroupId property.
14
- # For security groups that are in a VPC, you must use the GroupId property. For example, EC2-VPC accounts must use the GroupId property.
15
- # this will be nil for inline ingress rules
16
- attr_accessor :groupId,
17
- :groupName
18
-
19
- # required
20
- attr_accessor :fromPort,
21
- :toPort,
22
- :ipProtocol
23
-
24
- # Required: Conditional. If you specify SourceSecurityGroupName and that security group is owned by a different
25
- # account than the account creating the stack, you must specify the SourceSecurityGroupOwnerId; otherwise, this property is optional.
26
- attr_accessor :sourceSecurityGroupOwnerId
27
-
28
- def initialize
7
+ def initialize(cfn_model)
8
+ super
29
9
  @resource_type = 'AWS::EC2::SecurityGroupIngress'
30
10
  end
31
-
32
- # def valid?
33
- # has_no_source = @cidrIp.nil? && @cidrIpv6.nil? && @sourceSecurityGroupName.nil? && @sourceSecurityGroupId.nil?
34
- # if has_no_source
35
- # raise "SG ingress #{@logical_resource_id} has no source specified"
36
- # end
37
- # end
38
11
  end
@@ -1,12 +1,11 @@
1
1
  require_relative 'model_element'
2
2
 
3
3
  class AWS::SNS::TopicPolicy < ModelElement
4
- attr_accessor :topics, :policyDocument
5
-
6
4
  # PolicyDocument - objectified policyDocument
7
5
  attr_accessor :policy_document
8
6
 
9
- def initialize
7
+ def initialize(cfn_model)
8
+ super
10
9
  @topics = []
11
10
  @resource_type = 'AWS::SNS::TopicPolicy'
12
11
  end
@@ -1,4 +1,5 @@
1
1
  require 'yaml'
2
+ require 'json'
2
3
  require 'cfn-model/validator/cloudformation_validator'
3
4
  require 'cfn-model/validator/reference_validator'
4
5
  require_relative 'parser_registry'
@@ -29,7 +30,7 @@ class CfnParser
29
30
  ##
30
31
  # Given raw json/yml CloudFormation template, returns a CfnModel object
31
32
  # or raise ParserErrors if something is amiss with the format
32
- def parse(cloudformation_yml)
33
+ def parse(cloudformation_yml, parameter_values_json=nil)
33
34
  cfn_hash = pre_validate_model cloudformation_yml
34
35
 
35
36
  cfn_model = CfnModel.new
@@ -37,15 +38,39 @@ class CfnParser
37
38
 
38
39
  # pass 1: wire properties into ModelElement objects
39
40
  transform_hash_into_model_elements cfn_hash, cfn_model
41
+ transform_hash_into_parameters cfn_hash, cfn_model
40
42
 
41
43
  # pass 2: tie together separate resources only where necessary to make life easier for rule logic
42
44
  post_process_resource_model_elements cfn_model
43
45
 
46
+ apply_parameter_values(cfn_model, parameter_values_json)
47
+
44
48
  cfn_model
45
49
  end
46
50
 
47
51
  private
48
52
 
53
+ def apply_parameter_values(cfn_model, parameter_values_json)
54
+ unless parameter_values_json.nil?
55
+ parameter_values = JSON.load parameter_values_json
56
+ return unless parameter_values.has_key? 'Parameters'
57
+ parameter_values['Parameters'].each do |parameter_name, parameter_value|
58
+ if cfn_model.parameters.has_key?(parameter_name)
59
+ cfn_model.parameters[parameter_name].synthesized_value = parameter_value.to_s
60
+ end
61
+ end
62
+
63
+ # any leftovers get default value
64
+ # if external values were specified, we take that as a cue to consider defaults
65
+ # if no external values, we will ignore default values
66
+ cfn_model.parameters.each do |_, parameter|
67
+ if parameter.synthesized_value.nil? && !parameter.default.nil?
68
+ parameter.synthesized_value = parameter.default.to_s
69
+ end
70
+ end
71
+ end
72
+ end
73
+
49
74
  def post_process_resource_model_elements(cfn_model)
50
75
  cfn_model.resources.each do |_, resource|
51
76
  resource_parser_class = ParserRegistry.instance.registry[resource.class.to_s]
@@ -64,7 +89,7 @@ class CfnParser
64
89
  cfn_hash['Resources'].each do |resource_name, resource|
65
90
  resource_class = class_from_type_name resource['Type']
66
91
 
67
- resource_object = resource_class.new
92
+ resource_object = resource_class.new(cfn_model)
68
93
  resource_object.logical_resource_id = resource_name
69
94
  resource_object.resource_type = resource['Type']
70
95
  resource_object.metadata = resource['Metadata']
@@ -76,6 +101,24 @@ class CfnParser
76
101
  cfn_model
77
102
  end
78
103
 
104
+ def transform_hash_into_parameters(cfn_hash, cfn_model)
105
+ return cfn_model unless cfn_hash.has_key?('Parameters')
106
+
107
+ cfn_hash['Parameters'].each do |parameter_name, parameter_hash|
108
+ parameter = Parameter.new
109
+ parameter.id = parameter_name
110
+ parameter.type = parameter_hash['Type']
111
+
112
+ parameter_hash.each do |property_name, property_value|
113
+ next if %w(Type).include? property_name
114
+ parameter.send("#{map_property_name_to_attribute(property_name)}=", property_value)
115
+ end
116
+
117
+ cfn_model.parameters[parameter_name] = parameter
118
+ end
119
+ cfn_model
120
+ end
121
+
79
122
  def pre_validate_model(cloudformation_yml)
80
123
  errors = CloudFormationValidator.new.validate cloudformation_yml
81
124
  if !errors.nil? && !errors.empty?
@@ -8,9 +8,9 @@ class SecurityGroupParser
8
8
  def parse(cfn_model:, resource:)
9
9
  security_group = resource
10
10
 
11
- objectify_egress security_group
11
+ objectify_egress cfn_model, security_group
12
12
 
13
- objectify_ingress security_group
13
+ objectify_ingress cfn_model, security_group
14
14
 
15
15
  wire_ingress_rules_to_security_group(cfn_model: cfn_model, security_group: security_group)
16
16
  wire_egress_rules_to_security_group(cfn_model: cfn_model, security_group: security_group)
@@ -26,14 +26,14 @@ class SecurityGroupParser
26
26
  end
27
27
  end
28
28
 
29
- def objectify_ingress(security_group)
29
+ def objectify_ingress(cfn_model, security_group)
30
30
  if security_group.securityGroupIngress.is_a? Hash
31
31
  security_group.securityGroupIngress = [security_group.securityGroupIngress]
32
32
  end
33
33
 
34
34
  security_group.ingresses = security_group.securityGroupIngress.map do |ingress|
35
35
  mapped_at_least_one_attribute = false
36
- ingress_object = AWS::EC2::SecurityGroupIngress.new
36
+ ingress_object = AWS::EC2::SecurityGroupIngress.new cfn_model
37
37
  ingress.each do |k,v|
38
38
  silently_fail do
39
39
  ingress_object.send("#{initialLower(k)}=", v)
@@ -45,7 +45,7 @@ class SecurityGroupParser
45
45
  end.reject { |ingress| ingress.nil? }
46
46
  end
47
47
 
48
- def objectify_egress(security_group)
48
+ def objectify_egress(cfn_model, security_group)
49
49
  if security_group.securityGroupEgress.is_a? Hash
50
50
  security_group.securityGroupEgress = [security_group.securityGroupEgress]
51
51
  end
@@ -53,7 +53,7 @@ class SecurityGroupParser
53
53
  security_group.egresses = security_group.securityGroupEgress.map do |egress|
54
54
  mapped_at_least_one_attribute = false
55
55
 
56
- egress_object = AWS::EC2::SecurityGroupEgress.new
56
+ egress_object = AWS::EC2::SecurityGroupEgress.new cfn_model
57
57
  egress.each do |k,v|
58
58
  next if k.match /::/
59
59
  silently_fail do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfn-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.19
4
+ version: 0.1.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Kascic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-14 00:00:00.000000000 Z
11
+ date: 2018-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kwalify
@@ -42,10 +42,10 @@ files:
42
42
  - lib/cfn-model/model/iam_policy.rb
43
43
  - lib/cfn-model/model/iam_role.rb
44
44
  - lib/cfn-model/model/iam_user.rb
45
- - lib/cfn-model/model/iam_user_to_group_addition.rb
46
45
  - lib/cfn-model/model/lambda_principal.rb
47
46
  - lib/cfn-model/model/load_balancer.rb
48
47
  - lib/cfn-model/model/model_element.rb
48
+ - lib/cfn-model/model/parameter.rb
49
49
  - lib/cfn-model/model/policy.rb
50
50
  - lib/cfn-model/model/policy_document.rb
51
51
  - lib/cfn-model/model/principal.rb
@@ -1,10 +0,0 @@
1
- require_relative 'model_element'
2
-
3
- class AWS::IAM::UserToGroupAddition < ModelElement
4
- attr_accessor :groupName, :users
5
-
6
- def initialize
7
- @users = []
8
- @resource_type = 'AWS::IAM::UserToGroupAddition'
9
- end
10
- end