yle_tf 1.0.0 → 1.4.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.
- checksums.yaml +4 -4
- data/bin/tf +1 -0
- data/lib/yle_tf.rb +4 -0
- data/lib/yle_tf/action.rb +4 -0
- data/lib/yle_tf/action/builder.rb +2 -0
- data/lib/yle_tf/action/command.rb +4 -2
- data/lib/yle_tf/action/copy_root_module.rb +2 -0
- data/lib/yle_tf/action/generate_vars_file.rb +2 -0
- data/lib/yle_tf/action/load_config.rb +9 -1
- data/lib/yle_tf/action/terraform_init.rb +63 -32
- data/lib/yle_tf/action/tf_hooks.rb +9 -5
- data/lib/yle_tf/action/tmpdir.rb +3 -1
- data/lib/yle_tf/action/verify_terraform_version.rb +19 -6
- data/lib/yle_tf/action/verify_tf_env.rb +2 -0
- data/lib/yle_tf/action/verify_yle_tf_version.rb +36 -0
- data/lib/yle_tf/action/write_terraformrc_defaults.rb +34 -17
- data/lib/yle_tf/backend.rb +47 -0
- data/lib/yle_tf/cli.rb +3 -1
- data/lib/yle_tf/config.rb +25 -9
- data/lib/yle_tf/config/defaults.rb +25 -11
- data/lib/yle_tf/config/erb.rb +2 -0
- data/lib/yle_tf/config/file.rb +2 -0
- data/lib/yle_tf/config/loader.rb +89 -59
- data/lib/yle_tf/config/migration.rb +117 -0
- data/lib/yle_tf/error.rb +2 -0
- data/lib/yle_tf/helpers/hash.rb +22 -0
- data/lib/yle_tf/logger.rb +2 -10
- data/lib/yle_tf/logger/colorize.rb +2 -0
- data/lib/yle_tf/plugin.rb +6 -2
- data/lib/yle_tf/plugin/action_hook.rb +2 -0
- data/lib/yle_tf/plugin/loader.rb +8 -1
- data/lib/yle_tf/plugin/manager.rb +3 -0
- data/lib/yle_tf/system.rb +5 -2
- data/lib/yle_tf/system/io_handlers.rb +5 -1
- data/lib/yle_tf/system/output_logger.rb +2 -0
- data/lib/yle_tf/system/tf_hook_output_logger.rb +2 -0
- data/lib/yle_tf/tf_hook.rb +11 -9
- data/lib/yle_tf/tf_hook/runner.rb +2 -0
- data/lib/yle_tf/vars_file.rb +16 -3
- data/lib/yle_tf/version.rb +3 -1
- data/lib/yle_tf/version_requirement.rb +2 -5
- data/lib/yle_tf_plugins/backends/{s3 → __default}/plugin.rb +6 -4
- data/lib/yle_tf_plugins/backends/file/backend.rb +89 -0
- data/lib/yle_tf_plugins/backends/file/plugin.rb +4 -2
- data/lib/yle_tf_plugins/commands/__default/command.rb +2 -0
- data/lib/yle_tf_plugins/commands/__default/plugin.rb +2 -0
- data/lib/yle_tf_plugins/commands/_config/command.rb +2 -0
- data/lib/yle_tf_plugins/commands/_config/plugin.rb +2 -0
- data/lib/yle_tf_plugins/commands/_shell/command.rb +2 -0
- data/lib/yle_tf_plugins/commands/_shell/plugin.rb +2 -0
- data/lib/yle_tf_plugins/commands/help/command.rb +9 -20
- data/lib/yle_tf_plugins/commands/help/plugin.rb +2 -0
- data/lib/yle_tf_plugins/commands/version/command.rb +2 -0
- data/lib/yle_tf_plugins/commands/version/plugin.rb +2 -0
- metadata +61 -22
- data/lib/yle_tf/backend_config.rb +0 -41
- data/lib/yle_tf_plugins/backends/file/command.rb +0 -31
- data/lib/yle_tf_plugins/backends/file/config.rb +0 -17
- data/lib/yle_tf_plugins/backends/s3/command.rb +0 -19
- data/lib/yle_tf_plugins/backends/swift/command.rb +0 -18
- data/lib/yle_tf_plugins/backends/swift/plugin.rb +0 -16
- data/vendor/logger_level_patch.rb +0 -29
@@ -0,0 +1,47 @@
|
|
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
|
+
# Tear down the backend
|
34
|
+
def tear_down
|
35
|
+
# Nothing to do by default
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the backend configuration as a `Hash` for Terraform
|
39
|
+
def to_h
|
40
|
+
{ type => backend_specific_config }
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
to_h.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/yle_tf/cli.rb
CHANGED
@@ -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
|
-
|
53
|
+
warn "Unknown option '#{arg}'"
|
52
54
|
@tf_command = 'help'
|
53
55
|
@tf_env = 'error'
|
54
56
|
break
|
data/lib/yle_tf/config.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
16
|
-
|
22
|
+
config = Loader.new(opts).load
|
23
|
+
new(config, **opts)
|
24
|
+
end
|
17
25
|
|
18
|
-
|
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
|
-
|
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
|
-
|
37
|
-
|
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
|
|
@@ -1,33 +1,47 @@
|
|
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
|
-
'
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
+
'file' => {
|
16
|
+
'path' => '<%= @module %>_<%= @env %>.tfstate',
|
17
|
+
'encrypt' => false,
|
18
|
+
'encrypt_command' => 'sops --encrypt --input-type binary --output-type binary --output "{{TO}}" "{{FROM}}"',
|
19
|
+
'decrypt_command' => 'sops --decrypt --input-type binary --output-type binary --output "{{TO}}" "{{FROM}}"'
|
20
|
+
},
|
21
|
+
's3' => {
|
22
|
+
'key' => '<%= @module %>_<%= @env %>.tfstate'
|
23
|
+
}
|
15
24
|
},
|
16
|
-
'tfvars'
|
25
|
+
'tfvars' => {
|
17
26
|
},
|
18
27
|
'terraform' => {
|
19
28
|
'version_requirement' => nil
|
29
|
+
},
|
30
|
+
'yle_tf' => {
|
31
|
+
'version_requirement' => nil
|
20
32
|
}
|
21
33
|
}.freeze
|
22
34
|
|
35
|
+
# Returns deep copy of the default config Hash.
|
23
36
|
def default_config
|
24
|
-
DEFAULT_CONFIG
|
37
|
+
Helpers::Hash.deep_copy(DEFAULT_CONFIG)
|
25
38
|
end
|
26
39
|
|
27
40
|
def default_config_context
|
28
41
|
{
|
29
|
-
env:
|
30
|
-
module:
|
42
|
+
env: tf_env,
|
43
|
+
module: module_dir.basename.to_s,
|
44
|
+
module_dir: module_dir.to_s,
|
31
45
|
}
|
32
46
|
end
|
33
47
|
end
|
data/lib/yle_tf/config/erb.rb
CHANGED
data/lib/yle_tf/config/file.rb
CHANGED
data/lib/yle_tf/config/loader.rb
CHANGED
@@ -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
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
35
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
81
|
-
|
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
|