14 """A test framework built for urlMap related xDS test cases."""
18 from typing
import Any, Iterable, Mapping, Tuple
20 from absl
import flags
21 from absl
import logging
23 from framework
import xds_flags
24 from framework
import xds_k8s_flags
32 flags.adopt_module_key_flags(xds_flags)
33 flags.adopt_module_key_flags(xds_k8s_flags)
35 STRATEGY = flags.DEFINE_enum(
'strategy',
37 enum_values=[
'create',
'keep',
'reuse'],
38 help=
'Strategy of GCP resources management')
47 """Where all the urlMap change happens."""
61 logging.info(
'Apply urlMap change for test case: %s.%s',
62 test_case.short_module_name, test_case.__name__)
63 url_map_parts = test_case.url_map_change(
69 test_case:
'XdsUrlMapTestCase') -> Tuple[HostRule, PathMatcher]:
71 "hosts": [test_case.hostname()],
72 "pathMatcher": test_case.path_matcher_name(),
75 "name": test_case.path_matcher_name(),
78 return host_rule, path_matcher
81 path_matcher: PathMatcher) ->
None:
82 self.
_map[
"hostRules"].append(host_rule)
83 self.
_map[
"pathMatchers"].append(path_matcher)
87 """Automatically parse Abseil flags into a dictionary.
89 Abseil flag is only available after the Abseil app initialization. If we use
90 __new__ in our metaclass, the flag value parse will happen during the
91 initialization of modules, hence will fail. That's why we are using __call__
92 to inject metaclass magics, and the flag parsing will be delayed until the
93 class is about to be instantiated.
96 for flag_module
in [xds_flags, xds_k8s_flags]:
97 for key, value
in inspect.getmembers(flag_module):
98 if isinstance(value, flags.FlagHolder):
99 res[key.lower()] = value.value
100 res[
'strategy'] = STRATEGY.value
105 """Ensures singleton and injects flag values."""
123 """Manages the lifecycle of GCP resources.
125 The GCP resources including:
126 - 3 K8s deployment (client, default backends, alternative backends)
127 - Full set of the Traffic Director stuff
128 - Merged gigantic urlMap from all imported test cases
130 All resources are intended to be used across test cases and multiple runs
131 (except the client K8s deployment).
137 def __init__(self, absl_flags: Mapping[str, Any] =
None):
138 if absl_flags
is not None:
139 for key
in absl_flags:
140 setattr(self, key, absl_flags[key])
142 if getattr(self,
'resource_suffix',
None)
is None:
145 raise NotImplementedError(
146 'Predefined resource_suffix is not supported for UrlMap tests')
147 logging.info(
'GcpResourceManager: resource prefix=%s, suffix=%s',
152 self.
td = traffic_director.TrafficDirectorManager(
155 resource_prefix=self.resource_prefix,
157 network=self.network,
158 compute_api_version=self.compute_api_version,
162 self.resource_prefix)
166 deployment_name=self.server_name,
167 image_name=self.server_image,
168 gcp_project=self.project,
170 gcp_service_account=self.gcp_service_account,
171 td_bootstrap_image=self.td_bootstrap_image,
172 xds_server_uri=self.xds_server_uri,
173 network=self.network,
174 enable_workload_identity=self.enable_workload_identity)
177 deployment_name=self.server_name +
'-alternative',
178 image_name=self.server_image,
179 gcp_project=self.project,
181 gcp_service_account=self.gcp_service_account,
182 td_bootstrap_image=self.td_bootstrap_image,
183 xds_server_uri=self.xds_server_uri,
184 network=self.network,
185 enable_workload_identity=self.enable_workload_identity,
186 reuse_namespace=
True)
189 deployment_name=self.server_name +
'-affinity',
190 image_name=self.server_image,
191 gcp_project=self.project,
193 gcp_service_account=self.gcp_service_account,
194 td_bootstrap_image=self.td_bootstrap_image,
195 xds_server_uri=self.xds_server_uri,
196 network=self.network,
197 enable_workload_identity=self.enable_workload_identity,
198 reuse_namespace=
True)
199 logging.info(
'Strategy of GCP resources management: %s', self.strategy)
207 logging.info(
'GcpResourceManager: client_namespace_suffix=%s',
208 client_namespace_suffix)
210 return client_app.KubernetesClientRunner(
211 k8s.KubernetesNamespace(
213 client_app.KubernetesClientRunner.make_namespace_name(
214 self.resource_prefix, client_namespace_suffix)),
215 deployment_name=self.client_name,
216 image_name=self.client_image,
217 gcp_project=self.project,
219 gcp_service_account=self.gcp_service_account,
220 td_bootstrap_image=self.td_bootstrap_image,
221 xds_server_uri=self.xds_server_uri,
222 network=self.network,
223 debug_use_port_forwarding=self.debug_use_port_forwarding,
224 enable_workload_identity=self.enable_workload_identity,
225 stats_port=self.client_port)
229 logging.info(
'GcpResourceManager: pre clean-up')
233 def setup(self, test_case_classes: Iterable[
'XdsUrlMapTestCase']) ->
None:
234 if self.strategy
not in [
'create',
'keep']:
235 logging.info(
'GcpResourceManager: skipping setup for strategy [%s]',
241 logging.info(
'GcpResourceManager: start setup')
243 if self.ensure_firewall:
244 self.
td.create_firewall_rule(
245 allowed_ports=self.firewall_allowed_ports)
249 self.
td.create_backend_service()
250 self.
td.create_alternative_backend_service()
251 self.
td.create_affinity_backend_service()
254 url_map_name=self.
td.make_resource_name(self.
td.URL_MAP_NAME))
255 for test_case_class
in test_case_classes:
256 aggregator.apply_change(test_case_class)
257 final_url_map = aggregator.get_map()
259 self.
td.create_url_map_with_content(final_url_map)
263 self.
td.create_forwarding_rule(self.server_xds_port)
266 test_port=self.server_port,
267 maintenance_port=self.server_maintenance_port)
270 test_port=self.server_port,
271 maintenance_port=self.server_maintenance_port)
275 test_port=self.server_port,
276 maintenance_port=self.server_maintenance_port,
281 self.
td.backend_service_add_neg_backends(neg_name, neg_zones)
283 neg_name_alt, neg_zones_alt = self.
k8s_namespace.get_service_neg(
285 self.
td.alternative_backend_service_add_neg_backends(
286 neg_name_alt, neg_zones_alt)
288 neg_name_affinity, neg_zones_affinity = self.
k8s_namespace.get_service_neg(
290 self.
td.affinity_backend_service_add_neg_backends(
291 neg_name_affinity, neg_zones_affinity)
293 self.
td.wait_for_backends_healthy_status()
294 self.
td.wait_for_alternative_backends_healthy_status()
295 self.
td.wait_for_affinity_backends_healthy_status()
298 if self.strategy
not in [
'create']:
300 'GcpResourceManager: skipping tear down for strategy [%s]',
303 logging.info(
'GcpResourceManager: start tear down')
304 if hasattr(self,
'td'):
306 if hasattr(self,
'test_server_runner'):
308 if hasattr(self,
'test_server_alternative_runner'):
310 force_namespace=
True)
311 if hasattr(self,
'test_server_affinity_runner'):
313 force_namespace=
True)
315 @functools.lru_cache(
None)
317 """Returns default backend service URL."""
318 self.
td.load_backend_service()
319 return self.
td.backend_service.url
321 @functools.lru_cache(
None)
323 """Returns alternative backend service URL."""
324 self.
td.load_alternative_backend_service()
325 return self.
td.alternative_backend_service.url
327 @functools.lru_cache(
None)
329 """Returns affinity backend service URL."""
330 self.
td.load_affinity_backend_service()
331 return self.
td.affinity_backend_service.url