2026-01-08 19:17:19 +00:00
|
|
|
package mysql
|
2025-02-13 20:32:19 +00:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"database/sql"
|
2026-02-17 13:10:52 +00:00
|
|
|
"errors"
|
2025-02-13 20:32:19 +00:00
|
|
|
"fmt"
|
2025-02-18 21:28:54 +00:00
|
|
|
|
2026-02-17 13:10:52 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
2026-02-26 23:39:10 +00:00
|
|
|
platform_errors "github.com/fleetdm/fleet/v4/server/platform/errors"
|
2026-02-17 13:10:52 +00:00
|
|
|
"github.com/go-sql-driver/mysql"
|
2025-02-13 20:32:19 +00:00
|
|
|
)
|
|
|
|
|
|
2025-02-18 21:28:54 +00:00
|
|
|
type NotFoundError struct {
|
2025-02-13 20:32:19 +00:00
|
|
|
ID uint
|
|
|
|
|
Name string
|
|
|
|
|
Message string
|
|
|
|
|
ResourceType string
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 21:28:54 +00:00
|
|
|
// Compile-time interface check.
|
2026-02-26 23:39:10 +00:00
|
|
|
var _ platform_errors.NotFoundError = &NotFoundError{}
|
2025-02-18 21:28:54 +00:00
|
|
|
|
|
|
|
|
func NotFound(kind string) *NotFoundError {
|
|
|
|
|
return &NotFoundError{
|
2025-02-13 20:32:19 +00:00
|
|
|
ResourceType: kind,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 21:28:54 +00:00
|
|
|
func (e *NotFoundError) Error() string {
|
2025-02-13 20:32:19 +00:00
|
|
|
if e.ID != 0 {
|
|
|
|
|
return fmt.Sprintf("%s %d was not found in the datastore", e.ResourceType, e.ID)
|
|
|
|
|
}
|
|
|
|
|
if e.Name != "" {
|
|
|
|
|
return fmt.Sprintf("%s %s was not found in the datastore", e.ResourceType, e.Name)
|
|
|
|
|
}
|
|
|
|
|
if e.Message != "" {
|
|
|
|
|
return fmt.Sprintf("%s %s was not found in the datastore", e.ResourceType, e.Message)
|
|
|
|
|
}
|
|
|
|
|
return fmt.Sprintf("%s was not found in the datastore", e.ResourceType)
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 21:28:54 +00:00
|
|
|
func (e *NotFoundError) WithID(id uint) error {
|
2025-02-13 20:32:19 +00:00
|
|
|
e.ID = id
|
|
|
|
|
return e
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 21:28:54 +00:00
|
|
|
func (e *NotFoundError) WithName(name string) error {
|
2025-02-13 20:32:19 +00:00
|
|
|
e.Name = name
|
|
|
|
|
return e
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 21:28:54 +00:00
|
|
|
func (e *NotFoundError) WithMessage(msg string) error {
|
2025-02-13 20:32:19 +00:00
|
|
|
e.Message = msg
|
|
|
|
|
return e
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 21:28:54 +00:00
|
|
|
func (e *NotFoundError) IsNotFound() bool {
|
2025-02-13 20:32:19 +00:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-26 23:07:32 +00:00
|
|
|
// IsClientError implements ErrWithIsClientError.
|
|
|
|
|
func (e *NotFoundError) IsClientError() bool {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 20:32:19 +00:00
|
|
|
// Is helps so that errors.Is(err, sql.ErrNoRows) returns true for an
|
2025-02-18 21:28:54 +00:00
|
|
|
// error of type *NotFoundError, without having to wrap sql.ErrNoRows
|
2025-02-13 20:32:19 +00:00
|
|
|
// explicitly.
|
2025-02-18 21:28:54 +00:00
|
|
|
func (e *NotFoundError) Is(other error) bool {
|
2025-02-13 20:32:19 +00:00
|
|
|
return other == sql.ErrNoRows
|
|
|
|
|
}
|
2026-02-17 13:10:52 +00:00
|
|
|
|
|
|
|
|
// MySQL error numbers for read-only conditions. These are not included in the
|
|
|
|
|
// VividCortex/mysqlerr package, so we define them here.
|
|
|
|
|
const (
|
|
|
|
|
// erReadOnlyTransaction is MySQL error 1792: Cannot execute statement in a READ ONLY transaction.
|
|
|
|
|
erReadOnlyTransaction = 1792
|
|
|
|
|
// erOptionPreventsStatement is MySQL error 1290: The MySQL server is running with the --read-only option.
|
|
|
|
|
erOptionPreventsStatement = 1290
|
|
|
|
|
// erReadOnlyMode is MySQL error 1836: Running in read-only mode.
|
|
|
|
|
erReadOnlyMode = 1836
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// IsReadOnlyError returns true if the error is a MySQL error indicating that
|
|
|
|
|
// the server is in read-only mode. This typically happens after an Aurora
|
|
|
|
|
// failover when the primary has been demoted to a reader.
|
|
|
|
|
func IsReadOnlyError(err error) bool {
|
|
|
|
|
err = ctxerr.Cause(err)
|
|
|
|
|
var mySQLErr *mysql.MySQLError
|
|
|
|
|
if errors.As(err, &mySQLErr) {
|
|
|
|
|
switch mySQLErr.Number {
|
|
|
|
|
case erReadOnlyTransaction, erOptionPreventsStatement, erReadOnlyMode:
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|