rubycfn 0.3.2 → 0.3.3

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -1
  3. data/Gemfile.lock +1 -1
  4. data/README.md +13 -40
  5. data/bin/rubycfn +56 -20
  6. data/lib/cli_methods.rb +5 -1
  7. data/lib/rubycfn/version.rb +1 -1
  8. data/lib/rubycfn.rb +0 -1
  9. data/templates/.env.erb +0 -1
  10. data/templates/.env.production.erb +6 -0
  11. data/templates/.env.rspec.erb +6 -0
  12. data/templates/.env.test.erb +5 -1
  13. data/templates/.gitignore.erb +82 -0
  14. data/templates/.gitlab-ci.yml.erb +75 -0
  15. data/templates/.rubocop.yml.erb +91 -0
  16. data/templates/Gemfile.erb +13 -5
  17. data/templates/Rakefile.erb +34 -1
  18. data/templates/aws_sdk.rb.erb +18 -0
  19. data/templates/compiler.rb.erb +61 -0
  20. data/templates/core_compile.rb.erb +6 -0
  21. data/templates/core_deploy.rb.erb +115 -0
  22. data/templates/core_diff.rb.erb +59 -0
  23. data/templates/core_upload.rb.erb +3 -0
  24. data/templates/dependencies.rb.erb +23 -0
  25. data/templates/deploy.rb.erb +53 -0
  26. data/templates/ecs_stack.rb.erb +12 -0
  27. data/templates/ecs_stack_concern.rb.erb +20 -0
  28. data/templates/global_variables.rb.erb +4 -0
  29. data/templates/helper_methods.rb.erb +3 -0
  30. data/templates/helpers.rb.erb +7 -0
  31. data/templates/main.rb.erb +4 -4
  32. data/templates/main_aws_helper.rb.erb +16 -0
  33. data/templates/parent_stack_spec.rb.erb +38 -0
  34. data/templates/project_concern.rb.erb +16 -50
  35. data/templates/project_stack.rb.erb +5 -2
  36. data/templates/shared_methods.rb.erb +38 -0
  37. data/templates/spec_helper.rb.erb +3 -1
  38. data/templates/subnets.rb.erb +18 -0
  39. data/templates/upload_stack.rb.erb +27 -0
  40. data/templates/vpc_concerns.rb.erb +87 -0
  41. data/templates/vpc_spec.rb.erb +40 -0
  42. data/templates/vpc_stack.rb.erb +12 -0
  43. metadata +30 -13
  44. data/lib/compound/resources.rb +0 -1
  45. data/lib/compound/vpc.rb +0 -90
  46. data/lib/compound.rb +0 -1
  47. data/templates/buildspec.yml.erb +0 -21
  48. data/templates/cfn2rubycfn.erb +0 -127
  49. data/templates/cicd.rb.erb +0 -91
  50. data/templates/compile.rb.erb +0 -18
  51. data/templates/example_stack_spec.rb.erb +0 -101
  52. data/templates/format.vim.erb +0 -3
