terraform-wrapper 0.0.2 → 0.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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/terraform-wrapper.rb +34 -16
  3. data/lib/terraform-wrapper/common.rb +12 -23
  4. data/lib/terraform-wrapper/shared.rb +7 -1
  5. data/lib/terraform-wrapper/shared/auths.rb +9 -0
  6. data/lib/terraform-wrapper/shared/auths/azure.rb +179 -0
  7. data/lib/terraform-wrapper/shared/auths/common.rb +95 -0
  8. data/lib/terraform-wrapper/shared/backends/aws.rb +34 -29
  9. data/lib/terraform-wrapper/shared/backends/azure.rb +38 -39
  10. data/lib/terraform-wrapper/shared/backends/common.rb +18 -14
  11. data/lib/terraform-wrapper/shared/backends/local.rb +20 -18
  12. data/lib/terraform-wrapper/shared/binary.rb +16 -5
  13. data/lib/terraform-wrapper/shared/code.rb +15 -4
  14. data/lib/terraform-wrapper/shared/config.rb +61 -31
  15. data/lib/terraform-wrapper/shared/latest.rb +11 -7
  16. data/lib/terraform-wrapper/shared/logger.rb +80 -0
  17. data/lib/terraform-wrapper/shared/logging.rb +77 -0
  18. data/lib/terraform-wrapper/shared/runner.rb +59 -22
  19. data/lib/terraform-wrapper/shared/variables.rb +66 -0
  20. data/lib/terraform-wrapper/tasks/apply.rb +16 -14
  21. data/lib/terraform-wrapper/tasks/binary.rb +25 -21
  22. data/lib/terraform-wrapper/tasks/clean.rb +15 -11
  23. data/lib/terraform-wrapper/tasks/destroy.rb +16 -14
  24. data/lib/terraform-wrapper/tasks/init.rb +16 -14
  25. data/lib/terraform-wrapper/tasks/plan.rb +16 -14
  26. data/lib/terraform-wrapper/tasks/plandestroy.rb +16 -14
  27. data/lib/terraform-wrapper/tasks/validate.rb +7 -3
  28. data/lib/terraform-wrapper/version.rb +1 -1
  29. metadata +8 -3
  30. data/lib/terraform-wrapper/shared/identifiers.rb +0 -70
@@ -21,6 +21,10 @@ module TerraformWrapper
21
21
 
22
22
  include Singleton
23
23
 
24
+ ###############################################################################
25
+
26
+ include TerraformWrapper::Shared::Logging
27
+
24
28
  ###############################################################################
25
29
 
26
30
  @version
@@ -40,22 +44,22 @@ module TerraformWrapper
40
44
  ###############################################################################
41
45
 
42
46
  def refresh
43
- $logger.info("Finding latest available Terraform release...")
47
+ logger.info("Finding latest available Terraform release...")
44
48
 
45
49
  response = Net::HTTP.get_response(URI("https://checkpoint-api.hashicorp.com/v1/check/terraform"))
46
50
 
47
- raise "Hashicorp Checkpoint did not return status 200 for latest version check!" if response.code != "200"
48
- raise "Response body from Hashicorp Checkpoint is not permitted!" if not response.class.body_permitted?
49
- raise "Response body from Hashicorp Checkpoint is empty!" if response.body.nil?
51
+ logger.fatal("Hashicorp Checkpoint did not return status 200 for latest version check!") if response.code != "200"
52
+ logger.fatal("Response body from Hashicorp Checkpoint is not permitted!") if not response.class.body_permitted?
53
+ logger.fatal("Response body from Hashicorp Checkpoint is empty!") if response.body.nil?
50
54
 
51
55
  body = JSON.parse(response.body)
52
56
 
