2019-05-10 00:05:21 -07:00
package event
import (
2022-10-03 11:19:01 +02:00
"context"
2024-01-31 00:08:15 +02:00
"fmt"
"os"
"time"
2020-12-23 17:48:00 -08:00
2020-07-20 08:00:02 -07:00
"github.com/go-logr/logr"
2023-12-22 11:47:22 +01:00
"github.com/kyverno/kyverno/pkg/client/clientset/versioned/scheme"
2024-01-31 00:08:15 +02:00
"github.com/kyverno/kyverno/pkg/metrics"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric"
2022-05-17 16:14:31 +02:00
corev1 "k8s.io/api/core/v1"
2024-01-31 00:08:15 +02:00
eventsv1 "k8s.io/api/events/v1"
2024-04-10 09:41:22 +02:00
apierrors "k8s.io/apimachinery/pkg/api/errors"
2024-01-31 00:08:15 +02:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2019-05-10 00:05:21 -07:00
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2023-12-22 11:47:22 +01:00
"k8s.io/apimachinery/pkg/util/sets"
2024-01-31 00:08:15 +02:00
"k8s.io/apimachinery/pkg/util/wait"
v1 "k8s.io/client-go/kubernetes/typed/events/v1"
"k8s.io/client-go/tools/record/util"
"k8s.io/client-go/tools/reference"
"k8s.io/client-go/util/workqueue"
"k8s.io/utils/clock"
2019-05-10 00:05:21 -07:00
)
2023-01-26 22:19:02 +01:00
const (
2023-12-22 11:47:22 +01:00
Workers = 3
2024-01-31 00:08:15 +02:00
ControllerName = "kyverno-events"
2023-01-26 22:19:02 +01:00
workQueueRetryLimit = 3
)
2023-12-22 11:47:22 +01:00
// Interface to generate event
type Interface interface {
Add ( infoList ... Info )
2019-05-10 00:05:21 -07:00
}
2024-01-31 00:08:15 +02:00
// controller generate events
type controller struct {
logger logr . Logger
eventsClient v1 . EventsV1Interface
omitEvents sets . Set [ string ]
2024-08-28 19:09:58 +02:00
queue workqueue . TypedRateLimitingInterface [ any ]
2024-01-31 00:08:15 +02:00
clock clock . Clock
hostname string
droppedEventsCounter metric . Int64Counter
2024-04-10 09:41:22 +02:00
maxQueuedEvents int
2023-01-26 22:19:02 +01:00
}
2022-08-24 15:08:24 +02:00
// NewEventGenerator to generate a new event controller
2024-04-10 09:41:22 +02:00
func NewEventGenerator ( eventsClient v1 . EventsV1Interface , logger logr . Logger , maxQueuedEvents int , omitEvents ... string ) * controller {
2024-01-31 00:08:15 +02:00
clock := clock . RealClock { }
hostname , _ := os . Hostname ( )
meter := otel . GetMeterProvider ( ) . Meter ( metrics . MeterName )
droppedEventsCounter , err := meter . Int64Counter (
"kyverno_events_dropped" ,
metric . WithDescription ( "can be used to track the number of events dropped by the event generator" ) ,
)
if err != nil {
logger . Error ( err , "failed to register metric kyverno_events_dropped" )
}
return & controller {
logger : logger ,
eventsClient : eventsClient ,
omitEvents : sets . New ( omitEvents ... ) ,
2024-08-28 19:09:58 +02:00
queue : workqueue . NewNamedRateLimitingQueue ( workqueue . DefaultTypedControllerRateLimiter [ any ] ( ) , ControllerName ) ,
2024-01-31 00:08:15 +02:00
clock : clock ,
hostname : hostname ,
droppedEventsCounter : droppedEventsCounter ,
2024-04-10 09:41:22 +02:00
maxQueuedEvents : maxQueuedEvents ,
2019-12-26 11:50:41 -08:00
}
2023-07-18 13:01:09 +03:00
}
2022-05-17 08:19:03 +02:00
// Add queues an event for generation
2024-01-31 00:08:15 +02:00
func ( gen * controller ) Add ( infos ... Info ) {
2023-12-22 11:47:22 +01:00
logger := gen . logger
2023-01-07 15:14:51 -08:00
logger . V ( 3 ) . Info ( "generating events" , "count" , len ( infos ) )
2024-04-10 09:41:22 +02:00
if gen . maxQueuedEvents == 0 || gen . queue . Len ( ) > gen . maxQueuedEvents {
2024-07-04 16:12:48 +03:00
logger . V ( 3 ) . Info ( "exceeds the event queue limit, dropping the event" , "maxQueuedEvents" , gen . maxQueuedEvents , "current size" , gen . queue . Len ( ) )
2024-04-10 09:41:22 +02:00
return
}
2019-06-27 11:43:07 -07:00
for _ , info := range infos {
2023-12-22 11:47:22 +01:00
// don't create event for resources with generateName as the name is not generated yet
if info . Regarding . Name == "" {
logger . V ( 3 ) . Info ( "skipping event creation for resource without a name" , "kind" , info . Regarding . Kind , "name" , info . Regarding . Name , "namespace" , info . Regarding . Namespace )
2019-09-04 15:30:09 -07:00
continue
}
2023-12-22 11:47:22 +01:00
if gen . omitEvents . Has ( string ( info . Reason ) ) {
logger . V ( 6 ) . Info ( "omitting event" , "kind" , info . Regarding . Kind , "name" , info . Regarding . Name , "namespace" , info . Regarding . Namespace , "reason" , info . Reason )
continue
2023-05-10 17:18:41 +05:30
}
2023-12-22 11:47:22 +01:00
gen . emitEvent ( info )
logger . V ( 6 ) . Info ( "creating event" , "kind" , info . Regarding . Kind , "name" , info . Regarding . Name , "namespace" , info . Regarding . Namespace , "reason" , info . Reason )
2019-06-26 18:04:50 -07:00
}
2019-05-10 00:05:21 -07:00
}
2019-08-09 13:41:56 -07:00
// Run begins generator
2024-01-31 00:08:15 +02:00
func ( gen * controller ) Run ( ctx context . Context , workers int ) {
2023-12-22 11:47:22 +01:00
logger := gen . logger
2020-03-17 11:05:20 -07:00
logger . Info ( "start" )
2023-12-22 11:47:22 +01:00
defer logger . Info ( "terminated" )
2023-03-17 11:48:48 +01:00
defer utilruntime . HandleCrash ( )
2024-01-31 00:08:15 +02:00
var waitGroup wait . Group
for i := 0 ; i < workers ; i ++ {
waitGroup . StartWithContext ( ctx , func ( ctx context . Context ) {
for gen . processNextWorkItem ( ctx ) {
}
} )
2019-05-10 00:05:21 -07:00
}
2022-10-03 11:19:01 +02:00
<- ctx . Done ( )
2024-01-31 00:08:15 +02:00
gen . queue . ShutDownWithDrain ( )
waitGroup . Wait ( )
2019-05-15 07:30:22 -07:00
}
2019-07-19 16:17:10 -07:00
2024-01-31 00:08:15 +02:00
func ( gen * controller ) processNextWorkItem ( ctx context . Context ) bool {
logger := gen . logger
key , quit := gen . queue . Get ( )
if quit {
return false
2019-05-10 00:05:21 -07:00
}
2024-01-31 00:08:15 +02:00
defer gen . queue . Done ( key )
event , ok := key . ( * eventsv1 . Event )
if ! ok {
logger . Error ( nil , "failed to convert key to Info" , "key" , key )
return true
2019-07-19 16:17:10 -07:00
}
2024-01-31 00:08:15 +02:00
_ , err := gen . eventsClient . Events ( event . Namespace ) . Create ( ctx , event , metav1 . CreateOptions { } )
2024-04-10 09:41:22 +02:00
if err != nil && ! apierrors . IsNotFound ( err ) {
2024-01-31 00:08:15 +02:00
if gen . queue . NumRequeues ( key ) < workQueueRetryLimit {
logger . Error ( err , "failed to create event" , "key" , key )
gen . queue . AddRateLimited ( key )
return true
}
gen . droppedEventsCounter . Add ( ctx , 1 )
logger . Error ( err , "dropping event" , "key" , key )
2020-11-03 16:07:02 -08:00
}
2024-01-31 00:08:15 +02:00
gen . queue . Forget ( key )
return true
2019-07-19 16:17:10 -07:00
}
2024-01-31 00:08:15 +02:00
func ( gen * controller ) emitEvent ( key Info ) {
2023-12-22 11:47:22 +01:00
logger := gen . logger
2022-05-17 16:14:31 +02:00
eventType := corev1 . EventTypeWarning
2024-02-23 12:34:04 +02:00
if key . Type != "" {
eventType = key . Type
} else if key . Reason == PolicyApplied || key . Reason == PolicySkipped {
2022-05-17 16:14:31 +02:00
eventType = corev1 . EventTypeNormal
2021-06-30 00:43:11 +03:00
}
2024-01-31 00:08:15 +02:00
timestamp := metav1 . MicroTime { Time : time . Now ( ) }
refRegarding , err := reference . GetReference ( scheme . Scheme , & key . Regarding )
if err != nil {
logger . Error ( err , "Could not construct reference, will not report event" , "object" , & key . Regarding , "eventType" , eventType , "reason" , string ( key . Reason ) , "message" , key . Message )
return
}
var refRelated * corev1 . ObjectReference
if key . Related != nil {
refRelated , err = reference . GetReference ( scheme . Scheme , key . Related )
if err != nil {
logger . V ( 9 ) . Info ( "Could not construct reference" , "object" , key . Related , "err" , err )
}
}
if ! util . ValidateEventType ( eventType ) {
logger . Error ( nil , "Unsupported event type" , "eventType" , eventType )
return
2019-05-10 00:05:21 -07:00
}
2024-01-31 00:08:15 +02:00
reportingController := string ( key . Source )
reportingInstance := reportingController + "-" + gen . hostname
t := metav1 . Time { Time : gen . clock . Now ( ) }
namespace := refRegarding . Namespace
if namespace == "" {
namespace = metav1 . NamespaceDefault
}
2024-07-10 18:31:32 +04:00
message := key . Message
if len ( message ) > 1024 {
message = message [ 0 : 1021 ] + "..."
}
2024-01-31 00:08:15 +02:00
event := & eventsv1 . Event {
ObjectMeta : metav1 . ObjectMeta {
Name : fmt . Sprintf ( "%v.%x" , refRegarding . Name , t . UnixNano ( ) ) ,
Namespace : namespace ,
} ,
EventTime : timestamp ,
Series : nil ,
ReportingController : reportingController ,
ReportingInstance : reportingInstance ,
Action : string ( key . Action ) ,
Reason : string ( key . Reason ) ,
Regarding : * refRegarding ,
Related : refRelated ,
2024-07-10 18:31:32 +04:00
Note : message ,
2024-01-31 00:08:15 +02:00
Type : eventType ,
}
gen . queue . Add ( event )
2019-05-10 00:05:21 -07:00
}