rubycfn 0.4.10 → 0.5.0
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/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
|