Skip to content

Commit 57de151

Browse files
committed
better handling for clients with incorrect configured secrets
1 parent fbdd313 commit 57de151

File tree

2 files changed

+47
-16
lines changed

2 files changed

+47
-16
lines changed

modules/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageClientsManager.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
import org.elasticsearch.cluster.metadata.ProjectId;
1515
import org.elasticsearch.cluster.metadata.ProjectMetadata;
1616
import org.elasticsearch.common.CheckedBiFunction;
17-
import org.elasticsearch.common.Strings;
1817
import org.elasticsearch.common.settings.ProjectSecrets;
1918
import org.elasticsearch.common.settings.Settings;
2019
import org.elasticsearch.common.util.Maps;
2120
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
21+
import org.elasticsearch.common.util.set.Sets;
2222
import org.elasticsearch.logging.LogManager;
2323
import org.elasticsearch.logging.Logger;
2424

@@ -89,20 +89,25 @@ public void applyClusterState(ClusterChangedEvent event) {
8989
.put(nodeGcsSettings)
9090
.setSecureSettings(projectSecrets.getSettings())
9191
.build();
92-
final Map<String, GoogleCloudStorageClientSettings> clientSettings = GoogleCloudStorageClientSettings.load(currentSettings)
93-
.entrySet()
92+
93+
final var allClientSettings = GoogleCloudStorageClientSettings.load(currentSettings);
94+
assert allClientSettings.isEmpty() == false;
95+
// Skip project clients that have no credentials configured. This should not happen in serverless.
96+
// But it is safer to skip them and is also a more consistent behaviour with the cases when
97+
// project secrets are not present.
98+
final var clientSettings = allClientSettings.entrySet()
9499
.stream()
95-
// Skip project clients that have no credentials configured. This should not happen in serverless.
96-
// But it is safer to skip them and is also a more consistent behaviour with the cases when
97-
// project secrets are not present.
98100
.filter(entry -> entry.getValue().getCredential() != null)
99101
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
100102

101-
if (clientSettings.isEmpty()) {
102-
// clientSettings should not be empty, i.e. there should be at least one client configured.
103-
// But if it does somehow happen, log a warning and continue. The project will not have usable client but that is ok.
104-
logger.warn("Skipping project [{}] with no client settings", project.id());
105-
continue;
103+
if (allClientSettings.size() != clientSettings.size()) {
104+
logger.warn(
105+
"Project [{}] has [{}] GCS client settings, but [{}] is usable due to missing credentials for clients {}",
106+
project.id(),
107+
allClientSettings.size(),
108+
clientSettings.size(),
109+
Sets.difference(allClientSettings.keySet(), clientSettings.keySet())
110+
);
106111
}
107112

108113
// TODO: If performance is an issue, we may consider comparing just the relevant project secrets for new or updated clients
@@ -221,10 +226,7 @@ MeteredStorage client(final String clientName, final String repositoryName, fina
221226

222227
if (settings == null) {
223228
throw new IllegalArgumentException(
224-
"Unknown client name ["
225-
+ clientName
226-
+ "]. Existing client configs: "
227-
+ Strings.collectionToDelimitedString(allClientSettings().keySet(), ",")
229+
"Unknown client name [" + clientName + "]. Existing client configs: " + allClientSettings().keySet()
228230
);
229231
}
230232

modules/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageClientsManagerTests.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
import java.util.List;
3838
import java.util.Map;
3939
import java.util.concurrent.atomic.AtomicInteger;
40+
import java.util.stream.Collectors;
4041
import java.util.stream.IntStream;
42+
import java.util.stream.Stream;
4143

4244
import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.CREDENTIALS_FILE_SETTING;
4345
import static org.hamcrest.Matchers.anEmptyMap;
@@ -69,7 +71,8 @@ public void setUp() throws Exception {
6971
super.setUp();
7072
privateKeyIdGenerators = ConcurrentCollections.newConcurrentMap();
7173
statsCollector = new GcsRepositoryStatsCollector();
72-
clientNames = IntStream.range(0, between(2, 5)).mapToObj(i -> randomIdentifier() + "_" + i).toList();
74+
clientNames = Stream.concat(Stream.of("default"), IntStream.range(0, between(1, 4)).mapToObj(i -> randomIdentifier() + "_" + i))
75+
.toList();
7376

7477
final Settings.Builder builder = Settings.builder();
7578
final var mockSecureSettings = new MockSecureSettings();
@@ -182,6 +185,32 @@ public void testClientsLifeCycleForSingleProject() throws Exception {
182185
assertThat(gcsClientsManager.getPerProjectClientsHolders(), not(hasKey(projectId)));
183186
}
184187

188+
public void testClientsWithNoCredentialsAreFilteredOut() throws IOException {
189+
final ProjectId projectId = randomUniqueProjectId();
190+
updateProjectInClusterState(projectId, newProjectClientsSecrets(projectId, clientNames.toArray(String[]::new)));
191+
for (var clientName : clientNames) {
192+
assertNotNull(getClientFromManager(projectId, clientName));
193+
}
194+
195+
final List<String> clientsWithIncorrectSecretsConfig = randomNonEmptySubsetOf(clientNames);
196+
197+
updateProjectInClusterState(projectId, clientNames.stream().collect(Collectors.toUnmodifiableMap(clientName -> {
198+
if (clientsWithIncorrectSecretsConfig.contains(clientName)) {
199+
return "gcs.client." + clientName + ".some_non_existing_setting";
200+
} else {
201+
return CREDENTIALS_FILE_SETTING.getConcreteSettingForNamespace(clientName).getKey();
202+
}
203+
}, clientName -> TestUtils.createServiceAccount(random(), projectClientPrivateKeyId(projectId, clientName)))));
204+
205+
for (var clientName : clientNames) {
206+
if (clientsWithIncorrectSecretsConfig.contains(clientName)) {
207+
assertClientNotFound(projectId, clientName);
208+
} else {
209+
assertNotNull(getClientFromManager(projectId, clientName));
210+
}
211+
}
212+
}
213+
185214
public void testClientsForMultipleProjects() throws InterruptedException {
186215
final List<ProjectId> projectIds = randomList(2, 8, ESTestCase::randomUniqueProjectId);
187216

0 commit comments

Comments
 (0)