domed-city 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/dome +6 -5
- data/lib/dome.rb +3 -0
- data/lib/dome/environment.rb +38 -189
- data/lib/dome/helpers/shell.rb +9 -0
- data/lib/dome/state.rb +69 -0
- data/lib/dome/terraform.rb +74 -0
- data/lib/dome/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc30be20e47da94a3dba7850dcbeace2d40dcd7f
|
4
|
+
data.tar.gz: 8e9255071a4b955afefdfc7af8dd833b7bb88778
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f692ed025597cbda665c7f42dc4442abc71be05d7288fdc3c30b43a5eeb20dfe08e466f0351ae473fa001584a347d45d44249059edca15cfc6189111ab5ff0ab
|
7
|
+
data.tar.gz: 0ca6e96eb0c50e23663c45d971ab341d2712748ba7cf982e47974a8c239fb6c5b94b9f133ca6dda4d2684c02498a0e25f1269a73bbc609b741b593577a5f7f21
|
data/bin/dome
CHANGED
@@ -19,22 +19,23 @@ EOS
|
|
19
19
|
opt :state, 'Synchronises the Terraform state'
|
20
20
|
end
|
21
21
|
|
22
|
+
# TODO: hopefully we can flip this logic to DRY it up
|
22
23
|
if opts[:plan]
|
23
|
-
@dome = Dome::
|
24
|
+
@dome = Dome::Terraform.new
|
24
25
|
@dome.validate_environment
|
25
26
|
@dome.plan
|
26
27
|
elsif opts[:apply]
|
27
|
-
@dome = Dome::
|
28
|
+
@dome = Dome::Terraform.new
|
28
29
|
@dome.validate_environment
|
29
30
|
@dome.apply
|
30
31
|
elsif opts[:plan_destroy]
|
31
|
-
@dome = Dome::
|
32
|
+
@dome = Dome::Terraform.new
|
32
33
|
@dome.validate_environment
|
33
34
|
@dome.plan_destroy
|
34
35
|
elsif opts[:state]
|
35
|
-
@dome = Dome::
|
36
|
+
@dome = Dome::Terraform.new
|
36
37
|
@dome.validate_environment
|
37
|
-
@dome.
|
38
|
+
@dome.state.s3_state
|
38
39
|
else
|
39
40
|
Trollop.educate
|
40
41
|
end
|
data/lib/dome.rb
CHANGED
data/lib/dome/environment.rb
CHANGED
@@ -1,225 +1,74 @@
|
|
1
1
|
module Dome
|
2
2
|
class Environment
|
3
|
+
attr_reader :environment, :account, :team
|
4
|
+
|
3
5
|
def initialize
|
4
|
-
@environment
|
5
|
-
@account
|
6
|
-
@team
|
7
|
-
@tfstate_bucket = "#{@team}-tfstate-#{@environment}"
|
8
|
-
@tfstate_s3_obj = "#{@environment}-terraform.tfstate"
|
9
|
-
@varfile = 'params/env.tfvars'
|
10
|
-
@plan = "plans/#{@account}-#{@environment}-plan.tf"
|
11
|
-
@state_file = "state-files/#{@environment}-terraform.tfstate"
|
6
|
+
@environment = Dir.pwd.split('/')[-1]
|
7
|
+
@account = Dir.pwd.split('/')[-2]
|
8
|
+
@team = @account.match(/(\w+)-\w+\z/)[1]
|
12
9
|
end
|
13
10
|
|
14
|
-
|
15
|
-
# Environment stuff
|
16
|
-
# --------------------------------------------------------------
|
17
|
-
|
18
|
-
def valid_accounts
|
11
|
+
def accounts
|
19
12
|
%w(deirdre-dev deirdre-prd)
|
20
13
|
end
|
21
14
|
|
22
|
-
def
|
15
|
+
def non_production_environments
|
23
16
|
%w(infradev sit qa stg)
|
24
17
|
end
|
25
18
|
|
26
|
-
def
|
19
|
+
def production_environments
|
27
20
|
%w(infraprd prd)
|
28
21
|
end
|
29
22
|
|
30
|
-
def
|
31
|
-
puts "Environment: #{@environment}"
|
32
|
-
puts "Account: #{@account}"
|
33
|
-
|
34
|
-
invalid_account_message(account) unless valid_account? @account
|
35
|
-
invalid_environment_message(account, environment) unless valid_environment?(@account, @environment)
|
36
|
-
|
37
|
-
set_aws_credentials(@account)
|
38
|
-
end
|
39
|
-
|
40
|
-
def set_aws_credentials(account)
|
23
|
+
def aws_credentials
|
41
24
|
begin
|
42
|
-
@
|
25
|
+
@aws_credentials ||= AWS::ProfileParser.new.get(@account)
|
43
26
|
rescue RuntimeError
|
44
|
-
raise "No credentials found for account: '#{account}'."
|
27
|
+
raise "No credentials found for account: '#{@account}'."
|
45
28
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
29
|
+
end
|
30
|
+
|
31
|
+
def populate_aws_access_keys
|
32
|
+
ENV['AWS_ACCESS_KEY_ID'] = aws_credentials[:access_key_id]
|
33
|
+
ENV['AWS_SECRET_ACCESS_KEY'] = aws_credentials[:secret_access_key]
|
34
|
+
ENV['AWS_DEFAULT_REGION'] = aws_credentials[:region]
|
49
35
|
end
|
50
36
|
|
51
37
|
def valid_account?(account)
|
52
|
-
|
38
|
+
puts "Account: #{account.colorize(:green)}"
|
39
|
+
accounts.include? account
|
53
40
|
end
|
54
41
|
|
55
42
|
def valid_environment?(account, environment)
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
43
|
+
puts "Environment: #{environment.colorize(:green)}"
|
44
|
+
if account[-4..-1] == '-dev'
|
45
|
+
non_production_environments.include? environment
|
46
|
+
else
|
47
|
+
production_environments.include? environment
|
60
48
|
end
|
61
49
|
end
|
62
50
|
|
63
|
-
def invalid_account_message
|
64
|
-
puts "\n'#{account}' is not a valid account.\n".colorize(:red)
|
65
|
-
puts "
|
51
|
+
def invalid_account_message
|
52
|
+
puts "\n'#{@account}' is not a valid account.\n".colorize(:red)
|
53
|
+
puts "The 'account' and 'environment' values are calculated based on your current directory.\n".colorize(:red)
|
54
|
+
puts "Valid accounts are: #{accounts}."
|
66
55
|
puts "\nEither:"
|
67
56
|
puts '1. Set your .aws/config to one of the valid accounts above.'
|
68
57
|
puts '2. Ensure you are running this from the correct directory.'
|
69
58
|
exit 1
|
70
59
|
end
|
71
60
|
|
72
|
-
def invalid_environment_message
|
73
|
-
puts "\n'#{environment}' is not a valid environment for the account: '#{account}'.\n".colorize(:red)
|
74
|
-
|
75
|
-
puts "Valid environments are: #{env}"
|
76
|
-
exit 1
|
77
|
-
end
|
78
|
-
|
79
|
-
# --------------------------------------------------------------
|
80
|
-
# Terraform commands
|
81
|
-
# --------------------------------------------------------------
|
82
|
-
|
83
|
-
def plan
|
84
|
-
puts "current dir: #{Dir.pwd}"
|
85
|
-
delete_terraform_directory
|
86
|
-
delete_plan_file
|
87
|
-
install_terraform_modules
|
88
|
-
fetch_s3_state
|
89
|
-
create_plan
|
90
|
-
end
|
91
|
-
|
92
|
-
def apply
|
93
|
-
command = "terraform apply #{@plan}"
|
94
|
-
failure_message = 'something went wrong when applying the TF plan'
|
95
|
-
execute_command(command, failure_message)
|
96
|
-
end
|
97
|
-
|
98
|
-
def create_plan
|
99
|
-
command = "terraform plan -module-depth=1 -refresh=true -out=#{@plan} -var-file=#{@varfile}"
|
100
|
-
failure_message = 'something went wrong when creating the TF plan'
|
101
|
-
execute_command(command, failure_message)
|
102
|
-
end
|
103
|
-
|
104
|
-
def delete_terraform_directory
|
105
|
-
puts 'Deleting older terraform module cache dir ...'.colorize(:green)
|
106
|
-
terraform_directory = '.terraform'
|
107
|
-
puts "About to delete directory: #{terraform_directory}"
|
108
|
-
FileUtils.rm_rf '.terraform/'
|
109
|
-
end
|
110
|
-
|
111
|
-
def delete_plan_file
|
112
|
-
puts 'Deleting older terraform plan ...'.colorize(:green)
|
113
|
-
puts "About to delete: #{@plan}"
|
114
|
-
FileUtils.rm_f @plan
|
115
|
-
end
|
116
|
-
|
117
|
-
def plan_destroy
|
118
|
-
delete_terraform_directory
|
119
|
-
delete_plan_file
|
120
|
-
install_terraform_modules
|
121
|
-
create_destroy_plan
|
122
|
-
end
|
123
|
-
|
124
|
-
def create_destroy_plan
|
125
|
-
command = "terraform plan -destroy -module-depth=1 -out=#{@plan} -var-file=#{@varfile}"
|
126
|
-
failure_message = 'something went wrong when creating the TF plan'
|
127
|
-
execute_command(command, failure_message)
|
128
|
-
end
|
129
|
-
|
130
|
-
def install_terraform_modules
|
131
|
-
command = 'terraform get -update=true'
|
132
|
-
failure_message = 'something went wrong when pulling remote TF modules'
|
133
|
-
execute_command(command, failure_message)
|
134
|
-
end
|
61
|
+
def invalid_environment_message
|
62
|
+
puts "\n'#{@environment}' is not a valid environment for the account: '#{@account}'.\n".colorize(:red)
|
63
|
+
puts "The 'account' and 'environment' values are calculated based on your current directory.\n".colorize(:red)
|
135
64
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
resp = s3_client.list_buckets
|
144
|
-
resp.buckets.each { |bucket| return true if bucket.name == tfstate_bucket }
|
145
|
-
false
|
146
|
-
end
|
147
|
-
|
148
|
-
def create_bucket(name)
|
149
|
-
begin
|
150
|
-
s3_client.create_bucket(bucket: name, acl: 'private')
|
151
|
-
rescue Aws::S3::Errors::BucketAlreadyExists
|
152
|
-
raise 'The S3 bucket must be globally unique. See https://docs.aws.amazon.com/AmazonS3/latest/gsg/CreatingABucket.html'.colorize(:red)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def enable_bucket_versioning(bucket_name)
|
157
|
-
puts 'Enabling versioning on the S3 bucket - http://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html'.colorize(:green)
|
158
|
-
s3_client.put_bucket_versioning(bucket: bucket_name,
|
159
|
-
versioning_configuration: {
|
160
|
-
mfa_delete: 'Disabled',
|
161
|
-
status: 'Enabled'
|
162
|
-
})
|
163
|
-
end
|
164
|
-
|
165
|
-
def put_empty_object_in_bucket(bucket_name, key_name)
|
166
|
-
puts "Putting an empty object with key: #{key_name} into bucket: #{bucket_name}".colorize(:green)
|
167
|
-
s3_client.put_object(
|
168
|
-
bucket: bucket_name,
|
169
|
-
key: key_name,
|
170
|
-
body: ''
|
171
|
-
)
|
172
|
-
end
|
173
|
-
|
174
|
-
def create_remote_state_bucket(tfstate_bucket, tfstate_s3_obj)
|
175
|
-
create_bucket tfstate_bucket
|
176
|
-
enable_bucket_versioning tfstate_bucket
|
177
|
-
put_empty_object_in_bucket(tfstate_bucket, tfstate_s3_obj)
|
178
|
-
end
|
179
|
-
|
180
|
-
def bootstrap_s3_state
|
181
|
-
if s3_bucket_exists?(@tfstate_bucket)
|
182
|
-
synchronise_s3_state
|
183
|
-
else
|
184
|
-
create_remote_state_bucket(@tfstate_bucket, @tfstate_s3_obj)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
def synchronise_s3_state
|
189
|
-
puts 'Synchronising the remote S3 state...'
|
190
|
-
# not clear for me if the -state in the below command matters
|
191
|
-
command = 'terraform remote config'\
|
192
|
-
' -backend=S3'\
|
193
|
-
" -backend-config='bucket=#{@tfstate_bucket}' -backend-config='key=#{@tfstate_s3_obj}'"\
|
194
|
-
" -state=#{@state_file}"
|
195
|
-
failure_message = 'something went wrong when creating the S3 state'
|
196
|
-
execute_command(command, failure_message)
|
197
|
-
end
|
198
|
-
|
199
|
-
def synchronise_s3_state_setup
|
200
|
-
puts "Setting up the initial terraform S3 state in the S3 bucket: #{@tfstate_bucket.colorize(:green)} for account: #{@account.colorize(:green)} and environment: #{@environment.colorize(:green)} ..."
|
201
|
-
command = 'terraform remote config'\
|
202
|
-
' -backend=S3'\
|
203
|
-
" -backend-config='bucket=#{@tfstate_bucket}' -backend-config='key=#{@tfstate_s3_obj}'"
|
204
|
-
failure_message = 'something went wrong when creating the S3 state'
|
205
|
-
execute_command(command, failure_message)
|
206
|
-
end
|
207
|
-
|
208
|
-
def fetch_s3_state
|
209
|
-
command = 'terraform remote config -backend=S3'\
|
210
|
-
" -backend-config='bucket=#{@tfstate_bucket}' -backend-config='key=#{@tfstate_s3_obj}'"
|
211
|
-
failure_message = 'something went wrong when fetching the S3 state'
|
212
|
-
execute_command(command, failure_message)
|
213
|
-
end
|
214
|
-
|
215
|
-
# --------------------------------------------------------------
|
216
|
-
# Misc.
|
217
|
-
# --------------------------------------------------------------
|
218
|
-
|
219
|
-
def execute_command(command, failure_message)
|
220
|
-
puts "About to execute command: #{command}"
|
221
|
-
success = system command
|
222
|
-
puts failure_message unless success
|
65
|
+
env = if account[-4..-1] == '-dev'
|
66
|
+
non_production_environments
|
67
|
+
else
|
68
|
+
production_environments
|
69
|
+
end
|
70
|
+
puts "Valid environments are: #{env}."
|
71
|
+
exit 1
|
223
72
|
end
|
224
73
|
end
|
225
74
|
end
|
data/lib/dome/state.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Dome
|
2
|
+
class State
|
3
|
+
include Dome::Shell
|
4
|
+
|
5
|
+
def initialize(environment)
|
6
|
+
@environment = environment
|
7
|
+
@state_bucket = "#{@environment.team}-tfstate-#{@environment.environment}"
|
8
|
+
@state_file = "#{@environment.environment}-terraform.tfstate"
|
9
|
+
end
|
10
|
+
|
11
|
+
def s3_client
|
12
|
+
@s3_client ||= Aws::S3::Client.new(@environment.aws_credentials)
|
13
|
+
end
|
14
|
+
|
15
|
+
def s3_bucket_exists?(bucket_name)
|
16
|
+
resp = s3_client.list_buckets
|
17
|
+
resp.buckets.each { |bucket| return true if bucket.name == bucket_name }
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_bucket(name)
|
22
|
+
begin
|
23
|
+
s3_client.create_bucket(bucket: name, acl: 'private')
|
24
|
+
rescue Aws::S3::Errors::BucketAlreadyExists
|
25
|
+
raise 'The S3 bucket must be globally unique. See https://docs.aws.amazon.com/AmazonS3/latest/gsg/CreatingABucket.html'.colorize(:red)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def enable_bucket_versioning(bucket_name)
|
30
|
+
puts 'Enabling versioning on the S3 bucket - http://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html'.colorize(:green)
|
31
|
+
s3_client.put_bucket_versioning(bucket: bucket_name,
|
32
|
+
versioning_configuration: {
|
33
|
+
mfa_delete: 'Disabled',
|
34
|
+
status: 'Enabled'
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
def put_empty_object_in_bucket(bucket_name, key_name)
|
39
|
+
puts "Putting an empty object with key: #{key_name} into bucket: #{bucket_name}".colorize(:green)
|
40
|
+
s3_client.put_object(
|
41
|
+
bucket: bucket_name,
|
42
|
+
key: key_name,
|
43
|
+
body: ''
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_remote_state_bucket(state_bucket, state_file)
|
48
|
+
create_bucket state_bucket
|
49
|
+
enable_bucket_versioning state_bucket
|
50
|
+
put_empty_object_in_bucket(state_bucket, state_file)
|
51
|
+
end
|
52
|
+
|
53
|
+
def s3_state
|
54
|
+
if s3_bucket_exists?(@state_bucket)
|
55
|
+
synchronise_s3_state
|
56
|
+
else
|
57
|
+
create_remote_state_bucket(@state_bucket, @state_file)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def synchronise_s3_state
|
62
|
+
puts 'Synchronising the remote S3 state...'
|
63
|
+
command = 'terraform remote config -backend=S3'\
|
64
|
+
" -backend-config='bucket=#{@state_bucket}' -backend-config='key=#{@state_file}'"
|
65
|
+
failure_message = 'Something went wrong when synchronising the S3 state.'
|
66
|
+
execute_command(command, failure_message)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Dome
|
2
|
+
class Terraform
|
3
|
+
include Dome::Shell
|
4
|
+
|
5
|
+
attr_reader :state
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@environment = Dome::Environment.new
|
9
|
+
@state = Dome::State.new(@environment)
|
10
|
+
@plan_file = "plans/#{@environment.account}-#{@environment.environment}-plan.tf"
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate_environment
|
14
|
+
environment_name = @environment.environment
|
15
|
+
account_name = @environment.account
|
16
|
+
@environment.invalid_account_message unless @environment.valid_account? account_name
|
17
|
+
@environment.invalid_environment_message unless @environment.valid_environment?(account_name, environment_name)
|
18
|
+
@environment.populate_aws_access_keys
|
19
|
+
end
|
20
|
+
|
21
|
+
def plan
|
22
|
+
delete_terraform_directory
|
23
|
+
delete_plan_file
|
24
|
+
install_terraform_modules
|
25
|
+
@state.synchronise_s3_state
|
26
|
+
create_plan
|
27
|
+
end
|
28
|
+
|
29
|
+
def plan_destroy
|
30
|
+
delete_terraform_directory
|
31
|
+
delete_plan_file
|
32
|
+
install_terraform_modules
|
33
|
+
@state.synchronise_s3_state
|
34
|
+
create_destroy_plan
|
35
|
+
end
|
36
|
+
|
37
|
+
def apply
|
38
|
+
command = "terraform apply #{@plan_file}"
|
39
|
+
failure_message = 'something went wrong when applying the TF plan'
|
40
|
+
execute_command(command, failure_message)
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_plan
|
44
|
+
command = "terraform plan -module-depth=1 -refresh=true -out=#{@plan_file} -var-file=params/env.tfvars"
|
45
|
+
failure_message = 'something went wrong when creating the TF plan'
|
46
|
+
execute_command(command, failure_message)
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete_terraform_directory
|
50
|
+
puts 'Deleting older terraform module cache dir ...'.colorize(:green)
|
51
|
+
terraform_directory = '.terraform'
|
52
|
+
puts "About to delete directory: #{terraform_directory}"
|
53
|
+
FileUtils.rm_rf '.terraform/'
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete_plan_file
|
57
|
+
puts 'Deleting older terraform plan ...'.colorize(:green)
|
58
|
+
puts "About to delete: #{@plan_file}"
|
59
|
+
FileUtils.rm_f @plan_file
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_destroy_plan
|
63
|
+
command = "terraform plan -destroy -module-depth=1 -out=#{@plan_file} -var-file=params/env.tfvars"
|
64
|
+
failure_message = 'something went wrong when creating the TF plan'
|
65
|
+
execute_command(command, failure_message)
|
66
|
+
end
|
67
|
+
|
68
|
+
def install_terraform_modules
|
69
|
+
command = 'terraform get -update=true'
|
70
|
+
failure_message = 'something went wrong when pulling remote TF modules'
|
71
|
+
execute_command(command, failure_message)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/dome/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: domed-city
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Snape
|
@@ -141,6 +141,9 @@ files:
|
|
141
141
|
- dome.gemspec
|
142
142
|
- lib/dome.rb
|
143
143
|
- lib/dome/environment.rb
|
144
|
+
- lib/dome/helpers/shell.rb
|
145
|
+
- lib/dome/state.rb
|
146
|
+
- lib/dome/terraform.rb
|
144
147
|
- lib/dome/version.rb
|
145
148
|
homepage: https://github.com/ITV/dome
|
146
149
|
licenses: []
|