fix(errors): improve error handling EE-4430 (#11987)

pull/11990/head
andres-portainer 2024-06-28 17:35:26 -03:00 committed by GitHub
parent dc62604ed8
commit 4adce14485
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 82 additions and 88 deletions

View File

@ -6,6 +6,8 @@ import (
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/dataservices"
"github.com/rs/zerolog/log"
) )
// BucketName represents the name of the bucket where this service stores data. // BucketName represents the name of the bucket where this service stores data.
@ -157,6 +159,7 @@ func (service *Service) EndpointsByTeamID(teamID portainer.TeamID) ([]portainer.
return true return true
} }
} }
return false return false
}), }),
) )
@ -166,11 +169,13 @@ func (service *Service) EndpointsByTeamID(teamID portainer.TeamID) ([]portainer.
func (service *Service) GetNextIdentifier() int { func (service *Service) GetNextIdentifier() int {
var identifier int var identifier int
service.connection.UpdateTx(func(tx portainer.Transaction) error { if err := service.connection.UpdateTx(func(tx portainer.Transaction) error {
identifier = service.Tx(tx).GetNextIdentifier() identifier = service.Tx(tx).GetNextIdentifier()
return nil return nil
}) }); err != nil {
log.Error().Err(err).Str("bucket", BucketName).Msg("could not get the next identifier")
}
return identifier return identifier
} }

View File

@ -32,8 +32,7 @@ func (service *Service) RegisterUpdateStackFunction(
// NewService creates a new instance of a service. // NewService creates a new instance of a service.
func NewService(connection portainer.Connection) (*Service, error) { func NewService(connection portainer.Connection) (*Service, error) {
err := connection.SetServiceName(BucketName) if err := connection.SetServiceName(BucketName); err != nil {
if err != nil {
return nil, err return nil, err
} }
@ -65,8 +64,7 @@ func (service *Service) EndpointRelation(endpointID portainer.EndpointID) (*port
var endpointRelation portainer.EndpointRelation var endpointRelation portainer.EndpointRelation
identifier := service.connection.ConvertToKey(int(endpointID)) identifier := service.connection.ConvertToKey(int(endpointID))
err := service.connection.GetObject(BucketName, identifier, &endpointRelation) if err := service.connection.GetObject(BucketName, identifier, &endpointRelation); err != nil {
if err != nil {
return nil, err return nil, err
} }
@ -161,8 +159,12 @@ func (service *Service) updateEdgeStacksAfterRelationChange(previousRelationStat
// list how many time this stack is referenced in all relations // list how many time this stack is referenced in all relations
// in order to update the stack deployments count // in order to update the stack deployments count
for refStackId, refStackEnabled := range stacksToUpdate { for refStackId, refStackEnabled := range stacksToUpdate {
if refStackEnabled { if !refStackEnabled {
continue
}
numDeployments := 0 numDeployments := 0
for _, r := range relations { for _, r := range relations {
for sId, enabled := range r.EdgeStacks { for sId, enabled := range r.EdgeStacks {
if enabled && sId == refStackId { if enabled && sId == refStackId {
@ -171,9 +173,10 @@ func (service *Service) updateEdgeStacksAfterRelationChange(previousRelationStat
} }
} }
service.updateStackFn(refStackId, func(edgeStack *portainer.EdgeStack) { if err := service.updateStackFn(refStackId, func(edgeStack *portainer.EdgeStack) {
edgeStack.NumDeployments = numDeployments edgeStack.NumDeployments = numDeployments
}) }); err != nil {
log.Error().Err(err).Msg("could not update the number of deployments")
} }
} }
} }

View File

@ -33,8 +33,7 @@ func (service ServiceTx) EndpointRelation(endpointID portainer.EndpointID) (*por
var endpointRelation portainer.EndpointRelation var endpointRelation portainer.EndpointRelation
identifier := service.service.connection.ConvertToKey(int(endpointID)) identifier := service.service.connection.ConvertToKey(int(endpointID))
err := service.tx.GetObject(BucketName, identifier, &endpointRelation) if err := service.tx.GetObject(BucketName, identifier, &endpointRelation); err != nil {
if err != nil {
return nil, err return nil, err
} }
@ -129,7 +128,10 @@ func (service ServiceTx) updateEdgeStacksAfterRelationChange(previousRelationSta
// list how many time this stack is referenced in all relations // list how many time this stack is referenced in all relations
// in order to update the stack deployments count // in order to update the stack deployments count
for refStackId, refStackEnabled := range stacksToUpdate { for refStackId, refStackEnabled := range stacksToUpdate {
if refStackEnabled { if !refStackEnabled {
continue
}
numDeployments := 0 numDeployments := 0
for _, r := range relations { for _, r := range relations {
for sId, enabled := range r.EdgeStacks { for sId, enabled := range r.EdgeStacks {
@ -139,9 +141,10 @@ func (service ServiceTx) updateEdgeStacksAfterRelationChange(previousRelationSta
} }
} }
service.service.updateStackFnTx(service.tx, refStackId, func(edgeStack *portainer.EdgeStack) { if err := service.service.updateStackFnTx(service.tx, refStackId, func(edgeStack *portainer.EdgeStack) {
edgeStack.NumDeployments = numDeployments edgeStack.NumDeployments = numDeployments
}) }); err != nil {
log.Error().Err(err).Msg("could not update the number of deployments")
} }
} }
} }

View File

@ -4,14 +4,14 @@ import (
"context" "context"
"strings" "strings"
"github.com/docker/docker/api/types"
dockercontainer "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/dataservices"
dockerclient "github.com/portainer/portainer/api/docker/client" dockerclient "github.com/portainer/portainer/api/docker/client"
"github.com/portainer/portainer/api/docker/images" "github.com/portainer/portainer/api/docker/images"
"github.com/docker/docker/api/types"
dockercontainer "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@ -54,8 +54,7 @@ func (c *ContainerService) Recreate(ctx context.Context, endpoint *portainer.End
} }
if imageTag != "" { if imageTag != "" {
err = img.WithTag(imageTag) if err := img.WithTag(imageTag); err != nil {
if err != nil {
return nil, errors.Wrapf(err, "set image tag error %s", imageTag) return nil, errors.Wrapf(err, "set image tag error %s", imageTag)
} }
@ -67,23 +66,20 @@ func (c *ContainerService) Recreate(ctx context.Context, endpoint *portainer.End
// 1. pull image if you need force pull // 1. pull image if you need force pull
if forcePullImage { if forcePullImage {
puller := images.NewPuller(cli, images.NewRegistryClient(c.dataStore), c.dataStore) puller := images.NewPuller(cli, images.NewRegistryClient(c.dataStore), c.dataStore)
err = puller.Pull(ctx, img) if err := puller.Pull(ctx, img); err != nil {
if err != nil {
return nil, errors.Wrapf(err, "pull image error %s", img.FullName()) return nil, errors.Wrapf(err, "pull image error %s", img.FullName())
} }
} }
// 2. stop the current container // 2. stop the current container
log.Debug().Str("container_id", containerId).Msg("starting to stop the container") log.Debug().Str("container_id", containerId).Msg("starting to stop the container")
err = cli.ContainerStop(ctx, containerId, dockercontainer.StopOptions{}) if err := cli.ContainerStop(ctx, containerId, dockercontainer.StopOptions{}); err != nil {
if err != nil {
return nil, errors.Wrap(err, "stop container error") return nil, errors.Wrap(err, "stop container error")
} }
// 3. rename the current container // 3. rename the current container
log.Debug().Str("container_id", containerId).Msg("starting to rename the container") log.Debug().Str("container_id", containerId).Msg("starting to rename the container")
err = cli.ContainerRename(ctx, containerId, container.Name+"-old") if err := cli.ContainerRename(ctx, containerId, container.Name+"-old"); err != nil {
if err != nil {
return nil, errors.Wrap(err, "rename container error") return nil, errors.Wrap(err, "rename container error")
} }
@ -94,8 +90,7 @@ func (c *ContainerService) Recreate(ctx context.Context, endpoint *portainer.End
// 4. disconnect all networks from the current container // 4. disconnect all networks from the current container
for name, network := range container.NetworkSettings.Networks { for name, network := range container.NetworkSettings.Networks {
// This allows new container to use the same IP address if specified // This allows new container to use the same IP address if specified
err = cli.NetworkDisconnect(ctx, network.NetworkID, containerId, true) if err := cli.NetworkDisconnect(ctx, network.NetworkID, containerId, true); err != nil {
if err != nil {
return nil, errors.Wrap(err, "disconnect network from old container error") return nil, errors.Wrap(err, "disconnect network from old container error")
} }
@ -113,9 +108,11 @@ func (c *ContainerService) Recreate(ctx context.Context, endpoint *portainer.End
c.sr.push(func() { c.sr.push(func() {
log.Debug().Str("container_id", containerId).Str("container", container.Name).Msg("restoring the container") log.Debug().Str("container_id", containerId).Str("container", container.Name).Msg("restoring the container")
cli.ContainerRename(ctx, containerId, container.Name) cli.ContainerRename(ctx, containerId, container.Name)
for _, network := range container.NetworkSettings.Networks { for _, network := range container.NetworkSettings.Networks {
cli.NetworkConnect(ctx, network.NetworkID, containerId, network) cli.NetworkConnect(ctx, network.NetworkID, containerId, network)
} }
cli.ContainerStart(ctx, containerId, dockercontainer.StartOptions{}) cli.ContainerStart(ctx, containerId, dockercontainer.StartOptions{})
}) })
@ -147,22 +144,19 @@ func (c *ContainerService) Recreate(ctx context.Context, endpoint *portainer.End
log.Debug().Str("container_id", newContainerId).Msg("connecting networks to container") log.Debug().Str("container_id", newContainerId).Msg("connecting networks to container")
networks := container.NetworkSettings.Networks networks := container.NetworkSettings.Networks
for key, network := range networks { for key, network := range networks {
_, ok := networkWithCreation.EndpointsConfig[key] if _, ok := networkWithCreation.EndpointsConfig[key]; ok {
if ok {
// skip the network that is used during container creation // skip the network that is used during container creation
continue continue
} }
err = cli.NetworkConnect(ctx, network.NetworkID, newContainerId, network) if err := cli.NetworkConnect(ctx, network.NetworkID, newContainerId, network); err != nil {
if err != nil {
return nil, errors.Wrap(err, "connect container network error") return nil, errors.Wrap(err, "connect container network error")
} }
} }
// 8. start the new container // 8. start the new container
log.Debug().Str("container_id", newContainerId).Msg("starting the new container") log.Debug().Str("container_id", newContainerId).Msg("starting the new container")
err = cli.ContainerStart(ctx, newContainerId, dockercontainer.StartOptions{}) if err := cli.ContainerStart(ctx, newContainerId, dockercontainer.StartOptions{}); err != nil {
if err != nil {
return nil, errors.Wrap(err, "start container error") return nil, errors.Wrap(err, "start container error")
} }

View File

@ -135,7 +135,7 @@ func (handler *Handler) inspectStatus(tx dataservices.DataStoreTx, r *http.Reque
// Take an initial snapshot // Take an initial snapshot
if endpoint.LastCheckInDate == 0 { if endpoint.LastCheckInDate == 0 {
handler.ReverseTunnelService.Open(endpoint) _ = handler.ReverseTunnelService.Open(endpoint)
} }
agentPlatform, agentPlatformErr := parseAgentPlatform(r) agentPlatform, agentPlatformErr := parseAgentPlatform(r)

View File

@ -9,10 +9,12 @@ import (
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/client" "github.com/portainer/portainer/api/http/client"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/api/pendingactions/handlers" "github.com/portainer/portainer/api/pendingactions/handlers"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response" "github.com/portainer/portainer/pkg/libhttp/response"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@ -80,8 +82,7 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
} }
var payload endpointUpdatePayload var payload endpointUpdatePayload
err = request.DecodeAndValidateJSONPayload(r, &payload) if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
if err != nil {
return httperror.BadRequest("Invalid request payload", err) return httperror.BadRequest("Invalid request payload", err)
} }
@ -106,7 +107,6 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
} }
endpoint.Name = name endpoint.Name = name
} }
if payload.URL != nil && *payload.URL != endpoint.URL { if payload.URL != nil && *payload.URL != endpoint.URL {
@ -136,8 +136,7 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
} }
if payload.TagIDs != nil { if payload.TagIDs != nil {
err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
tagsChanged, err := updateEnvironmentTags(tx, payload.TagIDs, endpoint.TagIDs, endpoint.ID) tagsChanged, err := updateEnvironmentTags(tx, payload.TagIDs, endpoint.TagIDs, endpoint.ID)
if err != nil { if err != nil {
return err return err
@ -147,10 +146,8 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
updateRelations = updateRelations || tagsChanged updateRelations = updateRelations || tagsChanged
return nil return nil
}) }); err != nil {
return httperror.InternalServerError("Unable to update environment tags", err)
if err != nil {
httperror.InternalServerError("Unable to update environment tags", err)
} }
} }
@ -191,17 +188,18 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
if payload.AzureApplicationID != nil { if payload.AzureApplicationID != nil {
credentials.ApplicationID = *payload.AzureApplicationID credentials.ApplicationID = *payload.AzureApplicationID
} }
if payload.AzureTenantID != nil { if payload.AzureTenantID != nil {
credentials.TenantID = *payload.AzureTenantID credentials.TenantID = *payload.AzureTenantID
} }
if payload.AzureAuthenticationKey != nil { if payload.AzureAuthenticationKey != nil {
credentials.AuthenticationKey = *payload.AzureAuthenticationKey credentials.AuthenticationKey = *payload.AzureAuthenticationKey
} }
httpClient := client.NewHTTPClient() httpClient := client.NewHTTPClient()
_, authErr := httpClient.ExecuteAzureAuthenticationRequest(&credentials) if _, err := httpClient.ExecuteAzureAuthenticationRequest(&credentials); err != nil {
if authErr != nil { return httperror.InternalServerError("Unable to authenticate against Azure", err)
return httperror.InternalServerError("Unable to authenticate against Azure", authErr)
} }
endpoint.AzureCredentials = credentials endpoint.AzureCredentials = credentials
} }
@ -236,20 +234,19 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
handler.FileService.DeleteTLSFile(folder, portainer.TLSFileKey) handler.FileService.DeleteTLSFile(folder, portainer.TLSFileKey)
} }
} }
} else { } else {
endpoint.TLSConfig.TLS = false endpoint.TLSConfig.TLS = false
endpoint.TLSConfig.TLSSkipVerify = false endpoint.TLSConfig.TLSSkipVerify = false
endpoint.TLSConfig.TLSCACertPath = "" endpoint.TLSConfig.TLSCACertPath = ""
endpoint.TLSConfig.TLSCertPath = "" endpoint.TLSConfig.TLSCertPath = ""
endpoint.TLSConfig.TLSKeyPath = "" endpoint.TLSConfig.TLSKeyPath = ""
err = handler.FileService.DeleteTLSFiles(folder)
if err != nil { if err := handler.FileService.DeleteTLSFiles(folder); err != nil {
return httperror.InternalServerError("Unable to remove TLS files from disk", err) return httperror.InternalServerError("Unable to remove TLS files from disk", err)
} }
} }
if endpoint.Type == portainer.AgentOnKubernetesEnvironment || endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment { if !endpointutils.IsLocalEndpoint(endpoint) && endpointutils.IsKubernetesEndpoint(endpoint) {
endpoint.TLSConfig.TLS = true endpoint.TLSConfig.TLS = true
endpoint.TLSConfig.TLSSkipVerify = true endpoint.TLSConfig.TLSSkipVerify = true
} }
@ -257,39 +254,32 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
if updateEndpointProxy { if updateEndpointProxy {
handler.ProxyManager.DeleteEndpointProxy(endpoint.ID) handler.ProxyManager.DeleteEndpointProxy(endpoint.ID)
_, err = handler.ProxyManager.CreateAndRegisterEndpointProxy(endpoint)
if err != nil { if _, err := handler.ProxyManager.CreateAndRegisterEndpointProxy(endpoint); err != nil {
return httperror.InternalServerError("Unable to register HTTP proxy for the environment", err) return httperror.InternalServerError("Unable to register HTTP proxy for the environment", err)
} }
} }
if updateAuthorizations { if updateAuthorizations && endpointutils.IsKubernetesEndpoint(endpoint) {
if endpoint.Type == portainer.KubernetesLocalEnvironment || endpoint.Type == portainer.AgentOnKubernetesEnvironment || endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment { if err := handler.AuthorizationService.CleanNAPWithOverridePolicies(handler.DataStore, endpoint, nil); err != nil {
err = handler.AuthorizationService.CleanNAPWithOverridePolicies(handler.DataStore, endpoint, nil)
if err != nil {
handler.PendingActionsService.Create(handlers.NewCleanNAPWithOverridePolicies(endpoint.ID, nil)) handler.PendingActionsService.Create(handlers.NewCleanNAPWithOverridePolicies(endpoint.ID, nil))
log.Warn().Err(err).Msgf("Unable to clean NAP with override policies for endpoint (%d). Will try to update when endpoint is online.", endpoint.ID) log.Warn().Err(err).Msgf("Unable to clean NAP with override policies for endpoint (%d). Will try to update when endpoint is online.", endpoint.ID)
} }
} }
}
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint) if err := handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint); err != nil {
if err != nil {
return httperror.InternalServerError("Unable to persist environment changes inside the database", err) return httperror.InternalServerError("Unable to persist environment changes inside the database", err)
} }
if updateRelations { if updateRelations {
err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.updateEdgeRelations(tx, endpoint) return handler.updateEdgeRelations(tx, endpoint)
}) }); err != nil {
if err != nil {
return httperror.InternalServerError("Unable to update environment relations", err) return httperror.InternalServerError("Unable to update environment relations", err)
} }
} }
err = handler.SnapshotService.FillSnapshotData(endpoint) if err := handler.SnapshotService.FillSnapshotData(endpoint); err != nil {
if err != nil {
return httperror.InternalServerError("Unable to add snapshot data", err) return httperror.InternalServerError("Unable to add snapshot data", err)
} }
@ -297,7 +287,6 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
} }
func shouldReloadTLSConfiguration(endpoint *portainer.Endpoint, payload *endpointUpdatePayload) bool { func shouldReloadTLSConfiguration(endpoint *portainer.Endpoint, payload *endpointUpdatePayload) bool {
// If we change anything in the tls config then we need to reload the proxy // If we change anything in the tls config then we need to reload the proxy
if payload.TLS != nil && endpoint.TLSConfig.TLS != *payload.TLS { if payload.TLS != nil && endpoint.TLSConfig.TLS != *payload.TLS {
return true return true
@ -318,5 +307,6 @@ func shouldReloadTLSConfiguration(endpoint *portainer.Endpoint, payload *endpoin
if payload.TLSSkipClientVerify != nil && !*payload.TLSSkipClientVerify { if payload.TLSSkipClientVerify != nil && !*payload.TLSSkipClientVerify {
return true return true
} }
return false return false
} }

View File

@ -95,8 +95,7 @@ func GetLatestVersion() string {
TagName string `json:"tag_name"` TagName string `json:"tag_name"`
} }
err = json.Unmarshal(motd, &data) if err := json.Unmarshal(motd, &data); err != nil {
if err != nil {
log.Debug().Err(err).Msg("couldn't parse latest Portainer version") log.Debug().Err(err).Msg("couldn't parse latest Portainer version")
return "" return ""

View File

@ -11,9 +11,9 @@ import (
"github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/dataservices"
httperrors "github.com/portainer/portainer/api/http/errors" httperrors "github.com/portainer/portainer/api/http/errors"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/rs/zerolog/log"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log"
) )
type ( type (
@ -376,7 +376,7 @@ func (bouncer *RequestBouncer) apiKeyLookup(r *http.Request) (*portainer.TokenDa
if now := time.Now().UTC().Unix(); now-apiKey.LastUsed > 60 { // [seconds] if now := time.Now().UTC().Unix(); now-apiKey.LastUsed > 60 { // [seconds]
// update the last used time of the key // update the last used time of the key
apiKey.LastUsed = now apiKey.LastUsed = now
bouncer.apiKeyService.UpdateAPIKey(&apiKey) _ = bouncer.apiKeyService.UpdateAPIKey(&apiKey)
} }
return tokenData, nil return tokenData, nil

View File

@ -71,7 +71,7 @@ func NewBackgroundSnapshotter(dataStore dataservices.DataStore, tunnelService po
s, err := tx.Snapshot().Read(e.ID) s, err := tx.Snapshot().Read(e.ID)
if dataservices.IsErrObjectNotFound(err) || if dataservices.IsErrObjectNotFound(err) ||
(err == nil && s.Docker == nil && s.Kubernetes == nil) { (err == nil && s.Docker == nil && s.Kubernetes == nil) {
tunnelService.Open(&e) _ = tunnelService.Open(&e)
} }
} }

View File

@ -56,7 +56,7 @@ func writeErrorResponse(rw http.ResponseWriter, err *HandlerError) {
enc.SetSortMapKeys(false) enc.SetSortMapKeys(false)
enc.SetAppendNewline(false) enc.SetAppendNewline(false)
enc.Encode(&errorResponse{Message: err.Message, Details: capitalize(err.Err.Error())}) _ = enc.Encode(&errorResponse{Message: err.Message, Details: capitalize(err.Err.Error())})
} }
// WriteError is a convenience function that creates a new HandlerError before calling writeErrorResponse. // WriteError is a convenience function that creates a new HandlerError before calling writeErrorResponse.