swirl 1.6.2 → 1.7.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/README.md +3 -0
- data/lib/swirl.rb +1 -0
- data/lib/swirl/aws.rb +154 -0
- data/lib/swirl/ec2.rb +6 -114
- data/lib/swirl/helpers.rb +2 -2
- data/test/compactor_test.rb +0 -7
- metadata +5 -5
- data/list-types +0 -36
data/README.md
CHANGED
data/lib/swirl.rb
CHANGED
data/lib/swirl/aws.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'base64'
|
3
|
+
require 'net/https'
|
4
|
+
|
5
|
+
require 'crack/xml'
|
6
|
+
require 'hmac-sha2'
|
7
|
+
|
8
|
+
require 'swirl/helpers'
|
9
|
+
|
10
|
+
|
11
|
+
module Swirl
|
12
|
+
|
13
|
+
## Errors
|
14
|
+
class InvalidRequest < StandardError ; end
|
15
|
+
|
16
|
+
|
17
|
+
class AWS
|
18
|
+
include Helpers::Compactor
|
19
|
+
include Helpers::Expander
|
20
|
+
|
21
|
+
@services = {}
|
22
|
+
|
23
|
+
def self.services
|
24
|
+
# This must be modified using `service`
|
25
|
+
@services.dup
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.service(name, url, version)
|
29
|
+
@services[name] = { :url => url, :version => version }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Default Services
|
33
|
+
service :ec2, "https://ec2.amazonaws.com", "2010-11-15"
|
34
|
+
|
35
|
+
|
36
|
+
def initialize(*args)
|
37
|
+
opts = args.last.is_a?(Hash) ? args.pop : Hash.new
|
38
|
+
name = args.shift
|
39
|
+
|
40
|
+
service = self.class.services[name] || {}
|
41
|
+
opts = service.merge(opts)
|
42
|
+
|
43
|
+
@url = opts[:url] ||
|
44
|
+
raise(ArgumentError, "No url given")
|
45
|
+
|
46
|
+
# Convert this here for future reference
|
47
|
+
@url = URI(@url)
|
48
|
+
|
49
|
+
@version = opts[:version] ||
|
50
|
+
raise(ArgumentError, "No version given")
|
51
|
+
|
52
|
+
@aws_access_key_id = \
|
53
|
+
ENV["AWS_ACCESS_KEY_ID"] ||
|
54
|
+
opts[:aws_access_key_id] ||
|
55
|
+
raise(ArgumentError, "No aws_access_key_id given")
|
56
|
+
|
57
|
+
@aws_secret_access_key = \
|
58
|
+
ENV["AWS_SECRET_ACCESS_KEY"] ||
|
59
|
+
opts[:aws_secret_access_key] ||
|
60
|
+
raise(ArgumentError, "No aws_secret_access_key given")
|
61
|
+
|
62
|
+
@hmac = HMAC::SHA256.new(@aws_secret_access_key)
|
63
|
+
end
|
64
|
+
|
65
|
+
def escape(value)
|
66
|
+
CGI.escape(value).gsub(/\+/, "%20")
|
67
|
+
end
|
68
|
+
|
69
|
+
def compile_sorted_form_data(query)
|
70
|
+
valid = query.reject {|_, v| v.nil? }
|
71
|
+
valid.sort.map {|k,v| [k, escape(v)] * "=" } * "&"
|
72
|
+
end
|
73
|
+
|
74
|
+
def compile_signature(method, body)
|
75
|
+
string_to_sign = [method, @url.host, "/", body] * "\n"
|
76
|
+
hmac = @hmac.update(string_to_sign)
|
77
|
+
encoded_sig = Base64.encode64(hmac.digest).chomp
|
78
|
+
escape(encoded_sig)
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Execute an EC2 command, expand the input,
|
83
|
+
# and compact the output
|
84
|
+
#
|
85
|
+
# Examples:
|
86
|
+
# ec2.call("DescribeInstances")
|
87
|
+
# ec2.call("TerminateInstances", "InstanceId" => ["i-1234", "i-993j"]
|
88
|
+
#
|
89
|
+
def call(action, query={}, &blk)
|
90
|
+
call!(action, expand(query)) do |code, data|
|
91
|
+
case code
|
92
|
+
when 200
|
93
|
+
response = compact(data)
|
94
|
+
when 400...500
|
95
|
+
messages = Array(data["Response"]["Errors"]).map {|_, e| e["Message"] }
|
96
|
+
raise InvalidRequest, messages.join(",")
|
97
|
+
else
|
98
|
+
msg = "unexpected response #{code} -> #{data.inspect}"
|
99
|
+
raise InvalidRequest, msg
|
100
|
+
end
|
101
|
+
|
102
|
+
if blk
|
103
|
+
blk.call(response)
|
104
|
+
else
|
105
|
+
response
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def call!(action, query={}, &blk)
|
111
|
+
# Hard coding this here until otherwise needed
|
112
|
+
method = "POST"
|
113
|
+
|
114
|
+
query["Action"] = action
|
115
|
+
query["AWSAccessKeyId"] = @aws_access_key_id
|
116
|
+
query["SignatureMethod"] = "HmacSHA256"
|
117
|
+
query["SignatureVersion"] = "2"
|
118
|
+
query["Timestamp"] = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
119
|
+
query["Version"] = @version
|
120
|
+
|
121
|
+
body = compile_sorted_form_data(query)
|
122
|
+
body += "&" + ["Signature", compile_signature(method, body)].join("=")
|
123
|
+
|
124
|
+
post(body) do |code, xml|
|
125
|
+
if ENV["SWIRL_LOG"]
|
126
|
+
puts response.body
|
127
|
+
end
|
128
|
+
|
129
|
+
data = Crack::XML.parse(xml)
|
130
|
+
blk.call(code, data)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def post(body, &blk)
|
135
|
+
headers = { "Content-Type" => "application/x-www-form-urlencoded" }
|
136
|
+
|
137
|
+
http = Net::HTTP.new(@url.host, @url.port)
|
138
|
+
http.use_ssl = true
|
139
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
140
|
+
|
141
|
+
request = Net::HTTP::Post.new(@url.request_uri, headers)
|
142
|
+
request.body = body
|
143
|
+
|
144
|
+
response = http.request(request)
|
145
|
+
blk.call(response.code.to_i, response.body)
|
146
|
+
end
|
147
|
+
|
148
|
+
def inspect
|
149
|
+
"<#{self.class.name} version: #{@version} url: #{@url} aws_access_key_id: #{@aws_access_key_id}>"
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
data/lib/swirl/ec2.rb
CHANGED
@@ -1,123 +1,15 @@
|
|
1
|
-
require '
|
2
|
-
require 'base64'
|
3
|
-
require 'net/https'
|
1
|
+
require 'swirl/aws'
|
4
2
|
|
5
|
-
|
6
|
-
require 'hmac-sha2'
|
7
|
-
|
8
|
-
require 'swirl/helpers'
|
3
|
+
$stderr.puts "WARNING: Requiring 'swirl' or 'swirl/ec2' will be deprecated. Require 'swirl/aws' instead."
|
9
4
|
|
10
5
|
module Swirl
|
11
6
|
|
12
|
-
|
13
|
-
class InvalidRequest < StandardError ; end
|
14
|
-
|
15
|
-
|
7
|
+
# Compat
|
16
8
|
class EC2
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def initialize(options)
|
22
|
-
@aws_access_key_id =
|
23
|
-
options[:aws_access_key_id] ||
|
24
|
-
(raise ArgumentError, "no aws_access_key_id provided")
|
25
|
-
@aws_secret_access_key =
|
26
|
-
options[:aws_secret_access_key] ||
|
27
|
-
(raise ArgumentError, "no aws_secret_access_key provided")
|
28
|
-
|
29
|
-
@hmac = HMAC::SHA256.new(@aws_secret_access_key)
|
30
|
-
@version = options[:version] || "2009-11-30"
|
31
|
-
@url = URI(options[:url] || "https://ec2.amazonaws.com")
|
32
|
-
end
|
33
|
-
|
34
|
-
def escape(value)
|
35
|
-
CGI.escape(value).gsub(/\+/, "%20")
|
36
|
-
end
|
37
|
-
|
38
|
-
def compile_sorted_form_data(query)
|
39
|
-
valid = query.reject {|_, v| v.nil? }
|
40
|
-
valid.sort.map {|k,v| [k, escape(v)] * "=" } * "&"
|
41
|
-
end
|
42
|
-
|
43
|
-
def compile_signature(method, body)
|
44
|
-
string_to_sign = [method, @url.host, "/", body] * "\n"
|
45
|
-
hmac = @hmac.update(string_to_sign)
|
46
|
-
encoded_sig = Base64.encode64(hmac.digest).chomp
|
47
|
-
escape(encoded_sig)
|
48
|
-
end
|
49
|
-
|
50
|
-
##
|
51
|
-
# Execute an EC2 command, expand the input,
|
52
|
-
# and compact the output
|
53
|
-
#
|
54
|
-
# Examples:
|
55
|
-
# ec2.call("DescribeInstances")
|
56
|
-
# ec2.call("TerminateInstances", "InstanceId" => ["i-1234", "i-993j"]
|
57
|
-
#
|
58
|
-
def call(action, query={}, &blk)
|
59
|
-
call!(action, expand(query)) do |code, data|
|
60
|
-
case code
|
61
|
-
when 200
|
62
|
-
response = compact(data)
|
63
|
-
when 400...500
|
64
|
-
messages = Array(data["Response"]["Errors"]).map {|_, e| e["Message"] }
|
65
|
-
raise InvalidRequest, messages.join(",")
|
66
|
-
else
|
67
|
-
msg = "unexpected response #{code} -> #{data.inspect}"
|
68
|
-
raise InvalidRequest, msg
|
69
|
-
end
|
70
|
-
|
71
|
-
if blk
|
72
|
-
blk.call(response)
|
73
|
-
else
|
74
|
-
response
|
75
|
-
end
|
76
|
-
end
|
9
|
+
def self.new(options)
|
10
|
+
$stderr.puts "WARNING: Swirl::EC2 will be deprecated. Use Swirl::AWS.new(:ec2) instead"
|
11
|
+
return AWS.new(:ec2, options)
|
77
12
|
end
|
78
|
-
|
79
|
-
def call!(action, query={}, &blk)
|
80
|
-
# Hard coding this here until otherwise needed
|
81
|
-
method = "POST"
|
82
|
-
|
83
|
-
query["Action"] = action
|
84
|
-
query["AWSAccessKeyId"] = @aws_access_key_id
|
85
|
-
query["SignatureMethod"] = "HmacSHA256"
|
86
|
-
query["SignatureVersion"] = "2"
|
87
|
-
query["Timestamp"] = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
88
|
-
query["Version"] = @version
|
89
|
-
|
90
|
-
body = compile_sorted_form_data(query)
|
91
|
-
body += "&" + ["Signature", compile_signature(method, body)].join("=")
|
92
|
-
|
93
|
-
post(body) do |code, xml|
|
94
|
-
if ENV["SWIRL_LOG"]
|
95
|
-
puts response.body
|
96
|
-
end
|
97
|
-
|
98
|
-
data = Crack::XML.parse(xml)
|
99
|
-
blk.call(code, data)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def post(body, &blk)
|
104
|
-
headers = { "Content-Type" => "application/x-www-form-urlencoded" }
|
105
|
-
|
106
|
-
http = Net::HTTP.new(@url.host, @url.port)
|
107
|
-
http.use_ssl = true
|
108
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
109
|
-
|
110
|
-
request = Net::HTTP::Post.new(@url.request_uri, headers)
|
111
|
-
request.body = body
|
112
|
-
|
113
|
-
response = http.request(request)
|
114
|
-
blk.call(response.code.to_i, response.body)
|
115
|
-
end
|
116
|
-
|
117
|
-
def inspect
|
118
|
-
"<#{self.class.name} version: #{@version} url: #{@url} aws_access_key_id: #{@aws_access_key_id}>"
|
119
|
-
end
|
120
|
-
|
121
13
|
end
|
122
14
|
|
123
15
|
end
|
data/lib/swirl/helpers.rb
CHANGED
@@ -37,8 +37,8 @@ module Swirl
|
|
37
37
|
|
38
38
|
def compact!(data)
|
39
39
|
data.inject({}) do |com, (key, value)|
|
40
|
-
if
|
41
|
-
converted = if value
|
40
|
+
if value.is_a?(Hash)
|
41
|
+
converted = if value.has_key?("item")
|
42
42
|
items = value["item"]
|
43
43
|
items ||= []
|
44
44
|
items = items.is_a?(Array) ? items : [items]
|
data/test/compactor_test.rb
CHANGED
@@ -39,13 +39,6 @@ class CompactorTest < Test::Unit::TestCase
|
|
39
39
|
assert_equal expected, Compactor.compact(response)
|
40
40
|
end
|
41
41
|
|
42
|
-
test "makes value empty Array if nil" do
|
43
|
-
response = { "Foo" => { "groupSet" => nil } }
|
44
|
-
expected = { "groupSet" => [] }
|
45
|
-
|
46
|
-
assert_equal expected, Compactor.compact(response)
|
47
|
-
end
|
48
|
-
|
49
42
|
test "traverses list values and compacts" do
|
50
43
|
response = {
|
51
44
|
"Foo" => {
|
metadata
CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 1.
|
8
|
+
- 7
|
9
|
+
- 0
|
10
|
+
version: 1.7.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Blake Mizerany
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-02-
|
18
|
+
date: 2011-02-28 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -78,7 +78,7 @@ extra_rdoc_files:
|
|
78
78
|
files:
|
79
79
|
- LICENSE
|
80
80
|
- README.md
|
81
|
-
-
|
81
|
+
- lib/swirl/aws.rb
|
82
82
|
- lib/swirl/ec2.rb
|
83
83
|
- lib/swirl/helpers.rb
|
84
84
|
- lib/swirl.rb
|
data/list-types
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
begin
|
4
|
-
require 'nokogiri'
|
5
|
-
rescue LoadError
|
6
|
-
abort <<-MSG
|
7
|
-
Unable to load nokogiri.
|
8
|
-
Ensure you have it installed and/or
|
9
|
-
export RUBYOPT=rubygems
|
10
|
-
MSG
|
11
|
-
end
|
12
|
-
|
13
|
-
require 'open-uri'
|
14
|
-
require 'stringio'
|
15
|
-
require 'pp'
|
16
|
-
|
17
|
-
DefaultWSDL = "http://s3.amazonaws.com/ec2-downloads/2008-02-01.ec2.wsdl"
|
18
|
-
|
19
|
-
wsdl_url = ARGV.shift || DefaultWSDL
|
20
|
-
Wsdl = Nokogiri::XML.parse(open(wsdl_url))
|
21
|
-
|
22
|
-
##__END__
|
23
|
-
|
24
|
-
list_keys = []
|
25
|
-
item_types = Wsdl.search(%Q|//xs:complexType//xs:element[@name="item"]|)
|
26
|
-
item_types.each do |item_type|
|
27
|
-
ns, name = item_type["type"].split(":")
|
28
|
-
next if ns != "tns"
|
29
|
-
complex = item_type.parent.parent
|
30
|
-
uses = Wsdl.search(%Q|//xs:element[@type="#{ns}:#{complex["name"]}"]|)
|
31
|
-
uses.each do |u|
|
32
|
-
list_keys << u["name"]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
pp list_keys.uniq
|