dh-proteus 0.1.3
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 +7 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +80 -0
- data/README.md +414 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/proteus +5 -0
- data/bin/proteus_testing +8 -0
- data/bin/setup +8 -0
- data/build.sh +28 -0
- data/dh-proteus.gemspec +50 -0
- data/lib/core_ext/hash.rb +14 -0
- data/lib/proteus.rb +9 -0
- data/lib/proteus/app.rb +86 -0
- data/lib/proteus/backend/backend.rb +41 -0
- data/lib/proteus/commands/apply.rb +53 -0
- data/lib/proteus/commands/clean.rb +22 -0
- data/lib/proteus/commands/destroy.rb +51 -0
- data/lib/proteus/commands/graph.rb +22 -0
- data/lib/proteus/commands/import.rb +75 -0
- data/lib/proteus/commands/move.rb +28 -0
- data/lib/proteus/commands/output.rb +29 -0
- data/lib/proteus/commands/plan.rb +55 -0
- data/lib/proteus/commands/remove.rb +71 -0
- data/lib/proteus/commands/render.rb +36 -0
- data/lib/proteus/commands/taint.rb +35 -0
- data/lib/proteus/common.rb +72 -0
- data/lib/proteus/config/config.rb +47 -0
- data/lib/proteus/context_management/context.rb +31 -0
- data/lib/proteus/context_management/helpers.rb +14 -0
- data/lib/proteus/generate.rb +18 -0
- data/lib/proteus/generators/context.rb +57 -0
- data/lib/proteus/generators/environment.rb +42 -0
- data/lib/proteus/generators/init.rb +40 -0
- data/lib/proteus/generators/module.rb +69 -0
- data/lib/proteus/generators/templates/config/config.yaml.erb +22 -0
- data/lib/proteus/generators/templates/context/main.tf.erb +7 -0
- data/lib/proteus/generators/templates/context/variables.tf.erb +3 -0
- data/lib/proteus/generators/templates/environment/terraform.tfvars.erb +6 -0
- data/lib/proteus/generators/templates/module/io.tf.erb +1 -0
- data/lib/proteus/generators/templates/module/module.tf.erb +1 -0
- data/lib/proteus/generators/templates/module/validator.rb.erb +9 -0
- data/lib/proteus/global_commands/validate.rb +45 -0
- data/lib/proteus/helpers.rb +91 -0
- data/lib/proteus/helpers/path_helpers.rb +90 -0
- data/lib/proteus/helpers/string_helpers.rb +13 -0
- data/lib/proteus/init.rb +16 -0
- data/lib/proteus/modules/manager.rb +53 -0
- data/lib/proteus/modules/terraform_module.rb +184 -0
- data/lib/proteus/templates/partial.rb +123 -0
- data/lib/proteus/templates/template_binding.rb +62 -0
- data/lib/proteus/validators/base_validator.rb +24 -0
- data/lib/proteus/validators/validation_dsl.rb +172 -0
- data/lib/proteus/validators/validation_error.rb +9 -0
- data/lib/proteus/validators/validation_helpers.rb +9 -0
- data/lib/proteus/version.rb +7 -0
- metadata +260 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
module Proteus
|
2
|
+
module Commands
|
3
|
+
module Move
|
4
|
+
def self.included(thor_class)
|
5
|
+
thor_class.class_eval do
|
6
|
+
|
7
|
+
desc "move FROM TO", "Moves an existing resource within the Terraform state"
|
8
|
+
def move(from, to)
|
9
|
+
init(verbose: parent_options[:verbose])
|
10
|
+
confirm question: "Do you really want to move #{from} to #{to} in context '(#{context}, #{environment})'?", color: :on_red, exit_code: 0 do
|
11
|
+
|
12
|
+
state_move_command = <<~STATE_MOVE_COMMAND
|
13
|
+
cd #{context_path(context)} && \
|
14
|
+
terraform state mv \
|
15
|
+
-var-file=#{var_file(context, environment)} \
|
16
|
+
#{aws_profile} \
|
17
|
+
#{from} \
|
18
|
+
#{to}
|
19
|
+
STATE_MOVE_COMMAND
|
20
|
+
syscall state_move_command.squeeze(' '), dryrun: dryrun
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Proteus
|
2
|
+
module Commands
|
3
|
+
module Output
|
4
|
+
def self.included(thor_class)
|
5
|
+
thor_class.class_eval do
|
6
|
+
|
7
|
+
desc "output", "Query Terraform outputs"
|
8
|
+
long_desc <<-LONGDESC
|
9
|
+
Query Terraform outputs.
|
10
|
+
LONGDESC
|
11
|
+
option :name, type: :string, default: nil, required: false
|
12
|
+
def output
|
13
|
+
|
14
|
+
init(verbose: parent_options[:verbose])
|
15
|
+
|
16
|
+
terraform_command = <<~TERRAFORM_COMMAND
|
17
|
+
cd #{context_path(context)} && \
|
18
|
+
terraform output \
|
19
|
+
-state=#{state_file(context, environment)} \
|
20
|
+
#{options[:name] ? options[:name] : ''}
|
21
|
+
TERRAFORM_COMMAND
|
22
|
+
|
23
|
+
syscall(terraform_command.squeeze(' '))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Proteus
|
2
|
+
module Commands
|
3
|
+
module Plan
|
4
|
+
def self.included(thor_class)
|
5
|
+
thor_class.class_eval do
|
6
|
+
|
7
|
+
desc "plan", "Runs terraform plan"
|
8
|
+
long_desc <<-LONGDESC
|
9
|
+
Runs terraform plan.
|
10
|
+
|
11
|
+
With --limit option, the plan run will be limited to the specified targets
|
12
|
+
LONGDESC
|
13
|
+
option :limit, type: :array, aliases: "-l", default: []
|
14
|
+
option :destroy, type: :boolean, default: false
|
15
|
+
def plan
|
16
|
+
module_manager = Proteus::Modules::Manager.new(context: context, environment: environment)
|
17
|
+
module_manager.clean
|
18
|
+
module_manager.render_modules
|
19
|
+
|
20
|
+
fmt_cmd = <<~FMT_CMD
|
21
|
+
cd #{context_path(context)} && \
|
22
|
+
terraform fmt -list=true .
|
23
|
+
FMT_CMD
|
24
|
+
|
25
|
+
fmt_output = syscall(
|
26
|
+
fmt_cmd.squeeze(' '),
|
27
|
+
capture: true,
|
28
|
+
suppress: true
|
29
|
+
)
|
30
|
+
|
31
|
+
say "Formatted files:", :green
|
32
|
+
say fmt_output, :green
|
33
|
+
|
34
|
+
init(verbose: parent_options[:verbose]) if not dryrun
|
35
|
+
|
36
|
+
terraform_command = <<~TERRAFORM_COMMAND
|
37
|
+
cd #{context_path(context)} && \
|
38
|
+
terraform plan \
|
39
|
+
#{"-destroy" if options[:destroy]} \
|
40
|
+
-input=false \
|
41
|
+
-refresh=true \
|
42
|
+
-module-depth=-1 \
|
43
|
+
-var-file=#{var_file(context, environment)} \
|
44
|
+
-out=#{plan_file(context, environment)} \
|
45
|
+
#{aws_profile} #{limit(options[:limit])} \
|
46
|
+
#{context_path(context)}
|
47
|
+
TERRAFORM_COMMAND
|
48
|
+
|
49
|
+
syscall terraform_command.squeeze(' '), dryrun: dryrun
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Proteus
|
2
|
+
module Commands
|
3
|
+
module Remove
|
4
|
+
def self.included(thor_class)
|
5
|
+
thor_class.class_eval do
|
6
|
+
|
7
|
+
desc "remove", "Remove a resource from the terraform state"
|
8
|
+
long_desc <<-LONGDESC
|
9
|
+
Remove a resource from the terraform state
|
10
|
+
--bulk Enables bulk import mode
|
11
|
+
|
12
|
+
--resource_address Terraform address of resource to remove from the terraform state
|
13
|
+
|
14
|
+
--resources_file File containing resource addresses and identifiers
|
15
|
+
LONGDESC
|
16
|
+
option :bulk, type: :boolean, aliases: "-b", required: false, default: false
|
17
|
+
option :resource_address, type: :string, aliases: "-a", required: false, default: nil
|
18
|
+
option :resources_file, type: :string, aliases: "-f", required: false, default: nil
|
19
|
+
def remove
|
20
|
+
if options[:bulk]
|
21
|
+
if !options[:resources_file]
|
22
|
+
say "Supply a file containing resource identifiers and Terraform addresses for bulk operations", :red
|
23
|
+
exit 1
|
24
|
+
end
|
25
|
+
else
|
26
|
+
if !options[:resource_address]
|
27
|
+
say "You need to supply a resource address.", :red
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
init(verbose: parent_options[:verbose])
|
33
|
+
|
34
|
+
confirm question: "Do you really want to run 'terraform state rm' in context '(#{context}, #{environment})'?", color: :on_red, exit_code: 0 do
|
35
|
+
state_remove_command = <<~STATE_REMOVE_COMMAND
|
36
|
+
cd #{context_path(context)} && \
|
37
|
+
terraform state rm \
|
38
|
+
-var-file=#{var_file(context, environment)} \
|
39
|
+
#{aws_profile} \
|
40
|
+
%{resource_addresses}
|
41
|
+
STATE_REMOVE_COMMAND
|
42
|
+
|
43
|
+
if options[:bulk]
|
44
|
+
if File.file?(options[:resources_file])
|
45
|
+
File.open(options[:resources_file], "r") do |file|
|
46
|
+
resource_addresses = []
|
47
|
+
file.each_line do |line|
|
48
|
+
resource = line.chomp.split(" = ")
|
49
|
+
resource_addresses << resource[0]
|
50
|
+
end
|
51
|
+
|
52
|
+
resource_addresses.each_slice(500) do |slice|
|
53
|
+
syscall (state_remove_command % { resource_addresses: slice.join(' ') }).squeeze(' ')
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
else
|
58
|
+
say "File #{options[:resources_file]} does not exist.", :red
|
59
|
+
exit 1
|
60
|
+
end
|
61
|
+
else
|
62
|
+
syscall (state_remove_command % { resource_addresses: options[:resource_address] })
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'proteus/modules/manager'
|
2
|
+
|
3
|
+
module Proteus
|
4
|
+
module Commands
|
5
|
+
module Render
|
6
|
+
def self.included(thor_class)
|
7
|
+
thor_class.class_eval do
|
8
|
+
|
9
|
+
desc "render", "Renders the module templates"
|
10
|
+
long_desc <<-LONGDESC
|
11
|
+
Renders the module templates without running Terraform
|
12
|
+
LONGDESC
|
13
|
+
def render
|
14
|
+
render_backend
|
15
|
+
module_manager = Proteus::Modules::Manager.new(context: context, environment: environment)
|
16
|
+
module_manager.render_modules
|
17
|
+
|
18
|
+
fmt_cmd = <<~FMT_CMD
|
19
|
+
cd #{context_path(context)} && \
|
20
|
+
terraform fmt -list=true .
|
21
|
+
FMT_CMD
|
22
|
+
|
23
|
+
fmt_output = syscall(
|
24
|
+
fmt_cmd.squeeze(' '),
|
25
|
+
capture: true,
|
26
|
+
suppress: true
|
27
|
+
)
|
28
|
+
|
29
|
+
say "Formatted files:", :green
|
30
|
+
say fmt_output, :green
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Proteus
|
2
|
+
module Commands
|
3
|
+
module Taint
|
4
|
+
def self.included(thor_class)
|
5
|
+
thor_class.class_eval do
|
6
|
+
|
7
|
+
desc "taint", "Taints an existing resource"
|
8
|
+
long_desc <<-LONGDESC
|
9
|
+
Taints an existing resource
|
10
|
+
|
11
|
+
--resource The resource to taint
|
12
|
+
LONGDESC
|
13
|
+
option :resource, type: :string, aliases: "-r", required: true
|
14
|
+
option :module, type: :string, aliases: "-m", required: false, default: nil
|
15
|
+
def taint
|
16
|
+
init(verbose: parent_options[:verbose])
|
17
|
+
confirm question: "Do you really want to run 'terraform taint' on environment '#{environment}' in context '#{context}'?", color: :on_red, exit_code: 0 do
|
18
|
+
|
19
|
+
taint_command = <<~TAINT_COMMAND
|
20
|
+
cd #{context_path(context)} && \
|
21
|
+
terraform taint \
|
22
|
+
-var-file=#{var_file(context, environment)} \
|
23
|
+
#{aws_profile} \
|
24
|
+
#{options[:module] ? "-module=#{options[:module]}" : ""} \
|
25
|
+
#{options[:resource]}
|
26
|
+
TAINT_COMMAND
|
27
|
+
syscall taint_command.squeeze(' ')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'proteus/commands/apply'
|
2
|
+
require 'proteus/commands/clean'
|
3
|
+
require 'proteus/commands/destroy'
|
4
|
+
require 'proteus/commands/graph'
|
5
|
+
require 'proteus/commands/import'
|
6
|
+
require 'proteus/commands/move'
|
7
|
+
require 'proteus/commands/output'
|
8
|
+
require 'proteus/commands/plan'
|
9
|
+
require 'proteus/commands/remove'
|
10
|
+
require 'proteus/commands/render'
|
11
|
+
require 'proteus/commands/taint'
|
12
|
+
|
13
|
+
module Proteus
|
14
|
+
class Common < Thor
|
15
|
+
include Helpers
|
16
|
+
include Proteus::Helpers::PathHelpers
|
17
|
+
|
18
|
+
Proteus::Commands.constants.each do |command|
|
19
|
+
include const_get("Proteus::Commands::#{command}")
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def context
|
25
|
+
self.class.context
|
26
|
+
end
|
27
|
+
|
28
|
+
def environment
|
29
|
+
self.class.environment
|
30
|
+
end
|
31
|
+
|
32
|
+
def render_backend
|
33
|
+
Proteus::Backend::Backend.new(config: config, context: context, environment: environment).render
|
34
|
+
end
|
35
|
+
|
36
|
+
def init(verbose: false)
|
37
|
+
say "initializing", :green
|
38
|
+
say "environment: #{environment}", :green
|
39
|
+
|
40
|
+
render_backend
|
41
|
+
|
42
|
+
`rm -rf #{context_path(context)}/.terraform/*.tf*`
|
43
|
+
`rm -rf #{context_path(context)}/.terraform/modules`
|
44
|
+
`rm -rf #{context_path(context)}/terraform.tfstate*`
|
45
|
+
|
46
|
+
terraform_command = <<~TERRAFORM_COMMAND
|
47
|
+
cd #{context_path(context)} && \
|
48
|
+
terraform init \
|
49
|
+
-backend-config='key=#{config[:backend][:key_prefix]}#{context}-#{environment}.tfstate' \
|
50
|
+
#{aws_profile} \
|
51
|
+
#{context_path(context)}
|
52
|
+
TERRAFORM_COMMAND
|
53
|
+
|
54
|
+
output = syscall terraform_command.squeeze(' '), suppress: true, capture: true
|
55
|
+
say(output, :green) if verbose
|
56
|
+
end
|
57
|
+
|
58
|
+
def aws_profile
|
59
|
+
config[:providers].select {|p| p[:name] == 'aws' }.first[:environments].each do |provider|
|
60
|
+
return "-var 'aws_profile=#{provider[:profile]}'" if environment =~ (/#{provider[:match]}/)
|
61
|
+
end
|
62
|
+
|
63
|
+
say "No provider found matching environment #{environment}.", :red
|
64
|
+
exit 1
|
65
|
+
end
|
66
|
+
|
67
|
+
def dryrun
|
68
|
+
parent_options[:dryrun]
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Proteus
|
2
|
+
module Config
|
3
|
+
def config
|
4
|
+
unless @config
|
5
|
+
@config = YAML.load(File.read(File.expand_path(config_path))).with_indifferent_access
|
6
|
+
begin
|
7
|
+
validator = ConfigValidator.new(@config)
|
8
|
+
rescue Proteus::Validators::ValidationError => validation_error
|
9
|
+
say "ConfigValidator: #{validation_error.message} [#{config_path}] #{"\u2718".encode('utf-8')}", :red
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
@config
|
15
|
+
end
|
16
|
+
|
17
|
+
class ConfigValidator < Proteus::Validators::BaseValidator
|
18
|
+
def validate
|
19
|
+
within :providers do
|
20
|
+
ensure_data_type Array
|
21
|
+
|
22
|
+
each do
|
23
|
+
within :environments do
|
24
|
+
ensure_uniqueness_across :match
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
within :slack_webhooks, optional: true do
|
30
|
+
ensure_data_type Array
|
31
|
+
|
32
|
+
each do
|
33
|
+
ensure_keys :match, :url
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
within :backend do
|
38
|
+
ensure_presence :key_prefix
|
39
|
+
|
40
|
+
within :bucket do
|
41
|
+
ensure_keys :name, :region, :profile
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'proteus/helpers/path_helpers'
|
2
|
+
|
3
|
+
module Proteus
|
4
|
+
module ContextManagement
|
5
|
+
class Context
|
6
|
+
include Proteus::Helpers::PathHelpers
|
7
|
+
|
8
|
+
attr_accessor :name
|
9
|
+
|
10
|
+
def initialize(name:)
|
11
|
+
@name = name
|
12
|
+
|
13
|
+
ensure_temp_directory
|
14
|
+
end
|
15
|
+
|
16
|
+
def environments
|
17
|
+
Dir.glob("#{File.expand_path(File.join(environments_path(@name)))}/*.tfvars").map do |vars_file|
|
18
|
+
File.basename(vars_file).split('.')[1]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def ensure_temp_directory
|
25
|
+
unless File.directory?(context_temp_directory(@name))
|
26
|
+
Dir.mkdir(context_temp_directory(@name))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Proteus
|
2
|
+
module ContextManagement
|
3
|
+
module Helpers
|
4
|
+
include Proteus::Helpers::PathHelpers
|
5
|
+
def contexts
|
6
|
+
say "loading contexts", :green
|
7
|
+
|
8
|
+
Dir.glob(File.join(contexts_path, "*")).collect do |context_path|
|
9
|
+
Context.new(name: File.basename(context_path))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'proteus/generators/context'
|
2
|
+
require 'proteus/generators/environment'
|
3
|
+
require 'proteus/generators/module'
|
4
|
+
|
5
|
+
module Proteus
|
6
|
+
class Generate < Thor
|
7
|
+
include Thor::Actions
|
8
|
+
include Helpers
|
9
|
+
|
10
|
+
def self.source_root
|
11
|
+
File.expand_path(File.join(File.dirname(__FILE__), 'generators', 'templates'))
|
12
|
+
end
|
13
|
+
|
14
|
+
include Generators::Context
|
15
|
+
include Generators::Environment
|
16
|
+
include Generators::Module
|
17
|
+
end
|
18
|
+
end
|