diff --git a/AGENTS_CHANGELOG.md b/AGENTS_CHANGELOG.md index 2a84297..9e0bcbb 100644 --- a/AGENTS_CHANGELOG.md +++ b/AGENTS_CHANGELOG.md @@ -175,3 +175,14 @@ - native 自动修复未实现(JAR 链接漂移修复需 filestore identity + release 校验)。 - `coverage.out` 未重新生成(不含新包)。 + +--- + +## 2026-08-22 Sonar 复杂度继续收敛 + +- 将部署配置的精确键校验拆为 root、daemon、backend、Node SSR 及槽位校验函数,保留原有字段与错误语义。 +- 将 backend 校验按 native/container 分支拆分,降低单方法认知复杂度。 +- 将 Node SSR Nginx marker 定位与 server 收集拆分,保留重复 marker、重复端口和槽位数量校验。 +- 将容器镜像解析的平台校验、tag 校验、仓库 digest 收集拆分,保留摘要唯一性要求。 + +验证:`GOCACHE=/tmp/yms-go-cache go test ./...` 通过。 diff --git a/internal/deploymentconfig/config.go b/internal/deploymentconfig/config.go index 88b174b..c0bbfef 100644 --- a/internal/deploymentconfig/config.go +++ b/internal/deploymentconfig/config.go @@ -212,16 +212,31 @@ func validateExactDocumentKeys(document []byte) error { if err := toml.Unmarshal(document, &root); err != nil { return err } - if err := rejectUnknownKeys(root, "", "daemon", "backend", "nodeSsr"); err != nil { + if err := validateRootKeys(root); err != nil { return err } + if err := validateDaemonKeys(root); err != nil { + return err + } + if err := validateBackendKeys(root); err != nil { + return err + } + return validateOptionalNodeSsrKeys(root) +} + +func validateRootKeys(root map[string]any) error { + return rejectUnknownKeys(root, "", "daemon", "backend", "nodeSsr") +} + +func validateDaemonKeys(root map[string]any) error { daemon, err := exactTable(root, "", "daemon") if err != nil { return err } - if err := rejectUnknownKeys(daemon, "daemon", "environment"); err != nil { - return err - } + return rejectUnknownKeys(daemon, "daemon", "environment") +} + +func validateBackendKeys(root map[string]any) error { backend, err := exactTable(root, "", "backend") if err != nil { return err @@ -233,18 +248,10 @@ func validateExactDocumentKeys(document []byte) error { if err != nil { return err } - if err := rejectUnknownKeys(slots, "backend.slot", "8080", "8081"); err != nil { - return err - } - for _, port := range []string{"8080", "8081"} { - slot, err := exactTable(slots, "backend.slot", port) - if err != nil { - return err - } - if err := rejectUnknownKeys(slot, "backend.slot."+port, "unit", "jar", "container_name", "health_endpoint"); err != nil { - return err - } - } + return validateSlotKeys(slots, "backend.slot", []string{"8080", "8081"}, []string{"unit", "jar", "container_name", "health_endpoint"}) +} + +func validateOptionalNodeSsrKeys(root map[string]any) error { nodeSsrValue, found := root["nodeSsr"] if !found { return nil @@ -263,15 +270,19 @@ func validateExactDocumentKeys(document []byte) error { if err != nil { return err } - if err := rejectUnknownKeys(nodeSlots, "nodeSsr.slot", "18910", "28910"); err != nil { + return validateSlotKeys(nodeSlots, "nodeSsr.slot", []string{"18910", "28910"}, []string{"container_name", "health_endpoint"}) +} + +func validateSlotKeys(slots map[string]any, parent string, ports []string, allowed []string) error { + if err := rejectUnknownKeys(slots, parent, ports...); err != nil { return err } - for _, port := range []string{"18910", "28910"} { - slot, err := exactTable(nodeSlots, "nodeSsr.slot", port) + for _, port := range ports { + slot, err := exactTable(slots, parent, port) if err != nil { return err } - if err := rejectUnknownKeys(slot, "nodeSsr.slot."+port, "container_name", "health_endpoint"); err != nil { + if err := rejectUnknownKeys(slot, parent+"."+port, allowed...); err != nil { return err } } @@ -343,33 +354,41 @@ func validateBackend(backend Backend) error { } switch backend.Type { case BackendTypeNative: - if backend.Slot.Port8080.ContainerName != "" || backend.Slot.Port8081.ContainerName != "" { - return errors.New("native backend slots do not accept container_name") - } - if backend.ReleaseDir != nativeReleaseDir { - return fmt.Errorf("backend.release_dir must be %q", nativeReleaseDir) - } - if backend.ActiveJAR != nativeActiveJAR { - return fmt.Errorf("backend.active_jar must be %q", nativeActiveJAR) - } - return validateNativeBackendSlots(backend.Slot) + return validateNativeBackend(backend) case BackendTypeContainer: - if backend.ReleaseDir != "" || backend.ActiveJAR != "" { - return errors.New("container backend does not accept release_dir or active_jar") - } - if backend.Slot.Port8080.Unit != "" || backend.Slot.Port8080.JAR != "" || backend.Slot.Port8081.Unit != "" || backend.Slot.Port8081.JAR != "" { - return errors.New("container backend slots do not accept unit or jar") - } - if err := validateContainerSlot("backend.slot.8080", backend.Slot.Port8080, containerName8080, nativeHealthURL8080); err != nil { - return err - } - return validateContainerSlot("backend.slot.8081", backend.Slot.Port8081, containerName8081, nativeHealthURL8081) + return validateContainerBackend(backend) default: return fmt.Errorf("backend.type must be %q or %q", BackendTypeNative, BackendTypeContainer) } } +func validateNativeBackend(backend Backend) error { + if backend.Slot.Port8080.ContainerName != "" || backend.Slot.Port8081.ContainerName != "" { + return errors.New("native backend slots do not accept container_name") + } + if backend.ReleaseDir != nativeReleaseDir { + return fmt.Errorf("backend.release_dir must be %q", nativeReleaseDir) + } + if backend.ActiveJAR != nativeActiveJAR { + return fmt.Errorf("backend.active_jar must be %q", nativeActiveJAR) + } + return validateNativeBackendSlots(backend.Slot) +} + +func validateContainerBackend(backend Backend) error { + if backend.ReleaseDir != "" || backend.ActiveJAR != "" { + return errors.New("container backend does not accept release_dir or active_jar") + } + if backend.Slot.Port8080.Unit != "" || backend.Slot.Port8080.JAR != "" || backend.Slot.Port8081.Unit != "" || backend.Slot.Port8081.JAR != "" { + return errors.New("container backend slots do not accept unit or jar") + } + if err := validateContainerSlot("backend.slot.8080", backend.Slot.Port8080, containerName8080, nativeHealthURL8080); err != nil { + return err + } + return validateContainerSlot("backend.slot.8081", backend.Slot.Port8081, containerName8081, nativeHealthURL8081) +} + func validateNativeBackendSlots(slots BackendSlots) error { if err := validateNativeSlot("backend.slot.8080", slots.Port8080, nativeUnit8080, nativeSlotJAR8080, nativeHealthURL8080); err != nil { return err diff --git a/internal/hostnginx/nodessr.go b/internal/hostnginx/nodessr.go index 884a455..a85404a 100644 --- a/internal/hostnginx/nodessr.go +++ b/internal/hostnginx/nodessr.go @@ -78,44 +78,60 @@ func parseNodeSsrBlock(content []byte) (nodeSsrBlock, error) { return nodeSsrBlock{}, errors.New("host Nginx configuration is empty") } lines := strings.Split(string(content), "\n") + begin, end, err := findNodeSsrMarkers(lines) + if err != nil { + return nodeSsrBlock{}, err + } + servers, err := collectNodeSsrServers(lines, begin, end) + if err != nil { + return nodeSsrBlock{}, err + } + return nodeSsrBlock{lines: lines, servers: servers}, nil +} + +func findNodeSsrMarkers(lines []string) (int, int, error) { begin, end := -1, -1 for index, line := range lines { switch strings.TrimSpace(line) { case nodeSsrManagedBegin: if begin != -1 { - return nodeSsrBlock{}, errors.New("host Nginx configuration contains duplicate Node SSR upstream begin markers") + return 0, 0, errors.New("host Nginx configuration contains duplicate Node SSR upstream begin markers") } begin = index case nodeSsrManagedEnd: if end != -1 { - return nodeSsrBlock{}, errors.New("host Nginx configuration contains duplicate Node SSR upstream end markers") + return 0, 0, errors.New("host Nginx configuration contains duplicate Node SSR upstream end markers") } end = index } } if begin == -1 || end <= begin { - return nodeSsrBlock{}, errors.New("host Nginx configuration requires one ordered Node SSR upstream marker pair") + return 0, 0, errors.New("host Nginx configuration requires one ordered Node SSR upstream marker pair") } + return begin, end, nil +} + +func collectNodeSsrServers(lines []string, begin, end int) ([]nodeSsrServer, error) { servers := make([]nodeSsrServer, 0, 2) seen := make(map[int]struct{}, 2) for index := begin + 1; index < end; index++ { server, found, err := parseNodeSsrServer(lines[index], index) if err != nil { - return nodeSsrBlock{}, err + return nil, err } if !found { continue } if _, exists := seen[server.port]; exists { - return nodeSsrBlock{}, fmt.Errorf("managed Node SSR upstream contains duplicate port %d", server.port) + return nil, fmt.Errorf("managed Node SSR upstream contains duplicate port %d", server.port) } seen[server.port] = struct{}{} servers = append(servers, server) } if len(servers) != 2 { - return nodeSsrBlock{}, fmt.Errorf("managed Node SSR upstream must contain exactly two servers, got %d", len(servers)) + return nil, fmt.Errorf("managed Node SSR upstream must contain exactly two servers, got %d", len(servers)) } - return nodeSsrBlock{lines: lines, servers: servers}, nil + return servers, nil } // parseNodeSsrServer parses one server line inside the managed Node SSR block.