def testWaitingForNonHomogenousOperations(self):
        def CreateRegionalOperation(status):
            return self.messages.Operation(
                name='operation-0',
                operationType='insert',
                selfLink=
                ('https://compute.googleapis.com/compute/v1/projects/my-project/'
                 'regions/us-central2/operations/operation-0'),
                status=status,
                targetLink=
                ('https://compute.googleapis.com/compute/v1/projects/my-project/'
                 'regions/us-central2/address-0'),
                region=
                ('https://compute.googleapis.com/compute/v1/projects/my-project/'
                 'regions/us-central2'))

        self.RegisterCalls(
            Call(requests=[(self.compute.regionOperations, 'Wait',
                            self.messages.ComputeRegionOperationsWaitRequest(
                                operation='operation-0',
                                project='my-project',
                                region='us-central2')),
                           (self.compute.zoneOperations, 'Wait',
                            self.messages.ComputeZoneOperationsWaitRequest(
                                operation='operation-0',
                                project='my-project',
                                zone='us-central2-a'))],
                 responses=[
                     CreateRegionalOperation(DONE_V1),
                     self.CreateOperations([DONE_V1])[0]
                 ]),
            Call(requests=[(self.compute.addresses, 'Get',
                            self.messages.ComputeAddressesGetRequest(
                                address='address-0',
                                project='my-project',
                                region='us-central2')),
                           (self.compute.instances, 'Get',
                            self.messages.ComputeInstancesGetRequest(
                                instance='instance-0',
                                project='my-project',
                                zone='us-central2-a'))],
                 responses=[
                     self.messages.Address(name='address-0'),
                     self.messages.Instance(name='instance-0')
                 ]))

        self.assertEqual(
            self.Wait(operations_data=[
                waiters.OperationData(CreateRegionalOperation(PENDING_V1),
                                      self.compute.regionOperations,
                                      self.compute.addresses,
                                      project='my-project'),
                self.CreateOperationsData([PENDING_V1])[0]
            ]), [
                self.messages.Address(name='address-0'),
                self.messages.Instance(name='instance-0')
            ])
        self.AssertSleeps()
        self.assertEqual(self.errors, [])
        self.assertEqual(self.warnings, [])
Beispiel #2
0
    def testWithProgressTracker(self):
        operations = [
            self.messages.Operation(targetLink='my-instance', zone='my-zone')
        ]
        resources = [self.messages.Instance(name='my-instance')]

        self.batch_helper.side_effect = iter([
            (operations, []),
        ])
        self.wait_for_operations.side_effect = iter([
            resources,
        ])

        requests = [
            (self.compute_v1.instances, 'Insert',
             self.messages.ComputeInstancesInsertRequest(
                 instance=self.messages.Instance(name='my-instance'),
                 project='my-project',
                 zone='my-zone')),
        ]

        errors = []
        mock_progress_tracker = mock.MagicMock()
        responses = request_helper.MakeRequests(
            requests=requests,
            http=self.mock_http,
            batch_url='https://www.googleapis.com/batch/compute',
            errors=errors,
            progress_tracker=mock_progress_tracker)

        self.assertEqual(list(responses), resources)
        self.assertFalse(errors)
        self.batch_helper.assert_called_once_with(
            requests=requests,
            http=self.mock_http,
            batch_url='https://www.googleapis.com/batch/compute')
        operations_data = []
        for operation in operations:
            operations_data.append(
                waiters.OperationData(operation,
                                      self.compute_v1.zoneOperations,
                                      self.compute_v1.instances,
                                      project='my-project'))
        self.wait_for_operations.assert_called_once_with(
            operations_data=operations_data,
            http=self.mock_http,
            batch_url='https://www.googleapis.com/batch/compute',
            progress_tracker=mock_progress_tracker,
            warnings=[],
            errors=[],
            log_result=True,
            timeout=None)
