Custom replication config resource provider from repository Fetch the override replication config from the All-Projects repository. Also-By: Luca Milanesio <luca.milanesio@gmail.com> Change-Id: I8fd15187fe17ce280d777374e91b2e48604aa43e
diff --git a/replication/replication-config-from-git.groovy b/replication/replication-config-from-git.groovy new file mode 100644 index 0000000..efcb32f --- /dev/null +++ b/replication/replication-config-from-git.groovy
@@ -0,0 +1,147 @@ +// Copyright (C) 2024 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. + +import com.googlesource.gerrit.plugins.replication.* + import com.googlesource.gerrit.plugins.replication.FanoutConfigResource.* + import com.googlesource.gerrit.plugins.replication.FileConfigResource.* + + import com.google.inject.* + import com.google.common.collect.* + import com.google.common.flogger.* + import com.google.common.io.* + import com.google.common.io.Files.* + import com.google.gerrit.entities.* + import com.google.gerrit.extensions.registration.* + import com.google.gerrit.server.config.* + import com.google.gerrit.server.git.* + import com.google.inject.* + + import java.io.* + import java.util.* + + import org.eclipse.jgit.errors.* + import org.eclipse.jgit.lib.* + import org.eclipse.jgit.lib.FileMode.* + + import org.eclipse.jgit.revwalk.* + import org.eclipse.jgit.treewalk.* + + class GitReplicationConfigOverrides implements ReplicationConfigOverrides { + FluentLogger logger = FluentLogger.forEnclosingClass() + Config EMPTY_CONFIG = new Config() + + def REF_NAME = RefNames.REFS_META + "replication" + + @Inject + AllProjectsName allProjectsName + + @Inject + GitRepositoryManager repoManager + + @Override + Config getConfig() { + Config config = EMPTY_CONFIG + + Repository repo = repoManager.openRepository(allProjectsName) + RevWalk rw = new RevWalk(repo) + try { + Ref ref = repo.exactRef(REF_NAME) + if (ref) { + RevTree tree = rw.parseTree(ref.objectId) + config = addFanoutRemotes(repo, tree, getBaseConfig(repo, tree)) + } + } catch (IOException e) { + logger.atWarning().withCause(e).log("Cannot read replication config from git repository") + } catch (ConfigInvalidException e) { + logger.atWarning().withCause(e).log("Cannot parse replication config from git repository") + } finally { + rw.close() + repo.close() + } + + config + } + + Config getBaseConfig(Repository repo, RevTree tree) { + TreeWalk tw = TreeWalk.forPath(repo, FileConfigResource.CONFIG_NAME, tree) + return tw ? new BlobBasedConfig(new Config(), repo, tw.getObjectId(0)) : EMPTY_CONFIG + } + + Config addFanoutRemotes(Repository repo, RevTree tree, Config destination) + throws IOException, ConfigInvalidException { + TreeWalk tw = TreeWalk.forPath(repo, FanoutConfigResource.CONFIG_DIR, tree) + if (tw) { + removeRemotes(destination) + + tw.enterSubtree() + while (tw.next()) { + if (tw.fileMode == FileMode.REGULAR_FILE && tw.nameString.endsWith(".config")) { + Config remoteConfig = new BlobBasedConfig(new Config(), repo, tw.getObjectId(0)) + addRemoteConfig(tw.nameString, remoteConfig, destination) + } + } + } + + destination + } + + def removeRemotes(Config config) { + Set < String > remoteNames = config.getSubsections("remote") + if (!remoteNames) { + logger.atSevere().log( + "When replication directory is present replication.config file cannot contain remote configuration. Ignoring: %s", + remoteNames.join(",")) + + for (String name: remoteNames) { + config.unsetSection("remote", name) + } + } + } + + def addRemoteConfig(String fileName, Config source, Config destination) { + String remoteName = Files.getNameWithoutExtension(fileName) + source.getNames("remote").each { + name -> + destination.setStringList( + "remote", + remoteName, + name, + Lists.newArrayList(source.getStringList("remote", null, name))) + } + } + + @Override + String getVersion() { + Repository repo = repoManager.openRepository(allProjectsName) + try { + ObjectId configHead = repo.resolve(REF_NAME) + return configHead ? configHead.name() : "" + } catch (IOException e) { + throw new IllegalStateException("Could not open replication configuration repository", e) + } finally { + repo.close() + } + } + } + +class GitReplicationConfigModule implements Module { + + @Override + void configure(Binder binder) { + DynamicItem.bind(binder, ReplicationConfigOverrides.class) + .to(GitReplicationConfigOverrides.class) + } +} + +modules = [GitReplicationConfigModule]