mirror of https://github.com/grpc/grpc.git
xDS interop: Move k8s-specific logic out of the test app (#30591)
Separates xDS Test Client/Server (represent an interface to corresponding workload running remotely) from their runners (kubernetes-specific logic to provision the workloads with prerequisites). This is a refactoring, should not change the behavior.pull/30596/head
parent
103f4c2f1e
commit
3817db13b6
20 changed files with 646 additions and 522 deletions
@ -0,0 +1,13 @@ |
|||||||
|
# Copyright 2022 gRPC authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
@ -0,0 +1,63 @@ |
|||||||
|
# Copyright 2022 gRPC authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
""" |
||||||
|
Common functionality for running xDS Test Client and Server remotely. |
||||||
|
""" |
||||||
|
from abc import ABCMeta |
||||||
|
from abc import abstractmethod |
||||||
|
from typing import Dict, Optional |
||||||
|
import urllib.parse |
||||||
|
|
||||||
|
|
||||||
|
class RunnerError(Exception): |
||||||
|
"""Error running xDS Test App running remotely.""" |
||||||
|
|
||||||
|
|
||||||
|
class BaseRunner(metaclass=ABCMeta): |
||||||
|
|
||||||
|
@abstractmethod |
||||||
|
def run(self, **kwargs): |
||||||
|
pass |
||||||
|
|
||||||
|
@abstractmethod |
||||||
|
def cleanup(self, *, force=False): |
||||||
|
pass |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def _logs_explorer_link_from_params( |
||||||
|
cls, |
||||||
|
*, |
||||||
|
gcp_ui_url: str, |
||||||
|
gcp_project: str, |
||||||
|
query: Dict[str, str], |
||||||
|
request: Optional[Dict[str, str]] = None) -> str: |
||||||
|
req_merged = {'query': cls._logs_explorer_query(query)} |
||||||
|
if request is not None: |
||||||
|
req_merged.update(request) |
||||||
|
|
||||||
|
req = cls._logs_explorer_request(req_merged) |
||||||
|
return f'https://{gcp_ui_url}/logs/query;{req}?project={gcp_project}' |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def _logs_explorer_query(cls, query: Dict[str, str]) -> str: |
||||||
|
return '\n'.join(f'{k}="{v}"' for k, v in query.items()) |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def _logs_explorer_request(cls, req: Dict[str, str]) -> str: |
||||||
|
return ';'.join( |
||||||
|
f'{k}={cls._logs_explorer_quote(v)}' for k, v in req.items()) |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def _logs_explorer_quote(cls, value: str) -> str: |
||||||
|
return urllib.parse.quote_plus(value, safe=':') |
@ -0,0 +1,13 @@ |
|||||||
|
# Copyright 2022 gRPC authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
@ -0,0 +1,194 @@ |
|||||||
|
# Copyright 2022 gRPC authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
""" |
||||||
|
Run xDS Test Client on Kubernetes. |
||||||
|
""" |
||||||
|
|
||||||
|
import logging |
||||||
|
from typing import Optional |
||||||
|
|
||||||
|
from framework.infrastructure import gcp |
||||||
|
from framework.infrastructure import k8s |
||||||
|
from framework.test_app.client_app import XdsTestClient |
||||||
|
from framework.test_app.runners.k8s import k8s_base_runner |
||||||
|
|
||||||
|
logger = logging.getLogger(__name__) |
||||||
|
|
||||||
|
|
||||||
|
class KubernetesClientRunner(k8s_base_runner.KubernetesBaseRunner): |
||||||
|
|
||||||
|
def __init__( # pylint: disable=too-many-locals |
||||||
|
self, |
||||||
|
k8s_namespace, |
||||||
|
*, |
||||||
|
deployment_name, |
||||||
|
image_name, |
||||||
|
td_bootstrap_image, |
||||||
|
gcp_api_manager: gcp.api.GcpApiManager, |
||||||
|
gcp_project: str, |
||||||
|
gcp_service_account: str, |
||||||
|
xds_server_uri=None, |
||||||
|
network='default', |
||||||
|
service_account_name=None, |
||||||
|
stats_port=8079, |
||||||
|
deployment_template='client.deployment.yaml', |
||||||
|
service_account_template='service-account.yaml', |
||||||
|
reuse_namespace=False, |
||||||
|
namespace_template=None, |
||||||
|
debug_use_port_forwarding=False, |
||||||
|
enable_workload_identity=True): |
||||||
|
super().__init__(k8s_namespace, namespace_template, reuse_namespace) |
||||||
|
|
||||||
|
# Settings |
||||||
|
self.deployment_name = deployment_name |
||||||
|
self.image_name = image_name |
||||||
|
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.debug_use_port_forwarding = debug_use_port_forwarding |
||||||
|
self.enable_workload_identity = enable_workload_identity |
||||||
|
# Service account settings: |
||||||
|
# Kubernetes service account |
||||||
|
if self.enable_workload_identity: |
||||||
|
self.service_account_name = service_account_name or deployment_name |
||||||
|
self.service_account_template = service_account_template |
||||||
|
else: |
||||||
|
self.service_account_name = None |
||||||
|
self.service_account_template = None |
||||||
|
# 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.port_forwarder: Optional[k8s.PortForwarder] = None |
||||||
|
|
||||||
|
# TODO(sergiitk): make rpc UnaryCall enum or get it from proto |
||||||
|
def run( # pylint: disable=arguments-differ |
||||||
|
self, |
||||||
|
*, |
||||||
|
server_target, |
||||||
|
rpc='UnaryCall', |
||||||
|
qps=25, |
||||||
|
metadata='', |
||||||
|
secure_mode=False, |
||||||
|
config_mesh=None, |
||||||
|
print_response=False) -> XdsTestClient: |
||||||
|
logger.info( |
||||||
|
'Deploying xDS test client "%s" to k8s namespace %s: ' |
||||||
|
'server_target=%s rpc=%s qps=%s metadata=%r secure_mode=%s ' |
||||||
|
'print_response=%s', self.deployment_name, self.k8s_namespace.name, |
||||||
|
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() |
||||||
|
|
||||||
|
if self.enable_workload_identity: |
||||||
|
# Allow Kubernetes service account to use the GCP service account |
||||||
|
# identity. |
||||||
|
self._grant_workload_identity_user( |
||||||
|
gcp_iam=self.gcp_iam, |
||||||
|
gcp_service_account=self.gcp_service_account, |
||||||
|
service_account_name=self.service_account_name) |
||||||
|
|
||||||
|
# Create service account |
||||||
|
self.service_account = self._create_service_account( |
||||||
|
self.service_account_template, |
||||||
|
service_account_name=self.service_account_name, |
||||||
|
namespace_name=self.k8s_namespace.name, |
||||||
|
gcp_service_account=self.gcp_service_account) |
||||||
|
|
||||||
|
# Always create a new deployment |
||||||
|
self.deployment = self._create_deployment( |
||||||
|
self.deployment_template, |
||||||
|
deployment_name=self.deployment_name, |
||||||
|
image_name=self.image_name, |
||||||
|
namespace_name=self.k8s_namespace.name, |
||||||
|
service_account_name=self.service_account_name, |
||||||
|
td_bootstrap_image=self.td_bootstrap_image, |
||||||
|
xds_server_uri=self.xds_server_uri, |
||||||
|
network=self.network, |
||||||
|
stats_port=self.stats_port, |
||||||
|
server_target=server_target, |
||||||
|
rpc=rpc, |
||||||
|
qps=qps, |
||||||
|
metadata=metadata, |
||||||
|
secure_mode=secure_mode, |
||||||
|
config_mesh=config_mesh, |
||||||
|
print_response=print_response) |
||||||
|
|
||||||
|
self._wait_deployment_with_available_replicas(self.deployment_name) |
||||||
|
|
||||||
|
# Load test client pod. We need only one client at the moment |
||||||
|
pod = self.k8s_namespace.list_deployment_pods(self.deployment)[0] |
||||||
|
self._wait_pod_started(pod.metadata.name) |
||||||
|
pod_ip = pod.status.pod_ip |
||||||
|
rpc_port = self.stats_port |
||||||
|
rpc_host = None |
||||||
|
|
||||||
|
# Experimental, for local debugging. |
||||||
|
if self.debug_use_port_forwarding: |
||||||
|
logger.info('LOCAL DEV MODE: Enabling port forwarding to %s:%s', |
||||||
|
pod_ip, self.stats_port) |
||||||
|
self.port_forwarder = self.k8s_namespace.port_forward_pod( |
||||||
|
pod, remote_port=self.stats_port) |
||||||
|
rpc_port = self.port_forwarder.local_port |
||||||
|
rpc_host = self.port_forwarder.local_address |
||||||
|
|
||||||
|
return XdsTestClient(ip=pod_ip, |
||||||
|
rpc_port=rpc_port, |
||||||
|
server_target=server_target, |
||||||
|
rpc_host=rpc_host) |
||||||
|
|
||||||
|
def cleanup(self, *, force=False, force_namespace=False): # pylint: disable=arguments-differ |
||||||
|
if self.port_forwarder: |
||||||
|
self.port_forwarder.close() |
||||||
|
self.port_forwarder = None |
||||||
|
if self.deployment or force: |
||||||
|
self._delete_deployment(self.deployment_name) |
||||||
|
self.deployment = None |
||||||
|
if self.enable_workload_identity and (self.service_account or force): |
||||||
|
self._revoke_workload_identity_user( |
||||||
|
gcp_iam=self.gcp_iam, |
||||||
|
gcp_service_account=self.gcp_service_account, |
||||||
|
service_account_name=self.service_account_name) |
||||||
|
self._delete_service_account(self.service_account_name) |
||||||
|
self.service_account = None |
||||||
|
super().cleanup(force=force_namespace and force) |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def make_namespace_name(cls, |
||||||
|
resource_prefix: str, |
||||||
|
resource_suffix: str, |
||||||
|
name: str = 'client') -> str: |
||||||
|
"""A helper to make consistent XdsTestClient kubernetes namespace name |
||||||
|
for given resource prefix and suffix. |
||||||
|
|
||||||
|
Note: the idea is to intentionally produce different namespace name for |
||||||
|
the test server, and the test client, as that closely mimics real-world |
||||||
|
deployments. |
||||||
|
""" |
||||||
|
return cls._make_namespace_name(resource_prefix, resource_suffix, name) |
@ -0,0 +1,252 @@ |
|||||||
|
# Copyright 2022 gRPC authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
""" |
||||||
|
Run xDS Test Client on Kubernetes. |
||||||
|
""" |
||||||
|
import logging |
||||||
|
from typing import List, Optional |
||||||
|
|
||||||
|
from framework.infrastructure import gcp |
||||||
|
from framework.infrastructure import k8s |
||||||
|
from framework.test_app.runners.k8s import k8s_base_runner |
||||||
|
from framework.test_app.server_app import XdsTestServer |
||||||
|
|
||||||
|
logger = logging.getLogger(__name__) |
||||||
|
|
||||||
|
|
||||||
|
class KubernetesServerRunner(k8s_base_runner.KubernetesBaseRunner): |
||||||
|
DEFAULT_TEST_PORT = 8080 |
||||||
|
DEFAULT_MAINTENANCE_PORT = 8080 |
||||||
|
DEFAULT_SECURE_MODE_MAINTENANCE_PORT = 8081 |
||||||
|
|
||||||
|
def __init__( # pylint: disable=too-many-locals |
||||||
|
self, |
||||||
|
k8s_namespace, |
||||||
|
*, |
||||||
|
deployment_name, |
||||||
|
image_name, |
||||||
|
td_bootstrap_image, |
||||||
|
gcp_api_manager: gcp.api.GcpApiManager, |
||||||
|
gcp_project: str, |
||||||
|
gcp_service_account: str, |
||||||
|
service_account_name=None, |
||||||
|
service_name=None, |
||||||
|
neg_name=None, |
||||||
|
xds_server_uri=None, |
||||||
|
network='default', |
||||||
|
deployment_template='server.deployment.yaml', |
||||||
|
service_account_template='service-account.yaml', |
||||||
|
service_template='server.service.yaml', |
||||||
|
reuse_service=False, |
||||||
|
reuse_namespace=False, |
||||||
|
namespace_template=None, |
||||||
|
debug_use_port_forwarding=False, |
||||||
|
enable_workload_identity=True): |
||||||
|
super().__init__(k8s_namespace, namespace_template, reuse_namespace) |
||||||
|
|
||||||
|
# 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.service_template = service_template |
||||||
|
self.reuse_service = reuse_service |
||||||
|
self.debug_use_port_forwarding = debug_use_port_forwarding |
||||||
|
self.enable_workload_identity = enable_workload_identity |
||||||
|
# Service account settings: |
||||||
|
# Kubernetes service account |
||||||
|
if self.enable_workload_identity: |
||||||
|
self.service_account_name = service_account_name or deployment_name |
||||||
|
self.service_account_template = service_account_template |
||||||
|
else: |
||||||
|
self.service_account_name = None |
||||||
|
self.service_account_template = None |
||||||
|
|
||||||
|
# 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 |
||||||
|
self.port_forwarders: List[k8s.PortForwarder] = [] |
||||||
|
|
||||||
|
def run( # pylint: disable=arguments-differ |
||||||
|
self, |
||||||
|
*, |
||||||
|
test_port=DEFAULT_TEST_PORT, |
||||||
|
maintenance_port=None, |
||||||
|
secure_mode=False, |
||||||
|
server_id=None, |
||||||
|
replica_count=1) -> List[XdsTestServer]: |
||||||
|
# Implementation detail: in secure mode, maintenance ("backchannel") |
||||||
|
# port must be different from the test port so communication with |
||||||
|
# maintenance services can be reached independently from the security |
||||||
|
# configuration under test. |
||||||
|
if maintenance_port is None: |
||||||
|
if not secure_mode: |
||||||
|
maintenance_port = self.DEFAULT_MAINTENANCE_PORT |
||||||
|
else: |
||||||
|
maintenance_port = self.DEFAULT_SECURE_MODE_MAINTENANCE_PORT |
||||||
|
|
||||||
|
if secure_mode and maintenance_port == test_port: |
||||||
|
raise ValueError('port and maintenance_port must be different ' |
||||||
|
'when running test server in secure mode') |
||||||
|
# To avoid bugs with comparing wrong types. |
||||||
|
if not (isinstance(test_port, int) and |
||||||
|
isinstance(maintenance_port, int)): |
||||||
|
raise TypeError('Port numbers must be integer') |
||||||
|
|
||||||
|
if secure_mode and not self.enable_workload_identity: |
||||||
|
raise ValueError('Secure mode requires Workload Identity enabled.') |
||||||
|
|
||||||
|
logger.info( |
||||||
|
'Deploying xDS test server "%s" to k8s namespace %s: test_port=%s ' |
||||||
|
'maintenance_port=%s secure_mode=%s server_id=%s replica_count=%s', |
||||||
|
self.deployment_name, self.k8s_namespace.name, test_port, |
||||||
|
maintenance_port, secure_mode, server_id, 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() |
||||||
|
|
||||||
|
# Reuse existing if requested, create a new deployment when missing. |
||||||
|
# Useful for debugging to avoid NEG loosing relation to deleted service. |
||||||
|
if self.reuse_service: |
||||||
|
self.service = self._reuse_service(self.service_name) |
||||||
|
if not self.service: |
||||||
|
self.service = self._create_service( |
||||||
|
self.service_template, |
||||||
|
service_name=self.service_name, |
||||||
|
namespace_name=self.k8s_namespace.name, |
||||||
|
deployment_name=self.deployment_name, |
||||||
|
neg_name=self.neg_name, |
||||||
|
test_port=test_port) |
||||||
|
self._wait_service_neg(self.service_name, test_port) |
||||||
|
|
||||||
|
if self.enable_workload_identity: |
||||||
|
# Allow Kubernetes service account to use the GCP service account |
||||||
|
# identity. |
||||||
|
self._grant_workload_identity_user( |
||||||
|
gcp_iam=self.gcp_iam, |
||||||
|
gcp_service_account=self.gcp_service_account, |
||||||
|
service_account_name=self.service_account_name) |
||||||
|
|
||||||
|
# Create service account |
||||||
|
self.service_account = self._create_service_account( |
||||||
|
self.service_account_template, |
||||||
|
service_account_name=self.service_account_name, |
||||||
|
namespace_name=self.k8s_namespace.name, |
||||||
|
gcp_service_account=self.gcp_service_account) |
||||||
|
|
||||||
|
# Always create a new deployment |
||||||
|
self.deployment = self._create_deployment( |
||||||
|
self.deployment_template, |
||||||
|
deployment_name=self.deployment_name, |
||||||
|
image_name=self.image_name, |
||||||
|
namespace_name=self.k8s_namespace.name, |
||||||
|
service_account_name=self.service_account_name, |
||||||
|
td_bootstrap_image=self.td_bootstrap_image, |
||||||
|
xds_server_uri=self.xds_server_uri, |
||||||
|
network=self.network, |
||||||
|
replica_count=replica_count, |
||||||
|
test_port=test_port, |
||||||
|
maintenance_port=maintenance_port, |
||||||
|
server_id=server_id, |
||||||
|
secure_mode=secure_mode) |
||||||
|
|
||||||
|
self._wait_deployment_with_available_replicas(self.deployment_name, |
||||||
|
replica_count) |
||||||
|
|
||||||
|
# Wait for pods running |
||||||
|
pods = self.k8s_namespace.list_deployment_pods(self.deployment) |
||||||
|
|
||||||
|
servers = [] |
||||||
|
for pod in pods: |
||||||
|
pod_name = pod.metadata.name |
||||||
|
self._wait_pod_started(pod_name) |
||||||
|
|
||||||
|
pod_ip = pod.status.pod_ip |
||||||
|
rpc_host = None |
||||||
|
# Experimental, for local debugging. |
||||||
|
local_port = maintenance_port |
||||||
|
if self.debug_use_port_forwarding: |
||||||
|
logger.info('LOCAL DEV MODE: Enabling port forwarding to %s:%s', |
||||||
|
pod_ip, maintenance_port) |
||||||
|
port_forwarder = self.k8s_namespace.port_forward_pod( |
||||||
|
pod, remote_port=maintenance_port) |
||||||
|
self.port_forwarders.append(port_forwarder) |
||||||
|
local_port = port_forwarder.local_port |
||||||
|
rpc_host = port_forwarder.local_address |
||||||
|
|
||||||
|
servers.append( |
||||||
|
XdsTestServer(ip=pod_ip, |
||||||
|
rpc_port=test_port, |
||||||
|
maintenance_port=local_port, |
||||||
|
secure_mode=secure_mode, |
||||||
|
server_id=server_id, |
||||||
|
rpc_host=rpc_host, |
||||||
|
pod_name=pod_name)) |
||||||
|
return servers |
||||||
|
|
||||||
|
def cleanup(self, *, force=False, force_namespace=False): # pylint: disable=arguments-differ |
||||||
|
if self.port_forwarders: |
||||||
|
for port_forwarder in self.port_forwarders: |
||||||
|
port_forwarder.close() |
||||||
|
self.port_forwarders = [] |
||||||
|
if self.deployment or force: |
||||||
|
self._delete_deployment(self.deployment_name) |
||||||
|
self.deployment = None |
||||||
|
if (self.service and not self.reuse_service) or force: |
||||||
|
self._delete_service(self.service_name) |
||||||
|
self.service = None |
||||||
|
if self.enable_workload_identity and (self.service_account or force): |
||||||
|
self._revoke_workload_identity_user( |
||||||
|
gcp_iam=self.gcp_iam, |
||||||
|
gcp_service_account=self.gcp_service_account, |
||||||
|
service_account_name=self.service_account_name) |
||||||
|
self._delete_service_account(self.service_account_name) |
||||||
|
self.service_account = None |
||||||
|
super().cleanup(force=(force_namespace and force)) |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def make_namespace_name(cls, |
||||||
|
resource_prefix: str, |
||||||
|
resource_suffix: str, |
||||||
|
name: str = 'server') -> str: |
||||||
|
"""A helper to make consistent XdsTestServer kubernetes namespace name |
||||||
|
for given resource prefix and suffix. |
||||||
|
|
||||||
|
Note: the idea is to intentionally produce different namespace name for |
||||||
|
the test server, and the test client, as that closely mimics real-world |
||||||
|
deployments. |
||||||
|
""" |
||||||
|
return cls._make_namespace_name(resource_prefix, resource_suffix, name) |
Loading…
Reference in new issue