awx 0.1.0 → 0.2.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/lib/aws/aws_cache.rb +53 -0
- data/lib/aws/aws_cli.rb +166 -0
- data/lib/aws/aws_cloudformation.rb +68 -0
- data/lib/aws/aws_config.rb +39 -0
- data/lib/aws/aws_credentials.rb +9 -0
- data/lib/aws/aws_outputter.rb +247 -0
- data/lib/aws/aws_reports.rb +271 -0
- data/lib/aws/aws_validator.rb +30 -0
- data/lib/awx.rb +65 -71
- data/lib/core/config.rb +127 -0
- data/lib/core/config_unique.rb +64 -0
- data/lib/core/opt.rb +17 -0
- data/lib/routes/aws_cloudformation_create.rb +732 -0
- data/lib/routes/aws_cloudformation_delete.rb +37 -0
- data/lib/routes/aws_cloudformation_detect_drift.rb +44 -0
- data/lib/routes/aws_lambda.rb +122 -0
- data/lib/routes/aws_list.rb +234 -0
- data/lib/routes/setup.rb +31 -0
- data/lib/version.rb +1 -1
- data/opt/yml/aws-reports.yml +113 -0
- metadata +28 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a22d18dbf189673739f5963a622151a85e814a1e
|
4
|
+
data.tar.gz: 8b61eee5e62db727d1c105df70588e2fa0209434
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 21cc5951d290166128b6eaa29b25de3ed4206744625013c2e420996827b47a7bfdfef276a0c5e2dffdba822e2ee353e95ba1115490c895099e29af3a34ace8fa
|
7
|
+
data.tar.gz: c9723e5ca2518609c52228387e2886ae85a272beed50a9392acd8953b8aa0972452a861ee5f931820fabe25ca39106e4ba0f5ecd39893ba93706dd3ec1cf238c
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module App
|
2
|
+
|
3
|
+
class CacheKey
|
4
|
+
|
5
|
+
ARRAY_AWS_REGIONS = 'aws_regions'
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
class Cache
|
10
|
+
|
11
|
+
# Stores a string representation of a Array/Hash/String in the /tmp directory using a unique key + timestamp.
|
12
|
+
# Key is the unique key used for retrieving it -- stored as constant in class above ^ App::CacheKey
|
13
|
+
# Data can be either anything. It will simply be stored as object.inspect.
|
14
|
+
# Expire hours is the amount of hours this Hash should be stored for.
|
15
|
+
# @return void
|
16
|
+
def self.store(key, data, expire_hours = nil)
|
17
|
+
|
18
|
+
# TODO
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns either the data or nil.
|
23
|
+
# Nil will be returned in 2 cases, the key doesn't exist or the data has expired.
|
24
|
+
# @return Object|nil
|
25
|
+
def self.get(key)
|
26
|
+
|
27
|
+
# TODO
|
28
|
+
|
29
|
+
nil
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
# Checks if a key exists but doesn't actually parse it.
|
34
|
+
# @return boolean
|
35
|
+
def self.exists?(key)
|
36
|
+
|
37
|
+
# TODO
|
38
|
+
|
39
|
+
false
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# Invalidate a cache key. Does nothing if key doesn't exist.
|
44
|
+
# @return void
|
45
|
+
def self.invalidate(key)
|
46
|
+
|
47
|
+
# TODO
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/lib/aws/aws_cli.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module App
|
5
|
+
|
6
|
+
class AWSCli
|
7
|
+
|
8
|
+
BLANK = 'BLANK'
|
9
|
+
REGION_SKIP = 'E+e^P7p?KYWvc~R/' # Must provide this to skip region (so it can't easily be done accidentally)
|
10
|
+
|
11
|
+
@@valid_regions = BLANK
|
12
|
+
@@ec2_instances = {}
|
13
|
+
@@ec2_volumes = {}
|
14
|
+
@@ec2_security_groups = {}
|
15
|
+
@@s3_buckets = {}
|
16
|
+
@@rds_mysql_instances = {}
|
17
|
+
@@sqs_queues = {}
|
18
|
+
@@cloudformation_stacks = {}
|
19
|
+
|
20
|
+
# @return void
|
21
|
+
def self.get_regions
|
22
|
+
|
23
|
+
# TODO - Remove the following line(s) and add actual caching...
|
24
|
+
|
25
|
+
@@valid_regions = %w(ap-south-1 eu-west-3 eu-west-2 eu-west-1 ap-northeast-2 ap-northeast-1 sa-east-1 ca-central-1 ap-southeast-1 ap-southeast-2 eu-central-1 us-east-1 us-east-2 us-west-1 us-west-2)
|
26
|
+
@@valid_regions = %w(us-east-1 us-east-2 us-west-1 us-west-2)
|
27
|
+
|
28
|
+
if @@valid_regions == BLANK
|
29
|
+
@@valid_regions = []
|
30
|
+
regions = execute('ec2 describe-regions', nil)
|
31
|
+
regions['Regions'].each do |region|
|
32
|
+
@@valid_regions << region['RegionName']
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
@@valid_regions
|
37
|
+
end
|
38
|
+
|
39
|
+
# Perform S3 upload. Will overwrite existing file (if exists)!
|
40
|
+
# @return Array
|
41
|
+
def self.s3_upload(source, bucket_name, bucket_path)
|
42
|
+
Blufin::Terminal::error("Source does not exist: #{source}", true) unless Blufin::Files::path_exists(source) || Blufin::Files::file_exists(source)
|
43
|
+
source = File.expand_path(source)
|
44
|
+
command = "s3 cp #{source} s3://#{bucket_name}/#{bucket_path}"
|
45
|
+
execute_as_proc("Uploading Template to S3: #{Blufin::Terminal::format_highlight("s3://#{bucket_name}/#{bucket_path}")}", command, REGION_SKIP)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Perform S3 sync.
|
49
|
+
# @return Array
|
50
|
+
def self.s3_sync(source, bucket_name, bucket_path, delete: false)
|
51
|
+
|
52
|
+
# TODO - This was written but is untested! Remove this comment once this method has been used successfully.
|
53
|
+
|
54
|
+
Blufin::Terminal::error("Source does not exist: #{source}", true) unless Blufin::Files::path_exists(source) || Blufin::Files::file_exists(source)
|
55
|
+
source = File.expand_path(source)
|
56
|
+
command = "s3 sync #{source} s3://#{bucket_name}/#{bucket_path}#{delete ? ' --delete' : ''}"
|
57
|
+
execute(command, REGION_SKIP, json: false)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Validate a CloudFormation Stack.
|
61
|
+
# @return Hash
|
62
|
+
def self.cloudformation_stack_validate(region, s3_url)
|
63
|
+
command = "cloudformation validate-template --template-url #{s3_url}"
|
64
|
+
execute_as_proc('Validating Template', command, region, json: true)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Create a CloudFormation Stack.
|
68
|
+
# @return Hash
|
69
|
+
def self.cloudformation_stack_create(region, stack_name, s3_url, params: nil, tags: nil, capabilities: [])
|
70
|
+
params = params.nil? ? '' : " --parameters '#{params.gsub("'", "\\'")}'"
|
71
|
+
tags = tags.nil? ? '' : " --tags '#{tags.gsub("'", "\\'")}'"
|
72
|
+
capabilities = !capabilities.any? ? '' : " --capabilities #{capabilities.join(' ')}"
|
73
|
+
command = "cloudformation create-stack --stack-name #{stack_name} --template-url #{s3_url}#{params}#{tags}#{capabilities}"
|
74
|
+
stack_send = execute_as_proc("Sending CloudFormation Stack: #{Blufin::Terminal::format_highlight(stack_name)}", command, region, json: true)
|
75
|
+
begin
|
76
|
+
if stack_send.has_key?('StackId')
|
77
|
+
Blufin::Terminal::success('Template was accepted by AWS.', "\x1B[38;5;240mStackId \xe2\x80\x94 \x1B[38;5;40m#{stack_send['StackId']}")
|
78
|
+
system("/usr/bin/open -a '/Applications/Google Chrome.app' https://#{region}.console.aws.amazon.com/cloudformation/home?region=#{region}#/stack/detail?stackId=#{URI::encode(stack_send['StackId'])}")
|
79
|
+
else
|
80
|
+
Blufin::Terminal::error("Key not found in response: #{Blufin::Terminal::format_invalid('StackId')}", stack_send.inspect, true)
|
81
|
+
end
|
82
|
+
rescue
|
83
|
+
Blufin::Terminal::error('Stack was not accepted by AWS.', App::AWSCli::format_cli_error(stack_send), true)
|
84
|
+
end
|
85
|
+
stack_complete = execute_as_proc("Creating CloudFormation Stack: #{Blufin::Terminal::format_highlight(stack_name)}", "cloudformation wait stack-create-complete --stack-name #{stack_name}", region, json: true)
|
86
|
+
if stack_complete.to_s.strip == ''
|
87
|
+
puts
|
88
|
+
puts " \x1B[38;5;40mCREATE_COMPLETE\x1B[0m"
|
89
|
+
puts
|
90
|
+
stack_details = execute_as_proc("Getting CloudFormation Stack: #{Blufin::Terminal::format_highlight(stack_name)}", "cloudformation describe-stacks --stack-name #{stack_name}", region, json: true)
|
91
|
+
begin
|
92
|
+
stack_json = JSON.parse(stack_details.to_json)
|
93
|
+
raise RuntimeError, 'Not a Hash' unless stack_json.is_a?(Hash)
|
94
|
+
puts
|
95
|
+
Blufin::Terminal::code_highlight(stack_json.to_yaml, 'yml', 4)
|
96
|
+
rescue
|
97
|
+
Blufin::Terminal::error('It seems you Stack was created (but then something went wrong).', stack_details.inspect, false)
|
98
|
+
end
|
99
|
+
else
|
100
|
+
Blufin::Terminal::error("Something went wrong. Go to: #{Blufin::Terminal::format_highlight('https://us-west-2.console.aws.amazon.com/cloudformation/home')} to see what happened.", format_cli_error(stack_complete.to_s), true)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Use this when a common such as "aws do-something" returns an error (such as: Stack [cognito-public-pool-go-eat-local-albert] already exists).
|
105
|
+
# This removes the generic error prefix and preceding blank line.
|
106
|
+
# @return string
|
107
|
+
def self.format_cli_error(error_string)
|
108
|
+
error = ''
|
109
|
+
error_string.split("\n").each do |line|
|
110
|
+
if line.strip.length > 0
|
111
|
+
error = "#{error}#{line.gsub(/(An error occurred \(.*\) when calling the [A-Za-z0-9]+ operation:)\s*/, '')}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
error
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# Helper method to execute aws-cli commands.
|
120
|
+
# @return Object
|
121
|
+
def self.execute(command, region, json: true, verbose: true)
|
122
|
+
raise RuntimeError, 'Must provide region.' if region.nil? || region.strip == ''
|
123
|
+
raise RuntimeError, "Invalid region: #{region}" unless get_regions.include?(region) || region == REGION_SKIP
|
124
|
+
begin
|
125
|
+
region = region == REGION_SKIP ? '' : " --region #{region}"
|
126
|
+
aws_credentials = App::AWSConfig.get_credentials(App::AWSConfig::AWS_PROFILE_ALBERT_CLI)
|
127
|
+
region = aws_credentials.region if region.nil?
|
128
|
+
puts "\x1B[38;5;70m$ \x1B[38;5;240maws #{command}#{region}\x1B[0m" if verbose
|
129
|
+
if json
|
130
|
+
result = `aws #{command}#{region} 2>/tmp/execute-output-aws`
|
131
|
+
# If no JSON, simply return the output.
|
132
|
+
return `cat /tmp/execute-output-aws` if result == ''
|
133
|
+
begin
|
134
|
+
return JSON.parse(result)
|
135
|
+
rescue => e
|
136
|
+
raise RuntimeError, "JSON Parsing Failed:\n\n#{e.message}"
|
137
|
+
end
|
138
|
+
else
|
139
|
+
result = system("aws #{command}#{region} &>/dev/null")
|
140
|
+
raise RuntimeError, 'This command this not return a status code of 0 (Success).' unless result
|
141
|
+
return result
|
142
|
+
end
|
143
|
+
rescue => e
|
144
|
+
Blufin::Terminal::error("Command Failed: #{Blufin::Terminal::format_command("aws #{command}#{region}")}", e.message, true)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Used when running commands showing the spinner.
|
149
|
+
# Set JSON to TRUE if you expect JSON output back, otherwise return value will be boolean (TRUE if successful).
|
150
|
+
# Highly recommended to keep verbose set to FALSE.
|
151
|
+
# @return void
|
152
|
+
def self.execute_as_proc(title, command, region, json: false, verbose: false, catch_exception: false)
|
153
|
+
output = nil
|
154
|
+
threads = []
|
155
|
+
threads << Thread.new {
|
156
|
+
output = execute(command, region, json: json, verbose: verbose)
|
157
|
+
}
|
158
|
+
sleep(0.1)
|
159
|
+
puts if verbose
|
160
|
+
Blufin::Terminal::execute_proc("AWS - #{title}", Proc.new { threads.each { |thread| thread.join } })
|
161
|
+
output
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module App
|
2
|
+
|
3
|
+
class AWSCloudFormation
|
4
|
+
|
5
|
+
S3_BUCKET_CLOUDFORMATION = 'blufin-cloudformation-templates'
|
6
|
+
S3_BUCKET_CLOUDFORMATION_REGION = 'us-west-2'
|
7
|
+
PATH_CLOUDFORMATION = 'aws-cloudformation'
|
8
|
+
FILE_CLOUDFORMATION_DEFAULTS = 'cloudformation.rb'
|
9
|
+
|
10
|
+
# Gets the path to the root of the cloudformation templates in blufin-secrets repo.
|
11
|
+
# @return string
|
12
|
+
def self.get_cloudformation_path(path = PATH_CLOUDFORMATION)
|
13
|
+
path = "#{App::Config.param(ConfigUnique::PATH_TO_REPO_SECRETS)}/#{path}"
|
14
|
+
Blufin::Terminal::error("CloudFormation path not found: #{Blufin::Terminal::format_directory(path)}", 'This probably means directories got renamed or code got changed.', true) unless Blufin::Files::path_exists(path)
|
15
|
+
path
|
16
|
+
end
|
17
|
+
|
18
|
+
# Uploads a cloudformation template to S3 (so we can create a stack from it).
|
19
|
+
# Returns the S3 URL.
|
20
|
+
# @return string
|
21
|
+
def self.upload_cloudformation_template(template_category, template_name, description)
|
22
|
+
source = "#{get_cloudformation_path}/#{template_category}/#{template_name}/template.yml"
|
23
|
+
raise RuntimeError, "File does not exist: #{source}" unless Blufin::Files::file_exists(source)
|
24
|
+
yml_data = YAML.load_file(File.expand_path(source))
|
25
|
+
yml_data['Description'] = description
|
26
|
+
description_written = false
|
27
|
+
new_yaml = {}
|
28
|
+
yml_data.keys.each do |key|
|
29
|
+
if %w(AWSTemplateFormatVersion).include?(key)
|
30
|
+
new_yaml[key] = yml_data[key]
|
31
|
+
else
|
32
|
+
unless description_written
|
33
|
+
description_written = true
|
34
|
+
new_yaml['Description'] = description
|
35
|
+
end
|
36
|
+
new_yaml[key] = yml_data[key] unless key == 'Description'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
tmp_file = "/tmp/aws-cf-upload-#{template_category}-#{template_name}.yml"
|
40
|
+
Blufin::Files::write_file(tmp_file, convert_string_to_line_array(new_yaml.to_yaml))
|
41
|
+
App::AWSCli::s3_upload(tmp_file, S3_BUCKET_CLOUDFORMATION, get_template_filename(template_category, template_name))
|
42
|
+
end
|
43
|
+
|
44
|
+
# Gets the S3 bucket URL.
|
45
|
+
# @return string
|
46
|
+
def self.get_cloudformation_s3_bucket_url(template_category, template_name)
|
47
|
+
"https://#{S3_BUCKET_CLOUDFORMATION}.s3-#{S3_BUCKET_CLOUDFORMATION_REGION}.amazonaws.com/#{get_template_filename(template_category, template_name)}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Converts cognito/private-pool to -> cognito-private-pool.yml
|
51
|
+
# @return string
|
52
|
+
def self.get_template_filename(template_category, template_name)
|
53
|
+
"#{template_category}-#{template_name}.yml"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Converts a string to an Array of lines to a string.
|
57
|
+
# @return String
|
58
|
+
# TODO - Must move Blufin::YmlCommon from blufin -> blufin-lib. Not sure what the damage will be... 07/08/2019.
|
59
|
+
def self.convert_string_to_line_array(string)
|
60
|
+
raise RuntimeError, "Expected String, instead got: #{string.class}" unless string.is_a?(String)
|
61
|
+
array_of_lines = []
|
62
|
+
string.split("\n").each { |line| array_of_lines << line.gsub("\n", '') }
|
63
|
+
array_of_lines
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module App
|
2
|
+
|
3
|
+
class AWSConfig
|
4
|
+
|
5
|
+
BLANK = '[BLANK]'
|
6
|
+
|
7
|
+
AWS_PROFILE_ALBERT_CLI = 'albert-cli'
|
8
|
+
|
9
|
+
FILE_AWS_CONFIG = File.expand_path('~/.aws/config')
|
10
|
+
FILE_AWS_CREDENTIALS = File.expand_path('~/.aws/credentials')
|
11
|
+
|
12
|
+
@@aws_credentials = BLANK
|
13
|
+
|
14
|
+
# Gets AWS credentials from ~/.aws directory for given profile.
|
15
|
+
# If credentials don't exist (or are missing information) -- nil is returned.
|
16
|
+
def self.get_credentials(profile = 'default')
|
17
|
+
if @@aws_credentials == BLANK
|
18
|
+
return nil unless Blufin::Files::file_exists(FILE_AWS_CONFIG)
|
19
|
+
return nil unless Blufin::Files::file_exists(FILE_AWS_CREDENTIALS)
|
20
|
+
config = ParseConfig.new(FILE_AWS_CONFIG)
|
21
|
+
credentials = ParseConfig.new(FILE_AWS_CREDENTIALS)
|
22
|
+
@@aws_credentials = nil
|
23
|
+
if !config.params[profile].nil? && !credentials.params[profile].nil?
|
24
|
+
if !config.params[profile]['region'].nil? && !config.params[profile]['output'].nil? && !credentials.params[profile]['aws_access_key_id'].nil? && !credentials.params[profile]['aws_secret_access_key'].nil?
|
25
|
+
aws_credentials = App::AWSCredentials.new
|
26
|
+
aws_credentials.region = config.params[profile]['region']
|
27
|
+
aws_credentials.output = config.params[profile]['output']
|
28
|
+
aws_credentials.aws_key = config.params[profile]['aws_access_key_id']
|
29
|
+
aws_credentials.aws_secret = config.params[profile]['aws_secret_access_key']
|
30
|
+
@@aws_credentials = aws_credentials
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@@aws_credentials
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'columnist'
|
3
|
+
|
4
|
+
module App
|
5
|
+
|
6
|
+
class AWSOutputter
|
7
|
+
|
8
|
+
EC2_INSTANCES = 'ec2_instances'
|
9
|
+
EC2_VOLUMES = 'ec2_volumes'
|
10
|
+
EC2_SECURITY_GROUPS = 'ec2_security_groups'
|
11
|
+
S3_BUCKETS = 's3_buckets'
|
12
|
+
RDS_MYSQL_INSTANCES = 'rds_mysql_instances'
|
13
|
+
SQS_QUEUES = 'sqs_queues'
|
14
|
+
|
15
|
+
HEADER_COLOR = 255
|
16
|
+
DIVIDER_COLOR = 240
|
17
|
+
DIM_COLOR = 246
|
18
|
+
CONSTRAINT_COLOR = 67
|
19
|
+
|
20
|
+
REGION_WIDTH = 15
|
21
|
+
|
22
|
+
extend Columnist
|
23
|
+
|
24
|
+
# Outputs EC2 data to terminal.
|
25
|
+
# @return void
|
26
|
+
def self.display(resource_title, resource, column_data, output, total_width, opts, args)
|
27
|
+
|
28
|
+
output = [] if output.nil?
|
29
|
+
|
30
|
+
@terminal_width = Blufin::Terminal::get_terminal_width
|
31
|
+
|
32
|
+
wildcard_width = @terminal_width - total_width - 8 - (column_data.length)
|
33
|
+
|
34
|
+
puts " \x1B[38;5;255m\x1B[48;5;54m #{output.length} \x1B[48;5;18m #{resource_title} \x1B[0m"
|
35
|
+
puts
|
36
|
+
table(:border => false) do
|
37
|
+
|
38
|
+
row do
|
39
|
+
column('', :width => 2, :color => HEADER_COLOR)
|
40
|
+
column_data.each do |title, data|
|
41
|
+
column(title, :width => get_width(data[:width], wildcard_width), :color => HEADER_COLOR)
|
42
|
+
end
|
43
|
+
column('', :width => 2, :color => HEADER_COLOR)
|
44
|
+
end
|
45
|
+
|
46
|
+
row do
|
47
|
+
column('')
|
48
|
+
column_data.each do |title, data|
|
49
|
+
column('-' * get_width(data[:width], wildcard_width), :color => DIVIDER_COLOR)
|
50
|
+
end
|
51
|
+
column('')
|
52
|
+
end
|
53
|
+
|
54
|
+
output.each do |item|
|
55
|
+
row do
|
56
|
+
column('')
|
57
|
+
column_data.each do |title, data|
|
58
|
+
color = data[:color].nil? ? 'default' : data[:color]
|
59
|
+
value = get_value(data[:key], item)
|
60
|
+
value, color = get_formatter(data[:formatter], resource: resource, color: color).call(value) unless data[:formatter].nil?
|
61
|
+
column(value, :color => get_color(color))
|
62
|
+
end
|
63
|
+
column('')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
puts
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
# Displays CF parameters in a table (currently during the CF intro screen).
|
73
|
+
# @return void
|
74
|
+
def self.display_parameters(table_data)
|
75
|
+
raise RuntimeError, "Expected Array, instead got: #{table_data.class}" unless table_data.is_a?(Array)
|
76
|
+
return unless table_data.any?
|
77
|
+
# Must match widths below (minus remaining).
|
78
|
+
tw = Blufin::Terminal::get_terminal_width
|
79
|
+
column_short = 5
|
80
|
+
column_widths = [32, 15, 75, 25, column_short, column_short, column_short, column_short]
|
81
|
+
remaining = tw - column_widths.inject(0) { |sum, x| sum + x } - 16
|
82
|
+
column_widths << remaining
|
83
|
+
table(:border => false) do
|
84
|
+
row do
|
85
|
+
column('', :width => 2, :color => HEADER_COLOR)
|
86
|
+
column('Parameter', :width => 32, :color => HEADER_COLOR)
|
87
|
+
column('Type', :width => 15, :color => HEADER_COLOR)
|
88
|
+
column('Description', :width => 75, :color => HEADER_COLOR)
|
89
|
+
column('AllowedPattern', :width => 25, :color => HEADER_COLOR)
|
90
|
+
column('Min-L', :width => column_short, :color => HEADER_COLOR)
|
91
|
+
column('Max-L', :width => column_short, :color => HEADER_COLOR)
|
92
|
+
column('Min-V', :width => column_short, :color => HEADER_COLOR)
|
93
|
+
column('Max-V', :width => column_short, :color => HEADER_COLOR)
|
94
|
+
column('Cached Value(s)', :width => remaining, :color => HEADER_COLOR)
|
95
|
+
column('', :width => 2, :color => HEADER_COLOR)
|
96
|
+
end
|
97
|
+
row do
|
98
|
+
column('')
|
99
|
+
column_widths.each do |width|
|
100
|
+
column('-' * width, :color => DIVIDER_COLOR)
|
101
|
+
end
|
102
|
+
column('')
|
103
|
+
end
|
104
|
+
table_data.each do |data|
|
105
|
+
alp = data[:allowed_pattern]
|
106
|
+
mil = data[:min_length]
|
107
|
+
miv = data[:min_value]
|
108
|
+
mal = data[:max_length]
|
109
|
+
mav = data[:max_value]
|
110
|
+
cop = 'light-green'
|
111
|
+
cop = 'purple' if data[:type_internal] == :autocomplete
|
112
|
+
cop = 'yellow' if data[:type_internal] == :system || [AppCommand::AWSCloudFormationCreate::OPTION_STACK_NAME, AppCommand::AWSCloudFormationCreate::OPTION_DESCRIPTION].include?(data[:parameter])
|
113
|
+
# In case you want to change color in future, currently this is redundant.
|
114
|
+
cod = data[:type_internal] == :system ? 'default' : 'default'
|
115
|
+
cot = data[:type_internal] == :system ? 'default' : 'default'
|
116
|
+
cot = data[:type] == AppCommand::AWSCloudFormationCreate::SPECIAL ? 'light-grey' : cot
|
117
|
+
row do
|
118
|
+
column('')
|
119
|
+
column(data[:parameter], :color => get_color(cop))
|
120
|
+
column(data[:type], :color => get_color(cot))
|
121
|
+
column(data[:description], :color => get_color(cod))
|
122
|
+
column(alp.nil? ? "\xe2\x80\x94" : alp, :color => alp.nil? ? get_color('default') : CONSTRAINT_COLOR)
|
123
|
+
column(mil.nil? ? "\xe2\x80\x94".rjust(column_short, ' ') : mil.to_s.rjust(column_short, ' '), :color => mil.nil? ? get_color('default') : CONSTRAINT_COLOR)
|
124
|
+
column(mal.nil? ? "\xe2\x80\x94".rjust(column_short, ' ') : mal.to_s.rjust(column_short, ' '), :color => mal.nil? ? get_color('default') : CONSTRAINT_COLOR)
|
125
|
+
column(miv.nil? ? "\xe2\x80\x94".rjust(column_short, ' ') : miv.to_s.rjust(column_short, ' '), :color => miv.nil? ? get_color('default') : CONSTRAINT_COLOR)
|
126
|
+
column(mav.nil? ? "\xe2\x80\x94".rjust(column_short, ' ') : mav.to_s.rjust(column_short, ' '), :color => mav.nil? ? get_color('default') : CONSTRAINT_COLOR)
|
127
|
+
column(data[:cached_value], :color => get_color('green'))
|
128
|
+
column('')
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# Recursively get nested keys. Does not handle Arrays.
|
137
|
+
# @return string
|
138
|
+
def self.get_value(key, item)
|
139
|
+
return '—' if item.nil? || !item.is_a?(Hash)
|
140
|
+
ks = key.split('.')
|
141
|
+
fk = ks[0]
|
142
|
+
ks.shift
|
143
|
+
if ks.length == 0
|
144
|
+
return '—' unless item.has_key?(key) # If key not found, return mdash.
|
145
|
+
item[fk]
|
146
|
+
else
|
147
|
+
get_value(ks.join('.'), item[fk])
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Define all custom formatters here.
|
152
|
+
# @return void
|
153
|
+
def self.get_formatter(key = nil, resource: nil, color: 'default')
|
154
|
+
formatters = {
|
155
|
+
'cloudfront-status' => Proc.new { |n|
|
156
|
+
case n
|
157
|
+
when 'Deployed'
|
158
|
+
color = 'green'
|
159
|
+
else
|
160
|
+
Blufin::Terminal::output("Add something to aws_outputter switch statement for: #{n}", Blufin::Terminal::MSG_TODO)
|
161
|
+
color = 'default'
|
162
|
+
end
|
163
|
+
[n, color]
|
164
|
+
},
|
165
|
+
'ec2-security-groups' => Proc.new { |n|
|
166
|
+
security_groups = []
|
167
|
+
if n.any?
|
168
|
+
n.each do |security_group|
|
169
|
+
group_name = security_group['GroupName']
|
170
|
+
group_name = 'N/A' if group_name.strip == '' || group_name.nil?
|
171
|
+
security_groups << group_name
|
172
|
+
end
|
173
|
+
end
|
174
|
+
security_groups.sort!
|
175
|
+
security_group = security_groups.include?('default') ? 'default' : security_groups[0]
|
176
|
+
security_group = "#{security_group}+#{security_groups.length - 1}" if security_groups.length > 1
|
177
|
+
[security_group, 'default']
|
178
|
+
},
|
179
|
+
'ec2-status' => Proc.new { |n|
|
180
|
+
case n
|
181
|
+
when 'pending'
|
182
|
+
color = 'orange'
|
183
|
+
when 'running'
|
184
|
+
color = 'green'
|
185
|
+
when 'in-use'
|
186
|
+
color = 'green'
|
187
|
+
when 'stopped'
|
188
|
+
color = 'dark-grey'
|
189
|
+
when 'terminated'
|
190
|
+
color = 'red'
|
191
|
+
else
|
192
|
+
Blufin::Terminal::output("Add something to aws_outputter switch statement for: #{n}", Blufin::Terminal::MSG_TODO)
|
193
|
+
color = 'default'
|
194
|
+
end
|
195
|
+
[n, color]
|
196
|
+
},
|
197
|
+
'epoch-date' => Proc.new { |n|
|
198
|
+
[DateTime.strptime(n.to_s, '%s'), color]
|
199
|
+
},
|
200
|
+
'region' => Proc.new { |n|
|
201
|
+
preferred_regions = resource.nil? ? [] : resource[App::AWSReports::KEY_REGIONS_PREFERRED]
|
202
|
+
region_color = preferred_regions.include?(n) ? 'brown' : 'red'
|
203
|
+
region = n.ljust(10, ' ')
|
204
|
+
[region, region_color]
|
205
|
+
},
|
206
|
+
'route-53-hosted-zone-id' => Proc.new { |n|
|
207
|
+
zone_color = color
|
208
|
+
ns = n.split('/')
|
209
|
+
[ns[ns.length - 1], zone_color]
|
210
|
+
},
|
211
|
+
}
|
212
|
+
raise RuntimeError, "Key not found: #{key}" unless key.nil? || formatters.has_key?(key)
|
213
|
+
return formatters.keys if key.nil?
|
214
|
+
formatters[key]
|
215
|
+
end
|
216
|
+
|
217
|
+
# Define all custom colors here.
|
218
|
+
# @return void
|
219
|
+
def self.get_color(key = nil)
|
220
|
+
colors = {
|
221
|
+
'default' => 240,
|
222
|
+
'red' => 196,
|
223
|
+
'orange' => 202,
|
224
|
+
'yellow' => 220,
|
225
|
+
'green' => 46,
|
226
|
+
'blue' => 39,
|
227
|
+
'purple' => 61,
|
228
|
+
'pink' => 198,
|
229
|
+
'brown' => 94,
|
230
|
+
'light-grey' => 246,
|
231
|
+
'light-green' => 77,
|
232
|
+
'dark-grey' => 240,
|
233
|
+
}
|
234
|
+
raise RuntimeError, "Key not found: #{key}" unless key.nil? || colors.has_key?(key)
|
235
|
+
return colors.keys if key.nil?
|
236
|
+
colors[key]
|
237
|
+
end
|
238
|
+
|
239
|
+
# Determines whether to get width or wildcard width.
|
240
|
+
# @return
|
241
|
+
def self.get_width(width, wildcard_width)
|
242
|
+
(width == '*') ? wildcard_width : width
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|