Skip to content

Commit

Permalink
feat: add support for instance labels (#193)
Browse files Browse the repository at this point in the history
* feat: add support for instance labels

* docs: update parameter docstrings

* docs: add missing literal end-string

* style: fix lint errors

* docs: update labels type

* docs: revert emulator_host docstring

Co-authored-by: larkee <larkee@users.noreply.github.com>
  • Loading branch information
larkee and larkee committed Dec 15, 2020
1 parent c7b3b9e commit ed462b5
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 4 deletions.
5 changes: 5 additions & 0 deletions google/cloud/spanner_v1/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ def instance(
configuration_name=None,
display_name=None,
node_count=DEFAULT_NODE_COUNT,
labels=None,
):
"""Factory to create a instance associated with this client.
Expand All @@ -313,6 +314,9 @@ def instance(
:param node_count: (Optional) The number of nodes in the instance's
cluster; used to set up the instance's cluster.
:type labels: dict (str -> str) or None
:param labels: (Optional) User-assigned labels for this instance.
:rtype: :class:`~google.cloud.spanner_v1.instance.Instance`
:returns: an instance owned by this client.
"""
Expand All @@ -323,6 +327,7 @@ def instance(
node_count,
display_name,
self._emulator_host,
labels,
)

def list_instances(self, filter_="", page_size=None):
Expand Down
14 changes: 12 additions & 2 deletions google/cloud/spanner_v1/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ class Instance(object):
Cloud Console UI. (Must be between 4 and 30
characters.) If this value is not set in the
constructor, will fall back to the instance ID.
:type labels: dict (str -> str) or None
:param labels: (Optional) User-assigned labels for this instance.
"""

def __init__(
Expand All @@ -109,13 +112,17 @@ def __init__(
node_count=DEFAULT_NODE_COUNT,
display_name=None,
emulator_host=None,
labels=None,
):
self.instance_id = instance_id
self._client = client
self.configuration_name = configuration_name
self.node_count = node_count
self.display_name = display_name or instance_id
self.emulator_host = emulator_host
if labels is None:
labels = {}
self.labels = labels

def _update_from_pb(self, instance_pb):
"""Refresh self from the server-provided protobuf.
Expand All @@ -127,6 +134,7 @@ def _update_from_pb(self, instance_pb):
self.display_name = instance_pb.display_name
self.configuration_name = instance_pb.config
self.node_count = instance_pb.node_count
self.labels = instance_pb.labels

@classmethod
def from_pb(cls, instance_pb, client):
Expand Down Expand Up @@ -242,6 +250,7 @@ def create(self):
config=self.configuration_name,
display_name=self.display_name,
node_count=self.node_count,
labels=self.labels,
)
metadata = _metadata_with_prefix(self.name)

Expand Down Expand Up @@ -296,7 +305,7 @@ def update(self):
.. note::
Updates the ``display_name`` and ``node_count``. To change those
Updates the ``display_name``, ``node_count`` and ``labels``. To change those
values before updating, set them via
.. code:: python
Expand All @@ -316,8 +325,9 @@ def update(self):
config=self.configuration_name,
display_name=self.display_name,
node_count=self.node_count,
labels=self.labels,
)
field_mask = FieldMask(paths=["config", "display_name", "node_count"])
field_mask = FieldMask(paths=["config", "display_name", "node_count", "labels"])
metadata = _metadata_with_prefix(self.name)

future = api.update_instance(
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class TestClient(unittest.TestCase):
INSTANCE_NAME = "%s/instances/%s" % (PATH, INSTANCE_ID)
DISPLAY_NAME = "display-name"
NODE_COUNT = 5
LABELS = {"test": "true"}
TIMEOUT_SECONDS = 80

def _get_target_class(self):
Expand Down Expand Up @@ -518,6 +519,7 @@ def test_instance_factory_defaults(self):
self.assertIsNone(instance.configuration_name)
self.assertEqual(instance.display_name, self.INSTANCE_ID)
self.assertEqual(instance.node_count, DEFAULT_NODE_COUNT)
self.assertEqual(instance.labels, {})
self.assertIs(instance._client, client)

def test_instance_factory_explicit(self):
Expand All @@ -531,13 +533,15 @@ def test_instance_factory_explicit(self):
self.CONFIGURATION_NAME,
display_name=self.DISPLAY_NAME,
node_count=self.NODE_COUNT,
labels=self.LABELS,
)

self.assertIsInstance(instance, Instance)
self.assertEqual(instance.instance_id, self.INSTANCE_ID)
self.assertEqual(instance.configuration_name, self.CONFIGURATION_NAME)
self.assertEqual(instance.display_name, self.DISPLAY_NAME)
self.assertEqual(instance.node_count, self.NODE_COUNT)
self.assertEqual(instance.labels, self.LABELS)
self.assertIs(instance._client, client)

def test_list_instances(self):
Expand Down
28 changes: 26 additions & 2 deletions tests/unit/test_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class TestInstance(unittest.TestCase):
TIMEOUT_SECONDS = 1
DATABASE_ID = "database_id"
DATABASE_NAME = "%s/databases/%s" % (INSTANCE_NAME, DATABASE_ID)
LABELS = {"test": "true"}

