diff --git a/cmd/server/commands.go b/cmd/server/commands.go index 7fa0c4e9f..dfbd0da88 100644 --- a/cmd/server/commands.go +++ b/cmd/server/commands.go @@ -15,6 +15,7 @@ package main import ( + "context" "fmt" "os" "strconv" @@ -23,7 +24,7 @@ import ( "github.com/dustin/go-humanize" "github.com/olekukonko/tablewriter" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "gopkg.in/yaml.v3" "github.com/livekit/protocol/auth" @@ -36,7 +37,7 @@ import ( "github.com/livekit/livekit-server/pkg/service" ) -func generateKeys(_ *cli.Context) error { +func generateKeys(_ context.Context, _ *cli.Command) error { apiKey := guid.New(utils.APIKeyPrefix) secret := utils.RandomSecret() fmt.Println("API Key: ", apiKey) @@ -44,7 +45,7 @@ func generateKeys(_ *cli.Context) error { return nil } -func printPorts(c *cli.Context) error { +func printPorts(_ context.Context, c *cli.Command) error { conf, err := getConfig(c) if err != nil { return err @@ -85,17 +86,17 @@ func printPorts(c *cli.Context) error { return nil } -func helpVerbose(c *cli.Context) error { +func helpVerbose(_ context.Context, c *cli.Command) error { generatedFlags, err := config.GenerateCLIFlags(baseFlags, false) if err != nil { return err } - c.App.Flags = append(baseFlags, generatedFlags...) + c.Flags = append(baseFlags, generatedFlags...) return cli.ShowAppHelp(c) } -func createToken(c *cli.Context) error { +func createToken(_ context.Context, c *cli.Command) error { room := c.String("room") identity := c.String("identity") @@ -161,7 +162,7 @@ func createToken(c *cli.Context) error { return nil } -func listNodes(c *cli.Context) error { +func listNodes(_ context.Context, c *cli.Command) error { conf, err := getConfig(c) if err != nil { return err diff --git a/cmd/server/main.go b/cmd/server/main.go index 6cdfba638..7bf350f8d 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -15,6 +15,7 @@ package main import ( + "context" "fmt" "math/rand" "net" @@ -25,7 +26,7 @@ import ( "syscall" "time" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "github.com/livekit/livekit-server/pkg/rtc" "github.com/livekit/livekit-server/pkg/telemetry/prometheus" @@ -49,7 +50,7 @@ var baseFlags = []cli.Flag{ &cli.StringFlag{ Name: "config-body", Usage: "LiveKit config in YAML, typically passed in as an environment var in a container", - EnvVars: []string{"LIVEKIT_CONFIG"}, + Sources: cli.EnvVars("LIVEKIT_CONFIG"), }, &cli.StringFlag{ Name: "key-file", @@ -58,42 +59,42 @@ var baseFlags = []cli.Flag{ &cli.StringFlag{ Name: "keys", Usage: "api keys (key: secret\\n)", - EnvVars: []string{"LIVEKIT_KEYS"}, + Sources: cli.EnvVars("LIVEKIT_KEYS"), }, &cli.StringFlag{ Name: "region", Usage: "region of the current node. Used by regionaware node selector", - EnvVars: []string{"LIVEKIT_REGION"}, + Sources: cli.EnvVars("LIVEKIT_REGION"), }, &cli.StringFlag{ Name: "node-ip", Usage: "IP address of the current node, used to advertise to clients. Automatically determined by default", - EnvVars: []string{"NODE_IP"}, + Sources: cli.EnvVars("NODE_IP"), }, &cli.StringFlag{ Name: "udp-port", Usage: "UDP port(s) to use for WebRTC traffic", - EnvVars: []string{"UDP_PORT"}, + Sources: cli.EnvVars("UDP_PORT"), }, &cli.StringFlag{ Name: "redis-host", Usage: "host (incl. port) to redis server", - EnvVars: []string{"REDIS_HOST"}, + Sources: cli.EnvVars("REDIS_HOST"), }, &cli.StringFlag{ Name: "redis-password", Usage: "password to redis", - EnvVars: []string{"REDIS_PASSWORD"}, + Sources: cli.EnvVars("REDIS_PASSWORD"), }, &cli.StringFlag{ Name: "turn-cert", Usage: "tls cert file for TURN server", - EnvVars: []string{"LIVEKIT_TURN_CERT"}, + Sources: cli.EnvVars("LIVEKIT_TURN_CERT"), }, &cli.StringFlag{ Name: "turn-key", Usage: "tls key file for TURN server", - EnvVars: []string{"LIVEKIT_TURN_KEY"}, + Sources: cli.EnvVars("LIVEKIT_TURN_KEY"), }, // debugging flags &cli.StringFlag{ @@ -127,7 +128,7 @@ func main() { fmt.Println(err) } - app := &cli.App{ + cmd := &cli.Command{ Name: "livekit-server", Usage: "High performance WebRTC server", Description: "run without subcommands to start the server", @@ -182,12 +183,12 @@ func main() { Version: version.Version, } - if err := app.Run(os.Args); err != nil { + if err := cmd.Run(context.Background(), os.Args); err != nil { fmt.Println(err) } } -func getConfig(c *cli.Context) (*config.Config, error) { +func getConfig(c *cli.Command) (*config.Config, error) { confString, err := getConfigString(c.String("config"), c.String("config-body")) if err != nil { return nil, err @@ -242,7 +243,7 @@ func getConfig(c *cli.Context) (*config.Config, error) { return conf, nil } -func startServer(c *cli.Context) error { +func startServer(_ context.Context, c *cli.Command) error { conf, err := getConfig(c) if err != nil { return err diff --git a/go.mod b/go.mod index ba19917e4..488c17c15 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,6 @@ require ( github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 github.com/twitchtv/twirp v8.1.3+incompatible github.com/ua-parser/uap-go v0.0.0-20250126222208-a52596c19dff - github.com/urfave/cli/v2 v2.27.5 github.com/urfave/negroni/v3 v3.1.1 go.uber.org/atomic v1.11.0 go.uber.org/multierr v1.11.0 @@ -79,7 +78,6 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/continuity v0.4.3 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/cli v26.1.4+incompatible // indirect @@ -124,14 +122,13 @@ require ( github.com/prometheus/common v0.64.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/urfave/cli/v3 v3.3.8 github.com/wlynxg/anet v0.0.5 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.uber.org/zap/exp v0.3.0 // indirect golang.org/x/crypto v0.39.0 // indirect diff --git a/go.sum b/go.sum index 24e908dc4..78bf2b34d 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,6 @@ github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao= github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= -github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -287,8 +285,6 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= @@ -316,8 +312,8 @@ github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJX github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/ua-parser/uap-go v0.0.0-20250126222208-a52596c19dff h1:NwMEGwb7JJ8wPjT8OPKP5hO1Xz6AQ7Z00+GLSJfW21s= github.com/ua-parser/uap-go v0.0.0-20250126222208-a52596c19dff/go.mod h1:BUbeWZiieNxAuuADTBNb3/aeje6on3DhU3rpWsQSB1E= -github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= -github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/urfave/cli/v3 v3.3.8 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E= +github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= github.com/urfave/negroni/v3 v3.1.1 h1:6MS4nG9Jk/UuCACaUlNXCbiKa0ywF9LXz5dGu09v8hw= github.com/urfave/negroni/v3 v3.1.1/go.mod h1:jWvnX03kcSjDBl/ShB0iHvx5uOs7mAzZXW+JvJ5XYAs= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= @@ -329,8 +325,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/pkg/config/config.go b/pkg/config/config.go index 9ccc51c4b..dd38a1376 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -23,7 +23,7 @@ import ( "github.com/mitchellh/go-homedir" "github.com/pkg/errors" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "gopkg.in/yaml.v3" "github.com/livekit/livekit-server/pkg/metric" @@ -402,7 +402,7 @@ var DefaultConfig = Config{ NodeStats: DefaultNodeStatsConfig, } -func NewConfig(confString string, strictMode bool, c *cli.Context, baseFlags []cli.Flag) (*Config, error) { +func NewConfig(confString string, strictMode bool, c *cli.Command, baseFlags []cli.Flag) (*Config, error) { // start with defaults marshalled, err := yaml.Marshal(&DefaultConfig) if err != nil { @@ -600,56 +600,56 @@ func GenerateCLIFlags(existingFlags []cli.Flag, hidden bool) ([]cli.Flag, error) case reflect.Bool: flag = &cli.BoolFlag{ Name: name, - EnvVars: []string{envVar}, + Sources: cli.EnvVars(envVar), Usage: generatedCLIFlagUsage, Hidden: hidden, } case reflect.String: flag = &cli.StringFlag{ Name: name, - EnvVars: []string{envVar}, + Sources: cli.EnvVars(envVar), Usage: generatedCLIFlagUsage, Hidden: hidden, } case reflect.Int, reflect.Int32: flag = &cli.IntFlag{ Name: name, - EnvVars: []string{envVar}, + Sources: cli.EnvVars(envVar), Usage: generatedCLIFlagUsage, Hidden: hidden, } case reflect.Int64: flag = &cli.Int64Flag{ Name: name, - EnvVars: []string{envVar}, + Sources: cli.EnvVars(envVar), Usage: generatedCLIFlagUsage, Hidden: hidden, } case reflect.Uint8, reflect.Uint16, reflect.Uint32: flag = &cli.UintFlag{ Name: name, - EnvVars: []string{envVar}, + Sources: cli.EnvVars(envVar), Usage: generatedCLIFlagUsage, Hidden: hidden, } case reflect.Uint64: flag = &cli.Uint64Flag{ Name: name, - EnvVars: []string{envVar}, + Sources: cli.EnvVars(envVar), Usage: generatedCLIFlagUsage, Hidden: hidden, } case reflect.Float32: flag = &cli.Float64Flag{ Name: name, - EnvVars: []string{envVar}, + Sources: cli.EnvVars(envVar), Usage: generatedCLIFlagUsage, Hidden: hidden, } case reflect.Float64: flag = &cli.Float64Flag{ Name: name, - EnvVars: []string{envVar}, + Sources: cli.EnvVars(envVar), Usage: generatedCLIFlagUsage, Hidden: hidden, } @@ -672,13 +672,12 @@ func GenerateCLIFlags(existingFlags []cli.Flag, hidden bool) ([]cli.Flag, error) return flags, nil } -func (conf *Config) updateFromCLI(c *cli.Context, baseFlags []cli.Flag) error { +func (conf *Config) updateFromCLI(c *cli.Command, baseFlags []cli.Flag) error { generatedFlagNames := conf.ToCLIFlagNames(baseFlags) - for _, flag := range c.App.Flags { + for _, flag := range c.Flags { flagName := flag.Names()[0] - // the `c.App.Name != "test"` check is needed because `c.IsSet(...)` is always false in unit tests - if !c.IsSet(flagName) && c.App.Name != "test" { + if !c.IsSet(flagName) { continue } @@ -687,34 +686,16 @@ func (conf *Config) updateFromCLI(c *cli.Context, baseFlags []cli.Flag) error { continue } - kind := configValue.Kind() - if kind == reflect.Ptr { - // instantiate value to be set + if configValue.Kind() == reflect.Ptr { configValue.Set(reflect.New(configValue.Type().Elem())) - - kind = configValue.Type().Elem().Kind() configValue = configValue.Elem() } - switch kind { - case reflect.Bool: - configValue.SetBool(c.Bool(flagName)) - case reflect.String: - configValue.SetString(c.String(flagName)) - case reflect.Int, reflect.Int32, reflect.Int64: - configValue.SetInt(c.Int64(flagName)) - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - configValue.SetUint(c.Uint64(flagName)) - case reflect.Float32: - configValue.SetFloat(c.Float64(flagName)) - case reflect.Float64: - configValue.SetFloat(c.Float64(flagName)) - // case reflect.Slice: - // // TODO - // case reflect.Map: - // // TODO - default: - return fmt.Errorf("unsupported generated cli flag type for config: %s is a %s", flagName, kind.String()) + value := reflect.ValueOf(c.Value(flagName)) + if value.CanConvert(configValue.Type()) { + configValue.Set(value.Convert(configValue.Type())) + } else { + return fmt.Errorf("unsupported generated cli flag type for config: %s (expected %s, got %s)", flagName, configValue.Type(), value.Type()) } } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index cf5fb23fc..da2b2e692 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -15,11 +15,10 @@ package config import ( - "flag" "testing" "github.com/stretchr/testify/require" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" "github.com/livekit/livekit-server/pkg/config/configtest" ) @@ -53,19 +52,17 @@ func TestGeneratedFlags(t *testing.T) { generatedFlags, err := GenerateCLIFlags(nil, false) require.NoError(t, err) - app := cli.NewApp() - app.Name = "test" - app.Flags = append(app.Flags, generatedFlags...) + c := &cli.Command{} + c.Name = "test" + c.Flags = append(c.Flags, generatedFlags...) - set := flag.NewFlagSet("test", 0) - set.Bool("rtc.use_ice_lite", true, "") // bool - set.String("redis.address", "localhost:6379", "") // string - set.Uint("prometheus.port", 9999, "") // uint32 - set.Bool("rtc.allow_tcp_fallback", true, "") // pointer - set.Bool("rtc.reconnect_on_publication_error", true, "") // pointer - set.Bool("rtc.reconnect_on_subscription_error", false, "") // pointer + c.Set("rtc.use_ice_lite", "true") + c.Set("redis.address", "localhost:6379") + c.Set("prometheus.port", "9999") + c.Set("rtc.allow_tcp_fallback", "true") + c.Set("rtc.reconnect_on_publication_error", "true") + c.Set("rtc.reconnect_on_subscription_error", "false") - c := cli.NewContext(app, set, nil) conf, err := NewConfig("", true, c, nil) require.NoError(t, err)