File Coverage

blib/lib/Net/Amazon/S3/Bucket.pm
Criterion Covered Total %
statement 168 187 89.8
branch 42 68 61.7
condition 10 28 35.7
subroutine 26 27 96.3
pod 17 18 94.4
total 263 328 80.1


line stmt bran cond sub pod time code
1             # ABSTRACT: convenience object for working with Amazon S3 buckets
2             $Net::Amazon::S3::Bucket::VERSION = '0.991';
3             use Moose 0.85;
4 99     99   602 use MooseX::StrictConstructor 0.16;
  99         1688  
  99         621  
5 99     99   532974 use Carp;
  99         1435  
  99         520  
6 99     99   255079 use File::stat;
  99         214  
  99         5448  
7 99     99   42417 use IO::File 1.14;
  99         554121  
  99         415  
8 99     99   43239  
  99         688317  
  99         216475  
9             has 'account' => (
10             is => 'ro',
11             isa => 'Net::Amazon::S3',
12             required => 1,
13             handles => [qw[ err errstr ]],
14             );
15             has 'bucket' => ( is => 'ro', isa => 'Str', required => 1 );
16             has 'creation_date' => ( is => 'ro', isa => 'Maybe[Str]', required => 0 );
17              
18             has 'region' => (
19             is => 'ro',
20             lazy => 1,
21             predicate => 'has_region',
22             default => sub {
23             return $_[0]->account->vendor->guess_bucket_region ($_[0]);
24             },
25             );
26              
27             __PACKAGE__->meta->make_immutable;
28              
29             # returns bool
30             my $self = shift;
31             my %args = Net::Amazon::S3::Utils->parse_arguments (\@_, ['key', 'value']);
32 17     17 1 27  
33 17         75 my $key = delete $args{key};
34             my $value = delete $args{value};
35 17         48  
36 17         30 if ( ref($value) eq 'SCALAR' ) {
37             $args{'Content-Length'} ||= -s $$value;
38 17 100       46 $value = _content_sub($$value);
39 3   33     105 } else {
40 3         13 $args{'Content-Length'} ||= length $value;
41             }
42 14   33     50  
43             my $acl;
44             $acl = delete $args{acl_short} if exists $args{acl_short};
45 17         24 $acl = delete $args{acl} if exists $args{acl};
46 17 100       37  
47 17 100       39 my $encryption = delete $args{encryption};
48             my %headers = %args;
49 17         26  
50 17         48 # we may get a 307 redirect; ask server to signal 100 Continue
51             # before reading the content from CODE reference (_content_sub)
52             $headers{expect} = '100-continue' if ref $value;
53              
54 17 100       39 my $response = $self->_perform_operation (
55             'Net::Amazon::S3::Operation::Object::Add',
56 17         69  
57             key => $key,
58             value => $value,
59             (acl => $acl) x!! defined $acl,
60             ((encryption => $encryption) x!! defined $encryption),
61             headers => \%headers,
62             );
63              
64             return $response->is_success;
65             }
66 10         201  
67             my $self = shift;
68             my %args = Net::Amazon::S3::Utils->parse_arguments (\@_, ['key', 'value']);
69             $args{value} = \ delete $args{value};
70 2     2 1 4  
71 2         11 return $self->add_key (%args);
72 2         8 }
73              
74 2         9 my $self = shift;
75             my %args = Net::Amazon::S3::Utils->parse_arguments (\@_, ['key', 'source' ]);
76              
77             my $key = delete $args{key};
78 6     6 1 10 my $source = delete $args{source};
79 6         22  
80             my $acl_short;
81 6         18 if (%args) {
82 6         12 if ( $args{acl_short} ) {
83             $acl_short = $args{acl_short};
84 6         7 delete $args{acl_short};
85 6 50       11 }
86 6 50       14 $args{Net::Amazon::S3::Constants->HEADER_METADATA_DIRECTIVE} ||= 'REPLACE';
87 6         8 }
88 6         10  
89             $args{Net::Amazon::S3::Constants->HEADER_COPY_SOURCE} = $source;
90 6   50     35  
91             my $encryption = delete $args{encryption};
92              
93 6         15 my $acct = $self->account;
94             my $response = $self->_perform_operation (
95 6         11 'Net::Amazon::S3::Operation::Object::Add',
96              
97 6         162 value => '',
98 6         22 key => $key,
99             acl_short => $acl_short,
100             (encryption => $encryption) x!! defined $encryption,
101             headers => \%args,
102             );
103              
104             return unless $response->is_success;
105             }
106              
107             my $self = shift;
108 0 0       0 my %args = Net::Amazon::S3::Utils->parse_arguments_with_object (\@_);
109              
110             my $key = delete $args{key};
111             croak "Need some metadata to change" unless %args;
112 2     2 1 5  
113 2         9 return $self->copy_key (
114             key => $key,
115 2         7 source => "/" . $self->bucket . "/" . $key,
116 2 50       6 %args,
117             );
118 2         59 }
119              
120             my $self = shift;
121             my %args = Net::Amazon::S3::Utils->parse_arguments_with_object (\@_);
122              
123             return $self->get_key (%args, method => 'HEAD', filename => undef);
124             }
125              
126 9     9 1 20 my $self = shift;
127 9         33 my %args = Net::Amazon::S3::Utils->parse_arguments (\@_, ['key', 'expires_at']);
128              
129 9         43 $args{method} = 'GET' unless exists $args{method};
130              
131             my $request = Net::Amazon::S3::Operation::Object::Fetch::Request->new (
132             s3 => $self->account,
133 4     4 1 11 bucket => $self,
134 4         38 key => $args{key},
135             method => $args{method},
136 4 100       17 );
137              
138             return $request->query_string_authentication_uri ($args{expires_at});
139             }
140              
141             my $self = shift;
142             my %args = Net::Amazon::S3::Utils->parse_arguments (\@_, ['key', 'method', 'filename']);
143 4         130  
144             $args{filename} = ${ delete $args{filename} }
145 4         20 if ref $args{filename};
146              
147             $args{method} ||= 'GET';
148              
149 23     23 1 39 my $response = $self->_perform_operation (
150 23         82 'Net::Amazon::S3::Operation::Object::Fetch',
151              
152 3         6 filename => $args{filename},
153 23 100       74 (range => $args{range}) x defined $args{range},
154              
155 23   50     47 key => $args{key},
156             method => $args{method},
157             );
158              
159             return unless $response->is_success;
160             my $etag = $response->etag;
161              
162             my $return;
163             foreach my $header ($response->headers->header_field_names) {
164             $return->{ lc $header } = $response->header ($header);
165 23         109 }
166             $return->{content_length} = $response->content_length || 0;
167 7 100       33 $return->{content_type} = $response->content_type;
168 3         61 $return->{etag} = $etag;
169             $return->{value} = $response->content;
170 3         4  
171 3         16 return $return;
172 15         585  
173             }
174 3   100     121  
175 3         95 my $self = shift;
176 3         67 my %args = Net::Amazon::S3::Utils->parse_arguments (\@_, ['key', 'method', 'filename']);
177 3         17  
178             $args{filename} = \ delete $args{filename};
179 3         61 return $self->get_key (%args);
180             }
181              
182             # returns bool
183             my $self = shift;
184 1     1 1 8 my @objects = @_;
185 1         6 return unless( scalar(@objects) );
186              
187 1         5 # Since delete can handle up to 1000 requests, be a little bit nicer
188 1         4 # and slice up requests and also allow keys to be strings
189             # rather than only objects.
190             my $last_result;
191             while (scalar(@objects) > 0) {
192             my $response = $self->_perform_operation (
193 5     5 0 13 'Net::Amazon::S3::Operation::Objects::Delete',
194 5         15  
195 5 50       13 keys => [
196             map { ref ($_) ? $_->key : $_ }
197             splice @objects, 0, ((scalar(@objects) > 1000) ? 1000 : scalar(@objects))
198             ]
199             );
200 5         7  
201 5         20 return unless $response->is_success;
202             }
203              
204             return 1;
205             }
206 5 50       21  
  11 50       39  
