diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000000..95a96cfddb --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,18 @@ +engines: + govet: + enabled: true + golint: + enabled: false + gofmt: + enabled: true + +ratings: + paths: + - "**.go" + +exclude_paths: +- documentation/ +- definitions +- gh-pages +- samples +- scripts \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000000..a589a168f3 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,17 @@ +linters: + enable: + - gosec + - errcheck + - gosimple + - bodyclose + - staticcheck + disable: + - ineffassign + - deadcode + - unused + - structcheck + +run: + skip-files: + - ".+_test.go" + - ".+_test_.+.go" \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 917a026a0c..3f9676cdb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,11 @@ language: go go: - "1.13" +cache: + directories: + - $HOME/.cache/go-build + - $GOPATH/pkg/mod + # safelist branches: only: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be104b5dc2..9941e08dd5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1,7 @@ -See: https://github.com/nirmata/kyverno#contributing +# Contributing to Kyverno + +## Code Style + +We follow the community provided standard [code structure](https://github.com/golang-standards/project-layout). + +See : https://github.com/nirmata/kyverno#contributing diff --git a/README.md b/README.md index df3ec092d1..2335a3b78f 100644 --- a/README.md +++ b/README.md @@ -171,5 +171,6 @@ See [Milestones](https://github.com/nirmata/kyverno/milestones) and [Issues](htt Thanks for your interest in contributing! * Please review and agree to abide with the [Code of Conduct](/CODE_OF_CONDUCT.md) before contributing. + * We encourage all contributions and encourage you to read our [contribution guidelines](./CONTRIBUTING.md). * See the [Wiki](https://github.com/nirmata/kyverno/wiki) for developer documentation. * Browse through the [open issues](https://github.com/nirmata/kyverno/issues) diff --git a/data/swaggerDoc.go b/api/swaggerDoc.go similarity index 100% rename from data/swaggerDoc.go rename to api/swaggerDoc.go diff --git a/cmd/initContainer/main.go b/cmd/initContainer/main.go index 9c5a9141ba..e9e377cb7f 100644 --- a/cmd/initContainer/main.go +++ b/cmd/initContainer/main.go @@ -5,23 +5,27 @@ 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/signal" "k8s.io/apimachinery/pkg/api/errors" rest "k8s.io/client-go/rest" clientcmd "k8s.io/client-go/tools/clientcmd" + "k8s.io/klog" + "k8s.io/klog/klogr" + "sigs.k8s.io/controller-runtime/pkg/log" ) var ( kubeconfig string + setupLog = log.Log.WithName("setup") ) const ( @@ -30,20 +34,30 @@ const ( ) func main() { - defer glog.Flush() + klog.InitFlags(nil) + log.SetLogger(klogr.New()) + // arguments + flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") + if err := flag.Set("v", "2"); err != nil { + klog.Fatalf("failed to set log level: %v", err) + } + flag.Parse() + // 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,53 +92,46 @@ 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) } } -func init() { - // arguments - flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flag.Set("logtostderr", "true") - flag.Set("stderrthreshold", "WARNING") - flag.Set("v", "2") - flag.Parse() -} - 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 +170,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 +178,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 +191,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 +201,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 +224,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) } } diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index 7087d41832..3bb05a3b39 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -3,11 +3,12 @@ package main import ( "context" "flag" + "fmt" + "os" "time" "github.com/nirmata/kyverno/pkg/openapi" - "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/checker" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions" @@ -27,6 +28,9 @@ import ( "github.com/nirmata/kyverno/pkg/webhooks" webhookgenerate "github.com/nirmata/kyverno/pkg/webhooks/generate" kubeinformers "k8s.io/client-go/informers" + "k8s.io/klog" + "k8s.io/klog/klogr" + log "sigs.k8s.io/controller-runtime/pkg/log" ) var ( @@ -38,20 +42,38 @@ 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 + setupLog = log.Log.WithName("setup") ) func main() { - defer glog.Flush() - version.PrintVersionInfo() + klog.InitFlags(nil) + log.SetLogger(klogr.New()) + flag.StringVar(&filterK8Resources, "filterK8Resources", "", "k8 resource in format [kind,namespace,name] where policy is not evaluated by the admission webhook. example --filterKind \"[Deployment, kyverno, kyverno]\" --filterKind \"[Deployment, kyverno, kyverno],[Events, *, *]\"") + flag.IntVar(&webhookTimeout, "webhooktimeout", 3, "timeout for webhook configurations") + flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") + flag.StringVar(&serverIP, "serverIP", "", "IP address where Kyverno controller runs. Only required if out-of-cluster.") + flag.StringVar(&runValidationInMutatingWebhook, "runValidationInMutatingWebhook", "", "Validation will also be done using the mutation webhook, set to 'true' to enable. Older kubernetes versions do not work properly when a validation webhook is registered.") + if err := flag.Set("v", "2"); err != nil { + setupLog.Error(err, "failed to set log level") + os.Exit(1) + } + + // 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") + + flag.Parse() + + 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 @@ -60,29 +82,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 @@ -99,16 +125,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 @@ -127,16 +155,19 @@ 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 Status Handler - deals with all logic related to policy status statusSync := policystatus.NewSync( @@ -149,7 +180,9 @@ func main() { client, pInformer.Kyverno().V1().ClusterPolicyViolations(), pInformer.Kyverno().V1().PolicyViolations(), - statusSync.Listener) + statusSync.Listener, + log.Log.WithName("PolicyViolationGenerator"), + ) // POLICY CONTROLLER // - reconciliation policy and policy violation @@ -165,13 +198,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 @@ -184,6 +220,7 @@ func main() { pvgen, kubedynamicInformer, statusSync.Listener, + 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 @@ -193,12 +230,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 @@ -207,7 +246,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) } // Sync openAPI definitions of resources @@ -234,9 +274,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) @@ -274,18 +317,5 @@ func main() { // resource cleanup // remove webhook configurations <-cleanUp - glog.Info("successful shutdown of kyverno controller") -} - -func init() { - flag.StringVar(&filterK8Resources, "filterK8Resources", "", "k8 resource in format [kind,namespace,name] where policy is not evaluated by the admission webhook. example --filterKind \"[Deployment, kyverno, kyverno]\" --filterKind \"[Deployment, kyverno, kyverno],[Events, *, *]\"") - flag.IntVar(&webhookTimeout, "webhooktimeout", 3, "timeout for webhook configurations") - flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&serverIP, "serverIP", "", "IP address where Kyverno controller runs. Only required if out-of-cluster.") - flag.StringVar(&runValidationInMutatingWebhook, "runValidationInMutatingWebhook", "", "Validation will also be done using the mutation webhook, set to 'true' to enable. Older kubernetes versions do not work properly when a validation webhook is registered.") - - // 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() - flag.Parse() + setupLog.Info("Kyverno shutdown successful") } diff --git a/definitions/install.yaml b/definitions/install.yaml index be688d3d97..3e7825e878 100644 --- a/definitions/install.yaml +++ b/definitions/install.yaml @@ -109,8 +109,6 @@ spec: type: string exclude: type: object - required: - - resources properties: roles: type: array diff --git a/definitions/install_debug.yaml b/definitions/install_debug.yaml index efc62b5a44..bf9194246d 100644 --- a/definitions/install_debug.yaml +++ b/definitions/install_debug.yaml @@ -109,8 +109,6 @@ spec: type: string exclude: type: object - required: - - resources properties: roles: type: array diff --git a/go.mod b/go.mod index 593b08fdef..f5ba937e12 100644 --- a/go.mod +++ b/go.mod @@ -5,39 +5,37 @@ go 1.13 require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/evanphx/json-patch v4.5.0+incompatible - github.com/gogo/protobuf v1.3.1 // indirect - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/go-logr/logr v0.1.0 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 // indirect github.com/googleapis/gnostic v0.3.1 - github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/golang-lru v0.5.3 // indirect github.com/imdario/mergo v0.3.8 // indirect github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af github.com/json-iterator/go v1.1.9 // indirect github.com/minio/minio v0.0.0-20200114012931-30922148fbb5 - github.com/ory/go-acc v0.1.0 // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/spf13/cobra v0.0.5 - github.com/spf13/pflag v1.0.5 // indirect github.com/tevino/abool v0.0.0-20170917061928-9b9efcf221b5 - golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 // indirect golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect google.golang.org/appengine v1.6.5 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.2.7 + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v2 v2.2.8 gotest.tools v2.2.0+incompatible - k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b - k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d - k8s.io/cli-runtime v0.0.0-20191004110135-b9eb767d2e1a - k8s.io/client-go v11.0.1-0.20190516230509-ae8359b20417+incompatible - k8s.io/klog v1.0.0 // indirect - k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a + k8s.io/api v0.17.4 + k8s.io/apimachinery v0.17.4 + k8s.io/cli-runtime v0.17.4 + k8s.io/client-go v0.17.4 + k8s.io/klog v1.0.0 + k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c k8s.io/utils v0.0.0-20200109141947-94aeca20bf09 // indirect - sigs.k8s.io/kustomize v2.0.3+incompatible // indirect + sigs.k8s.io/controller-runtime v0.5.0 ) // Added for go1.13 migration https://github.com/golang/go/issues/32805 -replace github.com/gorilla/rpc v1.2.0+incompatible => github.com/gorilla/rpc v1.2.0 +replace ( + github.com/gorilla/rpc v1.2.0+incompatible => github.com/gorilla/rpc v1.2.0 + k8s.io/code-generator => k8s.io/code-generator v0.0.0-20200306081859-6a048a382944 + k8s.io/component-base => k8s.io/component-base v0.0.0-20190612130303-4062e14deebe +) diff --git a/go.sum b/go.sum index 9b6fc04130..99c5af7f3b 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,13 @@ github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiU github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v11.7.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= @@ -26,21 +33,29 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0 h1:0GoNN3taZV6QI81IXgCbxMyEaJDXMSIjArYBCYzVVvs= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2 h1:JCHLVE3B+kJde7bIEo5N4J+ZbLhp0J1Fs+ulyRws4gE= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.24.1/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/alecthomas/participle v0.2.1/go.mod h1:SW6HZGeZgSIpcUWX3fXpfZhuaWHnmoD5KCVaqSaNTkk= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.20.21/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.23.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04= @@ -50,6 +65,7 @@ github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NR github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -64,6 +80,7 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -73,11 +90,16 @@ github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.12+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -85,9 +107,13 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/djherbis/atime v1.0.0/go.mod h1:5W+KBIuTwVGcqjIfaTwt+KSYX1o6uep8dtevevQP/f8= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v0.0.0-20180713052910-9f541cc9db5d/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -96,9 +122,14 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20181003060214-f58a169a71a5/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -113,21 +144,68 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1 h1:wSt/4CYxs70xbATrGXhokKF1i0tZjENLOo1ioIO13zk= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9 h1:tF+augKRWlWx0J0B7ZyyKSiTyV6E1zZe+7b3qQlcEf8= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501 h1:C1JKChikHGpXwT5UQDFaryIpDtyyGL/CR6C2kB7F1oc= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87 h1:zP3nY8Tk2E6RTkqGYrarZXuzh+ffyLDljLxCy1iJw80= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -301,14 +379,18 @@ github.com/gobuffalo/x v0.0.0-20181003152136-452098b06085/go.mod h1:WevpGD+5YOre github.com/gobuffalo/x v0.0.0-20181007152206-913e47c59ca7/go.mod h1:9rDPXaB3kXdKWzMc4odGQQdG2e2DIEmANy5aSJ9yesY= github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/gddo v0.0.0-20180828051604-96d2a289f41e/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA= @@ -333,6 +415,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -341,11 +424,13 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181004151105-1babbf986f6f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190328170749-bb2674552d8f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -358,9 +443,12 @@ github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36j github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.1.2/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -368,6 +456,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -399,6 +488,8 @@ github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvh github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= @@ -415,7 +506,10 @@ github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -445,11 +539,14 @@ github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kurin/blazer v0.5.4-0.20190613185654-cf2f27cc0be3/go.mod h1:4FCXMUWo9DllR2Do4TtBd377ezyAJ51vB5uTBjt0pGU= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/luna-duclos/instrumentedsql v1.1.2/go.mod h1:4LGbEqDnopzNAiyxPPDXhLspyunZxgPTMJBKtC6U0BQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= @@ -457,6 +554,12 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5 h1:0x4qcEHDpruK6ML/m/YSlFUUu0UpRD3I2PHsNCuGnyA= github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/markbates/deplist v1.0.4/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= github.com/markbates/deplist v1.0.5/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= github.com/markbates/going v1.0.2/go.mod h1:UWCk3zm0UKefHZ7l8BNqi26UyiEMniznk8naLdTcy6c= @@ -483,6 +586,7 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -525,7 +629,9 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd github.com/monoculum/formam v0.0.0-20180901015400-4e68be1d79ba/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q= github.com/moul/http2curl v0.0.0-20170919181001-9ac6cf4d929b/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nats-io/gnatsd v1.4.1/go.mod h1:nqco77VO78hLCJpIcVfygDP2rPGfsEHkGTUk94uh5DQ= github.com/nats-io/go-nats v1.7.2/go.mod h1:+t7RHT5ApZebkrQdnn6AhQJmhJJiKAvJUio1PiiCtj0= github.com/nats-io/go-nats-streaming v0.4.4/go.mod h1:gfq4R3c9sKAINOpelo0gn/b9QDMBZnmrttcsNF+lqyo= @@ -545,15 +651,21 @@ github.com/nsqio/go-nsq v1.0.7/go.mod h1:XP5zaUs3pqf+Q71EqUJs3HYfBIqfK6G83WQMdNN github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oleiade/reflections v1.0.0/go.mod h1:RbATFBbKYkVdqmSFtx13Bb/tVhR0lgOBXunWTZKeL4w= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c h1:Hww8mOyEKTeON4bZn7FrlLismspbPc1teNRUVH7wLQ8= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c h1:eSfnfIuwhxZyULg1NNuZycJcYkjYVGYe7FczwQReM6U= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= @@ -591,10 +703,12 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -602,10 +716,12 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20190704165056-9c2d0518ed81/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -666,6 +782,7 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6 github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -677,6 +794,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= @@ -690,6 +808,7 @@ github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJH github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= @@ -701,8 +820,10 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= github.com/unrolled/secure v0.0.0-20180918153822-f340ee86eb8b/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= github.com/unrolled/secure v0.0.0-20181005190816-ff9db2ff917f/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -712,6 +833,10 @@ github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63M github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= @@ -734,15 +859,22 @@ golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190102171810-8d7daa0c54b3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0= golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -771,16 +903,22 @@ golang.org/x/net v0.0.0-20181207154023-610586996380/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -822,9 +960,11 @@ golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -832,8 +972,10 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 h1:gZpLHxUX5BdYLA08Lj4YCJNN/jk7KtquiArPoeX0WvA= @@ -844,6 +986,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= @@ -875,6 +1018,7 @@ golang.org/x/tools v0.0.0-20190104182027-498d95493402/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190111214448-fc1d57b08d7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190118193359-16909d206f00/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -884,9 +1028,14 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -918,6 +1067,7 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= @@ -925,12 +1075,15 @@ gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUy gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -942,6 +1095,7 @@ gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuv gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= gopkg.in/ldap.v3 v3.0.3/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/olivere/elastic.v5 v5.0.80/go.mod h1:uhHoB4o3bvX5sorxBU29rPcmBQdV2Qfg0FBrx5D6pV0= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.1.9/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -955,6 +1109,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -963,24 +1119,53 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b h1:aBGgKJUM9Hk/3AE8WaZIApnTxG35kbuQba2w+SXqezo= k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4= +k8s.io/api v0.17.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo= +k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA= +k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d h1:Jmdtdt1ZnoGfWWIIik61Z7nKYgO3J+swQJtPYsP9wHA= k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/apimachinery v0.0.0-20190612125636-6a5db36e93ad/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= +k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= +k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw= +k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= +k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= k8s.io/cli-runtime v0.0.0-20191004110135-b9eb767d2e1a h1:REMzGxu+NpG9dPRsE9my/fw9iYIecz1S8UFFl6hbe18= k8s.io/cli-runtime v0.0.0-20191004110135-b9eb767d2e1a/go.mod h1:qWnH3/b8sp/l7EvlDh7ulDU3UWA4P4N1NFbEEP791tM= +k8s.io/cli-runtime v0.17.4 h1:ZIJdxpBEszZqUhydrCoiI5rLXS2J/1AF5xFok2QJ9bc= +k8s.io/cli-runtime v0.17.4/go.mod h1:IVW4zrKKx/8gBgNNkhiUIc7nZbVVNhc1+HcQh+PiNHc= +k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI= +k8s.io/client-go v0.17.4 h1:VVdVbpTY70jiNHS1eiFkUt7ZIJX3txd29nDxxXH4en8= +k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc= k8s.io/client-go v11.0.1-0.20190516230509-ae8359b20417+incompatible h1:bK03DJulJi9j05gwnXUufcs2j7h4M85YFvJ0dIlQ9k4= k8s.io/client-go v11.0.1-0.20190516230509-ae8359b20417+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/code-generator v0.0.0-20200306081859-6a048a382944/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/component-base v0.0.0-20190612130303-4062e14deebe/go.mod h1:MmIDXnint3qMN0cqXHKrSiJ2XQKo3J1BPIz7in7NvO0= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200109141947-94aeca20bf09 h1:sz6xjn8QP74104YNmJpzLbJ+a3ZtHt0tkD0g8vpdWNw= k8s.io/utils v0.0.0-20200109141947-94aeca20bf09/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +sigs.k8s.io/controller-runtime v0.5.0 h1:CbqIy5fbUX+4E9bpnBFd204YAzRYlM9SWW77BbrcDQo= +sigs.k8s.io/controller-runtime v0.5.0/go.mod h1:REiJzC7Y00U+2YkMbT8wxgrsX5USpXKGhb2sCtAXiT8= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/pkg/api/kyverno/v1/zz_generated.deepcopy.go b/pkg/api/kyverno/v1/zz_generated.deepcopy.go index d4d7cc6820..ff1a7c63e4 100644 --- a/pkg/api/kyverno/v1/zz_generated.deepcopy.go +++ b/pkg/api/kyverno/v1/zz_generated.deepcopy.go @@ -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)) diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go new file mode 100644 index 0000000000..8f1ef24e1e --- /dev/null +++ b/pkg/auth/auth.go @@ -0,0 +1,108 @@ +package auth + +import ( + "fmt" + "reflect" + + "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" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +//CanIOptions provides utility ti check if user has authorization for the given operation +type CanIOptions struct { + namespace string + 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, log logr.Logger) *CanIOptions { + o := CanIOptions{ + client: client, + log: log, + } + + o.namespace = namespace + o.kind = kind + o.verb = verb + + return &o +} + +//RunAccessCheck checks if the caller can perform the operation +// - operation is a combination of namespace, kind, verb +// - can only evaluate a single verb +// - group version resource is determined from the kind using the discovery client REST mapper +// - If disallowed, the reason and evaluationError is avialable in the logs +// - each can generates a SelfSubjectAccessReview resource and response is evaluated for permissions +func (o *CanIOptions) RunAccessCheck() (bool, error) { + // get GroupVersionResource from RESTMapper + // get GVR from kind + gvr := o.client.DiscoveryClient.GetGVRFromKind(o.kind) + if reflect.DeepEqual(gvr, schema.GroupVersionResource{}) { + // cannot find GVR + return false, fmt.Errorf("failed to get the Group Version Resource for kind %s", o.kind) + } + + sar := &authorizationv1.SelfSubjectAccessReview{ + Spec: authorizationv1.SelfSubjectAccessReviewSpec{ + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Namespace: o.namespace, + Verb: o.verb, + Group: gvr.Group, + Resource: gvr.Resource, + }, + }, + } + // Set self subject access review + // - namespace + // - 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 { + logger.Error(err, "failed to create resource") + return false, err + } + + // status.allowed + allowed, ok, err := unstructured.NestedBool(resp.Object, "status", "allowed") + if !ok { + if err != nil { + logger.Error(err, "failed to get the field", "field", "status.allowed") + } + logger.Info("field not found", "field", "status.allowed") + } + + if !allowed { + // status.reason + reason, ok, err := unstructured.NestedString(resp.Object, "status", "reason") + if !ok { + if err != nil { + logger.Error(err, "failed to get the field", "field", "status.reason") + } + logger.Info("field not found", "field", "status.reason") + } + // status.evaluationError + evaluationError, ok, err := unstructured.NestedString(resp.Object, "status", "evaludationError") + if !ok { + if err != nil { + logger.Error(err, "failed to get the field", "field", "status.evaluationError") + } + logger.Info("field not found", "field", "status.evaluationError") + } + + // Reporting ? (just logs) + logger.Info("disallowed operation", "reason", reason, "evaluationError", evaluationError) + } + + return allowed, nil +} diff --git a/pkg/auth/auth_test.go b/pkg/auth/auth_test.go new file mode 100644 index 0000000000..6dbc800285 --- /dev/null +++ b/pkg/auth/auth_test.go @@ -0,0 +1,48 @@ +package auth + +// import ( +// "testing" +// "time" + +// "github.com/golang/glog" +// "github.com/nirmata/kyverno/pkg/config" +// dclient "github.com/nirmata/kyverno/pkg/dclient" +// "github.com/nirmata/kyverno/pkg/signal" +// ) + +// func Test_Auth_pass(t *testing.T) { +// // needs running cluster +// var kubeconfig string +// stopCh := signal.SetupSignalHandler() +// kubeconfig = "/Users/shivd/.kube/config" +// clientConfig, err := config.CreateClientConfig(kubeconfig) +// if err != nil { +// glog.Fatalf("Error building kubeconfig: %v\n", err) +// } + +// // 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) +// if err != nil { +// glog.Fatalf("Error creating client: %v\n", err) +// } + +// // Can i authenticate + +// kind := "Deployment" +// namespace := "default" +// verb := "test" +// canI := NewCanI(client, kind, namespace, verb) +// ok, err := canI.RunAccessCheck() +// if err != nil { +// t.Error(err) +// } +// if ok { +// t.Log("allowed") +// } else { +// t.Log("notallowed") +// } +// t.FailNow() + +// } diff --git a/pkg/checker/checker.go b/pkg/checker/checker.go index 762776dffd..16b8276d40 100644 --- a/pkg/checker/checker.go +++ b/pkg/checker/checker.go @@ -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 } } diff --git a/pkg/checker/status.go b/pkg/checker/status.go index 39b19189dd..110abf8eef 100644 --- a/pkg/checker/status.go +++ b/pkg/checker/status.go @@ -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 diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index 5fbce444ff..8aa7b0e529 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -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 diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index 215b1d997a..c3264dcb64 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -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 diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index 4a90cf5ba8..482e66ffc7 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -29,7 +29,8 @@ import ( var scheme = runtime.NewScheme() var codecs = serializer.NewCodecFactory(scheme) -var parameterCodec = runtime.NewParameterCodec(scheme) + +// var parameterCodec = runtime.NewParameterCodec(scheme) var localSchemeBuilder = runtime.SchemeBuilder{ kyvernov1.AddToScheme, } diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1/kyverno_client.go b/pkg/client/clientset/versioned/typed/kyverno/v1/kyverno_client.go index 54d8944680..8c1b5a0ea8 100644 --- a/pkg/client/clientset/versioned/typed/kyverno/v1/kyverno_client.go +++ b/pkg/client/clientset/versioned/typed/kyverno/v1/kyverno_client.go @@ -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() diff --git a/pkg/config/config.go b/pkg/config/config.go index 241728a2c7..2d6ccdcb68 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,9 +1,7 @@ package config import ( - "flag" - - "github.com/golang/glog" + "github.com/go-logr/logr" rest "k8s.io/client-go/rest" clientcmd "k8s.io/client-go/tools/clientcmd" ) @@ -74,29 +72,13 @@ var ( VerifyMutatingWebhookServicePath = "/verifymutate" ) -//LogDefaultFlags sets default glog flags -func LogDefaultFlags() { - var err error - err = flag.Set("logtostderr", "true") - if err != nil { - glog.Fatalf("failed to set flag 'logtostderr' to 'true':%v", err) - } - err = flag.Set("stderrthreshold", "WARNING") - if err != nil { - glog.Fatalf("failed to set flag 'stderrthreshold' to 'WARNING':%v", err) - } - flag.Set("v", "2") - if err != nil { - glog.Fatalf("failed to set flag 'v' to '2':%v", err) - } -} - //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) } diff --git a/pkg/config/dynamicconfig.go b/pkg/config/dynamicconfig.go index 6bf624e7bb..1787f67bd9 100644 --- a/pkg/config/dynamicconfig.go +++ b/pkg/config/dynamicconfig.go @@ -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{} diff --git a/pkg/dclient/certificates.go b/pkg/dclient/certificates.go index 76b3cda819..125dc7cec3 100644 --- a/pkg/dclient/certificates.go +++ b/pkg/dclient/certificates.go @@ -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 } diff --git a/pkg/dclient/client.go b/pkg/dclient/client.go index 6cacaeaf9a..557059bc15 100644 --- a/pkg/dclient/client.go +++ b/pkg/dclient/client.go @@ -5,9 +5,8 @@ import ( "strings" "time" + "github.com/go-logr/logr" openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2" - - "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/config" apps "k8s.io/api/apps/v1" certificates "k8s.io/api/certificates/v1beta1" @@ -32,13 +31,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 @@ -51,9 +52,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 @@ -189,7 +191,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} @@ -228,22 +229,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() } } @@ -261,12 +264,12 @@ func (c ServerPreferredResources) OpenAPISchema() (*openapi_v2.Document, error) 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 } @@ -279,11 +282,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 { @@ -293,7 +297,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 diff --git a/pkg/engine/anchor/anchor.go b/pkg/engine/anchor/anchor.go index 2d81a068ec..9dccab3577 100644 --- a/pkg/engine/anchor/anchor.go +++ b/pkg/engine/anchor/anchor.go @@ -4,7 +4,8 @@ import ( "fmt" "strconv" - "github.com/golang/glog" + "github.com/go-logr/logr" + "sigs.k8s.io/controller-runtime/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 } } diff --git a/pkg/engine/context/context.go b/pkg/engine/context/context.go index 05805a0a91..163e81fef0 100644 --- a/pkg/engine/context/context.go +++ b/pkg/engine/context/context.go @@ -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" + "sigs.k8s.io/controller-runtime/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 { diff --git a/pkg/engine/context/evaluate.go b/pkg/engine/context/evaluate.go index 78f9643497..f15a8a1e17 100644 --- a/pkg/engine/context/evaluate.go +++ b/pkg/engine/context/evaluate.go @@ -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 diff --git a/pkg/engine/forceMutate.go b/pkg/engine/forceMutate.go index 14ba42c468..9453109742 100644 --- a/pkg/engine/forceMutate.go +++ b/pkg/engine/forceMutate.go @@ -12,6 +12,7 @@ import ( "github.com/nirmata/kyverno/pkg/engine/utils" "github.com/nirmata/kyverno/pkg/engine/variables" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/log" ) func mutateResourceWithOverlay(resource unstructured.Unstructured, overlay interface{}) (unstructured.Unstructured, error) { @@ -57,7 +58,7 @@ func ForceMutate(ctx context.EvalInterface, policy kyverno.ClusterPolicy, resour if mutation.Overlay != nil { overlay := mutation.Overlay if ctx != nil { - if overlay, err = variables.SubstituteVars(ctx, overlay); err != nil { + if overlay, err = variables.SubstituteVars(log.Log, ctx, overlay); err != nil { return unstructured.Unstructured{}, err } } else { @@ -72,7 +73,7 @@ func ForceMutate(ctx context.EvalInterface, policy kyverno.ClusterPolicy, resour if rule.Mutation.Patches != nil { var resp response.RuleResponse - resp, resource = mutate.ProcessPatches(rule, resource) + resp, resource = mutate.ProcessPatches(log.Log, rule, resource) if !resp.Success { return unstructured.Unstructured{}, fmt.Errorf(resp.Message) } diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 0f8cea1937..552bbc3a4d 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -3,12 +3,14 @@ package engine import ( "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/variables" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/log" ) // Generate checks for validity of generate rule on the resource @@ -20,10 +22,11 @@ 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 } @@ -31,15 +34,14 @@ func filterRule(rule kyverno.Rule, resource unstructured.Unstructured, admission startTime := time.Now() 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 @@ -53,7 +55,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, @@ -66,7 +68,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) } } diff --git a/pkg/engine/mutate/overlay.go b/pkg/engine/mutate/overlay.go index 3b76f30c74..5e4982260e 100644 --- a/pkg/engine/mutate/overlay.go +++ b/pkg/engine/mutate/overlay.go @@ -10,51 +10,53 @@ import ( "strings" "time" - "github.com/golang/glog" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/log" jsonpatch "github.com/evanphx/json-patch" + "github.com/go-logr/logr" "github.com/nirmata/kyverno/pkg/engine/anchor" "github.com/nirmata/kyverno/pkg/engine/response" "github.com/nirmata/kyverno/pkg/engine/utils" ) // ProcessOverlay processes mutation overlay on the resource -func ProcessOverlay(ruleName string, overlay interface{}, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) { +func ProcessOverlay(log logr.Logger, ruleName string, overlay interface{}, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) { startTime := time.Now() - glog.V(4).Infof("started applying overlay rule %q (%v)", ruleName, startTime) + logger := log.WithValues("rule", ruleName) + logger.V(4).Info("started applying overlay rule ", "startTime", startTime) resp.Name = ruleName resp.Type = utils.Mutation.String() defer func() { resp.RuleStats.ProcessingTime = time.Since(startTime) - glog.V(4).Infof("finished applying overlay rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime) + logger.V(4).Info("finished applying overlay rule", "processingTime", resp.RuleStats.ProcessingTime) }() - patches, overlayerr := processOverlayPatches(resource.UnstructuredContent(), overlay) + patches, overlayerr := processOverlayPatches(logger, resource.UnstructuredContent(), overlay) // resource does not satisfy the overlay pattern, we don't apply this rule if !reflect.DeepEqual(overlayerr, overlayError{}) { switch overlayerr.statusCode { // condition key is not present in the resource, don't apply this rule // consider as success case conditionNotPresent: - glog.V(3).Infof("Skip applying rule '%s' on resource '%s/%s/%s': %s", ruleName, resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.ErrorMsg()) + logger.V(3).Info("skip applying rule") resp.Success = true return resp, resource // conditions are not met, don't apply this rule case conditionFailure: - glog.V(3).Infof("Skip applying rule '%s' on resource '%s/%s/%s': %s", ruleName, resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.ErrorMsg()) + logger.V(3).Info("skip applying rule") //TODO: send zero response and not consider this as applied? resp.Success = true resp.Message = overlayerr.ErrorMsg() return resp, resource // rule application failed case overlayFailure: - glog.Errorf("Resource %s/%s/%s: failed to process overlay: %v in the rule %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.ErrorMsg(), ruleName) + logger.Info("failed to process overlay") resp.Success = false resp.Message = fmt.Sprintf("failed to process overlay: %v", overlayerr.ErrorMsg()) return resp, resource default: - glog.Errorf("Resource %s/%s/%s: Unknown type of error: %v", resource.GetKind(), resource.GetNamespace(), resource.GetName(), overlayerr.Error()) + logger.Info("failed to process overlay") resp.Success = false resp.Message = fmt.Sprintf("Unknown type of error: %v", overlayerr.Error()) return resp, resource @@ -70,7 +72,7 @@ func ProcessOverlay(ruleName string, overlay interface{}, resource unstructured. resourceRaw, err := resource.MarshalJSON() if err != nil { resp.Success = false - glog.Infof("unable to marshall resource: %v", err) + logger.Error(err, "failed to marshal resource") resp.Message = fmt.Sprintf("failed to process JSON patches: %v", err) return resp, resource } @@ -79,7 +81,7 @@ func ProcessOverlay(ruleName string, overlay interface{}, resource unstructured. patchResource, err = utils.ApplyPatches(resourceRaw, patches) if err != nil { msg := fmt.Sprintf("failed to apply JSON patches: %v", err) - glog.V(2).Infof("%s, patches=%s", msg, string(utils.JoinPatches(patches))) + logger.V(2).Info("applying patches", "patches", string(utils.JoinPatches(patches))) resp.Success = false resp.Message = msg return resp, resource @@ -87,7 +89,7 @@ func ProcessOverlay(ruleName string, overlay interface{}, resource unstructured. err = patchedResource.UnmarshalJSON(patchResource) if err != nil { - glog.Infof("failed to unmarshall resource to undstructured: %v", err) + logger.Error(err, "failed to unmarshal resource") resp.Success = false resp.Message = fmt.Sprintf("failed to process JSON patches: %v", err) return resp, resource @@ -101,17 +103,17 @@ func ProcessOverlay(ruleName string, overlay interface{}, resource unstructured. return resp, patchedResource } -func processOverlayPatches(resource, overlay interface{}) ([][]byte, overlayError) { - if path, overlayerr := meetConditions(resource, overlay); !reflect.DeepEqual(overlayerr, overlayError{}) { +func processOverlayPatches(log logr.Logger, resource, overlay interface{}) ([][]byte, overlayError) { + if path, overlayerr := meetConditions(log, resource, overlay); !reflect.DeepEqual(overlayerr, overlayError{}) { switch overlayerr.statusCode { // anchor key does not exist in the resource, skip applying policy case conditionNotPresent: - glog.V(4).Infof("Mutate rule: skip applying policy: %v at %s", overlayerr, path) + log.V(4).Info("skip applying policy", "path", path, "error", overlayerr) return nil, newOverlayError(overlayerr.statusCode, fmt.Sprintf("Policy not applied, condition tag not present: %v at %s", overlayerr.ErrorMsg(), path)) // anchor key is not satisfied in the resource, skip applying policy case conditionFailure: // anchor key is not satisfied in the resource, skip applying policy - glog.V(4).Infof("Mutate rule: failed to validate condition at %s, err: %v", path, overlayerr) + log.V(4).Info("failed to validate condition", "path", path, "error", overlayerr) return nil, newOverlayError(overlayerr.statusCode, fmt.Sprintf("Policy not applied, conditions are not met at %s, %v", path, overlayerr)) } } @@ -383,7 +385,7 @@ func prepareJSONValue(overlay interface{}) string { overlayWithoutAnchors := removeAnchorFromSubTree(overlay) jsonOverlay, err := json.Marshal(overlayWithoutAnchors) if err != nil || hasOnlyAnchors(overlay) { - glog.V(3).Info(err) + log.Log.Error(err, "failed to marshall withoutanchors or has only anchors") return "" } @@ -409,7 +411,7 @@ func removeAnchorFromSubTree(overlay interface{}) interface{} { } func removeAnchroFromMap(overlay map[string]interface{}) map[string]interface{} { - result := make(map[string]interface{}, 0) + result := make(map[string]interface{}) for k, v := range overlay { result[getRawKeyIfWrappedWithAttributes(k)] = removeAnchorFromSubTree(v) } diff --git a/pkg/engine/mutate/overlayCondition.go b/pkg/engine/mutate/overlayCondition.go index 134cc23cc0..56b19de4e9 100755 --- a/pkg/engine/mutate/overlayCondition.go +++ b/pkg/engine/mutate/overlayCondition.go @@ -5,17 +5,18 @@ import ( "reflect" "strconv" - "github.com/golang/glog" + "github.com/go-logr/logr" "github.com/nirmata/kyverno/pkg/engine/anchor" "github.com/nirmata/kyverno/pkg/engine/validate" + "sigs.k8s.io/controller-runtime/pkg/log" ) -func meetConditions(resource, overlay interface{}) (string, overlayError) { - return checkConditions(resource, overlay, "/") +func meetConditions(log logr.Logger, resource, overlay interface{}) (string, overlayError) { + return checkConditions(log, resource, overlay, "/") } // resource and overlay should be the same type -func checkConditions(resource, overlay interface{}, path string) (string, overlayError) { +func checkConditions(log logr.Logger, resource, overlay interface{}, path string) (string, overlayError) { // overlay has no anchor, return true if !hasNestedAnchors(overlay) { return "", overlayError{} @@ -26,7 +27,7 @@ func checkConditions(resource, overlay interface{}, path string) (string, overla // condition never be true in this case if reflect.TypeOf(resource) != reflect.TypeOf(overlay) { if hasNestedAnchors(overlay) { - glog.V(4).Infof("Found anchor on different types of element at path %s: overlay %T, resource %T", path, overlay, resource) + log.V(4).Info(fmt.Sprintf("Found anchor on different types of element at path %s: overlay %T, resource %T", path, overlay, resource)) return path, newOverlayError(conditionFailure, fmt.Sprintf("Found anchor on different types of element at path %s: overlay %T %v, resource %T %v", path, overlay, overlay, resource, resource)) @@ -44,7 +45,7 @@ func checkConditions(resource, overlay interface{}, path string) (string, overla default: // anchor on non map/array is invalid: // - anchor defined on values - glog.Warningln("Found invalid conditional anchor: anchor defined on values") + log.Info("Found invalid conditional anchor: anchor defined on values") return "", overlayError{} } } @@ -68,12 +69,12 @@ func checkConditionOnMap(resourceMap, overlayMap map[string]interface{}, path st func checkConditionOnArray(resource, overlay []interface{}, path string) (string, overlayError) { if 0 == len(overlay) { - glog.Infof("Mutate overlay pattern is empty, path %s", path) + log.Log.V(4).Info("Mutate overlay pattern is empty", "path", path) return "", overlayError{} } if reflect.TypeOf(resource[0]) != reflect.TypeOf(overlay[0]) { - glog.V(4).Infof("Overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0]) + log.Log.V(4).Info(fmt.Sprintf("Overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0])) return path, newOverlayError(conditionFailure, fmt.Sprintf("Overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0])) } @@ -111,7 +112,7 @@ func validateConditionAnchorMap(resourceMap, anchors map[string]interface{}, pat // resource - A: B2 func compareOverlay(resource, overlay interface{}, path string) (string, overlayError) { if reflect.TypeOf(resource) != reflect.TypeOf(overlay) { - glog.V(4).Infof("Found anchor on different types of element: overlay %T, resource %T", overlay, resource) + log.Log.V(4).Info("Found anchor on different types of element: overlay %T, resource %T", overlay, resource) return path, newOverlayError(conditionFailure, fmt.Sprintf("Found anchor on different types of element: overlay %T, resource %T", overlay, resource)) } @@ -139,8 +140,8 @@ func compareOverlay(resource, overlay interface{}, path string) (string, overlay } } case string, float64, int, int64, bool, nil: - if !validate.ValidateValueWithPattern(resource, overlay) { - glog.V(4).Infof("Mutate rule: failed validating value %v with overlay %v", resource, overlay) + if !validate.ValidateValueWithPattern(log.Log, resource, overlay) { + log.Log.V(4).Info(fmt.Sprintf("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)) } default: @@ -165,7 +166,7 @@ func validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor map[string]in continue } } - if newPath, err := checkConditions(resourceValue, overlayValue, curPath); !reflect.DeepEqual(err, overlayError{}) { + if newPath, err := checkConditions(log.Log, resourceValue, overlayValue, curPath); !reflect.DeepEqual(err, overlayError{}) { return newPath, err } } @@ -179,7 +180,7 @@ func checkConditionsOnArrayOfSameTypes(resource, overlay []interface{}, path str default: for i, overlayElement := range overlay { curPath := path + strconv.Itoa(i) + "/" - path, err := checkConditions(resource[i], overlayElement, curPath) + path, err := checkConditions(log.Log, resource[i], overlayElement, curPath) if !reflect.DeepEqual(err, overlayError{}) { return path, err } diff --git a/pkg/engine/mutate/overlayCondition_test.go b/pkg/engine/mutate/overlayCondition_test.go index b898acfbcd..8da6eefee9 100644 --- a/pkg/engine/mutate/overlayCondition_test.go +++ b/pkg/engine/mutate/overlayCondition_test.go @@ -7,6 +7,7 @@ import ( "testing" "gotest.tools/assert" + "sigs.k8s.io/controller-runtime/pkg/log" ) func TestMeetConditions_NoAnchor(t *testing.T) { @@ -26,9 +27,11 @@ func TestMeetConditions_NoAnchor(t *testing.T) { }`) var overlay interface{} - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(overlayRaw, &overlay) + assert.Assert(t, reflect.DeepEqual(err, nil)) + + _, err = meetConditions(log.Log, nil, overlay) - _, err := meetConditions(nil, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) } @@ -78,10 +81,12 @@ func TestMeetConditions_conditionalAnchorOnMap(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - _, err := meetConditions(resource, overlay) + _, err = meetConditions(log.Log, resource, overlay) assert.Assert(t, !reflect.DeepEqual(err, overlayError{})) overlayRaw = []byte(` @@ -99,9 +104,10 @@ func TestMeetConditions_conditionalAnchorOnMap(t *testing.T) { ] }`) - json.Unmarshal(overlayRaw, &overlay) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - _, overlayerr := meetConditions(resource, overlay) + _, overlayerr := meetConditions(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{})) } @@ -136,11 +142,14 @@ func TestMeetConditions_DifferentTypes(t *testing.T) { }`) var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) // anchor exist - _, err := meetConditions(resource, overlay) + + _, err = meetConditions(log.Log, resource, overlay) assert.Assert(t, strings.Contains(err.Error(), "Found anchor on different types of element at path /subsets/")) } @@ -190,10 +199,12 @@ func TestMeetConditions_anchosInSameObject(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - _, err := meetConditions(resource, overlay) + _, err = meetConditions(log.Log, resource, overlay) assert.Error(t, err, "[overlayError:0] Failed validating value 443 with overlay 444") } @@ -248,10 +259,13 @@ func TestMeetConditions_anchorOnPeer(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) + + _, err = meetConditions(log.Log, resource, overlay) - _, err := meetConditions(resource, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) } @@ -325,10 +339,13 @@ func TestMeetConditions_anchorsOnMetaAndSpec(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) + + _, err = meetConditions(log.Log, resource, overlay) - _, err := meetConditions(resource, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) } @@ -406,10 +423,13 @@ func TestMeetConditions_anchorsOnPeer_single(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRawAnchorOnPeers, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRawAnchorOnPeers, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) + + _, err = meetConditions(log.Log, resource, overlay) - _, err := meetConditions(resource, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) } @@ -440,10 +460,13 @@ func TestMeetConditions_anchorsOnPeer_two(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRawAnchorOnPeers, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRawAnchorOnPeers, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) + + _, err = meetConditions(log.Log, resource, overlay) - _, err := meetConditions(resource, overlay) assert.Error(t, err, "[overlayError:0] Failed validating value true with overlay false") overlayRaw = []byte(`{ @@ -470,9 +493,10 @@ func TestMeetConditions_anchorsOnPeer_two(t *testing.T) { } }`) - json.Unmarshal(overlayRaw, &overlay) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - _, err = meetConditions(resource, overlay) + _, err = meetConditions(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) overlayRaw = []byte(`{ @@ -499,9 +523,10 @@ func TestMeetConditions_anchorsOnPeer_two(t *testing.T) { } }`) - json.Unmarshal(overlayRaw, &overlay) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - _, err = meetConditions(resource, overlay) + _, err = meetConditions(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) } @@ -532,10 +557,13 @@ func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRawAnchorOnPeers, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRawAnchorOnPeers, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) + + _, err = meetConditions(log.Log, resource, overlay) - _, err := meetConditions(resource, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) overlayRaw = []byte(`{ @@ -562,9 +590,10 @@ func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) { } }`) - json.Unmarshal(overlayRaw, &overlay) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - _, err = meetConditions(resource, overlay) + _, err = meetConditions(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) overlayRaw = []byte(`{ @@ -591,9 +620,10 @@ func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) { } }`) - json.Unmarshal(overlayRaw, &overlay) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - _, err = meetConditions(resource, overlay) + _, err = meetConditions(log.Log, resource, overlay) assert.Error(t, err, "[overlayError:0] Failed validating value ENV_VALUE with overlay ENV_VALUE1") } @@ -649,10 +679,13 @@ func TestMeetConditions_AtleastOneExist(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) + + path, err := meetConditions(log.Log, resource, overlay) - path, err := meetConditions(resource, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) assert.Assert(t, len(path) == 0) } diff --git a/pkg/engine/mutate/overlay_test.go b/pkg/engine/mutate/overlay_test.go index 8c062fe58d..a7c8890039 100644 --- a/pkg/engine/mutate/overlay_test.go +++ b/pkg/engine/mutate/overlay_test.go @@ -8,6 +8,7 @@ import ( jsonpatch "github.com/evanphx/json-patch" "github.com/nirmata/kyverno/pkg/engine/utils" "gotest.tools/assert" + "sigs.k8s.io/controller-runtime/pkg/log" ) func compareJSONAsMap(t *testing.T, expected, actual []byte) { @@ -63,10 +64,12 @@ func TestProcessOverlayPatches_NestedListWithAnchor(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, overlayerr := processOverlayPatches(resource, overlay) + patches, overlayerr := processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{})) assert.Assert(t, patches != nil) @@ -163,10 +166,12 @@ func TestProcessOverlayPatches_InsertIntoArray(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, overlayerr := processOverlayPatches(resource, overlay) + patches, overlayerr := processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{})) assert.Assert(t, patches != nil) @@ -284,10 +289,12 @@ func TestProcessOverlayPatches_TestInsertToArray(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, overlayerr := processOverlayPatches(resource, overlay) + patches, overlayerr := processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{})) assert.Assert(t, patches != nil) @@ -367,10 +374,12 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, overlayerr := processOverlayPatches(resource, overlay) + patches, overlayerr := processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{})) assert.Assert(t, len(patches) != 0) @@ -456,9 +465,10 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) { } }`) - json.Unmarshal(overlayRaw, &overlay) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, err = processOverlayPatches(resource, overlay) + patches, err = processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) assert.Assert(t, len(patches) != 0) @@ -492,9 +502,10 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) { } }`) - json.Unmarshal(overlayRaw, &overlay) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, err = processOverlayPatches(resource, overlay) + patches, err = processOverlayPatches(log.Log, resource, overlay) assert.Error(t, err, "[overlayError:0] Policy not applied, conditions are not met at /spec/template/metadata/labels/app/, [overlayError:0] Failed validating value nginx with overlay nginx1") assert.Assert(t, len(patches) == 0) } @@ -520,10 +531,12 @@ func TestProcessOverlayPatches_AddingAnchor(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, overlayerr := processOverlayPatches(resource, overlay) + patches, overlayerr := processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{})) assert.Assert(t, len(patches) != 0) @@ -605,10 +618,12 @@ func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, overlayerr := processOverlayPatches(resource, overlay) + patches, overlayerr := processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{})) assert.Assert(t, len(patches) != 0) @@ -684,9 +699,10 @@ func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) { } }`) - json.Unmarshal(overlayRaw, &overlay) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, err = processOverlayPatches(resource, overlay) + patches, err = processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(err, overlayError{})) assert.Assert(t, len(patches) != 0) @@ -747,10 +763,12 @@ func TestProcessOverlayPatches_anchorOnPeer(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, overlayerr := processOverlayPatches(resource, overlay) + patches, overlayerr := processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{})) assert.Assert(t, len(patches) != 0) @@ -805,9 +823,10 @@ func TestProcessOverlayPatches_anchorOnPeer(t *testing.T) { ] }`) - json.Unmarshal(overlayRaw, &overlay) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, err = processOverlayPatches(resource, overlay) + patches, err = processOverlayPatches(log.Log, resource, overlay) assert.Error(t, err, "[overlayError:0] Policy not applied, conditions are not met at /subsets/0/ports/0/port/, [overlayError:0] Failed validating value 443 with overlay 444") assert.Assert(t, len(patches) == 0) } @@ -886,10 +905,12 @@ func TestProcessOverlayPatches_insertWithCondition(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, overlayerr := processOverlayPatches(resource, overlay) + patches, overlayerr := processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{})) assert.Assert(t, len(patches) != 0) @@ -997,10 +1018,12 @@ func TestProcessOverlayPatches_InsertIfNotPresentWithConditions(t *testing.T) { var resource, overlay interface{} - json.Unmarshal(resourceRaw, &resource) - json.Unmarshal(overlayRaw, &overlay) + err := json.Unmarshal(resourceRaw, &resource) + assert.NilError(t, err) + err = json.Unmarshal(overlayRaw, &overlay) + assert.NilError(t, err) - patches, overlayerr := processOverlayPatches(resource, overlay) + patches, overlayerr := processOverlayPatches(log.Log, resource, overlay) assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{})) assert.Assert(t, len(patches) != 0) diff --git a/pkg/engine/mutate/patches.go b/pkg/engine/mutate/patches.go index 61fc93ecea..7b294bbca9 100644 --- a/pkg/engine/mutate/patches.go +++ b/pkg/engine/mutate/patches.go @@ -6,7 +6,7 @@ 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/response" "github.com/nirmata/kyverno/pkg/engine/utils" @@ -20,21 +20,22 @@ func applyPatch(resource []byte, patchRaw []byte) ([]byte, error) { } //ProcessPatches applies the patches on the resource and returns the patched resource -func ProcessPatches(rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) { +func ProcessPatches(log logr.Logger, rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) { + logger := log.WithValues("rule", rule.Name) startTime := time.Now() - glog.V(4).Infof("started JSON patch rule %q (%v)", rule.Name, startTime) + logger.V(4).Info("started JSON patch", "startTime", startTime) resp.Name = rule.Name resp.Type = utils.Mutation.String() defer func() { resp.RuleStats.ProcessingTime = time.Since(startTime) - glog.V(4).Infof("finished JSON patch rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime) + logger.V(4).Info("finished JSON patch", "processingTime", resp.RuleStats.ProcessingTime) }() // convert to RAW resourceRaw, err := resource.MarshalJSON() if err != nil { resp.Success = false - glog.Infof("unable to marshall resource: %v", err) + logger.Error(err, "failed to marshal resource") resp.Message = fmt.Sprintf("failed to process JSON patches: %v", err) return resp, resource } @@ -45,14 +46,14 @@ func ProcessPatches(rule kyverno.Rule, resource unstructured.Unstructured) (resp // JSON patch patchRaw, err := json.Marshal(patch) if err != nil { - glog.V(4).Infof("failed to marshall JSON patch %v: %v", patch, err) + logger.Error(err, "failed to marshal JSON patch") errs = append(errs, err) continue } patchResource, err := applyPatch(resourceRaw, patchRaw) // TODO: continue on error if one of the patches fails, will add the failure event in such case if err != nil && patch.Operation == "remove" { - glog.Info(err) + log.Error(err, "failed to process JSON path or patch is a 'remove' operation") continue } if err != nil { @@ -77,7 +78,7 @@ func ProcessPatches(rule kyverno.Rule, resource unstructured.Unstructured) (resp } err = patchedResource.UnmarshalJSON(resourceRaw) if err != nil { - glog.Infof("failed to unmarshall resource to undstructured: %v", err) + logger.Error(err, "failed to unmmarshal resource") resp.Success = false resp.Message = fmt.Sprintf("failed to process JSON patches: %v", err) return resp, resource diff --git a/pkg/engine/mutate/patches_test.go b/pkg/engine/mutate/patches_test.go index f38b68d2c4..1b617d5517 100644 --- a/pkg/engine/mutate/patches_test.go +++ b/pkg/engine/mutate/patches_test.go @@ -5,6 +5,7 @@ import ( "gotest.tools/assert" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/log" types "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/nirmata/kyverno/pkg/engine/utils" @@ -41,7 +42,7 @@ func TestProcessPatches_EmptyPatches(t *testing.T) { if err != nil { t.Error(err) } - rr, _ := ProcessPatches(emptyRule, *resourceUnstructured) + rr, _ := ProcessPatches(log.Log, emptyRule, *resourceUnstructured) assert.Check(t, rr.Success) assert.Assert(t, len(rr.Patches) == 0) } @@ -70,14 +71,14 @@ func makeRuleWithPatches(patches []types.Patch) types.Rule { func TestProcessPatches_EmptyDocument(t *testing.T) { rule := makeRuleWithPatch(makeAddIsMutatedLabelPatch()) - rr, _ := ProcessPatches(rule, unstructured.Unstructured{}) + rr, _ := ProcessPatches(log.Log, rule, unstructured.Unstructured{}) assert.Assert(t, !rr.Success) assert.Assert(t, len(rr.Patches) == 0) } func TestProcessPatches_AllEmpty(t *testing.T) { emptyRule := types.Rule{} - rr, _ := ProcessPatches(emptyRule, unstructured.Unstructured{}) + rr, _ := ProcessPatches(log.Log, emptyRule, unstructured.Unstructured{}) assert.Check(t, !rr.Success) assert.Assert(t, len(rr.Patches) == 0) } @@ -90,7 +91,7 @@ func TestProcessPatches_AddPathDoesntExist(t *testing.T) { if err != nil { t.Error(err) } - rr, _ := ProcessPatches(rule, *resourceUnstructured) + rr, _ := ProcessPatches(log.Log, rule, *resourceUnstructured) assert.Check(t, !rr.Success) assert.Assert(t, len(rr.Patches) == 0) } @@ -102,7 +103,7 @@ func TestProcessPatches_RemovePathDoesntExist(t *testing.T) { if err != nil { t.Error(err) } - rr, _ := ProcessPatches(rule, *resourceUnstructured) + rr, _ := ProcessPatches(log.Log, rule, *resourceUnstructured) assert.Check(t, rr.Success) assert.Assert(t, len(rr.Patches) == 0) } @@ -115,7 +116,7 @@ func TestProcessPatches_AddAndRemovePathsDontExist_EmptyResult(t *testing.T) { if err != nil { t.Error(err) } - rr, _ := ProcessPatches(rule, *resourceUnstructured) + rr, _ := ProcessPatches(log.Log, rule, *resourceUnstructured) assert.Check(t, !rr.Success) assert.Assert(t, len(rr.Patches) == 0) } @@ -129,7 +130,7 @@ func TestProcessPatches_AddAndRemovePathsDontExist_ContinueOnError_NotEmptyResul if err != nil { t.Error(err) } - rr, _ := ProcessPatches(rule, *resourceUnstructured) + rr, _ := ProcessPatches(log.Log, rule, *resourceUnstructured) assert.Check(t, rr.Success) assert.Assert(t, len(rr.Patches) != 0) assertEqStringAndData(t, `{"path":"/metadata/labels/label3","op":"add","value":"label3Value"}`, rr.Patches[0]) @@ -142,7 +143,7 @@ func TestProcessPatches_RemovePathDoesntExist_EmptyResult(t *testing.T) { if err != nil { t.Error(err) } - rr, _ := ProcessPatches(rule, *resourceUnstructured) + rr, _ := ProcessPatches(log.Log, rule, *resourceUnstructured) assert.Check(t, rr.Success) assert.Assert(t, len(rr.Patches) == 0) } @@ -155,7 +156,7 @@ func TestProcessPatches_RemovePathDoesntExist_NotEmptyResult(t *testing.T) { if err != nil { t.Error(err) } - rr, _ := ProcessPatches(rule, *resourceUnstructured) + rr, _ := ProcessPatches(log.Log, rule, *resourceUnstructured) assert.Check(t, rr.Success) assert.Assert(t, len(rr.Patches) == 1) assertEqStringAndData(t, `{"path":"/metadata/labels/label2","op":"add","value":"label2Value"}`, rr.Patches[0]) diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index d1fab46211..b147f5b490 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -5,12 +5,13 @@ 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" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/log" ) const ( @@ -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() @@ -74,15 +74,13 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) { continue } - ruleResponse, patchedResource = mutate.ProcessOverlay(rule.Name, overlay, patchedResource) + ruleResponse, patchedResource = mutate.ProcessOverlay(logger, rule.Name, overlay, patchedResource) 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) @@ -92,8 +90,8 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) { // Process Patches 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()) + ruleResponse, patchedResource = mutate.ProcessPatches(logger, rule, patchedResource) + logger.V(4).Info("patches applied successfully") resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse) incrementAppliedRuleCount(&resp) } @@ -106,14 +104,14 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) { if strings.Contains(PodControllers, resource.GetKind()) { var ruleResponse response.RuleResponse - ruleResponse, patchedResource = mutate.ProcessOverlay(rule.Name, podTemplateRule, patchedResource) + ruleResponse, patchedResource = mutate.ProcessOverlay(logger, 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 diff --git a/pkg/engine/mutation_test.go b/pkg/engine/mutation_test.go index 278d9672f2..07f345e265 100644 --- a/pkg/engine/mutation_test.go +++ b/pkg/engine/mutation_test.go @@ -67,11 +67,17 @@ func Test_VariableSubstitutionOverlay(t *testing.T) { expectedPatch := []byte(`{ "op": "add", "path": "/metadata/labels", "value":{"appname":"check-root-user"} }`) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + if err != nil { + t.Error(err) + } resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(rawResource) + err = ctx.AddResource(rawResource) + if err != nil { + t.Error(err) + } value, err := ctx.Query("request.object.metadata.name") t.Log(value) if err != nil { @@ -139,12 +145,14 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) { }`) var policy kyverno.ClusterPolicy - json.Unmarshal(policyraw, &policy) + err := json.Unmarshal(policyraw, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw) assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(resourceRaw) + err = ctx.AddResource(resourceRaw) + assert.NilError(t, err) policyContext := PolicyContext{ Policy: policy, diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index c97b2eccb8..1ee8449d23 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -9,8 +9,7 @@ import ( "github.com/nirmata/kyverno/pkg/utils" authenticationv1 "k8s.io/api/authentication/v1" rbacv1 "k8s.io/api/rbac/v1" - - "github.com/golang/glog" + "sigs.k8s.io/controller-runtime/pkg/log" "github.com/minio/minio/pkg/wildcard" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/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 } diff --git a/pkg/engine/validate/pattern.go b/pkg/engine/validate/pattern.go index e7b37283bd..7779f9b6ca 100644 --- a/pkg/engine/validate/pattern.go +++ b/pkg/engine/validate/pattern.go @@ -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 diff --git a/pkg/engine/validate/pattern_test.go b/pkg/engine/validate/pattern_test.go index 8ee4e34881..8d8d437d2b 100644 --- a/pkg/engine/validate/pattern_test.go +++ b/pkg/engine/validate/pattern_test.go @@ -6,13 +6,14 @@ import ( "github.com/nirmata/kyverno/pkg/engine/operator" "gotest.tools/assert" + "sigs.k8s.io/controller-runtime/pkg/log" ) 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) { diff --git a/pkg/engine/validate/validate.go b/pkg/engine/validate/validate.go index 9d94f07789..6e3d40da7d 100644 --- a/pkg/engine/validate/validate.go +++ b/pkg/engine/validate/validate.go @@ -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,19 +221,21 @@ 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) } + // TODO : SA4004: the surrounding loop is unconditionally terminated (staticcheck) return nil, errors.New("Reference to non-existent place in the document") } + return nil, nil // Just a hack to fix the lint } 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 +253,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 } diff --git a/pkg/engine/validate/validate_test.go b/pkg/engine/validate/validate_test.go index acabd87ac1..7f916239e0 100644 --- a/pkg/engine/validate/validate_test.go +++ b/pkg/engine/validate/validate_test.go @@ -5,6 +5,7 @@ import ( "testing" "gotest.tools/assert" + "sigs.k8s.io/controller-runtime/pkg/log" ) func TestValidateMap(t *testing.T) { @@ -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) } @@ -557,9 +558,10 @@ func TestValidateMap_livenessProbeIsNull(t *testing.T) { var pattern, resource map[string]interface{} assert.Assert(t, json.Unmarshal(rawPattern, &pattern)) - json.Unmarshal(rawMap, &resource) + err := json.Unmarshal(rawMap, &resource) + assert.NilError(t, err) - path, err := validateMap(resource, pattern, pattern, "/") + path, err := validateMap(log.Log, resource, pattern, pattern, "/") assert.Equal(t, path, "") assert.NilError(t, err) } @@ -649,7 +651,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 +697,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 +732,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 +786,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 +840,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 +894,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 +948,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 +1002,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 +1056,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 +1110,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 +1151,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 +1193,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 +1247,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 +1278,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) } @@ -1344,10 +1346,12 @@ func TestValidateMapElement_OneElementInArrayNotPass(t *testing.T) { ]`) var pattern, resource interface{} - json.Unmarshal(rawPattern, &pattern) - json.Unmarshal(rawMap, &resource) + err := json.Unmarshal(rawPattern, &pattern) + assert.NilError(t, err) + err = json.Unmarshal(rawMap, &resource) + assert.NilError(t, err) - 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) } diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index fc4e89bf1c..00c32f6791 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -5,7 +5,7 @@ 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" @@ -13,6 +13,7 @@ import ( "github.com/nirmata/kyverno/pkg/engine/validate" "github.com/nirmata/kyverno/pkg/engine/variables" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/log" ) //Validate applies validation rules from policy on the resource @@ -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) } @@ -234,7 +233,7 @@ func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructu errorStr = append(errorStr, err.Error()) } resp.Success = false - glog.V(4).Infof("Validation rule '%s' failed. %s", rule.Name, errorStr) + log.V(4).Info(fmt.Sprintf("Validation rule '%s' failed. %s", rule.Name, errorStr)) if rule.Validation.Message == "" { resp.Message = fmt.Sprintf("Validation rule '%s' has failed", rule.Name) } else { diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go index 5a77d81b69..fb6f272966 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -23,7 +23,8 @@ func TestGetAnchorsFromMap_ThereAreAnchors(t *testing.T) { }`) var unmarshalled map[string]interface{} - json.Unmarshal(rawMap, &unmarshalled) + err := json.Unmarshal(rawMap, &unmarshalled) + assert.NilError(t, err) actualMap := utils.GetAnchorsFromMap(unmarshalled) assert.Equal(t, len(actualMap), 2) @@ -114,7 +115,8 @@ func TestValidate_image_tag_fail(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -212,7 +214,8 @@ func TestValidate_image_tag_pass(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -289,7 +292,8 @@ func TestValidate_Fail_anyPattern(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -370,7 +374,8 @@ func TestValidate_host_network_port(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -459,7 +464,8 @@ func TestValidate_anchor_arraymap_pass(t *testing.T) { } `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -547,8 +553,8 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) { } `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) - + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured}) @@ -616,7 +622,8 @@ func TestValidate_anchor_map_notfound(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -688,7 +695,8 @@ func TestValidate_anchor_map_found_valid(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -760,7 +768,8 @@ func TestValidate_anchor_map_found_invalid(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -834,7 +843,8 @@ func TestValidate_AnchorList_pass(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -908,7 +918,8 @@ func TestValidate_AnchorList_fail(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -982,7 +993,8 @@ func TestValidate_existenceAnchor_fail(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -1057,7 +1069,8 @@ func TestValidate_existenceAnchor_pass(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -1144,7 +1157,8 @@ func TestValidate_negationAnchor_deny(t *testing.T) { } `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -1230,7 +1244,8 @@ func TestValidate_negationAnchor_pass(t *testing.T) { `) var policy kyverno.ClusterPolicy - json.Unmarshal(rawPolicy, &policy) + err := json.Unmarshal(rawPolicy, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) @@ -1297,12 +1312,14 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) { }`) var policy kyverno.ClusterPolicy - json.Unmarshal(policyraw, &policy) + err := json.Unmarshal(policyraw, &policy) + assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw) assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(resourceRaw) + err = ctx.AddResource(resourceRaw) + assert.NilError(t, err) policyContext := PolicyContext{ Policy: policy, @@ -1392,7 +1409,8 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfies(t *t assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(resourceRaw) + err = ctx.AddResource(resourceRaw) + assert.NilError(t, err) policyContext := PolicyContext{ Policy: policy, @@ -1482,7 +1500,8 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(resourceRaw) + err = ctx.AddResource(resourceRaw) + assert.NilError(t, err) policyContext := PolicyContext{ Policy: policy, @@ -1572,7 +1591,8 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter assert.NilError(t, err) ctx := context.NewContext() - ctx.AddResource(resourceRaw) + err = ctx.AddResource(resourceRaw) + assert.NilError(t, err) policyContext := PolicyContext{ Policy: policy, diff --git a/pkg/engine/variables/common.go b/pkg/engine/variables/common.go new file mode 100644 index 0000000000..292b82d988 --- /dev/null +++ b/pkg/engine/variables/common.go @@ -0,0 +1,10 @@ +package variables + +import "regexp" + +//IsVariable returns true if the element contains a 'valid' variable {{}} +func IsVariable(element string) bool { + validRegex := regexp.MustCompile(variableRegex) + groups := validRegex.FindAllStringSubmatch(element, -1) + return len(groups) != 0 +} diff --git a/pkg/engine/variables/evaluate.go b/pkg/engine/variables/evaluate.go index 519a909d01..d0ebf7ed74 100644 --- a/pkg/engine/variables/evaluate.go +++ b/pkg/engine/variables/evaluate.go @@ -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 } } diff --git a/pkg/engine/variables/evaluate_test.go b/pkg/engine/variables/evaluate_test.go index 33b3a4f2b7..d7d409b8f5 100644 --- a/pkg/engine/variables/evaluate_test.go +++ b/pkg/engine/variables/evaluate_test.go @@ -6,6 +6,7 @@ import ( kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/nirmata/kyverno/pkg/engine/context" + "sigs.k8s.io/controller-runtime/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") } } diff --git a/pkg/engine/variables/operator/equal.go b/pkg/engine/variables/operator/equal.go index 81ea80c621..1bc181f0f8 100644 --- a/pkg/engine/variables/operator/equal.go +++ b/pkg/engine/variables/operator/equal.go @@ -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 } } diff --git a/pkg/engine/variables/operator/notequal.go b/pkg/engine/variables/operator/notequal.go index 9af9e891f3..ce9b4e87f8 100644 --- a/pkg/engine/variables/operator/notequal.go +++ b/pkg/engine/variables/operator/notequal.go @@ -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 } } diff --git a/pkg/engine/variables/operator/operator.go b/pkg/engine/variables/operator/operator.go index 2b3f9ce7a1..adafe69982 100644 --- a/pkg/engine/variables/operator/operator.go +++ b/pkg/engine/variables/operator/operator.go @@ -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 } diff --git a/pkg/engine/variables/variables_test.go b/pkg/engine/variables/variables_test.go index 6d2eb7c60c..e01bf4f820 100644 --- a/pkg/engine/variables/variables_test.go +++ b/pkg/engine/variables/variables_test.go @@ -7,6 +7,7 @@ import ( kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" authenticationv1 "k8s.io/api/authentication/v1" + "sigs.k8s.io/controller-runtime/pkg/log" "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) diff --git a/pkg/engine/variables/vars.go b/pkg/engine/variables/vars.go index 4c336f2ede..59fd8f7fd5 100644 --- a/pkg/engine/variables/vars.go +++ b/pkg/engine/variables/vars.go @@ -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 diff --git a/pkg/engine/variables/vars_test.go b/pkg/engine/variables/vars_test.go index 171c62ace4..aae517a082 100644 --- a/pkg/engine/variables/vars_test.go +++ b/pkg/engine/variables/vars_test.go @@ -6,6 +6,7 @@ import ( "github.com/nirmata/kyverno/pkg/engine/context" "gotest.tools/assert" + "sigs.k8s.io/controller-runtime/pkg/log" ) func Test_subVars_success(t *testing.T) { @@ -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) } diff --git a/pkg/event/controller.go b/pkg/event/controller.go index aa98d09e42..a33e50f756 100644 --- a/pkg/event/controller.go +++ b/pkg/event/controller.go @@ -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" @@ -17,6 +17,7 @@ import ( "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" + "k8s.io/klog" ) //Generator generate events @@ -34,6 +35,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 +44,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(klog.Infof) 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 +84,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 +98,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 +120,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 +151,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 +159,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 +174,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 +200,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 +218,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, diff --git a/pkg/generate/cleanup/cleanup.go b/pkg/generate/cleanup/cleanup.go index d25fddd44e..de1758aeba 100644 --- a/pkg/generate/cleanup/cleanup.go +++ b/pkg/generate/cleanup/cleanup.go @@ -1,19 +1,20 @@ package cleanup import ( - "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" apierrors "k8s.io/apimachinery/pkg/api/errors" ) func (c *Controller) processGR(gr kyverno.GenerateRequest) error { + logger := c.log.WithValues("kind", gr.Kind, "namespace", gr.Namespace, "name", gr.Name) // 1- Corresponding policy has been deleted // then we dont delete the generated resources // 2- The trigger resource is deleted, then delete the generated resources - if !ownerResourceExists(c.client, gr) { - if err := deleteGeneratedResources(c.client, gr); err != nil { + if !ownerResourceExists(logger, c.client, gr) { + if err := deleteGeneratedResources(logger, c.client, gr); err != nil { return err } // - trigger-resource is deleted @@ -24,25 +25,25 @@ func (c *Controller) processGR(gr kyverno.GenerateRequest) error { return nil } -func ownerResourceExists(client *dclient.Client, gr kyverno.GenerateRequest) bool { +func ownerResourceExists(log logr.Logger, client *dclient.Client, gr kyverno.GenerateRequest) bool { _, err := client.GetResource(gr.Spec.Resource.Kind, gr.Spec.Resource.Namespace, gr.Spec.Resource.Name) // trigger resources has been deleted if apierrors.IsNotFound(err) { return false } if err != nil { - glog.V(4).Infof("Failed to get resource %s/%s/%s: error : %s", gr.Spec.Resource.Kind, gr.Spec.Resource.Namespace, gr.Spec.Resource.Name, err) + log.Error(err, "failed to get resource", "genKind", gr.Spec.Resource.Kind, "genNamespace", gr.Spec.Resource.Namespace, "genName", gr.Spec.Resource.Name) } // if there was an error while querying the resources we dont delete the generated resources // but expect the deletion in next reconciliation loop return true } -func deleteGeneratedResources(client *dclient.Client, gr kyverno.GenerateRequest) error { +func deleteGeneratedResources(log logr.Logger, client *dclient.Client, gr kyverno.GenerateRequest) error { for _, genResource := range gr.Status.GeneratedResources { err := client.DeleteResource(genResource.Kind, genResource.Namespace, genResource.Name, false) if apierrors.IsNotFound(err) { - glog.V(4).Infof("resource %s/%s/%s not found, will no delete", genResource.Kind, genResource.Namespace, genResource.Name) + log.Error(err, "resource not foundl will not delete", "genKind", gr.Spec.Resource.Kind, "genNamespace", gr.Spec.Resource.Namespace, "genName", gr.Spec.Resource.Name) continue } if err != nil { diff --git a/pkg/generate/cleanup/controller.go b/pkg/generate/cleanup/controller.go index 396058a346..2c31254219 100644 --- a/pkg/generate/cleanup/controller.go +++ b/pkg/generate/cleanup/controller.go @@ -1,11 +1,9 @@ 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 +51,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 +61,7 @@ func NewController( pInformer kyvernoinformer.ClusterPolicyInformer, grInformer kyvernoinformer.GenerateRequestInformer, dynamicInformer dynamicinformer.DynamicSharedInformerFactory, + log logr.Logger, ) *Controller { c := Controller{ kyvernoClient: kyvernoclient, @@ -70,6 +70,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 +103,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 +117,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 +156,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) + logger.Error(err, "failed to extract key") 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 +224,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 { diff --git a/pkg/generate/controller.go b/pkg/generate/controller.go index d40ed2af0d..b848609177 100644 --- a/pkg/generate/controller.go +++ b/pkg/generate/controller.go @@ -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,9 +56,9 @@ type Controller struct { dynamicInformer dynamicinformer.DynamicSharedInformerFactory //TODO: list of generic informers // only support Namespaces for re-evalutation on resource updates - nsInformer informers.GenericInformer - + nsInformer informers.GenericInformer policyStatusListener policystatus.Listener + log logr.Logger } //NewController returns an instance of the Generate-Request Controller @@ -72,6 +71,7 @@ func NewController( pvGenerator policyviolation.GeneratorInterface, dynamicInformer dynamicinformer.DynamicSharedInformerFactory, policyStatus policystatus.Listener, + log logr.Logger, ) *Controller { c := Controller{ client: client, @@ -82,6 +82,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, policyStatusListener: policyStatus, } c.statusControl = StatusControl{client: kyvernoclient} @@ -117,11 +118,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 @@ -134,13 +136,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 { @@ -148,11 +151,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 @@ -183,34 +186,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++ { @@ -239,27 +244,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 { @@ -268,7 +275,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) diff --git a/pkg/generate/generate.go b/pkg/generate/generate.go index bff1dbbca6..9f3edc4bc8 100644 --- a/pkg/generate/generate.go +++ b/pkg/generate/generate.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "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" @@ -17,6 +17,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 @@ -24,45 +25,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 } @@ -76,12 +78,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 c.applyGeneratePolicy(policyContext, gr) + return c.applyGeneratePolicy(logger, policyContext, gr) } func updateStatus(statusControl StatusControlInterface, gr kyverno.GenerateRequest, err error, genResources []kyverno.ResourceSpec) error { @@ -93,7 +95,7 @@ func updateStatus(statusControl StatusControlInterface, gr kyverno.GenerateReque return statusControl.Success(gr, genResources) } -func (c *Controller) applyGeneratePolicy(policyContext engine.PolicyContext, gr kyverno.GenerateRequest) ([]kyverno.ResourceSpec, error) { +func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext engine.PolicyContext, gr kyverno.GenerateRequest) ([]kyverno.ResourceSpec, error) { // List of generatedResources var genResources []kyverno.ResourceSpec // Get the response as the actions to be performed on the resource @@ -113,9 +115,8 @@ func (c *Controller) applyGeneratePolicy(policyContext engine.PolicyContext, gr if !rule.HasGenerate() { continue } - startTime := time.Now() - genResource, err := applyRule(c.client, rule, resource, ctx, processExisting) + genResource, err := applyRule(log, c.client, rule, resource, ctx, processExisting) if err != nil { return nil, err } @@ -172,7 +173,7 @@ func updateGenerateExecutionTime(newTime time.Duration, oldAverageTimeString str return time.Duration(newAverageTimeInNanoSeconds) * time.Nanosecond } -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 @@ -187,7 +188,7 @@ func applyRule(client *dclient.Client, rule kyverno.Rule, resource unstructured. // format : {{ 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") @@ -219,9 +220,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 @@ -248,38 +249,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 { @@ -288,18 +289,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 { @@ -308,6 +308,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 } @@ -325,8 +326,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 { @@ -349,10 +348,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 diff --git a/pkg/generate/labels.go b/pkg/generate/labels.go index 282caf55fa..c7d67b5a55 100644 --- a/pkg/generate/labels.go +++ b/pkg/generate/labels.go @@ -3,8 +3,8 @@ package generate import ( "fmt" - "github.com/golang/glog" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/log" ) func manageLabels(unstr *unstructured.Unstructured, triggerResource unstructured.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 } } diff --git a/pkg/generate/report.go b/pkg/generate/report.go index eaa5939e41..f9d24fcc10 100644 --- a/pkg/generate/report.go +++ b/pkg/generate/report.go @@ -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...) } diff --git a/pkg/generate/status.go b/pkg/generate/status.go index 70d9539053..db0182f89a 100644 --- a/pkg/generate/status.go +++ b/pkg/generate/status.go @@ -1,9 +1,9 @@ package generate import ( - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + "sigs.k8s.io/controller-runtime/pkg/log" ) //StatusControlInterface provides interface to update status subresource @@ -25,10 +25,10 @@ func (sc StatusControl) Failed(gr kyverno.GenerateRequest, message string, genRe gr.Status.GeneratedResources = genResources _, err := sc.client.KyvernoV1().GenerateRequests("kyverno").UpdateStatus(&gr) if err != nil { - glog.V(4).Infof("FAILED: updated gr %s status to %s", gr.Name, string(kyverno.Failed)) + log.Log.Error(err, "failed to update generate request status", "name", gr.Name) return err } - glog.V(4).Infof("updated gr %s status to %s", gr.Name, string(kyverno.Failed)) + log.Log.Info("updated generate request status", "name", gr.Name, "status", string(kyverno.Failed)) return nil } @@ -41,9 +41,9 @@ func (sc StatusControl) Success(gr kyverno.GenerateRequest, genResources []kyver _, err := sc.client.KyvernoV1().GenerateRequests("kyverno").UpdateStatus(&gr) if err != nil { - glog.V(4).Infof("FAILED: updated gr %s status to %s", gr.Name, string(kyverno.Completed)) + log.Log.Error(err, "failed to update generate request status", "name", gr.Name) return err } - glog.V(4).Infof("updated gr %s status to %s", gr.Name, string(kyverno.Completed)) + log.Log.Info("updated generate request status", "name", gr.Name, "status", string(kyverno.Completed)) return nil } diff --git a/pkg/kyverno/apply/command.go b/pkg/kyverno/apply/command.go index 95e0f241cd..26023c43e1 100644 --- a/pkg/kyverno/apply/command.go +++ b/pkg/kyverno/apply/command.go @@ -13,8 +13,6 @@ import ( policy2 "github.com/nirmata/kyverno/pkg/policy" - "github.com/golang/glog" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" @@ -34,6 +32,7 @@ import ( yamlv2 "gopkg.in/yaml.v2" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/kubernetes/scheme" + log "sigs.k8s.io/controller-runtime/pkg/log" ) func Command() *cobra.Command { @@ -51,7 +50,7 @@ func Command() *cobra.Command { defer func() { if err != nil { if !sanitizedError.IsErrorSanitized(err) { - glog.V(4).Info(err) + log.Log.Error(err, "failed to sanitize") err = fmt.Errorf("Internal error") } } @@ -71,7 +70,7 @@ func Command() *cobra.Command { } for _, policy := range policies { - err := policy2.Validate(utils.MarshalPolicy(*policy)) + err := policy2.Validate(utils.MarshalPolicy(*policy), nil, true) if err != nil { return sanitizedError.New(fmt.Sprintf("Policy %v is not valid", policy.Name)) } diff --git a/pkg/kyverno/main.go b/pkg/kyverno/main.go index d0d1163ef6..6424a57ddc 100644 --- a/pkg/kyverno/main.go +++ b/pkg/kyverno/main.go @@ -9,6 +9,9 @@ import ( "github.com/nirmata/kyverno/pkg/kyverno/apply" "github.com/nirmata/kyverno/pkg/kyverno/version" + "k8s.io/klog" + "k8s.io/klog/klogr" + log "sigs.k8s.io/controller-runtime/pkg/log" "github.com/spf13/cobra" ) @@ -19,7 +22,7 @@ func CLI() { Short: "kyverno manages native policies of Kubernetes", } - configureGlog(cli) + configurelog(cli) commands := []*cobra.Command{ version.Command(), @@ -36,9 +39,9 @@ func CLI() { } } -func configureGlog(cli *cobra.Command) { - flag.Parse() - _ = flag.Set("logtostderr", "true") +func configurelog(cli *cobra.Command) { + klog.InitFlags(nil) + log.SetLogger(klogr.New()) cli.PersistentFlags().AddGoFlagSet(flag.CommandLine) _ = cli.PersistentFlags().MarkHidden("alsologtostderr") diff --git a/pkg/kyverno/validate/command.go b/pkg/kyverno/validate/command.go index dce6e7dc32..82863aa397 100644 --- a/pkg/kyverno/validate/command.go +++ b/pkg/kyverno/validate/command.go @@ -11,13 +11,12 @@ import ( "github.com/nirmata/kyverno/pkg/kyverno/sanitizedError" - "github.com/golang/glog" - policyvalidate "github.com/nirmata/kyverno/pkg/policy" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/util/yaml" + log "sigs.k8s.io/controller-runtime/pkg/log" ) func Command() *cobra.Command { @@ -29,7 +28,7 @@ func Command() *cobra.Command { defer func() { if err != nil { if !sanitizedError.IsErrorSanitized(err) { - glog.V(4).Info(err) + log.Log.Error(err, "failed to sanitize") err = fmt.Errorf("Internal error") } } @@ -45,7 +44,7 @@ func Command() *cobra.Command { } for _, policy := range policies { - err = policyvalidate.Validate(utils.MarshalPolicy(*policy)) + err = policyvalidate.Validate(utils.MarshalPolicy(*policy), nil, true) if err != nil { fmt.Println("Policy " + policy.Name + " is invalid") } else { diff --git a/pkg/openapi/crdSync.go b/pkg/openapi/crdSync.go index 61c32c45d3..2c8f3023e4 100644 --- a/pkg/openapi/crdSync.go +++ b/pkg/openapi/crdSync.go @@ -6,13 +6,12 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "github.com/golang/glog" - "gopkg.in/yaml.v2" "github.com/googleapis/gnostic/compiler" openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2" + log "sigs.k8s.io/controller-runtime/pkg/log" client "github.com/nirmata/kyverno/pkg/dclient" "k8s.io/apimachinery/pkg/util/wait" @@ -44,12 +43,12 @@ func NewCRDSync(client *client.Client) *crdSync { func (c *crdSync) Run(workers int, stopCh <-chan struct{}) { newDoc, err := c.client.DiscoveryClient.OpenAPISchema() if err != nil { - glog.V(4).Infof("cannot get openapi schema: %v", err) + log.Log.Error(err, "cannot get openapi schema") } err = useOpenApiDocument(newDoc) if err != nil { - glog.V(4).Infof("Could not set custom OpenApi document: %v\n", err) + log.Log.Error(err, "Could not set custom OpenApi document") } // Sync CRD before kyverno starts @@ -63,7 +62,7 @@ func (c *crdSync) Run(workers int, stopCh <-chan struct{}) { func (c *crdSync) sync() { crds, err := c.client.ListResource("CustomResourceDefinition", "", nil) if err != nil { - glog.V(4).Infof("could not fetch crd's from server: %v", err) + log.Log.Error(err, "could not fetch crd's from server") return } @@ -93,7 +92,7 @@ func parseCRD(crd unstructured.Unstructured) { crdName := crdDefinition.Spec.Names.Kind if len(crdDefinition.Spec.Versions) < 1 { - glog.V(4).Infof("could not parse crd schema, no versions present") + log.Log.V(4).Info("could not parse crd schema, no versions present") return } @@ -104,7 +103,7 @@ func parseCRD(crd unstructured.Unstructured) { parsedSchema, err := openapi_v2.NewSchema(schema, compiler.NewContext("schema", nil)) if err != nil { - glog.V(4).Infof("could not parse crd schema:%v", err) + log.Log.Error(err, "could not parse crd schema:") return } diff --git a/pkg/openapi/validation.go b/pkg/openapi/validation.go index bb724b9d0e..01ea226b0f 100644 --- a/pkg/openapi/validation.go +++ b/pkg/openapi/validation.go @@ -8,12 +8,9 @@ import ( "strings" "sync" + data "github.com/nirmata/kyverno/api" "github.com/nirmata/kyverno/pkg/engine/utils" - "github.com/nirmata/kyverno/data" - - "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/engine" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -23,6 +20,7 @@ import ( "github.com/googleapis/gnostic/compiler" "k8s.io/kube-openapi/pkg/util/proto" "k8s.io/kube-openapi/pkg/util/proto/validation" + log "sigs.k8s.io/controller-runtime/pkg/log" "gopkg.in/yaml.v2" ) @@ -120,7 +118,7 @@ func validatePolicyMutation(policy v1.ClusterPolicy) error { newPolicy.Spec.Rules = rules resource, _ := generateEmptyResource(openApiGlobalState.definitions[openApiGlobalState.kindToDefinitionName[kind]]).(map[string]interface{}) if resource == nil { - glog.V(4).Infof("Cannot Validate policy: openApi definition now found for %v", kind) + log.Log.V(4).Info(fmt.Sprintf("Cannot Validate policy: openApi definition now found for %v", kind)) return nil } newResource := unstructured.Unstructured{Object: resource} diff --git a/pkg/policy/actions.go b/pkg/policy/actions.go new file mode 100644 index 0000000000..4442113cd4 --- /dev/null +++ b/pkg/policy/actions.go @@ -0,0 +1,61 @@ +package policy + +import ( + "fmt" + + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + dclient "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/policy/generate" + "github.com/nirmata/kyverno/pkg/policy/mutate" + "github.com/nirmata/kyverno/pkg/policy/validate" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +//Validation provides methods to validate a rule +type Validation interface { + Validate() (string, error) +} + +//validateAction performs validation on the rule actions +// - Mutate +// - Validation +// - Generate +func validateActions(idx int, rule kyverno.Rule, client *dclient.Client, mock bool) error { + var checker Validation + + // Mutate + if rule.HasMutate() { + checker = mutate.NewMutateFactory(rule.Mutation) + if path, err := checker.Validate(); err != nil { + return fmt.Errorf("path: spec.rules[%d].mutate.%s.: %v", idx, path, err) + } + } + + // Validate + if rule.HasValidate() { + checker = validate.NewValidateFactory(rule.Validation) + if path, err := checker.Validate(); err != nil { + return fmt.Errorf("path: spec.rules[%d].validate.%s.: %v", idx, path, err) + } + } + + // Generate + if rule.HasGenerate() { + //TODO: this check is there to support offline validations + // generate uses selfSubjectReviews to verify actions + // this need to modified to use different implementation for online and offline mode + if mock { + checker = generate.NewFakeGenerate(rule.Generation) + if path, err := checker.Validate(); err != nil { + return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err) + } + } else { + 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) + } + } + } + + return nil +} diff --git a/pkg/policy/apply.go b/pkg/policy/apply.go index 32d654346c..ad60ad5d6d 100644 --- a/pkg/policy/apply.go +++ b/pkg/policy/apply.go @@ -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,12 @@ import ( // applyPolicy applies policy on a resource //TODO: generation rules -func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructured) (responses []response.EngineResponse) { +func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, logger logr.Logger) (responses []response.EngineResponse) { startTime := time.Now() - 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)) }() var engineResponses []response.EngineResponse @@ -32,13 +32,15 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure var err error // build context ctx := context.NewContext() - ctx.AddResource(transformResource(resource)) - + err = ctx.AddResource(transformResource(resource)) + if err != nil { + logger.Error(err, "enable to add transform resource to ctx") + } //MUTATION - engineResponse, err = mutation(policy, resource, ctx) + engineResponse, err = mutation(policy, resource, 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") } //VALIDATION @@ -48,52 +50,52 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure //TODO: GENERATION return engineResponses } -func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, ctx context.EvalInterface) (response.EngineResponse, error) { +func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, 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 @@ -105,14 +107,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) diff --git a/pkg/policy/background.go b/pkg/policy/background.go index 423fc51c4d..2331359534 100644 --- a/pkg/policy/background.go +++ b/pkg/policy/background.go @@ -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" + "sigs.k8s.io/controller-runtime/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) } } diff --git a/pkg/policy/cleanup.go b/pkg/policy/cleanup.go index 6a41c579ba..40394c2785 100644 --- a/pkg/policy/cleanup.go +++ b/pkg/policy/cleanup.go @@ -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) } diff --git a/pkg/policy/clusterpv.go b/pkg/policy/clusterpv.go index 0f9f2564ae..35704f72d8 100644 --- a/pkg/policy/clusterpv.go +++ b/pkg/policy/clusterpv.go @@ -1,15 +1,13 @@ package policy import ( - "fmt" - - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "k8s.io/client-go/tools/cache" ) func (pc *PolicyController) addClusterPolicyViolation(obj interface{}) { pv := obj.(*kyverno.ClusterPolicyViolation) + logger := pc.log.WithValues("kind", pv.Kind, "namespace", pv.Namespace, "name", pv.Name) if pv.DeletionTimestamp != nil { // On a restart of the controller manager, it's possible for an object to @@ -22,15 +20,15 @@ func (pc *PolicyController) addClusterPolicyViolation(obj interface{}) { ps := pc.getPolicyForClusterPolicyViolation(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("Cluster Policy Violation %s does not belong to an active policy, will be cleanedup", pv.Name) + logger.V(4).Info("Cluster Policy Violation does not belong to an active policy, will be cleanedup") if err := pc.pvControl.DeleteClusterPolicyViolation(pv.Name); err != nil { - glog.Errorf("Failed to deleted cluster policy violation %s: %v", pv.Name, err) + logger.Error(err, "failed to delete resource") return } - glog.V(4).Infof("Cluster Policy Violation %s deleted", pv.Name) + logger.V(4).Info("resource deleted") return } - glog.V(4).Infof("Cluster Policy Violation %s added.", pv.Name) + logger.V(4).Info("resource added") for _, p := range ps { pc.enqueuePolicy(p) } @@ -44,19 +42,20 @@ func (pc *PolicyController) updateClusterPolicyViolation(old, cur interface{}) { // Two different versions of the same replica set will always have different RVs. return } + logger := pc.log.WithValues("kind", curPV.Kind, "namespace", curPV.Namespace, "name", curPV.Name) ps := pc.getPolicyForClusterPolicyViolation(curPV) if len(ps) == 0 { // there is no cluster policy for this violation, so we can delete this cluster policy violation - glog.V(4).Infof("Cluster Policy Violation %s does not belong to an active policy, will be cleanedup", curPV.Name) + logger.V(4).Info("Cluster Policy Violation does not belong to an active policy, will be cleanedup") if err := pc.pvControl.DeleteClusterPolicyViolation(curPV.Name); err != nil { - glog.Errorf("Failed to deleted cluster policy violation %s: %v", curPV.Name, err) + logger.Error(err, "failed to delete resource") return } - glog.V(4).Infof("PolicyViolation %s deleted", curPV.Name) + logger.V(4).Info("resource deleted") return } - glog.V(4).Infof("Cluster PolicyViolation %s updated", curPV.Name) + logger.V(4).Info("resource updated") for _, p := range ps { pc.enqueuePolicy(p) } @@ -67,6 +66,7 @@ func (pc *PolicyController) updateClusterPolicyViolation(old, cur interface{}) { // a DeletionFinalStateUnknown marker item. func (pc *PolicyController) deleteClusterPolicyViolation(obj interface{}) { + logger := pc.log pv, ok := obj.(*kyverno.ClusterPolicyViolation) // When a delete is dropped, the relist will notice a PolicyViolation in the store not // in the list, leading to the insertion of a tombstone object which contains @@ -75,33 +75,35 @@ func (pc *PolicyController) deleteClusterPolicyViolation(obj interface{}) { 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 } pv, ok = tombstone.Obj.(*kyverno.ClusterPolicyViolation) 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 } } + logger = logger.WithValues("kind", pv.Kind, "namespace", pv.Namespace, "name", pv.Name) ps := pc.getPolicyForClusterPolicyViolation(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("Cluster Policy Violation %s does not belong to an active policy, will be cleanedup", pv.Name) + logger.V(4).Info("Cluster Policy Violation does not belong to an active policy, will be cleanedup") if err := pc.pvControl.DeleteClusterPolicyViolation(pv.Name); err != nil { - glog.Errorf("Failed to deleted cluster policy violation %s: %v", pv.Name, err) + logger.Error(err, "failed to delete resource") return } - glog.V(4).Infof("Cluster Policy Violation %s deleted", pv.Name) + logger.V(4).Info("resource deleted") return } - glog.V(4).Infof("Cluster PolicyViolation %s updated", pv.Name) + logger.V(4).Info("resource updated") for _, p := range ps { pc.enqueuePolicy(p) } } func (pc *PolicyController) getPolicyForClusterPolicyViolation(pv *kyverno.ClusterPolicyViolation) []*kyverno.ClusterPolicy { + logger := pc.log.WithValues("kind", pv.Kind, "namespace", pv.Namespace, "name", pv.Name) policies, err := pc.pLister.GetPolicyForPolicyViolation(pv) if err != nil || len(policies) == 0 { return nil @@ -113,8 +115,7 @@ func (pc *PolicyController) getPolicyForClusterPolicyViolation(pv *kyverno.Clust if len(policies) > 1 { // ControllerRef will ensure we don't do anything crazy, but more than one // item in this list nevertheless constitutes user error. - glog.V(4).Infof("user error! more than one policy is selecting policy violation %s with labels: %#v, returning %s", - pv.Name, pv.Labels, policies[0].Name) + logger.V(4).Info("user error! more than one policy is selecting policy violation", "labels", pv.Labels, "policy", policies[0].Name) } return policies } diff --git a/pkg/policy/common.go b/pkg/policy/common.go index b4d6155abc..cb99b9dd2f 100644 --- a/pkg/policy/common.go +++ b/pkg/policy/common.go @@ -3,10 +3,10 @@ package policy import ( "fmt" - "github.com/golang/glog" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/log" ) func buildPolicyLabel(policyName string) (labels.Selector, error) { @@ -27,7 +27,7 @@ func buildPolicyLabel(policyName string) (labels.Selector, error) { func transformResource(resource unstructured.Unstructured) []byte { data, err := resource.MarshalJSON() if err != nil { - glog.Errorf("failed to marshall resource %v: %v", resource, err) + log.Log.Error(err, "failed to marshal resource") return nil } return data diff --git a/pkg/policy/common/common.go b/pkg/policy/common/common.go new file mode 100644 index 0000000000..35bc132007 --- /dev/null +++ b/pkg/policy/common/common.go @@ -0,0 +1,85 @@ +package common + +import ( + "fmt" + "regexp" + "strconv" + + "github.com/nirmata/kyverno/pkg/engine/anchor" +) + +//ValidatePattern validates the pattern +func ValidatePattern(patternElement interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) { + switch typedPatternElement := patternElement.(type) { + case map[string]interface{}: + return validateMap(typedPatternElement, path, supportedAnchors) + case []interface{}: + return validateArray(typedPatternElement, path, supportedAnchors) + case string, float64, int, int64, bool, nil: + //TODO? check operator + return "", nil + default: + return path, fmt.Errorf("Validation rule failed at '%s', pattern contains unknown type", path) + } +} +func validateMap(patternMap map[string]interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) { + // check if anchors are defined + for key, value := range patternMap { + // if key is anchor + // check regex () -> this is anchor + // () + // single char () + re, err := regexp.Compile(`^.?\(.+\)$`) + if err != nil { + return path + "/" + key, fmt.Errorf("Unable to parse the field %s: %v", key, err) + } + + matched := re.MatchString(key) + // check the type of anchor + if matched { + // some type of anchor + // check if valid anchor + if !checkAnchors(key, supportedAnchors) { + return path + "/" + key, fmt.Errorf("Unsupported anchor %s", key) + } + + // addition check for existence anchor + // value must be of type list + if anchor.IsExistenceAnchor(key) { + typedValue, ok := value.([]interface{}) + if !ok { + return path + "/" + key, fmt.Errorf("Existence anchor should have value of type list") + } + // validate there is only one entry in the list + if len(typedValue) == 0 || len(typedValue) > 1 { + return path + "/" + key, fmt.Errorf("Existence anchor: single value expected, multiple specified") + } + } + } + // lets validate the values now :) + if errPath, err := ValidatePattern(value, path+"/"+key, supportedAnchors); err != nil { + return errPath, err + } + } + return "", nil +} + +func validateArray(patternArray []interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) { + for i, patternElement := range patternArray { + currentPath := path + strconv.Itoa(i) + "/" + // lets validate the values now :) + if errPath, err := ValidatePattern(patternElement, currentPath, supportedAnchors); err != nil { + return errPath, err + } + } + return "", nil +} + +func checkAnchors(key string, supportedAnchors []anchor.IsAnchor) bool { + for _, f := range supportedAnchors { + if f(key) { + return true + } + } + return false +} diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index d303bebf52..2a471a37fe 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -1,10 +1,9 @@ package policy 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" "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" @@ -72,6 +71,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 @@ -84,10 +84,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 @@ -104,6 +105,7 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, pMetaStore: pMetaStore, pvGenerator: pvGenerator, resourceWebhookWatcher: resourceWebhookWatcher, + log: log, } pc.pvControl = RealPVControl{Client: kyvernoClient, Recorder: pc.eventRecorder} @@ -145,6 +147,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" @@ -168,19 +171,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) @@ -203,28 +206,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. @@ -232,9 +236,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) @@ -242,15 +247,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 } @@ -285,31 +291,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 @@ -322,8 +330,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 } diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index 97c09affac..af8dfc09c6 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -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,27 @@ 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) + engineResponse := applyPolicy(policy, resource, logger) // get engine response for mutation & validation independently engineResponses = append(engineResponses, engineResponse...) // post-processing, register the resource as processed @@ -48,36 +48,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 +76,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 +93,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 +107,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 +142,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 +193,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 +230,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 +278,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 +311,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 } diff --git a/pkg/policy/generate/auth.go b/pkg/policy/generate/auth.go new file mode 100644 index 0000000000..3da64a4289 --- /dev/null +++ b/pkg/policy/generate/auth.go @@ -0,0 +1,74 @@ +package generate + +import ( + "github.com/go-logr/logr" + "github.com/nirmata/kyverno/pkg/auth" + dclient "github.com/nirmata/kyverno/pkg/dclient" +) + +//Operations provides methods to performing operations on resource +type Operations interface { + // CanICreate returns 'true' if self can 'create' resource + CanICreate(kind, namespace string) (bool, error) + // CanIUpdate returns 'true' if self can 'update' resource + CanIUpdate(kind, namespace string) (bool, error) + // CanIDelete returns 'true' if self can 'delete' resource + CanIDelete(kind, namespace string) (bool, error) + // CanIGet returns 'true' if self can 'get' resource + CanIGet(kind, namespace string) (bool, error) +} + +//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, 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", a.log) + ok, err := canI.RunAccessCheck() + if err != nil { + return false, err + } + return ok, nil +} + +// 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", a.log) + ok, err := canI.RunAccessCheck() + if err != nil { + return false, err + } + return ok, nil +} + +// 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", a.log) + ok, err := canI.RunAccessCheck() + if err != nil { + return false, err + } + return ok, nil +} + +// 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", a.log) + ok, err := canI.RunAccessCheck() + if err != nil { + return false, err + } + return ok, nil +} diff --git a/pkg/policy/generate/fake.go b/pkg/policy/generate/fake.go new file mode 100644 index 0000000000..0d561c7a94 --- /dev/null +++ b/pkg/policy/generate/fake.go @@ -0,0 +1,21 @@ +package generate + +import ( + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "github.com/nirmata/kyverno/pkg/policy/generate/fake" +) + +//FakeGenerate provides implementation for generate rule processing +// with mocks/fakes for cluster interactions +type FakeGenerate struct { + Generate +} + +//NewFakeGenerate returns a new instance of generatecheck that uses +// fake/mock implementation for operation access(always returns true) +func NewFakeGenerate(rule kyverno.Generation) *FakeGenerate { + g := FakeGenerate{} + g.rule = rule + g.authCheck = fake.NewFakeAuth() + return &g +} diff --git a/pkg/policy/generate/fake/auth.go b/pkg/policy/generate/fake/auth.go new file mode 100644 index 0000000000..3e7467bf06 --- /dev/null +++ b/pkg/policy/generate/fake/auth.go @@ -0,0 +1,31 @@ +package fake + +//FakeAuth providers implementation for testing, retuning true for all operations +type FakeAuth struct { +} + +//NewFakeAuth returns a new instance of Fake Auth that returns true for each operation +func NewFakeAuth() *FakeAuth { + a := FakeAuth{} + return &a +} + +// CanICreate returns 'true' +func (a *FakeAuth) CanICreate(kind, namespace string) (bool, error) { + return true, nil +} + +// CanIUpdate returns 'true' +func (a *FakeAuth) CanIUpdate(kind, namespace string) (bool, error) { + return true, nil +} + +// CanIDelete returns 'true' +func (a *FakeAuth) CanIDelete(kind, namespace string) (bool, error) { + return true, nil +} + +// CanIGet returns 'true' +func (a *FakeAuth) CanIGet(kind, namespace string) (bool, error) { + return true, nil +} diff --git a/pkg/policy/generate/validate.go b/pkg/policy/generate/validate.go new file mode 100644 index 0000000000..460842ea98 --- /dev/null +++ b/pkg/policy/generate/validate.go @@ -0,0 +1,151 @@ +package generate + +import ( + "fmt" + "reflect" + + "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" + "github.com/nirmata/kyverno/pkg/engine/variables" + "github.com/nirmata/kyverno/pkg/policy/common" +) + +// Generate provides implementation to validate 'generate' rule +type Generate struct { + // rule to hold 'generate' rule specifications + 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, log logr.Logger) *Generate { + g := Generate{ + rule: rule, + authCheck: NewAuth(client, log), + log: log, + } + + return &g +} + +//Validate validates the 'generate' rule +func (g *Generate) Validate() (string, error) { + rule := g.rule + if rule.Data == nil && rule.Clone == (kyverno.CloneFrom{}) { + return "", fmt.Errorf("clone or data are required") + } + if rule.Data != nil && rule.Clone != (kyverno.CloneFrom{}) { + return "", fmt.Errorf("only one operation allowed per generate rule(data or clone)") + } + kind, name, namespace := rule.Kind, rule.Name, rule.Namespace + + if name == "" { + return "name", fmt.Errorf("name cannot be empty") + } + if kind == "" { + return "kind", fmt.Errorf("kind cannot be empty") + } + // Can I generate resource + + if !reflect.DeepEqual(rule.Clone, kyverno.CloneFrom{}) { + if path, err := g.validateClone(rule.Clone, kind); err != nil { + return fmt.Sprintf("clone.%s", path), err + } + } + if rule.Data != nil { + //TODO: is this required ?? as anchors can only be on pattern and not resource + // we can add this check by not sure if its needed here + if path, err := common.ValidatePattern(rule.Data, "/", []anchor.IsAnchor{}); err != nil { + return fmt.Sprintf("data.%s", path), fmt.Errorf("anchors not supported on generate resources: %v", err) + } + } + + // Kyverno generate-controller create/update/deletes the resources specified in generate rule of policy + // kyverno uses SA 'kyverno-service-account' and has default ClusterRoles and ClusterRoleBindings + // instuctions to modify the RBAC for kyverno are mentioned at https://github.com/nirmata/kyverno/blob/master/documentation/installation.md + // - operations required: create/update/delete/get + // If kind and namespace contain variables, then we cannot resolve then so we skip the processing + if err := g.canIGenerate(kind, namespace); err != nil { + return "", err + } + return "", nil +} + +func (g *Generate) validateClone(c kyverno.CloneFrom, kind string) (string, error) { + if c.Name == "" { + return "name", fmt.Errorf("name cannot be empty") + } + if c.Namespace == "" { + return "namespace", fmt.Errorf("namespace cannot be empty") + } + namespace := c.Namespace + // Skip if there is variable defined + if !variables.IsVariable(kind) && !variables.IsVariable(namespace) { + // GET + ok, err := g.authCheck.CanIGet(kind, namespace) + if err != nil { + return "", err + } + if !ok { + return "", fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace) + } + } else { + g.log.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.") + } + return "", nil +} + +//canIGenerate returns a error if kyverno cannot perform oprations +func (g *Generate) canIGenerate(kind, namespace string) error { + // Skip if there is variable defined + authCheck := g.authCheck + if !variables.IsVariable(kind) && !variables.IsVariable(namespace) { + // CREATE + ok, err := authCheck.CanICreate(kind, namespace) + if err != nil { + // machinery error + return err + } + if !ok { + return fmt.Errorf("kyverno does not have permissions to 'create' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace) + } + // UPDATE + ok, err = authCheck.CanIUpdate(kind, namespace) + if err != nil { + // machinery error + return err + } + if !ok { + return fmt.Errorf("kyverno does not have permissions to 'update' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace) + } + // GET + ok, err = authCheck.CanIGet(kind, namespace) + if err != nil { + // machinery error + return err + } + if !ok { + return fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace) + } + + // DELETE + ok, err = authCheck.CanIDelete(kind, namespace) + if err != nil { + // machinery error + return err + } + if !ok { + return fmt.Errorf("kyverno does not have permissions to 'delete' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace) + } + + } else { + g.log.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.") + } + + return nil +} diff --git a/pkg/policy/generate/validate_test.go b/pkg/policy/generate/validate_test.go new file mode 100644 index 0000000000..631aac6ed3 --- /dev/null +++ b/pkg/policy/generate/validate_test.go @@ -0,0 +1,89 @@ +package generate + +import ( + "encoding/json" + "testing" + + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "gotest.tools/assert" +) + +func Test_Validate_Generate(t *testing.T) { + rawGenerate := []byte(` + { + "kind": "NetworkPolicy", + "name": "defaultnetworkpolicy", + "data": { + "spec": { + "podSelector": {}, + "policyTypes": [ + "Ingress", + "Egress" + ], + "ingress": [ + {} + ], + "egress": [ + {} + ] + } + } + }`) + + var genRule kyverno.Generation + err := json.Unmarshal(rawGenerate, &genRule) + assert.NilError(t, err) + checker := NewFakeGenerate(genRule) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } +} + +func Test_Validate_Generate_HasAnchors(t *testing.T) { + var err error + rawGenerate := []byte(` + { + "kind": "NetworkPolicy", + "name": "defaultnetworkpolicy", + "data": { + "spec": { + "(podSelector)": {}, + "policyTypes": [ + "Ingress", + "Egress" + ], + "ingress": [ + {} + ], + "egress": [ + {} + ] + } + } + }`) + + var genRule kyverno.Generation + err = json.Unmarshal(rawGenerate, &genRule) + assert.NilError(t, err) + checker := NewFakeGenerate(genRule) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } + + rawGenerate = []byte(` + { + "kind": "ConfigMap", + "name": "copied-cm", + "clone": { + "^(namespace)": "default", + "name": "game" + } + }`) + + err = json.Unmarshal(rawGenerate, &genRule) + assert.NilError(t, err) + checker = NewFakeGenerate(genRule) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } +} diff --git a/pkg/policy/mutate/validate.go b/pkg/policy/mutate/validate.go new file mode 100644 index 0000000000..6a24c3d388 --- /dev/null +++ b/pkg/policy/mutate/validate.go @@ -0,0 +1,63 @@ +package mutate + +import ( + "errors" + "fmt" + + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "github.com/nirmata/kyverno/pkg/engine/anchor" + "github.com/nirmata/kyverno/pkg/policy/common" +) + +// Mutate provides implementation to validate 'mutate' rule +type Mutate struct { + // rule to hold 'mutate' rule specifications + rule kyverno.Mutation +} + +//NewMutateFactory returns a new instance of Mutate validation checker +func NewMutateFactory(rule kyverno.Mutation) *Mutate { + m := Mutate{ + rule: rule, + } + return &m +} + +//Validate validates the 'mutate' rule +func (m *Mutate) Validate() (string, error) { + rule := m.rule + // JSON Patches + if len(rule.Patches) != 0 { + for i, patch := range rule.Patches { + if err := validatePatch(patch); err != nil { + return fmt.Sprintf("patch[%d]", i), err + } + } + } + // Overlay + if rule.Overlay != nil { + path, err := common.ValidatePattern(rule.Overlay, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsAddingAnchor}) + if err != nil { + return path, err + } + } + return "", nil +} + +// Validate if all mandatory PolicyPatch fields are set +func validatePatch(pp kyverno.Patch) error { + if pp.Path == "" { + return errors.New("JSONPatch field 'path' is mandatory") + } + if pp.Operation == "add" || pp.Operation == "replace" { + if pp.Value == nil { + return fmt.Errorf("JSONPatch field 'value' is mandatory for operation '%s'", pp.Operation) + } + + return nil + } else if pp.Operation == "remove" { + return nil + } + + return fmt.Errorf("Unsupported JSONPatch operation '%s'", pp.Operation) +} diff --git a/pkg/policy/mutate/validate_test.go b/pkg/policy/mutate/validate_test.go new file mode 100644 index 0000000000..84ff0e59b7 --- /dev/null +++ b/pkg/policy/mutate/validate_test.go @@ -0,0 +1,151 @@ +package mutate + +import ( + "encoding/json" + "testing" + + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "gotest.tools/assert" +) + +func Test_Validate_Mutate_ConditionAnchor(t *testing.T) { + rawMutate := []byte(` + { + "overlay": { + "spec": { + "(serviceAccountName)": "*", + "automountServiceAccountToken": false + } + } + }`) + + var mutate kyverno.Mutation + err := json.Unmarshal(rawMutate, &mutate) + assert.NilError(t, err) + checker := NewMutateFactory(mutate) + if _, err := checker.Validate(); err != nil { + assert.NilError(t, err) + } +} + +func Test_Validate_Mutate_PlusAnchor(t *testing.T) { + rawMutate := []byte(` + { + "overlay": { + "spec": { + "+(serviceAccountName)": "*", + "automountServiceAccountToken": false + } + } + }`) + + var mutate kyverno.Mutation + err := json.Unmarshal(rawMutate, &mutate) + assert.NilError(t, err) + + checker := NewMutateFactory(mutate) + if _, err := checker.Validate(); err != nil { + assert.NilError(t, err) + } +} + +func Test_Validate_Mutate_Mismatched(t *testing.T) { + rawMutate := []byte(` + { + "overlay": { + "spec": { + "^(serviceAccountName)": "*", + "automountServiceAccountToken": false + } + } + }`) + + var mutateExistence kyverno.Mutation + err := json.Unmarshal(rawMutate, &mutateExistence) + assert.NilError(t, err) + + checker := NewMutateFactory(mutateExistence) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } + + var mutateEqual kyverno.Mutation + rawMutate = []byte(` + { + "overlay": { + "spec": { + "=(serviceAccountName)": "*", + "automountServiceAccountToken": false + } + } + }`) + + err = json.Unmarshal(rawMutate, &mutateEqual) + assert.NilError(t, err) + + checker = NewMutateFactory(mutateEqual) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } + + var mutateNegation kyverno.Mutation + rawMutate = []byte(` + { + "overlay": { + "spec": { + "X(serviceAccountName)": "*", + "automountServiceAccountToken": false + } + } + }`) + + err = json.Unmarshal(rawMutate, &mutateNegation) + assert.NilError(t, err) + + checker = NewMutateFactory(mutateEqual) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } +} + +func Test_Validate_Mutate_Unsupported(t *testing.T) { + var err error + var mutate kyverno.Mutation + // case 1 + rawMutate := []byte(` + { + "overlay": { + "spec": { + "!(serviceAccountName)": "*", + "automountServiceAccountToken": false + } + } + }`) + + err = json.Unmarshal(rawMutate, &mutate) + assert.NilError(t, err) + + checker := NewMutateFactory(mutate) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } + + // case 2 + rawMutate = []byte(` + { + "overlay": { + "spec": { + "~(serviceAccountName)": "*", + "automountServiceAccountToken": false + } + } + }`) + + err = json.Unmarshal(rawMutate, &mutate) + assert.NilError(t, err) + + checker = NewMutateFactory(mutate) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } +} diff --git a/pkg/policy/namespacedpv.go b/pkg/policy/namespacedpv.go index 654343d7df..8dd83d1f12 100644 --- a/pkg/policy/namespacedpv.go +++ b/pkg/policy/namespacedpv.go @@ -1,13 +1,13 @@ package policy import ( - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" cache "k8s.io/client-go/tools/cache" ) func (pc *PolicyController) addNamespacedPolicyViolation(obj interface{}) { pv := obj.(*kyverno.PolicyViolation) + logger := pc.log.WithValues("kind", pv.Kind, "namespace", pv.Namespace, "name", pv.Name) if pv.DeletionTimestamp != nil { // On a restart of the controller manager, it's possible for an object to @@ -20,15 +20,16 @@ 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 an active policy, will be cleanedup") 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 resource") return } - glog.V(4).Infof("PolicyViolation %s deleted", pv.Name) + logger.V(4).Info("resource deleted") return } - glog.V(4).Infof("Orphan Policy Violation %s added.", pv.Name) + + logger.V(4).Info("resource added") for _, p := range ps { pc.enqueuePolicy(p) } @@ -42,19 +43,20 @@ func (pc *PolicyController) updateNamespacedPolicyViolation(old, cur interface{} // Two different versions of the same replica set will always have different RVs. return } + logger := pc.log.WithValues("kind", curPV.Kind, "namespace", curPV.Namespace, "name", curPV.Name) ps := pc.getPolicyForNamespacedPolicyViolation(curPV) if len(ps) == 0 { // there is no namespaced policy for this violation, so we can delete this cluster policy violation - glog.V(4).Infof("Namespaced Policy Violation %s does not belong to an active policy, will be cleanedup", curPV.Name) + logger.V(4).Info("nameapced policy violation does not belong to an active policy, will be cleanedup") if err := pc.pvControl.DeleteNamespacedPolicyViolation(curPV.Namespace, curPV.Name); err != nil { - glog.Errorf("Failed to deleted namespaced policy violation %s: %v", curPV.Name, err) + logger.Error(err, "failed to delete resource") return } - glog.V(4).Infof("Namespaced Policy Violation %s deleted", curPV.Name) + logger.V(4).Info("resource deleted") return } - glog.V(4).Infof("Namespaced Policy sViolation %s updated", curPV.Name) + logger.V(4).Info("resource updated") for _, p := range ps { pc.enqueuePolicy(p) } @@ -62,6 +64,7 @@ func (pc *PolicyController) updateNamespacedPolicyViolation(old, cur interface{} } func (pc *PolicyController) deleteNamespacedPolicyViolation(obj interface{}) { + logger := pc.log pv, ok := obj.(*kyverno.PolicyViolation) // When a delete is dropped, the relist will notice a PolicyViolation in the store not // in the list, leading to the insertion of a tombstone object which contains @@ -70,34 +73,36 @@ func (pc *PolicyController) deleteNamespacedPolicyViolation(obj interface{}) { if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { - glog.Infof("Couldn't get object from tombstone %#v", obj) + logger.Info("Couldn't get object from tombstone", "obj", obj) return } pv, ok = tombstone.Obj.(*kyverno.PolicyViolation) if !ok { - glog.Infof("Couldn't get object from tombstone %#v", obj) + logger.Info("Couldn't get object from tombstone", "obj", obj) return } } + logger = logger.WithValues("kind", pv.Kind, "namespace", pv.Namespace, "name", pv.Name) 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("Namespaced Policy Violation %s does not belong to an active policy, will be cleanedup", pv.Name) + logger.V(4).Info("nameapced policy violation does not belong to an active policy, will be cleanedup") if err := pc.pvControl.DeleteNamespacedPolicyViolation(pv.Namespace, pv.Name); err != nil { - glog.Errorf("Failed to deleted namespaced policy violation %s: %v", pv.Name, err) + logger.Error(err, "failed to delete resource") return } - glog.V(4).Infof("Namespaced Policy Violation %s deleted", pv.Name) + logger.V(4).Info("resource deleted") return } - glog.V(4).Infof("Namespaced PolicyViolation %s updated", pv.Name) + logger.V(4).Info("resource updated") for _, p := range ps { pc.enqueuePolicy(p) } } func (pc *PolicyController) getPolicyForNamespacedPolicyViolation(pv *kyverno.PolicyViolation) []*kyverno.ClusterPolicy { + logger := pc.log.WithValues("kind", pv.Kind, "namespace", pv.Namespace, "name", pv.Name) policies, err := pc.pLister.GetPolicyForNamespacedPolicyViolation(pv) if err != nil || len(policies) == 0 { return nil @@ -109,8 +114,7 @@ func (pc *PolicyController) getPolicyForNamespacedPolicyViolation(pv *kyverno.Po if len(policies) > 1 { // ControllerRef will ensure we don't do anything crazy, but more than one // item in this list nevertheless constitutes user error. - glog.V(4).Infof("user error! more than one policy is selecting policy violation %s with labels: %#v, returning %s", - pv.Name, pv.Labels, policies[0].Name) + logger.V(4).Info("user error! more than one policy is selecting policy violation", "labels", pv.Labels, "policy", policies[0].Name) } return policies } diff --git a/pkg/policy/report.go b/pkg/policy/report.go index 3614da9b32..476e07abb9 100644 --- a/pkg/policy/report.go +++ b/pkg/policy/report.go @@ -3,7 +3,7 @@ package policy import ( "fmt" - "github.com/golang/glog" + "github.com/go-logr/logr" "github.com/nirmata/kyverno/pkg/engine/response" "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/policyviolation" @@ -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) + eventInfos := generateEvents(pc.log, engineResponses) pc.eventGen.Add(eventInfos...) // create policy violation - pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses) + pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses, logger) for i := range pvInfos { pvInfos[i].FromSync = true } @@ -29,39 +30,27 @@ 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 { +func generateEvents(log logr.Logger, ers []response.EngineResponse) []event.Info { var eventInfos []event.Info for _, er := range ers { if er.IsSuccesful() { continue } - eventInfos = append(eventInfos, generateEventsPerEr(er)...) + eventInfos = append(eventInfos, generateEventsPerEr(log, er)...) } return eventInfos } -func generateEventsPerEr(er response.EngineResponse) []event.Info { +func generateEventsPerEr(log logr.Logger, er response.EngineResponse) []event.Info { + logger := log.WithValues("policy", er.PolicyResponse.Policy, "kind", er.PolicyResponse.Resource.Kind, "namespace", er.PolicyResponse.Resource.Namespace, "name", er.PolicyResponse.Resource.Name) var eventInfos []event.Info - glog.V(4).Infof("reporting results for policy '%s' application on resource '%s/%s/%s'", er.PolicyResponse.Policy, er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name) + logger.V(4).Info("reporting results for policy") for _, rule := range er.PolicyResponse.Rules { if rule.Success { continue } // generate event on resource for each failed rule - glog.V(4).Infof("generation event on resource '%s/%s/%s' for policy '%s'", er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name, er.PolicyResponse.Policy) + logger.V(4).Info("generating event on resource") e := event.Info{} e.Kind = er.PolicyResponse.Resource.Kind e.Namespace = er.PolicyResponse.Resource.Namespace @@ -76,7 +65,7 @@ func generateEventsPerEr(er response.EngineResponse) []event.Info { } // generate a event on policy for all failed rules - glog.V(4).Infof("generation event on policy '%s'", er.PolicyResponse.Policy) + logger.V(4).Info("generating event on policy") e := event.Info{} e.Kind = "ClusterPolicy" e.Namespace = "" diff --git a/pkg/policy/validate.go b/pkg/policy/validate.go index 9dec98a2c9..cffa69fe68 100644 --- a/pkg/policy/validate.go +++ b/pkg/policy/validate.go @@ -5,16 +5,12 @@ import ( "errors" "fmt" "reflect" - "regexp" - "strconv" "strings" - "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/openapi" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" - "github.com/nirmata/kyverno/pkg/engine/anchor" + dclient "github.com/nirmata/kyverno/pkg/dclient" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -22,11 +18,10 @@ import ( // Validate does some initial check to verify some conditions // - One operation per rule // - ResourceDescription mandatory checks -func Validate(policyRaw []byte) error { +func Validate(policyRaw []byte, client *dclient.Client, mock bool) error { var p kyverno.ClusterPolicy err := json.Unmarshal(policyRaw, &p) if err != nil { - glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err) return fmt.Errorf("failed to unmarshal policy admission request err %v", err) } @@ -62,24 +57,12 @@ func Validate(policyRaw []byte) error { // as there are more than 1 operation in rule, not need to evaluate it further return fmt.Errorf("path: spec.rules[%d]: %v", i, err) } - // Operation Validation - // Mutation - if rule.HasMutate() { - if path, err := validateMutation(rule.Mutation); err != nil { - return fmt.Errorf("path: spec.rules[%d].mutate.%s.: %v", i, path, err) - } - } - // Validation - if rule.HasValidate() { - if path, err := validateValidation(rule.Validation); err != nil { - return fmt.Errorf("path: spec.rules[%d].validate.%s.: %v", i, path, err) - } - } - // Generation - if rule.HasGenerate() { - if path, err := validateGeneration(rule.Generation); err != nil { - return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", i, path, err) - } + // validate rule actions + // - Mutate + // - Validate + // - Generate + if err := validateActions(i, rule, client, mock); err != nil { + return err } // If a rules match block does not match any kind, @@ -278,193 +261,3 @@ func validateResourceDescription(rd kyverno.ResourceDescription) error { } return nil } - -func validateMutation(m kyverno.Mutation) (string, error) { - // JSON Patches - if len(m.Patches) != 0 { - for i, patch := range m.Patches { - if err := validatePatch(patch); err != nil { - return fmt.Sprintf("patch[%d]", i), err - } - } - } - // Overlay - if m.Overlay != nil { - path, err := validatePattern(m.Overlay, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsAddingAnchor}) - if err != nil { - return path, err - } - } - return "", nil -} - -// Validate if all mandatory PolicyPatch fields are set -func validatePatch(pp kyverno.Patch) error { - if pp.Path == "" { - return errors.New("JSONPatch field 'path' is mandatory") - } - if pp.Operation == "add" || pp.Operation == "replace" { - if pp.Value == nil { - return fmt.Errorf("JSONPatch field 'value' is mandatory for operation '%s'", pp.Operation) - } - - return nil - } else if pp.Operation == "remove" { - return nil - } - - return fmt.Errorf("Unsupported JSONPatch operation '%s'", pp.Operation) -} - -func validateValidation(v kyverno.Validation) (string, error) { - if err := validateOverlayPattern(v); err != nil { - // no need to proceed ahead - return "", err - } - - if v.Pattern != nil { - if path, err := validatePattern(v.Pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistenceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil { - return fmt.Sprintf("pattern.%s", path), err - } - } - - if len(v.AnyPattern) != 0 { - for i, pattern := range v.AnyPattern { - if path, err := validatePattern(pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistenceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil { - return fmt.Sprintf("anyPattern[%d].%s", i, path), err - } - } - } - return "", nil -} - -// validateOverlayPattern checks one of pattern/anyPattern must exist -func validateOverlayPattern(v kyverno.Validation) error { - if v.Pattern == nil && len(v.AnyPattern) == 0 { - return fmt.Errorf("a pattern or anyPattern must be specified") - } - - if v.Pattern != nil && len(v.AnyPattern) != 0 { - return fmt.Errorf("only one operation allowed per validation rule(pattern or anyPattern)") - } - - return nil -} - -// Validate returns error if generator is configured incompletely -func validateGeneration(gen kyverno.Generation) (string, error) { - - if gen.Data == nil && gen.Clone == (kyverno.CloneFrom{}) { - return "", fmt.Errorf("clone or data are required") - } - if gen.Data != nil && gen.Clone != (kyverno.CloneFrom{}) { - return "", fmt.Errorf("only one operation allowed per generate rule(data or clone)") - } - // check kind is non empty - // check name is non empty - if gen.Name == "" { - return "name", fmt.Errorf("name cannot be empty") - } - if gen.Kind == "" { - return "kind", fmt.Errorf("kind cannot be empty") - } - if !reflect.DeepEqual(gen.Clone, kyverno.CloneFrom{}) { - if path, err := validateClone(gen.Clone); err != nil { - return fmt.Sprintf("clone.%s", path), err - } - } - if gen.Data != nil { - //TODO: is this required ?? as anchors can only be on pattern and not resource - // we can add this check by not sure if its needed here - if path, err := validatePattern(gen.Data, "/", []anchor.IsAnchor{}); err != nil { - return fmt.Sprintf("data.%s", path), fmt.Errorf("anchors not supported on generate resources: %v", err) - } - } - return "", nil -} - -func validateClone(c kyverno.CloneFrom) (string, error) { - if c.Name == "" { - return "name", fmt.Errorf("name cannot be empty") - } - if c.Namespace == "" { - return "namespace", fmt.Errorf("namespace cannot be empty") - } - return "", nil -} - -func validatePattern(patternElement interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) { - switch typedPatternElement := patternElement.(type) { - case map[string]interface{}: - return validateMap(typedPatternElement, path, supportedAnchors) - case []interface{}: - return validateArray(typedPatternElement, path, supportedAnchors) - case string, float64, int, int64, bool, nil: - //TODO? check operator - return "", nil - default: - return path, fmt.Errorf("Validation rule failed at '%s', pattern contains unknown type", path) - } -} - -func validateMap(patternMap map[string]interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) { - // check if anchors are defined - for key, value := range patternMap { - // if key is anchor - // check regex () -> this is anchor - // () - // single char () - re, err := regexp.Compile(`^.?\(.+\)$`) - if err != nil { - return path + "/" + key, fmt.Errorf("Unable to parse the field %s: %v", key, err) - } - - matched := re.MatchString(key) - // check the type of anchor - if matched { - // some type of anchor - // check if valid anchor - if !checkAnchors(key, supportedAnchors) { - return path + "/" + key, fmt.Errorf("Unsupported anchor %s", key) - } - - // addition check for existence anchor - // value must be of type list - if anchor.IsExistenceAnchor(key) { - typedValue, ok := value.([]interface{}) - if !ok { - return path + "/" + key, fmt.Errorf("Existence anchor should have value of type list") - } - // validate there is only one entry in the list - if len(typedValue) == 0 || len(typedValue) > 1 { - return path + "/" + key, fmt.Errorf("Existence anchor: single value expected, multiple specified") - } - } - } - // lets validate the values now :) - if errPath, err := validatePattern(value, path+"/"+key, supportedAnchors); err != nil { - return errPath, err - } - } - return "", nil -} - -func validateArray(patternArray []interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) { - for i, patternElement := range patternArray { - currentPath := path + strconv.Itoa(i) + "/" - // lets validate the values now :) - if errPath, err := validatePattern(patternElement, currentPath, supportedAnchors); err != nil { - return errPath, err - } - } - return "", nil -} - -func checkAnchors(key string, supportedAnchors []anchor.IsAnchor) bool { - for _, f := range supportedAnchors { - if f(key) { - return true - } - } - return false -} diff --git a/pkg/policy/validate/validate.go b/pkg/policy/validate/validate.go new file mode 100644 index 0000000000..3889f9f163 --- /dev/null +++ b/pkg/policy/validate/validate.go @@ -0,0 +1,61 @@ +package validate + +import ( + "fmt" + + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "github.com/nirmata/kyverno/pkg/engine/anchor" + "github.com/nirmata/kyverno/pkg/policy/common" +) + +// Validate provides implementation to validate 'validate' rule +type Validate struct { + // rule to hold 'validate' rule specifications + rule kyverno.Validation +} + +//NewValidateFactory returns a new instance of Mutate validation checker +func NewValidateFactory(rule kyverno.Validation) *Validate { + m := Validate{ + rule: rule, + } + return &m +} + +//Validate validates the 'validate' rule +func (v *Validate) Validate() (string, error) { + rule := v.rule + if err := v.validateOverlayPattern(); err != nil { + // no need to proceed ahead + return "", err + } + + if rule.Pattern != nil { + if path, err := common.ValidatePattern(rule.Pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistenceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil { + return fmt.Sprintf("pattern.%s", path), err + } + } + + if len(rule.AnyPattern) != 0 { + for i, pattern := range rule.AnyPattern { + if path, err := common.ValidatePattern(pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistenceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil { + return fmt.Sprintf("anyPattern[%d].%s", i, path), err + } + } + } + return "", nil +} + +// validateOverlayPattern checks one of pattern/anyPattern must exist +func (v *Validate) validateOverlayPattern() error { + rule := v.rule + if rule.Pattern == nil && len(rule.AnyPattern) == 0 { + return fmt.Errorf("a pattern or anyPattern must be specified") + } + + if rule.Pattern != nil && len(rule.AnyPattern) != 0 { + return fmt.Errorf("only one operation allowed per validation rule(pattern or anyPattern)") + } + + return nil +} diff --git a/pkg/policy/validate/validate_test.go b/pkg/policy/validate/validate_test.go new file mode 100644 index 0000000000..ee83204707 --- /dev/null +++ b/pkg/policy/validate/validate_test.go @@ -0,0 +1,381 @@ +package validate + +import ( + "encoding/json" + "testing" + + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + "gotest.tools/assert" +) + +func Test_Validate_OverlayPattern_Empty(t *testing.T) { + rawValidation := []byte(` + {}`) + + var validation kyverno.Validation + err := json.Unmarshal(rawValidation, &validation) + assert.NilError(t, err) + + checker := NewValidateFactory(validation) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } +} +func Test_Validate_OverlayPattern_Nil_PatternAnypattern(t *testing.T) { + rawValidation := []byte(` + { "message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false" + } + `) + + var validation kyverno.Validation + err := json.Unmarshal(rawValidation, &validation) + assert.NilError(t, err) + checker := NewValidateFactory(validation) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } +} + +func Test_Validate_OverlayPattern_Exist_PatternAnypattern(t *testing.T) { + rawValidation := []byte(` + { + "message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false", + "anyPattern": [ + { + "spec": { + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false + } + } + } + ], + "pattern": { + "spec": { + "containers": [ + { + "name": "*", + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false + } + } + ] + } + } + }`) + + var validation kyverno.Validation + err := json.Unmarshal(rawValidation, &validation) + assert.NilError(t, err) + checker := NewValidateFactory(validation) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } +} +func Test_Validate_OverlayPattern_Valid(t *testing.T) { + rawValidation := []byte(` + { + "message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false", + "anyPattern": [ + { + "spec": { + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false + } + } + }, + { + "spec": { + "containers": [ + { + "name": "*", + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false + } + } + ] + } + } + ] + } +`) + + var validation kyverno.Validation + err := json.Unmarshal(rawValidation, &validation) + assert.NilError(t, err) + checker := NewValidateFactory(validation) + if _, err := checker.Validate(); err != nil { + assert.NilError(t, err) + } +} + +func Test_Validate_ExistingAnchor_AnchorOnMap(t *testing.T) { + rawValidation := []byte(` + { + "message": "validate container security contexts", + "anyPattern": [ + { + "spec": { + "template": { + "spec": { + "containers": [ + { + "^(securityContext)": { + "runAsNonRoot": true + } + } + ] + } + } + } + } + ] + } +`) + + var validation kyverno.Validation + err := json.Unmarshal(rawValidation, &validation) + assert.NilError(t, err) + checker := NewValidateFactory(validation) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } +} + +func Test_Validate_ExistingAnchor_AnchorOnString(t *testing.T) { + rawValidation := []byte(`{ + "message": "validate container security contexts", + "pattern": { + "spec": { + "template": { + "spec": { + "containers": [ + { + "securityContext": { + "allowPrivilegeEscalation": "^(false)" + } + } + ] + } + } + } + } + } + `) + + var validation kyverno.Validation + err := json.Unmarshal(rawValidation, &validation) + assert.NilError(t, err) + checker := NewValidateFactory(validation) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } +} + +func Test_Validate_ExistingAnchor_Valid(t *testing.T) { + var err error + var validation kyverno.Validation + rawValidation := []byte(` + { + "message": "validate container security contexts", + "anyPattern": [ + { + "spec": { + "template": { + "spec": { + "^(containers)": [ + { + "securityContext": { + "runAsNonRoot": "true" + } + } + ] + } + } + } + } + ] + }`) + + err = json.Unmarshal(rawValidation, &validation) + assert.NilError(t, err) + checker := NewValidateFactory(validation) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } + rawValidation = []byte(` + { + "message": "validate container security contexts", + "pattern": { + "spec": { + "template": { + "spec": { + "^(containers)": [ + { + "securityContext": { + "allowPrivilegeEscalation": "false" + } + } + ] + } + } + } + } + } `) + err = json.Unmarshal(rawValidation, &validation) + assert.NilError(t, err) + checker = NewValidateFactory(validation) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } + +} + +func Test_Validate_Validate_ValidAnchor(t *testing.T) { + var err error + var validate kyverno.Validation + var rawValidate []byte + // case 1 + rawValidate = []byte(` + { + "message": "Root user is not allowed. Set runAsNonRoot to true.", + "anyPattern": [ + { + "spec": { + "securityContext": { + "(runAsNonRoot)": true + } + } + }, + { + "spec": { + "^(containers)": [ + { + "name": "*", + "securityContext": { + "runAsNonRoot": true + } + } + ] + } + } + ] + }`) + + err = json.Unmarshal(rawValidate, &validate) + assert.NilError(t, err) + + checker := NewValidateFactory(validate) + if _, err := checker.Validate(); err != nil { + assert.NilError(t, err) + } + + // case 2 + validate = kyverno.Validation{} + rawValidate = []byte(` + { + "message": "Root user is not allowed. Set runAsNonRoot to true.", + "pattern": { + "spec": { + "=(securityContext)": { + "runAsNonRoot": "true" + } + } + } + }`) + + err = json.Unmarshal(rawValidate, &validate) + assert.NilError(t, err) + + checker = NewValidateFactory(validate) + if _, err := checker.Validate(); err != nil { + assert.NilError(t, err) + } +} + +func Test_Validate_Validate_Mismatched(t *testing.T) { + rawValidate := []byte(` + { + "message": "Root user is not allowed. Set runAsNonRoot to true.", + "pattern": { + "spec": { + "containers": [ + { + "name": "*", + "securityContext": { + "+(runAsNonRoot)": true + } + } + ] + } + } + }`) + + var validate kyverno.Validation + err := json.Unmarshal(rawValidate, &validate) + assert.NilError(t, err) + checker := NewValidateFactory(validate) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } +} + +func Test_Validate_Validate_Unsupported(t *testing.T) { + var err error + var validate kyverno.Validation + + // case 1 + rawValidate := []byte(` + { + "message": "Root user is not allowed. Set runAsNonRoot to true.", + "pattern": { + "spec": { + "containers": [ + { + "name": "*", + "securityContext": { + "!(runAsNonRoot)": true + } + } + ] + } + } + }`) + + err = json.Unmarshal(rawValidate, &validate) + assert.NilError(t, err) + checker := NewValidateFactory(validate) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } + + // case 2 + rawValidate = []byte(` + { + "message": "Root user is not allowed. Set runAsNonRoot to true.", + "pattern": { + "spec": { + "containers": [ + { + "name": "*", + "securityContext": { + "~(runAsNonRoot)": true + } + } + ] + } + } + }`) + + err = json.Unmarshal(rawValidate, &validate) + assert.NilError(t, err) + + checker = NewValidateFactory(validate) + if _, err := checker.Validate(); err != nil { + assert.Assert(t, err != nil) + } + +} diff --git a/pkg/policy/validate_test.go b/pkg/policy/validate_test.go index 9082e9322a..bd239995cc 100644 --- a/pkg/policy/validate_test.go +++ b/pkg/policy/validate_test.go @@ -287,369 +287,6 @@ func Test_Validate_ResourceDescription_InvalidSelector(t *testing.T) { assert.Assert(t, err != nil) } -func Test_Validate_OverlayPattern_Empty(t *testing.T) { - rawValidation := []byte(` - {}`) - - var validation kyverno.Validation - err := json.Unmarshal(rawValidation, &validation) - assert.NilError(t, err) - - if _, err := validateValidation(validation); err != nil { - assert.Assert(t, err != nil) - } -} - -func Test_Validate_OverlayPattern_Nil_PatternAnypattern(t *testing.T) { - rawValidation := []byte(` - { "message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false" - } - `) - - var validation kyverno.Validation - err := json.Unmarshal(rawValidation, &validation) - assert.NilError(t, err) - if _, err := validateValidation(validation); err != nil { - assert.Assert(t, err != nil) - } -} - -func Test_Validate_OverlayPattern_Exist_PatternAnypattern(t *testing.T) { - rawValidation := []byte(` - { - "message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false", - "anyPattern": [ - { - "spec": { - "securityContext": { - "allowPrivilegeEscalation": false, - "privileged": false - } - } - } - ], - "pattern": { - "spec": { - "containers": [ - { - "name": "*", - "securityContext": { - "allowPrivilegeEscalation": false, - "privileged": false - } - } - ] - } - } - }`) - - var validation kyverno.Validation - err := json.Unmarshal(rawValidation, &validation) - assert.NilError(t, err) - if _, err := validateValidation(validation); err != nil { - assert.Assert(t, err != nil) - } -} - -func Test_Validate_OverlayPattern_Valid(t *testing.T) { - rawValidation := []byte(` - { - "message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false", - "anyPattern": [ - { - "spec": { - "securityContext": { - "allowPrivilegeEscalation": false, - "privileged": false - } - } - }, - { - "spec": { - "containers": [ - { - "name": "*", - "securityContext": { - "allowPrivilegeEscalation": false, - "privileged": false - } - } - ] - } - } - ] - } -`) - - var validation kyverno.Validation - err := json.Unmarshal(rawValidation, &validation) - assert.NilError(t, err) - if _, err := validateValidation(validation); err != nil { - assert.NilError(t, err) - } - -} - -func Test_Validate_ExistingAnchor_AnchorOnMap(t *testing.T) { - rawValidation := []byte(` - { - "message": "validate container security contexts", - "anyPattern": [ - { - "spec": { - "template": { - "spec": { - "containers": [ - { - "^(securityContext)": { - "runAsNonRoot": true - } - } - ] - } - } - } - } - ] - } -`) - - var validation kyverno.Validation - err := json.Unmarshal(rawValidation, &validation) - assert.NilError(t, err) - if _, err := validateValidation(validation); err != nil { - assert.Assert(t, err != nil) - } - -} - -func Test_Validate_ExistingAnchor_AnchorOnString(t *testing.T) { - rawValidation := []byte(`{ - "message": "validate container security contexts", - "pattern": { - "spec": { - "template": { - "spec": { - "containers": [ - { - "securityContext": { - "allowPrivilegeEscalation": "^(false)" - } - } - ] - } - } - } - } - } - `) - - var validation kyverno.Validation - err := json.Unmarshal(rawValidation, &validation) - assert.NilError(t, err) - if _, err := validateValidation(validation); err != nil { - assert.Assert(t, err != nil) - } -} - -func Test_Validate_ExistingAnchor_Valid(t *testing.T) { - var err error - var validation kyverno.Validation - rawValidation := []byte(` - { - "message": "validate container security contexts", - "anyPattern": [ - { - "spec": { - "template": { - "spec": { - "^(containers)": [ - { - "securityContext": { - "runAsNonRoot": "true" - } - } - ] - } - } - } - } - ] - }`) - - err = json.Unmarshal(rawValidation, &validation) - assert.NilError(t, err) - if _, err := validateValidation(validation); err != nil { - assert.Assert(t, err != nil) - } - rawValidation = []byte(` - { - "message": "validate container security contexts", - "pattern": { - "spec": { - "template": { - "spec": { - "^(containers)": [ - { - "securityContext": { - "allowPrivilegeEscalation": "false" - } - } - ] - } - } - } - } - } `) - err = json.Unmarshal(rawValidation, &validation) - assert.NilError(t, err) - if _, err := validateValidation(validation); err != nil { - assert.Assert(t, err != nil) - } - -} - -func Test_Validate_Validate_ValidAnchor(t *testing.T) { - var err error - var validate kyverno.Validation - var rawValidate []byte - // case 1 - rawValidate = []byte(` - { - "message": "Root user is not allowed. Set runAsNonRoot to true.", - "anyPattern": [ - { - "spec": { - "securityContext": { - "(runAsNonRoot)": true - } - } - }, - { - "spec": { - "^(containers)": [ - { - "name": "*", - "securityContext": { - "runAsNonRoot": true - } - } - ] - } - } - ] - }`) - - err = json.Unmarshal(rawValidate, &validate) - assert.NilError(t, err) - - if _, err := validateValidation(validate); err != nil { - assert.NilError(t, err) - } - - // case 2 - validate = kyverno.Validation{} - rawValidate = []byte(` - { - "message": "Root user is not allowed. Set runAsNonRoot to true.", - "pattern": { - "spec": { - "=(securityContext)": { - "runAsNonRoot": "true" - } - } - } - }`) - - err = json.Unmarshal(rawValidate, &validate) - assert.NilError(t, err) - - if _, err := validateValidation(validate); err != nil { - assert.NilError(t, err) - } -} - -func Test_Validate_Validate_Mismatched(t *testing.T) { - rawValidate := []byte(` - { - "message": "Root user is not allowed. Set runAsNonRoot to true.", - "pattern": { - "spec": { - "containers": [ - { - "name": "*", - "securityContext": { - "+(runAsNonRoot)": true - } - } - ] - } - } - }`) - - var validate kyverno.Validation - err := json.Unmarshal(rawValidate, &validate) - assert.NilError(t, err) - if _, err := validateValidation(validate); err != nil { - assert.Assert(t, err != nil) - } -} - -func Test_Validate_Validate_Unsupported(t *testing.T) { - var err error - var validate kyverno.Validation - - // case 1 - rawValidate := []byte(` - { - "message": "Root user is not allowed. Set runAsNonRoot to true.", - "pattern": { - "spec": { - "containers": [ - { - "name": "*", - "securityContext": { - "!(runAsNonRoot)": true - } - } - ] - } - } - }`) - - err = json.Unmarshal(rawValidate, &validate) - assert.NilError(t, err) - if _, err := validateValidation(validate); err != nil { - assert.Assert(t, err != nil) - } - - // case 2 - rawValidate = []byte(` - { - "message": "Root user is not allowed. Set runAsNonRoot to true.", - "pattern": { - "spec": { - "containers": [ - { - "name": "*", - "securityContext": { - "~(runAsNonRoot)": true - } - } - ] - } - } - }`) - - err = json.Unmarshal(rawValidate, &validate) - assert.NilError(t, err) - - if _, err := validateValidation(validate); err != nil { - assert.Assert(t, err != nil) - } - -} - func Test_Validate_Policy(t *testing.T) { rawPolicy := []byte(` { @@ -732,225 +369,10 @@ func Test_Validate_Policy(t *testing.T) { } }`) - err := Validate(rawPolicy) + err := Validate(rawPolicy, nil, true) assert.NilError(t, err) } -func Test_Validate_Mutate_ConditionAnchor(t *testing.T) { - rawMutate := []byte(` - { - "overlay": { - "spec": { - "(serviceAccountName)": "*", - "automountServiceAccountToken": false - } - } - }`) - - var mutate kyverno.Mutation - err := json.Unmarshal(rawMutate, &mutate) - assert.NilError(t, err) - if _, err := validateMutation(mutate); err != nil { - assert.NilError(t, err) - } -} - -func Test_Validate_Mutate_PlusAnchor(t *testing.T) { - rawMutate := []byte(` - { - "overlay": { - "spec": { - "+(serviceAccountName)": "*", - "automountServiceAccountToken": false - } - } - }`) - - var mutate kyverno.Mutation - err := json.Unmarshal(rawMutate, &mutate) - assert.NilError(t, err) - - if _, err := validateMutation(mutate); err != nil { - assert.NilError(t, err) - } -} - -func Test_Validate_Mutate_Mismatched(t *testing.T) { - rawMutate := []byte(` - { - "overlay": { - "spec": { - "^(serviceAccountName)": "*", - "automountServiceAccountToken": false - } - } - }`) - - var mutateExistence kyverno.Mutation - err := json.Unmarshal(rawMutate, &mutateExistence) - assert.NilError(t, err) - - if _, err := validateMutation(mutateExistence); err != nil { - assert.Assert(t, err != nil) - } - - var mutateEqual kyverno.Mutation - rawMutate = []byte(` - { - "overlay": { - "spec": { - "=(serviceAccountName)": "*", - "automountServiceAccountToken": false - } - } - }`) - - err = json.Unmarshal(rawMutate, &mutateEqual) - assert.NilError(t, err) - - if _, err := validateMutation(mutateEqual); err != nil { - assert.Assert(t, err != nil) - } - - var mutateNegation kyverno.Mutation - rawMutate = []byte(` - { - "overlay": { - "spec": { - "X(serviceAccountName)": "*", - "automountServiceAccountToken": false - } - } - }`) - - err = json.Unmarshal(rawMutate, &mutateNegation) - assert.NilError(t, err) - - if _, err := validateMutation(mutateEqual); err != nil { - assert.Assert(t, err != nil) - } -} - -func Test_Validate_Mutate_Unsupported(t *testing.T) { - var err error - var mutate kyverno.Mutation - // case 1 - rawMutate := []byte(` - { - "overlay": { - "spec": { - "!(serviceAccountName)": "*", - "automountServiceAccountToken": false - } - } - }`) - - err = json.Unmarshal(rawMutate, &mutate) - assert.NilError(t, err) - - if _, err := validateMutation(mutate); err != nil { - assert.Assert(t, err != nil) - } - - // case 2 - rawMutate = []byte(` - { - "overlay": { - "spec": { - "~(serviceAccountName)": "*", - "automountServiceAccountToken": false - } - } - }`) - - err = json.Unmarshal(rawMutate, &mutate) - assert.NilError(t, err) - - if _, err := validateMutation(mutate); err != nil { - assert.Assert(t, err != nil) - } -} - -func Test_Validate_Generate(t *testing.T) { - rawGenerate := []byte(` - { - "kind": "NetworkPolicy", - "name": "defaultnetworkpolicy", - "data": { - "spec": { - "podSelector": {}, - "policyTypes": [ - "Ingress", - "Egress" - ], - "ingress": [ - {} - ], - "egress": [ - {} - ] - } - } - }`) - - var generate kyverno.Generation - err := json.Unmarshal(rawGenerate, &generate) - assert.NilError(t, err) - - if _, err := validateGeneration(generate); err != nil { - assert.Assert(t, err != nil) - } -} - -func Test_Validate_Generate_HasAnchors(t *testing.T) { - var err error - var generate kyverno.Generation - rawGenerate := []byte(` - { - "kind": "NetworkPolicy", - "name": "defaultnetworkpolicy", - "data": { - "spec": { - "(podSelector)": {}, - "policyTypes": [ - "Ingress", - "Egress" - ], - "ingress": [ - {} - ], - "egress": [ - {} - ] - } - } - }`) - - err = json.Unmarshal(rawGenerate, &generate) - assert.NilError(t, err) - if _, err := validateGeneration(generate); err != nil { - assert.Assert(t, err != nil) - } - - rawGenerate = []byte(` - { - "kind": "ConfigMap", - "name": "copied-cm", - "clone": { - "^(namespace)": "default", - "name": "game" - } - }`) - - errNew := json.Unmarshal(rawGenerate, &generate) - assert.NilError(t, errNew) - err = json.Unmarshal(rawGenerate, &generate) - assert.NilError(t, err) - if _, err := validateGeneration(generate); err != nil { - assert.Assert(t, err != nil) - } -} - func Test_Validate_ErrorFormat(t *testing.T) { rawPolicy := []byte(` { @@ -1089,7 +511,7 @@ func Test_Validate_ErrorFormat(t *testing.T) { } `) - err := Validate(rawPolicy) + err := Validate(rawPolicy, nil, true) assert.Assert(t, err != nil) } diff --git a/pkg/policy/webhookregistration.go b/pkg/policy/webhookregistration.go index f4b2188b2a..5c8fc0aa69 100644 --- a/pkg/policy/webhookregistration.go +++ b/pkg/policy/webhookregistration.go @@ -1,25 +1,25 @@ package policy import ( - "github.com/golang/glog" "k8s.io/apimachinery/pkg/labels" ) func (pc *PolicyController) removeResourceWebhookConfiguration() error { + logger := pc.log var err error // get all existing policies policies, err := pc.pLister.List(labels.NewSelector()) if err != nil { - glog.V(4).Infof("failed to list policies: %v", err) + logger.Error(err, "failed to list policies") return err } if len(policies) == 0 { - glog.V(4).Info("no policies loaded, removing resource webhook configuration if one exists") + logger.V(4).Info("no policies loaded, removing resource webhook configuration if one exists") return pc.resourceWebhookWatcher.RemoveResourceWebhookConfiguration() } - glog.V(4).Info("no policies with mutating or validating webhook configurations, remove resource webhook configuration if one exists") + logger.V(4).Info("no policies with mutating or validating webhook configurations, remove resource webhook configuration if one exists") return pc.resourceWebhookWatcher.RemoveResourceWebhookConfiguration() } diff --git a/pkg/policystatus/main.go b/pkg/policystatus/main.go index 3e633aaf82..b21ff2e505 100644 --- a/pkg/policystatus/main.go +++ b/pkg/policystatus/main.go @@ -2,16 +2,16 @@ package policystatus import ( "encoding/json" + "fmt" "sync" "time" - "github.com/golang/glog" - "k8s.io/apimachinery/pkg/util/wait" "github.com/nirmata/kyverno/pkg/client/clientset/versioned" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" + log "sigs.k8s.io/controller-runtime/pkg/log" ) // Policy status implementation works in the following way, @@ -111,8 +111,7 @@ func (s *Sync) updateStatusCache(stopCh <-chan struct{}) { s.cache.keyToMutex.Get(statusUpdater.PolicyName()).Unlock() oldStatus, _ := json.Marshal(status) newStatus, _ := json.Marshal(updatedStatus) - - glog.V(4).Infof("\nupdated status of policy - %v\noldStatus:\n%v\nnewStatus:\n%v\n", statusUpdater.PolicyName(), string(oldStatus), string(newStatus)) + log.Log.V(4).Info(fmt.Sprintf("\nupdated status of policy - %v\noldStatus:\n%v\nnewStatus:\n%v\n", statusUpdater.PolicyName(), string(oldStatus), string(newStatus))) case <-stopCh: return } @@ -140,7 +139,7 @@ func (s *Sync) updatePolicyStatus() { s.cache.dataMu.Lock() delete(s.cache.data, policyName) s.cache.dataMu.Unlock() - glog.V(4).Info(err) + log.Log.Error(err, "failed to update policy status") } } } diff --git a/pkg/policystore/policystore.go b/pkg/policystore/policystore.go index f8a8e30874..0ecfd5ea32 100644 --- a/pkg/policystore/policystore.go +++ b/pkg/policystore/policystore.go @@ -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 diff --git a/pkg/policyviolation/builder.go b/pkg/policyviolation/builder.go index 1de366eb66..da556697be 100644 --- a/pkg/policyviolation/builder.go +++ b/pkg/policyviolation/builder.go @@ -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)) } diff --git a/pkg/policyviolation/builder_test.go b/pkg/policyviolation/builder_test.go index bd3f8c97f0..a44314f9f2 100644 --- a/pkg/policyviolation/builder_test.go +++ b/pkg/policyviolation/builder_test.go @@ -5,6 +5,7 @@ import ( "github.com/nirmata/kyverno/pkg/engine/response" "gotest.tools/assert" + "sigs.k8s.io/controller-runtime/pkg/log" ) func Test_GeneratePVsFromEngineResponse_PathNotExist(t *testing.T) { @@ -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) } diff --git a/pkg/policyviolation/clusterpv.go b/pkg/policyviolation/clusterpv.go index c9336f8059..4cb26ea216 100644 --- a/pkg/policyviolation/clusterpv.go +++ b/pkg/policyviolation/clusterpv.go @@ -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" kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1" kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1" @@ -21,11 +21,13 @@ type clusterPV struct { cpvLister kyvernolister.ClusterPolicyViolationLister // policy violation interface kyvernoInterface kyvernov1.KyvernoV1Interface + // logger + log logr.Logger // update policy stats with violationCount policyStatusListener policystatus.Listener } -func newClusterPV(dclient *client.Client, +func newClusterPV(log logr.Logger, dclient *client.Client, cpvLister kyvernolister.ClusterPolicyViolationLister, kyvernoInterface kyvernov1.KyvernoV1Interface, policyStatus policystatus.Listener, @@ -34,6 +36,7 @@ func newClusterPV(dclient *client.Client, dclient: dclient, cpvLister: cpvLister, kyvernoInterface: kyvernoInterface, + log: log, policyStatusListener: policyStatus, } return &cpv @@ -56,6 +59,7 @@ func (cpv *clusterPV) create(pv kyverno.PolicyViolationTemplate) error { } func (cpv *clusterPV) getExisting(newPv kyverno.ClusterPolicyViolation) (*kyverno.ClusterPolicyViolation, error) { + logger := cpv.log.WithValues("namespace", newPv.Namespace, "name", newPv.Name) var err error // use labels policyLabelmap := map[string]string{"policy": newPv.Spec.Policy, "resource": newPv.Spec.ResourceSpec.ToKey()} @@ -66,7 +70,7 @@ func (cpv *clusterPV) getExisting(newPv kyverno.ClusterPolicyViolation) (*kyvern pvs, err := cpv.cpvLister.List(ls) if err != nil { - glog.Errorf("unable to list cluster policy violations : %v", err) + logger.Error(err, "failed to list cluster policy violations") return nil, err } @@ -83,7 +87,8 @@ func (cpv *clusterPV) getExisting(newPv kyverno.ClusterPolicyViolation) (*kyvern func (cpv *clusterPV) createPV(newPv *kyverno.ClusterPolicyViolation) error { var err error - glog.V(4).Infof("creating new policy violation for policy %s & resource %s/%s", newPv.Spec.Policy, newPv.Spec.ResourceSpec.Kind, newPv.Spec.ResourceSpec.Name) + logger := cpv.log.WithValues("policy", newPv.Spec.Policy, "kind", newPv.Spec.ResourceSpec.Kind, "namespace", newPv.Spec.ResourceSpec.Namespace, "name", newPv.Spec.ResourceSpec.Name) + logger.V(4).Info("creating new policy violation") obj, err := retryGetResource(cpv.dclient, newPv.Spec.ResourceSpec) if err != nil { return fmt.Errorf("failed to retry getting resource for policy violation %s/%s: %v", newPv.Name, newPv.Spec.Policy, err) @@ -95,7 +100,7 @@ func (cpv *clusterPV) createPV(newPv *kyverno.ClusterPolicyViolation) error { // create resource _, err = cpv.kyvernoInterface.ClusterPolicyViolations().Create(newPv) if err != nil { - glog.V(4).Infof("failed to create Cluster Policy Violation: %v", err) + logger.Error(err, "failed to create cluster policy violation") return err } @@ -103,15 +108,16 @@ func (cpv *clusterPV) createPV(newPv *kyverno.ClusterPolicyViolation) error { cpv.policyStatusListener.Send(violationCount{policyName: newPv.Spec.Policy, violatedRules: newPv.Spec.ViolatedRules}) } - glog.Infof("policy violation created for resource %v", newPv.Spec.ResourceSpec) + logger.Info("cluster policy violation created") return nil } func (cpv *clusterPV) updatePV(newPv, oldPv *kyverno.ClusterPolicyViolation) error { + logger := cpv.log.WithValues("policy", newPv.Spec.Policy, "kind", newPv.Spec.ResourceSpec.Kind, "namespace", newPv.Spec.ResourceSpec.Namespace, "name", newPv.Spec.ResourceSpec.Name) var err error // check if there is any update if reflect.DeepEqual(newPv.Spec, oldPv.Spec) { - glog.V(4).Infof("policy violation spec %v did not change so not updating it", newPv.Spec) + logger.V(4).Info("policy violation spec did not change, not upadating the resource") return nil } // set name @@ -123,7 +129,7 @@ func (cpv *clusterPV) updatePV(newPv, oldPv *kyverno.ClusterPolicyViolation) err if err != nil { return fmt.Errorf("failed to update cluster policy violation: %v", err) } - glog.Infof("cluster policy violation updated for resource %v", newPv.Spec.ResourceSpec) + logger.Info("cluster policy violation created") if newPv.Annotations["fromSync"] != "true" { cpv.policyStatusListener.Send(violationCount{policyName: newPv.Spec.Policy, violatedRules: newPv.Spec.ViolatedRules}) diff --git a/pkg/policyviolation/common.go b/pkg/policyviolation/common.go index 6f077b25b8..e2211edb13 100644 --- a/pkg/policyviolation/common.go +++ b/pkg/policyviolation/common.go @@ -5,13 +5,13 @@ import ( "time" backoff "github.com/cenkalti/backoff" - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" client "github.com/nirmata/kyverno/pkg/dclient" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/log" ) func createOwnerReference(resource *unstructured.Unstructured) metav1.OwnerReference { @@ -34,7 +34,7 @@ func retryGetResource(client *client.Client, rspec kyverno.ResourceSpec) (*unstr var err error getResource := func() error { obj, err = client.GetResource(rspec.Kind, rspec.Namespace, rspec.Name) - glog.V(4).Infof("retry %v getting %s/%s/%s", i, rspec.Kind, rspec.Namespace, rspec.Name) + log.Log.V(4).Info(fmt.Sprintf("retry %v getting %s/%s/%s", i, rspec.Kind, rspec.Namespace, rspec.Name)) i++ return err } diff --git a/pkg/policyviolation/generator.go b/pkg/policyviolation/generator.go index db5ca63fcd..ed3ffe76a2 100644 --- a/pkg/policyviolation/generator.go +++ b/pkg/policyviolation/generator.go @@ -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" @@ -38,6 +38,7 @@ type Generator struct { // returns true if the cluster policy store has been synced at least once pvSynced cache.InformerSynced // returns true if the namespaced cluster policy store has been synced at at least once + log logr.Logger nspvSynced cache.InformerSynced queue workqueue.RateLimitingInterface dataStore *dataStore @@ -107,7 +108,8 @@ func NewPVGenerator(client *kyvernoclient.Clientset, dclient *dclient.Client, pvInformer kyvernoinformer.ClusterPolicyViolationInformer, nspvInformer kyvernoinformer.PolicyViolationInformer, - policyStatus policystatus.Listener) *Generator { + policyStatus policystatus.Listener, + log logr.Logger) *Generator { gen := Generator{ kyvernoInterface: client.KyvernoV1(), dclient: dclient, @@ -117,6 +119,7 @@ func NewPVGenerator(client *kyvernoclient.Clientset, nspvSynced: nspvInformer.Informer().HasSynced, queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), workQueueName), dataStore: newDataStore(), + log: log, policyStatusListener: policyStatus, } return &gen @@ -135,18 +138,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++ { @@ -161,6 +164,7 @@ func (gen *Generator) runWorker() { } func (gen *Generator) handleErr(err error, key interface{}) { + logger := gen.log if err == nil { gen.queue.Forget(key) return @@ -168,23 +172,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 @@ -196,7 +199,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 @@ -204,7 +207,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) @@ -212,22 +215,22 @@ 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() == "" { // cluster scope resource generate a clusterpolicy violation - handler = newClusterPV(gen.dclient, gen.cpvLister, gen.kyvernoInterface, gen.policyStatusListener) + handler = newClusterPV(gen.log.WithName("ClusterPV"), gen.dclient, gen.cpvLister, gen.kyvernoInterface, gen.policyStatusListener) } else { // namespaced resources generated a namespaced policy violation in the namespace of the resource - handler = newNamespacedPV(gen.dclient, gen.nspvLister, gen.kyvernoInterface, gen.policyStatusListener) + handler = newNamespacedPV(gen.log.WithName("NamespacedPV"), gen.dclient, gen.nspvLister, gen.kyvernoInterface, gen.policyStatusListener) } failure := false @@ -240,12 +243,12 @@ func (gen *Generator) syncHandler(info Info) error { } // 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 { diff --git a/pkg/policyviolation/namespacedpv.go b/pkg/policyviolation/namespacedpv.go index ff32748d0b..3b0202c6df 100644 --- a/pkg/policyviolation/namespacedpv.go +++ b/pkg/policyviolation/namespacedpv.go @@ -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" kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1" kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1" @@ -21,11 +21,13 @@ type namespacedPV struct { nspvLister kyvernolister.PolicyViolationLister // policy violation interface kyvernoInterface kyvernov1.KyvernoV1Interface + // logger + log logr.Logger // update policy status with violationCount policyStatusListener policystatus.Listener } -func newNamespacedPV(dclient *client.Client, +func newNamespacedPV(log logr.Logger, dclient *client.Client, nspvLister kyvernolister.PolicyViolationLister, kyvernoInterface kyvernov1.KyvernoV1Interface, policyStatus policystatus.Listener, @@ -34,6 +36,7 @@ func newNamespacedPV(dclient *client.Client, dclient: dclient, nspvLister: nspvLister, kyvernoInterface: kyvernoInterface, + log: log, policyStatusListener: policyStatus, } return &nspv @@ -56,6 +59,7 @@ func (nspv *namespacedPV) create(pv kyverno.PolicyViolationTemplate) error { } func (nspv *namespacedPV) getExisting(newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) { + logger := nspv.log.WithValues("namespace", newPv.Namespace, "name", newPv.Name) var err error // use labels policyLabelmap := map[string]string{"policy": newPv.Spec.Policy, "resource": newPv.Spec.ResourceSpec.ToKey()} @@ -65,7 +69,7 @@ func (nspv *namespacedPV) getExisting(newPv kyverno.PolicyViolation) (*kyverno.P } pvs, err := nspv.nspvLister.PolicyViolations(newPv.GetNamespace()).List(ls) if err != nil { - glog.Errorf("unable to list namespaced policy violations : %v", err) + logger.Error(err, "failed to list namespaced policy violations") return nil, err } @@ -82,7 +86,8 @@ func (nspv *namespacedPV) getExisting(newPv kyverno.PolicyViolation) (*kyverno.P func (nspv *namespacedPV) createPV(newPv *kyverno.PolicyViolation) error { var err error - glog.V(4).Infof("creating new policy violation for policy %s & resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.ResourceSpec.Kind, newPv.Spec.ResourceSpec.Namespace, newPv.Spec.ResourceSpec.Name) + logger := nspv.log.WithValues("policy", newPv.Spec.Policy, "kind", newPv.Spec.ResourceSpec.Kind, "namespace", newPv.Spec.ResourceSpec.Namespace, "name", newPv.Spec.ResourceSpec.Name) + logger.V(4).Info("creating new policy violation") obj, err := retryGetResource(nspv.dclient, newPv.Spec.ResourceSpec) if err != nil { return fmt.Errorf("failed to retry getting resource for policy violation %s/%s: %v", newPv.Name, newPv.Spec.Policy, err) @@ -94,22 +99,23 @@ func (nspv *namespacedPV) createPV(newPv *kyverno.PolicyViolation) error { // create resource _, err = nspv.kyvernoInterface.PolicyViolations(newPv.GetNamespace()).Create(newPv) if err != nil { - glog.V(4).Infof("failed to create Cluster Policy Violation: %v", err) + logger.Error(err, "failed to create namespaced policy violation") return err } if newPv.Annotations["fromSync"] != "true" { nspv.policyStatusListener.Send(violationCount{policyName: newPv.Spec.Policy, violatedRules: newPv.Spec.ViolatedRules}) } - glog.Infof("policy violation created for resource %v", newPv.Spec.ResourceSpec) + logger.Info("namespaced policy violation created") return nil } func (nspv *namespacedPV) updatePV(newPv, oldPv *kyverno.PolicyViolation) error { + logger := nspv.log.WithValues("policy", newPv.Spec.Policy, "kind", newPv.Spec.ResourceSpec.Kind, "namespace", newPv.Spec.ResourceSpec.Namespace, "name", newPv.Spec.ResourceSpec.Name) var err error // check if there is any update if reflect.DeepEqual(newPv.Spec, oldPv.Spec) { - glog.V(4).Infof("policy violation spec %v did not change so not updating it", newPv.Spec) + logger.V(4).Info("policy violation spec did not change, not upadating the resource") return nil } // set name @@ -124,6 +130,6 @@ func (nspv *namespacedPV) updatePV(newPv, oldPv *kyverno.PolicyViolation) error if newPv.Annotations["fromSync"] != "true" { nspv.policyStatusListener.Send(violationCount{policyName: newPv.Spec.Policy, violatedRules: newPv.Spec.ViolatedRules}) } - glog.Infof("namespaced policy violation updated for resource %v", newPv.Spec.ResourceSpec) + logger.Info("namespaced policy violation created") return nil } diff --git a/pkg/testrunner/scenario.go b/pkg/testrunner/scenario.go index 39eaeb7257..6ca1ed583a 100644 --- a/pkg/testrunner/scenario.go +++ b/pkg/testrunner/scenario.go @@ -3,7 +3,6 @@ package testrunner import ( "bytes" "encoding/json" - "flag" "io/ioutil" "os" ospath "path" @@ -19,7 +18,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/kubernetes/scheme" - "github.com/golang/glog" "gopkg.in/yaml.v2" apiyaml "k8s.io/apimachinery/pkg/util/yaml" ) @@ -308,7 +306,7 @@ func loadPolicyResource(t *testing.T, file string) *unstructured.Unstructured { func getClient(t *testing.T, files []string) *client.Client { var objects []runtime.Object if files != nil { - glog.V(4).Infof("loading resources: %v", files) + for _, file := range files { objects = loadObjects(t, file) } @@ -404,7 +402,7 @@ func loadPolicy(t *testing.T, path string) *kyverno.ClusterPolicy { policy := kyverno.ClusterPolicy{} pBytes, err := apiyaml.ToJSON(p) if err != nil { - glog.Error(err) + t.Error(err) continue } @@ -427,7 +425,8 @@ func loadPolicy(t *testing.T, path string) *kyverno.ClusterPolicy { } func testScenario(t *testing.T, path string) { - flag.Set("logtostderr", "true") + + // flag.Set("logtostderr", "true") // flag.Set("v", "8") scenario, err := loadScenario(t, path) diff --git a/pkg/testrunner/utils.go b/pkg/testrunner/utils.go index d888cdcdc6..6fdfb91c78 100644 --- a/pkg/testrunner/utils.go +++ b/pkg/testrunner/utils.go @@ -4,8 +4,8 @@ import ( "io/ioutil" "os" - "github.com/golang/glog" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/log" ) const ( @@ -42,7 +42,7 @@ func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) { resource := &unstructured.Unstructured{} err := resource.UnmarshalJSON(data) if err != nil { - glog.V(4).Infof("failed to unmarshall resource: %v", err) + log.Log.Error(err, "failed to unmarshal resource") return nil, err } return resource, nil diff --git a/pkg/tls/tls.go b/pkg/tls/tls.go index c91c9b922c..4acc08272d 100644 --- a/pkg/tls/tls.go +++ b/pkg/tls/tls.go @@ -142,5 +142,6 @@ func IsTLSPairShouldBeUpdated(tlsPair *TlsPemPair) bool { return true } + // TODO : should use time.Until instead of t.Sub(time.Now()) (gosimple) return expirationDate.Sub(time.Now()) < timeReserveBeforeCertificateExpiration } diff --git a/pkg/userinfo/roleRef.go b/pkg/userinfo/roleRef.go index ac089bc041..d813f07f4e 100644 --- a/pkg/userinfo/roleRef.go +++ b/pkg/userinfo/roleRef.go @@ -4,12 +4,12 @@ import ( "fmt" "strings" - "github.com/golang/glog" v1beta1 "k8s.io/api/admission/v1beta1" authenticationv1 "k8s.io/api/authentication/v1" rbacv1 "k8s.io/api/rbac/v1" labels "k8s.io/apimachinery/pkg/labels" rbaclister "k8s.io/client-go/listers/rbac/v1" + "sigs.k8s.io/controller-runtime/pkg/log" ) const ( @@ -101,7 +101,7 @@ func matchSubjectsMap(subject rbacv1.Subject, userInfo authenticationv1.UserInfo func matchServiceAccount(subject rbacv1.Subject, userInfo authenticationv1.UserInfo) bool { subjectServiceAccount := subject.Namespace + ":" + subject.Name if userInfo.Username[len(SaPrefix):] != subjectServiceAccount { - glog.V(3).Infof("service account not match, expect %s, got %s", subjectServiceAccount, userInfo.Username[len(SaPrefix):]) + log.Log.V(3).Info(fmt.Sprintf("service account not match, expect %s, got %s", subjectServiceAccount, userInfo.Username[len(SaPrefix):])) return false } @@ -117,6 +117,6 @@ func matchUserOrGroup(subject rbacv1.Subject, userInfo authenticationv1.UserInfo } } - glog.V(3).Infof("user/group '%v' info not found in request userInfo: %v", subject.Name, keys) + log.Log.V(3).Info(fmt.Sprintf("user/group '%v' info not found in request userInfo: %v", subject.Name, keys)) return false } diff --git a/pkg/userinfo/roleRef_test.go b/pkg/userinfo/roleRef_test.go index 3929c38577..003bbe2cd6 100644 --- a/pkg/userinfo/roleRef_test.go +++ b/pkg/userinfo/roleRef_test.go @@ -1,7 +1,6 @@ package userinfo import ( - "flag" "reflect" "testing" @@ -153,9 +152,10 @@ func newRoleBinding(name, ns string, subjects []rbacv1.Subject, roles rbacv1.Rol } func Test_getRoleRefByRoleBindings(t *testing.T) { - flag.Parse() - flag.Set("logtostderr", "true") - flag.Set("v", "3") + + // flag.Parse() + // flag.Set("logtostderr", "true") + // flag.Set("v", "3") list := make([]*rbacv1.RoleBinding, 2) diff --git a/pkg/utils/util.go b/pkg/utils/util.go index c6c41f4be0..5fe0602e71 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -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") } } } diff --git a/pkg/version/version.go b/pkg/version/version.go index ef768a35bf..8d9303d204 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -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) } diff --git a/pkg/webhookconfig/checker.go b/pkg/webhookconfig/checker.go index ee85f36c35..db1e765629 100644 --- a/pkg/webhookconfig/checker.go +++ b/pkg/webhookconfig/checker.go @@ -4,7 +4,6 @@ import ( "fmt" "sync" - "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/config" admregapi "k8s.io/api/admissionregistration/v1beta1" errorsapi "k8s.io/apimachinery/pkg/api/errors" @@ -19,8 +18,8 @@ func (wrc *WebhookRegistrationClient) constructVerifyMutatingWebhookConfig(caDat wrc.constructOwner(), }, }, - Webhooks: []admregapi.Webhook{ - generateWebhook( + Webhooks: []admregapi.MutatingWebhook{ + generateMutatingWebhook( config.VerifyMutatingWebhookName, config.VerifyMutatingWebhookServicePath, caData, @@ -36,14 +35,15 @@ func (wrc *WebhookRegistrationClient) constructVerifyMutatingWebhookConfig(caDat } func (wrc *WebhookRegistrationClient) constructDebugVerifyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration { + logger := wrc.log url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.VerifyMutatingWebhookServicePath) - glog.V(4).Infof("Debug VerifyMutatingWebhookConfig is registered with url %s\n", url) + logger.V(4).Info("Debug VerifyMutatingWebhookConfig is registered with url", "url", url) return &admregapi.MutatingWebhookConfiguration{ ObjectMeta: v1.ObjectMeta{ Name: config.VerifyMutatingWebhookConfigurationDebugName, }, - Webhooks: []admregapi.Webhook{ - generateDebugWebhook( + Webhooks: []admregapi.MutatingWebhook{ + generateDebugMutatingWebhook( config.VerifyMutatingWebhookName, url, caData, @@ -68,13 +68,14 @@ func (wrc *WebhookRegistrationClient) removeVerifyWebhookMutatingWebhookConfig(w } else { mutatingConfig = config.VerifyMutatingWebhookConfigurationName } - glog.V(4).Infof("removing webhook configuration %s", mutatingConfig) + logger := wrc.log.WithValues("name", mutatingConfig) + logger.V(4).Info("removing webhook configuration") err = wrc.client.DeleteResource(MutatingWebhookConfigurationKind, "", mutatingConfig, false) if errorsapi.IsNotFound(err) { - glog.V(4).Infof("verify webhook configuration %s, does not exits. not deleting", mutatingConfig) + logger.Error(err, "verify webhook configuration, does not exits. not deleting") } else if err != nil { - glog.Errorf("failed to delete verify webhook configuration %s: %v", mutatingConfig, err) + logger.Error(err, "failed to delete verify wwebhook configuration") } else { - glog.V(4).Infof("successfully deleted verify webhook configuration %s", mutatingConfig) + logger.V(4).Info("successfully deleted verify webhook configuration") } } diff --git a/pkg/webhookconfig/common.go b/pkg/webhookconfig/common.go index d73fcafb13..2b1c17f6e0 100644 --- a/pkg/webhookconfig/common.go +++ b/pkg/webhookconfig/common.go @@ -3,7 +3,6 @@ package webhookconfig import ( "io/ioutil" - "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/config" admregapi "k8s.io/api/admissionregistration/v1beta1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -11,20 +10,21 @@ import ( ) func (wrc *WebhookRegistrationClient) readCaData() []byte { + logger := wrc.log var caData []byte // Check if ca is defined in the secret tls-ca // assume the key and signed cert have been defined in secret tls.kyverno if caData = wrc.client.ReadRootCASecret(); len(caData) != 0 { - glog.V(4).Infof("read CA from secret") + logger.V(4).Info("read CA from secret") return caData } - glog.V(4).Infof("failed to read CA from secret, reading from kubeconfig") + logger.V(4).Info("failed to read CA from secret, reading from kubeconfig") // load the CA from kubeconfig if caData = extractCA(wrc.clientConfig); len(caData) != 0 { - glog.V(4).Infof("read CA from kubeconfig") + logger.V(4).Info("read CA from kubeconfig") return caData } - glog.V(4).Infof("failed to read CA from kubeconfig") + logger.V(4).Info("failed to read CA from kubeconfig") return nil } @@ -46,10 +46,11 @@ func extractCA(config *rest.Config) (result []byte) { } func (wrc *WebhookRegistrationClient) constructOwner() v1.OwnerReference { + logger := wrc.log kubePolicyDeployment, err := wrc.client.GetKubePolicyDeployment() if err != nil { - glog.Errorf("Error when constructing OwnerReference, err: %v\n", err) + logger.Error(err, "failed to construct OwnerReference") return v1.OwnerReference{} } @@ -61,10 +62,11 @@ 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 { +// 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 +95,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{ diff --git a/pkg/webhookconfig/policy.go b/pkg/webhookconfig/policy.go index d1798a54e5..da9fa0fa8a 100644 --- a/pkg/webhookconfig/policy.go +++ b/pkg/webhookconfig/policy.go @@ -3,7 +3,6 @@ package webhookconfig import ( "fmt" - "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/config" admregapi "k8s.io/api/admissionregistration/v1beta1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -18,8 +17,8 @@ func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig(caDa wrc.constructOwner(), }, }, - Webhooks: []admregapi.Webhook{ - generateWebhook( + Webhooks: []admregapi.ValidatingWebhook{ + generateValidatingWebhook( config.PolicyValidatingWebhookName, config.PolicyValidatingWebhookServicePath, caData, @@ -35,15 +34,16 @@ func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig(caDa } func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration { + logger := wrc.log url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.PolicyValidatingWebhookServicePath) - glog.V(4).Infof("Debug PolicyValidatingWebhookConfig is registered with url %s\n", url) + logger.V(4).Info("Debug PolicyValidatingWebhookConfig is registered with url ", "url", url) return &admregapi.ValidatingWebhookConfiguration{ 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, @@ -82,15 +82,16 @@ func (wrc *WebhookRegistrationClient) contructPolicyMutatingWebhookConfig(caData } } func (wrc *WebhookRegistrationClient) contructDebugPolicyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration { + logger := wrc.log url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.PolicyMutatingWebhookServicePath) - glog.V(4).Infof("Debug PolicyMutatingWebhookConfig is registered with url %s\n", url) + logger.V(4).Info("Debug PolicyMutatingWebhookConfig is registered with url ", "url", url) return &admregapi.MutatingWebhookConfiguration{ ObjectMeta: v1.ObjectMeta{ Name: config.PolicyMutatingWebhookConfigurationDebugName, }, - Webhooks: []admregapi.Webhook{ - generateDebugWebhook( + Webhooks: []admregapi.MutatingWebhook{ + generateDebugMutatingWebhook( config.PolicyMutatingWebhookName, url, caData, diff --git a/pkg/webhookconfig/registration.go b/pkg/webhookconfig/registration.go index 8db0cba73c..8124ad39d4 100644 --- a/pkg/webhookconfig/registration.go +++ b/pkg/webhookconfig/registration.go @@ -2,10 +2,11 @@ package webhookconfig import ( "errors" + "fmt" "sync" "time" - "github.com/golang/glog" + "github.com/go-logr/logr" "github.com/nirmata/kyverno/pkg/config" client "github.com/nirmata/kyverno/pkg/dclient" admregapi "k8s.io/api/admissionregistration/v1beta1" @@ -27,6 +28,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 +36,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 +93,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 +114,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 +141,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,20 +176,19 @@ 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 } func (wrc *WebhookRegistrationClient) createPolicyMutatingWebhookConfiguration() error { var caData []byte var config *admregapi.MutatingWebhookConfiguration - // read CA data from // 1) secret(config) // 2) kubeconfig @@ -203,8 +210,7 @@ func (wrc *WebhookRegistrationClient) createPolicyMutatingWebhookConfiguration() if _, err := wrc.client.CreateResource(MutatingWebhookConfigurationKind, "", *config, false); err != nil { return err } - - glog.V(4).Infof("created Mutating Webhook Configuration %s ", config.Name) + wrc.log.V(4).Info("reated Mutating Webhook Configuration", "name", config.Name) return nil } @@ -234,7 +240,7 @@ func (wrc *WebhookRegistrationClient) createVerifyMutatingWebhookConfiguration() return err } - glog.V(4).Infof("created Mutating Webhook Configuration %s ", config.Name) + wrc.log.V(4).Info("reated Mutating Webhook Configuration", "name", config.Name) return nil } @@ -243,9 +249,9 @@ func (wrc *WebhookRegistrationClient) createVerifyMutatingWebhookConfiguration() // Register will fail if the config exists, so there is no need to fail on error func (wrc *WebhookRegistrationClient) removeWebhookConfigurations() { startTime := time.Now() - glog.V(4).Infof("Started cleaning up webhookconfigurations") + wrc.log.Info("Started cleaning up webhookconfigurations") defer func() { - glog.V(4).Infof("Finished cleaning up webhookcongfigurations (%v)", time.Since(startTime)) + wrc.log.V(4).Info("Finished cleaning up webhookcongfigurations", "processingTime", time.Since(startTime)) }() var wg sync.WaitGroup @@ -269,13 +275,13 @@ func (wrc *WebhookRegistrationClient) removeWebhookConfigurations() { func (wrc *WebhookRegistrationClient) removeResourceMutatingWebhookConfiguration(wg *sync.WaitGroup) { defer wg.Done() if err := wrc.RemoveResourceMutatingWebhookConfiguration(); err != nil { - glog.Error(err) + wrc.log.Error(err, "failed to remove resource mutating webhook configuration") } } func (wrc *WebhookRegistrationClient) removeResourceValidatingWebhookConfiguration(wg *sync.WaitGroup) { defer wg.Done() if err := wrc.RemoveResourceValidatingWebhookConfiguration(); err != nil { - glog.Error(err) + wrc.log.Error(err, "failed to remove resource validation webhook configuration") } } @@ -290,15 +296,15 @@ func (wrc *WebhookRegistrationClient) removePolicyMutatingWebhookConfiguration(w } else { mutatingConfig = config.PolicyMutatingWebhookConfigurationName } - - glog.V(4).Infof("removing webhook configuration %s", mutatingConfig) + logger := wrc.log.WithValues("name", mutatingConfig) + logger.V(4).Info("removing mutating webhook configuration") err := wrc.client.DeleteResource(MutatingWebhookConfigurationKind, "", mutatingConfig, false) if errorsapi.IsNotFound(err) { - glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", mutatingConfig) + logger.Error(err, "policy mutating webhook configuration does not exist, not deleting") } else if err != nil { - glog.Errorf("failed to delete policy webhook configuration %s: %v", mutatingConfig, err) + logger.Error(err, "failed to delete policy mutating webhook configuration") } else { - glog.V(4).Infof("successfully deleted policy webhook configuration %s", mutatingConfig) + logger.V(4).Info("successfully deleted policy mutating webhook configutation") } } @@ -313,13 +319,14 @@ func (wrc *WebhookRegistrationClient) removePolicyValidatingWebhookConfiguration } else { validatingConfig = config.PolicyValidatingWebhookConfigurationName } - glog.V(4).Infof("removing webhook configuration %s", validatingConfig) + logger := wrc.log.WithValues("name", validatingConfig) + logger.V(4).Info("removing validating webhook configuration") err := wrc.client.DeleteResource(ValidatingWebhookConfigurationKind, "", validatingConfig, false) if errorsapi.IsNotFound(err) { - glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", validatingConfig) + logger.Error(err, "policy validating webhook configuration does not exist, not deleting") } else if err != nil { - glog.Errorf("failed to delete policy webhook configuration %s: %v", validatingConfig, err) + logger.Error(err, "failed to delete policy validating webhook configuration") } else { - glog.V(4).Infof("successfully deleted policy webhook configuration %s", validatingConfig) + logger.V(4).Info("successfully deleted policy validating webhook configutation") } } diff --git a/pkg/webhookconfig/resource.go b/pkg/webhookconfig/resource.go index 090c33636a..050517da0a 100644 --- a/pkg/webhookconfig/resource.go +++ b/pkg/webhookconfig/resource.go @@ -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 } diff --git a/pkg/webhookconfig/rwebhookregister.go b/pkg/webhookconfig/rwebhookregister.go index aacdc72b9f..a312e07277 100644 --- a/pkg/webhookconfig/rwebhookregister.go +++ b/pkg/webhookconfig/rwebhookregister.go @@ -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 diff --git a/pkg/webhooks/admission_test.go b/pkg/webhooks/admission_test.go deleted file mode 100644 index d753352a72..0000000000 --- a/pkg/webhooks/admission_test.go +++ /dev/null @@ -1 +0,0 @@ -package webhooks_test diff --git a/pkg/webhooks/annotations.go b/pkg/webhooks/annotations.go index 34d3e10468..eddded1cde 100644 --- a/pkg/webhooks/annotations.go +++ b/pkg/webhooks/annotations.go @@ -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 } diff --git a/pkg/webhooks/annotations_test.go b/pkg/webhooks/annotations_test.go index 320e347ea3..d14657b6cb 100644 --- a/pkg/webhooks/annotations_test.go +++ b/pkg/webhooks/annotations_test.go @@ -6,6 +6,7 @@ import ( "github.com/nirmata/kyverno/pkg/engine/response" "gotest.tools/assert" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/log" ) func newPolicyResponse(policy, rule string, patchesStr []string, success bool) response.PolicyResponse { @@ -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) } diff --git a/pkg/webhooks/checker.go b/pkg/webhooks/checker.go index e6ccfc41c9..4a52fa7649 100644 --- a/pkg/webhooks/checker.go +++ b/pkg/webhooks/checker.go @@ -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, } diff --git a/pkg/webhooks/common.go b/pkg/webhooks/common.go index 2ca90049f8..476606b468 100644 --- a/pkg/webhooks/common.go +++ b/pkg/webhooks/common.go @@ -4,9 +4,8 @@ import ( "fmt" "strings" + "github.com/go-logr/logr" yamlv2 "gopkg.in/yaml.v2" - - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/nirmata/kyverno/pkg/engine/response" engineutils "github.com/nirmata/kyverno/pkg/engine/utils" @@ -27,14 +26,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 } @@ -103,14 +102,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 diff --git a/pkg/webhooks/generate/generate.go b/pkg/webhooks/generate/generate.go index f120c9fb5d..3631845541 100644 --- a/pkg/webhooks/generate/generate.go +++ b/pkg/webhooks/generate/generate.go @@ -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 } diff --git a/pkg/webhooks/generation.go b/pkg/webhooks/generation.go index 956c568cac..7f0fc4a692 100644 --- a/pkg/webhooks/generation.go +++ b/pkg/webhooks/generation.go @@ -5,7 +5,6 @@ import ( "sort" "time" - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/nirmata/kyverno/pkg/engine" @@ -18,19 +17,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, @@ -41,16 +40,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{ diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 1991427ebf..68ca58fae1 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -7,7 +7,6 @@ import ( "github.com/nirmata/kyverno/pkg/openapi" - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/nirmata/kyverno/pkg/engine" @@ -22,8 +21,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 engineResponses []response.EngineResponse @@ -39,16 +38,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{ @@ -58,39 +57,36 @@ 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) engineResponses = append(engineResponses, engineResponse) ws.statusListener.Send(mutateStats{resp: engineResponse}) 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 } err := openapi.ValidateResource(*engineResponse.PatchedResource.DeepCopy(), engineResponse.PatchedResource.GetKind()) if err != nil { - glog.V(4).Infoln(err) + logger.Error(err, "failed to validate resource") 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: @@ -100,25 +96,21 @@ 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...) // 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) } diff --git a/pkg/webhooks/policymutation.go b/pkg/webhooks/policymutation.go index 8081528edd..dea2f87fff 100644 --- a/pkg/webhooks/policymutation.go +++ b/pkg/webhooks/policymutation.go @@ -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 diff --git a/pkg/webhooks/policymutation_test.go b/pkg/webhooks/policymutation_test.go index 0b5af63c59..d887cd6ee2 100644 --- a/pkg/webhooks/policymutation_test.go +++ b/pkg/webhooks/policymutation_test.go @@ -8,6 +8,7 @@ import ( kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/nirmata/kyverno/pkg/engine/utils" "gotest.tools/assert" + "sigs.k8s.io/controller-runtime/pkg/log" ) func compareJSONAsMap(t *testing.T, expected, actual []byte) { @@ -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) diff --git a/pkg/webhooks/policyvalidation.go b/pkg/webhooks/policyvalidation.go index d58a22eec9..897bba0de0 100644 --- a/pkg/webhooks/policyvalidation.go +++ b/pkg/webhooks/policyvalidation.go @@ -4,13 +4,14 @@ import ( policyvalidate "github.com/nirmata/kyverno/pkg/policy" v1beta1 "k8s.io/api/admission/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) //HandlePolicyValidation performs the validation check on policy resource func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { //TODO: can this happen? wont this be picked by OpenAPI spec schema ? - if err := policyvalidate.Validate(request.Object.Raw); err != nil { + if err := policyvalidate.Validate(request.Object.Raw, ws.client, false); err != nil { return &v1beta1.AdmissionResponse{ Allowed: false, Result: &metav1.Status{ diff --git a/pkg/webhooks/report.go b/pkg/webhooks/report.go index 035a60688e..83e67c5753 100644 --- a/pkg/webhooks/report.go +++ b/pkg/webhooks/report.go @@ -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, diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index b0ca988449..fbc8788d0b 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -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 } diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 54a7fbdf3e..d0b49347ad 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -5,7 +5,6 @@ import ( "sort" "time" - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" "github.com/nirmata/kyverno/pkg/engine" @@ -19,16 +18,14 @@ 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) - - evalTime := time.Now() + 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") // Get new and old resource 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{ @@ -40,17 +37,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{ @@ -61,8 +58,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{}) { @@ -75,17 +71,13 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol resp: engineResponse, }) 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: @@ -97,19 +89,18 @@ 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") return false, getEnforceFailureErrorMsg(engineResponses) } // ADD POLICY VIOLATIONS // violations are created with resource on "audit" - pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses) + pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses, logger) ws.pvGenerator.Add(pvInfos...) // report time end - glog.V(4).Infof("report: %v %s/%s/%s", time.Since(reportTime), request.Kind, request.Namespace, request.Name) return true, "" } diff --git a/scripts/update-codegen.sh b/scripts/update-codegen.sh index 686c2cdfe1..a6f56b314d 100755 --- a/scripts/update-codegen.sh +++ b/scripts/update-codegen.sh @@ -13,8 +13,10 @@ esac NIRMATA_DIR=$(dirname ${BASH_SOURCE})/.. NIRMATA_ROOT=$(${linkutil} -f ${NIRMATA_DIR}) +# instructions to build project https://github.com/nirmata/kyverno/wiki/Building + # get relative path to code generation script -CODEGEN_PKG=${NIRMATA_DIR}/vendor/k8s.io/code-generator +CODEGEN_PKG="${GOPATH}/src/k8s.io/code-generator" # get relative path of nirmata NIRMATA_PKG=${NIRMATA_ROOT#"${GOPATH}/src/"}