diff --git a/server/channels/app/analytics.go b/server/channels/app/analytics.go index 0086b892db..701b14def8 100644 --- a/server/channels/app/analytics.go +++ b/server/channels/app/analytics.go @@ -19,16 +19,26 @@ const ( ) func (a *App) GetAnalytics(rctx request.CTX, name string, teamID string) (model.AnalyticsRows, *model.AppError) { - skipIntensiveQueries := false - var systemUserCount int64 + return a.getAnalytics(rctx, name, teamID, false) +} + +func (a *App) GetAnalyticsForSupportPacket(rctx request.CTX) (model.AnalyticsRows, *model.AppError) { + return a.getAnalytics(rctx, "standard", "", true) +} + +func (a *App) getAnalytics(rctx request.CTX, name string, teamID string, forSupportPacket bool) (model.AnalyticsRows, *model.AppError) { systemUserCount, err := a.Srv().Store().User().Count(model.UserCountOptions{}) if err != nil { return nil, model.NewAppError("GetAnalytics", "app.user.get_total_users_count.app_error", nil, "", http.StatusInternalServerError).Wrap(err) } - if systemUserCount > int64(*a.Config().AnalyticsSettings.MaxUsersForStatistics) { - rctx.Logger().Debug("More than limit users are on the system, intensive queries skipped", mlog.Int("limit", *a.Config().AnalyticsSettings.MaxUsersForStatistics)) - skipIntensiveQueries = true + skipIntensiveQueries := false + // When generating a Support packet, always run intensive queries. + if !forSupportPacket { + if systemUserCount > int64(*a.Config().AnalyticsSettings.MaxUsersForStatistics) { + rctx.Logger().Debug("More than limit users are on the system, intensive queries skipped", mlog.Int("limit", *a.Config().AnalyticsSettings.MaxUsersForStatistics)) + skipIntensiveQueries = true + } } if name == "standard" { diff --git a/server/channels/app/app_iface.go b/server/channels/app/app_iface.go index 3921b66402..d00d4feaba 100644 --- a/server/channels/app/app_iface.go +++ b/server/channels/app/app_iface.go @@ -628,6 +628,7 @@ type AppIface interface { GetAllTeamsPage(offset int, limit int, opts *model.TeamSearch) ([]*model.Team, *model.AppError) GetAllTeamsPageWithCount(offset int, limit int, opts *model.TeamSearch) (*model.TeamsWithCount, *model.AppError) GetAnalytics(rctx request.CTX, name string, teamID string) (model.AnalyticsRows, *model.AppError) + GetAnalyticsForSupportPacket(rctx request.CTX) (model.AnalyticsRows, *model.AppError) GetAppliedSchemaMigrations() ([]model.AppliedMigration, *model.AppError) GetAudits(rctx request.CTX, userID string, limit int) (model.Audits, *model.AppError) GetAuditsPage(rctx request.CTX, userID string, page int, perPage int) (model.Audits, *model.AppError) diff --git a/server/channels/app/opentracing/opentracing_layer.go b/server/channels/app/opentracing/opentracing_layer.go index 38e91c025a..4149da59d2 100644 --- a/server/channels/app/opentracing/opentracing_layer.go +++ b/server/channels/app/opentracing/opentracing_layer.go @@ -5089,6 +5089,28 @@ func (a *OpenTracingAppLayer) GetAnalytics(rctx request.CTX, name string, teamID return resultVar0, resultVar1 } +func (a *OpenTracingAppLayer) GetAnalyticsForSupportPacket(rctx request.CTX) (model.AnalyticsRows, *model.AppError) { + origCtx := a.ctx + span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetAnalyticsForSupportPacket") + + a.ctx = newCtx + a.app.Srv().Store().SetContext(newCtx) + defer func() { + a.app.Srv().Store().SetContext(origCtx) + a.ctx = origCtx + }() + + defer span.Finish() + resultVar0, resultVar1 := a.app.GetAnalyticsForSupportPacket(rctx) + + if resultVar1 != nil { + span.LogFields(spanlog.Error(resultVar1)) + ext.Error.Set(span, true) + } + + return resultVar0, resultVar1 +} + func (a *OpenTracingAppLayer) GetAppliedSchemaMigrations() ([]model.AppliedMigration, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetAppliedSchemaMigrations") diff --git a/server/channels/app/support_packet.go b/server/channels/app/support_packet.go index f324c68543..dcc8dbab22 100644 --- a/server/channels/app/support_packet.go +++ b/server/channels/app/support_packet.go @@ -70,7 +70,7 @@ func (a *App) GenerateSupportPacket(c request.CTX) []model.FileData { } func (a *App) generateSupportPacketYaml(c request.CTX) (*model.FileData, error) { - var rErr error + var rErr *multierror.Error /* DB */ @@ -143,7 +143,7 @@ func (a *App) generateSupportPacketYaml(c request.CTX) (*model.FileData, error) monthlyActiveUsers int inactiveUserCount int ) - analytics, appErr := a.GetAnalytics(c, "standard", "") + analytics, appErr := a.GetAnalyticsForSupportPacket(c) if appErr != nil { rErr = multierror.Append(errors.Wrap(appErr, "error while getting analytics")) } @@ -257,7 +257,7 @@ func (a *App) generateSupportPacketYaml(c request.CTX) (*model.FileData, error) Filename: "support_packet.yaml", Body: supportPacketYaml, } - return fileData, rErr + return fileData, rErr.ErrorOrNil() } func (a *App) createPluginsFile(_ request.CTX) (*model.FileData, error) { diff --git a/server/channels/app/support_packet_test.go b/server/channels/app/support_packet_test.go index a6520587fd..97fe7b173d 100644 --- a/server/channels/app/support_packet_test.go +++ b/server/channels/app/support_packet_test.go @@ -50,23 +50,91 @@ func TestGenerateSupportPacketYaml(t *testing.T) { license.Features.Users = model.NewInt(licenseUsers) th.App.Srv().SetLicense(license) - t.Run("Happy path", func(t *testing.T) { - // Happy path where we have a support packet yaml file without any warnings + generateSupportPacket := func(t *testing.T) *model.SupportPacket { + t.Helper() fileData, err := th.App.generateSupportPacketYaml(th.Context) require.NotNil(t, fileData) assert.Equal(t, "support_packet.yaml", fileData.Filename) assert.Positive(t, len(fileData.Body)) assert.NoError(t, err) + var packet model.SupportPacket require.NoError(t, yaml.Unmarshal(fileData.Body, &packet)) + require.NotNil(t, packet) + return &packet + } - assert.Equal(t, 3, packet.ActiveUsers) // from InitBasic. - assert.Equal(t, licenseUsers, packet.LicenseSupportedUsers) - assert.Equal(t, false, packet.LicenseIsTrial) + t.Run("Happy path", func(t *testing.T) { + // Happy path where we have a support packet yaml file without any warnings + packet := generateSupportPacket(t) + + /* Build information */ + assert.NotEmpty(t, packet.ServerOS) + assert.NotEmpty(t, packet.ServerArchitecture) + assert.Equal(t, model.CurrentVersion, packet.ServerVersion) + // BuildHash is not present in tests + + /* DB */ + assert.NotEmpty(t, packet.DatabaseType) + assert.NotEmpty(t, packet.DatabaseVersion) + assert.NotEmpty(t, packet.DatabaseSchemaVersion) + assert.Zero(t, packet.WebsocketConnections) + assert.NotZero(t, packet.MasterDbConnections) + assert.Zero(t, packet.ReplicaDbConnections) + + /* Cluster */ assert.Empty(t, packet.ClusterID) + + /* File store */ assert.Equal(t, "local", packet.FileDriver) assert.Equal(t, "OK", packet.FileStatus) + + /* LDAP */ + assert.Empty(t, packet.LdapVendorName) + assert.Empty(t, packet.LdapVendorVersion) + + /* Elastic Search */ + assert.Empty(t, packet.ElasticServerVersion) + assert.Empty(t, packet.ElasticServerPlugins) + + /* License */ + assert.Equal(t, "My awesome Company", packet.LicenseTo) + assert.Equal(t, licenseUsers, packet.LicenseSupportedUsers) + assert.Equal(t, false, packet.LicenseIsTrial) + + /* Server stats */ + assert.Equal(t, 3, packet.ActiveUsers) // from InitBasic() + assert.Equal(t, 0, packet.DailyActiveUsers) + assert.Equal(t, 0, packet.MonthlyActiveUsers) + assert.Equal(t, 0, packet.InactiveUserCount) + assert.Equal(t, 5, packet.TotalPosts) // from InitBasic() + assert.Equal(t, 3, packet.TotalChannels) // from InitBasic() + assert.Equal(t, 1, packet.TotalTeams) // from InitBasic() + + /* Jobs */ + assert.Empty(t, packet.DataRetentionJobs) + assert.Empty(t, packet.MessageExportJobs) + assert.Empty(t, packet.ElasticPostIndexingJobs) + assert.Empty(t, packet.ElasticPostAggregationJobs) + assert.Empty(t, packet.BlevePostIndexingJobs) + assert.Empty(t, packet.LdapSyncJobs) + assert.Empty(t, packet.MigrationJobs) + }) + + t.Run("post count should be present if number of users extends AnalyticsSettings.MaxUsersForStatistics", func(t *testing.T) { + th.App.UpdateConfig(func(cfg *model.Config) { + cfg.AnalyticsSettings.MaxUsersForStatistics = model.NewInt(1) + }) + + for i := 0; i < 5; i++ { + p := th.CreatePost(th.BasicChannel) + require.NotNil(t, p) + } + + // InitBasic() already creats 5 posts + packet := generateSupportPacket(t) + assert.Equal(t, 10, packet.TotalPosts) }) t.Run("filestore fails", func(t *testing.T) { @@ -75,13 +143,7 @@ func TestGenerateSupportPacketYaml(t *testing.T) { fb.On("DriverName").Return("mock") fb.On("TestConnection").Return(errors.New("all broken")) - fileData, err := th.App.generateSupportPacketYaml(th.Context) - require.NotNil(t, fileData) - assert.Equal(t, "support_packet.yaml", fileData.Filename) - assert.Positive(t, len(fileData.Body)) - assert.NoError(t, err) - var packet model.SupportPacket - require.NoError(t, yaml.Unmarshal(fileData.Body, &packet)) + packet := generateSupportPacket(t) assert.Equal(t, "mock", packet.FileDriver) assert.Equal(t, "FAIL: all broken", packet.FileStatus) diff --git a/server/public/model/license.go b/server/public/model/license.go index b4a08d8578..f31b26b21b 100644 --- a/server/public/model/license.go +++ b/server/public/model/license.go @@ -398,8 +398,13 @@ func (l *License) HasSharedChannels() bool { func NewTestLicense(features ...string) *License { ret := &License{ ExpiresAt: GetMillis() + 90*DayInMilliseconds, - Customer: &Customer{}, - Features: &Features{}, + Customer: &Customer{ + Id: "some ID", + Email: "admin@example.com", + Name: "Main Contact Person", + Company: "My awesome Company", + }, + Features: &Features{}, } ret.Features.SetDefaults()