Skip to content

Commit 9f6424a

Browse files
committed
feat: add comprehensive event recording for ConfigMap operations
Add EventRecorder to both controllers and webhook for better observability: **NamespaceReconciler Events:** - ReconciliationSkipped (Normal): Namespace opted out of WIF injection - ConfigMapCreated (Normal): Direct identity ConfigMap created - ConfigMapUpdated (Normal): Direct identity ConfigMap updated (self-healing) - ConfigMapCreateFailed (Warning): Failed to create ConfigMap - ConfigMapUpdateFailed (Warning): Failed to update ConfigMap **ServiceAccountReconciler Events:** - ReconciliationSkipped (Normal): Namespace opted out of WIF injection - ConfigMapCreated (Normal): Impersonation ConfigMap created with GCP SA details - ConfigMapUpdated (Normal): Impersonation ConfigMap updated (annotation change/self-healing) - ConfigMapCreateFailed (Warning): Failed to create ConfigMap - ConfigMapUpdateFailed (Warning): Failed to update ConfigMap **Webhook Events (Enhanced):** - ConfigMapCreated (Normal): ConfigMap created during pod admission with type info - ConfigMapCreateFailed (Warning): Failed to create ConfigMap during pod admission **Metrics:** - Added 'error' label to track failed operations - Error operations now properly excluded from success metrics All events include descriptive messages with ConfigMap names and relevant context. Events are nil-safe to support testing without EventRecorder.
1 parent 0099c6a commit 9f6424a

File tree

4 files changed

+71
-6
lines changed

4 files changed

+71
-6
lines changed

