yle_tf 0.4.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/bin/tf +2 -1
  3. data/lib/yle_tf.rb +4 -0
  4. data/lib/yle_tf/action.rb +4 -0
  5. data/lib/yle_tf/action/builder.rb +2 -0
  6. data/lib/yle_tf/action/command.rb +4 -2
  7. data/lib/yle_tf/action/copy_root_module.rb +2 -0
  8. data/lib/yle_tf/action/generate_vars_file.rb +2 -0
  9. data/lib/yle_tf/action/load_config.rb +9 -1
  10. data/lib/yle_tf/action/terraform_init.rb +31 -33
  11. data/lib/yle_tf/action/tf_hooks.rb +9 -5
  12. data/lib/yle_tf/action/tmpdir.rb +3 -1
  13. data/lib/yle_tf/action/verify_terraform_version.rb +19 -6
  14. data/lib/yle_tf/action/verify_tf_env.rb +2 -0
  15. data/lib/yle_tf/action/verify_yle_tf_version.rb +36 -0
  16. data/lib/yle_tf/action/write_terraformrc_defaults.rb +4 -2
  17. data/lib/yle_tf/backend.rb +42 -0
  18. data/lib/yle_tf/cli.rb +3 -1
  19. data/lib/yle_tf/config.rb +25 -9
  20. data/lib/yle_tf/config/defaults.rb +22 -11
  21. data/lib/yle_tf/config/erb.rb +2 -0
  22. data/lib/yle_tf/config/file.rb +2 -0
  23. data/lib/yle_tf/config/loader.rb +89 -59
  24. data/lib/yle_tf/config/migration.rb +117 -0
  25. data/lib/yle_tf/error.rb +2 -0
  26. data/lib/yle_tf/helpers/hash.rb +22 -0
  27. data/lib/yle_tf/logger.rb +2 -10
  28. data/lib/yle_tf/logger/colorize.rb +2 -0
  29. data/lib/yle_tf/plugin.rb +6 -2
  30. data/lib/yle_tf/plugin/action_hook.rb +2 -0
  31. data/lib/yle_tf/plugin/loader.rb +9 -2
  32. data/lib/yle_tf/plugin/manager.rb +3 -0
  33. data/lib/yle_tf/system.rb +5 -2
  34. data/lib/yle_tf/system/io_handlers.rb +5 -1
  35. data/lib/yle_tf/system/output_logger.rb +2 -0
  36. data/lib/yle_tf/system/tf_hook_output_logger.rb +2 -0
  37. data/lib/yle_tf/tf_hook.rb +11 -9
  38. data/lib/yle_tf/tf_hook/runner.rb +2 -0
  39. data/lib/yle_tf/vars_file.rb +16 -3
  40. data/lib/yle_tf/version.rb +3 -1
  41. data/lib/yle_tf/version_requirement.rb +2 -5
  42. data/lib/yle_tf_plugins/backends/{s3 → __default}/plugin.rb +6 -4
  43. data/lib/yle_tf_plugins/backends/file/{command.rb → backend.rb} +12 -13
  44. data/lib/yle_tf_plugins/backends/file/plugin.rb +4 -2
  45. data/lib/yle_tf_plugins/commands/__default/command.rb +2 -0
  46. data/lib/yle_tf_plugins/commands/__default/plugin.rb +2 -0
  47. data/lib/yle_tf_plugins/commands/_config/command.rb +2 -0
  48. data/lib/yle_tf_plugins/commands/_config/plugin.rb +2 -0
  49. data/lib/yle_tf_plugins/commands/_shell/command.rb +2 -0
  50. data/lib/yle_tf_plugins/commands/_shell/plugin.rb +2 -0
  51. data/lib/yle_tf_plugins/commands/help/command.rb +2 -0
  52. data/lib/yle_tf_plugins/commands/help/plugin.rb +2 -0
  53. data/lib/yle_tf_plugins/commands/version/command.rb +2 -0
  54. data/lib/yle_tf_plugins/commands/version/plugin.rb +2 -0
  55. metadata +61 -20
  56. data/lib/yle_tf/backend_config.rb +0 -41
  57. data/lib/yle_tf_plugins/backends/file/config.rb +0 -17
  58. data/lib/yle_tf_plugins/backends/s3/command.rb +0 -19
  59. data/vendor/logger_level_patch.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fef8984348903ca788037c6d3562009f2499d513375aca7b8a6e93d14e9cf7cb
