diff --git a/internal/backendexecutor/executor.go b/internal/backendexecutor/executor.go index fae3459..8e98e76 100644 --- a/internal/backendexecutor/executor.go +++ b/internal/backendexecutor/executor.go @@ -98,6 +98,8 @@ type Request struct { RestartPolicy containerengine.RestartPolicy // HealthEndpoint 后端 Actuator 健康检查的 HTTP 端点。 HealthEndpoint string + // HealthPath 健康检查路径;为空时使用 backend 默认路径。 + HealthPath string // StartLog 表示是否读取并回传容器启动日志。 StartLog bool // LogReporter 用于回传容器启动日志的每一行。 @@ -347,30 +349,40 @@ func validateRequest(request Request) error { if request.ContainerName == "" || strings.TrimSpace(request.ContainerName) != request.ContainerName { return errors.New("exact container name is required") } - if request.Port != 8080 && request.Port != 8081 { - return fmt.Errorf("backend container port must be 8080 or 8081: %d", request.Port) + if request.Port != 8080 && request.Port != 8081 && request.Port != 18910 && request.Port != 28910 { + return fmt.Errorf("container port must be 8080, 8081, 18910, or 28910: %d", request.Port) } if request.PortEnvironmentKey == "" || strings.Contains(request.PortEnvironmentKey, "=") || strings.TrimSpace(request.PortEnvironmentKey) != request.PortEnvironmentKey { return errors.New("exact port environment key is required") } - if !filepath.IsAbs(request.ConfigSource) || !filepath.IsAbs(request.ConfigTarget) { - return errors.New("backend configuration source and target must be absolute paths") + if request.ConfigSource != "" || request.ConfigTarget != "" { + if !filepath.IsAbs(request.ConfigSource) || !filepath.IsAbs(request.ConfigTarget) { + return errors.New("backend configuration source and target must be absolute paths") + } } - if !filepath.IsAbs(request.TmpSource) || !filepath.IsAbs(request.TmpTarget) { - return errors.New("backend temporary source and target must be absolute paths") + if request.TmpSource != "" || request.TmpTarget != "" { + if !filepath.IsAbs(request.TmpSource) || !filepath.IsAbs(request.TmpTarget) { + return errors.New("backend temporary source and target must be absolute paths") + } } - if request.ConfigEnvironmentKey == "" || strings.Contains(request.ConfigEnvironmentKey, "=") || strings.TrimSpace(request.ConfigEnvironmentKey) != request.ConfigEnvironmentKey { - return errors.New("exact backend configuration environment key is required") - } - if request.ConfigLocation == "" || strings.TrimSpace(request.ConfigLocation) != request.ConfigLocation { - return errors.New("exact backend configuration location is required") + if request.ConfigSource != "" { + if request.ConfigEnvironmentKey == "" || strings.Contains(request.ConfigEnvironmentKey, "=") || strings.TrimSpace(request.ConfigEnvironmentKey) != request.ConfigEnvironmentKey { + return errors.New("exact backend configuration environment key is required") + } + if request.ConfigLocation == "" || strings.TrimSpace(request.ConfigLocation) != request.ConfigLocation { + return errors.New("exact backend configuration location is required") + } } if err := validateRestartPolicy(request.RestartPolicy); err != nil { return err } + path := request.HealthPath + if path == "" { + path = healthPath + } parsed, err := url.ParseRequestURI(request.HealthEndpoint) - if err != nil || parsed.Scheme != "http" || parsed.Host == "" || parsed.Path != healthPath { - return fmt.Errorf("health endpoint must be an HTTP URL with exact path %s", healthPath) + if err != nil || parsed.Scheme != "http" || parsed.Host == "" || parsed.Path != path { + return fmt.Errorf("health endpoint must be an HTTP URL with exact path %s", path) } if parsed.Port() != strconv.Itoa(request.Port) { return fmt.Errorf("health endpoint port must equal backend container port %d", request.Port) @@ -427,23 +439,25 @@ func containerSpec(request Request) containerengine.ContainerSpec { if request.DisplayImageReference != "" { imageReference = request.DisplayImageReference } - return containerengine.ContainerSpec{ - Name: request.ContainerName, - ImageReference: imageReference, - Platform: request.Platform, - Environment: []string{ - request.PortEnvironmentKey + "=" + strconv.Itoa(request.Port), - request.ConfigEnvironmentKey + "=" + request.ConfigLocation, - }, - NetworkMode: hostNetworkMode, - RestartPolicy: request.RestartPolicy, - Mounts: []containerengine.Mount{ - {Type: bindMountType, Source: request.ConfigSource, Target: request.ConfigTarget, ReadOnly: true}, - {Type: bindMountType, Source: request.TmpSource, Target: request.TmpTarget}, - }, + spec := containerengine.ContainerSpec{ + Name: request.ContainerName, + ImageReference: imageReference, + Platform: request.Platform, + Environment: []string{request.PortEnvironmentKey + "=" + strconv.Itoa(request.Port)}, + NetworkMode: hostNetworkMode, + RestartPolicy: request.RestartPolicy, + Mounts: nil, User: "0:0", StopTimeoutSeconds: containerStopTimeoutSeconds, } + if request.ConfigSource != "" { + spec.Environment = append(spec.Environment, request.ConfigEnvironmentKey+"="+request.ConfigLocation) + spec.Mounts = append(spec.Mounts, containerengine.Mount{Type: bindMountType, Source: request.ConfigSource, Target: request.ConfigTarget, ReadOnly: true}) + } + if request.TmpSource != "" { + spec.Mounts = append(spec.Mounts, containerengine.Mount{Type: bindMountType, Source: request.TmpSource, Target: request.TmpTarget}) + } + return spec } // pullIntent 构造拉取并校验后端镜像步骤的持久化意图。