Add integrity check command to CLI (#11599)
* Add integrity command * Add structures and implementation for basic referential integrity check * Use a channel to receive integrity check reports as they generates * Setup unit testing * Add confirm prompt to integrity command and make verbose output optional * Add more integrity checks * Use wrapper functions to simplify behaviour and tests * Improve extensibility of IntegrityCheckResult * Improve CheckIntegrity tests performance * Use a config structure for relational integrity checks * Add more relational integrity checks * Add more checks and do some cleanup * Add more relational integrity checks with proper tests * Fix tests to use sync functions * Add more info to integrity command help * Add more relational integrity checks * Add more relational integrity checks * Add missing checks * Show more information about missing records * Fix to use new sync function * Change integrity check functions to accept a SqlSupplier * Fix code duplication * Use squirrel for query building
Этот коммит содержится в:
коммит произвёл
Jesse Hallam
родитель
d1f0216c22
Коммит
e1eb839636
78
cmd/mattermost/commands/integrity.go
Обычный файл
78
cmd/mattermost/commands/integrity.go
Обычный файл
@@ -0,0 +1,78 @@
|
|||||||
|
// Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/store"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var IntegrityCmd = &cobra.Command{
|
||||||
|
Use: "integrity",
|
||||||
|
Short: "Check database data integrity",
|
||||||
|
RunE: integrityCmdF,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
IntegrityCmd.Flags().Bool("confirm", false, "Confirm you really want to run a complete integrity check that may temporarily harm system performance")
|
||||||
|
IntegrityCmd.Flags().BoolP("verbose", "v", false, "Show detailed information on integrity check results")
|
||||||
|
RootCmd.AddCommand(IntegrityCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printRelationalIntegrityCheckResult(data store.RelationalIntegrityCheckData, verbose bool) {
|
||||||
|
fmt.Println(fmt.Sprintf("Found %d records in relation %s orphans of relation %s",
|
||||||
|
len(data.Records), data.ChildName, data.ParentName))
|
||||||
|
if !verbose {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, record := range data.Records {
|
||||||
|
if record.ChildId != "" {
|
||||||
|
fmt.Println(fmt.Sprintf(" Child %s (%s.%s) is missing Parent %s (%s.%s)", record.ChildId, data.ChildName, data.ChildIdAttr, record.ParentId, data.ChildName, data.ParentIdAttr))
|
||||||
|
} else {
|
||||||
|
fmt.Println(fmt.Sprintf(" Child is missing Parent %s (%s.%s)", record.ParentId, data.ChildName, data.ParentIdAttr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printIntegrityCheckResult(result store.IntegrityCheckResult, verbose bool) {
|
||||||
|
switch data := result.Data.(type) {
|
||||||
|
case store.RelationalIntegrityCheckData:
|
||||||
|
printRelationalIntegrityCheckResult(data, verbose)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func integrityCmdF(command *cobra.Command, args []string) error {
|
||||||
|
a, err := InitDBCommandContextCobra(command)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer a.Shutdown()
|
||||||
|
|
||||||
|
confirmFlag, _ := command.Flags().GetBool("confirm")
|
||||||
|
if !confirmFlag {
|
||||||
|
var confirm string
|
||||||
|
fmt.Fprintf(os.Stdout, "This check may harm performance on live systems. Are you sure you want to proceed? (y/N): ")
|
||||||
|
fmt.Scanln(&confirm)
|
||||||
|
if !strings.EqualFold(confirm, "y") && !strings.EqualFold(confirm, "yes") {
|
||||||
|
fmt.Fprintf(os.Stderr, "Aborted.\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verboseFlag, _ := command.Flags().GetBool("verbose")
|
||||||
|
results := a.Srv.Store.CheckIntegrity()
|
||||||
|
for result := range results {
|
||||||
|
if result.Err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", result.Err.Error())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
printIntegrityCheckResult(result, verboseFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -217,6 +217,10 @@ func (s *LayeredStore) TotalSearchDbConnections() int {
|
|||||||
return s.DatabaseLayer.TotalSearchDbConnections()
|
return s.DatabaseLayer.TotalSearchDbConnections()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *LayeredStore) CheckIntegrity() <-chan IntegrityCheckResult {
|
||||||
|
return s.DatabaseLayer.CheckIntegrity()
|
||||||
|
}
|
||||||
|
|
||||||
type LayeredRoleStore struct {
|
type LayeredRoleStore struct {
|
||||||
*LayeredStore
|
*LayeredStore
|
||||||
}
|
}
|
||||||
|
|||||||
514
store/sqlstore/integrity.go
Обычный файл
514
store/sqlstore/integrity.go
Обычный файл
@@ -0,0 +1,514 @@
|
|||||||
|
// Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package sqlstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/mattermost-server/mlog"
|
||||||
|
"github.com/mattermost/mattermost-server/store"
|
||||||
|
|
||||||
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
)
|
||||||
|
|
||||||
|
type relationalCheckConfig struct {
|
||||||
|
parentName string
|
||||||
|
parentIdAttr string
|
||||||
|
childName string
|
||||||
|
childIdAttr string
|
||||||
|
canParentIdBeEmpty bool
|
||||||
|
sortRecords bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOrphanedRecords(ss *SqlSupplier, cfg relationalCheckConfig) ([]store.OrphanedRecord, error) {
|
||||||
|
var records []store.OrphanedRecord
|
||||||
|
|
||||||
|
sub := ss.getQueryBuilder().
|
||||||
|
Select("TRUE").
|
||||||
|
From(cfg.parentName).
|
||||||
|
Prefix("NOT EXISTS (").
|
||||||
|
Suffix(")").
|
||||||
|
Where(sq.Eq{"id": cfg.childName + "." + cfg.parentIdAttr})
|
||||||
|
|
||||||
|
main := ss.getQueryBuilder().
|
||||||
|
Select().
|
||||||
|
Column(cfg.parentIdAttr + " AS ParentId").
|
||||||
|
From(cfg.childName).
|
||||||
|
Where(sub)
|
||||||
|
|
||||||
|
if cfg.childIdAttr != "" {
|
||||||
|
main = main.Column(cfg.childIdAttr + " AS ChildId")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.canParentIdBeEmpty {
|
||||||
|
main = main.Where(sq.NotEq{cfg.parentIdAttr: ""})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.sortRecords {
|
||||||
|
main = main.OrderBy(cfg.parentIdAttr)
|
||||||
|
}
|
||||||
|
|
||||||
|
query, args, _ := main.ToSql()
|
||||||
|
_, err := ss.GetMaster().Select(&records, query, args...)
|
||||||
|
|
||||||
|
return records, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkParentChildIntegrity(ss *SqlSupplier, config relationalCheckConfig) store.IntegrityCheckResult {
|
||||||
|
var result store.IntegrityCheckResult
|
||||||
|
var data store.RelationalIntegrityCheckData
|
||||||
|
|
||||||
|
config.sortRecords = true
|
||||||
|
data.Records, result.Err = getOrphanedRecords(ss, config)
|
||||||
|
if result.Err != nil {
|
||||||
|
mlog.Error(result.Err.Error())
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
data.ParentName = config.parentName
|
||||||
|
data.ChildName = config.childName
|
||||||
|
data.ParentIdAttr = config.parentIdAttr
|
||||||
|
data.ChildIdAttr = config.childIdAttr
|
||||||
|
result.Data = data
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkChannelsCommandWebhooksIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Channels",
|
||||||
|
parentIdAttr: "ChannelId",
|
||||||
|
childName: "CommandWebhooks",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkChannelsChannelMemberHistoryIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Channels",
|
||||||
|
parentIdAttr: "ChannelId",
|
||||||
|
childName: "ChannelMemberHistory",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkChannelsChannelMembersIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Channels",
|
||||||
|
parentIdAttr: "ChannelId",
|
||||||
|
childName: "ChannelMembers",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkChannelsIncomingWebhooksIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Channels",
|
||||||
|
parentIdAttr: "ChannelId",
|
||||||
|
childName: "IncomingWebhooks",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkChannelsOutgoingWebhooksIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Channels",
|
||||||
|
parentIdAttr: "ChannelId",
|
||||||
|
childName: "OutgoingWebhooks",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkChannelsPostsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Channels",
|
||||||
|
parentIdAttr: "ChannelId",
|
||||||
|
childName: "Posts",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCommandsCommandWebhooksIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Commands",
|
||||||
|
parentIdAttr: "CommandId",
|
||||||
|
childName: "CommandWebhooks",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPostsFileInfoIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Posts",
|
||||||
|
parentIdAttr: "PostId",
|
||||||
|
childName: "FileInfo",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPostsPostsParentIdIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Posts",
|
||||||
|
parentIdAttr: "ParentId",
|
||||||
|
childName: "Posts",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
canParentIdBeEmpty: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPostsPostsRootIdIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Posts",
|
||||||
|
parentIdAttr: "RootId",
|
||||||
|
childName: "Posts",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
canParentIdBeEmpty: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPostsReactionsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Posts",
|
||||||
|
parentIdAttr: "PostId",
|
||||||
|
childName: "Reactions",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSchemesChannelsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Schemes",
|
||||||
|
parentIdAttr: "SchemeId",
|
||||||
|
childName: "Channels",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
canParentIdBeEmpty: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSchemesTeamsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Schemes",
|
||||||
|
parentIdAttr: "SchemeId",
|
||||||
|
childName: "Teams",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
canParentIdBeEmpty: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSessionsAuditsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Sessions",
|
||||||
|
parentIdAttr: "SessionId",
|
||||||
|
childName: "Audits",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
canParentIdBeEmpty: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkTeamsChannelsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Teams",
|
||||||
|
parentIdAttr: "TeamId",
|
||||||
|
childName: "Channels",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkTeamsCommandsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Teams",
|
||||||
|
parentIdAttr: "TeamId",
|
||||||
|
childName: "Commands",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkTeamsIncomingWebhooksIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Teams",
|
||||||
|
parentIdAttr: "TeamId",
|
||||||
|
childName: "IncomingWebhooks",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkTeamsOutgoingWebhooksIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Teams",
|
||||||
|
parentIdAttr: "TeamId",
|
||||||
|
childName: "OutgoingWebhooks",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkTeamsTeamMembersIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Teams",
|
||||||
|
parentIdAttr: "TeamId",
|
||||||
|
childName: "TeamMembers",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersAuditsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "Audits",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
canParentIdBeEmpty: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersCommandWebhooksIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "CommandWebhooks",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersChannelMemberHistoryIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "ChannelMemberHistory",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersChannelMembersIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "ChannelMembers",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersChannelsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "CreatorId",
|
||||||
|
childName: "Channels",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
canParentIdBeEmpty: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersCommandsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "CreatorId",
|
||||||
|
childName: "Commands",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersCompliancesIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "Compliances",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersEmojiIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "CreatorId",
|
||||||
|
childName: "Emoji",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersFileInfoIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Posts",
|
||||||
|
parentIdAttr: "CreatorId",
|
||||||
|
childName: "FileInfo",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersIncomingWebhooksIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "IncomingWebhooks",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersOAuthAccessDataIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "OAuthAccessData",
|
||||||
|
childIdAttr: "Token",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersOAuthAppsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "CreatorId",
|
||||||
|
childName: "OAuthApps",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersOAuthAuthDataIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "OAuthAuthData",
|
||||||
|
childIdAttr: "Code",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersOutgoingWebhooksIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "CreatorId",
|
||||||
|
childName: "OutgoingWebhooks",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersPostsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "Posts",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersPreferencesIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "Preferences",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersReactionsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "Reactions",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersSessionsIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "Sessions",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersStatusIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "Status",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersTeamMembersIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "TeamMembers",
|
||||||
|
childIdAttr: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersUserAccessTokensIntegrity(ss *SqlSupplier) store.IntegrityCheckResult {
|
||||||
|
return checkParentChildIntegrity(ss, relationalCheckConfig{
|
||||||
|
parentName: "Users",
|
||||||
|
parentIdAttr: "UserId",
|
||||||
|
childName: "UserAccessTokens",
|
||||||
|
childIdAttr: "Id",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkChannelsIntegrity(ss *SqlSupplier, results chan<- store.IntegrityCheckResult) {
|
||||||
|
results <- checkChannelsCommandWebhooksIntegrity(ss)
|
||||||
|
results <- checkChannelsChannelMemberHistoryIntegrity(ss)
|
||||||
|
results <- checkChannelsChannelMembersIntegrity(ss)
|
||||||
|
results <- checkChannelsIncomingWebhooksIntegrity(ss)
|
||||||
|
results <- checkChannelsOutgoingWebhooksIntegrity(ss)
|
||||||
|
results <- checkChannelsPostsIntegrity(ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCommandsIntegrity(ss *SqlSupplier, results chan<- store.IntegrityCheckResult) {
|
||||||
|
results <- checkCommandsCommandWebhooksIntegrity(ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPostsIntegrity(ss *SqlSupplier, results chan<- store.IntegrityCheckResult) {
|
||||||
|
results <- checkPostsFileInfoIntegrity(ss)
|
||||||
|
results <- checkPostsPostsParentIdIntegrity(ss)
|
||||||
|
results <- checkPostsPostsRootIdIntegrity(ss)
|
||||||
|
results <- checkPostsReactionsIntegrity(ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSchemesIntegrity(ss *SqlSupplier, results chan<- store.IntegrityCheckResult) {
|
||||||
|
results <- checkSchemesChannelsIntegrity(ss)
|
||||||
|
results <- checkSchemesTeamsIntegrity(ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSessionsIntegrity(ss *SqlSupplier, results chan<- store.IntegrityCheckResult) {
|
||||||
|
results <- checkSessionsAuditsIntegrity(ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkTeamsIntegrity(ss *SqlSupplier, results chan<- store.IntegrityCheckResult) {
|
||||||
|
results <- checkTeamsChannelsIntegrity(ss)
|
||||||
|
results <- checkTeamsCommandsIntegrity(ss)
|
||||||
|
results <- checkTeamsIncomingWebhooksIntegrity(ss)
|
||||||
|
results <- checkTeamsOutgoingWebhooksIntegrity(ss)
|
||||||
|
results <- checkTeamsTeamMembersIntegrity(ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUsersIntegrity(ss *SqlSupplier, results chan<- store.IntegrityCheckResult) {
|
||||||
|
results <- checkUsersAuditsIntegrity(ss)
|
||||||
|
results <- checkUsersCommandWebhooksIntegrity(ss)
|
||||||
|
results <- checkUsersChannelMemberHistoryIntegrity(ss)
|
||||||
|
results <- checkUsersChannelMembersIntegrity(ss)
|
||||||
|
results <- checkUsersChannelsIntegrity(ss)
|
||||||
|
results <- checkUsersCommandsIntegrity(ss)
|
||||||
|
results <- checkUsersCompliancesIntegrity(ss)
|
||||||
|
results <- checkUsersEmojiIntegrity(ss)
|
||||||
|
results <- checkUsersFileInfoIntegrity(ss)
|
||||||
|
results <- checkUsersIncomingWebhooksIntegrity(ss)
|
||||||
|
results <- checkUsersOAuthAccessDataIntegrity(ss)
|
||||||
|
results <- checkUsersOAuthAppsIntegrity(ss)
|
||||||
|
results <- checkUsersOAuthAuthDataIntegrity(ss)
|
||||||
|
results <- checkUsersOutgoingWebhooksIntegrity(ss)
|
||||||
|
results <- checkUsersPostsIntegrity(ss)
|
||||||
|
results <- checkUsersPreferencesIntegrity(ss)
|
||||||
|
results <- checkUsersReactionsIntegrity(ss)
|
||||||
|
results <- checkUsersSessionsIntegrity(ss)
|
||||||
|
results <- checkUsersStatusIntegrity(ss)
|
||||||
|
results <- checkUsersTeamMembersIntegrity(ss)
|
||||||
|
results <- checkUsersUserAccessTokensIntegrity(ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckRelationalIntegrity(ss *SqlSupplier, results chan<- store.IntegrityCheckResult) {
|
||||||
|
mlog.Info("Starting relational integrity checks...")
|
||||||
|
checkChannelsIntegrity(ss, results)
|
||||||
|
checkCommandsIntegrity(ss, results)
|
||||||
|
checkPostsIntegrity(ss, results)
|
||||||
|
checkSchemesIntegrity(ss, results)
|
||||||
|
checkSessionsIntegrity(ss, results)
|
||||||
|
checkTeamsIntegrity(ss, results)
|
||||||
|
checkUsersIntegrity(ss, results)
|
||||||
|
mlog.Info("Done with relational integrity checks")
|
||||||
|
close(results)
|
||||||
|
}
|
||||||
1539
store/sqlstore/integrity_test.go
Обычный файл
1539
store/sqlstore/integrity_test.go
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@@ -1066,6 +1066,12 @@ func (ss *SqlSupplier) getQueryBuilder() sq.StatementBuilderType {
|
|||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ss *SqlSupplier) CheckIntegrity() <-chan store.IntegrityCheckResult {
|
||||||
|
results := make(chan store.IntegrityCheckResult)
|
||||||
|
go CheckRelationalIntegrity(ss, results)
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
type mattermConverter struct{}
|
type mattermConverter struct{}
|
||||||
|
|
||||||
func (me mattermConverter) ToDb(val interface{}) (interface{}, error) {
|
func (me mattermConverter) ToDb(val interface{}) (interface{}, error) {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ type Store interface {
|
|||||||
TotalMasterDbConnections() int
|
TotalMasterDbConnections() int
|
||||||
TotalReadDbConnections() int
|
TotalReadDbConnections() int
|
||||||
TotalSearchDbConnections() int
|
TotalSearchDbConnections() int
|
||||||
|
CheckIntegrity() <-chan IntegrityCheckResult
|
||||||
}
|
}
|
||||||
|
|
||||||
type TeamStore interface {
|
type TeamStore interface {
|
||||||
@@ -627,3 +628,21 @@ type UserGetByIdsOpts struct {
|
|||||||
// Since filters the users based on their UpdateAt timestamp.
|
// Since filters the users based on their UpdateAt timestamp.
|
||||||
Since int64
|
Since int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OrphanedRecord struct {
|
||||||
|
ParentId string
|
||||||
|
ChildId string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RelationalIntegrityCheckData struct {
|
||||||
|
ParentName string
|
||||||
|
ChildName string
|
||||||
|
ParentIdAttr string
|
||||||
|
ChildIdAttr string
|
||||||
|
Records []OrphanedRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntegrityCheckResult struct {
|
||||||
|
Data interface{}
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|||||||
@@ -78,6 +78,22 @@ func (_m *LayeredStoreDatabaseLayer) ChannelMemberHistory() store.ChannelMemberH
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckIntegrity provides a mock function with given fields:
|
||||||
|
func (_m *LayeredStoreDatabaseLayer) CheckIntegrity() <-chan store.IntegrityCheckResult {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
var r0 <-chan store.IntegrityCheckResult
|
||||||
|
if rf, ok := ret.Get(0).(func() <-chan store.IntegrityCheckResult); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(<-chan store.IntegrityCheckResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// Close provides a mock function with given fields:
|
// Close provides a mock function with given fields:
|
||||||
func (_m *LayeredStoreDatabaseLayer) Close() {
|
func (_m *LayeredStoreDatabaseLayer) Close() {
|
||||||
_m.Called()
|
_m.Called()
|
||||||
|
|||||||
@@ -76,6 +76,22 @@ func (_m *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore {
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckIntegrity provides a mock function with given fields:
|
||||||
|
func (_m *Store) CheckIntegrity() <-chan store.IntegrityCheckResult {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
var r0 <-chan store.IntegrityCheckResult
|
||||||
|
if rf, ok := ret.Get(0).(func() <-chan store.IntegrityCheckResult); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(<-chan store.IntegrityCheckResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// Close provides a mock function with given fields:
|
// Close provides a mock function with given fields:
|
||||||
func (_m *Store) Close() {
|
func (_m *Store) Close() {
|
||||||
_m.Called()
|
_m.Called()
|
||||||
|
|||||||
@@ -87,6 +87,9 @@ func (s *Store) TotalMasterDbConnections() int { return 1 }
|
|||||||
func (s *Store) TotalReadDbConnections() int { return 1 }
|
func (s *Store) TotalReadDbConnections() int { return 1 }
|
||||||
func (s *Store) TotalSearchDbConnections() int { return 1 }
|
func (s *Store) TotalSearchDbConnections() int { return 1 }
|
||||||
func (s *Store) GetCurrentSchemaVersion() string { return "" }
|
func (s *Store) GetCurrentSchemaVersion() string { return "" }
|
||||||
|
func (s *Store) CheckIntegrity() <-chan store.IntegrityCheckResult {
|
||||||
|
return make(chan store.IntegrityCheckResult)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) AssertExpectations(t mock.TestingT) bool {
|
func (s *Store) AssertExpectations(t mock.TestingT) bool {
|
||||||
return mock.AssertExpectationsForObjects(t,
|
return mock.AssertExpectationsForObjects(t,
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user