Skip to content

Commit

Permalink
chore: Implement quota handler stub
Browse files Browse the repository at this point in the history
  • Loading branch information
rg0now committed Dec 10, 2024
1 parent 835c1b2 commit f03fa71
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 1 deletion.
29 changes: 29 additions & 0 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,35 @@ func (s *Stunner) NewStatusHandler() object.StatusHandler {
return func() stnrv1.Status { return s.Status() }
}

// Quota handler
type QuotaHandler interface {
QuotaHandler() turn.QuotaHandler
}

var quotaHandlerConstructor = newQuotaHandlerStub

// NewUserQuotaHandler creates a quota handler that defaults to a stub.
func (s *Stunner) NewQuotaHandler() QuotaHandler {
return quotaHandlerConstructor(s)
}

type quotaHandlerStub struct {
quotaHandler turn.QuotaHandler
}

func (q *quotaHandlerStub) QuotaHandler() turn.QuotaHandler {
return q.quotaHandler
}

func newQuotaHandlerStub(_ *Stunner) QuotaHandler {
return &quotaHandlerStub{
quotaHandler: func(_, _ string, _ net.Addr) (ok bool) {
return true
},
}
}

// Event handlers
var eventHandlerConstructor = newEventHandlerStub

// NewEventHandler creates a set of callbcks for tracking the lifecycle of TURN allocations.
Expand Down
5 changes: 5 additions & 0 deletions internal/object/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Admin struct {
MetricsEndpoint, HealthCheckEndpoint string
metricsServer, healthCheckServer *http.Server
health *http.ServeMux
quota int
LicenseManager licensecfg.ConfigManager
licenseConfig *stnrv1.LicenseConfig
log logging.LeveledLogger
Expand Down Expand Up @@ -124,6 +125,8 @@ func (a *Admin) Reconcile(conf stnrv1.Config) error {
return err
}

a.quota = req.UserQuota

a.LicenseManager.Reconcile(req.LicenseConfig)
a.licenseConfig = req.LicenseConfig

Expand Down Expand Up @@ -153,6 +156,7 @@ func (a *Admin) GetConfig() stnrv1.Config {
LogLevel: a.LogLevel,
MetricsEndpoint: a.MetricsEndpoint,
HealthCheckEndpoint: &h,
UserQuota: a.quota,
LicenseConfig: a.licenseConfig,
}
}
Expand Down Expand Up @@ -187,6 +191,7 @@ func (a *Admin) Status() stnrv1.Status {
LogLevel: a.LogLevel,
MetricsEndpoint: a.MetricsEndpoint,
HealthCheckEndpoint: a.HealthCheckEndpoint,
UserQuota: fmt.Sprintf("%d", a.quota),
LicensingInfo: a.LicenseManager.Status(),
}
return &s
Expand Down
12 changes: 12 additions & 0 deletions pkg/apis/v1/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ type AdminConfig struct {
// health-checking at `http://0.0.0.0:8086`. Set to a pointer to an empty string to disable
// health-checking.
HealthCheckEndpoint *string `json:"healthcheck_endpoint,omitempty"`
// UserQuota defines the number of permitted TURN allocatoins per username. Affects
// allocation created on any listener. Default is 0, meaning no quota is enforced.
UserQuota int `json:"user_quota,omitempty"`
// LicenseConfig describes the licensing info to be used to check subscription status with
// the license server.
LicenseConfig *LicenseConfig `json:"license_config,omitempty"`
Expand Down Expand Up @@ -69,6 +72,10 @@ func (req *AdminConfig) Validate() error {
}
}

if req.UserQuota < 0 {
req.UserQuota = 0
}

return nil
}

