feat(stacks): use source id to create git stacks [BE-13043] (#2870)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,17 +22,25 @@ import (
|
||||
)
|
||||
|
||||
type stackGitUpdatePayload struct {
|
||||
AutoUpdate *portainer.AutoUpdateSettings
|
||||
Env []portainer.Pair
|
||||
Prune bool
|
||||
RepositoryURL string
|
||||
ConfigFilePath string
|
||||
AdditionalFiles []string
|
||||
RepositoryReferenceName string
|
||||
AutoUpdate *portainer.AutoUpdateSettings
|
||||
Env []portainer.Pair
|
||||
Prune bool
|
||||
ConfigFilePath string
|
||||
AdditionalFiles []string
|
||||
RepositoryReferenceName string
|
||||
// SourceID references an existing Source for git credentials/URL.
|
||||
// When set, the inline URL and authentication fields are ignored.
|
||||
SourceID portainer.SourceID
|
||||
// Deprecated: use SourceID instead. URL of a Git repository hosting the Stack file.
|
||||
RepositoryURL string
|
||||
// Deprecated: use SourceID instead. Use basic authentication to clone the Git repository.
|
||||
RepositoryAuthentication bool
|
||||
RepositoryUsername string
|
||||
RepositoryPassword string
|
||||
TLSSkipVerify bool
|
||||
// Deprecated: use SourceID instead. Username used in basic authentication.
|
||||
RepositoryUsername string
|
||||
// Deprecated: use SourceID instead. Password used in basic authentication.
|
||||
RepositoryPassword string
|
||||
// Deprecated: use SourceID instead. Skip TLS verification when cloning the Git repository.
|
||||
TLSSkipVerify bool
|
||||
}
|
||||
|
||||
func (payload *stackGitUpdatePayload) Validate(r *http.Request) error {
|
||||
@@ -41,7 +49,7 @@ func (payload *stackGitUpdatePayload) Validate(r *http.Request) error {
|
||||
|
||||
// @id StackUpdateGit
|
||||
// @summary Update a stack's Git configs
|
||||
// @description Update the Git settings in a stack, e.g., RepositoryReferenceName and AutoUpdate
|
||||
// @description Update the Git settings in a stack, e.g., RepositoryReferenceName and AutoUpdate. When SourceID is set, URL/auth/TLS are taken from the referenced Source.
|
||||
// @description **Access policy**: authenticated
|
||||
// @tags stacks
|
||||
// @security ApiKeyAuth
|
||||
@@ -51,7 +59,7 @@ func (payload *stackGitUpdatePayload) Validate(r *http.Request) error {
|
||||
// @param id path int true "Stack identifier"
|
||||
// @param endpointId query int false "Stacks created before version 1.18.0 might not have an associated environment(endpoint) identifier. Use this optional parameter to set the environment(endpoint) identifier used by the stack."
|
||||
// @param body body stackGitUpdatePayload true "Git configs for pull and redeploy a stack"
|
||||
// @success 200 {object} portainer.Stack "Success"
|
||||
// @success 200 {object} stackResponse "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 403 "Permission denied"
|
||||
// @failure 404 "Not found"
|
||||
@@ -152,6 +160,7 @@ func (handler *Handler) stackUpdateGit(w http.ResponseWriter, r *http.Request) *
|
||||
deployments.StopAutoupdate(stack.ID, stack.AutoUpdate.JobID, handler.Scheduler)
|
||||
}
|
||||
|
||||
// Record the current git config as the deployment baseline if it was never set (legacy stacks).
|
||||
if stack.CurrentDeploymentInfo == nil {
|
||||
stack.CurrentDeploymentInfo = &portainer.StackDeploymentInfo{
|
||||
RepositoryURL: gitConfig.URL,
|
||||
@@ -159,15 +168,12 @@ func (handler *Handler) stackUpdateGit(w http.ResponseWriter, r *http.Request) *
|
||||
ConfigFilePath: gitConfig.ConfigFilePath,
|
||||
AdditionalFiles: stack.AdditionalFiles,
|
||||
ConfigHash: gitConfig.ConfigHash,
|
||||
SourceID: sourceID,
|
||||
}
|
||||
}
|
||||
|
||||
// Update gitConfig based on payload; the updated config is saved to Source (not stack.GitConfig).
|
||||
gitConfig.ReferenceName = payload.RepositoryReferenceName
|
||||
gitConfig.TLSSkipVerify = payload.TLSSkipVerify
|
||||
if payload.RepositoryURL != "" {
|
||||
gitConfig.URL = payload.RepositoryURL
|
||||
}
|
||||
if payload.ConfigFilePath != "" {
|
||||
gitConfig.ConfigFilePath = payload.ConfigFilePath
|
||||
}
|
||||
@@ -186,32 +192,48 @@ func (handler *Handler) stackUpdateGit(w http.ResponseWriter, r *http.Request) *
|
||||
stack.Option = &portainer.StackOption{Prune: payload.Prune}
|
||||
}
|
||||
|
||||
if payload.RepositoryAuthentication {
|
||||
password := payload.RepositoryPassword
|
||||
|
||||
// When the existing stack is using the custom username/password and the password is not updated,
|
||||
// the stack should keep using the saved username/password
|
||||
if password == "" && gitConfig.Authentication != nil {
|
||||
password = gitConfig.Authentication.Password
|
||||
if payload.SourceID != 0 {
|
||||
src, httpErr := validateSourceForStack(handler.DataStore, payload.SourceID)
|
||||
if httpErr != nil {
|
||||
return httpErr
|
||||
}
|
||||
|
||||
gitConfig.Authentication = &gittypes.GitAuthentication{
|
||||
Username: payload.RepositoryUsername,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
if _, err := handler.GitService.LatestCommitID(
|
||||
context.TODO(),
|
||||
gitConfig.URL,
|
||||
gitConfig.ReferenceName,
|
||||
gitConfig.Authentication.Username,
|
||||
gitConfig.Authentication.Password,
|
||||
gitConfig.TLSSkipVerify,
|
||||
); err != nil {
|
||||
return httperror.InternalServerError("Unable to fetch git repository", err)
|
||||
if src.Git == nil {
|
||||
return httperror.BadRequest("Source has no git configuration", errors.New("source has no git config"))
|
||||
}
|
||||
} else {
|
||||
gitConfig.Authentication = nil
|
||||
gitConfig.TLSSkipVerify = payload.TLSSkipVerify
|
||||
if payload.RepositoryURL != "" {
|
||||
gitConfig.URL = payload.RepositoryURL
|
||||
}
|
||||
|
||||
if payload.RepositoryAuthentication {
|
||||
password := payload.RepositoryPassword
|
||||
|
||||
// When the existing stack is using the custom username/password and the password is not updated,
|
||||
// the stack should keep using the saved username/password
|
||||
if password == "" && gitConfig.Authentication != nil {
|
||||
password = gitConfig.Authentication.Password
|
||||
}
|
||||
|
||||
gitConfig.Authentication = &gittypes.GitAuthentication{
|
||||
Username: payload.RepositoryUsername,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
if _, err := handler.GitService.LatestCommitID(
|
||||
context.TODO(),
|
||||
gitConfig.URL,
|
||||
gitConfig.ReferenceName,
|
||||
gitConfig.Authentication.Username,
|
||||
gitConfig.Authentication.Password,
|
||||
gitConfig.TLSSkipVerify,
|
||||
); err != nil {
|
||||
return httperror.InternalServerError("Unable to fetch git repository", err)
|
||||
}
|
||||
} else {
|
||||
gitConfig.Authentication = nil
|
||||
}
|
||||
}
|
||||
|
||||
if payload.AutoUpdate != nil && payload.AutoUpdate.Interval != "" {
|
||||
@@ -222,18 +244,20 @@ func (handler *Handler) stackUpdateGit(w http.ResponseWriter, r *http.Request) *
|
||||
}
|
||||
}
|
||||
|
||||
// Save the updated stack and git config to DB.
|
||||
var resp *stackResponse
|
||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
if err := tx.Stack().Update(stack.ID, stack); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := saveStackGitConfig(tx, stack.WorkflowID, stack.ID, sourceID, gitConfig); err != nil {
|
||||
if err := saveStackGitConfig(tx, stack.WorkflowID, stack.ID, sourceID, payload.SourceID, gitConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
return fillStackGitConfig(tx, stack)
|
||||
var err error
|
||||
resp, err = newStackResponse(tx, stack)
|
||||
return err
|
||||
}); err != nil {
|
||||
return httperror.InternalServerError("Unable to persist the stack changes inside the database", err)
|
||||
}
|
||||
|
||||
return response.JSON(w, stack)
|
||||
return response.JSON(w, resp)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user