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.
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