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

Merge pull request from nirmata/release-0.1

Release 0.1
This commit is contained in:
Jim Bugwadia 2019-05-22 20:56:59 -07:00 committed by GitHub
commit 682f64a1ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7881 changed files with 2978753 additions and 449 deletions
.gitignoreREADME.md
client
cmd/kyverno
definitions
documentation
examples
pkg
scripts
test

5
.gitignore vendored
View file

@ -1,9 +1,6 @@
vendor
pkg/client
pkg/apis/policy/v1alpha1/zz_generated.deepcopy.go
certs
kube-policy
Gopkg.lock
Dockerfile
.vscode
gh-pages/public
/cmd/kyverno/kyverno

View file

@ -10,7 +10,7 @@ Kyverno allows cluster adminstrators to manage environment specific configuratio
Kyverno policies are Kubernetes resources that can be written in YAML or JSON. Kyverno policies can validate, mutate, and generate any Kubernetes resources.
Kyverno runs as a [dynamic admission controller](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) in a Kubernetes cluster. Kyverno receives validating and mutating admission webhook HTTP callbacks from the kube-apiserver and applies matching polcies to return results that enforce admission policies or reject requests.
Kyverno runs as a [dynamic admission controller](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) in a Kubernetes cluster. Kyverno receives validating and mutating admission webhook HTTP callbacks from the kube-apiserver and applies matching policies to return results that enforce admission policies or reject requests.
Kyverno policies can match resources using the resource kind, name, and label selectors. Wildcards are supported in names.
@ -25,7 +25,7 @@ Policy enforcement is captured using Kubernetes events. Kyverno also reports pol
This policy requires that all pods have CPU and memory resource requests and limits:
````yaml
apiVersion: policy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: check-cpu-memory
@ -33,7 +33,8 @@ spec:
rules:
- name: check-pod-resources
resource:
kind: Pod
kinds:
- Pod
validate:
message: "CPU and memory resource requests and limits are required"
pattern:
@ -43,12 +44,13 @@ spec:
- name: "*"
resources:
limits:
# '?' requires a value (at least 1 character)
memory: "?"
cpu: "?"
# '?' requires 1 alphanumeric character and '*' means that there can be 0 or more characters.
# Using them togther e.g. '?*' requires at least one character.
memory: "?*"
cpu: "?*"
requests:
memory: "?"
cpu: "?"
memory: "?*"
cpu: "?*"
````
### 2. Mutating resources
@ -56,7 +58,7 @@ spec:
This policy sets the imagePullPolicy to Always if the image tag is latest:
````yaml
apiVersion: policy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: set-image-pull-policy
@ -64,15 +66,18 @@ spec:
rules:
- name: set-image-pull-policy
resource:
kind: Pod
kinds:
- Deployment
mutate:
overlay:
spec:
containers:
# match images which end with :latest
- image: "(*:latest)"
# set the imagePullPolicy to "Always"
imagePullPolicy: "Always"
template:
spec:
containers:
# match images which end with :latest
- (image): "*:latest"
# set the imagePullPolicy to "Always"
imagePullPolicy: "Always"
````
### 3. Generating resources
@ -80,7 +85,7 @@ spec:
This policy sets the Zookeeper and Kafka connection strings for all namespaces with a label key 'kafka'.
````yaml
apiVersion: policy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: "zk-kafka-address"
@ -88,7 +93,8 @@ spec:
rules:
- name: "zk-kafka-address"
resource:
kind : Namespace
kinds:
- Namespace
selector:
matchExpressions:
- {key: kafka, operator: Exists}
@ -104,6 +110,17 @@ spec:
Additional examples are available in [examples](/examples).
## Alternatives
### Open Policy Agent
[Open Policy Agent (OPA)](https://www.openpolicyagent.org/) is a general-purpose policy engine that can be used as a Kubernetes admission controller. It supports a large set of use cases. Policies are written using [Rego](https://www.openpolicyagent.org/docs/latest/how-do-i-write-policies#what-is-rego) a custom query language.
### External configuration management tools
Tools like [Kustomize](https://github.com/kubernetes-sigs/kustomize) can be used to manage variations in configurations outside of clusters. There are several advantages to this approach when used to produce variations of the same base configuration. However, such solutions cannot be used to validate or enforce configurations.
## Status
*Kyverno is under active development and not ready for production use. Key components and policy definitions are likely to change as we complete core features.*
@ -113,23 +130,23 @@ Additional examples are available in [examples](/examples).
* [Getting Started](documentation/installation.md)
* [Writing Policies](documentation/writing-policies.md)
* [Validate](documentation/writing-policies-validate.md)
* [Mutate](documentation/writing-policies-mutate.md)
* [Validate](documentation/writing-policies-validate.md)
* [Generate](documentation/writing-policies-generate.md)
* [Testing Policies](documentation/testing-policies.md)
* [Using kubectl](documentation/testing-policies-kubectl.md)
* [Using the Kyverno CLI](documentation/testing-policies-kyverno-cli.md)
* [Using kubectl](documentation/testing-policies.md#Test-using-kubectl)
* [Using the Kyverno CLI](documentation/testing-policies.md#Test-using-the-Kyverno-CLI)
## Roadmap
Here are some the major features we plan on completing before a 1.0 release:
* Events
* Policy Violations
* Generate any resource
* Conditionals on existing resources
* Extend CLI to operate on cluster resources
* [Events](https://github.com/nirmata/kyverno/issues/14)
* [Policy Violations](https://github.com/nirmata/kyverno/issues/24)
* [Generate any resource](https://github.com/nirmata/kyverno/issues/21)
* [Conditionals on existing resources](https://github.com/nirmata/kyverno/issues/57)
* [Extend CLI to operate on cluster resources ](https://github.com/nirmata/kyverno/issues/25)
## Getting help

View file

@ -46,14 +46,14 @@ func (c *Client) submitAndApproveCertificateRequest(req *certificates.Certificat
if err != nil {
return nil, err
}
csrList, err := c.ListResource(CSR, "")
csrList, err := c.ListResource(CSRs, "")
if err != nil {
return nil, errors.New(fmt.Sprintf("Unable to list existing certificate requests: %v", err))
}
for _, csr := range csrList.Items {
if csr.GetName() == req.ObjectMeta.Name {
err := c.DeleteResouce(CSR, "", csr.GetName())
err := c.DeleteResouce(CSRs, "", csr.GetName())
if err != nil {
return nil, errors.New(fmt.Sprintf("Unable to delete existing certificate request: %v", err))
}
@ -62,7 +62,7 @@ func (c *Client) submitAndApproveCertificateRequest(req *certificates.Certificat
}
}
unstrRes, err := c.CreateResource(CSR, "", req)
unstrRes, err := c.CreateResource(CSRs, "", req)
if err != nil {
return nil, err
}
@ -91,7 +91,7 @@ func (c *Client) fetchCertificateFromRequest(req *certificates.CertificateSignin
// TODO: react of SIGINT and SIGTERM
timeStart := time.Now()
for time.Now().Sub(timeStart) < time.Duration(maxWaitSeconds)*time.Second {
unstrR, err := c.GetResource(CSR, "", req.ObjectMeta.Name)
unstrR, err := c.GetResource(CSRs, "", req.ObjectMeta.Name)
if err != nil {
return nil, err
}
@ -119,7 +119,7 @@ const certificateField string = "certificate"
// Reads the pair of TLS certificate and key from the specified secret.
func (c *Client) ReadTlsPair(props tls.TlsCertificateProps) *tls.TlsPemPair {
name := generateSecretName(props)
unstrSecret, err := c.GetResource(Secret, props.Namespace, name)
unstrSecret, err := c.GetResource(Secrets, props.Namespace, name)
if err != nil {
c.logger.Printf("Unable to get secret %s/%s: %s", props.Namespace, name, err)
return nil
@ -147,7 +147,7 @@ func (c *Client) ReadTlsPair(props tls.TlsCertificateProps) *tls.TlsPemPair {
// Updates existing secret or creates new one.
func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPemPair) error {
name := generateSecretName(props)
unstrSecret, err := c.GetResource(Secret, props.Namespace, name)
unstrSecret, err := c.GetResource(Secrets, props.Namespace, name)
if err == nil {
secret, err := convertToSecret(unstrSecret)
if err != nil {
@ -159,7 +159,7 @@ func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPem
}
secret.Data[certificateField] = pemPair.Certificate
secret.Data[privateKeyField] = pemPair.PrivateKey
_, err = c.UpdateResource(Secret, props.Namespace, secret)
_, err = c.UpdateResource(Secrets, props.Namespace, secret)
if err == nil {
c.logger.Printf("Secret %s is updated", name)
}
@ -181,7 +181,7 @@ func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPem
},
}
_, err := c.CreateResource(Secret, props.Namespace, secret)
_, err := c.CreateResource(Secrets, props.Namespace, secret)
if err == nil {
c.logger.Printf("Secret %s is created", name)
}

View file

@ -4,7 +4,6 @@ import (
"fmt"
"log"
"os"
"strings"
"time"
types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
@ -17,16 +16,21 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/discovery"
"k8s.io/client-go/discovery/cached/memory"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
csrtype "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
event "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
)
type Client struct {
logger *log.Logger
client dynamic.Interface
cachedClient discovery.CachedDiscoveryInterface
logger *log.Logger
clientConfig *rest.Config
kclient *kubernetes.Clientset
}
func NewClient(config *rest.Config, logger *log.Logger) (*Client, error) {
@ -39,10 +43,17 @@ func NewClient(config *rest.Config, logger *log.Logger) (*Client, error) {
logger = log.New(os.Stdout, "Client : ", log.LstdFlags)
}
kclient, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
return &Client{
logger: logger,
client: client,
clientConfig: config,
kclient: kclient,
cachedClient: memory.NewMemCacheClient(kclient.Discovery()),
}, nil
}
@ -62,29 +73,20 @@ func (c *Client) GetKubePolicyDeployment() (*apps.Deployment, error) {
// or generate a kube client value to access the interface
//GetEventsInterface provides typed interface for events
func (c *Client) GetEventsInterface() (event.EventInterface, error) {
kubeClient, err := newKubeClient(c.clientConfig)
if err != nil {
return nil, err
}
return kubeClient.CoreV1().Events(""), nil
return c.kclient.CoreV1().Events(""), nil
}
func (c *Client) GetCSRInterface() (csrtype.CertificateSigningRequestInterface, error) {
kubeClient, err := newKubeClient(c.clientConfig)
if err != nil {
return nil, err
}
return kubeClient.CertificatesV1beta1().CertificateSigningRequests(), nil
return c.kclient.CertificatesV1beta1().CertificateSigningRequests(), nil
}
func (c *Client) getInterface(kind string) dynamic.NamespaceableResourceInterface {
return c.client.Resource(c.getGroupVersionMapper(kind))
func (c *Client) getInterface(resource string) dynamic.NamespaceableResourceInterface {
return c.client.Resource(c.getGroupVersionMapper(resource))
}
func (c *Client) getResourceInterface(kind string, namespace string) dynamic.ResourceInterface {
func (c *Client) getResourceInterface(resource string, namespace string) dynamic.ResourceInterface {
// Get the resource interface
namespaceableInterface := c.getInterface(kind)
namespaceableInterface := c.getInterface(resource)
// Get the namespacable interface
var resourceInteface dynamic.ResourceInterface
if namespace != "" {
@ -96,52 +98,52 @@ func (c *Client) getResourceInterface(kind string, namespace string) dynamic.Res
}
// Keep this a stateful as the resource list will be based on the kubernetes version we connect to
func (c *Client) getGroupVersionMapper(kind string) schema.GroupVersionResource {
//TODO: add checks to see if the kind is supported
//TODO: build the resource list dynamically( by querying the registered resource kinds)
func (c *Client) getGroupVersionMapper(resource string) schema.GroupVersionResource {
//TODO: add checks to see if the resource is supported
//TODO: build the resource list dynamically( by querying the registered resources)
//TODO: the error scenarios
return getGrpVersionMapper(kind, c.clientConfig, false)
return c.getGVR(resource)
}
// GetResource returns the resource in unstructured/json format
func (c *Client) GetResource(kind string, namespace string, name string) (*unstructured.Unstructured, error) {
return c.getResourceInterface(kind, namespace).Get(name, meta.GetOptions{})
func (c *Client) GetResource(resource string, namespace string, name string) (*unstructured.Unstructured, error) {
return c.getResourceInterface(resource, namespace).Get(name, meta.GetOptions{})
}
// ListResource returns the list of resources in unstructured/json format
// Access items using []Items
func (c *Client) ListResource(kind string, namespace string) (*unstructured.UnstructuredList, error) {
return c.getResourceInterface(kind, namespace).List(meta.ListOptions{})
func (c *Client) ListResource(resource string, namespace string) (*unstructured.UnstructuredList, error) {
return c.getResourceInterface(resource, namespace).List(meta.ListOptions{})
}
func (c *Client) DeleteResouce(kind string, namespace string, name string) error {
return c.getResourceInterface(kind, namespace).Delete(name, &meta.DeleteOptions{})
func (c *Client) DeleteResouce(resource string, namespace string, name string) error {
return c.getResourceInterface(resource, namespace).Delete(name, &meta.DeleteOptions{})
}
// CreateResource creates object for the specified kind/namespace
func (c *Client) CreateResource(kind string, namespace string, obj interface{}) (*unstructured.Unstructured, error) {
// CreateResource creates object for the specified resource/namespace
func (c *Client) CreateResource(resource string, namespace string, obj interface{}) (*unstructured.Unstructured, error) {
// convert typed to unstructured obj
if unstructuredObj := convertToUnstructured(obj); unstructuredObj != nil {
return c.getResourceInterface(kind, namespace).Create(unstructuredObj, meta.CreateOptions{})
return c.getResourceInterface(resource, namespace).Create(unstructuredObj, meta.CreateOptions{})
}
return nil, fmt.Errorf("Unable to create resource ")
}
// UpdateResource updates object for the specified kind/namespace
func (c *Client) UpdateResource(kind string, namespace string, obj interface{}) (*unstructured.Unstructured, error) {
// UpdateResource updates object for the specified resource/namespace
func (c *Client) UpdateResource(resource string, namespace string, obj interface{}) (*unstructured.Unstructured, error) {
// convert typed to unstructured obj
if unstructuredObj := convertToUnstructured(obj); unstructuredObj != nil {
return c.getResourceInterface(kind, namespace).Update(unstructuredObj, meta.UpdateOptions{})
return c.getResourceInterface(resource, namespace).Update(unstructuredObj, meta.UpdateOptions{})
}
return nil, fmt.Errorf("Unable to update resource ")
}
// UpdateStatusResource updates the resource "status" subresource
func (c *Client) UpdateStatusResource(kind string, namespace string, obj interface{}) (*unstructured.Unstructured, error) {
func (c *Client) UpdateStatusResource(resource string, namespace string, obj interface{}) (*unstructured.Unstructured, error) {
// convert typed to unstructured obj
if unstructuredObj := convertToUnstructured(obj); unstructuredObj != nil {
return c.getResourceInterface(kind, namespace).UpdateStatus(unstructuredObj, meta.UpdateOptions{})
return c.getResourceInterface(resource, namespace).UpdateStatus(unstructuredObj, meta.UpdateOptions{})
}
return nil, fmt.Errorf("Unable to update resource ")
}
@ -176,19 +178,19 @@ func (c *Client) GenerateSecret(generator types.Generation, namespace string) er
c.logger.Printf("Preparing to create secret %s/%s", namespace, generator.Name)
secret := v1.Secret{}
// if generator.CopyFrom != nil {
c.logger.Printf("Copying data from secret %s/%s", generator.CopyFrom.Namespace, generator.CopyFrom.Name)
// Get configMap resource
unstrSecret, err := c.GetResource(Secret, generator.CopyFrom.Namespace, generator.CopyFrom.Name)
if err != nil {
return err
if generator.CopyFrom != nil {
c.logger.Printf("Copying data from secret %s/%s", generator.CopyFrom.Namespace, generator.CopyFrom.Name)
// Get configMap resource
unstrSecret, err := c.GetResource(Secrets, generator.CopyFrom.Namespace, generator.CopyFrom.Name)
if err != nil {
return err
}
// typed object
secret, err = convertToSecret(unstrSecret)
if err != nil {
return err
}
}
// typed object
secret, err = convertToSecret(unstrSecret)
if err != nil {
return err
}
// }
secret.ObjectMeta = meta.ObjectMeta{
Name: generator.Name,
@ -216,20 +218,20 @@ func (c *Client) GenerateConfigMap(generator types.Generation, namespace string)
c.logger.Printf("Preparing to create configmap %s/%s", namespace, generator.Name)
configMap := v1.ConfigMap{}
// if generator.CopyFrom != nil {
c.logger.Printf("Copying data from configmap %s/%s", generator.CopyFrom.Namespace, generator.CopyFrom.Name)
// Get configMap resource
unstrConfigMap, err := c.GetResource("configmaps", generator.CopyFrom.Namespace, generator.CopyFrom.Name)
if err != nil {
return err
}
// typed object
configMap, err = convertToConfigMap(unstrConfigMap)
if err != nil {
return err
if generator.CopyFrom != nil {
c.logger.Printf("Copying data from configmap %s/%s", generator.CopyFrom.Namespace, generator.CopyFrom.Name)
// Get configMap resource
unstrConfigMap, err := c.GetResource(ConfigMaps, generator.CopyFrom.Namespace, generator.CopyFrom.Name)
if err != nil {
return err
}
// typed object
configMap, err = convertToConfigMap(unstrConfigMap)
if err != nil {
return err
}
}
// }
configMap.ObjectMeta = meta.ObjectMeta{
Name: generator.Name,
Namespace: namespace,
@ -276,7 +278,7 @@ func convertToCSR(obj *unstructured.Unstructured) (*certificates.CertificateSign
func (c *Client) createConfigMapAfterNamespaceIsCreated(configMap v1.ConfigMap, namespace string) {
err := c.waitUntilNamespaceIsCreated(namespace)
if err == nil {
_, err = c.CreateResource("configmaps", namespace, configMap)
_, err = c.CreateResource(ConfigMaps, namespace, configMap)
}
if err != nil {
c.logger.Printf("Can't create a configmap: %s", err)
@ -286,7 +288,7 @@ func (c *Client) createConfigMapAfterNamespaceIsCreated(configMap v1.ConfigMap,
func (c *Client) createSecretAfterNamespaceIsCreated(secret v1.Secret, namespace string) {
err := c.waitUntilNamespaceIsCreated(namespace)
if err == nil {
_, err = c.CreateResource(Secret, namespace, secret)
_, err = c.CreateResource(Secrets, namespace, secret)
}
if err != nil {
c.logger.Printf("Can't create a secret: %s", err)
@ -299,7 +301,7 @@ func (c *Client) waitUntilNamespaceIsCreated(name string) error {
var lastError error = nil
for time.Now().Sub(timeStart) < namespaceCreationMaxWaitTime {
_, lastError = c.GetResource("namespaces", "", name)
_, lastError = c.GetResource(Namespaces, "", name)
if lastError == nil {
break
}
@ -308,10 +310,24 @@ func (c *Client) waitUntilNamespaceIsCreated(name string) error {
return lastError
}
// KindIsSupported checks if the kind is a registerd GVK
func (c *Client) KindIsSupported(kind string) bool {
kind = strings.ToLower(kind) + "s"
buildGVKMapper(c.clientConfig, false)
_, ok := getValue(kind)
return ok
func (c *Client) getGVR(resource string) schema.GroupVersionResource {
emptyGVR := schema.GroupVersionResource{}
serverresources, err := c.cachedClient.ServerPreferredResources()
if err != nil {
utilruntime.HandleError(err)
return emptyGVR
}
resources, err := discovery.GroupVersionResources(serverresources)
if err != nil {
utilruntime.HandleError(err)
return emptyGVR
}
//TODO using cached client to support cache validation and invalidation
// iterate over the key to compare the resource
for gvr, _ := range resources {
if gvr.Resource == resource {
return gvr
}
}
return emptyGVR
}

View file

@ -1,99 +1,14 @@
package client
import (
"fmt"
"strings"
"time"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
const (
CSR string = "certificatesigningrequests"
Secret string = "secrets"
CSRs string = "certificatesigningrequests"
Secrets string = "secrets"
ConfigMaps string = "configmaps"
Namespaces string = "namespaces"
)
const namespaceCreationMaxWaitTime time.Duration = 30 * time.Second
const namespaceCreationWaitInterval time.Duration = 100 * time.Millisecond
var groupVersionMapper map[string]schema.GroupVersionResource
var kubeClient *kubernetes.Clientset
func getGrpVersionMapper(kind string, clientConfig *rest.Config, refresh bool) schema.GroupVersionResource {
// build the GVK mapper
buildGVKMapper(clientConfig, refresh)
// Query mapper
if val, ok := getValue(kind); ok {
return *val
}
utilruntime.HandleError(fmt.Errorf("Resouce '%s' not registered", kind))
return schema.GroupVersionResource{}
}
func buildGVKMapper(clientConfig *rest.Config, refresh bool) {
if groupVersionMapper == nil || refresh {
groupVersionMapper = make(map[string]schema.GroupVersionResource)
// refresh the mapper
if err := refreshRegisteredResources(groupVersionMapper, clientConfig); err != nil {
utilruntime.HandleError(err)
return
}
}
}
func getValue(kind string) (*schema.GroupVersionResource, bool) {
if groupVersionMapper == nil {
utilruntime.HandleError(fmt.Errorf("GroupVersionKind mapper is not loaded"))
return nil, false
}
if val, ok := groupVersionMapper[kind]; ok {
return &val, true
}
return nil, false
}
func refreshRegisteredResources(mapper map[string]schema.GroupVersionResource, clientConfig *rest.Config) error {
// build kubernetes client
client, err := newKubeClient(clientConfig)
if err != nil {
return err
}
// get registered server groups and resources
_, resourceList, err := client.Discovery().ServerGroupsAndResources()
if err != nil {
return err
}
for _, apiResource := range resourceList {
for _, resource := range apiResource.APIResources {
grpVersion := strings.Split(apiResource.GroupVersion, "/")
if len(grpVersion) == 2 {
mapper[resource.Name] = schema.GroupVersionResource{
Group: grpVersion[0],
Version: grpVersion[1],
Resource: resource.Name,
}
} else {
// resources with only versions
mapper[resource.Name] = schema.GroupVersionResource{
Version: apiResource.GroupVersion,
Resource: resource.Name,
}
}
}
}
return nil
}
func newKubeClient(clientConfig *rest.Config) (*kubernetes.Clientset, error) {
var err error
if kubeClient == nil {
kubeClient, err = kubernetes.NewForConfig(clientConfig)
if err != nil {
return nil, err
}
}
return kubeClient, nil
}

View file

@ -3,14 +3,14 @@
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
name: nirmata-kube-policy-webhook-cfg
name: nirmata-kyverno-webhook-cfg
labels:
app: kube-policy
app: kyverno
webhooks:
- name: webhook.nirmata.kube-policy
- name: webhook.nirmata.kyverno
clientConfig:
service:
name: kube-policy-svc
name: kyverno-svc
namespace: default
path: "/mutate"
caBundle: ${CA_BUNDLE}

View file

@ -3,11 +3,11 @@
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
name: nirmata-kube-policy-webhook-cfg-debug
name: nirmata-kyverno-webhook-cfg-debug
labels:
app: kube-policy
app: kyverno
webhooks:
- name: webhook.nirmata.kube-policy
- name: webhook.nirmata.kyverno
clientConfig:
url: "https://localhost/mutate"
caBundle: ${CA_BUNDLE}

View file

@ -1,9 +1,9 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: policies.kubepolicy.nirmata.io
name: policies.kyverno.io
spec:
group: kubepolicy.nirmata.io
group: kyverno.io
versions:
- name: v1alpha1
served: true
@ -35,29 +35,12 @@ spec:
resource:
type: object
required:
- kind
- kinds
properties:
kind:
type: string
enum:
- ConfigMap
- CronJob
- DaemonSet
- Deployment
- Endpoints
- HorizontalPodAutoscaler
- Ingress
- Job
- LimitRange
- Namespace
- NetworkPolicy
- PersistentVolumeClaim
- PodDisruptionBudget
- PodTemplate
- ResourceQuota
- Secret
- Service
- StatefulSet
kinds:
type: array
items:
type: string
name:
type: string
selector:
@ -143,57 +126,62 @@ spec:
additionalProperties:
type: string
---
kind: Namespace
apiVersion: v1
metadata:
name: "kyverno"
---
apiVersion: v1
kind: Service
metadata:
namespace: kube-system
name: kube-policy-svc
namespace: kyverno
name: kyverno-svc
labels:
app: kube-policy
app: kyverno
spec:
ports:
- port: 443
targetPort: 443
selector:
app: kube-policy
app: kyverno
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: kube-policy-service-account
namespace: kube-system
name: kyverno-service-account
namespace: kyverno
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: kube-policy-admin
name: kyverno-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: kube-policy-service-account
namespace: kube-system
name: kyverno-service-account
namespace: kyverno
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
namespace: kube-system
name: kube-policy-deployment
namespace: kyverno
name: kyverno-deployment
labels:
app: kube-policy
app: kyverno
spec:
replicas: 1
template:
metadata:
labels:
app: kube-policy
app: kyverno
spec:
serviceAccountName: kube-policy-service-account
serviceAccountName: kyverno-service-account
containers:
- name: kube-policy
image: nirmata/kube-policy:latest
- name: kyverno
image: nirmata/kyverno:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 443

View file

@ -1,23 +1,41 @@
<small>*[documentation](/README.md#documentation) / Installation*</small>
# Installation
The controller can be installed and operated in two ways: **Outside the cluster** and **Inside the cluster**. The controller **outside** the cluster is much more convenient to debug and verify changes in its code, so we can call it 'debug mode'. The controller **inside** the cluster is designed for use in the real world, and the **QA testing** should be performed when controller operate in this mode.
To install Kyverno in your cluster run the following command on a host with kubectl access:
````sh
kubectl create -f https://github.com/nirmata/kyverno/raw/master/definitions/install.yaml
````
To check the Kyverno controller status, run the command:
````sh
kubectl get pods -n kyverno
````
If the Kyverno controller is not running, you can check its status and logs for errors:
````sh
kubectl describe pod <kyverno-pod-name> -n kyverno
````
````sh
kubectl logs <kyverno-pod-name> -n kyverno
````
# Installing in a Development Environment
To build and run Kyverno in a development environment see: https://github.com/nirmata/kyverno/wiki/Building
To check if the controller is working, find it in the list of kyverno pods:
`kubectl get pods -n kyverno`
# Try Kyverno without a Kubernetes cluster
The [Kyverno CLI](documentation/testing-policies-cli.md) allows you to write and test policies without installing Kyverno in a Kubernetes cluster.
## Inside the cluster (normal use)
Just execute the command for creating all necesarry resources:
`kubectl create -f definitions/install.yaml`
In this mode controller will get TLS key/certificate pair and loads in-cluster config automatically on start.
To check if the controller is working, find it in the list of kube-system pods:
`kubectl get pods -n kube-system`
The pod with controller contains **'kube-policy'** in its name. The STATUS column will show the health state of the controller. If controller doesn't start, see its logs:
`kubectl describe pod <kube-policy-pod-name> -n kube-system`
or
`kubectl logs <kube-policy-pod-name> -n kube-system`
---
<small>*Read Next >> [Writing Policies](/documentation/writing-policies.md)*</small>

View file

@ -1 +0,0 @@
# Kyverno CLI

View file

@ -1,2 +0,0 @@
# Testing using kubectl

View file

@ -1 +1,62 @@
<small>*[documentation](/README.md#documentation) / Testing Policies*</small>
# Testing Policies
The resources definitions for testing are located in [/test](/test) directory. Each test contains a pair of files: one is the resource definition, and the second is the kyverno policy for this definition.
## Test using kubectl
To do this you should [install kyverno to the cluster](/documentation/installation.md).
For example, to test the simplest kyverno policy for ConfigMap, create the policy and then the resource itself via kubectl:
````bash
cd test/ConfigMap
kubectl create -f policy-CM.yaml
kubectl create -f CM.yaml
````
Then compare the original resource definition in CM.yaml with the actual one:
````bash
kubectl get -f CM.yaml -o yaml
````
## Test using the Kyverno CLI
The Kyverno Command Line Interface (CLI) tool enables writing and testing policies without requiring Kubernetes clusters and without having to apply local policy changes to a cluster.
### Building the CLI
You will need a [Go environment](https://golang.org/doc/install) setup.
1. Clone the Kyverno repo
````bash
git clone https://github.com/nirmata/kyverno/
````
2. Build the CLI
````bash
cd kyverno/cmd/kyverno
go build
````
Or, you can directly build and install the CLI using `go get`:
````bash
go get -u https://github.com/nirmata/kyverno/cmd/kyverno
````
### Using the CLI
To test a policy using the CLI type:
`kyverno <policy> <resource YAML file or folder>`
For example:
```bash
kyverno ../../examples/CLI/policy-deployment.yaml ../../examples/CLI/resources
```
In future releases, the CLI will support complete validation of policies and will allow testing policies against resources in Kubernetes clusters.

View file

@ -1 +1,49 @@
# Policies that Generate Configurations
<small>*[documentation](/README.md#documentation) / [Writing Policies](/documentation/writing-policies.md) / Generate*</small>
# Generate Configurations
```generatate``` feature can be applied to created namespaces to create new resources in them. This feature is useful when every namespace in a cluster must contain some basic required resources. The feature is available for policy rules in which the resource kind is Namespace.
## Example
````yaml
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : basic-policy
spec :
rules:
- name: "Basic confog generator for all namespaces"
resource:
kind: Namespace
generate:
# For now the next kinds are supported:
# ConfigMap
# Secret
- kind: ConfigMap
name: default-config
copyFrom:
namespace: default
name: config-template
data:
DB_ENDPOINT: mongodb://mydomain.ua/db_stage:27017
labels:
purpose: mongo
- kind: Secret
name: mongo-creds
data:
DB_USER: YWJyYWthZGFicmE=
DB_PASSWORD: YXBwc3dvcmQ=
labels:
purpose: mongo
````
In this example, when this policy is applied, any new namespace will receive 2 new resources after its creation:
* ConfigMap copied from default/config-template with added value DB_ENDPOINT.
* Secret with values DB_USER and DB_PASSWORD.
Both resources will contain a label ```purpose: mongo```
---
<small>*Read Next >> [Testing Policies](/documentation/testing-policies.md)*</small>

View file

@ -1 +1,139 @@
# Policies that Mutate Configurations
<small>*[documentation](/README.md#documentation) / [Writing Policies](/documentation/writing-policies.md) / Mutate*</small>
# Mutate Configurations
The ```mutate``` rule contains actions that should be applied to the resource before its creation. Mutation can be made using patches or overlay. Using ```patches``` in the JSONPatch format, you can make point changes to the created resource, and ```overlays``` are designed to bring the resource to the desired view according to a specific pattern.
Resource mutation occurs before validation, so the validation rules should not contradict the changes set in the mutation section.
## Patches
The patches are used to make direct changes in the created resource. In the next example the patch will be applied to all Deployments that contain a word "nirmata" in the name.
````yaml
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-v1
spec :
rules:
- name: "Deployment of *nirmata* images"
resource:
kind: Deployment
# Name is optional. By default validation policy is applicable to any resource of supported kind.
# Name supports wildcards * and ?
name: "*nirmata*"
mutate:
patches:
# This patch adds sidecar container to every deployment that matches this policy
- path: "/spec/template/spec/containers/0/"
op: add
value:
- image: "nirmata.io/sidecar:latest"
imagePullPolicy: "Always"
ports:
- containerPort: 443
````
There is one patch in the rule, it will add the new image to the "containers" list with specified parameters. Patch is described in [JSONPatch](http://jsonpatch.com/) format and support the operations ('op' field):
* **add**
* **replace**
* **remove**
Here is the example with of a patch which removes a label from the secret:
````yaml
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-remove-label
spec :
rules:
- name: "Remove unwanted label"
resource:
# Will be applied to all secrets, because name and selector are not specified
kind: Secret
mutate:
patches:
- path: "/metadata/labels/purpose"
op: remove
````
Note, that if **remove** operation cannot be applied, then this **remove** operation will be skipped with no error.
## Overlay
The Mutation Overlay is the desired form of resource. The existing resource parameters are replaced with the parameters described in the overlay. If there are no such parameters in the target resource, they are copied to the resource from the overlay. The overlay is not used to delete the properties of a resource: use **patches** for this purpose.
The next overlay will add or change the hard limit for memory to 2 gigabytes in every ResourceQuota with label ```quota: low```:
````yaml
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-change-memory-limit
spec :
rules:
- name: "Set hard memory limit to 2Gi"
resource:
# Will be applied to all secrets, because name and selector are not specified
kind: ResourceQuota
selector:
matchLabels:
quota: low
mutate:
overlay:
spec:
hard:
limits.memory: 2Gi
````
The ```overlay``` keyword under ```mutate``` feature describes the desired form of ResourceQuota.
### Working with lists
The application of an overlay to the list without additional settings is pretty straightforward: the new items will be added to the list exсept of those that totally equal to existent items. For example, the next overlay will add IP "192.168.10.172" to all addresses in all Endpoints:
````yaml
apiVersion: policy.nirmata.io/v1alpha1
kind: Policy
metadata:
name: policy-endpoints-
spec:
rules:
- resource:
# Applied to all endpoints
kind : Endpoints
mutate:
overlay:
subsets:
- addresses:
- ip: 192.168.10.172
````
You can use overlays to merge objects inside lists using **anchor** items marked by parentheses. For example, this overlay will add/replace port to 6443 in all ports with name that start from the word "secure":
````yaml
apiVersion : policy.nirmata.io/v1alpha1
kind : Policy
metadata :
name : policy-endpoints-should-be-more-secure
spec :
rules:
- resource:
# Applied to all endpoints
kind : Endpoints
mutate:
overlay:
subsets:
- ports:
- (name): "secure*"
port: 6443
````
The **anchors** marked in parentheses support **wildcards**:
1. `*` - matches zero or more alphanumeric characters
2. `?` - matches a single alphanumeric character
## Details
The behavior of overlays described more detailed in the project's wiki: [Mutation Overlay](https://github.com/nirmata/kyverno/wiki/Mutation-Overlay)
---
<small>*Read Next >> [Validate](/documentation/writing-policies-validate.md)*</small>

View file

@ -1 +1,71 @@
# Policies that Validate Configurations
<small>*[documentation](/README.md#documentation) / [Writing Policies](/documentation/writing-policies.md) / Validate*</small>
# Validate Configurations
A validation rule is expressed as an overlay pattern that expresses the desired configuration. Resource configurations must match fields and expressions defined in the pattern to pass the validation rule. The following rules are followed when processing the overlay pattern:
1. Validation will fail if a field is defined in the pattern and if the field does not exist in the configuration.
2. Undefined fields are treated as wildcards.
3. A validation pattern field with the wildcard value '*' will match zero or more alphanumeric characters. Empty values or missing fields are matched.
4. A validation pattern field with the wildcard value '?' will match any single alphanumeric character. Empty or missing fields are not matched.
5. A validation pattern field with the wildcard value '?*' will match any alphanumeric characters and requires the field to be present with non-empty values.
6. A validation pattern field with the value `null` or "" (empty string) requires that the field not be defined or has no value.
7. The validation of siblings is performed only when one of the field values matches the value defined in the pattern. You can use the parenthesis operator to explictly specify a field value that must be matched. This allows writing rules like 'if fieldA equals X, then fieldB must equal Y'.
8. Validation of child values is only performed if the parent matches the pattern.
## Patterns
### Wildcards
1. `*` - matches zero or more alphanumeric characters
2. `?` - matches a single alphanumeric character
### Operators
| Operator | Meaning |
|------------|---------------------------|
| `>` | greater than |
| `<` | less than |
| `>=` | greater than or equals to |
| `<=` | less than or equals to |
| `!` | not equals |
| \| | logical or |
There is no operator for `equals` as providing a field value in the pattern requires equality to the value.
## Example
The next rule prevents the creation of Deployment, StatefuleSet and DaemonSet resources without label 'app' in selector:
````yaml
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : validation-example
spec :
rules:
- resource:
# Kind specifies one or more resource types to match
kinds:
- Deployment
- StatefuleSet
- DaemonSet
# Name is optional and can use wildcards
name: *
# Selector is optional
selector:
validate:
# Message is optional
message: "The label app is required"
pattern:
spec:
selector:
matchLabels:
app: "?*"
````
Additional examples are available in [examples](/examples/)
---
<small>*Read Next >> [Generate](/documentation/writing-policies-generate.md)*</small>

View file

@ -1 +1,43 @@
<small>*[documentation](/README.md#documentation) / Writing Policies*</small>
# Writing Policies
A Kyverno policy contains a set of rules. Each rule matches resources by kind, name, or selectors.
````yaml
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy
spec :
# Each policy has a list of rules applied in declaration order
rules:
# Rules must have a name
- name: "check-pod-controller-labels"
# Each rule matches specific resource described by "resource" field.
resource:
kind: Deployment, StatefulSet, DaemonSet
# Name is optional. By default validation policy is applicable to any resource of supported kinds.
# Name supports wildcards * and ?
name: "*"
# Selector is optional and can be used to match specific resources
# Selector values support wildcards * and ?
selector:
# A selector can use match
matchLabels:
app: mongodb
matchExpressions:
- {key: tier, operator: In, values: [database]}
# Each rule can contain a single validate, mutate, or generate directive
...
````
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](/documentation/writing-policies-validate.md)*</small>

View file

@ -1,4 +1,4 @@
apiVersion : kubepolicy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-deployment
@ -6,7 +6,8 @@ spec :
rules:
- name: deployment-policy
resource:
kind : Deployment
kinds :
- Deployment
selector :
matchLabels :
cli: test
@ -19,12 +20,12 @@ spec :
op: replace
value: "nginx_is_mutated"
validate:
message: "The imagePullPolicy shoud set to Always"
message: "The imagePullPolicy must be Always when :latest is used as a tag"
pattern:
spec:
template:
spec:
containers:
- (name): "*"
- (name): "*:latest"
imagePullPolicy: Always

View file

@ -1,4 +1,4 @@
apiVersion: policy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
name: check-cpu-memory
@ -6,7 +6,8 @@ spec:
rules:
- name: check-defined
resource:
kind: Deployment
kinds:
- Deployment
validate:
message: "Resource requests and limits are required for CPU and memory"
pattern:
@ -22,7 +23,8 @@ spec:
cpu: "?"
- name: check-memory-in-range
resource:
kind: Deployment
kinds:
- Deployment
validate:
message: "Memory request cannot be greater than 10Gi"
pattern:

View file

@ -1,4 +1,4 @@
apiVersion: policy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
name: check-host-path
@ -6,7 +6,8 @@ spec:
rules:
- name: check-host-path
resource:
kind: Pod
kinds:
- Pod
validate:
message: "Host path volumes are not allowed"
pattern:

View file

@ -1,4 +1,4 @@
apiVersion: policy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
name: image-pull-policy
@ -7,7 +7,8 @@ spec:
- name: image-pull-policy
message: "Image tag ':latest' requires imagePullPolicy 'Always'"
resource:
kind: Deployment
kinds:
- Deployment
overlay:
template:
spec:

View file

@ -1,4 +1,4 @@
apiVersion: policy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
name: check-host-path
@ -6,7 +6,8 @@ spec:
rules:
- name: check-host-path
resource:
kind: Service
kinds:
- Service
validate:
message: "Node port services are not allowed"
pattern:

View file

@ -0,0 +1,17 @@
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : check-non-root
spec :
rules:
- name: check-non-root
resource:
kind: Deployment, StatefuleSet, DaemonSet
validate:
message: "Root user is not allowed"
pattern:
spec:
template:
spec:
securityContext:
runAsNotRoot: true

View file

@ -1,4 +1,4 @@
apiVersion: policy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
name: check-probe-exists
@ -6,7 +6,8 @@ spec:
rules:
- name: check-liveness-probe-exists
resource:
kind: StatefulSet
kinds:
- StatefulSet
validate:
message: "a livenessProbe is required"
pattern:
@ -14,10 +15,11 @@ spec:
# In this case every object in containers list will be checked for pattern
- name: "*"
livenessProbe:
periodSeconds: ?
periodSeconds: "?"
- resource:
kind: Deployment
name: check-readiness-probe-exists
kinds:
- Deployment
name: check-readinessprobe-exists
validate:
message: "a readinessProbe is required"
pattern:
@ -25,4 +27,4 @@ spec:
# In this case every object in containers list will be checked for pattern
- name: "*"
readinessProbe:
periodSeconds: ?
periodSeconds: "?"

View file

@ -1,4 +1,4 @@
apiVersion: policy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
name: check-probe-intervals
@ -6,7 +6,8 @@ spec:
rules:
- name: check-probe-intervals
resource:
kind: Deployment
kinds:
- Deployment
validate:
message: "livenessProbe must be > 10s"
pattern:
@ -16,7 +17,9 @@ spec:
livenessProbe:
periodSeconds: ">10"
- resource:
kind: Deployment
kinds:
- Deployment
name: check-readinessprobe-intervals
validate:
pattern:
message: "readinessProbe must be > 10s"

View file

@ -1,4 +1,4 @@
apiVersion: policy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
name: check-whitelist-registries
@ -7,7 +7,8 @@ spec:
- name: check-whitelist-registries
message: "Registry is not allowed"
resource:
kind: Deployment
kinds:
- Deployment
validate:
pattern:
template:

View file

@ -0,0 +1,20 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: game-config
namespace: default
labels:
originalLabel : isHere
data:
ui.properties : |
color.good=green
color.bad=red
game.properties : |
enemies=predators
lives=3
configmap.data: |
ns=default
labels=originalLabel
labelscount=1

View file

@ -0,0 +1,7 @@
kind: Namespace
apiVersion: v1
metadata:
name: "ns2"
labels:
LabelForSelector : "namespace2"

View file

@ -0,0 +1,34 @@
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: "zk-kafka-address"
spec:
rules:
- name: "copy-comfigmap"
resource :
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
generate :
kind: ConfigMap
name : copied-cm
copyFrom :
namespace : default
name : game-config
data :
secretData: "data from cmg"
- name: "zk-kafka-address"
resource:
kinds:
- Namespace
selector:
matchExpressions:
- {key: LabelForSelector, operator: In, values: [namespace2]}
generate:
kind: ConfigMap
name: zk-kafka-address
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,20 @@
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: set-image-pull-policy
spec:
rules:
- name: set-image-pull-policy
resource:
kinds:
- Deployment
mutate:
overlay:
spec:
template:
spec:
containers:
# match images which end with :latest
- (image): "*:latest"
# set the imagePullPolicy to "Always"
imagePullPolicy: "Always"

View file

@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
- name: ghost
image: ghost:latest

View file

@ -0,0 +1,13 @@
apiVersion: v1
kind: Endpoints
metadata:
name: test-endpoint
labels:
label : test
subsets:
- addresses:
- ip: 192.168.10.171
ports:
- name: secure-connection
port: 443
protocol: TCP

View file

@ -0,0 +1,27 @@
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-endpoints
spec :
rules:
- name: pEP
resource:
kinds :
- Endpoints
selector:
matchLabels:
label : test
mutate:
patches:
- path : "/subsets/0/ports/0/port"
op : replace
value: 9663
- path : "/subsets/0"
op: add
value:
addresses:
- ip: "192.168.10.172"
ports:
- name: load-balancer-connection
port: 80
protocol: UDP

View file

@ -2,5 +2,5 @@ package policy
const (
// GroupName must be the same as specified in Policy CRD
GroupName = "kubepolicy.nirmata.io"
GroupName = "kyverno.io"
)

View file

@ -1,4 +1,4 @@
// +k8s:deepcopy-gen=package
// +groupName=kubepolicy.nirmata.io
// +groupName=kyverno.io
package v1alpha1

View file

@ -33,7 +33,7 @@ type Rule struct {
// ResourceDescription describes the resource to which the PolicyRule will be applied.
type ResourceDescription struct {
Kind string `json:"kind"`
Kinds []string `json:"kinds"`
Name *string `json:"name"`
Selector *metav1.LabelSelector `json:"selector"`
}

View file

@ -22,13 +22,11 @@ func (r *Rule) Validate() error {
}
// Validate checks if all necesarry fields are present and have values. Also checks a Selector.
// Returns error if resource definition is invalid.
// Returns error if
// - kinds is not defined
func (pr *ResourceDescription) Validate() error {
// TBD: selector or name MUST be specified
if pr.Kind == "" {
if len(pr.Kinds) == 0 {
return errors.New("The Kind is not specified")
} else if pr.Name == nil && pr.Selector == nil {
return errors.New("Neither Name nor Selector is specified")
}
if pr.Selector != nil {

View file

@ -9,8 +9,8 @@ import (
var defaultResourceDescriptionName = "defaultResourceDescription"
var defaultResourceDescription = ResourceDescription{
Kind: "Deployment",
Name: &defaultResourceDescriptionName,
Kinds: []string{"Deployment"},
Name: &defaultResourceDescriptionName,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"LabelForSelector": "defaultResourceDescription"},
},
@ -43,7 +43,7 @@ func Test_ResourceDescription_EmptyKind(t *testing.T) {
func Test_ResourceDescription_EmptyNameAndSelector(t *testing.T) {
resourceDescription := ResourceDescription{
Kind: "Deployment",
Kinds: []string{"Deployment"},
}
err := resourceDescription.Validate()
assert.Assert(t, err != nil)

View file

@ -0,0 +1,274 @@
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CopyFrom) DeepCopyInto(out *CopyFrom) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CopyFrom.
func (in *CopyFrom) DeepCopy() *CopyFrom {
if in == nil {
return nil
}
out := new(CopyFrom)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Generation) DeepCopyInto(out *Generation) {
*out = *in
if in.CopyFrom != nil {
in, out := &in.CopyFrom, &out.CopyFrom
*out = new(CopyFrom)
**out = **in
}
if in.Data != nil {
in, out := &in.Data, &out.Data
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Generation.
func (in *Generation) DeepCopy() *Generation {
if in == nil {
return nil
}
out := new(Generation)
in.DeepCopyInto(out)
return out
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mutation.
func (in *Mutation) DeepCopy() *Mutation {
if in == nil {
return nil
}
out := new(Mutation)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Policy) DeepCopyInto(out *Policy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Policy.
func (in *Policy) DeepCopy() *Policy {
if in == nil {
return nil
}
out := new(Policy)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Policy) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PolicyList) DeepCopyInto(out *PolicyList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Policy, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyList.
func (in *PolicyList) DeepCopy() *PolicyList {
if in == nil {
return nil
}
out := new(PolicyList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PolicyList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceDescription) DeepCopyInto(out *ResourceDescription) {
*out = *in
if in.Name != nil {
in, out := &in.Name, &out.Name
*out = new(string)
**out = **in
}
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceDescription.
func (in *ResourceDescription) DeepCopy() *ResourceDescription {
if in == nil {
return nil
}
out := new(ResourceDescription)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Rule) DeepCopyInto(out *Rule) {
*out = *in
in.ResourceDescription.DeepCopyInto(&out.ResourceDescription)
if in.Mutation != nil {
in, out := &in.Mutation, &out.Mutation
*out = (*in).DeepCopy()
}
if in.Validation != nil {
in, out := &in.Validation, &out.Validation
*out = (*in).DeepCopy()
}
if in.Generation != nil {
in, out := &in.Generation, &out.Generation
*out = new(Generation)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
func (in *Rule) DeepCopy() *Rule {
if in == nil {
return nil
}
out := new(Rule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Spec) DeepCopyInto(out *Spec) {
*out = *in
if in.Rules != nil {
in, out := &in.Rules, &out.Rules
*out = make([]Rule, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Spec.
func (in *Spec) DeepCopy() *Spec {
if in == nil {
return nil
}
out := new(Spec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Status) DeepCopyInto(out *Status) {
*out = *in
if in.Violations != nil {
in, out := &in.Violations, &out.Violations
*out = make([]Violation, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Status.
func (in *Status) DeepCopy() *Status {
if in == nil {
return nil
}
out := new(Status)
in.DeepCopyInto(out)
return out
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Validation.
func (in *Validation) DeepCopy() *Validation {
if in == nil {
return nil
}
out := new(Validation)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Violation) DeepCopyInto(out *Violation) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Violation.
func (in *Violation) DeepCopy() *Violation {
if in == nil {
return nil
}
out := new(Violation)
in.DeepCopyInto(out)
return out
}

View file

@ -0,0 +1,90 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package versioned
import (
kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/policy/v1alpha1"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
flowcontrol "k8s.io/client-go/util/flowcontrol"
)
type Interface interface {
Discovery() discovery.DiscoveryInterface
KyvernoV1alpha1() kyvernov1alpha1.KyvernoV1alpha1Interface
}
// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
*discovery.DiscoveryClient
kyvernoV1alpha1 *kyvernov1alpha1.KyvernoV1alpha1Client
}
// KyvernoV1alpha1 retrieves the KyvernoV1alpha1Client
func (c *Clientset) KyvernoV1alpha1() kyvernov1alpha1.KyvernoV1alpha1Interface {
return c.kyvernoV1alpha1
}
// Discovery retrieves the DiscoveryClient
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
if c == nil {
return nil
}
return c.DiscoveryClient
}
// NewForConfig creates a new Clientset for the given config.
func NewForConfig(c *rest.Config) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
}
var cs Clientset
var err error
cs.kyvernoV1alpha1, err = kyvernov1alpha1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
return &cs, nil
}
// NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *Clientset {
var cs Clientset
cs.kyvernoV1alpha1 = kyvernov1alpha1.NewForConfigOrDie(c)
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
return &cs
}
// New creates a new Clientset for the given RESTClient.
func New(c rest.Interface) *Clientset {
var cs Clientset
cs.kyvernoV1alpha1 = kyvernov1alpha1.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
return &cs
}

View file

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated clientset.
package versioned

View file

@ -0,0 +1,77 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
clientset "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/policy/v1alpha1"
fakekyvernov1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
fakediscovery "k8s.io/client-go/discovery/fake"
"k8s.io/client-go/testing"
)
// NewSimpleClientset returns a clientset that will respond with the provided objects.
// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
// without applying any validations and/or defaults. It shouldn't be considered a replacement
// for a real clientset and is mostly useful in simple unit tests.
func NewSimpleClientset(objects ...runtime.Object) *Clientset {
o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
for _, obj := range objects {
if err := o.Add(obj); err != nil {
panic(err)
}
}
cs := &Clientset{}
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) {
gvr := action.GetResource()
ns := action.GetNamespace()
watch, err := o.Watch(gvr, ns)
if err != nil {
return false, nil, err
}
return true, watch, nil
})
return cs
}
// Clientset implements clientset.Interface. Meant to be embedded into a
// struct to get a default implementation. This makes faking out just the method
// you want to test easier.
type Clientset struct {
testing.Fake
discovery *fakediscovery.FakeDiscovery
}
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
return c.discovery
}
var _ clientset.Interface = &Clientset{}
// KyvernoV1alpha1 retrieves the KyvernoV1alpha1Client
func (c *Clientset) KyvernoV1alpha1() kyvernov1alpha1.KyvernoV1alpha1Interface {
return &fakekyvernov1alpha1.FakeKyvernoV1alpha1{Fake: &c.Fake}
}

View file

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated fake clientset.
package fake

View file

@ -0,0 +1,56 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
kyvernov1alpha1.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
var AddToScheme = localSchemeBuilder.AddToScheme
func init() {
v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
utilruntime.Must(AddToScheme(scheme))
}

View file

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package contains the scheme of the automatically generated clientset.
package scheme

View file

@ -0,0 +1,56 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package scheme
import (
kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
var Scheme = runtime.NewScheme()
var Codecs = serializer.NewCodecFactory(Scheme)
var ParameterCodec = runtime.NewParameterCodec(Scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
kyvernov1alpha1.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
var AddToScheme = localSchemeBuilder.AddToScheme
func init() {
v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
utilruntime.Must(AddToScheme(Scheme))
}

View file

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated typed clients.
package v1alpha1

View file

@ -0,0 +1,20 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// Package fake has the automatically generated clients.
package fake

View file

@ -0,0 +1,131 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakePolicies implements PolicyInterface
type FakePolicies struct {
Fake *FakeKyvernoV1alpha1
}
var policiesResource = schema.GroupVersionResource{Group: "kyverno.io", Version: "v1alpha1", Resource: "policies"}
var policiesKind = schema.GroupVersionKind{Group: "kyverno.io", Version: "v1alpha1", Kind: "Policy"}
// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any.
func (c *FakePolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.Policy, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootGetAction(policiesResource, name), &v1alpha1.Policy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.Policy), err
}
// List takes label and field selectors, and returns the list of Policies that match those selectors.
func (c *FakePolicies) List(opts v1.ListOptions) (result *v1alpha1.PolicyList, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootListAction(policiesResource, policiesKind, opts), &v1alpha1.PolicyList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.PolicyList{ListMeta: obj.(*v1alpha1.PolicyList).ListMeta}
for _, item := range obj.(*v1alpha1.PolicyList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested policies.
func (c *FakePolicies) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewRootWatchAction(policiesResource, opts))
}
// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any.
func (c *FakePolicies) Create(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootCreateAction(policiesResource, policy), &v1alpha1.Policy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.Policy), err
}
// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any.
func (c *FakePolicies) Update(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateAction(policiesResource, policy), &v1alpha1.Policy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.Policy), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakePolicies) UpdateStatus(policy *v1alpha1.Policy) (*v1alpha1.Policy, error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateSubresourceAction(policiesResource, "status", policy), &v1alpha1.Policy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.Policy), err
}
// Delete takes name of the policy and deletes it. Returns an error if one occurs.
func (c *FakePolicies) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewRootDeleteAction(policiesResource, name), &v1alpha1.Policy{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakePolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewRootDeleteCollectionAction(policiesResource, listOptions)
_, err := c.Fake.Invokes(action, &v1alpha1.PolicyList{})
return err
}
// Patch applies the patch and returns the patched policy.
func (c *FakePolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootPatchSubresourceAction(policiesResource, name, pt, data, subresources...), &v1alpha1.Policy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.Policy), err
}

View file

@ -0,0 +1,40 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/policy/v1alpha1"
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
)
type FakeKyvernoV1alpha1 struct {
*testing.Fake
}
func (c *FakeKyvernoV1alpha1) Policies() v1alpha1.PolicyInterface {
return &FakePolicies{c}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeKyvernoV1alpha1) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

View file

@ -0,0 +1,21 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
type PolicyExpansion interface{}

View file

@ -0,0 +1,180 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
import (
"time"
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
scheme "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// PoliciesGetter has a method to return a PolicyInterface.
// A group's client should implement this interface.
type PoliciesGetter interface {
Policies() PolicyInterface
}
// PolicyInterface has methods to work with Policy resources.
type PolicyInterface interface {
Create(*v1alpha1.Policy) (*v1alpha1.Policy, error)
Update(*v1alpha1.Policy) (*v1alpha1.Policy, error)
UpdateStatus(*v1alpha1.Policy) (*v1alpha1.Policy, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1alpha1.Policy, error)
List(opts v1.ListOptions) (*v1alpha1.PolicyList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error)
PolicyExpansion
}
// policies implements PolicyInterface
type policies struct {
client rest.Interface
}
// newPolicies returns a Policies
func newPolicies(c *KyvernoV1alpha1Client) *policies {
return &policies{
client: c.RESTClient(),
}
}
// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any.
func (c *policies) Get(name string, options v1.GetOptions) (result *v1alpha1.Policy, err error) {
result = &v1alpha1.Policy{}
err = c.client.Get().
Resource("policies").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of Policies that match those selectors.
func (c *policies) List(opts v1.ListOptions) (result *v1alpha1.PolicyList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.PolicyList{}
err = c.client.Get().
Resource("policies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested policies.
func (c *policies) Watch(opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Resource("policies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any.
func (c *policies) Create(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) {
result = &v1alpha1.Policy{}
err = c.client.Post().
Resource("policies").
Body(policy).
Do().
Into(result)
return
}
// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any.
func (c *policies) Update(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) {
result = &v1alpha1.Policy{}
err = c.client.Put().
Resource("policies").
Name(policy.Name).
Body(policy).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *policies) UpdateStatus(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) {
result = &v1alpha1.Policy{}
err = c.client.Put().
Resource("policies").
Name(policy.Name).
SubResource("status").
Body(policy).
Do().
Into(result)
return
}
// Delete takes name of the policy and deletes it. Returns an error if one occurs.
func (c *policies) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Resource("policies").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *policies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Resource("policies").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched policy.
func (c *policies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) {
result = &v1alpha1.Policy{}
err = c.client.Patch(pt).
Resource("policies").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View file

@ -0,0 +1,90 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
import (
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
"github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
rest "k8s.io/client-go/rest"
)
type KyvernoV1alpha1Interface interface {
RESTClient() rest.Interface
PoliciesGetter
}
// KyvernoV1alpha1Client is used to interact with features provided by the kyverno.io group.
type KyvernoV1alpha1Client struct {
restClient rest.Interface
}
func (c *KyvernoV1alpha1Client) Policies() PolicyInterface {
return newPolicies(c)
}
// NewForConfig creates a new KyvernoV1alpha1Client for the given config.
func NewForConfig(c *rest.Config) (*KyvernoV1alpha1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &KyvernoV1alpha1Client{client}, nil
}
// NewForConfigOrDie creates a new KyvernoV1alpha1Client for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *KyvernoV1alpha1Client {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new KyvernoV1alpha1Client for the given RESTClient.
func New(c rest.Interface) *KyvernoV1alpha1Client {
return &KyvernoV1alpha1Client{c}
}
func setConfigDefaults(config *rest.Config) error {
gv := v1alpha1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *KyvernoV1alpha1Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}

View file

@ -0,0 +1,180 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package externalversions
import (
reflect "reflect"
sync "sync"
time "time"
versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces"
policy "github.com/nirmata/kyverno/pkg/client/informers/externalversions/policy"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
)
// SharedInformerOption defines the functional option type for SharedInformerFactory.
type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory
type sharedInformerFactory struct {
client versioned.Interface
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
lock sync.Mutex
defaultResync time.Duration
customResync map[reflect.Type]time.Duration
informers map[reflect.Type]cache.SharedIndexInformer
// startedInformers is used for tracking which informers have been started.
// This allows Start() to be called multiple times safely.
startedInformers map[reflect.Type]bool
}
// WithCustomResyncConfig sets a custom resync period for the specified informer types.
func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption {
return func(factory *sharedInformerFactory) *sharedInformerFactory {
for k, v := range resyncConfig {
factory.customResync[reflect.TypeOf(k)] = v
}
return factory
}
}
// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory.
func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption {
return func(factory *sharedInformerFactory) *sharedInformerFactory {
factory.tweakListOptions = tweakListOptions
return factory
}
}
// WithNamespace limits the SharedInformerFactory to the specified namespace.
func WithNamespace(namespace string) SharedInformerOption {
return func(factory *sharedInformerFactory) *sharedInformerFactory {
factory.namespace = namespace
return factory
}
}
// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces.
func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {
return NewSharedInformerFactoryWithOptions(client, defaultResync)
}
// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory.
// Listers obtained via this SharedInformerFactory will be subject to the same filters
// as specified here.
// Deprecated: Please use NewSharedInformerFactoryWithOptions instead
func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))
}
// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options.
func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory {
factory := &sharedInformerFactory{
client: client,
namespace: v1.NamespaceAll,
defaultResync: defaultResync,
informers: make(map[reflect.Type]cache.SharedIndexInformer),
startedInformers: make(map[reflect.Type]bool),
customResync: make(map[reflect.Type]time.Duration),
}
// Apply all options
for _, opt := range options {
factory = opt(factory)
}
return factory
}
// Start initializes all requested informers.
func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
f.lock.Lock()
defer f.lock.Unlock()
for informerType, informer := range f.informers {
if !f.startedInformers[informerType] {
go informer.Run(stopCh)
f.startedInformers[informerType] = true
}
}
}
// WaitForCacheSync waits for all started informers' cache were synced.
func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
informers := func() map[reflect.Type]cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informers := map[reflect.Type]cache.SharedIndexInformer{}
for informerType, informer := range f.informers {
if f.startedInformers[informerType] {
informers[informerType] = informer
}
}
return informers
}()
res := map[reflect.Type]bool{}
for informType, informer := range informers {
res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
}
return res
}
// InternalInformerFor returns the SharedIndexInformer for obj using an internal
// client.
func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informerType := reflect.TypeOf(obj)
informer, exists := f.informers[informerType]
if exists {
return informer
}
resyncPeriod, exists := f.customResync[informerType]
if !exists {
resyncPeriod = f.defaultResync
}
informer = newFunc(f.client, resyncPeriod)
f.informers[informerType] = informer
return informer
}
// SharedInformerFactory provides shared informers for resources in all known
// API group versions.
type SharedInformerFactory interface {
internalinterfaces.SharedInformerFactory
ForResource(resource schema.GroupVersionResource) (GenericInformer, error)
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
Kyverno() policy.Interface
}
func (f *sharedInformerFactory) Kyverno() policy.Interface {
return policy.New(f, f.namespace, f.tweakListOptions)
}

View file

@ -0,0 +1,62 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package externalversions
import (
"fmt"
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
)
// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
// sharedInformers based on type
type GenericInformer interface {
Informer() cache.SharedIndexInformer
Lister() cache.GenericLister
}
type genericInformer struct {
informer cache.SharedIndexInformer
resource schema.GroupResource
}
// Informer returns the SharedIndexInformer.
func (f *genericInformer) Informer() cache.SharedIndexInformer {
return f.informer
}
// Lister returns the GenericLister.
func (f *genericInformer) Lister() cache.GenericLister {
return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)
}
// ForResource gives generic access to a shared informer of the matching type
// TODO extend this to unknown resources with a client pool
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
switch resource {
// Group=kyverno.io, Version=v1alpha1
case v1alpha1.SchemeGroupVersion.WithResource("policies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1alpha1().Policies().Informer()}, nil
}
return nil, fmt.Errorf("no informer found for %v", resource)
}

View file

@ -0,0 +1,40 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package internalinterfaces
import (
time "time"
versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
cache "k8s.io/client-go/tools/cache"
)
// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer.
type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer
// SharedInformerFactory a small interface to allow for adding an informer without an import cycle
type SharedInformerFactory interface {
Start(stopCh <-chan struct{})
InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
}
// TweakListOptionsFunc is a function that transforms a v1.ListOptions.
type TweakListOptionsFunc func(*v1.ListOptions)

View file

@ -0,0 +1,46 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package kyverno
import (
internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces"
v1alpha1 "github.com/nirmata/kyverno/pkg/client/informers/externalversions/policy/v1alpha1"
)
// Interface provides access to each of this group's versions.
type Interface interface {
// V1alpha1 provides access to shared informers for resources in V1alpha1.
V1alpha1() v1alpha1.Interface
}
type group struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// V1alpha1 returns a new v1alpha1.Interface.
func (g *group) V1alpha1() v1alpha1.Interface {
return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)
}

View file

@ -0,0 +1,45 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1alpha1
import (
internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to all the informers in this group version.
type Interface interface {
// Policies returns a PolicyInformer.
Policies() PolicyInformer
}
type version struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// Policies returns a PolicyInformer.
func (v *version) Policies() PolicyInformer {
return &policyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
}

View file

@ -0,0 +1,88 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1alpha1
import (
time "time"
policyv1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces"
v1alpha1 "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// PolicyInformer provides access to a shared informer and lister for
// Policies.
type PolicyInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.PolicyLister
}
type policyInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// NewPolicyInformer constructs a new informer for Policy type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredPolicyInformer(client, resyncPeriod, indexers, nil)
}
// NewFilteredPolicyInformer constructs a new informer for Policy type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.KyvernoV1alpha1().Policies().List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.KyvernoV1alpha1().Policies().Watch(options)
},
},
&policyv1alpha1.Policy{},
resyncPeriod,
indexers,
)
}
func (f *policyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredPolicyInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *policyInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&policyv1alpha1.Policy{}, f.defaultInformer)
}
func (f *policyInformer) Lister() v1alpha1.PolicyLister {
return v1alpha1.NewPolicyLister(f.Informer().GetIndexer())
}

View file

@ -0,0 +1,23 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1alpha1
// PolicyListerExpansion allows custom methods to be added to
// PolicyLister.
type PolicyListerExpansion interface{}

View file

@ -0,0 +1,65 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1alpha1
import (
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// PolicyLister helps list Policies.
type PolicyLister interface {
// List lists all Policies in the indexer.
List(selector labels.Selector) (ret []*v1alpha1.Policy, err error)
// Get retrieves the Policy from the index for a given name.
Get(name string) (*v1alpha1.Policy, error)
PolicyListerExpansion
}
// policyLister implements the PolicyLister interface.
type policyLister struct {
indexer cache.Indexer
}
// NewPolicyLister returns a new PolicyLister.
func NewPolicyLister(indexer cache.Indexer) PolicyLister {
return &policyLister{indexer: indexer}
}
// List lists all Policies in the indexer.
func (s *policyLister) List(selector labels.Selector) (ret []*v1alpha1.Policy, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.Policy))
})
return ret, err
}
// Get retrieves the Policy from the index for a given name.
func (s *policyLister) Get(name string) (*v1alpha1.Policy, error) {
obj, exists, err := s.indexer.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1alpha1.Resource("policy"), name)
}
return obj.(*v1alpha1.Policy), nil
}

View file

@ -2,28 +2,28 @@ package config
const (
// These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml
KubePolicyNamespace = "kube-system"
WebhookServiceName = "kube-policy-svc"
KubePolicyNamespace = "kyverno"
WebhookServiceName = "kyverno-svc"
MutatingWebhookConfigurationName = "kube-policy-mutating-webhook-cfg"
MutatingWebhookName = "nirmata.kube-policy.mutating-webhook"
MutatingWebhookConfigurationName = "kyverno-mutating-webhook-cfg"
MutatingWebhookName = "nirmata.kyverno.mutating-webhook"
ValidatingWebhookConfigurationName = "kube-policy-validating-webhook-cfg"
ValidatingWebhookName = "nirmata.kube-policy.validating-webhook"
ValidatingWebhookConfigurationName = "kyverno-validating-webhook-cfg"
ValidatingWebhookName = "nirmata.kyverno.validating-webhook"
// Due to kubernetes issue, we must use next literal constants instead of deployment TypeMeta fields
// Issue: https://github.com/kubernetes/kubernetes/pull/63972
// When the issue is closed, we should use TypeMeta struct instead of this constants
DeploymentKind = "Deployment"
DeploymentAPIVersion = "extensions/v1beta1"
KubePolicyDeploymentName = "kube-policy-deployment"
KubePolicyDeploymentName = "kyverno-deployment"
)
var (
MutatingWebhookServicePath = "/mutate"
ValidatingWebhookServicePath = "/validate"
KubePolicyAppLabels = map[string]string{
"app": "kube-policy",
"app": "kyverno",
}
SupportedKinds = []string{

View file

@ -4,15 +4,14 @@ import (
"log"
kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
"github.com/nirmata/kyverno/pkg/engine/mutation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Mutate performs mutation. Overlay first and then mutation patches
// TODO: return events and violations
func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([]mutation.PatchBytes, []byte) {
var policyPatches []mutation.PatchBytes
var processedPatches []mutation.PatchBytes
func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([]PatchBytes, []byte) {
var policyPatches []PatchBytes
var processedPatches []PatchBytes
var err error
patchedDocument := rawResource
@ -30,7 +29,7 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio
// Process Overlay
if rule.Mutation.Overlay != nil {
overlayPatches, err := mutation.ProcessOverlay(rule.Mutation.Overlay, rawResource)
overlayPatches, err := ProcessOverlay(policy, rawResource, gvk)
if err != nil {
log.Printf("Overlay application has failed for rule %s in policy %s, err: %v\n", rule.Name, policy.ObjectMeta.Name, err)
} else {
@ -41,7 +40,7 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio
// Process Patches
if rule.Mutation.Patches != nil {
processedPatches, patchedDocument, err = mutation.ProcessPatches(rule.Mutation.Patches, patchedDocument)
processedPatches, patchedDocument, err = ProcessPatches(rule.Mutation.Patches, patchedDocument)
if err != nil {
log.Printf("Patches application has failed for rule %s in policy %s, err: %v\n", rule.Name, policy.ObjectMeta.Name, err)
} else {

View file

@ -1,6 +0,0 @@
package mutation
func ProcessOverlay(overlay interface{}, rawResource []byte) ([]PatchBytes, error) {
// TODO: Overlay to be implemented
return nil, nil
}

379
pkg/engine/overlay.go Normal file
View file

@ -0,0 +1,379 @@
package engine
import (
"encoding/json"
"fmt"
"log"
"reflect"
jsonpatch "github.com/evanphx/json-patch"
kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ProcessOverlay handles validating admission request
// Checks the target resourse for rules defined in the policy
func ProcessOverlay(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([]PatchBytes, []byte) {
var resource interface{}
json.Unmarshal(rawResource, &resource)
for _, rule := range policy.Spec.Rules {
if rule.Mutation == nil || rule.Mutation.Overlay == nil {
continue
}
ok := ResourceMeetsDescription(rawResource, rule.ResourceDescription, gvk)
if !ok {
log.Printf("Rule \"%s\" is not applicable to resource\n", rule.Name)
continue
}
overlay := *rule.Mutation.Overlay
if err, _ := applyOverlay(resource, overlay, ""); err != nil {
//return fmt.Errorf("%s: %s", *rule.Validation.Message, err.Error())
}
}
return nil, nil
}
func applyOverlay(resource, overlay interface{}, path string) ([]PatchBytes, error) {
var appliedPatches []PatchBytes
// resource item exists but has different type - replace
// all subtree within this path by overlay
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
patch, err := replaceSubtree(overlay, path)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patch)
return appliedPatches, nil
}
switch typedOverlay := overlay.(type) {
case map[string]interface{}:
typedResource := resource.(map[string]interface{})
for key, value := range typedOverlay {
if wrappedWithParentheses(key) {
key = key[1 : len(key)-1]
}
currentPath := path + key + "/"
resourcePart, ok := typedResource[key]
if ok {
patches, err := applyOverlay(resourcePart, value, currentPath)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patches...)
} else {
patch, err := insertSubtree(value, currentPath)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patch)
}
}
case []interface{}:
typedResource := resource.([]interface{})
patches, err := applyOverlayToArray(typedResource, typedOverlay, path)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patches...)
case string:
patch, err := replaceSubtree(overlay, path)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patch)
case float64:
patch, err := replaceSubtree(overlay, path)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patch)
case int64:
patch, err := replaceSubtree(overlay, path)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patch)
}
return appliedPatches, nil
}
func applyOverlayToArray(resource, overlay []interface{}, path string) ([]PatchBytes, error) {
var appliedPatches []PatchBytes
if len(overlay) == 0 {
return nil, fmt.Errorf("overlay does not support empty arrays")
}
if len(resource) == 0 {
patches, err := fillEmptyArray(overlay, path)
if err != nil {
return nil, err
}
return patches, nil
}
if reflect.TypeOf(resource[0]) != reflect.TypeOf(overlay[0]) {
return nil, fmt.Errorf("overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0])
}
switch overlay[0].(type) {
case map[string]interface{}:
for _, overlayElement := range overlay {
typedOverlay := overlayElement.(map[string]interface{})
anchors := GetAnchorsFromMap(typedOverlay)
currentPath := path + "0/"
for _, resourceElement := range resource {
typedResource := resourceElement.(map[string]interface{})
if len(anchors) > 0 {
if !skipArrayObject(typedResource, anchors) {
patches, err := applyOverlay(resourceElement, overlayElement, currentPath)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patches...)
}
} else {
if hasNestedAnchors(overlayElement) {
patches, err := applyOverlay(resourceElement, overlayElement, currentPath)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patches...)
} else {
patch, err := insertSubtree(overlayElement, currentPath)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patch)
}
}
}
}
default:
path += "0/"
for _, value := range overlay {
patch, err := insertSubtree(value, path)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patch)
}
}
return appliedPatches, nil
}
// In case of empty resource array
// append all non-anchor items to front
func fillEmptyArray(overlay []interface{}, path string) ([]PatchBytes, error) {
var appliedPatches []PatchBytes
if len(overlay) == 0 {
return nil, fmt.Errorf("overlay does not support empty arrays")
}
path += "0/"
switch overlay[0].(type) {
case map[string]interface{}:
for _, overlayElement := range overlay {
typedOverlay := overlayElement.(map[string]interface{})
anchors := GetAnchorsFromMap(typedOverlay)
if len(anchors) == 0 {
patch, err := insertSubtree(overlayElement, path)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patch)
}
}
default:
for _, overlayElement := range overlay {
patch, err := insertSubtree(overlayElement, path)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patch)
}
}
return appliedPatches, nil
}
func skipArrayObject(object, anchors map[string]interface{}) bool {
for key, pattern := range anchors {
key = key[1 : len(key)-1]
value, ok := object[key]
if !ok {
return true
}
if value != pattern {
return true
}
}
return false
}
func insertSubtree(overlay interface{}, path string) ([]byte, error) {
return processSubtree(overlay, path, "add")
}
func replaceSubtree(overlay interface{}, path string) ([]byte, error) {
return processSubtree(overlay, path, "replace")
}
func processSubtree(overlay interface{}, path string, op string) ([]byte, error) {
if len(path) > 1 && path[len(path)-1] == '/' {
path = path[:len(path)-1]
}
if path == "" {
path = "/"
}
value := prepareJSONValue(overlay)
patchStr := fmt.Sprintf(`{ "op": "%s", "path": "%s", "value": %s }`, op, path, value)
// check the patch
_, err := jsonpatch.DecodePatch([]byte("[" + patchStr + "]"))
if err != nil {
return nil, err
}
return []byte(patchStr), nil
}
func prepareJSONValue(overlay interface{}) string {
switch typed := overlay.(type) {
case map[string]interface{}:
if len(typed) == 0 {
return ""
}
if hasOnlyAnchors(overlay) {
return ""
}
result := ""
for key, value := range typed {
jsonValue := prepareJSONValue(value)
pair := fmt.Sprintf(`"%s":%s`, key, jsonValue)
if result != "" {
result += ", "
}
result += pair
}
result = fmt.Sprintf(`{ %s }`, result)
return result
case []interface{}:
if len(typed) == 0 {
return ""
}
if hasOnlyAnchors(overlay) {
return ""
}
result := ""
for _, value := range typed {
jsonValue := prepareJSONValue(value)
if result != "" {
result += ", "
}
result += jsonValue
}
result = fmt.Sprintf(`[ %s ]`, result)
return result
case string:
return fmt.Sprintf(`"%s"`, typed)
case float64:
return fmt.Sprintf("%f", typed)
case int64:
return fmt.Sprintf("%d", typed)
default:
return ""
}
}
func hasOnlyAnchors(overlay interface{}) bool {
switch typed := overlay.(type) {
case map[string]interface{}:
if anchors := GetAnchorsFromMap(typed); len(anchors) == len(typed) {
return true
}
for _, value := range typed {
if !hasOnlyAnchors(value) {
return false
}
}
return true
case string:
return false
case float64:
return false
case int64:
return false
default:
return false
}
}
func hasNestedAnchors(overlay interface{}) bool {
switch typed := overlay.(type) {
case map[string]interface{}:
if anchors := GetAnchorsFromMap(typed); len(anchors) > 0 {
return true
}
for _, value := range typed {
if hasNestedAnchors(value) {
return true
}
}
return false
case string:
return false
case float64:
return false
case int64:
return false
default:
return false
}
}

View file

@ -0,0 +1,62 @@
package engine
import (
"encoding/json"
"testing"
jsonpatch "github.com/evanphx/json-patch"
"gotest.tools/assert"
)
func TestApplyOverlay_NestedListWithAnchor(t *testing.T) {
resourceRaw := []byte(`{ "apiVersion": "v1", "kind": "Endpoints", "metadata": { "name": "test-endpoint", "labels": { "label": "test" } }, "subsets": [ { "addresses": [ { "ip": "192.168.10.171" } ], "ports": [ { "name": "secure-connection", "port": 443, "protocol": "TCP" } ] } ] }`)
overlayRaw := []byte(`{ "subsets": [ { "ports": [ { "(name)": "secure-connection", "port": 444, "protocol": "UDP" } ] } ] }`)
var resource, overlay interface{}
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
patches, err := applyOverlay(resource, overlay, "/")
assert.NilError(t, err)
assert.Assert(t, patches != nil)
patch := JoinPatches(patches)
decoded, err := jsonpatch.DecodePatch(patch)
assert.NilError(t, err)
assert.Assert(t, decoded != nil)
patched, err := decoded.Apply(resourceRaw)
assert.NilError(t, err)
assert.Assert(t, patched != nil)
expectedResult := []byte(`{"apiVersion":"v1","kind":"Endpoints","metadata":{"name":"test-endpoint","labels":{"label":"test"}},"subsets":[{"addresses":[{"ip":"192.168.10.171"}],"ports":[{"name":"secure-connection","port":444.000000,"protocol":"UDP"}]}]}`)
assert.Equal(t, string(expectedResult), string(patched))
}
func TestApplyOverlay_InsertIntoArray(t *testing.T) {
resourceRaw := []byte(`{ "apiVersion": "v1", "kind": "Endpoints", "metadata": { "name": "test-endpoint", "labels": { "label": "test" } }, "subsets": [ { "addresses": [ { "ip": "192.168.10.171" } ], "ports": [ { "name": "secure-connection", "port": 443, "protocol": "TCP" } ] } ] }`)
overlayRaw := []byte(`{ "subsets": [ { "addresses": [ { "ip": "192.168.10.172" }, { "ip": "192.168.10.173" } ], "ports": [ { "name": "insecure-connection", "port": 80, "protocol": "UDP" } ] } ] }`)
var resource, overlay interface{}
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
patches, err := applyOverlay(resource, overlay, "/")
assert.NilError(t, err)
assert.Assert(t, patches != nil)
patch := JoinPatches(patches)
decoded, err := jsonpatch.DecodePatch(patch)
assert.NilError(t, err)
assert.Assert(t, decoded != nil)
patched, err := decoded.Apply(resourceRaw)
assert.NilError(t, err)
assert.Assert(t, patched != nil)
expectedResult := []byte(`{"apiVersion":"v1","kind":"Endpoints","metadata":{"name":"test-endpoint","labels":{"label":"test"}},"subsets":[{"addresses":[{"ip":"192.168.10.172"},{"ip":"192.168.10.173"}],"ports":[{"name":"insecure-connection","port":80.000000,"protocol":"UDP"}]},{"addresses":[{"ip":"192.168.10.171"}],"ports":[{"name":"secure-connection","port":443,"protocol":"TCP"}]}]}`)
assert.Equal(t, string(expectedResult), string(patched))
}

View file

@ -1,4 +1,4 @@
package mutation
package engine
import (
"encoding/json"

View file

@ -1,4 +1,4 @@
package mutation
package engine
import (
"testing"

View file

@ -13,7 +13,7 @@ import (
// ResourceMeetsDescription checks requests kind, name and labels to fit the policy rule
func ResourceMeetsDescription(resourceRaw []byte, description kubepolicy.ResourceDescription, gvk metav1.GroupVersionKind) bool {
if description.Kind != gvk.Kind {
if !findKind(description.Kinds, gvk.Kind) {
return false
}
@ -104,3 +104,24 @@ func ParseRegexPolicyResourceName(policyResourceName string) (string, bool) {
}
return strings.Trim(regex[1], " "), true
}
func GetAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
for key, value := range anchorsMap {
if wrappedWithParentheses(key) {
result[key] = value
}
}
return result
}
func findKind(kinds []string, kindGVK string) bool {
for _, kind := range kinds {
if kind == kindGVK {
return true
}
}
return false
}

View file

@ -96,10 +96,7 @@ func validateArray(resourcePart, patternPart interface{}) error {
switch pattern := patternArray[0].(type) {
case map[string]interface{}:
anchors, err := getAnchorsFromMap(pattern)
if err != nil {
return err
}
anchors := GetAnchorsFromMap(pattern)
for _, value := range resourceArray {
resource, ok := value.(map[string]interface{})
@ -107,7 +104,7 @@ func validateArray(resourcePart, patternPart interface{}) error {
return fmt.Errorf("expected array, found %T", resourcePart)
}
if skipArrayObject(resource, anchors) {
if skipValidatingObject(resource, anchors) {
continue
}
@ -177,19 +174,7 @@ func validateMapElement(resourcePart, patternPart interface{}) error {
return nil
}
func getAnchorsFromMap(pattern map[string]interface{}) (map[string]interface{}, error) {
result := make(map[string]interface{})
for key, value := range pattern {
if wrappedWithParentheses(key) {
result[key] = value
}
}
return result, nil
}
func skipArrayObject(object, anchors map[string]interface{}) bool {
func skipValidatingObject(object, anchors map[string]interface{}) bool {
for key, pattern := range anchors {
key = key[1 : len(key)-1]

View file

@ -306,8 +306,7 @@ func TestGetAnchorsFromMap_ThereAreAnchors(t *testing.T) {
var unmarshalled map[string]interface{}
json.Unmarshal(rawMap, &unmarshalled)
actualMap, err := getAnchorsFromMap(unmarshalled)
assert.NilError(t, err)
actualMap := GetAnchorsFromMap(unmarshalled)
assert.Equal(t, len(actualMap), 2)
assert.Equal(t, actualMap["(name)"].(string), "nirmata-*")
assert.Equal(t, actualMap["(namespace)"].(string), "kube-?olicy")
@ -319,8 +318,7 @@ func TestGetAnchorsFromMap_ThereAreNoAnchors(t *testing.T) {
var unmarshalled map[string]interface{}
json.Unmarshal(rawMap, &unmarshalled)
actualMap, err := getAnchorsFromMap(unmarshalled)
assert.NilError(t, err)
actualMap := GetAnchorsFromMap(unmarshalled)
assert.Assert(t, len(actualMap) == 0)
}
@ -358,7 +356,7 @@ func TestValidateMapElement_OneElementInArrayNotPass(t *testing.T) {
}
func TestValidate_ServiceTest(t *testing.T) {
rawPolicy := []byte(`{ "apiVersion": "kubepolicy.nirmata.io/v1alpha1", "kind": "Policy", "metadata": { "name": "policy-service" }, "spec": { "rules": [ { "name": "ps1", "resource": { "kind": "Service", "name": "game-service*" }, "mutate": { "patches": [ { "path": "/metadata/labels/isMutated", "op": "add", "value": "true" }, { "path": "/metadata/labels/secretLabel", "op": "replace", "value": "weKnow" }, { "path": "/metadata/labels/originalLabel", "op": "remove" }, { "path": "/spec/selector/app", "op": "replace", "value": "mutedApp" } ] }, "validate": { "message": "This resource is broken", "pattern": { "spec": { "ports": [ { "name": "hs", "protocol": 32 } ] } } } } ] } }`)
rawPolicy := []byte(`{ "apiVersion": "kyverno.nirmata.io/v1alpha1", "kind": "Policy", "metadata": { "name": "policy-service" }, "spec": { "rules": [ { "name": "ps1", "resource": { "kind": "Service", "name": "game-service*" }, "mutate": { "patches": [ { "path": "/metadata/labels/isMutated", "op": "add", "value": "true" }, { "path": "/metadata/labels/secretLabel", "op": "replace", "value": "weKnow" }, { "path": "/metadata/labels/originalLabel", "op": "remove" }, { "path": "/spec/selector/app", "op": "replace", "value": "mutedApp" } ] }, "validate": { "message": "This resource is broken", "pattern": { "spec": { "ports": [ { "name": "hs", "protocol": 32 } ] } } } } ] } }`)
rawResource := []byte(`{ "kind": "Service", "apiVersion": "v1", "metadata": { "name": "game-service", "labels": { "originalLabel": "isHere", "secretLabel": "thisIsMySecret" } }, "spec": { "selector": { "app": "MyApp" }, "ports": [ { "name": "http", "protocol": "TCP", "port": 80, "targetPort": 9376 } ] } }`)
var policy kubepolicy.Policy
@ -372,7 +370,7 @@ func TestValidate_ServiceTest(t *testing.T) {
}
func TestValidate_MapHasFloats(t *testing.T) {
rawPolicy := []byte(`{ "apiVersion": "kubepolicy.nirmata.io/v1alpha1", "kind": "Policy", "metadata": { "name": "policy-deployment-changed" }, "spec": { "rules": [ { "name": "First policy v2", "resource": { "kind": "Deployment", "name": "nginx-*" }, "mutate": { "patches": [ { "path": "/metadata/labels/isMutated", "op": "add", "value": "true" }, { "path": "/metadata/labels/app", "op": "replace", "value": "nginx_is_mutated" } ] }, "validate": { "message": "replicas number is wrong", "pattern": { "metadata": { "labels": { "app": "*" } }, "spec": { "replicas": 3 } } } } ] } }`)
rawPolicy := []byte(`{ "apiVersion": "kyverno.nirmata.io/v1alpha1", "kind": "Policy", "metadata": { "name": "policy-deployment-changed" }, "spec": { "rules": [ { "name": "First policy v2", "resource": { "kind": "Deployment", "name": "nginx-*" }, "mutate": { "patches": [ { "path": "/metadata/labels/isMutated", "op": "add", "value": "true" }, { "path": "/metadata/labels/app", "op": "replace", "value": "nginx_is_mutated" } ] }, "validate": { "message": "replicas number is wrong", "pattern": { "metadata": { "labels": { "app": "*" } }, "spec": { "replicas": 3 } } } } ] } }`)
rawResource := []byte(`{ "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "name": "nginx-deployment", "labels": { "app": "nginx" } }, "spec": { "replicas": 3, "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "labels": { "app": "nginx" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx:1.7.9", "ports": [ { "containerPort": 80 } ] } ] } } } }`)
var policy kubepolicy.Policy

View file

@ -82,7 +82,7 @@ func complete(args []string) (*kubepolicy.Policy, []*resourceInfo) {
func applyPolicy(policy *kubepolicy.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]byte, error) {
_, patchedDocument := engine.Mutate(*policy, rawResource, *gvk)
if err := engine.Validate(*policy, rawResource, *gvk); err != nil {
if err := engine.Validate(*policy, patchedDocument, *gvk); err != nil {
return nil, err
}
return patchedDocument, nil

View file

@ -44,7 +44,7 @@ func (si *sharedInfomer) Run(stopCh <-chan struct{}) {
}
func (si *sharedInfomer) getInfomer() infomertypes.PolicyInformer {
return si.policyInformerFactory.Kubepolicy().V1alpha1().Policies()
return si.policyInformerFactory.Kyverno().V1alpha1().Policies()
}
func (si *sharedInfomer) GetInfomer() cache.SharedIndexInformer {
return si.getInfomer().Informer()

View file

@ -90,7 +90,7 @@ func (b *builder) processViolation(info Info) error {
modifiedPolicy.Status.Violations = modifiedViolations
// Violations are part of the status sub resource, so we can use the Update Status api instead of updating the policy object
_, err = b.client.UpdateStatusResource("policies", namespace, modifiedPolicy)
_, err = b.client.UpdateStatusResource("policies/status", namespace, modifiedPolicy)
if err != nil {
return err
}

View file

@ -16,7 +16,6 @@ import (
"github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1"
"github.com/nirmata/kyverno/pkg/config"
engine "github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/mutation"
"github.com/nirmata/kyverno/pkg/sharedinformer"
tlsutils "github.com/nirmata/kyverno/pkg/tls"
v1beta1 "k8s.io/api/admission/v1beta1"
@ -87,13 +86,11 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
admissionReview.Response = &v1beta1.AdmissionResponse{
Allowed: true,
}
if ws.client.KindIsSupported(admissionReview.Request.Kind.Kind) {
switch r.URL.Path {
case config.MutatingWebhookServicePath:
admissionReview.Response = ws.HandleMutation(admissionReview.Request)
case config.ValidatingWebhookServicePath:
admissionReview.Response = ws.HandleValidation(admissionReview.Request)
}
switch r.URL.Path {
case config.MutatingWebhookServicePath:
admissionReview.Response = ws.HandleMutation(admissionReview.Request)
case config.ValidatingWebhookServicePath:
admissionReview.Response = ws.HandleValidation(admissionReview.Request)
}
admissionReview.Response.UID = admissionReview.Request.UID
@ -143,7 +140,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
return nil
}
var allPatches []mutation.PatchBytes
var allPatches []engine.PatchBytes
for _, policy := range policies {
ws.logger.Printf("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules))
@ -160,7 +157,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
patchType := v1beta1.PatchTypeJSONPatch
return &v1beta1.AdmissionResponse{
Allowed: true,
Patch: mutation.JoinPatches(allPatches),
Patch: engine.JoinPatches(allPatches),
PatchType: &patchType,
}
}

View file

@ -7,7 +7,7 @@ Compiles the project to go executable, generates docker image and pushes it to t
### generate-server-cert.sh ###
Generates TLS certificate and key that used by webhook server. Example:
`scripts/generate-server-cert.sh --service=kube-policy-svc --namespace=my_namespace --serverIp=192.168.10.117`
`scripts/generate-server-cert.sh --service=kyverno-svc --namespace=my_namespace --serverIp=192.168.10.117`
* `--service` identifies the service for in-cluster webhook server. Do not specify it if you plan to run webhook server outside the cluster, or cpecify 'localhost' if you want to run controller locally.
* `--namespace` identifies the namespace for in-cluster webhook server. Do not specify it if you plan to run controller locally.
* `--serverIp` is the IP of master node, it can be found in `~/.kube/config`: clusters.cluster[0].server. You should explicitly specify it.
@ -18,7 +18,7 @@ Prepares controller for free (local) or in-cluster use. Uses `generate-server-ce
* `--namespace` - the target namespace to deploy the controller. Do not specify it if you want to depoloy controller locally.
* `--serverIp` means the same as for `generate-server-cert.sh`
Examples:
`scripts/deploy-controller.sh --service=my-kube-policy --namespace=my_namespace --serverIp=192.168.10.117` - deploy controller to the cluster with master node '192.168.10.117' to the namespace 'my_namespace' as a service 'my-kube-policy'
`scripts/deploy-controller.sh --service=my-kyverno --namespace=my_namespace --serverIp=192.168.10.117` - deploy controller to the cluster with master node '192.168.10.117' to the namespace 'my_namespace' as a service 'my-kyverno'
`scripts/deploy-controller.sh --service=localhost --serverIp=192.168.10.117` - deploy controller locally for usage in cluster with mnaster node at '192.168.10.117'
### test-web-hook.sh ###

View file

@ -19,7 +19,7 @@ esac
done
hub_user_name="nirmata"
project_name="kube-policy"
project_name="kyverno"
if [ -z "${service_name}" ]; then
service_name="${project_name}-svc"
@ -40,7 +40,7 @@ if [ -z "${namespace}" ]; then # controller should be launched locally
kubectl delete -f definitions/install.yaml
kubectl create -f definitions/install.yaml || exit 3
echo -e "\n### You can build and run kube-policy project locally.\n### To check its work, run it with parameters -cert, -key and -kubeconfig parameters (see paths of -cert and -key in the log above)."
echo -e "\n### You can build and run kyverno project locally.\n### To check its work, run it with parameters -cert, -key and -kubeconfig parameters (see paths of -cert and -key in the log above)."
else # controller should be launched within a cluster

View file

@ -1,4 +1,4 @@
apiVersion : kubepolicy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-cm
@ -6,7 +6,8 @@ spec :
rules:
- name: pCM1
resource:
kind : ConfigMap
kinds :
- ConfigMap
name: "game-config"
mutate:
patches:
@ -15,7 +16,8 @@ spec :
value : newValue
- name: pCM2
resource:
kind : ConfigMap
kinds :
- ConfigMap
name: "game-config"
mutate:
patches:
@ -26,7 +28,8 @@ spec :
value : "data is replaced"
- name: pCM3
resource:
kind : ConfigMap
kinds :
- ConfigMap
name: "game-config"
mutate:
patches:
@ -40,7 +43,8 @@ spec :
game.properties: "*enemies=aliens*"
- name: pCM4
resource:
kind : ConfigMap
kinds :
- ConfigMap
name: "game-config"
validate:
message: "This CM data is broken because it does not have ui.properties"

View file

@ -1,4 +1,4 @@
apiVersion : kubepolicy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata :
name: "policy-configmapgenerator-test"
@ -6,7 +6,8 @@ spec:
rules:
- name: "copyCM"
resource :
kind : Namespace
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"

View file

@ -3,7 +3,7 @@
# To apply this policy you need to create secret and configMap in "default" namespace
# and then create a namespace
apiVersion : kubepolicy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : "policy-ns-patch-cmg-sg"
@ -11,7 +11,8 @@ spec :
rules:
- name: "patchNamespace2"
resource :
kind : Namespace
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
@ -23,7 +24,8 @@ spec :
- name: "copyCM"
resource :
kind : Namespace
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
@ -38,7 +40,8 @@ spec :
- name: "generateCM"
resource :
kind : Namespace
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace2"
@ -56,7 +59,8 @@ spec :
- name: "generateSecret"
resource :
kind : Namespace
kinds :
- Namespace
name: ns2
generate :
kind: Secret
@ -72,7 +76,8 @@ spec :
- name: "copySecret"
resource :
kind : Namespace
kinds :
- Namespace
name: ns2
generate :
kind: Secret

View file

@ -1,12 +1,13 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: policy-cronjob
spec:
rules:
- name:
- name: pCJ
resource:
kind : CronJob
kinds :
- CronJob
name: "?ell*"
mutate:
patches:

View file

@ -1,4 +1,4 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: policy-daemonset
@ -6,7 +6,8 @@ spec:
rules:
- name: "Patch and Volume validation"
resource:
kind: DaemonSet
kinds:
- DaemonSet
name: fluentd-elasticsearch
mutate:
patches:

View file

@ -1,4 +1,4 @@
apiVersion : kubepolicy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-deployment
@ -6,8 +6,8 @@ spec :
rules:
- name: "First policy v2"
resource:
kind : Deployment
name: nginx-*
kinds :
- Deployment
mutate:
patches:
- path: /metadata/labels/isMutated

View file

@ -1,12 +1,13 @@
apiVersion : kubepolicy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-endpoints
spec :
rules:
- name:
- name: pEP
resource:
kind : Endpoints
kinds :
- Endpoints
selector:
matchLabels:
label : test
@ -19,7 +20,7 @@ spec :
op: add
value:
addresses:
- ip: "192.168.10.171"
- ip: "192.168.10.172"
ports:
- name: load-balancer-connection
port: 80

View file

@ -1,4 +1,4 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: policy-hpa
@ -6,7 +6,8 @@ spec :
rules:
- name: hpa1
resource:
kind : HorizontalPodAutoscaler
kinds :
- HorizontalPodAutoscaler
selector:
matchLabels:
originalLabel: isHere

View file

@ -1,4 +1,4 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata :
name : policy-ingress
@ -6,7 +6,8 @@ spec :
rules:
- name: ingress1
resource:
kind : Ingress
kinds :
- Ingress
selector:
matchLabels:
originalLabel: isHere

View file

@ -1,4 +1,4 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: policy-job-perl-bigint
@ -6,7 +6,8 @@ spec :
rules:
- name: job1
resource:
kind: Job
kinds:
- Job
name: pi
mutate:
patches:

View file

@ -1,12 +1,13 @@
apiVersion : kubepolicy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-limitrange
spec :
rules:
- name:
- name: "rule"
resource:
kind : LimitRange
kinds :
- LimitRange
selector:
matchLabels:
containerSize: minimal

View file

@ -1,4 +1,4 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata :
name : policy-namespace
@ -7,7 +7,8 @@ spec :
rules:
- name: ns1
resource:
kind : Namespace
kinds :
- Namespace
selector:
matchLabels:
LabelForSelector : "namespace"

View file

@ -1,4 +1,4 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: policy-network-policy
@ -6,7 +6,8 @@ spec:
rules:
- name: np1
resource:
kind : NetworkPolicy
kinds :
- NetworkPolicy
selector:
matchLabels:
originalLabel: isHere

View file

@ -1,4 +1,4 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: policy-pvc
@ -6,7 +6,8 @@ spec:
rules:
- name: pvc1
resource:
kind : PersistentVolumeClaim
kinds :
- PersistentVolumeClaim
matchLabels:
originalLabel: isHere
mutate:

View file

@ -1,4 +1,4 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: policy-pdb
@ -6,7 +6,8 @@ spec:
rules:
- name: pdb1
resource:
kind : PodDisruptionBudget
kinds :
- PodDisruptionBudget
name: "game-pdb"
mutate:
patches:

View file

@ -1,4 +1,4 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: test-podtemplate
@ -6,7 +6,8 @@ spec:
rules:
- name: podtemplate1
resource:
kind : PodTemplate
kinds :
- PodTemplate
selector:
matchLabels:
originalLabel: isHere

View file

@ -1,10 +1,15 @@
# Examples
Examples of policies and resources with which you can play to see the kube-policy in action. There are definitions for each supported resource type and an example policy for the corresponding resource.
## How to play
First of all, **build and install the policy controller**: see README file in the project's root.
Each folder contains a pair of files, one of which is the definition of the resource, and the second is the definition of the policy for this resource. Let's look at an example of the endpoints mutation. Endpoints are listed in file `examples/Endpoints/endpoints.yaml`:
# Test samples
```apiVersion: v1
This directory contains policies and resources for testing. There are definitions for each supported resource type and an sample policy for the corresponding resource.
## How to use
Currently, the testing is possible only via ```kubectl``` when kyverno is installed to the cluster. So, [build and install the policy controller](/documentation/installation.md) first.
Each folder contains a pair of files, one of which is the definition of the resource, and the second is the definition of the policy for this resource. Let's look at an example of the endpoints mutation. Endpoints are listed in file `test/Endpoints/endpoints.yaml`:
````yaml
apiVersion: v1
kind: Endpoints
metadata:
name: test-endpoint
@ -17,25 +22,25 @@ subsets:
- name: secure-connection
port: 443
protocol: TCP
```
````
Create this resource:
```
> kubectl create -f examples/Endpoints/endpoints.yaml
````yaml
> kubectl create -f test/Endpoints/endpoints.yaml
endpoints/test-endpoint created
> kubectl get -f examples/Endpoints/endpoints.yaml
> kubectl get -f test/Endpoints/endpoints.yaml
NAME ENDPOINTS AGE
test-endpoint 192.168.10.171:443 6s
```
````
We just created an endpoints resource and made sure that it was created without changes. Let's remove it now and try to create it again, but with an active policy for endpoints resources.
```
> kubectl delete -f test/endpoints.yaml
````bash
> kubectl delete -f test/Endpoints/endpoints.yaml
endpoints "test-endpoint" deleted
```
We have this a policy for enpoints (`examples/Endpoints/policy-endpoint.yaml`):
````
We have this a policy for enpoints ([policy-endpoint.yaml](/test/Endpoints/policy-endpoint.yaml)):
```
apiVersion : kubepolicy.nirmata.io/v1alpha1
````yaml
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-endpoints
@ -43,7 +48,8 @@ spec :
rules:
- name:
resource:
kind : Endpoints
kinds:
- Endpoints
selector:
matchLabels:
label : test
@ -61,22 +67,22 @@ spec :
- name: load-balancer-connection
port: 80
protocol: UDP
```
````
This policy does 2 patches:
- **replaces** the first port of the first connection to 6443
- **adds** new endpoint with IP 192.168.10.171 and port 80 (UDP)
Let's apply this policy and create the endpoints again to see the changes:
```
> kubectl create -f examples/Endpoints/policy-endpoints.yaml
````bash
> kubectl create -f test/Endpoints/policy-endpoints.yaml
policy.policy.nirmata.io/policy-endpoints created
> kubectl create -f examples/Endpoints/endpoints.yaml
> kubectl create -f test/Endpoints/endpoints.yaml
endpoints/test-endpoint created
> kubectl get -f examples/Endpoints/endpoints.yaml
> kubectl get -f test/Endpoints/endpoints.yaml
NAME ENDPOINTS AGE
test-endpoint 192.168.10.171:80,192.168.10.171:9663 30s
```
````
As you can see, the endpoints resource was created with changes: a new port 80 was added, and port 443 was changed to 6443.
**Enjoy :)**

View file

@ -1,12 +1,13 @@
apiVersion : kubepolicy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-quota-low-test-validation
spec :
rules:
- name:
- name: "rule1"
resource:
kind : ResourceQuota
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low
@ -16,9 +17,10 @@ spec :
spec:
hard:
memory: "8Gi|12Gi"
- name:
- name: "rule2"
resource:
kind : ResourceQuota
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low
@ -28,9 +30,10 @@ spec :
spec:
hard:
cpu: <3
- name:
- name: "rule3"
resource:
kind : ResourceQuota
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low

View file

@ -1,12 +1,13 @@
apiVersion : kubepolicy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-quota-low-test
spec :
rules:
- name:
- name: "rule"
resource:
kind : ResourceQuota
kinds :
- ResourceQuota
selector:
matchLabels:
quota: low

View file

@ -1,4 +1,4 @@
apiVersion: kubepolicy.nirmata.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: policy-secrets
@ -6,7 +6,8 @@ spec:
rules:
- name: secret1
resource:
kind : Secret
kinds :
- Secret
name: "mysecret"
mutate:
patches:

View file

@ -1,4 +1,4 @@
apiVersion : kubepolicy.nirmata.io/v1alpha1
apiVersion : kyverno.io/v1alpha1
kind : Policy
metadata :
name : policy-service
@ -6,7 +6,8 @@ spec :
rules:
- name: ps1
resource:
kind: Service
kinds:
- Service
name: "game-service*"
mutate:
patches:

Some files were not shown because too many files have changed in this diff Show more