yle_tf 0.4.0 → 1.3.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 (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
@@ -1,33 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yle_tf/helpers/hash'
4
+
1
5
  class YleTf
2
6
  class Config
3
7
  module Defaults
4
8
  DEFAULT_CONFIG = {
5
- 'hooks' => {
6
- 'pre' => [],
9
+ 'hooks' => {
10
+ 'pre' => [],
7
11
  'post' => []
8
12
  },
9
- 'backend' => {
13
+ 'backend' => {
10
14
  'type' => 'file',
11
- 'bucket' => nil,
12
- 'file' => '<%= @module %>_<%= @env %>.tfstate',
13
- 'region' => nil,
14
- 'encrypt' => false,
15
+ 'file' => {
16
+ 'path' => '<%= @module %>_<%= @env %>.tfstate'
17
+ },
18
+ 's3' => {
19
+ 'key' => '<%= @module %>_<%= @env %>.tfstate'
20
+ }
15
21
  },
16
- 'tfvars' => {
22
+ 'tfvars' => {
17
23
  },
18
24
  'terraform' => {
19
25
  'version_requirement' => nil
26
+ },
27
+ 'yle_tf' => {
28
+ 'version_requirement' => nil
20
29
  }
21
30
  }.freeze
22
31
 
32
+ # Returns deep copy of the default config Hash.
23
33
  def default_config
24
- DEFAULT_CONFIG.dup
34
+ Helpers::Hash.deep_copy(DEFAULT_CONFIG)
25
35
  end
26
36
 
27
37
  def default_config_context
28
38
  {
29
- env: tf_env,
30
- module: module_dir.basename.to_s,
39
+ env: tf_env,
40
+ module: module_dir.basename.to_s,
41
+ module_dir: module_dir.to_s,
31
42
  }
32
43
  end
33
44
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
4
 
3
5
  class YleTf
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
 
3
5
  require 'yle_tf/logger'
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yle_tf/config/defaults'
2
4
  require 'yle_tf/config/erb'
3
5
  require 'yle_tf/config/file'
6
+ require 'yle_tf/config/migration'
7
+ require 'yle_tf/helpers/hash'
4
8
  require 'yle_tf/logger'
5
9
  require 'yle_tf/plugin'
6
10
 
7
- require_relative '../../../vendor/hash_deep_merge'
8
-
9
11
  class YleTf
10
12
  class Config
11
13
  class Loader
@@ -19,20 +21,88 @@ class YleTf
19
21
  end
20
22
 
21
23
  def load
22
- Logger.debug('Loading default config')
23
- config = default_config
24
- Logger.debug(config.inspect)
24
+ load_sequence = %i[
25
+ load_default_config
26
+ load_plugin_configurations
27
+ load_config_files
28
+ evaluate_configuration_strings
29
+ ]
30
+ load_sequence.inject({}) { |config, method| send(method, config) }
31
+ end
32
+
33
+ def load_default_config(_config)
34
+ task('Loading default config') { default_config }
35
+ end
36
+
37
+ def load_plugin_configurations(config)
38
+ Logger.debug('Loading configuration from plugins')
39
+
40
+ plugins.inject(config) do |prev_config, plugin|
41
+ migrate_and_merge_configuration(prev_config, plugin.default_config,
42
+ type: 'plugin', name: plugin.to_s)
43
+ end
44
+ end
45
+
46
+ def load_config_files(config)
47
+ Logger.debug('Loading configuration from files')
48
+
49
+ config_files.inject(config) do |prev_config, file|
50
+ migrate_and_merge_configuration(prev_config, file.read,
51
+ type: 'file', name: file.name)
52
+ end
53
+ end
54
+
55
+ def evaluate_configuration_strings(config)
56
+ task('Evaluating the configuration strings') { eval_config(config) }
57
+ end
58
+
59
+ def eval_config(config)
60
+ case config
61
+ when Hash
62
+ config.each_with_object({}) { |(key, value), h| h[key] = eval_config(value) }
63
+ when Array
64
+ config.map { |item| eval_config(item) }
65
+ when String
66
+ Config::ERB.evaluate(config, config_context)
67
+ else
68
+ config
69
+ end
70
+ end
25
71
 
26
- Logger.debug('Merging default configurations from plugins')
27
- merge_plugin_configurations(config)
28
- Logger.debug(config.inspect)
72
+ def plugins
73
+ Plugin.manager.registered
74
+ end
75
+
76
+ def config_files
77
+ module_dir.descend.lazy
78
+ .map { |dir| dir.join('tf.yaml') }
79
+ .select(&:exist?)
80
+ .map { |file| Config::File.new(file) }
81
+ end
29
82
 
30
- Logger.debug('Merging configurations from files')
31
- merge_config_files(config)
32
- Logger.debug(config.inspect)
83
+ def migrate_and_merge_configuration(prev_config, config, **opts)
84
+ task("- #{opts[:name]}") { config }
85
+ return prev_config if config.empty?
33
86
 
34
- Logger.debug('Evaluating the configuration strings')
35
- eval_config(config)
87
+ source = "#{opts[:type]}: '#{opts[:name]}'"
88
+ config = migrate_old_config(config, source: source)
89
+ deep_merge(prev_config, config, source: source)
90
+ end
91
+
92
+ def migrate_old_config(config, **opts)
93
+ task(' -> Migrating') do
94
+ Config::Migration.migrate_old_config(config, **opts)
95
+ end
96
+ end
97
+
98
+ def deep_merge(prev_config, config, **opts)
99
+ task(' -> Merging') do
100
+ Helpers::Hash.deep_merge(prev_config, config)
101
+ end
102
+ rescue StandardError => e
103
+ Logger.fatal("Failed to merge configuration from #{opts[:source]}:\n" \
104
+ "#{config.inspect}\ninto:\n#{prev_config.inspect}")
105
+ raise e
36
106
  end
37
107
 
38
108
  def config_context
@@ -54,53 +124,13 @@ class YleTf
54
124
  end
55
125
  end
56
126
 
57
- def merge_plugin_configurations(config)
58
- Plugin.manager.default_configs.each do |plugin_config|
59
- deep_merge(
60
- config, plugin_config,
61
- error_msg:
62
- "Failed to merge a plugin's default configuration:\n" \
63
- "#{plugin_config.inspect}\ninto:\n#{config.inspect}"
64
- )
65
- end
66
- end
67
-
68
- def merge_config_files(config)
69
- config_files do |file|
70
- Logger.debug(" - #{file}")
71
- deep_merge(
72
- config, file.read,
73
- error_msg:
74
- "Failed to merge configuration from '#{file}' into:\n" \
75
- "#{config.inspect}"
76
- )
77
- end
78
- end
127
+ # Helper to print debug information about the task and configuration
128
+ # after it
129
+ def task(message = nil)
130
+ Logger.debug(message) if message
79
131
 
80
- def deep_merge(config, new_config, opts = {})
81
- config.deep_merge!(new_config)
82
- rescue StandardError => e
83
- Logger.fatal(opts[:error_msg]) if opts[:error_msg]
84
- raise e
85
- end
86
-
87
- def config_files
88
- module_dir.descend do |dir|
89
- file = dir.join('tf.yaml')
90
- yield(Config::File.new(file)) if file.exist?
91
- end
92
- end
93
-
94
- def eval_config(config)
95
- case config
96
- when Hash
97
- config.each_with_object({}) { |(key, value), h| h[key] = eval_config(value) }
98
- when Array
99
- config.map { |item| eval_config(item) }
100
- when String
101
- Config::ERB.evaluate(config, config_context)
102
- else
103
- config
132
+ yield.tap do |config|
133
+ Logger.debug(" #{config.inspect}")
104
134
  end
105
135
  end
106
136
  end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'yle_tf/helpers/hash'
5
+ require 'yle_tf/logger'
6
+
7
+ class YleTf
8
+ class Config
9
+ class Migration
10
+ BACKEND_MIGRATIONS = {
11
+ 'file' => {
12
+ 'file' => 'path'
13
+ },
14
+ 's3' => {
15
+ 'region' => 'region',
16
+ 'bucket' => 'bucket',
17
+ 'file' => 'key',
18
+ 'encrypt' => 'encrypt'
19
+ },
20
+ 'swift' => {
21
+ 'region' => 'region_name',
22
+ 'container' => 'container',
23
+ 'archive_container' => 'archive_container'
24
+ }
25
+ }.freeze
26
+
27
+ include Helpers::Hash
28
+
29
+ def self.migrate_old_config(config, **opts)
30
+ new(config, **opts).migrated_config
31
+ end
32
+
33
+ attr_reader :config, :config_source
34
+
35
+ def initialize(config, **opts)
36
+ @config = config
37
+ @config_source = opts.fetch(:source)
38
+ end
39
+
40
+ def old_backend_config
41
+ config['backend']
42
+ end
43
+
44
+ def migrated_config
45
+ migrate_old_backend_config(&deprecation_warning)
46
+ end
47
+
48
+ # Returns a `Proc` to print deprecation warnings unless denied by an env var
49
+ def deprecation_warning
50
+ return nil if ENV['TF_OLD_CONFIG_WARNINGS'] == 'false'
51
+
52
+ lambda do |new_config|
53
+ Logger.warn("Old configuration found in #{config_source}")
54
+ Logger.warn("Please migrate to relevant parts of:\n" \
55
+ "#{sanitize_config(new_config)}")
56
+ Logger.warn(
57
+ 'See https://github.com/Yleisradio/yle_tf/wiki/Migrating-Configuration for more details'
58
+ )
59
+ end
60
+ end
61
+
62
+ # TODO: Remove support in v2.0
63
+ def migrate_old_backend_config
64
+ changed = false
65
+
66
+ new_config = BACKEND_MIGRATIONS.inject(config) do |prev_config, (type, keys)|
67
+ migrate_old_backend_config_keys(prev_config, type, keys) { changed = true }
68
+ end
69
+
70
+ yield(new_config) if changed && block_given?
71
+
72
+ new_config
73
+ end
74
+
75
+ def migrate_old_backend_config_keys(config, type, keys)
76
+ migrated_keys = find_old_backend_config_keys(keys)
77
+ return config if migrated_keys.empty?
78
+
79
+ defaults = {
80
+ 'backend' => {
81
+ type => {}
82
+ }
83
+ }
84
+ copy_with_defaults(config, defaults).tap do |new_config|
85
+ migrated_keys.each do |old_key, new_key|
86
+ new_config['backend'][type][new_key] = old_backend_config[old_key]
87
+ end
88
+
89
+ yield new_config
90
+ end
91
+ end
92
+
93
+ def find_old_backend_config_keys(keys)
94
+ return {} if !old_backend_config.is_a?(Hash)
95
+
96
+ keys.select do |old_key, _new_key|
97
+ old_backend_config.key?(old_key) &&
98
+ # Special case for 'file' as it is now used for option Hash for the
99
+ # 'file' backend
100
+ !(old_key == 'file' && old_backend_config['file'].is_a?(Hash))
101
+ end
102
+ end
103
+
104
+ def copy_with_defaults(config, defaults)
105
+ deep_merge(deep_copy(config), defaults)
106
+ end
107
+
108
+ def sanitize_config(config)
109
+ backend_config = config['backend'].select do |key, value|
110
+ key == 'type' || value.is_a?(Hash)
111
+ end
112
+
113
+ YAML.dump('backend' => backend_config)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class YleTf
2
4
  # Base class for yle_tf errors
3
5
  Error = Class.new(StandardError)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../vendor/hash_deep_merge'
4
+
5
+ class YleTf
6
+ module Helpers
7
+ module Hash
8
+ module_function
9
+
10
+ # Returns deep merged new Hash
11
+ def deep_merge(target, source)
12
+ target.deep_merge(source)
13
+ end
14
+
15
+ # Returns deep copy of a Hash.
16
+ # `dup` and `clone` only return shallow copies.
17
+ def deep_copy(hash)
18
+ Marshal.load(Marshal.dump(hash))
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
  require 'logger'
3
5
  require 'rubygems'
@@ -21,7 +23,6 @@ class YleTf
21
23
 
22
24
  def self.logger
23
25
  @logger ||= ::Logger.new(DEVICE).tap do |logger|
24
- patch_for_old_ruby(logger)
25
26
  logger.level = log_level
26
27
  logger.formatter = log_formatter
27
28
  end
@@ -63,14 +64,5 @@ class YleTf
63
64
  :brown
64
65
  end
65
66
  end
66
-
67
- # Patches the `::Logger` in older Ruby versions to
68
- # accept log level as a `String`
69
- def self.patch_for_old_ruby(logger)
70
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
71
- require_relative '../../vendor/logger_level_patch'
72
- logger.extend(LoggerLevelPatch)
73
- end
74
- end
75
67
  end
76
68
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class YleTf
2
4
  module Logger
3
5
  module Colorize
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class YleTf
2
4
  class Plugin
3
5
  autoload :ActionHook, 'yle_tf/plugin/action_hook'
4
6
  autoload :Loader, 'yle_tf/plugin/loader'
5
7
  autoload :Manager, 'yle_tf/plugin/manager'
6
8
 
9
+ DEFAULT_BACKEND = Object.new.freeze
7
10
  DEFAULT_COMMAND = Object.new.freeze
8
11
 
9
12
  def self.manager
@@ -30,7 +33,7 @@ class YleTf
30
33
  name = name.to_s if name.is_a?(Symbol)
31
34
  commands[name] = {
32
35
  synopsis: synopsis,
33
- proc: block
36
+ proc: block
34
37
  }
35
38
  end
36
39
 
@@ -49,7 +52,8 @@ class YleTf
49
52
  end
50
53
 
51
54
  def self.backend(type, &block)
52
- backends[type.to_sym] = block
55
+ type = type.to_s if type.is_a?(Symbol)
56
+ backends[type] = block
53
57
  end
54
58
  end
55
59
  end