mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-15 17:51:20 +00:00
parent
1171ac691b
commit
8c1d79ab28
72 changed files with 310 additions and 212 deletions
|
@ -116,7 +116,7 @@ func main() {
|
||||||
10*time.Second)
|
10*time.Second)
|
||||||
|
|
||||||
// Configuration Data
|
// Configuration Data
|
||||||
// dyamically load the configuration from configMap
|
// dynamically load the configuration from configMap
|
||||||
// - resource filters
|
// - resource filters
|
||||||
// if the configMap is update, the configuration will be updated :D
|
// if the configMap is update, the configuration will be updated :D
|
||||||
configData := config.NewConfigData(
|
configData := config.NewConfigData(
|
||||||
|
@ -143,7 +143,7 @@ func main() {
|
||||||
// POLICY CONTROLLER
|
// POLICY CONTROLLER
|
||||||
// - reconciliation policy and policy violation
|
// - reconciliation policy and policy violation
|
||||||
// - process policy on existing resources
|
// - process policy on existing resources
|
||||||
// - status aggregator: recieves stats when a policy is applied
|
// - status aggregator: receives stats when a policy is applied
|
||||||
// & updates the policy status
|
// & updates the policy status
|
||||||
pc, err := policy.NewPolicyController(pclient,
|
pc, err := policy.NewPolicyController(pclient,
|
||||||
client,
|
client,
|
||||||
|
|
|
@ -43,7 +43,7 @@ type RequestInfo struct {
|
||||||
type GenerateRequestStatus struct {
|
type GenerateRequestStatus struct {
|
||||||
State GenerateRequestState `json:"state"`
|
State GenerateRequestState `json:"state"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
// This will track the resoruces that are generated by the generate Policy
|
// This will track the resources that are generated by the generate Policy
|
||||||
// Will be used during clean up resources
|
// Will be used during clean up resources
|
||||||
GeneratedResources []ResourceSpec `json:"generatedResources,omitempty"`
|
GeneratedResources []ResourceSpec `json:"generatedResources,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -143,19 +143,25 @@ type Rule struct {
|
||||||
Generation Generation `json:"generate,omitempty"`
|
Generation Generation `json:"generate,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Condition defines the evaluation condition
|
||||||
type Condition struct {
|
type Condition struct {
|
||||||
Key interface{} `json:"key"`
|
Key interface{} `json:"key"`
|
||||||
Operator ConditionOperator `json:"operator"`
|
Operator ConditionOperator `json:"operator"`
|
||||||
Value interface{} `json:"value"`
|
Value interface{} `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConditionOperator defines the type for condition operator
|
||||||
type ConditionOperator string
|
type ConditionOperator string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Equal ConditionOperator = "Equal"
|
//Equal for Equal operator
|
||||||
|
Equal ConditionOperator = "Equal"
|
||||||
|
//NotEqual for NotEqual operator
|
||||||
NotEqual ConditionOperator = "NotEqual"
|
NotEqual ConditionOperator = "NotEqual"
|
||||||
In ConditionOperator = "In"
|
//In for In operator
|
||||||
NotIn ConditionOperator = "NotIn"
|
In ConditionOperator = "In"
|
||||||
|
//NotIn for NotIn operator
|
||||||
|
NotIn ConditionOperator = "NotIn"
|
||||||
)
|
)
|
||||||
|
|
||||||
//MatchResources contains resource description of the resources that the rule is to apply on
|
//MatchResources contains resource description of the resources that the rule is to apply on
|
||||||
|
@ -254,7 +260,7 @@ type RuleStats struct {
|
||||||
|
|
||||||
// PolicyList is a list of Policy resources
|
// PolicyList is a list of Policy resources
|
||||||
|
|
||||||
// PolicyViolation stores the information regarinding the resources for which a policy failed to apply
|
// PolicyViolationTemplate stores the information regarinding the resources for which a policy failed to apply
|
||||||
type PolicyViolationTemplate struct {
|
type PolicyViolationTemplate struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
|
@ -2,6 +2,7 @@ package v1
|
||||||
|
|
||||||
import "reflect"
|
import "reflect"
|
||||||
|
|
||||||
|
//HasMutateOrValidateOrGenerate checks for rule types
|
||||||
func (p ClusterPolicy) HasMutateOrValidateOrGenerate() bool {
|
func (p ClusterPolicy) HasMutateOrValidateOrGenerate() bool {
|
||||||
for _, rule := range p.Spec.Rules {
|
for _, rule := range p.Spec.Rules {
|
||||||
if rule.HasMutate() || rule.HasValidate() || rule.HasGenerate() {
|
if rule.HasMutate() || rule.HasValidate() || rule.HasGenerate() {
|
||||||
|
@ -11,14 +12,17 @@ func (p ClusterPolicy) HasMutateOrValidateOrGenerate() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//HasMutate checks for mutate rule
|
||||||
func (r Rule) HasMutate() bool {
|
func (r Rule) HasMutate() bool {
|
||||||
return !reflect.DeepEqual(r.Mutation, Mutation{})
|
return !reflect.DeepEqual(r.Mutation, Mutation{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//HasValidate checks for validate rule
|
||||||
func (r Rule) HasValidate() bool {
|
func (r Rule) HasValidate() bool {
|
||||||
return !reflect.DeepEqual(r.Validation, Validation{})
|
return !reflect.DeepEqual(r.Validation, Validation{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//HasGenerate checks for generate rule
|
||||||
func (r Rule) HasGenerate() bool {
|
func (r Rule) HasGenerate() bool {
|
||||||
return !reflect.DeepEqual(r.Generation, Generation{})
|
return !reflect.DeepEqual(r.Generation, Generation{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,24 +18,27 @@ const (
|
||||||
DefaultResync time.Duration = 60 * time.Second
|
DefaultResync time.Duration = 60 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// LastReqTime
|
// LastReqTime stores the lastrequest times for incoming api-requests
|
||||||
type LastReqTime struct {
|
type LastReqTime struct {
|
||||||
t time.Time
|
t time.Time
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Time returns the lastrequest time
|
||||||
func (t *LastReqTime) Time() time.Time {
|
func (t *LastReqTime) Time() time.Time {
|
||||||
t.mu.RLock()
|
t.mu.RLock()
|
||||||
defer t.mu.RUnlock()
|
defer t.mu.RUnlock()
|
||||||
return t.t
|
return t.t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//SetTime stes the lastrequest time
|
||||||
func (t *LastReqTime) SetTime(tm time.Time) {
|
func (t *LastReqTime) SetTime(tm time.Time) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
t.t = tm
|
t.t = tm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewLastReqTime returns a new instance of LastRequestTime store
|
||||||
func NewLastReqTime() *LastReqTime {
|
func NewLastReqTime() *LastReqTime {
|
||||||
return &LastReqTime{
|
return &LastReqTime{
|
||||||
t: time.Now(),
|
t: time.Now(),
|
||||||
|
@ -73,8 +76,8 @@ func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister, eventGen ev
|
||||||
}
|
}
|
||||||
|
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
// - has recieved request -> set webhookstatus as "True"
|
// - has received request -> set webhookstatus as "True"
|
||||||
// - no requests recieved
|
// - no requests received
|
||||||
// -> if greater than deadline, send update request
|
// -> if greater than deadline, send update request
|
||||||
// -> if greater than maxDeadline, send failed status update
|
// -> if greater than maxDeadline, send failed status update
|
||||||
for {
|
for {
|
||||||
|
@ -88,8 +91,8 @@ func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister, eventGen ev
|
||||||
// get current time
|
// get current time
|
||||||
timeDiff := time.Since(t.Time())
|
timeDiff := time.Since(t.Time())
|
||||||
if timeDiff > maxDeadline {
|
if timeDiff > maxDeadline {
|
||||||
glog.Infof("failed to recieve any request for more than %v ", maxDeadline)
|
glog.Infof("failed to receive any request for more than %v ", maxDeadline)
|
||||||
glog.Info("Admission Control failing: Webhook is not recieving requests forwarded by api-server as per webhook configurations")
|
glog.Info("Admission Control failing: Webhook is not receiving requests forwarded by api-server as per webhook configurations")
|
||||||
// set the status unavailable
|
// set the status unavailable
|
||||||
if err := statuscontrol.FailedStatus(); err != nil {
|
if err := statuscontrol.FailedStatus(); err != nil {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
|
@ -97,7 +100,7 @@ func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister, eventGen ev
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if timeDiff > deadline {
|
if timeDiff > deadline {
|
||||||
glog.Info("Admission Control failing: Webhook is not recieving requests forwarded by api-server as per webhook configurations")
|
glog.Info("Admission Control failing: Webhook is not receiving requests forwarded by api-server as per webhook configurations")
|
||||||
// send request to update the kyverno deployment
|
// send request to update the kyverno deployment
|
||||||
if err := statuscontrol.IncrementAnnotation(); err != nil {
|
if err := statuscontrol.IncrementAnnotation(); err != nil {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
|
|
|
@ -8,69 +8,73 @@ import (
|
||||||
clientcmd "k8s.io/client-go/tools/clientcmd"
|
clientcmd "k8s.io/client-go/tools/clientcmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml
|
||||||
// These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml
|
|
||||||
KubePolicyNamespace = "kyverno"
|
|
||||||
WebhookServiceName = "kyverno-svc"
|
|
||||||
|
|
||||||
MutatingWebhookConfigurationName = "kyverno-resource-mutating-webhook-cfg"
|
const (
|
||||||
|
//KubePolicyNamespace default kyverno namespace
|
||||||
|
KubePolicyNamespace = "kyverno"
|
||||||
|
//WebhookServiceName default kyverno webhook service name
|
||||||
|
WebhookServiceName = "kyverno-svc"
|
||||||
|
|
||||||
|
//MutatingWebhookConfigurationName default resource mutating webhook configuration name
|
||||||
|
MutatingWebhookConfigurationName = "kyverno-resource-mutating-webhook-cfg"
|
||||||
|
//MutatingWebhookConfigurationDebugName default resource mutating webhook configuration name for debug mode
|
||||||
MutatingWebhookConfigurationDebugName = "kyverno-resource-mutating-webhook-cfg-debug"
|
MutatingWebhookConfigurationDebugName = "kyverno-resource-mutating-webhook-cfg-debug"
|
||||||
MutatingWebhookName = "nirmata.kyverno.resource.mutating-webhook"
|
//MutatingWebhookName default resource mutating webhook name
|
||||||
|
MutatingWebhookName = "nirmata.kyverno.resource.mutating-webhook"
|
||||||
|
|
||||||
// ValidatingWebhookConfigurationName = "kyverno-validating-webhook-cfg"
|
// ValidatingWebhookConfigurationName = "kyverno-validating-webhook-cfg"
|
||||||
// ValidatingWebhookConfigurationDebug = "kyverno-validating-webhook-cfg-debug"
|
// ValidatingWebhookConfigurationDebug = "kyverno-validating-webhook-cfg-debug"
|
||||||
// ValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook"
|
// ValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook"
|
||||||
|
|
||||||
VerifyMutatingWebhookConfigurationName = "kyverno-verify-mutating-webhook-cfg"
|
//VerifyMutatingWebhookConfigurationName default verify mutating webhook configuration name
|
||||||
|
VerifyMutatingWebhookConfigurationName = "kyverno-verify-mutating-webhook-cfg"
|
||||||
|
//VerifyMutatingWebhookConfigurationDebugName default verify mutating webhook configuration name for debug mode
|
||||||
VerifyMutatingWebhookConfigurationDebugName = "kyverno-verify-mutating-webhook-cfg-debug"
|
VerifyMutatingWebhookConfigurationDebugName = "kyverno-verify-mutating-webhook-cfg-debug"
|
||||||
VerifyMutatingWebhookName = "nirmata.kyverno.verify-mutating-webhook"
|
//VerifyMutatingWebhookName default verify mutating webhook name
|
||||||
|
VerifyMutatingWebhookName = "nirmata.kyverno.verify-mutating-webhook"
|
||||||
|
|
||||||
PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg"
|
//PolicyValidatingWebhookConfigurationName default policy validating webhook configuration name
|
||||||
|
PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg"
|
||||||
|
//PolicyValidatingWebhookConfigurationDebugName default policy validating webhook configuration name for debug mode
|
||||||
PolicyValidatingWebhookConfigurationDebugName = "kyverno-policy-validating-webhook-cfg-debug"
|
PolicyValidatingWebhookConfigurationDebugName = "kyverno-policy-validating-webhook-cfg-debug"
|
||||||
PolicyValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook"
|
//PolicyValidatingWebhookName default policy validating webhook name
|
||||||
|
PolicyValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook"
|
||||||
|
|
||||||
PolicyMutatingWebhookConfigurationName = "kyverno-policy-mutating-webhook-cfg"
|
//PolicyMutatingWebhookConfigurationName default policy mutating webhook configuration name
|
||||||
|
PolicyMutatingWebhookConfigurationName = "kyverno-policy-mutating-webhook-cfg"
|
||||||
|
//PolicyMutatingWebhookConfigurationDebugName default policy mutating webhook configuration name for debug mode
|
||||||
PolicyMutatingWebhookConfigurationDebugName = "kyverno-policy-mutating-webhook-cfg-debug"
|
PolicyMutatingWebhookConfigurationDebugName = "kyverno-policy-mutating-webhook-cfg-debug"
|
||||||
PolicyMutatingWebhookName = "nirmata.kyverno.policy-mutating-webhook"
|
//PolicyMutatingWebhookName default policy mutating webhook name
|
||||||
|
PolicyMutatingWebhookName = "nirmata.kyverno.policy-mutating-webhook"
|
||||||
|
|
||||||
// Due to kubernetes issue, we must use next literal constants instead of deployment TypeMeta fields
|
// Due to kubernetes issue, we must use next literal constants instead of deployment TypeMeta fields
|
||||||
// Issue: https://github.com/kubernetes/kubernetes/pull/63972
|
// Issue: https://github.com/kubernetes/kubernetes/pull/63972
|
||||||
// When the issue is closed, we should use TypeMeta struct instead of this constants
|
// When the issue is closed, we should use TypeMeta struct instead of this constants
|
||||||
DeploymentKind = "Deployment"
|
|
||||||
DeploymentAPIVersion = "extensions/v1beta1"
|
// DeploymentKind define the default deployment resource kind
|
||||||
|
DeploymentKind = "Deployment"
|
||||||
|
|
||||||
|
// DeploymentAPIVersion define the default deployment resource apiVersion
|
||||||
|
DeploymentAPIVersion = "extensions/v1beta1"
|
||||||
|
// KubePolicyDeploymentName define the default deployment namespace
|
||||||
KubePolicyDeploymentName = "kyverno"
|
KubePolicyDeploymentName = "kyverno"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
MutatingWebhookServicePath = "/mutate"
|
//MutatingWebhookServicePath is the path for mutation webhook
|
||||||
ValidatingWebhookServicePath = "/validate"
|
MutatingWebhookServicePath = "/mutate"
|
||||||
|
//ValidatingWebhookServicePath is the path for validation webhook
|
||||||
|
ValidatingWebhookServicePath = "/validate"
|
||||||
|
//PolicyValidatingWebhookServicePath is the path for policy validation webhook(used to validate policy resource)
|
||||||
PolicyValidatingWebhookServicePath = "/policyvalidate"
|
PolicyValidatingWebhookServicePath = "/policyvalidate"
|
||||||
PolicyMutatingWebhookServicePath = "/policymutate"
|
//PolicyMutatingWebhookServicePath is the path for policy mutation webhook(used to default)
|
||||||
VerifyMutatingWebhookServicePath = "/verifymutate"
|
PolicyMutatingWebhookServicePath = "/policymutate"
|
||||||
|
//VerifyMutatingWebhookServicePath is the path for verify webhook(used to veryfing if admission control is enabled and active)
|
||||||
SupportedKinds = []string{
|
VerifyMutatingWebhookServicePath = "/verifymutate"
|
||||||
"ConfigMap",
|
|
||||||
"CronJob",
|
|
||||||
"DaemonSet",
|
|
||||||
"Deployment",
|
|
||||||
"Endpoints",
|
|
||||||
"HorizontalPodAutoscaler",
|
|
||||||
"Ingress",
|
|
||||||
"Job",
|
|
||||||
"LimitRange",
|
|
||||||
"Namespace",
|
|
||||||
"NetworkPolicy",
|
|
||||||
"PersistentVolumeClaim",
|
|
||||||
"PodDisruptionBudget",
|
|
||||||
"PodTemplate",
|
|
||||||
"ResourceQuota",
|
|
||||||
"Secret",
|
|
||||||
"Service",
|
|
||||||
"StatefulSet",
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//LogDefaults sets default glog flags
|
//LogDefaultFlags sets default glog flags
|
||||||
func LogDefaultFlags() {
|
func LogDefaultFlags() {
|
||||||
var err error
|
var err error
|
||||||
err = flag.Set("logtostderr", "true")
|
err = flag.Set("logtostderr", "true")
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
// this configmap stores the resources that are to be filtered
|
// this configmap stores the resources that are to be filtered
|
||||||
const cmNameEnv string = "INIT_CONFIG"
|
const cmNameEnv string = "INIT_CONFIG"
|
||||||
|
|
||||||
|
// ConfigData stores the configuration
|
||||||
type ConfigData struct {
|
type ConfigData struct {
|
||||||
client kubernetes.Interface
|
client kubernetes.Interface
|
||||||
// configMap Name
|
// configMap Name
|
||||||
|
@ -119,7 +120,7 @@ func (cd *ConfigData) deleteCM(obj interface{}) {
|
||||||
if cm.Name != cd.cmName {
|
if cm.Name != cd.cmName {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// remove the configuration paramaters
|
// remove the configuration parameters
|
||||||
cd.unload(*cm)
|
cd.unload(*cm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +183,7 @@ type k8Resource struct {
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
//ParseKinds parses the kinds if a single string contains comma seperated kinds
|
//ParseKinds parses the kinds if a single string contains comma separated kinds
|
||||||
// {"1,2,3","4","5"} => {"1","2","3","4","5"}
|
// {"1,2,3","4","5"} => {"1","2","3","4","5"}
|
||||||
func parseKinds(list string) []k8Resource {
|
func parseKinds(list string) []k8Resource {
|
||||||
resources := []k8Resource{}
|
resources := []k8Resource{}
|
||||||
|
|
|
@ -62,6 +62,7 @@ func NewClient(config *rest.Config, resync time.Duration, stopCh <-chan struct{}
|
||||||
return &client, nil
|
return &client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewDynamicSharedInformerFactory returns a new instance of DynamicSharedInformerFactory
|
||||||
func (c *Client) NewDynamicSharedInformerFactory(defaultResync time.Duration) dynamicinformer.DynamicSharedInformerFactory {
|
func (c *Client) NewDynamicSharedInformerFactory(defaultResync time.Duration) dynamicinformer.DynamicSharedInformerFactory {
|
||||||
return dynamicinformer.NewDynamicSharedInformerFactory(c.client, defaultResync)
|
return dynamicinformer.NewDynamicSharedInformerFactory(c.client, defaultResync)
|
||||||
}
|
}
|
||||||
|
@ -224,14 +225,17 @@ func (c *Client) waitUntilNamespaceIsCreated(name string) error {
|
||||||
return lastError
|
return lastError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//IDiscovery provides interface to mange Kind and GVR mapping
|
||||||
type IDiscovery interface {
|
type IDiscovery interface {
|
||||||
GetGVRFromKind(kind string) schema.GroupVersionResource
|
GetGVRFromKind(kind string) schema.GroupVersionResource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDiscovery sets the discovery client implementation
|
||||||
func (c *Client) SetDiscovery(discoveryClient IDiscovery) {
|
func (c *Client) SetDiscovery(discoveryClient IDiscovery) {
|
||||||
c.DiscoveryClient = discoveryClient
|
c.DiscoveryClient = discoveryClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ServerPreferredResources stores the cachedClient instance for discovery client
|
||||||
type ServerPreferredResources struct {
|
type ServerPreferredResources struct {
|
||||||
cachedClient discovery.CachedDiscoveryInterface
|
cachedClient discovery.CachedDiscoveryInterface
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Kind names are case sensitive
|
// CSRs CertificateSigningRequest
|
||||||
//CSRs CertificateSigningRequest
|
|
||||||
CSRs string = "CertificateSigningRequest"
|
CSRs string = "CertificateSigningRequest"
|
||||||
// Secrets Secret
|
// Secrets Secret
|
||||||
Secrets string = "Secret"
|
Secrets string = "Secret"
|
||||||
|
@ -25,7 +24,7 @@ const (
|
||||||
const namespaceCreationMaxWaitTime time.Duration = 30 * time.Second
|
const namespaceCreationMaxWaitTime time.Duration = 30 * time.Second
|
||||||
const namespaceCreationWaitInterval time.Duration = 100 * time.Millisecond
|
const namespaceCreationWaitInterval time.Duration = 100 * time.Millisecond
|
||||||
|
|
||||||
//---testing utilities
|
//NewMockClient ---testing utilities
|
||||||
func NewMockClient(scheme *runtime.Scheme, objects ...runtime.Object) (*Client, error) {
|
func NewMockClient(scheme *runtime.Scheme, objects ...runtime.Object) (*Client, error) {
|
||||||
client := fake.NewSimpleDynamicClient(scheme, objects...)
|
client := fake.NewSimpleDynamicClient(scheme, objects...)
|
||||||
// the typed and dynamic client are initalized with similar resources
|
// the typed and dynamic client are initalized with similar resources
|
||||||
|
|
|
@ -19,8 +19,8 @@ func CreateElementHandler(element string, pattern interface{}, path string) Vali
|
||||||
switch {
|
switch {
|
||||||
case IsConditionAnchor(element):
|
case IsConditionAnchor(element):
|
||||||
return NewConditionAnchorHandler(element, pattern, path)
|
return NewConditionAnchorHandler(element, pattern, path)
|
||||||
case IsExistanceAnchor(element):
|
case IsExistenceAnchor(element):
|
||||||
return NewExistanceHandler(element, pattern, path)
|
return NewExistenceHandler(element, pattern, path)
|
||||||
case IsEqualityAnchor(element):
|
case IsEqualityAnchor(element):
|
||||||
return NewEqualityHandler(element, pattern, path)
|
return NewEqualityHandler(element, pattern, path)
|
||||||
case IsNegationAnchor(element):
|
case IsNegationAnchor(element):
|
||||||
|
@ -156,30 +156,30 @@ func (ch ConditionAnchorHandler) Handle(handler resourceElementHandler, resource
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewExistanceHandler returns existence handler
|
//NewExistenceHandler returns existence handler
|
||||||
func NewExistanceHandler(anchor string, pattern interface{}, path string) ValidationHandler {
|
func NewExistenceHandler(anchor string, pattern interface{}, path string) ValidationHandler {
|
||||||
return ExistanceHandler{
|
return ExistenceHandler{
|
||||||
anchor: anchor,
|
anchor: anchor,
|
||||||
pattern: pattern,
|
pattern: pattern,
|
||||||
path: path,
|
path: path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ExistanceHandler provides handlers to process exitence anchor handler
|
//ExistenceHandler provides handlers to process exitence anchor handler
|
||||||
type ExistanceHandler struct {
|
type ExistenceHandler struct {
|
||||||
anchor string
|
anchor string
|
||||||
pattern interface{}
|
pattern interface{}
|
||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
//Handle processes the existence anchor handler
|
//Handle processes the existence anchor handler
|
||||||
func (eh ExistanceHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}) (string, error) {
|
func (eh ExistenceHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}) (string, error) {
|
||||||
// skip is used by existance anchor to not process further if condition is not satisfied
|
// skip is used by existance anchor to not process further if condition is not satisfied
|
||||||
anchorKey := removeAnchor(eh.anchor)
|
anchorKey := removeAnchor(eh.anchor)
|
||||||
currentPath := eh.path + anchorKey + "/"
|
currentPath := eh.path + anchorKey + "/"
|
||||||
// check if anchor is present in resource
|
// check if anchor is present in resource
|
||||||
if value, ok := resourceMap[anchorKey]; ok {
|
if value, ok := resourceMap[anchorKey]; ok {
|
||||||
// Existance anchor can only exist on resource value type of list
|
// Existence anchor can only exist on resource value type of list
|
||||||
switch typedResource := value.(type) {
|
switch typedResource := value.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
typedPattern, ok := eh.pattern.([]interface{})
|
typedPattern, ok := eh.pattern.([]interface{})
|
||||||
|
@ -194,8 +194,8 @@ func (eh ExistanceHandler) Handle(handler resourceElementHandler, resourceMap ma
|
||||||
}
|
}
|
||||||
return validateExistenceListResource(handler, typedResource, typedPatternMap, originPattern, currentPath)
|
return validateExistenceListResource(handler, typedResource, typedPatternMap, originPattern, currentPath)
|
||||||
default:
|
default:
|
||||||
glog.Error("Invalid type: Existance ^ () anchor can be used only on list/array type resource")
|
glog.Error("Invalid type: Existence ^ () anchor can be used only on list/array type resource")
|
||||||
return currentPath, fmt.Errorf("Invalid resource type %T: Existance ^ () anchor can be used only on list/array type resource", value)
|
return currentPath, fmt.Errorf("Invalid resource type %T: Existence ^ () anchor can be used only on list/array type resource", value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
|
@ -213,15 +213,16 @@ func validateExistenceListResource(handler resourceElementHandler, resourceList
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// none of the existance checks worked, so thats a failure sceanario
|
// none of the existence checks worked, so thats a failure sceanario
|
||||||
return path, fmt.Errorf("Existence anchor validation failed at path %s", path)
|
return path, fmt.Errorf("Existence anchor validation failed at path %s", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GetAnchorsResourcesFromMap returns map of anchors
|
||||||
func GetAnchorsResourcesFromMap(patternMap map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
|
func GetAnchorsResourcesFromMap(patternMap map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
|
||||||
anchors := map[string]interface{}{}
|
anchors := map[string]interface{}{}
|
||||||
resources := map[string]interface{}{}
|
resources := map[string]interface{}{}
|
||||||
for key, value := range patternMap {
|
for key, value := range patternMap {
|
||||||
if IsConditionAnchor(key) || IsExistanceAnchor(key) || IsEqualityAnchor(key) || IsNegationAnchor(key) {
|
if IsConditionAnchor(key) || IsExistenceAnchor(key) || IsEqualityAnchor(key) || IsNegationAnchor(key) {
|
||||||
anchors[key] = value
|
anchors[key] = value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package anchor
|
package anchor
|
||||||
|
|
||||||
// Anchor function type
|
// IsAnchor is a function handler
|
||||||
type IsAnchor func(str string) bool
|
type IsAnchor func(str string) bool
|
||||||
|
|
||||||
|
//IsConditionAnchor checks for condition anchor
|
||||||
func IsConditionAnchor(str string) bool {
|
func IsConditionAnchor(str string) bool {
|
||||||
if len(str) < 2 {
|
if len(str) < 2 {
|
||||||
return false
|
return false
|
||||||
|
@ -11,6 +12,7 @@ func IsConditionAnchor(str string) bool {
|
||||||
return (str[0] == '(' && str[len(str)-1] == ')')
|
return (str[0] == '(' && str[len(str)-1] == ')')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//IsNegationAnchor checks for negation anchor
|
||||||
func IsNegationAnchor(str string) bool {
|
func IsNegationAnchor(str string) bool {
|
||||||
left := "X("
|
left := "X("
|
||||||
right := ")"
|
right := ")"
|
||||||
|
@ -21,6 +23,7 @@ func IsNegationAnchor(str string) bool {
|
||||||
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAddingAnchor checks for addition anchor
|
||||||
func IsAddingAnchor(key string) bool {
|
func IsAddingAnchor(key string) bool {
|
||||||
const left = "+("
|
const left = "+("
|
||||||
const right = ")"
|
const right = ")"
|
||||||
|
@ -32,6 +35,7 @@ func IsAddingAnchor(key string) bool {
|
||||||
return left == key[:len(left)] && right == key[len(key)-len(right):]
|
return left == key[:len(left)] && right == key[len(key)-len(right):]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsEqualityAnchor checks for equality anchor
|
||||||
func IsEqualityAnchor(str string) bool {
|
func IsEqualityAnchor(str string) bool {
|
||||||
left := "=("
|
left := "=("
|
||||||
right := ")"
|
right := ")"
|
||||||
|
@ -42,7 +46,8 @@ func IsEqualityAnchor(str string) bool {
|
||||||
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsExistanceAnchor(str string) bool {
|
//IsExistenceAnchor checks for existence anchor
|
||||||
|
func IsExistenceAnchor(str string) bool {
|
||||||
left := "^("
|
left := "^("
|
||||||
right := ")"
|
right := ")"
|
||||||
|
|
||||||
|
@ -53,15 +58,14 @@ func IsExistanceAnchor(str string) bool {
|
||||||
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func removeAnchor(key string) string {
|
func removeAnchor(key string) string {
|
||||||
if IsConditionAnchor(key) {
|
if IsConditionAnchor(key) {
|
||||||
return key[1 : len(key)-1]
|
return key[1 : len(key)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
if IsExistanceAnchor(key) || IsAddingAnchor(key) || IsEqualityAnchor(key) || IsNegationAnchor(key) {
|
if IsExistenceAnchor(key) || IsAddingAnchor(key) || IsEqualityAnchor(key) || IsNegationAnchor(key) {
|
||||||
return key[2 : len(key)-1]
|
return key[2 : len(key)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,18 +41,18 @@ func TestWrappedWithParentheses_Empty(t *testing.T) {
|
||||||
assert.Assert(t, !IsConditionAnchor(str))
|
assert.Assert(t, !IsConditionAnchor(str))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsExistanceAnchor_Yes(t *testing.T) {
|
func TestIsExistenceAnchor_Yes(t *testing.T) {
|
||||||
assert.Assert(t, IsExistanceAnchor("^(abc)"))
|
assert.Assert(t, IsExistenceAnchor("^(abc)"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsExistanceAnchor_NoRightBracket(t *testing.T) {
|
func TestIsExistenceAnchor_NoRightBracket(t *testing.T) {
|
||||||
assert.Assert(t, !IsExistanceAnchor("^(abc"))
|
assert.Assert(t, !IsExistenceAnchor("^(abc"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsExistanceAnchor_OnlyHat(t *testing.T) {
|
func TestIsExistenceAnchor_OnlyHat(t *testing.T) {
|
||||||
assert.Assert(t, !IsExistanceAnchor("^abc"))
|
assert.Assert(t, !IsExistenceAnchor("^abc"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsExistanceAnchor_ConditionAnchor(t *testing.T) {
|
func TestIsExistenceAnchor_ConditionAnchor(t *testing.T) {
|
||||||
assert.Assert(t, !IsExistanceAnchor("(abc)"))
|
assert.Assert(t, !IsExistenceAnchor("(abc)"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,15 @@ import (
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Interface ... normal functions
|
//Interface to manage context operations
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
// merges the json with context
|
//AddJSON merges the json with context
|
||||||
AddJSON(dataRaw []byte) error
|
AddJSON(dataRaw []byte) error
|
||||||
// merges resource json under request.object
|
//AddResource merges resource json under request.object
|
||||||
AddResource(dataRaw []byte) error
|
AddResource(dataRaw []byte) error
|
||||||
// merges userInfo json under kyverno.userInfo
|
//AddUserInfo merges userInfo json under kyverno.userInfo
|
||||||
AddUserInfo(userInfo kyverno.UserInfo) error
|
AddUserInfo(userInfo kyverno.UserInfo) error
|
||||||
// merges serrviceaccount
|
//AddSA merges serrviceaccount
|
||||||
AddSA(userName string) error
|
AddSA(userName string) error
|
||||||
EvalInterface
|
EvalInterface
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ func (ctx *Context) AddJSON(dataRaw []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add data at path: request.object
|
//AddResource data at path: request.object
|
||||||
func (ctx *Context) AddResource(dataRaw []byte) error {
|
func (ctx *Context) AddResource(dataRaw []byte) error {
|
||||||
|
|
||||||
// unmarshall the resource struct
|
// unmarshall the resource struct
|
||||||
|
@ -86,6 +86,7 @@ func (ctx *Context) AddResource(dataRaw []byte) error {
|
||||||
return ctx.AddJSON(objRaw)
|
return ctx.AddJSON(objRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//AddUserInfo adds userInfo at path request.userInfo
|
||||||
func (ctx *Context) AddUserInfo(userRequestInfo kyverno.RequestInfo) error {
|
func (ctx *Context) AddUserInfo(userRequestInfo kyverno.RequestInfo) error {
|
||||||
modifiedResource := struct {
|
modifiedResource := struct {
|
||||||
Request interface{} `json:"request"`
|
Request interface{} `json:"request"`
|
||||||
|
@ -101,7 +102,7 @@ func (ctx *Context) AddUserInfo(userRequestInfo kyverno.RequestInfo) error {
|
||||||
return ctx.AddJSON(objRaw)
|
return ctx.AddJSON(objRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
// removes prefix 'system:serviceaccount:' and namespace, then loads only username
|
//AddSA removes prefix 'system:serviceaccount:' and namespace, then loads only SA name and SA namespace
|
||||||
func (ctx *Context) AddSA(userName string) error {
|
func (ctx *Context) AddSA(userName string) error {
|
||||||
saPrefix := "system:serviceaccount:"
|
saPrefix := "system:serviceaccount:"
|
||||||
var sa string
|
var sa string
|
||||||
|
|
|
@ -13,11 +13,11 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateNew
|
// Generate checks for validity of generate rule on the resource
|
||||||
// 1. validate variables to be susbtitute in the general ruleInfo (match,exclude,condition)
|
// 1. validate variables to be susbtitute in the general ruleInfo (match,exclude,condition)
|
||||||
// - the caller has to check the ruleResponse to determine whether the path exist
|
// - the caller has to check the ruleResponse to determine whether the path exist
|
||||||
// 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
|
// 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
|
||||||
func GenerateNew(policyContext PolicyContext) (resp response.EngineResponse) {
|
func Generate(policyContext PolicyContext) (resp response.EngineResponse) {
|
||||||
policy := policyContext.Policy
|
policy := policyContext.Policy
|
||||||
resource := policyContext.NewResource
|
resource := policyContext.NewResource
|
||||||
admissionInfo := policyContext.AdmissionInfo
|
admissionInfo := policyContext.AdmissionInfo
|
||||||
|
|
|
@ -111,7 +111,7 @@ func ProcessOverlay(ctx context.EvalInterface, rule kyverno.Rule, resource unstr
|
||||||
return resp, resource
|
return resp, resource
|
||||||
}
|
}
|
||||||
|
|
||||||
// rule application succesfuly
|
// rule application successfully
|
||||||
resp.Success = true
|
resp.Success = true
|
||||||
resp.Message = fmt.Sprintf("successfully processed overlay")
|
resp.Message = fmt.Sprintf("successfully processed overlay")
|
||||||
resp.Patches = patches
|
resp.Patches = patches
|
||||||
|
|
|
@ -23,7 +23,7 @@ func checkConditions(resource, overlay interface{}, path string) (string, overla
|
||||||
|
|
||||||
// resource item exists but has different type
|
// resource item exists but has different type
|
||||||
// return false if anchor exists in overlay
|
// return false if anchor exists in overlay
|
||||||
// conditon never be true in this case
|
// condition never be true in this case
|
||||||
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
|
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
|
||||||
if hasNestedAnchors(overlay) {
|
if hasNestedAnchors(overlay) {
|
||||||
glog.V(4).Infof("Found anchor on different types of element at path %s: overlay %T, resource %T", path, overlay, resource)
|
glog.V(4).Infof("Found anchor on different types of element at path %s: overlay %T, resource %T", path, overlay, resource)
|
||||||
|
|
|
@ -19,6 +19,7 @@ func applyPatch(resource []byte, patchRaw []byte) ([]byte, error) {
|
||||||
return utils.ApplyPatches(resource, patchesList)
|
return utils.ApplyPatches(resource, patchesList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ProcessPatches applies the patches on the resource and returns the patched resource
|
||||||
func ProcessPatches(rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) {
|
func ProcessPatches(rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
glog.V(4).Infof("started JSON patch rule %q (%v)", rule.Name, startTime)
|
glog.V(4).Infof("started JSON patch rule %q (%v)", rule.Name, startTime)
|
||||||
|
@ -82,9 +83,9 @@ func ProcessPatches(rule kyverno.Rule, resource unstructured.Unstructured) (resp
|
||||||
return resp, resource
|
return resp, resource
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON patches processed succesfully
|
// JSON patches processed successfully
|
||||||
resp.Success = true
|
resp.Success = true
|
||||||
resp.Message = fmt.Sprintf("succesfully process JSON patches")
|
resp.Message = fmt.Sprintf("successfully process JSON patches")
|
||||||
resp.Patches = patches
|
resp.Patches = patches
|
||||||
return resp, patchedResource
|
return resp, patchedResource
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ func removeAnchor(key string) string {
|
||||||
return key[1 : len(key)-1]
|
return key[1 : len(key)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
if anchor.IsExistanceAnchor(key) || anchor.IsAddingAnchor(key) || anchor.IsEqualityAnchor(key) || anchor.IsNegationAnchor(key) {
|
if anchor.IsExistenceAnchor(key) || anchor.IsAddingAnchor(key) || anchor.IsEqualityAnchor(key) || anchor.IsNegationAnchor(key) {
|
||||||
return key[2 : len(key)-1]
|
return key[2 : len(key)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PodControllers = "DaemonSet,Deployment,Job,StatefulSet"
|
//PodControllers stores the list of Pod-controllers in csv string
|
||||||
|
PodControllers = "DaemonSet,Deployment,Job,StatefulSet"
|
||||||
|
//PodControllersAnnotation defines the annotation key for Pod-Controllers
|
||||||
PodControllersAnnotation = "pod-policies.kyverno.io/autogen-controllers"
|
PodControllersAnnotation = "pod-policies.kyverno.io/autogen-controllers"
|
||||||
PodTemplateAnnotation = "pod-policies.kyverno.io/autogen-applied"
|
//PodTemplateAnnotation defines the annotation key for Pod-Template
|
||||||
|
PodTemplateAnnotation = "pod-policies.kyverno.io/autogen-applied"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mutate performs mutation. Overlay first and then mutation patches
|
// Mutate performs mutation. Overlay first and then mutation patches
|
||||||
|
@ -34,7 +37,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
|
||||||
defer endMutateResultResponse(&resp, startTime)
|
defer endMutateResultResponse(&resp, startTime)
|
||||||
|
|
||||||
incrementAppliedRuleCount := func() {
|
incrementAppliedRuleCount := func() {
|
||||||
// rules applied succesfully count
|
// rules applied successfully count
|
||||||
resp.PolicyResponse.RulesAppliedCount++
|
resp.PolicyResponse.RulesAppliedCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) {
|
||||||
"apiVersion": "kyverno.io/v1",
|
"apiVersion": "kyverno.io/v1",
|
||||||
"kind": "ClusterPolicy",
|
"kind": "ClusterPolicy",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "substitue-variable"
|
"name": "substitute-variable"
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"rules": [
|
"rules": [
|
||||||
|
|
|
@ -18,9 +18,10 @@ const (
|
||||||
Less Operator = "<"
|
Less Operator = "<"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//ReferenceSign defines the operator for anchor reference
|
||||||
const ReferenceSign Operator = "$()"
|
const ReferenceSign Operator = "$()"
|
||||||
|
|
||||||
// getOperatorFromStringPattern parses opeartor from pattern
|
// GetOperatorFromStringPattern parses opeartor from pattern
|
||||||
func GetOperatorFromStringPattern(pattern string) Operator {
|
func GetOperatorFromStringPattern(pattern string) Operator {
|
||||||
if len(pattern) < 2 {
|
if len(pattern) < 2 {
|
||||||
return Equal
|
return Equal
|
||||||
|
|
|
@ -268,14 +268,14 @@ func validateValidation(v kyverno.Validation) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Pattern != nil {
|
if v.Pattern != nil {
|
||||||
if path, err := validatePattern(v.Pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistanceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil {
|
if path, err := validatePattern(v.Pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistenceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil {
|
||||||
return fmt.Sprintf("pattern.%s", path), err
|
return fmt.Sprintf("pattern.%s", path), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(v.AnyPattern) != 0 {
|
if len(v.AnyPattern) != 0 {
|
||||||
for i, pattern := range v.AnyPattern {
|
for i, pattern := range v.AnyPattern {
|
||||||
if path, err := validatePattern(pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistanceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil {
|
if path, err := validatePattern(pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistenceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil {
|
||||||
return fmt.Sprintf("anyPattern[%d].%s", i, path), err
|
return fmt.Sprintf("anyPattern[%d].%s", i, path), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -322,7 +322,7 @@ func validateGeneration(gen kyverno.Generation) (string, error) {
|
||||||
//TODO: is this required ?? as anchors can only be on pattern and not resource
|
//TODO: is this required ?? as anchors can only be on pattern and not resource
|
||||||
// we can add this check by not sure if its needed here
|
// we can add this check by not sure if its needed here
|
||||||
if path, err := validatePattern(gen.Data, "/", []anchor.IsAnchor{}); err != nil {
|
if path, err := validatePattern(gen.Data, "/", []anchor.IsAnchor{}); err != nil {
|
||||||
return fmt.Sprintf("data.%s", path), fmt.Errorf("anchors not supported on generate resoruces: %v", err)
|
return fmt.Sprintf("data.%s", path), fmt.Errorf("anchors not supported on generate resources: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
|
@ -371,16 +371,16 @@ func validateMap(patternMap map[string]interface{}, path string, supportedAnchor
|
||||||
return path + "/" + key, fmt.Errorf("Unsupported anchor %s", key)
|
return path + "/" + key, fmt.Errorf("Unsupported anchor %s", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// addition check for existance anchor
|
// addition check for existence anchor
|
||||||
// value must be of type list
|
// value must be of type list
|
||||||
if anchor.IsExistanceAnchor(key) {
|
if anchor.IsExistenceAnchor(key) {
|
||||||
typedValue, ok := value.([]interface{})
|
typedValue, ok := value.([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return path + "/" + key, fmt.Errorf("Existance anchor should have value of type list")
|
return path + "/" + key, fmt.Errorf("Existence anchor should have value of type list")
|
||||||
}
|
}
|
||||||
// validate there is only one entry in the list
|
// validate there is only one entry in the list
|
||||||
if len(typedValue) == 0 || len(typedValue) > 1 {
|
if len(typedValue) == 0 || len(typedValue) > 1 {
|
||||||
return path + "/" + key, fmt.Errorf("Existance anchor: single value expected, multiple specified")
|
return path + "/" + key, fmt.Errorf("Existence anchor: single value expected, multiple specified")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
//SaPrefix defines the prefix for service accounts
|
||||||
SaPrefix = "system:serviceaccount:"
|
SaPrefix = "system:serviceaccount:"
|
||||||
)
|
)
|
||||||
|
|
||||||
// matchAdmissionInfo return true if the rule can be applied to the request
|
// MatchAdmissionInfo return true if the rule can be applied to the request
|
||||||
func MatchAdmissionInfo(rule kyverno.Rule, requestInfo kyverno.RequestInfo) bool {
|
func MatchAdmissionInfo(rule kyverno.Rule, requestInfo kyverno.RequestInfo) bool {
|
||||||
// when processing existing resource, it does not contain requestInfo
|
// when processing existing resource, it does not contain requestInfo
|
||||||
// skip permission checking
|
// skip permission checking
|
||||||
|
|
|
@ -47,7 +47,7 @@ func (rs ResourceSpec) GetKey() string {
|
||||||
type PolicyStats struct {
|
type PolicyStats struct {
|
||||||
// time required to process the policy rules on a resource
|
// time required to process the policy rules on a resource
|
||||||
ProcessingTime time.Duration `json:"processingTime"`
|
ProcessingTime time.Duration `json:"processingTime"`
|
||||||
// Count of rules that were applied succesfully
|
// Count of rules that were applied successfully
|
||||||
RulesAppliedCount int `json:"rulesAppliedCount"`
|
RulesAppliedCount int `json:"rulesAppliedCount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
type EngineStats struct {
|
type EngineStats struct {
|
||||||
// average time required to process the policy rules on a resource
|
// average time required to process the policy rules on a resource
|
||||||
ExecutionTime time.Duration
|
ExecutionTime time.Duration
|
||||||
// Count of rules that were applied succesfully
|
// Count of rules that were applied successfully
|
||||||
RulesAppliedCount int
|
RulesAppliedCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,12 +145,16 @@ func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Condition type for conditions
|
||||||
type Condition int
|
type Condition int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// NotEvaluate to not-evaluate to condition
|
||||||
NotEvaluate Condition = 0
|
NotEvaluate Condition = 0
|
||||||
Process Condition = 1
|
// Process to process the condition
|
||||||
Skip Condition = 2
|
Process Condition = 1
|
||||||
|
// Skip to skip the condition
|
||||||
|
Skip Condition = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseResourceInfoFromObject get kind/namepace/name from resource
|
// ParseResourceInfoFromObject get kind/namepace/name from resource
|
||||||
|
|
|
@ -6,12 +6,17 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//RuleType defines the type for rule
|
||||||
type RuleType int
|
type RuleType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
//Mutation type for mutation rule
|
||||||
Mutation RuleType = iota
|
Mutation RuleType = iota
|
||||||
|
//Validation type for validation rule
|
||||||
Validation
|
Validation
|
||||||
|
//Generation type for generation rule
|
||||||
Generation
|
Generation
|
||||||
|
//All type for other rule operations(future)
|
||||||
All
|
All
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -73,6 +78,7 @@ func JoinPatches(patches [][]byte) []byte {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ConvertToUnstructured converts the resource to unstructured format
|
||||||
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
||||||
resource := &unstructured.Unstructured{}
|
resource := &unstructured.Unstructured{}
|
||||||
err := resource.UnmarshalJSON(data)
|
err := resource.UnmarshalJSON(data)
|
||||||
|
@ -82,7 +88,7 @@ func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
||||||
return resource, nil
|
return resource, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAnchorsFromMap gets the conditional anchor map
|
// GetAnchorsFromMap gets the conditional anchor map
|
||||||
func GetAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{} {
|
func GetAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{} {
|
||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,13 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//ValidationFailureReason defeins type for Validation Failure reason
|
||||||
type ValidationFailureReason int
|
type ValidationFailureReason int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// PathNotPresent if path is not present
|
||||||
PathNotPresent ValidationFailureReason = iota
|
PathNotPresent ValidationFailureReason = iota
|
||||||
|
// Rulefailure if the rule failed
|
||||||
Rulefailure
|
Rulefailure
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,6 +45,7 @@ func getRawKeyIfWrappedWithAttributes(str string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ValidationError stores error for validation error
|
||||||
type ValidationError struct {
|
type ValidationError struct {
|
||||||
StatusCode ValidationFailureReason
|
StatusCode ValidationFailureReason
|
||||||
ErrorMsg string
|
ErrorMsg string
|
||||||
|
|
|
@ -55,7 +55,7 @@ func ValidateValueWithPattern(value, pattern interface{}) bool {
|
||||||
|
|
||||||
func validateValueWithMapPattern(value interface{}, typedPattern map[string]interface{}) bool {
|
func validateValueWithMapPattern(value interface{}, typedPattern map[string]interface{}) bool {
|
||||||
// verify the type of the resource value is map[string]interface,
|
// verify the type of the resource value is map[string]interface,
|
||||||
// we only check for existance of object, not the equality of content and value
|
// we only check for existence of object, not the equality of content and value
|
||||||
//TODO: check if adding
|
//TODO: check if adding
|
||||||
_, ok := value.(map[string]interface{})
|
_, ok := value.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"github.com/nirmata/kyverno/pkg/engine/variables"
|
"github.com/nirmata/kyverno/pkg/engine/variables"
|
||||||
)
|
)
|
||||||
|
|
||||||
// validateResourceWithPattern is a start of element-by-element validation process
|
// ValidateResourceWithPattern is a start of element-by-element validation process
|
||||||
// It assumes that validation is started from root, so "/" is passed
|
// It assumes that validation is started from root, so "/" is passed
|
||||||
func ValidateResourceWithPattern(ctx context.EvalInterface, resource, pattern interface{}) (string, ValidationError) {
|
func ValidateResourceWithPattern(ctx context.EvalInterface, resource, pattern interface{}) (string, ValidationError) {
|
||||||
// if referenced path is not present, we skip processing the rule and report violation
|
// if referenced path is not present, we skip processing the rule and report violation
|
||||||
|
@ -93,7 +93,7 @@ func validateMap(resourceMap, patternMap map[string]interface{}, origPattern int
|
||||||
for key, patternElement := range anchors {
|
for key, patternElement := range anchors {
|
||||||
// get handler for each pattern in the pattern
|
// get handler for each pattern in the pattern
|
||||||
// - Conditional
|
// - Conditional
|
||||||
// - Existance
|
// - Existence
|
||||||
// - Equality
|
// - Equality
|
||||||
handler := anchor.CreateElementHandler(key, patternElement, path)
|
handler := anchor.CreateElementHandler(key, patternElement, path)
|
||||||
handlerPath, err := handler.Handle(validateResourceElement, resourceMap, origPattern)
|
handlerPath, err := handler.Handle(validateResourceElement, resourceMap, origPattern)
|
||||||
|
|
|
@ -37,7 +37,7 @@ func Validate(policyContext PolicyContext) (resp response.EngineResponse) {
|
||||||
resp := validateResource(ctx, policy, newR, admissionInfo)
|
resp := validateResource(ctx, policy, newR, admissionInfo)
|
||||||
startResultResponse(resp, policy, newR)
|
startResultResponse(resp, policy, newR)
|
||||||
defer endResultResponse(resp, startTime)
|
defer endResultResponse(resp, startTime)
|
||||||
// set PatchedResource with orgin resource if empty
|
// set PatchedResource with origin resource if empty
|
||||||
// in order to create policy violation
|
// in order to create policy violation
|
||||||
if reflect.DeepEqual(resp.PatchedResource, unstructured.Unstructured{}) {
|
if reflect.DeepEqual(resp.PatchedResource, unstructured.Unstructured{}) {
|
||||||
resp.PatchedResource = newR
|
resp.PatchedResource = newR
|
||||||
|
@ -79,11 +79,11 @@ func startResultResponse(resp *response.EngineResponse, policy kyverno.ClusterPo
|
||||||
func endResultResponse(resp *response.EngineResponse, startTime time.Time) {
|
func endResultResponse(resp *response.EngineResponse, startTime time.Time) {
|
||||||
resp.PolicyResponse.ProcessingTime = time.Since(startTime)
|
resp.PolicyResponse.ProcessingTime = time.Since(startTime)
|
||||||
glog.V(4).Infof("Finished applying validation rules policy %v (%v)", resp.PolicyResponse.Policy, resp.PolicyResponse.ProcessingTime)
|
glog.V(4).Infof("Finished applying validation rules policy %v (%v)", resp.PolicyResponse.Policy, resp.PolicyResponse.ProcessingTime)
|
||||||
glog.V(4).Infof("Validation Rules appplied succesfully count %v for policy %q", resp.PolicyResponse.RulesAppliedCount, resp.PolicyResponse.Policy)
|
glog.V(4).Infof("Validation Rules appplied successfully count %v for policy %q", resp.PolicyResponse.RulesAppliedCount, resp.PolicyResponse.Policy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func incrementAppliedCount(resp *response.EngineResponse) {
|
func incrementAppliedCount(resp *response.EngineResponse) {
|
||||||
// rules applied succesfully count
|
// rules applied successfully count
|
||||||
resp.PolicyResponse.RulesAppliedCount++
|
resp.PolicyResponse.RulesAppliedCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ func validateResource(ctx context.EvalInterface, policy kyverno.ClusterPolicy, r
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSameResponse(oldResponse, newResponse *response.EngineResponse) bool {
|
func isSameResponse(oldResponse, newResponse *response.EngineResponse) bool {
|
||||||
// if the respones are same then return true
|
// if the response are same then return true
|
||||||
return isSamePolicyResponse(oldResponse.PolicyResponse, newResponse.PolicyResponse)
|
return isSamePolicyResponse(oldResponse.PolicyResponse, newResponse.PolicyResponse)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -203,14 +203,14 @@ func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructu
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
// rule application succesful
|
// rule application successful
|
||||||
glog.V(4).Infof("rule %s pattern validated succesfully on resource %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
glog.V(4).Infof("rule %s pattern validated successfully on resource %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
||||||
resp.Success = true
|
resp.Success = true
|
||||||
resp.Message = fmt.Sprintf("Validation rule '%s' succeeded.", rule.Name)
|
resp.Message = fmt.Sprintf("Validation rule '%s' succeeded.", rule.Name)
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
// using anyPattern we can define multiple patterns and only one of them has to be succesfully validated
|
// using anyPattern we can define multiple patterns and only one of them has to be successfully validated
|
||||||
// return directly if one pattern succeed
|
// return directly if one pattern succeed
|
||||||
// if none succeed, report violation / policyerror(TODO)
|
// if none succeed, report violation / policyerror(TODO)
|
||||||
if rule.Validation.AnyPattern != nil {
|
if rule.Validation.AnyPattern != nil {
|
||||||
|
@ -218,9 +218,9 @@ func validatePatterns(ctx context.EvalInterface, resource unstructured.Unstructu
|
||||||
var failedPaths, invalidPaths []string
|
var failedPaths, invalidPaths []string
|
||||||
for index, pattern := range rule.Validation.AnyPattern {
|
for index, pattern := range rule.Validation.AnyPattern {
|
||||||
path, err := validate.ValidateResourceWithPattern(ctx, resource.Object, pattern)
|
path, err := validate.ValidateResourceWithPattern(ctx, resource.Object, pattern)
|
||||||
// this pattern was succesfully validated
|
// this pattern was successfully validated
|
||||||
if reflect.DeepEqual(err, validate.ValidationError{}) {
|
if reflect.DeepEqual(err, validate.ValidationError{}) {
|
||||||
glog.V(4).Infof("anyPattern %v succesfully validated on resource %s/%s/%s", pattern, resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
glog.V(4).Infof("anyPattern %v successfully validated on resource %s/%s/%s", pattern, resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
||||||
resp.Success = true
|
resp.Success = true
|
||||||
resp.Message = fmt.Sprintf("Validation rule '%s' anyPattern[%d] succeeded.", rule.Name, index)
|
resp.Message = fmt.Sprintf("Validation rule '%s' anyPattern[%d] succeeded.", rule.Name, index)
|
||||||
return resp
|
return resp
|
||||||
|
|
|
@ -1457,7 +1457,7 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) {
|
||||||
"apiVersion": "kyverno.io/v1",
|
"apiVersion": "kyverno.io/v1",
|
||||||
"kind": "ClusterPolicy",
|
"kind": "ClusterPolicy",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "substitue-variable"
|
"name": "substitute-variable"
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"rules": [
|
"rules": [
|
||||||
|
@ -1528,7 +1528,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfies(t *t
|
||||||
"apiVersion": "kyverno.io/v1",
|
"apiVersion": "kyverno.io/v1",
|
||||||
"kind": "ClusterPolicy",
|
"kind": "ClusterPolicy",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "substitue-variable"
|
"name": "substitute-variable"
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"rules": [
|
"rules": [
|
||||||
|
@ -1620,7 +1620,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test
|
||||||
"apiVersion": "kyverno.io/v1",
|
"apiVersion": "kyverno.io/v1",
|
||||||
"kind": "ClusterPolicy",
|
"kind": "ClusterPolicy",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "substitue-variable"
|
"name": "substitute-variable"
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"rules": [
|
"rules": [
|
||||||
|
@ -1710,7 +1710,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter
|
||||||
"apiVersion": "kyverno.io/v1",
|
"apiVersion": "kyverno.io/v1",
|
||||||
"kind": "ClusterPolicy",
|
"kind": "ClusterPolicy",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "substitue-variable"
|
"name": "substitute-variable"
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"rules": [
|
"rules": [
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/nirmata/kyverno/pkg/engine/variables/operator"
|
"github.com/nirmata/kyverno/pkg/engine/variables/operator"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//Evaluate evaluates the condition
|
||||||
func Evaluate(ctx context.EvalInterface, condition kyverno.Condition) bool {
|
func Evaluate(ctx context.EvalInterface, condition kyverno.Condition) bool {
|
||||||
// get handler for the operator
|
// get handler for the operator
|
||||||
handle := operator.CreateOperatorHandler(ctx, condition.Operator, SubstituteVariables)
|
handle := operator.CreateOperatorHandler(ctx, condition.Operator, SubstituteVariables)
|
||||||
|
@ -16,6 +17,7 @@ func Evaluate(ctx context.EvalInterface, condition kyverno.Condition) bool {
|
||||||
return handle.Evaluate(condition.Key, condition.Value)
|
return handle.Evaluate(condition.Key, condition.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//EvaluateConditions evaluates multiple conditions
|
||||||
func EvaluateConditions(ctx context.EvalInterface, conditions []kyverno.Condition) bool {
|
func EvaluateConditions(ctx context.EvalInterface, conditions []kyverno.Condition) bool {
|
||||||
// AND the conditions
|
// AND the conditions
|
||||||
for _, condition := range conditions {
|
for _, condition := range conditions {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/nirmata/kyverno/pkg/engine/context"
|
"github.com/nirmata/kyverno/pkg/engine/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//NewEqualHandler returns handler to manage Equal operations
|
||||||
func NewEqualHandler(ctx context.EvalInterface, subHandler VariableSubstitutionHandler) OperatorHandler {
|
func NewEqualHandler(ctx context.EvalInterface, subHandler VariableSubstitutionHandler) OperatorHandler {
|
||||||
return EqualHandler{
|
return EqualHandler{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
@ -16,11 +17,13 @@ func NewEqualHandler(ctx context.EvalInterface, subHandler VariableSubstitutionH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//EqualHandler provides implementation to handle NotEqual Operator
|
||||||
type EqualHandler struct {
|
type EqualHandler struct {
|
||||||
ctx context.EvalInterface
|
ctx context.EvalInterface
|
||||||
subHandler VariableSubstitutionHandler
|
subHandler VariableSubstitutionHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Evaluate evaluates expression with Equal Operator
|
||||||
func (eh EqualHandler) Evaluate(key, value interface{}) bool {
|
func (eh EqualHandler) Evaluate(key, value interface{}) bool {
|
||||||
// substitute the variables
|
// substitute the variables
|
||||||
nKey := eh.subHandler(eh.ctx, key)
|
nKey := eh.subHandler(eh.ctx, key)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/nirmata/kyverno/pkg/engine/context"
|
"github.com/nirmata/kyverno/pkg/engine/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//NewNotEqualHandler returns handler to manage NotEqual operations
|
||||||
func NewNotEqualHandler(ctx context.EvalInterface, subHandler VariableSubstitutionHandler) OperatorHandler {
|
func NewNotEqualHandler(ctx context.EvalInterface, subHandler VariableSubstitutionHandler) OperatorHandler {
|
||||||
return NotEqualHandler{
|
return NotEqualHandler{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
@ -16,11 +17,13 @@ func NewNotEqualHandler(ctx context.EvalInterface, subHandler VariableSubstituti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NotEqualHandler provides implementation to handle NotEqual Operator
|
||||||
type NotEqualHandler struct {
|
type NotEqualHandler struct {
|
||||||
ctx context.EvalInterface
|
ctx context.EvalInterface
|
||||||
subHandler VariableSubstitutionHandler
|
subHandler VariableSubstitutionHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Evaluate evaluates expression with NotEqual Operator
|
||||||
func (neh NotEqualHandler) Evaluate(key, value interface{}) bool {
|
func (neh NotEqualHandler) Evaluate(key, value interface{}) bool {
|
||||||
// substitute the variables
|
// substitute the variables
|
||||||
nKey := neh.subHandler(neh.ctx, key)
|
nKey := neh.subHandler(neh.ctx, key)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/nirmata/kyverno/pkg/engine/context"
|
"github.com/nirmata/kyverno/pkg/engine/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//OperatorHandler provides interface to manage types
|
||||||
type OperatorHandler interface {
|
type OperatorHandler interface {
|
||||||
Evaluate(key, value interface{}) bool
|
Evaluate(key, value interface{}) bool
|
||||||
validateValuewithBoolPattern(key bool, value interface{}) bool
|
validateValuewithBoolPattern(key bool, value interface{}) bool
|
||||||
|
@ -15,8 +16,10 @@ type OperatorHandler interface {
|
||||||
validateValueWithSlicePattern(key []interface{}, value interface{}) bool
|
validateValueWithSlicePattern(key []interface{}, value interface{}) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//VariableSubstitutionHandler defines the handler function for variable substitution
|
||||||
type VariableSubstitutionHandler = func(ctx context.EvalInterface, pattern interface{}) interface{}
|
type VariableSubstitutionHandler = func(ctx context.EvalInterface, pattern interface{}) interface{}
|
||||||
|
|
||||||
|
//CreateOperatorHandler returns the operator handler based on the operator used in condition
|
||||||
func CreateOperatorHandler(ctx context.EvalInterface, op kyverno.ConditionOperator, subHandler VariableSubstitutionHandler) OperatorHandler {
|
func CreateOperatorHandler(ctx context.EvalInterface, op kyverno.ConditionOperator, subHandler VariableSubstitutionHandler) OperatorHandler {
|
||||||
switch op {
|
switch op {
|
||||||
case kyverno.Equal:
|
case kyverno.Equal:
|
||||||
|
|
|
@ -96,7 +96,7 @@ func getValueQuery(ctx context.EvalInterface, valuePattern string) interface{} {
|
||||||
return newVal
|
return newVal
|
||||||
}
|
}
|
||||||
|
|
||||||
// we do not support mutliple substitution per statement for non-string types
|
// we do not support multiple substitution per statement for non-string types
|
||||||
for _, value := range varMap {
|
for _, value := range varMap {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ func (gen *Generator) Add(infos ...Info) {
|
||||||
if info.Name == "" {
|
if info.Name == "" {
|
||||||
// dont create event for resources with generateName
|
// dont create event for resources with generateName
|
||||||
// as the name is not generated yet
|
// as the name is not generated yet
|
||||||
glog.V(4).Infof("recieved info %v, not creating an event as the resource has not been assigned a name yet", info)
|
glog.V(4).Infof("received info %v, not creating an event as the resource has not been assigned a name yet", info)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
gen.queue.Add(info)
|
gen.queue.Add(info)
|
||||||
|
|
|
@ -35,7 +35,7 @@ func (c *Controller) processGR(gr kyverno.GenerateRequest) error {
|
||||||
createTime := gr.GetCreationTimestamp()
|
createTime := gr.GetCreationTimestamp()
|
||||||
if time.Since(createTime.UTC()) > timeout {
|
if time.Since(createTime.UTC()) > timeout {
|
||||||
// the GR was in state ["",Failed] for more than timeout
|
// the GR was in state ["",Failed] for more than timeout
|
||||||
glog.V(4).Infof("GR %s was not processed succesfully in %d minutes", gr.Name, timoutMins)
|
glog.V(4).Infof("GR %s was not processed successfully in %d minutes", gr.Name, timoutMins)
|
||||||
glog.V(4).Infof("delete GR %s", gr.Name)
|
glog.V(4).Infof("delete GR %s", gr.Name)
|
||||||
return c.control.Delete(gr.Name)
|
return c.control.Delete(gr.Name)
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ func deleteGeneratedResources(client *dclient.Client, gr kyverno.GenerateRequest
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ const (
|
||||||
maxRetries = 5
|
maxRetries = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//Controller manages life-cycle of generate-requests
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
// dyanmic client implementation
|
// dyanmic client implementation
|
||||||
client *dclient.Client
|
client *dclient.Client
|
||||||
|
@ -54,6 +55,7 @@ type Controller struct {
|
||||||
nsInformer informers.GenericInformer
|
nsInformer informers.GenericInformer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewController returns a new controller instance to manage generate-requests
|
||||||
func NewController(
|
func NewController(
|
||||||
kyvernoclient *kyvernoclient.Clientset,
|
kyvernoclient *kyvernoclient.Clientset,
|
||||||
client *dclient.Client,
|
client *dclient.Client,
|
||||||
|
@ -106,10 +108,10 @@ func (c *Controller) deleteGenericResource(obj interface{}) {
|
||||||
glog.Errorf("failed to Generate Requests for resource %s/%s/%s: %v", r.GetKind(), r.GetNamespace(), r.GetName(), err)
|
glog.Errorf("failed to Generate Requests for resource %s/%s/%s: %v", r.GetKind(), r.GetNamespace(), r.GetName(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// re-evaluate the GR as the resource was deleted
|
// re-evaluate the GR as the resource was deleted
|
||||||
for _, gr := range grs {
|
for _, gr := range grs {
|
||||||
c.enqueueGR(gr)
|
c.enqueueGR(gr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) deletePolicy(obj interface{}) {
|
func (c *Controller) deletePolicy(obj interface{}) {
|
||||||
|
@ -179,6 +181,7 @@ func (c *Controller) enqueue(gr *kyverno.GenerateRequest) {
|
||||||
c.queue.Add(key)
|
c.queue.Add(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Run starts the generate-request re-conciliation loop
|
||||||
func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
|
func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
|
||||||
defer utilruntime.HandleCrash()
|
defer utilruntime.HandleCrash()
|
||||||
defer c.queue.ShutDown()
|
defer c.queue.ShutDown()
|
||||||
|
|
|
@ -5,14 +5,17 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ControlInterface manages resource deletes
|
||||||
type ControlInterface interface {
|
type ControlInterface interface {
|
||||||
Delete(gr string) error
|
Delete(gr string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Control provides implementation to manage resource
|
||||||
type Control struct {
|
type Control struct {
|
||||||
client kyvernoclient.Interface
|
client kyvernoclient.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Delete deletes the specified resource
|
||||||
func (c Control) Delete(gr string) error {
|
func (c Control) Delete(gr string) error {
|
||||||
return c.client.KyvernoV1().GenerateRequests("kyverno").Delete(gr,&metav1.DeleteOptions{})
|
return c.client.KyvernoV1().GenerateRequests("kyverno").Delete(gr, &metav1.DeleteOptions{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ const (
|
||||||
maxRetries = 5
|
maxRetries = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Controller manages the life-cycle for Generate-Requests and applies generate rule
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
// dyanmic client implementation
|
// dyanmic client implementation
|
||||||
client *dclient.Client
|
client *dclient.Client
|
||||||
|
@ -58,6 +59,7 @@ type Controller struct {
|
||||||
nsInformer informers.GenericInformer
|
nsInformer informers.GenericInformer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewController returns an instance of the Generate-Request Controller
|
||||||
func NewController(
|
func NewController(
|
||||||
kyvernoclient *kyvernoclient.Clientset,
|
kyvernoclient *kyvernoclient.Clientset,
|
||||||
client *dclient.Client,
|
client *dclient.Client,
|
||||||
|
|
|
@ -2,7 +2,7 @@ package generate
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// DATA
|
// ParseFailed stores the resource that failed to parse
|
||||||
type ParseFailed struct {
|
type ParseFailed struct {
|
||||||
spec interface{}
|
spec interface{}
|
||||||
parseError error
|
parseError error
|
||||||
|
@ -12,10 +12,12 @@ func (e *ParseFailed) Error() string {
|
||||||
return fmt.Sprintf("failed to parse the resource spec %v: %v", e.spec, e.parseError.Error())
|
return fmt.Sprintf("failed to parse the resource spec %v: %v", e.spec, e.parseError.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewParseFailed returns a new ParseFailed error
|
||||||
func NewParseFailed(spec interface{}, err error) *ParseFailed {
|
func NewParseFailed(spec interface{}, err error) *ParseFailed {
|
||||||
return &ParseFailed{spec: spec, parseError: err}
|
return &ParseFailed{spec: spec, parseError: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Violation stores the rule that violated
|
||||||
type Violation struct {
|
type Violation struct {
|
||||||
rule string
|
rule string
|
||||||
err error
|
err error
|
||||||
|
@ -25,10 +27,12 @@ func (e *Violation) Error() string {
|
||||||
return fmt.Sprintf("creating Violation; error %s", e.err)
|
return fmt.Sprintf("creating Violation; error %s", e.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewViolation returns a new Violation error
|
||||||
func NewViolation(rule string, err error) *Violation {
|
func NewViolation(rule string, err error) *Violation {
|
||||||
return &Violation{rule: rule, err: err}
|
return &Violation{rule: rule, err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotFound stores the resource that was not found
|
||||||
type NotFound struct {
|
type NotFound struct {
|
||||||
kind string
|
kind string
|
||||||
namespace string
|
namespace string
|
||||||
|
@ -39,10 +43,12 @@ func (e *NotFound) Error() string {
|
||||||
return fmt.Sprintf("resource %s/%s/%s not present", e.kind, e.namespace, e.name)
|
return fmt.Sprintf("resource %s/%s/%s not present", e.kind, e.namespace, e.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewNotFound returns a new NotFound error
|
||||||
func NewNotFound(kind, namespace, name string) *NotFound {
|
func NewNotFound(kind, namespace, name string) *NotFound {
|
||||||
return &NotFound{kind: kind, namespace: namespace, name: name}
|
return &NotFound{kind: kind, namespace: namespace, name: name}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ConfigNotFound stores the config information
|
||||||
type ConfigNotFound struct {
|
type ConfigNotFound struct {
|
||||||
config interface{}
|
config interface{}
|
||||||
kind string
|
kind string
|
||||||
|
@ -54,6 +60,7 @@ func (e *ConfigNotFound) Error() string {
|
||||||
return fmt.Sprintf("configuration %v, not present in resource %s/%s/%s", e.config, e.kind, e.namespace, e.name)
|
return fmt.Sprintf("configuration %v, not present in resource %s/%s/%s", e.config, e.kind, e.namespace, e.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewConfigNotFound returns a new NewConfigNotFound error
|
||||||
func NewConfigNotFound(config interface{}, kind, namespace, name string) *ConfigNotFound {
|
func NewConfigNotFound(config interface{}, kind, namespace, name string) *ConfigNotFound {
|
||||||
return &ConfigNotFound{config: config, kind: kind, namespace: namespace, name: name}
|
return &ConfigNotFound{config: config, kind: kind, namespace: namespace, name: name}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the policy still applies to the resource
|
// check if the policy still applies to the resource
|
||||||
engineResponse := engine.GenerateNew(policyContext)
|
engineResponse := engine.Generate(policyContext)
|
||||||
if len(engineResponse.PolicyResponse.Rules) == 0 {
|
if len(engineResponse.PolicyResponse.Rules) == 0 {
|
||||||
glog.V(4).Infof("policy %s, dont not apply to resource %v", gr.Spec.Policy, gr.Spec.Resource)
|
glog.V(4).Infof("policy %s, dont not apply to resource %v", gr.Spec.Policy, gr.Spec.Resource)
|
||||||
return nil, fmt.Errorf("policy %s, dont not apply to resource %v", gr.Spec.Policy, gr.Spec.Resource)
|
return nil, fmt.Errorf("policy %s, dont not apply to resource %v", gr.Spec.Policy, gr.Spec.Resource)
|
||||||
|
|
|
@ -14,8 +14,8 @@ import (
|
||||||
func reportEvents(err error, eventGen event.Interface, gr kyverno.GenerateRequest, resource unstructured.Unstructured) {
|
func reportEvents(err error, eventGen event.Interface, gr kyverno.GenerateRequest, resource unstructured.Unstructured) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Success Events
|
// Success Events
|
||||||
// - resource -> policy rule applied succesfully
|
// - resource -> policy rule applied successfully
|
||||||
// - policy -> rule succesfully applied on resource
|
// - policy -> rule successfully applied on resource
|
||||||
events := successEvents(gr, resource)
|
events := successEvents(gr, resource)
|
||||||
eventGen.Add(events...)
|
eventGen.Add(events...)
|
||||||
return
|
return
|
||||||
|
@ -95,7 +95,7 @@ func successEvents(gr kyverno.GenerateRequest, resource unstructured.Unstructure
|
||||||
pe.Name = gr.Spec.Policy
|
pe.Name = gr.Spec.Policy
|
||||||
pe.Reason = event.PolicyApplied.String()
|
pe.Reason = event.PolicyApplied.String()
|
||||||
pe.Source = event.GeneratePolicyController
|
pe.Source = event.GeneratePolicyController
|
||||||
pe.Message = fmt.Sprintf("applied succesfully on resource %s/%s/%s", resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
pe.Message = fmt.Sprintf("applied successfully on resource %s/%s/%s", resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
||||||
events = append(events, pe)
|
events = append(events, pe)
|
||||||
|
|
||||||
// Resource
|
// Resource
|
||||||
|
@ -105,7 +105,7 @@ func successEvents(gr kyverno.GenerateRequest, resource unstructured.Unstructure
|
||||||
re.Name = resource.GetName()
|
re.Name = resource.GetName()
|
||||||
re.Reason = event.PolicyApplied.String()
|
re.Reason = event.PolicyApplied.String()
|
||||||
re.Source = event.GeneratePolicyController
|
re.Source = event.GeneratePolicyController
|
||||||
re.Message = fmt.Sprintf("policy %s succesfully applied", gr.Spec.Policy)
|
re.Message = fmt.Sprintf("policy %s successfully applied", gr.Spec.Policy)
|
||||||
events = append(events, re)
|
events = append(events, re)
|
||||||
|
|
||||||
return events
|
return events
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//StatusControlInterface provides interface to update status subresource
|
||||||
type StatusControlInterface interface {
|
type StatusControlInterface interface {
|
||||||
Failed(gr kyverno.GenerateRequest, message string, genResources []kyverno.ResourceSpec) error
|
Failed(gr kyverno.GenerateRequest, message string, genResources []kyverno.ResourceSpec) error
|
||||||
Success(gr kyverno.GenerateRequest, genResources []kyverno.ResourceSpec) error
|
Success(gr kyverno.GenerateRequest, genResources []kyverno.ResourceSpec) error
|
||||||
|
@ -16,7 +17,7 @@ type StatusControl struct {
|
||||||
client kyvernoclient.Interface
|
client kyvernoclient.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
//FailedGR sets gr status.state to failed with message
|
//Failed sets gr status.state to failed with message
|
||||||
func (sc StatusControl) Failed(gr kyverno.GenerateRequest, message string, genResources []kyverno.ResourceSpec) error {
|
func (sc StatusControl) Failed(gr kyverno.GenerateRequest, message string, genResources []kyverno.ResourceSpec) error {
|
||||||
gr.Status.State = kyverno.Failed
|
gr.Status.State = kyverno.Failed
|
||||||
gr.Status.Message = message
|
gr.Status.Message = message
|
||||||
|
@ -31,7 +32,7 @@ func (sc StatusControl) Failed(gr kyverno.GenerateRequest, message string, genRe
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuccessGR sets the gr status.state to completed and clears message
|
// Success sets the gr status.state to completed and clears message
|
||||||
func (sc StatusControl) Success(gr kyverno.GenerateRequest, genResources []kyverno.ResourceSpec) error {
|
func (sc StatusControl) Success(gr kyverno.GenerateRequest, genResources []kyverno.ResourceSpec) error {
|
||||||
gr.Status.State = kyverno.Completed
|
gr.Status.State = kyverno.Completed
|
||||||
gr.Status.Message = ""
|
gr.Status.Message = ""
|
||||||
|
|
|
@ -112,7 +112,7 @@ func applyPolicyOnRaw(policy *kyverno.ClusterPolicy, rawResource []byte, gvk *me
|
||||||
glog.Warning(r.Message)
|
glog.Warning(r.Message)
|
||||||
}
|
}
|
||||||
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
|
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
|
||||||
glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
|
glog.Infof("Mutation from policy %s has applied successfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
|
||||||
|
|
||||||
// Process Validation
|
// Process Validation
|
||||||
engineResponse := engine.Validate(engine.PolicyContext{Policy: *policy, NewResource: *resource})
|
engineResponse := engine.Validate(engine.PolicyContext{Policy: *policy, NewResource: *resource})
|
||||||
|
@ -124,7 +124,7 @@ func applyPolicyOnRaw(policy *kyverno.ClusterPolicy, rawResource []byte, gvk *me
|
||||||
}
|
}
|
||||||
return patchedResource, fmt.Errorf("policy %s on resource %s/%s not satisfied", policy.Name, rname, rns)
|
return patchedResource, fmt.Errorf("policy %s on resource %s/%s not satisfied", policy.Name, rname, rns)
|
||||||
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
|
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
|
||||||
glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
|
glog.Infof("Validation from policy %s has applied successfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return patchedResource, nil
|
return patchedResource, nil
|
||||||
|
|
|
@ -94,6 +94,8 @@ func scanDir(dir string) ([]string, error) {
|
||||||
|
|
||||||
return res[1:], nil
|
return res[1:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ConvertToUnstructured converts the resource to unstructured format
|
||||||
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
||||||
resource := &unstructured.Unstructured{}
|
resource := &unstructured.Unstructured{}
|
||||||
err := resource.UnmarshalJSON(data)
|
err := resource.UnmarshalJSON(data)
|
||||||
|
|
|
@ -98,7 +98,7 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []r
|
||||||
|
|
||||||
ns := unstructured.Unstructured{Object: unstr}
|
ns := unstructured.Unstructured{Object: unstr}
|
||||||
|
|
||||||
// get all the policies that have a generate rule and resource description satifies the namespace
|
// get all the policies that have a generate rule and resource description satisfies the namespace
|
||||||
// apply policy on resource
|
// apply policy on resource
|
||||||
policies := listpolicies(ns, nsc.pMetaStore)
|
policies := listpolicies(ns, nsc.pMetaStore)
|
||||||
var engineResponses []response.EngineResponse
|
var engineResponses []response.EngineResponse
|
||||||
|
@ -233,7 +233,7 @@ func applyPolicy(client *client.Client, resource unstructured.Unstructured, p ky
|
||||||
Client: client,
|
Client: client,
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
}
|
}
|
||||||
engineResponse := engine.GenerateNew(policyContext)
|
engineResponse := engine.Generate(policyContext)
|
||||||
// gather stats
|
// gather stats
|
||||||
gatherStat(p.Name, engineResponse.PolicyResponse)
|
gatherStat(p.Name, engineResponse.PolicyResponse)
|
||||||
//send stats
|
//send stats
|
||||||
|
|
|
@ -129,7 +129,7 @@ func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse
|
||||||
return response.EngineResponse{}, err
|
return response.EngineResponse{}, err
|
||||||
}
|
}
|
||||||
if !jsonpatch.Equal(patchedResource, rawResource) {
|
if !jsonpatch.Equal(patchedResource, rawResource) {
|
||||||
glog.V(4).Infof("policy %s rule %s condition not satisifed by existing resource", engineResponse.PolicyResponse.Policy, rule.Name)
|
glog.V(4).Infof("policy %s rule %s condition not satisfied by existing resource", engineResponse.PolicyResponse.Policy, rule.Name)
|
||||||
engineResponse.PolicyResponse.Rules[index].Success = false
|
engineResponse.PolicyResponse.Rules[index].Success = false
|
||||||
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(rule.Patches))
|
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(rule.Patches))
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ type PolicyController struct {
|
||||||
rm resourceManager
|
rm resourceManager
|
||||||
// helpers to validate against current loaded configuration
|
// helpers to validate against current loaded configuration
|
||||||
configHandler config.Interface
|
configHandler config.Interface
|
||||||
// recieves stats and aggregates details
|
// receives stats and aggregates details
|
||||||
statusAggregator *PolicyStatusAggregator
|
statusAggregator *PolicyStatusAggregator
|
||||||
// store to hold policy meta data for faster lookup
|
// store to hold policy meta data for faster lookup
|
||||||
pMetaStore policystore.UpdateInterface
|
pMetaStore policystore.UpdateInterface
|
||||||
|
@ -450,7 +450,7 @@ type RealPVControl struct {
|
||||||
Recorder record.EventRecorder
|
Recorder record.EventRecorder
|
||||||
}
|
}
|
||||||
|
|
||||||
//DeletePolicyViolation deletes the policy violation
|
//DeleteClusterPolicyViolation deletes the policy violation
|
||||||
func (r RealPVControl) DeleteClusterPolicyViolation(name string) error {
|
func (r RealPVControl) DeleteClusterPolicyViolation(name string) error {
|
||||||
return r.Client.KyvernoV1().ClusterPolicyViolations().Delete(name, &metav1.DeleteOptions{})
|
return r.Client.KyvernoV1().ClusterPolicyViolations().Delete(name, &metav1.DeleteOptions{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ func (pc *PolicyController) processExistingResources(policy kyverno.ClusterPolic
|
||||||
// apply the policy on each
|
// apply the policy on each
|
||||||
glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
|
glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
|
||||||
engineResponse := applyPolicy(policy, resource, pc.statusAggregator)
|
engineResponse := applyPolicy(policy, resource, pc.statusAggregator)
|
||||||
// get engine response for mutation & validation indipendently
|
// get engine response for mutation & validation independently
|
||||||
engineResponses = append(engineResponses, engineResponse...)
|
engineResponses = append(engineResponses, engineResponse...)
|
||||||
// post-processing, register the resource as processed
|
// post-processing, register the resource as processed
|
||||||
pc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
|
pc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
|
||||||
|
@ -225,12 +225,16 @@ func excludeResources(included map[string]unstructured.Unstructured, exclude kyv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Condition defines condition type
|
||||||
type Condition int
|
type Condition int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
//NotEvaluate to not evaluate condition
|
||||||
NotEvaluate Condition = 0
|
NotEvaluate Condition = 0
|
||||||
Process Condition = 1
|
// Process to evaluate condition
|
||||||
Skip Condition = 2
|
Process Condition = 1
|
||||||
|
// Skip to ignore/skip the condition
|
||||||
|
Skip Condition = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// merge b into a map
|
// merge b into a map
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
type PolicyStatusAggregator struct {
|
type PolicyStatusAggregator struct {
|
||||||
// time since we start aggregating the stats
|
// time since we start aggregating the stats
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
// channel to recieve stats
|
// channel to receive stats
|
||||||
ch chan PolicyStat
|
ch chan PolicyStat
|
||||||
//TODO: lock based on key, possibly sync.Map ?
|
//TODO: lock based on key, possibly sync.Map ?
|
||||||
//sync RW for policyData
|
//sync RW for policyData
|
||||||
|
@ -47,13 +47,13 @@ func (psa *PolicyStatusAggregator) Run(workers int, stopCh <-chan struct{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (psa *PolicyStatusAggregator) process() {
|
func (psa *PolicyStatusAggregator) process() {
|
||||||
// As mutation and validation are handled seperately
|
// As mutation and validation are handled separately
|
||||||
// ideally we need to combine the exection time from both for a policy
|
// ideally we need to combine the execution time from both for a policy
|
||||||
// but its tricky to detect here the type of rules policy contains
|
// but its tricky to detect here the type of rules policy contains
|
||||||
// so we dont combine the results, but instead compute the execution time for
|
// so we dont combine the results, but instead compute the execution time for
|
||||||
// mutation & validation rules seperately
|
// mutation & validation rules separately
|
||||||
for r := range psa.ch {
|
for r := range psa.ch {
|
||||||
glog.V(4).Infof("recieved policy stats %v", r)
|
glog.V(4).Infof("received policy stats %v", r)
|
||||||
psa.aggregate(r)
|
psa.aggregate(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,6 +178,7 @@ type PolicyStat struct {
|
||||||
Stats PolicyStatInfo
|
Stats PolicyStatInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//PolicyStatInfo provides statistics for policy
|
||||||
type PolicyStatInfo struct {
|
type PolicyStatInfo struct {
|
||||||
MutationExecutionTime time.Duration
|
MutationExecutionTime time.Duration
|
||||||
ValidationExecutionTime time.Duration
|
ValidationExecutionTime time.Duration
|
||||||
|
@ -187,6 +188,7 @@ type PolicyStatInfo struct {
|
||||||
Rules []RuleStatinfo
|
Rules []RuleStatinfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//RuleStatinfo provides statistics for rule
|
||||||
type RuleStatinfo struct {
|
type RuleStatinfo struct {
|
||||||
RuleName string
|
RuleName string
|
||||||
ExecutionTime time.Duration
|
ExecutionTime time.Duration
|
||||||
|
|
|
@ -8,9 +8,10 @@ import (
|
||||||
"github.com/nirmata/kyverno/pkg/engine/response"
|
"github.com/nirmata/kyverno/pkg/engine/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//GeneratePVsFromEngineResponse generate Violations from engine responses
|
||||||
func GeneratePVsFromEngineResponse(ers []response.EngineResponse) (pvInfos []Info) {
|
func GeneratePVsFromEngineResponse(ers []response.EngineResponse) (pvInfos []Info) {
|
||||||
for _, er := range ers {
|
for _, er := range ers {
|
||||||
// ignore creation of PV for resoruces that are yet to be assigned a name
|
// ignore creation of PV for resources that are yet to be assigned a name
|
||||||
if er.PolicyResponse.Resource.Name == "" {
|
if er.PolicyResponse.Resource.Name == "" {
|
||||||
glog.V(4).Infof("resource %v, has not been assigned a name, not creating a policy violation for it", er.PolicyResponse.Resource)
|
glog.V(4).Infof("resource %v, has not been assigned a name, not creating a policy violation for it", er.PolicyResponse.Resource)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -11,7 +11,7 @@ func Test_GeneratePVsFromEngineResponse_PathNotExist(t *testing.T) {
|
||||||
ers := []response.EngineResponse{
|
ers := []response.EngineResponse{
|
||||||
response.EngineResponse{
|
response.EngineResponse{
|
||||||
PolicyResponse: response.PolicyResponse{
|
PolicyResponse: response.PolicyResponse{
|
||||||
Policy: "test-substitue-variable",
|
Policy: "test-substitute-variable",
|
||||||
Resource: response.ResourceSpec{
|
Resource: response.ResourceSpec{
|
||||||
Kind: "Pod",
|
Kind: "Pod",
|
||||||
Name: "test",
|
Name: "test",
|
||||||
|
@ -36,7 +36,7 @@ func Test_GeneratePVsFromEngineResponse_PathNotExist(t *testing.T) {
|
||||||
},
|
},
|
||||||
response.EngineResponse{
|
response.EngineResponse{
|
||||||
PolicyResponse: response.PolicyResponse{
|
PolicyResponse: response.PolicyResponse{
|
||||||
Policy: "test-substitue-variable2",
|
Policy: "test-substitute-variable2",
|
||||||
Resource: response.ResourceSpec{
|
Resource: response.ResourceSpec{
|
||||||
Kind: "Pod",
|
Kind: "Pod",
|
||||||
Name: "test",
|
Name: "test",
|
||||||
|
@ -44,7 +44,7 @@ func Test_GeneratePVsFromEngineResponse_PathNotExist(t *testing.T) {
|
||||||
},
|
},
|
||||||
Rules: []response.RuleResponse{
|
Rules: []response.RuleResponse{
|
||||||
response.RuleResponse{
|
response.RuleResponse{
|
||||||
Name: "test-path-not-exist-accross-policy",
|
Name: "test-path-not-exist-across-policy",
|
||||||
Type: "Mutation",
|
Type: "Mutation",
|
||||||
Message: "referenced paths are not present: request.object.metadata.name1",
|
Message: "referenced paths are not present: request.object.metadata.name1",
|
||||||
Success: true,
|
Success: true,
|
||||||
|
|
|
@ -214,7 +214,7 @@ func (gen *Generator) processNextWorkitem() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gen *Generator) syncHandler(info Info) error {
|
func (gen *Generator) syncHandler(info Info) error {
|
||||||
glog.V(4).Infof("recieved info:%v", info)
|
glog.V(4).Infof("received info:%v", info)
|
||||||
var handler pvGenerator
|
var handler pvGenerator
|
||||||
var builder Builder
|
var builder Builder
|
||||||
builder = newPvBuilder()
|
builder = newPvBuilder()
|
||||||
|
|
|
@ -50,19 +50,19 @@ type sExpected struct {
|
||||||
type sMutation struct {
|
type sMutation struct {
|
||||||
// path to the patched resource to be compared with
|
// path to the patched resource to be compared with
|
||||||
PatchedResource string `yaml:"patchedresource,omitempty"`
|
PatchedResource string `yaml:"patchedresource,omitempty"`
|
||||||
// expected respone from the policy engine
|
// expected response from the policy engine
|
||||||
PolicyResponse response.PolicyResponse `yaml:"policyresponse"`
|
PolicyResponse response.PolicyResponse `yaml:"policyresponse"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type sValidation struct {
|
type sValidation struct {
|
||||||
// expected respone from the policy engine
|
// expected response from the policy engine
|
||||||
PolicyResponse response.PolicyResponse `yaml:"policyresponse"`
|
PolicyResponse response.PolicyResponse `yaml:"policyresponse"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type sGeneration struct {
|
type sGeneration struct {
|
||||||
// generated resources
|
// generated resources
|
||||||
GeneratedResources []kyverno.ResourceSpec `yaml:"generatedResources"`
|
GeneratedResources []kyverno.ResourceSpec `yaml:"generatedResources"`
|
||||||
// expected respone from the policy engine
|
// expected response from the policy engine
|
||||||
PolicyResponse response.PolicyResponse `yaml:"policyresponse"`
|
PolicyResponse response.PolicyResponse `yaml:"policyresponse"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ func loadScenario(t *testing.T, path string) (*scenarioT, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var testCases []scaseT
|
var testCases []scaseT
|
||||||
// load test cases seperated by '---'
|
// load test cases separated by '---'
|
||||||
// each test case defines an input & expected result
|
// each test case defines an input & expected result
|
||||||
scenariosBytes := bytes.Split(fileBytes, []byte("---"))
|
scenariosBytes := bytes.Split(fileBytes, []byte("---"))
|
||||||
for _, scenarioBytes := range scenariosBytes {
|
for _, scenarioBytes := range scenariosBytes {
|
||||||
|
@ -160,7 +160,7 @@ func runTestCase(t *testing.T, tc scaseT) bool {
|
||||||
Client: client,
|
Client: client,
|
||||||
}
|
}
|
||||||
|
|
||||||
er = engine.GenerateNew(policyContext)
|
er = engine.Generate(policyContext)
|
||||||
t.Log(("---Generation---"))
|
t.Log(("---Generation---"))
|
||||||
validateResponse(t, er.PolicyResponse, tc.Expected.Generation.PolicyResponse)
|
validateResponse(t, er.PolicyResponse, tc.Expected.Generation.PolicyResponse)
|
||||||
// Expected generate resource will be in same namesapces as resource
|
// Expected generate resource will be in same namesapces as resource
|
||||||
|
@ -219,10 +219,10 @@ func validateResponse(t *testing.T, er response.PolicyResponse, expected respons
|
||||||
}
|
}
|
||||||
// cant do deepEquals and the stats will be different, or we nil those fields and then do a comparison
|
// cant do deepEquals and the stats will be different, or we nil those fields and then do a comparison
|
||||||
// forcing only the fields that are specified to be comprared
|
// forcing only the fields that are specified to be comprared
|
||||||
// doing a field by fields comparsion will allow us to provied more details logs and granular error reporting
|
// doing a field by fields comparison will allow us to provied more details logs and granular error reporting
|
||||||
// check policy name is same :P
|
// check policy name is same :P
|
||||||
if er.Policy != expected.Policy {
|
if er.Policy != expected.Policy {
|
||||||
t.Errorf("Policy name: expected %s, recieved %s", expected.Policy, er.Policy)
|
t.Errorf("Policy name: expected %s, received %s", expected.Policy, er.Policy)
|
||||||
}
|
}
|
||||||
// compare resource spec
|
// compare resource spec
|
||||||
compareResourceSpec(t, er.Resource, expected.Resource)
|
compareResourceSpec(t, er.Resource, expected.Resource)
|
||||||
|
@ -238,7 +238,7 @@ func validateResponse(t *testing.T, er response.PolicyResponse, expected respons
|
||||||
}
|
}
|
||||||
if len(er.Rules) == len(expected.Rules) {
|
if len(er.Rules) == len(expected.Rules) {
|
||||||
// if there are rules being applied then we compare the rule response
|
// if there are rules being applied then we compare the rule response
|
||||||
// as the rules are applied in the order defined, the comparions of rules will be in order
|
// as the rules are applied in the order defined, the comparison of rules will be in order
|
||||||
for index, r := range expected.Rules {
|
for index, r := range expected.Rules {
|
||||||
compareRules(t, er.Rules[index], r)
|
compareRules(t, er.Rules[index], r)
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ func validateResponse(t *testing.T, er response.PolicyResponse, expected respons
|
||||||
func compareResourceSpec(t *testing.T, resource response.ResourceSpec, expectedResource response.ResourceSpec) {
|
func compareResourceSpec(t *testing.T, resource response.ResourceSpec, expectedResource response.ResourceSpec) {
|
||||||
// kind
|
// kind
|
||||||
if resource.Kind != expectedResource.Kind {
|
if resource.Kind != expectedResource.Kind {
|
||||||
t.Errorf("kind: expected %s, recieved %s", expectedResource.Kind, resource.Kind)
|
t.Errorf("kind: expected %s, received %s", expectedResource.Kind, resource.Kind)
|
||||||
}
|
}
|
||||||
// //TODO apiVersion
|
// //TODO apiVersion
|
||||||
// if resource.APIVersion != expectedResource.APIVersion {
|
// if resource.APIVersion != expectedResource.APIVersion {
|
||||||
|
@ -257,28 +257,28 @@ func compareResourceSpec(t *testing.T, resource response.ResourceSpec, expectedR
|
||||||
|
|
||||||
// namespace
|
// namespace
|
||||||
if resource.Namespace != expectedResource.Namespace {
|
if resource.Namespace != expectedResource.Namespace {
|
||||||
t.Errorf("namespace: expected %s, recieved %s", expectedResource.Namespace, resource.Namespace)
|
t.Errorf("namespace: expected %s, received %s", expectedResource.Namespace, resource.Namespace)
|
||||||
}
|
}
|
||||||
// name
|
// name
|
||||||
if resource.Name != expectedResource.Name {
|
if resource.Name != expectedResource.Name {
|
||||||
t.Errorf("name: expected %s, recieved %s", expectedResource.Name, resource.Name)
|
t.Errorf("name: expected %s, received %s", expectedResource.Name, resource.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareRules(t *testing.T, rule response.RuleResponse, expectedRule response.RuleResponse) {
|
func compareRules(t *testing.T, rule response.RuleResponse, expectedRule response.RuleResponse) {
|
||||||
// name
|
// name
|
||||||
if rule.Name != expectedRule.Name {
|
if rule.Name != expectedRule.Name {
|
||||||
t.Errorf("rule name: expected %s, recieved %+v", expectedRule.Name, rule.Name)
|
t.Errorf("rule name: expected %s, received %+v", expectedRule.Name, rule.Name)
|
||||||
// as the rule names dont match no need to compare the rest of the information
|
// as the rule names dont match no need to compare the rest of the information
|
||||||
}
|
}
|
||||||
// type
|
// type
|
||||||
if rule.Type != expectedRule.Type {
|
if rule.Type != expectedRule.Type {
|
||||||
t.Errorf("rule type: expected %s, recieved %s", expectedRule.Type, rule.Type)
|
t.Errorf("rule type: expected %s, received %s", expectedRule.Type, rule.Type)
|
||||||
}
|
}
|
||||||
// message
|
// message
|
||||||
// compare messages if expected rule message is not empty
|
// compare messages if expected rule message is not empty
|
||||||
if expectedRule.Message != "" && rule.Message != expectedRule.Message {
|
if expectedRule.Message != "" && rule.Message != expectedRule.Message {
|
||||||
t.Errorf("rule message: expected %s, recieved %s", expectedRule.Message, rule.Message)
|
t.Errorf("rule message: expected %s, received %s", expectedRule.Message, rule.Message)
|
||||||
}
|
}
|
||||||
// //TODO patches
|
// //TODO patches
|
||||||
// if reflect.DeepEqual(rule.Patches, expectedRule.Patches) {
|
// if reflect.DeepEqual(rule.Patches, expectedRule.Patches) {
|
||||||
|
@ -287,7 +287,7 @@ func compareRules(t *testing.T, rule response.RuleResponse, expectedRule respons
|
||||||
|
|
||||||
// success
|
// success
|
||||||
if rule.Success != expectedRule.Success {
|
if rule.Success != expectedRule.Success {
|
||||||
t.Errorf("rule success: expected %t, recieved %t", expectedRule.Success, rule.Success)
|
t.Errorf("rule success: expected %t, received %t", expectedRule.Success, rule.Success)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ func getResourceFromKind(kind string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ConvertToUnstructured converts a resource to unstructured format
|
||||||
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
||||||
resource := &unstructured.Unstructured{}
|
resource := &unstructured.Unstructured{}
|
||||||
err := resource.UnmarshalJSON(data)
|
err := resource.UnmarshalJSON(data)
|
||||||
|
|
|
@ -18,6 +18,7 @@ const (
|
||||||
rolekind = "Role"
|
rolekind = "Role"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//GetRoleRef gets the list of roles and cluster roles for the incoming api-request
|
||||||
func GetRoleRef(rbLister rbaclister.RoleBindingLister, crbLister rbaclister.ClusterRoleBindingLister, request *v1beta1.AdmissionRequest) (roles []string, clusterRoles []string, err error) {
|
func GetRoleRef(rbLister rbaclister.RoleBindingLister, crbLister rbaclister.ClusterRoleBindingLister, request *v1beta1.AdmissionRequest) (roles []string, clusterRoles []string, err error) {
|
||||||
// rolebindings
|
// rolebindings
|
||||||
roleBindings, err := rbLister.List(labels.NewSelector())
|
roleBindings, err := rbLister.List(labels.NewSelector())
|
||||||
|
|
|
@ -7,10 +7,10 @@ import (
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/wildcard"
|
"github.com/minio/minio/pkg/wildcard"
|
||||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||||
|
dclient "github.com/nirmata/kyverno/pkg/dclient"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
dclient "github.com/nirmata/kyverno/pkg/dclient"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//Contains Check if strint is contained in a list of string
|
//Contains Check if strint is contained in a list of string
|
||||||
|
@ -75,6 +75,8 @@ func CRDInstalled(discovery client.IDiscovery) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//CleanupOldCrd deletes any existing NamespacedPolicyViolation resources in cluster
|
||||||
|
// If resource violates policy, new Violations will be generated
|
||||||
func CleanupOldCrd(client *dclient.Client) {
|
func CleanupOldCrd(client *dclient.Client) {
|
||||||
gvr := client.DiscoveryClient.GetGVRFromKind("NamespacedPolicyViolation")
|
gvr := client.DiscoveryClient.GetGVRFromKind("NamespacedPolicyViolation")
|
||||||
if !reflect.DeepEqual(gvr, (schema.GroupVersionResource{})) {
|
if !reflect.DeepEqual(gvr, (schema.GroupVersionResource{})) {
|
||||||
|
|
|
@ -75,6 +75,6 @@ func (wrc *WebhookRegistrationClient) removeVerifyWebhookMutatingWebhookConfig(w
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
glog.Errorf("failed to delete verify webhook configuration %s: %v", mutatingConfig, err)
|
glog.Errorf("failed to delete verify webhook configuration %s: %v", mutatingConfig, err)
|
||||||
} else {
|
} else {
|
||||||
glog.V(4).Infof("succesfully deleted verify webhook configuration %s", mutatingConfig)
|
glog.V(4).Infof("successfully deleted verify webhook configuration %s", mutatingConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,7 +124,7 @@ func (wrc *WebhookRegistrationClient) removePolicyWebhookConfigurations() {
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
glog.Errorf("failed to delete policy webhook configuration %s: %v", validatingConfig, err)
|
glog.Errorf("failed to delete policy webhook configuration %s: %v", validatingConfig, err)
|
||||||
} else {
|
} else {
|
||||||
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", validatingConfig)
|
glog.V(4).Infof("successfully deleted policy webhook configuration %s", validatingConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mutating webhook configuration
|
// Mutating webhook configuration
|
||||||
|
@ -142,6 +142,6 @@ func (wrc *WebhookRegistrationClient) removePolicyWebhookConfigurations() {
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
glog.Errorf("failed to delete policy webhook configuration %s: %v", mutatingConfig, err)
|
glog.Errorf("failed to delete policy webhook configuration %s: %v", mutatingConfig, err)
|
||||||
} else {
|
} else {
|
||||||
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", mutatingConfig)
|
glog.V(4).Infof("successfully deleted policy webhook configuration %s", mutatingConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MutatingWebhookConfigurationKind string = "MutatingWebhookConfiguration"
|
//MutatingWebhookConfigurationKind defines the kind for MutatingWebhookConfiguration
|
||||||
|
MutatingWebhookConfigurationKind string = "MutatingWebhookConfiguration"
|
||||||
|
//ValidatingWebhookConfigurationKind defines the kind for ValidatingWebhookConfiguration
|
||||||
ValidatingWebhookConfigurationKind string = "ValidatingWebhookConfiguration"
|
ValidatingWebhookConfigurationKind string = "ValidatingWebhookConfiguration"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -260,7 +262,7 @@ func (wrc *WebhookRegistrationClient) removePolicyMutatingWebhookConfiguration(w
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
glog.Errorf("failed to delete policy webhook configuration %s: %v", mutatingConfig, err)
|
glog.Errorf("failed to delete policy webhook configuration %s: %v", mutatingConfig, err)
|
||||||
} else {
|
} else {
|
||||||
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", mutatingConfig)
|
glog.V(4).Infof("successfully deleted policy webhook configuration %s", mutatingConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +284,6 @@ func (wrc *WebhookRegistrationClient) removePolicyValidatingWebhookConfiguration
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
glog.Errorf("failed to delete policy webhook configuration %s: %v", validatingConfig, err)
|
glog.Errorf("failed to delete policy webhook configuration %s: %v", validatingConfig, err)
|
||||||
} else {
|
} else {
|
||||||
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", validatingConfig)
|
glog.V(4).Infof("successfully deleted policy webhook configuration %s", validatingConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
cache "k8s.io/client-go/tools/cache"
|
cache "k8s.io/client-go/tools/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//ResourceWebhookRegister manages the resource webhook registration
|
||||||
type ResourceWebhookRegister struct {
|
type ResourceWebhookRegister struct {
|
||||||
// pendingCreation indicates the status of resource webhook creation
|
// pendingCreation indicates the status of resource webhook creation
|
||||||
pendingCreation *abool.AtomicBool
|
pendingCreation *abool.AtomicBool
|
||||||
|
@ -21,6 +22,7 @@ type ResourceWebhookRegister struct {
|
||||||
webhookRegistrationClient *WebhookRegistrationClient
|
webhookRegistrationClient *WebhookRegistrationClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewResourceWebhookRegister returns a new instance of ResourceWebhookRegister manager
|
||||||
func NewResourceWebhookRegister(
|
func NewResourceWebhookRegister(
|
||||||
lastReqTime *checker.LastReqTime,
|
lastReqTime *checker.LastReqTime,
|
||||||
mconfigwebhookinformer mconfiginformer.MutatingWebhookConfigurationInformer,
|
mconfigwebhookinformer mconfiginformer.MutatingWebhookConfigurationInformer,
|
||||||
|
@ -35,6 +37,7 @@ func NewResourceWebhookRegister(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//RegisterResourceWebhook registers a resource webhook
|
||||||
func (rww *ResourceWebhookRegister) RegisterResourceWebhook() {
|
func (rww *ResourceWebhookRegister) RegisterResourceWebhook() {
|
||||||
// drop the request if creation is in processing
|
// drop the request if creation is in processing
|
||||||
if rww.pendingCreation.IsSet() {
|
if rww.pendingCreation.IsSet() {
|
||||||
|
@ -72,6 +75,7 @@ func (rww *ResourceWebhookRegister) RegisterResourceWebhook() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Run starts the ResourceWebhookRegister manager
|
||||||
func (rww *ResourceWebhookRegister) Run(stopCh <-chan struct{}) {
|
func (rww *ResourceWebhookRegister) Run(stopCh <-chan struct{}) {
|
||||||
// wait for cache to populate first time
|
// wait for cache to populate first time
|
||||||
if !cache.WaitForCacheSync(stopCh, rww.mwebhookconfigSynced) {
|
if !cache.WaitForCacheSync(stopCh, rww.mwebhookconfigSynced) {
|
||||||
|
@ -79,6 +83,7 @@ func (rww *ResourceWebhookRegister) Run(stopCh <-chan struct{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveResourceWebhookConfiguration removes the resource webhook configurations
|
||||||
func (rww *ResourceWebhookRegister) RemoveResourceWebhookConfiguration() error {
|
func (rww *ResourceWebhookRegister) RemoveResourceWebhookConfiguration() error {
|
||||||
var err error
|
var err error
|
||||||
// check informer cache
|
// check informer cache
|
||||||
|
|
|
@ -12,17 +12,20 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//GenerateRequests provides interface to manage generate requests
|
||||||
type GenerateRequests interface {
|
type GenerateRequests interface {
|
||||||
Create(gr kyverno.GenerateRequestSpec) error
|
Create(gr kyverno.GenerateRequestSpec) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generator defines the implmentation to mange generate request resource
|
||||||
type Generator struct {
|
type Generator struct {
|
||||||
// channel to recieve request
|
// channel to receive request
|
||||||
ch chan kyverno.GenerateRequestSpec
|
ch chan kyverno.GenerateRequestSpec
|
||||||
client *kyvernoclient.Clientset
|
client *kyvernoclient.Clientset
|
||||||
stopCh <-chan struct{}
|
stopCh <-chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewGenerator returns a new instance of Generate-Request resource generator
|
||||||
func NewGenerator(client *kyvernoclient.Clientset, stopCh <-chan struct{}) *Generator {
|
func NewGenerator(client *kyvernoclient.Clientset, stopCh <-chan struct{}) *Generator {
|
||||||
gen := &Generator{
|
gen := &Generator{
|
||||||
ch: make(chan kyverno.GenerateRequestSpec, 1000),
|
ch: make(chan kyverno.GenerateRequestSpec, 1000),
|
||||||
|
@ -32,7 +35,7 @@ func NewGenerator(client *kyvernoclient.Clientset, stopCh <-chan struct{}) *Gene
|
||||||
return gen
|
return gen
|
||||||
}
|
}
|
||||||
|
|
||||||
// blocking if channel is full
|
//Create to create generate request resoruce (blocking call if channel is full)
|
||||||
func (g *Generator) Create(gr kyverno.GenerateRequestSpec) error {
|
func (g *Generator) Create(gr kyverno.GenerateRequestSpec) error {
|
||||||
glog.V(4).Infof("create GR %v", gr)
|
glog.V(4).Infof("create GR %v", gr)
|
||||||
// Send to channel
|
// Send to channel
|
||||||
|
@ -60,7 +63,7 @@ func (g *Generator) Run(workers int) {
|
||||||
|
|
||||||
func (g *Generator) process() {
|
func (g *Generator) process() {
|
||||||
for r := range g.ch {
|
for r := range g.ch {
|
||||||
glog.V(4).Infof("recived generate request %v", r)
|
glog.V(4).Infof("received generate request %v", r)
|
||||||
if err := g.generate(r); err != nil {
|
if err := g.generate(r); err != nil {
|
||||||
glog.Errorf("Failed to create Generate Request CR: %v", err)
|
glog.Errorf("Failed to create Generate Request CR: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +78,7 @@ func (g *Generator) generate(grSpec kyverno.GenerateRequestSpec) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// -> recieving channel to take requests to create request
|
// -> receiving channel to take requests to create request
|
||||||
// use worker pattern to read and create the CR resource
|
// use worker pattern to read and create the CR resource
|
||||||
|
|
||||||
func retryCreateResource(client *kyvernoclient.Clientset, grSpec kyverno.GenerateRequestSpec) error {
|
func retryCreateResource(client *kyvernoclient.Clientset, grSpec kyverno.GenerateRequestSpec) error {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//HandleGenerate handles admission-requests for policies with generate rules
|
||||||
func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, policies []kyverno.ClusterPolicy, patchedResource []byte, roles, clusterRoles []string) (bool, string) {
|
func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, policies []kyverno.ClusterPolicy, patchedResource []byte, roles, clusterRoles []string) (bool, string) {
|
||||||
var engineResponses []response.EngineResponse
|
var engineResponses []response.EngineResponse
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
|
||||||
// engine.Generate returns a list of rules that are applicable on this resource
|
// engine.Generate returns a list of rules that are applicable on this resource
|
||||||
for _, policy := range policies {
|
for _, policy := range policies {
|
||||||
policyContext.Policy = policy
|
policyContext.Policy = policy
|
||||||
engineResponse := engine.GenerateNew(policyContext)
|
engineResponse := engine.Generate(policyContext)
|
||||||
if len(engineResponse.PolicyResponse.Rules) > 0 {
|
if len(engineResponse.PolicyResponse.Rules) > 0 {
|
||||||
// some generate rules do apply to the resource
|
// some generate rules do apply to the resource
|
||||||
engineResponses = append(engineResponses, engineResponse)
|
engineResponses = append(engineResponses, engineResponse)
|
||||||
|
|
|
@ -92,7 +92,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
|
||||||
}
|
}
|
||||||
// gather patches
|
// gather patches
|
||||||
patches = append(patches, engineResponse.GetPatches()...)
|
patches = append(patches, engineResponse.GetPatches()...)
|
||||||
glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
|
glog.V(4).Infof("Mutation from policy %s has applied successfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
|
||||||
|
|
||||||
policyContext.NewResource = engineResponse.PatchedResource
|
policyContext.NewResource = engineResponse.PatchedResource
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ func generateEvents(engineResponses []response.EngineResponse, onUpdate bool) []
|
||||||
return events
|
return events
|
||||||
}
|
}
|
||||||
if !onUpdate {
|
if !onUpdate {
|
||||||
// All policies were applied succesfully
|
// All policies were applied successfully
|
||||||
// CREATE
|
// CREATE
|
||||||
for _, er := range engineResponses {
|
for _, er := range engineResponses {
|
||||||
successRules := er.GetSuccessRules()
|
successRules := er.GetSuccessRules()
|
||||||
|
|
|
@ -140,7 +140,7 @@ func NewWebhookServer(
|
||||||
// Main server endpoint for all requests
|
// Main server endpoint for all requests
|
||||||
func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
// for every request recieved on the ep update last request time,
|
// for every request received on the ep update last request time,
|
||||||
// this is used to verify admission control
|
// this is used to verify admission control
|
||||||
ws.lastReqTime.SetTime(time.Now())
|
ws.lastReqTime.SetTime(time.Now())
|
||||||
admissionReview := ws.bodyToAdmissionReview(r, w)
|
admissionReview := ws.bodyToAdmissionReview(r, w)
|
||||||
|
@ -159,7 +159,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
||||||
request := admissionReview.Request
|
request := admissionReview.Request
|
||||||
switch r.URL.Path {
|
switch r.URL.Path {
|
||||||
case config.VerifyMutatingWebhookServicePath:
|
case config.VerifyMutatingWebhookServicePath:
|
||||||
// we do not apply filters as this endpoint is used explicity
|
// we do not apply filters as this endpoint is used explicitly
|
||||||
// to watch kyveno deployment and verify if admission control is enabled
|
// to watch kyveno deployment and verify if admission control is enabled
|
||||||
admissionReview.Response = ws.handleVerifyRequest(request)
|
admissionReview.Response = ws.handleVerifyRequest(request)
|
||||||
case config.MutatingWebhookServicePath:
|
case config.MutatingWebhookServicePath:
|
||||||
|
|
|
@ -10,13 +10,13 @@ expected:
|
||||||
- name: add-label
|
- name: add-label
|
||||||
type: Mutation
|
type: Mutation
|
||||||
messages:
|
messages:
|
||||||
- "Rule add-label: Patches succesfully applied."
|
- "Rule add-label: Patches successfully applied."
|
||||||
validation:
|
validation:
|
||||||
rules:
|
rules:
|
||||||
- name: check-image
|
- name: check-image
|
||||||
type : Validation
|
type : Validation
|
||||||
messages:
|
messages:
|
||||||
- "Rule check-image: Validation succesfully."
|
- "Rule check-image: Validation successfully."
|
||||||
---
|
---
|
||||||
input:
|
input:
|
||||||
policy: test/scenarios/cli/policy_deployment.yaml
|
policy: test/scenarios/cli/policy_deployment.yaml
|
||||||
|
@ -29,10 +29,10 @@ expected:
|
||||||
- name: add-label
|
- name: add-label
|
||||||
type: Mutation
|
type: Mutation
|
||||||
messages:
|
messages:
|
||||||
- "Rule add-label: Patches succesfully applied."
|
- "Rule add-label: Patches successfully applied."
|
||||||
validation:
|
validation:
|
||||||
rules:
|
rules:
|
||||||
- name: check-image
|
- name: check-image
|
||||||
type : Validation
|
type : Validation
|
||||||
messages:
|
messages:
|
||||||
- "Rule check-image: Validation succesfully."
|
- "Rule check-image: Validation successfully."
|
||||||
|
|
|
@ -14,11 +14,11 @@ expected:
|
||||||
- name: "Basic config generator for all namespaces"
|
- name: "Basic config generator for all namespaces"
|
||||||
type: Generation
|
type: Generation
|
||||||
messages:
|
messages:
|
||||||
- "Rule Basic config generator for all namespaces: Generation succesfully."
|
- "Rule Basic config generator for all namespaces: Generation successfully."
|
||||||
- name: "Basic clone config generator for all namespaces"
|
- name: "Basic clone config generator for all namespaces"
|
||||||
type: Generation
|
type: Generation
|
||||||
messages:
|
messages:
|
||||||
- "Rule Basic clone config generator for all namespaces: Generation succesfully."
|
- "Rule Basic clone config generator for all namespaces: Generation successfully."
|
||||||
|
|
||||||
---
|
---
|
||||||
input:
|
input:
|
||||||
|
@ -36,11 +36,11 @@ expected:
|
||||||
- name: "copy-comfigmap"
|
- name: "copy-comfigmap"
|
||||||
type: Generation
|
type: Generation
|
||||||
messages:
|
messages:
|
||||||
- "Rule copy-comfigmap: Generation succesfully."
|
- "Rule copy-comfigmap: Generation successfully."
|
||||||
- name: "zk-kafka-address"
|
- name: "zk-kafka-address"
|
||||||
type: Generation
|
type: Generation
|
||||||
messages:
|
messages:
|
||||||
- "Rule zk-kafka-address: Generation succesfully."
|
- "Rule zk-kafka-address: Generation successfully."
|
||||||
|
|
||||||
---
|
---
|
||||||
input:
|
input:
|
||||||
|
@ -55,4 +55,4 @@ expected:
|
||||||
- name: deny-all-traffic
|
- name: deny-all-traffic
|
||||||
type: Generation
|
type: Generation
|
||||||
messages:
|
messages:
|
||||||
- "Rule deny-all-traffic: Generation succesfully."
|
- "Rule deny-all-traffic: Generation successfully."
|
|
@ -10,4 +10,4 @@ expected:
|
||||||
- name: set-image-pull-policy
|
- name: set-image-pull-policy
|
||||||
type: Mutation
|
type: Mutation
|
||||||
messages:
|
messages:
|
||||||
- "Rule set-image-pull-policy: Overlay succesfully applied."
|
- "Rule set-image-pull-policy: Overlay successfully applied."
|
|
@ -10,4 +10,4 @@ expected:
|
||||||
- name: pEP
|
- name: pEP
|
||||||
type: Mutation
|
type: Mutation
|
||||||
messages:
|
messages:
|
||||||
- "Rule pEP: Patches succesfully applied."
|
- "Rule pEP: Patches successfully applied."
|
|
@ -16,4 +16,4 @@ expected:
|
||||||
- name: pEP
|
- name: pEP
|
||||||
type: Mutation
|
type: Mutation
|
||||||
success: true
|
success: true
|
||||||
message: succesfully process JSON patches
|
message: successfully process JSON patches
|
||||||
|
|
Loading…
Reference in a new issue