Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions docs/reference/watcher/how-watcher-works.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,18 @@ add, the more distributed the watches can be executed. If you add or remove
replicas, all watches need to be reloaded. If a shard is relocated, the
primary and all replicas of this particular shard will reload.

Because the watches are executed on the node, where the watch shards are, you can create
dedicated watcher nodes by using shard allocation filtering.
Because the watches are executed on the node, where the watch shards are, you
can create dedicated watcher nodes by using shard allocation filtering. To do this
, configure nodes with a dedicated `node.attr.role: watcher` property.

You could configure nodes with a dedicated `node.attr.role: watcher` property and
then configure the `.watches` index like this:
As the `.watches` index is a system index, you can't use the normal `.watcher/_settings`
endpoint to modify its routing allocation. Instead, you can use the following dedicated
endpoint to adjust the allocation of the `.watches` shards to the nodes with the
`watcher` role attribute:

[source,console]
------------------------
PUT .watches/_settings
PUT _watcher/settings
{
"index.routing.allocation.include.role": "watcher"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.core.UpdateForV9;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class UpdateWatcherSettingsAction extends ActionType<AcknowledgedResponse> {

Expand All @@ -34,6 +34,16 @@ public class UpdateWatcherSettingsAction extends ActionType<AcknowledgedResponse
IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS
);

public static final Set<String> ALLOWED_SETTINGS_PREFIXES = Set.of(
IndexMetadata.INDEX_ROUTING_EXCLUDE_GROUP_PREFIX,
IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_PREFIX,
IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_PREFIX
);

public static final Set<String> EXPLICITLY_DENIED_SETTINGS = Set.of(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you're denying this one because we always want the .watches shards in the hot tier? That makes me wonder what other possible values there are for these afix settings, and if we ought to just explicitly allow the index.routing.allocation.exclude.role, index.routing.allocation.include.role, and index.routing.allocation.require.role ones and disallow anything else to avoid causing some unforeseen problems. You've probably looked at all these settings more than I have -- what do you think?

Copy link
Contributor Author

@lukewhiting lukewhiting Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I based this restriction on what the default setting for the .watcher index are. I assumed we put it in the hot tier for good reason so locked off the ability to change that. I may be wrong in that assumption.

Locking down the index.routing.allocation.*.* settings further is difficult / undesirable. index.routing.allocation.include.role isn't actually a setting as such but a convention for a wildcard setting. Anything after index.routing.allocation.include. is assumed to be an arbitrary node attribute hence my leaving it as allowing any setting with that prefix as we have no way of knowing what attributes the end user might have set on their nodes and how they would like to use those for scheduling watches.

For instance index.routing.allocation.include.sandwich_filling=turkey is a valid setting for this if the user chose to use a bread based node attributes scheme to group their cluster members :-)

IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_PREFIX + "._tier_preference"
);