def _CreateOperationData(compute,
                         messages,
                         status,
                         operation_id=None,
                         operation_type=None):
    operation = _CreateOperation(messages, status, operation_id,
                                 operation_type)

    operation_data = waiters.OperationData(operation,
                                           compute.zoneOperations,
                                           compute.instances,
                                           project='my-project')
    return operation_data
Beispiel #4
0
    def testWithSingleRegionMutation(self):
        operations = [
            self.messages.Operation(targetLink='my-address',
                                    region='my-region'),
        ]
        resources = [self.messages.Address(name='my-address')]

        self.batch_helper.side_effect = iter([
            (operations, []),
        ])
        self.wait_for_operations.side_effect = iter([
            resources,
        ])

        requests = [
            (self.compute_v1.addresses, 'Insert',
             self.messages.ComputeAddressesInsertRequest(
                 address=self.messages.Address(name='my-address'),
                 project='my-project',
                 region='my-region')),
        ]

        errors = []
        responses = request_helper.MakeRequests(
            requests=requests,
            http=self.mock_http,
            batch_url='https://www.googleapis.com/batch/compute',
            errors=errors)

        self.assertEqual(list(responses), resources)
        self.assertFalse(errors)
        self.batch_helper.assert_called_once_with(
            requests=requests,
            http=self.mock_http,
            batch_url='https://www.googleapis.com/batch/compute')
        operations_data = []
        for operation in operations:
            operations_data.append(
                waiters.OperationData(operation,
                                      self.compute_v1.regionOperations,
                                      self.compute_v1.addresses,
                                      project='my-project'))
        self.wait_for_operations.assert_called_once_with(
            operations_data=operations_data,
            http=self.mock_http,
            batch_url='https://www.googleapis.com/batch/compute',
            progress_tracker=None,
            warnings=[],
            errors=[],
            log_result=True,
            timeout=None)
    def CreateOperationsData(self,
                             statuses,
                             ids=None,
                             operation_type=None,
                             followup_overrides=None):
        operations = self.CreateOperations(statuses, ids, operation_type)

        if not followup_overrides:
            followup_overrides = [None for _ in statuses]

        operations_data = []
        for operation, followup_override in zip(operations,
                                                followup_overrides):
            operations_data.append(
                waiters.OperationData(operation,
                                      self.compute.zoneOperations,
                                      self.compute.instances,
                                      project='my-project',
                                      followup_override=followup_override))
        return operations_data
def MakeRequests(requests, http, batch_url, errors, progress_tracker=None):
    """Makes one or more requests to the API.

  Each request can be either a synchronous API call or an asynchronous
  one. For synchronous calls (e.g., get and list), the result from the
  server is yielded immediately. For asynchronous calls (e.g., calls
  that return operations like insert), this function waits until the
  operation reaches the DONE state and fetches the corresponding
  object and yields that object (nothing is yielded for deletions).

  Currently, a heterogenous set of synchronous calls can be made
  (e.g., get request to fetch a disk and instance), however, the
  asynchronous requests must be homogenous (e.g., they must all be the
  same verb on the same collection). In the future, heterogenous
  asynchronous requests will be supported. For now, it is up to the
  client to ensure that the asynchronous requests are
  homogenous. Synchronous and asynchronous requests can be mixed.

  Args:
    requests: A list of requests to make. Each element must be a 3-element
      tuple where the first element is the service, the second element is
      the string name of the method on the service, and the last element
      is a protocol buffer representing the request.
    http: An httplib2.Http-like object.
    batch_url: The handler for making batch requests.
    errors: A list for capturing errors. If any response contains an error,
      it is added to this list.
    progress_tracker: progress tracker to be ticked while waiting for operations
                      to finish.

  Yields:
    A response for each request. For deletion requests, no corresponding
    responses are returned.
  """
    if _RequestsAreListRequests(requests):
        for item in _List(requests=requests,
                          http=http,
                          batch_url=batch_url,
                          errors=errors):
            yield item
        return

    responses, new_errors = batch_helper.MakeRequests(requests=requests,
                                                      http=http,
                                                      batch_url=batch_url)
    errors.extend(new_errors)

    operation_service = None
    resource_service = None
    project = None

    # Collects all operation objects in a list so they can be waited on
    # and yields all non-operation objects since non-operation responses
    # cannot be waited on.
    operations_data = []

    for request, response in zip(requests, responses):
        if response is None:
            continue

        service, _, request_body = request
        if (isinstance(response, service.client.MESSAGES_MODULE.Operation)
                and service.__class__.__name__
                not in ('GlobalOperationsService', 'RegionOperationsService',
                        'ZoneOperationsService',
                        'GlobalAccountsOperationsService')):

            resource_service = service
            project = request_body.project

            if response.zone:
                operation_service = service.client.zoneOperations
            elif response.region:
                operation_service = service.client.regionOperations
            else:
                operation_service = service.client.globalOperations

            operations_data.append(
                waiters.OperationData(response, project, operation_service,
                                      resource_service))

        else:
            yield response

    if operations_data:
        warnings = []
        for response in waiters.WaitForOperations(
                operations_data=operations_data,
                http=http,
                batch_url=batch_url,
                warnings=warnings,
                progress_tracker=progress_tracker,
                errors=errors):
            yield response

        if warnings:
            log.warning(
                utils.ConstructList('Some requests generated warnings:',
                                    warnings))
