rubycfn 0.5.0 → 0.5.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.
data/Gemfile.lock CHANGED
@@ -1,35 +1,35 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rubycfn (0.5.0)
4
+ rubycfn (0.5.5)
5
5
  activesupport (~> 5.1.5)
6
6
  dotenv (~> 2.4.0)
7
- json (~> 2.1.0)
7
+ json (~> 2.3.0)
8
8
  neatjson (~> 0.8.4)
9
9
  tty-prompt (~> 0.16.0)
10
10
 
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- activesupport (5.1.7)
14
+ activesupport (6.0.3.1)
15
15
  concurrent-ruby (~> 1.0, >= 1.0.2)
16
16
  i18n (>= 0.7, < 2)
17
17
  minitest (~> 5.1)
18
18
  tzinfo (~> 1.1)
19
- addressable (2.7.0)
19
+ addressable (2.8.0)
20
20
  public_suffix (>= 2.0.2, < 5.0)
21
- awesome_print (1.8.0)
22
- coderay (1.1.2)
23
- concurrent-ruby (1.1.6)
24
- diff-lcs (1.3)
25
- docile (1.3.2)
21
+ awesome_print (1.9.2)
22
+ coderay (1.1.3)
23
+ concurrent-ruby (1.1.9)
24
+ diff-lcs (1.4.4)
25
+ docile (1.4.0)
26
26
  dotenv (2.4.0)
27
- equatable (0.6.1)
28
- ffi (1.12.2)
29
- formatador (0.2.5)
30
- given_core (3.8.0)
27
+ equatable (0.7.0)
28
+ ffi (1.15.3)
29
+ formatador (0.3.0)
30
+ given_core (3.8.2)
31
31
  sorcerer (>= 0.3.7)
32
- guard (2.16.1)
32
+ guard (2.17.0)
33
33
  formatador (>= 0.2.4)
34
34
  listen (>= 2.7, < 4.0)
35
35
  lumberjack (>= 1.0.12, < 2.0)
@@ -43,65 +43,67 @@ GEM
43
43
  guard (~> 2.1)
44
44
  guard-compat (~> 1.1)
45
45
  rspec (>= 2.99.0, < 4.0)
46
- i18n (1.8.2)
46
+ i18n (1.8.10)
47
47
  concurrent-ruby (~> 1.0)
48
- json (2.1.0)
48
+ json (2.3.1)
49
49
  launchy (2.5.0)
50
50
  addressable (~> 2.7)
51
- listen (3.2.1)
51
+ listen (3.5.1)
52
52
  rb-fsevent (~> 0.10, >= 0.10.3)
53
53
  rb-inotify (~> 0.9, >= 0.9.10)
54
- lumberjack (1.2.4)
55
- method_source (0.9.2)
56
- minitest (5.14.0)
54
+ lumberjack (1.2.8)
55
+ method_source (1.0.0)
56
+ minitest (5.14.4)
57
57
  neatjson (0.8.4)
58
58
  necromancer (0.4.0)
59
59
  nenv (0.3.0)
60
60
  notiffany (0.1.3)
61
61
  nenv (~> 0.1)
62
62
  shellany (~> 0.0)
63
- pastel (0.7.3)
63
+ pastel (0.7.4)
64
64
  equatable (~> 0.6)
65
65
  tty-color (~> 0.5)
66
- pry (0.12.2)
67
- coderay (~> 1.1.0)
68
- method_source (~> 0.9.0)
69
- public_suffix (4.0.3)
70
- rake (13.0.1)
71
- rb-fsevent (0.10.3)
66
+ pry (0.14.1)
67
+ coderay (~> 1.1)
68
+ method_source (~> 1.0)
69
+ public_suffix (4.0.6)
70
+ rake (13.0.4)
71
+ rb-fsevent (0.11.0)
72
72
  rb-inotify (0.10.1)
73
73
  ffi (~> 1.0)
74
- rspec (3.9.0)
75
- rspec-core (~> 3.9.0)
76
- rspec-expectations (~> 3.9.0)
77
- rspec-mocks (~> 3.9.0)
78
- rspec-core (3.9.1)
79
- rspec-support (~> 3.9.1)
80
- rspec-expectations (3.9.0)
74
+ rspec (3.10.0)
75
+ rspec-core (~> 3.10.0)
76
+ rspec-expectations (~> 3.10.0)
77
+ rspec-mocks (~> 3.10.0)
78
+ rspec-core (3.10.1)
79
+ rspec-support (~> 3.10.0)
80
+ rspec-expectations (3.10.1)
81
81
  diff-lcs (>= 1.2.0, < 2.0)
