@ -32,6 +32,7 @@ func (payload *updateComposeStackPayload) Validate(r *http.Request) error {
if govalidator . IsNull ( payload . StackFileContent ) {
if govalidator . IsNull ( payload . StackFileContent ) {
return errors . New ( "Invalid stack file content" )
return errors . New ( "Invalid stack file content" )
}
}
return nil
return nil
}
}
@ -50,6 +51,7 @@ func (payload *updateSwarmStackPayload) Validate(r *http.Request) error {
if govalidator . IsNull ( payload . StackFileContent ) {
if govalidator . IsNull ( payload . StackFileContent ) {
return errors . New ( "Invalid stack file content" )
return errors . New ( "Invalid stack file content" )
}
}
return nil
return nil
}
}
@ -102,8 +104,7 @@ func (handler *Handler) stackUpdate(w http.ResponseWriter, r *http.Request) *htt
return httperror . InternalServerError ( "Unable to find the environment associated to the stack inside the database" , err )
return httperror . InternalServerError ( "Unable to find the environment associated to the stack inside the database" , err )
}
}
err = handler . requestBouncer . AuthorizedEndpointOperation ( r , endpoint )
if err := handler . requestBouncer . AuthorizedEndpointOperation ( r , endpoint ) ; err != nil {
if err != nil {
return httperror . Forbidden ( "Permission denied to access environment" , err )
return httperror . Forbidden ( "Permission denied to access environment" , err )
}
}
@ -114,45 +115,40 @@ func (handler *Handler) stackUpdate(w http.ResponseWriter, r *http.Request) *htt
//only check resource control when it is a DockerSwarmStack or a DockerComposeStack
//only check resource control when it is a DockerSwarmStack or a DockerComposeStack
if stack . Type == portainer . DockerSwarmStack || stack . Type == portainer . DockerComposeStack {
if stack . Type == portainer . DockerSwarmStack || stack . Type == portainer . DockerComposeStack {
resourceControl , err := handler . DataStore . ResourceControl ( ) . ResourceControlByResourceIDAndType ( stackutils . ResourceControlID ( stack . EndpointID , stack . Name ) , portainer . StackResourceControl )
resourceControl , err := handler . DataStore . ResourceControl ( ) . ResourceControlByResourceIDAndType ( stackutils . ResourceControlID ( stack . EndpointID , stack . Name ) , portainer . StackResourceControl )
if err != nil {
if err != nil {
return httperror . InternalServerError ( "Unable to retrieve a resource control associated to the stack" , err )
return httperror . InternalServerError ( "Unable to retrieve a resource control associated to the stack" , err )
}
}
access , err := handler . userCanAccessStack ( securityContext , endpoint . ID , resourceControl )
if access , err := handler . userCanAccessStack ( securityContext , endpoint . ID , resourceControl ) ; err != nil {
if err != nil {
return httperror . InternalServerError ( "Unable to verify user authorizations to validate stack access" , err )
return httperror . InternalServerError ( "Unable to verify user authorizations to validate stack access" , err )
}
} else if ! access {
if ! access {
return httperror . Forbidden ( "Access denied to resource" , httperrors . ErrResourceAccessDenied )
return httperror . Forbidden ( "Access denied to resource" , httperrors . ErrResourceAccessDenied )
}
}
}
}
canManage , err := handler . userCanManageStacks ( securityContext , endpoint )
if canManage , err := handler . userCanManageStacks ( securityContext , endpoint ) ; err != nil {
if err != nil {
return httperror . InternalServerError ( "Unable to verify user authorizations to validate stack deletion" , err )
return httperror . InternalServerError ( "Unable to verify user authorizations to validate stack deletion" , err )
}
} else if ! canManage {
if ! canManage {
errMsg := "Stack editing is disabled for non-admin users"
errMsg := "Stack editing is disabled for non-admin users"
return httperror . Forbidden ( errMsg , errors . New ( errMsg ) )
return httperror . Forbidden ( errMsg , errors . New ( errMsg ) )
}
}
updateError := handler . updateAndDeployStack ( r , stack , endpoint )
if err := handler . updateAndDeployStack ( r , stack , endpoint ) ; err != nil {
if updateError != nil {
return err
return updateError
}
}
user , err := handler . DataStore . User ( ) . Read ( securityContext . UserID )
user , err := handler . DataStore . User ( ) . Read ( securityContext . UserID )
if err != nil {
if err != nil {
return httperror . BadRequest ( "Cannot find context user" , errors . Wrap ( err , "failed to fetch the user" ) )
return httperror . BadRequest ( "Cannot find context user" , errors . Wrap ( err , "failed to fetch the user" ) )
}
}
stack . UpdatedBy = user . Username
stack . UpdatedBy = user . Username
stack . UpdateDate = time . Now ( ) . Unix ( )
stack . UpdateDate = time . Now ( ) . Unix ( )
stack . Status = portainer . StackStatusActive
stack . Status = portainer . StackStatusActive
err = handler . DataStore . Stack ( ) . Update ( stack . ID , stack )
if err := handler . DataStore . Stack ( ) . Update ( stack . ID , stack ) ; err != nil {
if err != nil {
return httperror . InternalServerError ( "Unable to persist the stack changes inside the database" , err )
return httperror . InternalServerError ( "Unable to persist the stack changes inside the database" , err )
}
}
@ -165,19 +161,20 @@ func (handler *Handler) stackUpdate(w http.ResponseWriter, r *http.Request) *htt
}
}
func ( handler * Handler ) updateAndDeployStack ( r * http . Request , stack * portainer . Stack , endpoint * portainer . Endpoint ) * httperror . HandlerError {
func ( handler * Handler ) updateAndDeployStack ( r * http . Request , stack * portainer . Stack , endpoint * portainer . Endpoint ) * httperror . HandlerError {
if stack . Type == portainer . DockerSwarmStack {
switch stack . Type {
case portainer . DockerSwarmStack :
stack . Name = handler . SwarmStackManager . NormalizeStackName ( stack . Name )
stack . Name = handler . SwarmStackManager . NormalizeStackName ( stack . Name )
return handler . updateSwarmStack ( r , stack , endpoint )
return handler . updateSwarmStack ( r , stack , endpoint )
} else if stack . Type == portainer . DockerComposeStack {
case portainer . DockerComposeStack :
stack . Name = handler . ComposeStackManager . NormalizeStackName ( stack . Name )
stack . Name = handler . ComposeStackManager . NormalizeStackName ( stack . Name )
return handler . updateComposeStack ( r , stack , endpoint )
return handler . updateComposeStack ( r , stack , endpoint )
} else if stack . Type == portainer . KubernetesStack {
case portainer . KubernetesStack :
return handler . updateKubernetesStack ( r , stack , endpoint )
return handler . updateKubernetesStack ( r , stack , endpoint )
} else {
return httperror . InternalServerError ( "Unsupported stack" , errors . Errorf ( "unsupported stack type: %v" , stack . Type ) )
}
}
return httperror . InternalServerError ( "Unsupported stack" , errors . Errorf ( "unsupported stack type: %v" , stack . Type ) )
}
}
func ( handler * Handler ) updateComposeStack ( r * http . Request , stack * portainer . Stack , endpoint * portainer . Endpoint ) * httperror . HandlerError {
func ( handler * Handler ) updateComposeStack ( r * http . Request , stack * portainer . Stack , endpoint * portainer . Endpoint ) * httperror . HandlerError {
@ -191,8 +188,7 @@ func (handler *Handler) updateComposeStack(r *http.Request, stack *portainer.Sta
}
}
var payload updateComposeStackPayload
var payload updateComposeStackPayload
err := request . DecodeAndValidateJSONPayload ( r , & payload )
if err := request . DecodeAndValidateJSONPayload ( r , & payload ) ; err != nil {
if err != nil {
return httperror . BadRequest ( "Invalid request payload" , err )
return httperror . BadRequest ( "Invalid request payload" , err )
}
}
@ -204,8 +200,7 @@ func (handler *Handler) updateComposeStack(r *http.Request, stack *portainer.Sta
}
}
stackFolder := strconv . Itoa ( int ( stack . ID ) )
stackFolder := strconv . Itoa ( int ( stack . ID ) )
_ , err = handler . FileService . UpdateStoreStackFileFromBytes ( stackFolder , stack . EntryPoint , [ ] byte ( payload . StackFileContent ) )
if _ , err := handler . FileService . UpdateStoreStackFileFromBytes ( stackFolder , stack . EntryPoint , [ ] byte ( payload . StackFileContent ) ) ; err != nil {
if err != nil {
if rollbackErr := handler . FileService . RollbackStackFile ( stackFolder , stack . EntryPoint ) ; rollbackErr != nil {
if rollbackErr := handler . FileService . RollbackStackFile ( stackFolder , stack . EntryPoint ) ; rollbackErr != nil {
log . Warn ( ) . Err ( rollbackErr ) . Msg ( "rollback stack file error" )
log . Warn ( ) . Err ( rollbackErr ) . Msg ( "rollback stack file error" )
}
}
@ -236,8 +231,7 @@ func (handler *Handler) updateComposeStack(r *http.Request, stack *portainer.Sta
}
}
// Deploy the stack
// Deploy the stack
err = composeDeploymentConfig . Deploy ( )
if err := composeDeploymentConfig . Deploy ( ) ; err != nil {
if err != nil {
if rollbackErr := handler . FileService . RollbackStackFile ( stackFolder , stack . EntryPoint ) ; rollbackErr != nil {
if rollbackErr := handler . FileService . RollbackStackFile ( stackFolder , stack . EntryPoint ) ; rollbackErr != nil {
log . Warn ( ) . Err ( rollbackErr ) . Msg ( "rollback stack file error" )
log . Warn ( ) . Err ( rollbackErr ) . Msg ( "rollback stack file error" )
}
}
@ -261,8 +255,7 @@ func (handler *Handler) updateSwarmStack(r *http.Request, stack *portainer.Stack
}
}
var payload updateSwarmStackPayload
var payload updateSwarmStackPayload
err := request . DecodeAndValidateJSONPayload ( r , & payload )
if err := request . DecodeAndValidateJSONPayload ( r , & payload ) ; err != nil {
if err != nil {
return httperror . BadRequest ( "Invalid request payload" , err )
return httperror . BadRequest ( "Invalid request payload" , err )
}
}
@ -274,8 +267,7 @@ func (handler *Handler) updateSwarmStack(r *http.Request, stack *portainer.Stack
}
}
stackFolder := strconv . Itoa ( int ( stack . ID ) )
stackFolder := strconv . Itoa ( int ( stack . ID ) )
_ , err = handler . FileService . UpdateStoreStackFileFromBytes ( stackFolder , stack . EntryPoint , [ ] byte ( payload . StackFileContent ) )
if _ , err := handler . FileService . UpdateStoreStackFileFromBytes ( stackFolder , stack . EntryPoint , [ ] byte ( payload . StackFileContent ) ) ; err != nil {
if err != nil {
if rollbackErr := handler . FileService . RollbackStackFile ( stackFolder , stack . EntryPoint ) ; rollbackErr != nil {
if rollbackErr := handler . FileService . RollbackStackFile ( stackFolder , stack . EntryPoint ) ; rollbackErr != nil {
log . Warn ( ) . Err ( rollbackErr ) . Msg ( "rollback stack file error" )
log . Warn ( ) . Err ( rollbackErr ) . Msg ( "rollback stack file error" )
}
}
@ -306,8 +298,7 @@ func (handler *Handler) updateSwarmStack(r *http.Request, stack *portainer.Stack
}
}
// Deploy the stack
// Deploy the stack
err = swarmDeploymentConfig . Deploy ( )
if err := swarmDeploymentConfig . Deploy ( ) ; err != nil {
if err != nil {
if rollbackErr := handler . FileService . RollbackStackFile ( stackFolder , stack . EntryPoint ) ; rollbackErr != nil {
if rollbackErr := handler . FileService . RollbackStackFile ( stackFolder , stack . EntryPoint ) ; rollbackErr != nil {
log . Warn ( ) . Err ( rollbackErr ) . Msg ( "rollback stack file error" )
log . Warn ( ) . Err ( rollbackErr ) . Msg ( "rollback stack file error" )
}
}