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 }