82
- rspec-support (~> 3.9.0)
83
- rspec-given (3.8.0)
84
- given_core (= 3.8.0)
82
+ rspec-support (~> 3.10.0)
83
+ rspec-given (3.8.2)
84
+ given_core (= 3.8.2)
85
85
  rspec (>= 2.14.0)
86
86
  rspec-its (1.3.0)
87
87
  rspec-core (>= 3.0.0)
88
88
  rspec-expectations (>= 3.0.0)
89
- rspec-mocks (3.9.1)
89
+ rspec-mocks (3.10.2)
90
90
  diff-lcs (>= 1.2.0, < 2.0)
91
- rspec-support (~> 3.9.0)
92
- rspec-support (3.9.2)
91
+ rspec-support (~> 3.10.0)
92
+ rspec-support (3.10.2)
93
93
  rspec_junit_formatter (0.4.1)
94
94
  rspec-core (>= 2, < 4, != 2.12.0)
95
95
  shellany (0.0.1)
96
- simplecov (0.18.5)
96
+ simplecov (0.21.2)
97
97
  docile (~> 1.1)
98
98
  simplecov-html (~> 0.11)
99
- simplecov-html (0.12.2)
99
+ simplecov_json_formatter (~> 0.1)
100
+ simplecov-html (0.12.3)
101
+ simplecov_json_formatter (0.1.3)
100
102
  sorcerer (2.0.1)
101
- thor (1.0.1)
103
+ thor (1.1.0)
102
104
  thread_safe (0.3.6)
103
- timers (4.3.0)
104
- tty-color (0.5.1)
105
+ timers (4.3.3)
106
+ tty-color (0.6.0)
105
107
  tty-cursor (0.5.0)
106
108
  tty-prompt (0.16.1)
107
109
  necromancer (~> 0.4.0)
@@ -114,9 +116,10 @@ GEM
114
116
  tty-screen (~> 0.6.4)
115
117
  wisper (~> 2.0.0)
116
118
  tty-screen (0.6.5)
117
- tzinfo (1.2.6)
119
+ tzinfo (1.2.9)
118
120
  thread_safe (~> 0.1)
119
121
  wisper (2.0.1)
122
+ zeitwerk (2.3.0)
120
123
 
121
124
  PLATFORMS
122
125
  ruby
@@ -137,4 +140,4 @@ DEPENDENCIES
137
140
  simplecov (~> 0.9)
138
141
 
139
142
  BUNDLED WITH
140
- 2.1.4
143
+ 2.2.22
data/README.md CHANGED
@@ -61,7 +61,7 @@ __________ ____ __________________.___._________ _____________________
61
61
  | _/ | /| | _// | |/ \ \/ | __) | | _/
62
62
  | | \ | / | | \\____ |\ \____| \ | | \
63
63
  |____|_ /______/ |______ // ______| \______ /\___ / |______ /
64
- \/ \/ \/ \/ \/ \/ [v0.5.0]
64
+ \/ \/ \/ \/ \/ \/ [v0.5.5]
65
65
  Project name? example
66
66
  Account ID? 1234567890
67
67
  Select region EU (Frankfurt)
@@ -159,12 +159,11 @@ You can reuse these environment variables in your project code.
159
159
  The `.env.rspec` is used when running unit tests. It contains mock variables
160
160
  so that you can test the resulting CloudFormation templates properly.
161
161
 
162
- #### The missing .env.private file
162
+ #### The .env.private file
163
163
 
164
- There is one file that is not generated by default but does need mentioning:
165
- the `.env.private` file. This is a special file that allows you to override
166
- environment variables. An environment variable set in .env.private always takes
167
- precedence over environment variables set in other .env files.
164
+ This is a special file that allows you to override environment variables.
165
+ An environment variable set in .env.private always takes precedence over
166
+ environment variables set in other .env files.
168
167
 
169
168
  #### .rubocop.yml
170
169
 
@@ -1,4 +1,4 @@
1
1
  # Rubycfn version
2
2
  module Rubycfn
3
- VERSION = "0.5.0".freeze
3
+ VERSION = "0.5.5".freeze
4
4
  end
data/rubycfn.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_runtime_dependency "neatjson", "~> 0.8.4"
22
- spec.add_runtime_dependency "json", "~> 2.1.0"
22
+ spec.add_runtime_dependency "json", "~> 2.3.0"
23
23
  spec.add_runtime_dependency "activesupport", "~> 5.1.5"
