Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 73aeb93

Browse files
committedMar 26, 2025·
Revive PR #1392: solves #940 and update to latest
(original by @DiptoChakrabarty) - Implement delete_from_yaml functionality - Combine create_from_yaml and delete_from_yaml into yaml_processor, preserving existing signatures for create
1 parent bd32360 commit 73aeb93

File tree

3 files changed

+601
-139
lines changed

3 files changed

+601
-139
lines changed
 

‎kubernetes/e2e_test/test_utils.py

Lines changed: 361 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from os import path
1818

1919
import yaml
20+
import time
2021

2122
from kubernetes import client, utils
2223
from kubernetes.client.rest import ApiException
@@ -36,6 +37,15 @@ def setUpClass(cls):
3637
body = client.V1Namespace(
3738
metadata=client.V1ObjectMeta(
3839
name=cls.test_namespace))
40+
41+
# Delete the namespace if it already exists
42+
try:
43+
core_v1.delete_namespace(name=cls.test_namespace)
44+
time.sleep(10) # Wait for the namespace to be deleted
45+
except ApiException as e:
46+
if e.status != 404:
47+
raise
48+
3949
core_v1.create_namespace(body=body)
4050

4151
@classmethod
@@ -51,15 +61,17 @@ def test_create_apps_deployment_from_yaml(self):
5161
Should be able to create an apps/v1 deployment.
5262
"""
5363
k8s_client = client.api_client.ApiClient(configuration=self.config)
54-
utils.create_from_yaml(
64+
utils.process_from_yaml(
5565
k8s_client, self.path_prefix + "apps-deployment.yaml")
5666
app_api = client.AppsV1Api(k8s_client)
5767
dep = app_api.read_namespaced_deployment(name="nginx-app",
5868
namespace="default")
5969
self.assertIsNotNone(dep)
6070
self.assertEqual("nginx-app", dep.metadata.name)
61-
self.assertEqual("nginx:1.15.4", dep.spec.template.spec.containers[0].image)
62-
self.assertEqual(80, dep.spec.template.spec.containers[0].ports[0].container_port)
71+
self.assertEqual(
72+
"nginx:1.15.4", dep.spec.template.spec.containers[0].image)
73+
self.assertEqual(
74+
80, dep.spec.template.spec.containers[0].ports[0].container_port)
6375
self.assertEqual("nginx", dep.spec.template.spec.containers[0].name)
6476
self.assertEqual("nginx", dep.spec.template.metadata.labels["app"])
6577
self.assertEqual(3, dep.spec.replicas)
@@ -79,28 +91,34 @@ def test_create_apps_deployment_from_yaml_with_apply_is_idempotent(self):
7991
"""
8092
k8s_client = client.api_client.ApiClient(configuration=self.config)
8193
try:
82-
utils.create_from_yaml(
94+
utils.process_from_yaml(
8395
k8s_client, self.path_prefix + "apps-deployment.yaml")
8496
app_api = client.AppsV1Api(k8s_client)
8597
dep = app_api.read_namespaced_deployment(name="nginx-app",
86-
namespace="default")
98+
namespace="default")
8799
self.assertIsNotNone(dep)
88100
self.assertEqual("nginx-app", dep.metadata.name)
89-
self.assertEqual("nginx:1.15.4", dep.spec.template.spec.containers[0].image)
90-
self.assertEqual(80, dep.spec.template.spec.containers[0].ports[0].container_port)
91-
self.assertEqual("nginx", dep.spec.template.spec.containers[0].name)
101+
self.assertEqual(
102+
"nginx:1.15.4", dep.spec.template.spec.containers[0].image)
103+
self.assertEqual(
104+
80, dep.spec.template.spec.containers[0].ports[0].container_port)
105+
self.assertEqual(
106+
"nginx", dep.spec.template.spec.containers[0].name)
92107
self.assertEqual("nginx", dep.spec.template.metadata.labels["app"])
93108
self.assertEqual(3, dep.spec.replicas)
94109

95-
utils.create_from_yaml(
110+
utils.process_from_yaml(
96111
k8s_client, self.path_prefix + "apps-deployment.yaml", apply=True)
97112
dep = app_api.read_namespaced_deployment(name="nginx-app",
98-
namespace="default")
113+
namespace="default")
99114
self.assertIsNotNone(dep)
100115
self.assertEqual("nginx-app", dep.metadata.name)
101-
self.assertEqual("nginx:1.15.4", dep.spec.template.spec.containers[0].image)
102-
self.assertEqual(80, dep.spec.template.spec.containers[0].ports[0].container_port)
103-
self.assertEqual("nginx", dep.spec.template.spec.containers[0].name)
116+
self.assertEqual(
117+
"nginx:1.15.4", dep.spec.template.spec.containers[0].image)
118+
self.assertEqual(
119+
80, dep.spec.template.spec.containers[0].ports[0].container_port)
120+
self.assertEqual(
121+
"nginx", dep.spec.template.spec.containers[0].name)
104122
self.assertEqual("nginx", dep.spec.template.metadata.labels["app"])
105123
self.assertEqual(3, dep.spec.replicas)
106124
except Exception as e:
@@ -123,7 +141,7 @@ def test_create_apps_deployment_from_yaml_object(self):
123141
_path = self.path_prefix + "apps-deployment.yaml"
124142
with open(path.abspath(_path)) as f:
125143
yaml_objects = yaml.safe_load_all(f)
126-
utils.create_from_yaml(
144+
utils.process_from_yaml(
127145
k8s_client,
128146
yaml_objects=yaml_objects,
129147
)
@@ -132,8 +150,10 @@ def test_create_apps_deployment_from_yaml_object(self):
132150
namespace="default")
133151
self.assertIsNotNone(dep)
134152
self.assertEqual("nginx-app", dep.metadata.name)
135-
self.assertEqual("nginx:1.15.4", dep.spec.template.spec.containers[0].image)
136-
self.assertEqual(80, dep.spec.template.spec.containers[0].ports[0].container_port)
153+
self.assertEqual(
154+
"nginx:1.15.4", dep.spec.template.spec.containers[0].image)
155+
self.assertEqual(
156+
80, dep.spec.template.spec.containers[0].ports[0].container_port)
137157
self.assertEqual("nginx", dep.spec.template.spec.containers[0].name)
138158
self.assertEqual("nginx", dep.spec.template.metadata.labels["app"])
139159
self.assertEqual(3, dep.spec.replicas)
@@ -154,15 +174,17 @@ def test_create_apps_deployment_from_yaml_obj(self):
154174

155175
yml_obj["metadata"]["name"] = "nginx-app-3"
156176

157-
utils.create_from_dict(k8s_client, yml_obj)
177+
utils.process_from_dict(k8s_client, yml_obj)
158178

159179
app_api = client.AppsV1Api(k8s_client)
160180
dep = app_api.read_namespaced_deployment(name="nginx-app-3",
161181
namespace="default")
162182
self.assertIsNotNone(dep)
163183
self.assertEqual("nginx-app-3", dep.metadata.name)
164-
self.assertEqual("nginx:1.15.4", dep.spec.template.spec.containers[0].image)
165-
self.assertEqual(80, dep.spec.template.spec.containers[0].ports[0].container_port)
184+
self.assertEqual(
185+
"nginx:1.15.4", dep.spec.template.spec.containers[0].image)
186+
self.assertEqual(
187+
80, dep.spec.template.spec.containers[0].ports[0].container_port)
166188
self.assertEqual("nginx", dep.spec.template.spec.containers[0].name)
167189
self.assertEqual("nginx", dep.spec.template.metadata.labels["app"])
168190
self.assertEqual(3, dep.spec.replicas)
@@ -176,7 +198,7 @@ def test_create_pod_from_yaml(self):
176198
Should be able to create a pod.
177199
"""
178200
k8s_client = client.api_client.ApiClient(configuration=self.config)
179-
utils.create_from_yaml(
201+
utils.process_from_yaml(
180202
k8s_client, self.path_prefix + "core-pod.yaml")
181203
core_api = client.CoreV1Api(k8s_client)
182204
pod = core_api.read_namespaced_pod(name="myapp-pod",
@@ -195,7 +217,7 @@ def test_create_service_from_yaml(self):
195217
Should be able to create a service.
196218
"""
197219
k8s_client = client.api_client.ApiClient(configuration=self.config)
198-
utils.create_from_yaml(
220+
utils.process_from_yaml(
199221
k8s_client, self.path_prefix + "core-service.yaml")
200222
core_api = client.CoreV1Api(k8s_client)
201223
svc = core_api.read_namespaced_service(name="my-service",
@@ -213,7 +235,7 @@ def test_create_namespace_from_yaml(self):
213235
Should be able to create a namespace.
214236
"""
215237
k8s_client = client.api_client.ApiClient(configuration=self.config)
216-
utils.create_from_yaml(
238+
utils.process_from_yaml(
217239
k8s_client, self.path_prefix + "core-namespace.yaml")
218240
core_api = client.CoreV1Api(k8s_client)
219241
nmsp = core_api.read_namespace(name="development")
@@ -228,7 +250,7 @@ def test_create_rbac_role_from_yaml(self):
228250
Should be able to create a rbac role.
229251
"""
230252
k8s_client = client.api_client.ApiClient(configuration=self.config)
231-
utils.create_from_yaml(
253+
utils.process_from_yaml(
232254
k8s_client, self.path_prefix + "rbac-role.yaml")
233255
rbac_api = client.RbacAuthorizationV1Api(k8s_client)
234256
rbac_role = rbac_api.read_namespaced_role(
@@ -245,7 +267,7 @@ def test_create_rbac_role_from_yaml_with_verbose_enabled(self):
245267
Should be able to create a rbac role with verbose enabled.
246268
"""
247269
k8s_client = client.api_client.ApiClient(configuration=self.config)
248-
utils.create_from_yaml(
270+
utils.process_from_yaml(
249271
k8s_client, self.path_prefix + "rbac-role.yaml", verbose=True)
250272
rbac_api = client.RbacAuthorizationV1Api(k8s_client)
251273
rbac_role = rbac_api.read_namespaced_role(
@@ -263,9 +285,9 @@ def test_create_deployment_non_default_namespace_from_yaml(self):
263285
and then create a deployment in the just-created namespace.
264286
"""
265287
k8s_client = client.ApiClient(configuration=self.config)
266-
utils.create_from_yaml(
288+
utils.process_from_yaml(
267289
k8s_client, self.path_prefix + "dep-namespace.yaml")
268-
utils.create_from_yaml(
290+
utils.process_from_yaml(
269291
k8s_client, self.path_prefix + "dep-deployment.yaml")
270292
core_api = client.CoreV1Api(k8s_client)
271293
ext_api = client.AppsV1Api(k8s_client)
@@ -288,7 +310,7 @@ def test_create_apiservice_from_yaml_with_conflict(self):
288310
fail due to conflict.
289311
"""
290312
k8s_client = client.api_client.ApiClient(configuration=self.config)
291-
utils.create_from_yaml(
313+
utils.process_from_yaml(
292314
k8s_client, self.path_prefix + "api-service.yaml")
293315
reg_api = client.ApiregistrationV1Api(k8s_client)
294316
svc = reg_api.read_api_service(
@@ -298,8 +320,8 @@ def test_create_apiservice_from_yaml_with_conflict(self):
298320
self.assertEqual("wardle.k8s.io", svc.spec.group)
299321
self.assertEqual("v1alpha1", svc.spec.version)
300322

301-
with self.assertRaises(utils.FailToCreateError) as cm:
302-
utils.create_from_yaml(
323+
with self.assertRaises(utils.FailToProcessError) as cm:
324+
utils.process_from_yaml(
303325
k8s_client, "kubernetes/e2e_test/test_yaml/api-service.yaml")
304326
exp_error = ('Error from server (Conflict): '
305327
'{"kind":"Status","apiVersion":"v1","metadata":{},'
@@ -323,7 +345,7 @@ def test_create_general_list_from_yaml(self):
323345
from a kind: List yaml file
324346
"""
325347
k8s_client = client.api_client.ApiClient(configuration=self.config)
326-
utils.create_from_yaml(
348+
utils.process_from_yaml(
327349
k8s_client, self.path_prefix + "list.yaml")
328350
core_api = client.CoreV1Api(k8s_client)
329351
ext_api = client.AppsV1Api(k8s_client)
@@ -337,7 +359,8 @@ def test_create_general_list_from_yaml(self):
337359
namespace="default")
338360
self.assertIsNotNone(dep)
339361
self.assertEqual("list-deployment-test", dep.metadata.name)
340-
self.assertEqual("nginx:1.15.4", dep.spec.template.spec.containers[0].image)
362+
self.assertEqual(
363+
"nginx:1.15.4", dep.spec.template.spec.containers[0].image)
341364
self.assertEqual(1, dep.spec.replicas)
342365

343366
core_api.delete_namespaced_service(name="list-service-test",
@@ -351,7 +374,7 @@ def test_create_namespace_list_from_yaml(self):
351374
from a kind: NamespaceList yaml file
352375
"""
353376
k8s_client = client.api_client.ApiClient(configuration=self.config)
354-
utils.create_from_yaml(
377+
utils.process_from_yaml(
355378
k8s_client, self.path_prefix + "namespace-list.yaml")
356379
core_api = client.CoreV1Api(k8s_client)
357380
nmsp_1 = core_api.read_namespace(name="mock-1")
@@ -373,8 +396,8 @@ def test_create_implicit_service_list_from_yaml_with_conflict(self):
373396
json file that implicitly indicates the kind of individual objects
374397
"""
375398
k8s_client = client.api_client.ApiClient(configuration=self.config)
376-
with self.assertRaises(utils.FailToCreateError):
377-
utils.create_from_yaml(
399+
with self.assertRaises(utils.FailToProcessError):
400+
utils.process_from_yaml(
378401
k8s_client, self.path_prefix + "implicit-svclist.json")
379402
core_api = client.CoreV1Api(k8s_client)
380403
svc_3 = core_api.read_namespaced_service(name="mock-3",
@@ -402,7 +425,7 @@ def test_create_multi_resource_from_directory(self):
402425
from a directory
403426
"""
404427
k8s_client = client.api_client.ApiClient(configuration=self.config)
405-
utils.create_from_directory(
428+
utils.process_from_directory(
406429
k8s_client, self.path_prefix + "multi-resource/")
407430
core_api = client.CoreV1Api(k8s_client)
408431
svc = core_api.read_namespaced_service(name="mock",
@@ -419,8 +442,10 @@ def test_create_multi_resource_from_directory(self):
419442
self.assertEqual("mock", ctr.spec.template.metadata.labels["app"])
420443
self.assertEqual("mock", ctr.spec.selector["app"])
421444
self.assertEqual(1, ctr.spec.replicas)
422-
self.assertEqual("k8s.gcr.io/pause:2.0", ctr.spec.template.spec.containers[0].image)
423-
self.assertEqual("mock-container", ctr.spec.template.spec.containers[0].name)
445+
self.assertEqual("k8s.gcr.io/pause:2.0",
446+
ctr.spec.template.spec.containers[0].image)
447+
self.assertEqual("mock-container",
448+
ctr.spec.template.spec.containers[0].name)
424449

425450
core_api.delete_namespaced_replication_controller(
426451
name="mock", namespace="default", propagation_policy="Background")
@@ -435,7 +460,7 @@ def test_create_from_multi_resource_yaml(self):
435460
from a multi-resource yaml file
436461
"""
437462
k8s_client = client.api_client.ApiClient(configuration=self.config)
438-
utils.create_from_yaml(
463+
utils.process_from_yaml(
439464
k8s_client, self.path_prefix + "multi-resource.yaml")
440465
core_api = client.CoreV1Api(k8s_client)
441466
svc = core_api.read_namespaced_service(name="mock",
@@ -452,8 +477,10 @@ def test_create_from_multi_resource_yaml(self):
452477
self.assertEqual("mock", ctr.spec.template.metadata.labels["app"])
453478
self.assertEqual("mock", ctr.spec.selector["app"])
454479
self.assertEqual(1, ctr.spec.replicas)
455-
self.assertEqual("k8s.gcr.io/pause:2.0", ctr.spec.template.spec.containers[0].image)
456-
self.assertEqual("mock-container", ctr.spec.template.spec.containers[0].name)
480+
self.assertEqual("k8s.gcr.io/pause:2.0",
481+
ctr.spec.template.spec.containers[0].image)
482+
self.assertEqual("mock-container",
483+
ctr.spec.template.spec.containers[0].name)
457484

458485
core_api.delete_namespaced_replication_controller(
459486
name="mock", namespace="default", propagation_policy="Background")
@@ -466,7 +493,7 @@ def test_create_from_list_in_multi_resource_yaml(self):
466493
specified in the multi-resource file
467494
"""
468495
k8s_client = client.api_client.ApiClient(configuration=self.config)
469-
utils.create_from_yaml(
496+
utils.process_from_yaml(
470497
k8s_client, self.path_prefix + "multi-resource-with-list.yaml")
471498
core_api = client.CoreV1Api(k8s_client)
472499
app_api = client.AppsV1Api(k8s_client)
@@ -508,7 +535,7 @@ def test_create_from_multi_resource_yaml_with_conflict(self):
508535
Should raise an exception for failure to create the same service twice.
509536
"""
510537
k8s_client = client.api_client.ApiClient(configuration=self.config)
511-
utils.create_from_yaml(
538+
utils.process_from_yaml(
512539
k8s_client, self.path_prefix + "yaml-conflict-first.yaml")
513540
core_api = client.CoreV1Api(k8s_client)
514541
svc = core_api.read_namespaced_service(name="mock-2",
@@ -519,8 +546,8 @@ def test_create_from_multi_resource_yaml_with_conflict(self):
519546
self.assertEqual("mock-2", svc.spec.selector["app"])
520547
self.assertEqual(99, svc.spec.ports[0].port)
521548

522-
with self.assertRaises(utils.FailToCreateError) as cm:
523-
utils.create_from_yaml(
549+
with self.assertRaises(utils.FailToProcessError) as cm:
550+
utils.process_from_yaml(
524551
k8s_client, self.path_prefix + "yaml-conflict-multi.yaml")
525552
exp_error = ('Error from server (Conflict): {"kind":"Status",'
526553
'"apiVersion":"v1","metadata":{},"status":"Failure",'
@@ -544,8 +571,8 @@ def test_create_from_multi_resource_yaml_with_multi_conflicts(self):
544571
Should raise an exception that contains two error messages.
545572
"""
546573
k8s_client = client.api_client.ApiClient(configuration=self.config)
547-
with self.assertRaises(utils.FailToCreateError) as cm:
548-
utils.create_from_yaml(
574+
with self.assertRaises(utils.FailToProcessError) as cm:
575+
utils.process_from_yaml(
549576
k8s_client, self.path_prefix + "triple-nginx.yaml")
550577
exp_error = ('Error from server (Conflict): {"kind":"Status",'
551578
'"apiVersion":"v1","metadata":{},"status":"Failure",'
@@ -570,7 +597,7 @@ def test_create_namespaced_apps_deployment_from_yaml(self):
570597
in a test namespace.
571598
"""
572599
k8s_client = client.api_client.ApiClient(configuration=self.config)
573-
utils.create_from_yaml(
600+
utils.process_from_yaml(
574601
k8s_client, self.path_prefix + "apps-deployment.yaml",
575602
namespace=self.test_namespace)
576603
app_api = client.AppsV1Api(k8s_client)
@@ -587,7 +614,7 @@ def test_create_from_list_in_multi_resource_yaml_namespaced(self):
587614
specified in the multi-resource file in a test namespace
588615
"""
589616
k8s_client = client.api_client.ApiClient(configuration=self.config)
590-
utils.create_from_yaml(
617+
utils.process_from_yaml(
591618
k8s_client, self.path_prefix + "multi-resource-with-list.yaml",
592619
namespace=self.test_namespace)
593620
core_api = client.CoreV1Api(k8s_client)
@@ -608,6 +635,254 @@ def test_create_from_list_in_multi_resource_yaml_namespaced(self):
608635
app_api.delete_namespaced_deployment(
609636
name="mock", namespace=self.test_namespace, body={})
610637

638+
def test_delete_namespace_from_yaml(self):
639+
"""
640+
Should be able to delete a namespace
641+
Create namespace from file first and ensure it is created
642+
"""
643+
k8s_client = client.api_client.ApiClient(configuration=self.config)
644+
utils.process_from_yaml(
645+
k8s_client, self.path_prefix + "core-namespace.yaml")
646+
core_api = client.CoreV1Api(k8s_client)
647+
nmsp = core_api.read_namespace(name="development")
648+
self.assertIsNotNone(nmsp)
649+
"""
650+
Delete namespace from yaml
651+
"""
652+
utils.process_from_yaml(
653+
k8s_client, self.path_prefix + "core-namespace.yaml", action="delete")
654+
time.sleep(10)
655+
namespace_status = False
656+
try:
657+
nmsp = core_api.read_namespace(name="development")
658+
namespace_status = True
659+
except Exception as e:
660+
self.assertFalse(namespace_status)
661+
self.assertFalse(namespace_status)
662+
663+
def test_delete_apps_deployment_from_yaml(self):
664+
"""
665+
Should delete a deployment
666+
First create deployment from file and ensure it is created
667+
"""
668+
k8s_client = client.api_client.ApiClient(configuration=self.config)
669+
utils.process_from_yaml(
670+
k8s_client, self.path_prefix + "apps-deployment.yaml")
671+
app_api = client.AppsV1Api(k8s_client)
672+
dep = app_api.read_namespaced_deployment(name="nginx-app",
673+
namespace="default")
674+
self.assertIsNotNone(dep)
675+
"""
676+
Deployment should be created
677+
Now delete deployment using delete_from_yaml method
678+
"""
679+
utils.process_from_yaml(
680+
k8s_client, self.path_prefix + "apps-deployment.yaml", action="delete")
681+
deployment_status = False
682+
time.sleep(10)
683+
try:
684+
response = app_api.read_namespaced_deployment(
685+
name="nginx-app", namespace="default")
686+
deployment_status = True
687+
except Exception as e:
688+
self.assertFalse(deployment_status)
689+
690+
self.assertFalse(deployment_status)
691+
692+
def test_delete_service_from_yaml(self):
693+
"""
694+
Should be able to delete a service
695+
Create service from yaml first and ensure it is created
696+
"""
697+
k8s_client = client.api_client.ApiClient(configuration=self.config)
698+
utils.process_from_yaml(
699+
k8s_client, self.path_prefix + "core-service.yaml")
700+
core_api = client.CoreV1Api(k8s_client)
701+
svc = core_api.read_namespaced_service(name="my-service",
702+
namespace="default")
703+
self.assertIsNotNone(svc)
704+
"""
705+
Delete service from yaml
706+
"""
707+
utils.process_from_yaml(
708+
k8s_client, self.path_prefix + "core-service.yaml", action="delete")
709+
service_status = False
710+
time.sleep(10)
711+
try:
712+
response = core_api.read_namespaced_service(
713+
name="my-service", namespace="default")
714+
service_status = True
715+
except Exception as e:
716+
self.assertFalse(service_status)
717+
self.assertFalse(service_status)
718+
719+
def test_delete_pod_from_yaml(self):
720+
"""
721+
Should be able to delete pod
722+
Create pod from file first and ensure it is created
723+
"""
724+
k8s_client = client.api_client.ApiClient(configuration=self.config)
725+
utils.process_from_yaml(
726+
k8s_client, self.path_prefix + "core-pod.yaml")
727+
core_api = client.CoreV1Api(k8s_client)
728+
pod = core_api.read_namespaced_pod(name="myapp-pod",
729+
namespace="default")
730+
self.assertIsNotNone(pod)
731+
"""
732+
Delete pod using delete_from_yaml
733+
"""
734+
utils.process_from_yaml(
735+
k8s_client, self.path_prefix + "core-pod.yaml", action="delete")
736+
time.sleep(10)
737+
pod_status = False
738+
try:
739+
response = core_api.read_namespaced_pod(name="myapp-pod",
740+
namespace="default")
741+
pod_status = True
742+
except Exception as e:
743+
self.assertFalse(pod_status)
744+
self.assertFalse(pod_status)
745+
746+
def test_delete_rbac_role_from_yaml(self):
747+
"""
748+
Should be able to delete rbac role
749+
Create rbac role from file first and ensure it is created
750+
"""
751+
k8s_client = client.api_client.ApiClient(configuration=self.config)
752+
utils.process_from_yaml(
753+
k8s_client, self.path_prefix + "rbac-role.yaml")
754+
rbac_api = client.RbacAuthorizationV1Api(k8s_client)
755+
rbac_role = rbac_api.read_namespaced_role(
756+
name="pod-reader", namespace="default")
757+
self.assertIsNotNone(rbac_role)
758+
"""
759+
Delete rbac role from yaml
760+
"""
761+
utils.process_from_yaml(
762+
k8s_client, self.path_prefix + "rbac-role.yaml", action="delete")
763+
rbac_role_status = False
764+
time.sleep(10)
765+
try:
766+
response = rbac_api.read_namespaced_role(
767+
name="pod-reader", namespace="default")
768+
rbac_role_status = True
769+
except Exception as e:
770+
self.assertFalse(rbac_role_status)
771+
self.assertFalse(rbac_role_status)
772+
773+
def test_delete_rbac_role_from_yaml_with_verbose_enabled(self):
774+
"""
775+
Should delete a rbac role with verbose enabled
776+
Create rbac role with verbose enabled and ensure it is created
777+
"""
778+
k8s_client = client.api_client.ApiClient(configuration=self.config)
779+
utils.process_from_yaml(
780+
k8s_client, self.path_prefix + "rbac-role.yaml", verbose=True)
781+
rbac_api = client.RbacAuthorizationV1Api(k8s_client)
782+
rbac_role = rbac_api.read_namespaced_role(
783+
name="pod-reader", namespace="default")
784+
self.assertIsNotNone(rbac_role)
785+
"""
786+
Delete the rbac role from yaml
787+
"""
788+
utils.process_from_yaml(
789+
k8s_client, self.path_prefix + "rbac-role.yaml", verbose=True, action="delete")
790+
791+
rbac_role_status = False
792+
time.sleep(10)
793+
try:
794+
response = rbac_api.read_namespaced_role(
795+
name="pod-reader", namespace="default")
796+
rbac_role_status = True
797+
except Exception as e:
798+
self.assertFalse(rbac_role_status)
799+
self.assertFalse(rbac_role_status)
800+
801+
# Deletion Tests for multi resource objects in yaml files
802+
803+
def test_delete_from_multi_resource_yaml(self):
804+
"""
805+
Should be able to delete service and replication controller
806+
from the multi resource yaml files
807+
Create the resources first and ensure they exist
808+
"""
809+
k8s_client = client.api_client.ApiClient(configuration=self.config)
810+
utils.process_from_yaml(
811+
k8s_client, self.path_prefix + "multi-resource.yaml")
812+
core_api = client.CoreV1Api(k8s_client)
813+
svc = core_api.read_namespaced_service(name="mock",
814+
namespace="default")
815+
self.assertIsNotNone(svc)
816+
ctr = core_api.read_namespaced_replication_controller(
817+
name="mock", namespace="default")
818+
self.assertIsNotNone(ctr)
819+
"""
820+
Delete service and replication controller using yaml file
821+
"""
822+
utils.process_from_yaml(
823+
k8s_client, self.path_prefix + "multi-resource.yaml", action="delete")
824+
svc_status = False
825+
replication_status = False
826+
time.sleep(10)
827+
try:
828+
resp_svc = core_api.read_namespaced_service(name="mock",
829+
namespace="default")
830+
svc_status = True
831+
resp_repl = core_api.read_namespaced_replication_controller(
832+
name="mock", namespace="default")
833+
repl_status = True
834+
except Exception as e:
835+
self.assertFalse(svc_status)
836+
self.assertFalse(replication_status)
837+
self.assertFalse(svc_status)
838+
self.assertFalse(replication_status)
839+
840+
def test_delete_from_list_in_multi_resource_yaml(self):
841+
"""
842+
Should delete the items in PodList and the deployment in the yaml file
843+
Create the items first and ensure they exist
844+
"""
845+
k8s_client = client.api_client.ApiClient(configuration=self.config)
846+
utils.process_from_yaml(
847+
k8s_client, self.path_prefix + "multi-resource-with-list.yaml")
848+
core_api = client.CoreV1Api(k8s_client)
849+
app_api = client.AppsV1Api(k8s_client)
850+
pod_0 = core_api.read_namespaced_pod(
851+
name="mock-pod-0", namespace="default")
852+
self.assertIsNotNone(pod_0)
853+
pod_1 = core_api.read_namespaced_pod(
854+
name="mock-pod-1", namespace="default")
855+
self.assertIsNotNone(pod_1)
856+
dep = app_api.read_namespaced_deployment(
857+
name="mock", namespace="default")
858+
self.assertIsNotNone(dep)
859+
"""
860+
Delete the PodList and Deployment using the yaml file
861+
"""
862+
utils.process_from_yaml(
863+
k8s_client, self.path_prefix + "multi-resource-with-list.yaml", action="delete")
864+
time.sleep(10)
865+
pod0_status = False
866+
pod1_status = False
867+
deploy_status = False
868+
try:
869+
core_api.read_namespaced_pod(
870+
name="mock-pod-0", namespace="default")
871+
core_api.read_namespaced_pod(
872+
name="mock-pod-1", namespace="default")
873+
app_api.read_namespaced_deployment(
874+
name="mock", namespace="default")
875+
pod0_status = True
876+
pod1_status = True
877+
deploy_status = True
878+
except Exception as e:
879+
self.assertFalse(pod0_status)
880+
self.assertFalse(pod1_status)
881+
self.assertFalse(deploy_status)
882+
self.assertFalse(pod0_status)
883+
self.assertFalse(pod1_status)
884+
self.assertFalse(deploy_status)
885+
611886

612887
class TestUtilsUnitTests(unittest.TestCase):
613888

@@ -624,7 +899,8 @@ def test_parse_quantity(self):
624899
self.assertRaises(
625900
ValueError, lambda: quantity.parse_quantity("1000ki")
626901
)
627-
self.assertRaises(ValueError, lambda: quantity.parse_quantity("1000foo"))
902+
self.assertRaises(
903+
ValueError, lambda: quantity.parse_quantity("1000foo"))
628904
self.assertRaises(ValueError, lambda: quantity.parse_quantity("foo"))
629905

630906
# == no suffix ==
@@ -641,14 +917,18 @@ def test_parse_quantity(self):
641917
self.assertEqual(quantity.parse_quantity("0.5Ki"), Decimal(512))
642918

643919
# == base 1000 ==
644-
self.assertAlmostEqual(quantity.parse_quantity("1n"), Decimal(0.000_000_001))
645-
self.assertAlmostEqual(quantity.parse_quantity("1u"), Decimal(0.000_001))
920+
self.assertAlmostEqual(quantity.parse_quantity(
921+
"1n"), Decimal(0.000_000_001))
922+
self.assertAlmostEqual(
923+
quantity.parse_quantity("1u"), Decimal(0.000_001))
646924
self.assertAlmostEqual(quantity.parse_quantity("1m"), Decimal(0.001))
647925
self.assertEqual(quantity.parse_quantity("1k"), Decimal(1_000))
648926
self.assertEqual(quantity.parse_quantity("1M"), Decimal(1_000_000))
649927
self.assertEqual(quantity.parse_quantity("1G"), Decimal(1_000_000_000))
650-
self.assertEqual(quantity.parse_quantity("1T"), Decimal(1_000_000_000_000))
651-
self.assertEqual(quantity.parse_quantity("1P"), Decimal(1_000_000_000_000_000))
928+
self.assertEqual(quantity.parse_quantity(
929+
"1T"), Decimal(1_000_000_000_000))
930+
self.assertEqual(quantity.parse_quantity(
931+
"1P"), Decimal(1_000_000_000_000_000))
652932
self.assertEqual(
653933
quantity.parse_quantity("1E"), Decimal(1_000_000_000_000_000_000))
654934
self.assertEqual(quantity.parse_quantity("1000k"), Decimal(1_000_000))
@@ -671,17 +951,25 @@ def test_format_quantity(self):
671951

672952
# == no suffix ==
673953
self.assertEqual(quantity.format_quantity(Decimal(1_000), ""), "1000")
674-
self.assertEqual(quantity.format_quantity(Decimal(1_000), None), "1000")
954+
self.assertEqual(quantity.format_quantity(
955+
Decimal(1_000), None), "1000")
675956

676957
# == base 1024 ==
677958
self.assertEqual(quantity.format_quantity(Decimal(1024), "Ki"), "1Ki")
678-
self.assertEqual(quantity.format_quantity(Decimal(1024**2), "Mi"), "1Mi")
679-
self.assertEqual(quantity.format_quantity(Decimal(1024**3), "Gi"), "1Gi")
680-
self.assertEqual(quantity.format_quantity(Decimal(1024**4), "Ti"), "1Ti")
681-
self.assertEqual(quantity.format_quantity(Decimal(1024**5), "Pi"), "1Pi")
682-
self.assertEqual(quantity.format_quantity(Decimal(1024**6), "Ei"), "1Ei")
683-
self.assertEqual(quantity.format_quantity(Decimal(1024**2), "Ki"), "1024Ki")
684-
self.assertEqual(quantity.format_quantity(Decimal((1024**3) / 2), "Gi"), "0.5Gi")
959+
self.assertEqual(quantity.format_quantity(
960+
Decimal(1024**2), "Mi"), "1Mi")
961+
self.assertEqual(quantity.format_quantity(
962+
Decimal(1024**3), "Gi"), "1Gi")
963+
self.assertEqual(quantity.format_quantity(
964+
Decimal(1024**4), "Ti"), "1Ti")
965+
self.assertEqual(quantity.format_quantity(
966+
Decimal(1024**5), "Pi"), "1Pi")
967+
self.assertEqual(quantity.format_quantity(
968+
Decimal(1024**6), "Ei"), "1Ei")
969+
self.assertEqual(quantity.format_quantity(
970+
Decimal(1024**2), "Ki"), "1024Ki")
971+
self.assertEqual(quantity.format_quantity(
972+
Decimal((1024**3) / 2), "Gi"), "0.5Gi")
685973
# Decimal((1024**3)/3) are 0.3333333333333333148296162562Gi; expecting to
686974
# be quantized to 0.3Gi
687975
self.assertEqual(
@@ -693,22 +981,28 @@ def test_format_quantity(self):
693981
"0.3Gi")
694982

695983
# == base 1000 ==
696-
self.assertEqual(quantity.format_quantity(Decimal(0.000_000_001), "n"), "1n")
697-
self.assertEqual(quantity.format_quantity(Decimal(0.000_001), "u"), "1u")
984+
self.assertEqual(quantity.format_quantity(
985+
Decimal(0.000_000_001), "n"), "1n")
986+
self.assertEqual(quantity.format_quantity(
987+
Decimal(0.000_001), "u"), "1u")
698988
self.assertEqual(quantity.format_quantity(Decimal(0.001), "m"), "1m")
699989
self.assertEqual(quantity.format_quantity(Decimal(1_000), "k"), "1k")
700-
self.assertEqual(quantity.format_quantity(Decimal(1_000_000), "M"), "1M")
701-
self.assertEqual(quantity.format_quantity(Decimal(1_000_000_000), "G"), "1G")
990+
self.assertEqual(quantity.format_quantity(
991+
Decimal(1_000_000), "M"), "1M")
992+
self.assertEqual(quantity.format_quantity(
993+
Decimal(1_000_000_000), "G"), "1G")
702994
self.assertEqual(
703995
quantity.format_quantity(Decimal(1_000_000_000_000), "T"), "1T"
704996
)
705997
self.assertEqual(
706998
quantity.format_quantity(Decimal(1_000_000_000_000_000), "P"), "1P"
707999
)
7081000
self.assertEqual(
709-
quantity.format_quantity(Decimal(1_000_000_000_000_000_000), "E"), "1E"
1001+
quantity.format_quantity(
1002+
Decimal(1_000_000_000_000_000_000), "E"), "1E"
7101003
)
711-
self.assertEqual(quantity.format_quantity(Decimal(1_000_000), "k"), "1000k")
1004+
self.assertEqual(quantity.format_quantity(
1005+
Decimal(1_000_000), "k"), "1000k")
7121006
# Decimal(1_000_000/3) are 333.3333333333333139307796955k; expecting to
7131007
# be quantized to 333k
7141008
self.assertEqual(

‎kubernetes/utils/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414

1515
from __future__ import absolute_import
1616

17-
from .create_from_yaml import (FailToCreateError, create_from_dict,
18-
create_from_yaml, create_from_directory)
17+
from .yaml_processor import (FailToCreateError, create_from_dict,
18+
create_from_yaml, create_from_directory,
19+
FailToProcessError, process_from_dict,
20+
process_from_yaml, process_from_directory)
1921
from .quantity import parse_quantity
20-
from. duration import parse_duration
22+
from . duration import parse_duration

‎kubernetes/utils/create_from_yaml.py renamed to ‎kubernetes/utils/yaml_processor.py

Lines changed: 235 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2019 The Kubernetes Authors.
1+
# Copyright 2025 The Kubernetes Authors.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -24,26 +24,32 @@
2424
LOWER_OR_NUM_FOLLOWED_BY_UPPER_RE = re.compile("([a-z0-9])([A-Z])")
2525

2626

27-
def create_from_directory(
28-
k8s_client, yaml_dir=None, verbose=False, namespace="default", apply=False, **kwargs
29-
):
27+
def process_from_directory(
28+
k8s_client,
29+
yaml_dir=None,
30+
verbose=False,
31+
namespace="default",
32+
apply=False,
33+
action="create",
34+
**kwargs):
3035
"""
31-
Perform an action from files from a directory. Pass True for verbose to
36+
Perform an action (create/delete) from files from a directory. Pass True for verbose to
3237
print confirmation information.
3338
3439
Input:
3540
k8s_client: an ApiClient object, initialized with the client args.
3641
yaml_dir: string. Contains the path to directory.
37-
verbose: If True, print confirmation from the create action.
42+
verbose: If True, print confirmation from the action.
3843
Default is False.
39-
namespace: string. Contains the namespace to create all
44+
namespace: string. Contains the namespace to process all
4045
resources inside. The namespace must preexist otherwise
41-
the resource creation will fail. If the API object in
46+
the resource processing will fail. If the API object in
4247
the yaml file already contains a namespace definition
4348
this parameter has no effect.
44-
apply: bool. If True, use server-side apply for creating resources.
49+
apply: bool. If True, use server-side apply for processing resources.
50+
action: string. The action to perform, either "create" or "delete".
4551
46-
Available parameters for creating <kind>:
52+
Available parameters for processing <kind>:
4753
:param async_req bool
4854
:param bool include_uninitialized: If true, partially initialized
4955
resources are included in the response.
@@ -55,11 +61,11 @@ def create_from_directory(
5561
Valid values are: - All: all dry run stages will be processed
5662
5763
Returns:
58-
The list containing the created kubernetes API objects.
64+
The list containing the processed kubernetes API objects.
5965
6066
Raises:
61-
FailToCreateError which holds list of `client.rest.ApiException`
62-
instances for each object that failed to create.
67+
FailToProcessError which holds list of `client.rest.ApiException`
68+
instances for each object that failed to process.
6369
"""
6470

6571
if not yaml_dir:
@@ -80,49 +86,52 @@ def create_from_directory(
8086

8187
for file in files:
8288
try:
83-
k8s_objects = create_from_yaml(
89+
k8s_objects = process_from_yaml(
8490
k8s_client,
8591
file,
8692
verbose=verbose,
8793
namespace=namespace,
8894
apply=apply,
95+
action=action,
8996
**kwargs,
9097
)
9198
k8s_objects_all.append(k8s_objects)
92-
except FailToCreateError as failure:
99+
except FailToProcessError as failure:
93100
failures.extend(failure.api_exceptions)
94101
if failures:
95-
raise FailToCreateError(failures)
102+
raise FailToProcessError(failures)
96103
return k8s_objects_all
97104

98105

99-
def create_from_yaml(
106+
def process_from_yaml(
100107
k8s_client,
101108
yaml_file=None,
102109
yaml_objects=None,
103110
verbose=False,
104111
namespace="default",
105112
apply=False,
113+
action="create",
106114
**kwargs,
107115
):
108116
"""
109-
Perform an action from a yaml file. Pass True for verbose to
117+
Perform an action (create/delete) from a yaml file. Pass True for verbose to
110118
print confirmation information.
111119
Input:
112120
yaml_file: string. Contains the path to yaml file.
113121
k8s_client: an ApiClient object, initialized with the client args.
114122
yaml_objects: List[dict]. Optional list of YAML objects; used instead
115123
of reading the `yaml_file`. Default is None.
116-
verbose: If True, print confirmation from the create action.
124+
verbose: If True, print confirmation from the action.
117125
Default is False.
118-
namespace: string. Contains the namespace to create all
126+
namespace: string. Contains the namespace to process all
119127
resources inside. The namespace must preexist otherwise
120-
the resource creation will fail. If the API object in
128+
the resource processing will fail. If the API object in
121129
the yaml file already contains a namespace definition
122130
this parameter has no effect.
123-
apply: bool. If True, use server-side apply for creating resources.
131+
apply: bool. If True, use server-side apply for processing resources.
132+
action: string. The action to perform, either "create" or "delete".
124133
125-
Available parameters for creating <kind>:
134+
Available parameters for processing <kind>:
126135
:param async_req bool
127136
:param bool include_uninitialized: If true, partially initialized
128137
resources are included in the response.
@@ -134,33 +143,34 @@ def create_from_yaml(
134143
Valid values are: - All: all dry run stages will be processed
135144
136145
Returns:
137-
The created kubernetes API objects.
146+
The processed kubernetes API objects.
138147
139148
Raises:
140-
FailToCreateError which holds list of `client.rest.ApiException`
141-
instances for each object that failed to create.
149+
FailToProcessError which holds list of `client.rest.ApiException`
150+
instances for each object that failed to process.
142151
"""
143152

144-
def create_with(objects, apply=apply):
153+
def process_with(objects, apply=apply):
145154
failures = []
146155
k8s_objects = []
147156
for yml_document in objects:
148157
if yml_document is None:
149158
continue
150159
try:
151-
created = create_from_dict(
160+
processed = process_from_dict(
152161
k8s_client,
153162
yml_document,
154163
verbose,
155164
namespace=namespace,
156165
apply=apply,
166+
action=action,
157167
**kwargs,
158168
)
159-
k8s_objects.append(created)
160-
except FailToCreateError as failure:
169+
k8s_objects.append(processed)
170+
except FailToProcessError as failure:
161171
failures.extend(failure.api_exceptions)
162172
if failures:
163-
raise FailToCreateError(failures)
173+
raise FailToProcessError(failures)
164174
return k8s_objects
165175

166176
class Loader(yaml.loader.SafeLoader):
@@ -170,42 +180,43 @@ class Loader(yaml.loader.SafeLoader):
170180

171181
if yaml_objects:
172182
yml_document_all = yaml_objects
173-
return create_with(yml_document_all)
183+
return process_with(yml_document_all)
174184
elif yaml_file:
175185
with open(os.path.abspath(yaml_file)) as f:
176186
yml_document_all = yaml.load_all(f, Loader=Loader)
177-
return create_with(yml_document_all, apply)
187+
return process_with(yml_document_all, apply)
178188
else:
179189
raise ValueError(
180190
"One of `yaml_file` or `yaml_objects` arguments must be provided"
181191
)
182192

183193

184-
def create_from_dict(
185-
k8s_client, data, verbose=False, namespace="default", apply=False, **kwargs
194+
def process_from_dict(
195+
k8s_client, data, verbose=False, namespace="default", apply=False, action="create", **kwargs
186196
):
187197
"""
188-
Perform an action from a dictionary containing valid kubernetes
198+
Perform an action (create/delete) from a dictionary containing valid kubernetes
189199
API object (i.e. List, Service, etc).
190200
191201
Input:
192202
k8s_client: an ApiClient object, initialized with the client args.
193203
data: a dictionary holding valid kubernetes objects
194-
verbose: If True, print confirmation from the create action.
204+
verbose: If True, print confirmation from the action.
195205
Default is False.
196-
namespace: string. Contains the namespace to create all
206+
namespace: string. Contains the namespace to process all
197207
resources inside. The namespace must preexist otherwise
198-
the resource creation will fail. If the API object in
208+
the resource processing will fail. If the API object in
199209
the yaml file already contains a namespace definition
200210
this parameter has no effect.
201-
apply: bool. If True, use server-side apply for creating resources.
211+
apply: bool. If True, use server-side apply for processing resources.
212+
action: string. The action to perform, either "create" or "delete".
202213
203214
Returns:
204-
The created kubernetes API objects.
215+
The processed kubernetes API objects.
205216
206217
Raises:
207-
FailToCreateError which holds list of `client.rest.ApiException`
208-
instances for each object that failed to create.
218+
FailToProcessError which holds list of `client.rest.ApiException`
219+
instances for each object that failed to process.
209220
"""
210221
# If it is a list type, will need to iterate its items
211222
api_exceptions = []
@@ -222,48 +233,58 @@ def create_from_dict(
222233
yml_object["apiVersion"] = data["apiVersion"]
223234
yml_object["kind"] = kind
224235
try:
225-
created = create_from_yaml_single_item(
236+
processed = process_from_yaml_single_item(
226237
k8s_client,
227238
yml_object,
228239
verbose,
229240
namespace=namespace,
230241
apply=apply,
242+
action=action,
231243
**kwargs,
232244
)
233-
k8s_objects.append(created)
245+
k8s_objects.append(processed)
234246
except client.rest.ApiException as api_exception:
235247
api_exceptions.append(api_exception)
236248
else:
237249
# This is a single object. Call the single item method
238250
try:
239-
created = create_from_yaml_single_item(
240-
k8s_client, data, verbose, namespace=namespace, apply=apply, **kwargs
241-
)
242-
k8s_objects.append(created)
251+
processed = process_from_yaml_single_item(
252+
k8s_client,
253+
data,
254+
verbose,
255+
namespace=namespace,
256+
apply=apply,
257+
action=action,
258+
**kwargs)
259+
k8s_objects.append(processed)
243260
except client.rest.ApiException as api_exception:
244261
api_exceptions.append(api_exception)
245262

246263
# In case we have exceptions waiting for us, raise them
247264
if api_exceptions:
248-
raise FailToCreateError(api_exceptions)
265+
raise FailToProcessError(api_exceptions)
249266

250267
return k8s_objects
251268

252269

253-
def create_from_yaml_single_item(
254-
k8s_client, yml_object, verbose=False, apply=False, **kwargs
270+
def process_from_yaml_single_item(
271+
k8s_client, yml_object, verbose=False, apply=False, action="create", **kwargs
255272
):
256-
257273
kind = yml_object["kind"]
258274
if apply is True:
259275
apply_client = DynamicClient(k8s_client).resources.get(
260276
api_version=yml_object["apiVersion"], kind=kind
261277
)
262-
resp = apply_client.server_side_apply(
263-
body=yml_object, field_manager="python-client", **kwargs
264-
)
278+
if action == "create":
279+
resp = apply_client.server_side_apply(
280+
body=yml_object, field_manager="python-client", **kwargs
281+
)
282+
elif action == "delete":
283+
resp = apply_client.delete(
284+
body=yml_object, field_manager="python-client", **kwargs
285+
)
265286
if verbose:
266-
msg = "{0} created.".format(kind)
287+
msg = "{0} {1}d.".format(kind, action)
267288
if hasattr(resp, "status"):
268289
msg += " status='{0}'".format(str(resp.status))
269290
print(msg)
@@ -283,30 +304,41 @@ def create_from_yaml_single_item(
283304
# Replace CamelCased action_type into snake_case
284305
kind = UPPER_FOLLOWED_BY_LOWER_RE.sub(r"\1_\2", kind)
285306
kind = LOWER_OR_NUM_FOLLOWED_BY_UPPER_RE.sub(r"\1_\2", kind).lower()
286-
# Expect the user to create namespaced objects more often
287-
if hasattr(k8s_api, "create_namespaced_{0}".format(kind)):
307+
# Prepare the arguments for the API call
308+
api_args = {
309+
"body": client.V1DeleteOptions(
310+
propagation_policy="Background",
311+
grace_period_seconds=5
312+
) if action == "delete" else yml_object,
313+
**kwargs
314+
}
315+
316+
if action == "delete":
317+
name = yml_object["metadata"]["name"]
318+
api_args["name"] = name
319+
320+
# Expect the user to process namespaced objects more often
321+
if hasattr(k8s_api, "{0}_namespaced_{1}".format(action, kind)):
288322
# Decide which namespace we are going to put the object in,
289323
# if any
290324
if "namespace" in yml_object["metadata"]:
291325
namespace = yml_object["metadata"]["namespace"]
292-
kwargs["namespace"] = namespace
293-
resp = getattr(k8s_api, "create_namespaced_{0}".format(kind))(
294-
body=yml_object, **kwargs
295-
)
326+
api_args["namespace"] = namespace
327+
resp = getattr(k8s_api, "{0}_namespaced_{1}".format(
328+
action, kind))(**api_args)
296329
else:
297-
kwargs.pop("namespace", None)
298-
resp = getattr(k8s_api, "create_{0}".format(kind))(
299-
body=yml_object, **kwargs
300-
)
330+
api_args.pop("namespace", None)
331+
resp = getattr(k8s_api, "{0}_{1}".format(action, kind))(**api_args)
332+
301333
if verbose:
302-
msg = "{0} created.".format(kind)
334+
msg = "{0} {1}d.".format(kind, action)
303335
if hasattr(resp, "status"):
304336
msg += " status='{0}'".format(str(resp.status))
305337
print(msg)
306338
return resp
307339

308340

309-
class FailToCreateError(Exception):
341+
class FailToProcessError(Exception):
310342
"""
311343
An exception class for handling error if an error occurred when
312344
handling a yaml file.
@@ -322,3 +354,137 @@ def __str__(self):
322354
api_exception.reason, api_exception.body
323355
)
324356
return msg
357+
358+
359+
def create_from_directory(
360+
k8s_client, yaml_dir=None, verbose=False, namespace="default", apply=False, **kwargs
361+
):
362+
"""
363+
Perform an action from files from a directory. Pass True for verbose to
364+
print confirmation information.
365+
366+
Input:
367+
k8s_client: an ApiClient object, initialized with the client args.
368+
yaml_dir: string. Contains the path to directory.
369+
verbose: If True, print confirmation from the create action.
370+
Default is False.
371+
namespace: string. Contains the namespace to create all
372+
resources inside. The namespace must preexist otherwise
373+
the resource creation will fail. If the API object in
374+
the yaml file already contains a namespace definition
375+
this parameter has no effect.
376+
apply: bool. If True, use server-side apply for creating resources.
377+
378+
Available parameters for creating <kind>:
379+
:param async_req bool
380+
:param bool include_uninitialized: If true, partially initialized
381+
resources are included in the response.
382+
:param str pretty: If 'true', then the output is pretty printed.
383+
:param str dry_run: When present, indicates that modifications
384+
should not be persisted. An invalid or unrecognized dryRun
385+
directive will result in an error response and no further
386+
processing of the request.
387+
Valid values are: - All: all dry run stages will be processed
388+
389+
Returns:
390+
The list containing the created kubernetes API objects.
391+
392+
Raises:
393+
FailToCreateError which holds list of `client.rest.ApiException`
394+
instances for each object that failed to create.
395+
"""
396+
return process_from_directory(
397+
k8s_client,
398+
yaml_dir,
399+
verbose=verbose,
400+
namespace=namespace,
401+
apply=apply,
402+
action="create",
403+
**kwargs,
404+
)
405+
406+
407+
def create_from_yaml(
408+
k8s_client,
409+
yaml_file=None,
410+
yaml_objects=None,
411+
verbose=False,
412+
namespace="default",
413+
apply=False,
414+
**kwargs):
415+
"""
416+
Perform an action from a yaml file. Pass True for verbose to
417+
print confirmation information.
418+
Input:
419+
yaml_file: string. Contains the path to yaml file.
420+
k8s_client: an ApiClient object, initialized with the client args.
421+
yaml_objects: List[dict]. Optional list of YAML objects; used instead
422+
of reading the `yaml_file`. Default is None.
423+
verbose: If True, print confirmation from the create action.
424+
Default is False.
425+
namespace: string. Contains the namespace to create all
426+
resources inside. The namespace must preexist otherwise
427+
the resource creation will fail. If the API object in
428+
the yaml file already contains a namespace definition
429+
this parameter has no effect.
430+
431+
Available parameters for creating <kind>:
432+
:param async_req bool
433+
:param bool include_uninitialized: If true, partially initialized
434+
resources are included in the response.
435+
:param str pretty: If 'true', then the output is pretty printed.
436+
:param str dry_run: When present, indicates that modifications
437+
should not be persisted. An invalid or unrecognized dryRun
438+
directive will result in an error response and no further
439+
processing of the request.
440+
Valid values are: - All: all dry run stages will be processed
441+
442+
Returns:
443+
The created kubernetes API objects.
444+
445+
Raises:
446+
FailToCreateError which holds list of `client.rest.ApiException`
447+
instances for each object that failed to create.
448+
"""
449+
return process_from_yaml(
450+
k8s_client,
451+
yaml_file,
452+
yaml_objects,
453+
verbose,
454+
namespace,
455+
apply,
456+
action="create",
457+
**kwargs)
458+
459+
460+
def create_from_dict(k8s_client, data, verbose=False, namespace='default',
461+
**kwargs):
462+
"""
463+
Perform an action from a dictionary containing valid kubernetes
464+
API object (i.e. List, Service, etc).
465+
466+
Input:
467+
k8s_client: an ApiClient object, initialized with the client args.
468+
data: a dictionary holding valid kubernetes objects
469+
verbose: If True, print confirmation from the create action.
470+
Default is False.
471+
namespace: string. Contains the namespace to create all
472+
resources inside. The namespace must preexist otherwise
473+
the resource creation will fail. If the API object in
474+
the yaml file already contains a namespace definition
475+
this parameter has no effect.
476+
477+
Returns:
478+
The created kubernetes API objects.
479+
480+
Raises:
481+
FailToCreateError which holds list of `client.rest.ApiException`
482+
instances for each object that failed to create.
483+
"""
484+
return process_from_dict(k8s_client, data, verbose, namespace, action="create", **kwargs)
485+
486+
class FailToCreateError(FailToProcessError):
487+
"""
488+
An exception class for handling error if an error occurred when
489+
handling a yaml file.
490+
"""

0 commit comments

Comments
 (0)
Please sign in to comment.