From fa50017b5b9c89613ec6ac8bdb0cfdd66ebd768e Mon Sep 17 00:00:00 2001 From: Min Min Date: Wed, 22 Jan 2025 09:09:13 +0800 Subject: [PATCH] add debug logs for api Signed-off-by: Min Min --- pkg/cli/initconfig/cmd/init.go | 1 + .../repository/models/wokflow_task_v4.go | 5 +- .../repository/models/workflow_task_revert.go | 38 +++++++++ .../mongodb/workflow_task_revert.go | 83 +++++++++++++++++++ .../aslan/core/workflow/handler/router.go | 1 + .../core/workflow/handler/workflow_task_v4.go | 59 ++++++++++++- .../service/workflow/workflow_task_v4.go | 83 +++++++++++++++++++ pkg/tool/nacos/client.go | 5 +- 8 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 pkg/microservice/aslan/core/common/repository/models/workflow_task_revert.go create mode 100644 pkg/microservice/aslan/core/common/repository/mongodb/workflow_task_revert.go diff --git a/pkg/cli/initconfig/cmd/init.go b/pkg/cli/initconfig/cmd/init.go index 50f7c01ef5..444844fc53 100644 --- a/pkg/cli/initconfig/cmd/init.go +++ b/pkg/cli/initconfig/cmd/init.go @@ -193,6 +193,7 @@ func createOrUpdateMongodbIndex(ctx context.Context) { commonrepo.NewSAEEnvColl(), commonrepo.NewEnvInfoColl(), commonrepo.NewApprovalTicketColl(), + commonrepo.NewWorkflowTaskRevertColl(), // msg queue commonrepo.NewMsgQueueCommonColl(), diff --git a/pkg/microservice/aslan/core/common/repository/models/wokflow_task_v4.go b/pkg/microservice/aslan/core/common/repository/models/wokflow_task_v4.go index d6834651f1..45c37ca44c 100644 --- a/pkg/microservice/aslan/core/common/repository/models/wokflow_task_v4.go +++ b/pkg/microservice/aslan/core/common/repository/models/wokflow_task_v4.go @@ -39,6 +39,7 @@ type WorkflowTask struct { GlobalContext map[string]string `bson:"global_context" json:"global_context"` ClusterIDMap map[string]bool `bson:"cluster_id_map" json:"cluster_id_map"` Status config.Status `bson:"status" json:"status,omitempty"` + Reverted bool `bson:"reverted" json:"reverted"` Remark string `bson:"remark,omitempty" json:"remark"` TaskCreator string `bson:"task_creator" json:"task_creator,omitempty"` TaskCreatorID string `bson:"task_creator_id" json:"task_creator_id,omitempty"` @@ -117,7 +118,8 @@ type JobTask struct { ErrorHandlerUserID string `bson:"error_handler_user_id" yaml:"error_handler_user_id" json:"error_handler_user_id"` ErrorHandlerUserName string `bson:"error_handler_username" yaml:"error_handler_username" json:"error_handler_username"` - RetryCount int `bson:"retry_count" json:"retry_count" yaml:"retry_count"` + RetryCount int `bson:"retry_count" json:"retry_count" yaml:"retry_count"` + Reverted bool `bson:"reverted" json:"reverted" yaml:"reverted"` } type TaskJobInfo struct { @@ -137,6 +139,7 @@ type WorkflowTaskPreview struct { WorkflowDisplayName string `bson:"workflow_display_name" json:"workflow_display_name"` Remark string `bson:"remark" json:"remark"` Status config.Status `bson:"status" json:"status"` + Reverted bool `bson:"reverted" json:"reverted"` CreateTime int64 `bson:"create_time" json:"create_time,omitempty"` StartTime int64 `bson:"start_time" json:"start_time,omitempty"` EndTime int64 `bson:"end_time" json:"end_time,omitempty"` diff --git a/pkg/microservice/aslan/core/common/repository/models/workflow_task_revert.go b/pkg/microservice/aslan/core/common/repository/models/workflow_task_revert.go new file mode 100644 index 0000000000..d955523e1e --- /dev/null +++ b/pkg/microservice/aslan/core/common/repository/models/workflow_task_revert.go @@ -0,0 +1,38 @@ +/* + * Copyright 2025 The KodeRover Authors. + * + * Licensed 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 models + +import ( + "github.com/koderover/zadig/v2/pkg/microservice/aslan/config" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type WorkflowTaskRevert struct { + ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` + TaskID int64 `bson:"task_id" json:"task_id"` + WorkflowName string `bson:"workflow_name" json:"workflow_name"` + JobName string `bson:"job_name" json:"job_name"` + RevertSpec interface{} `bson:"revert_spec" json:"revert_spec"` + CreateTime int64 `bson:"create_time" json:"create_time,omitempty"` + TaskCreator string `bson:"creator" json:"creator,omitempty"` + TaskCreatorID string `bson:"creator_id" json:"creator_id,omitempty"` + Status config.Status `bson:"status" json:"status,omitempty"` +} + +func (WorkflowTaskRevert) TableName() string { + return "workflow_task_revert" +} diff --git a/pkg/microservice/aslan/core/common/repository/mongodb/workflow_task_revert.go b/pkg/microservice/aslan/core/common/repository/mongodb/workflow_task_revert.go new file mode 100644 index 0000000000..3862630ee2 --- /dev/null +++ b/pkg/microservice/aslan/core/common/repository/mongodb/workflow_task_revert.go @@ -0,0 +1,83 @@ +/* +Copyright 2025 The KodeRover Authors. + +Licensed 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 mongodb + +import ( + "context" + "fmt" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + + "github.com/koderover/zadig/v2/pkg/microservice/aslan/config" + "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" + mongotool "github.com/koderover/zadig/v2/pkg/tool/mongo" +) + +type WorkflowTasKRevertColl struct { + *mongo.Collection + + coll string +} + +func NewWorkflowTaskRevertColl() *WorkflowTasKRevertColl { + name := models.WorkflowTaskRevert{}.TableName() + return &WorkflowTasKRevertColl{Collection: mongotool.Database(config.MongoDatabase()).Collection(name), coll: name} +} + +func (c *WorkflowTasKRevertColl) GetCollectionName() string { + return c.coll +} + +func (c *WorkflowTasKRevertColl) EnsureIndex(ctx context.Context) error { + mod := []mongo.IndexModel{ + { + Keys: bson.D{ + bson.E{Key: "workflow_name", Value: 1}, + bson.E{Key: "task_id", Value: 1}, + bson.E{Key: "job_name", Value: 1}, + }, + Options: options.Index().SetUnique(false), + }, + { + Keys: bson.M{"create_time": 1}, + Options: options.Index().SetUnique(false), + }, + } + + _, err := c.Indexes().CreateMany(ctx, mod) + + return err +} + +func (c *WorkflowTasKRevertColl) Create(obj *models.WorkflowTaskRevert) (string, error) { + if obj == nil { + return "", fmt.Errorf("nil object") + } + + res, err := c.InsertOne(context.TODO(), obj) + if err != nil { + return "", err + } + ID, ok := res.InsertedID.(primitive.ObjectID) + if !ok { + return "", fmt.Errorf("failed to get object id from create") + } + return ID.Hex(), err +} diff --git a/pkg/microservice/aslan/core/workflow/handler/router.go b/pkg/microservice/aslan/core/workflow/handler/router.go index 68fff25780..54c99bb6b9 100644 --- a/pkg/microservice/aslan/core/workflow/handler/router.go +++ b/pkg/microservice/aslan/core/workflow/handler/router.go @@ -212,6 +212,7 @@ func (*Router) Inject(router *gin.RouterGroup) { taskV4.POST("/breakpoint/:workflowName/:jobName/task/:taskID/:position", SetWorkflowTaskV4Breakpoint) taskV4.POST("/debug/:workflowName/task/:taskID", EnableDebugWorkflowTaskV4) taskV4.DELETE("/debug/:workflowName/:jobName/task/:taskID/:position", StopDebugWorkflowTaskJobV4) + taskV4.POST("/revert/:workflowName/:jobName/task/:taskID", RevertWorkflowTaskV4Job) taskV4.POST("/approve", ApproveStage) taskV4.POST("/handle/error", HandleJobError) taskV4.GET("/workflow/:workflowName/taskId/:taskId/job/:jobName", GetWorkflowV4ArtifactFileContent) diff --git a/pkg/microservice/aslan/core/workflow/handler/workflow_task_v4.go b/pkg/microservice/aslan/core/workflow/handler/workflow_task_v4.go index fa3007ab11..3fd4e81524 100644 --- a/pkg/microservice/aslan/core/workflow/handler/workflow_task_v4.go +++ b/pkg/microservice/aslan/core/workflow/handler/workflow_task_v4.go @@ -560,7 +560,6 @@ func StopDebugWorkflowTaskJobV4(c *gin.Context) { defer func() { internalhandler.JSONResponse(c, ctx) }() if err != nil { - ctx.RespErr = fmt.Errorf("authorization Info Generation failed: err %s", err) ctx.UnAuthorized = true return @@ -601,6 +600,64 @@ func StopDebugWorkflowTaskJobV4(c *gin.Context) { ctx.RespErr = workflow.StopDebugWorkflowTaskJobV4(workflowName, c.Param("jobName"), taskID, c.Param("position"), ctx.Logger) } +type revertWorkflowTaskV4JobReq struct { + Input interface{} `json:"input"` +} + +func RevertWorkflowTaskV4Job(c *gin.Context) { + ctx, err := internalhandler.NewContextWithAuthorization(c) + defer func() { internalhandler.JSONResponse(c, ctx) }() + + if err != nil { + ctx.RespErr = fmt.Errorf("authorization Info Generation failed: err %s", err) + ctx.UnAuthorized = true + return + } + + args := new(revertWorkflowTaskV4JobReq) + data := getBody(c) + if err := json.Unmarshal([]byte(data), args); err != nil { + log.Errorf("CreateWorkflowTaskv4 json.Unmarshal err : %s", err) + ctx.RespErr = e.ErrInvalidParam.AddDesc(err.Error()) + return + } + + workflowName := c.Param("workflowName") + + w, err := workflow.FindWorkflowV4Raw(workflowName, ctx.Logger) + if err != nil { + ctx.Logger.Errorf("EnableDebugWorkflowTaskV4 error: %v", err) + ctx.RespErr = e.ErrInvalidParam.AddErr(err) + return + } + + // authorization check + if !ctx.Resources.IsSystemAdmin { + if _, ok := ctx.Resources.ProjectAuthInfo[w.Project]; !ok { + ctx.UnAuthorized = true + return + } + + if !ctx.Resources.ProjectAuthInfo[w.Project].IsProjectAdmin && + !ctx.Resources.ProjectAuthInfo[w.Project].Workflow.Execute { + // check if the permission is given by collaboration mode + permitted, err := internalhandler.GetCollaborationModePermission(ctx.UserID, w.Project, types.ResourceTypeWorkflow, w.Name, types.WorkflowActionRun) + if err != nil || !permitted { + ctx.UnAuthorized = true + return + } + } + } + + taskID, err := strconv.ParseInt(c.Param("taskID"), 10, 64) + if err != nil { + ctx.RespErr = e.ErrInvalidParam.AddDesc("invalid task id") + return + } + + ctx.RespErr = workflow.RevertWorkflowTaskV4Job(workflowName, c.Param("jobName"), taskID, args.Input, ctx.UserName, ctx.UserID, ctx.Logger) +} + func ApproveStage(c *gin.Context) { ctx := internalhandler.NewContext(c) defer func() { internalhandler.JSONResponse(c, ctx) }() diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go index a596ab1772..7d4d92ac33 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_task_v4.go @@ -50,6 +50,7 @@ import ( e "github.com/koderover/zadig/v2/pkg/tool/errors" larktool "github.com/koderover/zadig/v2/pkg/tool/lark" "github.com/koderover/zadig/v2/pkg/tool/log" + "github.com/koderover/zadig/v2/pkg/tool/nacos" s3tool "github.com/koderover/zadig/v2/pkg/tool/s3" "github.com/koderover/zadig/v2/pkg/tool/sonar" workflowtool "github.com/koderover/zadig/v2/pkg/tool/workflow" @@ -1090,6 +1091,87 @@ func StopDebugWorkflowTaskJobV4(workflowName, jobName string, taskID int64, posi return nil } +func RevertWorkflowTaskV4Job(workflowName, jobName string, taskID int64, input interface{}, userName, userID string, logger *zap.SugaredLogger) error { + task, err := commonrepo.NewworkflowTaskv4Coll().Find(workflowName, taskID) + if err != nil { + logger.Errorf("find workflowTaskV4 error: %s", err) + return e.ErrGetTask.AddErr(err) + } + + for _, stage := range task.Stages { + for _, job := range stage.Jobs { + if job.Name == jobName { + switch job.JobType { + case string(config.JobNacos): + jobTaskSpec := &commonmodels.JobTaskNacosSpec{} + if err := commonmodels.IToi(job.Spec, jobTaskSpec); err != nil { + logger.Error(err) + return fmt.Errorf("failed to decode nacos job spec, error: %s", err) + } + inputSpec := make([]*commonmodels.NacosData, 0) + + if err := commonmodels.IToi(input, inputSpec); err != nil { + log.Errorf("failed to decode nacos revert input spec, error: %s", err) + return fmt.Errorf("failed to decode nacos revert input spec, error: %s", err) + } + + err = revertNacosJob(jobTaskSpec, inputSpec) + if err != nil { + log.Errorf("failed to revert nacos job %s, error: %s", job.Name, err) + return fmt.Errorf("failed to revert nacos job: %s, error: %s", job.Name, err) + } + + job.Reverted = true + err = commonrepo.NewworkflowTaskv4Coll().Update(task.ID.Hex(), task) + if err != nil { + log.Errorf("failed to update nacos job revert information, error: %s", err) + } + + revertTaskSpec := &commonmodels.JobTaskNacosSpec{ + NacosID: jobTaskSpec.NacosID, + NamespaceID: jobTaskSpec.NamespaceID, + NamespaceName: jobTaskSpec.NamespaceName, + NacosAddr: jobTaskSpec.NacosAddr, + UserName: jobTaskSpec.UserName, + Password: jobTaskSpec.Password, + } + + _, err = commonrepo.NewWorkflowTaskRevertColl().Create(&commonmodels.WorkflowTaskRevert{ + TaskID: taskID, + WorkflowName: workflowName, + JobName: jobName, + RevertSpec: revertTaskSpec, + CreateTime: time.Now().Unix(), + TaskCreator: userName, + TaskCreatorID: userID, + Status: config.StatusPassed, + }) + return nil + default: + return fmt.Errorf("job of type: %s does not support reverting yet") + } + } + } + } + + return fmt.Errorf("failed to revert job: %s, job not found") +} + +func revertNacosJob(jobspec *commonmodels.JobTaskNacosSpec, input []*commonmodels.NacosData) error { + client, err := nacos.NewNacosClient(jobspec.NacosAddr, jobspec.UserName, jobspec.Password) + if err != nil { + return err + } + + for _, data := range input { + if err := client.UpdateConfig(data.DataID, data.Group, jobspec.NamespaceID, data.Content, data.Format); err != nil { + return err + } + } + + return nil +} + type TaskHistoryFilter struct { PageSize int64 `json:"page_size" form:"page_size,default=20"` PageNum int64 `json:"page_num" form:"page_num,default=1"` @@ -1151,6 +1233,7 @@ func ListWorkflowTaskV4ByFilter(filter *TaskHistoryFilter, filterList []string, WorkflowDisplayName: task.WorkflowDisplayName, Remark: task.Remark, Status: task.Status, + Reverted: task.Reverted, CreateTime: task.CreateTime, StartTime: task.StartTime, EndTime: task.EndTime, diff --git a/pkg/tool/nacos/client.go b/pkg/tool/nacos/client.go index 6db919ef29..068cabcf02 100644 --- a/pkg/tool/nacos/client.go +++ b/pkg/tool/nacos/client.go @@ -17,6 +17,7 @@ limitations under the License. package nacos import ( + "fmt" "net/url" "strconv" "strings" @@ -152,8 +153,10 @@ func (c *Client) ListConfigs(namespaceID string) ([]*types.NacosConfig, error) { "tenant": namespaceID, "accessToken": c.token, }) - if _, err := c.Client.Get(url, params, httpclient.SetResult(res)); err != nil { + if resp, err := c.Client.Get(url, params, httpclient.SetResult(res)); err != nil { return nil, errors.Wrap(err, "list nacos config failed") + } else { + fmt.Println(resp.Body()) } for _, conf := range res.PageItems { resp = append(resp, &types.NacosConfig{