24
24
  spec.add_runtime_dependency "tty-prompt", "~> 0.16.0"
25
25
  spec.add_runtime_dependency "dotenv", "~> 2.4.0"
data/templates/.gitignore CHANGED
@@ -83,5 +83,9 @@ attic/
83
83
  /venv
84
84
  /Gemfile.lock
85
85
  .env.dependencies
86
+ .env.dependencies.development
87
+ .env.dependencies.test
88
+ .env.dependencies.acceptance
89
+ .env.dependencies.production
86
90
  CloudFormationResourceSpecification.json
87
91
  config.json
@@ -102,3 +102,6 @@ Metrics/PerceivedComplexity:
102
102
 
103
103
  Style/EvalWithLocation:
104
104
  Enabled: false
105
+
106
+ Style/PerlBackrefs:
107
+ Enabled: false
data/templates/README.md CHANGED
@@ -16,11 +16,12 @@ __________ ____ __________________.___._________ _____________________
16
16
 
17
17
  ## Rake commands
18
18
 
19
- `rake` - Compile the code into CloudFormation templates and run unit tests
19
+ `rake` - Retrieve required outputs from DependencyStack, Compile the code into CloudFormation templates and run unit tests
20
20
  `rake init` - Deploy the DependencyStack in the AWS account
21
21
  `rake compile` - Compile the code into CloudFormation templates
22
22
  `rake spec` - Run unit tests
23
23
  `rake upload` - Upload the CloudFormation templates to s3
24
+ `rake update` - Save required outputs from DependencyStack to .env.dependencies.<ENVIRONMENT>
24
25
  `rake apply` - Deploy the CloudFormation templates
25
26
 
26
27
  ## Stack configuration
data/templates/Rakefile CHANGED
@@ -36,7 +36,7 @@ task :clean do
36
36
  end
37
37
  end
38
38
 
39
- desc "Store dependencies of DependencyStack in .env.dependencies"
39
+ desc "Store dependencies of DependencyStack in .env.dependencies.<ENVIRONMENT>"
40
40
  task :dependencies do
41
41
  require_relative "lib/core/dependencies.rb"
42
42
  end
@@ -52,6 +52,9 @@ applications:
52
52
  env:
53
53
  SOME_ENV_VAR: Exposed
54
54
  SOME_OTHER_VAR: desopxE
55
+ # SOME_REFFED_VAR: :foobar.ef
56
+ # SOME_OTHER_STACK_VAR: :vpc_stack.ref("Outputs.VpcId")
57
+ # SOME_ENV_VAR: ${MY_ENV_VAR}
55
58
  hello-world2:
56
59
  image: tutum/hello-world
57
60
  container_port: 80
@@ -2,7 +2,7 @@ require "git-revision"
2
2
 
3
3
  def update_references(contents, environment, _artifact_bucket)
4
4
  Dotenv.load(".env.private")
5
- Dotenv.load(".env.dependencies")
5
+ Dotenv.load(".env.dependencies.#{ENV["ENVIRONMENT"]}")
6
6
  contents["Resources"].map do |resource|
7
7
  resource_name = resource.shift
8
8
  resource_values = resource.shift
@@ -1,25 +1,35 @@
1
+ def infra_config
2
+ config = YAML.safe_load(File.read("config.yaml"), [Symbol])
3
+ config["applications"] ||= {}
4
+ config["environments"] ||= {}
5
+ config["subnets"] ||= {}
6
+ config
7
+ end
8
+
1
9
  def load_env_vars
2
10
  Dotenv.load(".env.private")
3
- Dotenv.load(".env.dependencies")
11
+ Dotenv.load(".env.dependencies.#{ENV["ENVIRONMENT"]}")
4
12
  Dotenv.load(".env")
5
13
  Dotenv.load(".env.#{ENV["ENVIRONMENT"]}")
6
14
 
7
15
  check_dependencies
8
16
  {
17
+ artifact_bucket: ENV["ARTIFACTBUCKET"],
9
18
  aws_region: ENV["AWS_REGION"],
10
19
  aws_access_key_id: ENV["AWS_ACCESS_KEY_ID"],
11
20
  aws_secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
12
- artifact_bucket: ENV["ARTIFACTBUCKET"],
21
+ cloudformation_bucket: ENV["CLOUDFORMATIONBUCKET"],
13
22
  environment: ENV["ENVIRONMENT"],
14
- stack_name: ENV["STACK_NAME"]
23
+ stack_name: infra_config["environments"][ENV["ENVIRONMENT"]]["stack_name"]
15
24
  }
