mirror of
				https://codeberg.org/cunicu/cunicu.git
				synced 2025-10-31 13:16:38 +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" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/pion/ice/v2" | ||||
| 	"github.com/pion/stun" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/credentials/insecure" | ||||
| @@ -33,8 +32,19 @@ var _ = Describe("Agent config", func() { | ||||
| 	}) | ||||
|  | ||||
| 	DescribeTable("can parse ICE urls with credentials", | ||||
| 		func(args []string, exp any) { | ||||
| 			cfg, err := config.ParseArgs(args...) | ||||
| 		func(url, username, password string, exp any) { | ||||
| 			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()) | ||||
|  | ||||
| 			icfg := cfg.DefaultInterfaceSettings | ||||
| @@ -50,7 +60,7 @@ var _ = Describe("Agent config", func() { | ||||
| 				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, | ||||
| 			Host:     "server1", | ||||
| 			Port:     3478, | ||||
| @@ -58,7 +68,7 @@ var _ = Describe("Agent config", func() { | ||||
| 			Username: "user1", | ||||
| 			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, | ||||
| 			Host:     "server2", | ||||
| 			Port:     1234, | ||||
| @@ -66,7 +76,7 @@ var _ = Describe("Agent config", func() { | ||||
| 			Username: "user1", | ||||
| 			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, | ||||
| 			Host:     "server3", | ||||
| 			Port:     1234, | ||||
| @@ -74,8 +84,8 @@ var _ = Describe("Agent config", func() { | ||||
| 			Username: "user3", | ||||
| 			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("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("url3", "http://bla.0l.de", "", "pass1", "failed to gather ICE URLs: invalid ICE URL scheme: http"), | ||||
| 		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() { | ||||
| @@ -120,7 +130,8 @@ var _ = Describe("Agent config", 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()) | ||||
|  | ||||
| 			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() { | ||||
| 		cfg, err := config.ParseArgs( | ||||
| 			"--backend", "grpc://server1", | ||||
| @@ -182,6 +179,8 @@ var _ = Describe("Agent config", func() { | ||||
| 		Expect(err).To(Succeed()) | ||||
|  | ||||
| 		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() { | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import ( | ||||
| 	"dario.cat/mergo" | ||||
| 	"github.com/knadh/koanf/parsers/yaml" | ||||
| 	"github.com/knadh/koanf/providers/confmap" | ||||
| 	"github.com/knadh/koanf/providers/rawbytes" | ||||
| 	"github.com/knadh/koanf/v2" | ||||
| 	"github.com/mitchellh/mapstructure" | ||||
| 	"github.com/spf13/pflag" | ||||
| @@ -25,6 +26,8 @@ type Config struct { | ||||
| 	*Settings | ||||
| 	*Meta | ||||
| 	*koanf.Koanf | ||||
| 	Runtime      *koanf.Koanf | ||||
| 	Sources      []*Source | ||||
| 	ExtraSources []*Source | ||||
|  | ||||
| 	// 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()) | ||||
| } | ||||
|  | ||||
| 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. | ||||
| func New(flags *pflag.FlagSet) *Config { | ||||
| 	if flags == nil { | ||||
| @@ -140,6 +153,8 @@ func (c *Config) Init(args []string) error { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	c.Sources = append(c.Sources, c.ExtraSources...) | ||||
|  | ||||
| 	_, err = c.Reload() | ||||
|  | ||||
| 	return err | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/knadh/koanf/parsers/yaml" | ||||
| 	"github.com/knadh/koanf/providers/rawbytes" | ||||
| 	"github.com/knadh/koanf/v2" | ||||
| ) | ||||
|  | ||||
| @@ -44,7 +45,7 @@ func (s *Source) Load() error { | ||||
| func load(p koanf.Provider) (*koanf.Koanf, []string, error) { | ||||
| 	var q koanf.Parser | ||||
| 	switch p.(type) { | ||||
| 	case *RemoteFileProvider, *LocalFileProvider: | ||||
| 	case *RemoteFileProvider, *LocalFileProvider, *rawbytes.RawBytes: | ||||
| 		q = yaml.Parser() | ||||
| 	default: | ||||
| 		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. | ||||
| 			// Hence we limit ourself to IPv6 here. | ||||
| 			// See: https://github.com/stv0g/cunicu/issues/224 | ||||
| 			opt.ExtraArgs{"--ice-network-type", "udp6,tcp6"}, | ||||
| 			opt.ConfigValue("ice.network_types", []string{"udp6", "tcp6"}), | ||||
| 		) | ||||
|  | ||||
| 		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. | ||||
| 			// Hence we limit ourself to IPv6 here. | ||||
| 			// See: https://github.com/stv0g/cunicu/issues/224 | ||||
| 			opt.ExtraArgs{"--ice-network-type", "udp6,tcp6"}, | ||||
| 			// opt.ExtraArgs{"--ice-candidate-type", "host,srflx"}, | ||||
| 			opt.ConfigValue("ice.network_types", []string{"udp6", "tcp6"}), | ||||
| 			// opt.ICECandidateTypes(ice.CandidateTypeHost, ice.CandidateTypeServerReflexive), | ||||
| 		) | ||||
| 		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() { | ||||
| 		n.AgentOptions = append(n.AgentOptions, | ||||
| 			opt.ExtraArgs(extraArgs), | ||||
| 		) | ||||
| 				n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.network_types", "udp4")) | ||||
| 			}) | ||||
|  | ||||
| 			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() { | ||||
| 				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("ipv4: Allow IPv4 network only", func() { | ||||
| 				n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "host", "--ice-network-type", "udp4") // , "--port-forwarding=false") | ||||
| 			BeforeEach(func() { | ||||
| 				n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.candidate_types", "host")) | ||||
| 			}) | ||||
|  | ||||
| 			Context("ipv6: Allow IPv6 network only", func() { | ||||
| 				n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "host", "--ice-network-type", "udp6") | ||||
| 			}) | ||||
| 			ConnectivityTests() | ||||
| 		}) | ||||
|  | ||||
| 		Context("srflx: Allow only server reflexive candidates", func() { | ||||
| 			Context("ipv4: Allow IPv4 network only", func() { | ||||
| 				n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "srflx", "--ice-network-type", "udp4") | ||||
| 			BeforeEach(func() { | ||||
| 				n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.candidate_types", "srflx")) | ||||
| 			}) | ||||
|  | ||||
| 			Context("ipv6: Allow IPv6 network only", func() { | ||||
| 				n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "srflx", "--ice-network-type", "udp6") | ||||
| 			}) | ||||
| 			ConnectivityTests() | ||||
| 		}) | ||||
|  | ||||
| 		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() { | ||||
| 				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 | ||||
| 			// Blocked by: https://github.com/pion/ice/pull/462 | ||||
| 			Context("ipv6", Pending, func() { | ||||
| 				n.ConnectivityTestsWithExtraArgs("--ice-candidate-type", "relay", "--ice-network-type", "udp6") | ||||
| 			Context("ipv6: Allow IPv6 network only", Pending, func() { | ||||
| 				BeforeEach(func() { | ||||
| 					n.AgentOptions = append(n.AgentOptions, opt.ConfigValue("ice.network_types", "udp6")) | ||||
| 				}) | ||||
|  | ||||
| 				n.ConnectivityTests() | ||||
| 			}) | ||||
| 		}) | ||||
| 	}) | ||||
|   | ||||
| @@ -9,6 +9,8 @@ import ( | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/knadh/koanf/v2" | ||||
| 	opt "github.com/stv0g/cunicu/test/e2e/nodes/options" | ||||
| 	g "github.com/stv0g/gont/v2/pkg" | ||||
| 	gopt "github.com/stv0g/gont/v2/pkg/options" | ||||
| 	copt "github.com/stv0g/gont/v2/pkg/options/capture" | ||||
| @@ -108,32 +110,28 @@ func (n *Network) Start() { | ||||
|  | ||||
| 	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) | ||||
| } | ||||
|  | ||||
| func (n *Network) AgentArgs() []any { | ||||
| 	extraArgs := []any{} | ||||
|  | ||||
| 	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(), | ||||
| 		) | ||||
| 	} | ||||
| func (n *Network) AgentConfig() nodes.AgentConfigOption { | ||||
| 	return opt.Config(func(k *koanf.Koanf) { | ||||
| 		var urls, backends []string | ||||
|  | ||||
| 		for _, r := range n.RelayNodes { | ||||
| 			for _, u := range r.URLs() { | ||||
| 			extraArgs = append(extraArgs, "--ice-url", u) | ||||
| 				urls = append(urls, u.String()) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	for _, s := range n.SignalingNodes { | ||||
| 		extraArgs = append(extraArgs, "--backend", s.URL()) | ||||
| 		for _, m := range n.SignalingNodes { | ||||
| 			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() { | ||||
| @@ -184,16 +182,19 @@ func (n *Network) WriteSpecReport() { | ||||
| func (n *Network) Init() { | ||||
| 	*n = Network{} | ||||
|  | ||||
| 	cwd, err := os.Getwd() | ||||
| 	Expect(err).To(Succeed()) | ||||
|  | ||||
| 	n.Name = fmt.Sprintf("cunicu-%d", rand.Uint32()) //nolint:gosec | ||||
| 	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") | ||||
| 	pcapFilename := filepath.Join(n.BasePath, "capture.pcapng") | ||||
|  | ||||
| 	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_thresh2": 15000, | ||||
| 		"net.ipv4.neigh.default.gc_thresh3": 20000, | ||||
|   | ||||
| @@ -11,6 +11,8 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/knadh/koanf/parsers/yaml" | ||||
| 	"github.com/knadh/koanf/v2" | ||||
| 	g "github.com/stv0g/gont/v2/pkg" | ||||
| 	copt "github.com/stv0g/gont/v2/pkg/options/cmd" | ||||
| 	"go.uber.org/zap" | ||||
| @@ -20,12 +22,17 @@ import ( | ||||
| 	"github.com/stv0g/cunicu/pkg/log" | ||||
| 	rpcproto "github.com/stv0g/cunicu/pkg/proto/rpc" | ||||
| 	"github.com/stv0g/cunicu/pkg/rpc" | ||||
| 	"github.com/stv0g/cunicu/test/e2e/nodes/options" | ||||
| ) | ||||
|  | ||||
| type AgentOption interface { | ||||
| 	Apply(a *Agent) | ||||
| } | ||||
|  | ||||
| type AgentConfigOption interface { | ||||
| 	Apply(k *koanf.Koanf) | ||||
| } | ||||
|  | ||||
| // Agent is a host running the cunīcu daemon. | ||||
| // | ||||
| // Each agent can have one or more WireGuard interfaces configured which are managed | ||||
| @@ -33,16 +40,14 @@ type AgentOption interface { | ||||
| type Agent struct { | ||||
| 	*g.Host | ||||
|  | ||||
| 	Config          *koanf.Koanf | ||||
| 	Command         *g.Cmd | ||||
| 	Client          *rpc.Client | ||||
|  | ||||
| 	WireGuardClient *wgctrl.Client | ||||
|  | ||||
| 	ExtraArgs           []any | ||||
| 	WireGuardInterfaces []*WireGuardInterface | ||||
|  | ||||
| 	logFile io.WriteCloser | ||||
|  | ||||
| 	logger  *log.Logger | ||||
| } | ||||
|  | ||||
| @@ -54,14 +59,26 @@ func NewAgent(m *g.Network, name string, opts ...g.Option) (*Agent, error) { | ||||
|  | ||||
| 	a := &Agent{ | ||||
| 		Host:   h, | ||||
| 		Config: koanf.New("."), | ||||
|  | ||||
| 		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 | ||||
| 	for _, opt := range opts { | ||||
| 		if aopt, ok := opt.(AgentOption); ok { | ||||
| 		switch aopt := opt.(type) { | ||||
| 		case AgentOption: | ||||
| 			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 | ||||
| } | ||||
|  | ||||
| func (a *Agent) Start(_, dir string, extraArgs ...any) error { | ||||
| 	var err error | ||||
| 	rpcSockPath := fmt.Sprintf("/var/run/cunicu.%s.sock", a.Name()) | ||||
| func (a *Agent) Start(_, dir string, args ...any) (err error) { | ||||
| 	cfgPath := fmt.Sprintf("%s/%s.yaml", 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. | ||||
| 	// However we also need to do it here to avoid racing | ||||
| 	// 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) | ||||
| 	} | ||||
|  | ||||
| 	binary, profileArgs, err := BuildBinary(a.Name()) | ||||
| 	binary, startArgs, err := BuildBinary(a.Name()) | ||||
| 	if err != nil { | ||||
| 		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) | ||||
| 	} | ||||
|  | ||||
| 	args := profileArgs | ||||
| 	args = append(args, | ||||
| 		"daemon", | ||||
| 		"--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"), | ||||
| 	) | ||||
| 	cfgRaw, err := cfg.Marshal(yaml.Parser()) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to marshal config: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	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) | ||||
| 	} | ||||
|  | ||||
| 	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) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -15,9 +15,9 @@ import ( | ||||
|  | ||||
| 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 { | ||||
| 		return a.Start("", dir, extraArgs...) | ||||
| 		return a.Start("", dir, args...) | ||||
| 	}); err != nil { | ||||
| 		return fmt.Errorf("failed to start agent: %w", err) | ||||
| 	} | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
| package nodes | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| @@ -31,6 +32,8 @@ var ( | ||||
| 	binaryPath    string | ||||
| 	binaryRunArgs []string | ||||
| 	binaryOnce    sync.Once | ||||
|  | ||||
| 	errBuild = errors.New("failed to build") | ||||
| ) | ||||
|  | ||||
| func BuildBinary(name string) (string, []any, error) { | ||||
| @@ -130,7 +133,7 @@ func buildBinary(packagePath string) (string, []string, error) { | ||||
| 		zap.Bool("test", test)) | ||||
|  | ||||
| 	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", | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
| import ( | ||||
| 	"github.com/pion/stun" | ||||
| 	"net/url" | ||||
|  | ||||
| 	g "github.com/stv0g/gont/v2/pkg" | ||||
| ) | ||||
|  | ||||
| @@ -12,9 +13,7 @@ type RelayNode interface { | ||||
| 	Node | ||||
|  | ||||
| 	WaitReady() error | ||||
| 	URLs() []*stun.URI | ||||
| 	Username() string | ||||
| 	Password() string | ||||
| 	URLs() []url.URL | ||||
|  | ||||
| 	// Options | ||||
| 	Apply(i *g.Interface) | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"strconv" | ||||
| @@ -22,6 +23,11 @@ import ( | ||||
|  | ||||
| var errTimeout = errors.New("timed out") | ||||
|  | ||||
| const ( | ||||
| 	relayUsername = "user1" | ||||
| 	relayPassword = "password1" | ||||
| ) | ||||
|  | ||||
| type CoturnNode struct { | ||||
| 	*g.Host | ||||
|  | ||||
| @@ -55,23 +61,14 @@ func NewCoturnNode(n *g.Network, name string, opts ...g.Option) (*CoturnNode, er | ||||
| 			"listening-port":           strconv.Itoa(stun.DefaultPort), | ||||
| 			"realm":                    "cunicu", | ||||
| 			"cli-password":             "cunicu", | ||||
| 			"user":                     fmt.Sprintf("%s:%s", relayUsername, relayPassword), | ||||
| 		}, | ||||
| 		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 | ||||
| } | ||||
|  | ||||
| func (c *CoturnNode) Username() string { | ||||
| 	return "user1" | ||||
| } | ||||
|  | ||||
| func (c *CoturnNode) Password() string { | ||||
| 	return "password1" | ||||
| } | ||||
|  | ||||
| func (c *CoturnNode) Start(_, dir string, extraArgs ...any) error { | ||||
| 	var err error | ||||
|  | ||||
| @@ -151,27 +148,25 @@ func (c *CoturnNode) WaitReady() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (c *CoturnNode) URLs() []*stun.URI { | ||||
| func (c *CoturnNode) URLs() []url.URL { | ||||
| 	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, | ||||
| 			Host:   host, | ||||
| 			Port:   stun.DefaultPort, | ||||
| 			Proto:  stun.ProtoTypeUDP, | ||||
| 			Scheme: "stun", | ||||
| 			Opaque: hostPort, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Scheme: stun.SchemeTypeTURN, | ||||
| 			Host:   host, | ||||
| 			Port:   stun.DefaultPort, | ||||
| 			Proto:  stun.ProtoTypeUDP, | ||||
| 			Scheme:   "turn", | ||||
| 			Opaque:   userHostPort, | ||||
| 			RawQuery: "transport=udp", | ||||
| 		}, | ||||
| 		{ | ||||
| 			Scheme: stun.SchemeTypeTURN, | ||||
| 			Host:   host, | ||||
| 			Port:   stun.DefaultPort, | ||||
| 			Proto:  stun.ProtoTypeTCP, | ||||
| 			Scheme:   "turn", | ||||
| 			Opaque:   userHostPort, | ||||
| 			RawQuery: "transport=tcp", | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -8,5 +8,5 @@ import "net/url" | ||||
| type SignalingNode interface { | ||||
| 	Node | ||||
|  | ||||
| 	URL() *url.URL | ||||
| 	URL() url.URL | ||||
| } | ||||
|   | ||||
| @@ -103,10 +103,12 @@ func (s *GrpcSignalingNode) Close() error { | ||||
| 	return s.Stop() | ||||
| } | ||||
|  | ||||
| func (s *GrpcSignalingNode) URL() *url.URL { | ||||
| 	return &url.URL{ | ||||
| func (s *GrpcSignalingNode) URL() url.URL { | ||||
| 	hostPort := fmt.Sprintf("%s:%d", s.Name(), s.port) | ||||
|  | ||||
| 	return url.URL{ | ||||
| 		Scheme:   "grpc", | ||||
| 		Host:     fmt.Sprintf("%s:%d", s.Name(), s.port), | ||||
| 		Host:     hostPort, | ||||
| 		RawQuery: "insecure=true", | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -60,7 +60,7 @@ var _ = Context("restart: Restart ICE agents", func() { | ||||
| 		n.AgentOptions = append(n.AgentOptions, | ||||
| 			gopt.EmptyDir(wg.ConfigPath), | ||||
| 			gopt.EmptyDir(wg.SocketPath), | ||||
| 			opt.ExtraArgs{"--ice-candidate-type", "host"}, | ||||
| 			opt.ConfigValue("ice.candidate_types", "host"), | ||||
| 		) | ||||
|  | ||||
| 		n.WireGuardInterfaceOptions = append(n.WireGuardInterfaceOptions, | ||||
| @@ -170,7 +170,7 @@ var _ = Context("restart: Restart ICE agents", func() { | ||||
|  | ||||
| 			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) | ||||
| 		}) | ||||
| 	}) | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import ( | ||||
| 	gfopt "github.com/stv0g/gont/v2/pkg/options/filters" | ||||
| 	"golang.org/x/sys/unix" | ||||
|  | ||||
| 	"github.com/stv0g/cunicu/pkg/config" | ||||
| 	"github.com/stv0g/cunicu/pkg/wg" | ||||
| 	"github.com/stv0g/cunicu/test" | ||||
| 	"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, | ||||
| 				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() { | ||||
| 		BeforeEach(OncePerOrdered, func() { | ||||
| 			n.AgentOptions = append(n.AgentOptions, | ||||
| 				opt.ExtraArgs{"--community", "hallo"}, | ||||
| 				opt.ConfigValue("community", "hallo"), | ||||
| 			) | ||||
|  | ||||
| 			n.WireGuardInterfaceOptions = append(n.WireGuardInterfaceOptions, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Steffen Vogel
					Steffen Vogel