@@ -0,0 +1,61 @@
1
+ require "git-revision"
2
+
3
+ def update_references(contents, environment, artifact_bucket)
4
+ contents["Resources"].map do |resource|
5
+ resource_name = resource.shift
6
+ resource_values = resource.shift
7
+ if resource_values["Type"] == "AWS::CloudFormation::Stack"
8
+ template_hash = @stack_hashes[resource_name.to_sym]
9
+ s3_url = "https://s3.amazonaws.com/#{artifact_bucket}/#{environment}-" \
10
+ "#{resource_name.downcase}-#{template_hash}.json"
11
+ resource_values["Properties"]["TemplateURL"] = s3_url
12
+ end
13
+ end
14
+ JSON.pretty_generate(JSON.parse(contents.to_json))
15
+ end
16
+
17
+ def inject_dummy_resource(stack)
18
+ hack_stack = JSON.parse(stack)
19
+ hack_stack["Resources"] = {} if hack_stack["Resources"].nil?
20
+ hack_stack["Resources"]["CloudFormationDummyResource"] = {
21
+ "Type": "AWS::CloudFormation::WaitConditionHandle",
22
+ "Metadata": {
23
+ "Comment": "Resource to update stack even if there are no changes",
24
+ "GitCommitHash": Git::Revision.commit
25
+ }
26
+ }
27
+ hack_stack.to_json
28
+ end
29
+
30
+ def compile_stacks(skip_creation = false)
31
+ stacks = {}
32
+ FileUtils.mkdir_p "build" unless skip_creation
33
+ Module.constants.select do |mod|
34
+ if mod =~ /Stack$/
35
+ send("include", Object.const_get("SharedConcerns"))
36
+ stacks[mod.to_sym] = send("include", Object.const_get(mod)).render_template("AWS")
37
+ end
38
+ end
39
+
40
+ stacks.each do |stack_name, stack|
41
+ stack = inject_dummy_resource(stack)
42
+ next if JSON.parse(stack)["Resources"].nil?
43
+ stack_to_md5(stack_name, stack)
44
+ unless skip_creation
45
+ puts "- Saved #{stack_name} to build/#{ENV["ENVIRONMENT"]}-#{stack_name.downcase}.json"
46
+ File.open("build/#{ENV["ENVIRONMENT"]}-#{stack_name.downcase}.json", "w") { |f| f.write(JSON.pretty_generate(JSON.parse(stack))) }
47
+ end
48
+ end
49
+
50
+ stacks.each do |stack_name, stack|
51
+ stack = inject_dummy_resource(stack)
52
+ next if JSON.parse(stack)["Resources"].nil?
53
+ stack = update_references(JSON.parse(stack), ENV["ENVIRONMENT"], ENV["ARTIFACT_BUCKET"])
54
+ stacks[stack_name] = stack
55
+ stack_to_md5(stack_name, stack)
56
+ unless skip_creation
57
+ File.open("build/#{ENV["ENVIRONMENT"]}-#{stack_name.downcase}.json", "w") { |f| f.write(JSON.pretty_generate(JSON.parse(stack))) }
58
+ end
59
+ end
60
+ stacks
61
+ end
@@ -0,0 +1,6 @@
1
+ require "digest/md5"
2
+ require "fileutils"
3
+
4
+ require_relative "../aws_helper/main"
5
+
6
+ compile_stacks
@@ -0,0 +1,115 @@
1
+ require "colorize"
2
+ require "digest/md5"
3
+ require "dotenv"
4
+ require "aws-sdk-s3"
5
+ require "aws-sdk"
6
+
7
+ require_relative "../aws_helper/main"
8
+
9
+ env_vars = load_env_vars
10
+
11
+ set_aws_credentials(
12
+ env_vars[:aws_region],
13
+ env_vars[:aws_access_key_id],
14
+ env_vars[:aws_secret_access_key]
15
+ )
16
+
17
+ s3_filename = get_parent_stack_s3_location(
18
+ env_vars[:artifact_bucket],
19
+ env_vars[:environment]
20
+ )
21
+
22
+ client = Aws::CloudFormation::Client.new
23
+
24
+ # Store previous CloudFormation events in an array, so that we don't output
25
+ # events from previous deploys.
26
+
27
+ stack_exists = false
28
+ previous_statuses = []
29
+ 80.times do
30
+ previous_events = get_prior_events(client, env_vars[:stack_name])
31
+ previous_statuses = previous_events.map(&:event_id)
32
+ stack_exists = previous_events.size.to_i.positive? ? true : false
33
+ break unless stack_exists
34
+
35
+ events_last_deploy = get_events_last_deploy(previous_events)
36
+ last_event = events_last_deploy.shift
37
+ break if last_event \
38
+ && (last_event.logical_resource_id == env_vars[:stack_name]) \
39
+ && (last_event.stack_name == env_vars[:stack_name]) \
40
+ && (DEPLOYABLE_STATES.include? last_event.resource_status)
41
+ puts "Stack is currently in #{last_event.resource_status} mode. Waiting for it to finish..." if last_event
42
+ sleep 15
43
+ end
44
+
45
+ if stack_exists
46
+ client.update_stack(
47
+ stack_name: env_vars[:stack_name],
48
+ template_url: s3_filename,
49
+ capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND),
50
+ # stack_policy_body: "StackPolicyBody",
51
+ # stack_policy_url: "StackPolicyURL",
52
+ tags: [
53
+ {
54
+ key: "team",
55
+ value: "<%= name.downcase %>"
56
+ }
57
+ ],
58
+ # client_request_token: "ClientRequestToken",
59
+ )
60
+ else
61
+ client.create_stack(
62
+ stack_name: env_vars[:stack_name],
63
+ template_url: s3_filename,
64
+ timeout_in_minutes: 60,
65
+ capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND),
66
+ on_failure: "ROLLBACK",
67
+ # stack_policy_body: "StackPolicyBody",
68
+ # stack_policy_url: "StackPolicyURL",
69
+ tags: [
70
+ {
71
+ key: "team",
72
+ value: "<%= name.downcase %>"
73
+ }
74
+ ],
75
+ # client_request_token: "ClientRequestToken",
76
+ enable_termination_protection: false
77
+ )
78
+ end
79
+
80
+ ## Rudimentary feedback for CI/CD
81
+ shown_log_lines = {}
82
+
83
+ 360.times do
84
+ resp = client.describe_stack_events(
85
+ stack_name: env_vars[:stack_name]
86
+ )
87
+ resp.stack_events.to_a.reverse.each_with_index do |event, index|
88
+ next if previous_statuses.include? event.event_id
89
+ @completed = false
90
+ @stack_name = event.stack_name
91
+ @logical_resource_id = event.logical_resource_id
92
+ @resource_type = event.resource_type
93
+ @timestamp = event.timestamp
94
+ @resource_status = event.resource_status
95
+ @resource_status_reason = event.resource_status_reason
96
+ log_line = "[#{@logical_resource_id.green}] #{@timestamp.to_s.blue}: " \
97
+ "#{@resource_status.white} #{@resource_status_reason.to_s.red}"
98
+ puts log_line unless shown_log_lines[log_line]
99
+ shown_log_lines[log_line] = true
100
+ if (@stack_name == env_vars[:stack_name]) \
101
+ && (@logical_resource_id == env_vars[:stack_name]) \
102
+ && (END_STATES.include? @resource_status) \
103
+ && (index + 1 == resp.stack_events.to_a.size)
104
+ @completed = true
105
+ end
106
+ end
107
+ if @completed
108
+ puts "==================================="
109
+ puts " STATUS: #{@resource_status} #{@resource_status_reason}"
110
+ puts "==================================="
111
+ raise "#{@resource_status} #{@resource_status_reason}" if FAILURE_STATES.include? @resource_status
112
+ break
113
+ end
114
+ sleep(10)
115
+ end
@@ -0,0 +1,59 @@
1
+ require "diffy"
2
+ require_relative "../aws_helper/main"
3
+
4
+ env_vars = load_env_vars
5
+
6
+ set_aws_credentials(
7
+ env_vars[:aws_region],
8
+ env_vars[:aws_access_key_id],
9
+ env_vars[:aws_secret_access_key]
10
+ )
11
+
12
+ client = Aws::CloudFormation::Client.new
13
+ template = client.get_template(
14
+ stack_name: "#{ENV["ENVIRONMENT"]}-#{ENV["PROJECT_NAME"]}"
15
+ )
16
+
17
+ s3 = Aws::S3::Resource.new(region: env_vars[:aws_region])
18
+ orig_template = {}
19
+
20
+ template = JSON.parse(template.template_body)
21
+ template["Resources"].each do |resource_name, content|
22
+ if content["Type"] == "AWS::CloudFormation::Stack"
23
+ stack_name = "#{ENV["PROJECT_NAME"].capitalize}Stack"
24
+ orig_template[stack_name] = JSON.pretty_generate(
25
+ JSON.parse(
26
+ template.to_json
27
+ )
28
+ )
29
+ end
30
+ next unless content["Type"] == "AWS::CloudFormation::Stack"
31
+ s3_filename = content["Properties"]["TemplateURL"].split("/").last
32
+ orig_template[resource_name] = JSON.pretty_generate(
33
+ JSON.parse(
34
+ s3.client.get_object(
35
+ bucket: env_vars[:artifact_bucket],
36
+ key: s3_filename
37
+ ).body.read
38
+ )
39
+ )
40
+ end
41
+
42
+ stacks = compile_stacks(true)
43
+ @stack_hashes.each do |stack_name, _hash|
44
+ new_template = JSON.pretty_generate(
45
+ JSON.parse(
46
+ stacks[stack_name]
47
+ )
48
+ )
49
+ diff = Diffy::Diff.new(
50
+ orig_template[stack_name.to_s], new_template
51
+ ).to_s(:color)
52
+
53
+ if diff.strip.empty?
54
+ puts "No difference between local #{stack_name} and remote #{stack_name}"
55
+ else
56
+ puts "Orig #{stack_name} vs #{stack_name}:"
57
+ puts diff
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ require_relative "../aws_helper/main"
2
+
3
+ upload_stacks
@@ -0,0 +1,23 @@
1
+ def load_env_vars
2
+ Dotenv.load(".env")
3
+ Dotenv.load(".env.#{ENV["ENVIRONMENT"]}")
4
+ Dotenv.load(".env.private")
5
+ check_dependencies
6
+ {
7
+ aws_region: ENV["AWS_REGION"],
8
+ aws_access_key_id: ENV["AWS_ACCESS_KEY_ID"],
9
+ aws_secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
10
+ artifact_bucket: ENV["ARTIFACT_BUCKET"],
11
+ environment: ENV["ENVIRONMENT"],
12
+ stack_name: ENV["STACK_NAME"]
13
+ }
14
+ end
15
+
16
+ def check_dependencies
17
+ ENV["AWS_REGION"] ||= ENV["AWS_DEFAULT_REGION"]
18
+ raise "AWS_REGION not set" unless ENV["AWS_REGION"]
19
+ raise "ARTIFACT_BUCKET not set" unless ENV["ARTIFACT_BUCKET"]
20
+ raise "ENVIRONMENT not set" unless ENV["ENVIRONMENT"]
21
+ raise "STACK_NAME not set" unless ENV["STACK_NAME"]
22
+ raise "AWS CREDENTIALS NOT SET" unless ENV["AWS_ACCESS_KEY_ID"] && ENV["AWS_SECRET_ACCESS_KEY"]
23
+ end
@@ -0,0 +1,53 @@
1
+ WAITING_STATES = %w(
2
+ CREATE_IN_PROGRESS DELETE_IN_PROGRESS ROLLBACK_IN_PROGRESS
3
+ UPDATE_COMPLETE_CLEANUP_IN_PROGRESS UPDATE_IN_PROGRESS
4
+ UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS UPDATE_ROLLBACK_IN_PROGRESS
5
+ ).freeze
6
+ SUCCESS_STATES = %w(CREATE_COMPLETE UPDATE_COMPLETE).freeze
7
+ FAILURE_STATES = %w(
8
+ CREATE_FAILED DELETE_FAILED UPDATE_ROLLBACK_FAILED
9
+ ROLLBACK_FAILED ROLLBACK_COMPLETE ROLLBACK_FAILED
10
+ UPDATE_ROLLBACK_COMPLETE UPDATE_ROLLBACK_FAILED
11
+ ).freeze
12
+ END_STATES = SUCCESS_STATES + FAILURE_STATES
13
+ DEPLOYABLE_STATES = %w(
14
+ CREATE_COMPLETE UPDATE_COMPLETE ROLLBACK_COMPLETE
15
+ UPDATE_ROLLBACK_COMPLETE
16
+ ).freeze
17
+
18
+ # Method to retrieve paste CF events.
19
+ # Arguments: Aws::Client and stack name
20
+ def get_prior_events(client, stack_name)
21
+ client.describe_stack_events(
22
+ stack_name: stack_name
23
+ ).stack_events.to_a
24
+ rescue => exception
25
+ raise exception unless exception.class == Aws::CloudFormation::Errors::ValidationError
26
+ []
27
+ end
28
+
29
+ def get_events_last_deploy(previous_events)
30
+ initiated = false
31
+ previous_events.map do |event|
32
+ initiated = true if event.resource_status_reason == "User Initiated"
33
+ initiated ? nil : event
34
+ end.to_a.compact
35
+ end
36
+
37
+ # Method to predict location of the s3 CloudFormation artifact
38
+ def get_parent_stack_s3_location(bucket, environment)
39
+ stacks = compile_stacks(true)
40
+ parent_stack = nil
41
+
42
+ stacks.each do |stack_name, stack|
43
+ next if JSON.parse(stack)["Resources"].nil?
44
+ JSON.parse(stack)["Resources"].each do |_resource, payload|
45
+ if payload["Type"] == "AWS::CloudFormation::Stack"
46
+ parent_stack = stack_name
47
+ break
48
+ end
49
+ end
50
+ end
51
+
52
+ "https://s3.amazonaws.com/#{bucket}/#{environment}-#{parent_stack.downcase}-#{@stack_hashes[parent_stack.to_sym]}.json"
53
+ end
@@ -0,0 +1,12 @@
1
+ module EcsStack
2
+ extend ActiveSupport::Concern
3
+ include Rubycfn
4
+
5
+ included do
6
+ include Concerns::GlobalVariables
7
+ include Concerns::SharedMethods
8
+ include EcsStack::EcsCluster
9
+
10
+ description generate_stack_description("EcsStack")
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module EcsStack
2
+ module EcsCluster
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ transform
7
+ parameter :vpc,
8
+ description: "VPC ID"
9
+
10
+ # Create an empty ECS Cluster to launch fargate bastions (or other things) in
11
+ resource :<%= name.downcase %>_ecs_cluster,
12
+ type: "AWS::ECS::Cluster"
13
+
14
+ output :<%= name.downcase %>_ecs_cluster,
15
+ value: "<%= name %>EcsCluster".ref
16
+ output :<%= name.downcase %>_ecs_cluster_arn,
17
+ value: "<%= name.downcase %>EcsCluster".ref("Arn")
18
+ end
19
+ end
20
+ end
@@ -7,6 +7,10 @@ module Concerns
7
7
  default: "test",
