pkg/admin/handler/application.go (462 lines of code) (raw):

/* * 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 handler // API Definition: https://app.apifox.com/project/3732499 // 资源详情-应用 import ( "net/http" "strconv" ) import ( "github.com/gin-gonic/gin" ) import ( mesh_proto "github.com/apache/dubbo-kubernetes/api/mesh/v1alpha1" "github.com/apache/dubbo-kubernetes/pkg/admin/model" "github.com/apache/dubbo-kubernetes/pkg/admin/service" "github.com/apache/dubbo-kubernetes/pkg/core/consts" "github.com/apache/dubbo-kubernetes/pkg/core/logger" core_mesh "github.com/apache/dubbo-kubernetes/pkg/core/resources/apis/mesh" core_store "github.com/apache/dubbo-kubernetes/pkg/core/resources/store" core_runtime "github.com/apache/dubbo-kubernetes/pkg/core/runtime" ) func GetApplicationDetail(rt core_runtime.Runtime) gin.HandlerFunc { return func(c *gin.Context) { req := &model.ApplicationDetailReq{} if err := c.ShouldBindQuery(req); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } resp, err := service.GetApplicationDetail(rt, req) if err != nil { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } func GetApplicationTabInstanceInfo(rt core_runtime.Runtime) gin.HandlerFunc { return func(c *gin.Context) { req := model.NewApplicationTabInstanceInfoReq() if err := c.ShouldBindQuery(req); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } resp, err := service.GetApplicationTabInstanceInfo(rt, req) if err != nil { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } func GetApplicationServiceForm(rt core_runtime.Runtime) gin.HandlerFunc { return func(c *gin.Context) { req := model.NewApplicationServiceFormReq() if err := c.ShouldBindQuery(req); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } resp, err := service.GetApplicationServiceFormInfo(rt, req) if err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } func ApplicationSearch(rt core_runtime.Runtime) gin.HandlerFunc { return func(c *gin.Context) { req := model.NewApplicationSearchReq() if err := c.ShouldBindQuery(req); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } resp, err := service.GetApplicationSearchInfo(rt, req) if err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } func isAppOperatorLogOpened(conf *mesh_proto.OverrideConfig, appName string) bool { if conf.Side != consts.SideProvider || conf.Parameters == nil || conf.Match == nil || conf.Match.Application == nil || conf.Match.Application.Oneof == nil || len(conf.Match.Application.Oneof) != 1 || conf.Match.Application.Oneof[0].Exact != appName { return false } else if val, ok := conf.Parameters[`accesslog`]; !ok || val != `true` { return false } return true } func generateDefaultConfigurator(name string, scope string, version string, enabled bool) *core_mesh.DynamicConfigResource { return &core_mesh.DynamicConfigResource{ Meta: nil, Spec: &mesh_proto.DynamicConfig{ Key: name, Scope: scope, ConfigVersion: version, Enabled: enabled, Configs: make([]*mesh_proto.OverrideConfig, 0), }, } } func ApplicationConfigOperatorLogPut(rt core_runtime.Runtime) gin.HandlerFunc { return func(c *gin.Context) { var ( ApplicationName string OperatorLog bool isNotExist = false ) ApplicationName = c.Query("appName") OperatorLog, err := strconv.ParseBool(c.Query("operatorLog")) if err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } res, err := service.GetConfigurator(rt, ApplicationName) if err != nil { if core_store.IsResourceNotFound(err) { // for check app exist data, err := service.GetApplicationDetail(rt, &model.ApplicationDetailReq{AppName: ApplicationName}) if err != nil || data == nil { c.JSON(http.StatusNotFound, model.NewErrorResp(err.Error())) return } res = generateDefaultConfigurator(ApplicationName, consts.ScopeApplication, consts.ConfiguratorVersionV3, true) isNotExist = true } else { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } } // append or remove if OperatorLog { // check is already exist alreadyExist := false res.Spec.RangeConfig(func(conf *mesh_proto.OverrideConfig) (isStop bool) { alreadyExist = isAppOperatorLogOpened(conf, ApplicationName) return alreadyExist }) if alreadyExist { c.JSON(http.StatusOK, model.NewSuccessResp(nil)) return } if res.Spec.Configs == nil { res.Spec.Configs = make([]*mesh_proto.OverrideConfig, 0) } res.Spec.Configs = append(res.Spec.Configs, &mesh_proto.OverrideConfig{ Side: consts.SideProvider, Parameters: map[string]string{`accesslog`: `true`}, Enabled: true, Match: &mesh_proto.ConditionMatch{Application: &mesh_proto.ListStringMatch{Oneof: []*mesh_proto.StringMatch{{Exact: ApplicationName}}}}, XGenerateByCp: true, }) } else { res.Spec.RangeConfigsToRemove(func(conf *mesh_proto.OverrideConfig) (IsRemove bool) { if conf == nil { return true } return isAppOperatorLogOpened(conf, ApplicationName) }) } // restore if isNotExist { err = service.CreateConfigurator(rt, ApplicationName, res) if err != nil { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } } else { err = service.UpdateConfigurator(rt, ApplicationName, res) if err != nil { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } } c.JSON(http.StatusOK, model.NewSuccessResp(nil)) } } func ApplicationConfigOperatorLogGet(rt core_runtime.Runtime) gin.HandlerFunc { return func(c *gin.Context) { ApplicationName := c.Query("appName") if ApplicationName == "" { c.JSON(http.StatusBadRequest, model.NewErrorResp("appName is required")) return } res, err := service.GetConfigurator(rt, ApplicationName) if err != nil { if core_store.IsResourceNotFound(err) { c.JSON(http.StatusOK, model.NewSuccessResp(map[string]interface{}{"operatorLog": false})) return } c.JSON(http.StatusNotFound, model.NewErrorResp(err.Error())) return } isExist := false res.Spec.RangeConfig(func(conf *mesh_proto.OverrideConfig) (isStop bool) { if isExist = isAppOperatorLogOpened(conf, ApplicationName); isExist { return true } return false }) c.JSON(http.StatusOK, model.NewSuccessResp(map[string]interface{}{"operatorLog": isExist})) } } func ApplicationConfigFlowWeightGET(rt core_runtime.Runtime) gin.HandlerFunc { return func(c *gin.Context) { var ( ApplicationName string resp = struct { FlowWeightSets []model.FlowWeightSet `json:"flowWeightSets"` }{} ) ApplicationName = c.Query("appName") if ApplicationName == "" { c.JSON(http.StatusBadRequest, model.NewErrorResp("appName is required")) return } resp.FlowWeightSets = make([]model.FlowWeightSet, 0) res, err := service.GetConfigurator(rt, ApplicationName) if err != nil { if core_store.IsResourceNotFound(err) { c.JSON(http.StatusOK, model.NewSuccessResp(resp)) return } c.JSON(http.StatusNotFound, model.NewErrorResp(err.Error())) return } weight := 0 res.Spec.RangeConfig(func(conf *mesh_proto.OverrideConfig) (isStop bool) { if isFlowWeight(conf) { weight, err = strconv.Atoi(conf.Parameters[`weight`]) if err != nil { logger.Error("parse weight failed", err) return true } scope := make([]model.ParamMatch, 0, len(conf.Match.Param)) for _, param := range conf.Match.Param { scope = append(scope, model.ParamMatch{ Key: &param.Key, Value: model.StringMatchToModelStringMatch(param.Value), }) } resp.FlowWeightSets = append(resp.FlowWeightSets, model.FlowWeightSet{ Weight: int32(weight), Scope: scope, }) } return false }) if err != nil { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } func isFlowWeight(conf *mesh_proto.OverrideConfig) bool { if conf.Side != consts.SideProvider || conf.Parameters == nil || conf.Match == nil || conf.Match.Param == nil { return false } else if _, ok := conf.Parameters[`weight`]; !ok { return false } return true } func ApplicationConfigFlowWeightPUT(rt core_runtime.Runtime) gin.HandlerFunc { return func(c *gin.Context) { var ( ApplicationName = "" body = struct { FlowWeightSets []model.FlowWeightSet `json:"flowWeightSets"` }{} ) ApplicationName = c.Query("appName") if ApplicationName == "" { c.JSON(http.StatusBadRequest, model.NewErrorResp("application name is required")) } if err := c.Bind(&body); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } // get from store, or generate default resource isNotExist := false res, err := service.GetConfigurator(rt, ApplicationName) if err != nil { if core_store.IsResourceNotFound(err) { // for check app exist data, err := service.GetApplicationDetail(rt, &model.ApplicationDetailReq{AppName: ApplicationName}) if err != nil { c.JSON(http.StatusNotFound, model.NewErrorResp(err.Error())) return } else if data == nil { c.JSON(http.StatusNotFound, model.NewErrorResp("application not found")) return } res = generateDefaultConfigurator(ApplicationName, consts.ScopeApplication, consts.ConfiguratorVersionV3, true) isNotExist = true } else { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } } // remove old res.Spec.RangeConfigsToRemove(func(conf *mesh_proto.OverrideConfig) (IsRemove bool) { return isFlowWeight(conf) }) // append new for _, set := range body.FlowWeightSets { paramMatch := make([]*mesh_proto.ParamMatch, 0, len(set.Scope)) for _, match := range set.Scope { paramMatch = append(paramMatch, &mesh_proto.ParamMatch{ Key: *match.Key, Value: model.ModelStringMatchToStringMatch(match.Value), }) } res.Spec.Configs = append(res.Spec.Configs, &mesh_proto.OverrideConfig{ Side: consts.SideProvider, Parameters: map[string]string{`weight`: strconv.Itoa(int(set.Weight))}, Match: &mesh_proto.ConditionMatch{ Param: paramMatch, }, XGenerateByCp: true, }) } // restore if isNotExist { err = service.CreateConfigurator(rt, ApplicationName, res) if err != nil { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } } else { err = service.UpdateConfigurator(rt, ApplicationName, res) if err != nil { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } } c.JSON(http.StatusOK, model.NewSuccessResp(nil)) } } func ApplicationConfigGrayGET(rt core_runtime.Runtime) gin.HandlerFunc { return func(c *gin.Context) { var ( ApplicationName = "" resp = struct { GraySets []model.GraySet `json:"graySets"` }{} ) ApplicationName = c.Query("appName") if ApplicationName == "" { c.JSON(http.StatusBadRequest, model.NewErrorResp("appName is required")) return } res, err := service.GetTagRule(rt, ApplicationName) if err != nil { if core_store.IsResourceNotFound(err) { resp.GraySets = make([]model.GraySet, 0) c.JSON(http.StatusOK, model.NewSuccessResp(resp)) return } c.JSON(http.StatusNotFound, model.NewErrorResp(err.Error())) return } resp.GraySets = make([]model.GraySet, 0, len(res.Spec.Tags)) res.Spec.RangeTags(func(tag *mesh_proto.Tag) (isStop bool) { if isGrayTag(tag) { scope := make([]model.ParamMatch, 0, len(tag.Match)) for _, paramMatch := range tag.Match { scope = append(scope, model.ParamMatch{ Key: &paramMatch.Key, Value: model.StringMatchToModelStringMatch(paramMatch.Value), }) } resp.GraySets = append(resp.GraySets, model.GraySet{ EnvName: tag.Name, Scope: scope, }) } return false }) c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } func ApplicationConfigGrayPUT(rt core_runtime.Runtime) gin.HandlerFunc { return func(c *gin.Context) { var ( ApplicationName = "" body = struct { GraySets []model.GraySet `json:"graySets"` }{} ) ApplicationName = c.Query("appName") if ApplicationName == "" { c.JSON(http.StatusBadRequest, model.NewErrorResp("application name is required")) return } if err := c.Bind(&body); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) } isNotExist := false res, err := service.GetTagRule(rt, ApplicationName) if core_store.IsResourceNotFound(err) { data, err := service.GetApplicationDetail(rt, &model.ApplicationDetailReq{AppName: ApplicationName}) if err != nil { c.JSON(http.StatusNotFound, model.NewErrorResp(err.Error())) return } else if data == nil { c.JSON(http.StatusNotFound, model.NewErrorResp("application not found")) return } res = generateDefaultTagRule(ApplicationName, consts.ConfiguratorVersionV3, true, false) isNotExist = true } // remove old config, generate config from admin, append res.Spec.RangeTagsToRemove(func(tag *mesh_proto.Tag) (IsRemove bool) { return isGrayTag(tag) }) newTags := make([]*mesh_proto.Tag, 0) for _, set := range body.GraySets { paramMatches := make([]*mesh_proto.ParamMatch, 0, len(set.Scope)) for _, match := range set.Scope { paramMatches = append(paramMatches, &mesh_proto.ParamMatch{ Key: *match.Key, Value: model.ModelStringMatchToStringMatch(match.Value), }) } newTags = append(newTags, &mesh_proto.Tag{ Name: set.EnvName, Match: paramMatches, XGenerateByCp: true, }) } res.Spec.Tags = append(res.Spec.Tags, newTags...) // restore if isNotExist { err = service.CreateTagRule(rt, ApplicationName, res) if err != nil { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } } else { err = service.UpdateTagRule(rt, ApplicationName, res) if err != nil { c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) return } } c.JSON(http.StatusOK, model.NewSuccessResp(nil)) } } func isGrayTag(tag *mesh_proto.Tag) bool { if tag.Name == "" || tag.Addresses != nil || len(tag.Addresses) != 0 { return false } return true } func generateDefaultTagRule(name string, version string, enable, force bool) *core_mesh.TagRouteResource { return &core_mesh.TagRouteResource{ Meta: nil, Spec: &mesh_proto.TagRoute{ Enabled: enable, Key: name, ConfigVersion: version, Force: force, Tags: make([]*mesh_proto.Tag, 0), }, } }