rubycfn 0.4.3 → 0.4.4

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: a68bc5a68e2c5d5e17afd182cc6584ecd7cbd2a0
4
- data.tar.gz: 8ea2e232f188466493fc1549a1bd4df5940b0d00
3
+ metadata.gz: ad6c0ee3940074c02e77ec200940b30cd04c88a9
4
+ data.tar.gz: 02df17c17bfb5d0a27065fa3507309426cd11fe0
5
5
  SHA512:
6
- metadata.gz: adb271c31360abcbb0526ecb9b967ff519aec21ba6a8f1cefb2f9405e4a468e2be8f3ed983e3b8b73ffa363fdf8a53c10b5939fb0510a13b6fbf93781534a332
7
- data.tar.gz: 93dcf56467cc1b90e6b6912488111b41b4d0e5f4203adef9ea02ff3656317566747665363f6a51801297302c50e221086de58182762cc310a0233918b3abb5f6
6
+ metadata.gz: 00960d82e125677be467a2578c08bbeb0396530d8e3d9c058398f30580886c98a2f0a8985cee19292bcd30f8e430856ce982a10cce5d25d8381997df6268ff7d
7
+ data.tar.gz: a3161e628b18490ac66aa330d6cc41ec3c4de798a68e5b350483f253e2452fdd6a9a02d54f197725b33223e0caa6093407dd27029762a047e672c4fab4d703d4
data/.rubocop.yml CHANGED
@@ -20,6 +20,9 @@ Metrics/AbcSize:
20
20
  - "test/**/*"
21
21
  - "spec/**/*"
22
22
 
23
+ Metrics/BlockNesting:
24
+ Enabled: false
25
+
23
26
  Metrics/ClassLength:
24
27
  Max: 500
25
28
  Exclude:
data/CHANGELOG.md CHANGED
@@ -2,7 +2,11 @@
2
2
  All notable changes to Rubycfn will be documented in this file.
