mirror of https://github.com/portainer/portainer
fix(stack): EE-4213 Allow latest image to be pulled for stacks: backport backend logic (#7669)
parent
fa162cafc1
commit
6078234d07
|
@ -85,6 +85,27 @@ func (manager *ComposeStackManager) Down(ctx context.Context, stack *portainer.S
|
||||||
return errors.Wrap(err, "failed to remove a stack")
|
return errors.Wrap(err, "failed to remove a stack")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pull an image associated with a service defined in a docker-compose.yml or docker-stack.yml file,
|
||||||
|
// but does not start containers based on those images.
|
||||||
|
func (manager *ComposeStackManager) Pull(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error {
|
||||||
|
url, proxy, err := manager.fetchEndpointProxy(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if proxy != nil {
|
||||||
|
defer proxy.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
envFile, err := createEnvFile(stack)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to create env file")
|
||||||
|
}
|
||||||
|
|
||||||
|
filePaths := stackutils.GetStackFilePaths(stack)
|
||||||
|
err = manager.deployer.Pull(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFile)
|
||||||
|
return errors.Wrap(err, "failed to pull images of the stack")
|
||||||
|
}
|
||||||
|
|
||||||
// NormalizeStackName returns a new stack name with unsupported characters replaced
|
// NormalizeStackName returns a new stack name with unsupported characters replaced
|
||||||
func (manager *ComposeStackManager) NormalizeStackName(name string) string {
|
func (manager *ComposeStackManager) NormalizeStackName(name string) string {
|
||||||
return stackNameNormalizeRegex.ReplaceAllString(strings.ToLower(name), "")
|
return stackNameNormalizeRegex.ReplaceAllString(strings.ToLower(name), "")
|
||||||
|
|
|
@ -89,7 +89,7 @@ func (manager *SwarmStackManager) Logout(endpoint *portainer.Endpoint) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deploy executes the docker stack deploy command.
|
// Deploy executes the docker stack deploy command.
|
||||||
func (manager *SwarmStackManager) Deploy(stack *portainer.Stack, prune bool, endpoint *portainer.Endpoint) error {
|
func (manager *SwarmStackManager) Deploy(stack *portainer.Stack, prune bool, pullImage bool, endpoint *portainer.Endpoint) error {
|
||||||
filePaths := stackutils.GetStackFilePaths(stack)
|
filePaths := stackutils.GetStackFilePaths(stack)
|
||||||
command, args, err := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint)
|
command, args, err := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -101,6 +101,9 @@ func (manager *SwarmStackManager) Deploy(stack *portainer.Stack, prune bool, end
|
||||||
} else {
|
} else {
|
||||||
args = append(args, "stack", "deploy", "--with-registry-auth")
|
args = append(args, "stack", "deploy", "--with-registry-auth")
|
||||||
}
|
}
|
||||||
|
if !pullImage {
|
||||||
|
args = append(args, "--resolve-image=never")
|
||||||
|
}
|
||||||
|
|
||||||
args = configureFilePaths(args, filePaths)
|
args = configureFilePaths(args, filePaths)
|
||||||
args = append(args, stack.Name)
|
args = append(args, stack.Name)
|
||||||
|
|
|
@ -124,7 +124,7 @@ func (handler *Handler) createComposeStackFromFileContent(w http.ResponseWriter,
|
||||||
doCleanUp := true
|
doCleanUp := true
|
||||||
defer handler.cleanUp(stack, &doCleanUp)
|
defer handler.cleanUp(stack, &doCleanUp)
|
||||||
|
|
||||||
config, configErr := handler.createComposeDeployConfig(r, stack, endpoint)
|
config, configErr := handler.createComposeDeployConfig(r, stack, endpoint, false)
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
return configErr
|
return configErr
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ func (handler *Handler) createComposeStackFromGitRepository(w http.ResponseWrite
|
||||||
}
|
}
|
||||||
stack.GitConfig.ConfigHash = commitID
|
stack.GitConfig.ConfigHash = commitID
|
||||||
|
|
||||||
config, configErr := handler.createComposeDeployConfig(r, stack, endpoint)
|
config, configErr := handler.createComposeDeployConfig(r, stack, endpoint, false)
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
return configErr
|
return configErr
|
||||||
}
|
}
|
||||||
|
@ -386,7 +386,7 @@ func (handler *Handler) createComposeStackFromFileUpload(w http.ResponseWriter,
|
||||||
doCleanUp := true
|
doCleanUp := true
|
||||||
defer handler.cleanUp(stack, &doCleanUp)
|
defer handler.cleanUp(stack, &doCleanUp)
|
||||||
|
|
||||||
config, configErr := handler.createComposeDeployConfig(r, stack, endpoint)
|
config, configErr := handler.createComposeDeployConfig(r, stack, endpoint, false)
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
return configErr
|
return configErr
|
||||||
}
|
}
|
||||||
|
@ -408,14 +408,15 @@ func (handler *Handler) createComposeStackFromFileUpload(w http.ResponseWriter,
|
||||||
}
|
}
|
||||||
|
|
||||||
type composeStackDeploymentConfig struct {
|
type composeStackDeploymentConfig struct {
|
||||||
stack *portainer.Stack
|
stack *portainer.Stack
|
||||||
endpoint *portainer.Endpoint
|
endpoint *portainer.Endpoint
|
||||||
registries []portainer.Registry
|
registries []portainer.Registry
|
||||||
isAdmin bool
|
isAdmin bool
|
||||||
user *portainer.User
|
user *portainer.User
|
||||||
|
forcePullImage bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) createComposeDeployConfig(r *http.Request, stack *portainer.Stack, endpoint *portainer.Endpoint) (*composeStackDeploymentConfig, *httperror.HandlerError) {
|
func (handler *Handler) createComposeDeployConfig(r *http.Request, stack *portainer.Stack, endpoint *portainer.Endpoint, forcePullImage bool) (*composeStackDeploymentConfig, *httperror.HandlerError) {
|
||||||
securityContext, err := security.RetrieveRestrictedRequestContext(r)
|
securityContext, err := security.RetrieveRestrictedRequestContext(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, httperror.InternalServerError("Unable to retrieve info from request context", err)
|
return nil, httperror.InternalServerError("Unable to retrieve info from request context", err)
|
||||||
|
@ -433,11 +434,12 @@ func (handler *Handler) createComposeDeployConfig(r *http.Request, stack *portai
|
||||||
filteredRegistries := security.FilterRegistries(registries, user, securityContext.UserMemberships, endpoint.ID)
|
filteredRegistries := security.FilterRegistries(registries, user, securityContext.UserMemberships, endpoint.ID)
|
||||||
|
|
||||||
config := &composeStackDeploymentConfig{
|
config := &composeStackDeploymentConfig{
|
||||||
stack: stack,
|
stack: stack,
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
registries: filteredRegistries,
|
registries: filteredRegistries,
|
||||||
isAdmin: securityContext.IsAdmin,
|
isAdmin: securityContext.IsAdmin,
|
||||||
user: user,
|
user: user,
|
||||||
|
forcePullImage: forcePullImage,
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
|
@ -477,5 +479,5 @@ func (handler *Handler) deployComposeStack(config *composeStackDeploymentConfig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.StackDeployer.DeployComposeStack(config.stack, config.endpoint, config.registries, forceCreate)
|
return handler.StackDeployer.DeployComposeStack(config.stack, config.endpoint, config.registries, config.forcePullImage, forceCreate)
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ func (handler *Handler) createSwarmStackFromFileContent(w http.ResponseWriter, r
|
||||||
doCleanUp := true
|
doCleanUp := true
|
||||||
defer handler.cleanUp(stack, &doCleanUp)
|
defer handler.cleanUp(stack, &doCleanUp)
|
||||||
|
|
||||||
config, configErr := handler.createSwarmDeployConfig(r, stack, endpoint, false)
|
config, configErr := handler.createSwarmDeployConfig(r, stack, endpoint, false, true)
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
return configErr
|
return configErr
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,7 @@ func (handler *Handler) createSwarmStackFromGitRepository(w http.ResponseWriter,
|
||||||
}
|
}
|
||||||
stack.GitConfig.ConfigHash = commitID
|
stack.GitConfig.ConfigHash = commitID
|
||||||
|
|
||||||
config, configErr := handler.createSwarmDeployConfig(r, stack, endpoint, false)
|
config, configErr := handler.createSwarmDeployConfig(r, stack, endpoint, false, true)
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
return configErr
|
return configErr
|
||||||
}
|
}
|
||||||
|
@ -332,7 +332,7 @@ func (handler *Handler) createSwarmStackFromFileUpload(w http.ResponseWriter, r
|
||||||
doCleanUp := true
|
doCleanUp := true
|
||||||
defer handler.cleanUp(stack, &doCleanUp)
|
defer handler.cleanUp(stack, &doCleanUp)
|
||||||
|
|
||||||
config, configErr := handler.createSwarmDeployConfig(r, stack, endpoint, false)
|
config, configErr := handler.createSwarmDeployConfig(r, stack, endpoint, false, true)
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
return configErr
|
return configErr
|
||||||
}
|
}
|
||||||
|
@ -360,9 +360,10 @@ type swarmStackDeploymentConfig struct {
|
||||||
prune bool
|
prune bool
|
||||||
isAdmin bool
|
isAdmin bool
|
||||||
user *portainer.User
|
user *portainer.User
|
||||||
|
pullImage bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) createSwarmDeployConfig(r *http.Request, stack *portainer.Stack, endpoint *portainer.Endpoint, prune bool) (*swarmStackDeploymentConfig, *httperror.HandlerError) {
|
func (handler *Handler) createSwarmDeployConfig(r *http.Request, stack *portainer.Stack, endpoint *portainer.Endpoint, prune bool, pullImage bool) (*swarmStackDeploymentConfig, *httperror.HandlerError) {
|
||||||
securityContext, err := security.RetrieveRestrictedRequestContext(r)
|
securityContext, err := security.RetrieveRestrictedRequestContext(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, httperror.InternalServerError("Unable to retrieve info from request context", err)
|
return nil, httperror.InternalServerError("Unable to retrieve info from request context", err)
|
||||||
|
@ -386,6 +387,7 @@ func (handler *Handler) createSwarmDeployConfig(r *http.Request, stack *portaine
|
||||||
prune: prune,
|
prune: prune,
|
||||||
isAdmin: securityContext.IsAdmin,
|
isAdmin: securityContext.IsAdmin,
|
||||||
user: user,
|
user: user,
|
||||||
|
pullImage: pullImage,
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
|
@ -413,5 +415,5 @@ func (handler *Handler) deploySwarmStack(config *swarmStackDeploymentConfig) err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.StackDeployer.DeploySwarmStack(config.stack, config.endpoint, config.registries, config.prune)
|
return handler.StackDeployer.DeploySwarmStack(config.stack, config.endpoint, config.registries, config.prune, config.pullImage)
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,7 @@ func (handler *Handler) migrateStack(r *http.Request, stack *portainer.Stack, ne
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) migrateComposeStack(r *http.Request, stack *portainer.Stack, next *portainer.Endpoint) *httperror.HandlerError {
|
func (handler *Handler) migrateComposeStack(r *http.Request, stack *portainer.Stack, next *portainer.Endpoint) *httperror.HandlerError {
|
||||||
config, configErr := handler.createComposeDeployConfig(r, stack, next)
|
config, configErr := handler.createComposeDeployConfig(r, stack, next, false)
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
return configErr
|
return configErr
|
||||||
}
|
}
|
||||||
|
@ -203,7 +203,7 @@ func (handler *Handler) migrateComposeStack(r *http.Request, stack *portainer.St
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) migrateSwarmStack(r *http.Request, stack *portainer.Stack, next *portainer.Endpoint) *httperror.HandlerError {
|
func (handler *Handler) migrateSwarmStack(r *http.Request, stack *portainer.Stack, next *portainer.Endpoint) *httperror.HandlerError {
|
||||||
config, configErr := handler.createSwarmDeployConfig(r, stack, next, true)
|
config, configErr := handler.createSwarmDeployConfig(r, stack, next, true, true)
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
return configErr
|
return configErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ func (handler *Handler) startStack(stack *portainer.Stack, endpoint *portainer.E
|
||||||
case portainer.DockerComposeStack:
|
case portainer.DockerComposeStack:
|
||||||
return handler.ComposeStackManager.Up(context.TODO(), stack, endpoint, false)
|
return handler.ComposeStackManager.Up(context.TODO(), stack, endpoint, false)
|
||||||
case portainer.DockerSwarmStack:
|
case portainer.DockerSwarmStack:
|
||||||
return handler.SwarmStackManager.Deploy(stack, true, endpoint)
|
return handler.SwarmStackManager.Deploy(stack, true, true, endpoint)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ type updateComposeStackPayload struct {
|
||||||
StackFileContent string `example:"version: 3\n services:\n web:\n image:nginx"`
|
StackFileContent string `example:"version: 3\n services:\n web:\n image:nginx"`
|
||||||
// A list of environment(endpoint) variables used during stack deployment
|
// A list of environment(endpoint) variables used during stack deployment
|
||||||
Env []portainer.Pair
|
Env []portainer.Pair
|
||||||
|
// Force a pulling to current image with the original tag though the image is already the latest
|
||||||
|
PullImage bool `example:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (payload *updateComposeStackPayload) Validate(r *http.Request) error {
|
func (payload *updateComposeStackPayload) Validate(r *http.Request) error {
|
||||||
|
@ -39,6 +41,8 @@ type updateSwarmStackPayload struct {
|
||||||
Env []portainer.Pair
|
Env []portainer.Pair
|
||||||
// Prune services that are no longer referenced (only available for Swarm stacks)
|
// Prune services that are no longer referenced (only available for Swarm stacks)
|
||||||
Prune bool `example:"true"`
|
Prune bool `example:"true"`
|
||||||
|
// Force a pulling to current image with the original tag though the image is already the latest
|
||||||
|
PullImage bool `example:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (payload *updateSwarmStackPayload) Validate(r *http.Request) error {
|
func (payload *updateSwarmStackPayload) Validate(r *http.Request) error {
|
||||||
|
@ -199,7 +203,7 @@ func (handler *Handler) updateComposeStack(r *http.Request, stack *portainer.Sta
|
||||||
return httperror.InternalServerError("Unable to persist updated Compose file on disk", err)
|
return httperror.InternalServerError("Unable to persist updated Compose file on disk", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config, configErr := handler.createComposeDeployConfig(r, stack, endpoint)
|
config, configErr := handler.createComposeDeployConfig(r, stack, endpoint, payload.PullImage)
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
if rollbackErr := handler.FileService.RollbackStackFile(stackFolder, stack.EntryPoint); rollbackErr != nil {
|
if rollbackErr := handler.FileService.RollbackStackFile(stackFolder, stack.EntryPoint); rollbackErr != nil {
|
||||||
log.Printf("[WARN] [stack,update] [message: rollback stack file error] [err: %s]", rollbackErr)
|
log.Printf("[WARN] [stack,update] [message: rollback stack file error] [err: %s]", rollbackErr)
|
||||||
|
@ -250,7 +254,7 @@ func (handler *Handler) updateSwarmStack(r *http.Request, stack *portainer.Stack
|
||||||
return httperror.InternalServerError("Unable to persist updated Compose file on disk", err)
|
return httperror.InternalServerError("Unable to persist updated Compose file on disk", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config, configErr := handler.createSwarmDeployConfig(r, stack, endpoint, payload.Prune)
|
config, configErr := handler.createSwarmDeployConfig(r, stack, endpoint, payload.Prune, payload.PullImage)
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
if rollbackErr := handler.FileService.RollbackStackFile(stackFolder, stack.EntryPoint); rollbackErr != nil {
|
if rollbackErr := handler.FileService.RollbackStackFile(stackFolder, stack.EntryPoint); rollbackErr != nil {
|
||||||
log.Printf("[WARN] [swarm,stack,update] [message: rollback stack file error] [err: %s]", rollbackErr)
|
log.Printf("[WARN] [swarm,stack,update] [message: rollback stack file error] [err: %s]", rollbackErr)
|
||||||
|
|
|
@ -25,6 +25,8 @@ type stackGitRedployPayload struct {
|
||||||
RepositoryPassword string
|
RepositoryPassword string
|
||||||
Env []portainer.Pair
|
Env []portainer.Pair
|
||||||
Prune bool
|
Prune bool
|
||||||
|
// Force a pulling to current image with the original tag though the image is already the latest
|
||||||
|
PullImage bool `example:"false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (payload *stackGitRedployPayload) Validate(r *http.Request) error {
|
func (payload *stackGitRedployPayload) Validate(r *http.Request) error {
|
||||||
|
@ -167,7 +169,7 @@ func (handler *Handler) stackGitRedeploy(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
httpErr := handler.deployStack(r, stack, endpoint)
|
httpErr := handler.deployStack(r, stack, payload.PullImage, endpoint)
|
||||||
if httpErr != nil {
|
if httpErr != nil {
|
||||||
return httpErr
|
return httpErr
|
||||||
}
|
}
|
||||||
|
@ -199,14 +201,14 @@ func (handler *Handler) stackGitRedeploy(w http.ResponseWriter, r *http.Request)
|
||||||
return response.JSON(w, stack)
|
return response.JSON(w, stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) deployStack(r *http.Request, stack *portainer.Stack, endpoint *portainer.Endpoint) *httperror.HandlerError {
|
func (handler *Handler) deployStack(r *http.Request, stack *portainer.Stack, pullImage bool, endpoint *portainer.Endpoint) *httperror.HandlerError {
|
||||||
switch stack.Type {
|
switch stack.Type {
|
||||||
case portainer.DockerSwarmStack:
|
case portainer.DockerSwarmStack:
|
||||||
prune := false
|
prune := false
|
||||||
if stack.Option != nil {
|
if stack.Option != nil {
|
||||||
prune = stack.Option.Prune
|
prune = stack.Option.Prune
|
||||||
}
|
}
|
||||||
config, httpErr := handler.createSwarmDeployConfig(r, stack, endpoint, prune)
|
config, httpErr := handler.createSwarmDeployConfig(r, stack, endpoint, prune, pullImage)
|
||||||
if httpErr != nil {
|
if httpErr != nil {
|
||||||
return httpErr
|
return httpErr
|
||||||
}
|
}
|
||||||
|
@ -216,7 +218,7 @@ func (handler *Handler) deployStack(r *http.Request, stack *portainer.Stack, end
|
||||||
}
|
}
|
||||||
|
|
||||||
case portainer.DockerComposeStack:
|
case portainer.DockerComposeStack:
|
||||||
config, httpErr := handler.createComposeDeployConfig(r, stack, endpoint)
|
config, httpErr := handler.createComposeDeployConfig(r, stack, endpoint, pullImage)
|
||||||
if httpErr != nil {
|
if httpErr != nil {
|
||||||
return httpErr
|
return httpErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -1244,6 +1244,7 @@ type (
|
||||||
NormalizeStackName(name string) string
|
NormalizeStackName(name string) string
|
||||||
Up(ctx context.Context, stack *Stack, endpoint *Endpoint, forceRereate bool) error
|
Up(ctx context.Context, stack *Stack, endpoint *Endpoint, forceRereate bool) error
|
||||||
Down(ctx context.Context, stack *Stack, endpoint *Endpoint) error
|
Down(ctx context.Context, stack *Stack, endpoint *Endpoint) error
|
||||||
|
Pull(ctx context.Context, stack *Stack, endpoint *Endpoint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// CryptoService represents a service for encrypting/hashing data
|
// CryptoService represents a service for encrypting/hashing data
|
||||||
|
@ -1396,7 +1397,7 @@ type (
|
||||||
SwarmStackManager interface {
|
SwarmStackManager interface {
|
||||||
Login(registries []Registry, endpoint *Endpoint) error
|
Login(registries []Registry, endpoint *Endpoint) error
|
||||||
Logout(endpoint *Endpoint) error
|
Logout(endpoint *Endpoint) error
|
||||||
Deploy(stack *Stack, prune bool, endpoint *Endpoint) error
|
Deploy(stack *Stack, prune bool, pullImage bool, endpoint *Endpoint) error
|
||||||
Remove(stack *Stack, endpoint *Endpoint) error
|
Remove(stack *Stack, endpoint *Endpoint) error
|
||||||
NormalizeStackName(name string) string
|
NormalizeStackName(name string) string
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,12 +89,12 @@ func RedeployWhenChanged(stackID portainer.StackID, deployer StackDeployer, data
|
||||||
|
|
||||||
switch stack.Type {
|
switch stack.Type {
|
||||||
case portainer.DockerComposeStack:
|
case portainer.DockerComposeStack:
|
||||||
err := deployer.DeployComposeStack(stack, endpoint, registries, false)
|
err := deployer.DeployComposeStack(stack, endpoint, registries, true, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessagef(err, "failed to deploy a docker compose stack %v", stackID)
|
return errors.WithMessagef(err, "failed to deploy a docker compose stack %v", stackID)
|
||||||
}
|
}
|
||||||
case portainer.DockerSwarmStack:
|
case portainer.DockerSwarmStack:
|
||||||
err := deployer.DeploySwarmStack(stack, endpoint, registries, true)
|
err := deployer.DeploySwarmStack(stack, endpoint, registries, true, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessagef(err, "failed to deploy a docker compose stack %v", stackID)
|
return errors.WithMessagef(err, "failed to deploy a docker compose stack %v", stackID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,11 +27,11 @@ func (g *gitService) LatestCommitID(repositoryURL, referenceName, username, pass
|
||||||
|
|
||||||
type noopDeployer struct{}
|
type noopDeployer struct{}
|
||||||
|
|
||||||
func (s *noopDeployer) DeploySwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune bool) error {
|
func (s *noopDeployer) DeploySwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune bool, pullImage bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *noopDeployer) DeployComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, forceRereate bool) error {
|
func (s *noopDeployer) DeployComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, forcePullImage bool, forceRereate bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type StackDeployer interface {
|
type StackDeployer interface {
|
||||||
DeploySwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune bool) error
|
DeploySwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune bool, pullImage bool) error
|
||||||
DeployComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, forceRereate bool) error
|
DeployComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, forcePullImage bool, forceRereate bool) error
|
||||||
DeployKubernetesStack(stack *portainer.Stack, endpoint *portainer.Endpoint, user *portainer.User) error
|
DeployKubernetesStack(stack *portainer.Stack, endpoint *portainer.Endpoint, user *portainer.User) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,23 +35,31 @@ func NewStackDeployer(swarmStackManager portainer.SwarmStackManager, composeStac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *stackDeployer) DeploySwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune bool) error {
|
func (d *stackDeployer) DeploySwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune bool, pullImage bool) error {
|
||||||
d.lock.Lock()
|
d.lock.Lock()
|
||||||
defer d.lock.Unlock()
|
defer d.lock.Unlock()
|
||||||
|
|
||||||
d.swarmStackManager.Login(registries, endpoint)
|
d.swarmStackManager.Login(registries, endpoint)
|
||||||
defer d.swarmStackManager.Logout(endpoint)
|
defer d.swarmStackManager.Logout(endpoint)
|
||||||
|
|
||||||
return d.swarmStackManager.Deploy(stack, prune, endpoint)
|
return d.swarmStackManager.Deploy(stack, prune, pullImage, endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *stackDeployer) DeployComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, forceRereate bool) error {
|
func (d *stackDeployer) DeployComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, forcePullImage bool, forceRereate bool) error {
|
||||||
d.lock.Lock()
|
d.lock.Lock()
|
||||||
defer d.lock.Unlock()
|
defer d.lock.Unlock()
|
||||||
|
|
||||||
d.swarmStackManager.Login(registries, endpoint)
|
d.swarmStackManager.Login(registries, endpoint)
|
||||||
defer d.swarmStackManager.Logout(endpoint)
|
defer d.swarmStackManager.Logout(endpoint)
|
||||||
|
|
||||||
|
// --force-recreate doesn't pull updated images
|
||||||
|
if forcePullImage {
|
||||||
|
err := d.composeStackManager.Pull(context.TODO(), stack, endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := d.composeStackManager.Up(context.TODO(), stack, endpoint, forceRereate)
|
err := d.composeStackManager.Up(context.TODO(), stack, endpoint, forceRereate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.composeStackManager.Down(context.TODO(), stack, endpoint)
|
d.composeStackManager.Down(context.TODO(), stack, endpoint)
|
||||||
|
|
|
@ -28,6 +28,7 @@ class StackRedeployGitFormController {
|
||||||
RepositoryUsername: '',
|
RepositoryUsername: '',
|
||||||
RepositoryPassword: '',
|
RepositoryPassword: '',
|
||||||
Env: [],
|
Env: [],
|
||||||
|
PullImage: false,
|
||||||
Option: {
|
Option: {
|
||||||
Prune: false,
|
Prune: false,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue