//go:build !remote package abi import ( "context" "errors" "fmt" "slices" "strconv" "github.com/containers/common/libnetwork/pasta" "github.com/containers/common/libnetwork/slirp4netns" "github.com/containers/common/libnetwork/types" netutil "github.com/containers/common/libnetwork/util" "github.com/containers/podman/v5/libpod/define" "github.com/containers/podman/v5/libpod/events" "github.com/containers/podman/v5/pkg/domain/entities" ) func (ic *ContainerEngine) NetworkUpdate(ctx context.Context, netName string, options entities.NetworkUpdateOptions) error { var networkUpdateOptions types.NetworkUpdateOptions networkUpdateOptions.AddDNSServers = options.AddDNSServers networkUpdateOptions.RemoveDNSServers = options.RemoveDNSServers err := ic.Libpod.Network().NetworkUpdate(netName, networkUpdateOptions) if err != nil { return err } return nil } func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]types.Network, error) { // dangling filter is not provided by netutil var wantDangling bool val, filterDangling := options.Filters["dangling"] if filterDangling { switch len(val) { case 0: return nil, fmt.Errorf("got no values for filter key \"dangling\"") case 1: var err error wantDangling, err = strconv.ParseBool(val[0]) if err != nil { return nil, fmt.Errorf("invalid dangling filter value \"%v\"", val[0]) } delete(options.Filters, "dangling") default: return nil, fmt.Errorf("got more than one value for filter key \"dangling\"") } } filters, err := netutil.GenerateNetworkFilters(options.Filters) if err != nil { return nil, err } if filterDangling { danglingFilterFunc, err := ic.createDanglingFilterFunc(wantDangling) if err != nil { return nil, err } filters = append(filters, danglingFilterFunc) } nets, err := ic.Libpod.Network().NetworkList(filters...) return nets, err } func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { var errs []error statuses, err := ic.GetContainerNetStatuses() if err != nil { return nil, nil, fmt.Errorf("failed to get network status for containers: %w", err) } networks := make([]entities.NetworkInspectReport, 0, len(namesOrIds)) for _, name := range namesOrIds { net, err := ic.Libpod.Network().NetworkInspect(name) if err != nil { if errors.Is(err, define.ErrNoSuchNetwork) { errs = append(errs, fmt.Errorf("network %s: %w", name, err)) continue } else { return nil, nil, fmt.Errorf("inspecting network %s: %w", name, err) } } containerMap := make(map[string]entities.NetworkContainerInfo) for _, st := range statuses { // Make sure to only show the info for the correct network if sb, ok := st.Status[net.Name]; ok { containerMap[st.ID] = entities.NetworkContainerInfo{ Name: st.Name, Interfaces: sb.Interfaces, } } } netReport := entities.NetworkInspectReport{ Network: net, Containers: containerMap, } networks = append(networks, netReport) } return networks, errs, nil } func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, options entities.NetworkReloadOptions) ([]*entities.NetworkReloadReport, error) { containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, names: names}) if err != nil { return nil, err } reports := make([]*entities.NetworkReloadReport, 0, len(containers)) for _, ctr := range containers { report := new(entities.NetworkReloadReport) report.Id = ctr.ID() report.Err = ctr.ReloadNetwork() // ignore errors for invalid ctr state and network mode when --all is used if options.All && (errors.Is(report.Err, define.ErrCtrStateInvalid) || errors.Is(report.Err, define.ErrNetworkModeInvalid)) { continue } reports = append(reports, report) } return reports, nil } func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { reports := make([]*entities.NetworkRmReport, 0, len(namesOrIds)) for _, name := range namesOrIds { report := entities.NetworkRmReport{Name: name} containers, err := ic.Libpod.GetAllContainers() if err != nil { return reports, err } // We need to iterate containers looking to see if they belong to the given network for _, c := range containers { networks, err := c.Networks() // if container vanished or network does not exist, go to next container if errors.Is(err, define.ErrNoSuchNetwork) || errors.Is(err, define.ErrNoSuchCtr) { continue } if err != nil { return reports, err } if slices.Contains(networks, name) { // if user passes force, we nuke containers and pods if !options.Force { // Without the force option, we return an error return reports, fmt.Errorf("%q has associated containers with it. Use -f to forcibly delete containers and pods: %w", name, define.ErrNetworkInUse) } if c.IsInfra() { // if we have an infra container we need to remove the pod pod, err := ic.Libpod.GetPod(c.PodID()) if err != nil { return reports, err } if _, err := ic.Libpod.RemovePod(ctx, pod, true, true, options.Timeout); err != nil { return reports, err } } else if err := ic.Libpod.RemoveContainer(ctx, c, true, true, options.Timeout); err != nil && !errors.Is(err, define.ErrNoSuchCtr) { return reports, err } } } net, err := ic.Libpod.Network().NetworkInspect(name) if err != nil && !errors.Is(err, define.ErrNoSuchNetwork) { return reports, err } if err := ic.Libpod.Network().NetworkRemove(name); err != nil { report.Err = err } if len(net.Name) != 0 { ic.Libpod.NewNetworkEvent(events.Remove, net.Name, net.ID, net.Driver) } reports = append(reports, &report) } return reports, nil } func (ic *ContainerEngine) NetworkCreate(ctx context.Context, network types.Network, createOptions *types.NetworkCreateOptions) (*types.Network, error) { if slices.Contains([]string{"none", "host", "bridge", "private", slirp4netns.BinaryName, pasta.BinaryName, "container", "ns", "default"}, network.Name) { return nil, fmt.Errorf("cannot create network with name %q because it conflicts with a valid network mode", network.Name) } network, err := ic.Libpod.Network().NetworkCreate(network, createOptions) if err != nil { return nil, err } ic.Libpod.NewNetworkEvent(events.Create, network.Name, network.ID, network.Driver) return &network, nil } // NetworkDisconnect removes a container from a given network func (ic *ContainerEngine) NetworkDisconnect(ctx context.Context, networkname string, options entities.NetworkDisconnectOptions) error { return ic.Libpod.DisconnectContainerFromNetwork(options.Container, networkname, options.Force) } func (ic *ContainerEngine) NetworkConnect(ctx context.Context, networkname string, options entities.NetworkConnectOptions) error { return ic.Libpod.ConnectContainerToNetwork(options.Container, networkname, options.PerNetworkOptions) } // NetworkExists checks if the given network exists func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string) (*entities.BoolReport, error) { _, err := ic.Libpod.Network().NetworkInspect(networkname) exists := true // if err is ErrNoSuchNetwork do not return it if errors.Is(err, define.ErrNoSuchNetwork) { exists = false } else if err != nil { return nil, err } return &entities.BoolReport{ Value: exists, }, nil } // Network prune removes unused networks func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.NetworkPruneOptions) ([]*entities.NetworkPruneReport, error) { // get all filters filters, err := netutil.GenerateNetworkPruneFilters(options.Filters) if err != nil { return nil, err } danglingFilterFunc, err := ic.createDanglingFilterFunc(true) if err != nil { return nil, err } filters = append(filters, danglingFilterFunc) nets, err := ic.Libpod.Network().NetworkList(filters...) if err != nil { return nil, err } pruneReport := make([]*entities.NetworkPruneReport, 0, len(nets)) for _, net := range nets { pruneReport = append(pruneReport, &entities.NetworkPruneReport{ Name: net.Name, Error: ic.Libpod.Network().NetworkRemove(net.Name), }) } return pruneReport, nil } // danglingFilter function is special and not implemented in libnetwork filters func (ic *ContainerEngine) createDanglingFilterFunc(wantDangling bool) (types.FilterFunc, error) { cons, err := ic.Libpod.GetAllContainers() if err != nil { return nil, err } // Gather up all the non-default networks that the // containers want networksToKeep := make(map[string]bool) for _, c := range cons { nets, err := c.Networks() if err != nil { return nil, err } for _, n := range nets { networksToKeep[n] = true } } // ignore the default network, this one cannot be deleted networksToKeep[ic.Libpod.GetDefaultNetworkName()] = true return func(net types.Network) bool { for network := range networksToKeep { if network == net.Name { return !wantDangling } } return wantDangling }, nil } type ContainerNetStatus struct { // Name of the container Name string // ID of the container ID string // Status contains the net status, the key is the network name Status map[string]types.StatusBlock } func (ic *ContainerEngine) GetContainerNetStatuses() ([]ContainerNetStatus, error) { cons, err := ic.Libpod.GetAllContainers() if err != nil { return nil, err } statuses := make([]ContainerNetStatus, 0, len(cons)) for _, con := range cons { status, err := con.GetNetworkStatus() if err != nil { if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) { continue } return nil, err } statuses = append(statuses, ContainerNetStatus{ ID: con.ID(), Name: con.Name(), Status: status, }) } return statuses, nil }