1
0
Fork 0
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:
Yuvraj 2020-03-27 14:10:21 +05:30
commit 4fa5a056f3
123 changed files with 2963 additions and 2104 deletions

View file

@ -2,6 +2,11 @@ language: go
go:
- "1.13"
cache:
directories:
- $HOME/.cache/go-build
- $GOPATH/pkg/mod
# safelist
branches:
only:

View file

@ -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

View file

@ -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)

View file

@ -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)
}
}

View file

@ -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")
}

View file

@ -109,8 +109,6 @@ spec:
type: string
exclude:
type: object
required:
- resources
properties:
roles:
type: array

View file

@ -109,8 +109,6 @@ spec:
type: string
exclude:
type: object
required:
- resources
properties:
roles:
type: array

View file

@ -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

View 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>

View file

@ -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
View file

@ -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
)

View file

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

108
pkg/auth/auth.go Normal file
View 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
View 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()
// }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
}

View file

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

View file

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

View file

@ -5,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

View file

@ -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
}
}

View file

@ -6,8 +6,9 @@ import (
"sync"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"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 {

View file

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

View file

@ -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)
}

View file

@ -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)
}
}

View file

@ -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 ""
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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)

View file

@ -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

View file

@ -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])

View file

@ -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

View file

@ -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
}

View file

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

View file

@ -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) {

View file

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

View file

@ -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)
}

View file

@ -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
}
}

View file

@ -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")
}

View 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
}

View file

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

View file

@ -6,6 +6,7 @@ import (
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
"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")
}
}

View file

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

View file

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

View file

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

View file

@ -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)

View file

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

View file

@ -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)
}

View file

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

View file

@ -1,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 {

View file

@ -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 {

View file

@ -1,10 +1,9 @@
package generate
import (
"fmt"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1"
@ -57,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)

View file

@ -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

View file

@ -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
}
}

View file

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

View file

@ -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
}

View file

@ -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))
}

View file

@ -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")

View file

@ -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 {

View file

@ -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
}

View file

@ -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
View 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
}

View file

@ -8,7 +8,7 @@ import (
"time"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
@ -19,12 +19,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)

View file

@ -6,6 +6,7 @@ import (
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/variables"
"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)
}
}

View file

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

View file

@ -1,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
}

View file

@ -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

View 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
}

View file

@ -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
}

View file

@ -5,7 +5,7 @@ import (
"sync"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
"github.com/minio/minio/pkg/wildcard"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/config"
@ -19,27 +19,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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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)
}
}

View 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)
}

View 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)
}
}

View file

@ -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
}

View file

@ -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 = ""

View file

@ -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
}

View 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
}

View 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)
}
}

View file

@ -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)
}

View file

@ -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()
}

View file

@ -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")
}
}
}

View file

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

View file

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

View file

@ -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)
}

View file

@ -4,7 +4,7 @@ import (
"fmt"
"reflect"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
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})

View file

@ -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
}

View file

@ -8,7 +8,7 @@ import (
"sync"
"time"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1"
@ -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 {

View file

@ -4,7 +4,7 @@ import (
"fmt"
"reflect"
"github.com/golang/glog"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
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
}

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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