forked from Silverfish/proton-bridge
feat(GODT-2803): Gluon IMAP State access
Update to latest Gluon to allow access to the database for bridge. The cache is placed in a `SharedCache` type to ensure that we respect calls to `Connector.Close`. In theory, this should never trigger an error, but this way we can catch it if it happens. https://github.com/ProtonMail/gluon/pull/391
This commit is contained in:
@ -19,9 +19,11 @@ package bridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/services/userevents"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -128,6 +130,38 @@ func (bridge *Bridge) GetGluonDataDir() (string, error) {
|
||||
}
|
||||
|
||||
func (bridge *Bridge) SetGluonDir(ctx context.Context, newGluonDir string) error {
|
||||
bridge.usersLock.RLock()
|
||||
|
||||
defer func() {
|
||||
logrus.Info("Restarting user event loops")
|
||||
for _, u := range bridge.users {
|
||||
u.ResumeEventLoop()
|
||||
}
|
||||
|
||||
bridge.usersLock.RUnlock()
|
||||
}()
|
||||
|
||||
type waiter struct {
|
||||
w *userevents.EventPollWaiter
|
||||
id string
|
||||
}
|
||||
|
||||
waiters := make([]waiter, 0, len(bridge.users))
|
||||
|
||||
logrus.Info("Pausing user event loops for gluon dir change")
|
||||
for id, u := range bridge.users {
|
||||
waiters = append(waiters, waiter{w: u.PauseEventLoopWithWaiter(), id: id})
|
||||
}
|
||||
|
||||
logrus.Info("Waiting on user event loop completion")
|
||||
for _, waiter := range waiters {
|
||||
if err := waiter.w.WaitPollFinished(ctx); err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to wait on event loop pause for user %v", waiter.id)
|
||||
return fmt.Errorf("failed on event loop pause: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Info("Changing gluon directory")
|
||||
return bridge.serverManager.SetGluonDir(ctx, newGluonDir)
|
||||
}
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ import (
|
||||
"github.com/ProtonMail/go-proton-api"
|
||||
"github.com/ProtonMail/go-proton-api/server"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/v3/internal/events"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -51,6 +52,45 @@ func TestBridge_Settings_GluonDir(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestBridge_Settings_GluonDirWithOnGoingEvents(t *testing.T) {
|
||||
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) {
|
||||
userID, addrID, err := s.CreateUser("imap", password)
|
||||
require.NoError(t, err)
|
||||
|
||||
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
|
||||
syncCh, done := chToType[events.Event, events.SyncFinished](bridge.GetEvents(events.SyncFinished{}))
|
||||
defer done()
|
||||
|
||||
_, err := bridge.LoginFull(context.Background(), "imap", password, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
<-syncCh
|
||||
})
|
||||
|
||||
labelID, err := s.CreateLabel(userID, "folder", "", proton.LabelTypeFolder)
|
||||
require.NoError(t, err)
|
||||
|
||||
withClient(ctx, t, s, "imap", password, func(ctx context.Context, c *proton.Client) {
|
||||
createNumMessages(ctx, t, c, addrID, labelID, 200)
|
||||
})
|
||||
|
||||
withBridgeWaitForServers(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
|
||||
// Create a new location for the Gluon data.
|
||||
newGluonDir := t.TempDir()
|
||||
|
||||
// Move the gluon dir; it should also move the user's data.
|
||||
require.NoError(t, bridge.SetGluonDir(context.Background(), newGluonDir))
|
||||
|
||||
// Check that the new directory is not empty.
|
||||
entries, err := os.ReadDir(newGluonDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// There should be at least one entry.
|
||||
require.NotEmpty(t, entries)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestBridge_Settings_IMAPPort(t *testing.T) {
|
||||
withEnv(t, func(ctx context.Context, s *server.Server, netCtl *proton.NetCtl, locator bridge.Locator, storeKey []byte) {
|
||||
withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
|
||||
|
||||
Reference in New Issue
Block a user