From f51df1d574fe81103e8a95435d423e5f81dc041b Mon Sep 17 00:00:00 2001 From: Cynthia Date: Mon, 24 Aug 2026 13:45:08 +0800 Subject: [PATCH 1/6] fix: exclude deleted service modules from env imports Signed-off-by: Cynthia --- .../aslan/core/common/service/service.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pkg/microservice/aslan/core/common/service/service.go b/pkg/microservice/aslan/core/common/service/service.go index e69331a1f7..31b739618c 100644 --- a/pkg/microservice/aslan/core/common/service/service.go +++ b/pkg/microservice/aslan/core/common/service/service.go @@ -1143,13 +1143,20 @@ func BuildServiceInfoInEnv(productInfo *commonmodels.Product, templateSvcs []*co } for _, svc := range productInfo.GetServiceMap() { - // prodcutInfo override default - if _, ok := svcModulesMap[svc.ServiceName]; !ok { - svcModulesMap[svc.ServiceName] = make(map[string]*commonmodels.Container) + // productInfo overrides the default image only for modules that still + // exist in the latest service template. Services that only exist in the + // environment keep all of their modules. + modules, templateExists := svcModulesMap[svc.ServiceName] + if !templateExists { + modules = make(map[string]*commonmodels.Container) + svcModulesMap[svc.ServiceName] = modules } for _, container := range svc.Containers { - svcModulesMap[svc.ServiceName][container.Name] = container + if _, exists := modules[container.Name]; templateExists && !exists { + continue + } + modules[container.Name] = container } } From 8458e771f35b6d8e54403205b3417fc8f55485cc Mon Sep 17 00:00:00 2001 From: Cynthia Date: Mon, 24 Aug 2026 14:17:55 +0800 Subject: [PATCH 2/6] fix: exclude deleted service modules from environment details Signed-off-by: Cynthia --- .../aslan/core/common/service/service.go | 15 ++++-------- .../aslan/core/environment/service/product.go | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/pkg/microservice/aslan/core/common/service/service.go b/pkg/microservice/aslan/core/common/service/service.go index 31b739618c..e69331a1f7 100644 --- a/pkg/microservice/aslan/core/common/service/service.go +++ b/pkg/microservice/aslan/core/common/service/service.go @@ -1143,20 +1143,13 @@ func BuildServiceInfoInEnv(productInfo *commonmodels.Product, templateSvcs []*co } for _, svc := range productInfo.GetServiceMap() { - // productInfo overrides the default image only for modules that still - // exist in the latest service template. Services that only exist in the - // environment keep all of their modules. - modules, templateExists := svcModulesMap[svc.ServiceName] - if !templateExists { - modules = make(map[string]*commonmodels.Container) - svcModulesMap[svc.ServiceName] = modules + // prodcutInfo override default + if _, ok := svcModulesMap[svc.ServiceName]; !ok { + svcModulesMap[svc.ServiceName] = make(map[string]*commonmodels.Container) } for _, container := range svc.Containers { - if _, exists := modules[container.Name]; templateExists && !exists { - continue - } - modules[container.Name] = container + svcModulesMap[svc.ServiceName][container.Name] = container } } diff --git a/pkg/microservice/aslan/core/environment/service/product.go b/pkg/microservice/aslan/core/environment/service/product.go index 5e98cd9987..24fbd19497 100644 --- a/pkg/microservice/aslan/core/environment/service/product.go +++ b/pkg/microservice/aslan/core/environment/service/product.go @@ -270,6 +270,10 @@ func buildProductResp(envName string, prod *commonmodels.Product, log *zap.Sugar for _, svc := range templateServices { templSvcMap[svc.ServiceName] = svc } + latestTemplSvcMap, err := repository.GetMaxRevisionsServicesMap(prod.ProductName, prod.Production) + if err != nil { + return nil, fmt.Errorf("failed to find latest services for env %s/%s: %w", prod.ProductName, envName, err) + } for _, svcGroup := range prod.Services { for _, svc := range svcGroup { @@ -280,6 +284,26 @@ func buildProductResp(envName string, prod *commonmodels.Product, log *zap.Sugar } svc.DeployStrategy = commonutil.GetServiceDeployStrategy(svc.ServiceName, prod.ServiceDeployStrategy) + + latestTemplSvc, ok := latestTemplSvcMap[svc.ServiceName] + if !ok || (latestTemplSvc.Type != setting.K8SDeployType && latestTemplSvc.Type != setting.HelmDeployType) { + continue + } + modules, _, err := repository.ResolveServiceModules(context.Background(), prod.ProductName, svc.ServiceName, prod.Production, latestTemplSvc.Revision) + if err != nil { + return nil, fmt.Errorf("failed to resolve modules for %s/%s rev %d: %w", prod.ProductName, svc.ServiceName, latestTemplSvc.Revision, err) + } + moduleNames := sets.NewString() + for _, module := range modules { + moduleNames.Insert(module.Name) + } + containers := svc.Containers[:0] + for _, container := range svc.Containers { + if container != nil && moduleNames.Has(container.Name) { + containers = append(containers, container) + } + } + svc.Containers = containers } } From 71695d5eeba7344c230579f498467a65a488786d Mon Sep 17 00:00:00 2001 From: Cynthia Date: Mon, 24 Aug 2026 15:04:17 +0800 Subject: [PATCH 3/6] fix: scope deleted module filtering to environment detail Signed-off-by: Cynthia --- .../core/environment/handler/environment.go | 40 ++++++++++++++++++- .../aslan/core/environment/service/product.go | 24 ----------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/pkg/microservice/aslan/core/environment/handler/environment.go b/pkg/microservice/aslan/core/environment/handler/environment.go index d9365ecad4..f406d018cd 100644 --- a/pkg/microservice/aslan/core/environment/handler/environment.go +++ b/pkg/microservice/aslan/core/environment/handler/environment.go @@ -18,6 +18,7 @@ package handler import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -37,6 +38,7 @@ import ( "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models/template" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb" commonservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service" + "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/repository" commontypes "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/types" commonutil "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/util" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/environment/service" @@ -1516,7 +1518,43 @@ func GetEnvironment(c *gin.Context) { } } - ctx.Resp, ctx.RespErr = service.GetProduct(ctx.UserName, envName, projectKey, ctx.Logger) + productResp, err := service.GetProduct(ctx.UserName, envName, projectKey, ctx.Logger) + if err != nil { + ctx.RespErr = err + return + } + + latestTemplSvcMap, err := repository.GetMaxRevisionsServicesMap(projectKey, production) + if err != nil { + ctx.RespErr = fmt.Errorf("failed to find latest services for env %s/%s: %w", projectKey, envName, err) + return + } + for _, svcGroup := range productResp.Services { + for _, svc := range svcGroup { + latestTemplSvc, ok := latestTemplSvcMap[svc.ServiceName] + if !ok || (latestTemplSvc.Type != setting.K8SDeployType && latestTemplSvc.Type != setting.HelmDeployType) { + continue + } + modules, _, err := repository.ResolveServiceModules(context.Background(), projectKey, svc.ServiceName, production, latestTemplSvc.Revision) + if err != nil { + ctx.RespErr = fmt.Errorf("failed to resolve modules for %s/%s rev %d: %w", projectKey, svc.ServiceName, latestTemplSvc.Revision, err) + return + } + moduleNames := sets.NewString() + for _, module := range modules { + moduleNames.Insert(module.Name) + } + containers := svc.Containers[:0] + for _, container := range svc.Containers { + if container != nil && moduleNames.Has(container.Name) { + containers = append(containers, container) + } + } + svc.Containers = containers + } + } + + ctx.Resp = productResp } func GetEstimatedRenderCharts(c *gin.Context) { diff --git a/pkg/microservice/aslan/core/environment/service/product.go b/pkg/microservice/aslan/core/environment/service/product.go index 24fbd19497..5e98cd9987 100644 --- a/pkg/microservice/aslan/core/environment/service/product.go +++ b/pkg/microservice/aslan/core/environment/service/product.go @@ -270,10 +270,6 @@ func buildProductResp(envName string, prod *commonmodels.Product, log *zap.Sugar for _, svc := range templateServices { templSvcMap[svc.ServiceName] = svc } - latestTemplSvcMap, err := repository.GetMaxRevisionsServicesMap(prod.ProductName, prod.Production) - if err != nil { - return nil, fmt.Errorf("failed to find latest services for env %s/%s: %w", prod.ProductName, envName, err) - } for _, svcGroup := range prod.Services { for _, svc := range svcGroup { @@ -284,26 +280,6 @@ func buildProductResp(envName string, prod *commonmodels.Product, log *zap.Sugar } svc.DeployStrategy = commonutil.GetServiceDeployStrategy(svc.ServiceName, prod.ServiceDeployStrategy) - - latestTemplSvc, ok := latestTemplSvcMap[svc.ServiceName] - if !ok || (latestTemplSvc.Type != setting.K8SDeployType && latestTemplSvc.Type != setting.HelmDeployType) { - continue - } - modules, _, err := repository.ResolveServiceModules(context.Background(), prod.ProductName, svc.ServiceName, prod.Production, latestTemplSvc.Revision) - if err != nil { - return nil, fmt.Errorf("failed to resolve modules for %s/%s rev %d: %w", prod.ProductName, svc.ServiceName, latestTemplSvc.Revision, err) - } - moduleNames := sets.NewString() - for _, module := range modules { - moduleNames.Insert(module.Name) - } - containers := svc.Containers[:0] - for _, container := range svc.Containers { - if container != nil && moduleNames.Has(container.Name) { - containers = append(containers, container) - } - } - svc.Containers = containers } } From cd7cda1bf748f204bf5561762b07ee96d0007a98 Mon Sep 17 00:00:00 2001 From: Cynthia Date: Tue, 25 Aug 2026 14:13:32 +0800 Subject: [PATCH 4/6] fix: filter environment modules by deployed revision Signed-off-by: Cynthia --- .../aslan/core/environment/handler/environment.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/pkg/microservice/aslan/core/environment/handler/environment.go b/pkg/microservice/aslan/core/environment/handler/environment.go index f406d018cd..a4d7f85dc5 100644 --- a/pkg/microservice/aslan/core/environment/handler/environment.go +++ b/pkg/microservice/aslan/core/environment/handler/environment.go @@ -1524,20 +1524,14 @@ func GetEnvironment(c *gin.Context) { return } - latestTemplSvcMap, err := repository.GetMaxRevisionsServicesMap(projectKey, production) - if err != nil { - ctx.RespErr = fmt.Errorf("failed to find latest services for env %s/%s: %w", projectKey, envName, err) - return - } for _, svcGroup := range productResp.Services { for _, svc := range svcGroup { - latestTemplSvc, ok := latestTemplSvcMap[svc.ServiceName] - if !ok || (latestTemplSvc.Type != setting.K8SDeployType && latestTemplSvc.Type != setting.HelmDeployType) { + if svc.Type != setting.K8SDeployType && svc.Type != setting.HelmDeployType { continue } - modules, _, err := repository.ResolveServiceModules(context.Background(), projectKey, svc.ServiceName, production, latestTemplSvc.Revision) + modules, _, err := repository.ResolveServiceModules(context.Background(), projectKey, svc.ServiceName, production, svc.Revision) if err != nil { - ctx.RespErr = fmt.Errorf("failed to resolve modules for %s/%s rev %d: %w", projectKey, svc.ServiceName, latestTemplSvc.Revision, err) + ctx.RespErr = fmt.Errorf("failed to resolve modules for %s/%s rev %d: %w", projectKey, svc.ServiceName, svc.Revision, err) return } moduleNames := sets.NewString() From ad1652aebfa380152d8139b0b5dbbadc80d80cb8 Mon Sep 17 00:00:00 2001 From: Cynthia Date: Wed, 26 Aug 2026 14:44:01 +0800 Subject: [PATCH 5/6] refactor: use a new slice when filtering containers Signed-off-by: Cynthia --- pkg/microservice/aslan/core/environment/handler/environment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/microservice/aslan/core/environment/handler/environment.go b/pkg/microservice/aslan/core/environment/handler/environment.go index a4d7f85dc5..7259d6b3d8 100644 --- a/pkg/microservice/aslan/core/environment/handler/environment.go +++ b/pkg/microservice/aslan/core/environment/handler/environment.go @@ -1538,7 +1538,7 @@ func GetEnvironment(c *gin.Context) { for _, module := range modules { moduleNames.Insert(module.Name) } - containers := svc.Containers[:0] + containers := make([]*commonmodels.Container, 0, len(svc.Containers)) for _, container := range svc.Containers { if container != nil && moduleNames.Has(container.Name) { containers = append(containers, container) From cfb1299abc98872b3a5b3f6b677c035fc91d8496 Mon Sep 17 00:00:00 2001 From: Cynthia Date: Wed, 26 Aug 2026 17:30:30 +0800 Subject: [PATCH 6/6] fix: prevent deleted modules from being restored when adding services Signed-off-by: Cynthia --- .../aslan/core/common/util/service.go | 23 +++++++++++++ .../core/environment/handler/environment.go | 34 +------------------ .../core/environment/service/environment.go | 15 ++++---- 3 files changed, 30 insertions(+), 42 deletions(-) diff --git a/pkg/microservice/aslan/core/common/util/service.go b/pkg/microservice/aslan/core/common/util/service.go index 173b412dca..1a64822f09 100644 --- a/pkg/microservice/aslan/core/common/util/service.go +++ b/pkg/microservice/aslan/core/common/util/service.go @@ -111,6 +111,29 @@ func FoldManualModulesInto(autos []*commonmodels.Container, manuals []*commonmod return autos } +// OverrideContainerImagesByName updates image fields for containers already +// present in the target slice. Overrides with unknown names or unresolved +// images are ignored. +func OverrideContainerImagesByName(containers, overrides []*commonmodels.Container) { + containerByName := make(map[string]*commonmodels.Container, len(containers)) + for _, container := range containers { + if container == nil || container.Name == "" { + continue + } + containerByName[container.Name] = container + } + + for _, override := range overrides { + if override == nil || override.Image == "" || IsModuleImagePlaceholder(override.Image) { + continue + } + if container, ok := containerByName[override.Name]; ok { + container.Image = override.Image + container.ImageName = override.ImageName + } + } +} + func GetServiceDeployStrategy(serviceName string, strategyMap map[string]setting.ServiceDeployStrategy) setting.ServiceDeployStrategy { if strategyMap == nil { return setting.ServiceDeployStrategyDeploy diff --git a/pkg/microservice/aslan/core/environment/handler/environment.go b/pkg/microservice/aslan/core/environment/handler/environment.go index 7259d6b3d8..d9365ecad4 100644 --- a/pkg/microservice/aslan/core/environment/handler/environment.go +++ b/pkg/microservice/aslan/core/environment/handler/environment.go @@ -18,7 +18,6 @@ package handler import ( "bytes" - "context" "encoding/json" "errors" "fmt" @@ -38,7 +37,6 @@ import ( "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models/template" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb" commonservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service" - "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/repository" commontypes "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/types" commonutil "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/util" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/environment/service" @@ -1518,37 +1516,7 @@ func GetEnvironment(c *gin.Context) { } } - productResp, err := service.GetProduct(ctx.UserName, envName, projectKey, ctx.Logger) - if err != nil { - ctx.RespErr = err - return - } - - for _, svcGroup := range productResp.Services { - for _, svc := range svcGroup { - if svc.Type != setting.K8SDeployType && svc.Type != setting.HelmDeployType { - continue - } - modules, _, err := repository.ResolveServiceModules(context.Background(), projectKey, svc.ServiceName, production, svc.Revision) - if err != nil { - ctx.RespErr = fmt.Errorf("failed to resolve modules for %s/%s rev %d: %w", projectKey, svc.ServiceName, svc.Revision, err) - return - } - moduleNames := sets.NewString() - for _, module := range modules { - moduleNames.Insert(module.Name) - } - containers := make([]*commonmodels.Container, 0, len(svc.Containers)) - for _, container := range svc.Containers { - if container != nil && moduleNames.Has(container.Name) { - containers = append(containers, container) - } - } - svc.Containers = containers - } - } - - ctx.Resp = productResp + ctx.Resp, ctx.RespErr = service.GetProduct(ctx.UserName, envName, projectKey, ctx.Logger) } func GetEstimatedRenderCharts(c *gin.Context) { diff --git a/pkg/microservice/aslan/core/environment/service/environment.go b/pkg/microservice/aslan/core/environment/service/environment.go index fe1edb5c0e..bf9f62a1f9 100644 --- a/pkg/microservice/aslan/core/environment/service/environment.go +++ b/pkg/microservice/aslan/core/environment/service/environment.go @@ -3076,15 +3076,12 @@ func upsertService(env *commonmodels.Product, newService *commonmodels.ProductSe if prevSvc == nil { fakeTemplateSvc := &commonmodels.Service{ServiceName: newService.ServiceName, ProductName: newService.ServiceName, KubeYamls: util.SplitYaml(parsedYaml)} commonutil.SetCurrentContainerImages(fakeTemplateSvc) - // Merge instead of overwrite. fakeTemplateSvc.Containers is the - // auto set re-parsed from the just-rendered YAML — authoritative - // for env-resolved image values on Deployment/StatefulSet/etc. - // newService.Containers came in from the caller already carrying - // manual modules (CRD/DaemonSet declarations), so blindly clobbering - // it would drop those manuals — and for ConfigMap/CRD-only services - // fakeTemplateSvc.Containers is empty, which would persist an empty - // Containers slice into ProductService and break later renders. - newService.Containers = commonutil.FoldManualModulesInto(fakeTemplateSvc.Containers, newService.Containers) + // Keep the revision's module list (service_module) authoritative: a + // container that only appears in the rendered YAML — e.g. a deleted + // component still present in the template — is intentionally not added + // back. The YAML is used only to refresh images for containers that + // already exist in the module list. + commonutil.OverrideContainerImagesByName(newService.Containers, fakeTemplateSvc.Containers) } preResourceYaml := ""