squab-client 1.4.0

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/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
@@ -0,0 +1,4 @@
1
+ Squab Client
2
+ =====
3
+
4
+ This is the Squab Client directory. Please see the root level directory for information on squab, using squab, and developing squab.
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
@@ -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
+