blob: a5e84d99797388a2e7ad95b294f87ba985bb174f [file] [log] [blame]
// 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();
}
}