Skip to content

Commit 5195c31

Browse files
authored
feat: Add link to GCP Workloads list under xpk workload list (#621)
* feat: Add link to GCP Workloads list under xpk workload list * refactor: Change job_filter to job_name_filter and add docs string * style: fix linter in test_workload
1 parent dfafdcb commit 5195c31

File tree

4 files changed

+95
-2
lines changed

4 files changed

+95
-2
lines changed

src/xpk/commands/workload.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
from ..core.workload import (
8686
check_if_workload_exists,
8787
get_workload_list,
88+
get_workload_list_gcp_link,
8889
wait_for_job_completion,
8990
zone_to_region,
9091
)
@@ -761,4 +762,13 @@ def workload_list(args) -> None:
761762
xpk_print(f'List Job request returned ERROR {return_code}')
762763
xpk_exit(return_code)
763764
xpk_print(f'Workload List Output:\n{return_value}')
765+
766+
workload_list_gcp_link = get_workload_list_gcp_link(
767+
project=args.project,
768+
cluster=args.cluster,
769+
zone=args.zone,
770+
job_name_filter=args.filter_by_job,
771+
)
772+
xpk_print(f'See your workloads in Cloud Console: {workload_list_gcp_link}')
773+
764774
xpk_exit(0)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""
2+
Copyright 2025 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
"""
16+
17+
from xpk.core.workload import get_workload_list_gcp_link
18+
19+
TEST_PROJECT = 'test-project'
20+
TEST_CLUSTER = 'test-cluster'
21+
TEST_ZONE = 'us-central1-c'
22+
LINK_WITHOUT_NAME_FILTER = 'https://console.cloud.google.com/kubernetes/workload/overview?project=test-project&pageState=(%22workload_list_table%22:(%22f%22:%22%255B%257B_22k_22_3A_22Cluster_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22test-cluster_5C_22_22_2C_22i_22_3A_22metadata%252FclusterReference%252Fname_22%257D_2C%257B_22k_22_3A_22Location_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22us-central1_5C_22_22_2C_22i_22_3A_22metadata%252FclusterReference%252FgcpLocation_22%257D_2C%257B_22k_22_3A_22Type_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22Job_5C_22_22_2C_22i_22_3A_22type_meta%252FkindName_22%257D%255D%22))'
23+
LINK_WITH_NAME_FILTER = 'https://console.cloud.google.com/kubernetes/workload/overview?project=test-project&pageState=(%22workload_list_table%22:(%22f%22:%22%255B%257B_22k_22_3A_22Cluster_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22test-cluster_5C_22_22_2C_22i_22_3A_22metadata%252FclusterReference%252Fname_22%257D_2C%257B_22k_22_3A_22Location_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22us-central1_5C_22_22_2C_22i_22_3A_22metadata%252FclusterReference%252FgcpLocation_22%257D_2C%257B_22k_22_3A_22Type_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22Job_5C_22_22_2C_22i_22_3A_22type_meta%252FkindName_22%257D_2C%257B_22k_22_3A_22Name_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22test-workload_5C_22_22_2C_22i_22_3A_22metadata%252Fname_22%257D%255D%22))'
24+
25+
26+
def test_get_workload_list_gcp_link_without_job_name_filter():
27+
result = get_workload_list_gcp_link(
28+
project=TEST_PROJECT,
29+
cluster=TEST_CLUSTER,
30+
zone=TEST_ZONE,
31+
job_name_filter=None,
32+
)
33+
34+
assert result == LINK_WITHOUT_NAME_FILTER
35+
36+
37+
def test_get_workload_list_gcp_link_with_job_name_filter():
38+
result = get_workload_list_gcp_link(
39+
project=TEST_PROJECT,
40+
cluster=TEST_CLUSTER,
41+
zone=TEST_ZONE,
42+
job_name_filter='test-workload',
43+
)
44+
45+
assert result == LINK_WITH_NAME_FILTER
46+
47+
48+
def test_get_workload_list_gcp_link_with_invalid_job_name_filter():
49+
result = get_workload_list_gcp_link(
50+
project=TEST_PROJECT,
51+
cluster=TEST_CLUSTER,
52+
zone=TEST_ZONE,
53+
job_name_filter='test-workload.*',
54+
)
55+
56+
assert result == LINK_WITHOUT_NAME_FILTER

src/xpk/core/workload.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
limitations under the License.
1515
"""
1616

17+
import re
1718
from ..utils.console import xpk_exit, xpk_print
1819
from .commands import run_command_for_value
1920
from .gcloud_context import zone_to_region
@@ -240,3 +241,29 @@ def wait_for_job_completion(args) -> int:
240241
xpk_print('Your workload did not complete successfully')
241242
return 125
242243
return 0
244+
245+
246+
GCP_NAME_FILTER_VALUE_REGEX = re.compile(r'[a-z0-9\-]+')
247+
"""Defines correct name prefix value (contains only letters, numbers and dashes) that can be used in GCP filter chips."""
248+
249+
250+
def get_workload_list_gcp_link(
251+
project: str,
252+
cluster: str,
253+
zone: str,
254+
job_name_filter: str | None,
255+
) -> str:
256+
"""Returns a link to Cloud Console Workloads list"""
257+
258+
filters_encoded = [
259+
f'%257B_22k_22_3A_22Cluster_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22{cluster}_5C_22_22_2C_22i_22_3A_22metadata%252FclusterReference%252Fname_22%257D',
260+
f'%257B_22k_22_3A_22Location_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22{zone_to_region(zone)}_5C_22_22_2C_22i_22_3A_22metadata%252FclusterReference%252FgcpLocation_22%257D',
261+
'%257B_22k_22_3A_22Type_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22Job_5C_22_22_2C_22i_22_3A_22type_meta%252FkindName_22%257D',
262+
]
263+
if job_name_filter and GCP_NAME_FILTER_VALUE_REGEX.fullmatch(job_name_filter):
264+
filters_encoded.append(
265+
f'%257B_22k_22_3A_22Name_22_2C_22t_22_3A10_2C_22v_22_3A_22_5C_22{job_name_filter}_5C_22_22_2C_22i_22_3A_22metadata%252Fname_22%257D'
266+
)
267+
filters_state = '_2C'.join(filters_encoded)
268+
269+
return f'https://console.cloud.google.com/kubernetes/workload/overview?project={project}&pageState=(%22workload_list_table%22:(%22f%22:%22%255B{filters_state}%255D%22))'

src/xpk/parser/workload.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ def set_workload_parsers(workload_parser):
452452
type=str,
453453
help=(
454454
'Filters the arguments based on job name. Provide a regex'
455-
' expressionto parse jobs that match the pattern or provide a job'
455+
' expression to parse jobs that match the pattern or provide a job'
456456
' name to delete a single job.'
457457
),
458458
)
@@ -521,7 +521,7 @@ def set_workload_parsers(workload_parser):
521521
type=str,
522522
help=(
523523
'Filters the arguments based on job name. Provide a regex'
524-
' expressionto parse jobs that match the pattern or provide a job'
524+
' expression to parse jobs that match the pattern or provide a job'
525525
' name to view a single job.'
526526
),
527527
required=False,

0 commit comments

Comments
 (0)