1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-29 02:45:06 +00:00

logs & access

This commit is contained in:
shivkumar dudhani 2020-03-17 11:05:20 -07:00
parent 5912eda1c0
commit 1b1ab78f77
79 changed files with 1415 additions and 958 deletions

View file

@ -5,23 +5,26 @@ package main
import (
"flag"
"fmt"
"os"
"regexp"
"strconv"
"sync"
"time"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/config"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/log"
"github.com/nirmata/kyverno/pkg/signal"
"k8s.io/apimachinery/pkg/api/errors"
rest "k8s.io/client-go/rest"
clientcmd "k8s.io/client-go/tools/clientcmd"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)
var (
kubeconfig string
setupLog = log.Log.WithName("setup")
)
const (
@ -30,20 +33,21 @@ const (
)
func main() {
defer glog.Flush()
// os signal handler
stopCh := signal.SetupSignalHandler()
// create client config
clientConfig, err := createClientConfig(kubeconfig)
if err != nil {
glog.Fatalf("Error building kubeconfig: %v\n", err)
setupLog.Error(err, "Failed to build kubeconfig")
os.Exit(1)
}
// DYNAMIC CLIENT
// - client for all registered resources
client, err := client.NewClient(clientConfig, 10*time.Second, stopCh)
client, err := client.NewClient(clientConfig, 10*time.Second, stopCh, log.Log)
if err != nil {
glog.Fatalf("Error creating client: %v\n", err)
setupLog.Error(err, "Failed to create client")
os.Exit(1)
}
// Exit for unsupported version of kubernetes cluster
@ -78,12 +82,12 @@ func main() {
for err := range merge(done, stopCh, p1, p2) {
if err != nil {
failure = true
glog.Errorf("failed to cleanup: %v", err)
log.Log.Error(err, "failed to cleanup resource")
}
}
// if there is any failure then we fail process
if failure {
glog.Errorf("failed to cleanup webhook configurations")
log.Log.Info("failed to cleanup webhook configurations")
os.Exit(1)
}
}
@ -95,36 +99,41 @@ func init() {
flag.Set("stderrthreshold", "WARNING")
flag.Set("v", "2")
flag.Parse()
log.SetLogger(zap.New(func(o *zap.Options) {
o.Development = true
}))
}
func removeWebhookIfExists(client *client.Client, kind string, name string) error {
logger := log.Log.WithName("removeExistingWebhook").WithValues("kind", kind, "name", name)
var err error
// Get resource
_, err = client.GetResource(kind, "", name)
if errors.IsNotFound(err) {
glog.V(4).Infof("%s(%s) not found", name, kind)
logger.V(4).Info("resource not found")
return nil
}
if err != nil {
glog.Errorf("failed to get resource %s(%s)", name, kind)
logger.Error(err, "failed to get resource")
return err
}
// Delete resource
err = client.DeleteResource(kind, "", name, false)
if err != nil {
glog.Errorf("failed to delete resource %s(%s)", name, kind)
logger.Error(err, "failed to delete resource")
return err
}
glog.Infof("cleaned up resource %s(%s)", name, kind)
logger.Info("removed the resource")
return nil
}
func createClientConfig(kubeconfig string) (*rest.Config, error) {
logger := log.Log
if kubeconfig == "" {
glog.Info("Using in-cluster configuration")
logger.Info("Using in-cluster configuration")
return rest.InClusterConfig()
}
glog.Infof("Using configuration from '%s'", kubeconfig)
logger.Info(fmt.Sprintf("Using configuration from '%s'", kubeconfig))
return clientcmd.BuildConfigFromFlags("", kubeconfig)
}
@ -163,6 +172,7 @@ func gen(done <-chan struct{}, stopCh <-chan struct{}, requests ...request) <-ch
// processes the requests
func process(client *client.Client, done <-chan struct{}, stopCh <-chan struct{}, requests <-chan request) <-chan error {
logger := log.Log.WithName("process")
out := make(chan error)
go func() {
defer close(out)
@ -170,10 +180,10 @@ func process(client *client.Client, done <-chan struct{}, stopCh <-chan struct{}
select {
case out <- removeWebhookIfExists(client, req.kind, req.name):
case <-done:
println("done process")
logger.Info("done")
return
case <-stopCh:
println("shutting down process")
logger.Info("shutting down")
return
}
}
@ -183,6 +193,7 @@ func process(client *client.Client, done <-chan struct{}, stopCh <-chan struct{}
// waits for all processes to be complete and merges result
func merge(done <-chan struct{}, stopCh <-chan struct{}, processes ...<-chan error) <-chan error {
logger := log.Log.WithName("merge")
var wg sync.WaitGroup
out := make(chan error)
// gets the output from each process
@ -192,10 +203,10 @@ func merge(done <-chan struct{}, stopCh <-chan struct{}, processes ...<-chan err
select {
case out <- err:
case <-done:
println("done merge")
logger.Info("done")
return
case <-stopCh:
println("shutting down merge")
logger.Info("shutting down")
return
}
}
@ -215,30 +226,37 @@ func merge(done <-chan struct{}, stopCh <-chan struct{}, processes ...<-chan err
}
func isVersionSupported(client *client.Client) {
logger := log.Log
serverVersion, err := client.DiscoveryClient.GetServerVersion()
if err != nil {
glog.Fatalf("Failed to get kubernetes server version: %v\n", err)
logger.Error(err, "Failed to get kubernetes server version")
os.Exit(1)
}
exp := regexp.MustCompile(`v(\d*).(\d*).(\d*)`)
groups := exp.FindAllStringSubmatch(serverVersion.String(), -1)
if len(groups) != 1 || len(groups[0]) != 4 {
glog.Fatalf("Failed to extract kubernetes server version: %v.err %v\n", serverVersion, err)
logger.Error(err, "Failed to extract kubernetes server version", "serverVersion", serverVersion)
os.Exit(1)
}
// convert string to int
// assuming the version are always intergers
major, err := strconv.Atoi(groups[0][1])
if err != nil {
glog.Fatalf("Failed to extract kubernetes major server version: %v.err %v\n", serverVersion, err)
logger.Error(err, "Failed to extract kubernetes major server version", "serverVersion", serverVersion)
os.Exit(1)
}
minor, err := strconv.Atoi(groups[0][2])
if err != nil {
glog.Fatalf("Failed to extract kubernetes minor server version: %v.err %v\n", serverVersion, err)
logger.Error(err, "Failed to extract kubernetes minor server version", "serverVersion", serverVersion)
os.Exit(1)
}
sub, err := strconv.Atoi(groups[0][3])
if err != nil {
glog.Fatalf("Failed to extract kubernetes sub minor server version:%v. err %v\n", serverVersion, err)
logger.Error(err, "Failed to extract kubernetes sub minor server version", "serverVersion", serverVersion)
os.Exit(1)
}
if major <= 1 && minor <= 12 && sub < 7 {
glog.Fatalf("Unsupported kubernetes server version %s. Kyverno is supported from version v1.12.7+", serverVersion)
logger.Info("Unsupported kubernetes server version %s. Kyverno is supported from version v1.12.7+", "serverVersion", serverVersion)
os.Exit(1)
}
}

View file

@ -3,6 +3,8 @@ package main
import (
"context"
"flag"
"fmt"
"os"
"time"
"github.com/golang/glog"
@ -14,6 +16,7 @@ import (
event "github.com/nirmata/kyverno/pkg/event"
"github.com/nirmata/kyverno/pkg/generate"
generatecleanup "github.com/nirmata/kyverno/pkg/generate/cleanup"
"github.com/nirmata/kyverno/pkg/log"
"github.com/nirmata/kyverno/pkg/policy"
"github.com/nirmata/kyverno/pkg/policystore"
"github.com/nirmata/kyverno/pkg/policyviolation"
@ -24,6 +27,7 @@ import (
"github.com/nirmata/kyverno/pkg/webhooks"
webhookgenerate "github.com/nirmata/kyverno/pkg/webhooks/generate"
kubeinformers "k8s.io/client-go/informers"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)
var (
@ -35,20 +39,23 @@ var (
// will be removed in future and the configuration will be set only via configmaps
filterK8Resources string
// User FQDN as CSR CN
fqdncn bool
fqdncn bool
verbosity string
setupLog = log.Log.WithName("setup")
)
func main() {
defer glog.Flush()
version.PrintVersionInfo()
version.PrintVersionInfo(log.Log)
// cleanUp Channel
cleanUp := make(chan struct{})
// handle os signals
stopCh := signal.SetupSignalHandler()
// CLIENT CONFIG
clientConfig, err := config.CreateClientConfig(kubeconfig)
clientConfig, err := config.CreateClientConfig(kubeconfig, log.Log)
if err != nil {
glog.Fatalf("Error building kubeconfig: %v\n", err)
setupLog.Error(err, "Failed to build kubeconfig")
os.Exit(1)
}
// KYVENO CRD CLIENT
@ -57,29 +64,33 @@ func main() {
// - PolicyViolation
pclient, err := kyvernoclient.NewForConfig(clientConfig)
if err != nil {
glog.Fatalf("Error creating client: %v\n", err)
setupLog.Error(err, "Failed to create client")
os.Exit(1)
}
// DYNAMIC CLIENT
// - client for all registered resources
// - invalidate local cache of registered resource every 10 seconds
client, err := dclient.NewClient(clientConfig, 10*time.Second, stopCh)
client, err := dclient.NewClient(clientConfig, 10*time.Second, stopCh, log.Log)
if err != nil {
glog.Fatalf("Error creating client: %v\n", err)
setupLog.Error(err, "Failed to create client")
os.Exit(1)
}
// CRD CHECK
// - verify if the CRD for Policy & PolicyViolation are available
if !utils.CRDInstalled(client.DiscoveryClient) {
glog.Fatalf("Required CRDs unavailable")
if !utils.CRDInstalled(client.DiscoveryClient, log.Log) {
setupLog.Error(fmt.Errorf("pre-requisite CRDs not installed"), "Failed to create watch on kyverno CRDs")
os.Exit(1)
}
// KUBERNETES CLIENT
kubeClient, err := utils.NewKubeClient(clientConfig)
if err != nil {
glog.Fatalf("Error creating kubernetes client: %v\n", err)
setupLog.Error(err, "Failed to create kubernetes client")
os.Exit(1)
}
// TODO(shuting): To be removed for v1.2.0
utils.CleanupOldCrd(client)
utils.CleanupOldCrd(client, log.Log)
// KUBERNETES RESOURCES INFORMER
// watches namespace resource
@ -96,16 +107,18 @@ func main() {
clientConfig,
client,
serverIP,
int32(webhookTimeout))
int32(webhookTimeout),
log.Log)
// Resource Mutating Webhook Watcher
lastReqTime := checker.NewLastReqTime()
lastReqTime := checker.NewLastReqTime(log.Log.WithName("LastReqTime"))
rWebhookWatcher := webhookconfig.NewResourceWebhookRegister(
lastReqTime,
kubeInformer.Admissionregistration().V1beta1().MutatingWebhookConfigurations(),
kubeInformer.Admissionregistration().V1beta1().ValidatingWebhookConfigurations(),
webhookRegistrationClient,
runValidationInMutatingWebhook,
log.Log.WithName("ResourceWebhookRegister"),
)
// KYVERNO CRD INFORMER
@ -124,23 +137,28 @@ func main() {
configData := config.NewConfigData(
kubeClient,
kubeInformer.Core().V1().ConfigMaps(),
filterK8Resources)
filterK8Resources,
log.Log.WithName("ConfigData"),
)
// Policy meta-data store
policyMetaStore := policystore.NewPolicyStore(pInformer.Kyverno().V1().ClusterPolicies())
policyMetaStore := policystore.NewPolicyStore(pInformer.Kyverno().V1().ClusterPolicies(), log.Log.WithName("PolicyStore"))
// EVENT GENERATOR
// - generate event with retry mechanism
egen := event.NewEventGenerator(
client,
pInformer.Kyverno().V1().ClusterPolicies())
pInformer.Kyverno().V1().ClusterPolicies(),
log.Log.WithName("EventGenerator"))
// POLICY VIOLATION GENERATOR
// -- generate policy violation
pvgen := policyviolation.NewPVGenerator(pclient,
client,
pInformer.Kyverno().V1().ClusterPolicyViolations(),
pInformer.Kyverno().V1().PolicyViolations())
pInformer.Kyverno().V1().PolicyViolations(),
log.Log.WithName("PolicyViolationGenerator"),
)
// POLICY CONTROLLER
// - reconciliation policy and policy violation
@ -156,13 +174,16 @@ func main() {
egen,
pvgen,
policyMetaStore,
rWebhookWatcher)
rWebhookWatcher,
log.Log.WithName("PolicyController"),
)
if err != nil {
glog.Fatalf("error creating policy controller: %v\n", err)
setupLog.Error(err, "Failed to create policy controller")
os.Exit(1)
}
// GENERATE REQUEST GENERATOR
grgen := webhookgenerate.NewGenerator(pclient, stopCh)
grgen := webhookgenerate.NewGenerator(pclient, stopCh, log.Log.WithName("GenerateRequestGenerator"))
// GENERATE CONTROLLER
// - applies generate rules on resources based on generate requests created by webhook
@ -174,6 +195,7 @@ func main() {
egen,
pvgen,
kubedynamicInformer,
log.Log.WithName("GenerateController"),
)
// GENERATE REQUEST CLEANUP
// -- cleans up the generate requests that have not been processed(i.e. state = [Pending, Failed]) for more than defined timeout
@ -183,12 +205,14 @@ func main() {
pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().GenerateRequests(),
kubedynamicInformer,
log.Log.WithName("GenerateCleanUpController"),
)
// CONFIGURE CERTIFICATES
tlsPair, err := client.InitTLSPemPair(clientConfig, fqdncn)
if err != nil {
glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err)
setupLog.Error(err, "Failed to initialize TLS key/certificate pair")
os.Exit(1)
}
// WEBHOOK REGISTRATION
@ -197,7 +221,8 @@ func main() {
// resource webhook confgiuration is generated dynamically in the webhook server and policy controller
// based on the policy resources created
if err = webhookRegistrationClient.Register(); err != nil {
glog.Fatalf("Failed registering Admission Webhooks: %v\n", err)
setupLog.Error(err, "Failed to register Admission webhooks")
os.Exit(1)
}
// WEBHOOOK
@ -221,9 +246,12 @@ func main() {
pvgen,
grgen,
rWebhookWatcher,
cleanUp)
cleanUp,
log.Log.WithName("WebhookServer"),
)
if err != nil {
glog.Fatalf("Unable to create webhook server: %v\n", err)
setupLog.Error(err, "Failed to create webhook server")
os.Exit(1)
}
// Start the components
pInformer.Start(stopCh)
@ -259,7 +287,7 @@ func main() {
// resource cleanup
// remove webhook configurations
<-cleanUp
glog.Info("successful shutdown of kyverno controller")
setupLog.Info("Kyverno shutdown successful")
}
func init() {
@ -271,6 +299,10 @@ func init() {
// Generate CSR with CN as FQDN due to https://github.com/nirmata/kyverno/issues/542
flag.BoolVar(&fqdncn, "fqdn-as-cn", false, "use FQDN as Common Name in CSR")
config.LogDefaultFlags()
config.LogDefaultFlags(setupLog)
flag.StringVar(&verbosity, "verbosity", "", "set verbosity for logs")
flag.Parse()
log.SetLogger(zap.New(func(o *zap.Options) {
o.Development = true
}))
}

View file

@ -74,7 +74,7 @@ func (in *ClusterPolicy) DeepCopyObject() runtime.Object {
func (in *ClusterPolicyList) DeepCopyInto(out *ClusterPolicyList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ClusterPolicy, len(*in))
@ -135,7 +135,7 @@ func (in *ClusterPolicyViolation) DeepCopyObject() runtime.Object {
func (in *ClusterPolicyViolationList) DeepCopyInto(out *ClusterPolicyViolationList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ClusterPolicyViolation, len(*in))
@ -241,7 +241,7 @@ func (in *GenerateRequestContext) DeepCopy() *GenerateRequestContext {
func (in *GenerateRequestList) DeepCopyInto(out *GenerateRequestList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]GenerateRequest, len(*in))
@ -420,7 +420,7 @@ func (in *PolicyViolation) DeepCopyObject() runtime.Object {
func (in *PolicyViolationList) DeepCopyInto(out *PolicyViolationList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]PolicyViolation, len(*in))

View file

@ -4,7 +4,7 @@ import (
"fmt"
"reflect"
"github.com/golang/glog"
"github.com/go-logr/logr"
client "github.com/nirmata/kyverno/pkg/dclient"
authorizationv1 "k8s.io/api/authorization/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -17,12 +17,14 @@ type CanIOptions struct {
verb string
kind string
client *client.Client
log logr.Logger
}
//NewCanI returns a new instance of operation access controler evaluator
func NewCanI(client *client.Client, kind, namespace, verb string) *CanIOptions {
func NewCanI(client *client.Client, kind, namespace, verb string, log logr.Logger) *CanIOptions {
o := CanIOptions{
client: client,
log: log,
}
o.namespace = namespace
@ -64,11 +66,12 @@ func (o *CanIOptions) RunAccessCheck() (bool, error) {
// - verb
// - resource
// - subresource
logger := o.log.WithValues("kind", sar.Kind, "namespace", sar.Namespace, "name", sar.Name)
// Create the Resource
resp, err := o.client.CreateResource("SelfSubjectAccessReview", "", sar, false)
if err != nil {
glog.Errorf("failed to create resource %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
logger.Error(err, "failed to create resource")
return false, err
}
@ -76,9 +79,9 @@ func (o *CanIOptions) RunAccessCheck() (bool, error) {
allowed, ok, err := unstructured.NestedBool(resp.Object, "status", "allowed")
if !ok {
if err != nil {
glog.Errorf("unexpected error when getting status.allowed for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
logger.Error(err, "failed to get the field", "field", "status.allowed")
}
glog.Errorf("status.allowed not found for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
logger.Info("field not found", "field", "status.allowed")
}
if !allowed {
@ -86,22 +89,21 @@ func (o *CanIOptions) RunAccessCheck() (bool, error) {
reason, ok, err := unstructured.NestedString(resp.Object, "status", "reason")
if !ok {
if err != nil {
glog.Errorf("unexpected error when getting status.reason for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
logger.Error(err, "failed to get the field", "field", "status.reason")
}
glog.Errorf("status.reason not found for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
logger.Info("field not found", "field", "status.reason")
}
// status.evaluationError
evaluationError, ok, err := unstructured.NestedString(resp.Object, "status", "evaludationError")
if !ok {
if err != nil {
glog.Errorf("unexpected error when getting status.evaluationError for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
logger.Error(err, "failed to get the field", "field", "status.evaluationError")
}
glog.Errorf("status.evaluationError not found for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
logger.Info("field not found", "field", "status.evaluationError")
}
// Reporting ? (just logs)
glog.Errorf("reason to disallow operation: %s", reason)
glog.Errorf("evaluationError to disallow operation: %s", evaluationError)
logger.Info("disallowed operation", "reason", reason, "evaluationError", evaluationError)
}
return allowed, nil

View file

@ -4,7 +4,7 @@ import (
"sync"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
dclient "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/event"
@ -20,8 +20,9 @@ const (
// LastReqTime stores the lastrequest times for incoming api-requests
type LastReqTime struct {
t time.Time
mu sync.RWMutex
t time.Time
mu sync.RWMutex
log logr.Logger
}
//Time returns the lastrequest time
@ -39,16 +40,17 @@ func (t *LastReqTime) SetTime(tm time.Time) {
}
//NewLastReqTime returns a new instance of LastRequestTime store
func NewLastReqTime() *LastReqTime {
func NewLastReqTime(log logr.Logger) *LastReqTime {
return &LastReqTime{
t: time.Now(),
t: time.Now(),
log: log,
}
}
func checkIfPolicyWithMutateAndGenerateExists(pLister kyvernolister.ClusterPolicyLister) bool {
func checkIfPolicyWithMutateAndGenerateExists(pLister kyvernolister.ClusterPolicyLister, log logr.Logger) bool {
policies, err := pLister.ListResources(labels.NewSelector())
if err != nil {
glog.Error()
log.Error(err, "failed to list cluster policies")
}
for _, policy := range policies {
if policy.HasMutateOrValidateOrGenerate() {
@ -62,15 +64,16 @@ func checkIfPolicyWithMutateAndGenerateExists(pLister kyvernolister.ClusterPolic
//Run runs the checker and verify the resource update
func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister, eventGen event.Interface, client *dclient.Client, defaultResync time.Duration, deadline time.Duration, stopCh <-chan struct{}) {
glog.V(2).Infof("starting default resync for webhook checker with resync time %d nanoseconds", defaultResync)
logger := t.log
logger.V(2).Info("tarting default resync for webhook checker", "resyncTime", defaultResync)
maxDeadline := deadline * time.Duration(MaxRetryCount)
ticker := time.NewTicker(defaultResync)
/// interface to update and increment kyverno webhook status via annotations
statuscontrol := NewVerifyControl(client, eventGen)
statuscontrol := NewVerifyControl(client, eventGen, logger.WithName("StatusControl"))
// send the initial update status
if checkIfPolicyWithMutateAndGenerateExists(pLister) {
if checkIfPolicyWithMutateAndGenerateExists(pLister, logger) {
if err := statuscontrol.SuccessStatus(); err != nil {
glog.Error(err)
logger.Error(err, "failed to set 'success' status")
}
}
@ -84,36 +87,36 @@ func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister, eventGen ev
case <-ticker.C:
// if there are no policies then we dont have a webhook on resource.
// we indirectly check if the resource
if !checkIfPolicyWithMutateAndGenerateExists(pLister) {
if !checkIfPolicyWithMutateAndGenerateExists(pLister, logger) {
continue
}
// get current time
timeDiff := time.Since(t.Time())
if timeDiff > maxDeadline {
glog.Infof("failed to receive any request for more than %v ", maxDeadline)
glog.Info("Admission Control failing: Webhook is not receiving requests forwarded by api-server as per webhook configurations")
logger.Info("request exceeded max deadline", "deadline", maxDeadline)
logger.Info("Admission Control failing: Webhook is not receiving requests forwarded by api-server as per webhook configurations")
// set the status unavailable
if err := statuscontrol.FailedStatus(); err != nil {
glog.Error(err)
logger.Error(err, "failed to set 'failed' status")
}
continue
}
if timeDiff > deadline {
glog.Info("Admission Control failing: Webhook is not receiving requests forwarded by api-server as per webhook configurations")
logger.Info("Admission Control failing: Webhook is not receiving requests forwarded by api-server as per webhook configurations")
// send request to update the kyverno deployment
if err := statuscontrol.IncrementAnnotation(); err != nil {
glog.Error(err)
logger.Error(err, "failed to increment annotation")
}
continue
}
// if the status was false before then we update it to true
// send request to update the kyverno deployment
if err := statuscontrol.SuccessStatus(); err != nil {
glog.Error(err)
logger.Error(err, "failed to update success status")
}
case <-stopCh:
// handler termination signal
glog.V(2).Infof("stopping default resync for webhook checker")
logger.V(2).Info("stopping default resync for webhook checker")
return
}
}

View file

@ -4,7 +4,7 @@ import (
"fmt"
"strconv"
"github.com/golang/glog"
"github.com/go-logr/logr"
dclient "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/event"
)
@ -29,6 +29,7 @@ type StatusInterface interface {
type StatusControl struct {
client *dclient.Client
eventGen event.Interface
log logr.Logger
}
//SuccessStatus ...
@ -42,20 +43,22 @@ func (vc StatusControl) FailedStatus() error {
}
// NewVerifyControl ...
func NewVerifyControl(client *dclient.Client, eventGen event.Interface) *StatusControl {
func NewVerifyControl(client *dclient.Client, eventGen event.Interface, log logr.Logger) *StatusControl {
return &StatusControl{
client: client,
eventGen: eventGen,
log: log,
}
}
func (vc StatusControl) setStatus(status string) error {
glog.Infof("setting deployment %s in ns %s annotation %s to %s", deployName, deployNamespace, annWebhookStats, status)
logger := vc.log
logger.Info(fmt.Sprintf("setting deployment %s in ns %s annotation %s to %s", deployName, deployNamespace, annWebhookStats, status))
var ann map[string]string
var err error
deploy, err := vc.client.GetResource("Deployment", deployNamespace, deployName)
if err != nil {
glog.V(4).Infof("failed to get deployment %s in namespace %s: %v", deployName, deployNamespace, err)
logger.Error(err, "failed to get deployment resource")
return err
}
ann = deploy.GetAnnotations()
@ -67,7 +70,7 @@ func (vc StatusControl) setStatus(status string) error {
if ok {
// annotatiaion is present
if webhookAction == status {
glog.V(4).Infof("annotation %s already set to '%s'", annWebhookStats, status)
logger.V(4).Info(fmt.Sprintf("annotation %s already set to '%s'", annWebhookStats, status))
return nil
}
}
@ -77,7 +80,7 @@ func (vc StatusControl) setStatus(status string) error {
// update counter
_, err = vc.client.UpdateResource("Deployment", deployNamespace, deploy, false)
if err != nil {
glog.V(4).Infof("failed to update annotation %s for deployment %s in namespace %s: %v", annWebhookStats, deployName, deployNamespace, err)
logger.Error(err, fmt.Sprintf("failed to update annotation %s for deployment %s in namespace %s", annWebhookStats, deployName, deployNamespace))
return err
}
// create event on kyverno deployment
@ -97,12 +100,13 @@ func createStatusUpdateEvent(status string, eventGen event.Interface) {
//IncrementAnnotation ...
func (vc StatusControl) IncrementAnnotation() error {
glog.Infof("setting deployment %s in ns %s annotation %s", deployName, deployNamespace, annCounter)
logger := vc.log
logger.Info(fmt.Sprintf("setting deployment %s in ns %s annotation %s", deployName, deployNamespace, annCounter))
var ann map[string]string
var err error
deploy, err := vc.client.GetResource("Deployment", deployNamespace, deployName)
if err != nil {
glog.V(4).Infof("failed to get deployment %s in namespace %s: %v", deployName, deployNamespace, err)
logger.Error(err, "failed to get deployment %s in namespace %s", deployName, deployNamespace)
return err
}
ann = deploy.GetAnnotations()
@ -112,18 +116,18 @@ func (vc StatusControl) IncrementAnnotation() error {
}
counter, err := strconv.Atoi(ann[annCounter])
if err != nil {
glog.V(4).Infof("failed to parse string: %v", err)
logger.Error(err, "Failed to parse string")
return err
}
// increment counter
counter++
ann[annCounter] = strconv.Itoa(counter)
glog.Infof("incrementing annotation %s counter to %d", annCounter, counter)
logger.Info("incrementing annotation", "old", annCounter, "new", counter)
deploy.SetAnnotations(ann)
// update counter
_, err = vc.client.UpdateResource("Deployment", deployNamespace, deploy, false)
if err != nil {
glog.V(4).Infof("failed to update annotation %s for deployment %s in namespace %s: %v", annCounter, deployName, deployNamespace, err)
logger.Error(err, fmt.Sprintf("failed to update annotation %s for deployment %s in namespace %s", annCounter, deployName, deployNamespace))
return err
}
return nil

View file

@ -19,6 +19,8 @@ limitations under the License.
package versioned
import (
"fmt"
kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
@ -51,9 +53,14 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface {
}
// NewForConfig creates a new Clientset for the given config.
// If config's RateLimiter is not set and QPS and Burst are acceptable,
// NewForConfig will generate a rate-limiter in configShallowCopy.
func NewForConfig(c *rest.Config) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
if configShallowCopy.Burst <= 0 {
return nil, fmt.Errorf("Burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
}
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
}
var cs Clientset

View file

@ -41,7 +41,7 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset {
}
}
cs := &Clientset{}
cs := &Clientset{tracker: o}
cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
cs.AddReactor("*", "*", testing.ObjectReaction(o))
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
@ -63,12 +63,17 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset {
type Clientset struct {
testing.Fake
discovery *fakediscovery.FakeDiscovery
tracker testing.ObjectTracker
}
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
return c.discovery
}
func (c *Clientset) Tracker() testing.ObjectTracker {
return c.tracker
}
var _ clientset.Interface = &Clientset{}
// KyvernoV1 retrieves the KyvernoV1Client

View file

@ -21,7 +21,6 @@ package v1
import (
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
rest "k8s.io/client-go/rest"
)
@ -86,7 +85,7 @@ func setConfigDefaults(config *rest.Config) error {
gv := v1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()

View file

@ -2,8 +2,9 @@ package config
import (
"flag"
"os"
"github.com/golang/glog"
"github.com/go-logr/logr"
rest "k8s.io/client-go/rest"
clientcmd "k8s.io/client-go/tools/clientcmd"
)
@ -74,29 +75,34 @@ var (
VerifyMutatingWebhookServicePath = "/verifymutate"
)
//LogDefaultFlags sets default glog flags
func LogDefaultFlags() {
//LogDefaultFlags sets default flags
func LogDefaultFlags(log logr.Logger) {
logger := log.WithName("LogDefaultFlags")
var err error
err = flag.Set("logtostderr", "true")
if err != nil {
glog.Fatalf("failed to set flag 'logtostderr' to 'true':%v", err)
logger.Error(err, "failed to set flag", "flag", "logtostderr", "value", "true")
os.Exit(1)
}
err = flag.Set("stderrthreshold", "WARNING")
if err != nil {
glog.Fatalf("failed to set flag 'stderrthreshold' to 'WARNING':%v", err)
logger.Error(err, "failed to set flag", "flag", "stderrthreshold", "value", "WARNING")
os.Exit(1)
}
flag.Set("v", "2")
if err != nil {
glog.Fatalf("failed to set flag 'v' to '2':%v", err)
logger.Error(err, "failed to set flag", "flag", "v", "value", "2")
os.Exit(1)
}
}
//CreateClientConfig creates client config
func CreateClientConfig(kubeconfig string) (*rest.Config, error) {
func CreateClientConfig(kubeconfig string, log logr.Logger) (*rest.Config, error) {
logger := log.WithName("CreateClientConfig")
if kubeconfig == "" {
glog.Info("Using in-cluster configuration")
logger.Info("Using in-cluster configuration")
return rest.InClusterConfig()
}
glog.V(4).Infof("Using configuration from '%s'", kubeconfig)
logger.V(4).Info("Using specified kubeconfig", "kubeconfig", kubeconfig)
return clientcmd.BuildConfigFromFlags("", kubeconfig)
}

View file

@ -1,14 +1,13 @@
package config
import (
"fmt"
"os"
"reflect"
"regexp"
"strings"
"sync"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/minio/minio/pkg/wildcard"
v1 "k8s.io/api/core/v1"
informers "k8s.io/client-go/informers/core/v1"
@ -31,6 +30,7 @@ type ConfigData struct {
filters []k8Resource
// hasynced
cmSycned cache.InformerSynced
log logr.Logger
}
// ToFilter checks if the given resource is set to be filtered in the configuration
@ -51,20 +51,21 @@ type Interface interface {
}
// NewConfigData ...
func NewConfigData(rclient kubernetes.Interface, cmInformer informers.ConfigMapInformer, filterK8Resources string) *ConfigData {
func NewConfigData(rclient kubernetes.Interface, cmInformer informers.ConfigMapInformer, filterK8Resources string, log logr.Logger) *ConfigData {
// environment var is read at start only
if cmNameEnv == "" {
glog.Info("ConfigMap name not defined in env:INIT_CONFIG: loading no default configuration")
log.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,
log: log,
}
//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.log.Info("init configuration from commandline arguments")
cd.initFilters(filterK8Resources)
}
@ -78,9 +79,10 @@ func NewConfigData(rclient kubernetes.Interface, cmInformer informers.ConfigMapI
//Run checks syncing
func (cd *ConfigData) Run(stopCh <-chan struct{}) {
logger := cd.log
// wait for cache to populate first time
if !cache.WaitForCacheSync(stopCh, cd.cmSycned) {
glog.Error("configuration: failed to sync informer cache")
logger.Info("configuration: failed to sync informer cache")
}
}
@ -103,16 +105,17 @@ func (cd *ConfigData) updateCM(old, cur interface{}) {
}
func (cd *ConfigData) deleteCM(obj interface{}) {
logger := cd.log
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))
logger.Info("failed to get object from tombstone")
return
}
_, ok = tombstone.Obj.(*v1.ConfigMap)
if !ok {
glog.Info(fmt.Errorf("Tombstone contained object that is not a ConfigMap %#v", obj))
logger.Info("Tombstone contained object that is not a ConfigMap", "object", obj)
return
}
}
@ -125,19 +128,20 @@ func (cd *ConfigData) deleteCM(obj interface{}) {
}
func (cd *ConfigData) load(cm v1.ConfigMap) {
logger := cd.log.WithValues("name", cm.Name, "namespace", cm.Namespace)
if cm.Data == nil {
glog.V(4).Infof("Configuration: No data defined in ConfigMap %s", cm.Name)
logger.V(4).Info("configuration: No data defined in ConfigMap")
return
}
// get resource filters
filters, ok := cm.Data["resourceFilters"]
if !ok {
glog.V(4).Infof("Configuration: No resourceFilters defined in ConfigMap %s", cm.Name)
logger.V(4).Info("configuration: No resourceFilters defined in ConfigMap")
return
}
// filters is a string
if filters == "" {
glog.V(4).Infof("Configuration: resourceFilters is empty in ConfigMap %s", cm.Name)
logger.V(4).Info("configuration: resourceFilters is empty in ConfigMap")
return
}
// parse and load the configuration
@ -146,11 +150,10 @@ func (cd *ConfigData) load(cm v1.ConfigMap) {
newFilters := parseKinds(filters)
if reflect.DeepEqual(newFilters, cd.filters) {
glog.V(4).Infof("Configuration: resourceFilters did not change in ConfigMap %s", cm.Name)
logger.V(4).Info("resourceFilters did not change")
return
}
glog.V(4).Infof("Configuration: Old resource filters %v", cd.filters)
glog.Infof("Configuration: New resource filters to %v", newFilters)
logger.V(4).Info(" Updated resource filters", "oldFilters", cd.filters, "newFilters", newFilters)
// update filters
cd.filters = newFilters
}
@ -158,20 +161,20 @@ func (cd *ConfigData) load(cm v1.ConfigMap) {
//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) {
logger := cd.log
// parse and load the configuration
cd.mux.Lock()
defer cd.mux.Unlock()
newFilters := parseKinds(filters)
glog.Infof("Configuration: Init resource filters to %v", newFilters)
logger.Info("Init resource filters", "filters", 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)
logger := cd.log
logger.Info("ConfigMap deleted, removing configuration filters", "name", cm.Name, "namespace", cm.Namespace)
cd.mux.Lock()
defer cd.mux.Unlock()
cd.filters = []k8Resource{}

View file

@ -6,7 +6,6 @@ import (
"net/url"
"time"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/config"
tls "github.com/nirmata/kyverno/pkg/tls"
certificates "k8s.io/api/certificates/v1beta1"
@ -19,13 +18,14 @@ import (
// Created pair is stored in cluster's secret.
// Returns struct with key/certificate pair.
func (c *Client) InitTLSPemPair(configuration *rest.Config, fqdncn bool) (*tls.TlsPemPair, error) {
logger := c.log
certProps, err := c.GetTLSCertProps(configuration)
if err != nil {
return nil, err
}
tlsPair := c.ReadTlsPair(certProps)
if tls.IsTLSPairShouldBeUpdated(tlsPair) {
glog.Info("Generating new key/certificate pair for TLS")
logger.Info("Generating new key/certificate pair for TLS")
tlsPair, err = c.generateTLSPemPair(certProps, fqdncn)
if err != nil {
return nil, err
@ -35,8 +35,7 @@ func (c *Client) InitTLSPemPair(configuration *rest.Config, fqdncn bool) (*tls.T
}
return tlsPair, nil
}
glog.Infoln("Using existing TLS key/certificate pair")
logger.Info("Using existing TLS key/certificate pair")
return tlsPair, nil
}
@ -71,6 +70,7 @@ func (c *Client) generateTLSPemPair(props tls.TlsCertificateProps, fqdncn bool)
// Submits and approves certificate request, returns request which need to be fetched
func (c *Client) submitAndApproveCertificateRequest(req *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, error) {
logger := c.log.WithName("submitAndApproveCertificateRequest")
certClient, err := c.GetCSRInterface()
if err != nil {
return nil, err
@ -86,7 +86,7 @@ func (c *Client) submitAndApproveCertificateRequest(req *certificates.Certificat
if err != nil {
return nil, fmt.Errorf("Unable to delete existing certificate request: %v", err)
}
glog.Info("Old certificate request is deleted")
logger.Info("Old certificate request is deleted")
break
}
}
@ -95,7 +95,7 @@ func (c *Client) submitAndApproveCertificateRequest(req *certificates.Certificat
if err != nil {
return nil, err
}
glog.Infof("Certificate request %s is created", unstrRes.GetName())
logger.Info("Certificate request created", "name", unstrRes.GetName())
res, err := convertToCSR(unstrRes)
if err != nil {
@ -110,7 +110,7 @@ func (c *Client) submitAndApproveCertificateRequest(req *certificates.Certificat
if err != nil {
return nil, fmt.Errorf("Unable to approve certificate request: %v", err)
}
glog.Infof("Certificate request %s is approved", res.ObjectMeta.Name)
logger.Info("Certificate request is approved", "name", res.ObjectMeta.Name)
return res, nil
}
@ -144,9 +144,10 @@ func (c *Client) fetchCertificateFromRequest(req *certificates.CertificateSignin
//ReadRootCASecret returns the RootCA from the pre-defined secret
func (c *Client) ReadRootCASecret() (result []byte) {
logger := c.log.WithName("ReadRootCASecret")
certProps, err := c.GetTLSCertProps(c.clientConfig)
if err != nil {
glog.Error(err)
logger.Error(err, "failed to get TLS Cert Properties")
return result
}
sname := generateRootCASecretName(certProps)
@ -156,16 +157,16 @@ func (c *Client) ReadRootCASecret() (result []byte) {
}
tlsca, err := convertToSecret(stlsca)
if err != nil {
glog.Error(err)
logger.Error(err, "failed to convert secret", "name", sname, "namespace", certProps.Namespace)
return result
}
result = tlsca.Data[rootCAKey]
if len(result) == 0 {
glog.Warningf("root CA certificate not found in secret %s/%s", certProps.Namespace, tlsca.Name)
logger.Info("root CA certificate not found in secret", "name", tlsca.Name, "namespace", certProps.Namespace)
return result
}
glog.V(4).Infof("using CA bundle defined in secret %s/%s to validate the webhook's server certificate", certProps.Namespace, tlsca.Name)
logger.V(4).Info("using CA bundle defined in secret to validate the webhook's server certificate", "name", tlsca.Name, "namespace", certProps.Namespace)
return result
}
@ -174,10 +175,11 @@ const rootCAKey string = "rootCA.crt"
//ReadTlsPair Reads the pair of TLS certificate and key from the specified secret.
func (c *Client) ReadTlsPair(props tls.TlsCertificateProps) *tls.TlsPemPair {
logger := c.log.WithName("ReadTlsPair")
sname := generateTLSPairSecretName(props)
unstrSecret, err := c.GetResource(Secrets, props.Namespace, sname)
if err != nil {
glog.Warningf("Unable to get secret %s/%s: %s", props.Namespace, sname, err)
logger.Error(err, "Failed to get secret", "name", sname, "namespace", props.Namespace)
return nil
}
@ -188,7 +190,7 @@ func (c *Client) ReadTlsPair(props tls.TlsCertificateProps) *tls.TlsPemPair {
sname := generateRootCASecretName(props)
_, err := c.GetResource(Secrets, props.Namespace, sname)
if err != nil {
glog.Errorf("Root CA secret %s/%s is required while using self-signed certificates TLS pair, defaulting to generating new TLS pair", props.Namespace, sname)
logger.Error(err, "Root CA secret is required while using self-signed certificates TLS pair, defaulting to generating new TLS pair", "name", sname, "namespace", props.Namespace)
return nil
}
}
@ -201,11 +203,11 @@ func (c *Client) ReadTlsPair(props tls.TlsCertificateProps) *tls.TlsPemPair {
PrivateKey: secret.Data[v1.TLSPrivateKeyKey],
}
if len(pemPair.Certificate) == 0 {
glog.Warningf("TLS Certificate not found in secret %s/%s", props.Namespace, sname)
logger.Info("TLS Certificate not found in secret", "name", sname, "namespace", props.Namespace)
return nil
}
if len(pemPair.PrivateKey) == 0 {
glog.Warningf("TLS PrivateKey not found in secret %s/%s", props.Namespace, sname)
logger.Info("TLS PrivateKey not found in secret", "name", sname, "namespace", props.Namespace)
return nil
}
return &pemPair
@ -214,6 +216,7 @@ func (c *Client) ReadTlsPair(props tls.TlsCertificateProps) *tls.TlsPemPair {
//WriteTlsPair Writes the pair of TLS certificate and key to the specified secret.
// Updates existing secret or creates new one.
func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPemPair) error {
logger := c.log.WithName("WriteTlsPair")
name := generateTLSPairSecretName(props)
_, err := c.GetResource(Secrets, props.Namespace, name)
if err != nil {
@ -235,7 +238,7 @@ func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPem
_, err := c.CreateResource(Secrets, props.Namespace, secret, false)
if err == nil {
glog.Infof("Secret %s is created", name)
logger.Info("secret created", "name", name, "namespace", props.Namespace)
}
return err
}
@ -251,7 +254,7 @@ func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPem
if err != nil {
return err
}
glog.Infof("Secret %s is updated", name)
logger.Info("secret updated", "name", name, "namespace", props.Namespace)
return nil
}

View file

@ -5,7 +5,7 @@ import (
"strings"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/config"
apps "k8s.io/api/apps/v1"
certificates "k8s.io/api/certificates/v1beta1"
@ -30,13 +30,15 @@ import (
//Client enables interaction with k8 resource
type Client struct {
client dynamic.Interface
log logr.Logger
clientConfig *rest.Config
kclient kubernetes.Interface
DiscoveryClient IDiscovery
}
//NewClient creates new instance of client
func NewClient(config *rest.Config, resync time.Duration, stopCh <-chan struct{}) (*Client, error) {
func NewClient(config *rest.Config, resync time.Duration, stopCh <-chan struct{}, log logr.Logger) (*Client, error) {
dclient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, err
@ -49,9 +51,10 @@ func NewClient(config *rest.Config, resync time.Duration, stopCh <-chan struct{}
client: dclient,
clientConfig: config,
kclient: kclient,
log: log.WithName("Client"),
}
// Set discovery client
discoveryClient := ServerPreferredResources{memory.NewMemCacheClient(kclient.Discovery())}
discoveryClient := ServerPreferredResources{cachedClient: memory.NewMemCacheClient(kclient.Discovery()), log: client.log}
// client will invalidate registered resources cache every x seconds,
// As there is no way to identify if the registered resource is available or not
// we will be invalidating the local cache, so the next request get a fresh cache
@ -187,7 +190,6 @@ func (c *Client) UpdateStatusResource(kind string, namespace string, obj interfa
func convertToUnstructured(obj interface{}) *unstructured.Unstructured {
unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj)
if err != nil {
glog.Errorf("Unable to convert : %v", err)
return nil
}
return &unstructured.Unstructured{Object: unstructuredObj}
@ -225,22 +227,24 @@ func (c *Client) SetDiscovery(discoveryClient IDiscovery) {
//ServerPreferredResources stores the cachedClient instance for discovery client
type ServerPreferredResources struct {
cachedClient discovery.CachedDiscoveryInterface
log logr.Logger
}
//Poll will keep invalidate the local cache
func (c ServerPreferredResources) Poll(resync time.Duration, stopCh <-chan struct{}) {
logger := c.log.WithName("Poll")
// start a ticker
ticker := time.NewTicker(resync)
defer func() { ticker.Stop() }()
glog.Infof("Starting registered resources sync: every %d seconds", resync)
logger.Info("starting registered resources sync", "period", resync)
for {
select {
case <-stopCh:
glog.Info("Stopping registered resources sync")
logger.Info("stopping registered resources sync")
return
case <-ticker.C:
// set cache as stale
glog.V(6).Info("invalidating local client cache for registered resources")
logger.V(6).Info("invalidating local client cache for registered resources")
c.cachedClient.Invalidate()
}
}
@ -254,12 +258,12 @@ func (c ServerPreferredResources) Poll(resync time.Duration, stopCh <-chan struc
func (c ServerPreferredResources) GetGVRFromKind(kind string) schema.GroupVersionResource {
var gvr schema.GroupVersionResource
var err error
gvr, err = loadServerResources(kind, c.cachedClient)
gvr, err = loadServerResources(kind, c.cachedClient, c.log)
if err != nil && !c.cachedClient.Fresh() {
// invalidate cahce & re-try once more
c.cachedClient.Invalidate()
gvr, err = loadServerResources(kind, c.cachedClient)
gvr, err = loadServerResources(kind, c.cachedClient, c.log)
if err == nil {
return gvr
}
@ -272,11 +276,12 @@ func (c ServerPreferredResources) GetServerVersion() (*version.Info, error) {
return c.cachedClient.ServerVersion()
}
func loadServerResources(k string, cdi discovery.CachedDiscoveryInterface) (schema.GroupVersionResource, error) {
serverresources, err := cdi.ServerPreferredResources()
func loadServerResources(k string, cdi discovery.CachedDiscoveryInterface, log logr.Logger) (schema.GroupVersionResource, error) {
logger := log.WithName("loadServerResources")
emptyGVR := schema.GroupVersionResource{}
serverresources, err := cdi.ServerPreferredResources()
if err != nil {
glog.Error(err)
logger.Error(err, "failed to get registered preferred resources")
return emptyGVR, err
}
for _, serverresource := range serverresources {
@ -286,7 +291,7 @@ func loadServerResources(k string, cdi discovery.CachedDiscoveryInterface) (sche
if resource.Kind == k && !strings.Contains(resource.Name, "/") {
gv, err := schema.ParseGroupVersion(serverresource.GroupVersion)
if err != nil {
glog.Error(err)
logger.Error(err, "failed to parse groupVersion from schema", "groupVersion", serverresource.GroupVersion)
return emptyGVR, err
}
return gv.WithResource(resource.Name), nil

View file

@ -4,7 +4,8 @@ import (
"fmt"
"strconv"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/log"
)
//ValidationHandler for element processes
@ -12,7 +13,7 @@ type ValidationHandler interface {
Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}) (string, error)
}
type resourceElementHandler = func(resourceElement, patternElement, originPattern interface{}, path string) (string, error)
type resourceElementHandler = func(log logr.Logger, resourceElement, patternElement, originPattern interface{}, path string) (string, error)
//CreateElementHandler factory to process elements
func CreateElementHandler(element string, pattern interface{}, path string) ValidationHandler {
@ -82,7 +83,7 @@ func (eh EqualityHandler) Handle(handler resourceElementHandler, resourceMap map
// check if anchor is present in resource
if value, ok := resourceMap[anchorKey]; ok {
// validate the values of the pattern
returnPath, err := handler(value, eh.pattern, originPattern, currentPath)
returnPath, err := handler(log.Log, value, eh.pattern, originPattern, currentPath)
if err != nil {
return returnPath, err
}
@ -115,7 +116,7 @@ func (dh DefaultHandler) Handle(handler resourceElementHandler, resourceMap map[
} else if dh.pattern == "*" && resourceMap[dh.element] == nil {
return dh.path, fmt.Errorf("Validation rule failed at %s, Field %s is not present", dh.path, dh.element)
} else {
path, err := handler(resourceMap[dh.element], dh.pattern, originPattern, currentPath)
path, err := handler(log.Log, resourceMap[dh.element], dh.pattern, originPattern, currentPath)
if err != nil {
return path, err
}
@ -146,7 +147,7 @@ func (ch ConditionAnchorHandler) Handle(handler resourceElementHandler, resource
// check if anchor is present in resource
if value, ok := resourceMap[anchorKey]; ok {
// validate the values of the pattern
returnPath, err := handler(value, ch.pattern, originPattern, currentPath)
returnPath, err := handler(log.Log, value, ch.pattern, originPattern, currentPath)
if err != nil {
return returnPath, err
}
@ -194,7 +195,6 @@ func (eh ExistenceHandler) Handle(handler resourceElementHandler, resourceMap ma
}
return validateExistenceListResource(handler, typedResource, typedPatternMap, originPattern, currentPath)
default:
glog.Error("Invalid type: Existence ^ () anchor can be used only on list/array type resource")
return currentPath, fmt.Errorf("Invalid resource type %T: Existence ^ () anchor can be used only on list/array type resource", value)
}
}
@ -206,10 +206,9 @@ func validateExistenceListResource(handler resourceElementHandler, resourceList
// if non satisfy then throw an error
for i, resourceElement := range resourceList {
currentPath := path + strconv.Itoa(i) + "/"
_, err := handler(resourceElement, patternMap, originPattern, currentPath)
_, err := handler(log.Log, resourceElement, patternMap, originPattern, currentPath)
if err == nil {
// condition is satisfied, dont check further
glog.V(4).Infof("Existence check satisfied at path %s, for pattern %v", currentPath, patternMap)
return "", nil
}
}

View file

@ -6,8 +6,9 @@ import (
"sync"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/log"
)
//Interface to manage context operations
@ -33,6 +34,7 @@ type Context struct {
mu sync.RWMutex
jsonRaw []byte
whiteListVars []string
log logr.Logger
}
//NewContext returns a new context
@ -42,6 +44,7 @@ func NewContext(whiteListVars ...string) *Context {
// data: map[string]interface{}{},
jsonRaw: []byte(`{}`), // empty json struct
whiteListVars: whiteListVars,
log: log.Log.WithName("context"),
}
return &ctx
}
@ -54,7 +57,7 @@ func (ctx *Context) AddJSON(dataRaw []byte) error {
// merge json
ctx.jsonRaw, err = jsonpatch.MergePatch(ctx.jsonRaw, dataRaw)
if err != nil {
glog.V(4).Infof("failed to merge JSON data: %v", err)
ctx.log.Error(err, "failed to merge JSON data")
return err
}
return nil
@ -66,7 +69,7 @@ func (ctx *Context) AddResource(dataRaw []byte) error {
// unmarshall the resource struct
var data interface{}
if err := json.Unmarshal(dataRaw, &data); err != nil {
glog.V(4).Infof("failed to unmarshall the context data: %v", err)
ctx.log.Error(err, "failed to unmarshall the resource")
return err
}
@ -82,7 +85,7 @@ func (ctx *Context) AddResource(dataRaw []byte) error {
objRaw, err := json.Marshal(modifiedResource)
if err != nil {
glog.V(4).Infof("failed to marshall the updated context data")
ctx.log.Error(err, "failed to marshal the resource")
return err
}
return ctx.AddJSON(objRaw)
@ -98,7 +101,7 @@ func (ctx *Context) AddUserInfo(userRequestInfo kyverno.RequestInfo) error {
objRaw, err := json.Marshal(modifiedResource)
if err != nil {
glog.V(4).Infof("failed to marshall the updated context data")
ctx.log.Error(err, "failed to marshal the UserInfo")
return err
}
return ctx.AddJSON(objRaw)
@ -118,8 +121,6 @@ func (ctx *Context) AddSA(userName string) error {
// filter namespace
groups := strings.Split(sa, ":")
if len(groups) >= 2 {
glog.V(4).Infof("serviceAccount namespace: %s", groups[0])
glog.V(4).Infof("serviceAccount name: %s", groups[1])
saName = groups[1]
saNamespace = groups[0]
}
@ -131,7 +132,7 @@ func (ctx *Context) AddSA(userName string) error {
}
saNameRaw, err := json.Marshal(saNameObj)
if err != nil {
glog.V(4).Infof("failed to marshall the updated context data")
ctx.log.Error(err, "failed to marshal the SA")
return err
}
if err := ctx.AddJSON(saNameRaw); err != nil {
@ -145,7 +146,7 @@ func (ctx *Context) AddSA(userName string) error {
}
saNsRaw, err := json.Marshal(saNsObj)
if err != nil {
glog.V(4).Infof("failed to marshall the updated context data")
ctx.log.Error(err, "failed to marshal the SA namespace")
return err
}
if err := ctx.AddJSON(saNsRaw); err != nil {

View file

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"github.com/golang/glog"
jmespath "github.com/jmespath/go-jmespath"
)
@ -19,7 +18,7 @@ func (ctx *Context) Query(query string) (interface{}, error) {
// compile the query
queryPath, err := jmespath.Compile(query)
if err != nil {
glog.V(4).Infof("incorrect query %s: %v", query, err)
ctx.log.Error(err, "incorrect query", "query", query)
return emptyResult, fmt.Errorf("incorrect query %s: %v", query, err)
}
// search
@ -28,13 +27,13 @@ func (ctx *Context) Query(query string) (interface{}, error) {
var data interface{}
if err := json.Unmarshal(ctx.jsonRaw, &data); err != nil {
glog.V(4).Infof("failed to unmarshall context: %v", err)
ctx.log.Error(err, "failed to unmarshal context")
return emptyResult, fmt.Errorf("failed to unmarshall context: %v", err)
}
result, err := queryPath.Search(data)
if err != nil {
glog.V(4).Infof("failed to search query %s: %v", query, err)
ctx.log.Error(err, "failed to search query", "query", query)
return emptyResult, fmt.Errorf("failed to search query %s: %v", query, err)
}
return result, nil

View file

@ -1,11 +1,12 @@
package engine
import (
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/engine/variables"
"github.com/nirmata/kyverno/pkg/log"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -18,23 +19,23 @@ func Generate(policyContext PolicyContext) (resp response.EngineResponse) {
resource := policyContext.NewResource
admissionInfo := policyContext.AdmissionInfo
ctx := policyContext.Context
return filterRules(policy, resource, admissionInfo, ctx)
logger := log.Log.WithName("Generate").WithValues("policy", policy.Name, "kind", resource.GetKind(), "namespace", resource.GetNamespace(), "name", resource.GetName())
return filterRules(policy, resource, admissionInfo, ctx, logger)
}
func filterRule(rule kyverno.Rule, resource unstructured.Unstructured, admissionInfo kyverno.RequestInfo, ctx context.EvalInterface) *response.RuleResponse {
func filterRule(rule kyverno.Rule, resource unstructured.Unstructured, admissionInfo kyverno.RequestInfo, ctx context.EvalInterface, log logr.Logger) *response.RuleResponse {
if !rule.HasGenerate() {
return nil
}
if err := MatchesResourceDescription(resource, rule, admissionInfo); err != nil {
glog.V(4).Infof(err.Error())
return nil
}
// operate on the copy of the conditions, as we perform variable substitution
copyConditions := copyConditions(rule.Conditions)
// evaluate pre-conditions
if !variables.EvaluateConditions(ctx, copyConditions) {
glog.V(4).Infof("resource %s/%s does not satisfy the conditions for the rule ", resource.GetNamespace(), resource.GetName())
if !variables.EvaluateConditions(log, ctx, copyConditions) {
log.V(4).Info("preconditions not satisfied, skipping rule", "rule", rule.Name)
return nil
}
// build rule Response
@ -44,7 +45,7 @@ func filterRule(rule kyverno.Rule, resource unstructured.Unstructured, admission
}
}
func filterRules(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, admissionInfo kyverno.RequestInfo, ctx context.EvalInterface) response.EngineResponse {
func filterRules(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, admissionInfo kyverno.RequestInfo, ctx context.EvalInterface, log logr.Logger) response.EngineResponse {
resp := response.EngineResponse{
PolicyResponse: response.PolicyResponse{
Policy: policy.Name,
@ -57,7 +58,7 @@ func filterRules(policy kyverno.ClusterPolicy, resource unstructured.Unstructure
}
for _, rule := range policy.Spec.Rules {
if ruleResp := filterRule(rule, resource, admissionInfo, ctx); ruleResp != nil {
if ruleResp := filterRule(rule, resource, admissionInfo, ctx, log); ruleResp != nil {
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
}
}

View file

@ -8,6 +8,7 @@ import (
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/engine/anchor"
"github.com/nirmata/kyverno/pkg/engine/validate"
"github.com/nirmata/kyverno/pkg/log"
)
func meetConditions(resource, overlay interface{}) (string, overlayError) {
@ -139,7 +140,7 @@ func compareOverlay(resource, overlay interface{}, path string) (string, overlay
}
}
case string, float64, int, int64, bool, nil:
if !validate.ValidateValueWithPattern(resource, overlay) {
if !validate.ValidateValueWithPattern(log.Log, resource, overlay) {
glog.V(4).Infof("Mutate rule: failed validating value %v with overlay %v", resource, overlay)
return path, newOverlayError(conditionFailure, fmt.Sprintf("Failed validating value %v with overlay %v", resource, overlay))
}

View file

@ -5,11 +5,12 @@ import (
"strings"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/mutate"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/engine/variables"
"github.com/nirmata/kyverno/pkg/log"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -28,26 +29,25 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
policy := policyContext.Policy
resource := policyContext.NewResource
ctx := policyContext.Context
logger := log.Log.WithName("Mutate").WithValues("policy", policy.Name, "kind", resource.GetKind(), "namespace", resource.GetNamespace(), "name", resource.GetName())
logger.V(4).Info("start processing", "startTime", startTime)
startMutateResultResponse(&resp, policy, resource)
glog.V(4).Infof("started applying mutation rules of policy %q (%v)", policy.Name, startTime)
defer endMutateResultResponse(&resp, startTime)
defer endMutateResultResponse(logger, &resp, startTime)
patchedResource := policyContext.NewResource
for _, rule := range policy.Spec.Rules {
var ruleResponse response.RuleResponse
logger := logger.WithValues("rule", rule.Name)
//TODO: to be checked before calling the resources as well
if !rule.HasMutate() && !strings.Contains(PodControllers, resource.GetKind()) {
continue
}
startTime := time.Now()
glog.V(4).Infof("Time: Mutate matchAdmissionInfo %v", time.Since(startTime))
// check if the resource satisfies the filter conditions defined in the rule
//TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
// dont statisfy a policy rule resource description
if err := MatchesResourceDescription(resource, rule, policyContext.AdmissionInfo); err != nil {
glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule:\n%s", resource.GetNamespace(), resource.GetName(), err.Error())
logger.V(4).Info("resource fails the match description")
continue
}
@ -55,8 +55,8 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
copyConditions := copyConditions(rule.Conditions)
// evaluate pre-conditions
// - handle variable subsitutions
if !variables.EvaluateConditions(ctx, copyConditions) {
glog.V(4).Infof("resource %s/%s does not satisfy the conditions for the rule ", resource.GetNamespace(), resource.GetName())
if !variables.EvaluateConditions(logger, ctx, copyConditions) {
logger.V(4).Info("resource fails the preconditions")
continue
}
@ -66,7 +66,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
overlay := mutation.Overlay
// subsiitue the variables
var err error
if overlay, err = variables.SubstituteVars(ctx, overlay); err != nil {
if overlay, err = variables.SubstituteVars(logger, ctx, overlay); err != nil {
// variable subsitution failed
ruleResponse.Success = false
ruleResponse.Message = err.Error()
@ -78,11 +78,9 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
if ruleResponse.Success {
// - overlay pattern does not match the resource conditions
if ruleResponse.Patches == nil {
glog.V(4).Infof(ruleResponse.Message)
continue
}
glog.V(4).Infof("Mutate overlay in rule '%s' successfully applied on %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
logger.V(4).Info("overlay applied succesfully")
}
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
@ -93,7 +91,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
if rule.Mutation.Patches != nil {
var ruleResponse response.RuleResponse
ruleResponse, patchedResource = mutate.ProcessPatches(rule, patchedResource)
glog.Infof("Mutate patches in rule '%s' successfully applied on %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
logger.V(4).Info("patches applied successfully")
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
incrementAppliedRuleCount(&resp)
}
@ -108,12 +106,12 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
var ruleResponse response.RuleResponse
ruleResponse, patchedResource = mutate.ProcessOverlay(rule.Name, podTemplateRule, patchedResource)
if !ruleResponse.Success {
glog.Errorf("Failed to insert annotation to podTemplate of %s/%s/%s: %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), ruleResponse.Message)
logger.Info("failed to insert annotation for podTemplate", "error", ruleResponse.Message)
continue
}
if ruleResponse.Success && ruleResponse.Patches != nil {
glog.V(2).Infof("Inserted annotation to podTemplate of %s/%s/%s: %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), ruleResponse.Message)
logger.V(2).Info("inserted annotation for podTemplate")
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
}
}
@ -137,10 +135,9 @@ func startMutateResultResponse(resp *response.EngineResponse, policy kyverno.Clu
// TODO(shuting): set response with mutationFailureAction
}
func endMutateResultResponse(resp *response.EngineResponse, startTime time.Time) {
func endMutateResultResponse(logger logr.Logger, resp *response.EngineResponse, startTime time.Time) {
resp.PolicyResponse.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("finished applying mutation rules policy %v (%v)", resp.PolicyResponse.Policy, resp.PolicyResponse.ProcessingTime)
glog.V(4).Infof("Mutation Rules appplied count %v for policy %q", resp.PolicyResponse.RulesAppliedCount, resp.PolicyResponse.Policy)
logger.V(4).Info("finshed processing", "processingTime", resp.PolicyResponse.ProcessingTime, "mutationRulesApplied", resp.PolicyResponse.RulesAppliedCount)
}
// podTemplateRule mutate pod template with annotation

View file

@ -1,6 +1,7 @@
package engine
import (
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/engine/context"
@ -20,4 +21,6 @@ type PolicyContext struct {
Client *client.Client
// Contexts to store resources
Context context.EvalInterface
// log
log logr.Logger
}

View file

@ -6,12 +6,11 @@ import (
"reflect"
"time"
"github.com/nirmata/kyverno/pkg/log"
"github.com/nirmata/kyverno/pkg/utils"
authenticationv1 "k8s.io/api/authentication/v1"
rbacv1 "k8s.io/api/rbac/v1"
"github.com/golang/glog"
"github.com/minio/minio/pkg/wildcard"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -53,7 +52,7 @@ func checkNameSpace(namespaces []string, resourceNameSpace string) bool {
func checkSelector(labelSelector *metav1.LabelSelector, resourceLabels map[string]string) (bool, error) {
selector, err := metav1.LabelSelectorAsSelector(labelSelector)
if err != nil {
glog.Error(err)
log.Log.Error(err, "failed to build label selector")
return false, err
}

View file

@ -1,12 +1,13 @@
package validate
import (
"fmt"
"math"
"regexp"
"strconv"
"strings"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/minio/minio/pkg/wildcard"
"github.com/nirmata/kyverno/pkg/engine/operator"
apiresource "k8s.io/apimachinery/pkg/api/resource"
@ -21,52 +22,52 @@ const (
)
// ValidateValueWithPattern validates value with operators and wildcards
func ValidateValueWithPattern(value, pattern interface{}) bool {
func ValidateValueWithPattern(log logr.Logger, value, pattern interface{}) bool {
switch typedPattern := pattern.(type) {
case bool:
typedValue, ok := value.(bool)
if !ok {
glog.V(4).Infof("Expected bool, found %T", value)
log.V(4).Info("Expected type bool", "type", fmt.Sprintf("%T", value), "value", value)
return false
}
return typedPattern == typedValue
case int:
return validateValueWithIntPattern(value, int64(typedPattern))
return validateValueWithIntPattern(log, value, int64(typedPattern))
case int64:
return validateValueWithIntPattern(value, typedPattern)
return validateValueWithIntPattern(log, value, typedPattern)
case float64:
return validateValueWithFloatPattern(value, typedPattern)
return validateValueWithFloatPattern(log, value, typedPattern)
case string:
return validateValueWithStringPatterns(value, typedPattern)
return validateValueWithStringPatterns(log, value, typedPattern)
case nil:
return validateValueWithNilPattern(value)
return validateValueWithNilPattern(log, value)
case map[string]interface{}:
// TODO: check if this is ever called?
return validateValueWithMapPattern(value, typedPattern)
return validateValueWithMapPattern(log, value, typedPattern)
case []interface{}:
// TODO: check if this is ever called?
glog.Warning("Arrays as patterns are not supported")
log.Info("arrays as patterns is not supported")
return false
default:
glog.Warningf("Unknown type as pattern: %v", typedPattern)
log.Info("Unkown type", "type", fmt.Sprintf("%T", typedPattern), "value", typedPattern)
return false
}
}
func validateValueWithMapPattern(value interface{}, typedPattern map[string]interface{}) bool {
func validateValueWithMapPattern(log logr.Logger, value interface{}, typedPattern map[string]interface{}) bool {
// verify the type of the resource value is map[string]interface,
// we only check for existence of object, not the equality of content and value
//TODO: check if adding
_, ok := value.(map[string]interface{})
if !ok {
glog.Warningf("Expected map[string]interface{}, found %T\n", value)
log.Info("Expected type map[string]interface{}", "type", fmt.Sprintf("%T", value), "value", value)
return false
}
return true
}
// Handler for int values during validation process
func validateValueWithIntPattern(value interface{}, pattern int64) bool {
func validateValueWithIntPattern(log logr.Logger, value interface{}, pattern int64) bool {
switch typedValue := value.(type) {
case int:
return int64(typedValue) == pattern
@ -78,38 +79,38 @@ func validateValueWithIntPattern(value interface{}, pattern int64) bool {
return int64(typedValue) == pattern
}
glog.Warningf("Expected int, found float: %f\n", typedValue)
log.Info("Expected type int", "type", fmt.Sprintf("%T", typedValue), "value", typedValue)
return false
case string:
// extract int64 from string
int64Num, err := strconv.ParseInt(typedValue, 10, 64)
if err != nil {
glog.Warningf("Failed to parse int64 from string: %v", err)
log.Error(err, "Failed to parse int64 from string")
return false
}
return int64Num == pattern
default:
glog.Warningf("Expected int, found: %T\n", value)
log.Info("Expected type int", "type", fmt.Sprintf("%T", value), "value", value)
return false
}
}
// Handler for float values during validation process
func validateValueWithFloatPattern(value interface{}, pattern float64) bool {
func validateValueWithFloatPattern(log logr.Logger, value interface{}, pattern float64) bool {
switch typedValue := value.(type) {
case int:
// check that float has no fraction
if pattern == math.Trunc(pattern) {
return int(pattern) == value
}
glog.Warningf("Expected float, found int: %d\n", typedValue)
log.Info("Expected type float", "type", fmt.Sprintf("%T", typedValue), "value", typedValue)
return false
case int64:
// check that float has no fraction
if pattern == math.Trunc(pattern) {
return int64(pattern) == value
}
glog.Warningf("Expected float, found int: %d\n", typedValue)
log.Info("Expected type float", "type", fmt.Sprintf("%T", typedValue), "value", typedValue)
return false
case float64:
return typedValue == pattern
@ -117,18 +118,18 @@ func validateValueWithFloatPattern(value interface{}, pattern float64) bool {
// extract float64 from string
float64Num, err := strconv.ParseFloat(typedValue, 64)
if err != nil {
glog.Warningf("Failed to parse float64 from string: %v", err)
log.Error(err, "Failed to parse float64 from string")
return false
}
return float64Num == pattern
default:
glog.Warningf("Expected float, found: %T\n", value)
log.Info("Expected type float", "type", fmt.Sprintf("%T", value), "value", value)
return false
}
}
// Handler for nil values during validation process
func validateValueWithNilPattern(value interface{}) bool {
func validateValueWithNilPattern(log logr.Logger, value interface{}) bool {
switch typed := value.(type) {
case float64:
return typed == 0.0
@ -143,20 +144,20 @@ func validateValueWithNilPattern(value interface{}) bool {
case nil:
return true
case map[string]interface{}, []interface{}:
glog.Warningf("Maps and arrays could not be checked with nil pattern")
log.Info("Maps and arrays could not be checked with nil pattern")
return false
default:
glog.Warningf("Unknown type as value when checking for nil pattern: %T\n", value)
log.Info("Unknown type as value when checking for nil pattern", "type", fmt.Sprintf("%T", value), "value", value)
return false
}
}
// Handler for pattern values during validation process
func validateValueWithStringPatterns(value interface{}, pattern string) bool {
func validateValueWithStringPatterns(log logr.Logger, value interface{}, pattern string) bool {
statements := strings.Split(pattern, "|")
for _, statement := range statements {
statement = strings.Trim(statement, " ")
if validateValueWithStringPattern(value, statement) {
if validateValueWithStringPattern(log, value, statement) {
return true
}
}
@ -166,24 +167,24 @@ func validateValueWithStringPatterns(value interface{}, pattern string) bool {
// Handler for single pattern value during validation process
// Detects if pattern has a number
func validateValueWithStringPattern(value interface{}, pattern string) bool {
func validateValueWithStringPattern(log logr.Logger, value interface{}, pattern string) bool {
operator := operator.GetOperatorFromStringPattern(pattern)
pattern = pattern[len(operator):]
number, str := getNumberAndStringPartsFromPattern(pattern)
if "" == number {
return validateString(value, str, operator)
return validateString(log, value, str, operator)
}
return validateNumberWithStr(value, pattern, operator)
return validateNumberWithStr(log, value, pattern, operator)
}
// Handler for string values
func validateString(value interface{}, pattern string, operatorVariable operator.Operator) bool {
func validateString(log logr.Logger, value interface{}, pattern string, operatorVariable operator.Operator) bool {
if operator.NotEqual == operatorVariable || operator.Equal == operatorVariable {
strValue, ok := value.(string)
if !ok {
glog.Warningf("Expected string, found %T\n", value)
log.Info("Expected type string", "type", fmt.Sprintf("%T", value), "value", value)
return false
}
@ -195,17 +196,16 @@ func validateString(value interface{}, pattern string, operatorVariable operator
return wildcardResult
}
glog.Warningf("Operators >, >=, <, <= are not applicable to strings")
log.Info("Operators >, >=, <, <= are not applicable to strings")
return false
}
// validateNumberWithStr compares quantity if pattern type is quantity
// or a wildcard match to pattern string
func validateNumberWithStr(value interface{}, pattern string, operator operator.Operator) bool {
func validateNumberWithStr(log logr.Logger, value interface{}, pattern string, operator operator.Operator) bool {
typedValue, err := convertToString(value)
if err != nil {
glog.Warning(err)
log.Error(err, "failed to convert to string")
return false
}
@ -214,7 +214,7 @@ func validateNumberWithStr(value interface{}, pattern string, operator operator.
if err == nil {
valueQuan, err := apiresource.ParseQuantity(typedValue)
if err != nil {
glog.Warningf("Invalid quantity in resource %s, err: %v\n", typedValue, err)
log.Error(err, "invalid quantity in resource", "type", fmt.Sprintf("%T", typedValue), "value", typedValue)
return false
}
@ -223,7 +223,7 @@ func validateNumberWithStr(value interface{}, pattern string, operator operator.
// 2. wildcard match
if !wildcard.Match(pattern, typedValue) {
glog.Warningf("Value '%s' has not passed wildcard check: %s", typedValue, pattern)
log.Info("value failed wildcard check", "type", fmt.Sprintf("%T", typedValue), "value", typedValue, "check", pattern)
return false
}
return true

View file

@ -5,14 +5,15 @@ import (
"testing"
"github.com/nirmata/kyverno/pkg/engine/operator"
"github.com/nirmata/kyverno/pkg/log"
"gotest.tools/assert"
)
func TestValidateValueWithPattern_Bool(t *testing.T) {
assert.Assert(t, ValidateValueWithPattern(true, true))
assert.Assert(t, !ValidateValueWithPattern(true, false))
assert.Assert(t, !ValidateValueWithPattern(false, true))
assert.Assert(t, ValidateValueWithPattern(false, false))
assert.Assert(t, ValidateValueWithPattern(log.Log, true, true))
assert.Assert(t, !ValidateValueWithPattern(log.Log, true, false))
assert.Assert(t, !ValidateValueWithPattern(log.Log, false, true))
assert.Assert(t, ValidateValueWithPattern(log.Log, false, false))
}
func TestValidateString_AsteriskTest(t *testing.T) {
@ -20,8 +21,8 @@ func TestValidateString_AsteriskTest(t *testing.T) {
value := "anything"
empty := ""
assert.Assert(t, validateString(value, pattern, operator.Equal))
assert.Assert(t, validateString(empty, pattern, operator.Equal))
assert.Assert(t, validateString(log.Log, value, pattern, operator.Equal))
assert.Assert(t, validateString(log.Log, empty, pattern, operator.Equal))
}
func TestValidateString_LeftAsteriskTest(t *testing.T) {
@ -29,32 +30,32 @@ func TestValidateString_LeftAsteriskTest(t *testing.T) {
value := "leftright"
right := "right"
assert.Assert(t, validateString(value, pattern, operator.Equal))
assert.Assert(t, validateString(right, pattern, operator.Equal))
assert.Assert(t, validateString(log.Log, value, pattern, operator.Equal))
assert.Assert(t, validateString(log.Log, right, pattern, operator.Equal))
value = "leftmiddle"
middle := "middle"
assert.Assert(t, !validateString(value, pattern, operator.Equal))
assert.Assert(t, !validateString(middle, pattern, operator.Equal))
assert.Assert(t, !validateString(log.Log, value, pattern, operator.Equal))
assert.Assert(t, !validateString(log.Log, middle, pattern, operator.Equal))
}
func TestValidateString_MiddleAsteriskTest(t *testing.T) {
pattern := "ab*ba"
value := "abbeba"
assert.Assert(t, validateString(value, pattern, operator.Equal))
assert.Assert(t, validateString(log.Log, value, pattern, operator.Equal))
value = "abbca"
assert.Assert(t, !validateString(value, pattern, operator.Equal))
assert.Assert(t, !validateString(log.Log, value, pattern, operator.Equal))
}
func TestValidateString_QuestionMark(t *testing.T) {
pattern := "ab?ba"
value := "abbba"
assert.Assert(t, validateString(value, pattern, operator.Equal))
assert.Assert(t, validateString(log.Log, value, pattern, operator.Equal))
value = "abbbba"
assert.Assert(t, !validateString(value, pattern, operator.Equal))
assert.Assert(t, !validateString(log.Log, value, pattern, operator.Equal))
}
func TestValidateValueWithPattern_BoolInJson(t *testing.T) {
@ -76,7 +77,7 @@ func TestValidateValueWithPattern_BoolInJson(t *testing.T) {
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"]))
assert.Assert(t, ValidateValueWithPattern(log.Log, value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_NullPatternStringValue(t *testing.T) {
@ -98,7 +99,7 @@ func TestValidateValueWithPattern_NullPatternStringValue(t *testing.T) {
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, !ValidateValueWithPattern(value["key"], pattern["key"]))
assert.Assert(t, !ValidateValueWithPattern(log.Log, value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_NullPatternDefaultString(t *testing.T) {
@ -120,7 +121,7 @@ func TestValidateValueWithPattern_NullPatternDefaultString(t *testing.T) {
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"]))
assert.Assert(t, ValidateValueWithPattern(log.Log, value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_NullPatternDefaultFloat(t *testing.T) {
@ -142,7 +143,7 @@ func TestValidateValueWithPattern_NullPatternDefaultFloat(t *testing.T) {
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"]))
assert.Assert(t, ValidateValueWithPattern(log.Log, value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_NullPatternDefaultInt(t *testing.T) {
@ -164,7 +165,7 @@ func TestValidateValueWithPattern_NullPatternDefaultInt(t *testing.T) {
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"]))
assert.Assert(t, ValidateValueWithPattern(log.Log, value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_NullPatternDefaultBool(t *testing.T) {
@ -186,77 +187,77 @@ func TestValidateValueWithPattern_NullPatternDefaultBool(t *testing.T) {
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"]))
assert.Assert(t, ValidateValueWithPattern(log.Log, value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_StringsLogicalOr(t *testing.T) {
pattern := "192.168.88.1 | 10.100.11.*"
value := "10.100.11.54"
assert.Assert(t, ValidateValueWithPattern(value, pattern))
assert.Assert(t, ValidateValueWithPattern(log.Log, value, pattern))
}
func TestValidateValueWithPattern_EqualTwoFloats(t *testing.T) {
assert.Assert(t, ValidateValueWithPattern(7.0, 7.000))
assert.Assert(t, ValidateValueWithPattern(log.Log, 7.0, 7.000))
}
func TestValidateValueWithNilPattern_NullPatternStringValue(t *testing.T) {
assert.Assert(t, !validateValueWithNilPattern("value"))
assert.Assert(t, !validateValueWithNilPattern(log.Log, "value"))
}
func TestValidateValueWithNilPattern_NullPatternDefaultString(t *testing.T) {
assert.Assert(t, validateValueWithNilPattern(""))
assert.Assert(t, validateValueWithNilPattern(log.Log, ""))
}
func TestValidateValueWithNilPattern_NullPatternDefaultFloat(t *testing.T) {
assert.Assert(t, validateValueWithNilPattern(0.0))
assert.Assert(t, validateValueWithNilPattern(log.Log, 0.0))
}
func TestValidateValueWithNilPattern_NullPatternFloat(t *testing.T) {
assert.Assert(t, !validateValueWithNilPattern(0.1))
assert.Assert(t, !validateValueWithNilPattern(log.Log, 0.1))
}
func TestValidateValueWithNilPattern_NullPatternDefaultInt(t *testing.T) {
assert.Assert(t, validateValueWithNilPattern(0))
assert.Assert(t, validateValueWithNilPattern(log.Log, 0))
}
func TestValidateValueWithNilPattern_NullPatternInt(t *testing.T) {
assert.Assert(t, !validateValueWithNilPattern(1))
assert.Assert(t, !validateValueWithNilPattern(log.Log, 1))
}
func TestValidateValueWithNilPattern_NullPatternDefaultBool(t *testing.T) {
assert.Assert(t, validateValueWithNilPattern(false))
assert.Assert(t, validateValueWithNilPattern(log.Log, false))
}
func TestValidateValueWithNilPattern_NullPatternTrueBool(t *testing.T) {
assert.Assert(t, !validateValueWithNilPattern(true))
assert.Assert(t, !validateValueWithNilPattern(log.Log, true))
}
func TestValidateValueWithFloatPattern_FloatValue(t *testing.T) {
assert.Assert(t, validateValueWithFloatPattern(7.9914, 7.9914))
assert.Assert(t, validateValueWithFloatPattern(log.Log, 7.9914, 7.9914))
}
func TestValidateValueWithFloatPattern_FloatValueNotPass(t *testing.T) {
assert.Assert(t, !validateValueWithFloatPattern(7.9914, 7.99141))
assert.Assert(t, !validateValueWithFloatPattern(log.Log, 7.9914, 7.99141))
}
func TestValidateValueWithFloatPattern_FloatPatternWithoutFractionIntValue(t *testing.T) {
assert.Assert(t, validateValueWithFloatPattern(7, 7.000000))
assert.Assert(t, validateValueWithFloatPattern(log.Log, 7, 7.000000))
}
func TestValidateValueWithFloatPattern_FloatPatternWithoutFraction(t *testing.T) {
assert.Assert(t, validateValueWithFloatPattern(7.000000, 7.000000))
assert.Assert(t, validateValueWithFloatPattern(log.Log, 7.000000, 7.000000))
}
func TestValidateValueWithIntPattern_FloatValueWithoutFraction(t *testing.T) {
assert.Assert(t, validateValueWithFloatPattern(7.000000, 7))
assert.Assert(t, validateValueWithFloatPattern(log.Log, 7.000000, 7))
}
func TestValidateValueWithIntPattern_FloatValueWitFraction(t *testing.T) {
assert.Assert(t, !validateValueWithFloatPattern(7.000001, 7))
assert.Assert(t, !validateValueWithFloatPattern(log.Log, 7.000001, 7))
}
func TestValidateValueWithIntPattern_NotPass(t *testing.T) {
assert.Assert(t, !validateValueWithFloatPattern(8, 7))
assert.Assert(t, !validateValueWithFloatPattern(log.Log, 8, 7))
}
func TestGetNumberAndStringPartsFromPattern_NumberAndString(t *testing.T) {
@ -290,35 +291,35 @@ func TestGetNumberAndStringPartsFromPattern_Empty(t *testing.T) {
}
func TestValidateNumberWithStr_LessFloatAndInt(t *testing.T) {
assert.Assert(t, validateNumberWithStr(7.00001, "7.000001", operator.More))
assert.Assert(t, validateNumberWithStr(7.00001, "7", operator.NotEqual))
assert.Assert(t, validateNumberWithStr(log.Log, 7.00001, "7.000001", operator.More))
assert.Assert(t, validateNumberWithStr(log.Log, 7.00001, "7", operator.NotEqual))
assert.Assert(t, validateNumberWithStr(7.0000, "7", operator.Equal))
assert.Assert(t, !validateNumberWithStr(6.000000001, "6", operator.Less))
assert.Assert(t, validateNumberWithStr(log.Log, 7.0000, "7", operator.Equal))
assert.Assert(t, !validateNumberWithStr(log.Log, 6.000000001, "6", operator.Less))
}
func TestValidateQuantity_InvalidQuantity(t *testing.T) {
assert.Assert(t, !validateNumberWithStr("1024Gi", "", operator.Equal))
assert.Assert(t, !validateNumberWithStr("gii", "1024Gi", operator.Equal))
assert.Assert(t, !validateNumberWithStr(log.Log, "1024Gi", "", operator.Equal))
assert.Assert(t, !validateNumberWithStr(log.Log, "gii", "1024Gi", operator.Equal))
}
func TestValidateQuantity_Equal(t *testing.T) {
assert.Assert(t, validateNumberWithStr("1024Gi", "1024Gi", operator.Equal))
assert.Assert(t, validateNumberWithStr("1024Mi", "1Gi", operator.Equal))
assert.Assert(t, validateNumberWithStr("0.2", "200m", operator.Equal))
assert.Assert(t, validateNumberWithStr("500", "500", operator.Equal))
assert.Assert(t, !validateNumberWithStr("2048", "1024", operator.Equal))
assert.Assert(t, validateNumberWithStr(1024, "1024", operator.Equal))
assert.Assert(t, validateNumberWithStr(log.Log, "1024Gi", "1024Gi", operator.Equal))
assert.Assert(t, validateNumberWithStr(log.Log, "1024Mi", "1Gi", operator.Equal))
assert.Assert(t, validateNumberWithStr(log.Log, "0.2", "200m", operator.Equal))
assert.Assert(t, validateNumberWithStr(log.Log, "500", "500", operator.Equal))
assert.Assert(t, !validateNumberWithStr(log.Log, "2048", "1024", operator.Equal))
assert.Assert(t, validateNumberWithStr(log.Log, 1024, "1024", operator.Equal))
}
func TestValidateQuantity_Operation(t *testing.T) {
assert.Assert(t, validateNumberWithStr("1Gi", "1000Mi", operator.More))
assert.Assert(t, validateNumberWithStr("1G", "1Gi", operator.Less))
assert.Assert(t, validateNumberWithStr("500m", "0.5", operator.MoreEqual))
assert.Assert(t, validateNumberWithStr("1", "500m", operator.MoreEqual))
assert.Assert(t, validateNumberWithStr("0.5", ".5", operator.LessEqual))
assert.Assert(t, validateNumberWithStr("0.2", ".5", operator.LessEqual))
assert.Assert(t, validateNumberWithStr("0.2", ".5", operator.NotEqual))
assert.Assert(t, validateNumberWithStr(log.Log, "1Gi", "1000Mi", operator.More))
assert.Assert(t, validateNumberWithStr(log.Log, "1G", "1Gi", operator.Less))
assert.Assert(t, validateNumberWithStr(log.Log, "500m", "0.5", operator.MoreEqual))
assert.Assert(t, validateNumberWithStr(log.Log, "1", "500m", operator.MoreEqual))
assert.Assert(t, validateNumberWithStr(log.Log, "0.5", ".5", operator.LessEqual))
assert.Assert(t, validateNumberWithStr(log.Log, "0.2", ".5", operator.LessEqual))
assert.Assert(t, validateNumberWithStr(log.Log, "0.2", ".5", operator.NotEqual))
}
func TestGetOperatorFromStringPattern_OneChar(t *testing.T) {

View file

@ -8,15 +8,15 @@ import (
"strconv"
"strings"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/engine/anchor"
"github.com/nirmata/kyverno/pkg/engine/operator"
)
// ValidateResourceWithPattern is a start of element-by-element validation process
// It assumes that validation is started from root, so "/" is passed
func ValidateResourceWithPattern(resource, pattern interface{}) (string, error) {
path, err := validateResourceElement(resource, pattern, pattern, "/")
func ValidateResourceWithPattern(log logr.Logger, resource, pattern interface{}) (string, error) {
path, err := validateResourceElement(log, resource, pattern, pattern, "/")
if err != nil {
return path, err
}
@ -27,44 +27,44 @@ func ValidateResourceWithPattern(resource, pattern interface{}) (string, error)
// validateResourceElement detects the element type (map, array, nil, string, int, bool, float)
// and calls corresponding handler
// Pattern tree and resource tree can have different structure. In this case validation fails
func validateResourceElement(resourceElement, patternElement, originPattern interface{}, path string) (string, error) {
func validateResourceElement(log logr.Logger, resourceElement, patternElement, originPattern interface{}, path string) (string, error) {
var err error
switch typedPatternElement := patternElement.(type) {
// map
case map[string]interface{}:
typedResourceElement, ok := resourceElement.(map[string]interface{})
if !ok {
glog.V(4).Infof("Pattern and resource have different structures. Path: %s. Expected %T, found %T", path, patternElement, resourceElement)
log.V(4).Info("Pattern and resource have different structures.", "path", path, "expected", fmt.Sprintf("%T", patternElement), "current", fmt.Sprintf("%T", resourceElement))
return path, fmt.Errorf("Pattern and resource have different structures. Path: %s. Expected %T, found %T", path, patternElement, resourceElement)
}
return validateMap(typedResourceElement, typedPatternElement, originPattern, path)
return validateMap(log, typedResourceElement, typedPatternElement, originPattern, path)
// array
case []interface{}:
typedResourceElement, ok := resourceElement.([]interface{})
if !ok {
glog.V(4).Infof("Pattern and resource have different structures. Path: %s. Expected %T, found %T", path, patternElement, resourceElement)
log.V(4).Info("Pattern and resource have different structures.", "path", path, "expected", fmt.Sprintf("%T", patternElement), "current", fmt.Sprintf("%T", resourceElement))
return path, fmt.Errorf("Validation rule Failed at path %s, resource does not satisfy the expected overlay pattern", path)
}
return validateArray(typedResourceElement, typedPatternElement, originPattern, path)
return validateArray(log, typedResourceElement, typedPatternElement, originPattern, path)
// elementary values
case string, float64, int, int64, bool, nil:
/*Analyze pattern */
if checkedPattern := reflect.ValueOf(patternElement); checkedPattern.Kind() == reflect.String {
if isStringIsReference(checkedPattern.String()) { //check for $ anchor
patternElement, err = actualizePattern(originPattern, checkedPattern.String(), path)
patternElement, err = actualizePattern(log, originPattern, checkedPattern.String(), path)
if err != nil {
return path, err
}
}
}
if !ValidateValueWithPattern(resourceElement, patternElement) {
if !ValidateValueWithPattern(log, resourceElement, patternElement) {
return path, fmt.Errorf("Validation rule failed at '%s' to validate value '%v' with pattern '%v'", path, resourceElement, patternElement)
}
default:
glog.V(4).Infof("Pattern contains unknown type %T. Path: %s", patternElement, path)
log.V(4).Info("Pattern contains unknown type", "path", path, "current", fmt.Sprintf("%T", patternElement))
return path, fmt.Errorf("Validation rule failed at '%s', pattern contains unknown type", path)
}
return "", nil
@ -72,7 +72,7 @@ func validateResourceElement(resourceElement, patternElement, originPattern inte
// If validateResourceElement detects map element inside resource and pattern trees, it goes to validateMap
// For each element of the map we must detect the type again, so we pass these elements to validateResourceElement
func validateMap(resourceMap, patternMap map[string]interface{}, origPattern interface{}, path string) (string, error) {
func validateMap(log logr.Logger, resourceMap, patternMap map[string]interface{}, origPattern interface{}, path string) (string, error) {
// check if there is anchor in pattern
// Phase 1 : Evaluate all the anchors
// Phase 2 : Evaluate non-anchors
@ -91,7 +91,7 @@ func validateMap(resourceMap, patternMap map[string]interface{}, origPattern int
if err != nil {
// If Conditional anchor fails then we dont process the resources
if anchor.IsConditionAnchor(key) {
glog.V(4).Infof("condition anchor did not satisfy, wont process the resources: %s", err)
log.Error(err, "condition anchor did not satisfy, wont process the resource")
return "", nil
}
return handlerPath, err
@ -109,7 +109,7 @@ func validateMap(resourceMap, patternMap map[string]interface{}, origPattern int
return "", nil
}
func validateArray(resourceArray, patternArray []interface{}, originPattern interface{}, path string) (string, error) {
func validateArray(log logr.Logger, resourceArray, patternArray []interface{}, originPattern interface{}, path string) (string, error) {
if 0 == len(patternArray) {
return path, fmt.Errorf("Pattern Array empty")
@ -119,7 +119,7 @@ func validateArray(resourceArray, patternArray []interface{}, originPattern inte
case map[string]interface{}:
// This is special case, because maps in arrays can have anchors that must be
// processed with the special way affecting the entire array
path, err := validateArrayOfMaps(resourceArray, typedPatternElement, originPattern, path)
path, err := validateArrayOfMaps(log, resourceArray, typedPatternElement, originPattern, path)
if err != nil {
return path, err
}
@ -127,7 +127,7 @@ func validateArray(resourceArray, patternArray []interface{}, originPattern inte
// In all other cases - detect type and handle each array element with validateResourceElement
for i, patternElement := range patternArray {
currentPath := path + strconv.Itoa(i) + "/"
path, err := validateResourceElement(resourceArray[i], patternElement, originPattern, currentPath)
path, err := validateResourceElement(log, resourceArray[i], patternElement, originPattern, currentPath)
if err != nil {
return path, err
}
@ -137,7 +137,7 @@ func validateArray(resourceArray, patternArray []interface{}, originPattern inte
return "", nil
}
func actualizePattern(origPattern interface{}, referencePattern, absolutePath string) (interface{}, error) {
func actualizePattern(log logr.Logger, origPattern interface{}, referencePattern, absolutePath string) (interface{}, error) {
var foundValue interface{}
referencePattern = strings.Trim(referencePattern, "$()")
@ -155,7 +155,7 @@ func actualizePattern(origPattern interface{}, referencePattern, absolutePath st
// value :=
actualPath := formAbsolutePath(referencePattern, absolutePath)
valFromReference, err := getValueFromReference(origPattern, actualPath)
valFromReference, err := getValueFromReference(log, origPattern, actualPath)
if err != nil {
return err, nil
}
@ -196,15 +196,15 @@ func formAbsolutePath(referencePath, absolutePath string) string {
}
//Prepares original pattern, path to value, and call traverse function
func getValueFromReference(origPattern interface{}, reference string) (interface{}, error) {
func getValueFromReference(log logr.Logger, origPattern interface{}, reference string) (interface{}, error) {
originalPatternMap := origPattern.(map[string]interface{})
reference = reference[1:]
statements := strings.Split(reference, "/")
return getValueFromPattern(originalPatternMap, statements, 0)
return getValueFromPattern(log, originalPatternMap, statements, 0)
}
func getValueFromPattern(patternMap map[string]interface{}, keys []string, currentKeyIndex int) (interface{}, error) {
func getValueFromPattern(log logr.Logger, patternMap map[string]interface{}, keys []string, currentKeyIndex int) (interface{}, error) {
for key, pattern := range patternMap {
rawKey := getRawKeyIfWrappedWithAttributes(key)
@ -221,11 +221,11 @@ func getValueFromPattern(patternMap map[string]interface{}, keys []string, curre
for i, value := range typedPattern {
resourceMap, ok := value.(map[string]interface{})
if !ok {
glog.V(4).Infof("Pattern and resource have different structures. Expected %T, found %T", pattern, value)
log.V(4).Info("Pattern and resource have different structures.", "expected", fmt.Sprintf("%T", pattern), "current", fmt.Sprintf("%T", value))
return nil, fmt.Errorf("Validation rule failed, resource does not have expected pattern %v", patternMap)
}
if keys[currentKeyIndex+1] == strconv.Itoa(i) {
return getValueFromPattern(resourceMap, keys, currentKeyIndex+2)
return getValueFromPattern(log, resourceMap, keys, currentKeyIndex+2)
}
return nil, errors.New("Reference to non-existent place in the document")
}
@ -233,7 +233,7 @@ func getValueFromPattern(patternMap map[string]interface{}, keys []string, curre
return nil, errors.New("Reference to non-existent place in the document")
case map[string]interface{}:
if keys[currentKeyIndex] == rawKey {
return getValueFromPattern(typedPattern, keys, currentKeyIndex+1)
return getValueFromPattern(log, typedPattern, keys, currentKeyIndex+1)
}
return nil, errors.New("Reference to non-existent place in the document")
case string, float64, int, int64, bool, nil:
@ -251,12 +251,12 @@ func getValueFromPattern(patternMap map[string]interface{}, keys []string, curre
// validateArrayOfMaps gets anchors from pattern array map element, applies anchors logic
// and then validates each map due to the pattern
func validateArrayOfMaps(resourceMapArray []interface{}, patternMap map[string]interface{}, originPattern interface{}, path string) (string, error) {
func validateArrayOfMaps(log logr.Logger, resourceMapArray []interface{}, patternMap map[string]interface{}, originPattern interface{}, path string) (string, error) {
for i, resourceElement := range resourceMapArray {
// check the types of resource element
// expect it to be map, but can be anything ?:(
currentPath := path + strconv.Itoa(i) + "/"
returnpath, err := validateResourceElement(resourceElement, patternMap, originPattern, currentPath)
returnpath, err := validateResourceElement(log, resourceElement, patternMap, originPattern, currentPath)
if err != nil {
return returnpath, err
}

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"testing"
"github.com/nirmata/kyverno/pkg/log"
"gotest.tools/assert"
)
@ -100,7 +101,7 @@ func TestValidateMap(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateMap(resource, pattern, pattern, "/")
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
assert.NilError(t, err)
}
@ -196,7 +197,7 @@ func TestValidateMap_AsteriskForInt(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateMap(resource, pattern, pattern, "/")
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
t.Log(path)
assert.NilError(t, err)
}
@ -289,7 +290,7 @@ func TestValidateMap_AsteriskForMap(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateMap(resource, pattern, pattern, "/")
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
assert.NilError(t, err)
}
@ -377,7 +378,7 @@ func TestValidateMap_AsteriskForArray(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateMap(resource, pattern, pattern, "/")
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
assert.NilError(t, err)
}
@ -468,7 +469,7 @@ func TestValidateMap_AsteriskFieldIsMissing(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateMap(resource, pattern, pattern, "/")
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "/spec/template/spec/containers/0/")
assert.Assert(t, err != nil)
}
@ -559,7 +560,7 @@ func TestValidateMap_livenessProbeIsNull(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
json.Unmarshal(rawMap, &resource)
path, err := validateMap(resource, pattern, pattern, "/")
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
assert.NilError(t, err)
}
@ -649,7 +650,7 @@ func TestValidateMap_livenessProbeIsMissing(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateMap(resource, pattern, pattern, "/")
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
assert.NilError(t, err)
}
@ -695,7 +696,7 @@ func TestValidateMapElement_TwoElementsInArrayOnePass(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
// assert.Equal(t, path, "/1/object/0/key2/")
// assert.NilError(t, err)
@ -730,7 +731,7 @@ func TestValidateMapElement_OneElementInArrayPass(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
assert.NilError(t, err)
}
@ -784,7 +785,7 @@ func TestValidateMap_CorrectRelativePathInConfig(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
assert.NilError(t, err)
}
@ -838,7 +839,7 @@ func TestValidateMap_RelativePathDoesNotExists(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
assert.Assert(t, err != nil)
}
@ -892,7 +893,7 @@ func TestValidateMap_OnlyAnchorsInPath(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
assert.Assert(t, err != nil)
}
@ -946,7 +947,7 @@ func TestValidateMap_MalformedReferenceOnlyDolarMark(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
assert.Assert(t, err != nil)
}
@ -1000,7 +1001,7 @@ func TestValidateMap_RelativePathWithParentheses(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
assert.NilError(t, err)
}
@ -1054,7 +1055,7 @@ func TestValidateMap_MalformedPath(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
assert.Assert(t, err != nil)
}
@ -1108,7 +1109,7 @@ func TestValidateMap_AbosolutePathExists(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
assert.Assert(t, err == nil)
}
@ -1149,7 +1150,7 @@ func TestValidateMap_AbsolutePathToMetadata(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "")
assert.Assert(t, err == nil)
}
@ -1191,7 +1192,7 @@ func TestValidateMap_AbsolutePathToMetadata_fail(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "/spec/containers/0/image/")
assert.Assert(t, err != nil)
}
@ -1245,7 +1246,7 @@ func TestValidateMap_AbosolutePathDoesNotExists(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
assert.Assert(t, json.Unmarshal(rawMap, &resource))
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
assert.Assert(t, err != nil)
}
@ -1276,7 +1277,7 @@ func TestActualizePattern_GivenRelativePathThatExists(t *testing.T) {
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
pattern, err := actualizePattern(pattern, referencePath, absolutePath)
pattern, err := actualizePattern(log.Log, pattern, referencePath, absolutePath)
assert.Assert(t, err == nil)
}
@ -1347,7 +1348,7 @@ func TestValidateMapElement_OneElementInArrayNotPass(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
path, err := validateResourceElement(resource, pattern, pattern, "/")
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
assert.Equal(t, path, "/0/object/0/key2/")
assert.Assert(t, err != nil)
}

View file

@ -5,13 +5,14 @@ import (
"reflect"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/engine/utils"
"github.com/nirmata/kyverno/pkg/engine/validate"
"github.com/nirmata/kyverno/pkg/engine/variables"
"github.com/nirmata/kyverno/pkg/log"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -23,17 +24,18 @@ func Validate(policyContext PolicyContext) (resp response.EngineResponse) {
oldR := policyContext.OldResource
ctx := policyContext.Context
admissionInfo := policyContext.AdmissionInfo
logger := log.Log.WithName("Validate").WithValues("policy", policy.Name, "kind", newR.GetKind(), "namespace", newR.GetNamespace(), "name", newR.GetName())
// policy information
glog.V(4).Infof("started applying validation rules of policy %q (%v)", policy.Name, startTime)
logger.V(4).Info("start processing", "startTime", startTime)
// Process new & old resource
if reflect.DeepEqual(oldR, unstructured.Unstructured{}) {
// Create Mode
// Operate on New Resource only
resp := validateResource(ctx, policy, newR, admissionInfo)
resp := validateResource(logger, ctx, policy, newR, admissionInfo)
startResultResponse(resp, policy, newR)
defer endResultResponse(resp, startTime)
defer endResultResponse(logger, resp, startTime)
// set PatchedResource with origin resource if empty
// in order to create policy violation
if reflect.DeepEqual(resp.PatchedResource, unstructured.Unstructured{}) {
@ -44,14 +46,14 @@ func Validate(policyContext PolicyContext) (resp response.EngineResponse) {
// Update Mode
// Operate on New and Old Resource only
// New resource
oldResponse := validateResource(ctx, policy, oldR, admissionInfo)
newResponse := validateResource(ctx, policy, newR, admissionInfo)
oldResponse := validateResource(logger, ctx, policy, oldR, admissionInfo)
newResponse := validateResource(logger, ctx, policy, newR, admissionInfo)
// if the old and new response is same then return empty response
if !isSameResponse(oldResponse, newResponse) {
// there are changes send response
startResultResponse(newResponse, policy, newR)
defer endResultResponse(newResponse, startTime)
defer endResultResponse(logger, newResponse, startTime)
if reflect.DeepEqual(newResponse.PatchedResource, unstructured.Unstructured{}) {
newResponse.PatchedResource = newR
}
@ -73,10 +75,9 @@ func startResultResponse(resp *response.EngineResponse, policy kyverno.ClusterPo
resp.PolicyResponse.ValidationFailureAction = policy.Spec.ValidationFailureAction
}
func endResultResponse(resp *response.EngineResponse, startTime time.Time) {
func endResultResponse(log logr.Logger, resp *response.EngineResponse, startTime time.Time) {
resp.PolicyResponse.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("Finished applying validation rules policy %v (%v)", resp.PolicyResponse.Policy, resp.PolicyResponse.ProcessingTime)
glog.V(4).Infof("Validation Rules appplied successfully count %v for policy %q", resp.PolicyResponse.RulesAppliedCount, resp.PolicyResponse.Policy)
log.V(4).Info("finshed processing", "processingTime", resp.PolicyResponse.ProcessingTime, "validationRulesApplied", resp.PolicyResponse.RulesAppliedCount)
}
func incrementAppliedCount(resp *response.EngineResponse) {
@ -84,20 +85,18 @@ func incrementAppliedCount(resp *response.EngineResponse) {
resp.PolicyResponse.RulesAppliedCount++
}
func validateResource(ctx context.EvalInterface, policy kyverno.ClusterPolicy, resource unstructured.Unstructured, admissionInfo kyverno.RequestInfo) *response.EngineResponse {
func validateResource(log logr.Logger, ctx context.EvalInterface, policy kyverno.ClusterPolicy, resource unstructured.Unstructured, admissionInfo kyverno.RequestInfo) *response.EngineResponse {
resp := &response.EngineResponse{}
for _, rule := range policy.Spec.Rules {
if !rule.HasValidate() {
continue
}
startTime := time.Now()
glog.V(4).Infof("Time: Validate matchAdmissionInfo %v", time.Since(startTime))
// check if the resource satisfies the filter conditions defined in the rule
// TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
// dont statisfy a policy rule resource description
if err := MatchesResourceDescription(resource, rule, admissionInfo); err != nil {
glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule:\n%s", resource.GetNamespace(), resource.GetName(), err.Error())
log.V(4).Info("resource fails the match description")
continue
}
@ -105,13 +104,13 @@ func validateResource(ctx context.EvalInterface, policy kyverno.ClusterPolicy, r
copyConditions := copyConditions(rule.Conditions)
// evaluate pre-conditions
// - handle variable subsitutions
if !variables.EvaluateConditions(ctx, copyConditions) {
glog.V(4).Infof("resource %s/%s does not satisfy the conditions for the rule ", resource.GetNamespace(), resource.GetName())
if !variables.EvaluateConditions(log, ctx, copyConditions) {
log.V(4).Info("resource fails the preconditions")
continue
}
if rule.Validation.Pattern != nil || rule.Validation.AnyPattern != nil {
ruleResponse := validatePatterns(ctx, resource, rule)
ruleResponse := validatePatterns(log, ctx, resource, rule)
incrementAppliedCount(resp)
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
}
@ -159,14 +158,15 @@ func isSameRules(oldRules []response.RuleResponse, newRules []response.RuleRespo
}
// validatePatterns validate pattern and anyPattern
func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructured, rule kyverno.Rule) (resp response.RuleResponse) {
func validatePatterns(log logr.Logger, ctx context.EvalInterface, resource unstructured.Unstructured, rule kyverno.Rule) (resp response.RuleResponse) {
startTime := time.Now()
glog.V(4).Infof("started applying validation rule %q (%v)", rule.Name, startTime)
logger := log.WithValues("rule", rule.Name)
logger.V(4).Info("start processing rule", "startTime", startTime)
resp.Name = rule.Name
resp.Type = utils.Validation.String()
defer func() {
resp.RuleStats.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("finished applying validation rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)
logger.V(4).Info("finshed processing", "processingTime", resp.RuleStats.ProcessingTime)
}()
// work on a copy of validation rule
validationRule := rule.Validation.DeepCopy()
@ -176,7 +176,7 @@ func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructu
// substitute variables in the pattern
pattern := validationRule.Pattern
var err error
if pattern, err = variables.SubstituteVars(ctx, pattern); err != nil {
if pattern, err = variables.SubstituteVars(logger, ctx, pattern); err != nil {
// variable subsitution failed
resp.Success = false
resp.Message = fmt.Sprintf("Validation error: %s; Validation rule '%s' failed. '%s'",
@ -184,7 +184,7 @@ func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructu
return resp
}
if path, err := validate.ValidateResourceWithPattern(resource.Object, pattern); err != nil {
if path, err := validate.ValidateResourceWithPattern(logger, resource.Object, pattern); err != nil {
// validation failed
resp.Success = false
resp.Message = fmt.Sprintf("Validation error: %s; Validation rule '%s' failed at path '%s'",
@ -192,7 +192,7 @@ func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructu
return resp
}
// rule application successful
glog.V(4).Infof("rule %s pattern validated successfully on resource %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
logger.V(4).Info("successfully processed rule")
resp.Success = true
resp.Message = fmt.Sprintf("Validation rule '%s' succeeded.", rule.Name)
return resp
@ -203,19 +203,18 @@ func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructu
var failedAnyPatternsErrors []error
var err error
for idx, pattern := range validationRule.AnyPattern {
if pattern, err = variables.SubstituteVars(ctx, pattern); err != nil {
if pattern, err = variables.SubstituteVars(logger, ctx, pattern); err != nil {
// variable subsitution failed
failedSubstitutionsErrors = append(failedSubstitutionsErrors, err)
continue
}
_, err := validate.ValidateResourceWithPattern(resource.Object, pattern)
_, err := validate.ValidateResourceWithPattern(logger, resource.Object, pattern)
if err == nil {
resp.Success = true
resp.Message = fmt.Sprintf("Validation rule '%s' anyPattern[%d] succeeded.", rule.Name, idx)
return resp
}
glog.V(4).Infof("Validation error: %s; Validation rule %s anyPattern[%d] for %s/%s/%s",
rule.Validation.Message, rule.Name, idx, resource.GetKind(), resource.GetNamespace(), resource.GetName())
logger.V(4).Info(fmt.Sprintf("validation rule failed for anyPattern[%d]", idx), "message", rule.Validation.Message)
patternErr := fmt.Errorf("anyPattern[%d] failed; %s", idx, err)
failedAnyPatternsErrors = append(failedAnyPatternsErrors, patternErr)
}

View file

@ -1,16 +1,16 @@
package variables
import (
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/variables/operator"
)
//Evaluate evaluates the condition
func Evaluate(ctx context.EvalInterface, condition kyverno.Condition) bool {
func Evaluate(log logr.Logger, ctx context.EvalInterface, condition kyverno.Condition) bool {
// get handler for the operator
handle := operator.CreateOperatorHandler(ctx, condition.Operator, SubstituteVars)
handle := operator.CreateOperatorHandler(log, ctx, condition.Operator, SubstituteVars)
if handle == nil {
return false
}
@ -18,11 +18,10 @@ func Evaluate(ctx context.EvalInterface, condition kyverno.Condition) bool {
}
//EvaluateConditions evaluates multiple conditions
func EvaluateConditions(ctx context.EvalInterface, conditions []kyverno.Condition) bool {
func EvaluateConditions(log logr.Logger, ctx context.EvalInterface, conditions []kyverno.Condition) bool {
// AND the conditions
for _, condition := range conditions {
if !Evaluate(ctx, condition) {
glog.V(4).Infof("condition %v failed", condition)
if !Evaluate(log, ctx, condition) {
return false
}
}

View file

@ -6,6 +6,7 @@ import (
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/log"
)
// STRINGS
@ -18,7 +19,7 @@ func Test_Eval_Equal_Const_String_Pass(t *testing.T) {
Value: "name",
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -32,7 +33,7 @@ func Test_Eval_Equal_Const_String_Fail(t *testing.T) {
Value: "name1",
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -46,7 +47,7 @@ func Test_Eval_NoEqual_Const_String_Pass(t *testing.T) {
Value: "name1",
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -60,7 +61,7 @@ func Test_Eval_NoEqual_Const_String_Fail(t *testing.T) {
Value: "name",
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -76,7 +77,7 @@ func Test_Eval_Equal_Const_Bool_Pass(t *testing.T) {
Value: true,
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -90,7 +91,7 @@ func Test_Eval_Equal_Const_Bool_Fail(t *testing.T) {
Value: false,
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -104,7 +105,7 @@ func Test_Eval_NoEqual_Const_Bool_Pass(t *testing.T) {
Value: false,
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -118,7 +119,7 @@ func Test_Eval_NoEqual_Const_Bool_Fail(t *testing.T) {
Value: true,
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -133,7 +134,7 @@ func Test_Eval_Equal_Const_int_Pass(t *testing.T) {
Value: 1,
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -147,7 +148,7 @@ func Test_Eval_Equal_Const_int_Fail(t *testing.T) {
Value: 2,
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -161,7 +162,7 @@ func Test_Eval_NoEqual_Const_int_Pass(t *testing.T) {
Value: 2,
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -175,7 +176,7 @@ func Test_Eval_NoEqual_Const_int_Fail(t *testing.T) {
Value: 1,
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -190,7 +191,7 @@ func Test_Eval_Equal_Const_int64_Pass(t *testing.T) {
Value: int64(1),
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -204,7 +205,7 @@ func Test_Eval_Equal_Const_int64_Fail(t *testing.T) {
Value: int64(2),
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -218,7 +219,7 @@ func Test_Eval_NoEqual_Const_int64_Pass(t *testing.T) {
Value: int64(2),
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -232,7 +233,7 @@ func Test_Eval_NoEqual_Const_int64_Fail(t *testing.T) {
Value: int64(1),
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -248,7 +249,7 @@ func Test_Eval_Equal_Const_float64_Pass(t *testing.T) {
Value: 1.5,
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -262,7 +263,7 @@ func Test_Eval_Equal_Const_float64_Fail(t *testing.T) {
Value: 1.6,
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -276,7 +277,7 @@ func Test_Eval_NoEqual_Const_float64_Pass(t *testing.T) {
Value: 1.6,
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -290,7 +291,7 @@ func Test_Eval_NoEqual_Const_float64_Fail(t *testing.T) {
Value: 1.5,
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -320,7 +321,7 @@ func Test_Eval_Equal_Const_object_Pass(t *testing.T) {
Value: obj2,
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -348,7 +349,7 @@ func Test_Eval_Equal_Const_object_Fail(t *testing.T) {
Value: obj2,
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -376,7 +377,7 @@ func Test_Eval_NotEqual_Const_object_Pass(t *testing.T) {
Value: obj2,
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -404,7 +405,7 @@ func Test_Eval_NotEqual_Const_object_Fail(t *testing.T) {
Value: obj2,
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -434,7 +435,7 @@ func Test_Eval_Equal_Const_list_Pass(t *testing.T) {
Value: obj2,
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -460,7 +461,7 @@ func Test_Eval_Equal_Const_list_Fail(t *testing.T) {
Value: obj2,
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -486,7 +487,7 @@ func Test_Eval_NotEqual_Const_list_Pass(t *testing.T) {
Value: obj2,
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -512,7 +513,7 @@ func Test_Eval_NotEqual_Const_list_Fail(t *testing.T) {
Value: obj2,
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}
@ -545,7 +546,7 @@ func Test_Eval_Equal_Var_Pass(t *testing.T) {
Value: "temp",
}
if !Evaluate(ctx, condition) {
if !Evaluate(log.Log, ctx, condition) {
t.Error("expected to pass")
}
}
@ -576,7 +577,7 @@ func Test_Eval_Equal_Var_Fail(t *testing.T) {
Value: "temp1",
}
if Evaluate(ctx, condition) {
if Evaluate(log.Log, ctx, condition) {
t.Error("expected to fail")
}
}

View file

@ -1,19 +1,21 @@
package operator
import (
"fmt"
"math"
"reflect"
"strconv"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/engine/context"
)
//NewEqualHandler returns handler to manage Equal operations
func NewEqualHandler(ctx context.EvalInterface, subHandler VariableSubstitutionHandler) OperatorHandler {
func NewEqualHandler(log logr.Logger, ctx context.EvalInterface, subHandler VariableSubstitutionHandler) OperatorHandler {
return EqualHandler{
ctx: ctx,
subHandler: subHandler,
log: log,
}
}
@ -21,6 +23,7 @@ func NewEqualHandler(ctx context.EvalInterface, subHandler VariableSubstitutionH
type EqualHandler struct {
ctx context.EvalInterface
subHandler VariableSubstitutionHandler
log logr.Logger
}
//Evaluate evaluates expression with Equal Operator
@ -28,14 +31,14 @@ func (eh EqualHandler) Evaluate(key, value interface{}) bool {
var err error
//TODO: decouple variables from evaluation
// substitute the variables
if key, err = eh.subHandler(eh.ctx, key); err != nil {
if key, err = eh.subHandler(eh.log, eh.ctx, key); err != nil {
// Failed to resolve the variable
glog.Infof("Failed to resolve variables in key: %s: %v", key, err)
eh.log.Error(err, "Failed to resolve variable", "variable", key)
return false
}
if value, err = eh.subHandler(eh.ctx, value); err != nil {
if value, err = eh.subHandler(eh.log, eh.ctx, value); err != nil {
// Failed to resolve the variable
glog.Infof("Failed to resolve variables in value: %s: %v", value, err)
eh.log.Error(err, "Failed to resolve variable", "variable", value)
return false
}
@ -56,7 +59,7 @@ func (eh EqualHandler) Evaluate(key, value interface{}) bool {
case []interface{}:
return eh.validateValueWithSlicePattern(typedKey, value)
default:
glog.Errorf("Unsupported type %v", typedKey)
eh.log.Info("Unsupported type", "value", typedKey, "type", fmt.Sprintf("%T", typedKey))
return false
}
}
@ -65,7 +68,7 @@ func (eh EqualHandler) validateValueWithSlicePattern(key []interface{}, value in
if val, ok := value.([]interface{}); ok {
return reflect.DeepEqual(key, val)
}
glog.Warningf("Expected []interface{}, %v is of type %T", value, value)
eh.log.Info("Expected type []interface{}", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
@ -73,7 +76,7 @@ func (eh EqualHandler) validateValueWithMapPattern(key map[string]interface{}, v
if val, ok := value.(map[string]interface{}); ok {
return reflect.DeepEqual(key, val)
}
glog.Warningf("Expected map[string]interface{}, %v is of type %T", value, value)
eh.log.Info("Expected type map[string]interface{}", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
@ -81,7 +84,8 @@ func (eh EqualHandler) validateValuewithStringPattern(key string, value interfac
if val, ok := value.(string); ok {
return key == val
}
glog.Warningf("Expected string, %v is of type %T", value, value)
eh.log.Info("Expected type string", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
@ -92,25 +96,25 @@ func (eh EqualHandler) validateValuewithFloatPattern(key float64, value interfac
if key == math.Trunc(key) {
return int(key) == typedValue
}
glog.Warningf("Expected float, found int: %d\n", typedValue)
eh.log.Info("Expected type float, found int", "typedValue", typedValue)
case int64:
// check that float has not fraction
if key == math.Trunc(key) {
return int64(key) == typedValue
}
glog.Warningf("Expected float, found int: %d\n", typedValue)
eh.log.Info("Expected type float, found int", "typedValue", typedValue)
case float64:
return typedValue == key
case string:
// extract float from string
float64Num, err := strconv.ParseFloat(typedValue, 64)
if err != nil {
glog.Warningf("Failed to parse float64 from string: %v", err)
eh.log.Error(err, "Failed to parse float64 from string")
return false
}
return float64Num == key
default:
glog.Warningf("Expected float, found: %T\n", value)
eh.log.Info("Expected type float", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
return false
@ -119,7 +123,7 @@ func (eh EqualHandler) validateValuewithFloatPattern(key float64, value interfac
func (eh EqualHandler) validateValuewithBoolPattern(key bool, value interface{}) bool {
typedValue, ok := value.(bool)
if !ok {
glog.Error("Expected bool, found %V", value)
eh.log.Info("Expected type bool", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
return key == typedValue
@ -136,18 +140,18 @@ func (eh EqualHandler) validateValuewithIntPattern(key int64, value interface{})
if typedValue == math.Trunc(typedValue) {
return int64(typedValue) == key
}
glog.Warningf("Expected int, found float: %f", typedValue)
eh.log.Info("Expected type int, found float", "value", typedValue, "type", fmt.Sprintf("%T", typedValue))
return false
case string:
// extract in64 from string
int64Num, err := strconv.ParseInt(typedValue, 10, 64)
if err != nil {
glog.Warningf("Failed to parse int64 from string: %v", err)
eh.log.Error(err, "Failed to parse int64 from string")
return false
}
return int64Num == key
default:
glog.Warningf("Expected int, %v is of type %T", value, value)
eh.log.Info("Expected type int", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
}

View file

@ -1,19 +1,21 @@
package operator
import (
"fmt"
"math"
"reflect"
"strconv"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/engine/context"
)
//NewNotEqualHandler returns handler to manage NotEqual operations
func NewNotEqualHandler(ctx context.EvalInterface, subHandler VariableSubstitutionHandler) OperatorHandler {
func NewNotEqualHandler(log logr.Logger, ctx context.EvalInterface, subHandler VariableSubstitutionHandler) OperatorHandler {
return NotEqualHandler{
ctx: ctx,
subHandler: subHandler,
log: log,
}
}
@ -21,6 +23,7 @@ func NewNotEqualHandler(ctx context.EvalInterface, subHandler VariableSubstituti
type NotEqualHandler struct {
ctx context.EvalInterface
subHandler VariableSubstitutionHandler
log logr.Logger
}
//Evaluate evaluates expression with NotEqual Operator
@ -28,14 +31,14 @@ func (neh NotEqualHandler) Evaluate(key, value interface{}) bool {
var err error
//TODO: decouple variables from evaluation
// substitute the variables
if key, err = neh.subHandler(neh.ctx, key); err != nil {
if key, err = neh.subHandler(neh.log, neh.ctx, key); err != nil {
// Failed to resolve the variable
glog.Infof("Failed to resolve variables in key: %s: %v", key, err)
neh.log.Error(err, "Failed to resolve variable", "variable", key)
return false
}
if value, err = neh.subHandler(neh.ctx, value); err != nil {
if value, err = neh.subHandler(neh.log, neh.ctx, value); err != nil {
// Failed to resolve the variable
glog.Infof("Failed to resolve variables in value: %s: %v", value, err)
neh.log.Error(err, "Failed to resolve variable", "variable", value)
return false
}
// key and value need to be of same type
@ -55,7 +58,7 @@ func (neh NotEqualHandler) Evaluate(key, value interface{}) bool {
case []interface{}:
return neh.validateValueWithSlicePattern(typedKey, value)
default:
glog.Error("Unsupported type %V", typedKey)
neh.log.Info("Unsupported type", "value", typedKey, "type", fmt.Sprintf("%T", typedKey))
return false
}
}
@ -64,7 +67,7 @@ func (neh NotEqualHandler) validateValueWithSlicePattern(key []interface{}, valu
if val, ok := value.([]interface{}); ok {
return !reflect.DeepEqual(key, val)
}
glog.Warningf("Expected []interface{}, %v is of type %T", value, value)
neh.log.Info("Expected type []interface{}", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
@ -72,7 +75,7 @@ func (neh NotEqualHandler) validateValueWithMapPattern(key map[string]interface{
if val, ok := value.(map[string]interface{}); ok {
return !reflect.DeepEqual(key, val)
}
glog.Warningf("Expected map[string]interface{}, %v is of type %T", value, value)
neh.log.Info("Expected type map[string]interface{}", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
@ -80,7 +83,7 @@ func (neh NotEqualHandler) validateValuewithStringPattern(key string, value inte
if val, ok := value.(string); ok {
return key != val
}
glog.Warningf("Expected string, %v is of type %T", value, value)
neh.log.Info("Expected type string", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
@ -91,25 +94,25 @@ func (neh NotEqualHandler) validateValuewithFloatPattern(key float64, value inte
if key == math.Trunc(key) {
return int(key) != typedValue
}
glog.Warningf("Expected float, found int: %d\n", typedValue)
neh.log.Info("Expected type float, found int", "typedValue", typedValue)
case int64:
// check that float has not fraction
if key == math.Trunc(key) {
return int64(key) != typedValue
}
glog.Warningf("Expected float, found int: %d\n", typedValue)
neh.log.Info("Expected type float, found int", "typedValue", typedValue)
case float64:
return typedValue != key
case string:
// extract float from string
float64Num, err := strconv.ParseFloat(typedValue, 64)
if err != nil {
glog.Warningf("Failed to parse float64 from string: %v", err)
neh.log.Error(err, "Failed to parse float64 from string")
return false
}
return float64Num != key
default:
glog.Warningf("Expected float, found: %T\n", value)
neh.log.Info("Expected type float", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
return false
@ -118,7 +121,7 @@ func (neh NotEqualHandler) validateValuewithFloatPattern(key float64, value inte
func (neh NotEqualHandler) validateValuewithBoolPattern(key bool, value interface{}) bool {
typedValue, ok := value.(bool)
if !ok {
glog.Error("Expected bool, found %V", value)
neh.log.Info("Expected type bool", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
return key != typedValue
@ -135,18 +138,18 @@ func (neh NotEqualHandler) validateValuewithIntPattern(key int64, value interfac
if typedValue == math.Trunc(typedValue) {
return int64(typedValue) != key
}
glog.Warningf("Expected int, found float: %f\n", typedValue)
neh.log.Info("Expected type int, found float", "value", typedValue, "type", fmt.Sprintf("%T", typedValue))
return false
case string:
// extract in64 from string
int64Num, err := strconv.ParseInt(typedValue, 10, 64)
if err != nil {
glog.Warningf("Failed to parse int64 from string: %v", err)
neh.log.Error(err, "Failed to parse int64 from string")
return false
}
return int64Num != key
default:
glog.Warningf("Expected int, %v is of type %T", value, value)
neh.log.Info("Expected type int", "value", value, "type", fmt.Sprintf("%T", value))
return false
}
}

View file

@ -1,7 +1,7 @@
package operator
import (
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
)
@ -17,17 +17,17 @@ type OperatorHandler interface {
}
//VariableSubstitutionHandler defines the handler function for variable substitution
type VariableSubstitutionHandler = func(ctx context.EvalInterface, pattern interface{}) (interface{}, error)
type VariableSubstitutionHandler = func(log logr.Logger, ctx context.EvalInterface, pattern interface{}) (interface{}, error)
//CreateOperatorHandler returns the operator handler based on the operator used in condition
func CreateOperatorHandler(ctx context.EvalInterface, op kyverno.ConditionOperator, subHandler VariableSubstitutionHandler) OperatorHandler {
func CreateOperatorHandler(log logr.Logger, ctx context.EvalInterface, op kyverno.ConditionOperator, subHandler VariableSubstitutionHandler) OperatorHandler {
switch op {
case kyverno.Equal:
return NewEqualHandler(ctx, subHandler)
return NewEqualHandler(log, ctx, subHandler)
case kyverno.NotEqual:
return NewNotEqualHandler(ctx, subHandler)
return NewNotEqualHandler(log, ctx, subHandler)
default:
glog.Errorf("unsupported operator: %s", string(op))
log.Info("operator not supported", "operator", string(op))
}
return nil
}

View file

@ -6,6 +6,7 @@ import (
"testing"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/log"
authenticationv1 "k8s.io/api/authentication/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
@ -84,7 +85,7 @@ func Test_variablesub1(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, patternCopy); err != nil {
if _, err := SubstituteVars(log.Log, ctx, patternCopy); err != nil {
t.Error(err)
}
resultRaw, err := json.Marshal(patternCopy)
@ -174,7 +175,7 @@ func Test_variablesub_multiple(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, patternCopy); err != nil {
if _, err := SubstituteVars(log.Log, ctx, patternCopy); err != nil {
t.Error(err)
}
resultRaw, err := json.Marshal(patternCopy)
@ -261,7 +262,7 @@ func Test_variablesubstitution(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, patternCopy); err != nil {
if _, err := SubstituteVars(log.Log, ctx, patternCopy); err != nil {
t.Error(err)
}
resultRaw, err := json.Marshal(patternCopy)
@ -322,7 +323,7 @@ func Test_variableSubstitutionValue(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, patternCopy); err != nil {
if _, err := SubstituteVars(log.Log, ctx, patternCopy); err != nil {
t.Error(err)
}
resultRaw, err := json.Marshal(patternCopy)
@ -380,7 +381,7 @@ func Test_variableSubstitutionValueOperatorNotEqual(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, patternCopy); err != nil {
if _, err := SubstituteVars(log.Log, ctx, patternCopy); err != nil {
t.Error(err)
}
resultRaw, err := json.Marshal(patternCopy)
@ -439,7 +440,7 @@ func Test_variableSubstitutionValueFail(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, patternCopy); err == nil {
if _, err := SubstituteVars(log.Log, ctx, patternCopy); err == nil {
t.Log("expected to fails")
t.Fail()
}
@ -497,7 +498,7 @@ func Test_variableSubstitutionObject(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, patternCopy); err != nil {
if _, err := SubstituteVars(log.Log, ctx, patternCopy); err != nil {
t.Error(err)
}
resultRaw, err := json.Marshal(patternCopy)
@ -561,7 +562,7 @@ func Test_variableSubstitutionObjectOperatorNotEqualFail(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, patternCopy); err == nil {
if _, err := SubstituteVars(log.Log, ctx, patternCopy); err == nil {
t.Error(err)
}
@ -620,7 +621,7 @@ func Test_variableSubstitutionMultipleObject(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, patternCopy); err != nil {
if _, err := SubstituteVars(log.Log, ctx, patternCopy); err != nil {
t.Error(err)
}
resultRaw, err := json.Marshal(patternCopy)

View file

@ -6,7 +6,7 @@ import (
"strconv"
"strings"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/engine/context"
)
@ -17,9 +17,9 @@ const (
//SubstituteVars replaces the variables with the values defined in the context
// - if any variable is invaid or has nil value, it is considered as a failed varable substitution
func SubstituteVars(ctx context.EvalInterface, pattern interface{}) (interface{}, error) {
func SubstituteVars(log logr.Logger, ctx context.EvalInterface, pattern interface{}) (interface{}, error) {
errs := []error{}
pattern = subVars(ctx, pattern, "", &errs)
pattern = subVars(log, ctx, pattern, "", &errs)
if len(errs) == 0 {
// no error while parsing the pattern
return pattern, nil
@ -27,40 +27,40 @@ func SubstituteVars(ctx context.EvalInterface, pattern interface{}) (interface{}
return pattern, fmt.Errorf("%v", errs)
}
func subVars(ctx context.EvalInterface, pattern interface{}, path string, errs *[]error) interface{} {
func subVars(log logr.Logger, ctx context.EvalInterface, pattern interface{}, path string, errs *[]error) interface{} {
switch typedPattern := pattern.(type) {
case map[string]interface{}:
return subMap(ctx, typedPattern, path, errs)
return subMap(log, ctx, typedPattern, path, errs)
case []interface{}:
return subArray(ctx, typedPattern, path, errs)
return subArray(log, ctx, typedPattern, path, errs)
case string:
return subValR(ctx, typedPattern, path, errs)
return subValR(log, ctx, typedPattern, path, errs)
default:
return pattern
}
}
func subMap(ctx context.EvalInterface, patternMap map[string]interface{}, path string, errs *[]error) map[string]interface{} {
func subMap(log logr.Logger, ctx context.EvalInterface, patternMap map[string]interface{}, path string, errs *[]error) map[string]interface{} {
for key, patternElement := range patternMap {
curPath := path + "/" + key
value := subVars(ctx, patternElement, curPath, errs)
value := subVars(log, ctx, patternElement, curPath, errs)
patternMap[key] = value
}
return patternMap
}
func subArray(ctx context.EvalInterface, patternList []interface{}, path string, errs *[]error) []interface{} {
func subArray(log logr.Logger, ctx context.EvalInterface, patternList []interface{}, path string, errs *[]error) []interface{} {
for idx, patternElement := range patternList {
curPath := path + "/" + strconv.Itoa(idx)
value := subVars(ctx, patternElement, curPath, errs)
value := subVars(log, ctx, patternElement, curPath, errs)
patternList[idx] = value
}
return patternList
}
// subValR resolves the variables if defined
func subValR(ctx context.EvalInterface, valuePattern string, path string, errs *[]error) interface{} {
func subValR(log logr.Logger, ctx context.EvalInterface, valuePattern string, path string, errs *[]error) interface{} {
// variable values can be scalar values(string,int, float) or they can be obects(map,slice)
// - {{variable}}
@ -73,7 +73,7 @@ func subValR(ctx context.EvalInterface, valuePattern string, path string, errs *
// since this might be a potential place for error, required better error reporting and handling
// object values are only suported for single variable substitution
if ok, retVal := processIfSingleVariable(ctx, valuePattern, path, errs); ok {
if ok, retVal := processIfSingleVariable(log, ctx, valuePattern, path, errs); ok {
return retVal
}
// var emptyInterface interface{}
@ -82,7 +82,7 @@ func subValR(ctx context.EvalInterface, valuePattern string, path string, errs *
for {
valueStr := valuePattern
if len(failedVars) != 0 {
glog.Info("some failed variables short-circuiting")
log.Info("failed to resolve variablesl short-circuiting")
break
}
// get variables at this level
@ -123,7 +123,7 @@ func subValR(ctx context.EvalInterface, valuePattern string, path string, errs *
continue
}
// if type is not scalar then consider this as a failed variable
glog.Infof("variable %s resolves to non-scalar value %v. Non-Scalar values are not supported for nested variables", k, v)
log.Info("variable resolves to non-scalar value. Non-Scalar values are not supported for nested variables", "variable", k, "value", v)
failedVars = append(failedVars, k)
}
valuePattern = newVal
@ -143,10 +143,10 @@ func subValR(ctx context.EvalInterface, valuePattern string, path string, errs *
// if the value can be evaluted return the value
// -> return value can be scalar or object type
// -> if the variable is not present in the context then add an error and dont process further
func processIfSingleVariable(ctx context.EvalInterface, valuePattern interface{}, path string, errs *[]error) (bool, interface{}) {
func processIfSingleVariable(log logr.Logger, ctx context.EvalInterface, valuePattern interface{}, path string, errs *[]error) (bool, interface{}) {
valueStr, ok := valuePattern.(string)
if !ok {
glog.Infof("failed to convert %v to string", valuePattern)
log.Info("failed to convert to string", "pattern", valuePattern)
return false, nil
}
// get variables at this level

View file

@ -5,6 +5,7 @@ import (
"testing"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/log"
"gotest.tools/assert"
)
@ -64,7 +65,7 @@ func Test_subVars_success(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, pattern); err != nil {
if _, err := SubstituteVars(log.Log, ctx, pattern); err != nil {
t.Error(err)
}
}
@ -125,7 +126,7 @@ func Test_subVars_failed(t *testing.T) {
t.Error(err)
}
if _, err := SubstituteVars(ctx, pattern); err == nil {
if _, err := SubstituteVars(log.Log, ctx, pattern); err == nil {
t.Error("error is expected")
}
}
@ -152,5 +153,5 @@ func Test_SubvarRecursive(t *testing.T) {
ctx := context.NewContext()
assert.Assert(t, ctx.AddResource(resourceRaw))
errs := []error{}
subValR(ctx, string(patternRaw), "/", &errs)
subValR(log.Log, ctx, string(patternRaw), "/", &errs)
}

View file

@ -3,7 +3,7 @@ package event
import (
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme"
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1"
@ -34,6 +34,7 @@ type Generator struct {
admissionCtrRecorder record.EventRecorder
// events generated at namespaced policy controller to process 'generate' rule
genPolicyRecorder record.EventRecorder
log logr.Logger
}
//Interface to generate event
@ -42,32 +43,33 @@ type Interface interface {
}
//NewEventGenerator to generate a new event controller
func NewEventGenerator(client *client.Client, pInformer kyvernoinformer.ClusterPolicyInformer) *Generator {
func NewEventGenerator(client *client.Client, pInformer kyvernoinformer.ClusterPolicyInformer, log logr.Logger) *Generator {
gen := Generator{
client: client,
pLister: pInformer.Lister(),
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), eventWorkQueueName),
pSynced: pInformer.Informer().HasSynced,
policyCtrRecorder: initRecorder(client, PolicyController),
admissionCtrRecorder: initRecorder(client, AdmissionController),
genPolicyRecorder: initRecorder(client, GeneratePolicyController),
policyCtrRecorder: initRecorder(client, PolicyController, log),
admissionCtrRecorder: initRecorder(client, AdmissionController, log),
genPolicyRecorder: initRecorder(client, GeneratePolicyController, log),
log: log,
}
return &gen
}
func initRecorder(client *client.Client, eventSource Source) record.EventRecorder {
func initRecorder(client *client.Client, eventSource Source, log logr.Logger) record.EventRecorder {
// Initliaze Event Broadcaster
err := scheme.AddToScheme(scheme.Scheme)
if err != nil {
glog.Error(err)
log.Error(err, "failed to add to scheme")
return nil
}
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartLogging(glog.V(4).Infof)
eventBroadcaster.StartLogging(log.Info)
eventInterface, err := client.GetEventsInterface()
if err != nil {
glog.Error(err) // TODO: add more specific error
log.Error(err, "failed to get event interface for logging")
return nil
}
eventBroadcaster.StartRecordingToSink(
@ -81,11 +83,12 @@ func initRecorder(client *client.Client, eventSource Source) record.EventRecorde
//Add queues an event for generation
func (gen *Generator) Add(infos ...Info) {
logger := gen.log
for _, info := range infos {
if info.Name == "" {
// dont create event for resources with generateName
// as the name is not generated yet
glog.V(4).Infof("received info %v, not creating an event as the resource has not been assigned a name yet", info)
logger.V(4).Info("not creating an event as the resource has not been assigned a name yet", "kind", info.Kind, "name", info.Name, "namespace", info.Namespace)
continue
}
gen.queue.Add(info)
@ -94,12 +97,14 @@ func (gen *Generator) Add(infos ...Info) {
// Run begins generator
func (gen *Generator) Run(workers int, stopCh <-chan struct{}) {
logger := gen.log
defer utilruntime.HandleCrash()
glog.Info("Starting event generator")
defer glog.Info("Shutting down event generator")
logger.Info("start")
defer logger.Info("shutting down")
if !cache.WaitForCacheSync(stopCh, gen.pSynced) {
glog.Error("event generator: failed to sync informer cache")
logger.Info("failed to sync informer cache")
}
for i := 0; i < workers; i++ {
@ -114,24 +119,25 @@ func (gen *Generator) runWorker() {
}
func (gen *Generator) handleErr(err error, key interface{}) {
logger := gen.log
if err == nil {
gen.queue.Forget(key)
return
}
// This controller retries if something goes wrong. After that, it stops trying.
if gen.queue.NumRequeues(key) < workQueueRetryLimit {
glog.Warningf("Error syncing events %v(re-queuing request, the resource might not have been created yet): %v", key, err)
logger.Error(err, "Error syncing events;re-queuing request,the resource might not have been created yet", "key", key)
// 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)
glog.Error(err)
glog.Warningf("Dropping the key out of the queue: %v", err)
logger.Error(err, "dropping the key out of queue", "key", key)
}
func (gen *Generator) processNextWorkItem() bool {
logger := gen.log
obj, shutdown := gen.queue.Get()
if shutdown {
return false
@ -144,7 +150,7 @@ func (gen *Generator) processNextWorkItem() bool {
if key, ok = obj.(Info); !ok {
gen.queue.Forget(obj)
glog.Warningf("Expecting type info by got %v\n", obj)
logger.Info("Incorrect type; expected type 'info'", "obj", obj)
return nil
}
err := gen.syncHandler(key)
@ -152,13 +158,14 @@ func (gen *Generator) processNextWorkItem() bool {
return nil
}(obj)
if err != nil {
glog.Error(err)
logger.Error(err, "failed to process next work item")
return true
}
return true
}
func (gen *Generator) syncHandler(key Info) error {
logger := gen.log
var robj runtime.Object
var err error
switch key.Kind {
@ -166,13 +173,13 @@ func (gen *Generator) syncHandler(key Info) error {
//TODO: policy is clustered resource so wont need namespace
robj, err = gen.pLister.Get(key.Name)
if err != nil {
glog.V(4).Infof("Error creating event: unable to get policy %s, will retry ", key.Name)
logger.Error(err, "failed to get policy", "name", key.Name)
return err
}
default:
robj, err = gen.client.GetResource(key.Kind, key.Namespace, key.Name)
if err != nil {
glog.V(4).Infof("Error creating event: unable to get resource %s/%s/%s, will retry ", key.Kind, key.Namespace, key.Name)
logger.Error(err, "failed to get resource", "kind", key.Kind, "name", key.Name, "namespace", key.Namespace)
return err
}
}
@ -192,13 +199,14 @@ func (gen *Generator) syncHandler(key Info) error {
case GeneratePolicyController:
gen.genPolicyRecorder.Event(robj, eventType, key.Reason, key.Message)
default:
glog.Info("info.source not defined for the event generator request")
logger.Info("info.source not defined for the request")
}
return nil
}
//NewEvent builds a event creation request
func NewEvent(
log logr.Logger,
rkind,
rapiVersion,
rnamespace,
@ -209,7 +217,7 @@ func NewEvent(
args ...interface{}) Info {
msgText, err := getEventMsg(message, args...)
if err != nil {
glog.Error(err)
log.Error(err, "failed to get event message")
}
return Info{
Kind: rkind,

View file

@ -1,11 +1,11 @@
package cleanup
import (
"fmt"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1"
@ -53,6 +53,7 @@ type Controller struct {
//TODO: list of generic informers
// only support Namespaces for deletion of resource
nsInformer informers.GenericInformer
log logr.Logger
}
//NewController returns a new controller instance to manage generate-requests
@ -62,6 +63,7 @@ func NewController(
pInformer kyvernoinformer.ClusterPolicyInformer,
grInformer kyvernoinformer.GenerateRequestInformer,
dynamicInformer dynamicinformer.DynamicSharedInformerFactory,
log logr.Logger,
) *Controller {
c := Controller{
kyvernoClient: kyvernoclient,
@ -70,6 +72,7 @@ func NewController(
// as we dont want a deleted GR to be re-queue
queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(1, 30), "generate-request-cleanup"),
dynamicInformer: dynamicInformer,
log: log,
}
c.control = Control{client: kyvernoclient}
c.enqueueGR = c.enqueue
@ -102,10 +105,11 @@ func NewController(
}
func (c *Controller) deleteGenericResource(obj interface{}) {
logger := c.log
r := obj.(*unstructured.Unstructured)
grs, err := c.grLister.GetGenerateRequestsForResource(r.GetKind(), r.GetNamespace(), r.GetName())
if err != nil {
glog.Errorf("failed to Generate Requests for resource %s/%s/%s: %v", r.GetKind(), r.GetNamespace(), r.GetName(), err)
logger.Error(err, "failed to get generate request CR for resource", "kind", r.GetKind(), "namespace", r.GetNamespace(), "name", r.GetName())
return
}
// re-evaluate the GR as the resource was deleted
@ -115,26 +119,27 @@ func (c *Controller) deleteGenericResource(obj interface{}) {
}
func (c *Controller) deletePolicy(obj interface{}) {
logger := c.log
p, ok := obj.(*kyverno.ClusterPolicy)
if !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
glog.Info(fmt.Errorf("Couldn't get object from tombstone %#v", obj))
logger.Info("ouldn't get object from tombstone", "obj", obj)
return
}
_, ok = tombstone.Obj.(*kyverno.ClusterPolicy)
if !ok {
glog.Info(fmt.Errorf("Tombstone contained object that is not a Generate Request %#v", obj))
logger.Info("Tombstone contained object that is not a Generate Request", "obj", obj)
return
}
}
glog.V(4).Infof("Deleting Policy %s", p.Name)
logger.V(4).Info("deleting policy", "name", p.Name)
// clean up the GR
// Get the corresponding GR
// get the list of GR for the current Policy version
grs, err := c.grLister.GetGenerateRequestsForClusterPolicy(p.Name)
if err != nil {
glog.Errorf("failed to Generate Requests for policy %s: %v", p.Name, err)
logger.Error(err, "failed to generate request CR for the policy", "name", p.Name)
return
}
for _, gr := range grs {
@ -153,44 +158,46 @@ func (c *Controller) updateGR(old, cur interface{}) {
}
func (c *Controller) deleteGR(obj interface{}) {
logger := c.log
gr, ok := obj.(*kyverno.GenerateRequest)
if !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
glog.Info(fmt.Errorf("Couldn't get object from tombstone %#v", obj))
logger.Info("Couldn't get object from tombstone", "obj", obj)
return
}
_, ok = tombstone.Obj.(*kyverno.GenerateRequest)
if !ok {
glog.Info(fmt.Errorf("Tombstone contained object that is not a Generate Request %#v", obj))
logger.Info("ombstone contained object that is not a Generate Request", "obj", obj)
return
}
}
glog.V(4).Infof("Deleting GR %s", gr.Name)
logger.V(4).Info("deleting Generate Request CR", "name", gr.Name)
// sync Handler will remove it from the queue
c.enqueueGR(gr)
}
func (c *Controller) enqueue(gr *kyverno.GenerateRequest) {
logger := c.log
key, err := cache.MetaNamespaceKeyFunc(gr)
if err != nil {
glog.Error(err)
return
}
glog.V(4).Infof("cleanup enqueu: %v", gr.Name)
logger.V(4).Info("eneque generate request", "name", gr.Name)
c.queue.Add(key)
}
//Run starts the generate-request re-conciliation loop
func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
logger := c.log
defer utilruntime.HandleCrash()
defer c.queue.ShutDown()
glog.Info("Starting generate-policy-cleanup controller")
defer glog.Info("Shutting down generate-policy-cleanup controller")
logger.Info("starting")
defer logger.Info("shutting down")
if !cache.WaitForCacheSync(stopCh, c.pSynced, c.grSynced) {
glog.Error("generate-policy-cleanup controller: failed to sync informer cache")
logger.Info("failed to sync informer cache")
return
}
for i := 0; i < workers; i++ {
@ -219,31 +226,33 @@ func (c *Controller) processNextWorkItem() bool {
}
func (c *Controller) handleErr(err error, key interface{}) {
logger := c.log
if err == nil {
c.queue.Forget(key)
return
}
if c.queue.NumRequeues(key) < maxRetries {
glog.Errorf("Error syncing Generate Request %v: %v", key, err)
logger.Error(err, "failed to sync generate request", "key", key)
c.queue.AddRateLimited(key)
return
}
utilruntime.HandleError(err)
glog.Infof("Dropping generate request %q out of the queue: %v", key, err)
logger.Error(err, "dropping generate request out of the queue", "key", key)
c.queue.Forget(key)
}
func (c *Controller) syncGenerateRequest(key string) error {
logger := c.log.WithValues("key", key)
var err error
startTime := time.Now()
glog.V(4).Infof("Started syncing GR %q (%v)", key, startTime)
logger.Info("started syncing generate request", "startTime", startTime)
defer func() {
glog.V(4).Infof("Finished syncing GR %q (%v)", key, time.Since(startTime))
logger.V(4).Info("finished syncying generate request", "processingTIme", time.Since(startTime))
}()
_, grName, err := cache.SplitMetaNamespaceKey(key)
if errors.IsNotFound(err) {
glog.Infof("Generate Request %s has been deleted", key)
logger.Info("generate request has been deleted")
return nil
}
if err != nil {

View file

@ -1,10 +1,9 @@
package generate
import (
"fmt"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1"
@ -57,6 +56,7 @@ type Controller struct {
//TODO: list of generic informers
// only support Namespaces for re-evalutation on resource updates
nsInformer informers.GenericInformer
log logr.Logger
}
//NewController returns an instance of the Generate-Request Controller
@ -68,6 +68,7 @@ func NewController(
eventGen event.Interface,
pvGenerator policyviolation.GeneratorInterface,
dynamicInformer dynamicinformer.DynamicSharedInformerFactory,
log logr.Logger,
) *Controller {
c := Controller{
client: client,
@ -78,6 +79,7 @@ func NewController(
// as we dont want a deleted GR to be re-queue
queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(1, 30), "generate-request"),
dynamicInformer: dynamicInformer,
log: log,
}
c.statusControl = StatusControl{client: kyvernoclient}
@ -112,11 +114,12 @@ func NewController(
}
func (c *Controller) updateGenericResource(old, cur interface{}) {
logger := c.log
curR := cur.(*unstructured.Unstructured)
grs, err := c.grLister.GetGenerateRequestsForResource(curR.GetKind(), curR.GetNamespace(), curR.GetName())
if err != nil {
glog.Errorf("failed to Generate Requests for resource %s/%s/%s: %v", curR.GetKind(), curR.GetNamespace(), curR.GetName(), err)
logger.Error(err, "failed to get generate request CR for the resoource", "kind", curR.GetKind(), "name", curR.GetName(), "namespace", curR.GetNamespace())
return
}
// re-evaluate the GR as the resource was updated
@ -129,13 +132,14 @@ func (c *Controller) updateGenericResource(old, cur interface{}) {
func (c *Controller) enqueue(gr *kyverno.GenerateRequest) {
key, err := cache.MetaNamespaceKeyFunc(gr)
if err != nil {
glog.Error(err)
c.log.Error(err, "failed to extract name")
return
}
c.queue.Add(key)
}
func (c *Controller) updatePolicy(old, cur interface{}) {
logger := c.log
oldP := old.(*kyverno.ClusterPolicy)
curP := cur.(*kyverno.ClusterPolicy)
if oldP.ResourceVersion == curP.ResourceVersion {
@ -143,11 +147,11 @@ func (c *Controller) updatePolicy(old, cur interface{}) {
// Two different versions of the same replica set will always have different RVs.
return
}
glog.V(4).Infof("Updating Policy %s", oldP.Name)
logger.V(4).Info("updating policy", "name", oldP.Name)
// get the list of GR for the current Policy version
grs, err := c.grLister.GetGenerateRequestsForClusterPolicy(curP.Name)
if err != nil {
glog.Errorf("failed to Generate Requests for policy %s: %v", curP.Name, err)
logger.Error(err, "failed to generate request for policy", "name", curP.Name)
return
}
// re-evaluate the GR as the policy was updated
@ -178,34 +182,36 @@ func (c *Controller) updateGR(old, cur interface{}) {
}
func (c *Controller) deleteGR(obj interface{}) {
logger := c.log
gr, ok := obj.(*kyverno.GenerateRequest)
if !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
glog.Info(fmt.Errorf("Couldn't get object from tombstone %#v", obj))
logger.Info("Couldn't get object from tombstone", "obj", obj)
return
}
_, ok = tombstone.Obj.(*kyverno.GenerateRequest)
if !ok {
glog.Info(fmt.Errorf("Tombstone contained object that is not a Generate Request %#v", obj))
logger.Info("tombstone contained object that is not a Generate Request CR", "obj", obj)
return
}
}
glog.V(4).Infof("Deleting GR %s", gr.Name)
logger.Info("deleting generate request", "name", gr.Name)
// sync Handler will remove it from the queue
c.enqueueGR(gr)
}
//Run ...
func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
logger := c.log
defer utilruntime.HandleCrash()
defer c.queue.ShutDown()
glog.Info("Starting generate-policy controller")
defer glog.Info("Shutting down generate-policy controller")
logger.Info("starting")
defer logger.Info("shutting down")
if !cache.WaitForCacheSync(stopCh, c.pSynced, c.grSynced) {
glog.Error("generate-policy controller: failed to sync informer cache")
logger.Info("failed to sync informer cache")
return
}
for i := 0; i < workers; i++ {
@ -234,27 +240,29 @@ func (c *Controller) processNextWorkItem() bool {
}
func (c *Controller) handleErr(err error, key interface{}) {
logger := c.log
if err == nil {
c.queue.Forget(key)
return
}
if c.queue.NumRequeues(key) < maxRetries {
glog.Errorf("Error syncing Generate Request %v: %v", key, err)
logger.Error(err, "failed to sync generate request", "key", key)
c.queue.AddRateLimited(key)
return
}
utilruntime.HandleError(err)
glog.Infof("Dropping generate request %q out of the queue: %v", key, err)
logger.Error(err, "Dropping generate request from the queue", "key", key)
c.queue.Forget(key)
}
func (c *Controller) syncGenerateRequest(key string) error {
logger := c.log
var err error
startTime := time.Now()
glog.V(4).Infof("Started syncing GR %q (%v)", key, startTime)
logger.Info("started sync", "key", key, "startTime", startTime)
defer func() {
glog.V(4).Infof("Finished syncing GR %q (%v)", key, time.Since(startTime))
logger.V(4).Info("finished sync", "key", key, "processingTime", time.Since(startTime))
}()
_, grName, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
@ -263,7 +271,7 @@ func (c *Controller) syncGenerateRequest(key string) error {
gr, err := c.grLister.Get(grName)
if err != nil {
glog.V(4).Info(err)
logger.Error(err, "failed to list generate requests")
return err
}
return c.processGR(gr)

View file

@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
dclient "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/engine"
@ -16,6 +16,7 @@ import (
)
func (c *Controller) processGR(gr *kyverno.GenerateRequest) error {
logger := c.log.WithValues("name", gr.Name, "policy", gr.Spec.Policy, "kind", gr.Spec.Resource.Kind, "namespace", gr.Spec.Resource.Namespace, "name", gr.Spec.Resource.Name)
var err error
var resource *unstructured.Unstructured
var genResources []kyverno.ResourceSpec
@ -23,45 +24,46 @@ func (c *Controller) processGR(gr *kyverno.GenerateRequest) error {
resource, err = getResource(c.client, gr.Spec.Resource)
if err != nil {
// Dont update status
glog.V(4).Infof("resource does not exist or is yet to be created, requeuing: %v", err)
logger.Error(err, "resource does not exist or is yet to be created, requeueing")
return err
}
// 2 - Apply the generate policy on the resource
genResources, err = c.applyGenerate(*resource, *gr)
// 3 - Report Events
reportEvents(err, c.eventGen, *gr, *resource)
reportEvents(logger, err, c.eventGen, *gr, *resource)
// 4 - Update Status
return updateStatus(c.statusControl, *gr, err, genResources)
}
func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyverno.GenerateRequest) ([]kyverno.ResourceSpec, error) {
logger := c.log.WithValues("name", gr.Name, "policy", gr.Spec.Policy, "kind", gr.Spec.Resource.Kind, "namespace", gr.Spec.Resource.Namespace, "name", gr.Spec.Resource.Name)
// Get the list of rules to be applied
// get policy
policy, err := c.pLister.Get(gr.Spec.Policy)
if err != nil {
glog.V(4).Infof("policy %s not found: %v", gr.Spec.Policy, err)
logger.Error(err, "policy not found")
return nil, nil
}
// build context
ctx := context.NewContext()
resourceRaw, err := resource.MarshalJSON()
if err != nil {
glog.V(4).Infof("failed to marshal resource: %v", err)
logger.Error(err, "failed to marshal resource")
return nil, err
}
err = ctx.AddResource(resourceRaw)
if err != nil {
glog.Infof("Failed to load resource in context: %v", err)
logger.Error(err, "failed to load resource in context")
return nil, err
}
err = ctx.AddUserInfo(gr.Spec.Context.UserRequestInfo)
if err != nil {
glog.Infof("Failed to load userInfo in context: %v", err)
logger.Error(err, "failed to load SA in context")
return nil, err
}
err = ctx.AddSA(gr.Spec.Context.UserRequestInfo.AdmissionUserInfo.Username)
if err != nil {
glog.Infof("Failed to load serviceAccount in context: %v", err)
logger.Error(err, "failed to load UserInfo in context")
return nil, err
}
@ -75,12 +77,12 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
// check if the policy still applies to the resource
engineResponse := engine.Generate(policyContext)
if len(engineResponse.PolicyResponse.Rules) == 0 {
glog.V(4).Infof("policy %s, dont not apply to resource %v", gr.Spec.Policy, gr.Spec.Resource)
logger.V(4).Info("policy does not apply to resource")
return nil, fmt.Errorf("policy %s, dont not apply to resource %v", gr.Spec.Policy, gr.Spec.Resource)
}
// Apply the generate rule on resource
return applyGeneratePolicy(c.client, policyContext)
return applyGeneratePolicy(logger, c.client, policyContext)
}
func updateStatus(statusControl StatusControlInterface, gr kyverno.GenerateRequest, err error, genResources []kyverno.ResourceSpec) error {
@ -92,7 +94,7 @@ func updateStatus(statusControl StatusControlInterface, gr kyverno.GenerateReque
return statusControl.Success(gr, genResources)
}
func applyGeneratePolicy(client *dclient.Client, policyContext engine.PolicyContext) ([]kyverno.ResourceSpec, error) {
func applyGeneratePolicy(log logr.Logger, client *dclient.Client, policyContext engine.PolicyContext) ([]kyverno.ResourceSpec, error) {
// List of generatedResources
var genResources []kyverno.ResourceSpec
// Get the response as the actions to be performed on the resource
@ -111,7 +113,7 @@ func applyGeneratePolicy(client *dclient.Client, policyContext engine.PolicyCont
if !rule.HasGenerate() {
continue
}
genResource, err := applyRule(client, rule, resource, ctx, processExisting)
genResource, err := applyRule(log, client, rule, resource, ctx, processExisting)
if err != nil {
return nil, err
}
@ -121,7 +123,7 @@ func applyGeneratePolicy(client *dclient.Client, policyContext engine.PolicyCont
return genResources, nil
}
func applyRule(client *dclient.Client, rule kyverno.Rule, resource unstructured.Unstructured, ctx context.EvalInterface, processExisting bool) (kyverno.ResourceSpec, error) {
func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resource unstructured.Unstructured, ctx context.EvalInterface, processExisting bool) (kyverno.ResourceSpec, error) {
var rdata map[string]interface{}
var err error
var mode ResourceMode
@ -136,7 +138,7 @@ func applyRule(client *dclient.Client, rule kyverno.Rule, resource unstructured.
// format : {{<variable_name}}
// - if there is variables that are not defined the context -> results in error and rule is not applied
// - valid variables are replaced with the values
if _, err := variables.SubstituteVars(ctx, genUnst.Object); err != nil {
if _, err := variables.SubstituteVars(log, ctx, genUnst.Object); err != nil {
return noGenResource, err
}
genKind, _, err := unstructured.NestedString(genUnst.Object, "kind")
@ -168,9 +170,9 @@ func applyRule(client *dclient.Client, rule kyverno.Rule, resource unstructured.
}
if genData != nil {
rdata, mode, err = manageData(genKind, genNamespace, genName, genData, client, resource)
rdata, mode, err = manageData(log, genKind, genNamespace, genName, genData, client, resource)
} else {
rdata, mode, err = manageClone(genKind, genNamespace, genName, genCopy, client, resource)
rdata, mode, err = manageClone(log, genKind, genNamespace, genName, genCopy, client, resource)
}
if err != nil {
return noGenResource, err
@ -197,38 +199,38 @@ func applyRule(client *dclient.Client, rule kyverno.Rule, resource unstructured.
// - app.kubernetes.io/managed-by: kyverno
// - kyverno.io/generated-by: kind/namespace/name (trigger resource)
manageLabels(newResource, resource)
logger := log.WithValues("genKind", genKind, "genNamespace", genNamespace, "genName", genName)
if mode == Create {
// Reset resource version
newResource.SetResourceVersion("")
// Create the resource
glog.V(4).Infof("Creating new resource %s/%s/%s", genKind, genNamespace, genName)
logger.V(4).Info("creating new resource")
_, err = client.CreateResource(genKind, genNamespace, newResource, false)
if err != nil {
// Failed to create resource
return noGenResource, err
}
glog.V(4).Infof("Created new resource %s/%s/%s", genKind, genNamespace, genName)
logger.V(4).Info("created new resource")
} else if mode == Update {
glog.V(4).Infof("Updating existing resource %s/%s/%s", genKind, genNamespace, genName)
logger.V(4).Info("updating existing resource")
// Update the resource
_, err := client.UpdateResource(genKind, genNamespace, newResource, false)
if err != nil {
// Failed to update resource
return noGenResource, err
}
glog.V(4).Infof("Updated existing resource %s/%s/%s", genKind, genNamespace, genName)
logger.V(4).Info("updated new resource")
}
return newGenResource, nil
}
func manageData(kind, namespace, name string, data map[string]interface{}, client *dclient.Client, resource unstructured.Unstructured) (map[string]interface{}, ResourceMode, error) {
func manageData(log logr.Logger, kind, namespace, name string, data map[string]interface{}, client *dclient.Client, resource unstructured.Unstructured) (map[string]interface{}, ResourceMode, error) {
// check if resource to be generated exists
obj, err := client.GetResource(kind, namespace, name)
if apierrors.IsNotFound(err) {
glog.V(4).Infof("Resource %s/%s/%s does not exists, will try to create", kind, namespace, name)
log.Error(err, "resource does not exist, will try to create", "genKind", kind, "genNamespace", namespace, "genName", name)
return data, Create, nil
}
if err != nil {
@ -237,18 +239,17 @@ func manageData(kind, namespace, name string, data map[string]interface{}, clien
return nil, Skip, err
}
// Resource exists; verfiy the content of the resource
err = checkResource(data, obj)
err = checkResource(log, data, obj)
if err == nil {
// Existing resource does contain the mentioned configuration in spec, skip processing the resource as it is already in expected state
return nil, Skip, nil
}
glog.V(4).Infof("Resource %s/%s/%s exists but missing required configuration, will try to update", kind, namespace, name)
log.Info("to be generated resoruce already exists, but is missing the specifeid configurations, will try to update", "genKind", kind, "genNamespace", namespace, "genName", name)
return data, Update, nil
}
func manageClone(kind, namespace, name string, clone map[string]interface{}, client *dclient.Client, resource unstructured.Unstructured) (map[string]interface{}, ResourceMode, error) {
func manageClone(log logr.Logger, kind, namespace, name string, clone map[string]interface{}, client *dclient.Client, resource unstructured.Unstructured) (map[string]interface{}, ResourceMode, error) {
// check if resource to be generated exists
_, err := client.GetResource(kind, namespace, name)
if err == nil {
@ -257,6 +258,7 @@ func manageClone(kind, namespace, name string, clone map[string]interface{}, cli
}
//TODO: check this
if !apierrors.IsNotFound(err) {
log.Error(err, "reference/clone resource is not found", "genKind", kind, "genNamespace", namespace, "genName", name)
//something wrong while fetching resource
return nil, Skip, err
}
@ -274,8 +276,6 @@ func manageClone(kind, namespace, name string, clone map[string]interface{}, cli
// attempting to clone it self, this will fail -> short-ciruit it
return nil, Skip, nil
}
glog.V(4).Infof("check if resource %s/%s/%s exists", kind, newRNs, newRName)
// check if the resource as reference in clone exists?
obj, err := client.GetResource(kind, newRNs, newRName)
if err != nil {
@ -298,10 +298,10 @@ const (
Update = "UPDATE"
)
func checkResource(newResourceSpec interface{}, resource *unstructured.Unstructured) error {
func checkResource(log logr.Logger, newResourceSpec interface{}, resource *unstructured.Unstructured) error {
// check if the resource spec if a subset of the resource
if path, err := validate.ValidateResourceWithPattern(resource.Object, newResourceSpec); err != nil {
glog.V(4).Infof("Failed to match the resource at path %s: err %v", path, err)
if path, err := validate.ValidateResourceWithPattern(log, resource.Object, newResourceSpec); err != nil {
log.Error(err, "Failed to match the resource ", "path", path)
return err
}
return nil

View file

@ -3,7 +3,7 @@ package generate
import (
"fmt"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/log"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -30,7 +30,7 @@ func managedBy(labels map[string]string) {
val, ok := labels[key]
if ok {
if val != value {
glog.Infof("resource managed by %s, kyverno wont over-ride the label", val)
log.Log.Info(fmt.Sprintf("resource managed by %s, kyverno wont over-ride the label", val))
return
}
}
@ -46,7 +46,7 @@ func generatedBy(labels map[string]string, triggerResource unstructured.Unstruct
val, ok := labels[key]
if ok {
if val != value {
glog.Infof("resource generated by %s, kyverno wont over-ride the label", val)
log.Log.Info(fmt.Sprintf("resource generated by %s, kyverno wont over-ride the label", val))
return
}
}

View file

@ -3,13 +3,13 @@ package generate
import (
"fmt"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/event"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func reportEvents(err error, eventGen event.Interface, gr kyverno.GenerateRequest, resource unstructured.Unstructured) {
func reportEvents(log logr.Logger, err error, eventGen event.Interface, gr kyverno.GenerateRequest, resource unstructured.Unstructured) {
if err == nil {
// Success Events
// - resource -> policy rule applied successfully
@ -18,7 +18,6 @@ func reportEvents(err error, eventGen event.Interface, gr kyverno.GenerateReques
eventGen.Add(events...)
return
}
glog.V(4).Infof("reporing events for %v", err)
events := failedEvents(err, gr, resource)
eventGen.Add(events...)
}

121
pkg/log/deleg.go Normal file
View file

@ -0,0 +1,121 @@
package log
import (
"sync"
"github.com/go-logr/logr"
)
// loggerPromise knows how to populate a concrete logr.Logger
// with options, given an actual base logger later on down the line.
type loggerPromise struct {
logger *DelegatingLogger
childPromises []*loggerPromise
promisesLock sync.Mutex
name *string
tags []interface{}
}
// WithName provides a new Logger with the name appended
func (p *loggerPromise) WithName(l *DelegatingLogger, name string) *loggerPromise {
res := &loggerPromise{
logger: l,
name: &name,
promisesLock: sync.Mutex{},
}
p.promisesLock.Lock()
defer p.promisesLock.Unlock()
p.childPromises = append(p.childPromises, res)
return res
}
// WithValues provides a new Logger with the tags appended
func (p *loggerPromise) WithValues(l *DelegatingLogger, tags ...interface{}) *loggerPromise {
res := &loggerPromise{
logger: l,
tags: tags,
promisesLock: sync.Mutex{},
}
p.promisesLock.Lock()
defer p.promisesLock.Unlock()
p.childPromises = append(p.childPromises, res)
return res
}
// Fulfill instantiates the Logger with the provided logger
func (p *loggerPromise) Fulfill(parentLogger logr.Logger) {
var logger = parentLogger
if p.name != nil {
logger = logger.WithName(*p.name)
}
if p.tags != nil {
logger = logger.WithValues(p.tags...)
}
p.logger.Logger = logger
p.logger.promise = nil
for _, childPromise := range p.childPromises {
childPromise.Fulfill(logger)
}
}
// DelegatingLogger is a logr.Logger that delegates to another logr.Logger.
// If the underlying promise is not nil, it registers calls to sub-loggers with
// the logging factory to be populated later, and returns a new delegating
// logger. It expects to have *some* logr.Logger set at all times (generally
// a no-op logger before the promises are fulfilled).
type DelegatingLogger struct {
logr.Logger
promise *loggerPromise
}
// WithName provides a new Logger with the name appended
func (l *DelegatingLogger) WithName(name string) logr.Logger {
if l.promise == nil {
return l.Logger.WithName(name)
}
res := &DelegatingLogger{Logger: l.Logger}
promise := l.promise.WithName(res, name)
res.promise = promise
return res
}
// WithValues provides a new Logger with the tags appended
func (l *DelegatingLogger) WithValues(tags ...interface{}) logr.Logger {
if l.promise == nil {
return l.Logger.WithValues(tags...)
}
res := &DelegatingLogger{Logger: l.Logger}
promise := l.promise.WithValues(res, tags...)
res.promise = promise
return res
}
// Fulfill switches the logger over to use the actual logger
// provided, instead of the temporary initial one, if this method
// has not been previously called.
func (l *DelegatingLogger) Fulfill(actual logr.Logger) {
if l.promise != nil {
l.promise.Fulfill(actual)
}
}
// NewDelegatingLogger constructs a new DelegatingLogger which uses
// the given logger before it's promise is fulfilled.
func NewDelegatingLogger(initial logr.Logger) *DelegatingLogger {
l := &DelegatingLogger{
Logger: initial,
promise: &loggerPromise{promisesLock: sync.Mutex{}},
}
l.promise.logger = l
return l
}

15
pkg/log/log.go Normal file
View file

@ -0,0 +1,15 @@
package log
import (
"github.com/go-logr/logr"
)
// SetLogger sets a concrete logging implementation for all deferred Loggers.
func SetLogger(l logr.Logger) {
Log.Fulfill(l)
}
// Log is the base logger used by kubebuilder. It delegates
// to another logr.Logger. You *must* call SetLogger to
// get any actual logging.
var Log = NewDelegatingLogger(NullLogger{})

44
pkg/log/null.go Normal file
View file

@ -0,0 +1,44 @@
package log
import (
"github.com/go-logr/logr"
)
// NB: this is the same as the null logger logr/testing,
// but avoids accidentally adding the testing flags to
// all binaries.
// NullLogger is a logr.Logger that does nothing.
type NullLogger struct{}
var _ logr.Logger = NullLogger{}
// Info implements logr.InfoLogger
func (NullLogger) Info(_ string, _ ...interface{}) {
// Do nothing.
}
// Enabled implements logr.InfoLogger
func (NullLogger) Enabled() bool {
return false
}
// Error implements logr.Logger
func (NullLogger) Error(_ error, _ string, _ ...interface{}) {
// Do nothing.
}
// V implements logr.Logger
func (log NullLogger) V(_ int) logr.InfoLogger {
return log
}
// WithName implements logr.Logger
func (log NullLogger) WithName(_ string) logr.Logger {
return log
}
// WithValues implements logr.Logger
func (log NullLogger) WithValues(_ ...interface{}) logr.Logger {
return log
}

View file

@ -5,6 +5,7 @@ import (
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
dclient "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/log"
"github.com/nirmata/kyverno/pkg/policy/generate"
"github.com/nirmata/kyverno/pkg/policy/mutate"
"github.com/nirmata/kyverno/pkg/policy/validate"
@ -40,7 +41,7 @@ func validateActions(idx int, rule kyverno.Rule, client *dclient.Client) error {
// Generate
if rule.HasGenerate() {
checker = generate.NewGenerateFactory(client, rule.Generation)
checker = generate.NewGenerateFactory(client, rule.Generation, log.Log)
if path, err := checker.Validate(); err != nil {
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
}

View file

@ -8,7 +8,7 @@ import (
"time"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
@ -19,12 +19,13 @@ import (
// applyPolicy applies policy on a resource
//TODO: generation rules
func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) (responses []response.EngineResponse) {
func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface, log logr.Logger) (responses []response.EngineResponse) {
logger := log.WithValues("kind", resource.GetKind(), "namespace", resource.GetNamespace(), "name", resource.GetName())
startTime := time.Now()
var policyStats []PolicyStat
glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime)
logger.Info("start applying policy", "startTime", startTime)
defer func() {
glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime))
logger.Info("finisnhed applying policy", "processingTime", time.Since(startTime))
}()
// gather stats from the engine response
@ -66,10 +67,10 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure
ctx.AddResource(transformResource(resource))
//MUTATION
engineResponse, err = mutation(policy, resource, policyStatus, ctx)
engineResponse, err = mutation(policy, resource, policyStatus, ctx, logger)
engineResponses = append(engineResponses, engineResponse)
if err != nil {
glog.Errorf("unable to process mutation rules: %v", err)
logger.Error(err, "failed to process mutation rule")
}
gatherStat(policy.Name, engineResponse.PolicyResponse)
//send stats
@ -86,52 +87,52 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure
//TODO: GENERATION
return engineResponses
}
func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface, ctx context.EvalInterface) (response.EngineResponse, error) {
func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface, ctx context.EvalInterface, log logr.Logger) (response.EngineResponse, error) {
engineResponse := engine.Mutate(engine.PolicyContext{Policy: policy, NewResource: resource, Context: ctx})
if !engineResponse.IsSuccesful() {
glog.V(4).Infof("mutation had errors reporting them")
log.V(4).Info("failed to apply mutation rules; reporting them")
return engineResponse, nil
}
// Verify if the JSON pathes returned by the Mutate are already applied to the resource
if reflect.DeepEqual(resource, engineResponse.PatchedResource) {
// resources matches
glog.V(4).Infof("resource %s/%s/%s satisfies policy %s", engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name, engineResponse.PolicyResponse.Policy)
log.V(4).Info("resource already satisfys the policy")
return engineResponse, nil
}
return getFailedOverallRuleInfo(resource, engineResponse)
return getFailedOverallRuleInfo(resource, engineResponse, log)
}
// getFailedOverallRuleInfo gets detailed info for over-all mutation failure
func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse response.EngineResponse) (response.EngineResponse, error) {
func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse response.EngineResponse, log logr.Logger) (response.EngineResponse, error) {
rawResource, err := resource.MarshalJSON()
if err != nil {
glog.V(4).Infof("unable to marshal resource: %v\n", err)
log.Error(err, "faield to marshall resource")
return response.EngineResponse{}, err
}
// resource does not match so there was a mutation rule violated
for index, rule := range engineResponse.PolicyResponse.Rules {
glog.V(4).Infof("veriying if policy %s rule %s was applied before to resource %s/%s/%s", engineResponse.PolicyResponse.Policy, rule.Name, engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
log.V(4).Info("veriying if policy rule was applied before", "rule", rule.Name)
if len(rule.Patches) == 0 {
continue
}
patch, err := jsonpatch.DecodePatch(utils.JoinPatches(rule.Patches))
if err != nil {
glog.V(4).Infof("unable to decode patch %s: %v", rule.Patches, err)
log.Error(err, "failed to decode JSON patch", "patches", rule.Patches)
return response.EngineResponse{}, err
}
// apply the patches returned by mutate to the original resource
patchedResource, err := patch.Apply(rawResource)
if err != nil {
glog.V(4).Infof("unable to apply patch %s: %v", rule.Patches, err)
log.Error(err, "failed to apply JSON patch", "patches", rule.Patches)
return response.EngineResponse{}, err
}
if !jsonpatch.Equal(patchedResource, rawResource) {
glog.V(4).Infof("policy %s rule %s condition not satisfied by existing resource", engineResponse.PolicyResponse.Policy, rule.Name)
log.V(4).Info("policy rule conditions not satisfied by resource", "rule", rule.Name)
engineResponse.PolicyResponse.Rules[index].Success = false
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(rule.Patches))
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(rule.Patches, log))
}
}
return engineResponse, nil
@ -143,14 +144,14 @@ type jsonPatch struct {
Value interface{} `json:"value"`
}
func extractPatchPath(patches [][]byte) string {
func extractPatchPath(patches [][]byte, log logr.Logger) string {
var resultPath []string
// extract the patch path and value
for _, patch := range patches {
glog.V(4).Infof("expected json patch not found in resource: %s", string(patch))
log.V(4).Info("expected json patch not found in resource", "patch", string(patch))
var data jsonPatch
if err := json.Unmarshal(patch, &data); err != nil {
glog.V(4).Infof("Failed to decode the generated patch %v: Error %v", string(patch), err)
log.Error(err, "failed to decode the generate patch", "patch", string(patch))
continue
}
resultPath = append(resultPath, data.Path)

View file

@ -6,6 +6,7 @@ import (
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/variables"
"github.com/nirmata/kyverno/pkg/log"
)
//ContainsUserInfo returns error is userInfo is defined
@ -35,23 +36,23 @@ func ContainsUserInfo(policy kyverno.ClusterPolicy) error {
filterVars := []string{"request.userInfo*", "serviceAccountName", "serviceAccountNamespace"}
ctx := context.NewContext(filterVars...)
for condIdx, condition := range rule.Conditions {
if condition.Key, err = variables.SubstituteVars(ctx, condition.Key); err != nil {
if condition.Key, err = variables.SubstituteVars(log.Log, ctx, condition.Key); err != nil {
return fmt.Errorf("userInfo variable used at spec/rules[%d]/condition[%d]/key", idx, condIdx)
}
if condition.Value, err = variables.SubstituteVars(ctx, condition.Value); err != nil {
if condition.Value, err = variables.SubstituteVars(log.Log, ctx, condition.Value); err != nil {
return fmt.Errorf("userInfo variable used at spec/rules[%d]/condition[%d]/value", idx, condIdx)
}
}
if rule.Mutation.Overlay, err = variables.SubstituteVars(ctx, rule.Mutation.Overlay); err != nil {
if rule.Mutation.Overlay, err = variables.SubstituteVars(log.Log, ctx, rule.Mutation.Overlay); err != nil {
return fmt.Errorf("userInfo variable used at spec/rules[%d]/mutate/overlay", idx)
}
if rule.Validation.Pattern, err = variables.SubstituteVars(ctx, rule.Validation.Pattern); err != nil {
if rule.Validation.Pattern, err = variables.SubstituteVars(log.Log, ctx, rule.Validation.Pattern); err != nil {
return fmt.Errorf("userInfo variable used at spec/rules[%d]/validate/pattern", idx)
}
for idx2, pattern := range rule.Validation.AnyPattern {
if rule.Validation.AnyPattern[idx2], err = variables.SubstituteVars(ctx, pattern); err != nil {
if rule.Validation.AnyPattern[idx2], err = variables.SubstituteVars(log.Log, ctx, pattern); err != nil {
return fmt.Errorf("userInfo variable used at spec/rules[%d]/validate/anyPattern[%d]", idx, idx2)
}
}

View file

@ -4,57 +4,71 @@ import (
"fmt"
"reflect"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/response"
"k8s.io/apimachinery/pkg/labels"
)
func (pc *PolicyController) cleanUp(ers []response.EngineResponse) {
for _, er := range ers {
if !er.IsSuccesful() {
continue
}
if len(er.PolicyResponse.Rules) == 0 {
continue
}
// clean up after the policy has been corrected
pc.cleanUpPolicyViolation(er.PolicyResponse)
}
}
func (pc *PolicyController) cleanUpPolicyViolation(pResponse response.PolicyResponse) {
logger := pc.log
// - check if there is violation on resource (label:Selector)
if pResponse.Resource.Namespace == "" {
pv, err := getClusterPV(pc.cpvLister, pResponse.Policy, pResponse.Resource.Kind, pResponse.Resource.Name)
pv, err := getClusterPV(pc.cpvLister, pResponse.Policy, pResponse.Resource.Kind, pResponse.Resource.Name, logger)
if err != nil {
glog.Errorf("failed to cleanUp violations: %v", err)
logger.Error(err, "failed to get cluster policy violation on policy and resource", "policy", pResponse.Policy, "kind", pResponse.Resource.Kind, "name", pResponse.Resource.Name)
return
}
if reflect.DeepEqual(pv, kyverno.ClusterPolicyViolation{}) {
return
}
glog.V(4).Infof("cleanup cluster violation %s on %s", pv.Name, pv.Spec.ResourceSpec.ToKey())
if err := pc.pvControl.DeleteClusterPolicyViolation(pv.Name); err != nil {
glog.Errorf("failed to delete cluster policy violation %s on %s: %v", pv.Name, pv.Spec.ResourceSpec.ToKey(), err)
logger.Error(err, "failed to delete cluster policy violation", "name", pv.Name)
} else {
logger.Info("deleted cluster policy violation", "name", pv.Name)
}
return
}
// namespace policy violation
nspv, err := getNamespacedPV(pc.nspvLister, pResponse.Policy, pResponse.Resource.Kind, pResponse.Resource.Namespace, pResponse.Resource.Name)
nspv, err := getNamespacedPV(pc.nspvLister, pResponse.Policy, pResponse.Resource.Kind, pResponse.Resource.Namespace, pResponse.Resource.Name, logger)
if err != nil {
glog.Error(err)
logger.Error(err, "failed to get namespaced policy violation on policy and resource", "policy", pResponse.Policy, "kind", pResponse.Resource.Kind, "namespace", pResponse.Resource.Namespace, "name", pResponse.Resource.Name)
return
}
if reflect.DeepEqual(nspv, kyverno.PolicyViolation{}) {
return
}
glog.V(4).Infof("cleanup namespaced violation %s on %s.%s", nspv.Name, pResponse.Resource.Namespace, nspv.Spec.ResourceSpec.ToKey())
if err := pc.pvControl.DeleteNamespacedPolicyViolation(nspv.Namespace, nspv.Name); err != nil {
glog.Errorf("failed to delete namespaced policy violation %s on %s: %v", nspv.Name, nspv.Spec.ResourceSpec.ToKey(), err)
logger.Error(err, "failed to delete cluster policy violation", "name", nspv.Name, "namespace", nspv.Namespace)
} else {
logger.Info("deleted namespaced policy violation", "name", nspv.Name, "namespace", nspv.Namespace)
}
}
// Wont do the claiming of objects, just lookup based on selectors
func getClusterPV(pvLister kyvernolister.ClusterPolicyViolationLister, policyName, rkind, rname string) (kyverno.ClusterPolicyViolation, error) {
func getClusterPV(pvLister kyvernolister.ClusterPolicyViolationLister, policyName, rkind, rname string, log logr.Logger) (kyverno.ClusterPolicyViolation, error) {
var err error
// Check Violation on resource
pvs, err := pvLister.List(labels.Everything())
if err != nil {
glog.V(2).Infof("unable to list policy violations : %v", err)
log.Error(err, "failed to list cluster policy violations")
return kyverno.ClusterPolicyViolation{}, fmt.Errorf("failed to list cluster pv: %v", err)
}
@ -69,10 +83,10 @@ func getClusterPV(pvLister kyvernolister.ClusterPolicyViolationLister, policyNam
return kyverno.ClusterPolicyViolation{}, nil
}
func getNamespacedPV(nspvLister kyvernolister.PolicyViolationLister, policyName, rkind, rnamespace, rname string) (kyverno.PolicyViolation, error) {
func getNamespacedPV(nspvLister kyvernolister.PolicyViolationLister, policyName, rkind, rnamespace, rname string, log logr.Logger) (kyverno.PolicyViolation, error) {
nspvs, err := nspvLister.PolicyViolations(rnamespace).List(labels.Everything())
if err != nil {
glog.V(2).Infof("failed to list namespaced pv: %v", err)
log.Error(err, "failed to list namespaced policy violation")
return kyverno.PolicyViolation{}, fmt.Errorf("failed to list namespaced pv: %v", err)
}

View file

@ -1,11 +1,10 @@
package policy
import (
"fmt"
"reflect"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
"github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme"
@ -75,6 +74,7 @@ type PolicyController struct {
pvGenerator policyviolation.GeneratorInterface
// resourceWebhookWatcher queues the webhook creation request, creates the webhook
resourceWebhookWatcher *webhookconfig.ResourceWebhookRegister
log logr.Logger
}
// NewPolicyController create a new PolicyController
@ -87,10 +87,11 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset,
eventGen event.Interface,
pvGenerator policyviolation.GeneratorInterface,
pMetaStore policystore.UpdateInterface,
resourceWebhookWatcher *webhookconfig.ResourceWebhookRegister) (*PolicyController, error) {
resourceWebhookWatcher *webhookconfig.ResourceWebhookRegister,
log logr.Logger) (*PolicyController, error) {
// Event broad caster
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartLogging(glog.Infof)
eventBroadcaster.StartLogging(log.Info)
eventInterface, err := client.GetEventsInterface()
if err != nil {
return nil, err
@ -107,6 +108,7 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset,
pMetaStore: pMetaStore,
pvGenerator: pvGenerator,
resourceWebhookWatcher: resourceWebhookWatcher,
log: log,
}
pc.pvControl = RealPVControl{Client: kyvernoClient, Recorder: pc.eventRecorder}
@ -152,6 +154,7 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset,
}
func (pc *PolicyController) addPolicy(obj interface{}) {
logger := pc.log
p := obj.(*kyverno.ClusterPolicy)
// Only process policies that are enabled for "background" execution
// policy.spec.background -> "True"
@ -175,19 +178,19 @@ func (pc *PolicyController) addPolicy(obj interface{}) {
return
}
}
glog.V(4).Infof("Adding Policy %s", p.Name)
logger.V(4).Info("adding policy", "name", p.Name)
pc.enqueuePolicy(p)
}
func (pc *PolicyController) updatePolicy(old, cur interface{}) {
logger := pc.log
oldP := old.(*kyverno.ClusterPolicy)
curP := cur.(*kyverno.ClusterPolicy)
// TODO: optimize this : policy meta-store
// Update policy-> (remove,add)
err := pc.pMetaStore.UnRegister(*oldP)
if err != nil {
glog.Infof("Failed to unregister policy %s", oldP.Name)
logger.Error(err, "failed to unregister policy", "name", oldP.Name)
}
pc.pMetaStore.Register(*curP)
@ -210,28 +213,29 @@ func (pc *PolicyController) updatePolicy(old, cur interface{}) {
return
}
}
glog.V(4).Infof("Updating Policy %s", oldP.Name)
logger.V(4).Info("updating policy", "name", oldP.Name)
pc.enqueuePolicy(curP)
}
func (pc *PolicyController) deletePolicy(obj interface{}) {
logger := pc.log
p, ok := obj.(*kyverno.ClusterPolicy)
if !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
glog.Info(fmt.Errorf("Couldn't get object from tombstone %#v", obj))
logger.Info("couldnt get object from tomstone", "obj", obj)
return
}
p, ok = tombstone.Obj.(*kyverno.ClusterPolicy)
if !ok {
glog.Info(fmt.Errorf("Tombstone contained object that is not a Policy %#v", obj))
logger.Info("tombstone container object that is not a policy", "obj", obj)
return
}
}
glog.V(4).Infof("Deleting Policy %s", p.Name)
logger.V(4).Info("deleting policy", "name", p.Name)
// Unregister from policy meta-store
if err := pc.pMetaStore.UnRegister(*p); err != nil {
glog.Infof("failed to unregister policy %s", p.Name)
logger.Error(err, "failed to unregister policy", "name", p.Name)
}
// we process policies that are not set of background processing as we need to perform policy violation
// cleanup when a policy is deleted.
@ -239,9 +243,10 @@ func (pc *PolicyController) deletePolicy(obj interface{}) {
}
func (pc *PolicyController) enqueue(policy *kyverno.ClusterPolicy) {
logger := pc.log
key, err := cache.MetaNamespaceKeyFunc(policy)
if err != nil {
glog.Error(err)
logger.Error(err, "failed to enqueu policy")
return
}
pc.queue.Add(key)
@ -249,15 +254,16 @@ func (pc *PolicyController) enqueue(policy *kyverno.ClusterPolicy) {
// Run begins watching and syncing.
func (pc *PolicyController) Run(workers int, stopCh <-chan struct{}) {
logger := pc.log
defer utilruntime.HandleCrash()
defer pc.queue.ShutDown()
glog.Info("Starting policy controller")
defer glog.Info("Shutting down policy controller")
logger.Info("starting")
defer logger.Info("shutting down")
if !cache.WaitForCacheSync(stopCh, pc.pListerSynced, pc.cpvListerSynced, pc.nspvListerSynced) {
glog.Error("failed to sync informer cache")
logger.Info("failed to sync informer cache")
return
}
@ -295,31 +301,33 @@ func (pc *PolicyController) processNextWorkItem() bool {
}
func (pc *PolicyController) handleErr(err error, key interface{}) {
logger := pc.log
if err == nil {
pc.queue.Forget(key)
return
}
if pc.queue.NumRequeues(key) < maxRetries {
glog.V(2).Infof("Error syncing Policy %v: %v", key, err)
logger.Error(err, "failed to sync policy", "key", key)
pc.queue.AddRateLimited(key)
return
}
utilruntime.HandleError(err)
glog.V(2).Infof("Dropping policy %q out of the queue: %v", key, err)
logger.V(2).Info("dropping policy out of queue", "key", key)
pc.queue.Forget(key)
}
func (pc *PolicyController) syncPolicy(key string) error {
logger := pc.log
startTime := time.Now()
glog.V(4).Infof("Started syncing policy %q (%v)", key, startTime)
logger.V(4).Info("started syncing policy", "key", key, "startTime", startTime)
defer func() {
glog.V(4).Infof("Finished syncing policy %q (%v)", key, time.Since(startTime))
logger.V(4).Info("finished syncing policy", "key", key, "processingTime", time.Since(startTime))
}()
policy, err := pc.pLister.Get(key)
if errors.IsNotFound(err) {
glog.V(2).Infof("Policy %v has been deleted", key)
logger.V(2).Info("policy deleted", "key", key)
// delete cluster policy violation
if err := pc.deleteClusterPolicyViolations(key); err != nil {
return err
@ -334,8 +342,7 @@ func (pc *PolicyController) syncPolicy(key string) error {
// remove webhook configurations if there are no policies
if err := pc.removeResourceWebhookConfiguration(); err != nil {
// do not fail, if unable to delete resource webhook config
glog.V(4).Infof("failed to remove resource webhook configuration: %v", err)
glog.Errorln(err)
logger.Error(err, "failed to remove resource webhook configurations")
}
return nil
}

View file

@ -5,7 +5,7 @@ import (
"sync"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/minio/minio/pkg/wildcard"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/config"
@ -19,27 +19,28 @@ import (
)
func (pc *PolicyController) processExistingResources(policy kyverno.ClusterPolicy) []response.EngineResponse {
logger := pc.log.WithValues("policy", policy.Name)
// Parse through all the resources
// drops the cache after configured rebuild time
pc.rm.Drop()
var engineResponses []response.EngineResponse
// get resource that are satisfy the resource description defined in the rules
resourceMap := listResources(pc.client, policy, pc.configHandler)
resourceMap := listResources(pc.client, policy, pc.configHandler, logger)
for _, resource := range resourceMap {
// pre-processing, check if the policy and resource version has been processed before
if !pc.rm.ProcessResource(policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) {
glog.V(4).Infof("policy %s with resource version %s already processed on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
logger.V(4).Info("policy and resource already processed", "policyResourceVersion", policy.ResourceVersion, "resourceResourceVersion", resource.GetResourceVersion(), "kind", resource.GetKind(), "namespace", resource.GetNamespace(), "name", resource.GetName())
continue
}
// skip reporting violation on pod which has annotation pod-policies.kyverno.io/autogen-applied
if skipPodApplication(resource) {
if skipPodApplication(resource, logger) {
continue
}
// apply the policy on each
glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
engineResponse := applyPolicy(policy, resource, pc.statusAggregator)
logger.V(4).Info("apply policy on resource", "policyResourceVersion", policy.ResourceVersion, "resourceResourceVersion", resource.GetResourceVersion(), "kind", resource.GetKind(), "namespace", resource.GetNamespace(), "name", resource.GetName())
engineResponse := applyPolicy(policy, resource, pc.statusAggregator, logger)
// get engine response for mutation & validation independently
engineResponses = append(engineResponses, engineResponse...)
// post-processing, register the resource as processed
@ -48,36 +49,26 @@ func (pc *PolicyController) processExistingResources(policy kyverno.ClusterPolic
return engineResponses
}
func listResources(client *client.Client, policy kyverno.ClusterPolicy, configHandler config.Interface) map[string]unstructured.Unstructured {
func listResources(client *client.Client, policy kyverno.ClusterPolicy, configHandler config.Interface, log logr.Logger) map[string]unstructured.Unstructured {
// key uid
resourceMap := map[string]unstructured.Unstructured{}
for _, rule := range policy.Spec.Rules {
// resources that match
for _, k := range rule.MatchResources.Kinds {
// if kindIsExcluded(k, rule.ExcludeResources.Kinds) {
// glog.V(4).Infof("processing policy %s rule %s: kind %s is exluded", policy.Name, rule.Name, k)
// continue
// }
var namespaces []string
if k == "Namespace" {
// TODO
// this is handled by generator controller
glog.V(4).Infof("skipping processing policy %s rule %s for kind Namespace", policy.Name, rule.Name)
continue
}
if len(rule.MatchResources.Namespaces) > 0 {
namespaces = append(namespaces, rule.MatchResources.Namespaces...)
glog.V(4).Infof("namespaces specified for inclusion: %v", rule.MatchResources.Namespaces)
log.V(4).Info("namespaces included", "namespaces", rule.MatchResources.Namespaces)
} else {
glog.V(4).Infof("processing policy %s rule %s, namespace not defined, getting all namespaces ", policy.Name, rule.Name)
log.V(4).Info("processing all namespaces", "rule", rule.Name)
// get all namespaces
namespaces = getAllNamespaces(client)
namespaces = getAllNamespaces(client, log)
}
// get resources in the namespaces
for _, ns := range namespaces {
rMap := getResourcesPerNamespace(k, client, ns, rule, configHandler)
rMap := getResourcesPerNamespace(k, client, ns, rule, configHandler, log)
mergeresources(resourceMap, rMap)
}
@ -86,16 +77,16 @@ func listResources(client *client.Client, policy kyverno.ClusterPolicy, configHa
return resourceMap
}
func getResourcesPerNamespace(kind string, client *client.Client, namespace string, rule kyverno.Rule, configHandler config.Interface) map[string]unstructured.Unstructured {
func getResourcesPerNamespace(kind string, client *client.Client, namespace string, rule kyverno.Rule, configHandler config.Interface, log logr.Logger) map[string]unstructured.Unstructured {
resourceMap := map[string]unstructured.Unstructured{}
// merge include and exclude label selector values
ls := rule.MatchResources.Selector
// ls := mergeLabelSectors(rule.MatchResources.Selector, rule.ExcludeResources.Selector)
// list resources
glog.V(4).Infof("get resources for kind %s, namespace %s, selector %v", kind, namespace, rule.MatchResources.Selector)
log.V(4).Info("list resources to be processed")
list, err := client.ListResource(kind, namespace, ls)
if err != nil {
glog.Infof("unable to get resources: err %v", err)
log.Error(err, "failed to list resources", "kind", kind)
return nil
}
// filter based on name
@ -103,7 +94,6 @@ func getResourcesPerNamespace(kind string, client *client.Client, namespace stri
// match name
if rule.MatchResources.Name != "" {
if !wildcard.Match(rule.MatchResources.Name, r.GetName()) {
glog.V(4).Infof("skipping resource %s/%s due to include condition name=%s mistatch", r.GetNamespace(), r.GetName(), rule.MatchResources.Name)
continue
}
}
@ -118,12 +108,11 @@ func getResourcesPerNamespace(kind string, client *client.Client, namespace stri
// exclude the resources
// skip resources to be filtered
excludeResources(resourceMap, rule.ExcludeResources.ResourceDescription, configHandler)
// glog.V(4).Infof("resource map: %v", resourceMap)
excludeResources(resourceMap, rule.ExcludeResources.ResourceDescription, configHandler, log)
return resourceMap
}
func excludeResources(included map[string]unstructured.Unstructured, exclude kyverno.ResourceDescription, configHandler config.Interface) {
func excludeResources(included map[string]unstructured.Unstructured, exclude kyverno.ResourceDescription, configHandler config.Interface, log logr.Logger) {
if reflect.DeepEqual(exclude, (kyverno.ResourceDescription{})) {
return
}
@ -154,7 +143,7 @@ func excludeResources(included map[string]unstructured.Unstructured, exclude kyv
selector, err := metav1.LabelSelectorAsSelector(exclude.Selector)
// if the label selector is incorrect, should be fail or
if err != nil {
glog.Error(err)
log.Error(err, "failed to build label selector")
return Skip
}
if selector.Matches(labels.Set(labelsMap)) {
@ -205,8 +194,6 @@ func excludeResources(included map[string]unstructured.Unstructured, exclude kyv
}
// exclude the filtered resources
if configHandler.ToFilter(resource.GetKind(), resource.GetNamespace(), resource.GetName()) {
//TODO: improve the text
glog.V(4).Infof("excluding resource %s/%s/%s as its satisfies the filtered resources", resource.GetKind(), resource.GetNamespace(), resource.GetName())
delete(included, uid)
continue
}
@ -244,12 +231,13 @@ func mergeresources(a, b map[string]unstructured.Unstructured) {
}
}
func getAllNamespaces(client *client.Client) []string {
func getAllNamespaces(client *client.Client, log logr.Logger) []string {
var namespaces []string
// get all namespaces
nsList, err := client.ListResource("Namespace", "", nil)
if err != nil {
glog.Error(err)
log.Error(err, "failed to list namespaces")
return namespaces
}
for _, ns := range nsList.Items {
@ -291,14 +279,11 @@ type resourceManager interface {
//TODO: or drop based on the size
func (rm *ResourceManager) Drop() {
timeSince := time.Since(rm.time)
glog.V(4).Infof("time since last cache reset time %v is %v", rm.time, timeSince)
glog.V(4).Infof("cache rebuild time %v", time.Duration(rm.rebuildTime)*time.Second)
if timeSince > time.Duration(rm.rebuildTime)*time.Second {
rm.mux.Lock()
defer rm.mux.Unlock()
rm.data = map[string]interface{}{}
rm.time = time.Now()
glog.V(4).Infof("dropping cache at time %v", rm.time)
}
}
@ -327,14 +312,14 @@ func buildKey(policy, pv, kind, ns, name, rv string) string {
return policy + "/" + pv + "/" + kind + "/" + ns + "/" + name + "/" + rv
}
func skipPodApplication(resource unstructured.Unstructured) bool {
func skipPodApplication(resource unstructured.Unstructured, log logr.Logger) bool {
if resource.GetKind() != "Pod" {
return false
}
annotation := resource.GetAnnotations()
if _, ok := annotation[engine.PodTemplateAnnotation]; ok {
glog.V(4).Infof("Policies already processed on pod controllers, skip processing policy on Pod/%s/%s", resource.GetNamespace(), resource.GetName())
log.V(4).Info("Policies already processed on pod controllers, skip processing policy on Pod", "kind", resource.GetKind(), "namespace", resource.GetNamespace(), "name", resource.GetName())
return true
}

View file

@ -1,6 +1,7 @@
package generate
import (
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/auth"
dclient "github.com/nirmata/kyverno/pkg/dclient"
)
@ -20,19 +21,21 @@ type Operations interface {
//Auth provides implementation to check if caller/self/kyverno has access to perofrm operations
type Auth struct {
client *dclient.Client
log logr.Logger
}
//NewAuth returns a new instance of Auth for operations
func NewAuth(client *dclient.Client) *Auth {
func NewAuth(client *dclient.Client, log logr.Logger) *Auth {
a := Auth{
client: client,
log: log,
}
return &a
}
// CanICreate returns 'true' if self can 'create' resource
func (a *Auth) CanICreate(kind, namespace string) (bool, error) {
canI := auth.NewCanI(a.client, kind, namespace, "create")
canI := auth.NewCanI(a.client, kind, namespace, "create", a.log)
ok, err := canI.RunAccessCheck()
if err != nil {
return false, err
@ -42,7 +45,7 @@ func (a *Auth) CanICreate(kind, namespace string) (bool, error) {
// CanIUpdate returns 'true' if self can 'update' resource
func (a *Auth) CanIUpdate(kind, namespace string) (bool, error) {
canI := auth.NewCanI(a.client, kind, namespace, "update")
canI := auth.NewCanI(a.client, kind, namespace, "update", a.log)
ok, err := canI.RunAccessCheck()
if err != nil {
return false, err
@ -52,7 +55,7 @@ func (a *Auth) CanIUpdate(kind, namespace string) (bool, error) {
// CanIDelete returns 'true' if self can 'delete' resource
func (a *Auth) CanIDelete(kind, namespace string) (bool, error) {
canI := auth.NewCanI(a.client, kind, namespace, "delete")
canI := auth.NewCanI(a.client, kind, namespace, "delete", a.log)
ok, err := canI.RunAccessCheck()
if err != nil {
return false, err
@ -62,7 +65,7 @@ func (a *Auth) CanIDelete(kind, namespace string) (bool, error) {
// CanIGet returns 'true' if self can 'get' resource
func (a *Auth) CanIGet(kind, namespace string) (bool, error) {
canI := auth.NewCanI(a.client, kind, namespace, "get")
canI := auth.NewCanI(a.client, kind, namespace, "get", a.log)
ok, err := canI.RunAccessCheck()
if err != nil {
return false, err

View file

@ -4,7 +4,7 @@ import (
"fmt"
"reflect"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
dclient "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/engine/anchor"
@ -18,13 +18,16 @@ type Generate struct {
rule kyverno.Generation
// authCheck to check access for operations
authCheck Operations
//logger
log logr.Logger
}
//NewGenerateFactory returns a new instance of Generate validation checker
func NewGenerateFactory(client *dclient.Client, rule kyverno.Generation) *Generate {
func NewGenerateFactory(client *dclient.Client, rule kyverno.Generation, log logr.Logger) *Generate {
g := Generate{
rule: rule,
authCheck: NewAuth(client),
authCheck: NewAuth(client, log),
log: log,
}
return &g
@ -92,7 +95,7 @@ func (g *Generate) validateClone(c kyverno.CloneFrom, kind string) (string, erro
return "", fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace)
}
} else {
glog.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.")
g.log.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.")
}
return "", nil
}
@ -141,7 +144,7 @@ func (g *Generate) canIGenerate(kind, namespace string) error {
}
} else {
glog.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.")
g.log.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.")
}
return nil

View file

@ -7,6 +7,7 @@ import (
)
func (pc *PolicyController) addNamespacedPolicyViolation(obj interface{}) {
logger := pc.log
pv := obj.(*kyverno.PolicyViolation)
if pv.DeletionTimestamp != nil {
@ -20,14 +21,15 @@ func (pc *PolicyController) addNamespacedPolicyViolation(obj interface{}) {
ps := pc.getPolicyForNamespacedPolicyViolation(pv)
if len(ps) == 0 {
// there is no cluster policy for this violation, so we can delete this cluster policy violation
glog.V(4).Infof("PolicyViolation %s does not belong to an active policy, will be cleanedup", pv.Name)
logger.V(4).Info("namepaced policy violation does not belong to any active policy, will be cleanedup", "namespacePolicyViolation", pv.Name)
if err := pc.pvControl.DeleteNamespacedPolicyViolation(pv.Namespace, pv.Name); err != nil {
glog.Errorf("Failed to deleted policy violation %s: %v", pv.Name, err)
logger.Error(err, "failed to delete namespaced policy violation", "namespacedPolicyViolation", pv.Name)
return
}
glog.V(4).Infof("PolicyViolation %s deleted", pv.Name)
logger.V(4).Info("resource delete", "namespacePOlicyViolation", pv.Name)
return
}
glog.V(4).Infof("Orphan Policy Violation %s added.", pv.Name)
for _, p := range ps {
pc.enqueuePolicy(p)

View file

@ -13,11 +13,12 @@ import (
// - has violation -> report
// - no violation -> cleanup policy violations
func (pc *PolicyController) cleanupAndReport(engineResponses []response.EngineResponse) {
logger := pc.log
// generate Events
eventInfos := generateEvents(engineResponses)
pc.eventGen.Add(eventInfos...)
// create policy violation
pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses)
pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses, logger)
pc.pvGenerator.Add(pvInfos...)
// cleanup existing violations if any
// if there is any error in clean up, we dont re-queue the resource
@ -25,19 +26,6 @@ func (pc *PolicyController) cleanupAndReport(engineResponses []response.EngineRe
pc.cleanUp(engineResponses)
}
func (pc *PolicyController) cleanUp(ers []response.EngineResponse) {
for _, er := range ers {
if !er.IsSuccesful() {
continue
}
if len(er.PolicyResponse.Rules) == 0 {
continue
}
// clean up after the policy has been corrected
pc.cleanUpPolicyViolation(er.PolicyResponse)
}
}
func generateEvents(ers []response.EngineResponse) []event.Info {
var eventInfos []event.Info
for _, er := range ers {

View file

@ -3,12 +3,11 @@ package policystore
import (
"sync"
"k8s.io/apimachinery/pkg/labels"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1"
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
@ -24,6 +23,7 @@ type PolicyStore struct {
pLister kyvernolister.ClusterPolicyLister
// returns true if the cluster policy store has been synced at least once
pSynched cache.InformerSynced
log logr.Logger
}
//UpdateInterface provides api to update policies
@ -40,25 +40,29 @@ type LookupInterface interface {
}
// NewPolicyStore returns a new policy store
func NewPolicyStore(pInformer kyvernoinformer.ClusterPolicyInformer) *PolicyStore {
func NewPolicyStore(pInformer kyvernoinformer.ClusterPolicyInformer,
log logr.Logger) *PolicyStore {
ps := PolicyStore{
data: make(kindMap),
pLister: pInformer.Lister(),
pSynched: pInformer.Informer().HasSynced,
log: log,
}
return &ps
}
//Run checks syncing
func (ps *PolicyStore) Run(stopCh <-chan struct{}) {
logger := ps.log
if !cache.WaitForCacheSync(stopCh, ps.pSynched) {
glog.Error("policy meta store: failed to sync informer cache")
logger.Info("failed to sync informer cache")
}
}
//Register a new policy
func (ps *PolicyStore) Register(policy kyverno.ClusterPolicy) {
glog.V(4).Infof("adding resources %s", policy.Name)
logger := ps.log
logger.V(4).Info("adding policy", "name", policy.Name)
ps.mu.Lock()
defer ps.mu.Unlock()
var pmap policyMap

View file

@ -3,24 +3,23 @@ package policyviolation
import (
"fmt"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/response"
)
//GeneratePVsFromEngineResponse generate Violations from engine responses
func GeneratePVsFromEngineResponse(ers []response.EngineResponse) (pvInfos []Info) {
func GeneratePVsFromEngineResponse(ers []response.EngineResponse, log logr.Logger) (pvInfos []Info) {
for _, er := range ers {
// ignore creation of PV for resources that are yet to be assigned a name
if er.PolicyResponse.Resource.Name == "" {
glog.V(4).Infof("resource %v, has not been assigned a name, not creating a policy violation for it", er.PolicyResponse.Resource)
log.V(4).Info("resource does no have a name assigned yet, not creating a policy violation", "resource", er.PolicyResponse.Resource)
continue
}
// skip when response succeed
if er.IsSuccesful() {
continue
}
glog.V(4).Infof("Building policy violation for engine response %v", er)
// build policy violation info
pvInfos = append(pvInfos, buildPVInfo(er))
}

View file

@ -4,6 +4,7 @@ import (
"testing"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/log"
"gotest.tools/assert"
)
@ -52,6 +53,6 @@ func Test_GeneratePVsFromEngineResponse_PathNotExist(t *testing.T) {
},
}
pvInfos := GeneratePVsFromEngineResponse(ers)
pvInfos := GeneratePVsFromEngineResponse(ers, log.Log)
assert.Assert(t, len(pvInfos) == 1)
}

View file

@ -8,7 +8,7 @@ import (
"sync"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1"
@ -40,6 +40,7 @@ type Generator struct {
nspvSynced cache.InformerSynced
queue workqueue.RateLimitingInterface
dataStore *dataStore
log logr.Logger
}
//NewDataStore returns an instance of data store
@ -103,7 +104,8 @@ type GeneratorInterface interface {
func NewPVGenerator(client *kyvernoclient.Clientset,
dclient *dclient.Client,
pvInformer kyvernoinformer.ClusterPolicyViolationInformer,
nspvInformer kyvernoinformer.PolicyViolationInformer) *Generator {
nspvInformer kyvernoinformer.PolicyViolationInformer,
log logr.Logger) *Generator {
gen := Generator{
kyvernoInterface: client.KyvernoV1(),
dclient: dclient,
@ -113,6 +115,7 @@ func NewPVGenerator(client *kyvernoclient.Clientset,
nspvSynced: nspvInformer.Informer().HasSynced,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), workQueueName),
dataStore: newDataStore(),
log: log,
}
return &gen
}
@ -130,18 +133,18 @@ func (gen *Generator) enqueue(info Info) {
func (gen *Generator) Add(infos ...Info) {
for _, info := range infos {
gen.enqueue(info)
glog.V(3).Infof("Added policy violation: %s", info.toKey())
}
}
// Run starts the workers
func (gen *Generator) Run(workers int, stopCh <-chan struct{}) {
logger := gen.log
defer utilruntime.HandleCrash()
glog.Info("Start policy violation generator")
defer glog.Info("Shutting down policy violation generator")
logger.Info("start")
defer logger.Info("shutting down")
if !cache.WaitForCacheSync(stopCh, gen.pvSynced, gen.nspvSynced) {
glog.Error("policy violation generator: failed to sync informer cache")
logger.Info("failed to sync informer cache")
}
for i := 0; i < workers; i++ {
@ -156,6 +159,7 @@ func (gen *Generator) runWorker() {
}
func (gen *Generator) handleErr(err error, key interface{}) {
logger := gen.log
if err == nil {
gen.queue.Forget(key)
return
@ -163,23 +167,22 @@ func (gen *Generator) handleErr(err error, key interface{}) {
// retires requests if there is error
if gen.queue.NumRequeues(key) < workQueueRetryLimit {
glog.V(4).Infof("Error syncing policy violation %v: %v", key, err)
logger.Error(err, "failed to sync policy violation", "key", key)
// 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)
glog.Error(err)
// remove from data store
if keyHash, ok := key.(string); ok {
gen.dataStore.delete(keyHash)
}
glog.Warningf("Dropping the key out of the queue: %v", err)
logger.Error(err, "dropping key out of the queue", "key", key)
}
func (gen *Generator) processNextWorkitem() bool {
logger := gen.log
obj, shutdown := gen.queue.Get()
if shutdown {
return false
@ -191,7 +194,7 @@ func (gen *Generator) processNextWorkitem() bool {
var ok bool
if keyHash, ok = obj.(string); !ok {
gen.queue.Forget(obj)
glog.Warningf("Expecting type string but got %v\n", obj)
logger.Info("incorrect type; expecting type 'string'", "obj", obj)
return nil
}
// lookup data store
@ -199,7 +202,7 @@ func (gen *Generator) processNextWorkitem() bool {
if reflect.DeepEqual(info, Info{}) {
// empty key
gen.queue.Forget(obj)
glog.Warningf("Got empty key %v\n", obj)
logger.Info("empty key")
return nil
}
err := gen.syncHandler(info)
@ -207,14 +210,14 @@ func (gen *Generator) processNextWorkitem() bool {
return nil
}(obj)
if err != nil {
glog.Error(err)
logger.Error(err, "failed to process item")
return true
}
return true
}
func (gen *Generator) syncHandler(info Info) error {
glog.V(4).Infof("received info:%v", info)
logger := gen.log
var handler pvGenerator
builder := newPvBuilder()
if info.Resource.GetNamespace() == "" {
@ -229,12 +232,12 @@ func (gen *Generator) syncHandler(info Info) error {
pv := builder.generate(info)
// Create Policy Violations
glog.V(3).Infof("Creating policy violation: %s", info.toKey())
logger.V(4).Info("creating policy violation", "key", info.toKey())
if err := handler.create(pv); err != nil {
failure = true
glog.V(3).Infof("Failed to create policy violation: %v", err)
logger.Error(err, "failed to create policy violation")
} else {
glog.V(3).Infof("Policy violation created: %s", info.toKey())
logger.Info("created policy violation", "key", info.toKey())
}
if failure {

View file

@ -3,8 +3,7 @@ package utils
import (
"reflect"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/minio/minio/pkg/wildcard"
client "github.com/nirmata/kyverno/pkg/dclient"
dclient "github.com/nirmata/kyverno/pkg/dclient"
@ -59,14 +58,15 @@ func Btoi(b bool) int {
}
//CRDInstalled to check if the CRD is installed or not
func CRDInstalled(discovery client.IDiscovery) bool {
func CRDInstalled(discovery client.IDiscovery, log logr.Logger) bool {
logger := log.WithName("CRDInstalled")
check := func(kind string) bool {
gvr := discovery.GetGVRFromKind(kind)
if reflect.DeepEqual(gvr, (schema.GroupVersionResource{})) {
glog.Errorf("%s CRD not installed", kind)
logger.Info("CRD not installed", "kind", kind)
return false
}
glog.Infof("CRD %s found ", kind)
logger.Info("CRD found", "kind", kind)
return true
}
if !check("ClusterPolicy") || !check("ClusterPolicyViolation") || !check("PolicyViolation") {
@ -77,11 +77,12 @@ func CRDInstalled(discovery client.IDiscovery) bool {
//CleanupOldCrd deletes any existing NamespacedPolicyViolation resources in cluster
// If resource violates policy, new Violations will be generated
func CleanupOldCrd(client *dclient.Client) {
func CleanupOldCrd(client *dclient.Client, log logr.Logger) {
logger := log.WithName("CleanupOldCrd")
gvr := client.DiscoveryClient.GetGVRFromKind("NamespacedPolicyViolation")
if !reflect.DeepEqual(gvr, (schema.GroupVersionResource{})) {
if err := client.DeleteResource("CustomResourceDefinition", "", "namespacedpolicyviolations.kyverno.io", false); err != nil {
glog.Infof("Failed to remove previous CRD namespacedpolicyviolations: %v", err)
logger.Error(err, "Failed to remove prevous CRD", "kind", "namespacedpolicyviolation")
}
}
}

View file

@ -1,7 +1,7 @@
package version
import (
"github.com/golang/glog"
"github.com/go-logr/logr"
)
// These fields are set during an official build
@ -13,8 +13,8 @@ var (
)
//PrintVersionInfo displays the kyverno version - git version
func PrintVersionInfo() {
glog.Infof("Kyverno version: %s\n", BuildVersion)
glog.Infof("Kyverno BuildHash: %s\n", BuildHash)
glog.Infof("Kyverno BuildTime: %s\n", BuildTime)
func PrintVersionInfo(log logr.Logger) {
log.Info("Kyverno", "Version", BuildVersion)
log.Info("Kyverno", "BuildHash", BuildHash)
log.Info("Kyverno", "BuildTime", BuildTime)
}

View file

@ -19,8 +19,8 @@ func (wrc *WebhookRegistrationClient) constructVerifyMutatingWebhookConfig(caDat
wrc.constructOwner(),
},
},
Webhooks: []admregapi.Webhook{
generateWebhook(
Webhooks: []admregapi.MutatingWebhook{
generateMutatingWebhook(
config.VerifyMutatingWebhookName,
config.VerifyMutatingWebhookServicePath,
caData,
@ -42,8 +42,8 @@ func (wrc *WebhookRegistrationClient) constructDebugVerifyMutatingWebhookConfig(
ObjectMeta: v1.ObjectMeta{
Name: config.VerifyMutatingWebhookConfigurationDebugName,
},
Webhooks: []admregapi.Webhook{
generateDebugWebhook(
Webhooks: []admregapi.MutatingWebhook{
generateDebugMutatingWebhook(
config.VerifyMutatingWebhookName,
url,
caData,

View file

@ -61,10 +61,43 @@ func (wrc *WebhookRegistrationClient) constructOwner() v1.OwnerReference {
}
}
func generateDebugWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.Webhook {
// func generateDebugWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.Webhook {
// sideEffect := admregapi.SideEffectClassNoneOnDryRun
// failurePolicy := admregapi.Ignore
// return admregapi.Webhook{
// Name: name,
// ClientConfig: admregapi.WebhookClientConfig{
// URL: &url,
// CABundle: caData,
// },
// SideEffects: &sideEffect,
// Rules: []admregapi.RuleWithOperations{
// admregapi.RuleWithOperations{
// Operations: operationTypes,
// Rule: admregapi.Rule{
// APIGroups: []string{
// apiGroups,
// },
// APIVersions: []string{
// apiVersions,
// },
// Resources: []string{
// resource,
// },
// },
// },
// },
// AdmissionReviewVersions: []string{"v1beta1"},
// TimeoutSeconds: &timeoutSeconds,
// FailurePolicy: &failurePolicy,
// }
// }
// debug mutating webhook
func generateDebugMutatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook {
sideEffect := admregapi.SideEffectClassNoneOnDryRun
failurePolicy := admregapi.Ignore
return admregapi.Webhook{
return admregapi.MutatingWebhook{
Name: name,
ClientConfig: admregapi.WebhookClientConfig{
URL: &url,
@ -93,10 +126,116 @@ func generateDebugWebhook(name, url string, caData []byte, validate bool, timeou
}
}
func generateWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.Webhook {
func generateDebugValidatingWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook {
sideEffect := admregapi.SideEffectClassNoneOnDryRun
failurePolicy := admregapi.Ignore
return admregapi.Webhook{
return admregapi.ValidatingWebhook{
Name: name,
ClientConfig: admregapi.WebhookClientConfig{
URL: &url,
CABundle: caData,
},
SideEffects: &sideEffect,
Rules: []admregapi.RuleWithOperations{
admregapi.RuleWithOperations{
Operations: operationTypes,
Rule: admregapi.Rule{
APIGroups: []string{
apiGroups,
},
APIVersions: []string{
apiVersions,
},
Resources: []string{
resource,
},
},
},
},
AdmissionReviewVersions: []string{"v1beta1"},
TimeoutSeconds: &timeoutSeconds,
FailurePolicy: &failurePolicy,
}
}
// func generateWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.Webhook {
// sideEffect := admregapi.SideEffectClassNoneOnDryRun
// failurePolicy := admregapi.Ignore
// return admregapi.Webhook{
// Name: name,
// ClientConfig: admregapi.WebhookClientConfig{
// Service: &admregapi.ServiceReference{
// Namespace: config.KubePolicyNamespace,
// Name: config.WebhookServiceName,
// Path: &servicePath,
// },
// CABundle: caData,
// },
// SideEffects: &sideEffect,
// Rules: []admregapi.RuleWithOperations{
// admregapi.RuleWithOperations{
// Operations: operationTypes,
// Rule: admregapi.Rule{
// APIGroups: []string{
// apiGroups,
// },
// APIVersions: []string{
// apiVersions,
// },
// Resources: []string{
// resource,
// },
// },
// },
// },
// AdmissionReviewVersions: []string{"v1beta1"},
// TimeoutSeconds: &timeoutSeconds,
// FailurePolicy: &failurePolicy,
// }
// }
// mutating webhook
func generateMutatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.MutatingWebhook {
sideEffect := admregapi.SideEffectClassNoneOnDryRun
failurePolicy := admregapi.Ignore
return admregapi.MutatingWebhook{
Name: name,
ClientConfig: admregapi.WebhookClientConfig{
Service: &admregapi.ServiceReference{
Namespace: config.KubePolicyNamespace,
Name: config.WebhookServiceName,
Path: &servicePath,
},
CABundle: caData,
},
SideEffects: &sideEffect,
Rules: []admregapi.RuleWithOperations{
admregapi.RuleWithOperations{
Operations: operationTypes,
Rule: admregapi.Rule{
APIGroups: []string{
apiGroups,
},
APIVersions: []string{
apiVersions,
},
Resources: []string{
resource,
},
},
},
},
AdmissionReviewVersions: []string{"v1beta1"},
TimeoutSeconds: &timeoutSeconds,
FailurePolicy: &failurePolicy,
}
}
// validating webhook
func generateValidatingWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.ValidatingWebhook {
sideEffect := admregapi.SideEffectClassNoneOnDryRun
failurePolicy := admregapi.Ignore
return admregapi.ValidatingWebhook{
Name: name,
ClientConfig: admregapi.WebhookClientConfig{
Service: &admregapi.ServiceReference{

View file

@ -18,8 +18,8 @@ func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig(caDa
wrc.constructOwner(),
},
},
Webhooks: []admregapi.Webhook{
generateWebhook(
Webhooks: []admregapi.ValidatingWebhook{
generateValidatingWebhook(
config.PolicyValidatingWebhookName,
config.PolicyValidatingWebhookServicePath,
caData,
@ -42,8 +42,8 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig
ObjectMeta: v1.ObjectMeta{
Name: config.PolicyValidatingWebhookConfigurationDebugName,
},
Webhooks: []admregapi.Webhook{
generateDebugWebhook(
Webhooks: []admregapi.ValidatingWebhook{
generateDebugValidatingWebhook(
config.PolicyValidatingWebhookName,
url,
caData,
@ -66,8 +66,8 @@ func (wrc *WebhookRegistrationClient) contructPolicyMutatingWebhookConfig(caData
wrc.constructOwner(),
},
},
Webhooks: []admregapi.Webhook{
generateWebhook(
Webhooks: []admregapi.MutatingWebhook{
generateMutatingWebhook(
config.PolicyMutatingWebhookName,
config.PolicyMutatingWebhookServicePath,
caData,
@ -89,8 +89,8 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyMutatingWebhookConfig(c
ObjectMeta: v1.ObjectMeta{
Name: config.PolicyMutatingWebhookConfigurationDebugName,
},
Webhooks: []admregapi.Webhook{
generateDebugWebhook(
Webhooks: []admregapi.MutatingWebhook{
generateDebugMutatingWebhook(
config.PolicyMutatingWebhookName,
url,
caData,

View file

@ -2,9 +2,11 @@ package webhookconfig
import (
"errors"
"fmt"
"sync"
"time"
"github.com/go-logr/logr"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/config"
client "github.com/nirmata/kyverno/pkg/dclient"
@ -27,6 +29,7 @@ type WebhookRegistrationClient struct {
// serverIP should be used if running Kyverno out of clutser
serverIP string
timeoutSeconds int32
log logr.Logger
}
// NewWebhookRegistrationClient creates new WebhookRegistrationClient instance
@ -34,19 +37,22 @@ func NewWebhookRegistrationClient(
clientConfig *rest.Config,
client *client.Client,
serverIP string,
webhookTimeout int32) *WebhookRegistrationClient {
webhookTimeout int32,
log logr.Logger) *WebhookRegistrationClient {
return &WebhookRegistrationClient{
clientConfig: clientConfig,
client: client,
serverIP: serverIP,
timeoutSeconds: webhookTimeout,
log: log.WithName("WebhookRegistrationClient"),
}
}
// Register creates admission webhooks configs on cluster
func (wrc *WebhookRegistrationClient) Register() error {
logger := wrc.log.WithName("Register")
if wrc.serverIP != "" {
glog.Infof("Registering webhook with url https://%s\n", wrc.serverIP)
logger.Info("Registering webhook", "url", fmt.Sprintf("https://%s", wrc.serverIP))
}
// For the case if cluster already has this configs
@ -88,6 +94,7 @@ func (wrc *WebhookRegistrationClient) RemoveWebhookConfigurations(cleanUp chan<-
// used to forward request to kyverno webhooks to apply policeis
// Mutationg webhook is be used for Mutating purpose
func (wrc *WebhookRegistrationClient) CreateResourceMutatingWebhookConfiguration() error {
logger := wrc.log
var caData []byte
var config *admregapi.MutatingWebhookConfiguration
@ -108,16 +115,17 @@ func (wrc *WebhookRegistrationClient) CreateResourceMutatingWebhookConfiguration
}
_, err := wrc.client.CreateResource(MutatingWebhookConfigurationKind, "", *config, false)
if errorsapi.IsAlreadyExists(err) {
glog.V(4).Infof("resource mutating webhook configuration %s, already exists. not creating one", config.Name)
logger.V(4).Info("resource mutating webhook configuration already exists. not creating one", "name", config.Name)
return nil
}
if err != nil {
glog.V(4).Infof("failed to create resource mutating webhook configuration %s: %v", config.Name, err)
logger.Error(err, "failed to create resource mutating webhook configuration", "name", config.Name)
return err
}
return nil
}
//CreateResourceValidatingWebhookConfiguration ...
func (wrc *WebhookRegistrationClient) CreateResourceValidatingWebhookConfiguration() error {
var caData []byte
var config *admregapi.ValidatingWebhookConfiguration
@ -134,14 +142,15 @@ func (wrc *WebhookRegistrationClient) CreateResourceValidatingWebhookConfigurati
// clientConfig - service
config = wrc.constructValidatingWebhookConfig(caData)
}
logger := wrc.log.WithValues("kind", ValidatingWebhookConfigurationKind, "name", config.Name)
_, err := wrc.client.CreateResource(ValidatingWebhookConfigurationKind, "", *config, false)
if errorsapi.IsAlreadyExists(err) {
glog.V(4).Infof("resource validating webhook configuration %s, already exists. not creating one", config.Name)
logger.V(4).Info("resource already exists. not create one")
return nil
}
if err != nil {
glog.V(4).Infof("failed to create resource validating webhook configuration %s: %v", config.Name, err)
logger.Error(err, "failed to create resource")
return err
}
return nil
@ -168,13 +177,13 @@ func (wrc *WebhookRegistrationClient) createPolicyValidatingWebhookConfiguration
// clientConfig - service
config = wrc.contructPolicyValidatingWebhookConfig(caData)
}
logger := wrc.log.WithValues("kind", ValidatingWebhookConfigurationKind, "name", config.Name)
// create validating webhook configuration resource
if _, err := wrc.client.CreateResource(ValidatingWebhookConfigurationKind, "", *config, false); err != nil {
return err
}
glog.V(4).Infof("created Validating Webhook Configuration %s ", config.Name)
logger.V(4).Info("created resource")
return nil
}

View file

@ -3,7 +3,6 @@ package webhookconfig
import (
"fmt"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/config"
admregapi "k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
@ -11,15 +10,15 @@ import (
)
func (wrc *WebhookRegistrationClient) constructDebugMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
logger := wrc.log
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.MutatingWebhookServicePath)
glog.V(4).Infof("Debug MutatingWebhookConfig is registered with url %s\n", url)
logger.V(4).Info("Debug MutatingWebhookConfig registed", "url", url)
return &admregapi.MutatingWebhookConfiguration{
ObjectMeta: v1.ObjectMeta{
Name: config.MutatingWebhookConfigurationDebugName,
},
Webhooks: []admregapi.Webhook{
generateDebugWebhook(
Webhooks: []admregapi.MutatingWebhook{
generateDebugMutatingWebhook(
config.MutatingWebhookName,
url,
caData,
@ -42,8 +41,8 @@ func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(caData []by
wrc.constructOwner(),
},
},
Webhooks: []admregapi.Webhook{
generateWebhook(
Webhooks: []admregapi.MutatingWebhook{
generateMutatingWebhook(
config.MutatingWebhookName,
config.MutatingWebhookServicePath,
caData,
@ -68,32 +67,31 @@ func (wrc *WebhookRegistrationClient) GetResourceMutatingWebhookConfigName() str
//RemoveResourceMutatingWebhookConfiguration removes mutating webhook configuration for all resources
func (wrc *WebhookRegistrationClient) RemoveResourceMutatingWebhookConfiguration() error {
configName := wrc.GetResourceMutatingWebhookConfigName()
logger := wrc.log.WithValues("kind", MutatingWebhookConfigurationKind, "name", configName)
// delete webhook configuration
err := wrc.client.DeleteResource(MutatingWebhookConfigurationKind, "", configName, false)
if errors.IsNotFound(err) {
glog.V(4).Infof("resource webhook configuration %s does not exits, so not deleting", configName)
logger.Error(err, "resource does not exit")
return nil
}
if err != nil {
glog.V(4).Infof("failed to delete resource webhook configuration %s: %v", configName, err)
logger.V(4).Info("failed to delete resource")
return err
}
glog.V(4).Infof("deleted resource webhook configuration %s", configName)
logger.V(4).Info("deleted resource")
return nil
}
func (wrc *WebhookRegistrationClient) constructDebugValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.ValidatingWebhookServicePath)
glog.V(4).Infof("Debug ValidatingWebhookConfig is registered with url %s\n", url)
return &admregapi.ValidatingWebhookConfiguration{
ObjectMeta: v1.ObjectMeta{
Name: config.ValidatingWebhookConfigurationDebugName,
},
Webhooks: []admregapi.Webhook{
generateDebugWebhook(
Webhooks: []admregapi.ValidatingWebhook{
generateDebugValidatingWebhook(
config.ValidatingWebhookName,
url,
caData,
@ -116,8 +114,8 @@ func (wrc *WebhookRegistrationClient) constructValidatingWebhookConfig(caData []
wrc.constructOwner(),
},
},
Webhooks: []admregapi.Webhook{
generateWebhook(
Webhooks: []admregapi.ValidatingWebhook{
generateValidatingWebhook(
config.ValidatingWebhookName,
config.ValidatingWebhookServicePath,
caData,
@ -141,15 +139,16 @@ func (wrc *WebhookRegistrationClient) GetResourceValidatingWebhookConfigName() s
func (wrc *WebhookRegistrationClient) RemoveResourceValidatingWebhookConfiguration() error {
configName := wrc.GetResourceValidatingWebhookConfigName()
logger := wrc.log.WithValues("kind", ValidatingWebhookConfigurationKind, "name", configName)
err := wrc.client.DeleteResource(ValidatingWebhookConfigurationKind, "", configName, false)
if errors.IsNotFound(err) {
glog.V(4).Infof("resource webhook configuration %s does not exits, so not deleting", configName)
logger.Error(err, "resource does not exist; deleted already")
return nil
}
if err != nil {
glog.V(4).Infof("failed to delete resource webhook configuration %s: %v", configName, err)
logger.Error(err, "failed to delete the resource")
return err
}
glog.V(4).Infof("deleted resource webhook configuration %s", configName)
logger.Info("resource deleted")
return nil
}

View file

@ -3,7 +3,7 @@ package webhookconfig
import (
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
checker "github.com/nirmata/kyverno/pkg/checker"
"github.com/tevino/abool"
mconfiginformer "k8s.io/client-go/informers/admissionregistration/v1beta1"
@ -23,6 +23,7 @@ type ResourceWebhookRegister struct {
vWebhookConfigLister mconfiglister.ValidatingWebhookConfigurationLister
webhookRegistrationClient *WebhookRegistrationClient
RunValidationInMutatingWebhook string
log logr.Logger
}
// NewResourceWebhookRegister returns a new instance of ResourceWebhookRegister manager
@ -32,6 +33,7 @@ func NewResourceWebhookRegister(
vconfigwebhookinformer mconfiginformer.ValidatingWebhookConfigurationInformer,
webhookRegistrationClient *WebhookRegistrationClient,
runValidationInMutatingWebhook string,
log logr.Logger,
) *ResourceWebhookRegister {
return &ResourceWebhookRegister{
pendingCreation: abool.New(),
@ -42,52 +44,54 @@ func NewResourceWebhookRegister(
vWebhookConfigLister: vconfigwebhookinformer.Lister(),
webhookRegistrationClient: webhookRegistrationClient,
RunValidationInMutatingWebhook: runValidationInMutatingWebhook,
log: log,
}
}
//RegisterResourceWebhook registers a resource webhook
func (rww *ResourceWebhookRegister) RegisterResourceWebhook() {
logger := rww.log
// drop the request if creation is in processing
if rww.pendingCreation.IsSet() {
glog.V(3).Info("resource webhook configuration is in pending creation, skip the request")
logger.V(3).Info("resource webhook configuration is in pending creation, skip the request")
return
}
timeDiff := time.Since(rww.LastReqTime.Time())
if timeDiff < checker.DefaultDeadline {
glog.V(3).Info("Verified webhook status, creating webhook configuration")
logger.V(3).Info("verified webhook status, creating webhook configuration")
go func() {
mutatingConfigName := rww.webhookRegistrationClient.GetResourceMutatingWebhookConfigName()
mutatingConfig, _ := rww.mWebhookConfigLister.Get(mutatingConfigName)
if mutatingConfig != nil {
glog.V(4).Info("mutating webhoook configuration already exists")
logger.V(4).Info("mutating webhoook configuration already exists")
} else {
rww.pendingCreation.Set()
err1 := rww.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration()
rww.pendingCreation.UnSet()
if err1 != nil {
glog.Errorf("failed to create resource mutating webhook configuration: %v, re-queue creation request", err1)
logger.Error(err1, "failed to create resource mutating webhook configuration, re-queue creation request")
rww.RegisterResourceWebhook()
return
}
glog.V(3).Info("Successfully created mutating webhook configuration for resources")
logger.V(3).Info("successfully created mutating webhook configuration for resources")
}
if rww.RunValidationInMutatingWebhook != "true" {
validatingConfigName := rww.webhookRegistrationClient.GetResourceValidatingWebhookConfigName()
validatingConfig, _ := rww.vWebhookConfigLister.Get(validatingConfigName)
if validatingConfig != nil {
glog.V(4).Info("validating webhoook configuration already exists")
logger.V(4).Info("validating webhoook configuration already exists")
} else {
rww.pendingCreation.Set()
err2 := rww.webhookRegistrationClient.CreateResourceValidatingWebhookConfiguration()
rww.pendingCreation.UnSet()
if err2 != nil {
glog.Errorf("failed to create resource validating webhook configuration: %v, re-queue creation request", err2)
logger.Error(err2, "failed to create resource validating webhook configuration; re-queue creation request")
rww.RegisterResourceWebhook()
return
}
glog.V(3).Info("Successfully created validating webhook configuration for resources")
logger.V(3).Info("successfully created validating webhook configuration for resources")
}
}
}()
@ -96,19 +100,20 @@ func (rww *ResourceWebhookRegister) RegisterResourceWebhook() {
//Run starts the ResourceWebhookRegister manager
func (rww *ResourceWebhookRegister) Run(stopCh <-chan struct{}) {
logger := rww.log
// wait for cache to populate first time
if !cache.WaitForCacheSync(stopCh, rww.mwebhookconfigSynced, rww.vwebhookconfigSynced) {
glog.Error("configuration: failed to sync webhook informer cache")
logger.Info("configuration: failed to sync webhook informer cache")
}
}
// RemoveResourceWebhookConfiguration removes the resource webhook configurations
func (rww *ResourceWebhookRegister) RemoveResourceWebhookConfiguration() error {
logger := rww.log
mutatingConfigName := rww.webhookRegistrationClient.GetResourceMutatingWebhookConfigName()
mutatingConfig, err := rww.mWebhookConfigLister.Get(mutatingConfigName)
if err != nil {
glog.V(4).Infof("failed to list mutating webhook config: %v", err)
logger.Error(err, "failed to list mutating webhook config")
return err
}
if mutatingConfig != nil {
@ -116,14 +121,14 @@ func (rww *ResourceWebhookRegister) RemoveResourceWebhookConfiguration() error {
if err != nil {
return err
}
glog.V(3).Info("removed mutating resource webhook configuration")
logger.V(3).Info("emoved mutating resource webhook configuration")
}
if rww.RunValidationInMutatingWebhook != "true" {
validatingConfigName := rww.webhookRegistrationClient.GetResourceValidatingWebhookConfigName()
validatingConfig, err := rww.vWebhookConfigLister.Get(validatingConfigName)
if err != nil {
glog.V(4).Infof("failed to list validating webhook config: %v", err)
logger.Error(err, "failed to list validating webhook config")
return err
}
if validatingConfig != nil {
@ -131,7 +136,7 @@ func (rww *ResourceWebhookRegister) RemoveResourceWebhookConfiguration() error {
if err != nil {
return err
}
glog.V(3).Info("removed validating resource webhook configuration")
logger.V(3).Info("removed validating resource webhook configuration")
}
}
return nil

View file

@ -1 +0,0 @@
package webhooks_test

View file

@ -7,7 +7,7 @@ import (
yamlv2 "gopkg.in/yaml.v2"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/response"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -38,7 +38,7 @@ var operationToPastTense = map[string]string{
"test": "tested",
}
func generateAnnotationPatches(engineResponses []response.EngineResponse) []byte {
func generateAnnotationPatches(engineResponses []response.EngineResponse, log logr.Logger) []byte {
var annotations map[string]string
for _, er := range engineResponses {
@ -53,7 +53,7 @@ func generateAnnotationPatches(engineResponses []response.EngineResponse) []byte
}
var patchResponse annresponse
value := annotationFromEngineResponses(engineResponses)
value := annotationFromEngineResponses(engineResponses, log)
if value == nil {
// no patches or error while processing patches
return nil
@ -90,21 +90,21 @@ func generateAnnotationPatches(engineResponses []response.EngineResponse) []byte
// check the patch
_, err := jsonpatch.DecodePatch([]byte("[" + string(patchByte) + "]"))
if err != nil {
glog.Errorf("Failed to make patch from annotation'%s', err: %v\n ", string(patchByte), err)
log.Error(err, "failed o build JSON patch for annotation", "patch", string(patchByte))
}
return patchByte
}
func annotationFromEngineResponses(engineResponses []response.EngineResponse) []byte {
func annotationFromEngineResponses(engineResponses []response.EngineResponse, log logr.Logger) []byte {
var annotationContent = make(map[string]string)
for _, engineResponse := range engineResponses {
if !engineResponse.IsSuccesful() {
glog.V(3).Infof("Policy %s failed, skip preparing annotation\n", engineResponse.PolicyResponse.Policy)
log.V(3).Info("skip building annotation; policy failed to apply", "policy", engineResponse.PolicyResponse.Policy)
continue
}
rulePatches := annotationFromPolicyResponse(engineResponse.PolicyResponse)
rulePatches := annotationFromPolicyResponse(engineResponse.PolicyResponse, log)
if rulePatches == nil {
continue
}
@ -126,13 +126,13 @@ func annotationFromEngineResponses(engineResponses []response.EngineResponse) []
return result
}
func annotationFromPolicyResponse(policyResponse response.PolicyResponse) []rulePatch {
func annotationFromPolicyResponse(policyResponse response.PolicyResponse, log logr.Logger) []rulePatch {
var rulePatches []rulePatch
for _, ruleInfo := range policyResponse.Rules {
for _, patch := range ruleInfo.Patches {
var patchmap map[string]interface{}
if err := json.Unmarshal(patch, &patchmap); err != nil {
glog.Errorf("Failed to parse patch bytes, err: %v\n", err)
log.Error(err, "Failed to parse JSON patch bytes")
continue
}
@ -142,14 +142,12 @@ func annotationFromPolicyResponse(policyResponse response.PolicyResponse) []rule
Path: patchmap["path"].(string)}
rulePatches = append(rulePatches, rp)
glog.V(4).Infof("Annotation value prepared: %v\n", rulePatches)
log.V(4).Info("annotation value prepared", "patches", rulePatches)
}
}
if len(rulePatches) == 0 {
return nil
}
return rulePatches
}

View file

@ -4,6 +4,7 @@ import (
"testing"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/log"
"gotest.tools/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -42,7 +43,7 @@ func Test_empty_annotation(t *testing.T) {
patchStr := `{ "op": "replace", "path": "/spec/containers/0/imagePullPolicy", "value": "IfNotPresent" }`
engineResponse := newEngineResponse("mutate-container", "default-imagepullpolicy", []string{patchStr}, true, nil)
annPatches := generateAnnotationPatches([]response.EngineResponse{engineResponse})
annPatches := generateAnnotationPatches([]response.EngineResponse{engineResponse}, log.Log)
expectedPatches := `{"op":"add","path":"/metadata/annotations","value":{"policies.kyverno.io/patches":"default-imagepullpolicy.mutate-container.kyverno.io: replaced /spec/containers/0/imagePullPolicy\n"}}`
assert.Assert(t, string(annPatches) == expectedPatches)
}
@ -54,7 +55,7 @@ func Test_exist_annotation(t *testing.T) {
patchStr := `{ "op": "replace", "path": "/spec/containers/0/imagePullPolicy", "value": "IfNotPresent" }`
engineResponse := newEngineResponse("mutate-container", "default-imagepullpolicy", []string{patchStr}, true, annotation)
annPatches := generateAnnotationPatches([]response.EngineResponse{engineResponse})
annPatches := generateAnnotationPatches([]response.EngineResponse{engineResponse}, log.Log)
expectedPatches := `{"op":"add","path":"/metadata/annotations","value":{"policies.kyverno.io/patches":"default-imagepullpolicy.mutate-container.kyverno.io: replaced /spec/containers/0/imagePullPolicy\n"}}`
assert.Assert(t, string(annPatches) == expectedPatches)
@ -67,7 +68,7 @@ func Test_exist_kyverno_annotation(t *testing.T) {
patchStr := `{ "op": "replace", "path": "/spec/containers/0/imagePullPolicy", "value": "IfNotPresent" }`
engineResponse := newEngineResponse("mutate-container", "default-imagepullpolicy", []string{patchStr}, true, annotation)
annPatches := generateAnnotationPatches([]response.EngineResponse{engineResponse})
annPatches := generateAnnotationPatches([]response.EngineResponse{engineResponse}, log.Log)
expectedPatches := `{"op":"add","path":"/metadata/annotations","value":{"policies.kyverno.io/patches":"default-imagepullpolicy.mutate-container.kyverno.io: replaced /spec/containers/0/imagePullPolicy\n"}}`
assert.Assert(t, string(annPatches) == expectedPatches)
@ -79,11 +80,11 @@ func Test_annotation_nil_patch(t *testing.T) {
}
engineResponse := newEngineResponse("mutate-container", "default-imagepullpolicy", nil, true, annotation)
annPatches := generateAnnotationPatches([]response.EngineResponse{engineResponse})
annPatches := generateAnnotationPatches([]response.EngineResponse{engineResponse}, log.Log)
assert.Assert(t, annPatches == nil)
engineResponseNew := newEngineResponse("mutate-container", "default-imagepullpolicy", []string{""}, true, annotation)
annPatchesNew := generateAnnotationPatches([]response.EngineResponse{engineResponseNew})
annPatchesNew := generateAnnotationPatches([]response.EngineResponse{engineResponseNew}, log.Log)
assert.Assert(t, annPatchesNew == nil)
}
@ -93,7 +94,7 @@ func Test_annotation_failed_Patch(t *testing.T) {
}
engineResponse := newEngineResponse("mutate-container", "default-imagepullpolicy", nil, false, annotation)
annPatches := generateAnnotationPatches([]response.EngineResponse{engineResponse})
annPatches := generateAnnotationPatches([]response.EngineResponse{engineResponse}, log.Log)
assert.Assert(t, annPatches == nil)
}

View file

@ -1,13 +1,12 @@
package webhooks
import (
"github.com/golang/glog"
"k8s.io/api/admission/v1beta1"
)
func (ws *WebhookServer) handleVerifyRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
glog.V(4).Infof("Receive request in mutating webhook '/verify': Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
logger := ws.log.WithValues("action", "verify", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
logger.V(4).Info("incoming request")
return &v1beta1.AdmissionResponse{
Allowed: true,
}

View file

@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/response"
engineutils "github.com/nirmata/kyverno/pkg/engine/utils"
@ -25,14 +25,14 @@ func isResponseSuccesful(engineReponses []response.EngineResponse) bool {
// returns true -> if there is even one policy that blocks resource request
// returns false -> if all the policies are meant to report only, we dont block resource request
func toBlockResource(engineReponses []response.EngineResponse) bool {
func toBlockResource(engineReponses []response.EngineResponse, log logr.Logger) bool {
for _, er := range engineReponses {
if !er.IsSuccesful() && er.PolicyResponse.ValidationFailureAction == Enforce {
glog.V(4).Infof("ValidationFailureAction set to enforce for policy %s , blocking resource request ", er.PolicyResponse.Policy)
log.Info("spec.ValidationFailureAction set to enforcel blocking resource request", "policy", er.PolicyResponse.Policy)
return true
}
}
glog.V(4).Infoln("ValidationFailureAction set to audit, allowing resource request, reporting with policy violation")
log.V(4).Info("sepc.ValidationFailureAction set to auit for all applicable policies;allowing resource reques; reporting with policy violation ")
return false
}
@ -98,14 +98,14 @@ const (
Audit = "audit" // dont block the request on failure, but report failiures as policy violations
)
func processResourceWithPatches(patch []byte, resource []byte) []byte {
func processResourceWithPatches(patch []byte, resource []byte, log logr.Logger) []byte {
if patch == nil {
return resource
}
resource, err := engineutils.ApplyPatchNew(resource, patch)
if err != nil {
glog.Errorf("failed to patch resource: %v", err)
log.Error(err, "failed to patch resource:")
return nil
}
return resource

View file

@ -5,7 +5,7 @@ import (
"time"
backoff "github.com/cenkalti/backoff"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@ -23,37 +23,41 @@ type Generator struct {
ch chan kyverno.GenerateRequestSpec
client *kyvernoclient.Clientset
stopCh <-chan struct{}
log logr.Logger
}
//NewGenerator returns a new instance of Generate-Request resource generator
func NewGenerator(client *kyvernoclient.Clientset, stopCh <-chan struct{}) *Generator {
func NewGenerator(client *kyvernoclient.Clientset, stopCh <-chan struct{}, log logr.Logger) *Generator {
gen := &Generator{
ch: make(chan kyverno.GenerateRequestSpec, 1000),
client: client,
stopCh: stopCh,
log: log,
}
return gen
}
//Create to create generate request resoruce (blocking call if channel is full)
func (g *Generator) Create(gr kyverno.GenerateRequestSpec) error {
glog.V(4).Infof("create GR %v", gr)
logger := g.log
logger.V(4).Info("creating Generate Request", "request", gr)
// Send to channel
select {
case g.ch <- gr:
return nil
case <-g.stopCh:
glog.Info("shutting down channel")
logger.Info("shutting down channel")
return fmt.Errorf("shutting down gr create channel")
}
}
// Run starts the generate request spec
func (g *Generator) Run(workers int) {
logger := g.log
defer utilruntime.HandleCrash()
glog.V(4).Info("Started generate request")
logger.V(4).Info("starting")
defer func() {
glog.V(4).Info("Shutting down generate request")
logger.V(4).Info("shutting down")
}()
for i := 0; i < workers; i++ {
go wait.Until(g.process, time.Second, g.stopCh)
@ -62,17 +66,18 @@ func (g *Generator) Run(workers int) {
}
func (g *Generator) process() {
logger := g.log
for r := range g.ch {
glog.V(4).Infof("received generate request %v", r)
logger.V(4).Info("recieved generate request", "request", r)
if err := g.generate(r); err != nil {
glog.Errorf("Failed to create Generate Request CR: %v", err)
logger.Error(err, "failed to generate request CR")
}
}
}
func (g *Generator) generate(grSpec kyverno.GenerateRequestSpec) error {
// create a generate request
if err := retryCreateResource(g.client, grSpec); err != nil {
if err := retryCreateResource(g.client, grSpec, g.log); err != nil {
return err
}
return nil
@ -81,7 +86,10 @@ func (g *Generator) generate(grSpec kyverno.GenerateRequestSpec) error {
// -> receiving channel to take requests to create request
// use worker pattern to read and create the CR resource
func retryCreateResource(client *kyvernoclient.Clientset, grSpec kyverno.GenerateRequestSpec) error {
func retryCreateResource(client *kyvernoclient.Clientset,
grSpec kyverno.GenerateRequestSpec,
log logr.Logger,
) error {
var i int
var err error
createResource := func() error {
@ -95,7 +103,7 @@ func retryCreateResource(client *kyvernoclient.Clientset, grSpec kyverno.Generat
// gr.Status.State = kyverno.Pending
// generate requests created in kyverno namespace
_, err = client.KyvernoV1().GenerateRequests("kyverno").Create(&gr)
glog.V(4).Infof("retry %v create generate request", i)
log.V(4).Info("retrying create generate request CR", "retryCount", i, "name", gr.GetGenerateName(), "namespace", gr.GetNamespace())
i++
return err
}

View file

@ -1,7 +1,6 @@
package webhooks
import (
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
@ -13,19 +12,19 @@ import (
//HandleGenerate handles admission-requests for policies with generate rules
func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, policies []kyverno.ClusterPolicy, patchedResource []byte, roles, clusterRoles []string) (bool, string) {
logger := ws.log.WithValues("action", "generation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
logger.V(4).Info("incoming request")
var engineResponses []response.EngineResponse
// convert RAW to unstructured
resource, err := utils.ConvertToUnstructured(request.Object.Raw)
if err != nil {
//TODO: skip applying the admission control ?
glog.Errorf("unable to convert raw resource to unstructured: %v", err)
logger.Error(err, "failed to convert RAR resource to unstructured format")
return true, ""
}
// CREATE resources, do not have name, assigned in admission-request
glog.V(4).Infof("Handle Generate: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
userRequestInfo := kyverno.RequestInfo{
Roles: roles,
@ -36,16 +35,16 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
// load incoming resource into the context
err = ctx.AddResource(request.Object.Raw)
if err != nil {
glog.Infof("Failed to load resource in context:%v", err)
logger.Error(err, "failed to load incoming resource in context")
}
err = ctx.AddUserInfo(userRequestInfo)
if err != nil {
glog.Infof("Failed to load userInfo in context:%v", err)
logger.Error(err, "failed to load userInfo in context")
}
// load service account in context
err = ctx.AddSA(userRequestInfo.AdmissionUserInfo.Username)
if err != nil {
glog.Infof("Failed to load service account in context:%v", err)
logger.Error(err, "failed to load service account in context")
}
policyContext := engine.PolicyContext{

View file

@ -1,9 +1,6 @@
package webhooks
import (
"time"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
@ -19,8 +16,8 @@ import (
// HandleMutation handles mutating webhook admission request
// return value: generated patches
func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resource unstructured.Unstructured, policies []kyverno.ClusterPolicy, roles, clusterRoles []string) []byte {
glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
logger := ws.log.WithValues("action", "mutation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
logger.V(4).Info("incoming request")
var patches [][]byte
var policyStats []policyctr.PolicyStat
@ -70,16 +67,16 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
// load incoming resource into the context
err = ctx.AddResource(request.Object.Raw)
if err != nil {
glog.Infof("Failed to load resource in context:%v", err)
logger.Error(err, "failed to load incoming resource in context")
}
err = ctx.AddUserInfo(userRequestInfo)
if err != nil {
glog.Infof("Failed to load userInfo in context:%v", err)
logger.Error(err, "failed to load userInfo in context")
}
err = ctx.AddSA(userRequestInfo.AdmissionUserInfo.Username)
if err != nil {
glog.Infof("Failed to load service account in context:%v", err)
logger.Error(err, "failed to load service account in context")
}
policyContext := engine.PolicyContext{
@ -89,8 +86,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
}
for _, policy := range policies {
glog.V(2).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
logger.V(2).Info("evaluating policy", "policy", policy.Name)
policyContext.Policy = policy
engineResponse := engine.Mutate(policyContext)
@ -98,27 +94,24 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
// Gather policy application statistics
gatherStat(policy.Name, engineResponse.PolicyResponse)
if !engineResponse.IsSuccesful() {
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, resource.GetNamespace(), resource.GetName())
logger.V(4).Info("failed to apply policy", "policy", policy.Name)
continue
}
// gather patches
patches = append(patches, engineResponse.GetPatches()...)
glog.V(4).Infof("Mutation from policy %s has applied successfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
logger.Info("mutation rules from policy applied succesfully", "policy", policy.Name)
policyContext.NewResource = engineResponse.PatchedResource
}
// generate annotations
if annPatches := generateAnnotationPatches(engineResponses); annPatches != nil {
if annPatches := generateAnnotationPatches(engineResponses, logger); annPatches != nil {
patches = append(patches, annPatches)
}
// report time
reportTime := time.Now()
// AUDIT
// generate violation when response fails
pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses)
pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses, logger)
ws.pvGenerator.Add(pvInfos...)
// REPORTING EVENTS
// Scenario 1:
@ -128,7 +121,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
// all policies were applied succesfully.
// create an event on the resource
// ADD EVENTS
events := generateEvents(engineResponses, false, (request.Operation == v1beta1.Update))
events := generateEvents(engineResponses, false, (request.Operation == v1beta1.Update), logger)
ws.eventGen.Add(events...)
sendStat(false)
@ -136,19 +129,15 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
// debug info
func() {
if len(patches) != 0 {
glog.V(4).Infof("Patches generated for %s/%s/%s, operation=%v:\n %v",
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.Operation, string(engineutils.JoinPatches(patches)))
logger.V(4).Info("JSON patches generated")
}
// if any of the policies fails, print out the error
if !isResponseSuccesful(engineResponses) {
glog.Errorf("Failed to mutate the resource, report as violation: %s\n", getErrorMsg(engineResponses))
logger.Info("failed to apply mutation rules on the resource, reporting policy violation", "errors", getErrorMsg(engineResponses))
}
}()
// report time end
glog.V(4).Infof("report: %v %s/%s/%s", time.Since(reportTime), resource.GetKind(), resource.GetNamespace(), resource.GetName())
// patches holds all the successful patches, if no patch is created, it returns nil
return engineutils.JoinPatches(patches)
}

View file

@ -8,7 +8,7 @@ import (
"strings"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/utils"
@ -17,12 +17,13 @@ import (
)
func (ws *WebhookServer) handlePolicyMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
logger := ws.log.WithValues("action", "policymutation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
var policy *kyverno.ClusterPolicy
raw := request.Object.Raw
//TODO: can this happen? wont this be picked by OpenAPI spec schema ?
if err := json.Unmarshal(raw, &policy); err != nil {
glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err)
logger.Error(err, "faield to unmarshall policy admission request")
return &v1beta1.AdmissionResponse{
Allowed: true,
Result: &metav1.Status{
@ -31,10 +32,9 @@ func (ws *WebhookServer) handlePolicyMutation(request *v1beta1.AdmissionRequest)
}
}
// Generate JSON Patches for defaults
patches, updateMsgs := generateJSONPatchesForDefaults(policy)
patches, updateMsgs := generateJSONPatchesForDefaults(policy, logger)
if patches != nil {
patchType := v1beta1.PatchTypeJSONPatch
glog.V(4).Infof("defaulted values %v policy %s", updateMsgs, policy.Name)
return &v1beta1.AdmissionResponse{
Allowed: true,
Result: &metav1.Status{
@ -44,35 +44,34 @@ func (ws *WebhookServer) handlePolicyMutation(request *v1beta1.AdmissionRequest)
PatchType: &patchType,
}
}
glog.V(4).Infof("nothing to default for policy %s", policy.Name)
return &v1beta1.AdmissionResponse{
Allowed: true,
}
}
func generateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy) ([]byte, []string) {
func generateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy, log logr.Logger) ([]byte, []string) {
var patches [][]byte
var updateMsgs []string
// default 'ValidationFailureAction'
if patch, updateMsg := defaultvalidationFailureAction(policy); patch != nil {
if patch, updateMsg := defaultvalidationFailureAction(policy, log); patch != nil {
patches = append(patches, patch)
updateMsgs = append(updateMsgs, updateMsg)
}
// default 'Background'
if patch, updateMsg := defaultBackgroundFlag(policy); patch != nil {
if patch, updateMsg := defaultBackgroundFlag(policy, log); patch != nil {
patches = append(patches, patch)
updateMsgs = append(updateMsgs, updateMsg)
}
patch, errs := generatePodControllerRule(*policy)
patch, errs := generatePodControllerRule(*policy, log)
if len(errs) > 0 {
var errMsgs []string
for _, err := range errs {
errMsgs = append(errMsgs, err.Error())
log.Error(err, "failed to generate pod controller rule")
}
glog.Errorf("failed auto generating rule for pod controllers: %s", errMsgs)
updateMsgs = append(updateMsgs, strings.Join(errMsgs, ";"))
}
@ -81,11 +80,11 @@ func generateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy) ([]byte, []st
return utils.JoinPatches(patches), updateMsgs
}
func defaultBackgroundFlag(policy *kyverno.ClusterPolicy) ([]byte, string) {
func defaultBackgroundFlag(policy *kyverno.ClusterPolicy, log logr.Logger) ([]byte, string) {
// default 'Background' flag to 'true' if not specified
defaultVal := true
if policy.Spec.Background == nil {
glog.V(4).Infof("default policy %s 'Background' to '%s'", policy.Name, strconv.FormatBool(true))
log.V(4).Info("setting default value", "spec.background", true)
jsonPatch := struct {
Path string `json:"path"`
Op string `json:"op"`
@ -97,19 +96,19 @@ func defaultBackgroundFlag(policy *kyverno.ClusterPolicy) ([]byte, string) {
}
patchByte, err := json.Marshal(jsonPatch)
if err != nil {
glog.Errorf("failed to set default 'Background' to '%s' for policy %s", strconv.FormatBool(true), policy.Name)
log.Error(err, "failed to set default value", "spec.background", true)
return nil, ""
}
glog.V(4).Infof("generate JSON Patch to set default 'Background' to '%s' for policy %s", strconv.FormatBool(true), policy.Name)
log.Info("generated JSON Patch to set default", "spec.background", true)
return patchByte, fmt.Sprintf("default 'Background' to '%s'", strconv.FormatBool(true))
}
return nil, ""
}
func defaultvalidationFailureAction(policy *kyverno.ClusterPolicy) ([]byte, string) {
func defaultvalidationFailureAction(policy *kyverno.ClusterPolicy, log logr.Logger) ([]byte, string) {
// default ValidationFailureAction to "audit" if not specified
if policy.Spec.ValidationFailureAction == "" {
glog.V(4).Infof("defaulting policy %s 'ValidationFailureAction' to '%s'", policy.Name, Audit)
log.V(4).Info("setting defautl value", "spec.validationFailureAction", Audit)
jsonPatch := struct {
Path string `json:"path"`
Op string `json:"op"`
@ -121,10 +120,10 @@ func defaultvalidationFailureAction(policy *kyverno.ClusterPolicy) ([]byte, stri
}
patchByte, err := json.Marshal(jsonPatch)
if err != nil {
glog.Errorf("failed to set default 'ValidationFailureAction' to '%s' for policy %s", Audit, policy.Name)
log.Error(err, "failed to default value", "spec.validationFailureAction", Audit)
return nil, ""
}
glog.V(4).Infof("generate JSON Patch to set default 'ValidationFailureAction' to '%s' for policy %s", Audit, policy.Name)
log.Info("generated JSON Patch to set default", "spec.validationFailureAction", Audit)
return patchByte, fmt.Sprintf("default 'ValidationFailureAction' to '%s'", Audit)
}
return nil, ""
@ -140,7 +139,7 @@ func defaultvalidationFailureAction(policy *kyverno.ClusterPolicy) ([]byte, stri
// make sure all fields are applicable to pod cotrollers
// generatePodControllerRule returns two patches: rulePatches and annotation patch(if necessary)
func generatePodControllerRule(policy kyverno.ClusterPolicy) (patches [][]byte, errs []error) {
func generatePodControllerRule(policy kyverno.ClusterPolicy, log logr.Logger) (patches [][]byte, errs []error) {
ann := policy.GetAnnotations()
controllers, ok := ann[engine.PodControllersAnnotation]
@ -159,10 +158,9 @@ func generatePodControllerRule(policy kyverno.ClusterPolicy) (patches [][]byte,
if controllers == "none" {
return nil, nil
}
log.V(3).Info("auto generating rule for pod controllers", "controlers", controllers)
glog.V(3).Infof("Auto generating rule for pod controller: %s", controllers)
p, err := generateRulePatches(policy, controllers)
p, err := generateRulePatches(policy, controllers, log)
patches = append(patches, p...)
errs = append(errs, err...)
return
@ -197,7 +195,7 @@ func createRuleMap(rules []kyverno.Rule) map[string]kyvernoRule {
}
// generateRulePatches generates rule for podControllers based on scenario A and C
func generateRulePatches(policy kyverno.ClusterPolicy, controllers string) (rulePatches [][]byte, errs []error) {
func generateRulePatches(policy kyverno.ClusterPolicy, controllers string, log logr.Logger) (rulePatches [][]byte, errs []error) {
var genRule kyvernoRule
insertIdx := len(policy.Spec.Rules)
@ -210,7 +208,7 @@ func generateRulePatches(policy kyverno.ClusterPolicy, controllers string) (rule
for _, rule := range policy.Spec.Rules {
patchPostion := insertIdx
genRule = generateRuleForControllers(rule, controllers)
genRule = generateRuleForControllers(rule, controllers, log)
if reflect.DeepEqual(genRule, kyvernoRule{}) {
continue
}
@ -272,7 +270,7 @@ type kyvernoRule struct {
Validation *kyverno.Validation `json:"validate,omitempty"`
}
func generateRuleForControllers(rule kyverno.Rule, controllers string) kyvernoRule {
func generateRuleForControllers(rule kyverno.Rule, controllers string, log logr.Logger) kyvernoRule {
if strings.HasPrefix(rule.Name, "autogen-") {
return kyvernoRule{}
}
@ -292,7 +290,7 @@ func generateRuleForControllers(rule kyverno.Rule, controllers string) kyvernoRu
if controllers == "all" {
if match.ResourceDescription.Name != "" || match.ResourceDescription.Selector != nil ||
exclude.ResourceDescription.Name != "" || exclude.ResourceDescription.Selector != nil {
glog.Warningf("Rule '%s' skip generating rule on pod controllers: Name / Selector in resource decription may not be applicable.", rule.Name)
log.Info("skip generating rule on pod controllers: Name / Selector in resource decription may not be applicable.", "rule", rule.Name)
return kyvernoRule{}
}
controllers = engine.PodControllers

View file

@ -7,6 +7,7 @@ import (
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/utils"
"github.com/nirmata/kyverno/pkg/log"
"gotest.tools/assert"
)
@ -28,7 +29,7 @@ func TestGeneratePodControllerRule_NilAnnotation(t *testing.T) {
var policy kyverno.ClusterPolicy
assert.Assert(t, json.Unmarshal(policyRaw, &policy))
patches, errs := generatePodControllerRule(policy)
patches, errs := generatePodControllerRule(policy, log.Log)
assert.Assert(t, len(errs) == 0)
p, err := utils.ApplyPatches(policyRaw, patches)
@ -61,7 +62,7 @@ func TestGeneratePodControllerRule_PredefinedAnnotation(t *testing.T) {
var policy kyverno.ClusterPolicy
assert.Assert(t, json.Unmarshal(policyRaw, &policy))
patches, errs := generatePodControllerRule(policy)
patches, errs := generatePodControllerRule(policy, log.Log)
assert.Assert(t, len(errs) == 0)
assert.Assert(t, len(patches) == 0)
}
@ -112,7 +113,7 @@ func TestGeneratePodControllerRule_DisableFeature(t *testing.T) {
var policy kyverno.ClusterPolicy
assert.Assert(t, json.Unmarshal(policyRaw, &policy))
patches, errs := generatePodControllerRule(policy)
patches, errs := generatePodControllerRule(policy, log.Log)
assert.Assert(t, len(errs) == 0)
assert.Assert(t, len(patches) == 0)
}
@ -163,7 +164,7 @@ func TestGeneratePodControllerRule_Mutate(t *testing.T) {
var policy kyverno.ClusterPolicy
assert.Assert(t, json.Unmarshal(policyRaw, &policy))
patches, errs := generatePodControllerRule(policy)
patches, errs := generatePodControllerRule(policy, log.Log)
assert.Assert(t, len(errs) == 0)
p, err := utils.ApplyPatches(policyRaw, patches)
@ -261,7 +262,7 @@ func TestGeneratePodControllerRule_ExistOtherAnnotation(t *testing.T) {
var policy kyverno.ClusterPolicy
assert.Assert(t, json.Unmarshal(policyRaw, &policy))
patches, errs := generatePodControllerRule(policy)
patches, errs := generatePodControllerRule(policy, log.Log)
assert.Assert(t, len(errs) == 0)
p, err := utils.ApplyPatches(policyRaw, patches)
@ -333,7 +334,7 @@ func TestGeneratePodControllerRule_ValidateAnyPattern(t *testing.T) {
var policy kyverno.ClusterPolicy
assert.Assert(t, json.Unmarshal(policyRaw, &policy))
patches, errs := generatePodControllerRule(policy)
patches, errs := generatePodControllerRule(policy, log.Log)
assert.Assert(t, len(errs) == 0)
p, err := utils.ApplyPatches(policyRaw, patches)
@ -471,7 +472,7 @@ func TestGeneratePodControllerRule_ValidatePattern(t *testing.T) {
var policy kyverno.ClusterPolicy
assert.Assert(t, json.Unmarshal(policyRaw, &policy))
patches, errs := generatePodControllerRule(policy)
patches, errs := generatePodControllerRule(policy, log.Log)
assert.Assert(t, len(errs) == 0)
p, err := utils.ApplyPatches(policyRaw, patches)

View file

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
policyvalidate "github.com/nirmata/kyverno/pkg/policy"
v1beta1 "k8s.io/api/admission/v1beta1"
@ -13,6 +12,7 @@ import (
//HandlePolicyValidation performs the validation check on policy resource
func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
logger := ws.log.WithValues("action", "policyvalidation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
var policy *kyverno.ClusterPolicy
admissionResp := &v1beta1.AdmissionResponse{
Allowed: true,
@ -21,7 +21,7 @@ func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionReques
//TODO: can this happen? wont this be picked by OpenAPI spec schema ?
raw := request.Object.Raw
if err := json.Unmarshal(raw, &policy); err != nil {
glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err)
logger.Error(err, "failed to unmarshal incoming resource to policy type")
return &v1beta1.AdmissionResponse{Allowed: false,
Result: &metav1.Status{
Message: fmt.Sprintf("Failed to unmarshal policy admission request err %v", err),

View file

@ -3,6 +3,7 @@ package webhooks
import (
"strings"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/response"
@ -10,7 +11,7 @@ import (
)
//generateEvents generates event info for the engine responses
func generateEvents(engineResponses []response.EngineResponse, blocked, onUpdate bool) []event.Info {
func generateEvents(engineResponses []response.EngineResponse, blocked, onUpdate bool, log logr.Logger) []event.Info {
var events []event.Info
// Scenario 1
// - Admission-Response is SUCCESS && CREATE
@ -26,6 +27,7 @@ func generateEvents(engineResponses []response.EngineResponse, blocked, onUpdate
successRulesStr := strings.Join(successRules, ";")
// event on resource
e := event.NewEvent(
log,
er.PolicyResponse.Resource.Kind,
er.PolicyResponse.Resource.APIVersion,
er.PolicyResponse.Resource.Namespace,
@ -59,6 +61,7 @@ func generateEvents(engineResponses []response.EngineResponse, blocked, onUpdate
filedRulesStr := strings.Join(failedRules, ";")
// Event on Policy
e := event.NewEvent(
log,
"ClusterPolicy",
kyverno.SchemeGroupVersion.String(),
"",
@ -90,6 +93,7 @@ func generateEvents(engineResponses []response.EngineResponse, blocked, onUpdate
filedRulesStr := strings.Join(failedRules, ";")
// Event on the policy
e := event.NewEvent(
log,
"ClusterPolicy",
kyverno.SchemeGroupVersion.String(),
"",
@ -104,6 +108,7 @@ func generateEvents(engineResponses []response.EngineResponse, blocked, onUpdate
// Event on the resource
// event on resource
e = event.NewEvent(
log,
er.PolicyResponse.Resource.Kind,
er.PolicyResponse.Resource.APIVersion,
er.PolicyResponse.Resource.Namespace,

View file

@ -10,7 +10,7 @@ import (
"net/http"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/checker"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1"
@ -69,6 +69,7 @@ type WebhookServer struct {
// generate request generator
grGenerator *generate.Generator
resourceWebhookWatcher *webhookconfig.ResourceWebhookRegister
log logr.Logger
}
// NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
@ -88,7 +89,9 @@ func NewWebhookServer(
pvGenerator policyviolation.GeneratorInterface,
grGenerator *generate.Generator,
resourceWebhookWatcher *webhookconfig.ResourceWebhookRegister,
cleanUp chan<- struct{}) (*WebhookServer, error) {
cleanUp chan<- struct{},
log logr.Logger,
) (*WebhookServer, error) {
if tlsPair == nil {
return nil, errors.New("NewWebhookServer is not initialized properly")
@ -120,6 +123,7 @@ func NewWebhookServer(
pMetaStore: pMetaStore,
grGenerator: grGenerator,
resourceWebhookWatcher: resourceWebhookWatcher,
log: log,
}
mux := http.NewServeMux()
mux.HandleFunc(config.MutatingWebhookServicePath, ws.serve)
@ -148,8 +152,9 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
if admissionReview == nil {
return
}
logger := ws.log.WithValues("kind", admissionReview.Request.Kind, "namespace", admissionReview.Request.Namespace, "name", admissionReview.Request.Name)
defer func() {
glog.V(4).Infof("request: %v %s/%s/%s", time.Since(startTime), admissionReview.Request.Kind, admissionReview.Request.Namespace, admissionReview.Request.Name)
logger.V(4).Info("request processed", "processingTime", time.Since(startTime))
}()
admissionReview.Response = &v1beta1.AdmissionResponse{
@ -195,31 +200,29 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
}
func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
logger := ws.log.WithValues("uid", request.UID, "kind", request.Kind.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
policies, err := ws.pMetaStore.ListAll()
if err != nil {
// Unable to connect to policy Lister to access policies
glog.Errorf("Unable to connect to policy controller to access policies. Policies are NOT being applied: %v", err)
logger.Error(err, "failed to list policies. Policies are NOT being applied")
return &v1beta1.AdmissionResponse{Allowed: true}
}
var roles, clusterRoles []string
// getRoleRef only if policy has roles/clusterroles defined
startTime := time.Now()
if containRBACinfo(policies) {
roles, clusterRoles, err = userinfo.GetRoleRef(ws.rbLister, ws.crbLister, request)
if err != nil {
// TODO(shuting): continue apply policy if error getting roleRef?
glog.Errorf("Unable to get rbac information for request Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s: %v",
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation, err)
logger.Error(err, "failed to get RBAC infromation for request")
}
}
glog.V(4).Infof("Time: webhook GetRoleRef %v", time.Since(startTime))
// convert RAW to unstructured
resource, err := convertResource(request.Object.Raw, request.Kind.Group, request.Kind.Version, request.Kind.Kind, request.Namespace)
if err != nil {
glog.Errorf(err.Error())
logger.Error(err, "failed to convert RAW resource to unstructured format")
return &v1beta1.AdmissionResponse{
Allowed: false,
@ -245,13 +248,13 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
patches := ws.HandleMutation(request, resource, policies, roles, clusterRoles)
// patch the resource with patches before handling validation rules
patchedResource := processResourceWithPatches(patches, request.Object.Raw)
patchedResource := processResourceWithPatches(patches, request.Object.Raw, logger)
if ws.resourceWebhookWatcher != nil && ws.resourceWebhookWatcher.RunValidationInMutatingWebhook == "true" {
// VALIDATION
ok, msg := ws.HandleValidation(request, policies, patchedResource, roles, clusterRoles)
if !ok {
glog.V(4).Infof("Deny admission request: %v/%s/%s", request.Kind, request.Namespace, request.Name)
logger.Info("admission request denied")
return &v1beta1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
@ -269,7 +272,7 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
if request.Operation == v1beta1.Create {
ok, msg := ws.HandleGenerate(request, policies, patchedResource, roles, clusterRoles)
if !ok {
glog.V(4).Infof("Deny admission request: %v/%s/%s", request.Kind, request.Namespace, request.Name)
logger.Info("admission request denied")
return &v1beta1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
@ -292,31 +295,29 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
}
func (ws *WebhookServer) handleValidateAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
logger := ws.log.WithValues("uid", request.UID, "kind", request.Kind.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
policies, err := ws.pMetaStore.ListAll()
if err != nil {
// Unable to connect to policy Lister to access policies
glog.Errorf("Unable to connect to policy controller to access policies. Policies are NOT being applied: %v", err)
logger.Error(err, "failed to list policies. Policies are NOT being applied")
return &v1beta1.AdmissionResponse{Allowed: true}
}
var roles, clusterRoles []string
// getRoleRef only if policy has roles/clusterroles defined
startTime := time.Now()
if containRBACinfo(policies) {
roles, clusterRoles, err = userinfo.GetRoleRef(ws.rbLister, ws.crbLister, request)
if err != nil {
// TODO(shuting): continue apply policy if error getting roleRef?
glog.Errorf("Unable to get rbac information for request Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s: %v",
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation, err)
logger.Error(err, "failed to get RBAC infromation for request")
}
}
glog.V(4).Infof("Time: webhook GetRoleRef %v", time.Since(startTime))
// VALIDATION
ok, msg := ws.HandleValidation(request, policies, nil, roles, clusterRoles)
if !ok {
glog.V(4).Infof("Deny admission request: %v/%s/%s", request.Kind, request.Namespace, request.Name)
logger.Info("admission request denied")
return &v1beta1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
@ -336,27 +337,28 @@ func (ws *WebhookServer) handleValidateAdmissionRequest(request *v1beta1.Admissi
// RunAsync TLS server in separate thread and returns control immediately
func (ws *WebhookServer) RunAsync(stopCh <-chan struct{}) {
logger := ws.log
if !cache.WaitForCacheSync(stopCh, ws.pSynced, ws.rbSynced, ws.crbSynced) {
glog.Error("webhook: failed to sync informer cache")
logger.Info("failed to sync informer cache")
}
go func(ws *WebhookServer) {
glog.V(3).Infof("serving on %s\n", ws.server.Addr)
logger.V(3).Info("started serving requests", "addr", ws.server.Addr)
if err := ws.server.ListenAndServeTLS("", ""); err != http.ErrServerClosed {
glog.Infof("HTTP server error: %v", err)
logger.Error(err, "failed to listen to requests")
}
}(ws)
glog.Info("Started Webhook Server")
logger.Info("starting")
// verifys if the admission control is enabled and active
// resync: 60 seconds
// deadline: 60 seconds (send request)
// max deadline: deadline*3 (set the deployment annotation as false)
go ws.lastReqTime.Run(ws.pLister, ws.eventGen, ws.client, checker.DefaultResync, checker.DefaultDeadline, stopCh)
}
// Stop TLS server and returns control after the server is shut down
func (ws *WebhookServer) Stop(ctx context.Context) {
logger := ws.log
// cleanUp
// remove the static webhookconfigurations
go ws.webhookRegistrationClient.RemoveWebhookConfigurations(ws.cleanUp)
@ -364,7 +366,7 @@ func (ws *WebhookServer) Stop(ctx context.Context) {
err := ws.server.Shutdown(ctx)
if err != nil {
// Error from closing listeners, or context timeout:
glog.Info("Server Shutdown error: ", err)
logger.Error(err, "shutting down server")
ws.server.Close()
}
}
@ -372,6 +374,7 @@ func (ws *WebhookServer) Stop(ctx context.Context) {
// bodyToAdmissionReview creates AdmissionReview object from request body
// Answers to the http.ResponseWriter if request is not valid
func (ws *WebhookServer) bodyToAdmissionReview(request *http.Request, writer http.ResponseWriter) *v1beta1.AdmissionReview {
logger := ws.log
var body []byte
if request.Body != nil {
if data, err := ioutil.ReadAll(request.Body); err == nil {
@ -379,21 +382,21 @@ func (ws *WebhookServer) bodyToAdmissionReview(request *http.Request, writer htt
}
}
if len(body) == 0 {
glog.Error("Error: empty body")
logger.Info("empty body")
http.Error(writer, "empty body", http.StatusBadRequest)
return nil
}
contentType := request.Header.Get("Content-Type")
if contentType != "application/json" {
glog.Error("Error: invalid Content-Type: ", contentType)
logger.Info("invalid Content-Type", "contextType", contentType)
http.Error(writer, "invalid Content-Type, expect `application/json`", http.StatusUnsupportedMediaType)
return nil
}
admissionReview := &v1beta1.AdmissionReview{}
if err := json.Unmarshal(body, &admissionReview); err != nil {
glog.Errorf("Error: Can't decode body as AdmissionReview: %v", err)
logger.Error(err, "failed to decode request body to type 'AdmissionReview")
http.Error(writer, "Can't decode body as AdmissionReview", http.StatusExpectationFailed)
return nil
}

View file

@ -2,9 +2,7 @@ package webhooks
import (
"reflect"
"time"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
@ -19,11 +17,10 @@ import (
// If there are no errors in validating rule we apply generation rules
// patchedResource is the (resource + patches) after applying mutation rules
func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, policies []kyverno.ClusterPolicy, patchedResource []byte, roles, clusterRoles []string) (bool, string) {
glog.V(4).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
logger := ws.log.WithValues("action", "validation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
logger.V(4).Info("incoming request")
var policyStats []policyctr.PolicyStat
evalTime := time.Now()
// gather stats from the engine response
gatherStat := func(policyName string, policyResponse response.PolicyResponse) {
ps := policyctr.PolicyStat{}
@ -57,7 +54,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
newR, oldR, err := extractResources(patchedResource, request)
if err != nil {
// as resource cannot be parsed, we skip processing
glog.Error(err)
logger.Error(err, "failed to extract resource")
return true, ""
}
userRequestInfo := kyverno.RequestInfo{
@ -69,17 +66,17 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
// load incoming resource into the context
err = ctx.AddResource(request.Object.Raw)
if err != nil {
glog.Infof("Failed to load resource in context:%v", err)
logger.Error(err, "failed to load incoming resource in context")
}
err = ctx.AddUserInfo(userRequestInfo)
if err != nil {
glog.Infof("Failed to load userInfo in context:%v", err)
logger.Error(err, "failed to load userInfo in context")
}
err = ctx.AddSA(userRequestInfo.AdmissionUserInfo.Username)
if err != nil {
glog.Infof("Failed to load service account in context:%v", err)
logger.Error(err, "failed to load service account in context")
}
policyContext := engine.PolicyContext{
@ -90,8 +87,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
}
var engineResponses []response.EngineResponse
for _, policy := range policies {
glog.V(2).Infof("Handling validation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
newR.GetKind(), newR.GetNamespace(), newR.GetName(), request.UID, request.Operation)
logger.V(2).Info("evaluating policy", "policy", policy.Name)
policyContext.Policy = policy
engineResponse := engine.Validate(policyContext)
if reflect.DeepEqual(engineResponse, response.EngineResponse{}) {
@ -103,17 +99,13 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
// Gather policy application statistics
gatherStat(policy.Name, engineResponse.PolicyResponse)
if !engineResponse.IsSuccesful() {
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, newR.GetNamespace(), newR.GetName())
logger.V(4).Info("failed to apply policy", "policy", policy.Name)
continue
}
}
glog.V(4).Infof("eval: %v %s/%s/%s ", time.Since(evalTime), request.Kind, request.Namespace, request.Name)
// report time
reportTime := time.Now()
// If Validation fails then reject the request
// no violations will be created on "enforce"
blocked := toBlockResource(engineResponses)
blocked := toBlockResource(engineResponses, logger)
// REPORTING EVENTS
// Scenario 1:
@ -125,10 +117,10 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
// Scenario 3:
// all policies were applied succesfully.
// create an event on the resource
events := generateEvents(engineResponses, blocked, (request.Operation == v1beta1.Update))
events := generateEvents(engineResponses, blocked, (request.Operation == v1beta1.Update), logger)
ws.eventGen.Add(events...)
if blocked {
glog.V(4).Infof("resource %s/%s/%s is blocked\n", newR.GetKind(), newR.GetNamespace(), newR.GetName())
logger.V(4).Info("resource blocked")
sendStat(true)
// EVENTS
// - event on the Policy
@ -137,10 +129,9 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
// ADD POLICY VIOLATIONS
// violations are created with resource on "audit"
pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses)
pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses, logger)
ws.pvGenerator.Add(pvInfos...)
sendStat(false)
// report time end
glog.V(4).Infof("report: %v %s/%s/%s", time.Since(reportTime), request.Kind, request.Namespace, request.Name)
return true, ""
}