blob: 56c6fa85aab6ead6d289b604af4820841ce71f96 [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 static com.google.gerrit.server.Sequence.LightweightGroups;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.entities.GroupReference;
import com.google.gerrit.entities.InternalGroup;
import com.google.gerrit.exceptions.DuplicateKeyException;
import com.google.gerrit.git.RefUpdateUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.Sequence;
import com.google.gerrit.server.account.GroupUuid;
import com.google.gerrit.server.account.ServiceUserClassifier;
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.db.AuditLogFormatter;
import com.google.gerrit.server.group.db.GroupConfig;
import com.google.gerrit.server.group.db.GroupDelta;
import com.google.gerrit.server.group.db.GroupNameNotes;
import com.google.gerrit.server.group.db.InternalGroupCreation;
import com.google.gerrit.server.index.group.GroupIndex;
import com.google.gerrit.server.index.group.GroupIndexCollection;
import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType;
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.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 Sequence groupsSequence;
private final PersonIdent serverUser;
private final GroupIndexCollection indexCollection;
private final String serverId;
private final AllProjectsName allProjectsName;
@Inject
public SchemaCreatorImpl(
GitRepositoryManager repoManager,
AllProjectsCreator ap,
AllUsersCreator auc,
AllUsersName allUsersName,
@LightweightGroups Sequence groupsSequence,
@GerritPersonIdent PersonIdent au,
GroupIndexCollection ic,
String serverId,
AllProjectsName apName) {
this.repoManager = repoManager;
allProjectsCreator = ap;
allUsersCreator = auc;
this.allUsersName = allUsersName;
this.groupsSequence = groupsSequence;
serverUser = au;
indexCollection = ic;
this.serverId = serverId;
this.allProjectsName = apName;
}
@Override
public void create() throws IOException, ConfigInvalidException {
try (RefUpdateContext ctx = RefUpdateContext.open(RefUpdateType.INIT_REPO)) {
GroupReference admins = createGroupReference("Administrators");
GroupReference serviceUsers = createGroupReference(ServiceUserClassifier.SERVICE_USERS);
AllProjectsInput allProjectsInput =
AllProjectsInput.builder()
.administratorsGroup(admins)
.serviceUsersGroup(serviceUsers)
.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();
try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
createAdminsGroup(allUsersRepo, admins);
createBatchUsersGroup(allUsersRepo, serviceUsers, admins.getUUID());
}
}
}
@Override
public void ensureCreated() throws IOException, ConfigInvalidException {
try {
repoManager.openRepository(allProjectsName).close();
} catch (RepositoryNotFoundException e) {
create();
}
}
private void createAdminsGroup(Repository allUsersRepo, GroupReference groupReference)
throws IOException, ConfigInvalidException {
InternalGroupCreation groupCreation = getGroupCreation(groupReference);
GroupDelta groupDelta =
GroupDelta.builder().setDescription("Gerrit Site Administrators").build();
createGroup(allUsersRepo, groupCreation, groupDelta);
}
private void createBatchUsersGroup(
Repository allUsersRepo, GroupReference groupReference, AccountGroup.UUID adminsGroupUuid)
throws IOException, ConfigInvalidException {
InternalGroupCreation groupCreation = getGroupCreation(groupReference);
GroupDelta groupDelta =
GroupDelta.builder()
.setDescription("Users who perform batch actions on Gerrit")
.setOwnerGroupUUID(adminsGroupUuid)
.build();
createGroup(allUsersRepo, groupCreation, groupDelta);
}
private void createGroup(
Repository allUsersRepo, InternalGroupCreation groupCreation, GroupDelta groupDelta)
throws ConfigInvalidException, IOException {
InternalGroup createdGroup = createGroupInNoteDb(allUsersRepo, groupCreation, groupDelta);
index(createdGroup);
}
private InternalGroup createGroupInNoteDb(
Repository allUsersRepo, InternalGroupCreation groupCreation, GroupDelta groupDelta)
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.setGroupDelta(groupDelta, auditLogFormatter);
AccountGroup.NameKey groupName = groupDelta.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 GroupReference.create(groupUuid, name);
}
private InternalGroupCreation getGroupCreation(GroupReference groupReference) {
int next = groupsSequence.next();
return InternalGroupCreation.builder()
.setNameKey(AccountGroup.nameKey(groupReference.getName()))
.setId(AccountGroup.id(next))
.setGroupUUID(groupReference.getUUID())
.build();
}
}