Index admin user account that is created during init

During the initialization of a new site the InitAdminUser init step
may create an account for the initial admin user. This new account
must be added to the index so that it becomes queryable. For this init
must have the account index available so that it can write the new
account to it. Reading from the index during init is not needed and
not supported.

Alternatively we could require users to run the Reindex program after
the initial site initialization, but likely many people would forget
about it and then wonder why the initial admin user is not working.

Change-Id: Iebee152fa0b28df3fe59da74ad688c4373488c53
Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
Edwin Kempin
2017-01-05 15:32:27 +01:00
parent a4c095f171
commit 1412ccfadf
8 changed files with 109 additions and 45 deletions

View File

@@ -43,6 +43,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
@@ -65,8 +66,8 @@ import io.searchbox.core.Search;
import io.searchbox.core.search.sort.Sort;
import io.searchbox.core.search.sort.Sort.Sorting;
class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, AccountState>
implements AccountIndex {
public class ElasticAccountIndex extends
AbstractElasticIndex<Account.Id, AccountState> implements AccountIndex {
static class AccountMapping {
MappingProperties accounts;
@@ -83,7 +84,7 @@ class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, AccountState>
private final Gson gson;
private final AccountMapping mapping;
private final AccountCache accountCache;
private final Provider<AccountCache> accountCache;
private final ElasticQueryBuilder queryBuilder;
@AssistedInject
@@ -91,7 +92,7 @@ class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, AccountState>
@GerritServerConfig Config cfg,
FillArgs fillArgs,
SitePaths sitePaths,
AccountCache accountCache,
Provider<AccountCache> accountCache,
@Assisted Schema<AccountState> schema) {
super(cfg, fillArgs, sitePaths, schema, ACCOUNTS_PREFIX);
this.accountCache = accountCache;
@@ -228,7 +229,7 @@ class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, AccountState>
// document (of which there shouldn't be any. The most expensive part to
// compute anyway is the effective group IDs, and we don't have a good way
// to reindex when those change.
return accountCache.get(id);
return accountCache.get().get(id);
}
}
}

View File

@@ -31,6 +31,7 @@ import com.google.gerrit.server.query.QueryParseException;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import org.apache.lucene.document.Document;
@@ -77,7 +78,7 @@ public class LuceneAccountIndex
private final GerritIndexWriterConfig indexWriterConfig;
private final QueryBuilder<AccountState> queryBuilder;
private final AccountCache accountCache;
private final Provider<AccountCache> accountCache;
private static Directory dir(Schema<AccountState> schema, Config cfg,
SitePaths sitePaths) throws IOException {
@@ -93,7 +94,7 @@ public class LuceneAccountIndex
LuceneAccountIndex(
@GerritServerConfig Config cfg,
SitePaths sitePaths,
AccountCache accountCache,
Provider<AccountCache> accountCache,
@Assisted Schema<AccountState> schema) throws IOException {
super(schema, sitePaths, dir(schema, cfg, sitePaths), ACCOUNTS, null,
new GerritIndexWriterConfig(cfg, ACCOUNTS), new SearcherFactory());
@@ -203,6 +204,6 @@ public class LuceneAccountIndex
// document (of which there shouldn't be any. The most expensive part to
// compute anyway is the effective group IDs, and we don't have a good way
// to reindex when those change.
return accountCache.get(id);
return accountCache.get().get(id);
}
}

View File

