fix(edge-stacks): add an endpoint to delete the status of an edge stack EE-2432 (#6551)

pull/6862/head
andres-portainer 2022-04-28 16:50:23 -03:00 committed by GitHub
parent 61a3bfe994
commit 8a6024ce9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 124 additions and 4 deletions

View File

@ -0,0 +1,64 @@
package edgestacks
import (
"net/http"
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"
)
func (handler *Handler) handlerDBErr(err error, msg string) *httperror.HandlerError {
httpErr := &httperror.HandlerError{http.StatusInternalServerError, msg, err}
if handler.DataStore.IsErrObjectNotFound(err) {
httpErr.StatusCode = http.StatusNotFound
}
return httpErr
}
// @id EdgeStackStatusDelete
// @summary Delete an EdgeStack status
// @description Authorized only if the request is done by an Edge Environment(Endpoint)
// @tags edge_stacks
// @produce json
// @param id path string true "EdgeStack Id"
// @success 200 {object} portainer.EdgeStack
// @failure 500
// @failure 400
// @failure 404
// @failure 403
// @router /edge_stacks/{id}/status/{endpoint_id} [delete]
func (handler *Handler) edgeStackStatusDelete(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
stackID, err := request.RetrieveNumericRouteVariableValue(r, "id")
if err != nil {
return &httperror.HandlerError{http.StatusBadRequest, "Invalid stack identifier route variable", err}
}
endpoint, err := middlewares.FetchEndpoint(r)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve a valid endpoint from the handler context", err}
}
err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access environment", err}
}
stack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(stackID))
if err != nil {
return handler.handlerDBErr(err, "Unable to find a stack with the specified identifier inside the database")
}
delete(stack.Status, endpoint.ID)
err = handler.DataStore.EdgeStack().UpdateEdgeStack(stack.ID, stack)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the stack changes inside the database", err}
}
return response.JSON(w, stack)
}

View File

@ -10,6 +10,7 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/filesystem"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/http/security"
)
@ -24,10 +25,11 @@ type Handler struct {
}
// NewHandler creates a handler to manage environment(endpoint) group operations.
func NewHandler(bouncer *security.RequestBouncer) *Handler {
func NewHandler(bouncer *security.RequestBouncer, dataStore dataservices.DataStore) *Handler {
h := &Handler{
Router: mux.NewRouter(),
requestBouncer: bouncer,
DataStore: dataStore,
}
h.Handle("/edge_stacks",
bouncer.AdminAccess(bouncer.EdgeComputeOperation(httperror.LoggerHandler(h.edgeStackCreate)))).Methods(http.MethodPost)
@ -43,6 +45,12 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
bouncer.AdminAccess(bouncer.EdgeComputeOperation(httperror.LoggerHandler(h.edgeStackFile)))).Methods(http.MethodGet)
h.Handle("/edge_stacks/{id}/status",
bouncer.PublicAccess(httperror.LoggerHandler(h.edgeStackStatusUpdate))).Methods(http.MethodPut)
edgeStackStatusRouter := h.NewRoute().Subrouter()
edgeStackStatusRouter.Use(middlewares.WithEndpoint(h.DataStore.Endpoint(), "endpoint_id"))
edgeStackStatusRouter.PathPrefix("/edge_stacks/{id}/status/{endpoint_id}").Handler(bouncer.PublicAccess(httperror.LoggerHandler(h.edgeStackStatusDelete))).Methods(http.MethodDelete)
return h
}

View File

@ -139,8 +139,7 @@ func (server *Server) Start() error {
edgeJobsHandler.FileService = server.FileService
edgeJobsHandler.ReverseTunnelService = server.ReverseTunnelService
var edgeStacksHandler = edgestacks.NewHandler(requestBouncer)
edgeStacksHandler.DataStore = server.DataStore
var edgeStacksHandler = edgestacks.NewHandler(requestBouncer, server.DataStore)
edgeStacksHandler.FileService = server.FileService
edgeStacksHandler.GitService = server.GitService
edgeStacksHandler.KubernetesDeployer = server.KubernetesDeployer

View File

@ -1,4 +1,5 @@
import _ from 'lodash-es';
import { confirmAsync } from '@/portainer/services/modal.service/confirm';
export class EdgeGroupFormController {
/* @ngInject */
@ -17,6 +18,7 @@ export class EdgeGroupFormController {
};
this.associateEndpoint = this.associateEndpoint.bind(this);
this.dissociateEndpointAsync = this.dissociateEndpointAsync.bind(this);
this.dissociateEndpoint = this.dissociateEndpoint.bind(this);
this.getDynamicEndpointsAsync = this.getDynamicEndpointsAsync.bind(this);
this.getDynamicEndpoints = this.getDynamicEndpoints.bind(this);
@ -39,6 +41,29 @@ export class EdgeGroupFormController {
}
dissociateEndpoint(endpoint) {
return this.$async(this.dissociateEndpointAsync, endpoint);
}
async dissociateEndpointAsync(endpoint) {
const confirmed = await confirmAsync({
title: 'Confirm action',
message: 'Removing the environment from this group will remove its corresponding edge stacks',
buttons: {
cancel: {
label: 'Cancel',
className: 'btn-default',
},
confirm: {
label: 'Confirm',
className: 'btn-primary',
},
},
});
if (!confirmed) {
return;
}
this.model.Endpoints = _.filter(this.model.Endpoints, (id) => id !== endpoint.Id);
}

View File

@ -5,6 +5,8 @@ import { PortainerEndpointTypes } from '@/portainer/models/endpoint/models';
import { EndpointSecurityFormData } from '@/portainer/components/endpointSecurity/porEndpointSecurityModel';
import EndpointHelper from '@/portainer/helpers/endpointHelper';
import { getAMTInfo } from 'Portainer/hostmanagement/open-amt/open-amt.service';
import { confirmAsync } from '@/portainer/services/modal.service/confirm';
import { isEdgeEnvironment } from '@/portainer/environments/utils';
angular.module('portainer.app').controller('EndpointController', EndpointController);
@ -105,7 +107,7 @@ function EndpointController(
});
}
$scope.updateEndpoint = function () {
$scope.updateEndpoint = async function () {
var endpoint = $scope.endpoint;
var securityData = $scope.formValues.SecurityFormData;
var TLS = securityData.TLS;
@ -113,6 +115,27 @@ function EndpointController(
var TLSSkipVerify = TLS && (TLSMode === 'tls_client_noca' || TLSMode === 'tls_only');
var TLSSkipClientVerify = TLS && (TLSMode === 'tls_ca' || TLSMode === 'tls_only');
if (isEdgeEnvironment(endpoint.Type) && _.difference($scope.initialTagIds, endpoint.TagIds).length > 0) {
let confirmed = await confirmAsync({
title: 'Confirm action',
message: 'Removing tags from this environment will remove the corresponding edge stacks when dynamic grouping is being used',
buttons: {
cancel: {
label: 'Cancel',
className: 'btn-default',
},
confirm: {
label: 'Confirm',
className: 'btn-primary',
},
},
});
if (!confirmed) {
return;
}
}
var payload = {
Name: endpoint.Name,
PublicURL: endpoint.PublicURL,
@ -229,6 +252,7 @@ function EndpointController(
}
$scope.endpoint = endpoint;
$scope.initialTagIds = endpoint.TagIds.slice();
$scope.groups = groups;
$scope.availableTags = tags;