humidifier 4.0.1 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/main.yml +34 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +59 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +125 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/CloudFormationResourceSpecification.json +107080 -30573
- data/Gemfile +5 -0
- data/Gemfile.lock +90 -0
- data/LICENSE +1 -1
- data/README.md +14 -4
- data/Rakefile +27 -0
- data/exe/humidifier +15 -0
- data/humidifier.gemspec +50 -0
- data/lib/humidifier/cli.rb +36 -20
- data/lib/humidifier/config/mapper.rb +1 -1
- data/lib/humidifier/config/mapping.rb +2 -2
- data/lib/humidifier/config.rb +1 -1
- data/lib/humidifier/directory.rb +5 -5
- data/lib/humidifier/loader.rb +104 -47
- data/lib/humidifier/output.rb +3 -3
- data/lib/humidifier/parameter.rb +2 -2
- data/lib/humidifier/props.rb +73 -51
- data/lib/humidifier/ref.rb +1 -1
- data/lib/humidifier/resource.rb +3 -3
- data/lib/humidifier/serializer.rb +1 -1
- data/lib/humidifier/stack.rb +6 -6
- data/lib/humidifier/upgrade.rb +39 -0
- data/lib/humidifier/version.rb +1 -1
- data/lib/humidifier.rb +33 -29
- metadata +49 -31
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
humidifier (4.2.0)
|
5
|
+
aws-sdk-cloudformation (~> 1.25)
|
6
|
+
aws-sdk-s3 (~> 1.48)
|
7
|
+
fast_underscore (~> 0.3)
|
8
|
+
nokogiri (~> 1.10)
|
9
|
+
thor (~> 1.0)
|
10
|
+
thor-hollaback (~> 0.2)
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: https://rubygems.org/
|
14
|
+
specs:
|
15
|
+
ast (2.4.2)
|
16
|
+
aws-eventstream (1.2.0)
|
17
|
+
aws-partitions (1.592.0)
|
18
|
+
aws-sdk-cloudformation (1.69.0)
|
19
|
+
aws-sdk-core (~> 3, >= 3.127.0)
|
20
|
+
aws-sigv4 (~> 1.1)
|
21
|
+
aws-sdk-core (3.131.1)
|
22
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
23
|
+
aws-partitions (~> 1, >= 1.525.0)
|
24
|
+
aws-sigv4 (~> 1.1)
|
25
|
+
jmespath (~> 1, >= 1.6.1)
|
26
|
+
aws-sdk-kms (1.56.0)
|
27
|
+
aws-sdk-core (~> 3, >= 3.127.0)
|
28
|
+
aws-sigv4 (~> 1.1)
|
29
|
+
aws-sdk-s3 (1.114.0)
|
30
|
+
aws-sdk-core (~> 3, >= 3.127.0)
|
31
|
+
aws-sdk-kms (~> 1)
|
32
|
+
aws-sigv4 (~> 1.4)
|
33
|
+
aws-sigv4 (1.5.0)
|
34
|
+
aws-eventstream (~> 1, >= 1.0.2)
|
35
|
+
docile (1.4.0)
|
36
|
+
fast_underscore (0.3.3)
|
37
|
+
hollaback (0.1.1)
|
38
|
+
jmespath (1.6.1)
|
39
|
+
minitest (5.15.0)
|
40
|
+
nokogiri (1.13.6-x86_64-darwin)
|
41
|
+
racc (~> 1.4)
|
42
|
+
parallel (1.22.1)
|
43
|
+
parser (3.1.2.0)
|
44
|
+
ast (~> 2.4.1)
|
45
|
+
racc (1.6.0)
|
46
|
+
rainbow (3.1.1)
|
47
|
+
rake (13.0.6)
|
48
|
+
regexp_parser (2.5.0)
|
49
|
+
rexml (3.2.5)
|
50
|
+
rubocop (1.30.1)
|
51
|
+
parallel (~> 1.10)
|
52
|
+
parser (>= 3.1.0.0)
|
53
|
+
rainbow (>= 2.2.2, < 4.0)
|
54
|
+
regexp_parser (>= 1.8, < 3.0)
|
55
|
+
rexml (>= 3.2.5, < 4.0)
|
56
|
+
rubocop-ast (>= 1.18.0, < 2.0)
|
57
|
+
ruby-progressbar (~> 1.7)
|
58
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
59
|
+
rubocop-ast (1.18.0)
|
60
|
+
parser (>= 3.1.1.0)
|
61
|
+
ruby-progressbar (1.11.0)
|
62
|
+
simplecov (0.21.2)
|
63
|
+
docile (~> 1.1)
|
64
|
+
simplecov-html (~> 0.11)
|
65
|
+
simplecov_json_formatter (~> 0.1)
|
66
|
+
simplecov-html (0.12.3)
|
67
|
+
simplecov_json_formatter (0.1.3)
|
68
|
+
thor (1.2.1)
|
69
|
+
thor-hollaback (0.2.1)
|
70
|
+
hollaback (~> 0.1)
|
71
|
+
thor (>= 0.19.1)
|
72
|
+
unicode-display_width (2.1.0)
|
73
|
+
webrick (1.7.0)
|
74
|
+
yard (0.9.28)
|
75
|
+
webrick (~> 1.7.0)
|
76
|
+
|
77
|
+
PLATFORMS
|
78
|
+
ruby
|
79
|
+
|
80
|
+
DEPENDENCIES
|
81
|
+
bundler (~> 2.0)
|
82
|
+
humidifier!
|
83
|
+
minitest (~> 5.13)
|
84
|
+
rake (~> 13.0)
|
85
|
+
rubocop (~> 1.24)
|
86
|
+
simplecov (~> 0.17)
|
87
|
+
yard (~> 0.9)
|
88
|
+
|
89
|
+
BUNDLED WITH
|
90
|
+
2.2.3
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Humidifier
|
2
2
|
|
3
|
-
[![Build Status](https://
|
4
|
-
[![Gem Version](https://img.shields.io/gem/v/humidifier.svg
|
3
|
+
[![Build Status](https://github.com/kddnewton/humidifier/workflows/Main/badge.svg)](https://github.com/kddnewton/humidifier/actions)
|
4
|
+
[![Gem Version](https://img.shields.io/gem/v/humidifier.svg)](https://rubygems.org/gems/humidifier)
|
5
5
|
|
6
6
|
Humidifier is a ruby tool for managing [AWS CloudFormation](https://aws.amazon.com/cloudformation/) stacks. You can use it to build and manage stacks programmatically or you can use it as a command line tool to manage stacks through configuration files.
|
7
7
|
|
@@ -22,8 +22,10 @@ Humidifier is a ruby tool for managing [AWS CloudFormation](https://aws.amazon.c
|
|
22
22
|
- [`deploy [?stack] [*parameters]`](#deploy-stack-parameters)
|
23
23
|
- [`display [stack] [?pattern]`](#display-stack-pattern)
|
24
24
|
- [`stacks`](#stacks)
|
25
|
+
- [`upgrade`](#upgrade)
|
25
26
|
- [`upload [?stack]`](#upload-stack)
|
26
27
|
- [`validate [?stack]`](#validate-stack)
|
28
|
+
- [`version`](#version)
|
27
29
|
- [Parameters](#parameters)
|
28
30
|
- [Shortcuts](#shortcuts)
|
29
31
|
- [Automatic id properties](#automatic-id-properties)
|
@@ -190,7 +192,7 @@ AdminUser:
|
|
190
192
|
- Administration
|
191
193
|
```
|
192
194
|
|
193
|
-
The top-level keys are the logical resource names that will be displayed in the CloudFormation screen. They point to a map of key/value pairs that will be passed on to `humidifier`. Any `humidifier` (and therefore any CloudFormation) attribute may be specified. For more information on CloudFormation templates and which attributes may be specified, see both the [`humidifier` docs](http://
|
195
|
+
The top-level keys are the logical resource names that will be displayed in the CloudFormation screen. They point to a map of key/value pairs that will be passed on to `humidifier`. Any `humidifier` (and therefore any CloudFormation) attribute may be specified. For more information on CloudFormation templates and which attributes may be specified, see both the [`humidifier` docs](http://kddnewton.github.io/humidifier) and the [CloudFormation docs](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-guide.html).
|
194
196
|
|
195
197
|
### Mappers
|
196
198
|
|
@@ -259,6 +261,10 @@ Displays the specified stack in JSON format on the command line. If you optional
|
|
259
261
|
|
260
262
|
Displays the names of all of the stacks that `humidifier` is managing.
|
261
263
|
|
264
|
+
#### `upgrade`
|
265
|
+
|
266
|
+
Downloads the latest CloudFormation resource specification. Periodically AWS will update the file that `humidifier` is based on, in which case the attributes of the resources that were changed could change. This gem usually stays relatively in sync, but if you need to use the latest specs and this gem has not yet released a new version containing them, then you can run this command to download the latest specs onto your system.
|
267
|
+
|
262
268
|
#### `upload [?stack]`
|
263
269
|
|
264
270
|
Upload one or all stacks in the repo to S3 for reference later. Note that this must be combined with the `humidifier` `s3_bucket` configuration option.
|
@@ -267,6 +273,10 @@ Upload one or all stacks in the repo to S3 for reference later. Note that this m
|
|
267
273
|
|
268
274
|
Validate that one or all stacks in the repo are properly configured and using values that CloudFormation understands.
|
269
275
|
|
276
|
+
#### `version`
|
277
|
+
|
278
|
+
Output the version of `Humidifier` as well as the version of the CloudFormation resource specification that you are using.
|
279
|
+
|
270
280
|
### Parameters
|
271
281
|
|
272
282
|
CloudFormation template parameters can be specified by having a special `parameters.yml` file in your stack directory. This file should contain a YAML-encoded object whose keys are the names of the parameters and whose values are the parameter configuration (using the same underscore paradigm as `humidifier` resources for specifying configuration).
|
@@ -397,7 +407,7 @@ The specs pulled from the CFN docs is saved to `CloudFormationResourceSpecificat
|
|
397
407
|
|
398
408
|
### Contributing
|
399
409
|
|
400
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
410
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/kddnewton/humidifier.
|
401
411
|
|
402
412
|
### License
|
403
413
|
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "fileutils"
|
5
|
+
require "rake/testtask"
|
6
|
+
require "yard"
|
7
|
+
|
8
|
+
Rake::TestTask.new(:test) do |t|
|
9
|
+
t.libs << "test"
|
10
|
+
t.libs << "lib"
|
11
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
12
|
+
t.warning = false
|
13
|
+
end
|
14
|
+
|
15
|
+
task default: :test
|
16
|
+
|
17
|
+
YARD::Rake::YardocTask.new(:yard) do |t|
|
18
|
+
filepath = File.join("lib", "humidifier", "magic.rb")
|
19
|
+
|
20
|
+
t.stats_options = ["--list-undoc"]
|
21
|
+
t.before = lambda do
|
22
|
+
require "humidifier"
|
23
|
+
require_relative "yard/dynamic"
|
24
|
+
Dynamic.generate(filepath)
|
25
|
+
end
|
26
|
+
t.after = -> { FileUtils.rm(filepath) }
|
27
|
+
end
|
data/exe/humidifier
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# If there is a `bin/humidifier` file, then swap out execution of this default
|
5
|
+
# CLI with the custom CLI instead.
|
6
|
+
if File.file?(File.join("bin", "humidifier"))
|
7
|
+
exec(File.join("bin", "humidifier"))
|
8
|
+
end
|
9
|
+
|
10
|
+
require "bundler/setup"
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift File.expand_path(File.join("..", "lib"), __dir__)
|
13
|
+
require "humidifier"
|
14
|
+
|
15
|
+
Humidifier::CLI.start(ARGV)
|
data/humidifier.gemspec
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/humidifier/version"
|
4
|
+
|
5
|
+
version = Humidifier::VERSION
|
6
|
+
repository = "https://github.com/kddnewton/humidifier"
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = "humidifier"
|
10
|
+
spec.version = version
|
11
|
+
spec.authors = ["Kevin Newton"]
|
12
|
+
spec.email = ["kddnewton@gmail.com"]
|
13
|
+
|
14
|
+
spec.summary = "CloudFormation made easy"
|
15
|
+
spec.description = "Programmatically generate and manage AWS " \
|
16
|
+
"CloudFormation templates, stacks, and change sets."
|
17
|
+
spec.homepage = repository
|
18
|
+
spec.license = "MIT"
|
19
|
+
|
20
|
+
spec.metadata = {
|
21
|
+
"bug_tracker_uri" => "#{repository}/issues",
|
22
|
+
"changelog_uri" => "#{repository}/blob/v#{version}/CHANGELOG.md",
|
23
|
+
"source_code_uri" => repository,
|
24
|
+
"rubygems_mfa_required" => "true"
|
25
|
+
}
|
26
|
+
|
27
|
+
spec.files = Dir.chdir(__dir__) do
|
28
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
29
|
+
f.match(%r{^(bin|docs|example|test|yard)/})
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
spec.bindir = "exe"
|
34
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
35
|
+
spec.require_paths = ["lib"]
|
36
|
+
|
37
|
+
spec.add_dependency "aws-sdk-cloudformation", "~> 1.25"
|
38
|
+
spec.add_dependency "aws-sdk-s3", "~> 1.48"
|
39
|
+
spec.add_dependency "fast_underscore", "~> 0.3"
|
40
|
+
spec.add_dependency "nokogiri", "~> 1.10"
|
41
|
+
spec.add_dependency "thor", "~> 1.0"
|
42
|
+
spec.add_dependency "thor-hollaback", "~> 0.2"
|
43
|
+
|
44
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
45
|
+
spec.add_development_dependency "minitest", "~> 5.13"
|
46
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
47
|
+
spec.add_development_dependency "rubocop", "~> 1.24"
|
48
|
+
spec.add_development_dependency "simplecov", "~> 0.17"
|
49
|
+
spec.add_development_dependency "yard", "~> 0.9"
|
50
|
+
end
|
data/lib/humidifier/cli.rb
CHANGED
@@ -4,13 +4,13 @@ module Humidifier
|
|
4
4
|
# A CLI for running commands to manipulate the stacks that Humidifier knows
|
5
5
|
# about.
|
6
6
|
class CLI < Thor
|
7
|
-
class_option :aws_profile, desc:
|
8
|
-
aliases: [
|
7
|
+
class_option :aws_profile, desc: "The AWS profile to authenticate with",
|
8
|
+
aliases: ["-p"]
|
9
9
|
|
10
|
-
class_option :debug, desc:
|
10
|
+
class_option :debug, desc: "Sets up debug mode", aliases: ["-d"]
|
11
11
|
class_around :safe_execute
|
12
12
|
|
13
|
-
desc
|
13
|
+
desc "change [?stack]", "Create changesets for one or all stacks"
|
14
14
|
def change(name = nil)
|
15
15
|
authorize
|
16
16
|
|
@@ -22,10 +22,10 @@ module Humidifier
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
desc
|
26
|
-
option :wait, desc:
|
25
|
+
desc "deploy [?stack] [*parameters]", "Update one or all stacks"
|
26
|
+
option :wait, desc: "Wait for the stack to create/update",
|
27
27
|
type: :boolean, default: false
|
28
|
-
option :prefix, desc:
|
28
|
+
option :prefix, desc: "The prefix to use for the stack"
|
29
29
|
def deploy(name = nil, *parameters)
|
30
30
|
authorize
|
31
31
|
|
@@ -37,8 +37,8 @@ module Humidifier
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
desc
|
41
|
-
|
40
|
+
desc "display [stack] [?pattern]",
|
41
|
+
"Display the CloudFormation JSON for a given stack"
|
42
42
|
def display(name, pattern = nil)
|
43
43
|
directory = Directory.new(name, pattern: pattern && /#{pattern}/i)
|
44
44
|
|
@@ -46,13 +46,21 @@ module Humidifier
|
|
46
46
|
puts directory.to_cf
|
47
47
|
end
|
48
48
|
|
49
|
-
desc
|
49
|
+
desc "stacks", "List the stacks known to Humidifier"
|
50
50
|
def stacks
|
51
|
-
puts
|
51
|
+
puts "🗒 Listing stacks"
|
52
52
|
puts Humidifier.config.stack_names.sort.map { |name| "- #{name}" }
|
53
53
|
end
|
54
54
|
|
55
|
-
desc
|
55
|
+
desc "upgrade", "Download the latest CloudFormation resource specification"
|
56
|
+
def upgrade
|
57
|
+
print "💾 Downloading..."
|
58
|
+
|
59
|
+
version = Upgrade.perform
|
60
|
+
puts " upgraded to v#{version}"
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "upload [?stack]", "Upload one or all stacks to S3"
|
56
64
|
def upload(name = nil)
|
57
65
|
authorize
|
58
66
|
|
@@ -64,22 +72,30 @@ module Humidifier
|
|
64
72
|
end
|
65
73
|
end
|
66
74
|
|
67
|
-
desc
|
68
|
-
|
75
|
+
desc "validate [?stack]",
|
76
|
+
"Validate that one or all stacks are valid with CloudFormation"
|
69
77
|
def validate(name = nil)
|
70
78
|
authorize
|
71
79
|
|
72
|
-
print
|
80
|
+
print "🔍 Validating... "
|
73
81
|
|
74
82
|
valid =
|
75
83
|
stack_names_from(name).all? do |stack_name|
|
76
84
|
Directory.new(stack_name).valid?
|
77
85
|
end
|
78
86
|
|
79
|
-
puts valid ?
|
87
|
+
puts valid ? "Valid." : "Invalid."
|
88
|
+
end
|
89
|
+
|
90
|
+
desc "version", "Display the version of Humidifier"
|
91
|
+
def version
|
92
|
+
filepath = File.expand_path("../../#{SPECIFICATION}", __dir__)
|
93
|
+
version = JSON.parse(File.read(filepath))["ResourceSpecificationVersion"]
|
94
|
+
|
95
|
+
puts "📦 CloudFormation specification v#{version}"
|
80
96
|
end
|
81
97
|
|
82
|
-
no_commands do
|
98
|
+
no_commands do
|
83
99
|
def authorize
|
84
100
|
return unless options[:aws_profile]
|
85
101
|
|
@@ -89,14 +105,14 @@ module Humidifier
|
|
89
105
|
|
90
106
|
def parameters_from(opts)
|
91
107
|
opts.map do |opt|
|
92
|
-
key, value = opt.split(
|
108
|
+
key, value = opt.split("=")
|
93
109
|
{ parameter_key: key, parameter_value: value }
|
94
110
|
end
|
95
111
|
end
|
96
112
|
|
97
113
|
def prelude
|
98
114
|
command = @_invocations.values.dig(0, 0)
|
99
|
-
command = command ? "#{command} " :
|
115
|
+
command = command ? "#{command} " : ""
|
100
116
|
puts "\033[1mhumidifier #{command}v#{VERSION}\033[0m"
|
101
117
|
end
|
102
118
|
|
@@ -110,7 +126,7 @@ module Humidifier
|
|
110
126
|
puts error.message
|
111
127
|
exit 1
|
112
128
|
else
|
113
|
-
puts
|
129
|
+
puts "✨ Done in %.2fs." % (Time.now.to_f - start)
|
114
130
|
end
|
115
131
|
|
116
132
|
def stack_names_from(name)
|
@@ -83,7 +83,7 @@ module Humidifier
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
-
def mapped_from(clazz, key, value)
|
86
|
+
def mapped_from(clazz, key, value)
|
87
87
|
if self.class.attribute_methods.include?(key.to_sym)
|
88
88
|
# The given attribute name has been defined using the `::attribute`
|
89
89
|
# DSL method, so send the given value to that method and return the
|
@@ -10,7 +10,7 @@ module Humidifier
|
|
10
10
|
raise Error, "Invalid resource: #{opts[:to].inspect}" if @clazz.nil?
|
11
11
|
|
12
12
|
if opts[:using] && block_given?
|
13
|
-
raise Error,
|
13
|
+
raise Error, "Cannot specify :using and provide an anonymous mapper"
|
14
14
|
end
|
15
15
|
|
16
16
|
@mapper = mapper_from(opts, &block)
|
@@ -33,7 +33,7 @@ module Humidifier
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def normalized(name)
|
36
|
-
name.start_with?(
|
36
|
+
name.start_with?("AWS") ? name : "AWS::#{name}"
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
data/lib/humidifier/config.rb
CHANGED
data/lib/humidifier/directory.rb
CHANGED
@@ -82,7 +82,7 @@ module Humidifier
|
|
82
82
|
begin
|
83
83
|
parameter_filepath =
|
84
84
|
Humidifier.config.files_for(name).detect do |filepath|
|
85
|
-
File.basename(filepath,
|
85
|
+
File.basename(filepath, ".yml") == "parameters"
|
86
86
|
end
|
87
87
|
|
88
88
|
parameter_filepath ? parameters_from(parameter_filepath) : {}
|
@@ -94,7 +94,7 @@ module Humidifier
|
|
94
94
|
return {} unless loaded
|
95
95
|
|
96
96
|
loaded.each_with_object({}) do |(name, opts), params|
|
97
|
-
opts = opts.
|
97
|
+
opts = opts.to_h { |key, value| [key.to_sym, value] }
|
98
98
|
params[name] = Parameter.new(opts)
|
99
99
|
end
|
100
100
|
end
|
@@ -109,7 +109,7 @@ module Humidifier
|
|
109
109
|
loaded.each_with_object({}) do |(name, attributes), resources|
|
110
110
|
next if pattern && name !~ pattern
|
111
111
|
|
112
|
-
attribute = attributes.delete(
|
112
|
+
attribute = attributes.delete("export")
|
113
113
|
exports << Export.new(name, attribute) if attribute
|
114
114
|
|
115
115
|
resources[name] = mapping.resource_for(name, attributes)
|
@@ -120,10 +120,10 @@ module Humidifier
|
|
120
120
|
filepaths = Humidifier.config.files_for(name)
|
121
121
|
|
122
122
|
filepaths.each_with_object({}) do |filepath, resources|
|
123
|
-
basename = File.basename(filepath,
|
123
|
+
basename = File.basename(filepath, ".yml")
|
124
124
|
|
125
125
|
# Explicitly skip past parameters so we can pull them out later
|
126
|
-
next if basename ==
|
126
|
+
next if basename == "parameters"
|
127
127
|
|
128
128
|
resources.merge!(parse(filepath, basename))
|
129
129
|
end
|
data/lib/humidifier/loader.rb
CHANGED
@@ -4,38 +4,58 @@ module Humidifier
|
|
4
4
|
# Reads the specs/CloudFormationResourceSpecification.json file and load each
|
5
5
|
# resource as a class
|
6
6
|
module Loader
|
7
|
-
|
8
|
-
|
9
|
-
class PropertyTypes
|
10
|
-
attr_reader :structs
|
7
|
+
class Compiler
|
8
|
+
attr_reader :specification, :property_types
|
11
9
|
|
12
|
-
def initialize(
|
13
|
-
@
|
14
|
-
end
|
10
|
+
def initialize(specification)
|
11
|
+
@specification = specification
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
# Set an initial value for each property types so that we can handle
|
14
|
+
# cycles in the specification
|
15
|
+
@property_types = specification["PropertyTypes"].to_h do |name, _|
|
16
|
+
[name, {}]
|
17
|
+
end
|
21
18
|
end
|
22
19
|
|
23
|
-
|
20
|
+
def compile
|
21
|
+
# Loop through every property type that's already defined and build up
|
22
|
+
# each of the properties into the list
|
23
|
+
property_types.each do |property_type_name, property_type|
|
24
|
+
prefix = property_type_name.split(".", 2).first
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
subspec = specification["PropertyTypes"].fetch(property_type_name)
|
27
|
+
subspec.fetch("Properties") { {} }.each do |property_name, property|
|
28
|
+
property = build_property(prefix, property_name, property)
|
29
|
+
property_type[property.name] = property if property
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Loop through every resource type in the specification and define a
|
34
|
+
# class for each one dynamically.
|
35
|
+
specification["ResourceTypes"].each do |aws_name, resource_type|
|
36
|
+
_top, group, resource = aws_name.split("::")
|
37
|
+
|
38
|
+
properties = {}
|
39
|
+
resource_type["Properties"].each do |property_name, property|
|
40
|
+
property = build_property(aws_name, property_name, property)
|
41
|
+
properties[property.name] = property if property
|
42
|
+
end
|
29
43
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
44
|
+
resource_class =
|
45
|
+
Class.new(Resource) do
|
46
|
+
self.aws_name = aws_name
|
47
|
+
self.props = properties
|
48
|
+
end
|
35
49
|
|
36
|
-
|
37
|
-
|
38
|
-
|
50
|
+
group_module =
|
51
|
+
if Humidifier.const_defined?(group)
|
52
|
+
Humidifier.const_get(group)
|
53
|
+
else
|
54
|
+
Humidifier.const_set(group, Module.new)
|
55
|
+
end
|
56
|
+
|
57
|
+
Humidifier.registry[aws_name] =
|
58
|
+
group_module.const_set(resource, resource_class)
|
39
59
|
end
|
40
60
|
|
41
61
|
Humidifier.registry.freeze
|
@@ -43,35 +63,72 @@ module Humidifier
|
|
43
63
|
|
44
64
|
private
|
45
65
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
66
|
+
def build_primitive(type, name, spec)
|
67
|
+
case type
|
68
|
+
in "Boolean"
|
69
|
+
Props::BooleanProp.new(name, spec)
|
70
|
+
in "Double"
|
71
|
+
Props::DoubleProp.new(name, spec)
|
72
|
+
in "Integer" | "Long"
|
73
|
+
Props::IntegerProp.new(name, spec)
|
74
|
+
in "Json"
|
75
|
+
Props::JsonProp.new(name, spec)
|
76
|
+
in "String"
|
77
|
+
Props::StringProp.new(name, spec)
|
78
|
+
in "Timestamp"
|
79
|
+
Props::TimestampProp.new(name, spec)
|
54
80
|
end
|
55
81
|
end
|
56
82
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
83
|
+
def build_property(prefix, name, spec)
|
84
|
+
case spec.transform_keys(&:to_sym)
|
85
|
+
in { PrimitiveType: type }
|
86
|
+
build_primitive(type, name, spec)
|
87
|
+
in { Type: "List", PrimitiveItemType: type }
|
88
|
+
Props::ListProp.new(name, spec, build_primitive(type, name, spec))
|
89
|
+
in { Type: "Map", PrimitiveItemType: type }
|
90
|
+
Props::MapProp.new(name, spec, build_primitive(type, name, spec))
|
91
|
+
in { Type: "List", ItemType: "List" }
|
92
|
+
# specifically calling this out since
|
93
|
+
# AWS::Rekognition::StreamProcessor.PolygonRegionsOfInterest has a
|
94
|
+
# nested list structure that otherwise breaks the compiler
|
95
|
+
Props::ListProp.new(name, spec, Props::ListProp.new(name, spec))
|
96
|
+
in { Type: "List", ItemType: item_type }
|
97
|
+
Props::ListProp.new(
|
98
|
+
name,
|
99
|
+
spec,
|
100
|
+
Props::StructureProp.new(name, spec,
|
101
|
+
property_type(prefix, item_type))
|
102
|
+
)
|
103
|
+
in { Type: "Map", ItemType: item_type }
|
104
|
+
Props::MapProp.new(
|
105
|
+
name,
|
106
|
+
spec,
|
107
|
+
Props::StructureProp.new(name, spec,
|
108
|
+
property_type(prefix, item_type))
|
109
|
+
)
|
110
|
+
in { Type: type }
|
111
|
+
Props::StructureProp.new(name, spec, property_type(prefix, type))
|
112
|
+
else # rubocop:disable Layout/IndentationWidth
|
113
|
+
# It's possible to hit this clause if the specification has a property
|
114
|
+
# that is not currently supported by CloudFormation. In this case,
|
115
|
+
# we're not going to create a property at all for it.
|
116
|
+
end
|
62
117
|
end
|
63
118
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
unless Humidifier.const_defined?(group)
|
69
|
-
Humidifier.const_set(group, Module.new)
|
119
|
+
def property_type(prefix, type)
|
120
|
+
property_types.fetch("#{prefix}.#{type}") do
|
121
|
+
property_types.fetch(type)
|
70
122
|
end
|
71
|
-
|
72
|
-
Humidifier.const_get(group).const_set(resource, resource_class)
|
73
|
-
Humidifier.registry[aws_name] = resource_class
|
74
123
|
end
|
75
124
|
end
|
125
|
+
|
126
|
+
# loop through the specs and register each class
|
127
|
+
def self.load
|
128
|
+
filepath = File.expand_path("../../#{SPECIFICATION}", __dir__)
|
129
|
+
return unless File.file?(filepath)
|
130
|
+
|
131
|
+
Compiler.new(JSON.parse(File.read(filepath))).compile
|
132
|
+
end
|
76
133
|
end
|
77
134
|
end
|
data/lib/humidifier/output.rb
CHANGED
@@ -11,9 +11,9 @@ module Humidifier
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def to_cf
|
14
|
-
{
|
15
|
-
cf[
|
16
|
-
cf[
|
14
|
+
{ "Value" => Serializer.dump(value) }.tap do |cf|
|
15
|
+
cf["Description"] = description if description
|
16
|
+
cf["Export"] = { "Name" => export_name } if export_name
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|