Skip to content

Commit 0d89fb2

Browse files
authored
Merge pull request #323 from aramase/event-recorder
feat: add event recorder for secrets creation and rotation
2 parents 0488598 + 36a3445 commit 0d89fb2

File tree

17 files changed

+599
-112
lines changed

17 files changed

+599
-112
lines changed

cmd/secrets-store-csi-driver/main.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package main
1818

1919
import (
2020
"flag"
21-
"sync"
2221
"time"
2322

2423
"sigs.k8s.io/secrets-store-csi-driver/pkg/metrics"
@@ -98,15 +97,10 @@ func main() {
9897
log.Fatalf("failed to start manager, error: %+v", err)
9998
}
10099

101-
reconciler := &controllers.SecretProviderClassPodStatusReconciler{
102-
Client: mgr.GetClient(),
103-
Mutex: &sync.Mutex{},
104-
Scheme: mgr.GetScheme(),
105-
NodeID: *nodeID,
106-
Reader: mgr.GetCache(),
107-
Writer: mgr.GetClient(),
100+
reconciler, err := controllers.New(mgr, *nodeID)
101+
if err != nil {
102+
log.Fatalf("failed to create secret provider class pod status reconciler, error: %+v", err)
108103
}
109-
110104
if err = reconciler.SetupWithManager(mgr); err != nil {
111105
log.Fatalf("failed to create controller, error: %+v", err)
112106
}

config/rbac/role.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ metadata:
66
creationTimestamp: null
77
name: secretproviderclasses-role
88
rules:
9+
- apiGroups:
10+
- ""
11+
resources:
12+
- events
13+
verbs:
14+
- create
15+
- patch
916
- apiGroups:
1017
- ""
1118
resources:

controllers/secretproviderclasspodstatus_controller.go

Lines changed: 101 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,21 @@ package controllers
1919
import (
2020
"context"
2121
"fmt"
22+
"regexp"
2223
"strings"
2324
"sync"
2425
"time"
2526

27+
"k8s.io/client-go/kubernetes"
28+
29+
"sigs.k8s.io/controller-runtime/pkg/manager"
30+
31+
"k8s.io/client-go/tools/record"
32+
2633
log "github.com/sirupsen/logrus"
2734

2835
"sigs.k8s.io/secrets-store-csi-driver/apis/v1alpha1"
36+
"sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned/scheme"
2937
"sigs.k8s.io/secrets-store-csi-driver/pkg/util/fileutil"
3038
"sigs.k8s.io/secrets-store-csi-driver/pkg/util/secretutil"
3139

@@ -35,27 +43,48 @@ import (
3543

3644
corev1 "k8s.io/api/core/v1"
3745
v1 "k8s.io/api/core/v1"
38-
"k8s.io/apimachinery/pkg/api/errors"
3946
apierrors "k8s.io/apimachinery/pkg/api/errors"
4047
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4148
"k8s.io/apimachinery/pkg/runtime"
49+
apiruntime "k8s.io/apimachinery/pkg/runtime"
4250
"k8s.io/apimachinery/pkg/types"
4351
"k8s.io/apimachinery/pkg/util/wait"
52+
clientcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
4453
)
4554

4655
const (
47-
secretManagedLabel = "secrets-store.csi.k8s.io/managed"
56+
secretManagedLabel = "secrets-store.csi.k8s.io/managed"
57+
secretCreationFailedReason = "FailedToCreateSecret"
4858
)
4959

5060
// SecretProviderClassPodStatusReconciler reconciles a SecretProviderClassPodStatus object
5161
type SecretProviderClassPodStatusReconciler struct {
5262
client.Client
53-
Mutex *sync.Mutex
54-
Log *log.Logger
55-
Scheme *runtime.Scheme
56-
NodeID string
57-
Reader client.Reader
58-
Writer client.Writer
63+
mutex *sync.Mutex
64+
log *log.Logger
65+
scheme *apiruntime.Scheme
66+
nodeID string
67+
reader client.Reader
68+
writer client.Writer
69+
eventRecorder record.EventRecorder
70+
}
71+
72+
// New creates a new SecretProviderClassPodStatusReconciler
73+
func New(mgr manager.Manager, nodeID string) (*SecretProviderClassPodStatusReconciler, error) {
74+
eventBroadcaster := record.NewBroadcaster()
75+
kubeClient := kubernetes.NewForConfigOrDie(mgr.GetConfig())
76+
eventBroadcaster.StartRecordingToSink(&clientcorev1.EventSinkImpl{Interface: kubeClient.CoreV1().Events("")})
77+
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "csi-secrets-store-controller"})
78+
79+
return &SecretProviderClassPodStatusReconciler{
80+
Client: mgr.GetClient(),
81+
mutex: &sync.Mutex{},
82+
scheme: mgr.GetScheme(),
83+
nodeID: nodeID,
84+
reader: mgr.GetCache(),
85+
writer: mgr.GetClient(),
86+
eventRecorder: recorder,
87+
}, nil
5988
}
6089

6190
func (r *SecretProviderClassPodStatusReconciler) RunPatcher(stopCh <-chan struct{}) {
@@ -76,15 +105,15 @@ func (r *SecretProviderClassPodStatusReconciler) RunPatcher(stopCh <-chan struct
76105

77106
func (r *SecretProviderClassPodStatusReconciler) Patcher() error {
78107
log.Debugf("patcher started")
79-
r.Mutex.Lock()
80-
defer r.Mutex.Unlock()
108+
r.mutex.Lock()
109+
defer r.mutex.Unlock()
81110

82111
ctx := context.Background()
83112
spcPodStatusList := &v1alpha1.SecretProviderClassPodStatusList{}
84113
spcMap := make(map[string]v1alpha1.SecretProviderClass)
85114
secretOwnerMap := make(map[types.NamespacedName][]*v1alpha1.SecretProviderClassPodStatus)
86115
// get a list of all spc pod status that belong to the node
87-
err := r.Reader.List(ctx, spcPodStatusList, r.ListOptionsLabelSelector())
116+
err := r.reader.List(ctx, spcPodStatusList, r.ListOptionsLabelSelector())
88117
if err != nil {
89118
return fmt.Errorf("failed to list secret provider class pod status, err: %+v", err)
90119
}
@@ -96,7 +125,7 @@ func (r *SecretProviderClassPodStatusReconciler) Patcher() error {
96125
if val, exists := spcMap[spcPodStatuses[i].Namespace+"/"+spcName]; exists {
97126
spc = &val
98127
} else {
99-
if err := r.Reader.Get(ctx, client.ObjectKey{Namespace: spcPodStatuses[i].Namespace, Name: spcName}, spc); err != nil {
128+
if err := r.reader.Get(ctx, client.ObjectKey{Namespace: spcPodStatuses[i].Namespace, Name: spcName}, spc); err != nil {
100129
log.Errorf("failed to get spc %s, err: %+v", spcName, err)
101130
return err
102131
}
@@ -140,25 +169,26 @@ func (r *SecretProviderClassPodStatusReconciler) Patcher() error {
140169
// ListOptionsLabelSelector returns a ListOptions with a label selector for node name.
141170
func (r *SecretProviderClassPodStatusReconciler) ListOptionsLabelSelector() client.ListOption {
142171
return client.MatchingLabels(map[string]string{
143-
v1alpha1.InternalNodeLabel: r.NodeID,
172+
v1alpha1.InternalNodeLabel: r.nodeID,
144173
})
145174
}
146175

147176
// +kubebuilder:rbac:groups=secrets-store.csi.x-k8s.io,resources=secretproviderclasspodstatuses,verbs=get;list;watch;create;update;patch;delete
148177
// +kubebuilder:rbac:groups=secrets-store.csi.x-k8s.io,resources=secretproviderclasspodstatuses/status,verbs=get;update;patch
149178
// +kubebuilder:rbac:groups=secrets-store.csi.x-k8s.io,resources=secretproviderclasses,verbs=get;list;watch
150179
// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
180+
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
151181

152182
func (r *SecretProviderClassPodStatusReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
153-
r.Mutex.Lock()
154-
defer r.Mutex.Unlock()
183+
r.mutex.Lock()
184+
defer r.mutex.Unlock()
155185

156186
ctx := context.Background()
157-
logger := log.WithFields(log.Fields{"secretproviderclasspodstatus": req.NamespacedName, "node": r.NodeID})
187+
logger := log.WithFields(log.Fields{"secretproviderclasspodstatus": req.NamespacedName, "node": r.nodeID})
158188
logger.Info("reconcile started")
159189

160190
var spcPodStatus v1alpha1.SecretProviderClassPodStatus
161-
if err := r.Get(ctx, req.NamespacedName, &spcPodStatus); err != nil {
191+
if err := r.reader.Get(ctx, req.NamespacedName, &spcPodStatus); err != nil {
162192
if apierrors.IsNotFound(err) {
163193
return ctrl.Result{}, nil
164194
}
@@ -177,14 +207,14 @@ func (r *SecretProviderClassPodStatusReconciler) Reconcile(req ctrl.Request) (ct
177207
logger.Info("node label not found, ignoring this spc pod status")
178208
return ctrl.Result{}, nil
179209
}
180-
if !strings.EqualFold(node, r.NodeID) {
210+
if !strings.EqualFold(node, r.nodeID) {
181211
logger.Infof("ignoring as spc pod status belongs to node %s", node)
182212
return ctrl.Result{}, nil
183213
}
184214

185215
spcName := spcPodStatus.Status.SecretProviderClassName
186216
spc := &v1alpha1.SecretProviderClass{}
187-
if err := r.Reader.Get(ctx, client.ObjectKey{Namespace: req.Namespace, Name: spcName}, spc); err != nil {
217+
if err := r.reader.Get(ctx, client.ObjectKey{Namespace: req.Namespace, Name: spcName}, spc); err != nil {
188218
logger.Errorf("failed to get spc %s, err: %+v", spcName, err)
189219
if apierrors.IsNotFound(err) {
190220
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
@@ -197,8 +227,18 @@ func (r *SecretProviderClassPodStatusReconciler) Reconcile(req ctrl.Request) (ct
197227
return ctrl.Result{}, nil
198228
}
199229

230+
// podObjectReference is an object reference to the pod that spc pod status
231+
// is created for. The object reference is created with minimal required fields
232+
// name, namespace and UID. By doing this we can skip an additional client call
233+
// to fetch the pod object
234+
podObjectReference, err := getPodObjectReference(spcPodStatus)
235+
if err != nil {
236+
logger.Errorf("failed to get pod object reference, error: %+v", err)
237+
}
238+
200239
files, err := fileutil.GetMountedFiles(spcPodStatus.Status.TargetPath)
201240
if err != nil {
241+
r.generateEvent(podObjectReference, corev1.EventTypeWarning, secretCreationFailedReason, fmt.Sprintf("failed to get mounted files, err: %+v", err))
202242
logger.Errorf("failed to get mounted files, err: %+v", err)
203243
return ctrl.Result{RequeueAfter: 10 * time.Second}, err
204244
}
@@ -225,6 +265,7 @@ func (r *SecretProviderClassPodStatusReconciler) Reconcile(req ctrl.Request) (ct
225265

226266
datamap := make(map[string][]byte)
227267
if datamap, err = secretutil.GetSecretData(secretObj.Data, secretType, files); err != nil {
268+
r.generateEvent(podObjectReference, corev1.EventTypeWarning, secretCreationFailedReason, fmt.Sprintf("failed to get data in spc %s/%s for secret %s, err: %+v", req.Namespace, spcName, secretName, err))
228269
log.Errorf("failed to get data in spc %s/%s for secret %s, err: %+v", req.Namespace, spcName, secretName, err)
229270
errs = append(errs, fmt.Errorf("failed to get data in spc %s/%s for secret %s, err: %+v", req.Namespace, spcName, secretName, err))
230271
continue
@@ -256,6 +297,7 @@ func (r *SecretProviderClassPodStatusReconciler) Reconcile(req ctrl.Request) (ct
256297
Factor: 1.0,
257298
Jitter: 0.1,
258299
}, f); err != nil {
300+
r.generateEvent(podObjectReference, corev1.EventTypeWarning, secretCreationFailedReason, err.Error())
259301
return ctrl.Result{RequeueAfter: 5 * time.Second}, err
260302
}
261303
}
@@ -291,7 +333,7 @@ func (r *SecretProviderClassPodStatusReconciler) createK8sSecret(ctx context.Con
291333
Data: datamap,
292334
}
293335

294-
err := r.Writer.Create(ctx, secret)
336+
err := r.writer.Create(ctx, secret)
295337
if err == nil {
296338
log.Infof("created k8s secret: %s/%s", namespace, name)
297339
return nil
@@ -309,8 +351,11 @@ func (r *SecretProviderClassPodStatusReconciler) patchSecretWithOwnerRef(ctx con
309351
Namespace: namespace,
310352
Name: name,
311353
}
312-
err := r.Client.Get(ctx, secretKey, secret)
313-
if err != nil && !errors.IsNotFound(err) {
354+
if err := r.Client.Get(ctx, secretKey, secret); err != nil {
355+
if apierrors.IsNotFound(err) {
356+
log.Debugf("secret %s/%s not found for patching", namespace, name)
357+
return nil
358+
}
314359
return err
315360
}
316361

@@ -327,14 +372,14 @@ func (r *SecretProviderClassPodStatusReconciler) patchSecretWithOwnerRef(ctx con
327372
continue
328373
}
329374
needsPatch = true
330-
err = controllerutil.SetOwnerReference(spcPodStatus[i], secret, r.Scheme)
375+
err := controllerutil.SetOwnerReference(spcPodStatus[i], secret, r.scheme)
331376
if err != nil {
332377
return err
333378
}
334379
}
335380

336381
if needsPatch {
337-
return r.Writer.Patch(ctx, secret, patch)
382+
return r.writer.Patch(ctx, secret, patch)
338383
}
339384
return nil
340385
}
@@ -355,3 +400,35 @@ func (r *SecretProviderClassPodStatusReconciler) secretExists(ctx context.Contex
355400
}
356401
return false, err
357402
}
403+
404+
// getPodObjectReference returns a v1.ObjectReference for the pod object
405+
func getPodObjectReference(spcPodStatus v1alpha1.SecretProviderClassPodStatus) (*v1.ObjectReference, error) {
406+
podName := spcPodStatus.Status.PodName
407+
podNamespace := spcPodStatus.Namespace
408+
podUID := getPodUIDFromTargetPath(spcPodStatus.Status.TargetPath)
409+
if podUID == "" {
410+
return nil, fmt.Errorf("failed to get pod UID from target path")
411+
}
412+
return &v1.ObjectReference{
413+
Name: podName,
414+
Namespace: podNamespace,
415+
UID: types.UID(podUID),
416+
}, nil
417+
}
418+
419+
// getPodUIDFromTargetPath returns podUID from targetPath
420+
func getPodUIDFromTargetPath(targetPath string) string {
421+
re := regexp.MustCompile(`[\\|\/]+pods[\\|\/]+(.+?)[\\|\/]+volumes`)
422+
match := re.FindStringSubmatch(targetPath)
423+
if len(match) < 2 {
424+
return ""
425+
}
426+
return match[1]
427+
}
428+
429+
// generateEvent generates an event
430+
func (r *SecretProviderClassPodStatusReconciler) generateEvent(obj runtime.Object, eventType, reason, message string) {
431+
if obj != nil {
432+
r.eventRecorder.Eventf(obj, eventType, reason, message)
433+
}
434+
}

0 commit comments

Comments
 (0)