squab-client 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.md +13 -0
- data/README.md +4 -0
- data/bin/squab-digest +87 -0
- data/bin/squawk +86 -0
- data/lib/squab-client.rb +167 -0
- metadata +74 -0
data/LICENSE.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2013 Square 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.
|
data/README.md
ADDED
data/bin/squab-digest
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# A squab stream to e-mail script
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'optparse'
|
8
|
+
require 'ostruct'
|
9
|
+
require 'pp'
|
10
|
+
require 'net/smtp'
|
11
|
+
|
12
|
+
require 'squab-client'
|
13
|
+
|
14
|
+
options = OpenStruct.new
|
15
|
+
options.days = 1
|
16
|
+
options.sources = []
|
17
|
+
options.to = []
|
18
|
+
options.from = "squab-digest@example.com"
|
19
|
+
options.today = Time.now.strftime("%a, %b %d")
|
20
|
+
|
21
|
+
optparse = OptionParser.new do |opts|
|
22
|
+
opts.banner = "Usarge #{__FILE__} [options]"
|
23
|
+
opts.on('-d', '--days DAYS', "Number of days to digest") do |arg|
|
24
|
+
options.days = arg.to_i
|
25
|
+
end
|
26
|
+
opts.on('-e', '--email EMAIL', "Where to send the digest") do |arg|
|
27
|
+
options.to.push(arg)
|
28
|
+
end
|
29
|
+
opts.on('-s', '--source SOURCE', 'Specify a source to digest. Can be used multiple times') do |arg|
|
30
|
+
options.sources.push(arg)
|
31
|
+
end
|
32
|
+
opts.on('-a', '--api URL', "API URL for squab") do |arg|
|
33
|
+
options.api = arg
|
34
|
+
end
|
35
|
+
opts.on('-D', '--dry-run', "Don't send mail, output the message to stdout") do
|
36
|
+
options.dry = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
optparse.parse!
|
41
|
+
|
42
|
+
sc = if options.api
|
43
|
+
Squab::Client.new(:api => options.api)
|
44
|
+
else
|
45
|
+
Squab::Client.new()
|
46
|
+
end
|
47
|
+
|
48
|
+
if options.sources.empty?
|
49
|
+
$stderr.puts optparse
|
50
|
+
$stderr.puts "\n[ERROR] You must supply a source to digest (-s)"
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
|
54
|
+
if options.to.empty?
|
55
|
+
$stderr.puts optparse
|
56
|
+
$stderr.puts "\n[ERROR] You must supply an email to send to (-e)"
|
57
|
+
exit 1
|
58
|
+
end
|
59
|
+
|
60
|
+
since = Time.now.to_i - (86400 * options.days.to_i)
|
61
|
+
message = <<-EOM.gsub(/^\s+/, '')
|
62
|
+
From: #{options.from}
|
63
|
+
To: #{options.to.join(',')}
|
64
|
+
Subject: Squab Digest Report for #{options.today}
|
65
|
+
EOM
|
66
|
+
|
67
|
+
message << "\n\n"
|
68
|
+
|
69
|
+
options.sources.each do |src|
|
70
|
+
search_params = { :source => src,
|
71
|
+
:from => since }
|
72
|
+
|
73
|
+
res = JSON.parse(sc.search(search_params))
|
74
|
+
message << "#{src} events from the last #{options.days} days\n\n"
|
75
|
+
res.each do |event|
|
76
|
+
message << sprintf("%-16s -- %s\n", event['uid'], event['value'])
|
77
|
+
end
|
78
|
+
message << "\n\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
if options.dry
|
82
|
+
puts message
|
83
|
+
else
|
84
|
+
Net::SMTP.start('localhost', 25) do |smtp|
|
85
|
+
smtp.send_message message, options.from, options.to
|
86
|
+
end
|
87
|
+
end
|
data/bin/squawk
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'squab-client'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
options[:max_retry] = 3
|
8
|
+
|
9
|
+
optparse = OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: #{__FILE__} [options] [message]"
|
11
|
+
opts.on('-a', '--api URL', 'Set the API url to use') do |arg|
|
12
|
+
options[:api] = arg
|
13
|
+
end
|
14
|
+
opts.on('-u', '--url URL', 'Set an explicit URL for your event') do |arg|
|
15
|
+
options[:url] = arg
|
16
|
+
end
|
17
|
+
opts.on('-U', '--user USER', 'Set an explicit user for your message') do |arg|
|
18
|
+
options[:user] = arg
|
19
|
+
end
|
20
|
+
opts.on('-S', '--source SOURCE', 'Set an explicit source for your message') do |arg|
|
21
|
+
options[:source] = arg
|
22
|
+
end
|
23
|
+
opts.on('-r', '--max-retry 3', "The maximum number of time to retry sending\n a message on failure (default: 3)") do |arg|
|
24
|
+
options[:max_retry] = arg
|
25
|
+
end
|
26
|
+
opts.on('--noverify', "Don't verify SSL. You probably shouldn't do this") do |arg|
|
27
|
+
options[:no_verify] = true
|
28
|
+
end
|
29
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
30
|
+
puts opts
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
optparse.parse!
|
35
|
+
|
36
|
+
sq = if (options[:api].nil?)
|
37
|
+
Squab::Client.new
|
38
|
+
else
|
39
|
+
Squab::Client.new(:api => options[:api])
|
40
|
+
end
|
41
|
+
|
42
|
+
if options[:user]
|
43
|
+
sq.uid = options[:user]
|
44
|
+
end
|
45
|
+
|
46
|
+
if options[:source]
|
47
|
+
sq.source = options[:source]
|
48
|
+
end
|
49
|
+
|
50
|
+
if (options[:no_verify])
|
51
|
+
sq.ssl_verify = false
|
52
|
+
end
|
53
|
+
|
54
|
+
if ARGV.length < 1
|
55
|
+
puts optparse
|
56
|
+
raise "You must provide a message to send to Squab"
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
message = ARGV.join(' ')
|
60
|
+
puts message
|
61
|
+
|
62
|
+
urls = if options[:url].nil?
|
63
|
+
URI.extract(message)
|
64
|
+
else
|
65
|
+
[options[:url]]
|
66
|
+
end
|
67
|
+
|
68
|
+
try_count = 0
|
69
|
+
begin
|
70
|
+
if urls.length == 1
|
71
|
+
sq.send(message, urls.first)
|
72
|
+
elsif urls.length > 1
|
73
|
+
puts "Multiple URLs found, using #{urls.last}"
|
74
|
+
sq.send(message, urls.last)
|
75
|
+
else
|
76
|
+
sq.send(message)
|
77
|
+
end
|
78
|
+
rescue SendEventFailed
|
79
|
+
try_count += 1
|
80
|
+
if try_count > options[:max_retry]
|
81
|
+
$stderr.puts "Failed to send message to squab, the server is not responding correctly"
|
82
|
+
else
|
83
|
+
sleep 1
|
84
|
+
retry
|
85
|
+
end
|
86
|
+
end
|
data/lib/squab-client.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'json'
|
5
|
+
require 'yaml'
|
6
|
+
require 'etc'
|
7
|
+
|
8
|
+
class SendEventFailed < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
module Squab
|
12
|
+
class Client
|
13
|
+
attr_accessor :source, :uid, :api_url, :ssl_verify
|
14
|
+
attr_accessor :api_version_prefix, :events_prefix
|
15
|
+
# Opts accepts
|
16
|
+
# api => The API URL to hit
|
17
|
+
# user => The user to report
|
18
|
+
# source => the source to report
|
19
|
+
def initialize(opts={})
|
20
|
+
@source = get_my_source(opts[:source])
|
21
|
+
@uid = get_my_user(opts[:user])
|
22
|
+
parse_config
|
23
|
+
@api_url = get_api(opts[:api] || @config[:api])
|
24
|
+
@ssl_verify = @config['ssl_verify'] || true
|
25
|
+
@api_version_prefix = @config['api_prefix'] || '/api/v1'
|
26
|
+
@events_prefix = [@api_version_prefix, 'events'].join('/')
|
27
|
+
end
|
28
|
+
|
29
|
+
def send(event, url=nil)
|
30
|
+
payload = {
|
31
|
+
"uid" => @uid,
|
32
|
+
"source" => @source,
|
33
|
+
"value" => event,
|
34
|
+
"url" => url,
|
35
|
+
}.to_json
|
36
|
+
header = {'Content-Type' => 'application/json'}
|
37
|
+
req = Net::HTTP::Post.new(@events_prefix, initheader = header)
|
38
|
+
req.body = payload
|
39
|
+
try_count = 1
|
40
|
+
begin
|
41
|
+
http = Net::HTTP.new(@api_url.host, @api_url.port)
|
42
|
+
http = setup_ssl(http) if @api_url.scheme == "https"
|
43
|
+
response = http.request(req)
|
44
|
+
rescue EOFError, Errno::ECONNREFUSED => e
|
45
|
+
raise SendEventFailed, "Could not reach the Squab server"
|
46
|
+
end
|
47
|
+
response
|
48
|
+
end
|
49
|
+
|
50
|
+
def list_sources
|
51
|
+
req = make_request('sources')
|
52
|
+
get_req(req)
|
53
|
+
end
|
54
|
+
|
55
|
+
def list_users
|
56
|
+
req = make_request('users')
|
57
|
+
get_req(req)
|
58
|
+
end
|
59
|
+
|
60
|
+
def list_urls
|
61
|
+
req = make_request('urls')
|
62
|
+
get_req(req)
|
63
|
+
end
|
64
|
+
|
65
|
+
def get(max=5, since=nil)
|
66
|
+
req = if since
|
67
|
+
make_event_request("since/#{since.to_i}")
|
68
|
+
else
|
69
|
+
make_event_request("limit/#{max}")
|
70
|
+
end
|
71
|
+
get_req(req)
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_from_event(event_num)
|
75
|
+
req = make_event_request("starting/#{event_num}")
|
76
|
+
get_req(req)
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_from_user(username)
|
80
|
+
req = make_event_request("user/#{username}")
|
81
|
+
get_req(req)
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_from_source(source)
|
85
|
+
req = make_event_request("source/#{source}")
|
86
|
+
get_req(req)
|
87
|
+
end
|
88
|
+
|
89
|
+
def simple_search(search_val)
|
90
|
+
req = make_event_request("search/value/#{search_val}/limit/5")
|
91
|
+
get_req(req)
|
92
|
+
end
|
93
|
+
|
94
|
+
def search(search_params)
|
95
|
+
req = Net::HTTP::Post.new([ @events_prefix, 'search' ].join('/'))
|
96
|
+
req.body = JSON.dump(search_params)
|
97
|
+
get_req(req)
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_api(url)
|
101
|
+
url ? URI.parse(url) : URI.parse(
|
102
|
+
"http://squab/"
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_config(file=nil)
|
107
|
+
# Default
|
108
|
+
config_file = file || '/etc/squab.yaml'
|
109
|
+
# Instance override
|
110
|
+
if File.exist?(config_file)
|
111
|
+
@config = YAML.load(File.open(config_file).read)
|
112
|
+
else
|
113
|
+
@config = {}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_my_source(source=nil)
|
118
|
+
source || File.basename($PROGRAM_NAME)
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_my_user(username=nil)
|
122
|
+
username || Etc.getpwuid(Process.uid).name
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
def setup_ssl(http)
|
127
|
+
http.use_ssl = true
|
128
|
+
http.ca_file = OpenSSL::X509::DEFAULT_CERT_FILE
|
129
|
+
if @ssl_verify
|
130
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
131
|
+
else
|
132
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
133
|
+
$stderr.puts("Bypassing SSL verification, this should only happen "+
|
134
|
+
"during testing and development")
|
135
|
+
end
|
136
|
+
http.verify_depth = 5
|
137
|
+
http
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_req(req, full_req=false)
|
141
|
+
http = Net::HTTP.new(@api_url.host, @api_url.port)
|
142
|
+
http = setup_ssl(http) if @api_url.scheme == "https"
|
143
|
+
resp = http.start { |h| h.request(req) }
|
144
|
+
if full_req
|
145
|
+
resp
|
146
|
+
else
|
147
|
+
resp.body
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def make_event_request(to)
|
152
|
+
Net::HTTP::Get.new([ @events_prefix, to ].join('/'))
|
153
|
+
end
|
154
|
+
|
155
|
+
def make_request(to)
|
156
|
+
Net::HTTP::Get.new([ @api_version_prefix, to ].join('/'))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Old name space, deprecated and should be removed eventually
|
162
|
+
class SquabClient < Squab::Client
|
163
|
+
def initialize(api_url=nil, source=nil, uid=nil)
|
164
|
+
super(:api => api_url, :source => source, :user => uid)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: squab-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 7
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 4
|
9
|
+
- 0
|
10
|
+
version: 1.4.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Grier Johnson
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-12-02 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: A client wrapper for the Squab API, also includes the CLI tool 'squawk' for quick message sending
|
23
|
+
email:
|
24
|
+
- github@squareup.com
|
25
|
+
executables:
|
26
|
+
- squawk
|
27
|
+
extensions: []
|
28
|
+
|
29
|
+
extra_rdoc_files:
|
30
|
+
- LICENSE.md
|
31
|
+
files:
|
32
|
+
- lib/squab-client.rb
|
33
|
+
- bin/squawk
|
34
|
+
- bin/squab-digest
|
35
|
+
- README.md
|
36
|
+
- LICENSE.md
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://github.com/square/squab
|
39
|
+
licenses:
|
40
|
+
- Apache 2.0
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --charset=UTF-8
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
hash: 3
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 23
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 3
|
64
|
+
- 6
|
65
|
+
version: 1.3.6
|
66
|
+
requirements: []
|
67
|
+
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.6.2
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Squab client
|
73
|
+
test_files: []
|
74
|
+
|