Allow to update accounts and project watches atomically

AccountsUpdate now also supports to update project watches. This means
account properties and project watches can now be updated in the same
transaction.

WatchConfig.Accessor is removed so that project watch updates can only
be done through AccountsUpdate. For reading project watches a new
method is added to Accounts. In the future we would rather like to
change the Accounts#get(Account.Id) method to return AccountState
instead of Account so that this get method can be used to retrieve the
account as well as the project watches. Once this is done the
getProjectWatches method will be removed again.

Reading and writing project watches is now done via AccountConfig. On
load we always read the watch.config file, but parsing it is done
lazily when the project watches are accessed for the first time. Most
callers that load accounts are not interested in project watches and
this avoids unneeded parsing effort for them.

Change-Id: I8cd441eee3f65817a3653610c5d3bb62b0bfea5e
Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
Edwin Kempin
2018-01-03 13:02:14 +01:00
parent f3306ea1f0
commit 1df95271ad
14 changed files with 294 additions and 286 deletions

View File

@@ -14,11 +14,14 @@
package com.google.gerrit.server.git.validators;
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountConfig;
import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -47,15 +50,16 @@ public class AccountValidator {
Optional<Account> oldAccount = Optional.empty();
if (oldId != null && !ObjectId.zeroId().equals(oldId)) {
try {
oldAccount = loadAccount(accountId, rw, oldId);
oldAccount = loadAccount(accountId, rw, oldId, null);
} catch (ConfigInvalidException e) {
// ignore, maybe the new commit is repairing it now
}
}
List<String> messages = new ArrayList<>();
Optional<Account> newAccount;
try {
newAccount = loadAccount(accountId, rw, newId);
newAccount = loadAccount(accountId, rw, newId, messages);
} catch (ConfigInvalidException e) {
return ImmutableList.of(
String.format(
@@ -67,7 +71,6 @@ public class AccountValidator {
return ImmutableList.of(String.format("account '%s' does not exist", accountId.get()));
}
List<String> messages = new ArrayList<>();
if (accountId.equals(self.get().getAccountId()) && !newAccount.get().isActive()) {
messages.add("cannot deactivate own account");
}
@@ -87,11 +90,20 @@ public class AccountValidator {
return ImmutableList.copyOf(messages);
}
private Optional<Account> loadAccount(Account.Id accountId, RevWalk rw, ObjectId commit)
private Optional<Account> loadAccount(
Account.Id accountId, RevWalk rw, ObjectId commit, @Nullable List<String> messages)
throws IOException, ConfigInvalidException {
rw.reset();
AccountConfig accountConfig = new AccountConfig(accountId);
accountConfig.load(rw, commit);
accountConfig.setEagerParsing(true).load(rw, commit);
if (messages != null) {
messages.addAll(
accountConfig
.getValidationErrors()
.stream()
.map(ValidationError::getMessage)
.collect(toSet()));
}
return accountConfig.getLoadedAccount();
}
}

View File

@@ -34,7 +34,6 @@ import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.WatchConfig;
import com.google.gerrit.server.account.externalids.ExternalIdsConsistencyChecker;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
@@ -418,34 +417,6 @@ public class CommitValidators {
}
}
if (allUsers.equals(branch.getParentKey()) && RefNames.isRefsUsers(branch.get())) {
List<CommitValidationMessage> messages = new ArrayList<>();
Account.Id accountId = Account.Id.fromRef(branch.get());
if (accountId != null) {
try {
WatchConfig wc = new WatchConfig(accountId);
wc.load(rw, receiveEvent.command.getNewId());
if (!wc.getValidationErrors().isEmpty()) {
addError("Invalid project configuration:", messages);
for (ValidationError err : wc.getValidationErrors()) {
addError(" " + err.getMessage(), messages);
}
throw new ConfigInvalidException("invalid watch configuration");
}
} catch (IOException | ConfigInvalidException e) {
log.error(
"User "
+ user.getUserName()
+ " tried to push an invalid watch configuration "
+ receiveEvent.command.getNewId().name()
+ " for account "
+ accountId.get(),
e);
throw new CommitValidationException("invalid watch configuration", messages);
}
}
}
return Collections.emptyList();
}
}