3
3
  This project uses [Semantic Versioning](http://semver.org/).
4
4
 
5
- ## 0.4.4 (Next Release)
5
+ ## 0.4.5 (Next Release)
6
+
7
+ ## 0.4.4
8
+
9
+ * Added autocorrection to property names so that developers won't have to think about camel casing -- [@dennisvink][@dennisvink]
6
10
 
7
11
  ## 0.4.3
8
12
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rubycfn (0.4.3)
4
+ rubycfn (0.4.4)
5
5
  activesupport (~> 5.1.5)
6
6
  dotenv (~> 2.4.0)
7
7
  json (~> 2.1.0)
data/README.md CHANGED
@@ -61,7 +61,7 @@ __________ ____ __________________.___._________ _____________________
61
61
  | _/ | /| | _// | |/ \ \/ | __) | | _/
62
62
  | | \ | / | | \\____ |\ \____| \ | | \
63
63
  |____|_ /______/ |______ // ______| \______ /\___ / |______ /
64
- \/ \/ \/ \/ \/ \/ [v0.4.3]
64
+ \/ \/ \/ \/ \/ \/ [v0.4.4]
65
65
  Project name? example
66
66
  Account ID? 1234567890
67
67
  Select region EU (Frankfurt)
@@ -0,0 +1,206 @@
1
+ class Symbol
2
+ def cfnize
3
+ return self.to_s if self.to_s !~ /_/ && self.to_s =~ /[A-Z]+.*/
4
+ to_s.split("_").map(&:capitalize).join
5
+ end
6
+
7
+ def ref(attr = nil)
8
+ unless attr
9
+ return { Ref: to_s.split("_").map(&:capitalize).join }
10
+ end
11
+ attr = attr.class == String ? attr : attr.to_s.split("_").map(&:capitalize).join
12
+ {
13
+ "Fn::GetAtt": [
14
+ to_s.split("_").map(&:capitalize).join, attr
15
+ ]
16
+ }
17
+ end
18
+
19
+ def fntransform(parameters = nil)
20
+ raise "fntransform parameters must be of type Hash" unless parameters.class == Hash
21
+ {
22
+ "Fn::Transform": {
23
+ "Name": to_s.split("_").map(&:capitalize).join,
24
+ "Parameters": parameters
25
+ }
26
+ }
27
+ end
28
+ end
29
+
30
+ class Hash
31
+ def fnsplit(separator = "")
32
+ {
33
+ "Fn::Split": [
34
+ separator,
35
+ self
36
+ ]
37
+ }
38
+ end
39
+ end
40
+
41
+ class String
42
+ def cfnize
43
+ return self if self !~ /_/ && self =~ /[A-Z]+.*/
44
+ split("_").map(&:capitalize).join
45
+ end
46
+
47
+ def ref(attr = nil)
48
+ unless attr
49
+ return { Ref: self }
50
+ end
51
+ attr = attr.class == String ? attr : attr.to_s.split("_").map(&:capitalize).join
52
+ {
53
+ "Fn::GetAtt": [
54
+ self,
55
+ attr
56
+ ]
57
+ }
58
+ end
59
+
60
+ def fntransform(parameters = nil)
61
+ raise "fntransform parameters must be of type Hash" unless parameters.class == Hash
62
+ {
63
+ "Fn::Transform": {
64
+ "Name": self,
65
+ "Parameters": parameters
66
+ }
67
+ }
68
+ end
69
+
70
+ def fnsplit(separator = "")
71
+ {
72
+ "Fn::Split": [
73
+ separator,
74
+ self
75
+ ]
76
+ }
77
+ end
78
+
79
+ def fnbase64
80
+ {
81
+ "Fn::Base64": self
82
+ }
83
+ end
84
+
85
+ def fngetazs
86
+ {
87
+ "Fn::GetAZs": self
88
+ }
89
+ end
90
+
91
+ def fnsub(variable_map = nil)
92
+ unless variable_map
93
+ return { "Fn::Sub": self }
94
+ end
95
+ {
96
+ "Fn::Sub": [
97
+ self,
98
+ variable_map
99
+ ]
100
+ }
101
+ end
102
+
103
+ def fnimportvalue
104
+ {
105
+ "Fn::Import": self
106
+ }
107
+ end
108
+ alias_method :fnimport, :fnimportvalue
109
+ end
110
+
111
+ class Array
112
+ def fncidr
113
+ {
114
+ "Fn::Cidr": self
115
+ }
116
+ end
117
+ alias_method :cidr, :fncidr
118
+
119
+ def fnequals
120
+ {
121
+ "Fn::Equals": self
122
+ }
123
+ end
124
+
125
+ def fnand
126
+ {
127
+ "Fn::And": self
128
+ }
129
+ end
130
+
131
+ def fnif
132
+ {
133
+ "Fn::If": self
134
+ }
135
+ end
136
+
137
+ def fnnot
138
+ {
139
+ "Fn::Not": self
140
+ }
141
+ end
142
+
143
+ def fnor
144
+ {
145
+ "Fn::Or": self
146
+ }
147
+ end
148
+
149
+ def fnfindinmap(name = nil)
150
+ unshift(name.cfnize) if name
151
+ {
152
+ "Fn::FindInMap": self
153
+ }
154
+ end
155
+ alias_method :find_in_map, :fnfindinmap
156
+ alias_method :findinmap, :fnfindinmap
157
+
158
+ def fnjoin(separator = "")
159
+ {
160
+ "Fn::Join": [
161
+ separator,
162
+ self
163
+ ]
164
+ }
165
+ end
166
+
167
+ def fnselect(index = 0)
168
+ {
169
+ "Fn::Select": [
170
+ index,
171
+ self
172
+ ]
173
+ }
174
+ end
175
+ end
176
+
177
+ class ::Hash
178
+ # rubocop:disable Style/CaseEquality
179
+ # rubocop:disable Lint/UnusedBlockArgument
180
+ def deep_merge(second)
181
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
182
+ self.merge(second.to_h, &merger)
183
+ end
184
+ # rubocop:enable Style/CaseEquality
185
+ # rubocop:enable Lint/UnusedBlockArgument
186
+
187
+ def recursive_compact
188
+ delete_if do |k, v|
189
+ next if v == false || k =~ /Fn\:/
190
+ (v.respond_to?(:empty?) ? v.empty? : !v) || v.instance_of?(Hash) && v.recursive_compact.empty?
191
+ end
192
+ end
193
+
194
+ def compact
195
+ delete_if { |_k, v| v.nil? }
196
+ end
197
+
198
+ def fnselect(index = 0)
199
+ {
200
+ "Fn::Select": [
201
+ index,
202
+ self
203
+ ]
204
+ }
205
+ end
206
+ end
@@ -1,4 +1,4 @@
1
1
  # Rubycfn version
2
2
  module Rubycfn
3
- VERSION = "0.4.3".freeze
3
+ VERSION = "0.4.4".freeze
4
4
  end
data/lib/rubycfn.rb CHANGED
@@ -3,6 +3,7 @@ require "active_support/concern"
3
3
  require "json"
4
4
  require "neatjson"
5
5
  require "rubycfn/version"
6
+ require_relative "monkeypatch"
6
7
 
7
8
  @description = ""
8
9
  @transform = ""
@@ -22,214 +23,6 @@ if File.file?("CloudFormationResourceSpecification.json")
22
23
  @resource_specification = JSON.parse(File.open("CloudFormationResourceSpecification.json").read)
23
24
  end
24
25
 
25
- # Monkey patching
26
- class Symbol
27
- def cfnize
28
- return self.to_s if self.to_s !~ /_/ && self.to_s =~ /[A-Z]+.*/
29
- to_s.split("_").map(&:capitalize).join
30
- end
31
-
32
- def ref(attr = nil)
33
- unless attr
34
- return { Ref: to_s.split("_").map(&:capitalize).join }
35
- end
36
- attr = attr.class == String ? attr : attr.to_s.split("_").map(&:capitalize).join
37
- {
38
- "Fn::GetAtt": [
39
- to_s.split("_").map(&:capitalize).join, attr
40
- ]
41
- }
42
- end
43
-
44
- def fntransform(parameters = nil)
45
- raise "fntransform parameters must be of type Hash" unless parameters.class == Hash
46
- {
47
- "Fn::Transform": {
48
- "Name": to_s.split("_").map(&:capitalize).join,
49
- "Parameters": parameters
50
- }
51
- }
52
- end
53
- end
54
-
55
- class Hash
56
- def fnsplit(separator = "")
57
- {
58
- "Fn::Split": [
59
- separator,
60
- self
61
- ]
62
- }
63
- end
64
- end
65
-
66
- class String
67
- def cfnize
68
- return self if self !~ /_/ && self =~ /[A-Z]+.*/
69
- split("_").map(&:capitalize).join
70
- end
71
-
72
- def ref(attr = nil)
73
- unless attr
74
- return { Ref: self }
75
- end
76
- attr = attr.class == String ? attr : attr.to_s.split("_").map(&:capitalize).join
77
- {
78
- "Fn::GetAtt": [
79
- self,
80
- attr
81
- ]
82
- }
83
- end
84
-
85
- def fntransform(parameters = nil)
86
- raise "fntransform parameters must be of type Hash" unless parameters.class == Hash
87
- {
88
- "Fn::Transform": {
89
- "Name": self,
90
- "Parameters": parameters
91
- }
92
- }
93
- end
94
-
95
- def fnsplit(separator = "")
96
- {
97
- "Fn::Split": [
98
- separator,
99
- self
100
- ]
101
- }
102
- end
103
-
104
- def fnbase64
105
- {
106
- "Fn::Base64": self
107
- }
108
- end
109
-
110
- def fngetazs
111
- {
112
- "Fn::GetAZs": self
113
- }
114
- end
115
-
116
- def fnsub(variable_map = nil)
117
- unless variable_map
118
- return { "Fn::Sub": self }
119
- end
120
- {
121
- "Fn::Sub": [
122
- self,
123
- variable_map
124
- ]
125
- }
126
- end
127
-
128
- def fnimportvalue
129
- {
130
- "Fn::Import": self
131
- }
132
- end
133
- alias_method :fnimport, :fnimportvalue
134
- end
135
-
136
- class Array
137
- def fncidr
138
- {
139
- "Fn::Cidr": self
140
- }
141
- end
142
- alias_method :cidr, :fncidr
143
-
144
- def fnequals
145
- {
146
- "Fn::Equals": self
147
- }
148
- end
149
-
150
- def fnand
151
- {
152
- "Fn::And": self
153
- }
154
- end
155
-
156
- def fnif
157
- {
158
- "Fn::If": self
159
- }
160
- end
161
-
162
- def fnnot
163
- {
164
- "Fn::Not": self
165
- }
166
- end
167
-
168
- def fnor
169
- {
170
- "Fn::Or": self
171
- }
172
- end
173
-
174
- def fnfindinmap(name = nil)
175
- unshift(name.cfnize) if name
176
- {
177
- "Fn::FindInMap": self
178
- }
179
- end
180
- alias_method :find_in_map, :fnfindinmap
181
- alias_method :findinmap, :fnfindinmap
182
-
183
- def fnjoin(separator = "")
184
- {
185
- "Fn::Join": [
186
- separator,
187
- self
188
- ]
189
- }
190
- end
191
-
192
- def fnselect(index = 0)
193
- {
194
- "Fn::Select": [
195
- index,
196
- self
197
- ]
198
- }
199
- end
200
- end
201
-
202
- class ::Hash
203
- # rubocop:disable Style/CaseEquality
204
- # rubocop:disable Lint/UnusedBlockArgument
205
- def deep_merge(second)
206
- merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
207
- self.merge(second.to_h, &merger)
208
- end
209
- # rubocop:enable Style/CaseEquality
210
- # rubocop:enable Lint/UnusedBlockArgument
211
-
212
- def recursive_compact
213
- delete_if do |k, v|
214
- next if v == false || k =~ /Fn\:/
215
- (v.respond_to?(:empty?) ? v.empty? : !v) || v.instance_of?(Hash) && v.recursive_compact.empty?
216
- end
217
- end
218
-
219
- def compact
220
- delete_if { |_k, v| v.nil? }
221
- end
222
-
223
- def fnselect(index = 0)
224
- {
225
- "Fn::Select": [
226
- index,
227
- self
228
- ]
229
- }
230
- end
231
- end
232
-
233
26
  module Rubycfn
234
27
  extend ActiveSupport::Concern
235
28
 
@@ -421,6 +214,8 @@ module Rubycfn
421
214
 
422
215
  arguments[:depends_on] ||= []
423
216
  rendered_depends_on = TOPLEVEL_BINDING.eval("@depends_on").nil? && arguments[:depends_on] || arguments[:depends_on] + TOPLEVEL_BINDING.eval("@depends_on")
217
+ rendered_properties = TOPLEVEL_BINDING.eval("@properties")
218
+ autocorrected_properties = {}
424
219
  if resource_specification["ResourceTypes"][arguments[:type].to_s]
425
220
  resource_specification = TOPLEVEL_BINDING.eval("@resource_specification")
426
221
  known_properties = resource_specification["ResourceTypes"][arguments[:type].to_s]["Properties"].keys
@@ -428,14 +223,23 @@ module Rubycfn
428
223
  known_properties.each do |prop|
429
224
  mandatory_properties.push(prop) if resource_specification["ResourceTypes"][arguments[:type].to_s]["Properties"][prop]["Required"] == true
430
225
  end
431
- TOPLEVEL_BINDING.eval("@properties").each do |k, _v|
226
+ rendered_properties.each do |k, v|
432
227
  unless known_properties.include? k.to_s
433
- TOPLEVEL_BINDING.eval("@depends_on = []")
434
- TOPLEVEL_BINDING.eval("@properties = {}")
435
- raise "Property `#{k}` for #{arguments[:type]} is not valid."
228
+ # Can we fix it ? Maybe we can
229
+ autocorrected = known_properties.find { |prop| prop.casecmp(k.to_s).zero? }
230
+ if autocorrected.nil?
231
+ TOPLEVEL_BINDING.eval("@depends_on = []")
232
+ TOPLEVEL_BINDING.eval("@properties = {}")
233
+ rendered_properties = {}
234
+ raise "Property `#{k}` for #{arguments[:type]} is not valid."
235
+ end
236
+ rendered_properties.delete(k)
237
+ autocorrected_properties[autocorrected.to_sym] = v
238
+ mandatory_properties.delete(autocorrected.to_s)
436
239
  end
437
240
  mandatory_properties.delete(k.to_s)
438
241
  end
242
+ rendered_properties = rendered_properties.deep_merge(autocorrected_properties)
439
243
  unless mandatory_properties.count.zero?
440
244
  TOPLEVEL_BINDING.eval("@depends_on = []")
441
245
  TOPLEVEL_BINDING.eval("@properties = {}")
@@ -444,7 +248,7 @@ module Rubycfn
444
248
  end
445
249
  res = {
446
250
  "#{name.to_s}#{i.zero? ? "" : resource_postpend}": {
447
- Properties: TOPLEVEL_BINDING.eval("@properties"),
251
+ Properties: rendered_properties,
448
252
  Type: arguments[:type],
449
253
  Condition: arguments[:condition],
450
254
  UpdatePolicy: arguments[:update_policy],
@@ -28,6 +28,39 @@ describe Rubycfn do
28
28
  r.property(:some_arn) { :rspec_resource.ref(:arn) }
29
29
  r.property(:some_other_arn) { :rspec_resource.ref("FooBar") }
30
30
  end
31
+
32
+ resource :asellion_com_database_instance,
33
+ type: "AWS::RDS::DBInstance" do |r|
34
+ r.property(:db_instance_identifier) { "rspec" }
35
+ r.property(:allocated_storage) { 100 }
36
+ r.property(:dbinstanceclass) { "db.t2.small" }
37
+ r.property(:engine) { "mariadb" }
38
+ r.property(:master_username) { "rspecroot" }
39
+ r.property(:master_user_password) { "rubycfn<3" }
40
+ r.property(:db_name) { "MyAwesomeDatabase" }
41
+ r.property(:preferred_backup_window) { "01:00-01:30" }
42
+ r.property(:backup_retention_period) { 14 }
43
+ r.property(:availability_zone) { "eu-central-1b" }
44
+ r.property(:preferred_maintenance_window) { "sun:06:00-sun:06:30" }
45
+ r.property(:multi_az) { true }
46
+ r.property(:engine_version) { "10.1.34" }
47
+ r.property(:auto_minor_version_upgrade) { true }
48
+ r.property(:license_model) { "general-public-license" }
49
+ r.property(:publicly_accessible) { true }
50
+ r.property(:storage_type) { "gp2" }
51
+ r.property(:port) { 3306 }
52
+ r.property(:copy_tags_to_snapshot) { true }
53
+ r.property(:monitoring_interval) { 60 }
54
+ r.property(:enable_iam_database_authentication) { false }
55
+ r.property(:enable_performance_insights) { false }
56
+ r.property(:deletion_protection) { true }
57
+ r.property(:db_subnet_group_name) { "default-vpc-123456789" }
58
+ r.property(:vpc_security_groups) do
59
+ [
60
+ "sg-0xc0ff3e"
61
+ ]
62
+ end
63
+ end
31
64
  end
32
65
  end
33
66
 
@@ -153,6 +186,24 @@ describe Rubycfn do
153
186
  it { should eq ["FooBar", "fooBar"] }
154
187
  end
155
188
  end
189
+
190
+ context "Database resource exists" do
191
+ subject { resources }
192
+
193
+ it { should have_key "AsellionComDatabaseInstance" }
194
+
195
+ context "Database resource has the right autocorrected properties" do
196
+ let(:properties) { resources["AsellionComDatabaseInstance"]["Properties"] }
197
+ subject { properties }
198
+
199
+ it { should have_key "DBInstanceClass" }
200
+ it { should have_key "DBInstanceIdentifier" }
201
+ it { should have_key "DBName" }
202
+ it { should have_key "DBSubnetGroupName" }
203
+ it { should have_key "MultiAZ" }
204
+ it { should have_key "VPCSecurityGroups" }
205
+ end
206
+ end
156
207
  end
157
208
  end
158
209
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubycfn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dennis Vink
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-23 00:00:00.000000000 Z
11
+ date: 2019-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: neatjson
@@ -267,6 +267,7 @@ files:
267
267
  - bin/rubycfn
268
268
  - format.vim
269
269
  - lib/cli_methods.rb
270
+ - lib/monkeypatch.rb
270
271
  - lib/rubycfn.rb
271
272
  - lib/rubycfn/version.rb
272
273
  - rubycfn.gemspec