53
- raise "Hashicorp Checkpoint JSON response did not include latest available Terraform version!" if not body.key?("current_version")
54
- raise "Hashicorp Checkpoint indicated latest available version of Terraform is blank!" if body["current_version"].empty?
57
+ logger.fatal("Hashicorp Checkpoint JSON response did not include latest available Terraform version!") if not body.key?("current_version")
58
+ logger.fatal("Hashicorp Checkpoint indicated latest available version of Terraform is blank!") if body["current_version"].empty?
55
59
 
56
60
  version = body["current_version"]
57
61
 
58
- $logger.info("Latest available Terraform release found: #{version}")
62
+ logger.success("Latest available Terraform release found: #{version}")
59
63
 
60
64
  return version
61
65
  end
@@ -0,0 +1,80 @@
1
+ ###############################################################################
2
+
3
+ require 'logger'
4
+
5
+ ###############################################################################
6
+
7
+ module TerraformWrapper
8
+
9
+ ###############################################################################
10
+
11
+ module Shared
12
+
13
+ ###############################################################################
14
+
15
+ class Logger < ::Logger
16
+
17
+ ###############################################################################
18
+
19
+ @colour = false
20
+
21
+ ###############################################################################
22
+
23
+ def colour()
24
+ @colour
25
+ end
26
+
27
+ ###############################################################################
28
+
29
+ def colour=(enabled)
30
+ @colour = [ true, false ].include?(enabled) ? enabled : false
31
+ end
32
+
33
+ ###############################################################################
34
+
35
+ def success(message)
36
+ info(format(colour: 32, message: message))
37
+ end
38
+
39
+ ###############################################################################
40
+
41
+ def warn(message)
42
+ warn(format(colour: 33, message: message))
43
+ end
44
+
45
+ ###############################################################################
46
+
47
+ def error(message)
48
+ error(format(colour: 31, message: message))
49
+ end
50
+
51
+ ###############################################################################
52
+
53
+ def fatal(message)
54
+ fatal(format(colour: 31, message: message))
55
+ exit(1)
56
+ end
57
+
58
+ ###############################################################################
59
+
60
+ private
61
+
62
+ ###############################################################################
63
+
64
+ def format(colour: 32, message:)
65
+ return @colour ? "\e[" + colour.to_s + "m" + message + "\e[0m" : message
66
+ end
67
+
68
+ ###############################################################################
69
+
70
+ end
71
+
72
+ ###############################################################################
73
+
74
+ end
75
+
76
+ ###############################################################################
77
+
78
+ end
79
+
80
+ ###############################################################################
@@ -0,0 +1,77 @@
1
+ ###############################################################################
2
+
3
+ require 'logger'
4
+
5
+ ###############################################################################
6
+
7
+ module TerraformWrapper
8
+
9
+ ###############################################################################
10
+
11
+ module Shared
12
+
13
+ ###############################################################################
14
+
15
+ module Logging
16
+
17
+ ###############################################################################
18
+
19
+ private
20
+
21
+ ###############################################################################
22
+
23
+ def logger
24
+ @logger ||= Logging.logger_for(self.class.name)
25
+ end
26
+
27
+ ###############################################################################
28
+
29
+ @loggers = {}
30
+
31
+ ###############################################################################
32
+
33
+ class << self
34
+
35
+ ###############################################################################
36
+
37
+ def logger_for(classname)
38
+ @loggers[classname] ||= configure_logger_for(classname)
39
+ end
40
+
41
+ ###############################################################################
42
+
43
+ def configure_logger_for(classname)
44
+ colour = ENV["TERRAFORM_WRAPPER_LOG_COLOUR"] || "true"
45
+ level = ENV["TERRAFORM_WRAPPER_LOG_LEVEL"] || "INFO"
46
+
47
+ logger = ::TerraformWrapper::Shared::Logger.new(STDOUT)
48
+
49
+ logger.colour = colour.downcase == "true"
50
+ logger.level = level.upcase
51
+ logger.progname = classname
52
+
53
+ logger.formatter = proc do |severity, datetime, progname, msg|
54
+ sevId = severity.chars.first.upcase
55
+ "[#{sevId}] [#{progname}] #{msg}\n"
56
+ end
57
+
58
+ logger
59
+ end
60
+
61
+ ###############################################################################
62
+
63
+ end
64
+
65
+ ###############################################################################
66
+
67
+ end
68
+
69
+ ###############################################################################
70
+
71
+ end
72
+
73
+ ###############################################################################
74
+
75
+ end
76
+
77
+ ###############################################################################
@@ -10,6 +10,10 @@ module TerraformWrapper
10
10
 
