From cce0fd08b07dd875a398c9e4cca4ecef1ee1295c Mon Sep 17 00:00:00 2001 From: Johan Siebens Date: Mon, 5 Feb 2024 16:20:00 +0100 Subject: [PATCH] chore: refactor target and tailnet selection in commands --- internal/cmd/acl.go | 76 ++------ internal/cmd/auth.go | 19 +- internal/cmd/auth_key.go | 73 ++------ internal/cmd/derp_map.go | 48 ++--- internal/cmd/dns.go | 57 ++---- internal/cmd/funcs.go | 52 ------ internal/cmd/iam.go | 76 ++------ internal/cmd/machine.go | 186 ++++++-------------- internal/cmd/tailnet.go | 370 +++++++-------------------------------- internal/cmd/target.go | 107 +++++++++-- internal/cmd/users.go | 46 ++--- internal/cmd/version.go | 21 +-- 12 files changed, 283 insertions(+), 848 deletions(-) delete mode 100644 internal/cmd/funcs.go diff --git a/internal/cmd/acl.go b/internal/cmd/acl.go index 48c9089..c8ce544 100644 --- a/internal/cmd/acl.go +++ b/internal/cmd/acl.go @@ -2,7 +2,6 @@ package cmd import ( "bytes" - "context" "encoding/json" "fmt" "github.com/bufbuild/connect-go" @@ -14,33 +13,14 @@ import ( ) func getACLConfigCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "get-acl-policy", Short: "Get the ACL policy", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags command.RunE = func(cmd *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - resp, err := client.GetACLPolicy(context.Background(), connect.NewRequest(&api.GetACLPolicyRequest{TailnetId: tailnet.Id})) + resp, err := tc.Client().GetACLPolicy(cmd.Context(), connect.NewRequest(&api.GetACLPolicyRequest{TailnetId: tc.TailnetID()})) if err != nil { return err } @@ -59,35 +39,16 @@ func getACLConfigCommand() *cobra.Command { } func editACLConfigCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "edit-acl-policy", Short: "Edit the ACL policy", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags command.RunE = func(cmd *cobra.Command, args []string) error { edit := editor.NewDefaultEditor([]string{"IONSCALE_EDITOR", "EDITOR"}) - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - resp, err := client.GetACLPolicy(context.Background(), connect.NewRequest(&api.GetACLPolicyRequest{TailnetId: tailnet.Id})) + resp, err := tc.Client().GetACLPolicy(cmd.Context(), connect.NewRequest(&api.GetACLPolicyRequest{TailnetId: tc.TailnetID()})) if err != nil { return err } @@ -114,7 +75,7 @@ func editACLConfigCommand() *cobra.Command { return err } - _, err = client.SetACLPolicy(context.Background(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tailnet.Id, Policy: policy})) + _, err = tc.Client().SetACLPolicy(cmd.Context(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy})) if err != nil { return err } @@ -128,23 +89,16 @@ func editACLConfigCommand() *cobra.Command { } func setACLConfigCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "set-acl-policy", Short: "Set ACL policy", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string var file string - var target = Target{} - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") command.Flags().StringVar(&file, "file", "", "Path to json file with the acl configuration") - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags command.RunE = func(cmd *cobra.Command, args []string) error { content, err := os.ReadFile(file) if err != nil { @@ -161,17 +115,7 @@ func setACLConfigCommand() *cobra.Command { return err } - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - _, err = client.SetACLPolicy(context.Background(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tailnet.Id, Policy: policy})) + _, err = tc.Client().SetACLPolicy(cmd.Context(), connect.NewRequest(&api.SetACLPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy})) if err != nil { return err } diff --git a/internal/cmd/auth.go b/internal/cmd/auth.go index 8676017..29beb5c 100644 --- a/internal/cmd/auth.go +++ b/internal/cmd/auth.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "fmt" "github.com/bufbuild/connect-go" "github.com/jsiebens/ionscale/pkg/client/ionscale" @@ -20,24 +19,14 @@ func authCommand() *cobra.Command { } func authLoginCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "login", SilenceUsage: true, - } - - var target = Target{} - - target.prepareCommand(command) - - command.RunE = func(command *cobra.Command, args []string) error { - - client, err := target.createGRPCClient() - if err != nil { - return err - } + }) + command.RunE = func(cmd *cobra.Command, args []string) error { req := &api.AuthenticateRequest{} - stream, err := client.Authenticate(context.Background(), connect.NewRequest(req)) + stream, err := tc.Client().Authenticate(cmd.Context(), connect.NewRequest(req)) if err != nil { return err } diff --git a/internal/cmd/auth_key.go b/internal/cmd/auth_key.go index 62f3855..bc6cce9 100644 --- a/internal/cmd/auth_key.go +++ b/internal/cmd/auth_key.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "fmt" "github.com/bufbuild/connect-go" api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1" @@ -28,40 +27,23 @@ func authkeysCommand() *cobra.Command { } func createAuthkeysCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "create", Short: "Creates a new auth key in the specified tailnet", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string var ephemeral bool var preAuthorized bool var tags []string var expiry string - var target = Target{} - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") command.Flags().BoolVar(&ephemeral, "ephemeral", false, "When enabled, machines authenticated by this key will be automatically removed after going offline.") command.Flags().StringSliceVar(&tags, "tag", []string{}, "Machines authenticated by this key will be automatically tagged with these tags") command.Flags().StringVar(&expiry, "expiry", "180d", "Human-readable expiration of the key") command.Flags().BoolVar(&preAuthorized, "pre-authorized", false, "Generate an auth key which is pre-authorized.") - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { var expiryDur *durationpb.Duration if expiry != "" && expiry != "none" { @@ -73,13 +55,13 @@ func createAuthkeysCommand() *cobra.Command { } req := &api.CreateAuthKeyRequest{ - TailnetId: tailnet.Id, + TailnetId: tc.TailnetID(), Ephemeral: ephemeral, PreAuthorized: preAuthorized, Tags: tags, Expiry: expiryDur, } - resp, err := client.CreateAuthKey(context.Background(), connect.NewRequest(req)) + resp, err := tc.Client().CreateAuthKey(cmd.Context(), connect.NewRequest(req)) if err != nil { return err @@ -99,25 +81,19 @@ func createAuthkeysCommand() *cobra.Command { } func deleteAuthKeyCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "delete", Short: "Delete a specified auth key", SilenceUsage: true, - } + }) var authKeyId uint64 - var target = Target{} - target.prepareCommand(command) + command.Flags().Uint64Var(&authKeyId, "id", 0, "Auth Key ID") - command.RunE = func(command *cobra.Command, args []string) error { - grpcClient, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.DeleteAuthKeyRequest{AuthKeyId: authKeyId} - if _, err := grpcClient.DeleteAuthKey(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().DeleteAuthKey(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -130,34 +106,15 @@ func deleteAuthKeyCommand() *cobra.Command { } func listAuthkeysCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "list", Short: "List all auth keys for a given tailnet", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - req := &api.ListAuthKeysRequest{TailnetId: tailnet.Id} - resp, err := client.ListAuthKeys(context.Background(), connect.NewRequest(req)) + command.RunE = func(cmd *cobra.Command, args []string) error { + req := &api.ListAuthKeysRequest{TailnetId: tc.TailnetID()} + resp, err := tc.Client().ListAuthKeys(cmd.Context(), connect.NewRequest(req)) if err != nil { return err diff --git a/internal/cmd/derp_map.go b/internal/cmd/derp_map.go index 7f5fac2..5158475 100644 --- a/internal/cmd/derp_map.go +++ b/internal/cmd/derp_map.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "encoding/json" "fmt" "github.com/bufbuild/connect-go" @@ -27,25 +26,18 @@ func systemCommand() *cobra.Command { } func getDefaultDERPMap() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "get-derp-map", Short: "Get the DERP Map configuration", SilenceUsage: true, - } + }) var asJson bool - var target = Target{} - target.prepareCommand(command) command.Flags().BoolVar(&asJson, "json", false, "When enabled, render output as json otherwise yaml") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - resp, err := client.GetDefaultDERPMap(context.Background(), connect.NewRequest(&api.GetDefaultDERPMapRequest{})) + command.RunE = func(cmd *cobra.Command, args []string) error { + resp, err := tc.Client().GetDefaultDERPMap(cmd.Context(), connect.NewRequest(&api.GetDefaultDERPMapRequest{})) if err != nil { return err @@ -82,23 +74,17 @@ func getDefaultDERPMap() *cobra.Command { } func setDefaultDERPMap() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "set-derp-map", Short: "Set the DERP Map configuration", SilenceUsage: true, - } + }) var file string - var target = Target{} - target.prepareCommand(command) + command.Flags().StringVar(&file, "file", "", "Path to json file with the DERP Map configuration") - command.RunE = func(command *cobra.Command, args []string) error { - grpcClient, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { content, err := os.ReadFile(file) if err != nil { return err @@ -109,7 +95,7 @@ func setDefaultDERPMap() *cobra.Command { return err } - resp, err := grpcClient.SetDefaultDERPMap(context.Background(), connect.NewRequest(&api.SetDefaultDERPMapRequest{Value: rawJson})) + resp, err := tc.Client().SetDefaultDERPMap(cmd.Context(), connect.NewRequest(&api.SetDefaultDERPMapRequest{Value: rawJson})) if err != nil { return err } @@ -128,22 +114,14 @@ func setDefaultDERPMap() *cobra.Command { } func resetDefaultDERPMap() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "reset-derp-map", Short: "Reset the DERP Map to the default configuration", SilenceUsage: true, - } + }) - var target = Target{} - target.prepareCommand(command) - - command.RunE = func(command *cobra.Command, args []string) error { - grpcClient, err := target.createGRPCClient() - if err != nil { - return err - } - - if _, err := grpcClient.ResetDefaultDERPMap(context.Background(), connect.NewRequest(&api.ResetDefaultDERPMapRequest{})); err != nil { + command.RunE = func(cmd *cobra.Command, args []string) error { + if _, err := tc.Client().ResetDefaultDERPMap(cmd.Context(), connect.NewRequest(&api.ResetDefaultDERPMapRequest{})); err != nil { return err } diff --git a/internal/cmd/dns.go b/internal/cmd/dns.go index 31d853d..1915cc6 100644 --- a/internal/cmd/dns.go +++ b/internal/cmd/dns.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "fmt" "github.com/bufbuild/connect-go" api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1" @@ -12,34 +11,15 @@ import ( ) func getDNSConfigCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "get-dns", Short: "Get DNS configuration", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - req := api.GetDNSConfigRequest{TailnetId: tailnet.Id} - resp, err := client.GetDNSConfig(context.Background(), connect.NewRequest(&req)) + command.RunE = func(cmd *cobra.Command, args []string) error { + req := api.GetDNSConfigRequest{TailnetId: tc.TailnetID()} + resp, err := tc.Client().GetDNSConfig(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err @@ -83,40 +63,23 @@ func getDNSConfigCommand() *cobra.Command { } func setDNSConfigCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "set-dns", Short: "Set DNS config", SilenceUsage: true, - } + }) var nameservers []string var magicDNS bool var httpsCerts bool var overrideLocalDNS bool - var tailnetID uint64 - var tailnetName string - var target = Target{} - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") command.Flags().StringSliceVarP(&nameservers, "nameserver", "", []string{}, "Machines on your network will use these nameservers to resolve DNS queries.") command.Flags().BoolVarP(&magicDNS, "magic-dns", "", false, "Enable MagicDNS for the specified Tailnet") command.Flags().BoolVarP(&httpsCerts, "https-certs", "", false, "Enable HTTPS Certificates for the specified Tailnet") command.Flags().BoolVarP(&overrideLocalDNS, "override-local-dns", "", false, "When enabled, connected clients ignore local DNS settings and always use the nameservers specified for this Tailnet") - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { var globalNameservers []string var routes = make(map[string]*api.Routes) @@ -135,7 +98,7 @@ func setDNSConfigCommand() *cobra.Command { } req := api.SetDNSConfigRequest{ - TailnetId: tailnet.Id, + TailnetId: tc.TailnetID(), Config: &api.DNSConfig{ MagicDns: magicDNS, OverrideLocalDns: overrideLocalDNS, @@ -144,7 +107,7 @@ func setDNSConfigCommand() *cobra.Command { HttpsCerts: httpsCerts, }, } - resp, err := client.SetDNSConfig(context.Background(), connect.NewRequest(&req)) + resp, err := tc.Client().SetDNSConfig(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err diff --git a/internal/cmd/funcs.go b/internal/cmd/funcs.go deleted file mode 100644 index 16ee2d7..0000000 --- a/internal/cmd/funcs.go +++ /dev/null @@ -1,52 +0,0 @@ -package cmd - -import ( - "context" - "fmt" - "github.com/bufbuild/connect-go" - "github.com/jsiebens/ionscale/pkg/client/ionscale" - api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1" - apiconnect "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1/ionscalev1connect" - "github.com/spf13/cobra" -) - -func checkRequiredTailnetAndTailnetIdFlags(cmd *cobra.Command, args []string) error { - savedTailnetID, err := ionscale.TailnetFromFile() - if err != nil { - return err - } - - if savedTailnetID == 0 && !cmd.Flags().Changed("tailnet") && !cmd.Flags().Changed("tailnet-id") { - return fmt.Errorf("flag --tailnet or --tailnet-id is required") - } - - if cmd.Flags().Changed("tailnet") && cmd.Flags().Changed("tailnet-id") { - return fmt.Errorf("flags --tailnet and --tailnet-id are mutually exclusive") - } - - return nil -} - -func findTailnet(client apiconnect.IonscaleServiceClient, tailnet string, tailnetID uint64) (*api.Tailnet, error) { - savedTailnetID, err := ionscale.TailnetFromFile() - if err != nil { - return nil, err - } - - if savedTailnetID == 0 && tailnetID == 0 && tailnet == "" { - return nil, fmt.Errorf("requested tailnet not found or you are not authorized for this tailnet") - } - - tailnets, err := client.ListTailnets(context.Background(), connect.NewRequest(&api.ListTailnetsRequest{})) - if err != nil { - return nil, err - } - - for _, t := range tailnets.Msg.Tailnet { - if t.Id == savedTailnetID || t.Id == tailnetID || t.Name == tailnet { - return t, nil - } - } - - return nil, fmt.Errorf("requested tailnet not found or you are not authorized for this tailnet") -} diff --git a/internal/cmd/iam.go b/internal/cmd/iam.go index 3114cc5..34cab15 100644 --- a/internal/cmd/iam.go +++ b/internal/cmd/iam.go @@ -2,7 +2,6 @@ package cmd import ( "bytes" - "context" "encoding/json" "fmt" "github.com/bufbuild/connect-go" @@ -14,33 +13,14 @@ import ( ) func getIAMPolicyCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "get-iam-policy", Short: "Get the IAM policy", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags command.RunE = func(cmd *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - resp, err := client.GetIAMPolicy(context.Background(), connect.NewRequest(&api.GetIAMPolicyRequest{TailnetId: tailnet.Id})) + resp, err := tc.Client().GetIAMPolicy(cmd.Context(), connect.NewRequest(&api.GetIAMPolicyRequest{TailnetId: tc.TailnetID()})) if err != nil { return err } @@ -59,35 +39,16 @@ func getIAMPolicyCommand() *cobra.Command { } func editIAMPolicyCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "edit-iam-policy", Short: "Edit the IAM policy", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags command.RunE = func(cmd *cobra.Command, args []string) error { edit := editor.NewDefaultEditor([]string{"IONSCALE_EDITOR", "EDITOR"}) - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - resp, err := client.GetIAMPolicy(context.Background(), connect.NewRequest(&api.GetIAMPolicyRequest{TailnetId: tailnet.Id})) + resp, err := tc.Client().GetIAMPolicy(cmd.Context(), connect.NewRequest(&api.GetIAMPolicyRequest{TailnetId: tc.TailnetID()})) if err != nil { return err } @@ -114,7 +75,7 @@ func editIAMPolicyCommand() *cobra.Command { return err } - _, err = client.SetIAMPolicy(context.Background(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tailnet.Id, Policy: policy})) + _, err = tc.Client().SetIAMPolicy(cmd.Context(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy})) if err != nil { return err } @@ -128,23 +89,16 @@ func editIAMPolicyCommand() *cobra.Command { } func setIAMPolicyCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "set-iam-policy", Short: "Set IAM policy", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string var file string - var target = Target{} - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") command.Flags().StringVar(&file, "file", "", "Path to json file with the acl configuration") - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags command.RunE = func(cmd *cobra.Command, args []string) error { content, err := os.ReadFile(file) if err != nil { @@ -161,17 +115,7 @@ func setIAMPolicyCommand() *cobra.Command { return err } - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - _, err = client.SetIAMPolicy(context.Background(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tailnet.Id, Policy: policy})) + _, err = tc.Client().SetIAMPolicy(cmd.Context(), connect.NewRequest(&api.SetIAMPolicyRequest{TailnetId: tc.TailnetID(), Policy: policy})) if err != nil { return err } diff --git a/internal/cmd/machine.go b/internal/cmd/machine.go index f853b74..d894e09 100644 --- a/internal/cmd/machine.go +++ b/internal/cmd/machine.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "fmt" "github.com/bufbuild/connect-go" api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1" @@ -39,27 +38,20 @@ func machineCommands() *cobra.Command { } func getMachineCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "get", Short: "Retrieve detailed information for a machine", SilenceUsage: true, - } + }) var machineID uint64 - var target = Target{} - target.prepareCommand(command) command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.") _ = command.MarkFlagRequired("machine-id") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.GetMachineRequest{MachineId: machineID} - resp, err := client.GetMachine(context.Background(), connect.NewRequest(&req)) + resp, err := tc.Client().GetMachine(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err } @@ -151,27 +143,20 @@ func getMachineCommand() *cobra.Command { } func deleteMachineCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "delete", Short: "Deletes a machine", SilenceUsage: true, - } + }) var machineID uint64 - var target = Target{} - target.prepareCommand(command) command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.") _ = command.MarkFlagRequired("machine-id") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.DeleteMachineRequest{MachineId: machineID} - if _, err := client.DeleteMachine(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().DeleteMachine(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -184,27 +169,20 @@ func deleteMachineCommand() *cobra.Command { } func expireMachineCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "expire", Short: "Expires a machine", SilenceUsage: true, - } + }) var machineID uint64 - var target = Target{} - target.prepareCommand(command) command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.") _ = command.MarkFlagRequired("machine-id") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.ExpireMachineRequest{MachineId: machineID} - if _, err := client.ExpireMachine(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().ExpireMachine(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -217,27 +195,20 @@ func expireMachineCommand() *cobra.Command { } func authorizeMachineCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "authorize", Short: "Authorizes a machine", SilenceUsage: true, - } + }) var machineID uint64 - var target = Target{} - target.prepareCommand(command) command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.") _ = command.MarkFlagRequired("machine-id") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.AuthorizeMachineRequest{MachineId: machineID} - if _, err := client.AuthorizeMachine(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().AuthorizeMachine(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -250,34 +221,15 @@ func authorizeMachineCommand() *cobra.Command { } func listMachinesCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "list", Short: "List machines", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string - - var target = Target{} - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - req := api.ListMachinesRequest{TailnetId: tailnet.Id} - resp, err := client.ListMachines(context.Background(), connect.NewRequest(&req)) + command.RunE = func(cmd *cobra.Command, args []string) error { + req := api.ListMachinesRequest{TailnetId: tc.TailnetID()} + resp, err := tc.Client().ListMachines(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err @@ -305,27 +257,20 @@ func listMachinesCommand() *cobra.Command { } func getMachineRoutesCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "get-routes", Short: "Show routes advertised and enabled by a given machine", SilenceUsage: true, - } + }) var machineID uint64 - var target = Target{} - target.prepareCommand(command) command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID.") _ = command.MarkFlagRequired("machine-id") - command.RunE = func(command *cobra.Command, args []string) error { - grpcClient, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.GetMachineRoutesRequest{MachineId: machineID} - resp, err := grpcClient.GetMachineRoutes(context.Background(), connect.NewRequest(&req)) + resp, err := tc.Client().GetMachineRoutes(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err } @@ -339,29 +284,23 @@ func getMachineRoutesCommand() *cobra.Command { } func enableMachineRoutesCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "enable-routes", Short: "Enable routes for a given machine", SilenceUsage: true, - } + }) var machineID uint64 var routes []string var replace bool - var target = Target{} - target.prepareCommand(command) + command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID") command.Flags().StringSliceVar(&routes, "routes", []string{}, "List of routes to enable") command.Flags().BoolVar(&replace, "replace", false, "Replace current enabled routes with this new list") _ = command.MarkFlagRequired("machine-id") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { for _, r := range routes { if _, err := netaddr.ParseIPPrefix(r); err != nil { return err @@ -369,7 +308,7 @@ func enableMachineRoutesCommand() *cobra.Command { } req := api.EnableMachineRoutesRequest{MachineId: machineID, Routes: routes, Replace: replace} - resp, err := client.EnableMachineRoutes(context.Background(), connect.NewRequest(&req)) + resp, err := tc.Client().EnableMachineRoutes(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err } @@ -383,27 +322,21 @@ func enableMachineRoutesCommand() *cobra.Command { } func disableMachineRoutesCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "disable-routes", Short: "Disable routes for a given machine", SilenceUsage: true, - } + }) var machineID uint64 var routes []string - var target = Target{} - target.prepareCommand(command) + command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID") command.Flags().StringSliceVar(&routes, "routes", []string{}, "List of routes to enable") _ = command.MarkFlagRequired("machine-id") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { for _, r := range routes { if _, err := netaddr.ParseIPPrefix(r); err != nil { return err @@ -411,7 +344,7 @@ func disableMachineRoutesCommand() *cobra.Command { } req := api.DisableMachineRoutesRequest{MachineId: machineID, Routes: routes} - resp, err := client.DisableMachineRoutes(context.Background(), connect.NewRequest(&req)) + resp, err := tc.Client().DisableMachineRoutes(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err } @@ -425,27 +358,20 @@ func disableMachineRoutesCommand() *cobra.Command { } func enableExitNodeCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "enable-exit-node", Short: "Enable given machine as an exit node", SilenceUsage: true, - } + }) var machineID uint64 - var target = Target{} - target.prepareCommand(command) command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID") _ = command.MarkFlagRequired("machine-id") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.EnableExitNodeRequest{MachineId: machineID} - resp, err := client.EnableExitNode(context.Background(), connect.NewRequest(&req)) + resp, err := tc.Client().EnableExitNode(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err } @@ -459,27 +385,21 @@ func enableExitNodeCommand() *cobra.Command { } func disableExitNodeCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "disable-exit-node", Short: "Disable given machine as an exit node", SilenceUsage: true, - } + }) var machineID uint64 - var target = Target{} - target.prepareCommand(command) + command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID") _ = command.MarkFlagRequired("machine-id") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.DisableExitNodeRequest{MachineId: machineID} - resp, err := client.DisableExitNode(context.Background(), connect.NewRequest(&req)) + resp, err := tc.Client().DisableExitNode(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err } @@ -512,22 +432,18 @@ func disableMachineKeyExpiryCommand() *cobra.Command { return configureSetMachineKeyExpiryCommand(command, true) } -func configureSetMachineKeyExpiryCommand(command *cobra.Command, v bool) *cobra.Command { +func configureSetMachineKeyExpiryCommand(cmdTmpl *cobra.Command, disable bool) *cobra.Command { + command, tc := prepareCommand(false, cmdTmpl) + var machineID uint64 - var target = Target{} - target.prepareCommand(command) + command.Flags().Uint64Var(&machineID, "machine-id", 0, "Machine ID") _ = command.MarkFlagRequired("machine-id") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - req := api.SetMachineKeyExpiryRequest{MachineId: machineID, Disabled: v} - _, err = client.SetMachineKeyExpiry(context.Background(), connect.NewRequest(&req)) + command.RunE = func(cmd *cobra.Command, args []string) error { + req := api.SetMachineKeyExpiryRequest{MachineId: machineID, Disabled: disable} + _, err := tc.Client().SetMachineKeyExpiry(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err } diff --git a/internal/cmd/tailnet.go b/internal/cmd/tailnet.go index 9792dd5..2746b20 100644 --- a/internal/cmd/tailnet.go +++ b/internal/cmd/tailnet.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "encoding/json" "fmt" "github.com/bufbuild/connect-go" @@ -50,23 +49,14 @@ func tailnetCommand() *cobra.Command { } func listTailnetsCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "list", Short: "List available Tailnets", SilenceUsage: true, - } + }) - var target = Target{} - target.prepareCommand(command) - - command.RunE = func(command *cobra.Command, args []string) error { - - client, err := target.createGRPCClient() - if err != nil { - return err - } - - resp, err := client.ListTailnets(context.Background(), connect.NewRequest(&api.ListTailnetsRequest{})) + command.RunE = func(cmd *cobra.Command, args []string) error { + resp, err := tc.Client().ListTailnets(cmd.Context(), connect.NewRequest(&api.ListTailnetsRequest{})) if err != nil { return err @@ -85,17 +75,15 @@ func listTailnetsCommand() *cobra.Command { } func createTailnetsCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "create", Short: "Create a new Tailnet", SilenceUsage: true, - } + }) var name string var domain string var email string - var target = Target{} - target.prepareCommand(command) command.Flags().StringVarP(&name, "name", "n", "", "") command.Flags().StringVar(&domain, "domain", "", "") @@ -111,7 +99,7 @@ func createTailnetsCommand() *cobra.Command { return nil } - command.RunE = func(command *cobra.Command, args []string) error { + command.RunE = func(cmd *cobra.Command, args []string) error { dnsConfig := defaults.DefaultDNSConfig() aclPolicy := defaults.DefaultACLPolicy() @@ -134,12 +122,7 @@ func createTailnetsCommand() *cobra.Command { } } - client, err := target.createGRPCClient() - if err != nil { - return err - } - - resp, err := client.CreateTailnet(context.Background(), connect.NewRequest(&api.CreateTailnetRequest{ + resp, err := tc.Client().CreateTailnet(cmd.Context(), connect.NewRequest(&api.CreateTailnetRequest{ Name: name, IamPolicy: iamPolicy, AclPolicy: aclPolicy, @@ -161,37 +144,18 @@ func createTailnetsCommand() *cobra.Command { } func deleteTailnetCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "delete", Short: "Delete a tailnet", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string var force bool - var target = Target{} - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") command.Flags().BoolVar(&force, "force", false, "When enabled, force delete the specified Tailnet even when machines are still available.") - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - _, err = client.DeleteTailnet(context.Background(), connect.NewRequest(&api.DeleteTailnetRequest{TailnetId: tailnet.Id, Force: force})) - + command.RunE = func(cmd *cobra.Command, args []string) error { + _, err := tc.Client().DeleteTailnet(cmd.Context(), connect.NewRequest(&api.DeleteTailnetRequest{TailnetId: tc.TailnetID(), Force: force})) if err != nil { return err } @@ -205,36 +169,18 @@ func deleteTailnetCommand() *cobra.Command { } func getDERPMap() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "get-derp-map", Short: "Get the DERP Map configuration", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string var asJson bool - var target = Target{} - target.prepareCommand(command) - - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") command.Flags().BoolVar(&asJson, "json", false, "When enabled, render output as json otherwise yaml") - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - resp, err := client.GetDERPMap(context.Background(), connect.NewRequest(&api.GetDERPMapRequest{TailnetId: tailnet.Id})) + command.RunE = func(cmd *cobra.Command, args []string) error { + resp, err := tc.Client().GetDERPMap(cmd.Context(), connect.NewRequest(&api.GetDERPMapRequest{TailnetId: tc.TailnetID()})) if err != nil { return err @@ -271,40 +217,23 @@ func getDERPMap() *cobra.Command { } func setDERPMap() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "set-derp-map", Short: "Set the DERP Map configuration", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string var file string - var target = Target{} - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") command.Flags().StringVar(&file, "file", "", "Path to json file with the DERP Map configuration") - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { rawJson, err := os.ReadFile(file) if err != nil { return err } - resp, err := client.SetDERPMap(context.Background(), connect.NewRequest(&api.SetDERPMapRequest{TailnetId: tailnet.Id, Value: rawJson})) + resp, err := tc.Client().SetDERPMap(cmd.Context(), connect.NewRequest(&api.SetDERPMapRequest{TailnetId: tc.TailnetID(), Value: rawJson})) if err != nil { return err } @@ -323,33 +252,14 @@ func setDERPMap() *cobra.Command { } func resetDERPMap() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "reset-derp-map", Short: "Reset the DERP Map to the default configuration", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string - var target = Target{} - target.prepareCommand(command) - - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - if _, err := client.ResetDERPMap(context.Background(), connect.NewRequest(&api.ResetDERPMapRequest{TailnetId: tailnet.Id})); err != nil { + command.RunE = func(cmd *cobra.Command, args []string) error { + if _, err := tc.Client().ResetDERPMap(cmd.Context(), connect.NewRequest(&api.ResetDERPMapRequest{TailnetId: tc.TailnetID()})); err != nil { return err } @@ -362,38 +272,19 @@ func resetDERPMap() *cobra.Command { } func enableFileSharingCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "enable-file-sharing", Aliases: []string{"enable-taildrop"}, Short: "Enable Taildrop, the file sharing feature", SilenceUsage: true, - } - - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } + }) + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.EnableFileSharingRequest{ - TailnetId: tailnet.Id, + TailnetId: tc.TailnetID(), } - if _, err := client.EnableFileSharing(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().EnableFileSharing(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -404,38 +295,19 @@ func enableFileSharingCommand() *cobra.Command { } func disableFileSharingCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "disable-file-sharing", Aliases: []string{"disable-taildrop"}, Short: "Disable Taildrop, the file sharing feature", SilenceUsage: true, - } - - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } + }) + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.DisableFileSharingRequest{ - TailnetId: tailnet.Id, + TailnetId: tc.TailnetID(), } - if _, err := client.DisableFileSharing(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().DisableFileSharing(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -446,37 +318,18 @@ func disableFileSharingCommand() *cobra.Command { } func enableServiceCollectionCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "enable-service-collection", Short: "Enable monitoring live services running on your network’s machines.", SilenceUsage: true, - } - - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } + }) + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.EnableServiceCollectionRequest{ - TailnetId: tailnet.Id, + TailnetId: tc.TailnetID(), } - if _, err := client.EnableServiceCollection(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().EnableServiceCollection(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -487,37 +340,18 @@ func enableServiceCollectionCommand() *cobra.Command { } func disableServiceCollectionCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "disable-service-collection", Short: "Disable monitoring live services running on your network’s machines.", SilenceUsage: true, - } - - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } + }) + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.DisableServiceCollectionRequest{ - TailnetId: tailnet.Id, + TailnetId: tc.TailnetID(), } - if _, err := client.DisableServiceCollection(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().DisableServiceCollection(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -528,37 +362,18 @@ func disableServiceCollectionCommand() *cobra.Command { } func enableSSHCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "enable-ssh", Short: "Enable ssh access using tailnet and ACLs.", SilenceUsage: true, - } - - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } + }) + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.EnableSSHRequest{ - TailnetId: tailnet.Id, + TailnetId: tc.TailnetID(), } - if _, err := client.EnableSSH(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().EnableSSH(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -569,37 +384,18 @@ func enableSSHCommand() *cobra.Command { } func disableSSHCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "disable-ssh", Short: "Disable ssh access using tailnet and ACLs.", SilenceUsage: true, - } - - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } + }) + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.DisableSSHRequest{ - TailnetId: tailnet.Id, + TailnetId: tc.TailnetID(), } - if _, err := client.DisableSSH(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().DisableSSH(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -610,37 +406,18 @@ func disableSSHCommand() *cobra.Command { } func enableMachineAuthorizationCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "enable-machine-authorization", Short: "Enable machine authorization.", SilenceUsage: true, - } - - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } + }) + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.EnableMachineAuthorizationRequest{ - TailnetId: tailnet.Id, + TailnetId: tc.TailnetID(), } - if _, err := client.EnableMachineAuthorization(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().EnableMachineAuthorization(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } @@ -651,37 +428,18 @@ func enableMachineAuthorizationCommand() *cobra.Command { } func disableMachineAuthorizationCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "disable-machine-authorization", Short: "Disable machine authorization.", SilenceUsage: true, - } - - var tailnetID uint64 - var tailnetName string - var target = Target{} - - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } + }) + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.DisableMachineAuthorizationRequest{ - TailnetId: tailnet.Id, + TailnetId: tc.TailnetID(), } - if _, err := client.DisableMachineAuthorization(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().DisableMachineAuthorization(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } diff --git a/internal/cmd/target.go b/internal/cmd/target.go index dd8e126..d3f1498 100644 --- a/internal/cmd/target.go +++ b/internal/cmd/target.go @@ -1,8 +1,11 @@ package cmd import ( + "fmt" + "github.com/bufbuild/connect-go" "github.com/jsiebens/ionscale/internal/config" "github.com/jsiebens/ionscale/pkg/client/ionscale" + ionscalev1 "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1" api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1/ionscalev1connect" "github.com/spf13/cobra" ) @@ -14,48 +17,122 @@ const ( ionscaleInsecureSkipVerify = "IONSCALE_SKIP_VERIFY" ) -type Target struct { +type TargetContext interface { + Client() api.IonscaleServiceClient + Addr() string + TailnetID() uint64 +} + +type target struct { addr string insecureSkipVerify bool systemAdminKey string + + tailnetID uint64 + tailnetName string + + client api.IonscaleServiceClient + tailnet *ionscalev1.Tailnet } -func (t *Target) prepareCommand(cmd *cobra.Command) { +func prepareCommand(enableTailnetSelector bool, cmd *cobra.Command) (*cobra.Command, TargetContext) { + t := &target{} + cmd.Flags().StringVar(&t.addr, "addr", "", "Addr of the ionscale server, as a complete URL") cmd.Flags().BoolVar(&t.insecureSkipVerify, "tls-skip-verify", false, "Disable verification of TLS certificates") cmd.Flags().StringVar(&t.systemAdminKey, "system-admin-key", "", "If specified, the given value will be used as the key to generate a Bearer token for the call. This can also be specified via the IONSCALE_ADMIN_KEY environment variable.") -} -func (t *Target) createGRPCClient() (api.IonscaleServiceClient, error) { - addr := t.getAddr() - skipVerify := t.getInsecureSkipVerify() - systemAdminKey := t.getSystemAdminKey() - - auth, err := ionscale.LoadClientAuth(systemAdminKey) - if err != nil { - return nil, err + if enableTailnetSelector { + cmd.Flags().StringVar(&t.tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") + cmd.Flags().Uint64Var(&t.tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") } - return ionscale.NewClient(auth, addr, skipVerify) + cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + addr := t.getAddr() + skipVerify := t.getInsecureSkipVerify() + systemAdminKey := t.getSystemAdminKey() + + auth, err := ionscale.LoadClientAuth(systemAdminKey) + if err != nil { + return err + } + + client, err := ionscale.NewClient(auth, addr, skipVerify) + if err != nil { + return err + } + + t.client = client + + if enableTailnetSelector { + savedTailnetID, err := ionscale.TailnetFromFile() + if err != nil { + return err + } + + if savedTailnetID == 0 && !cmd.Flags().Changed("tailnet") && !cmd.Flags().Changed("tailnet-id") { + return fmt.Errorf("flag --tailnet or --tailnet-id is required") + } + + if cmd.Flags().Changed("tailnet") && cmd.Flags().Changed("tailnet-id") { + return fmt.Errorf("flags --tailnet and --tailnet-id are mutually exclusive") + } + + tailnets, err := t.client.ListTailnets(cmd.Context(), connect.NewRequest(&ionscalev1.ListTailnetsRequest{})) + if err != nil { + return err + } + + for _, tailnet := range tailnets.Msg.Tailnet { + if tailnet.Id == savedTailnetID || tailnet.Id == t.tailnetID || tailnet.Name == t.tailnetName { + t.tailnet = tailnet + break + } + } + + if t.tailnet == nil { + return fmt.Errorf("requested tailnet not found or you are not authorized for this tailnet") + } + } + + return nil + } + + return cmd, t } -func (t *Target) getAddr() string { +func (t *target) getAddr() string { if len(t.addr) != 0 { return t.addr } return config.GetString(ionscaleAddr, "https://localhost:8443") } -func (t *Target) getInsecureSkipVerify() bool { +func (t *target) getInsecureSkipVerify() bool { if t.insecureSkipVerify { return true } return config.GetBool(ionscaleInsecureSkipVerify, false) } -func (t *Target) getSystemAdminKey() string { +func (t *target) getSystemAdminKey() string { if len(t.systemAdminKey) != 0 { return t.systemAdminKey } return config.GetString(ionscaleSystemAdminKey, config.GetString(ionscaleKeysSystemAdminKey, "")) } + +func (t *target) Addr() string { + return t.getAddr() +} + +func (t *target) Client() api.IonscaleServiceClient { + return t.client +} + +func (t *target) TailnetID() uint64 { + if t.tailnet == nil { + return 0 + } + return t.tailnet.Id +} diff --git a/internal/cmd/users.go b/internal/cmd/users.go index b740e82..cdce20f 100644 --- a/internal/cmd/users.go +++ b/internal/cmd/users.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "fmt" "github.com/bufbuild/connect-go" api "github.com/jsiebens/ionscale/pkg/gen/ionscale/v1" @@ -24,34 +23,15 @@ func userCommands() *cobra.Command { } func listUsersCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(true, &cobra.Command{ Use: "list", Short: "List users", SilenceUsage: true, - } + }) - var tailnetID uint64 - var tailnetName string - - var target = Target{} - target.prepareCommand(command) - command.Flags().StringVar(&tailnetName, "tailnet", "", "Tailnet name. Mutually exclusive with --tailnet-id.") - command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "Tailnet ID. Mutually exclusive with --tailnet.") - - command.PreRunE = checkRequiredTailnetAndTailnetIdFlags - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - - tailnet, err := findTailnet(client, tailnetName, tailnetID) - if err != nil { - return err - } - - req := api.ListUsersRequest{TailnetId: tailnet.Id} - resp, err := client.ListUsers(context.Background(), connect.NewRequest(&req)) + command.RunE = func(cmd *cobra.Command, args []string) error { + req := api.ListUsersRequest{TailnetId: tc.TailnetID()} + resp, err := tc.Client().ListUsers(cmd.Context(), connect.NewRequest(&req)) if err != nil { return err @@ -70,27 +50,21 @@ func listUsersCommand() *cobra.Command { } func deleteUserCommand() *cobra.Command { - command := &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "delete", Short: "Deletes a user", SilenceUsage: true, - } + }) var userID uint64 - var target = Target{} - target.prepareCommand(command) + command.Flags().Uint64Var(&userID, "user-id", 0, "User ID.") _ = command.MarkFlagRequired("user-id") - command.RunE = func(command *cobra.Command, args []string) error { - client, err := target.createGRPCClient() - if err != nil { - return err - } - + command.RunE = func(cmd *cobra.Command, args []string) error { req := api.DeleteUserRequest{UserId: userID} - if _, err := client.DeleteUser(context.Background(), connect.NewRequest(&req)); err != nil { + if _, err := tc.Client().DeleteUser(cmd.Context(), connect.NewRequest(&req)); err != nil { return err } diff --git a/internal/cmd/version.go b/internal/cmd/version.go index 7dbdc43..fc3e76a 100644 --- a/internal/cmd/version.go +++ b/internal/cmd/version.go @@ -1,7 +1,6 @@ package cmd import ( - "context" "fmt" "github.com/bufbuild/connect-go" "github.com/jsiebens/ionscale/internal/version" @@ -10,14 +9,11 @@ import ( ) func versionCommand() *cobra.Command { - var command = &cobra.Command{ + command, tc := prepareCommand(false, &cobra.Command{ Use: "version", Short: "Display version information", SilenceUsage: true, - } - - var target = Target{} - target.prepareCommand(command) + }) command.Run = func(cmd *cobra.Command, args []string) { clientVersion, clientRevision := version.GetReleaseInfo() @@ -27,16 +23,7 @@ Client: Git Revision: %s `, clientVersion, clientRevision) - client, err := target.createGRPCClient() - if err != nil { - fmt.Printf(` -Server: - Error: %s -`, err) - return - } - - resp, err := client.GetVersion(context.Background(), connect.NewRequest(&api.GetVersionRequest{})) + resp, err := tc.Client().GetVersion(cmd.Context(), connect.NewRequest(&api.GetVersionRequest{})) if err != nil { fmt.Printf(` Server: @@ -50,7 +37,7 @@ Server: Addr: %s Version: %s Git Revision: %s -`, target.getAddr(), resp.Msg.Version, resp.Msg.Revision) +`, tc.Addr(), resp.Msg.Version, resp.Msg.Revision) }