mirror of
https://codeberg.org/cunicu/cunicu.git
synced 2025-10-30 22:56:19 +08:00
refactor: Use koanf.Koanf in e2e tests to pass agent configuration instead of CLI arguments
Signed-off-by: Steffen Vogel <post@steffenvogel.de>
This commit is contained in:
@@ -10,7 +10,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/ice/v2"
|
|
||||||
"github.com/pion/stun"
|
"github.com/pion/stun"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
@@ -33,8 +32,19 @@ var _ = Describe("Agent config", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
DescribeTable("can parse ICE urls with credentials",
|
DescribeTable("can parse ICE urls with credentials",
|
||||||
func(args []string, exp any) {
|
func(url, username, password string, exp any) {
|
||||||
cfg, err := config.ParseArgs(args...)
|
iceCfgStr := fmt.Sprintf("urls: [ '%s' ]", url)
|
||||||
|
|
||||||
|
if username != "" {
|
||||||
|
iceCfgStr += fmt.Sprintf(", username: '%s'", username)
|
||||||
|
}
|
||||||
|
|
||||||
|
if password != "" {
|
||||||
|
iceCfgStr += fmt.Sprintf(", password: '%s'", password)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgStr := fmt.Sprintf("ice: { %s }", iceCfgStr)
|
||||||
|
cfg, err := config.ParseRaw(cfgStr)
|
||||||
Expect(err).To(Succeed())
|
Expect(err).To(Succeed())
|
||||||
|
|
||||||
icfg := cfg.DefaultInterfaceSettings
|
icfg := cfg.DefaultInterfaceSettings
|
||||||
@@ -50,7 +60,7 @@ var _ = Describe("Agent config", func() {
|
|||||||
Expect(aCfg.Urls).To(ContainElements(exp))
|
Expect(aCfg.Urls).To(ContainElements(exp))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Entry("url1", []string{"--ice-url", "stun:server1", "--ice-username", "user1", "--ice-password", "pass1"}, &stun.URI{
|
Entry("url1", "stun:server1", "user1", "pass1", &stun.URI{
|
||||||
Scheme: stun.SchemeTypeSTUN,
|
Scheme: stun.SchemeTypeSTUN,
|
||||||
Host: "server1",
|
Host: "server1",
|
||||||
Port: 3478,
|
Port: 3478,
|
||||||
@@ -58,7 +68,7 @@ var _ = Describe("Agent config", func() {
|
|||||||
Username: "user1",
|
Username: "user1",
|
||||||
Password: "pass1",
|
Password: "pass1",
|
||||||
}),
|
}),
|
||||||
Entry("url2", []string{"--ice-url", "turn:server2:1234?transport=tcp", "--ice-username", "user1", "--ice-password", "pass1"}, &stun.URI{
|
Entry("url2", "turn:server2:1234?transport=tcp", "user1", "pass1", &stun.URI{
|
||||||
Scheme: stun.SchemeTypeTURN,
|
Scheme: stun.SchemeTypeTURN,
|
||||||
Host: "server2",
|
Host: "server2",
|
||||||
Port: 1234,
|
Port: 1234,
|
||||||
@@ -66,7 +76,7 @@ var _ = Describe("Agent config", func() {
|
|||||||
Username: "user1",
|
Username: "user1",
|
||||||
Password: "pass1",
|
Password: "pass1",
|
||||||
}),
|
}),
|
||||||
Entry("url3", []string{"--ice-url", "turn:user3:pass3@server3:1234?transport=tcp", "--ice-password", "pass3"}, &stun.URI{
|
Entry("url3", "turn:user3:pass3@server3:1234?transport=tcp", "", "pass3", &stun.URI{
|
||||||
Scheme: stun.SchemeTypeTURN,
|
Scheme: stun.SchemeTypeTURN,
|
||||||
Host: "server3",
|
Host: "server3",
|
||||||
Port: 1234,
|
Port: 1234,
|
||||||
@@ -74,8 +84,8 @@ var _ = Describe("Agent config", func() {
|
|||||||
Username: "user3",
|
Username: "user3",
|
||||||
Password: "pass3",
|
Password: "pass3",
|
||||||
}),
|
}),
|
||||||
Entry("url3", []string{"--ice-password", "pass1", "--ice-url", "http://bla.0l.de"}, "failed to gather ICE URLs: invalid ICE URL scheme: http"),
|
Entry("url3", "http://bla.0l.de", "", "pass1", "failed to gather ICE URLs: invalid ICE URL scheme: http"),
|
||||||
Entry("url4", []string{"--ice-url", "stun:stun.cunicu.li?transport=tcp"}, "failed to gather ICE URLs: failed to parse STUN/TURN URL 'stun:stun.cunicu.li?transport=tcp': queries not supported in stun address"),
|
Entry("url4", "stun:stun.cunicu.li?transport=tcp", "", "", "failed to gather ICE URLs: failed to parse STUN/TURN URL 'stun:stun.cunicu.li?transport=tcp': queries not supported in stun address"),
|
||||||
)
|
)
|
||||||
|
|
||||||
Context("can get ICE URLs from relay API", func() {
|
Context("can get ICE URLs from relay API", func() {
|
||||||
@@ -120,7 +130,8 @@ var _ = Describe("Agent config", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("can get list of relays", func() {
|
It("can get list of relays", func() {
|
||||||
cfg, err := config.ParseArgs("--ice-url", fmt.Sprintf("grpc://localhost:%d?insecure=true", port))
|
cfgStr := fmt.Sprintf("ice: { urls: [ 'grpc://localhost:%d?insecure=true' ] }", port)
|
||||||
|
cfg, err := config.ParseRaw(cfgStr)
|
||||||
Expect(err).To(Succeed())
|
Expect(err).To(Succeed())
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -160,20 +171,6 @@ var _ = Describe("Agent config", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
It("can parse multiple candidate types", func() {
|
|
||||||
cfg, err := config.ParseArgs(
|
|
||||||
"--ice-candidate-type", "host",
|
|
||||||
"--ice-candidate-type", "relay",
|
|
||||||
)
|
|
||||||
Expect(err).To(Succeed())
|
|
||||||
|
|
||||||
icfg := cfg.DefaultInterfaceSettings
|
|
||||||
|
|
||||||
aCfg, err := icfg.AgentConfig(context.Background(), &pk)
|
|
||||||
Expect(err).To(Succeed())
|
|
||||||
Expect(aCfg.CandidateTypes).To(ConsistOf(ice.CandidateTypeRelay, ice.CandidateTypeHost))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("can parse multiple backend URLs when passed as individual command line arguments", func() {
|
It("can parse multiple backend URLs when passed as individual command line arguments", func() {
|
||||||
cfg, err := config.ParseArgs(
|
cfg, err := config.ParseArgs(
|
||||||
"--backend", "grpc://server1",
|
"--backend", "grpc://server1",
|
||||||
@@ -182,6 +179,8 @@ var _ = Describe("Agent config", func() {
|
|||||||
Expect(err).To(Succeed())
|
Expect(err).To(Succeed())
|
||||||
|
|
||||||
Expect(cfg.Backends).To(HaveLen(2))
|
Expect(cfg.Backends).To(HaveLen(2))
|
||||||
|
Expect(cfg.Backends[0].Host).To(Equal("server1"))
|
||||||
|
Expect(cfg.Backends[1].Host).To(Equal("server2"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("can parse multiple backend URLs when passed as comma-separated command line arguments", func() {
|
It("can parse multiple backend URLs when passed as comma-separated command line arguments", func() {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"dario.cat/mergo"
|
"dario.cat/mergo"
|
||||||
"github.com/knadh/koanf/parsers/yaml"
|
"github.com/knadh/koanf/parsers/yaml"
|
||||||
"github.com/knadh/koanf/providers/confmap"
|
"github.com/knadh/koanf/providers/confmap"
|
||||||
|
"github.com/knadh/koanf/providers/rawbytes"
|
||||||
"github.com/knadh/koanf/v2"
|
"github.com/knadh/koanf/v2"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
@@ -25,6 +26,8 @@ type Config struct {
|
|||||||
*Settings
|
*Settings
|
||||||
*Meta
|
*Meta
|
||||||
*koanf.Koanf
|
*koanf.Koanf
|
||||||
|
Runtime *koanf.Koanf
|
||||||
|
Sources []*Source
|
||||||
ExtraSources []*Source
|
ExtraSources []*Source
|
||||||
|
|
||||||
// Settings which are not configurable via configuration file
|
// Settings which are not configurable via configuration file
|
||||||
@@ -55,6 +58,16 @@ func ParseArgs(args ...string) (*Config, error) {
|
|||||||
return c, c.Init(c.flags.Args())
|
return c, c.Init(c.flags.Args())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseRaw(cfg string) (*Config, error) {
|
||||||
|
c := New(nil)
|
||||||
|
|
||||||
|
c.ExtraSources = append(c.ExtraSources, &Source{
|
||||||
|
Provider: rawbytes.Provider([]byte(cfg)),
|
||||||
|
})
|
||||||
|
|
||||||
|
return c, c.Init(nil)
|
||||||
|
}
|
||||||
|
|
||||||
// New creates a new configuration instance.
|
// New creates a new configuration instance.
|
||||||
func New(flags *pflag.FlagSet) *Config {
|
func New(flags *pflag.FlagSet) *Config {
|
||||||
if flags == nil {
|
if flags == nil {
|
||||||
@@ -140,6 +153,8 @@ func (c *Config) Init(args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.Sources = append(c.Sources, c.ExtraSources...)
|
||||||
|
|
||||||
_, err = c.Reload()
|
_, err = c.Reload()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/knadh/koanf/parsers/yaml"
|
"github.com/knadh/koanf/parsers/yaml"
|
||||||
|
"github.com/knadh/koanf/providers/rawbytes"
|
||||||
"github.com/knadh/koanf/v2"
|
"github.com/knadh/koanf/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ func (s *Source) Load() error {
|
|||||||
func load(p koanf.Provider) (*koanf.Koanf, []string, error) {
|
func load(p koanf.Provider) (*koanf.Koanf, []string, error) {
|
||||||
var q koanf.Parser
|
var q koanf.Parser
|
||||||
switch p.(type) {
|
switch p.(type) {
|
||||||
case *RemoteFileProvider, *LocalFileProvider:
|
case *RemoteFileProvider, *LocalFileProvider, *rawbytes.RawBytes:
|
||||||
q = yaml.Parser()
|
q = yaml.Parser()
|
||||||
default:
|
default:
|
||||||
q = nil
|
q = nil
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ var _ = Context("nat double: Carrier Grade NAT setup with two relays and a singl
|
|||||||
// Mixed IPv4/IPv6 NAT tests are currently broken for some reason.
|
// Mixed IPv4/IPv6 NAT tests are currently broken for some reason.
|
||||||
// Hence we limit ourself to IPv6 here.
|
// Hence we limit ourself to IPv6 here.
|
||||||
// See: https://github.com/stv0g/cunicu/issues/224
|
// See: https://github.com/stv0g/cunicu/issues/224
|
||||||
opt.ExtraArgs{"--ice-network-type", "udp6,tcp6"},
|
opt.ConfigValue("ice.network_types", []string{"udp6", "tcp6"}),
|
||||||
)
|
)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ var _ = Context("nat simple: Simple home-router NAT setup", func() {
|
|||||||
// Mixed IPv4/IPv6 NAT tests are currently broken for some reason.
|
// Mixed IPv4/IPv6 NAT tests are currently broken for some reason.
|
||||||
// Hence we limit ourself to IPv6 here.
|
// Hence we limit ourself to IPv6 here.
|
||||||
// See: https://github.com/stv0g/cunicu/issues/224
|
// See: https://github.com/stv0g/cunicu/issues/224
|
||||||
opt.ExtraArgs{"--ice-network-type", "udp6,tcp6"},
|
opt.ConfigValue("ice.network_types", []string{"udp6", "tcp6"}),
|
||||||
// opt.ExtraArgs{"--ice-candidate-type", "host,srflx"},
|
// opt.ICECandidateTypes(ice.CandidateTypeHost, ice.CandidateTypeServerReflexive),
|
||||||
)
|
)
|
||||||
Expect(err).To(Succeed(), "Failed to created nodes: %s", err)
|
Expect(err).To(Succeed(), "Failed to created nodes: %s", err)
|
||||||
|
|
||||||
|
|||||||
@@ -31,57 +31,67 @@ func (n *Network) ConnectivityTests() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Network) ConnectivityTestsWithExtraArgs(extraArgs ...any) {
|
func (n *Network) ConnectivityTestsForAllCandidateTypes() {
|
||||||
|
ConnectivityTests := func() {
|
||||||
|
Context("ipv4: Allow IPv4 network only", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
n.AgentOptions = append(n.AgentOptions,
|
n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.network_types", "udp4"))
|
||||||
opt.ExtraArgs(extraArgs),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
n.ConnectivityTests()
|
n.ConnectivityTests()
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Network) ConnectivityTestsForAllCandidateTypes() {
|
|
||||||
Context("candidate-types", func() {
|
|
||||||
Context("any: Allow any candidate type", func() {
|
|
||||||
Context("ipv4: Allow IPv4 network only", func() {
|
|
||||||
n.ConnectivityTestsWithExtraArgs("--ice-network-type", "udp4")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("ipv6: Allow IPv6 network only", func() {
|
Context("ipv6: Allow IPv6 network only", func() {
|
||||||
n.ConnectivityTestsWithExtraArgs("--ice-network-type", "udp6")
|
BeforeEach(func() {
|
||||||
|
n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.network_types", "udp6"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
n.ConnectivityTests()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Context("candidate-types", func() {
|
||||||
|
Context("any: Allow any candidate type", func() {
|
||||||
|
ConnectivityTests()
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("host: Allow only host candidates", func() {
|
Context("host: Allow only host candidates", func() {
|
||||||
Context("ipv4: Allow IPv4 network only", func() {
|
BeforeEach(func() {
|
||||||
n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "host", "--ice-network-type", "udp4") // , "--port-forwarding=false")
|
n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.candidate_types", "host"))
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("ipv6: Allow IPv6 network only", func() {
|
ConnectivityTests()
|
||||||
n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "host", "--ice-network-type", "udp6")
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("srflx: Allow only server reflexive candidates", func() {
|
Context("srflx: Allow only server reflexive candidates", func() {
|
||||||
Context("ipv4: Allow IPv4 network only", func() {
|
BeforeEach(func() {
|
||||||
n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "srflx", "--ice-network-type", "udp4")
|
n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.candidate_types", "srflx"))
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("ipv6: Allow IPv6 network only", func() {
|
ConnectivityTests()
|
||||||
n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "srflx", "--ice-network-type", "udp6")
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("relay: Allow only relay candidates", func() {
|
Context("relay: Allow only relay candidates", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.candidate_types", "relay"))
|
||||||
|
})
|
||||||
|
|
||||||
Context("ipv4: Allow IPv4 network only", func() {
|
Context("ipv4: Allow IPv4 network only", func() {
|
||||||
n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "relay", "--ice-network-type", "udp4")
|
BeforeEach(func() {
|
||||||
|
n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.network_types", "udp4"))
|
||||||
|
})
|
||||||
|
|
||||||
|
n.ConnectivityTests()
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: Check why IPv6 relay is not working
|
// TODO: Check why IPv6 relay is not working
|
||||||
// Blocked by: https://github.com/pion/ice/pull/462
|
// Blocked by: https://github.com/pion/ice/pull/462
|
||||||
Context("ipv6", Pending, func() {
|
Context("ipv6: Allow IPv6 network only", Pending, func() {
|
||||||
n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "relay", "--ice-network-type", "udp6")
|
BeforeEach(func() {
|
||||||
|
n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.network_types", "udp6"))
|
||||||
|
})
|
||||||
|
|
||||||
|
n.ConnectivityTests()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/knadh/koanf/v2"
|
||||||
|
opt "github.com/stv0g/cunicu/test/e2e/nodes/options"
|
||||||
g "github.com/stv0g/gont/v2/pkg"
|
g "github.com/stv0g/gont/v2/pkg"
|
||||||
gopt "github.com/stv0g/gont/v2/pkg/options"
|
gopt "github.com/stv0g/gont/v2/pkg/options"
|
||||||
copt "github.com/stv0g/gont/v2/pkg/options/capture"
|
copt "github.com/stv0g/gont/v2/pkg/options/capture"
|
||||||
@@ -108,32 +110,28 @@ func (n *Network) Start() {
|
|||||||
|
|
||||||
By("Starting agent nodes")
|
By("Starting agent nodes")
|
||||||
|
|
||||||
err = n.AgentNodes.Start(n.BasePath, n.AgentArgs()...)
|
err = n.AgentNodes.Start(n.BasePath, n.AgentConfig())
|
||||||
Expect(err).To(Succeed(), "Failed to start cunicu: %s", err)
|
Expect(err).To(Succeed(), "Failed to start cunicu: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Network) AgentArgs() []any {
|
func (n *Network) AgentConfig() nodes.AgentConfigOption {
|
||||||
extraArgs := []any{}
|
return opt.Config(func(k *koanf.Koanf) {
|
||||||
|
var urls, backends []string
|
||||||
if len(n.RelayNodes) > 0 {
|
|
||||||
// TODO: We currently assume that all relays use the same credentials
|
|
||||||
extraArgs = append(extraArgs,
|
|
||||||
"--ice-username", n.RelayNodes[0].Username(),
|
|
||||||
"--ice-password", n.RelayNodes[0].Password(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range n.RelayNodes {
|
for _, r := range n.RelayNodes {
|
||||||
for _, u := range r.URLs() {
|
for _, u := range r.URLs() {
|
||||||
extraArgs = append(extraArgs, "--ice-url", u)
|
urls = append(urls, u.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range n.SignalingNodes {
|
for _, m := range n.SignalingNodes {
|
||||||
extraArgs = append(extraArgs, "--backend", s.URL())
|
u := m.URL()
|
||||||
|
backends = append(backends, u.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return extraArgs
|
k.Set("ice.urls", urls) //nolint:errcheck
|
||||||
|
k.Set("backends", backends) //nolint:errcheck
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Network) Close() {
|
func (n *Network) Close() {
|
||||||
@@ -184,16 +182,19 @@ func (n *Network) WriteSpecReport() {
|
|||||||
func (n *Network) Init() {
|
func (n *Network) Init() {
|
||||||
*n = Network{}
|
*n = Network{}
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
Expect(err).To(Succeed())
|
||||||
|
|
||||||
n.Name = fmt.Sprintf("cunicu-%d", rand.Uint32()) //nolint:gosec
|
n.Name = fmt.Sprintf("cunicu-%d", rand.Uint32()) //nolint:gosec
|
||||||
n.BasePath = filepath.Join(SpecName()...)
|
n.BasePath = filepath.Join(SpecName()...)
|
||||||
n.BasePath = filepath.Join("logs", n.BasePath)
|
n.BasePath = filepath.Join(cwd, "logs", n.BasePath)
|
||||||
|
|
||||||
logFilename := filepath.Join(n.BasePath, "test.log")
|
logFilename := filepath.Join(n.BasePath, "test.log")
|
||||||
pcapFilename := filepath.Join(n.BasePath, "capture.pcapng")
|
pcapFilename := filepath.Join(n.BasePath, "capture.pcapng")
|
||||||
|
|
||||||
By("Tweaking sysctls for large Gont networks")
|
By("Tweaking sysctls for large Gont networks")
|
||||||
|
|
||||||
err := osx.SetSysctlMap(map[string]any{
|
err = osx.SetSysctlMap(map[string]any{
|
||||||
"net.ipv4.neigh.default.gc_thresh1": 10000,
|
"net.ipv4.neigh.default.gc_thresh1": 10000,
|
||||||
"net.ipv4.neigh.default.gc_thresh2": 15000,
|
"net.ipv4.neigh.default.gc_thresh2": 15000,
|
||||||
"net.ipv4.neigh.default.gc_thresh3": 20000,
|
"net.ipv4.neigh.default.gc_thresh3": 20000,
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/knadh/koanf/parsers/yaml"
|
||||||
|
"github.com/knadh/koanf/v2"
|
||||||
g "github.com/stv0g/gont/v2/pkg"
|
g "github.com/stv0g/gont/v2/pkg"
|
||||||
copt "github.com/stv0g/gont/v2/pkg/options/cmd"
|
copt "github.com/stv0g/gont/v2/pkg/options/cmd"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@@ -20,12 +22,17 @@ import (
|
|||||||
"github.com/stv0g/cunicu/pkg/log"
|
"github.com/stv0g/cunicu/pkg/log"
|
||||||
rpcproto "github.com/stv0g/cunicu/pkg/proto/rpc"
|
rpcproto "github.com/stv0g/cunicu/pkg/proto/rpc"
|
||||||
"github.com/stv0g/cunicu/pkg/rpc"
|
"github.com/stv0g/cunicu/pkg/rpc"
|
||||||
|
"github.com/stv0g/cunicu/test/e2e/nodes/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AgentOption interface {
|
type AgentOption interface {
|
||||||
Apply(a *Agent)
|
Apply(a *Agent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentConfigOption interface {
|
||||||
|
Apply(k *koanf.Koanf)
|
||||||
|
}
|
||||||
|
|
||||||
// Agent is a host running the cunīcu daemon.
|
// Agent is a host running the cunīcu daemon.
|
||||||
//
|
//
|
||||||
// Each agent can have one or more WireGuard interfaces configured which are managed
|
// Each agent can have one or more WireGuard interfaces configured which are managed
|
||||||
@@ -33,16 +40,14 @@ type AgentOption interface {
|
|||||||
type Agent struct {
|
type Agent struct {
|
||||||
*g.Host
|
*g.Host
|
||||||
|
|
||||||
|
Config *koanf.Koanf
|
||||||
Command *g.Cmd
|
Command *g.Cmd
|
||||||
Client *rpc.Client
|
Client *rpc.Client
|
||||||
|
|
||||||
WireGuardClient *wgctrl.Client
|
WireGuardClient *wgctrl.Client
|
||||||
|
|
||||||
ExtraArgs []any
|
|
||||||
WireGuardInterfaces []*WireGuardInterface
|
WireGuardInterfaces []*WireGuardInterface
|
||||||
|
|
||||||
logFile io.WriteCloser
|
logFile io.WriteCloser
|
||||||
|
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,14 +59,26 @@ func NewAgent(m *g.Network, name string, opts ...g.Option) (*Agent, error) {
|
|||||||
|
|
||||||
a := &Agent{
|
a := &Agent{
|
||||||
Host: h,
|
Host: h,
|
||||||
|
Config: koanf.New("."),
|
||||||
|
|
||||||
logger: log.Global.Named("node.agent").With(zap.String("node", name)),
|
logger: log.Global.Named("node.agent").With(zap.String("node", name)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default config
|
||||||
|
options.ConfigMap{
|
||||||
|
"experimental": true,
|
||||||
|
"log.level": "debug10",
|
||||||
|
"rpc.wait": true,
|
||||||
|
"sync_hosts": false,
|
||||||
|
}.Apply(a.Config)
|
||||||
|
|
||||||
// Apply agent options
|
// Apply agent options
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
if aopt, ok := opt.(AgentOption); ok {
|
switch aopt := opt.(type) {
|
||||||
|
case AgentOption:
|
||||||
aopt.Apply(a)
|
aopt.Apply(a)
|
||||||
|
case AgentConfigOption:
|
||||||
|
aopt.Apply(a.Config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,19 +93,32 @@ func NewAgent(m *g.Network, name string, opts ...g.Option) (*Agent, error) {
|
|||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Agent) Start(_, dir string, extraArgs ...any) error {
|
func (a *Agent) Start(_, dir string, args ...any) (err error) {
|
||||||
var err error
|
cfgPath := fmt.Sprintf("%s/%s.yaml", dir, a.Name())
|
||||||
rpcSockPath := fmt.Sprintf("/var/run/cunicu.%s.sock", a.Name())
|
|
||||||
logPath := fmt.Sprintf("%s/%s.log", dir, a.Name())
|
logPath := fmt.Sprintf("%s/%s.log", dir, a.Name())
|
||||||
|
rpcPath := fmt.Sprintf("/var/run/cunicu.%s.sock", a.Name())
|
||||||
|
|
||||||
|
// Create agent configuration
|
||||||
|
cfg := a.Config.Copy()
|
||||||
|
cfg.Set("rpc.socket", rpcPath) //nolint:errcheck
|
||||||
|
|
||||||
|
extraArgs := []any{}
|
||||||
|
for _, arg := range args {
|
||||||
|
if aopt, ok := arg.(AgentConfigOption); ok {
|
||||||
|
aopt.Apply(cfg)
|
||||||
|
} else {
|
||||||
|
extraArgs = append(extraArgs, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Old RPC sockets are also removed by cunīcu.
|
// Old RPC sockets are also removed by cunīcu.
|
||||||
// However we also need to do it here to avoid racing
|
// However we also need to do it here to avoid racing
|
||||||
// against rpc.Connect() further down here
|
// against rpc.Connect() further down here
|
||||||
if err := os.RemoveAll(rpcSockPath); err != nil {
|
if err := os.RemoveAll(rpcPath); err != nil {
|
||||||
return fmt.Errorf("failed to remove old socket: %w", err)
|
return fmt.Errorf("failed to remove old socket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
binary, profileArgs, err := BuildBinary(a.Name())
|
binary, startArgs, err := BuildBinary(a.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to build: %w", err)
|
return fmt.Errorf("failed to build: %w", err)
|
||||||
}
|
}
|
||||||
@@ -98,27 +128,28 @@ func (a *Agent) Start(_, dir string, extraArgs ...any) error {
|
|||||||
return fmt.Errorf("failed to open log file: %w", err)
|
return fmt.Errorf("failed to open log file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
args := profileArgs
|
cfgRaw, err := cfg.Marshal(yaml.Parser())
|
||||||
args = append(args,
|
if err != nil {
|
||||||
"daemon",
|
return fmt.Errorf("failed to marshal config: %w", err)
|
||||||
"--rpc-socket", rpcSockPath,
|
}
|
||||||
"--rpc-wait",
|
|
||||||
"--log-level", "debug5",
|
|
||||||
"--sync-hosts=false",
|
|
||||||
)
|
|
||||||
args = append(args, a.ExtraArgs...)
|
|
||||||
args = append(args, extraArgs...)
|
|
||||||
args = append(args,
|
|
||||||
copt.Combined(a.logFile),
|
|
||||||
copt.Dir(dir),
|
|
||||||
copt.EnvVar("CUNICU_EXPERIMENTAL", "1"),
|
|
||||||
)
|
|
||||||
|
|
||||||
if a.Command, err = a.Host.Start(binary, args...); err != nil {
|
if err := os.WriteFile(cfgPath, cfgRaw, 0o644); err != nil { //nolint:gosec
|
||||||
|
return fmt.Errorf("failed to write config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
startArgs = append(startArgs,
|
||||||
|
copt.Combined(a.logFile),
|
||||||
|
copt.EnvVar("CUNICU_CONFIG_ALLOW_INSECURE", "true"),
|
||||||
|
copt.Dir(dir),
|
||||||
|
"daemon", "--config", cfgPath,
|
||||||
|
)
|
||||||
|
startArgs = append(startArgs, extraArgs...)
|
||||||
|
|
||||||
|
if a.Command, err = a.Host.Start(binary, startArgs...); err != nil {
|
||||||
return fmt.Errorf("failed to start: %w", err)
|
return fmt.Errorf("failed to start: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.Client, err = rpc.Connect(rpcSockPath); err != nil {
|
if a.Client, err = rpc.Connect(rpcPath); err != nil {
|
||||||
return fmt.Errorf("failed to connect to to control socket: %w", err)
|
return fmt.Errorf("failed to connect to to control socket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import (
|
|||||||
|
|
||||||
type AgentList []*Agent
|
type AgentList []*Agent
|
||||||
|
|
||||||
func (al AgentList) Start(dir string, extraArgs ...any) error {
|
func (al AgentList) Start(dir string, args ...any) error {
|
||||||
if err := al.ForEachAgent(func(a *Agent) error {
|
if err := al.ForEachAgent(func(a *Agent) error {
|
||||||
return a.Start("", dir, extraArgs...)
|
return a.Start("", dir, args...)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("failed to start agent: %w", err)
|
return fmt.Errorf("failed to start agent: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -31,6 +32,8 @@ var (
|
|||||||
binaryPath string
|
binaryPath string
|
||||||
binaryRunArgs []string
|
binaryRunArgs []string
|
||||||
binaryOnce sync.Once
|
binaryOnce sync.Once
|
||||||
|
|
||||||
|
errBuild = errors.New("failed to build")
|
||||||
)
|
)
|
||||||
|
|
||||||
func BuildBinary(name string) (string, []any, error) {
|
func BuildBinary(name string) (string, []any, error) {
|
||||||
@@ -130,7 +133,7 @@ func buildBinary(packagePath string) (string, []string, error) {
|
|||||||
zap.Bool("test", test))
|
zap.Bool("test", test))
|
||||||
|
|
||||||
if output, err := exec.Command("go", cmdArgs...).CombinedOutput(); err != nil {
|
if output, err := exec.Command("go", cmdArgs...).CombinedOutput(); err != nil {
|
||||||
return "", nil, fmt.Errorf("failed to build %s:\n\nError:\n%s\n\nOutput:\n%s", packagePath, path, string(output))
|
return "", nil, fmt.Errorf("%w %s:\n\nError:\n%s\n\nOutput:\n%s", errBuild, packagePath, path, string(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug("Finished building",
|
logger.Debug("Finished building",
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 Steffen Vogel <post@steffenvogel.de>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stv0g/cunicu/test/e2e/nodes"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ExtraArgs []any
|
|
||||||
|
|
||||||
func (ea ExtraArgs) Apply(a *nodes.Agent) {
|
|
||||||
a.ExtraArgs = append(a.ExtraArgs, ea...)
|
|
||||||
}
|
|
||||||
38
test/e2e/nodes/options/agent_config.go
Normal file
38
test/e2e/nodes/options/agent_config.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Steffen Vogel <post@steffenvogel.de>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/knadh/koanf/providers/confmap"
|
||||||
|
"github.com/knadh/koanf/v2"
|
||||||
|
"github.com/stv0g/cunicu/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config func(k *koanf.Koanf)
|
||||||
|
|
||||||
|
func (c Config) Apply(k *koanf.Koanf) {
|
||||||
|
c(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigMap map[string]any
|
||||||
|
|
||||||
|
func (m ConfigMap) Apply(k *koanf.Koanf) {
|
||||||
|
p := confmap.Provider(m, ".")
|
||||||
|
|
||||||
|
k.Load(p, nil) //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfigValue(key string, value any) Config {
|
||||||
|
return func(k *koanf.Koanf) {
|
||||||
|
k.Set(key, value) //nolint:errcheck
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfigStruct(s *config.Settings) Config {
|
||||||
|
return func(k *koanf.Koanf) {
|
||||||
|
p := config.NewStructsProvider(s, "koanf")
|
||||||
|
|
||||||
|
k.Load(p, nil) //nolint:errcheck
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pion/stun"
|
"net/url"
|
||||||
|
|
||||||
g "github.com/stv0g/gont/v2/pkg"
|
g "github.com/stv0g/gont/v2/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,9 +13,7 @@ type RelayNode interface {
|
|||||||
Node
|
Node
|
||||||
|
|
||||||
WaitReady() error
|
WaitReady() error
|
||||||
URLs() []*stun.URI
|
URLs() []url.URL
|
||||||
Username() string
|
|
||||||
Password() string
|
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
Apply(i *g.Interface)
|
Apply(i *g.Interface)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -22,6 +23,11 @@ import (
|
|||||||
|
|
||||||
var errTimeout = errors.New("timed out")
|
var errTimeout = errors.New("timed out")
|
||||||
|
|
||||||
|
const (
|
||||||
|
relayUsername = "user1"
|
||||||
|
relayPassword = "password1"
|
||||||
|
)
|
||||||
|
|
||||||
type CoturnNode struct {
|
type CoturnNode struct {
|
||||||
*g.Host
|
*g.Host
|
||||||
|
|
||||||
@@ -55,23 +61,14 @@ func NewCoturnNode(n *g.Network, name string, opts ...g.Option) (*CoturnNode, er
|
|||||||
"listening-port": strconv.Itoa(stun.DefaultPort),
|
"listening-port": strconv.Itoa(stun.DefaultPort),
|
||||||
"realm": "cunicu",
|
"realm": "cunicu",
|
||||||
"cli-password": "cunicu",
|
"cli-password": "cunicu",
|
||||||
|
"user": fmt.Sprintf("%s:%s", relayUsername, relayPassword),
|
||||||
},
|
},
|
||||||
logger: log.Global.Named("node.relay").With(zap.String("node", name)),
|
logger: log.Global.Named("node.relay").With(zap.String("node", name)),
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Config["user"] = fmt.Sprintf("%s:%s", t.Username(), t.Password())
|
|
||||||
|
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CoturnNode) Username() string {
|
|
||||||
return "user1"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CoturnNode) Password() string {
|
|
||||||
return "password1"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CoturnNode) Start(_, dir string, extraArgs ...any) error {
|
func (c *CoturnNode) Start(_, dir string, extraArgs ...any) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -151,27 +148,25 @@ func (c *CoturnNode) WaitReady() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CoturnNode) URLs() []*stun.URI {
|
func (c *CoturnNode) URLs() []url.URL {
|
||||||
host := c.Name()
|
host := c.Name()
|
||||||
|
hostPort := fmt.Sprintf("%s:%d", host, stun.DefaultPort)
|
||||||
|
userHostPort := fmt.Sprintf("%s:%s@%s", relayUsername, relayPassword, hostPort)
|
||||||
|
|
||||||
return []*stun.URI{
|
return []url.URL{
|
||||||
{
|
{
|
||||||
Scheme: stun.SchemeTypeSTUN,
|
Scheme: "stun",
|
||||||
Host: host,
|
Opaque: hostPort,
|
||||||
Port: stun.DefaultPort,
|
|
||||||
Proto: stun.ProtoTypeUDP,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme: stun.SchemeTypeTURN,
|
Scheme: "turn",
|
||||||
Host: host,
|
Opaque: userHostPort,
|
||||||
Port: stun.DefaultPort,
|
RawQuery: "transport=udp",
|
||||||
Proto: stun.ProtoTypeUDP,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme: stun.SchemeTypeTURN,
|
Scheme: "turn",
|
||||||
Host: host,
|
Opaque: userHostPort,
|
||||||
Port: stun.DefaultPort,
|
RawQuery: "transport=tcp",
|
||||||
Proto: stun.ProtoTypeTCP,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ import "net/url"
|
|||||||
type SignalingNode interface {
|
type SignalingNode interface {
|
||||||
Node
|
Node
|
||||||
|
|
||||||
URL() *url.URL
|
URL() url.URL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,10 +103,12 @@ func (s *GrpcSignalingNode) Close() error {
|
|||||||
return s.Stop()
|
return s.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GrpcSignalingNode) URL() *url.URL {
|
func (s *GrpcSignalingNode) URL() url.URL {
|
||||||
return &url.URL{
|
hostPort := fmt.Sprintf("%s:%d", s.Name(), s.port)
|
||||||
|
|
||||||
|
return url.URL{
|
||||||
Scheme: "grpc",
|
Scheme: "grpc",
|
||||||
Host: fmt.Sprintf("%s:%d", s.Name(), s.port),
|
Host: hostPort,
|
||||||
RawQuery: "insecure=true",
|
RawQuery: "insecure=true",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ var _ = Context("restart: Restart ICE agents", func() {
|
|||||||
n.AgentOptions = append(n.AgentOptions,
|
n.AgentOptions = append(n.AgentOptions,
|
||||||
gopt.EmptyDir(wg.ConfigPath),
|
gopt.EmptyDir(wg.ConfigPath),
|
||||||
gopt.EmptyDir(wg.SocketPath),
|
gopt.EmptyDir(wg.SocketPath),
|
||||||
opt.ExtraArgs{"--ice-candidate-type", "host"},
|
opt.ConfigValue("ice.candidate_types", "host"),
|
||||||
)
|
)
|
||||||
|
|
||||||
n.WireGuardInterfaceOptions = append(n.WireGuardInterfaceOptions,
|
n.WireGuardInterfaceOptions = append(n.WireGuardInterfaceOptions,
|
||||||
@@ -170,7 +170,7 @@ var _ = Context("restart: Restart ICE agents", func() {
|
|||||||
|
|
||||||
By("Re-starting first agent again")
|
By("Re-starting first agent again")
|
||||||
|
|
||||||
err = n1.Start("", n.BasePath, n.AgentArgs()...)
|
err = n1.Start("", n.BasePath, n.AgentConfig())
|
||||||
Expect(err).To(Succeed(), "Failed to restart first agent: %s", err)
|
Expect(err).To(Succeed(), "Failed to restart first agent: %s", err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
gfopt "github.com/stv0g/gont/v2/pkg/options/filters"
|
gfopt "github.com/stv0g/gont/v2/pkg/options/filters"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/stv0g/cunicu/pkg/config"
|
||||||
"github.com/stv0g/cunicu/pkg/wg"
|
"github.com/stv0g/cunicu/pkg/wg"
|
||||||
"github.com/stv0g/cunicu/test"
|
"github.com/stv0g/cunicu/test"
|
||||||
"github.com/stv0g/cunicu/test/e2e/nodes"
|
"github.com/stv0g/cunicu/test/e2e/nodes"
|
||||||
@@ -140,7 +141,9 @@ var _ = Context("simple: Simple local-area switched topology with variable numbe
|
|||||||
)
|
)
|
||||||
|
|
||||||
n.AgentOptions = append(n.AgentOptions,
|
n.AgentOptions = append(n.AgentOptions,
|
||||||
opt.ExtraArgs{"--wg-userspace", "wg0"},
|
opt.ConfigValue("interfaces.wg0", config.InterfaceSettings{
|
||||||
|
UserSpace: true,
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -189,7 +192,7 @@ var _ = Context("simple: Simple local-area switched topology with variable numbe
|
|||||||
Context("pdisc: Peer Discovery", Pending, Ordered, func() {
|
Context("pdisc: Peer Discovery", Pending, Ordered, func() {
|
||||||
BeforeEach(OncePerOrdered, func() {
|
BeforeEach(OncePerOrdered, func() {
|
||||||
n.AgentOptions = append(n.AgentOptions,
|
n.AgentOptions = append(n.AgentOptions,
|
||||||
opt.ExtraArgs{"--community", "hallo"},
|
opt.ConfigValue("community", "hallo"),
|
||||||
)
|
)
|
||||||
|
|
||||||
n.WireGuardInterfaceOptions = append(n.WireGuardInterfaceOptions,
|
n.WireGuardInterfaceOptions = append(n.WireGuardInterfaceOptions,
|
||||||
|
|||||||
Reference in New Issue
Block a user