Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ test: manifests generate fmt vet envtest ## Run tests.
github.com/StarRocks/starrocks-kubernetes-operator/pkg/controllers/... \
github.com/StarRocks/starrocks-kubernetes-operator/pkg/k8sutils/... \
github.com/StarRocks/starrocks-kubernetes-operator/pkg/subcontrollers/... \
github.com/StarRocks/starrocks-kubernetes-operator/pkg/predicates/... \
-coverprofile=coverage.data -timeout 30m || return 1
@go tool cover -func=coverage.data

Expand Down
6 changes: 4 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var (
_enableLeaderElection bool
_probeAddr string
_namespace string
_denyList string
)

func main() {
Expand All @@ -48,6 +49,7 @@ func main() {
"restricts the manager's cache to watch objects in the desired namespace. Defaults to all namespaces.")
flag.StringVar(&config.DNSDomainSuffix, "dns-domain-suffix", "cluster.local", "The suffix of the dns domain in k8s")
flag.BoolVar(&config.VolumeNameWithHash, "volume-name-with-hash", true, "Add a hash to the volume name")
flag.StringVar(&_denyList, "deny-list", "", "Comma-separated list of namespaces to exclude from reconciliation")

// Set up logger.
opts := zap.Options{}
Expand Down Expand Up @@ -76,12 +78,12 @@ func main() {
}

// setup all reconciles
if err := controllers.SetupClusterReconciler(mgr); err != nil {
if err := controllers.SetupClusterReconciler(mgr, _denyList); err != nil {
logger.Error(err, "unable to set up cluster reconciler")
os.Exit(1)
}

if err := controllers.SetupWarehouseReconciler(mgr, _namespace); err != nil {
if err := controllers.SetupWarehouseReconciler(mgr, _namespace, _denyList); err != nil {
logger.Error(err, "unable to set up warehouse reconciler")
os.Exit(1)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ spec:
- --dns-domain-suffix={{ .Values.starrocksOperator.dnsDomainSuffix }}
{{- end }}
- --volume-name-with-hash={{ .Values.starrocksOperator.volumeNameWithHash }}
{{- if .Values.starrocksOperator.denyList }}
- --deny-list={{ .Values.starrocksOperator.denyList }}
{{- end }}
env:
- name: TZ
value: {{ .Values.timeZone }}
Expand Down
7 changes: 7 additions & 0 deletions helm-charts/charts/kube-starrocks/charts/operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ starrocksOperator:
# the operator watching all namespaces uses too many memory resources, you can set this value.
# Defaults to all namespaces.
watchNamespace: ""
# Comma-separated list of namespaces to exclude from reconciliation.
# When specified, the operator will not reconcile StarRocks resources in these namespaces.
# This is useful when multiple operators manage different sets of namespaces.
# Note: This is different from watchNamespace - watchNamespace limits what the operator watches,
# while denyList excludes specific namespaces from reconciliation even if watching all namespaces.
# Example: "kube-system,kube-public,monitoring"
denyList: ""
# Additional operator container environment variables
# You specify this manually like you would a raw deployment manifest.
# Ref: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/
Expand Down
7 changes: 7 additions & 0 deletions helm-charts/charts/kube-starrocks/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ operator:
# the operator watching all namespaces uses too many memory resources, you can set this value.
# Defaults to all namespaces.
watchNamespace: ""
# Comma-separated list of namespaces to exclude from reconciliation.
# When specified, the operator will not reconcile StarRocks resources in these namespaces.
# This is useful when multiple operators manage different sets of namespaces.
# Note: This is different from watchNamespace - watchNamespace limits what the operator watches,
# while denyList excludes specific namespaces from reconciliation even if watching all namespaces.
# Example: "kube-system,kube-public,monitoring"
denyList: ""
# Additional operator container environment variables
# You specify this manually like you would a raw deployment manifest.
# Ref: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/
Expand Down
9 changes: 7 additions & 2 deletions pkg/controllers/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"

srapi "github.com/StarRocks/starrocks-kubernetes-operator/pkg/apis/starrocks/v1"
"github.com/StarRocks/starrocks-kubernetes-operator/pkg/predicates"
"github.com/StarRocks/starrocks-kubernetes-operator/pkg/subcontrollers"
"github.com/StarRocks/starrocks-kubernetes-operator/pkg/subcontrollers/be"
"github.com/StarRocks/starrocks-kubernetes-operator/pkg/subcontrollers/cn"
"github.com/StarRocks/starrocks-kubernetes-operator/pkg/subcontrollers/fe"
"github.com/StarRocks/starrocks-kubernetes-operator/pkg/subcontrollers/feproxy"
)

func SetupClusterReconciler(mgr ctrl.Manager) error {
func SetupClusterReconciler(mgr ctrl.Manager, denyList string) error {
feController := fe.New(mgr.GetClient(), mgr.GetEventRecorderFor)
beController := be.New(mgr.GetClient(), mgr.GetEventRecorderFor)
cnController := cn.New(mgr.GetClient(), mgr.GetEventRecorderFor)
Expand All @@ -30,6 +31,7 @@ func SetupClusterReconciler(mgr ctrl.Manager) error {
Client: mgr.GetClient(),
Recorder: mgr.GetEventRecorderFor("starrockscluster-controller"),
Scs: subcs,
denyList: denyList,
}

if err := reconciler.SetupWithManager(mgr); err != nil {
Expand All @@ -49,6 +51,7 @@ func (r *StarRocksClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
Owns(&appsv1.Deployment{}).
Owns(&corev1.ConfigMap{}).
Owns(&corev1.Service{}).
WithEventFilter(predicates.NewGenericPredicates(r.denyList)).
Complete(r)
}

Expand All @@ -57,7 +60,7 @@ func (r *StarRocksClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
// 1. Warehouse CRD is an optional feature, and user may not install it.
// 2. We try to use list Warehouses operation to check if Warehouse CRD exists or not.
// 3. By Default, It needs the cluster scope permission.
func SetupWarehouseReconciler(mgr ctrl.Manager, namespace string) error {
func SetupWarehouseReconciler(mgr ctrl.Manager, namespace string, denyList string) error {
var listOpts []client.ListOption
if namespace != "" {
listOpts = append(listOpts, client.InNamespace(namespace))
Expand All @@ -75,6 +78,7 @@ func SetupWarehouseReconciler(mgr ctrl.Manager, namespace string) error {
Client: mgr.GetClient(),
recorder: mgr.GetEventRecorderFor("starrockswarehouse-controller"),
subControllers: []subcontrollers.WarehouseSubController{cn.New(mgr.GetClient(), mgr.GetEventRecorderFor)},
denyList: denyList,
}
if err := reconciler.SetupWithManager(mgr); err != nil {
return err
Expand All @@ -92,5 +96,6 @@ func (r *StarRocksWarehouseReconciler) SetupWithManager(mgr ctrl.Manager) error
Owns(&appsv1.StatefulSet{}).
Owns(&corev1.ConfigMap{}).
Owns(&corev1.Service{}).
WithEventFilter(predicates.NewGenericPredicates(r.denyList)).
Complete(r)
}
4 changes: 2 additions & 2 deletions pkg/controllers/controllers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestSetupClusterReconciler(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := SetupClusterReconciler(tt.args.mgr); (err != nil) != tt.wantErr {
if err := SetupClusterReconciler(tt.args.mgr, ""); (err != nil) != tt.wantErr {
t.Errorf("SetupClusterReconciler() error = %v, wantErr %v", err, tt.wantErr)
}
})
Expand Down Expand Up @@ -93,7 +93,7 @@ func TestSetupWarehouseReconciler(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := SetupWarehouseReconciler(tt.args.mgr, tt.args.namespace); (err != nil) != tt.wantErr {
if err := SetupWarehouseReconciler(tt.args.mgr, tt.args.namespace, ""); (err != nil) != tt.wantErr {
t.Errorf("SetupWarehouseReconciler() error = %v, wantErr %v", err, tt.wantErr)
}
})
Expand Down
1 change: 1 addition & 0 deletions pkg/controllers/starrockscluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type StarRocksClusterReconciler struct {
client.Client
Recorder record.EventRecorder
Scs []subcontrollers.ClusterSubController
denyList string
}

// +kubebuilder:rbac:groups=starrocks.com,resources=starrocksclusters,verbs=get;list;watch;create;update;patch;delete
Expand Down
1 change: 1 addition & 0 deletions pkg/controllers/starrockswarehouse_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type StarRocksWarehouseReconciler struct {
client.Client
recorder record.EventRecorder
subControllers []subcontrollers.WarehouseSubController
denyList string
}

// +kubebuilder:rbac:groups=starrocks.com,resources=starrockswarehouses,verbs=get;list;watch;create;update;patch;delete
Expand Down
110 changes: 110 additions & 0 deletions pkg/predicates/predicates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package predicates

import (
"fmt"
"strings"

"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
)

const (
// ignoredAnnotation is the annotation key that marks an object as ignored by the operator
ignoredAnnotation = "starrocks.com/ignored"
)

// GenericPredicates implements predicate.Predicate for filtering events.
// The deny list is cached at initialization time for better performance.
type GenericPredicates struct {
predicate.Funcs
denyList map[string]struct{}
}

// NewGenericPredicates creates a new GenericPredicates with the given deny list.
// The denyList parameter is a comma-separated string of namespace names.
func NewGenericPredicates(denyList string) GenericPredicates {
denyMap := make(map[string]struct{})
if denyList != "" {
for _, ns := range strings.Split(denyList, ",") {
trimmed := strings.TrimSpace(ns)
if trimmed != "" {
denyMap[trimmed] = struct{}{}
}
}
}
return GenericPredicates{denyList: denyMap}
}

// Create returns true if the Create event should be processed
func (gp GenericPredicates) Create(e event.CreateEvent) bool {
return gp.shouldReconcile(e.Object)
}

// Update returns true if the Update event should be processed
func (gp GenericPredicates) Update(e event.UpdateEvent) bool {
return gp.shouldReconcile(e.ObjectNew)
}

// Delete returns true if the Delete event should be processed
func (gp GenericPredicates) Delete(e event.DeleteEvent) bool {
return gp.shouldReconcile(e.Object)
}

// Generic returns true if the Generic event should be processed
func (gp GenericPredicates) Generic(e event.GenericEvent) bool {
return gp.shouldReconcile(e.Object)
}

// shouldReconcile checks if an object should be reconciled based on namespace and annotation filters
func (gp GenericPredicates) shouldReconcile(obj client.Object) bool {
if obj == nil {
return false
}

// Check namespace deny list
if !gp.isNamespaceAllowed(obj) {
return false
}

// Check ignored annotation
if !isObjectAllowed(obj) {
return false
}

return true
}

// isNamespaceAllowed returns true if the object's namespace is not in the deny list
func (gp GenericPredicates) isNamespaceAllowed(obj client.Object) bool {
if len(gp.denyList) == 0 {
return true
}

if _, denied := gp.denyList[obj.GetNamespace()]; denied {
logger := log.Log.WithName("predicates")
logger.Info("starrocks operator will not reconcile namespace, update --deny-list to reconcile",
"namespace", obj.GetNamespace())
return false
}
return true
}

// isObjectAllowed returns true if the object does not have the ignored annotation set to "true"
func isObjectAllowed(obj client.Object) bool {
if ignoredStatus := obj.GetAnnotations()[ignoredAnnotation]; ignoredStatus == "true" {
objType := "StarRocks resource"
if runtimeObj, ok := obj.(runtime.Object); ok {
objType = fmt.Sprintf("%T", runtimeObj)
}
logger := log.Log.WithName("predicates")
logger.Info("starrocks operator will not reconcile ignored resource, remove annotation to reconcile",
"type", objType,
"namespace", obj.GetNamespace(),
"name", obj.GetName())
return false
}
return true
}
Loading