podman/pkg/domain/infra/abi/network.go

234 lines
7.5 KiB
Go

package abi
import (
"context"
"errors"
"fmt"
"strconv"
"github.com/containers/common/libnetwork/types"
netutil "github.com/containers/common/libnetwork/util"
"github.com/containers/common/pkg/util"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
)
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) ([]types.Network, []error, error) {
var errs []error
networks := make([]types.Network, 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)
}
}
networks = append(networks, net)
}
return networks, errs, nil
}
func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, options entities.NetworkReloadOptions) ([]*entities.NetworkReloadReport, error) {
ctrs, err := getContainersByContext(options.All, options.Latest, false, names, ic.Libpod)
if err != nil {
return nil, err
}
reports := make([]*entities.NetworkReloadReport, 0, len(ctrs))
for _, ctr := range ctrs {
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 util.StringInSlice(name, networks) {
// 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 a 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
}
}
}
if err := ic.Libpod.Network().NetworkRemove(name); err != nil {
report.Err = err
}
reports = append(reports, &report)
}
return reports, nil
}
func (ic *ContainerEngine) NetworkCreate(ctx context.Context, network types.Network) (*types.Network, error) {
// TODO (5.0): Stop accepting "pasta" as value here
if util.StringInSlice(network.Name, []string{"none", "host", "bridge", "private", "slirp4netns", "container", "ns"}) {
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)
if err != nil {
return nil, err
}
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
}