rubycfn 0.4.10 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/Gemfile.lock +1 -1
- data/README.md +43 -67
- data/bin/rubycfn +17 -73
- data/lib/cli_methods.rb +2 -2
- data/lib/rubycfn/version.rb +1 -1
- data/templates/.env +2 -0
- data/templates/.env.acceptance +1 -0
- data/templates/.env.dependencies.rspec +6 -0
- data/templates/.env.development +1 -0
- data/templates/.env.production +1 -0
- data/templates/.env.rspec +1 -0
- data/templates/.env.test +1 -0
- data/templates/{.gitignore.erb → .gitignore} +3 -0
- data/templates/{.rubocop.yml.erb → .rubocop.yml} +14 -1
- data/templates/{Gemfile.erb → Gemfile} +0 -1
- data/templates/README.md +57 -0
- data/templates/{Rakefile.erb → Rakefile} +15 -8
- data/templates/bootstrap/dependency_stack.rb +49 -0
- data/templates/config.yaml +65 -0
- data/templates/lib/aws_helper/aws_sdk.rb +30 -0
- data/templates/{compiler.rb.erb → lib/aws_helper/compiler.rb} +15 -9
- data/templates/{dependencies.rb.erb → lib/aws_helper/dependencies.rb} +5 -3
- data/templates/{deploy.rb.erb → lib/aws_helper/deploy.rb} +6 -4
- data/templates/lib/aws_helper/helpers.rb +3 -0
- data/templates/{main_aws_helper.rb.erb → lib/aws_helper/main.rb} +0 -0
- data/templates/{upload_stack.rb.erb → lib/aws_helper/upload_stack.rb} +15 -6
- data/templates/lib/core/applications.rb +479 -0
- data/templates/lib/core/assume_role.rb +40 -0
- data/templates/lib/core/classes.rb +25 -0
- data/templates/{core_compile.rb.erb → lib/core/compile.rb} +1 -0
- data/templates/lib/core/dependencies.rb +22 -0
- data/templates/{core_deploy.rb.erb → lib/core/deploy.rb} +20 -10
- data/templates/lib/core/git.rb +15 -0
- data/templates/lib/core/init.rb +173 -0
- data/templates/{core_upload.rb.erb → lib/core/upload.rb} +0 -0
- data/templates/{main.rb.erb → lib/main.rb} +8 -6
- data/templates/lib/shared_concerns/global_variables.rb +56 -0
- data/templates/{helper_methods.rb.erb → lib/shared_concerns/helper_functions.rb} +0 -0
- data/templates/lib/shared_concerns/helper_methods.rb +3 -0
- data/templates/{shared_methods.rb.erb → lib/shared_concerns/shared_methods.rb} +9 -0
- data/templates/lib/stacks/acm_stack/certificate_manager.rb +79 -0
- data/templates/{new_stack.rb.erb → lib/stacks/acm_stack/main.rb} +3 -4
- data/templates/lib/stacks/ecs_stack/ecs_cluster.rb +344 -0
- data/templates/lib/stacks/ecs_stack/lifecycle_hook.rb +188 -0
- data/templates/lib/stacks/ecs_stack/load_balancer.rb +68 -0
- data/templates/{ecs_stack.rb.erb → lib/stacks/ecs_stack/main.rb} +2 -1
- data/templates/{project_stack.rb.erb → lib/stacks/parent_stack/main.rb} +2 -2
- data/templates/lib/stacks/parent_stack/parent.rb +18 -0
- data/templates/lib/stacks/vpc_stack/infra_vpc.rb +193 -0
- data/templates/{vpc_stack.rb.erb → lib/stacks/vpc_stack/main.rb} +1 -2
- data/templates/{parent_stack_spec.rb.erb → spec/lib/parent_spec.rb} +2 -5
- data/templates/{spec_helper.rb.erb → spec/spec_helper.rb} +2 -2
- metadata +54 -44
- data/format.vim +0 -3
- data/templates/.env.erb +0 -4
- data/templates/.env.production.erb +0 -6
- data/templates/.env.rspec.erb +0 -6
- data/templates/.env.test.erb +0 -6
- data/templates/.gitlab-ci.yml.erb +0 -75
- data/templates/aws_sdk.rb.erb +0 -18
- data/templates/core_diff.rb.erb +0 -59
- data/templates/ecs_stack_concern.rb.erb +0 -20
- data/templates/global_variables.rb.erb +0 -16
- data/templates/helpers.rb.erb +0 -7
- data/templates/new_concern.rb.erb +0 -10
- data/templates/project_concern.rb.erb +0 -26
- data/templates/subnets.rb.erb +0 -18
- data/templates/vpc_concerns.rb.erb +0 -87
- data/templates/vpc_spec.rb.erb +0 -39
@@ -0,0 +1,173 @@
|
|
1
|
+
require "aws-sdk"
|
2
|
+
require "dotenv"
|
3
|
+
require "git-revision"
|
4
|
+
require "rubycfn"
|
5
|
+
require "yaml"
|
6
|
+
|
7
|
+
require_relative "../aws_helper/main"
|
8
|
+
|
9
|
+
Dotenv.load(".env.private")
|
10
|
+
Dotenv.load(".env")
|
11
|
+
Dotenv.load(".env.#{ENV["ENVIRONMENT"]}")
|
12
|
+
|
13
|
+
set_aws_credentials(
|
14
|
+
ENV["AWS_REGION"],
|
15
|
+
ENV["AWS_ACCESS_KEY_ID"],
|
16
|
+
ENV["AWS_SECRET_ACCESS_KEY"]
|
17
|
+
)
|
18
|
+
|
19
|
+
def inject_dummy_resource(stack)
|
20
|
+
hack_stack = JSON.parse(stack)
|
21
|
+
hack_stack["Resources"] = {} if hack_stack["Resources"].nil?
|
22
|
+
hack_stack["Resources"]["CloudFormationDummyResource"] = {
|
23
|
+
"Type": "AWS::CloudFormation::WaitConditionHandle",
|
24
|
+
"Metadata": {
|
25
|
+
"Comment": "Resource to update stack even if there are no changes",
|
26
|
+
"GitCommitHash": Git::Revision.commit
|
27
|
+
}
|
28
|
+
}
|
29
|
+
hack_stack.to_json
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_domain_name
|
33
|
+
config = YAML.safe_load(File.read("config.yaml"))
|
34
|
+
config["applications"] ||= {}
|
35
|
+
config["environments"] ||= {}
|
36
|
+
config["subnets"] ||= {}
|
37
|
+
|
38
|
+
# Allow override by setting ENV var
|
39
|
+
domain_name = ENV["DOMAIN_NAME"]
|
40
|
+
subdomain = ENV["SUBDOMAIN"] # rubocop:disable Lint/UselessAssignment
|
41
|
+
|
42
|
+
# First, look at environment specific domain name configuration
|
43
|
+
domain_name ||= config["environments"][ENV["ENVIRONMENT"]]["domain_name"].nil? && nil || config["environments"][ENV["ENVIRONMENT"]]["domain_name"]
|
44
|
+
|
45
|
+
# Assign the global domain name configuration unless domain_name was set to false in environment config.yaml
|
46
|
+
domain_name ||= config["domain_name"] unless domain_name.class == FalseClass
|
47
|
+
|
48
|
+
# Set domain name to an empty string if domain_name was set to false
|
49
|
+
domain_name = "" if domain_name.class == FalseClass
|
50
|
+
|
51
|
+
# If the no_subdomain setting for environments in config.yaml is omitted or set to false, set subdomain to environment, else assign empty string
|
52
|
+
subdomain = (config["environments"][ENV["ENVIRONMENT"]]["no_subdomain"].nil? || config["environments"][ENV["ENVIRONMENT"]]["no_subdomain"] == false) && ENV["ENVIRONMENT"] || ""
|
53
|
+
|
54
|
+
# Return an array with t he subdomain and domain_name
|
55
|
+
[subdomain, domain_name]
|
56
|
+
end
|
57
|
+
|
58
|
+
subdomain, domain_name = read_domain_name
|
59
|
+
|
60
|
+
raise "ENVIRONMENT not set" unless ENV["ENVIRONMENT"]
|
61
|
+
warn "WARNING: domain_name not set in config.yaml... Route53 Hosted Zone will not be created" if domain_name.empty?
|
62
|
+
|
63
|
+
module DependencyStack
|
64
|
+
extend ActiveSupport::Concern
|
65
|
+
include Rubycfn
|
66
|
+
|
67
|
+
included do
|
68
|
+
self.class_eval(File.open("bootstrap/dependency_stack.rb").read)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
stack = include DependencyStack # rubocop:disable Style/MixinUsage
|
73
|
+
template = stack.render_template
|
74
|
+
|
75
|
+
client = Aws::CloudFormation::Client.new
|
76
|
+
|
77
|
+
stack_exists = false
|
78
|
+
previous_statuses = []
|
79
|
+
80.times do
|
80
|
+
previous_events = get_prior_events(client, "DependencyStack")
|
81
|
+
previous_statuses = previous_events.map(&:event_id)
|
82
|
+
stack_exists = previous_events.size.to_i.positive? ? true : false
|
83
|
+
break unless stack_exists
|
84
|
+
|
85
|
+
events_last_deploy = get_events_last_deploy(previous_events)
|
86
|
+
last_event = events_last_deploy.shift
|
87
|
+
break if last_event \
|
88
|
+
&& (last_event.logical_resource_id == "DependencyStack") \
|
89
|
+
&& (last_event.stack_name == "DependencyStack") \
|
90
|
+
&& (DEPLOYABLE_STATES.include? last_event.resource_status)
|
91
|
+
puts "Stack is currently in #{last_event.resource_status} mode. Waiting for it to finish..." if last_event
|
92
|
+
sleep 15
|
93
|
+
end
|
94
|
+
|
95
|
+
template = inject_dummy_resource(template)
|
96
|
+
|
97
|
+
parameters = [
|
98
|
+
{
|
99
|
+
parameter_key: "Environment",
|
100
|
+
parameter_value: subdomain
|
101
|
+
},
|
102
|
+
{
|
103
|
+
parameter_key: "DomainName",
|
104
|
+
parameter_value: domain_name
|
105
|
+
}
|
106
|
+
]
|
107
|
+
|
108
|
+
if stack_exists
|
109
|
+
client.update_stack(
|
110
|
+
stack_name: "DependencyStack",
|
111
|
+
template_body: template,
|
112
|
+
capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM),
|
113
|
+
parameters: parameters,
|
114
|
+
tags: [
|
115
|
+
{
|
116
|
+
key: "team",
|
117
|
+
value: "infra"
|
118
|
+
}
|
119
|
+
]
|
120
|
+
)
|
121
|
+
else
|
122
|
+
client.create_stack(
|
123
|
+
stack_name: "DependencyStack",
|
124
|
+
template_body: template,
|
125
|
+
timeout_in_minutes: 60,
|
126
|
+
capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM),
|
127
|
+
on_failure: "ROLLBACK",
|
128
|
+
parameters: parameters,
|
129
|
+
tags: [
|
130
|
+
{
|
131
|
+
key: "team",
|
132
|
+
value: "infra"
|
133
|
+
}
|
134
|
+
],
|
135
|
+
enable_termination_protection: false
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
shown_log_lines = {}
|
140
|
+
|
141
|
+
360.times do
|
142
|
+
resp = client.describe_stack_events(
|
143
|
+
stack_name: "DependencyStack"
|
144
|
+
)
|
145
|
+
resp.stack_events.to_a.reverse.each_with_index do |event, index|
|
146
|
+
next if previous_statuses.include? event.event_id
|
147
|
+
@completed = false
|
148
|
+
@stack_name = event.stack_name
|
149
|
+
@logical_resource_id = event.logical_resource_id
|
150
|
+
@resource_type = event.resource_type
|
151
|
+
@timestamp = event.timestamp
|
152
|
+
@resource_status = event.resource_status
|
153
|
+
@resource_status_reason = event.resource_status_reason
|
154
|
+
log_line = "[#{@logical_resource_id.green}] #{@timestamp.to_s.blue}: " \
|
155
|
+
"#{@resource_status.white} #{@resource_status_reason.to_s.red}"
|
156
|
+
puts log_line unless shown_log_lines[log_line]
|
157
|
+
shown_log_lines[log_line] = true
|
158
|
+
if (@stack_name == "DependencyStack") \
|
159
|
+
&& (@logical_resource_id == "DependencyStack") \
|
160
|
+
&& (END_STATES.include? @resource_status) \
|
161
|
+
&& (index + 1 == resp.stack_events.to_a.size)
|
162
|
+
@completed = true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
if @completed
|
166
|
+
puts "==================================="
|
167
|
+
puts " STATUS: #{@resource_status} #{@resource_status_reason}"
|
168
|
+
puts "==================================="
|
169
|
+
raise "#{@resource_status} #{@resource_status_reason}" if FAILURE_STATES.include? @resource_status
|
170
|
+
break
|
171
|
+
end
|
172
|
+
sleep(10)
|
173
|
+
end
|
File without changes
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require "rubycfn"
|
2
2
|
require "dotenv"
|
3
|
+
require_relative "core/classes"
|
3
4
|
|
4
|
-
Dotenv.load(".env")
|
5
5
|
Dotenv.load(".env.private")
|
6
|
+
Dotenv.load(".env.dependencies")
|
7
|
+
Dotenv.load(".env")
|
6
8
|
Dotenv.load(".env.#{ENV["ENVIRONMENT"]}")
|
7
9
|
|
8
10
|
# Include all group concerns
|
@@ -21,10 +23,10 @@ module SharedConcerns
|
|
21
23
|
end
|
22
24
|
|
23
25
|
# Include all stack concerns
|
24
|
-
|
25
|
-
|
26
|
-
Dir[File.expand_path("../stacks/",
|
27
|
-
require
|
26
|
+
# Load module code first, and last include main.rb's.
|
27
|
+
2.times do |i|
|
28
|
+
Dir[File.expand_path("../stacks/", __FILE__) + "/**/*.rb"].sort.each do |file|
|
29
|
+
require file unless File.basename(file) == "main.rb" || i.positive?
|
30
|
+
require file if File.basename(file) == "main.rb" && i.positive?
|
28
31
|
end
|
29
|
-
require file
|
30
32
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Concerns
|
4
|
+
module GlobalVariables
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
def load_config(_val)
|
9
|
+
config = YAML.safe_load(File.read("config.yaml"))
|
10
|
+
config["applications"] ||= {}
|
11
|
+
config["environments"] ||= {}
|
12
|
+
config["subnets"] ||= {}
|
13
|
+
config
|
14
|
+
end
|
15
|
+
|
16
|
+
def global_tags(_val)
|
17
|
+
[
|
18
|
+
{
|
19
|
+
"Key": "Team",
|
20
|
+
"Value": "infra"
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"Key": "Environment",
|
24
|
+
"Value": environment.to_s
|
25
|
+
},
|
26
|
+
{
|
27
|
+
"Key": "StackName",
|
28
|
+
"Value": stack_name.to_s
|
29
|
+
}
|
30
|
+
]
|
31
|
+
end
|
32
|
+
|
33
|
+
variable :environment,
|
34
|
+
default: "development",
|
35
|
+
global: true,
|
36
|
+
value: ENV["ENVIRONMENT"]
|
37
|
+
|
38
|
+
variable :region,
|
39
|
+
global: true,
|
40
|
+
default: ENV["DEFAULT_AWS_REGION"],
|
41
|
+
value: ENV["AWS_REGION"]
|
42
|
+
|
43
|
+
variable :infra_config,
|
44
|
+
global: true,
|
45
|
+
filter: :load_config
|
46
|
+
|
47
|
+
variable :stack_name,
|
48
|
+
global: true,
|
49
|
+
default: infra_config["environments"][environment]["stack_name"]
|
50
|
+
|
51
|
+
variable :default_tags,
|
52
|
+
global: true,
|
53
|
+
filter: :global_tags
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
File without changes
|
@@ -33,6 +33,15 @@ module Concerns
|
|
33
33
|
"#{stack_name}-#{Git::Revision.commit}" \
|
34
34
|
"#{Git::Revision.dirty? ? "-dirty" : ""}"
|
35
35
|
end
|
36
|
+
|
37
|
+
def generate_bootstrap_parameters
|
38
|
+
File.open(".env.dependencies#{environment == "rspec" && ".rspec" || ""}").read.each_line do |line|
|
39
|
+
line.strip!
|
40
|
+
param, _value = line.split("=")
|
41
|
+
parameter param.to_sym,
|
42
|
+
description: param
|
43
|
+
end
|
44
|
+
end
|
36
45
|
end
|
37
46
|
end
|
38
47
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module AcmStack
|
2
|
+
module CertificateManager
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
included do
|
5
|
+
resource :certificate_provider_function,
|
6
|
+
type: "AWS::Lambda::Function" do |r|
|
7
|
+
r.property(:code) do
|
8
|
+
{
|
9
|
+
"S3Bucket": "xebia-${AWS::Region}".fnsub,
|
10
|
+
"S3Key": "cfn-certificate-provider-0.2.4.zip"
|
11
|
+
}
|
12
|
+
end
|
13
|
+
r.property(:handler) { "provider.handler" }
|
14
|
+
r.property(:role) { :lambda_execution_role.ref(:arn) }
|
15
|
+
r.property(:runtime) { "python3.6" }
|
16
|
+
r.property(:memory_size) { 128 }
|
17
|
+
r.property(:timeout) { 300 }
|
18
|
+
end
|
19
|
+
|
20
|
+
resource :lambda_execution_role,
|
21
|
+
type: "AWS::IAM::Role" do |r|
|
22
|
+
r.property(:assume_role_policy_document) do
|
23
|
+
{
|
24
|
+
"Version": "2012-10-17",
|
25
|
+
"Statement": [
|
26
|
+
{
|
27
|
+
"Effect": "Allow",
|
28
|
+
"Principal": {
|
29
|
+
"Service": [
|
30
|
+
"lambda.amazonaws.com"
|
31
|
+
]
|
32
|
+
},
|
33
|
+
"Action": [
|
34
|
+
"sts:AssumeRole"
|
35
|
+
]
|
36
|
+
}
|
37
|
+
]
|
38
|
+
}
|
39
|
+
end
|
40
|
+
r.property(:path) { "/" }
|
41
|
+
r.property(:policies) do
|
42
|
+
[
|
43
|
+
{
|
44
|
+
"PolicyName": "CertificateProviderExecutionRole",
|
45
|
+
"PolicyDocument": {
|
46
|
+
"Version": "2012-10-17",
|
47
|
+
"Statement": [
|
48
|
+
{
|
49
|
+
"Effect": "Allow",
|
50
|
+
"Resource": "*",
|
51
|
+
"Action": [
|
52
|
+
"acm:RequestCertificate",
|
53
|
+
"acm:DescribeCertificate",
|
54
|
+
"acm:UpdateCertificateOptions",
|
55
|
+
"acm:DeleteCertificate"
|
56
|
+
]
|
57
|
+
},
|
58
|
+
{
|
59
|
+
"Effect": "Allow",
|
60
|
+
"Action": "lambda:InvokeFunction",
|
61
|
+
"Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:*".fnsub
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"Effect": "Allow",
|
65
|
+
"Action": %w(logs:CreateLogGroup logs:CreateLogStream logs:PutLogEvents),
|
66
|
+
"Resource": "arn:aws:logs:*:*:*"
|
67
|
+
}
|
68
|
+
]
|
69
|
+
}
|
70
|
+
}
|
71
|
+
]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
output :certificate_provider_function_arn,
|
76
|
+
value: :certificate_provider_function.ref(:arn)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
|
-
module
|
1
|
+
module AcmStack
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
include Rubycfn
|
4
|
-
|
5
4
|
included do
|
6
5
|
include Concerns::GlobalVariables
|
7
6
|
include Concerns::SharedMethods
|
8
|
-
include
|
7
|
+
include AcmStack::CertificateManager
|
9
8
|
|
10
|
-
description generate_stack_description("
|
9
|
+
description generate_stack_description("AcmStack")
|
11
10
|
end
|
12
11
|
end
|