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 f68200a438..6282f1f75b 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 @@ -307,6 +307,7 @@ type JobTaskHelmDeploySpec struct { YamlContent string `bson:"yaml_content" json:"yaml_content" yaml:"yaml_content"` // UserSuppliedValue added since 1.18, the values that users gives. UserSuppliedValue string `bson:"user_supplied_value" json:"user_supplied_value" yaml:"user_supplied_value"` + ValuesSyncedFromSource bool `bson:"values_synced_from_source" json:"values_synced_from_source" yaml:"values_synced_from_source"` UpdateConfig bool `bson:"update_config" json:"update_config" yaml:"update_config"` SkipCheckRunStatus bool `bson:"skip_check_run_status" json:"skip_check_run_status" yaml:"skip_check_run_status"` SkipCheckHelmWorkfloadStatus bool `bson:"skip_check_helm_workload_status" json:"skip_check_helm_workload_status" yaml:"skip_check_helm_workload_status"` diff --git a/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_helm_deploy.go b/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_helm_deploy.go index 0e595974e8..3fb1ab52ad 100644 --- a/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_helm_deploy.go +++ b/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_helm_deploy.go @@ -35,6 +35,7 @@ import ( "github.com/koderover/zadig/v2/pkg/microservice/aslan/config" commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" + templatemodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models/template" commonrepo "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb" helmservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/helm" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/joblog" @@ -150,6 +151,14 @@ func (c *HelmDeployJobCtl) Run(ctx context.Context) { } newEnvService.DeployStrategy = setting.ServiceDeployStrategyDeploy productInfo.ServiceDeployStrategy[c.jobTaskSpec.ServiceName] = setting.ServiceDeployStrategyDeploy + if currentEnvSvc == nil { + fillHelmValuesSource(newEnvService, latestTmplSvc) + if c.jobTaskSpec.ValuesSyncedFromSource && c.jobTaskSpec.VariableYaml != "" { + serviceRender := newEnvService.GetServiceRender() + serviceRender.SetOverrideYaml(c.jobTaskSpec.VariableYaml) + serviceRender.SetAutoSyncYaml(c.jobTaskSpec.UserSuppliedValue) + } + } // calc final values yaml finalValuesYaml := "" @@ -212,6 +221,18 @@ func (c *HelmDeployJobCtl) Run(ctx context.Context) { logError(c.job, msg, c.logger) return } + if currentEnvSvc == nil && c.jobTaskSpec.ValuesSyncedFromSource { + svcRender := newEnvService.GetServiceRender() + // For a git-loaded helm service (no YamlData on the template), fillHelmValuesSource + // left the source empty. Seed it from the chart git repo so future auto-sync runs + // can re-fetch the latest values.yaml from the same source. + if svcRender.OverrideYaml.Source == "" { + seedChartRepoSource(svcRender, latestTmplSvc) + } + // use the pure source values (before image injection in GenMergedValues) as the + // auto-sync baseline, so that future syncs compare against the unmodified source yaml. + svcRender.SetAutoSyncYaml(c.jobTaskSpec.UserSuppliedValue) + } } newResourceMap, err := genHelmResourceMap(helmDeploySvc, productInfo, newEnvService, latestTmplSvc, helmClient) @@ -346,6 +367,48 @@ func (c *HelmDeployJobCtl) Run(ctx context.Context) { c.job.Status = config.StatusPassed } +func fillHelmValuesSource(envSvc *commonmodels.ProductService, tmplSvc *commonmodels.Service) { + if envSvc == nil || tmplSvc == nil { + return + } + + createFrom, err := tmplSvc.GetHelmCreateFrom() + if err != nil || createFrom.YamlData == nil { + return + } + + serviceRender := envSvc.GetServiceRender() + serviceRender.OverrideYaml.Source = createFrom.YamlData.Source + serviceRender.OverrideYaml.SourceDetail = createFrom.YamlData.SourceDetail + serviceRender.OverrideYaml.SourceID = createFrom.YamlData.SourceID + serviceRender.OverrideYaml.AutoSync = createFrom.YamlData.AutoSync + serviceRender.OverrideYaml.AutoSyncYaml = createFrom.YamlData.AutoSyncYaml +} + +// seedChartRepoSource records the chart git repo as the values source for git-loaded helm services. +func seedChartRepoSource(svcRender *templatemodels.ServiceRender, tmplSvc *commonmodels.Service) { + if svcRender == nil || svcRender.OverrideYaml == nil || tmplSvc == nil { + return + } + if svcRender.OverrideYaml.Source != "" || svcRender.OverrideYaml.SourceDetail != nil { + return + } + if tmplSvc.CodehostID == 0 || tmplSvc.RepoName == "" || tmplSvc.LoadPath == "" { + return + } + svcRender.OverrideYaml.Source = setting.SourceFromGitRepo + svcRender.OverrideYaml.SourceDetail = &commonmodels.CreateFromRepo{ + GitRepoConfig: &templatemodels.GitRepoConfig{ + CodehostID: tmplSvc.CodehostID, + Owner: tmplSvc.RepoOwner, + Repo: tmplSvc.RepoName, + Branch: tmplSvc.BranchName, + Namespace: tmplSvc.RepoNamespace, + }, + LoadPath: fmt.Sprintf("%s/%s", tmplSvc.LoadPath, setting.ValuesYaml), + } +} + type ManifestStruct struct { Manifest string Unstructured *unstructured.Unstructured diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_deploy.go b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_deploy.go index b9b0634272..fac43dd36f 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_deploy.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_deploy.go @@ -24,9 +24,11 @@ import ( "github.com/koderover/zadig/v2/pkg/microservice/aslan/config" commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" + templatemodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models/template" commonrepo "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb" templaterepo "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb/template" commonservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service" + "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/fs" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/kube" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/repository" commontypes "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/types" @@ -639,17 +641,19 @@ func (j DeployJobController) ToTask(taskID int64) ([]*commonmodels.JobTask, erro for jobSubTaskID, svc := range j.jobSpec.Services { var serviceRevision int64 var autoSyncFlag bool + var serviceRender *templatemodels.ServiceRender pSvc, ok := productServiceMap[svc.ServiceName] if ok { serviceRevision = pSvc.Revision + serviceRender = pSvc.GetServiceRender() // if the service is deployed in the env, and the variable is set to auto sync, ignore user input. // sync the values from codehost and set the value merge strategy to override - if pSvc.GetServiceRender().OverrideYaml.AutoSync { + if serviceRender.OverrideYaml.AutoSync { autoSyncFlag = true } if len(j.jobSpec.DeployContents) == 1 && slices.Contains(j.jobSpec.DeployContents, config.DeployImage) && - commonutil.GetChartDeployed(pSvc.GetServiceRender(), product.ServiceDeployStrategy) == setting.ServiceDeployStrategyDraft { + commonutil.GetChartDeployed(serviceRender, product.ServiceDeployStrategy) == setting.ServiceDeployStrategyDraft { return nil, fmt.Errorf("service %s is in draft, cannot deploy image only", svc.ServiceName) } } @@ -698,15 +702,29 @@ func (j DeployJobController) ToTask(taskID int64) ([]*commonmodels.JobTask, erro } if autoSyncFlag || j.jobSpec.ValueSyncStrategy == config.ValueSyncStrategyAuto { - pSvc.GetServiceRender().SetAutoSync(true) - _, values, err := commonservice.SyncYamlFromSource(pSvc.GetServiceRender().OverrideYaml, pSvc.GetServiceRender().OverrideYaml.YamlContent, pSvc.GetServiceRender().OverrideYaml.AutoSyncYaml) - if err != nil { - return nil, fmt.Errorf("failed to sync values for service: %s, error: %s", svc.ServiceName, err) + if serviceRender != nil { + serviceRender.SetAutoSync(true) + _, values, err := commonservice.SyncYamlFromSource(serviceRender.OverrideYaml, serviceRender.OverrideYaml.YamlContent, serviceRender.OverrideYaml.AutoSyncYaml) + if err != nil { + return nil, fmt.Errorf("failed to sync values for service: %s, error: %s", svc.ServiceName, err) + } + serviceRender.SetAutoSync(autoSyncFlag) + jobTaskSpec.UpdateConfig = svc.UpdateConfig + jobTaskSpec.VariableYaml = values + jobTaskSpec.UserSuppliedValue = values + jobTaskSpec.ValuesSyncedFromSource = true + } else { + values, synced, err := syncValuesFromTemplateService(revisionSvc, svc.VariableYaml) + if err != nil { + return nil, fmt.Errorf("failed to sync values for service: %s, error: %s", svc.ServiceName, err) + } + if synced { + jobTaskSpec.UpdateConfig = svc.UpdateConfig + jobTaskSpec.VariableYaml = values + jobTaskSpec.UserSuppliedValue = values + jobTaskSpec.ValuesSyncedFromSource = true + } } - pSvc.GetServiceRender().SetAutoSync(autoSyncFlag) - jobTaskSpec.UpdateConfig = svc.UpdateConfig - jobTaskSpec.VariableYaml = values - jobTaskSpec.UserSuppliedValue = values } jobTask := &commonmodels.JobTask{ @@ -738,6 +756,55 @@ func (j DeployJobController) SetRepoCommitInfo() error { return nil } +func syncValuesFromTemplateService(service *commonmodels.Service, currentValues string) (string, bool, error) { + if service == nil { + return "", false, nil + } + + // chart-template service with a values source: sync from the configured source. + createFrom, err := service.GetHelmCreateFrom() + if err == nil && createFrom.YamlData != nil { + yamlData := &templatemodels.CustomYaml{ + YamlContent: createFrom.YamlData.YamlContent, + RenderVariableKVs: createFrom.YamlData.RenderVariableKVs, + Source: createFrom.YamlData.Source, + AutoSync: true, + AutoSyncYaml: createFrom.YamlData.AutoSyncYaml, + SourceDetail: createFrom.YamlData.SourceDetail, + SourceID: createFrom.YamlData.SourceID, + } + _, values, err := commonservice.SyncYamlFromSource(yamlData, currentValues, yamlData.AutoSyncYaml) + if err != nil { + return "", false, err + } + if values == "" { + return "", false, nil + } + return values, true, nil + } + + if service.CodehostID == 0 || service.RepoName == "" || service.LoadPath == "" { + return "", false, nil + } + + valuesPath := fmt.Sprintf("%s/%s", service.LoadPath, setting.ValuesYaml) + valuesYAML, err := fs.DownloadFileFromSource(&fs.DownloadFromSourceArgs{ + CodehostID: service.CodehostID, + Owner: service.RepoOwner, + Namespace: service.RepoNamespace, + Repo: service.RepoName, + Path: valuesPath, + Branch: service.BranchName, + }) + if err != nil { + return "", false, err + } + if len(valuesYAML) == 0 { + return "", false, nil + } + return string(valuesYAML), true, nil +} + func (j DeployJobController) GetVariableList(jobName string, getAggregatedVariables, getRuntimeVariables, getPlaceHolderVariables, getServiceSpecificVariables, useUserInputValue bool) ([]*commonmodels.KeyVal, error) { resp := make([]*commonmodels.KeyVal, 0)