lita-enhance 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +8 -0
- data/Gemfile +5 -0
- data/LICENSE +19 -0
- data/README.md +95 -0
- data/Rakefile +6 -0
- data/lib/lita-enhance.rb +7 -0
- data/lib/lita/handlers/enhance.rb +209 -0
- data/lib/lita/handlers/enhance/chef_indexer.rb +133 -0
- data/lib/lita/handlers/enhance/enhancer.rb +58 -0
- data/lib/lita/handlers/enhance/enhancers/hostname_enhancer.rb +72 -0
- data/lib/lita/handlers/enhance/enhancers/instance_id_enhancer.rb +40 -0
- data/lib/lita/handlers/enhance/enhancers/ip_enhancer.rb +40 -0
- data/lib/lita/handlers/enhance/enhancers/mac_address_enhancer.rb +42 -0
- data/lib/lita/handlers/enhance/node.rb +57 -0
- data/lib/lita/handlers/enhance/node_index.rb +41 -0
- data/lib/lita/handlers/enhance/session.rb +77 -0
- data/lita-enhance.gemspec +23 -0
- data/locales/en.yml +16 -0
- data/spec/data/box01.json +214 -0
- data/spec/data/box02.json +163 -0
- data/spec/data/box03.json +123 -0
- data/spec/data/stg-web01.json +89 -0
- data/spec/data/web01.json +89 -0
- data/spec/lita/handlers/enhance/chef_indexer_spec.rb +18 -0
- data/spec/lita/handlers/enhance/enhancer_example.rb +16 -0
- data/spec/lita/handlers/enhance/enhancers/hostname_enhancer_spec.rb +58 -0
- data/spec/lita/handlers/enhance/enhancers/instance_id_enhancer_spec.rb +31 -0
- data/spec/lita/handlers/enhance/enhancers/ip_enhancer_spec.rb +48 -0
- data/spec/lita/handlers/enhance/enhancers/mac_address_enhancer_spec.rb +32 -0
- data/spec/lita/handlers/enhance/node_index_spec.rb +33 -0
- data/spec/lita/handlers/enhance/node_spec.rb +51 -0
- data/spec/lita/handlers/enhance/session_spec.rb +48 -0
- data/spec/lita/handlers/enhance_spec.rb +136 -0
- data/spec/spec_helper.rb +64 -0
- metadata +168 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d328609e647b18fe537f7456b95728754019f40f
|
4
|
+
data.tar.gz: 6a90c0ca0b0e92da38d212caa14b9477d5548e05
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 74f55095d74a7c740dde25093af30f70e8919237549fdfdc8a6c88537c322e9d5c8b847bfc0a92e8298b491098143f4d8024e5f8dd0c6e9c0ee6388376d352b0
|
7
|
+
data.tar.gz: 4ce5807e836c8a5c448439a873f4b7f8252ba7e5904cc06d6a973917f75fea0f37806ae89a5b150f95c7366e0995aad9a9fdada3f72a67a3ba04aac5f66187d1
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2014 PagerDuty
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# lita-enhance
|
2
|
+
|
3
|
+
Enhances text that contains opaque machine identifiers by replacing them with that machine's hostname.
|
4
|
+
|
5
|
+
```
|
6
|
+
you> enhance 10.214.0.1
|
7
|
+
lita> *box01*
|
8
|
+
```
|
9
|
+
|
10
|
+
Text that is enhanced is returned in the same format that is passed in, which makes enhancing log lines or CLI output nice to view.
|
11
|
+
|
12
|
+
```
|
13
|
+
you> enhance Finished hinted handoff of 8826 rows to endpoint /192.168.100.1
|
14
|
+
lita> Finished hinted handoff of 8826 rows to endpoint /*box01*
|
15
|
+
|
16
|
+
you> enhance tcp 0 0 10.214.0.1:ssh 10.214.0.2:4997 ESTABLISHED
|
17
|
+
lita> tcp 0 0 *box01*:ssh *box02*:4997 ESTABLISHED
|
18
|
+
```
|
19
|
+
|
20
|
+
You can increase the level of enhancement to add more machine details to the output.
|
21
|
+
|
22
|
+
```
|
23
|
+
you> enhance 10.214.0.1
|
24
|
+
lita> *box01*
|
25
|
+
|
26
|
+
you> enhance lvl:2 10.214.0.1
|
27
|
+
lita> *box01 (us-west-2)*
|
28
|
+
```
|
29
|
+
|
30
|
+
Machine details are obtained by indexing database of machine assets. Right now only Chef servers are indexed. Results are indexed every 15 minutes by default. Old machines are left in the index so you can identify them even after they have been torn down.
|
31
|
+
|
32
|
+
```
|
33
|
+
you> enhance lvl:2 old-box01
|
34
|
+
lita> ¿old-box01 (us-east-1)?
|
35
|
+
```
|
36
|
+
|
37
|
+
[And for fun](https://www.youtube.com/watch?v=Vxq9yj2pVWk), you can implicitly enhance previously enhanced text by just sending ```enhance```. The string to enhance is retained separately in each room that the enhance string was sent.
|
38
|
+
|
39
|
+
```
|
40
|
+
you> enhance 10.214.0.1
|
41
|
+
lita> *box01*
|
42
|
+
|
43
|
+
you> enhance
|
44
|
+
lita> *box01 (us-west-2)*
|
45
|
+
```
|
46
|
+
|
47
|
+
## Installation
|
48
|
+
|
49
|
+
Add lita-enhance to your Lita instance's Gemfile:
|
50
|
+
|
51
|
+
``` ruby
|
52
|
+
gem "lita-enhance"
|
53
|
+
```
|
54
|
+
|
55
|
+
## Configuration
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
# Configure one or more knife files for the Chef server that lita-enhance should index
|
59
|
+
config.handlers.enhance.knife_configs = {
|
60
|
+
'staging' => 'knife-staging.rb',
|
61
|
+
'production' => 'knife-production.rb'
|
62
|
+
}
|
63
|
+
|
64
|
+
# How often to refresh enhance's index in seconds. Default is 15 minutes.
|
65
|
+
config.handlers.enhance.refresh_interval = 15 * 60
|
66
|
+
|
67
|
+
# How long in seconds to remember messages to implicitly enhance. Default is 1 week.
|
68
|
+
config.handlers.enhance.blurry_message_ttl = 7 * 24 * 60 * 60
|
69
|
+
```
|
70
|
+
|
71
|
+
## Usage
|
72
|
+
|
73
|
+
```
|
74
|
+
# Enhance IP addresses (public and private) with the machine's name if it is known
|
75
|
+
enhance 54.189.200.22
|
76
|
+
enhance 10.214.0.1
|
77
|
+
|
78
|
+
# Enhance EC2 host names
|
79
|
+
enhance ec2-54-189-200-22.us-west-2.compute.amazonaws.com
|
80
|
+
enhance ip-10-214-13-102.us-west-2.compute.internal
|
81
|
+
|
82
|
+
# Enhance host names (short & long)
|
83
|
+
enhance box01
|
84
|
+
enhance box01.example.com
|
85
|
+
|
86
|
+
# Enhance EC2 instance IDs
|
87
|
+
enhance i-123456
|
88
|
+
|
89
|
+
# Enhance MAC addresses
|
90
|
+
enhance 22:00:0a:00:32:23
|
91
|
+
```
|
92
|
+
|
93
|
+
## License
|
94
|
+
|
95
|
+
[MIT](http://opensource.org/licenses/MIT)
|
data/Rakefile
ADDED
data/lib/lita-enhance.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
require 'lita/handlers/enhance/chef_indexer'
|
4
|
+
require 'lita/handlers/enhance/node'
|
5
|
+
require 'lita/handlers/enhance/enhancer'
|
6
|
+
require 'lita/handlers/enhance/session'
|
7
|
+
|
8
|
+
module Lita
|
9
|
+
module Handlers
|
10
|
+
class Enhance < Handler
|
11
|
+
on :loaded, :setup_background_refresh
|
12
|
+
|
13
|
+
route(
|
14
|
+
/^refresh enhance$/,
|
15
|
+
:refresh,
|
16
|
+
command: true
|
17
|
+
)
|
18
|
+
|
19
|
+
route(
|
20
|
+
/^enhance stats$/,
|
21
|
+
:stats,
|
22
|
+
command: true
|
23
|
+
)
|
24
|
+
|
25
|
+
route(
|
26
|
+
/\Aenhance(\slvl:([0-9]))?(\s(.*))?\z/m,
|
27
|
+
:enhance,
|
28
|
+
command: true,
|
29
|
+
help: {
|
30
|
+
'enhance <anything>' => 'Enhances details in your text. https://www.youtube.com/watch?v=Vxq9yj2pVWk'
|
31
|
+
}
|
32
|
+
)
|
33
|
+
|
34
|
+
def self.default_config(config)
|
35
|
+
config.knife_configs = {}
|
36
|
+
if File.exist?('~/.chef/knife.rb')
|
37
|
+
config.knife_configs['default'] = '~/.chef/knife.rb'
|
38
|
+
end
|
39
|
+
|
40
|
+
config.refresh_interval = 15 * 60
|
41
|
+
config.add_quote = true
|
42
|
+
|
43
|
+
# How long to remember the previously enhanced message for.
|
44
|
+
config.blurry_message_ttl = 7 * 24 * 60 * 60 # seconds
|
45
|
+
end
|
46
|
+
|
47
|
+
def setup_background_refresh(payload)
|
48
|
+
@@chef_indexer = ChefIndexer.new(redis, config.knife_configs)
|
49
|
+
@@enhancers = Enhancer.all.map do |enhancer_klass|
|
50
|
+
enhancer_klass.new(redis)
|
51
|
+
end
|
52
|
+
|
53
|
+
bg_refresh = proc do
|
54
|
+
begin
|
55
|
+
lock_and_refresh_index
|
56
|
+
rescue => e
|
57
|
+
# Keep error from killing background thread
|
58
|
+
log.error { "#{e.message}\n#{e.backtrace.join("\n")}" }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
log.info { "Will refresh enhance index every #{config.refresh_interval} seconds" }
|
63
|
+
after(0, &bg_refresh)
|
64
|
+
every(config.refresh_interval, &bg_refresh)
|
65
|
+
end
|
66
|
+
|
67
|
+
def refresh(response)
|
68
|
+
response.reply(t 'refresh.queued')
|
69
|
+
|
70
|
+
after(0) do
|
71
|
+
begin
|
72
|
+
lock_and_refresh_index
|
73
|
+
response.reply(success(t 'refresh.success'))
|
74
|
+
rescue => e
|
75
|
+
response.reply(failed(t 'refresh.failed'))
|
76
|
+
log.info { "#{e.message}\n#{e.backtrace.join("\n")}" }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def enhance(response)
|
82
|
+
level = response.matches[0][1]
|
83
|
+
level = level.to_i if level
|
84
|
+
|
85
|
+
blurry_string = response.matches[0][3]
|
86
|
+
return if blurry_string == "stats"
|
87
|
+
|
88
|
+
key = last_message_key(response)
|
89
|
+
level_key = key + ":level"
|
90
|
+
|
91
|
+
session = Session.new(redis, key, config.blurry_message_ttl)
|
92
|
+
|
93
|
+
if blurry_string && !blurry_string.empty?
|
94
|
+
level = 1 unless level
|
95
|
+
else
|
96
|
+
blurry_string = session.last_message
|
97
|
+
end
|
98
|
+
|
99
|
+
unless blurry_string
|
100
|
+
response.reply(failed(t 'enhance.message_required'))
|
101
|
+
return
|
102
|
+
end
|
103
|
+
|
104
|
+
level = session.last_level + 1 unless level
|
105
|
+
|
106
|
+
if level > max_level
|
107
|
+
response.reply(failed(t 'enhance.level_too_high', max_level: max_level))
|
108
|
+
return
|
109
|
+
elsif level < 1
|
110
|
+
response.reply(failed(t 'enhance.level_too_low', max_level: max_level))
|
111
|
+
return
|
112
|
+
end
|
113
|
+
|
114
|
+
enhanced_message = session.enhance!(blurry_string, level)
|
115
|
+
|
116
|
+
if enhanced_message != blurry_string
|
117
|
+
response.reply(mono(enhanced_message))
|
118
|
+
else
|
119
|
+
response.reply(no_change(t 'enhance.nothing_to_enhance'))
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def stats(response)
|
124
|
+
INDEX_MUTEX.synchronize do
|
125
|
+
response_msg = t('stats.last_refreshed', last_refreshed: (@@chef_indexer.last_refreshed.to_s || 'never'))
|
126
|
+
response_msg += "\n" + t('stats.refresh_frequency', refresh_mins: ('%.2f' % (config.refresh_interval / 60.0)))
|
127
|
+
@@enhancers.each do |e|
|
128
|
+
response_msg += "\n#{e}"
|
129
|
+
end
|
130
|
+
response.reply(response_msg)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
# This mutex must be obtained to refresh the index
|
136
|
+
REFRESH_MUTEX = Mutex.new unless defined?(REFRESH_MUTEX)
|
137
|
+
|
138
|
+
# This mutex must be obtains to update the index with new data, or to use the index to enhance some text
|
139
|
+
INDEX_MUTEX = Mutex.new unless defined?(INDEX_MUTEX)
|
140
|
+
|
141
|
+
def lock_and_refresh_index
|
142
|
+
REFRESH_MUTEX.synchronize do
|
143
|
+
@@chef_indexer.refresh
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def last_message_key(response)
|
148
|
+
response.message.source.room || response.message.source.user.id
|
149
|
+
end
|
150
|
+
|
151
|
+
def max_level
|
152
|
+
@@enhancers.map {|x| x.max_level }.max
|
153
|
+
end
|
154
|
+
|
155
|
+
def adapter
|
156
|
+
if Lita.respond_to?(:config)
|
157
|
+
Lita.config.robot.adapter
|
158
|
+
elsif robot.respond_to?(:config)
|
159
|
+
robot.config.robot.adapter
|
160
|
+
else
|
161
|
+
:unknown
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Calls out that this message was successful via adapter specific messaging
|
166
|
+
def success(message)
|
167
|
+
case adapter
|
168
|
+
when :hipchat
|
169
|
+
"(successful) #{message}"
|
170
|
+
else
|
171
|
+
message
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Calls out that the action failed via adapter specific messaging
|
176
|
+
def failed(message)
|
177
|
+
case adapter
|
178
|
+
when :hipchat
|
179
|
+
"(failed) #{message}"
|
180
|
+
else
|
181
|
+
message
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Calls out that action resulted in no change via adapter specific messaging
|
186
|
+
def no_change(message)
|
187
|
+
case adapter
|
188
|
+
when :hipchat
|
189
|
+
"(nothingtodohere) #{message}"
|
190
|
+
else
|
191
|
+
message
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Attempts to render the message using a monospaced font via adapter specific messaging
|
196
|
+
def mono(message)
|
197
|
+
case adapter
|
198
|
+
when :hipchat
|
199
|
+
"/quote #{message}"
|
200
|
+
else
|
201
|
+
message
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
Lita.register_handler(Enhance)
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'chef'
|
2
|
+
|
3
|
+
module Lita
|
4
|
+
module Handlers
|
5
|
+
class Enhance < Handler
|
6
|
+
# This class is responsible for indexing Chef servers to extract
|
7
|
+
# enhanceable data to populate the enhance indices.
|
8
|
+
class ChefIndexer
|
9
|
+
attr_reader :last_refreshed
|
10
|
+
|
11
|
+
attr_reader :redis, :knife_configs
|
12
|
+
|
13
|
+
def initialize(redis, knife_configs)
|
14
|
+
@redis = redis
|
15
|
+
@knife_configs = knife_configs
|
16
|
+
end
|
17
|
+
|
18
|
+
def refresh
|
19
|
+
log.debug { "Refreshing enhance index..." }
|
20
|
+
|
21
|
+
self.knife_configs.each do |_, config_path|
|
22
|
+
index(config_path, @enhancers)
|
23
|
+
end
|
24
|
+
|
25
|
+
@last_refreshed = Time.now
|
26
|
+
|
27
|
+
log.debug { "Refreshed enhance index" }
|
28
|
+
|
29
|
+
# Refreshing the index pulls a lot of large objects into memory,
|
30
|
+
# forcing a GC run to ensure that our heap doesn't grow aggressively.
|
31
|
+
GC.start
|
32
|
+
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def index_chef_node(chef_node)
|
37
|
+
node = node_from_chef_node(chef_node)
|
38
|
+
node.store!(redis)
|
39
|
+
|
40
|
+
index_hostname(chef_node, node)
|
41
|
+
index_instanceid(chef_node, node)
|
42
|
+
index_ip(chef_node, node)
|
43
|
+
index_mac_address(chef_node, node)
|
44
|
+
end
|
45
|
+
|
46
|
+
def node_from_chef_node(chef_node)
|
47
|
+
Node.new.tap do |n|
|
48
|
+
n.name = chef_node.name
|
49
|
+
n.dc = if chef_node['ec2']
|
50
|
+
chef_node['ec2']['placement_availability_zone']
|
51
|
+
elsif chef_node['cloud']
|
52
|
+
chef_node['cloud']['provider']
|
53
|
+
end
|
54
|
+
n.environment = chef_node.environment
|
55
|
+
n.fqdn = chef_node['fqdn']
|
56
|
+
n.last_seen_at = Time.now
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def index(chef_config_path, enhancers)
|
63
|
+
log.debug { "Indexing #{chef_config_path}" }
|
64
|
+
|
65
|
+
Chef::Config.from_file(File.expand_path(chef_config_path))
|
66
|
+
query = Chef::Search::Query.new
|
67
|
+
|
68
|
+
query.search("node", "*:*") do |chef_node|
|
69
|
+
index_chef_node(chef_node)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def index_hostname(chef_node, node)
|
74
|
+
enhancer = HostnameEnhancer.new(redis)
|
75
|
+
|
76
|
+
enhancer.index(chef_node['fqdn'], node)
|
77
|
+
|
78
|
+
if chef_node['cloud']
|
79
|
+
enhancer.index(chef_node['cloud']['local_hostname'], node)
|
80
|
+
enhancer.index(chef_node['cloud']['public_hostname'], node)
|
81
|
+
end
|
82
|
+
|
83
|
+
if chef_node['cloud_v2']
|
84
|
+
enhancer.index(chef_node['cloud_v2']['local_hostname'], node)
|
85
|
+
enhancer.index(chef_node['cloud_v2']['public_hostname'], node)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def index_instanceid(chef_node, node)
|
90
|
+
enhancer = InstanceIdEnhancer.new(redis)
|
91
|
+
|
92
|
+
if chef_node['ec2']
|
93
|
+
enhancer.index(chef_node['ec2']['instance_id'], node)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def index_ip(chef_node, node)
|
98
|
+
enhancer = IpEnhancer.new(redis)
|
99
|
+
|
100
|
+
enhancer.index(chef_node['ipaddress'], node)
|
101
|
+
|
102
|
+
if chef_node['cloud']
|
103
|
+
enhancer.index(chef_node['cloud']['local_ipv4'], node)
|
104
|
+
enhancer.index(chef_node['cloud']['public_ipv4'], node)
|
105
|
+
end
|
106
|
+
|
107
|
+
if chef_node['cloud_v2']
|
108
|
+
if chef_node['cloud_v2']['public_ipv4_addrs']
|
109
|
+
ips = chef_node['cloud_v2']['public_ipv4_addrs']
|
110
|
+
ips.each {|ip| enhancer.index(ip, node) }
|
111
|
+
end
|
112
|
+
if chef_node['cloud_v2']['local_ipv4_addrs']
|
113
|
+
ips = chef_node['cloud_v2']['local_ipv4_addrs']
|
114
|
+
ips.each {|ip| enhancer.index(ip, node) }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def index_mac_address(chef_node, node)
|
120
|
+
enhancer = MacAddressEnhancer.new(redis)
|
121
|
+
|
122
|
+
if chef_node['macaddress']
|
123
|
+
enhancer.index(chef_node['macaddress'], node)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def log
|
128
|
+
Lita.logger
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|