fix(errors): improve error handling EE-4430 (#11987)
parent
dc62604ed8
commit
4adce14485
|
@ -6,6 +6,8 @@ import (
|
|||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// 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 false
|
||||
}),
|
||||
)
|
||||
|
@ -166,11 +169,13 @@ func (service *Service) EndpointsByTeamID(teamID portainer.TeamID) ([]portainer.
|
|||
func (service *Service) GetNextIdentifier() 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()
|
||||
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
log.Error().Err(err).Str("bucket", BucketName).Msg("could not get the next identifier")
|
||||
}
|
||||
|
||||
return identifier
|
||||
}
|
||||
|
|
|
@ -32,8 +32,7 @@ func (service *Service) RegisterUpdateStackFunction(
|
|||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
if err := connection.SetServiceName(BucketName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -65,8 +64,7 @@ func (service *Service) EndpointRelation(endpointID portainer.EndpointID) (*port
|
|||
var endpointRelation portainer.EndpointRelation
|
||||
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &endpointRelation)
|
||||
if err != nil {
|
||||
if err := service.connection.GetObject(BucketName, identifier, &endpointRelation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -161,19 +159,24 @@ func (service *Service) updateEdgeStacksAfterRelationChange(previousRelationStat
|
|||
// list how many time this stack is referenced in all relations
|
||||
// in order to update the stack deployments count
|
||||
for refStackId, refStackEnabled := range stacksToUpdate {
|
||||
if refStackEnabled {
|
||||
numDeployments := 0
|
||||
for _, r := range relations {
|
||||
for sId, enabled := range r.EdgeStacks {
|
||||
if enabled && sId == refStackId {
|
||||
numDeployments += 1
|
||||
}
|
||||
if !refStackEnabled {
|
||||
continue
|
||||
}
|
||||
|
||||
numDeployments := 0
|
||||
|
||||
for _, r := range relations {
|
||||
for sId, enabled := range r.EdgeStacks {
|
||||
if enabled && sId == refStackId {
|
||||
numDeployments += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
service.updateStackFn(refStackId, func(edgeStack *portainer.EdgeStack) {
|
||||
edgeStack.NumDeployments = numDeployments
|
||||
})
|
||||
if err := service.updateStackFn(refStackId, func(edgeStack *portainer.EdgeStack) {
|
||||
edgeStack.NumDeployments = numDeployments
|
||||
}); err != nil {
|
||||
log.Error().Err(err).Msg("could not update the number of deployments")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,8 +33,7 @@ func (service ServiceTx) EndpointRelation(endpointID portainer.EndpointID) (*por
|
|||
var endpointRelation portainer.EndpointRelation
|
||||
identifier := service.service.connection.ConvertToKey(int(endpointID))
|
||||
|
||||
err := service.tx.GetObject(BucketName, identifier, &endpointRelation)
|
||||
if err != nil {
|
||||
if err := service.tx.GetObject(BucketName, identifier, &endpointRelation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -129,19 +128,23 @@ func (service ServiceTx) updateEdgeStacksAfterRelationChange(previousRelationSta
|
|||
// list how many time this stack is referenced in all relations
|
||||
// in order to update the stack deployments count
|
||||
for refStackId, refStackEnabled := range stacksToUpdate {
|
||||
if refStackEnabled {
|
||||
numDeployments := 0
|
||||
for _, r := range relations {
|
||||
for sId, enabled := range r.EdgeStacks {
|
||||
if enabled && sId == refStackId {
|
||||
numDeployments += 1
|
||||
}
|
||||
if !refStackEnabled {
|
||||
continue
|
||||
}
|
||||
|
||||
numDeployments := 0
|
||||
for _, r := range relations {
|
||||
for sId, enabled := range r.EdgeStacks {
|
||||
if enabled && sId == refStackId {
|
||||
numDeployments += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
service.service.updateStackFnTx(service.tx, refStackId, func(edgeStack *portainer.EdgeStack) {
|
||||
edgeStack.NumDeployments = numDeployments
|
||||
})
|
||||
if err := service.service.updateStackFnTx(service.tx, refStackId, func(edgeStack *portainer.EdgeStack) {
|
||||
edgeStack.NumDeployments = numDeployments
|
||||
}); err != nil {
|
||||
log.Error().Err(err).Msg("could not update the number of deployments")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,14 @@ import (
|
|||
"context"
|
||||
"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"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dockerclient "github.com/portainer/portainer/api/docker/client"
|
||||
"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/rs/zerolog/log"
|
||||
)
|
||||
|
@ -54,8 +54,7 @@ func (c *ContainerService) Recreate(ctx context.Context, endpoint *portainer.End
|
|||
}
|
||||
|
||||
if imageTag != "" {
|
||||
err = img.WithTag(imageTag)
|
||||
if err != nil {
|
||||
if err := img.WithTag(imageTag); err != nil {
|
||||
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
|
||||
if forcePullImage {
|
||||
puller := images.NewPuller(cli, images.NewRegistryClient(c.dataStore), c.dataStore)
|
||||
err = puller.Pull(ctx, img)
|
||||
if err != nil {
|
||||
if err := puller.Pull(ctx, img); err != nil {
|
||||
return nil, errors.Wrapf(err, "pull image error %s", img.FullName())
|
||||
}
|
||||
}
|
||||
|
||||
// 2. stop the current container
|
||||
log.Debug().Str("container_id", containerId).Msg("starting to stop the container")
|
||||
err = cli.ContainerStop(ctx, containerId, dockercontainer.StopOptions{})
|
||||
if err != nil {
|
||||
if err := cli.ContainerStop(ctx, containerId, dockercontainer.StopOptions{}); err != nil {
|
||||
return nil, errors.Wrap(err, "stop container error")
|
||||
}
|
||||
|
||||
// 3. rename the current container
|
||||
log.Debug().Str("container_id", containerId).Msg("starting to rename the container")
|
||||
err = cli.ContainerRename(ctx, containerId, container.Name+"-old")
|
||||
if err != nil {
|
||||
if err := cli.ContainerRename(ctx, containerId, container.Name+"-old"); err != nil {
|
||||
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
|
||||
for name, network := range container.NetworkSettings.Networks {
|
||||
// This allows new container to use the same IP address if specified
|
||||
err = cli.NetworkDisconnect(ctx, network.NetworkID, containerId, true)
|
||||
if err != nil {
|
||||
if err := cli.NetworkDisconnect(ctx, network.NetworkID, containerId, true); err != nil {
|
||||
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() {
|
||||
log.Debug().Str("container_id", containerId).Str("container", container.Name).Msg("restoring the container")
|
||||
cli.ContainerRename(ctx, containerId, container.Name)
|
||||
|
||||
for _, network := range container.NetworkSettings.Networks {
|
||||
cli.NetworkConnect(ctx, network.NetworkID, containerId, network)
|
||||
}
|
||||
|
||||
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")
|
||||
networks := container.NetworkSettings.Networks
|
||||
for key, network := range networks {
|
||||
_, ok := networkWithCreation.EndpointsConfig[key]
|
||||
if ok {
|
||||
if _, ok := networkWithCreation.EndpointsConfig[key]; ok {
|
||||
// skip the network that is used during container creation
|
||||
continue
|
||||
}
|
||||
|
||||
err = cli.NetworkConnect(ctx, network.NetworkID, newContainerId, network)
|
||||
if err != nil {
|
||||
if err := cli.NetworkConnect(ctx, network.NetworkID, newContainerId, network); err != nil {
|
||||
return nil, errors.Wrap(err, "connect container network error")
|
||||
}
|
||||
}
|
||||
|
||||
// 8. start the new container
|
||||
log.Debug().Str("container_id", newContainerId).Msg("starting the new container")
|
||||
err = cli.ContainerStart(ctx, newContainerId, dockercontainer.StartOptions{})
|
||||
if err != nil {
|
||||
if err := cli.ContainerStart(ctx, newContainerId, dockercontainer.StartOptions{}); err != nil {
|
||||
return nil, errors.Wrap(err, "start container error")
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ func (handler *Handler) inspectStatus(tx dataservices.DataStoreTx, r *http.Reque
|
|||
|
||||
// Take an initial snapshot
|
||||
if endpoint.LastCheckInDate == 0 {
|
||||
handler.ReverseTunnelService.Open(endpoint)
|
||||
_ = handler.ReverseTunnelService.Open(endpoint)
|
||||
}
|
||||
|
||||
agentPlatform, agentPlatformErr := parseAgentPlatform(r)
|
||||
|
|
|
@ -9,10 +9,12 @@ import (
|
|||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/http/client"
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
"github.com/portainer/portainer/api/pendingactions/handlers"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
|
@ -80,8 +82,7 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
}
|
||||
|
||||
var payload endpointUpdatePayload
|
||||
err = request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
|
@ -106,7 +107,6 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
}
|
||||
|
||||
endpoint.Name = name
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -147,10 +146,8 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
updateRelations = updateRelations || tagsChanged
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
httperror.InternalServerError("Unable to update environment tags", err)
|
||||
}); err != nil {
|
||||
return 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 {
|
||||
credentials.ApplicationID = *payload.AzureApplicationID
|
||||
}
|
||||
|
||||
if payload.AzureTenantID != nil {
|
||||
credentials.TenantID = *payload.AzureTenantID
|
||||
}
|
||||
|
||||
if payload.AzureAuthenticationKey != nil {
|
||||
credentials.AuthenticationKey = *payload.AzureAuthenticationKey
|
||||
}
|
||||
|
||||
httpClient := client.NewHTTPClient()
|
||||
_, authErr := httpClient.ExecuteAzureAuthenticationRequest(&credentials)
|
||||
if authErr != nil {
|
||||
return httperror.InternalServerError("Unable to authenticate against Azure", authErr)
|
||||
if _, err := httpClient.ExecuteAzureAuthenticationRequest(&credentials); err != nil {
|
||||
return httperror.InternalServerError("Unable to authenticate against Azure", err)
|
||||
}
|
||||
endpoint.AzureCredentials = credentials
|
||||
}
|
||||
|
@ -236,20 +234,19 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
handler.FileService.DeleteTLSFile(folder, portainer.TLSFileKey)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
endpoint.TLSConfig.TLS = false
|
||||
endpoint.TLSConfig.TLSSkipVerify = false
|
||||
endpoint.TLSConfig.TLSCACertPath = ""
|
||||
endpoint.TLSConfig.TLSCertPath = ""
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if endpoint.Type == portainer.AgentOnKubernetesEnvironment || endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment {
|
||||
if !endpointutils.IsLocalEndpoint(endpoint) && endpointutils.IsKubernetesEndpoint(endpoint) {
|
||||
endpoint.TLSConfig.TLS = true
|
||||
endpoint.TLSConfig.TLSSkipVerify = true
|
||||
}
|
||||
|
@ -257,39 +254,32 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
|
|||
|
||||
if updateEndpointProxy {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if updateAuthorizations {
|
||||
if endpoint.Type == portainer.KubernetesLocalEnvironment || endpoint.Type == portainer.AgentOnKubernetesEnvironment || endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment {
|
||||
err = handler.AuthorizationService.CleanNAPWithOverridePolicies(handler.DataStore, endpoint, nil)
|
||||
if err != 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)
|
||||
}
|
||||
if updateAuthorizations && endpointutils.IsKubernetesEndpoint(endpoint) {
|
||||
if err := handler.AuthorizationService.CleanNAPWithOverridePolicies(handler.DataStore, endpoint, nil); err != 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)
|
||||
}
|
||||
}
|
||||
|
||||
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint); err != nil {
|
||||
return httperror.InternalServerError("Unable to persist environment changes inside the database", err)
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
return httperror.InternalServerError("Unable to update environment relations", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = handler.SnapshotService.FillSnapshotData(endpoint)
|
||||
if err != nil {
|
||||
if err := handler.SnapshotService.FillSnapshotData(endpoint); err != nil {
|
||||
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 {
|
||||
|
||||
// If we change anything in the tls config then we need to reload the proxy
|
||||
if payload.TLS != nil && endpoint.TLSConfig.TLS != *payload.TLS {
|
||||
return true
|
||||
|
@ -318,5 +307,6 @@ func shouldReloadTLSConfiguration(endpoint *portainer.Endpoint, payload *endpoin
|
|||
if payload.TLSSkipClientVerify != nil && !*payload.TLSSkipClientVerify {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -95,8 +95,7 @@ func GetLatestVersion() string {
|
|||
TagName string `json:"tag_name"`
|
||||
}
|
||||
|
||||
err = json.Unmarshal(motd, &data)
|
||||
if err != nil {
|
||||
if err := json.Unmarshal(motd, &data); err != nil {
|
||||
log.Debug().Err(err).Msg("couldn't parse latest Portainer version")
|
||||
|
||||
return ""
|
||||
|
|
|
@ -11,9 +11,9 @@ import (
|
|||
"github.com/portainer/portainer/api/dataservices"
|
||||
httperrors "github.com/portainer/portainer/api/http/errors"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
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]
|
||||
// update the last used time of the key
|
||||
apiKey.LastUsed = now
|
||||
bouncer.apiKeyService.UpdateAPIKey(&apiKey)
|
||||
_ = bouncer.apiKeyService.UpdateAPIKey(&apiKey)
|
||||
}
|
||||
|
||||
return tokenData, nil
|
||||
|
|
|
@ -71,7 +71,7 @@ func NewBackgroundSnapshotter(dataStore dataservices.DataStore, tunnelService po
|
|||
s, err := tx.Snapshot().Read(e.ID)
|
||||
if dataservices.IsErrObjectNotFound(err) ||
|
||||
(err == nil && s.Docker == nil && s.Kubernetes == nil) {
|
||||
tunnelService.Open(&e)
|
||||
_ = tunnelService.Open(&e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ func writeErrorResponse(rw http.ResponseWriter, err *HandlerError) {
|
|||
enc.SetSortMapKeys(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.
|
||||
|
|
Loading…
Reference in New Issue