gitea

Зеркало из https://github.com/go-gitea/gitea
Форк
0
/
dbconsistency.go 
254 строки · 9.3 Кб
1
// Copyright 2020 The Gitea Authors. All rights reserved.
2
// SPDX-License-Identifier: MIT
3

4
package doctor
5

6
import (
7
	"context"
8

9
	actions_model "code.gitea.io/gitea/models/actions"
10
	activities_model "code.gitea.io/gitea/models/activities"
11
	"code.gitea.io/gitea/models/db"
12
	issues_model "code.gitea.io/gitea/models/issues"
13
	"code.gitea.io/gitea/models/migrations"
14
	repo_model "code.gitea.io/gitea/models/repo"
15
	"code.gitea.io/gitea/modules/log"
16
	"code.gitea.io/gitea/modules/setting"
17
)
18

19
type consistencyCheck struct {
20
	Name         string
21
	Counter      func(context.Context) (int64, error)
22
	Fixer        func(context.Context) (int64, error)
23
	FixedMessage string
24
}
25

26
func (c *consistencyCheck) Run(ctx context.Context, logger log.Logger, autofix bool) error {
27
	count, err := c.Counter(ctx)
28
	if err != nil {
29
		logger.Critical("Error: %v whilst counting %s", err, c.Name)
30
		return err
31
	}
32
	if count > 0 {
33
		if autofix {
34
			var fixed int64
35
			if fixed, err = c.Fixer(ctx); err != nil {
36
				logger.Critical("Error: %v whilst fixing %s", err, c.Name)
37
				return err
38
			}
39

40
			prompt := "Deleted"
41
			if c.FixedMessage != "" {
42
				prompt = c.FixedMessage
43
			}
44

45
			if fixed < 0 {
46
				logger.Info(prompt+" %d %s", count, c.Name)
47
			} else {
48
				logger.Info(prompt+" %d/%d %s", fixed, count, c.Name)
49
			}
50
		} else {
51
			logger.Warn("Found %d %s", count, c.Name)
52
		}
53
	}
54
	return nil
55
}
56

57
func asFixer(fn func(ctx context.Context) error) func(ctx context.Context) (int64, error) {
58
	return func(ctx context.Context) (int64, error) {
59
		err := fn(ctx)
60
		return -1, err
61
	}
62
}
63

64
func genericOrphanCheck(name, subject, refObject, joinCond string) consistencyCheck {
65
	return consistencyCheck{
66
		Name: name,
67
		Counter: func(ctx context.Context) (int64, error) {
68
			return db.CountOrphanedObjects(ctx, subject, refObject, joinCond)
69
		},
70
		Fixer: func(ctx context.Context) (int64, error) {
71
			err := db.DeleteOrphanedObjects(ctx, subject, refObject, joinCond)
72
			return -1, err
73
		},
74
	}
75
}
76

