lita-lbcfg 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8dc3039476b7f7c9144df73795aad96ad54f91b6
4
+ data.tar.gz: 0be0e424b5a064e73228b3d13568d3fb0ea6ad77
5
+ SHA512:
6
+ metadata.gz: d679d7f740277385c323baa326fedec32e1f0a2be58712bb0e8c508196a43ad26b5be2f87b7f43c205c5df71ce87e5bacdcdbd7346c3d45e218bed613d7bde6b
7
+ data.tar.gz: e6c6eec1cff7861f5f0dfedf85fa594354871e89b220614c3f1a18d06c8bbe69404ba5fa24ea383d0046ec2804905d69cb11d158e8797d763fee0931940d8e75
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2016 DoubleDutch, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ source 'https://rubygems.org'
18
+
19
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,169 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction, and
10
+ distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright
13
+ owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all other entities
16
+ that control, are controlled by, or are under common control with that entity.
17
+ For the purposes of this definition, "control" means (i) the power, direct or
18
+ indirect, to cause the direction or management of such entity, whether by
19
+ contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20
+ outstanding shares, or (iii) beneficial ownership of such entity.
21
+
22
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
23
+ permissions granted by this License.
24
+
25
+ "Source" form shall mean the preferred form for making modifications, including
26
+ but not limited to software source code, documentation source, and configuration
27
+ files.
28
+
29
+ "Object" form shall mean any form resulting from mechanical transformation or
30
+ translation of a Source form, including but not limited to compiled object
31
+ code, generated documentation, and conversions to other media types.
32
+
33
+ "Work" shall mean the work of authorship, whether in Source or Object form,
34
+ made available under the License, as indicated by a copyright notice that is
35
+ included in or attached to the work (an example is provided in the Appendix
36
+ below).
37
+
38
+ "Derivative Works" shall mean any work, whether in Source or Object form, that
39
+ is based on (or derived from) the Work and for which the editorial revisions,
40
+ annotations, elaborations, or other modifications represent, as a whole, an
41
+ original work of authorship. For the purposes of this License, Derivative Works
42
+ shall not include works that remain separable from, or merely link (or bind by
43
+ name) to the interfaces of, the Work and Derivative Works thereof.
44
+
45
+ "Contribution" shall mean any work of authorship, including the original version
46
+ of the Work and any modifications or additions to that Work or Derivative Works
47
+ thereof, that is intentionally submitted to Licensor for inclusion in the Work
48
+ by the copyright owner or by an individual or Legal Entity authorized to submit
49
+ on behalf of the copyright owner. For the purposes of this definition,
50
+ "submitted" means any form of electronic, verbal, or written communication sent
51
+ to the Licensor or its representatives, including but not limited to
52
+ communication on electronic mailing lists, source code control systems, and
53
+ issue tracking systems that are managed by, or on behalf of, the Licensor for
54
+ the purpose of discussing and improving the Work, but excluding communication
55
+ that is conspicuously marked or otherwise designated in writing by the copyright
56
+ owner as "Not a Contribution."
57
+
58
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
59
+ of whom a Contribution has been received by Licensor and subsequently
60
+ incorporated within the Work.
61
+
62
+ 2. Grant of Copyright License.
63
+
64
+ Subject to the terms and conditions of this License, each Contributor hereby
65
+ grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
66
+ irrevocable copyright license to reproduce, prepare Derivative Works of,
67
+ publicly display, publicly perform, sublicense, and distribute the Work and such
68
+ Derivative Works in Source or Object form.
69
+
70
+ 3. Grant of Patent License.
71
+
72
+ Subject to the terms and conditions of this License, each Contributor hereby
73
+ grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
74
+ irrevocable (except as stated in this section) patent license to make, have
75
+ made, use, offer to sell, sell, import, and otherwise transfer the Work, where
76
+ such license applies only to those patent claims licensable by such Contributor
77
+ that are necessarily infringed by their Contribution(s) alone or by combination
78
+ of their Contribution(s) with the Work to which such Contribution(s) was
79
+ submitted. If You institute patent litigation against any entity (including a
80
+ cross-claim or counterclaim in a lawsuit) alleging that the Work or a
81
+ Contribution incorporated within the Work constitutes direct or contributory
82
+ patent infringement, then any patent licenses granted to You under this License
83
+ for that Work shall terminate as of the date such litigation is filed.
84
+
85
+ 4. Redistribution.
86
+
87
+ You may reproduce and distribute copies of the Work or Derivative Works thereof
88
+ in any medium, with or without modifications, and in Source or Object form,
89
+ provided that You meet the following conditions:
90
+
91
+ 1. You must give any other recipients of the Work or Derivative Works a copy of
92
+ this License; and
93
+ 2. You must cause any modified files to carry prominent notices stating that You
94
+ changed the files; and
95
+ 3. You must retain, in the Source form of any Derivative Works that You
96
+ distribute, all copyright, patent, trademark, and attribution notices from
97
+ the Source form of the Work, excluding those notices that do not pertain to
98
+ any part of the Derivative Works; and
99
+ 4. If the Work includes a "NOTICE" text file as part of its distribution, then
100
+ any Derivative Works that You distribute must include a readable copy of the
101
+ attribution notices contained within such NOTICE file, excluding those
102
+ notices that do not pertain to any part of the Derivative Works, in at least
103
+ one of the following places: within a NOTICE text file distributed as part of
104
+ the Derivative Works; within the Source form or documentation, if provided
105
+ along with the Derivative Works; or, within a display generated by the
106
+ Derivative Works, if and wherever such third-party notices normally appear.
107
+ The contents of the NOTICE file are for informational purposes only and do
108
+ not modify the License. You may add Your own attribution notices within
109
+ Derivative Works that You distribute, alongside or as an addendum to the
110
+ NOTICE text from the Work, provided that such additional attribution notices
111
+ cannot be construed as modifying the License.
112
+
113
+ You may add Your own copyright statement to Your modifications and may provide
114
+ additional or different license terms and conditions for use, reproduction, or
115
+ distribution of Your modifications, or for any such Derivative Works as a whole,
116
+ provided Your use, reproduction, and distribution of the Work otherwise complies
117
+ with the conditions stated in this License.
118
+
119
+ 5. Submission of Contributions.
120
+
121
+ Unless You explicitly state otherwise, any Contribution intentionally submitted
122
+ for inclusion in the Work by You to the Licensor shall be under the terms and
123
+ conditions of this License, without any additional terms or conditions.
124
+ Notwithstanding the above, nothing herein shall supersede or modify the terms of
125
+ any separate license agreement you may have executed with Licensor regarding
126
+ such Contributions.
127
+
128
+ 6. Trademarks.
129
+
130
+ This License does not grant permission to use the trade names, trademarks,
131
+ service marks, or product names of the Licensor, except as required for
132
+ reasonable and customary use in describing the origin of the Work and
133
+ reproducing the content of the NOTICE file.
134
+
135
+ 7. Disclaimer of Warranty.
136
+
137
+ Unless required by applicable law or agreed to in writing, Licensor provides the
138
+ Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
139
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
140
+ including, without limitation, any warranties or conditions of TITLE,
141
+ NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
142
+ solely responsible for determining the appropriateness of using or
143
+ redistributing the Work and assume any risks associated with Your exercise of
144
+ permissions under this License.
145
+
146
+ 8. Limitation of Liability.
147
+
148
+ In no event and under no legal theory, whether in tort (including negligence),
149
+ contract, or otherwise, unless required by applicable law (such as deliberate
150
+ and grossly negligent acts) or agreed to in writing, shall any Contributor be
151
+ liable to You for damages, including any direct, indirect, special, incidental,
152
+ or consequential damages of any character arising as a result of this License
153
+ or out of the use or inability to use the Work (including but not limited to
154
+ damages for loss of goodwill, work stoppage, computer failure or malfunction,
155
+ or any and all other commercial damages or losses), even if such Contributor
156
+ has been advised of the possibility of such damages.
157
+
158
+ 9. Accepting Warranty or Additional Liability.
159
+
160
+ While redistributing the Work or Derivative Works thereof, You may choose to
161
+ offer, and charge a fee for, acceptance of support, warranty, indemnity, or
162
+ other liability obligations and/or rights consistent with this License. However,
163
+ in accepting such obligations, You may act only on Your own behalf and on Your
164
+ sole responsibility, not on behalf of any other Contributor, and only if You
165
+ agree to indemnify, defend, and hold each Contributor harmless for any liability
166
+ incurred by, or claims asserted against, such Contributor by reason of your
167
+ accepting any such warranty or additional liability.
168
+
169
+ END OF TERMS AND CONDITIONS
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # lita-lbcfg
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ # Copyright 2016 DoubleDutch, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'bundler/gem_tasks'
16
+ require 'rspec/core/rake_task'
17
+
18
+ RSpec::Core::RakeTask.new(:spec)
19
+
20
+ task default: :spec
@@ -0,0 +1,248 @@
1
+ # Copyright 2016 DoubleDutch, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'dd_spacecadet/error'
16
+ require 'lita-lbcfg/loader'
17
+
18
+ module Lita
19
+ module Handlers
20
+ class LBCfgErr < StandardError; end
21
+ class MissingKey < LBCfgErr; end
22
+
23
+ class Lbcfg < Handler
24
+ include LitaLBCfg::Loader
25
+
26
+ # {
27
+ # '<region>' => {
28
+ # '<environment>' => {
29
+ # '<balancer>' => [
30
+ # 0,
31
+ # 1
32
+ # ]
33
+ # }
34
+ # }
35
+ # }
36
+ config :lb_hash, default: {}
37
+ config :credentials, default: []
38
+
39
+ on :loaded, :set_up_spacecadet # from LitaLBCfg::Loader
40
+ on :unhandled_message, :not_found
41
+
42
+ # route for getting the status of the load balancer
43
+ route(
44
+ /^lbcfg\sstatus\s(?<region>\w+)\s(?<environment>\w+)\s(?<balancer>\w+)/,
45
+ :status,
46
+ command: true,
47
+ help: {
48
+ 'lbcfg status <region> <environment> <balancer>' => 'show the current status of the balancer in the region.environment'
49
+ }
50
+ )
51
+
52
+ # route for taking actions on a load balancer
53
+ route(
54
+ /^lbcfg\s(?<action>\w+)\s(?<region>\w+)\s(?<environment>\w+)\s(?<balancer>\w+)\s(?<node>[A-Za-z0-9\-]+)/,
55
+ :lbcfg_router,
56
+ command: true,
57
+ help: {
58
+ 'lbcfg enable <region> <environment> <balancer> <node>' => 'enable the backend within the load balancer',
59
+ 'lbcfg drain <region> <environment> <balancer> <node>' => 'drain the backend within the load balancer'
60
+ }
61
+ )
62
+
63
+ # handler for command_not_found replies
64
+ def not_found(h)
65
+ return unless h[:message].command?
66
+
67
+ dest = h[:message].source
68
+ msg = t('not_found', body: h[:message].body)
69
+
70
+ robot.send_message(dest, msg)
71
+ end
72
+
73
+ # handler function for getting the status of a load balancer
74
+ def status(response)
75
+ region, environment, balancer = split_params(response)
76
+
77
+ client = new_client("#{region}-#{environment}")
78
+
79
+ # get the IDs from the config and add it to the client
80
+ # respond with an error if we don't recognize the user input
81
+
82
+ begin
83
+ balancer_ids(region, environment, balancer).each { |id| client.add_lb(id) }
84
+ rescue MissingKey => e
85
+ return response.reply(e.to_s)
86
+ end
87
+
88
+ # get the status of the LBs
89
+ # do it within a begin block so we can catch
90
+ # the exceptions thrown by SpaceCadet
91
+ begin
92
+ # get the details for all LBs we care about
93
+ details = client.status
94
+
95
+ # if we don't have any details, bail
96
+ return response.reply(t('status.no_details', region: region, env: environment, balancer: balancer)) if details.empty?
97
+
98
+ # otherwise, render the proper output and print it
99
+ response.reply(render_template('status', output: render_status(details)))
100
+
101
+ rescue DoubleDutch::SpaceCadet::Error => e # catch all SpaceCadet errors
102
+ title = "An error has occured trying to get the status of #{region}.#{environment}.#{balancer}:"
103
+ reply = render_template('exception', title: title, exception: e.class, message: e.message)
104
+ response.reply(reply)
105
+
106
+ rescue StandardError => e # catch all remaining errors
107
+ title = "A generic excpetion has been caught trying to get the status of #{region}.#{environment}.#{balancer}:"
108
+ reply = render_template('exception', title: title, exception: e.class, message: e.message)
109
+ response.reply(reply)
110
+ end
111
+ end
112
+
113
+ # router for taking !lbcfg <action> commands and calling appropriate functions
114
+ def lbcfg_router(response)
115
+ # we don't want to allow updating LBs in private
116
+ # so if this is a DM, return a helpful message to tell them "no"
117
+ return response.reply(t('general.private_message')) if is_dm?(response)
118
+
119
+ action = response.match_data['action'].downcase
120
+
121
+ # if the action is unknown, return an error message
122
+ return response.reply(t('router.invalid_action', action: action)) unless %w(enable drain).include?(action)
123
+
124
+ # wrap the call to the appropriation action function in a rescue block
125
+ # both functions would need similar rescue statements, so let's do it here
126
+ begin
127
+ # call lbcfg_<action> on this Class
128
+ # so if action == 'drain' it'll call lbcfg_drain
129
+ response.reply(send("lbcfg_#{action}".to_sym, response))
130
+
131
+ rescue DoubleDutch::SpaceCadet::Error => e # catch all SpaceCadet errors
132
+ title = "An error has occured trying to #{action} the requested node:"
133
+ reply = render_template('exception', title: title, exception: e.class, message: e.message)
134
+ response.reply(reply)
135
+
136
+ rescue StandardError => e # catch all remaining errors
137
+ title = "A generic excpetion has been caught trying to #{action} the requested node:"
138
+ reply = render_template('exception', title: title, exception: e.class, message: e.message)
139
+ response.reply(reply)
140
+ end
141
+ end
142
+
143
+ private
144
+
145
+ def new_client(env)
146
+ DoubleDutch::SpaceCadet::LB.new(env)
147
+ end
148
+
149
+ def render_status(details)
150
+ out = ''
151
+
152
+ # loop over each LB returned and add its formatted status
153
+ # to the end of the `out` string
154
+ details.each do |lb|
155
+ out << "#{lb[:name]} (#{lb[:id]})\n"
156
+
157
+ lb[:nodes].each do |node|
158
+ out << " #{node[:name]} #{node[:condition]} #{node[:ip]} #{node[:id]}\n"
159
+ end
160
+
161
+ out << "---\n"
162
+ end
163
+
164
+ out
165
+ end
166
+
167
+ def lbcfg_drain(response)
168
+ region, environment, balancer, node = split_long_params(response)
169
+
170
+ client = new_client("#{region}-#{environment}")
171
+
172
+ begin
173
+ balancer_ids(region, environment, balancer).each { |id| client.add_lb(id) }
174
+ rescue MissingKey => e
175
+ return e.to_s
176
+ end
177
+
178
+ response.reply(t('drain.starting_update', node: node, region: region, env: environment, balancer: balancer))
179
+ client.update_node(node, :draining)
180
+ t('general.update_done', balancer: balancer)
181
+ end
182
+
183
+ def lbcfg_enable(response)
184
+ region, environment, balancer, node = split_long_params(response)
185
+
186
+ client = new_client("#{region}-#{environment}")
187
+
188
+ begin
189
+ balancer_ids(region, environment, balancer).each { |id| client.add_lb(id) }
190
+ rescue MissingKey => e
191
+ return e.message
192
+ end
193
+
194
+ response.reply(t('enable.starting_update', node: node, region: region, env: environment, balancer: balancer))
195
+ client.update_node(node, :enabled)
196
+ t('general.update_done', balancer: balancer)
197
+ end
198
+
199
+ # filter function to check if the message looks to be coming via DM
200
+ def is_dm?(response)
201
+ # both the :shell and :console adapters *always* appear to be DMs, so short-circuit those
202
+ return false if [:shell, :console].include?(Lita.config.robot.adapter)
203
+
204
+ # return whether it's a PM, or not, according to the adapter
205
+ response.message.source.private_message?
206
+ end
207
+
208
+ # take the response Object and parse out the match data
209
+ def split_params(response)
210
+ [
211
+ response.match_data['region'].downcase,
212
+ response.match_data['environment'].downcase,
213
+ response.match_data['balancer'].downcase,
214
+ ]
215
+ end
216
+
217
+ # take the response Object and parse out the match data
218
+ # for commands including a node to mutate
219
+ def split_long_params(response)
220
+ split_params(response) << response.match_data['node'].downcase
221
+ end
222
+
223
+ # get the load balancer IDs for the cluster we want to operate on
224
+ # this walks the configuration Hash to look for any missing keys
225
+ # we do the walking to provide a helpful error message to the consumer
226
+ def balancer_ids(region, env, balancer)
227
+ h = config.lb_hash
228
+
229
+ # if either the <region> or <region>.<environment> key is missing: bail out
230
+ raise MissingKey, t('general.missing_region', region: region) unless h.key?(region)
231
+ raise MissingKey, t('general.missing_env', region: region, env: env) unless h[region].key?(env)
232
+
233
+ # if the <region>.<environment>.<balancer> key is missing: bail out
234
+ unless h[region][env].key?(balancer)
235
+ raise MissingKey, t('general.missing_balancer', region: region, env: env, balancer: balancer)
236
+ end
237
+
238
+ # if the value for <region>.<environment>.<balancer> is not an Array: bail out
239
+ raise MissingKey, t('general.balancer_not_arr') unless h[region][env][balancer].kind_of?(Array)
240
+
241
+ # return the Array of IDs
242
+ h[region][env][balancer]
243
+ end
244
+
245
+ Lita.register_handler(self)
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,45 @@
1
+ # Copyright 2016 DoubleDutch, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'dd_spacecadet/config'
16
+
17
+ module LitaLBCfg
18
+ module Loader
19
+ # helper function to set up SpaceCadet
20
+ # meant to be used with the `on :loaded` event handler
21
+ # to use this, please add `include LitaLBCfg::Loader` to your handler
22
+ def set_up_spacecadet(_)
23
+ creds = _config.credentials
24
+
25
+ creds.each do |credentials|
26
+ _register_client(credentials)
27
+ end
28
+ end
29
+
30
+ # helper method for reliably pulling the right Lita config key
31
+ def _config
32
+ Lita.config.handlers.lbcfg
33
+ end
34
+
35
+ # helper method to register the client based on the credentials
36
+ def _register_client(credentials)
37
+ DoubleDutch::SpaceCadet::Config.register(
38
+ "#{credentials[:region]}-#{credentials[:env]}".downcase,
39
+ credentials[:username].downcase,
40
+ credentials[:key],
41
+ credentials[:region].upcase
42
+ )
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright 2016 DoubleDutch, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module LitaLBCfg
16
+ VERSION ||= '0.1.1'.freeze
17
+ end
data/lib/lita-lbcfg.rb ADDED
@@ -0,0 +1,28 @@
1
+ # Copyright 2016 DoubleDutch, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'lita-lbcfg/version'
16
+
17
+ require 'lita'
18
+
19
+ Lita.load_locales Dir[File.expand_path(
20
+ File.join("..", "..", "locales", "*.yml"), __FILE__
21
+ )]
22
+
23
+ require 'lita/handlers/lbcfg'
24
+
25
+ Lita::Handlers::Lbcfg.template_root File.expand_path(
26
+ File.join("..", "..", "templates"),
27
+ __FILE__
28
+ )
@@ -0,0 +1,44 @@
1
+ # Copyright 2016 DoubleDutch, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
16
+
17
+ require 'lita-lbcfg/version'
18
+
19
+ Gem::Specification.new do |s|
20
+ s.name = 'lita-lbcfg'
21
+ s.summary = 'Lita handler for manipulating Rackspace Cloud Load Balancers'
22
+ s.author = 'DoubleDutch Engineering Operations'
23
+ s.email = 'engops@doubledutch.me'
24
+ s.license = 'Apache 2.0'
25
+ s.version = LitaLBCfg::VERSION
26
+ s.required_ruby_version = '~> 2.3'
27
+ s.date = Time.now.strftime('%Y-%m-%d')
28
+ s.homepage = 'https://github.com/DoubleDutch/lita-lbcfg'
29
+ s.description = 'Lita handler for manipulating Rackspace Cloud Load Balancers using the dd_spacecadet gem'
30
+
31
+ s.test_files = `git ls-files spec/*`.split
32
+ s.files = `git ls-files`.split
33
+
34
+ # not sure we want this showing up in the Lita plugin listings online
35
+ # s.metadata = { 'lita_plugin_type' => 'handler' }
36
+
37
+ s.add_development_dependency 'rake', '~> 11.2.2'
38
+ s.add_development_dependency 'rspec', '~> 3.5.0'
39
+ s.add_development_dependency 'rubocop', '~> 0.42.0'
40
+ s.add_development_dependency 'irbtools', '~> 2.0.1'
41
+
42
+ s.add_runtime_dependency 'lita', '~> 4.7.0'
43
+ s.add_runtime_dependency 'dd_spacecadet', '~> 0.2.0'
44
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,20 @@
1
+ en:
2
+ lita:
3
+ handlers:
4
+ lbcfg:
5
+ general:
6
+ private_message: "I'm a huge fan of visibility around infrastructure changes. Let's do this in a public channel! :)"
7
+ missing_region: "Region '%{region}' does not exist in the config"
8
+ missing_env: "Environment '%{env}' does not exist in the config for %{region}"
9
+ missing_balancer: "Balancer '%{balancer}' does not exist in the config for %{region}.%{env}"
10
+ balancer_not_arr: "Balancer value in the config is not an Array"
11
+ update_done: "Finished updating the %{balancer} load balancer"
12
+ status:
13
+ no_details: "No details were returned for %{region}.%{env}.%{balancer} load balancer"
14
+ enable:
15
+ starting_update: "Enabling %{node} within the %{region}.%{env}.%{balancer} balancer..."
16
+ drain:
17
+ starting_update: "Draining %{node} within the %{region}.%{env}.%{balancer} balancer..."
18
+ router:
19
+ invalid_action: "%{action} is not a valid action, please use 'drain' or 'enable'"
20
+ not_found: "The command did not match any known routes, please try again. ('%{body}')"
@@ -0,0 +1,21 @@
1
+ # Copyright 2016 DoubleDutch, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'lita-lbcfg'
16
+ require 'dd_spacecadet'
17
+ require 'lita/rspec'
18
+
19
+ # A compatibility mode is provided for older plugins upgrading from Lita 3. Since this plugin
20
+ # was generated with Lita 4, the compatibility mode should be left disabled.
21
+ Lita.version_3_compatibility_mode = false
@@ -0,0 +1,408 @@
1
+ # Copyright 2016 DoubleDutch, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'spec_helper'
16
+
17
+ def action_to_verb(action)
18
+ case action.downcase
19
+ when 'drain'
20
+ 'Draining'
21
+ when 'enable'
22
+ 'Enabling'
23
+ else
24
+ '???'
25
+ end
26
+ end
27
+
28
+ def action_to_sym(action)
29
+ case action.downcase
30
+ when 'drain'
31
+ :draining
32
+ when 'enable'
33
+ :enabled
34
+ else
35
+ :UNKNOWN_THINGY
36
+ end
37
+ end
38
+
39
+
40
+ describe Lita::Handlers::Lbcfg, lita_handler: true do
41
+ it { is_expected.to route_command('lbcfg status sf test lita').to(:status) }
42
+ it { is_expected.to route_command('lbcfg drain sf test lita us-lita-01').to(:lbcfg_router) }
43
+ it { is_expected.to route_command('lbcfg enable sf test lita us-lita-01').to(:lbcfg_router) }
44
+ it { is_expected.to route_command('lbcfg invalid sf test lita us-lita-01').to(:lbcfg_router) }
45
+
46
+ let(:happy_status) do
47
+ [
48
+ {
49
+ name: 'test-lb01',
50
+ id: 42,
51
+ nodes: [
52
+ { name: 'app01', condition: 'ENABLED', ip: '127.0.0.1', id: 142 },
53
+ { name: 'app02', condition: 'ENABLED', ip: '127.0.0.2', id: 184 }
54
+ ]
55
+ },
56
+ {
57
+ name: 'test-lb02',
58
+ id: 84,
59
+ nodes: [
60
+ { name: 'app01', condition: 'ENABLED', ip: '127.0.0.1', id: 242 },
61
+ { name: 'app02', condition: 'ENABLED', ip: '127.0.0.2', id: 284 }
62
+ ]
63
+ }
64
+ ]
65
+ end
66
+
67
+ let(:happy_cfg) do
68
+ {
69
+ 'sf' => {
70
+ 'test' => {
71
+ 'lita' => [42, 84]
72
+ }
73
+ }
74
+ }
75
+ end
76
+
77
+ let(:sad_region_cfg) do
78
+ {
79
+ 'sfx' => {
80
+ 'test' => {
81
+ 'lita' => [42, 84]
82
+ }
83
+ }
84
+ }
85
+ end
86
+
87
+ let(:sad_env_cfg) do
88
+ {
89
+ 'sf' => {
90
+ 'testx' => {
91
+ 'lita' => [42, 84]
92
+ }
93
+ }
94
+ }
95
+ end
96
+
97
+ let(:sad_balancer_cfg) do
98
+ {
99
+ 'sf' => {
100
+ 'test' => {
101
+ 'litax' => [42, 84]
102
+ }
103
+ }
104
+ }
105
+ end
106
+
107
+ describe '#config' do
108
+ it 'should set the lb_hash key to an empty Hash' do
109
+ expect(Lita.config.handlers.lbcfg.lb_hash).to be_a(Hash)
110
+ expect(Lita.config.handlers.lbcfg.lb_hash).to be_empty
111
+ end
112
+
113
+ it 'should set the credentials key to an empty Array' do
114
+ expect(Lita.config.handlers.lbcfg.credentials).to be_an(Array)
115
+ expect(Lita.config.handlers.lbcfg.credentials).to be_empty
116
+ end
117
+ end
118
+
119
+ ####
120
+ # Shared Tests
121
+ ####
122
+ shared_examples 'happy path' do |opts|
123
+ let(:action) { opts[:action] }
124
+ let(:verb) { action_to_verb(action) }
125
+ let(:symbol) { action_to_sym(action) }
126
+
127
+ before do
128
+ @lb_client = double('DoubleDutch::SpaceCadet::LB', status: happy_status, add_lb: nil, update_node: nil)
129
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:new_client)
130
+ .with('sf-test')
131
+ .and_return(@lb_client)
132
+ end
133
+
134
+ after do
135
+ allow_any_instance_of(DoubleDutch::SpaceCadet::LB).to receive(:new_client).and_call_original
136
+ end
137
+
138
+ it 'should create a new DoubleDutch::SpaceCadet::LB config and add the IDs to it' do
139
+ expect(@lb_client).to receive(:add_lb).with(42)
140
+ expect(@lb_client).to receive(:add_lb).with(84)
141
+ send_command("lbcfg #{action} sf test lita app01")
142
+ end
143
+
144
+ it 'should call the update with the proper values' do
145
+ expect(@lb_client).to receive(:update_node).with('app01', symbol)
146
+ send_command("lbcfg #{action} sf test lita app01")
147
+ end
148
+
149
+ it 'should respond with the correct response' do
150
+ send_command("lbcfg #{action} sf test lita app01")
151
+ expect(replies.size).to eql(2)
152
+ expect(replies[0]).to eql("#{verb} app01 within the sf.test.lita balancer...")
153
+ expect(replies[1]).to eql('Finished updating the lita load balancer')
154
+ end
155
+ end
156
+
157
+ shared_examples 'unsafe or inconsistent lb' do |opts|
158
+ let(:action) { opts[:action] }
159
+ let(:verb) { action_to_verb(action) }
160
+ let(:symbol) { action_to_sym(action) }
161
+
162
+ context 'when the LB is unsafe' do
163
+ before do
164
+ @lb_client = double('DoubleDutch::SpaceCadet::LB', status: happy_status, add_lb: nil, update_node: nil)
165
+ allow(@lb_client).to receive(:update_node).and_raise(DoubleDutch::SpaceCadet::LBUnsafe, 'testBody')
166
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:new_client)
167
+ .with('sf-test')
168
+ .and_return(@lb_client)
169
+ end
170
+
171
+ after do
172
+ allow_any_instance_of(DoubleDutch::SpaceCadet::LB).to receive(:new_client).and_call_original
173
+ end
174
+
175
+ it 'should create a new DoubleDutch::SpaceCadet::LB config and add the IDs to it' do
176
+ expect(@lb_client).to receive(:add_lb).with(42)
177
+ expect(@lb_client).to receive(:add_lb).with(84)
178
+ send_command("lbcfg #{action} sf test lita app01")
179
+ end
180
+
181
+ it 'should call the update with the proper values' do
182
+ expect(@lb_client).to receive(:update_node).with('app01', symbol)
183
+ send_command("lbcfg #{action} sf test lita app01")
184
+ end
185
+
186
+ it 'should respond with the correct response' do
187
+ send_command("lbcfg #{opts[:action]} sf test lita app01")
188
+ expect(replies.size).to eql(2)
189
+ expect(replies[0]).to eql("#{verb} app01 within the sf.test.lita balancer...")
190
+ expect(replies[1]).to eql(
191
+ "An error has occured trying to #{action} the requested node:\n" +
192
+ 'DoubleDutch::SpaceCadet::LBUnsafe: testBody'
193
+ )
194
+ end
195
+ end
196
+
197
+ context 'when the LB is inconsistent' do
198
+ before do
199
+ @lb_client = double('DoubleDutch::SpaceCadet::LB', status: happy_status, add_lb: nil, update_node: nil)
200
+ allow(@lb_client).to receive(:update_node).and_raise(DoubleDutch::SpaceCadet::LBInconsistentState, 'testBody')
201
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:new_client)
202
+ .with('sf-test')
203
+ .and_return(@lb_client)
204
+ end
205
+
206
+ after do
207
+ allow_any_instance_of(DoubleDutch::SpaceCadet::LB).to receive(:new_client).and_call_original
208
+ end
209
+
210
+ it 'should create a new DoubleDutch::SpaceCadet::LB config and add the IDs to it' do
211
+ expect(@lb_client).to receive(:add_lb).with(42)
212
+ expect(@lb_client).to receive(:add_lb).with(84)
213
+ send_command("lbcfg #{action} sf test lita app01")
214
+ end
215
+
216
+ it 'should call the update with the proper values' do
217
+ expect(@lb_client).to receive(:update_node).with('app01', symbol)
218
+ send_command("lbcfg #{action} sf test lita app01")
219
+ end
220
+
221
+ it 'should respond with the correct response' do
222
+ send_command("lbcfg #{opts[:action]} sf test lita app01")
223
+ expect(replies.size).to eql(2)
224
+ expect(replies[0]).to eql("#{verb} app01 within the sf.test.lita balancer...")
225
+ expect(replies[1]).to eql(
226
+ "An error has occured trying to #{action} the requested node:\n" +
227
+ 'DoubleDutch::SpaceCadet::LBInconsistentState: testBody'
228
+ )
229
+ end
230
+ end
231
+ end
232
+
233
+ shared_examples 'action bad bot config' do |opts|
234
+ context "when the region isn't found in the bot config" do
235
+ before(:each) do
236
+ lbcfg = double('Lita::', lb_hash: sad_region_cfg, credentials: [])
237
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:config).and_return(lbcfg)
238
+ end
239
+
240
+ it 'should return a proper error' do
241
+ send_command("lbcfg #{opts[:action]} sf test lita app01")
242
+ expect(replies.last).to eql("Region 'sf' does not exist in the config")
243
+ end
244
+ end
245
+
246
+ context "when the env isn't found in the bot config" do
247
+ before(:each) do
248
+ lbcfg = double('Lita::', lb_hash: sad_env_cfg, credentials: [])
249
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:config).and_return(lbcfg)
250
+ end
251
+
252
+ it 'should return a proper error' do
253
+ send_command("lbcfg #{opts[:action]} sf test lita app01")
254
+ expect(replies.last).to eql("Environment 'test' does not exist in the config for sf")
255
+ end
256
+ end
257
+
258
+ context "when the balancer isn't found in the bot config" do
259
+ before(:each) do
260
+ lbcfg = double('Lita::', lb_hash: sad_balancer_cfg, credentials: [])
261
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:config).and_return(lbcfg)
262
+ end
263
+
264
+ it 'should return a proper error' do
265
+ send_command("lbcfg #{opts[:action]} sf test lita app01")
266
+ expect(replies.last).to eql("Balancer 'lita' does not exist in the config for sf.test")
267
+ end
268
+ end
269
+ end
270
+
271
+ ####
272
+ # Handlers
273
+ ####
274
+
275
+ context 'invalid command' do
276
+ it 'should respond with an error' do
277
+ send_command('unknown bot command')
278
+ expect(replies.last).to eql(
279
+ "The command did not match any known routes, please try again. ('unknown bot command')"
280
+ )
281
+ end
282
+ end
283
+
284
+ # action router
285
+ context do
286
+ before(:each) do
287
+ lbcfg = double('Lita::', lb_hash: happy_cfg, credentials: [])
288
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:config).and_return(lbcfg)
289
+ end
290
+
291
+ describe '.lbcfg_drain' do
292
+ it_behaves_like 'happy path', action: 'drain'
293
+ it_behaves_like 'action bad bot config', action: 'drain'
294
+ it_behaves_like 'unsafe or inconsistent lb', action: 'drain'
295
+ end
296
+
297
+ describe '.lbcfg_enable' do
298
+ it_behaves_like 'happy path', action: 'enable'
299
+ it_behaves_like 'action bad bot config', action: 'enable'
300
+ it_behaves_like 'unsafe or inconsistent lb', action: 'enable'
301
+ end
302
+
303
+ context 'invalid command' do
304
+ it 'returns an error message' do
305
+ send_command('lbcfg invalid sf test lita app01')
306
+ expect(replies.last).to eql("invalid is not a valid action, please use 'drain' or 'enable'")
307
+ end
308
+ end
309
+ end
310
+
311
+ describe '.status' do
312
+ let(:happy_data) do
313
+ [
314
+ {
315
+ name: 'test-lb01',
316
+ id: 42,
317
+ nodes: [
318
+ { name: 'app01', condition: 'ENABLED', ip: '127.0.0.1', id: 142 },
319
+ { name: 'app02', condition: 'DRAINING', ip: '127.0.0.2', id: 184 }
320
+ ]
321
+ },
322
+ {
323
+ name: 'test-lb02',
324
+ id: 84,
325
+ nodes: [
326
+ { name: 'app01', condition: 'ENABLED', ip: '127.0.0.1', id: 242 },
327
+ { name: 'app02', condition: 'DRAINING', ip: '127.0.0.2', id: 284 }
328
+ ]
329
+ },
330
+ ]
331
+ end
332
+
333
+ before(:each) do
334
+ lbcfg = double('Lita::', lb_hash: happy_cfg, credentials: [])
335
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:config).and_return(lbcfg)
336
+ end
337
+
338
+ context 'happy-path' do
339
+ before do
340
+ @lb_client = double('DoubleDutch::SpaceCadet::LB', status: happy_data, add_lb: nil)
341
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:new_client)
342
+ .with('sf-test')
343
+ .and_return(@lb_client)
344
+ end
345
+
346
+ after do
347
+ allow_any_instance_of(DoubleDutch::SpaceCadet::LB).to receive(:new_client).and_call_original
348
+ end
349
+
350
+ it 'should create a new DoubleDutch::SpaceCadet::LB config and add the IDs to it' do
351
+ expect(@lb_client).to receive(:add_lb).with(42)
352
+ expect(@lb_client).to receive(:add_lb).with(84)
353
+ send_command('lbcfg status sf test lita')
354
+ end
355
+
356
+ it 'should render the proper response' do
357
+ send_command('lbcfg status sf test lita')
358
+ expect(replies.last).to eql(
359
+ 'test-lb01 (42)
360
+ app01 ENABLED 127.0.0.1 142
361
+ app02 DRAINING 127.0.0.2 184
362
+ ---
363
+ test-lb02 (84)
364
+ app01 ENABLED 127.0.0.1 242
365
+ app02 DRAINING 127.0.0.2 284
366
+ ---
367
+ '
368
+ )
369
+ end
370
+ end
371
+
372
+ context "when the region isn't found in the bot config" do
373
+ before(:each) do
374
+ lbcfg = double('Lita::', lb_hash: sad_region_cfg, credentials: [])
375
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:config).and_return(lbcfg)
376
+ end
377
+
378
+ it 'should return a proper error' do
379
+ send_command('lbcfg status sf test lita')
380
+ expect(replies.last).to eql("Region 'sf' does not exist in the config")
381
+ end
382
+ end
383
+
384
+ context "when the env isn't found in the bot config" do
385
+ before(:each) do
386
+ lbcfg = double('Lita::', lb_hash: sad_env_cfg, credentials: [])
387
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:config).and_return(lbcfg)
388
+ end
389
+
390
+ it 'should return a proper error' do
391
+ send_command('lbcfg status sf test lita')
392
+ expect(replies.last).to eql("Environment 'test' does not exist in the config for sf")
393
+ end
394
+ end
395
+
396
+ context "when the balancer isn't found in the bot config" do
397
+ before(:each) do
398
+ lbcfg = double('Lita::', lb_hash: sad_balancer_cfg, credentials: [])
399
+ allow_any_instance_of(Lita::Handlers::Lbcfg).to receive(:config).and_return(lbcfg)
400
+ end
401
+
402
+ it 'should return a proper error' do
403
+ send_command('lbcfg status sf test lita')
404
+ expect(replies.last).to eql("Balancer 'lita' does not exist in the config for sf.test")
405
+ end
406
+ end
407
+ end
408
+ end
@@ -0,0 +1,3 @@
1
+ <%= @title %>
2
+
3
+ <%= @exception %>: <%= @message %>
@@ -0,0 +1,3 @@
1
+ <%= @title %>
2
+
3
+ `<%= @exception %>: <%= @message %>`
@@ -0,0 +1 @@
1
+ <%= @output %>
@@ -0,0 +1,3 @@
1
+ ```
2
+ <%= @output %>
3
+ ```
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lita-lbcfg
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - DoubleDutch Engineering Operations
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 11.2.2
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 11.2.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.5.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.5.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.42.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.42.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: irbtools
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.0.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.0.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: lita
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 4.7.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 4.7.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: dd_spacecadet
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.2.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.2.0
97
+ description: Lita handler for manipulating Rackspace Cloud Load Balancers using the
98
+ dd_spacecadet gem
99
+ email: engops@doubledutch.me
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - Gemfile
106
+ - LICENSE
107
+ - README.md
108
+ - Rakefile
109
+ - lib/lita-lbcfg.rb
110
+ - lib/lita-lbcfg/loader.rb
111
+ - lib/lita-lbcfg/version.rb
112
+ - lib/lita/handlers/lbcfg.rb
113
+ - lita-lbcfg.gemspec
114
+ - locales/en.yml
115
+ - spec/spec_helper.rb
116
+ - spec/unit/lita/handlers/lbcfg_spec.rb
117
+ - templates/exception.erb
118
+ - templates/exception.slack.erb
119
+ - templates/status.erb
120
+ - templates/status.slack.erb
121
+ homepage: https://github.com/DoubleDutch/lita-lbcfg
122
+ licenses:
123
+ - Apache 2.0
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - "~>"
132
+ - !ruby/object:Gem::Version
133
+ version: '2.3'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.5.1
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Lita handler for manipulating Rackspace Cloud Load Balancers
145
+ test_files:
146
+ - spec/spec_helper.rb
147
+ - spec/unit/lita/handlers/lbcfg_spec.rb