0
# Custom Resources and API Extensions
1
2
Work with custom resource definitions, API extensions, and admission controllers. Enables extending Kubernetes with custom APIs and controllers while maintaining full client library support for dynamic resource management and custom operators.
3
4
## Capabilities
5
6
### Custom Resource Definition Management
7
8
Create and manage custom resource definitions (CRDs) that extend the Kubernetes API with new resource types.
9
10
```python { .api }
11
class ApiextensionsV1Api:
12
def create_custom_resource_definition(
13
self,
14
body: V1CustomResourceDefinition,
15
dry_run: str = None,
16
field_manager: str = None,
17
pretty: str = None
18
) -> V1CustomResourceDefinition:
19
"""Create a custom resource definition."""
20
21
def list_custom_resource_definition(
22
self,
23
pretty: str = None,
24
allow_watch_bookmarks: bool = None,
25
continue_: str = None,
26
field_selector: str = None,
27
label_selector: str = None,
28
limit: int = None,
29
resource_version: str = None,
30
timeout_seconds: int = None,
31
watch: bool = None
32
) -> V1CustomResourceDefinitionList:
33
"""List custom resource definitions."""
34
35
def read_custom_resource_definition(
36
self,
37
name: str,
38
pretty: str = None
39
) -> V1CustomResourceDefinition:
40
"""Read specified custom resource definition."""
41
42
def patch_custom_resource_definition(
43
self,
44
name: str,
45
body: object,
46
dry_run: str = None,
47
field_manager: str = None,
48
force: bool = None,
49
pretty: str = None
50
) -> V1CustomResourceDefinition:
51
"""Patch specified custom resource definition."""
52
53
def delete_custom_resource_definition(
54
self,
55
name: str,
56
body: V1DeleteOptions = None,
57
dry_run: str = None,
58
grace_period_seconds: int = None,
59
orphan_dependents: bool = None,
60
propagation_policy: str = None,
61
pretty: str = None
62
) -> V1Status:
63
"""Delete specified custom resource definition."""
64
```
65
66
### Custom Object Operations
67
68
Manage instances of custom resources using the generic custom objects API.
69
70
```python { .api }
71
class CustomObjectsApi:
72
def create_namespaced_custom_object(
73
self,
74
group: str,
75
version: str,
76
namespace: str,
77
plural: str,
78
body: object,
79
dry_run: str = None,
80
field_manager: str = None,
81
pretty: str = None
82
) -> object:
83
"""Create a namespaced custom object."""
84
85
def list_namespaced_custom_object(
86
self,
87
group: str,
88
version: str,
89
namespace: str,
90
plural: str,
91
pretty: str = None,
92
allow_watch_bookmarks: bool = None,
93
continue_: str = None,
94
field_selector: str = None,
95
label_selector: str = None,
96
limit: int = None,
97
resource_version: str = None,
98
timeout_seconds: int = None,
99
watch: bool = None
100
) -> object:
101
"""List namespaced custom objects."""
102
103
def get_namespaced_custom_object(
104
self,
105
group: str,
106
version: str,
107
namespace: str,
108
plural: str,
109
name: str,
110
pretty: str = None
111
) -> object:
112
"""Get a namespaced custom object."""
113
114
def patch_namespaced_custom_object(
115
self,
116
group: str,
117
version: str,
118
namespace: str,
119
plural: str,
120
name: str,
121
body: object,
122
dry_run: str = None,
123
field_manager: str = None,
124
force: bool = None,
125
pretty: str = None
126
) -> object:
127
"""Patch a namespaced custom object."""
128
129
def delete_namespaced_custom_object(
130
self,
131
group: str,
132
version: str,
133
namespace: str,
134
plural: str,
135
name: str,
136
body: V1DeleteOptions = None,
137
dry_run: str = None,
138
grace_period_seconds: int = None,
139
orphan_dependents: bool = None,
140
propagation_policy: str = None,
141
pretty: str = None
142
) -> object:
143
"""Delete a namespaced custom object."""
144
```
145
146
### Cluster-scoped Custom Objects
147
148
Manage cluster-scoped custom resources that are not namespaced.
149
150
```python { .api }
151
class CustomObjectsApi:
152
def create_cluster_custom_object(
153
self,
154
group: str,
155
version: str,
156
plural: str,
157
body: object,
158
dry_run: str = None,
159
field_manager: str = None,
160
pretty: str = None
161
) -> object:
162
"""Create a cluster-scoped custom object."""
163
164
def list_cluster_custom_object(
165
self,
166
group: str,
167
version: str,
168
plural: str,
169
pretty: str = None,
170
allow_watch_bookmarks: bool = None,
171
continue_: str = None,
172
field_selector: str = None,
173
label_selector: str = None,
174
limit: int = None,
175
resource_version: str = None,
176
timeout_seconds: int = None,
177
watch: bool = None
178
) -> object:
179
"""List cluster-scoped custom objects."""
180
181
def get_cluster_custom_object(
182
self,
183
group: str,
184
version: str,
185
plural: str,
186
name: str,
187
pretty: str = None
188
) -> object:
189
"""Get a cluster-scoped custom object."""
190
```
191
192
## Resource Models
193
194
### V1CustomResourceDefinition
195
```python { .api }
196
class V1CustomResourceDefinition:
197
api_version: str # "apiextensions.k8s.io/v1"
198
kind: str # "CustomResourceDefinition"
199
metadata: V1ObjectMeta
200
spec: V1CustomResourceDefinitionSpec
201
status: V1CustomResourceDefinitionStatus
202
```
203
204
### V1CustomResourceDefinitionSpec
205
```python { .api }
206
class V1CustomResourceDefinitionSpec:
207
group: str
208
names: V1CustomResourceDefinitionNames
209
scope: str # "Namespaced" or "Cluster"
210
versions: list # List of V1CustomResourceDefinitionVersion
211
conversion: V1CustomResourceConversion
212
preserve_unknown_fields: bool
213
```
214
215
### V1CustomResourceDefinitionNames
216
```python { .api }
217
class V1CustomResourceDefinitionNames:
218
kind: str
219
plural: str
220
singular: str
221
short_names: list
222
categories: list
223
list_kind: str
224
```
225
226
### V1CustomResourceDefinitionVersion
227
```python { .api }
228
class V1CustomResourceDefinitionVersion:
229
name: str
230
served: bool
231
storage: bool
232
schema: V1CustomResourceValidation
233
subresources: V1CustomResourceSubresources
234
additional_printer_columns: list
235
deprecated: bool
236
```
237
238
## Usage Examples
239
240
### Creating a Custom Resource Definition
241
242
```python
243
from kubernetes import client, config
244
245
config.load_kube_config()
246
api = client.ApiextensionsV1Api()
247
248
# Define a CRD for a custom application
249
crd_manifest = {
250
"apiVersion": "apiextensions.k8s.io/v1",
251
"kind": "CustomResourceDefinition",
252
"metadata": {
253
"name": "applications.mycompany.io"
254
},
255
"spec": {
256
"group": "mycompany.io",
257
"versions": [{
258
"name": "v1",
259
"served": True,
260
"storage": True,
261
"schema": {
262
"openAPIV3Schema": {
263
"type": "object",
264
"properties": {
265
"spec": {
266
"type": "object",
267
"properties": {
268
"image": {
269
"type": "string"
270
},
271
"replicas": {
272
"type": "integer",
273
"minimum": 1,
274
"maximum": 10
275
},
276
"port": {
277
"type": "integer"
278
}
279
},
280
"required": ["image", "replicas"]
281
},
282
"status": {
283
"type": "object",
284
"properties": {
285
"ready": {
286
"type": "boolean"
287
},
288
"message": {
289
"type": "string"
290
}
291
}
292
}
293
}
294
}
295
},
296
"subresources": {
297
"status": {}
298
}
299
}],
300
"scope": "Namespaced",
301
"names": {
302
"plural": "applications",
303
"singular": "application",
304
"kind": "Application",
305
"shortNames": ["app"]
306
}
307
}
308
}
309
310
# Create the CRD
311
try:
312
crd = api.create_custom_resource_definition(body=crd_manifest)
313
print(f"CRD created: {crd.metadata.name}")
314
except client.ApiException as e:
315
print(f"Failed to create CRD: {e}")
316
```
317
318
### Working with Custom Objects
319
320
```python
321
from kubernetes import client, config
322
323
config.load_kube_config()
324
custom_api = client.CustomObjectsApi()
325
326
# Create a custom resource instance
327
application_manifest = {
328
"apiVersion": "mycompany.io/v1",
329
"kind": "Application",
330
"metadata": {
331
"name": "my-web-app",
332
"namespace": "default"
333
},
334
"spec": {
335
"image": "nginx:1.20",
336
"replicas": 3,
337
"port": 80
338
}
339
}
340
341
# Create the custom resource
342
try:
343
app = custom_api.create_namespaced_custom_object(
344
group="mycompany.io",
345
version="v1",
346
namespace="default",
347
plural="applications",
348
body=application_manifest
349
)
350
print(f"Application created: {app['metadata']['name']}")
351
except client.ApiException as e:
352
print(f"Failed to create application: {e}")
353
354
# List all applications
355
apps = custom_api.list_namespaced_custom_object(
356
group="mycompany.io",
357
version="v1",
358
namespace="default",
359
plural="applications"
360
)
361
362
print("Applications:")
363
for app in apps['items']:
364
name = app['metadata']['name']
365
replicas = app['spec']['replicas']
366
image = app['spec']['image']
367
print(f" - {name}: {replicas} replicas of {image}")
368
```
369
370
### Using Dynamic Client with Custom Resources
371
372
```python
373
from kubernetes import client, config, dynamic
374
375
config.load_kube_config()
376
dyn_client = dynamic.DynamicClient(client.ApiClient())
377
378
# Get the custom resource definition
379
applications = dyn_client.resources.get(
380
api_version="mycompany.io/v1",
381
kind="Application"
382
)
383
384
# Create instance using dynamic client
385
app_spec = {
386
"apiVersion": "mycompany.io/v1",
387
"kind": "Application",
388
"metadata": {
389
"name": "dynamic-app",
390
"namespace": "default"
391
},
392
"spec": {
393
"image": "httpd:2.4",
394
"replicas": 2,
395
"port": 8080
396
}
397
}
398
399
# Create application
400
app = applications.create(body=app_spec, namespace="default")
401
print(f"Created application: {app.metadata.name}")
402
403
# Get specific application
404
app = applications.get(name="dynamic-app", namespace="default")
405
print(f"Application status: {getattr(app, 'status', 'No status')}")
406
407
# Update application
408
patch_data = {
409
"spec": {
410
"replicas": 5
411
}
412
}
413
414
patched_app = applications.patch(
415
name="dynamic-app",
416
namespace="default",
417
body=patch_data
418
)
419
print(f"Scaled application to {patched_app.spec.replicas} replicas")
420
```
421
422
### Managing CRD Versions
423
424
```python
425
from kubernetes import client, config
426
427
config.load_kube_config()
428
api = client.ApiextensionsV1Api()
429
430
def add_crd_version(crd_name, new_version_spec):
431
"""Add a new version to an existing CRD."""
432
433
# Get existing CRD
434
crd = api.read_custom_resource_definition(name=crd_name)
435
436
# Add new version
437
crd.spec.versions.append(new_version_spec)
438
439
# Update CRD
440
updated_crd = api.replace_custom_resource_definition(
441
name=crd_name,
442
body=crd
443
)
444
445
return updated_crd
446
447
# Define new version
448
v2_spec = {
449
"name": "v2",
450
"served": True,
451
"storage": False, # Keep v1 as storage version
452
"schema": {
453
"openAPIV3Schema": {
454
"type": "object",
455
"properties": {
456
"spec": {
457
"type": "object",
458
"properties": {
459
"image": {"type": "string"},
460
"replicas": {
461
"type": "integer",
462
"minimum": 1,
463
"maximum": 100 # Increased limit in v2
464
},
465
"port": {"type": "integer"},
466
"healthCheck": { # New field in v2
467
"type": "object",
468
"properties": {
469
"path": {"type": "string"},
470
"port": {"type": "integer"}
471
}
472
}
473
},
474
"required": ["image", "replicas"]
475
}
476
}
477
}
478
}
479
}
480
481
# Add version to CRD
482
try:
483
updated_crd = add_crd_version("applications.mycompany.io", v2_spec)
484
print(f"Added v2 to CRD. Versions: {[v['name'] for v in updated_crd.spec.versions]}")
485
except client.ApiException as e:
486
print(f"Failed to update CRD: {e}")
487
```
488
489
### Custom Resource Status Updates
490
491
```python
492
from kubernetes import client, config
493
494
config.load_kube_config()
495
custom_api = client.CustomObjectsApi()
496
497
def update_application_status(name, namespace, status_data):
498
"""Update the status subresource of a custom application."""
499
500
try:
501
# Update status subresource
502
result = custom_api.patch_namespaced_custom_object_status(
503
group="mycompany.io",
504
version="v1",
505
namespace=namespace,
506
plural="applications",
507
name=name,
508
body={"status": status_data}
509
)
510
return result
511
except client.ApiException as e:
512
print(f"Failed to update status: {e}")
513
return None
514
515
# Update application status
516
status_update = {
517
"ready": True,
518
"message": "Application is healthy and running",
519
"lastUpdated": "2023-01-01T12:00:00Z",
520
"readyReplicas": 3
521
}
522
523
updated_app = update_application_status(
524
"my-web-app",
525
"default",
526
status_update
527
)
528
529
if updated_app:
530
print(f"Status updated for application: {updated_app['metadata']['name']}")
531
```
532
533
### Watching Custom Resources
534
535
```python
536
from kubernetes import client, config, watch
537
538
config.load_kube_config()
539
custom_api = client.CustomObjectsApi()
540
w = watch.Watch()
541
542
# Watch custom resource events
543
print("Watching application events...")
544
for event in w.stream(
545
custom_api.list_namespaced_custom_object,
546
group="mycompany.io",
547
version="v1",
548
namespace="default",
549
plural="applications"
550
):
551
event_type = event['type']
552
app = event['object']
553
app_name = app['metadata']['name']
554
555
print(f"{event_type}: Application {app_name}")
556
557
if event_type in ['ADDED', 'MODIFIED']:
558
spec = app.get('spec', {})
559
status = app.get('status', {})
560
561
print(f" Image: {spec.get('image', 'unknown')}")
562
print(f" Replicas: {spec.get('replicas', 0)}")
563
print(f" Ready: {status.get('ready', False)}")
564
```
565
566
### Cluster-scoped Custom Resources
567
568
```python
569
from kubernetes import client, config
570
571
config.load_kube_config()
572
api = client.ApiextensionsV1Api()
573
custom_api = client.CustomObjectsApi()
574
575
# Create cluster-scoped CRD
576
cluster_crd = {
577
"apiVersion": "apiextensions.k8s.io/v1",
578
"kind": "CustomResourceDefinition",
579
"metadata": {
580
"name": "clusters.infrastructure.mycompany.io"
581
},
582
"spec": {
583
"group": "infrastructure.mycompany.io",
584
"versions": [{
585
"name": "v1",
586
"served": True,
587
"storage": True,
588
"schema": {
589
"openAPIV3Schema": {
590
"type": "object",
591
"properties": {
592
"spec": {
593
"type": "object",
594
"properties": {
595
"region": {"type": "string"},
596
"nodeCount": {"type": "integer"},
597
"version": {"type": "string"}
598
}
599
}
600
}
601
}
602
}
603
}],
604
"scope": "Cluster", # Cluster-scoped
605
"names": {
606
"plural": "clusters",
607
"singular": "cluster",
608
"kind": "Cluster"
609
}
610
}
611
}
612
613
# Create cluster-scoped CRD
614
api.create_custom_resource_definition(body=cluster_crd)
615
616
# Create cluster-scoped custom resource instance
617
cluster_instance = {
618
"apiVersion": "infrastructure.mycompany.io/v1",
619
"kind": "Cluster",
620
"metadata": {
621
"name": "production-cluster"
622
},
623
"spec": {
624
"region": "us-west-2",
625
"nodeCount": 5,
626
"version": "1.25.0"
627
}
628
}
629
630
# Create cluster resource (no namespace parameter)
631
cluster = custom_api.create_cluster_custom_object(
632
group="infrastructure.mycompany.io",
633
version="v1",
634
plural="clusters",
635
body=cluster_instance
636
)
637
638
print(f"Created cluster: {cluster['metadata']['name']}")
639
640
# List cluster resources
641
clusters = custom_api.list_cluster_custom_object(
642
group="infrastructure.mycompany.io",
643
version="v1",
644
plural="clusters"
645
)
646
647
for cluster in clusters['items']:
648
name = cluster['metadata']['name']
649
region = cluster['spec']['region']
650
nodes = cluster['spec']['nodeCount']
651
print(f"Cluster {name}: {nodes} nodes in {region}")
652
```