From 25897a79cda9d4eaddab1bab51c54bc4addf4763 Mon Sep 17 00:00:00 2001 From: Patrick Zhao Date: Fri, 24 Jan 2025 10:16:45 +0800 Subject: [PATCH] improve preview Signed-off-by: Patrick Zhao --- .../core/environment/handler/environment.go | 23 +- .../core/environment/service/environment.go | 234 ++++++++++++++---- .../core/workflow/handler/workflow_v4.go | 20 +- .../workflow/service/workflow/workflow_v4.go | 11 +- 4 files changed, 221 insertions(+), 67 deletions(-) diff --git a/pkg/microservice/aslan/core/environment/handler/environment.go b/pkg/microservice/aslan/core/environment/handler/environment.go index 5f356afa8d..7b8d04fa4c 100644 --- a/pkg/microservice/aslan/core/environment/handler/environment.go +++ b/pkg/microservice/aslan/core/environment/handler/environment.go @@ -681,6 +681,21 @@ func AffectedServices(c *gin.Context) { ctx.Resp, ctx.RespErr = service.GetAffectedServices(projectName, envName, arg, ctx.Logger) } +// @summary 预览Helm服务环境变量 +// @description +// @tags environment +// @accept json +// @produce json +// @Param projectName query string true "项目标识" +// @Param name path string true "环境名称" +// @Param serviceName query string true "服务名称或release名称" +// @Param scene query service.EstimateValuesScene true "使用场景" +// @Param isHelmChartDeploy query bool true "是否是helm实力化部署" +// @Param updateServiceRevision query bool true "是否更新服务配置" +// @Param production query bool true "是否为生产环境" +// @Param body body service.EstimateValuesArg true "body" +// @Success 200 {object} service.GetHelmValuesDifferenceResp +// @router /api/aslan/environment/environments/{name}/estimated-values [post] func EstimatedValues(c *gin.Context) { ctx := internalhandler.NewContext(c) defer func() { internalhandler.JSONResponse(c, ctx) }() @@ -704,6 +719,12 @@ func EstimatedValues(c *gin.Context) { return } + updateServiceRevision := c.Query("updateServiceRevision") + if updateServiceRevision == "" { + ctx.RespErr = e.ErrInvalidParam.AddDesc("updateServiceRevision can't be empty!") + return + } + arg := new(service.EstimateValuesArg) if err := c.ShouldBind(arg); err != nil { ctx.RespErr = e.ErrInvalidParam.AddDesc(err.Error()) @@ -720,7 +741,7 @@ func EstimatedValues(c *gin.Context) { } } - ctx.Resp, ctx.RespErr = service.GeneEstimatedValues(projectName, envName, serviceName, c.Query("scene"), c.Query("format"), arg, isHelmChartDeploy == "true", ctx.Logger) + ctx.Resp, ctx.RespErr = service.GeneEstimatedValues2(projectName, envName, serviceName, service.EstimateValuesScene(c.Query("scene")), arg, updateServiceRevision == "true", production, isHelmChartDeploy == "true", ctx.Logger) } func SyncHelmProductRenderset(c *gin.Context) { ctx, err := internalhandler.NewContextWithAuthorization(c) diff --git a/pkg/microservice/aslan/core/environment/service/environment.go b/pkg/microservice/aslan/core/environment/service/environment.go index e768cf0136..0108d39376 100644 --- a/pkg/microservice/aslan/core/environment/service/environment.go +++ b/pkg/microservice/aslan/core/environment/service/environment.go @@ -1339,12 +1339,12 @@ func genImageFromYaml(c *commonmodels.Container, serviceValuesYaml, defaultValue return image, nil } -func prepareEstimateDataForEnvCreation(productName, serviceName string, production bool, isHelmChartDeploy bool, log *zap.SugaredLogger) (*commonmodels.ProductService, *commonmodels.Service, error) { +func prepareEstimateDataForEnvCreation(projectName, serviceName string, production bool, isHelmChartDeploy bool, log *zap.SugaredLogger) (*commonmodels.ProductService, *commonmodels.Service, error) { if isHelmChartDeploy { prodSvc := &commonmodels.ProductService{ ServiceName: serviceName, ReleaseName: serviceName, - ProductName: productName, + ProductName: projectName, Type: setting.HelmChartDeployType, Render: &templatemodels.ServiceRender{ ServiceName: serviceName, @@ -1358,7 +1358,7 @@ func prepareEstimateDataForEnvCreation(productName, serviceName string, producti } else { templateService, err := repository.QueryTemplateService(&commonrepo.ServiceFindOption{ ServiceName: serviceName, - ProductName: productName, + ProductName: projectName, Type: setting.HelmDeployType, }, production) if err != nil { @@ -1367,11 +1367,10 @@ func prepareEstimateDataForEnvCreation(productName, serviceName string, producti } prodSvc := &commonmodels.ProductService{ - ServiceName: serviceName, - ProductName: productName, - Revision: templateService.Revision, - Containers: templateService.Containers, - VariableYaml: templateService.VariableYaml, + ServiceName: serviceName, + ProductName: projectName, + Revision: templateService.Revision, + Containers: templateService.Containers, Render: &templatemodels.ServiceRender{ ServiceName: serviceName, OverrideYaml: &templatemodels.CustomYaml{}, @@ -1383,7 +1382,7 @@ func prepareEstimateDataForEnvCreation(productName, serviceName string, producti } } -func prepareEstimateDataForEnvUpdate(productName, envName, serviceOrReleaseName, scene string, production bool, isHelmChartDeploy bool, log *zap.SugaredLogger) ( +func prepareEstimateDataForEnvUpdate(productName, envName, serviceOrReleaseName string, scene EstimateValuesScene, production bool, isHelmChartDeploy bool, log *zap.SugaredLogger) ( *commonmodels.ProductService, *commonmodels.Service, *commonmodels.Product, error) { productInfo, err := commonrepo.NewProductColl().Find(&commonrepo.ProductFindOptions{ Name: productName, @@ -1416,7 +1415,7 @@ func prepareEstimateDataForEnvUpdate(productName, envName, serviceOrReleaseName, } else { targetSvcTmplRevision := int64(0) prodSvc = productInfo.GetServiceMap()[serviceOrReleaseName] - if scene == usageScenarioUpdateRenderSet { + if scene == EstimateValuesSceneUpdateService { if prodSvc == nil { return nil, nil, nil, fmt.Errorf("can't find service in env: %s, name %s", productInfo.EnvName, serviceOrReleaseName) } @@ -1488,85 +1487,222 @@ func GetAffectedServices(productName, envName string, arg *K8sRendersetArg, log return ret, nil } -func GeneEstimatedValues(productName, envName, serviceOrReleaseName, scene, format string, arg *EstimateValuesArg, isHelmChartDeploy bool, log *zap.SugaredLogger) (interface{}, error) { +type EstimateValuesScene string + +const ( + EstimateValuesSceneCreateEnv EstimateValuesScene = "create_env" + EstimateValuesSceneCreateService EstimateValuesScene = "create_service" + EstimateValuesSceneUpdateService EstimateValuesScene = "update_service" + EstimateValuesSceneGenFlatMap EstimateValuesScene = "gen_flat_map" +) + +type GetHelmValuesDifferenceResp struct { + Current string `json:"current"` + Latest string `json:"latest"` + LatestFlatMap map[string]interface{} `json:"latest_flat_map"` +} + +func GeneEstimatedValues2(projectName, envName, serviceOrReleaseName string, scene EstimateValuesScene, arg *EstimateValuesArg, updateServiceRevision, isProduction, isHelmChartDeploy bool, log *zap.SugaredLogger) (*GetHelmValuesDifferenceResp, error) { var ( - productSvc *commonmodels.ProductService - tmplSvc *commonmodels.Service - productInfo *commonmodels.Product - err error + prodSvc *commonmodels.ProductService + tmplSvc *commonmodels.Service + prod *commonmodels.Product + err error ) switch scene { - case usageScenarioCreateEnv: - productInfo = &commonmodels.Product{} - productSvc, tmplSvc, err = prepareEstimateDataForEnvCreation(productName, serviceOrReleaseName, arg.Production, isHelmChartDeploy, log) + case EstimateValuesSceneCreateEnv: + prod = &commonmodels.Product{} + prodSvc, tmplSvc, err = prepareEstimateDataForEnvCreation(projectName, serviceOrReleaseName, arg.Production, isHelmChartDeploy, log) + if err != nil { + return nil, fmt.Errorf("failed to prepare estimate data for env creation, err: %s", err) + } + case EstimateValuesSceneCreateService, EstimateValuesSceneUpdateService, EstimateValuesSceneGenFlatMap: + prodSvc, tmplSvc, prod, err = prepareEstimateDataForEnvUpdate(projectName, envName, serviceOrReleaseName, scene, arg.Production, isHelmChartDeploy, log) + if err != nil { + return nil, fmt.Errorf("failed to prepare estimate data for env update, err: %s", err) + } default: - productSvc, tmplSvc, productInfo, err = prepareEstimateDataForEnvUpdate(productName, envName, serviceOrReleaseName, scene, arg.Production, isHelmChartDeploy, log) - } - - if err != nil { - return nil, fmt.Errorf("failed to prepare estimated value data, err %s", err) - } - - targetChart := productSvc.Render - - tempArg := &commonservice.HelmSvcRenderArg{OverrideValues: arg.OverrideValues} - if targetChart.OverrideYaml == nil { - targetChart.OverrideYaml = &templatemodels.CustomYaml{} + return nil, fmt.Errorf("invalid scene: %s", scene) + } + + currentYaml := "" + latestYaml := "" + if scene == EstimateValuesSceneCreateEnv || scene == EstimateValuesSceneCreateService || scene == EstimateValuesSceneGenFlatMap { + // service already exists in the current environment, create it + currentYaml = "" + } else if scene == EstimateValuesSceneUpdateService { + // service not exists in the current environment, update it + // + // get current values yaml + resp, err := commonservice.GetChartValues(projectName, envName, serviceOrReleaseName, isHelmChartDeploy, isProduction, true) + if err != nil { + err = fmt.Errorf("failed to get the current service[%s] values from project: %s, env: %s", serviceOrReleaseName, projectName, envName) + log.Error(err) + return nil, err + } else { + currentYaml = resp.ValuesYaml + } } - targetChart.OverrideYaml.YamlContent = arg.OverrideYaml - targetChart.OverrideValues = tempArg.ToOverrideValueString() - mergedValues := "" + // generate the new yaml content if isHelmChartDeploy { chartRepo, err := commonrepo.NewHelmRepoColl().Find(&commonrepo.HelmRepoFindOption{RepoName: arg.ChartRepo}) if err != nil { return nil, fmt.Errorf("failed to query chart-repo info, repoName: %s", arg.ChartRepo) } - client, err := commonutil.NewHelmClient(chartRepo) if err != nil { return nil, fmt.Errorf("failed to new helm client, err %s", err) } - valuesYaml, err := client.GetChartValues(commonutil.GeneHelmRepo(chartRepo), productName, serviceOrReleaseName, arg.ChartRepo, arg.ChartName, arg.ChartVersion, arg.Production) + latestChartValuesYaml, err := client.GetChartValues(commonutil.GeneHelmRepo(chartRepo), projectName, serviceOrReleaseName, arg.ChartRepo, arg.ChartName, arg.ChartVersion, arg.Production) if err != nil { return nil, fmt.Errorf("failed to get chart values, chartRepo: %s, chartName: %s, chartVersion: %s, err %s", arg.ChartRepo, arg.ChartName, arg.ChartVersion, err) } + tempArg := &commonservice.HelmSvcRenderArg{OverrideValues: arg.OverrideValues} + prodSvc.GetServiceRender().SetOverrideYaml(arg.OverrideYaml) + prodSvc.GetServiceRender().OverrideValues = tempArg.ToOverrideValueString() + helmDeploySvc := helmservice.NewHelmDeployService() - mergedValues, err = helmDeploySvc.GenMergedValues(productSvc, productInfo.DefaultValues, nil) + mergedYaml, err := helmDeploySvc.GenMergedValues(prodSvc, prod.DefaultValues, nil) if err != nil { - return nil, e.ErrUpdateRenderSet.AddDesc(fmt.Sprintf("failed to merge values, err %s", err)) + return nil, fmt.Errorf("failed to merge override values, err: %s", err) } - mergedValues, err = helmDeploySvc.GeneFullValues(valuesYaml, mergedValues) + + latestYaml, err = helmDeploySvc.GeneFullValues(latestChartValuesYaml, mergedYaml) if err != nil { - return nil, e.ErrUpdateRenderSet.AddDesc(fmt.Sprintf("failed to generate full values, err %s", err)) + return nil, fmt.Errorf("failed to generate full values, err: %s", err) } + + currentYaml = strings.TrimSuffix(currentYaml, "\n") + latestYaml = strings.TrimSuffix(latestYaml, "\n") } else { + tempArg := &commonservice.HelmSvcRenderArg{OverrideValues: arg.OverrideValues} + prodSvc.GetServiceRender().SetOverrideYaml(arg.OverrideYaml) + prodSvc.GetServiceRender().OverrideValues = tempArg.ToOverrideValueString() + helmDeploySvc := helmservice.NewHelmDeployService() - mergedValues, err = helmDeploySvc.GenMergedValues(productSvc, productInfo.DefaultValues, nil) + yamlContent, err := helmDeploySvc.GenMergedValues(prodSvc, prod.DefaultValues, nil) if err != nil { - return nil, e.ErrUpdateRenderSet.AddDesc(fmt.Sprintf("failed to merge values, err %s", err)) + return nil, fmt.Errorf("failed to generate merged values yaml, err: %s", err) } - mergedValues, err = helmDeploySvc.GeneFullValues(tmplSvc.HelmChart.ValuesYaml, mergedValues) + + fullValuesYaml, err := helmDeploySvc.GeneFullValues(tmplSvc.HelmChart.ValuesYaml, yamlContent) if err != nil { - return nil, e.ErrUpdateRenderSet.AddDesc(fmt.Sprintf("failed to generate full values, err %s", err)) + return nil, fmt.Errorf("failed to generate full values yaml, err: %s", err) } + + // re-marshal it into string to make sure indentation is right + tmp := make(map[string]interface{}) + if err := yaml.Unmarshal([]byte(fullValuesYaml), &tmp); err != nil { + log.Errorf("failed to unmarshal latest yaml content, err: %s", err) + return nil, err + } + latestYamlBytes, err := yaml.Marshal(tmp) + if err != nil { + log.Errorf("failed to marshal latest yaml content, err: %s", err) + return nil, err + } + latestYaml = string(latestYamlBytes) + } + + resp := &GetHelmValuesDifferenceResp{ + Current: currentYaml, + Latest: latestYaml, } - switch format { - case "flatMap": - mapData, err := converter.YamlToFlatMap([]byte(mergedValues)) + if scene == EstimateValuesSceneGenFlatMap { + mapData, err := converter.YamlToFlatMap([]byte(latestYaml)) if err != nil { return nil, e.ErrUpdateRenderSet.AddDesc(fmt.Sprintf("failed to generate flat map , err %s", err)) } - return mapData, nil - default: - return &RawYamlResp{YamlContent: mergedValues}, nil + resp.LatestFlatMap = mapData } + + return resp, nil } +// func GeneEstimatedValues(productName, envName, serviceOrReleaseName, scene, format string, arg *EstimateValuesArg, isHelmChartDeploy bool, log *zap.SugaredLogger) (interface{}, error) { +// var ( +// productSvc *commonmodels.ProductService +// tmplSvc *commonmodels.Service +// productInfo *commonmodels.Product +// err error +// ) + +// switch scene { +// case usageScenarioCreateEnv: +// productInfo = &commonmodels.Product{} +// productSvc, tmplSvc, err = prepareEstimateDataForEnvCreation(productName, serviceOrReleaseName, arg.Production, isHelmChartDeploy, log) +// default: +// productSvc, tmplSvc, productInfo, err = prepareEstimateDataForEnvUpdate(productName, envName, serviceOrReleaseName, scene, arg.Production, isHelmChartDeploy, log) +// } + +// if err != nil { +// return nil, fmt.Errorf("failed to prepare estimated value data, err %s", err) +// } + +// targetChart := productSvc.Render + +// tempArg := &commonservice.HelmSvcRenderArg{OverrideValues: arg.OverrideValues} +// if targetChart.OverrideYaml == nil { +// targetChart.OverrideYaml = &templatemodels.CustomYaml{} +// } +// targetChart.OverrideYaml.YamlContent = arg.OverrideYaml +// targetChart.OverrideValues = tempArg.ToOverrideValueString() + +// mergedValues := "" +// if isHelmChartDeploy { +// chartRepo, err := commonrepo.NewHelmRepoColl().Find(&commonrepo.HelmRepoFindOption{RepoName: arg.ChartRepo}) +// if err != nil { +// return nil, fmt.Errorf("failed to query chart-repo info, repoName: %s", arg.ChartRepo) +// } + +// client, err := commonutil.NewHelmClient(chartRepo) +// if err != nil { +// return nil, fmt.Errorf("failed to new helm client, err %s", err) +// } + +// valuesYaml, err := client.GetChartValues(commonutil.GeneHelmRepo(chartRepo), productName, serviceOrReleaseName, arg.ChartRepo, arg.ChartName, arg.ChartVersion, arg.Production) +// if err != nil { +// return nil, fmt.Errorf("failed to get chart values, chartRepo: %s, chartName: %s, chartVersion: %s, err %s", arg.ChartRepo, arg.ChartName, arg.ChartVersion, err) +// } + +// helmDeploySvc := helmservice.NewHelmDeployService() +// mergedValues, err = helmDeploySvc.GenMergedValues(productSvc, productInfo.DefaultValues, nil) +// if err != nil { +// return nil, e.ErrUpdateRenderSet.AddDesc(fmt.Sprintf("failed to merge values, err %s", err)) +// } +// mergedValues, err = helmDeploySvc.GeneFullValues(valuesYaml, mergedValues) +// if err != nil { +// return nil, e.ErrUpdateRenderSet.AddDesc(fmt.Sprintf("failed to generate full values, err %s", err)) +// } +// } else { +// helmDeploySvc := helmservice.NewHelmDeployService() +// mergedValues, err = helmDeploySvc.GenMergedValues(productSvc, productInfo.DefaultValues, nil) +// if err != nil { +// return nil, e.ErrUpdateRenderSet.AddDesc(fmt.Sprintf("failed to merge values, err %s", err)) +// } +// mergedValues, err = helmDeploySvc.GeneFullValues(tmplSvc.HelmChart.ValuesYaml, mergedValues) +// if err != nil { +// return nil, e.ErrUpdateRenderSet.AddDesc(fmt.Sprintf("failed to generate full values, err %s", err)) +// } +// } + +// switch format { +// case "flatMap": +// mapData, err := converter.YamlToFlatMap([]byte(mergedValues)) +// if err != nil { +// return nil, e.ErrUpdateRenderSet.AddDesc(fmt.Sprintf("failed to generate flat map , err %s", err)) +// } +// return mapData, nil +// default: +// return &RawYamlResp{YamlContent: mergedValues}, nil +// } +// } + // check if override values or yaml content changes // return [need-Redeploy] and [need-SaveToDB] func checkOverrideValuesChange(source *templatemodels.ServiceRender, args *commonservice.HelmSvcRenderArg) (bool, bool) { diff --git a/pkg/microservice/aslan/core/workflow/handler/workflow_v4.go b/pkg/microservice/aslan/core/workflow/handler/workflow_v4.go index c78feb0b80..413b398bca 100644 --- a/pkg/microservice/aslan/core/workflow/handler/workflow_v4.go +++ b/pkg/microservice/aslan/core/workflow/handler/workflow_v4.go @@ -53,13 +53,12 @@ type filterDeployServiceVarsQuery struct { } type getHelmValuesDifferenceReq struct { - ServiceName string `json:"service_name"` - VariableYaml string `json:"variable_yaml"` - EnvName string `json:"env_name"` - IsProduction bool `json:"production"` - IsHelmChartDeploy bool `json:"is_helm_chart_deploy"` - UpdateServiceRevision bool `json:"update_service_revision"` - ServiceModules []*ModuleAndImage `json:"service_modules"` + ServiceName string `json:"service_name"` + VariableYaml string `json:"variable_yaml"` + EnvName string `json:"env_name"` + IsProduction bool `json:"production"` + IsHelmChartDeploy bool `json:"is_helm_chart_deploy"` + UpdateServiceRevision bool `json:"update_service_revision"` } type ModuleAndImage struct { @@ -1303,12 +1302,7 @@ func CompareHelmServiceYamlInEnv(c *gin.Context) { return } projectName := c.Query("projectName") - - images := make([]string, 0) - for _, imageInfos := range req.ServiceModules { - images = append(images, imageInfos.Image) - } - ctx.Resp, ctx.RespErr = workflow.CompareHelmServiceYamlInEnv(req.ServiceName, req.VariableYaml, req.EnvName, projectName, images, req.IsProduction, req.UpdateServiceRevision, req.IsHelmChartDeploy, ctx.Logger) + ctx.Resp, ctx.RespErr = workflow.CompareHelmServiceYamlInEnv(projectName, req.EnvName, req.ServiceName, req.VariableYaml, req.IsProduction, req.UpdateServiceRevision, req.IsHelmChartDeploy, ctx.Logger) } type YamlResponse struct { diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_v4.go b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_v4.go index 9fe4303611..b38c668e2a 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/workflow_v4.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/workflow_v4.go @@ -2329,7 +2329,7 @@ func GetFilteredEnvServices(workflowName, jobName, envName string, serviceNames return resp, nil } -func CompareHelmServiceYamlInEnv(serviceName, variableYaml, envName, projectName string, images []string, isProduction, updateServiceRevision, isHelmChartDeploy bool, log *zap.SugaredLogger) (*GetHelmValuesDifferenceResp, error) { +func CompareHelmServiceYamlInEnv(projectName, envName, serviceName, variableYaml string, isProduction, updateServiceRevision, isHelmChartDeploy bool, log *zap.SugaredLogger) (*GetHelmValuesDifferenceResp, error) { opt := &commonrepo.ProductFindOptions{Name: projectName, EnvName: envName} prod, err := commonrepo.NewProductColl().Find(opt) if err != nil { @@ -2341,13 +2341,16 @@ func CompareHelmServiceYamlInEnv(serviceName, variableYaml, envName, projectName latestYaml := "" prodSvc := prod.GetChartServiceMap()[serviceName] if prodSvc != nil { - helmDeploySvc := helmservice.NewHelmDeployService() - currentYaml, err = helmDeploySvc.GenMergedValues(prodSvc, prod.DefaultValues, nil) + resp, err := commonservice.GetChartValues(projectName, envName, serviceName, true, isProduction, true) if err != nil { - return nil, fmt.Errorf("failed to merge override values, err: %s", err) + log.Infof("failed to get the current service[%s] values from project: %s, env: %s", serviceName, projectName, envName) + currentYaml = "" + } else { + currentYaml = resp.ValuesYaml } prodSvc.GetServiceRender().SetOverrideYaml(variableYaml) + helmDeploySvc := helmservice.NewHelmDeployService() latestYaml, err = helmDeploySvc.GenMergedValues(prodSvc, prod.DefaultValues, nil) if err != nil { return nil, fmt.Errorf("failed to merge override values, err: %s", err)