Skip to content

Commit

Permalink
refactor: Rewrite config discovery in strict OpenAPI mode
Browse files Browse the repository at this point in the history
  • Loading branch information
rg0now committed Jan 19, 2024
1 parent 22c2575 commit 60021e7
Show file tree
Hide file tree
Showing 30 changed files with 989 additions and 390 deletions.
32 changes: 12 additions & 20 deletions cmd/stunnerd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,9 @@ import (

// usage: stunnerd -v turn://user1:[email protected]:3478?transport=udp

const (
defaultLoglevel = "all:INFO"
defaultConfigDiscoveryAddress = "http://localhost:13478"
envVarName = "STUNNER_NAME"
envVarNamespace = "STUNNER_NAMESPACE"
envVarConfigOrigin = "STUNNER_CONFIG_ORIGIN"
)

func main() {
os.Args[0] = "stunnerd"
var config = flag.StringP("config", "c", "", fmt.Sprintf("Config origin, either a valid IP address or URL to the CDS server, or a file name (overrides: STUNNER_CONFIG_ORIGIN, default: %s).", defaultConfigDiscoveryAddress))
var config = flag.StringP("config", "c", "", fmt.Sprintf("Config origin, either a valid IP address or URL to the CDS server, or a file name (overrides: STUNNER_CONFIG_ORIGIN, default: %s).", stnrv1.DefaultConfigDiscoveryAddress))
var level = flag.StringP("log", "l", "", "Log level (format: <scope>:<level>, overrides: PION_LOG_*, default: all:INFO).")
var id = flag.StringP("id", "i", "", "Id for identifying with the CDS server (format: <namespace>/<name>, overrides: STUNNER_NAMESPACE/STUNNER_NAME, default: <default/stunnerd-hostname>).")
var watch = flag.BoolP("watch", "w", false, "Watch config file for updates (default: false).")
Expand All @@ -37,7 +29,7 @@ func main() {
var verbose = flag.BoolP("verbose", "v", false, "Verbose logging, identical to <-l all:DEBUG>.")
flag.Parse()

logLevel := defaultLoglevel
logLevel := stnrv1.DefaultLogLevel
if *verbose {
logLevel = "all:DEBUG"
}
Expand All @@ -46,24 +38,24 @@ func main() {
logLevel = *level
}

configOrigin := defaultConfigDiscoveryAddress
if origin, ok := os.LookupEnv(envVarConfigOrigin); ok {
configOrigin := stnrv1.DefaultConfigDiscoveryAddress
if origin, ok := os.LookupEnv(stnrv1.DefaultEnvVarConfigOrigin); ok {
configOrigin = origin
}
if *config != "" {
configOrigin = *config
}

if *id == "" {
name, ok1 := os.LookupEnv(envVarName)
namespace, ok2 := os.LookupEnv(envVarNamespace)
name, ok1 := os.LookupEnv(stnrv1.DefaultEnvVarName)
namespace, ok2 := os.LookupEnv(stnrv1.DefaultEnvVarNamespace)
if ok1 && ok2 {
*id = fmt.Sprintf("%s/%s", namespace, name)
}
}

st := stunner.NewStunner(stunner.Options{
Id: *id,
Name: *id,
LogLevel: logLevel,
DryRun: *dryRun,
UDPListenerThreadNum: *udpThreadNum,
Expand All @@ -74,7 +66,7 @@ func main() {

log.Infof("starting stunnerd instance %q", st.GetId())

conf := make(chan stnrv1.StunnerConfig, 1)
conf := make(chan *stnrv1.StunnerConfig, 1)
defer close(conf)

var cancelConfigLoader context.CancelFunc
Expand All @@ -88,7 +80,7 @@ func main() {
os.Exit(1)
}

conf <- *c
conf <- c

} else if !*watch {
log.Infof("loading configuration from origin %q", configOrigin)
Expand All @@ -99,12 +91,12 @@ func main() {
os.Exit(1)
}

conf <- *c
conf <- c

} else if *watch {
log.Info("bootstrapping with minimal config")
z := cdsclient.ZeroConfig(st.GetId())
conf <- *z
conf <- z

log.Infof("watching configuration at origin %q", configOrigin)
ctx, cancel := context.WithCancel(context.Background())
Expand Down Expand Up @@ -157,7 +149,7 @@ func main() {
}()

case c := <-conf:
log.Trace("new configuration file available")
log.Trace("new configuration available")

// command line loglevel overrides config
if *verbose || *level != "" {
Expand Down
12 changes: 6 additions & 6 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (

// Options defines various options for the STUNner server.
type Options struct {
// Id is the identifier of this stunnerd daemon instance. Defaults to hostname.
Id string
// Name is the identifier of this stunnerd daemon instance. Defaults to hostname.
Name string
// DryRun suppresses sideeffects: STUNner will not initialize listener sockets and bring up
// the TURN server, and it will not fire up the health-check and the metrics
// servers. Intended for testing, default is false.
Expand Down Expand Up @@ -144,8 +144,8 @@ func (s *Stunner) GetConfig() *stnrv1.StunnerConfig {
}

// LoadConfig loads a configuration from an origin. This is a shim wrapper around ConfigOrigin.Load.
func (s *Stunner) LoadConfig(config string) (*stnrv1.StunnerConfig, error) {
client, err := client.New(config, s.id, s.logger)
func (s *Stunner) LoadConfig(origin string) (*stnrv1.StunnerConfig, error) {
client, err := client.New(origin, s.name, s.logger)
if err != nil {
return nil, err
}
Expand All @@ -154,8 +154,8 @@ func (s *Stunner) LoadConfig(config string) (*stnrv1.StunnerConfig, error) {
}

// WatchConfig watches a configuration from an origin. This is a shim wrapper around ConfigOrigin.Watch.
func (s *Stunner) WatchConfig(ctx context.Context, origin string, ch chan<- stnrv1.StunnerConfig) error {
client, err := client.New(origin, s.id, s.logger)
func (s *Stunner) WatchConfig(ctx context.Context, origin string, ch chan<- *stnrv1.StunnerConfig) error {
client, err := client.New(origin, s.name, s.logger)
if err != nil {
return err
}
Expand Down
44 changes: 26 additions & 18 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestStunnerDefaultServerVNet(t *testing.T) {
})

log.Debug("starting stunnerd")
assert.NoError(t, stunner.Reconcile(*c), "starting server")
assert.NoError(t, stunner.Reconcile(c), "starting server")

log.Debug("creating a client")
lconn, err := v.wan.ListenPacket("udp4", "0.0.0.0:0")
Expand Down Expand Up @@ -145,7 +145,7 @@ func TestStunnerConfigFileWatcher(t *testing.T) {
stunner := NewStunner(Options{LogLevel: stunnerTestLoglevel})

log.Debug("starting watcher")
conf := make(chan stnrv1.StunnerConfig, 1)
conf := make(chan *stnrv1.StunnerConfig, 1)
defer close(conf)

log.Debug("init watcher with nonexistent config file")
Expand Down Expand Up @@ -187,9 +187,9 @@ func TestStunnerConfigFileWatcher(t *testing.T) {

c2, ok := <-conf
assert.True(t, ok, "config emitted")
checkDefaultConfig(t, &c2, "TURN-UDP")
checkDefaultConfig(t, c2, "TURN-UDP")

log.Debug("write a wrong config file (WatchConfig does not validate)")
log.Debug("write a wrong config file: WatchConfig validates")

c2.Listeners[0].Protocol = "dummy"
y, err = yaml.Marshal(c2)
Expand All @@ -204,12 +204,20 @@ func TestStunnerConfigFileWatcher(t *testing.T) {
// this makes sure that we do not share anything with ConfigWatch
c2.Listeners[0].PublicAddr = "AAAAAAAAAAAAAa"

c3 := <-conf
checkDefaultConfig(t, &c3, "dummy")
// we should not read anything so that channel should not br redable
time.Sleep(50 * time.Millisecond)
readable := false
select {
case _, ok := <-conf:
readable = ok
default:
readable = false
}
assert.False(t, readable, "wrong config file does not trigger a watch event")

log.Debug("update the config file and check")
c3.Listeners[0].Protocol = "TURN-TCP"
y, err = yaml.Marshal(c3)
c2.Listeners[0].Protocol = "TURN-TCP"
y, err = yaml.Marshal(c2)
assert.NoError(t, err, "marshal config file")
err = f.Truncate(0)
assert.NoError(t, err, "truncate temp file")
Expand All @@ -218,15 +226,15 @@ func TestStunnerConfigFileWatcher(t *testing.T) {
_, err = f.Write(y)
assert.NoError(t, err, "write config to temp file")

c4 := <-conf
checkDefaultConfig(t, &c4, "TURN-TCP")
c3 := <-conf
checkDefaultConfig(t, c3, "TURN-TCP")

stunner.Close()
}

const (
testConfigV1 = `{"version":"v1","admin":{"name":"ns1/tester", "loglevel":"all:ERROR"},"auth":{"type":"static","credentials":{"password":"passwd1","username":"user1"}},"listeners":[{"name":"udp","protocol":"turn-udp","address":"1.2.3.4","port":3478,"routes":["echo-server-cluster"]}],"clusters":[{"name":"echo-server-cluster","type":"STATIC","endpoints":["1.2.3.5"]}]}`
testConfigV1A1 = `{"version":"v1alpha1","admin":{"name":"ns1/tester", "loglevel":"all:ERROR"},"auth":{"type":"longterm","credentials":{"password":"passwd1","username":"user1"}},"listeners":[{"name":"udp","protocol":"turn-udp","address":"1.2.3.4","port":3478,"routes":["echo-server-cluster"]}],"clusters":[{"name":"echo-server-cluster","type":"STATIC","endpoints":["1.2.3.5"]}]}`
testConfigV1A1 = `{"version":"v1alpha1","admin":{"name":"ns1/tester", "loglevel":"all:ERROR"},"auth":{"type":"ephemeral","credentials":{"secret":"test-secret"}},"listeners":[{"name":"udp","protocol":"turn-udp","address":"1.2.3.4","port":3478,"routes":["echo-server-cluster"]}],"clusters":[{"name":"echo-server-cluster","type":"STATIC","endpoints":["1.2.3.5"]}]}`
)

// test with v1alpha1 and v1
Expand All @@ -251,7 +259,7 @@ func TestStunnerConfigFileWatcherMultiVersion(t *testing.T) {
stunner := NewStunner(Options{LogLevel: stunnerTestLoglevel})

log.Debug("starting watcher")
conf := make(chan stnrv1.StunnerConfig, 1)
conf := make(chan *stnrv1.StunnerConfig, 1)
defer close(conf)

log.Debug("init watcher with nonexistent config file")
Expand Down Expand Up @@ -285,7 +293,7 @@ func TestStunnerConfigFileWatcherMultiVersion(t *testing.T) {
assert.True(t, c2.Auth.Type == "static" || c2.Auth.Type == "ephemeral", "loglevel")
assert.Len(t, c2.Listeners, 1, "listeners len")
assert.Equal(t, "udp", c2.Listeners[0].Name, "listener name")
assert.Equal(t, "turn-udp", c2.Listeners[0].Protocol, "listener proto")
assert.Equal(t, "TURN-UDP", c2.Listeners[0].Protocol, "listener proto")
assert.Equal(t, 3478, c2.Listeners[0].Port, "listener port")
assert.Len(t, c2.Listeners[0].Routes, 1, "routes len")
assert.Equal(t, "echo-server-cluster", c2.Listeners[0].Routes[0], "route name")
Expand All @@ -310,7 +318,7 @@ func TestStunnerConfigFileWatcherMultiVersion(t *testing.T) {
assert.True(t, c2.Auth.Type == "static" || c2.Auth.Type == "ephemeral", "loglevel")
assert.Len(t, c2.Listeners, 1, "listeners len")
assert.Equal(t, "udp", c2.Listeners[0].Name, "listener name")
assert.Equal(t, "turn-udp", c2.Listeners[0].Protocol, "listener proto")
assert.Equal(t, "TURN-UDP", c2.Listeners[0].Protocol, "listener proto")
assert.Equal(t, 3478, c2.Listeners[0].Port, "listener port")
assert.Len(t, c2.Listeners[0].Routes, 1, "routes len")
assert.Equal(t, "echo-server-cluster", c2.Listeners[0].Routes[0], "route name")
Expand Down Expand Up @@ -391,10 +399,10 @@ func TestStunnerConfigPollerMultiVersion(t *testing.T) {
time.Sleep(50 * time.Millisecond)

log.Debug("creating a stunnerd")
stunner := NewStunner(Options{LogLevel: stunnerTestLoglevel, Id: "ns1/tester"})
stunner := NewStunner(Options{LogLevel: stunnerTestLoglevel, Name: "ns1/tester"})

log.Debug("starting watcher")
conf := make(chan stnrv1.StunnerConfig, 1)
conf := make(chan *stnrv1.StunnerConfig, 1)
defer close(conf)

log.Debug("init config poller")
Expand All @@ -408,7 +416,7 @@ func TestStunnerConfigPollerMultiVersion(t *testing.T) {
assert.True(t, c2.Auth.Type == "static" || c2.Auth.Type == "ephemeral", "loglevel")
assert.Len(t, c2.Listeners, 1, "listeners len")
assert.Equal(t, "udp", c2.Listeners[0].Name, "listener name")
assert.Equal(t, "turn-udp", c2.Listeners[0].Protocol, "listener proto")
assert.Equal(t, "TURN-UDP", c2.Listeners[0].Protocol, "listener proto")
assert.Equal(t, 3478, c2.Listeners[0].Port, "listener port")
assert.Len(t, c2.Listeners[0].Routes, 1, "routes len")
assert.Equal(t, "echo-server-cluster", c2.Listeners[0].Routes[0], "route name")
Expand All @@ -427,7 +435,7 @@ func TestStunnerConfigPollerMultiVersion(t *testing.T) {
assert.True(t, c2.Auth.Type == "static" || c2.Auth.Type == "ephemeral", "loglevel")
assert.Len(t, c2.Listeners, 1, "listeners len")
assert.Equal(t, "udp", c2.Listeners[0].Name, "listener name")
assert.Equal(t, "turn-udp", c2.Listeners[0].Protocol, "listener proto")
assert.Equal(t, "TURN-UDP", c2.Listeners[0].Protocol, "listener proto")
assert.Equal(t, 3478, c2.Listeners[0].Port, "listener port")
assert.Len(t, c2.Listeners[0].Routes, 1, "routes len")
assert.Equal(t, "echo-server-cluster", c2.Listeners[0].Routes[0], "route name")
Expand Down
2 changes: 1 addition & 1 deletion handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func TestStunnerAuthServerVNet(t *testing.T) {
})

log.Debug("starting stunnerd")
assert.NoError(t, stunner.Reconcile(c), "starting server")
assert.NoError(t, stunner.Reconcile(&c), "starting server")

log.Debug("creating a client")
lconn, err := v.wan.ListenPacket("udp4", "0.0.0.0:0")
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/v1/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ func (req *AuthConfig) Validate() error {
req.Realm = DefaultRealm
}

if req.Credentials == nil {
req.Credentials = map[string]string{}
}

return nil
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/v1/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ func (req *ClusterConfig) Validate() error {
}
}

if req.Endpoints == nil {
req.Endpoints = []string{}
}

sort.Strings(req.Endpoints)

return nil
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/v1/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ const DefaultMetricsPort int = 8080
const DefaultHealthCheckPort int = 8086

const DefaultConfigDiscoveryAddress = ":13478"
const DefaultEnvVarName = "STUNNER_NAME"
const DefaultEnvVarNamespace = "STUNNER_NAMESPACE"
const DefaultEnvVarNodeName = "STUNNER_NODENAME"
const DefaultEnvVarConfigOrigin = "STUNNER_CONFIG_ORIGIN"
11 changes: 10 additions & 1 deletion pkg/apis/v1/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ func (req *ListenerConfig) Validate() error {
}
}

if req.Routes == nil {
req.Routes = []string{}
}

sort.Strings(req.Routes)
return nil
}
Expand Down Expand Up @@ -102,7 +106,12 @@ func (req *ListenerConfig) String() string {
n = req.Name
}

status = append(status, fmt.Sprintf("turn://0.0.0.0:%d", req.Port))
addr := "0.0.0.0"
if req.Addr != "" && req.Addr != "$STUNNER_ADDR" {
addr = req.Addr
}

status = append(status, fmt.Sprintf("turn://%s:%d", addr, req.Port))

a, p := "-", "-"
if req.PublicAddr != "" {
Expand Down
25 changes: 13 additions & 12 deletions pkg/apis/v1/stunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,37 +67,38 @@ func (req *StunnerConfig) ConfigName() string {
}

// DeepEqual compares two configurations.
func (req *StunnerConfig) DeepEqual(conf Config) bool {
other, ok := conf.(*StunnerConfig)
func (a *StunnerConfig) DeepEqual(conf Config) bool {
b, ok := conf.(*StunnerConfig)
if !ok {
return false
}

if req.ApiVersion != other.ApiVersion {
if a.ApiVersion != b.ApiVersion {
return false
}
if !req.Admin.DeepEqual(&other.Admin) {

if !a.Admin.DeepEqual(&b.Admin) {
return false
}
if !req.Auth.DeepEqual(&other.Auth) {

if !a.Auth.DeepEqual(&b.Auth) {
return false
}

if len(req.Listeners) != len(other.Listeners) {
if len(a.Listeners) != len(b.Listeners) {
return false
}
for i := range req.Listeners {
if !req.Listeners[i].DeepEqual(&other.Listeners[i]) {
for i := range a.Listeners {
if !a.Listeners[i].DeepEqual(&b.Listeners[i]) {
return false
}
}

if len(req.Clusters) != len(other.Clusters) {
if len(a.Clusters) != len(b.Clusters) {
return false
}

for i := range req.Clusters {
if !req.Clusters[i].DeepEqual(&other.Clusters[i]) {
for i := range a.Clusters {
if !a.Clusters[i].DeepEqual(&b.Clusters[i]) {
return false
}
}
Expand Down
Loading

0 comments on commit 60021e7

Please sign in to comment.