1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-01-20 18:52:16 +00:00

feat: generate support for namespace policy (#3498)

* feat: generate support for namespace policy

Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com>

* use policy spec instead

Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com>

* refactor the changes as per the 1.6 branch

Signed-off-by: prateekpandey14 <prateek.pandey@nirmata.com>
This commit is contained in:
Prateek Pandey 2022-03-29 17:57:21 +05:30 committed by GitHub
parent 2349d1c451
commit f7528b4343
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 145 additions and 16 deletions

View file

@ -335,6 +335,7 @@ func main() {
pclient,
client,
pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().Policies(),
pInformer.Kyverno().V1().GenerateRequests(),
eventGenerator,
kubedynamicInformer,
@ -353,6 +354,7 @@ func main() {
pclient,
client,
pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().Policies(),
pInformer.Kyverno().V1().GenerateRequests(),
kubedynamicInformer,
log.Log.WithName("GenerateCleanUpController"),

View file

@ -4,6 +4,7 @@ import (
"time"
"github.com/kyverno/kyverno/pkg/engine/common"
"k8s.io/client-go/tools/cache"
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/response"
@ -20,6 +21,54 @@ func Generate(policyContext *PolicyContext) (resp *response.EngineResponse) {
return filterRules(policyContext, policyStartTime)
}
// GenerateResponse checks for validity of generate rule on the resource
func GenerateResponse(policyContext *PolicyContext, gr kyverno.GenerateRequest) (resp *response.EngineResponse) {
policyStartTime := time.Now()
return filterGenerateRules(policyContext, gr.Spec.Policy, policyStartTime)
}
func filterGenerateRules(policyContext *PolicyContext, policyNameKey string, startTime time.Time) *response.EngineResponse {
kind := policyContext.NewResource.GetKind()
name := policyContext.NewResource.GetName()
namespace := policyContext.NewResource.GetNamespace()
apiVersion := policyContext.NewResource.GetAPIVersion()
pNamespace, pName, err := cache.SplitMetaNamespaceKey(policyNameKey)
if err != nil {
log.Log.Error(err, "failed to spilt name and namespace", policyNameKey)
}
resp := &response.EngineResponse{
PolicyResponse: response.PolicyResponse{
Policy: response.PolicySpec{
Name: pName,
Namespace: pNamespace,
},
PolicyStats: response.PolicyStats{
PolicyExecutionTimestamp: startTime.Unix(),
},
Resource: response.ResourceSpec{
Kind: kind,
Name: name,
Namespace: namespace,
APIVersion: apiVersion,
},
},
}
if policyContext.ExcludeResourceFunc(kind, namespace, name) {
log.Log.WithName("Generate").Info("resource excluded", "kind", kind, "namespace", namespace, "name", name)
return resp
}
for _, rule := range policyContext.Policy.Spec.Rules {
if ruleResp := filterRule(rule, policyContext); ruleResp != nil {
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
}
}
return resp
}
func filterRules(policyContext *PolicyContext, startTime time.Time) *response.EngineResponse {
kind := policyContext.NewResource.GetKind()
name := policyContext.NewResource.GetName()

View file

@ -49,12 +49,18 @@ type Controller struct {
// pLister can list/get cluster policy from the shared informer's store
pLister kyvernolister.ClusterPolicyLister
// npLister can list/get namespace policy from the shared informer's store
npLister kyvernolister.PolicyLister
// grLister can list/get generate request from the shared informer's store
grLister kyvernolister.GenerateRequestNamespaceLister
// pSynced returns true if the cluster policy has been synced at least once
pSynced cache.InformerSynced
// npSynced returns true if the Namespace policy has been synced at least once
npSynced cache.InformerSynced
// grSynced returns true if the generate request store has been synced at least once
grSynced cache.InformerSynced
@ -74,6 +80,7 @@ func NewController(
kyvernoclient *kyvernoclient.Clientset,
client *dclient.Client,
pInformer kyvernoinformer.ClusterPolicyInformer,
npInformer kyvernoinformer.PolicyInformer,
grInformer kyvernoinformer.GenerateRequestInformer,
dynamicInformer dynamicinformer.DynamicSharedInformerFactory,
log logr.Logger,
@ -91,9 +98,11 @@ func NewController(
c.control = Control{client: kyvernoclient}
c.pLister = pInformer.Lister()
c.npLister = npInformer.Lister()
c.grLister = grInformer.Lister().GenerateRequests(config.KyvernoNamespace)
c.pSynced = pInformer.Informer().HasSynced
c.npSynced = npInformer.Informer().HasSynced
c.grSynced = grInformer.Informer().HasSynced
gvr, err := client.DiscoveryClient.GetGVRFromKind("Namespace")
@ -248,7 +257,7 @@ func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
logger.Info("starting")
defer logger.Info("shutting down")
if !cache.WaitForCacheSync(stopCh, c.pSynced, c.grSynced) {
if !cache.WaitForCacheSync(stopCh, c.pSynced, c.grSynced, c.npSynced) {
logger.Info("failed to sync informer cache")
return
}
@ -337,16 +346,37 @@ func (c *Controller) syncGenerateRequest(key string) error {
return err
}
_, err = c.pLister.Get(gr.Spec.Policy)
pNamespace, pName, err := cache.SplitMetaNamespaceKey(gr.Spec.Policy)
if err != nil {
if !apierrors.IsNotFound(err) {
return err
}
err = c.control.Delete(gr.Name)
return err
}
if pNamespace == "" {
_, err = c.pLister.Get(pName)
if err != nil {
return err
if !apierrors.IsNotFound(err) {
return err
}
logger.Error(err, "failed to get clusterpolicy, deleting the generate request")
err = c.control.Delete(gr.Name)
if err != nil {
return err
}
return nil
}
} else {
_, err = c.npLister.Policies(pNamespace).Get(pName)
if err != nil {
if !apierrors.IsNotFound(err) {
return err
}
logger.Error(err, "failed to get policy, deleting the generate request")
err = c.control.Delete(gr.Name)
if err != nil {
return err
}
return nil
}
return nil
}
return c.processGR(*gr)
}

View file

@ -27,6 +27,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
func (c *Controller) processGR(gr *kyverno.GenerateRequest) error {
@ -120,7 +121,7 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
logger.V(3).Info("applying generate policy rule")
policyObj, err := c.policyLister.Get(gr.Spec.Policy)
policySpec, err := c.getPolicySpec(gr)
if err != nil {
if apierrors.IsNotFound(err) {
for _, e := range gr.Status.GeneratedResources {
@ -136,7 +137,6 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
}
}
}
return nil, false, nil
}
@ -188,9 +188,12 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
logger.Error(err, "unable to add image info to variables context")
}
policy := kyverno.ClusterPolicy{
Spec: policySpec,
}
policyContext := &engine.PolicyContext{
NewResource: resource,
Policy: *policyObj,
Policy: policy,
AdmissionInfo: gr.Spec.Context.UserRequestInfo,
ExcludeGroupRole: c.Config.GetExcludeGroupRole(),
ExcludeResourceFunc: c.Config.ToFilter,
@ -200,7 +203,7 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
}
// check if the policy still applies to the resource
engineResponse := engine.Generate(policyContext)
engineResponse := engine.GenerateResponse(policyContext, gr)
if len(engineResponse.PolicyResponse.Rules) == 0 {
logger.V(4).Info(doesNotApply)
return nil, false, errors.New(doesNotApply)
@ -238,6 +241,30 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
return c.applyGeneratePolicy(logger, policyContext, gr, applicableRules)
}
// getPolicySpec gets the policy spec from the ClusterPolicy/Policy
func (c *Controller) getPolicySpec(gr kyverno.GenerateRequest) (kyverno.Spec, error) {
var policySpec kyverno.Spec
pNamespace, pName, err := cache.SplitMetaNamespaceKey(gr.Spec.Policy)
if err != nil {
return policySpec, err
}
if pNamespace == "" {
policyObj, err := c.policyLister.Get(pName)
if err != nil {
return policySpec, err
}
return policyObj.Spec, err
} else {
npolicyObj, err := c.npolicyLister.Policies(pNamespace).Get(pName)
if err != nil {
return policySpec, err
}
return npolicyObj.Spec, nil
}
}
func updateStatus(statusControl StatusControlInterface, gr kyverno.GenerateRequest, err error, genResources []kyverno.ResourceSpec, precreatedResource bool) error {
if err != nil {
return statusControl.Failed(gr, err.Error(), genResources)

View file

@ -51,12 +51,18 @@ type Controller struct {
// policyLister can list/get cluster policy from the shared informer's store
policyLister kyvernolister.ClusterPolicyLister
// policyLister can list/get policy from the shared informer's store
npolicyLister kyvernolister.PolicyLister
// grLister can list/get generate request from the shared informer's store
grLister kyvernolister.GenerateRequestNamespaceLister
// policySynced returns true if the Cluster policy store has been synced at least once
policySynced cache.InformerSynced
// npolicySynced returns true if the Namespace policy store has been synced at least once
npolicySynced cache.InformerSynced
// grSynced returns true if the Generate Request store has been synced at least once
grSynced cache.InformerSynced
@ -76,6 +82,7 @@ func NewController(
kyvernoClient *kyvernoclient.Clientset,
client *dclient.Client,
policyInformer kyvernoinformer.ClusterPolicyInformer,
npolicyInformer kyvernoinformer.PolicyInformer,
grInformer kyvernoinformer.GenerateRequestInformer,
eventGen event.Interface,
dynamicInformer dynamicinformer.DynamicSharedInformerFactory,
@ -98,6 +105,8 @@ func NewController(
c.policySynced = policyInformer.Informer().HasSynced
c.npolicySynced = npolicyInformer.Informer().HasSynced
c.grSynced = grInformer.Informer().HasSynced
grInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: c.addGR,
@ -106,6 +115,7 @@ func NewController(
})
c.policyLister = policyInformer.Lister()
c.npolicyLister = npolicyInformer.Lister()
c.grLister = grInformer.Lister().GenerateRequests(config.KyvernoNamespace)
gvr, err := client.DiscoveryClient.GetGVRFromKind("Namespace")
@ -124,7 +134,7 @@ func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
defer c.queue.ShutDown()
defer c.log.Info("shutting down")
if !cache.WaitForCacheSync(stopCh, c.policySynced, c.grSynced) {
if !cache.WaitForCacheSync(stopCh, c.policySynced, c.grSynced, c.npolicySynced) {
c.log.Info("failed to sync informer cache")
return
}

View file

@ -109,6 +109,10 @@ func retryApplyResource(client *kyvernoclient.Clientset, grSpec kyverno.Generate
var i int
var err error
_, policyName, err := cache.SplitMetaNamespaceKey(grSpec.Policy)
if err != nil {
return err
}
applyResource := func() error {
gr := kyverno.GenerateRequest{
Spec: grSpec,
@ -121,7 +125,7 @@ func retryApplyResource(client *kyvernoclient.Clientset, grSpec kyverno.Generate
if action == v1beta1.Create || action == v1beta1.Update {
log.V(4).Info("querying all generate requests")
selector := labels.SelectorFromSet(labels.Set(map[string]string{
"generate.kyverno.io/policy-name": grSpec.Policy,
"generate.kyverno.io/policy-name": policyName,
"generate.kyverno.io/resource-name": grSpec.Resource.Name,
"generate.kyverno.io/resource-kind": grSpec.Resource.Kind,
"generate.kyverno.io/resource-namespace": grSpec.Resource.Namespace,
@ -154,7 +158,7 @@ func retryApplyResource(client *kyvernoclient.Clientset, grSpec kyverno.Generate
if !isExist {
gr.SetGenerateName("gr-")
gr.SetLabels(map[string]string{
"generate.kyverno.io/policy-name": grSpec.Policy,
"generate.kyverno.io/policy-name": policyName,
"generate.kyverno.io/resource-name": grSpec.Resource.Name,
"generate.kyverno.io/resource-kind": grSpec.Resource.Kind,
"generate.kyverno.io/resource-namespace": grSpec.Resource.Namespace,

View file

@ -442,8 +442,15 @@ func applyGenerateRequest(request *v1beta1.AdmissionRequest, gnGenerator generat
}
func transform(admissionRequestInfo kyverno.AdmissionRequestInfoObject, userRequestInfo kyverno.RequestInfo, er *response.EngineResponse) kyverno.GenerateRequestSpec {
var PolicyNameNamespaceKey string
if er.PolicyResponse.Policy.Namespace != "" {
PolicyNameNamespaceKey = fmt.Sprintf("%s", er.PolicyResponse.Policy.Namespace+"/"+er.PolicyResponse.Policy.Name)
} else {
PolicyNameNamespaceKey = er.PolicyResponse.Policy.Name
}
gr := kyverno.GenerateRequestSpec{
Policy: er.PolicyResponse.Policy.Name,
Policy: PolicyNameNamespaceKey,
Resource: kyverno.ResourceSpec{
Kind: er.PolicyResponse.Resource.Kind,
Namespace: er.PolicyResponse.Resource.Namespace,