cfndsl 0.12.11 → 0.13.0
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 +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
|