Repository: googleapis/google-cloud-python

Issue:
storage.bucket.Bucket.copy_blob missing option to preserve ACL
[Unlike](https://cloud.google.com/storage/docs/gsutil/commands/cp) `gsutil`.

> Note that by default, the gsutil cp command does not copy the object ACL to the new object, and instead will use the default bucket ACL. You can override this behavior with the -p option.



Discussion:
``` Diff
--- a/bucket.py
+++ b/bucket.py
@@ -431,7 +431,7 @@
                     raise

     def copy_blob(self, blob, destination_bucket, new_name=None,
-                  client=None):
+                  client=None, preserve_acl=False):
         """Copy the given blob to the given bucket, optionally with a new name.

         :type blob: :class:`gcloud.storage.blob.Blob`
@@ -459,6 +459,8 @@
         copy_result = client.connection.api_request(
             method='POST', path=api_path, _target_object=new_blob)
         new_blob._set_properties(copy_result)
+        if preserve_acl is True:
+            new_blob.acl.save(blob.acl)
         return new_blob

     def rename_blob(self, blob, new_name, client=None):

```


Resolving patch:
diff --git a/storage/google/cloud/storage/bucket.py b/storage/google/cloud/storage/bucket.py
--- a/storage/google/cloud/storage/bucket.py
+++ b/storage/google/cloud/storage/bucket.py
@@ -440,7 +440,7 @@ def delete_blobs(self, blobs, on_error=None, client=None):
                     raise
 
     def copy_blob(self, blob, destination_bucket, new_name=None,
-                  client=None):
+                  client=None, preserve_acl=True):
         """Copy the given blob to the given bucket, optionally with a new name.
 
         :type blob: :class:`google.cloud.storage.blob.Blob`
@@ -458,6 +458,10 @@ def copy_blob(self, blob, destination_bucket, new_name=None,
         :param client: Optional. The client to use.  If not passed, falls back
                        to the ``client`` stored on the current bucket.
 
+        :type preserve_acl: bool
+        :param preserve_acl: Optional. Copies ACL from old blob to new blob.
+                             Default: True.
+
         :rtype: :class:`google.cloud.storage.blob.Blob`
         :returns: The new Blob.
         """
@@ -468,6 +472,8 @@ def copy_blob(self, blob, destination_bucket, new_name=None,
         api_path = blob.path + '/copyTo' + new_blob.path
         copy_result = client.connection.api_request(
             method='POST', path=api_path, _target_object=new_blob)
+        if not preserve_acl:
+            new_blob.acl.save(acl={}, client=client)
         new_blob._set_properties(copy_result)
         return new_blob
 


Test patch:
diff --git a/storage/unit_tests/test_bucket.py b/storage/unit_tests/test_bucket.py
--- a/storage/unit_tests/test_bucket.py
+++ b/storage/unit_tests/test_bucket.py
@@ -534,6 +534,38 @@ class _Blob(object):
         self.assertEqual(kw['method'], 'POST')
         self.assertEqual(kw['path'], COPY_PATH)
 
+    def test_copy_blobs_preserve_acl(self):
+        from google.cloud.storage.acl import ObjectACL
+        SOURCE = 'source'
+        DEST = 'dest'
+        BLOB_NAME = 'blob-name'
+        NEW_NAME = 'new_name'
+        BLOB_PATH = '/b/%s/o/%s' % (SOURCE, BLOB_NAME)
+        NEW_BLOB_PATH = '/b/%s/o/%s' % (DEST, NEW_NAME)
+        COPY_PATH = '/b/%s/o/%s/copyTo/b/%s/o/%s' % (SOURCE, BLOB_NAME,
+                                                     DEST, NEW_NAME)
+
+        class _Blob(object):
+            name = BLOB_NAME
+            path = BLOB_PATH
+
+        connection = _Connection({}, {})
+        client = _Client(connection)
+        source = self._makeOne(client=client, name=SOURCE)
+        dest = self._makeOne(client=client, name=DEST)
+        blob = _Blob()
+        new_blob = source.copy_blob(blob, dest, NEW_NAME, client=client,
+                                    preserve_acl=False)
+        self.assertIs(new_blob.bucket, dest)
+        self.assertEqual(new_blob.name, NEW_NAME)
+        self.assertIsInstance(new_blob.acl, ObjectACL)
+        kw = connection._requested
+        self.assertEqual(len(kw), 2)
+        self.assertEqual(kw[0]['method'], 'POST')
+        self.assertEqual(kw[0]['path'], COPY_PATH)
+        self.assertEqual(kw[1]['method'], 'PATCH')
+        self.assertEqual(kw[1]['path'], NEW_BLOB_PATH)
+
     def test_copy_blobs_w_name(self):
         SOURCE = 'source'
         DEST = 'dest'