16
25
  end
17
26
 
18
27
  def check_dependencies
19
28
  ENV["AWS_REGION"] ||= ENV["AWS_DEFAULT_REGION"]
20
- raise "AWS_REGION not set" unless ENV["AWS_REGION"]
21
- raise "ARTIFACTBUCKET not set" unless ENV["ARTIFACTBUCKET"]
22
- raise "ENVIRONMENT not set" unless ENV["ENVIRONMENT"]
23
- raise "STACK_NAME not set" unless ENV["STACK_NAME"]
29
+ raise "AWS_REGION not set." unless ENV["AWS_REGION"]
30
+ raise "CLOUDFORMATIONBUCKET not set. Run `rake init` and `rake update` first!" unless ENV["CLOUDFORMATIONBUCKET"]
31
+ raise "ARTIFACTBUCKET not set. Run `rake init` and `rake update` first!" unless ENV["ARTIFACTBUCKET"]
32
+ raise "ENVIRONMENT not set." unless ENV["ENVIRONMENT"]
33
+ raise "`stack_name` not configured in config.yaml" unless infra_config["environments"][ENV["ENVIRONMENT"]]["stack_name"]
24
34
  raise "AWS CREDENTIALS NOT SET" unless ENV["AWS_ACCESS_KEY_ID"] && ENV["AWS_SECRET_ACCESS_KEY"]
25
35
  end
@@ -45,7 +45,6 @@ def get_parent_stack_s3_location(bucket, environment)
45
45
  JSON.parse(stack)["Resources"].each do |_resource, payload|
46
46
  if payload["Type"] == "AWS::CloudFormation::Stack"
47
47
  parent_stack = stack_name
48
- json_file = "build/#{environment}-#{stack_name.downcase}.json"
49
48
  file_hash = git_revision
50
49
  break
51
50
  end
@@ -2,7 +2,7 @@ require_relative "../core/git"
2
2
 
3
3
  def upload_stacks
4
4
  Dotenv.load(".env.private")
5
- Dotenv.load(".env.dependencies")
5
+ Dotenv.load(".env.dependencies.#{ENV["ENVIRONMENT"]}")
6
6
  env_vars = load_env_vars
7
7
 
8
8
  set_aws_credentials(
@@ -11,17 +11,10 @@ def upload_stacks
11
11
  env_vars[:aws_secret_access_key]
12
12
  )
13
13
 
14
- begin
15
- s3 = create_bucket_if_not_exists(
16
- env_vars[:aws_region],
17
- env_vars[:artifact_bucket]
18
- )
19
- rescue => e
20
- puts "Exception create_bucket_if_not_exists #{e}"
21
- end
14
+ s3 = Aws::S3::Resource.new(region: env_vars[:aws_region])
22
15
 
23
16
  stacks = compile_stacks(true)
24
- raise "CLOUDFORMATIONBUCKET not found in DependencyStack outputs" unless ENV["CLOUDFORMATIONBUCKET"]
17
+ raise "CLOUDFORMATIONBUCKET not found in <%= project_name %>#{ENV["ENVIRONMENT"].capitalize}DependencyStack outputs" unless ENV["CLOUDFORMATIONBUCKET"]
25
18
  stacks.each do |stack_name, stack|
26
19
  next if JSON.parse(stack)["Resources"].nil?
27
20
  hash = @stack_hashes[stack_name.to_sym]
