exported API errors

This commit is contained in:
Grigoriy Mikhalkin 2022-07-29 17:35:21 +02:00
parent f9c4d577e2
commit 911e6ba6de
10 changed files with 57 additions and 57 deletions

View File

@ -14,7 +14,7 @@ const (
apiPrefixLength = 7 apiPrefixLength = 7
apiKeyLength = 32 apiKeyLength = 32
errAPIKeyFailedToParse = Error("Failed to parse ApiKey") ErrAPIKeyFailedToParse = Error("Failed to parse ApiKey")
) )
// APIKey describes the datamodel for API keys used to remotely authenticate with // APIKey describes the datamodel for API keys used to remotely authenticate with
@ -116,7 +116,7 @@ func (h *Headscale) ExpireAPIKey(key *APIKey) error {
func (h *Headscale) ValidateAPIKey(keyStr string) (bool, error) { func (h *Headscale) ValidateAPIKey(keyStr string) (bool, error) {
prefix, hash, found := strings.Cut(keyStr, ".") prefix, hash, found := strings.Cut(keyStr, ".")
if !found { if !found {
return false, errAPIKeyFailedToParse return false, ErrAPIKeyFailedToParse
} }
key, err := h.GetAPIKey(prefix) key, err := h.GetAPIKey(prefix)

6
db.go
View File

@ -248,7 +248,7 @@ func (hi *HostInfo) Scan(destination interface{}) error {
return json.Unmarshal([]byte(value), hi) return json.Unmarshal([]byte(value), hi)
default: default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
} }
} }
@ -270,7 +270,7 @@ func (i *IPPrefixes) Scan(destination interface{}) error {
return json.Unmarshal([]byte(value), i) return json.Unmarshal([]byte(value), i)
default: default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
} }
} }
@ -292,7 +292,7 @@ func (i *StringList) Scan(destination interface{}) error {
return json.Unmarshal([]byte(value), i) return json.Unmarshal([]byte(value), i)
default: default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
} }
} }

View File

@ -18,14 +18,14 @@ import (
) )
const ( const (
errMachineNotFound = Error("machine not found") ErrMachineNotFound = Error("machine not found")
errMachineRouteIsNotAvailable = Error("route is not available on machine") ErrMachineRouteIsNotAvailable = Error("route is not available on machine")
errMachineAddressesInvalid = Error("failed to parse machine addresses") ErrMachineAddressesInvalid = Error("failed to parse machine addresses")
errMachineNotFoundRegistrationCache = Error( ErrMachineNotFoundRegistrationCache = Error(
"machine not found in registration cache", "machine not found in registration cache",
) )
errCouldNotConvertMachineInterface = Error("failed to convert machine interface") ErrCouldNotConvertMachineInterface = Error("failed to convert machine interface")
errHostnameTooLong = Error("Hostname too long") ErrHostnameTooLong = Error("Hostname too long")
MachineGivenNameHashLength = 8 MachineGivenNameHashLength = 8
MachineGivenNameTrimSize = 2 MachineGivenNameTrimSize = 2
) )
@ -112,7 +112,7 @@ func (ma *MachineAddresses) Scan(destination interface{}) error {
return nil return nil
default: default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination) return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
} }
} }
@ -337,7 +337,7 @@ func (h *Headscale) GetMachine(namespace string, name string) (*Machine, error)
} }
} }
return nil, errMachineNotFound 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.
@ -635,7 +635,7 @@ func (machine Machine) toNode(
return nil, fmt.Errorf( return nil, fmt.Errorf(
"hostname %q is too long it cannot except 255 ASCII chars: %w", "hostname %q is too long it cannot except 255 ASCII chars: %w",
hostname, hostname,
errHostnameTooLong, ErrHostnameTooLong,
) )
} }
} else { } else {
@ -785,11 +785,11 @@ func (h *Headscale) RegisterMachineFromAuthCallback(
return machine, err return machine, err
} else { } else {
return nil, errCouldNotConvertMachineInterface return nil, ErrCouldNotConvertMachineInterface
} }
} }
return nil, errMachineNotFoundRegistrationCache return nil, ErrMachineNotFoundRegistrationCache
} }
// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey. // RegisterMachine is executed from the CLI to register a new Machine using its MachineKey.
@ -877,7 +877,7 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error {
return fmt.Errorf( return fmt.Errorf(
"route (%s) is not available on node %s: %w", "route (%s) is not available on node %s: %w",
machine.Hostname, machine.Hostname,
newRoute, errMachineRouteIsNotAvailable, newRoute, ErrMachineRouteIsNotAvailable,
) )
} }
} }

View File

@ -16,10 +16,10 @@ import (
) )
const ( const (
errNamespaceExists = Error("Namespace already exists") ErrNamespaceExists = Error("Namespace already exists")
errNamespaceNotFound = Error("Namespace not found") ErrNamespaceNotFound = Error("Namespace not found")
errNamespaceNotEmptyOfNodes = Error("Namespace not empty: node(s) found") ErrNamespaceNotEmptyOfNodes = Error("Namespace not empty: node(s) found")
errInvalidNamespaceName = Error("Invalid namespace name") ErrInvalidNamespaceName = Error("Invalid namespace name")
) )
const ( const (
@ -47,7 +47,7 @@ func (h *Headscale) CreateNamespace(name string) (*Namespace, error) {
} }
namespace := Namespace{} namespace := Namespace{}
if err := h.db.Where("name = ?", name).First(&namespace).Error; err == nil { if err := h.db.Where("name = ?", name).First(&namespace).Error; err == nil {
return nil, errNamespaceExists return nil, ErrNamespaceExists
} }
namespace.Name = name namespace.Name = name
if err := h.db.Create(&namespace).Error; err != nil { if err := h.db.Create(&namespace).Error; err != nil {
@ -67,7 +67,7 @@ func (h *Headscale) CreateNamespace(name string) (*Namespace, error) {
func (h *Headscale) DestroyNamespace(name string) error { func (h *Headscale) DestroyNamespace(name string) error {
namespace, err := h.GetNamespace(name) namespace, err := h.GetNamespace(name)
if err != nil { if err != nil {
return errNamespaceNotFound return ErrNamespaceNotFound
} }
machines, err := h.ListMachinesInNamespace(name) machines, err := h.ListMachinesInNamespace(name)
@ -75,7 +75,7 @@ func (h *Headscale) DestroyNamespace(name string) error {
return err return err
} }
if len(machines) > 0 { if len(machines) > 0 {
return errNamespaceNotEmptyOfNodes return ErrNamespaceNotEmptyOfNodes
} }
keys, err := h.ListPreAuthKeys(name) keys, err := h.ListPreAuthKeys(name)
@ -110,9 +110,9 @@ func (h *Headscale) RenameNamespace(oldName, newName string) error {
} }
_, err = h.GetNamespace(newName) _, err = h.GetNamespace(newName)
if err == nil { if err == nil {
return errNamespaceExists return ErrNamespaceExists
} }
if !errors.Is(err, errNamespaceNotFound) { if !errors.Is(err, ErrNamespaceNotFound) {
return err return err
} }
@ -132,7 +132,7 @@ func (h *Headscale) GetNamespace(name string) (*Namespace, error) {
result.Error, result.Error,
gorm.ErrRecordNotFound, gorm.ErrRecordNotFound,
) { ) {
return nil, errNamespaceNotFound return nil, ErrNamespaceNotFound
} }
return &namespace, nil return &namespace, nil
@ -272,7 +272,7 @@ func NormalizeToFQDNRules(name string, stripEmailDomain bool) (string, error) {
return "", fmt.Errorf( return "", fmt.Errorf(
"label %v is more than 63 chars: %w", "label %v is more than 63 chars: %w",
elt, elt,
errInvalidNamespaceName, ErrInvalidNamespaceName,
) )
} }
} }
@ -285,21 +285,21 @@ func CheckForFQDNRules(name string) error {
return fmt.Errorf( return fmt.Errorf(
"DNS segment must not be over 63 chars. %v doesn't comply with this rule: %w", "DNS segment must not be over 63 chars. %v doesn't comply with this rule: %w",
name, name,
errInvalidNamespaceName, ErrInvalidNamespaceName,
) )
} }
if strings.ToLower(name) != name { if strings.ToLower(name) != name {
return fmt.Errorf( return fmt.Errorf(
"DNS segment should be lowercase. %v doesn't comply with this rule: %w", "DNS segment should be lowercase. %v doesn't comply with this rule: %w",
name, name,
errInvalidNamespaceName, ErrInvalidNamespaceName,
) )
} }
if invalidCharsInNamespaceRegex.MatchString(name) { if invalidCharsInNamespaceRegex.MatchString(name) {
return fmt.Errorf( return fmt.Errorf(
"DNS segment should only be composed of lowercase ASCII letters numbers, hyphen and dots. %v doesn't comply with theses rules: %w", "DNS segment should only be composed of lowercase ASCII letters numbers, hyphen and dots. %v doesn't comply with theses rules: %w",
name, name,
errInvalidNamespaceName, ErrInvalidNamespaceName,
) )
} }

View File

@ -26,7 +26,7 @@ func (s *Suite) TestCreateAndDestroyNamespace(c *check.C) {
func (s *Suite) TestDestroyNamespaceErrors(c *check.C) { func (s *Suite) TestDestroyNamespaceErrors(c *check.C) {
err := app.DestroyNamespace("test") err := app.DestroyNamespace("test")
c.Assert(err, check.Equals, errNamespaceNotFound) c.Assert(err, check.Equals, ErrNamespaceNotFound)
namespace, err := app.CreateNamespace("test") namespace, err := app.CreateNamespace("test")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
@ -60,7 +60,7 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) {
app.db.Save(&machine) app.db.Save(&machine)
err = app.DestroyNamespace("test") err = app.DestroyNamespace("test")
c.Assert(err, check.Equals, errNamespaceNotEmptyOfNodes) c.Assert(err, check.Equals, ErrNamespaceNotEmptyOfNodes)
} }
func (s *Suite) TestRenameNamespace(c *check.C) { func (s *Suite) TestRenameNamespace(c *check.C) {
@ -76,20 +76,20 @@ func (s *Suite) TestRenameNamespace(c *check.C) {
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = app.GetNamespace("test") _, err = app.GetNamespace("test")
c.Assert(err, check.Equals, errNamespaceNotFound) c.Assert(err, check.Equals, ErrNamespaceNotFound)
_, err = app.GetNamespace("test-renamed") _, err = app.GetNamespace("test-renamed")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
err = app.RenameNamespace("test-does-not-exit", "test") err = app.RenameNamespace("test-does-not-exit", "test")
c.Assert(err, check.Equals, errNamespaceNotFound) c.Assert(err, check.Equals, ErrNamespaceNotFound)
namespaceTest2, err := app.CreateNamespace("test2") namespaceTest2, err := app.CreateNamespace("test2")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(namespaceTest2.Name, check.Equals, "test2") c.Assert(namespaceTest2.Name, check.Equals, "test2")
err = app.RenameNamespace("test2", "test-renamed") err = app.RenameNamespace("test2", "test-renamed")
c.Assert(err, check.Equals, errNamespaceExists) c.Assert(err, check.Equals, ErrNamespaceExists)
} }
func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
@ -402,7 +402,7 @@ func (s *Suite) TestSetMachineNamespace(c *check.C) {
c.Assert(machine.Namespace.Name, check.Equals, newNamespace.Name) c.Assert(machine.Namespace.Name, check.Equals, newNamespace.Name)
err = app.SetMachineNamespace(&machine, "non-existing-namespace") err = app.SetMachineNamespace(&machine, "non-existing-namespace")
c.Assert(err, check.Equals, errNamespaceNotFound) c.Assert(err, check.Equals, ErrNamespaceNotFound)
err = app.SetMachineNamespace(&machine, newNamespace.Name) err = app.SetMachineNamespace(&machine, newNamespace.Name)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)

View File

@ -416,7 +416,7 @@ func (h *Headscale) OIDCCallback(
log.Debug().Msg("Registering new machine after successful callback") log.Debug().Msg("Registering new machine after successful callback")
namespace, err := h.GetNamespace(namespaceName) namespace, err := h.GetNamespace(namespaceName)
if errors.Is(err, errNamespaceNotFound) { if errors.Is(err, ErrNamespaceNotFound) {
namespace, err = h.CreateNamespace(namespaceName) namespace, err = h.CreateNamespace(namespaceName)
if err != nil { if err != nil {

View File

@ -14,10 +14,10 @@ import (
) )
const ( 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") 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.
@ -92,7 +92,7 @@ func (h *Headscale) GetPreAuthKey(namespace string, key string) (*PreAuthKey, er
} }
if pak.Namespace.Name != namespace { if pak.Namespace.Name != namespace {
return nil, errNamespaceMismatch return nil, ErrNamespaceMismatch
} }
return pak, nil return pak, nil
@ -135,11 +135,11 @@ func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) {
result.Error, result.Error,
gorm.ErrRecordNotFound, gorm.ErrRecordNotFound,
) { ) {
return nil, errPreAuthKeyNotFound return nil, ErrPreAuthKeyNotFound
} }
if pak.Expiration != nil && pak.Expiration.Before(time.Now()) { if pak.Expiration != nil && pak.Expiration.Before(time.Now()) {
return nil, errPreAuthKeyExpired return nil, ErrPreAuthKeyExpired
} }
if pak.Reusable || pak.Ephemeral { // we don't need to check if has been used before if pak.Reusable || pak.Ephemeral { // we don't need to check if has been used before
@ -152,7 +152,7 @@ func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) {
} }
if len(machines) != 0 || pak.Used { if len(machines) != 0 || pak.Used {
return nil, errSingleUseAuthKeyHasBeenUsed return nil, ErrSingleUseAuthKeyHasBeenUsed
} }
return &pak, nil return &pak, nil

View File

@ -44,13 +44,13 @@ func (*Suite) TestExpiredPreAuthKey(c *check.C) {
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
key, err := app.checkKeyValidity(pak.Key) key, err := app.checkKeyValidity(pak.Key)
c.Assert(err, check.Equals, errPreAuthKeyExpired) c.Assert(err, check.Equals, ErrPreAuthKeyExpired)
c.Assert(key, check.IsNil) c.Assert(key, check.IsNil)
} }
func (*Suite) TestPreAuthKeyDoesNotExist(c *check.C) { func (*Suite) TestPreAuthKeyDoesNotExist(c *check.C) {
key, err := app.checkKeyValidity("potatoKey") key, err := app.checkKeyValidity("potatoKey")
c.Assert(err, check.Equals, errPreAuthKeyNotFound) c.Assert(err, check.Equals, ErrPreAuthKeyNotFound)
c.Assert(key, check.IsNil) c.Assert(key, check.IsNil)
} }
@ -86,7 +86,7 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) {
app.db.Save(&machine) app.db.Save(&machine)
key, err := app.checkKeyValidity(pak.Key) key, err := app.checkKeyValidity(pak.Key)
c.Assert(err, check.Equals, errSingleUseAuthKeyHasBeenUsed) c.Assert(err, check.Equals, ErrSingleUseAuthKeyHasBeenUsed)
c.Assert(key, check.IsNil) c.Assert(key, check.IsNil)
} }
@ -174,7 +174,7 @@ func (*Suite) TestExpirePreauthKey(c *check.C) {
c.Assert(pak.Expiration, check.NotNil) c.Assert(pak.Expiration, check.NotNil)
key, err := app.checkKeyValidity(pak.Key) key, err := app.checkKeyValidity(pak.Key)
c.Assert(err, check.Equals, errPreAuthKeyExpired) c.Assert(err, check.Equals, ErrPreAuthKeyExpired)
c.Assert(key, check.IsNil) c.Assert(key, check.IsNil)
} }
@ -188,5 +188,5 @@ func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) {
app.db.Save(&pak) app.db.Save(&pak)
_, err = app.checkKeyValidity(pak.Key) _, err = app.checkKeyValidity(pak.Key)
c.Assert(err, check.Equals, errSingleUseAuthKeyHasBeenUsed) c.Assert(err, check.Equals, ErrSingleUseAuthKeyHasBeenUsed)
} }

View File

@ -7,7 +7,7 @@ import (
) )
const ( const (
errRouteIsNotAvailable = Error("route is not available") ErrRouteIsNotAvailable = Error("route is not available")
) )
// Deprecated: use machine function instead // Deprecated: use machine function instead
@ -106,7 +106,7 @@ func (h *Headscale) EnableNodeRoute(
} }
if !available { if !available {
return errRouteIsNotAvailable return ErrRouteIsNotAvailable
} }
machine.EnabledRoutes = enabledRoutes machine.EnabledRoutes = enabledRoutes

View File

@ -27,8 +27,8 @@ import (
) )
const ( const (
errCannotDecryptReponse = Error("cannot decrypt response") ErrCannotDecryptReponse = Error("cannot decrypt response")
errCouldNotAllocateIP = Error("could not find any suitable IP") ErrCouldNotAllocateIP = Error("could not find any suitable IP")
// These constants are copied from the upstream tailscale.com/types/key // These constants are copied from the upstream tailscale.com/types/key
// library, because they are not exported. // library, because they are not exported.
@ -120,7 +120,7 @@ func decode(
decrypted, ok := privKey.OpenFrom(*pubKey, msg) decrypted, ok := privKey.OpenFrom(*pubKey, msg)
if !ok { if !ok {
return errCannotDecryptReponse return ErrCannotDecryptReponse
} }
if err := json.Unmarshal(decrypted, output); err != nil { if err := json.Unmarshal(decrypted, output); err != nil {
@ -181,7 +181,7 @@ func (h *Headscale) getAvailableIP(ipPrefix netaddr.IPPrefix) (*netaddr.IP, erro
for { for {
if !ipPrefix.Contains(ip) { if !ipPrefix.Contains(ip) {
return nil, errCouldNotAllocateIP return nil, ErrCouldNotAllocateIP
} }
switch { switch {