| // 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. |
| |
| package com.googlesource.gerrit.plugins.replication; |
| |
| import static com.google.gerrit.common.UsedAt.Project.PLUGIN_GITHUB; |
| |
| import com.google.gerrit.common.Nullable; |
| import com.google.gerrit.common.UsedAt; |
| import com.google.gerrit.extensions.registration.DynamicItem; |
| import com.google.gerrit.server.securestore.SecureStore; |
| import com.google.inject.Inject; |
| import com.google.inject.Provider; |
| import com.google.inject.Singleton; |
| import com.googlesource.gerrit.plugins.replication.api.ConfigResource; |
| import com.googlesource.gerrit.plugins.replication.api.ReplicationConfigOverrides; |
| import java.io.IOException; |
| import java.util.List; |
| import org.eclipse.jgit.lib.Config; |
| |
| /** Public API to update replication plugin remotes configurations programmatically. */ |
| @Singleton |
| @UsedAt(PLUGIN_GITHUB) |
| public class ReplicationRemotesUpdater { |
| |
| private final SecureStore secureStore; |
| private final Provider<ConfigResource> baseConfigProvider; |
| private final DynamicItem<ReplicationConfigOverrides> configOverridesItem; |
| |
| @Inject |
| ReplicationRemotesUpdater( |
| SecureStore secureStore, |
| Provider<ConfigResource> baseConfigProvider, |
| @Nullable DynamicItem<ReplicationConfigOverrides> configOverridesItem) { |
| this.secureStore = secureStore; |
| this.baseConfigProvider = baseConfigProvider; |
| this.configOverridesItem = configOverridesItem; |
| } |
| |
| /** |
| * Adds or updates the remote configuration for the replication plugin. |
| * |
| * <p>Provided JGit {@link Config} object should contain at least one named <em>remote</em> |
| * section. All other configurations will be ignored. |
| * |
| * <p>NOTE: The {@code remote.$name.password} will be stored using {@link SecureStore}. |
| * |
| * @param remoteConfig remotes to add or update |
| * @throws IOException when persisting fails |
| */ |
| public void update(Config remoteConfig) throws IOException { |
| if (remoteConfig.getSubsections("remote").isEmpty()) { |
| throw new IllegalArgumentException( |
| "configuration update must have at least one 'remote' section"); |
| } |
| |
| SeparatedRemoteConfigs configs = onlyRemoteSectionsWithSeparatedPasswords(remoteConfig); |
| persistRemotesPasswords(configs); |
| |
| if (hasConfigOverrides()) { |
| configOverridesItem.get().update(configs.remotes); |
| } else { |
| baseConfigProvider.get().update(configs.remotes); |
| } |
| } |
| |
| private SeparatedRemoteConfigs onlyRemoteSectionsWithSeparatedPasswords(Config configUpdates) { |
| SeparatedRemoteConfigs configs = new SeparatedRemoteConfigs(); |
| for (String subSection : configUpdates.getSubsections("remote")) { |
| for (String name : configUpdates.getNames("remote", subSection)) { |
| List<String> values = List.of(configUpdates.getStringList("remote", subSection, name)); |
| if ("password".equals(name)) { |
| configs.passwords.setStringList("remote", subSection, "password", values); |
| } else { |
| configs.remotes.setStringList("remote", subSection, name, values); |
| } |
| } |
| } |
| |
| return configs; |
| } |
| |
| private void persistRemotesPasswords(SeparatedRemoteConfigs configs) { |
| for (String subSection : configs.passwords.getSubsections("remote")) { |
| List<String> values = |
| List.of(configs.passwords.getStringList("remote", subSection, "password")); |
| secureStore.setList("remote", subSection, "password", values); |
| } |
| } |
| |
| private boolean hasConfigOverrides() { |
| return configOverridesItem != null && configOverridesItem.get() != null; |
| } |
| |
| private static class SeparatedRemoteConfigs { |
| private final Config remotes = new Config(); |
| private final Config passwords = new Config(); |
| } |
| } |