11
11
  class Runner
12
12
 
13
+ ###############################################################################
14
+
15
+ include TerraformWrapper::Shared::Logging
16
+
13
17
  ###############################################################################
14
18
 
15
19
  attr_reader :binary
@@ -30,75 +34,90 @@ module TerraformWrapper
30
34
 
31
35
  ###############################################################################
32
36
 
33
- def download
34
- parameters = [ "-backend=false" ]
37
+ def download(upgrade: true)
38
+ parameters = Array.new
39
+ parameters.append("-backend=false")
40
+ parameters.append("-upgrade") if upgrade
41
+
35
42
  @downloaded = run(action: "init", parameters: parameters)
36
- raise("Failed to download Terraform modules.") unless @downloaded
43
+ logger.fatal("Failed to download Terraform modules.") unless @downloaded
37
44
  end
38
45
 
39
46
  ###############################################################################
40
47
 
41
- def init(config:)
42
- parameters = [ "-reconfigure" ]
48
+ def init(config:, upgrade: true)
49
+ parameters = Array.new
50
+ parameters.append("-reconfigure")
51
+ parameters.append("-upgrade") if upgrade
52
+
43
53
  config.backend.hash.each do |key, value|
44
54
  parameters.append("-backend-config=\"#{key}=#{value}\"")
45
55
  end
46
56
 
57
+ config.auths.map(&:auth)
58
+
47
59
  @config = config
48
60
  @initialised = run(action: "init", parameters: parameters)
49
- raise("Failed to initialise Terraform with backend.") unless @initialised
61
+ logger.fatal("Failed to initialise Terraform with backend.") unless @initialised
50
62
  end
51
63
 
52
64
  ###############################################################################
53
65
 
54
66
  def plan(destroy: false, file: nil)
55
- raise("Cannot Terraform plan before initialising backend!") unless initialised
67
+ logger.fatal("Cannot Terraform plan before initialising backend!") unless initialised
56
68
 
57
- parameters = variable_files
69
+ parameters = Array.new
70
+ parameters.concat(variable_files)
71
+ parameters.concat(variable_strings)
58
72
 
59
73
  if not file.nil? and file.kind_of?(String) and not file.strip.empty? then
60
- raise "Failed to create plan directory: #{directory}" unless create_directory(directory: File.dirname(file), purpose: "plan")
74
+ logger.fatal("Failed to create plan directory: #{directory}") unless ::TerraformWrapper.create_directory(directory: File.dirname(file), purpose: "plan")
61
75
  parameters.append("-out=\"#{file}\"")
62
76
  end
63
77
 
64
78
  parameters.append("-destroy") if destroy
65
79
 
66
- raise("Terraform plan failed!") unless run(action: "plan", parameters: parameters)
80
+ logger.fatal("Terraform plan failed!") unless run(action: "plan", parameters: parameters)
67
81
  end
68
82
 
69
83
  ###############################################################################
70
84
 
71
85
  def apply(file: nil)
72
- raise("Cannot Terraform apply before initialising backend!") unless initialised
86
+ logger.fatal("Cannot Terraform apply before initialising backend!") unless initialised
73
87
 
74
- parameters = [ "-auto-approve" ]
88
+ parameters = Array.new
89
+ parameters.concat(variable_files)
90
+ parameters.concat(variable_strings)
91
+ parameters.append("-auto-approve")
75
92
 
