rubycfn 0.4.1 → 0.4.2

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.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rubycfn (0.4.1)
4
+ rubycfn (0.4.2)
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.1]
64
+ \/ \/ \/ \/ \/ \/ [v0.4.2]
65
65
  Project name? example
66
66
  Account ID? 1234567890
67
67
  Select region EU (Frankfurt)
@@ -338,6 +338,98 @@ convert it to YAML format ;)
338
338
  To allow for SAM transformation use the 'transform' method inside your template.
339
339
  The transform method takes an optional argument, and defaults to "AWS::Serverless-2016-10-31"
340
340
 
341
+ ## Resource attributes
342
+
343
+ When creating a resource there are a couple of arguments that can be passed
344
+ along with it. In its most simple form a resource looks like this:
345
+
346
+ ```ruby
347
+ resource :my_resource,
348
+ type: "AWS::Some::Resource"
349
+ ```
350
+
351
+ The following arguments are supported:
352
+
353
+ * condition
354
+ * creation_policy
355
+ * deletion_policy
356
+ * depends_on
357
+ * metadata
358
+ * type
359
+ * update_policy
360
+ * update_replace_policy
361
+
362
+ To make a resource depend on another resource you can use the `depends_on`
363
+ argument as follows:
364
+
365
+ ```ruby
366
+ resource :my_resource,
367
+ depends_on: :some_other_resource,
368
+ type: "AWS::Some::Resource"
369
+ ```
370
+
371
+ or... if you want to make it dependant on multiple resources:
372
+
373
+ ```ruby
374
+ resource :my_resource,
375
+ depends_on: %i(some_other_resource yet_another_resource),
376
+ type: "AWS::Some::Resource"
377
+ ```
378
+
379
+ If you want to dynamically generate DependsOn, you can do that in this way:
380
+
381
+ ```ruby
382
+ resource :my_resource,
383
+ amount: 2,
384
+ type: "AWS::Some::Resource" do |r, index|
385
+ r.depends_on "SomeResource#{index}"
386
+ end
387
+ ```
388
+
389
+ The `depends_on` attribute and `r.depends_on` method can be used together.
390
+ The `r.depends_on` specified resources get appended to the `depends_on`
391
+ specified resources.
392
+
393
+ ## Manipulating the resource name
394
+
395
+ Common practice is to use a symbol as argument to the `resource` method. The
396
+ passed symbol is camel cased in the final CloudFormation template generation.
397
+ It is imaginable that you have a use case where you need to control the resource
398
+ name. There are two ways to achieve this.
399
+
400
+ The first method is to pass a string as resource name, rather than a symbol.
401
+ When you pass a string it will be taken as the literal resource name and not be
402
+ camel cased. E.g.:
403
+
404
+ ```ruby
405
+ resource "myAmazingResource",
406
+ type: "AWS::Some::Resource"
407
+ ```
408
+
409
+ The second method is to use the resource method `_id`:
410
+
411
+ ```ruby
412
+ resource :irrelevant_resource_name,
413
+ amount: 3,
414
+ type: "AWS::Some::Resource" do |r, index|
415
+ r._id "ResourceNameOverride#{index+1}"
416
+ end
417
+ ```
418
+
419
+ ## Resource validation
420
+
421
+ Rubycfn is shipped with a `CloudFormationResourceSpecification.json` file. It
422
+ is used to validate whether used properties are valid and if any mandatory
423
+ properties were omitted. The CloudFormation compiler will throw an error if
424
+ a mandatory property is missing or if an unknown property is specified. Note
425
+ that the CloudFormationResourceSpecification.json is not actively maintained
426
+ by me. It is maintained by AWS at:
427
+
428
+ https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-resource-specification.html
429
+
430
+ You can also place the `CloudFormationResourceSpecification.json` file in the
431
+ root of your project. It will override the one supplied by Rubycfn.
432
+
341
433
  ## License
342
434
 
343
435
  MIT License
data/lib/rubycfn.rb CHANGED
@@ -17,6 +17,11 @@ require "rubycfn/version"
17
17
  @variables = {}
18
18
  @global_variables = {}
19
19
 
20
+ @resource_specification = JSON.parse(File.open(File.join(File.dirname(__FILE__), "/../CloudFormationResourceSpecification.json")).read)
21
+ if File.file?("CloudFormationResourceSpecification.json")
22
+ @resource_specification = JSON.parse(File.open("CloudFormationResourceSpecification.json").read)
23
+ end
24
+
20
25
  # Monkey patching
21
26
  class Symbol
22
27
  def cfnize
@@ -380,6 +385,9 @@ module Rubycfn
380
385
  name = name.class == Symbol ? name.to_s.cfnize : name = name.to_s
381
386
  arguments[:type] =~ /^([A-Za-z0-9]*)\:\:/
382
387
  arguments[:cloud] ||= $1
388
+ resource_specification = TOPLEVEL_BINDING.eval("@resource_specification")
389
+
390
+ raise "#{arguments[:type]} is not a valid resource type" unless resource_specification["ResourceTypes"][arguments[:type].to_s] || arguments[:type] =~ /Rspec\:\:/ || arguments[:type] =~ /^Custom\:\:/ || arguments[:type] =~ /AWS\:\:Serverless\:\:/
383
391
 
384
392
  # Custom resource types are AWS resources
385
393
  if arguments[:cloud] == "Custom" || arguments[:cloud] == "Rspec"
@@ -413,6 +421,19 @@ module Rubycfn
413
421
 
414
422
  arguments[:depends_on] ||= []
415
423
  rendered_depends_on = TOPLEVEL_BINDING.eval("@depends_on").nil? && arguments[:depends_on] || arguments[:depends_on] + TOPLEVEL_BINDING.eval("@depends_on")
424
+ if resource_specification["ResourceTypes"][arguments[:type].to_s]
425
+ resource_specification = TOPLEVEL_BINDING.eval("@resource_specification")
426
+ known_properties = resource_specification["ResourceTypes"][arguments[:type].to_s]["Properties"].keys
427
+ mandatory_properties = []
428
+ known_properties.each do |prop|
429
+ mandatory_properties.push(prop) if resource_specification["ResourceTypes"][arguments[:type].to_s]["Properties"][prop]["Required"] == true
430
+ end
431
+ TOPLEVEL_BINDING.eval("@properties").each do |k, _v|
432
+ raise "Property `#{k}` for #{arguments[:type]} is not valid." unless known_properties.include? k.to_s
433
+ mandatory_properties.delete(k.to_s)
434
+ end
435
+ raise "Property #{mandatory_properties.join(", ")} is mandatory for #{arguments[:type]}" unless mandatory_properties.count.zero?
436
+ end
416
437
  res = {
417
438
  "#{name.to_s}#{i.zero? ? "" : resource_postpend}": {
418
439
  Properties: TOPLEVEL_BINDING.eval("@properties"),
@@ -1,4 +1,4 @@
1
1
  # Rubycfn version
2
2
  module Rubycfn
3
- VERSION = "0.4.1".freeze
3
+ VERSION = "0.4.2".freeze
4
4
  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.1
4
+ version: 0.4.2
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-19 00:00:00.000000000 Z
11
+ date: 2019-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: neatjson
@@ -259,6 +259,7 @@ files:
259
259
  - ".gitignore"
260
260
  - ".rubocop.yml"
261
261
  - CHANGELOG.md
262
+ - CloudFormationResourceSpecification.json
262
263
  - Gemfile
263
264
  - Gemfile.lock
264
265
  - README.md