cmd/headscale/cli: add grpcRun wrapper for gRPC client lifecycle

Add a grpcRun helper that wraps cobra RunFuncs, injecting a ready
gRPC client and context. The connection lifecycle (cancel, close)
is managed by the wrapper, eliminating the duplicated 3-line
boilerplate (newHeadscaleCLIWithConfig + defer cancel + defer
conn.Close) from 22 command handlers across 7 files.

Three call sites are intentionally left unconverted:
- backfillNodeIPsCmd: creates the client only after user confirmation
- getPolicy/setPolicy: conditionally use gRPC vs direct DB access
This commit is contained in:
Kristoffer Dalby
2026-02-18 13:18:09 +00:00
parent cfb308b4a7
commit aae2f7de71
7 changed files with 67 additions and 132 deletions

View File

@@ -13,6 +13,7 @@ import (
"github.com/juanfont/headscale/hscontrol/util"
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
@@ -41,6 +42,21 @@ func newHeadscaleServerWithConfig() (*hscontrol.Headscale, error) {
return app, nil
}
// grpcRun wraps a cobra RunFunc, injecting a ready gRPC client and context.
// Connection lifecycle is managed by the wrapper — callers never see
// the underlying conn or cancel func.
func grpcRun(
fn func(ctx context.Context, client v1.HeadscaleServiceClient, cmd *cobra.Command, args []string),
) func(*cobra.Command, []string) {
return func(cmd *cobra.Command, args []string) {
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
fn(ctx, client, cmd, args)
}
}
func newHeadscaleCLIWithConfig() (context.Context, v1.HeadscaleServiceClient, *grpc.ClientConn, context.CancelFunc) {
cfg, err := types.LoadCLIConfig()
if err != nil {