4
- data.tar.gz: adf1e1f5a27f03c420cde1d49496956a224a88a5fa434cbd0eb4dcf90fa00810
3
+ metadata.gz: 7811f980206094727502839932ba9a800a4e37694ae26bc71d9c4935de38d3c3
4
+ data.tar.gz: 95e2c481a30b3caffb65c08407f9e222210fe01514f59ff88ca38a00915ea31b
5
5
  SHA512:
6
- metadata.gz: 834703a380e72cd8ccd7c8c3dd4f5013042f6fa39f230da61fa3859a860d02102415a48a1cd7065021439b5b506a13c6a184f39241aef7f4e719b2af9a42164e
7
- data.tar.gz: c432b0981046afd428eb2110dd45becfa810952c2d4e4d7263b117da82d5dd3da8c0a01713c8fff01ff5abe29c269dcfb99927d80a3941e8114dd6f573e9dc60
6
+ metadata.gz: 84010f77dfa1d2786f0c9b4a3783073ad15bbf6146bac16f4a49401b8b378e28f0747f82a6880eb01dda175fccbd1465f3e25391586e10c11d424e304fe8e74b
7
+ data.tar.gz: 95c19822da06edb58e250215fb0c5e2154da99e8881a2b8a8ce4fb9fe7c124772e81d7a2774595fdedb97cec3166e0b34e7c098ac20d8133ad97bfcb1196534e
data/bin/tf CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # Catch Ctrl+C to avoid stack traces
4
5
  Signal.trap('INT') { abort }
5
6
 
6
- require 'bundler/setup' if File.exist?(File.expand_path('../../Gemfile', __FILE__))
7
+ require 'bundler/setup' if File.exist?(File.expand_path('../Gemfile', __dir__))
7
8
  require 'yle_tf/cli'
8
9
 
9
10
  cli = YleTf::CLI.new(ARGV)
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yle_tf/logger'
2
4
  require 'yle_tf/version'
3
5
 
4
6
  class YleTf
7
+ TERRAFORM_VERSION_REQUIREMENT = '>= 0.9'
8
+
5
9
  autoload :Action, 'yle_tf/action'
6
10
  autoload :Error, 'yle_tf/error'
7
11
  autoload :Plugin, 'yle_tf/plugin'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class YleTf
2
4
  module Action
3
5
  autoload :Builder, 'yle_tf/action/builder'
@@ -10,11 +12,13 @@ class YleTf
10
12
  autoload :TmpDir, 'yle_tf/action/tmpdir'
11
13
  autoload :VerifyTerraformVersion, 'yle_tf/action/verify_terraform_version'
12
14
  autoload :VerifyTfEnv, 'yle_tf/action/verify_tf_env'
15
+ autoload :VerifyYleTfVersion, 'yle_tf/action/verify_yle_tf_version'
13
16
  autoload :WriteTerraformrcDefaults, 'yle_tf/action/write_terraformrc_defaults'
14
17
 
15
18
  def self.default_action_stack(command_class = nil)
16
19
  Builder.new do
17
20
  use LoadConfig
21
+ use VerifyYleTfVersion
18
22
  use VerifyTfEnv
19
23
  use TmpDir
20
24
  use WriteTerraformrcDefaults
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../../../vendor/middleware/builder'
2
4
 
3
5
  class YleTf
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yle_tf/logger'
2
4
 
3
5
  class YleTf
@@ -12,9 +14,9 @@ class YleTf
12
14
 
13
15
  def call(env)
14
16
  if env[:tf_options][:only_hooks]
15
- Logger.debug "Skipping command #{command.class} due to `--only-hooks`"
17
+ Logger.debug "Skipping command #{command} due to `--only-hooks`"
16
18
  else
17
- Logger.debug "Executing command #{command.class} with env: #{env.inspect}"
19
+ Logger.debug "Executing command #{command} with env: #{env.inspect}"
18
20
  command.new.execute(env)
19
21
  end
20
22
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
 
3
5
  require 'yle_tf/logger'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yle_tf/logger'
2
4
  require 'yle_tf/vars_file'
3
5
 
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yle_tf/config'
4
+ require 'yle_tf/logger'
2
5
 
3
6
  class YleTf
4
7
  module Action
@@ -8,10 +11,15 @@ class YleTf
8
11
  end
