mirror of
https://github.com/juanfont/headscale.git
synced 2026-02-07 20:04:00 +01:00
82 lines
2.1 KiB
Markdown
82 lines
2.1 KiB
Markdown
# CLI Simplification - WithClient Pattern
|
|
|
|
## Problem
|
|
Every CLI command has repetitive gRPC client setup boilerplate:
|
|
|
|
```go
|
|
// This pattern appears 25+ times across all commands
|
|
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
|
|
defer cancel()
|
|
defer conn.Close()
|
|
|
|
// ... command logic ...
|
|
```
|
|
|
|
## Solution
|
|
Simple closure that handles client lifecycle:
|
|
|
|
```go
|
|
// client.go - 16 lines total
|
|
func WithClient(fn func(context.Context, v1.HeadscaleServiceClient) error) error {
|
|
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
|
|
defer cancel()
|
|
defer conn.Close()
|
|
|
|
return fn(ctx, client)
|
|
}
|
|
```
|
|
|
|
## Usage Example
|
|
|
|
### Before (users.go listUsersCmd):
|
|
```go
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
output, _ := cmd.Flags().GetString("output")
|
|
|
|
ctx, client, conn, cancel := newHeadscaleCLIWithConfig() // 4 lines
|
|
defer cancel()
|
|
defer conn.Close()
|
|
|
|
request := &v1.ListUsersRequest{}
|
|
// ... build request ...
|
|
|
|
response, err := client.ListUsers(ctx, request)
|
|
if err != nil {
|
|
ErrorOutput(err, "Cannot get users: "+status.Convert(err).Message(), output)
|
|
}
|
|
// ... handle response ...
|
|
}
|
|
```
|
|
|
|
### After:
|
|
```go
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
output, _ := cmd.Flags().GetString("output")
|
|
|
|
err := WithClient(func(ctx context.Context, client v1.HeadscaleServiceClient) error {
|
|
request := &v1.ListUsersRequest{}
|
|
// ... build request ...
|
|
|
|
response, err := client.ListUsers(ctx, request)
|
|
if err != nil {
|
|
ErrorOutput(err, "Cannot get users: "+status.Convert(err).Message(), output)
|
|
return err
|
|
}
|
|
// ... handle response ...
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return // Error already handled
|
|
}
|
|
}
|
|
```
|
|
|
|
## Benefits
|
|
- **Removes 4 lines of boilerplate** from every command
|
|
- **Ensures proper cleanup** - no forgetting defer statements
|
|
- **Simpler error handling** - return from closure, handled centrally
|
|
- **Easy to apply** - minimal changes to existing commands
|
|
|
|
## Rollout
|
|
This pattern can be applied to all 25+ commands systematically, removing ~100 lines of repetitive boilerplate. |