Skip to content

Commit 6d534d6

Browse files
authored
Support passing 'uniqueWriterIdentity' to 'Sink.update'. (#4708)
Also, capture server-set read-only properties returned from 'sinks.update', such as 'writerIdentity'.
1 parent ccbd95c commit 6d534d6

File tree

6 files changed

+141
-38
lines changed

6 files changed

+141
-38
lines changed

google/cloud/logging/_gax.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,8 @@ def sink_get(self, project, sink_name):
265265
# so `MessageToDict`` can safely be used.
266266
return MessageToDict(sink_pb)
267267

268-
def sink_update(self, project, sink_name, filter_, destination):
268+
def sink_update(self, project, sink_name, filter_, destination,
269+
unique_writer_identity=False):
269270
"""API call: update a sink resource.
270271
271272
:type project: str
@@ -282,15 +283,24 @@ def sink_update(self, project, sink_name, filter_, destination):
282283
:param destination: destination URI for the entries exported by
283284
the sink.
284285
286+
:type unique_writer_identity: bool
287+
:param unique_writer_identity: (Optional) determines the kind of
288+
IAM identity returned as
289+
writer_identity in the new sink.
290+
285291
:rtype: dict
286-
:returns: The sink object returned from the API (converted from a
292+
:returns: The sink resource returned from the API (converted from a
287293
protobuf to a dictionary).
288294
"""
289295
options = None
290296
path = 'projects/%s/sinks/%s' % (project, sink_name)
291297
sink_pb = LogSink(name=path, filter=filter_, destination=destination)
292298
try:
293-
sink_pb = self._gax_api.update_sink(path, sink_pb, options=options)
299+
sink_pb = self._gax_api.update_sink(
300+
path,
301+
sink_pb,
302+
unique_writer_identity=unique_writer_identity,
303+
options=options)
294304
except GaxError as exc:
295305
if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
296306
raise NotFound(path)

google/cloud/logging/_http.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,8 @@ def sink_get(self, project, sink_name):
290290
target = '/projects/%s/sinks/%s' % (project, sink_name)
291291
return self.api_request(method='GET', path=target)
292292

293-
def sink_update(self, project, sink_name, filter_, destination):
293+
def sink_update(self, project, sink_name, filter_, destination,
294+
unique_writer_identity=False):
294295
"""API call: update a sink resource.
295296
296297
See
@@ -310,6 +311,11 @@ def sink_update(self, project, sink_name, filter_, destination):
310311
:param destination: destination URI for the entries exported by
311312
the sink.
312313
314+
:type unique_writer_identity: bool
315+
:param unique_writer_identity: (Optional) determines the kind of
316+
IAM identity returned as
317+
writer_identity in the new sink.
318+
313319
:rtype: dict
314320
:returns: The returned (updated) resource.
315321
"""
@@ -319,7 +325,9 @@ def sink_update(self, project, sink_name, filter_, destination):
319325
'filter': filter_,
320326
'destination': destination,
321327
}
322-
return self.api_request(method='PUT', path=target, data=data)
328+
query_params = {'uniqueWriterIdentity': unique_writer_identity}
329+
return self.api_request(
330+
method='PUT', path=target, query_params=query_params, data=data)
323331

324332
def sink_delete(self, project, sink_name):
325333
"""API call: delete a sink resource.

google/cloud/logging/sink.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def reload(self, client=None):
175175
resource = client.sinks_api.sink_get(self.project, self.name)
176176
self._update_from_api_repr(resource)
177177

178-
def update(self, client=None):
178+
def update(self, client=None, unique_writer_identity=False):
179179
"""API call: update sink configuration via a PUT request
180180
181181
See
@@ -185,10 +185,18 @@ def update(self, client=None):
185185
``NoneType``
186186
:param client: the client to use. If not passed, falls back to the
187187
``client`` stored on the current sink.
188+
189+
:type unique_writer_identity: bool
190+
:param unique_writer_identity: (Optional) determines the kind of
191+
IAM identity returned as
192+
writer_identity in the new sink.
188193
"""
189194
client = self._require_client(client)
190-
client.sinks_api.sink_update(
191-
self.project, self.name, self.filter_, self.destination)
195+
resource = client.sinks_api.sink_update(
196+
self.project, self.name, self.filter_, self.destination,
197+
unique_writer_identity=unique_writer_identity,
198+
)
199+
self._update_from_api_repr(resource)
192200

193201
def delete(self, client=None):
194202
"""API call: delete a sink via a DELETE request

tests/unit/test__gax.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,12 @@ def test_sink_create_ok(self):
744744
from google.cloud.proto.logging.v2.logging_config_pb2 import LogSink
745745

746746
gax_api = _GAXSinksAPI()
747+
gax_api._create_sink_response = LogSink(
748+
name=self.SINK_NAME,
749+
destination=self.DESTINATION_URI,
750+
filter=self.FILTER,
751+
writer_identity=self.SINK_WRITER_IDENTITY,
752+
)
747753
api = self._make_one(gax_api, None)
748754

