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:
parent
2349d1c451
commit
f7528b4343
7 changed files with 145 additions and 16 deletions
|
@ -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"),
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue