| // 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] |