feat: extend container executor for Node SSR

This commit is contained in:
2026-08-22 15:48:57 +08:00
parent 13da865566
commit 40e314744a
+41 -27
View File
@@ -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 构造拉取并校验后端镜像步骤的持久化意图。