isle/go/daemon/network/network_it_util_test.go

228 lines
4.9 KiB
Go

package network
import (
"context"
"fmt"
"isle/bootstrap"
"isle/daemon/children"
"isle/daemon/daecommon"
"isle/nebula"
"isle/toolkit"
"os"
"path/filepath"
"sync"
"sync/atomic"
"testing"
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
"gopkg.in/yaml.v3"
)
// Utilities related to running network integration tests
var (
getEnvBinDirPath = sync.OnceValue(func() string {
appDirPath := os.Getenv("APPDIR")
if appDirPath == "" {
panic("APPDIR not set")
}
return filepath.Join(appDirPath, "bin")
})
ipNetCounter uint64 = 0
publicAddrPortCounter uint64 = 1024
tunDeviceCounter uint64 = 0
)
func newIPNet() nebula.IPNet {
var (
ipNet nebula.IPNet
ipNetStr = fmt.Sprintf(
"172.16.%d.0/24", atomic.AddUint64(&ipNetCounter, 1),
)
)
if err := ipNet.UnmarshalText([]byte(ipNetStr)); err != nil {
panic(fmt.Sprintf("parsing IPNet from %q: %v", ipNetStr, err))
}
return ipNet
}
func newPublicAddr() string {
return fmt.Sprintf(
"127.0.0.200:%d", atomic.AddUint64(&publicAddrPortCounter, 1),
)
}
func newTunDevice() string {
return fmt.Sprintf("isle-test-%d", atomic.AddUint64(&tunDeviceCounter, 1))
}
func mustParseNetworkConfigf(str string, args ...any) daecommon.NetworkConfig {
str = fmt.Sprintf(str, args...)
var networkConfig daecommon.NetworkConfig
if err := yaml.Unmarshal([]byte(str), &networkConfig); err != nil {
panic(fmt.Sprintf("parsing network config: %v", err))
}
return networkConfig
}
type integrationHarness struct {
ctx context.Context
logger *mlog.Logger
rootDir toolkit.Dir
dirCounter uint64
}
func newIntegrationHarness(t *testing.T) *integrationHarness {
toolkit.MarkIntegrationTest(t)
rootDir, err := os.MkdirTemp("", "isle-network-it-test.*")
if err != nil {
t.Fatalf("creating root temp dir: %v", err)
}
t.Cleanup(func() {
if t.Failed() {
t.Logf("Temp directory for failed test not deleted: %q", rootDir)
return
}
t.Logf("Deleting temp directory %q", rootDir)
if err := os.RemoveAll(rootDir); err != nil {
t.Errorf("failed to remove %q: %v", rootDir, err)
}
})
return &integrationHarness{
ctx: context.Background(),
logger: mlog.NewLogger(nil),
rootDir: toolkit.Dir{Path: rootDir},
}
}
func (h *integrationHarness) mkDir(t *testing.T, name string) toolkit.Dir {
fullName := fmt.Sprintf("%s-%d", name, atomic.AddUint64(&h.dirCounter, 1))
t.Logf("Creating directory %q", fullName)
d, err := h.rootDir.MkChildDir(fullName, false)
if err != nil {
t.Fatalf("creating %q: %v", fullName, err)
}
return d
}
type createNetworkOpts struct {
creationParams bootstrap.CreationParams
noCleanup bool
}
func (o *createNetworkOpts) withDefaults() *createNetworkOpts {
if o == nil {
o = new(createNetworkOpts)
}
if o.creationParams == (bootstrap.CreationParams{}) {
o.creationParams = bootstrap.NewCreationParams("test", "test.localnet")
}
return o
}
type integrationHarnessNetwork struct {
Network
creationParams bootstrap.CreationParams
stateDir, runtimeDir toolkit.Dir
}
func (h *integrationHarness) createNetwork(
t *testing.T,
hostNameStr string,
opts *createNetworkOpts,
) integrationHarnessNetwork {
opts = opts.withDefaults()
var (
networkConfig = mustParseNetworkConfigf(`
vpn:
public_addr: %q
tun:
device: %q
storage:
allocations:
- data_path: %s
meta_path: %s
capacity: 1
- data_path: %s
meta_path: %s
capacity: 1
- data_path: %s
meta_path: %s
capacity: 1
`,
newPublicAddr(),
newTunDevice(),
h.mkDir(t, "data").Path,
h.mkDir(t, "meta").Path,
h.mkDir(t, "data").Path,
h.mkDir(t, "meta").Path,
h.mkDir(t, "data").Path,
h.mkDir(t, "meta").Path,
)
stateDir = h.mkDir(t, "state")
runtimeDir = h.mkDir(t, "runtime")
childrenLogFilePath = filepath.Join(runtimeDir.Path, "children.log")
ipNet = newIPNet()
hostName = nebula.HostName(hostNameStr)
)
childrenLogFile, err := os.Create(childrenLogFilePath)
if err != nil {
t.Fatalf("creating %q: %v", childrenLogFilePath, err)
}
t.Cleanup(func() {
if err := childrenLogFile.Close(); err != nil {
t.Errorf("closing %q: %v", childrenLogFilePath, err)
}
})
network, err := Create(
h.ctx,
h.logger.WithNamespace("network"),
networkConfig,
getEnvBinDirPath(),
stateDir,
runtimeDir,
opts.creationParams,
ipNet,
hostName,
&Opts{
ChildrenOpts: &children.Opts{
Stdout: childrenLogFile,
Stderr: childrenLogFile,
},
},
)
if err != nil {
t.Fatalf("creating Network: %v", err)
}
if !opts.noCleanup {
t.Cleanup(func() {
t.Log("Shutting down Network")
if err := network.Shutdown(); err != nil {
t.Logf("Shutting down Network failed: %v", err)
}
})
}
return integrationHarnessNetwork{
network, opts.creationParams, stateDir, runtimeDir,
}
}