2022-11-14 12:00:40 +01:00
package main
2022-11-14 18:30:12 +01:00
import (
"context"
"flag"
2022-11-16 18:41:33 +05:30
"net/http"
2022-11-14 18:30:12 +01:00
"os"
"time"
"github.com/go-logr/logr"
2022-11-18 15:21:15 +01:00
"github.com/kyverno/kyverno/cmd/internal"
2022-11-16 18:41:33 +05:30
"github.com/kyverno/kyverno/pkg/clients/dclient"
2022-11-22 14:37:27 +01:00
dynamicclient "github.com/kyverno/kyverno/pkg/clients/dynamic"
2022-11-21 18:16:25 +01:00
kubeclient "github.com/kyverno/kyverno/pkg/clients/kube"
2022-11-14 18:30:12 +01:00
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/logging"
2022-11-16 18:41:33 +05:30
"github.com/kyverno/kyverno/pkg/metrics"
2022-11-14 18:30:12 +01:00
corev1 "k8s.io/api/core/v1"
kubeinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
var (
kubeconfig string
clientRateLimitQPS float64
clientRateLimitBurst int
2022-11-16 18:41:33 +05:30
otel string
otelCollector string
metricsPort string
transportCreds string
disableMetricsExport bool
2022-11-14 18:30:12 +01:00
)
const (
2022-11-18 15:21:15 +01:00
resyncPeriod = 15 * time . Minute
2022-11-14 18:30:12 +01:00
)
2022-11-18 15:21:15 +01:00
func parseFlags ( config internal . Configuration ) {
internal . InitFlags ( config )
2022-11-14 18:30:12 +01:00
flag . StringVar ( & kubeconfig , "kubeconfig" , "" , "Path to a kubeconfig. Only required if out-of-cluster." )
flag . Float64Var ( & clientRateLimitQPS , "clientRateLimitQPS" , 20 , "Configure the maximum QPS to the Kubernetes API server from Kyverno. Uses the client default if zero." )
flag . IntVar ( & clientRateLimitBurst , "clientRateLimitBurst" , 50 , "Configure the maximum burst for throttle. Uses the client default if zero." )
2022-11-16 18:41:33 +05:30
flag . StringVar ( & otel , "otelConfig" , "prometheus" , "Set this flag to 'grpc', to enable exporting metrics to an Opentelemetry Collector. The default collector is set to \"prometheus\"" )
flag . StringVar ( & otelCollector , "otelCollector" , "opentelemetrycollector.kyverno.svc.cluster.local" , "Set this flag to the OpenTelemetry Collector Service Address. Kyverno will try to connect to this on the metrics port." )
flag . StringVar ( & transportCreds , "transportCreds" , "" , "Set this flag to the CA secret containing the certificate which is used by our Opentelemetry Metrics Client. If empty string is set, means an insecure connection will be used" )
flag . StringVar ( & metricsPort , "metricsPort" , "8000" , "Expose prometheus metrics at the given port, default to 8000." )
flag . BoolVar ( & disableMetricsExport , "disableMetrics" , false , "Set this flag to 'true' to disable metrics." )
2022-11-14 18:30:12 +01:00
flag . Parse ( )
}
2022-11-17 17:01:30 +01:00
func createKubeClients ( logger logr . Logger ) ( * rest . Config , kubernetes . Interface , error ) {
2022-11-14 18:30:12 +01:00
logger = logger . WithName ( "kube-clients" )
logger . Info ( "create kube clients..." , "kubeconfig" , kubeconfig , "qps" , clientRateLimitQPS , "burst" , clientRateLimitBurst )
clientConfig , err := config . CreateClientConfig ( kubeconfig , clientRateLimitQPS , clientRateLimitBurst )
if err != nil {
return nil , nil , err
}
kubeClient , err := kubernetes . NewForConfig ( clientConfig )
if err != nil {
return nil , nil , err
}
return clientConfig , kubeClient , nil
}
2022-11-17 17:01:30 +01:00
func createInstrumentedClients ( ctx context . Context , logger logr . Logger , clientConfig * rest . Config , metricsConfig * metrics . MetricsConfig ) ( kubernetes . Interface , dclient . Interface , error ) {
2022-11-16 18:41:33 +05:30
logger = logger . WithName ( "instrumented-clients" )
logger . Info ( "create instrumented clients..." , "kubeconfig" , kubeconfig , "qps" , clientRateLimitQPS , "burst" , clientRateLimitBurst )
2022-11-21 18:16:25 +01:00
kubeClient , err := kubeclient . NewForConfig (
clientConfig ,
kubeclient . WithMetrics ( metricsConfig , metrics . KubeClient ) ,
kubeclient . WithTracing ( ) ,
)
2022-11-16 18:41:33 +05:30
if err != nil {
return nil , nil , err
}
2022-11-22 14:37:27 +01:00
dynamicClient , err := dynamicclient . NewForConfig (
clientConfig ,
dynamicclient . WithMetrics ( metricsConfig , metrics . KubeClient ) ,
dynamicclient . WithTracing ( ) ,
)
if err != nil {
return nil , nil , err
}
dClient , err := dclient . NewClient ( ctx , dynamicClient , kubeClient , resyncPeriod )
2022-11-16 18:41:33 +05:30
if err != nil {
return nil , nil , err
}
2022-11-22 14:37:27 +01:00
return kubeClient , dClient , nil
2022-11-16 18:41:33 +05:30
}
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
}
metricsAddr := ":" + metricsPort
metricsConfig , metricsServerMux , metricsPusher , err := metrics . InitMetrics (
disableMetricsExport ,
otel ,
metricsAddr ,
otelCollector ,
metricsConfigData ,
transportCreds ,
kubeClient ,
logging . WithName ( "metrics" ) ,
)
if err != nil {
return nil , nil , err
}
var cancel context . CancelFunc
if otel == "grpc" {
cancel = func ( ) {
ctx , cancel := context . WithTimeout ( context . Background ( ) , 20 * time . Second )
defer cancel ( )
metrics . ShutDownController ( ctx , metricsPusher )
}
}
if otel == "prometheus" {
go func ( ) {
if err := http . ListenAndServe ( metricsAddr , metricsServerMux ) ; err != nil {
logger . Error ( err , "failed to enable metrics" , "address" , metricsAddr )
}
} ( )
}
return metricsConfig , cancel , nil
}
2022-11-14 12:00:40 +01:00
func main ( ) {
2022-11-18 15:21:15 +01:00
// config
appConfig := internal . NewConfiguration ( internal . WithProfiling ( ) , internal . WithTracing ( ) )
2022-11-14 18:30:12 +01:00
// parse flags
2022-11-18 15:21:15 +01:00
parseFlags ( appConfig )
2022-11-14 18:30:12 +01:00
// setup logger
2022-11-18 15:21:15 +01:00
logger := internal . SetupLogger ( )
// setup maxprocs
undo := internal . SetupMaxProcs ( logger )
defer undo ( )
// show version
internal . ShowVersion ( logger )
// start profiling
internal . SetupProfiling ( logger )
2022-11-14 18:30:12 +01:00
// create client config and kube clients
2022-11-17 17:01:30 +01:00
clientConfig , rawClient , err := createKubeClients ( logger )
2022-11-14 18:30:12 +01:00
if err != nil {
os . Exit ( 1 )
}
// setup signals
2022-11-23 08:28:19 +01:00
signalCtx , signalCancel := internal . SetupSignals ( logger )
2022-11-14 18:30:12 +01:00
defer signalCancel ( )
2022-11-17 17:01:30 +01:00
// setup metrics
metricsConfig , metricsShutdown , err := setupMetrics ( logger , rawClient )
2022-11-16 18:41:33 +05:30
if err != nil {
logger . Error ( err , "failed to setup metrics" )
os . Exit ( 1 )
}
if metricsShutdown != nil {
defer metricsShutdown ( )
}
2022-11-17 17:01:30 +01:00
// create instrumented clients
kubeClient , dynamicClient , err := createInstrumentedClients ( signalCtx , logger , clientConfig , metricsConfig )
2022-11-16 18:41:33 +05:30
if err != nil {
logger . Error ( err , "failed to create instrument clients" )
os . Exit ( 1 )
}
2022-11-17 17:01:30 +01:00
kubeKyvernoInformer := kubeinformers . NewSharedInformerFactoryWithOptions ( kubeClient , resyncPeriod , kubeinformers . WithNamespace ( config . KyvernoNamespace ( ) ) )
2022-11-16 18:41:33 +05:30
policyHandlers := NewHandlers (
dynamicClient ,
)
2022-11-14 18:30:12 +01:00
secretLister := kubeKyvernoInformer . Core ( ) . V1 ( ) . Secrets ( ) . Lister ( )
2022-11-16 18:41:33 +05:30
// start informers and wait for cache sync
// we need to call start again because we potentially registered new informers
2022-11-18 15:21:15 +01:00
if ! internal . StartInformersAndWaitForCacheSync ( signalCtx , kubeKyvernoInformer ) {
2022-11-16 18:41:33 +05:30
os . Exit ( 1 )
}
2022-11-14 18:30:12 +01:00
server := NewServer (
2022-11-16 18:41:33 +05:30
policyHandlers ,
2022-11-14 18:30:12 +01:00
func ( ) ( [ ] byte , [ ] byte , error ) {
secret , err := secretLister . Secrets ( config . KyvernoNamespace ( ) ) . Get ( "cleanup-controller-tls" )
if err != nil {
return nil , nil , err
}
return secret . Data [ corev1 . TLSCertKey ] , secret . Data [ corev1 . TLSPrivateKeyKey ] , nil
} ,
)
// start webhooks server
server . Run ( signalCtx . Done ( ) )
// wait for termination signal
<- signalCtx . Done ( )
2022-11-14 12:00:40 +01:00
}