package netstack
import (
"context"
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
cnilibrary "github.com/containernetworking/cni/libcni"
)
func TestCNIContextUsesPluginTimeout(t *testing.T) {
earliestDeadline := time.Now().Add(cniPluginTimeout)
ctx, cancel := cniContext(context.Background())
defer cancel()
latestDeadline := time.Now().Add(cniPluginTimeout)
deadline, ok := ctx.Deadline()
if !ok {
t.Fatal("cniContext() has no deadline")
}
if deadline.Before(earliestDeadline) || deadline.After(latestDeadline) {
t.Fatalf("cniContext() deadline = %v, want between %v and %v", deadline, earliestDeadline, latestDeadline)
}
}
func TestCNIContextPreservesShorterCallerDeadline(t *testing.T) {
parent, parentCancel := context.WithTimeout(context.Background(), time.Second)
defer parentCancel()
parentDeadline, _ := parent.Deadline()
ctx, cancel := cniContext(parent)
defer cancel()
deadline, ok := ctx.Deadline()
if !ok || !deadline.Equal(parentDeadline) {
t.Fatalf("cniContext() deadline = %v, want caller deadline %v", deadline, parentDeadline)
}
}
const testBridgeCNIConfig = `{
"cniVersion": "1.0.0",
"name": "conch-test",
"type": "bridge",
"bridge": "conch-test0"
}`
func writeTestCNIConfig(t *testing.T, dir, name, config string) {
t.Helper()
if err := os.WriteFile(filepath.Join(dir, name), []byte(config), 0o600); err != nil {
t.Fatalf("write CNI config: %v", err)
}
}
func TestSetHostLocalIPAMDataDir(t *testing.T) {
network := &cnilibrary.NetworkConfigList{Plugins: []*cnilibrary.PluginConfig{{
Bytes: []byte(`{"type":"bridge","ipam":{"type":"host-local","dataDir":"/old"}}`),
}}}
if err := setHostLocalIPAMDataDir(network, "/state/conch/cni/networks"); err != nil {
t.Fatalf("setHostLocalIPAMDataDir() error = %v", err)
}
var config map[string]any
if err := json.Unmarshal(network.Plugins[0].Bytes, &config); err != nil {
t.Fatalf("decode rewritten CNI config: %v", err)
}
ipam := config["ipam"].(map[string]any)
if got := ipam["dataDir"]; got != "/state/conch/cni/networks" {
t.Fatalf("host-local dataDir = %q, want /state/conch/cni/networks", got)
}
}
func TestLoadDefaultCNINetworkUsesFirstSortedConfig(t *testing.T) {
confDir := t.TempDir()
writeTestCNIConfig(t, confDir, "20-second.conf", `{
"cniVersion": "1.0.0", "name": "second", "type": "bridge", "bridge": "second0"
}`)
writeTestCNIConfig(t, confDir, "10-first.conf", testBridgeCNIConfig)
network, err := loadDefaultCNINetwork(confDir)
if err != nil {
t.Fatalf("loadDefaultCNINetwork(): %v", err)
}
if network.Name != "conch-test" {
t.Fatalf("loaded network = %q, want conch-test", network.Name)
}
}
func TestNewCNIManagerUsesInternalInterface(t *testing.T) {
confDir := t.TempDir()
writeTestCNIConfig(t, confDir, "10-conch.conf", testBridgeCNIConfig)
manager, err := NewCNIManager(CNIManagerConfig{
PluginBinDirs: []string{t.TempDir()},
PluginConfDir: confDir,
})
if err != nil {
t.Fatalf("NewCNIManager(): %v", err)
}
if manager.ifName != cniOuterInterfaceName {
t.Fatalf("CNI interface = %q, want %q", manager.ifName, cniOuterInterfaceName)
}
}