Beispiel #7
0
    def testWithAsynchronousAndSynchronousRequests(self):
        operations = [
            self.messages.Operation(targetLink='my-instance-3',
                                    zone='my-zone'),
            self.messages.Operation(targetLink='my-instance-4',
                                    zone='my-zone'),
        ]
        batch_helper_sync_response = [
            self.messages.Instance(name='my-instance-1'),
            self.messages.Instance(name='my-instance-2'),
        ]
        wait_response = [
            self.messages.Instance(name='my-instance-3'),
            self.messages.Instance(name='my-instance-4'),
        ]

        self.batch_helper.side_effect = iter([
            (batch_helper_sync_response + operations, []),
        ])
        self.wait_for_operations.side_effect = iter([
            wait_response,
        ])

        requests = [
            (self.compute_v1.instances, 'Get',
             self.messages.ComputeInstancesGetRequest(instance='my-instance-1',
                                                      project='my-project',
                                                      zone='my-zone')),
            (self.compute_v1.instances, 'Insert',
             self.messages.ComputeInstancesInsertRequest(
                 instance=self.messages.Instance(name='my-instance-3'),
                 project='my-project',
                 zone='my-zone')),
            (self.compute_v1.instances, 'Get',
             self.messages.ComputeInstancesGetRequest(instance='my-instance-2',
                                                      project='my-project',
                                                      zone='my-zone')),
            (self.compute_v1.instances, 'Insert',
             self.messages.ComputeInstancesInsertRequest(
                 instance=self.messages.Instance(name='my-instance-4'),
                 project='my-project',
                 zone='my-zone')),
        ]

        errors = []
        responses = request_helper.MakeRequests(
            requests=requests,
            http=self.mock_http,
            batch_url='https://www.googleapis.com/batch/compute',
            errors=errors)

        self.assertEqual(list(responses),
                         batch_helper_sync_response + wait_response)
        self.assertFalse(errors)
        self.batch_helper.assert_called_once_with(
            requests=requests,
            http=self.mock_http,
            batch_url='https://www.googleapis.com/batch/compute')
        operations_data = []
        for operation in operations:
            operations_data.append(
                waiters.OperationData(operation,
                                      self.compute_v1.zoneOperations,
                                      self.compute_v1.instances,
                                      project='my-project'))
        self.wait_for_operations.assert_called_once_with(
            operations_data=operations_data,
            http=self.mock_http,
            batch_url='https://www.googleapis.com/batch/compute',
            progress_tracker=None,
            warnings=[],
            errors=[],
            log_result=True,
            timeout=None)