@@ -1,44 +1,120 @@
1
+ def to_bool(val)
2
+ return false unless val.nil? || val.empty?
3
+ return false unless val != "true"
4
+ true
5
+ end
6
+
7
+ def env_vars_to_array(val)
8
+ if val.nil? || val.empty?
9
+ return [
10
+ {
11
+ Name: "WITHOUT_ENV",
12
+ Value: "true"
13
+ }
14
+ ]
15
+ end
16
+ vars = []
17
+
18
+ # Create an array of variables to pass to this application.
19
+ # Resolve any references to its intrinsic function.
20
+ val.keys.each_with_index do |object, index|
21
+ value = val.values[index].class == Symbol && Kernel.class_eval(":#{val.values[index]}") || val.values[index]
22
+
23
+ # Support ${ENV_VAR} in config_yaml
24
+ if value =~ /\$\{([^\}]*)\}/
25
+ capture = $1
26
+ value = ENV[capture]
27
+ end
28
+
29
+ vars.push(
30
+ Name: object,
31
+ Value: value
32
+ )
33
+ end
34
+ vars
35
+ end
36
+
37
+ @ignore_params = {}
38
+
39
+ # Return the mandatory Stack parameters and add all defined ENV vars from config.yaml
40
+ def parent_parameters(env_vars, simple_name)
41
+ params = {
42
+ "Vpc": :vpc_stack.ref("Outputs.Vpc"),
43
+ "Cluster": :ecs_stack.ref("Outputs.EcsCluster"),
44
+ "Listener": :ecs_stack.ref("Outputs.EcsLoadBalancerListener"),
45
+ "EcsServiceAutoScalingRoleArn": :ecs_stack.ref("Outputs.EcsAutoScalingRoleArn"),
46
+ "HostedZoneId": "HOSTEDZONEID".ref,
47
+ "HostedZoneName": "HOSTEDZONENAME".ref,
48
+ "LoadBalancerDnsName": :ecs_stack.ref("Outputs.EcsLoadBalancerUrl"),
49
+ "CanonicalHostedZoneId": :ecs_stack.ref("Outputs.EcsLoadBalancerHostedZoneId"),
50
+ "CertificateProviderFunctionArn": :acm_stack.ref("Outputs.CertificateProviderFunctionArn"),
51
+ "ApplicationDeploymentFailureRollbackFunctionArn": :ecs_stack.ref("Outputs.ApplicationDeploymentFailureRollbackFunctionArn")
52
+ }
53
+
54
+ env_vars.each do |var|
55
+ TOPLEVEL_BINDING.eval("@ignore_params[:#{simple_name}] ||= []")
56
+ if var[:Value].class == Hash && var[:Value].keys[0] == :Ref
57
+ TOPLEVEL_BINDING.eval("@ignore_params[:#{simple_name}].push(\"#{var[:Name]}\")")
58
+ next
59
+ end
60
+ params[var[:Name].cfnize] = var[:Value]
61
+ end
62
+ params
63
+ end
64
+
65
+ def application_parameters(env_vars)
66
+ vars = []
67
+
68
+ env_vars.each do |var|
69
+ if var[:Value].class == Hash && var[:Value].keys[0] == :Ref
70
+ vars.push(
71
+ Name: var[:Name],
72
+ Value: var[:Value]
73
+ )
74
+ else
75
+ vars.push(
76
+ Name: var[:Name],
77
+ Value: var[:Name].cfnize.ref
78
+ )
79
+ end
80
+ end
81
+ vars
82
+ end
83
+
1
84
  # Generate nested stacks for each defined application, and create module dynamically.
2
85
  def create_applications
86
+ return if infra_config["environments"][environment]["cluster_size"].nil? || infra_config["environments"][environment]["cluster_size"].to_i.zero?
3
87
  resource :applications_stack,
4
88
  amount: infra_config["applications"].count,
5
89
  type: "AWS::CloudFormation::Stack" do |r, index|
90
+ env_vars = env_vars_to_array(infra_config["applications"].values[index]["env"])
91
+ application_vars = application_parameters(env_vars) # rubocop:disable Lint/UselessAssignment
6
92
  name = infra_config["applications"].keys[index]
7
93
  resource_name = name.tr("-", "_").cfnize
8
94
  simple_name = resource_name.downcase
9
95
  app_config = infra_config["applications"].values[index]
10
- warn "App config is empty" unless app_config # Rubocop didn't understand that the variable is actually used.
11
- # request_certificate("Gateway", gateway_domains[environment], :asellion_com_hosted_zone_id.ref, "us-east-1")
96
+ is_essential = to_bool(app_config["essential"].to_s)
12
97
 
13
98
  r._id("#{resource_name}Stack")
14
99
  r.property(:template_url) { "#{simple_name}stack" }
15
- r.property(:parameters) do
16
- {
17
- "Vpc": :vpc_stack.ref("Outputs.SfsVpc"),
18
- "Cluster": :ecs_stack.ref("Outputs.SfsEcsCluster"),
19
- "Listener": :ecs_stack.ref("Outputs.EcsLoadBalancerListener"),
20
- "EcsServiceAutoScalingRoleArn": :ecs_stack.ref("Outputs.SfsEcsAutoScalingRoleArn"),
21
- "HostedZoneId": :route53_stack.ref("Outputs.HostedZoneId"),
22
- "HostedZoneName": :route53_stack.ref("Outputs.HostedZoneName"),
23
- "LoadBalancerDnsName": :ecs_stack.ref("Outputs.EcsLoadBalancerUrl"),
24
- "CanonicalHostedZoneId": :ecs_stack.ref("Outputs.EcsLoadBalancerHostedZoneId"),
25
- "CertificateProviderFunctionArn": :acm_stack.ref("Outputs.CertificateProviderFunctionArn")
26
- }
27
- end
100
+ r.property(:parameters) { parent_parameters(env_vars, simple_name) }
28
101
  Object.const_set("#{resource_name}Stack", Module.new).class_eval <<-RUBY
