deltacloud-core 0.0.6 → 0.0.7
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/deltacloud.rb +2 -0
- data/lib/deltacloud/base_driver/base_driver.rb +30 -0
- data/lib/deltacloud/base_driver/features.rb +7 -0
- data/lib/deltacloud/base_driver/mock_driver.rb +18 -0
- data/lib/deltacloud/drivers/azure/azure_driver.rb +127 -0
- data/lib/deltacloud/drivers/ec2/ec2_driver.rb +128 -4
- data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +5 -2
- data/lib/deltacloud/drivers/mock/mock_driver.rb +10 -16
- data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +1 -1
- data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +110 -11
- data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +7 -4
- data/lib/deltacloud/helpers/application_helper.rb +8 -1
- data/lib/deltacloud/helpers/blob_stream.rb +51 -0
- data/lib/deltacloud/models/blob.rb +26 -0
- data/lib/deltacloud/models/bucket.rb +24 -0
- data/lib/drivers.rb +1 -0
- data/lib/sinatra/respond_to.rb +147 -181
- data/lib/sinatra/respond_to_old.rb +253 -0
- data/server.rb +104 -22
- data/tests/instance_states_test.rb +1 -2
- data/tests/instances_test.rb +12 -9
- data/tests/url_for_test.rb +1 -1
- data/views/blobs/show.html.haml +20 -0
- data/views/blobs/show.xml.haml +7 -0
- data/views/buckets/index.html.haml +33 -0
- data/views/buckets/index.xml.haml +10 -0
- data/views/buckets/new.html.haml +13 -0
- data/views/buckets/show.html.haml +19 -0
- data/views/buckets/show.xml.haml +8 -0
- data/views/instance_states/show.html.haml +1 -1
- data/views/instance_states/{show.gv.erb → show.png.erb} +0 -0
- metadata +61 -33
@@ -0,0 +1,253 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'sinatra/accept_media_types'
|
3
|
+
|
4
|
+
# Accept header parsing was looked at but deemed
|
5
|
+
# too much of an irregularity to deal with. Problems with the header
|
6
|
+
# differences from IE, Firefox, Safari, and every other UA causes
|
7
|
+
# problems with the expected output. The general expected behavior
|
8
|
+
# would be serve html when no extension provided, but most UAs say
|
9
|
+
# they will accept application/xml with out a quality indicator, meaning
|
10
|
+
# you'd get the xml block served insead. Just plain retarded, use the
|
11
|
+
# extension and you'll never be suprised.
|
12
|
+
|
13
|
+
module Sinatra
|
14
|
+
module RespondTo
|
15
|
+
class UnhandledFormat < Sinatra::NotFound; end
|
16
|
+
class MissingTemplate < Sinatra::NotFound
|
17
|
+
def code; 500 end
|
18
|
+
end
|
19
|
+
|
20
|
+
TEXT_MIME_TYPES = [:txt, :html, :js, :json, :xml, :rss, :atom, :css, :asm, :c, :cc, :conf,
|
21
|
+
:csv, :cxx, :diff, :dtd, :f, :f77, :f90, :for, :gemspec, :h, :hh, :htm,
|
22
|
+
:log, :mathml, :mml, :p, :pas, :pl, :pm, :py, :rake, :rb, :rdf, :rtf, :ru,
|
23
|
+
:s, :sgm, :sgml, :sh, :svg, :svgz, :text, :wsdl, :xhtml, :xsl, :xslt, :yaml,
|
24
|
+
:yml, :ics, :png]
|
25
|
+
|
26
|
+
def self.registered(app)
|
27
|
+
app.helpers RespondTo::Helpers
|
28
|
+
|
29
|
+
app.set :default_charset, 'utf-8'
|
30
|
+
app.set :default_content, :html
|
31
|
+
app.set :assume_xhr_is_js, true
|
32
|
+
|
33
|
+
#deltacloud: removed the code that tried to 'guess' content based on extension
|
34
|
+
#as this broke blobstore api (stripping blob names). Use ?format if present, otherwise
|
35
|
+
#use http accept, otherwise set to default
|
36
|
+
app.before do
|
37
|
+
if (params[:format])
|
38
|
+
@mime_types = [Helpers::mime_type(params[:format])]
|
39
|
+
format params[:format]
|
40
|
+
elsif env['HTTP_ACCEPT'].nil? || env['HTTP_ACCEPT'].empty?
|
41
|
+
ext = options.default_content
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
app.configure :development do |dev|
|
46
|
+
dev.error UnhandledFormat do
|
47
|
+
content_type :html, :charset => 'utf-8'
|
48
|
+
|
49
|
+
(<<-HTML).gsub(/^ {10}/, '')
|
50
|
+
<!DOCTYPE html>
|
51
|
+
<html>
|
52
|
+
<head>
|
53
|
+
<style type="text/css">
|
54
|
+
body { text-align:center;font-family:helvetica,arial;font-size:22px;
|
55
|
+
color:#888;margin:20px}
|
56
|
+
#c {margin:0 auto;width:500px;text-align:left}
|
57
|
+
</style>
|
58
|
+
</head>
|
59
|
+
<body>
|
60
|
+
<h2>Sinatra doesn't know this ditty.</h2>
|
61
|
+
<img src='/__sinatra__/404.png'>
|
62
|
+
<div id="c">
|
63
|
+
Try this:
|
64
|
+
<pre>#{request.request_method.downcase} '#{request.path_info}' do\n respond_to do |wants|\n wants.#{format} { "Hello World" }\n end\nend</pre>
|
65
|
+
</div>
|
66
|
+
</body>
|
67
|
+
</html>
|
68
|
+
HTML
|
69
|
+
end
|
70
|
+
|
71
|
+
dev.error MissingTemplate do
|
72
|
+
content_type :html, :charset => 'utf-8'
|
73
|
+
response.status = request.env['sinatra.error'].code
|
74
|
+
|
75
|
+
engine = request.env['sinatra.error'].message.split('.').last
|
76
|
+
engine = 'haml' unless ['haml', 'builder', 'erb'].include? engine
|
77
|
+
|
78
|
+
path = File.basename(request.path_info)
|
79
|
+
path = "root" if path.nil? || path.empty?
|
80
|
+
|
81
|
+
format = engine == 'builder' ? 'xml' : 'html'
|
82
|
+
|
83
|
+
layout = case engine
|
84
|
+
when 'haml' then "!!!\n%html\n %body= yield"
|
85
|
+
when 'erb' then "<html>\n <body>\n <%= yield %>\n </body>\n</html>"
|
86
|
+
when 'builder' then ::Sinatra::VERSION =~ /^1.0/ ? "xml << yield" : "builder do |xml|\n xml << yield\nend"
|
87
|
+
end
|
88
|
+
|
89
|
+
layout = "<small>app.#{format}.#{engine}</small>\n<pre>#{escape_html(layout)}</pre>"
|
90
|
+
|
91
|
+
(<<-HTML).gsub(/^ {10}/, '')
|
92
|
+
<!DOCTYPE html>
|
93
|
+
<html>
|
94
|
+
<head>
|
95
|
+
<style type="text/css">
|
96
|
+
body { text-align:center;font-family:helvetica,arial;font-size:22px;
|
97
|
+
color:#888;margin:20px}
|
98
|
+
#c {margin:0 auto;width:500px;text-align:left;}
|
99
|
+
small {float:right;clear:both;}
|
100
|
+
pre {clear:both;}
|
101
|
+
</style>
|
102
|
+
</head>
|
103
|
+
<body>
|
104
|
+
<h2>Sinatra can't find #{request.env['sinatra.error'].message}</h2>
|
105
|
+
<img src='/__sinatra__/500.png'>
|
106
|
+
<div id="c">
|
107
|
+
Try this:<br />
|
108
|
+
#{layout}
|
109
|
+
<small>#{path}.#{format}.#{engine}</small>
|
110
|
+
<pre>Hello World!</pre>
|
111
|
+
<small>application.rb</small>
|
112
|
+
<pre>#{request.request_method.downcase} '#{request.path_info}' do\n respond_to do |wants|\n wants.#{engine == 'builder' ? 'xml' : 'html'} { #{engine} :#{path}#{",\n#{' '*32}layout => :app" if layout} }\n end\nend</pre>
|
113
|
+
</div>
|
114
|
+
</body>
|
115
|
+
</html>
|
116
|
+
HTML
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
app.class_eval do
|
122
|
+
private
|
123
|
+
def accept_list
|
124
|
+
@mime_types || Rack::AcceptMediaTypes.new(env['HTTP_ACCEPT'] || '')
|
125
|
+
end
|
126
|
+
|
127
|
+
# Changes in 1.0 Sinatra reuse render for layout so we store
|
128
|
+
# the original value to tell us if this is an automatic attempt
|
129
|
+
# to do a layout call. If it is, it might fail with Errno::ENOENT
|
130
|
+
# and we want to pass that back to sinatra since it isn't a MissingTemplate
|
131
|
+
# error
|
132
|
+
def render_with_format(*args, &block)
|
133
|
+
assumed_layout = args[1] == :layout
|
134
|
+
args[1] = "#{args[1]}.#{format}".to_sym if args[1].is_a?(::Symbol)
|
135
|
+
render_without_format *args, &block
|
136
|
+
rescue Errno::ENOENT => e
|
137
|
+
raise MissingTemplate, "#{args[1]}.#{args[0]}" unless assumed_layout
|
138
|
+
raise e
|
139
|
+
end
|
140
|
+
alias_method :render_without_format, :render
|
141
|
+
alias_method :render, :render_with_format
|
142
|
+
|
143
|
+
if ::Sinatra::VERSION =~ /^0\.9/
|
144
|
+
def lookup_layout_with_format(*args)
|
145
|
+
args[1] = "#{args[1]}.#{format}".to_sym if args[1].is_a?(::Symbol)
|
146
|
+
lookup_layout_without_format *args
|
147
|
+
end
|
148
|
+
alias_method :lookup_layout_without_format, :lookup_layout
|
149
|
+
alias_method :lookup_layout, :lookup_layout_with_format
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
module Helpers
|
155
|
+
# Patch the content_type function to remember the set type
|
156
|
+
# This helps cut down on time in the format helper so it
|
157
|
+
# doesn't have to do a reverse lookup on the header
|
158
|
+
def self.included(klass)
|
159
|
+
klass.class_eval do
|
160
|
+
def content_type_with_save(*args)
|
161
|
+
content_type_without_save *args
|
162
|
+
@_format = args.first.to_sym
|
163
|
+
response['Content-Type']
|
164
|
+
end
|
165
|
+
alias_method :content_type_without_save, :content_type
|
166
|
+
alias_method :content_type, :content_type_with_save
|
167
|
+
end if ::Sinatra::VERSION =~ /^1.0/
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.mime_type(sym)
|
171
|
+
::Sinatra::Base.respond_to?(:mime_type) && ::Sinatra::Base.mime_type(sym) || ::Sinatra::Base.media_type(sym)
|
172
|
+
end
|
173
|
+
|
174
|
+
def format(val=nil)
|
175
|
+
unless val.nil?
|
176
|
+
mime_type = ::Sinatra::RespondTo::Helpers.mime_type(val)
|
177
|
+
fail "Unknown media type #{val}\nTry registering the extension with a mime type" if mime_type.nil?
|
178
|
+
|
179
|
+
@_format = val.to_sym
|
180
|
+
response['Content-Type'].sub!(/^[^;]+/, mime_type)
|
181
|
+
charset options.default_charset if Sinatra::RespondTo::TEXT_MIME_TYPES.include?(format) and format!=:png
|
182
|
+
end
|
183
|
+
|
184
|
+
@_format
|
185
|
+
end
|
186
|
+
|
187
|
+
# This is mostly just a helper so request.path_info isn't changed when
|
188
|
+
# serving files from the public directory
|
189
|
+
def static_file?(path)
|
190
|
+
public_dir = File.expand_path(options.public)
|
191
|
+
path = File.expand_path(File.join(public_dir, unescape(path)))
|
192
|
+
|
193
|
+
path[0, public_dir.length] == public_dir && File.file?(path)
|
194
|
+
end
|
195
|
+
|
196
|
+
def charset(val=nil)
|
197
|
+
fail "Content-Type must be set in order to specify a charset" if response['Content-Type'].nil?
|
198
|
+
|
199
|
+
if response['Content-Type'] =~ /charset=[^;]+/
|
200
|
+
response['Content-Type'].sub!(/charset=[^;]+/, (val == '' && '') || "charset=#{val}")
|
201
|
+
else
|
202
|
+
response['Content-Type'] += ";charset=#{val}"
|
203
|
+
end unless val.nil?
|
204
|
+
|
205
|
+
response['Content-Type'][/charset=([^;]+)/, 1]
|
206
|
+
end
|
207
|
+
|
208
|
+
def respond_to(&block)
|
209
|
+
wants = Format.new
|
210
|
+
yield wants
|
211
|
+
fmt, type, handler = match_accept_type(accept_list, wants)
|
212
|
+
raise UnhandledFormat if fmt.nil?
|
213
|
+
format fmt
|
214
|
+
handler.nil? ? nil : handler.call
|
215
|
+
end
|
216
|
+
|
217
|
+
def match_accept_type(mime_types, format)
|
218
|
+
selected = []
|
219
|
+
|
220
|
+
accepted_types = mime_types.map {|type| Regexp.escape(type).gsub(/\\\*/,'.*') }
|
221
|
+
# Fix for Chrome based browsers which returns XML when 'xhtml' is requested.
|
222
|
+
if env['HTTP_USER_AGENT'] =~ /Chrome/ and accepted_types.size>1
|
223
|
+
accepted_types[0], accepted_types[1] = accepted_types[1], accepted_types[0]
|
224
|
+
if accepted_types[0].eql?('application/xhtml\\+xml')
|
225
|
+
accepted_types[0] = 'text/html'
|
226
|
+
end
|
227
|
+
end
|
228
|
+
accepted_types.each do |at|
|
229
|
+
format.each do |fmt, ht, handler|
|
230
|
+
(selected = [fmt, ht, handler]) and break if ht.match(at)
|
231
|
+
end
|
232
|
+
break unless selected.empty?
|
233
|
+
end
|
234
|
+
selected
|
235
|
+
end
|
236
|
+
|
237
|
+
# NOTE Array instead of hash because order matters (wildcard type
|
238
|
+
# matches first handler)
|
239
|
+
class Format < Array #:nodoc:
|
240
|
+
def method_missing(format, *args, &handler)
|
241
|
+
mt = Sinatra::RespondTo::Helpers.mime_type(format)
|
242
|
+
if mt.nil?
|
243
|
+
Sinatra::Base.send(:fail, "Unknown media type for respond_to: #{format}\nTry registering the extension with a mime type")
|
244
|
+
end
|
245
|
+
self << [format.to_s, mt, handler]
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
Rack::Mime::MIME_TYPES.merge!({ ".gv" => "text/plain" })
|
253
|
+
Sinatra::Application.register Sinatra::RespondTo
|
data/server.rb
CHANGED
@@ -9,6 +9,7 @@ require 'sinatra/lazy_auth'
|
|
9
9
|
require 'erb'
|
10
10
|
require 'haml'
|
11
11
|
require 'open3'
|
12
|
+
require 'lib/deltacloud/helpers/blob_stream'
|
12
13
|
|
13
14
|
configure do
|
14
15
|
set :raise_errors => false
|
@@ -37,6 +38,8 @@ error Deltacloud::BackendError do
|
|
37
38
|
report_error(500, "backend_error")
|
38
39
|
end
|
39
40
|
|
41
|
+
Sinatra::Application.register Sinatra::RespondTo
|
42
|
+
|
40
43
|
# Redirect to /api
|
41
44
|
get '/' do redirect url_for('/api'); end
|
42
45
|
|
@@ -74,7 +77,8 @@ END
|
|
74
77
|
|
75
78
|
operation :index do
|
76
79
|
description <<END
|
77
|
-
Operation will list all available realms.
|
80
|
+
Operation will list all available realms. Realms can be filtered using
|
81
|
+
the "architecture" parameter.
|
78
82
|
END
|
79
83
|
param :id, :string
|
80
84
|
param :architecture, :string, :optional, [ 'i386', 'x86_64' ]
|
@@ -98,9 +102,9 @@ END
|
|
98
102
|
|
99
103
|
operation :index do
|
100
104
|
description <<END
|
101
|
-
The
|
102
|
-
available to the current use.
|
103
|
-
"owner_id" and "architecture"
|
105
|
+
The images collection will return a set of all images
|
106
|
+
available to the current use. Images can be filtered using the
|
107
|
+
"owner_id" and "architecture" parameters.
|
104
108
|
END
|
105
109
|
param :id, :string
|
106
110
|
param :architecture, :string, :optional
|
@@ -138,7 +142,10 @@ collection :instance_states do
|
|
138
142
|
format.png do
|
139
143
|
# Trick respond_to into looking up the right template for the
|
140
144
|
# graphviz file
|
141
|
-
|
145
|
+
format_backup = format
|
146
|
+
format(:gv)
|
147
|
+
gv = erb(:"instance_states/show")
|
148
|
+
format(format_backup)
|
142
149
|
png = ''
|
143
150
|
cmd = 'dot -Kdot -Gpad="0.2,0.2" -Gsize="5.0,8.0" -Gdpi="180" -Tpng'
|
144
151
|
Open3.popen3( cmd ) do |stdin, stdout, stderr|
|
@@ -146,6 +153,7 @@ collection :instance_states do
|
|
146
153
|
stdin.close()
|
147
154
|
png = stdout.read
|
148
155
|
end
|
156
|
+
content_type 'image/png'
|
149
157
|
png
|
150
158
|
end
|
151
159
|
end
|
@@ -170,7 +178,7 @@ collection :instances do
|
|
170
178
|
END
|
171
179
|
|
172
180
|
operation :index do
|
173
|
-
description "List all instances"
|
181
|
+
description "List all instances."
|
174
182
|
param :id, :string, :optional
|
175
183
|
param :state, :string, :optional
|
176
184
|
control { filter_all(:instances) }
|
@@ -183,7 +191,7 @@ END
|
|
183
191
|
end
|
184
192
|
|
185
193
|
operation :create do
|
186
|
-
description "Create a new instance"
|
194
|
+
description "Create a new instance."
|
187
195
|
param :image_id, :string, :required
|
188
196
|
param :realm_id, :string, :optional
|
189
197
|
param :hwp_id, :string, :optional
|
@@ -206,25 +214,25 @@ END
|
|
206
214
|
end
|
207
215
|
|
208
216
|
operation :reboot, :method => :post, :member => true do
|
209
|
-
description "Reboot running instance"
|
217
|
+
description "Reboot a running instance."
|
210
218
|
param :id, :string, :required
|
211
219
|
control { instance_action(:reboot) }
|
212
220
|
end
|
213
221
|
|
214
222
|
operation :start, :method => :post, :member => true do
|
215
|
-
description "Start an instance"
|
223
|
+
description "Start an instance."
|
216
224
|
param :id, :string, :required
|
217
225
|
control { instance_action(:start) }
|
218
226
|
end
|
219
227
|
|
220
228
|
operation :stop, :method => :post, :member => true do
|
221
|
-
description "Stop running instance"
|
229
|
+
description "Stop a running instance."
|
222
230
|
param :id, :string, :required
|
223
231
|
control { instance_action(:stop) }
|
224
232
|
end
|
225
233
|
|
226
234
|
operation :destroy do
|
227
|
-
description "Destroy instance"
|
235
|
+
description "Destroy an instance."
|
228
236
|
param :id, :string, :required
|
229
237
|
control { instance_action(:destroy) }
|
230
238
|
end
|
@@ -239,7 +247,7 @@ collection :hardware_profiles do
|
|
239
247
|
END
|
240
248
|
|
241
249
|
operation :index do
|
242
|
-
description "List of available hardware profiles"
|
250
|
+
description "List of available hardware profiles."
|
243
251
|
param :id, :string
|
244
252
|
param :architecture, :string, :optional, [ 'i386', 'x86_64' ]
|
245
253
|
control do
|
@@ -253,7 +261,7 @@ END
|
|
253
261
|
end
|
254
262
|
|
255
263
|
operation :show do
|
256
|
-
description "Show specific hardware profile"
|
264
|
+
description "Show specific hardware profile."
|
257
265
|
param :id, :string, :required
|
258
266
|
control do
|
259
267
|
@profile = driver.hardware_profile(credentials, params[:id])
|
@@ -275,13 +283,13 @@ collection :storage_snapshots do
|
|
275
283
|
description "Storage snapshots description here"
|
276
284
|
|
277
285
|
operation :index do
|
278
|
-
description "
|
286
|
+
description "List of storage snapshots."
|
279
287
|
param :id, :string
|
280
288
|
control { filter_all(:storage_snapshots) }
|
281
289
|
end
|
282
290
|
|
283
291
|
operation :show do
|
284
|
-
description "Show storage snapshot"
|
292
|
+
description "Show storage snapshot."
|
285
293
|
param :id, :string, :required
|
286
294
|
control { show(:storage_snapshot) }
|
287
295
|
end
|
@@ -291,13 +299,13 @@ collection :storage_volumes do
|
|
291
299
|
description "Storage volumes description here"
|
292
300
|
|
293
301
|
operation :index do
|
294
|
-
description "
|
302
|
+
description "List of storage volumes."
|
295
303
|
param :id, :string
|
296
304
|
control { filter_all(:storage_volumes) }
|
297
305
|
end
|
298
306
|
|
299
307
|
operation :show do
|
300
|
-
description "Show storage volume"
|
308
|
+
description "Show storage volume."
|
301
309
|
param :id, :string, :required
|
302
310
|
control { show(:storage_volume) }
|
303
311
|
end
|
@@ -310,23 +318,23 @@ get '/api/keys/new' do
|
|
310
318
|
end
|
311
319
|
|
312
320
|
collection :keys do
|
313
|
-
description "Instance authentication credentials"
|
321
|
+
description "Instance authentication credentials."
|
314
322
|
|
315
323
|
operation :index do
|
316
|
-
description "List all available credentials which could be used for instance authentication"
|
324
|
+
description "List all available credentials which could be used for instance authentication."
|
317
325
|
control do
|
318
326
|
filter_all :keys
|
319
327
|
end
|
320
328
|
end
|
321
329
|
|
322
330
|
operation :show do
|
323
|
-
description "Show details about given instance credential"
|
331
|
+
description "Show details about given instance credential."
|
324
332
|
param :id, :string, :required
|
325
333
|
control { show :key }
|
326
334
|
end
|
327
335
|
|
328
336
|
operation :create do
|
329
|
-
description "Create a new instance credential if backend supports this"
|
337
|
+
description "Create a new instance credential if backend supports this."
|
330
338
|
param :name, :string, :required
|
331
339
|
control do
|
332
340
|
unless driver.respond_to?(:create_key)
|
@@ -342,7 +350,7 @@ collection :keys do
|
|
342
350
|
end
|
343
351
|
|
344
352
|
operation :destroy do
|
345
|
-
description "Destroy given instance credential if backend supports this"
|
353
|
+
description "Destroy given instance credential if backend supports this."
|
346
354
|
param :id, :string, :required
|
347
355
|
control do
|
348
356
|
unless driver.respond_to?(:destroy_key)
|
@@ -355,3 +363,77 @@ collection :keys do
|
|
355
363
|
end
|
356
364
|
|
357
365
|
end
|
366
|
+
|
367
|
+
get '/api/buckets/:bucket/:blob' do
|
368
|
+
@blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
|
369
|
+
if @blob
|
370
|
+
respond_to do |format|
|
371
|
+
format.html { haml :"blobs/show" }
|
372
|
+
format.xml { haml :"blobs/show" }
|
373
|
+
format.json { convert_to_json(blobs, @blob) }
|
374
|
+
end
|
375
|
+
else
|
376
|
+
report_error(404, 'not_found')
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
get '/api/buckets/new' do
|
381
|
+
respond_to do |format|
|
382
|
+
format.html { haml :"buckets/new" }
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
|
387
|
+
get '/api/buckets/:bucket/:blob/content' do
|
388
|
+
@blob = driver.blob(credentials, { :id => params[:blob], 'bucket' => params[:bucket]})
|
389
|
+
params['content_length'] = @blob.content_length
|
390
|
+
params['content_type'] = @blob.content_type
|
391
|
+
BlobStream.call(env, credentials, params)
|
392
|
+
end
|
393
|
+
|
394
|
+
collection :buckets do
|
395
|
+
description "Cloud Storage buckets - aka buckets|directories|folders"
|
396
|
+
|
397
|
+
operation :index do
|
398
|
+
description "List buckets associated with this account"
|
399
|
+
param :id, :string
|
400
|
+
param :name, :string
|
401
|
+
param :size, :string
|
402
|
+
control { filter_all(:buckets) }
|
403
|
+
end
|
404
|
+
|
405
|
+
operation :show do
|
406
|
+
description "Show bucket"
|
407
|
+
param :id, :string
|
408
|
+
control { show(:bucket) }
|
409
|
+
end
|
410
|
+
|
411
|
+
operation :create do
|
412
|
+
description "Create a new bucket (POST /api/buckets)"
|
413
|
+
param :name, :string, :required
|
414
|
+
control do
|
415
|
+
@bucket = driver.create_bucket(credentials, params[:name], params)
|
416
|
+
respond_to do |format|
|
417
|
+
format.xml do
|
418
|
+
response.status = 201 # Created
|
419
|
+
response['Location'] = bucket_url(@bucket.id)
|
420
|
+
haml :"buckets/show"
|
421
|
+
end
|
422
|
+
format.html do
|
423
|
+
redirect bucket_url(@bucket.id) if @bucket and @bucket.id
|
424
|
+
redirect buckets_url
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
operation :destroy do
|
431
|
+
description "Delete a bucket by name - bucket must be empty"
|
432
|
+
param :id, :string, :required
|
433
|
+
control do
|
434
|
+
driver.delete_bucket(credentials, params[:id], params)
|
435
|
+
redirect(buckets_url)
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
end
|