8
8
  global: true,
9
9
  value: ENV["ENVIRONMENT"]
10
+
11
+ variable :stack_name,
12
+ default: "#{environment}-<%= name %>",
13
+ value: ENV["STACK_NAME"]
10
14
  end
11
15
  end
12
16
  end
@@ -0,0 +1,3 @@
1
+ def file_to_inline(filename)
2
+ File.open(filename).read.split("\n")
3
+ end
@@ -0,0 +1,7 @@
1
+ def stack_to_md5(stack_name, stack)
2
+ @stack_hashes[stack_name] = Digest::MD5.hexdigest(JSON.pretty_generate(JSON.parse(stack.to_s)))
3
+ end
4
+
5
+ def generate_s3_filename(filename, hash)
6
+ "#{File.basename(filename, ".json")}-#{hash}.json"
7
+ end
@@ -6,7 +6,7 @@ Dotenv.load(".env.private")
6
6
  Dotenv.load(".env.#{ENV["ENVIRONMENT"]}")
7
7
 
8
8
  # Include all group concerns
9
- Dir[File.expand_path('../shared_concerns/', __FILE__) + '/*.rb'].sort.each do |file|
9
+ Dir[File.expand_path("../shared_concerns/", __FILE__) + "/**/*.rb"].sort.each do |file|
10
10
  require file