749755
returned = api.sink_create(
@@ -824,6 +830,7 @@ def test_sink_update_error(self):
824830

825831
def test_sink_update_miss(self):
826832
from google.cloud.exceptions import NotFound
833+
from google.cloud.proto.logging.v2.logging_config_pb2 import LogSink
827834

828835
gax_api = _GAXSinksAPI()
829836
api = self._make_one(gax_api, None)
@@ -833,25 +840,50 @@ def test_sink_update_miss(self):
833840
self.PROJECT, self.SINK_NAME, self.FILTER,
834841
self.DESTINATION_URI)
835842

843+
sink_name, sink, unique_writer_identity, options = (
844+
gax_api._update_sink_called_with)
845+
self.assertEqual(sink_name, self.SINK_PATH)
846+
self.assertIsInstance(sink, LogSink)
847+
self.assertEqual(sink.name, self.SINK_PATH)
848+
self.assertEqual(sink.filter, self.FILTER)
849+
self.assertEqual(sink.destination, self.DESTINATION_URI)
850+
self.assertFalse(unique_writer_identity)
851+
self.assertIsNone(options)
852+
836853
def test_sink_update_hit(self):
837854
from google.cloud.proto.logging.v2.logging_config_pb2 import LogSink
838855

839-
response = LogSink(name=self.SINK_NAME,
840-
destination=self.DESTINATION_URI,
841-
filter=self.FILTER)
856+
response = LogSink(
857+
name=self.SINK_NAME,
858+
destination=self.DESTINATION_URI,
859+
filter=self.FILTER,
860+
writer_identity=Test_SinksAPI.SINK_WRITER_IDENTITY,
861+
)
842862
gax_api = _GAXSinksAPI(_update_sink_response=response)
843863
api = self._make_one(gax_api, None)
844864

845-
api.sink_update(
846-
self.PROJECT, self.SINK_NAME, self.FILTER, self.DESTINATION_URI)
865+
returned = api.sink_update(
866+
self.PROJECT,
867+
self.SINK_NAME,
868+
self.FILTER,
869+
self.DESTINATION_URI,
870+
unique_writer_identity=True)
847871

