mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-08-20 13:06:51 +00:00
* chore * perfromance improvements Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * perfromance improvements Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> * feat(performance): removed duplicate client calls from all admission paths Signed-off-by: Oliver Baehler <oliver@sudo-i.net> --------- Signed-off-by: Oliver Baehler <oliver@sudo-i.net>
203 lines
4.3 KiB
Go
203 lines
4.3 KiB
Go
// Copyright 2020-2026 Project Capsule Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package events
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/go-logr/logr"
|
|
corev1 "k8s.io/api/core/v1"
|
|
eventsv1 "k8s.io/api/events/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
k8sevents "k8s.io/client-go/tools/events"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"github.com/projectcapsule/capsule/pkg/runtime/configuration"
|
|
)
|
|
|
|
const (
|
|
ReportingController = "controller.projectcapsule.dev"
|
|
ReportingInstance = "capsule-admission"
|
|
|
|
eventQueueSize = 1024
|
|
eventCreateTimeout = 10 * time.Second
|
|
)
|
|
|
|
type EventRecorder interface {
|
|
k8sevents.EventRecorder
|
|
LabeledEvent(regarding runtime.Object, eventType string, reason string, action string, note string) LabeledEvent
|
|
}
|
|
|
|
type eventEmitter interface {
|
|
Emit(ctx context.Context, e LabeledEvent)
|
|
}
|
|
|
|
type eventRecorder struct {
|
|
k8sevents.EventRecorder
|
|
|
|
client client.Client
|
|
configuration configuration.Configuration
|
|
log logr.Logger
|
|
queue chan *eventsv1.Event
|
|
}
|
|
|
|
func NewEventRecorder(
|
|
c client.Client,
|
|
log logr.Logger,
|
|
recorder k8sevents.EventRecorder,
|
|
configuration configuration.Configuration,
|
|
) EventRecorder {
|
|
r := &eventRecorder{
|
|
EventRecorder: recorder,
|
|
client: c,
|
|
log: log.WithName("event-recorder"),
|
|
configuration: configuration,
|
|
}
|
|
|
|
if c != nil {
|
|
r.queue = make(chan *eventsv1.Event, eventQueueSize)
|
|
|
|
go r.run()
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
func (r *eventRecorder) Emit(ctx context.Context, e LabeledEvent) {
|
|
if r == nil {
|
|
return
|
|
}
|
|
|
|
if r.client == nil {
|
|
r.log.Error(nil, "cannot emit labeled event: client is nil")
|
|
|
|
return
|
|
}
|
|
|
|
if e == nil {
|
|
r.log.Error(nil, "cannot emit labeled event: event is nil")
|
|
|
|
return
|
|
}
|
|
|
|
if e.Reason() == "" {
|
|
r.log.Error(nil, "cannot emit labeled event: reason is empty")
|
|
|
|
return
|
|
}
|
|
|
|
if e.Action() == "" {
|
|
r.log.Error(nil, "cannot emit labeled event: action is empty")
|
|
|
|
return
|
|
}
|
|
|
|
regardingRef, metaObj, err := objectReference(e.Regarding())
|
|
if err != nil {
|
|
r.log.Error(err, "cannot emit labeled event: build regarding reference")
|
|
|
|
return
|
|
}
|
|
|
|
namespace := metaObj.GetNamespace()
|
|
if namespace == "" {
|
|
namespace = r.configuration.Events().ClusterEventNamespace
|
|
}
|
|
|
|
if namespace == "" {
|
|
r.log.Error(nil, "cannot emit labeled event: namespace is empty")
|
|
|
|
return
|
|
}
|
|
|
|
event := &eventsv1.Event{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: metaObj.GetName(),
|
|
Namespace: namespace,
|
|
Labels: e.Labels(),
|
|
Annotations: e.Annotations(),
|
|
},
|
|
EventTime: metav1.MicroTime{Time: time.Now()},
|
|
ReportingController: ReportingController,
|
|
ReportingInstance: ReportingInstance,
|
|
Action: e.Action(),
|
|
Reason: e.Reason(),
|
|
Regarding: regardingRef,
|
|
Note: e.Note(),
|
|
Type: e.EventType(),
|
|
}
|
|
|
|
if e.Related() != nil {
|
|
relatedRef, _, err := objectReference(e.Related())
|
|
if err != nil {
|
|
r.log.Error(err, "cannot emit labeled event: build related reference")
|
|
|
|
return
|
|
}
|
|
|
|
event.Related = &relatedRef
|
|
}
|
|
|
|
select {
|
|
case r.queue <- event:
|
|
default:
|
|
r.log.Error(
|
|
nil,
|
|
"cannot enqueue labeled event: queue is full",
|
|
"reason", e.Reason(),
|
|
"action", e.Action(),
|
|
"type", e.EventType(),
|
|
"regarding", regardingRef.Name,
|
|
"namespace", namespace,
|
|
)
|
|
}
|
|
}
|
|
|
|
func (r *eventRecorder) run() {
|
|
for event := range r.queue {
|
|
ctx, cancel := context.WithTimeout(context.Background(), eventCreateTimeout)
|
|
err := r.client.Create(ctx, event)
|
|
|
|
cancel()
|
|
|
|
if err == nil {
|
|
continue
|
|
}
|
|
|
|
r.log.Error(
|
|
err,
|
|
"cannot emit labeled event",
|
|
"reason", event.Reason,
|
|
"action", event.Action,
|
|
"type", event.Type,
|
|
"regarding", event.Regarding.Name,
|
|
"namespace", event.Namespace,
|
|
)
|
|
}
|
|
}
|
|
|
|
func objectReference(obj runtime.Object) (corev1.ObjectReference, metav1.Object, error) {
|
|
if obj == nil {
|
|
return corev1.ObjectReference{}, nil, fmt.Errorf("object is nil")
|
|
}
|
|
|
|
metaObj, ok := obj.(metav1.Object)
|
|
if !ok {
|
|
return corev1.ObjectReference{}, nil, fmt.Errorf("%T does not implement metav1.Object", obj)
|
|
}
|
|
|
|
gvk := obj.GetObjectKind().GroupVersionKind()
|
|
|
|
return corev1.ObjectReference{
|
|
APIVersion: gvk.GroupVersion().String(),
|
|
Kind: gvk.Kind,
|
|
Namespace: metaObj.GetNamespace(),
|
|
Name: metaObj.GetName(),
|
|
UID: metaObj.GetUID(),
|
|
}, metaObj, nil
|
|
}
|