diff --git a/api/adminmonitor/admin_monitor.go b/api/adminmonitor/admin_monitor.go index aeabe0187..2b32bb9c2 100644 --- a/api/adminmonitor/admin_monitor.go +++ b/api/adminmonitor/admin_monitor.go @@ -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) diff --git a/api/apikey/service.go b/api/apikey/service.go index be794509b..48e7ea5fa 100644 --- a/api/apikey/service.go +++ b/api/apikey/service.go @@ -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_" diff --git a/api/apikey/service_test.go b/api/apikey/service_test.go index c8c603b1b..707bb73c3 100644 --- a/api/apikey/service_test.go +++ b/api/apikey/service_test.go @@ -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) { diff --git a/api/chisel/service.go b/api/chisel/service.go index 023bc714d..febbd18b9 100644 --- a/api/chisel/service.go +++ b/api/chisel/service.go @@ -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") } } diff --git a/api/cli/pairlistbool.go b/api/cli/pairlistbool.go index 2c0949b6d..69e89b792 100644 --- a/api/cli/pairlistbool.go +++ b/api/cli/pairlistbool.go @@ -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" ) diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index e0a2c50c4..c5d6a0ce5 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -186,6 +186,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 +243,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 +590,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server { ldapService := initLDAPService() oauthService := initOAuthService() + gitService := initGitService(shutdownCtx) openAMTService := openamt.NewService() @@ -704,15 +715,15 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server { log.Fatal().Err(err).Msg("failed starting tunnel server") } + scheduler := scheduler.NewScheduler(shutdownCtx) + stackDeployer := stacks.NewStackDeployer(swarmStackManager, composeStackManager, kubernetesDeployer) + stacks.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 +737,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, diff --git a/api/cmd/portainer/main_test.go b/api/cmd/portainer/main_test.go index 85933db8b..9310c24df 100644 --- a/api/cmd/portainer/main_test.go +++ b/api/cmd/portainer/main_test.go @@ -8,6 +8,7 @@ import ( "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" ) diff --git a/api/database/boltdb/db.go b/api/database/boltdb/db.go index 7fc0b8832..203f9255c 100644 --- a/api/database/boltdb/db.go +++ b/api/database/boltdb/db.go @@ -1,6 +1,7 @@ package boltdb import ( + "bytes" "encoding/binary" "errors" "fmt" @@ -162,7 +163,7 @@ func (connection *DbConnection) ExportRaw(filename string) error { return fmt.Errorf("stat on %s failed: %s", databasePath, err) } - b, err := connection.ExportJson(databasePath, true) + b, err := connection.ExportJSON(databasePath, true) if err != nil { return err } @@ -369,6 +370,27 @@ func (connection *DbConnection) GetAllWithJsoniter(bucketName string, obj interf return err } +func (connection *DbConnection) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj interface{}, append func(o interface{}) (interface{}, error)) error { + return connection.View(func(tx *bolt.Tx) error { + cursor := tx.Bucket([]byte(bucketName)).Cursor() + + for k, v := cursor.Seek(keyPrefix); k != nil && bytes.HasPrefix(k, keyPrefix); 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 + }) +} + +// BackupMetadata will return a copy of the boltdb sequence numbers for all buckets. func (connection *DbConnection) BackupMetadata() (map[string]interface{}, error) { buckets := map[string]interface{}{} @@ -387,6 +409,7 @@ func (connection *DbConnection) BackupMetadata() (map[string]interface{}, error) return buckets, err } +// RestoreMetadata will restore the boltdb sequence numbers for all buckets. func (connection *DbConnection) RestoreMetadata(s map[string]interface{}) error { var err error diff --git a/api/database/boltdb/export.go b/api/database/boltdb/export.go index 4e7ce763e..fb582468c 100644 --- a/api/database/boltdb/export.go +++ b/api/database/boltdb/export.go @@ -31,7 +31,7 @@ func backupMetadata(connection *bolt.DB) (map[string]interface{}, error) { // 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) { +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}) diff --git a/api/database/database.go b/api/database/database.go index 03feea8e9..f4705cc94 100644 --- a/api/database/database.go +++ b/api/database/database.go @@ -16,5 +16,6 @@ func NewDatabase(storeType, storePath string, encryptionKey []byte) (connection EncryptionKey: encryptionKey, }, nil } - return nil, fmt.Errorf("unknown storage database: %s", storeType) + + return nil, fmt.Errorf("Unknown storage database: %s", storeType) } diff --git a/api/dataservices/edgeupdateschedule/edgeupdateschedule.go b/api/dataservices/edgeupdateschedule/edgeupdateschedule.go index 28df7bacc..2430bab11 100644 --- a/api/dataservices/edgeupdateschedule/edgeupdateschedule.go +++ b/api/dataservices/edgeupdateschedule/edgeupdateschedule.go @@ -85,7 +85,7 @@ func (service *Service) List() ([]edgetypes.UpdateSchedule, 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) + return nil, fmt.Errorf("Failed to convert to EdgeUpdateSchedule object: %s", obj) } list = append(list, *item) return &edgetypes.UpdateSchedule{}, nil diff --git a/api/dataservices/interface.go b/api/dataservices/interface.go index 6fbfb218d..619cc6f4d 100644 --- a/api/dataservices/interface.go +++ b/api/dataservices/interface.go @@ -24,7 +24,6 @@ type ( BackupTo(w io.Writer) error Export(filename string) (err error) IsErrObjectNotFound(err error) bool - CustomTemplate() CustomTemplateService EdgeGroup() EdgeGroupService EdgeJob() EdgeJobService diff --git a/api/datastore/datastore.go b/api/datastore/datastore.go index fe952e343..1a5efae7c 100644 --- a/api/datastore/datastore.go +++ b/api/datastore/datastore.go @@ -156,6 +156,7 @@ func (store *Store) encryptDB() error { if err != nil { // Remove the new encrypted file that we failed to import os.Remove(store.connection.GetDatabaseFilePath()) + log.Fatal().Err(portainerErrors.ErrDBImportFailed).Msg("") } diff --git a/api/datastore/init.go b/api/datastore/init.go index 6912227af..b16ba495c 100644 --- a/api/datastore/init.go +++ b/api/datastore/init.go @@ -36,6 +36,7 @@ func (store *Store) checkOrCreateInstanceID() error { instanceID := uid.String() return store.VersionService.StoreInstanceID(instanceID) } + return err } @@ -88,7 +89,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 +96,7 @@ func (store *Store) checkOrCreateDefaultSSLSettings() error { return store.SSLSettings().UpdateSettings(defaultSSLSettings) } + return err } diff --git a/api/datastore/migrate_data_test.go b/api/datastore/migrate_data_test.go index 9a8e7acfe..a1b52e860 100644 --- a/api/datastore/migrate_data_test.go +++ b/api/datastore/migrate_data_test.go @@ -259,7 +259,7 @@ func migrateDBTestHelper(t *testing.T, srcPath, wantPath string) error { return fmt.Errorf("stat on %s failed: %s", databasePath, err) } - gotJSON, err := con.ExportJson(databasePath, false) + gotJSON, err := con.ExportJSON(databasePath, false) if err != nil { t.Logf( "failed re-exporting database %s to JSON: %v", diff --git a/api/datastore/migrator/migrate_ce.go b/api/datastore/migrator/migrate_ce.go index 01514e35f..a767dfee0 100644 --- a/api/datastore/migrator/migrate_ce.go +++ b/api/datastore/migrator/migrate_ce.go @@ -1,13 +1,12 @@ package migrator import ( - "errors" "reflect" "runtime" portainer "github.com/portainer/portainer/api" - werrors "github.com/pkg/errors" + "github.com/pkg/errors" "github.com/rs/zerolog/log" ) @@ -17,7 +16,7 @@ type migration struct { } func migrationError(err error, context string) error { - return werrors.Wrap(err, "failed in "+context) + return errors.Wrap(err, "failed in "+context) } func newMigration(dbversion int, migrate func() error) migration { diff --git a/api/datastore/migrator/migrate_dbversion19.go b/api/datastore/migrator/migrate_dbversion19.go index a69abe65f..78375cd9c 100644 --- a/api/datastore/migrator/migrate_dbversion19.go +++ b/api/datastore/migrator/migrate_dbversion19.go @@ -15,6 +15,7 @@ func (m *Migrator) updateUsersToDBVersion20() error { } func (m *Migrator) updateSettingsToDBVersion20() error { + log.Info().Msg("updating settings") legacySettings, err := m.settingsService.Settings() if err != nil { return err diff --git a/api/datastore/migrator/migrate_dbversion22.go b/api/datastore/migrator/migrate_dbversion22.go index bcf043c71..b5de60130 100644 --- a/api/datastore/migrator/migrate_dbversion22.go +++ b/api/datastore/migrator/migrate_dbversion22.go @@ -22,6 +22,7 @@ func (m *Migrator) updateTagsToDBVersion23() error { return err } } + return nil } diff --git a/api/datastore/migrator/migrate_dbversion29.go b/api/datastore/migrator/migrate_dbversion29.go index 8dc167f24..ce54aeff0 100644 --- a/api/datastore/migrator/migrate_dbversion29.go +++ b/api/datastore/migrator/migrate_dbversion29.go @@ -19,5 +19,6 @@ func (m *Migrator) MigrateSettingsToDB30() error { legacySettings.OAuthSettings.SSO = false legacySettings.OAuthSettings.LogoutURI = "" + return m.settingsService.UpdateSettings(legacySettings) } diff --git a/api/datastore/migrator/migrate_dbversion32.go b/api/datastore/migrator/migrate_dbversion32.go index aa52af17f..26cd207de 100644 --- a/api/datastore/migrator/migrate_dbversion32.go +++ b/api/datastore/migrator/migrate_dbversion32.go @@ -13,6 +13,7 @@ func (m *Migrator) migrateDBVersionToDB33() error { } func (m *Migrator) migrateSettingsToDB33() error { + log.Info().Msg("setting default kubctl shell") settings, err := m.settingsService.Settings() if err != nil { return err diff --git a/api/datastore/migrator/migrate_dbversion35.go b/api/datastore/migrator/migrate_dbversion35.go index 27ccf757d..7b9e4a253 100644 --- a/api/datastore/migrator/migrate_dbversion35.go +++ b/api/datastore/migrator/migrate_dbversion35.go @@ -14,6 +14,8 @@ func (m *Migrator) migrateDBVersionToDB36() error { } func (m *Migrator) migrateUsersToDB36() error { + log.Info().Msg("updating user authorizations") + users, err := m.userService.Users() if err != nil { return err diff --git a/api/datastore/migrator/migrate_dbversion70.go b/api/datastore/migrator/migrate_dbversion70.go index 25f50f88f..ff121f598 100644 --- a/api/datastore/migrator/migrate_dbversion70.go +++ b/api/datastore/migrator/migrate_dbversion70.go @@ -2,6 +2,7 @@ package migrator import ( portainer "github.com/portainer/portainer/api" + "github.com/rs/zerolog/log" ) diff --git a/api/datastore/services.go b/api/datastore/services.go index 4a52a4b80..c4bd90003 100644 --- a/api/datastore/services.go +++ b/api/datastore/services.go @@ -518,7 +518,7 @@ func (store *Store) Export(filename string) (err error) { if snapshot, err := store.Snapshot().Snapshots(); err != nil { if !store.IsErrObjectNotFound(err) { - log.Err(err).Msg("Exporting Snapshots") + log.Error().Err(err).Msg("exporting Snapshots") } } else { backup.Snapshot = snapshot @@ -707,7 +707,7 @@ func (store *Store) Import(filename string) (err error) { 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") + log.Debug().Str("user", fmt.Sprintf("%+v", user)).Err(err).Msg("failed to update the user in the database") } } diff --git a/api/docker/labels.go b/api/docker/labels.go new file mode 100644 index 000000000..3a56cdbc8 --- /dev/null +++ b/api/docker/labels.go @@ -0,0 +1,6 @@ +package docker + +const ( + ComposeStackNameLabel = "com.docker.compose.project" + SwarmStackNameLabel = "com.docker.stack.namespace" +) diff --git a/api/docker/snapshot.go b/api/docker/snapshot.go index 441a08fad..2e9119259 100644 --- a/api/docker/snapshot.go +++ b/api/docker/snapshot.go @@ -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{}{} } } diff --git a/api/exec/compose_stack.go b/api/exec/compose_stack.go index c29b625da..990ac94bb 100644 --- a/api/exec/compose_stack.go +++ b/api/exec/compose_stack.go @@ -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/pkg/errors" ) // ComposeStackManager is a wrapper for docker-compose binary diff --git a/api/exec/exectest/kubernetes_mocks.go b/api/exec/exectest/kubernetes_mocks.go index cabc4a716..b48196586 100644 --- a/api/exec/exectest/kubernetes_mocks.go +++ b/api/exec/exectest/kubernetes_mocks.go @@ -6,6 +6,7 @@ import ( type kubernetesMockDeployer struct{} +// NewKubernetesDeployer creates a mock kubernetes deployer func NewKubernetesDeployer() portainer.KubernetesDeployer { return &kubernetesMockDeployer{} } diff --git a/api/exec/kubernetes_deploy.go b/api/exec/kubernetes_deploy.go index 235626e76..acadef038 100644 --- a/api/exec/kubernetes_deploy.go +++ b/api/exec/kubernetes_deploy.go @@ -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 } diff --git a/api/http/handler/auth/authenticate.go b/api/http/handler/auth/authenticate.go index d1c8bbae5..f5da2b59b 100644 --- a/api/http/handler/auth/authenticate.go +++ b/api/http/handler/auth/authenticate.go @@ -1,7 +1,6 @@ package auth import ( - "errors" "net/http" "strings" @@ -13,6 +12,7 @@ import ( "github.com/portainer/portainer/api/internal/authorization" "github.com/asaskevich/govalidator" + "github.com/pkg/errors" "github.com/rs/zerolog/log" ) diff --git a/api/http/handler/auth/authenticate_oauth.go b/api/http/handler/auth/authenticate_oauth.go index 14863723b..7122e0021 100644 --- a/api/http/handler/auth/authenticate_oauth.go +++ b/api/http/handler/auth/authenticate_oauth.go @@ -22,6 +22,7 @@ func (payload *oauthPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Code) { return errors.New("Invalid OAuth authorization code") } + return nil } diff --git a/api/http/handler/auth/handler.go b/api/http/handler/auth/handler.go index cd9bd45b7..259941298 100644 --- a/api/http/handler/auth/handler.go +++ b/api/http/handler/auth/handler.go @@ -3,13 +3,14 @@ package auth import ( "net/http" - "github.com/gorilla/mux" httperror "github.com/portainer/libhttp/error" 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/kubernetes" "github.com/portainer/portainer/api/http/security" + + "github.com/gorilla/mux" ) // Handler is the HTTP handler used to handle authentication operations. diff --git a/api/http/handler/backup/backup_test.go b/api/http/handler/backup/backup_test.go index 3abc5cb72..8c2fb979f 100644 --- a/api/http/handler/backup/backup_test.go +++ b/api/http/handler/backup/backup_test.go @@ -56,6 +56,7 @@ func Test_backupHandlerWithoutPassword_shouldCreateATarballArchive(t *testing.T) body, _ := io.ReadAll(response.Body) tmpdir := t.TempDir() + archivePath := filepath.Join(tmpdir, "archive.tar.gz") err := ioutil.WriteFile(archivePath, body, 0600) if err != nil { @@ -91,6 +92,7 @@ func Test_backupHandlerWithPassword_shouldCreateEncryptedATarballArchive(t *test body, _ := io.ReadAll(response.Body) tmpdir := t.TempDir() + dr, err := crypto.AesDecrypt(bytes.NewReader(body), []byte("secret")) if err != nil { t.Fatal("Failed to decrypt archive") diff --git a/api/http/handler/customtemplates/customtemplate_create.go b/api/http/handler/customtemplates/customtemplate_create.go index 4929edb65..48534bebd 100644 --- a/api/http/handler/customtemplates/customtemplate_create.go +++ b/api/http/handler/customtemplates/customtemplate_create.go @@ -3,6 +3,7 @@ package customtemplates import ( "encoding/json" "errors" + "fmt" "net/http" "os" "regexp" @@ -13,6 +14,7 @@ import ( "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/filesystem" + "github.com/portainer/portainer/api/git" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" @@ -290,6 +292,9 @@ func (handler *Handler) createCustomTemplateFromGitRepository(r *http.Request) ( err = handler.GitService.CloneRepository(projectPath, payload.RepositoryURL, payload.RepositoryReferenceName, repositoryUsername, repositoryPassword) if err != nil { + if err == git.ErrAuthenticationFailure { + return nil, fmt.Errorf("invalid git credential") + } return nil, err } diff --git a/api/http/handler/edgegroups/edgegroup_create.go b/api/http/handler/edgegroups/edgegroup_create.go index 60ceab258..5866bf751 100644 --- a/api/http/handler/edgegroups/edgegroup_create.go +++ b/api/http/handler/edgegroups/edgegroup_create.go @@ -4,11 +4,13 @@ import ( "errors" "net/http" - "github.com/asaskevich/govalidator" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/endpointutils" + + "github.com/asaskevich/govalidator" ) type edgeGroupCreatePayload struct { @@ -81,7 +83,7 @@ func (handler *Handler) edgeGroupCreate(w http.ResponseWriter, r *http.Request) return httperror.InternalServerError("Unable to retrieve environment from the database", err) } - if endpoint.Type == portainer.EdgeAgentOnDockerEnvironment || endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment { + if endpointutils.IsEdgeEndpoint(endpoint) { endpointIDs = append(endpointIDs, endpoint.ID) } } diff --git a/api/http/handler/edgegroups/edgegroup_delete.go b/api/http/handler/edgegroups/edgegroup_delete.go index 660f3f0a0..de159ab20 100644 --- a/api/http/handler/edgegroups/edgegroup_delete.go +++ b/api/http/handler/edgegroups/edgegroup_delete.go @@ -53,5 +53,4 @@ func (handler *Handler) edgeGroupDelete(w http.ResponseWriter, r *http.Request) } return response.Empty(w) - } diff --git a/api/http/handler/edgegroups/edgegroup_list.go b/api/http/handler/edgegroups/edgegroup_list.go index 295c8dee9..e16787e67 100644 --- a/api/http/handler/edgegroups/edgegroup_list.go +++ b/api/http/handler/edgegroups/edgegroup_list.go @@ -81,7 +81,7 @@ func getEndpointTypes(endpointService dataservices.EndpointService, endpointIds for _, endpointID := range endpointIds { endpoint, err := endpointService.Endpoint(endpointID) if err != nil { - return nil, fmt.Errorf("failed fetching endpoint: %w", err) + return nil, fmt.Errorf("failed fetching environment: %w", err) } typeSet[endpoint.Type] = true diff --git a/api/http/handler/edgegroups/edgegroup_update.go b/api/http/handler/edgegroups/edgegroup_update.go index ed24ed1cd..9676066f2 100644 --- a/api/http/handler/edgegroups/edgegroup_update.go +++ b/api/http/handler/edgegroups/edgegroup_update.go @@ -4,12 +4,14 @@ import ( "errors" "net/http" - "github.com/asaskevich/govalidator" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/internal/edge" + "github.com/portainer/portainer/api/internal/endpointutils" + + "github.com/asaskevich/govalidator" ) type edgeGroupUpdatePayload struct { @@ -102,7 +104,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request) return httperror.InternalServerError("Unable to retrieve environment from the database", err) } - if endpoint.Type == portainer.EdgeAgentOnDockerEnvironment || endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment { + if endpointutils.IsEdgeEndpoint(endpoint) { endpointIDs = append(endpointIDs, endpoint.ID) } } diff --git a/api/http/handler/edgejobs/edgejob_create.go b/api/http/handler/edgejobs/edgejob_create.go index efac661a2..4363493c1 100644 --- a/api/http/handler/edgejobs/edgejob_create.go +++ b/api/http/handler/edgejobs/edgejob_create.go @@ -12,6 +12,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/endpointutils" ) // @id EdgeJobCreate @@ -200,7 +201,7 @@ func (handler *Handler) addAndPersistEdgeJob(edgeJob *portainer.EdgeJob, file [] return err } - if endpoint.Type != portainer.EdgeAgentOnDockerEnvironment && endpoint.Type != portainer.EdgeAgentOnKubernetesEnvironment { + if !endpointutils.IsEdgeEndpoint(endpoint) { delete(edgeJob.Endpoints, ID) } } diff --git a/api/http/handler/edgejobs/edgejob_update.go b/api/http/handler/edgejobs/edgejob_update.go index 5932d4cab..0fd035470 100644 --- a/api/http/handler/edgejobs/edgejob_update.go +++ b/api/http/handler/edgejobs/edgejob_update.go @@ -5,11 +5,12 @@ import ( "net/http" "strconv" - "github.com/asaskevich/govalidator" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" + + "github.com/asaskevich/govalidator" ) type edgeJobUpdatePayload struct { diff --git a/api/http/handler/edgestacks/edgestack_create.go b/api/http/handler/edgestacks/edgestack_create.go index c3208a54c..ff27516d2 100644 --- a/api/http/handler/edgestacks/edgestack_create.go +++ b/api/http/handler/edgestacks/edgestack_create.go @@ -1,14 +1,12 @@ package edgestacks import ( - "errors" "fmt" "net/http" "strconv" "strings" "time" - "github.com/asaskevich/govalidator" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" @@ -16,6 +14,9 @@ import ( "github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/filesystem" "github.com/portainer/portainer/api/internal/edge" + + "github.com/asaskevich/govalidator" + "github.com/pkg/errors" ) // @id EdgeStackCreate @@ -271,7 +272,7 @@ func (handler *Handler) createSwarmStackFromGitRepository(r *http.Request) (*por err = updateEndpointRelations(handler.DataStore.EndpointRelation(), stack.ID, relatedEndpointIds) if err != nil { - return nil, fmt.Errorf("Unable to update endpoint relations: %w", err) + return nil, fmt.Errorf("Unable to update environment relations: %w", err) } err = handler.DataStore.EdgeStack().Create(stack.ID, stack) @@ -378,7 +379,7 @@ func (handler *Handler) createSwarmStackFromFileUpload(r *http.Request) (*portai err = updateEndpointRelations(handler.DataStore.EndpointRelation(), stack.ID, relatedEndpointIds) if err != nil { - return nil, fmt.Errorf("Unable to update endpoint relations: %w", err) + return nil, fmt.Errorf("Unable to update environment relations: %w", err) } err = handler.DataStore.EdgeStack().Create(stack.ID, stack) @@ -408,14 +409,14 @@ func updateEndpointRelations(endpointRelationService dataservices.EndpointRelati for _, endpointID := range relatedEndpointIds { relation, err := endpointRelationService.EndpointRelation(endpointID) if err != nil { - return fmt.Errorf("unable to find endpoint relation in database: %w", err) + return fmt.Errorf("unable to find environment relation in database: %w", err) } relation.EdgeStacks[edgeStackID] = true err = endpointRelationService.UpdateEndpointRelation(endpointID, relation) if err != nil { - return fmt.Errorf("unable to persist endpoint relation in database: %w", err) + return fmt.Errorf("unable to persist environment relation in database: %w", err) } } diff --git a/api/http/handler/edgestacks/edgestack_create_test.go b/api/http/handler/edgestacks/edgestack_create_test.go index 8e1144e4a..a519e741c 100644 --- a/api/http/handler/edgestacks/edgestack_create_test.go +++ b/api/http/handler/edgestacks/edgestack_create_test.go @@ -5,6 +5,7 @@ import ( portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/internal/testhelpers" + "github.com/stretchr/testify/assert" ) diff --git a/api/http/handler/edgestacks/edgestack_delete.go b/api/http/handler/edgestacks/edgestack_delete.go index cc89b105a..2d1dff2b0 100644 --- a/api/http/handler/edgestacks/edgestack_delete.go +++ b/api/http/handler/edgestacks/edgestack_delete.go @@ -42,7 +42,7 @@ func (handler *Handler) edgeStackDelete(w http.ResponseWriter, r *http.Request) relationConfig, err := fetchEndpointRelationsConfig(handler.DataStore) if err != nil { - return httperror.InternalServerError("Unable to retrieve environments relations config from database", err) + return httperror.InternalServerError("Unable to find environment relations in database", err) } relatedEndpointIds, err := edge.EdgeStackRelatedEndpoints(edgeStack.EdgeGroups, relationConfig.endpoints, relationConfig.endpointGroups, relationConfig.edgeGroups) diff --git a/api/http/handler/edgestacks/edgestack_file.go b/api/http/handler/edgestacks/edgestack_file.go index 732133502..62ed9e4c1 100644 --- a/api/http/handler/edgestacks/edgestack_file.go +++ b/api/http/handler/edgestacks/edgestack_file.go @@ -46,7 +46,7 @@ func (handler *Handler) edgeStackFile(w http.ResponseWriter, r *http.Request) *h stackFileContent, err := handler.FileService.GetFileContent(stack.ProjectPath, fileName) if err != nil { - return httperror.InternalServerError("Unable to retrieve Compose file from disk", err) + return httperror.InternalServerError("Unable to retrieve stack file from disk", err) } return response.JSON(w, &stackFileResponse{StackFileContent: string(stackFileContent)}) diff --git a/api/http/handler/edgestacks/edgestack_test.go b/api/http/handler/edgestacks/edgestack_test.go index cc9267b22..c8f487250 100644 --- a/api/http/handler/edgestacks/edgestack_test.go +++ b/api/http/handler/edgestacks/edgestack_test.go @@ -3,7 +3,6 @@ package edgestacks import ( "bytes" "encoding/json" - "errors" "fmt" "net/http" "net/http/httptest" @@ -19,6 +18,8 @@ import ( "github.com/portainer/portainer/api/filesystem" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/jwt" + + "github.com/pkg/errors" ) type gitService struct { diff --git a/api/http/handler/edgestacks/edgestack_update.go b/api/http/handler/edgestacks/edgestack_update.go index 5d45d7a75..05bd2fd6a 100644 --- a/api/http/handler/edgestacks/edgestack_update.go +++ b/api/http/handler/edgestacks/edgestack_update.go @@ -2,7 +2,6 @@ package edgestacks import ( "errors" - "github.com/portainer/portainer/api/internal/endpointutils" "net/http" "strconv" @@ -12,6 +11,7 @@ import ( portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/filesystem" "github.com/portainer/portainer/api/internal/edge" + "github.com/portainer/portainer/api/internal/endpointutils" ) type updateEdgeStackPayload struct { diff --git a/api/http/handler/edgeupdateschedules/edgeupdateschedule_create.go b/api/http/handler/edgeupdateschedules/edgeupdateschedule_create.go index b3b7fe7aa..394f3a594 100644 --- a/api/http/handler/edgeupdateschedules/edgeupdateschedule_create.go +++ b/api/http/handler/edgeupdateschedules/edgeupdateschedule_create.go @@ -31,14 +31,14 @@ func (payload *createPayload) Validate(r *http.Request) error { return errors.New("Required to choose at least one group") } - if payload.Type != edgetypes.UpdateScheduleRollback && payload.Type != edgetypes.UpdateScheduleUpdate { - return errors.New("Invalid schedule type") - } - if len(payload.Environments) == 0 { return errors.New("No Environment is scheduled for update") } + if payload.Type != edgetypes.UpdateScheduleRollback && payload.Type != edgetypes.UpdateScheduleUpdate { + return errors.New("Invalid schedule type") + } + if payload.Time < time.Now().Unix() { return errors.New("Invalid time") } diff --git a/api/http/handler/endpointedge/handler.go b/api/http/handler/endpointedge/handler.go index 62d079ff2..af94f977f 100644 --- a/api/http/handler/endpointedge/handler.go +++ b/api/http/handler/endpointedge/handler.go @@ -3,14 +3,13 @@ package endpointedge import ( "net/http" - "github.com/portainer/portainer/api/http/middlewares" - httperror "github.com/portainer/libhttp/error" - - "github.com/gorilla/mux" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/dataservices" + "github.com/portainer/portainer/api/http/middlewares" "github.com/portainer/portainer/api/http/security" + + "github.com/gorilla/mux" ) // Handler is the HTTP handler used to handle edge environment(endpoint) operations. diff --git a/api/http/handler/endpointproxy/proxy_docker.go b/api/http/handler/endpointproxy/proxy_docker.go index 2370f0c10..9a3c698b6 100644 --- a/api/http/handler/endpointproxy/proxy_docker.go +++ b/api/http/handler/endpointproxy/proxy_docker.go @@ -2,13 +2,13 @@ package endpointproxy import ( "errors" - httperror "github.com/portainer/libhttp/error" - "github.com/portainer/libhttp/request" - portainer "github.com/portainer/portainer/api" + "net/http" "strconv" "strings" - "net/http" + httperror "github.com/portainer/libhttp/error" + "github.com/portainer/libhttp/request" + portainer "github.com/portainer/portainer/api" ) func (handler *Handler) proxyRequestsToDockerAPI(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { diff --git a/api/http/handler/endpointproxy/proxy_kubernetes.go b/api/http/handler/endpointproxy/proxy_kubernetes.go index ac0bded50..73d39ac21 100644 --- a/api/http/handler/endpointproxy/proxy_kubernetes.go +++ b/api/http/handler/endpointproxy/proxy_kubernetes.go @@ -3,12 +3,12 @@ package endpointproxy import ( "errors" "fmt" + "net/http" + "strings" + httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" portainer "github.com/portainer/portainer/api" - "strings" - - "net/http" ) func (handler *Handler) proxyRequestsToKubernetesAPI(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { diff --git a/api/http/handler/endpoints/endpoint_list_test.go b/api/http/handler/endpoints/endpoint_list_test.go index a3b5ad174..e94142d0c 100644 --- a/api/http/handler/endpoints/endpoint_list_test.go +++ b/api/http/handler/endpoints/endpoint_list_test.go @@ -102,7 +102,6 @@ func Test_EndpointList_AgentVersion(t *testing.T) { is.ElementsMatch(test.expected, respIds) }) } - } func Test_endpointList_edgeDeviceFilter(t *testing.T) { diff --git a/api/http/handler/endpoints/endpoint_snapshots.go b/api/http/handler/endpoints/endpoint_snapshots.go index 4b86bb8ca..a187fdbe2 100644 --- a/api/http/handler/endpoints/endpoint_snapshots.go +++ b/api/http/handler/endpoints/endpoint_snapshots.go @@ -32,6 +32,10 @@ func (handler *Handler) endpointSnapshots(w http.ResponseWriter, r *http.Request continue } + if endpoint.URL == "" { + continue + } + snapshotError := handler.SnapshotService.SnapshotEndpoint(&endpoint) latestEndpointReference, err := handler.DataStore.Endpoint().Endpoint(endpoint.ID) diff --git a/api/http/handler/helm/helm_list.go b/api/http/handler/helm/helm_list.go index 029f55dae..2105c7b15 100644 --- a/api/http/handler/helm/helm_list.go +++ b/api/http/handler/helm/helm_list.go @@ -38,8 +38,6 @@ func (handler *Handler) helmList(w http.ResponseWriter, r *http.Request) *httper KubernetesClusterAccess: clusterAccess, } - params := r.URL.Query() - // optional namespace. The library defaults to "default" namespace, _ := request.RetrieveQueryParameter(r, "namespace", true) if namespace != "" { @@ -47,12 +45,12 @@ func (handler *Handler) helmList(w http.ResponseWriter, r *http.Request) *httper } // optional filter - if filter := params.Get("filter"); filter != "" { + if filter, _ := request.RetrieveQueryParameter(r, "filter", true); filter != "" { listOpts.Filter = filter } // optional selector - if selector := params.Get("selector"); selector != "" { + if selector, _ := request.RetrieveQueryParameter(r, "selector", true); selector != "" { listOpts.Selector = selector } diff --git a/api/http/handler/helm/helm_repo_search_test.go b/api/http/handler/helm/helm_repo_search_test.go index f804a5195..974e527ee 100644 --- a/api/http/handler/helm/helm_repo_search_test.go +++ b/api/http/handler/helm/helm_repo_search_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/portainer/libhelm/binary/test" - helper "github.com/portainer/portainer/api/internal/testhelpers" "github.com/stretchr/testify/assert" ) @@ -32,6 +31,7 @@ func Test_helmRepoSearch(t *testing.T) { h.ServeHTTP(rr, req) is.Equal(http.StatusOK, rr.Code, "Status should be 200 OK") + body, err := io.ReadAll(rr.Body) is.NoError(err, "ReadAll should not return error") is.NotEmpty(body, "Body should not be empty") diff --git a/api/http/handler/helm/helm_show.go b/api/http/handler/helm/helm_show.go index a42fb93b9..631b0aa90 100644 --- a/api/http/handler/helm/helm_show.go +++ b/api/http/handler/helm/helm_show.go @@ -5,11 +5,11 @@ import ( "net/http" "net/url" - "github.com/pkg/errors" "github.com/portainer/libhelm/options" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" + "github.com/pkg/errors" "github.com/rs/zerolog/log" ) diff --git a/api/http/handler/helm/user_helm_repos.go b/api/http/handler/helm/user_helm_repos.go index ab3a0c5f3..e25eeb81a 100644 --- a/api/http/handler/helm/user_helm_repos.go +++ b/api/http/handler/helm/user_helm_repos.go @@ -56,6 +56,7 @@ func (handler *Handler) userCreateHelmRepo(w http.ResponseWriter, r *http.Reques if err != nil { return httperror.BadRequest("Invalid Helm repository URL", err) } + // lowercase, remove trailing slash p.URL = strings.TrimSuffix(strings.ToLower(p.URL), "/") diff --git a/api/http/handler/hostmanagement/fdo/fdo.go b/api/http/handler/hostmanagement/fdo/fdo.go index 15e035643..ef846a648 100644 --- a/api/http/handler/hostmanagement/fdo/fdo.go +++ b/api/http/handler/hostmanagement/fdo/fdo.go @@ -89,7 +89,7 @@ func (handler *Handler) fdoConfigure(w http.ResponseWriter, r *http.Request) *ht err := request.DecodeAndValidateJSONPayload(r, &payload) if err != nil { - log.Error().Err(err).Msg("Invalid request payload") + log.Error().Err(err).Msg("invalid request payload") return httperror.BadRequest("Invalid request payload", err) } diff --git a/api/http/handler/hostmanagement/fdo/profile_duplicate.go b/api/http/handler/hostmanagement/fdo/profile_duplicate.go index 8ead56a0c..071041ec5 100644 --- a/api/http/handler/hostmanagement/fdo/profile_duplicate.go +++ b/api/http/handler/hostmanagement/fdo/profile_duplicate.go @@ -3,13 +3,14 @@ package fdo import ( "errors" "fmt" + "net/http" + "strconv" + "time" + httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" - "net/http" - "strconv" - "time" ) // @id duplicate diff --git a/api/http/handler/kubernetes/config.go b/api/http/handler/kubernetes/config.go index 02fdc2f1d..208b6da73 100644 --- a/api/http/handler/kubernetes/config.go +++ b/api/http/handler/kubernetes/config.go @@ -38,7 +38,7 @@ func (handler *Handler) getKubernetesConfig(w http.ResponseWriter, r *http.Reque if err != nil { return httperror.Forbidden("Permission denied to access environment", err) } - bearerToken, err := handler.jwtService.GenerateTokenForKubeconfig(tokenData) + bearerToken, err := handler.JwtService.GenerateTokenForKubeconfig(tokenData) if err != nil { return httperror.InternalServerError("Unable to generate JWT token", err) } @@ -75,7 +75,7 @@ func (handler *Handler) filterUserKubeEndpoints(r *http.Request) ([]portainer.En return nil, httperror.InternalServerError("Unable to retrieve info from request context", err) } - endpointGroups, err := handler.dataStore.EndpointGroup().EndpointGroups() + endpointGroups, err := handler.DataStore.EndpointGroup().EndpointGroups() if err != nil { return nil, httperror.InternalServerError("Unable to retrieve environment groups from the database", err) } @@ -83,7 +83,7 @@ func (handler *Handler) filterUserKubeEndpoints(r *http.Request) ([]portainer.En if len(endpointIDs) > 0 { var endpoints []portainer.Endpoint for _, endpointID := range endpointIDs { - endpoint, err := handler.dataStore.Endpoint().Endpoint(endpointID) + endpoint, err := handler.DataStore.Endpoint().Endpoint(endpointID) if err != nil { return nil, httperror.InternalServerError("Unable to retrieve environment from the database", err) } @@ -97,7 +97,7 @@ func (handler *Handler) filterUserKubeEndpoints(r *http.Request) ([]portainer.En } var kubeEndpoints []portainer.Endpoint - endpoints, err := handler.dataStore.Endpoint().Endpoints() + endpoints, err := handler.DataStore.Endpoint().Endpoints() if err != nil { return nil, httperror.InternalServerError("Unable to retrieve environments from the database", err) } @@ -122,7 +122,7 @@ func (handler *Handler) buildConfig(r *http.Request, tokenData *portainer.TokenD authInfosSet := make(map[string]bool) for idx, endpoint := range endpoints { - instanceID := handler.kubernetesClientFactory.GetInstanceID() + instanceID := handler.KubernetesClientFactory.GetInstanceID() serviceAccountName := kcli.UserServiceAccountName(int(tokenData.ID), instanceID) configClusters[idx] = handler.buildCluster(r, endpoint) @@ -145,6 +145,7 @@ func (handler *Handler) buildConfig(r *http.Request, tokenData *portainer.TokenD func (handler *Handler) buildCluster(r *http.Request, endpoint portainer.Endpoint) clientV1.NamedCluster { kubeConfigInternal := handler.kubeClusterAccessService.GetData(r.Host, endpoint.ID) + return clientV1.NamedCluster{ Name: buildClusterName(endpoint.Name), Cluster: clientV1.Cluster{ diff --git a/api/http/handler/kubernetes/handler.go b/api/http/handler/kubernetes/handler.go index e5846ca4e..680e4ea46 100644 --- a/api/http/handler/kubernetes/handler.go +++ b/api/http/handler/kubernetes/handler.go @@ -23,10 +23,10 @@ import ( type Handler struct { *mux.Router authorizationService *authorization.Service - dataStore dataservices.DataStore + DataStore dataservices.DataStore KubernetesClient portainer.KubeClient - kubernetesClientFactory *cli.ClientFactory - jwtService dataservices.JWTService + KubernetesClientFactory *cli.ClientFactory + JwtService dataservices.JWTService kubeClusterAccessService kubernetes.KubeClusterAccessService } @@ -35,10 +35,10 @@ func NewHandler(bouncer *security.RequestBouncer, authorizationService *authoriz h := &Handler{ Router: mux.NewRouter(), authorizationService: authorizationService, - dataStore: dataStore, - jwtService: jwtService, + DataStore: dataStore, + JwtService: jwtService, kubeClusterAccessService: kubeClusterAccessService, - kubernetesClientFactory: kubernetesClientFactory, + KubernetesClientFactory: kubernetesClientFactory, KubernetesClient: kubernetesClient, } @@ -90,7 +90,7 @@ func kubeOnlyMiddleware(next http.Handler) http.Handler { } if !endpointutils.IsKubernetesEndpoint(endpoint) { - errMessage := "Environment is not a kubernetes environment" + errMessage := "environment is not a Kubernetes environment" httperror.WriteError(rw, http.StatusBadRequest, errMessage, errors.New(errMessage)) return } @@ -111,7 +111,7 @@ func (handler *Handler) kubeClient(next http.Handler) http.Handler { ) } - endpoint, err := handler.dataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) + endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) if err == portainerDsErrors.ErrObjectNotFound { httperror.WriteError( w, @@ -128,11 +128,11 @@ func (handler *Handler) kubeClient(next http.Handler) http.Handler { ) } - if handler.kubernetesClientFactory == nil { + if handler.KubernetesClientFactory == nil { next.ServeHTTP(w, r) return } - kubeCli, err := handler.kubernetesClientFactory.GetKubeClient(endpoint) + kubeCli, err := handler.KubernetesClientFactory.GetKubeClient(endpoint) if err != nil { httperror.WriteError( w, diff --git a/api/http/handler/kubernetes/ingresses.go b/api/http/handler/kubernetes/ingresses.go index 40051cb4f..f12492c13 100644 --- a/api/http/handler/kubernetes/ingresses.go +++ b/api/http/handler/kubernetes/ingresses.go @@ -20,7 +20,7 @@ func (handler *Handler) getKubernetesIngressControllers(w http.ResponseWriter, r ) } - endpoint, err := handler.dataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) + endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) if err == portainerDsErrors.ErrObjectNotFound { return httperror.NotFound( "Unable to find an environment with the specified identifier inside the database", @@ -41,7 +41,7 @@ func (handler *Handler) getKubernetesIngressControllers(w http.ResponseWriter, r ) } - cli, err := handler.kubernetesClientFactory.GetKubeClient(endpoint) + cli, err := handler.KubernetesClientFactory.GetKubeClient(endpoint) if err != nil { return httperror.InternalServerError( "Unable to create Kubernetes client", @@ -86,7 +86,7 @@ func (handler *Handler) getKubernetesIngressControllers(w http.ResponseWriter, r newClasses = append(newClasses, class) } endpoint.Kubernetes.Configuration.IngressClasses = newClasses - err = handler.dataStore.Endpoint().UpdateEndpoint( + err = handler.DataStore.Endpoint().UpdateEndpoint( portainer.EndpointID(endpointID), endpoint, ) @@ -120,8 +120,8 @@ func (handler *Handler) getKubernetesIngressControllersByNamespace(w http.Respon ) } - endpoint, err := handler.dataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainerDsErrors.ErrObjectNotFound { + endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) + if handler.DataStore.IsErrObjectNotFound(err) { return httperror.NotFound( "Unable to find an environment with the specified identifier inside the database", err, @@ -183,7 +183,7 @@ func (handler *Handler) getKubernetesIngressControllersByNamespace(w http.Respon // Update the database to match the list of found controllers. // This includes pruning out controllers which no longer exist. endpoint.Kubernetes.Configuration.IngressClasses = updatedClasses - err = handler.dataStore.Endpoint().UpdateEndpoint( + err = handler.DataStore.Endpoint().UpdateEndpoint( portainer.EndpointID(endpointID), endpoint, ) @@ -205,8 +205,8 @@ func (handler *Handler) updateKubernetesIngressControllers(w http.ResponseWriter ) } - endpoint, err := handler.dataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainerDsErrors.ErrObjectNotFound { + endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) + if handler.DataStore.IsErrObjectNotFound(err) { return httperror.NotFound( "Unable to find an environment with the specified identifier inside the database", err, @@ -227,7 +227,7 @@ func (handler *Handler) updateKubernetesIngressControllers(w http.ResponseWriter ) } - cli, err := handler.kubernetesClientFactory.GetKubeClient(endpoint) + cli, err := handler.KubernetesClientFactory.GetKubeClient(endpoint) if err != nil { return httperror.InternalServerError( "Unable to create Kubernetes client", @@ -269,7 +269,7 @@ func (handler *Handler) updateKubernetesIngressControllers(w http.ResponseWriter } endpoint.Kubernetes.Configuration.IngressClasses = newClasses - err = handler.dataStore.Endpoint().UpdateEndpoint( + err = handler.DataStore.Endpoint().UpdateEndpoint( portainer.EndpointID(endpointID), endpoint, ) @@ -291,8 +291,8 @@ func (handler *Handler) updateKubernetesIngressControllersByNamespace(w http.Res ) } - endpoint, err := handler.dataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainerDsErrors.ErrObjectNotFound { + endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) + if handler.DataStore.IsErrObjectNotFound(err) { return httperror.NotFound( "Unable to find an environment with the specified identifier inside the database", err, @@ -369,7 +369,7 @@ PayloadLoop: } endpoint.Kubernetes.Configuration.IngressClasses = updatedClasses - err = handler.dataStore.Endpoint().UpdateEndpoint( + err = handler.DataStore.Endpoint().UpdateEndpoint( portainer.EndpointID(endpointID), endpoint, ) diff --git a/api/http/handler/kubernetes/namespaces.go b/api/http/handler/kubernetes/namespaces.go index 7dfc09d5a..884ae7038 100644 --- a/api/http/handler/kubernetes/namespaces.go +++ b/api/http/handler/kubernetes/namespaces.go @@ -73,20 +73,12 @@ func (handler *Handler) updateKubernetesNamespace(w http.ResponseWriter, r *http var payload models.K8sNamespaceDetails err := request.DecodeAndValidateJSONPayload(r, &payload) if err != nil { - return &httperror.HandlerError{ - StatusCode: http.StatusBadRequest, - Message: "Invalid request payload", - Err: err, - } + return httperror.BadRequest("Invalid request payload", err) } err = cli.UpdateNamespace(payload) if err != nil { - return &httperror.HandlerError{ - StatusCode: http.StatusInternalServerError, - Message: "Unable to retrieve nodes limits", - Err: err, - } + return httperror.InternalServerError("Unable to retrieve nodes limits", err) } return nil } diff --git a/api/http/handler/kubernetes/nodes_limits.go b/api/http/handler/kubernetes/nodes_limits.go index 293b020f0..6db7f0f93 100644 --- a/api/http/handler/kubernetes/nodes_limits.go +++ b/api/http/handler/kubernetes/nodes_limits.go @@ -32,14 +32,14 @@ func (handler *Handler) getKubernetesNodesLimits(w http.ResponseWriter, r *http. return httperror.BadRequest("Invalid environment identifier route variable", err) } - endpoint, err := handler.dataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if handler.dataStore.IsErrObjectNotFound(err) { + endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) + if handler.DataStore.IsErrObjectNotFound(err) { return httperror.NotFound("Unable to find an environment with the specified identifier inside the database", err) } else if err != nil { return httperror.InternalServerError("Unable to find an environment with the specified identifier inside the database", err) } - cli, err := handler.kubernetesClientFactory.GetKubeClient(endpoint) + cli, err := handler.KubernetesClientFactory.GetKubeClient(endpoint) if err != nil { return httperror.InternalServerError("Unable to create Kubernetes client", err) } diff --git a/api/http/handler/kubernetes/toggle_system.go b/api/http/handler/kubernetes/toggle_system.go index a77cd2f94..4eeaaf11e 100644 --- a/api/http/handler/kubernetes/toggle_system.go +++ b/api/http/handler/kubernetes/toggle_system.go @@ -51,7 +51,7 @@ func (handler *Handler) namespacesToggleSystem(rw http.ResponseWriter, r *http.R return httperror.BadRequest("Invalid request payload", err) } - kubeClient, err := handler.kubernetesClientFactory.GetKubeClient(endpoint) + kubeClient, err := handler.KubernetesClientFactory.GetKubeClient(endpoint) if err != nil { return httperror.InternalServerError("Unable to create kubernetes client", err) } diff --git a/api/http/handler/registries/registry_create.go b/api/http/handler/registries/registry_create.go index b79ca5eaf..8da875a3f 100644 --- a/api/http/handler/registries/registry_create.go +++ b/api/http/handler/registries/registry_create.go @@ -119,9 +119,7 @@ func (handler *Handler) registryCreate(w http.ResponseWriter, r *http.Request) * Ecr: payload.Ecr, } - rs := handler.DataStore.Registry() - - registries, err := rs.Registries() + registries, err := handler.DataStore.Registry().Registries() if err != nil { return httperror.InternalServerError("Unable to retrieve registries from the database", err) } @@ -134,7 +132,7 @@ func (handler *Handler) registryCreate(w http.ResponseWriter, r *http.Request) * } } - err = rs.Create(registry) + err = handler.DataStore.Registry().Create(registry) if err != nil { return httperror.InternalServerError("Unable to persist the registry inside the database", err) } diff --git a/api/http/handler/registries/registry_update_test.go b/api/http/handler/registries/registry_update_test.go index 2516b097b..59c28d695 100644 --- a/api/http/handler/registries/registry_update_test.go +++ b/api/http/handler/registries/registry_update_test.go @@ -74,7 +74,7 @@ func delete_TestHandler_registryUpdate(t *testing.T) { }, } - handler.Router.ServeHTTP(w, r) + handler.ServeHTTP(w, r) assert.Equal(t, http.StatusOK, w.Code) // Registry type should remain intact assert.Equal(t, registry.Type, updatedRegistry.Type) @@ -85,5 +85,4 @@ func delete_TestHandler_registryUpdate(t *testing.T) { assert.Equal(t, *payload.Authentication, updatedRegistry.Authentication) assert.Equal(t, *payload.Username, updatedRegistry.Username) assert.Equal(t, *payload.Password, updatedRegistry.Password) - } diff --git a/api/http/handler/settings/settings_update.go b/api/http/handler/settings/settings_update.go index c49fb8ac5..49a005207 100644 --- a/api/http/handler/settings/settings_update.go +++ b/api/http/handler/settings/settings_update.go @@ -141,7 +141,6 @@ func (handler *Handler) settingsUpdate(w http.ResponseWriter, r *http.Request) * if err != nil { return httperror.BadRequest("Invalid Helm repository URL. Must correspond to a valid URL format", err) } - } settings.HelmRepositoryURL = newHelmRepo @@ -161,12 +160,15 @@ func (handler *Handler) settingsUpdate(w http.ResponseWriter, r *http.Request) * if payload.LDAPSettings != nil { ldapReaderDN := settings.LDAPSettings.ReaderDN ldapPassword := settings.LDAPSettings.Password + if payload.LDAPSettings.ReaderDN != "" { ldapReaderDN = payload.LDAPSettings.ReaderDN } + if payload.LDAPSettings.Password != "" { ldapPassword = payload.LDAPSettings.Password } + settings.LDAPSettings = *payload.LDAPSettings settings.LDAPSettings.ReaderDN = ldapReaderDN settings.LDAPSettings.Password = ldapPassword diff --git a/api/http/handler/stacks/create_compose_stack.go b/api/http/handler/stacks/create_compose_stack.go index d91b358e6..344eef71f 100644 --- a/api/http/handler/stacks/create_compose_stack.go +++ b/api/http/handler/stacks/create_compose_stack.go @@ -6,8 +6,6 @@ import ( "strconv" "time" - "github.com/asaskevich/govalidator" - "github.com/pkg/errors" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" portainer "github.com/portainer/portainer/api" @@ -16,6 +14,8 @@ import ( "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/stackutils" + "github.com/asaskevich/govalidator" + "github.com/pkg/errors" "github.com/rs/zerolog/log" ) @@ -438,6 +438,7 @@ func (handler *Handler) createComposeDeployConfig(r *http.Request, stack *portai if err != nil { return nil, httperror.InternalServerError("Unable to retrieve registries from the database", err) } + filteredRegistries := security.FilterRegistries(registries, user, securityContext.UserMemberships, endpoint.ID) config := &composeStackDeploymentConfig{ diff --git a/api/http/handler/stacks/create_kubernetes_stack.go b/api/http/handler/stacks/create_kubernetes_stack.go index ae9bc2d38..50429f5e5 100644 --- a/api/http/handler/stacks/create_kubernetes_stack.go +++ b/api/http/handler/stacks/create_kubernetes_stack.go @@ -8,9 +8,8 @@ import ( "strconv" "time" - "github.com/pkg/errors" - "github.com/asaskevich/govalidator" + "github.com/pkg/errors" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" @@ -133,6 +132,7 @@ func (handler *Handler) createKubernetesStackFromFileContent(w http.ResponseWrit } stackFolder := strconv.Itoa(int(stack.ID)) + projectPath, err := handler.FileService.StoreStackFileFromBytes(stackFolder, stack.EntryPoint, []byte(payload.StackFileContent)) if err != nil { fileType := "Manifest" @@ -274,7 +274,7 @@ func (handler *Handler) createKubernetesStackFromGitRepository(w http.ResponseWr err = handler.DataStore.Stack().Create(stack) if err != nil { - return httperror.InternalServerError("Unable to persist the stack inside the database", err) + return httperror.InternalServerError("Unable to persist the Kubernetes stack inside the database", err) } resp := &createKubernetesStackResponse{ diff --git a/api/http/handler/stacks/create_swarm_stack.go b/api/http/handler/stacks/create_swarm_stack.go index 78e5808a8..5c37e2a37 100644 --- a/api/http/handler/stacks/create_swarm_stack.go +++ b/api/http/handler/stacks/create_swarm_stack.go @@ -6,9 +6,9 @@ import ( "strconv" "time" + "github.com/asaskevich/govalidator" "github.com/pkg/errors" - "github.com/asaskevich/govalidator" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" portainer "github.com/portainer/portainer/api" @@ -378,6 +378,7 @@ func (handler *Handler) createSwarmDeployConfig(r *http.Request, stack *portaine if err != nil { return nil, httperror.InternalServerError("Unable to retrieve registries from the database", err) } + filteredRegistries := security.FilterRegistries(registries, user, securityContext.UserMemberships, endpoint.ID) config := &swarmStackDeploymentConfig{ diff --git a/api/http/handler/stacks/stack_delete.go b/api/http/handler/stacks/stack_delete.go index d3bfccafa..8240930ce 100644 --- a/api/http/handler/stacks/stack_delete.go +++ b/api/http/handler/stacks/stack_delete.go @@ -199,7 +199,7 @@ func (handler *Handler) deleteStack(userID portainer.UserID, stack *portainer.St //then process the remove operation if stack.IsComposeFormat { fileNames := append([]string{stack.EntryPoint}, stack.AdditionalFiles...) - tmpDir, err := ioutil.TempDir("", "kub_delete") + tmpDir, err := ioutil.TempDir("", "kube_delete") if err != nil { return errors.Wrap(err, "failed to create temp directory for deleting kub stack") } diff --git a/api/http/handler/teammemberships/handler.go b/api/http/handler/teammemberships/handler.go index e1c4ab998..b8d8c8c59 100644 --- a/api/http/handler/teammemberships/handler.go +++ b/api/http/handler/teammemberships/handler.go @@ -1,12 +1,12 @@ package teammemberships import ( + "net/http" + httperror "github.com/portainer/libhttp/error" "github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/http/security" - "net/http" - "github.com/gorilla/mux" ) diff --git a/api/http/handler/users/user_update.go b/api/http/handler/users/user_update.go index 60d650855..c7aba3c5f 100644 --- a/api/http/handler/users/user_update.go +++ b/api/http/handler/users/user_update.go @@ -5,13 +5,14 @@ import ( "net/http" "time" - "github.com/asaskevich/govalidator" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" + + "github.com/asaskevich/govalidator" ) type userUpdatePayload struct { @@ -122,5 +123,6 @@ func (handler *Handler) userUpdate(w http.ResponseWriter, r *http.Request) *http // remove all of the users persisted API keys handler.apiKeyService.InvalidateUserKeyCache(user.ID) + return response.JSON(w, user) } diff --git a/api/http/handler/users/user_update_password.go b/api/http/handler/users/user_update_password.go index bfca121fb..95c0cd210 100644 --- a/api/http/handler/users/user_update_password.go +++ b/api/http/handler/users/user_update_password.go @@ -5,13 +5,14 @@ import ( "net/http" "time" - "github.com/asaskevich/govalidator" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" + + "github.com/asaskevich/govalidator" ) type userUpdatePasswordPayload struct { diff --git a/api/http/handler/webhooks/handler.go b/api/http/handler/webhooks/handler.go index dcf86a82d..45cacfc73 100644 --- a/api/http/handler/webhooks/handler.go +++ b/api/http/handler/webhooks/handler.go @@ -5,11 +5,12 @@ import ( "github.com/portainer/portainer/api/internal/authorization" "net/http" - "github.com/gorilla/mux" httperror "github.com/portainer/libhttp/error" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/docker" "github.com/portainer/portainer/api/http/security" + + "github.com/gorilla/mux" ) // Handler is the HTTP handler used to handle webhook operations. diff --git a/api/http/handler/webhooks/webhook_create.go b/api/http/handler/webhooks/webhook_create.go index 3fe172525..2d0e66548 100644 --- a/api/http/handler/webhooks/webhook_create.go +++ b/api/http/handler/webhooks/webhook_create.go @@ -7,12 +7,13 @@ import ( "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/registryutils/access" - "github.com/asaskevich/govalidator" - "github.com/gofrs/uuid" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" + + "github.com/asaskevich/govalidator" + "github.com/gofrs/uuid" ) type webhookCreatePayload struct { diff --git a/api/http/handler/websocket/attach.go b/api/http/handler/websocket/attach.go index 0c137d290..b4a5afb48 100644 --- a/api/http/handler/websocket/attach.go +++ b/api/http/handler/websocket/attach.go @@ -6,11 +6,12 @@ import ( "net/http/httputil" "time" - "github.com/asaskevich/govalidator" - "github.com/gorilla/websocket" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" portainer "github.com/portainer/portainer/api" + + "github.com/asaskevich/govalidator" + "github.com/gorilla/websocket" ) // @summary Attach a websocket diff --git a/api/http/handler/websocket/exec.go b/api/http/handler/websocket/exec.go index 5d412d752..fb50403eb 100644 --- a/api/http/handler/websocket/exec.go +++ b/api/http/handler/websocket/exec.go @@ -8,11 +8,12 @@ import ( "net/http/httputil" "time" - "github.com/asaskevich/govalidator" - "github.com/gorilla/websocket" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" portainer "github.com/portainer/portainer/api" + + "github.com/asaskevich/govalidator" + "github.com/gorilla/websocket" ) type execStartOperationPayload struct { diff --git a/api/http/handler/websocket/initdial.go b/api/http/handler/websocket/initdial.go index 27663734a..327753d28 100644 --- a/api/http/handler/websocket/initdial.go +++ b/api/http/handler/websocket/initdial.go @@ -2,10 +2,11 @@ package websocket import ( "crypto/tls" - "github.com/portainer/portainer/api" - "github.com/portainer/portainer/api/crypto" "net" "net/url" + + "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/crypto" ) func initDial(endpoint *portainer.Endpoint) (net.Conn, error) { diff --git a/api/http/handler/websocket/pod.go b/api/http/handler/websocket/pod.go index 153506126..45fb85271 100644 --- a/api/http/handler/websocket/pod.go +++ b/api/http/handler/websocket/pod.go @@ -91,12 +91,14 @@ func (handler *Handler) websocketPodExec(w http.ResponseWriter, r *http.Request) if err != nil { return httperror.InternalServerError("Unable to proxy websocket request to agent", err) } + return nil } else if endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment { err := handler.proxyEdgeAgentWebsocketRequest(w, r, params) if err != nil { return httperror.InternalServerError("Unable to proxy websocket request to Edge agent", err) } + return nil } diff --git a/api/http/handler/websocket/types.go b/api/http/handler/websocket/types.go index b321ea075..614143aa0 100644 --- a/api/http/handler/websocket/types.go +++ b/api/http/handler/websocket/types.go @@ -1,8 +1,6 @@ package websocket -import ( - "github.com/portainer/portainer/api" -) +import "github.com/portainer/portainer/api" type webSocketRequestParams struct { ID string diff --git a/api/http/proxy/factory/azure/containergroup.go b/api/http/proxy/factory/azure/containergroup.go index 44672a50b..d5eeb7a10 100644 --- a/api/http/proxy/factory/azure/containergroup.go +++ b/api/http/proxy/factory/azure/containergroup.go @@ -23,7 +23,7 @@ func (transport *Transport) proxyContainerGroupRequest(request *http.Request) (* } func (transport *Transport) proxyContainerGroupPutRequest(request *http.Request) (*http.Response, error) { - //add a lock before processing existense check + //add a lock before processing existence check transport.mutex.Lock() defer transport.mutex.Unlock() diff --git a/api/http/proxy/factory/docker/portainer.go b/api/http/proxy/factory/docker/portainer.go index b53f2f92b..437d0427f 100644 --- a/api/http/proxy/factory/docker/portainer.go +++ b/api/http/proxy/factory/docker/portainer.go @@ -24,7 +24,6 @@ func (transport *Transport) applyPortainerContainers(resources []interface{}) ([ continue } responseObject, _ = transport.applyPortainerContainer(responseObject) - decoratedResourceData = append(decoratedResourceData, responseObject) } return decoratedResourceData, nil diff --git a/api/http/proxy/factory/kubernetes/edge_transport.go b/api/http/proxy/factory/kubernetes/edge_transport.go index 21eab8349..6d227d589 100644 --- a/api/http/proxy/factory/kubernetes/edge_transport.go +++ b/api/http/proxy/factory/kubernetes/edge_transport.go @@ -18,6 +18,8 @@ type edgeTransport struct { // NewAgentTransport returns a new transport that can be used to send signed requests to a Portainer Edge agent func NewEdgeTransport(dataStore dataservices.DataStore, signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, endpoint *portainer.Endpoint, tokenManager *tokenManager, k8sClientFactory *cli.ClientFactory) *edgeTransport { transport := &edgeTransport{ + reverseTunnelService: reverseTunnelService, + signatureService: signatureService, baseTransport: newBaseTransport( &http.Transport{}, tokenManager, @@ -25,8 +27,6 @@ func NewEdgeTransport(dataStore dataservices.DataStore, signatureService portain k8sClientFactory, dataStore, ), - reverseTunnelService: reverseTunnelService, - signatureService: signatureService, } return transport diff --git a/api/http/proxy/factory/kubernetes/refresh_registry.go b/api/http/proxy/factory/kubernetes/refresh_registry.go index e58f672ae..84881cb10 100644 --- a/api/http/proxy/factory/kubernetes/refresh_registry.go +++ b/api/http/proxy/factory/kubernetes/refresh_registry.go @@ -1,8 +1,9 @@ package kubernetes import ( - "github.com/portainer/portainer/api/internal/registryutils" "net/http" + + "github.com/portainer/portainer/api/internal/registryutils" ) func (transport *baseTransport) refreshRegistry(request *http.Request, namespace string) (err error) { diff --git a/api/http/proxy/factory/kubernetes/token_cache.go b/api/http/proxy/factory/kubernetes/token_cache.go index 316b3a3e9..fd701000d 100644 --- a/api/http/proxy/factory/kubernetes/token_cache.go +++ b/api/http/proxy/factory/kubernetes/token_cache.go @@ -4,7 +4,7 @@ import ( "strconv" "sync" - "github.com/orcaman/concurrent-map" + cmap "github.com/orcaman/concurrent-map" ) type ( diff --git a/api/http/proxy/factory/kubernetes/transport.go b/api/http/proxy/factory/kubernetes/transport.go index 3c1471dd6..86b793eed 100644 --- a/api/http/proxy/factory/kubernetes/transport.go +++ b/api/http/proxy/factory/kubernetes/transport.go @@ -3,7 +3,6 @@ package kubernetes import ( "bytes" "encoding/json" - "errors" "fmt" "io/ioutil" "net/http" @@ -17,6 +16,7 @@ import ( "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/kubernetes/cli" + "github.com/pkg/errors" "github.com/rs/zerolog/log" ) diff --git a/api/http/proxy/factory/utils/json.go b/api/http/proxy/factory/utils/json.go index 23e7bc52f..cab676834 100644 --- a/api/http/proxy/factory/utils/json.go +++ b/api/http/proxy/factory/utils/json.go @@ -87,7 +87,7 @@ func marshal(contentType string, data interface{}) ([]byte, error) { } func unmarshal(contentType string, body []byte, returnBody interface{}) error { - // Note: contentType can look look like: "application/json" or "application/json; charset=utf-8" + // Note: contentType can look like: "application/json" or "application/json; charset=utf-8" mediaType, _, err := mime.ParseMediaType(contentType) if err != nil { return err diff --git a/api/http/proxy/factory/utils/response.go b/api/http/proxy/factory/utils/response.go index a4479b1c0..674529883 100644 --- a/api/http/proxy/factory/utils/response.go +++ b/api/http/proxy/factory/utils/response.go @@ -2,12 +2,12 @@ package utils import ( "bytes" - "errors" "fmt" "io/ioutil" "net/http" "strconv" + "github.com/pkg/errors" "github.com/rs/zerolog/log" ) @@ -18,7 +18,10 @@ func GetResponseAsJSONObject(response *http.Response) (map[string]interface{}, e return nil, err } - responseObject := responseData.(map[string]interface{}) + responseObject, ok := responseData.(map[string]interface{}) + if !ok { + return nil, nil + } return responseObject, nil } @@ -28,6 +31,9 @@ func GetResponseAsJSONArray(response *http.Response) ([]interface{}, error) { if err != nil { return nil, err } + if responseData == nil { + return nil, nil + } switch responseObject := responseData.(type) { case []interface{}: diff --git a/api/http/proxy/manager.go b/api/http/proxy/manager.go index 62591b2de..e077e39c6 100644 --- a/api/http/proxy/manager.go +++ b/api/http/proxy/manager.go @@ -16,7 +16,7 @@ import ( ) type ( - // Manager represents a service used to manage proxies to environments (endpoints). + // Manager represents a service used to manage proxies to environments (endpoints) and extensions. Manager struct { proxyFactory *factory.ProxyFactory endpointProxies cmap.ConcurrentMap diff --git a/api/http/security/authorization.go b/api/http/security/authorization.go index 29aec0408..e1d1a8d5b 100644 --- a/api/http/security/authorization.go +++ b/api/http/security/authorization.go @@ -42,7 +42,7 @@ func AuthorizedResourceControlAccess(resourceControl *portainer.ResourceControl, // AuthorizedResourceControlUpdate ensure that the user can update a resource control object. // A non-administrator user cannot create a resource control where: // * the Public flag is set false -// * the AdministatorsOnly flag is set to true +// * the AdministratorsOnly flag is set to true // * he wants to create a resource control without any user/team accesses // * he wants to add more than one user in the user accesses // * he wants to add a user in the user accesses that is not corresponding to its id diff --git a/api/http/security/bouncer.go b/api/http/security/bouncer.go index 5f6f9bbb6..e9980d6cf 100644 --- a/api/http/security/bouncer.go +++ b/api/http/security/bouncer.go @@ -409,7 +409,7 @@ func (bouncer *RequestBouncer) newRestrictedContextRequest(userID portainer.User }, nil } -// EdgeComputeOperation defines a restriced edge compute operation. +// EdgeComputeOperation defines a restricted edge compute operation. // Use of this operation will only be authorized if edgeCompute is enabled in settings func (bouncer *RequestBouncer) EdgeComputeOperation(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/api/http/security/bouncer_test.go b/api/http/security/bouncer_test.go index 9df12b4b8..23c1170b0 100644 --- a/api/http/security/bouncer_test.go +++ b/api/http/security/bouncer_test.go @@ -12,6 +12,7 @@ import ( "github.com/portainer/portainer/api/datastore" httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/jwt" + "github.com/stretchr/testify/assert" ) diff --git a/api/http/security/filter.go b/api/http/security/filter.go index 518be87bd..549e05e18 100644 --- a/api/http/security/filter.go +++ b/api/http/security/filter.go @@ -74,7 +74,6 @@ func FilterRegistries(registries []portainer.Registry, user *portainer.User, tea } filteredRegistries := []portainer.Registry{} - for _, registry := range registries { if AuthorizedRegistryAccess(®istry, user, teamMemberships, endpointID) { filteredRegistries = append(filteredRegistries, registry) diff --git a/api/internal/edge/edgegroup.go b/api/internal/edge/edgegroup.go index 745f480ce..5120927c2 100644 --- a/api/internal/edge/edgegroup.go +++ b/api/internal/edge/edgegroup.go @@ -1,7 +1,8 @@ package edge import ( - "github.com/portainer/portainer/api" + portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/internal/endpointutils" "github.com/portainer/portainer/api/internal/tag" ) @@ -13,7 +14,7 @@ func EdgeGroupRelatedEndpoints(edgeGroup *portainer.EdgeGroup, endpoints []porta endpointIDs := []portainer.EndpointID{} for _, endpoint := range endpoints { - if endpoint.Type != portainer.EdgeAgentOnDockerEnvironment && endpoint.Type != portainer.EdgeAgentOnKubernetesEnvironment { + if !endpointutils.IsEdgeEndpoint(&endpoint) { continue } diff --git a/api/internal/edge/edgestack.go b/api/internal/edge/edgestack.go index 10633598a..9a0837bc2 100644 --- a/api/internal/edge/edgestack.go +++ b/api/internal/edge/edgestack.go @@ -2,6 +2,7 @@ package edge import ( "errors" + "github.com/portainer/portainer/api" ) diff --git a/api/internal/edge/endpoint.go b/api/internal/edge/endpoint.go index b09fa5cca..492c803a7 100644 --- a/api/internal/edge/endpoint.go +++ b/api/internal/edge/endpoint.go @@ -23,5 +23,4 @@ func EndpointRelatedEdgeStacks(endpoint *portainer.Endpoint, endpointGroup *port } return relatedEdgeStacks - } diff --git a/api/internal/registryutils/auth_header.go b/api/internal/registryutils/auth_header.go index de5d3a999..0676a367d 100644 --- a/api/internal/registryutils/auth_header.go +++ b/api/internal/registryutils/auth_header.go @@ -3,6 +3,7 @@ package registryutils import ( "encoding/base64" "encoding/json" + portainer "github.com/portainer/portainer/api" ) diff --git a/api/internal/snapshot/snapshot.go b/api/internal/snapshot/snapshot.go index 55f8eadec..9a4f6c8d5 100644 --- a/api/internal/snapshot/snapshot.go +++ b/api/internal/snapshot/snapshot.go @@ -213,6 +213,10 @@ func (service *Service) snapshotEndpoints() error { continue } + if endpoint.URL == "" { + continue + } + snapshotError := service.SnapshotEndpoint(&endpoint) latestEndpointReference, err := service.dataStore.Endpoint().Endpoint(endpoint.ID) diff --git a/api/internal/url/url.go b/api/internal/url/url.go index a4f5beb9e..dcae11173 100644 --- a/api/internal/url/url.go +++ b/api/internal/url/url.go @@ -11,7 +11,6 @@ import ( // to prevent an error when url has port but no protocol prefix // we add `//` prefix if needed func ParseURL(endpointURL string) (*url.URL, error) { - if !strings.HasPrefix(endpointURL, "http") && !strings.HasPrefix(endpointURL, "tcp") && !strings.HasPrefix(endpointURL, "//") && diff --git a/api/jwt/jwt_kubeconfig.go b/api/jwt/jwt_kubeconfig.go index 6e099d7f5..7cdd1d6b9 100644 --- a/api/jwt/jwt_kubeconfig.go +++ b/api/jwt/jwt_kubeconfig.go @@ -1,8 +1,9 @@ package jwt import ( - portainer "github.com/portainer/portainer/api" "time" + + portainer "github.com/portainer/portainer/api" ) // GenerateTokenForKubeconfig generates a new JWT token for Kubeconfig diff --git a/api/kubernetes/cli/client.go b/api/kubernetes/cli/client.go index bc3e43c46..23bd59993 100644 --- a/api/kubernetes/cli/client.go +++ b/api/kubernetes/cli/client.go @@ -2,13 +2,12 @@ package cli import ( "fmt" - cmap "github.com/orcaman/concurrent-map" "net/http" "strconv" "sync" + cmap "github.com/orcaman/concurrent-map" "github.com/pkg/errors" - portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/dataservices" "k8s.io/client-go/kubernetes" diff --git a/api/stacks/scheduled.go b/api/stacks/scheduled.go index e924351f5..4712d4a70 100644 --- a/api/stacks/scheduled.go +++ b/api/stacks/scheduled.go @@ -14,6 +14,7 @@ func StartStackSchedules(scheduler *scheduler.Scheduler, stackdeployer StackDepl if err != nil { return errors.Wrap(err, "failed to fetch refreshable stacks") } + for _, stack := range stacks { d, err := time.ParseDuration(stack.AutoUpdate.Interval) if err != nil {