29
102
  extend ActiveSupport::Concern
30
103
  include Rubycfn
31
104
 
32
105
  included do
33
- def env_vars_to_array(val)
34
- vars = []
35
- val.keys.each_with_index do |object, index|
36
- vars.push(
37
- Name: object,
38
- Value: val.values[index]
39
- )
40
- end
41
- vars
106
+
107
+ def get_aliases(val)
108
+ aliases = []
109
+ return aliases if val["aliases"].nil?
110
+ return aliases if val["aliases"][environment].nil?
111
+ val["aliases"][environment]
112
+ end
113
+
114
+ ignore_params = TOPLEVEL_BINDING.eval("@ignore_params[:#{simple_name}]")
115
+ application_vars.each do |var|
116
+ next if ignore_params.include? var[:Name]
117
+ Object.class_eval("parameter var[:Name].to_sym, description: 'Value for ENV var \#{var[:Name]}'")
42
118
  end
43
119
 
44
120
  parameter :vpc,
@@ -77,17 +153,34 @@ def create_applications
77
153
  description: "ARN of certificate provider",
78
154
  type: "String"
79
155
 
156
+ parameter :application_deployment_failure_rollback_function_arn,
157
+ description: "ARN of ECS deployment failure Lambda",
158
+ type: "String"
159
+
160
+ variable :aliases,
161
+ value: app_config,
162
+ filter: :get_aliases
163
+
164
+ variable :min_override,
165
+ value: app_config[environment].nil? ? "" : app_config[environment]["min"].to_s
166
+
80
167
  variable :min,
81
- value: app_config["min"].to_s
168
+ value: min_override.empty? ? app_config["min"].to_s : min_override
169
+
170
+ variable :max_override,
171
+ value: app_config[environment].nil? ? "" : app_config[environment]["max"].to_s
82
172
 
83
173
  variable :max,
84
- value: app_config["max"].to_s
174
+ value: max_override.empty? ? app_config["max"].to_s : max_override
85
175
 
86
176
  variable :container_port,
87
177
  value: app_config["container_port"].to_s
88
178
 
179
+ variable :memory_override,
180
+ value: app_config[environment].nil? ? "" : app_config[environment]["mem"].to_s
181
+
89
182
  variable :memory,
90
- value: app_config["mem"].to_s
183
+ value: memory_override.empty? ? app_config["mem"].to_s : memory_override
91
184
 
92
185
  variable :image,
93
186
  value: app_config["image"]
@@ -101,11 +194,14 @@ def create_applications
101
194
 
102
195
 
103
196
  description generate_stack_description("#{resource_name}Stack")
197
+
104
198
  resource :service,
199
+ amount: max.to_i.positive? ? 1 : 0,
105
200
  type: "AWS::ECS::Service" do |r|
106
-
201
+ r.property(:service_name) { "#{environment}-<%= project_name %>-#{simple_name}" }
107
202
  r.property(:cluster) { :cluster.ref }
108
203
  r.property(:role) { :service_role.ref }
204
+ r.property(:health_check_grace_period_seconds) { 120 }
109
205
  r.property(:desired_count) { min.to_i }
110
206
  r.property(:task_definition) { :task_definition.ref }
111
207
  r.property(:load_balancers) do
@@ -117,17 +213,27 @@ def create_applications
117
213
  end
118
214
  end
119
215
 
216
+ resource :application_deployment_health,
217
+ amount: max.to_i.positive? ? 1 : 0,
218
+ type: "Custom::EcsDeploymentCheck" do |r|
219
+ r.property(:service_token) { :application_deployment_failure_rollback_function_arn.ref }
220
+ r.property("AWSRegion") { "${AWS::Region}".fnsub }
221
+ r.property(:service) { "#{environment}-<%= project_name %>-#{simple_name}" }
222
+ r.property(:cluster) { :cluster.ref }
223
+ end
224
+
120
225
  resource :task_definition,
226
+ amount: max.to_i.positive? ? 1 : 0,
121
227
  type: "AWS::ECS::TaskDefinition" do |r|
