Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cmd/lk/perf.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ var (
},
},
{
Name: "agent-load-test",
Usage: "Run load tests for a running agent",
Action: agentLoadTest,
Name: "agent-load-test",
Usage: "Run load tests for a running agent",
Action: agentLoadTest,
DisableSliceFlagSeparator: true,
Flags: []cli.Flag{
&cli.IntFlag{
Name: "rooms",
Expand Down
11 changes: 6 additions & 5 deletions cmd/lk/room.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,12 @@ var (
ArgsUsage: "ROOM_NAME",
},
{
Name: "join",
Usage: "Joins a room as a participant",
UsageText: "lk room join [OPTIONS] ROOM_NAME",
Action: joinRoom,
ArgsUsage: "ROOM_NAME",
Name: "join",
Usage: "Joins a room as a participant",
UsageText: "lk room join [OPTIONS] ROOM_NAME",
Action: joinRoom,
ArgsUsage: "ROOM_NAME",
DisableSliceFlagSeparator: true,
Flags: []cli.Flag{
optional(identityFlag),
optional(roomFlag),
Expand Down
60 changes: 36 additions & 24 deletions cmd/lk/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"maps"
"os"
"slices"
"strings"
"time"

"charm.land/huh/v2"
Expand Down Expand Up @@ -66,9 +67,10 @@ var (
Before: loadProjectConfig,
Commands: []*cli.Command{
{
Name: "create",
Usage: "Creates an access token",
Action: createToken,
Name: "create",
Usage: "Creates an access token",
Action: createToken,
DisableSliceFlagSeparator: true,
Flags: []cli.Flag{
optional(roomFlag),
optional(identityFlag),
Expand Down Expand Up @@ -157,10 +159,11 @@ var (

// Deprecated commands kept for compatibility
{
Hidden: true, // deprecated: use `token create`
Name: "create-token",
Usage: "Creates an access token",
Action: createToken,
Hidden: true, // deprecated: use `token create`
Name: "create-token",
Usage: "Creates an access token",
Action: createToken,
DisableSliceFlagSeparator: true,
Flags: []cli.Flag{
optional(roomFlag),
&cli.BoolFlag{
Expand Down Expand Up @@ -241,6 +244,29 @@ var (
}
)

// parseAllowSources converts --allow-source values to track sources. The token commands disable the
// slice flag separator so that --attribute values keep their commas, so split comma-separated sources here.
func parseAllowSources(values []string) ([]livekit.TrackSource, error) {
var sources []livekit.TrackSource
for _, value := range values {
for _, s := range strings.Split(value, ",") {
switch s {
case "camera":
sources = append(sources, livekit.TrackSource_CAMERA)
case "microphone":
sources = append(sources, livekit.TrackSource_MICROPHONE)
case "screen_share":
sources = append(sources, livekit.TrackSource_SCREEN_SHARE)
case "screen_share_audio":
sources = append(sources, livekit.TrackSource_SCREEN_SHARE_AUDIO)
default:
return nil, fmt.Errorf("invalid source: %s", s)
}
}
}
return sources, nil
}

func createToken(ctx context.Context, c *cli.Command) error {
tokenOnly := c.Bool("token-only")
jsonOutput := c.Bool("json")
Expand Down Expand Up @@ -324,23 +350,9 @@ func createToken(ctx context.Context, c *cli.Command) error {
hasPerms = true
}
if c.IsSet("allow-source") {
sourcesStr := c.StringSlice("allow-source")
sources := make([]livekit.TrackSource, 0, len(sourcesStr))
for _, s := range sourcesStr {
var source livekit.TrackSource
switch s {
case "camera":
source = livekit.TrackSource_CAMERA
case "microphone":
source = livekit.TrackSource_MICROPHONE
case "screen_share":
source = livekit.TrackSource_SCREEN_SHARE
case "screen_share_audio":
source = livekit.TrackSource_SCREEN_SHARE_AUDIO
default:
return fmt.Errorf("invalid source: %s", s)
}
sources = append(sources, source)
sources, err := parseAllowSources(c.StringSlice("allow-source"))
if err != nil {
return err
}
grant.SetCanPublishSources(sources)
}
Expand Down
61 changes: 61 additions & 0 deletions cmd/lk/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"

"github.com/livekit/protocol/auth"
"github.com/livekit/protocol/livekit"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v3"
Expand Down Expand Up @@ -105,3 +106,63 @@ func slicesContains(items []string, item string) bool {
}
return false
}

// collectAttributeCommands returns every command in the tree that defines an "attribute" string slice flag.
func collectAttributeCommands(commands []*cli.Command) []*cli.Command {
var found []*cli.Command
for _, cmd := range commands {
if cmd == nil {
continue
}
for _, flag := range cmd.Flags {
if f, ok := flag.(*cli.StringSliceFlag); ok && f.Name == "attribute" {
found = append(found, cmd)
break
}
}
found = append(found, collectAttributeCommands(cmd.Commands)...)
}
return found
}

func TestAttributeFlagKeepsCommasInValues(t *testing.T) {
var cmds []*cli.Command
cmds = append(cmds, collectAttributeCommands(TokenCommands)...)
cmds = append(cmds, collectAttributeCommands(RoomCommands)...)
cmds = append(cmds, collectAttributeCommands(PerfCommands)...)
require.NotEmpty(t, cmds)

for _, c := range cmds {
t.Run(c.Name, func(t *testing.T) {
var got map[string]string
var parseErr error
// Run the real command's flags and slice separator setting with a stand-in action.
cmd := &cli.Command{
Name: c.Name,
Flags: c.Flags,
DisableSliceFlagSeparator: c.DisableSliceFlagSeparator,
Action: func(_ context.Context, cmd *cli.Command) error {
got, parseErr = parseKeyValuePairs(cmd, "attribute")
return nil
},
}
err := cmd.Run(context.Background(), []string{c.Name, "--attribute", "tags=a,b", "--attribute", "mode=x"})
require.NoError(t, err)
require.NoError(t, parseErr)
assert.Equal(t, map[string]string{"tags": "a,b", "mode": "x"}, got)
})
}
}

func TestParseAllowSources(t *testing.T) {
sources, err := parseAllowSources([]string{"camera,microphone", "screen_share"})
require.NoError(t, err)
assert.Equal(t, []livekit.TrackSource{
livekit.TrackSource_CAMERA,
livekit.TrackSource_MICROPHONE,
livekit.TrackSource_SCREEN_SHARE,
}, sources)

_, err = parseAllowSources([]string{"camera,video"})
require.Error(t, err)
}
Loading