rake-terraform 0.0.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +19 -0
  3. data/.rspec +5 -0
  4. data/.rubocop.yml +25 -0
  5. data/.travis.yml +1 -0
  6. data/README.md +93 -1
  7. data/Rakefile +22 -3
  8. data/lib/{rake_terraform.rb → rake-terraform.rb} +1 -0
  9. data/lib/rake-terraform/apply_task/config.rb +47 -0
  10. data/lib/rake-terraform/{tasks/applytask.rb → apply_task/task.rb} +21 -33
  11. data/lib/rake-terraform/applytask.rb +11 -0
  12. data/lib/rake-terraform/{tasks/basetask.rb → basetask.rb} +2 -1
  13. data/lib/rake-terraform/default_tasks.rb +5 -2
  14. data/lib/rake-terraform/dsl.rb +2 -2
  15. data/lib/rake-terraform/env_process.rb +142 -0
  16. data/lib/rake-terraform/errors.rb +4 -0
  17. data/lib/rake-terraform/plan_task/config.rb +61 -0
  18. data/lib/rake-terraform/plan_task/task.rb +51 -0
  19. data/lib/rake-terraform/plantask.rb +13 -0
  20. data/lib/rake-terraform/terraformcmd.rb +56 -0
  21. data/lib/rake-terraform/version.rb +1 -1
  22. data/rake-terraform.gemspec +3 -0
  23. data/spec/fixtures/envprocess_uniq_state_dir_var_invalid.env +3 -0
  24. data/spec/fixtures/envprocess_uniq_state_dir_var_valid.env +4 -0
  25. data/spec/fixtures/envprocess_uniq_state_false.env +2 -0
  26. data/spec/fixtures/envprocess_uniq_state_false_file_valid.env +3 -0
  27. data/spec/fixtures/envprocess_uniq_state_invalid_state_file_str.env +3 -0
  28. data/spec/fixtures/envprocess_uniq_state_missing_dir_var.env +4 -0
  29. data/spec/fixtures/envprocess_uniq_state_true.env +5 -0
  30. data/spec/fixtures/envprocess_uniq_state_true_both.env +6 -0
  31. data/spec/fixtures/envprocess_uniq_state_true_file_valid.env +3 -0
  32. data/spec/fixtures/set_all_variables_nil.env +8 -0
  33. data/spec/spec_helper.rb +86 -0
  34. data/spec/unit/applytask_spec.rb +112 -0
  35. data/spec/unit/basetask_spec.rb +10 -0
  36. data/spec/unit/envprocess_spec.rb +263 -0
  37. data/spec/unit/plantask_spec.rb +118 -0
  38. data/spec/unit/terraformcmd_spec.rb +174 -0
  39. metadata +90 -7
  40. data/lib/rake-terraform/tasks/plantask.rb +0 -87
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- M2I1MmU3MTY0MTcyYTZkNjljMzYxZWJkY2M3YzVlOGMwODA0YTU5YQ==
4
+ NWE5MWZmZDg3OGZlNWE4YmNkNmVhY2U3NTFkZGFmODhiMGMzOWE1MA==
5
5
  data.tar.gz: !binary |-
6
- NzZlYjVmM2U4MjQ3OTg2Njk1NWJkMzA3MzlhOGU3OGJjMWM1OTQ0Zg==
6
+ NjA1MWMyZjY1YjljN2U0NjM3MTY3OTM2Y2I0YzlmNTQ4NzhhOTAyNA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MzcwMzQyOGVjMDM0ZjdhNWQ2OThjNzgwMDQ3MzVhYjY5NDQwZWQ0Mjg4ZGI3
10
- OTljNjgyM2ZmMTFiOWQxNjZjOWEwNDM3M2UwMzIzNDNmZmNmNmNjNzljMDk0
11
- NjRiZjRjMjJkYWYzNTVjOGMzYzk1NGQ4ODBiY2I4ODkwYjdkNjI=
9
+ YTFkNjY4MjY4MDkxNjNmZWNhYTQ1YTVmYWY4NWFhODkxYWY2YjJjZWE0YjM0
10
+ ZjEwMjE3M2YzMjc1NDU4ZjU0N2Y5YjQ3NDAzYTc2ODUwOWU0OGI4NGE2ZjE2
11
+ YmQzMWEwZWJkOWQwMmIxMTQyYmE4NmM5ODY0MDM3MWNiOWM2ZjE=
12
12
  data.tar.gz: !binary |-
