sporf 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +55 -0
- data/Rakefile +2 -0
- data/bin/sporf +18 -0
- data/lib/sporf.rb +25 -0
- data/lib/sporf/chef.rb +16 -0
- data/lib/sporf/helpers.rb +40 -0
- data/lib/sporf/printers.rb +49 -0
- data/lib/sporf/sporf.rb +30 -0
- data/lib/sporf/ssh.rb +32 -0
- data/santoku.gemspec +19 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d2a07ae3b91c942e61941a54de255b29cd4c00ea
|
4
|
+
data.tar.gz: 6c871094b1711edd72f04bbea81e7ce669571baa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 81648b6af255eda4967006276fadd8eb7e1d373cf521d1060c06c7f21c4fe430576fe15fefbf062a710627c9ce250e9f025c7e118a4cae3a5f2ffbffabb89b0b
|
7
|
+
data.tar.gz: f1c3ac76ba1e250fcba9b0c9ab294b8d7ad70b22ec57746783e719ad031e057e39306b6f1b202ad75ae0efe91c81e69ab470ab7b54f221619cf74dc9dabd4a95
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Openminds BVBA
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
Sporf
|
2
|
+
=======
|
3
|
+
|
4
|
+
Parrallel ssh commands over chef servers with rspec-like output. sporf uses the exit codes of a command to determine whether it qualifies as a success or failure.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
============
|
8
|
+
|
9
|
+
## Requirements
|
10
|
+
|
11
|
+
* Ruby 1.9.3+
|
12
|
+
* [Knife configuration file](http://docs.opscode.com/config_rb_knife.html)
|
13
|
+
|
14
|
+
Install the gem:
|
15
|
+
|
16
|
+
gem install sporf
|
17
|
+
|
18
|
+
Usage
|
19
|
+
=====
|
20
|
+
|
21
|
+
`sporf test COMMAND [QUERY]` or `sporf -t COMMAND [QUERY]`
|
22
|
+
|
23
|
+
## Test command
|
24
|
+
|
25
|
+
Test if a given command returns 0 on all nodes:
|
26
|
+
|
27
|
+
sporf -t 'test -d /foo/bar/'
|
28
|
+
|
29
|
+
Test if a given command **does not** return 0 on nodes:
|
30
|
+
|
31
|
+
sporf -t 'php -i | grep -i memcached' --invert
|
32
|
+
|
33
|
+
Test if a given command returns 0 on all nodes, also print out the STDOUT of successfull commands:
|
34
|
+
|
35
|
+
sporf -t 'cat /etc/apache2/conf.d/ssl' --print-success
|
36
|
+
|
37
|
+
Test if a given command returns 0, scoped on certain nodes (with [knife search syntax](http://docs.opscode.com/knife_search.html)):
|
38
|
+
|
39
|
+
sporf -t 'test -f /etc/apache2/conf.d/ssl' 'recipe:apache AND listen_ports:443'
|
40
|
+
|
41
|
+
### Note
|
42
|
+
|
43
|
+
sporf is not meant to modify server configurations, only to test how a server replies on a given (non-destructive) command. Use with caution.
|
44
|
+
|
45
|
+
To-do
|
46
|
+
=====
|
47
|
+
|
48
|
+
* Make the output truly RSpec compatible, so we can use RSpec formatters
|
49
|
+
* Stop forcing root user, system user (with sudo) should also work
|
50
|
+
* sporf should not be limited to Chef (main reason this isn't a knife plugin)
|
51
|
+
|
52
|
+
Contributing
|
53
|
+
============
|
54
|
+
|
55
|
+
Bug reports, feature requests and test implementations are more than welcome. Please use [our Github account](https://github.com/openminds/sporf) for this.
|
data/Rakefile
ADDED
data/bin/sporf
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'sporf'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'thor'
|
5
|
+
|
6
|
+
class SporfCommand < Thor
|
7
|
+
map '-t' => :test
|
8
|
+
|
9
|
+
desc 'test COMMAND [QUERY]', 'Execute command on nodes, scoped on the given query if query is given. Query syntax should be the same as `knife search` syntax.'
|
10
|
+
option :invert, type: :boolean, desc: 'invert matched results', default: false
|
11
|
+
option :timeout, type: :numeric, desc: 'timeout interval per ssh connection (default: 15)', default: 15
|
12
|
+
option :'print-success', type: :boolean, desc: 'prints output of successful commands', default: false
|
13
|
+
def test(command, query='name:*')
|
14
|
+
Sporf.test(command, query, options[:timeout], options[:'print-success'], options[:invert])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
SporfCommand.start
|
data/lib/sporf.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'peach'
|
2
|
+
|
3
|
+
require 'sporf/chef'
|
4
|
+
require 'sporf/helpers'
|
5
|
+
require 'sporf/printers'
|
6
|
+
require 'sporf/sporf'
|
7
|
+
require 'sporf/ssh'
|
8
|
+
|
9
|
+
class Sporf
|
10
|
+
class << self
|
11
|
+
def test command, query, timeout_interval, verbose_success, invert
|
12
|
+
@command = command
|
13
|
+
@query = query
|
14
|
+
@timeout_interval = timeout_interval
|
15
|
+
@verbose_success = verbose_success
|
16
|
+
@invert = invert
|
17
|
+
|
18
|
+
knife_search(@query).peach(5) do |fqdn|
|
19
|
+
execute_query fqdn
|
20
|
+
end
|
21
|
+
|
22
|
+
print_summary
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/sporf/chef.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'chef/knife'
|
2
|
+
|
3
|
+
class Sporf
|
4
|
+
class << self
|
5
|
+
def knife_search query
|
6
|
+
# Monkey patch Chef::Knife::UI to hide stdout
|
7
|
+
Chef::Knife::UI.class_eval do
|
8
|
+
def stdout
|
9
|
+
@stdout_hack ||= ::File.new('/dev/null', 'w')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Chef::Knife.run(['search', 'node', query]).map { |node| node[:fqdn] }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'sporf/printers'
|
2
|
+
|
3
|
+
class Sporf
|
4
|
+
class << self
|
5
|
+
def failed(reason)
|
6
|
+
failed_output_stream.push reason
|
7
|
+
print_fail
|
8
|
+
end
|
9
|
+
|
10
|
+
def succeeded(reason)
|
11
|
+
success_output_stream.push reason
|
12
|
+
print_success
|
13
|
+
end
|
14
|
+
|
15
|
+
def timed_out(reason)
|
16
|
+
timeout_output_stream.push reason
|
17
|
+
print_timeout
|
18
|
+
end
|
19
|
+
|
20
|
+
def timeout_output_stream
|
21
|
+
@timeout_output_stream ||= Array.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def failed_output_stream
|
25
|
+
@failed_output_stream ||= Array.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def success_output_stream
|
29
|
+
@success_output_stream ||= Array.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def verbose_success?
|
33
|
+
@verbose_success
|
34
|
+
end
|
35
|
+
|
36
|
+
def invert?
|
37
|
+
@invert
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
class Sporf
|
4
|
+
class << self
|
5
|
+
def print_fail
|
6
|
+
print 'F'.red
|
7
|
+
end
|
8
|
+
|
9
|
+
def print_success
|
10
|
+
print '.'.green
|
11
|
+
end
|
12
|
+
|
13
|
+
def print_timeout
|
14
|
+
print '*'.yellow
|
15
|
+
end
|
16
|
+
|
17
|
+
def print_timeout_stream
|
18
|
+
timeout_output_stream.each do |output|
|
19
|
+
puts output.yellow
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def print_failed_stream
|
24
|
+
failed_output_stream.each do |output|
|
25
|
+
puts output.red
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def print_success_stream
|
30
|
+
success_output_stream.each do |output|
|
31
|
+
puts output.green
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def print_summary
|
36
|
+
print "\n"
|
37
|
+
print_timeout_stream
|
38
|
+
print_failed_stream
|
39
|
+
print_success_stream if verbose_success?
|
40
|
+
|
41
|
+
puts "\n---------------------------------------\n"
|
42
|
+
|
43
|
+
puts "#{'Success'.green}: #{success_output_stream.count}"
|
44
|
+
puts "#{'Timed out or does not resolve'.yellow}: #{timeout_output_stream.count}"
|
45
|
+
puts "#{'Failed'.red}: #{failed_output_stream.count}"
|
46
|
+
puts "#{'Total'.blue}: #{success_output_stream.count + timeout_output_stream.count + failed_output_stream.count}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/sporf/sporf.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
|
3
|
+
class Sporf
|
4
|
+
class << self
|
5
|
+
def execute_query fqdn
|
6
|
+
begin
|
7
|
+
timeout @timeout_interval do
|
8
|
+
Net::SSH.start(fqdn, 'root', paranoid: false, forward_agent: true) do |ssh|
|
9
|
+
output = ssh_exec!(ssh, @command)
|
10
|
+
parse_output output, fqdn
|
11
|
+
end
|
12
|
+
end
|
13
|
+
rescue TimeoutError, Errno::ETIMEDOUT, SocketError, Errno::EHOSTUNREACH => e
|
14
|
+
timed_out "#{fqdn}: #{e.message}"
|
15
|
+
rescue Exception => e
|
16
|
+
timed_out "#{fqdn}: #{e.inspect}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_output output, fqdn
|
21
|
+
if output[2] == 0 && !invert?
|
22
|
+
succeeded "#{fqdn}: #{output[0]}"
|
23
|
+
elsif output[2] != 0 && invert?
|
24
|
+
succeeded "#{fqdn} returned #{output[2]}: #{output[1]} #{output[0]}"
|
25
|
+
else
|
26
|
+
failed "#{fqdn} returned #{output[2]}: #{output[1]} #{output[0]}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/sporf/ssh.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
class Sporf
|
2
|
+
class << self
|
3
|
+
def ssh_exec!(ssh, command)
|
4
|
+
# I am not awesome enough to have made this method myself
|
5
|
+
# Originally submitted by 'flitzwald' over here: http://stackoverflow.com/a/3386375
|
6
|
+
stdout_data = ''
|
7
|
+
stderr_data = ''
|
8
|
+
exit_code = nil
|
9
|
+
|
10
|
+
ssh.open_channel do |channel|
|
11
|
+
channel.exec(command) do |ch, success|
|
12
|
+
unless success
|
13
|
+
abort "FAILED: couldn't execute command (ssh.channel.exec)"
|
14
|
+
end
|
15
|
+
channel.on_data do |ch,data|
|
16
|
+
stdout_data+=data
|
17
|
+
end
|
18
|
+
|
19
|
+
channel.on_extended_data do |ch,type,data|
|
20
|
+
stderr_data+=data
|
21
|
+
end
|
22
|
+
|
23
|
+
channel.on_request('exit-status') do |ch,data|
|
24
|
+
exit_code = data.read_long
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
ssh.loop
|
29
|
+
[stdout_data, stderr_data, exit_code]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/santoku.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = 'sporf'
|
3
|
+
spec.version = '0.0.1'
|
4
|
+
spec.executables << 'sporf'
|
5
|
+
spec.date = '2013-11-26'
|
6
|
+
spec.summary = 'Parrallel ssh commands over chef servers with rspec-like output'
|
7
|
+
spec.description = 'A gem to perform command over parallel ssh connections on multiple chef serverspec. Output is rspec-like.'
|
8
|
+
spec.authors = ['Steven De Coeyer', 'Jeroen Jacobs']
|
9
|
+
spec.email = 'tech@openminds.be'
|
10
|
+
spec.files = `git ls-files`.split($\)
|
11
|
+
spec.homepage = 'https://github.com/openminds/sporf'
|
12
|
+
spec.license = 'MIT'
|
13
|
+
|
14
|
+
spec.add_dependency 'chef', '~> 11.0'
|
15
|
+
spec.add_dependency 'colorize'
|
16
|
+
spec.add_dependency 'net-ssh'
|
17
|
+
spec.add_dependency 'peach'
|
18
|
+
spec.add_dependency 'thor'
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sporf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Steven De Coeyer
|
8
|
+
- Jeroen Jacobs
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-11-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: chef
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ~>
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '11.0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '11.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: colorize
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: net-ssh
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: peach
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: thor
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
description: A gem to perform command over parallel ssh connections on multiple chef
|
85
|
+
serverspec. Output is rspec-like.
|
86
|
+
email: tech@openminds.be
|
87
|
+
executables:
|
88
|
+
- sporf
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- .gitignore
|
93
|
+
- Gemfile
|
94
|
+
- LICENSE
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- bin/sporf
|
98
|
+
- lib/sporf.rb
|
99
|
+
- lib/sporf/chef.rb
|
100
|
+
- lib/sporf/helpers.rb
|
101
|
+
- lib/sporf/printers.rb
|
102
|
+
- lib/sporf/sporf.rb
|
103
|
+
- lib/sporf/ssh.rb
|
104
|
+
- santoku.gemspec
|
105
|
+
homepage: https://github.com/openminds/sporf
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - '>='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.0.14
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Parrallel ssh commands over chef servers with rspec-like output
|
129
|
+
test_files: []
|