TerraformDevKit 0.3.8 → 0.4.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/.appveyor.yml +8 -1
- data/.rspec +2 -2
- data/.travis.yml +10 -8
- data/Gemfile +4 -4
- data/README.md +239 -239
- data/Rakefile +6 -6
- data/TerraformDevKit.gemspec +36 -36
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/exe/wait_for_url +0 -0
- data/lib/TerraformDevKit.rb +21 -21
- data/lib/TerraformDevKit/aws/aws.rb +65 -65
- data/lib/TerraformDevKit/aws/cloudfront.rb +20 -20
- data/lib/TerraformDevKit/aws/dynamodb.rb +46 -46
- data/lib/TerraformDevKit/aws/s3.rb +44 -44
- data/lib/TerraformDevKit/aws/terraform_remote_state.rb +67 -67
- data/lib/TerraformDevKit/backup_state.rb +18 -18
- data/lib/TerraformDevKit/command.rb +31 -31
- data/lib/TerraformDevKit/download.rb +1 -1
- data/lib/TerraformDevKit/extended_file_utils.rb +21 -21
- data/lib/TerraformDevKit/project_config.rb +10 -10
- data/lib/TerraformDevKit/request.rb +1 -1
- data/lib/TerraformDevKit/version.rb +3 -3
- data/lib/TerraformDevKit/zip_file_generator.rb +47 -47
- data/tasks/devkit.rake +187 -187
- metadata +47 -48
@@ -1,67 +1,67 @@
|
|
1
|
-
require 'aws-sdk-dynamodb'
|
2
|
-
require 'aws-sdk-s3'
|
3
|
-
|
4
|
-
module TerraformDevKit
|
5
|
-
module Aws
|
6
|
-
class TerraformRemoteState
|
7
|
-
ATTRIBUTES = [
|
8
|
-
{
|
9
|
-
attribute_name: 'LockID',
|
10
|
-
attribute_type: 'S'
|
11
|
-
}
|
12
|
-
]
|
13
|
-
KEYS = [
|
14
|
-
{
|
15
|
-
attribute_name: 'LockID',
|
16
|
-
key_type: 'HASH'
|
17
|
-
}
|
18
|
-
]
|
19
|
-
|
20
|
-
def initialize(dynamodb, s3)
|
21
|
-
@dynamodb = dynamodb
|
22
|
-
@s3 = s3
|
23
|
-
end
|
24
|
-
|
25
|
-
def init(environment, project)
|
26
|
-
table_name = table_name(environment, project)
|
27
|
-
return if lock_table_exists_and_is_active(table_name)
|
28
|
-
|
29
|
-
@dynamodb.create_table(table_name, ATTRIBUTES, KEYS, 1, 1)
|
30
|
-
|
31
|
-
begin
|
32
|
-
@s3.create_bucket(state_bucket_name(environment, project))
|
33
|
-
rescue ::Aws::S3::Errors::BucketAlreadyOwnedByYou
|
34
|
-
return
|
35
|
-
end
|
36
|
-
|
37
|
-
sleep(0.2) until lock_table_exists_and_is_active(table_name)
|
38
|
-
end
|
39
|
-
|
40
|
-
def destroy(environment, project)
|
41
|
-
table_name = table_name(environment, project)
|
42
|
-
|
43
|
-
@dynamodb.delete_table(table_name)
|
44
|
-
@s3.delete_bucket(state_bucket_name(environment, project))
|
45
|
-
end
|
46
|
-
|
47
|
-
private_class_method
|
48
|
-
def lock_table_exists_and_is_active(table_name)
|
49
|
-
begin
|
50
|
-
return @dynamodb.get_table_status(table_name) == 'ACTIVE'
|
51
|
-
rescue ::Aws::DynamoDB::Errors::ResourceNotFoundException
|
52
|
-
return false
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
private_class_method
|
57
|
-
def table_name(environment, project)
|
58
|
-
"#{project.acronym}-#{environment.name}-lock-table"
|
59
|
-
end
|
60
|
-
|
61
|
-
private_class_method
|
62
|
-
def state_bucket_name(environment, project)
|
63
|
-
"#{project.name}-#{environment.name}-state"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
1
|
+
require 'aws-sdk-dynamodb'
|
2
|
+
require 'aws-sdk-s3'
|
3
|
+
|
4
|
+
module TerraformDevKit
|
5
|
+
module Aws
|
6
|
+
class TerraformRemoteState
|
7
|
+
ATTRIBUTES = [
|
8
|
+
{
|
9
|
+
attribute_name: 'LockID',
|
10
|
+
attribute_type: 'S'
|
11
|
+
}
|
12
|
+
]
|
13
|
+
KEYS = [
|
14
|
+
{
|
15
|
+
attribute_name: 'LockID',
|
16
|
+
key_type: 'HASH'
|
17
|
+
}
|
18
|
+
]
|
19
|
+
|
20
|
+
def initialize(dynamodb, s3)
|
21
|
+
@dynamodb = dynamodb
|
22
|
+
@s3 = s3
|
23
|
+
end
|
24
|
+
|
25
|
+
def init(environment, project)
|
26
|
+
table_name = table_name(environment, project)
|
27
|
+
return if lock_table_exists_and_is_active(table_name)
|
28
|
+
|
29
|
+
@dynamodb.create_table(table_name, ATTRIBUTES, KEYS, 1, 1)
|
30
|
+
|
31
|
+
begin
|
32
|
+
@s3.create_bucket(state_bucket_name(environment, project))
|
33
|
+
rescue ::Aws::S3::Errors::BucketAlreadyOwnedByYou
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
sleep(0.2) until lock_table_exists_and_is_active(table_name)
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy(environment, project)
|
41
|
+
table_name = table_name(environment, project)
|
42
|
+
|
43
|
+
@dynamodb.delete_table(table_name)
|
44
|
+
@s3.delete_bucket(state_bucket_name(environment, project))
|
45
|
+
end
|
46
|
+
|
47
|
+
private_class_method
|
48
|
+
def lock_table_exists_and_is_active(table_name)
|
49
|
+
begin
|
50
|
+
return @dynamodb.get_table_status(table_name) == 'ACTIVE'
|
51
|
+
rescue ::Aws::DynamoDB::Errors::ResourceNotFoundException
|
52
|
+
return false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private_class_method
|
57
|
+
def table_name(environment, project)
|
58
|
+
"#{project.acronym}-#{environment.name}-lock-table"
|
59
|
+
end
|
60
|
+
|
61
|
+
private_class_method
|
62
|
+
def state_bucket_name(environment, project)
|
63
|
+
"#{project.name}-#{environment.name}-state"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,18 +1,18 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
|
3
|
-
require_relative 'zip_file_generator'
|
4
|
-
|
5
|
-
module TerraformDevKit
|
6
|
-
class BackupState
|
7
|
-
def self.backup(prefix)
|
8
|
-
backup_path = ENV['TM_STATE_BACKUP_PATH']
|
9
|
-
return if backup_path.nil?
|
10
|
-
|
11
|
-
filename = "#{prefix}failure_state.zip"
|
12
|
-
ZipFileGenerator.new('.', filename).write
|
13
|
-
|
14
|
-
FileUtils.cp(filename, backup_path)
|
15
|
-
puts "Copied state to #{File.join(backup_path, filename)}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
require_relative 'zip_file_generator'
|
4
|
+
|
5
|
+
module TerraformDevKit
|
6
|
+
class BackupState
|
7
|
+
def self.backup(prefix)
|
8
|
+
backup_path = ENV['TM_STATE_BACKUP_PATH']
|
9
|
+
return if backup_path.nil?
|
10
|
+
|
11
|
+
filename = "#{prefix}failure_state.zip"
|
12
|
+
ZipFileGenerator.new('.', filename).write
|
13
|
+
|
14
|
+
FileUtils.cp(filename, backup_path)
|
15
|
+
puts "Copied state to #{File.join(backup_path, filename)}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,31 +1,31 @@
|
|
1
|
-
require 'open3'
|
2
|
-
|
3
|
-
require 'TerraformDevKit/errors/command_error'
|
4
|
-
|
5
|
-
module TerraformDevKit
|
6
|
-
class Command
|
7
|
-
def self.run(cmd, directory: Dir.pwd, print_output: true)
|
8
|
-
out = IO.popen(cmd, err: %i[child out], chdir: directory) do |io|
|
9
|
-
begin
|
10
|
-
out = ''
|
11
|
-
loop do
|
12
|
-
chunk = io.readpartial(4096)
|
13
|
-
print chunk if print_output
|
14
|
-
out += chunk
|
15
|
-
end
|
16
|
-
rescue EOFError; end
|
17
|
-
out
|
18
|
-
end
|
19
|
-
|
20
|
-
out = process_output(out)
|
21
|
-
$?.exitstatus.zero? || (raise CommandError.new(cmd, out))
|
22
|
-
out
|
23
|
-
end
|
24
|
-
|
25
|
-
private_class_method
|
26
|
-
def self.process_output(out)
|
27
|
-
out.split("\n")
|
28
|
-
.map { |line| line.tr("\r\n", '') }
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
require 'TerraformDevKit/errors/command_error'
|
4
|
+
|
5
|
+
module TerraformDevKit
|
6
|
+
class Command
|
7
|
+
def self.run(cmd, directory: Dir.pwd, print_output: true)
|
8
|
+
out = IO.popen(cmd, err: %i[child out], chdir: directory) do |io|
|
9
|
+
begin
|
10
|
+
out = ''
|
11
|
+
loop do
|
12
|
+
chunk = io.readpartial(4096)
|
13
|
+
print chunk if print_output
|
14
|
+
out += chunk
|
15
|
+
end
|
16
|
+
rescue EOFError; end
|
17
|
+
out
|
18
|
+
end
|
19
|
+
|
20
|
+
out = process_output(out)
|
21
|
+
$?.exitstatus.zero? || (raise CommandError.new(cmd, out))
|
22
|
+
out
|
23
|
+
end
|
24
|
+
|
25
|
+
private_class_method
|
26
|
+
def self.process_output(out)
|
27
|
+
out.split("\n")
|
28
|
+
.map { |line| line.tr("\r\n", '') }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -10,7 +10,7 @@ module TerraformDevKit
|
|
10
10
|
puts "Downloading #{url} to #{filename}..."
|
11
11
|
|
12
12
|
open(filename, 'wb') do |file|
|
13
|
-
file << open(url, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE).read
|
13
|
+
file << URI.open(url, ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE).read
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -1,21 +1,21 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'TerraformDevKit/command'
|
3
|
-
require 'TerraformDevKit/os'
|
4
|
-
|
5
|
-
module TerraformDevKit::ExtendedFileUtils
|
6
|
-
def self.copy(files, dest_base_path)
|
7
|
-
files.to_h.each do |dest, src|
|
8
|
-
dest = File.join(dest_base_path, dest)
|
9
|
-
FileUtils.copy_entry(src, dest)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.rm_rf(list, options = {})
|
14
|
-
if TerraformDevKit::OS.host_os == 'windows'
|
15
|
-
windows_path = TerraformDevKit::OS.convert_to_local_path(list)
|
16
|
-
TerraformDevKit::Command.run("rmdir /s/q \"#{windows_path}\"")
|
17
|
-
else
|
18
|
-
FileUtils.rm_rf(list, options)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
1
|
+
require 'fileutils'
|
2
|
+
require 'TerraformDevKit/command'
|
3
|
+
require 'TerraformDevKit/os'
|
4
|
+
|
5
|
+
module TerraformDevKit::ExtendedFileUtils
|
6
|
+
def self.copy(files, dest_base_path)
|
7
|
+
files.to_h.each do |dest, src|
|
8
|
+
dest = File.join(dest_base_path, dest)
|
9
|
+
FileUtils.copy_entry(src, dest)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.rm_rf(list, options = {})
|
14
|
+
if TerraformDevKit::OS.host_os == 'windows'
|
15
|
+
windows_path = TerraformDevKit::OS.convert_to_local_path(list)
|
16
|
+
TerraformDevKit::Command.run("rmdir /s/q \"#{windows_path}\"")
|
17
|
+
else
|
18
|
+
FileUtils.rm_rf(list, options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
module TerraformDevKit
|
2
|
-
class ProjectConfig
|
3
|
-
attr_reader :name, :acronym
|
4
|
-
|
5
|
-
def initialize(project_name, project_acronym = nil)
|
6
|
-
@name = project_name.tr(' ', '-').downcase
|
7
|
-
@acronym = project_acronym || project_name.scan(/\b[a-z]/i).join.upcase
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
1
|
+
module TerraformDevKit
|
2
|
+
class ProjectConfig
|
3
|
+
attr_reader :name, :acronym
|
4
|
+
|
5
|
+
def initialize(project_name, project_acronym = nil)
|
6
|
+
@name = project_name.tr(' ', '-').downcase
|
7
|
+
@acronym = project_acronym || project_name.scan(/\b[a-z]/i).join.upcase
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -18,7 +18,7 @@ module TerraformDevKit
|
|
18
18
|
ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE
|
19
19
|
}
|
20
20
|
options.merge!(@headers)
|
21
|
-
open(url, options)
|
21
|
+
URI.open(url, options)
|
22
22
|
rescue OpenURI::HTTPError => error
|
23
23
|
response = error.io
|
24
24
|
raise if raise_on_codes.include?(response.status[0])
|
@@ -1,3 +1,3 @@
|
|
1
|
-
module TerraformDevKit
|
2
|
-
VERSION = '0.
|
3
|
-
end
|
1
|
+
module TerraformDevKit
|
2
|
+
VERSION = '0.4.0'.freeze
|
3
|
+
end
|
@@ -1,47 +1,47 @@
|
|
1
|
-
require 'zip'
|
2
|
-
|
3
|
-
module TerraformDevKit
|
4
|
-
class ZipFileGenerator
|
5
|
-
def initialize(input_dir, output_file)
|
6
|
-
@input_dir = input_dir
|
7
|
-
@output_file = output_file
|
8
|
-
end
|
9
|
-
|
10
|
-
def write
|
11
|
-
entries = Dir.entries(@input_dir)
|
12
|
-
entries.delete('.')
|
13
|
-
entries.delete('..')
|
14
|
-
Zip::File.open(@output_file, Zip::File::CREATE) do |zipfile|
|
15
|
-
write_entries(entries, '', zipfile)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def write_entries(entries, path, zipfile)
|
22
|
-
entries.each do |e|
|
23
|
-
zip_file_path = path == '' ? e : File.join(path, e)
|
24
|
-
disk_file_path = File.join(@input_dir, zip_file_path)
|
25
|
-
if File.directory?(disk_file_path)
|
26
|
-
write_directory(disk_file_path, zip_file_path, zipfile)
|
27
|
-
else
|
28
|
-
write_file(disk_file_path, zip_file_path, zipfile)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def write_directory(disk_file_path, zip_file_path, zipfile)
|
34
|
-
zipfile.mkdir(zip_file_path)
|
35
|
-
subdir = Dir.entries(disk_file_path)
|
36
|
-
subdir.delete('.')
|
37
|
-
subdir.delete('..')
|
38
|
-
write_entries(subdir, zip_file_path, zipfile)
|
39
|
-
end
|
40
|
-
|
41
|
-
def write_file(disk_file_path, zip_file_path, zipfile)
|
42
|
-
zipfile.get_output_stream(zip_file_path) do |f|
|
43
|
-
f.puts(File.open(disk_file_path, 'rb').read)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
1
|
+
require 'zip'
|
2
|
+
|
3
|
+
module TerraformDevKit
|
4
|
+
class ZipFileGenerator
|
5
|
+
def initialize(input_dir, output_file)
|
6
|
+
@input_dir = input_dir
|
7
|
+
@output_file = output_file
|
8
|
+
end
|
9
|
+
|
10
|
+
def write
|
11
|
+
entries = Dir.entries(@input_dir)
|
12
|
+
entries.delete('.')
|
13
|
+
entries.delete('..')
|
14
|
+
Zip::File.open(@output_file, Zip::File::CREATE) do |zipfile|
|
15
|
+
write_entries(entries, '', zipfile)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def write_entries(entries, path, zipfile)
|
22
|
+
entries.each do |e|
|
23
|
+
zip_file_path = path == '' ? e : File.join(path, e)
|
24
|
+
disk_file_path = File.join(@input_dir, zip_file_path)
|
25
|
+
if File.directory?(disk_file_path)
|
26
|
+
write_directory(disk_file_path, zip_file_path, zipfile)
|
27
|
+
else
|
28
|
+
write_file(disk_file_path, zip_file_path, zipfile)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def write_directory(disk_file_path, zip_file_path, zipfile)
|
34
|
+
zipfile.mkdir(zip_file_path)
|
35
|
+
subdir = Dir.entries(disk_file_path)
|
36
|
+
subdir.delete('.')
|
37
|
+
subdir.delete('..')
|
38
|
+
write_entries(subdir, zip_file_path, zipfile)
|
39
|
+
end
|
40
|
+
|
41
|
+
def write_file(disk_file_path, zip_file_path, zipfile)
|
42
|
+
zipfile.get_output_stream(zip_file_path) do |f|
|
43
|
+
f.puts(File.open(disk_file_path, 'rb').read)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/tasks/devkit.rake
CHANGED
@@ -1,187 +1,187 @@
|
|
1
|
-
require 'rainbow'
|
2
|
-
require 'TerraformDevKit'
|
3
|
-
|
4
|
-
TDK = TerraformDevKit
|
5
|
-
|
6
|
-
raise 'ROOT_PATH is not defined' if defined?(ROOT_PATH).nil?
|
7
|
-
BIN_PATH = File.join(ROOT_PATH, 'bin')
|
8
|
-
CONFIG_FILE ||= File.join(ROOT_PATH, 'config', 'config-%s.yml')
|
9
|
-
|
10
|
-
# Ensure terraform is in the PATH
|
11
|
-
ENV['PATH'] = TDK::OS.join_env_path(
|
12
|
-
TDK::OS.convert_to_local_path(BIN_PATH),
|
13
|
-
ENV['PATH']
|
14
|
-
)
|
15
|
-
|
16
|
-
PLAN_FILE = 'plan.tfplan'.freeze
|
17
|
-
|
18
|
-
def destroy_if_fails(env, task)
|
19
|
-
yield
|
20
|
-
rescue Exception => e
|
21
|
-
puts "ERROR: #{e.message}"
|
22
|
-
puts e.backtrace.join("\n")
|
23
|
-
invoke('destroy', task, env.name) if env.local_backend?
|
24
|
-
raise
|
25
|
-
end
|
26
|
-
|
27
|
-
def invoke(task_name, task_context, env, safe_invoke: false)
|
28
|
-
task_in_context = task_in_current_namespace(task_name, task_context)
|
29
|
-
should_invoke = !safe_invoke || Rake::Task.task_defined?(task_in_context)
|
30
|
-
Rake::Task[task_in_context].invoke(env) if should_invoke
|
31
|
-
end
|
32
|
-
|
33
|
-
def task_in_current_namespace(task_name, current_task)
|
34
|
-
namespace = current_task.scope.path.to_s
|
35
|
-
namespace.empty? ? task_name : "#{namespace}:#{task_name}"
|
36
|
-
end
|
37
|
-
|
38
|
-
def remote_state
|
39
|
-
aws_config = TDK::Aws::AwsConfig.new(TDK::Configuration.get('aws'))
|
40
|
-
dynamo_db = TDK::Aws::DynamoDB.new(
|
41
|
-
aws_config.credentials,
|
42
|
-
aws_config.region
|
43
|
-
)
|
44
|
-
s3 = TDK::Aws::S3.new(
|
45
|
-
aws_config.credentials,
|
46
|
-
aws_config.region
|
47
|
-
)
|
48
|
-
TDK::Aws::TerraformRemoteState.new(dynamo_db, s3)
|
49
|
-
end
|
50
|
-
|
51
|
-
desc 'Prepares the environment to create the infrastructure'
|
52
|
-
task :prepare, [:env] do |_, args|
|
53
|
-
env = TDK::Environment.new(args.env)
|
54
|
-
|
55
|
-
puts "== Configuring environment #{env.name}"
|
56
|
-
|
57
|
-
config_file = CONFIG_FILE % env.config
|
58
|
-
puts "== Loading configuration from #{config_file}"
|
59
|
-
TDK::Configuration.init(config_file)
|
60
|
-
|
61
|
-
invoke('custom_prepare', task, env.name, safe_invoke: true)
|
62
|
-
|
63
|
-
TDK::TerraformInstaller.install_local(
|
64
|
-
TDK::Configuration.get('terraform-version'),
|
65
|
-
directory: BIN_PATH
|
66
|
-
)
|
67
|
-
|
68
|
-
project_config = TDK::ProjectConfig.new(
|
69
|
-
TDK::Configuration.get('project-name'),
|
70
|
-
TDK::Configuration.get('project-acronym')
|
71
|
-
)
|
72
|
-
TDK::TerraformConfigManager.setup(env, project_config)
|
73
|
-
|
74
|
-
unless env.local_backend?
|
75
|
-
puts '== Initializing remote state'
|
76
|
-
remote_state.init(env, project_config)
|
77
|
-
end
|
78
|
-
|
79
|
-
if File.exist?(File.join(env.working_dir, '.terraform'))
|
80
|
-
get_cmd = 'terraform get'
|
81
|
-
get_cmd += ' -update=true' if TDK::TerraformConfigManager.update_modules?
|
82
|
-
TDK::Command.run(get_cmd, directory: env.working_dir)
|
83
|
-
else
|
84
|
-
init_cmd = 'terraform init'
|
85
|
-
init_cmd += ' -upgrade=false' unless TDK::TerraformConfigManager.update_modules?
|
86
|
-
TDK::Command.run(init_cmd, directory: env.working_dir)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
desc 'Shows the plan to create the infrastructure'
|
91
|
-
task :plan, [:env] => :prepare do |_, args|
|
92
|
-
env = TDK::Environment.new(args.env)
|
93
|
-
cmd = "terraform plan -out=#{PLAN_FILE}"
|
94
|
-
TDK::Command.run(cmd, directory: env.working_dir)
|
95
|
-
end
|
96
|
-
|
97
|
-
desc 'Creates the infrastructure'
|
98
|
-
task :apply, [:env] => :prepare do |task, args|
|
99
|
-
env = TDK::Environment.new(args.env)
|
100
|
-
|
101
|
-
invoke('plan', task, env.name)
|
102
|
-
|
103
|
-
unless env.local_backend? || allow_remote_apply?
|
104
|
-
puts Rainbow("Are you sure you want to apply the above plan?\n" \
|
105
|
-
"Only 'yes' will be accepted.").green
|
106
|
-
response = STDIN.gets.strip
|
107
|
-
unless response == 'yes'
|
108
|
-
raise "Apply cancelled because response was not 'yes'.\n" \
|
109
|
-
"Response was: #{response}"
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
invoke('pre_apply', task, env.name, safe_invoke: true)
|
114
|
-
|
115
|
-
destroy_if_fails(env, task) do
|
116
|
-
cmd = "terraform apply \"#{PLAN_FILE}\""
|
117
|
-
TDK::Command.run(cmd, directory: env.working_dir)
|
118
|
-
end
|
119
|
-
|
120
|
-
invoke('post_apply', task, env.name, safe_invoke: true)
|
121
|
-
end
|
122
|
-
|
123
|
-
def allow_remote_apply?
|
124
|
-
aws = TDK::Configuration.get('aws')
|
125
|
-
aws.key?('remote_apply') && aws['remote_apply']
|
126
|
-
end
|
127
|
-
|
128
|
-
desc 'Tests a local environment'
|
129
|
-
task :test, [:env] do |task, args|
|
130
|
-
env = TDK::Environment.new(args.env)
|
131
|
-
env.local_backend? || (raise 'Testing is only allowed for local environments')
|
132
|
-
|
133
|
-
invoke('apply', task, env.name)
|
134
|
-
|
135
|
-
destroy_if_fails(env, task) do
|
136
|
-
invoke('custom_test', task, env.name, safe_invoke: true)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
desc 'Creates the infrastructure and runs the tests'
|
141
|
-
task :preflight, [:prefix, :teardown] do |task, args|
|
142
|
-
args.with_defaults(teardown: 'true')
|
143
|
-
args.with_defaults(prefix: TDK::Environment.temp_name)
|
144
|
-
env = TDK::Environment.new(args.prefix)
|
145
|
-
|
146
|
-
invoke('test', task, env.name)
|
147
|
-
invoke('clean', task, env.name) if args.teardown == 'true'
|
148
|
-
end
|
149
|
-
|
150
|
-
desc 'Destroys the infrastructure'
|
151
|
-
task :destroy, [:env] => :prepare do |task, args|
|
152
|
-
env = TDK::Environment.new(args.env)
|
153
|
-
|
154
|
-
unless env.local_backend?
|
155
|
-
puts Rainbow("\n\n!!!! WARNING !!!!\n\n" \
|
156
|
-
"You are about to destroy #{env.name} and its remote state.\n" \
|
157
|
-
"Are you sure you want to proceed?\n" \
|
158
|
-
"Only 'yes' will be accepted.").red.bright
|
159
|
-
response = STDIN.gets.strip
|
160
|
-
|
161
|
-
unless response == 'yes'
|
162
|
-
raise "Destroy cancelled because response was not 'yes'.\n" \
|
163
|
-
"Response was: #{response}"
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
invoke('pre_destroy', task, env.name, safe_invoke: true)
|
168
|
-
|
169
|
-
TDK::Command.run('terraform destroy -force', directory: env.working_dir)
|
170
|
-
|
171
|
-
unless env.local_backend?
|
172
|
-
project_config = TDK::ProjectConfig.new(
|
173
|
-
TDK::Configuration.get('project-name'),
|
174
|
-
TDK::Configuration.get('project-acronym')
|
175
|
-
)
|
176
|
-
remote_state.destroy(env, project_config)
|
177
|
-
end
|
178
|
-
|
179
|
-
invoke('post_destroy', task, env.name, safe_invoke: true)
|
180
|
-
end
|
181
|
-
|
182
|
-
desc 'Cleans an environment (infrastructure is destroyed too)'
|
183
|
-
task :clean, [:env] => :destroy do |_, args|
|
184
|
-
env = TDK::Environment.new(args.env)
|
185
|
-
puts "Deleting environment #{env.name} in #{env.working_dir}"
|
186
|
-
TDK::ExtendedFileUtils.rm_rf(env.working_dir, secure: true)
|
187
|
-
end
|
1
|
+
require 'rainbow'
|
2
|
+
require 'TerraformDevKit'
|
3
|
+
|
4
|
+
TDK = TerraformDevKit
|
5
|
+
|
6
|
+
raise 'ROOT_PATH is not defined' if defined?(ROOT_PATH).nil?
|
7
|
+
BIN_PATH = File.join(ROOT_PATH, 'bin')
|
8
|
+
CONFIG_FILE ||= File.join(ROOT_PATH, 'config', 'config-%s.yml')
|
9
|
+
|
10
|
+
# Ensure terraform is in the PATH
|
11
|
+
ENV['PATH'] = TDK::OS.join_env_path(
|
12
|
+
TDK::OS.convert_to_local_path(BIN_PATH),
|
13
|
+
ENV['PATH']
|
14
|
+
)
|
15
|
+
|
16
|
+
PLAN_FILE = 'plan.tfplan'.freeze
|
17
|
+
|
18
|
+
def destroy_if_fails(env, task)
|
19
|
+
yield
|
20
|
+
rescue Exception => e
|
21
|
+
puts "ERROR: #{e.message}"
|
22
|
+
puts e.backtrace.join("\n")
|
23
|
+
invoke('destroy', task, env.name) if env.local_backend?
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
|
27
|
+
def invoke(task_name, task_context, env, safe_invoke: false)
|
28
|
+
task_in_context = task_in_current_namespace(task_name, task_context)
|
29
|
+
should_invoke = !safe_invoke || Rake::Task.task_defined?(task_in_context)
|
30
|
+
Rake::Task[task_in_context].invoke(env) if should_invoke
|
31
|
+
end
|
32
|
+
|
33
|
+
def task_in_current_namespace(task_name, current_task)
|
34
|
+
namespace = current_task.scope.path.to_s
|
35
|
+
namespace.empty? ? task_name : "#{namespace}:#{task_name}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def remote_state
|
39
|
+
aws_config = TDK::Aws::AwsConfig.new(TDK::Configuration.get('aws'))
|
40
|
+
dynamo_db = TDK::Aws::DynamoDB.new(
|
41
|
+
aws_config.credentials,
|
42
|
+
aws_config.region
|
43
|
+
)
|
44
|
+
s3 = TDK::Aws::S3.new(
|
45
|
+
aws_config.credentials,
|
46
|
+
aws_config.region
|
47
|
+
)
|
48
|
+
TDK::Aws::TerraformRemoteState.new(dynamo_db, s3)
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'Prepares the environment to create the infrastructure'
|
52
|
+
task :prepare, [:env] do |_, args|
|
53
|
+
env = TDK::Environment.new(args.env)
|
54
|
+
|
55
|
+
puts "== Configuring environment #{env.name}"
|
56
|
+
|
57
|
+
config_file = CONFIG_FILE % env.config
|
58
|
+
puts "== Loading configuration from #{config_file}"
|
59
|
+
TDK::Configuration.init(config_file)
|
60
|
+
|
61
|
+
invoke('custom_prepare', task, env.name, safe_invoke: true)
|
62
|
+
|
63
|
+
TDK::TerraformInstaller.install_local(
|
64
|
+
TDK::Configuration.get('terraform-version'),
|
65
|
+
directory: BIN_PATH
|
66
|
+
)
|
67
|
+
|
68
|
+
project_config = TDK::ProjectConfig.new(
|
69
|
+
TDK::Configuration.get('project-name'),
|
70
|
+
TDK::Configuration.get('project-acronym')
|
71
|
+
)
|
72
|
+
TDK::TerraformConfigManager.setup(env, project_config)
|
73
|
+
|
74
|
+
unless env.local_backend?
|
75
|
+
puts '== Initializing remote state'
|
76
|
+
remote_state.init(env, project_config)
|
77
|
+
end
|
78
|
+
|
79
|
+
if File.exist?(File.join(env.working_dir, '.terraform'))
|
80
|
+
get_cmd = 'terraform get'
|
81
|
+
get_cmd += ' -update=true' if TDK::TerraformConfigManager.update_modules?
|
82
|
+
TDK::Command.run(get_cmd, directory: env.working_dir)
|
83
|
+
else
|
84
|
+
init_cmd = 'terraform init'
|
85
|
+
init_cmd += ' -upgrade=false' unless TDK::TerraformConfigManager.update_modules?
|
86
|
+
TDK::Command.run(init_cmd, directory: env.working_dir)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
desc 'Shows the plan to create the infrastructure'
|
91
|
+
task :plan, [:env] => :prepare do |_, args|
|
92
|
+
env = TDK::Environment.new(args.env)
|
93
|
+
cmd = "terraform plan -out=#{PLAN_FILE}"
|
94
|
+
TDK::Command.run(cmd, directory: env.working_dir)
|
95
|
+
end
|
96
|
+
|
97
|
+
desc 'Creates the infrastructure'
|
98
|
+
task :apply, [:env] => :prepare do |task, args|
|
99
|
+
env = TDK::Environment.new(args.env)
|
100
|
+
|
101
|
+
invoke('plan', task, env.name)
|
102
|
+
|
103
|
+
unless env.local_backend? || allow_remote_apply?
|
104
|
+
puts Rainbow("Are you sure you want to apply the above plan?\n" \
|
105
|
+
"Only 'yes' will be accepted.").green
|
106
|
+
response = STDIN.gets.strip
|
107
|
+
unless response == 'yes'
|
108
|
+
raise "Apply cancelled because response was not 'yes'.\n" \
|
109
|
+
"Response was: #{response}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
invoke('pre_apply', task, env.name, safe_invoke: true)
|
114
|
+
|
115
|
+
destroy_if_fails(env, task) do
|
116
|
+
cmd = "terraform apply \"#{PLAN_FILE}\""
|
117
|
+
TDK::Command.run(cmd, directory: env.working_dir)
|
118
|
+
end
|
119
|
+
|
120
|
+
invoke('post_apply', task, env.name, safe_invoke: true)
|
121
|
+
end
|
122
|
+
|
123
|
+
def allow_remote_apply?
|
124
|
+
aws = TDK::Configuration.get('aws')
|
125
|
+
aws.key?('remote_apply') && aws['remote_apply']
|
126
|
+
end
|
127
|
+
|
128
|
+
desc 'Tests a local environment'
|
129
|
+
task :test, [:env] do |task, args|
|
130
|
+
env = TDK::Environment.new(args.env)
|
131
|
+
env.local_backend? || (raise 'Testing is only allowed for local environments')
|
132
|
+
|
133
|
+
invoke('apply', task, env.name)
|
134
|
+
|
135
|
+
destroy_if_fails(env, task) do
|
136
|
+
invoke('custom_test', task, env.name, safe_invoke: true)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
desc 'Creates the infrastructure and runs the tests'
|
141
|
+
task :preflight, [:prefix, :teardown] do |task, args|
|
142
|
+
args.with_defaults(teardown: 'true')
|
143
|
+
args.with_defaults(prefix: TDK::Environment.temp_name)
|
144
|
+
env = TDK::Environment.new(args.prefix)
|
145
|
+
|
146
|
+
invoke('test', task, env.name)
|
147
|
+
invoke('clean', task, env.name) if args.teardown == 'true'
|
148
|
+
end
|
149
|
+
|
150
|
+
desc 'Destroys the infrastructure'
|
151
|
+
task :destroy, [:env] => :prepare do |task, args|
|
152
|
+
env = TDK::Environment.new(args.env)
|
153
|
+
|
154
|
+
unless env.local_backend?
|
155
|
+
puts Rainbow("\n\n!!!! WARNING !!!!\n\n" \
|
156
|
+
"You are about to destroy #{env.name} and its remote state.\n" \
|
157
|
+
"Are you sure you want to proceed?\n" \
|
158
|
+
"Only 'yes' will be accepted.").red.bright
|
159
|
+
response = STDIN.gets.strip
|
160
|
+
|
161
|
+
unless response == 'yes'
|
162
|
+
raise "Destroy cancelled because response was not 'yes'.\n" \
|
163
|
+
"Response was: #{response}"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
invoke('pre_destroy', task, env.name, safe_invoke: true)
|
168
|
+
|
169
|
+
TDK::Command.run('terraform destroy -force', directory: env.working_dir)
|
170
|
+
|
171
|
+
unless env.local_backend?
|
172
|
+
project_config = TDK::ProjectConfig.new(
|
173
|
+
TDK::Configuration.get('project-name'),
|
174
|
+
TDK::Configuration.get('project-acronym')
|
175
|
+
)
|
176
|
+
remote_state.destroy(env, project_config)
|
177
|
+
end
|
178
|
+
|
179
|
+
invoke('post_destroy', task, env.name, safe_invoke: true)
|
180
|
+
end
|
181
|
+
|
182
|
+
desc 'Cleans an environment (infrastructure is destroyed too)'
|
183
|
+
task :clean, [:env] => :destroy do |_, args|
|
184
|
+
env = TDK::Environment.new(args.env)
|
185
|
+
puts "Deleting environment #{env.name} in #{env.working_dir}"
|
186
|
+
TDK::ExtendedFileUtils.rm_rf(env.working_dir, secure: true)
|
187
|
+
end
|