Skip to content

Commit 033fbbb

Browse files
Add test for ClusterSettings impl, add javadoc
1 parent 2cc57b4 commit 033fbbb

File tree

4 files changed

+182
-0
lines changed

4 files changed

+182
-0
lines changed

server/src/main/java/org/elasticsearch/transport/AbstractLinkedProjectConfigService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
import java.util.List;
1515
import java.util.concurrent.CopyOnWriteArrayList;
1616

17+
/**
18+
* Abstract base class for {@link LinkedProjectConfigService} implementations.
19+
* Provides common functionality for managing a list of registered listeners and notifying them of updates.
20+
*/
1721
public abstract class AbstractLinkedProjectConfigService implements LinkedProjectConfigService {
1822
private final List<LinkedProjectConfigListener> listeners = new CopyOnWriteArrayList<>();
1923

server/src/main/java/org/elasticsearch/transport/ClusterSettingsLinkedProjectConfigService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
import java.util.Collection;
1919
import java.util.List;
2020

21+
/**
22+
* A {@link LinkedProjectConfigService} implementation that listens for {@link ClusterSettings} changes,
23+
* creating {@link LinkedProjectConfig}s from the relevant settings and notifying registered listeners of updates.
24+
*/
2125
public class ClusterSettingsLinkedProjectConfigService extends AbstractLinkedProjectConfigService {
2226
private final Settings settings;
2327
private final ProjectResolver projectResolver;

server/src/main/java/org/elasticsearch/transport/LinkedProjectConfigService.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,32 @@
1414
import java.util.Collection;
1515
import java.util.Collections;
1616

17+
/**
18+
* Service for registering {@link LinkedProjectConfigListener}s to be notified of changes to linked project configurations.
19+
*/
1720
public interface LinkedProjectConfigService {
1821

22+
/**
23+
* Listener interface for receiving updates about linked project configurations.
24+
* Implementations must not throw from any of the interface methods.
25+
*/
1926
interface LinkedProjectConfigListener {
27+
/**
28+
* Called when a linked project configuration has been added or updated.
29+
*
30+
* @param config The updated {@link LinkedProjectConfig}.
31+
*/
2032
void updateLinkedProject(LinkedProjectConfig config);
2133

34+
/**
35+
* Called when the boolean skip_unavailable setting has changed for a linked project configuration.
36+
* Note that skip_unavailable may not be supported in all contexts where linked projects are used.
37+
*
38+
* @param originProjectId The {@link ProjectId} of the owning project that has the linked project configuration.
39+
* @param linkedProjectId The {@link ProjectId} of the linked project.
40+
* @param linkedProjectAlias The alias used for the linked project.
41+
* @param skipUnavailable The new value of the skip_unavailable setting.
42+
*/
2243
default void skipUnavailableChanged(
2344
ProjectId originProjectId,
2445
ProjectId linkedProjectId,
@@ -27,10 +48,24 @@ default void skipUnavailableChanged(
2748
) {}
2849
}
2950

51+
/**
52+
* Registers a {@link LinkedProjectConfigListener} to receive updates about linked project configurations.
53+
*
54+
* @param listener The listener to register.
55+
*/
3056
void register(LinkedProjectConfigListener listener);
3157

58+
/**
59+
* Loads all existing linked project configurations for all origin projects.
60+
*
61+
* @return A collection of all existing {@link LinkedProjectConfig}s.
62+
*/
3263
Collection<LinkedProjectConfig> loadAllLinkedProjectConfigs();
3364

65+
/**
66+
* A no-op stub implementation of {@link LinkedProjectConfigService} intended for use in test scenarios where linked project
67+
* configuration updates are not needed.
68+
*/
3469
LinkedProjectConfigService NOOP = new LinkedProjectConfigService() {
3570
@Override
3671
public void register(LinkedProjectConfigListener listener) {}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.transport;
11+
12+
import org.elasticsearch.cluster.metadata.ProjectId;
13+
import org.elasticsearch.cluster.project.DefaultProjectResolver;
14+
import org.elasticsearch.common.settings.ClusterSettings;
15+
import org.elasticsearch.common.settings.Settings;
16+
import org.elasticsearch.test.ESTestCase;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
21+
import static org.elasticsearch.transport.LinkedProjectConfigService.LinkedProjectConfigListener;
22+
import static org.elasticsearch.transport.LinkedProjectConfig.ProxyLinkedProjectConfigBuilder;
23+
import static org.hamcrest.Matchers.equalTo;
24+
import static org.hamcrest.Matchers.sameInstance;
25+
26+
public class ClusterSettingsLinkedProjectConfigServiceTests extends ESTestCase {
27+
28+
private record SkipUnavailableUpdate(
29+
ProjectId originProjectId,
30+
ProjectId linkedProjectId,
31+
String linkedProjectAlias,
32+
boolean skipUnavailable
33+
) {}
34+
35+
private static class StubLinkedProjectConfigListener implements LinkedProjectConfigListener {
36+
LinkedProjectConfig updatedConfig;
37+
SkipUnavailableUpdate skipUnavailableUpdate;
38+
39+
@Override
40+
public void updateLinkedProject(LinkedProjectConfig config) {
41+
updatedConfig = config;
42+
}
43+
44+
@Override
45+
public void skipUnavailableChanged(
46+
ProjectId originProjectId,
47+
ProjectId linkedProjectId,
48+
String linkedProjectAlias,
49+
boolean skipUnavailable
50+
) {
51+
skipUnavailableUpdate = new SkipUnavailableUpdate(originProjectId, linkedProjectId, linkedProjectAlias, skipUnavailable);
52+
}
53+
54+
void reset() {
55+
updatedConfig = null;
56+
skipUnavailableUpdate = null;
57+
}
58+
}
59+
60+
/**
61+
* A simple test to exercise the callback registration and notification mechanism.
62+
* Note that {@link RemoteClusterServiceTests} uses {@link ClusterSettingsLinkedProjectConfigService}
63+
* and contains more thorough tests of all the settings being monitored.
64+
*/
65+
public void testListenersReceiveUpdates() {
66+
final var alias = randomAlphaOfLength(10);
67+
68+
final var initialProxyAddress = "localhost:9400";
69+
final var initialSettings = Settings.builder()
70+
.put("cluster.remote." + alias + ".mode", "proxy")
71+
.put("cluster.remote." + alias + ".proxy_address", initialProxyAddress)
72+
.build();
73+
final var clusterSettings = ClusterSettings.createBuiltInClusterSettings(initialSettings);
74+
final var service = new ClusterSettingsLinkedProjectConfigService(
75+
initialSettings,
76+
clusterSettings,
77+
DefaultProjectResolver.INSTANCE
78+
);
79+
final var config = new ProxyLinkedProjectConfigBuilder(alias).proxyAddress(initialProxyAddress).build();
80+
81+
// Verify we can get the linked projects on startup.
82+
assertThat(service.loadAllLinkedProjectConfigs(), equalTo(List.of(config)));
83+
84+
final int numListeners = randomIntBetween(1, 10);
85+
final var listeners = new ArrayList<StubLinkedProjectConfigListener>(numListeners);
86+
for (int i = 0; i < numListeners; ++i) {
87+
listeners.add(new StubLinkedProjectConfigListener());
88+
service.register(listeners.getLast());
89+
}
90+
91+
// Expect no updates when applying the same settings.
92+
clusterSettings.applySettings(initialSettings);
93+
for (int i = 0; i < numListeners; ++i) {
94+
assertThat(listeners.get(i).updatedConfig, sameInstance(null));
95+
assertThat(listeners.get(i).skipUnavailableUpdate, sameInstance(null));
96+
listeners.get(i).reset();
97+
}
98+
99+
// Change the skip_unavailable, leave the other settings alone, we should get the skip_unavailable update only.
100+
var expectedSkipUnavailableUpdate = new SkipUnavailableUpdate(
101+
config.originProjectId(),
102+
config.linkedProjectId(),
103+
config.linkedProjectAlias(),
104+
config.skipUnavailable() == false
105+
);
106+
clusterSettings.applySettings(
107+
Settings.builder()
108+
.put(initialSettings)
109+
.put("cluster.remote." + alias + ".skip_unavailable", expectedSkipUnavailableUpdate.skipUnavailable)
110+
.build()
111+
);
112+
for (int i = 0; i < numListeners; ++i) {
113+
assertThat(listeners.get(i).updatedConfig, sameInstance(null));
114+
assertThat(listeners.get(i).skipUnavailableUpdate, equalTo(expectedSkipUnavailableUpdate));
115+
listeners.get(i).reset();
116+
}
117+
118+
// Change the proxy address, and set skip_unavailable back to original value, we should get both updates.
119+
expectedSkipUnavailableUpdate = new SkipUnavailableUpdate(
120+
config.originProjectId(),
121+
config.linkedProjectId(),
122+
config.linkedProjectAlias(),
123+
config.skipUnavailable()
124+
);
125+
final var newProxyAddress = "localhost:9401";
126+
clusterSettings.applySettings(
127+
Settings.builder()
128+
.put(initialSettings)
129+
.put("cluster.remote." + alias + ".proxy_address", newProxyAddress)
130+
.put("cluster.remote." + alias + ".skip_unavailable", expectedSkipUnavailableUpdate.skipUnavailable)
131+
.build()
132+
);
133+
for (int i = 0; i < numListeners; ++i) {
134+
assertNotNull("expected non-null updatedConfig for listener " + i, listeners.get(i).updatedConfig);
135+
assertThat(listeners.get(i).updatedConfig.proxyAddress(), equalTo(newProxyAddress));
136+
assertThat(listeners.get(i).skipUnavailableUpdate, equalTo(expectedSkipUnavailableUpdate));
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)