11
11
  end
12
12
 
@@ -21,9 +21,9 @@ module SharedConcerns
21
21
  end
22
22
 
23
23
  # Include all stack concerns
24
- Dir[File.expand_path('../stacks/', __FILE__) + '/*.rb'].sort.each do |file|
25
- subdir = File.basename(file, ".rb")
26
- Dir[File.expand_path('../stacks/', __FILE__) + "/#{subdir}/*.rb"].sort.each do |subfile|
24
+ Dir[File.expand_path("../stacks/", __FILE__) + "/**/*.rb"].sort.each do |file|
25
+ subdir = File.basename(file, ".rb")
26
+ Dir[File.expand_path("../stacks/", __dir__) + "/#{subdir}/**/*.rb"].sort.each do |subfile|
27
27
  require subfile
28
28
  end
29
29
  require file
@@ -0,0 +1,16 @@
1
+ require "aws-sdk-s3"
2
+ require "aws-sdk"
3
+ require "colorize"
4
+ require "digest/md5"
5
+ require "dotenv"
6
+ require "fileutils"
7
+ require "json"
8
+
9
+ require_relative "aws_sdk"
10
+ require_relative "compiler"
11
+ require_relative "dependencies"
12
+ require_relative "deploy"
13
+ require_relative "helpers"
14
+ require_relative "upload_stack"
15
+
16
+ @stack_hashes = {}
@@ -0,0 +1,38 @@
1
+ require "spec_helper"
2
+
3
+ require "rubycfn"
4
+ require "active_support/concern"
5
+ require_relative "../../lib/main.rb"
6
+
7
+ module ParentSpec
8
+ extend ActiveSupport::Concern
9
+ include Rubycfn
10
+
11
+ included do
12
+ description "<%= name %> RSpec"
13
+ include Concerns::GlobalVariables
14
+ include Concerns::SharedMethods
15
+ include <%= name %>Stack::Parent
16
+ end
17
+ end
18
+
19
+ ParentSpecCfn = include ParentSpec
20
+
21
+ describe ParentSpec do
22
+ RspecParentSpec = ParentSpecCfn.render_template
23
+ let(:template) { JSON.parse(RspecParentSpec) }
24
+
25
+ context "Renders template" do
26
+ subject { template }
27
+
28
+ it { should have_key "Resources" }
29
+
30
+ context "Has Required Resources" do
31
+ let(:resources) { template["Resources"] }
32
+ subject { resources }
33
+
34
+ it { should have_key "VpcStack" }
35
+ it { should have_key "EcsStack" }
36
+ end
37
+ end
38
+ end
@@ -1,59 +1,25 @@
1
+ require_relative "../vpc_stack/subnets"
2
+
1
3
  module <%= name %>Stack
