rubycfn 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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