feat: extend container executor for Node SSR

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