[PSM Interop] Output GCP log links at the end of the test (#33104)

Output example:

```
I0512 21:33:09.879951 140704627381056 xds_k8s_testcase.py:482] ----- TestMethod __main__.BaselineTest.test_traffic_director_grpc_setup teardown -----
I0512 21:33:09.880299 140704627381056 traffic_director.py:582] Deleting Forwarding rule "sergiitk-forwarding-rule-20230513-0428-aolg9"
...
I0512 21:35:53.437206 140704627381056 xds_k8s_testcase.py:491] ----- Test client/server logs -----
I0512 21:35:53.437497 140704627381056 k8s_base_runner.py:491] GCP Logs Explorer link to psm-grpc-client:
https://pantheon.corp.google.com/logs/query;query=resource.type%3D%22k8s_container%22%0Aresource.labels.project_id%3D%22sergiitk-grpc-gke%22%0Aresource.labels.container_name%3D%22psm-grpc-client%22%0Aresource.labels.namespace_name%3D%22sergiitk-client-20230513-0428-aolg9%22;timeRange=2023-05-13T04:30:45.361596Z%2F2023-05-13T04:34:41.227014Z?project=sergiitk-grpc-gke
I0512 21:35:53.437677 140704627381056 k8s_base_runner.py:491] GCP Logs Explorer link to psm-grpc-server:
https://pantheon.corp.google.com/logs/query;query=resource.type%3D%22k8s_container%22%0Aresource.labels.project_id%3D%22sergiitk-grpc-gke%22%0Aresource.labels.container_name%3D%22psm-grpc-server%22%0Aresource.labels.namespace_name%3D%22sergiitk-server-20230513-0428-aolg9%22;timeRange=2023-05-13T04:29:41.171388Z%2F2023-05-13T04:35:53.437068Z?project=sergiitk-grpc-gke
[       OK ] BaselineTest.test_traffic_director_grpc_setup
```
pull/33266/head
Sergii Tkachenko 2 years ago committed by GitHub
parent d1c0dc58cc
commit acbac3deee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      tools/run_tests/xds_k8s_test_driver/bin/lib/common.py
  2. 3
      tools/run_tests/xds_k8s_test_driver/config/common.cfg
  3. 12
      tools/run_tests/xds_k8s_test_driver/framework/helpers/datetime.py
  4. 101
      tools/run_tests/xds_k8s_test_driver/framework/test_app/runners/k8s/k8s_base_runner.py
  5. 129
      tools/run_tests/xds_k8s_test_driver/framework/test_app/runners/k8s/k8s_xds_client_runner.py
  6. 158
      tools/run_tests/xds_k8s_test_driver/framework/test_app/runners/k8s/k8s_xds_server_runner.py
  7. 4
      tools/run_tests/xds_k8s_test_driver/framework/xds_k8s_testcase.py
  8. 4
      tools/run_tests/xds_k8s_test_driver/framework/xds_url_map_testcase.py

@ -86,6 +86,7 @@ def make_server_runner(namespace: k8s.KubernetesNamespace,
deployment_name=xds_flags.SERVER_NAME.value, deployment_name=xds_flags.SERVER_NAME.value,
image_name=xds_k8s_flags.SERVER_IMAGE.value, image_name=xds_k8s_flags.SERVER_IMAGE.value,
td_bootstrap_image=xds_k8s_flags.TD_BOOTSTRAP_IMAGE.value, td_bootstrap_image=xds_k8s_flags.TD_BOOTSTRAP_IMAGE.value,
xds_server_uri=xds_flags.XDS_SERVER_URI.value,
gcp_project=xds_flags.PROJECT.value, gcp_project=xds_flags.PROJECT.value,
gcp_api_manager=gcp_api_manager, gcp_api_manager=gcp_api_manager,
gcp_service_account=xds_k8s_flags.GCP_SERVICE_ACCOUNT.value, gcp_service_account=xds_k8s_flags.GCP_SERVICE_ACCOUNT.value,
@ -95,9 +96,7 @@ def make_server_runner(namespace: k8s.KubernetesNamespace,
debug_use_port_forwarding=port_forwarding) debug_use_port_forwarding=port_forwarding)
if secure: if secure:
runner_kwargs.update( runner_kwargs['deployment_template'] = 'server-secure.deployment.yaml'
xds_server_uri=xds_flags.XDS_SERVER_URI.value,
deployment_template='server-secure.deployment.yaml')
return KubernetesServerRunner(namespace, **runner_kwargs) return KubernetesServerRunner(namespace, **runner_kwargs)

@ -11,6 +11,3 @@
--logger_levels=__main__:DEBUG,framework:INFO --logger_levels=__main__:DEBUG,framework:INFO
--verbosity=0 --verbosity=0
# Google projects: remove if console.cloud.google.com redirects to Logs Explorer
# ref: https://github.com/grpc/grpc/pull/26844#discussion_r680224772
--gcp_ui_url=pantheon.corp.google.com

@ -14,7 +14,7 @@
"""This contains common helpers for working with dates and time.""" """This contains common helpers for working with dates and time."""
import datetime import datetime
import re import re
from typing import Optional, Pattern from typing import Pattern
RE_ZERO_OFFSET: Pattern[str] = re.compile(r'[+\-]00:?00$') RE_ZERO_OFFSET: Pattern[str] = re.compile(r'[+\-]00:?00$')
@ -29,12 +29,10 @@ def shorten_utc_zone(utc_datetime_str: str) -> str:
return RE_ZERO_OFFSET.sub('Z', utc_datetime_str) return RE_ZERO_OFFSET.sub('Z', utc_datetime_str)
def iso8601_utc_time(timedelta: Optional[datetime.timedelta] = None) -> str: def iso8601_utc_time(time: datetime.datetime = None) -> str:
"""Return datetime relative to current in ISO-8601 format, UTC tz.""" """Converts datetime UTC and formats as ISO-8601 Zulu time."""
time: datetime.datetime = utc_now() utc_time = time.astimezone(tz=datetime.timezone.utc)
if timedelta: return shorten_utc_zone(utc_time.isoformat())
time += timedelta
return shorten_utc_zone(time.isoformat())
def datetime_suffix(*, seconds: bool = False) -> str: def datetime_suffix(*, seconds: bool = False) -> str:

@ -14,6 +14,7 @@
""" """
Common functionality for running xDS Test Client and Server on Kubernetes. Common functionality for running xDS Test Client and Server on Kubernetes.
""" """
from abc import ABCMeta
import contextlib import contextlib
import datetime import datetime
import logging import logging
@ -37,42 +38,95 @@ logger = logging.getLogger(__name__)
_RunnerError = base_runner.RunnerError _RunnerError = base_runner.RunnerError
_HighlighterYaml = framework.helpers.highlighter.HighlighterYaml _HighlighterYaml = framework.helpers.highlighter.HighlighterYaml
_helper_datetime = framework.helpers.datetime _helper_datetime = framework.helpers.datetime
_datetime = datetime.datetime
_timedelta = datetime.timedelta _timedelta = datetime.timedelta
class KubernetesBaseRunner(base_runner.BaseRunner): class KubernetesBaseRunner(base_runner.BaseRunner, metaclass=ABCMeta):
# Pylint wants abstract classes to override abstract methods.
# pylint: disable=abstract-method
TEMPLATE_DIR_NAME = 'kubernetes-manifests' TEMPLATE_DIR_NAME = 'kubernetes-manifests'
TEMPLATE_DIR_RELATIVE_PATH = f'../../../../{TEMPLATE_DIR_NAME}' TEMPLATE_DIR_RELATIVE_PATH = f'../../../../{TEMPLATE_DIR_NAME}'
ROLE_WORKLOAD_IDENTITY_USER = 'roles/iam.workloadIdentityUser' ROLE_WORKLOAD_IDENTITY_USER = 'roles/iam.workloadIdentityUser'
pod_port_forwarders: List[k8s.PortForwarder] pod_port_forwarders: List[k8s.PortForwarder]
pod_log_collectors: List[k8s.PodLogCollector] pod_log_collectors: List[k8s.PodLogCollector]
# Required fields.
k8s_namespace: k8s.KubernetesNamespace
deployment_name: str
image_name: str
gcp_project: str
gcp_service_account: str
gcp_ui_url: str
# Fields with default values.
namespace_template: str = 'namespace.yaml'
reuse_namespace: bool = False
# Mutable state.
deployment: Optional[k8s.V1Deployment] = None
service_account: Optional[k8s.V1ServiceAccount] = None
time_start_requested: Optional[_datetime] = None
time_start_completed: Optional[_datetime] = None
time_stopped: Optional[_datetime] = None
def __init__(self, def __init__(self,
k8s_namespace, k8s_namespace: k8s.KubernetesNamespace,
namespace_template=None, *,
reuse_namespace=False): deployment_name: str,
image_name: str,
gcp_project: str,
gcp_service_account: str,
gcp_ui_url: str,
namespace_template: Optional[str] = 'namespace.yaml',
reuse_namespace: bool = False):
super().__init__() super().__init__()
self._highlighter = _HighlighterYaml()
# Kubernetes namespaced resources manager # Required fields.
self.k8s_namespace: k8s.KubernetesNamespace = k8s_namespace self.deployment_name = deployment_name
self.image_name = image_name
self.gcp_project = gcp_project
# Maps GCP service account to Kubernetes service account
self.gcp_service_account = gcp_service_account
self.gcp_ui_url = gcp_ui_url
# Kubernetes namespace resources manager.
self.k8s_namespace = k8s_namespace
if namespace_template:
self.namespace_template = namespace_template
self.reuse_namespace = reuse_namespace self.reuse_namespace = reuse_namespace
self.namespace_template = namespace_template or 'namespace.yaml'
# Mutable state # Mutable state
self.namespace: Optional[k8s.V1Namespace] = None self.namespace: Optional[k8s.V1Namespace] = None
self.pod_port_forwarders = [] self.pod_port_forwarders = []
self.pod_log_collectors = [] self.pod_log_collectors = []
# Highlighter.
self._highlighter = _HighlighterYaml()
def run(self, **kwargs): def run(self, **kwargs):
del kwargs del kwargs
if self.time_start_requested:
if self.time_start_completed:
raise RuntimeError(
f"Deployment {self.deployment_name}: has already been"
f" started at {self.time_start_completed.isoformat()}")
else:
raise RuntimeError(
f"Deployment {self.deployment_name}: start has already been"
f" requested at {self.time_start_requested.isoformat()}")
self.time_start_requested = _datetime.now()
self.logs_explorer_link()
if self.reuse_namespace: if self.reuse_namespace:
self.namespace = self._reuse_namespace() self.namespace = self._reuse_namespace()
if not self.namespace: if not self.namespace:
self.namespace = self._create_namespace( self.namespace = self._create_namespace(
self.namespace_template, namespace_name=self.k8s_namespace.name) self.namespace_template, namespace_name=self.k8s_namespace.name)
def cleanup(self, *, force=False): def _cleanup_namespace(self, *, force=False):
if (self.namespace and not self.reuse_namespace) or force: if (self.namespace and not self.reuse_namespace) or force:
self.delete_namespace() self.delete_namespace()
self.namespace = None self.namespace = None
@ -405,6 +459,19 @@ class KubernetesBaseRunner(base_runner.BaseRunner):
logger.info("Service %s: detected NEG=%s in zones=%s", name, neg_name, logger.info("Service %s: detected NEG=%s in zones=%s", name, neg_name,
neg_zones) neg_zones)
def logs_explorer_link(self):
if not self.time_start_requested:
logger.warning(
'Skipped printing GCP log link for a non-started deployment %s',
self.deployment_name)
return
self._logs_explorer_link(deployment_name=self.deployment_name,
namespace_name=self.k8s_namespace.name,
gcp_project=self.gcp_project,
gcp_ui_url=self.gcp_ui_url,
start_time=self.time_start_requested,
end_time=self.time_stopped)
@classmethod @classmethod
def _logs_explorer_link(cls, def _logs_explorer_link(cls,
*, *,
@ -412,13 +479,17 @@ class KubernetesBaseRunner(base_runner.BaseRunner):
namespace_name: str, namespace_name: str,
gcp_project: str, gcp_project: str,
gcp_ui_url: str, gcp_ui_url: str,
end_delta: Optional[_timedelta] = None) -> None: start_time: Optional[_datetime] = None,
end_time: Optional[_datetime] = None):
"""Output the link to test server/client logs in GCP Logs Explorer.""" """Output the link to test server/client logs in GCP Logs Explorer."""
if end_delta is None: if not start_time:
end_delta = _timedelta(hours=1) start_time = _datetime.now()
time_now = _helper_datetime.iso8601_utc_time() if not end_time:
time_end = _helper_datetime.iso8601_utc_time(end_delta) end_time = start_time + _timedelta(minutes=30)
request = {'timeRange': f'{time_now}/{time_end}'}
logs_start = _helper_datetime.iso8601_utc_time(start_time)
logs_end = _helper_datetime.iso8601_utc_time(end_time)
request = {'timeRange': f'{logs_start}/{logs_end}'}
query = { query = {
'resource.type': 'k8s_container', 'resource.type': 'k8s_container',
'resource.labels.project_id': gcp_project, 'resource.labels.project_id': gcp_project,

@ -14,7 +14,7 @@
""" """
Run xDS Test Client on Kubernetes. Run xDS Test Client on Kubernetes.
""" """
import datetime
import logging import logging
from typing import Optional from typing import Optional
@ -28,61 +28,69 @@ logger = logging.getLogger(__name__)
class KubernetesClientRunner(k8s_base_runner.KubernetesBaseRunner): class KubernetesClientRunner(k8s_base_runner.KubernetesBaseRunner):
# Required fields.
xds_server_uri: str
stats_port: int
deployment_template: str
enable_workload_identity: bool
debug_use_port_forwarding: bool
td_bootstrap_image: str
network: str
# Optional fields.
service_account_name: Optional[str] = None
service_account_template: Optional[str] = None
gcp_iam: Optional[gcp.iam.IamV1] = None
def __init__( # pylint: disable=too-many-locals def __init__( # pylint: disable=too-many-locals
self, self,
k8s_namespace, k8s_namespace: k8s.KubernetesNamespace,
*, *,
deployment_name, deployment_name: str,
image_name, image_name: str,
td_bootstrap_image, td_bootstrap_image: str,
network='default',
xds_server_uri: Optional[str] = None,
gcp_api_manager: gcp.api.GcpApiManager, gcp_api_manager: gcp.api.GcpApiManager,
gcp_project: str, gcp_project: str,
gcp_service_account: str, gcp_service_account: str,
xds_server_uri=None, service_account_name: Optional[str] = None,
network='default', stats_port: int = 8079,
service_account_name=None, deployment_template: str = 'client.deployment.yaml',
stats_port=8079, service_account_template: str = 'service-account.yaml',
deployment_template='client.deployment.yaml', reuse_namespace: bool = False,
service_account_template='service-account.yaml', namespace_template: Optional[str] = None,
reuse_namespace=False, debug_use_port_forwarding: bool = False,
namespace_template=None, enable_workload_identity: bool = True):
debug_use_port_forwarding=False, super().__init__(k8s_namespace,
enable_workload_identity=True): deployment_name=deployment_name,
super().__init__(k8s_namespace, namespace_template, reuse_namespace) image_name=image_name,
gcp_project=gcp_project,
gcp_service_account=gcp_service_account,
gcp_ui_url=gcp_api_manager.gcp_ui_url,
namespace_template=namespace_template,
reuse_namespace=reuse_namespace)
# Settings # Settings
self.deployment_name = deployment_name
self.image_name = image_name
self.stats_port = stats_port self.stats_port = stats_port
# xDS bootstrap generator
self.td_bootstrap_image = td_bootstrap_image
self.xds_server_uri = xds_server_uri
self.network = network
self.deployment_template = deployment_template self.deployment_template = deployment_template
self.debug_use_port_forwarding = debug_use_port_forwarding
self.enable_workload_identity = enable_workload_identity self.enable_workload_identity = enable_workload_identity
# Service account settings: self.debug_use_port_forwarding = debug_use_port_forwarding
# Kubernetes service account
# Used by the TD bootstrap generator.
self.td_bootstrap_image = td_bootstrap_image
self.network = network
self.xds_server_uri = xds_server_uri
# Workload identity settings:
if self.enable_workload_identity: if self.enable_workload_identity:
# Kubernetes service account.
self.service_account_name = service_account_name or deployment_name self.service_account_name = service_account_name or deployment_name
self.service_account_template = service_account_template self.service_account_template = service_account_template
else: # GCP IAM API used to grant allow workload service accounts
self.service_account_name = None # permission to use GCP service account identity.
self.service_account_template = None self.gcp_iam = gcp.iam.IamV1(gcp_api_manager, gcp_project)
# GCP.
self.gcp_project = gcp_project
self.gcp_ui_url = gcp_api_manager.gcp_ui_url
# GCP service account to map to Kubernetes service account
self.gcp_service_account = gcp_service_account
# GCP IAM API used to grant allow workload service accounts permission
# to use GCP service account identity.
self.gcp_iam = gcp.iam.IamV1(gcp_api_manager, gcp_project)
# Mutable state
self.deployment: Optional[k8s.V1Deployment] = None
self.service_account: Optional[k8s.V1ServiceAccount] = None
# TODO(sergiitk): make rpc UnaryCall enum or get it from proto
def run( # pylint: disable=arguments-differ def run( # pylint: disable=arguments-differ
self, self,
*, *,
@ -99,11 +107,6 @@ class KubernetesClientRunner(k8s_base_runner.KubernetesBaseRunner):
'server_target=%s rpc=%s qps=%s metadata=%r secure_mode=%s ' 'server_target=%s rpc=%s qps=%s metadata=%r secure_mode=%s '
'print_response=%s', self.deployment_name, self.k8s_namespace.name, 'print_response=%s', self.deployment_name, self.k8s_namespace.name,
server_target, rpc, qps, metadata, secure_mode, print_response) server_target, rpc, qps, metadata, secure_mode, print_response)
self._logs_explorer_link(deployment_name=self.deployment_name,
namespace_name=self.k8s_namespace.name,
gcp_project=self.gcp_project,
gcp_ui_url=self.gcp_ui_url)
super().run() super().run()
if self.enable_workload_identity: if self.enable_workload_identity:
@ -148,6 +151,7 @@ class KubernetesClientRunner(k8s_base_runner.KubernetesBaseRunner):
# Verify the deployment reports all pods started as well. # Verify the deployment reports all pods started as well.
self._wait_deployment_with_available_replicas(self.deployment_name) self._wait_deployment_with_available_replicas(self.deployment_name)
self.time_start_completed = datetime.datetime.now()
return self._xds_test_client_for_pod(pod, server_target=server_target) return self._xds_test_client_for_pod(pod, server_target=server_target)
@ -165,18 +169,25 @@ class KubernetesClientRunner(k8s_base_runner.KubernetesBaseRunner):
hostname=pod.metadata.name, hostname=pod.metadata.name,
rpc_host=rpc_host) rpc_host=rpc_host)
def cleanup(self, *, force=False, force_namespace=False): # pylint: disable=arguments-differ # pylint: disable=arguments-differ
if self.deployment or force: def cleanup(self, *, force=False, force_namespace=False):
self._delete_deployment(self.deployment_name) try:
self.deployment = None if self.deployment or force:
if self.enable_workload_identity and (self.service_account or force): self._delete_deployment(self.deployment_name)
self._revoke_workload_identity_user( self.deployment = None
gcp_iam=self.gcp_iam, if (self.enable_workload_identity and
gcp_service_account=self.gcp_service_account, (self.service_account or force)):
service_account_name=self.service_account_name) self._revoke_workload_identity_user(
self._delete_service_account(self.service_account_name) gcp_iam=self.gcp_iam,
self.service_account = None gcp_service_account=self.gcp_service_account,
super().cleanup(force=force_namespace and force) service_account_name=self.service_account_name)
self._delete_service_account(self.service_account_name)
self.service_account = None
self._cleanup_namespace(force=force_namespace and force)
finally:
self.time_stopped = datetime.datetime.now()
# pylint: enable=arguments-differ
@classmethod @classmethod
def make_namespace_name(cls, def make_namespace_name(cls,

@ -14,6 +14,7 @@
""" """
Run xDS Test Client on Kubernetes. Run xDS Test Client on Kubernetes.
""" """
import datetime
import logging import logging
from typing import List, Optional from typing import List, Optional
@ -30,70 +31,82 @@ class KubernetesServerRunner(k8s_base_runner.KubernetesBaseRunner):
DEFAULT_MAINTENANCE_PORT = 8080 DEFAULT_MAINTENANCE_PORT = 8080
DEFAULT_SECURE_MODE_MAINTENANCE_PORT = 8081 DEFAULT_SECURE_MODE_MAINTENANCE_PORT = 8081
# Required fields.
deployment_template: str
service_name: str
service_template: str
reuse_service: bool
enable_workload_identity: bool
debug_use_port_forwarding: bool
gcp_neg_name: str
td_bootstrap_image: str
xds_server_uri: str
network: str
# Optional fields.
service_account_name: Optional[str] = None
service_account_template: Optional[str] = None
gcp_iam: Optional[gcp.iam.IamV1] = None
# Mutable state.
service: Optional[k8s.V1Service] = None
def __init__( # pylint: disable=too-many-locals def __init__( # pylint: disable=too-many-locals
self, self,
k8s_namespace, k8s_namespace: k8s.KubernetesNamespace,
*, *,
deployment_name, deployment_name: str,
image_name, image_name: str,
td_bootstrap_image, td_bootstrap_image: str,
network: str = 'default',
xds_server_uri: Optional[str] = None,
gcp_api_manager: gcp.api.GcpApiManager, gcp_api_manager: gcp.api.GcpApiManager,
gcp_project: str, gcp_project: str,
gcp_service_account: str, gcp_service_account: str,
service_account_name=None, service_account_name: Optional[str] = None,
service_name=None, service_name: Optional[str] = None,
neg_name=None, neg_name: Optional[str] = None,
xds_server_uri=None, deployment_template: str = 'server.deployment.yaml',
network='default', service_account_template: str = 'service-account.yaml',
deployment_template='server.deployment.yaml', service_template: str = 'server.service.yaml',
service_account_template='service-account.yaml', reuse_service: bool = False,
service_template='server.service.yaml', reuse_namespace: bool = False,
reuse_service=False, namespace_template: Optional[str] = None,
reuse_namespace=False, debug_use_port_forwarding: bool = False,
namespace_template=None, enable_workload_identity: bool = True):
debug_use_port_forwarding=False, super().__init__(k8s_namespace,
enable_workload_identity=True): deployment_name=deployment_name,
super().__init__(k8s_namespace, namespace_template, reuse_namespace) image_name=image_name,
gcp_project=gcp_project,
gcp_service_account=gcp_service_account,
gcp_ui_url=gcp_api_manager.gcp_ui_url,
namespace_template=namespace_template,
reuse_namespace=reuse_namespace)
# Settings # Settings
self.deployment_name = deployment_name
self.image_name = image_name
self.service_name = service_name or deployment_name
# xDS bootstrap generator
self.td_bootstrap_image = td_bootstrap_image
self.xds_server_uri = xds_server_uri
# This only works in k8s >= 1.18.10-gke.600
# https://cloud.google.com/kubernetes-engine/docs/how-to/standalone-neg#naming_negs
self.neg_name = neg_name or (f'{self.k8s_namespace.name}-'
f'{self.service_name}')
self.network = network
self.deployment_template = deployment_template self.deployment_template = deployment_template
self.service_name = service_name or deployment_name
self.service_template = service_template self.service_template = service_template
self.reuse_service = reuse_service self.reuse_service = reuse_service
self.debug_use_port_forwarding = debug_use_port_forwarding
self.enable_workload_identity = enable_workload_identity self.enable_workload_identity = enable_workload_identity
# Service account settings: self.debug_use_port_forwarding = debug_use_port_forwarding
# Kubernetes service account # GCP Network Endpoint Group.
self.gcp_neg_name = neg_name or (f'{self.k8s_namespace.name}-'
f'{self.service_name}')
# Used by the TD bootstrap generator.
self.td_bootstrap_image = td_bootstrap_image
self.network = network
self.xds_server_uri = xds_server_uri
# Workload identity settings:
if self.enable_workload_identity: if self.enable_workload_identity:
# Kubernetes service account.
self.service_account_name = service_account_name or deployment_name self.service_account_name = service_account_name or deployment_name
self.service_account_template = service_account_template self.service_account_template = service_account_template
else: # GCP IAM API used to grant allow workload service accounts
self.service_account_name = None # permission to use GCP service account identity.
self.service_account_template = None self.gcp_iam = gcp.iam.IamV1(gcp_api_manager, gcp_project)
# GCP.
self.gcp_project = gcp_project
self.gcp_ui_url = gcp_api_manager.gcp_ui_url
# GCP service account to map to Kubernetes service account
self.gcp_service_account = gcp_service_account
# GCP IAM API used to grant allow workload service accounts permission
# to use GCP service account identity.
self.gcp_iam = gcp.iam.IamV1(gcp_api_manager, gcp_project)
# Mutable state
self.deployment: Optional[k8s.V1Deployment] = None
self.service_account: Optional[k8s.V1ServiceAccount] = None
self.service: Optional[k8s.V1Service] = None
def run( # pylint: disable=arguments-differ,too-many-branches def run( # pylint: disable=arguments-differ,too-many-branches
self, self,
@ -126,12 +139,6 @@ class KubernetesServerRunner(k8s_base_runner.KubernetesBaseRunner):
'maintenance_port=%s secure_mode=%s replica_count=%s', 'maintenance_port=%s secure_mode=%s replica_count=%s',
self.deployment_name, self.k8s_namespace.name, test_port, self.deployment_name, self.k8s_namespace.name, test_port,
maintenance_port, secure_mode, replica_count) maintenance_port, secure_mode, replica_count)
self._logs_explorer_link(deployment_name=self.deployment_name,
namespace_name=self.k8s_namespace.name,
gcp_project=self.gcp_project,
gcp_ui_url=self.gcp_ui_url)
# Create namespace.
super().run() super().run()
# Reuse existing if requested, create a new deployment when missing. # Reuse existing if requested, create a new deployment when missing.
@ -144,7 +151,7 @@ class KubernetesServerRunner(k8s_base_runner.KubernetesBaseRunner):
service_name=self.service_name, service_name=self.service_name,
namespace_name=self.k8s_namespace.name, namespace_name=self.k8s_namespace.name,
deployment_name=self.deployment_name, deployment_name=self.deployment_name,
neg_name=self.neg_name, neg_name=self.gcp_neg_name,
test_port=test_port) test_port=test_port)
self._wait_service_neg(self.service_name, test_port) self._wait_service_neg(self.service_name, test_port)
@ -190,6 +197,8 @@ class KubernetesServerRunner(k8s_base_runner.KubernetesBaseRunner):
# Verify the deployment reports all pods started as well. # Verify the deployment reports all pods started as well.
self._wait_deployment_with_available_replicas(self.deployment_name, self._wait_deployment_with_available_replicas(self.deployment_name,
replica_count) replica_count)
self.time_start_completed = datetime.datetime.now()
servers: List[XdsTestServer] = [] servers: List[XdsTestServer] = []
for pod in pods: for pod in pods:
servers.append( servers.append(
@ -228,21 +237,28 @@ class KubernetesServerRunner(k8s_base_runner.KubernetesBaseRunner):
secure_mode=secure_mode, secure_mode=secure_mode,
rpc_host=rpc_host) rpc_host=rpc_host)
def cleanup(self, *, force=False, force_namespace=False): # pylint: disable=arguments-differ # pylint: disable=arguments-differ
if self.deployment or force: def cleanup(self, *, force=False, force_namespace=False):
self._delete_deployment(self.deployment_name) try:
self.deployment = None if self.deployment or force:
if (self.service and not self.reuse_service) or force: self._delete_deployment(self.deployment_name)
self._delete_service(self.service_name) self.deployment = None
self.service = None if (self.service and not self.reuse_service) or force:
if self.enable_workload_identity and (self.service_account or force): self._delete_service(self.service_name)
self._revoke_workload_identity_user( self.service = None
gcp_iam=self.gcp_iam, if (self.enable_workload_identity and
gcp_service_account=self.gcp_service_account, (self.service_account or force)):
service_account_name=self.service_account_name) self._revoke_workload_identity_user(
self._delete_service_account(self.service_account_name) gcp_iam=self.gcp_iam,
self.service_account = None gcp_service_account=self.gcp_service_account,
super().cleanup(force=(force_namespace and force)) service_account_name=self.service_account_name)
self._delete_service_account(self.service_account_name)
self.service_account = None
self._cleanup_namespace(force=(force_namespace and force))
finally:
self.time_stopped = datetime.datetime.now()
# pylint: enable=arguments-differ
@classmethod @classmethod
def make_namespace_name(cls, def make_namespace_name(cls,

@ -511,6 +511,10 @@ class IsolatedXdsKubernetesTestCase(XdsKubernetesBaseTestCase,
except retryers.RetryError: except retryers.RetryError:
logger.exception('Got error during teardown') logger.exception('Got error during teardown')
finally: finally:
logger.info('----- Test client/server logs -----')
self.client_runner.logs_explorer_link()
self.server_runner.logs_explorer_link()
# Fail if any of the pods restarted. # Fail if any of the pods restarted.
self.assertEqual( self.assertEqual(
client_restarts, client_restarts,

@ -393,6 +393,10 @@ class XdsUrlMapTestCase(absltest.TestCase, metaclass=_MetaXdsUrlMapTestCase):
except retryers.RetryError: except retryers.RetryError:
logging.exception('Got error during teardown') logging.exception('Got error during teardown')
finally: finally:
if hasattr(cls, 'test_client_runner') and cls.test_client_runner:
logging.info('----- Test client logs -----')
cls.test_client_runner.logs_explorer_link()
# Fail if any of the pods restarted. # Fail if any of the pods restarted.
error_msg = ( error_msg = (
'Client pods unexpectedly restarted' 'Client pods unexpectedly restarted'

Loading…
Cancel
Save