Expand Down Expand Up @@ -104,6 +111,9 @@ func (req *AdminConfig) String() string {
if req.HealthCheckEndpoint != nil {
status = append(status, fmt.Sprintf("health-check=%q", *req.HealthCheckEndpoint))
}
if req.UserQuota > 0 {
status = append(status, fmt.Sprintf("quota=%d", req.UserQuota))
}
status = append(status, fmt.Sprintf("license_info=%s", LicensingStatus(req.LicenseConfig)))

return fmt.Sprintf("admin:{%s}", strings.Join(status, ","))
Expand Down Expand Up @@ -131,6 +141,7 @@ type AdminStatus struct {
LogLevel string `json:"loglevel,omitempty"`
MetricsEndpoint string `json:"metrics_endpoint,omitempty"`
HealthCheckEndpoint string `json:"healthcheck_endpoint,omitempty"`
UserQuota string `json:"quota,omitempty"`
LicensingInfo string `json:"licensing_info,omitempty"`
}

Expand All @@ -146,6 +157,7 @@ func (a *AdminStatus) String() string {
if a.HealthCheckEndpoint != "" {
status = append(status, fmt.Sprintf("health-check=%q", a.HealthCheckEndpoint))
}
status = append(status, fmt.Sprintf("quota=%s", a.UserQuota))
if a.LicensingInfo != "" {
status = append(status, fmt.Sprintf("license-info=%s", a.LicensingInfo))
}
Expand Down
30 changes: 30 additions & 0 deletions reconcile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1763,6 +1763,36 @@ var testReconcileDefault = []StunnerReconcileTestConfig{
net.ParseIP("3.0.0.0")), "route to 3.0.0.0 fails")
},
},
{
name: "reconcile-test: reconcile user quota",
config: stnrv1.StunnerConfig{
ApiVersion: stnrv1.ApiVersion,
Admin: stnrv1.AdminConfig{
UserQuota: 12,
LogLevel: stunnerTestLoglevel,
},
Auth: stnrv1.AuthConfig{
Credentials: map[string]string{
"username": "user",
"password": "pass",
},
},
Listeners: []stnrv1.ListenerConfig{},
Clusters: []stnrv1.ClusterConfig{},
},
tester: func(t *testing.T, s *Stunner, err error) {
assert.NoError(t, err, err)

a := s.GetAdmin()
assert.NotNil(t, a, "admin")

c := a.GetConfig()
assert.NotNil(t, c, "admin-getconfig")
ca, ok := c.(*stnrv1.AdminConfig)
assert.True(t, ok, "adminconfig cast")
assert.Equal(t, 12, ca.UserQuota, "quota")
},
},
}

// start with default config and then reconcile with the given config
Expand Down
3 changes: 2 additions & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ func (s *Stunner) StartServer(l *object.Listener) error {
t, err := turn.NewServer(turn.ServerConfig{
Realm: s.GetRealm(),
AuthHandler: s.NewAuthHandler(),
EventHandlers: s.NewEventHandler(),
EventHandlers: s.eventHandlers,
QuotaHandler: s.quotaHandler.QuotaHandler(),
PacketConnConfigs: pConns,
ListenerConfigs: lConns,
LoggerFactory: logger.NewRateLimitedLoggerFactory(s.logger, LogRateLimit, LogBurst),
Expand Down
5 changes: 5 additions & 0 deletions stunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/pion/logging"
"github.com/pion/transport/v3"
"github.com/pion/transport/v3/stdnet"
"github.com/pion/turn/v4"

"github.com/l7mp/stunner/internal/manager"
"github.com/l7mp/stunner/internal/object"
Expand All @@ -32,6 +33,8 @@ type Stunner struct {
telemetry *telemetry.Telemetry
logger logger.LoggerFactory
log logging.LeveledLogger
eventHandlers turn.EventHandlers
quotaHandler QuotaHandler
net transport.Net
ready, shutdown bool
}
Expand Down Expand Up @@ -101,6 +104,8 @@ func NewStunner(options Options) *Stunner {
object.NewListenerFactory(vnet, s.NewRealmHandler(), logger), logger)
s.clusterManager = manager.NewManager("cluster-manager",
object.NewClusterFactory(r, logger), logger)
s.eventHandlers = s.NewEventHandler()
s.quotaHandler = s.NewQuotaHandler()

telemetryCallbacks := telemetry.Callbacks{
GetAllocationCount: func() int64 { return s.GetActiveConnections() },
Expand Down

0 comments on commit f03fa71

Please sign in to comment.