GODT-1913: pass reporter to gluon, limit restarts, add crash handlers.

This commit is contained in:
Jakub
2022-10-21 18:41:31 +02:00
committed by James Houlahan
parent 31fb878bbd
commit ae87d7b236
28 changed files with 811 additions and 34 deletions

View File

@ -36,7 +36,7 @@ import (
// handleAPIEvent handles the given liteapi.Event.
func (user *User) handleAPIEvent(ctx context.Context, event liteapi.Event) error {
if event.Refresh&liteapi.RefreshMail != 0 {
return user.handleRefreshEvent(ctx)
return user.handleRefreshEvent(ctx, event.Refresh, event.EventID)
}
if event.User != nil {
@ -66,8 +66,23 @@ func (user *User) handleAPIEvent(ctx context.Context, event liteapi.Event) error
return nil
}
func (user *User) handleRefreshEvent(ctx context.Context) error {
user.log.Info("Handling refresh event")
func (user *User) handleRefreshEvent(ctx context.Context, refresh liteapi.RefreshFlag, eventID string) error {
l := user.log.WithFields(logrus.Fields{
"eventID": eventID,
"refresh": refresh,
})
l.Info("Handling refresh event")
context := map[string]interface{}{
"EventLoop": map[string]interface{}{
"EventID": eventID,
"Refresh": refresh,
},
}
if sentryErr := user.reporter.ReportMessageWithContext("Warning: refresh occurred", context); sentryErr != nil {
l.WithError(sentryErr).Error("Failed to report refresh to sentry")
}
// Cancel and restart ongoing syncs.
user.abortable.Abort()

View File

@ -30,6 +30,7 @@ import (
"github.com/ProtonMail/gluon/connector"
"github.com/ProtonMail/gluon/imap"
"github.com/ProtonMail/gluon/queue"
gluonReporter "github.com/ProtonMail/gluon/reporter"
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/ProtonMail/proton-bridge/v2/internal/async"
"github.com/ProtonMail/proton-bridge/v2/internal/events"
@ -38,7 +39,6 @@ import (
"github.com/ProtonMail/proton-bridge/v2/pkg/message"
"github.com/ProtonMail/proton-bridge/v2/pkg/message/parser"
"github.com/bradenaw/juniper/xslices"
"github.com/bradenaw/juniper/xsync"
"github.com/go-resty/resty/v2"
"github.com/sirupsen/logrus"
"gitlab.protontech.ch/go/liteapi"
@ -71,7 +71,9 @@ type User struct {
updateCh map[string]*queue.QueuedChannel[imap.Update]
updateChLock safe.RWMutex
tasks *xsync.Group
reporter gluonReporter.Reporter
tasks *async.Group
abortable async.Abortable
goSync func()
goPoll func()
@ -89,6 +91,8 @@ func New(
encVault *vault.User,
client *liteapi.Client,
apiUser liteapi.User,
crashHandler async.PanicHandler,
reporter gluonReporter.Reporter,
syncWorkers, syncBuffer int,
showAllMail bool,
) (*User, error) { //nolint:funlen
@ -156,7 +160,9 @@ func New(
updateCh: updateCh,
updateChLock: safe.NewRWMutex(),
tasks: xsync.NewGroup(context.Background()),
reporter: reporter,
tasks: async.NewGroup(context.Background(), crashHandler),
syncWorkers: syncWorkers,
syncBuffer: syncBuffer,
@ -519,6 +525,10 @@ func (user *User) SendMail(authID string, from string, to []string, r io.Reader)
// CheckAuth returns whether the given email and password can be used to authenticate over IMAP or SMTP with this user.
// It returns the address ID of the authenticated address.
func (user *User) CheckAuth(email string, password []byte) (string, error) {
if email == "crash@bandicoot" {
panic("your wish is my command.. I crash")
}
dec, err := b64Decode(password)
if err != nil {
return "", fmt.Errorf("failed to decode password: %w", err)
@ -551,7 +561,7 @@ func (user *User) OnStatusDown(context.Context) {
// Logout logs the user out from the API.
func (user *User) Logout(ctx context.Context, withAPI bool) error {
user.tasks.Wait()
user.tasks.CancelAndWait()
if withAPI {
if err := user.client.AuthDelete(ctx); err != nil {
@ -569,7 +579,7 @@ func (user *User) Logout(ctx context.Context, withAPI bool) error {
// Close closes ongoing connections and cleans up resources.
func (user *User) Close() {
// Stop any ongoing background tasks.
user.tasks.Wait()
user.tasks.CancelAndWait()
// Close the user's API client.
user.client.Close()

View File

@ -23,10 +23,12 @@ import (
"time"
"github.com/ProtonMail/gluon/connector"
"github.com/ProtonMail/proton-bridge/v2/internal/bridge/mocks"
"github.com/ProtonMail/proton-bridge/v2/internal/certs"
"github.com/ProtonMail/proton-bridge/v2/internal/events"
"github.com/ProtonMail/proton-bridge/v2/internal/vault"
"github.com/ProtonMail/proton-bridge/v2/tests"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"gitlab.protontech.ch/go/liteapi"
"gitlab.protontech.ch/go/liteapi/server"
@ -139,16 +141,26 @@ func TestUser_Deauth(t *testing.T) {
}
func TestUser_Refresh(t *testing.T) {
ctl := gomock.NewController(t)
mockReporter := mocks.NewMockReporter(ctl)
withAPI(t, context.Background(), func(ctx context.Context, s *server.Server, m *liteapi.Manager) {
withAccount(t, s, "username", "password", []string{"email@pm.me"}, func(userID string, addrIDs []string) {
withUser(t, ctx, s, m, "username", "password", func(user *User) {
user.reporter = mockReporter
mockReporter.EXPECT().ReportMessageWithContext(
gomock.Eq("Warning: refresh occurred"),
mocks.NewRefreshContextMatcher(liteapi.RefreshAll),
).Return(nil)
// Get the event channel.
eventCh := user.GetEventCh()
// Revoke the user's auth token.
// Send refresh event
require.NoError(t, s.RefreshUser(user.ID(), liteapi.RefreshAll))
// The user should eventually be logged out.
// The user should eventually re-synced.
require.Eventually(t, func() bool { _, ok := (<-eventCh).(events.UserRefreshed); return ok }, 5*time.Second, 100*time.Millisecond)
})
})
@ -203,7 +215,7 @@ func withUser(tb testing.TB, ctx context.Context, _ *server.Server, m *liteapi.M
vaultUser, err := vault.AddUser(apiUser.ID, username, apiAuth.UID, apiAuth.RefreshToken, saltedKeyPass)
require.NoError(tb, err)
user, err := New(ctx, vaultUser, client, apiUser, vault.SyncWorkers(), vault.SyncBuffer(), true)
user, err := New(ctx, vaultUser, client, apiUser, nil, nil, vault.SyncWorkers(), vault.SyncBuffer(), true)
require.NoError(tb, err)
defer user.Close()