[MM-71036] Health Dashboard PR01b: pointer-ize Support Packet diagnostics fields that can fail (#38888)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
authored and GitHub committed 2026-10-06 23:27:04 +02:00
1 parent 20ef077c5d
commit c9c447677b
9 files changed
+242 -132

No files matched your search

@@ -78,7 +78,8 @@ func TestBuildHealthSnapshotOnlyLeaderHasDiagnostics(t *testing.T) {
if node.IsLeader {
assert.Equal(t, "id-2", node.ClusterInfo.Id)
require.NotNil(t, node.Diagnostics)
assert.Equal(t, 3, node.Diagnostics.Diagnostics.Cluster.NumberOfNodes)
require.NotNil(t, node.Diagnostics.Diagnostics.Cluster.NumberOfNodes)
assert.Equal(t, 3, *node.Diagnostics.Diagnostics.Cluster.NumberOfNodes)
} else {
assert.Nil(t, node.Diagnostics, "follower %q", node.ClusterInfo.Id)
}
+34 -22
View File
@@ -190,8 +190,9 @@ func (ps *PlatformService) GetSupportPacketDiagnostics(rctx request.CTX) (*model
totalMemoryBytes, err := getTotalMemory()
if err != nil {
fail(model.SectionServerHost, errors.Wrap(err, "error while getting total memory"))
} else {
d.Server.TotalMemoryMB = new(totalMemoryBytes / 1024 / 1024)
}
d.Server.TotalMemoryMB = totalMemoryBytes / 1024 / 1024
containerLimits, err := getContainerLimits()
if err != nil {
rctx.Logger().Debug("Failed to get container limits for Support Packet", mlog.Err(err))
@@ -199,9 +200,11 @@ func (ps *PlatformService) GetSupportPacketDiagnostics(rctx request.CTX) (*model
d.Server.ContainerCPULimit = containerLimits.CPULimit
d.Server.ContainerMemoryLimitMB = containerLimits.MemoryLimitMB
}
d.Server.Hostname, err = os.Hostname()
serverHostname, err := os.Hostname()
if err != nil {
fail(model.SectionServerHost, errors.Wrap(err, "error while getting hostname"))
} else {
d.Server.Hostname = new(serverHostname)
}
d.Server.ProcessID = os.Getpid()
d.Server.StartedAt = ps.startTime.UTC()
@@ -219,29 +222,36 @@ func (ps *PlatformService) GetSupportPacketDiagnostics(rctx request.CTX) (*model
installationType = unknownDataPoint
}
d.Server.InstallationType = installationType
d.Server.OpenFileDescriptors, err = getOpenFileDescriptors()
openFileDescriptors, err := getOpenFileDescriptors()
if err != nil {
fail(model.SectionServerFDs, errors.Wrap(err, "error while getting open file descriptor count"))
} else {
d.Server.OpenFileDescriptors = new(openFileDescriptors)
}
d.Server.MaxFileDescriptors, err = getMaxFileDescriptors()
maxFileDescriptors, err := getMaxFileDescriptors()
if err != nil {
fail(model.SectionServerFDs, errors.Wrap(err, "error while getting max file descriptor limit"))
} else {
d.Server.MaxFileDescriptors = new(maxFileDescriptors)
}
/* Config */
d.Config.Source = ps.DescribeConfig()
/* DB */
d.Database.Type, d.Database.SchemaVersion, err = ps.DatabaseTypeAndSchemaVersion()
databaseType, schemaVersion, err := ps.DatabaseTypeAndSchemaVersion()
if err != nil {
fail(model.SectionDatabaseIdentity, errors.Wrap(err, "error while getting DB type and schema version"))
} else {
d.Database.Type = new(databaseType)
d.Database.SchemaVersion = new(schemaVersion)
}
databaseVersion, err := ps.Store.GetDbVersion(false)
if err != nil {
fail(model.SectionDatabaseIdentity, errors.Wrap(err, "error while getting DB version"))
} else {
d.Database.Version = databaseVersion
d.Database.Version = new(databaseVersion)
}
d.Database.MasterConnections = ps.Store.TotalMasterDbConnections()
d.Database.ReplicaConnections = ps.Store.TotalReadDbConnections()
@@ -269,9 +279,9 @@ func (ps *PlatformService) GetSupportPacketDiagnostics(rctx request.CTX) (*model
if diskErr != nil {
fail(model.SectionFilestoreDisk, errors.Wrap(diskErr, "error while getting disk space info"))
} else {
d.FileStore.FilesystemType = di.FilesystemType
d.FileStore.TotalMB = di.TotalMB
d.FileStore.AvailableMB = di.AvailableMB
d.FileStore.FilesystemType = new(di.FilesystemType)
d.FileStore.TotalMB = new(di.TotalMB)
d.FileStore.AvailableMB = new(di.AvailableMB)
}
}
@@ -286,8 +296,10 @@ func (ps *PlatformService) GetSupportPacketDiagnostics(rctx request.CTX) (*model
if e != nil {
fail(model.SectionCluster, errors.Wrap(e, "error while getting cluster infos"))
} else {
d.Cluster.NumberOfNodes = max(len(clusterInfo), 1) // clusterInfo is empty if the node is the only one in the cluster
d.Cluster.NumberOfNodes = new(max(len(clusterInfo), 1)) // clusterInfo is empty if the node is the only one in the cluster
}
} else {
d.Cluster.NumberOfNodes = new(1)
}
/* LDAP */
@@ -416,18 +428,18 @@ func (ps *PlatformService) applyStoreDiagnostics(rctx request.CTX, diagnostics *
return nil
}
diagnostics.Database.MasterConnectionsInUse = storeDiagnostics.MasterConnectionsInUse
diagnostics.Database.MasterConnectionsIdle = storeDiagnostics.MasterConnectionsIdle
diagnostics.Database.MasterPoolWaitCount = storeDiagnostics.MasterPoolWaitCount
diagnostics.Database.MasterPoolWaitDurationMs = storeDiagnostics.MasterPoolWaitDurationMs
diagnostics.Database.MasterConnectionsClosedMaxIdle = storeDiagnostics.MasterConnectionsClosedMaxIdle
diagnostics.Database.MasterConnectionsClosedMaxLifetime = storeDiagnostics.MasterConnectionsClosedMaxLifetime
diagnostics.Database.ReplicaConnectionsInUse = storeDiagnostics.ReplicaConnectionsInUse
diagnostics.Database.ReplicaConnectionsIdle = storeDiagnostics.ReplicaConnectionsIdle
diagnostics.Database.ReplicaPoolWaitCount = storeDiagnostics.ReplicaPoolWaitCount
diagnostics.Database.ReplicaPoolWaitDurationMs = storeDiagnostics.ReplicaPoolWaitDurationMs
diagnostics.Database.ReplicaConnectionsClosedMaxIdle = storeDiagnostics.ReplicaConnectionsClosedMaxIdle
diagnostics.Database.ReplicaConnectionsClosedMaxLifetime = storeDiagnostics.ReplicaConnectionsClosedMaxLifetime
diagnostics.Database.MasterConnectionsInUse = new(storeDiagnostics.MasterConnectionsInUse)
diagnostics.Database.MasterConnectionsIdle = new(storeDiagnostics.MasterConnectionsIdle)
diagnostics.Database.MasterPoolWaitCount = new(storeDiagnostics.MasterPoolWaitCount)
diagnostics.Database.MasterPoolWaitDurationMs = new(storeDiagnostics.MasterPoolWaitDurationMs)
diagnostics.Database.MasterConnectionsClosedMaxIdle = new(storeDiagnostics.MasterConnectionsClosedMaxIdle)
diagnostics.Database.MasterConnectionsClosedMaxLifetime = new(storeDiagnostics.MasterConnectionsClosedMaxLifetime)
diagnostics.Database.ReplicaConnectionsInUse = new(storeDiagnostics.ReplicaConnectionsInUse)
diagnostics.Database.ReplicaConnectionsIdle = new(storeDiagnostics.ReplicaConnectionsIdle)
diagnostics.Database.ReplicaPoolWaitCount = new(storeDiagnostics.ReplicaPoolWaitCount)
diagnostics.Database.ReplicaPoolWaitDurationMs = new(storeDiagnostics.ReplicaPoolWaitDurationMs)
diagnostics.Database.ReplicaConnectionsClosedMaxIdle = new(storeDiagnostics.ReplicaConnectionsClosedMaxIdle)
diagnostics.Database.ReplicaConnectionsClosedMaxLifetime = new(storeDiagnostics.ReplicaConnectionsClosedMaxLifetime)
diagnostics.Database.CacheHitRatio = storeDiagnostics.CacheHitRatio
diagnostics.Database.Deadlocks = storeDiagnostics.Deadlocks
diagnostics.Database.TempFiles = storeDiagnostics.TempFiles
@@ -285,15 +285,23 @@ func TestGetSupportPacketDiagnostics(t *testing.T) {
/* Server information */
assert.NotEmpty(t, d.Server.OS)
assert.NotEmpty(t, d.Server.Architecture)
assert.NotEmpty(t, d.Server.Hostname)
require.NotNil(t, d.Server.Hostname)
assert.NotEmpty(t, *d.Server.Hostname)
assert.Equal(t, model.CurrentVersion, d.Server.Version)
// BuildHash is not present in tests
assert.NotEmpty(t, d.Server.GoVersion)
assert.Equal(t, "docker", d.Server.InstallationType)
assert.Positive(t, d.Server.CPUCores)
assert.Positive(t, d.Server.TotalMemoryMB)
assert.True(t, d.Server.OpenFileDescriptors == -1 || d.Server.OpenFileDescriptors > 0, "OpenFileDescriptors should be -1 (unsupported) or positive, got %d", d.Server.OpenFileDescriptors)
assert.True(t, d.Server.MaxFileDescriptors == -1 || d.Server.MaxFileDescriptors > 0, "MaxFileDescriptors should be -1 (unsupported) or positive, got %d", d.Server.MaxFileDescriptors)
if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
require.NotNil(t, d.Server.TotalMemoryMB)
assert.Positive(t, *d.Server.TotalMemoryMB)
} else {
assert.Nil(t, d.Server.TotalMemoryMB)
}
require.NotNil(t, d.Server.OpenFileDescriptors)
require.NotNil(t, d.Server.MaxFileDescriptors)
assert.True(t, *d.Server.OpenFileDescriptors == -1 || *d.Server.OpenFileDescriptors > 0, "OpenFileDescriptors should be -1 (unsupported) or positive, got %d", *d.Server.OpenFileDescriptors)
assert.True(t, *d.Server.MaxFileDescriptors == -1 || *d.Server.MaxFileDescriptors > 0, "MaxFileDescriptors should be -1 (unsupported) or positive, got %d", *d.Server.MaxFileDescriptors)
assert.Positive(t, d.Server.ProcessID)
assert.False(t, d.Server.StartedAt.IsZero())
if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
@@ -307,37 +315,50 @@ func TestGetSupportPacketDiagnostics(t *testing.T) {
assert.Equal(t, "memory://", d.Config.Source)
/* DB */
assert.NotEmpty(t, d.Database.Type)
assert.NotEmpty(t, d.Database.Version)
assert.NotEmpty(t, d.Database.SchemaVersion)
require.NotNil(t, d.Database.Type)
assert.NotEmpty(t, *d.Database.Type)
require.NotNil(t, d.Database.Version)
assert.NotEmpty(t, *d.Database.Version)
require.NotNil(t, d.Database.SchemaVersion)
assert.NotEmpty(t, *d.Database.SchemaVersion)
assert.NotZero(t, d.Database.MasterConnections)
assert.Zero(t, d.Database.ReplicaConnections)
assert.Zero(t, d.Database.SearchConnections)
assert.GreaterOrEqual(t, d.Database.MasterConnectionsInUse, 0)
assert.GreaterOrEqual(t, d.Database.MasterConnectionsIdle, 0)
assert.GreaterOrEqual(t, d.Database.MasterPoolWaitCount, int64(0))
assert.GreaterOrEqual(t, d.Database.MasterPoolWaitDurationMs, int64(0))
assert.GreaterOrEqual(t, d.Database.MasterConnectionsClosedMaxIdle, int64(0))
assert.GreaterOrEqual(t, d.Database.MasterConnectionsClosedMaxLifetime, int64(0))
assert.GreaterOrEqual(t, d.Database.ReplicaConnectionsInUse, 0)
assert.GreaterOrEqual(t, d.Database.ReplicaConnectionsIdle, 0)
assert.GreaterOrEqual(t, d.Database.ReplicaPoolWaitCount, int64(0))
assert.GreaterOrEqual(t, d.Database.ReplicaPoolWaitDurationMs, int64(0))
assert.GreaterOrEqual(t, d.Database.ReplicaConnectionsClosedMaxIdle, int64(0))
assert.GreaterOrEqual(t, d.Database.ReplicaConnectionsClosedMaxLifetime, int64(0))
require.NotNil(t, d.Database.MasterConnectionsInUse)
assert.GreaterOrEqual(t, *d.Database.MasterConnectionsInUse, 0)
for _, v := range []*int{d.Database.MasterConnectionsIdle, d.Database.ReplicaConnectionsInUse, d.Database.ReplicaConnectionsIdle} {
require.NotNil(t, v)
assert.GreaterOrEqual(t, *v, 0)
}
for _, v := range []*int64{
d.Database.MasterPoolWaitCount,
d.Database.MasterPoolWaitDurationMs,
d.Database.MasterConnectionsClosedMaxIdle,
d.Database.MasterConnectionsClosedMaxLifetime,
d.Database.ReplicaPoolWaitCount,
d.Database.ReplicaPoolWaitDurationMs,
d.Database.ReplicaConnectionsClosedMaxIdle,
d.Database.ReplicaConnectionsClosedMaxLifetime,
} {
require.NotNil(t, v)
assert.GreaterOrEqual(t, *v, int64(0))
}
/* File store */
assert.Equal(t, "OK", d.FileStore.Status)
assert.Empty(t, d.FileStore.Error)
assert.Equal(t, "local", d.FileStore.Driver)
if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
assert.NotEmpty(t, d.FileStore.FilesystemType, "FilesystemType should not be empty on supported platforms")
assert.Positive(t, d.FileStore.TotalMB, "TotalMB should be positive on supported platforms")
assert.Positive(t, d.FileStore.AvailableMB, "AvailableMB should be positive on supported platforms")
require.NotNil(t, d.FileStore.FilesystemType)
require.NotNil(t, d.FileStore.TotalMB)
require.NotNil(t, d.FileStore.AvailableMB)
assert.NotEmpty(t, *d.FileStore.FilesystemType, "FilesystemType should not be empty on supported platforms")
assert.Positive(t, *d.FileStore.TotalMB, "TotalMB should be positive on supported platforms")
assert.Positive(t, *d.FileStore.AvailableMB, "AvailableMB should be positive on supported platforms")
} else {
assert.Empty(t, d.FileStore.FilesystemType)
assert.Zero(t, d.FileStore.TotalMB)
assert.Zero(t, d.FileStore.AvailableMB)
assert.Nil(t, d.FileStore.FilesystemType)
assert.Nil(t, d.FileStore.TotalMB)
assert.Nil(t, d.FileStore.AvailableMB)
}
/* Websockets */
@@ -345,7 +366,8 @@ func TestGetSupportPacketDiagnostics(t *testing.T) {
/* Cluster */
assert.Empty(t, d.Cluster.ID)
assert.Zero(t, d.Cluster.NumberOfNodes)
require.NotNil(t, d.Cluster.NumberOfNodes)
assert.Equal(t, 1, *d.Cluster.NumberOfNodes)
assert.False(t, d.Cluster.IsLeader)
/* LDAP */
@@ -439,9 +461,9 @@ func TestGetSupportPacketDiagnostics(t *testing.T) {
assert.Equal(t, "OK", packet.FileStore.Status)
assert.Equal(t, "amazons3", packet.FileStore.Driver)
assert.Empty(t, packet.FileStore.FilesystemType)
assert.Zero(t, packet.FileStore.TotalMB)
assert.Zero(t, packet.FileStore.AvailableMB)
assert.Nil(t, packet.FileStore.FilesystemType)
assert.Nil(t, packet.FileStore.TotalMB)
assert.Nil(t, packet.FileStore.AvailableMB)
})
t.Run("no LDAP info if LDAP sync is disabled", func(t *testing.T) {
@@ -984,18 +1006,19 @@ func TestGetSupportPacketDiagnostics(t *testing.T) {
})
packet := getDiagnostics(t)
assert.Equal(t, 3, packet.Database.MasterConnectionsInUse)
assert.Equal(t, 7, packet.Database.MasterConnectionsIdle)
assert.Equal(t, int64(11), packet.Database.MasterPoolWaitCount)
assert.Equal(t, int64(2025), packet.Database.MasterPoolWaitDurationMs)
assert.Equal(t, int64(13), packet.Database.MasterConnectionsClosedMaxIdle)
assert.Equal(t, int64(17), packet.Database.MasterConnectionsClosedMaxLifetime)
assert.Equal(t, 5, packet.Database.ReplicaConnectionsInUse)
assert.Equal(t, 9, packet.Database.ReplicaConnectionsIdle)
assert.Equal(t, int64(19), packet.Database.ReplicaPoolWaitCount)
assert.Equal(t, int64(4090), packet.Database.ReplicaPoolWaitDurationMs)
assert.Equal(t, int64(23), packet.Database.ReplicaConnectionsClosedMaxIdle)
assert.Equal(t, int64(29), packet.Database.ReplicaConnectionsClosedMaxLifetime)
require.NotNil(t, packet.Database.MasterConnectionsInUse)
assert.Equal(t, 3, *packet.Database.MasterConnectionsInUse)
assert.Equal(t, new(7), packet.Database.MasterConnectionsIdle)
assert.Equal(t, new(int64(11)), packet.Database.MasterPoolWaitCount)
assert.Equal(t, new(int64(2025)), packet.Database.MasterPoolWaitDurationMs)
assert.Equal(t, new(int64(13)), packet.Database.MasterConnectionsClosedMaxIdle)
assert.Equal(t, new(int64(17)), packet.Database.MasterConnectionsClosedMaxLifetime)
assert.Equal(t, new(5), packet.Database.ReplicaConnectionsInUse)
assert.Equal(t, new(9), packet.Database.ReplicaConnectionsIdle)
assert.Equal(t, new(int64(19)), packet.Database.ReplicaPoolWaitCount)
assert.Equal(t, new(int64(4090)), packet.Database.ReplicaPoolWaitDurationMs)
assert.Equal(t, new(int64(23)), packet.Database.ReplicaConnectionsClosedMaxIdle)
assert.Equal(t, new(int64(29)), packet.Database.ReplicaConnectionsClosedMaxLifetime)
})
t.Run("OpenID disabled", func(t *testing.T) {
@@ -1176,7 +1199,7 @@ func TestGetSupportPacketDiagnosticsSectionErrors(t *testing.T) {
})
}
requireSectionErrors := func(t *testing.T, failed map[model.NodeSection][]string) {
requireSectionErrors := func(t *testing.T, failed map[model.NodeSection][]string) *model.SupportPacketDiagnostics {
t.Helper()
nodeDiagnostics, err := th.Service.GetSupportPacketDiagnostics(th.Context)
@@ -1204,6 +1227,7 @@ func TestGetSupportPacketDiagnosticsSectionErrors(t *testing.T) {
assert.ErrorContains(t, err, msg)
}
}
return nodeDiagnostics.Diagnostics
}
t.Run("all sections present and clean", func(t *testing.T) {
@@ -1213,17 +1237,20 @@ func TestGetSupportPacketDiagnosticsSectionErrors(t *testing.T) {
t.Run("DB schema version fails", func(t *testing.T) {
setStore(t, &failingDiagnosticsStore{schemaVersionErr: errors.New("schema down")})
requireSectionErrors(t, map[model.NodeSection][]string{
d := requireSectionErrors(t, map[model.NodeSection][]string{
model.SectionDatabaseIdentity: {"error while getting DB type and schema version"},
})
assert.Nil(t, d.Database.Type)
assert.Nil(t, d.Database.SchemaVersion)
})
t.Run("DB version fails", func(t *testing.T) {
setStore(t, &failingDiagnosticsStore{dbVersionErr: errors.New("version down")})
requireSectionErrors(t, map[model.NodeSection][]string{
d := requireSectionErrors(t, map[model.NodeSection][]string{
model.SectionDatabaseIdentity: {"error while getting DB version"},
})
assert.Nil(t, d.Database.Version)
})
t.Run("both DB identity sites fail", func(t *testing.T) {
@@ -1240,9 +1267,21 @@ func TestGetSupportPacketDiagnosticsSectionErrors(t *testing.T) {
t.Run("store diagnostics fail", func(t *testing.T) {
setStore(t, &failingDiagnosticsStore{diagnosticsErr: errors.New("stats down")})
requireSectionErrors(t, map[model.NodeSection][]string{
d := requireSectionErrors(t, map[model.NodeSection][]string{
model.SectionDatabaseStats: {"error while collecting support packet database diagnostics"},
})
assert.Nil(t, d.Database.MasterConnectionsInUse)
assert.Nil(t, d.Database.MasterConnectionsIdle)
assert.Nil(t, d.Database.MasterPoolWaitCount)
assert.Nil(t, d.Database.MasterPoolWaitDurationMs)
assert.Nil(t, d.Database.MasterConnectionsClosedMaxIdle)
assert.Nil(t, d.Database.MasterConnectionsClosedMaxLifetime)
assert.Nil(t, d.Database.ReplicaConnectionsInUse)
assert.Nil(t, d.Database.ReplicaConnectionsIdle)
assert.Nil(t, d.Database.ReplicaPoolWaitCount)
assert.Nil(t, d.Database.ReplicaPoolWaitDurationMs)
assert.Nil(t, d.Database.ReplicaConnectionsClosedMaxIdle)
assert.Nil(t, d.Database.ReplicaConnectionsClosedMaxLifetime)
})
t.Run("disk space fails", func(t *testing.T) {
@@ -1265,9 +1304,12 @@ func TestGetSupportPacketDiagnosticsSectionErrors(t *testing.T) {
cfg.FileSettings.Directory = model.NewPointer(filepath.Join(t.TempDir(), "missing"))
})
requireSectionErrors(t, map[model.NodeSection][]string{
d := requireSectionErrors(t, map[model.NodeSection][]string{
model.SectionFilestoreDisk: {"error while getting disk space info"},
})
assert.Nil(t, d.FileStore.FilesystemType)
assert.Nil(t, d.FileStore.TotalMB)
assert.Nil(t, d.FileStore.AvailableMB)
})
t.Run("cluster infos fail", func(t *testing.T) {
@@ -1283,9 +1325,10 @@ func TestGetSupportPacketDiagnosticsSectionErrors(t *testing.T) {
})
th.Service.clusterIFace = cluster
requireSectionErrors(t, map[model.NodeSection][]string{
d := requireSectionErrors(t, map[model.NodeSection][]string{
model.SectionCluster: {"error while getting cluster infos"},
})
assert.Nil(t, d.Cluster.NumberOfNodes)
})
t.Run("LDAP vendor info fails", func(t *testing.T) {
@@ -1523,39 +1566,39 @@ func TestSupportPacketMarshalGolden(t *testing.T) {
diagnostics.License.IsNonProduction = true
diagnostics.Server.OS = "linux"
diagnostics.Server.Architecture = "amd64"
diagnostics.Server.Hostname = "mm-host"
diagnostics.Server.Hostname = new("mm-host")
diagnostics.Server.InstallationType = "docker"
diagnostics.Server.CPUCores = 8
diagnostics.Server.TotalMemoryMB = 32768
diagnostics.Server.TotalMemoryMB = new(uint64(32768))
diagnostics.Server.ContainerCPULimit = 6.5
diagnostics.Server.ContainerMemoryLimitMB = 16384
diagnostics.Server.ProcessID = 90210
diagnostics.Server.StartedAt = time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC)
diagnostics.Server.HostStartedAt = time.Date(2025, 12, 30, 0, 0, 0, 0, time.UTC)
diagnostics.Server.OpenFileDescriptors = 512
diagnostics.Server.MaxFileDescriptors = 8192
diagnostics.Server.OpenFileDescriptors = new(int64(512))
diagnostics.Server.MaxFileDescriptors = new(int64(8192))
diagnostics.Server.Version = "11.0.0"
diagnostics.Server.BuildHash = "abc123"
diagnostics.Server.GoVersion = "go1.26"
diagnostics.Config.Source = "memory://"
diagnostics.Database.Type = "postgres"
diagnostics.Database.Version = "16.4"
diagnostics.Database.SchemaVersion = "123"
diagnostics.Database.Type = new("postgres")
diagnostics.Database.Version = new("16.4")
diagnostics.Database.SchemaVersion = new("123")
diagnostics.Database.MasterConnections = 40
diagnostics.Database.ReplicaConnections = 20
diagnostics.Database.SearchConnections = 10
diagnostics.Database.MasterConnectionsInUse = 5
diagnostics.Database.MasterConnectionsIdle = 35
diagnostics.Database.MasterPoolWaitCount = 100
diagnostics.Database.MasterPoolWaitDurationMs = 220
diagnostics.Database.MasterConnectionsClosedMaxIdle = 2
diagnostics.Database.MasterConnectionsClosedMaxLifetime = 1
diagnostics.Database.ReplicaConnectionsInUse = 3
diagnostics.Database.ReplicaConnectionsIdle = 17
diagnostics.Database.ReplicaPoolWaitCount = 12
diagnostics.Database.ReplicaPoolWaitDurationMs = 66
diagnostics.Database.ReplicaConnectionsClosedMaxIdle = 4
diagnostics.Database.ReplicaConnectionsClosedMaxLifetime = 3
diagnostics.Database.MasterConnectionsInUse = new(5)
diagnostics.Database.MasterConnectionsIdle = new(35)
diagnostics.Database.MasterPoolWaitCount = new(int64(100))
diagnostics.Database.MasterPoolWaitDurationMs = new(int64(220))
diagnostics.Database.MasterConnectionsClosedMaxIdle = new(int64(2))
diagnostics.Database.MasterConnectionsClosedMaxLifetime = new(int64(1))
diagnostics.Database.ReplicaConnectionsInUse = new(3)
diagnostics.Database.ReplicaConnectionsIdle = new(17)
diagnostics.Database.ReplicaPoolWaitCount = new(int64(12))
diagnostics.Database.ReplicaPoolWaitDurationMs = new(int64(66))
diagnostics.Database.ReplicaConnectionsClosedMaxIdle = new(int64(4))
diagnostics.Database.ReplicaConnectionsClosedMaxLifetime = new(int64(3))
diagnostics.Database.CacheHitRatio = &cacheHitRatio
diagnostics.Database.Deadlocks = &deadlocks
diagnostics.Database.TempFiles = &tempFiles
@@ -1568,12 +1611,12 @@ func TestSupportPacketMarshalGolden(t *testing.T) {
diagnostics.Database.PostsLastAutovacuum = &postsLastAutovacuum
diagnostics.FileStore.Status = model.StatusOk
diagnostics.FileStore.Driver = model.ImageDriverLocal
diagnostics.FileStore.FilesystemType = "ext4"
diagnostics.FileStore.TotalMB = 204800
diagnostics.FileStore.AvailableMB = 102400
diagnostics.FileStore.FilesystemType = new("ext4")
diagnostics.FileStore.TotalMB = new(uint64(204800))
diagnostics.FileStore.AvailableMB = new(uint64(102400))
diagnostics.Websocket.Connections = 77
diagnostics.Cluster.ID = "cluster-id"
diagnostics.Cluster.NumberOfNodes = 3
diagnostics.Cluster.NumberOfNodes = new(3)
diagnostics.Notifications.Email.Status = model.StatusOk
diagnostics.Notifications.Push.Status = model.StatusFail
diagnostics.Notifications.Push.Error = "proxy timeout"
+2 -2
View File
@@ -35,8 +35,8 @@ func (n *NodeSnapshot) SchemaVersion() (string, bool) {
if n != nil && n.ClusterInfo != nil && n.ClusterInfo.SchemaVersion != "" {
return n.ClusterInfo.SchemaVersion, true
}
if diag, ok := n.Diag(); ok && diag.Database.SchemaVersion != "" && n.sectionOK(model.SectionDatabaseIdentity) {
return diag.Database.SchemaVersion, true
if diag, ok := n.Diag(); ok && model.SafeDereference(diag.Database.SchemaVersion) != "" && n.sectionOK(model.SectionDatabaseIdentity) {
return *diag.Database.SchemaVersion, true
}
return "", false
@@ -51,7 +51,7 @@ func TestSchemaVersion(t *testing.T) {
Diagnostics: &model.SupportPacketDiagnostics{},
},
}
node.Diagnostics.Diagnostics.Database.SchemaVersion = "diag"
node.Diagnostics.Diagnostics.Database.SchemaVersion = new("diag")
version, ok := node.SchemaVersion()
require.True(t, ok)
@@ -65,7 +65,7 @@ func TestSchemaVersion(t *testing.T) {
Errors: model.SectionErrors{model.SectionDatabaseIdentity: nil},
},
}
node.Diagnostics.Diagnostics.Database.SchemaVersion = "diag"
node.Diagnostics.Diagnostics.Database.SchemaVersion = new("diag")
version, ok := node.SchemaVersion()
require.True(t, ok)
@@ -79,7 +79,32 @@ func TestSchemaVersion(t *testing.T) {
Errors: model.SectionErrors{model.SectionDatabaseIdentity: errors.New("collect failed")},
},
}
node.Diagnostics.Diagnostics.Database.SchemaVersion = "diag"
node.Diagnostics.Diagnostics.Database.SchemaVersion = new("diag")
_, ok := node.SchemaVersion()
require.False(t, ok)
})
t.Run("nil schema version in diagnostics", func(t *testing.T) {
node := &NodeSnapshot{
Diagnostics: &model.NodeDiagnostics{
Diagnostics: &model.SupportPacketDiagnostics{},
Errors: model.SectionErrors{model.SectionDatabaseIdentity: nil},
},
}
_, ok := node.SchemaVersion()
require.False(t, ok)
})
t.Run("empty schema version from an older packet", func(t *testing.T) {
node := &NodeSnapshot{
Diagnostics: &model.NodeDiagnostics{
Diagnostics: &model.SupportPacketDiagnostics{},
Errors: model.SectionErrors{model.SectionDatabaseIdentity: nil},
},
}
node.Diagnostics.Diagnostics.Database.SchemaVersion = new("")
_, ok := node.SchemaVersion()
require.False(t, ok)
@@ -179,7 +179,7 @@ func readNodes(z *budgetedZip, root []string, byNode map[string][]string) ([]*he
if len(byNode) == 0 && slices.Contains(root, model.SupportPacketDiagnosticsFileName) {
node := read("", model.SupportPacketDiagnosticsFileName)
if diag, ok := node.Diag(); ok {
node.Hostname = diag.Server.Hostname
node.Hostname = model.SafeDereference(diag.Server.Hostname)
}
nodes = append(nodes, node)
}
+4 -4
View File
@@ -35,8 +35,8 @@ func TestSectionErrorsThreeStates(t *testing.T) {
func TestSectionErrorsFailClosedWithinSection(t *testing.T) {
diagnostics := &SupportPacketDiagnostics{}
diagnostics.Server.OpenFileDescriptors = 5000
diagnostics.Server.MaxFileDescriptors = 0
diagnostics.Server.OpenFileDescriptors = new(int64(5000))
diagnostics.Server.MaxFileDescriptors = new(int64(0))
node := NodeDiagnostics{
Diagnostics: diagnostics,
@@ -48,8 +48,8 @@ func TestSectionErrorsFailClosedWithinSection(t *testing.T) {
_, ok := node.Errors[SectionServerFDs]
require.True(t, ok)
require.EqualError(t, node.Errors[SectionServerFDs], "max file descriptors read failed")
assert.EqualValues(t, 5000, node.Diagnostics.Server.OpenFileDescriptors)
assert.EqualValues(t, 0, node.Diagnostics.Server.MaxFileDescriptors)
assert.EqualValues(t, 5000, *node.Diagnostics.Server.OpenFileDescriptors)
assert.EqualValues(t, 0, *node.Diagnostics.Server.MaxFileDescriptors)
}
func TestNodeAndWorkspaceSectionsDoNotOverlap(t *testing.T) {
+30 -29
View File
@@ -16,6 +16,7 @@ const (
SupportPacketPluginsFileName = "plugins.json"
)
// SupportPacketDiagnostics reports one node's diagnostics. A nil pointer field was not collected, which is not the same as zero.
type SupportPacketDiagnostics struct {
Version int `yaml:"version"`
@@ -31,14 +32,14 @@ type SupportPacketDiagnostics struct {
Server struct {
// Machine
OS string `yaml:"os"`
Architecture string `yaml:"architecture"`
Hostname string `yaml:"hostname"`
InstallationType string `yaml:"installation_type"`
OS string `yaml:"os"`
Architecture string `yaml:"architecture"`
Hostname *string `yaml:"hostname,omitempty"`
InstallationType string `yaml:"installation_type"`
// Capacity
CPUCores int `yaml:"cpu_cores"`
TotalMemoryMB uint64 `yaml:"total_memory_mb"`
TotalMemoryMB *uint64 `yaml:"total_memory_mb,omitempty"`
ContainerCPULimit float64 `yaml:"container_cpu_limit,omitempty"`
ContainerMemoryLimitMB uint64 `yaml:"container_memory_limit_mb,omitempty"`
@@ -46,8 +47,8 @@ type SupportPacketDiagnostics struct {
ProcessID int `yaml:"process_id"`
StartedAt time.Time `yaml:"started_at"`
HostStartedAt time.Time `yaml:"host_started_at,omitempty"`
OpenFileDescriptors int64 `yaml:"open_file_descriptors"`
MaxFileDescriptors int64 `yaml:"max_file_descriptors"`
OpenFileDescriptors *int64 `yaml:"open_file_descriptors,omitempty"`
MaxFileDescriptors *int64 `yaml:"max_file_descriptors,omitempty"`
// Software
Version string `yaml:"version"`
@@ -60,24 +61,24 @@ type SupportPacketDiagnostics struct {
} `yaml:"config"`
Database struct {
Type string `yaml:"type"`
Version string `yaml:"version"`
SchemaVersion string `yaml:"schema_version"`
Type *string `yaml:"type,omitempty"`
Version *string `yaml:"version,omitempty"`
SchemaVersion *string `yaml:"schema_version,omitempty"`
MasterConnections int `yaml:"master_connections"`
ReplicaConnections int `yaml:"replica_connections"`
SearchConnections int `yaml:"search_connections"`
MasterConnectionsInUse int `yaml:"master_connections_in_use"`
MasterConnectionsIdle int `yaml:"master_connections_idle"`
MasterPoolWaitCount int64 `yaml:"master_pool_wait_count"`
MasterPoolWaitDurationMs int64 `yaml:"master_pool_wait_duration_ms"`
MasterConnectionsClosedMaxIdle int64 `yaml:"master_connections_closed_max_idle"`
MasterConnectionsClosedMaxLifetime int64 `yaml:"master_connections_closed_max_lifetime"`
ReplicaConnectionsInUse int `yaml:"replica_connections_in_use"`
ReplicaConnectionsIdle int `yaml:"replica_connections_idle"`
ReplicaPoolWaitCount int64 `yaml:"replica_pool_wait_count"`
ReplicaPoolWaitDurationMs int64 `yaml:"replica_pool_wait_duration_ms"`
ReplicaConnectionsClosedMaxIdle int64 `yaml:"replica_connections_closed_max_idle"`
ReplicaConnectionsClosedMaxLifetime int64 `yaml:"replica_connections_closed_max_lifetime"`
MasterConnectionsInUse *int `yaml:"master_connections_in_use,omitempty"`
MasterConnectionsIdle *int `yaml:"master_connections_idle,omitempty"`
MasterPoolWaitCount *int64 `yaml:"master_pool_wait_count,omitempty"`
MasterPoolWaitDurationMs *int64 `yaml:"master_pool_wait_duration_ms,omitempty"`
MasterConnectionsClosedMaxIdle *int64 `yaml:"master_connections_closed_max_idle,omitempty"`
MasterConnectionsClosedMaxLifetime *int64 `yaml:"master_connections_closed_max_lifetime,omitempty"`
ReplicaConnectionsInUse *int `yaml:"replica_connections_in_use,omitempty"`
ReplicaConnectionsIdle *int `yaml:"replica_connections_idle,omitempty"`
ReplicaPoolWaitCount *int64 `yaml:"replica_pool_wait_count,omitempty"`
ReplicaPoolWaitDurationMs *int64 `yaml:"replica_pool_wait_duration_ms,omitempty"`
ReplicaConnectionsClosedMaxIdle *int64 `yaml:"replica_connections_closed_max_idle,omitempty"`
ReplicaConnectionsClosedMaxLifetime *int64 `yaml:"replica_connections_closed_max_lifetime,omitempty"`
CacheHitRatio *float64 `yaml:"cache_hit_ratio,omitempty"`
Deadlocks *int64 `yaml:"deadlocks,omitempty"`
TempFiles *int64 `yaml:"temp_files,omitempty"`
@@ -91,12 +92,12 @@ type SupportPacketDiagnostics struct {
} `yaml:"database"`
FileStore struct {
Status string `yaml:"file_status"`
Error string `yaml:"error,omitempty"`
Driver string `yaml:"file_driver"`
FilesystemType string `yaml:"filesystem_type,omitempty"`
TotalMB uint64 `yaml:"total_mb,omitempty"`
AvailableMB uint64 `yaml:"available_mb,omitempty"`
Status string `yaml:"file_status"`
Error string `yaml:"error,omitempty"`
Driver string `yaml:"file_driver"`
FilesystemType *string `yaml:"filesystem_type,omitempty"`
TotalMB *uint64 `yaml:"total_mb,omitempty"`
AvailableMB *uint64 `yaml:"available_mb,omitempty"`
} `yaml:"file_store"`
Websocket struct {
@@ -105,7 +106,7 @@ type SupportPacketDiagnostics struct {
Cluster struct {
ID string `yaml:"id"`
NumberOfNodes int `yaml:"number_of_nodes"`
NumberOfNodes *int `yaml:"number_of_nodes,omitempty"`
IsLeader bool `yaml:"is_leader,omitempty"`
} `yaml:"cluster"`
@@ -70,3 +70,31 @@ outgoing_webhooks: 14
assert.Equal(t, expected, string(data))
}
func TestSupportPacketDiagnosticsYAMLOmitsNilAndKeepsZero(t *testing.T) {
t.Parallel()
var diagnostics SupportPacketDiagnostics
diagnostics.Server.OpenFileDescriptors = new(int64(0))
diagnostics.Database.MasterConnectionsInUse = new(0)
diagnostics.FileStore.AvailableMB = new(uint64(0))
data, err := yaml.Marshal(&diagnostics)
require.NoError(t, err)
body := string(data)
assert.Contains(t, body, "open_file_descriptors: 0\n")
assert.Contains(t, body, "master_connections_in_use: 0\n")
assert.Contains(t, body, "available_mb: 0\n")
assert.NotContains(t, body, "max_file_descriptors")
assert.NotContains(t, body, "schema_version")
var roundTrip SupportPacketDiagnostics
err = yaml.Unmarshal(data, &roundTrip)
require.NoError(t, err)
assert.Nil(t, roundTrip.Server.MaxFileDescriptors)
assert.Nil(t, roundTrip.Database.SchemaVersion)
assert.Equal(t, new(int64(0)), roundTrip.Server.OpenFileDescriptors)
assert.Equal(t, new(0), roundTrip.Database.MasterConnectionsInUse)
assert.Equal(t, new(uint64(0)), roundTrip.FileStore.AvailableMB)
}