Skip to content

Commit 6f7df95

Browse files
committed
Delete legacy watcher templates
We moved watcher to run on the system indices infrastructure in elastic#67588. This commit makes sure Watcher cleans up the legacy templates it used before it was migrated to system indices.
1 parent 5b476d9 commit 6f7df95

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLifeCycleService.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,21 @@
88

99
import org.apache.logging.log4j.LogManager;
1010
import org.apache.logging.log4j.Logger;
11+
import org.apache.logging.log4j.message.ParameterizedMessage;
12+
import org.elasticsearch.action.ActionListener;
1113
import org.elasticsearch.cluster.ClusterChangedEvent;
1214
import org.elasticsearch.cluster.ClusterState;
1315
import org.elasticsearch.cluster.ClusterStateListener;
1416
import org.elasticsearch.cluster.block.ClusterBlockLevel;
1517
import org.elasticsearch.cluster.metadata.IndexMetadata;
18+
import org.elasticsearch.cluster.metadata.IndexTemplateMetadata;
19+
import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService;
1620
import org.elasticsearch.cluster.node.DiscoveryNode;
1721
import org.elasticsearch.cluster.routing.RoutingNode;
1822
import org.elasticsearch.cluster.routing.ShardRouting;
1923
import org.elasticsearch.cluster.service.ClusterService;
2024
import org.elasticsearch.common.Strings;
25+
import org.elasticsearch.common.collect.ImmutableOpenMap;
2126
import org.elasticsearch.common.component.LifecycleListener;
2227
import org.elasticsearch.gateway.GatewayService;
2328
import org.elasticsearch.index.shard.ShardId;
@@ -26,11 +31,13 @@
2631
import org.elasticsearch.xpack.core.watcher.watch.Watch;
2732
import org.elasticsearch.xpack.watcher.watch.WatchStoreUtils;
2833

34+
import java.util.ArrayList;
2935
import java.util.Collections;
3036
import java.util.Comparator;
3137
import java.util.EnumSet;
3238
import java.util.List;
3339
import java.util.Set;
40+
import java.util.concurrent.atomic.AtomicBoolean;
3441
import java.util.concurrent.atomic.AtomicReference;
3542
import java.util.function.Supplier;
3643
import java.util.stream.Collectors;
@@ -41,14 +48,24 @@
4148
public class WatcherLifeCycleService implements ClusterStateListener {
4249

4350
private static final Logger logger = LogManager.getLogger(WatcherLifeCycleService.class);
51+
52+
public static final Set<String> LEGACY_WATCHER_INDEX_TEMPLATES = Set.of(
53+
".watches",
54+
".triggered_watches"
55+
);
56+
4457
private final AtomicReference<WatcherState> state = new AtomicReference<>(WatcherState.STARTED);
4558
private final AtomicReference<List<ShardRouting>> previousShardRoutings = new AtomicReference<>(Collections.emptyList());
4659
private volatile boolean shutDown = false; // indicates that the node has been shutdown and we should never start watcher after this.
60+
private final ClusterService clusterService;
4761
private volatile WatcherService watcherService;
4862
private final EnumSet<WatcherState> stopStates = EnumSet.of(WatcherState.STOPPED, WatcherState.STOPPING);
63+
private final AtomicBoolean legacyTemplatesDeleteInProgress = new AtomicBoolean(false);
64+
private final AtomicBoolean checkForLegacyTemplates = new AtomicBoolean(true);
4965

5066
WatcherLifeCycleService(ClusterService clusterService, WatcherService watcherService) {
5167
this.watcherService = watcherService;
68+
this.clusterService = clusterService;
5269
clusterService.addListener(this);
5370
// Close if the indices service is being stopped, so we don't run into search failures (locally) that will
5471
// happen because we're shutting down and an watch is scheduled.
@@ -96,6 +113,36 @@ public void clusterChanged(ClusterChangedEvent event) {
96113
return;
97114
}
98115

116+
if (event.localNodeMaster() && checkForLegacyTemplates.get()) {
117+
List<String> existingTemplatesToDelete = new ArrayList<>(LEGACY_WATCHER_INDEX_TEMPLATES.size());
118+
ImmutableOpenMap<String, IndexTemplateMetadata> clusterLegacyTemplates = event.state().getMetadata().templates();
119+
for (String legacyWatcherIndexTemplate : LEGACY_WATCHER_INDEX_TEMPLATES) {
120+
if (clusterLegacyTemplates.containsKey(legacyWatcherIndexTemplate)) {
121+
existingTemplatesToDelete.add(legacyWatcherIndexTemplate);
122+
}
123+
}
124+
125+
if (existingTemplatesToDelete.isEmpty() == false) {
126+
// if someone else is executing the deletion of templates (due to fast successive cluster updates), we'll skip doing so
127+
if (legacyTemplatesDeleteInProgress.compareAndSet(false, true) == false) {
128+
return;
129+
}
130+
131+
MetadataIndexTemplateService.removeTemplates(clusterService, LEGACY_WATCHER_INDEX_TEMPLATES,
132+
ActionListener.wrap(r -> {
133+
legacyTemplatesDeleteInProgress.set(false);
134+
// we've done it so we shouldn't check anymore
135+
checkForLegacyTemplates.set(false);
136+
logger.debug("deleted legacy Watcher index templates [{}]", String.join(",", LEGACY_WATCHER_INDEX_TEMPLATES));
137+
}, e -> {
138+
legacyTemplatesDeleteInProgress.set(false);
139+
logger.debug(new ParameterizedMessage("unable to delete legacy Watcher index templates [{}]",
140+
String.join(",", LEGACY_WATCHER_INDEX_TEMPLATES)), e);
141+
})
142+
);
143+
}
144+
}
145+
99146
boolean isWatcherStoppedManually = isWatcherStoppedManually(event.state());
100147
boolean isStoppedOrStopping = stopStates.contains(this.state.get());
101148
// if this is not a data node, we need to start it ourselves possibly

x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherLifeCycleServiceTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
import java.util.Collections;
4242
import java.util.HashSet;
43+
import java.util.Iterator;
4344
import java.util.List;
4445
import java.util.stream.Collectors;
4546
import java.util.stream.IntStream;
@@ -562,6 +563,13 @@ public void testWatcherReloadsOnNodeOutageWithWatcherShard() {
562563
verify(watcherService).reload(eq(event.state()), anyString());
563564
}
564565

566+
public void testLegacyWatcherTemplatesToDelete() {
567+
assertThat(WatcherLifeCycleService.LEGACY_WATCHER_INDEX_TEMPLATES.size(), is(2));
568+
Iterator<String> legacyTemplatesIterator = WatcherLifeCycleService.LEGACY_WATCHER_INDEX_TEMPLATES.iterator();
569+
assertThat(legacyTemplatesIterator.next(), is(".watches"));
570+
assertThat(legacyTemplatesIterator.next(), is(".triggered_watches"));
571+
}
572+
565573
private void startWatcher() {
566574
Index index = new Index(Watch.INDEX, "uuid");
567575
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(index);

x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/WatcherRestartIT.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@
99
import org.apache.http.util.EntityUtils;
1010
import org.elasticsearch.client.Request;
1111
import org.elasticsearch.client.Response;
12+
import org.elasticsearch.client.ResponseException;
1213

14+
import java.io.IOException;
1315
import java.nio.charset.StandardCharsets;
16+
import java.util.Map;
1417

1518
import static org.hamcrest.Matchers.containsString;
19+
import static org.hamcrest.Matchers.is;
1620
import static org.hamcrest.Matchers.not;
1721

1822
public class WatcherRestartIT extends AbstractUpgradeTestCase {
@@ -46,4 +50,30 @@ private void ensureWatcherStarted() throws Exception {
4650
assertThat(responseBody, not(containsString("\"watcher_state\":\"stopped\"")));
4751
});
4852
}
53+
54+
public void testEnsureWatcherDeletesLegacyTemplates() throws Exception {
55+
client().performRequest(new Request("POST", "/_watcher/_start"));
56+
ensureWatcherStarted();
57+
58+
// All the legacy ML templates we created over the years should be deleted now they're no longer needed
59+
assertBusy(() -> {
60+
Request request = new Request("GET", "/_template/*watches*");
61+
try {
62+
Response response = client().performRequest(request);
63+
Map<String, Object> responseLevel = entityAsMap(response);
64+
assertNotNull(responseLevel);
65+
66+
assertThat(responseLevel.containsKey(".watches"), is(false));
67+
assertThat(responseLevel.containsKey(".triggered_watches"), is(false));
68+
} catch (ResponseException e) {
69+
// Not found is fine
70+
assertThat(
71+
"Unexpected failure getting templates: " + e.getResponse().getStatusLine(),
72+
e.getResponse().getStatusLine().getStatusCode(),
73+
is(404)
74+
);
75+
}
76+
});
77+
}
78+
4979
}

0 commit comments

Comments
 (0)