@@ -111,19 +111,22 @@ def extract_secrets_from_dict(data, secret_names=None):
111111 return secret_names
112112
113113
114- def backupResources (dynClient : DynamicClient , namespace : str , kind : str , api_version : str , backup_path : str , name = None ) -> tuple :
114+ def backupResources (dynClient : DynamicClient , kind : str , api_version : str , backup_path : str , namespace = None , name = None , labels = None ) -> tuple :
115115 """
116- Backup resources of a given kind in a namespace .
116+ Backup resources of a given kind.
117117 If name is provided, backs up that specific resource.
118118 If name is None, backs up all resources of that kind.
119+ If namespace is None, backs up cluster-level resources.
120+ If labels is provided, filters resources by label selectors.
119121
120122 Args:
121123 dynClient: Kubernetes dynamic client
122- namespace: Namespace to backup from
123- kind: Resource kind (e.g., 'MongoCfg', 'Secret')
124+ kind: Resource kind (e.g., 'MongoCfg', 'Secret', 'ClusterRole')
124125 api_version: API version (e.g., 'config.mas.ibm.com/v1')
125126 backup_path: Path to save backup files
127+ namespace: Optional namespace to backup from (None for cluster-level resources)
126128 name: Optional specific resource name
129+ labels: Optional list of label selectors (e.g., ['app=myapp', 'env=prod'])
127130
128131 Returns:
129132 tuple: (backed_up_count: int, not_found_count: int, failed_count: int, discovered_secrets: set)
@@ -133,28 +136,49 @@ def backupResources(dynClient: DynamicClient, namespace: str, kind: str, api_ver
133136 not_found_count = 0
134137 failed_count = 0
135138
139+ # Build label selector string if labels provided
140+ label_selector = None
141+ if labels :
142+ label_selector = ',' .join (labels )
143+
144+ # Determine scope description for logging
145+ scope_desc = f"namespace '{ namespace } '" if namespace else "cluster-level"
146+ label_desc = f" with labels [{ label_selector } ]" if label_selector else ""
147+
136148 try :
137149 resourceAPI = dynClient .resources .get (api_version = api_version , kind = kind )
138150
139151 if name :
140152 # Backup specific named resource
141- logger .info (f"Backing up { kind } '{ name } ' from namespace ' { namespace } ' (API version: { api_version } )" )
153+ logger .info (f"Backing up { kind } '{ name } ' from { scope_desc } (API version: { api_version } ){ label_desc } " )
142154 try :
143- resource = resourceAPI .get (name = name , namespace = namespace )
155+ if namespace :
156+ resource = resourceAPI .get (name = name , namespace = namespace )
157+ else :
158+ resource = resourceAPI .get (name = name )
159+
144160 if resource :
145161 resources_to_process = [resource ]
146162 else :
147- logger .info (f"{ kind } '{ name } ' not found in namespace ' { namespace } ' , skipping backup" )
163+ logger .info (f"{ kind } '{ name } ' not found in { scope_desc } , skipping backup" )
148164 not_found_count = 1
149165 return (backed_up_count , not_found_count , failed_count , discovered_secrets )
150166 except NotFoundError :
151- logger .error (f"{ kind } '{ name } ' not found in namespace ' { namespace } ' , skipping backup" )
167+ logger .error (f"{ kind } '{ name } ' not found in { scope_desc } , skipping backup" )
152168 not_found_count = 1
153169 return (backed_up_count , not_found_count , failed_count , discovered_secrets )
154170 else :
155171 # Backup all resources of this kind
156- logger .info (f"Backing up all { kind } resources from namespace '{ namespace } ' (API version: { api_version } )" )
157- resources = resourceAPI .get (namespace = namespace )
172+ logger .info (f"Backing up all { kind } resources from { scope_desc } (API version: { api_version } ){ label_desc } " )
173+
174+ # Build get parameters
175+ get_params = {}
176+ if namespace :
177+ get_params ['namespace' ] = namespace
178+ if label_selector :
179+ get_params ['label_selector' ] = label_selector
180+
181+ resources = resourceAPI .get (** get_params )
158182 resources_to_process = resources .items
159183
160184 # Process each resource
@@ -170,7 +194,9 @@ def backupResources(dynClient: DynamicClient, namespace: str, kind: str, api_ver
170194 discovered_secrets .update (secrets )
171195
172196 # Backup the resource
173- resource_file_path = f"{ backup_path } /{ resource_name } .yaml"
197+ resource_backup_path = f"{ backup_path } /resources/{ kind .lower ()} s"
198+ createBackupDirectories ([resource_backup_path ])
199+ resource_file_path = f"{ resource_backup_path } /{ resource_name } .yaml"
174200 filtered_resource = filterResourceData (resource_dict )
175201 if copyContentsToYamlFile (resource_file_path , filtered_resource ):
176202 logger .info (f"Successfully backed up { kind } '{ resource_name } ' to '{ resource_file_path } '" )
@@ -182,16 +208,16 @@ def backupResources(dynClient: DynamicClient, namespace: str, kind: str, api_ver
182208 if backed_up_count > 0 :
183209 logger .info (f"Successfully backed up { backed_up_count } { kind } resource(s)" )
184210 elif not name :
185- logger .info (f"No { kind } resources found in namespace ' { namespace } ' " )
211+ logger .info (f"No { kind } resources found in { scope_desc } { label_desc } " )
186212
187213 return (backed_up_count , not_found_count , failed_count , discovered_secrets )
188214
189215 except NotFoundError :
190216 if name :
191- logger .info (f"{ kind } '{ name } ' not found in namespace ' { namespace } ' " )
217+ logger .info (f"{ kind } '{ name } ' not found in { scope_desc } " )
192218 not_found_count = 1
193219 else :
194- logger .info (f"No { kind } resources found in namespace ' { namespace } ' " )
220+ logger .info (f"No { kind } resources found in { scope_desc } { label_desc } " )
195221 return (backed_up_count , not_found_count , failed_count , discovered_secrets )
196222 except Exception as e :
197223 logger .error (f"Error backing up { kind } resources: { e } " )
0 commit comments