Get rid of dynamic errors

This commit is contained in:
Kristoffer Dalby 2021-11-15 19:18:14 +00:00
parent 25b790d025
commit d6739386a0
No known key found for this signature in database
GPG key ID: 09F62DC067465735
10 changed files with 53 additions and 22 deletions

View file

@ -30,7 +30,6 @@ linters:
# We should strive to enable these: # We should strive to enable these:
- wrapcheck - wrapcheck
- goerr113
- dupl - dupl
- makezero - makezero

9
app.go
View file

@ -52,6 +52,11 @@ const (
Sqlite = "sqlite3" Sqlite = "sqlite3"
updateInterval = 5000 updateInterval = 5000
HTTPReadTimeout = 30 * time.Second HTTPReadTimeout = 30 * time.Second
errUnsupportedDatabase = Error("unsupported DB")
errUnsupportedLetsEncryptChallengeType = Error(
"unknown value for Lets Encrypt challenge type",
)
) )
// Config contains the initial Headscale configuration. // Config contains the initial Headscale configuration.
@ -166,7 +171,7 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
case Sqlite: case Sqlite:
dbString = cfg.DBpath dbString = cfg.DBpath
default: default:
return nil, errors.New("unsupported DB") return nil, errUnsupportedDatabase
} }
app := Headscale{ app := Headscale{
@ -626,7 +631,7 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) {
return certManager.TLSConfig(), nil return certManager.TLSConfig(), nil
default: default:
return nil, errors.New("unknown value for TLSLetsEncryptChallengeType") return nil, errUnsupportedLetsEncryptChallengeType
} }
} else if h.cfg.TLSCertPath == "" { } else if h.cfg.TLSCertPath == "" {
if !strings.HasPrefix(h.cfg.ServerURL, "http://") { if !strings.HasPrefix(h.cfg.ServerURL, "http://") {

View file

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
survey "github.com/AlecAivazis/survey/v2" survey "github.com/AlecAivazis/survey/v2"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/pterm/pterm" "github.com/pterm/pterm"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -19,6 +20,10 @@ func init() {
namespaceCmd.AddCommand(renameNamespaceCmd) namespaceCmd.AddCommand(renameNamespaceCmd)
} }
const (
errMissingParameter = headscale.Error("missing parameters")
)
var namespaceCmd = &cobra.Command{ var namespaceCmd = &cobra.Command{
Use: "namespaces", Use: "namespaces",
Short: "Manage the namespaces of Headscale", Short: "Manage the namespaces of Headscale",
@ -29,7 +34,7 @@ var createNamespaceCmd = &cobra.Command{
Short: "Creates a new namespace", Short: "Creates a new namespace",
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("missing parameters") return errMissingParameter
} }
return nil return nil
@ -71,7 +76,7 @@ var destroyNamespaceCmd = &cobra.Command{
Short: "Destroys a namespace", Short: "Destroys a namespace",
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("missing parameters") return errMissingParameter
} }
return nil return nil
@ -197,7 +202,7 @@ var renameNamespaceCmd = &cobra.Command{
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
expectedArguments := 2 expectedArguments := 2
if len(args) < expectedArguments { if len(args) < expectedArguments {
return fmt.Errorf("missing parameters") return errMissingParameter
} }
return nil return nil

View file

@ -178,7 +178,7 @@ var expirePreAuthKeyCmd = &cobra.Command{
Short: "Expire a preauthkey", Short: "Expire a preauthkey",
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("missing parameters") return errMissingParameter
} }
return nil return nil

View file

@ -81,6 +81,7 @@ func LoadConfig(path string) error {
errorText += "Fatal config error: server_url must start with https:// or http://\n" errorText += "Fatal config error: server_url must start with https:// or http://\n"
} }
if errorText != "" { if errorText != "" {
//nolint
return errors.New(strings.TrimSuffix(errorText, "\n")) return errors.New(strings.TrimSuffix(errorText, "\n"))
} else { } else {
return nil return nil
@ -305,6 +306,8 @@ func getHeadscaleApp() (*headscale.Headscale, error) {
// to avoid races // to avoid races
minInactivityTimeout, _ := time.ParseDuration("65s") minInactivityTimeout, _ := time.ParseDuration("65s")
if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout { if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout {
// TODO: Find a better way to return this text
//nolint
err := fmt.Errorf( err := fmt.Errorf(
"ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s", "ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s",
viper.GetString("ephemeral_node_inactivity_timeout"), viper.GetString("ephemeral_node_inactivity_timeout"),

7
db.go
View file

@ -9,7 +9,10 @@ import (
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
) )
const dbVersion = "1" const (
dbVersion = "1"
errValueNotFound = Error("not found")
)
// KV is a key-value store in a psql table. For future use... // KV is a key-value store in a psql table. For future use...
type KV struct { type KV struct {
@ -92,7 +95,7 @@ func (h *Headscale) getValue(key string) (string, error) {
result.Error, result.Error,
gorm.ErrRecordNotFound, gorm.ErrRecordNotFound,
) { ) {
return "", errors.New("not found") return "", errValueNotFound
} }
return row.Value, nil return row.Value, nil

View file

@ -20,6 +20,12 @@ import (
"tailscale.com/types/wgkey" "tailscale.com/types/wgkey"
) )
const (
errMachineNotFound = Error("machine not found")
errMachineAlreadyRegistered = Error("machine already registered")
errMachineRouteIsNotAvailable = Error("route is not available on machine")
)
// Machine is a Headscale client. // Machine is a Headscale client.
type Machine struct { type Machine struct {
ID uint64 `gorm:"primary_key"` ID uint64 `gorm:"primary_key"`
@ -248,7 +254,7 @@ func (h *Headscale) GetMachine(namespace string, name string) (*Machine, error)
} }
} }
return nil, fmt.Errorf("machine not found") return nil, errMachineNotFound
} }
// GetMachineByID finds a Machine by ID and returns the Machine struct. // GetMachineByID finds a Machine by ID and returns the Machine struct.
@ -615,7 +621,7 @@ func (h *Headscale) RegisterMachine(
result.Error, result.Error,
gorm.ErrRecordNotFound, gorm.ErrRecordNotFound,
) { ) {
return nil, errors.New("Machine not found") return nil, errMachineNotFound
} }
log.Trace(). log.Trace().
@ -624,7 +630,7 @@ func (h *Headscale) RegisterMachine(
Msg("Attempting to register machine") Msg("Attempting to register machine")
if machine.isAlreadyRegistered() { if machine.isAlreadyRegistered() {
err := errors.New("Machine already registered") err := errMachineAlreadyRegistered
log.Error(). log.Error().
Caller(). Caller().
Err(err). Err(err).
@ -740,9 +746,9 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error {
for _, newRoute := range newRoutes { for _, newRoute := range newRoutes {
if !containsIPPrefix(availableRoutes, newRoute) { if !containsIPPrefix(availableRoutes, newRoute) {
return fmt.Errorf( return fmt.Errorf(
"route (%s) is not available on node %s", "route (%s) is not available on node %s: %w",
machine.Name, machine.Name,
newRoute, newRoute, errMachineRouteIsNotAvailable,
) )
} }
} }

View file

@ -16,6 +16,7 @@ const (
errPreAuthKeyNotFound = Error("AuthKey not found") errPreAuthKeyNotFound = Error("AuthKey not found")
errPreAuthKeyExpired = Error("AuthKey expired") errPreAuthKeyExpired = Error("AuthKey expired")
errSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used") errSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used")
errNamespaceMismatch = Error("namespace mismatch")
) )
// PreAuthKey describes a pre-authorization key usable in a particular namespace. // PreAuthKey describes a pre-authorization key usable in a particular namespace.
@ -87,7 +88,7 @@ func (h *Headscale) GetPreAuthKey(namespace string, key string) (*PreAuthKey, er
} }
if pak.Namespace.Name != namespace { if pak.Namespace.Name != namespace {
return nil, errors.New("Namespace mismatch") return nil, errNamespaceMismatch
} }
return pak, nil return pak, nil

View file

@ -2,12 +2,15 @@ package headscale
import ( import (
"encoding/json" "encoding/json"
"fmt"
"gorm.io/datatypes" "gorm.io/datatypes"
"inet.af/netaddr" "inet.af/netaddr"
) )
const (
errRouteIsNotAvailable = Error("route is not available")
)
// Deprecated: use machine function instead // Deprecated: use machine function instead
// GetAdvertisedNodeRoutes returns the subnet routes advertised by a node (identified by // GetAdvertisedNodeRoutes returns the subnet routes advertised by a node (identified by
// namespace and node name). // namespace and node name).
@ -129,7 +132,7 @@ func (h *Headscale) EnableNodeRoute(
} }
if !available { if !available {
return fmt.Errorf("route (%s) is not available on node %s", nodeName, routeStr) return errRouteIsNotAvailable
} }
routes, err := json.Marshal(enabledRoutes) routes, err := json.Marshal(enabledRoutes)

View file

@ -20,6 +20,12 @@ import (
"tailscale.com/types/wgkey" "tailscale.com/types/wgkey"
) )
const (
errCannotDecryptReponse = Error("cannot decrypt response")
errResponseMissingNonce = Error("response missing nonce")
errCouldNotAllocateIP = Error("could not find any suitable IP")
)
// Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors // Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors
type Error string type Error string
@ -46,7 +52,7 @@ func decodeMsg(
} }
// fmt.Println(string(decrypted)) // fmt.Println(string(decrypted))
if err := json.Unmarshal(decrypted, output); err != nil { if err := json.Unmarshal(decrypted, output); err != nil {
return fmt.Errorf("response: %w", err) return err
} }
return nil return nil
@ -55,7 +61,7 @@ func decodeMsg(
func decryptMsg(msg []byte, pubKey *wgkey.Key, privKey *wgkey.Private) ([]byte, error) { func decryptMsg(msg []byte, pubKey *wgkey.Key, privKey *wgkey.Private) ([]byte, error) {
var nonce [24]byte var nonce [24]byte
if len(msg) < len(nonce)+1 { if len(msg) < len(nonce)+1 {
return nil, fmt.Errorf("response missing nonce, len=%d", len(msg)) return nil, errResponseMissingNonce
} }
copy(nonce[:], msg) copy(nonce[:], msg)
msg = msg[len(nonce):] msg = msg[len(nonce):]
@ -63,7 +69,7 @@ func decryptMsg(msg []byte, pubKey *wgkey.Key, privKey *wgkey.Private) ([]byte,
pub, pri := (*[32]byte)(pubKey), (*[32]byte)(privKey) pub, pri := (*[32]byte)(pubKey), (*[32]byte)(privKey)
decrypted, ok := box.Open(nil, msg, &nonce, pub, pri) decrypted, ok := box.Open(nil, msg, &nonce, pub, pri)
if !ok { if !ok {
return nil, fmt.Errorf("cannot decrypt response") return nil, errCannotDecryptReponse
} }
return decrypted, nil return decrypted, nil
@ -106,7 +112,7 @@ func (h *Headscale) getAvailableIP() (*netaddr.IP, error) {
for { for {
if !ipPrefix.Contains(ip) { if !ipPrefix.Contains(ip) {
return nil, fmt.Errorf("could not find any suitable IP in %s", ipPrefix) return nil, errCouldNotAllocateIP
} }
// Some OS (including Linux) does not like when IPs ends with 0 or 255, which // Some OS (including Linux) does not like when IPs ends with 0 or 255, which
@ -143,7 +149,7 @@ func (h *Headscale) getUsedIPs() ([]netaddr.IP, error) {
if addr != "" { if addr != "" {
ip, err := netaddr.ParseIP(addr) ip, err := netaddr.ParseIP(addr)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse ip from database, %w", err) return nil, fmt.Errorf("failed to parse ip from database: %w", err)
} }
ips[index] = ip ips[index] = ip