@@ -131,10 +131,18 @@ public class BaseInit extends SiteProgram {
init.initializer.run();
init.flags.deleteOnFailure = false;
run = createSiteRun(init);
run.upgradeSchema();
Injector sysInjector = createSysInjector(init);
IndexManagerOnInit indexManager =
sysInjector.getInstance(IndexManagerOnInit.class);
try {
indexManager.start();
run = createSiteRun(init);
run.upgradeSchema();
init.initializer.postRun(createSysInjector(init));
init.initializer.postRun(sysInjector);
} finally {
indexManager.stop();
}
} catch (Exception | Error failure) {
if (init.flags.deleteOnFailure) {
recursiveDelete(getSitePath());
@@ -347,7 +355,6 @@ public class BaseInit extends SiteProgram {
final SchemaUpdater schemaUpdater;
final SchemaFactory<ReviewDb> schema;
final GitRepositoryManager repositoryManager;
final IndexManagerOnInit indexManager;
@Inject
SiteRun(ConsoleUI ui,
@@ -355,50 +362,43 @@ public class BaseInit extends SiteProgram {
InitFlags flags,
SchemaUpdater schemaUpdater,
SchemaFactory<ReviewDb> schema,
GitRepositoryManager repositoryManager,
IndexManagerOnInit indexManager) {
GitRepositoryManager repositoryManager) {
this.ui = ui;
this.site = site;
this.flags = flags;
this.schemaUpdater = schemaUpdater;
this.schema = schema;
this.repositoryManager = repositoryManager;
this.indexManager = indexManager;
}
void upgradeSchema() throws OrmException {
final List<String> pruneList = new ArrayList<>();
try {
indexManager.start();
schemaUpdater.update(new UpdateUI() {
@Override
public void message(String msg) {
System.err.println(msg);
System.err.flush();
}
schemaUpdater.update(new UpdateUI() {
@Override
public void message(String msg) {
System.err.println(msg);
System.err.flush();
}
@Override
public boolean yesno(boolean def, String msg) {
return ui.yesno(def, msg);
}
@Override
public boolean yesno(boolean def, String msg) {
return ui.yesno(def, msg);
}
@Override
public boolean isBatch() {
return ui.isBatch();
}
@Override
public boolean isBatch() {
return ui.isBatch();
}
@Override
public void pruneSchema(StatementExecutor e, List<String> prune) {
for (String p : prune) {
if (!pruneList.contains(p)) {
pruneList.add(p);
}
@Override
public void pruneSchema(StatementExecutor e, List<String> prune) {
for (String p : prune) {
if (!pruneList.contains(p)) {
pruneList.add(p);
}
}
});
} finally {
indexManager.stop();
}
}
});
if (!pruneList.isEmpty()) {
StringBuilder msg = new StringBuilder();

View File

@@ -29,6 +29,8 @@ import com.google.gerrit.reviewdb.client.AccountGroupMember;
import com.google.gerrit.reviewdb.client.AccountGroupName;
import com.google.gerrit.reviewdb.client.AccountSshKey;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.index.account.AccountIndexCollection;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
@@ -38,13 +40,17 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
public class InitAdminUser implements InitStep {
private final ConsoleUI ui;
private final InitFlags flags;
private final VersionedAuthorizedKeysOnInit.Factory authorizedKeysFactory;
private SchemaFactory<ReviewDb> dbFactory;
private AccountIndexCollection indexCollection;
@Inject
InitAdminUser(
@@ -65,6 +71,11 @@ public class InitAdminUser implements InitStep {
this.dbFactory = dbFactory;
}
@Inject(optional = true)
void set(AccountIndexCollection indexCollection) {
this.indexCollection = indexCollection;
}
@Override
public void postRun() throws Exception {
AuthType authType =
@@ -84,12 +95,14 @@ public class InitAdminUser implements InitStep {
AccountSshKey sshKey = readSshKey(id);
String email = readEmail(sshKey);
List<AccountExternalId> extIds = new ArrayList<>(2);
AccountExternalId extUser =
new AccountExternalId(id, new AccountExternalId.Key(
AccountExternalId.SCHEME_USERNAME, username));
if (!Strings.isNullOrEmpty(httpPassword)) {
extUser.setPassword(httpPassword);
}
extIds.add(extUser);
db.accountExternalIds().insert(Collections.singleton(extUser));
if (email != null) {
@@ -97,6 +110,7 @@ public class InitAdminUser implements InitStep {
new AccountExternalId(id, new AccountExternalId.Key(
AccountExternalId.SCHEME_MAILTO, email));
extMailto.setEmailAddress(email);
extIds.add(extMailto);
db.accountExternalIds().insert(Collections.singleton(extMailto));
}
@@ -105,11 +119,11 @@ public class InitAdminUser implements InitStep {
a.setPreferredEmail(email);
db.accounts().insert(Collections.singleton(a));
AccountGroupName adminGroup = db.accountGroupNames().get(
AccountGroupName adminGroupName = db.accountGroupNames().get(
new AccountGroup.NameKey("Administrators"));
AccountGroupMember m =
new AccountGroupMember(new AccountGroupMember.Key(id,
adminGroup.getId()));
adminGroupName.getId()));
db.accountGroupMembers().insert(Collections.singleton(m));
if (sshKey != null) {
@@ -118,6 +132,13 @@ public class InitAdminUser implements InitStep {
authorizedKeys.addKey(sshKey.getSshPublicKey());
authorizedKeys.save("Added SSH key for initial admin user\n");
}
AccountGroup adminGroup =
db.accountGroups().get(adminGroupName.getId());
AccountState as = new AccountState(a,
Collections.singleton(adminGroup.getGroupUUID()), extIds,
new HashMap<>());
indexCollection.getSearchIndex().replace(as);
}
}
}

View File

@@ -15,14 +15,20 @@
package com.google.gerrit.pgm.init.index;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.index.IndexDefinition;
import com.google.gerrit.server.index.SchemaDefinitions;
import com.google.gerrit.server.index.SingleVersionModule;
import com.google.gerrit.server.index.SingleVersionModule.SingleVersionListener;
import com.google.gerrit.server.index.account.AccountIndexCollection;
import com.google.gerrit.server.index.account.AccountIndexDefinition;
import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
import com.google.gerrit.server.index.account.AllAccountsIndexer;
import com.google.gerrit.server.index.group.AllGroupsIndexer;
import com.google.gerrit.server.index.group.GroupIndexCollection;
import com.google.gerrit.server.index.group.GroupIndexDefinition;
@@ -41,8 +47,26 @@ import java.util.Set;
public class IndexModuleOnInit extends AbstractModule {
static final String INDEX_MANAGER = "IndexModuleOnInit/IndexManager";
private static final ImmutableCollection<SchemaDefinitions<?>> ALL_SCHEMA_DEFS =
ImmutableList.<SchemaDefinitions<?>> of(
AccountSchemaDefinitions.INSTANCE,
GroupSchemaDefinitions.INSTANCE);
@Override
protected void configure() {
// The AccountIndex implementations (LuceneAccountIndex and
// ElasticAccountIndex) need AccountCache only for reading from the index.
// On init we only want to write to the index, hence we don't need the
// account cache.
bind(AccountCache.class).toProvider(Providers.of(null));
// AccountIndexDefinition wants to have AllAccountsIndexer but it is only
// used by the Reindex program and the OnlineReindexer which are both not
// used during init, hence we don't need AllAccountsIndexer.
bind(AllAccountsIndexer.class).toProvider(Providers.of(null));
bind(AccountIndexCollection.class);
// The GroupIndex implementations (LuceneGroupIndex and ElasticGroupIndex)
// need GroupCache only for reading from the index. On init we only want to
// write to the index, hence we don't need the group cache.
@@ -64,12 +88,14 @@ public class IndexModuleOnInit extends AbstractModule {
@Provides
Collection<IndexDefinition<?, ?, ?>> getIndexDefinitions(
AccountIndexDefinition accounts,
GroupIndexDefinition groups) {
Collection<IndexDefinition<?, ?, ?>> result =
ImmutableList.<IndexDefinition<?, ?, ?>> of(
accounts,
groups);
Set<String> expected =
FluentIterable.of(GroupSchemaDefinitions.INSTANCE)
FluentIterable.from(ALL_SCHEMA_DEFS)
.transform(SchemaDefinitions::getName)
.toSet();
Set<String> actual = FluentIterable.from(result)

View File

@@ -14,8 +14,10 @@
package com.google.gerrit.pgm.init.index.elasticsearch;
import com.google.gerrit.elasticsearch.ElasticAccountIndex;
import com.google.gerrit.elasticsearch.ElasticGroupIndex;
import com.google.gerrit.pgm.init.index.IndexModuleOnInit;
import com.google.gerrit.server.index.account.AccountIndex;
import com.google.gerrit.server.index.group.GroupIndex;
import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryModuleBuilder;
@@ -24,6 +26,11 @@ public class ElasticIndexModuleOnInit extends AbstractModule {
@Override
protected void configure() {
install(
new FactoryModuleBuilder()
.implement(AccountIndex.class, ElasticAccountIndex.class)
.build(AccountIndex.Factory.class));
install(
new FactoryModuleBuilder()
.implement(GroupIndex.class, ElasticGroupIndex.class)

View File

@@ -14,8 +14,10 @@
package com.google.gerrit.pgm.init.index.lucene;
import com.google.gerrit.lucene.LuceneAccountIndex;
import com.google.gerrit.lucene.LuceneGroupIndex;
import com.google.gerrit.pgm.init.index.IndexModuleOnInit;
import com.google.gerrit.server.index.account.AccountIndex;
import com.google.gerrit.server.index.group.GroupIndex;
import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryModuleBuilder;
@@ -23,6 +25,11 @@ import com.google.inject.assistedinject.FactoryModuleBuilder;
public class LuceneIndexModuleOnInit extends AbstractModule {
@Override
protected void configure() {
install(
new FactoryModuleBuilder()
.implement(AccountIndex.class, LuceneAccountIndex.class)
.build(AccountIndex.Factory.class));
install(
new FactoryModuleBuilder()
.implement(GroupIndex.class, LuceneGroupIndex.class)

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.server.index.account;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.index.IndexDefinition;
@@ -27,7 +28,7 @@ public class AccountIndexDefinition
AccountIndexDefinition(
AccountIndexCollection indexCollection,
AccountIndex.Factory indexFactory,
AllAccountsIndexer allAccountsIndexer) {
@Nullable AllAccountsIndexer allAccountsIndexer) {
super(AccountSchemaDefinitions.INSTANCE, indexCollection, indexFactory,
Providers.of(allAccountsIndexer));
}