13
- YTFkMDkxZTc1ZWU4ZDNjOTk1NTg4Mjc1ZjE0OGFjNTdjNDBhMDg3ZTliNmQ5
14
- OTc2NWFjM2Q5MDJmYzMxMzcyMDRhMjdlNGM2Y2ZkZjk0MGE0YjE4OTI4Y2E2
15
- OTVjZTBmMDYwYjgzODNmZjk5YjhiYzk0ZGUxZThjYzBiMWFjMjU=
13
+ NDJmZDc5YjE2YTQwMTVmMTU0NjJiYTQ4Y2ZjZGUxOWNlMWY2YTZjMWEwNDY5
14
+ YmYwZmU2ZjEzNjNmMmU5YzQyNmJiMzIwMzllYmY0N2M0MTE1MWI0MDY0NmZm
15
+ N2NkYWMxZDE5ZmQ1YzZmYmFlMTU0ZjYzNWJiOGQ1YWY5MjFmZjc=
data/.gitignore CHANGED
@@ -15,3 +15,22 @@ mkmf.log
15
15
  terraform/
16
16
  .idea/
17
17
  output/
18
+
19
+ # ignore bundlers vendor dir
20
+ /vendor/bundle/
21
+
22
+ # Default ignores for vim editor
23
+ .*.sw[a-z]
24
+ *.un~
25
+ Session.vim
26
+ .netrwhist
27
+ # Default ignores for emacs editor
28
+ *~
29
+ \#*\#
30
+ /.emacs.desktop
31
+ /.emacs.desktop.lock
32
+ .elc
33
+ auto-save-list
34
+ tramp
35
+ .\#*
36
+
data/.rspec ADDED
@@ -0,0 +1,5 @@
1
+ --color
2
+ --tty
3
+ --warnings
4
+ --require spec_helper
5
+ --format documentation
@@ -0,0 +1,25 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'spec/spec_helper.rb'
4
+ - 'vendor/**/*'
5
+ Style/FileName:
6
+ Exclude:
7
+ # usability - module name should match the gem name
8
+ - 'lib/rake-terraform.rb'
9
+ Metrics/AbcSize:
10
+ Exclude:
11
+ # TODO - fix this
12
+ - 'lib/rake-terraform/plan_task/task.rb'
13
+ Metrics/MethodLength:
14
+ Max: 12
15
+ Metrics/ModuleLength:
16
+ Exclude:
17
+ - 'spec/unit/*_spec.rb'
18
+ Metrics/CyclomaticComplexity:
19
+ Exclude:
20
+ # TODO: fix tf_plan method to support splat arguments and break
21
+ # down into one or more command constructor methods
22
+ - 'lib/rake-terraform/terraformcmd.rb'
23
+ Metrics/PerceivedComplexity:
24
+ Exclude:
25
+ - 'lib/rake-terraform/terraformcmd.rb'
@@ -1,5 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.2.3
3
4
  - 2.1.0
4
5
  - 2.0.0
5
6
  deploy:
data/README.md CHANGED
@@ -52,7 +52,7 @@ You can set the following configuration for the task:
52
52
  * `t.execution_path`: The path from which to execute the plan (default: `./terraform`)
53
53
  * This is useful if you are referencing cloud-config files using relative paths
54
54
 
55
- To use these tasks, you should `require 'rake_terraform'` at the top of your Rakefile.
55
+ To use these tasks, you should `require 'rake-terraform'` at the top of your Rakefile.
56
56
 
57
57
  ### Default Tasks
