yle_tf 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.rubocop.yml +26 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +3 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +442 -0
- data/Rakefile +6 -0
- data/bin/tf +7 -0
- data/examples/envs/prod.tfvars +5 -0
- data/examples/envs/test.tfvars +2 -0
- data/examples/main.tf +10 -0
- data/examples/tf.yaml +4 -0
- data/examples/tf_hooks/pre/get_current_hash.sh +26 -0
- data/examples/variables.tf +22 -0
- data/lib/yle_tf.rb +70 -0
- data/lib/yle_tf/action.rb +29 -0
- data/lib/yle_tf/action/builder.rb +9 -0
- data/lib/yle_tf/action/command.rb +25 -0
- data/lib/yle_tf/action/copy_root_module.rb +22 -0
- data/lib/yle_tf/action/generate_vars_file.rb +27 -0
- data/lib/yle_tf/action/load_config.rb +17 -0
- data/lib/yle_tf/action/terraform_init.rb +63 -0
- data/lib/yle_tf/action/tf_hooks.rb +48 -0
- data/lib/yle_tf/action/tmpdir.rb +35 -0
- data/lib/yle_tf/action/verify_terraform_version.rb +41 -0
- data/lib/yle_tf/action/verify_tf_env.rb +24 -0
- data/lib/yle_tf/backend_config.rb +41 -0
- data/lib/yle_tf/cli.rb +88 -0
- data/lib/yle_tf/config.rb +45 -0
- data/lib/yle_tf/config/defaults.rb +35 -0
- data/lib/yle_tf/config/erb.rb +22 -0
- data/lib/yle_tf/config/file.rb +26 -0
- data/lib/yle_tf/config/loader.rb +108 -0
- data/lib/yle_tf/error.rb +4 -0
- data/lib/yle_tf/logger.rb +48 -0
- data/lib/yle_tf/plugin.rb +55 -0
- data/lib/yle_tf/plugin/action_hook.rb +23 -0
- data/lib/yle_tf/plugin/loader.rb +59 -0
- data/lib/yle_tf/plugin/manager.rb +49 -0
- data/lib/yle_tf/system.rb +22 -0
- data/lib/yle_tf/tf_hook.rb +90 -0
- data/lib/yle_tf/tf_hook/runner.rb +48 -0
- data/lib/yle_tf/vars_file.rb +42 -0
- data/lib/yle_tf/version.rb +3 -0
- data/lib/yle_tf/version_requirement.rb +25 -0
- data/lib/yle_tf_plugins/backends/file/command.rb +31 -0
- data/lib/yle_tf_plugins/backends/file/config.rb +17 -0
- data/lib/yle_tf_plugins/backends/file/plugin.rb +16 -0
- data/lib/yle_tf_plugins/backends/s3/command.rb +19 -0
- data/lib/yle_tf_plugins/backends/s3/plugin.rb +16 -0
- data/lib/yle_tf_plugins/commands/__default/command.rb +14 -0
- data/lib/yle_tf_plugins/commands/__default/plugin.rb +14 -0
- data/lib/yle_tf_plugins/commands/_config/command.rb +11 -0
- data/lib/yle_tf_plugins/commands/_config/plugin.rb +19 -0
- data/lib/yle_tf_plugins/commands/_shell/command.rb +15 -0
- data/lib/yle_tf_plugins/commands/_shell/plugin.rb +14 -0
- data/lib/yle_tf_plugins/commands/help/command.rb +55 -0
- data/lib/yle_tf_plugins/commands/help/plugin.rb +18 -0
- data/lib/yle_tf_plugins/commands/version/command.rb +20 -0
- data/lib/yle_tf_plugins/commands/version/plugin.rb +18 -0
- data/vendor/hash_deep_merge.rb +59 -0
- data/vendor/logger_level_patch.rb +29 -0
- data/vendor/middleware/LICENSE +23 -0
- data/vendor/middleware/builder.rb +149 -0
- data/vendor/middleware/runner.rb +69 -0
- data/yle_tf.gemspec +37 -0
- metadata +160 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
class YleTf
|
4
|
+
# Helper class for comparing versions
|
5
|
+
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
|
+
attr_reader :requirement
|
12
|
+
|
13
|
+
def initialize(requirement)
|
14
|
+
@requirement = requirement && Gem::Requirement.new(*requirement)
|
15
|
+
end
|
16
|
+
|
17
|
+
def satisfied_by?(version)
|
18
|
+
!requirement || requirement.satisfied_by?(Gem::Version.new(version))
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
requirement.to_s
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'config'
|
2
|
+
|
3
|
+
module YleTfPlugins
|
4
|
+
module Backends
|
5
|
+
module File
|
6
|
+
class Command
|
7
|
+
def backend_config(config)
|
8
|
+
local_path = Pathname.pwd.join('terraform.tfstate')
|
9
|
+
tfstate_path = tfstate_path(config)
|
10
|
+
|
11
|
+
YleTf::Logger.info("Symlinking state to '#{tfstate_path}'")
|
12
|
+
local_path.make_symlink(tfstate_path)
|
13
|
+
tfstate_path.write(tfstate_template, perm: 0o644) if !tfstate_path.exist?
|
14
|
+
|
15
|
+
Config.new(
|
16
|
+
'file',
|
17
|
+
'file' => tfstate_path.to_s
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def tfstate_path(config)
|
22
|
+
config.module_dir.join(config.fetch('backend', 'file'))
|
23
|
+
end
|
24
|
+
|
25
|
+
def tfstate_template
|
26
|
+
'{"version": 1}'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'yle_tf/backend_config'
|
2
|
+
|
3
|
+
module YleTfPlugins
|
4
|
+
module Backends
|
5
|
+
module S3
|
6
|
+
class Command
|
7
|
+
def backend_config(config)
|
8
|
+
YleTf::BackendConfig.new(
|
9
|
+
's3',
|
10
|
+
'region' => config.fetch('backend', 'region'),
|
11
|
+
'bucket' => config.fetch('backend', 'bucket'),
|
12
|
+
'key' => config.fetch('backend', 'file'),
|
13
|
+
'encrypt' => config.fetch('backend', 'encrypt')
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'yle_tf'
|
2
|
+
|
3
|
+
module YleTfPlugins
|
4
|
+
module CommandDefault
|
5
|
+
class Plugin < YleTf::Plugin
|
6
|
+
register
|
7
|
+
|
8
|
+
command(DEFAULT_COMMAND, 'Calls Terraform with the given subcommand') do
|
9
|
+
require_relative 'command'
|
10
|
+
YleTf::Action.default_action_stack(Command)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'yle_tf'
|
2
|
+
|
3
|
+
module YleTfPlugins
|
4
|
+
module CommandConfig
|
5
|
+
class Plugin < YleTf::Plugin
|
6
|
+
register
|
7
|
+
|
8
|
+
command('_config', 'Prints the evaluated configuration') do
|
9
|
+
require_relative 'command'
|
10
|
+
|
11
|
+
YleTf::Action::Builder.new do
|
12
|
+
use YleTf::Action::LoadConfig
|
13
|
+
use YleTf::Action::VerifyTfEnv
|
14
|
+
use YleTf::Action::Command, Command
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module YleTfPlugins
|
2
|
+
module CommandShell
|
3
|
+
class Command
|
4
|
+
def execute(_env)
|
5
|
+
shell = ENV.fetch('SHELL', 'bash')
|
6
|
+
|
7
|
+
puts "Executing shell '#{shell}' in the Terraform directory"
|
8
|
+
puts 'Use `exit` to quit'
|
9
|
+
puts
|
10
|
+
|
11
|
+
system(shell)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'yle_tf'
|
2
|
+
|
3
|
+
module YleTfPlugins
|
4
|
+
module CommandShell
|
5
|
+
class Plugin < YleTf::Plugin
|
6
|
+
register
|
7
|
+
|
8
|
+
command('_shell', 'Executes shell in the prepared environment') do
|
9
|
+
require_relative 'command'
|
10
|
+
YleTf::Action.default_action_stack(Command)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
require 'yle_tf/plugin'
|
4
|
+
|
5
|
+
module YleTfPlugins
|
6
|
+
module CommandHelp
|
7
|
+
class Command
|
8
|
+
def execute(env)
|
9
|
+
device(env).puts(opts.help)
|
10
|
+
exit 1 if error?(env)
|
11
|
+
end
|
12
|
+
|
13
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
14
|
+
def opts
|
15
|
+
OptionParser.new do |o|
|
16
|
+
o.summary_width = 18
|
17
|
+
o.banner = 'Usage: tf <environment> <command> [<args>]'
|
18
|
+
o.separator ''
|
19
|
+
o.separator 'YleTf options:'
|
20
|
+
o.on('-h', '--help', 'Prints this help')
|
21
|
+
o.on('-v', '--version', 'Prints the version information')
|
22
|
+
o.on('--debug', 'Print debug information')
|
23
|
+
o.on('--no-hooks', 'Do not run any hooks')
|
24
|
+
o.on('--only-hooks', 'Only run the hooks')
|
25
|
+
o.separator ''
|
26
|
+
o.separator 'Special tf commands:'
|
27
|
+
o.separator tf_command_help
|
28
|
+
o.separator ''
|
29
|
+
o.separator 'Terraform commands:'
|
30
|
+
o.separator terraform_help
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def error?(env)
|
35
|
+
env[:tf_env] == 'error'
|
36
|
+
end
|
37
|
+
|
38
|
+
def device(env)
|
39
|
+
error?(env) ? STDERR : STDOUT
|
40
|
+
end
|
41
|
+
|
42
|
+
def tf_command_help
|
43
|
+
YleTf::Plugin.manager.commands.sort.map do |command, data|
|
44
|
+
" #{command.ljust(18)} #{data[:synopsis]}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def terraform_help
|
49
|
+
`terraform help`.lines.grep(/^ /)
|
50
|
+
rescue Errno::ENOENT
|
51
|
+
' [Terraform not found]'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'yle_tf'
|
2
|
+
|
3
|
+
module YleTfPlugins
|
4
|
+
module CommandHelp
|
5
|
+
class Plugin < YleTf::Plugin
|
6
|
+
register
|
7
|
+
|
8
|
+
command('help', 'Prints this help') do
|
9
|
+
require_relative 'command'
|
10
|
+
|
11
|
+
YleTf::Action::Builder.new do
|
12
|
+
use YleTf::Action::TmpDir
|
13
|
+
use YleTf::Action::Command, Command
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
require 'yle_tf/version'
|
4
|
+
|
5
|
+
module YleTfPlugins
|
6
|
+
module CommandVersion
|
7
|
+
class Command
|
8
|
+
def execute(_env)
|
9
|
+
puts "YleTf #{YleTf::VERSION}"
|
10
|
+
puts terraform_version
|
11
|
+
end
|
12
|
+
|
13
|
+
def terraform_version
|
14
|
+
`terraform version`.lines.first
|
15
|
+
rescue Errno::ENOENT
|
16
|
+
'[Terraform not found]'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'yle_tf'
|
2
|
+
|
3
|
+
module YleTfPlugins
|
4
|
+
module CommandVersion
|
5
|
+
class Plugin < YleTf::Plugin
|
6
|
+
register
|
7
|
+
|
8
|
+
command('version', 'Prints the version information') do
|
9
|
+
require_relative 'command'
|
10
|
+
|
11
|
+
YleTf::Action::Builder.new do
|
12
|
+
use YleTf::Action::TmpDir
|
13
|
+
use YleTf::Action::Command, Command
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright (c) 2005-2017 David Heinemeier Hansson
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
class Hash
|
23
|
+
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
24
|
+
#
|
25
|
+
# h1 = { a: true, b: { c: [1, 2, 3] } }
|
26
|
+
# h2 = { a: false, b: { x: [3, 4, 5] } }
|
27
|
+
#
|
28
|
+
# h1.deep_merge(h2) # => { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
|
29
|
+
#
|
30
|
+
# Like with Hash#merge in the standard library, a block can be provided
|
31
|
+
# to merge values:
|
32
|
+
#
|
33
|
+
# h1 = { a: 100, b: 200, c: { c1: 100 } }
|
34
|
+
# h2 = { b: 250, c: { c1: 200 } }
|
35
|
+
# h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
|
36
|
+
# # => { a: 100, b: 450, c: { c1: 300 } }
|
37
|
+
def deep_merge(other_hash, &block)
|
38
|
+
dup.deep_merge!(other_hash, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Same as +deep_merge+, but modifies +self+.
|
42
|
+
def deep_merge!(other_hash, &block)
|
43
|
+
other_hash.each_pair do |current_key, other_value|
|
44
|
+
this_value = self[current_key]
|
45
|
+
|
46
|
+
self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
|
47
|
+
this_value.deep_merge(other_value, &block)
|
48
|
+
else
|
49
|
+
if block_given? && key?(current_key)
|
50
|
+
block.call(current_key, this_value, other_value)
|
51
|
+
else
|
52
|
+
other_value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
self
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
class YleTf
|
4
|
+
module LoggerLevelPatch
|
5
|
+
# Taken from Ruby 2.4.1
|
6
|
+
def level=(severity)
|
7
|
+
if severity.is_a?(Integer)
|
8
|
+
@level = severity
|
9
|
+
else
|
10
|
+
case severity.to_s.downcase
|
11
|
+
when 'debug'.freeze
|
12
|
+
@level = ::Logger::DEBUG
|
13
|
+
when 'info'.freeze
|
14
|
+
@level = ::Logger::INFO
|
15
|
+
when 'warn'.freeze
|
16
|
+
@level = ::Logger::WARN
|
17
|
+
when 'error'.freeze
|
18
|
+
@level = ::Logger::ERROR
|
19
|
+
when 'fatal'.freeze
|
20
|
+
@level = ::Logger::FATAL
|
21
|
+
when 'unknown'.freeze
|
22
|
+
@level = ::Logger::UNKNOWN
|
23
|
+
else
|
24
|
+
raise ArgumentError, "invalid log level: #{severity}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|