Skip to content

Commit 863e1de

Browse files
cuppettclaude
andcommitted
Add comprehensive documentation for operator enablement features
Documentation for all four operator/controller features: **Usage Guides (new section: Operators & Controllers):** - Finalizers: Complete guide with operator patterns, best practices, cleanup examples, resource dependency management, and timeout protection - Owner References: Parent-child relationships, controller ownership, garbage collection, multi-level ownership, reconciliation loops - Status Updates: Status subresource operations, controller patterns, condition management, conflict handling, progressive updates **Resource Documentation:** - Lease: Leader election patterns, distributed locking, HA controllers, complete LeaderElection class example with acquire/renew logic **Sidebar Updates:** - Added "Operators & Controllers" section under Usage guide - Added Lease to Cluster Resources section All documentation includes: - Comprehensive code examples with real-world use cases - Best practices and common pitfalls - Operator pattern implementations - Attribution footer for fork Documentation verified to build successfully with VitePress. Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
1 parent f23883d commit 863e1de

5 files changed

Lines changed: 1451 additions & 1 deletion

File tree

docs/.vitepress/config.mjs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ export default defineConfig({
5555
{ text: 'Scaling', link: '/guide/usage/scaling' },
5656
{ text: 'Custom Resources (CRDs)', link: '/guide/usage/custom-resources' }
5757
]
58+
},
59+
{
60+
text: 'Operators & Controllers',
61+
items: [
62+
{ text: 'Finalizers', link: '/guide/usage/finalizers' },
63+
{ text: 'Owner References', link: '/guide/usage/owner-references' },
64+
{ text: 'Status Updates', link: '/guide/usage/status-updates' }
65+
]
5866
}
5967
],
6068

