2021-05-05 00:41:13 +05:30
package metrics
import (
2022-07-11 23:19:47 +05:30
"context"
"net/http"
"time"
2021-09-11 03:09:12 +05:30
2022-07-11 23:19:47 +05:30
"github.com/go-logr/logr"
2022-12-09 10:49:45 +01:00
"github.com/kyverno/kyverno/pkg/config"
2022-07-11 23:19:47 +05:30
kconfig "github.com/kyverno/kyverno/pkg/config"
2023-04-05 15:51:07 +02:00
tlsutils "github.com/kyverno/kyverno/pkg/utils/tls"
2022-12-06 16:41:00 +01:00
"github.com/kyverno/kyverno/pkg/version"
"github.com/prometheus/client_golang/prometheus/promhttp"
2022-07-25 14:55:26 +05:30
"go.opentelemetry.io/otel"
2022-07-11 23:19:47 +05:30
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/prometheus"
2022-12-06 16:41:00 +01:00
"go.opentelemetry.io/otel/metric"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
2022-07-11 23:19:47 +05:30
"go.opentelemetry.io/otel/sdk/resource"
2024-01-19 10:36:28 +01:00
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
2022-07-11 23:19:47 +05:30
"k8s.io/client-go/kubernetes"
2021-05-05 00:41:13 +05:30
)
2022-07-11 23:19:47 +05:30
const (
2022-11-29 13:16:15 +01:00
MeterName = "kyverno"
2022-07-11 23:19:47 +05:30
)
type MetricsConfig struct {
// instruments
2023-05-11 12:16:48 +02:00
policyChangesMetric metric . Int64Counter
clientQueriesMetric metric . Int64Counter
2022-07-11 23:19:47 +05:30
// config
2022-11-30 14:37:53 +01:00
config kconfig . MetricsConfiguration
2022-07-11 23:19:47 +05:30
Log logr . Logger
2021-05-05 00:41:13 +05:30
}
2022-08-31 14:03:47 +08:00
type MetricsConfigManager interface {
2022-11-30 14:37:53 +01:00
Config ( ) kconfig . MetricsConfiguration
2022-11-28 11:30:14 +01:00
RecordPolicyChanges ( ctx context . Context , policyValidationMode PolicyValidationMode , policyType PolicyType , policyBackgroundMode PolicyBackgroundMode , policyNamespace string , policyName string , policyChangeType string )
RecordClientQueries ( ctx context . Context , clientQueryOperation ClientQueryOperation , clientType ClientType , resourceKind string , resourceNamespace string )
2022-08-31 14:03:47 +08:00
}
2022-11-30 14:37:53 +01:00
func ( m * MetricsConfig ) Config ( ) kconfig . MetricsConfiguration {
return m . config
}
2022-12-06 16:41:00 +01:00
func ( m * MetricsConfig ) initializeMetrics ( meterProvider metric . MeterProvider ) error {
2022-07-11 23:19:47 +05:30
var err error
2022-12-06 16:41:00 +01:00
meter := meterProvider . Meter ( MeterName )
2023-05-11 12:16:48 +02:00
m . policyChangesMetric , err = meter . Int64Counter ( "kyverno_policy_changes" , metric . WithDescription ( "can be used to track all the changes associated with the Kyverno policies present on the cluster such as creation, updates and deletions" ) )
2022-07-11 23:19:47 +05:30
if err != nil {
2022-12-06 16:41:00 +01:00
m . Log . Error ( err , "Failed to create instrument, kyverno_policy_changes" )
2022-11-25 14:14:55 +01:00
return err
2022-07-11 23:19:47 +05:30
}
2023-05-11 12:16:48 +02:00
m . clientQueriesMetric , err = meter . Int64Counter ( "kyverno_client_queries" , metric . WithDescription ( "can be used to track the number of client queries sent from Kyverno to the API-server" ) )
2022-07-11 23:19:47 +05:30
if err != nil {
2022-12-06 16:41:00 +01:00
m . Log . Error ( err , "Failed to create instrument, kyverno_client_queries" )
2022-11-25 14:14:55 +01:00
return err
2022-08-31 14:03:47 +08:00
}
2022-11-25 14:14:55 +01:00
return nil
2021-05-05 00:41:13 +05:30
}
2022-12-06 16:41:00 +01:00
func ShutDownController ( ctx context . Context , pusher * sdkmetric . MeterProvider ) {
2022-11-17 06:29:32 +01:00
if pusher != nil {
// pushes any last exports to the receiver
2022-12-06 16:41:00 +01:00
if err := pusher . Shutdown ( ctx ) ; err != nil {
2022-11-17 06:29:32 +01:00
otel . Handle ( err )
}
2022-07-25 14:55:26 +05:30
}
}
2023-10-12 11:50:27 +02:00
func aggregationSelector ( metricsConfiguration kconfig . MetricsConfiguration ) func ( ik sdkmetric . InstrumentKind ) sdkmetric . Aggregation {
return func ( ik sdkmetric . InstrumentKind ) sdkmetric . Aggregation {
switch ik {
case sdkmetric . InstrumentKindHistogram :
return sdkmetric . AggregationExplicitBucketHistogram {
Boundaries : metricsConfiguration . GetBucketBoundaries ( ) ,
NoMinMax : false ,
}
default :
return sdkmetric . DefaultAggregationSelector ( ik )
2023-04-05 11:58:07 +02:00
}
}
}
2023-10-12 11:50:27 +02:00
func NewOTLPGRPCConfig ( ctx context . Context , endpoint string , certs string , kubeClient kubernetes . Interface , log logr . Logger , configuration kconfig . MetricsConfiguration ) ( metric . MeterProvider , error ) {
options := [ ] otlpmetricgrpc . Option { otlpmetricgrpc . WithEndpoint ( endpoint ) , otlpmetricgrpc . WithAggregationSelector ( aggregationSelector ( configuration ) ) }
2022-07-11 23:19:47 +05:30
if certs != "" {
// here the certificates are stored as configmaps
2023-04-05 15:51:07 +02:00
transportCreds , err := tlsutils . FetchCert ( ctx , certs , kubeClient )
2022-07-11 23:19:47 +05:30
if err != nil {
log . Error ( err , "Error fetching certificate from secret" )
2022-11-17 06:29:32 +01:00
return nil , err
2022-07-11 23:19:47 +05:30
}
2022-12-06 16:41:00 +01:00
options = append ( options , otlpmetricgrpc . WithTLSCredentials ( transportCreds ) )
2022-07-11 23:19:47 +05:30
} else {
2022-12-06 16:41:00 +01:00
options = append ( options , otlpmetricgrpc . WithInsecure ( ) )
2021-05-05 00:41:13 +05:30
}
2022-12-06 16:41:00 +01:00
// create new exporter for exporting metrics
exporter , err := otlpmetricgrpc . New ( ctx , options ... )
2022-07-11 23:19:47 +05:30
if err != nil {
log . Error ( err , "Failed to create the collector exporter" )
2022-11-17 06:29:32 +01:00
return nil , err
2021-05-05 00:41:13 +05:30
}
2022-12-06 16:41:00 +01:00
res , err := resource . Merge (
resource . Default ( ) ,
resource . NewWithAttributes (
semconv . SchemaURL ,
2022-12-09 10:49:45 +01:00
semconv . ServiceNameKey . String ( MeterName ) ,
2023-07-01 01:27:06 +02:00
semconv . ServiceVersionKey . String ( version . Version ( ) ) ,
2022-12-06 16:41:00 +01:00
) ,
2021-05-05 00:41:13 +05:30
)
2022-07-11 23:19:47 +05:30
if err != nil {
log . Error ( err , "failed creating resource" )
2022-11-17 06:29:32 +01:00
return nil , err
2022-07-11 23:19:47 +05:30
}
2022-12-06 16:41:00 +01:00
reader := sdkmetric . NewPeriodicReader (
exporter ,
sdkmetric . WithInterval ( 2 * time . Second ) ,
)
2022-07-11 23:19:47 +05:30
// create controller and bind the exporter with it
2022-12-06 16:41:00 +01:00
provider := sdkmetric . NewMeterProvider (
sdkmetric . WithReader ( reader ) ,
sdkmetric . WithResource ( res ) ,
2023-10-12 11:50:27 +02:00
sdkmetric . WithView ( configuration . BuildMeterProviderViews ( ) ... ) ,
2021-05-05 00:41:13 +05:30
)
2022-12-06 16:41:00 +01:00
return provider , nil
2022-07-11 23:19:47 +05:30
}
2023-10-12 11:50:27 +02:00
func NewPrometheusConfig ( ctx context . Context , log logr . Logger , configuration kconfig . MetricsConfiguration ) ( metric . MeterProvider , * http . ServeMux , error ) {
2022-12-06 16:41:00 +01:00
res , err := resource . Merge (
resource . Default ( ) ,
resource . NewWithAttributes (
semconv . SchemaURL ,
semconv . ServiceNameKey . String ( "kyverno-svc-metrics" ) ,
semconv . ServiceNamespaceKey . String ( kconfig . KyvernoNamespace ( ) ) ,
2023-07-01 01:27:06 +02:00
semconv . ServiceVersionKey . String ( version . Version ( ) ) ,
2022-12-06 16:41:00 +01:00
) ,
2021-05-05 00:41:13 +05:30
)
2022-07-11 23:19:47 +05:30
if err != nil {
log . Error ( err , "failed creating resource" )
2022-12-06 16:41:00 +01:00
return nil , nil , err
2022-07-11 23:19:47 +05:30
}
2022-12-06 16:41:00 +01:00
exporter , err := prometheus . New (
prometheus . WithoutUnits ( ) ,
prometheus . WithoutTargetInfo ( ) ,
2023-10-12 11:50:27 +02:00
prometheus . WithAggregationSelector ( aggregationSelector ( configuration ) ) ,
2022-07-11 23:19:47 +05:30
)
if err != nil {
log . Error ( err , "failed to initialize prometheus exporter" )
2022-12-06 16:41:00 +01:00
return nil , nil , err
2022-07-11 23:19:47 +05:30
}
2022-12-06 16:41:00 +01:00
provider := sdkmetric . NewMeterProvider (
sdkmetric . WithReader ( exporter ) ,
sdkmetric . WithResource ( res ) ,
2023-10-12 11:50:27 +02:00
sdkmetric . WithView ( configuration . BuildMeterProviderViews ( ) ... ) ,
2022-12-06 16:41:00 +01:00
)
2022-07-11 23:19:47 +05:30
metricsServerMux := http . NewServeMux ( )
2022-12-09 10:49:45 +01:00
metricsServerMux . Handle ( config . MetricsPath , promhttp . Handler ( ) )
2022-12-06 16:41:00 +01:00
return provider , metricsServerMux , nil
2022-07-11 23:19:47 +05:30
}
2022-11-28 11:30:14 +01:00
func ( m * MetricsConfig ) RecordPolicyChanges ( ctx context . Context , policyValidationMode PolicyValidationMode , policyType PolicyType , policyBackgroundMode PolicyBackgroundMode , policyNamespace string , policyName string , policyChangeType string ) {
2022-07-11 23:19:47 +05:30
commonLabels := [ ] attribute . KeyValue {
attribute . String ( "policy_validation_mode" , string ( policyValidationMode ) ) ,
attribute . String ( "policy_type" , string ( policyType ) ) ,
attribute . String ( "policy_background_mode" , string ( policyBackgroundMode ) ) ,
attribute . String ( "policy_namespace" , policyNamespace ) ,
attribute . String ( "policy_name" , policyName ) ,
attribute . String ( "policy_change_type" , policyChangeType ) ,
}
2023-05-11 12:16:48 +02:00
m . policyChangesMetric . Add ( ctx , 1 , metric . WithAttributes ( commonLabels ... ) )
2022-07-11 23:19:47 +05:30
}
2022-11-28 11:30:14 +01:00
func ( m * MetricsConfig ) RecordClientQueries ( ctx context . Context , clientQueryOperation ClientQueryOperation , clientType ClientType , resourceKind string , resourceNamespace string ) {
2022-08-31 14:03:47 +08:00
commonLabels := [ ] attribute . KeyValue {
attribute . String ( "operation" , string ( clientQueryOperation ) ) ,
attribute . String ( "client_type" , string ( clientType ) ) ,
attribute . String ( "resource_kind" , resourceKind ) ,
attribute . String ( "resource_namespace" , resourceNamespace ) ,
}
2023-05-11 12:16:48 +02:00
m . clientQueriesMetric . Add ( ctx , 1 , metric . WithAttributes ( commonLabels ... ) )
2022-08-31 14:03:47 +08:00
}