cmd/headscale/cli: add confirmAction helper for force/prompt patterns
Centralise the repeated force-flag-check + YesNo-prompt logic into a single confirmAction(cmd, prompt) helper. Callers still decide what to return on decline (error, message, or nil).
This commit is contained in:
parent
7b7b270126
commit
6c08b49d63
@ -262,32 +262,23 @@ var deleteNodeCmd = &cobra.Command{
|
|||||||
NodeId: identifier,
|
NodeId: identifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm := false
|
if !confirmAction(cmd, fmt.Sprintf(
|
||||||
|
"Do you want to remove the node %s?",
|
||||||
force, _ := cmd.Flags().GetBool("force")
|
getResponse.GetNode().GetName(),
|
||||||
if !force {
|
)) {
|
||||||
confirm = util.YesNo(fmt.Sprintf(
|
return printOutput(cmd, map[string]string{"Result": "Node not deleted"}, "Node not deleted")
|
||||||
"Do you want to remove the node %s?",
|
|
||||||
getResponse.GetNode().GetName(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if confirm || force {
|
_, err = client.DeleteNode(ctx, deleteRequest)
|
||||||
response, err := client.DeleteNode(ctx, deleteRequest)
|
if err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("deleting node: %w", err)
|
||||||
return fmt.Errorf("deleting node: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = response // consumed for structured output if needed
|
|
||||||
|
|
||||||
return printOutput(
|
|
||||||
cmd,
|
|
||||||
map[string]string{"Result": "Node deleted"},
|
|
||||||
"Node deleted",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return printOutput(cmd, map[string]string{"Result": "Node not deleted"}, "Node not deleted")
|
return printOutput(
|
||||||
|
cmd,
|
||||||
|
map[string]string{"Result": "Node deleted"},
|
||||||
|
"Node deleted",
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,14 +298,7 @@ If you remove IPv4 or IPv6 prefixes from the config,
|
|||||||
it can be run to remove the IPs that should no longer
|
it can be run to remove the IPs that should no longer
|
||||||
be assigned to nodes.`,
|
be assigned to nodes.`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
confirm := false
|
if !confirmAction(cmd, "Are you sure that you want to assign/remove IPs to/from nodes?") {
|
||||||
|
|
||||||
force, _ := cmd.Flags().GetBool("force")
|
|
||||||
if !force {
|
|
||||||
confirm = util.YesNo("Are you sure that you want to assign/remove IPs to/from nodes?")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !confirm && !force {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/juanfont/headscale/hscontrol/db"
|
"github.com/juanfont/headscale/hscontrol/db"
|
||||||
"github.com/juanfont/headscale/hscontrol/policy"
|
"github.com/juanfont/headscale/hscontrol/policy"
|
||||||
"github.com/juanfont/headscale/hscontrol/types"
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"tailscale.com/types/views"
|
"tailscale.com/types/views"
|
||||||
)
|
)
|
||||||
@ -49,87 +48,7 @@ var getPolicy = &cobra.Command{
|
|||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
var policyData string
|
var policyData string
|
||||||
if bypass, _ := cmd.Flags().GetBool(bypassFlag); bypass {
|
if bypass, _ := cmd.Flags().GetBool(bypassFlag); bypass {
|
||||||
confirm := false
|
if !confirmAction(cmd, "DO NOT run this command if an instance of headscale is running, are you sure headscale is not running?") {
|
||||||
|
|
||||||
force, _ := cmd.Flags().GetBool("force")
|
|
||||||
if !force {
|
|
||||||
confirm = util.YesNo("DO NOT run this command if an instance of headscale is running, are you sure headscale is not running?")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !confirm && !force {
|
|
||||||
return errAborted
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg, err := types.LoadServerConfig()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("loading config: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
d, err := db.NewHeadscaleDatabase(cfg, nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("opening database: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pol, err := d.GetPolicy()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("loading policy from database: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policyData = pol.Data
|
|
||||||
} else {
|
|
||||||
ctx, client, conn, cancel, err := newHeadscaleCLIWithConfig()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("connecting to headscale: %w", err)
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
response, err := client.GetPolicy(ctx, &v1.GetPolicyRequest{})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("loading ACL policy: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policyData = response.GetPolicy()
|
|
||||||
}
|
|
||||||
|
|
||||||
// This does not pass output format as we don't support yaml, json or
|
|
||||||
// json-line output for this command. It is HuJSON already.
|
|
||||||
fmt.Println(policyData)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var setPolicy = &cobra.Command{
|
|
||||||
Use: "set",
|
|
||||||
Short: "Updates the ACL Policy",
|
|
||||||
Long: `
|
|
||||||
Updates the existing ACL Policy with the provided policy. The policy must be a valid HuJSON object.
|
|
||||||
This command only works when the acl.policy_mode is set to "db", and the policy will be stored in the database.`,
|
|
||||||
Aliases: []string{"put", "update"},
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
policyPath, _ := cmd.Flags().GetString("file")
|
|
||||||
|
|
||||||
f, err := os.Open(policyPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("opening policy file: %w", err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
policyBytes, err := io.ReadAll(f)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("reading policy file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if bypass, _ := cmd.Flags().GetBool(bypassFlag); bypass {
|
|
||||||
confirm := false
|
|
||||||
|
|
||||||
force, _ := cmd.Flags().GetBool("force")
|
|
||||||
if !force {
|
|
||||||
confirm = util.YesNo("DO NOT run this command if an instance of headscale is running, are you sure headscale is not running?")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !confirm && !force {
|
|
||||||
return errAborted
|
return errAborted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
|
||||||
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
||||||
"github.com/pterm/pterm"
|
"github.com/pterm/pterm"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@ -140,28 +139,21 @@ var destroyUserCmd = &cobra.Command{
|
|||||||
|
|
||||||
user := users.GetUsers()[0]
|
user := users.GetUsers()[0]
|
||||||
|
|
||||||
confirm := false
|
if !confirmAction(cmd, fmt.Sprintf(
|
||||||
|
"Do you want to remove the user %q (%d) and any associated preauthkeys?",
|
||||||
force, _ := cmd.Flags().GetBool("force")
|
user.GetName(), user.GetId(),
|
||||||
if !force {
|
)) {
|
||||||
confirm = util.YesNo(fmt.Sprintf(
|
return printOutput(cmd, map[string]string{"Result": "User not destroyed"}, "User not destroyed")
|
||||||
"Do you want to remove the user %q (%d) and any associated preauthkeys?",
|
|
||||||
user.GetName(), user.GetId(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if confirm || force {
|
deleteRequest := &v1.DeleteUserRequest{Id: user.GetId()}
|
||||||
request := &v1.DeleteUserRequest{Id: user.GetId()}
|
|
||||||
|
|
||||||
response, err := client.DeleteUser(ctx, request)
|
response, err := client.DeleteUser(ctx, deleteRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("destroying user: %w", err)
|
return fmt.Errorf("destroying user: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
return printOutput(cmd, response, "User destroyed")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return printOutput(cmd, map[string]string{"Result": "User not destroyed"}, "User not destroyed")
|
return printOutput(cmd, response, "User destroyed")
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -221,6 +221,17 @@ func printOutput(cmd *cobra.Command, result any, override string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// confirmAction returns true when the user confirms a prompt, or when
|
||||||
|
// --force is set. Callers decide what to do when it returns false.
|
||||||
|
func confirmAction(cmd *cobra.Command, prompt string) bool {
|
||||||
|
force, _ := cmd.Flags().GetBool("force")
|
||||||
|
if force {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.YesNo(prompt)
|
||||||
|
}
|
||||||
|
|
||||||
// printListOutput checks the --output flag: when a machine-readable format is
|
// printListOutput checks the --output flag: when a machine-readable format is
|
||||||
// requested it serialises data as JSON/YAML; otherwise it calls renderTable
|
// requested it serialises data as JSON/YAML; otherwise it calls renderTable
|
||||||
// to produce the human-readable pterm table.
|
// to produce the human-readable pterm table.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user