def MakeRequests(requests,
                 http,
                 batch_url,
                 errors,
                 progress_tracker=None,
                 no_followup=False,
                 always_return_operation=False,
                 followup_overrides=None,
                 log_result=True,
                 log_warnings=True,
                 timeout=None):
    """Makes one or more requests to the API.

  Each request can be either a synchronous API call or an asynchronous
  one. For synchronous calls (e.g., get and list), the result from the
  server is yielded immediately. For asynchronous calls (e.g., calls
  that return operations like insert), this function waits until the
  operation reaches the DONE state and fetches the corresponding
  object and yields that object (nothing is yielded for deletions).

  Currently, a heterogenous set of synchronous calls can be made
  (e.g., get request to fetch a disk and instance), however, the
  asynchronous requests must be homogenous (e.g., they must all be the
  same verb on the same collection). In the future, heterogenous
  asynchronous requests will be supported. For now, it is up to the
  client to ensure that the asynchronous requests are
  homogenous. Synchronous and asynchronous requests can be mixed.

  Args:
    requests: A list of requests to make. Each element must be a 3-element tuple
      where the first element is the service, the second element is the string
      name of the method on the service, and the last element is a protocol
      buffer representing the request.
    http: An httplib2.Http-like object.
    batch_url: The handler for making batch requests.
    errors: A list for capturing errors. If any response contains an error, it
      is added to this list.
    progress_tracker: progress tracker to be ticked while waiting for operations
      to finish.
    no_followup: If True, do not followup operation with a GET request.
    always_return_operation: If True, return operation object even if operation
      fails.
    followup_overrides: A list of new resource names to GET once the operation
      finishes. Generally used in renaming calls.
    log_result: Whether the Operation Waiter should print the result in past
      tense of each request.
    log_warnings: Whether warnings for completed operation should be printed.
    timeout: The maximum amount of time, in seconds, to wait for the
      operations to reach the DONE state.

  Yields:
    A response for each request. For deletion requests, no corresponding
    responses are returned.
  """
    if _RequestsAreListRequests(requests):
        for item in _List(requests=requests,
                          http=http,
                          batch_url=batch_url,
                          errors=errors):
            yield item
        return
    responses, new_errors = batch_helper.MakeRequests(requests=requests,
                                                      http=http,
                                                      batch_url=batch_url)
    errors.extend(new_errors)

    operation_service = None
    resource_service = None

    # Collects all operation objects in a list so they can be waited on
    # and yields all non-operation objects since non-operation responses
    # cannot be waited on.
    operations_data = []

    if not followup_overrides:
        followup_overrides = [None for _ in requests]
    for request, response, followup_override in zip(requests, responses,
                                                    followup_overrides):
        if response is None:
            continue

        service, _, request_body = request
        if (isinstance(response, service.client.MESSAGES_MODULE.Operation)
                and not _IsEmptyOperation(response, service)
                and service.__class__.__name__
                not in ('GlobalOperationsService', 'RegionOperationsService',
                        'ZoneOperationsService',
                        'GlobalOrganizationOperationsService',
                        'GlobalAccountsOperationsService')):

            resource_service = service
            project = None
            if hasattr(request_body, 'project'):
                project = request_body.project
                if response.zone:
                    operation_service = service.client.zoneOperations
                elif response.region:
                    operation_service = service.client.regionOperations
                else:
                    operation_service = service.client.globalOperations
            else:
                operation_service = service.client.globalOrganizationOperations

            operations_data.append(
                waiters.OperationData(
                    response,
                    operation_service,
                    resource_service,
                    project=project,
                    no_followup=no_followup,
                    followup_override=followup_override,
                    always_return_operation=always_return_operation))

        else:
            yield response

    if operations_data:
        warnings = []
        for response in waiters.WaitForOperations(
                operations_data=operations_data,
                http=http,
                batch_url=batch_url,
                warnings=warnings,
                progress_tracker=progress_tracker,
                errors=errors,
                log_result=log_result,
                timeout=timeout):
            yield response

        if warnings and log_warnings:
            log.warning(
                utils.ConstructList('Some requests generated warnings:',
                                    warnings))