Files
Memoh/internal/containerd/network.go
T

142 lines
3.6 KiB
Go

package containerd
import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/containerd/containerd/v2/client"
gocni "github.com/containerd/go-cni"
)
func setupCNINetwork(ctx context.Context, task client.Task, containerID string, cniBinDir string, cniConfDir string) (string, error) {
if task == nil {
return "", ErrInvalidArgument
}
if containerID == "" {
containerID = task.ID()
}
if containerID == "" {
return "", ErrInvalidArgument
}
pid := task.Pid()
if pid == 0 {
return "", fmt.Errorf("task pid not available for %s", containerID)
}
if _, err := os.Stat(cniConfDir); err != nil {
return "", fmt.Errorf("cni config dir missing: %s: %w", cniConfDir, err)
}
if _, err := os.Stat(cniBinDir); err != nil {
return "", fmt.Errorf("cni bin dir missing: %s: %w", cniBinDir, err)
}
netnsPath := filepath.Join("/proc", strconv.FormatUint(uint64(pid), 10), "ns", "net")
if _, err := os.Stat(netnsPath); err != nil {
return "", fmt.Errorf("netns not found: %s: %w", netnsPath, err)
}
cni, err := gocni.New(
gocni.WithPluginDir([]string{cniBinDir}),
gocni.WithPluginConfDir(cniConfDir),
)
if err != nil {
return "", err
}
if err := cni.Load(gocni.WithLoNetwork, gocni.WithDefaultConf); err != nil {
return "", err
}
result, err := cni.Setup(ctx, containerID, netnsPath)
if err != nil {
if !isDuplicateAllocationError(err) && !isVethExistsError(err) {
return "", err
}
// Stale IPAM allocation or veth exists (e.g. after container restart with persisted
// /var/lib/cni). Remove may fail if the previous iptables/veth state
// is already gone; ignore the error so the retry Setup still runs.
_ = cni.Remove(ctx, containerID, netnsPath)
result, err = cni.Setup(ctx, containerID, netnsPath)
if err != nil {
return "", err
}
}
return extractIP(result), nil
}
func extractIP(result *gocni.Result) string {
if result == nil {
return ""
}
for _, cfg := range result.Interfaces {
for _, ipCfg := range cfg.IPConfigs {
if ipCfg.IP != nil {
ip := ipCfg.IP.String()
if ip != "" && ip != "127.0.0.1" && ip != "::1" {
return ip
}
}
}
}
return ""
}
func removeCNINetwork(ctx context.Context, task client.Task, containerID string, cniBinDir string, cniConfDir string) error {
if task == nil {
return ErrInvalidArgument
}
if containerID == "" {
containerID = task.ID()
}
if containerID == "" {
return ErrInvalidArgument
}
pid := task.Pid()
if pid == 0 {
return fmt.Errorf("task pid not available for %s", containerID)
}
if _, err := os.Stat(cniConfDir); err != nil {
return fmt.Errorf("cni config dir missing: %s: %w", cniConfDir, err)
}
if _, err := os.Stat(cniBinDir); err != nil {
return fmt.Errorf("cni bin dir missing: %s: %w", cniBinDir, err)
}
netnsPath := filepath.Join("/proc", strconv.FormatUint(uint64(pid), 10), "ns", "net")
if _, err := os.Stat(netnsPath); err != nil {
return fmt.Errorf("netns not found: %s: %w", netnsPath, err)
}
cni, err := gocni.New(
gocni.WithPluginDir([]string{cniBinDir}),
gocni.WithPluginConfDir(cniConfDir),
)
if err != nil {
return err
}
if err := cni.Load(gocni.WithLoNetwork, gocni.WithDefaultConf); err != nil {
return err
}
return cni.Remove(ctx, containerID, netnsPath)
}
func isDuplicateAllocationError(err error) bool {
if err == nil {
return false
}
return strings.Contains(err.Error(), "duplicate allocation")
}
// isVethExistsError returns true if the CNI setup failed because veth devices
// already exist (e.g. after container restart with stale network state).
func isVethExistsError(err error) bool {
if err == nil {
return false
}
return strings.Contains(err.Error(), "already exists")
}