forked from gatekeeper/gatekeeper-operator
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Set sync setting in config automatically
Signed-off-by: Yi Rae Kim <[email protected]>
- Loading branch information
1 parent
8ab1797
commit 827a608
Showing
24 changed files
with
1,075 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,3 +19,5 @@ testbin/* | |
!vendor/**/zz_generated.* | ||
|
||
ci-tools/ | ||
|
||
.vscode/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -867,6 +867,7 @@ spec: | |
enum: | ||
- Enabled | ||
- Disabled | ||
- Automatic | ||
type: string | ||
auditInterval: | ||
type: string | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -867,6 +867,7 @@ spec: | |
enum: | ||
- Enabled | ||
- Disabled | ||
- Automatic | ||
type: string | ||
auditInterval: | ||
type: string | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,260 @@ | ||
package controllers | ||
|
||
import ( | ||
"context" | ||
"strings" | ||
"time" | ||
|
||
"github.com/go-logr/logr" | ||
"github.com/open-policy-agent/gatekeeper/v3/apis/config/v1alpha1" | ||
"github.com/open-policy-agent/gatekeeper/v3/apis/status/v1beta1" | ||
"github.com/pkg/errors" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/client-go/dynamic" | ||
"k8s.io/client-go/kubernetes" | ||
"k8s.io/utils/strings/slices" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/builder" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/controller" | ||
"sigs.k8s.io/controller-runtime/pkg/event" | ||
"sigs.k8s.io/controller-runtime/pkg/predicate" | ||
"sigs.k8s.io/controller-runtime/pkg/reconcile" | ||
) | ||
|
||
var ControllerName = "contraintstatus_reconciler" | ||
|
||
type discoveryInfo struct { | ||
apiResourceList []*metav1.APIResourceList | ||
discoveryLastRefreshed time.Time | ||
} | ||
|
||
type ConstraintStatusReconciler struct { | ||
client.Client | ||
Log logr.Logger | ||
DynamicClient dynamic.Interface | ||
ClientSet *kubernetes.Clientset | ||
Scheme *runtime.Scheme | ||
Namespace string | ||
MaxConcurrentReconciles uint | ||
discoveryInfo | ||
} | ||
|
||
// SetupWithManager sets up the controller with the Manager. | ||
func (r *ConstraintStatusReconciler) SetupWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewControllerManagedBy(mgr). | ||
// The work queue prevents the same item being reconciled concurrently: | ||
// https://github.com/kubernetes-sigs/controller-runtime/issues/1416#issuecomment-899833144 | ||
WithOptions(controller.Options{MaxConcurrentReconciles: int(r.MaxConcurrentReconciles)}). | ||
// watch Config resrouce as secondary | ||
Named(ControllerName). | ||
For(&v1beta1.ConstraintPodStatus{}, | ||
builder.WithPredicates(predicate.Funcs{ | ||
// Execute this reconcile func when it is audit-constraintStatuspod | ||
// because a constraint creates 4 constraintStatuspods | ||
CreateFunc: func(e event.CreateEvent) bool { | ||
obj := e.Object.(*v1beta1.ConstraintPodStatus) | ||
|
||
return slices.Contains(obj.Status.Operations, "audit") | ||
}, | ||
UpdateFunc: func(e event.UpdateEvent) bool { | ||
oldObj := e.ObjectOld.(*v1beta1.ConstraintPodStatus) | ||
newObj := e.ObjectNew.(*v1beta1.ConstraintPodStatus) | ||
|
||
return slices.Contains(newObj.Status.Operations, "audit") && | ||
oldObj.Status.ObservedGeneration != newObj.Status.ObservedGeneration | ||
}, | ||
DeleteFunc: func(e event.DeleteEvent) bool { | ||
obj := e.Object.(*v1beta1.ConstraintPodStatus) | ||
|
||
return slices.Contains(obj.Status.Operations, "audit") | ||
}, | ||
}, | ||
)). | ||
Complete(r) | ||
} | ||
|
||
// User set gatekeeper.spec.audit.auditFromCache to Automatic, this reconcile function | ||
// collect all constraints and add found kinds which is used in contraint to config.spec.sync.syncOnly | ||
func (r *ConstraintStatusReconciler) Reconcile(ctx context.Context, | ||
request reconcile.Request, | ||
) (reconcile.Result, error) { | ||
log := r.Log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) | ||
log.Info("Reconciling ConstraintPodStatus and Config") | ||
|
||
// Get config or create if not exist | ||
config := &v1alpha1.Config{} | ||
err := r.Get(ctx, types.NamespacedName{ | ||
Namespace: r.Namespace, | ||
Name: "config", | ||
}, config) | ||
|
||
if apierrors.IsNotFound(err) { | ||
config = &v1alpha1.Config{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "config", | ||
Namespace: r.Namespace, | ||
}, | ||
} | ||
err = r.Create(ctx, config) | ||
|
||
if err != nil { | ||
return reconcile.Result{}, err | ||
} | ||
} | ||
|
||
contraintPodStatus := &v1beta1.ConstraintPodStatus{} | ||
err = r.Get(ctx, request.NamespacedName, contraintPodStatus) | ||
|
||
if err != nil { | ||
if apierrors.IsNotFound(err) { | ||
log.Error(err, "Cannot find any contraintStatus") | ||
|
||
return reconcile.Result{}, nil | ||
} | ||
// Requeue | ||
return reconcile.Result{}, err | ||
} | ||
|
||
labels := contraintPodStatus.GetLabels() | ||
|
||
constraintKind := labels["internal.gatekeeper.sh/constraint-kind"] | ||
constraintName := labels["internal.gatekeeper.sh/constraint-name"] | ||
|
||
table := map[v1alpha1.SyncOnlyEntry]bool{} | ||
// Add to table for uniqueue filtering | ||
for _, entry := range config.Spec.Sync.SyncOnly { | ||
table[entry] = true | ||
} | ||
|
||
constraintGVR := schema.GroupVersionResource{ | ||
Group: "constraints.gatekeeper.sh", | ||
Version: "v1beta1", | ||
Resource: strings.ToLower(constraintKind), | ||
} | ||
|
||
constraint, err := r.DynamicClient.Resource(constraintGVR).Get(ctx, constraintName, metav1.GetOptions{}) | ||
if err != nil { | ||
return reconcile.Result{}, err | ||
} | ||
|
||
constraintMatchKinds, _, err := unstructured.NestedSlice(constraint.Object, "spec", "match", "kinds") | ||
if err != nil { | ||
return reconcile.Result{}, err | ||
} | ||
|
||
contraintSyncOnlyEntries, err := r.getSyncOnlys(constraintMatchKinds) | ||
if err != nil { | ||
log.Error(err, "Error to get matching kind and apigroup") | ||
|
||
return reconcile.Result{}, nil | ||
} | ||
|
||
for _, ce := range *contraintSyncOnlyEntries { | ||
table[ce] = true | ||
} | ||
|
||
syncOnlys := []v1alpha1.SyncOnlyEntry{} | ||
for key := range table { | ||
syncOnlys = append(syncOnlys, key) | ||
} | ||
|
||
config.Spec.Sync.SyncOnly = syncOnlys | ||
err = r.Update(ctx, config, &client.UpdateOptions{}) | ||
|
||
if err != nil { | ||
return reconcile.Result{}, err | ||
} | ||
|
||
return reconcile.Result{}, nil | ||
} | ||
|
||
func (r *ConstraintStatusReconciler) getSyncOnlys(constraintMatchKinds []interface{}) ( | ||
*[]v1alpha1.SyncOnlyEntry, error, | ||
) { | ||
syncOnlys := []v1alpha1.SyncOnlyEntry{} | ||
|
||
for _, kind := range constraintMatchKinds { | ||
newKind := kind.(map[string]interface{}) | ||
apiGroups := newKind["apiGroups"].([]interface{}) | ||
kindsInKinds := newKind["kinds"].([]interface{}) | ||
|
||
for _, apiGroup := range apiGroups { | ||
for _, kindkind := range kindsInKinds { | ||
version, err := r.getAPIVersion(kindkind.(string), apiGroup.(string)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
syncOnlys = append(syncOnlys, v1alpha1.SyncOnlyEntry{ | ||
Group: apiGroup.(string), | ||
Version: version, | ||
Kind: kindkind.(string), | ||
}) | ||
} | ||
} | ||
} | ||
|
||
return &syncOnlys, nil | ||
} | ||
|
||
func (r *ConstraintStatusReconciler) getAPIVersion(kind string, apiGroup string) (string, error) { | ||
// cool time(10 min) to refresh discoveries | ||
if len(r.apiResourceList) == 0 || | ||
r.discoveryLastRefreshed.Add(time.Minute*10).Before(time.Now()) { | ||
err := r.refreshDiscoveryInfo() | ||
r.discoveryLastRefreshed = time.Now() | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
} | ||
|
||
for _, resc := range r.apiResourceList { | ||
groupVerison, err := schema.ParseGroupVersion(resc.GroupVersion) | ||
if err != nil { | ||
r.Log.Error(err, "Cannot Parse Group and version in getApiVersion") | ||
|
||
return "", errors.New("Error in parse Group and version in getApiVersion function") | ||
} | ||
|
||
group := groupVerison.Group | ||
version := groupVerison.Version | ||
// Consider groupversion == v1 or groupversion == app1/v1 | ||
for _, apiResource := range resc.APIResources { | ||
if apiResource.Kind == kind && group == apiGroup { | ||
return version, nil | ||
} | ||
} | ||
} | ||
|
||
// Get new discoveryInfo, when any resource is not found | ||
err := r.refreshDiscoveryInfo() | ||
r.discoveryLastRefreshed = time.Now() | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return "", errors.New("Getting discovery has error") | ||
} | ||
|
||
// Retrieve all groups and versions to add in config sync | ||
// Constraints present only kind and group so this function helps to find the version | ||
func (r *ConstraintStatusReconciler) refreshDiscoveryInfo() error { | ||
discoveryClient := r.ClientSet.Discovery() | ||
|
||
apiList, err := discoveryClient.ServerPreferredResources() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
r.apiResourceList = apiList | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -874,6 +874,7 @@ spec: | |
enum: | ||
- Enabled | ||
- Disabled | ||
- Automatic | ||
type: string | ||
auditInterval: | ||
type: string | ||
|
Oops, something went wrong.