Skip to content

Commit

Permalink
Implementing cluster_standlone.Cluster.operation_finished().
Browse files Browse the repository at this point in the history
This is a replacement for the previous polling mechanism
used in Cluster.create(). It puts the onus on the user to
check if the operation finished.
  • Loading branch information
dhermes committed Jul 27, 2015
1 parent 860f833 commit aa7dfb8
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 15 deletions.
5 changes: 3 additions & 2 deletions gcloud_bigtable/_grpc_mocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ def _grpc_call_helper(self, call_method, method_name, request_obj,
self.assertEqual(mock_make_stub.method_calls, method_calls)

def _grpc_client_test_helper(self, method_name, result_method, request_pb,
response_pb, expected_result, project_id):
response_pb, expected_result, project_id,
stub_factory=None):
from gcloud_bigtable._testing import _MockWithAttachedMethods
from gcloud_bigtable._testing import _Monkey
from gcloud_bigtable.client import Client
Expand All @@ -187,7 +188,7 @@ def _grpc_client_test_helper(self, method_name, result_method, request_pb,
])
factory_args = (
scoped_creds,
getattr(self._MUT, self._STUB_FACTORY_NAME),
stub_factory or getattr(self._MUT, self._STUB_FACTORY_NAME),
self._STUB_HOST,
self._STUB_PORT,
)
Expand Down
37 changes: 37 additions & 0 deletions gcloud_bigtable/cluster_standalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from gcloud_bigtable._generated import (
bigtable_cluster_service_messages_pb2 as messages_pb2)
from gcloud_bigtable._generated import bigtable_cluster_service_pb2
from gcloud_bigtable._generated import operations_pb2
from gcloud_bigtable._helpers import _CLUSTER_CREATE_METADATA
from gcloud_bigtable._helpers import _parse_pb_any_to_native
from gcloud_bigtable._helpers import _pb_timestamp_to_datetime
Expand All @@ -43,6 +44,7 @@

CLUSTER_STUB_FACTORY = (bigtable_cluster_service_pb2.
early_adopter_create_BigtableClusterService_stub)
OPERATIONS_STUB_FACTORY = operations_pb2.early_adopter_create_Operations_stub


