visor-image 0.0.1
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/bin/visor +423 -0
- data/bin/visor-image +10 -0
- data/config/server.rb +14 -0
- data/lib/image/auth.rb +147 -0
- data/lib/image/cli.rb +397 -0
- data/lib/image/client.rb +490 -0
- data/lib/image/meta.rb +219 -0
- data/lib/image/routes/delete_all_images.rb +40 -0
- data/lib/image/routes/delete_image.rb +62 -0
- data/lib/image/routes/get_image.rb +78 -0
- data/lib/image/routes/get_images.rb +54 -0
- data/lib/image/routes/get_images_detail.rb +54 -0
- data/lib/image/routes/head_image.rb +51 -0
- data/lib/image/routes/post_image.rb +189 -0
- data/lib/image/routes/put_image.rb +205 -0
- data/lib/image/server.rb +307 -0
- data/lib/image/store/cumulus.rb +126 -0
- data/lib/image/store/file_system.rb +119 -0
- data/lib/image/store/hdfs.rb +149 -0
- data/lib/image/store/http.rb +78 -0
- data/lib/image/store/lunacloud.rb +126 -0
- data/lib/image/store/s3.rb +121 -0
- data/lib/image/store/store.rb +39 -0
- data/lib/image/store/walrus.rb +130 -0
- data/lib/image/version.rb +5 -0
- data/lib/visor-image.rb +30 -0
- data/spec/lib/client_spec.rb +0 -0
- data/spec/lib/meta_spec.rb +230 -0
- data/spec/lib/routes/delete_image_spec.rb +98 -0
- data/spec/lib/routes/get_image_spec.rb +78 -0
- data/spec/lib/routes/get_images_detail_spec.rb +104 -0
- data/spec/lib/routes/get_images_spec.rb +104 -0
- data/spec/lib/routes/head_image_spec.rb +51 -0
- data/spec/lib/routes/post_image_spec.rb +112 -0
- data/spec/lib/routes/put_image_spec.rb +109 -0
- data/spec/lib/server_spec.rb +62 -0
- data/spec/lib/store/cumulus_spec.rb +0 -0
- data/spec/lib/store/file_system_spec.rb +32 -0
- data/spec/lib/store/http_spec.rb +56 -0
- data/spec/lib/store/s3_spec.rb +37 -0
- data/spec/lib/store/store_spec.rb +36 -0
- data/spec/lib/store/walrus_spec.rb +0 -0
- metadata +217 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Visor
|
4
|
+
module Image
|
5
|
+
|
6
|
+
# Visor Image Store module encapsulates all store backend classes, plus a set of
|
7
|
+
# utility methods common to all stores.
|
8
|
+
#
|
9
|
+
module Store
|
10
|
+
extend self
|
11
|
+
include Visor::Common::Exception
|
12
|
+
|
13
|
+
# Base API mapping for the multiple storage backend classes
|
14
|
+
BACKENDS = {:s3 => Visor::Image::Store::S3,
|
15
|
+
:cumulus => Visor::Image::Store::Cumulus,
|
16
|
+
:walrus => Visor::Image::Store::Walrus,
|
17
|
+
:lunacloud => Visor::Image::Store::Lunacloud,
|
18
|
+
:hdfs => Visor::Image::Store::HDFS,
|
19
|
+
:file => Visor::Image::Store::FileSystem,
|
20
|
+
:http => Visor::Image::Store::HTTP}
|
21
|
+
|
22
|
+
# Get a store backend class object ready to use, based on a file URI or store name.
|
23
|
+
#
|
24
|
+
# @param string [String] The file location URI or backend name.
|
25
|
+
# @param config [Hash] A set of configurations for the wanted store, loaded from
|
26
|
+
# VISoR configuration file.
|
27
|
+
#
|
28
|
+
# @return [Object] An instantiated store object ready to use.
|
29
|
+
#
|
30
|
+
def get_backend(string, config)
|
31
|
+
name = URI(string).scheme || string
|
32
|
+
store = BACKENDS[name.to_sym]
|
33
|
+
raise UnsupportedStore, "The store '#{name}' is not supported" unless store
|
34
|
+
store.new(string, config)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 's3restful'
|
3
|
+
require 'em-synchrony'
|
4
|
+
require 'em-synchrony/em-http'
|
5
|
+
|
6
|
+
module Visor
|
7
|
+
module Image
|
8
|
+
module Store
|
9
|
+
|
10
|
+
# The Eucalyptus Walrus (Walrus) backend store.
|
11
|
+
#
|
12
|
+
# This class handles the management of image files located in the Walrus storage system,
|
13
|
+
# based on a URI like *walrus://<access_key>:<secret_key>@<host>:<port>/<bucket>/<image>*.
|
14
|
+
#
|
15
|
+
class Walrus
|
16
|
+
include Visor::Common::Exception
|
17
|
+
|
18
|
+
attr_accessor :uri, :config, :access_key, :secret_key, :bucket, :file, :host, :port
|
19
|
+
|
20
|
+
# Initializes a new Walrus store client object. Walrus credentials are loaded from the URI,
|
21
|
+
# on GET and DELETE operations, or from the configuration file for POST and PUT operation.
|
22
|
+
#
|
23
|
+
# @param [String] uri The URI of the file location.
|
24
|
+
# @param config [Hash] A set of configurations for the wanted store, loaded from
|
25
|
+
# VISoR configuration file.
|
26
|
+
#
|
27
|
+
# @return [Object] An instantiated Walrus store object ready to use.
|
28
|
+
#
|
29
|
+
def initialize(uri, config)
|
30
|
+
@uri = URI(uri)
|
31
|
+
@config = config[:walrus]
|
32
|
+
|
33
|
+
if @uri.scheme
|
34
|
+
@access_key = @uri.user
|
35
|
+
@secret_key = @uri.password
|
36
|
+
# Trick:
|
37
|
+
# To build a successful Walrus connection we need happening to sign the
|
38
|
+
# '/services/Walrus' in path too. If we append it to server's address it wont get
|
39
|
+
# signed in the authorization string, we need to prepend it to the bucket name
|
40
|
+
@bucket = File.join('services/Walrus', @uri.path.split('/')[3])
|
41
|
+
@file = @uri.path.split('/').last
|
42
|
+
@host = @uri.host
|
43
|
+
@port = @uri.port
|
44
|
+
else
|
45
|
+
@access_key = @config[:access_key]
|
46
|
+
@secret_key = @config[:secret_key]
|
47
|
+
@bucket = File.join('services/Walrus', @config[:bucket])
|
48
|
+
@host = @config[:host]
|
49
|
+
@port = @config[:port]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns a Happening library S3 Walrus compatible connection object.
|
54
|
+
#
|
55
|
+
# @return [Happening::S3::Item] A new Walrus connection object.
|
56
|
+
#
|
57
|
+
def connection
|
58
|
+
S3restful::S3::Item.new(bucket, file, server: host, port: port, protocol: 'http',
|
59
|
+
aws_access_key_id: access_key, aws_secret_access_key: secret_key)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns the image file to clients, streamed in chunks.
|
63
|
+
#
|
64
|
+
# @return [Object] Yields the file, a chunk at time.
|
65
|
+
#
|
66
|
+
def get
|
67
|
+
s3 = connection.aget
|
68
|
+
finish = proc { yield nil }
|
69
|
+
|
70
|
+
s3.stream { |chunk| yield chunk }
|
71
|
+
s3.callback &finish
|
72
|
+
s3.errback &finish
|
73
|
+
end
|
74
|
+
|
75
|
+
# Saves the image file to the its final destination, based on the temporary file
|
76
|
+
# created by the server at data reception time.
|
77
|
+
#
|
78
|
+
# @param [String] id The image id.
|
79
|
+
# @param [File] tmp_file The temporary file descriptor.
|
80
|
+
# @param [String] format The image file format.
|
81
|
+
#
|
82
|
+
# @return [String, Integer] The generated file location URI and image file size.
|
83
|
+
#
|
84
|
+
# @raise [Duplicated] If the image file already exists.
|
85
|
+
#
|
86
|
+
def save(id, tmp_file, format)
|
87
|
+
@file = "#{id}.#{format}"
|
88
|
+
uri = "walrus://#{access_key}:#{secret_key}@#{host}:#{port}/#{bucket}/#{file}"
|
89
|
+
size = tmp_file.size
|
90
|
+
|
91
|
+
raise Duplicated, "The image file #{fp} already exists" if file_exists?(false)
|
92
|
+
STDERR.puts "COPYING!!"
|
93
|
+
|
94
|
+
connection.store tmp_file.path
|
95
|
+
|
96
|
+
[uri, size]
|
97
|
+
end
|
98
|
+
|
99
|
+
# Deletes the image file from its location.
|
100
|
+
#
|
101
|
+
# @raise [NotFound] If the image file was not found.
|
102
|
+
#
|
103
|
+
def delete
|
104
|
+
connection.delete
|
105
|
+
end
|
106
|
+
|
107
|
+
# Check if the image file exists.
|
108
|
+
#
|
109
|
+
# @param [True, False] raise_exc (true) If it should raise exception or return
|
110
|
+
# true/false whether the file exists or not.
|
111
|
+
#
|
112
|
+
# @return [True, False] If raise_exc is false, return true/false whether the
|
113
|
+
# file exists or not.
|
114
|
+
#
|
115
|
+
# @raise [NotFound] If the image file was not found.
|
116
|
+
#
|
117
|
+
def file_exists?(raise_exc=true)
|
118
|
+
exist = nil
|
119
|
+
error = proc { exist = false }
|
120
|
+
success = proc { |res| exist = true if res.response_header.status == 200 }
|
121
|
+
|
122
|
+
connection.head(on_error: error, on_success: success)
|
123
|
+
raise NotFound, "No image file found at #{uri}" if raise_exc && !exist
|
124
|
+
exist
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/lib/visor-image.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'goliath'
|
2
|
+
require 'visor-common'
|
3
|
+
|
4
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
5
|
+
|
6
|
+
require 'image/version'
|
7
|
+
require 'image/meta'
|
8
|
+
require 'image/auth'
|
9
|
+
require 'image/cli'
|
10
|
+
require 'image/store/s3'
|
11
|
+
require 'image/store/cumulus'
|
12
|
+
require 'image/store/walrus'
|
13
|
+
require 'image/store/lunacloud'
|
14
|
+
require 'image/store/hdfs'
|
15
|
+
require 'image/store/http'
|
16
|
+
require 'image/store/file_system'
|
17
|
+
require 'image/store/store'
|
18
|
+
require 'image/routes/head_image'
|
19
|
+
require 'image/routes/get_images'
|
20
|
+
require 'image/routes/get_images_detail'
|
21
|
+
require 'image/routes/get_image'
|
22
|
+
require 'image/routes/post_image'
|
23
|
+
require 'image/routes/put_image'
|
24
|
+
require 'image/routes/delete_image'
|
25
|
+
require 'image/routes/delete_all_images'
|
26
|
+
require 'image/server'
|
27
|
+
require 'image/client'
|
28
|
+
|
29
|
+
|
30
|
+
|
File without changes
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Visor::Image::Meta do
|
4
|
+
|
5
|
+
let(:meta) { Visor::Image::Meta.new }
|
6
|
+
let(:not_found) { Visor::Common::Exception::NotFound }
|
7
|
+
let(:invalid) { Visor::Common::Exception::Invalid }
|
8
|
+
|
9
|
+
let(:valid_post) { {name: 'client_spec', architecture: 'i386', access: 'public'} }
|
10
|
+
let(:invalid_post) { {name: 'client_spec', architecture: 'i386', access: 'invalid'} }
|
11
|
+
|
12
|
+
let(:valid_update) { {architecture: 'x86_64'} }
|
13
|
+
let(:invalid_update) { {architecture: 'invalid'} }
|
14
|
+
|
15
|
+
inserted = []
|
16
|
+
|
17
|
+
before(:all) do
|
18
|
+
EM.synchrony do
|
19
|
+
inserted << meta.post_image(valid_post)[:_id]
|
20
|
+
inserted << meta.post_image(valid_post.merge(architecture: 'x86_64'))[:_id]
|
21
|
+
EM.stop
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:all) do
|
26
|
+
EM.synchrony do
|
27
|
+
inserted.each { |id| meta.delete_image(id) }
|
28
|
+
EM.stop
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#initialize" do
|
33
|
+
it "should create a new meta api object with default parameters" do
|
34
|
+
meta.host.should == Visor::Image::Meta::DEFAULT_HOST
|
35
|
+
meta.port.should == Visor::Image::Meta::DEFAULT_PORT
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should create a new meta api object with custom parameters" do
|
39
|
+
custom = Visor::Image::Meta.new(host: '0.0.0.0', port: 1111)
|
40
|
+
custom.host.should == '0.0.0.0'
|
41
|
+
custom.port.should == 1111
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#get_images" do
|
46
|
+
it "should return an array of hashes" do
|
47
|
+
EM.synchrony do
|
48
|
+
res = meta.get_images
|
49
|
+
res.should be_a Array
|
50
|
+
res.each { |h| h.should be_a Hash }
|
51
|
+
EM.stop
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should filter results by parameter" do
|
56
|
+
EM.synchrony do
|
57
|
+
res = meta.get_images(architecture: 'i386')
|
58
|
+
res.each { |h| h[:architecture].should == 'i386' }
|
59
|
+
EM.stop
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should sort results by parameter and direction" do
|
64
|
+
EM.synchrony do
|
65
|
+
pub = meta.get_images(sort: 'architecture', dir: 'desc')
|
66
|
+
pub.first[:architecture].should == 'x86_64'
|
67
|
+
pub = meta.get_images(sort: 'architecture', dir: 'asc')
|
68
|
+
pub.first[:architecture].should == 'i386'
|
69
|
+
EM.stop
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should raise if no matches found" do
|
74
|
+
EM.synchrony do
|
75
|
+
lambda { meta.get_images(:name => 'fake') }.should raise_error not_found
|
76
|
+
EM.stop
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#get_images_detail" do
|
82
|
+
it "should return an array of hashes" do
|
83
|
+
EM.synchrony do
|
84
|
+
res = meta.get_images
|
85
|
+
res.should be_a Array
|
86
|
+
res.each { |h| h.should be_a Hash }
|
87
|
+
EM.stop
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should filter results by parameter" do
|
92
|
+
EM.synchrony do
|
93
|
+
res = meta.get_images(architecture: 'x86_64')
|
94
|
+
res.each { |h| h[:architecture].should == 'x86_64' }
|
95
|
+
EM.stop
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should sort results by parameter and direction" do
|
100
|
+
EM.synchrony do
|
101
|
+
pub = meta.get_images(sort: 'architecture', dir: 'desc')
|
102
|
+
pub.first[:architecture].should == 'x86_64'
|
103
|
+
pub = meta.get_images(sort: 'architecture', dir: 'asc')
|
104
|
+
pub.first[:architecture].should == 'i386'
|
105
|
+
EM.stop
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should raise if no matches found" do
|
110
|
+
EM.synchrony do
|
111
|
+
lambda { meta.get_images(:name => 'fake') }.should raise_error not_found
|
112
|
+
EM.stop
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "#get_image" do
|
118
|
+
before(:each) do
|
119
|
+
EM.synchrony do
|
120
|
+
@id = meta.post_image(valid_post)[:_id]
|
121
|
+
inserted << @id
|
122
|
+
@image = meta.get_image(@id)
|
123
|
+
EM.stop
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should return a hash" do
|
128
|
+
@image.should be_a Hash
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should return the asked image metadata" do
|
132
|
+
@image[:_id].should == @id
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should raise an exception if image not found" do
|
136
|
+
fake_id = 0
|
137
|
+
EM.synchrony do
|
138
|
+
lambda { meta.get_image(fake_id) }.should raise_error not_found
|
139
|
+
EM.stop
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "#delete_image" do
|
145
|
+
before(:each) do
|
146
|
+
EM.synchrony do
|
147
|
+
@id = meta.post_image(valid_post)[:_id]
|
148
|
+
@image = meta.delete_image(@id)
|
149
|
+
EM.stop
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should return a hash" do
|
154
|
+
@image.should be_a Hash
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should return the deleted image metadata" do
|
158
|
+
@image[:_id].should == @id
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should trully delete that image from database" do
|
162
|
+
EM.synchrony do
|
163
|
+
lambda { meta.get_image(@id) }.should raise_error not_found
|
164
|
+
EM.stop
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should raise an exception if image not found" do
|
169
|
+
fake_id = 0
|
170
|
+
EM.synchrony do
|
171
|
+
lambda { meta.delete_image(fake_id) }.should raise_error not_found
|
172
|
+
EM.stop
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "#post_image" do
|
178
|
+
before(:each) do
|
179
|
+
EM.synchrony do
|
180
|
+
@image = meta.post_image(valid_post)
|
181
|
+
inserted << @image[:_id]
|
182
|
+
EM.stop
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should return a hash" do
|
187
|
+
@image.should be_a Hash
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should return posted image metadata" do
|
191
|
+
@image[:_id].should be_a(String)
|
192
|
+
@image[:access].should == valid_post[:access]
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should raise an exception if meta validation fails" do
|
196
|
+
EM.synchrony do
|
197
|
+
lambda { meta.post_image(invalid_post) }.should raise_error invalid
|
198
|
+
EM.stop
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "#put_image" do
|
204
|
+
before :each do
|
205
|
+
EM.synchrony do
|
206
|
+
@id = meta.post_image(valid_post)[:_id]
|
207
|
+
inserted << @id
|
208
|
+
@image = meta.put_image(@id, valid_update)
|
209
|
+
EM.stop
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should return a hash" do
|
214
|
+
@image.should be_a Hash
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should return update image metadata" do
|
218
|
+
@image[:_id].should == @id
|
219
|
+
@image[:architecture].should == valid_update[:architecture]
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should raise an exception if meta validation fails" do
|
223
|
+
EM.synchrony do
|
224
|
+
lambda { meta.put_image(@id, invalid_update) }.should raise_error invalid
|
225
|
+
EM.stop
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Visor::Image::Server do
|
4
|
+
|
5
|
+
let(:test_api) { Visor::Image::Server }
|
6
|
+
let(:err) { Proc.new { fail "API request failed" } }
|
7
|
+
|
8
|
+
let(:accept_json) { {'Accept' => 'application/json'} }
|
9
|
+
let(:accept_xml) { {'Accept' => 'application/xml'} }
|
10
|
+
|
11
|
+
let(:parse_opts) { {symbolize_names: true} }
|
12
|
+
let(:valid_post) { {name: 'server_spec', architecture: 'i386', access: 'public'} }
|
13
|
+
let(:api_options) { {config: File.expand_path(File.join(File.dirname(__FILE__), '../../../', 'config/server.rb'))} }
|
14
|
+
|
15
|
+
inserted = []
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
EM.synchrony do
|
19
|
+
inserted << DB.post_image(valid_post)[:_id]
|
20
|
+
EM.stop
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
after(:all) do
|
25
|
+
EM.synchrony do
|
26
|
+
inserted.each { |id| DB.delete_image(id) }
|
27
|
+
EM.stop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# DELETE /images/<id>
|
33
|
+
#
|
34
|
+
describe "DELETE /images/:id" do
|
35
|
+
|
36
|
+
it "should delete an image metadata" do
|
37
|
+
id = inserted.delete_at(0)
|
38
|
+
|
39
|
+
with_api(test_api, api_options) do
|
40
|
+
delete_request({:path => "/images/#{id}"}, err) do |c|
|
41
|
+
assert_200 c
|
42
|
+
c.response.should be_a String
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
with_api(test_api, api_options) do
|
47
|
+
delete_request({:path => "/images/#{id}"}, err) do |c|
|
48
|
+
assert_404 c
|
49
|
+
c.response.should match /id/
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should return a JSON string if no accept header provided" do
|
55
|
+
with_api(test_api, api_options) do
|
56
|
+
id = inserted.delete_at(0)
|
57
|
+
delete_request({:path => "/images/#{id}"}, err) do |c|
|
58
|
+
assert_200 c
|
59
|
+
c.response.should match /\{/
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should return a JSON string if accepted" do
|
65
|
+
with_api(test_api, api_options) do
|
66
|
+
id = inserted.delete_at(0)
|
67
|
+
delete_request({:path => "/images/#{id}", head: accept_json}, err) do |c|
|
68
|
+
assert_200 c
|
69
|
+
c.response.should match /\{/
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should return a XML document if accepted" do
|
75
|
+
with_api(test_api, api_options) do
|
76
|
+
id = inserted.delete_at(0)
|
77
|
+
delete_request({:path => "/images/#{id}", head: accept_xml}, err) do |c|
|
78
|
+
assert_200 c
|
79
|
+
c.response.should match /\<?xml/
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should raise a HTTPNotFound 404 error if no images found" do
|
85
|
+
with_api(test_api, api_options) do
|
86
|
+
delete_request({:path => "/images/fakeid"}, err) do |c|
|
87
|
+
assert_404 c
|
88
|
+
c.response.should match /fakeid/
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should raise a HTTPInternalServer 500 error if no server error" do
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|