2
- module Main
4
+ module Parent
3
5
  extend ActiveSupport::Concern
4
6
 
5
7
  included do
6
- # Example stack parameter
7
- #parameter :example_stack_parameter_name,
8
- # default: "example_value"
9
-
10
- # Example variables
11
- variable :example_variable,
12
- required: false,
13
- value: ENV["SOME_ENV_VAR"],
14
- default: "default_value"
15
-
16
- variable :cidr_block,
17
- default: "10.0.0.0/16"
18
- variable :enable_dns_support,
19
- default: true
20
- variable :enable_dns_hostnames,
21
- default: true
22
-
23
- # Example resource, custom name
24
- resource :my_example_s3_bucket,
25
- type: "AWS::S3::Bucket" do |r|
26
- #r.depends_on "SomeLogicalResourceName"
27
- r.property(:bucket_name) { example_variable }
8
+ resource :vpc_stack,
9
+ type: "AWS::CloudFormation::Stack" do |r|
10
+ r.property(:template_u_r_l) { "vpcstack" }
11
+ r.property(:timeout_in_minutes) { "5" }
28
12
  end
29
13
 
30
- # Example VPC, creates VPC, IGW, Route, and VPCGatewayAttachment
31
- # Settable variables:
32
- # - cidr_block (defaults to 10.0.0.0/16)
33
- # - enable_dns_support (boolean, default true)
34
- # - enable_dns_hostnames (boolean, default true)
35
- # - instance_tenancy (`default` or `dedicated`)
36
- resource :my_first,
37
- type: RubyCfn::VPC do |r|
38
- r.set(:cidr_block) { "10.1.0.0/16" }
39
- end
40
-
41
- # Example resource, dynamically generated name
42
- resource :my_example_s3_bucket,
43
- type: "AWS::S3::Bucket"
44
-
45
- # Multiple resources of the same type
46
- resource :my_sqs_queue,
47
- amount: 3,
48
- type: "AWS::SQS::Queue"
49
-
50
- queue_names = %w(MyFirstQueue MySecondQueue)
51
-
52
- # Multiple resources with index, starts at 0
53
- resource :sqs_with_index,
54
- amount: 2,
55
- type: "AWS::SQS::Queue" do |r, index|
56
- r.property(:queue_name) { queue_names[index] }
14
+ resource :ecs_stack,
15
+ type: "AWS::CloudFormation::Stack" do |r|
16
+ r.depends_on %w(VpcStack)
17
+ r.property(:template_u_r_l) { "ecsstack" }
18
+ r.property(:parameters) do
19
+ {
20
+ "Vpc": "VpcStack".ref("Outputs.<%= name %>Vpc")
21
+ }
22
+ end
57
23
  end
