mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 10:28:36 +00:00
Fix conflict
Signed-off-by: Yuvraj <yuvraj.yad001@gmail.com>
This commit is contained in:
commit
4fa5a056f3
123 changed files with 2963 additions and 2104 deletions
|
@ -2,6 +2,11 @@ language: go
|
|||
go:
|
||||
- "1.13"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/go-build
|
||||
- $GOPATH/pkg/mod
|
||||
|
||||
# safelist
|
||||
branches:
|
||||
only:
|
||||
|
|
|
@ -1 +1,7 @@
|
|||
See: https://github.com/nirmata/kyverno#contributing
|
||||
# Contributing to Kyverno
|
||||
|
||||
## Code Style
|
||||
|
||||
We follow the community provided standard [code structure](https://github.com/golang-standards/project-layout).
|
||||
|
||||
See : https://github.com/nirmata/kyverno#contributing
|
||||
|
|
|
@ -120,6 +120,7 @@ Refer to a list of curated of ***[sample policies](/samples/README.md)*** that c
|
|||
|
||||
* [Getting Started](documentation/installation.md)
|
||||
* [Writing Policies](documentation/writing-policies.md)
|
||||
* [Selecting Resources](/documentation/writing-policies-match-exclude.md)
|
||||
* [Validate Resources](documentation/writing-policies-validate.md)
|
||||
* [Mutate Resources](documentation/writing-policies-mutate.md)
|
||||
* [Generate Resources](documentation/writing-policies-generate.md)
|
||||
|
@ -170,5 +171,6 @@ See [Milestones](https://github.com/nirmata/kyverno/milestones) and [Issues](htt
|
|||
Thanks for your interest in contributing!
|
||||
|
||||
* Please review and agree to abide with the [Code of Conduct](/CODE_OF_CONDUCT.md) before contributing.
|
||||
* We encourage all contributions and encourage you to read our [contribution guidelines](./CONTRIBUTING.md).
|
||||
* See the [Wiki](https://github.com/nirmata/kyverno/wiki) for developer documentation.
|
||||
* Browse through the [open issues](https://github.com/nirmata/kyverno/issues)
|
||||
|
|
|
@ -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,62 +92,47 @@ 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.")
|
||||
err := flag.Set("logtostderr", "true")
|
||||
if err != nil {
|
||||
glog.Errorf("failed to set flag %v", err)
|
||||
}
|
||||
err = flag.Set("stderrthreshold", "WARNING")
|
||||
if err != nil {
|
||||
glog.Errorf("failed to set flag %v", err)
|
||||
}
|
||||
err = flag.Set("v", "2")
|
||||
if err != nil {
|
||||
glog.Errorf("failed to set flag %v", err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -172,6 +171,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)
|
||||
|
@ -179,10 +179,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
|
||||
}
|
||||
}
|
||||
|
@ -192,6 +192,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
|
||||
|
@ -201,10 +202,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
|
||||
}
|
||||
}
|
||||
|
@ -224,30 +225,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")
|
||||
}
|
||||
|
|
|
@ -109,8 +109,6 @@ spec:
|
|||
type: string
|
||||
exclude:
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
properties:
|
||||
roles:
|
||||
type: array
|
||||
|
|
|
@ -109,8 +109,6 @@ spec:
|
|||
type: string
|
||||
exclude:
|
||||
type: object
|
||||
required:
|
||||
- resources
|
||||
properties:
|
||||
roles:
|
||||
type: array
|
||||
|
|
|
@ -16,6 +16,14 @@ make cli
|
|||
mv ./cmd/cli/kubectl-kyverno/kyverno /usr/local/bin/kyverno
|
||||
```
|
||||
|
||||
## Install via AUR (archlinux)
|
||||
|
||||
You can install the kyverno cli via your favourite AUR helper (e.g. [yay](https://github.com/Jguer/yay))
|
||||
|
||||
```
|
||||
yay -S kyverno-git
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
#### Version
|
||||
|
|
93
documentation/writing-policies-match-exclude.md
Normal file
93
documentation/writing-policies-match-exclude.md
Normal file
|
@ -0,0 +1,93 @@
|
|||
<small>*[documentation](/README.md#documentation) / Writing Policies / Match & Exclude *</small>
|
||||
|
||||
# Match & Exclude
|
||||
|
||||
The `match` and `exclude` filters control which resources policies are applied to.
|
||||
|
||||
The match / exclude clauses have the same structure, and can each contain the following elements:
|
||||
* resources: select resources by name, namespaces, kinds, and label selectors.
|
||||
* subjects: select users, user groups, and service accounts
|
||||
* roles: select namespaced roles
|
||||
* clusterroles: select cluster wide roles
|
||||
|
||||
At least one element must be specified in a `match` block. The `kind` attribute is optional, but if it's not specified the policy rule will only be applicable to metatdata that is common across all resources kinds.
|
||||
|
||||
When Kyverno receives an admission controller request, i.e. a validation or mutation webhook, it first checks to see if the resource and user information matches or should be excluded from processing. If both checks pass, then the rule logic to mutate, validate, or generate resources is applied.
|
||||
|
||||
The following YAML provides an example for a match clause.
|
||||
|
||||
````yaml
|
||||
apiVersion : kyverno.io/v1
|
||||
kind : ClusterPolicy
|
||||
metadata :
|
||||
name : policy
|
||||
spec :
|
||||
# 'enforce' to block resource request if any rules fail
|
||||
# 'audit' to allow resource request on failure of rules, but create policy violations to report them
|
||||
validationFailureAction: enforce
|
||||
# Each policy has a list of rules applied in declaration order
|
||||
rules:
|
||||
# Rules must have a unique name
|
||||
- name: "check-pod-controller-labels"
|
||||
# Each rule matches specific resource described by "match" field.
|
||||
match:
|
||||
resources:
|
||||
kinds: # Required, list of kinds
|
||||
- Deployment
|
||||
- StatefulSet
|
||||
name: "mongo*" # Optional, a resource name is optional. Name supports wildcards (* and ?)
|
||||
namespaces: # Optional, list of namespaces. Supports wildcards (* and ?)
|
||||
- "dev*"
|
||||
- test
|
||||
selector: # Optional, a resource selector is optional. Values support wildcards (* and ?)
|
||||
matchLabels:
|
||||
app: mongodb
|
||||
matchExpressions:
|
||||
- {key: tier, operator: In, values: [database]}
|
||||
# Optional, subjects to be matched
|
||||
subjects:
|
||||
- kind: User
|
||||
name: mary@somecorp.com
|
||||
# Optional, roles to be matched
|
||||
roles:
|
||||
# Optional, clusterroles to be matched
|
||||
clusterroles: cluster-admin
|
||||
|
||||
...
|
||||
|
||||
````
|
||||
|
||||
All`match` and `exclude` element must be satisfied for the resource to be selected as a candidate for the policy rule. In other words, the match and exclude conditions are evaluated using a logical AND operation.
|
||||
|
||||
Here is an example of a rule that matches all pods, excluding pods created by using the `cluster-admin` cluster role.
|
||||
|
||||
````yaml
|
||||
spec:
|
||||
rules:
|
||||
name: "match-pods-except-admin"
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
clusterroles: cluster-admin
|
||||
````
|
||||
|
||||
This rule that matches all pods, excluding pods in the `kube-system` namespace.
|
||||
|
||||
````yaml
|
||||
spec:
|
||||
rules:
|
||||
name: "match-pods-except-admin"
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
namespaces:
|
||||
- "kube-system"
|
||||
````
|
||||
|
||||
|
||||
---
|
||||
<small>*Read Next >> [Validate Resources](/documentation/writing-policies-validate.md)*</small>
|
|
@ -8,59 +8,9 @@ The following picture shows the structure of a Kyverno Policy:
|
|||
|
||||
Each Kyverno policy contains one or more rules. Each rule has a `match` clause, an optional `exclude` clause, and one of a `mutate`, `validate`, or `generate` clause.
|
||||
|
||||
The match / exclude clauses have the same structure, and can contain the following elements:
|
||||
* resources: select resources by name, namespaces, kinds, and label selectors.
|
||||
* subjects: select users, user groups, and service accounts
|
||||
* roles: select namespaced roles
|
||||
* clusterroles: select cluster wide roles
|
||||
|
||||
When Kyverno receives an admission controller request, i.e. a validation or mutation webhook, it first checks to see if the resource and user information matches or should be excluded from processing. If both checks pass, then the rule logic to mutate, validate, or generate resources is applied.
|
||||
|
||||
The following YAML provides an example for a match clause.
|
||||
|
||||
````yaml
|
||||
apiVersion : kyverno.io/v1
|
||||
kind : ClusterPolicy
|
||||
metadata :
|
||||
name : policy
|
||||
spec :
|
||||
# 'enforce' to block resource request if any rules fail
|
||||
# 'audit' to allow resource request on failure of rules, but create policy violations to report them
|
||||
validationFailureAction: enforce
|
||||
# Each policy has a list of rules applied in declaration order
|
||||
rules:
|
||||
# Rules must have a unique name
|
||||
- name: "check-pod-controller-labels"
|
||||
# Each rule matches specific resource described by "match" field.
|
||||
match:
|
||||
resources:
|
||||
kinds: # Required, list of kinds
|
||||
- Deployment
|
||||
- StatefulSet
|
||||
name: "mongo*" # Optional, a resource name is optional. Name supports wildcards (* and ?)
|
||||
namespaces: # Optional, list of namespaces. Supports wildcards (* and ?)
|
||||
- "dev*"
|
||||
- test
|
||||
selector: # Optional, a resource selector is optional. Values support wildcards (* and ?)
|
||||
matchLabels:
|
||||
app: mongodb
|
||||
matchExpressions:
|
||||
- {key: tier, operator: In, values: [database]}
|
||||
# Optional, subjects to be matched
|
||||
subjects:
|
||||
- kind: User
|
||||
name: mary@somecorp.com
|
||||
# Optional, roles to be matched
|
||||
roles:
|
||||
# Optional, clusterroles to be matched
|
||||
clusterroles: cluster-admin
|
||||
|
||||
...
|
||||
|
||||
````
|
||||
|
||||
Each rule can validate, mutate, or generate configurations of matching resources. A rule definition can contain only a single **mutate**, **validate**, or **generate** child node. These actions are applied to the resource in described order: mutation, validation and then generation.
|
||||
Each rule can validate, mutate, or generate configurations of matching resources. A rule definition can contain only a single **mutate**, **validate**, or **generate** child node.
|
||||
|
||||
These actions are applied to the resource in described order: mutation, validation and then generation.
|
||||
|
||||
---
|
||||
<small>*Read Next >> [Validate Resources](/documentation/writing-policies-validate.md)*</small>
|
||||
<small>*Read Next >> [Selecting Resources](/documentation/writing-policies-match-exclude.md)*</small>
|
||||
|
|
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)
|
||||
}
|
||||
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) {
|
||||
|
@ -29,7 +30,8 @@ func TestMeetConditions_NoAnchor(t *testing.T) {
|
|||
err := json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, nil))
|
||||
|
||||
_, err = meetConditions(nil, overlay)
|
||||
|
||||
_, err := meetConditions(log.Log, nil, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -84,7 +86,8 @@ func TestMeetConditions_conditionalAnchorOnMap(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = meetConditions(resource, overlay)
|
||||
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, !reflect.DeepEqual(err, overlayError{}))
|
||||
|
||||
overlayRaw = []byte(`
|
||||
|
@ -105,7 +108,7 @@ func TestMeetConditions_conditionalAnchorOnMap(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, overlayerr := meetConditions(resource, overlay)
|
||||
_, overlayerr := meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -146,7 +149,7 @@ func TestMeetConditions_DifferentTypes(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
// anchor exist
|
||||
_, err = meetConditions(resource, overlay)
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, strings.Contains(err.Error(), "Found anchor on different types of element at path /subsets/"))
|
||||
}
|
||||
|
||||
|
@ -201,7 +204,7 @@ func TestMeetConditions_anchosInSameObject(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = meetConditions(resource, overlay)
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
assert.Error(t, err, "[overlayError:0] Failed validating value 443 with overlay 444")
|
||||
}
|
||||
|
||||
|
@ -256,11 +259,13 @@ func TestMeetConditions_anchorOnPeer(t *testing.T) {
|
|||
|
||||
var resource, overlay interface{}
|
||||
|
||||
|
||||
err := json.Unmarshal(resourceRaw, &resource)
|
||||
assert.NilError(t, err)
|
||||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
_, err = meetConditions(resource, overlay)
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -339,7 +344,11 @@ func TestMeetConditions_anchorsOnMetaAndSpec(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
<<<<<<< HEAD
|
||||
_, err = meetConditions(resource, overlay)
|
||||
=======
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
>>>>>>> 589f8ece47f95aedc4ee0decee8d29cb77b73dd6
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -422,7 +431,11 @@ func TestMeetConditions_anchorsOnPeer_single(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
<<<<<<< HEAD
|
||||
_, err = meetConditions(resource, overlay)
|
||||
=======
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
>>>>>>> 589f8ece47f95aedc4ee0decee8d29cb77b73dd6
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -458,7 +471,11 @@ func TestMeetConditions_anchorsOnPeer_two(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
<<<<<<< HEAD
|
||||
_, err = meetConditions(resource, overlay)
|
||||
=======
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
>>>>>>> 589f8ece47f95aedc4ee0decee8d29cb77b73dd6
|
||||
assert.Error(t, err, "[overlayError:0] Failed validating value true with overlay false")
|
||||
|
||||
overlayRaw = []byte(`{
|
||||
|
@ -488,7 +505,7 @@ func TestMeetConditions_anchorsOnPeer_two(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = meetConditions(resource, overlay)
|
||||
_, err = meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
|
||||
overlayRaw = []byte(`{
|
||||
|
@ -518,7 +535,7 @@ func TestMeetConditions_anchorsOnPeer_two(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = meetConditions(resource, overlay)
|
||||
_, err = meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
}
|
||||
|
||||
|
@ -554,7 +571,11 @@ func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
<<<<<<< HEAD
|
||||
_, err = meetConditions(resource, overlay)
|
||||
=======
|
||||
_, err := meetConditions(log.Log, resource, overlay)
|
||||
>>>>>>> 589f8ece47f95aedc4ee0decee8d29cb77b73dd6
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
|
||||
overlayRaw = []byte(`{
|
||||
|
@ -584,7 +605,7 @@ func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = meetConditions(resource, overlay)
|
||||
_, err = meetConditions(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
|
||||
overlayRaw = []byte(`{
|
||||
|
@ -614,7 +635,7 @@ func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = meetConditions(resource, overlay)
|
||||
_, err = meetConditions(log.Log, resource, overlay)
|
||||
assert.Error(t, err, "[overlayError:0] Failed validating value ENV_VALUE with overlay ENV_VALUE1")
|
||||
}
|
||||
|
||||
|
@ -670,11 +691,18 @@ func TestMeetConditions_AtleastOneExist(t *testing.T) {
|
|||
|
||||
var resource, overlay interface{}
|
||||
|
||||
<<<<<<< HEAD
|
||||
err := json.Unmarshal(resourceRaw, &resource)
|
||||
assert.NilError(t, err)
|
||||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
path, err := meetConditions(resource, overlay)
|
||||
=======
|
||||
json.Unmarshal(resourceRaw, &resource)
|
||||
json.Unmarshal(overlayRaw, &overlay)
|
||||
|
||||
path, err := meetConditions(log.Log, resource, overlay)
|
||||
>>>>>>> 589f8ece47f95aedc4ee0decee8d29cb77b73dd6
|
||||
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) {
|
||||
|
@ -68,7 +69,7 @@ func TestProcessOverlayPatches_NestedListWithAnchor(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, patches != nil)
|
||||
|
||||
|
@ -170,7 +171,7 @@ func TestProcessOverlayPatches_InsertIntoArray(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, patches != nil)
|
||||
|
||||
|
@ -293,7 +294,7 @@ func TestProcessOverlayPatches_TestInsertToArray(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, patches != nil)
|
||||
|
||||
|
@ -378,7 +379,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, len(patches) != 0)
|
||||
|
||||
|
@ -467,7 +468,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, err = processOverlayPatches(resource, overlay)
|
||||
patches, err = processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
assert.Assert(t, len(patches) != 0)
|
||||
|
||||
|
@ -504,7 +505,7 @@ func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, err = processOverlayPatches(resource, overlay)
|
||||
patches, err = processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Error(t, err, "[overlayError:0] Policy not applied, conditions are not met at /spec/template/metadata/labels/app/, [overlayError:0] Failed validating value nginx with overlay nginx1")
|
||||
assert.Assert(t, len(patches) == 0)
|
||||
}
|
||||
|
@ -535,7 +536,7 @@ func TestProcessOverlayPatches_AddingAnchor(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, len(patches) != 0)
|
||||
|
||||
|
@ -622,7 +623,7 @@ func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, len(patches) != 0)
|
||||
|
||||
|
@ -701,7 +702,7 @@ func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, err = processOverlayPatches(resource, overlay)
|
||||
patches, err = processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(err, overlayError{}))
|
||||
assert.Assert(t, len(patches) != 0)
|
||||
|
||||
|
@ -767,7 +768,7 @@ func TestProcessOverlayPatches_anchorOnPeer(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, len(patches) != 0)
|
||||
|
||||
|
@ -825,7 +826,7 @@ func TestProcessOverlayPatches_anchorOnPeer(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, err = processOverlayPatches(resource, overlay)
|
||||
patches, err = processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Error(t, err, "[overlayError:0] Policy not applied, conditions are not met at /subsets/0/ports/0/port/, [overlayError:0] Failed validating value 443 with overlay 444")
|
||||
assert.Assert(t, len(patches) == 0)
|
||||
}
|
||||
|
@ -909,7 +910,7 @@ func TestProcessOverlayPatches_insertWithCondition(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, len(patches) != 0)
|
||||
|
||||
|
@ -1022,7 +1023,7 @@ func TestProcessOverlayPatches_InsertIfNotPresentWithConditions(t *testing.T) {
|
|||
err = json.Unmarshal(overlayRaw, &overlay)
|
||||
assert.NilError(t, err)
|
||||
|
||||
patches, overlayerr := processOverlayPatches(resource, overlay)
|
||||
patches, overlayerr := processOverlayPatches(log.Log, resource, overlay)
|
||||
assert.Assert(t, reflect.DeepEqual(overlayerr, overlayError{}))
|
||||
assert.Assert(t, len(patches) != 0)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
// TODO : SA4004: the surrounding loop is unconditionally terminated (staticcheck)
|
||||
return nil, errors.New("Reference to non-existent place in the document")
|
||||
|
@ -235,7 +235,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:
|
||||
|
@ -253,12 +253,12 @@ func getValueFromPattern(patternMap map[string]interface{}, keys []string, curre
|
|||
|
||||
// validateArrayOfMaps gets anchors from pattern array map element, applies anchors logic
|
||||
// and then validates each map due to the pattern
|
||||
func validateArrayOfMaps(resourceMapArray []interface{}, patternMap map[string]interface{}, originPattern interface{}, path string) (string, error) {
|
||||
func validateArrayOfMaps(log logr.Logger, resourceMapArray []interface{}, patternMap map[string]interface{}, originPattern interface{}, path string) (string, error) {
|
||||
for i, resourceElement := range resourceMapArray {
|
||||
// check the types of resource element
|
||||
// expect it to be map, but can be anything ?:(
|
||||
currentPath := path + strconv.Itoa(i) + "/"
|
||||
returnpath, err := validateResourceElement(resourceElement, patternMap, originPattern, currentPath)
|
||||
returnpath, err := validateResourceElement(log, resourceElement, patternMap, originPattern, currentPath)
|
||||
if err != nil {
|
||||
return returnpath, err
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
@ -560,7 +561,7 @@ func TestValidateMap_livenessProbeIsNull(t *testing.T) {
|
|||
err := json.Unmarshal(rawMap, &resource)
|
||||
assert.NilError(t, err)
|
||||
|
||||
path, err := validateMap(resource, pattern, pattern, "/")
|
||||
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -650,7 +651,7 @@ func TestValidateMap_livenessProbeIsMissing(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateMap(resource, pattern, pattern, "/")
|
||||
path, err := validateMap(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -696,7 +697,7 @@ func TestValidateMapElement_TwoElementsInArrayOnePass(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
// assert.Equal(t, path, "/1/object/0/key2/")
|
||||
// assert.NilError(t, err)
|
||||
|
@ -731,7 +732,7 @@ func TestValidateMapElement_OneElementInArrayPass(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -785,7 +786,7 @@ func TestValidateMap_CorrectRelativePathInConfig(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -839,7 +840,7 @@ func TestValidateMap_RelativePathDoesNotExists(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -893,7 +894,7 @@ func TestValidateMap_OnlyAnchorsInPath(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -947,7 +948,7 @@ func TestValidateMap_MalformedReferenceOnlyDolarMark(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -1001,7 +1002,7 @@ func TestValidateMap_RelativePathWithParentheses(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
@ -1055,7 +1056,7 @@ func TestValidateMap_MalformedPath(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -1109,7 +1110,7 @@ func TestValidateMap_AbosolutePathExists(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.Assert(t, err == nil)
|
||||
}
|
||||
|
@ -1150,7 +1151,7 @@ func TestValidateMap_AbsolutePathToMetadata(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "")
|
||||
assert.Assert(t, err == nil)
|
||||
}
|
||||
|
@ -1192,7 +1193,7 @@ func TestValidateMap_AbsolutePathToMetadata_fail(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/image/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -1246,7 +1247,7 @@ func TestValidateMap_AbosolutePathDoesNotExists(t *testing.T) {
|
|||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
assert.Assert(t, json.Unmarshal(rawMap, &resource))
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/spec/containers/0/resources/requests/memory/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
@ -1277,7 +1278,7 @@ func TestActualizePattern_GivenRelativePathThatExists(t *testing.T) {
|
|||
|
||||
assert.Assert(t, json.Unmarshal(rawPattern, &pattern))
|
||||
|
||||
pattern, err := actualizePattern(pattern, referencePath, absolutePath)
|
||||
pattern, err := actualizePattern(log.Log, pattern, referencePath, absolutePath)
|
||||
|
||||
assert.Assert(t, err == nil)
|
||||
}
|
||||
|
@ -1350,7 +1351,7 @@ func TestValidateMapElement_OneElementInArrayNotPass(t *testing.T) {
|
|||
err = json.Unmarshal(rawMap, &resource)
|
||||
assert.NilError(t, err)
|
||||
|
||||
path, err := validateResourceElement(resource, pattern, pattern, "/")
|
||||
path, err := validateResourceElement(log.Log, resource, pattern, pattern, "/")
|
||||
assert.Equal(t, path, "/0/object/0/key2/")
|
||||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
|
|
@ -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,12 @@ func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructu
|
|||
errorStr = append(errorStr, err.Error())
|
||||
}
|
||||
resp.Success = false
|
||||
resp.Message = fmt.Sprintf("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 {
|
||||
resp.Message = rule.Validation.Message
|
||||
}
|
||||
return resp
|
||||
}
|
||||
}
|
||||
|
|
|
@ -298,7 +298,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) {
|
|||
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||
assert.NilError(t, err)
|
||||
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
|
||||
msgs := []string{"Validation rule 'check-default-namespace' failed. [anyPattern[0] failed; Validation rule failed at '/metadata/namespace/' to validate value '<nil>' with pattern '?*' anyPattern[1] failed; Validation rule failed at '/metadata/namespace/' to validate value '<nil>' with pattern '!default']"}
|
||||
msgs := []string{"A namespace is required"}
|
||||
for index, r := range er.PolicyResponse.Rules {
|
||||
assert.Equal(t, r.Message, msgs[index])
|
||||
}
|
||||
|
@ -1602,5 +1602,5 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter
|
|||
|
||||
// expectedMsg := "Validation error: ; Validation rule test-path-not-exist anyPattern[0] failed at path /spec/template/spec/containers/0/name/. Validation rule test-path-not-exist anyPattern[1] failed at path /spec/template/spec/containers/0/name/."
|
||||
assert.Assert(t, !er.PolicyResponse.Rules[0].Success)
|
||||
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "Validation rule 'test-path-not-exist' failed. [anyPattern[0] failed; Validation rule failed at '/spec/template/spec/containers/0/name/' to validate value 'pod-test-pod' with pattern 'test*' anyPattern[1] failed; Validation rule failed at '/spec/template/spec/containers/0/name/' to validate value 'pod-test-pod' with pattern 'test*']")
|
||||
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "Validation rule 'test-path-not-exist' has failed")
|
||||
}
|
||||
|
|
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
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/nirmata/kyverno/data"
|
||||
|
||||
"github.com/golang/glog"
|
||||
data "github.com/nirmata/kyverno/api"
|
||||
|
||||
"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
|
||||
|
@ -37,10 +37,10 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure
|
|||
glog.Errorf("enable to add transform resource to ctx: %v", err)
|
||||
}
|
||||
//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
|
||||
|
@ -50,52 +50,52 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure
|
|||
//TODO: GENERATION
|
||||
return engineResponses
|
||||
}
|
||||
func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, ctx context.EvalInterface) (response.EngineResponse, error) {
|
||||
func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, ctx context.EvalInterface, log logr.Logger) (response.EngineResponse, error) {
|
||||
|
||||
engineResponse := engine.Mutate(engine.PolicyContext{Policy: policy, NewResource: resource, Context: ctx})
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.V(4).Infof("mutation had errors reporting them")
|
||||
log.V(4).Info("failed to apply mutation rules; reporting them")
|
||||
return engineResponse, nil
|
||||
}
|
||||
// Verify if the JSON pathes returned by the Mutate are already applied to the resource
|
||||
if reflect.DeepEqual(resource, engineResponse.PatchedResource) {
|
||||
// resources matches
|
||||
glog.V(4).Infof("resource %s/%s/%s satisfies policy %s", engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name, engineResponse.PolicyResponse.Policy)
|
||||
log.V(4).Info("resource already satisfys the policy")
|
||||
return engineResponse, nil
|
||||
}
|
||||
return getFailedOverallRuleInfo(resource, engineResponse)
|
||||
return getFailedOverallRuleInfo(resource, engineResponse, log)
|
||||
}
|
||||
|
||||
// getFailedOverallRuleInfo gets detailed info for over-all mutation failure
|
||||
func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse response.EngineResponse) (response.EngineResponse, error) {
|
||||
func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse response.EngineResponse, log logr.Logger) (response.EngineResponse, error) {
|
||||
rawResource, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to marshal resource: %v\n", err)
|
||||
log.Error(err, "faield to marshall resource")
|
||||
return response.EngineResponse{}, err
|
||||
}
|
||||
|
||||
// resource does not match so there was a mutation rule violated
|
||||
for index, rule := range engineResponse.PolicyResponse.Rules {
|
||||
glog.V(4).Infof("veriying if policy %s rule %s was applied before to resource %s/%s/%s", engineResponse.PolicyResponse.Policy, rule.Name, engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
log.V(4).Info("veriying if policy rule was applied before", "rule", rule.Name)
|
||||
if len(rule.Patches) == 0 {
|
||||
continue
|
||||
}
|
||||
patch, err := jsonpatch.DecodePatch(utils.JoinPatches(rule.Patches))
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to decode patch %s: %v", rule.Patches, err)
|
||||
log.Error(err, "failed to decode JSON patch", "patches", rule.Patches)
|
||||
return response.EngineResponse{}, err
|
||||
}
|
||||
|
||||
// apply the patches returned by mutate to the original resource
|
||||
patchedResource, err := patch.Apply(rawResource)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to apply patch %s: %v", rule.Patches, err)
|
||||
log.Error(err, "failed to apply JSON patch", "patches", rule.Patches)
|
||||
return response.EngineResponse{}, err
|
||||
}
|
||||
if !jsonpatch.Equal(patchedResource, rawResource) {
|
||||
glog.V(4).Infof("policy %s rule %s condition not satisfied by existing resource", engineResponse.PolicyResponse.Policy, rule.Name)
|
||||
log.V(4).Info("policy rule conditions not satisfied by resource", "rule", rule.Name)
|
||||
engineResponse.PolicyResponse.Rules[index].Success = false
|
||||
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(rule.Patches))
|
||||
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(rule.Patches, log))
|
||||
}
|
||||
}
|
||||
return engineResponse, nil
|
||||
|
@ -107,14 +107,14 @@ type jsonPatch struct {
|
|||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func extractPatchPath(patches [][]byte) string {
|
||||
func extractPatchPath(patches [][]byte, log logr.Logger) string {
|
||||
var resultPath []string
|
||||
// extract the patch path and value
|
||||
for _, patch := range patches {
|
||||
glog.V(4).Infof("expected json patch not found in resource: %s", string(patch))
|
||||
log.V(4).Info("expected json patch not found in resource", "patch", string(patch))
|
||||
var data jsonPatch
|
||||
if err := json.Unmarshal(patch, &data); err != nil {
|
||||
glog.V(4).Infof("Failed to decode the generated patch %v: Error %v", string(patch), err)
|
||||
log.Error(err, "failed to decode the generate patch", "patch", string(patch))
|
||||
continue
|
||||
}
|
||||
resultPath = append(resultPath, data.Path)
|
||||
|
|
|
@ -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,11 +425,8 @@ func loadPolicy(t *testing.T, path string) *kyverno.ClusterPolicy {
|
|||
}
|
||||
|
||||
func testScenario(t *testing.T, path string) {
|
||||
err := flag.Set("logtostderr", "true")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 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,11 +152,11 @@ func newRoleBinding(name, ns string, subjects []rbacv1.Subject, roles rbacv1.Rol
|
|||
}
|
||||
|
||||
func Test_getRoleRefByRoleBindings(t *testing.T) {
|
||||
flag.Parse()
|
||||
err := flag.Set("logtostderr", "true")
|
||||
assert.Assert(t, err == nil)
|
||||
err = flag.Set("v", "3")
|
||||
assert.Assert(t, err == nil)
|
||||
|
||||
// flag.Parse()
|
||||
// flag.Set("logtostderr", "true")
|
||||
// flag.Set("v", "3")
|
||||
|
||||
list := make([]*rbacv1.RoleBinding, 2)
|
||||
|
||||
list[0] = newRoleBinding("test1", "mynamespace",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue