From f12c3968f10a92834cb6e7e1457c76fce481b5ab Mon Sep 17 00:00:00 2001 From: Marcelo Rydel Date: Fri, 8 Apr 2022 11:27:38 -0300 Subject: [PATCH] feat(edge): edgeStacks and edgeJobs operations small refactors [EE-2744] (#6648) --- api/http/handler/edgejobs/edgejob_update.go | 15 +++++++++----- .../handler/edgestacks/edgestack_update.go | 15 +++----------- .../endpointedge/endpoint_edgejob_logs.go | 14 ++++--------- .../endpoint_edgestack_inspect.go | 12 +++-------- .../endpoint_edgestatus_inspect.go | 13 +++--------- api/http/handler/endpointedge/handler.go | 20 +++++++++++++------ api/http/handler/endpoints/endpoint_update.go | 6 +++--- api/http/server.go | 5 +---- api/internal/endpointutils/endpointutils.go | 11 ++++++++++ 9 files changed, 52 insertions(+), 59 deletions(-) diff --git a/api/http/handler/edgejobs/edgejob_update.go b/api/http/handler/edgejobs/edgejob_update.go index 3c734c36e..2e1f6106b 100644 --- a/api/http/handler/edgejobs/edgejob_update.go +++ b/api/http/handler/edgejobs/edgejob_update.go @@ -92,7 +92,7 @@ func (handler *Handler) updateEdgeSchedule(edgeJob *portainer.EdgeJob, payload * continue } - if meta, ok := edgeJob.Endpoints[endpointID]; ok { + if meta, exists := edgeJob.Endpoints[endpointID]; exists { endpointsMap[endpointID] = meta } else { endpointsMap[endpointID] = portainer.EdgeJobEndpointMeta{} @@ -103,13 +103,18 @@ func (handler *Handler) updateEdgeSchedule(edgeJob *portainer.EdgeJob, payload * } updateVersion := false - if payload.CronExpression != nil { + if payload.CronExpression != nil && *payload.CronExpression != edgeJob.CronExpression { edgeJob.CronExpression = *payload.CronExpression updateVersion = true } - if payload.FileContent != nil { - _, err := handler.FileService.StoreEdgeJobFileFromBytes(strconv.Itoa(int(edgeJob.ID)), []byte(*payload.FileContent)) + fileContent, err := handler.FileService.GetFileContent(edgeJob.ScriptPath, "") + if err != nil { + return err + } + + if payload.FileContent != nil && *payload.FileContent != string(fileContent) { + _, err := handler.FileService.StoreEdgeJobFileFromBytes(strconv.Itoa(int(edgeJob.ID)), fileContent) if err != nil { return err } @@ -117,7 +122,7 @@ func (handler *Handler) updateEdgeSchedule(edgeJob *portainer.EdgeJob, payload * updateVersion = true } - if payload.Recurring != nil { + if payload.Recurring != nil && *payload.Recurring != edgeJob.Recurring { edgeJob.Recurring = *payload.Recurring updateVersion = true } diff --git a/api/http/handler/edgestacks/edgestack_update.go b/api/http/handler/edgestacks/edgestack_update.go index 8a0efc070..0fc7f4ad7 100644 --- a/api/http/handler/edgestacks/edgestack_update.go +++ b/api/http/handler/edgestacks/edgestack_update.go @@ -2,6 +2,7 @@ package edgestacks import ( "errors" + "github.com/portainer/portainer/api/internal/endpointutils" "net/http" "strconv" @@ -80,8 +81,8 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request) return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge stack related environments from database", err} } - oldRelatedSet := EndpointSet(relatedEndpointIds) - newRelatedSet := EndpointSet(newRelated) + oldRelatedSet := endpointutils.EndpointSet(relatedEndpointIds) + newRelatedSet := endpointutils.EndpointSet(newRelated) endpointsToRemove := map[portainer.EndpointID]bool{} for endpointID := range oldRelatedSet { @@ -189,13 +190,3 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request) return response.JSON(w, stack) } - -func EndpointSet(endpointIDs []portainer.EndpointID) map[portainer.EndpointID]bool { - set := map[portainer.EndpointID]bool{} - - for _, endpointID := range endpointIDs { - set[endpointID] = true - } - - return set -} diff --git a/api/http/handler/endpointedge/endpoint_edgejob_logs.go b/api/http/handler/endpointedge/endpoint_edgejob_logs.go index a1d8c1091..74b120495 100644 --- a/api/http/handler/endpointedge/endpoint_edgejob_logs.go +++ b/api/http/handler/endpointedge/endpoint_edgejob_logs.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/http/middlewares" ) type logsPayload struct { @@ -31,16 +32,9 @@ func (payload *logsPayload) Validate(r *http.Request) error { // @failure 400 // @router /endpoints/{id}/edge/jobs/{jobID}/logs [post] func (handler *Handler) endpointEdgeJobsLogs(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id") + endpoint, err := middlewares.FetchEndpoint(r) if err != nil { - return &httperror.HandlerError{http.StatusBadRequest, "Invalid environment identifier route variable", err} - } - - endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if handler.DataStore.IsErrObjectNotFound(err) { - return &httperror.HandlerError{http.StatusNotFound, "Unable to find an environment with the specified identifier inside the database", err} - } else if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an environment with the specified identifier inside the database", err} + return httperror.BadRequest("Unable to find an environment on request context", err) } err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint) @@ -66,7 +60,7 @@ func (handler *Handler) endpointEdgeJobsLogs(w http.ResponseWriter, r *http.Requ return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an edge job with the specified identifier inside the database", err} } - err = handler.FileService.StoreEdgeJobTaskLogFileFromBytes(strconv.Itoa(edgeJobID), strconv.Itoa(endpointID), []byte(payload.FileContent)) + err = handler.FileService.StoreEdgeJobTaskLogFileFromBytes(strconv.Itoa(edgeJobID), strconv.Itoa(int(endpoint.ID)), []byte(payload.FileContent)) if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to save task log to the filesystem", err} } diff --git a/api/http/handler/endpointedge/endpoint_edgestack_inspect.go b/api/http/handler/endpointedge/endpoint_edgestack_inspect.go index 14eff89ac..ccb736ab8 100644 --- a/api/http/handler/endpointedge/endpoint_edgestack_inspect.go +++ b/api/http/handler/endpointedge/endpoint_edgestack_inspect.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/http/middlewares" "github.com/portainer/portainer/api/internal/endpointutils" ) @@ -29,16 +30,9 @@ type configResponse struct { // @failure 404 // @router /endpoints/{id}/edge/stacks/{stackId} [get] func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id") + endpoint, err := middlewares.FetchEndpoint(r) if err != nil { - return &httperror.HandlerError{http.StatusBadRequest, "Invalid environment identifier route variable", err} - } - - endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if handler.DataStore.IsErrObjectNotFound(err) { - return &httperror.HandlerError{http.StatusNotFound, "Unable to find an environment with the specified identifier inside the database", err} - } else if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an environment with the specified identifier inside the database", err} + return httperror.BadRequest("Unable to find an environment on request context", err) } err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint) diff --git a/api/http/handler/endpointedge/endpoint_edgestatus_inspect.go b/api/http/handler/endpointedge/endpoint_edgestatus_inspect.go index bf4dd7dce..7e4bada51 100644 --- a/api/http/handler/endpointedge/endpoint_edgestatus_inspect.go +++ b/api/http/handler/endpointedge/endpoint_edgestatus_inspect.go @@ -9,9 +9,9 @@ import ( "time" httperror "github.com/portainer/libhttp/error" - "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/http/middlewares" ) type stackStatusResponse struct { @@ -64,16 +64,9 @@ type endpointEdgeStatusInspectResponse struct { // @failure 500 "Server error" // @router /endpoints/{id}/edge/status [get] func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id") + endpoint, err := middlewares.FetchEndpoint(r) if err != nil { - return &httperror.HandlerError{http.StatusBadRequest, "Invalid environment identifier route variable", err} - } - - endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if handler.DataStore.IsErrObjectNotFound(err) { - return &httperror.HandlerError{http.StatusNotFound, "Unable to find an environment with the specified identifier inside the database", err} - } else if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an environment with the specified identifier inside the database", err} + return httperror.BadRequest("Unable to find an environment on request context", err) } err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint) diff --git a/api/http/handler/endpointedge/handler.go b/api/http/handler/endpointedge/handler.go index 3bee3d403..62d079ff2 100644 --- a/api/http/handler/endpointedge/handler.go +++ b/api/http/handler/endpointedge/handler.go @@ -3,6 +3,8 @@ package endpointedge import ( "net/http" + "github.com/portainer/portainer/api/http/middlewares" + httperror "github.com/portainer/libhttp/error" "github.com/gorilla/mux" @@ -21,17 +23,23 @@ type Handler struct { } // NewHandler creates a handler to manage environment(endpoint) operations. -func NewHandler(bouncer *security.RequestBouncer) *Handler { +func NewHandler(bouncer *security.RequestBouncer, dataStore dataservices.DataStore, fileService portainer.FileService, reverseTunnelService portainer.ReverseTunnelService) *Handler { h := &Handler{ - Router: mux.NewRouter(), - requestBouncer: bouncer, + Router: mux.NewRouter(), + requestBouncer: bouncer, + DataStore: dataStore, + FileService: fileService, + ReverseTunnelService: reverseTunnelService, } - h.Handle("/{id}/edge/status", + endpointRouter := h.PathPrefix("/{id}").Subrouter() + endpointRouter.Use(middlewares.WithEndpoint(dataStore.Endpoint(), "id")) + + endpointRouter.PathPrefix("/edge/status").Handler( bouncer.PublicAccess(httperror.LoggerHandler(h.endpointEdgeStatusInspect))).Methods(http.MethodGet) - h.Handle("/{id}/edge/stacks/{stackId}", + endpointRouter.PathPrefix("/edge/stacks/{stackId}").Handler( bouncer.PublicAccess(httperror.LoggerHandler(h.endpointEdgeStackInspect))).Methods(http.MethodGet) - h.Handle("/{id}/edge/jobs/{jobID}/logs", + endpointRouter.PathPrefix("/edge/jobs/{jobID}/logs").Handler( bouncer.PublicAccess(httperror.LoggerHandler(h.endpointEdgeJobsLogs))).Methods(http.MethodPost) return h } diff --git a/api/http/handler/endpoints/endpoint_update.go b/api/http/handler/endpoints/endpoint_update.go index b8364b4a2..2b896f7a0 100644 --- a/api/http/handler/endpoints/endpoint_update.go +++ b/api/http/handler/endpoints/endpoint_update.go @@ -302,14 +302,14 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) * return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge stacks from the database", err} } - edgeStackSet := map[portainer.EdgeStackID]bool{} + currentEdgeStackSet := map[portainer.EdgeStackID]bool{} endpointEdgeStacks := edge.EndpointRelatedEdgeStacks(endpoint, endpointGroup, edgeGroups, edgeStacks) for _, edgeStackID := range endpointEdgeStacks { - edgeStackSet[edgeStackID] = true + currentEdgeStackSet[edgeStackID] = true } - relation.EdgeStacks = edgeStackSet + relation.EdgeStacks = currentEdgeStackSet err = handler.DataStore.EndpointRelation().UpdateEndpointRelation(endpoint.ID, relation) if err != nil { diff --git a/api/http/server.go b/api/http/server.go index ad07cafc4..15690f2b7 100644 --- a/api/http/server.go +++ b/api/http/server.go @@ -160,10 +160,7 @@ func (server *Server) Start() error { endpointHandler.BindAddress = server.BindAddress endpointHandler.BindAddressHTTPS = server.BindAddressHTTPS - var endpointEdgeHandler = endpointedge.NewHandler(requestBouncer) - endpointEdgeHandler.DataStore = server.DataStore - endpointEdgeHandler.FileService = server.FileService - endpointEdgeHandler.ReverseTunnelService = server.ReverseTunnelService + var endpointEdgeHandler = endpointedge.NewHandler(requestBouncer, server.DataStore, server.FileService, server.ReverseTunnelService) var endpointGroupHandler = endpointgroups.NewHandler(requestBouncer) endpointGroupHandler.AuthorizationService = server.AuthorizationService diff --git a/api/internal/endpointutils/endpointutils.go b/api/internal/endpointutils/endpointutils.go index 128e61a86..356a600e6 100644 --- a/api/internal/endpointutils/endpointutils.go +++ b/api/internal/endpointutils/endpointutils.go @@ -58,3 +58,14 @@ func FilterByExcludeIDs(endpoints []portainer.Endpoint, excludeIds []portainer.E } return filteredEndpoints } + +// EndpointSet receives an environment(endpoint) array and returns a set +func EndpointSet(endpointIDs []portainer.EndpointID) map[portainer.EndpointID]bool { + set := map[portainer.EndpointID]bool{} + + for _, endpointID := range endpointIDs { + set[endpointID] = true + } + + return set +}