From 0585b39684c73eaae1af9980d352bd7555e87d6f Mon Sep 17 00:00:00 2001 From: Yi Rae Kim Date: Mon, 6 Nov 2023 13:20:07 -0500 Subject: [PATCH] change to watching gatekeeper Signed-off-by: Yi Rae Kim --- controllers/constraintstatus_controller.go | 52 ++------------- main.go | 75 +++++++++++++++++----- 2 files changed, 63 insertions(+), 64 deletions(-) diff --git a/controllers/constraintstatus_controller.go b/controllers/constraintstatus_controller.go index 46871516..f2c8e0d2 100644 --- a/controllers/constraintstatus_controller.go +++ b/controllers/constraintstatus_controller.go @@ -6,7 +6,6 @@ import ( "sync" "time" - operatorv1alpha1 "github.com/gatekeeper/gatekeeper-operator/api/v1alpha1" "github.com/go-logr/logr" constraintV1 "github.com/open-policy-agent/frameworks/constraint/pkg/apis/templates/v1beta1" "github.com/open-policy-agent/gatekeeper/v3/apis/config/v1alpha1" @@ -91,10 +90,6 @@ func (r *ConstraintStatusReconciler) Reconcile(ctx context.Context, log := r.Log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) log.Info("Reconciling ConstraintPodStatus and Config") - // check Automatic set in gatekeeper resource - if !r.getAutomaticSet(ctx) { - return reconcile.Result{RequeueAfter: time.Minute * 3}, nil - } // Get config or create if not exist config := &v1alpha1.Config{} err := r.Get(ctx, types.NamespacedName{ @@ -156,9 +151,9 @@ func (r *ConstraintStatusReconciler) Reconcile(ctx context.Context, constraint, err := r.DynamicClient.Resource(constraintGVR).Get(ctx, constraintName, metav1.GetOptions{}) if err != nil { if apierrors.IsNotFound(err) { - r.Log.Info("The Constraint is deleted in the config resouce", constraint.GetName()) + r.Log.Info("The Constraint is deleted in the config resouce", "constraintName:", constraint.GetName()) - return reconcile.Result{}, nil + return reconcile.Result{}, nil //nolint:nilerr } return reconcile.Result{}, err @@ -166,9 +161,9 @@ func (r *ConstraintStatusReconciler) Reconcile(ctx context.Context, constraintMatchKinds, _, err := unstructured.NestedSlice(constraint.Object, "spec", "match", "kinds") if err != nil { - r.Log.V(1).Info("There is no provided kinds in contsraint", constraint.GetName()) + r.Log.V(1).Info("There is no provided kinds in contsraint", "constraintName:", constraint.GetName()) - return reconcile.Result{}, nil + return reconcile.Result{}, nil //nolint:nilerr } // remove constraintSyncOnlyEntries, err := r.getSyncOnlys(constraintMatchKinds) @@ -293,45 +288,6 @@ func (r *ConstraintStatusReconciler) refreshDiscoveryInfo() error { return nil } -// Check audit.automatic set in gatekeeper resource. -// audit.automatic is false or not set then skip adding constraintpodstatus controller -func (r *ConstraintStatusReconciler) getAutomaticSet(ctx context.Context) bool { - gatekeeper := &operatorv1alpha1.Gatekeeper{} - - if err := r.Get(ctx, types.NamespacedName{ - Namespace: "", - Name: "gatekeeper", - }, gatekeeper); err != nil { - if apierrors.IsNotFound(err) { - r.Log.V(1).Info("Gatekeeper resource is not found") - - return false - } - - r.Log.Error(err, "Getting gatekeeper resource has error") - - return false - } - - audit := gatekeeper.Spec.Audit - if audit == nil { - return false - } - - auditFromCache := audit.AuditFromCache - if auditFromCache == nil { - return false - } - - if *auditFromCache != operatorv1alpha1.AuditFromCacheAutomatic { - r.Log.V(1).Info("AuditFromCache is not set") - - return false - } - - return true -} - func (r *ConstraintStatusReconciler) getUniqSyncOnly() []v1alpha1.SyncOnlyEntry { syncOnlySet := map[v1alpha1.SyncOnlyEntry]bool{} // Add to table for unique filtering diff --git a/main.go b/main.go index a5dfbcd0..4172fd5d 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( "fmt" "os" "sync" + "time" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -265,25 +266,63 @@ func (c *ConstraintStatusInstaller) setIsInstalled(isInstalled bool) { } // Check constraintpodstatuses.status.gatekeeper.sh crd status has "NamesAccepted" condition. -func (c *ConstraintStatusInstaller) isApiAvailable(result watch.Event) bool { - crd, ok := runtime.DefaultUnstructuredConverter.ToUnstructured(result.Object) +func (c *ConstraintStatusInstaller) isApiAvailable( + mainCtx context.Context, result watch.Event, dynamicClient *dynamic.DynamicClient, +) bool { + gatekeeper, ok := runtime.DefaultUnstructuredConverter.ToUnstructured(result.Object) if ok != nil { return false } - conditions, _, err := unstructured.NestedSlice(crd, "status", "conditions") + audit, _, err := unstructured.NestedMap(gatekeeper, "spec", "audit") if err != nil { return false } + auditFromCache := audit["auditFromCache"] + if auditFromCache != string(operatorv1alpha1.AuditFromCacheAutomatic) { + setupLog.V(1).Info("auditFromCache is not Automatic. ConstraintStatus controller cannot be created") + return false + } + + // Start checking constraintpodstatuses.status.gatekeeper.sh crd exist and available + constraintCrdGVR := schema.GroupVersionResource{ + Group: "apiextensions.k8s.io", + Version: "v1", + Resource: "customresourcedefinitions", + } + + crdUnstructured, err := dynamicClient.Resource(constraintCrdGVR).Get(mainCtx, "constraintpodstatuses.status.gatekeeper.sh", + metav1.GetOptions{}) + if err != nil { + setupLog.V(1).Info("Parsing Constraintpodstatuses crd has error (crd), Wait for crd status update") + time.Sleep(time.Second) + return c.isApiAvailable(mainCtx, result, dynamicClient) + } + + conditions, _, err := unstructured.NestedSlice(crdUnstructured.Object, "status", "conditions") + if err != nil { + setupLog.V(1).Info("Parsing Constraintpodstatuses crd has error (Condition), Wait for crd status update") + time.Sleep(time.Second) + return c.isApiAvailable(mainCtx, result, dynamicClient) + } + for _, condition := range conditions { conditionObj, ok := condition.(map[string]interface{}) if !ok { continue } - conditionStatus := conditionObj["status"].(string) - conditionType := conditionObj["type"].(string) + conditionStatus, ok := conditionObj["status"].(string) + if !ok { + continue + } + + conditionType, ok := conditionObj["type"].(string) + if !ok { + continue + } + if conditionType == string(v1.NamesAccepted) && conditionStatus == "True" { return true } @@ -291,7 +330,8 @@ func (c *ConstraintStatusInstaller) isApiAvailable(result watch.Event) bool { setupLog.V(1).Info("Constraintpodstatuses crd is not ready, Wait for crd status update") - return false + time.Sleep(time.Second) + return c.isApiAvailable(mainCtx, result, dynamicClient) } // Watch the constraintpodstatuses resource in this function. When adding or Modified events comes, @@ -317,6 +357,7 @@ func (c *ConstraintStatusInstaller) watchGatekeeperInstall(mainCtx context.Conte watcher, _ = c.newWatcher(mainCtx, dynamicClient) continue } + switch result.Type { //nolint:exhaustive case watch.Modified, watch.Added: if c.getIsInstalled() { @@ -324,7 +365,7 @@ func (c *ConstraintStatusInstaller) watchGatekeeperInstall(mainCtx context.Conte } // Check constraintpodstatuses crd available - if !c.isApiAvailable(result) { + if !c.isApiAvailable(mainCtx, result, dynamicClient) { break } @@ -342,6 +383,7 @@ func (c *ConstraintStatusInstaller) watchGatekeeperInstall(mainCtx context.Conte setupLog.Error(err, "unable to start ConstraintStatus manager") } }() + case watch.Deleted: c.setIsInstalled(false) ctxCancel() @@ -355,19 +397,20 @@ func (c *ConstraintStatusInstaller) watchGatekeeperInstall(mainCtx context.Conte } } -func (c *ConstraintStatusInstaller) newWatcher(mainCtx context.Context, dynamicClient *dynamic.DynamicClient) (*toolsWatch.RetryWatcher, error) { - fieldSelector := "metadata.name=constraintpodstatuses.status.gatekeeper.sh" +func (c *ConstraintStatusInstaller) newWatcher(mainCtx context.Context, + dynamicClient *dynamic.DynamicClient, +) (*toolsWatch.RetryWatcher, error) { timeout := int64(30) - crdGVR := schema.GroupVersionResource{ - Group: "apiextensions.k8s.io", - Version: "v1", - Resource: "customresourcedefinitions", + gatekeeperGVR := schema.GroupVersionResource{ + Group: "operator.gatekeeper.sh", + Version: "v1alpha1", + Resource: "gatekeepers", } watchFunc := func(options metav1.ListOptions) (watch.Interface, error) { - return dynamicClient.Resource(crdGVR).Watch(mainCtx, - metav1.ListOptions{FieldSelector: fieldSelector, TimeoutSeconds: &timeout}) + return dynamicClient.Resource(gatekeeperGVR).Watch(mainCtx, + metav1.ListOptions{TimeoutSeconds: &timeout}) } - return toolsWatch.NewRetryWatcher(crdGVR.Version, &cache.ListWatch{WatchFunc: watchFunc}) + return toolsWatch.NewRetryWatcher(gatekeeperGVR.Version, &cache.ListWatch{WatchFunc: watchFunc}) }