From 956bc08896cc845f561e8eacfaa2082f9b1f40c0 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Fri, 10 Jul 2026 16:01:45 -0400 Subject: [PATCH 1/3] add config options to up --- cmd/config/config.go | 140 +++++++++++++++++++++++++++ cmd/root.go | 4 +- cmd/up/client/client.go | 22 ++++- cmd/up/up_unix.go | 2 +- docs/pangolin.md | 1 + docs/pangolin_config.md | 22 +++++ docs/pangolin_config_get.md | 18 ++++ docs/pangolin_config_list.md | 18 ++++ docs/pangolin_config_path.md | 18 ++++ docs/pangolin_config_set.md | 36 +++++++ docs/pangolin_up_client.md | 2 +- internal/config/config.go | 182 +++++++++++++++++++++++++++++++++++ 12 files changed, 461 insertions(+), 4 deletions(-) create mode 100644 cmd/config/config.go create mode 100644 docs/pangolin_config.md create mode 100644 docs/pangolin_config_get.md create mode 100644 docs/pangolin_config_list.md create mode 100644 docs/pangolin_config_path.md create mode 100644 docs/pangolin_config_set.md diff --git a/cmd/config/config.go b/cmd/config/config.go new file mode 100644 index 0000000..a996b8b --- /dev/null +++ b/cmd/config/config.go @@ -0,0 +1,140 @@ +package configcmd + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/fosrl/cli/internal/config" + "github.com/spf13/cobra" +) + +func ConfigCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "config", + Short: "View and edit CLI configuration", + Long: `View and edit persistent CLI configuration without manually editing the config file.`, + } + + cmd.AddCommand(configPathCmd()) + cmd.AddCommand(configListCmd()) + cmd.AddCommand(configGetCmd()) + cmd.AddCommand(configSetCmd()) + + return cmd +} + +func configPathCmd() *cobra.Command { + return &cobra.Command{ + Use: "path", + Short: "Print the config file path", + RunE: func(cmd *cobra.Command, args []string) error { + path, err := config.ConfigFilePath() + if err != nil { + return err + } + fmt.Println(path) + return nil + }, + } +} + +func configListCmd() *cobra.Command { + return &cobra.Command{ + Use: "list", + Short: "List settable config keys", + RunE: func(cmd *cobra.Command, args []string) error { + for _, key := range config.ConfigOptions { + fmt.Fprintln(cmd.OutOrStdout(), key) + } + return nil + }, + } +} + +func configGetCmd() *cobra.Command { + return &cobra.Command{ + Use: "get [key]", + Short: "Get a config value, or dump config when no key is given", + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cfg := config.ConfigFromContext(cmd.Context()) + + if len(args) == 0 { + return dumpConfig(cfg) + } + + value, err := cfg.GetKey(args[0]) + if err != nil { + return err + } + fmt.Println(value) + return nil + }, + } +} + +func configSetCmd() *cobra.Command { + return &cobra.Command{ + Use: "set ", + Short: "Set a config value", + Long: `Set a config value and write it to the config file. + +Supported keys: + ` + strings.Join(config.SupportedConfigKeys(), "\n ") + ` + +Examples: + pangolin config set up.tunnel_dns true + pangolin config set up.upstream_dns 10.0.0.53 + pangolin config set up.upstream_dns 10.0.0.53,10.0.0.54 +`, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cfg := config.ConfigFromContext(cmd.Context()) + + if err := cfg.SetKey(args[0], args[1]); err != nil { + return err + } + if err := cfg.Save(); err != nil { + return err + } + + value, err := cfg.GetKey(args[0]) + if err != nil { + return err + } + fmt.Printf("%s = %s\n", args[0], value) + return nil + }, + } +} + +func dumpConfig(cfg *config.Config) error { + out := map[string]any{ + "log_level": cfg.LogLevel, + "log_file": cfg.LogFile, + "disable_update_check": cfg.DisableUpdateCheck, + "disable_companion_mode": cfg.DisableCompanionMode, + } + + up := map[string]any{} + if cfg.IsSet("up.tunnel_dns") { + up["tunnel_dns"] = cfg.GetBool("up.tunnel_dns") + } + if cfg.IsSet("up.override_dns") { + up["override_dns"] = cfg.GetBool("up.override_dns") + } + if cfg.IsSet("up.upstream_dns") { + up["upstream_dns"] = cfg.GetStringSlice("up.upstream_dns") + } + if len(up) > 0 { + out["up"] = up + } + + data, err := json.MarshalIndent(out, "", " ") + if err != nil { + return err + } + fmt.Println(string(data)) + return nil +} diff --git a/cmd/root.go b/cmd/root.go index 457c06f..377d662 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -12,6 +12,7 @@ import ( "github.com/fosrl/cli/cmd/auth/logout" "github.com/fosrl/cli/cmd/authdaemon" companioncmd "github.com/fosrl/cli/cmd/companion" + configcmd "github.com/fosrl/cli/cmd/config" "github.com/fosrl/cli/cmd/down" "github.com/fosrl/cli/cmd/list" "github.com/fosrl/cli/cmd/logs" @@ -60,6 +61,7 @@ func RootCommand(initResources bool) (*cobra.Command, error) { } cmd.AddCommand(selectcmd.SelectCmd()) cmd.AddCommand(list.ListCmd()) + cmd.AddCommand(configcmd.ConfigCmd()) // Platform-specific commands - nil on unsupported platforms if upCmd := up.UpCmd(); upCmd != nil { @@ -112,7 +114,7 @@ func RootCommand(initResources bool) (*cobra.Command, error) { func commandNeedsAuthInit(cmd *cobra.Command) bool { for c := cmd; c != nil; c = c.Parent() { - if c.Name() == "companion" { + if c.Name() == "companion" || c.Name() == "config" { return false } } diff --git a/cmd/up/client/client.go b/cmd/up/client/client.go index b8cac33..21d262e 100644 --- a/cmd/up/client/client.go +++ b/cmd/up/client/client.go @@ -82,7 +82,7 @@ func ClientUpCmd() *cobra.Command { cmd := &cobra.Command{ Use: "client", Short: "Start a client connection", - Long: "Bring up a client tunneled connection", + Long: `Bring up a client tunneled connection.`, PreRunE: func(cmd *cobra.Command, args []string) error { // `--id` and `--secret` must be specified together if (opts.ID == "") != (opts.Secret == "") { @@ -93,6 +93,9 @@ func ClientUpCmd() *cobra.Command { return errors.New("--silent and --attached options conflict") } + cfg := config.ConfigFromContext(cmd.Context()) + applyUpDefaults(cmd, &opts, cfg) + if err := validateDNSIP(opts.DNS, "netstack-dns"); err != nil { return err } @@ -136,6 +139,23 @@ func ClientUpCmd() *cobra.Command { return cmd } +// Precedence: flags > env/config > built-in defaults. +func applyUpDefaults(cmd *cobra.Command, opts *ClientUpCmdOpts, cfg *config.Config) { + if cfg == nil { + return + } + + if !cmd.Flags().Changed("tunnel-dns") && cfg.IsSet("up.tunnel_dns") { + opts.TunnelDNS = cfg.GetBool("up.tunnel_dns") + } + if !cmd.Flags().Changed("override-dns") && cfg.IsSet("up.override_dns") { + opts.OverrideDNS = cfg.GetBool("up.override_dns") + } + if !cmd.Flags().Changed("upstream-dns") && cfg.IsSet("up.upstream_dns") { + opts.UpstreamDNS = cfg.GetStringSlice("up.upstream_dns") + } +} + func clientUpMain(cmd *cobra.Command, opts *ClientUpCmdOpts, extraArgs []string) error { apiClient := api.FromContext(cmd.Context()) accountStore := config.AccountStoreFromContext(cmd.Context()) diff --git a/cmd/up/up_unix.go b/cmd/up/up_unix.go index c540293..d70f5bf 100644 --- a/cmd/up/up_unix.go +++ b/cmd/up/up_unix.go @@ -22,4 +22,4 @@ If ran with no subcommand, 'client' is passed. cmd.AddCommand(client.ClientUpCmd()) return cmd -} \ No newline at end of file +} diff --git a/docs/pangolin.md b/docs/pangolin.md index ee2179d..c570a4a 100644 --- a/docs/pangolin.md +++ b/docs/pangolin.md @@ -12,6 +12,7 @@ Pangolin CLI * [pangolin apply](pangolin_apply.md) - Apply commands * [pangolin auth](pangolin_auth.md) - Authentication commands +* [pangolin config](pangolin_config.md) - View and edit CLI configuration * [pangolin down](pangolin_down.md) - Stop a connection * [pangolin list](pangolin_list.md) - List resources and other items from the server * [pangolin login](pangolin_login.md) - Login to Pangolin diff --git a/docs/pangolin_config.md b/docs/pangolin_config.md new file mode 100644 index 0000000..b508e61 --- /dev/null +++ b/docs/pangolin_config.md @@ -0,0 +1,22 @@ +## pangolin config + +View and edit CLI configuration + +### Synopsis + +View and edit persistent CLI configuration without manually editing the config file. + +### Options + +``` + -h, --help help for config +``` + +### SEE ALSO + +* [pangolin](pangolin.md) - Pangolin CLI +* [pangolin config get](pangolin_config_get.md) - Get a config value, or dump config when no key is given +* [pangolin config list](pangolin_config_list.md) - List settable config keys +* [pangolin config path](pangolin_config_path.md) - Print the config file path +* [pangolin config set](pangolin_config_set.md) - Set a config value + diff --git a/docs/pangolin_config_get.md b/docs/pangolin_config_get.md new file mode 100644 index 0000000..740eaa5 --- /dev/null +++ b/docs/pangolin_config_get.md @@ -0,0 +1,18 @@ +## pangolin config get + +Get a config value, or dump config when no key is given + +``` +pangolin config get [key] [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### SEE ALSO + +* [pangolin config](pangolin_config.md) - View and edit CLI configuration + diff --git a/docs/pangolin_config_list.md b/docs/pangolin_config_list.md new file mode 100644 index 0000000..c3d21ed --- /dev/null +++ b/docs/pangolin_config_list.md @@ -0,0 +1,18 @@ +## pangolin config list + +List settable config keys + +``` +pangolin config list [flags] +``` + +### Options + +``` + -h, --help help for list +``` + +### SEE ALSO + +* [pangolin config](pangolin_config.md) - View and edit CLI configuration + diff --git a/docs/pangolin_config_path.md b/docs/pangolin_config_path.md new file mode 100644 index 0000000..c406a11 --- /dev/null +++ b/docs/pangolin_config_path.md @@ -0,0 +1,18 @@ +## pangolin config path + +Print the config file path + +``` +pangolin config path [flags] +``` + +### Options + +``` + -h, --help help for path +``` + +### SEE ALSO + +* [pangolin config](pangolin_config.md) - View and edit CLI configuration + diff --git a/docs/pangolin_config_set.md b/docs/pangolin_config_set.md new file mode 100644 index 0000000..82e3a1a --- /dev/null +++ b/docs/pangolin_config_set.md @@ -0,0 +1,36 @@ +## pangolin config set + +Set a config value + +### Synopsis + +Set a config value and write it to the config file. + +Supported keys: + log_level + disable_update_check + disable_companion_mode + up.tunnel_dns + up.upstream_dns + up.override_dns + +Examples: + pangolin config set up.tunnel_dns true + pangolin config set up.upstream_dns 10.0.0.53 + pangolin config set up.upstream_dns 10.0.0.53,10.0.0.54 + + +``` +pangolin config set [flags] +``` + +### Options + +``` + -h, --help help for set +``` + +### SEE ALSO + +* [pangolin config](pangolin_config.md) - View and edit CLI configuration + diff --git a/docs/pangolin_up_client.md b/docs/pangolin_up_client.md index c14bb91..68217d4 100644 --- a/docs/pangolin_up_client.md +++ b/docs/pangolin_up_client.md @@ -4,7 +4,7 @@ Start a client connection ### Synopsis -Bring up a client tunneled connection +Bring up a client tunneled connection. ``` pangolin up client [flags] diff --git a/internal/config/config.go b/internal/config/config.go index 96c4ecf..d040fec 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -23,6 +23,15 @@ type Config struct { DisableUpdateCheck bool `mapstructure:"disable_update_check" json:"disable_update_check"` DisableCompanionMode bool `mapstructure:"disable_companion_mode" json:"disable_companion_mode"` CompanionAppDataDirs CompanionAppDataDirs `mapstructure:"companion_app_data_dirs" json:"companion_app_data_dirs"` + Up UpConfig `mapstructure:"up" json:"up,omitempty"` +} + +// UpConfig holds persistent defaults for pangolin up DNS-related flags. +// Pointer bools distinguish unset from explicitly false. +type UpConfig struct { + TunnelDNS *bool `mapstructure:"tunnel_dns" json:"tunnel_dns,omitempty"` + UpstreamDNS []string `mapstructure:"upstream_dns" json:"upstream_dns,omitempty"` + OverrideDNS *bool `mapstructure:"override_dns" json:"override_dns,omitempty"` } // CompanionAppDataDirs holds per-platform overrides for the desktop app data directory. @@ -31,6 +40,23 @@ type CompanionAppDataDirs struct { Darwin string `mapstructure:"darwin" json:"darwin,omitempty"` } +// ConfigOptions is the registry of keys supported by pangolin config get/set/list. +var ConfigOptions = []string{ + "log_level", + "disable_update_check", + "disable_companion_mode", + "up.tunnel_dns", + "up.upstream_dns", + "up.override_dns", +} + +// SupportedConfigKeys returns the settable config keys. +func SupportedConfigKeys() []string { + out := make([]string, len(ConfigOptions)) + copy(out, ConfigOptions) + return out +} + // CompanionAppDataDirForPlatform returns the configured override for the current OS. func (c *Config) CompanionAppDataDirForPlatform() string { return companionAppDataDirForGOOS(c, runtime.GOOS) @@ -122,6 +148,144 @@ func (c *Config) SetCompanionModeEnabled(enabled bool) { c.DisableCompanionMode = !enabled } +// IsSet reports whether key was set via config file or environment variable. +// Keys that only have a built-in default are not considered set. +func (c *Config) IsSet(key string) bool { + return c.v.IsSet(key) +} + +// GetBool returns the boolean value for key from the merged config sources. +func (c *Config) GetBool(key string) bool { + return c.v.GetBool(key) +} + +// GetString returns the string value for key from the merged config sources. +func (c *Config) GetString(key string) string { + return c.v.GetString(key) +} + +// GetStringSlice returns the string slice value for key from the merged config sources. +func (c *Config) GetStringSlice(key string) []string { + return c.v.GetStringSlice(key) +} + +// ConfigFilePath returns the path to the CLI config file. +func ConfigFilePath() (string, error) { + dir, err := GetPangolinConfigDir() + if err != nil { + return "", err + } + return filepath.Join(dir, "config.json"), nil +} + +// SetKey sets a supported configuration key and syncs the in-memory struct. +func (c *Config) SetKey(key, value string) error { + switch key { + case "log_level": + level := logger.LogLevel(value) + switch level { + case logger.LogLevelDebug, logger.LogLevelInfo: + c.LogLevel = level + c.v.Set(key, value) + default: + return fmt.Errorf("invalid log_level %q: must be debug or info", value) + } + case "disable_update_check": + b, err := parseBool(value) + if err != nil { + return err + } + c.DisableUpdateCheck = b + c.v.Set(key, b) + case "disable_companion_mode": + b, err := parseBool(value) + if err != nil { + return err + } + c.DisableCompanionMode = b + c.v.Set(key, b) + case "up.tunnel_dns": + b, err := parseBool(value) + if err != nil { + return err + } + c.Up.TunnelDNS = &b + c.v.Set(key, b) + case "up.override_dns": + b, err := parseBool(value) + if err != nil { + return err + } + c.Up.OverrideDNS = &b + c.v.Set(key, b) + case "up.upstream_dns": + servers := splitCommaList(value) + c.Up.UpstreamDNS = servers + c.v.Set(key, servers) + default: + return fmt.Errorf("unknown config key %q; supported keys: %s", key, strings.Join(SupportedConfigKeys(), ", ")) + } + return nil +} + +// GetKey returns a string representation of a supported configuration key. +func (c *Config) GetKey(key string) (string, error) { + switch key { + case "log_level": + return string(c.LogLevel), nil + case "log_file": + return c.LogFile, nil + case "disable_update_check": + return fmt.Sprintf("%t", c.DisableUpdateCheck), nil + case "disable_companion_mode": + return fmt.Sprintf("%t", c.DisableCompanionMode), nil + case "up.tunnel_dns": + if !c.IsSet(key) { + return "", errConfigKeyUnset(key) + } + return fmt.Sprintf("%t", c.GetBool(key)), nil + case "up.override_dns": + if !c.IsSet(key) { + return "", errConfigKeyUnset(key) + } + return fmt.Sprintf("%t", c.GetBool(key)), nil + case "up.upstream_dns": + if !c.IsSet(key) { + return "", errConfigKeyUnset(key) + } + return strings.Join(c.GetStringSlice(key), ","), nil + default: + return "", fmt.Errorf("unknown config key %q; supported keys: %s", key, strings.Join(SupportedConfigKeys(), ", ")) + } +} + +func errConfigKeyUnset(key string) error { + return fmt.Errorf("config key %q is not set", key) +} + +func parseBool(value string) (bool, error) { + switch strings.ToLower(strings.TrimSpace(value)) { + case "true", "1", "yes", "on": + return true, nil + case "false", "0", "no", "off": + return false, nil + default: + return false, fmt.Errorf("invalid boolean value %q: use true or false", value) + } +} + +func splitCommaList(value string) []string { + parts := strings.Split(value, ",") + out := make([]string, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p != "" { + out = append(out, p) + } + } + return out +} + func (c *Config) Save() error { c.v.Set("log_level", c.LogLevel) c.v.Set("log_file", c.LogFile) @@ -129,6 +293,18 @@ func (c *Config) Save() error { c.v.Set("disable_companion_mode", c.DisableCompanionMode) c.v.Set("companion_app_data_dirs", c.CompanionAppDataDirs) + // Only persist up keys that were explicitly set so we do not write + // zero-value bools that would later look like intentional overrides. + if c.Up.TunnelDNS != nil { + c.v.Set("up.tunnel_dns", *c.Up.TunnelDNS) + } + if c.Up.OverrideDNS != nil { + c.v.Set("up.override_dns", *c.Up.OverrideDNS) + } + if c.Up.UpstreamDNS != nil { + c.v.Set("up.upstream_dns", c.Up.UpstreamDNS) + } + dir, err := GetPangolinConfigDir() if err != nil { return err @@ -138,6 +314,12 @@ func (c *Config) Save() error { } configFile := c.v.ConfigFileUsed() + if configFile == "" { + configFile, err = ConfigFilePath() + if err != nil { + return err + } + } if _, err := os.Stat(configFile); errors.Is(err, os.ErrNotExist) { return c.v.WriteConfigAs(configFile) } From 558ff0d3674ae941f45857273871195b4f8c723e Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Fri, 10 Jul 2026 18:05:04 -0400 Subject: [PATCH 2/3] only list approved alises --- cmd/list/aliases.go | 33 +++++++++++++++++++++++++-------- internal/api/client.go | 4 ++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/cmd/list/aliases.go b/cmd/list/aliases.go index 0cacc2e..dbf3bc3 100644 --- a/cmd/list/aliases.go +++ b/cmd/list/aliases.go @@ -17,6 +17,7 @@ const aliasesPageSize = 1000 type aliasesFetchOptions struct { includeLabels bool labelFilter []string + status string } func aliasesCmd() *cobra.Command { @@ -43,6 +44,7 @@ func aliasesCmd() *cobra.Command { requested := aliasesFetchOptions{ includeLabels: withLabels, labelFilter: labelFilter, + status: "approved", } data, err := fetchAllAliases(apiClient, orgID, requested) @@ -71,10 +73,7 @@ func fetchAllAliases(apiClient *api.Client, orgID string, requested aliasesFetch var combined api.ListUserResourceAliasesData for page := 1; ; page++ { - pageData, err := apiClient.ListUserResourceAliases(orgID, page, aliasesPageSize, api.ListUserResourceAliasesOptions{ - IncludeLabels: effective.includeLabels, - LabelFilter: effective.labelFilter, - }) + pageData, err := apiClient.ListUserResourceAliases(orgID, page, aliasesPageSize, listAliasesAPIOptions(effective)) if err != nil { return nil, err } @@ -99,6 +98,18 @@ func fetchAllAliases(apiClient *api.Client, orgID string, requested aliasesFetch } func resolveAliasesFetchOptions(apiClient *api.Client, orgID string, requested aliasesFetchOptions) (aliasesFetchOptions, bool, error) { + effective, clientSideFilter, err := resolveAliasesFetchOptionsWithStatus(apiClient, orgID, requested) + if err == nil || !isBadRequest(err) || requested.status == "" { + return effective, clientSideFilter, err + } + + // Older servers reject unknown query params such as status; retry without it. + withoutStatus := requested + withoutStatus.status = "" + return resolveAliasesFetchOptionsWithStatus(apiClient, orgID, withoutStatus) +} + +func resolveAliasesFetchOptionsWithStatus(apiClient *api.Client, orgID string, requested aliasesFetchOptions) (aliasesFetchOptions, bool, error) { effective := requested if err := probeAliasesPage(apiClient, orgID, effective); err == nil { @@ -111,6 +122,7 @@ func resolveAliasesFetchOptions(apiClient *api.Client, orgID string, requested a effective = aliasesFetchOptions{ includeLabels: true, labelFilter: nil, + status: requested.status, } if err := probeAliasesPage(apiClient, orgID, effective); err == nil { return effective, true, nil @@ -119,7 +131,7 @@ func resolveAliasesFetchOptions(apiClient *api.Client, orgID string, requested a } } - effective = aliasesFetchOptions{} + effective = aliasesFetchOptions{status: requested.status} if err := probeAliasesPage(apiClient, orgID, effective); err != nil { return effective, false, err } @@ -127,11 +139,16 @@ func resolveAliasesFetchOptions(apiClient *api.Client, orgID string, requested a return effective, false, nil } -func probeAliasesPage(apiClient *api.Client, orgID string, opts aliasesFetchOptions) error { - _, err := apiClient.ListUserResourceAliases(orgID, 1, 1, api.ListUserResourceAliasesOptions{ +func listAliasesAPIOptions(opts aliasesFetchOptions) api.ListUserResourceAliasesOptions { + return api.ListUserResourceAliasesOptions{ IncludeLabels: opts.includeLabels, LabelFilter: opts.labelFilter, - }) + Status: opts.status, + } +} + +func probeAliasesPage(apiClient *api.Client, orgID string, opts aliasesFetchOptions) error { + _, err := apiClient.ListUserResourceAliases(orgID, 1, 1, listAliasesAPIOptions(opts)) return err } diff --git a/internal/api/client.go b/internal/api/client.go index 56f4893..3986297 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -356,6 +356,7 @@ func (c *Client) CheckOrgUserAccess(orgID, userID string) (*CheckOrgUserAccessRe type ListUserResourceAliasesOptions struct { IncludeLabels bool LabelFilter []string + Status string } // ListUserResourceAliases returns one page of host-mode private site resource aliases for the user in the org. @@ -372,6 +373,9 @@ func (c *Client) ListUserResourceAliases(orgID string, page, pageSize int, opts if len(opts.LabelFilter) > 0 { query["labels"] = strings.Join(opts.LabelFilter, ",") } + if opts.Status != "" { + query["status"] = opts.Status + } reqOpts := RequestOptions{Query: query} if err := c.Get(path, &data, reqOpts); err != nil { return nil, err From 2f97dd064f6c2c9ec7cc3214ca2414f31c48b77c Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Wed, 15 Jul 2026 15:47:29 -0400 Subject: [PATCH 3/3] add config show command and update docs --- cmd/config/config.go | 21 ++++++++++++++------- docs/pangolin_config.md | 3 ++- docs/pangolin_config_get.md | 4 ++-- docs/pangolin_config_show.md | 18 ++++++++++++++++++ docs/pangolin_up.md | 2 +- docs/pangolin_up_client.md | 7 ++++--- 6 files changed, 41 insertions(+), 14 deletions(-) create mode 100644 docs/pangolin_config_show.md diff --git a/cmd/config/config.go b/cmd/config/config.go index a996b8b..4d5a93e 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -17,6 +17,7 @@ func ConfigCmd() *cobra.Command { } cmd.AddCommand(configPathCmd()) + cmd.AddCommand(configShowCmd()) cmd.AddCommand(configListCmd()) cmd.AddCommand(configGetCmd()) cmd.AddCommand(configSetCmd()) @@ -39,6 +40,16 @@ func configPathCmd() *cobra.Command { } } +func configShowCmd() *cobra.Command { + return &cobra.Command{ + Use: "show", + Short: "Print the current config contents", + RunE: func(cmd *cobra.Command, args []string) error { + return dumpConfig(config.ConfigFromContext(cmd.Context())) + }, + } +} + func configListCmd() *cobra.Command { return &cobra.Command{ Use: "list", @@ -54,16 +65,12 @@ func configListCmd() *cobra.Command { func configGetCmd() *cobra.Command { return &cobra.Command{ - Use: "get [key]", - Short: "Get a config value, or dump config when no key is given", - Args: cobra.MaximumNArgs(1), + Use: "get ", + Short: "Get a config value", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cfg := config.ConfigFromContext(cmd.Context()) - if len(args) == 0 { - return dumpConfig(cfg) - } - value, err := cfg.GetKey(args[0]) if err != nil { return err diff --git a/docs/pangolin_config.md b/docs/pangolin_config.md index b508e61..e232889 100644 --- a/docs/pangolin_config.md +++ b/docs/pangolin_config.md @@ -15,8 +15,9 @@ View and edit persistent CLI configuration without manually editing the config f ### SEE ALSO * [pangolin](pangolin.md) - Pangolin CLI -* [pangolin config get](pangolin_config_get.md) - Get a config value, or dump config when no key is given +* [pangolin config get](pangolin_config_get.md) - Get a config value * [pangolin config list](pangolin_config_list.md) - List settable config keys * [pangolin config path](pangolin_config_path.md) - Print the config file path * [pangolin config set](pangolin_config_set.md) - Set a config value +* [pangolin config show](pangolin_config_show.md) - Print the current config contents diff --git a/docs/pangolin_config_get.md b/docs/pangolin_config_get.md index 740eaa5..2fac4ac 100644 --- a/docs/pangolin_config_get.md +++ b/docs/pangolin_config_get.md @@ -1,9 +1,9 @@ ## pangolin config get -Get a config value, or dump config when no key is given +Get a config value ``` -pangolin config get [key] [flags] +pangolin config get [flags] ``` ### Options diff --git a/docs/pangolin_config_show.md b/docs/pangolin_config_show.md new file mode 100644 index 0000000..513b508 --- /dev/null +++ b/docs/pangolin_config_show.md @@ -0,0 +1,18 @@ +## pangolin config show + +Print the current config contents + +``` +pangolin config show [flags] +``` + +### Options + +``` + -h, --help help for show +``` + +### SEE ALSO + +* [pangolin config](pangolin_config.md) - View and edit CLI configuration + diff --git a/docs/pangolin_up.md b/docs/pangolin_up.md index 60a90a9..36f8237 100644 --- a/docs/pangolin_up.md +++ b/docs/pangolin_up.md @@ -25,6 +25,7 @@ pangolin up [flags] --interface-name name Interface name (default "pangolin") --log-level string Log level (default "info") --mtu int Maximum transmission unit (default 1280) + --netstack-dns server DNS server to use for Netstack. This handles DNS resolution outside of the upstream servers. --org string Organization ID (default: selected organization if logged in) --override-dns When enabled, the client uses custom DNS servers to resolve internal resources and aliases. This overrides your system's default DNS settings. Queries that cannot be resolved as a Pangolin resource will be forwarded to your configured Upstream DNS Server. (default true) --ping-interval interval Ping interval (default 5s) @@ -33,7 +34,6 @@ pangolin up [flags] --silent Disable TUI and run silently when detached --tls-client-cert path TLS client certificate path --tunnel-dns When enabled, DNS queries are routed through the tunnel for remote resolution. To ensure queries are tunneled correctly, you must define the DNS server as a Pangolin resource and enter its address as an Upstream DNS Server. - --netstack-dns server DNS server to use for Netstack. This handles DNS resolution outside of the upstream servers --upstream-dns strings List of DNS servers to use for external DNS resolution if overriding system DNS ``` diff --git a/docs/pangolin_up_client.md b/docs/pangolin_up_client.md index 68217d4..e7c1c67 100644 --- a/docs/pangolin_up_client.md +++ b/docs/pangolin_up_client.md @@ -22,6 +22,7 @@ pangolin up client [flags] --interface-name name Interface name (default "pangolin") --log-level string Log level (default "info") --mtu int Maximum transmission unit (default 1280) + --netstack-dns server DNS server to use for Netstack. This handles DNS resolution outside of the upstream servers. --org string Organization ID (default: selected organization if logged in) --override-dns When enabled, the client uses custom DNS servers to resolve internal resources and aliases. This overrides your system's default DNS settings. Queries that cannot be resolved as a Pangolin resource will be forwarded to your configured Upstream DNS Server. (default true) --ping-interval interval Ping interval (default 5s) @@ -30,10 +31,10 @@ pangolin up client [flags] --silent Disable TUI and run silently when detached --tls-client-cert path TLS client certificate path --tunnel-dns When enabled, DNS queries are routed through the tunnel for remote resolution. To ensure queries are tunneled correctly, you must define the DNS server as a Pangolin resource and enter its address as an Upstream DNS Server. - --netstack-dns server DNS server to use for Netstack. This handles DNS resolution outside of the upstream servers if overriding using the system DNS - --upstream-dns strings List of DNS servers to use for external DNS resolution if overriding using the system DNS + --upstream-dns strings List of DNS servers to use for external DNS resolution if overriding system DNS ``` ### SEE ALSO -- [pangolin up](pangolin_up.md) - Start a connection +* [pangolin up](pangolin_up.md) - Start a connection +