207             my $self = shift;
208             my %args = Net::Amazon::S3::Utils->parse_arguments_with_object (\@_);
209              
210             croak 'must specify key' unless defined $args{key} && length $args{key};
211 4 100       26  
212             my $response = $self->_perform_operation (
213             'Net::Amazon::S3::Operation::Object::Delete',
214 1         129 %args,
215             );
216              
217             return $response->is_success;
218 9     9 1 16 }
219 9         35  
220             my $self = shift;
221 9 50 33     45 return $self->account->delete_bucket (bucket => $self, @_);
222             }
223 9         29  
224             my $self = shift;
225             my %args = Net::Amazon::S3::Utils->parse_arguments (\@_);
226              
227             return $self->account->list_bucket ($self, %args);
228 5         23 }
229              
230             my $self = shift;
231             my %args = Net::Amazon::S3::Utils->parse_arguments (\@_);
232 1     1 1 2  
233 1         33 return $self->account->list_bucket_all ($self, %args);
234             }
235              
236             my $self = shift;
237 2     2 1 4 my %args = Net::Amazon::S3::Utils->parse_arguments_with_object (\@_);
238 2         7  
239             my $response;
240 2         56 if (defined $args{key}) {
241             $response = $self->_perform_operation (
242             'Net::Amazon::S3::Operation::Object::Acl::Fetch',
243             %args,
244 2     2 1 4 );
245 2         7 } else {
246             delete $args{key};
247 2         45 $response = $self->_perform_operation (
248             'Net::Amazon::S3::Operation::Bucket::Acl::Fetch',
249             %args,
250             );
251 14     14 1 29 }
252 14         57  
253             return unless $response->is_success;
254 14         34 return $response->content;
255 14 100       39 }
256 8         24  
257             my $self = shift;
258             my %args = Net::Amazon::S3::Utils->parse_arguments_with_object (\@_);
259              
260             my $response;
261 6         12 if (defined $args{key}) {
262 6         22 $response = $self->_perform_operation (
263             'Net::Amazon::S3::Operation::Object::Acl::Set',
264             %args,
265             );
266             } else {
267             delete $args{key};
268 5 100       36 $response = $self->_perform_operation (
269 2         54 'Net::Amazon::S3::Operation::Bucket::Acl::Set',
270             %args,
271             );
272             }
273 24     24 1 44  
274 24         102 return unless $response->is_success;
275             return 1;
276 24         62 }
277 24 100       75  
278 13         67 my $self = shift;
279             my %args = Net::Amazon::S3::Utils->parse_arguments (\@_);
280              
281             my $response = $self->_perform_operation (
282             'Net::Amazon::S3::Operation::Bucket::Location',
283 11         18 %args,
284 11         34 );
285              
286             return unless $response->is_success;
287             return $response->location;
288             }
289              
290 18 100       1303 my $self = shift;
291 11         231 my %args = @_ == 1 ? %{ $_[0] } : @_;
292              
293             my $response;
294             if (defined $args{key}) {
295 2     2 1 4 $response = $self->_perform_operation (
296 2         8 'Net::Amazon::S3::Operation::Object::Tags::Add',
297             %args,
298 2         7 );
299             } else {
300             delete $args{key};
301             $response = $self->_perform_operation (
302             'Net::Amazon::S3::Operation::Bucket::Tags::Add',
303 0 0       0 %args,
304 0         0 );
305             }
306              
307             return $response->is_success;
308 14     14 1 33 }
309 14 100       49  
  11         38  
