TerraformDevKit 0.3.8 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.8'.freeze
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
@@ -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