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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f45efb7c22579d93855b4b979bd61d48b19c5d6d
4
- data.tar.gz: 6f56e452dc4298b9cd1bafe150691325df84e745
3
+ metadata.gz: a22d18dbf189673739f5963a622151a85e814a1e
4
+ data.tar.gz: 8b61eee5e62db727d1c105df70588e2fa0209434
5
5
  SHA512:
6
- metadata.gz: eabe941e7349e2003decadcd90258029c0ed7250c9f3f91305a85094387777217b732f4c5bfecc3214b16b26f8724a1ce7ac3163c418bcdb664bf80142f76313
7
- data.tar.gz: 2101dcd95ea79eb57e6c58da6f6f7b10dba7bb1233247e2b2b2f6fe421af4ad75602c02d6608da9b4d8683dedda9fbefbe4b79c98c6456f73e24935e97c0df88
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
@@ -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,9 @@
1
+ module App
2
+
3
+ class AWSCredentials
4
+
5
+ attr_accessor :region, :output, :aws_key, :aws_secret
6
+
7
+ end
8
+
9
+ 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