From 55cda8c78eef13e49ed5cc1676cce73d921e77c5 Mon Sep 17 00:00:00 2001 From: Oscar Zhou <100548325+oscarzhou-portainer@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:40:39 +1300 Subject: [PATCH] fix(edge): backport agent id/name into edge api response [BE-10988] (#36) --- .../edgestacks/edgestack_status_update.go | 20 ++++++++---------- .../endpointedge/endpointedge_job_logs.go | 21 +++++++++---------- .../endpointedge_stack_inspect.go | 21 +++++++++---------- .../endpointedge_status_inspect.go | 12 ++++++----- .../EnvironmentsDatatable/columns.tsx | 2 +- 5 files changed, 37 insertions(+), 39 deletions(-) diff --git a/api/http/handler/edgestacks/edgestack_status_update.go b/api/http/handler/edgestacks/edgestack_status_update.go index 837a292a1..bcab662ef 100644 --- a/api/http/handler/edgestacks/edgestack_status_update.go +++ b/api/http/handler/edgestacks/edgestack_status_update.go @@ -2,6 +2,7 @@ package edgestacks import ( "errors" + "fmt" "net/http" "time" @@ -63,9 +64,8 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req } var payload updateStatusPayload - err = request.DecodeAndValidateJSONPayload(r, &payload) - if err != nil { - return httperror.BadRequest("Invalid request payload", err) + if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil { + return httperror.BadRequest("Invalid request payload", fmt.Errorf("edge polling error: %w. Environment ID: %d", err, payload.EndpointID)) } var stack *portainer.EdgeStack @@ -98,17 +98,16 @@ func (handler *Handler) updateEdgeStackStatus(tx dataservices.DataStoreTx, r *ht return nil, nil } - return nil, err + return nil, fmt.Errorf("unable to retrieve Edge stack from the database: %w. Environment ID: %d", err, payload.EndpointID) } endpoint, err := tx.Endpoint().Endpoint(payload.EndpointID) if err != nil { - return nil, handler.handlerDBErr(err, "Unable to find an environment with the specified identifier inside the database") + return nil, handler.handlerDBErr(fmt.Errorf("unable to find the environment from the database: %w. Environment ID: %d", err, payload.EndpointID), "unable to find the environment") } - err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint) - if err != nil { - return nil, httperror.Forbidden("Permission denied to access environment", err) + if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil { + return nil, httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment name: %s", err, endpoint.Name)) } status := *payload.Status @@ -126,9 +125,8 @@ func (handler *Handler) updateEdgeStackStatus(tx dataservices.DataStoreTx, r *ht updateEnvStatus(payload.EndpointID, stack, deploymentStatus) - err = tx.EdgeStack().UpdateEdgeStack(stackID, stack) - if err != nil { - return nil, handler.handlerDBErr(err, "Unable to persist the stack changes inside the database") + if err := tx.EdgeStack().UpdateEdgeStack(stackID, stack); err != nil { + return nil, handler.handlerDBErr(fmt.Errorf("unable to update Edge stack to the database: %w. Environment name: %s", err, endpoint.Name), "unable to update Edge stack") } return stack, nil diff --git a/api/http/handler/endpointedge/endpointedge_job_logs.go b/api/http/handler/endpointedge/endpointedge_job_logs.go index 90dba680a..2d8b1958f 100644 --- a/api/http/handler/endpointedge/endpointedge_job_logs.go +++ b/api/http/handler/endpointedge/endpointedge_job_logs.go @@ -2,6 +2,7 @@ package endpointedge import ( "errors" + "fmt" "net/http" "strconv" @@ -39,32 +40,30 @@ func (handler *Handler) endpointEdgeJobsLogs(w http.ResponseWriter, r *http.Requ return httperror.BadRequest("Unable to find an environment on request context", err) } - err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint) - if err != nil { - return httperror.Forbidden("Permission denied to access environment", err) + if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil { + return httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment name: %s", err, endpoint.Name)) } edgeJobID, err := request.RetrieveNumericRouteVariableValue(r, "jobID") if err != nil { - return httperror.BadRequest("Invalid edge job identifier route variable", err) + return httperror.BadRequest("Invalid edge job identifier route variable", fmt.Errorf("invalid Edge job route variable: %w. Environment name: %s", err, endpoint.Name)) } var payload logsPayload - err = request.DecodeAndValidateJSONPayload(r, &payload) - if err != nil { - return httperror.BadRequest("Invalid request payload", err) + if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil { + return httperror.BadRequest("Invalid request payload", fmt.Errorf("invalid Edge job request payload: %w. Environment name: %s", err, endpoint.Name)) } - err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { + if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error { return handler.getEdgeJobLobs(tx, endpoint.ID, portainer.EdgeJobID(edgeJobID), payload) - }) - if err != nil { + }); err != nil { var httpErr *httperror.HandlerError if errors.As(err, &httpErr) { + httpErr.Err = fmt.Errorf("edge polling error: %w. Environment name: %s", httpErr.Err, endpoint.Name) return httpErr } - return httperror.InternalServerError("Unexpected error", err) + return httperror.InternalServerError("Unexpected error", fmt.Errorf("edge polling error: %w. Environment name: %s", err, endpoint.Name)) } return response.JSON(w, nil) diff --git a/api/http/handler/endpointedge/endpointedge_stack_inspect.go b/api/http/handler/endpointedge/endpointedge_stack_inspect.go index fc9f208b0..ef146c7e6 100644 --- a/api/http/handler/endpointedge/endpointedge_stack_inspect.go +++ b/api/http/handler/endpointedge/endpointedge_stack_inspect.go @@ -1,7 +1,7 @@ package endpointedge import ( - "errors" + "fmt" "net/http" portainer "github.com/portainer/portainer/api" @@ -33,27 +33,26 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http. return httperror.BadRequest("Unable to find an environment on request context", err) } - err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint) - if err != nil { - return httperror.Forbidden("Permission denied to access environment", err) + if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil { + return httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment name: %s", err, endpoint.Name)) } edgeStackID, err := request.RetrieveNumericRouteVariableValue(r, "stackId") if err != nil { - return httperror.BadRequest("Invalid edge stack identifier route variable", err) + return httperror.BadRequest("Invalid edge stack identifier route variable", fmt.Errorf("invalid Edge stack route variable: %w. Environment name: %s", err, endpoint.Name)) } edgeStack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(edgeStackID)) if handler.DataStore.IsErrObjectNotFound(err) { - return httperror.NotFound("Unable to find an edge stack with the specified identifier inside the database", err) + return httperror.NotFound("Unable to find an edge stack with the specified identifier inside the database", fmt.Errorf("unable to find the Edge stack from database: %w. Environment name: %s", err, endpoint.Name)) } else if err != nil { - return httperror.InternalServerError("Unable to find an edge stack with the specified identifier inside the database", err) + return httperror.InternalServerError("Unable to find an edge stack with the specified identifier inside the database", fmt.Errorf("failed to find the Edge stack from database: %w. Environment name: %s", err, endpoint.Name)) } fileName := edgeStack.EntryPoint if endpointutils.IsDockerEndpoint(endpoint) { if fileName == "" { - return httperror.BadRequest("Docker is not supported by this stack", errors.New("Docker is not supported by this stack")) + return httperror.BadRequest("Docker is not supported by this stack", fmt.Errorf("no filename is provided for the Docker endpoint. Environment name: %s", endpoint.Name)) } } @@ -66,18 +65,18 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http. fileName = edgeStack.ManifestPath if fileName == "" { - return httperror.BadRequest("Kubernetes is not supported by this stack", errors.New("Kubernetes is not supported by this stack")) + return httperror.BadRequest("Kubernetes is not supported by this stack", fmt.Errorf("no filename is provided for the Kubernetes endpoint. Environment name: %s", endpoint.Name)) } } dirEntries, err := filesystem.LoadDir(edgeStack.ProjectPath) if err != nil { - return httperror.InternalServerError("Unable to load repository", err) + return httperror.InternalServerError("Unable to load repository", fmt.Errorf("failed to load project directory: %w. Environment name: %s", err, endpoint.Name)) } fileContent, err := filesystem.FilterDirForCompatibility(dirEntries, fileName, endpoint.Agent.Version) if err != nil { - return httperror.InternalServerError("File not found", err) + return httperror.InternalServerError("File not found", fmt.Errorf("unable to find file: %w. Environment name: %s", err, endpoint.Name)) } dirEntries = filesystem.FilterDirForEntryFile(dirEntries, fileName) diff --git a/api/http/handler/endpointedge/endpointedge_status_inspect.go b/api/http/handler/endpointedge/endpointedge_status_inspect.go index 87c02c8ad..44166c614 100644 --- a/api/http/handler/endpointedge/endpointedge_status_inspect.go +++ b/api/http/handler/endpointedge/endpointedge_status_inspect.go @@ -86,27 +86,27 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http if _, ok := handler.DataStore.Endpoint().Heartbeat(portainer.EndpointID(endpointID)); !ok { // EE-5190 - return httperror.Forbidden("Permission denied to access environment", errors.New("the device has not been trusted yet")) + return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("unable to retrieve endpoint heartbeat. Environment ID: %d", endpointID)) } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) if err != nil { // EE-5190 - return httperror.Forbidden("Permission denied to access environment", errors.New("the device has not been trusted yet")) + return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("unable to retrieve endpoint from database: %w. Environment ID: %d", err, endpointID)) } firstConn := endpoint.LastCheckInDate == 0 err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint) if err != nil { - return httperror.Forbidden("Permission denied to access environment", err) + return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("unauthorized Edge endpoint operation: %w. Environment name: %s", err, endpoint.Name)) } handler.DataStore.Endpoint().UpdateHeartbeat(endpoint.ID) err = handler.requestBouncer.TrustedEdgeEnvironmentAccess(handler.DataStore, endpoint) if err != nil { - return httperror.Forbidden("Permission denied to access environment", err) + return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("untrusted Edge environment access: %w. Environment name: %s", err, endpoint.Name)) } var statusResponse *endpointEdgeStatusInspectResponse @@ -117,10 +117,11 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http if err != nil { var httpErr *httperror.HandlerError if errors.As(err, &httpErr) { + httpErr.Err = fmt.Errorf("edge polling error: %w. Environment name: %s", httpErr.Err, endpoint.Name) return httpErr } - return httperror.InternalServerError("Unexpected error", err) + return httperror.InternalServerError("Unexpected error", fmt.Errorf("edge polling error: %w. Environment name: %s", err, endpoint.Name)) } return cacheResponse(w, endpoint.ID, *statusResponse) @@ -265,6 +266,7 @@ func cacheResponse(w http.ResponseWriter, endpointID portainer.EndpointID, statu httpErr := response.JSON(rr, statusResponse) if httpErr != nil { + httpErr.Err = fmt.Errorf("failed to cache response: %w. Environment ID: %d", httpErr.Err, endpointID) return httpErr } diff --git a/app/react/edge/edge-stacks/ItemView/EnvironmentsDatatable/columns.tsx b/app/react/edge/edge-stacks/ItemView/EnvironmentsDatatable/columns.tsx index 34a48c8a4..c7507752e 100644 --- a/app/react/edge/edge-stacks/ItemView/EnvironmentsDatatable/columns.tsx +++ b/app/react/edge/edge-stacks/ItemView/EnvironmentsDatatable/columns.tsx @@ -131,7 +131,7 @@ function ErrorCell({ getValue }: CellContext) {
{value}