package breaker import ( "context" "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/metrics" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" sdkmetric "go.opentelemetry.io/otel/metric" ) type Breaker interface { Do(context.Context, func(context.Context) error) error } type breaker struct { name string drops sdkmetric.Int64Counter total sdkmetric.Int64Counter open func(context.Context) bool } func NewBreaker(name string, open func(context.Context) bool) *breaker { logger := logging.WithName("circuit-breaker") meter := otel.GetMeterProvider().Meter(metrics.MeterName) drops, err := meter.Int64Counter( "kyverno_breaker_drops", sdkmetric.WithDescription("track the number of times the breaker failed open and dropped"), ) if err != nil { logger.Error(err, "Failed to create instrument, kyverno_breaker_drops") } total, err := meter.Int64Counter( "kyverno_breaker_total", sdkmetric.WithDescription("track number of times the breaker was invoked"), ) if err != nil { logger.Error(err, "Failed to create instrument, kyverno_breaker_total") } return &breaker{ name: name, drops: drops, total: total, open: open, } } func (b *breaker) Do(ctx context.Context, inner func(context.Context) error) error { attributes := sdkmetric.WithAttributes( attribute.String("circuit_name", b.name), ) if b.total != nil { b.total.Add(ctx, 1, attributes) } if b.open != nil && b.open(ctx) { if b.drops != nil { b.drops.Add(ctx, 1, attributes) } return nil } if inner == nil { return nil } return inner(ctx) }