77
func prepareDBConsistencyChecks() []consistencyCheck {
78
	consistencyChecks := []consistencyCheck{
79
		{
80
			// find labels without existing repo or org
81
			Name:    "Orphaned Labels without existing repository or organisation",
82
			Counter: issues_model.CountOrphanedLabels,
83
			Fixer:   asFixer(issues_model.DeleteOrphanedLabels),
84
		},
85
		{
86
			// find IssueLabels without existing label
87
			Name:    "Orphaned Issue Labels without existing label",
88
			Counter: issues_model.CountOrphanedIssueLabels,
89
			Fixer:   asFixer(issues_model.DeleteOrphanedIssueLabels),
90
		},
91
		{
92
			// find issues without existing repository
93
			Name:    "Orphaned Issues without existing repository",
94
			Counter: issues_model.CountOrphanedIssues,
95
			Fixer:   asFixer(issues_model.DeleteOrphanedIssues),
96
		},
97
		// find releases without existing repository
98
		genericOrphanCheck("Orphaned Releases without existing repository",
99
			"release", "repository", "`release`.repo_id=repository.id"),
100
		// find pulls without existing issues
101
		genericOrphanCheck("Orphaned PullRequests without existing issue",
102
			"pull_request", "issue", "pull_request.issue_id=issue.id"),
103
		// find pull requests without base repository
104
		genericOrphanCheck("Pull request entries without existing base repository",
105
			"pull_request", "repository", "pull_request.base_repo_id=repository.id"),
106
		// find tracked times without existing issues/pulls
107
		genericOrphanCheck("Orphaned TrackedTimes without existing issue",
108
			"tracked_time", "issue", "tracked_time.issue_id=issue.id"),
109
		// find attachments without existing issues or releases
110
		{
111
			Name:    "Orphaned Attachments without existing issues or releases",
112
			Counter: repo_model.CountOrphanedAttachments,
113
			Fixer:   asFixer(repo_model.DeleteOrphanedAttachments),
114
		},
115
		// find null archived repositories
116
		{
117
			Name:         "Repositories with is_archived IS NULL",
118
			Counter:      repo_model.CountNullArchivedRepository,
119
			Fixer:        repo_model.FixNullArchivedRepository,
120
			FixedMessage: "Fixed",
121
		},
122
		// find label comments with empty labels
123
		{
124
			Name:         "Label comments with empty labels",
125
			Counter:      issues_model.CountCommentTypeLabelWithEmptyLabel,
126
			Fixer:        issues_model.FixCommentTypeLabelWithEmptyLabel,
127
			FixedMessage: "Fixed",
128
		},
129
		// find label comments with labels from outside the repository
130
		{
131
			Name:         "Label comments with labels from outside the repository",
132
			Counter:      issues_model.CountCommentTypeLabelWithOutsideLabels,
133
			Fixer:        issues_model.FixCommentTypeLabelWithOutsideLabels,
134
			FixedMessage: "Removed",
135
		},
136
		// find issue_label with labels from outside the repository
137
		{
138
			Name:         "IssueLabels with Labels from outside the repository",
139
			Counter:      issues_model.CountIssueLabelWithOutsideLabels,
140
			Fixer:        issues_model.FixIssueLabelWithOutsideLabels,
141
			FixedMessage: "Removed",
142
		},
143
		{
144
			Name:         "Action with created_unix set as an empty string",
145
			Counter:      activities_model.CountActionCreatedUnixString,
146
			Fixer:        activities_model.FixActionCreatedUnixString,
147
			FixedMessage: "Set to zero",
148
		},
149
		{
150
			Name:         "Action Runners without existing owner",
151
			Counter:      actions_model.CountRunnersWithoutBelongingOwner,
152
			Fixer:        actions_model.FixRunnersWithoutBelongingOwner,
153
			FixedMessage: "Removed",
154
		},
155
		{
156
			Name:         "Action Runners without existing repository",
157
			Counter:      actions_model.CountRunnersWithoutBelongingRepo,
158
			Fixer:        actions_model.FixRunnersWithoutBelongingRepo,
159
			FixedMessage: "Removed",
160
		},
161
		{
162
			Name:         "Topics with empty repository count",
163
			Counter:      repo_model.CountOrphanedTopics,
164
			Fixer:        repo_model.DeleteOrphanedTopics,
165
			FixedMessage: "Removed",
166
		},
167
	}
168

169
	// TODO: function to recalc all counters
170

171
	if setting.Database.Type.IsPostgreSQL() {
172
		consistencyChecks = append(consistencyChecks, consistencyCheck{
173
			Name:         "Sequence values",
174
			Counter:      db.CountBadSequences,
175
			Fixer:        asFixer(db.FixBadSequences),
176
			FixedMessage: "Updated",
177
		})
178
	}
179

180
	consistencyChecks = append(consistencyChecks,
181
		// find protected branches without existing repository
182
		genericOrphanCheck("Protected Branches without existing repository",
183
			"protected_branch", "repository", "protected_branch.repo_id=repository.id"),
184
		// find branches without existing repository
185
		genericOrphanCheck("Branches without existing repository",
186
			"branch", "repository", "branch.repo_id=repository.id"),
187
		// find LFS locks without existing repository
188
		genericOrphanCheck("LFS locks without existing repository",
189
			"lfs_lock", "repository", "lfs_lock.repo_id=repository.id"),
190
		// find collaborations without users
191
		genericOrphanCheck("Collaborations without existing user",
192
			"collaboration", "user", "collaboration.user_id=`user`.id"),
193
		// find collaborations without repository
194
		genericOrphanCheck("Collaborations without existing repository",
195
			"collaboration", "repository", "collaboration.repo_id=repository.id"),
196
		// find access without users
197
		genericOrphanCheck("Access entries without existing user",
198
			"access", "user", "access.user_id=`user`.id"),
199
		// find access without repository
200
		genericOrphanCheck("Access entries without existing repository",
201
			"access", "repository", "access.repo_id=repository.id"),
202
		// find action without repository
203
		genericOrphanCheck("Action entries without existing repository",
204
			"action", "repository", "action.repo_id=repository.id"),
205
		// find action without user
206
		genericOrphanCheck("Action entries without existing user",
207
			"action", "user", "action.act_user_id=`user`.id"),
208
		// find OAuth2Grant without existing user
209
		genericOrphanCheck("Orphaned OAuth2Grant without existing User",
210
			"oauth2_grant", "user", "oauth2_grant.user_id=`user`.id"),
211
		// find OAuth2Application without existing user
212
		genericOrphanCheck("Orphaned OAuth2Application without existing User",
213
			"oauth2_application", "user", "oauth2_application.uid=0 OR oauth2_application.uid=`user`.id"),
214
		// find OAuth2AuthorizationCode without existing OAuth2Grant
215
		genericOrphanCheck("Orphaned OAuth2AuthorizationCode without existing OAuth2Grant",
216
			"oauth2_authorization_code", "oauth2_grant", "oauth2_authorization_code.grant_id=oauth2_grant.id"),
217
		// find stopwatches without existing user
218
		genericOrphanCheck("Orphaned Stopwatches without existing User",
219
			"stopwatch", "user", "stopwatch.user_id=`user`.id"),
220
		// find stopwatches without existing issue
221
		genericOrphanCheck("Orphaned Stopwatches without existing Issue",
222
			"stopwatch", "issue", "stopwatch.issue_id=`issue`.id"),
223
		// find redirects without existing user.
224
		genericOrphanCheck("Orphaned Redirects without existing redirect user",
225
			"user_redirect", "user", "user_redirect.redirect_user_id=`user`.id"),
226
	)
227
	return consistencyChecks
228
}
229

230
func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) error {
231
	// make sure DB version is uptodate
232
	if err := db.InitEngineWithMigration(ctx, migrations.EnsureUpToDate); err != nil {
233
		logger.Critical("Model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded")
234
		return err
235
	}
236
	consistencyChecks := prepareDBConsistencyChecks()
237
	for _, c := range consistencyChecks {
238
		if err := c.Run(ctx, logger, autofix); err != nil {
239
			return err
240
		}
241
	}
242

243
	return nil
244
}
245

246
func init() {
247
	Register(&Check{
248
		Title:     "Check consistency of database",
249
		Name:      "check-db-consistency",
250
		IsDefault: false,
251
		Run:       checkDBConsistency,
252
		Priority:  3,
253
	})
254
}
255

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.