fix(errors): wrap db errors, improve error handling (#8859)

* use error check func, wrap db object not found

* add errorlint and fix all the linting errors

* add exportloopref linter and fix errors

* fix incorrect error details returned on an api

* fix new errors

* increase linter timeout

* increase timeout to 10minutes

* increase timeout to 10minutes

* rebase and fix new lint errors

* make CE match EE

* fix govet issue
pull/8906/head
Matt Hook 2023-05-05 12:19:47 +12:00 committed by GitHub
parent 550e235d59
commit 334eee0c8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 158 additions and 115 deletions

View File

@ -21,13 +21,12 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
node-version: '18'
cache: 'yarn'
- uses: actions/setup-go@v3
- uses: actions/setup-go@v4
with:
go-version: 1.19.4
go-version: 1.19.5
- run: yarn --frozen-lockfile
- name: Run linters
uses: wearerequired/lint-action@v1
with:
@ -44,4 +43,4 @@ jobs:
with:
version: latest
working-directory: api
args: -c .golangci.yaml
args: --timeout=10m -c .golangci.yaml

View File

@ -1,8 +1,13 @@
linters:
# Disable all linters.
# Disable all linters, the defaults don't pass on our code yet
disable-all: true
# Enable these for now
enable:
- depguard
- govet
- errorlint
- exportloopref
linters-settings:
depguard:
list-type: denylist
@ -13,14 +18,12 @@ linters-settings:
packages-with-error-message:
- github.com/sirupsen/logrus: 'logging is allowed only by github.com/rs/zerolog'
ignore-file-rules:
- "**/*_test.go"
# Create additional guards that follow the same configuration pattern.
# Results from all guards are aggregated together.
# additional-guards:
# - list-type: allowlist
# include-go-root: false
# packages:
# - github.com/sirupsen/logrus
# # Specify rules by which the linter ignores certain files for consideration.
# ignore-file-rules:
# - "!**/*_test.go"
- '**/*_test.go'
# errorlint is causing a typecheck error for some reason. The go compiler will report these
# anyway, so ignore them from the linter
issues:
exclude-rules:
- path: ./
linters:
- typecheck

View File

@ -3,6 +3,7 @@ package archive
import (
"archive/tar"
"compress/gzip"
"errors"
"fmt"
"io"
"os"
@ -84,7 +85,7 @@ func ExtractTarGz(r io.Reader, outputDirPath string) error {
for {
header, err := tarReader.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
@ -109,7 +110,7 @@ func ExtractTarGz(r io.Reader, outputDirPath string) error {
}
outFile.Close()
default:
return fmt.Errorf("Tar: uknown type: %v in %s",
return fmt.Errorf("tar: unknown type: %v in %s",
header.Typeflag,
header.Name)
}

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"math"
"os"
"path"
"time"
@ -182,7 +183,7 @@ func (connection *DbConnection) BackupTo(w io.Writer) error {
func (connection *DbConnection) ExportRaw(filename string) error {
databasePath := connection.GetDatabaseFilePath()
if _, err := os.Stat(databasePath); err != nil {
return fmt.Errorf("stat on %s failed: %s", databasePath, err)
return fmt.Errorf("stat on %s failed, error: %w", databasePath, err)
}
b, err := connection.ExportJSON(databasePath, true)
@ -201,6 +202,20 @@ func (connection *DbConnection) ConvertToKey(v int) []byte {
return b
}
// keyToString Converts a key to a string value suitable for logging
func keyToString(b []byte) string {
if len(b) != 8 {
return string(b)
}
v := binary.BigEndian.Uint64(b)
if v <= math.MaxInt32 {
return fmt.Sprintf("%d", v)
}
return string(b)
}
// CreateBucket is a generic function used to create a bucket inside a database.
func (connection *DbConnection) SetServiceName(bucketName string) error {
return connection.UpdateTx(func(tx portainer.Transaction) error {
@ -237,7 +252,7 @@ func (connection *DbConnection) UpdateObjectFunc(bucketName string, key []byte,
data := bucket.Get(key)
if data == nil {
return dserrors.ErrObjectNotFound
return fmt.Errorf("%w (bucket=%s, key=%s)", dserrors.ErrObjectNotFound, bucketName, keyToString(key))
}
err := connection.UnmarshalObjectWithJsoniter(data, object)

View File

@ -2,6 +2,7 @@ package boltdb
import (
"bytes"
"fmt"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
@ -24,7 +25,7 @@ func (tx *DbTransaction) GetObject(bucketName string, key []byte, object interfa
value := bucket.Get(key)
if value == nil {
return dserrors.ErrObjectNotFound
return fmt.Errorf("%w (bucket=%s, key=%s)", dserrors.ErrObjectNotFound, bucketName, keyToString(key))
}
data := make([]byte, len(value))
@ -74,7 +75,6 @@ func (tx *DbTransaction) GetNextIdentifier(bucketName string) int {
id, err := bucket.NextSequence()
if err != nil {
log.Error().Err(err).Str("bucket", bucketName).Msg("failed to get the next identifer")
return 0
}

View File

@ -5,7 +5,7 @@ import (
"testing"
portainer "github.com/portainer/portainer/api"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/portainer/portainer/api/dataservices"
)
const testBucketName = "test-bucket"
@ -97,7 +97,7 @@ func TestTxs(t *testing.T) {
err = conn.ViewTx(func(tx portainer.Transaction) error {
return tx.GetObject(testBucketName, conn.ConvertToKey(testId), &obj)
})
if err != dserrors.ErrObjectNotFound {
if !dataservices.IsErrObjectNotFound(err) {
t.Fatal(err)
}

View File

@ -2,10 +2,11 @@ package apikeyrepository
import (
"bytes"
"errors"
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices/errors"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/rs/zerolog/log"
)
@ -78,12 +79,12 @@ func (service *Service) GetAPIKeyByDigest(digest []byte) (*portainer.APIKey, err
return &portainer.APIKey{}, nil
})
if err == stop {
if errors.Is(err, stop) {
return k, nil
}
if err == nil {
return nil, errors.ErrObjectNotFound
return nil, dserrors.ErrObjectNotFound
}
return nil, err

View File

@ -1,9 +1,10 @@
package errors
import "errors"
import (
"errors"
)
var (
// TODO: i'm pretty sure this needs wrapping at several levels
ErrObjectNotFound = errors.New("object not found inside the database")
ErrWrongDBEdition = errors.New("the Portainer database is set for Portainer Business Edition, please follow the instructions in our documentation to downgrade it: https://documentation.portainer.io/v2.0-be/downgrade/be-to-ce/")
ErrDBImportFailed = errors.New("importing backup failed")

View File

@ -1,15 +1,13 @@
package dataservices
// "github.com/portainer/portainer/api/dataservices"
import (
"errors"
"io"
"time"
"github.com/portainer/portainer/api/database/models"
"github.com/portainer/portainer/api/dataservices/errors"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/database/models"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
)
type (
@ -325,5 +323,5 @@ type (
)
func IsErrObjectNotFound(e error) bool {
return e == errors.ErrObjectNotFound
return errors.Is(e, dserrors.ErrObjectNotFound)
}

View File

@ -1,6 +1,7 @@
package resourcecontrol
import (
"errors"
"fmt"
portainer "github.com/portainer/portainer/api"
@ -82,7 +83,7 @@ func (service *Service) ResourceControlByResourceIDAndType(resourceID string, re
return &portainer.ResourceControl{}, nil
})
if err == stop {
if errors.Is(err, stop) {
return resourceControl, nil
}

View File

@ -1,6 +1,7 @@
package resourcecontrol
import (
"errors"
"fmt"
portainer "github.com/portainer/portainer/api"
@ -60,7 +61,7 @@ func (service ServiceTx) ResourceControlByResourceIDAndType(resourceID string, r
return &portainer.ResourceControl{}, nil
})
if err == stop {
if errors.Is(err, stop) {
return resourceControl, nil
}

View File

@ -1,11 +1,12 @@
package stack
import (
"errors"
"fmt"
"strings"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices/errors"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/rs/zerolog/log"
)
@ -71,11 +72,11 @@ func (service *Service) StackByName(name string) (*portainer.Stack, error) {
return &portainer.Stack{}, nil
})
if err == stop {
if errors.Is(err, stop) {
return s, nil
}
if err == nil {
return nil, errors.ErrObjectNotFound
return nil, dserrors.ErrObjectNotFound
}
return nil, err
@ -92,7 +93,7 @@ func (service *Service) StacksByName(name string) ([]portainer.Stack, error) {
stack, ok := obj.(portainer.Stack)
if !ok {
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj)
return nil, fmt.Errorf("failed to convert to Stack object: %s", obj)
}
if stack.Name == name {
@ -173,11 +174,11 @@ func (service *Service) StackByWebhookID(id string) (*portainer.Stack, error) {
return &portainer.Stack{}, nil
})
if err == stop {
if errors.Is(err, stop) {
return s, nil
}
if err == nil {
return nil, errors.ErrObjectNotFound
return nil, dserrors.ErrObjectNotFound
}
return nil, err

View File

@ -59,7 +59,7 @@ func (b *stackBuilder) createNewStack(webhookID string) portainer.Stack {
Type: portainer.DockerComposeStack,
EndpointID: 2,
EntryPoint: filesystem.ComposeFileDefaultName,
Env: []portainer.Pair{{"Name1", "Value1"}},
Env: []portainer.Pair{{Name: "Name1", Value: "Value1"}},
Status: portainer.StackStatusActive,
CreationDate: time.Now().Unix(),
ProjectPath: "/tmp/project",

View File

@ -1,11 +1,12 @@
package team
import (
"errors"
"fmt"
"strings"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices/errors"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/rs/zerolog/log"
)
@ -71,11 +72,11 @@ func (service *Service) TeamByName(name string) (*portainer.Team, error) {
return &portainer.Team{}, nil
})
if err == stop {
if errors.Is(err, stop) {
return t, nil
}
if err == nil {
return nil, errors.ErrObjectNotFound
return nil, dserrors.ErrObjectNotFound
}
return nil, err

View File

@ -1,11 +1,12 @@
package user
import (
"errors"
"fmt"
"strings"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices/errors"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/rs/zerolog/log"
)
@ -72,12 +73,12 @@ func (service *Service) UserByUsername(username string) (*portainer.User, error)
return &portainer.User{}, nil
})
if err == stop {
if errors.Is(err, stop) {
return u, nil
}
if err == nil {
return nil, errors.ErrObjectNotFound
return nil, dserrors.ErrObjectNotFound
}
return nil, err

View File

@ -1,10 +1,11 @@
package webhook
import (
"errors"
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices/errors"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/rs/zerolog/log"
)
@ -93,12 +94,12 @@ func (service *Service) WebhookByResourceID(ID string) (*portainer.Webhook, erro
return &portainer.Webhook{}, nil
})
if err == stop {
if errors.Is(err, stop) {
return w, nil
}
if err == nil {
return nil, errors.ErrObjectNotFound
return nil, dserrors.ErrObjectNotFound
}
return nil, err
@ -127,12 +128,12 @@ func (service *Service) WebhookByToken(token string) (*portainer.Webhook, error)
return &portainer.Webhook{}, nil
})
if err == stop {
if errors.Is(err, stop) {
return w, nil
}
if err == nil {
return nil, errors.ErrObjectNotFound
return nil, dserrors.ErrObjectNotFound
}
return nil, err

View File

@ -115,7 +115,7 @@ func (store *Store) backupWithOptions(options *BackupOptions) (string, error) {
if err := store.Close(); err != nil {
return options.BackupPath, fmt.Errorf(
"error closing datastore before creating backup: %v",
"error closing datastore before creating backup: %w",
err,
)
}
@ -126,7 +126,7 @@ func (store *Store) backupWithOptions(options *BackupOptions) (string, error) {
if _, err := store.Open(); err != nil {
return options.BackupPath, fmt.Errorf(
"error opening datastore after creating backup: %v",
"error opening datastore after creating backup: %w",
err,
)
}

View File

@ -1,6 +1,7 @@
package datastore
import (
"errors"
"fmt"
"io"
"os"
@ -104,7 +105,7 @@ func (store *Store) edition() portainer.SoftwareEdition {
// TODO: move the use of this to dataservices.IsErrObjectNotFound()?
func (store *Store) IsErrObjectNotFound(e error) bool {
return e == portainerErrors.ErrObjectNotFound
return errors.Is(e, portainerErrors.ErrObjectNotFound)
}
func (store *Store) Connection() portainer.Connection {

View File

@ -288,7 +288,7 @@ func migrateDBTestHelper(t *testing.T, srcPath, wantPath string, overrideInstanc
// Convert database back to json.
databasePath := con.GetDatabaseFilePath()
if _, err := os.Stat(databasePath); err != nil {
return fmt.Errorf("stat on %s failed: %s", databasePath, err)
return fmt.Errorf("stat on %s failed: %w", databasePath, err)
}
gotJSON, err := con.ExportJSON(databasePath, false)

View File

@ -2,7 +2,7 @@ package migrator
import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices/errors"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/stacks/stackutils"
"github.com/rs/zerolog/log"
@ -25,7 +25,7 @@ func (m *Migrator) updateStackResourceControlToDB27() error {
stack, err := m.stackService.StackByName(stackName)
if err != nil {
if err == errors.ErrObjectNotFound {
if dataservices.IsErrObjectNotFound(err) {
continue
}

View File

@ -4,7 +4,7 @@ import (
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices/errors"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/endpointutils"
snapshotutils "github.com/portainer/portainer/api/internal/snapshot"
@ -86,7 +86,7 @@ func (m *Migrator) updateDockerhubToDB32() error {
log.Info().Msg("updating dockerhub")
dockerhub, err := m.dockerhubService.DockerHub()
if err == errors.ErrObjectNotFound {
if dataservices.IsErrObjectNotFound(err) {
return nil
} else if err != nil {
return err

View File

@ -1,7 +1,7 @@
package migrator
import (
"github.com/portainer/portainer/api/dataservices/errors"
"github.com/portainer/portainer/api/dataservices"
"github.com/rs/zerolog/log"
)
@ -19,7 +19,7 @@ func (m *Migrator) migrateDBVersionToDB71() error {
if err == nil {
log.Debug().Int("endpoint_id", int(s.EndpointID)).Msg("keeping snapshot")
continue
} else if err != errors.ErrObjectNotFound {
} else if !dataservices.IsErrObjectNotFound(err) {
log.Debug().Int("endpoint_id", int(s.EndpointID)).Err(err).Msg("database error")
return err
}

View File

@ -4,7 +4,7 @@ import (
"github.com/rs/zerolog/log"
portainer "github.com/portainer/portainer/api"
portainerDsErrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/portainer/portainer/api/dataservices"
)
func (m *Migrator) migrateDBVersionToDB90() error {
@ -30,7 +30,7 @@ func (m *Migrator) updateEdgeStackStatusForDB90() error {
for _, edgeJob := range edgeJobs {
for endpointId := range edgeJob.Endpoints {
_, err := m.endpointService.Endpoint(endpointId)
if err == portainerDsErrors.ErrObjectNotFound {
if dataservices.IsErrObjectNotFound(err) {
delete(edgeJob.Endpoints, endpointId)
err = m.edgeJobService.UpdateEdgeJob(edgeJob.ID, &edgeJob)

View File

@ -178,6 +178,7 @@ func snapshotContainers(snapshot *portainer.DockerSnapshot, cli *client.Client)
} else {
var gpuOptions *_container.DeviceRequest = nil
for _, deviceRequest := range response.HostConfig.Resources.DeviceRequests {
deviceRequest := deviceRequest
if deviceRequest.Driver == "nvidia" || deviceRequest.Capabilities[0][0] == "gpu" {
gpuOptions = &deviceRequest
}

View File

@ -53,7 +53,7 @@ func CloneWithBackup(gitService portainer.GitService, fileService portainer.File
log.Warn().Err(restoreError).Msg("failed restoring backup folder")
}
if err == gittypes.ErrAuthenticationFailure {
if errors.Is(err, gittypes.ErrAuthenticationFailure) {
return cleanFn, errors.WithMessage(err, ErrInvalidGitCredential.Error())
}

View File

@ -1,6 +1,7 @@
package edgegroups
import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
@ -39,8 +40,9 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
func txResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
if err != nil {
if httpErr, ok := err.(*httperror.HandlerError); ok {
return httpErr
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)

View File

@ -1,6 +1,7 @@
package edgejobs
import (
"errors"
"net/http"
"strconv"
@ -42,8 +43,9 @@ func (handler *Handler) edgeJobDelete(w http.ResponseWriter, r *http.Request) *h
}
if err != nil {
if httpErr, ok := err.(*httperror.HandlerError); ok {
return httpErr
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)

View File

@ -1,6 +1,7 @@
package edgejobs
import (
"errors"
"net/http"
"strconv"
@ -75,8 +76,9 @@ func (handler *Handler) edgeJobTasksClear(w http.ResponseWriter, r *http.Request
}
if err != nil {
if httpErr, ok := err.(*httperror.HandlerError); ok {
return httpErr
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)

View File

@ -1,6 +1,7 @@
package edgejobs
import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
@ -83,8 +84,9 @@ func (handler *Handler) edgeJobTasksCollect(w http.ResponseWriter, r *http.Reque
})
if err != nil {
if httpErr, ok := err.(*httperror.HandlerError); ok {
return httpErr
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)

View File

@ -1,6 +1,7 @@
package edgejobs
import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
@ -62,8 +63,9 @@ func convertEndpointsToMetaObject(endpoints []portainer.EndpointID) map[portaine
func txResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
if err != nil {
if httpErr, ok := err.(*httperror.HandlerError); ok {
return httpErr
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)

View File

@ -153,8 +153,8 @@ func registryAccessPoliciesContainsNamespace(registryAccess portainer.RegistryAc
func (handler *Handler) filterKubernetesRegistriesByUserRole(r *http.Request, registries []portainer.Registry, endpoint *portainer.Endpoint, user *portainer.User) ([]portainer.Registry, *httperror.HandlerError) {
err := handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint)
if err == security.ErrAuthorizationRequired {
return nil, httperror.Forbidden("User is not authorized", errors.New("missing namespace query parameter"))
if errors.Is(err, security.ErrAuthorizationRequired) {
return nil, httperror.Forbidden("User is not authorized", err)
}
if err != nil {
return nil, httperror.InternalServerError("Unable to retrieve info from request context", err)

View File

@ -29,7 +29,7 @@ type repositoryFilePreviewPayload struct {
func (payload *repositoryFilePreviewPayload) Validate(r *http.Request) error {
if govalidator.IsNull(payload.Repository) || !govalidator.IsURL(payload.Repository) {
return errors.New("Invalid repository URL. Must correspond to a valid URL format")
return errors.New("invalid repository URL. Must correspond to a valid URL format")
}
if govalidator.IsNull(payload.Reference) {
@ -37,7 +37,7 @@ func (payload *repositoryFilePreviewPayload) Validate(r *http.Request) error {
}
if govalidator.IsNull(payload.TargetFile) {
return errors.New("Invalid target filename.")
return errors.New("invalid target filename")
}
return nil
@ -70,11 +70,11 @@ func (handler *Handler) gitOperationRepoFilePreview(w http.ResponseWriter, r *ht
err = handler.gitService.CloneRepository(projectPath, payload.Repository, payload.Reference, payload.Username, payload.Password, payload.TLSSkipVerify)
if err != nil {
if err == gittypes.ErrAuthenticationFailure {
if errors.Is(err, gittypes.ErrAuthenticationFailure) {
return httperror.BadRequest("Invalid git credential", err)
}
newErr := fmt.Errorf("unable to clone git repository: %w", err)
newErr := fmt.Errorf("unable to clone git repository, error: %w", err)
return httperror.InternalServerError(newErr.Error(), newErr)
}

View File

@ -9,7 +9,6 @@ import (
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
bolterrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/portainer/portainer/api/internal/endpointutils"
)
@ -33,7 +32,7 @@ func (handler *Handler) openAMTActivate(w http.ResponseWriter, r *http.Request)
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
if err == bolterrors.ErrObjectNotFound {
if handler.DataStore.IsErrObjectNotFound(err) {
return httperror.NotFound("Unable to find an endpoint with the specified identifier inside the database", err)
} else if err != nil {
return httperror.InternalServerError("Unable to find an endpoint with the specified identifier inside the database", err)

View File

@ -8,7 +8,6 @@ import (
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
bolterrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/rs/zerolog/log"
)
@ -33,7 +32,7 @@ func (handler *Handler) openAMTDevices(w http.ResponseWriter, r *http.Request) *
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
if err == bolterrors.ErrObjectNotFound {
if handler.DataStore.IsErrObjectNotFound(err) {
return httperror.NotFound("Unable to find an endpoint with the specified identifier inside the database", err)
} else if err != nil {
return httperror.InternalServerError("Unable to find an endpoint with the specified identifier inside the database", err)

View File

@ -12,7 +12,6 @@ import (
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
bolterrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/portainer/portainer/api/hostmanagement/openamt"
"github.com/docker/docker/api/types"
@ -63,7 +62,7 @@ func (handler *Handler) openAMTHostInfo(w http.ResponseWriter, r *http.Request)
log.Info().Int("endpointID", endpointID).Msg("OpenAMTHostInfo")
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
if err == bolterrors.ErrObjectNotFound {
if handler.DataStore.IsErrObjectNotFound(err) {
return httperror.NotFound("Unable to find an endpoint with the specified identifier inside the database", err)
} else if err != nil {
return httperror.InternalServerError("Unable to find an endpoint with the specified identifier inside the database", err)

View File

@ -7,7 +7,6 @@ import (
"strconv"
portainer "github.com/portainer/portainer/api"
portainerDsErrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/portainer/portainer/api/kubernetes"
"github.com/gorilla/mux"
@ -121,7 +120,7 @@ func (handler *Handler) kubeClient(next http.Handler) http.Handler {
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
if err == portainerDsErrors.ErrObjectNotFound {
if handler.DataStore.IsErrObjectNotFound(err) {
httperror.WriteError(
w,
http.StatusNotFound,

View File

@ -8,7 +8,6 @@ import (
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
portainerDsErrors "github.com/portainer/portainer/api/dataservices/errors"
models "github.com/portainer/portainer/api/http/models/kubernetes"
"github.com/portainer/portainer/api/http/security"
)
@ -23,7 +22,7 @@ func (handler *Handler) getKubernetesIngressControllers(w http.ResponseWriter, r
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
if err == portainerDsErrors.ErrObjectNotFound {
if handler.DataStore.IsErrObjectNotFound(err) {
return httperror.NotFound(
"Unable to find an environment with the specified identifier inside the database",
err,

View File

@ -1,6 +1,7 @@
package stacks
import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
@ -38,7 +39,8 @@ func (handler *Handler) webhookInvoke(w http.ResponseWriter, r *http.Request) *h
}
if err = deployments.RedeployWhenChanged(stack.ID, handler.StackDeployer, handler.DataStore, handler.GitService); err != nil {
if _, ok := err.(*deployments.StackAuthorMissingErr); ok {
var StackAuthorMissingErr *deployments.StackAuthorMissingErr
if errors.As(err, &StackAuthorMissingErr) {
return &httperror.HandlerError{StatusCode: http.StatusConflict, Message: "Autoupdate for the stack isn't available", Err: err}
}

View File

@ -1,6 +1,7 @@
package tags
import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
@ -34,8 +35,9 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
func txResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
if err != nil {
if httpErr, ok := err.(*httperror.HandlerError); ok {
return httpErr
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)

View File

@ -1,6 +1,7 @@
package tags
import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
@ -41,8 +42,9 @@ func (handler *Handler) tagDelete(w http.ResponseWriter, r *http.Request) *httpe
}
if err != nil {
if httpErr, ok := err.(*httperror.HandlerError); ok {
return httpErr
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
return handlerError
}
return httperror.InternalServerError("Unexpected error", err)

View File

@ -1,6 +1,7 @@
package users
import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
@ -66,7 +67,7 @@ func (handler *Handler) userRemoveAccessToken(w http.ResponseWriter, r *http.Req
err = handler.apiKeyService.DeleteAPIKey(portainer.APIKeyID(apiKeyID))
if err != nil {
if err == apikey.ErrInvalidAPIKey {
if errors.Is(err, apikey.ErrInvalidAPIKey) {
return httperror.NotFound("Unable to find an api-key with the specified identifier inside the database", err)
}
return httperror.InternalServerError("Unable to remove the api-key from the user", err)

View File

@ -1,6 +1,7 @@
package websocket
import (
"errors"
"fmt"
"net/http"
"net/http/httputil"
@ -11,7 +12,7 @@ import (
func hijackRequest(websocketConn *websocket.Conn, httpConn *httputil.ClientConn, request *http.Request) error {
// Server hijacks the connection, error 'connection closed' expected
resp, err := httpConn.Do(request)
if err != httputil.ErrPersistEOF {
if !errors.Is(err, httputil.ErrPersistEOF) {
if err != nil {
return err
}

View File

@ -8,7 +8,7 @@ import (
"github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
bolterrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/portainer/portainer/api/dataservices"
)
type ItemContextKey string
@ -31,7 +31,7 @@ func WithItem[TId ~int, TObject any](getter ItemGetter[TId, TObject], idParam st
item, err := getter(TId(itemId))
if err != nil {
statusCode := http.StatusInternalServerError
if err == bolterrors.ErrObjectNotFound {
if dataservices.IsErrObjectNotFound(err) {
statusCode = http.StatusNotFound
}
httperror.WriteError(rw, statusCode, "Unable to find a object with the specified identifier inside the database", err)

View File

@ -37,6 +37,7 @@ func createRegistryAuthenticationHeader(
} else { // any "custom" registry
var matchingRegistry *portainer.Registry
for _, registry := range accessContext.registries {
registry := registry
if registry.ID == registryId &&
(accessContext.isAdmin ||
security.AuthorizedRegistryAccess(&registry, accessContext.user, accessContext.teamMemberships, accessContext.endpointID)) {

View File

@ -15,7 +15,6 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
dataerrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/portainer/portainer/api/docker"
"github.com/portainer/portainer/api/http/proxy/factory/utils"
"github.com/portainer/portainer/api/http/security"
@ -647,7 +646,7 @@ func (transport *Transport) executeGenericResourceDeletionOperation(request *htt
if response.StatusCode == http.StatusNoContent || response.StatusCode == http.StatusOK {
resourceControl, err := transport.dataStore.ResourceControl().ResourceControlByResourceIDAndType(resourceIdentifierAttribute, resourceType)
if err != nil {
if err == dataerrors.ErrObjectNotFound {
if dataservices.IsErrObjectNotFound(err) {
return response, nil
}

View File

@ -18,6 +18,7 @@ func EdgeStackRelatedEndpoints(edgeGroupIDs []portainer.EdgeGroupID, endpoints [
var edgeGroup *portainer.EdgeGroup
for _, group := range edgeGroups {
group := group
if group.ID == edgeGroupID {
edgeGroup = &group
break

View File

@ -81,7 +81,7 @@ func (service *Service) PersistEdgeStack(
relatedEndpointIds, err := edge.EdgeStackRelatedEndpoints(stack.EdgeGroups, relationConfig.Endpoints, relationConfig.EndpointGroups, relationConfig.EdgeGroups)
if err != nil {
if err == edge.ErrEdgeGroupNotFound {
if errors.Is(err, edge.ErrEdgeGroupNotFound) {
return nil, httperrors.NewInvalidPayloadError(err.Error())
}
return nil, fmt.Errorf("unable to persist environment relation in database: %w", err)

View File

@ -1,6 +1,7 @@
package cli
import (
"context"
"errors"
"io"
@ -50,13 +51,14 @@ func (kcl *KubeClient) StartExecProcess(token string, useAdminToken bool, namesp
return
}
err = exec.Stream(remotecommand.StreamOptions{
err = exec.StreamWithContext(context.TODO(), remotecommand.StreamOptions{
Stdin: stdin,
Stdout: stdout,
Tty: true,
})
if err != nil {
if _, ok := err.(utilexec.ExitError); !ok {
var exitError utilexec.ExitError
if !errors.As(err, &exitError) {
errChan <- errors.New("unable to start exec process")
}
}

View File

@ -2,6 +2,7 @@ package cli
import (
"context"
"errors"
"testing"
"time"
@ -30,7 +31,7 @@ func Test_waitForPodStatus(t *testing.T) {
ctx, cancel := context.WithCancel(context.TODO())
cancel()
err := k.waitForPodStatus(ctx, v1.PodRunning, podSpec)
if err != context.Canceled {
if !errors.Is(err, context.Canceled) {
t.Errorf("waitForPodStatus should throw context cancellation error; err=%s", err)
}
})
@ -59,7 +60,7 @@ func Test_waitForPodStatus(t *testing.T) {
ctx, cancelFunc := context.WithTimeout(context.TODO(), 0*time.Second)
defer cancelFunc()
err = k.waitForPodStatus(ctx, v1.PodRunning, podSpec)
if err != context.DeadlineExceeded {
if !errors.Is(err, context.DeadlineExceeded) {
t.Errorf("waitForPodStatus should throw deadline exceeded error; err=%s", err)
}
})

View File

@ -55,7 +55,7 @@ func (s *Scheduler) Shutdown() error {
s.mu.Unlock()
err := ctx.Err()
if err == context.Canceled {
if errors.Is(err, context.Canceled) {
return nil
}
return err

View File

@ -27,7 +27,7 @@ func DownloadGitRepository(config gittypes.RepoConfig, gitService portainer.GitS
projectPath := getProjectPath()
err := gitService.CloneRepository(projectPath, config.URL, config.ReferenceName, username, password, config.TLSSkipVerify)
if err != nil {
if err == gittypes.ErrAuthenticationFailure {
if errors.Is(err, gittypes.ErrAuthenticationFailure) {
newErr := ErrInvalidGitCredential
return "", newErr
}

4
golangci-lint.sh Normal file → Executable file
View File

@ -2,10 +2,10 @@
#!/bin/bash
cd api
if golangci-lint run -c .golangci.yaml
if golangci-lint run --timeout=10m -c .golangci.yaml
then
echo "golangci-lint run successfully"
else
echo "golangci-lint run failed"
exit 1
fi
fi