14 """Clean up resources created by the tests.
16 This is intended as a tool to delete leaked resources from old tests.
18 Typical usage examples:
20 python3 tools/run_tests/xds_k8s_test_driver/bin/cleanup/cleanup.py\
21 --project=grpc-testing\
22 --network=default-vpc\
23 --kube_context=gke_grpc-testing_us-central1-a_interop-test-psm-sec-v2-us-central1-a\
24 --resource_prefix='required-but-does-not-matter'\
25 --td_bootstrap_image='required-but-does-not-matter' --server_image='required-but-does-not-matter' --client_image='required-but-does-not-matter'
34 from typing
import Any, List
37 from absl
import flags
40 from framework
import xds_flags
41 from framework
import xds_k8s_flags
48 logger = logging.getLogger(__name__)
50 KubernetesClientRunner = client_app.KubernetesClientRunner
51 KubernetesServerRunner = server_app.KubernetesServerRunner
53 GCLOUD = os.environ.get(
'GCLOUD',
'gcloud')
54 GCLOUD_CMD_TIMEOUT_S = datetime.timedelta(seconds=5).total_seconds()
55 ZONE =
'us-central1-a'
56 SECONDARY_ZONE =
'us-west1-b'
58 PSM_SECURITY_PREFIX =
'xds-k8s-security'
59 URL_MAP_TEST_PREFIX =
'interop-psm-url-map'
61 KEEP_PERIOD_HOURS = flags.DEFINE_integer(
65 "number of hours for a resource to keep. Resources older than this will be deleted. Default is 168 (7 days)"
67 DRY_RUN = flags.DEFINE_bool(
70 help=
"dry run, print resources but do not perform deletion")
71 TD_RESOURCE_PREFIXES = flags.DEFINE_list(
72 "td_resource_prefixes",
73 default=[PSM_SECURITY_PREFIX],
75 "a comma-separated list of prefixes for which the leaked TD resources will be deleted",
77 SERVER_PREFIXES = flags.DEFINE_list(
79 default=[PSM_SECURITY_PREFIX],
81 "a comma-separated list of prefixes for which the leaked servers will be deleted",
83 CLIENT_PREFIXES = flags.DEFINE_list(
85 default=[PSM_SECURITY_PREFIX, URL_MAP_TEST_PREFIX],
87 "a comma-separated list of prefixes for which the leaked clients will be deleted",
93 json_path = os.path.realpath(
94 os.path.join(os.path.dirname(os.path.abspath(__file__)),
95 'keep_xds_interop_resources.json'))
96 with open(json_path,
'r')
as f:
97 KEEP_CONFIG = json.load(f)
98 logging.debug(
'Resource keep config loaded: %s',
99 json.dumps(KEEP_CONFIG, indent=2))
103 return suffix
in KEEP_CONFIG[
"gce_framework"][
"suffix"]
107 return suffix
in KEEP_CONFIG[
"gke_framework"][
"suffix"]
110 @functools.lru_cache()
112 return datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(
113 hours=KEEP_PERIOD_HOURS.value)
117 cmds = [GCLOUD,
'--project', project,
'--quiet'] + list(cmds)
121 '--format',
'json',
'--filter',
122 f
'creationTimestamp <= {get_expire_timestamp().isoformat()}'
125 logging.debug(
'Executing: %s',
" ".join(cmds))
126 proc = subprocess.Popen(cmds,
127 stdout=subprocess.PIPE,
128 stderr=subprocess.PIPE)
130 stdout = proc.stdout.read()
131 stderr = proc.stderr.read()
133 returncode = proc.wait(timeout=GCLOUD_CMD_TIMEOUT_S)
134 except subprocess.TimeoutExpired:
135 logging.error(
'> Timeout executing cmd [%s]',
" ".join(cmds))
138 logging.error(
'> Failed to execute cmd [%s], returned %d, stderr: %s',
139 " ".join(cmds), returncode, stderr)
142 return json.loads(stdout)
147 prefix: str, suffix: str):
148 """Removing GCP resources created by run_xds_tests.py."""
149 logging.info(
'----- Removing run_xds_tests.py resources with suffix [%s]',
151 exec_gcloud(project,
'compute',
'forwarding-rules',
'delete',
152 f
'test-forwarding-rule{suffix}',
'--global')
153 exec_gcloud(project,
'compute',
'target-http-proxies',
'delete',
154 f
'test-target-proxy{suffix}')
155 exec_gcloud(project,
'alpha',
'compute',
'target-grpc-proxies',
'delete',
156 f
'test-target-proxy{suffix}')
157 exec_gcloud(project,
'compute',
'url-maps',
'delete', f
'test-map{suffix}')
158 exec_gcloud(project,
'compute',
'backend-services',
'delete',
159 f
'test-backend-service{suffix}',
'--global')
160 exec_gcloud(project,
'compute',
'backend-services',
'delete',
161 f
'test-backend-service-alternate{suffix}',
'--global')
162 exec_gcloud(project,
'compute',
'backend-services',
'delete',
163 f
'test-backend-service-extra{suffix}',
'--global')
164 exec_gcloud(project,
'compute',
'backend-services',
'delete',
165 f
'test-backend-service-more-extra{suffix}',
'--global')
166 exec_gcloud(project,
'compute',
'firewall-rules',
'delete',
167 f
'test-fw-rule{suffix}')
168 exec_gcloud(project,
'compute',
'health-checks',
'delete',
170 exec_gcloud(project,
'compute',
'instance-groups',
'managed',
'delete',
171 f
'test-ig{suffix}',
'--zone', ZONE)
172 exec_gcloud(project,
'compute',
'instance-groups',
'managed',
'delete',
173 f
'test-ig-same-zone{suffix}',
'--zone', ZONE)
174 exec_gcloud(project,
'compute',
'instance-groups',
'managed',
'delete',
175 f
'test-ig-secondary-zone{suffix}',
'--zone', SECONDARY_ZONE)
176 exec_gcloud(project,
'compute',
'instance-templates',
'delete',
177 f
'test-template{suffix}')
187 gcp_api_manager = gcp.api.GcpApiManager()
188 plain_td = traffic_director.TrafficDirectorManager(
192 resource_prefix=resource_prefix,
193 resource_suffix=resource_suffix)
194 security_td = traffic_director.TrafficDirectorSecureManager(
198 resource_prefix=resource_prefix,
199 resource_suffix=resource_suffix)
208 logger.info(
'----- Removing traffic director for gke, prefix %s, suffix %s',
209 resource_prefix, resource_suffix)
210 security_td.cleanup(force=
True)
212 plain_td.cleanup(force=
True)
217 resource_suffix, gcp_service_account):
218 runner_kwargs = dict(
219 deployment_name=xds_flags.CLIENT_NAME.value,
220 image_name=xds_k8s_flags.CLIENT_IMAGE.value,
221 td_bootstrap_image=xds_k8s_flags.TD_BOOTSTRAP_IMAGE.value,
223 gcp_api_manager=gcp.api.GcpApiManager(),
224 gcp_service_account=gcp_service_account,
225 xds_server_uri=xds_flags.XDS_SERVER_URI.value,
227 stats_port=xds_flags.CLIENT_PORT.value)
229 client_namespace = KubernetesClientRunner.make_namespace_name(
230 resource_prefix, resource_suffix)
232 k8s.KubernetesNamespace(k8s_api_manager, client_namespace),
235 logger.info(
'Cleanup client')
236 client_runner.cleanup(force=
True, force_namespace=
True)
241 resource_suffix, gcp_service_account):
242 runner_kwargs = dict(
243 deployment_name=xds_flags.SERVER_NAME.value,
244 image_name=xds_k8s_flags.SERVER_IMAGE.value,
245 td_bootstrap_image=xds_k8s_flags.TD_BOOTSTRAP_IMAGE.value,
247 gcp_api_manager=gcp.api.GcpApiManager(),
248 gcp_service_account=gcp_service_account,
251 server_namespace = KubernetesServerRunner.make_namespace_name(
252 resource_prefix, resource_suffix)
254 k8s.KubernetesNamespace(k8s_api_manager, server_namespace),
257 logger.info(
'Cleanup server')
258 server_runner.cleanup(force=
True, force_namespace=
True)
263 for resource
in resources:
265 logger.info(
'----- Cleaning up resource %s', resource[
'name'])
268 logging.info(
'----- Skipped [Dry Run]: %s', resource[
'name'])
271 for (regex, resource_prefix, keep, remove)
in td_resource_rules:
272 result = re.search(regex, resource[
'name'])
273 if result
is not None:
275 if keep(result.group(1)):
276 logging.info(
'Skipped [keep]:')
278 remove(project, network, resource_prefix, result.group(1))
282 '----- Skipped [does not matching resource name templates]')
286 k8s_api_manager, gcp_service_account, namespaces):
287 for ns
in namespaces:
289 logger.info(
'----- Cleaning up k8s namespaces %s', ns.metadata.name)
293 logging.info(
'----- Skipped [Dry Run]: %s', ns.metadata.name)
297 for (regex, resource_prefix, remove)
in k8s_resource_rules:
298 result = re.search(regex, ns.metadata.name)
299 if result
is not None:
301 remove(project, network, k8s_api_manager, resource_prefix,
302 result.group(1), gcp_service_account)
306 '----- Skipped [does not matching resource name templates]')
308 logging.info(
'----- Skipped [resource is within expiry date]')
312 gcp_service_account):
313 k8s_resource_rules = [
319 for prefix
in CLIENT_PREFIXES.value:
320 k8s_resource_rules.append(
321 (f
'{prefix}-client-(.*)', prefix, cleanup_client),)
322 for prefix
in SERVER_PREFIXES.value:
323 k8s_resource_rules.append(
324 (f
'{prefix}-server-(.*)', prefix, cleanup_server),)
328 k8s_api_manager = k8s.KubernetesApiManager(xds_k8s_flags.KUBE_CONTEXT.value)
329 nss = k8s_api_manager.core.list_namespace()
331 k8s_api_manager, gcp_service_account, nss.items)
336 raise app.UsageError(
'Too many command-line arguments.')
339 project: str = xds_flags.PROJECT.value
340 network: str = xds_flags.NETWORK.value
341 gcp_service_account: str = xds_k8s_flags.GCP_SERVICE_ACCOUNT.value
342 dry_run: bool = DRY_RUN.value
344 td_resource_rules = [
350 (
r'test-hc(.*)',
'', is_marked_as_keep_gce,
351 remove_relative_resources_run_xds_tests),
352 (
r'test-template(.*)',
'', is_marked_as_keep_gce,
353 remove_relative_resources_run_xds_tests),
355 for prefix
in TD_RESOURCE_PREFIXES.value:
356 td_resource_rules.append((f
'{prefix}-health-check-(.*)', prefix,
357 is_marked_as_keep_gke, cleanup_td_for_gke),)
367 compute = gcp.compute.ComputeV1(gcp.api.GcpApiManager(), project)
368 leakedHealthChecks = []
369 for item
in compute.list_health_check()[
'items']:
370 if dateutil.parser.isoparse(
372 leakedHealthChecks.append(item)
380 leakedInstanceTemplates =
exec_gcloud(project,
'compute',
381 'instance-templates',
'list')
383 leakedInstanceTemplates)
389 if __name__ ==
'__main__':