blob: 66c40782bb293d4dd5e1b2e1f3e4d51d0764b786 [file] [log] [blame]
// Copyright (C) 2014 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.acceptance.pgm;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.truth.StreamSubject.streams;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.extensions.client.ListGroupsOption.MEMBERS;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.io.MoreFiles;
import com.google.common.io.RecursiveDeleteOption;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.StandaloneSiteTest;
import com.google.gerrit.acceptance.pgm.IndexUpgradeController.UpgradeAttempt;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.index.IndexDefinition;
import com.google.gerrit.index.Schema;
import com.google.gerrit.launcher.GerritLauncher;
import com.google.gerrit.server.index.GerritIndexStatus;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import java.nio.file.Files;
import java.util.Collection;
import java.util.function.Consumer;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.junit.Assume;
import org.junit.Test;
@NoHttpd
public abstract class AbstractReindexTests extends StandaloneSiteTest {
private static final String CHANGES = ChangeSchemaDefinitions.NAME;
private Project.NameKey project;
private String changeId;
@Test
public void reindexFromScratch() throws Exception {
setUpChange();
MoreFiles.deleteRecursively(sitePaths.index_dir, RecursiveDeleteOption.ALLOW_INSECURE);
Files.createDirectory(sitePaths.index_dir);
assertServerStartupFails();
runGerrit("reindex", "-d", sitePaths.site_path.toString(), "--show-stack-trace", "--verbose");
assertReady(ChangeSchemaDefinitions.INSTANCE.getLatest().getVersion());
assertIndexQueries();
}
@Test
public void reindexWithSkipExistingDocumentsEnabled() throws Exception {
updateConfig(config -> config.setBoolean("index", null, "reuseExistingDocuments", true));
setUpChange();
MoreFiles.deleteRecursively(sitePaths.index_dir, RecursiveDeleteOption.ALLOW_INSECURE);
Files.createDirectory(sitePaths.index_dir);
assertServerStartupFails();
runGerrit("reindex", "-d", sitePaths.site_path.toString(), "--show-stack-trace", "--verbose");
assertReady(ChangeSchemaDefinitions.INSTANCE.getLatest().getVersion());
runGerrit("reindex", "-d", sitePaths.site_path.toString(), "--show-stack-trace", "--verbose");
assertIndexQueries();
Files.copy(sitePaths.index_dir, sitePaths.resolve("index-backup"));
try (ServerContext ctx = startServer()) {
GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
gApi.changes().id(changeId).revision(1).review(ReviewInput.approve());
// Query change index
assertThat(gApi.changes().query("label:Code-Review+2").get().stream().map(c -> c.changeId))
.containsExactly(changeId);
}
MoreFiles.deleteRecursively(sitePaths.index_dir, RecursiveDeleteOption.ALLOW_INSECURE);
Files.copy(sitePaths.resolve("index-backup"), sitePaths.index_dir);
runGerrit("reindex", "-d", sitePaths.site_path.toString(), "--show-stack-trace", "--verbose");
try (ServerContext ctx = startServer()) {
GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
assertThat(gApi.changes().query("label:Code-Review+2").get().stream().map(c -> c.changeId))
.containsExactly(changeId);
}
}
private void assertIndexQueries() throws Exception {
try (ServerContext ctx = startServer()) {
GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
// Query change index
assertThat(gApi.changes().query("message:Test").get().stream().map(c -> c.changeId))
.containsExactly(changeId);
// Query account index
assertThat(gApi.accounts().query("admin").get().stream().map(a -> a._accountId))
.containsExactly(admin.id().get());
// Query group index
assertThat(
gApi.groups().query("Group").withOption(MEMBERS).get().stream()
.flatMap(g -> g.members.stream())
.map(a -> a._accountId))
.containsExactly(admin.id().get());
// Query project index
assertThat(gApi.projects().query(project.get()).get().stream().map(p -> p.name))
.containsExactly(project.get());
}
}
@Test
public void offlineReindexForChangesIsNotPossibleInSlaveMode() throws Exception {
enableSlaveMode();
int exitCode =
runGerritAndReturnExitCode(
"reindex",
"--index",
"changes",
"-d",
sitePaths.site_path.toString(),
"--show-stack-trace");
assertWithMessage("Slave hosts shouldn't allow to offline reindex changes")
.that(exitCode)
.isGreaterThan(0);
}
@Test
public void offlineReindexForAccountsIsNotPossibleInSlaveMode() throws Exception {
enableSlaveMode();
int exitCode =
runGerritAndReturnExitCode(
"reindex",
"--index",
"accounts",
"-d",
sitePaths.site_path.toString(),
"--show-stack-trace");
assertWithMessage("Slave hosts shouldn't allow to offline reindex accounts")
.that(exitCode)
.isGreaterThan(0);
}
@Test
public void offlineReindexForProjectsIsNotPossibleInSlaveMode() throws Exception {
enableSlaveMode();
int exitCode =
runGerritAndReturnExitCode(
"reindex",
"--index",
"projects",
"-d",
sitePaths.site_path.toString(),
"--show-stack-trace");
assertWithMessage("Slave hosts shouldn't allow to offline reindex projects")
.that(exitCode)
.isGreaterThan(0);
}
@Test
public void offlineReindexForGroupsIsPossibleInSlaveMode() throws Exception {
enableSlaveMode();
int exitCode =
runGerritAndReturnExitCode(
"reindex",
"--index",
"groups",
"-d",
sitePaths.site_path.toString(),
"--show-stack-trace");
assertWithMessage("Slave hosts should allow to offline reindex groups")
.that(exitCode)
.isEqualTo(0);
}
@Test
public void offlineReindexForAllAvailableIndicesIsPossibleInSlaveMode() throws Exception {
enableSlaveMode();
int exitCode =
runGerritAndReturnExitCode(
"reindex", "-d", sitePaths.site_path.toString(), "--show-stack-trace");
assertWithMessage("Slave hosts should allow to perform a general offline reindex")
.that(exitCode)
.isEqualTo(0);
}
@Test
public void onlineUpgradeChanges() throws Exception {
Schema<ChangeData> previous = ChangeSchemaDefinitions.INSTANCE.getPrevious();
Assume.assumeNotNull(previous);
int prevVersion = previous.getVersion();
int currVersion = ChangeSchemaDefinitions.INSTANCE.getLatest().getVersion();
// Before storing any changes, switch back to the previous version.
GerritIndexStatus status = new GerritIndexStatus(sitePaths);
status.setReady(CHANGES, currVersion, false);
status.setReady(CHANGES, prevVersion, true);
status.save();
assertReady(prevVersion);
setOnlineUpgradeConfig(false);
setUpChange();
setOnlineUpgradeConfig(true);
IndexUpgradeController u = new IndexUpgradeController(1);
try (ServerContext ctx = startServer(u.module())) {
assertSearchVersion(ctx, prevVersion);
assertWriteVersions(ctx, prevVersion, currVersion);
// Updating and searching old schema version works.
Provider<InternalChangeQuery> queryProvider =
ctx.getInjector().getProvider(InternalChangeQuery.class);
assertThat(queryProvider.get().byKey(Change.key(changeId))).hasSize(1);
assertThat(queryProvider.get().byTopicOpen("topic1")).isEmpty();
GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
gApi.changes().id(changeId).topic("topic1");
assertThat(queryProvider.get().byTopicOpen("topic1")).hasSize(1);
u.runUpgrades();
assertThat(u.getStartedAttempts())
.containsExactly(UpgradeAttempt.create(CHANGES, prevVersion, currVersion));
assertThat(u.getSucceededAttempts())
.containsExactly(UpgradeAttempt.create(CHANGES, prevVersion, currVersion));
assertThat(u.getFailedAttempts()).isEmpty();
assertReady(currVersion);
assertSearchVersion(ctx, currVersion);
assertWriteVersions(ctx, currVersion);
// Updating and searching new schema version works.
assertThat(queryProvider.get().byTopicOpen("topic1")).hasSize(1);
assertThat(queryProvider.get().byTopicOpen("topic2")).isEmpty();
gApi.changes().id(changeId).topic("topic2");
assertThat(queryProvider.get().byTopicOpen("topic1")).isEmpty();
assertThat(queryProvider.get().byTopicOpen("topic2")).hasSize(1);
}
}
protected static void createAllIndexes(Injector injector) {
Collection<IndexDefinition<?, ?, ?>> indexDefs =
injector.getInstance(Key.get(new TypeLiteral<Collection<IndexDefinition<?, ?, ?>>>() {}));
for (IndexDefinition<?, ?, ?> indexDef : indexDefs) {
indexDef.getIndexCollection().getSearchIndex().deleteAll();
}
}
private void setUpChange() throws Exception {
project = Project.nameKey("reindex-project-test");
try (ServerContext ctx = startServer()) {
createAllIndexes(ctx.getInjector());
GerritApi gApi = ctx.getInjector().getInstance(GerritApi.class);
gApi.projects().create(project.get());
ChangeInput in = new ChangeInput(project.get(), "master", "Test change");
in.newBranch = true;
changeId = gApi.changes().create(in).info().changeId;
}
}
private void setOnlineUpgradeConfig(boolean enable) throws Exception {
updateConfig(cfg -> cfg.setBoolean("index", null, "onlineUpgrade", enable));
}
private void enableSlaveMode() throws Exception {
updateConfig(config -> config.setBoolean("container", null, "replica", true));
}
private void updateConfig(Consumer<Config> configConsumer) throws Exception {
FileBasedConfig cfg = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.detect());
cfg.load();
configConsumer.accept(cfg);
cfg.save();
}
private static int runGerritAndReturnExitCode(String... args) throws Exception {
return GerritLauncher.mainImpl(args);
}
private void assertSearchVersion(ServerContext ctx, int expected) {
assertWithMessage("search version")
.that(
ctx.getInjector()
.getInstance(ChangeIndexCollection.class)
.getSearchIndex()
.getSchema()
.getVersion())
.isEqualTo(expected);
}
private void assertWriteVersions(ServerContext ctx, Integer... expected) {
assertWithMessage("write versions")
.about(streams())
.that(
ctx.getInjector().getInstance(ChangeIndexCollection.class).getWriteIndexes().stream()
.map(i -> i.getSchema().getVersion()))
.containsExactlyElementsIn(ImmutableSet.copyOf(expected));
}
private void assertReady(int expectedReady) throws Exception {
ImmutableSortedSet<Integer> allVersions =
ChangeSchemaDefinitions.INSTANCE.getSchemas().keySet();
GerritIndexStatus status = new GerritIndexStatus(sitePaths);
assertWithMessage("ready state for index versions")
.that(
allVersions.stream().collect(toImmutableMap(v -> v, v -> status.getReady(CHANGES, v))))
.isEqualTo(allVersions.stream().collect(toImmutableMap(v -> v, v -> v == expectedReady)));
}
}