Skip to content

Commit cae6bb7

Browse files
stuggiclaude
andcommitted
Update webhook design to reuse existing ValidateCreate pattern
Clarify that we reuse the existing webhook pattern where openstack-operator calls ValidateCreate/ValidateUpdate from service operators, rather than creating new webhook infrastructure. Architecture: - OpenStack operator: Calls service operator ValidateCreate functions - Service operators: Add backup labeling in existing webhook functions - Infrastructure operator: Runs its own independent webhook - No new webhook registration needed Benefits: - Reuses existing openstack-operator webhook pattern - Service operators have knowledge of their own resources - Generic helper function for user-provided resource detection - Works on both Create and Update (existing environments) - No coordination needed between operators Example: keystone.ValidateCreate() calls labelUserProvidedResources() which labels referenced Secrets/ConfigMaps if they have no ownerReferences (user-provided). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 423a3f5 commit cae6bb7

1 file changed

Lines changed: 126 additions & 19 deletions

File tree

docs/dev/backup-restore-webhook-design.md

Lines changed: 126 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,132 @@ metadata:
3939
- `"all"`: General resources needed by all deployments
4040
- `openstack.org/restore-order`: Numeric order for restore sequence (e.g., `"1"`, `"2"`, `"3"`)
4141

42-
### Mutating Webhook
43-
44-
A mutating webhook in the openstack-operator watches resource creation and automatically adds labels based on CRD annotations.
45-
46-
**Webhook Logic:**
47-
1. Resource CREATE request arrives
48-
2. Webhook looks up CRD for resource type
49-
3. If CRD has `openstack.org/backup: "true"`:
50-
- For Secrets/ConfigMaps: Only label if no `ownerReferences` (user-provided resources)
51-
- For all other resources: Add labels
52-
4. Labels added to resource:
53-
- `openstack.org/backup: "true"`
54-
- `openstack.org/backup-category: "<category>"`
55-
- `openstack.org/restore-order: "<order>"`
56-
57-
**Special Handling for Secrets and ConfigMaps:**
58-
- Only label if `metadata.ownerReferences` is empty (user-provided)
59-
- Skip operator-managed resources (created by controllers)
60-
- This prevents backing up temporary/generated resources
42+
### Mutating Webhooks
43+
44+
Reuse the existing webhook pattern where openstack-operator already calls `ValidateCreate` and `ValidateUpdate` functions from service operators.
45+
46+
**Architecture:**
47+
48+
1. **OpenStack Operator Webhooks** (existing pattern):
49+
- Existing webhooks in `api/core/v1beta1/openstackcontrolplane_webhook.go`
50+
- Already calls `ValidateCreate` from service operators (e.g., `keystone.ValidateCreate`)
51+
- Add backup labeling logic to these existing validation functions
52+
- Works on both Create and Update (handles existing environments)
53+
54+
2. **Infrastructure Operator Webhook** (independent):
55+
- Runs its own separate mutating webhook
56+
- Handles infrastructure resources (NetConfig, IPSet, RabbitMQUser, etc.)
57+
- Independent from openstack-operator webhook
58+
59+
**Example: Adding Backup Labeling to Existing Webhooks**
60+
61+
In `keystone-operator/api/v1beta1/keystoneapi_webhook.go`:
62+
63+
```go
64+
func (r *KeystoneAPI) ValidateCreate() error {
65+
// Existing validation logic...
66+
67+
// NEW: Add backup labels to user-provided resources
68+
if err := r.labelUserProvidedResources(); err != nil {
69+
return err
70+
}
71+
72+
return nil
73+
}
74+
75+
func (r *KeystoneAPI) labelUserProvidedResources() error {
76+
ctx := context.Background()
77+
78+
// Label Secret if user-provided (no ownerReferences)
79+
if r.Spec.Secret != "" {
80+
if err := labelResourceIfUserProvided(ctx, r.Namespace, "Secret", r.Spec.Secret); err != nil {
81+
return err
82+
}
83+
}
84+
85+
// Label CustomConfigSecret if user-provided
86+
if r.Spec.HttpdCustomization != nil && r.Spec.HttpdCustomization.CustomConfigSecret != "" {
87+
if err := labelResourceIfUserProvided(ctx, r.Namespace, "Secret",
88+
r.Spec.HttpdCustomization.CustomConfigSecret); err != nil {
89+
return err
90+
}
91+
}
92+
93+
// Label referenced ConfigMaps in ExtraMounts if user-provided
94+
for _, mount := range r.Spec.ExtraMounts {
95+
for _, vol := range mount.Propagation {
96+
if vol.ConfigMap != nil {
97+
if err := labelResourceIfUserProvided(ctx, r.Namespace, "ConfigMap",
98+
vol.ConfigMap.Name); err != nil {
99+
return err
100+
}
101+
}
102+
}
103+
}
104+
105+
return nil
106+
}
107+
```
108+
109+
**Generic Helper Function (in lib-common):**
110+
111+
```go
112+
// labelResourceIfUserProvided adds backup label to resource if it has no ownerReferences
113+
func labelResourceIfUserProvided(ctx context.Context, namespace, kind, name string) error {
114+
// Get the resource
115+
var obj client.Object
116+
switch kind {
117+
case "Secret":
118+
obj = &corev1.Secret{}
119+
case "ConfigMap":
120+
obj = &corev1.ConfigMap{}
121+
default:
122+
return fmt.Errorf("unsupported resource kind: %s", kind)
123+
}
124+
125+
key := client.ObjectKey{Namespace: namespace, Name: name}
126+
if err := k8sClient.Get(ctx, key, obj); err != nil {
127+
if errors.IsNotFound(err) {
128+
// Resource doesn't exist yet, skip labeling
129+
return nil
130+
}
131+
return err
132+
}
133+
134+
// Check if resource has ownerReferences
135+
if len(obj.GetOwnerReferences()) > 0 {
136+
// Resource is managed by controller, skip labeling
137+
return nil
138+
}
139+
140+
// Add backup label (user-provided resource)
141+
labels := obj.GetLabels()
142+
if labels == nil {
143+
labels = make(map[string]string)
144+
}
145+
146+
// Only add if not already labeled
147+
if labels["openstack.org/backup"] == "true" {
148+
return nil
149+
}
150+
151+
labels["openstack.org/backup"] = "true"
152+
labels["openstack.org/backup-category"] = "all"
153+
labels["openstack.org/restore-order"] = "1" // Secrets/ConfigMaps always order 1
154+
obj.SetLabels(labels)
155+
156+
// Update the resource
157+
return k8sClient.Update(ctx, obj)
158+
}
159+
```
160+
161+
**Key Points:**
162+
163+
1. **Reuse Existing Pattern**: No new webhook infrastructure needed
164+
2. **Service Operator Knowledge**: Each service operator knows what resources it references
165+
3. **Generic Helper**: Common logic to check ownerReferences and add labels
166+
4. **Works on Create and Update**: Handles both new and existing deployments
167+
5. **User-Provided Detection**: Only labels resources without ownerReferences
61168

62169
### OADP Integration
63170

0 commit comments

Comments
 (0)