mirror of
https://github.com/juanfont/headscale.git
synced 2025-07-29 11:55:43 +00:00
derp
This commit is contained in:
parent
024ed59ea9
commit
8253d588c6
41
CHANGELOG.md
41
CHANGELOG.md
@ -41,6 +41,47 @@ systemctl start headscale
|
|||||||
|
|
||||||
### BREAKING
|
### BREAKING
|
||||||
|
|
||||||
|
- **CLI: Remove deprecated flags**
|
||||||
|
- `--identifier` flag removed - use `--node` or `--user` instead
|
||||||
|
- `--namespace` flag removed - use `--user` instead
|
||||||
|
|
||||||
|
**Command changes:**
|
||||||
|
```bash
|
||||||
|
# Before
|
||||||
|
headscale nodes expire --identifier 123
|
||||||
|
headscale nodes rename --identifier 123 new-name
|
||||||
|
headscale nodes delete --identifier 123
|
||||||
|
headscale nodes move --identifier 123 --user 456
|
||||||
|
headscale nodes list-routes --identifier 123
|
||||||
|
|
||||||
|
# After
|
||||||
|
headscale nodes expire --node 123
|
||||||
|
headscale nodes rename --node 123 new-name
|
||||||
|
headscale nodes delete --node 123
|
||||||
|
headscale nodes move --node 123 --user 456
|
||||||
|
headscale nodes list-routes --node 123
|
||||||
|
|
||||||
|
# Before
|
||||||
|
headscale users destroy --identifier 123
|
||||||
|
headscale users rename --identifier 123 --new-name john
|
||||||
|
headscale users list --identifier 123
|
||||||
|
|
||||||
|
# After
|
||||||
|
headscale users destroy --user 123
|
||||||
|
headscale users rename --user 123 --new-name john
|
||||||
|
headscale users list --user 123
|
||||||
|
|
||||||
|
# Before
|
||||||
|
headscale nodes register --namespace myuser nodekey
|
||||||
|
headscale nodes list --namespace myuser
|
||||||
|
headscale preauthkeys create --namespace myuser
|
||||||
|
|
||||||
|
# After
|
||||||
|
headscale nodes register --user myuser nodekey
|
||||||
|
headscale nodes list --user myuser
|
||||||
|
headscale preauthkeys create --user myuser
|
||||||
|
```
|
||||||
|
|
||||||
- Policy: Zero or empty destination port is no longer allowed
|
- Policy: Zero or empty destination port is no longer allowed
|
||||||
[#2606](https://github.com/juanfont/headscale/pull/2606)
|
[#2606](https://github.com/juanfont/headscale/pull/2606)
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(apiKeysCmd)
|
rootCmd.AddCommand(apiKeysCmd)
|
||||||
apiKeysCmd.AddCommand(listAPIKeys)
|
apiKeysCmd.AddCommand(listAPIKeys)
|
||||||
@ -98,7 +97,6 @@ var listAPIKeys = &cobra.Command{
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -148,7 +146,6 @@ If you loose a key, create a new one and revoke (expire) the old one.`,
|
|||||||
SuccessOutput(response.GetApiKey(), response.GetApiKey(), output)
|
SuccessOutput(response.GetApiKey(), response.GetApiKey(), output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -185,7 +182,6 @@ var expireAPIKeyCmd = &cobra.Command{
|
|||||||
SuccessOutput(response, "Key expired", output)
|
SuccessOutput(response, "Key expired", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -222,7 +218,6 @@ var deleteAPIKeyCmd = &cobra.Command{
|
|||||||
SuccessOutput(response, "Key deleted", output)
|
SuccessOutput(response, "Key deleted", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,6 @@ const (
|
|||||||
errPreAuthKeyMalformed = Error("key is malformed. expected 64 hex characters with `nodekey` prefix")
|
errPreAuthKeyMalformed = Error("key is malformed. expected 64 hex characters with `nodekey` prefix")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors
|
|
||||||
type Error string
|
|
||||||
|
|
||||||
func (e Error) Error() string { return string(e) }
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(debugCmd)
|
rootCmd.AddCommand(debugCmd)
|
||||||
|
|
||||||
@ -30,11 +25,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
createNodeCmd.Flags().StringP("user", "u", "", "User")
|
createNodeCmd.Flags().StringP("user", "u", "", "User")
|
||||||
|
|
||||||
createNodeCmd.Flags().StringP("namespace", "n", "", "User")
|
|
||||||
createNodeNamespaceFlag := createNodeCmd.Flags().Lookup("namespace")
|
|
||||||
createNodeNamespaceFlag.Deprecated = deprecateNamespaceMessage
|
|
||||||
createNodeNamespaceFlag.Hidden = true
|
|
||||||
|
|
||||||
err = createNodeCmd.MarkFlagRequired("user")
|
err = createNodeCmd.MarkFlagRequired("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("")
|
log.Fatal().Err(err).Msg("")
|
||||||
@ -60,7 +50,7 @@ var createNodeCmd = &cobra.Command{
|
|||||||
Use: "create-node",
|
Use: "create-node",
|
||||||
Short: "Create a node that can be registered with `nodes register <>` command",
|
Short: "Create a node that can be registered with `nodes register <>` command",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
user, err := cmd.Flags().GetString("user")
|
user, err := cmd.Flags().GetString("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -129,7 +119,6 @@ var createNodeCmd = &cobra.Command{
|
|||||||
SuccessOutput(response.GetNode(), "Node created", output)
|
SuccessOutput(response.GetNode(), "Node created", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -63,12 +63,6 @@ func TestCreateNodeCommandFlags(t *testing.T) {
|
|||||||
assert.NotNil(t, routeFlag)
|
assert.NotNil(t, routeFlag)
|
||||||
assert.Equal(t, "r", routeFlag.Shorthand)
|
assert.Equal(t, "r", routeFlag.Shorthand)
|
||||||
|
|
||||||
// Test deprecated namespace flag
|
|
||||||
namespaceFlag := createNodeCmd.Flags().Lookup("namespace")
|
|
||||||
assert.NotNil(t, namespaceFlag)
|
|
||||||
assert.Equal(t, "n", namespaceFlag.Shorthand)
|
|
||||||
assert.True(t, namespaceFlag.Hidden, "Namespace flag should be hidden")
|
|
||||||
assert.Equal(t, deprecateNamespaceMessage, namespaceFlag.Deprecated)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateNodeCommandRequiredFlags(t *testing.T) {
|
func TestCreateNodeCommandRequiredFlags(t *testing.T) {
|
||||||
@ -134,8 +128,6 @@ func TestCreateNodeCommandFlagDescriptions(t *testing.T) {
|
|||||||
routeFlag := createNodeCmd.Flags().Lookup("route")
|
routeFlag := createNodeCmd.Flags().Lookup("route")
|
||||||
assert.Contains(t, routeFlag.Usage, "routes to advertise")
|
assert.Contains(t, routeFlag.Usage, "routes to advertise")
|
||||||
|
|
||||||
namespaceFlag := createNodeCmd.Flags().Lookup("namespace")
|
|
||||||
assert.Equal(t, "User", namespaceFlag.Usage) // Same as user flag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: We can't easily test the actual execution of create-node because:
|
// Note: We can't easily test the actual execution of create-node because:
|
||||||
|
@ -22,7 +22,7 @@ var generatePrivateKeyCmd = &cobra.Command{
|
|||||||
Use: "private-key",
|
Use: "private-key",
|
||||||
Short: "Generate a private key for the headscale server",
|
Short: "Generate a private key for the headscale server",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
machineKey := key.NewMachine()
|
machineKey := key.NewMachine()
|
||||||
|
|
||||||
machineKeyStr, err := machineKey.MarshalText()
|
machineKeyStr, err := machineKey.MarshalText()
|
||||||
|
@ -15,6 +15,11 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors
|
||||||
|
type Error string
|
||||||
|
|
||||||
|
func (e Error) Error() string { return string(e) }
|
||||||
|
|
||||||
const (
|
const (
|
||||||
errMockOidcClientIDNotDefined = Error("MOCKOIDC_CLIENT_ID not defined")
|
errMockOidcClientIDNotDefined = Error("MOCKOIDC_CLIENT_ID not defined")
|
||||||
errMockOidcClientSecretNotDefined = Error("MOCKOIDC_CLIENT_SECRET not defined")
|
errMockOidcClientSecretNotDefined = Error("MOCKOIDC_CLIENT_SECRET not defined")
|
||||||
|
@ -32,27 +32,13 @@ func init() {
|
|||||||
// Display options
|
// Display options
|
||||||
listNodesCmd.Flags().BoolP("tags", "t", false, "Show tags")
|
listNodesCmd.Flags().BoolP("tags", "t", false, "Show tags")
|
||||||
listNodesCmd.Flags().String("columns", "", "Comma-separated list of columns to display")
|
listNodesCmd.Flags().String("columns", "", "Comma-separated list of columns to display")
|
||||||
// Backward compatibility
|
|
||||||
listNodesCmd.Flags().StringP("namespace", "n", "", "User")
|
|
||||||
listNodesNamespaceFlag := listNodesCmd.Flags().Lookup("namespace")
|
|
||||||
listNodesNamespaceFlag.Deprecated = deprecateNamespaceMessage
|
|
||||||
listNodesNamespaceFlag.Hidden = true
|
|
||||||
nodeCmd.AddCommand(listNodesCmd)
|
nodeCmd.AddCommand(listNodesCmd)
|
||||||
|
|
||||||
listNodeRoutesCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
listNodeRoutesCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
||||||
listNodeRoutesCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID) - deprecated, use --node")
|
|
||||||
identifierFlag := listNodeRoutesCmd.Flags().Lookup("identifier")
|
|
||||||
identifierFlag.Deprecated = "use --node"
|
|
||||||
identifierFlag.Hidden = true
|
|
||||||
nodeCmd.AddCommand(listNodeRoutesCmd)
|
nodeCmd.AddCommand(listNodeRoutesCmd)
|
||||||
|
|
||||||
registerNodeCmd.Flags().StringP("user", "u", "", "User")
|
registerNodeCmd.Flags().StringP("user", "u", "", "User")
|
||||||
|
|
||||||
registerNodeCmd.Flags().StringP("namespace", "n", "", "User")
|
|
||||||
registerNodeNamespaceFlag := registerNodeCmd.Flags().Lookup("namespace")
|
|
||||||
registerNodeNamespaceFlag.Deprecated = deprecateNamespaceMessage
|
|
||||||
registerNodeNamespaceFlag.Hidden = true
|
|
||||||
|
|
||||||
err := registerNodeCmd.MarkFlagRequired("user")
|
err := registerNodeCmd.MarkFlagRequired("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
@ -65,40 +51,24 @@ func init() {
|
|||||||
nodeCmd.AddCommand(registerNodeCmd)
|
nodeCmd.AddCommand(registerNodeCmd)
|
||||||
|
|
||||||
expireNodeCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
expireNodeCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
||||||
expireNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID) - deprecated, use --node")
|
|
||||||
identifierFlag = expireNodeCmd.Flags().Lookup("identifier")
|
|
||||||
identifierFlag.Deprecated = "use --node"
|
|
||||||
identifierFlag.Hidden = true
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
nodeCmd.AddCommand(expireNodeCmd)
|
nodeCmd.AddCommand(expireNodeCmd)
|
||||||
|
|
||||||
renameNodeCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
renameNodeCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
||||||
renameNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID) - deprecated, use --node")
|
|
||||||
identifierFlag = renameNodeCmd.Flags().Lookup("identifier")
|
|
||||||
identifierFlag.Deprecated = "use --node"
|
|
||||||
identifierFlag.Hidden = true
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
nodeCmd.AddCommand(renameNodeCmd)
|
nodeCmd.AddCommand(renameNodeCmd)
|
||||||
|
|
||||||
deleteNodeCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
deleteNodeCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
||||||
deleteNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID) - deprecated, use --node")
|
|
||||||
identifierFlag = deleteNodeCmd.Flags().Lookup("identifier")
|
|
||||||
identifierFlag.Deprecated = "use --node"
|
|
||||||
identifierFlag.Hidden = true
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
nodeCmd.AddCommand(deleteNodeCmd)
|
nodeCmd.AddCommand(deleteNodeCmd)
|
||||||
|
|
||||||
moveNodeCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
moveNodeCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
||||||
moveNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID) - deprecated, use --node")
|
|
||||||
identifierFlag = moveNodeCmd.Flags().Lookup("identifier")
|
|
||||||
identifierFlag.Deprecated = "use --node"
|
|
||||||
identifierFlag.Hidden = true
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
@ -106,24 +76,19 @@ func init() {
|
|||||||
|
|
||||||
moveNodeCmd.Flags().Uint64P("user", "u", 0, "New user")
|
moveNodeCmd.Flags().Uint64P("user", "u", 0, "New user")
|
||||||
|
|
||||||
moveNodeCmd.Flags().StringP("namespace", "n", "", "User")
|
|
||||||
moveNodeNamespaceFlag := moveNodeCmd.Flags().Lookup("namespace")
|
|
||||||
moveNodeNamespaceFlag.Deprecated = deprecateNamespaceMessage
|
|
||||||
moveNodeNamespaceFlag.Hidden = true
|
|
||||||
|
|
||||||
err = moveNodeCmd.MarkFlagRequired("user")
|
err = moveNodeCmd.MarkFlagRequired("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
nodeCmd.AddCommand(moveNodeCmd)
|
nodeCmd.AddCommand(moveNodeCmd)
|
||||||
|
|
||||||
tagCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
|
tagCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
||||||
tagCmd.MarkFlagRequired("identifier")
|
tagCmd.MarkFlagRequired("node")
|
||||||
tagCmd.Flags().StringSliceP("tags", "t", []string{}, "List of tags to add to the node")
|
tagCmd.Flags().StringSliceP("tags", "t", []string{}, "List of tags to add to the node")
|
||||||
nodeCmd.AddCommand(tagCmd)
|
nodeCmd.AddCommand(tagCmd)
|
||||||
|
|
||||||
approveRoutesCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
|
approveRoutesCmd.Flags().StringP("node", "n", "", "Node identifier (ID, name, hostname, or IP)")
|
||||||
approveRoutesCmd.MarkFlagRequired("identifier")
|
approveRoutesCmd.MarkFlagRequired("node")
|
||||||
approveRoutesCmd.Flags().StringSliceP("routes", "r", []string{}, `List of routes that will be approved (comma-separated, e.g. "10.0.0.0/8,192.168.0.0/24" or empty string to remove all approved routes)`)
|
approveRoutesCmd.Flags().StringSliceP("routes", "r", []string{}, `List of routes that will be approved (comma-separated, e.g. "10.0.0.0/8,192.168.0.0/24" or empty string to remove all approved routes)`)
|
||||||
nodeCmd.AddCommand(approveRoutesCmd)
|
nodeCmd.AddCommand(approveRoutesCmd)
|
||||||
|
|
||||||
@ -140,7 +105,7 @@ var registerNodeCmd = &cobra.Command{
|
|||||||
Use: "register",
|
Use: "register",
|
||||||
Short: "Registers a node to your network",
|
Short: "Registers a node to your network",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
user, err := cmd.Flags().GetString("user")
|
user, err := cmd.Flags().GetString("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
|
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
|
||||||
@ -181,7 +146,6 @@ var registerNodeCmd = &cobra.Command{
|
|||||||
fmt.Sprintf("Node %s registered", response.GetNode().GetGivenName()), output)
|
fmt.Sprintf("Node %s registered", response.GetNode().GetGivenName()), output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -207,9 +171,6 @@ var listNodesCmd = &cobra.Command{
|
|||||||
if user, _ := cmd.Flags().GetString("user"); user != "" {
|
if user, _ := cmd.Flags().GetString("user"); user != "" {
|
||||||
request.User = user
|
request.User = user
|
||||||
}
|
}
|
||||||
if namespace, _ := cmd.Flags().GetString("namespace"); namespace != "" {
|
|
||||||
request.User = namespace // backward compatibility
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle node filtering (new functionality)
|
// Handle node filtering (new functionality)
|
||||||
if nodeFlag, _ := cmd.Flags().GetString("node"); nodeFlag != "" {
|
if nodeFlag, _ := cmd.Flags().GetString("node"); nodeFlag != "" {
|
||||||
@ -267,7 +228,6 @@ var listNodesCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -279,7 +239,7 @@ var listNodeRoutesCmd = &cobra.Command{
|
|||||||
Short: "List routes available on nodes",
|
Short: "List routes available on nodes",
|
||||||
Aliases: []string{"lsr", "routes"},
|
Aliases: []string{"lsr", "routes"},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
identifier, err := GetNodeIdentifier(cmd)
|
identifier, err := GetNodeIdentifier(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorOutput(
|
ErrorOutput(
|
||||||
@ -339,7 +299,6 @@ var listNodeRoutesCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -352,7 +311,7 @@ var expireNodeCmd = &cobra.Command{
|
|||||||
Long: "Expiring a node will keep the node in the database and force it to reauthenticate.",
|
Long: "Expiring a node will keep the node in the database and force it to reauthenticate.",
|
||||||
Aliases: []string{"logout", "exp", "e"},
|
Aliases: []string{"logout", "exp", "e"},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
identifier, err := GetNodeIdentifier(cmd)
|
identifier, err := GetNodeIdentifier(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -385,7 +344,6 @@ var expireNodeCmd = &cobra.Command{
|
|||||||
SuccessOutput(response.GetNode(), "Node expired", output)
|
SuccessOutput(response.GetNode(), "Node expired", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -396,7 +354,7 @@ var renameNodeCmd = &cobra.Command{
|
|||||||
Use: "rename NEW_NAME",
|
Use: "rename NEW_NAME",
|
||||||
Short: "Renames a node in your network",
|
Short: "Renames a node in your network",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
identifier, err := GetNodeIdentifier(cmd)
|
identifier, err := GetNodeIdentifier(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -435,7 +393,6 @@ var renameNodeCmd = &cobra.Command{
|
|||||||
SuccessOutput(response.GetNode(), "Node renamed", output)
|
SuccessOutput(response.GetNode(), "Node renamed", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -447,7 +404,7 @@ var deleteNodeCmd = &cobra.Command{
|
|||||||
Short: "Delete a node",
|
Short: "Delete a node",
|
||||||
Aliases: []string{"del"},
|
Aliases: []string{"del"},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
identifier, err := GetNodeIdentifier(cmd)
|
identifier, err := GetNodeIdentifier(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -477,7 +434,6 @@ var deleteNodeCmd = &cobra.Command{
|
|||||||
nodeName = getResponse.GetNode().GetName()
|
nodeName = getResponse.GetNode().GetName()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -523,7 +479,6 @@ var deleteNodeCmd = &cobra.Command{
|
|||||||
)
|
)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -538,7 +493,7 @@ var moveNodeCmd = &cobra.Command{
|
|||||||
Short: "Move node to another user",
|
Short: "Move node to another user",
|
||||||
Aliases: []string{"mv"},
|
Aliases: []string{"mv"},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
identifier, err := GetNodeIdentifier(cmd)
|
identifier, err := GetNodeIdentifier(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -593,7 +548,6 @@ var moveNodeCmd = &cobra.Command{
|
|||||||
SuccessOutput(moveResponse.GetNode(), "Node moved to another user", output)
|
SuccessOutput(moveResponse.GetNode(), "Node moved to another user", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -618,7 +572,7 @@ it can be run to remove the IPs that should no longer
|
|||||||
be assigned to nodes.`,
|
be assigned to nodes.`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
var err error
|
var err error
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
confirm := false
|
confirm := false
|
||||||
prompt := &survey.Confirm{
|
prompt := &survey.Confirm{
|
||||||
@ -643,7 +597,6 @@ be assigned to nodes.`,
|
|||||||
SuccessOutput(changes, "Node IPs backfilled successfully", output)
|
SuccessOutput(changes, "Node IPs backfilled successfully", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -829,7 +782,7 @@ var tagCmd = &cobra.Command{
|
|||||||
Short: "Manage the tags of a node",
|
Short: "Manage the tags of a node",
|
||||||
Aliases: []string{"tags", "t"},
|
Aliases: []string{"tags", "t"},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
// retrieve flags from CLI
|
// retrieve flags from CLI
|
||||||
identifier, err := GetNodeIdentifier(cmd)
|
identifier, err := GetNodeIdentifier(cmd)
|
||||||
@ -876,7 +829,6 @@ var tagCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -887,7 +839,7 @@ var approveRoutesCmd = &cobra.Command{
|
|||||||
Use: "approve-routes",
|
Use: "approve-routes",
|
||||||
Short: "Manage the approved routes of a node",
|
Short: "Manage the approved routes of a node",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
// retrieve flags from CLI
|
// retrieve flags from CLI
|
||||||
identifier, err := GetNodeIdentifier(cmd)
|
identifier, err := GetNodeIdentifier(cmd)
|
||||||
@ -934,7 +886,6 @@ var approveRoutesCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ var getPolicy = &cobra.Command{
|
|||||||
Short: "Print the current ACL Policy",
|
Short: "Print the current ACL Policy",
|
||||||
Aliases: []string{"show", "view", "fetch"},
|
Aliases: []string{"show", "view", "fetch"},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
err := WithClient(func(ctx context.Context, client v1.HeadscaleServiceClient) error {
|
err := WithClient(func(ctx context.Context, client v1.HeadscaleServiceClient) error {
|
||||||
request := &v1.GetPolicyRequest{}
|
request := &v1.GetPolicyRequest{}
|
||||||
@ -58,7 +58,6 @@ var getPolicy = &cobra.Command{
|
|||||||
SuccessOutput("", response.GetPolicy(), "")
|
SuccessOutput("", response.GetPolicy(), "")
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -73,7 +72,7 @@ var setPolicy = &cobra.Command{
|
|||||||
This command only works when the acl.policy_mode is set to "db", and the policy will be stored in the database.`,
|
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"},
|
Aliases: []string{"put", "update"},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
policyPath, _ := cmd.Flags().GetString("file")
|
policyPath, _ := cmd.Flags().GetString("file")
|
||||||
|
|
||||||
f, err := os.Open(policyPath)
|
f, err := os.Open(policyPath)
|
||||||
@ -100,7 +99,6 @@ var setPolicy = &cobra.Command{
|
|||||||
SuccessOutput(nil, "Policy updated.", "")
|
SuccessOutput(nil, "Policy updated.", "")
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -111,23 +109,26 @@ var checkPolicy = &cobra.Command{
|
|||||||
Use: "check",
|
Use: "check",
|
||||||
Short: "Check the Policy file for errors",
|
Short: "Check the Policy file for errors",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
policyPath, _ := cmd.Flags().GetString("file")
|
policyPath, _ := cmd.Flags().GetString("file")
|
||||||
|
|
||||||
f, err := os.Open(policyPath)
|
f, err := os.Open(policyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorOutput(err, fmt.Sprintf("Error opening the policy file: %s", err), output)
|
ErrorOutput(err, fmt.Sprintf("Error opening the policy file: %s", err), output)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
policyBytes, err := io.ReadAll(f)
|
policyBytes, err := io.ReadAll(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorOutput(err, fmt.Sprintf("Error reading the policy file: %s", err), output)
|
ErrorOutput(err, fmt.Sprintf("Error reading the policy file: %s", err), output)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = policy.NewPolicyManager(policyBytes, nil, views.Slice[types.NodeView]{})
|
_, err = policy.NewPolicyManager(policyBytes, nil, views.Slice[types.NodeView]{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorOutput(err, fmt.Sprintf("Error parsing the policy file: %s", err), output)
|
ErrorOutput(err, fmt.Sprintf("Error parsing the policy file: %s", err), output)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
SuccessOutput(nil, "Policy is valid", "")
|
SuccessOutput(nil, "Policy is valid", "")
|
||||||
|
@ -15,16 +15,10 @@ import (
|
|||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(preauthkeysCmd)
|
rootCmd.AddCommand(preauthkeysCmd)
|
||||||
preauthkeysCmd.PersistentFlags().Uint64P("user", "u", 0, "User identifier (ID)")
|
preauthkeysCmd.PersistentFlags().Uint64P("user", "u", 0, "User identifier (ID)")
|
||||||
|
|
||||||
preauthkeysCmd.PersistentFlags().StringP("namespace", "n", "", "User")
|
|
||||||
pakNamespaceFlag := preauthkeysCmd.PersistentFlags().Lookup("namespace")
|
|
||||||
pakNamespaceFlag.Deprecated = deprecateNamespaceMessage
|
|
||||||
pakNamespaceFlag.Hidden = true
|
|
||||||
|
|
||||||
err := preauthkeysCmd.MarkPersistentFlagRequired("user")
|
err := preauthkeysCmd.MarkPersistentFlagRequired("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("")
|
log.Fatal().Err(err).Msg("")
|
||||||
@ -53,7 +47,7 @@ var listPreAuthKeys = &cobra.Command{
|
|||||||
Short: "List the preauthkeys for this user",
|
Short: "List the preauthkeys for this user",
|
||||||
Aliases: []string{"ls", "show"},
|
Aliases: []string{"ls", "show"},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
user, err := cmd.Flags().GetUint64("user")
|
user, err := cmd.Flags().GetUint64("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -130,7 +124,6 @@ var listPreAuthKeys = &cobra.Command{
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -142,7 +135,7 @@ var createPreAuthKeyCmd = &cobra.Command{
|
|||||||
Short: "Creates a new preauthkey in the specified user",
|
Short: "Creates a new preauthkey in the specified user",
|
||||||
Aliases: []string{"c", "new"},
|
Aliases: []string{"c", "new"},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
user, err := cmd.Flags().GetUint64("user")
|
user, err := cmd.Flags().GetUint64("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -195,7 +188,6 @@ var createPreAuthKeyCmd = &cobra.Command{
|
|||||||
SuccessOutput(response.GetPreAuthKey(), response.GetPreAuthKey().GetKey(), output)
|
SuccessOutput(response.GetPreAuthKey(), response.GetPreAuthKey().GetKey(), output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -214,7 +206,7 @@ var expirePreAuthKeyCmd = &cobra.Command{
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
user, err := cmd.Flags().GetUint64("user")
|
user, err := cmd.Flags().GetUint64("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
|
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
|
||||||
@ -240,7 +232,6 @@ var expirePreAuthKeyCmd = &cobra.Command{
|
|||||||
SuccessOutput(response, "Key expired", output)
|
SuccessOutput(response, "Key expired", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/tcnksm/go-latest"
|
"github.com/tcnksm/go-latest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
var cfgFile string = ""
|
var cfgFile string = ""
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -8,10 +8,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
deprecateNamespaceMessage = "use --user"
|
HeadscaleDateTimeFormat = "2006-01-02 15:04:05"
|
||||||
HeadscaleDateTimeFormat = "2006-01-02 15:04:05"
|
DefaultAPIKeyExpiry = "90d"
|
||||||
DefaultAPIKeyExpiry = "90d"
|
DefaultPreAuthKeyExpiry = "1h"
|
||||||
DefaultPreAuthKeyExpiry = "1h"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FilterTableColumns filters table columns based on --columns flag
|
// FilterTableColumns filters table columns based on --columns flag
|
||||||
|
@ -18,16 +18,12 @@ import (
|
|||||||
|
|
||||||
func usernameAndIDFlag(cmd *cobra.Command) {
|
func usernameAndIDFlag(cmd *cobra.Command) {
|
||||||
cmd.Flags().StringP("user", "u", "", "User identifier (ID, name, or email)")
|
cmd.Flags().StringP("user", "u", "", "User identifier (ID, name, or email)")
|
||||||
cmd.Flags().Uint64P("identifier", "i", 0, "User identifier (ID) - deprecated, use --user")
|
|
||||||
identifierFlag := cmd.Flags().Lookup("identifier")
|
|
||||||
identifierFlag.Deprecated = "use --user"
|
|
||||||
identifierFlag.Hidden = true
|
|
||||||
cmd.Flags().StringP("name", "n", "", "Username")
|
cmd.Flags().StringP("name", "n", "", "Username")
|
||||||
}
|
}
|
||||||
|
|
||||||
// usernameAndIDFromFlag returns the user ID using smart lookup.
|
// userIDFromFlag returns the user ID using smart lookup.
|
||||||
// If no user is specified, it will exit the program with an error.
|
// If no user is specified, it will exit the program with an error.
|
||||||
func usernameAndIDFromFlag(cmd *cobra.Command) (uint64, string) {
|
func userIDFromFlag(cmd *cobra.Command) uint64 {
|
||||||
userID, err := GetUserIdentifier(cmd)
|
userID, err := GetUserIdentifier(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrorOutput(
|
ErrorOutput(
|
||||||
@ -37,7 +33,7 @@ func usernameAndIDFromFlag(cmd *cobra.Command) (uint64, string) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return userID, ""
|
return userID
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -52,11 +48,6 @@ func init() {
|
|||||||
listUsersCmd.Flags().Uint64P("id", "", 0, "Filter by user ID")
|
listUsersCmd.Flags().Uint64P("id", "", 0, "Filter by user ID")
|
||||||
listUsersCmd.Flags().StringP("name", "n", "", "Filter by username")
|
listUsersCmd.Flags().StringP("name", "n", "", "Filter by username")
|
||||||
listUsersCmd.Flags().StringP("email", "e", "", "Filter by email address")
|
listUsersCmd.Flags().StringP("email", "e", "", "Filter by email address")
|
||||||
// Backward compatibility (deprecated)
|
|
||||||
listUsersCmd.Flags().Uint64P("identifier", "i", 0, "Filter by user ID - deprecated, use --id")
|
|
||||||
identifierFlag := listUsersCmd.Flags().Lookup("identifier")
|
|
||||||
identifierFlag.Deprecated = "use --id"
|
|
||||||
identifierFlag.Hidden = true
|
|
||||||
listUsersCmd.Flags().String("columns", "", "Comma-separated list of columns to display (ID,Name,Username,Email,Created)")
|
listUsersCmd.Flags().String("columns", "", "Comma-separated list of columns to display (ID,Name,Username,Email,Created)")
|
||||||
userCmd.AddCommand(destroyUserCmd)
|
userCmd.AddCommand(destroyUserCmd)
|
||||||
usernameAndIDFlag(destroyUserCmd)
|
usernameAndIDFlag(destroyUserCmd)
|
||||||
@ -131,7 +122,6 @@ var createUserCmd = &cobra.Command{
|
|||||||
SuccessOutput(response.GetUser(), "User created", output)
|
SuccessOutput(response.GetUser(), "User created", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -145,10 +135,9 @@ var destroyUserCmd = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output := GetOutputFlag(cmd)
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
id, username := usernameAndIDFromFlag(cmd)
|
id := userIDFromFlag(cmd)
|
||||||
request := &v1.ListUsersRequest{
|
request := &v1.ListUsersRequest{
|
||||||
Name: username,
|
Id: id,
|
||||||
Id: id,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var user *v1.User
|
var user *v1.User
|
||||||
@ -176,7 +165,6 @@ var destroyUserCmd = &cobra.Command{
|
|||||||
user = users.GetUsers()[0]
|
user = users.GetUsers()[0]
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -212,7 +200,6 @@ var destroyUserCmd = &cobra.Command{
|
|||||||
SuccessOutput(response, "User destroyed", output)
|
SuccessOutput(response, "User destroyed", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -247,8 +234,6 @@ var listUsersCmd = &cobra.Command{
|
|||||||
// Check specific filter flags
|
// Check specific filter flags
|
||||||
if id, _ := cmd.Flags().GetUint64("id"); id > 0 {
|
if id, _ := cmd.Flags().GetUint64("id"); id > 0 {
|
||||||
request.Id = id
|
request.Id = id
|
||||||
} else if identifier, _ := cmd.Flags().GetUint64("identifier"); identifier > 0 {
|
|
||||||
request.Id = identifier // backward compatibility
|
|
||||||
} else if name, _ := cmd.Flags().GetString("name"); name != "" {
|
} else if name, _ := cmd.Flags().GetString("name"); name != "" {
|
||||||
request.Name = name
|
request.Name = name
|
||||||
} else if email, _ := cmd.Flags().GetString("email"); email != "" {
|
} else if email, _ := cmd.Flags().GetString("email"); email != "" {
|
||||||
@ -296,7 +281,6 @@ var listUsersCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Error already handled in closure
|
// Error already handled in closure
|
||||||
return
|
return
|
||||||
@ -311,13 +295,12 @@ var renameUserCmd = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output := GetOutputFlag(cmd)
|
output := GetOutputFlag(cmd)
|
||||||
|
|
||||||
id, username := usernameAndIDFromFlag(cmd)
|
id := userIDFromFlag(cmd)
|
||||||
newName, _ := cmd.Flags().GetString("new-name")
|
newName, _ := cmd.Flags().GetString("new-name")
|
||||||
|
|
||||||
err := WithClient(func(ctx context.Context, client v1.HeadscaleServiceClient) error {
|
err := WithClient(func(ctx context.Context, client v1.HeadscaleServiceClient) error {
|
||||||
listReq := &v1.ListUsersRequest{
|
listReq := &v1.ListUsersRequest{
|
||||||
Name: username,
|
Id: id,
|
||||||
Id: id,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := client.ListUsers(ctx, listReq)
|
users, err := client.ListUsers(ctx, listReq)
|
||||||
@ -358,7 +341,6 @@ var renameUserCmd = &cobra.Command{
|
|||||||
SuccessOutput(response.GetUser(), "User renamed", output)
|
SuccessOutput(response.GetUser(), "User renamed", output)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,6 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
SocketWritePermissions = 0o666
|
|
||||||
)
|
|
||||||
|
|
||||||
func newHeadscaleServerWithConfig() (*hscontrol.Headscale, error) {
|
func newHeadscaleServerWithConfig() (*hscontrol.Headscale, error) {
|
||||||
cfg, err := types.LoadServerConfig()
|
cfg, err := types.LoadServerConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -75,7 +71,7 @@ func newHeadscaleCLIWithConfig() (context.Context, v1.HeadscaleServiceClient, *g
|
|||||||
|
|
||||||
// Try to give the user better feedback if we cannot write to the headscale
|
// Try to give the user better feedback if we cannot write to the headscale
|
||||||
// socket.
|
// socket.
|
||||||
socket, err := os.OpenFile(cfg.UnixSocket, os.O_WRONLY, SocketWritePermissions) // nolint
|
socket, err := os.OpenFile(cfg.UnixSocket, os.O_WRONLY, 0o666) // nolint
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsPermission(err) {
|
if os.IsPermission(err) {
|
||||||
log.Fatal().
|
log.Fatal().
|
||||||
@ -210,15 +206,10 @@ func GetOutputFlag(cmd *cobra.Command) string {
|
|||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// GetNodeIdentifier returns the node ID using smart lookup via gRPC ListNodes call
|
// GetNodeIdentifier returns the node ID using smart lookup via gRPC ListNodes call
|
||||||
func GetNodeIdentifier(cmd *cobra.Command) (uint64, error) {
|
func GetNodeIdentifier(cmd *cobra.Command) (uint64, error) {
|
||||||
nodeFlag, _ := cmd.Flags().GetString("node")
|
nodeFlag, _ := cmd.Flags().GetString("node")
|
||||||
identifierFlag, _ := cmd.Flags().GetUint64("identifier")
|
|
||||||
|
|
||||||
// Check if --identifier (deprecated) was used
|
|
||||||
if identifierFlag > 0 {
|
|
||||||
return identifierFlag, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use --node flag
|
// Use --node flag
|
||||||
if nodeFlag == "" {
|
if nodeFlag == "" {
|
||||||
@ -270,7 +261,6 @@ func lookupNodeBySpecifier(specifier string) (uint64, error) {
|
|||||||
nodeID = nodes[0].GetId()
|
nodeID = nodes[0].GetId()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -295,7 +285,6 @@ func isIPAddress(s string) bool {
|
|||||||
func GetUserIdentifier(cmd *cobra.Command) (uint64, error) {
|
func GetUserIdentifier(cmd *cobra.Command) (uint64, error) {
|
||||||
userFlag, _ := cmd.Flags().GetString("user")
|
userFlag, _ := cmd.Flags().GetString("user")
|
||||||
nameFlag, _ := cmd.Flags().GetString("name")
|
nameFlag, _ := cmd.Flags().GetString("name")
|
||||||
identifierFlag, _ := cmd.Flags().GetUint64("identifier")
|
|
||||||
|
|
||||||
var specifier string
|
var specifier string
|
||||||
|
|
||||||
@ -304,8 +293,6 @@ func GetUserIdentifier(cmd *cobra.Command) (uint64, error) {
|
|||||||
specifier = userFlag
|
specifier = userFlag
|
||||||
} else if nameFlag != "" {
|
} else if nameFlag != "" {
|
||||||
specifier = nameFlag
|
specifier = nameFlag
|
||||||
} else if identifierFlag > 0 {
|
|
||||||
return identifierFlag, nil // Direct ID, no lookup needed
|
|
||||||
} else {
|
} else {
|
||||||
return 0, fmt.Errorf("--user flag is required")
|
return 0, fmt.Errorf("--user flag is required")
|
||||||
}
|
}
|
||||||
@ -355,7 +342,6 @@ func lookupUserBySpecifier(specifier string) (uint64, error) {
|
|||||||
userID = users[0].GetId()
|
userID = users[0].GetId()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ var versionCmd = &cobra.Command{
|
|||||||
Short: "Print the version",
|
Short: "Print the version",
|
||||||
Long: "The version of headscale",
|
Long: "The version of headscale",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
output, _ := cmd.Flags().GetString("output")
|
output := GetOutputFlag(cmd)
|
||||||
SuccessOutput(map[string]string{
|
SuccessOutput(map[string]string{
|
||||||
"version": types.Version,
|
"version": types.Version,
|
||||||
"commit": types.GitCommitHash,
|
"commit": types.GitCommitHash,
|
||||||
|
2
go.mod
2
go.mod
@ -81,7 +81,7 @@ require (
|
|||||||
modernc.org/libc v1.62.1 // indirect
|
modernc.org/libc v1.62.1 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.10.0 // indirect
|
modernc.org/memory v1.10.0 // indirect
|
||||||
modernc.org/sqlite v1.37.0 // indirect
|
modernc.org/sqlite v1.37.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"cmp"
|
"cmp"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -18,7 +19,6 @@ import (
|
|||||||
"github.com/juanfont/headscale/integration/tsic"
|
"github.com/juanfont/headscale/integration/tsic"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ func TestUserCommand(t *testing.T) {
|
|||||||
"users",
|
"users",
|
||||||
"rename",
|
"rename",
|
||||||
"--output=json",
|
"--output=json",
|
||||||
fmt.Sprintf("--identifier=%d", listUsers[1].GetId()),
|
fmt.Sprintf("--user=%d", listUsers[1].GetId()),
|
||||||
"--new-name=newname",
|
"--new-name=newname",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -161,7 +161,7 @@ func TestUserCommand(t *testing.T) {
|
|||||||
"list",
|
"list",
|
||||||
"--output",
|
"--output",
|
||||||
"json",
|
"json",
|
||||||
"--identifier=1",
|
"--user=1",
|
||||||
},
|
},
|
||||||
&listByID,
|
&listByID,
|
||||||
)
|
)
|
||||||
@ -187,7 +187,7 @@ func TestUserCommand(t *testing.T) {
|
|||||||
"destroy",
|
"destroy",
|
||||||
"--force",
|
"--force",
|
||||||
// Delete "user1"
|
// Delete "user1"
|
||||||
"--identifier=1",
|
"--user=1",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -354,7 +354,10 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, []string{"tag:test1", "tag:test2"}, listedPreAuthKeys[index].GetAclTags())
|
// Sort tags for consistent comparison
|
||||||
|
tags := listedPreAuthKeys[index].GetAclTags()
|
||||||
|
slices.Sort(tags)
|
||||||
|
assert.Equal(t, []string{"tag:test1", "tag:test2"}, tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test key expiry
|
// Test key expiry
|
||||||
@ -869,7 +872,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"tag",
|
"tag",
|
||||||
"-i", "1",
|
"--node", "1",
|
||||||
"-t", "tag:test",
|
"-t", "tag:test",
|
||||||
"--output", "json",
|
"--output", "json",
|
||||||
},
|
},
|
||||||
@ -884,7 +887,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"tag",
|
"tag",
|
||||||
"-i", "2",
|
"--node", "2",
|
||||||
"-t", "wrong-tag",
|
"-t", "wrong-tag",
|
||||||
"--output", "json",
|
"--output", "json",
|
||||||
},
|
},
|
||||||
@ -1259,7 +1262,7 @@ func TestNodeCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"delete",
|
"delete",
|
||||||
"--identifier",
|
"--node",
|
||||||
// Delete the last added machine
|
// Delete the last added machine
|
||||||
"4",
|
"4",
|
||||||
"--output",
|
"--output",
|
||||||
@ -1385,7 +1388,7 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"expire",
|
"expire",
|
||||||
"--identifier",
|
"--node",
|
||||||
strconv.FormatUint(listAll[idx].GetId(), 10),
|
strconv.FormatUint(listAll[idx].GetId(), 10),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -1511,7 +1514,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"rename",
|
"rename",
|
||||||
"--identifier",
|
"--node",
|
||||||
strconv.FormatUint(listAll[idx].GetId(), 10),
|
strconv.FormatUint(listAll[idx].GetId(), 10),
|
||||||
fmt.Sprintf("newnode-%d", idx+1),
|
fmt.Sprintf("newnode-%d", idx+1),
|
||||||
},
|
},
|
||||||
@ -1549,7 +1552,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"rename",
|
"rename",
|
||||||
"--identifier",
|
"--node",
|
||||||
strconv.FormatUint(listAll[4].GetId(), 10),
|
strconv.FormatUint(listAll[4].GetId(), 10),
|
||||||
strings.Repeat("t", 64),
|
strings.Repeat("t", 64),
|
||||||
},
|
},
|
||||||
@ -1649,7 +1652,7 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"move",
|
"move",
|
||||||
"--identifier",
|
"--node",
|
||||||
strconv.FormatUint(node.GetId(), 10),
|
strconv.FormatUint(node.GetId(), 10),
|
||||||
"--user",
|
"--user",
|
||||||
strconv.FormatUint(userMap["new-user"].GetId(), 10),
|
strconv.FormatUint(userMap["new-user"].GetId(), 10),
|
||||||
@ -1687,7 +1690,7 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"move",
|
"move",
|
||||||
"--identifier",
|
"--node",
|
||||||
nodeID,
|
nodeID,
|
||||||
"--user",
|
"--user",
|
||||||
"999",
|
"999",
|
||||||
@ -1708,7 +1711,7 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"move",
|
"move",
|
||||||
"--identifier",
|
"--node",
|
||||||
nodeID,
|
nodeID,
|
||||||
"--user",
|
"--user",
|
||||||
strconv.FormatUint(userMap["old-user"].GetId(), 10),
|
strconv.FormatUint(userMap["old-user"].GetId(), 10),
|
||||||
@ -1727,7 +1730,7 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"move",
|
"move",
|
||||||
"--identifier",
|
"--node",
|
||||||
nodeID,
|
nodeID,
|
||||||
"--user",
|
"--user",
|
||||||
strconv.FormatUint(userMap["old-user"].GetId(), 10),
|
strconv.FormatUint(userMap["old-user"].GetId(), 10),
|
||||||
|
@ -41,7 +41,7 @@ func TestDebugCommand(t *testing.T) {
|
|||||||
|
|
||||||
// Help text should contain expected information
|
// Help text should contain expected information
|
||||||
assert.Contains(t, result, "debug", "help should mention debug command")
|
assert.Contains(t, result, "debug", "help should mention debug command")
|
||||||
assert.Contains(t, result, "debug and testing commands", "help should contain command description")
|
assert.Contains(t, result, "debugging and testing", "help should contain command description")
|
||||||
assert.Contains(t, result, "create-node", "help should mention create-node subcommand")
|
assert.Contains(t, result, "create-node", "help should mention create-node subcommand")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -564,10 +564,10 @@ func TestUpdateHostnameFromClient(t *testing.T) {
|
|||||||
_, err = headscale.Execute(
|
_, err = headscale.Execute(
|
||||||
[]string{
|
[]string{
|
||||||
"headscale",
|
"headscale",
|
||||||
"node",
|
"nodes",
|
||||||
"rename",
|
"rename",
|
||||||
givenName,
|
givenName,
|
||||||
"--identifier",
|
"--node",
|
||||||
strconv.FormatUint(node.GetId(), 10),
|
strconv.FormatUint(node.GetId(), 10),
|
||||||
})
|
})
|
||||||
assertNoErr(t, err)
|
assertNoErr(t, err)
|
||||||
@ -702,7 +702,7 @@ func TestExpireNode(t *testing.T) {
|
|||||||
// TODO(kradalby): This is Headscale specific and would not play nicely
|
// TODO(kradalby): This is Headscale specific and would not play nicely
|
||||||
// with other implementations of the ControlServer interface
|
// with other implementations of the ControlServer interface
|
||||||
result, err := headscale.Execute([]string{
|
result, err := headscale.Execute([]string{
|
||||||
"headscale", "nodes", "expire", "--identifier", "1", "--output", "json",
|
"headscale", "nodes", "expire", "--node", "1", "--output", "json",
|
||||||
})
|
})
|
||||||
assertNoErr(t, err)
|
assertNoErr(t, err)
|
||||||
|
|
||||||
@ -1060,7 +1060,7 @@ func Test2118DeletingOnlineNodePanics(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"delete",
|
"delete",
|
||||||
"--identifier",
|
"--node",
|
||||||
// Delete the last added machine
|
// Delete the last added machine
|
||||||
fmt.Sprintf("%d", nodeList[0].GetId()),
|
fmt.Sprintf("%d", nodeList[0].GetId()),
|
||||||
"--output",
|
"--output",
|
||||||
|
@ -320,7 +320,9 @@ func TestGenerateCommandEdgeCases(t *testing.T) {
|
|||||||
|
|
||||||
// Should fail gracefully for non-existent subcommand
|
// Should fail gracefully for non-existent subcommand
|
||||||
assert.Error(t, err, "should fail for non-existent subcommand")
|
assert.Error(t, err, "should fail for non-existent subcommand")
|
||||||
assert.NotContains(t, err.Error(), "panic", "should not panic on non-existent subcommand")
|
if err != nil {
|
||||||
|
assert.NotContains(t, err.Error(), "panic", "should not panic on non-existent subcommand")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("test_generate_key_format_consistency", func(t *testing.T) {
|
t.Run("test_generate_key_format_consistency", func(t *testing.T) {
|
||||||
@ -349,8 +351,8 @@ func TestGenerateCommandEdgeCases(t *testing.T) {
|
|||||||
for _, char := range keyPart {
|
for _, char := range keyPart {
|
||||||
assert.True(t,
|
assert.True(t,
|
||||||
(char >= '0' && char <= '9') ||
|
(char >= '0' && char <= '9') ||
|
||||||
(char >= 'a' && char <= 'f') ||
|
(char >= 'a' && char <= 'f') ||
|
||||||
(char >= 'A' && char <= 'F'),
|
(char >= 'A' && char <= 'F'),
|
||||||
"private key should only contain hex characters, found: %c", char)
|
"private key should only contain hex characters, found: %c", char)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1122,7 +1122,7 @@ func (t *HeadscaleInContainer) ApproveRoutes(id uint64, routes []netip.Prefix) (
|
|||||||
command := []string{
|
command := []string{
|
||||||
"headscale", "nodes", "approve-routes",
|
"headscale", "nodes", "approve-routes",
|
||||||
"--output", "json",
|
"--output", "json",
|
||||||
"--identifier", strconv.FormatUint(id, 10),
|
"--node", strconv.FormatUint(id, 10),
|
||||||
"--routes=" + strings.Join(util.PrefixesToString(routes), ","),
|
"--routes=" + strings.Join(util.PrefixesToString(routes), ","),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ func TestRouteCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"list-routes",
|
"list-routes",
|
||||||
"--identifier",
|
"--node",
|
||||||
fmt.Sprintf("%d", nodeID),
|
fmt.Sprintf("%d", nodeID),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -124,7 +124,7 @@ func TestRouteCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"approve-routes",
|
"approve-routes",
|
||||||
"--identifier",
|
"--node",
|
||||||
fmt.Sprintf("%d", nodeID),
|
fmt.Sprintf("%d", nodeID),
|
||||||
"--routes",
|
"--routes",
|
||||||
"10.0.0.0/24",
|
"10.0.0.0/24",
|
||||||
@ -158,7 +158,7 @@ func TestRouteCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"approve-routes",
|
"approve-routes",
|
||||||
"--identifier",
|
"--node",
|
||||||
fmt.Sprintf("%d", nodeID),
|
fmt.Sprintf("%d", nodeID),
|
||||||
"--routes",
|
"--routes",
|
||||||
"", // Empty string removes all routes
|
"", // Empty string removes all routes
|
||||||
@ -192,7 +192,7 @@ func TestRouteCommand(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"list-routes",
|
"list-routes",
|
||||||
"--identifier",
|
"--node",
|
||||||
fmt.Sprintf("%d", nodeID),
|
fmt.Sprintf("%d", nodeID),
|
||||||
"--output",
|
"--output",
|
||||||
"json",
|
"json",
|
||||||
@ -231,7 +231,7 @@ func TestRouteCommandEdgeCases(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"list-routes",
|
"list-routes",
|
||||||
"--identifier",
|
"--node",
|
||||||
"999999",
|
"999999",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -246,7 +246,7 @@ func TestRouteCommandEdgeCases(t *testing.T) {
|
|||||||
"headscale",
|
"headscale",
|
||||||
"nodes",
|
"nodes",
|
||||||
"approve-routes",
|
"approve-routes",
|
||||||
"--identifier",
|
"--node",
|
||||||
"1",
|
"1",
|
||||||
"--routes",
|
"--routes",
|
||||||
"invalid-cidr",
|
"invalid-cidr",
|
||||||
@ -287,7 +287,7 @@ func TestRouteCommandHelp(t *testing.T) {
|
|||||||
|
|
||||||
// Verify help text contains expected information
|
// Verify help text contains expected information
|
||||||
assert.Contains(t, result, "list-routes", "help should mention list-routes command")
|
assert.Contains(t, result, "list-routes", "help should mention list-routes command")
|
||||||
assert.Contains(t, result, "identifier", "help should mention identifier flag")
|
assert.Contains(t, result, "node", "help should mention node flag")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("test_approve_routes_help", func(t *testing.T) {
|
t.Run("test_approve_routes_help", func(t *testing.T) {
|
||||||
@ -303,7 +303,7 @@ func TestRouteCommandHelp(t *testing.T) {
|
|||||||
|
|
||||||
// Verify help text contains expected information
|
// Verify help text contains expected information
|
||||||
assert.Contains(t, result, "approve-routes", "help should mention approve-routes command")
|
assert.Contains(t, result, "approve-routes", "help should mention approve-routes command")
|
||||||
assert.Contains(t, result, "identifier", "help should mention identifier flag")
|
assert.Contains(t, result, "node", "help should mention node flag")
|
||||||
assert.Contains(t, result, "routes", "help should mention routes flag")
|
assert.Contains(t, result, "routes", "help should mention routes flag")
|
||||||
})
|
})
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user