Skip to content

Commit 79fb4a0

Browse files
committed
black formatting
1 parent 6fa74be commit 79fb4a0

2 files changed

Lines changed: 62 additions & 62 deletions

File tree

src/mas/devops/backup.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ def copyContentsToYamlFile(file_path: str, content: dict) -> bool:
3838
# Create a custom dumper that uses literal style for multi-line strings
3939
class LiteralDumper(yaml.SafeDumper):
4040
pass
41-
41+
4242
def str_representer(dumper, data):
4343
if '\n' in data:
4444
return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
4545
return dumper.represent_scalar('tag:yaml.org,2002:str', data)
46-
46+
4747
LiteralDumper.add_representer(str, str_representer)
48-
48+
4949
with open(file_path, 'w') as yaml_file:
5050
yaml.dump(content, yaml_file, default_flow_style=False, Dumper=LiteralDumper)
5151
return True
@@ -75,25 +75,25 @@ def filterResourceData(data: dict) -> dict:
7575

7676
if 'status' in filteredCopy:
7777
del filteredCopy['status']
78-
78+
7979
return filteredCopy
8080

8181

8282
def extract_secrets_from_dict(data, secret_names=None):
8383
"""
8484
Recursively extract secret names from a dictionary structure.
8585
Looks for keys named 'secretName' and collects their values.
86-
86+
8787
Args:
8888
data: Dictionary to search
8989
secret_names: Set to collect secret names (created if None)
90-
90+
9191
Returns:
9292
Set of secret names found
9393
"""
9494
if secret_names is None:
9595
secret_names = set()
96-
96+
9797
if isinstance(data, dict):
9898
for key, value in data.items():
9999
# Check if this key is 'secretName' and has a string value
@@ -102,12 +102,12 @@ def extract_secrets_from_dict(data, secret_names=None):
102102
# Recursively search nested structures
103103
elif isinstance(value, (dict, list)):
104104
extract_secrets_from_dict(value, secret_names)
105-
105+
106106
elif isinstance(data, list):
107107
for item in data:
108108
if isinstance(item, (dict, list)):
109109
extract_secrets_from_dict(item, secret_names)
110-
110+
111111
return secret_names
112112

113113

