diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e66c468..adb5cc82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,13 @@ The format is based on [Keep a Changelog], and this project adheres to ### Changed - Use GitHub Actions for the CI build instead of Travis CI ([#353]). +- Templates compiled with `cfndsl` have a pretty format ([#356]). +- Update `cfndsl` requirement from < 1.0 to ~> 1 ([#356]). The changes in + version 1 are potentially breaking for projects using `cfndsl` templates. [Unreleased]: https://github.com/envato/stack_master/compare/v2.12.0...HEAD [#353]: https://github.com/envato/stack_master/pull/353 +[#356]: https://github.com/envato/stack_master/pull/356 ## [2.12.0] - 2020-10-22 diff --git a/features/compile_with_cfndsl.feature b/features/compile_with_cfndsl.feature new file mode 100644 index 00000000..ac86bddf --- /dev/null +++ b/features/compile_with_cfndsl.feature @@ -0,0 +1,69 @@ +Feature: Compile command with a CfnDsl template + + Scenario: Run compile stack on CfnDsl template + Given a file named "stack_master.yml" with: + """ + template_compilers: + rb: cfndsl + stacks: + us_east_1: + myapp_vpc: + template: myapp_vpc.rb + """ + And a directory named "parameters" + And a file named "parameters/myapp_vpc.yml" with: + """ + KeyName: my-key + compile_time_parameters: + cidr_block: 10.200.0.0/16 + """ + And a directory named "templates" + And a file named "templates/myapp_vpc.rb" with: + """ + CloudFormation do + Description "Test template" + + Parameter("KeyName") do + Description "Key name" + Type "String" + end + + VPC(:Vpc) do + CidrBlock external_parameters[:CidrBlock] + end + + Output(:VpcId) do + Description "A VPC ID" + Value Ref("Vpc") + end + end + """ + When I run `stack_master compile us-east-1 myapp-vpc` + Then the output should contain all of these lines: + | Executing compile on myapp-vpc in us-east-1 | + | "AWSTemplateFormatVersion": "2010-09-09", | + | "Description": "Test template", | + | "Parameters": { | + | "KeyName": { | + | "Type": "String" | + | "Description": "Key name" | + | } | + | }, | + | "Resources": { | + | "Vpc": { | + | "Properties": { | + | "CidrBlock": "10.200.0.0/16" | + | }, | + | "Type": "AWS::EC2::VPC" | + | } | + | }, | + | "Outputs": { | + | "VpcId": { | + | "Description": "A VPC ID", | + | "Value": { | + | "Ref": "Vpc" | + | } | + | } | + | } | + | } | + And the exit status should be 0 diff --git a/features/compile_with_sparkle_formation.feature b/features/compile_with_sparkle_formation.feature new file mode 100644 index 00000000..f673c5d3 --- /dev/null +++ b/features/compile_with_sparkle_formation.feature @@ -0,0 +1,71 @@ +Feature: Compile command with a SparkleFormation template + + Scenario: Run compile stack on SparkleFormation template + Given a file named "stack_master.yml" with: + """ + stacks: + us_east_1: + myapp_vpc: + template: myapp_vpc.rb + """ + And a directory named "parameters" + And a file named "parameters/myapp_vpc.yml" with: + """ + KeyName: my-key + compile_time_parameters: + cidr_block: 10.200.0.0/16 + """ + And a directory named "templates" + And a file named "templates/myapp_vpc.rb" with: + """ + SparkleFormation.new(:myapp_vpc, + compile_time_parameters: { cidr_block: { type: :string }}) do + description "Test template" + + parameters.key_name do + description 'Key name' + type 'String' + end + + resources.vpc do + type 'AWS::EC2::VPC' + properties do + cidr_block '10.200.0.0/16' + end + end + + outputs.vpc_id do + description 'A VPC ID' + value ref!(:vpc) + end + end + """ + When I run `stack_master compile us-east-1 myapp-vpc` + Then the output should contain all of these lines: + | Executing compile on myapp-vpc in us-east-1 | + | { | + | "Description": "Test template", | + | "Parameters": { | + | "KeyName": { | + | "Description": "Key name", | + | "Type": "String" | + | } | + | }, | + | "Resources": { | + | "Vpc": { | + | "Type": "AWS::EC2::VPC", | + | "Properties": { | + | "CidrBlock": "10.200.0.0/16" | + | } | + | } | + | }, | + | "Outputs": { | + | "VpcId": { | + | "Description": "A VPC ID", | + | "Value": { | + | "Ref": "Vpc" | + | } | + | } | + | } | + | } | + And the exit status should be 0 diff --git a/lib/stack_master/commands/compile.rb b/lib/stack_master/commands/compile.rb index 9857042b..2d0dcebb 100644 --- a/lib/stack_master/commands/compile.rb +++ b/lib/stack_master/commands/compile.rb @@ -5,7 +5,7 @@ class Compile include Commander::UI def perform - puts(proposed_stack.template_body) + StackMaster.stdout.puts(proposed_stack.template_body) end private diff --git a/lib/stack_master/template_compilers/cfndsl.rb b/lib/stack_master/template_compilers/cfndsl.rb index 31599cb2..801c3d70 100644 --- a/lib/stack_master/template_compilers/cfndsl.rb +++ b/lib/stack_master/template_compilers/cfndsl.rb @@ -2,14 +2,15 @@ module StackMaster::TemplateCompilers class Cfndsl def self.require_dependencies require 'cfndsl' + require 'json' end def self.compile(template_dir, template, compile_time_parameters, _compiler_options = {}) - CfnDsl.disable_binding CfnDsl::ExternalParameters.defaults.clear # Ensure there's no leakage across invocations CfnDsl::ExternalParameters.defaults(compile_time_parameters.symbolize_keys) template_file_path = File.join(template_dir, template) - ::CfnDsl.eval_file_with_extras(template_file_path).to_json + json_hash = ::CfnDsl.eval_file_with_extras(template_file_path).as_json + JSON.pretty_generate(json_hash) end StackMaster::TemplateCompiler.register(:cfndsl, self) diff --git a/lib/stack_master/template_compilers/sparkle_formation.rb b/lib/stack_master/template_compilers/sparkle_formation.rb index d779db9c..e4e932b2 100644 --- a/lib/stack_master/template_compilers/sparkle_formation.rb +++ b/lib/stack_master/template_compilers/sparkle_formation.rb @@ -22,7 +22,7 @@ def self.compile(template_dir, template, compile_time_parameters, compiler_optio sparkle_template.compile_state = create_state(definitions, compile_time_parameters) end - JSON.pretty_generate(sparkle_template) + JSON.pretty_generate(sparkle_template.dump) end private diff --git a/spec/fixtures/templates/rb/cfndsl/sample-ctp-repeated.rb b/spec/fixtures/templates/rb/cfndsl/sample-ctp-repeated.rb index efb9ad28..4da68513 100644 --- a/spec/fixtures/templates/rb/cfndsl/sample-ctp-repeated.rb +++ b/spec/fixtures/templates/rb/cfndsl/sample-ctp-repeated.rb @@ -10,7 +10,7 @@ Output(:One,FnBase64( Ref("One"))) EC2_Instance(:MyInstance) { - DisableApiTermination external_parameters["DisableApiTermination"] + DisableApiTermination external_parameters.fetch(:DisableApiTermination, "false") InstanceType external_parameters["InstanceType"] ImageId "ami-12345678" } diff --git a/spec/stack_master/template_compilers/cfndsl_spec.rb b/spec/stack_master/template_compilers/cfndsl_spec.rb index 28ee2101..94b907bb 100644 --- a/spec/stack_master/template_compilers/cfndsl_spec.rb +++ b/spec/stack_master/template_compilers/cfndsl_spec.rb @@ -36,7 +36,7 @@ def compile it 'does not leak compile time params across invocations' do expect { compile_time_parameters.delete("DisableApiTermination") - }.to change { JSON.parse(compile)["Resources"]["MyInstance"]["Properties"]["DisableApiTermination"] }.from('true').to(nil) + }.to change { JSON.parse(compile)["Resources"]["MyInstance"]["Properties"]["DisableApiTermination"] }.from('true').to('false') end end end diff --git a/stack_master.gemspec b/stack_master.gemspec index 69de0bcb..dd6a1d95 100644 --- a/stack_master.gemspec +++ b/stack_master.gemspec @@ -51,7 +51,7 @@ Gem::Specification.new do |spec| spec.add_dependency "sparkle_formation", "~> 3" spec.add_dependency "table_print" spec.add_dependency "deep_merge" - spec.add_dependency "cfndsl", "< 1.0" + spec.add_dependency "cfndsl", "~> 1" spec.add_dependency "multi_json" spec.add_dependency "hashdiff", "~> 1" spec.add_dependency "ejson_wrapper"