stormmq-client 0.0.4
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 +21 -0
- data/README +1 -0
- data/Rakefile +19 -0
- data/TODO +11 -0
- data/bin/stormmq-amqp-echo-test +204 -0
- data/bin/stormmq-create-system +211 -0
- data/bin/stormmq-delete-system +117 -0
- data/bin/stormmq-describe-company +106 -0
- data/bin/stormmq-describe-system +135 -0
- data/bin/stormmq-get-amqpuser-password +148 -0
- data/bin/stormmq-list-amqpusers +142 -0
- data/bin/stormmq-list-apis +86 -0
- data/bin/stormmq-list-bindings +130 -0
- data/bin/stormmq-list-clusters +101 -0
- data/bin/stormmq-list-companies +89 -0
- data/bin/stormmq-list-exchanges +131 -0
- data/bin/stormmq-list-queues +131 -0
- data/bin/stormmq-list-systems +111 -0
- data/bin/stormmq-url-signer +111 -0
- data/lib/stormmq/amqp.rb +92 -0
- data/lib/stormmq/application.rb +36 -0
- data/lib/stormmq/base64_extensions.rb +19 -0
- data/lib/stormmq/errors.rb +33 -0
- data/lib/stormmq/rest.rb +160 -0
- data/lib/stormmq/secret_keys.rb +70 -0
- data/lib/stormmq/url.rb +125 -0
- data/lib/stormmq/utils.rb +27 -0
- data/spec/stormmq/amqp_spec.rb +21 -0
- data/spec/stormmq/secret_keys_spec.rb +25 -0
- data/spec/stormmq/url_spec.rb +129 -0
- data/spec/stormmq/utils_spec.rb +13 -0
- metadata +254 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
#--
|
4
|
+
# Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
|
5
|
+
# All rights reserved.
|
6
|
+
#
|
7
|
+
# Please refer to the LICENSE file that accompanies this source
|
8
|
+
# for terms of use and redistribution.
|
9
|
+
#++
|
10
|
+
|
11
|
+
$:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
|
12
|
+
|
13
|
+
require 'stormmq/secret_keys'
|
14
|
+
require 'stormmq/application'
|
15
|
+
|
16
|
+
class StormMQ::Application::URLSigner < StormMQ::Application
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
synopsis "--help | --user <userName> --method <HttpMethod> --url <URL>"
|
20
|
+
options :help
|
21
|
+
|
22
|
+
option :names => %w(--user -u),
|
23
|
+
:opt_description => "a valid user name, i.e. the user name that you log into the site with",
|
24
|
+
:arity => [1,1],
|
25
|
+
:opt_found => get_args
|
26
|
+
|
27
|
+
option :names => %w(--method -m),
|
28
|
+
:opt_description => "the HTTP method to use - defaults to 'GET'",
|
29
|
+
:arity => [1,1],
|
30
|
+
:opt_found => get_args,
|
31
|
+
:opt_not_found => 'GET'
|
32
|
+
|
33
|
+
option :names => %w(--url -a),
|
34
|
+
:opt_description => "the URL to be signed",
|
35
|
+
:arity => [1,1],
|
36
|
+
:opt_found => get_args,
|
37
|
+
:opt_not_found => CommandLine::OptionParser::OPT_NOT_FOUND_BUT_REQUIRED
|
38
|
+
end
|
39
|
+
|
40
|
+
def main
|
41
|
+
puts self.url.canonicalise_and_sign(opt.user, self.secret_key(opt.user), opt['--method'])
|
42
|
+
end
|
43
|
+
|
44
|
+
def man
|
45
|
+
<<-EOM
|
46
|
+
NAME
|
47
|
+
|
48
|
+
#{File.basename(__FILE__)}
|
49
|
+
|
50
|
+
PURPOSE
|
51
|
+
|
52
|
+
Use this to experiment with the how signing URLs works if you need
|
53
|
+
a reference implementation.
|
54
|
+
|
55
|
+
USAGE
|
56
|
+
|
57
|
+
#{File.basename(__FILE__)} --help
|
58
|
+
#{File.basename(__FILE__)} --user <userName> --method <HttpMethod> --url <URL>
|
59
|
+
|
60
|
+
PARAMETERS
|
61
|
+
|
62
|
+
--user
|
63
|
+
|
64
|
+
The user name you log in to this site with.
|
65
|
+
|
66
|
+
--method
|
67
|
+
|
68
|
+
One of:
|
69
|
+
|
70
|
+
* GET
|
71
|
+
* HEAD
|
72
|
+
* OPTIONS
|
73
|
+
* PUT
|
74
|
+
* DELETE
|
75
|
+
|
76
|
+
POST is not yet supported.
|
77
|
+
|
78
|
+
--url
|
79
|
+
|
80
|
+
The unsigned URL to be signed.
|
81
|
+
|
82
|
+
--help, -h
|
83
|
+
|
84
|
+
Displays this help page then quits.
|
85
|
+
|
86
|
+
RESULT
|
87
|
+
|
88
|
+
Outputs a signed URL to STDOUT.
|
89
|
+
|
90
|
+
NOTES
|
91
|
+
|
92
|
+
Advanced use only - commandline subject to change.
|
93
|
+
|
94
|
+
COPYRIGHT
|
95
|
+
|
96
|
+
Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
|
97
|
+
All rights reserved.
|
98
|
+
|
99
|
+
EOM
|
100
|
+
end
|
101
|
+
|
102
|
+
def url
|
103
|
+
StormMQ::URL.new(opt.url)
|
104
|
+
end
|
105
|
+
|
106
|
+
def secret_key(user) # :nodoc:
|
107
|
+
StormMQ::SecretKeys.key_for(user)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
StormMQ::Application::URLSigner.run
|
data/lib/stormmq/amqp.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Please refer to the LICENSE file that accompanies this source
|
6
|
+
# for terms of use and redistribution.
|
7
|
+
#++
|
8
|
+
|
9
|
+
require 'mq'
|
10
|
+
require 'pp'
|
11
|
+
|
12
|
+
module StormMQ
|
13
|
+
|
14
|
+
module AMQPClientImplementation
|
15
|
+
|
16
|
+
VERSION = '0.0.4'
|
17
|
+
|
18
|
+
def process_frame frame
|
19
|
+
if mq = channels[frame.channel]
|
20
|
+
mq.process_frame(frame)
|
21
|
+
return
|
22
|
+
end
|
23
|
+
|
24
|
+
case frame
|
25
|
+
when Frame::Method
|
26
|
+
case method = frame.payload
|
27
|
+
when Protocol::Connection::Start
|
28
|
+
send Protocol::Connection::StartOk.new(
|
29
|
+
{
|
30
|
+
:platform => 'Ruby/EventMachine',
|
31
|
+
:product => 'StormMQ AMQP',
|
32
|
+
:information => 'http://github.com/tonybyrne/StormMQ-Ruby-Client/',
|
33
|
+
:version => VERSION
|
34
|
+
},
|
35
|
+
'AMQPLAIN',
|
36
|
+
{
|
37
|
+
:LOGIN => @settings[:user],
|
38
|
+
:PASSWORD => @settings[:password]
|
39
|
+
},
|
40
|
+
'en_US'
|
41
|
+
)
|
42
|
+
|
43
|
+
when Protocol::Connection::Tune
|
44
|
+
send Protocol::Connection::TuneOk.new(
|
45
|
+
:channel_max => 0,
|
46
|
+
:frame_max => 131072,
|
47
|
+
:heartbeat => 0
|
48
|
+
)
|
49
|
+
|
50
|
+
send Protocol::Connection::Open.new(
|
51
|
+
:virtual_host => @settings[:vhost],
|
52
|
+
:capabilities => '',
|
53
|
+
:insist => @settings[:insist]
|
54
|
+
)
|
55
|
+
|
56
|
+
when Protocol::Connection::OpenOk
|
57
|
+
succeed(self)
|
58
|
+
|
59
|
+
when Protocol::Connection::Close
|
60
|
+
STDERR.puts "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
|
61
|
+
|
62
|
+
when Protocol::Connection::CloseOk
|
63
|
+
@on_disconnect.call if @on_disconnect
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module AMQPClient
|
70
|
+
|
71
|
+
def self.connect(options={})
|
72
|
+
AMQP.client = AMQPClientImplementation
|
73
|
+
AMQP.connect(
|
74
|
+
{
|
75
|
+
:vhost => self.vhost_from_options(options),
|
76
|
+
:host => 'amqp.stormmq.com',
|
77
|
+
:port => 443,
|
78
|
+
:ssl => true
|
79
|
+
}.merge(options)
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.vhost_from_options(options)
|
84
|
+
vhost = "/#{options[:company]}/#{options[:system]}/#{options[:environment]}"
|
85
|
+
[:company, :system, :environment].each {|option| options.delete(option)}
|
86
|
+
vhost
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Please refer to the LICENSE file that accompanies this source
|
6
|
+
# for terms of use and redistribution.
|
7
|
+
#++
|
8
|
+
|
9
|
+
require 'stormmq/errors'
|
10
|
+
require 'stormmq/rest'
|
11
|
+
require 'commandline'
|
12
|
+
require 'commandline/optionparser'
|
13
|
+
|
14
|
+
module StormMQ
|
15
|
+
class Application < CommandLine::Application_wo_AutoRun
|
16
|
+
|
17
|
+
def initialize(*args)
|
18
|
+
author "Tony Byrne"
|
19
|
+
copyright "2010, Tony Byrne & StormMQ. All rights reserved."
|
20
|
+
super(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def rest_client(user)
|
24
|
+
begin
|
25
|
+
Rest.new(:user => user)
|
26
|
+
rescue Error::SecretKeyNotProvidedError
|
27
|
+
raise "Could not find the secret key for user '#{user}' - please ensure it is present in the secret key file"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self_test
|
32
|
+
'Self test ouput goes here'
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Please refer to the LICENSE file that accompanies this source
|
6
|
+
# for terms of use and redistribution.
|
7
|
+
#++
|
8
|
+
|
9
|
+
module Base64
|
10
|
+
|
11
|
+
def urlsafe_decode64(str)
|
12
|
+
decode64(str.tr("-_", "+/"))
|
13
|
+
end
|
14
|
+
|
15
|
+
def urlsafe_encode64(bin)
|
16
|
+
encode64(bin).tr("+/", "-_")
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Please refer to the LICENSE file that accompanies this source
|
6
|
+
# for terms of use and redistribution.
|
7
|
+
#++
|
8
|
+
|
9
|
+
module StormMQ
|
10
|
+
|
11
|
+
module Error
|
12
|
+
|
13
|
+
class Base < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
class LoadSecretKeysError < StormMQ::Error::Base
|
17
|
+
end
|
18
|
+
|
19
|
+
class SecretKeyNotFoundError < StormMQ::Error::Base
|
20
|
+
end
|
21
|
+
|
22
|
+
class SecretKeyNotProvidedError < StormMQ::Error::Base
|
23
|
+
end
|
24
|
+
|
25
|
+
class UserNotProvidedError < StormMQ::Error::Base
|
26
|
+
end
|
27
|
+
|
28
|
+
class InvalidURLError < StormMQ::Error::Base
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/stormmq/rest.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010, Tony Byrne & StormMQ Ltd.
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Please refer to the LICENSE file that accompanies this source
|
6
|
+
# for terms of use and redistribution.
|
7
|
+
#++
|
8
|
+
|
9
|
+
require 'stormmq/url'
|
10
|
+
require 'stormmq/errors'
|
11
|
+
require 'stormmq/secret_keys'
|
12
|
+
require 'json'
|
13
|
+
require 'rest_client'
|
14
|
+
|
15
|
+
module StormMQ
|
16
|
+
|
17
|
+
API_HOST = ENV['STORMMQ_HOST'] || "api.stormmq.com"
|
18
|
+
API = ENV['STORMMQ_API'] || "api"
|
19
|
+
API_VERSION = ENV['STORMMQ_API_VERSION'] || "2009-01-01"
|
20
|
+
|
21
|
+
COMPANIES_PATH = '/companies/'
|
22
|
+
CLUSTERS_PATH = '/clusters'
|
23
|
+
APIS_PATH = '/'
|
24
|
+
|
25
|
+
# The Rest class implements the client for StormMQ's RESTful API. The API allows clients to access
|
26
|
+
# company, system and environment information.
|
27
|
+
|
28
|
+
class Rest
|
29
|
+
|
30
|
+
RestClient.proxy = ENV['http_proxy'] unless ENV['http_proxy'].nil?
|
31
|
+
|
32
|
+
def initialize(options={})
|
33
|
+
unless @user = options.delete(:user)
|
34
|
+
raise Error::UserNotProvidedError,
|
35
|
+
"could not determine the user name - either provide it via the :user param",
|
36
|
+
caller
|
37
|
+
end
|
38
|
+
|
39
|
+
unless @secret_key = options.delete(:secret_key) || self.class.secret_key_from_key_store(@user)
|
40
|
+
raise Error::SecretKeyNotProvidedError, "could not determine the secret key for user '#{@user}' - either provide it via the :secret_key param or ensure it is available in the secret key file",
|
41
|
+
caller
|
42
|
+
end
|
43
|
+
|
44
|
+
@host = options.delete(:host) || API_HOST
|
45
|
+
@api = options.delete(:api) || API
|
46
|
+
@version = options.delete(:version) || API_VERSION
|
47
|
+
|
48
|
+
@url_options = options
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns an Array of company indentifiers associated with the user.
|
52
|
+
def companies
|
53
|
+
signed_get(COMPANIES_PATH)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Takes a String containing a valid company identifier and returns a
|
57
|
+
# Hash representation of the detailed information stored on the StormMQ
|
58
|
+
# system about the company.
|
59
|
+
def describe_company(company)
|
60
|
+
signed_get(COMPANIES_PATH, escape(company))
|
61
|
+
end
|
62
|
+
|
63
|
+
def systems(company)
|
64
|
+
signed_get(COMPANIES_PATH, escape(company), '/')
|
65
|
+
end
|
66
|
+
|
67
|
+
def create_system(company, system, json)
|
68
|
+
signed_put(json, COMPANIES_PATH, escape(company), escape(system))
|
69
|
+
end
|
70
|
+
|
71
|
+
def describe_system(company, system)
|
72
|
+
signed_get(COMPANIES_PATH, escape(company), escape(system))
|
73
|
+
end
|
74
|
+
|
75
|
+
def delete_system(company, system)
|
76
|
+
signed_delete(COMPANIES_PATH, escape(company), escape(system))
|
77
|
+
end
|
78
|
+
|
79
|
+
def clusters(company)
|
80
|
+
signed_get(CLUSTERS_PATH, escape(company))
|
81
|
+
end
|
82
|
+
|
83
|
+
def queues(company, system, environment)
|
84
|
+
signed_get(COMPANIES_PATH, escape(company), escape(system), escape(environment), 'queues/')
|
85
|
+
end
|
86
|
+
|
87
|
+
def bindings(company, system, environment)
|
88
|
+
signed_get(COMPANIES_PATH, escape(company), escape(system), escape(environment), 'bindings/')
|
89
|
+
end
|
90
|
+
|
91
|
+
def exchanges(company, system, environment)
|
92
|
+
signed_get(COMPANIES_PATH, escape(company), escape(system), escape(environment), 'exchanges/')
|
93
|
+
end
|
94
|
+
|
95
|
+
def apis
|
96
|
+
signed_get(APIS_PATH)
|
97
|
+
end
|
98
|
+
|
99
|
+
def amqp_password(company, system, environment, amqp_user)
|
100
|
+
signed_get(COMPANIES_PATH, escape(company), escape(system), escape(environment), 'users', escape(amqp_user))
|
101
|
+
end
|
102
|
+
|
103
|
+
def amqp_users(company, system, environment)
|
104
|
+
signed_get(COMPANIES_PATH, escape(company), escape(system), escape(environment), 'users/')
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def signed_get(*path_args)
|
110
|
+
get(build_signed_resource_url(make_normalised_path(*path_args)))
|
111
|
+
end
|
112
|
+
|
113
|
+
def signed_delete(*path_args)
|
114
|
+
delete(build_signed_resource_url(make_normalised_path(*path_args), 'DELETE'))
|
115
|
+
end
|
116
|
+
|
117
|
+
def signed_put(payload, *path_args)
|
118
|
+
put(build_signed_resource_url(make_normalised_path(*path_args), 'PUT'), payload)
|
119
|
+
end
|
120
|
+
|
121
|
+
def escape(string) # :nodoc:
|
122
|
+
StormMQ::URL.escape(string)
|
123
|
+
end
|
124
|
+
|
125
|
+
def get(signed_url) # :nodoc:
|
126
|
+
JSON.parse(RestClient.get(signed_url.to_s, {:accept => '*/*'}).to_s)
|
127
|
+
end
|
128
|
+
|
129
|
+
def delete(signed_url) # :nodoc:
|
130
|
+
JSON.parse(RestClient.delete(signed_url.to_s).to_s)
|
131
|
+
end
|
132
|
+
|
133
|
+
def put(signed_url, payload) # :nodoc:
|
134
|
+
JSON.parse(RestClient.put(signed_url.to_s, payload, {:accept => '*/*', :content_type => 'application/json'}).to_s)
|
135
|
+
end
|
136
|
+
|
137
|
+
def build_signed_resource_url(resource_path='/', method='GET') # :nodoc:
|
138
|
+
StormMQ::URL.new(
|
139
|
+
{
|
140
|
+
:host => @host,
|
141
|
+
:scheme => 'https',
|
142
|
+
:path => make_normalised_path(@api, @version, resource_path)
|
143
|
+
}.merge(@url_options)
|
144
|
+
).canonicalise_and_sign(@user, @secret_key, method)
|
145
|
+
end
|
146
|
+
|
147
|
+
def make_normalised_path(*args) # :nodoc:
|
148
|
+
('/' + args.join('/')).gsub(/\/+/,'/')
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.secret_key_from_key_store(user) # :nodoc:
|
152
|
+
begin
|
153
|
+
SecretKeys.key_for(user)
|
154
|
+
rescue
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|