@@ -116,26 +116,26 @@ def backupResources(dynClient: DynamicClient, namespace: str, kind: str, api_ver
116116
Backup resources of a given kind in a namespace.
117117
If name is provided, backs up that specific resource.
118118
If name is None, backs up all resources of that kind.
119-
119+
120120
Args:
121121
dynClient: Kubernetes dynamic client
122122
namespace: Namespace to backup from
123123
kind: Resource kind (e.g., 'MongoCfg', 'Secret')
124124
api_version: API version (e.g., 'config.mas.ibm.com/v1')
125125
backup_path: Path to save backup files
126126
name: Optional specific resource name
127-
127+
128128
Returns:
129129
tuple: (backed_up_count: int, not_found_count: int, failed_count: int, discovered_secrets: set)
130130
"""
131131
discovered_secrets = set()
132132
backed_up_count = 0
133133
not_found_count = 0
134134
failed_count = 0
135-
135+
136136
try:
137137
resourceAPI = dynClient.resources.get(api_version=api_version, kind=kind)
138-
138+
139139
if name:
140140
# Backup specific named resource
141141
logger.info(f"Backing up {kind} '{name}' from namespace '{namespace}' (API version: {api_version})")
@@ -156,19 +156,19 @@ def backupResources(dynClient: DynamicClient, namespace: str, kind: str, api_ver
156156
logger.info(f"Backing up all {kind} resources from namespace '{namespace}' (API version: {api_version})")
157157
resources = resourceAPI.get(namespace=namespace)
158158
resources_to_process = resources.items
159-
159+
160160
# Process each resource
161161
for resource in resources_to_process:
162162
resource_name = resource["metadata"]["name"]
163163
resource_dict = resource.to_dict()
164-
164+
165165
# Extract secrets from this resource if it's not a Secret itself
166166
if kind != 'Secret':
167167
secrets = extract_secrets_from_dict(resource_dict.get('spec', {}))
168168
if secrets:
169169
logger.info(f"Found {len(secrets)} secret reference(s) in {kind} '{resource_name}': {', '.join(sorted(secrets))}")
170170
discovered_secrets.update(secrets)
171-
171+
172172
# Backup the resource
173173
resource_file_path = f"{backup_path}/{resource_name}.yaml"
174174
filtered_resource = filterResourceData(resource_dict)
@@ -178,14 +178,14 @@ def backupResources(dynClient: DynamicClient, namespace: str, kind: str, api_ver
178178
else:
179179
logger.error(f"Failed to back up {kind} '{resource_name}' to '{resource_file_path}'")
180180
failed_count += 1
181-
181+
182182
if backed_up_count > 0:
183183
logger.info(f"Successfully backed up {backed_up_count} {kind} resource(s)")
184184
elif not name:
185185
logger.info(f"No {kind} resources found in namespace '{namespace}'")
186-
186+
187187
return (backed_up_count, not_found_count, failed_count, discovered_secrets)
188-
188+
189189
except NotFoundError:
190190
if name:
191191
logger.info(f"{kind} '{name}' not found in namespace '{namespace}'")

test/src/test_backup.py

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def test_create_single_directory(self, tmp_path):
2020
"""Test creating a single backup directory"""
2121
test_dir = tmp_path / "backup1"
2222
result = createBackupDirectories([str(test_dir)])
23-
23+
2424
assert result is True
2525
assert test_dir.exists()
2626
assert test_dir.is_dir()
@@ -34,7 +34,7 @@ def test_create_multiple_directories(self, tmp_path):
3434
]
3535
paths = [str(d) for d in test_dirs]
3636
result = createBackupDirectories(paths)
37-
37+
3838
assert result is True
3939
for test_dir in test_dirs:
4040
assert test_dir.exists()
@@ -44,7 +44,7 @@ def test_create_nested_directories(self, tmp_path):
4444
"""Test creating nested backup directories"""
4545
nested_dir = tmp_path / "level1" / "level2" / "level3"
4646
result = createBackupDirectories([str(nested_dir)])
47-
47+
4848
assert result is True
4949
assert nested_dir.exists()
5050
assert nested_dir.is_dir()
@@ -53,9 +53,9 @@ def test_create_existing_directory(self, tmp_path):
5353
"""Test creating a directory that already exists"""
5454
test_dir = tmp_path / "existing"
5555
test_dir.mkdir()
56-
56+
5757
result = createBackupDirectories([str(test_dir)])
58-
58+
5959
assert result is True
6060
assert test_dir.exists()
6161

@@ -67,18 +67,18 @@ def test_create_empty_list(self):
6767
def test_create_directory_permission_error(self, mocker):
6868
"""Test handling of permission errors"""
6969
mock_makedirs = mocker.patch('os.makedirs', side_effect=PermissionError("Permission denied"))
70-
70+
7171
result = createBackupDirectories(["/invalid/path"])
72-
72+
7373
assert result is False
7474
mock_makedirs.assert_called_once()
7575

7676
def test_create_directory_os_error(self, mocker):
7777
"""Test handling of OS errors"""
7878
mocker.patch('os.makedirs', side_effect=OSError("OS error"))
79-
79+
8080
result = createBackupDirectories(["/some/path"])
81-
81+
8282
assert result is False
8383

8484

@@ -89,12 +89,12 @@ def test_write_simple_dict(self, tmp_path):
8989
"""Test writing a simple dictionary to YAML file"""
9090
test_file = tmp_path / "test.yaml"
9191
content = {"key1": "value1", "key2": "value2"}
92-
92+
9393
result = copyContentsToYamlFile(str(test_file), content)
94-
94+
9595
assert result is True
9696
assert test_file.exists()
97-
97+
9898
with open(test_file, 'r') as f:
9999
loaded_content = yaml.safe_load(f)
100100
assert loaded_content == content
@@ -110,9 +110,9 @@ def test_write_nested_dict(self, tmp_path):
110110
},
111111
"list": [1, 2, 3]
112112
}
113-
113+
114114
result = copyContentsToYamlFile(str(test_file), content)
115-
115+
116116
assert result is True
117117
with open(test_file, 'r') as f:
118118
loaded_content = yaml.safe_load(f)
@@ -122,9 +122,9 @@ def test_write_empty_dict(self, tmp_path):
122122
"""Test writing an empty dictionary"""
123123
test_file = tmp_path / "empty.yaml"
124124
content = {}
125-
125+
126126
result = copyContentsToYamlFile(str(test_file), content)
127-
127+
128128
assert result is True
129129
with open(test_file, 'r') as f:
130130
loaded_content = yaml.safe_load(f)
@@ -135,14 +135,14 @@ def test_overwrite_existing_file(self, tmp_path):
135135
test_file = tmp_path / "overwrite.yaml"
136136
old_content = {"old": "data"}
137137
new_content = {"new": "data"}
138-
138+
139139
# Write initial content
140140
with open(test_file, 'w') as f:
141141
yaml.dump(old_content, f)
142-
142+
143143
# Overwrite with new content
144144
result = copyContentsToYamlFile(str(test_file), new_content)
145-
145+
146146
assert result is True
147147
with open(test_file, 'r') as f:
148148
loaded_content = yaml.safe_load(f)
@@ -153,17 +153,17 @@ def test_write_to_nonexistent_directory(self, tmp_path):
153153
"""Test writing to a file in a non-existent directory"""
154154
test_file = tmp_path / "nonexistent" / "test.yaml"
155155
content = {"key": "value"}
156-
156+
157157
result = copyContentsToYamlFile(str(test_file), content)
158-
158+
159159
assert result is False
160160

161161
def test_write_permission_error(self, mocker):
162162
"""Test handling of permission errors during write"""
163163
mocker.patch('builtins.open', side_effect=PermissionError("Permission denied"))
164-
164+
165165
result = copyContentsToYamlFile("/invalid/path.yaml", {"key": "value"})
166-
166+
167167
assert result is False
168168

169169
def test_write_with_special_characters(self, tmp_path):
@@ -174,9 +174,9 @@ def test_write_with_special_characters(self, tmp_path):
174174
"unicode": "café ☕",
175175
"quotes": "value with 'quotes' and \"double quotes\""
176176
}
177-
177+
178178
result = copyContentsToYamlFile(str(test_file), content)
179-
179+
180180
assert result is True
181181
with open(test_file, 'r') as f:
182182
loaded_content = yaml.safe_load(f)
@@ -204,9 +204,9 @@ def test_filter_all_metadata_fields(self):
204204
},
205205
"spec": {"replicas": 3}
206206
}
207-
207+
208208
result = filterResourceData(data)
209-
209+
210210
assert "name" in result["metadata"]
211211
assert "namespace" in result["metadata"]
212212
assert "annotations" not in result["metadata"]
@@ -228,9 +228,9 @@ def test_filter_status_field(self):
228228
"conditions": []
229229
}
230230
}
231-
231+
232232
result = filterResourceData(data)
233-
233+
234234
assert "status" not in result
235235
assert "spec" in result
236236
assert "metadata" in result
@@ -244,9 +244,9 @@ def test_filter_partial_metadata(self):
244244
"labels": {"app": "test"}
245245
}
246246
}
247-
247+
248248
result = filterResourceData(data)
249-
249+
250250
assert "name" in result["metadata"]
251251
assert "labels" in result["metadata"]
252252
assert "uid" not in result["metadata"]
@@ -258,9 +258,9 @@ def test_filter_no_metadata(self):
258258
"kind": "Resource",
259259
"spec": {"replicas": 3}
260260
}
261-
261+
262262
result = filterResourceData(data)
263-
263+
264264
assert "metadata" not in result
265265
assert "spec" in result
266266
assert "apiVersion" in result
@@ -271,9 +271,9 @@ def test_filter_empty_metadata(self):
271271
"metadata": {},
272272
"spec": {"replicas": 3}
273273
}
274-
274+
275275
result = filterResourceData(data)
276-
276+
277277
assert "metadata" in result
278278
assert result["metadata"] == {}
279279

@@ -291,9 +291,9 @@ def test_filter_preserves_other_fields(self):
291291
"key2": "value2"
292292
}
293293
}
294-
294+
295295
result = filterResourceData(data)
296-
296+
297297
assert result["apiVersion"] == "v1"
298298
assert result["kind"] == "ConfigMap"
299299
assert result["data"] == {"key1": "value1", "key2": "value2"}
@@ -308,14 +308,14 @@ def test_filter_shallow_copy_behavior(self):
308308
},
309309
"status": {"phase": "Running"}
310310
}
311-
311+
312312
result = filterResourceData(data)
313-
313+
314314
# Due to shallow copy, nested metadata dict is modified in original
315315
# but top-level status is not (it's deleted from copy only)
316316
assert "uid" not in data["metadata"] # Modified due to shallow copy
317317
assert "status" in data # Not modified (top-level key)
318-
318+
319319
# Result should not have uid and status
320320
assert "uid" not in result["metadata"]
321321
assert "status" not in result
@@ -345,17 +345,17 @@ def test_filter_complex_resource(self):
345345
"readyReplicas": 3
346346
}
347347
}
348-
348+
349349
result = filterResourceData(data)
350-
350+
351351
# Check preserved fields
352352
assert result["apiVersion"] == "apps/v1"
353353
assert result["kind"] == "Deployment"
354354
assert result["metadata"]["name"] == "my-deployment"
355355
assert result["metadata"]["namespace"] == "default"
356356
assert result["metadata"]["labels"] == {"app": "myapp"}
357357
assert result["spec"]["replicas"] == 3
358-
358+
359359
# Check removed fields
360360
assert "annotations" not in result["metadata"]
361361
assert "creationTimestamp" not in result["metadata"]

0 commit comments

Comments
 (0)