From 1f2f6157d7abfe1f0d3103cf6b88e077b3387497 Mon Sep 17 00:00:00 2001 From: wanjunlei <53003665+wanjunlei@users.noreply.github.com> Date: Fri, 22 Jul 2022 17:46:57 +0800 Subject: [PATCH] add apis for switch the notification language (#5088) add apis for switch the notification lanaguae Signed-off-by: wanjunlei --- pkg/apiserver/apiserver.go | 1 + pkg/kapis/notification/v2beta2/handler.go | 23 +++ pkg/kapis/notification/v2beta2/register.go | 25 ++- pkg/models/notification/notification.go | 192 +++++++++++++----- .../v1alpha3/notification/notification.go | 25 +++ .../resources/v1alpha3/resource/resource.go | 1 + 6 files changed, 208 insertions(+), 59 deletions(-) diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 075025af6..625ea8a67 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -506,6 +506,7 @@ func (s *APIServer) waitForResourceSync(ctx context.Context) error { notificationv2beta1.ResourcesPluralReceiver, }, {Group: "notification.kubesphere.io", Version: "v2beta2"}: { + notificationv2beta2.ResourcesPluralNotificationManager, notificationv2beta2.ResourcesPluralConfig, notificationv2beta2.ResourcesPluralReceiver, notificationv2beta2.ResourcesPluralRouter, diff --git a/pkg/kapis/notification/v2beta2/handler.go b/pkg/kapis/notification/v2beta2/handler.go index 4e296f48c..3e81bce1d 100644 --- a/pkg/kapis/notification/v2beta2/handler.go +++ b/pkg/kapis/notification/v2beta2/handler.go @@ -16,6 +16,8 @@ package v2beta2 import ( + "io/ioutil" + "github.com/emicklei/go-restful" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/client-go/kubernetes" @@ -118,6 +120,27 @@ func (h *handler) UpdateResource(req *restful.Request, resp *restful.Response) { handleResponse(req, resp, updated, err) } +func (h *handler) PatchResource(req *restful.Request, resp *restful.Response) { + + user := req.PathParameter("user") + resource := req.PathParameter("resources") + name := req.PathParameter("name") + + if !h.operator.IsKnownResource(resource, nmoperator.V2beta2, "") { + api.HandleBadRequest(resp, req, servererr.New("unknown resource type %s", resource)) + return + } + + data, err := ioutil.ReadAll(req.Request.Body) + if err != nil { + api.HandleBadRequest(resp, req, err) + return + } + + patched, err := h.operator.Patch(user, resource, name, data) + handleResponse(req, resp, patched, err) +} + func (h *handler) DeleteResource(req *restful.Request, resp *restful.Response) { user := req.PathParameter("user") diff --git a/pkg/kapis/notification/v2beta2/register.go b/pkg/kapis/notification/v2beta2/register.go index 527148276..8f57aa4b3 100644 --- a/pkg/kapis/notification/v2beta2/register.go +++ b/pkg/kapis/notification/v2beta2/register.go @@ -69,7 +69,7 @@ func AddToContainer( To(h.ListResource). Doc("list the notification resources"). Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). - Param(ws.PathParameter("resources", "known values include configs, receivers, secrets, routers, silences, configmaps")). + Param(ws.PathParameter("resources", "known values include notificationmanagers, configs, receivers, secrets, routers, silences, configmaps")). Param(ws.QueryParameter(query.ParameterName, "name used for filtering").Required(false)). Param(ws.QueryParameter(query.ParameterLabelSelector, "label selector used for filtering").Required(false)). Param(ws.QueryParameter("type", "config or receiver type, known values include dingtalk, email, feishu, slack, webhook, wechat").Required(false)). @@ -83,7 +83,7 @@ func AddToContainer( To(h.GetResource). Doc("get the specified notification resources"). Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). - Param(ws.PathParameter("resources", "known values include configs, receivers, secrets, routers, silences, configmaps")). + Param(ws.PathParameter("resources", "known values include notificationmanagers, configs, receivers, secrets, routers, silences, configmaps")). Param(ws.PathParameter(query.ParameterName, "the name of the resource")). Param(ws.QueryParameter("type", "config or receiver type, known values include dingtalk, feishu, email, slack, webhook, wechat").Required(false)). Returns(http.StatusOK, api.StatusOK, nil)) @@ -92,14 +92,22 @@ func AddToContainer( To(h.CreateResource). Doc("create a notification resources"). Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). - Param(ws.PathParameter("resources", "known values include configs, receivers, secrets, routers, silences, configmaps")). + Param(ws.PathParameter("resources", "known values include notificationmanagers, configs, receivers, secrets, routers, silences, configmaps")). Returns(http.StatusOK, api.StatusOK, nil)) ws.Route(ws.PUT("/{resources}/{name}"). To(h.UpdateResource). Doc("update the specified notification resources"). Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). - Param(ws.PathParameter("resources", "known values include configs, receivers, secrets, routers, silences, configmaps")). + Param(ws.PathParameter("resources", "known values include notificationmanagers, configs, receivers, secrets, routers, silences, configmaps")). + Param(ws.PathParameter(query.ParameterName, "the name of the resource")). + Returns(http.StatusOK, api.StatusOK, nil)) + + ws.Route(ws.PATCH("/{resources}/{name}"). + To(h.PatchResource). + Doc("patch the specified notification resources"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("resources", "known values include notificationmanagers, configs, receivers, secrets, routers, silences, configmaps")). Param(ws.PathParameter(query.ParameterName, "the name of the resource")). Returns(http.StatusOK, api.StatusOK, nil)) @@ -154,6 +162,15 @@ func AddToContainer( Param(ws.PathParameter(query.ParameterName, "the name of the resource")). Returns(http.StatusOK, api.StatusOK, nil)) + ws.Route(ws.PATCH("/users/{user}/{resources}/{name}"). + To(h.PatchResource). + Doc("Patch the specified notification resources"). + Metadata(KeyOpenAPITags, []string{constants.NotificationTag}). + Param(ws.PathParameter("user", "user name")). + Param(ws.PathParameter("resources", "known values include configs, receivers, secrets, silences, configmaps")). + Param(ws.PathParameter(query.ParameterName, "the name of the resource")). + Returns(http.StatusOK, api.StatusOK, nil)) + ws.Route(ws.DELETE("/users/{user}/{resources}/{name}"). To(h.DeleteResource). Doc("delete the specified notification resources"). diff --git a/pkg/models/notification/notification.go b/pkg/models/notification/notification.go index d830b2d97..10d0c817e 100644 --- a/pkg/models/notification/notification.go +++ b/pkg/models/notification/notification.go @@ -25,6 +25,8 @@ import ( "net/http" "reflect" + "k8s.io/apimachinery/pkg/types" + "github.com/emicklei/go-restful" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -66,6 +68,7 @@ type Operator interface { Create(user, resource string, obj runtime.Object) (runtime.Object, error) Delete(user, resource, name string) error Update(user, resource, name string, obj runtime.Object) (runtime.Object, error) + Patch(user, resource, name string, data []byte) (runtime.Object, error) Verify(request *restful.Request, response *restful.Response) @@ -120,29 +123,15 @@ func (o *operator) List(user, resource, subresource string, q *query.Query) (*ap func (o *operator) list(user, resource, version, subresource string, q *query.Query) (*api.ListResult, error) { - if user != "" && resource == v2beta2.ResourcesPluralRouter { - return nil, errors.NewForbidden(v2beta2.Resource(v2beta2.ResourcesPluralRouter), "", - fmt.Errorf("tenant can not list router")) - } - - if len(q.LabelSelector) > 0 { - q.LabelSelector = q.LabelSelector + "," - } - - filter := "" - // If user is nil, it will list all global object. - if user == "" { - if isConfig(o.GetObject(resource, version)) { - filter = "type=default" - } else { - filter = "type=global" + if user != "" { + if resource == v2beta2.ResourcesPluralRouter || + resource == v2beta2.ResourcesPluralNotificationManager { + return nil, errors.NewForbidden(v2beta2.Resource(resource), "", + fmt.Errorf("tenant can not list %s", resource)) } - } else { - // If the user is not nil, only return the object belong to this user. - filter = "type=tenant,user=" + user } - q.LabelSelector = q.LabelSelector + filter + q.LabelSelector = o.generateLabelSelector(q, user, resource, version) ns := "" if resource == Secret || resource == ConfigMap { @@ -155,7 +144,8 @@ func (o *operator) list(user, resource, version, subresource string, q *query.Qu return nil, err } - if subresource == "" || resource == Secret { + if subresource == "" || + (resource != v2beta2.ResourcesPluralConfig && resource != v2beta2.ResourcesPluralReceiver) { return res, nil } @@ -174,6 +164,34 @@ func (o *operator) list(user, resource, version, subresource string, q *query.Qu return results, nil } +func (o *operator) generateLabelSelector(q *query.Query, user, resource, version string) string { + + if resource == v2beta2.ResourcesPluralNotificationManager { + return q.LabelSelector + } + + labelSelector := q.LabelSelector + if len(labelSelector) > 0 { + labelSelector = q.LabelSelector + "," + } + + filter := "" + // If user is nil, it will list all global object. + if user == "" { + if isConfig(o.GetObject(resource, version)) { + filter = "type=default" + } else { + filter = "type=global" + } + } else { + // If the user is not nil, only return the object belong to this user. + filter = "type=tenant,user=" + user + } + + labelSelector = labelSelector + filter + return labelSelector +} + // GetV2beta1 get the specified object of version v2beta1, if you want to get a global object, the user must be nil. // If you want to get a tenant object, the user must equal to the tenant specified in labels of the object. func (o *operator) GetV2beta1(user, resource, name, subresource string) (runtime.Object, error) { @@ -188,9 +206,12 @@ func (o *operator) Get(user, resource, name, subresource string) (runtime.Object func (o *operator) get(user, resource, version, name, subresource string) (runtime.Object, error) { - if user != "" && resource == v2beta2.ResourcesPluralRouter { - return nil, errors.NewForbidden(v2beta2.Resource(v2beta2.ResourcesPluralRouter), "", - fmt.Errorf("tenant can not get router")) + if user != "" { + if resource == v2beta2.ResourcesPluralRouter || + resource == v2beta2.ResourcesPluralNotificationManager { + return nil, errors.NewForbidden(v2beta2.Resource(resource), "", + fmt.Errorf("tenant can not get %s", resource)) + } } ns := "" @@ -209,7 +230,8 @@ func (o *operator) get(user, resource, version, name, subresource string) (runti return nil, err } - if subresource == "" || resource == Secret || resource == ConfigMap { + if subresource == "" || + (resource != v2beta2.ResourcesPluralConfig && resource != v2beta2.ResourcesPluralReceiver) { return obj, nil } @@ -239,16 +261,21 @@ func (o *operator) Create(user, resource string, obj runtime.Object) (runtime.Ob func (o *operator) create(user, resource, version string, obj runtime.Object) (runtime.Object, error) { + if user != "" { + if resource == v2beta2.ResourcesPluralRouter || + resource == v2beta2.ResourcesPluralNotificationManager { + return nil, errors.NewForbidden(v2beta2.Resource(resource), "", + fmt.Errorf("tenant can not create %s", resource)) + } + } + if err := appendLabel(user, resource, obj); err != nil { return nil, err } - if user != "" && resource == v2beta2.ResourcesPluralRouter { - return nil, errors.NewForbidden(v2beta2.Resource(v2beta2.ResourcesPluralRouter), "", - fmt.Errorf("tenant can not create router")) - } - switch resource { + case v2beta2.ResourcesPluralNotificationManager: + return o.ksClient.NotificationV2beta2().NotificationManagers().Create(context.Background(), obj.(*v2beta2.NotificationManager), v1.CreateOptions{}) case v2beta2.ResourcesPluralConfig: if version == V2beta1 { return o.ksClient.NotificationV2beta1().Configs().Create(context.Background(), obj.(*v2beta1.Config), v1.CreateOptions{}) @@ -288,22 +315,22 @@ func (o *operator) Delete(user, resource, name string) error { func (o *operator) delete(user, resource, name string) error { - if user != "" && resource == v2beta2.ResourcesPluralRouter { - return errors.NewForbidden(v2beta2.Resource(v2beta2.ResourcesPluralRouter), "", - fmt.Errorf("tenant can not delete router")) - } - - if obj, err := o.Get(user, resource, name, ""); err != nil { - klog.Error(err) - return err - } else { - if err := authorizer(user, obj); err != nil { - klog.Error(err) - return err + if user != "" { + if resource == v2beta2.ResourcesPluralRouter || + resource == v2beta2.ResourcesPluralNotificationManager { + return errors.NewForbidden(v2beta2.Resource(resource), "", + fmt.Errorf("tenant can not delete %s", resource)) } } + if _, err := o.Get(user, resource, name, ""); err != nil { + klog.Error(err) + return err + } + switch resource { + case v2beta2.ResourcesPluralNotificationManager: + return o.ksClient.NotificationV2beta2().NotificationManagers().Delete(context.Background(), name, v1.DeleteOptions{}) case v2beta2.ResourcesPluralConfig: return o.ksClient.NotificationV2beta2().Configs().Delete(context.Background(), name, v1.DeleteOptions{}) case v2beta2.ResourcesPluralReceiver: @@ -335,26 +362,36 @@ func (o *operator) Update(user, resource, name string, obj runtime.Object) (runt func (o *operator) update(user, resource, version, name string, obj runtime.Object) (runtime.Object, error) { - if user != "" && resource == v2beta2.ResourcesPluralRouter { - return nil, errors.NewForbidden(v2beta2.Resource(v2beta2.ResourcesPluralRouter), "", - fmt.Errorf("tenant can not update router")) + if user != "" { + if resource == v2beta2.ResourcesPluralRouter || + resource == v2beta2.ResourcesPluralNotificationManager { + return nil, errors.NewForbidden(v2beta2.Resource(resource), "", + fmt.Errorf("tenant can not update %s", resource)) + } + } + + accessor, err := meta.Accessor(obj) + if err != nil { + return nil, err + } + + if accessor.GetName() != name { + return nil, fmt.Errorf("incorrcet parameter, resource name is not equal to the name in body") + } + + _, err = o.Get(user, resource, name, "") + if err != nil { + klog.Error(err) + return nil, err } if err := appendLabel(user, resource, obj); err != nil { return nil, err } - if old, err := o.Get(user, resource, name, ""); err != nil { - klog.Error(err) - return nil, err - } else { - if err := authorizer(user, old); err != nil { - klog.Error(err) - return nil, err - } - } - switch resource { + case v2beta2.ResourcesPluralNotificationManager: + return o.ksClient.NotificationV2beta2().NotificationManagers().Update(context.Background(), obj.(*v2beta2.NotificationManager), v1.UpdateOptions{}) case v2beta2.ResourcesPluralConfig: if version == V2beta1 { return o.ksClient.NotificationV2beta1().Configs().Update(context.Background(), obj.(*v2beta1.Config), v1.UpdateOptions{}) @@ -380,9 +417,49 @@ func (o *operator) update(user, resource, version, name string, obj runtime.Obje } } +// Patch an object, only a global object will be patched if the user is nil. +// If the user is not nil, a tenant object whose tenant label matches the user will be patched. +func (o *operator) Patch(user, resource, name string, data []byte) (runtime.Object, error) { + + if user != "" { + if resource == v2beta2.ResourcesPluralRouter || + resource == v2beta2.ResourcesPluralNotificationManager { + return nil, errors.NewForbidden(v2beta2.Resource(resource), "", + fmt.Errorf("tenant can not update %s", resource)) + } + } + + _, err := o.Get(user, resource, name, "") + if err != nil { + klog.Error(err) + return nil, err + } + + switch resource { + case v2beta2.ResourcesPluralNotificationManager: + return o.ksClient.NotificationV2beta2().NotificationManagers().Patch(context.Background(), name, types.MergePatchType, data, v1.PatchOptions{}) + case v2beta2.ResourcesPluralConfig: + return o.ksClient.NotificationV2beta2().Configs().Patch(context.Background(), name, types.MergePatchType, data, v1.PatchOptions{}) + case v2beta2.ResourcesPluralReceiver: + return o.ksClient.NotificationV2beta2().Receivers().Patch(context.Background(), name, types.MergePatchType, data, v1.PatchOptions{}) + case v2beta2.ResourcesPluralRouter: + return o.ksClient.NotificationV2beta2().Routers().Patch(context.Background(), name, types.MergePatchType, data, v1.PatchOptions{}) + case v2beta2.ResourcesPluralSilence: + return o.ksClient.NotificationV2beta2().Silences().Patch(context.Background(), name, types.MergePatchType, data, v1.PatchOptions{}) + case Secret: + return o.k8sClient.CoreV1().Secrets(constants.NotificationSecretNamespace).Patch(context.Background(), name, types.MergePatchType, data, v1.PatchOptions{}) + case ConfigMap: + return o.k8sClient.CoreV1().ConfigMaps(constants.NotificationSecretNamespace).Patch(context.Background(), name, types.MergePatchType, data, v1.PatchOptions{}) + default: + return nil, errors.NewInternalError(nil) + } +} + func (o *operator) GetObject(resource, version string) runtime.Object { switch resource { + case v2beta2.ResourcesPluralNotificationManager: + return &v2beta2.NotificationManager{} case v2beta2.ResourcesPluralConfig: if version == V2beta1 { return &v2beta1.Config{} @@ -542,6 +619,11 @@ func isConfig(obj runtime.Object) bool { // Is the object is a global object. func isGlobal(obj runtime.Object) bool { + + if _, ok := obj.(*v2beta2.NotificationManager); ok { + return true + } + accessor, err := meta.Accessor(obj) if err != nil { klog.Errorln(err) diff --git a/pkg/models/resources/v1alpha3/notification/notification.go b/pkg/models/resources/v1alpha3/notification/notification.go index 68d525411..bded09e7e 100644 --- a/pkg/models/resources/v1alpha3/notification/notification.go +++ b/pkg/models/resources/v1alpha3/notification/notification.go @@ -28,6 +28,31 @@ import ( "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" ) +type notificationmanagerGetter struct { + ksInformer ksinformers.SharedInformerFactory +} + +func NewNotificationManagerGetter(informer ksinformers.SharedInformerFactory) v1alpha3.Interface { + return ¬ificationmanagerGetter{ksInformer: informer} +} + +func (g *notificationmanagerGetter) Get(_, name string) (runtime.Object, error) { + return g.ksInformer.Notification().V2beta2().NotificationManagers().Lister().Get(name) +} + +func (g *notificationmanagerGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + objs, err := g.ksInformer.Notification().V2beta2().NotificationManagers().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, obj := range objs { + result = append(result, obj) + } + return v1alpha3.DefaultList(result, query, compare, filter), nil +} + type configGetter struct { ksInformer ksinformers.SharedInformerFactory } diff --git a/pkg/models/resources/v1alpha3/resource/resource.go b/pkg/models/resources/v1alpha3/resource/resource.go index d10b61d40..18e0ca8ce 100644 --- a/pkg/models/resources/v1alpha3/resource/resource.go +++ b/pkg/models/resources/v1alpha3/resource/resource.go @@ -143,6 +143,7 @@ func NewResourceGetter(factory informers.InformerFactory, cache cache.Cache) *Re clusterResourceGetters[rbacv1.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralClusterRole)] = clusterrole.New(factory.KubernetesSharedInformerFactory()) clusterResourceGetters[rbacv1.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralClusterRoleBinding)] = clusterrolebinding.New(factory.KubernetesSharedInformerFactory()) clusterResourceGetters[clusterv1alpha1.SchemeGroupVersion.WithResource(clusterv1alpha1.ResourcesPluralCluster)] = cluster.New(factory.KubeSphereSharedInformerFactory()) + clusterResourceGetters[notificationv2beta2.SchemeGroupVersion.WithResource(notificationv2beta2.ResourcesPluralNotificationManager)] = notification.NewNotificationManagerGetter(factory.KubeSphereSharedInformerFactory()) clusterResourceGetters[notificationv2beta2.SchemeGroupVersion.WithResource(notificationv2beta2.ResourcesPluralConfig)] = notification.NewNotificationConfigGetter(factory.KubeSphereSharedInformerFactory()) clusterResourceGetters[notificationv2beta2.SchemeGroupVersion.WithResource(notificationv2beta2.ResourcesPluralReceiver)] = notification.NewNotificationReceiverGetter(factory.KubeSphereSharedInformerFactory()) clusterResourceGetters[notificationv2beta2.SchemeGroupVersion.WithResource(notificationv2beta2.ResourcesPluralRouter)] = notification.NewNotificationRouterGetter(factory.KubeSphereSharedInformerFactory())