9
12
 
10
13
  def call(env)
11
- env[:config] ||= Config.new(env[:tf_env])
14
+ env[:config] ||= load_config(env[:tf_env])
12
15
 
13
16
  @app.call(env)
14
17
  end
18
+
19
+ def load_config(tf_env)
20
+ Logger.debug("Initializing configuration for the #{tf_env.inspect} environment")
21
+ Config.load(tf_env).tap { |config| Logger.debug(config.inspect) }
22
+ end
15
23
  end
16
24
  end
17
25
  end
@@ -1,8 +1,10 @@
1
- require 'yle_tf/error'
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
2
5
  require 'yle_tf/logger'
3
6
  require 'yle_tf/plugin'
4
7
  require 'yle_tf/system'
5
- require 'yle_tf/version_requirement'
6
8
 
7
9
  class YleTf
8
10
  module Action
@@ -10,62 +12,58 @@ class YleTf
10
12
  TF_CMD_ARGS = %w[-input=false -no-color].freeze
11
13
 
12
14
  TF_CMD_OPTS = {
13
- env: { 'TF_IN_AUTOMATION' => 'true' }, # Reduces some output
14
- stdout: :debug # Hide the output to the debug level
15
+ env: { 'TF_IN_AUTOMATION' => 'true' }, # Reduces some output
16
+ stdout: :debug # Hide the output to the debug level
15
17
  }.freeze
16
18
 
19
+ attr_reader :config
20
+
17
21
  def initialize(app)
18
22
  @app = app
19
23
  end
20
24
 
21
25
  def call(env)
22
- config = env[:config]
23
- backend = backend_config(config)
26
+ @config = env[:config]
24
27
 
25
28
  Logger.info('Initializing Terraform')
26
29
  Logger.debug("Backend configuration: #{backend}")
27
30
 
28
- if VersionRequirement.pre_0_9?(env[:terraform_version])
29
- init_pre_0_9(backend)
30
- else
31
- init(backend)
32
- end
31
+ init(backend)
33
32
 
34
33
  @app.call(env)
35
34
  end
36
35
 
37
- def init_pre_0_9(backend)
38
- cli_args = backend.cli_args
39
- if cli_args
40
- YleTf::System.cmd('terraform', 'remote', 'config', *TF_CMD_ARGS, *cli_args, TF_CMD_OPTS)
41
- end
36
+ def init(backend)
37
+ Logger.debug('Configuring the backend')
38
+ backend.configure
39
+
40
+ Logger.debug('Symlinking errored.tfstate')
41
+ symlink_errored_tfstate
42
42
 
43
- Logger.debug('Fetching Terraform modules')
44
- YleTf::System.cmd('terraform', 'get', *TF_CMD_ARGS, TF_CMD_OPTS)
43
+ Logger.debug('Initializing Terraform')
44
+ YleTf::System.cmd('terraform', 'init', *TF_CMD_ARGS, **TF_CMD_OPTS)
45
45
  end
46
46
 
47
- def init(backend)
48
- Logger.debug('Generating the backend configuration')
49
- backend.generate_config do
50
- Logger.debug('Initializing Terraform')
51
- YleTf::System.cmd('terraform', 'init', *TF_CMD_ARGS, TF_CMD_OPTS)
52
- end
47
+ def backend
48
+ @backend ||= find_backend
53
49
  end
54
50
 
55
- def backend_config(config)
51
+ def find_backend
56
52
  backend_type = config.fetch('backend', 'type').downcase
57
- backend_proc = backend_proc(backend_type)
53
+ backend_proc = Plugin.manager.backends[backend_type]
58
54
 
59
55
  klass = backend_proc.call
60
- klass.new.backend_config(config)
56
+ klass.new(config)
61
57
  end
62
58
 
63
- def backend_proc(backend_type)
64
- backends = Plugin.manager.backends
65
- backends.fetch(backend_type.to_sym) do
66
- raise Error, "Unknown backend type '#{backend_type}'. " \
67
- "Supported backends: #{backends.keys.join(', ')}"
68
- end
59
+ def symlink_errored_tfstate
60
+ local_path = Pathname.pwd.join('errored.tfstate')
61
+ remote_path = config.module_dir.join('errored.tfstate')
62
+
63
+ # Remove the possibly copied old file
64
+ local_path.unlink if local_path.exist?
65
+
66
+ local_path.make_symlink(remote_path)
69
67
  end
