[MM-54318] Add file storage information to support package (#24474)

* Reorder stats in support package struct

* Add file storage information to support package

* Add clusterID

* Change method name to DriverName

* Ordering

* Fix test
Этот коммит содержится в:
Ben Schumacher
2023-09-06 09:21:25 +02:00
коммит произвёл GitHub
родитель 4482ba0fd0
Коммит b2f36c7cdf
7 изменённых файлов: 182 добавлений и 61 удалений

Просмотреть файл

@@ -59,7 +59,36 @@ func (a *App) GenerateSupportPacket() []model.FileData {
func (a *App) generateSupportPacketYaml() (*model.FileData, error) { func (a *App) generateSupportPacketYaml() (*model.FileData, error) {
var rErr error var rErr error
// Here we are getting information regarding Elastic Search /* DB */
databaseType, databaseSchemaVersion := a.Srv().DatabaseTypeAndSchemaVersion()
databaseVersion, _ := a.Srv().Store().GetDbVersion(false)
/* Cluster */
var clusterID string
if a.Cluster() != nil {
clusterID = a.Cluster().GetClusterId()
}
/* File store */
fileDriver := a.Srv().Platform().FileBackend().DriverName()
fileStatus := model.StatusOk
err := a.Srv().Platform().FileBackend().TestConnection()
if err != nil {
fileStatus = model.StatusFail + ": " + err.Error()
}
/* LDAP */
var vendorName, vendorVersion string
if ldapInterface := a.ch.Ldap; a.ch.Ldap != nil {
vendorName, vendorVersion = ldapInterface.GetVendorNameAndVendorVersion()
}
/* Elastic Search */
var elasticServerVersion string var elasticServerVersion string
var elasticServerPlugins []string var elasticServerPlugins []string
if a.Srv().Platform().SearchEngine.ElasticsearchEngine != nil { if a.Srv().Platform().SearchEngine.ElasticsearchEngine != nil {
@@ -67,16 +96,16 @@ func (a *App) generateSupportPacketYaml() (*model.FileData, error) {
elasticServerPlugins = a.Srv().Platform().SearchEngine.ElasticsearchEngine.GetPlugins() elasticServerPlugins = a.Srv().Platform().SearchEngine.ElasticsearchEngine.GetPlugins()
} }
// Here we are getting information regarding LDAP /* License */
ldapInterface := a.ch.Ldap
var vendorName, vendorVersion string licenseTo := ""
if ldapInterface != nil { supportedUsers := 0
vendorName, vendorVersion = ldapInterface.GetVendorNameAndVendorVersion() if license := a.Srv().License(); license != nil {
supportedUsers = *license.Features.Users
licenseTo = license.Customer.Company
} }
// Here we are getting information regarding the database (mysql/postgres + current schema version) /* Jobs */
databaseType, databaseSchemaVersion := a.Srv().DatabaseTypeAndSchemaVersion()
databaseVersion, _ := a.Srv().Store().GetDbVersion(false)
uniqueUserCount, err := a.Srv().Store().User().Count(model.UserCountOptions{}) uniqueUserCount, err := a.Srv().Store().User().Count(model.UserCountOptions{})
if err != nil { if err != nil {
@@ -112,31 +141,42 @@ func (a *App) generateSupportPacketYaml() (*model.FileData, error) {
rErr = multierror.Append(errors.Wrap(err, "error while getting migration jobs")) rErr = multierror.Append(errors.Wrap(err, "error while getting migration jobs"))
} }
licenseTo := ""
supportedUsers := 0
if license := a.Srv().License(); license != nil {
supportedUsers = *license.Features.Users
licenseTo = license.Customer.Company
}
// Creating the struct for support packet yaml file // Creating the struct for support packet yaml file
supportPacket := model.SupportPacket{ supportPacket := model.SupportPacket{
LicenseTo: licenseTo, /* Build information */
ServerOS: runtime.GOOS, ServerOS: runtime.GOOS,
ServerArchitecture: runtime.GOARCH, ServerArchitecture: runtime.GOARCH,
ServerVersion: model.CurrentVersion, ServerVersion: model.CurrentVersion,
BuildHash: model.BuildHash, BuildHash: model.BuildHash,
/* DB */
DatabaseType: databaseType, DatabaseType: databaseType,
DatabaseVersion: databaseVersion, DatabaseVersion: databaseVersion,
DatabaseSchemaVersion: databaseSchemaVersion, DatabaseSchemaVersion: databaseSchemaVersion,
LdapVendorName: vendorName,
LdapVendorVersion: vendorVersion, /* Cluster */
ElasticServerVersion: elasticServerVersion, ClusterID: clusterID,
ElasticServerPlugins: elasticServerPlugins,
ActiveUsers: int(uniqueUserCount), /* File store */
FileDriver: fileDriver,
FileStatus: fileStatus,
/* LDAP */
LdapVendorName: vendorName,
LdapVendorVersion: vendorVersion,
/* Elastic Search */
ElasticServerVersion: elasticServerVersion,
ElasticServerPlugins: elasticServerPlugins,
/* License */
LicenseTo: licenseTo,
LicenseSupportedUsers: supportedUsers, LicenseSupportedUsers: supportedUsers,
// Jobs /* Server stats */
ActiveUsers: int(uniqueUserCount),
/* Jobs */
DataRetentionJobs: dataRetentionJobs, DataRetentionJobs: dataRetentionJobs,
MessageExportJobs: messageExportJobs, MessageExportJobs: messageExportJobs,
ElasticPostIndexingJobs: elasticPostIndexingJobs, ElasticPostIndexingJobs: elasticPostIndexingJobs,
@@ -146,6 +186,8 @@ func (a *App) generateSupportPacketYaml() (*model.FileData, error) {
MigrationJobs: migrationJobs, MigrationJobs: migrationJobs,
} }
/* Server stats */
analytics, appErr := a.GetAnalytics("standard", "") analytics, appErr := a.GetAnalytics("standard", "")
if appErr != nil { if appErr != nil {
rErr = multierror.Append(errors.Wrap(appErr, "error while getting analytics")) rErr = multierror.Append(errors.Wrap(appErr, "error while getting analytics"))

Просмотреть файл

@@ -4,6 +4,7 @@
package app package app
import ( import (
"errors"
"os" "os"
"testing" "testing"
@@ -12,6 +13,8 @@ import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/channels/app/platform"
fmocks "github.com/mattermost/mattermost/server/v8/platform/shared/filestore/mocks"
) )
func TestCreatePluginsFile(t *testing.T) { func TestCreatePluginsFile(t *testing.T) {
@@ -45,16 +48,42 @@ func TestGenerateSupportPacketYaml(t *testing.T) {
license.Features.Users = model.NewInt(licenseUsers) license.Features.Users = model.NewInt(licenseUsers)
th.App.Srv().SetLicense(license) th.App.Srv().SetLicense(license)
// Happy path where we have a support packet yaml file without any warnings t.Run("Happy path", func(t *testing.T) {
fileData, err := th.App.generateSupportPacketYaml() // Happy path where we have a support packet yaml file without any warnings
require.NotNil(t, fileData)
assert.Equal(t, "support_packet.yaml", fileData.Filename) fileData, err := th.App.generateSupportPacketYaml()
assert.Positive(t, len(fileData.Body)) require.NotNil(t, fileData)
assert.NoError(t, err) assert.Equal(t, "support_packet.yaml", fileData.Filename)
var packet model.SupportPacket assert.Positive(t, len(fileData.Body))
require.NoError(t, yaml.Unmarshal(fileData.Body, &packet)) assert.NoError(t, err)
assert.Equal(t, 3, packet.ActiveUsers) // from InitBasic. var packet model.SupportPacket
assert.Equal(t, licenseUsers, packet.LicenseSupportedUsers) require.NoError(t, yaml.Unmarshal(fileData.Body, &packet))
assert.Equal(t, 3, packet.ActiveUsers) // from InitBasic.
assert.Equal(t, licenseUsers, packet.LicenseSupportedUsers)
assert.Empty(t, packet.ClusterID)
assert.Equal(t, "local", packet.FileDriver)
assert.Equal(t, "OK", packet.FileStatus)
})
t.Run("filestore fails", func(t *testing.T) {
fb := &fmocks.FileBackend{}
platform.SetFileStore(fb)(th.Server.Platform())
fb.On("DriverName").Return("mock")
fb.On("TestConnection").Return(errors.New("all broken"))
fileData, err := th.App.generateSupportPacketYaml()
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))
assert.Equal(t, "mock", packet.FileDriver)
assert.Equal(t, "FAIL: all broken", packet.FileStatus)
})
} }
func TestGenerateSupportPacket(t *testing.T) { func TestGenerateSupportPacket(t *testing.T) {

Просмотреть файл

@@ -23,6 +23,7 @@ type ReadCloseSeeker interface {
} }
type FileBackend interface { type FileBackend interface {
DriverName() string
TestConnection() error TestConnection() error
Reader(path string) (ReadCloseSeeker, error) Reader(path string) (ReadCloseSeeker, error)

Просмотреть файл

@@ -68,6 +68,10 @@ func copyFile(src, dst string) (err error) {
return return
} }
func (b *LocalFileBackend) DriverName() string {
return driverLocal
}
func (b *LocalFileBackend) TestConnection() error { func (b *LocalFileBackend) TestConnection() error {
f := bytes.NewReader([]byte("testingwrite")) f := bytes.NewReader([]byte("testingwrite"))
if _, err := writeFileLocally(f, filepath.Join(b.directory, TestFilePath)); err != nil { if _, err := writeFileLocally(f, filepath.Join(b.directory, TestFilePath)); err != nil {

Просмотреть файл

@@ -57,6 +57,20 @@ func (_m *FileBackend) CopyFile(oldPath string, newPath string) error {
return r0 return r0
} }
// DriverName provides a mock function with given fields:
func (_m *FileBackend) DriverName() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// FileExists provides a mock function with given fields: path // FileExists provides a mock function with given fields: path
func (_m *FileBackend) FileExists(path string) (bool, error) { func (_m *FileBackend) FileExists(path string) (bool, error) {
ret := _m.Called(path) ret := _m.Called(path)

Просмотреть файл

@@ -184,6 +184,10 @@ func (b *S3FileBackend) s3New(isCloud bool) (*s3.Client, error) {
return s3Clnt, nil return s3Clnt, nil
} }
func (b *S3FileBackend) DriverName() string {
return driverS3
}
func (b *S3FileBackend) TestConnection() error { func (b *S3FileBackend) TestConnection() error {
exists := true exists := true
var err error var err error

Просмотреть файл

@@ -78,31 +78,58 @@ type ServerBusyState struct {
} }
type SupportPacket struct { type SupportPacket struct {
LicenseTo string `yaml:"license_to"` /* Build information */
ServerOS string `yaml:"server_os"`
ServerArchitecture string `yaml:"server_architecture"` ServerOS string `yaml:"server_os"`
ServerVersion string `yaml:"server_version"` ServerArchitecture string `yaml:"server_architecture"`
BuildHash string `yaml:"build_hash,omitempty"` ServerVersion string `yaml:"server_version"`
DatabaseType string `yaml:"database_type"` BuildHash string `yaml:"build_hash,omitempty"`
DatabaseVersion string `yaml:"database_version"`
DatabaseSchemaVersion string `yaml:"database_schema_version"` /* DB */
LdapVendorName string `yaml:"ldap_vendor_name,omitempty"`
LdapVendorVersion string `yaml:"ldap_vendor_version,omitempty"` DatabaseType string `yaml:"database_type"`
ElasticServerVersion string `yaml:"elastic_server_version,omitempty"` DatabaseVersion string `yaml:"database_version"`
ElasticServerPlugins []string `yaml:"elastic_server_plugins,omitempty"` DatabaseSchemaVersion string `yaml:"database_schema_version"`
ActiveUsers int `yaml:"active_users"` WebsocketConnections int `yaml:"websocket_connections"`
LicenseSupportedUsers int `yaml:"license_supported_users,omitempty"` MasterDbConnections int `yaml:"master_db_connections"`
TotalChannels int `yaml:"total_channels"` ReplicaDbConnections int `yaml:"read_db_connections"`
TotalPosts int `yaml:"total_posts"`
TotalTeams int `yaml:"total_teams"` /* Cluster */
DailyActiveUsers int `yaml:"daily_active_users"`
MonthlyActiveUsers int `yaml:"monthly_active_users"` ClusterID string `yaml:"cluster_id"`
WebsocketConnections int `yaml:"websocket_connections"`
MasterDbConnections int `yaml:"master_db_connections"` /* File store */
ReplicaDbConnections int `yaml:"read_db_connections"`
InactiveUserCount int `yaml:"inactive_user_count"` FileDriver string `yaml:"file_driver"`
FileStatus string `yaml:"file_status"`
/* LDAP */
LdapVendorName string `yaml:"ldap_vendor_name,omitempty"`
LdapVendorVersion string `yaml:"ldap_vendor_version,omitempty"`
/* Elastic Search */
ElasticServerVersion string `yaml:"elastic_server_version,omitempty"`
ElasticServerPlugins []string `yaml:"elastic_server_plugins,omitempty"`
/* License */
LicenseTo string `yaml:"license_to"`
LicenseSupportedUsers int `yaml:"license_supported_users,omitempty"`
/* Server stats */
ActiveUsers int `yaml:"active_users"`
DailyActiveUsers int `yaml:"daily_active_users"`
MonthlyActiveUsers int `yaml:"monthly_active_users"`
InactiveUserCount int `yaml:"inactive_user_count"`
TotalPosts int `yaml:"total_posts"`
TotalChannels int `yaml:"total_channels"`
TotalTeams int `yaml:"total_teams"`
/* Jobs */
// Jobs
DataRetentionJobs []*Job `yaml:"data_retention_jobs"` DataRetentionJobs []*Job `yaml:"data_retention_jobs"`
MessageExportJobs []*Job `yaml:"message_export_jobs"` MessageExportJobs []*Job `yaml:"message_export_jobs"`
ElasticPostIndexingJobs []*Job `yaml:"elastic_post_indexing_jobs"` ElasticPostIndexingJobs []*Job `yaml:"elastic_post_indexing_jobs"`