1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-19 13:29:41 +02:00

fix(kubernetes): remove unique check from kubernetes stacks [EE-6170] (#10542)

This commit is contained in:
Prabhat Khera 2023-10-27 15:41:02 +13:00 committed by GitHub
parent 8ee718f808
commit 26036c05f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 164 additions and 52 deletions

View file

@ -251,3 +251,137 @@ func (handler *Handler) deleteStack(userID portainer.UserID, stack *portainer.St
return fmt.Errorf("unsupported stack type: %v", stack.Type)
}
// @id StackDeleteKubernetesByName
// @summary Remove Kubernetes stacks by name
// @description Remove a stack.
// @description **Access policy**: restricted
// @tags stacks
// @security ApiKeyAuth
// @security jwt
// @param name path string true "Stack name"
// @param external query boolean false "Set to true to delete an external stack. Only external Swarm stacks are supported"
// @param endpointId query int true "Environment identifier"
// @success 204 "Success"
// @failure 400 "Invalid request"
// @failure 403 "Permission denied"
// @failure 404 "Not found"
// @failure 500 "Server error"
// @router /stacks/name/{name} [delete]
func (handler *Handler) stackDeleteKubernetesByName(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
stackName, err := request.RetrieveRouteVariableValue(r, "name")
if err != nil {
return httperror.BadRequest("Invalid stack identifier route variable", err)
}
log.Debug().Msgf("Trying to delete Kubernetes stack %q", stackName)
securityContext, err := security.RetrieveRestrictedRequestContext(r)
if err != nil {
return httperror.InternalServerError("Unable to retrieve info from request context", err)
}
endpointID, err := request.RetrieveNumericQueryParameter(r, "endpointId", true)
if err != nil {
return httperror.BadRequest("Invalid query parameter: endpointId", err)
}
namespace, err := request.RetrieveQueryParameter(r, "namespace", false)
if err != nil {
return httperror.BadRequest("Invalid query parameter: namespace", err)
}
stacks, err := handler.DataStore.Stack().StacksByName(stackName)
if err != nil && !handler.DataStore.IsErrObjectNotFound(err) {
return httperror.InternalServerError("Unable to check for stack existence inside the database", err)
}
if stacks == nil {
return httperror.InternalServerError("Unable to find a stacks with the specified identifier name the database", err)
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
if handler.DataStore.IsErrObjectNotFound(err) {
return httperror.NotFound("Unable to find the endpoint associated to the stack inside the database", err)
} else if err != nil {
return httperror.InternalServerError("Unable to find the endpoint associated to the stack inside the database", err)
}
log.Debug().Msgf("Trying to delete Kubernetes stack %q for endpoint `%d`", stackName, endpointID)
// check authorizations on all the stacks one by one
stacksToDelete := make([]portainer.Stack, 0)
for _, stack := range stacks {
// only delete stacks for the specified namespace
if stack.Namespace != namespace {
continue
}
isOrphaned := portainer.EndpointID(endpointID) != stack.EndpointID
if stack.Type != portainer.KubernetesStack {
return httperror.BadRequest("Only Kubernetes stacks can be deleted by name", errors.New("Only Kubernetes stacks can be deleted by name"))
}
if isOrphaned && !securityContext.IsAdmin {
return httperror.Forbidden("Permission denied to remove orphaned stack", errors.New("Permission denied to remove orphaned stack"))
}
if !isOrphaned {
err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint)
if err != nil {
return httperror.Forbidden("Permission denied to access endpoint", err)
}
}
canManage, err := handler.userCanManageStacks(securityContext, endpoint)
if err != nil {
return httperror.InternalServerError("Unable to verify user authorizations to validate stack deletion", err)
}
if !canManage {
errMsg := "stack deletion is disabled for non-admin users"
return httperror.Forbidden(errMsg, fmt.Errorf(errMsg))
}
stacksToDelete = append(stacksToDelete, stack)
}
log.Debug().Msgf("Trying to delete Kubernetes stacks `%v` for endpoint `%d`", stacksToDelete, endpointID)
errors := make([]error, 0)
// Delete all the stacks one by one
for _, stack := range stacksToDelete {
log.Debug().Msgf("Trying to delete Kubernetes stack id `%d`", stack.ID)
// stop scheduler updates of the stack before removal
if stack.AutoUpdate != nil {
deployments.StopAutoupdate(stack.ID, stack.AutoUpdate.JobID, handler.Scheduler)
}
err = handler.deleteStack(securityContext.UserID, &stack, endpoint)
if err != nil {
log.Err(err).Msgf("Unable to delete Kubernetes stack `%d`", stack.ID)
errors = append(errors, err)
continue
}
err = handler.DataStore.Stack().Delete(stack.ID)
if err != nil {
errors = append(errors, err)
log.Err(err).Msgf("Unable to remove the stack `%d` from the database", stack.ID)
continue
}
err = handler.FileService.RemoveDirectory(stack.ProjectPath)
if err != nil {
errors = append(errors, err)
log.Warn().Err(err).Msg("Unable to remove stack files from disk")
}
log.Debug().Msgf("Kubernetes stack `%d` deleted", stack.ID)
}
if len(errors) > 0 {
return httperror.InternalServerError("Unable to delete some Kubernetes stack(s). Check Portainer logs for more details", nil)
}
return response.Empty(w)
}