Skip to content
Merged
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
31 changes: 29 additions & 2 deletions api/disaggregated/v1/disaggregatedcluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import (
"fmt"

"k8s.io/apimachinery/pkg/runtime"
kerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"strings"
)

// log is for logging in this package.
Expand Down Expand Up @@ -53,14 +55,22 @@ var _ webhook.CustomValidator = &DorisDisaggregatedCluster{}
func (ddc *DorisDisaggregatedCluster) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
klog.Info("validate create", "name", ddc.Name)

return nil, ddc.validateFEReplicas()
if errs := ddc.validate(); len(errs) != 0 {
return nil, kerrors.NewAggregate(errs)
}

return nil, nil
}

// ValidateUpdate implements webhook.Validator so a unnamedwatches will be registered for the type
func (ddc *DorisDisaggregatedCluster) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
klog.Info("validate update", "name", ddc.Name)

return nil, ddc.validateFEReplicas()
if errs := ddc.validate(); len(errs) != 0 {
return nil, kerrors.NewAggregate(errs)
}

return nil, nil
}

// ValidateDelete implements webhook.Validator so a unnamedwatches will be registered for the type
Expand All @@ -71,6 +81,23 @@ func (ddc *DorisDisaggregatedCluster) ValidateDelete(ctx context.Context, obj ru
return nil, nil
}

func (ddc *DorisDisaggregatedCluster) validate() []error {
var errs []error
errs = append(errs, ddc.validateManagementUser()...)
if err := ddc.validateFEReplicas(); err != nil {
errs = append(errs, err)
}
return errs
}

func (ddc *DorisDisaggregatedCluster) validateManagementUser() []error {
if ddc.Spec.AdminUser == nil || !strings.EqualFold(ddc.Spec.AdminUser.Name, "admin") {
return nil
}

return []error{fmt.Errorf("'adminUser.name' error: admin is not supported as management user, use root or a dedicated user with NODE_PRIV")}
}

func (ddc *DorisDisaggregatedCluster) validateFEReplicas() error {
if ddc.Spec.FeSpec.Replicas == nil {
return nil
Expand Down
32 changes: 32 additions & 0 deletions api/disaggregated/v1/disaggregatedcluster_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,38 @@ import (
"testing"
)

func TestDorisDisaggregatedClusterRejectsAdminManagementUser(t *testing.T) {
cluster := &DorisDisaggregatedCluster{
Spec: DorisDisaggregatedClusterSpec{
AdminUser: &AdminUser{Name: "admin"},
},
}

if _, err := cluster.ValidateCreate(context.Background(), cluster); err == nil {
t.Fatal("expected admin management user to be rejected on create")
}

if _, err := cluster.ValidateUpdate(context.Background(), cluster, cluster); err == nil {
t.Fatal("expected admin management user to be rejected on update")
}
}

func TestDorisDisaggregatedClusterAllowsNonAdminManagementUser(t *testing.T) {
cluster := &DorisDisaggregatedCluster{
Spec: DorisDisaggregatedClusterSpec{
AdminUser: &AdminUser{Name: "doris_operator"},
},
}

if _, err := cluster.ValidateCreate(context.Background(), cluster); err != nil {
t.Fatalf("expected non-admin management user to be allowed on create: %v", err)
}

if _, err := cluster.ValidateUpdate(context.Background(), cluster, cluster); err != nil {
t.Fatalf("expected non-admin management user to be allowed on update: %v", err)
}
}

func TestDorisDisaggregatedClusterValidateFEReplicas(t *testing.T) {
replicas := int32(1)
electionNumber := int32(2)
Expand Down
15 changes: 14 additions & 1 deletion api/doris/v1/doriscluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"strings"
)

// log is for logging in this package.
Expand Down Expand Up @@ -71,14 +72,18 @@ var _ webhook.CustomValidator = &DorisCluster{}
func (r *DorisCluster) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
klog.Info("validate create", "name", r.Name)

// TODO(user): fill in your validation logic upon object creation.
if errs := r.validateManagementUser(); len(errs) != 0 {
return nil, kerrors.NewAggregate(errs)
}

return nil, nil
}

// ValidateUpdate implements webhook.Validator so a unnamedwatches will be registered for the type
func (r *DorisCluster) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
klog.Info("validate update", "name", r.Name)
var errors []error
errors = append(errors, r.validateManagementUser()...)
// fe FeSpec.Replicas must greater than or equal to FeSpec.ElectionNumber
if *r.Spec.FeSpec.Replicas < r.GetElectionNumber() {
errors = append(errors, fmt.Errorf("'FeSpec.Replicas' error: the number of FeSpec.Replicas should greater than or equal to FeSpec.ElectionNumber"))
Expand All @@ -98,3 +103,11 @@ func (r *DorisCluster) ValidateDelete(ctx context.Context, obj runtime.Object) (
// TODO(user): fill in your validation logic upon object deletion.
return nil, nil
}

func (r *DorisCluster) validateManagementUser() []error {
if r.Spec.AdminUser == nil || !strings.EqualFold(r.Spec.AdminUser.Name, "admin") {
return nil
}

return []error{fmt.Errorf("'adminUser.name' error: admin is not supported as management user, use root or a dedicated user with NODE_PRIV")}
}
59 changes: 59 additions & 0 deletions api/doris/v1/doriscluster_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package v1

import (
"context"
"testing"
)

func TestDorisClusterRejectsAdminManagementUser(t *testing.T) {
cluster := &DorisCluster{
Spec: DorisClusterSpec{
AdminUser: &AdminUser{Name: "admin"},
},
}

if _, err := cluster.ValidateCreate(context.Background(), cluster); err == nil {
t.Fatal("expected admin management user to be rejected on create")
}

replicas := int32(3)
cluster.Spec.FeSpec = &FeSpec{BaseSpec: BaseSpec{Replicas: &replicas}}
if _, err := cluster.ValidateUpdate(context.Background(), cluster, cluster); err == nil {
t.Fatal("expected admin management user to be rejected on update")
}
}

func TestDorisClusterAllowsNonAdminManagementUser(t *testing.T) {
cluster := &DorisCluster{
Spec: DorisClusterSpec{
AdminUser: &AdminUser{Name: "doris_operator"},
},
}

if _, err := cluster.ValidateCreate(context.Background(), cluster); err != nil {
t.Fatalf("expected non-admin management user to be allowed on create: %v", err)
}

replicas := int32(3)
cluster.Spec.FeSpec = &FeSpec{BaseSpec: BaseSpec{Replicas: &replicas}}
if _, err := cluster.ValidateUpdate(context.Background(), cluster, cluster); err != nil {
t.Fatalf("expected non-admin management user to be allowed on update: %v", err)
}
}
Loading