You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
portainer/api/http/handler/docker/containers/recreate.go

98 lines
3.2 KiB

package containers
import (
"net/http"
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"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"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
if err := request.DecodeAndValidateJSONPayload(r, &payload); 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)
}
if err := handler.bouncer.AuthorizedEndpointOperation(r, endpoint); 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().ReadAll()
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
if err := handler.dataStore.ResourceControl().Create(resourceControl); err != nil {
log.Error().Err(err).Str("containerId", newContainerId).Msg("Failed to create new resource control for container")
}
}
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
if err := handler.dataStore.Webhook().Update(webhook.ID, webhook); err != nil {
log.Error().Err(err).Int("webhookId", int(webhook.ID)).Msg("cannot update webhook")
}
}