mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-30 03:15:05 +00:00
Merge pull request #750 from nirmata/access_check
Access check & logging framework refactor & update code-gen version
This commit is contained in:
commit
589f8ece47
113 changed files with 2799 additions and 2026 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
32
go.mod
32
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
|
||||
)
|
||||
|
|
|
@ -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))
|
||||
|
|
108
pkg/auth/auth.go
Normal file
108
pkg/auth/auth.go
Normal file
|
@ -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
|
||||
}
|
48
pkg/auth/auth_test.go
Normal file
48
pkg/auth/auth_test.go
Normal file
|
@ -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()
|
||||
|
||||
// }
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ""
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
func TestMeetConditions_NoAnchor(t *testing.T) {
|
||||
|
@ -28,7 +29,7 @@ func TestMeetConditions_NoAnchor(t *testing.T) {
|
|||
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err := meetConditions(nil, overlay)
|
||||
_, err := meetConditions(log.Log, nil, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -81,7 +82,7 @@ func TestMeetConditions_conditionalAnchorOnMap(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err := meetConditions(resource, overlay)
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, !reflect.DeepEqual(err, overlayError{}))
|
||||
|
||||
overlayRaw = []byte(`
|
||||
|
@ -101,7 +102,7 @@ func TestMeetConditions_conditionalAnchorOnMap(t *testing.T) {
|
|||
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, overlayerr := meetConditions(resource, overlay)
|
||||
_, overlayerr := meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -140,7 +141,7 @@ func TestMeetConditions_DifferentTypes(t *testing.T) {
|
|||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
// 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/"))
|
||||
}
|
||||
|
||||
|
@ -193,7 +194,7 @@ func TestMeetConditions_anchosInSameObject(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err := meetConditions(resource, overlay)
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Error(t, err, "[overlayError:0] Failed validating value 443 with overlay 444")
|
||||
}
|
||||
|
||||
|
@ -251,7 +252,7 @@ func TestMeetConditions_anchorOnPeer(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err := meetConditions(resource, overlay)
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -328,7 +329,7 @@ func TestMeetConditions_anchorsOnMetaAndSpec(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err := meetConditions(resource, overlay)
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -409,7 +410,7 @@ func TestMeetConditions_anchorsOnPeer_single(t *testing.T) {
|
|||
json.Unmarshal(resourceRawAnchorOnPeers, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err := meetConditions(resource, overlay)
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -443,7 +444,7 @@ func TestMeetConditions_anchorsOnPeer_two(t *testing.T) {
|
|||
json.Unmarshal(resourceRawAnchorOnPeers, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err := meetConditions(resource, overlay)
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Error(t, err, "[overlayError:0] Failed validating value true with overlay false")
|
||||
|
||||
overlayRaw = []byte(`{
|
||||
|
@ -472,7 +473,7 @@ func TestMeetConditions_anchorsOnPeer_two(t *testing.T) {
|
|||
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err = meetConditions(resource, overlay)
|
||||
_, err = meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
|
||||
overlayRaw = []byte(`{
|
||||
|
@ -501,7 +502,7 @@ func TestMeetConditions_anchorsOnPeer_two(t *testing.T) {
|
|||
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err = meetConditions(resource, overlay)
|
||||
_, err = meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -535,7 +536,7 @@ func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) {
|
|||
json.Unmarshal(resourceRawAnchorOnPeers, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err := meetConditions(resource, overlay)
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
|
||||
overlayRaw = []byte(`{
|
||||
|
@ -564,7 +565,7 @@ func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) {
|
|||
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, err = meetConditions(resource, overlay)
|
||||
_, err = meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
|
||||
overlayRaw = []byte(`{
|
||||
|
@ -593,7 +594,7 @@ func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) {
|
|||
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
_, 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")
|
||||
}
|
||||
|
||||
|
@ -652,7 +653,7 @@ func TestMeetConditions_AtleastOneExist(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
path, err := meetConditions(resource, overlay)
|
||||
path, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
assert.Assert(t, len(path) == 0)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
@ -66,7 +67,7 @@ func TestProcessOverlayPatches_NestedListWithAnchor(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, patches != nil)
|
||||
|
||||
|
@ -166,7 +167,7 @@ func TestProcessOverlayPatches_InsertIntoArray(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, patches != nil)
|
||||
|
||||
|
@ -287,7 +288,7 @@ func TestProcessOverlayPatches_TestInsertToArray(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, patches != nil)
|
||||
|
||||
|
@ -370,7 +371,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -458,7 +459,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
|
|||
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -494,7 +495,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
|
|||
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -523,7 +524,7 @@ func TestProcessOverlayPatches_AddingAnchor(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -608,7 +609,7 @@ func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -686,7 +687,7 @@ func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) {
|
|||
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -750,7 +751,7 @@ func TestProcessOverlayPatches_anchorOnPeer(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -807,7 +808,7 @@ func TestProcessOverlayPatches_anchorOnPeer(t *testing.T) {
|
|||
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -889,7 +890,7 @@ func TestProcessOverlayPatches_insertWithCondition(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -1000,7 +1001,7 @@ func TestProcessOverlayPatches_InsertIfNotPresentWithConditions(t *testing.T) {
|
|||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -8,15 +8,15 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||
"github.com/nirmata/kyverno/pkg/engine/operator"
|
||||
)
|
||||
|
||||
// ValidateResourceWithPattern is a start of element-by-element validation process
|
||||
// It assumes that validation is started from root, so "/" is passed
|
||||
func ValidateResourceWithPattern(resource, pattern interface{}) (string, error) {
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
func ValidateResourceWithPattern(log logr.Logger, resource, pattern interface{}) (string, error) {
|
||||
path, err := validateResourceElement(log, resource, pattern, pattern, "/")
|
||||
if err != nil {
|
||||
return path, err
|
||||
}
|
||||
|
@ -27,44 +27,44 @@ func ValidateResourceWithPattern(resource, pattern interface{}) (string, error)
|
|||
// validateResourceElement detects the element type (map, array, nil, string, int, bool, float)
|
||||
// and calls corresponding handler
|
||||
// Pattern tree and resource tree can have different structure. In this case validation fails
|
||||
func validateResourceElement(resourceElement, patternElement, originPattern interface{}, path string) (string, error) {
|
||||
func validateResourceElement(log logr.Logger, resourceElement, patternElement, originPattern interface{}, path string) (string, error) {
|
||||
var err error
|
||||
switch typedPatternElement := patternElement.(type) {
|
||||
// map
|
||||
case map[string]interface{}:
|
||||
typedResourceElement, ok := resourceElement.(map[string]interface{})
|
||||
if !ok {
|
||||
glog.V(4).Infof("Pattern and resource have different structures. Path: %s. Expected %T, found %T", path, patternElement, resourceElement)
|
||||
log.V(4).Info("Pattern and resource have different structures.", "path", path, "expected", fmt.Sprintf("%T", patternElement), "current", fmt.Sprintf("%T", resourceElement))
|
||||
return path, fmt.Errorf("Pattern and resource have different structures. Path: %s. Expected %T, found %T", path, patternElement, resourceElement)
|
||||
}
|
||||
|
||||
return validateMap(typedResourceElement, typedPatternElement, originPattern, path)
|
||||
return validateMap(log, typedResourceElement, typedPatternElement, originPattern, path)
|
||||
// array
|
||||
case []interface{}:
|
||||
typedResourceElement, ok := resourceElement.([]interface{})
|
||||
if !ok {
|
||||
glog.V(4).Infof("Pattern and resource have different structures. Path: %s. Expected %T, found %T", path, patternElement, resourceElement)
|
||||
log.V(4).Info("Pattern and resource have different structures.", "path", path, "expected", fmt.Sprintf("%T", patternElement), "current", fmt.Sprintf("%T", resourceElement))
|
||||
return path, fmt.Errorf("Validation rule Failed at path %s, resource does not satisfy the expected overlay pattern", path)
|
||||
}
|
||||
|
||||
return validateArray(typedResourceElement, typedPatternElement, originPattern, path)
|
||||
return validateArray(log, typedResourceElement, typedPatternElement, originPattern, path)
|
||||
// elementary values
|
||||
case string, float64, int, int64, bool, nil:
|
||||
/*Analyze pattern */
|
||||
if checkedPattern := reflect.ValueOf(patternElement); checkedPattern.Kind() == reflect.String {
|
||||
if isStringIsReference(checkedPattern.String()) { //check for $ anchor
|
||||
patternElement, err = actualizePattern(originPattern, checkedPattern.String(), path)
|
||||
patternElement, err = actualizePattern(log, originPattern, checkedPattern.String(), path)
|
||||
if err != nil {
|
||||
return path, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ValidateValueWithPattern(resourceElement, patternElement) {
|
||||
if !ValidateValueWithPattern(log, resourceElement, patternElement) {
|
||||
return path, fmt.Errorf("Validation rule failed at '%s' to validate value '%v' with pattern '%v'", path, resourceElement, patternElement)
|
||||
}
|
||||
|
||||
default:
|
||||
glog.V(4).Infof("Pattern contains unknown type %T. Path: %s", patternElement, path)
|
||||
log.V(4).Info("Pattern contains unknown type", "path", path, "current", fmt.Sprintf("%T", patternElement))
|
||||
return path, fmt.Errorf("Validation rule failed at '%s', pattern contains unknown type", path)
|
||||
}
|
||||
return "", nil
|
||||
|
@ -72,7 +72,7 @@ func validateResourceElement(resourceElement, patternElement, originPattern inte
|
|||
|
||||
// If validateResourceElement detects map element inside resource and pattern trees, it goes to validateMap
|
||||
// For each element of the map we must detect the type again, so we pass these elements to validateResourceElement
|
||||
func validateMap(resourceMap, patternMap map[string]interface{}, origPattern interface{}, path string) (string, error) {
|
||||
func validateMap(log logr.Logger, resourceMap, patternMap map[string]interface{}, origPattern interface{}, path string) (string, error) {
|
||||
// check if there is anchor in pattern
|
||||
// Phase 1 : Evaluate all the anchors
|
||||
// Phase 2 : Evaluate non-anchors
|
||||
|
@ -91,7 +91,7 @@ func validateMap(resourceMap, patternMap map[string]interface{}, origPattern int
|
|||
if err != nil {
|
||||
// If Conditional anchor fails then we dont process the resources
|
||||
if anchor.IsConditionAnchor(key) {
|
||||
glog.V(4).Infof("condition anchor did not satisfy, wont process the resources: %s", err)
|
||||
log.Error(err, "condition anchor did not satisfy, wont process the resource")
|
||||
return "", nil
|
||||
}
|
||||
return handlerPath, err
|
||||
|
@ -109,7 +109,7 @@ func validateMap(resourceMap, patternMap map[string]interface{}, origPattern int
|
|||
return "", nil
|
||||
}
|
||||
|
||||
func validateArray(resourceArray, patternArray []interface{}, originPattern interface{}, path string) (string, error) {
|
||||
func validateArray(log logr.Logger, resourceArray, patternArray []interface{}, originPattern interface{}, path string) (string, error) {
|
||||
|
||||
if 0 == len(patternArray) {
|
||||
return path, fmt.Errorf("Pattern Array empty")
|
||||
|
@ -119,7 +119,7 @@ func validateArray(resourceArray, patternArray []interface{}, originPattern inte
|
|||
case map[string]interface{}:
|
||||
// This is special case, because maps in arrays can have anchors that must be
|
||||
// processed with the special way affecting the entire array
|
||||
path, err := validateArrayOfMaps(resourceArray, typedPatternElement, originPattern, path)
|
||||
path, err := validateArrayOfMaps(log, resourceArray, typedPatternElement, originPattern, path)
|
||||
if err != nil {
|
||||
return path, err
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ func validateArray(resourceArray, patternArray []interface{}, originPattern inte
|
|||
// In all other cases - detect type and handle each array element with validateResourceElement
|
||||
for i, patternElement := range patternArray {
|
||||
currentPath := path + strconv.Itoa(i) + "/"
|
||||
path, err := validateResourceElement(resourceArray[i], patternElement, originPattern, currentPath)
|
||||
path, err := validateResourceElement(log, resourceArray[i], patternElement, originPattern, currentPath)
|
||||
if err != nil {
|
||||
return path, err
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ func validateArray(resourceArray, patternArray []interface{}, originPattern inte
|
|||
return "", nil
|
||||
}
|
||||
|
||||
func actualizePattern(origPattern interface{}, referencePattern, absolutePath string) (interface{}, error) {
|
||||
func actualizePattern(log logr.Logger, origPattern interface{}, referencePattern, absolutePath string) (interface{}, error) {
|
||||
var foundValue interface{}
|
||||
|
||||
referencePattern = strings.Trim(referencePattern, "$()")
|
||||
|
@ -155,7 +155,7 @@ func actualizePattern(origPattern interface{}, referencePattern, absolutePath st
|
|||
// value :=
|
||||
actualPath := formAbsolutePath(referencePattern, absolutePath)
|
||||
|
||||
valFromReference, err := getValueFromReference(origPattern, actualPath)
|
||||
valFromReference, err := getValueFromReference(log, origPattern, actualPath)
|
||||
if err != nil {
|
||||
return err, nil
|
||||
}
|
||||
|
@ -196,15 +196,15 @@ func formAbsolutePath(referencePath, absolutePath string) string {
|
|||
}
|
||||
|
||||
//Prepares original pattern, path to value, and call traverse function
|
||||
func getValueFromReference(origPattern interface{}, reference string) (interface{}, error) {
|
||||
func getValueFromReference(log logr.Logger, origPattern interface{}, reference string) (interface{}, error) {
|
||||
originalPatternMap := origPattern.(map[string]interface{})
|
||||
reference = reference[1:]
|
||||
statements := strings.Split(reference, "/")
|
||||
|
||||
return getValueFromPattern(originalPatternMap, statements, 0)
|
||||
return getValueFromPattern(log, originalPatternMap, statements, 0)
|
||||
}
|
||||
|
||||
func getValueFromPattern(patternMap map[string]interface{}, keys []string, currentKeyIndex int) (interface{}, error) {
|
||||
func getValueFromPattern(log logr.Logger, patternMap map[string]interface{}, keys []string, currentKeyIndex int) (interface{}, error) {
|
||||
|
||||
for key, pattern := range patternMap {
|
||||
rawKey := getRawKeyIfWrappedWithAttributes(key)
|
||||
|
@ -221,11 +221,11 @@ func getValueFromPattern(patternMap map[string]interface{}, keys []string, curre
|
|||
for i, value := range typedPattern {
|
||||
resourceMap, ok := value.(map[string]interface{})
|
||||
if !ok {
|
||||
glog.V(4).Infof("Pattern and resource have different structures. Expected %T, found %T", pattern, value)
|
||||
log.V(4).Info("Pattern and resource have different structures.", "expected", fmt.Sprintf("%T", pattern), "current", fmt.Sprintf("%T", value))
|
||||
return nil, fmt.Errorf("Validation rule failed, resource does not have expected pattern %v", patternMap)
|
||||
}
|
||||
if keys[currentKeyIndex+1] == strconv.Itoa(i) {
|
||||
return getValueFromPattern(resourceMap, keys, currentKeyIndex+2)
|
||||
return getValueFromPattern(log, resourceMap, keys, currentKeyIndex+2)
|
||||
}
|
||||
return nil, errors.New("Reference to non-existent place in the document")
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ func getValueFromPattern(patternMap map[string]interface{}, keys []string, curre
|
|||
return nil, errors.New("Reference to non-existent place in the document")
|
||||
case map[string]interface{}:
|
||||
if keys[currentKeyIndex] == rawKey {
|
||||
return getValueFromPattern(typedPattern, keys, currentKeyIndex+1)
|
||||
return getValueFromPattern(log, typedPattern, keys, currentKeyIndex+1)
|
||||
}
|
||||
return nil, errors.New("Reference to non-existent place in the document")
|
||||
case string, float64, int, int64, bool, nil:
|
||||
|
@ -251,12 +251,12 @@ func getValueFromPattern(patternMap map[string]interface{}, keys []string, curre
|
|||
|
||||
// validateArrayOfMaps gets anchors from pattern array map element, applies anchors logic
|
||||
// and then validates each map due to the pattern
|
||||
func validateArrayOfMaps(resourceMapArray []interface{}, patternMap map[string]interface{}, originPattern interface{}, path string) (string, error) {
|
||||
func validateArrayOfMaps(log logr.Logger, resourceMapArray []interface{}, patternMap map[string]interface{}, originPattern interface{}, path string) (string, error) {
|
||||
for i, resourceElement := range resourceMapArray {
|
||||
// check the types of resource element
|
||||
// expect it to be map, but can be anything ?:(
|
||||
currentPath := path + strconv.Itoa(i) + "/"
|
||||
returnpath, err := validateResourceElement(resourceElement, patternMap, originPattern, currentPath)
|
||||
returnpath, err := validateResourceElement(log, resourceElement, patternMap, originPattern, currentPath)
|
||||
if err != nil {
|
||||
return returnpath, err
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
@ -559,7 +560,7 @@ func TestValidateMap_livenessProbeIsNull(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
json.Unmarshal(rawMap, &resource)
|
||||
|
||||
path, err := validateMap(resource, pattern, pattern, "/")
|
||||
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -649,7 +650,7 @@ func TestValidateMap_livenessProbeIsMissing(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateMap(resource, pattern, pattern, "/")
|
||||
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -695,7 +696,7 @@ func TestValidateMapElement_TwoElementsInArrayOnePass(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
// assert.Equal(t, path, "/1/object/0/key2/")
|
||||
// assert.NilError(t, err)
|
||||
|
@ -730,7 +731,7 @@ func TestValidateMapElement_OneElementInArrayPass(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -784,7 +785,7 @@ func TestValidateMap_CorrectRelativePathInConfig(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -838,7 +839,7 @@ func TestValidateMap_RelativePathDoesNotExists(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -892,7 +893,7 @@ func TestValidateMap_OnlyAnchorsInPath(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -946,7 +947,7 @@ func TestValidateMap_MalformedReferenceOnlyDolarMark(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -1000,7 +1001,7 @@ func TestValidateMap_RelativePathWithParentheses(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -1054,7 +1055,7 @@ func TestValidateMap_MalformedPath(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -1108,7 +1109,7 @@ func TestValidateMap_AbosolutePathExists(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.Assert(t, err == nil)
|
||||
}
|
||||
|
@ -1149,7 +1150,7 @@ func TestValidateMap_AbsolutePathToMetadata(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.Assert(t, err == nil)
|
||||
}
|
||||
|
@ -1191,7 +1192,7 @@ func TestValidateMap_AbsolutePathToMetadata_fail(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/image/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -1245,7 +1246,7 @@ func TestValidateMap_AbosolutePathDoesNotExists(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -1276,7 +1277,7 @@ func TestActualizePattern_GivenRelativePathThatExists(t *testing.T) {
|
|||
|
||||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
|
||||
pattern, err := actualizePattern(pattern, referencePath, absolutePath)
|
||||
pattern, err := actualizePattern(log.Log, pattern, referencePath, absolutePath)
|
||||
|
||||
assert.Assert(t, err == nil)
|
||||
}
|
||||
|
@ -1347,7 +1348,7 @@ func TestValidateMapElement_OneElementInArrayNotPass(t *testing.T) {
|
|||
json.Unmarshal(rawPattern, &pattern)
|
||||
json.Unmarshal(rawMap, &resource)
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/0/object/0/key2/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
10
pkg/engine/variables/common.go
Normal file
10
pkg/engine/variables/common.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package event
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme"
|
||||
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1"
|
||||
|
@ -34,6 +34,7 @@ type Generator struct {
|
|||
admissionCtrRecorder record.EventRecorder
|
||||
// events generated at namespaced policy controller to process 'generate' rule
|
||||
genPolicyRecorder record.EventRecorder
|
||||
log logr.Logger
|
||||
}
|
||||
|
||||
//Interface to generate event
|
||||
|
@ -42,32 +43,33 @@ type Interface interface {
|
|||
}
|
||||
|
||||
//NewEventGenerator to generate a new event controller
|
||||
func NewEventGenerator(client *client.Client, pInformer kyvernoinformer.ClusterPolicyInformer) *Generator {
|
||||
func NewEventGenerator(client *client.Client, pInformer kyvernoinformer.ClusterPolicyInformer, log logr.Logger) *Generator {
|
||||
|
||||
gen := Generator{
|
||||
client: client,
|
||||
pLister: pInformer.Lister(),
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), eventWorkQueueName),
|
||||
pSynced: pInformer.Informer().HasSynced,
|
||||
policyCtrRecorder: initRecorder(client, PolicyController),
|
||||
admissionCtrRecorder: initRecorder(client, AdmissionController),
|
||||
genPolicyRecorder: initRecorder(client, GeneratePolicyController),
|
||||
policyCtrRecorder: initRecorder(client, PolicyController, log),
|
||||
admissionCtrRecorder: initRecorder(client, AdmissionController, log),
|
||||
genPolicyRecorder: initRecorder(client, GeneratePolicyController, log),
|
||||
log: log,
|
||||
}
|
||||
return &gen
|
||||
}
|
||||
|
||||
func initRecorder(client *client.Client, eventSource Source) record.EventRecorder {
|
||||
func initRecorder(client *client.Client, eventSource Source, log logr.Logger) record.EventRecorder {
|
||||
// Initliaze Event Broadcaster
|
||||
err := scheme.AddToScheme(scheme.Scheme)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err, "failed to add to scheme")
|
||||
return nil
|
||||
}
|
||||
eventBroadcaster := record.NewBroadcaster()
|
||||
eventBroadcaster.StartLogging(glog.V(4).Infof)
|
||||
eventBroadcaster.StartLogging(log.Info)
|
||||
eventInterface, err := client.GetEventsInterface()
|
||||
if err != nil {
|
||||
glog.Error(err) // TODO: add more specific error
|
||||
log.Error(err, "failed to get event interface for logging")
|
||||
return nil
|
||||
}
|
||||
eventBroadcaster.StartRecordingToSink(
|
||||
|
@ -81,11 +83,12 @@ func initRecorder(client *client.Client, eventSource Source) record.EventRecorde
|
|||
|
||||
//Add queues an event for generation
|
||||
func (gen *Generator) Add(infos ...Info) {
|
||||
logger := gen.log
|
||||
for _, info := range infos {
|
||||
if info.Name == "" {
|
||||
// dont create event for resources with generateName
|
||||
// as the name is not generated yet
|
||||
glog.V(4).Infof("received info %v, not creating an event as the resource has not been assigned a name yet", info)
|
||||
logger.V(4).Info("not creating an event as the resource has not been assigned a name yet", "kind", info.Kind, "name", info.Name, "namespace", info.Namespace)
|
||||
continue
|
||||
}
|
||||
gen.queue.Add(info)
|
||||
|
@ -94,12 +97,14 @@ func (gen *Generator) Add(infos ...Info) {
|
|||
|
||||
// Run begins generator
|
||||
func (gen *Generator) Run(workers int, stopCh <-chan struct{}) {
|
||||
logger := gen.log
|
||||
defer utilruntime.HandleCrash()
|
||||
glog.Info("Starting event generator")
|
||||
defer glog.Info("Shutting down event generator")
|
||||
|
||||
logger.Info("start")
|
||||
defer logger.Info("shutting down")
|
||||
|
||||
if !cache.WaitForCacheSync(stopCh, gen.pSynced) {
|
||||
glog.Error("event generator: failed to sync informer cache")
|
||||
logger.Info("failed to sync informer cache")
|
||||
}
|
||||
|
||||
for i := 0; i < workers; i++ {
|
||||
|
@ -114,24 +119,25 @@ func (gen *Generator) runWorker() {
|
|||
}
|
||||
|
||||
func (gen *Generator) handleErr(err error, key interface{}) {
|
||||
logger := gen.log
|
||||
if err == nil {
|
||||
gen.queue.Forget(key)
|
||||
return
|
||||
}
|
||||
// This controller retries if something goes wrong. After that, it stops trying.
|
||||
if gen.queue.NumRequeues(key) < workQueueRetryLimit {
|
||||
glog.Warningf("Error syncing events %v(re-queuing request, the resource might not have been created yet): %v", key, err)
|
||||
logger.Error(err, "Error syncing events;re-queuing request,the resource might not have been created yet", "key", key)
|
||||
// Re-enqueue the key rate limited. Based on the rate limiter on the
|
||||
// queue and the re-enqueue history, the key will be processed later again.
|
||||
gen.queue.AddRateLimited(key)
|
||||
return
|
||||
}
|
||||
gen.queue.Forget(key)
|
||||
glog.Error(err)
|
||||
glog.Warningf("Dropping the key out of the queue: %v", err)
|
||||
logger.Error(err, "dropping the key out of queue", "key", key)
|
||||
}
|
||||
|
||||
func (gen *Generator) processNextWorkItem() bool {
|
||||
logger := gen.log
|
||||
obj, shutdown := gen.queue.Get()
|
||||
if shutdown {
|
||||
return false
|
||||
|
@ -144,7 +150,7 @@ func (gen *Generator) processNextWorkItem() bool {
|
|||
|
||||
if key, ok = obj.(Info); !ok {
|
||||
gen.queue.Forget(obj)
|
||||
glog.Warningf("Expecting type info by got %v\n", obj)
|
||||
logger.Info("Incorrect type; expected type 'info'", "obj", obj)
|
||||
return nil
|
||||
}
|
||||
err := gen.syncHandler(key)
|
||||
|
@ -152,13 +158,14 @@ func (gen *Generator) processNextWorkItem() bool {
|
|||
return nil
|
||||
}(obj)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
logger.Error(err, "failed to process next work item")
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (gen *Generator) syncHandler(key Info) error {
|
||||
logger := gen.log
|
||||
var robj runtime.Object
|
||||
var err error
|
||||
switch key.Kind {
|
||||
|
@ -166,13 +173,13 @@ func (gen *Generator) syncHandler(key Info) error {
|
|||
//TODO: policy is clustered resource so wont need namespace
|
||||
robj, err = gen.pLister.Get(key.Name)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("Error creating event: unable to get policy %s, will retry ", key.Name)
|
||||
logger.Error(err, "failed to get policy", "name", key.Name)
|
||||
return err
|
||||
}
|
||||
default:
|
||||
robj, err = gen.client.GetResource(key.Kind, key.Namespace, key.Name)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("Error creating event: unable to get resource %s/%s/%s, will retry ", key.Kind, key.Namespace, key.Name)
|
||||
logger.Error(err, "failed to get resource", "kind", key.Kind, "name", key.Name, "namespace", key.Namespace)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -192,13 +199,14 @@ func (gen *Generator) syncHandler(key Info) error {
|
|||
case GeneratePolicyController:
|
||||
gen.genPolicyRecorder.Event(robj, eventType, key.Reason, key.Message)
|
||||
default:
|
||||
glog.Info("info.source not defined for the event generator request")
|
||||
logger.Info("info.source not defined for the request")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//NewEvent builds a event creation request
|
||||
func NewEvent(
|
||||
log logr.Logger,
|
||||
rkind,
|
||||
rapiVersion,
|
||||
rnamespace,
|
||||
|
@ -209,7 +217,7 @@ func NewEvent(
|
|||
args ...interface{}) Info {
|
||||
msgText, err := getEventMsg(message, args...)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err, "failed to get event message")
|
||||
}
|
||||
return Info{
|
||||
Kind: rkind,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 : {{<variable_name}}
|
||||
// - if there is variables that are not defined the context -> results in error and rule is not applied
|
||||
// - valid variables are replaced with the values
|
||||
if _, err := variables.SubstituteVars(ctx, genUnst.Object); err != nil {
|
||||
if _, err := variables.SubstituteVars(log, ctx, genUnst.Object); err != nil {
|
||||
return noGenResource, err
|
||||
}
|
||||
genKind, _, err := unstructured.NestedString(genUnst.Object, "kind")
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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...)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@ import (
|
|||
|
||||
policy2 "github.com/nirmata/kyverno/pkg/policy"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
|
@ -32,6 +30,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 {
|
||||
|
@ -49,7 +48,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")
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +68,7 @@ func Command() *cobra.Command {
|
|||
}
|
||||
|
||||
for _, policy := range policies {
|
||||
err := policy2.Validate(*policy)
|
||||
err := policy2.Validate(*policy, nil, true)
|
||||
if err != nil {
|
||||
return sanitizedError.New(fmt.Sprintf("Policy %v is not valid", policy.Name))
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -9,13 +9,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 {
|
||||
|
@ -27,7 +26,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")
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +42,7 @@ func Command() *cobra.Command {
|
|||
}
|
||||
|
||||
for _, policy := range policies {
|
||||
err = policyvalidate.Validate(*policy)
|
||||
err = policyvalidate.Validate(*policy, nil, true)
|
||||
if err != nil {
|
||||
fmt.Println("Policy " + policy.Name + " is invalid")
|
||||
} else {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
for i := 0; i < workers; i++ {
|
||||
|
@ -64,7 +63,7 @@ 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
|
||||
}
|
||||
|
||||
|
@ -91,7 +90,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
|
||||
}
|
||||
|
||||
|
@ -101,7 +100,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
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@ import (
|
|||
|
||||
data "github.com/nirmata/kyverno/api"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
|
@ -19,6 +17,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"
|
||||
)
|
||||
|
@ -69,7 +68,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}
|
||||
|
|
61
pkg/policy/actions.go
Normal file
61
pkg/policy/actions.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
@ -35,10 +35,10 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure
|
|||
ctx.AddResource(transformResource(resource))
|
||||
|
||||
//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 +48,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 +105,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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
85
pkg/policy/common/common.go
Normal file
85
pkg/policy/common/common.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
74
pkg/policy/generate/auth.go
Normal file
74
pkg/policy/generate/auth.go
Normal file
|
@ -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
|
||||
}
|
21
pkg/policy/generate/fake.go
Normal file
21
pkg/policy/generate/fake.go
Normal file
|
@ -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
|
||||
}
|
31
pkg/policy/generate/fake/auth.go
Normal file
31
pkg/policy/generate/fake/auth.go
Normal file
|
@ -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
|
||||
}
|
151
pkg/policy/generate/validate.go
Normal file
151
pkg/policy/generate/validate.go
Normal file
|
@ -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
|
||||
}
|
89
pkg/policy/generate/validate_test.go
Normal file
89
pkg/policy/generate/validate_test.go
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
63
pkg/policy/mutate/validate.go
Normal file
63
pkg/policy/mutate/validate.go
Normal file
|
@ -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)
|
||||
}
|
151
pkg/policy/mutate/validate_test.go
Normal file
151
pkg/policy/mutate/validate_test.go
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 = ""
|
||||
|
|
|
@ -4,14 +4,12 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
@ -19,7 +17,7 @@ import (
|
|||
// Validate does some initial check to verify some conditions
|
||||
// - One operation per rule
|
||||
// - ResourceDescription mandatory checks
|
||||
func Validate(p kyverno.ClusterPolicy) error {
|
||||
func Validate(p kyverno.ClusterPolicy, client *dclient.Client, mock bool) error {
|
||||
if path, err := validateUniqueRuleName(p); err != nil {
|
||||
return fmt.Errorf("path: spec.%s: %v", path, err)
|
||||
}
|
||||
|
@ -52,24 +50,12 @@ func Validate(p kyverno.ClusterPolicy) 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,
|
||||
|
@ -268,193 +254,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
|
||||
}
|
||||
|
|
61
pkg/policy/validate/validate.go
Normal file
61
pkg/policy/validate/validate.go
Normal file
|
@ -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
|
||||
}
|
381
pkg/policy/validate/validate_test.go
Normal file
381
pkg/policy/validate/validate_test.go
Normal file
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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(`
|
||||
{
|
||||
|
@ -736,225 +373,10 @@ func Test_Validate_Policy(t *testing.T) {
|
|||
err := json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = Validate(policy)
|
||||
err = Validate(policy, 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(`
|
||||
{
|
||||
|
@ -1097,7 +519,7 @@ func Test_Validate_ErrorFormat(t *testing.T) {
|
|||
err := json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = Validate(policy)
|
||||
err = Validate(policy, nil, true)
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,7 @@ 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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package userinfo
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
@ -153,9 +152,9 @@ 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)
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
package webhooks_test
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue