prism-marauder 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/marauder +6 -0
- data/bin/prism +6 -0
- data/lib/marauder/commands.rb +184 -0
- data/lib/marauder/marauder.rb +3 -0
- metadata +100 -0
data/bin/marauder
ADDED
data/bin/prism
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# FIXME: use bundler?
|
4
|
+
require 'marauder/marauder'
|
5
|
+
require 'httparty'
|
6
|
+
require 'net/ssh'
|
7
|
+
require 'commander/import'
|
8
|
+
|
9
|
+
program :version, Marauder::VERSION
|
10
|
+
program :description, 'command-line tool to locate infrastructure'
|
11
|
+
|
12
|
+
PRISM_URL = 'http://prism.gutools.co.uk'
|
13
|
+
|
14
|
+
class Api
|
15
|
+
include HTTParty
|
16
|
+
#debug_output $stderr
|
17
|
+
disable_rails_query_string_format
|
18
|
+
end
|
19
|
+
|
20
|
+
# If you're sshing into too many hosts, you might be doing something wrong
|
21
|
+
MAX_SSH_HOSTS = 4
|
22
|
+
|
23
|
+
LOGGED_IN_USER = ENV['USER']
|
24
|
+
|
25
|
+
def table(rows)
|
26
|
+
lengths = rows.map { |row| row.map { |value| value.nil? ? 0 : value.size } }
|
27
|
+
col_widths = lengths.transpose.map { |column| column.max }
|
28
|
+
rows.map { |row|
|
29
|
+
col_widths.each_with_index.map { |width, index|
|
30
|
+
(row[index] || "").ljust(width)
|
31
|
+
}.join("\t")
|
32
|
+
}.join("\n")
|
33
|
+
end
|
34
|
+
|
35
|
+
def tokenize(s)
|
36
|
+
separators = ['-', '_', '::']
|
37
|
+
separators.inject([s]) do |tokens, sep|
|
38
|
+
tokens.map {|t| t.split(sep)}.flatten
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def prism_query(path, filter)
|
43
|
+
prism_filters = filter.select{ |f| f =~ /=/ }
|
44
|
+
|
45
|
+
api_query = Hash[prism_filters.map { |f|
|
46
|
+
param = f.split('=')
|
47
|
+
[param[0], param[1]]
|
48
|
+
}.group_by { |pair|
|
49
|
+
pair[0]
|
50
|
+
}.map { |key, kvs|
|
51
|
+
[key, kvs.map{|v| v[1]}]
|
52
|
+
}]
|
53
|
+
|
54
|
+
data = Api.get("#{PRISM_URL}#{path}", :query => {:_expand => true}.merge(api_query))
|
55
|
+
|
56
|
+
if data["stale"]
|
57
|
+
update_time = data["lastUpdated"]
|
58
|
+
STDERR.puts "WARNING: Prism reports that the data returned from #{path} is stale, it was last updated at #{update_time}"
|
59
|
+
end
|
60
|
+
|
61
|
+
data
|
62
|
+
end
|
63
|
+
|
64
|
+
def token_filter(things_to_filter, filter)
|
65
|
+
dumb_filters = filter.reject{ |f| f =~ /=/ }
|
66
|
+
query = dumb_filters.map(&:downcase).map{|s| Regexp.new("^#{s}.*")}
|
67
|
+
|
68
|
+
things_to_filter.select do |thing|
|
69
|
+
query.all? do |phrase|
|
70
|
+
tokens = yield thing
|
71
|
+
tokens.compact.any? {|token| phrase.match(token.downcase)}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def find_hosts(filter)
|
77
|
+
find_instances(filter) + find_hardware(filter)
|
78
|
+
end
|
79
|
+
|
80
|
+
def find_instances(filter)
|
81
|
+
data = prism_query('/instances', filter)
|
82
|
+
hosts = data["data"]["instances"]
|
83
|
+
token_filter(hosts, filter){ |host|
|
84
|
+
host["mainclasses"].map{|mc| tokenize(mc)}.flatten + host["mainclasses"] + [host["stage"], host["stack"]] + host["app"]
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def find_hardware(filter)
|
89
|
+
data = prism_query('/hardware', filter)
|
90
|
+
hardware = data["data"]["hardware"]
|
91
|
+
token_filter(hardware, filter){ |h| [h["dnsName"], h["stage"], h["stack"]] + h["app"] }
|
92
|
+
end
|
93
|
+
|
94
|
+
def user_for_host(hostname)
|
95
|
+
Net::SSH.configuration_for(hostname)[:user]
|
96
|
+
end
|
97
|
+
|
98
|
+
def display_results(matching, short, noun)
|
99
|
+
if matching.empty?
|
100
|
+
STDERR.puts "No #{noun} found"
|
101
|
+
else
|
102
|
+
if short
|
103
|
+
matching.each { |host| puts host['dnsName'] }
|
104
|
+
else
|
105
|
+
puts table(matching.map { |host|
|
106
|
+
app = host['app'].join(',')
|
107
|
+
app = host['mainclasses'].join(',') if app.length == 0
|
108
|
+
[host['stage'], host['stack'], app, host['dnsName'], host['createdAt']]
|
109
|
+
})
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
###### COMMANDS ######
|
115
|
+
|
116
|
+
command :hosts do |c|
|
117
|
+
c.description = 'List all hosts (hardware or instances) that match the search filter'
|
118
|
+
c.syntax = 'marauder hosts <filter>'
|
119
|
+
c.option '-s', '--short', 'Only return hostnames'
|
120
|
+
c.action do |args, options|
|
121
|
+
display_results(find_hosts(args), options.short, 'hosts')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
command :instances do |c|
|
126
|
+
c.description = 'List instances that match the search filter'
|
127
|
+
c.syntax = 'marauder instances <filter>'
|
128
|
+
c.option '-s', '--short', 'Only return hostnames'
|
129
|
+
c.action do |args, options|
|
130
|
+
display_results(find_instances(args), options.short, 'instances')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
command :hardware do |c|
|
135
|
+
c.description = 'List hardware that matches the search filter'
|
136
|
+
c.syntax = 'marauder hardware <filter>'
|
137
|
+
c.option '-s', '--short', 'Only return hostnames'
|
138
|
+
c.action do |args, options|
|
139
|
+
display_results(find_hardware(args), options.short, 'hardware')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
command :ssh do |c|
|
144
|
+
c.syntax = 'marauder ssh <filter>'
|
145
|
+
c.description = 'Execute command on matching hosts'
|
146
|
+
c.option '-u', '--user STRING', String, 'Remote username'
|
147
|
+
c.option '-c', '--cmd STRING', String, 'Command to execute (quote this if it contains a space)'
|
148
|
+
c.action do |args, options|
|
149
|
+
|
150
|
+
STDERR.puts "#{args}"
|
151
|
+
|
152
|
+
query = args.take_while {|s| s != '--'}
|
153
|
+
cmd = options.cmd
|
154
|
+
|
155
|
+
STDERR.puts "Query: #{query}"
|
156
|
+
STDERR.puts "Command: #{cmd}"
|
157
|
+
|
158
|
+
matching = find_hosts(query)
|
159
|
+
|
160
|
+
if cmd.nil?
|
161
|
+
puts "Please provide a command."
|
162
|
+
usage
|
163
|
+
exit 1
|
164
|
+
else if matching.size > MAX_SSH_HOSTS
|
165
|
+
exit 1 unless agree("Do you really want to SSH into #{matching.size} hosts?")
|
166
|
+
end
|
167
|
+
|
168
|
+
puts "ssh into #{matching.size} hosts and run `#{cmd}`..."
|
169
|
+
puts
|
170
|
+
|
171
|
+
matching.each do |host|
|
172
|
+
hostname = host['dnsName']
|
173
|
+
user = options.user || user_for_host(hostname) || LOGGED_IN_USER
|
174
|
+
Net::SSH.start(hostname, user) do |ssh|
|
175
|
+
puts "== #{hostname} as #{user} =="
|
176
|
+
puts ssh.exec!(cmd)
|
177
|
+
puts
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
default_command :hosts
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: prism-marauder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sébastien Cevey
|
9
|
+
- Simon Hildrew
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-02-20 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: commander
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: net-ssh
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: httparty
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
description: Command line tool to find services in Prism based on simple queries
|
64
|
+
email: seb@cine7.net
|
65
|
+
executables:
|
66
|
+
- marauder
|
67
|
+
- prism
|
68
|
+
extensions: []
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- bin/marauder
|
72
|
+
- bin/prism
|
73
|
+
- lib/marauder/commands.rb
|
74
|
+
- lib/marauder/marauder.rb
|
75
|
+
homepage: https://github.com/guardian/prism/tree/master/marauder#readme
|
76
|
+
licenses:
|
77
|
+
- GPL
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.8.23
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: service locator based on prism
|
100
|
+
test_files: []
|