cmd/main.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,17 +206,19 @@ func main() {
206206

207207
// Setup Namespace controller for direct identity ConfigMap management
208208
if err = (&controller.NamespaceReconciler{
209-
Client: mgr.GetClient(),
210-
Scheme: mgr.GetScheme(),
209+
Client: mgr.GetClient(),
210+
Scheme: mgr.GetScheme(),
211+
Recorder: mgr.GetEventRecorderFor("wif-namespace-controller"),
211212
}).SetupWithManager(mgr); err != nil {
212213
setupLog.Error(err, "unable to create controller", "controller", "Namespace")
213214
os.Exit(1)
214215
}
215216

216217
// Setup ServiceAccount controller for impersonation ConfigMap reconciliation
217218
if err = (&controller.ServiceAccountReconciler{
218-
Client: mgr.GetClient(),
219-
Scheme: mgr.GetScheme(),
219+
Client: mgr.GetClient(),
220+
Scheme: mgr.GetScheme(),
221+
Recorder: mgr.GetEventRecorderFor("wif-serviceaccount-controller"),
220222
}).SetupWithManager(mgr); err != nil {
221223
setupLog.Error(err, "unable to create controller", "controller", "ServiceAccount")
222224
os.Exit(1)

internal/controller/namespace_controller.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package controller
1818

1919
// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch
2020
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch
21+
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
2122

2223
import (
2324
"context"
@@ -29,6 +30,7 @@ import (
2930
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3031
"k8s.io/apimachinery/pkg/runtime"
3132
"k8s.io/apimachinery/pkg/types"
33+
"k8s.io/client-go/tools/record"
3234
ctrl "sigs.k8s.io/controller-runtime"
3335
"sigs.k8s.io/controller-runtime/pkg/client"
3436
"sigs.k8s.io/controller-runtime/pkg/log"
@@ -64,7 +66,8 @@ func init() {
6466
// NamespaceReconciler reconciles Namespace objects and ensures direct identity ConfigMaps exist
6567
type NamespaceReconciler struct {
6668
client.Client
67-
Scheme *runtime.Scheme
69+
Scheme *runtime.Scheme
70+
Recorder record.EventRecorder
6871
}
6972

7073
// SetupWithManager sets up the controller with the Manager
@@ -101,6 +104,10 @@ func (r *NamespaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
101104
// This prevents credential access even via manual ConfigMap mounting (security requirement)
102105
if r.isWIFInjectionDisabled(namespace) {
103106
log.V(1).Info("Skipping namespace with WIF injection disabled", "namespace", namespace.Name)
107+
if r.Recorder != nil {
108+
r.Recorder.Event(namespace, corev1.EventTypeNormal, "ReconciliationSkipped",
109+
"WIF injection disabled - skipping direct identity ConfigMap reconciliation")
110+
}
104111
return ctrl.Result{}, nil
105112
}
106113

@@ -158,10 +165,19 @@ func (r *NamespaceReconciler) reconcileDirectConfigMap(ctx context.Context, name
158165
directConfigMapOperations.WithLabelValues("noop").Inc()
159166
return nil
160167
}
168+
directConfigMapOperations.WithLabelValues("error").Inc()
169+
if r.Recorder != nil {
170+
r.Recorder.Event(namespace, corev1.EventTypeWarning, "ConfigMapCreateFailed",
171+
fmt.Sprintf("Failed to create direct identity ConfigMap: %v", err))
172+
}
161173
return fmt.Errorf("failed to create ConfigMap %s/%s: %w", namespace.Name, directIdentityConfigMapName, err)
162174
}
163175

164176
directConfigMapOperations.WithLabelValues("create").Inc()
177+
if r.Recorder != nil {
178+
r.Recorder.Event(namespace, corev1.EventTypeNormal, "ConfigMapCreated",
179+
fmt.Sprintf("Created direct identity ConfigMap '%s'", directIdentityConfigMapName))
180+
}
165181
log.Info("Successfully created direct identity ConfigMap",
166182
"configMap", directIdentityConfigMapName,
167183
"namespace", namespace.Name)
@@ -184,10 +200,19 @@ func (r *NamespaceReconciler) reconcileDirectConfigMap(ctx context.Context, name
184200
existingCM.Labels = desiredCM.Labels
185201

186202
if err := r.Update(ctx, existingCM); err != nil {
203+
directConfigMapOperations.WithLabelValues("error").Inc()
204+
if r.Recorder != nil {
205+
r.Recorder.Event(namespace, corev1.EventTypeWarning, "ConfigMapUpdateFailed",
206+
fmt.Sprintf("Failed to update direct identity ConfigMap: %v", err))
207+
}
187208
return fmt.Errorf("failed to update ConfigMap %s/%s: %w", namespace.Name, directIdentityConfigMapName, err)
188209
}
189210

190211
directConfigMapOperations.WithLabelValues("update").Inc()
212+
if r.Recorder != nil {
213+
r.Recorder.Event(namespace, corev1.EventTypeNormal, "ConfigMapUpdated",
214+
fmt.Sprintf("Updated direct identity ConfigMap '%s' (self-healing)", directIdentityConfigMapName))
215+
}
191216
log.Info("Successfully updated direct identity ConfigMap",
192217
"configMap", directIdentityConfigMapName,
193218
"namespace", namespace.Name)

internal/controller/serviceaccount_controller.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3131
"k8s.io/apimachinery/pkg/runtime"
3232
"k8s.io/apimachinery/pkg/types"
33+
"k8s.io/client-go/tools/record"
3334
ctrl "sigs.k8s.io/controller-runtime"
3435
"sigs.k8s.io/controller-runtime/pkg/client"
3536
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -62,7 +63,8 @@ func init() {
6263
// ServiceAccountReconciler reconciles ServiceAccount objects with WIF annotations
6364
type ServiceAccountReconciler struct {
6465
client.Client
65-
Scheme *runtime.Scheme
66+
Scheme *runtime.Scheme
67+
Recorder record.EventRecorder
6668
}
6769

6870
// SetupWithManager sets up the controller with the Manager
@@ -99,6 +101,10 @@ func (r *ServiceAccountReconciler) Reconcile(ctx context.Context, req ctrl.Reque
99101
if r.isWIFInjectionDisabled(namespace) {
100102
log.V(1).Info("Skipping ServiceAccount in namespace with WIF injection disabled",
101103
"serviceAccount", sa.Name, "namespace", sa.Namespace)
104+
if r.Recorder != nil {
105+
r.Recorder.Event(sa, corev1.EventTypeNormal, "ReconciliationSkipped",
106+
"WIF injection disabled in namespace - skipping impersonation ConfigMap reconciliation")
107+
}
102108
return ctrl.Result{}, nil
103109
}
104110

@@ -180,10 +186,20 @@ func (r *ServiceAccountReconciler) reconcileConfigMap(ctx context.Context, sa *c
180186
configMapReconcileOperations.WithLabelValues("noop").Inc()
181187
return nil
182188
}
189+
configMapReconcileOperations.WithLabelValues("error").Inc()
190+
if r.Recorder != nil {
191+
r.Recorder.Event(sa, corev1.EventTypeWarning, "ConfigMapCreateFailed",
192+
fmt.Sprintf("Failed to create impersonation ConfigMap '%s': %v", config.CredentialsConfigMap, err))
193+
}
183194
return fmt.Errorf("failed to create ConfigMap %s/%s: %w", sa.Namespace, config.CredentialsConfigMap, err)
184195
}
185196

186197
configMapReconcileOperations.WithLabelValues("create").Inc()
198+
if r.Recorder != nil {
199+
r.Recorder.Event(sa, corev1.EventTypeNormal, "ConfigMapCreated",
200+
fmt.Sprintf("Created impersonation ConfigMap '%s' for GCP service account '%s'",
201+
config.CredentialsConfigMap, config.GoogleServiceAccount))
202+
}
187203
log.Info("Successfully created impersonation ConfigMap",
188204
"configMap", config.CredentialsConfigMap,
189205
"namespace", sa.Namespace)
@@ -208,10 +224,20 @@ func (r *ServiceAccountReconciler) reconcileConfigMap(ctx context.Context, sa *c
208224
existingCM.OwnerReferences = desiredCM.OwnerReferences
209225

210226
if err := r.Update(ctx, existingCM); err != nil {
227+
configMapReconcileOperations.WithLabelValues("error").Inc()
228+
if r.Recorder != nil {
229+
r.Recorder.Event(sa, corev1.EventTypeWarning, "ConfigMapUpdateFailed",
230+
fmt.Sprintf("Failed to update impersonation ConfigMap '%s': %v", config.CredentialsConfigMap, err))
231+
}
211232
return fmt.Errorf("failed to update ConfigMap %s/%s: %w", sa.Namespace, config.CredentialsConfigMap, err)
212233
}
213234

214235
configMapReconcileOperations.WithLabelValues("update").Inc()
236+
if r.Recorder != nil {
237+
r.Recorder.Event(sa, corev1.EventTypeNormal, "ConfigMapUpdated",
238+
fmt.Sprintf("Updated impersonation ConfigMap '%s' for GCP service account '%s' (annotation change or self-healing)",
239+
config.CredentialsConfigMap, config.GoogleServiceAccount))
240+
}
215241
log.Info("Successfully updated impersonation ConfigMap",
216242
"configMap", config.CredentialsConfigMap,
217243
"namespace", sa.Namespace)

internal/webhook/v1/pod_webhook.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,22 @@ func (d *PodCustomDefaulter) ensureConfigMaps(ctx context.Context, namespace str
296296
return nil
297297
}
298298
configMapOperations.WithLabelValues("create", "error").Inc()
299+
if d.Recorder != nil {
300+
d.Recorder.Event(pod, corev1.EventTypeWarning, "ConfigMapCreateFailed",
301+
fmt.Sprintf("Failed to create WIF ConfigMap '%s': %v", configMapName, err))
302+
}
299303
return fmt.Errorf("failed to create ConfigMap %s/%s: %w", namespace, configMapName, err)
300304
}
301305

302306
configMapOperations.WithLabelValues("create", "success").Inc()
307+
if d.Recorder != nil {
308+
cmType := "direct identity"
309+
if !config.UseDirectIdentity {
310+
cmType = fmt.Sprintf("impersonation (GCP SA: %s)", config.GoogleServiceAccount)
311+
}
312+
d.Recorder.Event(pod, corev1.EventTypeNormal, "ConfigMapCreated",
313+
fmt.Sprintf("Created WIF ConfigMap '%s' (%s)", configMapName, cmType))
314+
}
303315
podlog.Info("Created WIF ConfigMap on-demand", "configmap", configMapName, "namespace", namespace)
304316
return nil
305317
}

0 commit comments

Comments
 (0)