diff --git a/pkg/network/network.go b/pkg/network/network.go index cbc73c70..4f3c969a 100644 --- a/pkg/network/network.go +++ b/pkg/network/network.go @@ -33,6 +33,11 @@ const ( var netlog = logrus.WithField("subsystem", "network") +// ErrNoContainerNetwork is returned when the current network namespace has no +// suitable container interface (e.g. ctr without networking, or --network none). +// Callers may treat this as a non-fatal condition and continue without guest networking. +var ErrNoContainerNetwork = errors.New("no suitable network interface found in namespace") + type UnikernelNetworkInfo struct { TapDevice string EthDevice Interface @@ -403,7 +408,7 @@ func discoverContainerIface() (netlink.Link, error) { } netlog.Debugf("skipping interface %s: no default route found", attrs.Name) } - return nil, errors.New("no suitable network interface found in namespace") + return nil, ErrNoContainerNetwork } func deleteAllQDiscs(device netlink.Link) error { diff --git a/pkg/network/network_static.go b/pkg/network/network_static.go index fa46608c..fef9be1c 100644 --- a/pkg/network/network_static.go +++ b/pkg/network/network_static.go @@ -93,8 +93,7 @@ func (n StaticNetwork) NetworkSetup(uid uint32, gid uint32) (*UnikernelNetworkIn addTCRules := false redirectLink, err := discoverContainerIface() if err != nil { - netlog.Errorf("failed to find container interface, (unikernel may have been spawned using ctr): %v", err) - return nil, err + return nil, fmt.Errorf("failed to find container interface, (unikernel may have been spawned using ctr): %w", err) } newTapDevice, err := networkSetup(newTapName, StaticIPAddr, redirectLink, addTCRules, uid, gid) if err != nil { diff --git a/pkg/network/network_test.go b/pkg/network/network_test.go index da8c1d3d..a5946ffa 100644 --- a/pkg/network/network_test.go +++ b/pkg/network/network_test.go @@ -15,6 +15,8 @@ package network import ( + "errors" + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -56,3 +58,13 @@ func TestNewNetworkManager(t *testing.T) { }) } } + +func TestErrNoContainerNetworkIsDetectableWhenWrapped(t *testing.T) { + t.Parallel() + + wrapped := fmt.Errorf("failed to find container interface, (unikernel may have been spawned using ctr): %w", ErrNoContainerNetwork) + assert.True(t, errors.Is(wrapped, ErrNoContainerNetwork)) + + other := fmt.Errorf("createTapDevice(tap0_urunc) failed: %w", errors.New("permission denied")) + assert.False(t, errors.Is(other, ErrNoContainerNetwork)) +} diff --git a/pkg/unikontainers/unikontainers.go b/pkg/unikontainers/unikontainers.go index bc43bcac..ad4f64d4 100644 --- a/pkg/unikontainers/unikontainers.go +++ b/pkg/unikontainers/unikontainers.go @@ -266,23 +266,23 @@ func (u *Unikontainer) SetupNet() (types.NetDevParams, error) { networkInfo, err := netManager.NetworkSetup(u.Spec.Process.User.UID, u.Spec.Process.User.GID) if err != nil { - // TODO: Handle this case better. We do not need to show an error - // since there was no network in the container. Therefore, we - // need better error handling and specifically check if the container - // di not have any network. - uniklog.Errorf("Failed to setup network :%v. Possibly due to ctr", err) - } - // if network info is nil, we didn't find eth0, so we are running with ctr - if networkInfo != nil { - netArgs.TapDev = networkInfo.TapDevice - netArgs.IP = networkInfo.EthDevice.IP - netArgs.Mask = networkInfo.EthDevice.Mask - netArgs.Gateway = networkInfo.EthDevice.DefaultGateway - // The MAC address for the guest network device is the same as the - // virtual ethernet interface inside the namespace - netArgs.MAC = networkInfo.EthDevice.MAC - netArgs.MTU = networkInfo.EthDevice.MTU - } + // No container interface means there is no network to set up (e.g. ctr + // without CNI, or --network none). Continue without guest networking. + // Any other failure (TAP creation, TC rules, etc.) is fatal. + if errors.Is(err, network.ErrNoContainerNetwork) { + uniklog.Debugf("no container network interface found, continuing without network: %v", err) + return netArgs, nil + } + return netArgs, fmt.Errorf("failed to setup network: %w", err) + } + netArgs.TapDev = networkInfo.TapDevice + netArgs.IP = networkInfo.EthDevice.IP + netArgs.Mask = networkInfo.EthDevice.Mask + netArgs.Gateway = networkInfo.EthDevice.DefaultGateway + // The MAC address for the guest network device is the same as the + // virtual ethernet interface inside the namespace + netArgs.MAC = networkInfo.EthDevice.MAC + netArgs.MTU = networkInfo.EthDevice.MTU return netArgs, nil }