1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-24 15:59:41 +02:00

refactor(stacks): extract auto update logic [EE-4945] (#8545)

This commit is contained in:
Chaim Lev-Ari 2023-03-02 17:07:50 +02:00 committed by GitHub
parent 085381e6fc
commit 6918da2414
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 410 additions and 166 deletions

94
api/git/update/update.go Normal file
View file

@ -0,0 +1,94 @@
package update
import (
"strings"
"github.com/pkg/errors"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/git"
gittypes "github.com/portainer/portainer/api/git/types"
"github.com/rs/zerolog/log"
)
// UpdateGitObject updates a git object based on its config
func UpdateGitObject(gitService portainer.GitService, dataStore dataservices.DataStore, objId string, gitConfig *gittypes.RepoConfig, autoUpdateConfig *portainer.AutoUpdateSettings, projectPath string) (bool, string, error) {
if gitConfig == nil {
return false, "", nil
}
log.Debug().
Str("url", gitConfig.URL).
Str("ref", gitConfig.ReferenceName).
Str("object", objId).
Msg("the object has a git config, try to poll from git repository")
username, password, err := git.GetCredentials(gitConfig.Authentication)
if err != nil {
return false, "", errors.WithMessagef(err, "failed to get credentials for %v", objId)
}
newHash, err := gitService.LatestCommitID(gitConfig.URL, gitConfig.ReferenceName, username, password)
if err != nil {
return false, "", errors.WithMessagef(err, "failed to fetch latest commit id of %v", objId)
}
hashChanged := !strings.EqualFold(newHash, string(gitConfig.ConfigHash))
forceUpdate := autoUpdateConfig != nil && autoUpdateConfig.ForceUpdate
if !hashChanged && !forceUpdate {
log.Debug().
Str("hash", newHash).
Str("url", gitConfig.URL).
Str("ref", gitConfig.ReferenceName).
Str("object", objId).
Msg("git repo is up to date")
return false, newHash, nil
}
cloneParams := &cloneRepositoryParameters{
url: gitConfig.URL,
ref: gitConfig.ReferenceName,
toDir: projectPath,
}
if gitConfig.Authentication != nil {
cloneParams.auth = &gitAuth{
username: username,
password: password,
}
}
if err := cloneGitRepository(gitService, cloneParams); err != nil {
return false, "", errors.WithMessagef(err, "failed to do a fresh clone of %v", objId)
}
log.Debug().
Str("hash", newHash).
Str("url", gitConfig.URL).
Str("ref", gitConfig.ReferenceName).
Str("object", objId).
Msg("git repo cloned updated")
return true, newHash, nil
}
type cloneRepositoryParameters struct {
url string
ref string
toDir string
auth *gitAuth
}
type gitAuth struct {
username string
password string
}
func cloneGitRepository(gitService portainer.GitService, cloneParams *cloneRepositoryParameters) error {
if cloneParams.auth != nil {
return gitService.CloneRepository(cloneParams.toDir, cloneParams.url, cloneParams.ref, cloneParams.auth.username, cloneParams.auth.password)
}
return gitService.CloneRepository(cloneParams.toDir, cloneParams.url, cloneParams.ref, "", "")
}

View file

@ -0,0 +1,31 @@
package update
import (
"time"
"github.com/asaskevich/govalidator"
portainer "github.com/portainer/portainer/api"
httperrors "github.com/portainer/portainer/api/http/errors"
)
func ValidateAutoUpdateSettings(autoUpdate *portainer.AutoUpdateSettings) error {
if autoUpdate == nil {
return nil
}
if autoUpdate.Webhook == "" && autoUpdate.Interval == "" {
return httperrors.NewInvalidPayloadError("Webhook or Interval must be provided")
}
if autoUpdate.Webhook != "" && !govalidator.IsUUID(autoUpdate.Webhook) {
return httperrors.NewInvalidPayloadError("invalid Webhook format")
}
if autoUpdate.Interval != "" {
if _, err := time.ParseDuration(autoUpdate.Interval); err != nil {
return httperrors.NewInvalidPayloadError("invalid Interval format")
}
}
return nil
}

View file

@ -0,0 +1,42 @@
package update
import (
"testing"
portainer "github.com/portainer/portainer/api"
"github.com/stretchr/testify/assert"
)
func Test_ValidateAutoUpdate(t *testing.T) {
tests := []struct {
name string
value *portainer.AutoUpdateSettings
wantErr bool
}{
{
name: "webhook is not a valid UUID",
value: &portainer.AutoUpdateSettings{Webhook: "fake-webhook"},
wantErr: true,
},
{
name: "incorrect interval value",
value: &portainer.AutoUpdateSettings{Interval: "1dd2hh3mm"},
wantErr: true,
},
{
name: "valid auto update",
value: &portainer.AutoUpdateSettings{
Webhook: "8dce8c2f-9ca1-482b-ad20-271e86536ada",
Interval: "5h30m40s10ms",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateAutoUpdateSettings(tt.value)
assert.Equalf(t, tt.wantErr, err != nil, "received %+v", err)
})
}
}