122
228
  r.property(:family) { "#{simple_name}-service" }
123
229
  r.property(:container_definitions) do
124
230
  [
125
231
  {
126
232
  "Name": "#{simple_name}-service",
127
- "Essential": true,
233
+ "Essential": #{is_essential},
128
234
  "Image": image,
129
235
  "Memory": memory.to_i,
130
- "Environment": env_vars,
236
+ "Environment": application_vars,
131
237
  "PortMappings": [
132
238
  {
133
239
  "ContainerPort": container_port.to_i
@@ -146,12 +252,14 @@ def create_applications
146
252
  end
147
253
 
148
254
  resource :cloud_watch_logs_group,
255
+ amount: max.to_i.positive? ? 1 : 0,
149
256
  type: "AWS::Logs::LogGroup" do |r|
150
257
  r.property(:log_group_name) { ["AWS::StackName".ref, "-#{simple_name}"].fnjoin }
151
258
  r.property(:retention_in_days) { 30 }
152
259
  end
153
260
 
154
261
  resource :target_group,
262
+ amount: max.to_i.positive? ? 1 : 0,
155
263
  type: "AWS::ElasticLoadBalancingV2::TargetGroup" do |r|
156
264
  r.property(:vpc_id) { :vpc.ref }
157
265
  r.property(:port) { container_port.to_i }
@@ -169,6 +277,7 @@ def create_applications
169
277
  end
170
278
 
171
279
  resource :listener_rule,
280
+ amount: max.to_i.positive? ? 1 : 0,
172
281
  type: "AWS::ElasticLoadBalancingV2::ListenerRule" do |r|
173
282
  r.property(:listener_arn) { :listener.ref }
174
283
  r.property(:priority) { priority.to_i }
@@ -179,7 +288,7 @@ def create_applications
179
288
  "Values": [
180
289
  ["origin", name.tr("_", "-"), :hosted_zone_name.ref].fnjoin("."),
181
290
  [name.tr("_", "-"), :hosted_zone_name.ref].fnjoin(".")
182
- ]
291
+ ] + aliases
183
292
  }
184
293
  ]
185
294
  end
@@ -210,6 +319,7 @@ def create_applications
210
319
  }
211
320
 
212
321
  resource :service_role,
322
+ amount: max.to_i.positive? ? 1 : 0,
213
323
  type: "AWS::IAM::Role" do |r|
214
324
  r.property(:role_name) { "ecs-service-${AWS::StackName}".fnsub }
215
325
  r.property(:path) { "/" }
@@ -235,6 +345,27 @@ def create_applications
235
345
  "elasticloadbalancing:RegisterTargets"
236
346
  ],
237
347
  "Resource": "*"
348
+ },
349
+ {
350
+ "Effect": "Allow",
351
+ "Action": [
352
+ "ec2:DescribeTags",
353
+ "ecs:CreateCluster",
354
+ "ecs:DeregisterContainerInstance",
355
+ "ecs:DiscoverPollEndpoint",
356
+ "ecs:Poll",
357
+ "ecs:RegisterContainerInstance",
358
+ "ecs:StartTelemetrySession",
359
+ "ecs:UpdateContainerInstancesState",
360
+ "ecs:Submit*",
361
+ "ecr:GetAuthorizationToken",
362
+ "ecr:BatchCheckLayerAvailability",
363
+ "ecr:GetDownloadUrlForLayer",
364
+ "ecr:BatchGetImage",
365
+ "logs:CreateLogStream",
366
+ "logs:PutLogEvents"
367
+ ],
368
+ "Resource": "*"
238
369
  }
239
370
  ]
240
371
  }
@@ -244,6 +375,7 @@ def create_applications
244
375
  end
245
376
 
246
377
  resource :application_load_balancer_record,
378
+ amount: max.to_i.positive? ? 1 : 0,
247
379
  type: "AWS::Route53::RecordSet" do |r|
248
380
  r.property(:type) { "A" }
249
381
  r.property(:name) { ["origin", name.tr("_", "-"), :hosted_zone_name.ref].fnjoin(".") }
@@ -258,6 +390,7 @@ def create_applications
258
390
  end
259
391
 
260
392
  resource :service_scalable_target,
393
+ amount: max.to_i.positive? ? 1 : 0,
261
394
  type: "AWS::ApplicationAutoScaling::ScalableTarget" do |r|
262
395
  r.property(:max_capacity) { max.to_i }
