Line data Source code
1 : // Copyright (C) 2009 The Android Open Source Project
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : package com.google.gerrit.server.schema;
16 :
17 : import com.google.common.collect.ImmutableSet;
18 : import com.google.gerrit.common.Nullable;
19 : import com.google.gerrit.entities.AccountGroup;
20 : import com.google.gerrit.entities.GroupReference;
21 : import com.google.gerrit.entities.InternalGroup;
22 : import com.google.gerrit.exceptions.DuplicateKeyException;
23 : import com.google.gerrit.git.RefUpdateUtil;
24 : import com.google.gerrit.metrics.MetricMaker;
25 : import com.google.gerrit.server.GerritPersonIdent;
26 : import com.google.gerrit.server.account.GroupUuid;
27 : import com.google.gerrit.server.account.ServiceUserClassifier;
28 : import com.google.gerrit.server.config.AllProjectsName;
29 : import com.google.gerrit.server.config.AllUsersName;
30 : import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
31 : import com.google.gerrit.server.git.GitRepositoryManager;
32 : import com.google.gerrit.server.git.meta.MetaDataUpdate;
33 : import com.google.gerrit.server.group.db.AuditLogFormatter;
34 : import com.google.gerrit.server.group.db.GroupConfig;
35 : import com.google.gerrit.server.group.db.GroupDelta;
36 : import com.google.gerrit.server.group.db.GroupNameNotes;
37 : import com.google.gerrit.server.group.db.InternalGroupCreation;
38 : import com.google.gerrit.server.index.group.GroupIndex;
39 : import com.google.gerrit.server.index.group.GroupIndexCollection;
40 : import com.google.gerrit.server.notedb.Sequences;
41 : import com.google.inject.Inject;
42 : import java.io.IOException;
43 : import org.eclipse.jgit.errors.ConfigInvalidException;
44 : import org.eclipse.jgit.errors.RepositoryNotFoundException;
45 : import org.eclipse.jgit.lib.BatchRefUpdate;
46 : import org.eclipse.jgit.lib.Config;
47 : import org.eclipse.jgit.lib.PersonIdent;
48 : import org.eclipse.jgit.lib.Repository;
49 :
50 : // TODO(dborowitz): The current NoteDb implementation mirrors the old ReviewDb code: this class is
51 : // called to create the site early on in NoteDbSchemaUpdater#update. This logic is a little
52 : // confusing and could stand to be reworked. Another smell is that this is an interface only for
53 : // testing purposes.
54 : public class SchemaCreatorImpl implements SchemaCreator {
55 : private final GitRepositoryManager repoManager;
56 : private final AllProjectsCreator allProjectsCreator;
57 : private final AllUsersCreator allUsersCreator;
58 : private final AllUsersName allUsersName;
59 : private final PersonIdent serverUser;
60 : private final GroupIndexCollection indexCollection;
61 : private final String serverId;
62 :
63 : private final Config config;
64 : private final MetricMaker metricMaker;
65 : private final AllProjectsName allProjectsName;
66 :
67 : @Inject
68 : public SchemaCreatorImpl(
69 : GitRepositoryManager repoManager,
70 : AllProjectsCreator ap,
71 : AllUsersCreator auc,
72 : AllUsersName allUsersName,
73 : @GerritPersonIdent PersonIdent au,
74 : GroupIndexCollection ic,
75 : String serverId,
76 : Config config,
77 : MetricMaker metricMaker,
78 151 : AllProjectsName apName) {
79 151 : this.repoManager = repoManager;
80 151 : allProjectsCreator = ap;
81 151 : allUsersCreator = auc;
82 151 : this.allUsersName = allUsersName;
83 151 : serverUser = au;
84 151 : indexCollection = ic;
85 151 : this.serverId = serverId;
86 :
87 151 : this.config = config;
88 151 : this.allProjectsName = apName;
89 151 : this.metricMaker = metricMaker;
90 151 : }
91 :
92 : @Override
93 : public void create() throws IOException, ConfigInvalidException {
94 151 : GroupReference admins = createGroupReference("Administrators");
95 151 : GroupReference serviceUsers = createGroupReference(ServiceUserClassifier.SERVICE_USERS);
96 :
97 : AllProjectsInput allProjectsInput =
98 151 : AllProjectsInput.builder()
99 151 : .administratorsGroup(admins)
100 151 : .serviceUsersGroup(serviceUsers)
101 151 : .build();
102 151 : allProjectsCreator.create(allProjectsInput);
103 : // We have to create the All-Users repository before we can use it to store the groups in it.
104 151 : allUsersCreator.setAdministrators(admins).create();
105 :
106 : // Don't rely on injection to construct Sequences, as the default GitReferenceUpdated has a
107 : // thick dependency stack which may not all be available at schema creation time.
108 151 : Sequences seqs =
109 : new Sequences(
110 : config,
111 : repoManager,
112 : GitReferenceUpdated.DISABLED,
113 : allProjectsName,
114 : allUsersName,
115 : metricMaker);
116 151 : try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
117 151 : createAdminsGroup(seqs, allUsersRepo, admins);
118 151 : createBatchUsersGroup(seqs, allUsersRepo, serviceUsers, admins.getUUID());
119 : }
120 151 : }
121 :
122 : @Override
123 : public void ensureCreated() throws IOException, ConfigInvalidException {
124 : try {
125 4 : repoManager.openRepository(allProjectsName).close();
126 138 : } catch (RepositoryNotFoundException e) {
127 138 : create();
128 4 : }
129 138 : }
130 :
131 : private void createAdminsGroup(
132 : Sequences seqs, Repository allUsersRepo, GroupReference groupReference)
133 : throws IOException, ConfigInvalidException {
134 151 : InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
135 : GroupDelta groupDelta =
136 151 : GroupDelta.builder().setDescription("Gerrit Site Administrators").build();
137 :
138 151 : createGroup(allUsersRepo, groupCreation, groupDelta);
139 151 : }
140 :
141 : private void createBatchUsersGroup(
142 : Sequences seqs,
143 : Repository allUsersRepo,
144 : GroupReference groupReference,
145 : AccountGroup.UUID adminsGroupUuid)
146 : throws IOException, ConfigInvalidException {
147 151 : InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
148 : GroupDelta groupDelta =
149 151 : GroupDelta.builder()
150 151 : .setDescription("Users who perform batch actions on Gerrit")
151 151 : .setOwnerGroupUUID(adminsGroupUuid)
152 151 : .build();
153 :
154 151 : createGroup(allUsersRepo, groupCreation, groupDelta);
155 151 : }
156 :
157 : private void createGroup(
158 : Repository allUsersRepo, InternalGroupCreation groupCreation, GroupDelta groupDelta)
159 : throws ConfigInvalidException, IOException {
160 151 : InternalGroup createdGroup = createGroupInNoteDb(allUsersRepo, groupCreation, groupDelta);
161 151 : index(createdGroup);
162 151 : }
163 :
164 : private InternalGroup createGroupInNoteDb(
165 : Repository allUsersRepo, InternalGroupCreation groupCreation, GroupDelta groupDelta)
166 : throws ConfigInvalidException, IOException, DuplicateKeyException {
167 : // This method is only executed on a new server which doesn't have any accounts or groups.
168 : AuditLogFormatter auditLogFormatter =
169 151 : AuditLogFormatter.createBackedBy(ImmutableSet.of(), ImmutableSet.of(), serverId);
170 :
171 151 : GroupConfig groupConfig =
172 151 : GroupConfig.createForNewGroup(allUsersName, allUsersRepo, groupCreation);
173 151 : groupConfig.setGroupDelta(groupDelta, auditLogFormatter);
174 :
175 151 : AccountGroup.NameKey groupName = groupDelta.getName().orElseGet(groupCreation::getNameKey);
176 151 : GroupNameNotes groupNameNotes =
177 151 : GroupNameNotes.forNewGroup(
178 151 : allUsersName, allUsersRepo, groupCreation.getGroupUUID(), groupName);
179 :
180 151 : commit(allUsersRepo, groupConfig, groupNameNotes);
181 :
182 151 : return groupConfig
183 151 : .getLoadedGroup()
184 151 : .orElseThrow(() -> new IllegalStateException("Created group wasn't automatically loaded"));
185 : }
186 :
187 : private void commit(
188 : Repository allUsersRepo, GroupConfig groupConfig, GroupNameNotes groupNameNotes)
189 : throws IOException {
190 151 : BatchRefUpdate batchRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
191 151 : try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate(allUsersRepo, batchRefUpdate)) {
192 151 : groupConfig.commit(metaDataUpdate);
193 : }
194 : // MetaDataUpdates unfortunately can't be reused. -> Create a new one.
195 151 : try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate(allUsersRepo, batchRefUpdate)) {
196 151 : groupNameNotes.commit(metaDataUpdate);
197 : }
198 151 : RefUpdateUtil.executeChecked(batchRefUpdate, allUsersRepo);
199 151 : }
200 :
201 : private MetaDataUpdate createMetaDataUpdate(
202 : Repository allUsersRepo, @Nullable BatchRefUpdate batchRefUpdate) {
203 151 : MetaDataUpdate metaDataUpdate =
204 : new MetaDataUpdate(
205 : GitReferenceUpdated.DISABLED, allUsersName, allUsersRepo, batchRefUpdate);
206 151 : metaDataUpdate.getCommitBuilder().setAuthor(serverUser);
207 151 : metaDataUpdate.getCommitBuilder().setCommitter(serverUser);
208 151 : return metaDataUpdate;
209 : }
210 :
211 : private void index(InternalGroup group) {
212 151 : for (GroupIndex groupIndex : indexCollection.getWriteIndexes()) {
213 30 : groupIndex.replace(group);
214 30 : }
215 151 : }
216 :
217 : private GroupReference createGroupReference(String name) {
218 151 : AccountGroup.UUID groupUuid = GroupUuid.make(name, serverUser);
219 151 : return GroupReference.create(groupUuid, name);
220 : }
221 :
222 : private InternalGroupCreation getGroupCreation(Sequences seqs, GroupReference groupReference) {
223 151 : int next = seqs.nextGroupId();
224 151 : return InternalGroupCreation.builder()
225 151 : .setNameKey(AccountGroup.nameKey(groupReference.getName()))
226 151 : .setId(AccountGroup.id(next))
227 151 : .setGroupUUID(groupReference.getUUID())
228 151 : .build();
229 : }
230 : }
|