fix(deploy): many docker compose bug

This commit is contained in:
Ran
2026-02-12 08:23:25 +08:00
parent 35ce7d169d
commit 01cb6c85db
16 changed files with 228 additions and 90 deletions
+23 -10
View File
@@ -9,7 +9,6 @@ import (
"io"
"log/slog"
"os"
"path/filepath"
"runtime"
"strings"
"syscall"
@@ -673,15 +672,8 @@ func (s *DefaultService) ExecTaskStreaming(ctx context.Context, containerID stri
if req.Terminal {
ioOpts = append(ioOpts, cio.WithTerminal)
}
fifoDir := strings.TrimSpace(req.FIFODir)
if fifoDir == "" {
if homeDir, err := os.UserHomeDir(); err == nil && homeDir != "" {
fifoDir = filepath.Join(homeDir, ".memoh", "containerd-fifo")
} else {
fifoDir = "/tmp/memoh-containerd-fifo"
}
}
if err := os.MkdirAll(fifoDir, 0o755); err != nil {
fifoDir, err := resolveExecFIFODir(req.FIFODir)
if err != nil {
_ = stdinR.Close()
_ = stdinW.Close()
_ = stdoutR.Close()
@@ -752,6 +744,27 @@ func (s *DefaultService) ExecTaskStreaming(ctx context.Context, containerID stri
}, nil
}
func resolveExecFIFODir(preferred string) (string, error) {
candidates := make([]string, 0, 3)
if p := strings.TrimSpace(preferred); p != "" {
candidates = append(candidates, p)
}
candidates = append(candidates, "/var/lib/containerd/memoh-fifo", "/tmp/memoh-containerd-fifo")
var lastErr error
for _, dir := range candidates {
if err := os.MkdirAll(dir, 0o755); err == nil {
return dir, nil
} else {
lastErr = err
}
}
if lastErr == nil {
lastErr = fmt.Errorf("no fifo directory candidate available")
}
return "", lastErr
}
func (s *DefaultService) ListContainersByLabel(ctx context.Context, key, value string) ([]containerd.Container, error) {
if key == "" {
return nil, ErrInvalidArgument
+37 -5
View File
@@ -4,11 +4,13 @@ import (
"bufio"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
@@ -188,7 +190,8 @@ func (h *ContainerdHandler) getMCPSession(ctx context.Context, containerID strin
func (h *ContainerdHandler) startContainerdMCPSession(ctx context.Context, containerID string) (*mcpSession, error) {
execSession, err := h.service.ExecTaskStreaming(ctx, containerID, ctr.ExecTaskRequest{
Args: []string{"/app/mcp"},
Args: []string{"/app/mcp"},
FIFODir: h.mcpFIFODir(),
})
if err != nil {
return nil, err
@@ -207,11 +210,15 @@ func (h *ContainerdHandler) startContainerdMCPSession(ctx context.Context, conta
go func() {
_, err := execSession.Wait()
if err != nil {
if isBenignMCPSessionExit(err) {
sess.closeWithError(io.EOF)
return
}
h.logger.Error("mcp session exited", slog.Any("error", err), slog.String("container_id", containerID))
sess.closeWithError(err)
} else {
sess.closeWithError(io.EOF)
return
}
sess.closeWithError(io.EOF)
}()
return sess, nil
@@ -273,11 +280,15 @@ func (h *ContainerdHandler) startLimaMCPSession(containerID string) (*mcpSession
go sess.readLoop()
go func() {
if err := cmd.Wait(); err != nil {
if isBenignMCPSessionExit(err) {
sess.closeWithError(io.EOF)
return
}
h.logger.Error("mcp session exited", slog.Any("error", err), slog.String("container_id", containerID))
sess.closeWithError(err)
} else {
sess.closeWithError(io.EOF)
return
}
sess.closeWithError(io.EOF)
}()
return sess, nil
@@ -320,11 +331,32 @@ func (h *ContainerdHandler) startMCPStderrLogger(stderr io.ReadCloser, container
h.logger.Warn("mcp stderr", slog.String("container_id", containerID), slog.String("message", line))
}
if err := scanner.Err(); err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || strings.Contains(err.Error(), "closed pipe") {
return
}
h.logger.Error("mcp stderr read failed", slog.Any("error", err), slog.String("container_id", containerID))
}
}()
}
func isBenignMCPSessionExit(err error) bool {
if err == nil {
return false
}
if errors.Is(err, context.Canceled) || errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) {
return true
}
msg := strings.ToLower(err.Error())
return strings.Contains(msg, "code = canceled") || strings.Contains(msg, "context canceled") || strings.Contains(msg, "closed pipe")
}
func (h *ContainerdHandler) mcpFIFODir() string {
if root := strings.TrimSpace(h.cfg.DataRoot); root != "" {
return filepath.Join(root, ".containerd-fifo")
}
return "/tmp/memoh-containerd-fifo"
}
func (s *mcpSession) readLoop() {
scanner := bufio.NewScanner(s.stdout)
scanner.Buffer(make([]byte, 0, 64*1024), 8*1024*1024)
+13 -4
View File
@@ -178,6 +178,7 @@ func (h *ContainerdHandler) startContainerdMCPCommandSession(ctx context.Context
Args: args,
Env: env,
WorkDir: strings.TrimSpace(req.Cwd),
FIFODir: h.mcpFIFODir(),
})
if err != nil {
return nil, err
@@ -195,11 +196,15 @@ func (h *ContainerdHandler) startContainerdMCPCommandSession(ctx context.Context
go func() {
_, err := execSession.Wait()
if err != nil {
if isBenignMCPSessionExit(err) {
sess.closeWithError(io.EOF)
return
}
h.logger.Error("mcp stdio session exited", slog.Any("error", err), slog.String("container_id", containerID))
sess.closeWithError(err)
} else {
sess.closeWithError(io.EOF)
return
}
sess.closeWithError(io.EOF)
}()
return sess, nil
}
@@ -342,11 +347,15 @@ func (h *ContainerdHandler) startLimaMCPCommandSession(containerID string, req M
go sess.readLoop()
go func() {
if err := cmd.Wait(); err != nil {
if isBenignMCPSessionExit(err) {
sess.closeWithError(io.EOF)
return
}
h.logger.Error("mcp stdio session exited", slog.Any("error", err), slog.String("container_id", containerID))
sess.closeWithError(err)
} else {
sess.closeWithError(io.EOF)
return
}
sess.closeWithError(io.EOF)
}()
return sess, nil
+5
View File
@@ -17,6 +17,7 @@ func NewPingHandler(log *slog.Logger) *PingHandler {
func (h *PingHandler) Register(e *echo.Echo) {
e.GET("/ping", h.Ping)
e.HEAD("/health", h.PingHead)
}
func (h *PingHandler) Ping(c echo.Context) error {
@@ -24,3 +25,7 @@ func (h *PingHandler) Ping(c echo.Context) error {
"status": "ok",
})
}
func (h *PingHandler) PingHead(c echo.Context) error {
return c.NoContent(http.StatusOK)
}
+1 -1
View File
@@ -42,7 +42,7 @@ func NewServer(log *slog.Logger, addr string, jwtSecret string, pingHandler *han
}))
e.Use(auth.JWTMiddleware(jwtSecret, func(c echo.Context) bool {
path := c.Request().URL.Path
if path == "/ping" || path == "/api/swagger.json" || path == "/auth/login" {
if path == "/ping" || path == "/health" || path == "/api/swagger.json" || path == "/auth/login" {
return true
}
if strings.HasPrefix(path, "/api/docs") {