263
396
  r.property(:min_capacity) { min.to_i }
@@ -268,6 +401,7 @@ def create_applications
268
401
  end
269
402
 
270
403
  resource :service_scale_out_policy,
404
+ amount: max.to_i.positive? ? 1 : 0,
271
405
  type: "AWS::ApplicationAutoScaling::ScalingPolicy" do |r|
272
406
  r.property(:policy_name) { "ServiceScaleOutPolicy" }
273
407
  r.property(:policy_type) { "StepScaling" }
@@ -288,6 +422,7 @@ def create_applications
288
422
  end
289
423
 
290
424
  resource :service_scale_in_policy,
425
+ amount: max.to_i.positive? ? 1 : 0,
291
426
  type: "AWS::ApplicationAutoScaling::ScalingPolicy" do |r|
292
427
  r.property(:policy_name) { "ServiceScaleInPolicy" }
293
428
  r.property(:policy_type) { "StepScaling" }
@@ -308,6 +443,7 @@ def create_applications
308
443
  end
309
444
 
310
445
  resource :cpu_scale_out_alarm,
446
+ amount: max.to_i.positive? ? 1 : 0,
311
447
  type: "AWS::CloudWatch::Alarm" do |r|
312
448
  r.property(:alarm_name) { "CPU utilization greater than 90% on #{simple_name}-#{environment}" }
313
449
  r.property(:alarm_description) { "Alarm if cpu utilization greater than 90% of reserved cpu" }
@@ -334,6 +470,7 @@ def create_applications
334
470
  end
335
471
 
336
472
  resource :cpu_scale_in_alarm,
473
+ amount: max.to_i.positive? ? 1 : 0,
337
474
  type: "AWS::CloudWatch::Alarm" do |r|
338
475
  r.property(:alarm_name) { "CPU utilization less than 70% on #{simple_name}-#{environment}" }
339
476
  r.property(:alarm_description) { "Alarm if cpu utilization greater than 70% of reserved cpu" }
@@ -360,6 +497,7 @@ def create_applications
360
497
  end
361
498
 
362
499
  resource :ecs_application_certificate,
500
+ amount: max.to_i.positive? ? 1 : 0,
363
501
  type: "Custom::Certificate" do |r|
364
502
  r.property(:service_token) { :certificate_provider_function_arn.ref }
365
503
  r.property(:region) { "us-east-1" }
@@ -368,6 +506,7 @@ def create_applications
368
506
  end
369
507
 
370
508
  resource :ecs_application_issued_certificate,
509
+ amount: max.to_i.positive? ? 1 : 0,
371
510
  type: "Custom::IssuedCertificate" do |r|
372
511
  r.property(:service_token) { :certificate_provider_function_arn.ref }
373
512
  r.property(:region) { "us-east-1" }
@@ -375,6 +514,7 @@ def create_applications
375
514
  end
376
515
 
377
516
  resource :ecs_application_dns_record,
517
+ amount: max.to_i.positive? ? 1 : 0,
378
518
  type: "Custom::CertificateDNSRecord" do |r|
379
519
  r.property(:service_token) { :certificate_provider_function_arn.ref }
380
520
  r.property(:region) { "us-east-1" }
@@ -383,6 +523,7 @@ def create_applications
383
523
  end
384
524
 
385
525
  resource :ecs_application_validation_record,
526
+ amount: max.to_i.positive? ? 1 : 0,
386
527
  type: "AWS::Route53::RecordSetGroup" do |r|
387
528
  r.property(:hosted_zone_id) { :hosted_zone_id.ref }
388
529
  r.property(:record_sets) do
@@ -402,7 +543,11 @@ def create_applications
402
543
  end
403
544
 
404
545
  resource :cloudfront_distribution,
405
- depends_on: :ecs_application_issued_certificate,
546
+ depends_on: [
547
+ :ecs_application_issued_certificate,
548
+ :application_deployment_health
549
+ ],
550
+ amount: max.to_i.positive? ? 1 : 0,
406
551
  type: "AWS::CloudFront::Distribution" do |r|
407
552
  r.property(:distribution_config) do
408
553
  {
@@ -458,6 +603,7 @@ def create_applications
458
603
  end
459
604
 
460
605
  resource :application_cloudfront_dns_record,
606
+ amount: max.to_i.positive? ? 1 : 0,
461
607
  type: "AWS::Route53::RecordSetGroup" do |r|
462
608
  r.property(:hosted_zone_id) { :hosted_zone_id.ref }
463
609
  r.property(:record_sets) do