1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-21 22:39:41 +02:00
portainer/api/http/handler/stacks/handler.go
Dmitry Salakhov a71e71f481
feat(compose): add docker-compose wrapper (#4713)
* feat(compose): add docker-compose wrapper

ce-187

* fix(compose): pick compose implementation upon startup

* Add static compose build for linux

* Fix wget

* Fix platofrm specific docker-compose download

* Keep amd64 architecture as download parameter

* Add tmp folder for docker-compose

* fix: line endings

* add proxy server

* logs

* Proxy

* Add lite transport for compose

* Fix local deployment

* refactor: pass proxyManager by ref

* fix: string conversion

* refactor: compose wrapper remove unused code

* fix: tests

* Add edge

* Fix merge issue

* refactor: remove unused code

* Move server to proxy implementation

* Cleanup wrapper and manager

* feat: pass max supported compose syntax version with each endpoint

* fix: pick compose syntax version

* fix: store wrapper version in portainer

* Get and show composeSyntaxMaxVersion at stack creation screen

* Get and show composeSyntaxMaxVersion at stack editor screen

* refactor: proxy server

* Fix used tmp

* Bump docker-compose to 1.28.0

* remove message for docker compose limitation

* fix: markup typo

* Rollback docker compose to 1.27.4

* * attempt to fix the windows build issue

* * attempt to debug grunt issue

* * use console log in grunt file

* fix: try to fix windows build by removing indirect deps from go.mod

* Remove tmp folder

* Remove builder stage

* feat(build/windows): add git for Docker Compose

* feat(build/windows): add git for Docker Compose

* feat(build/windows): add git for Docker Compose

* feat(build/windows): add git for Docker Compose

* feat(build/windows): add git for Docker Compose

* feat(build/windows): add git for Docker Compose - fixed verbose output

* refactor: renames

* fix(stack): get endpoint by EndpointProvider

* fix(stack): use margin to add space between line instead of using br tag

Co-authored-by: Stéphane Busso <stephane.busso@gmail.com>
Co-authored-by: Simon Meng <simon.meng@portainer.io>
Co-authored-by: yi-portainer <yi.chen@portainer.io>
Co-authored-by: Steven Kang <skan070@gmail.com>
2021-01-26 08:16:53 +13:00

105 lines
3.7 KiB
Go

package stacks
import (
"errors"
"net/http"
"sync"
"github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/authorization"
)
var (
errStackAlreadyExists = errors.New("A stack already exists with this name")
errStackNotExternal = errors.New("Not an external stack")
)
// Handler is the HTTP handler used to handle stack operations.
type Handler struct {
stackCreationMutex *sync.Mutex
stackDeletionMutex *sync.Mutex
requestBouncer *security.RequestBouncer
*mux.Router
DataStore portainer.DataStore
FileService portainer.FileService
GitService portainer.GitService
SwarmStackManager portainer.SwarmStackManager
ComposeStackManager portainer.ComposeStackManager
KubernetesDeployer portainer.KubernetesDeployer
}
// NewHandler creates a handler to manage stack operations.
func NewHandler(bouncer *security.RequestBouncer) *Handler {
h := &Handler{
Router: mux.NewRouter(),
stackCreationMutex: &sync.Mutex{},
stackDeletionMutex: &sync.Mutex{},
requestBouncer: bouncer,
}
h.Handle("/stacks",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.stackCreate))).Methods(http.MethodPost)
h.Handle("/stacks",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.stackList))).Methods(http.MethodGet)
h.Handle("/stacks/{id}",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.stackInspect))).Methods(http.MethodGet)
h.Handle("/stacks/{id}",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.stackDelete))).Methods(http.MethodDelete)
h.Handle("/stacks/{id}",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.stackUpdate))).Methods(http.MethodPut)
h.Handle("/stacks/{id}/file",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.stackFile))).Methods(http.MethodGet)
h.Handle("/stacks/{id}/migrate",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.stackMigrate))).Methods(http.MethodPost)
h.Handle("/stacks/{id}/start",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.stackStart))).Methods(http.MethodPost)
h.Handle("/stacks/{id}/stop",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.stackStop))).Methods(http.MethodPost)
return h
}
func (handler *Handler) userCanAccessStack(securityContext *security.RestrictedRequestContext, endpointID portainer.EndpointID, resourceControl *portainer.ResourceControl) (bool, error) {
user, err := handler.DataStore.User().User(securityContext.UserID)
if err != nil {
return false, err
}
userTeamIDs := make([]portainer.TeamID, 0)
for _, membership := range securityContext.UserMemberships {
userTeamIDs = append(userTeamIDs, membership.TeamID)
}
if resourceControl != nil && authorization.UserCanAccessResource(securityContext.UserID, userTeamIDs, resourceControl) {
return true, nil
}
return handler.userIsAdminOrEndpointAdmin(user, endpointID)
}
func (handler *Handler) userIsAdmin(userID portainer.UserID) (bool, error) {
user, err := handler.DataStore.User().User(userID)
if err != nil {
return false, err
}
isAdmin := user.Role == portainer.AdministratorRole
return isAdmin, nil
}
func (handler *Handler) userIsAdminOrEndpointAdmin(user *portainer.User, endpointID portainer.EndpointID) (bool, error) {
isAdmin := user.Role == portainer.AdministratorRole
return isAdmin, nil
}
func (handler *Handler) userCanCreateStack(securityContext *security.RestrictedRequestContext, endpointID portainer.EndpointID) (bool, error) {
user, err := handler.DataStore.User().User(securityContext.UserID)
if err != nil {
return false, err
}
return handler.userIsAdminOrEndpointAdmin(user, endpointID)
}