rubycfn 0.4.10 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -1
  3. data/Gemfile.lock +1 -1
  4. data/README.md +43 -67
  5. data/bin/rubycfn +17 -73
  6. data/lib/cli_methods.rb +2 -2
  7. data/lib/rubycfn/version.rb +1 -1
  8. data/templates/.env +2 -0
  9. data/templates/.env.acceptance +1 -0
  10. data/templates/.env.dependencies.rspec +6 -0
  11. data/templates/.env.development +1 -0
  12. data/templates/.env.production +1 -0
  13. data/templates/.env.rspec +1 -0
  14. data/templates/.env.test +1 -0
  15. data/templates/{.gitignore.erb → .gitignore} +3 -0
  16. data/templates/{.rubocop.yml.erb → .rubocop.yml} +14 -1
  17. data/templates/{Gemfile.erb → Gemfile} +0 -1
  18. data/templates/README.md +57 -0
  19. data/templates/{Rakefile.erb → Rakefile} +15 -8
  20. data/templates/bootstrap/dependency_stack.rb +49 -0
  21. data/templates/config.yaml +65 -0
  22. data/templates/lib/aws_helper/aws_sdk.rb +30 -0
  23. data/templates/{compiler.rb.erb → lib/aws_helper/compiler.rb} +15 -9
  24. data/templates/{dependencies.rb.erb → lib/aws_helper/dependencies.rb} +5 -3
  25. data/templates/{deploy.rb.erb → lib/aws_helper/deploy.rb} +6 -4
  26. data/templates/lib/aws_helper/helpers.rb +3 -0
  27. data/templates/{main_aws_helper.rb.erb → lib/aws_helper/main.rb} +0 -0
  28. data/templates/{upload_stack.rb.erb → lib/aws_helper/upload_stack.rb} +15 -6
  29. data/templates/lib/core/applications.rb +479 -0
  30. data/templates/lib/core/assume_role.rb +40 -0
  31. data/templates/lib/core/classes.rb +25 -0
  32. data/templates/{core_compile.rb.erb → lib/core/compile.rb} +1 -0
  33. data/templates/lib/core/dependencies.rb +22 -0
  34. data/templates/{core_deploy.rb.erb → lib/core/deploy.rb} +20 -10
  35. data/templates/lib/core/git.rb +15 -0
  36. data/templates/lib/core/init.rb +173 -0
  37. data/templates/{core_upload.rb.erb → lib/core/upload.rb} +0 -0
  38. data/templates/{main.rb.erb → lib/main.rb} +8 -6
  39. data/templates/lib/shared_concerns/global_variables.rb +56 -0
  40. data/templates/{helper_methods.rb.erb → lib/shared_concerns/helper_functions.rb} +0 -0
  41. data/templates/lib/shared_concerns/helper_methods.rb +3 -0
  42. data/templates/{shared_methods.rb.erb → lib/shared_concerns/shared_methods.rb} +9 -0
  43. data/templates/lib/stacks/acm_stack/certificate_manager.rb +79 -0
  44. data/templates/{new_stack.rb.erb → lib/stacks/acm_stack/main.rb} +3 -4
  45. data/templates/lib/stacks/ecs_stack/ecs_cluster.rb +344 -0
  46. data/templates/lib/stacks/ecs_stack/lifecycle_hook.rb +188 -0
  47. data/templates/lib/stacks/ecs_stack/load_balancer.rb +68 -0
  48. data/templates/{ecs_stack.rb.erb → lib/stacks/ecs_stack/main.rb} +2 -1
  49. data/templates/{project_stack.rb.erb → lib/stacks/parent_stack/main.rb} +2 -2
  50. data/templates/lib/stacks/parent_stack/parent.rb +18 -0
  51. data/templates/lib/stacks/vpc_stack/infra_vpc.rb +193 -0
  52. data/templates/{vpc_stack.rb.erb → lib/stacks/vpc_stack/main.rb} +1 -2
  53. data/templates/{parent_stack_spec.rb.erb → spec/lib/parent_spec.rb} +2 -5
  54. data/templates/{spec_helper.rb.erb → spec/spec_helper.rb} +2 -2
  55. metadata +54 -44
  56. data/format.vim +0 -3
  57. data/templates/.env.erb +0 -4
  58. data/templates/.env.production.erb +0 -6
  59. data/templates/.env.rspec.erb +0 -6
  60. data/templates/.env.test.erb +0 -6
  61. data/templates/.gitlab-ci.yml.erb +0 -75
  62. data/templates/aws_sdk.rb.erb +0 -18
  63. data/templates/core_diff.rb.erb +0 -59
  64. data/templates/ecs_stack_concern.rb.erb +0 -20
  65. data/templates/global_variables.rb.erb +0 -16
  66. data/templates/helpers.rb.erb +0 -7
  67. data/templates/new_concern.rb.erb +0 -10
  68. data/templates/project_concern.rb.erb +0 -26
  69. data/templates/subnets.rb.erb +0 -18
  70. data/templates/vpc_concerns.rb.erb +0 -87
  71. 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
@@ -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
- 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
- require subfile
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
@@ -0,0 +1,3 @@
1
+ def file_to_inline(filename)
2
+ File.open(filename).read.split("\n")
3
+ end
@@ -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 <%= stack_name %>
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 <%= stack_name %>::MyModule
7
+ include AcmStack::CertificateManager
9
8
 
10
- description generate_stack_description("<%= stack_name %>")
9
+ description generate_stack_description("AcmStack")
11
10
  end
12
11
  end