252 lines
5.4 KiB
Go
252 lines
5.4 KiB
Go
package network
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"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 {
|
|
t.Parallel()
|
|
toolkit.MarkIntegrationTest(t)
|
|
|
|
rootDir, err := os.MkdirTemp("", "isle-network-it-test.*")
|
|
if err != nil {
|
|
t.Fatalf("creating root temp dir: %v", err)
|
|
}
|
|
t.Logf("Temporary test directory: %q", rootDir)
|
|
|
|
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
|
|
manualShutdown 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
|
|
networkConfig daecommon.NetworkConfig
|
|
stateDir, runtimeDir toolkit.Dir
|
|
opts *Opts
|
|
}
|
|
|
|
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)
|
|
|
|
childrenOpts *children.Opts
|
|
)
|
|
|
|
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)
|
|
}
|
|
})
|
|
|
|
if os.Getenv("ISLE_INTEGRATION_TEST_CHILDREN_LOG_STDOUT") == "" {
|
|
childrenOpts = &children.Opts{
|
|
Stdout: childrenLogFile,
|
|
Stderr: childrenLogFile,
|
|
}
|
|
} else {
|
|
childrenOpts = &children.Opts{
|
|
Stdout: io.MultiWriter(os.Stdout, childrenLogFile),
|
|
Stderr: io.MultiWriter(os.Stdout, childrenLogFile),
|
|
}
|
|
}
|
|
|
|
networkOpts := &Opts{
|
|
ChildrenOpts: childrenOpts,
|
|
}
|
|
|
|
network, err := Create(
|
|
h.ctx,
|
|
h.logger.WithNamespace("network"),
|
|
networkConfig,
|
|
getEnvBinDirPath(),
|
|
stateDir,
|
|
runtimeDir,
|
|
opts.creationParams,
|
|
ipNet,
|
|
hostName,
|
|
networkOpts,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("creating Network: %v", err)
|
|
}
|
|
|
|
if !opts.manualShutdown {
|
|
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,
|
|
networkConfig,
|
|
stateDir,
|
|
runtimeDir,
|
|
networkOpts,
|
|
}
|
|
}
|