70
68
  end
71
69
  end
@@ -1,5 +1,4 @@
1
- require 'yle_tf/logger'
2
- require 'yle_tf/tf_hook/runner'
1
+ # frozen_string_literal: true
3
2
 
4
3
  require 'yle_tf/logger'
5
4
  require 'yle_tf/tf_hook/runner'
@@ -21,7 +20,7 @@ class YleTf
21
20
 
22
21
  def hook_runner
23
22
  if run_hooks?
24
- TfHook::Runner.new(@env[:config], hook_env)
23
+ TfHook::Runner.new(config, hook_env)
25
24
  else
26
25
  NoRunner
27
26
  end
@@ -29,11 +28,16 @@ class YleTf
29
28
 
30
29
  def hook_env
31
30
  {
32
- 'TF_COMMAND' => @env[:tf_command],
33
- 'TF_ENV' => @env[:tf_env],
31
+ 'TF_COMMAND' => @env[:tf_command],
32
+ 'TF_ENV' => @env[:tf_env],
33
+ 'TF_MODULE_DIR' => config.module_dir.to_s,
34
34
  }
35
35
  end
36
36
 
37
+ def config
38
+ @env[:config]
39
+ end
40
+
37
41
  def run_hooks?
38
42
  !@env[:tf_options][:no_hooks]
39
43
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
  require 'tmpdir'
3
5
 
@@ -20,7 +22,7 @@ class YleTf
20
22
  @app.call(env)
21
23
  end
22
24
  ensure
23
- FileUtils.rm_r(tmpdir) if tmpdir && Dir.exist?(tmpdir)
25
+ FileUtils.rm_rf(tmpdir, secure: true) if tmpdir && Dir.exist?(tmpdir)
24
26
  end
25
27
 
26
28
  def tmpdir_prefix(config)
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yle_tf'
1
4
  require 'yle_tf/error'
2
5
  require 'yle_tf/logger'
3
6
  require 'yle_tf/system'
@@ -17,22 +20,32 @@ class YleTf
17
20
  raise(Error, 'Terraform not found') if !version
18
21
 
19
22
  Logger.debug("Terraform version: #{version}")
20
- verify_version(env)
23
+
24
+ verify_version(version, requirement_by_yletf, required_by: 'YleTf')
25
+ verify_version(version, requirement_by_config(env), required_by: 'config')
21
26
 
22
27
  @app.call(env)
23
28
  end
24
29
 
25
30
  def terraform_version
26
31
  v = YleTf::System.read_cmd('terraform', 'version', error_handler: proc {})
27
- Regexp.last_match(1) if v =~ /^Terraform v([^\s]+)/
32
+ m = /^Terraform v(?<version>[^\s]+)/.match(v)
33
+ m && m[:version]
28
34
  end
29
35
 
30
- def verify_version(env)
31
- version = env[:terraform_version]
36
+ def requirement_by_config(env)
32
37
  requirement = env[:config].fetch('terraform', 'version_requirement') { nil }
38
+ VersionRequirement.new(requirement)
39
+ end
40
+
41
+ def requirement_by_yletf
42
+ VersionRequirement.new(YleTf::TERRAFORM_VERSION_REQUIREMENT)
43
+ end
33
44
 
34
- if !VersionRequirement.new(requirement).satisfied_by?(version)
35
- raise Error, "Terraform version '#{requirement}' required, '#{version}' found"
45
+ def verify_version(version, requirement, **opts)
46
+ if !requirement.satisfied_by?(version)
47
+ raise Error, "Terraform version '#{requirement}' required by #{opts[:required_by]}, " \
48
+ "'#{version}' found"
36
49
  end
37
50
  end
38
51
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yle_tf/error'
2
4
  require 'yle_tf/vars_file'
3
5
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yle_tf/error'
4
+ require 'yle_tf/logger'
5
+ require 'yle_tf/version'
6
+ require 'yle_tf/version_requirement'
7
+
8
+ class YleTf
9
+ module Action
10
+ class VerifyYleTfVersion
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ Logger.debug('Verifying YleTf version')
17
+
18
+ requirement = requirement(env[:config])
19
+ verify_version(requirement)
20
+
21
+ @app.call(env)
22
+ end
23
+
24
+ def requirement(config)
25
+ requirement = config.fetch('yle_tf', 'version_requirement') { nil }
26
+ VersionRequirement.new(requirement)
27
+ end
28
+
29
+ def verify_version(requirement)
30
+ return if requirement.satisfied_by?(YleTf::VERSION)
31
+
32
+ raise Error, "YleTf version '#{YleTf::VERSION}', '#{requirement}' required by config"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fileutils'
2
4
  require 'yle_tf/logger'
