Browse code

read/modify/write on modify command the headers and ACL

When calling modify, S3 normally resets headers and ACLs to a default
we probably don't want.

Keep the header and ACL the same (unless that's what is being
modified) by reading the header and ACL before invoking the modify,
then write the ACL after the modify completes.

Matt Domsch authored on 2014/12/05 08:35:10
Showing 2 changed files
... ...
@@ -619,18 +619,39 @@ class S3(object):
619 619
         debug("Received response '%s'" % (response))
620 620
         return response
621 621
 
622
+    @staticmethod
623
+    def _sanitize_headers(headers):
624
+        to_remove = ('content-length',
625
+                     'x-amz-id-2',
626
+                     'accept-ranges',
627
+                     'x-amz-meta-s3cmd-attrs',
628
+                     'server',
629
+                     'last-modified',
630
+                     'etag',
631
+                     'x-amz-request-id',
632
+                     'content-type')
633
+        for h in to_remove:
634
+            if h in headers: del headers[h]
635
+        return headers
636
+
622 637
     def object_copy(self, src_uri, dst_uri, extra_headers = None):
623 638
         if src_uri.type != "s3":
624 639
             raise ValueError("Expected URI type 's3', got '%s'" % src_uri.type)
625 640
         if dst_uri.type != "s3":
626 641
             raise ValueError("Expected URI type 's3', got '%s'" % dst_uri.type)
627
-        headers = SortedDict(ignore_case = True)
642
+
643
+        response = self.object_info(src_uri)
644
+        headers = response['headers']
645
+        headers = self._sanitize_headers(headers)
646
+
628 647
         headers['x-amz-copy-source'] = "/%s/%s" % (src_uri.bucket(), self.urlencode_string(src_uri.object()))
629 648
         headers['x-amz-metadata-directive'] = "COPY"
630 649
         if self.config.acl_public:
631 650
             headers["x-amz-acl"] = "public-read"
632 651
         if self.config.reduced_redundancy:
633 652
             headers["x-amz-storage-class"] = "REDUCED_REDUNDANCY"
653
+        else:
654
+            headers["x-amz-storage-class"] = "STANDARD"
634 655
 
635 656
         ## Set server side encryption
636 657
         if self.config.server_side_encryption:
... ...
@@ -642,19 +663,22 @@ class S3(object):
642 642
         request = self.create_request("OBJECT_PUT", uri = dst_uri, headers = headers)
643 643
         response = self.send_request(request)
644 644
         return response
645
-
646
-    def object_replace(self, src_uri, dst_uri, extra_headers = None):
645
+        
646
+    def object_modify(self, src_uri, dst_uri, extra_headers = None):
647 647
         if src_uri.type != "s3":
648 648
             raise ValueError("Expected URI type 's3', got '%s'" % src_uri.type)
649 649
         if dst_uri.type != "s3":
650 650
             raise ValueError("Expected URI type 's3', got '%s'" % dst_uri.type)
651
-        headers = SortedDict(ignore_case = True)
651
+
652
+        info_response = self.object_info(src_uri)
653
+        headers = info_response['headers']
654
+        headers = self._sanitize_headers(headers)
655
+        acl = self.get_acl(src_uri)
656
+
652 657
         headers['x-amz-copy-source'] = "/%s/%s" % (src_uri.bucket(), self.urlencode_string(src_uri.object()))
653 658
         headers['x-amz-metadata-directive'] = "REPLACE"
654
-        if self.config.acl_public:
655
-            headers["x-amz-acl"] = "public-read"
656
-        if self.config.reduced_redundancy:
657
-            headers["x-amz-storage-class"] = "REDUCED_REDUNDANCY"
659
+
660
+        # cannot change between standard and reduced redundancy with a REPLACE.
658 661
 
659 662
         ## Set server side encryption
660 663
         if self.config.server_side_encryption:
... ...
@@ -665,8 +689,11 @@ class S3(object):
665 665
 
666 666
         headers["content-type"] = self.content_type()
667 667
 
668
-        request = self.create_request("OBJECT_PUT", uri = dst_uri, headers = headers)
668
+        request = self.create_request("OBJECT_PUT", uri = src_uri, headers = headers)
669 669
         response = self.send_request(request)
670
+
671
+        acl_response = self.set_acl(src_uri, acl)
672
+
670 673
         return response
671 674
 
672 675
     def object_move(self, src_uri, dst_uri, extra_headers = None):
... ...
@@ -753,7 +753,7 @@ def cmd_cp(args):
753 753
 
754 754
 def cmd_modify(args):
755 755
     s3 = S3(Config())
756
-    return subcmd_cp_mv(args, s3.object_replace, "modify", u"File %(src)s modified")
756
+    return subcmd_cp_mv(args, s3.object_modify, "modify", u"File %(src)s modified")
757 757
 
758 758
 def cmd_mv(args):
759 759
     s3 = S3(Config())
... ...
@@ -2172,6 +2172,7 @@ def main():
2172 2172
     optparser.add_option(      "--files-from", dest="files_from", action="append", metavar="FILE", help="Read list of source-file names from FILE. Use - to read from stdin.")
2173 2173
     optparser.add_option(      "--bucket-location", dest="bucket_location", help="Datacentre to create bucket in. As of now the datacenters are: US (default), EU, ap-northeast-1, ap-southeast-1, sa-east-1, us-west-1 and us-west-2")
2174 2174
     optparser.add_option(      "--reduced-redundancy", "--rr", dest="reduced_redundancy", action="store_true", help="Store object with 'Reduced redundancy'. Lower per-GB price. [put, cp, mv]")
2175
+    optparser.add_option(      "--no-reduced-redundancy", "--no-rr", dest="reduced_redundancy", action="store_false", help="Store object without 'Reduced redundancy'. Higher per-GB price. [put, cp, mv]")
2175 2176
 
2176 2177
     optparser.add_option(      "--access-logging-target-prefix", dest="log_target_prefix", help="Target prefix for access logs (S3 URI) (for [cfmodify] and [accesslog] commands)")
2177 2178
     optparser.add_option(      "--no-access-logging", dest="log_target_prefix", action="store_false", help="Disable access logging (for [cfmodify] and [accesslog] commands)")