Repository: googleapis/google-cloud-python

Issue:
Logging: Can't retrieve the writerIdentity for a logging sink
In #4578 , support was added for creating a sink with a unique writerIdentity. It would appear that only the uniqueWriterIdentity boolean for logging.sinks.create was implemented; but retrieving the writerIdentity for an existing sink is not possible.

## API response (via gcloud)
```
$ gcloud logging sinks list --project xxxx --log-http
[...]
-- body start --
{
  "sinks": [
    {
      "name": "xxxx",
      "destination": "storage.googleapis.com/xxxx",
      "filter": "xxxx",
      "outputVersionFormt": "V2",
      "writerIdentity": "serviceAccount:xxxx@logging-xxxx.iam.gserviceaccount.com"
    }
  ]
}

-- body end --
```

## Simplified example code
```
from google.cloud import logging

logging_client = logging.Client(project_id)
sink = logging_client.sink(sink_name)
sink.reload()
```

## Resulting object
```
(Pdb) sink.
sink.__class__(         sink.__hash__(          sink.__setattr__(       sink.destination
sink.__delattr__(       sink.__init__(          sink.__sizeof__(        sink.exists(
sink.__dict__           sink.__le__(            sink.__str__(           sink.filter_
sink.__dir__(           sink.__lt__(            sink.__subclasshook__(  sink.from_api_repr(
sink.__doc__            sink.__module__         sink.__weakref__        sink.full_name
sink.__eq__(            sink.__ne__(            sink._client            sink.name
sink.__format__(        sink.__new__(           sink._require_client(   sink.path
sink.__ge__(            sink.__reduce__(        sink.client             sink.project
sink.__getattribute__(  sink.__reduce_ex__(     sink.create(            sink.reload(
sink.__gt__(            sink.__repr__(          sink.delete(            sink.update(

(Pdb) sink.__dict__
{'destination': 'storage.googleapis.com/logs-00006913-fake-customer', '_client': <google.cloud.logging.client.Client object at 0x7f1d2728e860>, 'name': 'asdf1234', 'filter_': '*'}
```




Discussion:

Resolving patch:
diff --git a/logging/google/cloud/logging/sink.py b/logging/google/cloud/logging/sink.py
--- a/logging/google/cloud/logging/sink.py
+++ b/logging/google/cloud/logging/sink.py
@@ -51,6 +51,7 @@ def __init__(self, name, filter_=None, destination=None, client=None,
         self.destination = destination
         self._client = client
         self._unique_writer_identity = unique_writer_identity
+        self._writer_identity = None
 
     @property
     def client(self):
@@ -72,6 +73,11 @@ def path(self):
         """URL path for the sink's APIs"""
         return '/%s' % (self.full_name)
 
+    @property
+    def writer_identity(self):
+        """Identity used for exports via the sink"""
+        return self._writer_identity
+
     @classmethod
     def from_api_repr(cls, resource, client):
         """Factory:  construct a sink given its API representation
@@ -92,7 +98,9 @@ def from_api_repr(cls, resource, client):
         sink_name = resource['name']
         destination = resource['destination']
         filter_ = resource.get('filter')
-        return cls(sink_name, filter_, destination, client=client)
+        instance = cls(sink_name, filter_, destination, client=client)
+        instance._writer_identity = resource.get('writerIdentity')
+        return instance
 
     def _require_client(self, client):
         """Check client or verify over-ride.
@@ -161,9 +169,10 @@ def reload(self, client=None):
                        ``client`` stored on the current sink.
         """
         client = self._require_client(client)
-        data = client.sinks_api.sink_get(self.project, self.name)
-        self.destination = data['destination']
-        self.filter_ = data.get('filter')
+        resource = client.sinks_api.sink_get(self.project, self.name)
+        self.destination = resource['destination']
+        self.filter_ = resource.get('filter')
+        self._writer_identity = resource.get('writerIdentity')
 
     def update(self, client=None):
         """API call:  update sink configuration via a PUT request


Test patch:
diff --git a/logging/tests/unit/test_sink.py b/logging/tests/unit/test_sink.py
--- a/logging/tests/unit/test_sink.py
+++ b/logging/tests/unit/test_sink.py
@@ -21,6 +21,7 @@ class TestSink(unittest.TestCase):
     SINK_NAME = 'sink-name'
     FILTER = 'logName:syslog AND severity>=INFO'
     DESTINATION_URI = 'faux.googleapis.com/destination'
+    WRITER_IDENTITY = 'serviceAccount:project-123@example.com'
 
     @staticmethod
     def _get_target_class():
@@ -67,25 +68,28 @@ def test_from_api_repr_minimal(self):
         klass = self._get_target_class()
         sink = klass.from_api_repr(RESOURCE, client=client)
         self.assertEqual(sink.name, self.SINK_NAME)
-        self.assertIsNone(sink.filter_)
         self.assertEqual(sink.destination, self.DESTINATION_URI)
+        self.assertIsNone(sink.filter_)
+        self.assertIsNone(sink.writer_identity)
         self.assertIs(sink._client, client)
         self.assertEqual(sink.project, self.PROJECT)
         self.assertEqual(sink.full_name, FULL)
 
-    def test_from_api_repr_w_description(self):
+    def test_from_api_repr_full(self):
         client = _Client(project=self.PROJECT)
         FULL = 'projects/%s/sinks/%s' % (self.PROJECT, self.SINK_NAME)
         RESOURCE = {
             'name': self.SINK_NAME,
-            'filter': self.FILTER,
             'destination': self.DESTINATION_URI,
+            'filter': self.FILTER,
+            'writerIdentity': self.WRITER_IDENTITY,
         }
         klass = self._get_target_class()
         sink = klass.from_api_repr(RESOURCE, client=client)
         self.assertEqual(sink.name, self.SINK_NAME)
         self.assertEqual(sink.filter_, self.FILTER)
         self.assertEqual(sink.destination, self.DESTINATION_URI)
+        self.assertEqual(sink.writer_identity, self.WRITER_IDENTITY)
         self.assertIs(sink._client, client)
         self.assertEqual(sink.project, self.PROJECT)
         self.assertEqual(sink.full_name, FULL)
@@ -177,6 +181,7 @@ def test_reload_w_bound_client(self):
 
         self.assertEqual(sink.destination, NEW_DESTINATION_URI)
         self.assertIsNone(sink.filter_)
+        self.assertIsNone(sink.writer_identity)
         self.assertEqual(api._sink_get_called_with,
                          (self.PROJECT, self.SINK_NAME))
 
@@ -187,6 +192,7 @@ def test_reload_w_alternate_client(self):
             'name': self.SINK_NAME,
             'filter': NEW_FILTER,
             'destination': NEW_DESTINATION_URI,
+            'writerIdentity': self.WRITER_IDENTITY,
         }
         client1 = _Client(project=self.PROJECT)
         client2 = _Client(project=self.PROJECT)
@@ -196,8 +202,9 @@ def test_reload_w_alternate_client(self):
 
         sink.reload(client=client2)
 
-        self.assertEqual(sink.filter_, NEW_FILTER)
         self.assertEqual(sink.destination, NEW_DESTINATION_URI)
+        self.assertEqual(sink.filter_, NEW_FILTER)
+        self.assertEqual(sink.writer_identity, self.WRITER_IDENTITY)
         self.assertEqual(api._sink_get_called_with,
                          (self.PROJECT, self.SINK_NAME))