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
data/lib/yle_tf/error.rb
CHANGED
@@ -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
|
data/lib/yle_tf/logger.rb
CHANGED
@@ -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
|
data/lib/yle_tf/plugin.rb
CHANGED
@@ -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:
|
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
|
-
|
55
|
+
type = type.to_s if type.is_a?(Symbol)
|
56
|
+
backends[type] = block
|
53
57
|
end
|
54
58
|
end
|
55
59
|
end
|
data/lib/yle_tf/plugin/loader.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yle_tf/logger'
|
2
4
|
|
3
5
|
class YleTf
|
@@ -13,7 +15,7 @@ class YleTf
|
|
13
15
|
|
14
16
|
def self.load_core_plugins
|
15
17
|
core_plugins.each do |plugin_file|
|
16
|
-
Logger.debug
|
18
|
+
Logger.debug { "Loading core plugin: #{core_plugin_name(plugin_file)}" }
|
17
19
|
load(plugin_file)
|
18
20
|
end
|
19
21
|
end
|
@@ -36,6 +38,11 @@ class YleTf
|
|
36
38
|
Dir.glob(File.expand_path('../../yle_tf_plugins/**/plugin.rb', __dir__))
|
37
39
|
end
|
38
40
|
|
41
|
+
def self.core_plugin_name(path)
|
42
|
+
m = %r{.*/yle_tf_plugins/(?<name>.+?)/plugin\.rb$}.match(path)
|
43
|
+
m ? m[:name] : path
|
44
|
+
end
|
45
|
+
|
39
46
|
def self.bundler_plugins
|
40
47
|
plugins = Bundler.definition.current_dependencies.select do |dep|
|
41
48
|
dep.groups.include?(BUNDLER_PLUGIN_GROUP)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yle_tf/logger'
|
2
4
|
|
3
5
|
class YleTf
|
@@ -42,6 +44,7 @@ class YleTf
|
|
42
44
|
registered.each do |plugin|
|
43
45
|
backends.merge!(plugin.backends)
|
44
46
|
end
|
47
|
+
backends.default = backends.delete(DEFAULT_BACKEND)
|
45
48
|
end
|
46
49
|
end
|
47
50
|
end
|
data/lib/yle_tf/system.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'English'
|
2
4
|
require 'open3'
|
3
5
|
require 'shellwords'
|
6
|
+
require 'stringio'
|
4
7
|
require 'thwait'
|
5
8
|
require 'yle_tf/error'
|
6
9
|
require 'yle_tf/logger'
|
@@ -14,7 +17,7 @@ class YleTf
|
|
14
17
|
DEFAULT_ERROR_HANDLER = ->(_exit_code, error) { raise error }.freeze
|
15
18
|
|
16
19
|
DEFAULT_IO_HANDLERS = {
|
17
|
-
stdin:
|
20
|
+
stdin: :dev_null,
|
18
21
|
stdout: :info,
|
19
22
|
stderr: :error
|
20
23
|
}.freeze
|
@@ -44,7 +47,7 @@ class YleTf
|
|
44
47
|
|
45
48
|
def self.read_cmd(*args, **opts)
|
46
49
|
buffer = StringIO.new
|
47
|
-
cmd(*args, opts.merge(stdout: buffer))
|
50
|
+
cmd(*args, **opts.merge(stdout: buffer))
|
48
51
|
buffer.string
|
49
52
|
end
|
50
53
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yle_tf/error'
|
2
4
|
require 'yle_tf/logger'
|
3
5
|
require 'yle_tf/system/output_logger'
|
@@ -22,6 +24,7 @@ class YleTf
|
|
22
24
|
if !handler.respond_to?(:call)
|
23
25
|
raise YleTf::Error, "Unknown input handler #{handler.inspect}"
|
24
26
|
end
|
27
|
+
|
25
28
|
handler
|
26
29
|
end
|
27
30
|
end
|
@@ -42,6 +45,7 @@ class YleTf
|
|
42
45
|
if !handler.respond_to?(:call)
|
43
46
|
raise YleTf::Error, "Unknown output handler #{handler.inspect}"
|
44
47
|
end
|
48
|
+
|
45
49
|
handler
|
46
50
|
end
|
47
51
|
end
|
@@ -104,7 +108,7 @@ class YleTf
|
|
104
108
|
while (data = source.readpartial(BLOCK_SIZE))
|
105
109
|
target.write(data)
|
106
110
|
end
|
107
|
-
rescue EOFError # rubocop:disable Lint/
|
111
|
+
rescue EOFError # rubocop:disable Lint/SuppressedException
|
108
112
|
# All read
|
109
113
|
rescue IOError => e
|
110
114
|
YleTf::Logger.debug e.full_message
|
data/lib/yle_tf/tf_hook.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'fileutils'
|
2
4
|
require 'tmpdir'
|
3
5
|
|
@@ -14,8 +16,8 @@ class YleTf
|
|
14
16
|
def self.from_config(config, tf_env)
|
15
17
|
TfHook.new(
|
16
18
|
description: config['description'],
|
17
|
-
source:
|
18
|
-
vars:
|
19
|
+
source: config['source'],
|
20
|
+
vars: merge_vars(config['vars'], tf_env)
|
19
21
|
)
|
20
22
|
end
|
21
23
|
|
@@ -23,7 +25,7 @@ class YleTf
|
|
23
25
|
def self.from_file(path)
|
24
26
|
TfHook.new(
|
25
27
|
description: File.basename(path),
|
26
|
-
path:
|
28
|
+
path: path
|
27
29
|
)
|
28
30
|
end
|
29
31
|
|
@@ -43,10 +45,10 @@ class YleTf
|
|
43
45
|
Logger.info("Running hook '#{description}'")
|
44
46
|
YleTf::System.cmd(
|
45
47
|
path,
|
46
|
-
env:
|
48
|
+
env: vars.merge(tf_vars),
|
47
49
|
progname: File.basename(path),
|
48
|
-
stdout:
|
49
|
-
stderr:
|
50
|
+
stdout: System::TfHookOutputLogger.new(:info),
|
51
|
+
stderr: System::TfHookOutputLogger.new(:error)
|
50
52
|
)
|
51
53
|
ensure
|
52
54
|
delete_tmpdir
|
@@ -57,9 +59,9 @@ class YleTf
|
|
57
59
|
raise Error, "Invalid or missing `source` for hook '#{description}'" if !m
|
58
60
|
|
59
61
|
{
|
60
|
-
uri:
|
62
|
+
uri: m[:uri],
|
61
63
|
path: m[:path],
|
62
|
-
ref:
|
64
|
+
ref: m[:ref] || 'master'
|
63
65
|
}
|
64
66
|
end
|
65
67
|
|
@@ -83,7 +85,7 @@ class YleTf
|
|
83
85
|
end
|
84
86
|
|
85
87
|
def delete_tmpdir
|
86
|
-
FileUtils.
|
88
|
+
FileUtils.rm_rf(@tmpdir, secure: true) if @tmpdir && Dir.exist?(@tmpdir)
|
87
89
|
@tmpdir = nil
|
88
90
|
end
|
89
91
|
|
data/lib/yle_tf/vars_file.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class YleTf
|
2
4
|
class VarsFile
|
3
|
-
ENV_DIR = 'envs'
|
5
|
+
ENV_DIR = 'envs'
|
4
6
|
|
5
7
|
# Returns the env specific tfvars file path if it exists
|
6
8
|
def self.find_env_vars_file(config)
|
@@ -10,7 +12,7 @@ class YleTf
|
|
10
12
|
|
11
13
|
# Returns all envs that have tfvars files
|
12
14
|
def self.list_all_envs(config)
|
13
|
-
Dir.glob("#{config.module_dir}/#{ENV_DIR}/*.tfvars").map do |path|
|
15
|
+
Dir.glob("#{config.module_dir}/#{ENV_DIR}/*.tfvars").sort.map do |path|
|
14
16
|
File.basename(path, '.tfvars')
|
15
17
|
end
|
16
18
|
end
|
@@ -36,9 +38,20 @@ class YleTf
|
|
36
38
|
File.open(path, 'a') do |file|
|
37
39
|
file.puts # ensure we don't append to an existing line
|
38
40
|
vars.each do |key, value|
|
39
|
-
file.puts
|
41
|
+
file.puts "#{key} = #{eval_value(value)}"
|
40
42
|
end
|
41
43
|
end
|
42
44
|
end
|
45
|
+
|
46
|
+
def eval_value(value)
|
47
|
+
case value
|
48
|
+
when Hash
|
49
|
+
%({ #{value.map { |k, v| "#{k} = #{eval_value(v)}" }.join(', ')} })
|
50
|
+
when Array
|
51
|
+
%([ #{value.map { |item| eval_value(item) }.join(', ')} ])
|
52
|
+
else
|
53
|
+
%("#{value}")
|
54
|
+
end
|
55
|
+
end
|
43
56
|
end
|
44
57
|
end
|
data/lib/yle_tf/version.rb
CHANGED
@@ -1,13 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rubygems'
|
2
4
|
|
3
5
|
class YleTf
|
4
6
|
# Helper class for comparing versions
|
5
7
|
class VersionRequirement
|
6
|
-
# Checks if the specified Terrform version is older than 0.9
|
7
|
-
def self.pre_0_9?(terraform_version)
|
8
|
-
new('< 0.9.0-beta').satisfied_by?(terraform_version)
|
9
|
-
end
|
10
|
-
|
11
8
|
attr_reader :requirement
|
12
9
|
|
13
10
|
def initialize(requirement)
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yle_tf'
|
4
|
+
require 'yle_tf/backend'
|
2
5
|
|
3
6
|
module YleTfPlugins
|
4
7
|
module Backends
|
5
|
-
module
|
8
|
+
module Default
|
6
9
|
class Plugin < YleTf::Plugin
|
7
10
|
register
|
8
11
|
|
9
|
-
backend(
|
10
|
-
|
11
|
-
Command
|
12
|
+
backend(DEFAULT_BACKEND) do
|
13
|
+
YleTf::Backend
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'pathname'
|
5
|
+
require 'shellwords'
|
6
|
+
|
7
|
+
require 'yle_tf/backend'
|
8
|
+
require 'yle_tf/logger'
|
9
|
+
require 'yle_tf/system'
|
10
|
+
|
11
|
+
module YleTfPlugins
|
12
|
+
module Backends
|
13
|
+
module File
|
14
|
+
class Backend < YleTf::Backend
|
15
|
+
def configure
|
16
|
+
if !encrypt?
|
17
|
+
create_tfstate(tfstate_path)
|
18
|
+
symlink_tfstate
|
19
|
+
elsif tfstate_path.exist?
|
20
|
+
decrypt_tfstate
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def tear_down
|
25
|
+
encrypt_tfstate if encrypt? && local_tfstate_path.exist?
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_tfstate(path)
|
29
|
+
return if path.exist?
|
30
|
+
|
31
|
+
YleTf::Logger.debug('Creating state file')
|
32
|
+
path.write(tfstate_template, perm: 0o644)
|
33
|
+
end
|
34
|
+
|
35
|
+
def symlink_tfstate
|
36
|
+
YleTf::Logger.info("Symlinking state to '#{tfstate_path}'")
|
37
|
+
local_tfstate_path.make_symlink(tfstate_path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def encrypt?
|
41
|
+
config.fetch('backend', type, 'encrypt')
|
42
|
+
end
|
43
|
+
|
44
|
+
def decrypt_tfstate
|
45
|
+
YleTf::Logger.info("Decrypting state from '#{tfstate_path}'")
|
46
|
+
|
47
|
+
cmd = config.fetch('backend', type, 'decrypt_command')
|
48
|
+
cmd.gsub!('{{FROM}}', tfstate_path.to_s)
|
49
|
+
cmd.gsub!('{{TO}}', local_tfstate_path.to_s)
|
50
|
+
|
51
|
+
# Split the command to have nicer logs
|
52
|
+
YleTf::System.cmd(*Shellwords.split(cmd))
|
53
|
+
end
|
54
|
+
|
55
|
+
def encrypt_tfstate
|
56
|
+
YleTf::Logger.info("Encrypting state to '#{tfstate_path}'")
|
57
|
+
|
58
|
+
cmd = config.fetch('backend', type, 'encrypt_command')
|
59
|
+
cmd.gsub!('{{FROM}}', local_tfstate_path.to_s)
|
60
|
+
cmd.gsub!('{{TO}}', tfstate_path.to_s)
|
61
|
+
|
62
|
+
YleTf::System.cmd(*Shellwords.split(cmd),
|
63
|
+
error_handler: method(:on_encrypt_error))
|
64
|
+
end
|
65
|
+
|
66
|
+
def on_encrypt_error(_exit_code, error)
|
67
|
+
plain_tfstate_path = "#{tfstate_path}.plaintext"
|
68
|
+
|
69
|
+
YleTf::Logger.warn("Copying unencrypted state to '#{plain_tfstate_path}'")
|
70
|
+
FileUtils.cp(local_tfstate_path.to_s, plain_tfstate_path)
|
71
|
+
|
72
|
+
raise error
|
73
|
+
end
|
74
|
+
|
75
|
+
def tfstate_path
|
76
|
+
@tfstate_path ||= config.module_dir.join(config.fetch('backend', type, 'path'))
|
77
|
+
end
|
78
|
+
|
79
|
+
def local_tfstate_path
|
80
|
+
@local_tfstate_path ||= Pathname.pwd.join('terraform.tfstate')
|
81
|
+
end
|
82
|
+
|
83
|
+
def tfstate_template
|
84
|
+
'{"version": 1}'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|