cfndsl 0.12.11 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -770
- data/Rakefile +0 -1
- data/bin/cfndsl +37 -10
- data/lib/cfndsl.rb +3 -7
- data/lib/cfndsl/aws/resource_specification.json +16056 -0
- data/lib/cfndsl/globals.rb +19 -7
- data/lib/cfndsl/orchestration_template.rb +1 -1
- data/lib/cfndsl/patches.rb +98 -0
- data/lib/cfndsl/specification.rb +89 -0
- data/lib/cfndsl/types.rb +5 -2
- data/lib/cfndsl/version.rb +1 -1
- data/spec/aws/serverless_spec.rb +1 -1
- data/spec/cfndsl_spec.rb +1 -1
- data/spec/cli_spec.rb +14 -0
- data/spec/fixtures/serverless-api.json +1 -1
- data/spec/spec_helper.rb +3 -0
- data/spec/types_definition_spec.rb +43 -0
- metadata +7 -3
- data/lib/cfndsl/generate_types.rb +0 -154
data/lib/cfndsl/globals.rb
CHANGED
@@ -1,10 +1,22 @@
|
|
1
|
+
# Global variables to adjust CfnDsl behavior
|
1
2
|
module CfnDsl
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def disable_binding
|
6
|
+
@disable_binding = true
|
7
|
+
end
|
8
|
+
|
9
|
+
def disable_binding?
|
10
|
+
@disable_binding
|
11
|
+
end
|
12
|
+
|
13
|
+
def specification_file(file = nil)
|
14
|
+
@specification_file = file if file
|
15
|
+
@specification_file ||= File.join(ENV['HOME'], '.cfndsl/resource_specification.json')
|
16
|
+
@specification_file
|
17
|
+
end
|
18
|
+
|
19
|
+
def reserved_items
|
20
|
+
%w[Resource Parameter Output].freeze
|
9
21
|
end
|
10
22
|
end
|
@@ -28,7 +28,7 @@ module CfnDsl
|
|
28
28
|
resource_name = create_resource_def(resource, info)
|
29
29
|
parts = resource.split('::')
|
30
30
|
until parts.empty?
|
31
|
-
break if
|
31
|
+
break if CfnDsl.reserved_items.include? parts.first
|
32
32
|
abreve_name = parts.join('_')
|
33
33
|
if accessors.key? abreve_name
|
34
34
|
accessors.delete abreve_name # Delete potentially ambiguous names
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module CfnDsl
|
2
|
+
# Module for handling inconsistencies in the published resource specification from AWS
|
3
|
+
module Patches
|
4
|
+
# Missing/malformed resources from the resource specification
|
5
|
+
# rubocop:disable Metrics/MethodLength
|
6
|
+
def self.resources
|
7
|
+
{
|
8
|
+
'AWS::Serverless::Function' => {
|
9
|
+
'Properties' => {
|
10
|
+
'Handler' => { 'PrimitiveType' => 'String' },
|
11
|
+
'Runtime' => { 'PrimitiveType' => 'String' },
|
12
|
+
'CodeUri' => { 'PrimitiveType' => 'String' },
|
13
|
+
'Description' => { 'PrimitiveType' => 'String' },
|
14
|
+
'MemorySize' => { 'PrimitiveType' => 'Integer' },
|
15
|
+
'Timeout' => { 'PrimitiveType' => 'Integer' },
|
16
|
+
'Environment' => { 'PrimitiveType' => 'Json' },
|
17
|
+
'Events' => { 'PrimitiveType' => 'Json' },
|
18
|
+
'Policies' => { 'Type' => 'List', 'ItemType' => 'Policy' }
|
19
|
+
}
|
20
|
+
},
|
21
|
+
'AWS::Serverless::Api' => {
|
22
|
+
'Properties' => {
|
23
|
+
'StageName' => { 'PrimitiveType' => 'String' },
|
24
|
+
'DefinitionUri' => { 'PrimitiveType' => 'String' },
|
25
|
+
'CacheClusterEnabled' => { 'PrimitiveType' => 'Boolean' },
|
26
|
+
'CacheClusterSize' => { 'PrimitiveType' => 'String' },
|
27
|
+
'Variables' => { 'PrimitiveType' => 'Json' }
|
28
|
+
}
|
29
|
+
},
|
30
|
+
'AWS::Serverless::SimpleTable' => {
|
31
|
+
'Properties' => {
|
32
|
+
'PrimaryKey' => { 'Type' => 'PrimaryKey' },
|
33
|
+
'ProvisionedThroughput' => { 'Type' => 'ProvisionedThroughput' }
|
34
|
+
}
|
35
|
+
},
|
36
|
+
'AWS::SSM::Parameter' => {
|
37
|
+
'Properties' => {
|
38
|
+
'Name' => { 'PrimitiveType' => 'String' },
|
39
|
+
'Description' => { 'PrimitiveType' => 'String' },
|
40
|
+
'Type' => { 'PrimitiveType' => 'String' },
|
41
|
+
'Value' => { 'PrimitiveType' => 'String' }
|
42
|
+
}
|
43
|
+
},
|
44
|
+
'AWS::EC2::VPNGatewayConnection' => {
|
45
|
+
'Properties' => {
|
46
|
+
'Type' => { 'PrimitiveType' => 'String' },
|
47
|
+
'Tags' => { 'Type' => 'List', 'ItemType' => 'Tag' }
|
48
|
+
}
|
49
|
+
},
|
50
|
+
'AWS::EC2::EIPAssociation' => {
|
51
|
+
'Properties' => {
|
52
|
+
'AllocationId' => { 'PrimitiveType' => 'String' },
|
53
|
+
'EIP' => { 'PrimitiveType' => 'String' },
|
54
|
+
'InstanceId' => { 'PrimitiveType' => 'String' },
|
55
|
+
'NetworkInterfaceId' => { 'PrimitiveType' => 'String' },
|
56
|
+
'PrivateIpAddress' => { 'PrimitiveType' => 'String' }
|
57
|
+
}
|
58
|
+
},
|
59
|
+
'AWS::Config::ConfigurationRecorder' => {
|
60
|
+
'Properties' => {
|
61
|
+
'Name' => { 'PrimitiveType' => 'String' },
|
62
|
+
'RecordingGroup' => { 'Type' => 'RecordingGroup' },
|
63
|
+
'RoleARN' => { 'PrimitiveType' => 'String' }
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Missing/malformed types from the resource specification
|
70
|
+
def self.types
|
71
|
+
{
|
72
|
+
'AWS::Serverless::SimpleTable.PrimaryKey' => {
|
73
|
+
'Properties' => {
|
74
|
+
'Name' => { 'PrimitiveType' => 'String' },
|
75
|
+
'Type' => { 'PrimitiveType' => 'String' }
|
76
|
+
}
|
77
|
+
},
|
78
|
+
'AWS::Serverless::SimpleTable.ProvisionedThroughput' => {
|
79
|
+
'Properties' => {
|
80
|
+
'ReadCapacityUnits' => { 'PrimitiveType' => 'Integer' },
|
81
|
+
'WriteCapacityUnits' => { 'PrimitiveType' => 'Integer' }
|
82
|
+
}
|
83
|
+
},
|
84
|
+
'AWS::Serverless::Function.Policy' => {
|
85
|
+
'Properties' => {
|
86
|
+
'PolicyDocument' => { 'PrimitiveType' => 'Json' },
|
87
|
+
'PolicyName' => { 'PrimitiveType' => 'String' }
|
88
|
+
}
|
89
|
+
},
|
90
|
+
'AWS::Cognito::IdentityPoolRoleAttachment.RulesConfigurationType' => {
|
91
|
+
'Properties' => {
|
92
|
+
'Rules' => { 'Type' => 'List', 'ItemType' => 'MappingRule' }
|
93
|
+
}
|
94
|
+
}
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module CfnDsl
|
2
|
+
# Helper module for bridging the gap between a static types file included in the repo
|
3
|
+
# and dynamically generating the types directly from the AWS specification
|
4
|
+
module Specification
|
5
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
6
|
+
def self.extract_resources(spec)
|
7
|
+
spec.each_with_object({}) do |(resource_name, resource_info), resources|
|
8
|
+
properties = resource_info['Properties'].each_with_object({}) do |(property_name, property_info), extracted|
|
9
|
+
# some json incorrectly labelled as Type -> Json instead of PrimitiveType
|
10
|
+
# also, AWS now has the concept of Map which cfndsl had never defined
|
11
|
+
if property_info['Type'] == 'Map' || property_info['Type'] == 'Json'
|
12
|
+
property_type = 'Json'
|
13
|
+
elsif property_info['PrimitiveType']
|
14
|
+
property_type = property_info['PrimitiveType']
|
15
|
+
elsif property_info['PrimitiveItemType']
|
16
|
+
property_type = Array(property_info['PrimitiveItemType'])
|
17
|
+
elsif property_info['ItemType']
|
18
|
+
# Tag is a reused type, but not quite primitive
|
19
|
+
# and not all resources use the general form
|
20
|
+
property_type = if property_info['ItemType'] == 'Tag'
|
21
|
+
['Tag']
|
22
|
+
else
|
23
|
+
Array(resource_name.split('::').join + property_info['ItemType'])
|
24
|
+
end
|
25
|
+
elsif property_info['Type']
|
26
|
+
# Special types (defined below) are joined with their parent
|
27
|
+
# resource name for uniqueness and connection
|
28
|
+
property_type = resource_name.split('::').join + property_info['Type']
|
29
|
+
else
|
30
|
+
warn "could not extract type from #{resource_name}"
|
31
|
+
end
|
32
|
+
extracted[property_name] = property_type
|
33
|
+
extracted
|
34
|
+
end
|
35
|
+
resources[resource_name] = { 'Properties' => properties }
|
36
|
+
resources
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.extract_types(spec)
|
41
|
+
primitive_types = {
|
42
|
+
'String' => 'String',
|
43
|
+
'Boolean' => 'Boolean',
|
44
|
+
'Json' => 'Json',
|
45
|
+
'Integer' => 'Integer',
|
46
|
+
'Number' => 'Number',
|
47
|
+
'Double' => 'Double',
|
48
|
+
'Timestamp' => 'Timestamp',
|
49
|
+
'Long' => 'Long'
|
50
|
+
}
|
51
|
+
spec.each_with_object(primitive_types) do |(property_name, property_info), types|
|
52
|
+
# In order to name things uniquely and allow for connections
|
53
|
+
# we extract the resource name from the property
|
54
|
+
# AWS::IAM::User.Policy becomes AWSIAMUserPolicy
|
55
|
+
root_resource = property_name.match(/(.*)\./)
|
56
|
+
root_resource_name = root_resource ? root_resource[1].gsub(/::/, '') : property_name
|
57
|
+
property_name = property_name.gsub(/::|\./, '')
|
58
|
+
next unless property_info['Properties']
|
59
|
+
properties = property_info['Properties'].each_with_object({}) do |(nested_prop_name, nested_prop_info), extracted|
|
60
|
+
if nested_prop_info['Type'] == 'Map' || nested_prop_info['Type'] == 'Json'
|
61
|
+
# The Map type and the incorrectly labelled Json type
|
62
|
+
nested_prop_type = 'Json'
|
63
|
+
elsif nested_prop_info['PrimitiveType']
|
64
|
+
nested_prop_type = nested_prop_info['PrimitiveType']
|
65
|
+
elsif nested_prop_info['PrimitiveItemType']
|
66
|
+
nested_prop_type = Array(nested_prop_info['PrimitiveItemType'])
|
67
|
+
elsif nested_prop_info['ItemType']
|
68
|
+
nested_prop_type = root_resource_name + nested_prop_info['ItemType']
|
69
|
+
elsif nested_prop_info['Type']
|
70
|
+
nested_prop_type = root_resource_name + nested_prop_info['Type']
|
71
|
+
else
|
72
|
+
warn "could not extract type from #{property_name}"
|
73
|
+
end
|
74
|
+
extracted[nested_prop_name] = nested_prop_type
|
75
|
+
extracted
|
76
|
+
end
|
77
|
+
types[property_name] = properties
|
78
|
+
types
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.extract_from_resource_spec!
|
83
|
+
spec_file = JSON.parse File.read(CfnDsl.specification_file)
|
84
|
+
resources = extract_resources spec_file['ResourceTypes'].merge(Patches.resources)
|
85
|
+
types = extract_types spec_file['PropertyTypes'].merge(Patches.types)
|
86
|
+
{ 'Resources' => resources, 'Types' => types }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/cfndsl/types.rb
CHANGED
@@ -10,9 +10,12 @@ module CfnDsl
|
|
10
10
|
module Types
|
11
11
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
12
12
|
def self.included(type_def)
|
13
|
-
types_list =
|
13
|
+
types_list = if type_def::TYPE_PREFIX == 'aws'
|
14
|
+
Specification.extract_from_resource_spec!
|
15
|
+
else
|
16
|
+
YAML.safe_load(File.open("#{File.dirname(__FILE__)}/#{type_def::TYPE_PREFIX}/types.yaml"))
|
17
|
+
end
|
14
18
|
type_def.const_set('Types_Internal', types_list)
|
15
|
-
|
16
19
|
# Do a little sanity checking - all of the types referenced in Resources
|
17
20
|
# should be represented in Types
|
18
21
|
types_list['Resources'].keys.each do |resource_name|
|
data/lib/cfndsl/version.rb
CHANGED
data/spec/aws/serverless_spec.rb
CHANGED
data/spec/cfndsl_spec.rb
CHANGED
@@ -89,7 +89,7 @@ describe CfnDsl::CloudFormationTemplate do
|
|
89
89
|
PolicyDocument(a: 7)
|
90
90
|
end
|
91
91
|
|
92
|
-
expect(result2).to be_a(CfnDsl::AWS::Types::
|
92
|
+
expect(result2).to be_a(CfnDsl::AWS::Types::AWSIAMUserPolicy)
|
93
93
|
expect(user.instance_variable_get('@Properties')['Policies'].value.length).to eq(2)
|
94
94
|
end
|
95
95
|
|
data/spec/cli_spec.rb
CHANGED
@@ -13,6 +13,8 @@ describe 'cfndsl', type: :aruba do
|
|
13
13
|
-D, --define "VARIABLE=VALUE" Directly set local VARIABLE as VALUE
|
14
14
|
-v, --verbose Turn on verbose ouptut
|
15
15
|
-b, --disable-binding Disable binding configuration
|
16
|
+
-s, --specification-file FILE Location of Cloudformation Resource Specification file
|
17
|
+
-u, --update-specification Update the Cloudformation Resource Specification file
|
16
18
|
-h, --help Display this screen
|
17
19
|
USAGE
|
18
20
|
end
|
@@ -27,6 +29,17 @@ describe 'cfndsl', type: :aruba do
|
|
27
29
|
|
28
30
|
before(:each) { write_file('template.rb', template_content) }
|
29
31
|
|
32
|
+
context 'cfndsl -u' do
|
33
|
+
it 'updates the specification file' do
|
34
|
+
run 'cfndsl -u'
|
35
|
+
expect(last_command_started).to have_output_on_stderr(<<-OUTPUT.gsub(/^ {8}/, '').chomp)
|
36
|
+
Updating specification file
|
37
|
+
Specification successfully written to #{ENV['HOME']}/.cfndsl/resource_specification.json
|
38
|
+
OUTPUT
|
39
|
+
expect(last_command_started).to have_exit_status(0)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
30
43
|
context 'cfndsl' do
|
31
44
|
it 'displays the usage' do
|
32
45
|
run 'cfndsl'
|
@@ -134,6 +147,7 @@ describe 'cfndsl', type: :aruba do
|
|
134
147
|
it 'displays the variables as they are interpolated in the CloudFormation template' do
|
135
148
|
run_simple 'cfndsl template.rb --yaml params.yaml --verbose'
|
136
149
|
verbose = /
|
150
|
+
Using \s specification \s file .* \.json \n
|
137
151
|
Loading \s YAML \s file \s .* params\.yaml \n
|
138
152
|
Setting \s local \s variable \s DESC \s to \s yaml \n
|
139
153
|
Loading \s template \s file \s .* template.rb \n
|
@@ -1 +1 @@
|
|
1
|
-
{"AWSTemplateFormatVersion":"2010-09-09","Resources":{"Test":{"Properties":{"StageName":"prod","DefinitionUri":"swagger.yml","CacheClusterEnabled":false,"CacheClusterSize":"512M","Variables":{"Var1":"value1"}},"Type":"AWS::Serverless::
|
1
|
+
{"AWSTemplateFormatVersion":"2010-09-09","Resources":{"Test":{"Properties":{"StageName":"prod","DefinitionUri":"swagger.yml","CacheClusterEnabled":false,"CacheClusterSize":"512M","Variables":{"Var1":"value1"}},"Type":"AWS::Serverless::Api"}}}
|
data/spec/spec_helper.rb
CHANGED
@@ -9,6 +9,9 @@ if ENV['CFNDSL_COV']
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
require 'cfndsl/globals'
|
13
|
+
CfnDsl.specification_file File.expand_path('../../lib/cfndsl/aws/resource_specification.json', __FILE__)
|
14
|
+
# use local fixture for tests
|
12
15
|
require 'cfndsl'
|
13
16
|
|
14
17
|
bindir = File.expand_path('../../bin', __FILE__)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# This is a somewhat temporary test class to compare functionality
|
4
|
+
# between the AWS, OS and new ways of defining types
|
5
|
+
RSpec.describe 'Type Definitions' do
|
6
|
+
aws_spec = YAML.load_file File.expand_path('../../lib/cfndsl/aws/types.yaml', __FILE__)
|
7
|
+
os_spec = YAML.load_file File.expand_path('../../lib/cfndsl/os/types.yaml', __FILE__)
|
8
|
+
new_spec = CfnDsl::Specification.extract_from_resource_spec!
|
9
|
+
|
10
|
+
{ 'AWS' => aws_spec, 'OS' => os_spec, 'New' => new_spec }.each_pair do |cloud, specdef|
|
11
|
+
context cloud do
|
12
|
+
resources = specdef['Resources']
|
13
|
+
types = specdef['Types']
|
14
|
+
|
15
|
+
context 'Resources' do
|
16
|
+
resources.each do |name, info|
|
17
|
+
it "#{name} has all property types defined" do
|
18
|
+
properties = info['Properties']
|
19
|
+
properties.each do |_, type|
|
20
|
+
type = type.first if type.is_a?(Array)
|
21
|
+
expect(types).to have_key(type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'Types' do
|
28
|
+
types.each do |name, type|
|
29
|
+
it "#{name} has all property types defined" do
|
30
|
+
type = type.first if type.is_a?(Array)
|
31
|
+
if type.is_a?(String)
|
32
|
+
expect(types).to have_key(type)
|
33
|
+
elsif type.is_a?(Hash)
|
34
|
+
type.values.flatten.each { |t| expect(types).to have_key(t) }
|
35
|
+
else
|
36
|
+
raise "A defined type should only be of the form String, Array or Hash, got #{type.class}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfndsl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Jack
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-05-
|
12
|
+
date: 2017-05-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -46,13 +46,13 @@ files:
|
|
46
46
|
- cfndsl.gemspec
|
47
47
|
- lib/cfndsl.rb
|
48
48
|
- lib/cfndsl/aws/cloud_formation_template.rb
|
49
|
+
- lib/cfndsl/aws/resource_specification.json
|
49
50
|
- lib/cfndsl/aws/types.rb
|
50
51
|
- lib/cfndsl/aws/types.yaml
|
51
52
|
- lib/cfndsl/conditions.rb
|
52
53
|
- lib/cfndsl/creation_policy.rb
|
53
54
|
- lib/cfndsl/errors.rb
|
54
55
|
- lib/cfndsl/external_parameters.rb
|
55
|
-
- lib/cfndsl/generate_types.rb
|
56
56
|
- lib/cfndsl/globals.rb
|
57
57
|
- lib/cfndsl/json_serialisable_object.rb
|
58
58
|
- lib/cfndsl/jsonable.rb
|
@@ -65,11 +65,13 @@ files:
|
|
65
65
|
- lib/cfndsl/os/types.yaml
|
66
66
|
- lib/cfndsl/outputs.rb
|
67
67
|
- lib/cfndsl/parameters.rb
|
68
|
+
- lib/cfndsl/patches.rb
|
68
69
|
- lib/cfndsl/plurals.rb
|
69
70
|
- lib/cfndsl/properties.rb
|
70
71
|
- lib/cfndsl/rake_task.rb
|
71
72
|
- lib/cfndsl/ref_check.rb
|
72
73
|
- lib/cfndsl/resources.rb
|
74
|
+
- lib/cfndsl/specification.rb
|
73
75
|
- lib/cfndsl/types.rb
|
74
76
|
- lib/cfndsl/update_policy.rb
|
75
77
|
- lib/cfndsl/version.rb
|
@@ -114,6 +116,7 @@ files:
|
|
114
116
|
- spec/spec_helper.rb
|
115
117
|
- spec/support/shared_examples/orchestration_template.rb
|
116
118
|
- spec/transform_spec.rb
|
119
|
+
- spec/types_definition_spec.rb
|
117
120
|
homepage: https://github.com/stevenjack/cfndsl
|
118
121
|
licenses:
|
119
122
|
- MIT
|
@@ -165,3 +168,4 @@ test_files:
|
|
165
168
|
- spec/spec_helper.rb
|
166
169
|
- spec/support/shared_examples/orchestration_template.rb
|
167
170
|
- spec/transform_spec.rb
|
171
|
+
- spec/types_definition_spec.rb
|
@@ -1,154 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'cfndsl/jsonable'
|
3
|
-
require 'cfndsl/plurals'
|
4
|
-
require 'cfndsl/names'
|
5
|
-
|
6
|
-
module CfnDsl
|
7
|
-
# Type generation helper
|
8
|
-
module GenerateTypes
|
9
|
-
# declare classes for all of the types with named methods for setting the values
|
10
|
-
class Type < JSONable
|
11
|
-
end
|
12
|
-
|
13
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
14
|
-
def generate_types(filename)
|
15
|
-
types = YAML.safe_load(File.open(filename))
|
16
|
-
const_set('Types_Internal', types)
|
17
|
-
|
18
|
-
validate_types(types)
|
19
|
-
|
20
|
-
classes = {}
|
21
|
-
|
22
|
-
# Go through and declare all of the types first
|
23
|
-
types['Types'].each_key do |typename|
|
24
|
-
if !const_defined?(typename)
|
25
|
-
klass = const_set(typename, Class.new(self))
|
26
|
-
classes[typename] = klass
|
27
|
-
else
|
28
|
-
classes[typename] = const_get(typename)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Now go through them again and define attribute setter methods
|
33
|
-
classes.each_pair do |typename, type|
|
34
|
-
typeval = types['Types'][typename]
|
35
|
-
next unless typeval.respond_to?(:each_pair)
|
36
|
-
typeval.each_pair do |attr_name, attr_type|
|
37
|
-
if attr_type.is_a?(Array)
|
38
|
-
klass = const_get(attr_type[0])
|
39
|
-
variable = "@#{attr_name}".to_sym
|
40
|
-
|
41
|
-
method = CfnDsl::Plurals.singularize(attr_name)
|
42
|
-
methods = attr_name
|
43
|
-
all_methods = CfnDsl.method_names(method) + CfnDsl.method_names(methods)
|
44
|
-
type.class_eval do
|
45
|
-
all_methods.each do |method_name|
|
46
|
-
define_method(method_name) do |value = nil, *rest, &block|
|
47
|
-
existing = instance_variable_get(variable)
|
48
|
-
# For no-op invocations, get out now
|
49
|
-
return existing if value.nil? && rest.empty? && !block
|
50
|
-
|
51
|
-
# We are going to modify the value in some
|
52
|
-
# way, make sure that we have an array to mess
|
53
|
-
# with if we start with nothing
|
54
|
-
existing = instance_variable_set(variable, []) unless existing
|
55
|
-
|
56
|
-
# special case for just a block, no args
|
57
|
-
if value.nil? && rest.empty? && block
|
58
|
-
val = klass.new
|
59
|
-
existing.push val
|
60
|
-
value.instance_eval(&block(val))
|
61
|
-
return existing
|
62
|
-
end
|
63
|
-
|
64
|
-
# Glue all of our parameters together into
|
65
|
-
# a giant array - flattening one level deep, if needed
|
66
|
-
array_params = []
|
67
|
-
if value.is_a?(Array)
|
68
|
-
value.each { |x| array_params.push x }
|
69
|
-
else
|
70
|
-
array_params.push value
|
71
|
-
end
|
72
|
-
unless rest.empty?
|
73
|
-
rest.each do |v|
|
74
|
-
if v.is_a?(Array)
|
75
|
-
array_params += rest
|
76
|
-
else
|
77
|
-
array_params.push v
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# Here, if we were given multiple arguments either
|
83
|
-
# as method [a,b,c], method(a,b,c), or even
|
84
|
-
# method( a, [b], c) we end up with
|
85
|
-
# array_params = [a,b,c]
|
86
|
-
#
|
87
|
-
# array_params will have at least one item
|
88
|
-
# unless the user did something like pass in
|
89
|
-
# a bunch of empty arrays.
|
90
|
-
if block
|
91
|
-
# TODO: Is this a bug? We don't do anything with the array conetns
|
92
|
-
array_params.each do |_array_params_value|
|
93
|
-
value = klass.new
|
94
|
-
existing.push value
|
95
|
-
value.instance_eval(&block(val)) if block
|
96
|
-
end
|
97
|
-
else
|
98
|
-
# List of parameters with no block -
|
99
|
-
# hope that the user knows what he is
|
100
|
-
# doing and stuff them into our existing
|
101
|
-
# array
|
102
|
-
# TODO: Is this a bug? We don't do anything with the array conetns
|
103
|
-
array_params.each do |_array_params_value|
|
104
|
-
existing.push value
|
105
|
-
end
|
106
|
-
end
|
107
|
-
return existing
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
else
|
112
|
-
klass = const_get(attr_type)
|
113
|
-
variable = "@#{attr_name}".to_sym
|
114
|
-
|
115
|
-
type.class_eval do
|
116
|
-
CfnDsl.method_names(attr_name) do |inner_method|
|
117
|
-
define_method(inner_method) do |value = nil, *_rest, &block|
|
118
|
-
value ||= klass.new
|
119
|
-
instance_variable_set(variable, value)
|
120
|
-
value.instance_eval(&block) if block
|
121
|
-
value
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
130
|
-
|
131
|
-
private
|
132
|
-
|
133
|
-
# Do a little sanity checking - all of the types referenced in Resources
|
134
|
-
# should be represented in Types
|
135
|
-
def validate_types(types)
|
136
|
-
types['Resources'].values.each do |resource|
|
137
|
-
resource.values.each do |thing|
|
138
|
-
thing.values.flatten.each do |type|
|
139
|
-
puts "unknown type #{type}" unless types['Types'].key?(type)
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# All of the type values should also be references
|
145
|
-
types['Types'].values do |type|
|
146
|
-
next unless type.respond_to?(:values)
|
147
|
-
|
148
|
-
type.values.each do |tv|
|
149
|
-
puts "unknown type #{tv}" unless types['Types'].key?(tv)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|