76
93
  if not file.nil? and file.kind_of?(String) and not file.strip.empty? then
77
- raise "Plan file: #{file} does not exist!" unless File.file?(file)
94
+ logger.fatal("Plan file: #{file} does not exist!") unless File.file?(file)
78
95
  parameters.append("\"#{file}\"")
79
96
  else
80
97
  parameters.concat(variable_files)
81
98
  end
82
99
 
83
- raise("Terraform apply failed!") unless run(action: "apply", parameters: parameters)
100
+ logger.fatal("Terraform apply failed!") unless run(action: "apply", parameters: parameters)
84
101
  end
85
102
 
86
103
  ###############################################################################
87
104
 
88
105
  def destroy
89
- raise("Cannot Terraform destroy before initialising backend!") unless initialised
106
+ logger.fatal("Cannot Terraform destroy before initialising backend!") unless initialised
90
107
 
91
- parameters = [ "-auto-approve" ]
108
+ parameters = Array.new
92
109
  parameters.concat(variable_files)
110
+ parameters.concat(variable_strings)
111
+ parameters.append("-auto-approve")
93
112
 
94
- raise("Terraform destroy failed!") unless run(action: "destroy", parameters: parameters)
113
+ logger.fatal("Terraform destroy failed!") unless run(action: "destroy", parameters: parameters)
95
114
  end
96
115
 
97
116
  ###############################################################################
98
117
 
99
118
  def validate
100
- raise("Cannot Terraform validate before downloading modules!") unless downloaded
101
- raise("Terraform validation failed!") unless run(action: "validate")
119
+ logger.fatal("Cannot Terraform validate before downloading modules!") unless downloaded
120
+ logger.fatal("Terraform validation failed!") unless run(action: "validate")
102
121
  end
103
122
 
104
123
  ###############################################################################
@@ -108,7 +127,7 @@ module TerraformWrapper
108
127
  ###############################################################################
109
128
 
110
129
  def variable_files
111
- raise("Cannot generate variable files until Terraform has been initialised!") unless @initialised
130
+ logger.fatal("Cannot generate variable files until Terraform has been initialised!") unless @initialised
112
131
 
113
132
  result = Array.new
114
133
 
@@ -119,6 +138,24 @@ module TerraformWrapper
119
138
  return result
120
139
  end
121
140
 
141
+ ###############################################################################
142
+
143
+ def variable_strings
144
+ logger.fatal("Cannot generate variable strings until Terraform has been initialised!") unless @initialised
145
+
146
+ result = Array.new
147
+
148
+ result.append("-var=\"component=#{@code.name}\"")
149
+ result.append("-var=\"config=#{@config.name}\"")
150
+ result.append("-var=\"service=#{@config.service}\"")
151
+
152
+ @config.variables.values.each do |key, value|
153
+ result.append("-var=\"#{key.to_s}=#{value}\"")
154
+ end
155
+
156
+ return result
157
+ end
158
+
122
159
  ###############################################################################
123
160
 
124
161
  def run(action:, parameters: Array.new)
@@ -128,14 +165,14 @@ module TerraformWrapper
128
165
 
129
166
  cmdline = [ "\"#{@binary.path}\"", action ].concat(parameters).join(" ")
130
167
 
131
- $logger.info("Starting Terraform, action: #{action}")
168
+ logger.info("Starting Terraform, action: #{action}")
132
169
 
133
170
  puts("\n" + ('#' * 80) + "\n\n")
134
171
 
135
172
  Dir.chdir(@code.path)
136
173
  result = system(cmdline) || false
137
174
 
138
- puts("\n")
175
+ puts("\n" + ('#' * 80) + "\n\n")
139
176
 
140
177
  return result
141
178
  end
