terraform_landscape 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/landscape +7 -0
- data/grammar/terraform_plan.treetop +67 -0
- data/lib/terraform_landscape.rb +7 -0
- data/lib/terraform_landscape/arguments_parser.rb +58 -0
- data/lib/terraform_landscape/cli.rb +51 -0
- data/lib/terraform_landscape/constants.rb +9 -0
- data/lib/terraform_landscape/errors.rb +33 -0
- data/lib/terraform_landscape/output.rb +118 -0
- data/lib/terraform_landscape/printer.rb +56 -0
- data/lib/terraform_landscape/terraform_plan.rb +188 -0
- data/lib/terraform_landscape/version.rb +6 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3adeba30b54a83c182ee6956d97f42e16fe1bb14
|
4
|
+
data.tar.gz: ab302a4d55105e6a0a3bc70e31c497e64dc3b489
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 065d407040083b704ddef29a67080db8b618f557ed498c9623ff26ced98ddbee28538fad417253370155d904974e06abea1a5d1741acaee63e1d6b15f7ceed44
|
7
|
+
data.tar.gz: 7602f283d8a02775dc4e07c5b93fd6c5763cc4e1d5f939fae3d1c042f47d0e3c387f48a2d3e472c9cb1d7610595f46829953f60bb7a356a9fbbfaaa28f2d4e72
|
data/bin/landscape
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Defines grammar for parsing `terraform plan` output.
|
2
|
+
|
3
|
+
grammar TerraformPlan
|
4
|
+
rule plan
|
5
|
+
[\s]* list:resource_list "\n"* {
|
6
|
+
def to_ast
|
7
|
+
list.to_ast
|
8
|
+
end
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
rule resource_list
|
13
|
+
item:resource "\n\n" list:resource_list {
|
14
|
+
def to_ast
|
15
|
+
[item.to_ast] + list.to_ast
|
16
|
+
end
|
17
|
+
}
|
18
|
+
/
|
19
|
+
item:resource "\n" {
|
20
|
+
def to_ast
|
21
|
+
[item.to_ast]
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
rule resource
|
27
|
+
header:resource_header "\n" attrs:attribute_list {
|
28
|
+
def to_ast
|
29
|
+
header.to_ast.merge(attributes: attrs.to_ast)
|
30
|
+
end
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
rule resource_header
|
35
|
+
change:('~' / '-' / '+') _ type:[a-zA-Z0-9_-]+ '.' name:[a-zA-Z0-9_-]+ {
|
36
|
+
def to_ast
|
37
|
+
{ change: change.text_value.to_sym, resource_type: type.text_value, resource_name: name.text_value }
|
38
|
+
end
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
rule attribute_list
|
43
|
+
_ item:attribute "\n" attrs:attribute_list {
|
44
|
+
def to_ast
|
45
|
+
item.to_ast.merge(attrs.to_ast)
|
46
|
+
end
|
47
|
+
}
|
48
|
+
/
|
49
|
+
_ item:attribute {
|
50
|
+
def to_ast
|
51
|
+
item.to_ast
|
52
|
+
end
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
rule attribute
|
57
|
+
attribute_name:[^\s:]* ':' _? attribute_value:[^\n]+ {
|
58
|
+
def to_ast
|
59
|
+
{ attribute_name.text_value => attribute_value.text_value }
|
60
|
+
end
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
rule _
|
65
|
+
[ ]+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require 'terraform_landscape/constants'
|
2
|
+
require 'terraform_landscape/version'
|
3
|
+
require 'terraform_landscape/output'
|
4
|
+
require 'terraform_landscape/errors'
|
5
|
+
require 'terraform_landscape/printer'
|
6
|
+
require 'terraform_landscape/terraform_plan'
|
7
|
+
require 'terraform_landscape/cli'
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module TerraformLandscape
|
4
|
+
# Handles option parsing for the command line application.
|
5
|
+
class ArgumentsParser
|
6
|
+
# Parses command line options into an options hash.
|
7
|
+
#
|
8
|
+
# @param args [Array<String>] arguments passed via the command line
|
9
|
+
#
|
10
|
+
# @return [Hash] parsed options
|
11
|
+
def parse(args)
|
12
|
+
@options = {}
|
13
|
+
@options[:command] = :pretty_print # Default command
|
14
|
+
|
15
|
+
OptionParser.new do |parser|
|
16
|
+
parser.banner = "Usage: landscape [options] [plan-output-file]"
|
17
|
+
|
18
|
+
add_info_options parser
|
19
|
+
end.parse!(args)
|
20
|
+
|
21
|
+
# Any remaining arguments are assumed to be the output file
|
22
|
+
@options[:plan_output_file] = args.first
|
23
|
+
|
24
|
+
@options
|
25
|
+
rescue OptionParser::InvalidOption => ex
|
26
|
+
raise InvalidCliOptionError,
|
27
|
+
"#{ex.message}\nRun `landscape --help` to " \
|
28
|
+
'see a list of available options.'
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Register informational flags.
|
34
|
+
def add_info_options(parser)
|
35
|
+
parser.on('--[no-]color', 'Force output to be colorized') do |color|
|
36
|
+
@options[:color] = color
|
37
|
+
end
|
38
|
+
|
39
|
+
parser.on('-d', '--debug', 'Enable debug mode for more verbose output') do
|
40
|
+
@options[:debug] = true
|
41
|
+
end
|
42
|
+
|
43
|
+
parser.on_tail('-h', '--help', 'Display help documentation') do
|
44
|
+
@options[:command] = :display_help
|
45
|
+
@options[:help_message] = parser.help
|
46
|
+
end
|
47
|
+
|
48
|
+
parser.on_tail('-v', '--version', 'Display version') do
|
49
|
+
@options[:command] = :display_version
|
50
|
+
end
|
51
|
+
|
52
|
+
parser.on_tail('-V', '--verbose-version', 'Display verbose version information') do
|
53
|
+
@options[:command] = :display_version
|
54
|
+
@options[:verbose_version] = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'commander'
|
2
|
+
|
3
|
+
module TerraformLandscape
|
4
|
+
# Command line application interface.
|
5
|
+
class CLI
|
6
|
+
include Commander::Methods
|
7
|
+
|
8
|
+
def initialize(output)
|
9
|
+
@output = output
|
10
|
+
end
|
11
|
+
|
12
|
+
# Parses the given command line arguments and executes appropriate logic
|
13
|
+
# based on those arguments.
|
14
|
+
#
|
15
|
+
# @param args [Array<String>] command line arguments
|
16
|
+
#
|
17
|
+
# @return [Integer] exit status code
|
18
|
+
def run(args)
|
19
|
+
program :name, 'Terraform Landscape'
|
20
|
+
program :version, VERSION
|
21
|
+
program :description, 'Pretty-print your Terraform plan output'
|
22
|
+
|
23
|
+
define_commands
|
24
|
+
|
25
|
+
run!
|
26
|
+
0 # OK
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def define_commands
|
32
|
+
command :print do |c|
|
33
|
+
c.action do
|
34
|
+
print
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
global_option '--no-color', 'Do not output any color' do
|
39
|
+
String.disable_colorization = true
|
40
|
+
@output.color_enabled = false
|
41
|
+
end
|
42
|
+
|
43
|
+
default_command :print
|
44
|
+
end
|
45
|
+
|
46
|
+
def print
|
47
|
+
printer = Printer.new(@output)
|
48
|
+
printer.process_stream(STDIN)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Global application constants.
|
4
|
+
module TerraformLandscape
|
5
|
+
HOME = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
|
6
|
+
|
7
|
+
REPO_URL = 'https://github.com/coinbase/terraform_landscape'.freeze
|
8
|
+
BUG_REPORT_URL = "#{REPO_URL}/issues".freeze
|
9
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Collection of errors that can be raised by the framework.
|
2
|
+
module TerraformLandscape
|
3
|
+
# Abstract error. Separates LintTrappings errors from other kinds of
|
4
|
+
# errors in the exception hierarchy.
|
5
|
+
#
|
6
|
+
# @abstract
|
7
|
+
class Error < StandardError
|
8
|
+
# Returns the status code that should be output if this error goes
|
9
|
+
# unhandled.
|
10
|
+
#
|
11
|
+
# Ideally these should resemble exit codes from the sysexits documentation
|
12
|
+
# where it makes sense.
|
13
|
+
def self.exit_status(*args)
|
14
|
+
if args.any?
|
15
|
+
@exit_status = args.first
|
16
|
+
elsif @exit_status
|
17
|
+
@exit_status
|
18
|
+
else
|
19
|
+
ancestors.each do |ancestor|
|
20
|
+
return 70 if ancestor == TerraformLandscape::Error # No exit status defined
|
21
|
+
return ancestor.exit_status if ancestor.exit_status
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def exit_status
|
27
|
+
self.class.exit_status
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Raised when there was a problem parsing a document.
|
32
|
+
class ParseError < Error; end
|
33
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module TerraformLandscape
|
2
|
+
# Encapsulates all communication to an output source.
|
3
|
+
class Output
|
4
|
+
# Whether colored output via ANSI escape sequences is enabled.
|
5
|
+
# @return [true,false]
|
6
|
+
attr_accessor :color_enabled
|
7
|
+
|
8
|
+
# Creates a logger which outputs nothing.
|
9
|
+
# @return [TerraformLandscape::Output]
|
10
|
+
def self.silent
|
11
|
+
new(File.open(File::NULL, 'w'))
|
12
|
+
end
|
13
|
+
|
14
|
+
# Creates a new {SlimLint::Logger} instance.
|
15
|
+
#
|
16
|
+
# @param out [IO] the output destination.
|
17
|
+
def initialize(out)
|
18
|
+
@out = out
|
19
|
+
@color_enabled = tty?
|
20
|
+
end
|
21
|
+
|
22
|
+
# Print the specified output.
|
23
|
+
#
|
24
|
+
# @param output [String] the output to send
|
25
|
+
# @param newline [true,false] whether to append a newline
|
26
|
+
def puts(output, newline = true)
|
27
|
+
@out.print(output)
|
28
|
+
@out.print("\n") if newline
|
29
|
+
end
|
30
|
+
|
31
|
+
# Print the specified output without a newline.
|
32
|
+
#
|
33
|
+
# @param output [String] the output to send
|
34
|
+
def print(output)
|
35
|
+
puts(output, false)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Print the specified output in bold face.
|
39
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
40
|
+
#
|
41
|
+
# @param args [Array<String>]
|
42
|
+
def bold(*args)
|
43
|
+
color('1', *args)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Print the specified output in a color indicative of error.
|
47
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
48
|
+
#
|
49
|
+
# @param args [Array<String>]
|
50
|
+
def error(*args)
|
51
|
+
color(31, *args)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Print the specified output in a bold face and color indicative of error.
|
55
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
56
|
+
#
|
57
|
+
# @param args [Array<String>]
|
58
|
+
def bold_error(*args)
|
59
|
+
color('1;31', *args)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Print the specified output in a color indicative of success.
|
63
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
64
|
+
#
|
65
|
+
# @param args [Array<String>]
|
66
|
+
def success(*args)
|
67
|
+
color(32, *args)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Print the specified output in a color indicative of a warning.
|
71
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
72
|
+
#
|
73
|
+
# @param args [Array<String>]
|
74
|
+
def warning(*args)
|
75
|
+
color(33, *args)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Print the specified output in a color indicating something worthy of
|
79
|
+
# notice.
|
80
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
81
|
+
#
|
82
|
+
# @param args [Array<String>]
|
83
|
+
def notice(*args)
|
84
|
+
color(35, *args)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Print the specified output in a color indicating information.
|
88
|
+
# If output destination is not a TTY, behaves the same as {#log}.
|
89
|
+
#
|
90
|
+
# @param args [Array<String>]
|
91
|
+
def info(*args)
|
92
|
+
color(36, *args)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Print a blank line.
|
96
|
+
def newline
|
97
|
+
puts('')
|
98
|
+
end
|
99
|
+
|
100
|
+
# Whether this logger is outputting to a TTY.
|
101
|
+
#
|
102
|
+
# @return [true,false]
|
103
|
+
def tty?
|
104
|
+
@out.respond_to?(:tty?) && @out.tty?
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# Print output in the specified color.
|
110
|
+
#
|
111
|
+
# @param code [Integer,String] ANSI color code
|
112
|
+
# @param output [String] output to print
|
113
|
+
# @param newline [Boolean] whether to append a newline
|
114
|
+
def color(code, output, newline = true)
|
115
|
+
puts(color_enabled ? "\033[#{code}m#{output}\033[0m" : output, newline)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module TerraformLandscape
|
4
|
+
class Printer
|
5
|
+
def initialize(output)
|
6
|
+
@output = output
|
7
|
+
end
|
8
|
+
|
9
|
+
def process_stream(io)
|
10
|
+
buffer = StringIO.new
|
11
|
+
begin
|
12
|
+
block_size = 1024
|
13
|
+
|
14
|
+
done = false
|
15
|
+
until done
|
16
|
+
readable_fds, = IO.select([io])
|
17
|
+
next unless readable_fds
|
18
|
+
|
19
|
+
readable_fds.each do |f|
|
20
|
+
begin
|
21
|
+
buffer << f.read_nonblock(block_size)
|
22
|
+
rescue IO::WaitReadable # rubocop:disable Lint/HandleExceptions
|
23
|
+
# Ignore; we'll call IO.select again
|
24
|
+
rescue EOFError
|
25
|
+
done = true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
ensure
|
30
|
+
io.close
|
31
|
+
end
|
32
|
+
|
33
|
+
plan_output = buffer.string
|
34
|
+
scrubbed_output = plan_output.gsub(/\e\[\d+m/, '')
|
35
|
+
|
36
|
+
# Remove preface
|
37
|
+
if (match = scrubbed_output.match(/^Path:[^\n]+/))
|
38
|
+
scrubbed_output = scrubbed_output[match.end(0)..-1]
|
39
|
+
else
|
40
|
+
raise ParseError, 'Output does not contain proper preface'
|
41
|
+
end
|
42
|
+
|
43
|
+
# Remove postface
|
44
|
+
if (match = scrubbed_output.match(/^Plan:[^\n]+/))
|
45
|
+
plan_summary = scrubbed_output[match.begin(0)..match.end(0)]
|
46
|
+
scrubbed_output = scrubbed_output[0...match.begin(0)]
|
47
|
+
else
|
48
|
+
raise ParseError, 'Output does not container proper postface'
|
49
|
+
end
|
50
|
+
|
51
|
+
plan = TerraformPlan.from_output(scrubbed_output)
|
52
|
+
plan.display(@output)
|
53
|
+
@output.puts plan_summary
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
require 'diffy'
|
3
|
+
require 'json'
|
4
|
+
require 'treetop'
|
5
|
+
|
6
|
+
########################################################################
|
7
|
+
# Represents the parsed output of `terraform plan`.
|
8
|
+
#
|
9
|
+
# This allows us to easily inspect the plan and present a more readable
|
10
|
+
# explanation of the plan to the user.
|
11
|
+
########################################################################
|
12
|
+
class TerraformLandscape::TerraformPlan
|
13
|
+
GRAMMAR_FILE = File.expand_path(File.join(File.dirname(__FILE__),
|
14
|
+
'..', '..', 'grammar',
|
15
|
+
'terraform_plan.treetop'))
|
16
|
+
|
17
|
+
CHANGE_SYMBOL_TO_COLOR = {
|
18
|
+
:~ => :yellow,
|
19
|
+
:- => :red,
|
20
|
+
:+ => :green
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
DEFAULT_DIFF_CONTEXT_LINES = 5
|
24
|
+
|
25
|
+
class ParseError < StandardError; end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def from_output(string)
|
29
|
+
return new([]) if string.strip.empty?
|
30
|
+
tree = parser.parse(string)
|
31
|
+
raise ParseError, parser.failure_reason unless tree
|
32
|
+
new(tree.to_ast)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def parser
|
38
|
+
@parser ||=
|
39
|
+
begin
|
40
|
+
Treetop.load(GRAMMAR_FILE)
|
41
|
+
TerraformPlanParser.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Create a plan from an abstract syntax tree (AST).
|
47
|
+
def initialize(plan_ast, options = {})
|
48
|
+
@ast = plan_ast
|
49
|
+
@diff_context_lines = options.fetch(:diff_context_lines, DEFAULT_DIFF_CONTEXT_LINES)
|
50
|
+
end
|
51
|
+
|
52
|
+
def display(output)
|
53
|
+
@out = output
|
54
|
+
@ast.each do |resource|
|
55
|
+
display_resource(resource)
|
56
|
+
@out.newline
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def display_resource(resource)
|
63
|
+
change_color = CHANGE_SYMBOL_TO_COLOR[resource[:change]]
|
64
|
+
|
65
|
+
@out.puts "#{resource[:change]} #{resource[:resource_type]}." \
|
66
|
+
"#{resource[:resource_name]}".colorize(change_color)
|
67
|
+
|
68
|
+
# Determine longest attribute name so we align all values at same indentation
|
69
|
+
attribute_value_indent_amount = attribute_indent_amount_for_resource(resource)
|
70
|
+
|
71
|
+
resource[:attributes].each do |attribute_name, attribute_value|
|
72
|
+
display_attribute(resource,
|
73
|
+
change_color,
|
74
|
+
attribute_name,
|
75
|
+
attribute_value,
|
76
|
+
attribute_value_indent_amount)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def attribute_indent_amount_for_resource(resource)
|
81
|
+
longest_name_length = resource[:attributes].keys.reduce(0) do |longest, name|
|
82
|
+
name.length > longest ? name.length : longest
|
83
|
+
end
|
84
|
+
longest_name_length + 8
|
85
|
+
end
|
86
|
+
|
87
|
+
def json?(value)
|
88
|
+
['{', '['].include?(value.to_s[0]) &&
|
89
|
+
(JSON.parse(value) rescue nil) # rubocop:disable Style/RescueModifier
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_pretty_json(value)
|
93
|
+
JSON.pretty_generate(JSON.parse(value),
|
94
|
+
{
|
95
|
+
indent: ' ',
|
96
|
+
space: ' ',
|
97
|
+
object_nl: "\n",
|
98
|
+
array_nl: "\n"
|
99
|
+
})
|
100
|
+
end
|
101
|
+
|
102
|
+
def display_diff(old, new, indent)
|
103
|
+
@out.print Diffy::Diff.new(old, new, { context: @diff_context_lines })
|
104
|
+
.to_s(String.disable_colorization ? :text : :color)
|
105
|
+
.gsub("\n", "\n" + indent)
|
106
|
+
.strip
|
107
|
+
end
|
108
|
+
|
109
|
+
def display_attribute(
|
110
|
+
resource,
|
111
|
+
change_color,
|
112
|
+
attribute_name,
|
113
|
+
attribute_value,
|
114
|
+
attribute_value_indent_amount
|
115
|
+
)
|
116
|
+
attribute_value_indent = ' ' * attribute_value_indent_amount
|
117
|
+
|
118
|
+
if resource[:change] == :~
|
119
|
+
display_modified_attribute(change_color,
|
120
|
+
attribute_name,
|
121
|
+
attribute_value,
|
122
|
+
attribute_value_indent,
|
123
|
+
attribute_value_indent_amount)
|
124
|
+
else
|
125
|
+
display_added_or_removed_attribute(change_color,
|
126
|
+
attribute_name,
|
127
|
+
attribute_value,
|
128
|
+
attribute_value_indent,
|
129
|
+
attribute_value_indent_amount)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def display_modified_attribute( # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
134
|
+
change_color,
|
135
|
+
attribute_name,
|
136
|
+
attribute_value,
|
137
|
+
attribute_value_indent,
|
138
|
+
attribute_value_indent_amount
|
139
|
+
)
|
140
|
+
# Since the attribute line is always of the form
|
141
|
+
# "old value" => "new value", we can add curly braces and parse with
|
142
|
+
# `eval` to obtain a hash with a single key/value.
|
143
|
+
old, new = eval("{#{attribute_value}}").to_a.first # rubocop:disable Lint/Eval
|
144
|
+
|
145
|
+
return if old == new # Don't show unchanged attributes
|
146
|
+
|
147
|
+
@out.print " #{attribute_name}:".ljust(attribute_value_indent_amount, ' ')
|
148
|
+
.colorize(change_color)
|
149
|
+
|
150
|
+
if json?(new)
|
151
|
+
# Value looks like JSON, so prettify it to make it more readable
|
152
|
+
fancy_old = "#{to_pretty_json(old)}\n"
|
153
|
+
fancy_new = "#{to_pretty_json(new)}\n"
|
154
|
+
display_diff(fancy_old, fancy_new, attribute_value_indent)
|
155
|
+
elsif old.include?("\n") || new.include?("\n")
|
156
|
+
# Multiline content, so display nicer diff
|
157
|
+
display_diff("#{old}\n", "#{new}\n", attribute_value_indent)
|
158
|
+
else
|
159
|
+
# Typical values, so just show before/after
|
160
|
+
@out.print '"' + old.colorize(:red) + '"'
|
161
|
+
@out.print ' => '.colorize(:light_black)
|
162
|
+
@out.print '"' + new.colorize(:green) + '"'
|
163
|
+
end
|
164
|
+
|
165
|
+
@out.newline
|
166
|
+
end
|
167
|
+
|
168
|
+
def display_added_or_removed_attribute(
|
169
|
+
change_color,
|
170
|
+
attribute_name,
|
171
|
+
attribute_value,
|
172
|
+
attribute_value_indent,
|
173
|
+
attribute_value_indent_amount
|
174
|
+
)
|
175
|
+
@out.print " #{attribute_name}:".ljust(attribute_value_indent_amount, ' ')
|
176
|
+
.colorize(change_color)
|
177
|
+
|
178
|
+
evaluated_string = eval(attribute_value) # rubocop:disable Lint/Eval
|
179
|
+
if json?(evaluated_string)
|
180
|
+
@out.print to_pretty_json(evaluated_string).gsub("\n", "\n" + attribute_value_indent)
|
181
|
+
.colorize(change_color)
|
182
|
+
else
|
183
|
+
@out.print "\"#{evaluated_string.colorize(change_color)}\""
|
184
|
+
end
|
185
|
+
|
186
|
+
@out.newline
|
187
|
+
end
|
188
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: terraform_landscape
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Coinbase
|
8
|
+
- Shane da Silva
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-02-28 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: colorize
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0.7'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0.7'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: commander
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '4.4'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '4.4'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: diffy
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: treetop
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.6'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.6'
|
70
|
+
description: Improve output of Terraform plans with color and indentation
|
71
|
+
email:
|
72
|
+
- shane@coinbase.com
|
73
|
+
executables:
|
74
|
+
- landscape
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- bin/landscape
|
79
|
+
- grammar/terraform_plan.treetop
|
80
|
+
- lib/terraform_landscape.rb
|
81
|
+
- lib/terraform_landscape/arguments_parser.rb
|
82
|
+
- lib/terraform_landscape/cli.rb
|
83
|
+
- lib/terraform_landscape/constants.rb
|
84
|
+
- lib/terraform_landscape/errors.rb
|
85
|
+
- lib/terraform_landscape/output.rb
|
86
|
+
- lib/terraform_landscape/printer.rb
|
87
|
+
- lib/terraform_landscape/terraform_plan.rb
|
88
|
+
- lib/terraform_landscape/version.rb
|
89
|
+
homepage: https://github.com/coinbase/terraform_landscape
|
90
|
+
licenses:
|
91
|
+
- Apache 2.0
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '2'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.6.10
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: Pretty-print Terraform plan output
|
113
|
+
test_files: []
|