Migrate accounts to NoteDb (part 1)
Always write account updates to both backends, ReviewDb and NoteDb.
In NoteDb accounts are represented as user branches in the All-Users
repository. Optionally a user branch can contain a 'account.config' file
that stores account properties, such as full name, preferred email,
status and the active flag. The timestamp of the first commit on a user
branch denotes the registration date. The initial commit on the user
branch may be empty (since having an 'account.config' is optional).
The 'account.config' file is a git config file that has one 'account'
section with the properties of the account:
[account]
active = false
fullName = John Doe
preferredEmail = john.doe@foo.com
status = Overloaded with reviews
All keys are optional. This means 'account.config' may not exist on the
user branch if no properties are set.
If no value for 'active' is specified, by default the account is
considered as active.
AccountsUpdate is now sending RefUpdatedEvent's when an account is
updated. ReindexAfterRefUpdate receives the events and takes care to
evict the updated accounts from the account cache, which in turn
triggers reindex of the accounts. This is why AccountsUpdate no longer
needs to evict the updated accounts itself from the account cache. Since
AccountsUpdate doesn't reindex accounts on its own anymore the
ServerNoReindex factory can be removed.
To support a live migration on a multi-master Gerrit installation, the
migration of accounts from ReviewDb to NoteDb is done in 3 steps:
- part 1 (this change):
* always write to both backends (ReviewDb and NoteDb)
* always read accounts from ReviewDb
* upgraded instances write to both backends, old instances only
write to ReviewDb
* after upgrading all instances (all still read from ReviewDb)
run a batch to copy all accounts from the ReviewDb to NoteDb
- part 2 (next change):
* bump the database schema version
* migrate the accounts from ReviewDb to NoteDb (for single instance
Gerrit servers)
* config option to control whether accounts are read from ReviewDb or
NoteDb
- part 3:
* remove config option to control whether accounts are read from
ReviewDb or NoteDb and always read from NoteDb
* delete the database table
Change-Id: I2e0b13feb3465e086b49b2de2439a56696b5fba9
Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
@@ -32,6 +32,7 @@ import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
|
||||
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.common.collect.FluentIterable;
|
||||
@@ -74,6 +75,7 @@ import com.google.gerrit.gpg.testutil.TestKey;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.account.AccountByEmailCache;
|
||||
import com.google.gerrit.server.account.AccountConfig;
|
||||
import com.google.gerrit.server.account.WatchConfig;
|
||||
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||
@@ -106,6 +108,7 @@ import org.eclipse.jgit.api.errors.TransportException;
|
||||
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.ObjectReader;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.RefUpdate;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
@@ -114,6 +117,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.transport.PushCertificateIdent;
|
||||
import org.eclipse.jgit.transport.PushResult;
|
||||
import org.eclipse.jgit.transport.RemoteRefUpdate;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
@@ -220,6 +224,67 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
create(3); // account creation + external ID creation + adding SSH keys
|
||||
}
|
||||
|
||||
private void create(int expectedAccountReindexCalls) throws Exception {
|
||||
String name = "foo";
|
||||
TestAccount foo = accountCreator.create(name);
|
||||
AccountInfo info = gApi.accounts().id(foo.id.get()).get();
|
||||
assertThat(info.username).isEqualTo(name);
|
||||
assertThat(info.name).isEqualTo(name);
|
||||
accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls);
|
||||
|
||||
// check user branch
|
||||
try (Repository repo = repoManager.openRepository(allUsers);
|
||||
RevWalk rw = new RevWalk(repo);
|
||||
ObjectReader or = repo.newObjectReader()) {
|
||||
Ref ref = repo.exactRef(RefNames.refsUsers(foo.getId()));
|
||||
assertThat(ref).isNotNull();
|
||||
RevCommit c = rw.parseCommit(ref.getObjectId());
|
||||
long timestampDiffMs =
|
||||
Math.abs(
|
||||
c.getCommitTime() * 1000L
|
||||
- accountCache.get(foo.getId()).getAccount().getRegisteredOn().getTime());
|
||||
assertThat(timestampDiffMs).isAtMost(ChangeRebuilderImpl.MAX_WINDOW_MS);
|
||||
|
||||
// Check the 'account.config' file.
|
||||
try (TreeWalk tw = TreeWalk.forPath(or, AccountConfig.ACCOUNT_CONFIG, c.getTree())) {
|
||||
assertThat(tw).isNotNull();
|
||||
Config cfg = new Config();
|
||||
cfg.fromText(new String(or.open(tw.getObjectId(0), OBJ_BLOB).getBytes(), UTF_8));
|
||||
assertThat(cfg.getString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_FULL_NAME))
|
||||
.isEqualTo(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAnonymousCoward() throws Exception {
|
||||
TestAccount anonymousCoward = accountCreator.create();
|
||||
accountIndexedCounter.assertReindexOf(anonymousCoward);
|
||||
|
||||
// check user branch
|
||||
try (Repository repo = repoManager.openRepository(allUsers);
|
||||
RevWalk rw = new RevWalk(repo);
|
||||
ObjectReader or = repo.newObjectReader()) {
|
||||
Ref ref = repo.exactRef(RefNames.refsUsers(anonymousCoward.getId()));
|
||||
assertThat(ref).isNotNull();
|
||||
RevCommit c = rw.parseCommit(ref.getObjectId());
|
||||
long timestampDiffMs =
|
||||
Math.abs(
|
||||
c.getCommitTime() * 1000L
|
||||
- accountCache
|
||||
.get(anonymousCoward.getId())
|
||||
.getAccount()
|
||||
.getRegisteredOn()
|
||||
.getTime());
|
||||
assertThat(timestampDiffMs).isAtMost(ChangeRebuilderImpl.MAX_WINDOW_MS);
|
||||
|
||||
// No account properties were set, hence an 'account.config' file was not created.
|
||||
try (TreeWalk tw = TreeWalk.forPath(or, AccountConfig.ACCOUNT_CONFIG, c.getTree())) {
|
||||
assertThat(tw).isNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void get() throws Exception {
|
||||
AccountInfo info = gApi.accounts().id("admin").get();
|
||||
@@ -1071,26 +1136,6 @@ public class AccountIT extends AbstractDaemonTest {
|
||||
assertThat(checkInfo.checkAccountsResult.problems).containsExactlyElementsIn(expectedProblems);
|
||||
}
|
||||
|
||||
public void create(int expectedAccountReindexCalls) throws Exception {
|
||||
TestAccount foo = accountCreator.create("foo");
|
||||
AccountInfo info = gApi.accounts().id(foo.id.get()).get();
|
||||
assertThat(info.username).isEqualTo("foo");
|
||||
accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls);
|
||||
|
||||
// check user branch
|
||||
try (Repository repo = repoManager.openRepository(allUsers);
|
||||
RevWalk rw = new RevWalk(repo)) {
|
||||
Ref ref = repo.exactRef(RefNames.refsUsers(foo.getId()));
|
||||
assertThat(ref).isNotNull();
|
||||
RevCommit c = rw.parseCommit(ref.getObjectId());
|
||||
long timestampDiffMs =
|
||||
Math.abs(
|
||||
c.getCommitTime() * 1000L
|
||||
- accountCache.get(foo.getId()).getAccount().getRegisteredOn().getTime());
|
||||
assertThat(timestampDiffMs).isAtMost(ChangeRebuilderImpl.MAX_WINDOW_MS);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertSequenceNumbers(List<SshKeyInfo> sshKeys) {
|
||||
int seq = 1;
|
||||
for (SshKeyInfo key : sshKeys) {
|
||||
|
||||
@@ -19,11 +19,13 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.pgm.init.api.InitFlags;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdentProvider;
|
||||
import com.google.gerrit.server.account.Accounts;
|
||||
import com.google.gerrit.server.account.AccountsUpdate;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import java.io.File;
|
||||
@@ -57,7 +59,15 @@ public class AccountsOnInit {
|
||||
ObjectInserter oi = repo.newObjectInserter()) {
|
||||
PersonIdent serverIdent = new GerritPersonIdentProvider(flags.cfg).get();
|
||||
AccountsUpdate.createUserBranch(
|
||||
repo, oi, serverIdent, serverIdent, account.getId(), account.getRegisteredOn());
|
||||
repo,
|
||||
new Project.NameKey(allUsers),
|
||||
GitReferenceUpdated.DISABLED,
|
||||
null,
|
||||
oi,
|
||||
serverIdent,
|
||||
serverIdent,
|
||||
account.getId(),
|
||||
account.getRegisteredOn());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
// Copyright (C) 2017 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.account;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.git.ValidationError;
|
||||
import com.google.gerrit.server.git.VersionedMetaData;
|
||||
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
|
||||
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.revwalk.RevSort;
|
||||
|
||||
/**
|
||||
* ‘account.config’ file in the user branch in the All-Users repository that contains the properties
|
||||
* of the account.
|
||||
*
|
||||
* <p>The 'account.config' file is a git config file that has one 'account' section with the
|
||||
* properties of the account:
|
||||
*
|
||||
* <pre>
|
||||
* [account]
|
||||
* active = false
|
||||
* fullName = John Doe
|
||||
* preferredEmail = john.doe@foo.com
|
||||
* status = Overloaded with reviews
|
||||
* </pre>
|
||||
*
|
||||
* <p>All keys are optional. This means 'account.config' may not exist on the user branch if no
|
||||
* properties are set.
|
||||
*
|
||||
* <p>Not setting a key and setting a key to an empty string are treated the same way and result in
|
||||
* a {@code null} value.
|
||||
*
|
||||
* <p>If no value for 'active' is specified, by default the account is considered as active.
|
||||
*
|
||||
* <p>The commit date of the first commit on the user branch is used as registration date of the
|
||||
* account. The first commit may be an empty commit (if no properties were set and 'account.config'
|
||||
* doesn't exist).
|
||||
*/
|
||||
public class AccountConfig extends VersionedMetaData implements ValidationError.Sink {
|
||||
public static final String ACCOUNT_CONFIG = "account.config";
|
||||
public static final String ACCOUNT = "account";
|
||||
public static final String KEY_ACTIVE = "active";
|
||||
public static final String KEY_FULL_NAME = "fullName";
|
||||
public static final String KEY_PREFERRED_EMAIL = "preferredEmail";
|
||||
public static final String KEY_STATUS = "status";
|
||||
|
||||
@Nullable private final OutgoingEmailValidator emailValidator;
|
||||
private final Account.Id accountId;
|
||||
private final String ref;
|
||||
|
||||
private boolean isLoaded;
|
||||
private Account account;
|
||||
private Timestamp registeredOn;
|
||||
private List<ValidationError> validationErrors;
|
||||
|
||||
public AccountConfig(@Nullable OutgoingEmailValidator emailValidator, Account.Id accountId) {
|
||||
this.emailValidator = emailValidator;
|
||||
this.accountId = accountId;
|
||||
this.ref = RefNames.refsUsers(accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRefName() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the loaded account.
|
||||
*
|
||||
* @return loaded account.
|
||||
* @throws IllegalStateException if the account was not loaded yet
|
||||
*/
|
||||
public Account getAccount() {
|
||||
checkLoaded();
|
||||
return account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the account. This means the loaded account will be overwritten with the given account.
|
||||
*
|
||||
* <p>Changing the registration date of an account is not supported.
|
||||
*
|
||||
* @param account account that should be set
|
||||
* @throws IllegalStateException if the account was not loaded yet
|
||||
*/
|
||||
public void setAccount(Account account) {
|
||||
checkLoaded();
|
||||
this.account = account;
|
||||
this.registeredOn = account.getRegisteredOn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new account.
|
||||
*
|
||||
* @return the new account
|
||||
* @throws OrmDuplicateKeyException if the user branch already exists
|
||||
*/
|
||||
public Account getNewAccount() throws OrmDuplicateKeyException {
|
||||
checkLoaded();
|
||||
if (revision != null) {
|
||||
throw new OrmDuplicateKeyException(String.format("account %s already exists", accountId));
|
||||
}
|
||||
this.registeredOn = TimeUtil.nowTs();
|
||||
this.account = new Account(accountId, registeredOn);
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLoad() throws IOException, ConfigInvalidException {
|
||||
if (revision != null) {
|
||||
rw.markStart(revision);
|
||||
rw.sort(RevSort.REVERSE);
|
||||
registeredOn = new Timestamp(rw.next().getCommitTime() * 1000L);
|
||||
|
||||
Config cfg = readConfig(ACCOUNT_CONFIG);
|
||||
|
||||
account = parse(cfg);
|
||||
}
|
||||
|
||||
isLoaded = true;
|
||||
}
|
||||
|
||||
private Account parse(Config cfg) {
|
||||
Account account = new Account(accountId, registeredOn);
|
||||
account.setActive(cfg.getBoolean(ACCOUNT, null, KEY_ACTIVE, true));
|
||||
account.setFullName(get(cfg, KEY_FULL_NAME));
|
||||
|
||||
String preferredEmail = get(cfg, KEY_PREFERRED_EMAIL);
|
||||
account.setPreferredEmail(preferredEmail);
|
||||
if (emailValidator != null && !emailValidator.isValid(preferredEmail)) {
|
||||
error(
|
||||
new ValidationError(
|
||||
ACCOUNT_CONFIG, String.format("Invalid preferred email: %s", preferredEmail)));
|
||||
}
|
||||
|
||||
account.setStatus(get(cfg, KEY_STATUS));
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
|
||||
checkLoaded();
|
||||
|
||||
if (revision != null) {
|
||||
commit.setMessage("Update account\n");
|
||||
} else if (account != null) {
|
||||
commit.setMessage("Create account\n");
|
||||
commit.setAuthor(new PersonIdent(commit.getAuthor(), registeredOn));
|
||||
commit.setCommitter(new PersonIdent(commit.getCommitter(), registeredOn));
|
||||
}
|
||||
|
||||
Config cfg = readConfig(ACCOUNT_CONFIG);
|
||||
setActive(cfg, account.isActive());
|
||||
set(cfg, KEY_FULL_NAME, account.getFullName());
|
||||
set(cfg, KEY_PREFERRED_EMAIL, account.getPreferredEmail());
|
||||
set(cfg, KEY_STATUS, account.getStatus());
|
||||
saveConfig(ACCOUNT_CONFIG, cfg);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets/Unsets {@code account.active} in the given config.
|
||||
*
|
||||
* <p>{@code account.active} is set to {@code false} if the account is inactive.
|
||||
*
|
||||
* <p>If the account is active {@code account.active} is unset since {@code true} is the default
|
||||
* if this field is missing.
|
||||
*
|
||||
* @param cfg the config
|
||||
* @param value whether the account is active
|
||||
*/
|
||||
private static void setActive(Config cfg, boolean value) {
|
||||
if (!value) {
|
||||
cfg.setBoolean(ACCOUNT, null, KEY_ACTIVE, false);
|
||||
} else {
|
||||
cfg.unset(ACCOUNT, null, KEY_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets/Unsets the given key in the given config.
|
||||
*
|
||||
* <p>The key unset if the value is {@code null}.
|
||||
*
|
||||
* @param cfg the config
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
private static void set(Config cfg, String key, String value) {
|
||||
if (!Strings.isNullOrEmpty(value)) {
|
||||
cfg.setString(ACCOUNT, null, key, value);
|
||||
} else {
|
||||
cfg.unset(ACCOUNT, null, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the given key from the given config.
|
||||
*
|
||||
* <p>Empty values are returned as {@code null}
|
||||
*
|
||||
* @param cfg the config
|
||||
* @param key the key
|
||||
* @return the value, {@code null} if key was not set or key was set to empty string
|
||||
*/
|
||||
private static String get(Config cfg, String key) {
|
||||
return Strings.emptyToNull(cfg.getString(ACCOUNT, null, key));
|
||||
}
|
||||
|
||||
private void checkLoaded() {
|
||||
checkState(isLoaded, "account not loaded yet");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation errors, if any were discovered during load.
|
||||
*
|
||||
* @return list of errors; empty list if there are no errors.
|
||||
*/
|
||||
public List<ValidationError> getValidationErrors() {
|
||||
if (validationErrors != null) {
|
||||
return ImmutableList.copyOf(validationErrors);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(ValidationError error) {
|
||||
if (validationErrors == null) {
|
||||
validationErrors = new ArrayList<>(4);
|
||||
}
|
||||
validationErrors.add(error);
|
||||
}
|
||||
}
|
||||
@@ -18,14 +18,18 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
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.MetaDataUpdate;
|
||||
import com.google.gerrit.server.index.change.ReindexAfterRefUpdate;
|
||||
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
|
||||
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
@@ -34,6 +38,7 @@ import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.function.Consumer;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
@@ -47,8 +52,18 @@ import org.eclipse.jgit.lib.Repository;
|
||||
/**
|
||||
* Updates accounts.
|
||||
*
|
||||
* <p>On updating accounts this class takes care to evict them from the account cache and thus
|
||||
* triggers reindex for them.
|
||||
* <p>The account updates are written to both ReviewDb and NoteDb.
|
||||
*
|
||||
* <p>In NoteDb accounts are represented as user branches in the All-Users repository. Optionally a
|
||||
* user branch can contain a 'account.config' file that stores account properties, such as full
|
||||
* name, preferred email, status and the active flag. The timestamp of the first commit on a user
|
||||
* branch denotes the registration date. The initial commit on the user branch may be empty (since
|
||||
* having an 'account.config' is optional). See {@link AccountConfig} for details of the
|
||||
* 'account.config' file format.
|
||||
*
|
||||
* <p>On updating accounts the accounts are evicted from the account cache and thus reindexed. The
|
||||
* eviction from the account cache is done by the {@link ReindexAfterRefUpdate} class which receives
|
||||
* the event about updating the user branch that is triggered by this class.
|
||||
*/
|
||||
@Singleton
|
||||
public class AccountsUpdate {
|
||||
@@ -61,56 +76,38 @@ public class AccountsUpdate {
|
||||
@Singleton
|
||||
public static class Server {
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final AccountCache accountCache;
|
||||
private final GitReferenceUpdated gitRefUpdated;
|
||||
private final AllUsersName allUsersName;
|
||||
private final OutgoingEmailValidator emailValidator;
|
||||
private final Provider<PersonIdent> serverIdent;
|
||||
private final Provider<MetaDataUpdate.Server> metaDataUpdateServerFactory;
|
||||
|
||||
@Inject
|
||||
public Server(
|
||||
GitRepositoryManager repoManager,
|
||||
AccountCache accountCache,
|
||||
GitReferenceUpdated gitRefUpdated,
|
||||
AllUsersName allUsersName,
|
||||
@GerritPersonIdent Provider<PersonIdent> serverIdent) {
|
||||
OutgoingEmailValidator emailValidator,
|
||||
@GerritPersonIdent Provider<PersonIdent> serverIdent,
|
||||
Provider<MetaDataUpdate.Server> metaDataUpdateServerFactory) {
|
||||
this.repoManager = repoManager;
|
||||
this.accountCache = accountCache;
|
||||
this.gitRefUpdated = gitRefUpdated;
|
||||
this.allUsersName = allUsersName;
|
||||
this.emailValidator = emailValidator;
|
||||
this.serverIdent = serverIdent;
|
||||
this.metaDataUpdateServerFactory = metaDataUpdateServerFactory;
|
||||
}
|
||||
|
||||
public AccountsUpdate create() {
|
||||
PersonIdent i = serverIdent.get();
|
||||
return new AccountsUpdate(repoManager, accountCache, allUsersName, i, i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory to create an AccountsUpdate instance for updating accounts by the Gerrit server.
|
||||
*
|
||||
* <p>Using this class will not perform reindexing for the updated accounts and they will also not
|
||||
* be evicted from the account cache.
|
||||
*
|
||||
* <p>The Gerrit server identity will be used as author and committer for all commits that update
|
||||
* the accounts.
|
||||
*/
|
||||
@Singleton
|
||||
public static class ServerNoReindex {
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final AllUsersName allUsersName;
|
||||
private final Provider<PersonIdent> serverIdent;
|
||||
|
||||
@Inject
|
||||
public ServerNoReindex(
|
||||
GitRepositoryManager repoManager,
|
||||
AllUsersName allUsersName,
|
||||
@GerritPersonIdent Provider<PersonIdent> serverIdent) {
|
||||
this.repoManager = repoManager;
|
||||
this.allUsersName = allUsersName;
|
||||
this.serverIdent = serverIdent;
|
||||
}
|
||||
|
||||
public AccountsUpdate create() {
|
||||
PersonIdent i = serverIdent.get();
|
||||
return new AccountsUpdate(repoManager, null, allUsersName, i, i);
|
||||
return new AccountsUpdate(
|
||||
repoManager,
|
||||
gitRefUpdated,
|
||||
null,
|
||||
allUsersName,
|
||||
emailValidator,
|
||||
i,
|
||||
() -> metaDataUpdateServerFactory.get().create(allUsersName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,29 +120,42 @@ public class AccountsUpdate {
|
||||
@Singleton
|
||||
public static class User {
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final AccountCache accountCache;
|
||||
private final GitReferenceUpdated gitRefUpdated;
|
||||
private final AllUsersName allUsersName;
|
||||
private final OutgoingEmailValidator emailValidator;
|
||||
private final Provider<PersonIdent> serverIdent;
|
||||
private final Provider<IdentifiedUser> identifiedUser;
|
||||
private final Provider<MetaDataUpdate.User> metaDataUpdateUserFactory;
|
||||
|
||||
@Inject
|
||||
public User(
|
||||
GitRepositoryManager repoManager,
|
||||
AccountCache accountCache,
|
||||
GitReferenceUpdated gitRefUpdated,
|
||||
AllUsersName allUsersName,
|
||||
OutgoingEmailValidator emailValidator,
|
||||
@GerritPersonIdent Provider<PersonIdent> serverIdent,
|
||||
Provider<IdentifiedUser> identifiedUser) {
|
||||
Provider<IdentifiedUser> identifiedUser,
|
||||
Provider<MetaDataUpdate.User> metaDataUpdateUserFactory) {
|
||||
this.repoManager = repoManager;
|
||||
this.accountCache = accountCache;
|
||||
this.gitRefUpdated = gitRefUpdated;
|
||||
this.allUsersName = allUsersName;
|
||||
this.serverIdent = serverIdent;
|
||||
this.emailValidator = emailValidator;
|
||||
this.identifiedUser = identifiedUser;
|
||||
this.metaDataUpdateUserFactory = metaDataUpdateUserFactory;
|
||||
}
|
||||
|
||||
public AccountsUpdate create() {
|
||||
IdentifiedUser user = identifiedUser.get();
|
||||
PersonIdent i = serverIdent.get();
|
||||
return new AccountsUpdate(
|
||||
repoManager, accountCache, allUsersName, createPersonIdent(i, identifiedUser.get()), i);
|
||||
repoManager,
|
||||
gitRefUpdated,
|
||||
user,
|
||||
allUsersName,
|
||||
emailValidator,
|
||||
createPersonIdent(i, user),
|
||||
() -> metaDataUpdateUserFactory.get().create(allUsersName));
|
||||
}
|
||||
|
||||
private PersonIdent createPersonIdent(PersonIdent ident, IdentifiedUser user) {
|
||||
@@ -154,22 +164,28 @@ public class AccountsUpdate {
|
||||
}
|
||||
|
||||
private final GitRepositoryManager repoManager;
|
||||
@Nullable private final AccountCache accountCache;
|
||||
private final GitReferenceUpdated gitRefUpdated;
|
||||
@Nullable private final IdentifiedUser currentUser;
|
||||
private final AllUsersName allUsersName;
|
||||
private final OutgoingEmailValidator emailValidator;
|
||||
private final PersonIdent committerIdent;
|
||||
private final PersonIdent authorIdent;
|
||||
private final MetaDataUpdateFactory metaDataUpdateFactory;
|
||||
|
||||
private AccountsUpdate(
|
||||
GitRepositoryManager repoManager,
|
||||
@Nullable AccountCache accountCache,
|
||||
GitReferenceUpdated gitRefUpdated,
|
||||
@Nullable IdentifiedUser currentUser,
|
||||
AllUsersName allUsersName,
|
||||
OutgoingEmailValidator emailValidator,
|
||||
PersonIdent committerIdent,
|
||||
PersonIdent authorIdent) {
|
||||
MetaDataUpdateFactory metaDataUpdateFactory) {
|
||||
this.repoManager = checkNotNull(repoManager, "repoManager");
|
||||
this.accountCache = accountCache;
|
||||
this.gitRefUpdated = checkNotNull(gitRefUpdated, "gitRefUpdated");
|
||||
this.currentUser = currentUser;
|
||||
this.allUsersName = checkNotNull(allUsersName, "allUsersName");
|
||||
this.emailValidator = checkNotNull(emailValidator, "emailValidator");
|
||||
this.committerIdent = checkNotNull(committerIdent, "committerIdent");
|
||||
this.authorIdent = checkNotNull(authorIdent, "authorIdent");
|
||||
this.metaDataUpdateFactory = checkNotNull(metaDataUpdateFactory, "metaDataUpdateFactory");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,37 +195,63 @@ public class AccountsUpdate {
|
||||
* @param accountId ID of the new account
|
||||
* @param init consumer to populate the new account
|
||||
* @return the newly created account
|
||||
* @throws OrmException if updating the database fails
|
||||
* @throws OrmDuplicateKeyException if the account already exists
|
||||
* @throws IOException if updating the user branch fails
|
||||
* @throws ConfigInvalidException if any of the account fields has an invalid value
|
||||
*/
|
||||
public Account insert(ReviewDb db, Account.Id accountId, Consumer<Account> init)
|
||||
throws OrmException, IOException {
|
||||
Account account = new Account(accountId, TimeUtil.nowTs());
|
||||
throws OrmException, IOException, ConfigInvalidException {
|
||||
AccountConfig accountConfig = read(accountId);
|
||||
Account account = accountConfig.getNewAccount();
|
||||
init.accept(account);
|
||||
|
||||
// Create in ReviewDb
|
||||
db.accounts().insert(ImmutableSet.of(account));
|
||||
createUserBranch(account);
|
||||
evictAccount(accountId);
|
||||
|
||||
// Create in NoteDb
|
||||
commitNew(accountConfig);
|
||||
return account;
|
||||
}
|
||||
|
||||
/** Updates the account. */
|
||||
public void update(ReviewDb db, Account account) throws OrmException, IOException {
|
||||
/**
|
||||
* Updates the account.
|
||||
*
|
||||
* <p>Changing the registration date of an account is not supported.
|
||||
*
|
||||
* @param db ReviewDb
|
||||
* @param account the account
|
||||
* @throws OrmException if updating the database fails
|
||||
* @throws IOException if updating the user branch fails
|
||||
* @throws ConfigInvalidException if any of the account fields has an invalid value
|
||||
*/
|
||||
public void update(ReviewDb db, Account account)
|
||||
throws OrmException, IOException, ConfigInvalidException {
|
||||
// Update in ReviewDb
|
||||
db.accounts().update(ImmutableSet.of(account));
|
||||
evictAccount(account.getId());
|
||||
|
||||
// Update in NoteDb
|
||||
AccountConfig accountConfig = read(account.getId());
|
||||
accountConfig.setAccount(account);
|
||||
commit(accountConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the account and updates it atomically.
|
||||
*
|
||||
* <p>Changing the registration date of an account is not supported.
|
||||
*
|
||||
* @param db ReviewDb
|
||||
* @param accountId ID of the account
|
||||
* @param consumer consumer to update the account, only invoked if the account exists
|
||||
* @return the updated account, {@code null} if the account doesn't exist
|
||||
* @throws OrmException if updating the account fails
|
||||
* @throws OrmException if updating the database fails
|
||||
* @throws IOException if updating the user branch fails
|
||||
* @throws ConfigInvalidException if any of the account fields has an invalid value
|
||||
*/
|
||||
public Account atomicUpdate(ReviewDb db, Account.Id accountId, Consumer<Account> consumer)
|
||||
throws OrmException, IOException {
|
||||
Account account =
|
||||
throws OrmException, IOException, ConfigInvalidException {
|
||||
// Update in ReviewDb
|
||||
db.accounts()
|
||||
.atomicUpdate(
|
||||
accountId,
|
||||
@@ -217,41 +259,52 @@ public class AccountsUpdate {
|
||||
consumer.accept(a);
|
||||
return a;
|
||||
});
|
||||
evictAccount(accountId);
|
||||
|
||||
// Update in NoteDb
|
||||
AccountConfig accountConfig = read(accountId);
|
||||
Account account = accountConfig.getAccount();
|
||||
consumer.accept(account);
|
||||
commit(accountConfig);
|
||||
return account;
|
||||
}
|
||||
|
||||
/** Deletes the account. */
|
||||
/**
|
||||
* Deletes the account.
|
||||
*
|
||||
* @param db ReviewDb
|
||||
* @param account the account that should be deleted
|
||||
* @throws OrmException if updating the database fails
|
||||
* @throws IOException if updating the user branch fails
|
||||
*/
|
||||
public void delete(ReviewDb db, Account account) throws OrmException, IOException {
|
||||
// Delete in ReviewDb
|
||||
db.accounts().delete(ImmutableSet.of(account));
|
||||
|
||||
// Delete in NoteDb
|
||||
deleteUserBranch(account.getId());
|
||||
evictAccount(account.getId());
|
||||
}
|
||||
|
||||
/** Deletes the account. */
|
||||
/**
|
||||
* Deletes the account.
|
||||
*
|
||||
* @param db ReviewDb
|
||||
* @param accountId the ID of the account that should be deleted
|
||||
* @throws OrmException if updating the database fails
|
||||
* @throws IOException if updating the user branch fails
|
||||
*/
|
||||
public void deleteByKey(ReviewDb db, Account.Id accountId) throws OrmException, IOException {
|
||||
// Delete in ReviewDb
|
||||
db.accounts().deleteKeys(ImmutableSet.of(accountId));
|
||||
deleteUserBranch(accountId);
|
||||
evictAccount(accountId);
|
||||
}
|
||||
|
||||
private void createUserBranch(Account account) throws IOException {
|
||||
try (Repository repo = repoManager.openRepository(allUsersName);
|
||||
ObjectInserter oi = repo.newObjectInserter()) {
|
||||
String refName = RefNames.refsUsers(account.getId());
|
||||
if (repo.exactRef(refName) != null) {
|
||||
throw new IOException(
|
||||
String.format(
|
||||
"User branch %s for newly created account %s already exists.",
|
||||
refName, account.getId().get()));
|
||||
}
|
||||
createUserBranch(
|
||||
repo, oi, committerIdent, authorIdent, account.getId(), account.getRegisteredOn());
|
||||
}
|
||||
// Delete in NoteDb
|
||||
deleteUserBranch(accountId);
|
||||
}
|
||||
|
||||
public static void createUserBranch(
|
||||
Repository repo,
|
||||
Project.NameKey project,
|
||||
GitReferenceUpdated gitRefUpdated,
|
||||
@Nullable IdentifiedUser user,
|
||||
ObjectInserter oi,
|
||||
PersonIdent committerIdent,
|
||||
PersonIdent authorIdent,
|
||||
@@ -271,6 +324,7 @@ public class AccountsUpdate {
|
||||
if (result != Result.NEW) {
|
||||
throw new IOException(String.format("Failed to update ref %s: %s", refName, result.name()));
|
||||
}
|
||||
gitRefUpdated.fire(project, ru, user != null ? user.getAccount() : null);
|
||||
}
|
||||
|
||||
private static ObjectId createInitialEmptyCommit(
|
||||
@@ -295,12 +349,18 @@ public class AccountsUpdate {
|
||||
|
||||
private void deleteUserBranch(Account.Id accountId) throws IOException {
|
||||
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||
deleteUserBranch(repo, committerIdent, accountId);
|
||||
deleteUserBranch(repo, allUsersName, gitRefUpdated, currentUser, committerIdent, accountId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteUserBranch(
|
||||
Repository repo, PersonIdent refLogIdent, Account.Id accountId) throws IOException {
|
||||
Repository repo,
|
||||
Project.NameKey project,
|
||||
GitReferenceUpdated gitRefUpdated,
|
||||
@Nullable IdentifiedUser user,
|
||||
PersonIdent refLogIdent,
|
||||
Account.Id accountId)
|
||||
throws IOException {
|
||||
String refName = RefNames.refsUsers(accountId);
|
||||
Ref ref = repo.exactRef(refName);
|
||||
if (ref == null) {
|
||||
@@ -317,11 +377,37 @@ public class AccountsUpdate {
|
||||
if (result != Result.FORCED) {
|
||||
throw new IOException(String.format("Failed to delete ref %s: %s", refName, result.name()));
|
||||
}
|
||||
gitRefUpdated.fire(project, ru, user != null ? user.getAccount() : null);
|
||||
}
|
||||
|
||||
private void evictAccount(Account.Id accountId) throws IOException {
|
||||
if (accountCache != null) {
|
||||
accountCache.evict(accountId);
|
||||
private AccountConfig read(Account.Id accountId) throws IOException, ConfigInvalidException {
|
||||
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||
AccountConfig accountConfig = new AccountConfig(emailValidator, accountId);
|
||||
accountConfig.load(repo);
|
||||
return accountConfig;
|
||||
}
|
||||
}
|
||||
|
||||
private void commitNew(AccountConfig accountConfig) throws IOException {
|
||||
// When creating a new account we must allow empty commits so that the user branch gets created
|
||||
// with an empty commit when no account properties are set and hence no 'account.config' file
|
||||
// will be created.
|
||||
commit(accountConfig, true);
|
||||
}
|
||||
|
||||
private void commit(AccountConfig accountConfig) throws IOException {
|
||||
commit(accountConfig, false);
|
||||
}
|
||||
|
||||
private void commit(AccountConfig accountConfig, boolean allowEmptyCommit) throws IOException {
|
||||
try (MetaDataUpdate md = metaDataUpdateFactory.create()) {
|
||||
md.setAllowEmpty(allowEmptyCommit);
|
||||
accountConfig.commit(md);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private static interface MetaDataUpdateFactory {
|
||||
MetaDataUpdate create() throws IOException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
|
||||
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
|
||||
@Singleton
|
||||
@@ -53,7 +54,7 @@ public class DeleteActive implements RestModifyView<AccountResource, Input> {
|
||||
|
||||
@Override
|
||||
public Response<?> apply(AccountResource rsrc, Input input)
|
||||
throws RestApiException, OrmException, IOException {
|
||||
throws RestApiException, OrmException, IOException, ConfigInvalidException {
|
||||
if (self.get() == rsrc.getUser()) {
|
||||
throw new ResourceConflictException("cannot deactivate own account");
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
|
||||
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
|
||||
@Singleton
|
||||
@@ -45,7 +46,7 @@ public class PutActive implements RestModifyView<AccountResource, Input> {
|
||||
|
||||
@Override
|
||||
public Response<String> apply(AccountResource rsrc, Input input)
|
||||
throws ResourceNotFoundException, OrmException, IOException {
|
||||
throws ResourceNotFoundException, OrmException, IOException, ConfigInvalidException {
|
||||
AtomicBoolean alreadyActive = new AtomicBoolean(false);
|
||||
Account account =
|
||||
accountsUpdate
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
|
||||
@Singleton
|
||||
public class PutName implements RestModifyView<AccountResource, Input> {
|
||||
@@ -65,7 +66,7 @@ public class PutName implements RestModifyView<AccountResource, Input> {
|
||||
@Override
|
||||
public Response<String> apply(AccountResource rsrc, Input input)
|
||||
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
|
||||
IOException, PermissionBackendException {
|
||||
IOException, PermissionBackendException, ConfigInvalidException {
|
||||
if (self.get() != rsrc.getUser()) {
|
||||
permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
|
||||
}
|
||||
@@ -73,7 +74,8 @@ public class PutName implements RestModifyView<AccountResource, Input> {
|
||||
}
|
||||
|
||||
public Response<String> apply(IdentifiedUser user, Input input)
|
||||
throws MethodNotAllowedException, ResourceNotFoundException, OrmException, IOException {
|
||||
throws MethodNotAllowedException, ResourceNotFoundException, OrmException, IOException,
|
||||
ConfigInvalidException {
|
||||
if (input == null) {
|
||||
input = new Input();
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
|
||||
@Singleton
|
||||
public class PutPreferred implements RestModifyView<AccountResource.Email, Input> {
|
||||
@@ -57,7 +58,7 @@ public class PutPreferred implements RestModifyView<AccountResource.Email, Input
|
||||
@Override
|
||||
public Response<String> apply(AccountResource.Email rsrc, Input input)
|
||||
throws AuthException, ResourceNotFoundException, OrmException, IOException,
|
||||
PermissionBackendException {
|
||||
PermissionBackendException, ConfigInvalidException {
|
||||
if (self.get() != rsrc.getUser()) {
|
||||
permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
|
||||
}
|
||||
@@ -65,7 +66,7 @@ public class PutPreferred implements RestModifyView<AccountResource.Email, Input
|
||||
}
|
||||
|
||||
public Response<String> apply(IdentifiedUser user, String email)
|
||||
throws ResourceNotFoundException, OrmException, IOException {
|
||||
throws ResourceNotFoundException, OrmException, IOException, ConfigInvalidException {
|
||||
AtomicBoolean alreadyPreferred = new AtomicBoolean(false);
|
||||
Account account =
|
||||
accountsUpdate
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
|
||||
@Singleton
|
||||
public class PutStatus implements RestModifyView<AccountResource, Input> {
|
||||
@@ -66,7 +67,7 @@ public class PutStatus implements RestModifyView<AccountResource, Input> {
|
||||
@Override
|
||||
public Response<String> apply(AccountResource rsrc, Input input)
|
||||
throws AuthException, ResourceNotFoundException, OrmException, IOException,
|
||||
PermissionBackendException {
|
||||
PermissionBackendException, ConfigInvalidException {
|
||||
if (self.get() != rsrc.getUser()) {
|
||||
permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
|
||||
}
|
||||
@@ -74,7 +75,7 @@ public class PutStatus implements RestModifyView<AccountResource, Input> {
|
||||
}
|
||||
|
||||
public Response<String> apply(IdentifiedUser user, Input input)
|
||||
throws ResourceNotFoundException, OrmException, IOException {
|
||||
throws ResourceNotFoundException, OrmException, IOException, ConfigInvalidException {
|
||||
if (input == null) {
|
||||
input = new Input();
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
@@ -2789,7 +2790,7 @@ public class ReceiveCommits {
|
||||
accountsUpdate.create().update(db, a);
|
||||
user.getAccount().setFullName(a.getFullName());
|
||||
}
|
||||
} catch (OrmException e) {
|
||||
} catch (OrmException | IOException | ConfigInvalidException e) {
|
||||
logWarn("Cannot default full_name", e);
|
||||
} finally {
|
||||
defaultName = false;
|
||||
|
||||
@@ -75,6 +75,7 @@ public abstract class VersionedMetaData {
|
||||
}
|
||||
|
||||
protected RevCommit revision;
|
||||
protected RevWalk rw;
|
||||
protected ObjectReader reader;
|
||||
protected ObjectInserter inserter;
|
||||
protected DirCache newTree;
|
||||
@@ -153,11 +154,13 @@ public abstract class VersionedMetaData {
|
||||
* @throws ConfigInvalidException
|
||||
*/
|
||||
public void load(RevWalk walk, ObjectId id) throws IOException, ConfigInvalidException {
|
||||
this.rw = walk;
|
||||
this.reader = walk.getObjectReader();
|
||||
try {
|
||||
revision = id != null ? walk.parseCommit(id) : null;
|
||||
onLoad();
|
||||
} finally {
|
||||
walk = null;
|
||||
reader = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.account.AccountsUpdate;
|
||||
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.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
@@ -88,7 +89,15 @@ public class Schema_146 extends SchemaVersion {
|
||||
rewriteUserBranch(repo, rw, oi, emptyTree, ref, e.getValue());
|
||||
} else {
|
||||
AccountsUpdate.createUserBranch(
|
||||
repo, oi, serverIdent, serverIdent, e.getKey(), e.getValue());
|
||||
repo,
|
||||
allUsersName,
|
||||
GitReferenceUpdated.DISABLED,
|
||||
null,
|
||||
oi,
|
||||
serverIdent,
|
||||
serverIdent,
|
||||
e.getKey(),
|
||||
e.getValue());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
import com.google.gerrit.server.account.AccountsUpdate;
|
||||
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.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
@@ -68,7 +69,8 @@ public class Schema_147 extends SchemaVersion {
|
||||
.collect(toSet());
|
||||
accountIdsFromUserBranches.removeAll(accountIdsFromReviewDb);
|
||||
for (Account.Id accountId : accountIdsFromUserBranches) {
|
||||
AccountsUpdate.deleteUserBranch(repo, serverIdent, accountId);
|
||||
AccountsUpdate.deleteUserBranch(
|
||||
repo, allUsersName, GitReferenceUpdated.DISABLED, null, serverIdent, accountId);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new OrmException("Failed to delete user branches for non-existing accounts.", e);
|
||||
|
||||
@@ -292,7 +292,8 @@ final class SetAccountCommand extends SshCommand {
|
||||
}
|
||||
|
||||
private void putPreferred(String email)
|
||||
throws RestApiException, OrmException, IOException, PermissionBackendException {
|
||||
throws RestApiException, OrmException, IOException, PermissionBackendException,
|
||||
ConfigInvalidException {
|
||||
for (EmailInfo e : getEmails.apply(rsrc)) {
|
||||
if (e.email.equals(email)) {
|
||||
putPreferred.apply(new AccountResource.Email(user, email), null);
|
||||
|
||||
Reference in New Issue
Block a user