Skip to content

Commit f89207c

Browse files
committed
Make watcher plugin reloadable
This commit allows for rebuilding watcher secure secrets via the reload_secure_settings API call. The commit also renames a method in the Notification Service to make it a bit more readable.
1 parent 1dd10fe commit f89207c

File tree

9 files changed

+107
-26
lines changed

9 files changed

+107
-26
lines changed

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

+51-12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package org.elasticsearch.xpack.watcher;
77

88
import org.apache.logging.log4j.Logger;
9+
import org.apache.lucene.util.SetOnce;
910
import org.elasticsearch.action.ActionRequest;
1011
import org.elasticsearch.action.ActionResponse;
1112
import org.elasticsearch.bootstrap.BootstrapCheck;
@@ -38,6 +39,7 @@
3839
import org.elasticsearch.node.Node;
3940
import org.elasticsearch.plugins.ActionPlugin;
4041
import org.elasticsearch.plugins.Plugin;
42+
import org.elasticsearch.plugins.ReloadablePlugin;
4143
import org.elasticsearch.plugins.ScriptPlugin;
4244
import org.elasticsearch.rest.RestController;
4345
import org.elasticsearch.rest.RestHandler;
@@ -123,6 +125,7 @@
123125
import org.elasticsearch.xpack.watcher.input.simple.SimpleInputFactory;
124126
import org.elasticsearch.xpack.watcher.input.transform.TransformInput;
125127
import org.elasticsearch.xpack.watcher.input.transform.TransformInputFactory;
128+
import org.elasticsearch.xpack.watcher.notification.NotificationService;
126129
import org.elasticsearch.xpack.watcher.notification.email.Account;
127130
import org.elasticsearch.xpack.watcher.notification.email.EmailService;
128131
import org.elasticsearch.xpack.watcher.notification.email.HtmlSanitizer;
@@ -194,7 +197,7 @@
194197

195198
import static java.util.Collections.emptyList;
196199

