Skip to content

Commit 7dde9d4

Browse files
authored
Ensure type exists for all monitoring configuration (#57399)
#47711 and #47246 helped to validate that monitoring settings are rejected at time of setting the monitoring settings. Else an invalid monitoring setting can find it's way into the cluster state and result in an exception thrown [1] on the cluster state application (there by causing significant issues). Some additional monitoring settings have been identified that can result in invalid cluster state that also result in exceptions thrown on cluster state application. All settings require a type of either http or local to be applicable. When a setting is changed, the exporters are automatically updated with the new settings. However, if the old or new settings lack of a type setting an exception will be thrown (since exporters are always of type 'http' or 'local'). Arguably we shouldn't blindly create and destroy new exporters on each monitoring setting update, but the lifecycle of the exporters is abit out the scope this PR is trying to address. This commit introduces a similar methodology to check for validity as #47711 and #47246 but this time for ALL (including non-http) settings. Monitoring settings are not useful unless there an exporter with a type defined. The type is used as dependent setting, such that it must exist to set the value. This ensures that when any monitoring settings changes that they can only get added to cluster state if the type exists. If the type exists (and the other validations pass) then the exporters will get re-built and the cluster state remains valid. Tests have been included to ensure that all dynamic monitoring settings have the type as dependent settings. [1] org.elasticsearch.common.settings.SettingsException: missing exporter type for [found-user-defined] exporter at org.elasticsearch.xpack.monitoring.exporter.Exporters.initExporters(Exporters.java:126) ~[?:?]
1 parent 1c7bd29 commit 7dde9d4

File tree

8 files changed

+84
-20
lines changed

8 files changed

+84
-20
lines changed

server/src/main/java/org/elasticsearch/common/settings/Setting.java

+9
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,15 @@ private Stream<String> matchStream(Settings settings) {
748748
return settings.keySet().stream().filter(this::match).map(key::getConcreteString);
749749
}
750750

751+
/**
752+
* Get the raw list of dependencies. This method is exposed for testing purposes and {@link #getSettingsDependencies(String)}
753+
* should be preferred for most all cases.
754+
* @return the raw list of dependencies for this setting
755+
*/
756+
public Set<AffixSettingDependency> getDependencies() {
757+
return Collections.unmodifiableSet(dependencies);
758+
}
759+
751760
@Override
752761
public Set<SettingDependency> getSettingsDependencies(String settingsKey) {
753762
if (dependencies.isEmpty()) {

x-pack/plugin/monitoring/src/internalClusterTest/java/org/elasticsearch/xpack/monitoring/integration/MonitoringIT.java

+2
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ public void enableMonitoring() throws Exception {
364364

365365
final Settings settings = Settings.builder()
366366
.put("xpack.monitoring.collection.enabled", true)
367+
.put("xpack.monitoring.exporters._local.type", "local")
367368
.put("xpack.monitoring.exporters._local.enabled", true)
368369
.build();
369370

@@ -390,6 +391,7 @@ public void enableMonitoring() throws Exception {
390391
public void disableMonitoring() throws Exception {
391392
final Settings settings = Settings.builder()
392393
.putNull("xpack.monitoring.collection.enabled")
394+
.putNull("xpack.monitoring.exporters._local.type")
393395
.putNull("xpack.monitoring.exporters._local.enabled")
394396
.putNull("cluster.metadata.display_name")
395397
.build();

x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/Exporter.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525

2626
public abstract class Exporter implements AutoCloseable {
2727

28+
public static Setting.AffixSettingDependency TYPE_DEPENDENCY = () -> Exporter.TYPE_SETTING;
29+
2830
private static final Setting.AffixSetting<Boolean> ENABLED_SETTING =
2931
Setting.affixKeySetting("xpack.monitoring.exporters.","enabled",
30-
key -> Setting.boolSetting(key, true, Property.Dynamic, Property.NodeScope));
32+
key -> Setting.boolSetting(key, true, Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY);
3133

3234
public static final Setting.AffixSetting<String> TYPE_SETTING = Setting.affixKeySetting(
3335
"xpack.monitoring.exporters.",
@@ -82,21 +84,22 @@ public Iterator<Setting<?>> settings() {
8284
*/
8385
public static final Setting.AffixSetting<Boolean> USE_INGEST_PIPELINE_SETTING =
8486
Setting.affixKeySetting("xpack.monitoring.exporters.","use_ingest",
85-
key -> Setting.boolSetting(key, true, Property.Dynamic, Property.NodeScope));
87+
key -> Setting.boolSetting(key, true, Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY);
8688
/**
8789
* Every {@code Exporter} allows users to explicitly disable cluster alerts.
8890
*/
8991
public static final Setting.AffixSetting<Boolean> CLUSTER_ALERTS_MANAGEMENT_SETTING =
9092
Setting.affixKeySetting("xpack.monitoring.exporters.", "cluster_alerts.management.enabled",
91-
key -> Setting.boolSetting(key, true, Property.Dynamic, Property.NodeScope));
93+
key -> Setting.boolSetting(key, true, Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY);
9294
/**
9395
* Every {@code Exporter} allows users to explicitly disable specific cluster alerts.
9496
* <p>
9597
* When cluster alerts management is enabled, this should delete anything blacklisted here in addition to not creating it.
9698
*/
9799
public static final Setting.AffixSetting<List<String>> CLUSTER_ALERTS_BLACKLIST_SETTING = Setting
98100
.affixKeySetting("xpack.monitoring.exporters.", "cluster_alerts.management.blacklist",
99-
key -> Setting.listSetting(key, Collections.emptyList(), Function.identity(), Property.Dynamic, Property.NodeScope));
101+
key -> Setting.listSetting(key, Collections.emptyList(), Function.identity(), Property.Dynamic, Property.NodeScope),
102+
TYPE_DEPENDENCY);
100103

101104
/**
102105
* Every {@code Exporter} allows users to use a different index time format.
@@ -108,7 +111,7 @@ public Iterator<Setting<?>> settings() {
108111
Exporter.INDEX_FORMAT,
109112
DateFormatter::forPattern,
110113
Property.Dynamic,
111-
Property.NodeScope));
114+
Property.NodeScope), TYPE_DEPENDENCY);
112115

113116
private static final String INDEX_FORMAT = "yyyy.MM.dd";
114117

x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpExporter.java

+16-14
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public class HttpExporter extends Exporter {
8383

8484
public static final String TYPE = "http";
8585

86-
private static Setting.AffixSettingDependency TYPE_DEPENDENCY = new Setting.AffixSettingDependency() {
86+
private static Setting.AffixSettingDependency HTTP_TYPE_DEPENDENCY = new Setting.AffixSettingDependency() {
8787
@Override
8888
public Setting.AffixSetting<String> getSetting() {
8989
return Exporter.TYPE_SETTING;
@@ -169,30 +169,32 @@ public Iterator<Setting<?>> settings() {
169169
},
170170
Property.Dynamic,
171171
Property.NodeScope),
172-
TYPE_DEPENDENCY);
172+
HTTP_TYPE_DEPENDENCY);
173173

174174
/**
175175
* Master timeout associated with bulk requests.
176176
*/
177177
public static final Setting.AffixSetting<TimeValue> BULK_TIMEOUT_SETTING =
178178
Setting.affixKeySetting("xpack.monitoring.exporters.","bulk.timeout",
179-
(key) -> Setting.timeSetting(key, TimeValue.MINUS_ONE, Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY);
179+
(key) -> Setting.timeSetting(key, TimeValue.MINUS_ONE, Property.Dynamic, Property.NodeScope), HTTP_TYPE_DEPENDENCY);
180180
/**
181181
* Timeout used for initiating a connection.
182182
*/
183183
public static final Setting.AffixSetting<TimeValue> CONNECTION_TIMEOUT_SETTING =
184184
Setting.affixKeySetting(
185185
"xpack.monitoring.exporters.",
186186
"connection.timeout",
187-
(key) -> Setting.timeSetting(key, TimeValue.timeValueSeconds(6), Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY);
187+
(key) -> Setting.timeSetting(key, TimeValue.timeValueSeconds(6), Property.Dynamic, Property.NodeScope),
188+
HTTP_TYPE_DEPENDENCY);
188189
/**
189190
* Timeout used for reading from the connection.
190191
*/
191192
public static final Setting.AffixSetting<TimeValue> CONNECTION_READ_TIMEOUT_SETTING =
192193
Setting.affixKeySetting(
193194
"xpack.monitoring.exporters.",
194195
"connection.read_timeout",
195-
(key) -> Setting.timeSetting(key, TimeValue.timeValueSeconds(60), Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY);
196+
(key) -> Setting.timeSetting(key, TimeValue.timeValueSeconds(60), Property.Dynamic, Property.NodeScope),
197+
HTTP_TYPE_DEPENDENCY);
196198
/**
197199
* Username for basic auth.
198200
*/
@@ -236,7 +238,7 @@ public Iterator<Setting<?>> settings() {
236238
Property.Dynamic,
237239
Property.NodeScope,
238240
Property.Filtered),
239-
TYPE_DEPENDENCY);
241+
HTTP_TYPE_DEPENDENCY);
240242
/**
241243
* Secure password for basic auth.
242244
*/
@@ -245,7 +247,7 @@ public Iterator<Setting<?>> settings() {
245247
"xpack.monitoring.exporters.",
246248
"auth.secure_password",
247249
key -> SecureSetting.secureString(key, null),
248-
TYPE_DEPENDENCY);
250+
HTTP_TYPE_DEPENDENCY);
249251
/**
250252
* The SSL settings.
251253
*
@@ -256,7 +258,7 @@ public Iterator<Setting<?>> settings() {
256258
"xpack.monitoring.exporters.",
257259
"ssl",
258260
(key) -> Setting.groupSetting(key + ".", Property.Dynamic, Property.NodeScope, Property.Filtered),
259-
TYPE_DEPENDENCY);
261+
HTTP_TYPE_DEPENDENCY);
260262

261263
/**
262264
* Proxy setting to allow users to send requests to a remote cluster that requires a proxy base path.
@@ -277,13 +279,13 @@ public Iterator<Setting<?>> settings() {
277279
},
278280
Property.Dynamic,
279281
Property.NodeScope),
280-
TYPE_DEPENDENCY);
282+
HTTP_TYPE_DEPENDENCY);
281283
/**
282284
* A boolean setting to enable or disable sniffing for extra connections.
283285
*/
284286
public static final Setting.AffixSetting<Boolean> SNIFF_ENABLED_SETTING =
285287
Setting.affixKeySetting("xpack.monitoring.exporters.","sniff.enabled",
286-
(key) -> Setting.boolSetting(key, false, Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY);
288+
(key) -> Setting.boolSetting(key, false, Property.Dynamic, Property.NodeScope), HTTP_TYPE_DEPENDENCY);
287289
/**
288290
* A parent setting to header key/value pairs, whose names are user defined.
289291
*/
@@ -306,7 +308,7 @@ public Iterator<Setting<?>> settings() {
306308
},
307309
Property.Dynamic,
308310
Property.NodeScope),
309-
TYPE_DEPENDENCY);
311+
HTTP_TYPE_DEPENDENCY);
310312
/**
311313
* Blacklist of headers that the user is not allowed to set.
312314
* <p>
@@ -318,19 +320,19 @@ public Iterator<Setting<?>> settings() {
318320
*/
319321
public static final Setting.AffixSetting<TimeValue> TEMPLATE_CHECK_TIMEOUT_SETTING =
320322
Setting.affixKeySetting("xpack.monitoring.exporters.","index.template.master_timeout",
321-
(key) -> Setting.timeSetting(key, TimeValue.MINUS_ONE, Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY);
323+
(key) -> Setting.timeSetting(key, TimeValue.MINUS_ONE, Property.Dynamic, Property.NodeScope), HTTP_TYPE_DEPENDENCY);
322324
/**
323325
* A boolean setting to enable or disable whether to create placeholders for the old templates.
324326
*/
325327
public static final Setting.AffixSetting<Boolean> TEMPLATE_CREATE_LEGACY_VERSIONS_SETTING =
326328
Setting.affixKeySetting("xpack.monitoring.exporters.","index.template.create_legacy_templates",
327-
(key) -> Setting.boolSetting(key, true, Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY);
329+
(key) -> Setting.boolSetting(key, true, Property.Dynamic, Property.NodeScope), HTTP_TYPE_DEPENDENCY);
328330
/**
329331
* ES level timeout used when checking and writing pipelines (used to speed up tests)
330332
*/
331333
public static final Setting.AffixSetting<TimeValue> PIPELINE_CHECK_TIMEOUT_SETTING =
332334
Setting.affixKeySetting("xpack.monitoring.exporters.","index.pipeline.master_timeout",
333-
(key) -> Setting.timeSetting(key, TimeValue.MINUS_ONE, Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY);
335+
(key) -> Setting.timeSetting(key, TimeValue.MINUS_ONE, Property.Dynamic, Property.NodeScope), HTTP_TYPE_DEPENDENCY);
334336

335337
/**
336338
* Minimum supported version of the remote monitoring cluster (same major).

x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public class LocalExporter extends Exporter implements ClusterStateListener, Cle
9696
public static final Setting.AffixSetting<TimeValue> WAIT_MASTER_TIMEOUT_SETTING = Setting.affixKeySetting(
9797
"xpack.monitoring.exporters.",
9898
"wait_master.timeout",
99-
(key) -> Setting.timeSetting(key, TimeValue.timeValueSeconds(30), Property.Dynamic, Property.NodeScope)
99+
(key) -> Setting.timeSetting(key, TimeValue.timeValueSeconds(30), Property.Dynamic, Property.NodeScope), TYPE_DEPENDENCY
100100
);
101101

102102
private final Client client;

x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/ExportersTests.java

+45
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@
4949
import java.util.concurrent.TimeUnit;
5050
import java.util.concurrent.atomic.AtomicInteger;
5151
import java.util.concurrent.atomic.AtomicReference;
52+
import java.util.stream.Collectors;
5253

54+
import static org.hamcrest.Matchers.contains;
5355
import static org.hamcrest.Matchers.containsString;
5456
import static org.hamcrest.Matchers.empty;
5557
import static org.hamcrest.Matchers.equalTo;
@@ -337,6 +339,49 @@ public void testConcurrentExports() throws Exception {
337339
exporters.close();
338340
}
339341

342+
/**
343+
* All dynamic monitoring settings should have dependency on type.
344+
*/
345+
public void testSettingsDependency() {
346+
List<Setting.AffixSetting<?>> settings = Exporters.getSettings().stream().filter(Setting::isDynamic).collect(Collectors.toList());
347+
settings.stream().filter(s -> s.getKey().equals("xpack.monitoring.exporters.*.type") == false)
348+
.forEach(setting -> assertThat(setting.getKey() + " does not have a dependency on type",
349+
setting.getDependencies().stream().map(Setting.AffixSettingDependency::getSetting).distinct().collect(Collectors.toList()),
350+
contains(Exporter.TYPE_SETTING)));
351+
}
352+
353+
/**
354+
* This is a variation of testing that all settings validated at cluster state update ensure that the type is set. This workflow
355+
* emulates adding valid settings, then attempting to remove the type. This should never be allowed since type if type is null
356+
* then any associated settings are extraneous and thus invalid (and can cause validation issues on cluster state application).
357+
*/
358+
public void testRemoveType() {
359+
//run the update for all dynamic settings and ensure that they correctly throw an exception
360+
List<Setting.AffixSetting<?>> settings = Exporters.getSettings().stream().filter(Setting::isDynamic).collect(Collectors.toList());
361+
settings.stream().filter(s -> s.getKey().equals("xpack.monitoring.exporters.*.type") == false)
362+
.forEach(setting -> {
363+
String fullSettingName = setting.getKey().replace("*", "foobar");
364+
Settings nodeSettings = Settings.builder()
365+
.put("xpack.monitoring.exporters.foobar.type", randomFrom("local, http")) //actual type should not matter
366+
.put(fullSettingName, "")
367+
.build();
368+
369+
clusterSettings = new ClusterSettings(nodeSettings, new HashSet<>(Exporters.getSettings()));
370+
when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
371+
372+
Settings update = Settings.builder()
373+
.put("xpack.monitoring.exporters.foobar.type", (String) null)
374+
.build();
375+
376+
Settings.Builder target = Settings.builder().put(nodeSettings);
377+
clusterSettings.updateDynamicSettings(update, target, Settings.builder(), "persistent");
378+
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
379+
() -> clusterSettings.validate(target.build(), true));
380+
assertThat(e.getMessage(),
381+
containsString("missing required setting [xpack.monitoring.exporters.foobar.type] for setting [" + fullSettingName));
382+
});
383+
}
384+
340385
/**
341386
* Attempt to export a random number of documents via {@code exporters} from multiple threads.
342387
*

x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/local/LocalExporterIntegTests.java

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ private void stopMonitoring() {
6565
// Now disabling the monitoring service, so that no more collection are started
6666
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(
6767
Settings.builder().putNull(MonitoringService.ENABLED.getKey())
68+
.putNull("xpack.monitoring.exporters._local.type")
6869
.putNull("xpack.monitoring.exporters._local.enabled")
6970
.putNull("xpack.monitoring.exporters._local.cluster_alerts.management.enabled")
7071
.putNull("xpack.monitoring.exporters._local.index.name.time_format")));
@@ -85,6 +86,7 @@ public void testExport() throws Exception {
8586
// start the monitoring service so that /_monitoring/bulk is not ignored
8687
final Settings.Builder exporterSettings = Settings.builder()
8788
.put(MonitoringService.ENABLED.getKey(), true)
89+
.put("xpack.monitoring.exporters._local.type", LocalExporter.TYPE)
8890
.put("xpack.monitoring.exporters._local.enabled", true)
8991
.put("xpack.monitoring.exporters._local.cluster_alerts.management.enabled", false);
9092

x-pack/plugin/src/test/java/org/elasticsearch/xpack/test/rest/XPackRestIT.java

+1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ private void enableMonitoring() throws Exception {
123123
final Map<String, Object> settings = new HashMap<>();
124124
settings.put("xpack.monitoring.collection.enabled", true);
125125
settings.put("xpack.monitoring.collection.interval", "1s");
126+
settings.put("xpack.monitoring.exporters._local.type", "local");
126127
settings.put("xpack.monitoring.exporters._local.enabled", true);
127128

128129
awaitCallApi("cluster.put_settings", emptyMap(),

0 commit comments

Comments
 (0)