lita-puppet 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +19 -0
- data/.travis.yml +7 -0
- data/Gemfile +1 -1
- data/README.md +5 -0
- data/Rakefile +5 -3
- data/lib/lita-puppet.rb +8 -8
- data/lib/lita/handlers/puppet.rb +80 -120
- data/lib/utils/lita_puppet/puppetdb.rb +39 -0
- data/lib/utils/lita_puppet/ssh.rb +47 -0
- data/lib/utils/lita_puppet/text.rb +42 -0
- data/lita-puppet.gemspec +21 -18
- data/locales/en.yml +40 -0
- data/spec/lita/handlers/puppet_spec.rb +20 -1
- data/spec/spec_helper.rb +9 -2
- metadata +48 -4
- data/lib/utils/puppetdb.rb +0 -31
- data/lib/utils/ssh.rb +0 -46
- data/lib/utils/text.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d628affc13749d488e0425795d1c186529d6aa1
|
4
|
+
data.tar.gz: 9d6b10693e3ecccab9e6250b5a6dda87fc7abc25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33df42ee1cb7799138a0b829423cbe5d6316da8eff92f0c856695bf5143e8f4214f5ebab06291d9297e2524628ef6a09ae021a77fece2cd25b3696cbe29b2709
|
7
|
+
data.tar.gz: a7e34d69a70957729c495f40bef7d835d140a99a722bbdf57e16d192758f741d9a49a0a887b7727e458a4352b65f5e577f41a8860fe3195f3564dbf7a2b9b026
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Documentation:
|
2
|
+
Exclude:
|
3
|
+
- lib/lita/handlers/puppet.rb
|
4
|
+
|
5
|
+
FileName:
|
6
|
+
Exclude:
|
7
|
+
- lib/lita-puppet.rb
|
8
|
+
|
9
|
+
Metrics/LineLength:
|
10
|
+
Max: 100
|
11
|
+
|
12
|
+
Metrics/AbcSize:
|
13
|
+
Max: 22
|
14
|
+
|
15
|
+
Metrics/MethodLength:
|
16
|
+
Max: 20
|
17
|
+
|
18
|
+
Metrics/ClassLength:
|
19
|
+
Max: 150
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# lita-puppet
|
2
2
|
|
3
|
+
[](https://travis-ci.org/knuedge/lita-puppet)
|
4
|
+
[](https://tldrlegal.com/license/mit-license)
|
5
|
+
[](https://rubygems.org/gems/lita-puppet)
|
6
|
+
[](https://codeclimate.com/github/knuedge/lita-puppet)
|
7
|
+
|
3
8
|
A [Lita](https://www.lita.io/) handler plugin for some basic [Puppet](https://puppet.com/) operations.
|
4
9
|
|
5
10
|
## Installation
|
data/Rakefile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
3
4
|
|
4
5
|
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
RuboCop::RakeTask.new(:rubocop)
|
5
7
|
|
6
|
-
task default: :spec
|
8
|
+
task default: [:spec, :rubocop]
|
data/lib/lita-puppet.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
require
|
1
|
+
require 'lita'
|
2
2
|
require 'rye'
|
3
3
|
require 'timeout'
|
4
4
|
require 'puppetdb'
|
5
5
|
|
6
6
|
Lita.load_locales Dir[File.expand_path(
|
7
|
-
File.join(
|
7
|
+
File.join('..', '..', 'locales', '*.yml'), __FILE__
|
8
8
|
)]
|
9
9
|
|
10
|
-
require 'utils/puppetdb'
|
11
|
-
require 'utils/ssh'
|
12
|
-
require 'utils/text'
|
13
|
-
require
|
10
|
+
require 'utils/lita_puppet/puppetdb'
|
11
|
+
require 'utils/lita_puppet/ssh'
|
12
|
+
require 'utils/lita_puppet/text'
|
13
|
+
require 'lita/handlers/puppet'
|
14
14
|
|
15
15
|
Lita::Handlers::Puppet.template_root File.expand_path(
|
16
|
-
File.join(
|
17
|
-
|
16
|
+
File.join('..', '..', 'templates'),
|
17
|
+
__FILE__
|
18
18
|
)
|
data/lib/lita/handlers/puppet.rb
CHANGED
@@ -3,225 +3,185 @@ module Lita
|
|
3
3
|
class Puppet < Handler
|
4
4
|
namespace 'Puppet'
|
5
5
|
config :master_hostname, required: true, type: String
|
6
|
-
config :ssh_user, required: false, type: String
|
7
|
-
config :control_repo_path, required: false, type: String
|
6
|
+
config :ssh_user, required: false, type: String, default: 'lita'
|
7
|
+
config :control_repo_path, required: false, type: String, default: '/opt/puppet/control'
|
8
8
|
config :puppetdb_url, required: false, type: String
|
9
9
|
|
10
10
|
route(
|
11
11
|
/(puppet|pp)(\s+agent)?\s+(run)(\s+on)?\s+(\S+)/i,
|
12
12
|
:puppet_agent_run,
|
13
13
|
command: true,
|
14
|
-
help: {
|
15
|
-
"puppet agent run on <host>" => "Run the puppet agent on <host>."
|
16
|
-
}
|
14
|
+
help: { t('help.puppet_agent_run.syntax') => t('help.puppet_agent_run.desc') }
|
17
15
|
)
|
18
16
|
|
19
17
|
route(
|
20
18
|
/(puppet|pp)\s+(cert)\s+(clean)\s+(\S+)/i,
|
21
19
|
:cert_clean,
|
22
20
|
command: true,
|
23
|
-
help: {
|
24
|
-
"puppet cert clean <host>" => "Remove all traces of the SSL cert for <host> on the Puppet Master."
|
25
|
-
}
|
21
|
+
help: { t('help.cert_clean.syntax') => t('help.cert_clean.desc') }
|
26
22
|
)
|
27
23
|
|
28
24
|
route(
|
29
25
|
/(puppet|pp)\s+(catalog|node)\s+(\S+)\s+(profiles)/i,
|
30
26
|
:node_profiles,
|
31
27
|
command: true,
|
32
|
-
help: {
|
33
|
-
"puppet catalog <host> profiles" => "Query PuppetDB to get a list of all roles and profiles applied to <host>."
|
34
|
-
}
|
28
|
+
help: { t('help.node_profiles.syntax') => t('help.node_profiles.desc') }
|
35
29
|
)
|
36
30
|
|
37
31
|
route(
|
38
32
|
/(puppet|pp)\s+(class)\s+(nodes)\s+(\S+)/i,
|
39
33
|
:nodes_with_class,
|
40
34
|
command: true,
|
41
|
-
help: {
|
42
|
-
"puppet class nodes <class>" => "Query PuppetDB to get a list of all nodes containing a class."
|
43
|
-
}
|
35
|
+
help: { t('help.nodes_with_class.syntax') => t('help.nodes_with_class.desc') }
|
44
36
|
)
|
45
37
|
|
46
38
|
route(
|
47
39
|
/(puppet|pp)\s+(r10k|deploy)(\s+(\S+)(\s+(\S+))?)?/i,
|
48
40
|
:r10k_deploy,
|
49
41
|
command: true,
|
50
|
-
help: {
|
51
|
-
"puppet r10k [env [module]]" => "Deploy the latest puppet code on the puppet master via r10k, optionally specifying an environment, and possibly a module."
|
52
|
-
}
|
42
|
+
help: { t('help.r10k_deploy.syntax') => t('help.r10k_deploy.desc') }
|
53
43
|
)
|
54
44
|
|
55
|
-
include ::Utils::PuppetDB
|
56
|
-
include ::Utils::SSH
|
57
|
-
include ::Utils::Text
|
45
|
+
include ::Utils::LitaPuppet::PuppetDB
|
46
|
+
include ::Utils::LitaPuppet::SSH
|
47
|
+
include ::Utils::LitaPuppet::Text
|
58
48
|
|
59
49
|
def cert_clean(response)
|
60
50
|
cert = response.matches[0][3]
|
61
|
-
user = config.ssh_user || 'lita'
|
62
|
-
username = friendly_name(response.user.name)
|
63
51
|
|
64
|
-
response.
|
52
|
+
response.reply_with_mention(t('replies.cert_clean.working'))
|
65
53
|
|
66
|
-
result =
|
67
|
-
server.cd '/tmp'
|
68
|
-
# Need to use sudo
|
69
|
-
server.enable_sudo
|
70
|
-
# scary...
|
71
|
-
server.disable_safe_mode
|
72
|
-
|
73
|
-
server.execute "puppet cert clean #{cert} 2>&1"
|
74
|
-
end
|
54
|
+
result = cert_clean_result(config.master_hostname, config.ssh_user, cert)
|
75
55
|
|
76
56
|
if result[:exception]
|
77
|
-
|
78
|
-
|
79
|
-
|
57
|
+
fail_message t('replies.cert_clean.failure'), result[:exception].message
|
58
|
+
else
|
59
|
+
success_message(
|
60
|
+
t('replies.cert_clean.success'),
|
61
|
+
(result[:stdout] + result[:stderr]).join("\n")
|
62
|
+
)
|
80
63
|
end
|
81
|
-
|
82
|
-
# build a reply
|
83
|
-
response.reply("#{username}, your `puppet cert clean` is all done!")
|
84
|
-
reply_content = [result[:stdout].join("\n"), result[:stderr].join("\n")].join("\n")
|
85
|
-
response.reply as_code(reply_content)
|
86
64
|
end
|
87
65
|
|
88
66
|
def puppet_agent_run(response)
|
89
67
|
host = response.matches[0][4]
|
90
|
-
user = config.ssh_user || 'lita'
|
91
|
-
username = friendly_name(response.user.name)
|
92
68
|
|
93
|
-
response.
|
69
|
+
response.reply_with_mention(t('replies.puppet_agent_run.working'))
|
94
70
|
|
95
|
-
result =
|
96
|
-
server.cd '/tmp'
|
97
|
-
|
98
|
-
# Need to use sudo from here on
|
99
|
-
server.enable_sudo
|
100
|
-
|
101
|
-
# scary...
|
102
|
-
server.disable_safe_mode
|
103
|
-
|
104
|
-
# build up the command
|
105
|
-
command = 'puppet agent'
|
106
|
-
command << ' --onetime --verbose --no-daemonize'
|
107
|
-
command << ' --no-usecacheonfailure'
|
108
|
-
command << ' --no-splay --show_diff 2>&1'
|
109
|
-
|
110
|
-
server.execute command
|
111
|
-
end
|
71
|
+
result = simple_ssh_command(host, config.ssh_user, agent_command)
|
112
72
|
|
113
73
|
# build a reply
|
114
|
-
if
|
115
|
-
|
116
|
-
# Send the standard out, but strip off the bash color code stuff...
|
117
|
-
response.reply as_code(result[:stdout].join("\n"))
|
74
|
+
if result[:exception]
|
75
|
+
fail_message t('replies.puppet_agent_run.failure'), result[:exception].message
|
118
76
|
else
|
119
|
-
|
120
|
-
|
77
|
+
success_message(
|
78
|
+
t('replies.puppet_agent_run.success', status: result[:exit_status]),
|
79
|
+
result[:stdout].join("\n")
|
80
|
+
)
|
121
81
|
end
|
122
82
|
end
|
123
83
|
|
124
84
|
def node_profiles(response)
|
125
85
|
host = response.matches[0][2]
|
126
86
|
url = config.puppetdb_url
|
127
|
-
username = friendly_name(response.user.name)
|
128
87
|
|
129
88
|
unless url
|
130
|
-
|
131
|
-
cant_reply << "Edit my config and add `config.handlers.puppet.puppetdb_url`."
|
132
|
-
response.reply(cant_reply)
|
89
|
+
response.reply(t('replies.node_profiles.notconf'))
|
133
90
|
return false
|
134
91
|
end
|
135
92
|
|
136
|
-
response.
|
93
|
+
response.reply_with_mention(t('replies.node_profiles.working'))
|
137
94
|
|
138
95
|
profiles = node_roles_and_profiles(url, host)
|
96
|
+
|
139
97
|
if profiles.is_a? String
|
140
|
-
|
141
|
-
return false
|
98
|
+
fail_message t('replies.node_profiles.failure', error: profiles)
|
142
99
|
else
|
143
|
-
|
144
|
-
response.reply as_code(profiles.join("\n"))
|
100
|
+
success_message t('replies.node_profiles.success', host: host), profiles.join("\n")
|
145
101
|
end
|
146
102
|
end
|
147
103
|
|
148
104
|
def nodes_with_class(response)
|
149
105
|
puppet_class = response.matches[0][3]
|
150
106
|
url = config.puppetdb_url
|
151
|
-
username = friendly_name(response.user.name)
|
152
107
|
|
153
108
|
unless url
|
154
|
-
|
155
|
-
cant_reply << "Edit my config and add `config.handlers.puppet.puppetdb_url`."
|
156
|
-
response.reply(cant_reply)
|
109
|
+
response.reply(t('replies.nodes_with_class.notconf'))
|
157
110
|
return false
|
158
111
|
end
|
159
112
|
|
160
|
-
response.
|
161
|
-
|
162
|
-
|
163
|
-
puppet_classes = class_nodes(url, search)
|
113
|
+
response.reply_with_mention(t('replies.nodes_with_class.working'))
|
114
|
+
|
115
|
+
puppet_classes = class_nodes(url, class_camel(puppet_class))
|
164
116
|
if puppet_classes.empty?
|
165
|
-
|
166
|
-
return false
|
117
|
+
fail_message t('replies.nodes_with_class.failure', pclass: puppet_class)
|
167
118
|
else
|
168
|
-
|
169
|
-
|
119
|
+
success_message(
|
120
|
+
t('replies.nodes_with_class.success', pclass: puppet_class),
|
121
|
+
puppet_classes.join("\n")
|
122
|
+
)
|
170
123
|
end
|
171
124
|
end
|
172
125
|
|
173
|
-
|
126
|
+
# rubocop:disable Metrics/AbcSize
|
174
127
|
def r10k_deploy(response)
|
175
128
|
environment = response.matches[0][3]
|
176
129
|
mod = response.matches[0][5]
|
177
|
-
|
178
|
-
user = config.ssh_user || 'lita'
|
179
|
-
username = friendly_name(response.user.name)
|
130
|
+
user = config.ssh_user
|
180
131
|
|
181
|
-
response.
|
132
|
+
response.reply_with_mention(t('replies.r10k_deploy.working'))
|
182
133
|
|
183
|
-
result1 =
|
184
|
-
# Need to use sudo
|
185
|
-
server.enable_sudo
|
186
|
-
server[control_repo].git :pull
|
187
|
-
end
|
134
|
+
result1 = r10k_git_result(config.master_hostname, user, config.control_repo_path)
|
188
135
|
|
189
136
|
if result1[:exception]
|
190
|
-
|
191
|
-
response.reply as_code(result1[:exception].message)
|
137
|
+
fail_message t('replies.r10k_deploy.gitfail'), result1[:exception].message
|
192
138
|
return false
|
193
139
|
end
|
194
140
|
|
195
|
-
result2 =
|
141
|
+
result2 = simple_ssh_command(config.master_hostname, user, r10k_command(environment, mod))
|
142
|
+
|
143
|
+
if result2[:exception]
|
144
|
+
fail_message t('replies.r10k_deploy.pupfail'), result2[:exception].message
|
145
|
+
else
|
146
|
+
success_message(
|
147
|
+
t('replies.r10k_deploy.success'),
|
148
|
+
[result1[:stdout].join("\n"), result2[:stderr].join("\n")].join("\n")
|
149
|
+
)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def fail_message(message, data = nil)
|
156
|
+
response.reply_with_mention(message)
|
157
|
+
response.reply(as_code(data)) if data
|
158
|
+
end
|
159
|
+
|
160
|
+
alias success_message fail_message
|
161
|
+
|
162
|
+
def simple_ssh_command(host, user, command, timeout = 300)
|
163
|
+
over_ssh(host: host, user: user, timeout: timeout) do |server|
|
164
|
+
server.cd '/tmp'
|
196
165
|
# Need to use sudo
|
197
166
|
server.enable_sudo
|
198
167
|
# scary...
|
199
168
|
server.disable_safe_mode
|
200
169
|
|
201
|
-
command = "r10k deploy"
|
202
|
-
if environment && mod
|
203
|
-
command << ' module'
|
204
|
-
command << " -e #{environment}"
|
205
|
-
command << " #{mod}"
|
206
|
-
command << " -v"
|
207
|
-
else
|
208
|
-
command << " environment"
|
209
|
-
command << " #{environment}" if environment
|
210
|
-
command << ' -pv'
|
211
|
-
end
|
212
170
|
server.execute command
|
213
171
|
end
|
172
|
+
end
|
214
173
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
end
|
174
|
+
def cert_clean_result(host, user, cert)
|
175
|
+
cmd = "puppet cert clean #{cert} 2>&1"
|
176
|
+
simple_ssh_command(host, user, cmd, 120)
|
177
|
+
end
|
220
178
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
179
|
+
def r10k_git_result(host, user, repo_location)
|
180
|
+
over_ssh(host: host, user: user, timeout: 120) do |server|
|
181
|
+
# Need to use sudo
|
182
|
+
server.enable_sudo
|
183
|
+
server[repo_location].git :pull
|
184
|
+
end
|
225
185
|
end
|
226
186
|
|
227
187
|
Lita.register_handler(self)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Utils
|
2
|
+
module LitaPuppet
|
3
|
+
# Utility methods for working with PuppetDB
|
4
|
+
module PuppetDB
|
5
|
+
def dbquery(url, q)
|
6
|
+
# TODO: validate incoming query structure
|
7
|
+
client = ::PuppetDB::Client.new(server: url)
|
8
|
+
client.request(*q)
|
9
|
+
end
|
10
|
+
|
11
|
+
def node_roles_and_profiles(url, nodename)
|
12
|
+
# TODO: validate url and nodename
|
13
|
+
::PuppetDB::Client.new(server: url) # this is weird but required
|
14
|
+
d = ::PuppetDB::Client.get("/catalogs/#{nodename}")
|
15
|
+
return d['error'] if d['error']
|
16
|
+
|
17
|
+
tags = []
|
18
|
+
d['data']['resources'].each { |r| tags.concat(r['tags']) }
|
19
|
+
|
20
|
+
# return all the tags related to profile:: or role::
|
21
|
+
tags.sort.uniq.select { |t| t.match(/^(profile|role)::/) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def class_nodes(url, classname)
|
25
|
+
client = ::PuppetDB::Client.new(server: url)
|
26
|
+
q = client.request(
|
27
|
+
'resources',
|
28
|
+
[
|
29
|
+
:and,
|
30
|
+
[:'=', 'type', 'Class'],
|
31
|
+
[:'=', 'title', classname.to_s]
|
32
|
+
]
|
33
|
+
)
|
34
|
+
|
35
|
+
q.data.map { |node| node['certname'] }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Utils
|
2
|
+
module LitaPuppet
|
3
|
+
# Utility methods for doing things over SSH
|
4
|
+
module SSH
|
5
|
+
# Intelligently do some things over SSH
|
6
|
+
def over_ssh(opts = {})
|
7
|
+
raise 'MissingSSHHost' unless opts[:host]
|
8
|
+
raise 'MissingSSHUser' unless opts[:user]
|
9
|
+
opts[:timeout] ||= 300 # default to a 5 minute timeout
|
10
|
+
|
11
|
+
remote = Rye::Box.new(
|
12
|
+
opts[:host],
|
13
|
+
user: opts[:user],
|
14
|
+
auth_methods: ['publickey'],
|
15
|
+
password_prompt: false,
|
16
|
+
error: STDOUT # send STDERR to STDOUT for things that actually print
|
17
|
+
)
|
18
|
+
|
19
|
+
exception = nil
|
20
|
+
|
21
|
+
# Getting serious about not crashing Lita...
|
22
|
+
output = begin
|
23
|
+
# pass our host back to the user to work with
|
24
|
+
Timeout.timeout(opts[:timeout]) { yield remote }
|
25
|
+
rescue Rye::Err, StandardError => e
|
26
|
+
exception = e
|
27
|
+
ensure
|
28
|
+
remote.disconnect
|
29
|
+
end
|
30
|
+
|
31
|
+
calculate_result(output, exception)
|
32
|
+
end
|
33
|
+
|
34
|
+
def calculate_result(output, exception)
|
35
|
+
result = {}
|
36
|
+
if exception
|
37
|
+
result[:exception] = exception
|
38
|
+
else
|
39
|
+
result[:exit_status] = output.exit_status
|
40
|
+
result[:stdout] = output.stdout
|
41
|
+
result[:stderr] = output.stderr
|
42
|
+
end
|
43
|
+
result
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Utils
|
2
|
+
module LitaPuppet
|
3
|
+
# Utility methods for manipulating text
|
4
|
+
module Text
|
5
|
+
# Strip off bad characters
|
6
|
+
def sanitze_for_chat(text)
|
7
|
+
# Remove bash colorings
|
8
|
+
text.gsub(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]/, '')
|
9
|
+
end
|
10
|
+
|
11
|
+
# camel case puppet classes
|
12
|
+
def class_camel(text)
|
13
|
+
text.split('::').map(&:capitalize).join('::')
|
14
|
+
end
|
15
|
+
|
16
|
+
# Format some text as code
|
17
|
+
# Note that this is HipChat specific for the moment
|
18
|
+
# TODO: Make this *not* HipChat specific
|
19
|
+
def as_code(text)
|
20
|
+
'/code ' + sanitze_for_chat(text)
|
21
|
+
end
|
22
|
+
|
23
|
+
def r10k_command(environment, mod)
|
24
|
+
command = 'r10k deploy'
|
25
|
+
if environment && mod
|
26
|
+
command << " module -e #{environment} #{mod} -v"
|
27
|
+
else
|
28
|
+
command << ' environment'
|
29
|
+
command << " #{environment}" if environment
|
30
|
+
command << ' -pv'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def agent_command
|
35
|
+
command = 'puppet agent'
|
36
|
+
command << ' --onetime --verbose --no-daemonize'
|
37
|
+
command << ' --no-usecacheonfailure'
|
38
|
+
command << ' --no-splay --show_diff 2>&1'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lita-puppet.gemspec
CHANGED
@@ -1,26 +1,29 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
|
-
spec.name =
|
3
|
-
spec.version =
|
4
|
-
spec.authors = [
|
5
|
-
spec.email = [
|
6
|
-
spec.description =
|
7
|
-
spec.summary =
|
8
|
-
spec.homepage =
|
2
|
+
spec.name = 'lita-puppet'
|
3
|
+
spec.version = '0.6.2'
|
4
|
+
spec.authors = ['Daniel Schaaff', 'Jonathan Gnagy'].sort
|
5
|
+
spec.email = ['jgnagy@knuedge.com']
|
6
|
+
spec.description = 'Some basic Puppet interactions for Lita'
|
7
|
+
spec.summary = 'Allow the Lita bot to handle requests for puppet tasks'
|
8
|
+
spec.homepage = 'https://github.com/knuedge/lita-puppet'
|
9
9
|
spec.license = 'MIT'
|
10
|
-
spec.metadata = {
|
10
|
+
spec.metadata = { 'lita_plugin_type' => 'handler' }
|
11
11
|
|
12
|
-
spec.files = `git ls-files`.split(
|
12
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
13
13
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
14
14
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
15
|
-
spec.require_paths = [
|
15
|
+
spec.require_paths = ['lib']
|
16
16
|
|
17
|
-
spec.add_runtime_dependency
|
18
|
-
spec.add_runtime_dependency
|
19
|
-
spec.add_runtime_dependency
|
17
|
+
spec.add_runtime_dependency 'lita', '~> 4.7'
|
18
|
+
spec.add_runtime_dependency 'rye'
|
19
|
+
spec.add_runtime_dependency 'puppetdb-ruby'
|
20
20
|
|
21
|
-
spec.add_development_dependency
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_development_dependency 'pry-byebug'
|
23
|
+
spec.add_development_dependency 'rake'
|
24
|
+
spec.add_development_dependency 'rack-test'
|
25
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
26
|
+
spec.add_development_dependency 'simplecov'
|
27
|
+
spec.add_development_dependency 'coveralls'
|
28
|
+
spec.add_development_dependency 'rubocop'
|
26
29
|
end
|
data/locales/en.yml
CHANGED
@@ -2,3 +2,43 @@ en:
|
|
2
2
|
lita:
|
3
3
|
handlers:
|
4
4
|
puppet:
|
5
|
+
help:
|
6
|
+
puppet_agent_run:
|
7
|
+
syntax: puppet agent run on <host>
|
8
|
+
desc: Run the puppet agent on <host>.
|
9
|
+
cert_clean:
|
10
|
+
syntax: puppet cert clean <host>
|
11
|
+
desc: Remove all traces of the SSL cert for <host> on the Puppet Master.
|
12
|
+
node_profiles:
|
13
|
+
syntax: puppet catalog <host> profiles
|
14
|
+
desc: Query PuppetDB to get a list of all roles and profiles applied to <host>.
|
15
|
+
nodes_with_class:
|
16
|
+
syntax: puppet class nodes <class>
|
17
|
+
desc: Query PuppetDB to get a list of all nodes containing a class.
|
18
|
+
r10k_deploy:
|
19
|
+
syntax: puppet r10k [env [module]]
|
20
|
+
desc: Deploy the latest puppet code on the puppet master via r10k, optionally specifying an environment, and possibly a module.
|
21
|
+
replies:
|
22
|
+
cert_clean:
|
23
|
+
working: "working on that `puppet cert clean`. I'll get right back to you."
|
24
|
+
failure: "your `puppet cert clean` didn't seem to work... ;-("
|
25
|
+
success: "your `puppet cert clean` is all done!"
|
26
|
+
puppet_agent_run:
|
27
|
+
working: "I'll run puppet right away. Give me a sec and I'll let you know how it goes."
|
28
|
+
failure: "your puppet run is done, but didn't seem to work... I think it may have timed out."
|
29
|
+
success: "that puppet run is complete! It exited with status %{status}."
|
30
|
+
node_profiles:
|
31
|
+
notconf: "I would do that, but I don't know how to connect to PuppetDB. Edit my config and add `config.handlers.puppet.puppetdb_url`."
|
32
|
+
working: "let me see what I can find in PuppetDB for you."
|
33
|
+
failure: "Hmmm, that didn't work. Here's what PuppetDB responded with: '%{error}'"
|
34
|
+
success: "Here are the profiles and roles for %{host}:"
|
35
|
+
nodes_with_class:
|
36
|
+
notconf: "I would do that, but I don't know how to connect to PuppetDB. Edit my config and add `config.handlers.puppet.puppetdb_url`."
|
37
|
+
working: "let me see what I can find in PuppetDB for you."
|
38
|
+
failure: "There are no nodes with %{pclass} class, are you sure it's a valid class?"
|
39
|
+
success: "Here are all the nodes with class %{pclass}:"
|
40
|
+
r10k_deploy:
|
41
|
+
working: "I'll get right on that. Give me a moment and I'll let you know how it went."
|
42
|
+
gitfail: "your r10k run didn't seem to work. Looks like there was a problem with Git:"
|
43
|
+
pupfail: "your r10k run didn't seem to work. Here's what went wrong:"
|
44
|
+
success: "your r10k deployment is done!"
|
@@ -1,4 +1,23 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Lita::Handlers::Puppet, lita_handler: true do
|
4
|
+
it 'routes agent run commands properly' do
|
5
|
+
is_expected.to route_command('puppet agent run on foo').to(:puppet_agent_run)
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'routes cert clean commands properly' do
|
9
|
+
is_expected.to route_command('puppet cert clean foo').to(:cert_clean)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'routes catalog profiles commands properly' do
|
13
|
+
is_expected.to route_command('puppet catalog foo profiles').to(:node_profiles)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'routes class nodes commands properly' do
|
17
|
+
is_expected.to route_command('puppet class nodes foo').to(:nodes_with_class)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'routes r10k commands properly' do
|
21
|
+
is_expected.to route_command('puppet r10k').to(:r10k_deploy)
|
22
|
+
end
|
4
23
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'simplecov'
|
2
|
+
# require 'coveralls'
|
3
|
+
SimpleCov.formatters = [
|
4
|
+
SimpleCov::Formatter::HTMLFormatter
|
5
|
+
]
|
6
|
+
SimpleCov.start { add_filter '/spec/' }
|
7
|
+
|
8
|
+
require 'lita-puppet'
|
9
|
+
require 'lita/rspec'
|
3
10
|
|
4
11
|
# A compatibility mode is provided for older plugins upgrading from Lita 3. Since this plugin
|
5
12
|
# was generated with Lita 4, the compatibility mode should be left disabled.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lita-puppet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Schaaff
|
@@ -123,6 +123,48 @@ dependencies:
|
|
123
123
|
- - "~>"
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '3.0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: simplecov
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: coveralls
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
type: :development
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
- !ruby/object:Gem::Dependency
|
155
|
+
name: rubocop
|
156
|
+
requirement: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
type: :development
|
162
|
+
prerelease: false
|
163
|
+
version_requirements: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
126
168
|
description: Some basic Puppet interactions for Lita
|
127
169
|
email:
|
128
170
|
- jgnagy@knuedge.com
|
@@ -131,6 +173,8 @@ extensions: []
|
|
131
173
|
extra_rdoc_files: []
|
132
174
|
files:
|
133
175
|
- ".gitignore"
|
176
|
+
- ".rubocop.yml"
|
177
|
+
- ".travis.yml"
|
134
178
|
- CONTRIBUTING.md
|
135
179
|
- Gemfile
|
136
180
|
- LICENSE
|
@@ -138,9 +182,9 @@ files:
|
|
138
182
|
- Rakefile
|
139
183
|
- lib/lita-puppet.rb
|
140
184
|
- lib/lita/handlers/puppet.rb
|
141
|
-
- lib/utils/puppetdb.rb
|
142
|
-
- lib/utils/ssh.rb
|
143
|
-
- lib/utils/text.rb
|
185
|
+
- lib/utils/lita_puppet/puppetdb.rb
|
186
|
+
- lib/utils/lita_puppet/ssh.rb
|
187
|
+
- lib/utils/lita_puppet/text.rb
|
144
188
|
- lita-puppet.gemspec
|
145
189
|
- locales/en.yml
|
146
190
|
- spec/lita/handlers/puppet_spec.rb
|
data/lib/utils/puppetdb.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
module Utils
|
2
|
-
# Utility methods for working with PuppetDB
|
3
|
-
module PuppetDB
|
4
|
-
def dbquery(url, q)
|
5
|
-
# TODO: validate incoming query structure
|
6
|
-
client = ::PuppetDB::Client.new(server: url)
|
7
|
-
client.request *q
|
8
|
-
end
|
9
|
-
|
10
|
-
def node_roles_and_profiles(url, nodename)
|
11
|
-
# TODO: validate url and nodename
|
12
|
-
::PuppetDB::Client.new(server: url) # this is weird but required
|
13
|
-
d = ::PuppetDB::Client.get("/catalogs/#{nodename}")
|
14
|
-
return d["error"] if d['error']
|
15
|
-
|
16
|
-
tags = []
|
17
|
-
d["data"]["resources"].each {|r| tags.concat(r['tags'])}
|
18
|
-
|
19
|
-
# return all the tags related to profile:: or role::
|
20
|
-
tags.sort.uniq.select {|t| t.match /^(profile|role)::/ }
|
21
|
-
end
|
22
|
-
|
23
|
-
def class_nodes(url, classname)
|
24
|
-
client = ::PuppetDB::Client.new(server: url)
|
25
|
-
q = client.request('resources',[:and,[:'=','type', 'Class'],[:'=','title',"#{classname}"]])
|
26
|
-
|
27
|
-
q.data.map { |node| node['certname'] }
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
data/lib/utils/ssh.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
module Utils
|
2
|
-
# Utility methods for doing things over SSH
|
3
|
-
module SSH
|
4
|
-
# Intelligently do some things over SSH
|
5
|
-
def over_ssh(opts = {})
|
6
|
-
result = {}
|
7
|
-
fail "MissingSSHHost" unless opts[:host]
|
8
|
-
fail "MissingSSHUser" unless opts[:user]
|
9
|
-
opts[:timeout] ||= 300 # default to a 5 minute timeout
|
10
|
-
|
11
|
-
remote = Rye::Box.new(
|
12
|
-
opts[:host],
|
13
|
-
user: opts[:user],
|
14
|
-
auth_methods: ['publickey'],
|
15
|
-
password_prompt: false,
|
16
|
-
error: STDOUT # send STDERR to STDOUT for things that actually print
|
17
|
-
)
|
18
|
-
|
19
|
-
exception = nil
|
20
|
-
|
21
|
-
# Getting serious about not crashing Lita...
|
22
|
-
output = begin
|
23
|
-
Timeout::timeout(opts[:timeout]) do
|
24
|
-
yield remote # pass our host back to the user to work with
|
25
|
-
end
|
26
|
-
rescue Rye::Err => e
|
27
|
-
exception = e
|
28
|
-
rescue StandardError => e
|
29
|
-
exception = e
|
30
|
-
rescue Exception => e
|
31
|
-
exception = e
|
32
|
-
ensure
|
33
|
-
remote.disconnect
|
34
|
-
end
|
35
|
-
|
36
|
-
if exception
|
37
|
-
result[:exception] = exception
|
38
|
-
else
|
39
|
-
result[:exit_status] = output.exit_status
|
40
|
-
result[:stdout] = output.stdout
|
41
|
-
result[:stderr] = output.stderr
|
42
|
-
end
|
43
|
-
return result
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
data/lib/utils/text.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
module Utils
|
2
|
-
# Utility methods for manipulating text
|
3
|
-
module Text
|
4
|
-
# Try to make names more friendly
|
5
|
-
def friendly_name(long_name)
|
6
|
-
long_name.split(/\s/).first
|
7
|
-
end
|
8
|
-
|
9
|
-
# Strip off bad characters
|
10
|
-
def sanitze_for_chat(text)
|
11
|
-
# Remove bash colorings
|
12
|
-
text.gsub(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]/, '')
|
13
|
-
end
|
14
|
-
|
15
|
-
# camel case puppet classes
|
16
|
-
def class_camel(text)
|
17
|
-
text.split('::').map(&:capitalize).join('::')
|
18
|
-
end
|
19
|
-
|
20
|
-
# Format some text as code
|
21
|
-
# Note that this is HipChat specific for the moment
|
22
|
-
# TODO: Make this *not* HipChat specific
|
23
|
-
def as_code(text)
|
24
|
-
"/code " + sanitze_for_chat(text)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|