def _getTargetClass(self):
from google.cloud.spanner_v1.instance import Instance
Expand All @@ -57,6 +58,7 @@ def test_constructor_defaults(self):
self.assertIs(instance.configuration_name, None)
self.assertEqual(instance.node_count, DEFAULT_NODE_COUNT)
self.assertEqual(instance.display_name, self.INSTANCE_ID)
self.assertEqual(instance.labels, {})

def test_constructor_non_default(self):
DISPLAY_NAME = "display_name"
Expand All @@ -68,12 +70,14 @@ def test_constructor_non_default(self):
configuration_name=self.CONFIG_NAME,
node_count=self.NODE_COUNT,
display_name=DISPLAY_NAME,
labels=self.LABELS,
)
self.assertEqual(instance.instance_id, self.INSTANCE_ID)
self.assertIs(instance._client, client)
self.assertEqual(instance.configuration_name, self.CONFIG_NAME)
self.assertEqual(instance.node_count, self.NODE_COUNT)
self.assertEqual(instance.display_name, DISPLAY_NAME)
self.assertEqual(instance.labels, self.LABELS)

def test_copy(self):
DISPLAY_NAME = "display_name"
Expand Down Expand Up @@ -145,6 +149,7 @@ def test_from_pb_success(self):
name=self.INSTANCE_NAME,
config=self.CONFIG_NAME,
display_name=self.INSTANCE_ID,
labels=self.LABELS,
)

klass = self._getTargetClass()
Expand All @@ -153,13 +158,22 @@ def test_from_pb_success(self):
self.assertEqual(instance._client, client)
self.assertEqual(instance.instance_id, self.INSTANCE_ID)
self.assertEqual(instance.configuration_name, self.CONFIG_NAME)
self.assertEqual(instance.labels, self.LABELS)

def test_name_property(self):
client = _Client(project=self.PROJECT)

instance = self._make_one(self.INSTANCE_ID, client, self.CONFIG_NAME)
self.assertEqual(instance.name, self.INSTANCE_NAME)

def test_labels_property(self):
client = _Client(project=self.PROJECT)

instance = self._make_one(
self.INSTANCE_ID, client, self.CONFIG_NAME, labels=self.LABELS
)
self.assertEqual(instance.labels, self.LABELS)

def test___eq__(self):
client = object()
instance1 = self._make_one(self.INSTANCE_ID, client, self.CONFIG_NAME)
Expand Down Expand Up @@ -231,6 +245,7 @@ def test_create_success(self):
configuration_name=self.CONFIG_NAME,
display_name=self.DISPLAY_NAME,
node_count=self.NODE_COUNT,
labels=self.LABELS,
)

future = instance.create()
Expand All @@ -244,6 +259,7 @@ def test_create_success(self):
self.assertEqual(instance.config, self.CONFIG_NAME)
self.assertEqual(instance.display_name, self.DISPLAY_NAME)
self.assertEqual(instance.node_count, self.NODE_COUNT)
self.assertEqual(instance.labels, self.LABELS)
self.assertEqual(metadata, [("google-cloud-resource-prefix", instance.name)])

def test_exists_instance_grpc_error(self):
Expand Down Expand Up @@ -327,6 +343,7 @@ def test_reload_success(self):
config=self.CONFIG_NAME,
display_name=self.DISPLAY_NAME,
node_count=self.NODE_COUNT,
labels=self.LABELS,
)
api = client.instance_admin_api = _FauxInstanceAdminAPI(
_get_instance_response=instance_pb
Expand All @@ -338,6 +355,7 @@ def test_reload_success(self):
self.assertEqual(instance.configuration_name, self.CONFIG_NAME)
self.assertEqual(instance.node_count, self.NODE_COUNT)
self.assertEqual(instance.display_name, self.DISPLAY_NAME)
self.assertEqual(instance.labels, self.LABELS)

name, metadata = api._got_instance
self.assertEqual(name, self.INSTANCE_NAME)
Expand Down Expand Up @@ -371,7 +389,9 @@ def test_update_not_found(self):
instance.update()

instance, field_mask, metadata = api._updated_instance
self.assertEqual(field_mask.paths, ["config", "display_name", "node_count"])
self.assertEqual(
field_mask.paths, ["config", "display_name", "node_count", "labels"]
)
self.assertEqual(instance.name, self.INSTANCE_NAME)
self.assertEqual(instance.config, self.CONFIG_NAME)
self.assertEqual(instance.display_name, self.INSTANCE_ID)
Expand All @@ -390,18 +410,22 @@ def test_update_success(self):
configuration_name=self.CONFIG_NAME,
node_count=self.NODE_COUNT,
display_name=self.DISPLAY_NAME,
labels=self.LABELS,
)

future = instance.update()

self.assertIs(future, op_future)

instance, field_mask, metadata = api._updated_instance
self.assertEqual(field_mask.paths, ["config", "display_name", "node_count"])
self.assertEqual(
field_mask.paths, ["config", "display_name", "node_count", "labels"]
)
self.assertEqual(instance.name, self.INSTANCE_NAME)
self.assertEqual(instance.config, self.CONFIG_NAME)
self.assertEqual(instance.display_name, self.DISPLAY_NAME)
self.assertEqual(instance.node_count, self.NODE_COUNT)
self.assertEqual(instance.labels, self.LABELS)
self.assertEqual(metadata, [("google-cloud-resource-prefix", instance.name)])

def test_delete_grpc_error(self):
Expand Down

0 comments on commit ed462b5

Please sign in to comment.