miasma 0.2.10 → 0.2.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 039c6245899398e6105945fa52ea6b60a6f8b496
4
- data.tar.gz: a50c93103661c9d7919ea9453b095d13d7cbe272
3
+ metadata.gz: b7396d4fda42e875b71624a0eca9ab303a606140
4
+ data.tar.gz: dc2d67fa4c59fdb2a0d0411bfbb099f91bc753dc
5
5
  SHA512:
6
- metadata.gz: 30654c515d23a58a58355b7b61ba4fc15179b0cb212dbeaa17bf27879bbba0494516efb1570bb45df29d5d157a6c7f243fdc57c02a0537847524ac651b734528
7
- data.tar.gz: e609e7a4a63d73df54f783ee6d33854b1dee23506a25aeaeeac66e14cf2e85a5d4e2ccae0918be602825511c55f12e1b7951e113585dafe7c059c6c6393874eb
6
+ metadata.gz: 4c58428f59a25b11576541387304375e4d7232c0016fbcef9fbaa5dfb746eda92a85cad33d7fc474e808c11ded50425b4699618fb6cec67c6a81be5728346a4c
7
+ data.tar.gz: 361b96d5a94c44335cc68f34b4cb7b36006cd85c270189aedb4356480dd17c5064d1da12a1702c20158a5ca87457681b3aca5a6d3f26a317309135ed11a83f2d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # v0.2.12
2
+ * Extract provider implementations to standalone libraries
3
+ * Add test helper executable
4
+ * Update API body extraction to retype Array contents
5
+ * Add streamable helper for consistent storage file read
6
+
1
7
  # V0.2.10
2
8
  * Add auto-follow to paginated results on aws
3
9
  * Speed up stack list building on aws
data/README.md CHANGED
@@ -141,54 +141,17 @@ model completions.
141
141
 
142
142
  ### Currently Supported Providers
143
143
 