def _prepare_create_request(cluster):
Expand Down Expand Up @@ -132,6 +134,9 @@ def __init__(self, zone, cluster_id, client,
self.display_name = display_name or cluster_id
self.serve_nodes = serve_nodes
self._client = client
self._operation_type = None
self._operation_id = None
self._operation_begin = None

def _update_from_pb(self, cluster_pb):
self.display_name = _require_pb_property(
Expand Down Expand Up @@ -236,6 +241,38 @@ def reload(self, timeout_seconds=TIMEOUT_SECONDS):
# cluster ID on the response match the request.
self._update_from_pb(cluster_pb)

def operation_finished(self, timeout_seconds=TIMEOUT_SECONDS):
"""Check if the current operation has finished.
:type timeout_seconds: integer
:param timeout_seconds: Number of seconds for request time-out.
If not passed, defaults to ``TIMEOUT_SECONDS``.
:rtype: boolean
:returns: A boolean indicating if the current operation has completed.
:raises: :class:`ValueError` if there is no current operation set.
"""
if self._operation_id is None:
raise ValueError('There is no current operation.')

operation_name = ('operations/' + self.name +
'/operations/%d' % (self._operation_id,))
request_pb = operations_pb2.GetOperationRequest(name=operation_name)
stub = make_stub(self.client._credentials, OPERATIONS_STUB_FACTORY,
CLUSTER_ADMIN_HOST, CLUSTER_ADMIN_PORT)
with stub:
response = stub.GetOperation.async(request_pb, timeout_seconds)
# We expact a `._generated.operations_pb2.Operation`.
operation_pb = response.result()

if operation_pb.done:
self._operation_type = None
self._operation_id = None
self._operation_begin = None
return True
else:
return False

def create(self, timeout_seconds=TIMEOUT_SECONDS):
"""Create this cluster.
Expand Down
83 changes: 70 additions & 13 deletions gcloud_bigtable/test_cluster_standalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,32 +295,61 @@ def result_method(client):
request_pb, response_pb, expected_result,
PROJECT_ID)

def test_delete(self):
from gcloud_bigtable._generated import (
bigtable_cluster_service_messages_pb2 as messages_pb2)
from gcloud_bigtable._generated import empty_pb2
def test_operation_finished_without_operation(self):
cluster = self._makeOne(ZONE, CLUSTER_ID, None)
self.assertEqual(cluster._operation_type, None)
with self.assertRaises(ValueError):
cluster.operation_finished()

TEST_CASE = self
def _operation_finished_helper(self, done):
from gcloud_bigtable._generated import operations_pb2
from gcloud_bigtable import cluster_standalone as MUT

# Create request_pb
cluster_name = ('projects/' + PROJECT_ID + '/zones/' + ZONE +
'/clusters/' + CLUSTER_ID)
request_pb = messages_pb2.DeleteClusterRequest(name=cluster_name)
op_id = 789
op_name = ('operations/projects/' + PROJECT_ID + '/zones/' +
ZONE + '/clusters/' + CLUSTER_ID +
'/operations/%d' % (op_id,))
request_pb = operations_pb2.GetOperationRequest(name=op_name)

# Create response_pb
response_pb = empty_pb2.Empty()
response_pb = operations_pb2.Operation(done=done)

# Create expected_result.
expected_result = None # delete() has no return value.
expected_result = done

# We must create the cluster with the client passed in.
TEST_CASE = self
CLUSTER_CREATED = []
op_begin = object()
op_type = object()

def result_method(client):
cluster = TEST_CASE._makeOne(ZONE, CLUSTER_ID, client)
return cluster.delete()
cluster._operation_id = op_id
cluster._operation_begin = op_begin
cluster._operation_type = op_type
CLUSTER_CREATED.append(cluster)
return cluster.operation_finished()

self._grpc_client_test_helper('DeleteCluster', result_method,
self._grpc_client_test_helper('GetOperation', result_method,
request_pb, response_pb, expected_result,
PROJECT_ID)
PROJECT_ID,
stub_factory=MUT.OPERATIONS_STUB_FACTORY)
if done:
self.assertEqual(CLUSTER_CREATED[0]._operation_type, None)
self.assertEqual(CLUSTER_CREATED[0]._operation_id, None)
self.assertEqual(CLUSTER_CREATED[0]._operation_begin, None)
else:
self.assertEqual(CLUSTER_CREATED[0]._operation_type, op_type)
self.assertEqual(CLUSTER_CREATED[0]._operation_id, op_id)
self.assertEqual(CLUSTER_CREATED[0]._operation_begin, op_begin)

def test_operation_finished(self):
self._operation_finished_helper(done=True)

def test_operation_finished_not_done(self):
self._operation_finished_helper(done=False)

def test_create(self):
from gcloud_bigtable._testing import _MockCalled
Expand Down Expand Up @@ -358,7 +387,35 @@ def result_method(client):
expected_result, PROJECT_ID)

self.assertEqual(len(CLUSTER_CREATED), 1)
self.assertEqual(CLUSTER_CREATED[0]._operation_type, 'create')
self.assertEqual(CLUSTER_CREATED[0]._operation_id, op_id)
self.assertTrue(CLUSTER_CREATED[0]._operation_begin is op_begin)
mock_prepare_create_request.check_called(self, [(CLUSTER_CREATED[0],)])
mock_process_create_response.check_called(self, [(response_pb,)])

def test_delete(self):
from gcloud_bigtable._generated import (
bigtable_cluster_service_messages_pb2 as messages_pb2)
from gcloud_bigtable._generated import empty_pb2

TEST_CASE = self

# Create request_pb
cluster_name = ('projects/' + PROJECT_ID + '/zones/' + ZONE +
'/clusters/' + CLUSTER_ID)
request_pb = messages_pb2.DeleteClusterRequest(name=cluster_name)

# Create response_pb
response_pb = empty_pb2.Empty()

# Create expected_result.
expected_result = None # delete() has no return value.

# We must create the cluster with the client passed in.
def result_method(client):
cluster = TEST_CASE._makeOne(ZONE, CLUSTER_ID, client)
return cluster.delete()

self._grpc_client_test_helper('DeleteCluster', result_method,
request_pb, response_pb, expected_result,
PROJECT_ID)

0 comments on commit aa7dfb8

Please sign in to comment.