310             my $self = shift;
311 14         23 my %args = Net::Amazon::S3::Utils->parse_arguments_with_object (\@_);
312 14 100       51  
313 8         33 my $response;
314             if (defined $args{key}) {
315             $response = $self->_perform_operation (
316             'Net::Amazon::S3::Operation::Object::Tags::Delete',
317             %args,
318 6         10 );
319 6         24 } else {
320             delete $args{key};
321             $response = $self->_perform_operation (
322             'Net::Amazon::S3::Operation::Bucket::Tags::Delete',
323             %args,
324             );
325 8         52 }
326              
327             return $response->is_success;
328             }
329 15     15 1 40  
330 15         62 my $filename = shift;
331             my $stat = stat($filename);
332 15         35 my $remaining = $stat->size;
333 15 100       41 my $blksize = $stat->blksize || 4096;
334 9         29  
335             croak "$filename not a readable file with fixed size"
336             unless -r $filename and ( -f _ || $remaining );
337             my $fh = IO::File->new( $filename, 'r' )
338             or croak "Could not open $filename: $!";
339 6         13 $fh->binmode;
340 6         19  
341             return sub {
342             my $buffer;
343              
344             # upon retries the file is closed and we must reopen it
345             unless ( $fh->opened ) {
346 9         46 $fh = IO::File->new( $filename, 'r' )
347             or croak "Could not open $filename: $!";
348             $fh->binmode;
349             $remaining = $stat->size;
350 3     3   7 }
351 3         14  
352 3         501 # warn "read remaining $remaining";
353 3   50     62 unless ( my $read = $fh->read( $buffer, $blksize ) ) {
354              
355 3 50 33     75 # warn "read $read buffer $buffer remaining $remaining";
      33        
356             croak
357 3 50       22 "Error while reading upload content $filename ($remaining remaining) $!"
358             if $! and $remaining;
359 3         320  
360             # otherwise, we found EOF
361             $fh->close
362 0     0   0 or croak "close of upload content $filename failed: $!";
363             $buffer ||= ''
364             ; # LWP expects an emptry string on finish, read returns 0
365 0 0       0 }
366 0 0       0 $remaining -= length($buffer);
367             return $buffer;
368 0         0 };
369 0         0 }
370              
371             my ($self) = @_;
372              
373 0 0       0 my $protocol = $self->account->secure ? 'https' : 'http';
374             my $host = $self->account->host;
375             my $path = $self->bucket;
376 0 0 0     0 my @retry = (1, 2, (4) x 8);
377              
378             if ($self->account->use_virtual_host) {
379             $host = "$path.$host";
380             $path = '';
381 0 0       0 }
382              
383 0   0     0 my $request_uri = "${protocol}://${host}/$path";
384             while (@retry) {
385             my $request = HTTP::Request->new (HEAD => $request_uri);
386 0         0  
387 0         0 # Disable redirects
388 3         39 my $requests_redirectable = $self->account->ua->requests_redirectable;
389             $self->account->ua->requests_redirectable( [] );
390              
391             my $response = $self->account->ua->request ($request);
392 1     1   3  
393             $self->account->ua->requests_redirectable( $requests_redirectable );
394 1 50       53  
395 1         25 return $response->header (Net::Amazon::S3::Constants->HEADER_BUCKET_REGION)
396 1         24 if $response->header (Net::Amazon::S3::Constants->HEADER_BUCKET_REGION);
397 1         5  
398             print STDERR "Invalid bucket head response; $request_uri\n";
399 1 50       22 print STDERR $response->as_string;
400 1         5  
401 1         2 sleep shift @retry;
402             }
403              
404 1         4 die "Cannot determine bucket region; bucket=${\ $self->bucket }";
405 1         5 }
406 1         11  
407             my ($self, $operation, %params) = @_;
408              
409 1         6582 $self->account->_perform_operation ($operation => (
410 1         42 bucket => $self,
411             %params,
412 1         31 ));
413             }
414 1         1299  
415             1;
416 1 50       19  
417              
418             =pod
419 0         0  
420 0         0 =encoding UTF-8
421              
422 0         0 =head1 NAME
423              
424             Net::Amazon::S3::Bucket - convenience object for working with Amazon S3 buckets
425 0         0  
  0         0  