58
24
  end
59
25
  end
@@ -3,7 +3,10 @@ module <%= name %>Stack
3
3
  include Rubycfn
4
4
 
5
5
  included do
6
- include <%= name %>Stack::Main
7
- include <%= name %>Stack::CICD
6
+ include Concerns::GlobalVariables
7
+ include Concerns::SharedMethods
8
+ include <%= name %>Stack::Parent
9
+
10
+ description generate_stack_description("ParentStack")
8
11
  end
9
12
  end
@@ -0,0 +1,38 @@
1
+ require "git-revision"
2
+
3
+ module Git
4
+ class Revision
5
+ class << self
6
+ def dirty?
7
+ !`git diff --numstat | wc -l`.strip.to_i.zero?
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ module Concerns
14
+ module SharedMethods
15
+ extend ActiveSupport::Concern
16
+ included do
17
+ # Convert array
18
+ def recipients_to_array(val)
19
+ val.split(",").map do |email|
20
+ {
21
+ "Endpoint": email,
22
+ "Protocol": "email-json"
23
+ }
24
+ end
25
+ end
26
+
27
+ # Returns 1 if enviroments[] match the environment, otherwise 0
28
+ def get_resources_amount(environments = %w(production rspec))
29
+ ((environments.include? ENV["ENVIRONMENT"]) && 1) || 0
30
+ end
31
+
32
+ def generate_stack_description(stack_name)
33
+ "#{stack_name}-#{Git::Revision.commit}" \
34
+ "#{Git::Revision.dirty? ? "-dirty" : ""}"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -9,6 +9,8 @@ require "rspec"
9
9
  require "rspec/its"
