cfndsl 1.8.0 → 1.9.5
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/.github/workflows/spec.yml +3 -2
- data/.github/workflows/spec_latest.yml +23 -0
- data/.github/workflows/spec_publish.yml +2 -2
- data/CHANGELOG.md +34 -4
- data/Gemfile.lock +211 -138
- data/README.md +4 -3
- data/Rakefile +2 -1
- data/lib/cfndsl/aws/patches/500_BadTagsv13.0.0_patch.json +1 -0
- data/lib/cfndsl/aws/patches/500_BadTagsv16.2.0_patch.json +1 -0
- data/lib/cfndsl/aws/region_spec_urls.json +38 -0
- data/lib/cfndsl/aws/resource_specification.json +27175 -3552
- data/lib/cfndsl/globals.rb +32 -4
- data/lib/cfndsl/rake_task.rb +6 -6
- data/lib/cfndsl/runner.rb +6 -2
- data/lib/cfndsl/version.rb +1 -1
- data/lib/deep_merge/core.rb +2 -2
- data/spec/cli_spec.rb +15 -2
- data/spec/region_spec_availability_spec.rb +37 -0
- data/spec/region_spec_urls_spec.rb +83 -0
- metadata +7 -1
data/lib/cfndsl/globals.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'version'
|
|
4
|
+
require 'json'
|
|
4
5
|
require 'fileutils'
|
|
5
6
|
|
|
6
7
|
# Global variables to adjust CfnDsl behavior
|
|
@@ -10,8 +11,15 @@ module CfnDsl
|
|
|
10
11
|
|
|
11
12
|
module_function
|
|
12
13
|
|
|
13
|
-
AWS_SPECIFICATION_URL = 'https://d201a2mn26r7lk.cloudfront.net/%<version>s/gzip/CloudFormationResourceSpecification.json'
|
|
14
14
|
LOCAL_SPEC_FILE = File.expand_path('aws/resource_specification.json', __dir__)
|
|
15
|
+
REGION_SPEC_URLS_FILE = File.expand_path('aws/region_spec_urls.json', __dir__)
|
|
16
|
+
|
|
17
|
+
SPEC_PATH_SUFFIX = '%<version>s/gzip/CloudFormationResourceSpecification.json'
|
|
18
|
+
DEFAULT_SPEC_REGION = 'us-east-1'
|
|
19
|
+
|
|
20
|
+
def region_spec_urls
|
|
21
|
+
@region_spec_urls ||= JSON.parse(File.read(REGION_SPEC_URLS_FILE)).tap { |h| h.delete('_source') }.freeze
|
|
22
|
+
end
|
|
15
23
|
|
|
16
24
|
def disable_deep_merge
|
|
17
25
|
@disable_deep_merge = true
|
|
@@ -42,15 +50,35 @@ module CfnDsl
|
|
|
42
50
|
File.join(ENV['HOME'], '.cfndsl/resource_specification.json')
|
|
43
51
|
end
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
# Build the full specification URL for a given region and version
|
|
54
|
+
#
|
|
55
|
+
# @param region [String] AWS region code
|
|
56
|
+
# @param version [String] specification version or 'latest'
|
|
57
|
+
# @return [String] full URL to the CloudFormation resource specification
|
|
58
|
+
def spec_url_for_region(region, version:)
|
|
59
|
+
base_url = region_spec_urls.fetch(region) do
|
|
60
|
+
raise Error, "Unsupported region '#{region}'. Use CfnDsl.supported_spec_regions for a list of supported regions."
|
|
61
|
+
end
|
|
62
|
+
"#{base_url}/#{format(SPEC_PATH_SUFFIX, version: version)}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# List all supported regions for spec downloads
|
|
66
|
+
#
|
|
67
|
+
# @return [Array<String>] sorted list of supported region codes
|
|
68
|
+
def supported_spec_regions
|
|
69
|
+
region_spec_urls.keys.sort
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def update_specification_file(file: user_specification_file, version: nil, region: nil)
|
|
46
73
|
require 'open-uri'
|
|
47
74
|
version ||= 'latest'
|
|
75
|
+
region ||= DEFAULT_SPEC_REGION
|
|
48
76
|
FileUtils.mkdir_p File.dirname(file)
|
|
49
|
-
url =
|
|
77
|
+
url = spec_url_for_region(region, version: version)
|
|
50
78
|
content = URI.parse(url).open.read
|
|
51
79
|
version = JSON.parse(content)['ResourceSpecificationVersion'] if version == 'latest'
|
|
52
80
|
File.open(file, 'w') { |f| f.puts content }
|
|
53
|
-
{ file: file, version: version, url: url }
|
|
81
|
+
{ file: file, version: version, url: url, region: region }
|
|
54
82
|
rescue StandardError
|
|
55
83
|
raise "Failed updating specification file #{file} from #{url}"
|
|
56
84
|
end
|
data/lib/cfndsl/rake_task.rb
CHANGED
|
@@ -79,14 +79,14 @@ module CfnDsl
|
|
|
79
79
|
# then any existing file is considered sufficient, and 'latest' is the version used for downloading
|
|
80
80
|
#
|
|
81
81
|
# @todo Add capability to provide a user spec/patches dir
|
|
82
|
-
def specification(file:, name: nil, version: nil)
|
|
82
|
+
def specification(file:, name: nil, version: nil, region: nil)
|
|
83
83
|
if name
|
|
84
84
|
desc 'Update Resource Specification' unless ::Rake.application.last_description
|
|
85
85
|
task name, [:cfn_spec_version] => file
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
@spec_task = file(file, :cfn_spec_version) do |t, args|
|
|
89
|
-
update_specification(t.name, args.with_defaults(cfn_spec_version: version)[:cfn_spec_version])
|
|
89
|
+
update_specification(t.name, args.with_defaults(cfn_spec_version: version)[:cfn_spec_version], region: region)
|
|
90
90
|
end
|
|
91
91
|
@spec_task.define_singleton_method(:needed?) { true } # We always need to check
|
|
92
92
|
self
|
|
@@ -163,13 +163,13 @@ module CfnDsl
|
|
|
163
163
|
tasks.unshift(load_spec)
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
-
def update_specification(file, minimum_version)
|
|
167
|
-
if CfnDsl::Specification.update_required?(version: minimum_version, file: file)
|
|
166
|
+
def update_specification(file, minimum_version, region: nil)
|
|
167
|
+
if region || CfnDsl::Specification.update_required?(version: minimum_version, file: file)
|
|
168
168
|
|
|
169
169
|
safe_update_embedded_spec if file == CfnDsl::LOCAL_SPEC_FILE
|
|
170
170
|
|
|
171
|
-
result = CfnDsl.update_specification_file(file: file, version: minimum_version)
|
|
172
|
-
puts "Specification #{result[:file]} updated to version #{result[:version]}"
|
|
171
|
+
result = CfnDsl.update_specification_file(file: file, version: minimum_version, region: region)
|
|
172
|
+
puts "Specification #{result[:file]} updated to version #{result[:version]} (#{result[:region]})"
|
|
173
173
|
@updated_version = result[:version]
|
|
174
174
|
elsif minimum_version
|
|
175
175
|
verbose&.puts "Specification #{file} is already >= #{minimum_version}"
|
data/lib/cfndsl/runner.rb
CHANGED
|
@@ -64,6 +64,10 @@ module CfnDsl
|
|
|
64
64
|
options[:update_spec] = true
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
opts.on('-r', '--region REGION', 'AWS region for specification download (use with -u)') do |region|
|
|
68
|
+
options[:region] = region
|
|
69
|
+
end
|
|
70
|
+
|
|
67
71
|
opts.on('-g', '--generate RESOURCE_TYPE,RESOURCE_LOGICAL_NAME', 'Add resource type and logical name') do |r|
|
|
68
72
|
options[:lego] = true
|
|
69
73
|
options[:resources] = []
|
|
@@ -92,8 +96,8 @@ module CfnDsl
|
|
|
92
96
|
|
|
93
97
|
if options[:update_spec]
|
|
94
98
|
warn 'Updating specification file'
|
|
95
|
-
result = CfnDsl.update_specification_file(version: options[:spec_version])
|
|
96
|
-
warn "Specification #{result[:version]} successfully written to #{result[:file]}"
|
|
99
|
+
result = CfnDsl.update_specification_file(version: options[:spec_version], region: options[:region])
|
|
100
|
+
warn "Specification #{result[:version]} (#{result[:region]}) successfully written to #{result[:file]}"
|
|
97
101
|
end
|
|
98
102
|
|
|
99
103
|
if options[:assetversion]
|
data/lib/cfndsl/version.rb
CHANGED
data/lib/deep_merge/core.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# rubocop:disable Metrics/AbcSize, Metrics/BlockNesting, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
4
|
-
# rubocop:disable Metrics/ModuleLength, Metrics/PerceivedComplexity, Style/
|
|
4
|
+
# rubocop:disable Metrics/ModuleLength, Metrics/PerceivedComplexity, Style/Semicolon
|
|
5
5
|
#
|
|
6
6
|
# Totally borrowed from https://github.com/danielsdeleo/deep_merge
|
|
7
7
|
module DeepMerge
|
|
@@ -246,4 +246,4 @@ module DeepMerge
|
|
|
246
246
|
end
|
|
247
247
|
end
|
|
248
248
|
# rubocop:enable Metrics/AbcSize, Metrics/BlockNesting, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
249
|
-
# rubocop:enable Metrics/ModuleLength, Metrics/PerceivedComplexity, Style/
|
|
249
|
+
# rubocop:enable Metrics/ModuleLength, Metrics/PerceivedComplexity, Style/Semicolon
|
data/spec/cli_spec.rb
CHANGED
|
@@ -17,6 +17,7 @@ describe 'cfndsl', type: :aruba do
|
|
|
17
17
|
-s, --specification-file FILE Location of Cloudformation Resource Specification file
|
|
18
18
|
-u [VERSION], Update the Resource Specification file to latest, or specific version
|
|
19
19
|
--update-specification
|
|
20
|
+
-r, --region REGION AWS region for specification download (use with -u)
|
|
20
21
|
-g RESOURCE_TYPE,RESOURCE_LOGICAL_NAME,
|
|
21
22
|
--generate Add resource type and logical name
|
|
22
23
|
-a, --assetversion Print out the specification version
|
|
@@ -45,7 +46,7 @@ describe 'cfndsl', type: :aruba do
|
|
|
45
46
|
run_command "cfndsl -u #{WORKING_SPEC_VERSION}"
|
|
46
47
|
expect(last_command_started).to have_output_on_stderr(<<-OUTPUT.gsub(/^ {8}/, '').chomp)
|
|
47
48
|
Updating specification file
|
|
48
|
-
Specification #{WORKING_SPEC_VERSION} successfully written to #{ENV['HOME']}/.cfndsl/resource_specification.json
|
|
49
|
+
Specification #{WORKING_SPEC_VERSION} (us-east-1) successfully written to #{ENV['HOME']}/.cfndsl/resource_specification.json
|
|
49
50
|
OUTPUT
|
|
50
51
|
expect(last_command_started).to have_exit_status(0)
|
|
51
52
|
end
|
|
@@ -56,7 +57,19 @@ describe 'cfndsl', type: :aruba do
|
|
|
56
57
|
run_command 'cfndsl -u'
|
|
57
58
|
|
|
58
59
|
expected = %r{Updating specification file
|
|
59
|
-
Specification ([0-9]+\.){2}[0-9]+ successfully written to #{ENV['HOME']}/.cfndsl/resource_specification.json}
|
|
60
|
+
Specification ([0-9]+\.){2}[0-9]+ \(us-east-1\) successfully written to #{ENV['HOME']}/.cfndsl/resource_specification.json}
|
|
61
|
+
|
|
62
|
+
expect(last_command_started).to have_output_on_stderr(expected)
|
|
63
|
+
expect(last_command_started).to have_exit_status(0)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
context 'cfndsl -u --region us-east-2' do
|
|
68
|
+
it 'updates the specification file from the specified region' do
|
|
69
|
+
run_command 'cfndsl -u --region us-east-2'
|
|
70
|
+
|
|
71
|
+
expected = %r{Updating specification file
|
|
72
|
+
Specification ([0-9]+\.){2}[0-9]+ \(us-east-2\) successfully written to #{ENV['HOME']}/.cfndsl/resource_specification.json}
|
|
60
73
|
|
|
61
74
|
expect(last_command_started).to have_output_on_stderr(expected)
|
|
62
75
|
expect(last_command_started).to have_exit_status(0)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'uri'
|
|
6
|
+
|
|
7
|
+
describe 'Region specification availability', :network do
|
|
8
|
+
results = {}
|
|
9
|
+
|
|
10
|
+
before(:all) do
|
|
11
|
+
threads = CfnDsl.region_spec_urls.map do |region, base_url|
|
|
12
|
+
Thread.new do
|
|
13
|
+
url = URI.parse("#{base_url}/latest/gzip/CloudFormationResourceSpecification.json")
|
|
14
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
15
|
+
http.use_ssl = (url.scheme == 'https')
|
|
16
|
+
http.open_timeout = 5
|
|
17
|
+
http.read_timeout = 5
|
|
18
|
+
|
|
19
|
+
begin
|
|
20
|
+
response = http.request_head(url.path)
|
|
21
|
+
results[region] = { code: response.code.to_i, url: url.to_s }
|
|
22
|
+
rescue Net::OpenTimeout, Net::ReadTimeout
|
|
23
|
+
results[region] = { code: nil, url: url.to_s, timeout: true }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
threads.each(&:join)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
CfnDsl.region_spec_urls.each_key do |region|
|
|
31
|
+
it "#{region} has a reachable latest specification" do
|
|
32
|
+
result = results[region]
|
|
33
|
+
skip "Timed out connecting to #{region} (#{result[:url]}) — likely a network route issue" if result[:timeout]
|
|
34
|
+
expect(result[:code]).to eq(200), "Expected 200 for #{region} (#{result[:url]}), got #{result[:code]}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe CfnDsl do
|
|
6
|
+
describe '.spec_url_for_region' do
|
|
7
|
+
it 'builds the correct URL for a CloudFront-backed region' do
|
|
8
|
+
url = CfnDsl.spec_url_for_region('eu-west-1', version: '255.0.0')
|
|
9
|
+
expect(url).to eq('https://d3teyb21fexa9r.cloudfront.net/255.0.0/gzip/CloudFormationResourceSpecification.json')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'builds the correct URL for an S3-backed region' do
|
|
13
|
+
url = CfnDsl.spec_url_for_region('af-south-1', version: 'latest')
|
|
14
|
+
expect(url).to eq('https://cfn-resource-specifications-af-south-1-prod.s3.af-south-1.amazonaws.com/latest/gzip/CloudFormationResourceSpecification.json')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'builds the correct URL for the default region (us-east-1)' do
|
|
18
|
+
url = CfnDsl.spec_url_for_region('us-east-1', version: '7.1.0')
|
|
19
|
+
expect(url).to eq('https://d1uauaxba7bl26.cloudfront.net/7.1.0/gzip/CloudFormationResourceSpecification.json')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'raises CfnDsl::Error for an unsupported region' do
|
|
23
|
+
expect { CfnDsl.spec_url_for_region('mars-west-1', version: 'latest') }
|
|
24
|
+
.to raise_error(CfnDsl::Error, /Unsupported region 'mars-west-1'/)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe '.supported_spec_regions' do
|
|
29
|
+
it 'returns a sorted array of 35 region codes' do
|
|
30
|
+
regions = CfnDsl.supported_spec_regions
|
|
31
|
+
expect(regions).to be_an(Array)
|
|
32
|
+
expect(regions).to eq(regions.sort)
|
|
33
|
+
expect(regions.size).to eq(35)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'includes well-known regions' do
|
|
37
|
+
regions = CfnDsl.supported_spec_regions
|
|
38
|
+
expect(regions).to include('us-east-1', 'us-west-2', 'eu-west-1', 'ap-southeast-2')
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '.update_specification_file' do
|
|
43
|
+
let(:tmpdir) { Dir.mktmpdir }
|
|
44
|
+
let(:tmpfile) { File.join(tmpdir, 'spec.json') }
|
|
45
|
+
let(:spec_content) { '{"ResourceSpecificationVersion":"255.0.0","ResourceTypes":{},"PropertyTypes":{}}' }
|
|
46
|
+
|
|
47
|
+
before do
|
|
48
|
+
mock_io = double('io', read: spec_content)
|
|
49
|
+
mock_uri = double('uri', open: mock_io)
|
|
50
|
+
allow(URI).to receive(:parse).and_return(mock_uri)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
after { FileUtils.rm_rf(tmpdir) }
|
|
54
|
+
|
|
55
|
+
context 'with explicit region' do
|
|
56
|
+
it 'uses the region-specific URL' do
|
|
57
|
+
result = CfnDsl.update_specification_file(file: tmpfile, version: '255.0.0', region: 'eu-west-1')
|
|
58
|
+
expect(result[:url]).to eq('https://d3teyb21fexa9r.cloudfront.net/255.0.0/gzip/CloudFormationResourceSpecification.json')
|
|
59
|
+
expect(result[:region]).to eq('eu-west-1')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'writes the spec content to the file' do
|
|
63
|
+
CfnDsl.update_specification_file(file: tmpfile, version: '255.0.0', region: 'eu-west-1')
|
|
64
|
+
expect(File.exist?(tmpfile)).to be true
|
|
65
|
+
parsed = JSON.parse(File.read(tmpfile))
|
|
66
|
+
expect(parsed['ResourceSpecificationVersion']).to eq('255.0.0')
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'resolves version from content when version is latest' do
|
|
70
|
+
result = CfnDsl.update_specification_file(file: tmpfile, version: 'latest', region: 'us-east-1')
|
|
71
|
+
expect(result[:version]).to eq('255.0.0')
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
context 'without region (defaults to us-east-1)' do
|
|
76
|
+
it 'uses the us-east-1 URL' do
|
|
77
|
+
result = CfnDsl.update_specification_file(file: tmpfile, version: '255.0.0')
|
|
78
|
+
expect(result[:url]).to eq('https://d1uauaxba7bl26.cloudfront.net/255.0.0/gzip/CloudFormationResourceSpecification.json')
|
|
79
|
+
expect(result[:region]).to eq('us-east-1')
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
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: 1.
|
|
4
|
+
version: 1.9.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steven Jack
|
|
@@ -52,6 +52,7 @@ extensions: []
|
|
|
52
52
|
extra_rdoc_files: []
|
|
53
53
|
files:
|
|
54
54
|
- ".github/workflows/spec.yml"
|
|
55
|
+
- ".github/workflows/spec_latest.yml"
|
|
55
56
|
- ".github/workflows/spec_publish.yml"
|
|
56
57
|
- ".gitignore"
|
|
57
58
|
- ".rubocop.yml"
|
|
@@ -86,6 +87,7 @@ files:
|
|
|
86
87
|
- lib/cfndsl/aws/patches/700_SAM_Serverless_Function_InlineCode_patch.json
|
|
87
88
|
- lib/cfndsl/aws/patches/900_SageMakerTags_patch.json
|
|
88
89
|
- lib/cfndsl/aws/patches/901_SageMakerTags_patch.json
|
|
90
|
+
- lib/cfndsl/aws/region_spec_urls.json
|
|
89
91
|
- lib/cfndsl/aws/resource_specification.json
|
|
90
92
|
- lib/cfndsl/aws/types.rb
|
|
91
93
|
- lib/cfndsl/aws/types.yaml
|
|
@@ -168,6 +170,8 @@ files:
|
|
|
168
170
|
- spec/names_spec.rb
|
|
169
171
|
- spec/output_spec.rb
|
|
170
172
|
- spec/plurals_spec.rb
|
|
173
|
+
- spec/region_spec_availability_spec.rb
|
|
174
|
+
- spec/region_spec_urls_spec.rb
|
|
171
175
|
- spec/resource_name_spec.rb
|
|
172
176
|
- spec/resources_spec.rb
|
|
173
177
|
- spec/rule_spec.rb
|
|
@@ -228,6 +232,8 @@ test_files:
|
|
|
228
232
|
- spec/names_spec.rb
|
|
229
233
|
- spec/output_spec.rb
|
|
230
234
|
- spec/plurals_spec.rb
|
|
235
|
+
- spec/region_spec_availability_spec.rb
|
|
236
|
+
- spec/region_spec_urls_spec.rb
|
|
231
237
|
- spec/resource_name_spec.rb
|
|
232
238
|
- spec/resources_spec.rb
|
|
233
239
|
- spec/rule_spec.rb
|