Compare commits
30 Commits
2.16.2
...
feat/fedex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eab395555c | ||
|
|
ab627107ad | ||
|
|
cdfa0f8f34 | ||
|
|
a304b9c7ef | ||
|
|
7a6ff10268 | ||
|
|
fd91de3571 | ||
|
|
ab3a6f402e | ||
|
|
d3edb7ebd5 | ||
|
|
c23b8b2816 | ||
|
|
724f1f63b7 | ||
|
|
c6ae8467c0 | ||
|
|
56087bcbb3 | ||
|
|
315c1c7e1e | ||
|
|
819dc4d561 | ||
|
|
380a64d546 | ||
|
|
6429546462 | ||
|
|
ebfb71da05 | ||
|
|
ae0b9b1e30 | ||
|
|
e9de484c3e | ||
|
|
83a1ce9d2a | ||
|
|
66fd039933 | ||
|
|
1722257d68 | ||
|
|
7d8b037761 | ||
|
|
cd52e04a5a | ||
|
|
a0fa64781a | ||
|
|
43e3cb476b | ||
|
|
a1a88eb5e4 | ||
|
|
cb79dc18f8 | ||
|
|
e9384a6987 | ||
|
|
90a0e6fe35 |
@@ -20,7 +20,7 @@ Portainer CE is updated regularly. We aim to do an update release every couple o
|
||||
|
||||
## Getting started
|
||||
|
||||
- [Deploy Portainer](https://docs.portainer.io/v/ce-2.9/start/install)
|
||||
- [Deploy Portainer](https://docs.portainer.io/start/install)
|
||||
- [Documentation](https://documentation.portainer.io)
|
||||
- [Contribute to the project](https://documentation.portainer.io/contributing/instructions/)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ type Monitor struct {
|
||||
adminInitDisabled bool
|
||||
}
|
||||
|
||||
// New creates a monitor that when started will wait for the timeout duration and then sends the timeout signal to disable the application
|
||||
// New creates a monitor that when started will wait for the timeout duration and then shutdown the application unless it has been initialized.
|
||||
func New(timeout time.Duration, datastore dataservices.DataStore, shutdownCtx context.Context) *Monitor {
|
||||
return &Monitor{
|
||||
timeout: timeout,
|
||||
@@ -105,12 +105,10 @@ func (m *Monitor) WasInstanceDisabled() bool {
|
||||
// Otherwise, it will pass through the request to next
|
||||
func (m *Monitor) WithRedirect(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if m.WasInstanceDisabled() {
|
||||
if strings.HasPrefix(r.RequestURI, "/api") && r.RequestURI != "/api/status" && r.RequestURI != "/api/settings/public" {
|
||||
w.Header().Set("redirect-reason", RedirectReasonAdminInitTimeout)
|
||||
httperror.WriteError(w, http.StatusSeeOther, "Administrator initialization timeout", nil)
|
||||
return
|
||||
}
|
||||
if m.WasInstanceDisabled() && strings.HasPrefix(r.RequestURI, "/api") && r.RequestURI != "/api/status" && r.RequestURI != "/api/settings/public" {
|
||||
w.Header().Set("redirect-reason", RedirectReasonAdminInitTimeout)
|
||||
httperror.WriteError(w, http.StatusSeeOther, "Administrator initialization timeout", nil)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const portainerAPIKeyPrefix = "ptr_"
|
||||
|
||||
@@ -2,6 +2,7 @@ package apikey
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -170,11 +171,9 @@ func Test_UpdateAPIKey(t *testing.T) {
|
||||
_, apiKeyGot, err := service.GetDigestUserAndKey(apiKey.Digest)
|
||||
is.NoError(err)
|
||||
|
||||
log.Debug().Msgf("%+v", apiKey)
|
||||
log.Debug().Msgf("%+v", apiKeyGot)
|
||||
log.Debug().Str("wanted", fmt.Sprintf("%+v", apiKey)).Str("got", fmt.Sprintf("%+v", apiKeyGot)).Msg("")
|
||||
|
||||
is.Equal(apiKey.LastUsed, apiKeyGot.LastUsed)
|
||||
|
||||
})
|
||||
|
||||
t.Run("Successfully updates api-key in cache upon api-key update", func(t *testing.T) {
|
||||
|
||||
@@ -1,110 +1,110 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
// import (
|
||||
// "fmt"
|
||||
// "os"
|
||||
// "path"
|
||||
// "path/filepath"
|
||||
// "time"
|
||||
|
||||
"github.com/portainer/portainer/api/archive"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/filesystem"
|
||||
"github.com/portainer/portainer/api/http/offlinegate"
|
||||
// "github.com/portainer/portainer/api/archive"
|
||||
// "github.com/portainer/portainer/api/crypto"
|
||||
// "github.com/portainer/portainer/api/dataservices"
|
||||
// "github.com/portainer/portainer/api/filesystem"
|
||||
// "github.com/portainer/portainer/api/http/offlinegate"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
// "github.com/pkg/errors"
|
||||
// "github.com/rs/zerolog/log"
|
||||
// )
|
||||
|
||||
const rwxr__r__ os.FileMode = 0744
|
||||
// const rwxr__r__ os.FileMode = 0744
|
||||
|
||||
var filesToBackup = []string{
|
||||
"certs",
|
||||
"compose",
|
||||
"config.json",
|
||||
"custom_templates",
|
||||
"edge_jobs",
|
||||
"edge_stacks",
|
||||
"extensions",
|
||||
"portainer.key",
|
||||
"portainer.pub",
|
||||
"tls",
|
||||
}
|
||||
// var filesToBackup = []string{
|
||||
// "certs",
|
||||
// "compose",
|
||||
// "config.json",
|
||||
// "custom_templates",
|
||||
// "edge_jobs",
|
||||
// "edge_stacks",
|
||||
// "extensions",
|
||||
// "portainer.key",
|
||||
// "portainer.pub",
|
||||
// "tls",
|
||||
// }
|
||||
|
||||
// Creates a tar.gz system archive and encrypts it if password is not empty. Returns a path to the archive file.
|
||||
func CreateBackupArchive(password string, gate *offlinegate.OfflineGate, datastore dataservices.DataStore, filestorePath string) (string, error) {
|
||||
unlock := gate.Lock()
|
||||
defer unlock()
|
||||
// // Creates a tar.gz system archive and encrypts it if password is not empty. Returns a path to the archive file.
|
||||
// func CreateBackupArchive(password string, gate *offlinegate.OfflineGate, datastore dataservices.DataStore, filestorePath string) (string, error) {
|
||||
// unlock := gate.Lock()
|
||||
// defer unlock()
|
||||
|
||||
backupDirPath := filepath.Join(filestorePath, "backup", time.Now().Format("2006-01-02_15-04-05"))
|
||||
if err := os.MkdirAll(backupDirPath, rwxr__r__); err != nil {
|
||||
return "", errors.Wrap(err, "Failed to create backup dir")
|
||||
}
|
||||
// backupDirPath := filepath.Join(filestorePath, "backup", time.Now().Format("2006-01-02_15-04-05"))
|
||||
// if err := os.MkdirAll(backupDirPath, rwxr__r__); err != nil {
|
||||
// return "", errors.Wrap(err, "Failed to create backup dir")
|
||||
// }
|
||||
|
||||
{
|
||||
// new export
|
||||
exportFilename := path.Join(backupDirPath, fmt.Sprintf("export-%d.json", time.Now().Unix()))
|
||||
// {
|
||||
// // new export
|
||||
// exportFilename := path.Join(backupDirPath, fmt.Sprintf("export-%d.json", time.Now().Unix()))
|
||||
|
||||
err := datastore.Export(exportFilename)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("filename", exportFilename).Msg("failed to export")
|
||||
} else {
|
||||
log.Debug().Str("filename", exportFilename).Msg("file exported")
|
||||
}
|
||||
}
|
||||
// err := datastore.Export(exportFilename)
|
||||
// if err != nil {
|
||||
// log.Error().Err(err).Str("filename", exportFilename).Msg("failed to export")
|
||||
// } else {
|
||||
// log.Debug().Str("filename", exportFilename).Msg("file exported")
|
||||
// }
|
||||
// }
|
||||
|
||||
if err := backupDb(backupDirPath, datastore); err != nil {
|
||||
return "", errors.Wrap(err, "Failed to backup database")
|
||||
}
|
||||
// if err := backupDb(backupDirPath, datastore); err != nil {
|
||||
// return "", errors.Wrap(err, "Failed to backup database")
|
||||
// }
|
||||
|
||||
for _, filename := range filesToBackup {
|
||||
err := filesystem.CopyPath(filepath.Join(filestorePath, filename), backupDirPath)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "Failed to create backup file")
|
||||
}
|
||||
}
|
||||
// for _, filename := range filesToBackup {
|
||||
// err := filesystem.CopyPath(filepath.Join(filestorePath, filename), backupDirPath)
|
||||
// if err != nil {
|
||||
// return "", errors.Wrap(err, "Failed to create backup file")
|
||||
// }
|
||||
// }
|
||||
|
||||
archivePath, err := archive.TarGzDir(backupDirPath)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "Failed to make an archive")
|
||||
}
|
||||
// archivePath, err := archive.TarGzDir(backupDirPath)
|
||||
// if err != nil {
|
||||
// return "", errors.Wrap(err, "Failed to make an archive")
|
||||
// }
|
||||
|
||||
if password != "" {
|
||||
archivePath, err = encrypt(archivePath, password)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "Failed to encrypt backup with the password")
|
||||
}
|
||||
}
|
||||
// if password != "" {
|
||||
// archivePath, err = encrypt(archivePath, password)
|
||||
// if err != nil {
|
||||
// return "", errors.Wrap(err, "Failed to encrypt backup with the password")
|
||||
// }
|
||||
// }
|
||||
|
||||
return archivePath, nil
|
||||
}
|
||||
// return archivePath, nil
|
||||
// }
|
||||
|
||||
func backupDb(backupDirPath string, datastore dataservices.DataStore) error {
|
||||
backupWriter, err := os.Create(filepath.Join(backupDirPath, "portainer.db"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = datastore.BackupTo(backupWriter); err != nil {
|
||||
return err
|
||||
}
|
||||
return backupWriter.Close()
|
||||
}
|
||||
// func backupDb(backupDirPath string, datastore dataservices.DataStore) error {
|
||||
// backupWriter, err := os.Create(filepath.Join(backupDirPath, "portainer.db"))
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err = datastore.BackupTo(backupWriter); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// return backupWriter.Close()
|
||||
// }
|
||||
|
||||
func encrypt(path string, passphrase string) (string, error) {
|
||||
in, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer in.Close()
|
||||
// func encrypt(path string, passphrase string) (string, error) {
|
||||
// in, err := os.Open(path)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// defer in.Close()
|
||||
|
||||
outFileName := fmt.Sprintf("%s.encrypted", path)
|
||||
out, err := os.Create(outFileName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// outFileName := fmt.Sprintf("%s.encrypted", path)
|
||||
// out, err := os.Create(outFileName)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
|
||||
err = crypto.AesEncrypt(in, out, []byte(passphrase))
|
||||
// err = crypto.AesEncrypt(in, out, []byte(passphrase))
|
||||
|
||||
return outFileName, err
|
||||
}
|
||||
// return outFileName, err
|
||||
// }
|
||||
|
||||
@@ -1,85 +1,85 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
// import (
|
||||
// "context"
|
||||
// "io"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/api/archive"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/filesystem"
|
||||
"github.com/portainer/portainer/api/http/offlinegate"
|
||||
)
|
||||
// "github.com/pkg/errors"
|
||||
// "github.com/portainer/portainer/api/archive"
|
||||
// "github.com/portainer/portainer/api/crypto"
|
||||
// "github.com/portainer/portainer/api/dataservices"
|
||||
// "github.com/portainer/portainer/api/filesystem"
|
||||
// "github.com/portainer/portainer/api/http/offlinegate"
|
||||
// )
|
||||
|
||||
var filesToRestore = append(filesToBackup, "portainer.db")
|
||||
// var filesToRestore = append(filesToBackup, "portainer.db")
|
||||
|
||||
// Restores system state from backup archive, will trigger system shutdown, when finished.
|
||||
func RestoreArchive(archive io.Reader, password string, filestorePath string, gate *offlinegate.OfflineGate, datastore dataservices.DataStore, shutdownTrigger context.CancelFunc) error {
|
||||
var err error
|
||||
if password != "" {
|
||||
archive, err = decrypt(archive, password)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to decrypt the archive")
|
||||
}
|
||||
}
|
||||
// // Restores system state from backup archive, will trigger system shutdown, when finished.
|
||||
// func RestoreArchive(archive io.Reader, password string, filestorePath string, gate *offlinegate.OfflineGate, datastore dataservices.DataStore, shutdownTrigger context.CancelFunc) error {
|
||||
// var err error
|
||||
// if password != "" {
|
||||
// archive, err = decrypt(archive, password)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "failed to decrypt the archive")
|
||||
// }
|
||||
// }
|
||||
|
||||
restorePath := filepath.Join(filestorePath, "restore", time.Now().Format("20060102150405"))
|
||||
defer os.RemoveAll(filepath.Dir(restorePath))
|
||||
// restorePath := filepath.Join(filestorePath, "restore", time.Now().Format("20060102150405"))
|
||||
// defer os.RemoveAll(filepath.Dir(restorePath))
|
||||
|
||||
err = extractArchive(archive, restorePath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot extract files from the archive. Please ensure the password is correct and try again")
|
||||
}
|
||||
// err = extractArchive(archive, restorePath)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "cannot extract files from the archive. Please ensure the password is correct and try again")
|
||||
// }
|
||||
|
||||
unlock := gate.Lock()
|
||||
defer unlock()
|
||||
// unlock := gate.Lock()
|
||||
// defer unlock()
|
||||
|
||||
if err = datastore.Close(); err != nil {
|
||||
return errors.Wrap(err, "Failed to stop db")
|
||||
}
|
||||
// if err = datastore.Close(); err != nil {
|
||||
// return errors.Wrap(err, "Failed to stop db")
|
||||
// }
|
||||
|
||||
if err = restoreFiles(restorePath, filestorePath); err != nil {
|
||||
return errors.Wrap(err, "failed to restore the system state")
|
||||
}
|
||||
// if err = restoreFiles(restorePath, filestorePath); err != nil {
|
||||
// return errors.Wrap(err, "failed to restore the system state")
|
||||
// }
|
||||
|
||||
shutdownTrigger()
|
||||
return nil
|
||||
}
|
||||
// shutdownTrigger()
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func decrypt(r io.Reader, password string) (io.Reader, error) {
|
||||
return crypto.AesDecrypt(r, []byte(password))
|
||||
}
|
||||
// func decrypt(r io.Reader, password string) (io.Reader, error) {
|
||||
// return crypto.AesDecrypt(r, []byte(password))
|
||||
// }
|
||||
|
||||
func extractArchive(r io.Reader, destinationDirPath string) error {
|
||||
return archive.ExtractTarGz(r, destinationDirPath)
|
||||
}
|
||||
// func extractArchive(r io.Reader, destinationDirPath string) error {
|
||||
// return archive.ExtractTarGz(r, destinationDirPath)
|
||||
// }
|
||||
|
||||
func restoreFiles(srcDir string, destinationDir string) error {
|
||||
for _, filename := range filesToRestore {
|
||||
err := filesystem.CopyPath(filepath.Join(srcDir, filename), destinationDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// func restoreFiles(srcDir string, destinationDir string) error {
|
||||
// for _, filename := range filesToRestore {
|
||||
// err := filesystem.CopyPath(filepath.Join(srcDir, filename), destinationDir)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO: This is very boltdb module specific once again due to the filename. Move to bolt module? Refactor for another day
|
||||
// // TODO: This is very boltdb module specific once again due to the filename. Move to bolt module? Refactor for another day
|
||||
|
||||
// Prevent the possibility of having both databases. Remove any default new instance
|
||||
os.Remove(filepath.Join(destinationDir, boltdb.DatabaseFileName))
|
||||
os.Remove(filepath.Join(destinationDir, boltdb.EncryptedDatabaseFileName))
|
||||
// // Prevent the possibility of having both databases. Remove any default new instance
|
||||
// os.Remove(filepath.Join(destinationDir, boltdb.DatabaseFileName))
|
||||
// os.Remove(filepath.Join(destinationDir, boltdb.EncryptedDatabaseFileName))
|
||||
|
||||
// Now copy the database. It'll be either portainer.db or portainer.edb
|
||||
// // Now copy the database. It'll be either portainer.db or portainer.edb
|
||||
|
||||
// Note: CopyPath does not return an error if the source file doesn't exist
|
||||
err := filesystem.CopyPath(filepath.Join(srcDir, boltdb.EncryptedDatabaseFileName), destinationDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// // Note: CopyPath does not return an error if the source file doesn't exist
|
||||
// err := filesystem.CopyPath(filepath.Join(srcDir, boltdb.EncryptedDatabaseFileName), destinationDir)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
return filesystem.CopyPath(filepath.Join(srcDir, boltdb.DatabaseFileName), destinationDir)
|
||||
}
|
||||
// // return filesystem.CopyPath(filepath.Join(srcDir, boltdb.DatabaseFileName), destinationDir)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
@@ -246,9 +246,8 @@ func (service *Service) checkTunnels() {
|
||||
err := service.snapshotEnvironment(endpointID, tunnel.Port)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Int("endpoint_id", int(endpointID)).Err(
|
||||
|
||||
err).
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Err(err).
|
||||
Msg("unable to snapshot Edge environment")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func importFromJson(fileService portainer.FileService, store *datastore.Store) {
|
||||
// EXPERIMENTAL - if used with an incomplete json file, it will fail, as we don't have a way to default the model values
|
||||
importFile := "/data/import.json"
|
||||
if exists, _ := fileService.FileExists(importFile); exists {
|
||||
if err := store.Import(importFile); err != nil {
|
||||
log.Error().Str("filename", importFile).Err(err).Msg("import failed")
|
||||
// TODO: should really rollback on failure, but then we have nothing.
|
||||
} else {
|
||||
log.Info().Str("filename", importFile).Msg("successfully imported the file to a new portainer database")
|
||||
}
|
||||
|
||||
// TODO: this is bad - its to ensure that any defaults that were broken in import, or migrations get set back to what we want
|
||||
// I also suspect that everything from "Init to Init" is potentially a migration
|
||||
err := store.Init()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing data store")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/portainer/libhelm"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
@@ -18,7 +17,6 @@ import (
|
||||
"github.com/portainer/portainer/api/cli"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/database"
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/portainer/portainer/api/demo"
|
||||
@@ -41,7 +39,7 @@ import (
|
||||
"github.com/portainer/portainer/api/ldap"
|
||||
"github.com/portainer/portainer/api/oauth"
|
||||
"github.com/portainer/portainer/api/scheduler"
|
||||
"github.com/portainer/portainer/api/stacks"
|
||||
"github.com/portainer/portainer/api/stacks/deployments"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -71,36 +69,28 @@ func initFileService(dataStorePath string) portainer.FileService {
|
||||
}
|
||||
|
||||
func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService portainer.FileService, shutdownCtx context.Context) dataservices.DataStore {
|
||||
connection, err := database.NewDatabase("boltdb", *flags.Data, secretKey)
|
||||
connection, err := database.NewDatabase("sqlite", *flags.Data, secretKey)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed creating database connection")
|
||||
}
|
||||
|
||||
if bconn, ok := connection.(*boltdb.DbConnection); ok {
|
||||
bconn.MaxBatchSize = *flags.MaxBatchSize
|
||||
bconn.MaxBatchDelay = *flags.MaxBatchDelay
|
||||
bconn.InitialMmapSize = *flags.InitialMmapSize
|
||||
} else {
|
||||
log.Fatal().Msg("failed creating database connection: expecting a boltdb database type but a different one was received")
|
||||
}
|
||||
|
||||
store := datastore.NewStore(*flags.Data, fileService, connection)
|
||||
isNew, err := store.Open()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed opening store")
|
||||
}
|
||||
|
||||
if *flags.Rollback {
|
||||
err := store.Rollback(false)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed rolling back")
|
||||
}
|
||||
// if *flags.Rollback {
|
||||
// err := store.Rollback(false)
|
||||
// if err != nil {
|
||||
// log.Fatal().Err(err).Msg("failed rolling back")
|
||||
// }
|
||||
|
||||
log.Info().Msg("exiting rollback")
|
||||
os.Exit(0)
|
||||
// log.Info().Msg("exiting rollback")
|
||||
// os.Exit(0)
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Init sets some defaults - it's basically a migration
|
||||
err = store.Init()
|
||||
@@ -117,17 +107,17 @@ func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService port
|
||||
log.Fatal().Err(err).Msg("failed updating settings from flags")
|
||||
}
|
||||
} else {
|
||||
storedVersion, err := store.VersionService.DBVersion()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failure during creation of new database")
|
||||
}
|
||||
// storedVersion, err := store.VersionService.DBVersion()
|
||||
// if err != nil {
|
||||
// log.Fatal().Err(err).Msg("failure during creation of new database")
|
||||
// }
|
||||
|
||||
if storedVersion != portainer.DBVersion {
|
||||
err = store.MigrateData()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed migration")
|
||||
}
|
||||
}
|
||||
// if storedVersion != portainer.DBVersion {
|
||||
// err = store.MigrateData()
|
||||
// if err != nil {
|
||||
// log.Fatal().Err(err).Msg("failed migration")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
err = updateSettingsFromFlags(store, flags)
|
||||
@@ -140,14 +130,14 @@ func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService port
|
||||
<-shutdownCtx.Done()
|
||||
defer connection.Close()
|
||||
|
||||
exportFilename := path.Join(*flags.Data, fmt.Sprintf("export-%d.json", time.Now().Unix()))
|
||||
// exportFilename := path.Join(*flags.Data, fmt.Sprintf("export-%d.json", time.Now().Unix()))
|
||||
|
||||
err := store.Export(exportFilename)
|
||||
if err != nil {
|
||||
log.Error().Str("filename", exportFilename).Err(err).Msg("failed to export")
|
||||
} else {
|
||||
log.Debug().Str("filename", exportFilename).Msg("exported")
|
||||
}
|
||||
// err := store.Export(exportFilename)
|
||||
// if err != nil {
|
||||
// log.Error().Str("filename", exportFilename).Err(err).Msg("failed to export")
|
||||
// } else {
|
||||
// log.Debug().Str("filename", exportFilename).Msg("exported")
|
||||
// }
|
||||
}()
|
||||
|
||||
return store
|
||||
@@ -186,6 +176,10 @@ func initAPIKeyService(datastore dataservices.DataStore) apikey.APIKeyService {
|
||||
}
|
||||
|
||||
func initJWTService(userSessionTimeout string, dataStore dataservices.DataStore) (dataservices.JWTService, error) {
|
||||
if userSessionTimeout == "" {
|
||||
userSessionTimeout = portainer.DefaultUserSessionTimeout
|
||||
}
|
||||
|
||||
jwtService, err := jwt.NewService(userSessionTimeout, dataStore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -239,7 +233,13 @@ func initKubernetesClientFactory(signatureService portainer.DigitalSignatureServ
|
||||
return kubecli.NewClientFactory(signatureService, reverseTunnelService, instanceID, dataStore)
|
||||
}
|
||||
|
||||
func initSnapshotService(snapshotIntervalFromFlag string, dataStore dataservices.DataStore, dockerClientFactory *docker.ClientFactory, kubernetesClientFactory *kubecli.ClientFactory, shutdownCtx context.Context) (portainer.SnapshotService, error) {
|
||||
func initSnapshotService(
|
||||
snapshotIntervalFromFlag string,
|
||||
dataStore dataservices.DataStore,
|
||||
dockerClientFactory *docker.ClientFactory,
|
||||
kubernetesClientFactory *kubecli.ClientFactory,
|
||||
shutdownCtx context.Context,
|
||||
) (portainer.SnapshotService, error) {
|
||||
dockerSnapshotter := docker.NewSnapshotter(dockerClientFactory)
|
||||
kubernetesSnapshotter := kubernetes.NewSnapshotter(kubernetesClientFactory)
|
||||
|
||||
@@ -580,6 +580,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
ldapService := initLDAPService()
|
||||
|
||||
oauthService := initOAuthService()
|
||||
|
||||
gitService := initGitService(shutdownCtx)
|
||||
|
||||
openAMTService := openamt.NewService()
|
||||
@@ -704,15 +705,15 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
log.Fatal().Err(err).Msg("failed starting tunnel server")
|
||||
}
|
||||
|
||||
scheduler := scheduler.NewScheduler(shutdownCtx)
|
||||
stackDeployer := deployments.NewStackDeployer(swarmStackManager, composeStackManager, kubernetesDeployer)
|
||||
deployments.StartStackSchedules(scheduler, stackDeployer, dataStore, gitService)
|
||||
|
||||
sslDBSettings, err := dataStore.SSLSettings().Settings()
|
||||
if err != nil {
|
||||
log.Fatal().Msg("failed to fetch SSL settings from DB")
|
||||
}
|
||||
|
||||
scheduler := scheduler.NewScheduler(shutdownCtx)
|
||||
stackDeployer := stacks.NewStackDeployer(swarmStackManager, composeStackManager, kubernetesDeployer)
|
||||
stacks.StartStackSchedules(scheduler, stackDeployer, dataStore, gitService)
|
||||
|
||||
return &http.Server{
|
||||
AuthorizationService: authorizationService,
|
||||
ReverseTunnelService: reverseTunnelService,
|
||||
@@ -726,8 +727,8 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
ComposeStackManager: composeStackManager,
|
||||
KubernetesDeployer: kubernetesDeployer,
|
||||
HelmPackageManager: helmPackageManager,
|
||||
CryptoService: cryptoService,
|
||||
APIKeyService: apiKeyService,
|
||||
CryptoService: cryptoService,
|
||||
JWTService: jwtService,
|
||||
FileService: fileService,
|
||||
LDAPService: ldapService,
|
||||
|
||||
@@ -1,110 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
// import (
|
||||
// "fmt"
|
||||
// "testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/cli"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
// portainer "github.com/portainer/portainer/api"
|
||||
// "github.com/portainer/portainer/api/cli"
|
||||
// "github.com/portainer/portainer/api/dataservices"
|
||||
// "github.com/portainer/portainer/api/datastore"
|
||||
|
||||
type mockKingpinSetting string
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// "gopkg.in/alecthomas/kingpin.v2"
|
||||
// )
|
||||
|
||||
func (m mockKingpinSetting) SetValue(value kingpin.Value) {
|
||||
value.Set(string(m))
|
||||
}
|
||||
// type mockKingpinSetting string
|
||||
|
||||
func Test_enableFeaturesFromFlags(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
// func (m mockKingpinSetting) SetValue(value kingpin.Value) {
|
||||
// value.Set(string(m))
|
||||
// }
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
defer teardown()
|
||||
// func Test_enableFeaturesFromFlags(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
|
||||
tests := []struct {
|
||||
featureFlag string
|
||||
isSupported bool
|
||||
}{
|
||||
{"test", false},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s succeeds:%v", test.featureFlag, test.isSupported), func(t *testing.T) {
|
||||
mockKingpinSetting := mockKingpinSetting(test.featureFlag)
|
||||
flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
|
||||
err := enableFeaturesFromFlags(store, flags)
|
||||
if test.isSupported {
|
||||
is.NoError(err)
|
||||
} else {
|
||||
is.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
// _, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
// defer teardown()
|
||||
|
||||
t.Run("passes for all supported feature flags", func(t *testing.T) {
|
||||
for _, flag := range portainer.SupportedFeatureFlags {
|
||||
mockKingpinSetting := mockKingpinSetting(flag)
|
||||
flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
|
||||
err := enableFeaturesFromFlags(store, flags)
|
||||
is.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
// tests := []struct {
|
||||
// featureFlag string
|
||||
// isSupported bool
|
||||
// }{
|
||||
// {"test", false},
|
||||
// }
|
||||
// for _, test := range tests {
|
||||
// t.Run(fmt.Sprintf("%s succeeds:%v", test.featureFlag, test.isSupported), func(t *testing.T) {
|
||||
// mockKingpinSetting := mockKingpinSetting(test.featureFlag)
|
||||
// flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
|
||||
// err := enableFeaturesFromFlags(store, flags)
|
||||
// if test.isSupported {
|
||||
// is.NoError(err)
|
||||
// } else {
|
||||
// is.Error(err)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
const FeatTest portainer.Feature = "optional-test"
|
||||
// t.Run("passes for all supported feature flags", func(t *testing.T) {
|
||||
// for _, flag := range portainer.SupportedFeatureFlags {
|
||||
// mockKingpinSetting := mockKingpinSetting(flag)
|
||||
// flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
|
||||
// err := enableFeaturesFromFlags(store, flags)
|
||||
// is.NoError(err)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
func optionalFunc(dataStore dataservices.DataStore) string {
|
||||
// const FeatTest portainer.Feature = "optional-test"
|
||||
|
||||
// TODO: this is a code smell - finding out if a feature flag is enabled should not require having access to the store, and asking for a settings obj.
|
||||
// ideally, the `if` should look more like:
|
||||
// if featureflags.FlagEnabled(FeatTest) {}
|
||||
settings, err := dataStore.Settings().Settings()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
// func optionalFunc(dataStore dataservices.DataStore) string {
|
||||
|
||||
if settings.FeatureFlagSettings[FeatTest] {
|
||||
return "enabled"
|
||||
}
|
||||
return "disabled"
|
||||
}
|
||||
// // TODO: this is a code smell - finding out if a feature flag is enabled should not require having access to the store, and asking for a settings obj.
|
||||
// // ideally, the `if` should look more like:
|
||||
// // if featureflags.FlagEnabled(FeatTest) {}
|
||||
// settings, err := dataStore.Settings().Settings()
|
||||
// if err != nil {
|
||||
// return err.Error()
|
||||
// }
|
||||
|
||||
func Test_optionalFeature(t *testing.T) {
|
||||
portainer.SupportedFeatureFlags = append(portainer.SupportedFeatureFlags, FeatTest)
|
||||
// if settings.FeatureFlagSettings[FeatTest] {
|
||||
// return "enabled"
|
||||
// }
|
||||
// return "disabled"
|
||||
// }
|
||||
|
||||
is := assert.New(t)
|
||||
// func Test_optionalFeature(t *testing.T) {
|
||||
// portainer.SupportedFeatureFlags = append(portainer.SupportedFeatureFlags, FeatTest)
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
defer teardown()
|
||||
// is := assert.New(t)
|
||||
|
||||
// Enable the test feature
|
||||
t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
|
||||
mockKingpinSetting := mockKingpinSetting(FeatTest)
|
||||
flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
|
||||
err := enableFeaturesFromFlags(store, flags)
|
||||
is.NoError(err)
|
||||
is.Equal("enabled", optionalFunc(store))
|
||||
})
|
||||
// _, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
// defer teardown()
|
||||
|
||||
// Same store, so the feature flag should still be enabled
|
||||
t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
|
||||
is.Equal("enabled", optionalFunc(store))
|
||||
})
|
||||
// // Enable the test feature
|
||||
// t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
|
||||
// mockKingpinSetting := mockKingpinSetting(FeatTest)
|
||||
// flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
|
||||
// err := enableFeaturesFromFlags(store, flags)
|
||||
// is.NoError(err)
|
||||
// is.Equal("enabled", optionalFunc(store))
|
||||
// })
|
||||
|
||||
// disable the test feature
|
||||
t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
|
||||
mockKingpinSetting := mockKingpinSetting(FeatTest + "=false")
|
||||
flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
|
||||
err := enableFeaturesFromFlags(store, flags)
|
||||
is.NoError(err)
|
||||
is.Equal("disabled", optionalFunc(store))
|
||||
})
|
||||
// // Same store, so the feature flag should still be enabled
|
||||
// t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
|
||||
// is.Equal("enabled", optionalFunc(store))
|
||||
// })
|
||||
|
||||
// Same store, so feature flag should still be disabled
|
||||
t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
|
||||
is.Equal("disabled", optionalFunc(store))
|
||||
})
|
||||
// // disable the test feature
|
||||
// t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
|
||||
// mockKingpinSetting := mockKingpinSetting(FeatTest + "=false")
|
||||
// flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
|
||||
// err := enableFeaturesFromFlags(store, flags)
|
||||
// is.NoError(err)
|
||||
// is.Equal("disabled", optionalFunc(store))
|
||||
// })
|
||||
|
||||
}
|
||||
// // Same store, so feature flag should still be disabled
|
||||
// t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
|
||||
// is.Equal("disabled", optionalFunc(store))
|
||||
// })
|
||||
|
||||
// }
|
||||
|
||||
@@ -1,40 +1,20 @@
|
||||
package portainer
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Connection interface {
|
||||
Open() error
|
||||
Close() error
|
||||
|
||||
// write the db contents to filename as json (the schema needs defining)
|
||||
ExportRaw(filename string) error
|
||||
Init() error
|
||||
|
||||
GetDB() *gorm.DB
|
||||
|
||||
// TODO: this one is very database specific atm
|
||||
BackupTo(w io.Writer) error
|
||||
GetDatabaseFileName() string
|
||||
GetDatabaseFilePath() string
|
||||
GetStorePath() string
|
||||
|
||||
SetEncrypted(encrypted bool)
|
||||
IsEncryptedStore() bool
|
||||
NeedsEncryptionMigration() (bool, error)
|
||||
SetEncrypted(encrypted bool)
|
||||
|
||||
SetServiceName(bucketName string) error
|
||||
GetObject(bucketName string, key []byte, object interface{}) error
|
||||
UpdateObject(bucketName string, key []byte, object interface{}) error
|
||||
DeleteObject(bucketName string, key []byte) error
|
||||
DeleteAllObjects(bucketName string, matching func(o interface{}) (id int, ok bool)) error
|
||||
GetNextIdentifier(bucketName string) int
|
||||
CreateObject(bucketName string, fn func(uint64) (int, interface{})) error
|
||||
CreateObjectWithId(bucketName string, id int, obj interface{}) error
|
||||
CreateObjectWithStringId(bucketName string, id []byte, obj interface{}) error
|
||||
CreateObjectWithSetSequence(bucketName string, id int, obj interface{}) error
|
||||
GetAll(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error
|
||||
GetAllWithJsoniter(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error
|
||||
ConvertToKey(v int) []byte
|
||||
|
||||
BackupMetadata() (map[string]interface{}, error)
|
||||
RestoreMetadata(s map[string]interface{}) error
|
||||
}
|
||||
|
||||
@@ -1,432 +0,0 @@
|
||||
package boltdb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
const (
|
||||
DatabaseFileName = "portainer.db"
|
||||
EncryptedDatabaseFileName = "portainer.edb"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHaveEncryptedAndUnencrypted = errors.New("Portainer has detected both an encrypted and un-encrypted database and cannot start. Only one database should exist")
|
||||
ErrHaveEncryptedWithNoKey = errors.New("The portainer database is encrypted, but no secret was loaded")
|
||||
)
|
||||
|
||||
type DbConnection struct {
|
||||
Path string
|
||||
MaxBatchSize int
|
||||
MaxBatchDelay time.Duration
|
||||
InitialMmapSize int
|
||||
EncryptionKey []byte
|
||||
isEncrypted bool
|
||||
|
||||
*bolt.DB
|
||||
}
|
||||
|
||||
// GetDatabaseFileName get the database filename
|
||||
func (connection *DbConnection) GetDatabaseFileName() string {
|
||||
if connection.IsEncryptedStore() {
|
||||
return EncryptedDatabaseFileName
|
||||
}
|
||||
|
||||
return DatabaseFileName
|
||||
}
|
||||
|
||||
// GetDataseFilePath get the path + filename for the database file
|
||||
func (connection *DbConnection) GetDatabaseFilePath() string {
|
||||
if connection.IsEncryptedStore() {
|
||||
return path.Join(connection.Path, EncryptedDatabaseFileName)
|
||||
}
|
||||
|
||||
return path.Join(connection.Path, DatabaseFileName)
|
||||
}
|
||||
|
||||
// GetStorePath get the filename and path for the database file
|
||||
func (connection *DbConnection) GetStorePath() string {
|
||||
return connection.Path
|
||||
}
|
||||
|
||||
func (connection *DbConnection) SetEncrypted(flag bool) {
|
||||
connection.isEncrypted = flag
|
||||
}
|
||||
|
||||
// Return true if the database is encrypted
|
||||
func (connection *DbConnection) IsEncryptedStore() bool {
|
||||
return connection.getEncryptionKey() != nil
|
||||
}
|
||||
|
||||
// NeedsEncryptionMigration returns true if database encryption is enabled and
|
||||
// we have an un-encrypted DB that requires migration to an encrypted DB
|
||||
func (connection *DbConnection) NeedsEncryptionMigration() (bool, error) {
|
||||
|
||||
// Cases: Note, we need to check both portainer.db and portainer.edb
|
||||
// to determine if it's a new store. We only need to differentiate between cases 2,3 and 5
|
||||
|
||||
// 1) portainer.edb + key => False
|
||||
// 2) portainer.edb + no key => ERROR Fatal!
|
||||
// 3) portainer.db + key => True (needs migration)
|
||||
// 4) portainer.db + no key => False
|
||||
// 5) NoDB (new) + key => False
|
||||
// 6) NoDB (new) + no key => False
|
||||
// 7) portainer.db & portainer.edb => ERROR Fatal!
|
||||
|
||||
// If we have a loaded encryption key, always set encrypted
|
||||
if connection.EncryptionKey != nil {
|
||||
connection.SetEncrypted(true)
|
||||
}
|
||||
|
||||
// Check for portainer.db
|
||||
dbFile := path.Join(connection.Path, DatabaseFileName)
|
||||
_, err := os.Stat(dbFile)
|
||||
haveDbFile := err == nil
|
||||
|
||||
// Check for portainer.edb
|
||||
edbFile := path.Join(connection.Path, EncryptedDatabaseFileName)
|
||||
_, err = os.Stat(edbFile)
|
||||
haveEdbFile := err == nil
|
||||
|
||||
if haveDbFile && haveEdbFile {
|
||||
// 7 - encrypted and unencrypted db?
|
||||
return false, ErrHaveEncryptedAndUnencrypted
|
||||
}
|
||||
|
||||
if haveDbFile && connection.EncryptionKey != nil {
|
||||
// 3 - needs migration
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if haveEdbFile && connection.EncryptionKey == nil {
|
||||
// 2 - encrypted db, but no key?
|
||||
return false, ErrHaveEncryptedWithNoKey
|
||||
}
|
||||
|
||||
// 1, 4, 5, 6
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Open opens and initializes the BoltDB database.
|
||||
func (connection *DbConnection) Open() error {
|
||||
|
||||
log.Info().Str("filename", connection.GetDatabaseFileName()).Msg("loading PortainerDB")
|
||||
|
||||
// Now we open the db
|
||||
databasePath := connection.GetDatabaseFilePath()
|
||||
db, err := bolt.Open(databasePath, 0600, &bolt.Options{
|
||||
Timeout: 1 * time.Second,
|
||||
InitialMmapSize: connection.InitialMmapSize,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
db.MaxBatchSize = connection.MaxBatchSize
|
||||
db.MaxBatchDelay = connection.MaxBatchDelay
|
||||
connection.DB = db
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the BoltDB database.
|
||||
// Safe to being called multiple times.
|
||||
func (connection *DbConnection) Close() error {
|
||||
if connection.DB != nil {
|
||||
return connection.DB.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BackupTo backs up db to a provided writer.
|
||||
// It does hot backup and doesn't block other database reads and writes
|
||||
func (connection *DbConnection) BackupTo(w io.Writer) error {
|
||||
return connection.View(func(tx *bolt.Tx) error {
|
||||
_, err := tx.WriteTo(w)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (connection *DbConnection) ExportRaw(filename string) error {
|
||||
databasePath := connection.GetDatabaseFilePath()
|
||||
if _, err := os.Stat(databasePath); err != nil {
|
||||
return fmt.Errorf("stat on %s failed: %s", databasePath, err)
|
||||
}
|
||||
|
||||
b, err := connection.ExportJson(databasePath, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(filename, b, 0600)
|
||||
}
|
||||
|
||||
// ConvertToKey returns an 8-byte big endian representation of v.
|
||||
// This function is typically used for encoding integer IDs to byte slices
|
||||
// so that they can be used as BoltDB keys.
|
||||
func (connection *DbConnection) ConvertToKey(v int) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(b, uint64(v))
|
||||
return b
|
||||
}
|
||||
|
||||
// CreateBucket is a generic function used to create a bucket inside a database database.
|
||||
func (connection *DbConnection) SetServiceName(bucketName string) error {
|
||||
return connection.Batch(func(tx *bolt.Tx) error {
|
||||
_, err := tx.CreateBucketIfNotExists([]byte(bucketName))
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// GetObject is a generic function used to retrieve an unmarshalled object from a database database.
|
||||
func (connection *DbConnection) GetObject(bucketName string, key []byte, object interface{}) error {
|
||||
var data []byte
|
||||
|
||||
err := connection.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
|
||||
value := bucket.Get(key)
|
||||
if value == nil {
|
||||
return dserrors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
data = make([]byte, len(value))
|
||||
copy(data, value)
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return connection.UnmarshalObjectWithJsoniter(data, object)
|
||||
}
|
||||
|
||||
func (connection *DbConnection) getEncryptionKey() []byte {
|
||||
if !connection.isEncrypted {
|
||||
return nil
|
||||
}
|
||||
|
||||
return connection.EncryptionKey
|
||||
}
|
||||
|
||||
// UpdateObject is a generic function used to update an object inside a database database.
|
||||
func (connection *DbConnection) UpdateObject(bucketName string, key []byte, object interface{}) error {
|
||||
data, err := connection.MarshalObject(object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return connection.Batch(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
return bucket.Put(key, data)
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteObject is a generic function used to delete an object inside a database database.
|
||||
func (connection *DbConnection) DeleteObject(bucketName string, key []byte) error {
|
||||
return connection.Batch(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
return bucket.Delete(key)
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteAllObjects delete all objects where matching() returns (id, ok).
|
||||
// TODO: think about how to return the error inside (maybe change ok to type err, and use "notfound"?
|
||||
func (connection *DbConnection) DeleteAllObjects(bucketName string, matching func(o interface{}) (id int, ok bool)) error {
|
||||
return connection.Batch(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
|
||||
cursor := bucket.Cursor()
|
||||
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||
var obj interface{}
|
||||
err := connection.UnmarshalObject(v, &obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if id, ok := matching(obj); ok {
|
||||
err := bucket.Delete(connection.ConvertToKey(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetNextIdentifier is a generic function that returns the specified bucket identifier incremented by 1.
|
||||
func (connection *DbConnection) GetNextIdentifier(bucketName string) int {
|
||||
var identifier int
|
||||
|
||||
connection.Batch(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
id, err := bucket.NextSequence()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
identifier = int(id)
|
||||
return nil
|
||||
})
|
||||
|
||||
return identifier
|
||||
}
|
||||
|
||||
// CreateObject creates a new object in the bucket, using the next bucket sequence id
|
||||
func (connection *DbConnection) CreateObject(bucketName string, fn func(uint64) (int, interface{})) error {
|
||||
return connection.Batch(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
|
||||
seqId, _ := bucket.NextSequence()
|
||||
id, obj := fn(seqId)
|
||||
|
||||
data, err := connection.MarshalObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bucket.Put(connection.ConvertToKey(int(id)), data)
|
||||
})
|
||||
}
|
||||
|
||||
// CreateObjectWithId creates a new object in the bucket, using the specified id
|
||||
func (connection *DbConnection) CreateObjectWithId(bucketName string, id int, obj interface{}) error {
|
||||
return connection.Batch(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
data, err := connection.MarshalObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bucket.Put(connection.ConvertToKey(id), data)
|
||||
})
|
||||
}
|
||||
|
||||
// CreateObjectWithStringId creates a new object in the bucket, using the specified id
|
||||
func (connection *DbConnection) CreateObjectWithStringId(bucketName string, id []byte, obj interface{}) error {
|
||||
return connection.Batch(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
data, err := connection.MarshalObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bucket.Put(id, data)
|
||||
})
|
||||
}
|
||||
|
||||
// CreateObjectWithSetSequence creates a new object in the bucket, using the specified id, and sets the bucket sequence
|
||||
// avoid this :)
|
||||
func (connection *DbConnection) CreateObjectWithSetSequence(bucketName string, id int, obj interface{}) error {
|
||||
return connection.Batch(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
|
||||
// We manually manage sequences for schedules
|
||||
err := bucket.SetSequence(uint64(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := connection.MarshalObject(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bucket.Put(connection.ConvertToKey(id), data)
|
||||
})
|
||||
}
|
||||
|
||||
func (connection *DbConnection) GetAll(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error {
|
||||
err := connection.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
|
||||
cursor := bucket.Cursor()
|
||||
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||
err := connection.UnmarshalObject(v, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj, err = append(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: decide which Unmarshal to use, and use one...
|
||||
func (connection *DbConnection) GetAllWithJsoniter(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error {
|
||||
err := connection.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
|
||||
cursor := bucket.Cursor()
|
||||
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||
err := connection.UnmarshalObjectWithJsoniter(v, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj, err = append(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (connection *DbConnection) BackupMetadata() (map[string]interface{}, error) {
|
||||
buckets := map[string]interface{}{}
|
||||
|
||||
err := connection.View(func(tx *bolt.Tx) error {
|
||||
err := tx.ForEach(func(name []byte, bucket *bolt.Bucket) error {
|
||||
bucketName := string(name)
|
||||
bucket = tx.Bucket([]byte(bucketName))
|
||||
seqId := bucket.Sequence()
|
||||
buckets[bucketName] = int(seqId)
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return buckets, err
|
||||
}
|
||||
|
||||
func (connection *DbConnection) RestoreMetadata(s map[string]interface{}) error {
|
||||
var err error
|
||||
|
||||
for bucketName, v := range s {
|
||||
id, ok := v.(float64) // JSON ints are unmarshalled to interface as float64. See: https://pkg.go.dev/encoding/json#Decoder.Decode
|
||||
if !ok {
|
||||
log.Error().Str("bucket", bucketName).Msg("failed to restore metadata to bucket, skipped")
|
||||
continue
|
||||
}
|
||||
|
||||
err = connection.Batch(func(tx *bolt.Tx) error {
|
||||
bucket, err := tx.CreateBucketIfNotExists([]byte(bucketName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bucket.SetSequence(uint64(id))
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package boltdb
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_NeedsEncryptionMigration(t *testing.T) {
|
||||
// Test the specific scenarios mentioned in NeedsEncryptionMigration
|
||||
|
||||
// i.e.
|
||||
// Cases: Note, we need to check both portainer.db and portainer.edb
|
||||
// to determine if it's a new store. We only need to differentiate between cases 2,3 and 5
|
||||
|
||||
// 1) portainer.edb + key => False
|
||||
// 2) portainer.edb + no key => ERROR Fatal!
|
||||
// 3) portainer.db + key => True (needs migration)
|
||||
// 4) portainer.db + no key => False
|
||||
// 5) NoDB (new) + key => False
|
||||
// 6) NoDB (new) + no key => False
|
||||
// 7) portainer.db & portainer.edb (key not important) => ERROR Fatal!
|
||||
|
||||
is := assert.New(t)
|
||||
dir := t.TempDir()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
dbname string
|
||||
key bool
|
||||
expectError error
|
||||
expectResult bool
|
||||
}{
|
||||
{
|
||||
name: "portainer.edb + key",
|
||||
dbname: EncryptedDatabaseFileName,
|
||||
key: true,
|
||||
expectError: nil,
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
name: "portainer.db + key (migration needed)",
|
||||
dbname: DatabaseFileName,
|
||||
key: true,
|
||||
expectError: nil,
|
||||
expectResult: true,
|
||||
},
|
||||
{
|
||||
name: "portainer.db + no key",
|
||||
dbname: DatabaseFileName,
|
||||
key: false,
|
||||
expectError: nil,
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
name: "NoDB (new) + key",
|
||||
dbname: "",
|
||||
key: false,
|
||||
expectError: nil,
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
name: "NoDB (new) + no key",
|
||||
dbname: "",
|
||||
key: false,
|
||||
expectError: nil,
|
||||
expectResult: false,
|
||||
},
|
||||
|
||||
// error tests
|
||||
{
|
||||
name: "portainer.edb + no key",
|
||||
dbname: EncryptedDatabaseFileName,
|
||||
key: false,
|
||||
expectError: ErrHaveEncryptedWithNoKey,
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
name: "portainer.db & portainer.edb",
|
||||
dbname: "both",
|
||||
key: true,
|
||||
expectError: ErrHaveEncryptedAndUnencrypted,
|
||||
expectResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
connection := DbConnection{Path: dir}
|
||||
|
||||
if tc.dbname == "both" {
|
||||
// Special case. If portainer.db and portainer.edb exist.
|
||||
dbFile1 := path.Join(connection.Path, DatabaseFileName)
|
||||
f, _ := os.Create(dbFile1)
|
||||
f.Close()
|
||||
defer os.Remove(dbFile1)
|
||||
|
||||
dbFile2 := path.Join(connection.Path, EncryptedDatabaseFileName)
|
||||
f, _ = os.Create(dbFile2)
|
||||
f.Close()
|
||||
defer os.Remove(dbFile2)
|
||||
} else if tc.dbname != "" {
|
||||
dbFile := path.Join(connection.Path, tc.dbname)
|
||||
f, _ := os.Create(dbFile)
|
||||
f.Close()
|
||||
defer os.Remove(dbFile)
|
||||
}
|
||||
|
||||
if tc.key {
|
||||
connection.EncryptionKey = []byte("secret")
|
||||
}
|
||||
|
||||
result, err := connection.NeedsEncryptionMigration()
|
||||
|
||||
is.Equal(tc.expectError, err, "Fatal Error failure. Test: %s", tc.name)
|
||||
is.Equal(result, tc.expectResult, "Failed test: %s", tc.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package boltdb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
func backupMetadata(connection *bolt.DB) (map[string]interface{}, error) {
|
||||
buckets := map[string]interface{}{}
|
||||
|
||||
err := connection.View(func(tx *bolt.Tx) error {
|
||||
err := tx.ForEach(func(name []byte, bucket *bolt.Bucket) error {
|
||||
bucketName := string(name)
|
||||
bucket = tx.Bucket([]byte(bucketName))
|
||||
seqId := bucket.Sequence()
|
||||
buckets[bucketName] = int(seqId)
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return buckets, err
|
||||
}
|
||||
|
||||
// ExportJSON creates a JSON representation from a DbConnection. You can include
|
||||
// the database's metadata or ignore it. Ensure the database is closed before
|
||||
// using this function.
|
||||
// inspired by github.com/konoui/boltdb-exporter (which has no license)
|
||||
// but very much simplified, based on how we use boltdb
|
||||
func (c *DbConnection) ExportJson(databasePath string, metadata bool) ([]byte, error) {
|
||||
log.Debug().Str("databasePath", databasePath).Msg("exportJson")
|
||||
|
||||
connection, err := bolt.Open(databasePath, 0600, &bolt.Options{Timeout: 1 * time.Second, ReadOnly: true})
|
||||
if err != nil {
|
||||
return []byte("{}"), err
|
||||
}
|
||||
defer connection.Close()
|
||||
|
||||
backup := make(map[string]interface{})
|
||||
if metadata {
|
||||
meta, err := backupMetadata(connection)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed exporting metadata")
|
||||
}
|
||||
|
||||
backup["__metadata"] = meta
|
||||
}
|
||||
|
||||
err = connection.View(func(tx *bolt.Tx) error {
|
||||
err = tx.ForEach(func(name []byte, bucket *bolt.Bucket) error {
|
||||
bucketName := string(name)
|
||||
var list []interface{}
|
||||
version := make(map[string]string)
|
||||
cursor := bucket.Cursor()
|
||||
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var obj interface{}
|
||||
err := c.UnmarshalObject(v, &obj)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("bucket", bucketName).
|
||||
Str("object", string(v)).
|
||||
Err(err).
|
||||
Msg("failed to unmarshal")
|
||||
|
||||
obj = v
|
||||
}
|
||||
|
||||
if bucketName == "version" {
|
||||
version[string(k)] = string(v)
|
||||
} else {
|
||||
list = append(list, obj)
|
||||
}
|
||||
}
|
||||
|
||||
if bucketName == "version" {
|
||||
backup[bucketName] = version
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(list) > 0 {
|
||||
if bucketName == "ssl" ||
|
||||
bucketName == "settings" ||
|
||||
bucketName == "tunnel_server" {
|
||||
backup[bucketName] = nil
|
||||
if len(list) > 0 {
|
||||
backup[bucketName] = list[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
backup[bucketName] = list
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return []byte("{}"), err
|
||||
}
|
||||
|
||||
return json.MarshalIndent(backup, "", " ")
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package boltdb
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var errEncryptedStringTooShort = fmt.Errorf("encrypted string too short")
|
||||
|
||||
// MarshalObject encodes an object to binary format
|
||||
func (connection *DbConnection) MarshalObject(object interface{}) (data []byte, err error) {
|
||||
// Special case for the VERSION bucket. Here we're not using json
|
||||
if v, ok := object.(string); ok {
|
||||
data = []byte(v)
|
||||
} else {
|
||||
data, err = json.Marshal(object)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
}
|
||||
if connection.getEncryptionKey() == nil {
|
||||
return data, nil
|
||||
}
|
||||
return encrypt(data, connection.getEncryptionKey())
|
||||
}
|
||||
|
||||
// UnmarshalObject decodes an object from binary data
|
||||
func (connection *DbConnection) UnmarshalObject(data []byte, object interface{}) error {
|
||||
var err error
|
||||
if connection.getEncryptionKey() != nil {
|
||||
data, err = decrypt(data, connection.getEncryptionKey())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed decrypting object")
|
||||
}
|
||||
}
|
||||
e := json.Unmarshal(data, object)
|
||||
if e != nil {
|
||||
// Special case for the VERSION bucket. Here we're not using json
|
||||
// So we need to return it as a string
|
||||
s, ok := object.(*string)
|
||||
if !ok {
|
||||
return errors.Wrap(err, e.Error())
|
||||
}
|
||||
|
||||
*s = string(data)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalObjectWithJsoniter decodes an object from binary data
|
||||
// using the jsoniter library. It is mainly used to accelerate environment(endpoint)
|
||||
// decoding at the moment.
|
||||
func (connection *DbConnection) UnmarshalObjectWithJsoniter(data []byte, object interface{}) error {
|
||||
if connection.getEncryptionKey() != nil {
|
||||
var err error
|
||||
data, err = decrypt(data, connection.getEncryptionKey())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var jsoni = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
err := jsoni.Unmarshal(data, &object)
|
||||
if err != nil {
|
||||
if s, ok := object.(*string); ok {
|
||||
*s = string(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// mmm, don't have a KMS .... aes GCM seems the most likely from
|
||||
// https://gist.github.com/atoponce/07d8d4c833873be2f68c34f9afc5a78a#symmetric-encryption
|
||||
|
||||
func encrypt(plaintext []byte, passphrase []byte) (encrypted []byte, err error) {
|
||||
block, _ := aes.NewCipher(passphrase)
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return encrypted, err
|
||||
}
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return encrypted, err
|
||||
}
|
||||
ciphertextByte := gcm.Seal(
|
||||
nonce,
|
||||
nonce,
|
||||
plaintext,
|
||||
nil)
|
||||
return ciphertextByte, nil
|
||||
}
|
||||
|
||||
func decrypt(encrypted []byte, passphrase []byte) (plaintextByte []byte, err error) {
|
||||
if string(encrypted) == "false" {
|
||||
return []byte("false"), nil
|
||||
}
|
||||
block, err := aes.NewCipher(passphrase)
|
||||
if err != nil {
|
||||
return encrypted, errors.Wrap(err, "Error creating cypher block")
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return encrypted, errors.Wrap(err, "Error creating GCM")
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(encrypted) < nonceSize {
|
||||
return encrypted, errEncryptedStringTooShort
|
||||
}
|
||||
|
||||
nonce, ciphertextByteClean := encrypted[:nonceSize], encrypted[nonceSize:]
|
||||
plaintextByte, err = gcm.Open(
|
||||
nil,
|
||||
nonce,
|
||||
ciphertextByteClean,
|
||||
nil)
|
||||
if err != nil {
|
||||
return encrypted, errors.Wrap(err, "Error decrypting text")
|
||||
}
|
||||
|
||||
return plaintextByte, err
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
package boltdb
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
jsonobject = `{"LogoURL":"","BlackListedLabels":[],"AuthenticationMethod":1,"InternalAuthSettings": {"RequiredPasswordLength": 12}"LDAPSettings":{"AnonymousMode":true,"ReaderDN":"","URL":"","TLSConfig":{"TLS":false,"TLSSkipVerify":false},"StartTLS":false,"SearchSettings":[{"BaseDN":"","Filter":"","UserNameAttribute":""}],"GroupSearchSettings":[{"GroupBaseDN":"","GroupFilter":"","GroupAttribute":""}],"AutoCreateUsers":true},"OAuthSettings":{"ClientID":"","AccessTokenURI":"","AuthorizationURI":"","ResourceURI":"","RedirectURI":"","UserIdentifier":"","Scopes":"","OAuthAutoCreateUsers":false,"DefaultTeamID":0,"SSO":true,"LogoutURI":"","KubeSecretKey":"j0zLVtY/lAWBk62ByyF0uP80SOXaitsABP0TTJX8MhI="},"OpenAMTConfiguration":{"Enabled":false,"MPSServer":"","MPSUser":"","MPSPassword":"","MPSToken":"","CertFileContent":"","CertFileName":"","CertFilePassword":"","DomainName":""},"FeatureFlagSettings":{},"SnapshotInterval":"5m","TemplatesURL":"https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json","EdgeAgentCheckinInterval":5,"EnableEdgeComputeFeatures":false,"UserSessionTimeout":"8h","KubeconfigExpiry":"0","EnableTelemetry":true,"HelmRepositoryURL":"https://charts.bitnami.com/bitnami","KubectlShellImage":"portainer/kubectl-shell","DisplayDonationHeader":false,"DisplayExternalContributors":false,"EnableHostManagementFeatures":false,"AllowVolumeBrowserForRegularUsers":false,"AllowBindMountsForRegularUsers":false,"AllowPrivilegedModeForRegularUsers":false,"AllowHostNamespaceForRegularUsers":false,"AllowStackManagementForRegularUsers":false,"AllowDeviceMappingForRegularUsers":false,"AllowContainerCapabilitiesForRegularUsers":false}`
|
||||
passphrase = "my secret key"
|
||||
)
|
||||
|
||||
func secretToEncryptionKey(passphrase string) []byte {
|
||||
hash := sha256.Sum256([]byte(passphrase))
|
||||
return hash[:]
|
||||
}
|
||||
|
||||
func Test_MarshalObjectUnencrypted(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
uuid := uuid.Must(uuid.NewV4())
|
||||
|
||||
tests := []struct {
|
||||
object interface{}
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
object: nil,
|
||||
expected: `null`,
|
||||
},
|
||||
{
|
||||
object: true,
|
||||
expected: `true`,
|
||||
},
|
||||
{
|
||||
object: false,
|
||||
expected: `false`,
|
||||
},
|
||||
{
|
||||
object: 123,
|
||||
expected: `123`,
|
||||
},
|
||||
{
|
||||
object: "456",
|
||||
expected: "456",
|
||||
},
|
||||
{
|
||||
object: uuid,
|
||||
expected: "\"" + uuid.String() + "\"",
|
||||
},
|
||||
{
|
||||
object: uuid.String(),
|
||||
expected: uuid.String(),
|
||||
},
|
||||
{
|
||||
object: map[string]interface{}{"key": "value"},
|
||||
expected: `{"key":"value"}`,
|
||||
},
|
||||
{
|
||||
object: []bool{true, false},
|
||||
expected: `[true,false]`,
|
||||
},
|
||||
{
|
||||
object: []int{1, 2, 3},
|
||||
expected: `[1,2,3]`,
|
||||
},
|
||||
{
|
||||
object: []string{"1", "2", "3"},
|
||||
expected: `["1","2","3"]`,
|
||||
},
|
||||
{
|
||||
object: []map[string]interface{}{{"key1": "value1"}, {"key2": "value2"}},
|
||||
expected: `[{"key1":"value1"},{"key2":"value2"}]`,
|
||||
},
|
||||
{
|
||||
object: []interface{}{1, "2", false, map[string]interface{}{"key1": "value1"}},
|
||||
expected: `[1,"2",false,{"key1":"value1"}]`,
|
||||
},
|
||||
}
|
||||
|
||||
conn := DbConnection{}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s -> %s", test.object, test.expected), func(t *testing.T) {
|
||||
data, err := conn.MarshalObject(test.object)
|
||||
is.NoError(err)
|
||||
is.Equal(test.expected, string(data))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_UnMarshalObjectUnencrypted(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// Based on actual data entering and what we expect out of the function
|
||||
|
||||
tests := []struct {
|
||||
object []byte
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
object: []byte(""),
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
object: []byte("35"),
|
||||
expected: "35",
|
||||
},
|
||||
{
|
||||
// An unmarshalled byte string should return the same without error
|
||||
object: []byte("9ca4a1dd-a439-4593-b386-a7dfdc2e9fc6"),
|
||||
expected: "9ca4a1dd-a439-4593-b386-a7dfdc2e9fc6",
|
||||
},
|
||||
{
|
||||
// An un-marshalled json object string should return the same as a string without error also
|
||||
object: []byte(jsonobject),
|
||||
expected: jsonobject,
|
||||
},
|
||||
}
|
||||
|
||||
conn := DbConnection{}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s -> %s", test.object, test.expected), func(t *testing.T) {
|
||||
var object string
|
||||
err := conn.UnmarshalObject(test.object, &object)
|
||||
is.NoError(err)
|
||||
is.Equal(test.expected, string(object))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ObjectMarshallingEncrypted(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// Based on actual data entering and what we expect out of the function
|
||||
|
||||
tests := []struct {
|
||||
object []byte
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
object: []byte(""),
|
||||
},
|
||||
{
|
||||
object: []byte("35"),
|
||||
},
|
||||
{
|
||||
// An unmarshalled byte string should return the same without error
|
||||
object: []byte("9ca4a1dd-a439-4593-b386-a7dfdc2e9fc6"),
|
||||
},
|
||||
{
|
||||
// An un-marshalled json object string should return the same as a string without error also
|
||||
object: []byte(jsonobject),
|
||||
},
|
||||
}
|
||||
|
||||
key := secretToEncryptionKey(passphrase)
|
||||
conn := DbConnection{EncryptionKey: key}
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s -> %s", test.object, test.expected), func(t *testing.T) {
|
||||
|
||||
data, err := conn.MarshalObject(test.object)
|
||||
is.NoError(err)
|
||||
|
||||
var object []byte
|
||||
err = conn.UnmarshalObject(data, &object)
|
||||
|
||||
is.NoError(err)
|
||||
is.Equal(test.object, object)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,18 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
"github.com/portainer/portainer/api/database/sqlite"
|
||||
)
|
||||
|
||||
// NewDatabase should use config options to return a connection to the requested database
|
||||
func NewDatabase(storeType, storePath string, encryptionKey []byte) (connection portainer.Connection, err error) {
|
||||
switch storeType {
|
||||
case "boltdb":
|
||||
return &boltdb.DbConnection{
|
||||
case "sqlite":
|
||||
return &sqlite.DbConnection{
|
||||
Path: storePath,
|
||||
EncryptionKey: encryptionKey,
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown storage database: %s", storeType)
|
||||
|
||||
return nil, fmt.Errorf("Unknown storage database: %s", storeType)
|
||||
}
|
||||
|
||||
21
api/database/models/version.go
Normal file
21
api/database/models/version.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
VersionKey string = "DB_VERSION"
|
||||
InstanceKey string = "INSTANCE_ID"
|
||||
EditionKey string = "EDITION"
|
||||
UpdatingKey string = "DB_UPDATING"
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
Key string `json:"Key" gorm:"unique,primaryKey"`
|
||||
Value string `json:"Value"`
|
||||
}
|
||||
|
||||
func (r *Version) Validate(request *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
169
api/database/sqlite/db.go
Normal file
169
api/database/sqlite/db.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
DatabaseFileName = "portainer.db"
|
||||
EncryptedDatabaseFileName = "portainer.edb"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHaveEncryptedAndUnencrypted = errors.New("Portainer has detected both an encrypted and un-encrypted database and cannot start. Only one database should exist")
|
||||
ErrHaveEncryptedWithNoKey = errors.New("The portainer database is encrypted, but no secret was loaded")
|
||||
)
|
||||
|
||||
type DbConnection struct {
|
||||
Path string
|
||||
EncryptionKey []byte
|
||||
isEncrypted bool
|
||||
|
||||
*gorm.DB
|
||||
}
|
||||
|
||||
func (connection *DbConnection) GetDB() *gorm.DB {
|
||||
if connection.DB == nil {
|
||||
err := connection.Open()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return connection.DB
|
||||
}
|
||||
|
||||
// GetDatabaseFileName get the database filename
|
||||
func (connection *DbConnection) GetDatabaseFileName() string {
|
||||
if connection.IsEncryptedStore() {
|
||||
return EncryptedDatabaseFileName
|
||||
}
|
||||
|
||||
return DatabaseFileName
|
||||
}
|
||||
|
||||
// GetDataseFilePath get the path + filename for the database file
|
||||
func (connection *DbConnection) GetDatabaseFilePath() string {
|
||||
if connection.IsEncryptedStore() {
|
||||
return path.Join(connection.Path, EncryptedDatabaseFileName)
|
||||
}
|
||||
|
||||
return path.Join(connection.Path, DatabaseFileName)
|
||||
}
|
||||
|
||||
// GetStorePath get the filename and path for the database file
|
||||
func (connection *DbConnection) GetStorePath() string {
|
||||
return connection.Path
|
||||
}
|
||||
|
||||
func (connection *DbConnection) SetEncrypted(flag bool) {
|
||||
connection.isEncrypted = flag
|
||||
}
|
||||
|
||||
// Return true if the database is encrypted
|
||||
func (connection *DbConnection) IsEncryptedStore() bool {
|
||||
return connection.getEncryptionKey() != nil
|
||||
}
|
||||
|
||||
// NeedsEncryptionMigration returns true if database encryption is enabled and
|
||||
// we have an un-encrypted DB that requires migration to an encrypted DB
|
||||
func (connection *DbConnection) NeedsEncryptionMigration() (bool, error) {
|
||||
|
||||
// Cases: Note, we need to check both portainer.db and portainer.edb
|
||||
// to determine if it's a new store. We only need to differentiate between cases 2,3 and 5
|
||||
|
||||
// 1) portainer.edb + key => False
|
||||
// 2) portainer.edb + no key => ERROR Fatal!
|
||||
// 3) portainer.db + key => True (needs migration)
|
||||
// 4) portainer.db + no key => False
|
||||
// 5) NoDB (new) + key => False
|
||||
// 6) NoDB (new) + no key => False
|
||||
// 7) portainer.db & portainer.edb => ERROR Fatal!
|
||||
|
||||
// If we have a loaded encryption key, always set encrypted
|
||||
if connection.EncryptionKey != nil {
|
||||
connection.SetEncrypted(true)
|
||||
}
|
||||
|
||||
// Check for portainer.db
|
||||
dbFile := path.Join(connection.Path, DatabaseFileName)
|
||||
_, err := os.Stat(dbFile)
|
||||
haveDbFile := err == nil
|
||||
|
||||
// Check for portainer.edb
|
||||
edbFile := path.Join(connection.Path, EncryptedDatabaseFileName)
|
||||
_, err = os.Stat(edbFile)
|
||||
haveEdbFile := err == nil
|
||||
|
||||
if haveDbFile && haveEdbFile {
|
||||
// 7 - encrypted and unencrypted db?
|
||||
return false, ErrHaveEncryptedAndUnencrypted
|
||||
}
|
||||
|
||||
if haveDbFile && connection.EncryptionKey != nil {
|
||||
// 3 - needs migration
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if haveEdbFile && connection.EncryptionKey == nil {
|
||||
// 2 - encrypted db, but no key?
|
||||
return false, ErrHaveEncryptedWithNoKey
|
||||
}
|
||||
|
||||
// 1, 4, 5, 6
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Open opens and initializes the BoltDB database.
|
||||
func (connection *DbConnection) Open() error {
|
||||
|
||||
log.Info().Str("filename", connection.GetDatabaseFileName()).Msg("loading PortainerDB")
|
||||
|
||||
// Now we open the db
|
||||
databasePath := connection.GetDatabaseFilePath()
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(databasePath), &gorm.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqlDB.SetMaxOpenConns(5)
|
||||
sqlDB.SetMaxOpenConns(10)
|
||||
connection.DB = db
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (connection *DbConnection) Close() error {
|
||||
sqlDB, err := connection.DB.DB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
connection.DB = nil
|
||||
return sqlDB.Close()
|
||||
}
|
||||
|
||||
func (connection *DbConnection) getEncryptionKey() []byte {
|
||||
if !connection.isEncrypted {
|
||||
return nil
|
||||
}
|
||||
|
||||
return connection.EncryptionKey
|
||||
}
|
||||
|
||||
func (connection *DbConnection) Init() error {
|
||||
connection.DB.AutoMigrate(&models.Version{})
|
||||
connection.DB.AutoMigrate(&portainer.Settings{})
|
||||
return nil
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
package apikeyrepository
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -22,10 +16,10 @@ type Service struct {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -36,90 +30,93 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) GetAPIKeysByUserID(userID portainer.UserID) ([]portainer.APIKey, error) {
|
||||
var result = make([]portainer.APIKey, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.APIKey{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
record, ok := obj.(*portainer.APIKey)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to APIKey object")
|
||||
return nil, fmt.Errorf("Failed to convert to APIKey object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.APIKey{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// record, ok := obj.(*portainer.APIKey)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to APIKey object")
|
||||
// return nil, fmt.Errorf("Failed to convert to APIKey object: %s", obj)
|
||||
// }
|
||||
|
||||
if record.UserID == userID {
|
||||
result = append(result, *record)
|
||||
}
|
||||
// if record.UserID == userID {
|
||||
// result = append(result, *record)
|
||||
// }
|
||||
|
||||
return &portainer.APIKey{}, nil
|
||||
})
|
||||
// return &portainer.APIKey{}, nil
|
||||
// })
|
||||
|
||||
return result, err
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetAPIKeyByDigest returns the API key for the associated digest.
|
||||
// Note: there is a 1-to-1 mapping of api-key and digest
|
||||
func (service *Service) GetAPIKeyByDigest(digest []byte) (*portainer.APIKey, error) {
|
||||
var k *portainer.APIKey
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.APIKey{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
key, ok := obj.(*portainer.APIKey)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to APIKey object")
|
||||
return nil, fmt.Errorf("Failed to convert to APIKey object: %s", obj)
|
||||
}
|
||||
if bytes.Equal(key.Digest, digest) {
|
||||
k = key
|
||||
return nil, stop
|
||||
}
|
||||
// var k *portainer.APIKey
|
||||
// stop := fmt.Errorf("ok")
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.APIKey{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// key, ok := obj.(*portainer.APIKey)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to APIKey object")
|
||||
// return nil, fmt.Errorf("Failed to convert to APIKey object: %s", obj)
|
||||
// }
|
||||
// if bytes.Equal(key.Digest, digest) {
|
||||
// k = key
|
||||
// return nil, stop
|
||||
// }
|
||||
|
||||
return &portainer.APIKey{}, nil
|
||||
})
|
||||
// return &portainer.APIKey{}, nil
|
||||
// })
|
||||
|
||||
if err == stop {
|
||||
return k, nil
|
||||
}
|
||||
// if err == stop {
|
||||
// return k, nil
|
||||
// }
|
||||
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
// if err == nil {
|
||||
// return nil, errors.ErrObjectNotFound
|
||||
// }
|
||||
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CreateAPIKey creates a new APIKey object.
|
||||
func (service *Service) CreateAPIKey(record *portainer.APIKey) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
record.ID = portainer.APIKeyID(id)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// record.ID = portainer.APIKeyID(id)
|
||||
|
||||
return int(record.ID), record
|
||||
},
|
||||
)
|
||||
// return int(record.ID), record
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAPIKey retrieves an existing APIKey object by api key ID.
|
||||
func (service *Service) GetAPIKey(keyID portainer.APIKeyID) (*portainer.APIKey, error) {
|
||||
var key portainer.APIKey
|
||||
identifier := service.connection.ConvertToKey(int(keyID))
|
||||
// identifier := service.connection.ConvertToKey(int(keyID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &key)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &key, nil
|
||||
}
|
||||
|
||||
func (service *Service) UpdateAPIKey(key *portainer.APIKey) error {
|
||||
identifier := service.connection.ConvertToKey(int(key.ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, key)
|
||||
// identifier := service.connection.ConvertToKey(int(key.ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) DeleteAPIKey(ID portainer.APIKeyID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package customtemplate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -38,56 +34,60 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) CustomTemplates() ([]portainer.CustomTemplate, error) {
|
||||
var customTemplates = make([]portainer.CustomTemplate, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.CustomTemplate{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
//var tag portainer.Tag
|
||||
customTemplate, ok := obj.(*portainer.CustomTemplate)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to CustomTemplate object")
|
||||
return nil, fmt.Errorf("Failed to convert to CustomTemplate object: %s", obj)
|
||||
}
|
||||
customTemplates = append(customTemplates, *customTemplate)
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.CustomTemplate{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// //var tag portainer.Tag
|
||||
// customTemplate, ok := obj.(*portainer.CustomTemplate)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to CustomTemplate object")
|
||||
// return nil, fmt.Errorf("Failed to convert to CustomTemplate object: %s", obj)
|
||||
// }
|
||||
// customTemplates = append(customTemplates, *customTemplate)
|
||||
|
||||
return &portainer.CustomTemplate{}, nil
|
||||
})
|
||||
// return &portainer.CustomTemplate{}, nil
|
||||
// })
|
||||
|
||||
return customTemplates, err
|
||||
return customTemplates, nil
|
||||
}
|
||||
|
||||
// CustomTemplate returns an custom template by ID.
|
||||
func (service *Service) CustomTemplate(ID portainer.CustomTemplateID) (*portainer.CustomTemplate, error) {
|
||||
var customTemplate portainer.CustomTemplate
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &customTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &customTemplate)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &customTemplate, nil
|
||||
}
|
||||
|
||||
// UpdateCustomTemplate updates an custom template.
|
||||
func (service *Service) UpdateCustomTemplate(ID portainer.CustomTemplateID, customTemplate *portainer.CustomTemplate) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, customTemplate)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, customTemplate)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteCustomTemplate deletes an custom template.
|
||||
func (service *Service) DeleteCustomTemplate(ID portainer.CustomTemplateID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateCustomTemplate uses the existing id and saves it.
|
||||
// TODO: where does the ID come from, and is it safe?
|
||||
func (service *Service) Create(customTemplate *portainer.CustomTemplate) error {
|
||||
return service.connection.CreateObjectWithId(BucketName, int(customTemplate.ID), customTemplate)
|
||||
// return service.connection.CreateObjectWithId(BucketName, int(customTemplate.ID), customTemplate)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for a custom template.
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.connection.GetNextIdentifier(BucketName)
|
||||
// return service.connection.GetNextIdentifier(BucketName)
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -35,15 +35,16 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) DockerHub() (*portainer.DockerHub, error) {
|
||||
var dockerhub portainer.DockerHub
|
||||
|
||||
err := service.connection.GetObject(BucketName, []byte(dockerHubKey), &dockerhub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, []byte(dockerHubKey), &dockerhub)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &dockerhub, nil
|
||||
}
|
||||
|
||||
// UpdateDockerHub updates a DockerHub object.
|
||||
func (service *Service) UpdateDockerHub(dockerhub *portainer.DockerHub) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(dockerHubKey), dockerhub)
|
||||
// return service.connection.UpdateObject(BucketName, []byte(dockerHubKey), dockerhub)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package edgegroup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -38,55 +34,58 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) EdgeGroups() ([]portainer.EdgeGroup, error) {
|
||||
var groups = make([]portainer.EdgeGroup, 0)
|
||||
|
||||
err := service.connection.GetAllWithJsoniter(
|
||||
BucketName,
|
||||
&portainer.EdgeGroup{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
group, ok := obj.(*portainer.EdgeGroup)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeGroup object")
|
||||
return nil, fmt.Errorf("Failed to convert to EdgeGroup object: %s", obj)
|
||||
}
|
||||
groups = append(groups, *group)
|
||||
// err := service.connection.GetAllWithJsoniter(
|
||||
// BucketName,
|
||||
// &portainer.EdgeGroup{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// group, ok := obj.(*portainer.EdgeGroup)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeGroup object")
|
||||
// return nil, fmt.Errorf("Failed to convert to EdgeGroup object: %s", obj)
|
||||
// }
|
||||
// groups = append(groups, *group)
|
||||
|
||||
return &portainer.EdgeGroup{}, nil
|
||||
})
|
||||
// return &portainer.EdgeGroup{}, nil
|
||||
// })
|
||||
|
||||
return groups, err
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
// EdgeGroup returns an Edge group by ID.
|
||||
func (service *Service) EdgeGroup(ID portainer.EdgeGroupID) (*portainer.EdgeGroup, error) {
|
||||
var group portainer.EdgeGroup
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &group)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &group, nil
|
||||
}
|
||||
|
||||
// UpdateEdgeGroup updates an Edge group.
|
||||
func (service *Service) UpdateEdgeGroup(ID portainer.EdgeGroupID, group *portainer.EdgeGroup) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, group)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, group)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEdgeGroup deletes an Edge group.
|
||||
func (service *Service) DeleteEdgeGroup(ID portainer.EdgeGroupID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateEdgeGroup assign an ID to a new Edge group and saves it.
|
||||
func (service *Service) Create(group *portainer.EdgeGroup) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
group.ID = portainer.EdgeGroupID(id)
|
||||
return int(group.ID), group
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// group.ID = portainer.EdgeGroupID(id)
|
||||
// return int(group.ID), group
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package edgejob
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -38,61 +34,65 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) EdgeJobs() ([]portainer.EdgeJob, error) {
|
||||
var edgeJobs = make([]portainer.EdgeJob, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.EdgeJob{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
job, ok := obj.(*portainer.EdgeJob)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeJob object")
|
||||
return nil, fmt.Errorf("Failed to convert to EdgeJob object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.EdgeJob{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// job, ok := obj.(*portainer.EdgeJob)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeJob object")
|
||||
// return nil, fmt.Errorf("Failed to convert to EdgeJob object: %s", obj)
|
||||
// }
|
||||
|
||||
edgeJobs = append(edgeJobs, *job)
|
||||
// edgeJobs = append(edgeJobs, *job)
|
||||
|
||||
return &portainer.EdgeJob{}, nil
|
||||
})
|
||||
// return &portainer.EdgeJob{}, nil
|
||||
// })
|
||||
|
||||
return edgeJobs, err
|
||||
return edgeJobs, nil
|
||||
}
|
||||
|
||||
// EdgeJob returns an Edge job by ID
|
||||
func (service *Service) EdgeJob(ID portainer.EdgeJobID) (*portainer.EdgeJob, error) {
|
||||
var edgeJob portainer.EdgeJob
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &edgeJob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &edgeJob)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &edgeJob, nil
|
||||
}
|
||||
|
||||
// Create creates a new EdgeJob
|
||||
func (service *Service) Create(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
|
||||
edgeJob.ID = ID
|
||||
// edgeJob.ID = ID
|
||||
|
||||
return service.connection.CreateObjectWithId(
|
||||
BucketName,
|
||||
int(edgeJob.ID),
|
||||
edgeJob,
|
||||
)
|
||||
// return service.connection.CreateObjectWithId(
|
||||
// BucketName,
|
||||
// int(edgeJob.ID),
|
||||
// edgeJob,
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEdgeJob updates an Edge job by ID
|
||||
func (service *Service) UpdateEdgeJob(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, edgeJob)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, edgeJob)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEdgeJob deletes an Edge job
|
||||
func (service *Service) DeleteEdgeJob(ID portainer.EdgeJobID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for an environment(endpoint).
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.connection.GetNextIdentifier(BucketName)
|
||||
// return service.connection.GetNextIdentifier(BucketName)
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package edgestack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -38,34 +34,34 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) EdgeStacks() ([]portainer.EdgeStack, error) {
|
||||
var stacks = make([]portainer.EdgeStack, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.EdgeStack{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
//var tag portainer.Tag
|
||||
stack, ok := obj.(*portainer.EdgeStack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeStack object")
|
||||
return nil, fmt.Errorf("Failed to convert to EdgeStack object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.EdgeStack{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// //var tag portainer.Tag
|
||||
// stack, ok := obj.(*portainer.EdgeStack)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeStack object")
|
||||
// return nil, fmt.Errorf("Failed to convert to EdgeStack object: %s", obj)
|
||||
// }
|
||||
|
||||
stacks = append(stacks, *stack)
|
||||
// stacks = append(stacks, *stack)
|
||||
|
||||
return &portainer.EdgeStack{}, nil
|
||||
})
|
||||
// return &portainer.EdgeStack{}, nil
|
||||
// })
|
||||
|
||||
return stacks, err
|
||||
return stacks, nil
|
||||
}
|
||||
|
||||
// EdgeStack returns an Edge stack by ID.
|
||||
func (service *Service) EdgeStack(ID portainer.EdgeStackID) (*portainer.EdgeStack, error) {
|
||||
var stack portainer.EdgeStack
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &stack)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &stack, nil
|
||||
}
|
||||
@@ -73,28 +69,32 @@ func (service *Service) EdgeStack(ID portainer.EdgeStackID) (*portainer.EdgeStac
|
||||
// CreateEdgeStack saves an Edge stack object to db.
|
||||
func (service *Service) Create(id portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error {
|
||||
|
||||
edgeStack.ID = id
|
||||
// edgeStack.ID = id
|
||||
|
||||
return service.connection.CreateObjectWithId(
|
||||
BucketName,
|
||||
int(edgeStack.ID),
|
||||
edgeStack,
|
||||
)
|
||||
// return service.connection.CreateObjectWithId(
|
||||
// BucketName,
|
||||
// int(edgeStack.ID),
|
||||
// edgeStack,
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEdgeStack updates an Edge stack.
|
||||
func (service *Service) UpdateEdgeStack(ID portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, edgeStack)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, edgeStack)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEdgeStack deletes an Edge stack.
|
||||
func (service *Service) DeleteEdgeStack(ID portainer.EdgeStackID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for an environment(endpoint).
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.connection.GetNextIdentifier(BucketName)
|
||||
// return service.connection.GetNextIdentifier(BucketName)
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package edgeupdateschedule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/edgetypes"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -29,10 +28,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
service := &Service{
|
||||
connection: connection,
|
||||
@@ -78,59 +77,59 @@ func (service *Service) ActiveSchedules(environmentsIDs []portainer.EndpointID)
|
||||
func (service *Service) List() ([]edgetypes.UpdateSchedule, error) {
|
||||
var list = make([]edgetypes.UpdateSchedule, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&edgetypes.UpdateSchedule{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
item, ok := obj.(*edgetypes.UpdateSchedule)
|
||||
if !ok {
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to EdgeUpdateSchedule object")
|
||||
return nil, fmt.Errorf("failed to convert to EdgeUpdateSchedule object: %s", obj)
|
||||
}
|
||||
list = append(list, *item)
|
||||
return &edgetypes.UpdateSchedule{}, nil
|
||||
})
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &edgetypes.UpdateSchedule{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// item, ok := obj.(*edgetypes.UpdateSchedule)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeUpdateSchedule object")
|
||||
// return nil, fmt.Errorf("Failed to convert to EdgeUpdateSchedule object: %s", obj)
|
||||
// }
|
||||
// list = append(list, *item)
|
||||
// return &edgetypes.UpdateSchedule{}, nil
|
||||
// })
|
||||
|
||||
return list, err
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Item returns a item by ID.
|
||||
func (service *Service) Item(ID edgetypes.UpdateScheduleID) (*edgetypes.UpdateSchedule, error) {
|
||||
var item edgetypes.UpdateSchedule
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &item)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
// Create assign an ID to a new object and saves it.
|
||||
func (service *Service) Create(item *edgetypes.UpdateSchedule) error {
|
||||
err := service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
item.ID = edgetypes.UpdateScheduleID(id)
|
||||
return int(item.ID), item
|
||||
},
|
||||
)
|
||||
// err := service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// item.ID = edgetypes.UpdateScheduleID(id)
|
||||
// return int(item.ID), item
|
||||
// },
|
||||
// )
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
return service.setRelation(item)
|
||||
}
|
||||
|
||||
// Update updates an item.
|
||||
func (service *Service) Update(id edgetypes.UpdateScheduleID, item *edgetypes.UpdateSchedule) error {
|
||||
identifier := service.connection.ConvertToKey(int(id))
|
||||
err := service.connection.UpdateObject(BucketName, identifier, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// identifier := service.connection.ConvertToKey(int(id))
|
||||
// err := service.connection.UpdateObject(BucketName, identifier, item)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
service.cleanRelation(id)
|
||||
|
||||
@@ -142,8 +141,9 @@ func (service *Service) Delete(id edgetypes.UpdateScheduleID) error {
|
||||
|
||||
service.cleanRelation(id)
|
||||
|
||||
identifier := service.connection.ConvertToKey(int(id))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(id))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) cleanRelation(id edgetypes.UpdateScheduleID) {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -37,56 +33,60 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Endpoint returns an environment(endpoint) by ID.
|
||||
func (service *Service) Endpoint(ID portainer.EndpointID) (*portainer.Endpoint, error) {
|
||||
var endpoint portainer.Endpoint
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &endpoint)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &endpoint, nil
|
||||
}
|
||||
|
||||
// UpdateEndpoint updates an environment(endpoint).
|
||||
func (service *Service) UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, endpoint)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, endpoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEndpoint deletes an environment(endpoint).
|
||||
func (service *Service) DeleteEndpoint(ID portainer.EndpointID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Endpoints return an array containing all the environments(endpoints).
|
||||
func (service *Service) Endpoints() ([]portainer.Endpoint, error) {
|
||||
var endpoints = make([]portainer.Endpoint, 0)
|
||||
|
||||
err := service.connection.GetAllWithJsoniter(
|
||||
BucketName,
|
||||
&portainer.Endpoint{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
endpoint, ok := obj.(*portainer.Endpoint)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Endpoint object")
|
||||
return nil, fmt.Errorf("failed to convert to Endpoint object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAllWithJsoniter(
|
||||
// BucketName,
|
||||
// &portainer.Endpoint{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// endpoint, ok := obj.(*portainer.Endpoint)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Endpoint object")
|
||||
// return nil, fmt.Errorf("failed to convert to Endpoint object: %s", obj)
|
||||
// }
|
||||
|
||||
endpoints = append(endpoints, *endpoint)
|
||||
// endpoints = append(endpoints, *endpoint)
|
||||
|
||||
return &portainer.Endpoint{}, nil
|
||||
})
|
||||
// return &portainer.Endpoint{}, nil
|
||||
// })
|
||||
|
||||
return endpoints, err
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
// CreateEndpoint assign an ID to a new environment(endpoint) and saves it.
|
||||
func (service *Service) Create(endpoint *portainer.Endpoint) error {
|
||||
return service.connection.CreateObjectWithSetSequence(BucketName, int(endpoint.ID), endpoint)
|
||||
// return service.connection.CreateObjectWithId(BucketName, int(endpoint.ID), endpoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for an environment(endpoint).
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.connection.GetNextIdentifier(BucketName)
|
||||
// return service.connection.GetNextIdentifier(BucketName)
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package endpointgroup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -37,57 +33,60 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// EndpointGroup returns an environment(endpoint) group by ID.
|
||||
func (service *Service) EndpointGroup(ID portainer.EndpointGroupID) (*portainer.EndpointGroup, error) {
|
||||
var endpointGroup portainer.EndpointGroup
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &endpointGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &endpointGroup)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &endpointGroup, nil
|
||||
}
|
||||
|
||||
// UpdateEndpointGroup updates an environment(endpoint) group.
|
||||
func (service *Service) UpdateEndpointGroup(ID portainer.EndpointGroupID, endpointGroup *portainer.EndpointGroup) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, endpointGroup)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, endpointGroup)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEndpointGroup deletes an environment(endpoint) group.
|
||||
func (service *Service) DeleteEndpointGroup(ID portainer.EndpointGroupID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// EndpointGroups return an array containing all the environment(endpoint) groups.
|
||||
func (service *Service) EndpointGroups() ([]portainer.EndpointGroup, error) {
|
||||
var endpointGroups = make([]portainer.EndpointGroup, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.EndpointGroup{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
endpointGroup, ok := obj.(*portainer.EndpointGroup)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EndpointGroup object")
|
||||
return nil, fmt.Errorf("Failed to convert to EndpointGroup object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.EndpointGroup{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// endpointGroup, ok := obj.(*portainer.EndpointGroup)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EndpointGroup object")
|
||||
// return nil, fmt.Errorf("Failed to convert to EndpointGroup object: %s", obj)
|
||||
// }
|
||||
|
||||
endpointGroups = append(endpointGroups, *endpointGroup)
|
||||
// endpointGroups = append(endpointGroups, *endpointGroup)
|
||||
|
||||
return &portainer.EndpointGroup{}, nil
|
||||
})
|
||||
// return &portainer.EndpointGroup{}, nil
|
||||
// })
|
||||
|
||||
return endpointGroups, err
|
||||
return endpointGroups, nil
|
||||
}
|
||||
|
||||
// CreateEndpointGroup assign an ID to a new environment(endpoint) group and saves it.
|
||||
func (service *Service) Create(endpointGroup *portainer.EndpointGroup) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
endpointGroup.ID = portainer.EndpointGroupID(id)
|
||||
return int(endpointGroup.ID), endpointGroup
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// endpointGroup.ID = portainer.EndpointGroupID(id)
|
||||
// return int(endpointGroup.ID), endpointGroup
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package endpointrelation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -38,50 +34,53 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) EndpointRelations() ([]portainer.EndpointRelation, error) {
|
||||
var all = make([]portainer.EndpointRelation, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.EndpointRelation{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
r, ok := obj.(*portainer.EndpointRelation)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EndpointRelation object")
|
||||
return nil, fmt.Errorf("Failed to convert to EndpointRelation object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.EndpointRelation{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// r, ok := obj.(*portainer.EndpointRelation)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EndpointRelation object")
|
||||
// return nil, fmt.Errorf("Failed to convert to EndpointRelation object: %s", obj)
|
||||
// }
|
||||
|
||||
all = append(all, *r)
|
||||
// all = append(all, *r)
|
||||
|
||||
return &portainer.EndpointRelation{}, nil
|
||||
})
|
||||
// return &portainer.EndpointRelation{}, nil
|
||||
// })
|
||||
|
||||
return all, err
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// EndpointRelation returns a Environment(Endpoint) relation object by EndpointID
|
||||
func (service *Service) EndpointRelation(endpointID portainer.EndpointID) (*portainer.EndpointRelation, error) {
|
||||
var endpointRelation portainer.EndpointRelation
|
||||
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
// identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &endpointRelation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &endpointRelation)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &endpointRelation, nil
|
||||
}
|
||||
|
||||
// CreateEndpointRelation saves endpointRelation
|
||||
func (service *Service) Create(endpointRelation *portainer.EndpointRelation) error {
|
||||
return service.connection.CreateObjectWithId(BucketName, int(endpointRelation.EndpointID), endpointRelation)
|
||||
// return service.connection.CreateObjectWithId(BucketName, int(endpointRelation.EndpointID), endpointRelation)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEndpointRelation updates an Environment(Endpoint) relation object
|
||||
func (service *Service) UpdateEndpointRelation(EndpointID portainer.EndpointID, endpointRelation *portainer.EndpointRelation) error {
|
||||
identifier := service.connection.ConvertToKey(int(EndpointID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, endpointRelation)
|
||||
// identifier := service.connection.ConvertToKey(int(EndpointID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, endpointRelation)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEndpointRelation deletes an Environment(Endpoint) relation object
|
||||
func (service *Service) DeleteEndpointRelation(EndpointID portainer.EndpointID) error {
|
||||
identifier := service.connection.ConvertToKey(int(EndpointID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(EndpointID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package extension
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -37,12 +33,12 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Extension returns a extension by ID
|
||||
func (service *Service) Extension(ID portainer.ExtensionID) (*portainer.Extension, error) {
|
||||
var extension portainer.Extension
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &extension)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &extension, nil
|
||||
}
|
||||
@@ -51,31 +47,33 @@ func (service *Service) Extension(ID portainer.ExtensionID) (*portainer.Extensio
|
||||
func (service *Service) Extensions() ([]portainer.Extension, error) {
|
||||
var extensions = make([]portainer.Extension, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Extension{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
extension, ok := obj.(*portainer.Extension)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Extension object")
|
||||
return nil, fmt.Errorf("Failed to convert to Extension object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Extension{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// extension, ok := obj.(*portainer.Extension)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Extension object")
|
||||
// return nil, fmt.Errorf("Failed to convert to Extension object: %s", obj)
|
||||
// }
|
||||
|
||||
extensions = append(extensions, *extension)
|
||||
// extensions = append(extensions, *extension)
|
||||
|
||||
return &portainer.Extension{}, nil
|
||||
})
|
||||
// return &portainer.Extension{}, nil
|
||||
// })
|
||||
|
||||
return extensions, err
|
||||
return extensions, nil
|
||||
}
|
||||
|
||||
// Persist persists a extension inside the database.
|
||||
func (service *Service) Persist(extension *portainer.Extension) error {
|
||||
return service.connection.CreateObjectWithId(BucketName, int(extension.ID), extension)
|
||||
// return service.connection.CreateObjectWithId(BucketName, int(extension.ID), extension)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteExtension deletes a Extension.
|
||||
func (service *Service) DeleteExtension(ID portainer.ExtensionID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package fdoprofile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -38,58 +34,62 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) FDOProfiles() ([]portainer.FDOProfile, error) {
|
||||
var fdoProfiles = make([]portainer.FDOProfile, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.FDOProfile{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
fdoProfile, ok := obj.(*portainer.FDOProfile)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to FDOProfile object")
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.FDOProfile{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// fdoProfile, ok := obj.(*portainer.FDOProfile)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to FDOProfile object")
|
||||
|
||||
return nil, fmt.Errorf("Failed to convert to FDOProfile object: %s", obj)
|
||||
}
|
||||
fdoProfiles = append(fdoProfiles, *fdoProfile)
|
||||
return &portainer.FDOProfile{}, nil
|
||||
})
|
||||
// return nil, fmt.Errorf("Failed to convert to FDOProfile object: %s", obj)
|
||||
// }
|
||||
// fdoProfiles = append(fdoProfiles, *fdoProfile)
|
||||
// return &portainer.FDOProfile{}, nil
|
||||
// })
|
||||
|
||||
return fdoProfiles, err
|
||||
return fdoProfiles, nil
|
||||
}
|
||||
|
||||
// FDOProfile returns an FDO Profile by ID.
|
||||
func (service *Service) FDOProfile(ID portainer.FDOProfileID) (*portainer.FDOProfile, error) {
|
||||
var FDOProfile portainer.FDOProfile
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &FDOProfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &FDOProfile)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &FDOProfile, nil
|
||||
}
|
||||
|
||||
// Create assign an ID to a new FDO Profile and saves it.
|
||||
func (service *Service) Create(FDOProfile *portainer.FDOProfile) error {
|
||||
return service.connection.CreateObjectWithId(
|
||||
BucketName,
|
||||
int(FDOProfile.ID),
|
||||
FDOProfile,
|
||||
)
|
||||
// return service.connection.CreateObjectWithId(
|
||||
// BucketName,
|
||||
// int(FDOProfile.ID),
|
||||
// FDOProfile,
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates an FDO Profile.
|
||||
func (service *Service) Update(ID portainer.FDOProfileID, FDOProfile *portainer.FDOProfile) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, FDOProfile)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, FDOProfile)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete deletes an FDO Profile.
|
||||
func (service *Service) Delete(ID portainer.FDOProfileID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for a FDO Profile.
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.connection.GetNextIdentifier(BucketName)
|
||||
// return service.connection.GetNextIdentifier(BucketName)
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package helmuserrepository
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -38,67 +34,70 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) HelmUserRepositories() ([]portainer.HelmUserRepository, error) {
|
||||
var repos = make([]portainer.HelmUserRepository, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.HelmUserRepository{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
r, ok := obj.(*portainer.HelmUserRepository)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to HelmUserRepository object")
|
||||
return nil, fmt.Errorf("Failed to convert to HelmUserRepository object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.HelmUserRepository{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// r, ok := obj.(*portainer.HelmUserRepository)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to HelmUserRepository object")
|
||||
// return nil, fmt.Errorf("Failed to convert to HelmUserRepository object: %s", obj)
|
||||
// }
|
||||
|
||||
repos = append(repos, *r)
|
||||
// repos = append(repos, *r)
|
||||
|
||||
return &portainer.HelmUserRepository{}, nil
|
||||
})
|
||||
// return &portainer.HelmUserRepository{}, nil
|
||||
// })
|
||||
|
||||
return repos, err
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
// HelmUserRepositoryByUserID return an array containing all the HelmUserRepository objects where the specified userID is present.
|
||||
func (service *Service) HelmUserRepositoryByUserID(userID portainer.UserID) ([]portainer.HelmUserRepository, error) {
|
||||
var result = make([]portainer.HelmUserRepository, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.HelmUserRepository{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
record, ok := obj.(*portainer.HelmUserRepository)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to HelmUserRepository object")
|
||||
return nil, fmt.Errorf("Failed to convert to HelmUserRepository object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.HelmUserRepository{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// record, ok := obj.(*portainer.HelmUserRepository)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to HelmUserRepository object")
|
||||
// return nil, fmt.Errorf("Failed to convert to HelmUserRepository object: %s", obj)
|
||||
// }
|
||||
|
||||
if record.UserID == userID {
|
||||
result = append(result, *record)
|
||||
}
|
||||
// if record.UserID == userID {
|
||||
// result = append(result, *record)
|
||||
// }
|
||||
|
||||
return &portainer.HelmUserRepository{}, nil
|
||||
})
|
||||
// return &portainer.HelmUserRepository{}, nil
|
||||
// })
|
||||
|
||||
return result, err
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CreateHelmUserRepository creates a new HelmUserRepository object.
|
||||
func (service *Service) Create(record *portainer.HelmUserRepository) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
record.ID = portainer.HelmUserRepositoryID(id)
|
||||
return int(record.ID), record
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// record.ID = portainer.HelmUserRepositoryID(id)
|
||||
// return int(record.ID), record
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateHelmUserRepostory updates an registry.
|
||||
func (service *Service) UpdateHelmUserRepository(ID portainer.HelmUserRepositoryID, registry *portainer.HelmUserRepository) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, registry)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, registry)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteHelmUserRepository deletes an registry.
|
||||
func (service *Service) DeleteHelmUserRepository(ID portainer.HelmUserRepositoryID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package dataservices
|
||||
// "github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
@@ -18,13 +17,9 @@ type (
|
||||
Open() (newStore bool, err error)
|
||||
Init() error
|
||||
Close() error
|
||||
MigrateData() error
|
||||
Rollback(force bool) error
|
||||
CheckCurrentEdition() error
|
||||
BackupTo(w io.Writer) error
|
||||
Export(filename string) (err error)
|
||||
IsErrObjectNotFound(err error) bool
|
||||
|
||||
CheckCurrentEdition() error
|
||||
IsErrObjectNotFound(err error) bool
|
||||
CustomTemplate() CustomTemplateService
|
||||
EdgeGroup() EdgeGroupService
|
||||
EdgeJob() EdgeJobService
|
||||
@@ -60,7 +55,6 @@ type (
|
||||
Create(customTemplate *portainer.CustomTemplate) error
|
||||
UpdateCustomTemplate(ID portainer.CustomTemplateID, customTemplate *portainer.CustomTemplate) error
|
||||
DeleteCustomTemplate(ID portainer.CustomTemplateID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// EdgeGroupService represents a service to manage Edge groups
|
||||
@@ -70,7 +64,6 @@ type (
|
||||
Create(group *portainer.EdgeGroup) error
|
||||
UpdateEdgeGroup(ID portainer.EdgeGroupID, group *portainer.EdgeGroup) error
|
||||
DeleteEdgeGroup(ID portainer.EdgeGroupID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// EdgeJobService represents a service to manage Edge jobs
|
||||
@@ -81,7 +74,6 @@ type (
|
||||
UpdateEdgeJob(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error
|
||||
DeleteEdgeJob(ID portainer.EdgeJobID) error
|
||||
GetNextIdentifier() int
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
EdgeUpdateScheduleService interface {
|
||||
@@ -92,7 +84,6 @@ type (
|
||||
Create(edgeUpdateSchedule *edgetypes.UpdateSchedule) error
|
||||
Update(ID edgetypes.UpdateScheduleID, edgeUpdateSchedule *edgetypes.UpdateSchedule) error
|
||||
Delete(ID edgetypes.UpdateScheduleID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// EdgeStackService represents a service to manage Edge stacks
|
||||
@@ -103,7 +94,6 @@ type (
|
||||
UpdateEdgeStack(ID portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error
|
||||
DeleteEdgeStack(ID portainer.EdgeStackID) error
|
||||
GetNextIdentifier() int
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// EndpointService represents a service for managing environment(endpoint) data
|
||||
@@ -114,7 +104,6 @@ type (
|
||||
UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error
|
||||
DeleteEndpoint(ID portainer.EndpointID) error
|
||||
GetNextIdentifier() int
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// EndpointGroupService represents a service for managing environment(endpoint) group data
|
||||
@@ -124,7 +113,6 @@ type (
|
||||
Create(group *portainer.EndpointGroup) error
|
||||
UpdateEndpointGroup(ID portainer.EndpointGroupID, group *portainer.EndpointGroup) error
|
||||
DeleteEndpointGroup(ID portainer.EndpointGroupID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// EndpointRelationService represents a service for managing environment(endpoint) relations data
|
||||
@@ -134,7 +122,6 @@ type (
|
||||
Create(endpointRelation *portainer.EndpointRelation) error
|
||||
UpdateEndpointRelation(EndpointID portainer.EndpointID, endpointRelation *portainer.EndpointRelation) error
|
||||
DeleteEndpointRelation(EndpointID portainer.EndpointID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// FDOProfileService represents a service to manage FDO Profiles
|
||||
@@ -145,7 +132,6 @@ type (
|
||||
Update(ID portainer.FDOProfileID, FDOProfile *portainer.FDOProfile) error
|
||||
Delete(ID portainer.FDOProfileID) error
|
||||
GetNextIdentifier() int
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// HelmUserRepositoryService represents a service to manage HelmUserRepositories
|
||||
@@ -155,7 +141,6 @@ type (
|
||||
Create(record *portainer.HelmUserRepository) error
|
||||
UpdateHelmUserRepository(ID portainer.HelmUserRepositoryID, repository *portainer.HelmUserRepository) error
|
||||
DeleteHelmUserRepository(ID portainer.HelmUserRepositoryID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// JWTService represents a service for managing JWT tokens
|
||||
@@ -174,7 +159,6 @@ type (
|
||||
Create(registry *portainer.Registry) error
|
||||
UpdateRegistry(ID portainer.RegistryID, registry *portainer.Registry) error
|
||||
DeleteRegistry(ID portainer.RegistryID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// ResourceControlService represents a service for managing resource control data
|
||||
@@ -185,7 +169,6 @@ type (
|
||||
Create(rc *portainer.ResourceControl) error
|
||||
UpdateResourceControl(ID portainer.ResourceControlID, resourceControl *portainer.ResourceControl) error
|
||||
DeleteResourceControl(ID portainer.ResourceControlID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// RoleService represents a service for managing user roles
|
||||
@@ -194,7 +177,6 @@ type (
|
||||
Roles() ([]portainer.Role, error)
|
||||
Create(role *portainer.Role) error
|
||||
UpdateRole(ID portainer.RoleID, role *portainer.Role) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// APIKeyRepositoryService
|
||||
@@ -212,7 +194,6 @@ type (
|
||||
Settings() (*portainer.Settings, error)
|
||||
UpdateSettings(settings *portainer.Settings) error
|
||||
IsFeatureFlagEnabled(feature portainer.Feature) bool
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
SnapshotService interface {
|
||||
@@ -221,14 +202,12 @@ type (
|
||||
UpdateSnapshot(snapshot *portainer.Snapshot) error
|
||||
DeleteSnapshot(endpointID portainer.EndpointID) error
|
||||
Create(snapshot *portainer.Snapshot) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// SSLSettingsService represents a service for managing application settings
|
||||
SSLSettingsService interface {
|
||||
Settings() (*portainer.SSLSettings, error)
|
||||
UpdateSettings(settings *portainer.SSLSettings) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// StackService represents a service for managing stack data
|
||||
@@ -243,7 +222,6 @@ type (
|
||||
GetNextIdentifier() int
|
||||
StackByWebhookID(ID string) (*portainer.Stack, error)
|
||||
RefreshableStacks() ([]portainer.Stack, error)
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// TagService represents a service for managing tag data
|
||||
@@ -253,7 +231,6 @@ type (
|
||||
Create(tag *portainer.Tag) error
|
||||
UpdateTag(ID portainer.TagID, tag *portainer.Tag) error
|
||||
DeleteTag(ID portainer.TagID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// TeamService represents a service for managing user data
|
||||
@@ -264,7 +241,6 @@ type (
|
||||
Create(team *portainer.Team) error
|
||||
UpdateTeam(ID portainer.TeamID, team *portainer.Team) error
|
||||
DeleteTeam(ID portainer.TeamID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// TeamMembershipService represents a service for managing team membership data
|
||||
@@ -278,14 +254,12 @@ type (
|
||||
DeleteTeamMembership(ID portainer.TeamMembershipID) error
|
||||
DeleteTeamMembershipByUserID(userID portainer.UserID) error
|
||||
DeleteTeamMembershipByTeamID(teamID portainer.TeamID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// TunnelServerService represents a service for managing data associated to the tunnel server
|
||||
TunnelServerService interface {
|
||||
Info() (*portainer.TunnelServerInfo, error)
|
||||
UpdateInfo(info *portainer.TunnelServerInfo) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// UserService represents a service for managing user data
|
||||
@@ -297,7 +271,6 @@ type (
|
||||
Create(user *portainer.User) error
|
||||
UpdateUser(ID portainer.UserID, user *portainer.User) error
|
||||
DeleteUser(ID portainer.UserID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// VersionService represents a service for managing version data
|
||||
@@ -307,7 +280,6 @@ type (
|
||||
InstanceID() (string, error)
|
||||
StoreDBVersion(version int) error
|
||||
StoreInstanceID(ID string) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// WebhookService represents a service for managing webhook data.
|
||||
@@ -319,7 +291,6 @@ type (
|
||||
WebhookByResourceID(resourceID string) (*portainer.Webhook, error)
|
||||
WebhookByToken(token string) (*portainer.Webhook, error)
|
||||
DeleteWebhook(ID portainer.WebhookID) error
|
||||
BucketName() string
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -37,12 +33,12 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Registry returns an registry by ID.
|
||||
func (service *Service) Registry(ID portainer.RegistryID) (*portainer.Registry, error) {
|
||||
var registry portainer.Registry
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, ®istry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, ®istry)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return ®istry, nil
|
||||
}
|
||||
@@ -51,43 +47,46 @@ func (service *Service) Registry(ID portainer.RegistryID) (*portainer.Registry,
|
||||
func (service *Service) Registries() ([]portainer.Registry, error) {
|
||||
var registries = make([]portainer.Registry, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Registry{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
registry, ok := obj.(*portainer.Registry)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Registry object")
|
||||
return nil, fmt.Errorf("Failed to convert to Registry object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Registry{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// registry, ok := obj.(*portainer.Registry)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Registry object")
|
||||
// return nil, fmt.Errorf("Failed to convert to Registry object: %s", obj)
|
||||
// }
|
||||
|
||||
registries = append(registries, *registry)
|
||||
// registries = append(registries, *registry)
|
||||
|
||||
return &portainer.Registry{}, nil
|
||||
})
|
||||
// return &portainer.Registry{}, nil
|
||||
// })
|
||||
|
||||
return registries, err
|
||||
return registries, nil
|
||||
}
|
||||
|
||||
// CreateRegistry creates a new registry.
|
||||
func (service *Service) Create(registry *portainer.Registry) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
registry.ID = portainer.RegistryID(id)
|
||||
return int(registry.ID), registry
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// registry.ID = portainer.RegistryID(id)
|
||||
// return int(registry.ID), registry
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRegistry updates an registry.
|
||||
func (service *Service) UpdateRegistry(ID portainer.RegistryID, registry *portainer.Registry) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, registry)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, registry)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRegistry deletes an registry.
|
||||
func (service *Service) DeleteRegistry(ID portainer.RegistryID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package resourcecontrol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -37,12 +33,12 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// ResourceControl returns a ResourceControl object by ID
|
||||
func (service *Service) ResourceControl(ID portainer.ResourceControlID) (*portainer.ResourceControl, error) {
|
||||
var resourceControl portainer.ResourceControl
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &resourceControl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &resourceControl)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &resourceControl, nil
|
||||
}
|
||||
@@ -51,80 +47,83 @@ func (service *Service) ResourceControl(ID portainer.ResourceControlID) (*portai
|
||||
// to the main ResourceID or in SubResourceIDs. It also performs a check on the resource type. Return nil
|
||||
// if no ResourceControl was found.
|
||||
func (service *Service) ResourceControlByResourceIDAndType(resourceID string, resourceType portainer.ResourceControlType) (*portainer.ResourceControl, error) {
|
||||
var resourceControl *portainer.ResourceControl
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.ResourceControl{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
rc, ok := obj.(*portainer.ResourceControl)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to ResourceControl object")
|
||||
return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj)
|
||||
}
|
||||
// var resourceControl *portainer.ResourceControl
|
||||
// stop := fmt.Errorf("ok")
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.ResourceControl{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// rc, ok := obj.(*portainer.ResourceControl)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to ResourceControl object")
|
||||
// return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj)
|
||||
// }
|
||||
|
||||
if rc.ResourceID == resourceID && rc.Type == resourceType {
|
||||
resourceControl = rc
|
||||
return nil, stop
|
||||
}
|
||||
// if rc.ResourceID == resourceID && rc.Type == resourceType {
|
||||
// resourceControl = rc
|
||||
// return nil, stop
|
||||
// }
|
||||
|
||||
for _, subResourceID := range rc.SubResourceIDs {
|
||||
if subResourceID == resourceID {
|
||||
resourceControl = rc
|
||||
return nil, stop
|
||||
}
|
||||
}
|
||||
// for _, subResourceID := range rc.SubResourceIDs {
|
||||
// if subResourceID == resourceID {
|
||||
// resourceControl = rc
|
||||
// return nil, stop
|
||||
// }
|
||||
// }
|
||||
|
||||
return &portainer.ResourceControl{}, nil
|
||||
})
|
||||
if err == stop {
|
||||
return resourceControl, nil
|
||||
}
|
||||
// return &portainer.ResourceControl{}, nil
|
||||
// })
|
||||
// if err == stop {
|
||||
// return resourceControl, nil
|
||||
// }
|
||||
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ResourceControls returns all the ResourceControl objects
|
||||
func (service *Service) ResourceControls() ([]portainer.ResourceControl, error) {
|
||||
var rcs = make([]portainer.ResourceControl, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.ResourceControl{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
rc, ok := obj.(*portainer.ResourceControl)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to ResourceControl object")
|
||||
return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.ResourceControl{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// rc, ok := obj.(*portainer.ResourceControl)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to ResourceControl object")
|
||||
// return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj)
|
||||
// }
|
||||
|
||||
rcs = append(rcs, *rc)
|
||||
// rcs = append(rcs, *rc)
|
||||
|
||||
return &portainer.ResourceControl{}, nil
|
||||
})
|
||||
// return &portainer.ResourceControl{}, nil
|
||||
// })
|
||||
|
||||
return rcs, err
|
||||
return rcs, nil
|
||||
}
|
||||
|
||||
// CreateResourceControl creates a new ResourceControl object
|
||||
func (service *Service) Create(resourceControl *portainer.ResourceControl) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
resourceControl.ID = portainer.ResourceControlID(id)
|
||||
return int(resourceControl.ID), resourceControl
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// resourceControl.ID = portainer.ResourceControlID(id)
|
||||
// return int(resourceControl.ID), resourceControl
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateResourceControl saves a ResourceControl object.
|
||||
func (service *Service) UpdateResourceControl(ID portainer.ResourceControlID, resourceControl *portainer.ResourceControl) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, resourceControl)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, resourceControl)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteResourceControl deletes a ResourceControl object by ID
|
||||
func (service *Service) DeleteResourceControl(ID portainer.ResourceControlID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package role
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -37,12 +33,12 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Role returns a Role by ID
|
||||
func (service *Service) Role(ID portainer.RoleID) (*portainer.Role, error) {
|
||||
var set portainer.Role
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &set)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &set)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &set, nil
|
||||
}
|
||||
@@ -51,37 +47,39 @@ func (service *Service) Role(ID portainer.RoleID) (*portainer.Role, error) {
|
||||
func (service *Service) Roles() ([]portainer.Role, error) {
|
||||
var sets = make([]portainer.Role, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Role{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
set, ok := obj.(*portainer.Role)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Role object")
|
||||
return nil, fmt.Errorf("Failed to convert to Role object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Role{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// set, ok := obj.(*portainer.Role)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Role object")
|
||||
// return nil, fmt.Errorf("Failed to convert to Role object: %s", obj)
|
||||
// }
|
||||
|
||||
sets = append(sets, *set)
|
||||
// sets = append(sets, *set)
|
||||
|
||||
return &portainer.Role{}, nil
|
||||
})
|
||||
// return &portainer.Role{}, nil
|
||||
// })
|
||||
|
||||
return sets, err
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
// CreateRole creates a new Role.
|
||||
func (service *Service) Create(role *portainer.Role) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
role.ID = portainer.RoleID(id)
|
||||
return int(role.ID), role
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// role.ID = portainer.RoleID(id)
|
||||
// return int(role.ID), role
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRole updates a role.
|
||||
func (service *Service) UpdateRole(ID portainer.RoleID, role *portainer.Role) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, role)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, role)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package schedule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -37,48 +33,33 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Schedule returns a schedule by ID.
|
||||
func (service *Service) Schedule(ID portainer.ScheduleID) (*portainer.Schedule, error) {
|
||||
var schedule portainer.Schedule
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &schedule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &schedule)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &schedule, nil
|
||||
}
|
||||
|
||||
// UpdateSchedule updates a schedule.
|
||||
func (service *Service) UpdateSchedule(ID portainer.ScheduleID, schedule *portainer.Schedule) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, schedule)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, schedule)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteSchedule deletes a schedule.
|
||||
func (service *Service) DeleteSchedule(ID portainer.ScheduleID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Schedules return a array containing all the schedules.
|
||||
func (service *Service) Schedules() ([]portainer.Schedule, error) {
|
||||
var schedules = make([]portainer.Schedule, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Schedule{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
schedule, ok := obj.(*portainer.Schedule)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Schedule object")
|
||||
return nil, fmt.Errorf("Failed to convert to Schedule object: %s", obj)
|
||||
}
|
||||
|
||||
schedules = append(schedules, *schedule)
|
||||
|
||||
return &portainer.Schedule{}, nil
|
||||
})
|
||||
|
||||
return schedules, err
|
||||
return schedules, nil
|
||||
}
|
||||
|
||||
// SchedulesByJobType return a array containing all the schedules
|
||||
@@ -86,32 +67,15 @@ func (service *Service) Schedules() ([]portainer.Schedule, error) {
|
||||
func (service *Service) SchedulesByJobType(jobType portainer.JobType) ([]portainer.Schedule, error) {
|
||||
var schedules = make([]portainer.Schedule, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Schedule{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
schedule, ok := obj.(*portainer.Schedule)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Schedule object")
|
||||
return nil, fmt.Errorf("Failed to convert to Schedule object: %s", obj)
|
||||
}
|
||||
|
||||
if schedule.JobType == jobType {
|
||||
schedules = append(schedules, *schedule)
|
||||
}
|
||||
|
||||
return &portainer.Schedule{}, nil
|
||||
})
|
||||
|
||||
return schedules, err
|
||||
return schedules, nil
|
||||
}
|
||||
|
||||
// Create assign an ID to a new schedule and saves it.
|
||||
func (service *Service) CreateSchedule(schedule *portainer.Schedule) error {
|
||||
return service.connection.CreateObjectWithSetSequence(BucketName, int(schedule.ID), schedule)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for a schedule.
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.connection.GetNextIdentifier(BucketName)
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -35,29 +30,14 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) Settings() (*portainer.Settings, error) {
|
||||
var settings portainer.Settings
|
||||
|
||||
err := service.connection.GetObject(BucketName, []byte(settingsKey), &settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &settings, nil
|
||||
}
|
||||
|
||||
// UpdateSettings persists a Settings object.
|
||||
func (service *Service) UpdateSettings(settings *portainer.Settings) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(settingsKey), settings)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) IsFeatureFlagEnabled(feature portainer.Feature) bool {
|
||||
settings, err := service.Settings()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
featureFlagSetting, ok := settings.FeatureFlagSettings[feature]
|
||||
if ok {
|
||||
return featureFlagSetting
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package snapshot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -20,10 +17,6 @@ func (service *Service) BucketName() string {
|
||||
}
|
||||
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -32,12 +25,6 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
|
||||
func (service *Service) Snapshot(endpointID portainer.EndpointID) (*portainer.Snapshot, error) {
|
||||
var snapshot portainer.Snapshot
|
||||
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &snapshot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &snapshot, nil
|
||||
}
|
||||
@@ -45,32 +32,17 @@ func (service *Service) Snapshot(endpointID portainer.EndpointID) (*portainer.Sn
|
||||
func (service *Service) Snapshots() ([]portainer.Snapshot, error) {
|
||||
var snapshots = make([]portainer.Snapshot, 0)
|
||||
|
||||
err := service.connection.GetAllWithJsoniter(
|
||||
BucketName,
|
||||
&portainer.Snapshot{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
snapshot, ok := obj.(*portainer.Snapshot)
|
||||
if !ok {
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Snapshot object")
|
||||
return nil, fmt.Errorf("failed to convert to Snapshot object: %s", obj)
|
||||
}
|
||||
snapshots = append(snapshots, *snapshot)
|
||||
return &portainer.Snapshot{}, nil
|
||||
})
|
||||
|
||||
return snapshots, err
|
||||
return snapshots, nil
|
||||
}
|
||||
|
||||
func (service *Service) UpdateSnapshot(snapshot *portainer.Snapshot) error {
|
||||
identifier := service.connection.ConvertToKey(int(snapshot.EndpointID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, snapshot)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) DeleteSnapshot(endpointID portainer.EndpointID) error {
|
||||
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) Create(snapshot *portainer.Snapshot) error {
|
||||
return service.connection.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -35,15 +30,10 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) Settings() (*portainer.SSLSettings, error) {
|
||||
var settings portainer.SSLSettings
|
||||
|
||||
err := service.connection.GetObject(BucketName, []byte(key), &settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &settings, nil
|
||||
}
|
||||
|
||||
// UpdateSettings persists a SSLSettings object.
|
||||
func (service *Service) UpdateSettings(settings *portainer.SSLSettings) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(key), settings)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,10 +20,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -39,171 +29,56 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Stack returns a stack object by ID.
|
||||
func (service *Service) Stack(ID portainer.StackID) (*portainer.Stack, error) {
|
||||
var stack portainer.Stack
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stack, nil
|
||||
}
|
||||
|
||||
// StackByName returns a stack object by name.
|
||||
func (service *Service) StackByName(name string) (*portainer.Stack, error) {
|
||||
var s *portainer.Stack
|
||||
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
stack, ok := obj.(*portainer.Stack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
|
||||
return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj)
|
||||
}
|
||||
|
||||
if stack.Name == name {
|
||||
s = stack
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
})
|
||||
if err == stop {
|
||||
return s, nil
|
||||
}
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Stacks returns an array containing all the stacks with same name
|
||||
func (service *Service) StacksByName(name string) ([]portainer.Stack, error) {
|
||||
var stacks = make([]portainer.Stack, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
stack, ok := obj.(portainer.Stack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
|
||||
return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj)
|
||||
}
|
||||
|
||||
if stack.Name == name {
|
||||
stacks = append(stacks, stack)
|
||||
}
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
})
|
||||
|
||||
return stacks, err
|
||||
return stacks, nil
|
||||
}
|
||||
|
||||
// Stacks returns an array containing all the stacks.
|
||||
func (service *Service) Stacks() ([]portainer.Stack, error) {
|
||||
var stacks = make([]portainer.Stack, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
stack, ok := obj.(*portainer.Stack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
|
||||
return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj)
|
||||
}
|
||||
|
||||
stacks = append(stacks, *stack)
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
})
|
||||
|
||||
return stacks, err
|
||||
return stacks, nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for a stack.
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.connection.GetNextIdentifier(BucketName)
|
||||
return 0
|
||||
}
|
||||
|
||||
// CreateStack creates a new stack.
|
||||
func (service *Service) Create(stack *portainer.Stack) error {
|
||||
return service.connection.CreateObjectWithSetSequence(BucketName, int(stack.ID), stack)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateStack updates a stack.
|
||||
func (service *Service) UpdateStack(ID portainer.StackID, stack *portainer.Stack) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, stack)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteStack deletes a stack.
|
||||
func (service *Service) DeleteStack(ID portainer.StackID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StackByWebhookID returns a pointer to a stack object by webhook ID.
|
||||
// It returns nil, errors.ErrObjectNotFound if there's no stack associated with the webhook ID.
|
||||
func (service *Service) StackByWebhookID(id string) (*portainer.Stack, error) {
|
||||
var s *portainer.Stack
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
var ok bool
|
||||
s, ok = obj.(*portainer.Stack)
|
||||
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
}
|
||||
|
||||
if s.AutoUpdate != nil && strings.EqualFold(s.AutoUpdate.Webhook, id) {
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
})
|
||||
if err == stop {
|
||||
return s, nil
|
||||
}
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
|
||||
// RefreshableStacks returns stacks that are configured for a periodic update
|
||||
func (service *Service) RefreshableStacks() ([]portainer.Stack, error) {
|
||||
stacks := make([]portainer.Stack, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
stack, ok := obj.(*portainer.Stack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
|
||||
return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj)
|
||||
}
|
||||
|
||||
if stack.AutoUpdate != nil && stack.AutoUpdate.Interval != "" {
|
||||
stacks = append(stacks, *stack)
|
||||
}
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
})
|
||||
|
||||
return stacks, err
|
||||
return stacks, nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package tag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,11 +20,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -37,57 +28,26 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Tags return an array containing all the tags.
|
||||
func (service *Service) Tags() ([]portainer.Tag, error) {
|
||||
var tags = make([]portainer.Tag, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Tag{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
tag, ok := obj.(*portainer.Tag)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Tag object")
|
||||
return nil, fmt.Errorf("Failed to convert to Tag object: %s", obj)
|
||||
}
|
||||
|
||||
tags = append(tags, *tag)
|
||||
|
||||
return &portainer.Tag{}, nil
|
||||
})
|
||||
|
||||
return tags, err
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
// Tag returns a tag by ID.
|
||||
func (service *Service) Tag(ID portainer.TagID) (*portainer.Tag, error) {
|
||||
var tag portainer.Tag
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tag, nil
|
||||
}
|
||||
|
||||
// CreateTag creates a new tag.
|
||||
func (service *Service) Create(tag *portainer.Tag) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
tag.ID = portainer.TagID(id)
|
||||
return int(tag.ID), tag
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateTag updates a tag.
|
||||
func (service *Service) UpdateTag(ID portainer.TagID, tag *portainer.Tag) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTag deletes a tag.
|
||||
func (service *Service) DeleteTag(ID portainer.TagID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package team
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,11 +20,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -39,89 +28,31 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Team returns a Team by ID
|
||||
func (service *Service) Team(ID portainer.TeamID) (*portainer.Team, error) {
|
||||
var team portainer.Team
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &team)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &team, nil
|
||||
}
|
||||
|
||||
// TeamByName returns a team by name.
|
||||
func (service *Service) TeamByName(name string) (*portainer.Team, error) {
|
||||
var t *portainer.Team
|
||||
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Team{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
team, ok := obj.(*portainer.Team)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Team object")
|
||||
return nil, fmt.Errorf("Failed to convert to Team object: %s", obj)
|
||||
}
|
||||
|
||||
if strings.EqualFold(team.Name, name) {
|
||||
t = team
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.Team{}, nil
|
||||
})
|
||||
if err == stop {
|
||||
return t, nil
|
||||
}
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Teams return an array containing all the teams.
|
||||
func (service *Service) Teams() ([]portainer.Team, error) {
|
||||
var teams = make([]portainer.Team, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Team{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
team, ok := obj.(*portainer.Team)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Team object")
|
||||
return nil, fmt.Errorf("Failed to convert to Team object: %s", obj)
|
||||
}
|
||||
|
||||
teams = append(teams, *team)
|
||||
|
||||
return &portainer.Team{}, nil
|
||||
})
|
||||
|
||||
return teams, err
|
||||
return teams, nil
|
||||
}
|
||||
|
||||
// UpdateTeam saves a Team.
|
||||
func (service *Service) UpdateTeam(ID portainer.TeamID, team *portainer.Team) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, team)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTeam creates a new Team.
|
||||
func (service *Service) Create(team *portainer.Team) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
team.ID = portainer.TeamID(id)
|
||||
return int(team.ID), team
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTeam deletes a Team.
|
||||
func (service *Service) DeleteTeam(ID portainer.TeamID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package teammembership
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,11 +20,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -37,145 +28,48 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// TeamMembership returns a TeamMembership object by ID
|
||||
func (service *Service) TeamMembership(ID portainer.TeamMembershipID) (*portainer.TeamMembership, error) {
|
||||
var membership portainer.TeamMembership
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &membership)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &membership, nil
|
||||
}
|
||||
|
||||
// TeamMemberships return an array containing all the TeamMembership objects.
|
||||
func (service *Service) TeamMemberships() ([]portainer.TeamMembership, error) {
|
||||
var memberships = make([]portainer.TeamMembership, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
membership, ok := obj.(*portainer.TeamMembership)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object")
|
||||
return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
}
|
||||
|
||||
memberships = append(memberships, *membership)
|
||||
|
||||
return &portainer.TeamMembership{}, nil
|
||||
})
|
||||
|
||||
return memberships, err
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
// TeamMembershipsByUserID return an array containing all the TeamMembership objects where the specified userID is present.
|
||||
func (service *Service) TeamMembershipsByUserID(userID portainer.UserID) ([]portainer.TeamMembership, error) {
|
||||
var memberships = make([]portainer.TeamMembership, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
membership, ok := obj.(*portainer.TeamMembership)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object")
|
||||
return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
}
|
||||
|
||||
if membership.UserID == userID {
|
||||
memberships = append(memberships, *membership)
|
||||
}
|
||||
|
||||
return &portainer.TeamMembership{}, nil
|
||||
})
|
||||
|
||||
return memberships, err
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
// TeamMembershipsByTeamID return an array containing all the TeamMembership objects where the specified teamID is present.
|
||||
func (service *Service) TeamMembershipsByTeamID(teamID portainer.TeamID) ([]portainer.TeamMembership, error) {
|
||||
var memberships = make([]portainer.TeamMembership, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
membership, ok := obj.(*portainer.TeamMembership)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object")
|
||||
return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
}
|
||||
|
||||
if membership.TeamID == teamID {
|
||||
memberships = append(memberships, *membership)
|
||||
}
|
||||
|
||||
return &portainer.TeamMembership{}, nil
|
||||
})
|
||||
|
||||
return memberships, err
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
// UpdateTeamMembership saves a TeamMembership object.
|
||||
func (service *Service) UpdateTeamMembership(ID portainer.TeamMembershipID, membership *portainer.TeamMembership) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, membership)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTeamMembership creates a new TeamMembership object.
|
||||
func (service *Service) Create(membership *portainer.TeamMembership) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
membership.ID = portainer.TeamMembershipID(id)
|
||||
return int(membership.ID), membership
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTeamMembership deletes a TeamMembership object.
|
||||
func (service *Service) DeleteTeamMembership(ID portainer.TeamMembershipID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTeamMembershipByUserID deletes all the TeamMembership object associated to a UserID.
|
||||
func (service *Service) DeleteTeamMembershipByUserID(userID portainer.UserID) error {
|
||||
return service.connection.DeleteAllObjects(
|
||||
BucketName,
|
||||
func(obj interface{}) (id int, ok bool) {
|
||||
membership, ok := obj.(portainer.TeamMembership)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object")
|
||||
//return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
return -1, false
|
||||
}
|
||||
|
||||
if membership.UserID == userID {
|
||||
return int(membership.ID), true
|
||||
}
|
||||
|
||||
return -1, false
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTeamMembershipByTeamID deletes all the TeamMembership object associated to a TeamID.
|
||||
func (service *Service) DeleteTeamMembershipByTeamID(teamID portainer.TeamID) error {
|
||||
return service.connection.DeleteAllObjects(
|
||||
BucketName,
|
||||
func(obj interface{}) (id int, ok bool) {
|
||||
membership, ok := obj.(portainer.TeamMembership)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object")
|
||||
//return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
return -1, false
|
||||
}
|
||||
|
||||
if membership.TeamID == teamID {
|
||||
return int(membership.ID), true
|
||||
}
|
||||
|
||||
return -1, false
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -35,15 +30,10 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) Info() (*portainer.TunnelServerInfo, error) {
|
||||
var info portainer.TunnelServerInfo
|
||||
|
||||
err := service.connection.GetObject(BucketName, []byte(infoKey), &info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
// UpdateInfo persists a TunnelServerInfo object.
|
||||
func (service *Service) UpdateInfo(settings *portainer.TunnelServerInfo) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(infoKey), settings)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,11 +20,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -39,120 +28,38 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// User returns a user by ID
|
||||
func (service *Service) User(ID portainer.UserID) (*portainer.User, error) {
|
||||
var user portainer.User
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// UserByUsername returns a user by username.
|
||||
func (service *Service) UserByUsername(username string) (*portainer.User, error) {
|
||||
var u *portainer.User
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.User{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
user, ok := obj.(*portainer.User)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to User object")
|
||||
|
||||
return nil, fmt.Errorf("Failed to convert to User object: %s", obj)
|
||||
}
|
||||
|
||||
if strings.EqualFold(user.Username, username) {
|
||||
u = user
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.User{}, nil
|
||||
})
|
||||
|
||||
if err == stop {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Users return an array containing all the users.
|
||||
func (service *Service) Users() ([]portainer.User, error) {
|
||||
var users = make([]portainer.User, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.User{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
user, ok := obj.(*portainer.User)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to User object")
|
||||
|
||||
return nil, fmt.Errorf("Failed to convert to User object: %s", obj)
|
||||
}
|
||||
|
||||
users = append(users, *user)
|
||||
|
||||
return &portainer.User{}, nil
|
||||
})
|
||||
|
||||
return users, err
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// UsersByRole return an array containing all the users with the specified role.
|
||||
func (service *Service) UsersByRole(role portainer.UserRole) ([]portainer.User, error) {
|
||||
var users = make([]portainer.User, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.User{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
user, ok := obj.(*portainer.User)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to User object")
|
||||
|
||||
return nil, fmt.Errorf("Failed to convert to User object: %s", obj)
|
||||
}
|
||||
|
||||
if user.Role == role {
|
||||
users = append(users, *user)
|
||||
}
|
||||
|
||||
return &portainer.User{}, nil
|
||||
})
|
||||
|
||||
return users, err
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// UpdateUser saves a user.
|
||||
func (service *Service) UpdateUser(ID portainer.UserID, user *portainer.User) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
user.Username = strings.ToLower(user.Username)
|
||||
return service.connection.UpdateObject(BucketName, identifier, user)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateUser creates a new user.
|
||||
func (service *Service) Create(user *portainer.User) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
user.ID = portainer.UserID(id)
|
||||
user.Username = strings.ToLower(user.Username)
|
||||
|
||||
return int(user.ID), user
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteUser deletes a user.
|
||||
func (service *Service) DeleteUser(ID portainer.UserID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,15 +4,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "version"
|
||||
versionKey = "DB_VERSION"
|
||||
instanceKey = "INSTANCE_ID"
|
||||
editionKey = "EDITION"
|
||||
updatingKey = "DB_UPDATING"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
)
|
||||
|
||||
// Service represents a service to manage stored versions.
|
||||
@@ -20,17 +12,8 @@ type Service struct {
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
func (service *Service) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -38,22 +21,24 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
|
||||
// DBVersion retrieves the stored database version.
|
||||
func (service *Service) DBVersion() (int, error) {
|
||||
var version string
|
||||
err := service.connection.GetObject(BucketName, []byte(versionKey), &version)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
db := service.connection.GetDB()
|
||||
var version models.Version
|
||||
tx := db.First(&version, `key = ?`, models.VersionKey)
|
||||
if tx.Error != nil {
|
||||
return 0, tx.Error
|
||||
}
|
||||
return strconv.Atoi(version)
|
||||
return strconv.Atoi(version.Value)
|
||||
}
|
||||
|
||||
// Edition retrieves the stored portainer edition.
|
||||
func (service *Service) Edition() (portainer.SoftwareEdition, error) {
|
||||
var edition string
|
||||
err := service.connection.GetObject(BucketName, []byte(editionKey), &edition)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
db := service.connection.GetDB()
|
||||
var version models.Version
|
||||
tx := db.First(&version, `key = ?`, models.EditionKey)
|
||||
if tx.Error != nil {
|
||||
return 0, tx.Error
|
||||
}
|
||||
e, err := strconv.Atoi(edition)
|
||||
e, err := strconv.Atoi(version.Value)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -61,31 +46,54 @@ func (service *Service) Edition() (portainer.SoftwareEdition, error) {
|
||||
}
|
||||
|
||||
// StoreDBVersion store the database version.
|
||||
func (service *Service) StoreDBVersion(version int) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(versionKey), strconv.Itoa(version))
|
||||
func (service *Service) StoreDBVersion(v int) error {
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Model(&models.Version{}).Where("key = ?", models.VersionKey).Update("value", strconv.FormatInt(int64(v), 10)).Limit(1)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsUpdating retrieves the database updating status.
|
||||
func (service *Service) IsUpdating() (bool, error) {
|
||||
var isUpdating bool
|
||||
err := service.connection.GetObject(BucketName, []byte(updatingKey), &isUpdating)
|
||||
return isUpdating, err
|
||||
db := service.connection.GetDB()
|
||||
var version models.Version
|
||||
tx := db.First(&version, `key = ?`, models.UpdatingKey)
|
||||
if tx.Error != nil {
|
||||
return false, tx.Error
|
||||
}
|
||||
return version.Value == "true", nil
|
||||
}
|
||||
|
||||
// StoreIsUpdating store the database updating status.
|
||||
func (service *Service) StoreIsUpdating(isUpdating bool) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(updatingKey), isUpdating)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Model(&models.Version{}).Where("key = ?", models.UpdatingKey).Update("value", strconv.FormatBool(isUpdating)).Limit(1)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstanceID retrieves the stored instance ID.
|
||||
func (service *Service) InstanceID() (string, error) {
|
||||
var id string
|
||||
err := service.connection.GetObject(BucketName, []byte(instanceKey), &id)
|
||||
return id, err
|
||||
db := service.connection.GetDB()
|
||||
var version models.Version
|
||||
tx := db.First(&version, `key = ?`, models.InstanceKey)
|
||||
if tx.Error != nil {
|
||||
return "", tx.Error
|
||||
}
|
||||
return version.Value, nil
|
||||
}
|
||||
|
||||
// StoreInstanceID store the instance ID.
|
||||
func (service *Service) StoreInstanceID(ID string) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(instanceKey), ID)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.FirstOrCreate(&models.Version{Key: models.InstanceKey, Value: ID})
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,11 +20,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -38,125 +28,36 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Webhooks returns an array of all webhooks
|
||||
func (service *Service) Webhooks() ([]portainer.Webhook, error) {
|
||||
var webhooks = make([]portainer.Webhook, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Webhook{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
webhook, ok := obj.(*portainer.Webhook)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Webhook object")
|
||||
return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj)
|
||||
}
|
||||
|
||||
webhooks = append(webhooks, *webhook)
|
||||
|
||||
return &portainer.Webhook{}, nil
|
||||
})
|
||||
|
||||
return webhooks, err
|
||||
return webhooks, nil
|
||||
}
|
||||
|
||||
// Webhook returns a webhook by ID.
|
||||
func (service *Service) Webhook(ID portainer.WebhookID) (*portainer.Webhook, error) {
|
||||
var webhook portainer.Webhook
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &webhook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &webhook, nil
|
||||
}
|
||||
|
||||
// WebhookByResourceID returns a webhook by the ResourceID it is associated with.
|
||||
func (service *Service) WebhookByResourceID(ID string) (*portainer.Webhook, error) {
|
||||
var w *portainer.Webhook
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Webhook{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
webhook, ok := obj.(*portainer.Webhook)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Webhook object")
|
||||
|
||||
return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj)
|
||||
}
|
||||
|
||||
if webhook.ResourceID == ID {
|
||||
w = webhook
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.Webhook{}, nil
|
||||
})
|
||||
|
||||
if err == stop {
|
||||
return w, nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// WebhookByToken returns a webhook by the random token it is associated with.
|
||||
func (service *Service) WebhookByToken(token string) (*portainer.Webhook, error) {
|
||||
var w *portainer.Webhook
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Webhook{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
webhook, ok := obj.(*portainer.Webhook)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Webhook object")
|
||||
|
||||
return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj)
|
||||
}
|
||||
|
||||
if webhook.Token == token {
|
||||
w = webhook
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.Webhook{}, nil
|
||||
})
|
||||
|
||||
if err == stop {
|
||||
return w, nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// DeleteWebhook deletes a webhook.
|
||||
func (service *Service) DeleteWebhook(ID portainer.WebhookID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateWebhook assign an ID to a new webhook and saves it.
|
||||
func (service *Service) Create(webhook *portainer.Webhook) error {
|
||||
return service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
webhook.ID = portainer.WebhookID(id)
|
||||
return int(webhook.ID), webhook
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateWebhook update a webhook.
|
||||
func (service *Service) UpdateWebhook(ID portainer.WebhookID, webhook *portainer.Webhook) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, webhook)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var backupDefaults = struct {
|
||||
backupDir string
|
||||
commonDir string
|
||||
}{
|
||||
"backups",
|
||||
"common",
|
||||
}
|
||||
|
||||
//
|
||||
// Backup Helpers
|
||||
//
|
||||
|
||||
// createBackupFolders create initial folders for backups
|
||||
func (store *Store) createBackupFolders() {
|
||||
// create common dir
|
||||
commonDir := store.commonBackupDir()
|
||||
if exists, _ := store.fileService.FileExists(commonDir); !exists {
|
||||
if err := os.MkdirAll(commonDir, 0700); err != nil {
|
||||
log.Error().Err(err).Msg("error while creating common backup folder")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (store *Store) databasePath() string {
|
||||
return store.connection.GetDatabaseFilePath()
|
||||
}
|
||||
|
||||
func (store *Store) commonBackupDir() string {
|
||||
return path.Join(store.connection.GetStorePath(), backupDefaults.backupDir, backupDefaults.commonDir)
|
||||
}
|
||||
|
||||
func (store *Store) copyDBFile(from string, to string) error {
|
||||
log.Info().Str("from", from).Str("to", to).Msg("copying DB file")
|
||||
|
||||
err := store.fileService.Copy(from, to, true)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// BackupOptions provide a helper to inject backup options
|
||||
type BackupOptions struct {
|
||||
Version int // I can't find this used for anything other than a filename
|
||||
BackupDir string
|
||||
BackupFileName string
|
||||
BackupPath string
|
||||
}
|
||||
|
||||
// getBackupRestoreOptions returns options to store db at common backup dir location; used by:
|
||||
// - db backup prior to version upgrade
|
||||
// - db rollback
|
||||
func getBackupRestoreOptions(backupDir string) *BackupOptions {
|
||||
return &BackupOptions{
|
||||
BackupDir: backupDir, //connection.commonBackupDir(),
|
||||
BackupFileName: beforePortainerVersionUpgradeBackup,
|
||||
}
|
||||
}
|
||||
|
||||
// Backup current database with default options
|
||||
func (store *Store) Backup() (string, error) {
|
||||
return store.backupWithOptions(nil)
|
||||
}
|
||||
|
||||
func (store *Store) setupOptions(options *BackupOptions) *BackupOptions {
|
||||
if options == nil {
|
||||
options = &BackupOptions{}
|
||||
}
|
||||
if options.Version == 0 {
|
||||
version, err := store.version()
|
||||
if err != nil {
|
||||
version = 0
|
||||
}
|
||||
options.Version = version
|
||||
}
|
||||
if options.BackupDir == "" {
|
||||
options.BackupDir = store.commonBackupDir()
|
||||
}
|
||||
if options.BackupFileName == "" {
|
||||
options.BackupFileName = fmt.Sprintf("%s.%s.%s", store.connection.GetDatabaseFileName(), fmt.Sprintf("%03d", options.Version), time.Now().Format("20060102150405"))
|
||||
}
|
||||
if options.BackupPath == "" {
|
||||
options.BackupPath = path.Join(options.BackupDir, options.BackupFileName)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// BackupWithOptions backup current database with options
|
||||
func (store *Store) backupWithOptions(options *BackupOptions) (string, error) {
|
||||
log.Info().Msg("creating DB backup")
|
||||
|
||||
store.createBackupFolders()
|
||||
|
||||
options = store.setupOptions(options)
|
||||
dbPath := store.databasePath()
|
||||
|
||||
if err := store.Close(); err != nil {
|
||||
return options.BackupPath, fmt.Errorf(
|
||||
"error closing datastore before creating backup: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
if err := store.copyDBFile(dbPath, options.BackupPath); err != nil {
|
||||
return options.BackupPath, err
|
||||
}
|
||||
|
||||
if _, err := store.Open(); err != nil {
|
||||
return options.BackupPath, fmt.Errorf(
|
||||
"error opening datastore after creating backup: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return options.BackupPath, nil
|
||||
}
|
||||
|
||||
// RestoreWithOptions previously saved backup for the current Edition with options
|
||||
// Restore strategies:
|
||||
// - default: restore latest from current edition
|
||||
// - restore a specific
|
||||
func (store *Store) restoreWithOptions(options *BackupOptions) error {
|
||||
options = store.setupOptions(options)
|
||||
|
||||
// Check if backup file exist before restoring
|
||||
_, err := os.Stat(options.BackupPath)
|
||||
if os.IsNotExist(err) {
|
||||
log.Error().Str("path", options.BackupPath).Err(err).Msg("backup file to restore does not exist %s")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.Close()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error while closing store before restore")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Msg("restoring DB backup")
|
||||
err = store.copyDBFile(options.BackupPath, store.databasePath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = store.Open()
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveWithOptions removes backup database based on supplied options
|
||||
func (store *Store) removeWithOptions(options *BackupOptions) error {
|
||||
log.Info().Msg("removing DB backup")
|
||||
|
||||
options = store.setupOptions(options)
|
||||
_, err := os.Stat(options.BackupPath)
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
log.Error().Str("path", options.BackupPath).Err(err).Msg("backup file to remove does not exist")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Str("path", options.BackupPath).Msg("removing DB file")
|
||||
err = os.Remove(options.BackupPath)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
func TestCreateBackupFolders(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
defer teardown()
|
||||
|
||||
connection := store.GetConnection()
|
||||
backupPath := path.Join(connection.GetStorePath(), backupDefaults.backupDir)
|
||||
|
||||
if isFileExist(backupPath) {
|
||||
t.Error("Expect backups folder to not exist")
|
||||
}
|
||||
|
||||
store.createBackupFolders()
|
||||
if !isFileExist(backupPath) {
|
||||
t.Error("Expect backups folder to exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreCreation(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, true, true)
|
||||
defer teardown()
|
||||
|
||||
if store == nil {
|
||||
t.Error("Expect to create a store")
|
||||
}
|
||||
|
||||
if store.CheckCurrentEdition() != nil {
|
||||
t.Error("Expect to get CE Edition")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackup(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, true, true)
|
||||
connection := store.GetConnection()
|
||||
defer teardown()
|
||||
|
||||
t.Run("Backup should create default db backup", func(t *testing.T) {
|
||||
store.VersionService.StoreDBVersion(portainer.DBVersion)
|
||||
store.backupWithOptions(nil)
|
||||
|
||||
backupFileName := path.Join(connection.GetStorePath(), "backups", "common", fmt.Sprintf("portainer.edb.%03d.*", portainer.DBVersion))
|
||||
if !isFileExist(backupFileName) {
|
||||
t.Errorf("Expect backup file to be created %s", backupFileName)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BackupWithOption should create a name specific backup at common path", func(t *testing.T) {
|
||||
store.backupWithOptions(&BackupOptions{
|
||||
BackupFileName: beforePortainerVersionUpgradeBackup,
|
||||
BackupDir: store.commonBackupDir(),
|
||||
})
|
||||
backupFileName := path.Join(connection.GetStorePath(), "backups", "common", beforePortainerVersionUpgradeBackup)
|
||||
if !isFileExist(backupFileName) {
|
||||
t.Errorf("Expect backup file to be created %s", backupFileName)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRemoveWithOptions(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, true, true)
|
||||
defer teardown()
|
||||
|
||||
t.Run("successfully removes file if existent", func(t *testing.T) {
|
||||
store.createBackupFolders()
|
||||
options := &BackupOptions{
|
||||
BackupDir: store.commonBackupDir(),
|
||||
BackupFileName: "test.txt",
|
||||
}
|
||||
|
||||
filePath := path.Join(options.BackupDir, options.BackupFileName)
|
||||
f, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
t.Fatalf("file should be created; err=%s", err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
err = store.removeWithOptions(options)
|
||||
if err != nil {
|
||||
t.Errorf("RemoveWithOptions should successfully remove file; err=%v", err)
|
||||
}
|
||||
|
||||
if isFileExist(f.Name()) {
|
||||
t.Errorf("RemoveWithOptions should successfully remove file; file=%s", f.Name())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fails to removes file if non-existent", func(t *testing.T) {
|
||||
options := &BackupOptions{
|
||||
BackupDir: store.commonBackupDir(),
|
||||
BackupFileName: "test.txt",
|
||||
}
|
||||
|
||||
err := store.removeWithOptions(options)
|
||||
if err == nil {
|
||||
t.Error("RemoveWithOptions should fail for non-existent file")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,16 +1,9 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
portainerErrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func (store *Store) version() (int, error) {
|
||||
@@ -41,18 +34,6 @@ func NewStore(storePath string, fileService portainer.FileService, connection po
|
||||
func (store *Store) Open() (newStore bool, err error) {
|
||||
newStore = true
|
||||
|
||||
encryptionReq, err := store.connection.NeedsEncryptionMigration()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if encryptionReq {
|
||||
err = store.encryptDB()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
err = store.connection.Open()
|
||||
if err != nil {
|
||||
return newStore, err
|
||||
@@ -64,20 +45,20 @@ func (store *Store) Open() (newStore bool, err error) {
|
||||
}
|
||||
|
||||
// if we have DBVersion in the database then ensure we flag this as NOT a new store
|
||||
version, err := store.VersionService.DBVersion()
|
||||
if err != nil {
|
||||
if store.IsErrObjectNotFound(err) {
|
||||
return newStore, nil
|
||||
}
|
||||
// version, err := store.VersionService.DBVersion()
|
||||
// if err != nil {
|
||||
// if store.IsErrObjectNotFound(err) {
|
||||
// return newStore, nil
|
||||
// }
|
||||
|
||||
return newStore, err
|
||||
}
|
||||
// return newStore, err
|
||||
// }
|
||||
|
||||
if version > 0 {
|
||||
log.Debug().Int("version", version).Msg("opened existing store")
|
||||
// if version > 0 {
|
||||
// log.Debug().Int("version", version).Msg("opened existing store")
|
||||
|
||||
return false, nil
|
||||
}
|
||||
// return false, nil
|
||||
// }
|
||||
|
||||
return newStore, nil
|
||||
}
|
||||
@@ -86,12 +67,6 @@ func (store *Store) Close() error {
|
||||
return store.connection.Close()
|
||||
}
|
||||
|
||||
// BackupTo backs up db to a provided writer.
|
||||
// It does hot backup and doesn't block other database reads and writes
|
||||
func (store *Store) BackupTo(w io.Writer) error {
|
||||
return store.connection.BackupTo(w)
|
||||
}
|
||||
|
||||
// CheckCurrentEdition checks if current edition is community edition
|
||||
func (store *Store) CheckCurrentEdition() error {
|
||||
if store.edition() != portainer.PortainerCE {
|
||||
@@ -102,77 +77,5 @@ func (store *Store) CheckCurrentEdition() error {
|
||||
|
||||
// TODO: move the use of this to dataservices.IsErrObjectNotFound()?
|
||||
func (store *Store) IsErrObjectNotFound(e error) bool {
|
||||
return e == portainerErrors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
func (store *Store) Rollback(force bool) error {
|
||||
return store.connectionRollback(force)
|
||||
}
|
||||
|
||||
func (store *Store) encryptDB() error {
|
||||
store.connection.SetEncrypted(false)
|
||||
err := store.connection.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.initServices()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The DB is not currently encrypted. First save the encrypted db filename
|
||||
oldFilename := store.connection.GetDatabaseFilePath()
|
||||
log.Info().Msg("encrypting database")
|
||||
|
||||
// export file path for backup
|
||||
exportFilename := path.Join(store.databasePath() + "." + fmt.Sprintf("backup-%d.json", time.Now().Unix()))
|
||||
|
||||
log.Info().Str("filename", exportFilename).Msg("exporting database backup")
|
||||
|
||||
err = store.Export(exportFilename)
|
||||
if err != nil {
|
||||
log.Error().Str("filename", exportFilename).Err(err).Msg("failed to export")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Msg("database backup exported")
|
||||
|
||||
// Close existing un-encrypted db so that we can delete the file later
|
||||
store.connection.Close()
|
||||
|
||||
// Tell the db layer to create an encrypted db when opened
|
||||
store.connection.SetEncrypted(true)
|
||||
store.connection.Open()
|
||||
|
||||
// We have to init services before import
|
||||
err = store.initServices()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.Import(exportFilename)
|
||||
if err != nil {
|
||||
// Remove the new encrypted file that we failed to import
|
||||
os.Remove(store.connection.GetDatabaseFilePath())
|
||||
log.Fatal().Err(portainerErrors.ErrDBImportFailed).Msg("")
|
||||
}
|
||||
|
||||
err = os.Remove(oldFilename)
|
||||
if err != nil {
|
||||
log.Error().Msg("failed to remove the un-encrypted db file")
|
||||
}
|
||||
|
||||
err = os.Remove(exportFilename)
|
||||
if err != nil {
|
||||
log.Error().Msg("failed to remove the json backup file")
|
||||
}
|
||||
|
||||
// Close db connection
|
||||
store.connection.Close()
|
||||
|
||||
log.Info().Msg("database successfully encrypted")
|
||||
|
||||
return nil
|
||||
return e == gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
@@ -1,417 +0,0 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/dchest/uniuri"
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/chisel"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
adminUsername = "admin"
|
||||
adminPassword = "password"
|
||||
standardUsername = "standard"
|
||||
standardPassword = "password"
|
||||
agentOnDockerEnvironmentUrl = "tcp://192.168.167.207:30775"
|
||||
edgeAgentOnKubernetesEnvironmentUrl = "tcp://192.168.167.207"
|
||||
kubernetesLocalEnvironmentUrl = "https://kubernetes.default.svc"
|
||||
)
|
||||
|
||||
// TestStoreFull an eventually comprehensive set of tests for the Store.
|
||||
// The idea is what we write to the store, we should read back.
|
||||
func TestStoreFull(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, true, true)
|
||||
defer teardown()
|
||||
|
||||
testCases := map[string]func(t *testing.T){
|
||||
"User Accounts": func(t *testing.T) {
|
||||
store.testUserAccounts(t)
|
||||
},
|
||||
"Environments": func(t *testing.T) {
|
||||
store.testEnvironments(t)
|
||||
},
|
||||
"Settings": func(t *testing.T) {
|
||||
store.testSettings(t)
|
||||
},
|
||||
"SSL Settings": func(t *testing.T) {
|
||||
store.testSSLSettings(t)
|
||||
},
|
||||
"Tunnel Server": func(t *testing.T) {
|
||||
store.testTunnelServer(t)
|
||||
},
|
||||
"Custom Templates": func(t *testing.T) {
|
||||
store.testCustomTemplates(t)
|
||||
},
|
||||
"Registries": func(t *testing.T) {
|
||||
store.testRegistries(t)
|
||||
},
|
||||
"Resource Control": func(t *testing.T) {
|
||||
store.testResourceControl(t)
|
||||
},
|
||||
"Schedules": func(t *testing.T) {
|
||||
store.testSchedules(t)
|
||||
},
|
||||
"Tags": func(t *testing.T) {
|
||||
store.testTags(t)
|
||||
},
|
||||
|
||||
// "Test Title": func(t *testing.T) {
|
||||
// },
|
||||
}
|
||||
|
||||
for name, test := range testCases {
|
||||
t.Run(name, test)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (store *Store) testEnvironments(t *testing.T) {
|
||||
id := store.CreateEndpoint(t, "local", portainer.KubernetesLocalEnvironment, "", true)
|
||||
store.CreateEndpointRelation(id)
|
||||
|
||||
id = store.CreateEndpoint(t, "agent", portainer.AgentOnDockerEnvironment, agentOnDockerEnvironmentUrl, true)
|
||||
store.CreateEndpointRelation(id)
|
||||
|
||||
id = store.CreateEndpoint(t, "edge", portainer.EdgeAgentOnKubernetesEnvironment, edgeAgentOnKubernetesEnvironmentUrl, true)
|
||||
store.CreateEndpointRelation(id)
|
||||
}
|
||||
|
||||
func newEndpoint(endpointType portainer.EndpointType, id portainer.EndpointID, name, URL string, TLS bool) *portainer.Endpoint {
|
||||
endpoint := &portainer.Endpoint{
|
||||
ID: id,
|
||||
Name: name,
|
||||
URL: URL,
|
||||
Type: endpointType,
|
||||
GroupID: portainer.EndpointGroupID(1),
|
||||
PublicURL: "",
|
||||
TLSConfig: portainer.TLSConfiguration{
|
||||
TLS: false,
|
||||
},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
TagIDs: []portainer.TagID{},
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
Kubernetes: portainer.KubernetesDefault(),
|
||||
}
|
||||
|
||||
if TLS {
|
||||
endpoint.TLSConfig = portainer.TLSConfiguration{
|
||||
TLS: true,
|
||||
TLSSkipVerify: true,
|
||||
}
|
||||
}
|
||||
|
||||
return endpoint
|
||||
}
|
||||
|
||||
func setEndpointAuthorizations(endpoint *portainer.Endpoint) {
|
||||
endpoint.SecuritySettings = portainer.EndpointSecuritySettings{
|
||||
AllowVolumeBrowserForRegularUsers: false,
|
||||
EnableHostManagementFeatures: false,
|
||||
|
||||
AllowSysctlSettingForRegularUsers: true,
|
||||
AllowBindMountsForRegularUsers: true,
|
||||
AllowPrivilegedModeForRegularUsers: true,
|
||||
AllowHostNamespaceForRegularUsers: true,
|
||||
AllowContainerCapabilitiesForRegularUsers: true,
|
||||
AllowDeviceMappingForRegularUsers: true,
|
||||
AllowStackManagementForRegularUsers: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (store *Store) CreateEndpoint(t *testing.T, name string, endpointType portainer.EndpointType, URL string, tls bool) portainer.EndpointID {
|
||||
is := assert.New(t)
|
||||
|
||||
var expectedEndpoint *portainer.Endpoint
|
||||
id := portainer.EndpointID(store.Endpoint().GetNextIdentifier())
|
||||
|
||||
switch endpointType {
|
||||
case portainer.DockerEnvironment:
|
||||
if URL == "" {
|
||||
URL = "unix:///var/run/docker.sock"
|
||||
if runtime.GOOS == "windows" {
|
||||
URL = "npipe:////./pipe/docker_engine"
|
||||
}
|
||||
}
|
||||
expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
|
||||
case portainer.AgentOnDockerEnvironment:
|
||||
expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
|
||||
case portainer.AgentOnKubernetesEnvironment:
|
||||
URL = strings.TrimPrefix(URL, "tcp://")
|
||||
expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
|
||||
case portainer.EdgeAgentOnKubernetesEnvironment:
|
||||
cs := chisel.NewService(store, nil)
|
||||
expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
edgeKey := cs.GenerateEdgeKey(URL, "", int(id))
|
||||
expectedEndpoint.EdgeKey = edgeKey
|
||||
store.testTunnelServer(t)
|
||||
|
||||
case portainer.KubernetesLocalEnvironment:
|
||||
if URL == "" {
|
||||
URL = kubernetesLocalEnvironmentUrl
|
||||
}
|
||||
expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
}
|
||||
|
||||
setEndpointAuthorizations(expectedEndpoint)
|
||||
store.Endpoint().Create(expectedEndpoint)
|
||||
|
||||
endpoint, err := store.Endpoint().Endpoint(id)
|
||||
is.NoError(err, "Endpoint() should not return an error")
|
||||
is.Equal(expectedEndpoint, endpoint, "endpoint should be the same")
|
||||
|
||||
return endpoint.ID
|
||||
}
|
||||
|
||||
func (store *Store) CreateEndpointRelation(id portainer.EndpointID) {
|
||||
relation := &portainer.EndpointRelation{
|
||||
EndpointID: id,
|
||||
EdgeStacks: map[portainer.EdgeStackID]bool{},
|
||||
}
|
||||
|
||||
store.EndpointRelation().Create(relation)
|
||||
}
|
||||
|
||||
func (store *Store) testSSLSettings(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
ssl := &portainer.SSLSettings{
|
||||
CertPath: "/data/certs/cert.pem",
|
||||
HTTPEnabled: true,
|
||||
KeyPath: "/data/certs/key.pem",
|
||||
SelfSigned: true,
|
||||
}
|
||||
|
||||
store.SSLSettings().UpdateSettings(ssl)
|
||||
|
||||
settings, err := store.SSLSettings().Settings()
|
||||
is.NoError(err, "Get sslsettings should succeed")
|
||||
is.Equal(ssl, settings, "Stored SSLSettings should be the same as what is read out")
|
||||
}
|
||||
|
||||
func (store *Store) testTunnelServer(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
expectPrivateKeySeed := uniuri.NewLen(16)
|
||||
|
||||
err := store.TunnelServer().UpdateInfo(&portainer.TunnelServerInfo{PrivateKeySeed: expectPrivateKeySeed})
|
||||
is.NoError(err, "UpdateInfo should have succeeded")
|
||||
|
||||
serverInfo, err := store.TunnelServer().Info()
|
||||
is.NoError(err, "Info should have succeeded")
|
||||
|
||||
is.Equal(expectPrivateKeySeed, serverInfo.PrivateKeySeed, "hashed passwords should not differ")
|
||||
}
|
||||
|
||||
// add users, read them back and check the details are unchanged
|
||||
func (store *Store) testUserAccounts(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
err := store.createAccount(adminUsername, adminPassword, portainer.AdministratorRole)
|
||||
is.NoError(err, "CreateAccount should succeed")
|
||||
store.checkAccount(adminUsername, adminPassword, portainer.AdministratorRole)
|
||||
is.NoError(err, "Account failure")
|
||||
|
||||
err = store.createAccount(standardUsername, standardPassword, portainer.StandardUserRole)
|
||||
is.NoError(err, "CreateAccount should succeed")
|
||||
store.checkAccount(standardUsername, standardPassword, portainer.StandardUserRole)
|
||||
is.NoError(err, "Account failure")
|
||||
}
|
||||
|
||||
// create an account with the provided details
|
||||
func (store *Store) createAccount(username, password string, role portainer.UserRole) error {
|
||||
var err error
|
||||
user := &portainer.User{Username: username, Role: role}
|
||||
|
||||
// encrypt the password
|
||||
cs := &crypto.Service{}
|
||||
user.Password, err = cs.Hash(password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.User().Create(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *Store) checkAccount(username, expectPassword string, expectRole portainer.UserRole) error {
|
||||
// Read the account for username. Check password and role is what we expect
|
||||
|
||||
user, err := store.User().UserByUsername(username)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to find user")
|
||||
}
|
||||
|
||||
if user.Username != username || user.Role != expectRole {
|
||||
return fmt.Errorf("%s user details do not match", user.Username)
|
||||
}
|
||||
|
||||
// Check the password
|
||||
cs := &crypto.Service{}
|
||||
expectPasswordHash, err := cs.Hash(expectPassword)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "hash failed")
|
||||
}
|
||||
|
||||
if user.Password != expectPasswordHash {
|
||||
return fmt.Errorf("%s user password hash failure", user.Username)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *Store) testSettings(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// since many settings are default and basically nil, I'm going to update some and read them back
|
||||
expectedSettings, err := store.Settings().Settings()
|
||||
is.NoError(err, "Settings() should not return an error")
|
||||
expectedSettings.TemplatesURL = "http://portainer.io/application-templates"
|
||||
expectedSettings.HelmRepositoryURL = "http://portainer.io/helm-repository"
|
||||
expectedSettings.EdgeAgentCheckinInterval = 60
|
||||
expectedSettings.AuthenticationMethod = portainer.AuthenticationLDAP
|
||||
expectedSettings.LDAPSettings = portainer.LDAPSettings{
|
||||
AnonymousMode: true,
|
||||
StartTLS: true,
|
||||
AutoCreateUsers: true,
|
||||
Password: "random",
|
||||
}
|
||||
expectedSettings.SnapshotInterval = "10m"
|
||||
|
||||
err = store.Settings().UpdateSettings(expectedSettings)
|
||||
is.NoError(err, "UpdateSettings() should succeed")
|
||||
|
||||
settings, err := store.Settings().Settings()
|
||||
is.NoError(err, "Settings() should not return an error")
|
||||
is.Equal(expectedSettings, settings, "stored settings should match")
|
||||
}
|
||||
|
||||
func (store *Store) testCustomTemplates(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
customTemplate := store.CustomTemplate()
|
||||
is.NotNil(customTemplate, "customTemplate Service shouldn't be nil")
|
||||
|
||||
expectedTemplate := &portainer.CustomTemplate{
|
||||
ID: portainer.CustomTemplateID(customTemplate.GetNextIdentifier()),
|
||||
Title: "Custom Title",
|
||||
Description: "Custom Template Description",
|
||||
ProjectPath: "/data/custom_template/1",
|
||||
Note: "A note about this custom template",
|
||||
EntryPoint: "docker-compose.yaml",
|
||||
CreatedByUserID: 10,
|
||||
}
|
||||
|
||||
customTemplate.Create(expectedTemplate)
|
||||
|
||||
actualTemplate, err := customTemplate.CustomTemplate(expectedTemplate.ID)
|
||||
is.NoError(err, "CustomTemplate should not return an error")
|
||||
is.Equal(expectedTemplate, actualTemplate, "expected and actual template do not match")
|
||||
}
|
||||
|
||||
func (store *Store) testRegistries(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
regService := store.RegistryService
|
||||
is.NotNil(regService, "RegistryService shouldn't be nil")
|
||||
|
||||
reg1 := &portainer.Registry{
|
||||
ID: 1,
|
||||
Type: portainer.DockerHubRegistry,
|
||||
Name: "Dockerhub Registry Test",
|
||||
}
|
||||
|
||||
reg2 := &portainer.Registry{
|
||||
ID: 2,
|
||||
Type: portainer.GitlabRegistry,
|
||||
Name: "Gitlab Registry Test",
|
||||
Gitlab: portainer.GitlabRegistryData{
|
||||
ProjectID: 12345,
|
||||
InstanceURL: "http://gitlab.com/12345",
|
||||
ProjectPath: "mytestproject",
|
||||
},
|
||||
}
|
||||
|
||||
err := regService.Create(reg1)
|
||||
is.NoError(err)
|
||||
|
||||
err = regService.Create(reg2)
|
||||
is.NoError(err)
|
||||
|
||||
actualReg1, err := regService.Registry(reg1.ID)
|
||||
is.NoError(err)
|
||||
is.Equal(reg1, actualReg1, "registries differ")
|
||||
|
||||
actualReg2, err := regService.Registry(reg2.ID)
|
||||
is.NoError(err)
|
||||
is.Equal(reg2, actualReg2, "registries differ")
|
||||
}
|
||||
|
||||
func (store *Store) testResourceControl(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
// resControl := store.ResourceControl()
|
||||
// ctrl := &portainer.ResourceControl{
|
||||
// }
|
||||
// resControl().Create()
|
||||
}
|
||||
|
||||
func (store *Store) testSchedules(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
schedule := store.ScheduleService
|
||||
s := &portainer.Schedule{
|
||||
ID: portainer.ScheduleID(schedule.GetNextIdentifier()),
|
||||
Name: "My Custom Schedule 1",
|
||||
CronExpression: "*/5 * * * * portainer /bin/sh -c echo 'hello world'",
|
||||
}
|
||||
|
||||
err := schedule.CreateSchedule(s)
|
||||
is.NoError(err, "CreateSchedule should succeed")
|
||||
|
||||
actual, err := schedule.Schedule(s.ID)
|
||||
is.NoError(err, "schedule should be found")
|
||||
is.Equal(s, actual, "schedules differ")
|
||||
}
|
||||
|
||||
func (store *Store) testTags(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
tags := store.TagService
|
||||
|
||||
tag1 := &portainer.Tag{
|
||||
ID: 1,
|
||||
Name: "Tag 1",
|
||||
}
|
||||
|
||||
tag2 := &portainer.Tag{
|
||||
ID: 2,
|
||||
Name: "Tag 1",
|
||||
}
|
||||
|
||||
err := tags.Create(tag1)
|
||||
is.NoError(err, "Tags.Create should succeed")
|
||||
|
||||
err = tags.Create(tag2)
|
||||
is.NoError(err, "Tags.Create should succeed")
|
||||
|
||||
actual, err := tags.Tag(tag1.ID)
|
||||
is.NoError(err, "tag1 should be found")
|
||||
is.Equal(tag1, actual, "tags differ")
|
||||
|
||||
actual, err = tags.Tag(tag2.ID)
|
||||
is.NoError(err, "tag2 should be found")
|
||||
is.Equal(tag2, actual, "tags differ")
|
||||
}
|
||||
@@ -1,31 +1,41 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
)
|
||||
|
||||
// Init creates the default data set.
|
||||
func (store *Store) Init() error {
|
||||
err := store.checkOrCreateInstanceID()
|
||||
conn := store.GetConnection()
|
||||
err := conn.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.checkOrCreateDefaultSettings()
|
||||
err = store.initVersions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.checkOrCreateDefaultSSLSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// err = store.checkOrCreateDefaultSettings()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
return store.checkOrCreateDefaultData()
|
||||
// err = store.checkOrCreateDefaultSSLSettings()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return store.checkOrCreateDefaultData()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *Store) checkOrCreateInstanceID() error {
|
||||
func (store *Store) initVersions() error {
|
||||
_, err := store.VersionService.InstanceID()
|
||||
if store.IsErrObjectNotFound(err) {
|
||||
uid, err := uuid.NewV4()
|
||||
@@ -34,9 +44,37 @@ func (store *Store) checkOrCreateInstanceID() error {
|
||||
}
|
||||
|
||||
instanceID := uid.String()
|
||||
return store.VersionService.StoreInstanceID(instanceID)
|
||||
|
||||
db := store.connection.GetDB()
|
||||
|
||||
fmt.Println("updating instance id")
|
||||
tx := db.Create(&models.Version{Key: models.InstanceKey, Value: instanceID})
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
fmt.Printf("updating version id %s\n", models.VersionKey)
|
||||
tx = db.Create(&models.Version{Key: models.VersionKey, Value: fmt.Sprint(portainer.DBVersion)})
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
fmt.Println("updating updating id")
|
||||
tx = db.Create(&models.Version{Key: models.UpdatingKey, Value: "false"})
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
fmt.Println("updating edition id")
|
||||
tx = db.Create(&models.Version{Key: models.EditionKey, Value: fmt.Sprint(portainer.PortainerCE)})
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *Store) checkOrCreateDefaultSettings() error {
|
||||
@@ -88,7 +126,6 @@ func (store *Store) checkOrCreateDefaultSettings() error {
|
||||
|
||||
func (store *Store) checkOrCreateDefaultSSLSettings() error {
|
||||
_, err := store.SSLSettings().Settings()
|
||||
|
||||
if store.IsErrObjectNotFound(err) {
|
||||
defaultSSLSettings := &portainer.SSLSettings{
|
||||
HTTPEnabled: true,
|
||||
@@ -96,6 +133,7 @@ func (store *Store) checkOrCreateDefaultSSLSettings() error {
|
||||
|
||||
return store.SSLSettings().UpdateSettings(defaultSSLSettings)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/cli"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
"github.com/portainer/portainer/api/datastore/migrator"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
|
||||
werrors "github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const beforePortainerVersionUpgradeBackup = "portainer.db.bak"
|
||||
|
||||
func (store *Store) MigrateData() error {
|
||||
version, err := store.version()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Backup Database
|
||||
backupPath, err := store.Backup()
|
||||
if err != nil {
|
||||
return werrors.Wrap(err, "while backing up db before migration")
|
||||
}
|
||||
|
||||
migratorParams := &migrator.MigratorParameters{
|
||||
DatabaseVersion: version,
|
||||
EndpointGroupService: store.EndpointGroupService,
|
||||
EndpointService: store.EndpointService,
|
||||
EndpointRelationService: store.EndpointRelationService,
|
||||
ExtensionService: store.ExtensionService,
|
||||
FDOProfilesService: store.FDOProfilesService,
|
||||
RegistryService: store.RegistryService,
|
||||
ResourceControlService: store.ResourceControlService,
|
||||
RoleService: store.RoleService,
|
||||
ScheduleService: store.ScheduleService,
|
||||
SettingsService: store.SettingsService,
|
||||
SnapshotService: store.SnapshotService,
|
||||
StackService: store.StackService,
|
||||
TagService: store.TagService,
|
||||
TeamMembershipService: store.TeamMembershipService,
|
||||
UserService: store.UserService,
|
||||
VersionService: store.VersionService,
|
||||
FileService: store.fileService,
|
||||
DockerhubService: store.DockerHubService,
|
||||
AuthorizationService: authorization.NewService(store),
|
||||
}
|
||||
|
||||
// restore on error
|
||||
err = store.connectionMigrateData(migratorParams)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("while DB migration, restoring DB")
|
||||
|
||||
// Restore options
|
||||
options := BackupOptions{
|
||||
BackupPath: backupPath,
|
||||
}
|
||||
|
||||
err := store.restoreWithOptions(&options)
|
||||
if err != nil {
|
||||
log.Fatal().
|
||||
Str("database_file", store.databasePath()).
|
||||
Str("backup", options.BackupPath).Err(err).
|
||||
Msg("failed restoring the backup, Portainer database file needs to restored manually by replacing the database file with a recent backup")
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// FailSafeMigrate backup and restore DB if migration fail
|
||||
func (store *Store) FailSafeMigrate(migrator *migrator.Migrator) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
store.Rollback(true)
|
||||
// return error with cause and stacktrace (recover() doesn't include a stacktrace)
|
||||
err = fmt.Errorf("%v %s", e, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
// !Important: we must use a named return value in the function definition and not a local
|
||||
// !variable referenced from the closure or else the return value will be incorrectly set
|
||||
return migrator.Migrate()
|
||||
}
|
||||
|
||||
// MigrateData automatically migrate the data based on the DBVersion.
|
||||
// This process is only triggered on an existing database, not if the database was just created.
|
||||
// if force is true, then migrate regardless.
|
||||
func (store *Store) connectionMigrateData(migratorParams *migrator.MigratorParameters) error {
|
||||
migrator := migrator.NewMigrator(migratorParams)
|
||||
|
||||
// backup db file before upgrading DB to support rollback
|
||||
isUpdating, err := migratorParams.VersionService.IsUpdating()
|
||||
if err != nil && err != errors.ErrObjectNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isUpdating && migrator.Version() != portainer.DBVersion {
|
||||
err = store.backupVersion(migrator)
|
||||
if err != nil {
|
||||
return werrors.Wrapf(err, "failed to backup database")
|
||||
}
|
||||
}
|
||||
|
||||
if migrator.Version() < portainer.DBVersion {
|
||||
log.Info().
|
||||
Int("migrator_version", migrator.Version()).
|
||||
Int("db_version", portainer.DBVersion).
|
||||
Msg("migrating database")
|
||||
|
||||
err = store.FailSafeMigrate(migrator)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("an error occurred during database migration")
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// backupVersion will backup the database or panic if any errors occur
|
||||
func (store *Store) backupVersion(migrator *migrator.Migrator) error {
|
||||
log.Info().Msg("backing up database prior to version upgrade")
|
||||
|
||||
options := getBackupRestoreOptions(store.commonBackupDir())
|
||||
|
||||
_, err := store.backupWithOptions(options)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("an error occurred during database backup")
|
||||
|
||||
removalErr := store.removeWithOptions(options)
|
||||
if removalErr != nil {
|
||||
log.Error().Err(err).Msg("an error occurred during store removal prior to backup")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rollback to a pre-upgrade backup copy/snapshot of portainer.db
|
||||
func (store *Store) connectionRollback(force bool) error {
|
||||
|
||||
if !force {
|
||||
confirmed, err := cli.Confirm("Are you sure you want to rollback your database to the previous backup?")
|
||||
if err != nil || !confirmed {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
options := getBackupRestoreOptions(store.commonBackupDir())
|
||||
|
||||
err := store.restoreWithOptions(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.connection.Close()
|
||||
}
|
||||
@@ -1,465 +0,0 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// testVersion is a helper which tests current store version against wanted version
|
||||
func testVersion(store *Store, versionWant int, t *testing.T) {
|
||||
v, err := store.VersionService.DBVersion()
|
||||
if err != nil {
|
||||
t.Errorf("Expect store version to be %d but was %d with error: %s", versionWant, v, err)
|
||||
}
|
||||
if v != versionWant {
|
||||
t.Errorf("Expect store version to be %d but was %d", versionWant, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateData(t *testing.T) {
|
||||
snapshotTests := []struct {
|
||||
testName string
|
||||
srcPath string
|
||||
wantPath string
|
||||
}{
|
||||
{
|
||||
testName: "migrate version 24 to latest",
|
||||
srcPath: "test_data/input_24.json",
|
||||
wantPath: "test_data/output_24_to_latest.json",
|
||||
},
|
||||
}
|
||||
for _, test := range snapshotTests {
|
||||
t.Run(test.testName, func(t *testing.T) {
|
||||
err := migrateDBTestHelper(t, test.srcPath, test.wantPath)
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"Failed migrating mock database %v: %v",
|
||||
test.srcPath,
|
||||
err,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("MigrateData for New Store & Re-Open Check", func(t *testing.T) {
|
||||
newStore, store, teardown := MustNewTestStore(t, false, true)
|
||||
defer teardown()
|
||||
|
||||
if !newStore {
|
||||
t.Error("Expect a new DB")
|
||||
}
|
||||
|
||||
// not called for new stores
|
||||
//store.MigrateData()
|
||||
|
||||
testVersion(store, portainer.DBVersion, t)
|
||||
store.Close()
|
||||
|
||||
newStore, _ = store.Open()
|
||||
if newStore {
|
||||
t.Error("Expect store to NOT be new DB")
|
||||
}
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
version int
|
||||
expectedVersion int
|
||||
}{
|
||||
{version: 17, expectedVersion: portainer.DBVersion},
|
||||
{version: 21, expectedVersion: portainer.DBVersion},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
_, store, teardown := MustNewTestStore(t, true, true)
|
||||
defer teardown()
|
||||
|
||||
// Setup data
|
||||
store.VersionService.StoreDBVersion(tc.version)
|
||||
|
||||
// Required roles by migrations 22.2
|
||||
store.RoleService.Create(&portainer.Role{ID: 1})
|
||||
store.RoleService.Create(&portainer.Role{ID: 2})
|
||||
store.RoleService.Create(&portainer.Role{ID: 3})
|
||||
store.RoleService.Create(&portainer.Role{ID: 4})
|
||||
|
||||
t.Run(fmt.Sprintf("MigrateData for version %d", tc.version), func(t *testing.T) {
|
||||
store.MigrateData()
|
||||
testVersion(store, tc.expectedVersion, t)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("Restoring DB after migrateData for version %d", tc.version), func(t *testing.T) {
|
||||
store.Rollback(true)
|
||||
store.Open()
|
||||
testVersion(store, tc.version, t)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("Error in MigrateData should restore backup before MigrateData", func(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
defer teardown()
|
||||
|
||||
version := 17
|
||||
store.VersionService.StoreDBVersion(version)
|
||||
|
||||
store.MigrateData()
|
||||
|
||||
testVersion(store, version, t)
|
||||
})
|
||||
|
||||
t.Run("MigrateData should create backup file upon update", func(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
defer teardown()
|
||||
store.VersionService.StoreDBVersion(0)
|
||||
|
||||
store.MigrateData()
|
||||
|
||||
options := store.setupOptions(getBackupRestoreOptions(store.commonBackupDir()))
|
||||
|
||||
if !isFileExist(options.BackupPath) {
|
||||
t.Errorf("Backup file should exist; file=%s", options.BackupPath)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MigrateData should fail to create backup if database file is set to updating", func(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
defer teardown()
|
||||
|
||||
store.VersionService.StoreIsUpdating(true)
|
||||
|
||||
store.MigrateData()
|
||||
|
||||
options := store.setupOptions(getBackupRestoreOptions(store.commonBackupDir()))
|
||||
|
||||
if isFileExist(options.BackupPath) {
|
||||
t.Errorf("Backup file should not exist for dirty database; file=%s", options.BackupPath)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MigrateData should not create backup on startup if portainer version matches db", func(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
defer teardown()
|
||||
|
||||
store.MigrateData()
|
||||
|
||||
options := store.setupOptions(getBackupRestoreOptions(store.commonBackupDir()))
|
||||
|
||||
if isFileExist(options.BackupPath) {
|
||||
t.Errorf("Backup file should not exist for dirty database; file=%s", options.BackupPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_getBackupRestoreOptions(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
defer teardown()
|
||||
|
||||
options := getBackupRestoreOptions(store.commonBackupDir())
|
||||
|
||||
wantDir := store.commonBackupDir()
|
||||
if !strings.HasSuffix(options.BackupDir, wantDir) {
|
||||
log.Fatal().Str("got", options.BackupDir).Str("want", wantDir).Msg("incorrect backup dir")
|
||||
}
|
||||
|
||||
wantFilename := "portainer.db.bak"
|
||||
if options.BackupFileName != wantFilename {
|
||||
log.Fatal().Str("got", options.BackupFileName).Str("want", wantFilename).Msg("incorrect backup file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRollback(t *testing.T) {
|
||||
t.Run("Rollback should restore upgrade after backup", func(t *testing.T) {
|
||||
version := 21
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
defer teardown()
|
||||
store.VersionService.StoreDBVersion(version)
|
||||
|
||||
_, err := store.backupWithOptions(getBackupRestoreOptions(store.commonBackupDir()))
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
|
||||
// Change the current edition
|
||||
err = store.VersionService.StoreDBVersion(version + 10)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
|
||||
err = store.Rollback(true)
|
||||
if err != nil {
|
||||
t.Logf("Rollback failed: %s", err)
|
||||
t.Fail()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
store.Open()
|
||||
testVersion(store, version, t)
|
||||
})
|
||||
}
|
||||
|
||||
// isFileExist is helper function to check for file existence
|
||||
func isFileExist(path string) bool {
|
||||
matches, err := filepath.Glob(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(matches) > 0
|
||||
}
|
||||
|
||||
// migrateDBTestHelper loads a json representation of a bolt database from srcPath,
|
||||
// parses it into a database, runs a migration on that database, and then
|
||||
// compares it with an expected output database.
|
||||
func migrateDBTestHelper(t *testing.T, srcPath, wantPath string) error {
|
||||
srcJSON, err := os.ReadFile(srcPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed loading source JSON file %v: %v", srcPath, err)
|
||||
}
|
||||
|
||||
// Parse source json to db.
|
||||
_, store, teardown := MustNewTestStore(t, true, false)
|
||||
defer teardown()
|
||||
err = importJSON(t, bytes.NewReader(srcJSON), store)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Run the actual migrations on our input database.
|
||||
err = store.MigrateData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Assert that our database connection is using bolt so we can call
|
||||
// exportJson rather than ExportRaw. The exportJson function allows us to
|
||||
// strip out the metadata which we don't want for our tests.
|
||||
// TODO: update connection interface in CE to allow us to use ExportRaw and pass meta false
|
||||
err = store.connection.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("err closing bolt connection: %v", err)
|
||||
}
|
||||
con, ok := store.connection.(*boltdb.DbConnection)
|
||||
if !ok {
|
||||
t.Fatalf("backing database is not using boltdb, but the migrations test requires it")
|
||||
}
|
||||
|
||||
// Convert database back to json.
|
||||
databasePath := con.GetDatabaseFilePath()
|
||||
if _, err := os.Stat(databasePath); err != nil {
|
||||
return fmt.Errorf("stat on %s failed: %s", databasePath, err)
|
||||
}
|
||||
|
||||
gotJSON, err := con.ExportJson(databasePath, false)
|
||||
if err != nil {
|
||||
t.Logf(
|
||||
"failed re-exporting database %s to JSON: %v",
|
||||
databasePath,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
wantJSON, err := os.ReadFile(wantPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed loading want JSON file %v: %v", wantPath, err)
|
||||
}
|
||||
|
||||
// Compare the result we got with the one we wanted.
|
||||
if diff := cmp.Diff(wantJSON, gotJSON); diff != "" {
|
||||
gotPath := filepath.Join(t.TempDir(), "portainer-migrator-test-fail.json")
|
||||
os.WriteFile(
|
||||
gotPath,
|
||||
gotJSON,
|
||||
0600,
|
||||
)
|
||||
t.Errorf(
|
||||
"migrate data from %s to %s failed\nwrote migrated input to %s\nmismatch (-want +got):\n%s",
|
||||
srcPath,
|
||||
wantPath,
|
||||
gotPath,
|
||||
diff,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// importJSON reads input JSON and commits it to a portainer datastore.Store.
|
||||
// Errors are logged with the testing package.
|
||||
func importJSON(t *testing.T, r io.Reader, store *Store) error {
|
||||
objects := make(map[string]interface{})
|
||||
|
||||
// Parse json into map of objects.
|
||||
d := json.NewDecoder(r)
|
||||
d.UseNumber()
|
||||
err := d.Decode(&objects)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get database connection from store.
|
||||
con := store.connection
|
||||
|
||||
for k, v := range objects {
|
||||
switch k {
|
||||
case "version":
|
||||
versions, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed casting %s to map[string]interface{}", k)
|
||||
}
|
||||
|
||||
dbVersion, ok := versions["DB_VERSION"]
|
||||
if !ok {
|
||||
t.Logf("failed getting DB_VERSION from %s", k)
|
||||
}
|
||||
|
||||
numDBVersion, ok := dbVersion.(json.Number)
|
||||
if !ok {
|
||||
t.Logf("failed parsing DB_VERSION as json number from %s", k)
|
||||
}
|
||||
|
||||
intDBVersion, err := numDBVersion.Int64()
|
||||
if err != nil {
|
||||
t.Logf("failed casting %v to int: %v", numDBVersion, intDBVersion)
|
||||
}
|
||||
|
||||
err = con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("DB_VERSION"),
|
||||
int(intDBVersion),
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing DB_VERSION in %s: %v", k, err)
|
||||
}
|
||||
|
||||
instanceID, ok := versions["INSTANCE_ID"]
|
||||
if !ok {
|
||||
t.Logf("failed getting INSTANCE_ID from %s", k)
|
||||
}
|
||||
|
||||
err = con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("INSTANCE_ID"),
|
||||
instanceID,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing INSTANCE_ID in %s: %v", k, err)
|
||||
}
|
||||
|
||||
case "dockerhub":
|
||||
obj, ok := v.([]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to cast %s to []interface{}", k)
|
||||
}
|
||||
err := con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("DOCKERHUB"),
|
||||
obj[0],
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing DOCKERHUB in %s: %v", k, err)
|
||||
}
|
||||
|
||||
case "ssl":
|
||||
obj, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to case %s to map[string]interface{}", k)
|
||||
}
|
||||
err := con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("SSL"),
|
||||
obj,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing SSL in %s: %v", k, err)
|
||||
}
|
||||
|
||||
case "settings":
|
||||
obj, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to case %s to map[string]interface{}", k)
|
||||
}
|
||||
err := con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("SETTINGS"),
|
||||
obj,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing SETTINGS in %s: %v", k, err)
|
||||
}
|
||||
|
||||
case "tunnel_server":
|
||||
obj, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to case %s to map[string]interface{}", k)
|
||||
}
|
||||
err := con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("INFO"),
|
||||
obj,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing INFO in %s: %v", k, err)
|
||||
}
|
||||
case "templates":
|
||||
continue
|
||||
|
||||
default:
|
||||
objlist, ok := v.([]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to cast %s to []interface{}", k)
|
||||
}
|
||||
|
||||
for _, obj := range objlist {
|
||||
value, ok := obj.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to cast %v to map[string]interface{}", obj)
|
||||
} else {
|
||||
var ok bool
|
||||
var id interface{}
|
||||
switch k {
|
||||
case "endpoint_relations":
|
||||
// TODO: need to make into an int, then do that weird
|
||||
// stringification
|
||||
id, ok = value["EndpointID"]
|
||||
default:
|
||||
id, ok = value["Id"]
|
||||
}
|
||||
if !ok {
|
||||
// endpoint_relations: EndpointID
|
||||
t.Logf("missing Id field: %s", k)
|
||||
id = "error"
|
||||
}
|
||||
n, ok := id.(json.Number)
|
||||
if !ok {
|
||||
t.Logf("failed to cast %v to json.Number in %s", id, k)
|
||||
} else {
|
||||
key, err := n.Int64()
|
||||
if err != nil {
|
||||
t.Logf("failed to cast %v to int in %s", n, k)
|
||||
} else {
|
||||
err := con.CreateObjectWithId(
|
||||
k,
|
||||
int(key),
|
||||
value,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing %v in %s: %v", key, k, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore/migrator"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
)
|
||||
|
||||
const dummyLogoURL = "example.com"
|
||||
|
||||
// initTestingDBConn creates a settings service with raw database DB connection
|
||||
// for unit testing usage only since using NewStore will cause cycle import inside migrator pkg
|
||||
func initTestingSettingsService(dbConn portainer.Connection, preSetObj map[string]interface{}) error {
|
||||
//insert a obj
|
||||
if err := dbConn.UpdateObject("settings", []byte("SETTINGS"), preSetObj); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setup(store *Store) error {
|
||||
var err error
|
||||
dummySettingsObj := map[string]interface{}{
|
||||
"LogoURL": dummyLogoURL,
|
||||
}
|
||||
err = initTestingSettingsService(store.connection, dummySettingsObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMigrateSettings(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
defer teardown()
|
||||
|
||||
err := setup(store)
|
||||
if err != nil {
|
||||
t.Errorf("failed to complete testing setups, err: %v", err)
|
||||
}
|
||||
|
||||
updatedSettings, err := store.SettingsService.Settings()
|
||||
// SO -basically, this test _isn't_ testing migration, its testing golang type defaults.
|
||||
if updatedSettings.LogoURL != dummyLogoURL { // ensure a pre-migrate setting isn't unset
|
||||
t.Errorf("unexpected value changes in the updated settings, want LogoURL value: %s, got LogoURL value: %s", dummyLogoURL, updatedSettings.LogoURL)
|
||||
}
|
||||
if updatedSettings.OAuthSettings.SSO != false { // I recon golang defaulting will make this false
|
||||
t.Errorf("unexpected default OAuth SSO setting, want: false, got: %t", updatedSettings.OAuthSettings.SSO)
|
||||
}
|
||||
if updatedSettings.OAuthSettings.LogoutURI != "" {
|
||||
t.Errorf("unexpected default OAuth HideInternalAuth setting, want:, got: %s", updatedSettings.OAuthSettings.LogoutURI)
|
||||
}
|
||||
|
||||
m := migrator.NewMigrator(&migrator.MigratorParameters{
|
||||
DatabaseVersion: 29,
|
||||
EndpointGroupService: store.EndpointGroupService,
|
||||
EndpointService: store.EndpointService,
|
||||
EndpointRelationService: store.EndpointRelationService,
|
||||
ExtensionService: store.ExtensionService,
|
||||
RegistryService: store.RegistryService,
|
||||
ResourceControlService: store.ResourceControlService,
|
||||
RoleService: store.RoleService,
|
||||
ScheduleService: store.ScheduleService,
|
||||
SettingsService: store.SettingsService,
|
||||
StackService: store.StackService,
|
||||
TagService: store.TagService,
|
||||
TeamMembershipService: store.TeamMembershipService,
|
||||
UserService: store.UserService,
|
||||
VersionService: store.VersionService,
|
||||
FileService: store.fileService,
|
||||
DockerhubService: store.DockerHubService,
|
||||
AuthorizationService: authorization.NewService(store),
|
||||
})
|
||||
if err := m.MigrateSettingsToDB30(); err != nil {
|
||||
t.Errorf("failed to update settings: %v", err)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("failed to retrieve the updated settings: %v", err)
|
||||
}
|
||||
if updatedSettings.LogoURL != dummyLogoURL {
|
||||
t.Errorf("unexpected value changes in the updated settings, want LogoURL value: %s, got LogoURL value: %s", dummyLogoURL, updatedSettings.LogoURL)
|
||||
}
|
||||
if updatedSettings.OAuthSettings.SSO != false {
|
||||
t.Errorf("unexpected default OAuth SSO setting, want: false, got: %t", updatedSettings.OAuthSettings.SSO)
|
||||
}
|
||||
if updatedSettings.OAuthSettings.LogoutURI != "" {
|
||||
t.Errorf("unexpected default OAuth HideInternalAuth setting, want:, got: %s", updatedSettings.OAuthSettings.LogoutURI)
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore/migrator"
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMigrateStackEntryPoint(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
defer teardown()
|
||||
|
||||
stackService := store.Stack()
|
||||
|
||||
stacks := []*portainer.Stack{
|
||||
{
|
||||
ID: 1,
|
||||
EntryPoint: "dir/sub/compose.yml",
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
EntryPoint: "dir/sub/compose.yml",
|
||||
GitConfig: &gittypes.RepoConfig{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range stacks {
|
||||
err := stackService.Create(s)
|
||||
assert.NoError(t, err, "failed to create stack")
|
||||
}
|
||||
|
||||
s, err := stackService.Stack(1)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, s.GitConfig, "first stack should not have git config")
|
||||
|
||||
s, err = stackService.Stack(2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", s.GitConfig.ConfigFilePath, "not migrated yet migrated")
|
||||
|
||||
err = migrator.MigrateStackEntryPoint(stackService)
|
||||
assert.NoError(t, err, "failed to migrate entry point to Git ConfigFilePath")
|
||||
|
||||
s, err = stackService.Stack(1)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, s.GitConfig, "first stack should not have git config")
|
||||
|
||||
s, err = stackService.Stack(2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "dir/sub/compose.yml", s.GitConfig.ConfigFilePath, "second stack should have config file path migrated")
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
werrors "github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type migration struct {
|
||||
dbversion int
|
||||
migrate func() error
|
||||
}
|
||||
|
||||
func migrationError(err error, context string) error {
|
||||
return werrors.Wrap(err, "failed in "+context)
|
||||
}
|
||||
|
||||
func newMigration(dbversion int, migrate func() error) migration {
|
||||
return migration{
|
||||
dbversion: dbversion,
|
||||
migrate: migrate,
|
||||
}
|
||||
}
|
||||
|
||||
func dbTooOldError() error {
|
||||
return errors.New("migrating from less than Portainer 1.21.0 is not supported, please contact Portainer support.")
|
||||
}
|
||||
|
||||
func GetFunctionName(i interface{}) string {
|
||||
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
|
||||
}
|
||||
|
||||
// Migrate checks the database version and migrate the existing data to the most recent data model.
|
||||
func (m *Migrator) Migrate() error {
|
||||
// set DB to updating status
|
||||
err := m.versionService.StoreIsUpdating(true)
|
||||
if err != nil {
|
||||
return migrationError(err, "StoreIsUpdating")
|
||||
}
|
||||
|
||||
migrations := []migration{
|
||||
// Portainer < 1.21.0
|
||||
newMigration(17, dbTooOldError),
|
||||
|
||||
// Portainer 1.21.0
|
||||
newMigration(18, m.updateUsersToDBVersion18),
|
||||
newMigration(18, m.updateEndpointsToDBVersion18),
|
||||
newMigration(18, m.updateEndpointGroupsToDBVersion18),
|
||||
newMigration(18, m.updateRegistriesToDBVersion18),
|
||||
|
||||
// 1.22.0
|
||||
newMigration(19, m.updateSettingsToDBVersion19),
|
||||
|
||||
// 1.22.1
|
||||
newMigration(20, m.updateUsersToDBVersion20),
|
||||
newMigration(20, m.updateSettingsToDBVersion20),
|
||||
newMigration(20, m.updateSchedulesToDBVersion20),
|
||||
|
||||
// Portainer 1.23.0
|
||||
// DBVersion 21 is missing as it was shipped as via hotfix 1.22.2
|
||||
newMigration(22, m.updateResourceControlsToDBVersion22),
|
||||
newMigration(22, m.updateUsersAndRolesToDBVersion22),
|
||||
|
||||
// Portainer 1.24.0
|
||||
newMigration(23, m.updateTagsToDBVersion23),
|
||||
newMigration(23, m.updateEndpointsAndEndpointGroupsToDBVersion23),
|
||||
|
||||
// Portainer 1.24.1
|
||||
newMigration(24, m.updateSettingsToDB24),
|
||||
|
||||
// Portainer 2.0.0
|
||||
newMigration(25, m.updateSettingsToDB25),
|
||||
newMigration(25, m.updateStacksToDB24), // yes this looks odd. Don't be tempted to move it
|
||||
|
||||
// Portainer 2.1.0
|
||||
newMigration(26, m.updateEndpointSettingsToDB25),
|
||||
|
||||
// Portainer 2.2.0
|
||||
newMigration(27, m.updateStackResourceControlToDB27),
|
||||
|
||||
// Portainer 2.6.0
|
||||
newMigration(30, m.migrateDBVersionToDB30),
|
||||
|
||||
// Portainer 2.9.0
|
||||
newMigration(32, m.migrateDBVersionToDB32),
|
||||
|
||||
// Portainer 2.9.1, 2.9.2
|
||||
newMigration(33, m.migrateDBVersionToDB33),
|
||||
|
||||
// Portainer 2.10
|
||||
newMigration(34, m.migrateDBVersionToDB34),
|
||||
|
||||
// Portainer 2.9.3 (yep out of order, but 2.10 is EE only)
|
||||
newMigration(35, m.migrateDBVersionToDB35),
|
||||
|
||||
newMigration(36, m.migrateDBVersionToDB36),
|
||||
|
||||
// Portainer 2.13
|
||||
newMigration(40, m.migrateDBVersionToDB40),
|
||||
|
||||
// Portainer 2.14
|
||||
newMigration(50, m.migrateDBVersionToDB50),
|
||||
|
||||
// Portainer 2.15
|
||||
newMigration(60, m.migrateDBVersionToDB60),
|
||||
|
||||
// Portainer 2.16
|
||||
newMigration(70, m.migrateDBVersionToDB70),
|
||||
}
|
||||
|
||||
var lastDbVersion int
|
||||
for _, migration := range migrations {
|
||||
if m.currentDBVersion < migration.dbversion {
|
||||
|
||||
// Print the next line only when the version changes
|
||||
if migration.dbversion > lastDbVersion {
|
||||
log.Info().Int("to_version", migration.dbversion).Msg("migrating DB")
|
||||
}
|
||||
|
||||
err := migration.migrate()
|
||||
if err != nil {
|
||||
return migrationError(err, GetFunctionName(migration.migrate))
|
||||
}
|
||||
}
|
||||
lastDbVersion = migration.dbversion
|
||||
}
|
||||
|
||||
log.Info().Int("version", portainer.DBVersion).Msg("setting DB version")
|
||||
|
||||
err = m.versionService.StoreDBVersion(portainer.DBVersion)
|
||||
if err != nil {
|
||||
return migrationError(err, "StoreDBVersion")
|
||||
}
|
||||
|
||||
log.Info().Int("version", portainer.DBVersion).Msg("updated DB version")
|
||||
|
||||
// reset DB updating status
|
||||
return m.versionService.StoreIsUpdating(false)
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateUsersToDBVersion18() error {
|
||||
log.Info().Msg("updating users")
|
||||
|
||||
legacyUsers, err := m.userService.Users()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range legacyUsers {
|
||||
user.PortainerAuthorizations = map[portainer.Authorization]bool{
|
||||
portainer.OperationPortainerDockerHubInspect: true,
|
||||
portainer.OperationPortainerEndpointGroupList: true,
|
||||
portainer.OperationPortainerEndpointList: true,
|
||||
portainer.OperationPortainerEndpointInspect: true,
|
||||
portainer.OperationPortainerEndpointExtensionAdd: true,
|
||||
portainer.OperationPortainerEndpointExtensionRemove: true,
|
||||
portainer.OperationPortainerExtensionList: true,
|
||||
portainer.OperationPortainerMOTD: true,
|
||||
portainer.OperationPortainerRegistryList: true,
|
||||
portainer.OperationPortainerRegistryInspect: true,
|
||||
portainer.OperationPortainerTeamList: true,
|
||||
portainer.OperationPortainerTemplateList: true,
|
||||
portainer.OperationPortainerTemplateInspect: true,
|
||||
portainer.OperationPortainerUserList: true,
|
||||
portainer.OperationPortainerUserMemberships: true,
|
||||
}
|
||||
|
||||
err = m.userService.UpdateUser(user.ID, &user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) updateEndpointsToDBVersion18() error {
|
||||
log.Info().Msg("updating endpoints")
|
||||
|
||||
legacyEndpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpoint := range legacyEndpoints {
|
||||
endpoint.UserAccessPolicies = make(portainer.UserAccessPolicies)
|
||||
for _, userID := range endpoint.AuthorizedUsers {
|
||||
endpoint.UserAccessPolicies[userID] = portainer.AccessPolicy{
|
||||
RoleID: 4,
|
||||
}
|
||||
}
|
||||
|
||||
endpoint.TeamAccessPolicies = make(portainer.TeamAccessPolicies)
|
||||
for _, teamID := range endpoint.AuthorizedTeams {
|
||||
endpoint.TeamAccessPolicies[teamID] = portainer.AccessPolicy{
|
||||
RoleID: 4,
|
||||
}
|
||||
}
|
||||
|
||||
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) updateEndpointGroupsToDBVersion18() error {
|
||||
log.Info().Msg("updating endpoint groups")
|
||||
|
||||
legacyEndpointGroups, err := m.endpointGroupService.EndpointGroups()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpointGroup := range legacyEndpointGroups {
|
||||
endpointGroup.UserAccessPolicies = make(portainer.UserAccessPolicies)
|
||||
for _, userID := range endpointGroup.AuthorizedUsers {
|
||||
endpointGroup.UserAccessPolicies[userID] = portainer.AccessPolicy{
|
||||
RoleID: 4,
|
||||
}
|
||||
}
|
||||
|
||||
endpointGroup.TeamAccessPolicies = make(portainer.TeamAccessPolicies)
|
||||
for _, teamID := range endpointGroup.AuthorizedTeams {
|
||||
endpointGroup.TeamAccessPolicies[teamID] = portainer.AccessPolicy{
|
||||
RoleID: 4,
|
||||
}
|
||||
}
|
||||
|
||||
err = m.endpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) updateRegistriesToDBVersion18() error {
|
||||
log.Info().Msg("updating registries")
|
||||
|
||||
legacyRegistries, err := m.registryService.Registries()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, registry := range legacyRegistries {
|
||||
registry.UserAccessPolicies = make(portainer.UserAccessPolicies)
|
||||
for _, userID := range registry.AuthorizedUsers {
|
||||
registry.UserAccessPolicies[userID] = portainer.AccessPolicy{}
|
||||
}
|
||||
|
||||
registry.TeamAccessPolicies = make(portainer.TeamAccessPolicies)
|
||||
for _, teamID := range registry.AuthorizedTeams {
|
||||
registry.TeamAccessPolicies[teamID] = portainer.AccessPolicy{}
|
||||
}
|
||||
|
||||
err = m.registryService.UpdateRegistry(registry.ID, ®istry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateSettingsToDBVersion19() error {
|
||||
log.Info().Msg("updating settings")
|
||||
|
||||
legacySettings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if legacySettings.EdgeAgentCheckinInterval == 0 {
|
||||
legacySettings.EdgeAgentCheckinInterval = portainer.DefaultEdgeAgentCheckinIntervalInSeconds
|
||||
}
|
||||
|
||||
return m.settingsService.UpdateSettings(legacySettings)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const scheduleScriptExecutionJobType = 1
|
||||
|
||||
func (m *Migrator) updateUsersToDBVersion20() error {
|
||||
log.Info().Msg("updating user authentication")
|
||||
|
||||
return m.authorizationService.UpdateUsersAuthorizations()
|
||||
}
|
||||
|
||||
func (m *Migrator) updateSettingsToDBVersion20() error {
|
||||
legacySettings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
legacySettings.AllowVolumeBrowserForRegularUsers = false
|
||||
|
||||
return m.settingsService.UpdateSettings(legacySettings)
|
||||
}
|
||||
|
||||
func (m *Migrator) updateSchedulesToDBVersion20() error {
|
||||
log.Info().Msg("updating schedules")
|
||||
|
||||
legacySchedules, err := m.scheduleService.Schedules()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, schedule := range legacySchedules {
|
||||
if schedule.JobType == scheduleScriptExecutionJobType {
|
||||
if schedule.CronExpression == "0 0 * * *" {
|
||||
schedule.CronExpression = "0 * * * *"
|
||||
} else if schedule.CronExpression == "0 0 0/2 * *" {
|
||||
schedule.CronExpression = "0 */2 * * *"
|
||||
} else if schedule.CronExpression == "0 0 0 * *" {
|
||||
schedule.CronExpression = "0 0 * * *"
|
||||
} else {
|
||||
revisedCronExpression := strings.Split(schedule.CronExpression, " ")
|
||||
if len(revisedCronExpression) == 5 {
|
||||
continue
|
||||
}
|
||||
|
||||
revisedCronExpression = revisedCronExpression[1:]
|
||||
schedule.CronExpression = strings.Join(revisedCronExpression, " ")
|
||||
}
|
||||
|
||||
err := m.scheduleService.UpdateSchedule(schedule.ID, &schedule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateResourceControlsToDBVersion22() error {
|
||||
log.Info().Msg("updating resource controls")
|
||||
|
||||
legacyResourceControls, err := m.resourceControlService.ResourceControls()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, resourceControl := range legacyResourceControls {
|
||||
resourceControl.AdministratorsOnly = false
|
||||
|
||||
err := m.resourceControlService.UpdateResourceControl(resourceControl.ID, &resourceControl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) updateUsersAndRolesToDBVersion22() error {
|
||||
log.Info().Msg("updating users and roles")
|
||||
|
||||
legacyUsers, err := m.userService.Users()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range legacyUsers {
|
||||
user.PortainerAuthorizations = authorization.DefaultPortainerAuthorizations()
|
||||
err = m.userService.UpdateUser(user.ID, &user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
endpointAdministratorRole, err := m.roleService.Role(portainer.RoleID(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpointAdministratorRole.Priority = 1
|
||||
endpointAdministratorRole.Authorizations = authorization.DefaultEndpointAuthorizationsForEndpointAdministratorRole()
|
||||
|
||||
err = m.roleService.UpdateRole(endpointAdministratorRole.ID, endpointAdministratorRole)
|
||||
|
||||
helpDeskRole, err := m.roleService.Role(portainer.RoleID(2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helpDeskRole.Priority = 2
|
||||
helpDeskRole.Authorizations = authorization.DefaultEndpointAuthorizationsForHelpDeskRole(settings.AllowVolumeBrowserForRegularUsers)
|
||||
|
||||
err = m.roleService.UpdateRole(helpDeskRole.ID, helpDeskRole)
|
||||
|
||||
standardUserRole, err := m.roleService.Role(portainer.RoleID(3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
standardUserRole.Priority = 3
|
||||
standardUserRole.Authorizations = authorization.DefaultEndpointAuthorizationsForStandardUserRole(settings.AllowVolumeBrowserForRegularUsers)
|
||||
|
||||
err = m.roleService.UpdateRole(standardUserRole.ID, standardUserRole)
|
||||
|
||||
readOnlyUserRole, err := m.roleService.Role(portainer.RoleID(4))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
readOnlyUserRole.Priority = 4
|
||||
readOnlyUserRole.Authorizations = authorization.DefaultEndpointAuthorizationsForReadOnlyUserRole(settings.AllowVolumeBrowserForRegularUsers)
|
||||
|
||||
err = m.roleService.UpdateRole(readOnlyUserRole.ID, readOnlyUserRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.authorizationService.UpdateUsersAuthorizations()
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateTagsToDBVersion23() error {
|
||||
log.Info().Msg("updating tags")
|
||||
|
||||
tags, err := m.tagService.Tags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, tag := range tags {
|
||||
tag.EndpointGroups = make(map[portainer.EndpointGroupID]bool)
|
||||
tag.Endpoints = make(map[portainer.EndpointID]bool)
|
||||
err = m.tagService.UpdateTag(tag.ID, &tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) updateEndpointsAndEndpointGroupsToDBVersion23() error {
|
||||
log.Info().Msg("updating endpoints and endpoint groups")
|
||||
|
||||
tags, err := m.tagService.Tags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tagsNameMap := make(map[string]portainer.Tag)
|
||||
for _, tag := range tags {
|
||||
tagsNameMap[tag.Name] = tag
|
||||
}
|
||||
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
endpointTags := make([]portainer.TagID, 0)
|
||||
for _, tagName := range endpoint.Tags {
|
||||
tag, ok := tagsNameMap[tagName]
|
||||
if ok {
|
||||
endpointTags = append(endpointTags, tag.ID)
|
||||
tag.Endpoints[endpoint.ID] = true
|
||||
}
|
||||
}
|
||||
endpoint.TagIDs = endpointTags
|
||||
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relation := &portainer.EndpointRelation{
|
||||
EndpointID: endpoint.ID,
|
||||
EdgeStacks: map[portainer.EdgeStackID]bool{},
|
||||
}
|
||||
|
||||
err = m.endpointRelationService.Create(relation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
endpointGroups, err := m.endpointGroupService.EndpointGroups()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpointGroup := range endpointGroups {
|
||||
endpointGroupTags := make([]portainer.TagID, 0)
|
||||
for _, tagName := range endpointGroup.Tags {
|
||||
tag, ok := tagsNameMap[tagName]
|
||||
if ok {
|
||||
endpointGroupTags = append(endpointGroupTags, tag.ID)
|
||||
tag.EndpointGroups[endpointGroup.ID] = true
|
||||
}
|
||||
}
|
||||
endpointGroup.TagIDs = endpointGroupTags
|
||||
err = m.endpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, tag := range tagsNameMap {
|
||||
err = m.tagService.UpdateTag(tag.ID, &tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateSettingsToDB24() error {
|
||||
log.Info().Msg("updating Settings")
|
||||
|
||||
legacySettings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
legacySettings.AllowHostNamespaceForRegularUsers = true
|
||||
legacySettings.AllowDeviceMappingForRegularUsers = true
|
||||
legacySettings.AllowStackManagementForRegularUsers = true
|
||||
|
||||
return m.settingsService.UpdateSettings(legacySettings)
|
||||
}
|
||||
|
||||
func (m *Migrator) updateStacksToDB24() error {
|
||||
log.Info().Msg("updating stacks")
|
||||
|
||||
stacks, err := m.stackService.Stacks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for idx := range stacks {
|
||||
stack := &stacks[idx]
|
||||
stack.Status = portainer.StackStatusActive
|
||||
err := m.stackService.UpdateStack(stack.ID, stack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateSettingsToDB25() error {
|
||||
log.Info().Msg("updating settings")
|
||||
|
||||
legacySettings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if legacySettings.TemplatesURL == "" {
|
||||
legacySettings.TemplatesURL = portainer.DefaultTemplatesURL
|
||||
}
|
||||
|
||||
legacySettings.UserSessionTimeout = portainer.DefaultUserSessionTimeout
|
||||
legacySettings.EnableTelemetry = true
|
||||
|
||||
legacySettings.AllowContainerCapabilitiesForRegularUsers = true
|
||||
|
||||
return m.settingsService.UpdateSettings(legacySettings)
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateEndpointSettingsToDB25() error {
|
||||
log.Info().Msg("updating endpoint settings")
|
||||
|
||||
settings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range endpoints {
|
||||
endpoint := endpoints[i]
|
||||
|
||||
securitySettings := portainer.EndpointSecuritySettings{}
|
||||
|
||||
if endpoint.Type == portainer.EdgeAgentOnDockerEnvironment ||
|
||||
endpoint.Type == portainer.AgentOnDockerEnvironment ||
|
||||
endpoint.Type == portainer.DockerEnvironment {
|
||||
|
||||
securitySettings = portainer.EndpointSecuritySettings{
|
||||
AllowBindMountsForRegularUsers: settings.AllowBindMountsForRegularUsers,
|
||||
AllowContainerCapabilitiesForRegularUsers: settings.AllowContainerCapabilitiesForRegularUsers,
|
||||
AllowDeviceMappingForRegularUsers: settings.AllowDeviceMappingForRegularUsers,
|
||||
AllowHostNamespaceForRegularUsers: settings.AllowHostNamespaceForRegularUsers,
|
||||
AllowPrivilegedModeForRegularUsers: settings.AllowPrivilegedModeForRegularUsers,
|
||||
AllowStackManagementForRegularUsers: settings.AllowStackManagementForRegularUsers,
|
||||
}
|
||||
|
||||
if endpoint.Type == portainer.AgentOnDockerEnvironment || endpoint.Type == portainer.EdgeAgentOnDockerEnvironment {
|
||||
securitySettings.AllowVolumeBrowserForRegularUsers = settings.AllowVolumeBrowserForRegularUsers
|
||||
securitySettings.EnableHostManagementFeatures = settings.EnableHostManagementFeatures
|
||||
}
|
||||
}
|
||||
|
||||
endpoint.SecuritySettings = securitySettings
|
||||
|
||||
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
"github.com/portainer/portainer/api/internal/stackutils"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateStackResourceControlToDB27() error {
|
||||
log.Info().Msg("updating stack resource controls")
|
||||
|
||||
resourceControls, err := m.resourceControlService.ResourceControls()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, resource := range resourceControls {
|
||||
if resource.Type != portainer.StackResourceControl {
|
||||
continue
|
||||
}
|
||||
|
||||
stackName := resource.ResourceID
|
||||
|
||||
stack, err := m.stackService.StackByName(stackName)
|
||||
if err != nil {
|
||||
if err == errors.ErrObjectNotFound {
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
resource.ResourceID = stackutils.ResourceControlID(stack.EndpointID, stack.Name)
|
||||
|
||||
err = m.resourceControlService.UpdateResourceControl(resource.ID, &resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import "github.com/rs/zerolog/log"
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB30() error {
|
||||
log.Info().Msg("updating legacy settings")
|
||||
|
||||
return m.MigrateSettingsToDB30()
|
||||
}
|
||||
|
||||
// so setting to false and "", is what would happen without this code
|
||||
// I'm going to bet there's zero point to changing the value inthe DB
|
||||
// Public for testing
|
||||
func (m *Migrator) MigrateSettingsToDB30() error {
|
||||
legacySettings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
legacySettings.OAuthSettings.SSO = false
|
||||
legacySettings.OAuthSettings.LogoutURI = ""
|
||||
return m.settingsService.UpdateSettings(legacySettings)
|
||||
}
|
||||
@@ -1,285 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
snapshotutils "github.com/portainer/portainer/api/internal/snapshot"
|
||||
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB32() error {
|
||||
err := m.updateRegistriesToDB32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.updateDockerhubToDB32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.updateVolumeResourceControlToDB32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.kubeconfigExpiryToDB32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.helmRepositoryURLToDB32(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) updateRegistriesToDB32() error {
|
||||
log.Info().Msg("updating registries")
|
||||
|
||||
registries, err := m.registryService.Registries()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, registry := range registries {
|
||||
|
||||
registry.RegistryAccesses = portainer.RegistryAccesses{}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
|
||||
filteredUserAccessPolicies := portainer.UserAccessPolicies{}
|
||||
for userId, registryPolicy := range registry.UserAccessPolicies {
|
||||
if _, found := endpoint.UserAccessPolicies[userId]; found {
|
||||
filteredUserAccessPolicies[userId] = registryPolicy
|
||||
}
|
||||
}
|
||||
|
||||
filteredTeamAccessPolicies := portainer.TeamAccessPolicies{}
|
||||
for teamId, registryPolicy := range registry.TeamAccessPolicies {
|
||||
if _, found := endpoint.TeamAccessPolicies[teamId]; found {
|
||||
filteredTeamAccessPolicies[teamId] = registryPolicy
|
||||
}
|
||||
}
|
||||
|
||||
registry.RegistryAccesses[endpoint.ID] = portainer.RegistryAccessPolicies{
|
||||
UserAccessPolicies: filteredUserAccessPolicies,
|
||||
TeamAccessPolicies: filteredTeamAccessPolicies,
|
||||
Namespaces: []string{},
|
||||
}
|
||||
}
|
||||
m.registryService.UpdateRegistry(registry.ID, ®istry)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) updateDockerhubToDB32() error {
|
||||
log.Info().Msg("updating dockerhub")
|
||||
|
||||
dockerhub, err := m.dockerhubService.DockerHub()
|
||||
if err == errors.ErrObjectNotFound {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !dockerhub.Authentication {
|
||||
return nil
|
||||
}
|
||||
|
||||
registry := &portainer.Registry{
|
||||
Type: portainer.DockerHubRegistry,
|
||||
Name: "Dockerhub (authenticated - migrated)",
|
||||
URL: "docker.io",
|
||||
Authentication: true,
|
||||
Username: dockerhub.Username,
|
||||
Password: dockerhub.Password,
|
||||
RegistryAccesses: portainer.RegistryAccesses{},
|
||||
}
|
||||
|
||||
// The following code will make this function idempotent.
|
||||
// i.e. if run again, it will not change the data. It will ensure that
|
||||
// we only have one migrated registry entry. Duplicates will be removed
|
||||
// if they exist and which has been happening due to earlier migration bugs
|
||||
migrated := false
|
||||
registries, _ := m.registryService.Registries()
|
||||
for _, r := range registries {
|
||||
if r.Type == registry.Type &&
|
||||
r.Name == registry.Name &&
|
||||
r.URL == registry.URL &&
|
||||
r.Authentication == registry.Authentication {
|
||||
|
||||
if !migrated {
|
||||
// keep this one entry
|
||||
migrated = true
|
||||
} else {
|
||||
// delete subsequent duplicates
|
||||
m.registryService.DeleteRegistry(portainer.RegistryID(r.ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if migrated {
|
||||
return nil
|
||||
}
|
||||
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
|
||||
if endpoint.Type != portainer.KubernetesLocalEnvironment &&
|
||||
endpoint.Type != portainer.AgentOnKubernetesEnvironment &&
|
||||
endpoint.Type != portainer.EdgeAgentOnKubernetesEnvironment {
|
||||
|
||||
userAccessPolicies := portainer.UserAccessPolicies{}
|
||||
for userId := range endpoint.UserAccessPolicies {
|
||||
if _, found := endpoint.UserAccessPolicies[userId]; found {
|
||||
userAccessPolicies[userId] = portainer.AccessPolicy{
|
||||
RoleID: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
teamAccessPolicies := portainer.TeamAccessPolicies{}
|
||||
for teamId := range endpoint.TeamAccessPolicies {
|
||||
if _, found := endpoint.TeamAccessPolicies[teamId]; found {
|
||||
teamAccessPolicies[teamId] = portainer.AccessPolicy{
|
||||
RoleID: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registry.RegistryAccesses[endpoint.ID] = portainer.RegistryAccessPolicies{
|
||||
UserAccessPolicies: userAccessPolicies,
|
||||
TeamAccessPolicies: teamAccessPolicies,
|
||||
Namespaces: []string{},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m.registryService.Create(registry)
|
||||
}
|
||||
|
||||
func (m *Migrator) updateVolumeResourceControlToDB32() error {
|
||||
log.Info().Msg("updating resource controls")
|
||||
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed fetching environments: %w", err)
|
||||
}
|
||||
|
||||
resourceControls, err := m.resourceControlService.ResourceControls()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed fetching resource controls: %w", err)
|
||||
}
|
||||
|
||||
toUpdate := map[portainer.ResourceControlID]string{}
|
||||
volumeResourceControls := map[string]*portainer.ResourceControl{}
|
||||
|
||||
for i := range resourceControls {
|
||||
resourceControl := resourceControls[i]
|
||||
if resourceControl.Type == portainer.VolumeResourceControl {
|
||||
volumeResourceControls[resourceControl.ResourceID] = &resourceControl
|
||||
}
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
if !endpointutils.IsDockerEndpoint(&endpoint) {
|
||||
continue
|
||||
}
|
||||
|
||||
totalSnapshots := len(endpoint.Snapshots)
|
||||
if totalSnapshots == 0 {
|
||||
log.Debug().Msg("no snapshot found")
|
||||
continue
|
||||
}
|
||||
|
||||
snapshot := endpoint.Snapshots[totalSnapshots-1]
|
||||
|
||||
endpointDockerID, err := snapshotutils.FetchDockerID(snapshot)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("failed fetching environment docker id")
|
||||
continue
|
||||
}
|
||||
|
||||
volumesData := snapshot.SnapshotRaw.Volumes
|
||||
if volumesData.Volumes == nil {
|
||||
log.Debug().Msg("no volume data found")
|
||||
continue
|
||||
}
|
||||
|
||||
findResourcesToUpdateForDB32(endpointDockerID, volumesData, toUpdate, volumeResourceControls)
|
||||
|
||||
}
|
||||
|
||||
for _, resourceControl := range volumeResourceControls {
|
||||
if newResourceID, ok := toUpdate[resourceControl.ID]; ok {
|
||||
resourceControl.ResourceID = newResourceID
|
||||
|
||||
err := m.resourceControlService.UpdateResourceControl(resourceControl.ID, resourceControl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed updating resource control %d: %w", resourceControl.ID, err)
|
||||
}
|
||||
} else {
|
||||
err := m.resourceControlService.DeleteResourceControl(resourceControl.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed deleting resource control %d: %w", resourceControl.ID, err)
|
||||
}
|
||||
|
||||
log.Debug().Str("resource_id", resourceControl.ResourceID).Msg("legacy resource control has been deleted")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findResourcesToUpdateForDB32(dockerID string, volumesData volume.VolumeListOKBody, toUpdate map[portainer.ResourceControlID]string, volumeResourceControls map[string]*portainer.ResourceControl) {
|
||||
volumes := volumesData.Volumes
|
||||
for _, volume := range volumes {
|
||||
volumeName := volume.Name
|
||||
createTime := volume.CreatedAt
|
||||
|
||||
oldResourceID := fmt.Sprintf("%s%s", volumeName, createTime)
|
||||
resourceControl, ok := volumeResourceControls[oldResourceID]
|
||||
|
||||
if ok {
|
||||
toUpdate[resourceControl.ID] = fmt.Sprintf("%s_%s", volumeName, dockerID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Migrator) kubeconfigExpiryToDB32() error {
|
||||
log.Info().Msg("updating kubeconfig expiry")
|
||||
|
||||
settings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.KubeconfigExpiry = portainer.DefaultKubeconfigExpiry
|
||||
return m.settingsService.UpdateSettings(settings)
|
||||
}
|
||||
|
||||
func (m *Migrator) helmRepositoryURLToDB32() error {
|
||||
log.Info().Msg("setting default helm repository URL")
|
||||
|
||||
settings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.HelmRepositoryURL = portainer.DefaultHelmRepositoryURL
|
||||
return m.settingsService.UpdateSettings(settings)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB33() error {
|
||||
log.Info().Msg("updating settings")
|
||||
|
||||
return m.migrateSettingsToDB33()
|
||||
}
|
||||
|
||||
func (m *Migrator) migrateSettingsToDB33() error {
|
||||
settings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Msg("setting default kubectl shell image")
|
||||
settings.KubectlShellImage = portainer.DefaultKubectlShellImage
|
||||
|
||||
return m.settingsService.UpdateSettings(settings)
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB34() error {
|
||||
log.Info().Msg("updating stacks")
|
||||
|
||||
return MigrateStackEntryPoint(m.stackService)
|
||||
}
|
||||
|
||||
// MigrateStackEntryPoint exported for testing (blah.)
|
||||
func MigrateStackEntryPoint(stackService dataservices.StackService) error {
|
||||
stacks, err := stackService.Stacks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range stacks {
|
||||
stack := &stacks[i]
|
||||
if stack.GitConfig == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
stack.GitConfig.ConfigFilePath = stack.EntryPoint
|
||||
if err := stackService.UpdateStack(stack.ID, stack); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import "github.com/rs/zerolog/log"
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB35() error {
|
||||
// These should have been migrated already, but due to an earlier bug and a bunch of duplicates,
|
||||
// calling it again will now fix the issue as the function has been repaired.
|
||||
|
||||
log.Info().Msg("updating dockerhub registries")
|
||||
|
||||
return m.updateDockerhubToDB32()
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package migrator
|
||||
|
||||
const (
|
||||
db35TestFile = "portainer-mig-35.db"
|
||||
username = "portainer"
|
||||
password = "password"
|
||||
)
|
||||
|
||||
// TODO: this is exactly the kind of reaching into the internals of the store we should not do
|
||||
// func setupDB35Test(t *testing.T) *Migrator {
|
||||
// is := assert.New(t)
|
||||
// dbConn, err := bolt.Open(path.Join(t.TempDir(), db35TestFile), 0600, &bolt.Options{Timeout: 1 * time.Second})
|
||||
// is.NoError(err, "failed to init testing DB connection")
|
||||
|
||||
// // Create an old style dockerhub authenticated account
|
||||
// dockerhubService, err := dockerhub.NewService(&database.DbConnection{DB: dbConn})
|
||||
// is.NoError(err, "failed to init testing registry service")
|
||||
// err = dockerhubService.UpdateDockerHub(&portainer.DockerHub{true, username, password})
|
||||
// is.NoError(err, "failed to create dockerhub account")
|
||||
|
||||
// registryService, err := registry.NewService(&database.DbConnection{DB: dbConn})
|
||||
// is.NoError(err, "failed to init testing registry service")
|
||||
|
||||
// endpointService, err := endpoint.NewService(&database.DbConnection{DB: dbConn})
|
||||
// is.NoError(err, "failed to init endpoint service")
|
||||
|
||||
// m := &Migrator{
|
||||
// db: dbConn,
|
||||
// dockerhubService: dockerhubService,
|
||||
// registryService: registryService,
|
||||
// endpointService: endpointService,
|
||||
// }
|
||||
|
||||
// return m
|
||||
// }
|
||||
|
||||
// // TestUpdateDockerhubToDB32 tests a normal upgrade
|
||||
// func TestUpdateDockerhubToDB32(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
// m := setupDB35Test(t)
|
||||
// defer m.db.Close()
|
||||
// defer os.Remove(db35TestFile)
|
||||
|
||||
// if err := m.updateDockerhubToDB32(); err != nil {
|
||||
// t.Errorf("failed to update settings: %v", err)
|
||||
// }
|
||||
|
||||
// // Verify we have a single registry were created
|
||||
// registries, err := m.registryService.Registries()
|
||||
// is.NoError(err, "failed to read registries from the RegistryService")
|
||||
// is.Equal(len(registries), 1, "only one migrated registry expected")
|
||||
// }
|
||||
|
||||
// // TestUpdateDockerhubToDB32_with_duplicate_migrations tests an upgrade where in earlier versions a broken migration
|
||||
// // created a large number of duplicate "dockerhub migrated" registry entries.
|
||||
// func TestUpdateDockerhubToDB32_with_duplicate_migrations(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
// m := setupDB35Test(t)
|
||||
// defer m.db.Close()
|
||||
// defer os.Remove(db35TestFile)
|
||||
|
||||
// // Create lots of duplicate entries...
|
||||
// registry := &portainer.Registry{
|
||||
// Type: portainer.DockerHubRegistry,
|
||||
// Name: "Dockerhub (authenticated - migrated)",
|
||||
// URL: "docker.io",
|
||||
// Authentication: true,
|
||||
// Username: "portainer",
|
||||
// Password: "password",
|
||||
// RegistryAccesses: portainer.RegistryAccesses{},
|
||||
// }
|
||||
|
||||
// for i := 1; i < 150; i++ {
|
||||
// err := m.registryService.CreateRegistry(registry)
|
||||
// assert.NoError(t, err, "create registry failed")
|
||||
// }
|
||||
|
||||
// // Verify they were created
|
||||
// registries, err := m.registryService.Registries()
|
||||
// is.NoError(err, "failed to read registries from the RegistryService")
|
||||
// is.Condition(func() bool {
|
||||
// return len(registries) > 1
|
||||
// }, "expected multiple duplicate registry entries")
|
||||
|
||||
// // Now run the migrator
|
||||
// if err := m.updateDockerhubToDB32(); err != nil {
|
||||
// t.Errorf("failed to update settings: %v", err)
|
||||
// }
|
||||
|
||||
// // Verify we have a single registry were created
|
||||
// registries, err = m.registryService.Registries()
|
||||
// is.NoError(err, "failed to read registries from the RegistryService")
|
||||
// is.Equal(len(registries), 1, "only one migrated registry expected")
|
||||
// }
|
||||
@@ -1,35 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB36() error {
|
||||
log.Info().Msg("updating user authorizations")
|
||||
|
||||
return m.migrateUsersToDB36()
|
||||
}
|
||||
|
||||
func (m *Migrator) migrateUsersToDB36() error {
|
||||
users, err := m.userService.Users()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
currentAuthorizations := authorization.DefaultPortainerAuthorizations()
|
||||
currentAuthorizations[portainer.OperationPortainerUserListToken] = true
|
||||
currentAuthorizations[portainer.OperationPortainerUserCreateToken] = true
|
||||
currentAuthorizations[portainer.OperationPortainerUserRevokeToken] = true
|
||||
user.PortainerAuthorizations = currentAuthorizations
|
||||
err = m.userService.UpdateUser(user.ID, &user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB40() error {
|
||||
return m.trustCurrentEdgeEndpointsDB40()
|
||||
}
|
||||
|
||||
func (m *Migrator) trustCurrentEdgeEndpointsDB40() error {
|
||||
log.Info().Msg("trusting current edge endpoints")
|
||||
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
if endpointutils.IsEdgeEndpoint(&endpoint) {
|
||||
endpoint.UserTrusted = true
|
||||
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB50() error {
|
||||
return m.migratePasswordLengthSettings()
|
||||
}
|
||||
|
||||
func (m *Migrator) migratePasswordLengthSettings() error {
|
||||
log.Info().Msg("updating required password length")
|
||||
|
||||
s, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to retrieve settings")
|
||||
}
|
||||
|
||||
s.InternalAuthSettings.RequiredPasswordLength = 12
|
||||
return m.settingsService.UpdateSettings(s)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB60() error {
|
||||
return m.addGpuInputFieldDB60()
|
||||
}
|
||||
|
||||
func (m *Migrator) addGpuInputFieldDB60() error {
|
||||
log.Info().Msg("add gpu input field")
|
||||
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint.Gpus = []portainer.Pair{}
|
||||
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB70() error {
|
||||
log.Info().Msg("- add IngressAvailabilityPerNamespace field")
|
||||
if err := m.addIngressAvailabilityPerNamespaceFieldDB70(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
// copy snapshots to new object
|
||||
log.Info().Msg("moving snapshots from endpoint to new object")
|
||||
snapshot := portainer.Snapshot{EndpointID: endpoint.ID}
|
||||
|
||||
if len(endpoint.Snapshots) > 0 {
|
||||
snapshot.Docker = &endpoint.Snapshots[len(endpoint.Snapshots)-1]
|
||||
}
|
||||
|
||||
if len(endpoint.Kubernetes.Snapshots) > 0 {
|
||||
snapshot.Kubernetes = &endpoint.Kubernetes.Snapshots[len(endpoint.Kubernetes.Snapshots)-1]
|
||||
}
|
||||
|
||||
// save new object
|
||||
err = m.snapshotService.Create(&snapshot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set to nil old fields
|
||||
log.Info().Msg("deleting snapshot from endpoint")
|
||||
endpoint.Snapshots = []portainer.DockerSnapshot{}
|
||||
endpoint.Kubernetes.Snapshots = []portainer.KubernetesSnapshot{}
|
||||
|
||||
// update endpoint
|
||||
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) addIngressAvailabilityPerNamespaceFieldDB70() error {
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint.Kubernetes.Configuration.IngressAvailabilityPerNamespace = true
|
||||
|
||||
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/dockerhub"
|
||||
"github.com/portainer/portainer/api/dataservices/endpoint"
|
||||
"github.com/portainer/portainer/api/dataservices/endpointgroup"
|
||||
"github.com/portainer/portainer/api/dataservices/endpointrelation"
|
||||
"github.com/portainer/portainer/api/dataservices/extension"
|
||||
"github.com/portainer/portainer/api/dataservices/fdoprofile"
|
||||
"github.com/portainer/portainer/api/dataservices/registry"
|
||||
"github.com/portainer/portainer/api/dataservices/resourcecontrol"
|
||||
"github.com/portainer/portainer/api/dataservices/role"
|
||||
"github.com/portainer/portainer/api/dataservices/schedule"
|
||||
"github.com/portainer/portainer/api/dataservices/settings"
|
||||
"github.com/portainer/portainer/api/dataservices/snapshot"
|
||||
"github.com/portainer/portainer/api/dataservices/stack"
|
||||
"github.com/portainer/portainer/api/dataservices/tag"
|
||||
"github.com/portainer/portainer/api/dataservices/teammembership"
|
||||
"github.com/portainer/portainer/api/dataservices/user"
|
||||
"github.com/portainer/portainer/api/dataservices/version"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
)
|
||||
|
||||
type (
|
||||
// Migrator defines a service to migrate data after a Portainer version update.
|
||||
Migrator struct {
|
||||
currentDBVersion int
|
||||
endpointGroupService *endpointgroup.Service
|
||||
endpointService *endpoint.Service
|
||||
endpointRelationService *endpointrelation.Service
|
||||
extensionService *extension.Service
|
||||
fdoProfilesService *fdoprofile.Service
|
||||
registryService *registry.Service
|
||||
resourceControlService *resourcecontrol.Service
|
||||
roleService *role.Service
|
||||
scheduleService *schedule.Service
|
||||
settingsService *settings.Service
|
||||
snapshotService *snapshot.Service
|
||||
stackService *stack.Service
|
||||
tagService *tag.Service
|
||||
teamMembershipService *teammembership.Service
|
||||
userService *user.Service
|
||||
versionService *version.Service
|
||||
fileService portainer.FileService
|
||||
authorizationService *authorization.Service
|
||||
dockerhubService *dockerhub.Service
|
||||
}
|
||||
|
||||
// MigratorParameters represents the required parameters to create a new Migrator instance.
|
||||
MigratorParameters struct {
|
||||
DatabaseVersion int
|
||||
EndpointGroupService *endpointgroup.Service
|
||||
EndpointService *endpoint.Service
|
||||
EndpointRelationService *endpointrelation.Service
|
||||
ExtensionService *extension.Service
|
||||
FDOProfilesService *fdoprofile.Service
|
||||
RegistryService *registry.Service
|
||||
ResourceControlService *resourcecontrol.Service
|
||||
RoleService *role.Service
|
||||
ScheduleService *schedule.Service
|
||||
SettingsService *settings.Service
|
||||
SnapshotService *snapshot.Service
|
||||
StackService *stack.Service
|
||||
TagService *tag.Service
|
||||
TeamMembershipService *teammembership.Service
|
||||
UserService *user.Service
|
||||
VersionService *version.Service
|
||||
FileService portainer.FileService
|
||||
AuthorizationService *authorization.Service
|
||||
DockerhubService *dockerhub.Service
|
||||
}
|
||||
)
|
||||
|
||||
// NewMigrator creates a new Migrator.
|
||||
func NewMigrator(parameters *MigratorParameters) *Migrator {
|
||||
return &Migrator{
|
||||
currentDBVersion: parameters.DatabaseVersion,
|
||||
endpointGroupService: parameters.EndpointGroupService,
|
||||
endpointService: parameters.EndpointService,
|
||||
endpointRelationService: parameters.EndpointRelationService,
|
||||
extensionService: parameters.ExtensionService,
|
||||
fdoProfilesService: parameters.FDOProfilesService,
|
||||
registryService: parameters.RegistryService,
|
||||
resourceControlService: parameters.ResourceControlService,
|
||||
roleService: parameters.RoleService,
|
||||
scheduleService: parameters.ScheduleService,
|
||||
settingsService: parameters.SettingsService,
|
||||
snapshotService: parameters.SnapshotService,
|
||||
tagService: parameters.TagService,
|
||||
teamMembershipService: parameters.TeamMembershipService,
|
||||
stackService: parameters.StackService,
|
||||
userService: parameters.UserService,
|
||||
versionService: parameters.VersionService,
|
||||
fileService: parameters.FileService,
|
||||
authorizationService: parameters.AuthorizationService,
|
||||
dockerhubService: parameters.DockerhubService,
|
||||
}
|
||||
}
|
||||
|
||||
// Version exposes version of database
|
||||
func (migrator *Migrator) Version() int {
|
||||
return migrator.currentDBVersion
|
||||
}
|
||||
@@ -1,11 +1,6 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/dataservices/apikeyrepository"
|
||||
@@ -36,8 +31,6 @@ import (
|
||||
"github.com/portainer/portainer/api/dataservices/user"
|
||||
"github.com/portainer/portainer/api/dataservices/version"
|
||||
"github.com/portainer/portainer/api/dataservices/webhook"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Store defines the implementation of portainer.DataStore using
|
||||
@@ -399,321 +392,3 @@ type storeExport struct {
|
||||
Webhook []portainer.Webhook `json:"webhooks,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
backup := storeExport{}
|
||||
|
||||
if c, err := store.CustomTemplate().CustomTemplates(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Custom Templates")
|
||||
}
|
||||
} else {
|
||||
backup.CustomTemplate = c
|
||||
}
|
||||
|
||||
if e, err := store.EdgeGroup().EdgeGroups(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Edge Groups")
|
||||
}
|
||||
} else {
|
||||
backup.EdgeGroup = e
|
||||
}
|
||||
|
||||
if e, err := store.EdgeJob().EdgeJobs(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Edge Jobs")
|
||||
}
|
||||
} else {
|
||||
backup.EdgeJob = e
|
||||
}
|
||||
|
||||
if e, err := store.EdgeStack().EdgeStacks(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Edge Stacks")
|
||||
}
|
||||
} else {
|
||||
backup.EdgeStack = e
|
||||
}
|
||||
|
||||
if e, err := store.Endpoint().Endpoints(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Endpoints")
|
||||
}
|
||||
} else {
|
||||
backup.Endpoint = e
|
||||
}
|
||||
|
||||
if e, err := store.EndpointGroup().EndpointGroups(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Endpoint Groups")
|
||||
}
|
||||
} else {
|
||||
backup.EndpointGroup = e
|
||||
}
|
||||
|
||||
if r, err := store.EndpointRelation().EndpointRelations(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Endpoint Relations")
|
||||
}
|
||||
} else {
|
||||
backup.EndpointRelation = r
|
||||
}
|
||||
|
||||
if r, err := store.ExtensionService.Extensions(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Extensions")
|
||||
}
|
||||
} else {
|
||||
backup.Extensions = r
|
||||
}
|
||||
|
||||
if r, err := store.HelmUserRepository().HelmUserRepositories(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Helm User Repositories")
|
||||
}
|
||||
} else {
|
||||
backup.HelmUserRepository = r
|
||||
}
|
||||
|
||||
if r, err := store.Registry().Registries(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Registries")
|
||||
}
|
||||
} else {
|
||||
backup.Registry = r
|
||||
}
|
||||
|
||||
if c, err := store.ResourceControl().ResourceControls(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Resource Controls")
|
||||
}
|
||||
} else {
|
||||
backup.ResourceControl = c
|
||||
}
|
||||
|
||||
if role, err := store.Role().Roles(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Roles")
|
||||
}
|
||||
} else {
|
||||
backup.Role = role
|
||||
}
|
||||
|
||||
if r, err := store.ScheduleService.Schedules(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Schedules")
|
||||
}
|
||||
} else {
|
||||
backup.Schedules = r
|
||||
}
|
||||
|
||||
if settings, err := store.Settings().Settings(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Settings")
|
||||
}
|
||||
} else {
|
||||
backup.Settings = *settings
|
||||
}
|
||||
|
||||
if snapshot, err := store.Snapshot().Snapshots(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Err(err).Msg("Exporting Snapshots")
|
||||
}
|
||||
} else {
|
||||
backup.Snapshot = snapshot
|
||||
}
|
||||
|
||||
if settings, err := store.SSLSettings().Settings(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting SSL Settings")
|
||||
}
|
||||
} else {
|
||||
backup.SSLSettings = *settings
|
||||
}
|
||||
|
||||
if t, err := store.Stack().Stacks(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Stacks")
|
||||
}
|
||||
} else {
|
||||
backup.Stack = t
|
||||
}
|
||||
|
||||
if t, err := store.Tag().Tags(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Tags")
|
||||
}
|
||||
} else {
|
||||
backup.Tag = t
|
||||
}
|
||||
|
||||
if t, err := store.TeamMembership().TeamMemberships(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Team Memberships")
|
||||
}
|
||||
} else {
|
||||
backup.TeamMembership = t
|
||||
}
|
||||
|
||||
if t, err := store.Team().Teams(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Teams")
|
||||
}
|
||||
} else {
|
||||
backup.Team = t
|
||||
}
|
||||
|
||||
if info, err := store.TunnelServer().Info(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Tunnel Server")
|
||||
}
|
||||
} else {
|
||||
backup.TunnelServer = *info
|
||||
}
|
||||
|
||||
if users, err := store.User().Users(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Users")
|
||||
}
|
||||
} else {
|
||||
backup.User = users
|
||||
}
|
||||
|
||||
if webhooks, err := store.Webhook().Webhooks(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Webhooks")
|
||||
}
|
||||
} else {
|
||||
backup.Webhook = webhooks
|
||||
}
|
||||
|
||||
v, err := store.Version().DBVersion()
|
||||
if err != nil && !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting DB version")
|
||||
}
|
||||
instance, _ := store.Version().InstanceID()
|
||||
backup.Version = map[string]string{
|
||||
"DB_VERSION": strconv.Itoa(v),
|
||||
"INSTANCE_ID": instance,
|
||||
}
|
||||
|
||||
backup.Metadata, err = store.connection.BackupMetadata()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("exporting Metadata")
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(backup, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(filename, b, 0600)
|
||||
}
|
||||
|
||||
func (store *Store) Import(filename string) (err error) {
|
||||
backup := storeExport{}
|
||||
|
||||
s, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal([]byte(s), &backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: yup, this is bad, and should be in a version struct...
|
||||
if dbversion, ok := backup.Version["DB_VERSION"]; ok {
|
||||
if v, err := strconv.Atoi(dbversion); err == nil {
|
||||
if err := store.Version().StoreDBVersion(v); err != nil {
|
||||
log.Error().Err(err).Msg("DB_VERSION import issue")
|
||||
}
|
||||
}
|
||||
}
|
||||
if instanceID, ok := backup.Version["INSTANCE_ID"]; ok {
|
||||
if err := store.Version().StoreInstanceID(instanceID); err != nil {
|
||||
log.Error().Err(err).Msg("INSTANCE_ID import issue")
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range backup.CustomTemplate {
|
||||
store.CustomTemplate().UpdateCustomTemplate(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.EdgeGroup {
|
||||
store.EdgeGroup().UpdateEdgeGroup(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.EdgeJob {
|
||||
store.EdgeJob().UpdateEdgeJob(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.EdgeStack {
|
||||
store.EdgeStack().UpdateEdgeStack(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.Endpoint {
|
||||
store.Endpoint().UpdateEndpoint(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.EndpointGroup {
|
||||
store.EndpointGroup().UpdateEndpointGroup(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.EndpointRelation {
|
||||
store.EndpointRelation().UpdateEndpointRelation(v.EndpointID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.HelmUserRepository {
|
||||
store.HelmUserRepository().UpdateHelmUserRepository(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.Registry {
|
||||
store.Registry().UpdateRegistry(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.ResourceControl {
|
||||
store.ResourceControl().UpdateResourceControl(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.Role {
|
||||
store.Role().UpdateRole(v.ID, &v)
|
||||
}
|
||||
|
||||
store.Settings().UpdateSettings(&backup.Settings)
|
||||
store.SSLSettings().UpdateSettings(&backup.SSLSettings)
|
||||
|
||||
for _, v := range backup.Snapshot {
|
||||
store.Snapshot().UpdateSnapshot(&v)
|
||||
}
|
||||
|
||||
for _, v := range backup.Stack {
|
||||
store.Stack().UpdateStack(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.Tag {
|
||||
store.Tag().UpdateTag(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.TeamMembership {
|
||||
store.TeamMembership().UpdateTeamMembership(v.ID, &v)
|
||||
}
|
||||
|
||||
for _, v := range backup.Team {
|
||||
store.Team().UpdateTeam(v.ID, &v)
|
||||
}
|
||||
|
||||
store.TunnelServer().UpdateInfo(&backup.TunnelServer)
|
||||
|
||||
for _, user := range backup.User {
|
||||
if err := store.User().UpdateUser(user.ID, &user); err != nil {
|
||||
log.Debug().Str("user", fmt.Sprintf("%+v", user)).Err(err).Msg("user: failed to Update Database")
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range backup.Webhook {
|
||||
store.Webhook().UpdateWebhook(v.ID, &v)
|
||||
}
|
||||
|
||||
return store.connection.RestoreMetadata(backup.Metadata)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,933 +0,0 @@
|
||||
{
|
||||
"dockerhub": [
|
||||
{
|
||||
"Authentication": false,
|
||||
"Username": ""
|
||||
}
|
||||
],
|
||||
"endpoint_groups": [
|
||||
{
|
||||
"AuthorizedTeams": null,
|
||||
"AuthorizedUsers": null,
|
||||
"Description": "Unassigned endpoints",
|
||||
"Id": 1,
|
||||
"Labels": [],
|
||||
"Name": "Unassigned",
|
||||
"TagIds": [],
|
||||
"Tags": null,
|
||||
"TeamAccessPolicies": {},
|
||||
"UserAccessPolicies": {}
|
||||
}
|
||||
],
|
||||
"endpoint_relations": [
|
||||
{
|
||||
"EdgeStacks": {},
|
||||
"EndpointID": 1
|
||||
}
|
||||
],
|
||||
"endpoints": [
|
||||
{
|
||||
"Agent": {
|
||||
"Version": ""
|
||||
},
|
||||
"AuthorizedTeams": null,
|
||||
"AuthorizedUsers": null,
|
||||
"AzureCredentials": {
|
||||
"ApplicationID": "",
|
||||
"AuthenticationKey": "",
|
||||
"TenantID": ""
|
||||
},
|
||||
"ComposeSyntaxMaxVersion": "",
|
||||
"Edge": {
|
||||
"AsyncMode": false,
|
||||
"CommandInterval": 0,
|
||||
"PingInterval": 0,
|
||||
"SnapshotInterval": 0
|
||||
},
|
||||
"EdgeCheckinInterval": 0,
|
||||
"EdgeKey": "",
|
||||
"Gpus": [],
|
||||
"GroupId": 1,
|
||||
"Id": 1,
|
||||
"IsEdgeDevice": false,
|
||||
"Kubernetes": {
|
||||
"Configuration": {
|
||||
"EnableResourceOverCommit": false,
|
||||
"IngressAvailabilityPerNamespace": true,
|
||||
"IngressClasses": null,
|
||||
"ResourceOverCommitPercentage": 0,
|
||||
"RestrictDefaultNamespace": false,
|
||||
"StorageClasses": null,
|
||||
"UseLoadBalancer": false,
|
||||
"UseServerMetrics": false
|
||||
},
|
||||
"Snapshots": []
|
||||
},
|
||||
"LastCheckInDate": 0,
|
||||
"Name": "local",
|
||||
"PublicURL": "",
|
||||
"QueryDate": 0,
|
||||
"SecuritySettings": {
|
||||
"allowBindMountsForRegularUsers": true,
|
||||
"allowContainerCapabilitiesForRegularUsers": true,
|
||||
"allowDeviceMappingForRegularUsers": true,
|
||||
"allowHostNamespaceForRegularUsers": true,
|
||||
"allowPrivilegedModeForRegularUsers": true,
|
||||
"allowStackManagementForRegularUsers": true,
|
||||
"allowSysctlSettingForRegularUsers": false,
|
||||
"allowVolumeBrowserForRegularUsers": false,
|
||||
"enableHostManagementFeatures": false
|
||||
},
|
||||
"Snapshots": [],
|
||||
"Status": 1,
|
||||
"TLSConfig": {
|
||||
"TLS": false,
|
||||
"TLSSkipVerify": false
|
||||
},
|
||||
"TagIds": [],
|
||||
"Tags": null,
|
||||
"TeamAccessPolicies": {},
|
||||
"Type": 1,
|
||||
"URL": "unix:///var/run/docker.sock",
|
||||
"UserAccessPolicies": {},
|
||||
"UserTrusted": false
|
||||
}
|
||||
],
|
||||
"registries": [
|
||||
{
|
||||
"Authentication": true,
|
||||
"AuthorizedTeams": null,
|
||||
"AuthorizedUsers": null,
|
||||
"BaseURL": "",
|
||||
"Ecr": {
|
||||
"Region": ""
|
||||
},
|
||||
"Gitlab": {
|
||||
"InstanceURL": "",
|
||||
"ProjectId": 0,
|
||||
"ProjectPath": ""
|
||||
},
|
||||
"Id": 1,
|
||||
"ManagementConfiguration": null,
|
||||
"Name": "canister.io",
|
||||
"Password": "MjWbx8A6YK7cw7",
|
||||
"Quay": {
|
||||
"OrganisationName": "",
|
||||
"UseOrganisation": false
|
||||
},
|
||||
"RegistryAccesses": {
|
||||
"1": {
|
||||
"Namespaces": [],
|
||||
"TeamAccessPolicies": {},
|
||||
"UserAccessPolicies": {}
|
||||
}
|
||||
},
|
||||
"TeamAccessPolicies": {},
|
||||
"Type": 3,
|
||||
"URL": "cloud.canister.io:5000",
|
||||
"UserAccessPolicies": {},
|
||||
"Username": "prabhatkhera"
|
||||
}
|
||||
],
|
||||
"resource_control": [
|
||||
{
|
||||
"AdministratorsOnly": false,
|
||||
"Id": 2,
|
||||
"Public": true,
|
||||
"ResourceId": "762gbwaj8r4gcsdy8ld1u4why",
|
||||
"SubResourceIds": [],
|
||||
"System": false,
|
||||
"TeamAccesses": [],
|
||||
"Type": 5,
|
||||
"UserAccesses": []
|
||||
},
|
||||
{
|
||||
"AdministratorsOnly": false,
|
||||
"Id": 3,
|
||||
"Public": true,
|
||||
"ResourceId": "1_alpine",
|
||||
"SubResourceIds": [],
|
||||
"System": false,
|
||||
"TeamAccesses": [],
|
||||
"Type": 6,
|
||||
"UserAccesses": []
|
||||
},
|
||||
{
|
||||
"AdministratorsOnly": false,
|
||||
"Id": 4,
|
||||
"Public": true,
|
||||
"ResourceId": "1_redis",
|
||||
"SubResourceIds": [],
|
||||
"System": false,
|
||||
"TeamAccesses": [],
|
||||
"Type": 6,
|
||||
"UserAccesses": []
|
||||
},
|
||||
{
|
||||
"AdministratorsOnly": false,
|
||||
"Id": 5,
|
||||
"Public": false,
|
||||
"ResourceId": "1_nginx",
|
||||
"SubResourceIds": [],
|
||||
"System": false,
|
||||
"TeamAccesses": [
|
||||
{
|
||||
"AccessLevel": 1,
|
||||
"TeamId": 1
|
||||
}
|
||||
],
|
||||
"Type": 6,
|
||||
"UserAccesses": []
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
{
|
||||
"Authorizations": {
|
||||
"DockerAgentBrowseDelete": true,
|
||||
"DockerAgentBrowseGet": true,
|
||||
"DockerAgentBrowseList": true,
|
||||
"DockerAgentBrowsePut": true,
|
||||
"DockerAgentBrowseRename": true,
|
||||
"DockerAgentHostInfo": true,
|
||||
"DockerAgentList": true,
|
||||
"DockerAgentPing": true,
|
||||
"DockerAgentUndefined": true,
|
||||
"DockerBuildCancel": true,
|
||||
"DockerBuildPrune": true,
|
||||
"DockerConfigCreate": true,
|
||||
"DockerConfigDelete": true,
|
||||
"DockerConfigInspect": true,
|
||||
"DockerConfigList": true,
|
||||
"DockerConfigUpdate": true,
|
||||
"DockerContainerArchive": true,
|
||||
"DockerContainerArchiveInfo": true,
|
||||
"DockerContainerAttach": true,
|
||||
"DockerContainerAttachWebsocket": true,
|
||||
"DockerContainerChanges": true,
|
||||
"DockerContainerCreate": true,
|
||||
"DockerContainerDelete": true,
|
||||
"DockerContainerExec": true,
|
||||
"DockerContainerExport": true,
|
||||
"DockerContainerInspect": true,
|
||||
"DockerContainerKill": true,
|
||||
"DockerContainerList": true,
|
||||
"DockerContainerLogs": true,
|
||||
"DockerContainerPause": true,
|
||||
"DockerContainerPrune": true,
|
||||
"DockerContainerPutContainerArchive": true,
|
||||
"DockerContainerRename": true,
|
||||
"DockerContainerResize": true,
|
||||
"DockerContainerRestart": true,
|
||||
"DockerContainerStart": true,
|
||||
"DockerContainerStats": true,
|
||||
"DockerContainerStop": true,
|
||||
"DockerContainerTop": true,
|
||||
"DockerContainerUnpause": true,
|
||||
"DockerContainerUpdate": true,
|
||||
"DockerContainerWait": true,
|
||||
"DockerDistributionInspect": true,
|
||||
"DockerEvents": true,
|
||||
"DockerExecInspect": true,
|
||||
"DockerExecResize": true,
|
||||
"DockerExecStart": true,
|
||||
"DockerImageBuild": true,
|
||||
"DockerImageCommit": true,
|
||||
"DockerImageCreate": true,
|
||||
"DockerImageDelete": true,
|
||||
"DockerImageGet": true,
|
||||
"DockerImageGetAll": true,
|
||||
"DockerImageHistory": true,
|
||||
"DockerImageInspect": true,
|
||||
"DockerImageList": true,
|
||||
"DockerImageLoad": true,
|
||||
"DockerImagePrune": true,
|
||||
"DockerImagePush": true,
|
||||
"DockerImageSearch": true,
|
||||
"DockerImageTag": true,
|
||||
"DockerInfo": true,
|
||||
"DockerNetworkConnect": true,
|
||||
"DockerNetworkCreate": true,
|
||||
"DockerNetworkDelete": true,
|
||||
"DockerNetworkDisconnect": true,
|
||||
"DockerNetworkInspect": true,
|
||||
"DockerNetworkList": true,
|
||||
"DockerNetworkPrune": true,
|
||||
"DockerNodeDelete": true,
|
||||
"DockerNodeInspect": true,
|
||||
"DockerNodeList": true,
|
||||
"DockerNodeUpdate": true,
|
||||
"DockerPing": true,
|
||||
"DockerPluginCreate": true,
|
||||
"DockerPluginDelete": true,
|
||||
"DockerPluginDisable": true,
|
||||
"DockerPluginEnable": true,
|
||||
"DockerPluginInspect": true,
|
||||
"DockerPluginList": true,
|
||||
"DockerPluginPrivileges": true,
|
||||
"DockerPluginPull": true,
|
||||
"DockerPluginPush": true,
|
||||
"DockerPluginSet": true,
|
||||
"DockerPluginUpgrade": true,
|
||||
"DockerSecretCreate": true,
|
||||
"DockerSecretDelete": true,
|
||||
"DockerSecretInspect": true,
|
||||
"DockerSecretList": true,
|
||||
"DockerSecretUpdate": true,
|
||||
"DockerServiceCreate": true,
|
||||
"DockerServiceDelete": true,
|
||||
"DockerServiceInspect": true,
|
||||
"DockerServiceList": true,
|
||||
"DockerServiceLogs": true,
|
||||
"DockerServiceUpdate": true,
|
||||
"DockerSessionStart": true,
|
||||
"DockerSwarmInit": true,
|
||||
"DockerSwarmInspect": true,
|
||||
"DockerSwarmJoin": true,
|
||||
"DockerSwarmLeave": true,
|
||||
"DockerSwarmUnlock": true,
|
||||
"DockerSwarmUnlockKey": true,
|
||||
"DockerSwarmUpdate": true,
|
||||
"DockerSystem": true,
|
||||
"DockerTaskInspect": true,
|
||||
"DockerTaskList": true,
|
||||
"DockerTaskLogs": true,
|
||||
"DockerUndefined": true,
|
||||
"DockerVersion": true,
|
||||
"DockerVolumeCreate": true,
|
||||
"DockerVolumeDelete": true,
|
||||
"DockerVolumeInspect": true,
|
||||
"DockerVolumeList": true,
|
||||
"DockerVolumePrune": true,
|
||||
"EndpointResourcesAccess": true,
|
||||
"IntegrationStoridgeAdmin": true,
|
||||
"PortainerResourceControlCreate": true,
|
||||
"PortainerResourceControlUpdate": true,
|
||||
"PortainerStackCreate": true,
|
||||
"PortainerStackDelete": true,
|
||||
"PortainerStackFile": true,
|
||||
"PortainerStackInspect": true,
|
||||
"PortainerStackList": true,
|
||||
"PortainerStackMigrate": true,
|
||||
"PortainerStackUpdate": true,
|
||||
"PortainerWebhookCreate": true,
|
||||
"PortainerWebhookDelete": true,
|
||||
"PortainerWebhookList": true,
|
||||
"PortainerWebsocketExec": true
|
||||
},
|
||||
"Description": "Full control of all resources in an endpoint",
|
||||
"Id": 1,
|
||||
"Name": "Endpoint administrator",
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Authorizations": {
|
||||
"DockerAgentHostInfo": true,
|
||||
"DockerAgentList": true,
|
||||
"DockerAgentPing": true,
|
||||
"DockerConfigInspect": true,
|
||||
"DockerConfigList": true,
|
||||
"DockerContainerArchiveInfo": true,
|
||||
"DockerContainerChanges": true,
|
||||
"DockerContainerInspect": true,
|
||||
"DockerContainerList": true,
|
||||
"DockerContainerLogs": true,
|
||||
"DockerContainerStats": true,
|
||||
"DockerContainerTop": true,
|
||||
"DockerDistributionInspect": true,
|
||||
"DockerEvents": true,
|
||||
"DockerImageGet": true,
|
||||
"DockerImageGetAll": true,
|
||||
"DockerImageHistory": true,
|
||||
"DockerImageInspect": true,
|
||||
"DockerImageList": true,
|
||||
"DockerImageSearch": true,
|
||||
"DockerInfo": true,
|
||||
"DockerNetworkInspect": true,
|
||||
"DockerNetworkList": true,
|
||||
"DockerNodeInspect": true,
|
||||
"DockerNodeList": true,
|
||||
"DockerPing": true,
|
||||
"DockerPluginList": true,
|
||||
"DockerSecretInspect": true,
|
||||
"DockerSecretList": true,
|
||||
"DockerServiceInspect": true,
|
||||
"DockerServiceList": true,
|
||||
"DockerServiceLogs": true,
|
||||
"DockerSwarmInspect": true,
|
||||
"DockerSystem": true,
|
||||
"DockerTaskInspect": true,
|
||||
"DockerTaskList": true,
|
||||
"DockerTaskLogs": true,
|
||||
"DockerVersion": true,
|
||||
"DockerVolumeInspect": true,
|
||||
"DockerVolumeList": true,
|
||||
"EndpointResourcesAccess": true,
|
||||
"PortainerStackFile": true,
|
||||
"PortainerStackInspect": true,
|
||||
"PortainerStackList": true,
|
||||
"PortainerWebhookList": true
|
||||
},
|
||||
"Description": "Read-only access of all resources in an endpoint",
|
||||
"Id": 2,
|
||||
"Name": "Helpdesk",
|
||||
"Priority": 2
|
||||
},
|
||||
{
|
||||
"Authorizations": {
|
||||
"DockerAgentHostInfo": true,
|
||||
"DockerAgentList": true,
|
||||
"DockerAgentPing": true,
|
||||
"DockerAgentUndefined": true,
|
||||
"DockerBuildCancel": true,
|
||||
"DockerBuildPrune": true,
|
||||
"DockerConfigCreate": true,
|
||||
"DockerConfigDelete": true,
|
||||
"DockerConfigInspect": true,
|
||||
"DockerConfigList": true,
|
||||
"DockerConfigUpdate": true,
|
||||
"DockerContainerArchive": true,
|
||||
"DockerContainerArchiveInfo": true,
|
||||
"DockerContainerAttach": true,
|
||||
"DockerContainerAttachWebsocket": true,
|
||||
"DockerContainerChanges": true,
|
||||
"DockerContainerCreate": true,
|
||||
"DockerContainerDelete": true,
|
||||
"DockerContainerExec": true,
|
||||
"DockerContainerExport": true,
|
||||
"DockerContainerInspect": true,
|
||||
"DockerContainerKill": true,
|
||||
"DockerContainerList": true,
|
||||
"DockerContainerLogs": true,
|
||||
"DockerContainerPause": true,
|
||||
"DockerContainerPutContainerArchive": true,
|
||||
"DockerContainerRename": true,
|
||||
"DockerContainerResize": true,
|
||||
"DockerContainerRestart": true,
|
||||
"DockerContainerStart": true,
|
||||
"DockerContainerStats": true,
|
||||
"DockerContainerStop": true,
|
||||
"DockerContainerTop": true,
|
||||
"DockerContainerUnpause": true,
|
||||
"DockerContainerUpdate": true,
|
||||
"DockerContainerWait": true,
|
||||
"DockerDistributionInspect": true,
|
||||
"DockerEvents": true,
|
||||
"DockerExecInspect": true,
|
||||
"DockerExecResize": true,
|
||||
"DockerExecStart": true,
|
||||
"DockerImageBuild": true,
|
||||
"DockerImageCommit": true,
|
||||
"DockerImageCreate": true,
|
||||
"DockerImageDelete": true,
|
||||
"DockerImageGet": true,
|
||||
"DockerImageGetAll": true,
|
||||
"DockerImageHistory": true,
|
||||
"DockerImageInspect": true,
|
||||
"DockerImageList": true,
|
||||
"DockerImageLoad": true,
|
||||
"DockerImagePush": true,
|
||||
"DockerImageSearch": true,
|
||||
"DockerImageTag": true,
|
||||
"DockerInfo": true,
|
||||
"DockerNetworkConnect": true,
|
||||
"DockerNetworkCreate": true,
|
||||
"DockerNetworkDelete": true,
|
||||
"DockerNetworkDisconnect": true,
|
||||
"DockerNetworkInspect": true,
|
||||
"DockerNetworkList": true,
|
||||
"DockerNodeDelete": true,
|
||||
"DockerNodeInspect": true,
|
||||
"DockerNodeList": true,
|
||||
"DockerNodeUpdate": true,
|
||||
"DockerPing": true,
|
||||
"DockerPluginCreate": true,
|
||||
"DockerPluginDelete": true,
|
||||
"DockerPluginDisable": true,
|
||||
"DockerPluginEnable": true,
|
||||
"DockerPluginInspect": true,
|
||||
"DockerPluginList": true,
|
||||
"DockerPluginPrivileges": true,
|
||||
"DockerPluginPull": true,
|
||||
"DockerPluginPush": true,
|
||||
"DockerPluginSet": true,
|
||||
"DockerPluginUpgrade": true,
|
||||
"DockerSecretCreate": true,
|
||||
"DockerSecretDelete": true,
|
||||
"DockerSecretInspect": true,
|
||||
"DockerSecretList": true,
|
||||
"DockerSecretUpdate": true,
|
||||
"DockerServiceCreate": true,
|
||||
"DockerServiceDelete": true,
|
||||
"DockerServiceInspect": true,
|
||||
"DockerServiceList": true,
|
||||
"DockerServiceLogs": true,
|
||||
"DockerServiceUpdate": true,
|
||||
"DockerSessionStart": true,
|
||||
"DockerSwarmInit": true,
|
||||
"DockerSwarmInspect": true,
|
||||
"DockerSwarmJoin": true,
|
||||
"DockerSwarmLeave": true,
|
||||
"DockerSwarmUnlock": true,
|
||||
"DockerSwarmUnlockKey": true,
|
||||
"DockerSwarmUpdate": true,
|
||||
"DockerSystem": true,
|
||||
"DockerTaskInspect": true,
|
||||
"DockerTaskList": true,
|
||||
"DockerTaskLogs": true,
|
||||
"DockerUndefined": true,
|
||||
"DockerVersion": true,
|
||||
"DockerVolumeCreate": true,
|
||||
"DockerVolumeDelete": true,
|
||||
"DockerVolumeInspect": true,
|
||||
"DockerVolumeList": true,
|
||||
"PortainerResourceControlUpdate": true,
|
||||
"PortainerStackCreate": true,
|
||||
"PortainerStackDelete": true,
|
||||
"PortainerStackFile": true,
|
||||
"PortainerStackInspect": true,
|
||||
"PortainerStackList": true,
|
||||
"PortainerStackMigrate": true,
|
||||
"PortainerStackUpdate": true,
|
||||
"PortainerWebhookCreate": true,
|
||||
"PortainerWebhookList": true,
|
||||
"PortainerWebsocketExec": true
|
||||
},
|
||||
"Description": "Full control of assigned resources in an endpoint",
|
||||
"Id": 3,
|
||||
"Name": "Standard user",
|
||||
"Priority": 3
|
||||
},
|
||||
{
|
||||
"Authorizations": {
|
||||
"DockerAgentHostInfo": true,
|
||||
"DockerAgentList": true,
|
||||
"DockerAgentPing": true,
|
||||
"DockerConfigInspect": true,
|
||||
"DockerConfigList": true,
|
||||
"DockerContainerArchiveInfo": true,
|
||||
"DockerContainerChanges": true,
|
||||
"DockerContainerInspect": true,
|
||||
"DockerContainerList": true,
|
||||
"DockerContainerLogs": true,
|
||||
"DockerContainerStats": true,
|
||||
"DockerContainerTop": true,
|
||||
"DockerDistributionInspect": true,
|
||||
"DockerEvents": true,
|
||||
"DockerImageGet": true,
|
||||
"DockerImageGetAll": true,
|
||||
"DockerImageHistory": true,
|
||||
"DockerImageInspect": true,
|
||||
"DockerImageList": true,
|
||||
"DockerImageSearch": true,
|
||||
"DockerInfo": true,
|
||||
"DockerNetworkInspect": true,
|
||||
"DockerNetworkList": true,
|
||||
"DockerNodeInspect": true,
|
||||
"DockerNodeList": true,
|
||||
"DockerPing": true,
|
||||
"DockerPluginList": true,
|
||||
"DockerSecretInspect": true,
|
||||
"DockerSecretList": true,
|
||||
"DockerServiceInspect": true,
|
||||
"DockerServiceList": true,
|
||||
"DockerServiceLogs": true,
|
||||
"DockerSwarmInspect": true,
|
||||
"DockerSystem": true,
|
||||
"DockerTaskInspect": true,
|
||||
"DockerTaskList": true,
|
||||
"DockerTaskLogs": true,
|
||||
"DockerVersion": true,
|
||||
"DockerVolumeInspect": true,
|
||||
"DockerVolumeList": true,
|
||||
"PortainerStackFile": true,
|
||||
"PortainerStackInspect": true,
|
||||
"PortainerStackList": true,
|
||||
"PortainerWebhookList": true
|
||||
},
|
||||
"Description": "Read-only access of assigned resources in an endpoint",
|
||||
"Id": 4,
|
||||
"Name": "Read-only user",
|
||||
"Priority": 4
|
||||
}
|
||||
],
|
||||
"schedules": [
|
||||
{
|
||||
"Created": 1648608136,
|
||||
"CronExpression": "@every 5m",
|
||||
"EdgeSchedule": null,
|
||||
"EndpointSyncJob": null,
|
||||
"Id": 1,
|
||||
"JobType": 2,
|
||||
"Name": "system_snapshot",
|
||||
"Recurring": true,
|
||||
"ScriptExecutionJob": null,
|
||||
"SnapshotJob": {}
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"AgentSecret": "",
|
||||
"AllowBindMountsForRegularUsers": true,
|
||||
"AllowContainerCapabilitiesForRegularUsers": true,
|
||||
"AllowDeviceMappingForRegularUsers": true,
|
||||
"AllowHostNamespaceForRegularUsers": true,
|
||||
"AllowPrivilegedModeForRegularUsers": true,
|
||||
"AllowStackManagementForRegularUsers": true,
|
||||
"AllowVolumeBrowserForRegularUsers": false,
|
||||
"AuthenticationMethod": 1,
|
||||
"BlackListedLabels": [],
|
||||
"DisplayDonationHeader": false,
|
||||
"DisplayExternalContributors": false,
|
||||
"Edge": {
|
||||
"AsyncMode": false,
|
||||
"CommandInterval": 0,
|
||||
"PingInterval": 0,
|
||||
"SnapshotInterval": 0
|
||||
},
|
||||
"EdgeAgentCheckinInterval": 5,
|
||||
"EdgePortainerUrl": "",
|
||||
"EnableEdgeComputeFeatures": false,
|
||||
"EnableHostManagementFeatures": false,
|
||||
"EnableTelemetry": true,
|
||||
"EnforceEdgeID": false,
|
||||
"FeatureFlagSettings": null,
|
||||
"HelmRepositoryURL": "https://charts.bitnami.com/bitnami",
|
||||
"InternalAuthSettings": {
|
||||
"RequiredPasswordLength": 12
|
||||
},
|
||||
"KubeconfigExpiry": "0",
|
||||
"KubectlShellImage": "portainer/kubectl-shell",
|
||||
"LDAPSettings": {
|
||||
"AnonymousMode": true,
|
||||
"AutoCreateUsers": true,
|
||||
"GroupSearchSettings": [
|
||||
{
|
||||
"GroupAttribute": "",
|
||||
"GroupBaseDN": "",
|
||||
"GroupFilter": ""
|
||||
}
|
||||
],
|
||||
"ReaderDN": "",
|
||||
"SearchSettings": [
|
||||
{
|
||||
"BaseDN": "",
|
||||
"Filter": "",
|
||||
"UserNameAttribute": ""
|
||||
}
|
||||
],
|
||||
"StartTLS": false,
|
||||
"TLSConfig": {
|
||||
"TLS": false,
|
||||
"TLSSkipVerify": false
|
||||
},
|
||||
"URL": ""
|
||||
},
|
||||
"LogoURL": "",
|
||||
"OAuthSettings": {
|
||||
"AccessTokenURI": "",
|
||||
"AuthorizationURI": "",
|
||||
"ClientID": "",
|
||||
"DefaultTeamID": 0,
|
||||
"KubeSecretKey": null,
|
||||
"LogoutURI": "",
|
||||
"OAuthAutoCreateUsers": false,
|
||||
"RedirectURI": "",
|
||||
"ResourceURI": "",
|
||||
"SSO": false,
|
||||
"Scopes": "",
|
||||
"UserIdentifier": ""
|
||||
},
|
||||
"SnapshotInterval": "5m",
|
||||
"TemplatesURL": "https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json",
|
||||
"TrustOnFirstConnect": false,
|
||||
"UserSessionTimeout": "8h",
|
||||
"fdoConfiguration": {
|
||||
"enabled": false,
|
||||
"ownerPassword": "",
|
||||
"ownerURL": "",
|
||||
"ownerUsername": ""
|
||||
},
|
||||
"openAMTConfiguration": {
|
||||
"certFileContent": "",
|
||||
"certFileName": "",
|
||||
"certFilePassword": "",
|
||||
"domainName": "",
|
||||
"enabled": false,
|
||||
"mpsPassword": "",
|
||||
"mpsServer": "",
|
||||
"mpsToken": "",
|
||||
"mpsUser": ""
|
||||
}
|
||||
},
|
||||
"snapshots": [
|
||||
{
|
||||
"Docker": {
|
||||
"DockerSnapshotRaw": {
|
||||
"Containers": null,
|
||||
"Images": null,
|
||||
"Info": {
|
||||
"Architecture": "",
|
||||
"BridgeNfIp6tables": false,
|
||||
"BridgeNfIptables": false,
|
||||
"CPUSet": false,
|
||||
"CPUShares": false,
|
||||
"CgroupDriver": "",
|
||||
"ContainerdCommit": {
|
||||
"Expected": "",
|
||||
"ID": ""
|
||||
},
|
||||
"Containers": 0,
|
||||
"ContainersPaused": 0,
|
||||
"ContainersRunning": 0,
|
||||
"ContainersStopped": 0,
|
||||
"CpuCfsPeriod": false,
|
||||
"CpuCfsQuota": false,
|
||||
"Debug": false,
|
||||
"DefaultRuntime": "",
|
||||
"DockerRootDir": "",
|
||||
"Driver": "",
|
||||
"DriverStatus": null,
|
||||
"ExperimentalBuild": false,
|
||||
"GenericResources": null,
|
||||
"HttpProxy": "",
|
||||
"HttpsProxy": "",
|
||||
"ID": "",
|
||||
"IPv4Forwarding": false,
|
||||
"Images": 0,
|
||||
"IndexServerAddress": "",
|
||||
"InitBinary": "",
|
||||
"InitCommit": {
|
||||
"Expected": "",
|
||||
"ID": ""
|
||||
},
|
||||
"Isolation": "",
|
||||
"KernelMemory": false,
|
||||
"KernelMemoryTCP": false,
|
||||
"KernelVersion": "",
|
||||
"Labels": null,
|
||||
"LiveRestoreEnabled": false,
|
||||
"LoggingDriver": "",
|
||||
"MemTotal": 0,
|
||||
"MemoryLimit": false,
|
||||
"NCPU": 0,
|
||||
"NEventsListener": 0,
|
||||
"NFd": 0,
|
||||
"NGoroutines": 0,
|
||||
"Name": "",
|
||||
"NoProxy": "",
|
||||
"OSType": "",
|
||||
"OSVersion": "",
|
||||
"OomKillDisable": false,
|
||||
"OperatingSystem": "",
|
||||
"PidsLimit": false,
|
||||
"Plugins": {
|
||||
"Authorization": null,
|
||||
"Log": null,
|
||||
"Network": null,
|
||||
"Volume": null
|
||||
},
|
||||
"RegistryConfig": null,
|
||||
"RuncCommit": {
|
||||
"Expected": "",
|
||||
"ID": ""
|
||||
},
|
||||
"Runtimes": null,
|
||||
"SecurityOptions": null,
|
||||
"ServerVersion": "",
|
||||
"SwapLimit": false,
|
||||
"Swarm": {
|
||||
"ControlAvailable": false,
|
||||
"Error": "",
|
||||
"LocalNodeState": "",
|
||||
"NodeAddr": "",
|
||||
"NodeID": "",
|
||||
"RemoteManagers": null
|
||||
},
|
||||
"SystemTime": "",
|
||||
"Warnings": null
|
||||
},
|
||||
"Networks": null,
|
||||
"Version": {
|
||||
"ApiVersion": "",
|
||||
"Arch": "",
|
||||
"GitCommit": "",
|
||||
"GoVersion": "",
|
||||
"Os": "",
|
||||
"Platform": {
|
||||
"Name": ""
|
||||
},
|
||||
"Version": ""
|
||||
},
|
||||
"Volumes": {
|
||||
"Volumes": null,
|
||||
"Warnings": null
|
||||
}
|
||||
},
|
||||
"DockerVersion": "20.10.13",
|
||||
"GpuUseAll": false,
|
||||
"GpuUseList": null,
|
||||
"HealthyContainerCount": 0,
|
||||
"ImageCount": 9,
|
||||
"NodeCount": 0,
|
||||
"RunningContainerCount": 5,
|
||||
"ServiceCount": 0,
|
||||
"StackCount": 2,
|
||||
"StoppedContainerCount": 0,
|
||||
"Swarm": false,
|
||||
"Time": 1648610112,
|
||||
"TotalCPU": 8,
|
||||
"TotalMemory": 25098706944,
|
||||
"UnhealthyContainerCount": 0,
|
||||
"VolumeCount": 10
|
||||
},
|
||||
"EndpointId": 1,
|
||||
"Kubernetes": null
|
||||
}
|
||||
],
|
||||
"ssl": {
|
||||
"certPath": "",
|
||||
"httpEnabled": true,
|
||||
"keyPath": "",
|
||||
"selfSigned": false
|
||||
},
|
||||
"stacks": [
|
||||
{
|
||||
"AdditionalFiles": null,
|
||||
"AutoUpdate": null,
|
||||
"CreatedBy": "",
|
||||
"CreationDate": 0,
|
||||
"EndpointId": 1,
|
||||
"EntryPoint": "docker/alpine37-compose.yml",
|
||||
"Env": [],
|
||||
"FromAppTemplate": false,
|
||||
"GitConfig": null,
|
||||
"Id": 2,
|
||||
"IsComposeFormat": false,
|
||||
"Name": "alpine",
|
||||
"Namespace": "",
|
||||
"Option": null,
|
||||
"ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/2",
|
||||
"ResourceControl": null,
|
||||
"Status": 1,
|
||||
"SwarmId": "s3fd604zdba7z13tbq2x6lyue",
|
||||
"Type": 1,
|
||||
"UpdateDate": 0,
|
||||
"UpdatedBy": ""
|
||||
},
|
||||
{
|
||||
"AdditionalFiles": null,
|
||||
"AutoUpdate": null,
|
||||
"CreatedBy": "",
|
||||
"CreationDate": 0,
|
||||
"EndpointId": 1,
|
||||
"EntryPoint": "docker-compose.yml",
|
||||
"Env": [],
|
||||
"FromAppTemplate": false,
|
||||
"GitConfig": null,
|
||||
"Id": 5,
|
||||
"IsComposeFormat": false,
|
||||
"Name": "redis",
|
||||
"Namespace": "",
|
||||
"Option": null,
|
||||
"ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/5",
|
||||
"ResourceControl": null,
|
||||
"Status": 1,
|
||||
"SwarmId": "",
|
||||
"Type": 2,
|
||||
"UpdateDate": 0,
|
||||
"UpdatedBy": ""
|
||||
},
|
||||
{
|
||||
"AdditionalFiles": null,
|
||||
"AutoUpdate": null,
|
||||
"CreatedBy": "",
|
||||
"CreationDate": 0,
|
||||
"EndpointId": 1,
|
||||
"EntryPoint": "docker-compose.yml",
|
||||
"Env": [],
|
||||
"FromAppTemplate": false,
|
||||
"GitConfig": null,
|
||||
"Id": 6,
|
||||
"IsComposeFormat": false,
|
||||
"Name": "nginx",
|
||||
"Namespace": "",
|
||||
"Option": null,
|
||||
"ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/6",
|
||||
"ResourceControl": null,
|
||||
"Status": 1,
|
||||
"SwarmId": "",
|
||||
"Type": 2,
|
||||
"UpdateDate": 0,
|
||||
"UpdatedBy": ""
|
||||
}
|
||||
],
|
||||
"teams": [
|
||||
{
|
||||
"Id": 1,
|
||||
"Name": "hello"
|
||||
}
|
||||
],
|
||||
"tunnel_server": {
|
||||
"PrivateKeySeed": "IvX6ZPRuWtLS5zyg"
|
||||
},
|
||||
"users": [
|
||||
{
|
||||
"EndpointAuthorizations": null,
|
||||
"Id": 1,
|
||||
"Password": "$2a$10$siRDprr/5uUFAU8iom3Sr./WXQkN2dhSNjAC471pkJaALkghS762a",
|
||||
"PortainerAuthorizations": {
|
||||
"PortainerDockerHubInspect": true,
|
||||
"PortainerEndpointGroupList": true,
|
||||
"PortainerEndpointInspect": true,
|
||||
"PortainerEndpointList": true,
|
||||
"PortainerMOTD": true,
|
||||
"PortainerRegistryInspect": true,
|
||||
"PortainerRegistryList": true,
|
||||
"PortainerTeamList": true,
|
||||
"PortainerTemplateInspect": true,
|
||||
"PortainerTemplateList": true,
|
||||
"PortainerUserCreateToken": true,
|
||||
"PortainerUserInspect": true,
|
||||
"PortainerUserList": true,
|
||||
"PortainerUserListToken": true,
|
||||
"PortainerUserMemberships": true,
|
||||
"PortainerUserRevokeToken": true
|
||||
},
|
||||
"Role": 1,
|
||||
"TokenIssueAt": 0,
|
||||
"UserTheme": "",
|
||||
"Username": "admin"
|
||||
},
|
||||
{
|
||||
"EndpointAuthorizations": null,
|
||||
"Id": 2,
|
||||
"Password": "$2a$10$WpCAW8mSt6FRRp1GkynbFOGSZnHR6E5j9cETZ8HiMlw06hVlDW/Li",
|
||||
"PortainerAuthorizations": {
|
||||
"PortainerDockerHubInspect": true,
|
||||
"PortainerEndpointGroupList": true,
|
||||
"PortainerEndpointInspect": true,
|
||||
"PortainerEndpointList": true,
|
||||
"PortainerMOTD": true,
|
||||
"PortainerRegistryInspect": true,
|
||||
"PortainerRegistryList": true,
|
||||
"PortainerTeamList": true,
|
||||
"PortainerTemplateInspect": true,
|
||||
"PortainerTemplateList": true,
|
||||
"PortainerUserCreateToken": true,
|
||||
"PortainerUserInspect": true,
|
||||
"PortainerUserList": true,
|
||||
"PortainerUserListToken": true,
|
||||
"PortainerUserMemberships": true,
|
||||
"PortainerUserRevokeToken": true
|
||||
},
|
||||
"Role": 1,
|
||||
"TokenIssueAt": 0,
|
||||
"UserTheme": "",
|
||||
"Username": "prabhat"
|
||||
}
|
||||
],
|
||||
"version": {
|
||||
"DB_UPDATING": "false",
|
||||
"DB_VERSION": "70",
|
||||
"INSTANCE_ID": "null"
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
var validate *validator.Validate
|
||||
|
||||
func ValidateLDAPSettings(ldp *portainer.LDAPSettings) error {
|
||||
validate = validator.New()
|
||||
registerValidationMethods(validate)
|
||||
|
||||
return validate.Struct(ldp)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
func TestValidateLDAPSettings(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ldap portainer.LDAPSettings
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Empty LDAP Settings",
|
||||
ldap: portainer.LDAPSettings{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "With URL",
|
||||
ldap: portainer.LDAPSettings{
|
||||
AnonymousMode: true,
|
||||
URL: "192.168.0.1:323",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Validate URL and URLs",
|
||||
ldap: portainer.LDAPSettings{
|
||||
AnonymousMode: true,
|
||||
URL: "192.168.0.1:323",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "validate client ldap",
|
||||
ldap: portainer.LDAPSettings{
|
||||
AnonymousMode: false,
|
||||
ReaderDN: "CN=LDAP API Service Account",
|
||||
Password: "Qu**dfUUU**",
|
||||
URL: "aukdc15.pgc.co:389",
|
||||
TLSConfig: portainer.TLSConfiguration{
|
||||
TLS: false,
|
||||
TLSSkipVerify: false,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := ValidateLDAPSettings(&tt.ldap)
|
||||
if (err == nil) == tt.wantErr {
|
||||
t.Errorf("No error expected but got %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
func registerValidationMethods(v *validator.Validate) {
|
||||
v.RegisterValidation("validate_bool", ValidateBool)
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation methods below are being used for custom validation
|
||||
*/
|
||||
func ValidateBool(fl validator.FieldLevel) bool {
|
||||
_, ok := fl.Field().Interface().(bool)
|
||||
return ok
|
||||
}
|
||||
6
api/docker/labels.go
Normal file
6
api/docker/labels.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package docker
|
||||
|
||||
const (
|
||||
ComposeStackNameLabel = "com.docker.compose.project"
|
||||
SwarmStackNameLabel = "com.docker.stack.namespace"
|
||||
)
|
||||
@@ -194,7 +194,7 @@ func snapshotContainers(snapshot *portainer.DockerSnapshot, cli *client.Client)
|
||||
}
|
||||
|
||||
for k, v := range container.Labels {
|
||||
if k == "com.docker.compose.project" {
|
||||
if k == ComposeStackNameLabel {
|
||||
stacks[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,14 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
libstack "github.com/portainer/docker-compose-wrapper"
|
||||
"github.com/portainer/docker-compose-wrapper/compose"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/http/proxy"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory"
|
||||
"github.com/portainer/portainer/api/internal/stackutils"
|
||||
"github.com/portainer/portainer/api/stacks/stackutils"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ComposeStackManager is a wrapper for docker-compose binary
|
||||
@@ -59,7 +58,7 @@ func (manager *ComposeStackManager) Up(ctx context.Context, stack *portainer.Sta
|
||||
return errors.Wrap(err, "failed to create env file")
|
||||
}
|
||||
|
||||
filePaths := stackutils.GetStackFilePaths(stack)
|
||||
filePaths := stackutils.GetStackFilePaths(stack, false)
|
||||
err = manager.deployer.Deploy(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFile, forceRereate)
|
||||
return errors.Wrap(err, "failed to deploy a stack")
|
||||
}
|
||||
@@ -79,7 +78,7 @@ func (manager *ComposeStackManager) Down(ctx context.Context, stack *portainer.S
|
||||
return errors.Wrap(err, "failed to create env file")
|
||||
}
|
||||
|
||||
filePaths := stackutils.GetStackFilePaths(stack)
|
||||
filePaths := stackutils.GetStackFilePaths(stack, false)
|
||||
|
||||
err = manager.deployer.Remove(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFile)
|
||||
return errors.Wrap(err, "failed to remove a stack")
|
||||
@@ -101,7 +100,7 @@ func (manager *ComposeStackManager) Pull(ctx context.Context, stack *portainer.S
|
||||
return errors.Wrap(err, "failed to create env file")
|
||||
}
|
||||
|
||||
filePaths := stackutils.GetStackFilePaths(stack)
|
||||
filePaths := stackutils.GetStackFilePaths(stack, false)
|
||||
err = manager.deployer.Pull(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFile)
|
||||
return errors.Wrap(err, "failed to pull images of the stack")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
type kubernetesMockDeployer struct{}
|
||||
|
||||
// NewKubernetesDeployer creates a mock kubernetes deployer
|
||||
func NewKubernetesDeployer() portainer.KubernetesDeployer {
|
||||
return &kubernetesMockDeployer{}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/http/proxy"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
|
||||
"github.com/portainer/portainer/api/kubernetes/cli"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// KubernetesDeployer represents a service to deploy resources inside a Kubernetes environment(endpoint).
|
||||
@@ -73,6 +73,7 @@ func (deployer *KubernetesDeployer) getToken(userID portainer.UserID, endpoint *
|
||||
if token == "" {
|
||||
return "", fmt.Errorf("can not get a valid user service account token")
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/registryutils"
|
||||
"github.com/portainer/portainer/api/internal/stackutils"
|
||||
"github.com/portainer/portainer/api/stacks/stackutils"
|
||||
)
|
||||
|
||||
// SwarmStackManager represents a service for managing stacks.
|
||||
@@ -90,7 +90,7 @@ func (manager *SwarmStackManager) Logout(endpoint *portainer.Endpoint) error {
|
||||
|
||||
// Deploy executes the docker stack deploy command.
|
||||
func (manager *SwarmStackManager) Deploy(stack *portainer.Stack, prune bool, pullImage bool, endpoint *portainer.Endpoint) error {
|
||||
filePaths := stackutils.GetStackFilePaths(stack)
|
||||
filePaths := stackutils.GetStackFilePaths(stack, false)
|
||||
command, args, err := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/api/archive"
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -483,9 +484,9 @@ func (a *azureClient) listFiles(ctx context.Context, opt fetchOption) ([]string,
|
||||
|
||||
func checkAzureStatusCode(err error, code int) error {
|
||||
if code == http.StatusNotFound {
|
||||
return ErrIncorrectRepositoryURL
|
||||
return gittypes.ErrIncorrectRepositoryURL
|
||||
} else if code == http.StatusUnauthorized || code == http.StatusNonAuthoritativeInfo {
|
||||
return ErrAuthenticationFailure
|
||||
return gittypes.ErrAuthenticationFailure
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -145,7 +146,7 @@ func TestService_ListFiles_Azure(t *testing.T) {
|
||||
extensions: []string{},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -161,7 +162,7 @@ func TestService_ListFiles_Azure(t *testing.T) {
|
||||
extensions: []string{},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -240,7 +241,7 @@ func TestService_ListFiles_Azure(t *testing.T) {
|
||||
extensions: []string{},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrIncorrectRepositoryURL,
|
||||
err: gittypes.ErrIncorrectRepositoryURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -466,7 +467,7 @@ func Test_listRefs_azure(t *testing.T) {
|
||||
password: "test-token",
|
||||
},
|
||||
expect: expectResult{
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -477,7 +478,7 @@ func Test_listRefs_azure(t *testing.T) {
|
||||
password: "",
|
||||
},
|
||||
expect: expectResult{
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -488,7 +489,7 @@ func Test_listRefs_azure(t *testing.T) {
|
||||
password: accessToken,
|
||||
},
|
||||
expect: expectResult{
|
||||
err: ErrIncorrectRepositoryURL,
|
||||
err: gittypes.ErrIncorrectRepositoryURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -540,7 +541,7 @@ func Test_listFiles_azure(t *testing.T) {
|
||||
},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -555,7 +556,7 @@ func Test_listFiles_azure(t *testing.T) {
|
||||
},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -599,7 +600,7 @@ func Test_listFiles_azure(t *testing.T) {
|
||||
},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrIncorrectRepositoryURL,
|
||||
err: gittypes.ErrIncorrectRepositoryURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/go-git/go-git/v5/storage/memory"
|
||||
"github.com/pkg/errors"
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
)
|
||||
|
||||
type gitClient struct {
|
||||
@@ -41,7 +41,7 @@ func (c *gitClient) download(ctx context.Context, dst string, opt cloneOption) e
|
||||
|
||||
if err != nil {
|
||||
if err.Error() == "authentication required" {
|
||||
return ErrAuthenticationFailure
|
||||
return gittypes.ErrAuthenticationFailure
|
||||
}
|
||||
return errors.Wrap(err, "failed to clone git repository")
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func (c *gitClient) latestCommitID(ctx context.Context, opt fetchOption) (string
|
||||
refs, err := remote.List(listOptions)
|
||||
if err != nil {
|
||||
if err.Error() == "authentication required" {
|
||||
return "", ErrAuthenticationFailure
|
||||
return "", gittypes.ErrAuthenticationFailure
|
||||
}
|
||||
return "", errors.Wrap(err, "failed to list repository refs")
|
||||
}
|
||||
@@ -172,9 +172,9 @@ func (c *gitClient) listFiles(ctx context.Context, opt fetchOption) ([]string, e
|
||||
func checkGitError(err error) error {
|
||||
errMsg := err.Error()
|
||||
if errMsg == "repository not found" {
|
||||
return ErrIncorrectRepositoryURL
|
||||
return gittypes.ErrIncorrectRepositoryURL
|
||||
} else if errMsg == "authentication required" {
|
||||
return ErrAuthenticationFailure
|
||||
return gittypes.ErrAuthenticationFailure
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -99,7 +100,7 @@ func TestService_ListFiles_GitHub(t *testing.T) {
|
||||
extensions: []string{},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -115,7 +116,7 @@ func TestService_ListFiles_GitHub(t *testing.T) {
|
||||
extensions: []string{},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -194,7 +195,7 @@ func TestService_ListFiles_GitHub(t *testing.T) {
|
||||
extensions: []string{},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrIncorrectRepositoryURL,
|
||||
err: gittypes.ErrIncorrectRepositoryURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -296,9 +297,9 @@ func TestService_HardRefresh_ListRefs_GitHub(t *testing.T) {
|
||||
assert.GreaterOrEqual(t, len(refs), 1)
|
||||
assert.Equal(t, 1, service.repoRefCache.Len())
|
||||
|
||||
refs, err = service.ListRefs(repositoryUrl, username, "fake-token", true)
|
||||
refs, err = service.ListRefs(repositoryUrl, username, "fake-token", false)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 0, service.repoRefCache.Len())
|
||||
assert.Equal(t, 1, service.repoRefCache.Len())
|
||||
}
|
||||
|
||||
func TestService_HardRefresh_ListRefs_And_RemoveAllCaches_GitHub(t *testing.T) {
|
||||
@@ -324,10 +325,13 @@ func TestService_HardRefresh_ListRefs_And_RemoveAllCaches_GitHub(t *testing.T) {
|
||||
assert.GreaterOrEqual(t, len(files), 1)
|
||||
assert.Equal(t, 2, service.repoFileCache.Len())
|
||||
|
||||
refs, err = service.ListRefs(repositoryUrl, username, "fake-token", false)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 1, service.repoRefCache.Len())
|
||||
|
||||
refs, err = service.ListRefs(repositoryUrl, username, "fake-token", true)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 0, service.repoRefCache.Len())
|
||||
|
||||
assert.Equal(t, 1, service.repoRefCache.Len())
|
||||
// The relevant file caches should be removed too
|
||||
assert.Equal(t, 0, service.repoFileCache.Len())
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/api/archive"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/api/archive"
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -148,7 +148,7 @@ func Test_listRefsPrivateRepository(t *testing.T) {
|
||||
password: "test-token",
|
||||
},
|
||||
expect: expectResult{
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -159,7 +159,7 @@ func Test_listRefsPrivateRepository(t *testing.T) {
|
||||
password: "",
|
||||
},
|
||||
expect: expectResult{
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -170,7 +170,7 @@ func Test_listRefsPrivateRepository(t *testing.T) {
|
||||
password: accessToken,
|
||||
},
|
||||
expect: expectResult{
|
||||
err: ErrIncorrectRepositoryURL,
|
||||
err: gittypes.ErrIncorrectRepositoryURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -222,7 +222,7 @@ func Test_listFilesPrivateRepository(t *testing.T) {
|
||||
},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -237,7 +237,7 @@ func Test_listFilesPrivateRepository(t *testing.T) {
|
||||
},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrAuthenticationFailure,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -281,7 +281,7 @@ func Test_listFilesPrivateRepository(t *testing.T) {
|
||||
},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: ErrIncorrectRepositoryURL,
|
||||
err: gittypes.ErrIncorrectRepositoryURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,19 +2,15 @@ package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrIncorrectRepositoryURL = errors.New("Git repository could not be found, please ensure that the URL is correct.")
|
||||
ErrAuthenticationFailure = errors.New("Authentication failed, please ensure that the git credentials are correct.")
|
||||
|
||||
REPOSITORY_CACHE_SIZE = 4
|
||||
REPOSITORY_CACHE_TTL = 5 * time.Minute
|
||||
)
|
||||
@@ -78,12 +74,12 @@ func newService(ctx context.Context, cacheSize int, cacheTTL time.Duration) *Ser
|
||||
var err error
|
||||
service.repoRefCache, err = lru.New(cacheSize)
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] [git] [message: failed to create ref cache: %v\n", err)
|
||||
log.Debug().Err(err).Msg("failed to create ref cache")
|
||||
}
|
||||
|
||||
service.repoFileCache, err = lru.New(cacheSize)
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] [git] [message: failed to create file cache: %v\n", err)
|
||||
log.Debug().Err(err).Msg("failed to create file cache")
|
||||
}
|
||||
|
||||
if cacheTTL > 0 {
|
||||
@@ -167,9 +163,10 @@ func (service *Service) LatestCommitID(repositoryURL, referenceName, username, p
|
||||
|
||||
// ListRefs will list target repository's references without cloning the repository
|
||||
func (service *Service) ListRefs(repositoryURL, username, password string, hardRefresh bool) ([]string, error) {
|
||||
refCacheKey := generateCacheKey(repositoryURL, password)
|
||||
if service.cacheEnabled && hardRefresh {
|
||||
// Should remove the cache explicitly, so that the following normal list can show the correct result
|
||||
service.repoRefCache.Remove(repositoryURL)
|
||||
service.repoRefCache.Remove(refCacheKey)
|
||||
// Remove file caches pointed to the same repository
|
||||
for _, fileCacheKey := range service.repoFileCache.Keys() {
|
||||
key, ok := fileCacheKey.(string)
|
||||
@@ -183,7 +180,7 @@ func (service *Service) ListRefs(repositoryURL, username, password string, hardR
|
||||
|
||||
if service.repoRefCache != nil {
|
||||
// Lookup the refs cache first
|
||||
cache, ok := service.repoRefCache.Get(repositoryURL)
|
||||
cache, ok := service.repoRefCache.Get(refCacheKey)
|
||||
if ok {
|
||||
refs, success := cache.([]string)
|
||||
if success {
|
||||
@@ -215,7 +212,7 @@ func (service *Service) ListRefs(repositoryURL, username, password string, hardR
|
||||
}
|
||||
|
||||
if service.cacheEnabled && service.repoRefCache != nil {
|
||||
service.repoRefCache.Add(options.repositoryUrl, refs)
|
||||
service.repoRefCache.Add(refCacheKey, refs)
|
||||
}
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
package gittypes
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrIncorrectRepositoryURL = errors.New("Git repository could not be found, please ensure that the URL is correct.")
|
||||
ErrAuthenticationFailure = errors.New("Authentication failed, please ensure that the git credentials are correct.")
|
||||
)
|
||||
|
||||
// RepoConfig represents a configuration for a repo
|
||||
type RepoConfig struct {
|
||||
// The repo url
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user