Files
1Panel/core/utils/mcpruntime/finalize.go
T
蘭 e29679768b feat: add MCP error metadata handling and improve source policy resolution (#14270)
* feat: add MCP error metadata handling and improve source policy resolution

* fix: adjust height-diff for improved table layout
2026-11-02 22:17:12 +08:00

87 lines
2.8 KiB
Go

package mcpruntime
import (
"context"
"errors"
"regexp"
)
var dockerContainerIDPattern = regexp.MustCompile(`^[a-f0-9]{64}$`)
func validCleanupReference(reference *cleanupReference) bool {
return reference != nil && dockerContainerIDPattern.MatchString(reference.ContainerID) &&
dockerContainerIDPattern.MatchString(reference.PreviousContainerID) &&
reference.ContainerID != reference.PreviousContainerID && ValidDockerImageID(reference.PreviousImageID)
}
// Finalize is called only after the new runtime settings have been committed.
// It uses the exact containers returned by this installation, never a tag or a scan.
func (m *DockerManager) Finalize(ctx context.Context, deleteOldImage bool) error {
m.installMu.Lock()
if m.installCleanup == nil {
m.installMu.Unlock()
return nil
}
reference, imageID, enabled := *m.installCleanup, m.installImageID, m.installEnabled
m.installMu.Unlock()
if !validCleanupReference(&reference) || !ValidDockerImageID(imageID) {
return errors.New("MCP installation cleanup reference is unavailable")
}
ReportProgress(ctx, Progress{Status: "cleanup"})
response, err := m.send(ctx, dockerRequest{
Operation: "finalize", Image: m.image, Port: m.port, Enabled: enabled, ImageID: imageID,
Cleanup: &reference, DeleteOldImage: deleteOldImage,
})
if err != nil {
return err
}
if !validCleanupResult(response.CleanupResult, deleteOldImage, reference.PreviousImageID == imageID) {
return errors.New("MCP Docker agent returned an invalid cleanup result")
}
result := response.CleanupResult
if result.ContainerRemoved {
ReportProgress(ctx, Progress{Status: "cleanup_complete"})
}
switch {
case result.ImageRemoved:
ReportProgress(ctx, Progress{Status: "old_image_removed"})
case result.ImageRetainedReason == "not_requested":
ReportProgress(ctx, Progress{Status: "old_image_kept"})
case result.ImageRetainedReason == "previous_missing":
ReportProgress(ctx, Progress{Status: "cleanup_skipped"})
default:
ReportProgress(ctx, Progress{Status: "old_image_retained"})
}
m.installMu.Lock()
if m.installCleanup != nil && *m.installCleanup == reference {
m.installCleanup = nil
}
m.installMu.Unlock()
return nil
}
func validCleanupResult(result *cleanupResult, deleteOldImage, sameImage bool) bool {
if result == nil {
return false
}
if result.ImageRetainedReason == "previous_missing" {
return !result.ContainerRemoved && !result.ImageRemoved
}
if !result.ContainerRemoved {
return false
}
if result.ImageRemoved {
return deleteOldImage && !sameImage && result.ImageRetainedReason == ""
}
switch result.ImageRetainedReason {
case "not_requested":
return !deleteOldImage
case "same_image":
return deleteOldImage && sameImage
case "in_use", "multiple_tags":
return deleteOldImage && !sameImage
default:
return false
}
}