58
58
 
@@ -96,6 +96,98 @@ The following environment variables can be set to tweak `default_task`'s behavio
96
96
  * `ENV['TERRAFORM_ENVIRONMENT_GLOB']` - Dir glob used to discover terraform environments (default: `terraform/**/*.tf`)
97
97
  * `ENV['TERRAFORM_OUTPUT_BASE']` - Directory to which plan files are saved/read. The environment name is appended to this automatically (default: `output/terraform`)
98
98
  * `ENV['TERRAFORM_CREDENTIAL_FILE']` - The path to your AWS credentials file (default: `~/.aws/credentials`)
99
+ * `ENV['TERRAFORM_UNIQUE_STATE']` - Whether to use a unique state for this run. Requires `TERRAFORM_STATE_FILE` OR `TERRAFORM_STATE_DIR_VAR`. Can be any truthy or falsey looking string from [this list][wannabe_bool_string] (e.g `TRUE` or `FALSE`)
100
+ * `ENV['TERRAFORM_STATE_FILE']` - The full path to a state file to use for this run. Only used when `TERRAFORM_UNIQUE_STATE` is true, and cannot be used in conjunction with `TERRAFORM_STATE_DIR_VAR`.
101
+ * `ENV['TERRAFORM_STATE_DIR_VAR']` - The name of an environment variable that holds a variable that will be used to reference a directory in which to store state files in for this run. This directory will be a subdirectory within the terraform environment. Only used when `TERRAFORM_STATE_DIR` is true, and cannot be used in conjunction with `TERRAFORM_STATE_FILE`
102
+
103
+ [wannabe_bool_string]: https://github.com/prodis/wannabe_bool#string
104
+
105
+ #### Unique States
106
+
107
+ Bu default, `rake-terraform` stores state within a given environment directory.
108
+
109
+ Sometimes, you will have several infrastructure environments ("infrastructure
110
+ environment" in this block here taken to mean e.g "staging" or "production"
111
+ rather than the broader "terraform environment" used more generally in this
112
+ doc) that are relatively homogeneous in terms of resources, where all changes
113
+ are rolled out through those infrastructure environments in a cascading manner.
114
+ Through application of [variable interpolation][tf_doc_var_interpol] and other
115
+ methods, you can provide differing configuration for each of your resources to
116
+ match those infrastructure environments. The issue with this is that Terraform
117
+ does not support resource names as variables, so when you come to apply the
118
+ same resource layout with differing configuration to the next infrastructure
119
+ environment, Terraform will see those configuration changes as needing to be
120
+ applied to the existing deployed resources.
121
+
122
+ One solution to this problem is to keep each of your infrastructure
123
+ environments in separate directories ("terraform environments"), each with
124
+ their own state file. An issue with this solution is that it does not confirm
125
+ to DRY principles, and also depends on you manually diffing changes between
126
+ files etc, rather than relying on `git diff` or similar. Another solution
127
+ might be to use separate divergent git branches, and cherry-pick relevant
128
+ commits between them. Again, depends on clean commit hygiene and easy to mess
129
+ up manual steps.
130
+
131
+ By using a unique state file for each of your infrastructure environments,
132
+ whilst utilising a single terraform environment, you can avoid repeating
133
+ yourself and manage roll out changes to each of your infrastructure
134
+ environments better.
135
+
136
+ To enable unique state files, you need to set the environment variable
137
+ `TERRAFORM_UNIQUE_STATE` to a [truthy value][wannabe_bool_string], then you
138
+ need to EITHER set `TERRAFORM_STATE_FILE` to the full path of your chosen state
139
+ file, OR set `TERRAFORM_STATE_DIR_VAR` to the name of another environment
140
+ variable containing the name of your infrastructure environment.
141
+
142
+ [tf_doc_var_interpol]: https://www.terraform.io/docs/configuration/interpolation.html
143
+
144
+ ##### Examples
145
+
146
+ Use a specific state file
147
+
148
+ $ export TERRAFORM_UNIQUE_STATE="TRUE"
149
+ $ export TERRAFORM_STATE_FILE="/home/dave/.tf_states/staging/web_tier.tfstate"
150
+ $ bundle exec rake terraform:plan_web_tier
151
+ ...
152
+
153
+ Use a variable to lookup the state file directory
154
+
155
+ $ export TF_VAR_infra_env="staging"
156
+ $ export TERRAFORM_UNIQUE_STATE="TRUE"
157
+ $ export TERRAFORM_STATE_DIR_VAR="TF_VAR_infra_env"
158
+ $ bundle exec rake terraform:plan_web_tier
159
+ ...
160
+
161
+ This would result in a directory layout resembling the following:
162
+
163
+ terraform
164
+ web_tier
165
+ main.tf
166
+ variables.tf
167
+ output.tf
168
+ state
169
+ staging
170
+ terraform.tfstate
171
+ terraform.tfstate.backup
172
+ production
173
+ terraform.tfstate
174
+ terraform.tfstate.backup
175
+ app_tier
176
+ main.tf
177
+ variables.tf
178
+ output.tf
179
+ state
180
+ staging
181
+ terraform.tfstate
182
+ terraform.tfstate.backup
183
+ production
184
+ terraform.tfstate
185
+ terraform.tfstate.backup
186
+
187
+ ## Testing
188
+
189
+ There is currently a basic rspec-based test harness in place. The default task
190
+ runs unit tests and rubocop tests.
99
191
 