10
10
  require "rspec/given"
11
11
 
12
+ ENV["ENVIRONMENT"] = "rspec"
13
+
12
14
  RSpec.configure do |config|
13
15
  config.color = true
14
16
  config.formatter = "documentation"
@@ -23,4 +25,4 @@ RSpec.configure do |config|
23
25
  config.run_all_when_everything_filtered = true
24
26
  config.filter_run_excluding :slow unless ENV["SLOW_SPECS"]
25
27
  config.filter_run_excluding :debug unless ENV["DEBUG_SPECS"]
26
- end
28
+ end
@@ -0,0 +1,18 @@
1
+ def vpc_subnets
2
+ [
3
+ {
4
+ "ec2_public": {
5
+ "owner": "<%= name.downcase %>",
6
+ "public": true,
7
+ "offset": 2
8
+ }
9
+ },
10
+ {
11
+ "ec2_private": {
12
+ "owner": "<%= name.downcase %>",
13
+ "public": false,
14
+ "offset": 3
15
+ }
16
+ }
17
+ ]
18
+ end
@@ -0,0 +1,27 @@
1
+ def upload_stacks
2
+ env_vars = load_env_vars
3
+
4
+ set_aws_credentials(
5
+ env_vars[:aws_region],
6
+ env_vars[:aws_access_key_id],
7
+ env_vars[:aws_secret_access_key]
8
+ )
9
+
10
+ s3 = create_bucket_if_not_exists(
11
+ env_vars[:aws_region],
12
+ env_vars[:artifact_bucket]
13
+ )
14
+
15
+ stacks = compile_stacks(true)
16
+ stacks.each do |stack_name, stack|
17
+ next if JSON.parse(stack)["Resources"].nil?
18
+ hash = @stack_hashes[stack_name.to_sym]
19
+ local_file = "#{env_vars[:environment]}-#{stack_name.downcase}.json"
20
+ s3_filename = "#{env_vars[:environment]}-#{stack_name.downcase}-#{hash}.json"
21
+ # content = JSON.parse(stack).to_json
22
+ obj = s3.bucket(env_vars[:artifact_bucket]).object(s3_filename)
23
+ content = File.open("build/#{local_file}").read
24
+ obj.put(body: content)
25
+ puts "Uploaded #{stack_name} to s3://#{env_vars[:artifact_bucket]}/#{s3_filename}"
26
+ end
27
+ end