yle_tf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +26 -0
  5. data/.travis.yml +7 -0
  6. data/CHANGELOG.md +3 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +442 -0
  11. data/Rakefile +6 -0
  12. data/bin/tf +7 -0
  13. data/examples/envs/prod.tfvars +5 -0
  14. data/examples/envs/test.tfvars +2 -0
  15. data/examples/main.tf +10 -0
  16. data/examples/tf.yaml +4 -0
  17. data/examples/tf_hooks/pre/get_current_hash.sh +26 -0
  18. data/examples/variables.tf +22 -0
  19. data/lib/yle_tf.rb +70 -0
  20. data/lib/yle_tf/action.rb +29 -0
  21. data/lib/yle_tf/action/builder.rb +9 -0
  22. data/lib/yle_tf/action/command.rb +25 -0
  23. data/lib/yle_tf/action/copy_root_module.rb +22 -0
  24. data/lib/yle_tf/action/generate_vars_file.rb +27 -0
  25. data/lib/yle_tf/action/load_config.rb +17 -0
  26. data/lib/yle_tf/action/terraform_init.rb +63 -0
  27. data/lib/yle_tf/action/tf_hooks.rb +48 -0
  28. data/lib/yle_tf/action/tmpdir.rb +35 -0
  29. data/lib/yle_tf/action/verify_terraform_version.rb +41 -0
  30. data/lib/yle_tf/action/verify_tf_env.rb +24 -0
  31. data/lib/yle_tf/backend_config.rb +41 -0
  32. data/lib/yle_tf/cli.rb +88 -0
  33. data/lib/yle_tf/config.rb +45 -0
  34. data/lib/yle_tf/config/defaults.rb +35 -0
  35. data/lib/yle_tf/config/erb.rb +22 -0
  36. data/lib/yle_tf/config/file.rb +26 -0
  37. data/lib/yle_tf/config/loader.rb +108 -0
  38. data/lib/yle_tf/error.rb +4 -0
  39. data/lib/yle_tf/logger.rb +48 -0
  40. data/lib/yle_tf/plugin.rb +55 -0
  41. data/lib/yle_tf/plugin/action_hook.rb +23 -0
  42. data/lib/yle_tf/plugin/loader.rb +59 -0
  43. data/lib/yle_tf/plugin/manager.rb +49 -0
  44. data/lib/yle_tf/system.rb +22 -0
  45. data/lib/yle_tf/tf_hook.rb +90 -0
  46. data/lib/yle_tf/tf_hook/runner.rb +48 -0
  47. data/lib/yle_tf/vars_file.rb +42 -0
  48. data/lib/yle_tf/version.rb +3 -0
  49. data/lib/yle_tf/version_requirement.rb +25 -0
  50. data/lib/yle_tf_plugins/backends/file/command.rb +31 -0
  51. data/lib/yle_tf_plugins/backends/file/config.rb +17 -0
  52. data/lib/yle_tf_plugins/backends/file/plugin.rb +16 -0
  53. data/lib/yle_tf_plugins/backends/s3/command.rb +19 -0
  54. data/lib/yle_tf_plugins/backends/s3/plugin.rb +16 -0
  55. data/lib/yle_tf_plugins/commands/__default/command.rb +14 -0
  56. data/lib/yle_tf_plugins/commands/__default/plugin.rb +14 -0
  57. data/lib/yle_tf_plugins/commands/_config/command.rb +11 -0
  58. data/lib/yle_tf_plugins/commands/_config/plugin.rb +19 -0
  59. data/lib/yle_tf_plugins/commands/_shell/command.rb +15 -0
  60. data/lib/yle_tf_plugins/commands/_shell/plugin.rb +14 -0
  61. data/lib/yle_tf_plugins/commands/help/command.rb +55 -0
  62. data/lib/yle_tf_plugins/commands/help/plugin.rb +18 -0
  63. data/lib/yle_tf_plugins/commands/version/command.rb +20 -0
  64. data/lib/yle_tf_plugins/commands/version/plugin.rb +18 -0
  65. data/vendor/hash_deep_merge.rb +59 -0
  66. data/vendor/logger_level_patch.rb +29 -0
  67. data/vendor/middleware/LICENSE +23 -0
  68. data/vendor/middleware/builder.rb +149 -0
  69. data/vendor/middleware/runner.rb +69 -0
  70. data/yle_tf.gemspec +37 -0
  71. metadata +160 -0
@@ -0,0 +1,3 @@
1
+ class YleTf
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -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,17 @@
1
+ require 'yle_tf/backend_config'
2
+
3
+ module YleTfPlugins
4
+ module Backends
5
+ module File
6
+ class Config < YleTf::BackendConfig
7
+ def generate_config
8
+ yield if block_given?
9
+ end
10
+
11
+ def cli_args
12
+ nil
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ require 'yle_tf'
2
+
3
+ module YleTfPlugins
4
+ module Backends
5
+ module File
6
+ class Plugin < YleTf::Plugin
7
+ register
8
+
9
+ backend('file') do
10
+ require_relative 'command'
11
+ Command
12
+ end
13
+ end
14
+ end
15
+ end
16
+ 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,16 @@
1
+ require 'yle_tf'
2
+
3
+ module YleTfPlugins
4
+ module Backends
5
+ module S3
6
+ class Plugin < YleTf::Plugin
7
+ register
8
+
9
+ backend('s3') do
10
+ require_relative 'command'
11
+ Command
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ require 'yle_tf/system'
2
+
3
+ module YleTfPlugins
4
+ module CommandDefault
5
+ class Command
6
+ def execute(env)
7
+ command = env[:tf_command]
8
+ args = env[:tf_command_args]
9
+
10
+ YleTf::System.cmd('terraform', command, *args)
11
+ end
12
+ end
13
+ end
14
+ 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,11 @@
1
+ require 'yle_tf/logger'
2
+
3
+ module YleTfPlugins
4
+ module CommandConfig
5
+ class Command
6
+ def execute(env)
7
+ puts env[:config]
8
+ end
9
+ end
10
+ end
11
+ 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