@@ -70,7 +78,8 @@ export default defineConfig({
7078
items: [
7179
{ text: 'Namespace', link: '/resources/cluster/namespace' },
7280
{ text: 'Node', link: '/resources/cluster/node' },
73-
{ text: 'Event', link: '/resources/cluster/event' }
81+
{ text: 'Event', link: '/resources/cluster/event' },
82+
{ text: 'Lease', link: '/resources/cluster/lease' }
7483
]
7584
},
7685
{

docs/guide/usage/finalizers.md

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
# Finalizers
2+
3+
Finalizers allow resources to perform cleanup operations before deletion. They're essential for building Kubernetes operators and controllers that manage dependent resources.
4+
5+
## What are Finalizers?
6+
7+
Finalizers are strings in `metadata.finalizers` that prevent a resource from being fully deleted until all finalizers are removed. When a resource with finalizers is deleted:
8+
9+
1. Kubernetes sets `metadata.deletionTimestamp`
10+
2. The resource enters "Terminating" state
11+
3. Controllers remove their finalizers after cleanup
12+
4. Once all finalizers are removed, the resource is deleted
13+
14+
## Managing Finalizers
15+
16+
### Get Finalizers
17+
18+
```php
19+
$configMap = $cluster->getConfigMapByName('my-config', 'default');
20+
21+
$finalizers = $configMap->getFinalizers();
22+
// Returns: ['example.com/cleanup', 'example.com/backup']
23+
```
24+
25+
### Set Finalizers
26+
27+
```php
28+
$configMap->setFinalizers([
29+
'example.com/cleanup',
30+
'example.com/backup',
31+
]);
32+
```
33+
34+
### Add a Finalizer
35+
36+
The `addFinalizer()` method is idempotent - adding the same finalizer twice has no effect:
37+
38+
```php
39+
$configMap->addFinalizer('example.com/cleanup');
40+
41+
// Safe to call multiple times
42+
$configMap->addFinalizer('example.com/cleanup');
43+
```
44+
45+
### Remove a Finalizer
46+
47+
```php
48+
$configMap->removeFinalizer('example.com/cleanup');
49+
```
50+
51+
### Check for a Finalizer
52+
53+
```php
54+
if ($configMap->hasFinalizer('example.com/cleanup')) {
55+
echo "Cleanup finalizer is present";
56+
}
57+
```
58+
59+
## Operator Pattern Example
60+
61+
Here's a complete example of using finalizers in an operator:
62+
63+
```php
64+
use RenokiCo\PhpK8s\KubernetesCluster;
65+
66+
$cluster = new KubernetesCluster('http://127.0.0.1:8080');
67+
$finalizerName = 'example.com/database-backup';
68+
69+
// When creating a resource
70+
$configMap = $cluster->configMap()
71+
->setName('database-config')
72+
->setNamespace('production')
73+
->setData(['connection' => 'postgresql://...'])
74+
->addFinalizer($finalizerName)
75+
->create();
76+
77+
// Later, in your reconciliation loop...
78+
$configMap = $cluster->getConfigMapByName('database-config', 'production');
79+
80+
if ($configMap->getAttribute('metadata.deletionTimestamp')) {
81+
// Resource is being deleted
82+
echo "Performing cleanup before deletion...\n";
83+
84+
// Do your cleanup (backup database, etc.)
85+
performDatabaseBackup($configMap);
86+
87+
// Remove finalizer to allow deletion
88+
// IMPORTANT: Use jsonMergePatch, not update(), on resources being deleted
89+
$configMap->jsonMergePatch([
90+
'metadata' => [
91+
'finalizers' => array_values(
92+
array_filter(
93+
$configMap->getFinalizers(),
94+
fn($f) => $f !== $finalizerName
95+
)
96+
),
97+
],
98+
]);
99+
100+
echo "Cleanup complete, resource will be deleted\n";
101+
} else {
102+
// Normal reconciliation
103+
echo "Resource is active\n";
104+
}
105+
```
106+
107+
## Best Practices
108+
109+
### Finalizer Naming
110+
111+
Use domain-prefixed names to avoid conflicts:
112+
113+
```php
114+
// Good
115+
$pod->addFinalizer('mycompany.com/cleanup');
116+
$pod->addFinalizer('myoperator.io/backup');
117+
118+
// Avoid
119+
$pod->addFinalizer('cleanup'); // Too generic
120+
```
121+
122+
### Removing Finalizers During Deletion
123+
124+
When a resource is being deleted (has `deletionTimestamp`), you **cannot** use `update()`. Use `jsonMergePatch()` instead:
125+
126+
```php
127+
// ❌ WRONG - will fail with 400 Bad Request
128+
$resource->removeFinalizer('my-finalizer')->update();
129+
130+
// ✅ CORRECT - use patch operations
131+
$resource->jsonMergePatch([
132+
'metadata' => [
133+
'finalizers' => [], // Or array without your finalizer
134+
],
135+
]);
136+
```
137+
138+
### Idempotent Cleanup
139+
140+
Make your cleanup operations idempotent - they should be safe to run multiple times:
141+
142+
```php
143+
function performCleanup($resource) {
144+
$backupId = $resource->getLabel('backup-id');
145+
146+
if ($backupId && backupExists($backupId)) {
147+
deleteBackup($backupId);
148+
}
149+
150+
// Safe to call even if backup doesn't exist
151+
}
152+
```
153+
154+
### Timeout Protection
155+
156+
Add timeouts to prevent stuck resources:
157+
158+
```php
159+
$deletionTime = strtotime($configMap->getAttribute('metadata.deletionTimestamp'));
160+
$gracePeriod = 300; // 5 minutes
161+
162+
if (time() - $deletionTime > $gracePeriod) {
163+
// Force remove finalizer after grace period
164+
$configMap->jsonMergePatch([
165+
'metadata' => ['finalizers' => []],
166+
]);
167+
}
168+
```
169+
170+
## Common Use Cases
171+
172+
### Resource Dependency Management
173+
174+
```php
175+
// Parent resource manages child lifecycle
176+
$parent = $cluster->configMap()
177+
->setName('parent-config')
178+
->addFinalizer('example.com/delete-children')
179+
->create();
180+
181+
// On deletion, clean up children
182+
if ($parent->getAttribute('metadata.deletionTimestamp')) {
183+
$children = $cluster->getAllConfigMaps()->filter(function ($cm) use ($parent) {
184+
return $cm->getLabel('parent') === $parent->getName();
185+
});
186+
187+
foreach ($children as $child) {
188+
$child->delete();
189+
}
190+
191+
$parent->removeFinalizer('example.com/delete-children');
192+
$parent->jsonMergePatch([
193+
'metadata' => ['finalizers' => $parent->getFinalizers()],
194+
]);
195+
}
196+
```
197+
198+
### External Resource Cleanup
199+
200+
```php
201+
// Clean up external resources (S3 buckets, databases, etc.)
202+
$backup = $cluster->configMap()
203+
->setName('backup-config')
204+
->addFinalizer('example.com/s3-cleanup')
205+
->create();
206+
207+
if ($backup->getAttribute('metadata.deletionTimestamp')) {
208+
$bucketName = $backup->getData('s3-bucket');
209+
210+
// Delete S3 bucket
211+
$s3Client->deleteBucket(['Bucket' => $bucketName]);
212+
213+
// Remove finalizer
214+
$backup->jsonMergePatch([
215+
'metadata' => ['finalizers' => []],
216+
]);
217+
}
218+
```
219+
220+
---
221+
222+
*Documentation for cuppett/php-k8s fork*

0 commit comments

Comments
 (0)