144
- * AWS
145
- * Rackspace
146
- * OpenStack
147
-
148
- ### Models
149
-
150
- #### AWS
151
-
152
- |Model |Create|Read|Update|Delete|
153
- |--------------|------|----|------|------|
154
- |AutoScale | X | X | | |
155
- |BlockStorage | | | | |
156
- |Compute | X | X | | X |
157
- |DNS | | | | |
158
- |LoadBalancer | X | X | X | X |
159
- |Network | | | | |
160
- |Orchestration | X | X | X | X |
161
- |Queues | | | | |
162
- |Storage | X | X | X | X |
163
-
164
- #### Rackspace
165
-
166
- |Model |Create|Read|Update|Delete|
167
- |--------------|------|----|------|------|
168
- |AutoScale | X | X | | |
169
- |BlockStorage | | | | |
170
- |Compute | X | X | | X |
171
- |DNS | | | | |
172
- |LoadBalancer | | X | | |
173
- |Network | | | | |
174
- |Orchestration | X | X | X | X |
175
- |Queues | | | | |
176
- |Storage | | | | |
177
-
178
- #### OpenStack
179
-
180
- |Model |Create|Read|Update|Delete|
181
- |--------------|------|----|------|------|
182
- |AutoScale | | | | |
183
- |BlockStorage | | | | |
184
- |Compute | X | X | | X |
185
- |DNS | | | | |
186
- |LoadBalancer | | | | |
187
- |Network | | | | |
188
- |Orchestration | X | X | X | X |
189
- |Queues | | | | |
190
- |Storage | | | | |
144
+ Coverage currently varies from provider to provider
145
+ based on functionality restrictions and in progress
146
+ implementation goals. The README on each library
147
+ should provide a simple feature matrix for a quick
148
+ check on support availability:
149
+
150
+ * [AWS](https://github.com/miasma-rb/miasma-aws)
151
+ * [Rackspace](https://github.com/miasma-rb/miasma-rackspace)
152
+ * [OpenStack](https://github.com/miasma-rb/miasma-open-stack)
153
+ * [Local](https://github.com/miasma-rb/miasma-local)
191
154
 
192
155
  ## Info
193
156
 
194
- * Repository: https://github.com/chrisroberts/miasma
157
+ * Repository: https://github.com/miasma-rb/miasma
data/bin/miasma-test ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'vcr'
4
+ require 'fileutils'
5
+ require 'webmock/minitest'
6
+ require 'minitest/autorun'
7
+
8
+ require 'miasma'
9
+
10
+ miasma_spec_dir = File.join(File.dirname(File.dirname(__FILE__)), 'test', 'specs')
11
+
12
+ # Always load in generic model specs
13
+ Dir.glob(File.join(miasma_spec_dir, 'models', '*.rb')).each do |path|
14
+ require File.expand_path(path)
15
+ end
16
+
17
+ library_spec_dir = File.join(Dir.pwd, 'test', 'specs')
18
+
19
+ unless(File.directory?(library_spec_dir))
20
+ $stderr.puts "ERROR: Failed to locate expected spec directory! (#{library_spec_dir})"
21
+ exit -1
22
+ end
23
+
24
+ if(ARGV.empty?)
25
+ cassette_directory = File.join(library_spec_dir, 'cassettes')
26
+
27
+ Dir.glob(File.join(library_spec_dir, '**/**/*_spec.rb')).each do |path|
28
+ require File.expand_path(path)
29
+ end
30
+
31
+ else
32
+ cassette_directory = File.join(library_spec_dir, 'cassettes')
33
+
34
+ ARGV.each do |path|
35
+ full_path = File.expand_path(path)
36
+ if(File.exists?(full_path))
37
+ require full_path
38
+ else
39
+ $stderr.puts "ERROR: Failed to locate specified path! (#{full_path})"
40
+ exit -1
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ FileUtils.mkdir_p(cassette_directory)
47
+
48
+ VCR.configure do |c|
49
+ c.cassette_library_dir = cassette_directory
50
+ c.hook_into :webmock
51
+ end
@@ -8,6 +8,42 @@ module Miasma
8
8
  # Abstract file
9
9
  class File < Types::Model
10
10
 
11
+ # Simple wrapper to keep consistent reading behavior
12
+ class Streamable
13
+
14
+ # @return [Object] IO-ish thing
15
+ attr_reader :io
16
+
17
+ def initialize(io_item)
18
+ unless(io_item.respond_to?(:readpartial))
19
+ raise TypeError.new 'Instance must respond to `#readpartial`'
20
+ end
21
+ @io = io_item
22
+ end
23
+
24
+ # Proxy missing methods to io
25
+ def method_missing(method_name, *args, &block)
26
+ if(io.respond_to?(method_name))
27
+ io.send(method_name, *args, &block)
28
+ else
29
+ raise
30
+ end
31
+ end
32
+
33
+ # Customized readpartial to automatically hand EOF
34
+ #
35
+ # @param length [Integer] length to read
36
+ # @return [String]
37
+ def readpartial(length=nil)
38
+ begin
39
+ io.readpartial(length)
40
+ rescue EOFError
41
+ nil
42
+ end
43
+ end
44
+
45
+ end
46
+
11
47
  attribute :name, String, :required => true
12
48
  attribute :content_type, String
13
49
  attribute :content_disposition, String
@@ -123,13 +123,27 @@ module Miasma
123
123
  if(extract_body)
124
124
  if(extracted_headers[:content_type].to_s.include?('json'))
125
125
  begin
126
- extracted_body = MultiJson.load(result.body.to_s).to_smash
126
+ extracted_body = MultiJson.load(result.body.to_s)
127
+ if(extracted_body.respond_to?(:to_smash))
128
+ extracted_body = extracted_body.to_smash
129
+ elsif(extracted_body.respond_to?(:map!))
130
+ extracted_body.map! do |i|
131
+ i.respond_to?(:to_smash) ? i.to_smash : i
132
+ end
133
+ end
127
134
  rescue MultiJson::ParseError
128
135
  extracted_body = result.body.to_s
129
136
  end
130
137
  elsif(extracted_headers[:content_type].to_s.include?('xml'))
131
138
  begin
132
- extracted_body = MultiXml.parse(result.body.to_s).to_smash
139
+ extracted_body = MultiXml.parse(result.body.to_s)
140
+ if(extracted_body.respond_to?(:to_smash))
141
+ extracted_body = extracted_body.to_smash
142
+ elsif(extracted_body.respond_to?(:map!))
143
+ extracted_body.map! do |i|
144
+ i.respond_to?(:to_smash) ? i.to_smash : i
145
+ end
146
+ end
133
147
  rescue MultiXml::ParseError
134
148
  extracted_body = result.body.to_s
135
149
  end
@@ -1,4 +1,4 @@
1
1
  module Miasma
2
2
  # current library version
3
- VERSION = Gem::Version.new('0.2.10')
3
+ VERSION = Gem::Version.new('0.2.12')
4
4
  end
data/miasma.gemspec CHANGED
@@ -6,14 +6,24 @@ Gem::Specification.new do |s|
6
6
  s.summary = 'Smoggy API'
7
7
  s.author = 'Chris Roberts'
8
8
  s.email = 'code@chrisroberts.org'
9
- s.homepage = 'https://github.com/chrisroberts/miasma'
9
+ s.homepage = 'https://github.com/miasma-rb/miasma'
10
10
  s.description = 'Smoggy API'
11
11
  s.license = 'Apache 2.0'
12
12
  s.require_path = 'lib'
13
- s.add_dependency 'hashie'
14
- s.add_dependency 'http'
15
- s.add_dependency 'multi_json'
16
- s.add_dependency 'multi_xml'
17
- s.add_dependency 'xml-simple'
13
+ s.add_runtime_dependency 'hashie'
14
+ s.add_runtime_dependency 'http'
15
+ s.add_runtime_dependency 'multi_json'
16
+ s.add_runtime_dependency 'multi_xml'
17
+ s.add_runtime_dependency 'xml-simple'
18
+ # Include provider libs that do not have outside deps
19
+ s.add_runtime_dependency 'miasma-aws'
20
+ s.add_runtime_dependency 'miasma-open-stack'
21
+ s.add_runtime_dependency 'miasma-rackspace'
22
+ s.executables << 'miasma-test'
23
+ # Include development dependencies for running tests
24
+ s.add_development_dependency 'pry'
25
+ s.add_development_dependency 'minitest'
26
+ s.add_development_dependency 'vcr'
27
+ s.add_development_dependency 'webmock'
18
28
  s.files = Dir['lib/**/*'] + %w(miasma.gemspec README.md CHANGELOG.md LICENSE)
19
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miasma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.10
4
+ version: 0.2.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-21 00:00:00.000000000 Z
11
+ date: 2015-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie
@@ -80,30 +80,116 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: miasma-aws
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: miasma-open-stack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: miasma-rackspace
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: minitest
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: vcr
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: webmock
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
83
181
  description: Smoggy API
84
182
  email: code@chrisroberts.org
85
- executables: []
183
+ executables:
184
+ - miasma-test
86
185
  extensions: []
87
186
  extra_rdoc_files: []
88
187
  files:
89
188
  - CHANGELOG.md
90
189
  - LICENSE
91
190
  - README.md
191
+ - bin/miasma-test
92
192
  - lib/miasma.rb
93
- - lib/miasma/contrib/aws.rb
94
- - lib/miasma/contrib/aws/auto_scale.rb
95
- - lib/miasma/contrib/aws/compute.rb
96
- - lib/miasma/contrib/aws/load_balancer.rb
97
- - lib/miasma/contrib/aws/orchestration.rb
98
- - lib/miasma/contrib/aws/storage.rb
99
- - lib/miasma/contrib/open_stack.rb
100
- - lib/miasma/contrib/open_stack/compute.rb
101
- - lib/miasma/contrib/open_stack/orchestration.rb
102
- - lib/miasma/contrib/rackspace.rb
103
- - lib/miasma/contrib/rackspace/auto_scale.rb
104
- - lib/miasma/contrib/rackspace/compute.rb
105
- - lib/miasma/contrib/rackspace/load_balancer.rb
106
- - lib/miasma/contrib/rackspace/orchestration.rb
107
193
  - lib/miasma/error.rb
108
194
  - lib/miasma/models.rb
109
195
  - lib/miasma/models/auto_scale.rb
@@ -145,7 +231,7 @@ files:
145
231
  - lib/miasma/utils/smash.rb
146
232
  - lib/miasma/version.rb
147
233
  - miasma.gemspec
148
- homepage: https://github.com/chrisroberts/miasma
234
+ homepage: https://github.com/miasma-rb/miasma
149
235
  licenses:
150
236
  - Apache 2.0
151
237
  metadata: {}
@@ -1,444 +0,0 @@
1
- require 'miasma'
2
- require 'miasma/utils/smash'
3
-
4
- require 'time'
5
- require 'openssl'
6
-
7
- module Miasma
8
- module Contrib
9
- # Core API for AWS access
10
- class AwsApiCore
11
-
12
- module RequestUtils
13
-
14
- # Fetch all results when tokens are being used
15
- # for paging results
16
- #
17
- # @param next_token [String]
18
- # @param result_key [Array<String, Symbol>] path to result
19
- # @yield block to perform request
20
- # @yieldparam options [Hash] request parameters (token information)
21
- # @return [Array]
22
- def all_result_pages(next_token, *result_key, &block)
23
- list = []
24
- options = next_token ? Smash.new('NextToken' => next_token) : Smash.new
25
- result = block.call(options)
26
- content = result.get(*result_key.dup)
27
- if(content.is_a?(Array))
28
- list += content
29
- else
30
- list << content
31
- end
32
- set = result.get(*result_key.slice(0, 3))
33
- if(set && set['NextToken'])
34
- list += all_result_pages(set['NextToken'], *result_key, &block)
35
- end
36
- list.compact
37
- end
38
-
39
- end
40
-
41
- # @return [String] current time ISO8601 format
42
- def self.time_iso8601
43
- Time.now.utc.strftime('%Y%m%dT%H%M%SZ')
44
- end
45
-
46
- # HMAC helper class
47
- class Hmac
48
-
49
- # @return [OpenSSL::Digest]
50
- attr_reader :digest
51
- # @return [String] secret key
52
- attr_reader :key
53
-
54
- # Create new HMAC helper
55
- #
56
- # @param kind [String] digest type (sha1, sha256, sha512, etc)
57
- # @param key [String] secret key
58
- # @return [self]
59
- def initialize(kind, key)
60
- @digest = OpenSSL::Digest.new(kind)
61
- @key = key
62
- end
63
-
64
- # @return [String]
65
- def to_s
66
- "Hmac#{digest.name}"
67
- end
68
-
69
- # Generate the hexdigest of the content
70
- #
71
- # @param content [String] content to digest
72
- # @return [String] hashed result
73
- def hexdigest_of(content)
74
- digest << content
75
- hash = digest.hexdigest
76
- digest.reset
77
- hash
78
- end
79
-
80
- # Sign the given data
81
- #
82
- # @param data [String]
83
- # @param key_override [Object]
84
- # @return [Object] signature
85
- def sign(data, key_override=nil)
86
- result = OpenSSL::HMAC.digest(digest, key_override || key, data)
87
- digest.reset
88
- result
89
- end
90
-
91
- # Sign the given data and return hexdigest
92
- #
93
- # @param data [String]
94
- # @param key_override [Object]
95
- # @return [String] hex encoded signature
96
- def hex_sign(data, key_override=nil)
97
- result = OpenSSL::HMAC.hexdigest(digest, key_override || key, data)
98
- digest.reset
99
- result
100
- end
101
-
102
- end
103
-
104
- # Base signature class
105
- class Signature
106
-
107
- # Create new instance
108
- def initialize(*args)
109
- raise NotImplementedError.new 'This class should not be used directly!'
110
- end
111
-
112
- # Generate the signature
113
- #
114
- # @param http_method [Symbol] HTTP request method
115
- # @param path [String] request path
116
- # @param opts [Hash] request options
117
- # @return [String] signature
118
- def generate(http_method, path, opts={})
119
- raise NotImplementedError
120
- end
121
-
122
- # URL string escape compatible with AWS requirements
123
- #
124
- # @param string [String] string to escape
125
- # @return [String] escaped string
126
- def safe_escape(string)
127
- string.to_s.gsub(/([^a-zA-Z0-9_.\-~])/) do
128
- '%' << $1.unpack('H2' * $1.bytesize).join('%').upcase
129
- end
130
- end
131
-
132
- end
133
-
134
- # AWS signature version 4
135
- class SignatureV4 < Signature
136
-
137
- # @return [Hmac]
138
- attr_reader :hmac
139
- # @return [String] access key
140
- attr_reader :access_key
141
- # @return [String] region
142
- attr_reader :region
143
- # @return [String] service
144
- attr_reader :service
145
-
146
- # Create new signature generator
147
- #
148
- # @param access_key [String]
149
- # @param secret_key [String]
150
- # @param region [String]
151
- # @param service [String]
152
- # @return [self]
153
- def initialize(access_key, secret_key, region, service)
154
- @hmac = Hmac.new('sha256', secret_key)
155
- @access_key = access_key
156
- @region = region
157
- @service = service
158
- end
159
-
160
- # Generate the signature string for AUTH
161
- #
162
- # @param http_method [Symbol] HTTP request method
163
- # @param path [String] request path
164
- # @param opts [Hash] request options
165
- # @return [String] signature
166
- def generate(http_method, path, opts)
167
- signature = generate_signature(http_method, path, opts)
168
- "#{algorithm} Credential=#{access_key}/#{credential_scope}, SignedHeaders=#{signed_headers(opts[:headers])}, Signature=#{signature}"
169
- end
170
-
171
- # Generate URL with signed params
172
- #
173
- # @param http_method [Symbol] HTTP request method
174
- # @param path [String] request path
175
- # @param opts [Hash] request options
176
- # @return [String] signature
177
- def generate_url(http_method, path, opts)
178
- opts[:params].merge!(
179
- Smash.new(
180
- 'X-Amz-SignedHeaders' => signed_headers(opts[:headers]),
181
- 'X-Amz-Algorithm' => algorithm,
182
- 'X-Amz-Credential' => "#{access_key}/#{credential_scope}"
183
- )
184
- )
185
- signature = generate_signature(http_method, path, opts.merge(:body => 'UNSIGNED-PAYLOAD'))
186
- params = opts[:params].merge('X-Amz-Signature' => signature)
187
- "https://#{opts[:headers]['Host']}/#{path}?#{canonical_query(params)}"
188
- end
189
-
190
- # Generate the signature
191
- #
192
- # @param http_method [Symbol] HTTP request method
193
- # @param path [String] request path
194
- # @param opts [Hash] request options
195
- # @return [String] signature
196
- def generate_signature(http_method, path, opts)
197
- to_sign = [
198
- algorithm,
199
- AwsApiCore.time_iso8601,
200
- credential_scope,
201
- hashed_canonical_request(
202
- can_req = build_canonical_request(http_method, path, opts)
203
- )
204
- ].join("\n")
205
- signature = sign_request(to_sign)
206
- end
207
-
208
- # Sign the request
209
- #
210
- # @param request [String] request to sign
211
- # @return [String] signature
212
- def sign_request(request)
213
- key = hmac.sign(
214
- 'aws4_request',
215
- hmac.sign(
216
- service,
217
- hmac.sign(
218
- region,
219
- hmac.sign(
220
- Time.now.utc.strftime('%Y%m%d'),
221
- "AWS4#{hmac.key}"
222
- )
223
- )
224
- )
225
- )
226
- hmac.hex_sign(request, key)
227
- end
228
-
229
- # @return [String] signature algorithm
230
- def algorithm
231
- 'AWS4-HMAC-SHA256'
232
- end
233
-
234
- # @return [String] credential scope for request
235
- def credential_scope
236
- [
237
- Time.now.utc.strftime('%Y%m%d'),
238
- region,
239
- service,
240
- 'aws4_request'
241
- ].join('/')
242
- end
243
-
244
- # Generate the hash of the canonical request
245
- #
246
- # @param request [String] canonical request string
247
- # @return [String] hashed canonical request
248
- def hashed_canonical_request(request)
249
- hmac.hexdigest_of(request)
250
- end
251
-
252
- # Build the canonical request string used for signing
253
- #
254
- # @param http_method [Symbol] HTTP request method
255
- # @param path [String] request path
256
- # @param opts [Hash] request options
257
- # @return [String] canonical request string
258
- def build_canonical_request(http_method, path, opts)
259
- unless(path.start_with?('/'))
260
- path = "/#{path}"
261
- end
262
- [
263
- http_method.to_s.upcase,
264
- path,
265
- canonical_query(opts[:params]),
266
- canonical_headers(opts[:headers]),
267
- signed_headers(opts[:headers]),
268
- canonical_payload(opts)
269
- ].join("\n")
270
- end
271
-
272
- # Build the canonical query string used for signing
273
- #
274
- # @param params [Hash] query params
275
- # @return [String] canonical query string
276
- def canonical_query(params)
277
- params ||= {}
278
- params = Hash[params.sort_by(&:first)]
279
- query = params.map do |key, value|
280
- "#{safe_escape(key)}=#{safe_escape(value)}"
281
- end.join('&')
282
- end
283
-
284
- # Build the canonical header string used for signing
285
- #
286
- # @param headers [Hash] request headers
287
- # @return [String] canonical headers string
288
- def canonical_headers(headers)
289
- headers ||= {}
290
- headers = Hash[headers.sort_by(&:first)]
291
- headers.map do |key, value|
292
- [key.downcase, value.chomp].join(':')
293
- end.join("\n") << "\n"
294
- end
295
-
296
- # List of headers included in signature
297
- #
298
- # @param headers [Hash] request headers
299
- # @return [String] header list
300
- def signed_headers(headers)
301
- headers ||= {}
302
- headers.sort_by(&:first).map(&:first).
303
- map(&:downcase).join(';')
304
- end
305
-
306
- # Build the canonical payload string used for signing
307
- #
308
- # @param options [Hash] request options
309
- # @return [String] body checksum
310
- def canonical_payload(options)
311
- body = options.fetch(:body, '')
312
- if(options[:json])
313
- body = MultiJson.dump(options[:json])
314
- elsif(options[:form])
315
- body = URI.encode_www_form(options[:form])
316
- end
317
- if(body == 'UNSIGNED-PAYLOAD')
318
- body
319
- else
320
- hmac.hexdigest_of(body)
321
- end
322
- end
323
-
324
- end
325
-
326
- module ApiCommon
327
-
328
- def self.included(klass)
329
- klass.class_eval do
330
- attribute :aws_access_key_id, String, :required => true
331
- attribute :aws_secret_access_key, String, :required => true
332
- attribute :aws_region, String, :required => true
333
- attribute :aws_host, String
334
- attribute :aws_bucket_region, String
335
-
336
- # @return [Contrib::AwsApiCore::SignatureV4]
337
- attr_reader :signer
338
- end
339
- end
340
-
341
- # Build new API for specified type using current provider / creds
342
- #
343
- # @param type [Symbol] api type
344
- # @return [Api]
345
- def api_for(type)
346
- memoize(type) do
347
- creds = attributes.dup
348
- creds.delete(:aws_host)
349
- Miasma.api(
350
- Smash.new(
351
- :type => type,
352
- :provider => provider,
353
- :credentials => creds
354
- )
355
- )
356
- end
357
- end
358
-
359
- # Setup for API connections
360
- def connect
361
- unless(aws_host)
362
- self.aws_host = [
363
- self.class::API_SERVICE.downcase,
364
- aws_region,
365
- 'amazonaws.com'
366
- ].join('.')
367
- end
368
- @signer = Contrib::AwsApiCore::SignatureV4.new(
369
- aws_access_key_id, aws_secret_access_key, aws_region, self.class::API_SERVICE
370
- )
371
- end
372
-
373
- # @return [String] custom escape for aws compat
374
- def uri_escape(string)
375
- signer.safe_escape(string)
376
- end
377
-
378
- # @return [HTTP] connection for requests (forces headers)
379
- def connection
380
- super.with_headers(
381
- 'Host' => aws_host,
382
- 'X-Amz-Date' => Contrib::AwsApiCore.time_iso8601
383
- )
384
- end
385
-
386
- # @return [String] endpoint for request
387
- def endpoint
388
- "https://#{aws_host}"
389
- end
390
-
391
- # Override to inject signature
392
- #
393
- # @param connection [HTTP]
394
- # @param http_method [Symbol]
395
- # @param request_args [Array]
396
- # @return [HTTP::Response]
397
- # @note if http_method is :post, params will be automatically
398
- # removed and placed into :form
399
- def make_request(connection, http_method, request_args)
400
- dest, options = request_args
401
- path = URI.parse(dest).path
402
- options = options ? options.to_smash : Smash.new
403
- options[:params] = options.fetch(:params, Smash.new).to_smash.deep_merge('Version' => self.class::API_VERSION)
404
- if(http_method.to_sym == :post)
405
- if(options[:form])
406
- options[:form].merge(options.delete(:params))
407
- else
408
- options[:form] = options.delete(:params)
409
- end
410
- end
411
- update_request(connection, options)
412
- signature = signer.generate(
413
- http_method, path, options.merge(
414
- Smash.new(
415
- :headers => Smash[
416
- connection.default_headers.to_a
417
- ]
418
- )
419
- )
420
- )
421
- options = Hash[options.map{|k,v|[k.to_sym,v]}]
422
- connection.auth(signature).send(http_method, dest, options)
423
- end
424
-
425
- # Simple callback to allow request option adjustments prior to
426
- # signature calculation
427
- #
428
- # @param opts [Smash] request options
429
- # @return [TrueClass]
430
- def update_request(con, opts)
431
- true
432
- end
433
-
434
- end
435
-
436
- end
437
- end
438
-
439
- Models::Compute.autoload :Aws, 'miasma/contrib/aws/compute'
440
- Models::LoadBalancer.autoload :Aws, 'miasma/contrib/aws/load_balancer'
441
- Models::AutoScale.autoload :Aws, 'miasma/contrib/aws/auto_scale'
442
- Models::Orchestration.autoload :Aws, 'miasma/contrib/aws/orchestration'
443
- Models::Storage.autoload :Aws, 'miasma/contrib/aws/storage'
444
- end