cfn-model 0.1.19 → 0.1.20

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