100
192
  ## Contributing
101
193
 
data/Rakefile CHANGED
@@ -1,7 +1,26 @@
1
1
  require 'bundler/gem_tasks'
2
- require 'rake-terraform/default_tasks'
3
2
  require 'rubocop/rake_task'
3
+ require 'rspec/core/rake_task'
4
+ require 'rake-terraform'
5
+ # include project rake tasks
6
+ require 'rake-terraform/default_tasks'
7
+
8
+ namespace :test do
9
+ # default unit tests
10
+ desc 'Run all unit tests in default spec suite'
11
+ RSpec::Core::RakeTask.new(:unit) do |task|
12
+ task.pattern = 'spec/(unit|rake-terraform)/*_spec.rb'
13
+ end
14
+ # add rubocop tasks to test namespace
15
+ RuboCop::RakeTask.new
16
+ # run all non-integration/default tests
17
+ desc 'Run all default test suites'
18
+ task default: [:unit, :rubocop]
19
+ end
4
20
 
5
- task default: :rubocop
21
+ # default task is to run all the default test suites
22
+ task default: ['test:default']
6
23
 
7
- RuboCop::RakeTask.new
24
+ # keep a rubocop alias in place for backwards compatibility
25
+ desc 'Alias for test:rubocop'
26
+ task rubocop: ['test:rubocop']
@@ -1,5 +1,6 @@
1
1
  require 'rake-terraform/version'
2
2
  require 'rake-terraform/dsl'
3
+ require 'rake-terraform/errors'
3
4
 
4
5
  # Top-level module
5
6
  module RakeTerraform
@@ -0,0 +1,47 @@
1
+ require 'rake-terraform/env_process'
2
+
3
+ module RakeTerraform
4
+ module ApplyTask
5
+ # Configuration data for terraform apply task
6
+ class Config
7
+ prepend RakeTerraform::EnvProcess
8
+
9
+ attr_writer :plan
10
+
11
+ def initialize
12
+ # initialize RakeTerraform::EnvProcess
13
+ super
14
+ end
15
+
16
+ def execution_path
17
+ @execution_path ||= File.expand_path 'terraform'
18
+ end
19
+
20
+ # setter method for execution_path triggers setters for tf_environment and
21
+ # state_file so that these are dynamically updated on change (but only if
22
+ # we are using directory state, and not explicit path to a state file)
23
+ def execution_path=(dir)
24
+ @tf_environment = dir
25
+ @state_file = tf_state_file if @state_dir
26
+ @execution_path = dir
27
+ end
28
+
29
+ def plan
30
+ @plan ||= File.expand_path(default_plan)
31
+ end
32
+
33
+ def opts
34
+ Map.new(plan: plan,
35
+ execution_path: execution_path,
36
+ unique_state: unique_state,
37
+ state_file: state_file)
38
+ end
39
+
40
+ private
41
+
42
+ def default_plan
43
+ File.join('output', 'terraform', 'plan.tf')
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,53 +1,41 @@
1
+ require 'highline/import'
2
+ require 'rake-terraform/basetask'
3
+ require 'rake-terraform/terraformcmd'
4
+
1
5
  module RakeTerraform