426             =head1 VERSION
427              
428             version 0.991
429 129     129   403  
430             =head1 SYNOPSIS
431 129         3495  
432             use Net::Amazon::S3;
433              
434             my $bucket = $s3->bucket("foo");
435              
436             ok($bucket->add_key("key", "data"));
437             ok($bucket->add_key("key", "data", {
438             content_type => "text/html",
439             'x-amz-meta-colour' => 'orange',
440             }));
441              
442             # Enable server-side encryption
443             ok($bucket->add_key("key", "data", {
444             encryption => 'AES256',
445             }));
446              
447             # the err and errstr methods just proxy up to the Net::Amazon::S3's
448             # objects err/errstr methods.
449             $bucket->add_key("bar", "baz") or
450             die $bucket->err . $bucket->errstr;
451              
452             # fetch a key
453             $val = $bucket->get_key("key");
454             is( $val->{value}, 'data' );
455             is( $val->{content_type}, 'text/html' );
456             is( $val->{etag}, 'b9ece18c950afbfa6b0fdbfa4ff731d3' );
457             is( $val->{'x-amz-meta-colour'}, 'orange' );
458              
459             # fetch a part of the key
460             $val = $bucket->get_key("key", { range => "bytes=1024-10240" });
461              
462             # returns undef on missing or on error (check $bucket->err)
463             is(undef, $bucket->get_key("non-existing-key"));
464             die $bucket->errstr if $bucket->err;
465              
466             # fetch a key's metadata
467             $val = $bucket->head_key("key");
468             is( $val->{value}, '' );
469             is( $val->{content_type}, 'text/html' );
470             is( $val->{etag}, 'b9ece18c950afbfa6b0fdbfa4ff731d3' );
471             is( $val->{'x-amz-meta-colour'}, 'orange' );
472              
473             # delete a key
474             ok($bucket->delete_key($key_name));
475             ok(! $bucket->delete_key("non-exist-key"));
476              
477             # delete the entire bucket (Amazon requires it first be empty)
478             $bucket->delete_bucket;
479              
480             =head1 DESCRIPTION
481              
482             This module represents an S3 bucket. You get a bucket object
483             from the Net::Amazon::S3 object.
484              
485             =head1 METHODS
486              
487             =head2 new
488              
489             Create a new bucket object. Expects a hash containing these two arguments:
490              
491             =over
492              
493             =item bucket
494              
495             =item account
496              
497             =back
498              
499             =head2 add_key
500              
501             Takes three positional parameters:
502              
503             =over
504              
505             =item key
506              
507             =item value
508              
509             =item configuration
510              
511             A hash of configuration data for this key.
512              
513             =over
514              
515             =item acl
516              
517             =item encryption
518              
519             =item any additional HTTP header
520              
521             =back
522              
523             See L<Net::Amazon::S3::Operation::Object::Add::Request> for details
524              
525             =back
526              
527             Returns a boolean.
528              
529             =head2 add_key_filename
530              
531             Use this to upload a large file to S3. Takes three positional parameters:
532              
533             =over
534              
535             =item key
536              
537             =item filename
538              
539             =item configuration
540              
541             A hash of configuration data for this key. (See synopsis);
542              
543             =back
544              
545             Returns a boolean.
546              
547             =head2 copy_key
548              
549             Creates (or replaces) a key, copying its contents from another key elsewhere in S3.
550             Takes the following parameters:
551              
552             =over
553              
554             =item key
555              
556             The key to (over)write
557              
558             =item source
559              
560             Where to copy the key from. Should be in the form C</I<bucketname>/I<keyname>>/.
561              
562             =item conf
563              
564             Optional configuration hash. If present and defined, the configuration (ACL
565             and headers) there will be used for the new key; otherwise it will be copied
566             from the source key.
567              
568             =back
569              
570             =head2 edit_metadata
571              
572             Changes the metadata associated with an existing key. Arguments:
573              
574             =over
575              
576             =item key
577              
578             The key to edit
579              
580             =item conf
581              
582             The new configuration hash to use
583              
584             =back
585              
586             =head2 head_key KEY
587              
588             Takes the name of a key in this bucket and returns its configuration hash
589              
590             =head2 query_string_authentication_uri KEY, EXPIRES_AT
591              
592             my $uri = $bucket->query_string_authentication_uri (
593             key => 'foo',
594             expires_at => time + 3_600, # valid for one hour
595             );
596              
597             my $uri = $bucket->query_string_authentication_uri (
598             key => 'foo',
599             expires_at => time + 3_600,
600             method => 'PUT',
601             );
602              
603             Returns uri presigned with your credentials.
604              
605             When used with Signature V4 you have to specify also HTTP method this
606             presigned uri will be used for (default: C<GET>)
607              
608             Method provides authenticated uri only for direct object operations.
609              
610             Method follows API's L</"CALLING CONVENTION">.
611              
612             Recognized positional arguments (mandatory).
613              
614             =over
615              
616             =item key
617              
618             =item expires_at
619              
620             Expiration time (epoch time).
621              
622             =back
623              
624             Optional arguments
625              
626             =over
627              
628             =item method
629              
630             Default: C<GET>
631              
632             Intended HTTP method this uri will be presigned for.
633              
634             Signature V2 doesn't use it but Signature V4 does.
635              
636             See L<https://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObject.html>
637              
638             =back
639              
640             =head2 get_key $key_name [$method]
641              
642             Takes a key name and an optional HTTP method (which defaults to C<GET>.
643             Fetches the key from AWS.
644              
645             On failure:
646              
647             Returns undef on missing content, throws an exception (dies) on server errors.
648              
649             On success:
650              
651             Returns a hashref of { content_type, etag, value, @meta } on success. Other
652             values from the server are there too, with the key being lowercased.
653              
654             =head2 get_key_filename $key_name $method $filename
655              
656             Use this to download large files from S3. Takes a key name and an optional
657             HTTP method (which defaults to C<GET>. Fetches the key from AWS and writes
658             it to the filename. THe value returned will be empty.
659              
660             On failure:
661              
662             Returns undef on missing content, throws an exception (dies) on server errors.
663              
664             On success:
665              
666             Returns a hashref of { content_type, etag, value, @meta } on success
667              
668             =head2 delete_key $key_name
669              
670             Removes C<$key> from the bucket. Forever. It's gone after this.
671              
672             Returns true on success and false on failure
673              
674             =head2 delete_bucket
675              
676             Delete the current bucket object from the server. Takes no arguments.
677              
678             Fails if the bucket has anything in it.
679              
680             This is an alias for C<< $s3->delete_bucket($bucket) >>
681              
682             =head2 list
683              
684             List all keys in this bucket.
685              
686             see L<Net::Amazon::S3/list_bucket> for documentation of this method.
687              
688             =head2 list_all
689              
690             List all keys in this bucket without having to worry about
691             'marker'. This may make multiple requests to S3 under the hood.
692              
693             see L<Net::Amazon::S3/list_bucket_all> for documentation of this method.
694              
695             =head2 get_acl
696              
697             Takes one optional positional parameter
698              
699             =over
700              
701             =item key (optional)
702              
703             If no key is specified, it returns the acl for the bucket.
704              
705             =back
706              
707             Returns an acl in XML format.
708              
709             =head2 set_acl
710              
711             Takes a configuration hash_ref containing:
712              
713             =over
714              
715             =item acl_xml (cannot be used in conjunction with acl_short)
716              
717             An XML string which contains access control information which matches
718             Amazon's published schema. There is an example of one of these XML strings
719             in the tests for this module.
720              
721             =item acl_short (cannot be used in conjunction with acl_xml)
722              
723             You can use the shorthand notation instead of specifying XML for
724             certain 'canned' types of acls.
725              
726             (from the Amazon API documentation)
727              
728             private: Owner gets FULL_CONTROL. No one else has any access rights.
729             This is the default.
730              
731             public-read:Owner gets FULL_CONTROL and the anonymous principal is granted
732             READ access. If this policy is used on an object, it can be read from a
733             browser with no authentication.
734              
735             public-read-write:Owner gets FULL_CONTROL, the anonymous principal is
736             granted READ and WRITE access. This is a useful policy to apply to a bucket,
737             if you intend for any anonymous user to PUT objects into the bucket.
738              
739             authenticated-read:Owner gets FULL_CONTROL, and any principal authenticated
740             as a registered Amazon S3 user is granted READ access.
741              
742             =item key (optional)
743              
744             If the key is not set, it will apply the acl to the bucket.
745              
746             =back
747              
748             Returns a boolean.
749              
750             =head2 get_location_constraint
751              
752             Retrieves the location constraint set when the bucket was created. Returns a
753             string (eg, 'EU'), or undef if no location constraint was set.
754              
755             =head2 err
756              
757             The S3 error code for the last error the object ran into
758              
759             =head2 errstr
760              
761             A human readable error string for the last error the object ran into
762              
763             =head2 add_tags
764              
765             # Add tags for a bucket
766             $s3->add_tags ({
767             bucket => 'bucket-name',
768             tags => { tag1 => 'value-1', tag2 => 'value-2' },
769             });
770              
771             # Add tags for an object
772             $s3->add_tags ({
773             bucket => 'bucket-name',
774             key => 'key',
775             tags => { tag1 => 'value-1', tag2 => 'value-2' },
776             });
777              
778             Takes configuration parameters
779              
780             =over
781              
782             =item key (optional, scalar)
783              
784             If key is specified, add tag(s) to object, otherwise on bucket.
785              
786             =item tags (mandatory, hashref)
787              
788             Set specified tags and their respective values.
789              
790             =item version_id (optional)
791              
792             Is specified (in conjunction with C<key>) add tag(s) to versioned object.
793              
794             =back
795              
796             Returns C<true> on success.
797              
798             Returns C<false> and sets C<err>/C<errstr> otherwise.
799              
800             =head2 delete_tags
801              
802             # Add tags for a bucket
803             $s3->delete_tags ({
804             bucket => 'bucket-name',
805             });
806              
807             # Add tags for an object
808             $s3->delete_tags ({
809             bucket => 'bucket-name',
810             key => 'key',
811             version_id => $version_id,
812             });
813              
814             Takes configuration parameters
815              
816             =over
817              
818             =item key (optional, scalar)
819              
820             If key is specified, add tag(s) to object, otherwise on bucket.
821              
822             =item version_id (optional)
823              
824             Is specified (in conjunction with C<key>) add tag(s) to versioned object.
825              
826             =back
827              
828             Returns C<true> on success.
829              
830             Returns C<false> and sets C<err>/C<errstr> otherwise.
831              
832             =head1 SEE ALSO
833              
834             L<Net::Amazon::S3>
835              
836             =head1 AUTHOR
837              
838             Branislav Zahradník <barney@cpan.org>
839              
840             =head1 COPYRIGHT AND LICENSE
841              
842             This software is copyright (c) 2022 by Amazon Digital Services, Leon Brocard, Brad Fitzpatrick, Pedro Figueiredo, Rusty Conover, Branislav Zahradník.
843              
844             This is free software; you can redistribute it and/or modify it under
845             the same terms as the Perl 5 programming language system itself.
846              
847             =cut