File Coverage

blib/lib/Net/OpenStack/Swift.pm
Criterion Covered Total %
statement 65 305 21.3
branch 6 66 9.0
condition 0 5 0.0
subroutine 13 28 46.4
pod 14 15 93.3
total 98 419 23.3


line stmt bran cond sub pod time code
1             package Net::OpenStack::Swift;
2 5     5   232928 use Carp;
  5         18  
  5         224  
3 5     5   1816 use Mouse;
  5         110267  
  5         18  
4 5     5   1713 use Mouse::Util::TypeConstraints;
  5         10  
  5         18  
5 5     5   2990 use JSON;
  5         43823  
  5         20  
6 5     5   3659 use Path::Tiny;
  5         45551  
  5         262  
7 5     5   2120 use Data::Validator;
  5         10873  
  5         138  
8 5     5   1908 use Net::OpenStack::Swift::Util qw/uri_escape uri_unescape debugf/;
  5         16  
  5         315  
9 5     5   1998 use Net::OpenStack::Swift::InnerKeystone;
  5         19  
  5         203  
10 5     5   28 use namespace::clean -except => 'meta';
  5         8  
  5         18  
11             our $VERSION = "0.15";
12              
13              
14             subtype 'Path' => as 'Path::Tiny';
15             coerce 'Path' => from 'Str' => via { Path::Tiny->new($_) };
16              
17              
18             has auth_version => (is => 'rw', required => 1, default => sub { $ENV{OS_AUTH_VERSION} || "2.0"});
19             has auth_url => (is => 'rw', required => 1, default => sub { $ENV{OS_AUTH_URL} || '' });
20             has user => (is => 'rw', required => 1, default => sub { $ENV{OS_USERNAME} || '' });
21             has password => (is => 'rw', required => 1, default => sub { $ENV{OS_PASSWORD} || '' });
22             has tenant_name => (is => 'rw', required => 1, default => sub { $ENV{OS_TENANT_NAME} || '' });
23             has region => (is => 'rw', required => 1, default => sub { $ENV{OS_REGION_NAME} || '' });
24             has storage_url => (is => 'rw');
25             has token => (is => 'rw');
26             has agent_options => (is => 'rw', isa => 'HashRef', default => sub{+{ timeout => 10 }});
27             has agent => (
28             is => 'rw',
29             lazy => 1,
30             default => sub {
31             my $self = shift;
32             my $furl_options = +{
33             timeout => $self->agent_options->{timeout} || 10
34             };
35             $furl_options->{agent} ||= $self->agent_options->{user_agent};
36             return Furl->new(%{$furl_options});
37             },
38             );
39              
40             sub _request {
41 0     0   0 my $self = shift;
42 0         0 my $args = shift;
43            
44             my $res = $self->agent->request(
45             method => $args->{method},
46             url => $args->{url},
47             special_headers => $args->{special_headers},
48             headers => $args->{header},
49             write_code => $args->{write_code},
50             write_file => $args->{write_file},
51             content => $args->{content},
52 0         0 );
53 0         0 return $res;
54             }
55              
56             sub auth_keystone {
57 0     0 0 0 my $self = shift;
58 0         0 (my $load_version = $self->auth_version) =~ s/\./_/;
59 0         0 my $ksclient = "Net::OpenStack::Swift::InnerKeystone::V${load_version}"->new(
60             auth_url => $self->auth_url,
61             user => $self->user,
62             password => $self->password,
63             tenant_name => $self->tenant_name,
64             );
65 0         0 $ksclient->agent($self->agent);
66 0         0 my $auth_token = $ksclient->auth();
67 0         0 my $endpoint = $ksclient->service_catalog_url_for(service_type=>'object-store', endpoint_type=>'publicURL', region=>$self->region);
68 0 0       0 croak "Not found endpoint type 'object-store'" unless $endpoint;
69 0         0 $self->token($auth_token);
70 0         0 $self->storage_url($endpoint);
71 0         0 return 1;
72             }
73              
74             sub get_auth {
75 1     1 1 2863 my $self = shift;
76 1         4 $self->auth_keystone();
77 1         89 return ($self->storage_url, $self->token);
78             }
79              
80             sub get_account {
81 1     1 1 499 my $self = shift;
82 1         18 my $rule = Data::Validator->new(
83             url => { isa => 'Str', default => $self->storage_url},
84             token => { isa => 'Str', default => $self->token },
85             marker => { isa => 'Str', default => undef },
86             limit => { isa => 'Int', default => undef },
87             prefix => { isa => 'Str', default => undef },
88             end_marker => { isa => 'Str', default => undef },
89             );
90 1         277 my $args = $rule->validate(@_);
91              
92             # make query strings
93 1         88 my @qs = ('format=json');
94 1 50       3 if ($args->{marker}) {
95 1         5 push @qs, sprintf "marker=%s", uri_escape($args->{marker});
96             }
97 1 50       61 if ($args->{limit}) {
98 0         0 push @qs, sprintf("limit=%d", $args->{limit});
99             }
100 1 50       3 if ($args->{prefix}) {
101 0         0 push @qs, sprintf("prefix=%s", uri_escape($args->{prefix}));
102             }
103 1 50       3 if ($args->{end_marker}) {
104 0         0 push @qs, sprintf("end_marker=%s", uri_escape($args->{end_marker}));
105             }
106              
107 1         3 my $request_header = ['X-Auth-Token' => $args->{token}];
108 1         5 my $request_url = sprintf "%s?%s", $args->{url}, join('&', @qs);
109 1         4 debugf("get_account() request header %s", $request_header);
110 1         6 debugf("get_account() request url: %s", $request_url);
111              
112 1         7 my $res = $self->_request({
113             method => 'GET', url => $request_url, header => $request_header
114             });
115              
116 1 50       205 croak "Account GET failed: ".$res->status_line unless $res->is_success;
117 1         61 my @headers = $res->headers->flatten();
118 1         93 debugf("get_account() response headers %s", \@headers);
119 1         8 debugf("get_account() response body %s", $res->content);
120 1         11 my %headers = @headers;
121 1         4 return (\%headers, from_json($res->content));
122             }
123              
124             sub head_account {
125 0     0 1 0 my $self = shift;
126 0         0 my $rule = Data::Validator->new(
127             url => { isa => 'Str', default => $self->storage_url},
128             token => { isa => 'Str', default => $self->token },
129             );
130 0         0 my $args = $rule->validate(@_);
131              
132 0         0 my $request_header = ['X-Auth-Token' => $args->{token}];
133 0         0 debugf("head_account() request header %s", $request_header);
134 0         0 debugf("head_account() request url: %s", $args->{url});
135              
136             my $res = $self->_request({
137 0         0 method => 'HEAD', url => $args->{url}, header => $request_header
138             });
139              
140 0 0       0 croak "Account HEAD failed: ".$res->status_line unless $res->is_success;
141 0         0 my @headers = $res->headers->flatten();
142 0         0 debugf("head_account() response headers %s", \@headers);
143 0         0 debugf("head_account() response body %s", $res->content);
144 0         0 my %headers = @headers;
145 0         0 return \%headers;
146             }
147              
148             sub post_account {
149 0     0 1 0 my $self = shift;
150 0         0 my $rule = Data::Validator->new(
151             url => { isa => 'Str', default => $self->storage_url},
152             token => { isa => 'Str', default => $self->token },
153             headers => { isa => 'HashRef'},
154             );
155 0         0 my $args = $rule->validate(@_);
156 0         0 my $request_header = ['X-Auth-Token' => $args->{token}];
157 0         0 my @additional_headers = %{ $args->{headers} };
  0         0  
158 0         0 push @{$request_header}, @additional_headers;
  0         0  
159 0         0 my $request_url = $args->{url};
160 0         0 debugf("post_account() request header %s", $request_header);
161 0         0 debugf("post_account() request url: %s", $request_url);
162              
163 0         0 my $res = $self->_request({
164             method => 'POST', url => $request_url, header => $request_header
165             });
166              
167 0 0       0 croak "Account POST failed: ".$res->status_line unless $res->is_success;
168 0         0 my @headers = $res->headers->flatten();
169 0         0 debugf("post_account() response headers %s", \@headers);
170 0         0 debugf("post_account() response body %s", $res->content);
171 0         0 my %headers = @headers;
172 0         0 return \%headers;
173             }
174              
175             sub get_container {
176 0     0 1 0 my $self = shift;
177 0         0 my $rule = Data::Validator->new(
178             url => { isa => 'Str', default => $self->storage_url},
179             token => { isa => 'Str', default => $self->token },
180             container_name => { isa => 'Path', coerce => 1 },
181             marker => { isa => 'Str', default => '', coerce => 1 },
182             limit => { isa => 'Int', default => 0, coerce => 1 },
183             prefix => { isa => 'Str', default => '', coerce => 1 },
184             delimiter => { isa => 'Str', default => '', coerce => 1 },
185             end_marker => { isa => 'Str', default => '', coerce => 1 },
186             full_listing => { isa => 'Bool', default => 0, coerce => 1 },
187             );
188 0         0 my $args = $rule->validate(@_);
189              
190 0 0       0 if ($args->{full_listing}) {
191 0         0 my @full_containers = ();
192             my ($rv_h, $rv_c) = __PACKAGE__->new->get_container(
193             url => $args->{url},
194             token => $args->{token},
195             container_name => $args->{container_name},
196             marker => $args->{marker},
197             limit => $args->{limit},
198             prefix => $args->{prefix},
199             delimiter => $args->{delimiter},
200             end_marker => $args->{end_marker},
201 0         0 );
202 0         0 my $total_count = int $rv_h->{'x-container-object-count'};
203 0         0 my $last_n = scalar @{$rv_c};
  0         0  
204 0 0       0 if (scalar @{$rv_c}) {
  0         0  
205 0         0 push @full_containers, @{$rv_c};
  0         0  
206             }
207 0         0 until ($total_count == scalar @full_containers) {
208             # find marker
209 0         0 my $marker;
210 0 0       0 unless ($args->{delimiter}) {
211 0         0 $marker = $full_containers[scalar(@full_containers) - 1]->{name};
212             }
213             else {
214 0 0       0 if (exists $full_containers[scalar(@full_containers) - 1]->{name}) {
215 0         0 $marker = $full_containers[scalar(@full_containers) - 1]->{name};
216             }
217             else {
218 0         0 $marker = $full_containers[scalar(@full_containers) - 1]->{subdir};
219             }
220             }
221             #
222             my ($rv_h2, $rv_c2) = __PACKAGE__->new->get_container(
223             url => $args->{url},
224             token => $args->{token},
225             container_name => $args->{container_name},
226             marker => $marker,
227             limit => $args->{limit},
228             prefix => $args->{prefix},
229             delimiter => $args->{delimiter},
230             end_marker => $args->{end_marker},
231 0         0 );
232 0 0       0 if (scalar @{$rv_c2}) {
  0         0  
233 0         0 push @full_containers, @{$rv_c2};
  0         0  
234             }
235             else {
236 0         0 last;
237             }
238             }
239 0         0 return ($rv_h, \@full_containers);
240             }
241              
242             # make query strings
243 0         0 my @qs = ('format=json');
244 0 0       0 if ($args->{marker}) {
245 0         0 push @qs, sprintf "marker=%s", uri_escape($args->{marker});
246             }
247 0 0       0 if ($args->{limit}) {
248 0         0 push @qs, sprintf("limit=%d", $args->{limit});
249             }
250 0 0       0 if ($args->{prefix}) {
251 0         0 push @qs, sprintf("prefix=%s", uri_escape($args->{prefix}));
252             }
253 0 0       0 if ($args->{delimiter}) {
254 0         0 push @qs, sprintf("delimiter=%s", uri_escape($args->{delimiter}));
255             }
256 0 0       0 if ($args->{end_marker}) {
257 0         0 push @qs, sprintf("end_marker=%s", uri_escape($args->{end_marker}));
258             }
259              
260 0         0 my $request_header = ['X-Auth-Token' => $args->{token}];
261 0         0 my $request_url = sprintf "%s/%s?%s", $args->{url}, uri_escape($args->{container_name}->stringify), join('&', @qs);
262 0         0 debugf("get_container() request header %s", $request_header);
263 0         0 debugf("get_container() request url: %s", $request_url);
264              
265 0         0 my $res = $self->_request({
266             method => 'GET', url => $request_url, header => $request_header
267             });
268              
269 0 0       0 croak "Container GET failed: ".$res->status_line unless $res->is_success;
270 0         0 my @headers = $res->headers->flatten();
271 0         0 debugf("get_container() response headers %s", \@headers);
272 0         0 debugf("get_container() response body %s", $res->content);
273 0         0 my %headers = @headers;
274 0         0 return (\%headers, from_json($res->content));
275             }
276              
277             sub head_container {
278 0     0 1 0 my $self = shift;
279 0         0 my $rule = Data::Validator->new(
280             url => { isa => 'Str', default => $self->storage_url},
281             token => { isa => 'Str', default => $self->token },
282             container_name => { isa => 'Path', coerce => 1 },
283             );
284 0         0 my $args = $rule->validate(@_);
285              
286 0         0 my $request_header = ['X-Auth-Token' => $args->{token}];
287 0         0 my $request_url = sprintf "%s/%s", $args->{url}, uri_escape($args->{container_name}->stringify);
288 0         0 debugf("head_container() request header %s", $request_header);
289 0         0 debugf("head_container() request url: %s", $args->{url});
290              
291 0         0 my $res = $self->_request({
292             method => 'HEAD', url => $request_url, header => $request_header
293             });
294              
295 0 0       0 croak "Container HEAD failed: ".$res->status_line unless $res->is_success;
296 0         0 my @headers = $res->headers->flatten();
297 0         0 debugf("head_container() response headers %s", \@headers);
298 0         0 debugf("head_container() response body %s", $res->content);
299 0         0 my %headers = @headers;
300 0         0 return \%headers;
301             }
302              
303             sub put_container {
304 2     2 1 963 my $self = shift;
305             my $rule = Data::Validator->new(
306             url => { isa => 'Str', default => $self->storage_url},
307             token => { isa => 'Str', default => $self->token },
308             container_name => { isa => 'Path', coerce => 1 },
309 2     2   119 content_length => { isa => 'Int', default => sub { 0 } },
310 2         45 content_type => { isa => 'Str', default => 'application/directory'},
311             );
312 2         493 my $args = $rule->validate(@_);
313              
314             my $request_header = [
315             'X-Auth-Token' => $args->{token},
316             'Content-Length' => $args->{content_length},
317             'Content-Type' => $args->{content_type},
318 2         35 ];
319              
320 2         10 my $request_url = sprintf "%s/%s", $args->{url}, uri_escape($args->{container_name}->stringify);
321 2         65 debugf("put_account() request header %s", $request_header);
322 2         12 debugf("put_account() request url: %s", $request_url);
323              
324 2         13 my $res = $self->_request({
325             method => 'PUT', url => $request_url, header => $request_header
326             });
327              
328 2 50       416 croak "Container PUT failed: ".$res->status_line unless $res->is_success;
329 2         123 my @headers = $res->headers->flatten();
330 2         186 debugf("put_container() response headers %s", \@headers);
331 2         16 debugf("put_container() response body %s", $res->content);
332 2         12 my %headers = @headers;
333 2         8 return \%headers;
334             }
335              
336             sub post_container {
337 0     0 1   my $self = shift;
338 0           my $rule = Data::Validator->new(
339             url => { isa => 'Str', default => $self->storage_url},
340             token => { isa => 'Str', default => $self->token },
341             container_name => { isa => 'Path', coerce => 1 },
342             headers => { isa => 'HashRef'},
343             );
344 0           my $args = $rule->validate(@_);
345              
346 0           my $request_header = ['X-Auth-Token' => $args->{token}];
347 0 0 0       unless (exists $args->{headers}->{'Content-Length'} || exists($args->{headers}->{'content-length'})) {
348 0           $args->{headers}->{'Content-Length'} = 0;
349             }
350 0           my @additional_headers = %{ $args->{headers} };
  0            
351 0           push @{$request_header}, @additional_headers;
  0            
352 0           my $request_url = sprintf "%s/%s", $args->{url}, uri_escape($args->{container_name}->stringify);
353 0           debugf("post_container() request header %s", $request_header);
354 0           debugf("post_container() request url: %s", $request_url);
355              
356 0           my $res = $self->_request({
357             method => 'POST', url => $request_url, header => $request_header
358             });
359              
360 0 0         croak "Container POST failed: ".$res->status_line unless $res->is_success;
361 0           my @headers = $res->headers->flatten();
362 0           debugf("post_container() response headers %s", \@headers);
363 0           debugf("post_container() response body %s", $res->content);
364 0           my %headers = @headers;
365 0           return \%headers;
366             }
367              
368             sub delete_container {
369 0     0 1   my $self = shift;
370 0           my $rule = Data::Validator->new(
371             url => { isa => 'Str', default => $self->storage_url},
372             token => { isa => 'Str', default => $self->token },
373             container_name => { isa => 'Path', coerce => 1 },
374             );
375 0           my $args = $rule->validate(@_);
376              
377             # corecing nested path
378             #$args->{container_name} = path($args->{container_name})->stringify;
379              
380 0           my $request_header = ['X-Auth-Token' => $args->{token}];
381 0           my $request_url = sprintf "%s/%s", $args->{url}, uri_escape($args->{container_name}->stringify);
382 0           debugf("delete_container() request header %s", $request_header);
383 0           debugf("delete_container() request url: %s", $request_url);
384              
385 0           my $res = $self->_request({
386             method => 'DELETE', url => $request_url, header => $request_header,
387             content => []
388             });
389              
390 0 0         croak "Container DELETE failed: ".$res->status_line unless $res->is_success;
391 0           my @headers = $res->headers->flatten();
392 0           debugf("delete_container() response headers %s", \@headers);
393 0           debugf("delete_container() response body %s", $res->content);
394 0           my %headers = @headers;
395 0           return \%headers;
396             }
397              
398             sub get_object {
399 0     0 1   my $self = shift;
400 0           my $rule = Data::Validator->new(
401             url => { isa => 'Str', default => $self->storage_url },
402             token => { isa => 'Str', default => $self->token },
403             container_name => { isa => 'Path', coerce => 1 },
404             object_name => { isa => 'Str'},
405             write_file => { isa => 'FileHandle', xor => [qw(write_code)] },
406             write_code => { isa => 'CodeRef' },
407             );
408 0           my $args = $rule->validate(@_);
409              
410 0           my $request_header = ['X-Auth-Token' => $args->{token}];
411             my $request_url = sprintf "%s/%s/%s", $args->{url},
412             uri_escape($args->{container_name}->stringify),
413 0           uri_escape($args->{object_name});
414 0           my %special_headers = ('Content-Length' => undef);
415 0           debugf("get_object() request header %s", $request_header);
416 0           debugf("get_object() request special headers: %s", $request_url);
417 0           debugf("get_object() request url: %s", $request_url);
418              
419 0           my $request_params = {
420             method => 'GET', url => $request_url, header => $request_header,
421             special_headers => \%special_headers,
422             write_code => undef,
423             write_file => undef,
424             };
425 0 0         if (exists $args->{write_code}) {
426 0           $request_params->{write_code} = $args->{write_code};
427             }
428 0 0         if (exists $args->{write_file}) {
429 0           $request_params->{write_file} = $args->{write_file};
430             }
431 0           my $res = $self->_request($request_params);
432              
433 0 0         croak "Object GET failed: ".$res->status_line unless $res->is_success;
434 0           my @headers = $res->headers->flatten();
435 0           debugf("get_object() response headers %s", \@headers);
436 0   0       debugf("get_object() response body length %s byte", length($res->content || 0));
437 0           my %headers = @headers;
438 0           my $etag = $headers{etag};
439 0           $etag =~ s/^\s*(.*?)\s*$/$1/; # delete spaces
440 0           return $etag;
441             }
442              
443             sub head_object {
444 0     0 1   my $self = shift;
445 0           my $rule = Data::Validator->new(
446             url => { isa => 'Str', default => $self->storage_url},
447             token => { isa => 'Str', default => $self->token },
448             container_name => { isa => 'Path', coerce => 1 },
449             object_name => { isa => 'Str'},
450             );
451 0           my $args = $rule->validate(@_);
452              
453 0           my $request_header = ['X-Auth-Token' => $args->{token}];
454             my $request_url = sprintf "%s/%s/%s", $args->{url},
455             uri_escape($args->{container_name}->stringify),
456 0           uri_escape($args->{object_name});
457 0           debugf("head_object() request header %s", $request_header);
458 0           debugf("head_object() request url: %s", $request_url);
459              
460 0           my $res = $self->_request({
461             method => 'HEAD', url => $request_url, header => $request_header,
462             content => []
463             });
464              
465 0 0         croak "Object HEAD failed: ".$res->status_line unless $res->is_success;
466 0           my @headers = $res->headers->flatten();
467 0           debugf("head_object() response headers %s", \@headers);
468 0           debugf("head_object() response body %s", $res->content);
469 0           my %headers = @headers;
470 0           return \%headers;
471             }
472              
473             sub put_object {
474 0     0 1   my $self = shift;
475             my $rule = Data::Validator->new(
476             url => { isa => 'Str', default => $self->storage_url},
477             token => { isa => 'Str', default => $self->token },
478             container_name => { isa => 'Path', coerce => 1 },
479             object_name => { isa => 'Str'},
480             content => { isa => 'Str|FileHandle'},
481 0     0     content_length => { isa => 'Int', default => sub { 0 } },
482             content_type => { isa => 'Str', default => ''},
483             etag => { isa => 'Str', default => undef },
484 0     0     query_string => { isa => 'Str', default => sub {''} },
485 0           headers => { isa => 'HashRef', default => {} },
486             );
487 0           my $args = $rule->validate(@_);
488              
489             my $request_header = [
490             'X-Auth-Token' => $args->{token},
491             'Content-Length' => $args->{content_length},
492             'Content-Type' => $args->{content_type},
493 0           ];
494 0 0         if ($args->{etag}) {
495 0           push @{$request_header}, ('ETag' => $args->{etag});
  0            
496             }
497 0           push @{$request_header}, %{ $args->{headers} };
  0            
  0            
498              
499             my $request_url = sprintf "%s/%s/%s", $args->{url},
500             uri_escape($args->{container_name}->stringify),
501 0           uri_escape($args->{object_name});
502 0 0         if ($args->{query_string}) {
503 0           $request_url .= '?'.$args->{query_string};
504             }
505 0           debugf("put_object() request header %s", $request_header);
506 0           debugf("put_object() request url: %s", $request_url);
507              
508             my $res = $self->_request({
509             method => 'PUT', url => $request_url, header => $request_header, content => $args->{content}
510 0           });
511              
512 0 0         croak "Object PUT failed: ".$res->status_line unless $res->is_success;
513 0           my @headers = $res->headers->flatten();
514 0           debugf("put_object() response headers %s", \@headers);
515 0           debugf("put_object() response body %s", $res->content);
516 0           my %headers = @headers;
517 0           my $etag = $headers{etag};
518 0           $etag =~ s/^\s*(.*?)\s*$/$1/; # delete spaces
519 0           return $etag;
520             }
521              
522             sub post_object {
523 0     0 1   my $self = shift;
524 0           my $rule = Data::Validator->new(
525             url => { isa => 'Str', default => $self->storage_url},
526             token => { isa => 'Str', default => $self->token },
527             container_name => { isa => 'Path', coerce => 1 },
528             object_name => { isa => 'Str'},
529             headers => { isa => 'HashRef'},
530             );
531 0           my $args = $rule->validate(@_);
532              
533 0           my $request_header = ['X-Auth-Token' => $args->{token}];
534 0           my @additional_headers = %{ $args->{headers} };
  0            
535 0           push @{$request_header}, @additional_headers;
  0            
536             my $request_url = sprintf "%s/%s/%s", $args->{url},
537             uri_escape($args->{container_name}->stringify),
538 0           uri_escape($args->{object_name});
539 0           debugf("post_object() request header %s", $request_header);
540 0           debugf("post_object() request url: %s", $request_url);
541              
542 0           my $res = $self->_request({
543             method => 'POST', url => $request_url, header => $request_header
544             });
545              
546 0 0         croak "Object POST failed: ".$res->status_line unless $res->is_success;
547 0           my @headers = $res->headers->flatten();
548 0           debugf("post_object() response headers %s", \@headers);
549 0           debugf("post_object() response body %s", $res->content);
550 0           my %headers = @headers;
551 0           return \%headers;
552             }
553              
554             sub delete_object {
555 0     0 1   my $self = shift;
556 0           my $rule = Data::Validator->new(
557             url => { isa => 'Str', default => $self->storage_url},
558             token => { isa => 'Str', default => $self->token },
559             container_name => { isa => 'Path', coerce => 1 },
560             object_name => { isa => 'Str'},
561             );
562 0           my $args = $rule->validate(@_);
563              
564 0           my $request_header = ['X-Auth-Token' => $args->{token}];
565             my $request_url = sprintf "%s/%s/%s", $args->{url},
566             uri_escape($args->{container_name}->stringify),
567 0           uri_escape($args->{object_name});
568 0           debugf("delete_object() request header %s", $request_header);
569 0           debugf("delete_object() request url: %s", $request_url);
570              
571 0           my $res = $self->_request({
572             method => 'DELETE', url => $request_url, header => $request_header,
573             content => []
574             });
575              
576 0 0         croak "Object DELETE failed: ".$res->status_line unless $res->is_success;
577 0           my @headers = $res->headers->flatten();
578 0           debugf("delete_object() response headers %s", \@headers);
579 0           debugf("delete_object() response body %s", $res->content);
580 0           my %headers = @headers;
581 0           return \%headers;
582             }
583              
584             1;
585             __END__