2021-05-04 19:11:13 +00:00
package metrics
import (
2022-07-11 17:49:47 +00:00
"context"
"net/http"
"time"
2021-09-10 21:39:12 +00:00
2022-07-11 17:49:47 +00:00
"github.com/go-logr/logr"
2022-12-09 09:49:45 +00:00
"github.com/kyverno/kyverno/pkg/config"
2022-07-11 17:49:47 +00:00
kconfig "github.com/kyverno/kyverno/pkg/config"
2023-04-05 13:51:07 +00:00
tlsutils "github.com/kyverno/kyverno/pkg/utils/tls"
2022-12-06 15:41:00 +00:00
"github.com/kyverno/kyverno/pkg/version"
"github.com/prometheus/client_golang/prometheus/promhttp"
2022-07-25 09:25:26 +00:00
"go.opentelemetry.io/otel"
2022-07-11 17:49:47 +00:00
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/prometheus"
2022-12-06 15:41:00 +00:00
"go.opentelemetry.io/otel/metric"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
2022-07-11 17:49:47 +00:00
"go.opentelemetry.io/otel/sdk/resource"
2024-01-19 09:36:28 +00:00
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
2022-07-11 17:49:47 +00:00
"k8s.io/client-go/kubernetes"
2021-05-04 19:11:13 +00:00
)
2022-07-11 17:49:47 +00:00
const (
2022-11-29 12:16:15 +00:00
MeterName = "kyverno"
2022-07-11 17:49:47 +00:00
)
type MetricsConfig struct {
// instruments
2023-05-11 10:16:48 +00:00
policyChangesMetric metric . Int64Counter
clientQueriesMetric metric . Int64Counter
2022-07-11 17:49:47 +00:00
// config
2022-11-30 13:37:53 +00:00
config kconfig . MetricsConfiguration
2022-07-11 17:49:47 +00:00
Log logr . Logger
2021-05-04 19:11:13 +00:00
}
2022-08-31 06:03:47 +00:00
type MetricsConfigManager interface {
2022-11-30 13:37:53 +00:00
Config ( ) kconfig . MetricsConfiguration
2022-11-28 10:30:14 +00: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 06:03:47 +00:00
}
2022-11-30 13:37:53 +00:00
func ( m * MetricsConfig ) Config ( ) kconfig . MetricsConfiguration {
return m . config
}
2022-12-06 15:41:00 +00:00
func ( m * MetricsConfig ) initializeMetrics ( meterProvider metric . MeterProvider ) error {
2022-07-11 17:49:47 +00:00
var err error
2022-12-06 15:41:00 +00:00
meter := meterProvider . Meter ( MeterName )
2023-05-11 10:16:48 +00: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 17:49:47 +00:00
if err != nil {
2022-12-06 15:41:00 +00:00
m . Log . Error ( err , "Failed to create instrument, kyverno_policy_changes" )
2022-11-25 13:14:55 +00:00
return err
2022-07-11 17:49:47 +00:00
}
2023-05-11 10:16:48 +00: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 17:49:47 +00:00
if err != nil {
2022-12-06 15:41:00 +00:00
m . Log . Error ( err , "Failed to create instrument, kyverno_client_queries" )
2022-11-25 13:14:55 +00:00
return err
2022-08-31 06:03:47 +00:00
}
2022-11-25 13:14:55 +00:00
return nil
2021-05-04 19:11:13 +00:00
}
2022-12-06 15:41:00 +00:00
func ShutDownController ( ctx context . Context , pusher * sdkmetric . MeterProvider ) {
2022-11-17 05:29:32 +00:00
if pusher != nil {
// pushes any last exports to the receiver
2022-12-06 15:41:00 +00:00
if err := pusher . Shutdown ( ctx ) ; err != nil {
2022-11-17 05:29:32 +00:00
otel . Handle ( err )
}
2022-07-25 09:25:26 +00:00
}
}
2023-10-12 09:50:27 +00: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 09:58:07 +00:00
}
}
}
2023-10-12 09:50:27 +00: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 17:49:47 +00:00
if certs != "" {
// here the certificates are stored as configmaps
2023-04-05 13:51:07 +00:00
transportCreds , err := tlsutils . FetchCert ( ctx , certs , kubeClient )
2022-07-11 17:49:47 +00:00
if err != nil {
log . Error ( err , "Error fetching certificate from secret" )
2022-11-17 05:29:32 +00:00
return nil , err
2022-07-11 17:49:47 +00:00
}
2022-12-06 15:41:00 +00:00
options = append ( options , otlpmetricgrpc . WithTLSCredentials ( transportCreds ) )
2022-07-11 17:49:47 +00:00
} else {
2022-12-06 15:41:00 +00:00
options = append ( options , otlpmetricgrpc . WithInsecure ( ) )
2021-05-04 19:11:13 +00:00
}
2022-12-06 15:41:00 +00:00
// create new exporter for exporting metrics
exporter , err := otlpmetricgrpc . New ( ctx , options ... )
2022-07-11 17:49:47 +00:00
if err != nil {
log . Error ( err , "Failed to create the collector exporter" )
2022-11-17 05:29:32 +00:00
return nil , err
2021-05-04 19:11:13 +00:00
}
2022-12-06 15:41:00 +00:00
res , err := resource . Merge (
resource . Default ( ) ,
resource . NewWithAttributes (
semconv . SchemaURL ,
2022-12-09 09:49:45 +00:00
semconv . ServiceNameKey . String ( MeterName ) ,
2023-06-30 23:27:06 +00:00
semconv . ServiceVersionKey . String ( version . Version ( ) ) ,
2022-12-06 15:41:00 +00:00
) ,
2021-05-04 19:11:13 +00:00
)
2022-07-11 17:49:47 +00:00
if err != nil {
log . Error ( err , "failed creating resource" )
2022-11-17 05:29:32 +00:00
return nil , err
2022-07-11 17:49:47 +00:00
}
2022-12-06 15:41:00 +00:00
reader := sdkmetric . NewPeriodicReader (
exporter ,
sdkmetric . WithInterval ( 2 * time . Second ) ,
)
2022-07-11 17:49:47 +00:00
// create controller and bind the exporter with it
2022-12-06 15:41:00 +00:00
provider := sdkmetric . NewMeterProvider (
sdkmetric . WithReader ( reader ) ,
sdkmetric . WithResource ( res ) ,
2023-10-12 09:50:27 +00:00
sdkmetric . WithView ( configuration . BuildMeterProviderViews ( ) ... ) ,
2021-05-04 19:11:13 +00:00
)
2022-12-06 15:41:00 +00:00
return provider , nil
2022-07-11 17:49:47 +00:00
}
2023-10-12 09:50:27 +00:00
func NewPrometheusConfig ( ctx context . Context , log logr . Logger , configuration kconfig . MetricsConfiguration ) ( metric . MeterProvider , * http . ServeMux , error ) {
2022-12-06 15:41:00 +00:00
res , err := resource . Merge (
resource . Default ( ) ,
resource . NewWithAttributes (
semconv . SchemaURL ,
semconv . ServiceNameKey . String ( "kyverno-svc-metrics" ) ,
semconv . ServiceNamespaceKey . String ( kconfig . KyvernoNamespace ( ) ) ,
2023-06-30 23:27:06 +00:00
semconv . ServiceVersionKey . String ( version . Version ( ) ) ,
2022-12-06 15:41:00 +00:00
) ,
2021-05-04 19:11:13 +00:00
)
2022-07-11 17:49:47 +00:00
if err != nil {
log . Error ( err , "failed creating resource" )
2022-12-06 15:41:00 +00:00
return nil , nil , err
2022-07-11 17:49:47 +00:00
}
2022-12-06 15:41:00 +00:00
exporter , err := prometheus . New (
prometheus . WithoutUnits ( ) ,
prometheus . WithoutTargetInfo ( ) ,
2023-10-12 09:50:27 +00:00
prometheus . WithAggregationSelector ( aggregationSelector ( configuration ) ) ,
2022-07-11 17:49:47 +00:00
)
if err != nil {
log . Error ( err , "failed to initialize prometheus exporter" )
2022-12-06 15:41:00 +00:00
return nil , nil , err
2022-07-11 17:49:47 +00:00
}
2022-12-06 15:41:00 +00:00
provider := sdkmetric . NewMeterProvider (
sdkmetric . WithReader ( exporter ) ,
sdkmetric . WithResource ( res ) ,
2023-10-12 09:50:27 +00:00
sdkmetric . WithView ( configuration . BuildMeterProviderViews ( ) ... ) ,
2022-12-06 15:41:00 +00:00
)
2022-07-11 17:49:47 +00:00
metricsServerMux := http . NewServeMux ( )
2022-12-09 09:49:45 +00:00
metricsServerMux . Handle ( config . MetricsPath , promhttp . Handler ( ) )
2022-12-06 15:41:00 +00:00
return provider , metricsServerMux , nil
2022-07-11 17:49:47 +00:00
}
2022-11-28 10:30:14 +00:00
func ( m * MetricsConfig ) RecordPolicyChanges ( ctx context . Context , policyValidationMode PolicyValidationMode , policyType PolicyType , policyBackgroundMode PolicyBackgroundMode , policyNamespace string , policyName string , policyChangeType string ) {
2022-07-11 17:49:47 +00:00
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 10:16:48 +00:00
m . policyChangesMetric . Add ( ctx , 1 , metric . WithAttributes ( commonLabels ... ) )
2022-07-11 17:49:47 +00:00
}
2022-11-28 10:30:14 +00:00
func ( m * MetricsConfig ) RecordClientQueries ( ctx context . Context , clientQueryOperation ClientQueryOperation , clientType ClientType , resourceKind string , resourceNamespace string ) {
2022-08-31 06:03:47 +00: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 10:16:48 +00:00
m . clientQueriesMetric . Add ( ctx , 1 , metric . WithAttributes ( commonLabels ... ) )
2022-08-31 06:03:47 +00:00
}