2
6
  module ApplyTask
3
- # Configuration data for terraform apply task
4
- class Config
5
- attr_accessor :plan
6
- attr_accessor :execution_path
7
- def initialize
8
- @plan = File.expand_path(default_plan)
9
- @execution_path = File.expand_path 'terraform'
10
- end
11
-
12
- def opts
13
- Map.new(plan: @plan,
14
- execution_path: @execution_path)
15
- end
16
-
17
- private
18
-
19
- def default_plan
20
- File.join('output', 'terraform', 'plan.tf')
21
- end
22
- end
23
-
24
7
  # Custom rake task to run `terraform apply`
25
8
  class Task < BaseTask
9
+ include RakeTerraform::TerraformCmd
10
+
26
11
  def initialize(opts)
27
12
  @opts = opts
28
13
  end
29
14
 
30
15
  def execute
31
- require 'highline/import'
32
-
33
16
  plan = @opts.get(:plan)
34
- validate_terraform_installed
35
- ensure_plan_exists plan
36
-
37
- system "terraform show --module-depth=2 #{plan}"
38
-
39
- say 'The above changes will be applied to your environment.'
40
- exit unless agree 'Are you sure you want to execute this plan? (y/n)'
41
-
17
+ pre_execute_checks(plan)
42
18
  Dir.chdir(@opts.get(:execution_path)) do
43
- system "terraform apply #{plan}"
19
+ if @opts[:unique_state]
20
+ tf_apply(plan, @opts[:state_file])
21
+ else
22
+ tf_apply(plan)
23
+ end
44
24
  end
45
25
  end
46
26
 
47
27
  private
48
28
 
29
+ # run pre execution checks
30
+ def pre_execute_checks(plan = @opts.get(:plan))
31
+ validate_terraform_installed
32
+ ensure_plan_exists plan
33
+ tf_show(plan)
34
+ say 'The above changes will be applied to your environment.'
35
+ exit unless agree 'Are you sure you want to execute this plan? (y/n)'
36
+ end
37
+
49
38
  def ensure_plan_exists(plan)
50
- require 'fileutils'
51
39
  fail "Plan #{plan} does not exist! Aborting!" unless File.exist? plan
52
40
  end
53
41
  end
@@ -0,0 +1,11 @@
1
+ require 'rake-terraform/apply_task/config'
2
+ require 'rake-terraform/apply_task/task'
3
+
4
+ module RakeTerraform
5
+ # == RakeTerraform::ApplyTask
6
+ #
7
+ # Helper classes for applying a terraform plan
8
+ #
9
+ module ApplyTask
10
+ end
11
+ end
@@ -1,4 +1,5 @@
1
1
  require 'rake/task'
2
+ require 'fileutils'
2
3
  require 'iniparse'
3
4
  require 'English'
4
5
 
@@ -7,7 +8,7 @@ module RakeTerraform
7
8
  class BaseTask < Rake::Task
8
9
  def validate_terraform_installed
9
10
  error = 'Please ensure you have terraform installed and on your path!'
10
- fail error unless terraform_installed?
11
+ fail TerraformNotInstalled, error unless terraform_installed?
11
12
  end
12
13
 
13
14
  def terraform_installed?
@@ -1,7 +1,11 @@
1
- require 'rake_terraform'
1
+ require 'rake-terraform'
2
2
  require 'pathname'