@@ -0,0 +1,66 @@
1
+ ###############################################################################
2
+
3
+ module TerraformWrapper
4
+
5
+ ###############################################################################
6
+
7
+ module Shared
8
+
9
+ ###############################################################################
10
+
11
+ class Variables
12
+
13
+ ###############################################################################
14
+
15
+ include TerraformWrapper::Shared::Logging
16
+
17
+ ###############################################################################
18
+
19
+ @@reserved = [ "component", "config", "service" ]
20
+
21
+ ###############################################################################
22
+
23
+ attr_reader :values
24
+
25
+ ###############################################################################
26
+
27
+ def initialize(values: Hash.new, sort: true)
28
+ cleansed = cleanse(values: values)
29
+ @values = sort ? cleansed.sort.to_h : cleansed
30
+ end
31
+
32
+ ###############################################################################
33
+
34
+ private
35
+
36
+ ###############################################################################
37
+
38
+ def cleanse(values:)
39
+ result = Hash.new
40
+
41
+ values.keys.each do |key|
42
+ logger.fatal("Could not clean variables hash. All keys MUST be strings!") unless key.kind_of?(String)
43
+ logger.fatal("Could not clean variables hash, key: #{key.downcase} is reserved and cannot be used!") if @@reserved.include?(key.downcase)
44
+ logger.fatal("Could not clean variables hash, duplicate key found: #{key.downcase}!") if result.key?(key.downcase.to_sym)
45
+ logger.fatal("Could not clean variables hash, value for: #{key.downcase} is not a string!") unless values[key].kind_of?(String)
46
+ logger.fatal("Could not clean variables hash, value for: #{key.downcase} is empty!") if values[key].strip.empty?
47
+
48
+ result[key.downcase.to_sym] = values[key].strip
49
+ end
50
+
51
+ return result
52
+ end
53
+
54
+ ###############################################################################
55
+
56
+ end
57
+
58
+ ###############################################################################
59
+
60
+ end
61
+
62
+ ###############################################################################
63
+
64
+ end
65
+
66
+ ###############################################################################
@@ -16,22 +16,20 @@ module TerraformWrapper
16
16
 
17
17
  ###############################################################################
18
18
 
19
- @backend
19
+ include TerraformWrapper::Shared::Logging
20
+
21
+ ###############################################################################
22
+
20
23
  @binary
21
24
  @code
22
- @configs
23
- @overrides
24
- @service
25
+ @options
25
26
 
26
27
  ###############################################################################
27
28
 
28
- def initialize(backend:, binary:, code:, configs:, overrides:, service:)
29
- @backend = backend
30
- @binary = binary
31
- @code = code
32
- @configs = configs
33
- @overrides = overrides
34
- @service = service
29
+ def initialize(binary:, code:, options:)
30
+ @binary = binary
31
+ @code = code
32
+ @options = options
35
33
 
36
34
  yield self if block_given?
37
35
 
@@ -41,13 +39,17 @@ module TerraformWrapper
41
39
  ###############################################################################
42
40
 
43
41
  def apply_task
44
- desc "Applies infrastructure with Terraform for a given configuration on a workspace."
42
+ desc "Applies infrastructure with Terraform for a given configuration on an infrastructure component."
45
43
  task :apply, [:config, :plan] => :binary do |t, args|
46
- $logger.info("Running Terraform apply for service: #{@service}, component: #{@code.name}...")
44
+ options = @options.merge({"name" => args[:config]})
47
45
 
48
- config = TerraformWrapper::Shared::Config.new(backend: @backend, base: @configs, code: @code, name: args[:config], overrides: @overrides, service: @service)
46
+ logger.info("Processing configuration for Terraform apply...")
47
+
48
+ config = TerraformWrapper::Shared::Config.new(code: @code, options: options)
49
49
  runner = TerraformWrapper::Shared::Runner.new(binary: @binary, code: @code)
50
50
 
51
+ logger.info("Running Terraform apply for service: #{config.service}, component: #{@code.name}...")
52
+
51
53
  runner.init(config: config)
52
54
  runner.apply(file: args[:plan])
53
55
  end