public UpdateWatcherSettingsAction() {
super(NAME);
}
Expand Down Expand Up @@ -79,13 +89,25 @@ public Map<String, Object> settings() {

@Override
public ActionRequestValidationException validate() {
Set<String> forbiddenSettings = Sets.difference(settings.keySet(), ALLOWED_SETTING_KEYS);
if (forbiddenSettings.size() > 0) {
Set<String> forbiddenSettings = settings.keySet()
.stream()
.filter(
setting -> (ALLOWED_SETTING_KEYS.contains(setting) == false
&& ALLOWED_SETTINGS_PREFIXES.stream().noneMatch(prefix -> setting.startsWith(prefix + ".")))
|| EXPLICITLY_DENIED_SETTINGS.contains(setting)
)
.collect(Collectors.toSet());

if (forbiddenSettings.isEmpty() == false) {
return ValidateActions.addValidationError(
"illegal settings: "
+ forbiddenSettings
+ ", these settings may not be configured. Only the following settings may be configured: "
+ ALLOWED_SETTING_KEYS,
+ ALLOWED_SETTING_KEYS
+ ", "
+ ALLOWED_SETTINGS_PREFIXES.stream().map(s -> s + ".*").collect(Collectors.toSet())
+ " excluding the following explicitly denied settings: "
+ EXPLICITLY_DENIED_SETTINGS,
null
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
setup:
- do:
cluster.health:
wait_for_status: yellow
- do:
watcher.put_watch:
id: "my_watch"
body: >
{
"trigger": {
"schedule": {
"hourly": {
"minute": [ 0, 5 ]
}
}
},
"input": {
"simple": {
"payload": {
"send": "yes"
}
}
},
"condition": {
"always": {}
},
"actions": {
"test_index": {
"index": {
"index": "test"
}
}
}
}

---
"Test update and get watch settings api":
- do:
watcher.get_settings: { }

- match: { index.auto_expand_replicas: "0-1" }
- match: { index.number_of_replicas: "0" }

- do:
watcher.update_settings:
body:
index.auto_expand_replicas: "0-all"

- do:
watcher.get_settings: { }

- match: { index.auto_expand_replicas: "0-all" }
- is_false: index.routing.allocation.include._tier_preference

- do:
watcher.update_settings:
body:
index.auto_expand_replicas: null
index.number_of_replicas: 1

- do:
watcher.get_settings: { }

- match: { index.number_of_replicas: "1" }
---
"Test disallowed setting name throws error":
- requires:
test_runner_features: regex
- do:
watcher.update_settings:
body:
index.disallowed_setting: "some_invalid_value"
catch: bad_request
- match:
error:
type: "action_request_validation_exception"
reason: '/illegal settings\: \[index.disallowed_setting\].*/'
---
"Test allowed prefix setting name":
- do:
watcher.update_settings:
body:
index.routing.allocation.include.role: "watcher"
index.routing.allocation.exclude.role: "noWatcher"
index.routing.allocation.require.role: "mustWatcher"
- do:
watcher.get_settings: { }
- match: { index.routing.allocation.include.role: "watcher" }
- match: { index.routing.allocation.exclude.role: "noWatcher" }
- match: { index.routing.allocation.require.role: "mustWatcher" }
---
"Test explicitly disallowed prefix setting name throws error":
- requires:
test_runner_features: regex
- do:
watcher.update_settings:
body:
index.routing.allocation.include.disallowed_prefix: "some_invalid_value"
catch: bad_request
- match:
error:
type: "action_request_validation_exception"
reason: '/illegal settings\: \[index.routing.allocation.include.disallowed_prefix\].*/'

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.watcher.transport.actions.put.GetWatcherSettingsAction;
import org.elasticsearch.xpack.core.watcher.transport.actions.put.UpdateWatcherSettingsAction;

import static org.elasticsearch.xpack.core.watcher.transport.actions.put.UpdateWatcherSettingsAction.ALLOWED_SETTINGS_PREFIXES;
import static org.elasticsearch.xpack.core.watcher.transport.actions.put.UpdateWatcherSettingsAction.ALLOWED_SETTING_KEYS;
import static org.elasticsearch.xpack.core.watcher.transport.actions.put.UpdateWatcherSettingsAction.EXPLICITLY_DENIED_SETTINGS;
import static org.elasticsearch.xpack.watcher.transport.actions.TransportUpdateWatcherSettingsAction.WATCHER_INDEX_NAME;
import static org.elasticsearch.xpack.watcher.transport.actions.TransportUpdateWatcherSettingsAction.WATCHER_INDEX_REQUEST;

Expand Down Expand Up @@ -73,11 +75,14 @@ protected void masterOperation(
*/
private static Settings filterSettableSettings(Settings settings) {
Settings.Builder builder = Settings.builder();
for (String settingName : UpdateWatcherSettingsAction.ALLOWED_SETTING_KEYS) {
if (settings.hasValue(settingName)) {
builder.put(settingName, settings.get(settingName));
}
}
settings.keySet()
.stream()
.filter(
setting -> (ALLOWED_SETTING_KEYS.contains(setting)
|| ALLOWED_SETTINGS_PREFIXES.stream().anyMatch(prefix -> setting.startsWith(prefix + ".")))
&& EXPLICITLY_DENIED_SETTINGS.contains(setting) == false
)
.forEach(setting -> builder.put(setting, settings.get(setting)));
return builder.build();
}

Expand Down
Loading