Files
1Panel/agent/utils/docker/network_cleanup.go
T
2026-10-08 17:44:29 +08:00

109 lines
3.1 KiB
Go

package docker
import (
"context"
"sort"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/errdefs"
)
type NetworkCleanupClient interface {
NetworkList(context.Context, network.ListOptions) ([]network.Inspect, error)
ContainerList(context.Context, container.ListOptions) ([]container.Summary, error)
NetworkInspect(context.Context, string, network.InspectOptions) (network.Inspect, error)
NetworkRemove(context.Context, string) error
}
func CleanUnusedNetworks(ctx context.Context, cli NetworkCleanupClient, onResult ...func(string, dto.NetworkCleanupItem)) (*dto.NetworkCleanupReport, error) {
networks, err := cli.NetworkList(ctx, network.ListOptions{})
if err != nil {
return nil, err
}
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
if err != nil {
return nil, err
}
used := make(map[string]bool)
for _, c := range containers {
if c.NetworkSettings == nil {
continue
}
for name, endpoint := range c.NetworkSettings.Networks {
used[name] = true
if endpoint != nil {
used[endpoint.NetworkID] = true
}
}
}
report := &dto.NetworkCleanupReport{Deleted: []dto.NetworkCleanupItem{}, Skipped: []dto.NetworkCleanupItem{}, Failed: []dto.NetworkCleanupItem{}}
record := func(status string, item dto.NetworkCleanupItem) {
switch status {
case "deleted":
report.Deleted = append(report.Deleted, item)
case "skipped":
report.Skipped = append(report.Skipped, item)
case "failed":
report.Failed = append(report.Failed, item)
}
for _, notify := range onResult {
if notify != nil {
notify(status, item)
}
}
}
sort.Slice(networks, func(i, j int) bool { return networks[i].Name < networks[j].Name })
for _, n := range networks {
if err := ctx.Err(); err != nil {
return report, err
}
item := dto.NetworkCleanupItem{ID: n.ID, Name: n.Name}
switch {
case n.Name == "none" || n.Name == "host" || n.Name == "bridge" || n.Name == "1panel-network":
item.Reason = "protected"
case n.Scope != "local" || n.Ingress || n.ConfigOnly:
item.Reason = "unsupported_network"
case used[n.Name] || used[n.ID]:
item.Reason = "container_connected"
}
if item.Reason != "" {
record("skipped", item)
continue
}
inspected, err := cli.NetworkInspect(ctx, n.ID, network.InspectOptions{})
if err != nil {
if errdefs.IsNotFound(err) {
item.Reason = "already_removed"
record("skipped", item)
} else {
item.Reason = "inspect_failed"
record("failed", item)
}
continue
}
if len(inspected.Containers) > 0 {
item.Reason = "container_connected"
record("skipped", item)
continue
}
if err := cli.NetworkRemove(ctx, n.ID); err != nil {
switch {
case errdefs.IsNotFound(err):
item.Reason = "already_removed"
record("skipped", item)
case errdefs.IsConflict(err):
item.Reason = "network_in_use"
record("skipped", item)
default:
item.Reason = "remove_failed"
record("failed", item)
}
continue
}
record("deleted", item)
}
return report, nil
}