3
3
 
4
4
  namespace :terraform do
5
+ # TODO: refactor all environment variable processing into
6
+ # RakeTerraform::EnvProcess, include in this task and pass to the
7
+ # relevant Config classes, rather than including directly in the Config
8
+ # classes
5
9
  env_glob = ENV['TERRAFORM_ENVIRONMENT_GLOB'] || 'terraform/**/*.tf'
6
10
  output_base = ENV['TERRAFORM_OUTPUT_BASE'] || 'output/terraform'
7
11
  credential_file = ENV['TERRAFORM_CREDENTIAL_FILE']
@@ -25,7 +29,6 @@ namespace :terraform do
25
29
 
26
30
  short_name = abs_relative_path.to_s.tr('/', '_')
27
31
  plan_path = File.expand_path File.join(output_base, "#{short_name}.tf")
28
-
29
32
  desc "Plan migration of #{short_name}" if hide_tasks == 'false'
30
33
  terraform_plan "plan_#{short_name}" do |t|
31
34
  t.input_dir = env
@@ -7,7 +7,7 @@ module RakeTerraform
7
7
  # Definitions of methods for custom rake tasks
8
8
  module DSL
9
9
  def terraform_plan(*args)
10
- require 'rake-terraform/tasks/plantask'
10
+ require 'rake-terraform/plantask'
11
11
  Rake::Task.define_task(*args) do
12
12
  c = RakeTerraform::PlanTask::Config.new
13
13
  yield c
@@ -16,7 +16,7 @@ module RakeTerraform
16
16
  end
17
17
 
18
18
  def terraform_apply(*args)
19
- require 'rake-terraform/tasks/applytask'
19
+ require 'rake-terraform/applytask'
20
20
  Rake::Task.define_task(*args) do
21
21
  c = RakeTerraform::ApplyTask::Config.new
22
22
  yield c
