1+ from __future__ import annotations
2+
3+ import asyncio
4+ from typing import Any , Dict , List
5+
6+ from src .shared .base_functions import BaseFunction
7+
8+
9+ class ClusterLabelManagement (BaseFunction ):
10+ """
11+ Add / update labels on an Open-Cluster-Management ManagedCluster.
12+
13+ Typical call:
14+ • cluster_name – managedcluster resource name
15+ • labels – dict of key → value
16+ • kube_context – context of the OCM Hub / ITS (default: its1)
17+ """
18+
19+ def __init__ (self ) -> None :
20+ super ().__init__ (
21+ name = "cluster_label_management" ,
22+ description = "Add or update labels on a ManagedCluster object."
23+ )
24+
25+ # ────────────────────────── public entry ──────────────────────────
26+ async def execute (
27+ self ,
28+ cluster_name : str ,
29+ labels : Dict [str , str ] | None = None ,
30+ remove_labels : List [str ] | None = None ,
31+ kube_context : str = "its1" , # default is ITS / OCM hub
32+ kubeconfig : str = "" ,
33+ ** _ : Any ,
34+ ) -> Dict [str , Any ]:
35+ if not cluster_name :
36+ return {"status" : "error" , "error" : "cluster_name is required" }
37+ if not labels and not remove_labels :
38+ return {"status" : "error" , "error" : "labels or remove_labels must be provided" }
39+
40+ label_args : List [str ] = []
41+ if labels :
42+ label_args += [f"{ k } ={ v } " for k , v in labels .items ()]
43+ if remove_labels :
44+ label_args += [f"{ key } -" for key in remove_labels ]
45+ cmd = [
46+ "kubectl" , "--context" , kube_context ,
47+ "label" , "managedcluster" , cluster_name ,
48+ * label_args , "--overwrite" ,
49+ ]
50+ if kubeconfig :
51+ cmd += ["--kubeconfig" , kubeconfig ]
52+
53+ proc = await asyncio .create_subprocess_exec (
54+ * cmd , stdout = asyncio .subprocess .PIPE , stderr = asyncio .subprocess .PIPE
55+ )
56+ stdout , stderr = await proc .communicate ()
57+
58+ if proc .returncode != 0 :
59+ # Surface full stderr so the LLM can show the exact cause
60+ return {
61+ "status" : "error" ,
62+ "stderr" : stderr .decode ().strip (),
63+ "cmd" : " " .join (cmd ),
64+ }
65+
66+ return {
67+ "status" : "success" ,
68+ "stdout" : stdout .decode ().strip (),
69+ "cmd" : " " .join (cmd ),
70+ }
71+
72+ # ────────────────────────── JSON schema ──────────────────────────
73+ def get_schema (self ) -> Dict [str , Any ]:
74+ return {
75+ "type" : "object" ,
76+ "properties" : {
77+ "cluster_name" : {
78+ "type" : "string" ,
79+ "description" : "Name of the ManagedCluster resource" ,
80+ },
81+ "labels" : {
82+ "type" : "object" ,
83+ "description" : "Dictionary of label key/value pairs to add/update" ,
84+ },
85+ "remove_labels" : {
86+ "type" : "array" ,
87+ "items" : {"type" : "string" },
88+ "description" : "List of label keys to delete" ,
89+ },
90+ "kube_context" : {
91+ "type" : "string" ,
92+ "description" : "kubectl context of the OCM hub" ,
93+ "default" : "its1" ,
94+ },
95+ "kubeconfig" : {
96+ "type" : "string" ,
97+ "description" : "Path to alternate kubeconfig (optional)" ,
98+ },
99+ },
100+ "required" : ["cluster_name" ],
101+ }
0 commit comments