3
5
 
@@ -5,10 +7,10 @@ class YleTf
5
7
  module Action
6
8
  class WriteTerraformrcDefaults
7
9
  # Path of the Terraform CLI configuration file
8
- RC_PATH = '~/.terraformrc'.freeze
10
+ RC_PATH = '~/.terraformrc'
9
11
 
10
12
  # Path of the plugin cache directory
11
- DEFAULT_PLUGIN_CACHE_PATH = '~/.terraform.d/plugin-cache'.freeze
13
+ DEFAULT_PLUGIN_CACHE_PATH = '$HOME/.terraform.d/plugin-cache'
12
14
 
13
15
  def initialize(app)
14
16
  @app = app
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ class YleTf
6
+ class Backend
7
+ BACKEND_CONFIG_FILE = '_backend.tf.json'
8
+
9
+ attr_reader :config
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ def type
16
+ @type ||= config.fetch('backend', 'type')
17
+ end
18
+
19
+ def backend_specific_config
20
+ config.fetch('backend', type)
21
+ end
22
+
23
+ # Generate backend configuration file for Terraform
24
+ def configure
25
+ data = {
26
+ terraform: [{
27
+ backend: [to_h]
28
+ }]
29
+ }
30
+ File.write(BACKEND_CONFIG_FILE, JSON.pretty_generate(data))
31
+ end
32
+
33
+ # Returns the backend configuration as a `Hash` for Terraform
34
+ def to_h
35
+ { type => backend_specific_config }
36
+ end
37
+
38
+ def to_s
39
+ to_h.to_s
40
+ end
41
+ end
42
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yle_tf'
2
4
 
3
5
  class YleTf
@@ -48,7 +50,7 @@ class YleTf
48
50
  if TF_OPTIONS.include?(arg)
49
51
  @tf_options[key(arg)] = true
50
52
  else
51
- STDERR.puts "Unknown option '#{arg}'"
53
+ warn "Unknown option '#{arg}'"
52
54
  @tf_command = 'help'
53
55
  @tf_env = 'error'
54
56
  break
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
  require 'yaml'
3
5
 
@@ -10,16 +12,23 @@ class YleTf
10
12
  class Config
11
13
  NotFoundError = Class.new(Error)
12
14
 
13
- attr_reader :config, :tf_env, :module_dir
15
+ # Loads the configuration based on the environment
16
+ def self.load(tf_env)
17
+ opts = {
18
+ tf_env: tf_env,
19
+ module_dir: Pathname.pwd
20
+ }
14
21
 
15
- def initialize(tf_env)
16
- Logger.debug("Initializing configuration for the #{tf_env.inspect} environment")
22
+ config = Loader.new(opts).load
23
+ new(config, **opts)
24
+ end
17
25
 
18
- @tf_env = tf_env
19
- @module_dir = Pathname.pwd
20
- @config = Loader.new(tf_env: tf_env, module_dir: module_dir).load
26
+ attr_reader :config, :tf_env, :module_dir
21
27
 
22
- Logger.debug(inspect)
28
+ def initialize(config, **opts)
29
+ @config = config
30
+ @tf_env = opts[:tf_env]
31
+ @module_dir = opts[:module_dir]
23
32
  end
24
33
 
25
34
  def to_s
@@ -33,8 +42,15 @@ class YleTf
33
42
  block ||= DEFAULT_NOT_FOUND_BLOCK
34
43
 
35
44
  keys.inject(config) do |conf, key|
36
- break block.call(keys) if !conf || !conf.key?(key)
37
- conf[key]
45
+ next conf[key] if conf.is_a?(Hash) && conf.key?(key)
46
+
47
+ if !conf.nil? && !conf.is_a?(Hash)
48
+ Logger.warn(
49
+ "Configuration [#{keys.join(' > ')}] includes non-hash element #{conf.inspect}"
50
+ )
51
+ end
52
+
53
+ break block.call(keys)
38
54
  end
39
55
  end
40
56