github.com/oam-dev/kubevela@v1.9.11/pkg/webhook/core.oam.dev/v1beta1/application/mutating_handler.go (about)

     1  /*
     2  Copyright 2022 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package application
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net/http"
    24  
    25  	"github.com/kubevela/pkg/controller/sharding"
    26  	"github.com/pkg/errors"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    29  	"k8s.io/klog/v2"
    30  	"k8s.io/utils/strings/slices"
    31  	"sigs.k8s.io/controller-runtime/pkg/manager"
    32  	"sigs.k8s.io/controller-runtime/pkg/webhook"
    33  	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
    34  
    35  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    36  	"github.com/oam-dev/kubevela/pkg/auth"
    37  	"github.com/oam-dev/kubevela/pkg/features"
    38  	"github.com/oam-dev/kubevela/pkg/oam"
    39  	"github.com/oam-dev/kubevela/pkg/utils"
    40  )
    41  
    42  // MutatingHandler adding user info to application annotations
    43  type MutatingHandler struct {
    44  	skipUsers []string
    45  	Decoder   *admission.Decoder
    46  }
    47  
    48  var _ admission.Handler = &MutatingHandler{}
    49  
    50  type appMutator func(ctx context.Context, req admission.Request, oldApp *v1beta1.Application, newApp *v1beta1.Application) (bool, error)
    51  
    52  func (h *MutatingHandler) handleIdentity(_ context.Context, req admission.Request, _ *v1beta1.Application, app *v1beta1.Application) (bool, error) {
    53  	if !utilfeature.DefaultMutableFeatureGate.Enabled(features.AuthenticateApplication) {
    54  		return false, nil
    55  	}
    56  
    57  	if slices.Contains(h.skipUsers, req.UserInfo.Username) {
    58  		return false, nil
    59  	}
    60  
    61  	if metav1.HasAnnotation(app.ObjectMeta, oam.AnnotationApplicationServiceAccountName) {
    62  		return false, errors.New("service-account annotation is not permitted when authentication enabled")
    63  	}
    64  	klog.Infof("[ApplicationMutatingHandler] Setting UserInfo into Application, UserInfo: %v, Application: %s/%s", req.UserInfo, app.GetNamespace(), app.GetName())
    65  	auth.SetUserInfoInAnnotation(&app.ObjectMeta, req.UserInfo)
    66  	return true, nil
    67  }
    68  
    69  func (h *MutatingHandler) handleWorkflow(_ context.Context, _ admission.Request, _ *v1beta1.Application, app *v1beta1.Application) (modified bool, err error) {
    70  	if app.Spec.Workflow != nil {
    71  		for i, step := range app.Spec.Workflow.Steps {
    72  			if step.Name == "" {
    73  				app.Spec.Workflow.Steps[i].Name = fmt.Sprintf("step-%d", i)
    74  				modified = true
    75  			}
    76  			for j, sub := range step.SubSteps {
    77  				if sub.Name == "" {
    78  					app.Spec.Workflow.Steps[i].SubSteps[j].Name = fmt.Sprintf("step-%d-%d", i, j)
    79  					modified = true
    80  				}
    81  			}
    82  		}
    83  	}
    84  	return modified, nil
    85  }
    86  
    87  func (h *MutatingHandler) handleSharding(_ context.Context, _ admission.Request, oldApp *v1beta1.Application, newApp *v1beta1.Application) (bool, error) {
    88  	if sharding.EnableSharding && !utilfeature.DefaultMutableFeatureGate.Enabled(features.DisableWebhookAutoSchedule) {
    89  		oid, scheduled := sharding.GetScheduledShardID(oldApp)
    90  		_, newScheduled := sharding.GetScheduledShardID(newApp)
    91  		if scheduled && !newScheduled {
    92  			klog.Infof("inherit old shard-id %s for app %s/%s", oid, newApp.Namespace, newApp.Name)
    93  			sharding.SetScheduledShardID(newApp, oid)
    94  			return true, nil
    95  		}
    96  		return sharding.DefaultScheduler.Get().Schedule(newApp), nil
    97  	}
    98  	return false, nil
    99  }
   100  
   101  // Handle mutate application
   102  func (h *MutatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
   103  	oldApp, newApp := &v1beta1.Application{}, &v1beta1.Application{}
   104  	if err := h.Decoder.Decode(req, newApp); err != nil {
   105  		return admission.Errored(http.StatusBadRequest, err)
   106  	}
   107  	if len(req.OldObject.Raw) > 0 {
   108  		if err := h.Decoder.DecodeRaw(req.OldObject, oldApp); err != nil {
   109  			return admission.Errored(http.StatusBadRequest, err)
   110  		}
   111  	}
   112  
   113  	modified := false
   114  	for _, handler := range []appMutator{h.handleIdentity, h.handleSharding, h.handleWorkflow} {
   115  		m, err := handler(ctx, req, oldApp, newApp)
   116  		if err != nil {
   117  			return admission.Errored(http.StatusBadRequest, err)
   118  		}
   119  		if m {
   120  			modified = true
   121  		}
   122  	}
   123  	if !modified {
   124  		return admission.Patched("")
   125  	}
   126  
   127  	bs, err := json.Marshal(newApp)
   128  	if err != nil {
   129  		return admission.Errored(http.StatusInternalServerError, err)
   130  	}
   131  	return admission.PatchResponseFromRaw(req.AdmissionRequest.Object.Raw, bs)
   132  }
   133  
   134  var _ admission.DecoderInjector = &MutatingHandler{}
   135  
   136  // InjectDecoder .
   137  func (h *MutatingHandler) InjectDecoder(d *admission.Decoder) error {
   138  	h.Decoder = d
   139  	return nil
   140  }
   141  
   142  // RegisterMutatingHandler will register component mutation handler to the webhook
   143  func RegisterMutatingHandler(mgr manager.Manager) {
   144  	server := mgr.GetWebhookServer()
   145  	handler := &MutatingHandler{}
   146  	if userInfo := utils.GetUserInfoFromConfig(mgr.GetConfig()); userInfo != nil {
   147  		klog.Infof("[ApplicationMutatingHandler] add skip user %s", userInfo.Username)
   148  		handler.skipUsers = []string{userInfo.Username}
   149  	}
   150  	server.Register("/mutating-core-oam-dev-v1beta1-applications", &webhook.Admission{Handler: handler})
   151  }