1# Copyright 2020 Google LLC All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15
16import datetime
17import mock
18import unittest
19
20from ._testing import _make_credentials
21from google.cloud._helpers import UTC
22
23
24class TestBackup(unittest.TestCase):
25    PROJECT_ID = "project-id"
26    INSTANCE_ID = "instance-id"
27    INSTANCE_NAME = "projects/" + PROJECT_ID + "/instances/" + INSTANCE_ID
28    CLUSTER_ID = "cluster-id"
29    CLUSTER_NAME = INSTANCE_NAME + "/clusters/" + CLUSTER_ID
30    TABLE_ID = "table-id"
31    TABLE_NAME = INSTANCE_NAME + "/tables/" + TABLE_ID
32    BACKUP_ID = "backup-id"
33    BACKUP_NAME = CLUSTER_NAME + "/backups/" + BACKUP_ID
34
35    ALT_INSTANCE = "other-instance-id"
36    ALT_INSTANCE_NAME = "projects/" + PROJECT_ID + "/instances/" + ALT_INSTANCE
37    ALT_CLUSTER_NAME = ALT_INSTANCE_NAME + "/clusters/" + CLUSTER_ID
38    ALT_BACKUP_NAME = ALT_CLUSTER_NAME + "/backups/" + BACKUP_ID
39
40    @staticmethod
41    def _get_target_class():
42        from google.cloud.bigtable.backup import Backup
43
44        return Backup
45
46    @staticmethod
47    def _make_table_admin_client():
48        from google.cloud.bigtable_admin_v2 import BigtableTableAdminClient
49
50        return mock.create_autospec(BigtableTableAdminClient, instance=True)
51
52    def _make_one(self, *args, **kwargs):
53        return self._get_target_class()(*args, **kwargs)
54
55    def _make_timestamp(self):
56        return datetime.datetime.utcnow().replace(tzinfo=UTC)
57
58    def test_constructor_defaults(self):
59        instance = _Instance(self.INSTANCE_NAME)
60        backup = self._make_one(self.BACKUP_ID, instance)
61
62        self.assertEqual(backup.backup_id, self.BACKUP_ID)
63        self.assertIs(backup._instance, instance)
64        self.assertIsNone(backup._cluster)
65        self.assertIsNone(backup.table_id)
66        self.assertIsNone(backup._expire_time)
67
68        self.assertIsNone(backup._parent)
69        self.assertIsNone(backup._source_table)
70        self.assertIsNone(backup._start_time)
71        self.assertIsNone(backup._end_time)
72        self.assertIsNone(backup._size_bytes)
73        self.assertIsNone(backup._state)
74        self.assertIsNone(backup._encryption_info)
75
76    def test_constructor_non_defaults(self):
77        instance = _Instance(self.INSTANCE_NAME)
78        expire_time = self._make_timestamp()
79
80        backup = self._make_one(
81            self.BACKUP_ID,
82            instance,
83            cluster_id=self.CLUSTER_ID,
84            table_id=self.TABLE_ID,
85            expire_time=expire_time,
86            encryption_info="encryption_info",
87        )
88
89        self.assertEqual(backup.backup_id, self.BACKUP_ID)
90        self.assertIs(backup._instance, instance)
91        self.assertIs(backup._cluster, self.CLUSTER_ID)
92        self.assertEqual(backup.table_id, self.TABLE_ID)
93        self.assertEqual(backup._expire_time, expire_time)
94        self.assertEqual(backup._encryption_info, "encryption_info")
95
96        self.assertIsNone(backup._parent)
97        self.assertIsNone(backup._source_table)
98        self.assertIsNone(backup._start_time)
99        self.assertIsNone(backup._end_time)
100        self.assertIsNone(backup._size_bytes)
101        self.assertIsNone(backup._state)
102
103    def test_from_pb_project_mismatch(self):
104        from google.cloud.bigtable_admin_v2.types import table
105
106        alt_project_id = "alt-project-id"
107        client = _Client(project=alt_project_id)
108        instance = _Instance(self.INSTANCE_NAME, client)
109        backup_pb = table.Backup(name=self.BACKUP_NAME)
110        klasse = self._get_target_class()
111
112        with self.assertRaises(ValueError):
113            klasse.from_pb(backup_pb, instance)
114
115    def test_from_pb_instance_mismatch(self):
116        from google.cloud.bigtable_admin_v2.types import table
117
118        alt_instance = "/projects/%s/instances/alt-instance" % self.PROJECT_ID
119        client = _Client()
120        instance = _Instance(alt_instance, client)
121        backup_pb = table.Backup(name=self.BACKUP_NAME)
122        klasse = self._get_target_class()
123
124        with self.assertRaises(ValueError):
125            klasse.from_pb(backup_pb, instance)
126
127    def test_from_pb_bad_name(self):
128        from google.cloud.bigtable_admin_v2.types import table
129
130        client = _Client()
131        instance = _Instance(self.INSTANCE_NAME, client)
132        backup_pb = table.Backup(name="invalid_name")
133        klasse = self._get_target_class()
134
135        with self.assertRaises(ValueError):
136            klasse.from_pb(backup_pb, instance)
137
138    def test_from_pb_success(self):
139        from google.cloud.bigtable.encryption_info import EncryptionInfo
140        from google.cloud.bigtable.error import Status
141        from google.cloud.bigtable_admin_v2.types import table
142        from google.cloud._helpers import _datetime_to_pb_timestamp
143        from google.rpc.code_pb2 import Code
144
145        client = _Client()
146        instance = _Instance(self.INSTANCE_NAME, client)
147        timestamp = _datetime_to_pb_timestamp(self._make_timestamp())
148        size_bytes = 1234
149        state = table.Backup.State.READY
150        GOOGLE_DEFAULT_ENCRYPTION = (
151            table.EncryptionInfo.EncryptionType.GOOGLE_DEFAULT_ENCRYPTION
152        )
153        backup_pb = table.Backup(
154            name=self.BACKUP_NAME,
155            source_table=self.TABLE_NAME,
156            expire_time=timestamp,
157            start_time=timestamp,
158            end_time=timestamp,
159            size_bytes=size_bytes,
160            state=state,
161            encryption_info=table.EncryptionInfo(
162                encryption_type=GOOGLE_DEFAULT_ENCRYPTION,
163                encryption_status=_StatusPB(Code.OK, "Status OK"),
164                kms_key_version="2",
165            ),
166        )
167        klasse = self._get_target_class()
168
169        backup = klasse.from_pb(backup_pb, instance)
170
171        self.assertTrue(isinstance(backup, klasse))
172        self.assertEqual(backup._instance, instance)
173        self.assertEqual(backup.backup_id, self.BACKUP_ID)
174        self.assertEqual(backup.cluster, self.CLUSTER_ID)
175        self.assertEqual(backup.table_id, self.TABLE_ID)
176        self.assertEqual(backup._expire_time, timestamp)
177        self.assertEqual(backup.start_time, timestamp)
178        self.assertEqual(backup.end_time, timestamp)
179        self.assertEqual(backup._size_bytes, size_bytes)
180        self.assertEqual(backup._state, state)
181        self.assertEqual(
182            backup.encryption_info,
183            EncryptionInfo(
184                encryption_type=GOOGLE_DEFAULT_ENCRYPTION,
185                encryption_status=Status(_StatusPB(Code.OK, "Status OK")),
186                kms_key_version="2",
187            ),
188        )
189
190    def test_property_name(self):
191        from google.cloud.bigtable.client import Client
192        from google.cloud.bigtable_admin_v2.services.bigtable_instance_admin import (
193            BigtableInstanceAdminClient,
194        )
195
196        api = mock.create_autospec(BigtableInstanceAdminClient)
197        credentials = _make_credentials()
198        client = Client(project=self.PROJECT_ID, credentials=credentials, admin=True)
199        client._table_admin_client = api
200        instance = _Instance(self.INSTANCE_NAME, client)
201
202        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
203        self.assertEqual(backup.name, self.BACKUP_NAME)
204
205    def test_property_cluster(self):
206        backup = self._make_one(
207            self.BACKUP_ID, _Instance(self.INSTANCE_NAME), cluster_id=self.CLUSTER_ID
208        )
209        self.assertEqual(backup.cluster, self.CLUSTER_ID)
210
211    def test_property_cluster_setter(self):
212        backup = self._make_one(self.BACKUP_ID, _Instance(self.INSTANCE_NAME))
213        backup.cluster = self.CLUSTER_ID
214        self.assertEqual(backup.cluster, self.CLUSTER_ID)
215
216    def test_property_parent_none(self):
217        backup = self._make_one(self.BACKUP_ID, _Instance(self.INSTANCE_NAME),)
218        self.assertIsNone(backup.parent)
219
220    def test_property_parent_w_cluster(self):
221        from google.cloud.bigtable.client import Client
222        from google.cloud.bigtable_admin_v2.services.bigtable_instance_admin import (
223            BigtableInstanceAdminClient,
224        )
225
226        api = mock.create_autospec(BigtableInstanceAdminClient)
227        credentials = _make_credentials()
228        client = Client(project=self.PROJECT_ID, credentials=credentials, admin=True)
229        client._table_admin_client = api
230        instance = _Instance(self.INSTANCE_NAME, client)
231
232        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
233        self.assertEqual(backup._cluster, self.CLUSTER_ID)
234        self.assertEqual(backup.parent, self.CLUSTER_NAME)
235
236    def test_property_source_table_none(self):
237        from google.cloud.bigtable.client import Client
238        from google.cloud.bigtable_admin_v2.services.bigtable_instance_admin import (
239            BigtableInstanceAdminClient,
240        )
241
242        api = mock.create_autospec(BigtableInstanceAdminClient)
243        credentials = _make_credentials()
244        client = Client(project=self.PROJECT_ID, credentials=credentials, admin=True)
245        client._table_admin_client = api
246        instance = _Instance(self.INSTANCE_NAME, client)
247
248        backup = self._make_one(self.BACKUP_ID, instance)
249        self.assertIsNone(backup.source_table)
250
251    def test_property_source_table_valid(self):
252        from google.cloud.bigtable.client import Client
253        from google.cloud.bigtable_admin_v2.services.bigtable_instance_admin import (
254            BigtableInstanceAdminClient,
255        )
256
257        api = mock.create_autospec(BigtableInstanceAdminClient)
258        credentials = _make_credentials()
259        client = Client(project=self.PROJECT_ID, credentials=credentials, admin=True)
260        client._table_admin_client = api
261        instance = _Instance(self.INSTANCE_NAME, client)
262
263        backup = self._make_one(self.BACKUP_ID, instance, table_id=self.TABLE_ID)
264        self.assertEqual(backup.source_table, self.TABLE_NAME)
265
266    def test_property_expire_time(self):
267        instance = _Instance(self.INSTANCE_NAME)
268        expire_time = self._make_timestamp()
269        backup = self._make_one(self.BACKUP_ID, instance, expire_time=expire_time)
270        self.assertEqual(backup.expire_time, expire_time)
271
272    def test_property_expire_time_setter(self):
273        instance = _Instance(self.INSTANCE_NAME)
274        expire_time = self._make_timestamp()
275        backup = self._make_one(self.BACKUP_ID, instance)
276        backup.expire_time = expire_time
277        self.assertEqual(backup.expire_time, expire_time)
278
279    def test_property_start_time(self):
280        instance = _Instance(self.INSTANCE_NAME)
281        backup = self._make_one(self.BACKUP_ID, instance)
282        expected = backup._start_time = self._make_timestamp()
283        self.assertEqual(backup.start_time, expected)
284
285    def test_property_end_time(self):
286        instance = _Instance(self.INSTANCE_NAME)
287        backup = self._make_one(self.BACKUP_ID, instance)
288        expected = backup._end_time = self._make_timestamp()
289        self.assertEqual(backup.end_time, expected)
290
291    def test_property_size(self):
292        instance = _Instance(self.INSTANCE_NAME)
293        backup = self._make_one(self.BACKUP_ID, instance)
294        expected = backup._size_bytes = 10
295        self.assertEqual(backup.size_bytes, expected)
296
297    def test_property_state(self):
298        from google.cloud.bigtable_admin_v2.types import table
299
300        instance = _Instance(self.INSTANCE_NAME)
301        backup = self._make_one(self.BACKUP_ID, instance)
302        expected = backup._state = table.Backup.State.READY
303        self.assertEqual(backup.state, expected)
304
305    def test___eq__(self):
306        instance = object()
307        backup1 = self._make_one(self.BACKUP_ID, instance)
308        backup2 = self._make_one(self.BACKUP_ID, instance)
309        self.assertTrue(backup1 == backup2)
310
311    def test___eq__different_types(self):
312        instance = object()
313        backup1 = self._make_one(self.BACKUP_ID, instance)
314        backup2 = object()
315        self.assertFalse(backup1 == backup2)
316
317    def test___ne__same_value(self):
318        instance = object()
319        backup1 = self._make_one(self.BACKUP_ID, instance)
320        backup2 = self._make_one(self.BACKUP_ID, instance)
321        self.assertFalse(backup1 != backup2)
322
323    def test___ne__(self):
324        backup1 = self._make_one("backup_1", "instance1")
325        backup2 = self._make_one("backup_2", "instance2")
326        self.assertTrue(backup1 != backup2)
327
328    def test_create_grpc_error(self):
329        from google.api_core.exceptions import GoogleAPICallError
330        from google.api_core.exceptions import Unknown
331        from google.cloud._helpers import _datetime_to_pb_timestamp
332        from google.cloud.bigtable_admin_v2.types import table
333
334        client = _Client()
335        api = client.table_admin_client = self._make_table_admin_client()
336        api.create_backup.side_effect = Unknown("testing")
337
338        timestamp = self._make_timestamp()
339        backup = self._make_one(
340            self.BACKUP_ID,
341            _Instance(self.INSTANCE_NAME, client=client),
342            table_id=self.TABLE_ID,
343            expire_time=timestamp,
344        )
345
346        backup_pb = table.Backup(
347            source_table=self.TABLE_NAME,
348            expire_time=_datetime_to_pb_timestamp(timestamp),
349        )
350
351        with self.assertRaises(GoogleAPICallError):
352            backup.create(self.CLUSTER_ID)
353
354        api.create_backup.assert_called_once_with(
355            request={
356                "parent": self.CLUSTER_NAME,
357                "backup_id": self.BACKUP_ID,
358                "backup": backup_pb,
359            }
360        )
361
362    def test_create_already_exists(self):
363        from google.cloud._helpers import _datetime_to_pb_timestamp
364        from google.cloud.bigtable_admin_v2.types import table
365        from google.cloud.exceptions import Conflict
366
367        client = _Client()
368        api = client.table_admin_client = self._make_table_admin_client()
369        api.create_backup.side_effect = Conflict("testing")
370
371        timestamp = self._make_timestamp()
372        backup = self._make_one(
373            self.BACKUP_ID,
374            _Instance(self.INSTANCE_NAME, client=client),
375            table_id=self.TABLE_ID,
376            expire_time=timestamp,
377        )
378
379        backup_pb = table.Backup(
380            source_table=self.TABLE_NAME,
381            expire_time=_datetime_to_pb_timestamp(timestamp),
382        )
383
384        with self.assertRaises(Conflict):
385            backup.create(self.CLUSTER_ID)
386
387        api.create_backup.assert_called_once_with(
388            request={
389                "parent": self.CLUSTER_NAME,
390                "backup_id": self.BACKUP_ID,
391                "backup": backup_pb,
392            }
393        )
394
395    def test_create_instance_not_found(self):
396        from google.cloud._helpers import _datetime_to_pb_timestamp
397        from google.cloud.bigtable_admin_v2.types import table
398        from google.cloud.exceptions import NotFound
399
400        client = _Client()
401        api = client.table_admin_client = self._make_table_admin_client()
402        api.create_backup.side_effect = NotFound("testing")
403
404        timestamp = self._make_timestamp()
405        backup = self._make_one(
406            self.BACKUP_ID,
407            _Instance(self.INSTANCE_NAME, client=client),
408            table_id=self.TABLE_ID,
409            expire_time=timestamp,
410        )
411
412        backup_pb = table.Backup(
413            source_table=self.TABLE_NAME,
414            expire_time=_datetime_to_pb_timestamp(timestamp),
415        )
416
417        with self.assertRaises(NotFound):
418            backup.create(self.CLUSTER_ID)
419
420        api.create_backup.assert_called_once_with(
421            request={
422                "parent": self.CLUSTER_NAME,
423                "backup_id": self.BACKUP_ID,
424                "backup": backup_pb,
425            }
426        )
427
428    def test_create_cluster_not_set(self):
429        backup = self._make_one(
430            self.BACKUP_ID,
431            _Instance(self.INSTANCE_NAME),
432            table_id=self.TABLE_ID,
433            expire_time=self._make_timestamp(),
434        )
435
436        with self.assertRaises(ValueError):
437            backup.create()
438
439    def test_create_table_not_set(self):
440        backup = self._make_one(
441            self.BACKUP_ID,
442            _Instance(self.INSTANCE_NAME),
443            expire_time=self._make_timestamp(),
444        )
445
446        with self.assertRaises(ValueError):
447            backup.create(self.CLUSTER_ID)
448
449    def test_create_expire_time_not_set(self):
450        backup = self._make_one(
451            self.BACKUP_ID, _Instance(self.INSTANCE_NAME), table_id=self.TABLE_ID,
452        )
453
454        with self.assertRaises(ValueError):
455            backup.create(self.CLUSTER_ID)
456
457    def test_create_success(self):
458        from google.cloud._helpers import _datetime_to_pb_timestamp
459        from google.cloud.bigtable_admin_v2.types import table
460        from google.cloud.bigtable import Client
461
462        op_future = object()
463        credentials = _make_credentials()
464        client = Client(project=self.PROJECT_ID, credentials=credentials, admin=True)
465        api = client._table_admin_client = self._make_table_admin_client()
466        api.create_backup.return_value = op_future
467
468        timestamp = self._make_timestamp()
469        backup = self._make_one(
470            self.BACKUP_ID,
471            _Instance(self.INSTANCE_NAME, client=client),
472            table_id=self.TABLE_ID,
473            expire_time=timestamp,
474        )
475
476        backup_pb = table.Backup(
477            source_table=self.TABLE_NAME,
478            expire_time=_datetime_to_pb_timestamp(timestamp),
479        )
480
481        future = backup.create(self.CLUSTER_ID)
482        self.assertEqual(backup._cluster, self.CLUSTER_ID)
483        self.assertIs(future, op_future)
484
485        api.create_backup.assert_called_once_with(
486            request={
487                "parent": self.CLUSTER_NAME,
488                "backup_id": self.BACKUP_ID,
489                "backup": backup_pb,
490            }
491        )
492
493    def test_exists_grpc_error(self):
494        from google.api_core.exceptions import Unknown
495
496        client = _Client()
497        api = client.table_admin_client = self._make_table_admin_client()
498        api.get_backup.side_effect = Unknown("testing")
499
500        instance = _Instance(self.INSTANCE_NAME, client=client)
501        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
502
503        with self.assertRaises(Unknown):
504            backup.exists()
505
506        request = {"name": self.BACKUP_NAME}
507        api.get_backup.assert_called_once_with(request)
508
509    def test_exists_not_found(self):
510        from google.api_core.exceptions import NotFound
511
512        client = _Client()
513        api = client.table_admin_client = self._make_table_admin_client()
514        api.get_backup.side_effect = NotFound("testing")
515
516        instance = _Instance(self.INSTANCE_NAME, client=client)
517        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
518
519        self.assertFalse(backup.exists())
520
521        api.get_backup.assert_called_once_with(request={"name": self.BACKUP_NAME})
522
523    def test_get(self):
524        from google.cloud.bigtable_admin_v2.types import table
525        from google.cloud._helpers import _datetime_to_pb_timestamp
526
527        timestamp = _datetime_to_pb_timestamp(self._make_timestamp())
528        state = table.Backup.State.READY
529
530        client = _Client()
531        backup_pb = table.Backup(
532            name=self.BACKUP_NAME,
533            source_table=self.TABLE_NAME,
534            expire_time=timestamp,
535            start_time=timestamp,
536            end_time=timestamp,
537            size_bytes=0,
538            state=state,
539        )
540        api = client.table_admin_client = self._make_table_admin_client()
541        api.get_backup.return_value = backup_pb
542
543        instance = _Instance(self.INSTANCE_NAME, client=client)
544        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
545
546        self.assertEqual(backup.get(), backup_pb)
547
548    def test_reload(self):
549        from google.cloud.bigtable_admin_v2.types import table
550        from google.cloud._helpers import _datetime_to_pb_timestamp
551
552        timestamp = _datetime_to_pb_timestamp(self._make_timestamp())
553        state = table.Backup.State.READY
554
555        client = _Client()
556        backup_pb = table.Backup(
557            name=self.BACKUP_NAME,
558            source_table=self.TABLE_NAME,
559            expire_time=timestamp,
560            start_time=timestamp,
561            end_time=timestamp,
562            size_bytes=0,
563            state=state,
564        )
565        api = client.table_admin_client = self._make_table_admin_client()
566        api.get_backup.return_value = backup_pb
567
568        instance = _Instance(self.INSTANCE_NAME, client=client)
569        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
570
571        backup.reload()
572        self.assertEqual(backup._source_table, self.TABLE_NAME)
573        self.assertEqual(backup._expire_time, timestamp)
574        self.assertEqual(backup._start_time, timestamp)
575        self.assertEqual(backup._end_time, timestamp)
576        self.assertEqual(backup._size_bytes, 0)
577        self.assertEqual(backup._state, state)
578
579    def test_exists_success(self):
580        from google.cloud.bigtable_admin_v2.types import table
581
582        client = _Client()
583        backup_pb = table.Backup(name=self.BACKUP_NAME)
584        api = client.table_admin_client = self._make_table_admin_client()
585        api.get_backup.return_value = backup_pb
586
587        instance = _Instance(self.INSTANCE_NAME, client=client)
588        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
589
590        self.assertTrue(backup.exists())
591
592        api.get_backup.assert_called_once_with(request={"name": self.BACKUP_NAME})
593
594    def test_delete_grpc_error(self):
595        from google.api_core.exceptions import Unknown
596
597        client = _Client()
598        api = client.table_admin_client = self._make_table_admin_client()
599        api.delete_backup.side_effect = Unknown("testing")
600        instance = _Instance(self.INSTANCE_NAME, client=client)
601        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
602
603        with self.assertRaises(Unknown):
604            backup.delete()
605
606        api.delete_backup.assert_called_once_with(request={"name": self.BACKUP_NAME})
607
608    def test_delete_not_found(self):
609        from google.api_core.exceptions import NotFound
610
611        client = _Client()
612        api = client.table_admin_client = self._make_table_admin_client()
613        api.delete_backup.side_effect = NotFound("testing")
614        instance = _Instance(self.INSTANCE_NAME, client=client)
615        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
616
617        with self.assertRaises(NotFound):
618            backup.delete()
619
620        api.delete_backup.assert_called_once_with(request={"name": self.BACKUP_NAME})
621
622    def test_delete_success(self):
623        from google.protobuf.empty_pb2 import Empty
624
625        client = _Client()
626        api = client.table_admin_client = self._make_table_admin_client()
627        api.delete_backup.return_value = Empty()
628        instance = _Instance(self.INSTANCE_NAME, client=client)
629        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
630
631        backup.delete()
632
633        api.delete_backup.assert_called_once_with(request={"name": self.BACKUP_NAME})
634
635    def test_update_expire_time_grpc_error(self):
636        from google.api_core.exceptions import Unknown
637        from google.cloud._helpers import _datetime_to_pb_timestamp
638        from google.cloud.bigtable_admin_v2.types import table
639        from google.protobuf import field_mask_pb2
640
641        client = _Client()
642        api = client.table_admin_client = self._make_table_admin_client()
643        api.update_backup.side_effect = Unknown("testing")
644        instance = _Instance(self.INSTANCE_NAME, client=client)
645        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
646        expire_time = self._make_timestamp()
647
648        with self.assertRaises(Unknown):
649            backup.update_expire_time(expire_time)
650
651        backup_update = table.Backup(
652            name=self.BACKUP_NAME, expire_time=_datetime_to_pb_timestamp(expire_time),
653        )
654        update_mask = field_mask_pb2.FieldMask(paths=["expire_time"])
655        api.update_backup.assert_called_once_with(
656            request={"backup": backup_update, "update_mask": update_mask}
657        )
658
659    def test_update_expire_time_not_found(self):
660        from google.api_core.exceptions import NotFound
661        from google.cloud._helpers import _datetime_to_pb_timestamp
662        from google.cloud.bigtable_admin_v2.types import table
663        from google.protobuf import field_mask_pb2
664
665        client = _Client()
666        api = client.table_admin_client = self._make_table_admin_client()
667        api.update_backup.side_effect = NotFound("testing")
668        instance = _Instance(self.INSTANCE_NAME, client=client)
669        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
670        expire_time = self._make_timestamp()
671
672        with self.assertRaises(NotFound):
673            backup.update_expire_time(expire_time)
674
675        backup_update = table.Backup(
676            name=self.BACKUP_NAME, expire_time=_datetime_to_pb_timestamp(expire_time),
677        )
678        update_mask = field_mask_pb2.FieldMask(paths=["expire_time"])
679        api.update_backup.assert_called_once_with(
680            request={"backup": backup_update, "update_mask": update_mask}
681        )
682
683    def test_update_expire_time_success(self):
684        from google.cloud._helpers import _datetime_to_pb_timestamp
685        from google.cloud.bigtable_admin_v2.types import table
686        from google.protobuf import field_mask_pb2
687
688        client = _Client()
689        api = client.table_admin_client = self._make_table_admin_client()
690        api.update_backup.return_type = table.Backup(name=self.BACKUP_NAME)
691        instance = _Instance(self.INSTANCE_NAME, client=client)
692        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
693        expire_time = self._make_timestamp()
694
695        backup.update_expire_time(expire_time)
696
697        backup_update = table.Backup(
698            name=self.BACKUP_NAME, expire_time=_datetime_to_pb_timestamp(expire_time),
699        )
700        update_mask = field_mask_pb2.FieldMask(paths=["expire_time"])
701        api.update_backup.assert_called_once_with(
702            request={"backup": backup_update, "update_mask": update_mask}
703        )
704
705    def test_restore_grpc_error(self):
706        from google.api_core.exceptions import GoogleAPICallError
707        from google.api_core.exceptions import Unknown
708
709        client = _Client()
710        api = client.table_admin_client = self._make_table_admin_client()
711        api.restore_table.side_effect = Unknown("testing")
712
713        timestamp = self._make_timestamp()
714        backup = self._make_one(
715            self.BACKUP_ID,
716            _Instance(self.INSTANCE_NAME, client=client),
717            cluster_id=self.CLUSTER_ID,
718            table_id=self.TABLE_NAME,
719            expire_time=timestamp,
720        )
721
722        with self.assertRaises(GoogleAPICallError):
723            backup.restore(self.TABLE_ID)
724
725        api.restore_table.assert_called_once_with(
726            request={
727                "parent": self.INSTANCE_NAME,
728                "table_id": self.TABLE_ID,
729                "backup": self.BACKUP_NAME,
730            }
731        )
732
733    def test_restore_cluster_not_set(self):
734        client = _Client()
735        client.table_admin_client = self._make_table_admin_client()
736        backup = self._make_one(
737            self.BACKUP_ID,
738            _Instance(self.INSTANCE_NAME, client=client),
739            table_id=self.TABLE_ID,
740            expire_time=self._make_timestamp(),
741        )
742
743        with self.assertRaises(ValueError):
744            backup.restore(self.TABLE_ID)
745
746    def _restore_helper(self, instance_id=None, instance_name=None):
747        op_future = object()
748        client = _Client()
749        api = client.table_admin_client = self._make_table_admin_client()
750        api.restore_table.return_value = op_future
751
752        timestamp = self._make_timestamp()
753        backup = self._make_one(
754            self.BACKUP_ID,
755            _Instance(self.INSTANCE_NAME, client=client),
756            cluster_id=self.CLUSTER_ID,
757            table_id=self.TABLE_NAME,
758            expire_time=timestamp,
759        )
760
761        future = backup.restore(self.TABLE_ID, instance_id)
762        self.assertEqual(backup._cluster, self.CLUSTER_ID)
763        self.assertIs(future, op_future)
764
765        api.restore_table.assert_called_once_with(
766            request={
767                "parent": instance_name or self.INSTANCE_NAME,
768                "table_id": self.TABLE_ID,
769                "backup": self.BACKUP_NAME,
770            }
771        )
772        api.restore_table.reset_mock()
773
774    def test_restore_default(self):
775        self._restore_helper()
776
777    def test_restore_to_another_instance(self):
778        self._restore_helper(self.ALT_INSTANCE, self.ALT_INSTANCE_NAME)
779
780    def test_get_iam_policy(self):
781        from google.cloud.bigtable.client import Client
782        from google.cloud.bigtable_admin_v2.services.bigtable_table_admin import (
783            BigtableTableAdminClient,
784        )
785        from google.iam.v1 import policy_pb2
786        from google.cloud.bigtable.policy import BIGTABLE_ADMIN_ROLE
787
788        credentials = _make_credentials()
789        client = Client(project=self.PROJECT_ID, credentials=credentials, admin=True)
790
791        instance = client.instance(instance_id=self.INSTANCE_ID)
792        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
793
794        version = 1
795        etag = b"etag_v1"
796        members = ["serviceAccount:service_acc1@test.com", "user:user1@test.com"]
797        bindings = [{"role": BIGTABLE_ADMIN_ROLE, "members": members}]
798        iam_policy = policy_pb2.Policy(version=version, etag=etag, bindings=bindings)
799
800        table_api = mock.create_autospec(BigtableTableAdminClient)
801        client._table_admin_client = table_api
802        table_api.get_iam_policy.return_value = iam_policy
803
804        result = backup.get_iam_policy()
805
806        table_api.get_iam_policy.assert_called_once_with(
807            request={"resource": backup.name}
808        )
809        self.assertEqual(result.version, version)
810        self.assertEqual(result.etag, etag)
811
812        admins = result.bigtable_admins
813        self.assertEqual(len(admins), len(members))
814        for found, expected in zip(sorted(admins), sorted(members)):
815            self.assertEqual(found, expected)
816
817    def test_set_iam_policy(self):
818        from google.cloud.bigtable.client import Client
819        from google.cloud.bigtable_admin_v2.services.bigtable_table_admin import (
820            BigtableTableAdminClient,
821        )
822        from google.iam.v1 import policy_pb2
823        from google.cloud.bigtable.policy import Policy
824        from google.cloud.bigtable.policy import BIGTABLE_ADMIN_ROLE
825
826        credentials = _make_credentials()
827        client = Client(project=self.PROJECT_ID, credentials=credentials, admin=True)
828
829        instance = client.instance(instance_id=self.INSTANCE_ID)
830        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
831
832        version = 1
833        etag = b"etag_v1"
834        members = ["serviceAccount:service_acc1@test.com", "user:user1@test.com"]
835        bindings = [{"role": BIGTABLE_ADMIN_ROLE, "members": sorted(members)}]
836        iam_policy_pb = policy_pb2.Policy(version=version, etag=etag, bindings=bindings)
837
838        table_api = mock.create_autospec(BigtableTableAdminClient)
839        client._table_admin_client = table_api
840        table_api.set_iam_policy.return_value = iam_policy_pb
841
842        iam_policy = Policy(etag=etag, version=version)
843        iam_policy[BIGTABLE_ADMIN_ROLE] = [
844            Policy.user("user1@test.com"),
845            Policy.service_account("service_acc1@test.com"),
846        ]
847
848        result = backup.set_iam_policy(iam_policy)
849
850        table_api.set_iam_policy.assert_called_once_with(
851            request={"resource": backup.name, "policy": iam_policy_pb}
852        )
853        self.assertEqual(result.version, version)
854        self.assertEqual(result.etag, etag)
855
856        admins = result.bigtable_admins
857        self.assertEqual(len(admins), len(members))
858        for found, expected in zip(sorted(admins), sorted(members)):
859            self.assertEqual(found, expected)
860
861    def test_test_iam_permissions(self):
862        from google.cloud.bigtable.client import Client
863        from google.cloud.bigtable_admin_v2.services.bigtable_table_admin import (
864            BigtableTableAdminClient,
865        )
866        from google.iam.v1 import iam_policy_pb2
867
868        credentials = _make_credentials()
869        client = Client(project=self.PROJECT_ID, credentials=credentials, admin=True)
870
871        instance = client.instance(instance_id=self.INSTANCE_ID)
872        backup = self._make_one(self.BACKUP_ID, instance, cluster_id=self.CLUSTER_ID)
873
874        permissions = ["bigtable.backups.create", "bigtable.backups.list"]
875
876        response = iam_policy_pb2.TestIamPermissionsResponse(permissions=permissions)
877
878        table_api = mock.create_autospec(BigtableTableAdminClient)
879        table_api.test_iam_permissions.return_value = response
880        client._table_admin_client = table_api
881
882        result = backup.test_iam_permissions(permissions)
883
884        self.assertEqual(result, permissions)
885        table_api.test_iam_permissions.assert_called_once_with(
886            request={"resource": backup.name, "permissions": permissions}
887        )
888
889
890class _Client(object):
891    def __init__(self, project=TestBackup.PROJECT_ID):
892        self.project = project
893        self.project_name = "projects/" + self.project
894
895
896class _Instance(object):
897    def __init__(self, name, client=None):
898        self.name = name
899        self.instance_id = name.rsplit("/", 1)[1]
900        self._client = client
901
902
903def _StatusPB(code, message):
904    from google.rpc import status_pb2
905
906    status_pb = status_pb2.Status()
907    status_pb.code = code
908    status_pb.message = message
909
910    return status_pb
911