1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-04-15 00:36:28 +00:00

refactor: metrics configuration code (#5475)

* refactor: metrics configuration

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2022-11-25 14:14:55 +01:00 committed by GitHub
parent 4984c5c878
commit e03f48128a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 117 additions and 115 deletions

View file

@ -41,17 +41,14 @@ const (
func setupMetrics(logger logr.Logger, kubeClient kubernetes.Interface) (*metrics.MetricsConfig, context.CancelFunc, error) {
logger = logger.WithName("metrics")
logger.Info("setup metrics...", "otel", otel, "port", metricsPort, "collector", otelCollector, "creds", transportCreds)
metricsConfigData, err := config.NewMetricsConfigData(kubeClient)
if err != nil {
return nil, nil, err
}
metricsConfiguration := internal.GetMetricsConfiguration(logger, kubeClient)
metricsAddr := ":" + metricsPort
metricsConfig, metricsServerMux, metricsPusher, err := metrics.InitMetrics(
disableMetricsExport,
otel,
metricsAddr,
otelCollector,
metricsConfigData,
metricsConfiguration,
transportCreds,
kubeClient,
logging.WithName("metrics"),

15
cmd/internal/metrics.go Normal file
View file

@ -0,0 +1,15 @@
package internal
import (
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/config"
"k8s.io/client-go/kubernetes"
)
func GetMetricsConfiguration(logger logr.Logger, client kubernetes.Interface) config.MetricsConfiguration {
logger = logger.WithName("metrics")
logger.Info("load metrics configuration...")
metricsConfiguration, err := config.NewMetricsConfiguration(client)
checkError(logger, err, "failed to load metrics configuration")
return metricsConfiguration
}

View file

@ -120,17 +120,14 @@ func parseFlags(config internal.Configuration) {
func setupMetrics(logger logr.Logger, kubeClient kubernetes.Interface) (*metrics.MetricsConfig, context.CancelFunc, error) {
logger = logger.WithName("metrics")
logger.Info("setup metrics...", "otel", otel, "port", metricsPort, "collector", otelCollector, "creds", transportCreds)
metricsConfigData, err := config.NewMetricsConfigData(kubeClient)
if err != nil {
return nil, nil, err
}
metricsConfiguration := internal.GetMetricsConfiguration(logger, kubeClient)
metricsAddr := ":" + metricsPort
metricsConfig, metricsServerMux, metricsPusher, err := metrics.InitMetrics(
disableMetricsExport,
otel,
metricsAddr,
otelCollector,
metricsConfigData,
metricsConfiguration,
transportCreds,
kubeClient,
logging.WithName("metrics"),

View file

@ -133,7 +133,7 @@ type configuration struct {
generateSuccessEvents bool
}
// NewConfiguration ...
// NewDefaultConfiguration ...
func NewDefaultConfiguration() *configuration {
return &configuration{
excludeGroupRole: defaultExcludeGroupRole,

View file

@ -1,21 +0,0 @@
package config
import "k8s.io/client-go/kubernetes"
func NewFakeConfig() Configuration {
return &configuration{}
}
func NewFakeMetricsConfig(client kubernetes.Interface) *MetricsConfigData {
return &MetricsConfigData{
client: client,
cmName: "",
metricsConfig: MetricsConfig{
metricsRefreshInterval: 0,
namespaces: namespacesConfig{
IncludeNamespaces: []string{},
ExcludeNamespaces: []string{},
},
},
}
}

View file

@ -3,10 +3,11 @@ package config
import (
"context"
"encoding/json"
"fmt"
"os"
"time"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
@ -15,17 +16,16 @@ import (
// that stores the information associated with Kyverno's metrics exposure
const metricsConfigEnvVar string = "METRICS_CONFIG"
// MetricsConfigData stores the metrics-related configuration
type MetricsConfigData struct {
client kubernetes.Interface
cmName string
metricsConfig MetricsConfig
}
// MetricsConfig stores the config for metrics
type MetricsConfig struct {
namespaces namespacesConfig
metricsRefreshInterval time.Duration
type MetricsConfiguration interface {
// GetExcludeNamespaces returns the namespaces to ignore for metrics exposure
GetExcludeNamespaces() []string
// GetIncludeNamespaces returns the namespaces to specifically consider for metrics exposure
GetIncludeNamespaces() []string
// GetMetricsRefreshInterval returns the refresh interval for the metrics
GetMetricsRefreshInterval() time.Duration
// namespaces namespacesConfig
// metricsRefreshInterval time.Duration
}
type namespacesConfig struct {
@ -33,74 +33,89 @@ type namespacesConfig struct {
ExcludeNamespaces []string `json:"exclude,omitempty"`
}
// metricsConfig stores the config for metrics
type metricsConfig struct {
namespaces namespacesConfig
metricsRefreshInterval time.Duration
}
// GetExcludeNamespaces returns the namespaces to ignore for metrics exposure
func (mcd *MetricsConfigData) GetExcludeNamespaces() []string {
return mcd.metricsConfig.namespaces.ExcludeNamespaces
func (mcd *metricsConfig) GetExcludeNamespaces() []string {
return mcd.namespaces.ExcludeNamespaces
}
// GetIncludeNamespaces returns the namespaces to specifically consider for metrics exposure
func (mcd *MetricsConfigData) GetIncludeNamespaces() []string {
return mcd.metricsConfig.namespaces.IncludeNamespaces
func (mcd *metricsConfig) GetIncludeNamespaces() []string {
return mcd.namespaces.IncludeNamespaces
}
// GetMetricsRefreshInterval returns the refresh interval for the metrics
func (mcd *MetricsConfigData) GetMetricsRefreshInterval() time.Duration {
return mcd.metricsConfig.metricsRefreshInterval
func (mcd *metricsConfig) GetMetricsRefreshInterval() time.Duration {
return mcd.metricsRefreshInterval
}
// GetMetricsConfigMapName returns the configmap name for the metric
func (mcd *MetricsConfigData) GetMetricsConfigMapName() string {
return mcd.cmName
func (cd *metricsConfig) load(cm *corev1.ConfigMap) {
logger := logger.WithValues("name", cm.Name, "namespace", cm.Namespace)
if cm.Data == nil {
return
}
// reset
cd.metricsRefreshInterval = 0
cd.namespaces = namespacesConfig{
IncludeNamespaces: []string{},
ExcludeNamespaces: []string{},
}
// load metricsRefreshInterval
metricsRefreshInterval, found := cm.Data["metricsRefreshInterval"]
if found {
metricsRefreshInterval, err := time.ParseDuration(metricsRefreshInterval)
if err != nil {
logger.Error(err, "failed to parse metricsRefreshInterval")
} else {
cd.metricsRefreshInterval = metricsRefreshInterval
}
}
// load namespaces
namespaces, ok := cm.Data["namespaces"]
if ok {
namespaces, err := parseIncludeExcludeNamespacesFromNamespacesConfig(namespaces)
if err != nil {
logger.Error(err, "failed to parse namespaces")
} else {
cd.namespaces = namespaces
}
}
}
// NewMetricsConfigData ...
func NewMetricsConfigData(rclient kubernetes.Interface) (*MetricsConfigData, error) {
cmName := os.Getenv(metricsConfigEnvVar)
mcd := MetricsConfigData{
client: rclient,
cmName: cmName,
metricsConfig: MetricsConfig{
metricsRefreshInterval: 0,
namespaces: namespacesConfig{
IncludeNamespaces: []string{},
ExcludeNamespaces: []string{},
},
// NewDefaultMetricsConfiguration ...
func NewDefaultMetricsConfiguration() *metricsConfig {
return &metricsConfig{
metricsRefreshInterval: 0,
namespaces: namespacesConfig{
IncludeNamespaces: []string{},
ExcludeNamespaces: []string{},
},
}
}
// NewMetricsConfiguration ...
func NewMetricsConfiguration(client kubernetes.Interface) (MetricsConfiguration, error) {
configuration := NewDefaultMetricsConfiguration()
cmName := os.Getenv(metricsConfigEnvVar)
if cmName != "" {
kyvernoNamespace := kyvernoNamespace
configMap, err := rclient.CoreV1().ConfigMaps(kyvernoNamespace).Get(context.TODO(), mcd.cmName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("error occurred while fetching the metrics configmap at %s/%s: %w", kyvernoNamespace, mcd.cmName, err)
}
namespacesConfigStr, found := configMap.Data["namespaces"]
if found {
mcd.metricsConfig.namespaces.IncludeNamespaces, mcd.metricsConfig.namespaces.ExcludeNamespaces, err = parseIncludeExcludeNamespacesFromNamespacesConfig(namespacesConfigStr)
if err != nil {
return nil, fmt.Errorf("error occurred while parsing the 'namespaces' field of metrics config map: %w", err)
if cm, err := client.CoreV1().ConfigMaps(kyvernoNamespace).Get(context.TODO(), cmName, metav1.GetOptions{}); err != nil {
if !errors.IsNotFound(err) {
return nil, err
}
} else {
configuration.load(cm)
}
metricsRefreshInterval, found := configMap.Data["metricsRefreshInterval"]
if found {
mcd.metricsConfig.metricsRefreshInterval, err = time.ParseDuration(metricsRefreshInterval)
if err != nil {
return nil, fmt.Errorf("error occurred while parsing metricsRefreshInterval: %w", err)
}
}
} else {
logger.Info("ConfigMap name not defined in env:METRICS_CONFIG: loading default configuration")
}
return &mcd, nil
return configuration, nil
}
func parseIncludeExcludeNamespacesFromNamespacesConfig(jsonStr string) ([]string, []string, error) {
var namespacesConfigObject *namespacesConfig
func parseIncludeExcludeNamespacesFromNamespacesConfig(jsonStr string) (namespacesConfig, error) {
var namespacesConfigObject namespacesConfig
err := json.Unmarshal([]byte(jsonStr), &namespacesConfigObject)
if err != nil {
return nil, nil, err
}
return namespacesConfigObject.IncludeNamespaces, namespacesConfigObject.ExcludeNamespaces, nil
return namespacesConfigObject, err
}

View file

@ -2,16 +2,14 @@ package metrics
import (
"github.com/kyverno/kyverno/pkg/config"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
)
func NewFakeMetricsConfig(client kubernetes.Interface) *MetricsConfig {
func NewFakeMetricsConfig() *MetricsConfig {
mc := &MetricsConfig{
Config: config.NewFakeMetricsConfig(client),
Config: config.NewDefaultMetricsConfiguration(),
Log: klog.NewKlogr(),
}
mc, _ = initializeMetrics(mc)
_ = mc.initializeMetrics()
return mc
}

View file

@ -14,7 +14,7 @@ func InitMetrics(
otel string,
metricsAddr string,
otelCollector string,
metricsConfigData *config.MetricsConfigData,
metricsConfiguration config.MetricsConfiguration,
transportCreds string,
kubeClient kubernetes.Interface,
log logr.Logger,
@ -23,11 +23,12 @@ func InitMetrics(
var metricsServerMux *http.ServeMux
var pusher *controller.Controller
metricsConfig := new(MetricsConfig)
metricsConfig.Log = log
metricsConfig.Config = metricsConfigData
metricsConfig := MetricsConfig{
Log: log,
Config: metricsConfiguration,
}
metricsConfig, err = initializeMetrics(metricsConfig)
err = metricsConfig.initializeMetrics()
if err != nil {
log.Error(err, "Failed initializing metrics")
return nil, nil, nil, err
@ -57,5 +58,5 @@ func InitMetrics(
}
}
}
return metricsConfig, metricsServerMux, pusher, nil
return &metricsConfig, metricsServerMux, pusher, nil
}

View file

@ -42,7 +42,7 @@ type MetricsConfig struct {
clientQueriesMetric syncint64.Counter
// config
Config *kconfig.MetricsConfigData
Config kconfig.MetricsConfiguration
Log logr.Logger
}
@ -56,54 +56,54 @@ type MetricsConfigManager interface {
RecordClientQueries(clientQueryOperation ClientQueryOperation, clientType ClientType, resourceKind string, resourceNamespace string)
}
func initializeMetrics(m *MetricsConfig) (*MetricsConfig, error) {
func (m *MetricsConfig) initializeMetrics() error {
var err error
meter := global.MeterProvider().Meter(meterName)
m.policyResultsMetric, err = meter.SyncInt64().Counter("kyverno_policy_results_total", instrument.WithDescription("can be used to track the results associated with the policies applied in the users cluster, at the level from rule to policy to admission requests"))
if err != nil {
m.Log.Error(err, "Failed to create instrument, kyverno_policy_results_total")
return nil, err
return err
}
m.policyChangesMetric, err = meter.SyncInt64().Counter("kyverno_policy_changes_total", instrument.WithDescription("can be used to track all the changes associated with the Kyverno policies present on the cluster such as creation, updates and deletions"))
if err != nil {
m.Log.Error(err, "Failed to create instrument, kyverno_policy_changes_total")
return nil, err
return err
}
m.admissionRequestsMetric, err = meter.SyncInt64().Counter("kyverno_admission_requests_total", instrument.WithDescription("can be used to track the number of admission requests encountered by Kyverno in the cluster"))
if err != nil {
m.Log.Error(err, "Failed to create instrument, kyverno_admission_requests_total")
return nil, err
return err
}
m.policyExecutionDurationMetric, err = meter.SyncFloat64().Histogram("kyverno_policy_execution_duration_seconds", instrument.WithDescription("can be used to track the latencies (in seconds) associated with the execution/processing of the individual rules under Kyverno policies whenever they evaluate incoming resource requests"))
if err != nil {
m.Log.Error(err, "Failed to create instrument, kyverno_policy_execution_duration_seconds")
return nil, err
return err
}
m.admissionReviewDurationMetric, err = meter.SyncFloat64().Histogram("kyverno_admission_review_duration_seconds", instrument.WithDescription("can be used to track the latencies (in seconds) associated with the entire individual admission review. For example, if an incoming request trigger, say, five policies, this metric will track the e2e latency associated with the execution of all those policies"))
if err != nil {
m.Log.Error(err, "Failed to create instrument, kyverno_admission_review_duration_seconds")
return nil, err
return err
}
// Register Async Callbacks
m.policyRuleInfoMetric, err = meter.AsyncFloat64().Gauge("kyverno_policy_rule_info_total", instrument.WithDescription("can be used to track the info of the rules or/and policies present in the cluster. 0 means the rule doesn't exist and has been deleted, 1 means the rule is currently existent in the cluster"))
if err != nil {
m.Log.Error(err, "Failed to create instrument, kyverno_policy_rule_info_total")
return nil, err
return err
}
m.clientQueriesMetric, err = meter.SyncInt64().Counter("kyverno_client_queries_total", instrument.WithDescription("can be used to track the number of client queries sent from Kyverno to the API-server"))
if err != nil {
m.Log.Error(err, "Failed to create instrument, kyverno_client_queries_total")
return nil, err
return err
}
return m, nil
return nil
}
func ShutDownController(ctx context.Context, pusher *controller.Controller) {

View file

@ -20,7 +20,7 @@ import (
func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhooks.ResourceHandlers {
client := fake.NewSimpleClientset()
metricsConfig := metrics.NewFakeMetricsConfig(client)
metricsConfig := metrics.NewFakeMetricsConfig()
informers := informers.NewSharedInformerFactory(client, 0)
informers.Start(ctx.Done())
@ -30,7 +30,7 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook
kyvernoInformers.Start(ctx.Done())
dclient := dclient.NewEmptyFakeClient()
configuration := config.NewFakeConfig()
configuration := config.NewDefaultConfiguration()
rbLister := informers.Rbac().V1().RoleBindings().Lister()
crbLister := informers.Rbac().V1().ClusterRoleBindings().Lister()
urLister := kyvernoInformers.Kyverno().V1beta1().UpdateRequests().Lister().UpdateRequests(config.KyvernoNamespace())