mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
fix(container/network): recreate container changes static IP [EE-5448] (#8960)
Co-authored-by: Chaim Lev-Ari <chaim.levi-ari@portainer.io>
This commit is contained in:
parent
d340c4ea96
commit
96de026eba
35 changed files with 1651 additions and 491 deletions
|
@ -5,27 +5,35 @@ import (
|
|||
|
||||
"github.com/gorilla/mux"
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/docker"
|
||||
dockerclient "github.com/portainer/portainer/api/docker/client"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
*mux.Router
|
||||
dockerClientFactory *docker.ClientFactory
|
||||
dockerClientFactory *dockerclient.ClientFactory
|
||||
dataStore dataservices.DataStore
|
||||
containerService *docker.ContainerService
|
||||
bouncer *security.RequestBouncer
|
||||
}
|
||||
|
||||
// NewHandler creates a handler to process non-proxied requests to docker APIs directly.
|
||||
func NewHandler(routePrefix string, bouncer *security.RequestBouncer, dockerClientFactory *docker.ClientFactory) *Handler {
|
||||
func NewHandler(routePrefix string, bouncer *security.RequestBouncer, dataStore dataservices.DataStore, dockerClientFactory *dockerclient.ClientFactory, containerService *docker.ContainerService) *Handler {
|
||||
h := &Handler{
|
||||
Router: mux.NewRouter(),
|
||||
|
||||
Router: mux.NewRouter(),
|
||||
dataStore: dataStore,
|
||||
dockerClientFactory: dockerClientFactory,
|
||||
containerService: containerService,
|
||||
bouncer: bouncer,
|
||||
}
|
||||
|
||||
router := h.PathPrefix(routePrefix).Subrouter()
|
||||
router.Use(bouncer.AuthenticatedAccess)
|
||||
|
||||
router.Handle("/{containerId}/gpus", httperror.LoggerHandler(h.containerGpusInspect)).Methods(http.MethodGet)
|
||||
router.Handle("/{containerId}/recreate", httperror.LoggerHandler(h.recreate)).Methods(http.MethodPost)
|
||||
|
||||
return h
|
||||
}
|
||||
|
|
97
api/http/handler/docker/containers/recreate.go
Normal file
97
api/http/handler/docker/containers/recreate.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
package containers
|
||||
|
||||
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/docker/consts"
|
||||
"github.com/portainer/portainer/api/docker/images"
|
||||
"github.com/portainer/portainer/api/http/middlewares"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type RecreatePayload struct {
|
||||
// PullImage if true will pull the image
|
||||
PullImage bool `json:"PullImage"`
|
||||
}
|
||||
|
||||
func (r RecreatePayload) Validate(request *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (handler *Handler) recreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
containerID, err := request.RetrieveRouteVariableValue(r, "containerId")
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Invalid containerId", err)
|
||||
}
|
||||
|
||||
var payload RecreatePayload
|
||||
err = request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
endpoint, err := middlewares.FetchEndpoint(r)
|
||||
if err != nil {
|
||||
return httperror.NotFound("Unable to find an environment on request context", err)
|
||||
}
|
||||
|
||||
err = handler.bouncer.AuthorizedEndpointOperation(r, endpoint)
|
||||
if err != nil {
|
||||
return httperror.Forbidden("Permission denied to force update service", err)
|
||||
}
|
||||
|
||||
agentTargetHeader := r.Header.Get(portainer.PortainerAgentTargetHeader)
|
||||
|
||||
newContainer, err := handler.containerService.Recreate(r.Context(), endpoint, containerID, payload.PullImage, "", agentTargetHeader)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Error recreating container", err)
|
||||
}
|
||||
|
||||
handler.updateWebhook(containerID, newContainer.ID)
|
||||
handler.createResourceControl(containerID, newContainer.ID)
|
||||
|
||||
go func() {
|
||||
images.EvictImageStatus(containerID)
|
||||
images.EvictImageStatus(newContainer.Config.Labels[consts.ComposeStackNameLabel])
|
||||
images.EvictImageStatus(newContainer.Config.Labels[consts.SwarmServiceIdLabel])
|
||||
}()
|
||||
return response.JSON(w, newContainer)
|
||||
}
|
||||
|
||||
func (handler *Handler) createResourceControl(oldContainerId string, newContainerId string) {
|
||||
resourceControls, err := handler.dataStore.ResourceControl().ResourceControls()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Exporting Resource Controls")
|
||||
return
|
||||
}
|
||||
|
||||
resourceControl := authorization.GetResourceControlByResourceIDAndType(oldContainerId, portainer.ContainerResourceControl, resourceControls)
|
||||
if resourceControl == nil {
|
||||
return
|
||||
}
|
||||
resourceControl.ResourceID = newContainerId
|
||||
err = handler.dataStore.ResourceControl().Create(resourceControl)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("containerId", newContainerId).Msg("Failed to create new resource control for container")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (handler *Handler) updateWebhook(oldContainerId string, newContainerId string) {
|
||||
webhook, err := handler.dataStore.Webhook().WebhookByResourceID(oldContainerId)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("containerId", oldContainerId).Msg("cannot find webhook by containerId")
|
||||
return
|
||||
}
|
||||
|
||||
webhook.ResourceID = newContainerId
|
||||
err = handler.dataStore.Webhook().UpdateWebhook(webhook.ID, webhook)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Int("webhookId", int(webhook.ID)).Msg("cannot update webhook")
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/portainer/portainer/api/docker"
|
||||
dockerclient "github.com/portainer/portainer/api/docker/client"
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -21,18 +22,20 @@ type Handler struct {
|
|||
*mux.Router
|
||||
requestBouncer *security.RequestBouncer
|
||||
dataStore dataservices.DataStore
|
||||
dockerClientFactory *docker.ClientFactory
|
||||
dockerClientFactory *dockerclient.ClientFactory
|
||||
authorizationService *authorization.Service
|
||||
containerService *docker.ContainerService
|
||||
}
|
||||
|
||||
// NewHandler creates a handler to process non-proxied requests to docker APIs directly.
|
||||
func NewHandler(bouncer *security.RequestBouncer, authorizationService *authorization.Service, dataStore dataservices.DataStore, dockerClientFactory *docker.ClientFactory) *Handler {
|
||||
func NewHandler(bouncer *security.RequestBouncer, authorizationService *authorization.Service, dataStore dataservices.DataStore, dockerClientFactory *dockerclient.ClientFactory, containerService *docker.ContainerService) *Handler {
|
||||
h := &Handler{
|
||||
Router: mux.NewRouter(),
|
||||
requestBouncer: bouncer,
|
||||
authorizationService: authorizationService,
|
||||
dataStore: dataStore,
|
||||
dockerClientFactory: dockerClientFactory,
|
||||
containerService: containerService,
|
||||
}
|
||||
|
||||
// endpoints
|
||||
|
@ -40,7 +43,7 @@ func NewHandler(bouncer *security.RequestBouncer, authorizationService *authoriz
|
|||
endpointRouter.Use(middlewares.WithEndpoint(dataStore.Endpoint(), "id"))
|
||||
endpointRouter.Use(dockerOnlyMiddleware)
|
||||
|
||||
containersHandler := containers.NewHandler("/{id}/containers", bouncer, dockerClientFactory)
|
||||
containersHandler := containers.NewHandler("/{id}/containers", bouncer, dataStore, dockerClientFactory, containerService)
|
||||
endpointRouter.PathPrefix("/containers").Handler(containersHandler)
|
||||
return h
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue