domed-city 1.0.2 → 1.1.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/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: []
|