mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 10:28:36 +00:00
generation test
This commit is contained in:
parent
aca4b13d54
commit
63a5337c9b
9 changed files with 103 additions and 195 deletions
9
main.go
9
main.go
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
clientNew "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned"
|
||||
|
@ -53,8 +54,8 @@ func main() {
|
|||
// watches CRD resources:
|
||||
// - Policy
|
||||
// - PolicyVolation
|
||||
// - cache resync time: 30 seconds
|
||||
pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 30)
|
||||
// - cache resync time: 10 seconds
|
||||
pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 10*time.Second)
|
||||
// EVENT GENERATOR
|
||||
// - generate event with retry
|
||||
egen := event.NewEventGenerator(client, pInformer.Kyverno().V1alpha1().Policies())
|
||||
|
@ -78,12 +79,12 @@ func main() {
|
|||
|
||||
// NAMESPACE INFORMER
|
||||
// watches namespace resource
|
||||
// - cache resync time: 30 seconds
|
||||
// - cache resync time: 10 seconds
|
||||
kubeClient, err := utils.NewKubeClient(clientConfig)
|
||||
if err != nil {
|
||||
glog.Fatalf("Error creating kubernetes client: %v\n", err)
|
||||
}
|
||||
kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 30)
|
||||
kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 10*time.Second)
|
||||
|
||||
// GENERATE CONTROLLER
|
||||
// - watches for Namespace resource and generates resource based on the policy generate rule
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
@ -30,11 +31,36 @@ import (
|
|||
// PolicyLister.
|
||||
type PolicyListerExpansion interface {
|
||||
GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.Policy, error)
|
||||
ListResources(selector labels.Selector) (ret []*v1alpha1.Policy, err error)
|
||||
}
|
||||
|
||||
// PolicyViolationListerExpansion allows custom methods to be added to
|
||||
// PolicyViolationLister.
|
||||
type PolicyViolationListerExpansion interface{}
|
||||
type PolicyViolationListerExpansion interface {
|
||||
// List lists all PolicyViolations in the indexer with GVK.
|
||||
// List lists all PolicyViolations in the indexer with GVK.
|
||||
ListResources(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error)
|
||||
}
|
||||
|
||||
//ListResources is a wrapper to List and adds the resource kind information
|
||||
// as the lister is specific to a gvk we can harcode the values here
|
||||
func (pvl *policyViolationLister) ListResources(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) {
|
||||
policyviolations, err := pvl.List(selector)
|
||||
for index := range policyviolations {
|
||||
policyviolations[index].SetGroupVersionKind(kyverno.SchemeGroupVersion.WithKind("PolicyViolation"))
|
||||
}
|
||||
return policyviolations, nil
|
||||
}
|
||||
|
||||
//ListResources is a wrapper to List and adds the resource kind information
|
||||
// as the lister is specific to a gvk we can harcode the values here
|
||||
func (pl *policyLister) ListResources(selector labels.Selector) (ret []*v1alpha1.Policy, err error) {
|
||||
policies, err := pl.List(selector)
|
||||
for index := range policies {
|
||||
policies[index].SetGroupVersionKind(kyverno.SchemeGroupVersion.WithKind("Policy"))
|
||||
}
|
||||
return policies, err
|
||||
}
|
||||
|
||||
func (pl *policyLister) GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.Policy, error) {
|
||||
if len(pv.Labels) == 0 {
|
||||
|
|
|
@ -93,10 +93,10 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen
|
|||
|
||||
_, err = client.CreateResource(gen.Kind, ns.GetName(), resource, false)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", gen.Kind, ns.GetKind(), ns.GetNamespace(), ns.GetName(), err)
|
||||
glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", gen.Kind, resource.GetNamespace(), resource.GetName(), err)
|
||||
return err
|
||||
}
|
||||
glog.V(4).Infof("generate rule: created resource %s/%s/%s", gen.Kind, ns.GetKind(), ns.GetNamespace(), ns.GetName())
|
||||
glog.V(4).Infof("generate rule: created resource %s/%s/%s", gen.Kind, resource.GetNamespace(), resource.GetName())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,6 @@ func checkResource(config interface{}, resource *unstructured.Unstructured) (boo
|
|||
if err != nil {
|
||||
// unable to unmarshall
|
||||
return false, err
|
||||
|
||||
}
|
||||
|
||||
var objData interface{}
|
||||
|
|
|
@ -16,7 +16,6 @@ import (
|
|||
v1 "k8s.io/api/core/v1"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
v1Informer "k8s.io/client-go/informers/core/v1"
|
||||
v1CoreLister "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
@ -33,8 +32,10 @@ type NamespaceController struct {
|
|||
syncHandler func(nsKey string) error
|
||||
enqueueNs func(ns *v1.Namespace)
|
||||
|
||||
//nsLister provides expansion to the namespace lister to inject GVK for the resource
|
||||
nsLister NamespaceListerExpansion
|
||||
// nLsister can list/get namespaces from the shared informer's store
|
||||
nsLister v1CoreLister.NamespaceLister
|
||||
// nsLister v1CoreLister.NamespaceLister
|
||||
// nsListerSynced returns true if the Namespace store has been synced at least once
|
||||
nsListerSynced cache.InformerSynced
|
||||
// pvLister can list/get policy violation from the shared informer's store
|
||||
|
@ -77,7 +78,7 @@ func NewNamespaceController(kyvernoClient *kyvernoclient.Clientset,
|
|||
nsc.enqueueNs = nsc.enqueue
|
||||
nsc.syncHandler = nsc.syncNamespace
|
||||
|
||||
nsc.nsLister = nsInformer.Lister()
|
||||
nsc.nsLister = NewNamespaceLister(nsInformer.Lister())
|
||||
nsc.nsListerSynced = nsInformer.Informer().HasSynced
|
||||
nsc.pLister = pInformer.Lister()
|
||||
nsc.pvListerSynced = pInformer.Informer().HasSynced
|
||||
|
@ -89,6 +90,7 @@ func NewNamespaceController(kyvernoClient *kyvernoclient.Clientset,
|
|||
|
||||
return nsc
|
||||
}
|
||||
|
||||
func (nsc *NamespaceController) addNamespace(obj interface{}) {
|
||||
ns := obj.(*v1.Namespace)
|
||||
glog.V(4).Infof("Adding Namespace %s", ns.Name)
|
||||
|
@ -167,7 +169,7 @@ func (nsc *NamespaceController) handleErr(err error, key interface{}) {
|
|||
}
|
||||
|
||||
if nsc.queue.NumRequeues(key) < maxRetries {
|
||||
glog.V(2).Infof("Error syncing Namespace %v: %v", key, err)
|
||||
glog.V(2).Infof("Error syncing namespace %v: %v", key, err)
|
||||
nsc.queue.AddRateLimited(key)
|
||||
return
|
||||
}
|
||||
|
@ -183,9 +185,9 @@ func (nsc *NamespaceController) syncNamespace(key string) error {
|
|||
defer func() {
|
||||
glog.V(4).Infof("Finished syncing namespace %q (%v)", key, time.Since(startTime))
|
||||
}()
|
||||
namespace, err := nsc.nsLister.Get(key)
|
||||
namespace, err := nsc.nsLister.GetResource(key)
|
||||
if errors.IsNotFound(err) {
|
||||
glog.V(2).Infof("Namespace %v has been deleted", key)
|
||||
glog.V(2).Infof("namespace %v has been deleted", key)
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
|
|
46
pkg/namespace/expansion.go
Normal file
46
pkg/namespace/expansion.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package namespace
|
||||
|
||||
import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
v1CoreLister "k8s.io/client-go/listers/core/v1"
|
||||
)
|
||||
|
||||
//NamespaceListerExpansion ...
|
||||
type NamespaceListerExpansion interface {
|
||||
v1CoreLister.NamespaceLister
|
||||
// List lists all Namespaces in the indexer.
|
||||
ListResources(selector labels.Selector) (ret []*v1.Namespace, err error)
|
||||
// GetsResource and injects gvk
|
||||
GetResource(name string) (*v1.Namespace, error)
|
||||
}
|
||||
|
||||
//NamespaceLister ...
|
||||
type NamespaceLister struct {
|
||||
v1CoreLister.NamespaceLister
|
||||
}
|
||||
|
||||
//NewNamespaceLister returns a new NamespaceLister
|
||||
func NewNamespaceLister(nsLister v1CoreLister.NamespaceLister) NamespaceListerExpansion {
|
||||
nsl := NamespaceLister{
|
||||
nsLister,
|
||||
}
|
||||
return &nsl
|
||||
}
|
||||
|
||||
//ListResources is a wrapper to List and adds the resource kind information
|
||||
// as the lister is specific to a gvk we can harcode the values here
|
||||
func (nsl *NamespaceLister) ListResources(selector labels.Selector) (ret []*v1.Namespace, err error) {
|
||||
namespaces, err := nsl.List(selector)
|
||||
for index := range namespaces {
|
||||
namespaces[index].SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("Namespace"))
|
||||
}
|
||||
return namespaces, err
|
||||
}
|
||||
|
||||
//GetResource is a wrapper to get the resource and inject the GVK
|
||||
func (nsl *NamespaceLister) GetResource(name string) (*v1.Namespace, error) {
|
||||
namespace, err := nsl.Get(name)
|
||||
namespace.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("Namespace"))
|
||||
return namespace, err
|
||||
}
|
|
@ -87,53 +87,10 @@ func buildKey(policy, pv, kind, ns, name, rv string) string {
|
|||
return policy + "/" + pv + "/" + kind + "/" + ns + "/" + name + "/" + rv
|
||||
}
|
||||
|
||||
// func (nsc *NamespaceController) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, error) {
|
||||
// var fpolicies []*v1alpha1.Policy
|
||||
// policies, err := c.policyLister.List(labels.NewSelector())
|
||||
// if err != nil {
|
||||
// glog.Error("Unable to connect to policy controller. Unable to access policies not applying GENERATION rules")
|
||||
// return nil, err
|
||||
// }
|
||||
// for _, p := range policies {
|
||||
// // Check if the policy contains a generatoin rule
|
||||
// for _, r := range p.Spec.Rules {
|
||||
// if r.Generation != nil {
|
||||
// // Check if the resource meets the description
|
||||
// data, err := json.Marshal(ns)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// continue
|
||||
// }
|
||||
// // convert types of GVK
|
||||
// nsGvk := schema.FromAPIVersionAndKind("v1", "Namespace")
|
||||
// // Hardcode as we have a informer on specified gvk
|
||||
// gvk := metav1.GroupVersionKind{Group: nsGvk.Group, Kind: nsGvk.Kind, Version: nsGvk.Version}
|
||||
// if engine.ResourceMeetsDescription(data, r.MatchResources.ResourceDescription, r.ExcludeResources.ResourceDescription, gvk) {
|
||||
// fpolicies = append(fpolicies, p)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// func (nsc *NamespaceController) processNamespace(ns *corev1.Namespace) error {
|
||||
// //Get all policies and then verify if the namespace matches any of the defined selectors
|
||||
// policies, err := c.listPolicies(ns)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// // process policy on namespace
|
||||
// for _, p := range policies {
|
||||
// c.processPolicy(ns, p)
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []info.PolicyInfo {
|
||||
var policyInfos []info.PolicyInfo
|
||||
// convert to unstructured
|
||||
unstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(namespace)
|
||||
unstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&namespace)
|
||||
if err != nil {
|
||||
glog.Infof("unable to convert to unstructured, not processing any policies: %v", err)
|
||||
return policyInfos
|
||||
|
@ -142,9 +99,8 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []i
|
|||
|
||||
ns := unstructured.Unstructured{Object: unstr}
|
||||
|
||||
// get all the policies that have a generate rule and apply on the namespace
|
||||
// get all the policies that have a generate rule and resource description satifies the namespace
|
||||
// apply policy on resource
|
||||
|
||||
policies := listpolicies(ns, nsc.pLister)
|
||||
for _, policy := range policies {
|
||||
// pre-processing, check if the policy and resource version has been processed before
|
||||
|
@ -162,7 +118,7 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []i
|
|||
|
||||
func listpolicies(ns unstructured.Unstructured, pLister lister.PolicyLister) []*kyverno.Policy {
|
||||
var filteredpolicies []*kyverno.Policy
|
||||
glog.V(4).Infof("listing policies that namespace %s", ns.GetName())
|
||||
glog.V(4).Infof("listing policies for namespace %s", ns.GetName())
|
||||
policies, err := pLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
glog.Errorf("failed to get list policies: %v", err)
|
||||
|
@ -175,13 +131,13 @@ func listpolicies(ns unstructured.Unstructured, pLister lister.PolicyLister) []*
|
|||
}
|
||||
ok := engine.MatchesResourceDescription(ns, rule)
|
||||
if !ok {
|
||||
glog.V(4).Infof("namespace %s does not satisfy the resource description for the rule ", ns.GetName())
|
||||
glog.V(4).Infof("namespace %s does not satisfy the resource description for the policy %s rule %s", ns.GetName(), policy.Name, rule.Name)
|
||||
continue
|
||||
}
|
||||
glog.V(4).Infof("namespace %s satisfies resource description for policy %s rule %s", ns.GetName(), policy.Name, rule.Name)
|
||||
filteredpolicies = append(filteredpolicies, policy)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredpolicies
|
||||
}
|
||||
|
||||
|
@ -197,125 +153,3 @@ func applyPolicy(client *client.Client, resource unstructured.Unstructured, poli
|
|||
|
||||
return policyInfo
|
||||
}
|
||||
|
||||
// // func (nsc *NamespaceController) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, error) {
|
||||
// // var fpolicies []*v1alpha1.Policy
|
||||
// // policies, err := c.policyLister.List(labels.NewSelector())
|
||||
// // if err != nil {
|
||||
// // glog.Error("Unable to connect to policy controller. Unable to access policies not applying GENERATION rules")
|
||||
// // return nil, err
|
||||
// // }
|
||||
// // for _, p := range policies {
|
||||
// // // Check if the policy contains a generatoin rule
|
||||
// // for _, r := range p.Spec.Rules {
|
||||
// // if r.Generation != nil {
|
||||
// // // Check if the resource meets the description
|
||||
// // data, err := json.Marshal(ns)
|
||||
// // if err != nil {
|
||||
// // glog.Error(err)
|
||||
// // continue
|
||||
// // }
|
||||
// // // convert types of GVK
|
||||
// // nsGvk := schema.FromAPIVersionAndKind("v1", "Namespace")
|
||||
// // // Hardcode as we have a informer on specified gvk
|
||||
// // gvk := metav1.GroupVersionKind{Group: nsGvk.Group, Kind: nsGvk.Kind, Version: nsGvk.Version}
|
||||
// // if engine.ResourceMeetsDescription(data, r.MatchResources.ResourceDescription, r.ExcludeResources.ResourceDescription, gvk) {
|
||||
// // fpolicies = append(fpolicies, p)
|
||||
// // break
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // return fpolicies, nil
|
||||
// // }
|
||||
|
||||
// func (nsc *NamespaceController) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) {
|
||||
// var eventInfo *event.Info
|
||||
// var onViolation bool
|
||||
// var msg string
|
||||
|
||||
// policyInfo := info.NewPolicyInfo(p.Name,
|
||||
// "Namespace",
|
||||
// ns.Name,
|
||||
// "",
|
||||
// p.Spec.ValidationFailureAction) // Namespace has no namespace..WOW
|
||||
|
||||
// // convert to unstructured
|
||||
// unstrMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// return
|
||||
// }
|
||||
// unstObj := unstructured.Unstructured{Object: unstrMap}
|
||||
// ruleInfos := engine.Generate(c.client, p, unstObj)
|
||||
// policyInfo.AddRuleInfos(ruleInfos)
|
||||
|
||||
// // generate annotations on namespace
|
||||
// c.createAnnotations(policyInfo)
|
||||
// //TODO generate namespace on created resources
|
||||
|
||||
// if !policyInfo.IsSuccessful() {
|
||||
// glog.Infof("Failed to apply policy %s on resource %s %s", p.Name, ns.Kind, ns.Name)
|
||||
// for _, r := range ruleInfos {
|
||||
// glog.Warning(r.Msgs)
|
||||
|
||||
// if msg = strings.Join(r.Msgs, " "); strings.Contains(msg, "rule configuration not present in resource") {
|
||||
// onViolation = true
|
||||
// msg = fmt.Sprintf(`Resource creation violates generate rule '%s' of policy '%s'`, r.Name, policyInfo.Name)
|
||||
// }
|
||||
// }
|
||||
|
||||
// if onViolation {
|
||||
// glog.Infof("Adding violation for generation rule of policy %s\n", policyInfo.Name)
|
||||
// // Policy Violation
|
||||
// v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.FailedRules())
|
||||
// c.violationBuilder.Add(v)
|
||||
// } else {
|
||||
// // Event
|
||||
// eventInfo = event.NewEvent(policyKind, "", policyInfo.Name, event.RequestBlocked,
|
||||
// event.FPolicyApplyBlockCreate, policyInfo.RName, policyInfo.GetRuleNames(false))
|
||||
|
||||
// glog.V(2).Infof("Request blocked event info has prepared for %s/%s\n", policyKind, policyInfo.Name)
|
||||
|
||||
// c.eventController.Add(eventInfo)
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
// glog.Infof("Generation from policy %s has succesfully applied to %s/%s", p.Name, policyInfo.RKind, policyInfo.RName)
|
||||
|
||||
// eventInfo = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName,
|
||||
// event.PolicyApplied, event.SRulesApply, policyInfo.GetRuleNames(true), policyInfo.Name)
|
||||
|
||||
// glog.V(2).Infof("Success event info has prepared for %s/%s\n", policyInfo.RKind, policyInfo.RName)
|
||||
|
||||
// c.eventController.Add(eventInfo)
|
||||
// }
|
||||
|
||||
// func (nsc *NamespaceController) createAnnotations(pi *info.PolicyInfo) {
|
||||
// //get resource
|
||||
// obj, err := c.client.GetResource(pi.RKind, pi.RNamespace, pi.RName)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// return
|
||||
// }
|
||||
// // add annotation for policy application
|
||||
// ann := obj.GetAnnotations()
|
||||
// // Generation rules
|
||||
// ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Generation)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// return
|
||||
// }
|
||||
// if gpatch == nil {
|
||||
// // nothing to patch
|
||||
// return
|
||||
// }
|
||||
// // add the anotation to the resource
|
||||
// _, err = c.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, gpatch)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -113,7 +113,8 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.
|
|||
|
||||
// resource manager
|
||||
// rebuild after 300 seconds/ 5 mins
|
||||
pc.rm = NewResourceManager(300)
|
||||
//TODO: pass the time in seconds instead of converting it internally
|
||||
pc.rm = NewResourceManager(30)
|
||||
|
||||
return &pc, nil
|
||||
}
|
||||
|
|
|
@ -57,7 +57,6 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
|
|||
// continue
|
||||
// }
|
||||
//TODO: check if the GVK information is present in the request of we set it explicity here ?
|
||||
glog.V(4).Infof("GVK is %v", resource.GroupVersionKind())
|
||||
// resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
//TODO: passing policy value as we dont wont to modify the policy
|
||||
|
||||
|
|
|
@ -285,11 +285,11 @@ func constructWebhook(name, servicePath string, caData []byte, validation bool)
|
|||
admregapi.Create,
|
||||
admregapi.Update,
|
||||
}
|
||||
// Add operation DELETE for validation
|
||||
if validation {
|
||||
operationtypes = append(operationtypes, admregapi.Delete)
|
||||
// // Add operation DELETE for validation
|
||||
// if validation {
|
||||
// operationtypes = append(operationtypes, admregapi.Delete)
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
return admregapi.Webhook{
|
||||
Name: name,
|
||||
|
@ -334,10 +334,10 @@ func constructDebugWebhook(name, url string, caData []byte, validation bool) adm
|
|||
admregapi.Create,
|
||||
admregapi.Update,
|
||||
}
|
||||
// Add operation DELETE for validation
|
||||
if validation {
|
||||
operationtypes = append(operationtypes, admregapi.Delete)
|
||||
}
|
||||
// // Add operation DELETE for validation
|
||||
// if validation {
|
||||
// operationtypes = append(operationtypes, admregapi.Delete)
|
||||
// }
|
||||
|
||||
return admregapi.Webhook{
|
||||
Name: name,
|
||||
|
|
Loading…
Add table
Reference in a new issue