fleet/server/platform/mysql/errors.go
Victor Lyuboslavsky 92bc1c650e
Move PostJSONWithTimeout to platform/http package and activity cleanup (#40561)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #38536

- Moved PostJSONWithTimeout to platform/http
- Created platform/errors package with only types needed by ctxerr. This
way, ctxerr did not need to import fleethttp.
- Made activity bounded context use PostJSONWithTimeout directly
- Removed some activity types from legacy code that were no longer
needed

# Checklist for submitter

- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- Changes file `38536-new-activity-bc` already present, and this is just
cleanup from that work.

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **Refactor**
* Reorganized error handling utilities for improved clarity and
decoupling.
* Consolidated HTTP utilities to centralize JSON posting functionality
with timeout support.
* Simplified activity service initialization by removing unused internal
parameters.
* Cleaned up test utilities and removed webhook-related test
scaffolding.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-26 17:39:10 -06:00

97 lines
2.5 KiB
Go

package mysql
import (
"database/sql"
"errors"
"fmt"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
platform_errors "github.com/fleetdm/fleet/v4/server/platform/errors"
"github.com/go-sql-driver/mysql"
)
type NotFoundError struct {
ID uint
Name string
Message string
ResourceType string
}
// Compile-time interface check.
var _ platform_errors.NotFoundError = &NotFoundError{}
func NotFound(kind string) *NotFoundError {
return &NotFoundError{
ResourceType: kind,
}
}
func (e *NotFoundError) Error() string {
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)
}
func (e *NotFoundError) WithID(id uint) error {
e.ID = id
return e
}
func (e *NotFoundError) WithName(name string) error {
e.Name = name
return e
}
func (e *NotFoundError) WithMessage(msg string) error {
e.Message = msg
return e
}
func (e *NotFoundError) IsNotFound() bool {
return true
}
// IsClientError implements ErrWithIsClientError.
func (e *NotFoundError) IsClientError() bool {
return true
}
// Is helps so that errors.Is(err, sql.ErrNoRows) returns true for an
// error of type *NotFoundError, without having to wrap sql.ErrNoRows
// explicitly.
func (e *NotFoundError) Is(other error) bool {
return other == sql.ErrNoRows
}
// 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
}