1# Copyright 2022 gRPC authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14import datetime 15import logging 16from typing import List 17 18from absl import flags 19from absl.testing import absltest 20from absl.testing import parameterized 21 22from framework import bootstrap_generator_testcase 23from framework import xds_k8s_testcase 24from framework.helpers import retryers 25from framework.test_app.runners.k8s import k8s_xds_client_runner 26from framework.test_app.runners.k8s import k8s_xds_server_runner 27 28logger = logging.getLogger(__name__) 29flags.adopt_module_key_flags(xds_k8s_testcase) 30 31# Type aliases 32XdsTestServer = xds_k8s_testcase.XdsTestServer 33XdsTestClient = xds_k8s_testcase.XdsTestClient 34KubernetesServerRunner = k8s_xds_server_runner.KubernetesServerRunner 35KubernetesClientRunner = k8s_xds_client_runner.KubernetesClientRunner 36_timedelta = datetime.timedelta 37 38 39# Returns a list of bootstrap generator versions to be tested along with their 40# image names. 41# 42# Whenever we release a new version of the bootstrap generator, we need to add a 43# corresponding entry here. 44# 45# TODO: Update bootstrap generator release instructions to add an entry here, 46# after the release is published. 47def bootstrap_version_testcases() -> List: 48 return ( 49 dict( 50 version='v0.14.0', 51 image= 52 'gcr.io/grpc-testing/td-grpc-bootstrap:d6baaf7b0e0c63054ac4d9bedc09021ff261d599' 53 ), 54 dict( 55 version='v0.13.0', 56 image= 57 'gcr.io/grpc-testing/td-grpc-bootstrap:203db6ce70452996f4183c30dd4c5ecaada168b0' 58 ), 59 dict( 60 version='v0.12.0', 61 image= 62 'gcr.io/grpc-testing/td-grpc-bootstrap:8765051ef3b742bc5cd20f16de078ae7547f2ba2' 63 ), 64 dict( 65 version='v0.11.0', 66 image= 67 'gcr.io/grpc-testing/td-grpc-bootstrap:b96f7a73314668aee83cbf86ab1e40135a0542fc' 68 ), 69 # v0.10.0 uses v2 xDS transport protocol by default. TD only supports v3 70 # and we can force the bootstrap generator to emit config with v3 71 # support by setting the --include-v3-features-experimental flag to 72 # true. 73 # 74 # TODO: Figure out how to pass flags to the bootstrap generator via the 75 # client and server runners, and uncomment this version. 76 # ('v0.10.0', 'gcr.io/grpc-testing/td-grpc-bootstrap:66de7ea0e170351c9fae17232b81adbfb3e80ec3'), 77 ) 78 79 80# TODO: Reuse service account and namespaces for significant improvements in 81# running time. 82class BootstrapGeneratorClientTest( 83 bootstrap_generator_testcase.BootstrapGeneratorBaseTest, 84 parameterized.TestCase): 85 client_runner: KubernetesClientRunner 86 server_runner: KubernetesServerRunner 87 test_server: XdsTestServer 88 89 @classmethod 90 def setUpClass(cls): 91 """Hook method for setting up class fixture before running tests in 92 the class. 93 """ 94 super().setUpClass() 95 96 # For client tests, we use a single server instance that can be shared 97 # across all the parameterized clients. And this server runner will use 98 # the version of the bootstrap generator as configured via the 99 # --td_bootstrap_image flag. 100 cls.server_runner = cls.initKubernetesServerRunner() 101 cls.test_server = cls.startTestServer( 102 server_runner=cls.server_runner, 103 port=cls.server_port, 104 maintenance_port=cls.server_maintenance_port, 105 xds_host=cls.server_xds_host, 106 xds_port=cls.server_xds_port) 107 108 # Load backends. 109 neg_name, neg_zones = cls.server_runner.k8s_namespace.get_service_neg( 110 cls.server_runner.service_name, cls.server_port) 111 112 # Add backends to the Backend Service. 113 cls.td.backend_service_add_neg_backends(neg_name, neg_zones) 114 cls.td.wait_for_backends_healthy_status() 115 116 @classmethod 117 def tearDownClass(cls): 118 # Remove backends from the Backend Service before closing the server 119 # runner. 120 neg_name, neg_zones = cls.server_runner.k8s_namespace.get_service_neg( 121 cls.server_runner.service_name, cls.server_port) 122 cls.td.backend_service_remove_neg_backends(neg_name, neg_zones) 123 cls.server_runner.cleanup(force=cls.force_cleanup) 124 super().tearDownClass() 125 126 def tearDown(self): 127 logger.info('----- TestMethod %s teardown -----', self.id()) 128 retryer = retryers.constant_retryer(wait_fixed=_timedelta(seconds=10), 129 attempts=3, 130 log_level=logging.INFO) 131 try: 132 retryer(self._cleanup) 133 except retryers.RetryError: 134 logger.exception('Got error during teardown') 135 super().tearDown() 136 137 def _cleanup(self): 138 self.client_runner.cleanup(force=self.force_cleanup) 139 140 @parameterized.parameters( 141 (t["version"], t["image"]) for t in bootstrap_version_testcases()) 142 def test_baseline_in_client_with_bootstrap_version(self, version, image): 143 """Runs the baseline test for multiple versions of the bootstrap 144 generator on the client. 145 """ 146 logger.info('----- testing bootstrap generator version %s -----', 147 version) 148 self.client_runner = self.initKubernetesClientRunner( 149 td_bootstrap_image=image) 150 test_client: XdsTestClient = self.startTestClient(self.test_server) 151 self.assertXdsConfigExists(test_client) 152 self.assertSuccessfulRpcs(test_client) 153 154 155# TODO: Use unique client and server deployment names while creating the 156# corresponding runners, by suffixing the version of the bootstrap generator 157# being tested. Then, run these in parallel. 158class BootstrapGeneratorServerTest( 159 bootstrap_generator_testcase.BootstrapGeneratorBaseTest, 160 parameterized.TestCase): 161 client_runner: KubernetesClientRunner 162 server_runner: KubernetesServerRunner 163 test_server: XdsTestServer 164 165 def tearDown(self): 166 logger.info('----- TestMethod %s teardown -----', self.id()) 167 retryer = retryers.constant_retryer(wait_fixed=_timedelta(seconds=10), 168 attempts=3, 169 log_level=logging.INFO) 170 try: 171 retryer(self._cleanup) 172 except retryers.RetryError: 173 logger.exception('Got error during teardown') 174 super().tearDown() 175 176 def _cleanup(self): 177 self.client_runner.cleanup(force=self.force_cleanup) 178 self.removeServerBackends() 179 self.server_runner.cleanup(force=self.force_cleanup) 180 181 @parameterized.parameters( 182 (t["version"], t["image"]) for t in bootstrap_version_testcases()) 183 def test_baseline_in_server_with_bootstrap_version(self, version, image): 184 """Runs the baseline test for multiple versions of the bootstrap 185 generator on the server. 186 """ 187 logger.info('----- Testing bootstrap generator version %s -----', 188 version) 189 self.server_runner = self.initKubernetesServerRunner( 190 td_bootstrap_image=image) 191 self.test_server = self.startTestServer( 192 server_runner=self.server_runner, 193 port=self.server_port, 194 maintenance_port=self.server_maintenance_port, 195 xds_host=self.server_xds_host, 196 xds_port=self.server_xds_port) 197 198 # Load backends. 199 neg_name, neg_zones = self.server_runner.k8s_namespace.get_service_neg( 200 self.server_runner.service_name, self.server_port) 201 202 # Add backends to the Backend Service. 203 self.td.backend_service_add_neg_backends(neg_name, neg_zones) 204 self.td.wait_for_backends_healthy_status() 205 206 self.client_runner = self.initKubernetesClientRunner() 207 test_client: XdsTestClient = self.startTestClient(self.test_server) 208 self.assertXdsConfigExists(test_client) 209 self.assertSuccessfulRpcs(test_client) 210 211 212if __name__ == '__main__': 213 absltest.main() 214