or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-workloads.mdautoscaling.mdconfiguration.mdcore-resources.mdcustom-resources.mddynamic-client.mdindex.mdleader-election.mdnetworking.mdrbac-security.mdresource-watching.mdstorage.mdstreaming-operations.mdutilities.md

custom-resources.mddocs/

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

```