Skip to content

Commit 5f0b9ce

Browse files
authored
feat(lk): respect config files globally (#569)
- now recognizes the `--config` flag globally to point to toml - if not otherwise specified, uses this project for commands - support new `livekit.toml` format, with backwards compat
1 parent a8a6744 commit 5f0b9ce

File tree

6 files changed

+224
-164
lines changed

6 files changed

+224
-164
lines changed

cmd/lk/agent.go

Lines changed: 59 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -31,32 +31,22 @@ import (
3131
"github.com/urfave/cli/v3"
3232

3333
"github.com/livekit/livekit-cli/v2/pkg/agentfs"
34+
"github.com/livekit/livekit-cli/v2/pkg/config"
3435
"github.com/livekit/livekit-cli/v2/pkg/util"
3536
lkproto "github.com/livekit/protocol/livekit"
3637
"github.com/livekit/protocol/logger"
3738
lksdk "github.com/livekit/server-sdk-go/v2"
3839
)
3940

4041
const (
41-
clientDefaults_CPU = "1"
42-
clientDefaults_Replicas = 1
43-
clientDefaults_MaxReplicas = 10
44-
cloudAgentsBetaSignupURL = "https://forms.gle/GkGNNTiMt2qyfnu78"
42+
cloudAgentsBetaSignupURL = "https://forms.gle/GkGNNTiMt2qyfnu78"
4543
)
4644

4745
var (
48-
tomlFlag = &cli.StringFlag{
49-
Name: "toml",
50-
Usage: "TOML `FILE` to use in the working directory",
51-
Value: agentfs.AgentTOMLFile,
52-
Destination: &tomlFilename,
53-
Required: false,
54-
}
55-
5646
nameFlag = func(required bool) *cli.StringFlag {
5747
return &cli.StringFlag{
5848
Name: "name",
59-
Usage: fmt.Sprintf("`NAME` of the agent. If unset, and the %s file is present, will use the name found there.", agentfs.AgentTOMLFile),
49+
Usage: fmt.Sprintf("`NAME` of the agent. If unset, and the %s file is present, will use the name found there.", config.LiveKitTOMLFile),
6050
Required: required,
6151
}
6252
}
@@ -108,18 +98,16 @@ var (
10898
Required: false,
10999
Value: false,
110100
},
111-
tomlFlag,
112101
},
113102
ArgsUsage: "[working-dir]",
114103
},
115104
{
116105
Name: "config",
117-
Usage: fmt.Sprintf("Creates a %s in the working directory for an existing agent.", agentfs.AgentTOMLFile),
106+
Usage: fmt.Sprintf("Creates a %s in the working directory for an existing agent.", config.LiveKitTOMLFile),
118107
Before: createAgentClient,
119108
Action: createAgentConfig,
120109
Flags: []cli.Flag{
121110
nameFlag(false),
122-
tomlFlag,
123111
},
124112
ArgsUsage: "[working-dir]",
125113
},
@@ -131,7 +119,6 @@ var (
131119
Flags: []cli.Flag{
132120
secretsFlag,
133121
secretsFileFlag,
134-
tomlFlag,
135122
},
136123
ArgsUsage: "[working-dir]",
137124
},
@@ -142,7 +129,6 @@ var (
142129
Action: getAgentStatus,
143130
Flags: []cli.Flag{
144131
nameFlag(false),
145-
tomlFlag,
146132
},
147133
ArgsUsage: "[working-dir]",
148134
},
@@ -154,7 +140,6 @@ var (
154140
Flags: []cli.Flag{
155141
secretsFlag,
156142
secretsFileFlag,
157-
tomlFlag,
158143
},
159144
ArgsUsage: "[working-dir]",
160145
},
@@ -171,7 +156,6 @@ var (
171156
Required: true,
172157
},
173158
nameFlag(false),
174-
tomlFlag,
175159
},
176160
ArgsUsage: "[working-dir]",
177161
},
@@ -184,7 +168,6 @@ var (
184168
Flags: []cli.Flag{
185169
nameFlag(false),
186170
logTypeFlag,
187-
tomlFlag,
188171
},
189172
ArgsUsage: "[working-dir]",
190173
},
@@ -196,7 +179,6 @@ var (
196179
Aliases: []string{"destroy"},
197180
Flags: []cli.Flag{
198181
nameFlag(false),
199-
tomlFlag,
200182
},
201183
ArgsUsage: "[working-dir]",
202184
},
@@ -207,7 +189,6 @@ var (
207189
Action: listAgentVersions,
208190
Flags: []cli.Flag{
209191
nameFlag(false),
210-
tomlFlag,
211192
},
212193
ArgsUsage: "[working-dir]",
213194
},
@@ -227,7 +208,6 @@ var (
227208
Action: listAgentSecrets,
228209
Flags: []cli.Flag{
229210
nameFlag(false),
230-
tomlFlag,
231211
},
232212
ArgsUsage: "[working-dir]",
233213
},
@@ -240,7 +220,6 @@ var (
240220
secretsFlag,
241221
secretsFileFlag,
242222
nameFlag(false),
243-
tomlFlag,
244223
&cli.BoolFlag{
245224
Name: "overwrite",
246225
Usage: "If set, will overwrite existing secrets",
@@ -255,8 +234,6 @@ var (
255234
}
256235
subdomainPattern = regexp.MustCompile(`^(?:https?|wss?)://([^.]+)\.`)
257236
agentsClient *lksdk.AgentClient
258-
tomlFilename = agentfs.AgentTOMLFile
259-
workingDir = "."
260237
ignoredSecrets = []string{
261238
"LIVEKIT_API_KEY",
262239
"LIVEKIT_API_SECRET",
@@ -266,7 +243,7 @@ var (
266243

267244
func createAgentClient(ctx context.Context, cmd *cli.Command) (context.Context, error) {
268245
var err error
269-
var agentConfig *agentfs.AgentTOML
246+
var lkConfig *config.LiveKitTOML
270247

271248
if _, err := requireProject(ctx, cmd); err != nil {
272249
return ctx, err
@@ -277,14 +254,17 @@ func createAgentClient(ctx context.Context, cmd *cli.Command) (context.Context,
277254
}
278255

279256
// Verify that the project and agent config match, if it exists.
280-
agentConfig, configExists, err := agentfs.LoadTomlFile(workingDir, tomlFilename)
257+
lkConfig, configExists, err := config.LoadTomlFile(workingDir, tomlFilename)
258+
if err != nil {
259+
fmt.Println(err.Error())
260+
}
281261
if configExists {
282262
projectSubdomainMatch := subdomainPattern.FindStringSubmatch(project.URL)
283263
if len(projectSubdomainMatch) < 2 {
284264
return nil, fmt.Errorf("invalid project URL [%s]", project.URL)
285265
}
286-
if projectSubdomainMatch[1] != agentConfig.ProjectSubdomain {
287-
return nil, fmt.Errorf("project does not match agent subdomain [%s]", agentConfig.ProjectSubdomain)
266+
if projectSubdomainMatch[1] != lkConfig.Project.Subdomain {
267+
return nil, fmt.Errorf("project does not match agent subdomain [%s]", lkConfig.Project.Subdomain)
288268
}
289269
} else {
290270
if !errors.Is(err, os.ErrNotExist) {
@@ -320,18 +300,18 @@ func createAgent(ctx context.Context, cmd *cli.Command) error {
320300
}
321301

322302
logger.Debugw("Creating agent", "working-dir", workingDir)
323-
agentConfig, configExists, err := agentfs.LoadTomlFile(workingDir, tomlFilename)
303+
lkConfig, configExists, err := config.LoadTomlFile(workingDir, tomlFilename)
324304
if err != nil && configExists {
325305
return err
326306
}
327307

328308
name := cmd.String("name")
329309
silent := cmd.Bool("silent")
330310

331-
if configExists {
311+
if configExists && lkConfig.Agent != nil {
332312
// If name was set via command line, it must match the name in the config.
333-
if name != "" && agentConfig.Name != name {
334-
return fmt.Errorf("agent name passed in command line: [%s] does not match name in [%s]: [%s]", name, tomlFilename, agentConfig.Name)
313+
if name != "" && lkConfig.Agent.Name != name {
314+
return fmt.Errorf("agent name passed in command line: [%s] does not match name in [%s]: [%s]", name, tomlFilename, lkConfig.Agent.Name)
335315
}
336316

337317
if !silent {
@@ -359,30 +339,25 @@ func createAgent(ctx context.Context, cmd *cli.Command) error {
359339
}
360340
defer f.Close()
361341

362-
agentConfig = &agentfs.AgentTOML{
363-
ProjectSubdomain: subdomainMatches[1],
364-
Name: name,
365-
CPU: clientDefaults_CPU,
366-
Replicas: clientDefaults_Replicas,
367-
MaxReplicas: clientDefaults_MaxReplicas,
368-
}
342+
lkConfig = config.NewLiveKitTOML(subdomainMatches[1]).
343+
WithDefaultAgent(name)
369344

370345
encoder := toml.NewEncoder(f)
371-
if err := encoder.Encode(agentConfig); err != nil {
346+
if err := encoder.Encode(lkConfig); err != nil {
372347
return fmt.Errorf("error encoding TOML: %w", err)
373348
}
374349
fmt.Printf("Creating config file [%s]\n", util.Accented(tomlFilename))
375350
}
376351

377352
if name == "" {
378-
if agentConfig.Name == "" {
353+
if lkConfig.Agent.Name == "" {
379354
return fmt.Errorf("name is required")
380355
}
381356
} else {
382-
agentConfig.Name = name
357+
lkConfig.Agent.Name = name
383358
}
384359
if !silent {
385-
fmt.Printf("Creating agent [%s]\n", util.Accented(agentConfig.Name))
360+
fmt.Printf("Creating agent [%s]\n", util.Accented(lkConfig.Agent.Name))
386361
}
387362

388363
secrets, err := requireSecrets(ctx, cmd, true, false)
@@ -395,11 +370,11 @@ func createAgent(ctx context.Context, cmd *cli.Command) error {
395370
}
396371

397372
req := &lkproto.CreateAgentRequest{
398-
AgentName: agentConfig.Name,
373+
AgentName: lkConfig.Agent.Name,
399374
Secrets: secrets,
400-
Replicas: int32(agentConfig.Replicas),
401-
MaxReplicas: int32(agentConfig.MaxReplicas),
402-
CpuReq: string(agentConfig.CPU),
375+
Replicas: int32(lkConfig.Agent.Replicas),
376+
MaxReplicas: int32(lkConfig.Agent.MaxReplicas),
377+
CpuReq: string(lkConfig.Agent.CPU),
403378
}
404379

405380
resp, err := agentsClient.CreateAgent(ctx, req)
@@ -412,13 +387,13 @@ func createAgent(ctx context.Context, cmd *cli.Command) error {
412387
return err
413388
}
414389

415-
err = agentfs.UploadTarball(workingDir, resp.PresignedUrl, []string{agentfs.AgentTOMLFile})
390+
err = agentfs.UploadTarball(workingDir, resp.PresignedUrl, []string{config.LiveKitTOMLFile})
416391
if err != nil {
417392
return err
418393
}
419394

420395
fmt.Printf("Created agent [%s] with ID [%s]\n", util.Accented(resp.AgentName), util.Accented(resp.AgentId))
421-
err = agentfs.Build(ctx, resp.AgentId, agentConfig.Name, "deploy", project)
396+
err = agentfs.Build(ctx, resp.AgentId, lkConfig.Agent.Name, "deploy", project)
422397
if err != nil {
423398
return err
424399
}
@@ -439,7 +414,7 @@ func createAgent(ctx context.Context, cmd *cli.Command) error {
439414
return err
440415
} else if viewLogs {
441416
fmt.Println("Tailing logs...safe to exit at any time")
442-
return agentfs.LogHelper(ctx, "", agentConfig.Name, "deploy", project)
417+
return agentfs.LogHelper(ctx, "", lkConfig.Agent.Name, "deploy", project)
443418
}
444419
}
445420
return nil
@@ -502,12 +477,12 @@ func createAgentConfig(ctx context.Context, cmd *cli.Command) error {
502477

503478
agent := response.Agents[0]
504479
regionAgent := agent.AgentDeployments[0]
505-
agentConfig := &agentfs.AgentTOML{
506-
ProjectSubdomain: matches[1],
507-
Name: agent.AgentName,
508-
CPU: agentfs.CPUString(regionAgent.CpuReq),
509-
Replicas: int(regionAgent.Replicas),
510-
MaxReplicas: int(regionAgent.MaxReplicas),
480+
lkConfig := config.NewLiveKitTOML(matches[1])
481+
lkConfig.Agent = &config.LiveKitTOMLAgentConfig{
482+
Name: agent.AgentName,
483+
CPU: config.CPUString(regionAgent.CpuReq),
484+
Replicas: int(regionAgent.Replicas),
485+
MaxReplicas: int(regionAgent.MaxReplicas),
511486
}
512487

513488
f, err := os.Create(tomlFilename)
@@ -516,7 +491,7 @@ func createAgentConfig(ctx context.Context, cmd *cli.Command) error {
516491
}
517492
defer f.Close()
518493

519-
if err := toml.NewEncoder(f).Encode(agentConfig); err != nil {
494+
if err := toml.NewEncoder(f).Encode(lkConfig); err != nil {
520495
return fmt.Errorf("error encoding TOML: %w", err)
521496
}
522497

@@ -525,19 +500,22 @@ func createAgentConfig(ctx context.Context, cmd *cli.Command) error {
525500
}
526501

527502
func deployAgent(ctx context.Context, cmd *cli.Command) error {
528-
agentConfig, configExists, err := agentfs.LoadTomlFile(workingDir, tomlFilename)
503+
lkConfig, configExists, err := config.LoadTomlFile(workingDir, tomlFilename)
529504
if err != nil && configExists {
530505
return err
531506
}
532507
if !configExists {
533508
return fmt.Errorf("config file [%s] required to update agent", util.Accented(tomlFilename))
534509
}
510+
if !lkConfig.HasAgent() {
511+
return fmt.Errorf("no agent config found in [%s]", tomlFilename)
512+
}
535513

536514
req := &lkproto.DeployAgentRequest{
537-
AgentName: agentConfig.Name,
538-
Replicas: int32(agentConfig.Replicas),
539-
CpuReq: string(agentConfig.CPU),
540-
MaxReplicas: int32(agentConfig.MaxReplicas),
515+
AgentName: lkConfig.Agent.Name,
516+
Replicas: int32(lkConfig.Agent.Replicas),
517+
CpuReq: string(lkConfig.Agent.CPU),
518+
MaxReplicas: int32(lkConfig.Agent.MaxReplicas),
541519
}
542520

543521
secrets, err := requireSecrets(ctx, cmd, false, true)
@@ -563,13 +541,13 @@ func deployAgent(ctx context.Context, cmd *cli.Command) error {
563541
}
564542

565543
presignedUrl := resp.PresignedUrl
566-
err = agentfs.UploadTarball(workingDir, presignedUrl, []string{agentfs.AgentTOMLFile})
544+
err = agentfs.UploadTarball(workingDir, presignedUrl, []string{config.LiveKitTOMLFile})
567545
if err != nil {
568546
return err
569547
}
570548

571549
fmt.Printf("Updated agent [%s]\n", util.Accented(resp.AgentId))
572-
err = agentfs.Build(ctx, resp.AgentId, agentConfig.Name, "update", project)
550+
err = agentfs.Build(ctx, resp.AgentId, lkConfig.Agent.Name, "update", project)
573551
if err != nil {
574552
return err
575553
}
@@ -638,19 +616,22 @@ func getAgentStatus(ctx context.Context, cmd *cli.Command) error {
638616
}
639617

640618
func updateAgent(ctx context.Context, cmd *cli.Command) error {
641-
agentConfig, configExists, err := agentfs.LoadTomlFile(workingDir, tomlFilename)
619+
lkConfig, configExists, err := config.LoadTomlFile(workingDir, tomlFilename)
642620
if err != nil && configExists {
643621
return err
644622
}
645623
if !configExists {
646624
return fmt.Errorf("config file [%s] required to update agent", tomlFilename)
647625
}
626+
if !lkConfig.HasAgent() {
627+
return fmt.Errorf("no agent config found in [%s]", tomlFilename)
628+
}
648629

649630
req := &lkproto.UpdateAgentRequest{
650-
AgentName: agentConfig.Name,
651-
Replicas: int32(agentConfig.Replicas),
652-
CpuReq: string(agentConfig.CPU),
653-
MaxReplicas: int32(agentConfig.MaxReplicas),
631+
AgentName: lkConfig.Agent.Name,
632+
Replicas: int32(lkConfig.Agent.Replicas),
633+
CpuReq: string(lkConfig.Agent.CPU),
634+
MaxReplicas: int32(lkConfig.Agent.MaxReplicas),
654635
}
655636

656637
secrets, err := requireSecrets(ctx, cmd, false, true)
@@ -672,7 +653,7 @@ func updateAgent(ctx context.Context, cmd *cli.Command) error {
672653
}
673654

674655
if resp.Success {
675-
fmt.Printf("Updated agent [%s]\n", util.Accented(agentConfig.Name))
656+
fmt.Printf("Updated agent [%s]\n", util.Accented(lkConfig.Agent.Name))
676657
return nil
677658
}
678659

@@ -920,15 +901,18 @@ func updateAgentSecrets(ctx context.Context, cmd *cli.Command) error {
920901
func getAgentName(cmd *cli.Command, agentDir string, tomlFileName string) (string, error) {
921902
agentName := cmd.String("name")
922903
if agentName == "" {
923-
agentConfig, configExists, err := agentfs.LoadTomlFile(agentDir, tomlFileName)
904+
lkConfig, configExists, err := config.LoadTomlFile(agentDir, tomlFileName)
924905
if err != nil && configExists {
925906
return "", err
926907
}
927908
if !configExists {
928909
return "", fmt.Errorf("config file [%s] required to update agent", tomlFileName)
929910
}
911+
if !lkConfig.HasAgent() {
912+
return "", fmt.Errorf("no agent config found in [%s]", tomlFileName)
913+
}
930914

931-
agentName = agentConfig.Name
915+
agentName = lkConfig.Agent.Name
932916
}
933917

934918
if agentName == "" {

0 commit comments

Comments
 (0)