xds-k8s: Fix the issue with parsing Operation.metadata (#28736)

1. Solves an issue with unpacking Operation.metadata causing
   json_format.ParseError Can not find message descriptor by type_url
2. Improves the readability of the
   framework.infrastructure.gcp.api.OperationError
pull/28742/head
Sergii Tkachenko 3 years ago committed by GitHub
parent 9a79d44e9b
commit b9b6255993
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 76
      tools/run_tests/xds_k8s_test_driver/framework/infrastructure/gcp/api.py

@ -14,6 +14,7 @@
import abc import abc
import contextlib import contextlib
import functools import functools
import json
import logging import logging
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
@ -21,9 +22,9 @@ from absl import flags
from google.cloud import secretmanager_v1 from google.cloud import secretmanager_v1
from google.longrunning import operations_pb2 from google.longrunning import operations_pb2
from google.protobuf import json_format from google.protobuf import json_format
from google.protobuf import text_format
from google.rpc import code_pb2 from google.rpc import code_pb2
from google.rpc import error_details_pb2 from google.rpc import error_details_pb2
from google.rpc import status_pb2
from googleapiclient import discovery from googleapiclient import discovery
import googleapiclient.errors import googleapiclient.errors
import googleapiclient.http import googleapiclient.http
@ -260,29 +261,68 @@ class OperationError(Error):
https://cloud.google.com/apis/design/design_patterns#long_running_operations https://cloud.google.com/apis/design/design_patterns#long_running_operations
https://github.com/googleapis/googleapis/blob/master/google/longrunning/operations.proto https://github.com/googleapis/googleapis/blob/master/google/longrunning/operations.proto
""" """
api_name: str
name: str
metadata: str
code_name: code_pb2.Code
error: status_pb2.Status
def __init__(self, api_name, operation_response, message=None): def __init__(self, api_name: str, response: dict):
self.api_name = api_name self.api_name = api_name
operation = json_format.ParseDict(
# Operation.metadata field is Any specific to the API. It may not be
# present in the default descriptor pool, and that's expected.
# To avoid json_format.ParseError, handle it separately.
self.metadata = response.pop('metadata', {})
# Must be after removing metadata field.
operation: Operation = self._parse_operation_response(response)
self.name = operation.name or 'unknown'
self.code_name = code_pb2.Code.Name(operation.error.code)
self.error = operation.error
super().__init__()
@staticmethod
def _parse_operation_response(operation_response: dict) -> Operation:
try:
return json_format.ParseDict(
operation_response, operation_response,
Operation(), Operation(),
ignore_unknown_fields=True, ignore_unknown_fields=True,
descriptor_pool=error_details_pb2.DESCRIPTOR.pool) descriptor_pool=error_details_pb2.DESCRIPTOR.pool)
self.name = operation.name or 'unknown' except (json_format.Error, TypeError) as e:
self.code_name = code_pb2.Code.Name(operation.error.code) # Swallow parsing errors if any. Building correct OperationError()
self.error = operation.error # is more important than losing debug information. Details still
# Collect error details packed as Any without parsing concrete types. # can be extracted from the warning.
self.error_details = [ logger.warning(
text_format.MessageToString(any_error, as_one_line=True) ("Can't parse response while processing OperationError: '%r', "
for any_error in self.error.details "error %r"), operation_response, e)
] return Operation()
if message is None:
message = (f'{api_name} operation "{self.name}" failed. Error ' def __str__(self):
f'code: {self.error.code} ({self.code_name}), ' indent_l1 = ' ' * 2
f'message: {self.error.message}, ' indent_l2 = indent_l1 * 2
f'details: {self.error_details}')
self.message = message result = (f'{self.api_name} operation "{self.name}" failed.\n'
super().__init__(message) f'{indent_l1}code: {self.error.code} ({self.code_name})\n'
f'{indent_l1}message: "{self.error.message}"')
if self.error.details:
result += f'\n{indent_l1}details: [\n'
for any_error in self.error.details:
error_str = json_format.MessageToJson(any_error)
for line in error_str.splitlines():
result += indent_l2 + line + '\n'
result += f'{indent_l1}]'
if self.metadata:
result += f'\n metadata: \n'
metadata_str = json.dumps(self.metadata, indent=2)
for line in metadata_str.splitlines():
result += indent_l2 + line + '\n'
result = result.rstrip()
return result
class GcpProjectApiResource: class GcpProjectApiResource:

Loading…
Cancel
Save