mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00

This commit resolves an issue where network creation and removal events were not being logged in `podman events`. A new function has been introduced in the `events` package to ensure consistent logging of network lifecycle events. This update will allow users to track network operations more effectively through the event log, improving visibility and aiding in debugging network-related issues. Fixes: #24032 Signed-off-by: Sainath Sativar <Sativar.sainath@gmail.com>
308 lines
9.8 KiB
Go
308 lines
9.8 KiB
Go
//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
|
|
}
|