Merge branch 'stable-3.2' into stable-3.3 * stable-3.2: Ensure in-memory model matches state committed to All-Projects Change-Id: I0d778129348f59b9979ad9a72188c1f4e2bbabe9
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUser.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUser.java index 296ffef..d7cdff8 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUser.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUser.java
@@ -40,7 +40,6 @@ import com.google.gerrit.server.config.PluginConfigFactory; import com.google.gerrit.server.git.meta.MetaDataUpdate; import com.google.gerrit.server.permissions.PermissionBackendException; -import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectLevelConfig; import com.google.gerrit.server.restapi.account.CreateAccount; import com.google.inject.Inject; @@ -76,42 +75,43 @@ } private final PluginConfig cfg; + private final Provider<ProjectLevelConfig.Bare> configProvider; private final CreateAccount createAccount; private final Provider<CurrentUser> userProvider; private final MetaDataUpdate.User metaDataUpdateFactory; private final Project.NameKey allProjects; - private final ProjectLevelConfig storage; private final DateFormat rfc2822DateFormatter; private final Provider<GetConfig> getConfig; private final AccountLoader.Factory accountLoader; + private final StorageCache storageCache; private final BlockedNameFilter blockedNameFilter; - private final ProjectCache projectCache; @Inject CreateServiceUser( PluginConfigFactory cfgFactory, + Provider<ProjectLevelConfig.Bare> configProvider, @PluginName String pluginName, CreateAccount createAccount, Provider<CurrentUser> userProvider, @GerritPersonIdent PersonIdent gerritIdent, MetaDataUpdate.User metaDataUpdateFactory, - ProjectCache projectCache, AllProjectsName allProjects, Provider<GetConfig> getConfig, AccountLoader.Factory accountLoader, + StorageCache storageCache, BlockedNameFilter blockedNameFilter) { this.cfg = cfgFactory.getFromGerritConfig(pluginName); + this.configProvider = configProvider; this.createAccount = createAccount; this.userProvider = userProvider; this.metaDataUpdateFactory = metaDataUpdateFactory; - this.projectCache = projectCache; - this.storage = projectCache.getAllProjects().getConfig(pluginName + ".db"); this.allProjects = allProjects; this.rfc2822DateFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); this.rfc2822DateFormatter.setCalendar( Calendar.getInstance(gerritIdent.getTimeZone(), Locale.US)); this.getConfig = getConfig; this.accountLoader = accountLoader; + this.storageCache = storageCache; this.blockedNameFilter = blockedNameFilter; } @@ -172,19 +172,20 @@ Account.Id creatorId = ((IdentifiedUser) user).getAccountId(); String creationDate = rfc2822DateFormatter.format(new Date()); - try { - Config db = storage.get(); + try (MetaDataUpdate md = metaDataUpdateFactory.create(allProjects)) { + ProjectLevelConfig.Bare update = configProvider.get(); + update.load(md); + + Config db = update.getConfig(); db.setInt(USER, username, KEY_CREATOR_ID, creatorId.get()); if (creator != null) { db.setString(USER, username, KEY_CREATED_BY, creator); } db.setString(USER, username, KEY_CREATED_AT, creationDate); - MetaDataUpdate md = metaDataUpdateFactory.create(allProjects); md.setMessage("Create service user '" + username + "'\n"); - storage.commit(md); - } finally { - projectCache.evict(allProjects); + update.commit(md); + storageCache.invalidate(); } ServiceUserInfo info = new ServiceUserInfo(response); AccountLoader al = accountLoader.create(true);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUserNotes.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUserNotes.java index 93fc84c..d60203b 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUserNotes.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/CreateServiceUserNotes.java
@@ -131,8 +131,9 @@ } } - private void markUninteresting(Repository git, String branch, RevWalk rw, ObjectId oldObjectId) { - for (Ref r : git.getAllRefs().values()) { + private void markUninteresting(Repository git, String branch, RevWalk rw, ObjectId oldObjectId) + throws IOException { + for (Ref r : git.getRefDatabase().getRefs()) { try { if (r.getName().equals(branch)) { if (!ObjectId.zeroId().equals(oldObjectId)) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetConfig.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetConfig.java index f89f047..3cca663 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetConfig.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetConfig.java
@@ -95,7 +95,7 @@ return v ? v : null; } - public class ConfigInfo { + static class ConfigInfo { public String info; public String onSuccess; public Boolean allowEmail;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetOwner.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetOwner.java index 31c2eb6..bf7227b 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetOwner.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetOwner.java
@@ -17,8 +17,7 @@ import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.KEY_OWNER; import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.USER; -import com.google.gerrit.common.data.GroupDescription; -import com.google.gerrit.extensions.annotations.PluginName; +import com.google.gerrit.entities.GroupDescription; import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.Response; @@ -26,37 +25,30 @@ import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.server.permissions.PermissionBackendException; -import com.google.gerrit.server.project.ProjectCache; -import com.google.gerrit.server.project.ProjectLevelConfig; import com.google.gerrit.server.restapi.group.GroupJson; import com.google.gerrit.server.restapi.group.GroupsCollection; import com.google.inject.Inject; import com.google.inject.Singleton; +import java.io.IOException; @Singleton class GetOwner implements RestReadView<ServiceUserResource> { private final GroupsCollection groups; - private final String pluginName; - private final ProjectCache projectCache; private final GroupJson json; + private final StorageCache storageCache; @Inject - GetOwner( - GroupsCollection groups, - @PluginName String pluginName, - ProjectCache projectCache, - GroupJson json) { + GetOwner(GroupsCollection groups, GroupJson json, StorageCache storageCache) { this.groups = groups; - this.pluginName = pluginName; - this.projectCache = projectCache; this.json = json; + this.storageCache = storageCache; } @Override public Response<GroupInfo> apply(ServiceUserResource rsrc) - throws RestApiException, PermissionBackendException { - ProjectLevelConfig storage = projectCache.getAllProjects().getConfig(pluginName + ".db"); - String owner = storage.get().getString(USER, rsrc.getUser().getUserName().get(), KEY_OWNER); + throws IOException, RestApiException, PermissionBackendException { + String owner = + storageCache.get().getString(USER, rsrc.getUser().getUserName().get(), KEY_OWNER); if (owner != null) { GroupDescription.Basic group = groups.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(owner)).getGroup();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetServiceUser.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetServiceUser.java index c700da8..784c6b0 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetServiceUser.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/GetServiceUser.java
@@ -21,7 +21,6 @@ import static javax.servlet.http.HttpServletResponse.SC_OK; import com.google.gerrit.entities.Account; -import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; @@ -30,42 +29,37 @@ import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.server.account.AccountLoader; import com.google.gerrit.server.permissions.PermissionBackendException; -import com.google.gerrit.server.project.ProjectCache; -import com.google.gerrit.server.project.ProjectLevelConfig; import com.google.gerrit.server.restapi.account.GetAccount; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; +import java.io.IOException; import org.eclipse.jgit.lib.Config; @Singleton class GetServiceUser implements RestReadView<ServiceUserResource> { private final Provider<GetAccount> getAccount; - private final String pluginName; - private final ProjectCache projectCache; private final GetOwner getOwner; private final AccountLoader.Factory accountLoader; + private final StorageCache storageCache; @Inject GetServiceUser( Provider<GetAccount> getAccount, - @PluginName String pluginName, - ProjectCache projectCache, GetOwner getOwner, - AccountLoader.Factory accountLoader) { + AccountLoader.Factory accountLoader, + StorageCache storageCache) { this.getAccount = getAccount; - this.pluginName = pluginName; - this.projectCache = projectCache; this.getOwner = getOwner; this.accountLoader = accountLoader; + this.storageCache = storageCache; } @Override public Response<ServiceUserInfo> apply(ServiceUserResource rsrc) - throws RestApiException, PermissionBackendException { - ProjectLevelConfig storage = projectCache.getAllProjects().getConfig(pluginName + ".db"); + throws IOException, RestApiException, PermissionBackendException { String username = rsrc.getUser().getUserName().get(); - Config db = storage.get(); + Config db = storageCache.get(); if (!db.getSubsections(USER).contains(username)) { throw new ResourceNotFoundException(username); }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ListServiceUsers.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ListServiceUsers.java index 5e77ce2..a40b989 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ListServiceUsers.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ListServiceUsers.java
@@ -18,7 +18,6 @@ import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.USER; import com.google.common.collect.Maps; -import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; @@ -30,8 +29,6 @@ import com.google.gerrit.server.account.AccountState; import com.google.gerrit.server.config.ConfigResource; import com.google.gerrit.server.permissions.PermissionBackendException; -import com.google.gerrit.server.project.ProjectCache; -import com.google.gerrit.server.project.ProjectLevelConfig; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; @@ -45,39 +42,35 @@ @Singleton class ListServiceUsers implements RestReadView<ConfigResource> { private final Provider<CurrentUser> userProvider; - private final String pluginName; - private final ProjectCache projectCache; private final AccountCache accountCache; private final Provider<ServiceUserCollection> serviceUsers; private final Provider<GetServiceUser> getServiceUser; + private final StorageCache storageCache; @Inject ListServiceUsers( Provider<CurrentUser> userProvider, - @PluginName String pluginName, - ProjectCache projectCache, AccountCache accountCache, Provider<ServiceUserCollection> serviceUsers, - Provider<GetServiceUser> getServiceUser) { + Provider<GetServiceUser> getServiceUser, + StorageCache storageCache) { this.userProvider = userProvider; - this.pluginName = pluginName; - this.projectCache = projectCache; this.accountCache = accountCache; this.serviceUsers = serviceUsers; this.getServiceUser = getServiceUser; + this.storageCache = storageCache; } @Override public Response<Map<String, ServiceUserInfo>> apply(ConfigResource rscr) throws IOException, RestApiException, PermissionBackendException, ConfigInvalidException { - ProjectLevelConfig storage = projectCache.getAllProjects().getConfig(pluginName + ".db"); CurrentUser user = userProvider.get(); if (user == null || !user.isIdentifiedUser()) { throw new AuthException("Authentication required"); } Map<String, ServiceUserInfo> accounts = Maps.newTreeMap(); - Config db = storage.get(); + Config db = storageCache.get(); for (String username : db.getSubsections(USER)) { Optional<AccountState> account = accountCache.getByUsername(username); if (account.isPresent()) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/Module.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/Module.java index 56d14e1..89510bb 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/Module.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/Module.java
@@ -19,13 +19,16 @@ import static com.googlesource.gerrit.plugins.serviceuser.ServiceUserResource.SERVICE_USER_SSH_KEY_KIND; import com.google.gerrit.extensions.annotations.Exports; +import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.config.CapabilityDefinition; import com.google.gerrit.extensions.events.GitReferenceUpdatedListener; import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.restapi.RestApiModule; import com.google.gerrit.server.git.validators.CommitValidationListener; +import com.google.gerrit.server.project.ProjectLevelConfig; import com.google.inject.AbstractModule; +import com.google.inject.Provides; import com.google.inject.assistedinject.FactoryModuleBuilder; class Module extends AbstractModule { @@ -71,5 +74,11 @@ } }); install(new HttpModule()); + install(StorageCache.module()); + } + + @Provides + ProjectLevelConfig.Bare createProjectLevelConfig(@PluginName String pluginName) { + return new ProjectLevelConfig.Bare(pluginName + ".db"); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/PutOwner.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/PutOwner.java index fd8bbbf..32ee082 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/PutOwner.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/PutOwner.java
@@ -20,11 +20,10 @@ import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.USER; import com.google.common.base.Strings; -import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.entities.AccountGroup; import com.google.gerrit.entities.AccountGroup.UUID; +import com.google.gerrit.entities.GroupDescription; import com.google.gerrit.entities.Project; -import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.common.GroupInfo; import com.google.gerrit.extensions.restapi.DefaultInput; import com.google.gerrit.extensions.restapi.IdString; @@ -47,6 +46,7 @@ import com.google.inject.Singleton; import com.googlesource.gerrit.plugins.serviceuser.PutOwner.Input; import java.io.IOException; +import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.Config; @Singleton @@ -57,39 +57,39 @@ private final Provider<GetConfig> getConfig; private final GroupsCollection groups; - private final String pluginName; - private final ProjectCache projectCache; + private final Provider<ProjectLevelConfig.Bare> configProvider; private final Project.NameKey allProjects; private final MetaDataUpdate.User metaDataUpdateFactory; private final GroupJson json; private final Provider<CurrentUser> self; private final PermissionBackend permissionBackend; + private final StorageCache storageCache; @Inject PutOwner( Provider<GetConfig> getConfig, GroupsCollection groups, - @PluginName String pluginName, + Provider<ProjectLevelConfig.Bare> configProvider, ProjectCache projectCache, MetaDataUpdate.User metaDataUpdateFactory, GroupJson json, Provider<CurrentUser> self, - PermissionBackend permissionBackend) { + PermissionBackend permissionBackend, + StorageCache storageCache) { this.getConfig = getConfig; this.groups = groups; - this.pluginName = pluginName; - this.projectCache = projectCache; + this.configProvider = configProvider; this.allProjects = projectCache.getAllProjects().getProject().getNameKey(); this.metaDataUpdateFactory = metaDataUpdateFactory; this.json = json; this.self = self; this.permissionBackend = permissionBackend; + this.storageCache = storageCache; } @Override public Response<GroupInfo> apply(ServiceUserResource rsrc, Input input) throws RestApiException, IOException, PermissionBackendException { - ProjectLevelConfig storage = projectCache.getAllProjects().getConfig(pluginName + ".db"); Boolean ownerAllowed; try { ownerAllowed = getConfig.get().apply(new ConfigResource()).value().allowOwner; @@ -103,22 +103,33 @@ if (input == null) { input = new Input(); } - Config db = storage.get(); - String oldGroup = db.getString(USER, rsrc.getUser().getUserName().get(), KEY_OWNER); + GroupDescription.Basic group = null; - if (Strings.isNullOrEmpty(input.group)) { - db.unset(USER, rsrc.getUser().getUserName().get(), KEY_OWNER); - } else { - group = groups.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(input.group)).getGroup(); - UUID groupUUID = group.getGroupUUID(); - if (!AccountGroup.uuid(groupUUID.get()).isInternalGroup()) { - throw new MethodNotAllowedException("Group with UUID '" + groupUUID + "' is external"); + String oldGroup; + try (MetaDataUpdate md = metaDataUpdateFactory.create(allProjects)) { + ProjectLevelConfig.Bare update = configProvider.get(); + update.load(md); + + Config db = update.getConfig(); + oldGroup = db.getString(USER, rsrc.getUser().getUserName().get(), KEY_OWNER); + if (Strings.isNullOrEmpty(input.group)) { + db.unset(USER, rsrc.getUser().getUserName().get(), KEY_OWNER); + } else { + group = + groups.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(input.group)).getGroup(); + UUID groupUUID = group.getGroupUUID(); + if (!AccountGroup.uuid(groupUUID.get()).isInternalGroup()) { + throw new MethodNotAllowedException("Group with UUID '" + groupUUID + "' is external"); + } + db.setString(USER, rsrc.getUser().getUserName().get(), KEY_OWNER, groupUUID.get()); } - db.setString(USER, rsrc.getUser().getUserName().get(), KEY_OWNER, groupUUID.get()); + md.setMessage("Set owner for service user '" + rsrc.getUser().getUserName() + "'\n"); + update.commit(md); + storageCache.invalidate(); + } catch (ConfigInvalidException e) { + throw asRestApiException("Invalid configuration", e); } - MetaDataUpdate md = metaDataUpdateFactory.create(allProjects); - md.setMessage("Set owner for service user '" + rsrc.getUser().getUserName() + "'\n"); - storage.commit(md); + return group != null ? (oldGroup != null ? Response.ok(json.format(group))
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RefUpdateListener.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RefUpdateListener.java index 31d1a56..421b326 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RefUpdateListener.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RefUpdateListener.java
@@ -26,6 +26,7 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import java.io.IOException; +import java.util.concurrent.Future; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.slf4j.Logger; @@ -90,7 +91,8 @@ } }; if (cfg.getBoolean("createNotesAsync", false)) { - workQueue.getDefaultQueue().submit(task); + @SuppressWarnings("unused") + Future<?> possiblyIgnoredError = workQueue.getDefaultQueue().submit(task); } else { task.run(); }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RegisterServiceUser.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RegisterServiceUser.java index 6bca1ff..17c8ab3 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RegisterServiceUser.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/RegisterServiceUser.java
@@ -24,7 +24,6 @@ import com.google.common.base.Strings; import com.google.gerrit.entities.Account; import com.google.gerrit.entities.Project; -import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.annotations.RequiresCapability; import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.restapi.AuthException; @@ -46,7 +45,6 @@ import com.google.gerrit.server.group.GroupResolver; import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackendException; -import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectLevelConfig; import com.google.inject.Inject; import com.google.inject.Provider; @@ -74,42 +72,42 @@ String owner; } + private final Provider<ProjectLevelConfig.Bare> configProvider; private final AccountResolver accountResolver; private final GroupResolver groupResolver; private final Provider<CurrentUser> userProvider; private final MetaDataUpdate.User metaDataUpdateFactory; private final Project.NameKey allProjects; - private final ProjectLevelConfig storage; private final DateFormat rfc2822DateFormatter; private final AccountLoader.Factory accountLoader; + private final StorageCache storageCache; private final PermissionBackend permissionBackend; private final BlockedNameFilter blockedNameFilter; - private final ProjectCache projectCache; @Inject RegisterServiceUser( + Provider<ProjectLevelConfig.Bare> configProvider, AccountResolver accountResolver, GroupResolver groupResolver, - @PluginName String pluginName, Provider<CurrentUser> userProvider, @GerritPersonIdent PersonIdent gerritIdent, MetaDataUpdate.User metaDataUpdateFactory, - ProjectCache projectCache, AllProjectsName allProjects, AccountLoader.Factory accountLoader, + StorageCache storageCache, PermissionBackend permissionBackend, BlockedNameFilter blockedNameFilter) { + this.configProvider = configProvider; this.accountResolver = accountResolver; this.groupResolver = groupResolver; this.userProvider = userProvider; this.metaDataUpdateFactory = metaDataUpdateFactory; - this.projectCache = projectCache; - this.storage = projectCache.getAllProjects().getConfig(pluginName + ".db"); this.allProjects = allProjects; this.rfc2822DateFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); this.rfc2822DateFormatter.setCalendar( Calendar.getInstance(gerritIdent.getTimeZone(), Locale.US)); this.accountLoader = accountLoader; + this.storageCache = storageCache; this.permissionBackend = permissionBackend; this.blockedNameFilter = blockedNameFilter; } @@ -138,11 +136,6 @@ throw new MethodNotAllowedException("Forbidden"); } - Config db = storage.get(); - if (db.getSubsections(USER).contains(input.username)) { - return Response.none(); - } - if (blockedNameFilter.isBlocked(input.username)) { throw new BadRequestException( "The username '" + input.username + "' is not allowed as name for service users."); @@ -168,7 +161,14 @@ } } - try { + try (MetaDataUpdate md = metaDataUpdateFactory.create(allProjects)) { + ProjectLevelConfig.Bare update = configProvider.get(); + update.load(md); + + Config db = update.getConfig(); + if (db.getSubsections(USER).contains(input.username)) { + return Response.none(); + } db.setInt(USER, input.username, KEY_CREATOR_ID, creatorId.get()); if (creator != null) { db.setString(USER, input.username, KEY_CREATED_BY, creator); @@ -178,12 +178,11 @@ } db.setString(USER, input.username, KEY_CREATED_AT, creationDate); - MetaDataUpdate md = metaDataUpdateFactory.create(allProjects); md.setMessage("Create service user '" + input.username + "'\n"); - storage.commit(md); - } finally { - projectCache.evict(allProjects); + update.commit(md); + storageCache.invalidate(); } + ServiceUserInfo info = new ServiceUserInfo(new AccountInfo(user.getAccountId().get())); AccountLoader al = accountLoader.create(true); info.createdBy = al.get(creatorId);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ServiceUserCollection.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ServiceUserCollection.java index bce9e37..f6f50ac 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ServiceUserCollection.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ServiceUserCollection.java
@@ -19,14 +19,14 @@ import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.KEY_OWNER; import static com.googlesource.gerrit.plugins.serviceuser.CreateServiceUser.USER; -import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.entities.Account; -import com.google.gerrit.extensions.annotations.PluginName; +import com.google.gerrit.entities.GroupDescription; import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.ChildCollection; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.server.CurrentUser; @@ -34,8 +34,6 @@ import com.google.gerrit.server.config.ConfigResource; import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackendException; -import com.google.gerrit.server.project.ProjectCache; -import com.google.gerrit.server.project.ProjectLevelConfig; import com.google.gerrit.server.restapi.account.AccountsCollection; import com.google.gerrit.server.restapi.group.GroupsCollection; import com.google.inject.Inject; @@ -43,6 +41,7 @@ import com.google.inject.Singleton; import java.io.IOException; import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.Config; @Singleton class ServiceUserCollection implements ChildCollection<ConfigResource, ServiceUserResource> { @@ -50,40 +49,36 @@ private final DynamicMap<RestView<ServiceUserResource>> views; private final Provider<ListServiceUsers> list; private final Provider<AccountsCollection> accounts; - private final String pluginName; - private final ProjectCache projectCache; private final Provider<CurrentUser> userProvider; private final GroupsCollection groups; private final PermissionBackend permissionBackend; + private final StorageCache storageCache; @Inject ServiceUserCollection( DynamicMap<RestView<ServiceUserResource>> views, Provider<ListServiceUsers> list, Provider<AccountsCollection> accounts, - @PluginName String pluginName, - ProjectCache projectCache, Provider<CurrentUser> userProvider, GroupsCollection groups, - PermissionBackend permissionBackend) { + PermissionBackend permissionBackend, + StorageCache storageCache) { this.views = views; this.list = list; this.accounts = accounts; - this.pluginName = pluginName; - this.projectCache = projectCache; this.userProvider = userProvider; this.groups = groups; this.permissionBackend = permissionBackend; + this.storageCache = storageCache; } @Override public ServiceUserResource parse(ConfigResource parent, IdString id) throws ResourceNotFoundException, AuthException, IOException, PermissionBackendException, - ConfigInvalidException { - ProjectLevelConfig storage = projectCache.getAllProjects().getConfig(pluginName + ".db"); + ConfigInvalidException, RestApiException { IdentifiedUser serviceUser = accounts.get().parse(TopLevelResource.INSTANCE, id).getUser(); - if (serviceUser == null - || !storage.get().getSubsections(USER).contains(serviceUser.getUserName().get())) { + Config db = storageCache.get(); + if (serviceUser == null || !db.getSubsections(USER).contains(serviceUser.getUserName().get())) { throw new ResourceNotFoundException(id); } CurrentUser user = userProvider.get(); @@ -92,7 +87,7 @@ } if (!permissionBackend.user(user).testOrFalse(ADMINISTRATE_SERVER)) { String username = serviceUser.getUserName().get(); - String owner = storage.get().getString(USER, username, KEY_OWNER); + String owner = db.getString(USER, username, KEY_OWNER); if (owner != null) { GroupDescription.Basic group = groups.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(owner)).getGroup(); @@ -101,7 +96,7 @@ } } else if (!((IdentifiedUser) user) .getAccountId() - .equals(Account.id(storage.get().getInt(USER, username, KEY_CREATOR_ID, -1)))) { + .equals(Account.id(db.getInt(USER, username, KEY_CREATOR_ID, -1)))) { throw new ResourceNotFoundException(id); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ServiceUserResolver.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ServiceUserResolver.java index 65ac232..0c46e26 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ServiceUserResolver.java +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/ServiceUserResolver.java
@@ -16,9 +16,9 @@ import static com.google.gerrit.server.api.ApiUtil.asRestApiException; -import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.entities.Account; import com.google.gerrit.entities.AccountGroup; +import com.google.gerrit.entities.GroupDescription; import com.google.gerrit.extensions.common.AccountInfo; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestApiException;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serviceuser/StorageCache.java b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/StorageCache.java new file mode 100644 index 0000000..0378347 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/serviceuser/StorageCache.java
@@ -0,0 +1,93 @@ +// Copyright (C) 2021 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.googlesource.gerrit.plugins.serviceuser; + +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.flogger.FluentLogger; +import com.google.gerrit.server.cache.CacheModule; +import com.google.gerrit.server.config.AllProjectsName; +import com.google.gerrit.server.git.meta.MetaDataUpdate; +import com.google.gerrit.server.project.ProjectLevelConfig; +import com.google.gerrit.server.project.ProjectLevelConfig.Bare; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.inject.name.Named; +import java.util.concurrent.ExecutionException; +import org.eclipse.jgit.lib.Config; + +@Singleton +public class StorageCache { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + private static final String CACHE_NAME = "storage"; + private static final Object ALL = new Object(); + + static CacheModule module() { + return new CacheModule() { + @Override + protected void configure() { + cache(CACHE_NAME, Object.class, Config.class).loader(Loader.class); + bind(StorageCache.class); + } + }; + } + + private final LoadingCache<Object, Config> cache; + + @Inject + StorageCache(@Named(CACHE_NAME) LoadingCache<Object, Config> cache) { + this.cache = cache; + } + + public Config get() { + try { + return cache.get(ALL); + } catch (ExecutionException e) { + logger.atSevere().withCause(e).log("Cannot load service users"); + return new Config(); + } + } + + public void invalidate() { + cache.invalidate(ALL); + } + + static class Loader extends CacheLoader<Object, Config> { + private final Provider<Bare> configProvider; + private final MetaDataUpdate.Server metaDataUpdateFactory; + private final AllProjectsName allProjects; + + @Inject + Loader( + Provider<ProjectLevelConfig.Bare> configProvider, + MetaDataUpdate.Server metaDataUpdateFactory, + AllProjectsName allProjects) { + this.configProvider = configProvider; + this.metaDataUpdateFactory = metaDataUpdateFactory; + this.allProjects = allProjects; + } + + @Override + public Config load(Object key) throws Exception { + ProjectLevelConfig.Bare storage = configProvider.get(); + try (MetaDataUpdate md = metaDataUpdateFactory.create(allProjects)) { + storage.load(md); + } + return storage.getConfig(); + } + } +}