848-
sink_name, sink, options = (
872+
self.assertEqual(returned, {
873+
'name': self.SINK_NAME,
874+
'filter': self.FILTER,
875+
'destination': self.DESTINATION_URI,
876+
'writerIdentity': self.SINK_WRITER_IDENTITY,
877+
})
878+
879+
sink_name, sink, unique_writer_identity, options = (
849880
gax_api._update_sink_called_with)
850881
self.assertEqual(sink_name, self.SINK_PATH)
851882
self.assertIsInstance(sink, LogSink)
852883
self.assertEqual(sink.name, self.SINK_PATH)
853884
self.assertEqual(sink.filter, self.FILTER)
854885
self.assertEqual(sink.destination, self.DESTINATION_URI)
886+
self.assertTrue(unique_writer_identity)
855887
self.assertIsNone(options)
856888

857889
def test_sink_delete_error(self):
@@ -1497,12 +1529,7 @@ def create_sink(self, parent, sink, unique_writer_identity, options):
14971529
raise GaxError('error')
14981530
if self._create_sink_conflict:
14991531
raise GaxError('conflict', self._make_grpc_failed_precondition())
1500-
return LogSink(
1501-
name=sink.name,
1502-
destination=sink.destination,
1503-
filter=sink.filter,
1504-
writer_identity=Test_SinksAPI.SINK_WRITER_IDENTITY,
1505-
)
1532+
return self._create_sink_response
15061533

15071534
def get_sink(self, sink_name, options):
15081535
from google.gax.errors import GaxError
@@ -1515,10 +1542,11 @@ def get_sink(self, sink_name, options):
15151542
except AttributeError:
15161543
raise GaxError('notfound', self._make_grpc_not_found())
15171544

1518-
def update_sink(self, sink_name, sink, options=None):
1545+
def update_sink(self, sink_name, sink, unique_writer_identity, options):
15191546
from google.gax.errors import GaxError
15201547

1521-
self._update_sink_called_with = sink_name, sink, options
1548+
self._update_sink_called_with = (
1549+
sink_name, sink, unique_writer_identity, options)
15221550
if self._random_gax_error:
15231551
raise GaxError('error')
15241552
try:

tests/unit/test__http.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ def test_sink_get_hit(self):
527527
def test_sink_update_miss(self):
528528
from google.cloud.exceptions import NotFound
529529

530-
SENT = {
530+
sent = {
531531
'name': self.SINK_NAME,
532532
'filter': self.FILTER,
533533
'destination': self.DESTINATION_URI,
@@ -541,28 +541,43 @@ def test_sink_update_miss(self):
541541
self.PROJECT, self.SINK_NAME, self.FILTER,
542542
self.DESTINATION_URI)
543543

544-
self.assertEqual(conn._called_with['method'], 'PUT')
545544
path = '/projects/%s/sinks/%s' % (self.PROJECT, self.SINK_NAME)
546-
self.assertEqual(conn._called_with['path'], path)
547-
self.assertEqual(conn._called_with['data'], SENT)
545+
expected = {
546+
'method': 'PUT',
547+
'path': path,
548+
'data': sent,
549+
'query_params': {'uniqueWriterIdentity': False},
550+
}
551+
self.assertEqual(conn._called_with, expected)
548552

549553
def test_sink_update_hit(self):
550-
SENT = {
554+
sent = {
551555
'name': self.SINK_NAME,
552556
'filter': self.FILTER,
553557
'destination': self.DESTINATION_URI,
554558
}
555-
conn = _Connection({})
559+
after_update = sent.copy()
560+
after_update['writerIdentity'] = self.WRITER_IDENTITY
561+
conn = _Connection(after_update)
556562
client = _Client(conn)
557563
api = self._make_one(client)
558564

559-
api.sink_update(
560-
self.PROJECT, self.SINK_NAME, self.FILTER, self.DESTINATION_URI)
565+
returned = api.sink_update(
566+
self.PROJECT,
567+
self.SINK_NAME,
568+
self.FILTER,
569+
self.DESTINATION_URI,
570+
unique_writer_identity=True)
561571

562-
self.assertEqual(conn._called_with['method'], 'PUT')
572+
self.assertEqual(returned, after_update)
563573
path = '/projects/%s/sinks/%s' % (self.PROJECT, self.SINK_NAME)
564-
self.assertEqual(conn._called_with['path'], path)
565-
self.assertEqual(conn._called_with['data'], SENT)
574+
expected = {
575+
'method': 'PUT',
576+
'path': path,
577+
'data': sent,
578+
'query_params': {'uniqueWriterIdentity': True},
579+
}
580+
self.assertEqual(conn._called_with, expected)
566581

567582
def test_sink_delete_miss(self):
568583
from google.cloud.exceptions import NotFound

tests/unit/test_sink.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,29 +231,61 @@ def test_reload_w_alternate_client(self):
231231
def test_update_w_bound_client(self):
232232
client = _Client(project=self.PROJECT)
233233
api = client.sinks_api = _DummySinksAPI()
234+
api._sink_update_response = {
235+
'name': self.SINK_NAME,
236+
'filter': self.FILTER,
237+
'destination': self.DESTINATION_URI,
238+
'writerIdentity': self.WRITER_IDENTITY,
239+
}
234240
sink = self._make_one(self.SINK_NAME, self.FILTER,
235241
self.DESTINATION_URI,
236242
client=client)
237243

238244
sink.update()
239245

246+
self.assertEqual(sink.name, self.SINK_NAME)
247+
self.assertEqual(sink.filter_, self.FILTER)
248+
self.assertEqual(sink.destination, self.DESTINATION_URI)
249+
self.assertEqual(sink.writer_identity, self.WRITER_IDENTITY)
240250
self.assertEqual(
241251
api._sink_update_called_with,
242-
(self.PROJECT, self.SINK_NAME, self.FILTER, self.DESTINATION_URI))
252+
(
253+
self.PROJECT,
254+
self.SINK_NAME,
255+
self.FILTER,
256+
self.DESTINATION_URI,
257+
False,
258+
))
243259

244260
def test_update_w_alternate_client(self):
245261
client1 = _Client(project=self.PROJECT)
246262
client2 = _Client(project=self.PROJECT)
247263
api = client2.sinks_api = _DummySinksAPI()
264+
api._sink_update_response = {
265+
'name': self.SINK_NAME,
266+
'filter': self.FILTER,
267+
'destination': self.DESTINATION_URI,
268+
'writerIdentity': self.WRITER_IDENTITY,
269+
}
248270
sink = self._make_one(self.SINK_NAME, self.FILTER,
249271
self.DESTINATION_URI,
250272
client=client1)
251273

252-
sink.update(client=client2)
274+
sink.update(client=client2, unique_writer_identity=True)
253275

276+
self.assertEqual(sink.name, self.SINK_NAME)
277+
self.assertEqual(sink.filter_, self.FILTER)
278+
self.assertEqual(sink.destination, self.DESTINATION_URI)
279+
self.assertEqual(sink.writer_identity, self.WRITER_IDENTITY)
254280
self.assertEqual(
255281
api._sink_update_called_with,
256-
(self.PROJECT, self.SINK_NAME, self.FILTER, self.DESTINATION_URI))
282+
(
283+
self.PROJECT,
284+
self.SINK_NAME,
285+
self.FILTER,
286+
self.DESTINATION_URI,
287+
True,
288+
))
257289

258290
def test_delete_w_bound_client(self):
259291
client = _Client(project=self.PROJECT)
@@ -304,9 +336,11 @@ def sink_get(self, project, sink_name):
304336
except AttributeError:
305337
raise NotFound('miss')
306338

307-
def sink_update(self, project, sink_name, filter_, destination):
339+
def sink_update(self, project, sink_name, filter_, destination,
340+
unique_writer_identity=False):
308341
self._sink_update_called_with = (
309-
project, sink_name, filter_, destination)
342+
project, sink_name, filter_, destination, unique_writer_identity)
343+
return self._sink_update_response
310344

311345
def sink_delete(self, project, sink_name):
312346
self._sink_delete_called_with = (project, sink_name)

0 commit comments

Comments
 (0)