1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-09 01:16:55 +00:00
kyverno/pkg/policyviolation/generator.go

269 lines
7.1 KiB
Go
Raw Normal View History

2019-11-12 14:41:29 -08:00
package policyviolation
import (
2019-11-26 18:07:15 -08:00
"errors"
2019-11-12 14:41:29 -08:00
"reflect"
"strconv"
"strings"
"sync"
2019-11-12 14:41:29 -08:00
2020-03-17 11:05:20 -07:00
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
kyvernov1 "github.com/kyverno/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1"
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
kyvernolister "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
"github.com/kyverno/kyverno/pkg/constant"
"github.com/kyverno/kyverno/pkg/policystatus"
2019-11-15 15:59:37 -08:00
dclient "github.com/kyverno/kyverno/pkg/dclient"
2019-11-12 14:41:29 -08:00
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
2019-11-15 15:59:37 -08:00
"k8s.io/client-go/tools/cache"
2019-11-12 14:41:29 -08:00
"k8s.io/client-go/util/workqueue"
)
const workQueueName = "policy-violation-controller"
const workQueueRetryLimit = 3
//Generator creates PV
type Generator struct {
2019-11-26 18:07:15 -08:00
dclient *dclient.Client
kyvernoInterface kyvernov1.KyvernoV1Interface
2019-11-15 15:59:37 -08:00
// get/list cluster policy violation
2019-11-26 18:07:15 -08:00
cpvLister kyvernolister.ClusterPolicyViolationLister
2019-11-15 15:59:37 -08:00
// get/ist namespaced policy violation
nspvLister kyvernolister.PolicyViolationLister
2019-11-15 15:59:37 -08:00
// returns true if the cluster policy store has been synced at least once
pvSynced cache.InformerSynced
// returns true if the namespaced cluster policy store has been synced at at least once
log logr.Logger
nspvSynced cache.InformerSynced
queue workqueue.RateLimitingInterface
dataStore *dataStore
policyStatusListener policystatus.Listener
}
2019-11-15 15:59:37 -08:00
//NewDataStore returns an instance of data store
func newDataStore() *dataStore {
ds := dataStore{
data: make(map[string]Info),
}
return &ds
}
type dataStore struct {
data map[string]Info
mu sync.RWMutex
}
func (ds *dataStore) add(keyHash string, info Info) {
ds.mu.Lock()
defer ds.mu.Unlock()
// queue the key hash
ds.data[keyHash] = info
}
func (ds *dataStore) lookup(keyHash string) Info {
ds.mu.RLock()
defer ds.mu.RUnlock()
return ds.data[keyHash]
}
func (ds *dataStore) delete(keyHash string) {
ds.mu.Lock()
defer ds.mu.Unlock()
delete(ds.data, keyHash)
2019-11-12 14:41:29 -08:00
}
//Info is a request to create PV
type Info struct {
PolicyName string
Resource unstructured.Unstructured
Rules []kyverno.ViolatedRule
2020-02-26 00:26:09 +05:30
FromSync bool
2019-11-12 14:41:29 -08:00
}
func (i Info) toKey() string {
keys := []string{
i.PolicyName,
i.Resource.GetKind(),
i.Resource.GetNamespace(),
i.Resource.GetName(),
strconv.Itoa(len(i.Rules)),
}
return strings.Join(keys, "/")
}
2019-11-12 14:41:29 -08:00
// make the struct hashable
//GeneratorInterface provides API to create PVs
type GeneratorInterface interface {
Add(infos ...Info)
}
// NewPVGenerator returns a new instance of policy violation generator
2019-11-26 18:07:15 -08:00
func NewPVGenerator(client *kyvernoclient.Clientset,
2020-01-07 11:33:18 -08:00
dclient *dclient.Client,
2019-11-15 15:59:37 -08:00
pvInformer kyvernoinformer.ClusterPolicyViolationInformer,
2020-03-17 11:05:20 -07:00
nspvInformer kyvernoinformer.PolicyViolationInformer,
policyStatus policystatus.Listener,
2020-03-17 11:05:20 -07:00
log logr.Logger) *Generator {
2019-11-12 14:41:29 -08:00
gen := Generator{
kyvernoInterface: client.KyvernoV1(),
dclient: dclient,
cpvLister: pvInformer.Lister(),
pvSynced: pvInformer.Informer().HasSynced,
nspvLister: nspvInformer.Lister(),
nspvSynced: nspvInformer.Informer().HasSynced,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), workQueueName),
dataStore: newDataStore(),
log: log,
policyStatusListener: policyStatus,
2019-11-12 14:41:29 -08:00
}
return &gen
}
func (gen *Generator) enqueue(info Info) {
// add to data map
keyHash := info.toKey()
// add to
// queue the key hash
gen.dataStore.add(keyHash, info)
gen.queue.Add(keyHash)
2019-11-12 14:41:29 -08:00
}
//Add queues a policy violation create request
func (gen *Generator) Add(infos ...Info) {
for _, info := range infos {
gen.enqueue(info)
}
}
// Run starts the workers
func (gen *Generator) Run(workers int, stopCh <-chan struct{}) {
2020-03-17 11:05:20 -07:00
logger := gen.log
2019-11-12 14:41:29 -08:00
defer utilruntime.HandleCrash()
2020-03-17 11:05:20 -07:00
logger.Info("start")
defer logger.Info("shutting down")
2019-11-12 14:41:29 -08:00
2019-11-15 15:59:37 -08:00
if !cache.WaitForCacheSync(stopCh, gen.pvSynced, gen.nspvSynced) {
2020-03-17 11:05:20 -07:00
logger.Info("failed to sync informer cache")
2019-11-15 15:59:37 -08:00
}
2019-11-12 14:41:29 -08:00
for i := 0; i < workers; i++ {
go wait.Until(gen.runWorker, constant.PolicyViolationControllerResync, stopCh)
2019-11-12 14:41:29 -08:00
}
<-stopCh
}
func (gen *Generator) runWorker() {
for gen.processNextWorkItem() {
2019-11-12 14:41:29 -08:00
}
}
func (gen *Generator) handleErr(err error, key interface{}) {
2020-03-17 11:05:20 -07:00
logger := gen.log
2019-11-12 14:41:29 -08:00
if err == nil {
gen.queue.Forget(key)
return
2019-11-12 14:41:29 -08:00
}
// retires requests if there is error
if gen.queue.NumRequeues(key) < workQueueRetryLimit {
2020-03-17 11:05:20 -07:00
logger.Error(err, "failed to sync policy violation", "key", key)
2019-11-12 14:41:29 -08:00
// Re-enqueue the key rate limited. Based on the rate limiter on the
// queue and the re-enqueue history, the key will be processed later again.
gen.queue.AddRateLimited(key)
return
}
gen.queue.Forget(key)
// remove from data store
if keyHash, ok := key.(string); ok {
gen.dataStore.delete(keyHash)
}
2020-03-17 11:05:20 -07:00
logger.Error(err, "dropping key out of the queue", "key", key)
2019-11-12 14:41:29 -08:00
}
func (gen *Generator) processNextWorkItem() bool {
2020-03-17 11:05:20 -07:00
logger := gen.log
2019-11-12 14:41:29 -08:00
obj, shutdown := gen.queue.Get()
if shutdown {
return false
}
err := func(obj interface{}) error {
defer gen.queue.Done(obj)
var keyHash string
2019-11-12 14:41:29 -08:00
var ok bool
if keyHash, ok = obj.(string); !ok {
2019-11-12 14:41:29 -08:00
gen.queue.Forget(obj)
2020-03-17 11:05:20 -07:00
logger.Info("incorrect type; expecting type 'string'", "obj", obj)
2019-11-12 14:41:29 -08:00
return nil
}
// lookup data store
info := gen.dataStore.lookup(keyHash)
if reflect.DeepEqual(info, Info{}) {
// empty key
gen.queue.Forget(obj)
2020-03-17 11:05:20 -07:00
logger.Info("empty key")
return nil
}
err := gen.syncHandler(info)
2019-11-12 14:41:29 -08:00
gen.handleErr(err, obj)
return nil
}(obj)
2019-11-12 14:41:29 -08:00
if err != nil {
2020-03-17 11:05:20 -07:00
logger.Error(err, "failed to process item")
2019-11-12 14:41:29 -08:00
return true
}
2019-11-12 14:41:29 -08:00
return true
}
func (gen *Generator) syncHandler(info Info) error {
2020-03-17 11:05:20 -07:00
logger := gen.log
2019-11-26 18:07:15 -08:00
var handler pvGenerator
builder := newPvBuilder()
2019-11-15 12:03:58 -08:00
if info.Resource.GetNamespace() == "" {
2019-11-26 18:07:15 -08:00
// cluster scope resource generate a clusterpolicy violation
handler = newClusterPV(gen.log.WithName("ClusterPV"), gen.dclient, gen.cpvLister, gen.kyvernoInterface, gen.policyStatusListener)
2019-11-26 18:07:15 -08:00
} else {
// namespaced resources generated a namespaced policy violation in the namespace of the resource
handler = newNamespacedPV(gen.log.WithName("NamespacedPV"), gen.dclient, gen.nspvLister, gen.kyvernoInterface, gen.policyStatusListener)
2019-11-26 18:07:15 -08:00
}
failure := false
pv := builder.generate(info)
2020-02-26 00:26:09 +05:30
if info.FromSync {
pv.Annotations = map[string]string{
"fromSync": "true",
}
2020-02-26 00:26:09 +05:30
}
// Create Policy Violations
2020-03-17 11:05:20 -07:00
logger.V(4).Info("creating policy violation", "key", info.toKey())
if err := handler.create(pv); err != nil {
failure = true
2020-03-17 11:05:20 -07:00
logger.Error(err, "failed to create policy violation")
2019-11-26 18:07:15 -08:00
}
2019-11-26 18:07:15 -08:00
if failure {
// even if there is a single failure we requeue the request
return errors.New("Failed to process some policy violations, re-queuing")
2019-11-12 14:41:29 -08:00
}
2019-11-26 18:07:15 -08:00
return nil
}
2019-11-12 14:41:29 -08:00
2019-11-26 18:07:15 -08:00
// Provides an interface to generate policy violations
// implementations for namespaced and cluster PV
type pvGenerator interface {
2019-12-12 16:35:37 -08:00
create(policyViolation kyverno.PolicyViolationTemplate) error
2019-11-12 14:41:29 -08:00
}