@@ -0,0 +1,142 @@
1
+ require 'wannabe_bool'
2
+
3
+ module RakeTerraform
4
+ # == RakeTerraform::EnvProcess
5
+ #
6
+ # Mixin for processing environment variables
7
+ #
8
+ # TODO: refactor all non accessor methods as private methods
9
+ #
10
+ module EnvProcess
11
+ attr_reader :unique_state, :state_file, :state_dir_var, :state_dir,
12
+ :tf_environment
13
+
14
+ def initialize
15
+ @unique_state, @state_file, @state_dir_var, @state_dir = nil
16
+ @tf_environment = nil
17
+ tf_unique_state_valid? && @unique_state = tf_unique_state
18
+ tf_state_dir_var_valid? && @state_dir_var = tf_state_dir_var
19
+ # tf_state_file represents the full path to the calculated file within
20
+ # tf_state_dir if given
21
+ if tf_state_dir_valid?
22
+ @state_dir = tf_state_dir
23
+ @state_file = tf_state_file
24
+ end
25
+ tf_state_file_valid? && @state_file = tf_state_file
26
+ end
27
+
28
+ # whether or not unique states are enabled and required args are also given
29
+ def tf_unique_state
30
+ state_var = ENV['TERRAFORM_UNIQUE_STATE'].to_b
31
+ return false if state_var == false
32
+ unless tf_unique_state_valid?
33
+ fail(
34
+ ArgumentError,
35
+ 'Both or neither of TERRAFORM_STATE_FILE or TERRAFORM_STATE_DIR_VAR' \
36
+ ' given, or missing target for TERRAFORM_STATE_DIR_VAR'
37
+ )
38
+ end
39
+ ENV['TERRAFORM_UNIQUE_STATE'].to_b
40
+ end
41
+
42
+ # if we are using tf_state_dir_var and that is valid, then return the full
43
+ # path to the calculated state file. Otherwise return the value of a valid
44
+ # TERRAFORM_STATE_FILE variable
45
+ def tf_state_file
46
+ return state_dir_full_path if tf_state_dir_valid?
47
+ return nil if ENV['TERRAFORM_STATE_FILE'].nil?
48
+ unless tf_state_file_valid?
49
+ fail(
50
+ ArgumentError,
51
+ 'Argument for TERRAFORM_STATE_FILE is invalid'
52
+ )
53
+ end
54
+ ENV['TERRAFORM_STATE_FILE']
55
+ end
56
+
57
+ # return the value if tf_state_dir_var
58
+ # see also: tf_state_dir
59
+ def tf_state_dir_var
60
+ return nil if ENV['TERRAFORM_STATE_DIR_VAR'].nil?
61
+ unless tf_state_dir_var_valid?
62
+ fail(
63
+ ArgumentError,
64
+ 'Argument for TERRAFORM_STATE_DIR_VAR is invalid'
65
+ )
66
+ end
67
+ ENV['TERRAFORM_STATE_DIR_VAR']
68
+ end
69
+
70
+ # return the target of tf_state_dir_var
71
+ # see also: tf_state_dir_var
72
+ def tf_state_dir
73
+ return nil if ENV['TERRAFORM_STATE_DIR_VAR'].nil?
74
+ unless tf_state_dir_valid?
75
+ fail(
76
+ ArgumentError,
77
+ 'Argument for TERRAFORM_STATE_DIR_VAR is invalid'
78
+ )
79
+ end
80
+ dir_var = ENV['TERRAFORM_STATE_DIR_VAR']
81
+ "#{tf_env_string}state/#{ENV[dir_var]}"
82
+ end
83
+
84
+ # calculate the full path to a state file within tf_state_dir
85
+ def state_dir_full_path(dir = tf_state_dir)
86
+ File.expand_path(
87
+ File.join(dir, default_state_file_name)
88
+ )
89
+ end
90
+
91
+ # if @tf_environment is set then return that, postfixed by a '/' -
92
+ # otherwise return 'terraform/'
93
+ def tf_env_string
94
+ if @tf_environment
95
+ "#{@tf_environment}/"
96
+ else
97
+ 'terraform/'
98
+ end
99
+ end
100
+
101
+ # validate tf_unique_state
102
+ def tf_unique_state_valid?
103
+ state_var = ENV['TERRAFORM_UNIQUE_STATE'].to_b
104
+ return true if state_var == false
105
+ if tf_state_file_valid? && tf_state_dir_var_valid?
106
+ return false
107
+ else
108
+ tf_state_file_valid? || tf_state_dir_var_valid?
109
+ end
110
+ end
111
+
112
+ # validate tf_state_file_valid?
113
+ # returns false if no environment set, or the file does not have a single
114
+ # a-z0-9 char
115
+ # TODO: improve regex?
116
+ def tf_state_file_valid?
117
+ return false if ENV['TERRAFORM_STATE_FILE'].nil?
118
+ ENV['TERRAFORM_STATE_FILE'] =~ /[a-z0-9]/i
119
+ end
120
+
121
+ # validate tf_state_dir_var (and corresponding target) is valid
122
+ # returns false if
123
+ # * TERRAFORM_STATE_DIR_VAR is nil
124
+ # * We cannot find the variable referenced by TERRAFORM_STATE_DIR_VAR
125
+ # * The variable referenced contains something other than a-z0-9_ chars
126
+ def tf_state_dir_var_valid?
127
+ return false if ENV['TERRAFORM_STATE_DIR_VAR'].nil?
128
+ dir_var = ENV['TERRAFORM_STATE_DIR_VAR']
129
+ return false unless dir_var =~ /^[a-z0-9_]+$/i
130
+ value = ENV[dir_var]
131
+ return false if value.nil?
132
+ value =~ /^[a-z0-9_]+$/i
133
+ end
134
+
135
+ alias_method :tf_state_dir_valid?, :tf_state_dir_var_valid?
136
+
137
+ # name of the default state file
138
+ def default_state_file_name
139
+ 'terraform.tfstate'
140
+ end
141
+ end
142
+ end