1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00
kyverno/pkg/config/dynamicconfig.go
Shivkumar Dudhani 8c1d79ab28
linter suggestions (#655)
* cleanup phase 1

* linter fixes phase 2
2020-01-24 12:05:53 -08:00

213 lines
5.8 KiB
Go

package config
import (
"fmt"
"os"
"reflect"
"regexp"
"strings"
"sync"
"github.com/golang/glog"
"github.com/minio/minio/pkg/wildcard"
v1 "k8s.io/api/core/v1"
informers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
)
// read the conifgMap with name in env:INIT_CONFIG
// this configmap stores the resources that are to be filtered
const cmNameEnv string = "INIT_CONFIG"
// ConfigData stores the configuration
type ConfigData struct {
client kubernetes.Interface
// configMap Name
cmName string
// lock configuration
mux sync.RWMutex
// configuration data
filters []k8Resource
// hasynced
cmSycned cache.InformerSynced
}
// ToFilter checks if the given resource is set to be filtered in the configuration
func (cd *ConfigData) ToFilter(kind, namespace, name string) bool {
cd.mux.RLock()
defer cd.mux.RUnlock()
for _, f := range cd.filters {
if wildcard.Match(f.Kind, kind) && wildcard.Match(f.Namespace, namespace) && wildcard.Match(f.Name, name) {
return true
}
}
return false
}
// Interface to be used by consumer to check filters
type Interface interface {
ToFilter(kind, namespace, name string) bool
}
// NewConfigData ...
func NewConfigData(rclient kubernetes.Interface, cmInformer informers.ConfigMapInformer, filterK8Resources string) *ConfigData {
// environment var is read at start only
if cmNameEnv == "" {
glog.Info("ConfigMap name not defined in env:INIT_CONFIG: loading no default configuration")
}
cd := ConfigData{
client: rclient,
cmName: os.Getenv(cmNameEnv),
cmSycned: cmInformer.Informer().HasSynced,
}
//TODO: this has been added to backward support command line arguments
// will be removed in future and the configuration will be set only via configmaps
if filterK8Resources != "" {
glog.Info("Init configuration from commandline arguments")
cd.initFilters(filterK8Resources)
}
cmInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: cd.addCM,
UpdateFunc: cd.updateCM,
DeleteFunc: cd.deleteCM,
})
return &cd
}
//Run checks syncing
func (cd *ConfigData) Run(stopCh <-chan struct{}) {
// wait for cache to populate first time
if !cache.WaitForCacheSync(stopCh, cd.cmSycned) {
glog.Error("configuration: failed to sync informer cache")
}
}
func (cd *ConfigData) addCM(obj interface{}) {
cm := obj.(*v1.ConfigMap)
if cm.Name != cd.cmName {
return
}
cd.load(*cm)
// else load the configuration
}
func (cd *ConfigData) updateCM(old, cur interface{}) {
cm := cur.(*v1.ConfigMap)
if cm.Name != cd.cmName {
return
}
// if data has not changed then dont load configmap
cd.load(*cm)
}
func (cd *ConfigData) deleteCM(obj interface{}) {
cm, ok := obj.(*v1.ConfigMap)
if !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
glog.Info(fmt.Errorf("Couldn't get object from tombstone %#v", obj))
return
}
_, ok = tombstone.Obj.(*v1.ConfigMap)
if !ok {
glog.Info(fmt.Errorf("Tombstone contained object that is not a ConfigMap %#v", obj))
return
}
}
if cm.Name != cd.cmName {
return
}
// remove the configuration parameters
cd.unload(*cm)
}
func (cd *ConfigData) load(cm v1.ConfigMap) {
if cm.Data == nil {
glog.V(4).Infof("Configuration: No data defined in ConfigMap %s", cm.Name)
return
}
// get resource filters
filters, ok := cm.Data["resourceFilters"]
if !ok {
glog.V(4).Infof("Configuration: No resourceFilters defined in ConfigMap %s", cm.Name)
return
}
// filters is a string
if filters == "" {
glog.V(4).Infof("Configuration: resourceFilters is empty in ConfigMap %s", cm.Name)
return
}
// parse and load the configuration
cd.mux.Lock()
defer cd.mux.Unlock()
newFilters := parseKinds(filters)
if reflect.DeepEqual(newFilters, cd.filters) {
glog.V(4).Infof("Configuration: resourceFilters did not change in ConfigMap %s", cm.Name)
return
}
glog.V(4).Infof("Configuration: Old resource filters %v", cd.filters)
glog.Infof("Configuration: New resource filters to %v", newFilters)
// update filters
cd.filters = newFilters
}
//TODO: this has been added to backward support command line arguments
// will be removed in future and the configuration will be set only via configmaps
func (cd *ConfigData) initFilters(filters string) {
// parse and load the configuration
cd.mux.Lock()
defer cd.mux.Unlock()
newFilters := parseKinds(filters)
glog.Infof("Configuration: Init resource filters to %v", newFilters)
// update filters
cd.filters = newFilters
}
func (cd *ConfigData) unload(cm v1.ConfigMap) {
// TODO pick one msg
glog.Infof("Configuration: ConfigMap %s deleted, removing configuration filters", cm.Name)
glog.Infof("Configuration: Removing all resource filters as ConfigMap %s deleted", cm.Name)
cd.mux.Lock()
defer cd.mux.Unlock()
cd.filters = []k8Resource{}
}
type k8Resource struct {
Kind string //TODO: as we currently only support one GVK version, we use the kind only. But if we support multiple GVK, then GV need to be added
Namespace string
Name string
}
//ParseKinds parses the kinds if a single string contains comma separated kinds
// {"1,2,3","4","5"} => {"1","2","3","4","5"}
func parseKinds(list string) []k8Resource {
resources := []k8Resource{}
var resource k8Resource
re := regexp.MustCompile(`\[([^\[\]]*)\]`)
submatchall := re.FindAllString(list, -1)
for _, element := range submatchall {
element = strings.Trim(element, "[")
element = strings.Trim(element, "]")
elements := strings.Split(element, ",")
//TODO: wildcards for namespace and name
if len(elements) == 0 {
continue
}
if len(elements) == 3 {
resource = k8Resource{Kind: elements[0], Namespace: elements[1], Name: elements[2]}
}
if len(elements) == 2 {
resource = k8Resource{Kind: elements[0], Namespace: elements[1]}
}
if len(elements) == 1 {
resource = k8Resource{Kind: elements[0]}
}
resources = append(resources, resource)
}
return resources
}