0
# Custom Resources
1
2
The Fabric8 Kubernetes Client provides comprehensive support for Custom Resource Definitions (CRDs) and custom resources. This includes type-safe operations on custom resources, dynamic resource handling for unknown types, and utilities for working with CRDs.
3
4
## Custom Resource Support
5
6
### CustomResource Base Class
7
8
Base class for creating type-safe custom resource implementations.
9
10
```java { .api }
11
public abstract class CustomResource<T, S> implements HasMetadata {
12
// Spec and Status management
13
public T getSpec();
14
public void setSpec(T spec);
15
public S getStatus();
16
public void setStatus(S status);
17
18
// Metadata implementation from HasMetadata
19
public ObjectMeta getMetadata();
20
public void setMetadata(ObjectMeta metadata);
21
public String getKind();
22
public void setKind(String kind);
23
public String getApiVersion();
24
public void setApiVersion(String apiVersion);
25
}
26
```
27
28
### Typed Custom Resource Operations
29
30
Operations on strongly-typed custom resources using the generic resources method.
31
32
```java { .api }
33
public interface KubernetesClient {
34
<T extends HasMetadata> MixedOperation<T, KubernetesResourceList<T>, Resource<T>>
35
resources(Class<T> resourceType);
36
37
<T extends HasMetadata> MixedOperation<T, KubernetesResourceList<T>, Resource<T>>
38
resources(Class<T> resourceType, Class<? extends KubernetesResourceList<T>> listClass);
39
}
40
```
41
42
### Generic Resource Operations
43
44
Operations on custom resources using GenericKubernetesResource for dynamic handling.
45
46
```java { .api }
47
public interface KubernetesClient {
48
MixedOperation<GenericKubernetesResource, GenericKubernetesResourceList, Resource<GenericKubernetesResource>>
49
genericKubernetesResources(String apiVersion, String kind);
50
51
MixedOperation<GenericKubernetesResource, GenericKubernetesResourceList, Resource<GenericKubernetesResource>>
52
genericKubernetesResources(ResourceDefinitionContext context);
53
}
54
```
55
56
### ResourceDefinitionContext
57
58
Context information for custom resource definitions.
59
60
```java { .api }
61
public class ResourceDefinitionContext {
62
// Factory methods
63
public static ResourceDefinitionContext fromResourceType(Class<? extends HasMetadata> resourceType);
64
public static ResourceDefinitionContext fromCrd(CustomResourceDefinition crd);
65
66
// Properties
67
public String getGroup();
68
public String getVersion();
69
public String getKind();
70
public String getPlural();
71
public String getSingular();
72
public boolean isNamespaceScoped();
73
74
// Builder methods
75
public static ResourceDefinitionContextBuilder builder();
76
}
77
```
78
79
## Custom Resource Definition Operations
80
81
### CRD Management
82
83
Operations for managing Custom Resource Definitions.
84
85
```java { .api }
86
public interface ApiextensionsAPIGroupDSL {
87
// V1 API (preferred)
88
V1ApiextensionsAPIGroupDSL v1();
89
90
// Default methods (v1)
91
NonNamespaceOperation<CustomResourceDefinition, CustomResourceDefinitionList, Resource<CustomResourceDefinition>> customResourceDefinitions();
92
}
93
94
public interface V1ApiextensionsAPIGroupDSL {
95
NonNamespaceOperation<CustomResourceDefinition, CustomResourceDefinitionList, Resource<CustomResourceDefinition>> customResourceDefinitions();
96
}
97
98
// Access via client
99
public interface KubernetesClient {
100
ApiextensionsAPIGroupDSL apiextensions();
101
}
102
```
103
104
## Usage Examples
105
106
### Defining a Custom Resource Class
107
108
```java
109
// Define the spec class
110
public class DatabaseSpec {
111
private String version;
112
private int replicas;
113
private String storageSize;
114
115
// Getters and setters
116
public String getVersion() { return version; }
117
public void setVersion(String version) { this.version = version; }
118
119
public int getReplicas() { return replicas; }
120
public void setReplicas(int replicas) { this.replicas = replicas; }
121
122
public String getStorageSize() { return storageSize; }
123
public void setStorageSize(String storageSize) { this.storageSize = storageSize; }
124
}
125
126
// Define the status class
127
public class DatabaseStatus {
128
private String phase;
129
private String message;
130
private List<String> conditions;
131
132
// Getters and setters
133
public String getPhase() { return phase; }
134
public void setPhase(String phase) { this.phase = phase; }
135
136
public String getMessage() { return message; }
137
public void setMessage(String message) { this.message = message; }
138
139
public List<String> getConditions() { return conditions; }
140
public void setConditions(List<String> conditions) { this.conditions = conditions; }
141
}
142
143
// Define the custom resource class
144
@Group("example.com")
145
@Version("v1")
146
@Kind("Database")
147
@Plural("databases")
148
@Singular("database")
149
public class Database extends CustomResource<DatabaseSpec, DatabaseStatus> {
150
// The CustomResource base class provides all necessary methods
151
}
152
```
153
154
### Creating a Custom Resource Definition
155
156
```java
157
// Create CRD for the Database custom resource
158
CustomResourceDefinition crd = client.apiextensions().v1().customResourceDefinitions()
159
.create(new CustomResourceDefinitionBuilder()
160
.withNewMetadata()
161
.withName("databases.example.com")
162
.endMetadata()
163
.withNewSpec()
164
.withGroup("example.com")
165
.withScope("Namespaced")
166
.addNewVersion()
167
.withName("v1")
168
.withServed(true)
169
.withStorage(true)
170
.withNewSchema()
171
.withNewOpenAPIV3Schema()
172
.withType("object")
173
.addToProperties("spec", new JSONSchemaPropsBuilder()
174
.withType("object")
175
.addToProperties("version", new JSONSchemaPropsBuilder()
176
.withType("string")
177
.build())
178
.addToProperties("replicas", new JSONSchemaPropsBuilder()
179
.withType("integer")
180
.withMinimum(1.0)
181
.build())
182
.addToProperties("storageSize", new JSONSchemaPropsBuilder()
183
.withType("string")
184
.build())
185
.build())
186
.addToProperties("status", new JSONSchemaPropsBuilder()
187
.withType("object")
188
.addToProperties("phase", new JSONSchemaPropsBuilder()
189
.withType("string")
190
.build())
191
.addToProperties("message", new JSONSchemaPropsBuilder()
192
.withType("string")
193
.build())
194
.build())
195
.build()
196
.endSchema()
197
.endVersion()
198
.withNewNames()
199
.withKind("Database")
200
.withPlural("databases")
201
.withSingular("database")
202
.endNames()
203
.endSpec()
204
.build());
205
206
System.out.println("Created CRD: " + crd.getMetadata().getName());
207
```
208
209
### Working with Typed Custom Resources
210
211
```java
212
// Create a Database custom resource instance
213
Database database = new Database();
214
database.setMetadata(new ObjectMetaBuilder()
215
.withName("my-database")
216
.withNamespace("production")
217
.build());
218
219
DatabaseSpec spec = new DatabaseSpec();
220
spec.setVersion("13.4");
221
spec.setReplicas(3);
222
spec.setStorageSize("100Gi");
223
database.setSpec(spec);
224
225
// Create the custom resource
226
Database created = client.resources(Database.class).create(database);
227
System.out.println("Created database: " + created.getMetadata().getName());
228
229
// List all Database resources
230
KubernetesResourceList<Database> databases = client.resources(Database.class).list();
231
for (Database db : databases.getItems()) {
232
System.out.println("Database: " + db.getMetadata().getName() +
233
" version: " + db.getSpec().getVersion());
234
}
235
236
// Update custom resource
237
Database updated = client.resources(Database.class)
238
.withName("my-database")
239
.edit(db -> {
240
db.getSpec().setReplicas(5);
241
return db;
242
});
243
244
// Update status subresource
245
client.resources(Database.class)
246
.withName("my-database")
247
.editStatus(db -> {
248
DatabaseStatus status = new DatabaseStatus();
249
status.setPhase("Running");
250
status.setMessage("Database is healthy");
251
db.setStatus(status);
252
return db;
253
});
254
255
// Watch for changes
256
Watch watch = client.resources(Database.class).watch(new Watcher<Database>() {
257
@Override
258
public void eventReceived(Action action, Database database) {
259
System.out.println(action + ": " + database.getMetadata().getName());
260
if (database.getStatus() != null) {
261
System.out.println("Status: " + database.getStatus().getPhase());
262
}
263
}
264
265
@Override
266
public void onClose(WatcherException cause) {
267
System.out.println("Watch closed: " + cause);
268
}
269
});
270
```
271
272
### Working with Generic Resources
273
274
When you don't have a typed class or need to work with arbitrary custom resources:
275
276
```java
277
// Using apiVersion and kind
278
MixedOperation<GenericKubernetesResource, GenericKubernetesResourceList, Resource<GenericKubernetesResource>>
279
genericOp = client.genericKubernetesResources("example.com/v1", "Database");
280
281
// Create a generic custom resource
282
GenericKubernetesResource genericDatabase = new GenericKubernetesResource();
283
genericDatabase.setApiVersion("example.com/v1");
284
genericDatabase.setKind("Database");
285
genericDatabase.setMetadata(new ObjectMetaBuilder()
286
.withName("generic-database")
287
.withNamespace("default")
288
.build());
289
290
// Set spec using the additional properties map
291
Map<String, Object> spec = new HashMap<>();
292
spec.put("version", "14.1");
293
spec.put("replicas", 2);
294
spec.put("storageSize", "50Gi");
295
genericDatabase.setAdditionalProperty("spec", spec);
296
297
// Create the resource
298
GenericKubernetesResource created = genericOp.create(genericDatabase);
299
300
// Access spec properties
301
Map<String, Object> createdSpec = (Map<String, Object>) created.getAdditionalProperties().get("spec");
302
String version = (String) createdSpec.get("version");
303
Integer replicas = (Integer) createdSpec.get("replicas");
304
305
System.out.println("Created database version: " + version + " with " + replicas + " replicas");
306
307
// List generic resources
308
GenericKubernetesResourceList list = genericOp.list();
309
for (GenericKubernetesResource resource : list.getItems()) {
310
System.out.println("Found resource: " + resource.getMetadata().getName());
311
}
312
```
313
314
### Using ResourceDefinitionContext
315
316
For more explicit resource definition:
317
318
```java
319
// Create ResourceDefinitionContext
320
ResourceDefinitionContext context = new ResourceDefinitionContextBuilder()
321
.withGroup("example.com")
322
.withVersion("v1")
323
.withKind("Database")
324
.withPlural("databases")
325
.withNamespaceScoped(true)
326
.build();
327
328
// Use with generic resources
329
MixedOperation<GenericKubernetesResource, GenericKubernetesResourceList, Resource<GenericKubernetesResource>>
330
contextOp = client.genericKubernetesResources(context);
331
332
// All operations work the same as with apiVersion/kind approach
333
GenericKubernetesResourceList databases = contextOp.inNamespace("production").list();
334
```
335
336
### Custom Resource from CRD
337
338
Create ResourceDefinitionContext from an existing CRD:
339
340
```java
341
// Get existing CRD
342
CustomResourceDefinition crd = client.apiextensions().v1().customResourceDefinitions()
343
.withName("databases.example.com")
344
.get();
345
346
if (crd != null) {
347
// Create context from CRD
348
ResourceDefinitionContext context = ResourceDefinitionContext.fromCrd(crd);
349
350
// Use for operations
351
MixedOperation<GenericKubernetesResource, GenericKubernetesResourceList, Resource<GenericKubernetesResource>>
352
crdOp = client.genericKubernetesResources(context);
353
354
GenericKubernetesResourceList resources = crdOp.list();
355
System.out.println("Found " + resources.getItems().size() + " custom resources");
356
}
357
```
358
359
### Custom Resource Informers
360
361
Custom resources work with the informer framework:
362
363
```java
364
// Create informer for typed custom resource
365
SharedIndexInformer<Database> informer = client.informers()
366
.sharedIndexInformerForCustomResource(Database.class, 30 * 1000);
367
368
informer.addEventHandler(new ResourceEventHandler<Database>() {
369
@Override
370
public void onAdd(Database database) {
371
System.out.println("Database added: " + database.getMetadata().getName());
372
}
373
374
@Override
375
public void onUpdate(Database oldDatabase, Database newDatabase) {
376
System.out.println("Database updated: " + newDatabase.getMetadata().getName());
377
}
378
379
@Override
380
public void onDelete(Database database, boolean deletedFinalStateUnknown) {
381
System.out.println("Database deleted: " + database.getMetadata().getName());
382
}
383
});
384
385
// Start the informer
386
client.informers().startAllRegisteredInformers();
387
388
// Get cached resources
389
List<Database> cachedDatabases = informer.getIndexer().list();
390
```
391
392
## Error Handling
393
394
Custom resource operations can encounter specific errors:
395
396
```java
397
try {
398
Database db = client.resources(Database.class).withName("non-existent").get();
399
} catch (KubernetesClientException e) {
400
if (e.getCode() == 404) {
401
System.out.println("Custom resource not found");
402
} else if (e.getCode() == 400) {
403
System.out.println("Invalid custom resource definition: " + e.getMessage());
404
}
405
}
406
407
// Check if CRD exists before using custom resources
408
CustomResourceDefinition crd = client.apiextensions().v1().customResourceDefinitions()
409
.withName("databases.example.com")
410
.get();
411
412
if (crd == null) {
413
System.out.println("CRD not found - cannot work with Database resources");
414
} else {
415
// Safe to use Database resources
416
KubernetesResourceList<Database> databases = client.resources(Database.class).list();
417
}
418
```