tailstack 0.0.1
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/bin/tailstack +182 -0
- metadata +45 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b0157e2f5e14279fee2665aa8f66d6c23dc01395
|
4
|
+
data.tar.gz: 9b8b81217097bc57aa4ebb0b8043d29cab3a7bc1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 52de217db800b434c5521ae3f5b3ebe24007da01358a4cefb7c602d1088b3a5a04d1db65858180d7f5892068ad4d5f32074f3beff21ee672786a95b3f24e7d4d
|
7
|
+
data.tar.gz: 6a348d21656d70f614dc37ac79f652fc520e9964b8bb411e0d1ba8ae0e3056e36a154661b287cece664423cd21d49a2d1514ff3c0c0a3997b5faf479eb538c81
|
data/bin/tailstack
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'aws-sdk'
|
4
|
+
require 'json'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: tailstack [options]"
|
10
|
+
opts.on('-r', '--region REGION', 'AWS Region such as "us-west-2"'){ |o| options[:region] = o }
|
11
|
+
opts.on('-s', '--stack STACK_NAME', 'CloudFormation Stack to tail'){ |o| options[:stack] = o }
|
12
|
+
opts.on('-p', '--profile PROFILE', 'Specify AWS profile from config file'){ |o| options[:profile] = o }
|
13
|
+
opts.on('-l', '--list', 'Lists stacks in environtment and exits'){ |o| options[:list] = o }
|
14
|
+
opts.on('-o', '--outputs', 'Combine with --list also list outputs'){ |o| options[:outputs] = o }
|
15
|
+
opts.on('-d', '--parms', 'Combine with --list also list parameters'){ |o| options[:parms] = o }
|
16
|
+
end.parse!
|
17
|
+
|
18
|
+
using_env_creds = true
|
19
|
+
aws_access_key_id = nil
|
20
|
+
aws_secret_access_key = nil
|
21
|
+
options[:profile] ||= 'default'
|
22
|
+
|
23
|
+
if !options.key?(:region)
|
24
|
+
found_profile = false
|
25
|
+
|
26
|
+
if File.exists?(File.expand_path('~/.aws/config'))
|
27
|
+
File.read(File.expand_path('~/.aws/config')).each_line do |line|
|
28
|
+
if found_profile
|
29
|
+
options[:region] = line.split('=').last.strip
|
30
|
+
puts "Using region #{options[:region]}"
|
31
|
+
break;
|
32
|
+
end
|
33
|
+
if line.match("#{options[:profile]}\\]")
|
34
|
+
found_profile = true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
else
|
38
|
+
puts "~/.aws/config file not found for reading the region"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
if (!ENV.key?('AWS_ACCESS_KEY_ID') || !ENV.key?('AWS_SECRET_ACCESS_KEY')) || options.key?(:profile)
|
43
|
+
found_profile = false
|
44
|
+
found_key = false
|
45
|
+
found_secret = false
|
46
|
+
|
47
|
+
if File.exist?(File.expand_path('~/.aws/credentials'))
|
48
|
+
File.read(File.expand_path('~/.aws/credentials')).each_line do |line|
|
49
|
+
if found_profile && line.match('aws_access_key_id')
|
50
|
+
aws_access_key_id = line.split('=').last.strip
|
51
|
+
found_key = true
|
52
|
+
if found_secret
|
53
|
+
break
|
54
|
+
end
|
55
|
+
end
|
56
|
+
if found_profile && line.match('aws_secret_access_key')
|
57
|
+
aws_secret_access_key = line.split('=').last.strip
|
58
|
+
found_secret = true
|
59
|
+
using_env_creds = false
|
60
|
+
if found_key
|
61
|
+
break
|
62
|
+
end
|
63
|
+
end
|
64
|
+
if line.match("\\[#{options[:profile]}\\]")
|
65
|
+
found_profile = true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
if found_key && using_env_creds == false
|
72
|
+
Aws.config.update({
|
73
|
+
region: options[:region],
|
74
|
+
credentials: Aws::Credentials.new(aws_access_key_id, aws_secret_access_key)
|
75
|
+
})
|
76
|
+
else
|
77
|
+
Aws.config.update({ region: options[:region] })
|
78
|
+
end
|
79
|
+
|
80
|
+
# Color stuff
|
81
|
+
class String
|
82
|
+
def black; "\e[30m#{self}\e[0m" end
|
83
|
+
def red; "\e[31m#{self}\e[0m" end
|
84
|
+
def green; "\e[32m#{self}\e[0m" end
|
85
|
+
def brown; "\e[33m#{self}\e[0m" end
|
86
|
+
def blue; "\e[34m#{self}\e[0m" end
|
87
|
+
def magenta; "\e[35m#{self}\e[0m" end
|
88
|
+
def cyan; "\e[36m#{self}\e[0m" end
|
89
|
+
def gray; "\e[37m#{self}\e[0m" end
|
90
|
+
|
91
|
+
def bg_black; "\e[40m#{self}\e[0m" end
|
92
|
+
def bg_red; "\e[41m#{self}\e[0m" end
|
93
|
+
def bg_green; "\e[42m#{self}\e[0m" end
|
94
|
+
def bg_brown; "\e[43m#{self}\e[0m" end
|
95
|
+
def bg_blue; "\e[44m#{self}\e[0m" end
|
96
|
+
def bg_magenta; "\e[45m#{self}\e[0m" end
|
97
|
+
def bg_cyan; "\e[46m#{self}\e[0m" end
|
98
|
+
def bg_gray; "\e[47m#{self}\e[0m" end
|
99
|
+
|
100
|
+
def bold; "\e[1m#{self}\e[22m" end
|
101
|
+
def italic; "\e[3m#{self}\e[23m" end
|
102
|
+
def underline; "\e[4m#{self}\e[24m" end
|
103
|
+
def blink; "\e[5m#{self}\e[25m" end
|
104
|
+
def reverse_color; "\e[7m#{self}\e[27m" end
|
105
|
+
end
|
106
|
+
|
107
|
+
def event_color(resource_status)
|
108
|
+
event_green = %w[ UPDATE_COMPLETE CREATE_COMPLETE DELETE_COMPLETE UPDATE_COMPLETE_CLEANUP_IN_PROGRESS UPDATE_ROLLBACK_COMPLETE ]
|
109
|
+
event_yellow = %w[ UPDATE_IN_PROGRESS CREATE_IN_PROGRESS DELETE_IN_PROGRESS UPDATE_ROLLBACK_IN_PROGRESS UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS ]
|
110
|
+
|
111
|
+
case
|
112
|
+
when event_green.include?(resource_status)
|
113
|
+
return resource_status.green
|
114
|
+
when event_yellow.include?(resource_status)
|
115
|
+
return resource_status.brown
|
116
|
+
else
|
117
|
+
return resource_status.red
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# AWS Calls now
|
122
|
+
cfn = Aws::CloudFormation::Client.new()
|
123
|
+
|
124
|
+
def isThisTheEnd? event
|
125
|
+
events_good = %w[ CREATE_COMPLETE UPDATE_COMPLETE DELETE_COMPLETE ]
|
126
|
+
events_bad = %w[ CREATE_FAILED UPDATE_ROLLBACK_COMPLETE ROLLBACK_COMPLETE ]
|
127
|
+
|
128
|
+
unless event[:resource_type] == 'AWS::CloudFormation::Stack'
|
129
|
+
return false
|
130
|
+
end
|
131
|
+
|
132
|
+
if events_bad.include? event[:resource_status]
|
133
|
+
exit 1
|
134
|
+
end
|
135
|
+
|
136
|
+
return events_good.include? event[:resource_status]
|
137
|
+
end
|
138
|
+
|
139
|
+
if options.key?(:list)
|
140
|
+
stack_details = cfn.describe_stacks.to_h[:stacks]
|
141
|
+
cfn.list_stacks.to_h[:stack_summaries].uniq{|s| s[:stack_name]}.collect do |s|
|
142
|
+
if s[:stack_status] != 'DELETE_COMPLETE'
|
143
|
+
puts "#{s[:stack_name].bold} - #{s[:template_description].cyan}"
|
144
|
+
outputs = stack_details.find{|stack| stack[:stack_name] == s[:stack_name]}[:outputs]
|
145
|
+
parms = stack_details.find{|stack| stack[:stack_name] == s[:stack_name]}[:parameters]
|
146
|
+
if options.key?(:parms) && parms
|
147
|
+
puts " #{"Parameters:".green}"
|
148
|
+
parms.each do |parm|
|
149
|
+
puts " #{parm[:parameter_key].blue}: #{parm[:parameter_value].gray}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
if options.key?(:outputs) && outputs
|
153
|
+
puts " #{"Outputs:".magenta}"
|
154
|
+
outputs.each do |output|
|
155
|
+
puts " #{output[:output_key].brown}: #{output[:output_value].gray}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
exit 0
|
162
|
+
end
|
163
|
+
|
164
|
+
events_seen = []
|
165
|
+
loop do
|
166
|
+
# Does the stack exist?
|
167
|
+
unless cfn.list_stacks.to_h[:stack_summaries].map{|s| s[:stack_name]}.include? options[:stack]
|
168
|
+
puts "Stack '#{options[:stack]}' not found."
|
169
|
+
exit 1
|
170
|
+
end
|
171
|
+
# Get the events from the stack
|
172
|
+
stack_out = cfn.describe_stack_events({ stack_name: options[:stack] })
|
173
|
+
stack_out.to_h[:stack_events].reverse.each do |event|
|
174
|
+
unless events_seen.include? event[:event_id]
|
175
|
+
puts "#{event[:timestamp]} #{event[:resource_type]} #{event_color(event[:resource_status])} #{event[:resource_status_reason]}"
|
176
|
+
#puts "#{event[:resource_properties]}"
|
177
|
+
events_seen << event[:event_id]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
break if isThisTheEnd? stack_out.to_h[:stack_events].first
|
181
|
+
sleep 2
|
182
|
+
end
|
metadata
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tailstack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tony Fruzza
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-18 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: aws sdk based tool for watching CloudFormation stacks being created,
|
14
|
+
updated, or deleted.
|
15
|
+
email: anthony.fruzza@sturdynetworks.com
|
16
|
+
executables:
|
17
|
+
- tailstack
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- bin/tailstack
|
22
|
+
homepage:
|
23
|
+
licenses: []
|
24
|
+
metadata: {}
|
25
|
+
post_install_message:
|
26
|
+
rdoc_options: []
|
27
|
+
require_paths:
|
28
|
+
- lib
|
29
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
requirements: []
|
40
|
+
rubyforge_project:
|
41
|
+
rubygems_version: 2.6.8
|
42
|
+
signing_key:
|
43
|
+
specification_version: 4
|
44
|
+
summary: Tails the event output of AWS CloudFormation stacks
|
45
|
+
test_files: []
|