197-
public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin {
200+
public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin, ReloadablePlugin {
198201

199202
// This setting is only here for backward compatibility reasons as 6.x indices made use of it. It can be removed in 8.x.
200203
@Deprecated
@@ -221,6 +224,11 @@ public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin {
221224
protected final boolean transportClient;
222225
protected final boolean enabled;
223226
protected final Environment env;
227+
private SetOnce<EmailService> emailService = new SetOnce<>();
228+
private SetOnce<HipChatService> hipChatService = new SetOnce<>();
229+
private SetOnce<JiraService> jiraService = new SetOnce<>();
230+
private SetOnce<SlackService> slackService = new SetOnce<>();
231+
private SetOnce<PagerDutyService> pagerDutyService = new SetOnce<>();
224232

225233
public Watcher(final Settings settings) {
226234
this.settings = settings;
@@ -269,11 +277,11 @@ public Collection<Object> createComponents(Client client, ClusterService cluster
269277
httpClient = new HttpClient(settings, httpAuthRegistry, getSslService());
270278

271279
// notification
272-
EmailService emailService = new EmailService(settings, cryptoService, clusterService.getClusterSettings());
273-
HipChatService hipChatService = new HipChatService(settings, httpClient, clusterService.getClusterSettings());
274-
JiraService jiraService = new JiraService(settings, httpClient, clusterService.getClusterSettings());
275-
SlackService slackService = new SlackService(settings, httpClient, clusterService.getClusterSettings());
276-
PagerDutyService pagerDutyService = new PagerDutyService(settings, httpClient, clusterService.getClusterSettings());
280+
emailService.set(new EmailService(settings, cryptoService, clusterService.getClusterSettings()));
281+
hipChatService.set(new HipChatService(settings, httpClient, clusterService.getClusterSettings()));
282+
jiraService.set(new JiraService(settings, httpClient, clusterService.getClusterSettings()));
283+
slackService.set(new SlackService(settings, httpClient, clusterService.getClusterSettings()));
284+
pagerDutyService.set(new PagerDutyService(settings, httpClient, clusterService.getClusterSettings()));
277285

278286
TextTemplateEngine templateEngine = new TextTemplateEngine(settings, scriptService);
279287
Map<String, EmailAttachmentParser> emailAttachmentParsers = new HashMap<>();
@@ -300,14 +308,15 @@ public Collection<Object> createComponents(Client client, ClusterService cluster
300308

301309
// actions
302310
final Map<String, ActionFactory> actionFactoryMap = new HashMap<>();
303-
actionFactoryMap.put(EmailAction.TYPE, new EmailActionFactory(settings, emailService, templateEngine, emailAttachmentsParser));
311+
actionFactoryMap.put(EmailAction.TYPE, new EmailActionFactory(settings, emailService.get(), templateEngine,
312+
emailAttachmentsParser));
304313
actionFactoryMap.put(WebhookAction.TYPE, new WebhookActionFactory(settings, httpClient, httpTemplateParser, templateEngine));
305314
actionFactoryMap.put(IndexAction.TYPE, new IndexActionFactory(settings, client));
306315
actionFactoryMap.put(LoggingAction.TYPE, new LoggingActionFactory(settings, templateEngine));
307-
actionFactoryMap.put(HipChatAction.TYPE, new HipChatActionFactory(settings, templateEngine, hipChatService));
308-
actionFactoryMap.put(JiraAction.TYPE, new JiraActionFactory(settings, templateEngine, jiraService));
309-
actionFactoryMap.put(SlackAction.TYPE, new SlackActionFactory(settings, templateEngine, slackService));
310-
actionFactoryMap.put(PagerDutyAction.TYPE, new PagerDutyActionFactory(settings, templateEngine, pagerDutyService));
316+
actionFactoryMap.put(HipChatAction.TYPE, new HipChatActionFactory(settings, templateEngine, hipChatService.get()));
317+
actionFactoryMap.put(JiraAction.TYPE, new JiraActionFactory(settings, templateEngine, jiraService.get()));
318+
actionFactoryMap.put(SlackAction.TYPE, new SlackActionFactory(settings, templateEngine, slackService.get()));
319+
actionFactoryMap.put(PagerDutyAction.TYPE, new PagerDutyActionFactory(settings, templateEngine, pagerDutyService.get()));
311320
final ActionRegistry registry = new ActionRegistry(actionFactoryMap, conditionRegistry, transformRegistry, getClock(),
312321
getLicenseState());
313322

@@ -367,7 +376,8 @@ public Collection<Object> createComponents(Client client, ClusterService cluster
367376

368377
return Arrays.asList(registry, inputRegistry, historyStore, triggerService, triggeredWatchParser,
369378
watcherLifeCycleService, executionService, triggerEngineListener, watcherService, watchParser,
370-
configuredTriggerEngine, triggeredWatchStore, watcherSearchTemplateService, slackService, pagerDutyService, hipChatService);
379+
configuredTriggerEngine, triggeredWatchStore, watcherSearchTemplateService, slackService.get(), pagerDutyService.get(),
380+
hipChatService.get());
371381
}
372382

373383
protected TriggerEngine getTriggerEngine(Clock clock, ScheduleRegistry scheduleRegistry) {
@@ -613,4 +623,33 @@ public List<ScriptContext> getContexts() {
613623
public void close() throws IOException {
614624
IOUtils.closeWhileHandlingException(httpClient);
615625
}
626+
627+
/**
628+
* Used to detect which {@link NotificationService} get reloaded. Also useful for testing the reload in tests.
629+
* @return
630+
*/
631+
protected List<NotificationService> getReloadableServices() {
632+
return Arrays.asList(
633+
emailService.get(),
634+
hipChatService.get(),
635+
jiraService.get(),
636+
slackService.get(),
637+
pagerDutyService.get());
638+
}
639+
640+
/**
641+
* Reloads all reloadable services' settings.
642+
* @param settings
643+
* Settings used while reloading the plugin. All values are
644+
* retrievable, including the values stored in the node's keystore.
645+
* The setting values are the initial ones, from when the node has be
646+
* started, i.e. they don't follow dynamic updates.
647+
* @throws Exception
648+
*/
649+
@Override
650+
public void reload(Settings settings) throws Exception {
651+
for (NotificationService service : getReloadableServices()) {
652+
service.loadSettings(settings);
653+
}
654+
}
616655
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public NotificationService(Settings settings, String type) {
3030
this.type = type;
3131
}
3232

33-
protected synchronized void setAccountSetting(Settings settings) {
33+
public synchronized void loadSettings(Settings settings) {
3434
Tuple<Map<String, Account>, Account> accounts = buildAccounts(settings, this::createAccount);
3535
this.accounts = Collections.unmodifiableMap(accounts.v1());
3636
this.defaultAccount = accounts.v2();

x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/EmailService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public class EmailService extends NotificationService<Account> {
9696
public EmailService(Settings settings, @Nullable CryptoService cryptoService, ClusterSettings clusterSettings) {
9797
super(settings, "email");
9898
this.cryptoService = cryptoService;
99-
clusterSettings.addSettingsUpdateConsumer(this::setAccountSetting, getSettings());
99+
clusterSettings.addSettingsUpdateConsumer(this::loadSettings, getSettings());
100100
// ensure logging of setting changes
101101
clusterSettings.addSettingsUpdateConsumer(SETTING_DEFAULT_ACCOUNT, (s) -> {});
102102
clusterSettings.addAffixUpdateConsumer(SETTING_PROFILE, (s, o) -> {}, (s, o) -> {});
@@ -116,7 +116,7 @@ public EmailService(Settings settings, @Nullable CryptoService cryptoService, Cl
116116
clusterSettings.addAffixUpdateConsumer(SETTING_SMTP_SEND_PARTIAL, (s, o) -> {}, (s, o) -> {});
117117
clusterSettings.addAffixUpdateConsumer(SETTING_SMTP_WAIT_ON_QUIT, (s, o) -> {}, (s, o) -> {});
118118
// do an initial load
119-
setAccountSetting(settings);
119+
loadSettings(settings);
120120
}
121121

122122
@Override

x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/hipchat/HipChatService.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class HipChatService extends NotificationService<HipChatAccount> {
6767
public HipChatService(Settings settings, HttpClient httpClient, ClusterSettings clusterSettings) {
6868
super(settings, "hipchat");
6969
this.httpClient = httpClient;
70-
clusterSettings.addSettingsUpdateConsumer(this::setAccountSetting, getSettings());
70+
clusterSettings.addSettingsUpdateConsumer(this::loadSettings, getSettings());
7171
// ensure logging of setting changes
7272
clusterSettings.addSettingsUpdateConsumer(SETTING_DEFAULT_ACCOUNT, (s) -> {});
7373
clusterSettings.addSettingsUpdateConsumer(SETTING_DEFAULT_HOST, (s) -> {});
@@ -80,13 +80,13 @@ public HipChatService(Settings settings, HttpClient httpClient, ClusterSettings
8080
clusterSettings.addAffixUpdateConsumer(SETTING_PORT, (s, o) -> {}, (s, o) -> {});
8181
clusterSettings.addAffixUpdateConsumer(SETTING_MESSAGE_DEFAULTS, (s, o) -> {}, (s, o) -> {});
8282

83-
setAccountSetting(settings);
83+
loadSettings(settings);
8484
}
8585

8686
@Override
87-
protected synchronized void setAccountSetting(Settings settings) {
87+
public synchronized void loadSettings(Settings settings) {
8888
defaultServer = new HipChatServer(settings.getByPrefix("xpack.notification.hipchat."));
89-
super.setAccountSetting(settings);
89+
super.loadSettings(settings);
9090
}
9191

9292
@Override

x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/jira/JiraService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public class JiraService extends NotificationService<JiraAccount> {
6262
public JiraService(Settings settings, HttpClient httpClient, ClusterSettings clusterSettings) {
6363
super(settings, "jira");
6464
this.httpClient = httpClient;
65-
clusterSettings.addSettingsUpdateConsumer(this::setAccountSetting, getSettings());
65+
clusterSettings.addSettingsUpdateConsumer(this::loadSettings, getSettings());
6666
// ensure logging of setting changes
6767
clusterSettings.addSettingsUpdateConsumer(SETTING_DEFAULT_ACCOUNT, (s) -> {});
6868
clusterSettings.addAffixUpdateConsumer(SETTING_ALLOW_HTTP, (s, o) -> {}, (s, o) -> {});
@@ -74,7 +74,7 @@ public JiraService(Settings settings, HttpClient httpClient, ClusterSettings clu
7474
clusterSettings.addAffixUpdateConsumer(SETTING_SECURE_PASSWORD, (s, o) -> {}, (s, o) -> {});
7575
clusterSettings.addAffixUpdateConsumer(SETTING_DEFAULTS, (s, o) -> {}, (s, o) -> {});
7676
// do an initial load
77-
setAccountSetting(settings);
77+
loadSettings(settings);
7878
}
7979

8080
@Override

x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/pagerduty/PagerDutyService.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public PagerDutyService(Settings settings, HttpClient httpClient, ClusterSetting
4545
clusterSettings.addAffixUpdateConsumer(SETTING_SERVICE_API_KEY, (s, o) -> {}, (s, o) -> {});
4646
clusterSettings.addAffixUpdateConsumer(SETTING_SECURE_SERVICE_API_KEY, (s, o) -> {}, (s, o) -> {});
4747
clusterSettings.addAffixUpdateConsumer(SETTING_DEFAULTS, (s, o) -> {}, (s, o) -> {});
48-
setAccountSetting(settings);
48+
loadSettings(settings);
4949
}
5050

5151
@Override

x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/slack/SlackService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ public class SlackService extends NotificationService<SlackAccount> {
4141
public SlackService(Settings settings, HttpClient httpClient, ClusterSettings clusterSettings) {
4242
super(settings, "slack");
4343
this.httpClient = httpClient;
44-
clusterSettings.addSettingsUpdateConsumer(this::setAccountSetting, getSettings());
44+
clusterSettings.addSettingsUpdateConsumer(this::loadSettings, getSettings());
4545
clusterSettings.addSettingsUpdateConsumer(SETTING_DEFAULT_ACCOUNT, (s) -> {});
4646
clusterSettings.addAffixUpdateConsumer(SETTING_URL, (s, o) -> {}, (s, o) -> {});
4747
clusterSettings.addAffixUpdateConsumer(SETTING_URL_SECURE, (s, o) -> {}, (s, o) -> {});
4848
clusterSettings.addAffixUpdateConsumer(SETTING_DEFAULTS, (s, o) -> {}, (s, o) -> {});
49-
setAccountSetting(settings);
49+
loadSettings(settings);
5050
}
5151

5252
@Override

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ private static class TestNotificationService extends NotificationService<String>
8282

8383
TestNotificationService(Settings settings) {
8484
super(settings, "test");
85-
setAccountSetting(settings);
85+
loadSettings(settings);
8686
}
8787

8888
@Override
8989
protected String createAccount(String name, Settings accountSettings) {
9090
return name;
9191
}
9292
}
93-
}
93+
}

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

+42
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
import org.elasticsearch.test.IndexSettingsModule;
1616
import org.elasticsearch.threadpool.ExecutorBuilder;
1717
import org.elasticsearch.xpack.core.watcher.watch.Watch;
18+
import org.elasticsearch.xpack.watcher.notification.NotificationService;
19+
import org.elasticsearch.xpack.watcher.notification.email.Account;
1820

21+
import java.util.Collections;
1922
import java.util.List;
2023

2124
import static java.util.Collections.emptyMap;
@@ -97,4 +100,43 @@ public void testThreadPoolSize() {
97100
.build();
98101
assertThat(Watcher.getWatcherThreadPoolSize(noDataNodeSettings), is(1));
99102
}
103+
104+
private class TestNotificationService extends NotificationService<Account> {
105+
106+
boolean calledCreateAccount = false;
107+
108+
TestNotificationService(Settings settings, String type) {
109+
super(settings, type);
110+
}
111+
112+
@Override
113+
protected Account createAccount(String name, Settings accountSettings) {
114+
return null;
115+
}
116+
117+
@Override
118+
public synchronized void loadSettings(Settings settings) {
119+
calledCreateAccount = true;
120+
super.loadSettings(settings);
121+
}
122+
}
123+
124+
public void testReload() throws Exception {
125+
Settings settings = Settings.builder()
126+
.put("xpack.watcher.enabled", false)
127+
.put("path.home", createTempDir())
128+
.build();
129+
TestNotificationService service = new TestNotificationService(settings, "test");
130+
Watcher watcher = new Watcher(settings) {
131+
@Override
132+
protected List<NotificationService> getReloadableServices() {
133+
return Collections.singletonList(service);
134+
}
135+
};
136+
137+
assertFalse(service.calledCreateAccount);
138+
watcher.reload(settings);
139+
assertTrue(service.calledCreateAccount);
140+
141+
}
100142
}

0 commit comments

Comments
 (0)