blob: 26f1990e89cfccd8620ce7f71117156308175a90 [file] [log] [blame]
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.schema;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.git.RefUpdateUtil;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.GroupUUID;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.db.AuditLogFormatter;
import com.google.gerrit.server.group.db.GroupConfig;
import com.google.gerrit.server.group.db.GroupNameNotes;
import com.google.gerrit.server.group.db.InternalGroupCreation;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.index.group.GroupIndex;
import com.google.gerrit.server.index.group.GroupIndexCollection;
import com.google.gerrit.server.notedb.Sequences;
import com.google.inject.Inject;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
// TODO(dborowitz): The current NoteDb implementation mirrors the old ReviewDb code: this class is
// called to create the site early on in NoteDbSchemaUpdater#update. This logic is a little
// confusing and could stand to be reworked. Another smell is that this is an interface only for
// testing purposes.
public class SchemaCreatorImpl implements SchemaCreator {
private final GitRepositoryManager repoManager;
private final AllProjectsCreator allProjectsCreator;
private final AllUsersCreator allUsersCreator;
private final AllUsersName allUsersName;
private final PersonIdent serverUser;
private final GroupIndexCollection indexCollection;
private final String serverId;
private final Config config;
private final MetricMaker metricMaker;
private final AllProjectsName allProjectsName;
@Inject
public SchemaCreatorImpl(
GitRepositoryManager repoManager,
AllProjectsCreator ap,
AllUsersCreator auc,
AllUsersName allUsersName,
@GerritPersonIdent PersonIdent au,
GroupIndexCollection ic,
String serverId,
Config config,
MetricMaker metricMaker,
AllProjectsName apName) {
this.repoManager = repoManager;
allProjectsCreator = ap;
allUsersCreator = auc;
this.allUsersName = allUsersName;
serverUser = au;
indexCollection = ic;
this.serverId = serverId;
this.config = config;
this.allProjectsName = apName;
this.metricMaker = metricMaker;
}
@Override
public void create() throws IOException, ConfigInvalidException {
GroupReference admins = createGroupReference("Administrators");
GroupReference batchUsers = createGroupReference("Non-Interactive Users");
AllProjectsInput allProjectsInput =
AllProjectsInput.builder().administratorsGroup(admins).batchUsersGroup(batchUsers).build();
allProjectsCreator.create(allProjectsInput);
// We have to create the All-Users repository before we can use it to store the groups in it.
allUsersCreator.setAdministrators(admins).create();
// Don't rely on injection to construct Sequences, as the default GitReferenceUpdated has a
// thick dependency stack which may not all be available at schema creation time.
Sequences seqs =
new Sequences(
config,
repoManager,
GitReferenceUpdated.DISABLED,
allProjectsName,
allUsersName,
metricMaker);
try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
createAdminsGroup(seqs, allUsersRepo, admins);
createBatchUsersGroup(seqs, allUsersRepo, batchUsers, admins.getUUID());
}
}
@Override
public void ensureCreated() throws IOException, ConfigInvalidException {
try {
repoManager.openRepository(allProjectsName).close();
} catch (RepositoryNotFoundException e) {
create();
}
}
private void createAdminsGroup(
Sequences seqs, Repository allUsersRepo, GroupReference groupReference)
throws IOException, ConfigInvalidException {
InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setDescription("Gerrit Site Administrators").build();
createGroup(allUsersRepo, groupCreation, groupUpdate);
}
private void createBatchUsersGroup(
Sequences seqs,
Repository allUsersRepo,
GroupReference groupReference,
AccountGroup.UUID adminsGroupUuid)
throws IOException, ConfigInvalidException {
InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setDescription("Users who perform batch actions on Gerrit")
.setOwnerGroupUUID(adminsGroupUuid)
.build();
createGroup(allUsersRepo, groupCreation, groupUpdate);
}
private void createGroup(
Repository allUsersRepo, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
throws ConfigInvalidException, IOException {
InternalGroup createdGroup = createGroupInNoteDb(allUsersRepo, groupCreation, groupUpdate);
index(createdGroup);
}
private InternalGroup createGroupInNoteDb(
Repository allUsersRepo, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
throws ConfigInvalidException, IOException, DuplicateKeyException {
// This method is only executed on a new server which doesn't have any accounts or groups.
AuditLogFormatter auditLogFormatter =
AuditLogFormatter.createBackedBy(ImmutableSet.of(), ImmutableSet.of(), serverId);
GroupConfig groupConfig =
GroupConfig.createForNewGroup(allUsersName, allUsersRepo, groupCreation);
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
AccountGroup.NameKey groupName = groupUpdate.getName().orElseGet(groupCreation::getNameKey);
GroupNameNotes groupNameNotes =
GroupNameNotes.forNewGroup(
allUsersName, allUsersRepo, groupCreation.getGroupUUID(), groupName);
commit(allUsersRepo, groupConfig, groupNameNotes);
return groupConfig
.getLoadedGroup()
.orElseThrow(() -> new IllegalStateException("Created group wasn't automatically loaded"));
}
private void commit(
Repository allUsersRepo, GroupConfig groupConfig, GroupNameNotes groupNameNotes)
throws IOException {
BatchRefUpdate batchRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate(allUsersRepo, batchRefUpdate)) {
groupConfig.commit(metaDataUpdate);
}
// MetaDataUpdates unfortunately can't be reused. -> Create a new one.
try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate(allUsersRepo, batchRefUpdate)) {
groupNameNotes.commit(metaDataUpdate);
}
RefUpdateUtil.executeChecked(batchRefUpdate, allUsersRepo);
}
private MetaDataUpdate createMetaDataUpdate(
Repository allUsersRepo, @Nullable BatchRefUpdate batchRefUpdate) {
MetaDataUpdate metaDataUpdate =
new MetaDataUpdate(
GitReferenceUpdated.DISABLED, allUsersName, allUsersRepo, batchRefUpdate);
metaDataUpdate.getCommitBuilder().setAuthor(serverUser);
metaDataUpdate.getCommitBuilder().setCommitter(serverUser);
return metaDataUpdate;
}
private void index(InternalGroup group) {
for (GroupIndex groupIndex : indexCollection.getWriteIndexes()) {
groupIndex.replace(group);
}
}
private GroupReference createGroupReference(String name) {
AccountGroup.UUID groupUuid = GroupUUID.make(name, serverUser);
return new GroupReference(groupUuid, name);
}
private InternalGroupCreation getGroupCreation(Sequences seqs, GroupReference groupReference) {
int next = seqs.nextGroupId();
return InternalGroupCreation.builder()
.setNameKey(AccountGroup.nameKey(groupReference.getName()))
.setId(AccountGroup.id(next))
.setGroupUUID(groupReference.getUUID())
.build();
}
}