2023-01-20 23:53:27 +08:00
package main
import (
"context"
"errors"
"flag"
"os"
"strings"
"sync"
"time"
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/cmd/internal"
"github.com/kyverno/kyverno/pkg/background"
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions"
"github.com/kyverno/kyverno/pkg/clients/dclient"
dynamicclient "github.com/kyverno/kyverno/pkg/clients/dynamic"
kubeclient "github.com/kyverno/kyverno/pkg/clients/kube"
kyvernoclient "github.com/kyverno/kyverno/pkg/clients/kyverno"
"github.com/kyverno/kyverno/pkg/config"
policymetricscontroller "github.com/kyverno/kyverno/pkg/controllers/metrics/policy"
"github.com/kyverno/kyverno/pkg/cosign"
2023-01-31 15:30:40 +01:00
"github.com/kyverno/kyverno/pkg/engine"
2023-01-31 08:46:38 +01:00
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
2023-01-20 23:53:27 +08:00
"github.com/kyverno/kyverno/pkg/engine/context/resolvers"
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/leaderelection"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/metrics"
"github.com/kyverno/kyverno/pkg/policy"
"github.com/kyverno/kyverno/pkg/registryclient"
kubeinformers "k8s.io/client-go/informers"
corev1listers "k8s.io/client-go/listers/core/v1"
kyamlopenapi "sigs.k8s.io/kustomize/kyaml/openapi"
)
const (
resyncPeriod = 15 * time . Minute
)
func setupRegistryClient ( ctx context . Context , logger logr . Logger , lister corev1listers . SecretNamespaceLister , imagePullSecrets string , allowInsecureRegistry bool ) ( registryclient . Client , error ) {
logger = logger . WithName ( "registry-client" )
logger . Info ( "setup registry client..." , "secrets" , imagePullSecrets , "insecure" , allowInsecureRegistry )
registryOptions := [ ] registryclient . Option {
registryclient . WithTracing ( ) ,
}
secrets := strings . Split ( imagePullSecrets , "," )
if imagePullSecrets != "" && len ( secrets ) > 0 {
registryOptions = append ( registryOptions , registryclient . WithKeychainPullSecrets ( ctx , lister , secrets ... ) )
}
if allowInsecureRegistry {
registryOptions = append ( registryOptions , registryclient . WithAllowInsecureRegistry ( ) )
}
return registryclient . New ( registryOptions ... )
}
func setupCosign ( logger logr . Logger , imageSignatureRepository string ) {
logger = logger . WithName ( "cosign" )
logger . Info ( "setup cosign..." , "repository" , imageSignatureRepository )
if imageSignatureRepository != "" {
cosign . ImageSignatureRepository = imageSignatureRepository
}
}
func createrLeaderControllers (
2023-02-03 06:01:11 +01:00
eng engineapi . Engine ,
2023-02-07 21:44:51 +08:00
genWorkers int ,
2023-01-20 23:53:27 +08:00
kubeInformer kubeinformers . SharedInformerFactory ,
kyvernoInformer kyvernoinformer . SharedInformerFactory ,
kyvernoClient versioned . Interface ,
dynamicClient dclient . Interface ,
rclient registryclient . Client ,
configuration config . Configuration ,
metricsConfig metrics . MetricsConfigManager ,
eventGenerator event . Interface ,
2023-01-31 08:46:38 +01:00
configMapResolver engineapi . ConfigmapResolver ,
2023-01-20 23:53:27 +08:00
) ( [ ] internal . Controller , error ) {
policyCtrl , err := policy . NewPolicyController (
kyvernoClient ,
dynamicClient ,
2023-02-03 06:01:11 +01:00
eng ,
2023-01-20 23:53:27 +08:00
kyvernoInformer . Kyverno ( ) . V1 ( ) . ClusterPolicies ( ) ,
kyvernoInformer . Kyverno ( ) . V1 ( ) . Policies ( ) ,
kyvernoInformer . Kyverno ( ) . V1beta1 ( ) . UpdateRequests ( ) ,
configuration ,
eventGenerator ,
kubeInformer . Core ( ) . V1 ( ) . Namespaces ( ) ,
configMapResolver ,
logging . WithName ( "PolicyController" ) ,
time . Hour ,
metricsConfig ,
)
if err != nil {
return nil , err
}
2023-02-07 21:44:51 +08:00
backgroundController := background . NewController (
kyvernoClient ,
dynamicClient ,
eng ,
kyvernoInformer . Kyverno ( ) . V1 ( ) . ClusterPolicies ( ) ,
kyvernoInformer . Kyverno ( ) . V1 ( ) . Policies ( ) ,
kyvernoInformer . Kyverno ( ) . V1beta1 ( ) . UpdateRequests ( ) ,
kubeInformer . Core ( ) . V1 ( ) . Namespaces ( ) ,
eventGenerator ,
configuration ,
configMapResolver ,
)
2023-01-20 23:53:27 +08:00
return [ ] internal . Controller {
internal . NewController ( "policy-controller" , policyCtrl , 2 ) ,
2023-02-07 21:44:51 +08:00
internal . NewController ( "background-controller" , backgroundController , genWorkers ) ,
2023-01-20 23:53:27 +08:00
} , err
}
func main ( ) {
var (
genWorkers int
maxQueuedEvents int
imagePullSecrets string
imageSignatureRepository string
allowInsecureRegistry bool
leaderElectionRetryPeriod time . Duration
)
flagset := flag . NewFlagSet ( "updaterequest-controller" , flag . ExitOnError )
2023-02-07 21:44:51 +08:00
flagset . IntVar ( & genWorkers , "genWorkers" , 10 , "Workers for the background controller." )
2023-01-20 23:53:27 +08:00
flagset . StringVar ( & imagePullSecrets , "imagePullSecrets" , "" , "Secret resource names for image registry access credentials." )
flagset . StringVar ( & imageSignatureRepository , "imageSignatureRepository" , "" , "Alternate repository for image signatures. Can be overridden per rule via `verifyImages.Repository`." )
flagset . BoolVar ( & allowInsecureRegistry , "allowInsecureRegistry" , false , "Whether to allow insecure connections to registries. Don't use this for anything but testing." )
flagset . IntVar ( & maxQueuedEvents , "maxQueuedEvents" , 1000 , "Maximum events to be queued." )
flagset . DurationVar ( & leaderElectionRetryPeriod , "leaderElectionRetryPeriod" , leaderelection . DefaultRetryPeriod , "Configure leader election retry period." )
// config
appConfig := internal . NewConfiguration (
internal . WithProfiling ( ) ,
internal . WithMetrics ( ) ,
internal . WithTracing ( ) ,
internal . WithKubeconfig ( ) ,
internal . WithFlagSets ( flagset ) ,
)
// parse flags
internal . ParseFlags ( appConfig )
// setup logger
// show version
// start profiling
// setup signals
// setup maxprocs
// setup metrics
2023-02-07 21:44:51 +08:00
signalCtx , logger , metricsConfig , sdown := internal . Setup ( "kyverno-background-controller" )
2023-01-20 23:53:27 +08:00
defer sdown ( )
// create instrumented clients
kubeClient := internal . CreateKubernetesClient ( logger , kubeclient . WithMetrics ( metricsConfig , metrics . KubeClient ) , kubeclient . WithTracing ( ) )
leaderElectionClient := internal . CreateKubernetesClient ( logger , kubeclient . WithMetrics ( metricsConfig , metrics . KubeClient ) , kubeclient . WithTracing ( ) )
kyvernoClient := internal . CreateKyvernoClient ( logger , kyvernoclient . WithMetrics ( metricsConfig , metrics . KyvernoClient ) , kyvernoclient . WithTracing ( ) )
dynamicClient := internal . CreateDynamicClient ( logger , dynamicclient . WithMetrics ( metricsConfig , metrics . KyvernoClient ) , dynamicclient . WithTracing ( ) )
dClient , err := dclient . NewClient ( signalCtx , dynamicClient , kubeClient , 15 * time . Minute )
if err != nil {
logger . Error ( err , "failed to create dynamic client" )
os . Exit ( 1 )
}
// THIS IS AN UGLY FIX
// ELSE KYAML IS NOT THREAD SAFE
kyamlopenapi . Schema ( )
// informer factories
kubeKyvernoInformer := kubeinformers . NewSharedInformerFactoryWithOptions ( kubeClient , resyncPeriod , kubeinformers . WithNamespace ( config . KyvernoNamespace ( ) ) )
kyvernoInformer := kyvernoinformer . NewSharedInformerFactory ( kyvernoClient , resyncPeriod )
cacheInformer , err := resolvers . GetCacheInformerFactory ( kubeClient , resyncPeriod )
if err != nil {
logger . Error ( err , "failed to create cache informer factory" )
os . Exit ( 1 )
}
secretLister := kubeKyvernoInformer . Core ( ) . V1 ( ) . Secrets ( ) . Lister ( ) . Secrets ( config . KyvernoNamespace ( ) )
// setup registry client
rclient , err := setupRegistryClient ( signalCtx , logger , secretLister , imagePullSecrets , allowInsecureRegistry )
if err != nil {
logger . Error ( err , "failed to setup registry client" )
os . Exit ( 1 )
}
// setup cosign
setupCosign ( logger , imageSignatureRepository )
informerBasedResolver , err := resolvers . NewInformerBasedResolver ( cacheInformer . Core ( ) . V1 ( ) . ConfigMaps ( ) . Lister ( ) )
if err != nil {
logger . Error ( err , "failed to create informer based resolver" )
os . Exit ( 1 )
}
clientBasedResolver , err := resolvers . NewClientBasedResolver ( kubeClient )
if err != nil {
logger . Error ( err , "failed to create client based resolver" )
os . Exit ( 1 )
}
2023-01-31 08:46:38 +01:00
configMapResolver , err := engineapi . NewNamespacedResourceResolver ( informerBasedResolver , clientBasedResolver )
2023-01-20 23:53:27 +08:00
if err != nil {
logger . Error ( err , "failed to create config map resolver" )
os . Exit ( 1 )
}
configuration , err := config . NewConfiguration ( kubeClient )
if err != nil {
logger . Error ( err , "failed to initialize configuration" )
os . Exit ( 1 )
}
eventGenerator := event . NewEventGenerator (
dClient ,
kyvernoInformer . Kyverno ( ) . V1 ( ) . ClusterPolicies ( ) ,
kyvernoInformer . Kyverno ( ) . V1 ( ) . Policies ( ) ,
maxQueuedEvents ,
logging . WithName ( "EventGenerator" ) ,
)
// this controller only subscribe to events, nothing is returned...
2023-03-17 11:48:48 +01:00
var wg sync . WaitGroup
2023-01-20 23:53:27 +08:00
policymetricscontroller . NewController (
metricsConfig ,
kyvernoInformer . Kyverno ( ) . V1 ( ) . ClusterPolicies ( ) ,
kyvernoInformer . Kyverno ( ) . V1 ( ) . Policies ( ) ,
2023-03-17 11:48:48 +01:00
& wg ,
2023-01-20 23:53:27 +08:00
)
2023-02-03 06:01:11 +01:00
engine := engine . NewEngine (
configuration ,
2023-02-07 16:09:15 +01:00
dClient ,
2023-02-08 06:55:03 +01:00
rclient ,
2023-02-08 14:19:56 +01:00
engineapi . DefaultContextLoaderFactory ( configMapResolver ) ,
2023-02-06 06:49:47 +01:00
// TODO: do we need exceptions here ?
nil ,
2023-02-03 06:01:11 +01:00
)
2023-01-20 23:53:27 +08:00
// start informers and wait for cache sync
2023-02-09 16:53:27 +01:00
if ! internal . StartInformersAndWaitForCacheSync ( signalCtx , logger , kyvernoInformer , kubeKyvernoInformer , cacheInformer ) {
2023-01-20 23:53:27 +08:00
logger . Error ( errors . New ( "failed to wait for cache sync" ) , "failed to wait for cache sync" )
os . Exit ( 1 )
}
// start event generator
2023-03-17 11:48:48 +01:00
go eventGenerator . Run ( signalCtx , 3 , & wg )
2023-01-20 23:53:27 +08:00
// setup leader election
le , err := leaderelection . New (
logger . WithName ( "leader-election" ) ,
2023-02-07 21:44:51 +08:00
"kyverno-background-controller" ,
2023-01-20 23:53:27 +08:00
config . KyvernoNamespace ( ) ,
leaderElectionClient ,
config . KyvernoPodName ( ) ,
leaderElectionRetryPeriod ,
func ( ctx context . Context ) {
logger := logger . WithName ( "leader" )
// create leader factories
kubeInformer := kubeinformers . NewSharedInformerFactory ( kubeClient , resyncPeriod )
kyvernoInformer := kyvernoinformer . NewSharedInformerFactory ( kyvernoClient , resyncPeriod )
// create leader controllers
leaderControllers , err := createrLeaderControllers (
2023-02-03 06:01:11 +01:00
engine ,
2023-02-07 21:44:51 +08:00
genWorkers ,
2023-01-20 23:53:27 +08:00
kubeInformer ,
kyvernoInformer ,
kyvernoClient ,
dClient ,
rclient ,
configuration ,
metricsConfig ,
eventGenerator ,
configMapResolver ,
)
if err != nil {
logger . Error ( err , "failed to create leader controllers" )
os . Exit ( 1 )
}
// start informers and wait for cache sync
2023-02-09 16:53:27 +01:00
if ! internal . StartInformersAndWaitForCacheSync ( signalCtx , logger , kyvernoInformer , kubeInformer ) {
2023-01-20 23:53:27 +08:00
logger . Error ( errors . New ( "failed to wait for cache sync" ) , "failed to wait for cache sync" )
os . Exit ( 1 )
}
// start leader controllers
var wg sync . WaitGroup
for _ , controller := range leaderControllers {
controller . Run ( signalCtx , logger . WithName ( "controllers" ) , & wg )
}
// wait all controllers shut down
wg . Wait ( )
} ,
nil ,
)
if err != nil {
logger . Error ( err , "failed to initialize leader election" )
os . Exit ( 1 )
}
// start leader election
2023-02-07 21:44:51 +08:00
for {
2023-01-20 23:53:27 +08:00
select {
case <- signalCtx . Done ( ) :
2023-03-17 11:48:48 +01:00
wg . Wait ( )
2023-01-20 23:53:27 +08:00
return
default :
le . Run ( signalCtx )
}
2023-02-07 21:44:51 +08:00
}
2023-01-20 23:53:27 +08:00
}