Watcher: Remove in memory watch store (elastic/elasticsearch#4201)
In order to prepare to the distributed watch execution, this commit removes the in memory watch store. Whenever a watch is needed now, a get request is executed and the parsing is done. This happens when * Put * Get * Ack * Activate/Deactivate * Execute Note: This also means there are no usage stats currently regarding the watch count, because we would need to execute a query. This would require the usage stats to be async, see elastic/elasticsearch#3569 Another advantage is, that there is no dirty flag in the watch itself needed anymore, because the watch is always the latest. Also write operations store immediately and dont leave anything in memory. Also ActionListener.wrap() was used a lot instead of more verbose anonmyous inner classes. Original commit: elastic/x-pack-elasticsearch@c47465b47c
This commit is contained in:
parent
7192c46307
commit
b57c4f6ebe
|
@ -142,7 +142,6 @@ import org.elasticsearch.xpack.watcher.trigger.schedule.engine.SchedulerSchedule
|
|||
import org.elasticsearch.xpack.watcher.trigger.schedule.engine.TickerScheduleTriggerEngine;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchLockService;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
|
@ -255,13 +254,10 @@ public class Watcher implements ActionPlugin, ScriptPlugin {
|
|||
final InputRegistry inputRegistry = new InputRegistry(settings, inputFactories);
|
||||
inputFactories.put(ChainInput.TYPE, new ChainInputFactory(settings, inputRegistry));
|
||||
|
||||
// TODO replace internal client where needed, so we can remove ctors
|
||||
final WatcherClientProxy watcherClientProxy = new WatcherClientProxy(settings, internalClient);
|
||||
|
||||
final WatcherClient watcherClient = new WatcherClient(internalClient);
|
||||
|
||||
final HistoryStore historyStore = new HistoryStore(settings, watcherClientProxy);
|
||||
|
||||
final Set<Schedule.Parser> scheduleParsers = new HashSet<>();
|
||||
scheduleParsers.add(new CronSchedule.Parser());
|
||||
scheduleParsers.add(new DailySchedule.Parser());
|
||||
|
@ -288,10 +284,9 @@ public class Watcher implements ActionPlugin, ScriptPlugin {
|
|||
final WatchLockService watchLockService = new WatchLockService(settings);
|
||||
final WatchExecutor watchExecutor = getWatchExecutor(threadPool);
|
||||
final Watch.Parser watchParser = new Watch.Parser(settings, triggerService, registry, inputRegistry, cryptoService, clock);
|
||||
final WatchStore watchStore = new WatchStore(settings, watcherClientProxy, watchParser);
|
||||
|
||||
final ExecutionService executionService = new ExecutionService(settings, historyStore, triggeredWatchStore, watchExecutor,
|
||||
watchStore, watchLockService, clock, threadPool);
|
||||
watchLockService, clock, threadPool, watchParser, watcherClientProxy);
|
||||
|
||||
final TriggerEngine.Listener triggerEngineListener = getTriggerEngineListener(executionService);
|
||||
triggerService.register(triggerEngineListener);
|
||||
|
@ -299,15 +294,15 @@ public class Watcher implements ActionPlugin, ScriptPlugin {
|
|||
final WatcherIndexTemplateRegistry watcherIndexTemplateRegistry = new WatcherIndexTemplateRegistry(settings,
|
||||
clusterService.getClusterSettings(), clusterService, threadPool, internalClient);
|
||||
|
||||
final WatcherService watcherService = new WatcherService(settings, clock, triggerService, watchStore,
|
||||
watchParser, executionService, watchLockService, watcherIndexTemplateRegistry);
|
||||
final WatcherService watcherService = new WatcherService(settings, triggerService, executionService, watchLockService,
|
||||
watcherIndexTemplateRegistry, watchParser, watcherClientProxy);
|
||||
|
||||
final WatcherLifeCycleService watcherLifeCycleService =
|
||||
new WatcherLifeCycleService(settings, threadPool, clusterService, watcherService);
|
||||
|
||||
return Arrays.asList(registry, watcherClient, inputRegistry, historyStore, triggerService, triggeredWatchParser,
|
||||
watcherLifeCycleService, executionService, watchStore, triggerEngineListener, watcherService, watchParser,
|
||||
configuredTriggerEngine, triggeredWatchStore, watcherSearchTemplateService);
|
||||
watcherLifeCycleService, executionService, triggerEngineListener, watcherService, watchParser,
|
||||
configuredTriggerEngine, triggeredWatchStore, watcherSearchTemplateService, watcherClientProxy);
|
||||
}
|
||||
|
||||
protected TriggerEngine getTriggerEngine(Clock clock, ScheduleRegistry scheduleRegistry) {
|
||||
|
@ -441,7 +436,7 @@ public class Watcher implements ActionPlugin, ScriptPlugin {
|
|||
|
||||
String errorMessage = LoggerMessageFormat.format("the [action.auto_create_index] setting value [{}] is too" +
|
||||
" restrictive. disable [action.auto_create_index] or set it to " +
|
||||
"[{}, {}, {}*]", (Object) value, WatchStore.INDEX, TriggeredWatchStore.INDEX_NAME, HistoryStore.INDEX_PREFIX);
|
||||
"[{}, {}, {}*]", (Object) value, Watch.INDEX, TriggeredWatchStore.INDEX_NAME, HistoryStore.INDEX_PREFIX);
|
||||
if (Booleans.isExplicitFalse(value)) {
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
|
@ -130,15 +130,15 @@ public class WatcherLifeCycleService extends AbstractComponent implements Cluste
|
|||
threadPool.executor(ThreadPool.Names.GENERIC).execute(() -> start(state, false));
|
||||
} else {
|
||||
boolean isWatchIndexDeleted = event.indicesDeleted().stream()
|
||||
.filter(index -> WatchStore.INDEX.equals(index.getName()))
|
||||
.filter(index -> Watch.INDEX.equals(index.getName()))
|
||||
.findAny()
|
||||
.isPresent();
|
||||
|
||||
boolean isWatchIndexOpenInPreviousClusterState = event.previousState().metaData().hasIndex(WatchStore.INDEX) &&
|
||||
event.previousState().metaData().index(WatchStore.INDEX).getState() == IndexMetaData.State.OPEN;
|
||||
boolean isWatchIndexClosedInCurrentClusterState = event.state().metaData().hasIndex(WatchStore.INDEX) &&
|
||||
event.state().metaData().index(WatchStore.INDEX).getState() == IndexMetaData.State.CLOSE;
|
||||
boolean hasWatcherIndexBeenClosed = isWatchIndexOpenInPreviousClusterState && isWatchIndexClosedInCurrentClusterState;
|
||||
final boolean isWatchIndexOpenInPreviousClusterState = event.previousState().metaData().hasIndex(Watch.INDEX) &&
|
||||
event.previousState().metaData().index(Watch.INDEX).getState() == IndexMetaData.State.OPEN;
|
||||
final boolean isWatchIndexClosedInCurrentClusterState = event.state().metaData().hasIndex(Watch.INDEX) &&
|
||||
event.state().metaData().index(Watch.INDEX).getState() == IndexMetaData.State.CLOSE;
|
||||
final boolean hasWatcherIndexBeenClosed = isWatchIndexOpenInPreviousClusterState && isWatchIndexClosedInCurrentClusterState;
|
||||
|
||||
if (isWatchIndexDeleted || hasWatcherIndexBeenClosed) {
|
||||
threadPool.executor(ThreadPool.Names.GENERIC).execute(() -> watcherService.watchIndexDeletedOrClosed());
|
||||
|
|
|
@ -6,57 +6,67 @@
|
|||
package org.elasticsearch.xpack.watcher;
|
||||
|
||||
|
||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
import org.apache.logging.log4j.util.Supplier;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ElasticsearchTimeoutException;
|
||||
import org.elasticsearch.action.DocWriteResponse;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.engine.VersionConflictEngineException;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilders;
|
||||
import org.elasticsearch.xpack.watcher.execution.ExecutionService;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerService;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchLockService;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStatus;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.joda.time.DateTime;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStoreUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Clock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.elasticsearch.xpack.watcher.support.Exceptions.illegalArgument;
|
||||
import static org.elasticsearch.xpack.watcher.support.Exceptions.illegalState;
|
||||
import static org.elasticsearch.xpack.watcher.support.Exceptions.ioException;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.elasticsearch.xpack.watcher.watch.Watch.DOC_TYPE;
|
||||
import static org.elasticsearch.xpack.watcher.watch.Watch.INDEX;
|
||||
|
||||
|
||||
public class WatcherService extends AbstractComponent {
|
||||
|
||||
private final Clock clock;
|
||||
private final TriggerService triggerService;
|
||||
private final Watch.Parser watchParser;
|
||||
private final WatchStore watchStore;
|
||||
private final WatchLockService watchLockService;
|
||||
private final ExecutionService executionService;
|
||||
private final WatcherIndexTemplateRegistry watcherIndexTemplateRegistry;
|
||||
// package-private for testing
|
||||
final AtomicReference<WatcherState> state = new AtomicReference<>(WatcherState.STOPPED);
|
||||
private final TimeValue scrollTimeout;
|
||||
private final int scrollSize;
|
||||
private final Watch.Parser parser;
|
||||
private final WatcherClientProxy client;
|
||||
|
||||
public WatcherService(Settings settings, Clock clock, TriggerService triggerService, WatchStore watchStore,
|
||||
Watch.Parser watchParser, ExecutionService executionService, WatchLockService watchLockService,
|
||||
WatcherIndexTemplateRegistry watcherIndexTemplateRegistry) {
|
||||
public WatcherService(Settings settings, TriggerService triggerService,
|
||||
ExecutionService executionService, WatchLockService watchLockService,
|
||||
WatcherIndexTemplateRegistry watcherIndexTemplateRegistry, Watch.Parser parser, WatcherClientProxy client) {
|
||||
super(settings);
|
||||
this.clock = clock;
|
||||
this.triggerService = triggerService;
|
||||
this.watchStore = watchStore;
|
||||
this.watchParser = watchParser;
|
||||
this.watchLockService = watchLockService;
|
||||
this.executionService = executionService;
|
||||
this.watcherIndexTemplateRegistry = watcherIndexTemplateRegistry;
|
||||
this.scrollTimeout = settings.getAsTime("xpack.watcher.watch.scroll.timeout", TimeValue.timeValueSeconds(30));
|
||||
this.scrollSize = settings.getAsInt("xpack.watcher.watch.scroll.size", 100);
|
||||
this.parser = parser;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public void start(ClusterState clusterState) throws Exception {
|
||||
|
@ -65,12 +75,9 @@ public class WatcherService extends AbstractComponent {
|
|||
logger.debug("starting watch service...");
|
||||
watcherIndexTemplateRegistry.addTemplatesIfMissing();
|
||||
watchLockService.start();
|
||||
|
||||
// Try to load watch store before the execution service, b/c action depends on watch store
|
||||
watchStore.start(clusterState);
|
||||
executionService.start(clusterState);
|
||||
triggerService.start(loadWatches(clusterState));
|
||||
|
||||
triggerService.start(watchStore.activeWatches());
|
||||
state.set(WatcherState.STARTED);
|
||||
logger.debug("watch service has started");
|
||||
} catch (Exception e) {
|
||||
|
@ -83,7 +90,7 @@ public class WatcherService extends AbstractComponent {
|
|||
}
|
||||
|
||||
public boolean validate(ClusterState state) {
|
||||
return watchStore.validate(state) && executionService.validate(state);
|
||||
return executionService.validate(state);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
|
@ -96,7 +103,6 @@ public class WatcherService extends AbstractComponent {
|
|||
} catch (ElasticsearchTimeoutException te) {
|
||||
logger.warn("error stopping WatchLockService", te);
|
||||
}
|
||||
watchStore.stop();
|
||||
state.set(WatcherState.STOPPED);
|
||||
logger.debug("watch service has stopped");
|
||||
} else {
|
||||
|
@ -104,151 +110,74 @@ public class WatcherService extends AbstractComponent {
|
|||
}
|
||||
}
|
||||
|
||||
public WatchStore.WatchDelete deleteWatch(String id) {
|
||||
ensureStarted();
|
||||
WatchStore.WatchDelete delete = watchStore.delete(id);
|
||||
if (delete.deleteResponse().getResult() == DocWriteResponse.Result.DELETED) {
|
||||
triggerService.remove(id);
|
||||
}
|
||||
return delete;
|
||||
}
|
||||
|
||||
public IndexResponse putWatch(String id, BytesReference watchSource, boolean active) throws IOException {
|
||||
ensureStarted();
|
||||
DateTime now = new DateTime(clock.millis(), UTC);
|
||||
Watch watch = watchParser.parseWithSecrets(id, false, watchSource, now);
|
||||
watch.setState(active, now);
|
||||
WatchStore.WatchPut result = watchStore.put(watch);
|
||||
|
||||
if (result.previous() == null) {
|
||||
// this is a newly created watch, so we only need to schedule it if it's active
|
||||
if (result.current().status().state().isActive()) {
|
||||
triggerService.add(result.current());
|
||||
}
|
||||
|
||||
} else if (result.current().status().state().isActive()) {
|
||||
|
||||
if (!result.previous().status().state().isActive()) {
|
||||
// the replaced watch was inactive, which means it wasn't scheduled. The new watch is active
|
||||
// so we need to schedule it
|
||||
triggerService.add(result.current());
|
||||
|
||||
} else if (!result.previous().trigger().equals(result.current().trigger())) {
|
||||
// the previous watch was active and its schedule is different than the schedule of the
|
||||
// new watch, so we need to
|
||||
triggerService.add(result.current());
|
||||
}
|
||||
} else {
|
||||
// if the current is inactive, we'll just remove it from the trigger service
|
||||
// just to be safe
|
||||
triggerService.remove(result.current().id());
|
||||
}
|
||||
return result.indexResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: add version, fields, etc support that the core get api has as well.
|
||||
* This reads all watches from the .watches index/alias and puts them into memory for a short period of time,
|
||||
* before they are fed into the trigger service.
|
||||
*
|
||||
* This is only invoked when a node becomes master, so either on start up or when a master node switches - while watcher is started up
|
||||
*/
|
||||
public Watch getWatch(String name) {
|
||||
return watchStore.get(name);
|
||||
private Collection<Watch> loadWatches(ClusterState clusterState) {
|
||||
IndexMetaData indexMetaData = WatchStoreUtils.getConcreteIndex(INDEX, clusterState.metaData());
|
||||
|
||||
// no index exists, all good, we can start
|
||||
if (indexMetaData == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
RefreshResponse refreshResponse = client.refresh(new RefreshRequest(INDEX));
|
||||
if (refreshResponse.getSuccessfulShards() < indexMetaData.getNumberOfShards()) {
|
||||
throw illegalState("not all required shards have been refreshed");
|
||||
}
|
||||
|
||||
List<Watch> watches = new ArrayList<>();
|
||||
SearchRequest searchRequest = new SearchRequest(INDEX)
|
||||
.types(DOC_TYPE)
|
||||
.scroll(scrollTimeout)
|
||||
.source(new SearchSourceBuilder()
|
||||
.size(scrollSize)
|
||||
.sort(SortBuilders.fieldSort("_doc"))
|
||||
.version(true));
|
||||
SearchResponse response = client.search(searchRequest, null);
|
||||
try {
|
||||
if (response.getTotalShards() != response.getSuccessfulShards()) {
|
||||
throw new ElasticsearchException("Partial response while loading watches");
|
||||
}
|
||||
|
||||
while (response.getHits().hits().length != 0) {
|
||||
for (SearchHit hit : response.getHits()) {
|
||||
String id = hit.getId();
|
||||
try {
|
||||
Watch watch = parser.parse(id, true, hit.getSourceRef());
|
||||
watch.version(hit.version());
|
||||
watches.add(watch);
|
||||
} catch (Exception e) {
|
||||
logger.error((Supplier<?>) () -> new ParameterizedMessage("couldn't load watch [{}], ignoring it...", id), e);
|
||||
}
|
||||
}
|
||||
response = client.searchScroll(response.getScrollId(), scrollTimeout);
|
||||
}
|
||||
} finally {
|
||||
client.clearScroll(response.getScrollId());
|
||||
}
|
||||
return watches;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public WatcherState state() {
|
||||
return state.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Acks the watch if needed
|
||||
*/
|
||||
public WatchStatus ackWatch(String id, String[] actionIds) throws IOException {
|
||||
ensureStarted();
|
||||
if (actionIds == null || actionIds.length == 0) {
|
||||
actionIds = new String[] { Watch.ALL_ACTIONS_ID };
|
||||
}
|
||||
Watch watch = watchStore.get(id);
|
||||
if (watch == null) {
|
||||
throw illegalArgument("watch [{}] does not exist", id);
|
||||
}
|
||||
// we need to create a safe copy of the status
|
||||
if (watch.ack(new DateTime(clock.millis(), UTC), actionIds)) {
|
||||
try {
|
||||
watchStore.updateStatus(watch);
|
||||
} catch (IOException ioe) {
|
||||
throw ioException("failed to update the watch [{}] on ack", ioe, watch.id());
|
||||
} catch (VersionConflictEngineException vcee) {
|
||||
throw illegalState("failed to update the watch [{}] on ack, perhaps it was force deleted", vcee, watch.id());
|
||||
}
|
||||
}
|
||||
return new WatchStatus(watch.status());
|
||||
}
|
||||
|
||||
public WatchStatus activateWatch(String id) throws IOException {
|
||||
return setWatchState(id, true);
|
||||
}
|
||||
|
||||
public WatchStatus deactivateWatch(String id) throws IOException {
|
||||
return setWatchState(id, false);
|
||||
}
|
||||
|
||||
WatchStatus setWatchState(String id, boolean active) throws IOException {
|
||||
ensureStarted();
|
||||
// for now, when a watch is deactivated we don't remove its runtime representation
|
||||
// that is, the store will still keep the watch in memory. We only mark the watch
|
||||
// as inactive (both in runtime and also update the watch in the watches index)
|
||||
// and remove the watch from the trigger service, such that it will not be triggered
|
||||
// nor its trigger be evaluated.
|
||||
//
|
||||
// later on we can consider removing the watch runtime representation from memory
|
||||
// as well. This will mean that the in-memory loaded watches will no longer be a
|
||||
// complete representation of the watches in the index. This requires careful thought
|
||||
// to make sure, such incompleteness doesn't hurt any other part of watcher (we need
|
||||
// to run this exercise anyway... and make sure that nothing in watcher relies on the
|
||||
// fact that the watch store holds all watches in memory.
|
||||
|
||||
Watch watch = watchStore.get(id);
|
||||
if (watch == null) {
|
||||
throw illegalArgument("watch [{}] does not exist", id);
|
||||
}
|
||||
if (watch.setState(active, new DateTime(clock.millis(), UTC))) {
|
||||
try {
|
||||
watchStore.updateStatus(watch);
|
||||
if (active) {
|
||||
triggerService.add(watch);
|
||||
} else {
|
||||
triggerService.remove(watch.id());
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw ioException("failed to update the watch [{}] on ack", ioe, watch.id());
|
||||
} catch (VersionConflictEngineException vcee) {
|
||||
throw illegalState("failed to update the watch [{}] on ack, perhaps it was force deleted", vcee, watch.id());
|
||||
}
|
||||
}
|
||||
// we need to create a safe copy of the status
|
||||
return new WatchStatus(watch.status());
|
||||
}
|
||||
|
||||
public long watchesCount() {
|
||||
return watchStore.watches().size();
|
||||
}
|
||||
|
||||
private void ensureStarted() {
|
||||
if (state.get() != WatcherState.STARTED) {
|
||||
throw new IllegalStateException("not started");
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> usageStats() {
|
||||
Map<String, Object> innerMap = executionService.usageStats();
|
||||
innerMap.putAll(watchStore.usageStats());
|
||||
return innerMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Something deleted or closed the {@link WatchStore#INDEX} and thus we need to do some cleanup to prevent further execution of watches
|
||||
* Something deleted or closed the {@link Watch#INDEX} and thus we need to do some cleanup to prevent further execution of watches
|
||||
* as those watches cannot be updated anymore
|
||||
*/
|
||||
public void watchIndexDeletedOrClosed() {
|
||||
watchStore.clearWatchesInMemory();
|
||||
executionService.clearExecutions();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,11 +74,7 @@ public class ActionStatus implements ToXContent {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = ackStatus.hashCode();
|
||||
result = 31 * result + (lastExecution != null ? lastExecution.hashCode() : 0);
|
||||
result = 31 * result + (lastSuccessfulExecution != null ? lastSuccessfulExecution.hashCode() : 0);
|
||||
result = 31 * result + (lastThrottle != null ? lastThrottle.hashCode() : 0);
|
||||
return result;
|
||||
return Objects.hash(ackStatus, lastExecution, lastSuccessfulExecution, lastThrottle);
|
||||
}
|
||||
|
||||
public void update(DateTime timestamp, Action.Result result) {
|
||||
|
@ -238,15 +234,12 @@ public class ActionStatus implements ToXContent {
|
|||
|
||||
AckStatus ackStatus = (AckStatus) o;
|
||||
|
||||
if (!timestamp.equals(ackStatus.timestamp)) return false;
|
||||
return state == ackStatus.state;
|
||||
return Objects.equals(timestamp, ackStatus.timestamp) && Objects.equals(state, ackStatus.state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = timestamp.hashCode();
|
||||
result = 31 * result + state.hashCode();
|
||||
return result;
|
||||
return Objects.hash(timestamp, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -11,10 +11,7 @@ import org.elasticsearch.common.ParseField;
|
|||
import org.elasticsearch.common.ParseFieldMatcher;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.xpack.watcher.actions.Action;
|
||||
import org.elasticsearch.xpack.common.secret.Secret;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.WatcherParams;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.WatcherXContentParser;
|
||||
import org.elasticsearch.xpack.notification.email.Authentication;
|
||||
import org.elasticsearch.xpack.notification.email.DataAttachment;
|
||||
import org.elasticsearch.xpack.notification.email.Email;
|
||||
|
@ -22,6 +19,9 @@ import org.elasticsearch.xpack.notification.email.EmailTemplate;
|
|||
import org.elasticsearch.xpack.notification.email.Profile;
|
||||
import org.elasticsearch.xpack.notification.email.attachment.EmailAttachments;
|
||||
import org.elasticsearch.xpack.notification.email.attachment.EmailAttachmentsParser;
|
||||
import org.elasticsearch.xpack.watcher.actions.Action;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.WatcherParams;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.WatcherXContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
@ -105,7 +105,7 @@ public class EmailAction implements Action {
|
|||
}
|
||||
if (auth != null) {
|
||||
builder.field(Field.USER.getPreferredName(), auth.user());
|
||||
if (!WatcherParams.hideSecrets(params)) {
|
||||
if (WatcherParams.hideSecrets(params) == false) {
|
||||
builder.field(Field.PASSWORD.getPreferredName(), auth.password(), params);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,19 +33,20 @@ public final class ScriptCondition extends Condition {
|
|||
|
||||
private final ScriptService scriptService;
|
||||
private final Script script;
|
||||
private final CompiledScript compiledScript;
|
||||
|
||||
public ScriptCondition(Script script) {
|
||||
super(TYPE);
|
||||
this.script = script;
|
||||
scriptService = null;
|
||||
compiledScript = null;
|
||||
}
|
||||
|
||||
ScriptCondition(Script script, ScriptService scriptService) {
|
||||
super(TYPE);
|
||||
this.scriptService = scriptService;
|
||||
this.script = script;
|
||||
// try to compile so we catch syntax errors early
|
||||
scriptService.compile(script, Watcher.SCRIPT_CONTEXT, Collections.emptyMap());
|
||||
compiledScript = scriptService.compile(script, Watcher.SCRIPT_CONTEXT, Collections.emptyMap());
|
||||
}
|
||||
|
||||
public Script getScript() {
|
||||
|
@ -72,7 +73,6 @@ public final class ScriptCondition extends Condition {
|
|||
if (script.getParams() != null && !script.getParams().isEmpty()) {
|
||||
parameters.putAll(script.getParams());
|
||||
}
|
||||
CompiledScript compiledScript = scriptService.compile(script, Watcher.SCRIPT_CONTEXT, Collections.emptyMap());
|
||||
ExecutableScript executable = scriptService.executable(compiledScript, parameters);
|
||||
Object value = executable.run();
|
||||
if (value instanceof Boolean) {
|
||||
|
|
|
@ -7,9 +7,10 @@ package org.elasticsearch.xpack.watcher.execution;
|
|||
|
||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
import org.apache.logging.log4j.util.Supplier;
|
||||
import org.elasticsearch.ExceptionsHelper;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.lease.Releasable;
|
||||
import org.elasticsearch.common.metrics.MeanMetric;
|
||||
|
@ -25,13 +26,15 @@ import org.elasticsearch.xpack.watcher.condition.Condition;
|
|||
import org.elasticsearch.xpack.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.xpack.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.xpack.watcher.input.Input;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.transform.Transform;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchLockService;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStoreUtils;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Clock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
|
@ -39,7 +42,6 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
@ -58,28 +60,31 @@ public class ExecutionService extends AbstractComponent {
|
|||
private final HistoryStore historyStore;
|
||||
private final TriggeredWatchStore triggeredWatchStore;
|
||||
private final WatchExecutor executor;
|
||||
private final WatchStore watchStore;
|
||||
private final WatchLockService watchLockService;
|
||||
private final Clock clock;
|
||||
private final TimeValue defaultThrottlePeriod;
|
||||
private final TimeValue maxStopTimeout;
|
||||
private final ThreadPool threadPool;
|
||||
private final Watch.Parser parser;
|
||||
private final WatcherClientProxy client;
|
||||
|
||||
private volatile CurrentExecutions currentExecutions;
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
|
||||
public ExecutionService(Settings settings, HistoryStore historyStore, TriggeredWatchStore triggeredWatchStore, WatchExecutor executor,
|
||||
WatchStore watchStore, WatchLockService watchLockService, Clock clock, ThreadPool threadPool) {
|
||||
WatchLockService watchLockService, Clock clock, ThreadPool threadPool, Watch.Parser parser,
|
||||
WatcherClientProxy client) {
|
||||
super(settings);
|
||||
this.historyStore = historyStore;
|
||||
this.triggeredWatchStore = triggeredWatchStore;
|
||||
this.executor = executor;
|
||||
this.watchStore = watchStore;
|
||||
this.watchLockService = watchLockService;
|
||||
this.clock = clock;
|
||||
this.defaultThrottlePeriod = DEFAULT_THROTTLE_PERIOD_SETTING.get(settings);
|
||||
this.maxStopTimeout = Watcher.MAX_STOP_TIMEOUT_SETTING.get(settings);
|
||||
this.threadPool = threadPool;
|
||||
this.parser = parser;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public void start(ClusterState state) throws Exception {
|
||||
|
@ -105,7 +110,16 @@ public class ExecutionService extends AbstractComponent {
|
|||
}
|
||||
|
||||
public boolean validate(ClusterState state) {
|
||||
return triggeredWatchStore.validate(state);
|
||||
boolean triggeredWatchStoreReady = triggeredWatchStore.validate(state);
|
||||
try {
|
||||
IndexMetaData indexMetaData = WatchStoreUtils.getConcreteIndex(Watch.INDEX, state.metaData());
|
||||
if (indexMetaData != null) {
|
||||
return triggeredWatchStoreReady && state.routingTable().index(indexMetaData.getIndex()).allPrimaryShardsActive();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
return triggeredWatchStoreReady;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
|
@ -171,41 +185,39 @@ public class ExecutionService extends AbstractComponent {
|
|||
if (!started.get()) {
|
||||
throw new IllegalStateException("not started");
|
||||
}
|
||||
final LinkedList<TriggeredWatch> triggeredWatches = new LinkedList<>();
|
||||
final LinkedList<TriggeredExecutionContext> contexts = new LinkedList<>();
|
||||
|
||||
final List<TriggeredWatch> triggeredWatches = new ArrayList<>();
|
||||
final List<TriggeredExecutionContext> contexts = new ArrayList<>();
|
||||
DateTime now = new DateTime(clock.millis(), UTC);
|
||||
|
||||
threadPool.generic().execute(() -> {
|
||||
for (TriggerEvent event : events) {
|
||||
Watch watch = watchStore.get(event.jobName());
|
||||
if (watch == null) {
|
||||
logger.warn("unable to find watch [{}] in the watch store, perhaps it has been deleted", event.jobName());
|
||||
continue;
|
||||
}
|
||||
GetResponse response = client.getWatch(event.jobName());
|
||||
if (response.isExists() == false) {
|
||||
logger.warn("unable to find watch [{}] in watch index, perhaps it has been deleted", event.jobName());
|
||||
} else {
|
||||
try {
|
||||
Watch watch = parser.parseWithSecrets(response.getId(), true, response.getSourceAsBytesRef(), now);
|
||||
TriggeredExecutionContext ctx = new TriggeredExecutionContext(watch, now, event, defaultThrottlePeriod);
|
||||
contexts.add(ctx);
|
||||
triggeredWatches.add(new TriggeredWatch(ctx.id(), event));
|
||||
} catch (IOException e) {
|
||||
logger.warn("unable to parse watch [{}]", event.jobName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("saving watch records [{}]", triggeredWatches.size());
|
||||
if (triggeredWatches.isEmpty() == false) {
|
||||
logger.debug("saving triggered [{}] watches", triggeredWatches.size());
|
||||
|
||||
triggeredWatchStore.putAll(triggeredWatches, new ActionListener<BitSet>() {
|
||||
@Override
|
||||
public void onResponse(BitSet slots) {
|
||||
triggeredWatchStore.putAll(triggeredWatches, ActionListener.wrap(
|
||||
(slots) -> {
|
||||
int slot = 0;
|
||||
while ((slot = slots.nextSetBit(slot)) != -1) {
|
||||
executeAsync(contexts.get(slot), triggeredWatches.get(slot));
|
||||
slot++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
Throwable cause = ExceptionsHelper.unwrapCause(e);
|
||||
if (cause instanceof EsRejectedExecutionException) {
|
||||
logger.debug("failed to store watch records due to overloaded threadpool [{}]", ExceptionsHelper.detailedMessage(e));
|
||||
} else {
|
||||
logger.warn("failed to store watch records", e);
|
||||
}
|
||||
},
|
||||
(e) -> logger.warn("failed to store watch [] records", e)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -214,26 +226,24 @@ public class ExecutionService extends AbstractComponent {
|
|||
if (!started.get()) {
|
||||
throw new IllegalStateException("not started");
|
||||
}
|
||||
final LinkedList<TriggeredWatch> triggeredWatches = new LinkedList<>();
|
||||
final LinkedList<TriggeredExecutionContext> contexts = new LinkedList<>();
|
||||
final List<TriggeredWatch> triggeredWatches = new ArrayList<>();
|
||||
final List<TriggeredExecutionContext> contexts = new ArrayList<>();
|
||||
|
||||
DateTime now = new DateTime(clock.millis(), UTC);
|
||||
for (TriggerEvent event : events) {
|
||||
Watch watch = watchStore.get(event.jobName());
|
||||
if (watch == null) {
|
||||
logger.warn("unable to find watch [{}] in the watch store, perhaps it has been deleted", event.jobName());
|
||||
GetResponse response = client.getWatch(event.jobName());
|
||||
if (response.isExists() == false) {
|
||||
logger.warn("unable to find watch [{}] in watch index, perhaps it has been deleted", event.jobName());
|
||||
continue;
|
||||
}
|
||||
Watch watch = parser.parseWithSecrets(response.getId(), true, response.getSourceAsBytesRef(), now);
|
||||
TriggeredExecutionContext ctx = new TriggeredExecutionContext(watch, now, event, defaultThrottlePeriod);
|
||||
contexts.add(ctx);
|
||||
triggeredWatches.add(new TriggeredWatch(ctx.id(), event));
|
||||
}
|
||||
|
||||
logger.debug("saving watch records [{}]", triggeredWatches.size());
|
||||
if (triggeredWatches.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (triggeredWatches.isEmpty() == false) {
|
||||
logger.debug("saving triggered [{}] watches", triggeredWatches.size());
|
||||
BitSet slots = triggeredWatchStore.putAll(triggeredWatches);
|
||||
int slot = 0;
|
||||
while ((slot = slots.nextSetBit(slot)) != -1) {
|
||||
|
@ -241,6 +251,7 @@ public class ExecutionService extends AbstractComponent {
|
|||
slot++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WatchRecord execute(WatchExecutionContext ctx) {
|
||||
WatchRecord record = null;
|
||||
|
@ -250,7 +261,10 @@ public class ExecutionService extends AbstractComponent {
|
|||
}
|
||||
try {
|
||||
currentExecutions.put(ctx.watch().id(), new WatchExecution(ctx, Thread.currentThread()));
|
||||
if (ctx.knownWatch() && watchStore.get(ctx.watch().id()) == null) {
|
||||
final AtomicBoolean watchExists = new AtomicBoolean(true);
|
||||
client.getWatch(ctx.watch().id(), ActionListener.wrap((r) -> watchExists.set(r.isExists()), (e) -> watchExists.set(false)));
|
||||
|
||||
if (ctx.knownWatch() && watchExists.get() == false) {
|
||||
// fail fast if we are trying to execute a deleted watch
|
||||
String message = "unable to find watch for record [" + ctx.id() + "], perhaps it has been deleted, ignoring...";
|
||||
logger.warn("{}", message);
|
||||
|
@ -261,7 +275,7 @@ public class ExecutionService extends AbstractComponent {
|
|||
|
||||
record = executeInner(ctx);
|
||||
if (ctx.recordExecution()) {
|
||||
watchStore.updateStatus(ctx.watch());
|
||||
client.updateWatchStatus(ctx.watch());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -330,11 +344,7 @@ public class ExecutionService extends AbstractComponent {
|
|||
try {
|
||||
executor.execute(new WatchExecutionTask(ctx));
|
||||
} catch (EsRejectedExecutionException e) {
|
||||
// we are still in the transport thread here most likely, so we cannot run heavy operations
|
||||
// this means some offloading needs to be done for indexing into the history and delete the triggered watches entry
|
||||
threadPool.generic().execute(() -> {
|
||||
String message = "failed to run triggered watch [" + triggeredWatch.id() + "] due to thread pool capacity";
|
||||
logger.debug("{}", message);
|
||||
WatchRecord record = ctx.abortBeforeExecution(ExecutionState.FAILED, message);
|
||||
try {
|
||||
if (ctx.overrideRecordOnConflict()) {
|
||||
|
@ -355,8 +365,7 @@ public class ExecutionService extends AbstractComponent {
|
|||
new ParameterizedMessage("Error deleting triggered watch store record for watch [{}] after thread pool " +
|
||||
"rejection", triggeredWatch.id()), exc);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
WatchRecord executeInner(WatchExecutionContext ctx) {
|
||||
|
@ -417,8 +426,8 @@ public class ExecutionService extends AbstractComponent {
|
|||
assert triggeredWatches != null;
|
||||
int counter = 0;
|
||||
for (TriggeredWatch triggeredWatch : triggeredWatches) {
|
||||
Watch watch = watchStore.get(triggeredWatch.id().watchId());
|
||||
if (watch == null) {
|
||||
GetResponse response = client.getWatch(triggeredWatch.id().watchId());
|
||||
if (response.isExists() == false) {
|
||||
String message = "unable to find watch for record [" + triggeredWatch.id().watchId() + "]/[" + triggeredWatch.id() +
|
||||
"], perhaps it has been deleted, ignoring...";
|
||||
WatchRecord record = new WatchRecord.MessageWatchRecord(triggeredWatch.id(), triggeredWatch.triggerEvent(),
|
||||
|
@ -426,8 +435,10 @@ public class ExecutionService extends AbstractComponent {
|
|||
historyStore.forcePut(record);
|
||||
triggeredWatchStore.delete(triggeredWatch.id());
|
||||
} else {
|
||||
TriggeredExecutionContext ctx = new StartupExecutionContext(watch, new DateTime(clock.millis(), UTC),
|
||||
triggeredWatch.triggerEvent(), defaultThrottlePeriod);
|
||||
DateTime now = new DateTime(clock.millis(), UTC);
|
||||
Watch watch = parser.parseWithSecrets(response.getId(), true, response.getSourceAsBytesRef(), now);
|
||||
TriggeredExecutionContext ctx =
|
||||
new StartupExecutionContext(watch, now, triggeredWatch.triggerEvent(), defaultThrottlePeriod);
|
||||
executeAsync(ctx, triggeredWatch);
|
||||
counter++;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.elasticsearch.action.bulk.BulkRequest;
|
|||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
|
@ -24,7 +23,6 @@ import org.elasticsearch.common.component.AbstractComponent;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilders;
|
||||
|
@ -75,8 +73,9 @@ public class TriggeredWatchStore extends AbstractComponent {
|
|||
public boolean validate(ClusterState state) {
|
||||
try {
|
||||
IndexMetaData indexMetaData = WatchStoreUtils.getConcreteIndex(INDEX_NAME, state.metaData());
|
||||
if (indexMetaData != null) {
|
||||
return state.routingTable().index(indexMetaData.getIndex()).allPrimaryShardsActive();
|
||||
} catch (IndexNotFoundException e) {
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
logger.trace((Supplier<?>) () -> new ParameterizedMessage("error getting index meta data [{}]: ", INDEX_NAME), e);
|
||||
return false;
|
||||
|
@ -108,29 +107,20 @@ public class TriggeredWatchStore extends AbstractComponent {
|
|||
}
|
||||
}
|
||||
|
||||
public void put(final TriggeredWatch triggeredWatch, final ActionListener<Boolean> listener) throws Exception {
|
||||
public void put(final TriggeredWatch triggeredWatch, final ActionListener<Boolean> listener) {
|
||||
ensureStarted();
|
||||
try {
|
||||
IndexRequest request = new IndexRequest(INDEX_NAME, DOC_TYPE, triggeredWatch.id().value())
|
||||
.source(XContentFactory.jsonBuilder().value(triggeredWatch))
|
||||
.opType(IndexRequest.OpType.CREATE);
|
||||
client.index(request, new ActionListener<IndexResponse>() {
|
||||
@Override
|
||||
public void onResponse(IndexResponse response) {
|
||||
listener.onResponse(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
client.index(request, ActionListener.wrap(response -> listener.onResponse(true), listener::onFailure));
|
||||
} catch (IOException e) {
|
||||
throw ioException("failed to persist triggered watch [{}]", e, triggeredWatch);
|
||||
logger.warn((Supplier<?>) () -> new ParameterizedMessage("could not index triggered watch [{}], ignoring it...",
|
||||
triggeredWatch.id()), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void putAll(final List<TriggeredWatch> triggeredWatches, final ActionListener<BitSet> listener) throws Exception {
|
||||
public void putAll(final List<TriggeredWatch> triggeredWatches, final ActionListener<BitSet> listener) {
|
||||
|
||||
if (triggeredWatches.isEmpty()) {
|
||||
listener.onResponse(new BitSet(0));
|
||||
|
@ -138,55 +128,39 @@ public class TriggeredWatchStore extends AbstractComponent {
|
|||
}
|
||||
|
||||
if (triggeredWatches.size() == 1) {
|
||||
put(triggeredWatches.get(0), new ActionListener<Boolean>() {
|
||||
@Override
|
||||
public void onResponse(Boolean success) {
|
||||
put(triggeredWatches.get(0), ActionListener.wrap(success -> {
|
||||
BitSet bitSet = new BitSet(1);
|
||||
bitSet.set(0);
|
||||
listener.onResponse(bitSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
}, listener::onFailure));
|
||||
return;
|
||||
}
|
||||
|
||||
ensureStarted();
|
||||
try {
|
||||
BulkRequest request = new BulkRequest();
|
||||
for (TriggeredWatch triggeredWatch : triggeredWatches) {
|
||||
try {
|
||||
IndexRequest indexRequest = new IndexRequest(INDEX_NAME, DOC_TYPE, triggeredWatch.id().value());
|
||||
indexRequest.source(XContentFactory.jsonBuilder().value(triggeredWatch));
|
||||
indexRequest.opType(IndexRequest.OpType.CREATE);
|
||||
request.add(indexRequest);
|
||||
} catch (IOException e) {
|
||||
logger.warn("could not create JSON to store triggered watch [{}]", triggeredWatch.id().value());
|
||||
}
|
||||
client.bulk(request, new ActionListener<BulkResponse>() {
|
||||
@Override
|
||||
public void onResponse(BulkResponse response) {
|
||||
}
|
||||
client.bulk(request, ActionListener.wrap(response -> {
|
||||
BitSet successFullSlots = new BitSet(triggeredWatches.size());
|
||||
for (int i = 0; i < response.getItems().length; i++) {
|
||||
BulkItemResponse itemResponse = response.getItems()[i];
|
||||
if (itemResponse.isFailed()) {
|
||||
logger.error("could store triggered watch with id [{}], because failed [{}]", itemResponse.getId(),
|
||||
logger.error("could not store triggered watch with id [{}], failed [{}]", itemResponse.getId(),
|
||||
itemResponse.getFailureMessage());
|
||||
} else {
|
||||
successFullSlots.set(i);
|
||||
}
|
||||
}
|
||||
listener.onResponse(successFullSlots);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw ioException("failed to persist triggered watches", e);
|
||||
}
|
||||
}, listener::onFailure));
|
||||
}
|
||||
|
||||
public BitSet putAll(final List<TriggeredWatch> triggeredWatches) throws Exception {
|
||||
|
@ -229,10 +203,8 @@ public class TriggeredWatchStore extends AbstractComponent {
|
|||
}
|
||||
|
||||
public Collection<TriggeredWatch> loadTriggeredWatches(ClusterState state) {
|
||||
IndexMetaData indexMetaData;
|
||||
try {
|
||||
indexMetaData = WatchStoreUtils.getConcreteIndex(INDEX_NAME, state.metaData());
|
||||
} catch (IndexNotFoundException e) {
|
||||
IndexMetaData indexMetaData = WatchStoreUtils.getConcreteIndex(INDEX_NAME, state.metaData());
|
||||
if (indexMetaData == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.xpack.watcher.rest.action;
|
|||
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.rest.BytesRestResponse;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
|
@ -16,10 +17,8 @@ import org.elasticsearch.rest.RestStatus;
|
|||
import org.elasticsearch.rest.action.RestBuilderListener;
|
||||
import org.elasticsearch.xpack.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.xpack.watcher.rest.WatcherRestHandler;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.WatcherParams;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchRequest;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -48,11 +47,9 @@ public class RestGetWatchAction extends WatcherRestHandler {
|
|||
.field("found", response.isFound())
|
||||
.field("_id", response.getId());
|
||||
if (response.isFound()) {
|
||||
WatcherParams params = WatcherParams.builder(request)
|
||||
.put(WatchStatus.INCLUDE_VERSION_KEY, true)
|
||||
.build();
|
||||
builder.field("_status", response.getStatus(), params);
|
||||
builder.field("watch", response.getSource(), params);
|
||||
ToXContent.MapParams xContentParams = new ToXContent.MapParams(request.params());
|
||||
builder.field("_status", response.getStatus(), xContentParams);
|
||||
builder.field("watch", response.getSource(), xContentParams);
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import org.elasticsearch.rest.RestRequest;
|
|||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.xpack.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.xpack.watcher.rest.WatcherRestHandler;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -33,18 +33,18 @@ public class RestHijackOperationAction extends WatcherRestHandler {
|
|||
super(settings);
|
||||
if (!settings.getAsBoolean(ALLOW_DIRECT_ACCESS_TO_WATCH_INDEX_SETTING, false)) {
|
||||
WatcherRestHandler unsupportedHandler = new UnsupportedHandler(settings);
|
||||
controller.registerHandler(POST, WatchStore.INDEX + "/watch", this);
|
||||
controller.registerHandler(POST, WatchStore.INDEX + "/watch/{id}", this);
|
||||
controller.registerHandler(PUT, WatchStore.INDEX + "/watch/{id}", this);
|
||||
controller.registerHandler(POST, WatchStore.INDEX + "/watch/{id}/_update", this);
|
||||
controller.registerHandler(DELETE, WatchStore.INDEX + "/watch/_query", this);
|
||||
controller.registerHandler(DELETE, WatchStore.INDEX + "/watch/{id}", this);
|
||||
controller.registerHandler(GET, WatchStore.INDEX + "/watch/{id}", this);
|
||||
controller.registerHandler(POST, WatchStore.INDEX + "/watch/_bulk", unsupportedHandler);
|
||||
controller.registerHandler(POST, WatchStore.INDEX + "/_bulk", unsupportedHandler);
|
||||
controller.registerHandler(PUT, WatchStore.INDEX + "/watch/_bulk", unsupportedHandler);
|
||||
controller.registerHandler(PUT, WatchStore.INDEX + "/_bulk", unsupportedHandler);
|
||||
controller.registerHandler(DELETE, WatchStore.INDEX, unsupportedHandler);
|
||||
controller.registerHandler(POST, Watch.INDEX + "/watch", this);
|
||||
controller.registerHandler(POST, Watch.INDEX + "/watch/{id}", this);
|
||||
controller.registerHandler(PUT, Watch.INDEX + "/watch/{id}", this);
|
||||
controller.registerHandler(POST, Watch.INDEX + "/watch/{id}/_update", this);
|
||||
controller.registerHandler(DELETE, Watch.INDEX + "/watch/_query", this);
|
||||
controller.registerHandler(DELETE, Watch.INDEX + "/watch/{id}", this);
|
||||
controller.registerHandler(GET, Watch.INDEX + "/watch/{id}", this);
|
||||
controller.registerHandler(POST, Watch.INDEX + "/watch/_bulk", unsupportedHandler);
|
||||
controller.registerHandler(POST, Watch.INDEX + "/_bulk", unsupportedHandler);
|
||||
controller.registerHandler(PUT, Watch.INDEX + "/watch/_bulk", unsupportedHandler);
|
||||
controller.registerHandler(PUT, Watch.INDEX + "/_bulk", unsupportedHandler);
|
||||
controller.registerHandler(DELETE, Watch.INDEX, unsupportedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ public class RestHijackOperationAction extends WatcherRestHandler {
|
|||
}
|
||||
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
|
||||
jsonBuilder.startObject().field("error", "This endpoint is not supported for " +
|
||||
request.method().name() + " on " + WatchStore.INDEX + " index. Please use " +
|
||||
request.method().name() + " on " + Watch.INDEX + " index. Please use " +
|
||||
request.method().name() + " " + URI_BASE + "/watch/<watch_id> instead");
|
||||
jsonBuilder.field("status", RestStatus.BAD_REQUEST.getStatus());
|
||||
jsonBuilder.endObject();
|
||||
|
@ -77,7 +77,7 @@ public class RestHijackOperationAction extends WatcherRestHandler {
|
|||
}
|
||||
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
|
||||
jsonBuilder.startObject().field("error", "This endpoint is not supported for " +
|
||||
request.method().name() + " on " + WatchStore.INDEX + " index.");
|
||||
request.method().name() + " on " + Watch.INDEX + " index.");
|
||||
jsonBuilder.field("status", RestStatus.BAD_REQUEST.getStatus());
|
||||
jsonBuilder.endObject();
|
||||
return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.BAD_REQUEST, jsonBuilder));
|
||||
|
|
|
@ -14,6 +14,8 @@ import org.elasticsearch.action.bulk.BulkRequest;
|
|||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.delete.DeleteResponse;
|
||||
import org.elasticsearch.action.get.GetRequest;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.search.ClearScrollRequest;
|
||||
|
@ -21,13 +23,23 @@ import org.elasticsearch.action.search.ClearScrollResponse;
|
|||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||
import org.elasticsearch.action.support.PlainActionFuture;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.routing.Preference;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.index.engine.DocumentMissingException;
|
||||
import org.elasticsearch.xpack.common.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.xpack.security.InternalClient;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* A lazily initialized proxy to an elasticsearch {@link Client}. Inject this proxy whenever a client
|
||||
|
@ -65,6 +77,10 @@ public class WatcherClientProxy extends ClientProxy {
|
|||
return client.update(preProcess(request)).actionGet(defaultIndexTimeout);
|
||||
}
|
||||
|
||||
public void update(UpdateRequest request, ActionListener<UpdateResponse> listener) {
|
||||
client.update(preProcess(request), listener);
|
||||
}
|
||||
|
||||
public BulkResponse bulk(BulkRequest request, TimeValue timeout) {
|
||||
if (timeout == null) {
|
||||
timeout = defaultBulkTimeout;
|
||||
|
@ -110,4 +126,47 @@ public class WatcherClientProxy extends ClientProxy {
|
|||
preProcess(request);
|
||||
client.admin().indices().putTemplate(request, listener);
|
||||
}
|
||||
|
||||
public GetResponse getWatch(String id) {
|
||||
PlainActionFuture<GetResponse> future = PlainActionFuture.newFuture();
|
||||
getWatch(id, future);
|
||||
return future.actionGet();
|
||||
}
|
||||
|
||||
public void getWatch(String id, ActionListener<GetResponse> listener) {
|
||||
GetRequest getRequest = new GetRequest(Watch.INDEX, Watch.DOC_TYPE, id).preference(Preference.LOCAL.type()).realtime(true);
|
||||
client.get(preProcess(getRequest), listener);
|
||||
}
|
||||
|
||||
public void deleteWatch(String id, ActionListener<DeleteResponse> listener) {
|
||||
DeleteRequest request = new DeleteRequest(Watch.INDEX, Watch.DOC_TYPE, id);
|
||||
client.delete(preProcess(request), listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates and persists the status of the given watch
|
||||
*
|
||||
* If the watch is missing (because it might have been deleted by the user during an execution), then this method
|
||||
* does nothing and just returns without throwing an exception
|
||||
*/
|
||||
public void updateWatchStatus(Watch watch) throws IOException {
|
||||
// at the moment we store the status together with the watch,
|
||||
// so we just need to update the watch itself
|
||||
ToXContent.MapParams params = new ToXContent.MapParams(Collections.singletonMap(Watch.INCLUDE_STATUS_KEY, "true"));
|
||||
XContentBuilder source = JsonXContent.contentBuilder().
|
||||
startObject()
|
||||
.field(Watch.Field.STATUS.getPreferredName(), watch.status(), params)
|
||||
.endObject();
|
||||
|
||||
UpdateRequest updateRequest = new UpdateRequest(Watch.INDEX, Watch.DOC_TYPE, watch.id());
|
||||
updateRequest.doc(source);
|
||||
updateRequest.version(watch.version());
|
||||
try {
|
||||
this.update(updateRequest);
|
||||
} catch (DocumentMissingException e) {
|
||||
// do not rethrow this exception, otherwise the watch history will contain an exception
|
||||
// even though the execution might have been fine
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,10 +25,6 @@ public class WatcherParams extends ToXContent.DelegatingMapParams {
|
|||
return wrap(params).hideSecrets();
|
||||
}
|
||||
|
||||
public static boolean collapseArrays(ToXContent.Params params) {
|
||||
return wrap(params).collapseArrays();
|
||||
}
|
||||
|
||||
public static boolean debug(ToXContent.Params params) {
|
||||
return wrap(params).debug();
|
||||
}
|
||||
|
@ -41,10 +37,6 @@ public class WatcherParams extends ToXContent.DelegatingMapParams {
|
|||
return paramAsBoolean(HIDE_SECRETS_KEY, false);
|
||||
}
|
||||
|
||||
public boolean collapseArrays() {
|
||||
return paramAsBoolean(COLLAPSE_ARRAYS_KEY, false);
|
||||
}
|
||||
|
||||
public boolean debug() {
|
||||
return paramAsBoolean(DEBUG_KEY, false);
|
||||
}
|
||||
|
@ -77,11 +69,6 @@ public class WatcherParams extends ToXContent.DelegatingMapParams {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder collapseArrays(boolean collapseArrays) {
|
||||
params.put(COLLAPSE_ARRAYS_KEY, String.valueOf(collapseArrays));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder debug(boolean debug) {
|
||||
params.put(DEBUG_KEY, String.valueOf(debug));
|
||||
return this;
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
package org.elasticsearch.xpack.watcher.transport.actions.ack;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ResourceNotFoundException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
|
@ -15,29 +17,43 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
|||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.watcher.WatcherService;
|
||||
import org.elasticsearch.xpack.watcher.actions.ActionWrapper;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStatus;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
/**
|
||||
* Performs the ack operation.
|
||||
*/
|
||||
public class TransportAckWatchAction extends WatcherTransportAction<AckWatchRequest, AckWatchResponse> {
|
||||
|
||||
private final WatcherService watcherService;
|
||||
private final Clock clock;
|
||||
private final Watch.Parser parser;
|
||||
private final WatcherClientProxy client;
|
||||
|
||||
@Inject
|
||||
public TransportAckWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
|
||||
XPackLicenseState licenseState) {
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, Clock clock, XPackLicenseState licenseState,
|
||||
Watch.Parser parser, WatcherClientProxy client) {
|
||||
super(settings, AckWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
|
||||
licenseState, AckWatchRequest::new);
|
||||
this.watcherService = watcherService;
|
||||
this.clock = clock;
|
||||
this.parser = parser;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,19 +69,56 @@ public class TransportAckWatchAction extends WatcherTransportAction<AckWatchRequ
|
|||
@Override
|
||||
protected void masterOperation(AckWatchRequest request, ClusterState state, ActionListener<AckWatchResponse> listener) throws
|
||||
ElasticsearchException {
|
||||
try {
|
||||
WatchStatus watchStatus = watcherService.ackWatch(request.getWatchId(), request.getActionIds());
|
||||
AckWatchResponse response = new AckWatchResponse(watchStatus);
|
||||
listener.onResponse(response);
|
||||
} catch (Exception e) {
|
||||
listener.onFailure(e);
|
||||
client.getWatch(request.getWatchId(), ActionListener.wrap((response) -> {
|
||||
if (response.isExists() == false) {
|
||||
listener.onFailure(new ResourceNotFoundException("Watch with id [{}] does not exit", request.getWatchId()));
|
||||
} else {
|
||||
DateTime now = new DateTime(clock.millis(), UTC);
|
||||
Watch watch = parser.parseWithSecrets(request.getWatchId(), true, response.getSourceAsBytesRef(), now);
|
||||
watch.version(response.getVersion());
|
||||
watch.status().version(response.getVersion());
|
||||
String[] actionIds = request.getActionIds();
|
||||
if (actionIds == null || actionIds.length == 0) {
|
||||
actionIds = new String[]{Watch.ALL_ACTIONS_ID};
|
||||
}
|
||||
|
||||
// exit early in case nothing changes
|
||||
boolean isChanged = watch.ack(now, actionIds);
|
||||
if (isChanged == false) {
|
||||
listener.onResponse(new AckWatchResponse(watch.status()));
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateRequest updateRequest = new UpdateRequest(Watch.INDEX, Watch.DOC_TYPE, request.getWatchId());
|
||||
// this may reject this action, but prevents concurrent updates from a watch execution
|
||||
updateRequest.version(response.getVersion());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject()
|
||||
.startObject(Watch.Field.STATUS.getPreferredName())
|
||||
.startObject("actions");
|
||||
|
||||
List<String> actionIdsAsList = Arrays.asList(actionIds);
|
||||
boolean updateAll = actionIdsAsList.contains("_all");
|
||||
for (ActionWrapper actionWrapper : watch.actions()) {
|
||||
if (updateAll || actionIdsAsList.contains(actionWrapper.id())) {
|
||||
builder.startObject(actionWrapper.id())
|
||||
.field("ack", watch.status().actionStatus(actionWrapper.id()).ackStatus(), ToXContent.EMPTY_PARAMS)
|
||||
.endObject();
|
||||
}
|
||||
}
|
||||
|
||||
builder.endObject().endObject().endObject();
|
||||
updateRequest.doc(builder);
|
||||
|
||||
client.update(updateRequest, ActionListener.wrap(
|
||||
(updateResponse) -> listener.onResponse(new AckWatchResponse(watch.status())),
|
||||
listener::onFailure));
|
||||
}
|
||||
}, listener::onFailure));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(AckWatchRequest request, ClusterState state) {
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, WatchStore.INDEX);
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, Watch.INDEX);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
package org.elasticsearch.xpack.watcher.transport.actions.activate;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ResourceNotFoundException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
|
@ -15,29 +17,46 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
|||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.watcher.WatcherService;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerService;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStatus;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Clock;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils.writeDate;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
/**
|
||||
* Performs the watch de/activation operation.
|
||||
*/
|
||||
public class TransportActivateWatchAction extends WatcherTransportAction<ActivateWatchRequest, ActivateWatchResponse> {
|
||||
|
||||
private final WatcherService watcherService;
|
||||
private final Clock clock;
|
||||
private final TriggerService triggerService;
|
||||
private final Watch.Parser parser;
|
||||
private final WatcherClientProxy client;
|
||||
|
||||
@Inject
|
||||
public TransportActivateWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
|
||||
XPackLicenseState licenseState) {
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, Clock clock,
|
||||
XPackLicenseState licenseState, TriggerService triggerService, Watch.Parser parser,
|
||||
WatcherClientProxy client) {
|
||||
super(settings, ActivateWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
|
||||
licenseState, ActivateWatchRequest::new);
|
||||
this.watcherService = watcherService;
|
||||
this.clock = clock;
|
||||
this.triggerService = triggerService;
|
||||
this.parser = parser;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,20 +73,50 @@ public class TransportActivateWatchAction extends WatcherTransportAction<Activat
|
|||
protected void masterOperation(ActivateWatchRequest request, ClusterState state, ActionListener<ActivateWatchResponse> listener)
|
||||
throws ElasticsearchException {
|
||||
try {
|
||||
WatchStatus watchStatus = request.isActivate() ?
|
||||
watcherService.activateWatch(request.getWatchId()) :
|
||||
watcherService.deactivateWatch(request.getWatchId());
|
||||
ActivateWatchResponse response = new ActivateWatchResponse(watchStatus);
|
||||
listener.onResponse(response);
|
||||
} catch (Exception e) {
|
||||
// if this is about deactivation, remove this immediately from the trigger service, no need to wait for all those async calls
|
||||
if (request.isActivate() == false) {
|
||||
triggerService.remove(request.getWatchId());
|
||||
}
|
||||
|
||||
DateTime now = new DateTime(clock.millis(), UTC);
|
||||
UpdateRequest updateRequest = new UpdateRequest(Watch.INDEX, Watch.DOC_TYPE, request.getWatchId());
|
||||
XContentBuilder builder = activateWatchBuilder(request.isActivate(), now);
|
||||
updateRequest.doc(builder);
|
||||
|
||||
client.update(updateRequest, ActionListener.wrap(updateResponse -> {
|
||||
client.getWatch(request.getWatchId(), ActionListener.wrap(getResponse -> {
|
||||
if (getResponse.isExists()) {
|
||||
Watch watch = parser.parseWithSecrets(request.getWatchId(), true, getResponse.getSourceAsBytesRef(), now);
|
||||
watch.version(getResponse.getVersion());
|
||||
watch.status().version(getResponse.getVersion());
|
||||
|
||||
if (request.isActivate()) {
|
||||
triggerService.add(watch);
|
||||
}
|
||||
listener.onResponse(new ActivateWatchResponse(watch.status()));
|
||||
} else {
|
||||
listener.onFailure(new ResourceNotFoundException("Watch with id [{}] does not exist", request.getWatchId()));
|
||||
}
|
||||
}, listener::onFailure));
|
||||
}, listener::onFailure));
|
||||
} catch (IOException e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
private XContentBuilder activateWatchBuilder(boolean active, DateTime now) throws IOException {
|
||||
XContentBuilder builder = jsonBuilder().startObject()
|
||||
.startObject(Watch.Field.STATUS.getPreferredName())
|
||||
.startObject(WatchStatus.Field.STATE.getPreferredName())
|
||||
.field(WatchStatus.Field.ACTIVE.getPreferredName(), active);
|
||||
|
||||
writeDate(WatchStatus.Field.TIMESTAMP.getPreferredName(), builder, now);
|
||||
builder.endObject().endObject().endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(ActivateWatchRequest request, ClusterState state) {
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, WatchStore.INDEX);
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, Watch.INDEX);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ package org.elasticsearch.xpack.watcher.transport.actions.delete;
|
|||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.DocWriteResponse;
|
||||
import org.elasticsearch.action.delete.DeleteResponse;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
|
@ -20,25 +19,28 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.watcher.WatcherService;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerService;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
|
||||
/**
|
||||
* Performs the delete operation.
|
||||
*/
|
||||
public class TransportDeleteWatchAction extends WatcherTransportAction<DeleteWatchRequest, DeleteWatchResponse> {
|
||||
|
||||
private final WatcherService watcherService;
|
||||
private final WatcherClientProxy client;
|
||||
private final TriggerService triggerService;
|
||||
|
||||
@Inject
|
||||
public TransportDeleteWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
|
||||
XPackLicenseState licenseState) {
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, WatcherClientProxy client,
|
||||
XPackLicenseState licenseState, TriggerService triggerService) {
|
||||
super(settings, DeleteWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
|
||||
licenseState, DeleteWatchRequest::new);
|
||||
this.watcherService = watcherService;
|
||||
this.client = client;
|
||||
this.triggerService = triggerService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,20 +56,19 @@ public class TransportDeleteWatchAction extends WatcherTransportAction<DeleteWat
|
|||
@Override
|
||||
protected void masterOperation(DeleteWatchRequest request, ClusterState state, ActionListener<DeleteWatchResponse> listener) throws
|
||||
ElasticsearchException {
|
||||
try {
|
||||
DeleteResponse deleteResponse = watcherService.deleteWatch(request.getId()).deleteResponse();
|
||||
client.deleteWatch(request.getId(), ActionListener.wrap(deleteResponse -> {
|
||||
boolean deleted = deleteResponse.getResult() == DocWriteResponse.Result.DELETED;
|
||||
DeleteWatchResponse response = new DeleteWatchResponse(deleteResponse.getId(), deleteResponse.getVersion(), deleted);
|
||||
listener.onResponse(response);
|
||||
} catch (Exception e) {
|
||||
listener.onFailure(e);
|
||||
if (deleted) {
|
||||
triggerService.remove(request.getId());
|
||||
}
|
||||
listener.onResponse(response);
|
||||
},
|
||||
listener::onFailure));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(DeleteWatchRequest request, ClusterState state) {
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, WatchStore.INDEX);
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, Watch.INDEX);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.apache.logging.log4j.message.ParameterizedMessage;
|
|||
import org.apache.logging.log4j.util.Supplier;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
|
@ -29,6 +30,7 @@ import org.elasticsearch.xpack.watcher.execution.ExecutionService;
|
|||
import org.elasticsearch.xpack.watcher.execution.ManualExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.WatcherParams;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerEvent;
|
||||
|
@ -36,13 +38,12 @@ import org.elasticsearch.xpack.watcher.trigger.TriggerService;
|
|||
import org.elasticsearch.xpack.watcher.trigger.manual.ManualTriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.watch.Payload;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Clock;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.xpack.watcher.support.Exceptions.illegalArgument;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
/**
|
||||
|
@ -51,24 +52,24 @@ import static org.joda.time.DateTimeZone.UTC;
|
|||
public class TransportExecuteWatchAction extends WatcherTransportAction<ExecuteWatchRequest, ExecuteWatchResponse> {
|
||||
|
||||
private final ExecutionService executionService;
|
||||
private final WatchStore watchStore;
|
||||
private final Clock clock;
|
||||
private final TriggerService triggerService;
|
||||
private final Watch.Parser watchParser;
|
||||
private final WatcherClientProxy client;
|
||||
|
||||
@Inject
|
||||
public TransportExecuteWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, ExecutionService executionService,
|
||||
Clock clock, XPackLicenseState licenseState, WatchStore watchStore, TriggerService triggerService,
|
||||
Watch.Parser watchParser) {
|
||||
Clock clock, XPackLicenseState licenseState, TriggerService triggerService,
|
||||
Watch.Parser watchParser, WatcherClientProxy client) {
|
||||
super(settings, ExecuteWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
|
||||
licenseState, ExecuteWatchRequest::new);
|
||||
this.executionService = executionService;
|
||||
this.watchStore = watchStore;
|
||||
this.clock = clock;
|
||||
this.triggerService = triggerService;
|
||||
this.watchParser = watchParser;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -84,24 +85,32 @@ public class TransportExecuteWatchAction extends WatcherTransportAction<ExecuteW
|
|||
@Override
|
||||
protected void masterOperation(ExecuteWatchRequest request, ClusterState state, ActionListener<ExecuteWatchResponse> listener)
|
||||
throws ElasticsearchException {
|
||||
try {
|
||||
Watch watch;
|
||||
boolean knownWatch;
|
||||
if (request.getId() != null) {
|
||||
watch = watchStore.get(request.getId());
|
||||
if (watch == null) {
|
||||
//todo we need to find a better std exception for this one
|
||||
throw new ElasticsearchException("watch [{}] does not exist", request.getId());
|
||||
try {
|
||||
// should be executed async in the future
|
||||
GetResponse getResponse = client.getWatch(request.getId());
|
||||
Watch watch = watchParser.parse(request.getId(), true, getResponse.getSourceAsBytesRef());
|
||||
ExecuteWatchResponse executeWatchResponse = executeWatch(request, watch, true);
|
||||
listener.onResponse(executeWatchResponse);
|
||||
} catch (IOException e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
knownWatch = true;
|
||||
} else if (request.getWatchSource() != null) {
|
||||
try {
|
||||
assert !request.isRecordExecution();
|
||||
watch = watchParser.parse(ExecuteWatchRequest.INLINE_WATCH_ID, false, request.getWatchSource());
|
||||
knownWatch = false;
|
||||
Watch watch = watchParser.parse(ExecuteWatchRequest.INLINE_WATCH_ID, true, request.getWatchSource());
|
||||
ExecuteWatchResponse response = executeWatch(request, watch, false);
|
||||
listener.onResponse(response);
|
||||
} catch (Exception e) {
|
||||
logger.error((Supplier<?>) () -> new ParameterizedMessage("failed to execute [{}]", request.getId()), e);
|
||||
listener.onFailure(e);
|
||||
}
|
||||
} else {
|
||||
throw illegalArgument("no watch provided");
|
||||
listener.onFailure(new IllegalArgumentException("no watch provided"));
|
||||
}
|
||||
}
|
||||
|
||||
private ExecuteWatchResponse executeWatch(ExecuteWatchRequest request, Watch watch, boolean knownWatch) throws IOException {
|
||||
String triggerType = watch.trigger().type();
|
||||
TriggerEvent triggerEvent = triggerService.simulateEvent(triggerType, watch.id(), request.getTriggerData());
|
||||
|
||||
|
@ -123,18 +132,14 @@ public class TransportExecuteWatchAction extends WatcherTransportAction<ExecuteW
|
|||
|
||||
WatchRecord record = executionService.execute(ctxBuilder.build());
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
|
||||
record.toXContent(builder, WatcherParams.builder().hideSecrets(true).debug(request.isDebug()).build());
|
||||
ExecuteWatchResponse response = new ExecuteWatchResponse(record.id().value(), builder.bytes(), XContentType.JSON);
|
||||
listener.onResponse(response);
|
||||
} catch (Exception e) {
|
||||
logger.error((Supplier<?>) () -> new ParameterizedMessage("failed to execute [{}]", request.getId()), e);
|
||||
listener.onFailure(e);
|
||||
}
|
||||
return new ExecuteWatchResponse(record.id().value(), builder.bytes(), XContentType.JSON);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(ExecuteWatchRequest request, ClusterState state) {
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, WatchStore.INDEX);
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, Watch.INDEX);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.watcher.transport.actions.get;
|
||||
|
||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
import org.apache.logging.log4j.util.Supplier;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
|
@ -15,7 +13,6 @@ import org.elasticsearch.cluster.block.ClusterBlockException;
|
|||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -25,36 +22,38 @@ import org.elasticsearch.license.XPackLicenseState;
|
|||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.watcher.WatcherService;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.WatcherParams;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Clock;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
/**
|
||||
* Performs the get operation.
|
||||
*/
|
||||
public class TransportGetWatchAction extends WatcherTransportAction<GetWatchRequest, GetWatchResponse> {
|
||||
|
||||
private final WatcherService watcherService;
|
||||
private final Watch.Parser parser;
|
||||
private final Clock clock;
|
||||
private final WatcherClientProxy client;
|
||||
|
||||
@Inject
|
||||
public TransportGetWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
|
||||
XPackLicenseState licenseState) {
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, XPackLicenseState licenseState,
|
||||
Watch.Parser parser, Clock clock, WatcherClientProxy client) {
|
||||
super(settings, GetWatchAction.NAME, transportService, clusterService, threadPool, actionFilters,
|
||||
indexNameExpressionResolver, licenseState, GetWatchRequest::new);
|
||||
this.watcherService = watcherService;
|
||||
this.parser = parser;
|
||||
this.clock = clock;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String executor() {
|
||||
return ThreadPool.Names.SAME; // Super lightweight operation, so don't fork
|
||||
return ThreadPool.Names.MANAGEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,32 +69,30 @@ public class TransportGetWatchAction extends WatcherTransportAction<GetWatchRequ
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Watch watch = watcherService.getWatch(request.getId());
|
||||
if (watch == null) {
|
||||
client.getWatch(request.getId(), ActionListener.wrap(getResponse -> {
|
||||
if (getResponse.isExists() == false) {
|
||||
listener.onResponse(new GetWatchResponse(request.getId()));
|
||||
return;
|
||||
}
|
||||
|
||||
try (XContentBuilder builder = jsonBuilder()) {
|
||||
// When we return the watch via the get api, we want to return the watch as was specified in the put api,
|
||||
// When we return the watch via the Get Watch REST API, we want to return the watch as was specified in the put api,
|
||||
// we don't include the status in the watch source itself, but as a separate top level field, so that
|
||||
// it indicates the the status is managed by watcher itself.
|
||||
watch.toXContent(builder, WatcherParams.builder().hideSecrets(true).build());
|
||||
BytesReference watchSource = builder.bytes();
|
||||
listener.onResponse(new GetWatchResponse(watch.id(), watch.status(), watchSource, XContentType.JSON));
|
||||
} catch (IOException e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error((Supplier<?>) () -> new ParameterizedMessage("failed to get watch [{}]", request.getId()), e);
|
||||
throw e;
|
||||
DateTime now = new DateTime(clock.millis(), UTC);
|
||||
Watch watch = parser.parseWithSecrets(request.getId(), true, getResponse.getSourceAsBytesRef(), now);
|
||||
watch.toXContent(builder, WatcherParams.builder()
|
||||
.hideSecrets(true)
|
||||
.put(Watch.INCLUDE_STATUS_KEY, false)
|
||||
.build());
|
||||
watch.version(getResponse.getVersion());
|
||||
watch.status().version(getResponse.getVersion());
|
||||
listener.onResponse(new GetWatchResponse(watch.id(), watch.status(), builder.bytes(), XContentType.JSON));
|
||||
}
|
||||
}, listener::onFailure));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(GetWatchRequest request, ClusterState state) {
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.READ, WatchStore.INDEX);
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.READ, Watch.INDEX);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,36 +8,53 @@ package org.elasticsearch.xpack.watcher.transport.actions.put;
|
|||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.DocWriteResponse;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.LicenseUtils;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.watcher.WatcherService;
|
||||
import org.elasticsearch.xpack.security.InternalClient;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerService;
|
||||
import org.elasticsearch.xpack.watcher.watch.Payload;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
public class TransportPutWatchAction extends WatcherTransportAction<PutWatchRequest, PutWatchResponse> {
|
||||
|
||||
private final WatcherService watcherService;
|
||||
private final Clock clock;
|
||||
private final TriggerService triggerService;
|
||||
private final Watch.Parser parser;
|
||||
private final InternalClient client;
|
||||
|
||||
@Inject
|
||||
public TransportPutWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
|
||||
XPackLicenseState licenseState) {
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, Clock clock, XPackLicenseState licenseState,
|
||||
TriggerService triggerService, Watch.Parser parser, InternalClient client) {
|
||||
super(settings, PutWatchAction.NAME, transportService, clusterService, threadPool, actionFilters,
|
||||
indexNameExpressionResolver, licenseState, PutWatchRequest::new);
|
||||
this.watcherService = watcherService;
|
||||
this.clock = clock;
|
||||
this.triggerService = triggerService;
|
||||
this.parser = parser;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,15 +70,30 @@ public class TransportPutWatchAction extends WatcherTransportAction<PutWatchRequ
|
|||
@Override
|
||||
protected void masterOperation(PutWatchRequest request, ClusterState state, ActionListener<PutWatchResponse> listener) throws
|
||||
ElasticsearchException {
|
||||
if (licenseState.isWatcherAllowed() == false) {
|
||||
listener.onFailure(LicenseUtils.newComplianceException(XPackPlugin.WATCHER));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
IndexResponse indexResponse = watcherService.putWatch(request.getId(), request.getSource(), request.isActive());
|
||||
DateTime now = new DateTime(clock.millis(), UTC);
|
||||
Watch watch = parser.parseWithSecrets(request.getId(), false, request.getSource(), now);
|
||||
watch.setState(request.isActive(), now);
|
||||
|
||||
try (XContentBuilder builder = jsonBuilder()) {
|
||||
Payload.XContent.MapParams params = new ToXContent.MapParams(Collections.singletonMap(Watch.INCLUDE_STATUS_KEY, "true"));
|
||||
watch.toXContent(builder, params);
|
||||
BytesReference bytesReference = builder.bytes();
|
||||
|
||||
IndexRequest indexRequest = new IndexRequest(Watch.INDEX).type(Watch.DOC_TYPE).id(request.getId());
|
||||
indexRequest.source(bytesReference);
|
||||
|
||||
client.index(indexRequest, ActionListener.wrap(indexResponse -> {
|
||||
boolean created = indexResponse.getResult() == DocWriteResponse.Result.CREATED;
|
||||
if (request.isActive()) {
|
||||
triggerService.add(watch);
|
||||
} else {
|
||||
triggerService.remove(request.getId());
|
||||
}
|
||||
listener.onResponse(new PutWatchResponse(indexResponse.getId(), indexResponse.getVersion(), created));
|
||||
}, listener::onFailure));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
|
@ -69,7 +101,7 @@ public class TransportPutWatchAction extends WatcherTransportAction<PutWatchRequ
|
|||
|
||||
@Override
|
||||
protected ClusterBlockException checkBlock(PutWatchRequest request, ClusterState state) {
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, WatchStore.INDEX);
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, Watch.INDEX);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@ package org.elasticsearch.xpack.watcher.transport.actions.stats;
|
|||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.client.Requests;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
|
@ -16,12 +18,15 @@ import org.elasticsearch.cluster.service.ClusterService;
|
|||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.elasticsearch.xpack.security.InternalClient;
|
||||
import org.elasticsearch.xpack.watcher.WatcherLifeCycleService;
|
||||
import org.elasticsearch.xpack.watcher.WatcherService;
|
||||
import org.elasticsearch.xpack.watcher.execution.ExecutionService;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.WatcherTransportAction;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
|
||||
/**
|
||||
* Performs the stats operation.
|
||||
|
@ -31,23 +36,24 @@ public class TransportWatcherStatsAction extends WatcherTransportAction<WatcherS
|
|||
private final WatcherService watcherService;
|
||||
private final ExecutionService executionService;
|
||||
private final WatcherLifeCycleService lifeCycleService;
|
||||
private final InternalClient client;
|
||||
|
||||
@Inject
|
||||
public TransportWatcherStatsAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver, WatcherService watcherService,
|
||||
ExecutionService executionService, XPackLicenseState licenseState,
|
||||
WatcherLifeCycleService lifeCycleService) {
|
||||
WatcherLifeCycleService lifeCycleService, InternalClient client) {
|
||||
super(settings, WatcherStatsAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
|
||||
licenseState, WatcherStatsRequest::new);
|
||||
this.watcherService = watcherService;
|
||||
this.executionService = executionService;
|
||||
this.lifeCycleService = lifeCycleService;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String executor() {
|
||||
// cheap operation, no need to fork into another thread
|
||||
return ThreadPool.Names.SAME;
|
||||
}
|
||||
|
||||
|
@ -59,13 +65,12 @@ public class TransportWatcherStatsAction extends WatcherTransportAction<WatcherS
|
|||
@Override
|
||||
protected void masterOperation(WatcherStatsRequest request, ClusterState state, ActionListener<WatcherStatsResponse> listener) throws
|
||||
ElasticsearchException {
|
||||
|
||||
WatcherStatsResponse statsResponse = new WatcherStatsResponse();
|
||||
statsResponse.setWatcherState(watcherService.state());
|
||||
statsResponse.setThreadPoolQueueSize(executionService.executionThreadPoolQueueSize());
|
||||
statsResponse.setWatchesCount(watcherService.watchesCount());
|
||||
statsResponse.setThreadPoolMaxSize(executionService.executionThreadPoolMaxSize());
|
||||
statsResponse.setWatcherMetaData(lifeCycleService.watcherMetaData());
|
||||
|
||||
if (request.includeCurrentWatches()) {
|
||||
statsResponse.setSnapshots(executionService.currentExecutions());
|
||||
}
|
||||
|
@ -73,7 +78,13 @@ public class TransportWatcherStatsAction extends WatcherTransportAction<WatcherS
|
|||
statsResponse.setQueuedWatches(executionService.queuedWatches());
|
||||
}
|
||||
|
||||
SearchRequest searchRequest =
|
||||
Requests.searchRequest(Watch.INDEX).types(Watch.DOC_TYPE).source(new SearchSourceBuilder().size(0));
|
||||
client.search(searchRequest, ActionListener.wrap(searchResponse -> {
|
||||
statsResponse.setWatchesCount(searchResponse.getHits().totalHits());
|
||||
listener.onResponse(statsResponse);
|
||||
},
|
||||
e -> listener.onResponse(statsResponse)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.ElasticsearchParseException;
|
|||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
@ -37,9 +38,9 @@ public class TriggerService extends AbstractComponent {
|
|||
this.engines = unmodifiableMap(builder);
|
||||
}
|
||||
|
||||
public synchronized void start(Collection<? extends TriggerEngine.Job> jobs) throws Exception {
|
||||
public synchronized void start(Collection<Watch> watches) throws Exception {
|
||||
for (TriggerEngine engine : engines.values()) {
|
||||
engine.start(jobs);
|
||||
engine.start(watches);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ package org.elasticsearch.xpack.watcher.trigger.schedule.engine;
|
|||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.xpack.scheduler.SchedulerEngine;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleRegistry;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEngine;
|
||||
|
@ -72,7 +71,7 @@ public class SchedulerScheduleTriggerEngine extends ScheduleTriggerEngine {
|
|||
final ScheduleTriggerEvent event = new ScheduleTriggerEvent(name, new DateTime(triggeredTime, UTC),
|
||||
new DateTime(scheduledTime, UTC));
|
||||
for (Listener listener : listeners) {
|
||||
listener.triggered(Collections.<TriggerEvent>singletonList(event));
|
||||
listener.triggered(Collections.singletonList(event));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ public class Watch implements TriggerEngine.Job, ToXContent {
|
|||
|
||||
public static final String ALL_ACTIONS_ID = "_all";
|
||||
public static final String INCLUDE_STATUS_KEY = "include_status";
|
||||
public static final String INDEX = ".watches";
|
||||
public static final String DOC_TYPE = "watch";
|
||||
|
||||
private final String id;
|
||||
private final Trigger trigger;
|
||||
|
@ -250,7 +252,6 @@ public class Watch implements TriggerEngine.Job, ToXContent {
|
|||
* This method is only called once - when the user adds a new watch. From that moment on, all representations
|
||||
* of the watch in the system will be use secrets for sensitive data.
|
||||
*
|
||||
* @see org.elasticsearch.xpack.watcher.WatcherService#putWatch(String, BytesReference, boolean)
|
||||
*/
|
||||
public Watch parseWithSecrets(String id, boolean includeStatus, BytesReference source, DateTime now) throws IOException {
|
||||
return parse(id, includeStatus, true, source, now);
|
||||
|
@ -317,7 +318,7 @@ public class Watch implements TriggerEngine.Job, ToXContent {
|
|||
metatdata = parser.map();
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.STATUS)) {
|
||||
if (includeStatus) {
|
||||
status = WatchStatus.parse(id, parser);
|
||||
status = WatchStatus.parse(id, parser, clock);
|
||||
} else {
|
||||
parser.skipChildren();
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.io.IOException;
|
|||
import java.time.Clock;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.unmodifiableMap;
|
||||
|
@ -37,18 +38,13 @@ import static org.joda.time.DateTimeZone.UTC;
|
|||
|
||||
public class WatchStatus implements ToXContent, Streamable {
|
||||
|
||||
public static final String INCLUDE_VERSION_KEY = "include_version";
|
||||
|
||||
private transient long version;
|
||||
|
||||
private State state;
|
||||
|
||||
@Nullable private DateTime lastChecked;
|
||||
@Nullable private DateTime lastMetCondition;
|
||||
@Nullable private long version;
|
||||
private Map<String, ActionStatus> actions;
|
||||
|
||||
private volatile boolean dirty = false;
|
||||
|
||||
// for serialization
|
||||
private WatchStatus() {
|
||||
}
|
||||
|
@ -73,14 +69,6 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
return state;
|
||||
}
|
||||
|
||||
public long version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void version(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public boolean checked() {
|
||||
return lastChecked != null;
|
||||
}
|
||||
|
@ -93,19 +81,12 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
return actions.get(actionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* marks this status as non-dirty. this should only be done when the current state of the status is in sync with
|
||||
* the persisted state.
|
||||
*/
|
||||
public void resetDirty() {
|
||||
this.dirty = false;
|
||||
public long version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return does this Watch.Status needs to be persisted to the index
|
||||
*/
|
||||
public boolean dirty() {
|
||||
return dirty;
|
||||
public void version(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -115,20 +96,15 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
|
||||
WatchStatus that = (WatchStatus) o;
|
||||
|
||||
if (version != that.version) return false;
|
||||
if (lastChecked != null ? !lastChecked.equals(that.lastChecked) : that.lastChecked != null) return false;
|
||||
if (lastMetCondition != null ? !lastMetCondition.equals(that.lastMetCondition) : that.lastMetCondition != null)
|
||||
return false;
|
||||
return !(actions != null ? !actions.equals(that.actions) : that.actions != null);
|
||||
return Objects.equals(lastChecked, that.lastChecked) &&
|
||||
Objects.equals(lastMetCondition, that.lastMetCondition) &&
|
||||
Objects.equals(version, that.version) &&
|
||||
Objects.equals(actions, that.actions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (int) (version ^ (version >>> 32));
|
||||
result = 31 * result + (lastChecked != null ? lastChecked.hashCode() : 0);
|
||||
result = 31 * result + (lastMetCondition != null ? lastMetCondition.hashCode() : 0);
|
||||
result = 31 * result + (actions != null ? actions.hashCode() : 0);
|
||||
return result;
|
||||
return Objects.hash(lastChecked, lastMetCondition, actions, version);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,7 +115,6 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
*/
|
||||
public void onCheck(boolean metCondition, DateTime timestamp) {
|
||||
lastChecked = timestamp;
|
||||
dirty = true;
|
||||
if (metCondition) {
|
||||
lastMetCondition = timestamp;
|
||||
}
|
||||
|
@ -148,7 +123,6 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
public void onActionResult(String actionId, DateTime timestamp, Action.Result result) {
|
||||
ActionStatus status = actions.get(actionId);
|
||||
status.update(timestamp, result);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,7 +147,6 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
for (ActionStatus status : actions.values()) {
|
||||
changed |= status.onAck(timestamp);
|
||||
}
|
||||
dirty |= changed;
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@ -183,14 +156,13 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
changed |= status.onAck(timestamp);
|
||||
}
|
||||
}
|
||||
dirty |= changed;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean setActive(boolean active, DateTime now) {
|
||||
boolean change = this.state.active != active;
|
||||
if (change) {
|
||||
this.dirty = true;
|
||||
this.state = new State(active, now);
|
||||
}
|
||||
return change;
|
||||
|
@ -233,9 +205,6 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
if (params.paramAsBoolean(INCLUDE_VERSION_KEY, false)) {
|
||||
builder.field(Field.VERSION.getPreferredName(), version);
|
||||
}
|
||||
builder.field(Field.STATE.getPreferredName(), state, params);
|
||||
if (lastChecked != null) {
|
||||
builder.field(Field.LAST_CHECKED.getPreferredName(), lastChecked);
|
||||
|
@ -250,14 +219,16 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
}
|
||||
builder.endObject();
|
||||
}
|
||||
builder.field(Field.VERSION.getPreferredName(), version);
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static WatchStatus parse(String watchId, XContentParser parser) throws IOException {
|
||||
public static WatchStatus parse(String watchId, XContentParser parser, Clock clock) throws IOException {
|
||||
State state = null;
|
||||
DateTime lastChecked = null;
|
||||
DateTime lastMetCondition = null;
|
||||
Map<String, ActionStatus> actions = null;
|
||||
long version = -1;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
|
@ -266,11 +237,18 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
currentFieldName = parser.currentName();
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.STATE)) {
|
||||
try {
|
||||
state = State.parse(parser);
|
||||
state = State.parse(parser, clock);
|
||||
} catch (ElasticsearchParseException e) {
|
||||
throw new ElasticsearchParseException("could not parse watch status for [{}]. failed to parse field [{}]",
|
||||
e, watchId, currentFieldName);
|
||||
}
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.VERSION)) {
|
||||
if (token.isValue()) {
|
||||
version = parser.longValue();
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not parse watch status for [{}]. expecting field [{}] to hold a long " +
|
||||
"value, found [{}] instead", watchId, currentFieldName, token);
|
||||
}
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.LAST_CHECKED)) {
|
||||
if (token.isValue()) {
|
||||
lastChecked = parseDate(currentFieldName, parser, UTC);
|
||||
|
@ -311,7 +289,7 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
}
|
||||
actions = actions == null ? emptyMap() : unmodifiableMap(actions);
|
||||
|
||||
return new WatchStatus(-1, state, lastChecked, lastMetCondition, actions);
|
||||
return new WatchStatus(version, state, lastChecked, lastMetCondition, actions);
|
||||
}
|
||||
|
||||
public static class State implements ToXContent {
|
||||
|
@ -340,12 +318,12 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static State parse(XContentParser parser) throws IOException {
|
||||
public static State parse(XContentParser parser, Clock clock) throws IOException {
|
||||
if (parser.currentToken() != XContentParser.Token.START_OBJECT) {
|
||||
throw new ElasticsearchParseException("expected an object but found [{}] instead", parser.currentToken());
|
||||
}
|
||||
boolean active = true;
|
||||
DateTime timestamp = new DateTime(Clock.systemUTC().millis(), UTC);
|
||||
DateTime timestamp = new DateTime(clock.millis(), UTC);
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
|
@ -361,13 +339,13 @@ public class WatchStatus implements ToXContent, Streamable {
|
|||
}
|
||||
}
|
||||
|
||||
interface Field {
|
||||
ParseField VERSION = new ParseField("version");
|
||||
public interface Field {
|
||||
ParseField STATE = new ParseField("state");
|
||||
ParseField ACTIVE = new ParseField("active");
|
||||
ParseField TIMESTAMP = new ParseField("timestamp");
|
||||
ParseField LAST_CHECKED = new ParseField("last_checked");
|
||||
ParseField LAST_MET_CONDITION = new ParseField("last_met_condition");
|
||||
ParseField ACTIONS = new ParseField("actions");
|
||||
ParseField VERSION = new ParseField("version");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,368 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.watcher.watch;
|
||||
|
||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||
import org.apache.logging.log4j.util.Supplier;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.delete.DeleteResponse;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.lucene.uid.Versions;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
import org.elasticsearch.index.engine.DocumentMissingException;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.sort.SortBuilders;
|
||||
import org.elasticsearch.xpack.common.stats.Counters;
|
||||
import org.elasticsearch.xpack.watcher.actions.ActionWrapper;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.Schedule;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.elasticsearch.xpack.watcher.support.Exceptions.illegalState;
|
||||
|
||||
public class WatchStore extends AbstractComponent {
|
||||
|
||||
public static final String INDEX = ".watches";
|
||||
public static final String DOC_TYPE = "watch";
|
||||
|
||||
private final WatcherClientProxy client;
|
||||
private final Watch.Parser watchParser;
|
||||
|
||||
private final ConcurrentMap<String, Watch> watches;
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
|
||||
private final int scrollSize;
|
||||
private final TimeValue scrollTimeout;
|
||||
|
||||
public WatchStore(Settings settings, WatcherClientProxy client, Watch.Parser watchParser) {
|
||||
super(settings);
|
||||
this.client = client;
|
||||
this.watchParser = watchParser;
|
||||
this.watches = ConcurrentCollections.newConcurrentMap();
|
||||
|
||||
this.scrollTimeout = settings.getAsTime("xpack.watcher.watch.scroll.timeout", TimeValue.timeValueSeconds(30));
|
||||
this.scrollSize = settings.getAsInt("xpack.watcher.watch.scroll.size", 100);
|
||||
}
|
||||
|
||||
public void start(ClusterState state) throws Exception {
|
||||
if (started.get()) {
|
||||
logger.debug("watch store already started");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
IndexMetaData indexMetaData = WatchStoreUtils.getConcreteIndex(INDEX, state.metaData());
|
||||
int count = loadWatches(indexMetaData.getNumberOfShards());
|
||||
logger.debug("loaded [{}] watches from the watches index [{}]", count, indexMetaData.getIndex().getName());
|
||||
} catch (IndexNotFoundException e) {
|
||||
} catch (Exception e) {
|
||||
logger.debug((Supplier<?>) () -> new ParameterizedMessage("failed to load watches for watch index [{}]", INDEX), e);
|
||||
watches.clear();
|
||||
throw e;
|
||||
}
|
||||
|
||||
started.set(true);
|
||||
}
|
||||
|
||||
public boolean validate(ClusterState state) {
|
||||
IndexMetaData watchesIndexMetaData;
|
||||
try {
|
||||
watchesIndexMetaData = WatchStoreUtils.getConcreteIndex(INDEX, state.metaData());
|
||||
} catch (IndexNotFoundException e) {
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
logger.trace((Supplier<?>) () -> new ParameterizedMessage("error getting index meta data [{}]: ", INDEX), e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return state.routingTable().index(watchesIndexMetaData.getIndex().getName()).allPrimaryShardsActive();
|
||||
}
|
||||
|
||||
public boolean started() {
|
||||
return started.get();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (started.compareAndSet(true, false)) {
|
||||
watches.clear();
|
||||
logger.info("stopped watch store");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the watch with the specified id otherwise <code>null</code> is returned.
|
||||
*/
|
||||
public Watch get(String id) {
|
||||
ensureStarted();
|
||||
return watches.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an watch if this watch already exists it will be overwritten
|
||||
*/
|
||||
public WatchPut put(Watch watch) throws IOException {
|
||||
ensureStarted();
|
||||
IndexRequest indexRequest = createIndexRequest(watch.id(), watch.getAsBytes(), Versions.MATCH_ANY);
|
||||
IndexResponse response = client.index(indexRequest, (TimeValue) null);
|
||||
watch.status().version(response.getVersion());
|
||||
watch.version(response.getVersion());
|
||||
Watch previous = watches.put(watch.id(), watch);
|
||||
return new WatchPut(previous, watch, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates and persists the status of the given watch
|
||||
*/
|
||||
public void updateStatus(Watch watch) throws IOException {
|
||||
ensureStarted();
|
||||
if (!watch.status().dirty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// at the moment we store the status together with the watch,
|
||||
// so we just need to update the watch itself
|
||||
XContentBuilder source = JsonXContent.contentBuilder().
|
||||
startObject()
|
||||
.field(Watch.Field.STATUS.getPreferredName(), watch.status(), ToXContent.EMPTY_PARAMS)
|
||||
.endObject();
|
||||
|
||||
UpdateRequest updateRequest = new UpdateRequest(INDEX, DOC_TYPE, watch.id());
|
||||
updateRequest.doc(source);
|
||||
updateRequest.version(watch.version());
|
||||
try {
|
||||
UpdateResponse response = client.update(updateRequest);
|
||||
watch.status().version(response.getVersion());
|
||||
watch.version(response.getVersion());
|
||||
watch.status().resetDirty();
|
||||
} catch (DocumentMissingException e) {
|
||||
// do not rethrow an exception, otherwise the watch history will contain an exception
|
||||
// even though the execution might has been fine
|
||||
logger.warn("Watch [{}] was deleted during watch execution, not updating watch status", watch.id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the watch with the specified id if exists
|
||||
*/
|
||||
public WatchDelete delete(String id) {
|
||||
ensureStarted();
|
||||
Watch watch = watches.remove(id);
|
||||
// even if the watch was not found in the watch map, we should still try to delete it
|
||||
// from the index, just to make sure we don't leave traces of it
|
||||
DeleteRequest request = new DeleteRequest(INDEX, DOC_TYPE, id);
|
||||
DeleteResponse response = client.delete(request);
|
||||
// Another operation may hold the Watch instance, so lets set the version for consistency:
|
||||
if (watch != null) {
|
||||
watch.version(response.getVersion());
|
||||
}
|
||||
return new WatchDelete(response);
|
||||
}
|
||||
|
||||
public Collection<Watch> watches() {
|
||||
return watches.values();
|
||||
}
|
||||
|
||||
public Collection<Watch> activeWatches() {
|
||||
Set<Watch> watches = new HashSet<>();
|
||||
for (Watch watch : watches()) {
|
||||
if (watch.status().state().isActive()) {
|
||||
watches.add(watch);
|
||||
}
|
||||
}
|
||||
return watches;
|
||||
}
|
||||
|
||||
public Map<String, Object> usageStats() {
|
||||
Counters counters = new Counters("count.total", "count.active");
|
||||
for (Watch watch : watches.values()) {
|
||||
boolean isActive = watch.status().state().isActive();
|
||||
addToCounters("count", isActive, counters);
|
||||
|
||||
// schedule
|
||||
if (watch.trigger() != null) {
|
||||
addToCounters("watch.trigger._all", isActive, counters);
|
||||
if ("schedule".equals(watch.trigger().type())) {
|
||||
Schedule schedule = ((ScheduleTrigger) watch.trigger()).getSchedule();
|
||||
addToCounters("watch.trigger.schedule._all", isActive, counters);
|
||||
addToCounters("watch.trigger.schedule." + schedule.type(), isActive, counters);
|
||||
}
|
||||
}
|
||||
|
||||
// input
|
||||
if (watch.input() != null) {
|
||||
String type = watch.input().type();
|
||||
addToCounters("watch.input._all", isActive, counters);
|
||||
addToCounters("watch.input." + type, isActive, counters);
|
||||
}
|
||||
|
||||
// condition
|
||||
if (watch.condition() != null) {
|
||||
String type = watch.condition().type();
|
||||
addToCounters("watch.condition._all", isActive, counters);
|
||||
addToCounters("watch.condition." + type, isActive, counters);
|
||||
}
|
||||
|
||||
// actions
|
||||
for (ActionWrapper actionWrapper : watch.actions()) {
|
||||
String type = actionWrapper.action().type();
|
||||
addToCounters("watch.action." + type, isActive, counters);
|
||||
if (actionWrapper.transform() != null) {
|
||||
String transformType = actionWrapper.transform().type();
|
||||
addToCounters("watch.transform." + transformType, isActive, counters);
|
||||
}
|
||||
}
|
||||
|
||||
// transform
|
||||
if (watch.transform() != null) {
|
||||
String type = watch.transform().type();
|
||||
addToCounters("watch.transform." + type, isActive, counters);
|
||||
}
|
||||
|
||||
// metadata
|
||||
if (watch.metadata() != null && watch.metadata().size() > 0) {
|
||||
addToCounters("watch.metadata", isActive, counters);
|
||||
}
|
||||
}
|
||||
|
||||
return counters.toMap();
|
||||
}
|
||||
|
||||
private void addToCounters(String name, boolean isActive, Counters counters) {
|
||||
counters.inc(name + ".total");
|
||||
if (isActive) {
|
||||
counters.inc(name + ".active");
|
||||
}
|
||||
}
|
||||
|
||||
IndexRequest createIndexRequest(String id, BytesReference source, long version) {
|
||||
IndexRequest indexRequest = new IndexRequest(INDEX, DOC_TYPE, id);
|
||||
indexRequest.source(BytesReference.toBytes(source));
|
||||
indexRequest.version(version);
|
||||
return indexRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* scrolls all the watch documents in the watches index, parses them, and loads them into
|
||||
* the given map.
|
||||
*/
|
||||
int loadWatches(int numPrimaryShards) {
|
||||
assert watches.isEmpty() : "no watches should reside, but there are [" + watches.size() + "] watches.";
|
||||
RefreshResponse refreshResponse = client.refresh(new RefreshRequest(INDEX));
|
||||
if (refreshResponse.getSuccessfulShards() < numPrimaryShards) {
|
||||
throw illegalState("not all required shards have been refreshed");
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
SearchRequest searchRequest = new SearchRequest(INDEX)
|
||||
.types(DOC_TYPE)
|
||||
.preference("_primary")
|
||||
.scroll(scrollTimeout)
|
||||
.source(new SearchSourceBuilder()
|
||||
.size(scrollSize)
|
||||
.sort(SortBuilders.fieldSort("_doc"))
|
||||
.version(true));
|
||||
SearchResponse response = client.search(searchRequest, null);
|
||||
try {
|
||||
if (response.getTotalShards() != response.getSuccessfulShards()) {
|
||||
throw new ElasticsearchException("Partial response while loading watches");
|
||||
}
|
||||
|
||||
while (response.getHits().hits().length != 0) {
|
||||
for (SearchHit hit : response.getHits()) {
|
||||
String id = hit.getId();
|
||||
try {
|
||||
Watch watch = watchParser.parse(id, true, hit.getSourceRef());
|
||||
watch.status().version(hit.version());
|
||||
watch.version(hit.version());
|
||||
watches.put(id, watch);
|
||||
count++;
|
||||
} catch (Exception e) {
|
||||
logger.error((Supplier<?>) () -> new ParameterizedMessage("couldn't load watch [{}], ignoring it...", id), e);
|
||||
}
|
||||
}
|
||||
response = client.searchScroll(response.getScrollId(), scrollTimeout);
|
||||
}
|
||||
} finally {
|
||||
client.clearScroll(response.getScrollId());
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private void ensureStarted() {
|
||||
if (!started.get()) {
|
||||
throw new IllegalStateException("watch store not started");
|
||||
}
|
||||
}
|
||||
|
||||
public void clearWatchesInMemory() {
|
||||
watches.clear();
|
||||
}
|
||||
|
||||
public class WatchPut {
|
||||
|
||||
private final Watch previous;
|
||||
private final Watch current;
|
||||
private final IndexResponse response;
|
||||
|
||||
public WatchPut(Watch previous, Watch current, IndexResponse response) {
|
||||
this.current = current;
|
||||
this.previous = previous;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public Watch current() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public Watch previous() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
public IndexResponse indexResponse() {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
public class WatchDelete {
|
||||
|
||||
private final DeleteResponse response;
|
||||
|
||||
public WatchDelete(DeleteResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public DeleteResponse deleteResponse() {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ public class WatchStoreUtils {
|
|||
public static IndexMetaData getConcreteIndex(String name, MetaData metaData) {
|
||||
AliasOrIndex aliasOrIndex = metaData.getAliasAndIndexLookup().get(name);
|
||||
if (aliasOrIndex == null) {
|
||||
throw new IndexNotFoundException(name);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (aliasOrIndex.isAlias() && aliasOrIndex.getIndices().size() > 1) {
|
||||
|
|
|
@ -95,6 +95,7 @@ public class LatchScriptEngine implements ScriptEngineService {
|
|||
}
|
||||
|
||||
public static class LatchScriptPlugin extends Plugin implements ScriptPlugin {
|
||||
|
||||
@Override
|
||||
public ScriptEngineService getScriptEngineService(Settings settings) {
|
||||
return INSTANCE;
|
||||
|
|
|
@ -19,7 +19,7 @@ import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchRes
|
|||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.After;
|
||||
|
@ -84,7 +84,7 @@ public class EmailSecretsIntegrationTests extends AbstractWatcherIntegrationTest
|
|||
.get();
|
||||
|
||||
// verifying the email password is stored encrypted in the index
|
||||
GetResponse response = client().prepareGet(WatchStore.INDEX, WatchStore.DOC_TYPE, "_id").get();
|
||||
GetResponse response = client().prepareGet(Watch.INDEX, Watch.DOC_TYPE, "_id").get();
|
||||
assertThat(response, notNullValue());
|
||||
assertThat(response.getId(), is("_id"));
|
||||
Map<String, Object> source = response.getSource();
|
||||
|
|
|
@ -21,7 +21,7 @@ import org.elasticsearch.common.util.concurrent.EsExecutors;
|
|||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.junit.Before;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
@ -177,7 +177,7 @@ public class WatcherLifeCycleServiceTests extends ESTestCase {
|
|||
// old cluster state that contains watcher index
|
||||
Settings indexSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build();
|
||||
ClusterState oldClusterState = ClusterState.builder(new ClusterName("my-cluster"))
|
||||
.metaData(new MetaData.Builder().put(IndexMetaData.builder(WatchStore.INDEX)
|
||||
.metaData(new MetaData.Builder().put(IndexMetaData.builder(Watch.INDEX)
|
||||
.settings(indexSettings).numberOfReplicas(0).numberOfShards(1)))
|
||||
.nodes(discoveryNodes).build();
|
||||
|
||||
|
@ -196,13 +196,13 @@ public class WatcherLifeCycleServiceTests extends ESTestCase {
|
|||
// old cluster state that contains watcher index
|
||||
Settings indexSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build();
|
||||
ClusterState oldClusterState = ClusterState.builder(new ClusterName("my-cluster"))
|
||||
.metaData(new MetaData.Builder().put(IndexMetaData.builder(WatchStore.INDEX)
|
||||
.metaData(new MetaData.Builder().put(IndexMetaData.builder(Watch.INDEX)
|
||||
.settings(indexSettings).numberOfReplicas(0).numberOfShards(1)))
|
||||
.nodes(discoveryNodes).build();
|
||||
|
||||
// new cluster state with a closed watcher index
|
||||
ClusterState newClusterState = ClusterState.builder(new ClusterName("my-cluster"))
|
||||
.metaData(new MetaData.Builder().put(IndexMetaData.builder(WatchStore.INDEX).state(IndexMetaData.State.CLOSE)
|
||||
.metaData(new MetaData.Builder().put(IndexMetaData.builder(Watch.INDEX).state(IndexMetaData.State.CLOSE)
|
||||
.settings(indexSettings).numberOfReplicas(0).numberOfShards(1)))
|
||||
.nodes(discoveryNodes).build();
|
||||
when(watcherService.state()).thenReturn(WatcherState.STARTED);
|
||||
|
|
|
@ -1,307 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.watcher;
|
||||
|
||||
import org.elasticsearch.action.DocWriteResponse;
|
||||
import org.elasticsearch.action.delete.DeleteResponse;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.support.clock.ClockMock;
|
||||
import org.elasticsearch.xpack.watcher.execution.ExecutionService;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry;
|
||||
import org.elasticsearch.xpack.watcher.trigger.Trigger;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerEngine;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerService;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchLockService;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStatus;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class WatcherServiceTests extends ESTestCase {
|
||||
private TriggerService triggerService;
|
||||
private WatchStore watchStore;
|
||||
private Watch.Parser watchParser;
|
||||
private WatcherService watcherService;
|
||||
private ClockMock clock;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
triggerService = mock(TriggerService.class);
|
||||
watchStore = mock(WatchStore.class);
|
||||
watchParser = mock(Watch.Parser.class);
|
||||
ExecutionService executionService = mock(ExecutionService.class);
|
||||
WatchLockService watchLockService = mock(WatchLockService.class);
|
||||
clock = ClockMock.frozen();
|
||||
WatcherIndexTemplateRegistry watcherIndexTemplateRegistry = mock(WatcherIndexTemplateRegistry.class);
|
||||
watcherService = new WatcherService(Settings.EMPTY, clock, triggerService, watchStore, watchParser, executionService,
|
||||
watchLockService, watcherIndexTemplateRegistry);
|
||||
AtomicReference<WatcherState> state = watcherService.state;
|
||||
state.set(WatcherState.STARTED);
|
||||
}
|
||||
|
||||
public void testPutWatch() throws Exception {
|
||||
boolean activeByDefault = randomBoolean();
|
||||
|
||||
IndexResponse indexResponse = mock(IndexResponse.class);
|
||||
Watch newWatch = mock(Watch.class);
|
||||
WatchStatus status = mock(WatchStatus.class);
|
||||
when(status.state()).thenReturn(new WatchStatus.State(activeByDefault, new DateTime(clock.millis(), UTC)));
|
||||
when(newWatch.status()).thenReturn(status);
|
||||
|
||||
WatchStore.WatchPut watchPut = mock(WatchStore.WatchPut.class);
|
||||
when(watchPut.indexResponse()).thenReturn(indexResponse);
|
||||
when(watchPut.current()).thenReturn(newWatch);
|
||||
|
||||
when(watchParser.parseWithSecrets(any(String.class), eq(false), any(BytesReference.class), any(DateTime.class)))
|
||||
.thenReturn(newWatch);
|
||||
when(watchStore.put(newWatch)).thenReturn(watchPut);
|
||||
IndexResponse response = watcherService.putWatch("_id", new BytesArray("{}"), activeByDefault);
|
||||
assertThat(response, sameInstance(indexResponse));
|
||||
|
||||
verify(newWatch, times(1)).setState(activeByDefault, new DateTime(clock.millis(), UTC));
|
||||
if (activeByDefault) {
|
||||
verify(triggerService, times(1)).add(any(TriggerEngine.Job.class));
|
||||
} else {
|
||||
verifyZeroInteractions(triggerService);
|
||||
}
|
||||
}
|
||||
|
||||
public void testPutWatchDifferentActiveStates() throws Exception {
|
||||
Trigger trigger = mock(Trigger.class);
|
||||
|
||||
IndexResponse indexResponse = mock(IndexResponse.class);
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
WatchStatus status = mock(WatchStatus.class);
|
||||
boolean active = randomBoolean();
|
||||
DateTime now = new DateTime(clock.millis(), UTC);
|
||||
when(status.state()).thenReturn(new WatchStatus.State(active, now));
|
||||
when(watch.status()).thenReturn(status);
|
||||
when(watch.trigger()).thenReturn(trigger);
|
||||
WatchStore.WatchPut watchPut = mock(WatchStore.WatchPut.class);
|
||||
when(watchPut.indexResponse()).thenReturn(indexResponse);
|
||||
when(watchPut.current()).thenReturn(watch);
|
||||
|
||||
Watch previousWatch = mock(Watch.class);
|
||||
WatchStatus previousStatus = mock(WatchStatus.class);
|
||||
boolean prevActive = randomBoolean();
|
||||
when(previousStatus.state()).thenReturn(new WatchStatus.State(prevActive, now));
|
||||
when(previousWatch.status()).thenReturn(previousStatus);
|
||||
when(previousWatch.trigger()).thenReturn(trigger);
|
||||
when(watchPut.previous()).thenReturn(previousWatch);
|
||||
|
||||
when(watchParser.parseWithSecrets(any(String.class), eq(false), any(BytesReference.class), eq(now))).thenReturn(watch);
|
||||
when(watchStore.put(watch)).thenReturn(watchPut);
|
||||
|
||||
IndexResponse response = watcherService.putWatch("_id", new BytesArray("{}"), active);
|
||||
assertThat(response, sameInstance(indexResponse));
|
||||
|
||||
if (!active) {
|
||||
// we should always remove the watch from the trigger service, just to be safe
|
||||
verify(triggerService, times(1)).remove("_id");
|
||||
} else if (prevActive) {
|
||||
// if both the new watch and the prev one are active, we should do nothing
|
||||
verifyZeroInteractions(triggerService);
|
||||
} else {
|
||||
// if the prev watch was not active and the new one is active, we should add the watch
|
||||
verify(triggerService, times(1)).add(watch);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDeleteWatch() throws Exception {
|
||||
WatchStore.WatchDelete expectedWatchDelete = mock(WatchStore.WatchDelete.class);
|
||||
DeleteResponse deleteResponse = mock(DeleteResponse.class);
|
||||
when(deleteResponse.getResult()).thenReturn(DocWriteResponse.Result.DELETED);
|
||||
when(expectedWatchDelete.deleteResponse()).thenReturn(deleteResponse);
|
||||
when(watchStore.delete("_id")).thenReturn(expectedWatchDelete);
|
||||
WatchStore.WatchDelete watchDelete = watcherService.deleteWatch("_id");
|
||||
|
||||
assertThat(watchDelete, sameInstance(expectedWatchDelete));
|
||||
verify(triggerService, times(1)).remove("_id");
|
||||
}
|
||||
|
||||
public void testDeleteWatchNotFound() throws Exception {
|
||||
WatchStore.WatchDelete expectedWatchDelete = mock(WatchStore.WatchDelete.class);
|
||||
DeleteResponse deleteResponse = mock(DeleteResponse.class);
|
||||
when(deleteResponse.getResult()).thenReturn(DocWriteResponse.Result.NOOP);
|
||||
when(expectedWatchDelete.deleteResponse()).thenReturn(deleteResponse);
|
||||
when(watchStore.delete("_id")).thenReturn(expectedWatchDelete);
|
||||
WatchStore.WatchDelete watchDelete = watcherService.deleteWatch("_id");
|
||||
|
||||
assertThat(watchDelete, sameInstance(expectedWatchDelete));
|
||||
verifyZeroInteractions(triggerService);
|
||||
}
|
||||
|
||||
public void testAckWatch() throws Exception {
|
||||
DateTime now = new DateTime(UTC);
|
||||
clock.setTime(now);
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.ack(now, "_all")).thenReturn(true);
|
||||
WatchStatus status = new WatchStatus(now, emptyMap());
|
||||
when(watch.status()).thenReturn(status);
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
|
||||
WatchStatus result = watcherService.ackWatch("_id", Strings.EMPTY_ARRAY);
|
||||
assertThat(result, not(sameInstance(status)));
|
||||
|
||||
verify(watchStore, times(1)).updateStatus(watch);
|
||||
}
|
||||
|
||||
public void testActivate() throws Exception {
|
||||
WatcherService service = spy(watcherService);
|
||||
WatchStatus expectedStatus = mock(WatchStatus.class);
|
||||
doReturn(expectedStatus).when(service).setWatchState("_id", true);
|
||||
WatchStatus actualStatus = service.activateWatch("_id");
|
||||
assertThat(actualStatus, sameInstance(expectedStatus));
|
||||
verify(service, times(1)).setWatchState("_id", true);
|
||||
}
|
||||
|
||||
public void testDeactivate() throws Exception {
|
||||
WatcherService service = spy(watcherService);
|
||||
WatchStatus expectedStatus = mock(WatchStatus.class);
|
||||
doReturn(expectedStatus).when(service).setWatchState("_id", false);
|
||||
WatchStatus actualStatus = service.deactivateWatch("_id");
|
||||
assertThat(actualStatus, sameInstance(expectedStatus));
|
||||
verify(service, times(1)).setWatchState("_id", false);
|
||||
}
|
||||
|
||||
public void testSetWatchStateSetActiveOnCurrentlyActive() throws Exception {
|
||||
// trying to activate a watch that is already active:
|
||||
// - the watch status should not change
|
||||
// - the watch doesn't need to be updated in the store
|
||||
// - the watch should not be removed or re-added to the trigger service
|
||||
DateTime now = new DateTime(UTC);
|
||||
clock.setTime(now);
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
WatchStatus status = new WatchStatus(now, emptyMap());
|
||||
when(watch.status()).thenReturn(status);
|
||||
when(watch.setState(true, now)).thenReturn(false);
|
||||
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
|
||||
|
||||
WatchStatus result = watcherService.setWatchState("_id", true);
|
||||
assertThat(result, not(sameInstance(status)));
|
||||
|
||||
verifyZeroInteractions(triggerService);
|
||||
verify(watchStore, never()).updateStatus(watch);
|
||||
}
|
||||
|
||||
public void testSetWatchStateSetActiveOnCurrentlyInactive() throws Exception {
|
||||
// activating a watch that is currently inactive:
|
||||
// - the watch status should be updated
|
||||
// - the watch needs to be updated in the store
|
||||
// - the watch should be re-added to the trigger service (the assumption is that it's not there)
|
||||
|
||||
DateTime now = new DateTime(UTC);
|
||||
clock.setTime(now);
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
WatchStatus status = new WatchStatus(now, emptyMap());
|
||||
when(watch.status()).thenReturn(status);
|
||||
when(watch.setState(true, now)).thenReturn(true);
|
||||
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
|
||||
WatchStatus result = watcherService.setWatchState("_id", true);
|
||||
assertThat(result, not(sameInstance(status)));
|
||||
|
||||
verify(triggerService, times(1)).add(watch);
|
||||
verify(watchStore, times(1)).updateStatus(watch);
|
||||
}
|
||||
|
||||
public void testSetWatchStateSetInactiveOnCurrentlyActive() throws Exception {
|
||||
// deactivating a watch that is currently active:
|
||||
// - the watch status should change
|
||||
// - the watch needs to be updated in the store
|
||||
// - the watch should be removed from the trigger service
|
||||
DateTime now = new DateTime(UTC);
|
||||
clock.setTime(now);
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
WatchStatus status = new WatchStatus(now, emptyMap());
|
||||
when(watch.status()).thenReturn(status);
|
||||
when(watch.setState(false, now)).thenReturn(true);
|
||||
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
|
||||
WatchStatus result = watcherService.setWatchState("_id", false);
|
||||
assertThat(result, not(sameInstance(status)));
|
||||
|
||||
verify(triggerService, times(1)).remove("_id");
|
||||
verify(watchStore, times(1)).updateStatus(watch);
|
||||
}
|
||||
|
||||
public void testSetWatchStateSetInactiveOnCurrentlyInactive() throws Exception {
|
||||
// trying to deactivate a watch that is currently inactive:
|
||||
// - the watch status should not be updated
|
||||
// - the watch should not be updated in the store
|
||||
// - the watch should be re-added or removed to/from the trigger service
|
||||
DateTime now = new DateTime(UTC);
|
||||
clock.setTime(now);
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
WatchStatus status = new WatchStatus(now, emptyMap());
|
||||
when(watch.status()).thenReturn(status);
|
||||
when(watch.setState(false, now)).thenReturn(false);
|
||||
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
|
||||
WatchStatus result = watcherService.setWatchState("_id", false);
|
||||
assertThat(result, not(sameInstance(status)));
|
||||
|
||||
verifyZeroInteractions(triggerService);
|
||||
verify(watchStore, never()).updateStatus(watch);
|
||||
}
|
||||
|
||||
public void testAckWatchNotAck() throws Exception {
|
||||
DateTime now = new DateTime(Clock.systemUTC().millis(), UTC);
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.ack(now)).thenReturn(false);
|
||||
WatchStatus status = new WatchStatus(now, emptyMap());
|
||||
when(watch.status()).thenReturn(status);
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
|
||||
WatchStatus result = watcherService.ackWatch("_id", Strings.EMPTY_ARRAY);
|
||||
assertThat(result, not(sameInstance(status)));
|
||||
|
||||
verify(watchStore, never()).updateStatus(watch);
|
||||
}
|
||||
|
||||
public void testAckWatchNoWatch() throws Exception {
|
||||
when(watchStore.get("_id")).thenReturn(null);
|
||||
expectThrows(IllegalArgumentException.class, () -> watcherService.ackWatch("_id", Strings.EMPTY_ARRAY));
|
||||
verify(watchStore, never()).updateStatus(any(Watch.class));
|
||||
}
|
||||
}
|
|
@ -52,14 +52,11 @@ public class ActionErrorIntegrationTests extends AbstractWatcherIntegrationTestC
|
|||
flush();
|
||||
|
||||
// there should be a single history record with a failure status for the action:
|
||||
assertBusy(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertBusy(() -> {
|
||||
long count = watchRecordCount(QueryBuilders.boolQuery()
|
||||
.must(termsQuery("result.actions.id", "_action"))
|
||||
.must(termsQuery("result.actions.status", "failure")));
|
||||
assertThat(count, is(1L));
|
||||
}
|
||||
});
|
||||
|
||||
// now we'll trigger the watch again and make sure that it's not throttled and instead
|
||||
|
@ -72,14 +69,11 @@ public class ActionErrorIntegrationTests extends AbstractWatcherIntegrationTestC
|
|||
flush();
|
||||
|
||||
// there should be a single history record with a failure status for the action:
|
||||
assertBusy(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertBusy(() -> {
|
||||
long count = watchRecordCount(QueryBuilders.boolQuery()
|
||||
.must(termsQuery("result.actions.id", "_action"))
|
||||
.must(termsQuery("result.actions.status", "failure")));
|
||||
assertThat(count, is(2L));
|
||||
}
|
||||
});
|
||||
|
||||
// now lets confirm that the ack status of the action is awaits_successful_execution
|
||||
|
|
|
@ -7,11 +7,11 @@ package org.elasticsearch.xpack.watcher.actions.throttler;
|
|||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.xpack.common.http.HttpMethod;
|
||||
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
|
||||
import org.elasticsearch.xpack.common.text.TextTemplate;
|
||||
import org.elasticsearch.xpack.notification.email.EmailTemplate;
|
||||
import org.elasticsearch.xpack.watcher.actions.Action;
|
||||
import org.elasticsearch.xpack.watcher.actions.ActionWrapper;
|
||||
import org.elasticsearch.xpack.watcher.actions.email.EmailAction;
|
||||
import org.elasticsearch.xpack.watcher.actions.index.IndexAction;
|
||||
import org.elasticsearch.xpack.watcher.actions.logging.LoggingAction;
|
||||
|
@ -19,24 +19,23 @@ import org.elasticsearch.xpack.watcher.actions.webhook.WebhookAction;
|
|||
import org.elasticsearch.xpack.watcher.client.WatchSourceBuilder;
|
||||
import org.elasticsearch.xpack.watcher.execution.ActionExecutionMode;
|
||||
import org.elasticsearch.xpack.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.xpack.watcher.execution.ManualExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.ObjectPath;
|
||||
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchRequestBuilder;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchRequest;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchRequest;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.trigger.manual.ManualTriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.IntervalSchedule;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Clock;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -47,12 +46,15 @@ import static org.elasticsearch.xpack.watcher.client.WatchSourceBuilders.watchBu
|
|||
import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule;
|
||||
import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.interval;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
|
||||
public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
||||
public void testSingleActionAckThrottle() throws Exception {
|
||||
boolean useClientForAcking = randomBoolean();
|
||||
|
||||
@Override
|
||||
protected boolean timeWarped() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void testSingleActionAckThrottle() throws Exception {
|
||||
WatchSourceBuilder watchSourceBuilder = watchBuilder()
|
||||
.trigger(schedule(interval("60m")));
|
||||
|
||||
|
@ -60,38 +62,37 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
Action.Builder action = availableAction.action();
|
||||
watchSourceBuilder.addAction("test_id", action);
|
||||
|
||||
PutWatchResponse putWatchResponse = watcherClient().putWatch(new PutWatchRequest("_id", watchSourceBuilder)).actionGet();
|
||||
assertThat(putWatchResponse.getVersion(), greaterThan(0L));
|
||||
refresh();
|
||||
watcherClient().putWatch(new PutWatchRequest("_id", watchSourceBuilder)).actionGet();
|
||||
refresh(Watch.INDEX);
|
||||
|
||||
assertThat(watcherClient().prepareGetWatch("_id").get().isFound(), equalTo(true));
|
||||
ManualExecutionContext ctx = getManualExecutionContext(new TimeValue(0, TimeUnit.SECONDS));
|
||||
WatchRecord watchRecord = executionService().execute(ctx);
|
||||
ExecuteWatchRequestBuilder executeWatchRequestBuilder = watcherClient().prepareExecuteWatch("_id")
|
||||
.setRecordExecution(true)
|
||||
.setActionMode("test_id", ActionExecutionMode.SIMULATE);
|
||||
|
||||
Map<String, Object> responseMap = executeWatchRequestBuilder.get().getRecordSource().getAsMap();
|
||||
String status = ObjectPath.eval("result.actions.0.status", responseMap);
|
||||
assertThat(status, equalTo(Action.Result.Status.SIMULATED.toString().toLowerCase(Locale.ROOT)));
|
||||
|
||||
timeWarp().clock().fastForward(TimeValue.timeValueSeconds(15));
|
||||
|
||||
assertThat(watchRecord.result().actionsResults().get("test_id").action().status(), equalTo(Action.Result.Status.SIMULATED));
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForward(TimeValue.timeValueSeconds(1));
|
||||
}
|
||||
boolean ack = randomBoolean();
|
||||
if (ack) {
|
||||
if (useClientForAcking) {
|
||||
watcherClient().prepareAckWatch("_id").setActionIds("test_id").get();
|
||||
} else {
|
||||
watchService().ackWatch("_id", new String[] { "test_id" });
|
||||
}
|
||||
}
|
||||
ctx = getManualExecutionContext(new TimeValue(0, TimeUnit.SECONDS));
|
||||
watchRecord = executionService().execute(ctx);
|
||||
|
||||
executeWatchRequestBuilder = watcherClient().prepareExecuteWatch("_id")
|
||||
.setRecordExecution(true)
|
||||
.setActionMode("test_id", ActionExecutionMode.SIMULATE);
|
||||
responseMap = executeWatchRequestBuilder.get().getRecordSource().getAsMap();
|
||||
status = ObjectPath.eval("result.actions.0.status", responseMap);
|
||||
if (ack) {
|
||||
assertThat(watchRecord.result().actionsResults().get("test_id").action().status(), equalTo(Action.Result.Status.THROTTLED));
|
||||
assertThat(status, equalTo(Action.Result.Status.THROTTLED.toString().toLowerCase(Locale.ROOT)));
|
||||
} else {
|
||||
assertThat(watchRecord.result().actionsResults().get("test_id").action().status(), equalTo(Action.Result.Status.SIMULATED));
|
||||
assertThat(status, equalTo(Action.Result.Status.SIMULATED.toString().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRandomMultiActionAckThrottle() throws Exception {
|
||||
boolean useClientForAcking = randomBoolean();
|
||||
|
||||
WatchSourceBuilder watchSourceBuilder = watchBuilder()
|
||||
.trigger(schedule(interval("60m")));
|
||||
|
||||
|
@ -105,38 +106,35 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
PutWatchResponse putWatchResponse = watcherClient().putWatch(new PutWatchRequest("_id", watchSourceBuilder)).actionGet();
|
||||
assertThat(putWatchResponse.getVersion(), greaterThan(0L));
|
||||
refresh();
|
||||
|
||||
assertThat(watcherClient().getWatch(new GetWatchRequest("_id")).actionGet().isFound(), equalTo(true));
|
||||
ManualExecutionContext ctx = getManualExecutionContext(new TimeValue(0, TimeUnit.SECONDS));
|
||||
executionService().execute(ctx);
|
||||
watcherClient().putWatch(new PutWatchRequest("_id", watchSourceBuilder)).actionGet();
|
||||
refresh(Watch.INDEX);
|
||||
executeWatch("_id");
|
||||
|
||||
for (String actionId : ackingActions) {
|
||||
if (useClientForAcking) {
|
||||
watcherClient().prepareAckWatch("_id").setActionIds(actionId).get();
|
||||
}
|
||||
|
||||
timeWarp().clock().fastForwardSeconds(15);
|
||||
|
||||
Map<String, Object> responseMap = executeWatch("_id");
|
||||
List<Map<String, String>> actions = ObjectPath.eval("result.actions", responseMap);
|
||||
for (Map<String, String> result : actions) {
|
||||
if (ackingActions.contains(result.get("id"))) {
|
||||
assertThat(result.get("status"), equalTo(Action.Result.Status.THROTTLED.toString().toLowerCase(Locale.ROOT)));
|
||||
} else {
|
||||
watchService().ackWatch("_id", new String[]{actionId});
|
||||
assertThat(result.get("status"), equalTo(Action.Result.Status.SIMULATED.toString().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
}
|
||||
|
||||
ctx = getManualExecutionContext(new TimeValue(0, TimeUnit.SECONDS));
|
||||
WatchRecord watchRecord = executionService().execute(ctx);
|
||||
for (ActionWrapper.Result result : watchRecord.result().actionsResults().values()) {
|
||||
if (ackingActions.contains(result.id())) {
|
||||
assertThat(result.action().status(), equalTo(Action.Result.Status.THROTTLED));
|
||||
} else {
|
||||
assertThat(result.action().status(), equalTo(Action.Result.Status.SIMULATED));
|
||||
}
|
||||
}
|
||||
private Map<String, Object> executeWatch(String id) {
|
||||
return watcherClient().prepareExecuteWatch(id)
|
||||
.setRecordExecution(true)
|
||||
.setActionMode("_all", ActionExecutionMode.SIMULATE).get().getRecordSource().getAsMap();
|
||||
}
|
||||
|
||||
public void testDifferentThrottlePeriods() throws Exception {
|
||||
timeWarp().clock().setTime(DateTime.now(DateTimeZone.UTC));
|
||||
WatchSourceBuilder watchSourceBuilder = watchBuilder()
|
||||
.trigger(schedule(interval("60m")));
|
||||
|
||||
|
@ -145,47 +143,35 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
watchSourceBuilder.addAction("fifteen_sec_throttle", new TimeValue(15, TimeUnit.SECONDS),
|
||||
randomFrom(AvailableAction.values()).action());
|
||||
|
||||
PutWatchResponse putWatchResponse = watcherClient().putWatch(new PutWatchRequest("_id", watchSourceBuilder)).actionGet();
|
||||
assertThat(putWatchResponse.getVersion(), greaterThan(0L));
|
||||
refresh();
|
||||
assertThat(watcherClient().getWatch(new GetWatchRequest("_id")).actionGet().isFound(), equalTo(true));
|
||||
watcherClient().putWatch(new PutWatchRequest("_id", watchSourceBuilder)).actionGet();
|
||||
refresh(Watch.INDEX);
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().setTime(new DateTime(DateTimeZone.UTC));
|
||||
timeWarp().clock().fastForwardSeconds(1);
|
||||
Map<String, Object> responseMap = executeWatch("_id");
|
||||
List<Map<String, String>> actions = ObjectPath.eval("result.actions", responseMap);
|
||||
for (Map<String, String> result : actions) {
|
||||
assertThat(result.get("status"), equalTo(Action.Result.Status.SIMULATED.toString().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
timeWarp().clock().fastForwardSeconds(1);
|
||||
|
||||
responseMap = executeWatch("_id");
|
||||
actions = ObjectPath.eval("result.actions", responseMap);
|
||||
for (Map<String, String> result : actions) {
|
||||
assertThat(result.get("status"), equalTo(Action.Result.Status.THROTTLED.toString().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
|
||||
ManualExecutionContext ctx = getManualExecutionContext(new TimeValue(0, TimeUnit.SECONDS));
|
||||
WatchRecord watchRecord = executionService().execute(ctx);
|
||||
long firstExecution = System.currentTimeMillis();
|
||||
for(ActionWrapper.Result actionResult : watchRecord.result().actionsResults().values()) {
|
||||
assertThat(actionResult.action().status(), equalTo(Action.Result.Status.SIMULATED));
|
||||
}
|
||||
ctx = getManualExecutionContext(new TimeValue(0, TimeUnit.SECONDS));
|
||||
watchRecord = executionService().execute(ctx);
|
||||
for(ActionWrapper.Result actionResult : watchRecord.result().actionsResults().values()) {
|
||||
assertThat(actionResult.action().status(), equalTo(Action.Result.Status.THROTTLED));
|
||||
}
|
||||
timeWarp().clock().fastForwardSeconds(10);
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(11);
|
||||
}
|
||||
|
||||
assertBusy(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ManualExecutionContext ctx = getManualExecutionContext(new TimeValue(0, TimeUnit.SECONDS));
|
||||
WatchRecord watchRecord = executionService().execute(ctx);
|
||||
for (ActionWrapper.Result actionResult : watchRecord.result().actionsResults().values()) {
|
||||
if ("ten_sec_throttle".equals(actionResult.id())) {
|
||||
assertThat(actionResult.action().status(), equalTo(Action.Result.Status.SIMULATED));
|
||||
responseMap = executeWatch("_id");
|
||||
actions = ObjectPath.eval("result.actions", responseMap);
|
||||
for (Map<String, String> result : actions) {
|
||||
if ("ten_sec_throttle".equals(result.get("id"))) {
|
||||
assertThat(result.get("status"), equalTo(Action.Result.Status.SIMULATED.toString().toLowerCase(Locale.ROOT)));
|
||||
} else {
|
||||
assertThat(actionResult.action().status(), equalTo(Action.Result.Status.THROTTLED));
|
||||
assertThat(result.get("status"), equalTo(Action.Result.Status.THROTTLED.toString().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 11000 - (System.currentTimeMillis() - firstExecution), TimeUnit.MILLISECONDS);
|
||||
|
||||
}
|
||||
|
||||
public void testDefaultThrottlePeriod() throws Exception {
|
||||
WatchSourceBuilder watchSourceBuilder = watchBuilder()
|
||||
|
@ -194,9 +180,8 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
AvailableAction availableAction = randomFrom(AvailableAction.values());
|
||||
watchSourceBuilder.addAction("default_global_throttle", availableAction.action());
|
||||
|
||||
PutWatchResponse putWatchResponse = watcherClient().putWatch(new PutWatchRequest("_id", watchSourceBuilder)).actionGet();
|
||||
assertThat(putWatchResponse.getVersion(), greaterThan(0L));
|
||||
refresh();
|
||||
watcherClient().putWatch(new PutWatchRequest("_id", watchSourceBuilder)).actionGet();
|
||||
refresh(Watch.INDEX);
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().setTime(new DateTime(DateTimeZone.UTC));
|
||||
|
@ -208,9 +193,9 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setActionMode("default_global_throttle", ActionExecutionMode.SIMULATE)
|
||||
.setRecordExecution(true)
|
||||
.get();
|
||||
Map<String, Object> watchRecordMap = executeWatchResponse.getRecordSource().getAsMap();
|
||||
Object resultStatus = getExecutionStatus(watchRecordMap);
|
||||
assertThat(resultStatus.toString(), equalTo("simulated"));
|
||||
|
||||
String status = ObjectPath.eval("result.actions.0.status", executeWatchResponse.getRecordSource().getAsMap());
|
||||
assertThat(status, equalTo("simulated"));
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(1);
|
||||
|
@ -222,31 +207,26 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setActionMode("default_global_throttle", ActionExecutionMode.SIMULATE)
|
||||
.setRecordExecution(true)
|
||||
.get();
|
||||
watchRecordMap = executeWatchResponse.getRecordSource().getAsMap();
|
||||
resultStatus = getExecutionStatus(watchRecordMap);
|
||||
assertThat(resultStatus.toString(), equalTo("throttled"));
|
||||
status = ObjectPath.eval("result.actions.0.status", executeWatchResponse.getRecordSource().getAsMap());
|
||||
assertThat(status, equalTo("throttled"));
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(5);
|
||||
}
|
||||
|
||||
assertBusy(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertBusy(() -> {
|
||||
try {
|
||||
ExecuteWatchResponse executeWatchResponse = watcherClient().prepareExecuteWatch("_id")
|
||||
ExecuteWatchResponse executeWatchResponse1 = watcherClient().prepareExecuteWatch("_id")
|
||||
.setTriggerEvent(new ManualTriggerEvent("execute_id",
|
||||
new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC))))
|
||||
.setActionMode("default_global_throttle", ActionExecutionMode.SIMULATE)
|
||||
.setRecordExecution(true)
|
||||
.get();
|
||||
Map<String, Object> watchRecordMap = executeWatchResponse.getRecordSource().getAsMap();
|
||||
Object resultStatus = getExecutionStatus(watchRecordMap);
|
||||
assertThat(resultStatus.toString(), equalTo("simulated"));
|
||||
String currentStatus = ObjectPath.eval("result.actions.0.status", executeWatchResponse1.getRecordSource().getAsMap());
|
||||
assertThat(currentStatus, equalTo("simulated"));
|
||||
} catch (IOException ioe) {
|
||||
throw new ElasticsearchException("failed to execute", ioe);
|
||||
}
|
||||
}
|
||||
}, 6, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
@ -258,9 +238,8 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
AvailableAction availableAction = randomFrom(AvailableAction.values());
|
||||
watchSourceBuilder.addAction("default_global_throttle", availableAction.action());
|
||||
|
||||
PutWatchResponse putWatchResponse = watcherClient().putWatch(new PutWatchRequest("_id", watchSourceBuilder)).actionGet();
|
||||
assertThat(putWatchResponse.getVersion(), greaterThan(0L));
|
||||
refresh();
|
||||
watcherClient().putWatch(new PutWatchRequest("_id", watchSourceBuilder)).actionGet();
|
||||
refresh(Watch.INDEX);
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().setTime(new DateTime(DateTimeZone.UTC));
|
||||
|
@ -272,9 +251,8 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setActionMode("default_global_throttle", ActionExecutionMode.SIMULATE)
|
||||
.setRecordExecution(true)
|
||||
.get();
|
||||
Map<String, Object> watchRecordMap = executeWatchResponse.getRecordSource().getAsMap();
|
||||
Object resultStatus = getExecutionStatus(watchRecordMap);
|
||||
assertThat(resultStatus.toString(), equalTo("simulated"));
|
||||
String status = ObjectPath.eval("result.actions.0.status", executeWatchResponse.getRecordSource().getAsMap());
|
||||
assertThat(status, equalTo("simulated"));
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(1);
|
||||
|
@ -286,38 +264,33 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setActionMode("default_global_throttle", ActionExecutionMode.SIMULATE)
|
||||
.setRecordExecution(true)
|
||||
.get();
|
||||
watchRecordMap = executeWatchResponse.getRecordSource().getAsMap();
|
||||
resultStatus = getExecutionStatus(watchRecordMap);
|
||||
assertThat(resultStatus.toString(), equalTo("throttled"));
|
||||
status = ObjectPath.eval("result.actions.0.status", executeWatchResponse.getRecordSource().getAsMap());
|
||||
assertThat(status, equalTo("throttled"));
|
||||
|
||||
if (timeWarped()) {
|
||||
timeWarp().clock().fastForwardSeconds(20);
|
||||
}
|
||||
assertBusy(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertBusy(() -> {
|
||||
try {
|
||||
//Since the default throttle period is 5 seconds but we have overridden the period in the watch this should trigger
|
||||
ExecuteWatchResponse executeWatchResponse = watcherClient().prepareExecuteWatch("_id")
|
||||
ExecuteWatchResponse executeWatchResponse1 = watcherClient().prepareExecuteWatch("_id")
|
||||
.setTriggerEvent(new ManualTriggerEvent("execute_id",
|
||||
new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC))))
|
||||
.setActionMode("default_global_throttle", ActionExecutionMode.SIMULATE)
|
||||
.setRecordExecution(true)
|
||||
.get();
|
||||
Map<String, Object> watchRecordMap = executeWatchResponse.getRecordSource().getAsMap();
|
||||
Object resultStatus = getExecutionStatus(watchRecordMap);
|
||||
assertThat(resultStatus.toString(), equalTo("simulated"));
|
||||
String status1 = ObjectPath.eval("result.actions.0.status", executeWatchResponse1.getRecordSource().getAsMap());
|
||||
assertThat(status1, equalTo("simulated"));
|
||||
} catch (IOException ioe) {
|
||||
throw new ElasticsearchException("failed to execute", ioe);
|
||||
}
|
||||
}
|
||||
}, 20, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void testFailingActionDoesGetThrottled() throws Exception {
|
||||
TimeValue throttlePeriod = new TimeValue(60, TimeUnit.MINUTES);
|
||||
|
||||
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("_id").setSource(watchBuilder()
|
||||
watcherClient().preparePutWatch("_id").setSource(watchBuilder()
|
||||
.trigger(new ScheduleTrigger(new IntervalSchedule(
|
||||
new IntervalSchedule.Interval(60, IntervalSchedule.Interval.Unit.MINUTES))))
|
||||
.defaultThrottlePeriod(throttlePeriod)
|
||||
|
@ -325,46 +298,50 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
// no DNS resolution here please
|
||||
.addAction("failing_hook", webhookAction(HttpRequestTemplate.builder("http://127.0.0.1/foobar", 80))))
|
||||
.get();
|
||||
assertThat(putWatchResponse.getVersion(), greaterThan(0L));
|
||||
refresh();
|
||||
refresh(Watch.INDEX);
|
||||
|
||||
ManualTriggerEvent triggerEvent = new ManualTriggerEvent("_id",
|
||||
new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)));
|
||||
ManualExecutionContext.Builder ctxBuilder = ManualExecutionContext.builder(watchService().getWatch("_id"), true, triggerEvent,
|
||||
throttlePeriod);
|
||||
ctxBuilder.recordExecution(true);
|
||||
{
|
||||
Map<String, Object> responseMap = watcherClient().prepareExecuteWatch("_id")
|
||||
.setRecordExecution(true)
|
||||
.get().getRecordSource().getAsMap();
|
||||
|
||||
ManualExecutionContext ctx = ctxBuilder.build();
|
||||
WatchRecord watchRecord = executionService().execute(ctx);
|
||||
String state = ObjectPath.eval("state", responseMap);
|
||||
|
||||
assertThat(watchRecord.state(), equalTo(ExecutionState.EXECUTED));
|
||||
assertThat(watchRecord.result().actionsResults().get("logging").action().status(), equalTo(Action.Result.Status.SUCCESS));
|
||||
assertThat(watchRecord.result().actionsResults().get("failing_hook").action().status(), equalTo(Action.Result.Status.FAILURE));
|
||||
|
||||
triggerEvent = new ManualTriggerEvent("_id",
|
||||
new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)));
|
||||
ctxBuilder = ManualExecutionContext.builder(watchService().getWatch("_id"), true, triggerEvent, throttlePeriod);
|
||||
ctxBuilder.recordExecution(true);
|
||||
|
||||
ctx = ctxBuilder.build();
|
||||
watchRecord = executionService().execute(ctx);
|
||||
assertThat(watchRecord.result().actionsResults().get("logging").action().status(), equalTo(Action.Result.Status.THROTTLED));
|
||||
assertThat(watchRecord.result().actionsResults().get("failing_hook").action().status(), equalTo(Action.Result.Status.FAILURE));
|
||||
assertThat(watchRecord.state(), equalTo(ExecutionState.THROTTLED));
|
||||
String firstId = ObjectPath.eval("result.actions.0.id", responseMap);
|
||||
String statusLogging, statusFailingHook;
|
||||
if ("logging".equals(firstId)) {
|
||||
statusLogging = ObjectPath.eval("result.actions.0.status", responseMap);
|
||||
statusFailingHook = ObjectPath.eval("result.actions.1.status", responseMap);
|
||||
} else {
|
||||
statusFailingHook = ObjectPath.eval("result.actions.0.status", responseMap);
|
||||
statusLogging = ObjectPath.eval("result.actions.1.status", responseMap);
|
||||
}
|
||||
|
||||
private String getExecutionStatus(Map<String, Object> watchRecordMap) {
|
||||
return ObjectPath.eval("result.actions.0.status", watchRecordMap);
|
||||
assertThat(state, equalTo(ExecutionState.EXECUTED.toString().toLowerCase(Locale.ROOT)));
|
||||
assertThat(statusLogging, equalTo(Action.Result.Status.SUCCESS.toString().toLowerCase(Locale.ROOT)));
|
||||
assertThat(statusFailingHook, equalTo(Action.Result.Status.FAILURE.toString().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
|
||||
private ManualExecutionContext getManualExecutionContext(TimeValue throttlePeriod) {
|
||||
ManualTriggerEvent triggerEvent = new ManualTriggerEvent("_id",
|
||||
new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)));
|
||||
return ManualExecutionContext.builder(watchService().getWatch("_id"), true, triggerEvent, throttlePeriod)
|
||||
.executionTime(timeWarped() ? new DateTime(timeWarp().clock().millis()) : new DateTime(Clock.systemUTC().millis()))
|
||||
.allActionsMode(ActionExecutionMode.SIMULATE)
|
||||
.recordExecution(true)
|
||||
.build();
|
||||
{
|
||||
Map<String, Object> responseMap = watcherClient().prepareExecuteWatch("_id")
|
||||
.setRecordExecution(true)
|
||||
.get().getRecordSource().getAsMap();
|
||||
String state = ObjectPath.eval("state", responseMap);
|
||||
|
||||
String firstId = ObjectPath.eval("result.actions.0.id", responseMap);
|
||||
String statusLogging, statusFailingHook;
|
||||
if ("logging".equals(firstId)) {
|
||||
statusLogging = ObjectPath.eval("result.actions.0.status", responseMap);
|
||||
statusFailingHook = ObjectPath.eval("result.actions.1.status", responseMap);
|
||||
} else {
|
||||
statusFailingHook = ObjectPath.eval("result.actions.0.status", responseMap);
|
||||
statusLogging = ObjectPath.eval("result.actions.1.status", responseMap);
|
||||
}
|
||||
|
||||
assertThat(state, equalTo(ExecutionState.THROTTLED.toString().toLowerCase(Locale.ROOT)));
|
||||
assertThat(statusLogging, equalTo(Action.Result.Status.THROTTLED.toString().toLowerCase(Locale.ROOT)));
|
||||
assertThat(statusFailingHook, equalTo(Action.Result.Status.FAILURE.toString().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
}
|
||||
|
||||
enum AvailableAction {
|
||||
|
@ -386,7 +363,9 @@ public class ActionThrottleTests extends AbstractWatcherIntegrationTestCase {
|
|||
WEBHOOK {
|
||||
@Override
|
||||
public Action.Builder action() throws Exception {
|
||||
HttpRequestTemplate.Builder requestBuilder = HttpRequestTemplate.builder("foo.bar.com", 1234);
|
||||
HttpRequestTemplate.Builder requestBuilder = HttpRequestTemplate.builder("localhost", 1234)
|
||||
.path("/")
|
||||
.method(HttpMethod.GET);
|
||||
return WebhookAction.builder(requestBuilder.build());
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.xpack.watcher.execution;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.lease.Releasable;
|
||||
|
@ -28,6 +29,7 @@ import org.elasticsearch.xpack.watcher.history.HistoryStore;
|
|||
import org.elasticsearch.xpack.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.xpack.watcher.input.ExecutableInput;
|
||||
import org.elasticsearch.xpack.watcher.input.Input;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
|
||||
import org.elasticsearch.xpack.watcher.transform.ExecutableTransform;
|
||||
import org.elasticsearch.xpack.watcher.transform.Transform;
|
||||
|
@ -36,7 +38,6 @@ import org.elasticsearch.xpack.watcher.watch.Payload;
|
|||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchLockService;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStatus;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.Before;
|
||||
|
@ -72,7 +73,6 @@ public class ExecutionServiceTests extends ESTestCase {
|
|||
private ExecutableInput input;
|
||||
private Input.Result inputResult;
|
||||
|
||||
private WatchStore watchStore;
|
||||
private TriggeredWatchStore triggeredWatchStore;
|
||||
private WatchExecutor executor;
|
||||
private HistoryStore historyStore;
|
||||
|
@ -80,6 +80,8 @@ public class ExecutionServiceTests extends ESTestCase {
|
|||
private ExecutionService executionService;
|
||||
private Clock clock;
|
||||
private ThreadPool threadPool;
|
||||
private WatcherClientProxy client;
|
||||
private Watch.Parser parser;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
|
@ -90,7 +92,6 @@ public class ExecutionServiceTests extends ESTestCase {
|
|||
when(inputResult.payload()).thenReturn(payload);
|
||||
when(input.execute(any(WatchExecutionContext.class), any(Payload.class))).thenReturn(inputResult);
|
||||
|
||||
watchStore = mock(WatchStore.class);
|
||||
triggeredWatchStore = mock(TriggeredWatchStore.class);
|
||||
historyStore = mock(HistoryStore.class);
|
||||
|
||||
|
@ -100,8 +101,11 @@ public class ExecutionServiceTests extends ESTestCase {
|
|||
watchLockService = mock(WatchLockService.class);
|
||||
clock = ClockMock.frozen();
|
||||
threadPool = mock(ThreadPool.class);
|
||||
executionService = new ExecutionService(Settings.EMPTY, historyStore, triggeredWatchStore, executor, watchStore,
|
||||
watchLockService, clock, threadPool);
|
||||
|
||||
client = mock(WatcherClientProxy.class);
|
||||
parser = mock(Watch.Parser.class);
|
||||
executionService = new ExecutionService(Settings.EMPTY, historyStore, triggeredWatchStore, executor, watchLockService, clock,
|
||||
threadPool, parser, client);
|
||||
|
||||
ClusterState clusterState = mock(ClusterState.class);
|
||||
when(triggeredWatchStore.loadTriggeredWatches(clusterState)).thenReturn(new ArrayList<>());
|
||||
|
@ -114,7 +118,9 @@ public class ExecutionServiceTests extends ESTestCase {
|
|||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
GetResponse getResponse = mock(GetResponse.class);
|
||||
when(getResponse.isExists()).thenReturn(true);
|
||||
when(client.getWatch("_id")).thenReturn(getResponse);
|
||||
|
||||
DateTime now = new DateTime(clock.millis());
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_id", now, now);
|
||||
|
@ -205,9 +211,12 @@ public class ExecutionServiceTests extends ESTestCase {
|
|||
Releasable releasable = mock(Releasable.class);
|
||||
when(watchLockService.acquire("_id")).thenReturn(releasable);
|
||||
|
||||
GetResponse getResponse = mock(GetResponse.class);
|
||||
when(getResponse.isExists()).thenReturn(true);
|
||||
when(client.getWatch("_id")).thenReturn(getResponse);
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
|
||||
DateTime now = new DateTime(clock.millis());
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_id", now, now);
|
||||
|
@ -277,7 +286,9 @@ public class ExecutionServiceTests extends ESTestCase {
|
|||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
GetResponse getResponse = mock(GetResponse.class);
|
||||
when(getResponse.isExists()).thenReturn(true);
|
||||
when(client.getWatch("_id")).thenReturn(getResponse);
|
||||
|
||||
DateTime now = new DateTime(clock.millis());
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_id", now, now);
|
||||
|
@ -343,7 +354,9 @@ public class ExecutionServiceTests extends ESTestCase {
|
|||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
GetResponse getResponse = mock(GetResponse.class);
|
||||
when(getResponse.isExists()).thenReturn(true);
|
||||
when(client.getWatch("_id")).thenReturn(getResponse);
|
||||
|
||||
DateTime now = new DateTime(clock.millis());
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_id", now, now);
|
||||
|
@ -408,7 +421,9 @@ public class ExecutionServiceTests extends ESTestCase {
|
|||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
GetResponse getResponse = mock(GetResponse.class);
|
||||
when(getResponse.isExists()).thenReturn(true);
|
||||
when(client.getWatch("_id")).thenReturn(getResponse);
|
||||
|
||||
DateTime now = new DateTime(clock.millis());
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_id", now, now);
|
||||
|
@ -773,7 +788,11 @@ public class ExecutionServiceTests extends ESTestCase {
|
|||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("foo");
|
||||
when(watch.nonce()).thenReturn(1L);
|
||||
when(watchStore.get(any())).thenReturn(watch);
|
||||
GetResponse getResponse = mock(GetResponse.class);
|
||||
when(getResponse.isExists()).thenReturn(true);
|
||||
when(getResponse.getId()).thenReturn("foo");
|
||||
when(client.getWatch(any())).thenReturn(getResponse);
|
||||
when(parser.parseWithSecrets(eq("foo"), eq(true), any(), any())).thenReturn(watch);
|
||||
|
||||
// execute needs to fail as well as storing the history
|
||||
doThrow(new EsRejectedExecutionException()).when(executor).execute(any());
|
||||
|
|
|
@ -5,54 +5,40 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.watcher.execution;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.MockScriptPlugin;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptType;
|
||||
import org.elasticsearch.xpack.watcher.WatcherService;
|
||||
import org.elasticsearch.xpack.watcher.actions.ActionStatus;
|
||||
import org.elasticsearch.xpack.watcher.actions.logging.LoggingAction;
|
||||
import org.elasticsearch.xpack.watcher.client.WatchSourceBuilder;
|
||||
import org.elasticsearch.xpack.watcher.condition.AlwaysCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.NeverCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.ScriptCondition;
|
||||
import org.elasticsearch.xpack.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.xpack.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.ObjectPath;
|
||||
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.delete.DeleteWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchRequest;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchRequestBuilder;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchRequest;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchRequest;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.trigger.manual.ManualTriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.watch.Payload;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||
import static org.elasticsearch.xpack.watcher.actions.ActionBuilders.loggingAction;
|
||||
import static org.elasticsearch.xpack.watcher.client.WatchSourceBuilders.watchBuilder;
|
||||
|
@ -61,13 +47,9 @@ import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule;
|
|||
import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.cron;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
public class ManualExecutionTests extends AbstractWatcherIntegrationTestCase {
|
||||
|
@ -125,79 +107,44 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTestCase {
|
|||
.condition(conditionAlwaysTrue ? AlwaysCondition.INSTANCE : NeverCondition.INSTANCE)
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
|
||||
ManualExecutionContext.Builder ctxBuilder;
|
||||
Watch parsedWatch = null;
|
||||
ManualTriggerEvent triggerEvent = new ManualTriggerEvent("_id",
|
||||
new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)));
|
||||
if (recordExecution) {
|
||||
PutWatchResponse putWatchResponse = watcherClient().putWatch(new PutWatchRequest("_id", watchBuilder)).actionGet();
|
||||
assertThat(putWatchResponse.getVersion(), greaterThan(0L));
|
||||
refresh();
|
||||
assertThat(watcherClient().getWatch(new GetWatchRequest("_id")).actionGet().isFound(), equalTo(true));
|
||||
//If we are persisting the state we need to use the exact watch that is in memory
|
||||
ctxBuilder = ManualExecutionContext.builder(watchService().getWatch("_id"), true, triggerEvent, timeValueSeconds(5));
|
||||
} else {
|
||||
parsedWatch = watchParser().parse("_id", false, watchBuilder.buildAsBytes(XContentType.JSON));
|
||||
ctxBuilder = ManualExecutionContext.builder(parsedWatch, false, triggerEvent, timeValueSeconds(5));
|
||||
}
|
||||
watcherClient().putWatch(new PutWatchRequest("_id", watchBuilder)).actionGet();
|
||||
|
||||
if (ignoreCondition) {
|
||||
ctxBuilder.withCondition(AlwaysCondition.RESULT_INSTANCE);
|
||||
}
|
||||
|
||||
ctxBuilder.recordExecution(recordExecution);
|
||||
|
||||
|
||||
if ("_all".equals(action)) {
|
||||
ctxBuilder.allActionsMode(ActionExecutionMode.SIMULATE);
|
||||
} else {
|
||||
ctxBuilder.actionMode(action, ActionExecutionMode.SIMULATE);
|
||||
}
|
||||
|
||||
ManualExecutionContext ctx = ctxBuilder.build();
|
||||
ExecuteWatchRequestBuilder executeWatchRequestBuilder = watcherClient().prepareExecuteWatch("_id");
|
||||
executeWatchRequestBuilder.setIgnoreCondition(ignoreCondition);
|
||||
executeWatchRequestBuilder.setRecordExecution(recordExecution);
|
||||
executeWatchRequestBuilder.setActionMode(action, ActionExecutionMode.SIMULATE);
|
||||
|
||||
refresh();
|
||||
long oldRecordCount = docCount(HistoryStore.INDEX_PREFIX_WITH_TEMPLATE + "*", HistoryStore.DOC_TYPE, matchAllQuery());
|
||||
|
||||
WatchRecord watchRecord = executionService().execute(ctx);
|
||||
ExecuteWatchResponse executeWatchResponse = executeWatchRequestBuilder.get();
|
||||
Map<String, Object> responseMap = executeWatchResponse.getRecordSource().getAsMap();
|
||||
|
||||
refresh();
|
||||
|
||||
long newRecordCount = docCount(HistoryStore.INDEX_PREFIX_WITH_TEMPLATE + "*", HistoryStore.DOC_TYPE, matchAllQuery());
|
||||
long expectedCount = oldRecordCount + (recordExecution ? 1 : 0);
|
||||
|
||||
assertThat("the expected count of history records should be [" + expectedCount + "]", newRecordCount, equalTo(expectedCount));
|
||||
|
||||
List<Map<String, Object>> actions = ObjectPath.eval("result.actions", responseMap);
|
||||
if (ignoreCondition) {
|
||||
assertThat("The action should have run", watchRecord.result().actionsResults().size(), equalTo(1));
|
||||
assertThat("The action should have run", actions.size(), equalTo(1));
|
||||
} else if (!conditionAlwaysTrue) {
|
||||
assertThat("The action should not have run", watchRecord.result().actionsResults().size(), equalTo(0));
|
||||
assertThat("The action should not have run", actions.size(), equalTo(0));
|
||||
}
|
||||
|
||||
if ((ignoreCondition || conditionAlwaysTrue) && action == null) {
|
||||
assertThat("The action should have run non simulated", watchRecord.result().actionsResults().get("log").action(),
|
||||
not(instanceOf(LoggingAction.Result.Simulated.class)) );
|
||||
}
|
||||
|
||||
if ((ignoreCondition || conditionAlwaysTrue) && action != null ) {
|
||||
assertThat("The action should have run simulated",
|
||||
watchRecord.result().actionsResults().get("log").action(), instanceOf(LoggingAction.Result.Simulated.class));
|
||||
}
|
||||
|
||||
Watch testWatch = watchService().getWatch("_id");
|
||||
if (recordExecution) {
|
||||
refresh();
|
||||
if (ignoreCondition || conditionAlwaysTrue) {
|
||||
assertThat(testWatch.status().actionStatus("log").ackStatus().state(), equalTo(ActionStatus.AckStatus.State.ACKABLE));
|
||||
GetWatchResponse response = watcherClient().getWatch(new GetWatchRequest("_id")).actionGet();
|
||||
assertThat(response.getStatus().actionStatus("log").ackStatus().state(), equalTo(ActionStatus.AckStatus.State.ACKABLE));
|
||||
} else {
|
||||
assertThat(testWatch.status().actionStatus("log").ackStatus().state(),
|
||||
equalTo(ActionStatus.AckStatus.State.AWAITS_SUCCESSFUL_EXECUTION));
|
||||
assertThat("The action should have run simulated", actions.get(0).get("status"), is("simulated"));
|
||||
}
|
||||
|
||||
if (recordExecution) {
|
||||
GetWatchResponse response = watcherClient().getWatch(new GetWatchRequest("_id")).actionGet();
|
||||
if (ignoreCondition || conditionAlwaysTrue) {
|
||||
assertThat(response.getStatus().actionStatus("log").ackStatus().state(), is(ActionStatus.AckStatus.State.ACKABLE));
|
||||
} else {
|
||||
assertThat(parsedWatch.status().actionStatus("log").ackStatus().state(),
|
||||
equalTo(ActionStatus.AckStatus.State.AWAITS_SUCCESSFUL_EXECUTION));
|
||||
assertThat(response.getStatus().actionStatus("log").ackStatus().state(),
|
||||
is(ActionStatus.AckStatus.State.AWAITS_SUCCESSFUL_EXECUTION));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,7 +161,8 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTestCase {
|
|||
builder.setRecordExecution(false);
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
builder.setTriggerEvent(new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)));
|
||||
DateTime now = new DateTime(DateTimeZone.UTC);
|
||||
builder.setTriggerEvent(new ScheduleTriggerEvent(now, now));
|
||||
}
|
||||
|
||||
ExecuteWatchResponse executeWatchResponse = builder.get();
|
||||
|
@ -231,17 +179,14 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTestCase {
|
|||
.condition(AlwaysCondition.INSTANCE)
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
|
||||
try {
|
||||
ActionRequestValidationException e = expectThrows(ActionRequestValidationException.class, () ->
|
||||
watcherClient().prepareExecuteWatch()
|
||||
.setWatchSource(watchBuilder)
|
||||
.setRecordExecution(true)
|
||||
.setTriggerEvent(new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)))
|
||||
.get();
|
||||
fail();
|
||||
} catch (ActionRequestValidationException e) {
|
||||
.get());
|
||||
assertThat(e.getMessage(), containsString("the execution of an inline watch cannot be recorded"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testExecutionWithInlineWatch_withWatchId() throws Exception {
|
||||
WatchSourceBuilder watchBuilder = watchBuilder()
|
||||
|
@ -269,10 +214,8 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTestCase {
|
|||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
|
||||
PutWatchResponse putWatchResponse = watcherClient().putWatch(new PutWatchRequest("_id", watchBuilder)).actionGet();
|
||||
assertThat(putWatchResponse.getVersion(), greaterThan(0L));
|
||||
refresh();
|
||||
assertThat(watcherClient().getWatch(new GetWatchRequest("_id")).actionGet().isFound(), equalTo(true));
|
||||
watcherClient().putWatch(new PutWatchRequest("_id", watchBuilder)).actionGet();
|
||||
refresh(Watch.INDEX);
|
||||
|
||||
Map<String, Object> map1 = new HashMap<>();
|
||||
map1.put("foo", "bar");
|
||||
|
@ -280,28 +223,24 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTestCase {
|
|||
Map<String, Object> map2 = new HashMap<>();
|
||||
map2.put("foo", map1);
|
||||
|
||||
ManualTriggerEvent triggerEvent = new ManualTriggerEvent("_id",
|
||||
new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)));
|
||||
ManualExecutionContext.Builder ctxBuilder1 = ManualExecutionContext.builder(watchService().getWatch("_id"), true, triggerEvent,
|
||||
timeValueSeconds(5));
|
||||
ctxBuilder1.actionMode("_all", ActionExecutionMode.SIMULATE);
|
||||
ExecuteWatchResponse firstResponse = watcherClient().prepareExecuteWatch("_id")
|
||||
.setActionMode("_all", ActionExecutionMode.SIMULATE)
|
||||
.setAlternativeInput(map1)
|
||||
.setRecordExecution(true)
|
||||
.setTriggerEvent(new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)))
|
||||
.get();
|
||||
|
||||
ctxBuilder1.withInput(new SimpleInput.Result(new Payload.Simple(map1)));
|
||||
ctxBuilder1.recordExecution(true);
|
||||
ExecuteWatchResponse secondResponse = watcherClient().prepareExecuteWatch("_id")
|
||||
.setActionMode("_all", ActionExecutionMode.SIMULATE)
|
||||
.setAlternativeInput(map2)
|
||||
.setRecordExecution(true)
|
||||
.setTriggerEvent(new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)))
|
||||
.get();
|
||||
|
||||
WatchRecord watchRecord1 = executionService().execute(ctxBuilder1.build());
|
||||
|
||||
ManualExecutionContext.Builder ctxBuilder2 = ManualExecutionContext.builder(watchService().getWatch("_id"), true, triggerEvent,
|
||||
timeValueSeconds(5));
|
||||
ctxBuilder2.actionMode("_all", ActionExecutionMode.SIMULATE);
|
||||
|
||||
ctxBuilder2.withInput(new SimpleInput.Result(new Payload.Simple(map2)));
|
||||
ctxBuilder2.recordExecution(true);
|
||||
|
||||
WatchRecord watchRecord2 = executionService().execute(ctxBuilder2.build());
|
||||
|
||||
assertThat(watchRecord1.result().inputResult().payload().data().get("foo").toString(), equalTo("bar"));
|
||||
assertThat(watchRecord2.result().inputResult().payload().data().get("foo"), instanceOf(Map.class));
|
||||
String firstPayload = ObjectPath.eval("result.input.payload.foo", firstResponse.getRecordSource().getAsMap());
|
||||
assertThat(firstPayload, is("bar"));
|
||||
Map<String, String> secondPayload = ObjectPath.eval("result.input.payload", secondResponse.getRecordSource().getAsMap());
|
||||
assertThat(secondPayload, instanceOf(Map.class));
|
||||
}
|
||||
|
||||
public void testExecutionRequestDefaults() throws Exception {
|
||||
|
@ -309,7 +248,7 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTestCase {
|
|||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.input(simpleInput("foo", "bar"))
|
||||
.condition(NeverCondition.INSTANCE)
|
||||
.defaultThrottlePeriod(new TimeValue(1, TimeUnit.HOURS))
|
||||
.defaultThrottlePeriod(TimeValue.timeValueHours(1))
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
watcherClient().putWatch(new PutWatchRequest("_id", watchBuilder)).actionGet();
|
||||
|
||||
|
@ -328,7 +267,7 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTestCase {
|
|||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.input(simpleInput("foo", "bar"))
|
||||
.condition(AlwaysCondition.INSTANCE)
|
||||
.defaultThrottlePeriod(new TimeValue(1, TimeUnit.HOURS))
|
||||
.defaultThrottlePeriod(TimeValue.timeValueHours(1))
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
watcherClient().putWatch(new PutWatchRequest("_id", watchBuilder)).actionGet();
|
||||
|
||||
|
@ -355,90 +294,13 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTestCase {
|
|||
.condition(new ScriptCondition(script))
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
|
||||
Watch watch = watchParser().parse("_id", false, watchBuilder.buildAsBytes(XContentType.JSON));
|
||||
ManualExecutionContext.Builder ctxBuilder = ManualExecutionContext.builder(watch, false, new ManualTriggerEvent("_id",
|
||||
new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC))),
|
||||
new TimeValue(1, TimeUnit.HOURS));
|
||||
WatchRecord record = executionService().execute(ctxBuilder.build());
|
||||
assertThat(record.result().executionDurationMs(), greaterThanOrEqualTo(100L));
|
||||
}
|
||||
watcherClient().preparePutWatch("_id").setSource(watchBuilder).get();
|
||||
refresh(Watch.INDEX);
|
||||
|
||||
public void testForceDeletionOfLongRunningWatch() throws Exception {
|
||||
Script script = new Script(ScriptType.INLINE, WATCHER_LANG, "sleep", singletonMap("millis", 10000L));
|
||||
WatchSourceBuilder watchBuilder = watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.input(simpleInput("foo", "bar"))
|
||||
.condition(new ScriptCondition(script))
|
||||
.defaultThrottlePeriod(new TimeValue(1, TimeUnit.HOURS))
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
ScheduleTriggerEvent triggerEvent = new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC));
|
||||
ExecuteWatchResponse executeWatchResponse = watcherClient().prepareExecuteWatch("_id").setTriggerEvent(triggerEvent).get();
|
||||
Integer duration = ObjectPath.eval("result.execution_duration", executeWatchResponse.getRecordSource().getAsMap());
|
||||
|
||||
PutWatchResponse putWatchResponse = watcherClient().putWatch(new PutWatchRequest("_id", watchBuilder)).actionGet();
|
||||
assertThat(putWatchResponse.getVersion(), greaterThan(0L));
|
||||
refresh();
|
||||
assertThat(watcherClient().getWatch(new GetWatchRequest("_id")).actionGet().isFound(), equalTo(true));
|
||||
|
||||
int numberOfThreads = scaledRandomIntBetween(1, 5);
|
||||
CountDownLatch startLatch = new CountDownLatch(1);
|
||||
|
||||
List<Thread> threads = new ArrayList<>();
|
||||
for (int i = 0; i < numberOfThreads; ++i) {
|
||||
threads.add(new Thread(new ExecutionRunner(watchService(), executionService(), "_id", startLatch)));
|
||||
}
|
||||
|
||||
for (Thread thread : threads) {
|
||||
thread.start();
|
||||
}
|
||||
DeleteWatchResponse deleteWatchResponse = watcherClient().prepareDeleteWatch("_id").get();
|
||||
assertThat(deleteWatchResponse.isFound(), is(true));
|
||||
|
||||
deleteWatchResponse = watcherClient().prepareDeleteWatch("_id").get();
|
||||
assertThat(deleteWatchResponse.isFound(), is(false));
|
||||
|
||||
startLatch.countDown();
|
||||
|
||||
long startJoin = System.currentTimeMillis();
|
||||
for (Thread thread : threads) {
|
||||
thread.join(30_000L);
|
||||
assertFalse(thread.isAlive());
|
||||
}
|
||||
long endJoin = System.currentTimeMillis();
|
||||
TimeValue tv = new TimeValue(10 * (numberOfThreads+1), TimeUnit.SECONDS);
|
||||
assertThat("Shouldn't take longer than [" + tv.getSeconds() + "] seconds for all the threads to stop", (endJoin - startJoin),
|
||||
lessThan(tv.getMillis()));
|
||||
}
|
||||
|
||||
public static class ExecutionRunner implements Runnable {
|
||||
|
||||
final WatcherService watcherService;
|
||||
final ExecutionService executionService;
|
||||
final String watchId;
|
||||
final CountDownLatch startLatch;
|
||||
final ManualExecutionContext.Builder ctxBuilder;
|
||||
|
||||
public ExecutionRunner(WatcherService watcherService, ExecutionService executionService, String watchId,
|
||||
CountDownLatch startLatch) {
|
||||
this.watcherService = watcherService;
|
||||
this.executionService = executionService;
|
||||
this.watchId = watchId;
|
||||
this.startLatch = startLatch;
|
||||
ManualTriggerEvent triggerEvent = new ManualTriggerEvent(watchId,
|
||||
new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)));
|
||||
ctxBuilder = ManualExecutionContext.builder(watcherService.getWatch(watchId), true, triggerEvent, timeValueSeconds(5));
|
||||
ctxBuilder.recordExecution(true);
|
||||
ctxBuilder.actionMode("_all", ActionExecutionMode.FORCE_EXECUTE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
startLatch.await();
|
||||
WatchRecord record = executionService.execute(ctxBuilder.build());
|
||||
assertThat(record, notNullValue());
|
||||
assertThat(record.state(), is(ExecutionState.NOT_EXECUTED_WATCH_MISSING));
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("Failure mode execution of [{}] failed in an unexpected way", e, watchId);
|
||||
assertThat(duration, greaterThanOrEqualTo(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,14 +11,13 @@ import org.elasticsearch.common.network.NetworkModule;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||
import org.elasticsearch.transport.Netty4Plugin;
|
||||
import org.elasticsearch.xpack.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.xpack.watcher.condition.CompareCondition;
|
||||
import org.elasticsearch.xpack.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
|
||||
import org.elasticsearch.xpack.common.http.auth.basic.BasicAuth;
|
||||
import org.elasticsearch.xpack.common.text.TextTemplate;
|
||||
import org.elasticsearch.xpack.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.xpack.watcher.condition.CompareCondition;
|
||||
import org.elasticsearch.xpack.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
|
||||
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse;
|
||||
|
@ -57,7 +56,6 @@ public class HttpInputIntegrationTests extends AbstractWatcherIntegrationTestCas
|
|||
return plugins;
|
||||
}
|
||||
|
||||
@TestLogging("org.elasticsearch.watcher.support.http:TRACE")
|
||||
public void testHttpInput() throws Exception {
|
||||
createIndex("index");
|
||||
client().prepareIndex("index", "type", "id").setSource("{}").setRefreshPolicy(IMMEDIATE).get();
|
||||
|
@ -71,7 +69,7 @@ public class HttpInputIntegrationTests extends AbstractWatcherIntegrationTestCas
|
|||
.body(jsonBuilder().startObject().field("size", 1).endObject().string())
|
||||
.auth(securityEnabled() ? new BasicAuth("test", "changeme".toCharArray()) : null)))
|
||||
.condition(new CompareCondition("ctx.payload.hits.total", CompareCondition.Op.EQ, 1L))
|
||||
.addAction("_id", loggingAction("watch [{{ctx.watch_id}}] matched")))
|
||||
.addAction("_id", loggingAction("anything")))
|
||||
.get();
|
||||
|
||||
if (timeWarped()) {
|
||||
|
@ -90,7 +88,7 @@ public class HttpInputIntegrationTests extends AbstractWatcherIntegrationTestCas
|
|||
.path("/_cluster/stats")
|
||||
.auth(securityEnabled() ? new BasicAuth("test", "changeme".toCharArray()) : null)))
|
||||
.condition(new CompareCondition("ctx.payload.nodes.count.total", CompareCondition.Op.GTE, 1L))
|
||||
.addAction("_id", loggingAction("watch [{{ctx.watch_id}}] matched")))
|
||||
.addAction("_id", loggingAction("anything")))
|
||||
.get();
|
||||
|
||||
assertTrue(putWatchResponse.isCreated());
|
||||
|
@ -101,7 +99,6 @@ public class HttpInputIntegrationTests extends AbstractWatcherIntegrationTestCas
|
|||
assertWatchWithMinimumPerformedActionsCount("_name", 1, false);
|
||||
}
|
||||
|
||||
@TestLogging("org.elasticsearch.watcher.support.http:TRACE")
|
||||
public void testInputFiltering() throws Exception {
|
||||
WatcherClient watcherClient = watcherClient();
|
||||
createIndex("idx");
|
||||
|
|
|
@ -18,10 +18,12 @@ import org.elasticsearch.xpack.watcher.transport.actions.stats.WatcherStatsRespo
|
|||
import org.elasticsearch.xpack.watcher.trigger.TriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.IntervalSchedule;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||
import static org.elasticsearch.xpack.watcher.client.WatchSourceBuilders.watchBuilder;
|
||||
import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule;
|
||||
|
@ -56,6 +58,8 @@ public class BasicSecurityTests extends AbstractWatcherIntegrationTestCase {
|
|||
}
|
||||
|
||||
public void testWatcherMonitorRole() throws Exception {
|
||||
assertAcked(client().admin().indices().prepareCreate(Watch.INDEX));
|
||||
|
||||
// stats and get watch apis require at least monitor role:
|
||||
String token = basicAuthHeaderValue("test", new SecuredString("changeme".toCharArray()));
|
||||
try {
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.ElasticsearchException;
|
|||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
|
@ -69,11 +70,10 @@ import org.elasticsearch.xpack.watcher.execution.ExecutionState;
|
|||
import org.elasticsearch.xpack.watcher.execution.TriggeredWatchStore;
|
||||
import org.elasticsearch.xpack.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
|
||||
import org.elasticsearch.xpack.watcher.trigger.ScheduleTriggerEngineMock;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerService;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
|
@ -91,6 +91,7 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
@ -128,8 +129,8 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase
|
|||
protected TestCluster buildTestCluster(Scope scope, long seed) throws IOException {
|
||||
if (securityEnabled == null) {
|
||||
securityEnabled = enableSecurity();
|
||||
scheduleEngineName = randomFrom("ticker", "scheduler");
|
||||
}
|
||||
scheduleEngineName = randomFrom("ticker", "scheduler");
|
||||
return super.buildTestCluster(scope, seed);
|
||||
}
|
||||
|
||||
|
@ -327,7 +328,7 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase
|
|||
|
||||
CreateIndexResponse response = client().admin().indices().prepareCreate(newIndex)
|
||||
.setCause("Index to test aliases with .watches index")
|
||||
.addAlias(new Alias(WatchStore.INDEX))
|
||||
.addAlias(new Alias(Watch.INDEX))
|
||||
.setSettings((Map<String, Object>) parserMap.get("settings"))
|
||||
.addMapping("watch", (Map<String, Object>) allMappings.get("watch"))
|
||||
.get();
|
||||
|
@ -395,18 +396,6 @@ public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase
|
|||
return getInstanceFromMaster(Watch.Parser.class);
|
||||
}
|
||||
|
||||
protected ExecutionService executionService() {
|
||||
return getInstanceFromMaster(ExecutionService.class);
|
||||
}
|
||||
|
||||
protected WatcherService watchService() {
|
||||
return getInstanceFromMaster(WatcherService.class);
|
||||
}
|
||||
|
||||
protected TriggerService triggerService() {
|
||||
return getInstanceFromMaster(TriggerService.class);
|
||||
}
|
||||
|
||||
public AbstractWatcherIntegrationTestCase() {
|
||||
super();
|
||||
}
|
||||
|
|
|
@ -9,14 +9,12 @@ import org.elasticsearch.common.Randomness;
|
|||
import org.elasticsearch.common.SuppressForbidden;
|
||||
import org.elasticsearch.common.metrics.MeanMetric;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.xpack.watcher.trigger.Trigger;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerEngine;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.Schedule;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleRegistry;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEngine;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.engine.BaseTriggerEngineTestCase;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.engine.SchedulerScheduleTriggerEngine;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.engine.TickerScheduleTriggerEngine;
|
||||
|
||||
|
@ -59,7 +57,7 @@ public class ScheduleEngineTriggerBenchmark {
|
|||
.build();
|
||||
List<TriggerEngine.Job> jobs = new ArrayList<>(numWatches);
|
||||
for (int i = 0; i < numWatches; i++) {
|
||||
jobs.add(new SimpleJob("job_" + i, interval(interval + "s")));
|
||||
jobs.add(new BaseTriggerEngineTestCase.SimpleJob("job_" + i, interval(interval + "s")));
|
||||
}
|
||||
ScheduleRegistry scheduleRegistry = new ScheduleRegistry(emptySet());
|
||||
List<String> impls = new ArrayList<>(Arrays.asList(new String[]{"schedule", "ticker"}));
|
||||
|
@ -143,28 +141,6 @@ public class ScheduleEngineTriggerBenchmark {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static class SimpleJob implements TriggerEngine.Job {
|
||||
|
||||
private final String name;
|
||||
private final ScheduleTrigger trigger;
|
||||
|
||||
public SimpleJob(String name, Schedule schedule) {
|
||||
this.name = name;
|
||||
this.trigger = new ScheduleTrigger(schedule);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trigger trigger() {
|
||||
return trigger;
|
||||
}
|
||||
}
|
||||
|
||||
static class Stats {
|
||||
|
||||
final String implementation;
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.elasticsearch.xpack.watcher.client.WatchSourceBuilder;
|
|||
import org.elasticsearch.xpack.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.xpack.watcher.condition.ScriptCondition;
|
||||
import org.elasticsearch.xpack.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -106,7 +106,7 @@ public class WatcherScheduleEngineBenchmark {
|
|||
System.out.println("===============> indexing [" + numWatches + "] watches");
|
||||
for (int i = 0; i < numWatches; i++) {
|
||||
final String id = "_id_" + i;
|
||||
client.prepareIndex(WatchStore.INDEX, WatchStore.DOC_TYPE, id)
|
||||
client.prepareIndex(Watch.INDEX, Watch.DOC_TYPE, id)
|
||||
.setSource(new WatchSourceBuilder()
|
||||
.trigger(schedule(interval(interval + "s")))
|
||||
.input(searchInput(templateRequest(new SearchSourceBuilder(), "test")))
|
||||
|
@ -115,7 +115,7 @@ public class WatcherScheduleEngineBenchmark {
|
|||
.buildAsBytes(XContentType.JSON)
|
||||
).get();
|
||||
}
|
||||
client.admin().indices().prepareFlush(WatchStore.INDEX, "test").get();
|
||||
client.admin().indices().prepareFlush(Watch.INDEX, "test").get();
|
||||
System.out.println("===============> indexed [" + numWatches + "] watches");
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ public class WatcherScheduleEngineBenchmark {
|
|||
try (final Client client = node.client()) {
|
||||
client.admin().cluster().prepareHealth().setWaitForNodes("2").get();
|
||||
client.admin().indices().prepareDelete(HistoryStore.INDEX_PREFIX_WITH_TEMPLATE + "*").get();
|
||||
client.admin().cluster().prepareHealth(WatchStore.INDEX, "test").setWaitForYellowStatus().get();
|
||||
client.admin().cluster().prepareHealth(Watch.INDEX, "test").setWaitForYellowStatus().get();
|
||||
|
||||
Clock clock = node.injector().getInstance(Clock.class);
|
||||
WatcherClient watcherClient = node.injector().getInstance(WatcherClient.class);
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
|||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptType;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||
import org.elasticsearch.xpack.watcher.client.WatchSourceBuilder;
|
||||
import org.elasticsearch.xpack.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.xpack.watcher.condition.AlwaysCondition;
|
||||
|
@ -27,11 +28,10 @@ import org.elasticsearch.xpack.watcher.trigger.schedule.IntervalSchedule;
|
|||
import org.elasticsearch.xpack.watcher.trigger.schedule.Schedules;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.support.MonthTimes;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.support.WeekTimes;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.time.Clock;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
|
@ -61,6 +61,11 @@ import static org.hamcrest.Matchers.notNullValue;
|
|||
|
||||
public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
||||
|
||||
@Override
|
||||
protected boolean enableSecurity() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean timeWarped() {
|
||||
return true;
|
||||
|
@ -130,7 +135,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
assertThat(deleteWatchResponse.isFound(), is(true));
|
||||
|
||||
refresh();
|
||||
assertHitCount(client().prepareSearch(WatchStore.INDEX).setSize(0).get(), 0L);
|
||||
assertHitCount(client().prepareSearch(Watch.INDEX).setSize(0).get(), 0L);
|
||||
|
||||
// Deleting the same watch for the second time
|
||||
deleteWatchResponse = watcherClient.prepareDeleteWatch("_name").get();
|
||||
|
@ -163,7 +168,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
// In watch store we fail parsing if an watch contains undefined fields.
|
||||
}
|
||||
try {
|
||||
client().prepareIndex(WatchStore.INDEX, WatchStore.DOC_TYPE, "_name")
|
||||
client().prepareIndex(Watch.INDEX, Watch.DOC_TYPE, "_name")
|
||||
.setSource(watchSource)
|
||||
.get();
|
||||
fail();
|
||||
|
@ -247,6 +252,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
equalTo(before));
|
||||
}
|
||||
|
||||
@TestLogging("org.elasticsearch.xpack.watcher.trigger:DEBUG")
|
||||
public void testConditionSearchWithSource() throws Exception {
|
||||
SearchSourceBuilder searchSourceBuilder = searchSource().query(matchQuery("level", "a"));
|
||||
testConditionSearch(templateRequest(searchSourceBuilder, "events"));
|
||||
|
@ -394,6 +400,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.get();
|
||||
|
||||
refresh();
|
||||
timeWarp().clock().fastForwardSeconds(1);
|
||||
timeWarp().scheduler().trigger(watchName);
|
||||
assertWatchWithNoActionNeeded(watchName, 1);
|
||||
|
||||
|
@ -401,6 +408,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setSource("level", "b")
|
||||
.get();
|
||||
refresh();
|
||||
timeWarp().clock().fastForwardSeconds(1);
|
||||
timeWarp().scheduler().trigger(watchName);
|
||||
assertWatchWithNoActionNeeded(watchName, 2);
|
||||
|
||||
|
@ -408,6 +416,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setSource("level", "a")
|
||||
.get();
|
||||
refresh();
|
||||
timeWarp().clock().fastForwardSeconds(1);
|
||||
timeWarp().scheduler().trigger(watchName);
|
||||
assertWatchWithMinimumPerformedActionsCount(watchName, 1);
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ package org.elasticsearch.xpack.watcher.test.integration;
|
|||
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.support.ActiveShardCount;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||
import org.elasticsearch.xpack.watcher.WatcherState;
|
||||
import org.elasticsearch.xpack.watcher.condition.Condition;
|
||||
import org.elasticsearch.xpack.watcher.condition.AlwaysCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.CompareCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.Condition;
|
||||
import org.elasticsearch.xpack.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.xpack.watcher.execution.TriggeredWatch;
|
||||
import org.elasticsearch.xpack.watcher.execution.TriggeredWatchStore;
|
||||
|
@ -25,7 +25,6 @@ import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
|
|||
import org.elasticsearch.xpack.watcher.transport.actions.stats.WatcherStatsResponse;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
@ -36,6 +35,7 @@ import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDI
|
|||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||
import static org.elasticsearch.xpack.watcher.actions.ActionBuilders.indexAction;
|
||||
import static org.elasticsearch.xpack.watcher.client.WatchSourceBuilders.watchBuilder;
|
||||
|
@ -51,62 +51,11 @@ public class BootStrapTests extends AbstractWatcherIntegrationTestCase {
|
|||
|
||||
@Override
|
||||
protected boolean timeWarped() {
|
||||
// timewarping isn't necessary here, because we aren't testing triggering or throttling
|
||||
return false;
|
||||
}
|
||||
|
||||
public void testLoadMalformedWatch() throws Exception {
|
||||
// valid watch
|
||||
client().prepareIndex(WatchStore.INDEX, WatchStore.DOC_TYPE, "_id0")
|
||||
.setSource(jsonBuilder().startObject()
|
||||
.startObject(Watch.Field.TRIGGER.getPreferredName())
|
||||
.startObject("schedule")
|
||||
.field("interval", "1s")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.startObject(Watch.Field.ACTIONS.getPreferredName())
|
||||
.endObject()
|
||||
.endObject())
|
||||
.get();
|
||||
|
||||
// invalid interval
|
||||
client().prepareIndex(WatchStore.INDEX, WatchStore.DOC_TYPE, "_id2")
|
||||
.setSource(jsonBuilder().startObject()
|
||||
.startObject(Watch.Field.TRIGGER.getPreferredName())
|
||||
.startObject("schedule")
|
||||
.field("interval", true)
|
||||
.endObject()
|
||||
.endObject()
|
||||
.startObject(Watch.Field.ACTIONS.getPreferredName())
|
||||
.endObject()
|
||||
.endObject())
|
||||
.get();
|
||||
|
||||
// illegal top level field
|
||||
client().prepareIndex(WatchStore.INDEX, WatchStore.DOC_TYPE, "_id3")
|
||||
.setSource(jsonBuilder().startObject()
|
||||
.startObject(Watch.Field.TRIGGER.getPreferredName())
|
||||
.startObject("schedule")
|
||||
.field("interval", "1s")
|
||||
.endObject()
|
||||
.startObject("illegal_field").endObject()
|
||||
.endObject()
|
||||
.startObject(Watch.Field.ACTIONS.getPreferredName()).endObject()
|
||||
.endObject())
|
||||
.get();
|
||||
|
||||
stopWatcher();
|
||||
startWatcher();
|
||||
|
||||
WatcherStatsResponse response = watcherClient().prepareWatcherStats().get();
|
||||
assertThat(response.getWatcherState(), equalTo(WatcherState.STARTED));
|
||||
// Only the valid watch should been loaded
|
||||
assertThat(response.getWatchesCount(), equalTo(1L));
|
||||
assertThat(watcherClient().prepareGetWatch("_id0").get().getId(), Matchers.equalTo("_id0"));
|
||||
}
|
||||
|
||||
public void testLoadMalformedWatchRecord() throws Exception {
|
||||
client().prepareIndex(WatchStore.INDEX, WatchStore.DOC_TYPE, "_id")
|
||||
client().prepareIndex(Watch.INDEX, Watch.DOC_TYPE, "_id")
|
||||
.setSource(jsonBuilder().startObject()
|
||||
.startObject(Watch.Field.TRIGGER.getPreferredName())
|
||||
.startObject("schedule")
|
||||
|
@ -185,6 +134,8 @@ public class BootStrapTests extends AbstractWatcherIntegrationTestCase {
|
|||
}
|
||||
|
||||
public void testDeletedWhileQueued() throws Exception {
|
||||
assertAcked(client().admin().indices().prepareCreate(".watches"));
|
||||
|
||||
DateTime now = DateTime.now(UTC);
|
||||
Wid wid = new Wid("_id", 1, now);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_id", now, now);
|
||||
|
@ -215,7 +166,7 @@ public class BootStrapTests extends AbstractWatcherIntegrationTestCase {
|
|||
WatcherSearchTemplateRequest request =
|
||||
templateRequest(searchSource().query(termQuery("field", "value")), "my-index");
|
||||
for (int i = 0; i < numWatches; i++) {
|
||||
client().prepareIndex(WatchStore.INDEX, WatchStore.DOC_TYPE, "_id" + i)
|
||||
client().prepareIndex(Watch.INDEX, Watch.DOC_TYPE, "_id" + i)
|
||||
.setSource(watchBuilder()
|
||||
.trigger(schedule(cron("0 0/5 * * * ? 2050")))
|
||||
.input(searchInput(request))
|
||||
|
@ -235,15 +186,19 @@ public class BootStrapTests extends AbstractWatcherIntegrationTestCase {
|
|||
assertThat(response.getWatchesCount(), equalTo((long) numWatches));
|
||||
}
|
||||
|
||||
@TestLogging("org.elasticsearch.watcher.actions:DEBUG")
|
||||
public void testTriggeredWatchLoading() throws Exception {
|
||||
createIndex("output");
|
||||
client().prepareIndex("my-index", "foo", "bar")
|
||||
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
|
||||
.setSource("field", "value").get();
|
||||
|
||||
WatcherStatsResponse response = watcherClient().prepareWatcherStats().get();
|
||||
assertThat(response.getWatcherState(), equalTo(WatcherState.STARTED));
|
||||
assertThat(response.getWatchesCount(), equalTo(0L));
|
||||
|
||||
WatcherSearchTemplateRequest request =
|
||||
templateRequest(searchSource().query(termQuery("field", "value")), "my-index");
|
||||
|
||||
int numWatches = 8;
|
||||
for (int i = 0; i < numWatches; i++) {
|
||||
String watchId = "_id" + i;
|
||||
|
@ -273,27 +228,27 @@ public class BootStrapTests extends AbstractWatcherIntegrationTestCase {
|
|||
stopWatcher();
|
||||
startWatcher();
|
||||
|
||||
assertBusy(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
assertBusy(() -> {
|
||||
// We need to wait until all the records are processed from the internal execution queue, only then we can assert
|
||||
// that numRecords watch records have been processed as part of starting up.
|
||||
WatcherStatsResponse response = watcherClient().prepareWatcherStats().get();
|
||||
assertThat(response.getWatcherState(), equalTo(WatcherState.STARTED));
|
||||
assertThat(response.getThreadPoolQueueSize(), equalTo(0L));
|
||||
WatcherStatsResponse response1 = watcherClient().prepareWatcherStats().get();
|
||||
assertThat(response1.getWatcherState(), equalTo(WatcherState.STARTED));
|
||||
assertThat(response1.getThreadPoolQueueSize(), equalTo(0L));
|
||||
|
||||
// but even then since the execution of the watch record is async it may take a little bit before
|
||||
// the actual documents are in the output index
|
||||
refresh();
|
||||
SearchResponse searchResponse = client().prepareSearch("output").get();
|
||||
assertHitCount(searchResponse, numRecords);
|
||||
}
|
||||
}, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void testMixedTriggeredWatchLoading() throws Exception {
|
||||
createIndex("output");
|
||||
client().prepareIndex("my-index", "foo", "bar")
|
||||
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
|
||||
.setSource("field", "value").get();
|
||||
|
||||
WatcherStatsResponse response = watcherClient().prepareWatcherStats().get();
|
||||
assertThat(response.getWatcherState(), equalTo(WatcherState.STARTED));
|
||||
assertThat(response.getWatchesCount(), equalTo(0L));
|
||||
|
@ -324,21 +279,18 @@ public class BootStrapTests extends AbstractWatcherIntegrationTestCase {
|
|||
stopWatcher();
|
||||
startWatcher();
|
||||
|
||||
assertBusy(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertBusy(() -> {
|
||||
// We need to wait until all the records are processed from the internal execution queue, only then we can assert
|
||||
// that numRecords watch records have been processed as part of starting up.
|
||||
WatcherStatsResponse response = watcherClient().prepareWatcherStats().get();
|
||||
assertThat(response.getWatcherState(), equalTo(WatcherState.STARTED));
|
||||
assertThat(response.getThreadPoolQueueSize(), equalTo(0L));
|
||||
WatcherStatsResponse response1 = watcherClient().prepareWatcherStats().get();
|
||||
assertThat(response1.getWatcherState(), equalTo(WatcherState.STARTED));
|
||||
assertThat(response1.getThreadPoolQueueSize(), equalTo(0L));
|
||||
|
||||
// but even then since the execution of the watch record is async it may take a little bit before
|
||||
// the actual documents are in the output index
|
||||
refresh();
|
||||
SearchResponse searchResponse = client().prepareSearch("output").get();
|
||||
assertHitCount(searchResponse, numRecords);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -353,10 +305,16 @@ public class BootStrapTests extends AbstractWatcherIntegrationTestCase {
|
|||
assertThat(response.getWatcherMetaData().manuallyStopped(), is(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean enableSecurity() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void testWatchRecordSavedTwice() throws Exception {
|
||||
// Watcher could prevent to start if a watch record tried to executed twice or more and the watch didn't exist
|
||||
// for that watch record or the execution threadpool rejected the watch record.
|
||||
// A watch record without a watch is the easiest to simulate, so that is what this test does.
|
||||
assertAcked(client().admin().indices().prepareCreate(Watch.INDEX));
|
||||
|
||||
DateTime triggeredTime = new DateTime(2015, 11, 5, 0, 0, 0, 0, DateTimeZone.UTC);
|
||||
final String watchRecordIndex = HistoryStore.getHistoryIndexNameForTime(triggeredTime);
|
||||
|
@ -380,10 +338,8 @@ public class BootStrapTests extends AbstractWatcherIntegrationTestCase {
|
|||
|
||||
stopWatcher();
|
||||
startWatcher();
|
||||
assertBusy(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
assertBusy(() -> {
|
||||
// We need to wait until all the records are processed from the internal execution queue, only then we can assert
|
||||
// that numRecords watch records have been processed as part of starting up.
|
||||
WatcherStatsResponse response = watcherClient().prepareWatcherStats().get();
|
||||
|
@ -396,8 +352,7 @@ public class BootStrapTests extends AbstractWatcherIntegrationTestCase {
|
|||
SearchResponse searchResponse = client().prepareSearch(watchRecordIndex).setSize(numRecords).get();
|
||||
assertThat(searchResponse.getHits().getTotalHits(), Matchers.equalTo((long) numRecords));
|
||||
for (int i = 0; i < numRecords; i++) {
|
||||
assertThat(searchResponse.getHits().getAt(i).getSource().get("state"), Matchers.equalTo("executed_multiple_times"));
|
||||
}
|
||||
assertThat(searchResponse.getHits().getAt(i).getSource().get("state"), is(ExecutionState.EXECUTED_MULTIPLE_TIMES.id()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchRes
|
|||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.trigger.TriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -92,7 +92,7 @@ public class HttpSecretsIntegrationTests extends AbstractWatcherIntegrationTestC
|
|||
|
||||
// verifying the basic auth password is stored encrypted in the index when security
|
||||
// is enabled, and when it's not enabled, it's stored in plain text
|
||||
GetResponse response = client().prepareGet(WatchStore.INDEX, WatchStore.DOC_TYPE, "_id").get();
|
||||
GetResponse response = client().prepareGet(Watch.INDEX, Watch.DOC_TYPE, "_id").get();
|
||||
assertThat(response, notNullValue());
|
||||
assertThat(response.getId(), is("_id"));
|
||||
Map<String, Object> source = response.getSource();
|
||||
|
@ -156,7 +156,7 @@ public class HttpSecretsIntegrationTests extends AbstractWatcherIntegrationTestC
|
|||
|
||||
// verifying the basic auth password is stored encrypted in the index when security
|
||||
// is enabled, when it's not enabled, the the passowrd should be stored in plain text
|
||||
GetResponse response = client().prepareGet(WatchStore.INDEX, WatchStore.DOC_TYPE, "_id").get();
|
||||
GetResponse response = client().prepareGet(Watch.INDEX, Watch.DOC_TYPE, "_id").get();
|
||||
assertThat(response, notNullValue());
|
||||
assertThat(response.getId(), is("_id"));
|
||||
Map<String, Object> source = response.getSource();
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchRequest;
|
|||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
import org.elasticsearch.xpack.watcher.watch.WatchStore;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
|
||||
|
@ -96,8 +95,8 @@ public class WatchAckTests extends AbstractWatcherIntegrationTestCase {
|
|||
refresh();
|
||||
long a1CountAfterAck = docCount("actions", "action1", matchAllQuery());
|
||||
long a2CountAfterAck = docCount("actions", "action2", matchAllQuery());
|
||||
assertThat(a1CountAfterAck, greaterThanOrEqualTo((long) 1));
|
||||
assertThat(a2CountAfterAck, greaterThanOrEqualTo((long) 1));
|
||||
assertThat(a1CountAfterAck, greaterThan(0L));
|
||||
assertThat(a2CountAfterAck, greaterThan(0L));
|
||||
|
||||
timeWarp().scheduler().trigger("_id", 4, TimeValue.timeValueSeconds(5));
|
||||
flush();
|
||||
|
@ -227,7 +226,7 @@ public class WatchAckTests extends AbstractWatcherIntegrationTestCase {
|
|||
assertThat(watchResponse.getStatus().actionStatus("_id").ackStatus().state(), Matchers.equalTo(ActionStatus.AckStatus.State.ACKED));
|
||||
|
||||
refresh();
|
||||
GetResponse getResponse = client().get(new GetRequest(WatchStore.INDEX, WatchStore.DOC_TYPE, "_name")).actionGet();
|
||||
GetResponse getResponse = client().get(new GetRequest(Watch.INDEX, Watch.DOC_TYPE, "_name")).actionGet();
|
||||
Watch indexedWatch = watchParser().parse("_name", true, getResponse.getSourceAsBytesRef());
|
||||
assertThat(watchResponse.getStatus().actionStatus("_id").ackStatus().state(),
|
||||
equalTo(indexedWatch.status().actionStatus("_id").ackStatus().state()));
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.elasticsearch.common.xcontent.ToXContent;
|
|||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.xpack.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.xpack.watcher.condition.AlwaysCondition;
|
||||
import org.elasticsearch.xpack.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
|
||||
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
|
@ -38,12 +37,13 @@ import static org.hamcrest.Matchers.is;
|
|||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
public class ActivateWatchTests extends AbstractWatcherIntegrationTestCase {
|
||||
|
||||
@Override
|
||||
protected boolean timeWarped() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@AwaitsFix(bugUrl = "https://github.com/elastic/x-plugins/issues/4002")
|
||||
// FIXME not to be sleep based
|
||||
public void testDeactivateAndActivate() throws Exception {
|
||||
WatcherClient watcherClient = watcherClient();
|
||||
|
||||
|
@ -52,7 +52,6 @@ public class ActivateWatchTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setSource(watchBuilder()
|
||||
.trigger(schedule(interval("1s")))
|
||||
.input(simpleInput("foo", "bar"))
|
||||
.condition(AlwaysCondition.INSTANCE)
|
||||
.addAction("_a1", indexAction("actions", "action1"))
|
||||
.defaultThrottlePeriod(new TimeValue(0, TimeUnit.SECONDS)))
|
||||
.get();
|
||||
|
@ -109,7 +108,6 @@ public class ActivateWatchTests extends AbstractWatcherIntegrationTestCase {
|
|||
.setSource(watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 1 ? 2050"))) // some time in 2050
|
||||
.input(simpleInput("foo", "bar"))
|
||||
.condition(AlwaysCondition.INSTANCE)
|
||||
.addAction("_a1", indexAction("actions", "action1"))
|
||||
.defaultThrottlePeriod(new TimeValue(0, TimeUnit.SECONDS)))
|
||||
.get();
|
||||
|
|
|
@ -38,6 +38,7 @@ import static org.hamcrest.Matchers.not;
|
|||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
public class DeleteWatchTests extends AbstractWatcherIntegrationTestCase {
|
||||
|
||||
public void testDelete() throws Exception {
|
||||
ensureWatcherStarted();
|
||||
PutWatchResponse putResponse = watcherClient().preparePutWatch("_name").setSource(watchBuilder()
|
||||
|
|
|
@ -5,21 +5,27 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.watcher.transport.action.get;
|
||||
|
||||
import org.elasticsearch.ExceptionsHelper;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
import org.elasticsearch.xpack.watcher.condition.AlwaysCondition;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
|
||||
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchRequest;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.watch.Watch;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.elasticsearch.xpack.watcher.actions.ActionBuilders.loggingAction;
|
||||
import static org.elasticsearch.xpack.watcher.client.WatchSourceBuilders.watchBuilder;
|
||||
import static org.elasticsearch.xpack.watcher.input.InputBuilders.simpleInput;
|
||||
import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule;
|
||||
import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.interval;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasKey;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
@ -41,7 +47,6 @@ public class GetWatchTests extends AbstractWatcherIntegrationTestCase {
|
|||
assertThat(getResponse, notNullValue());
|
||||
assertThat(getResponse.isFound(), is(true));
|
||||
assertThat(getResponse.getId(), is("_name"));
|
||||
assertThat(getResponse.getStatus().version(), is(putResponse.getVersion()));
|
||||
Map<String, Object> source = getResponse.getSource().getAsMap();
|
||||
assertThat(source, notNullValue());
|
||||
assertThat(source, hasKey("trigger"));
|
||||
|
@ -51,7 +56,13 @@ public class GetWatchTests extends AbstractWatcherIntegrationTestCase {
|
|||
assertThat(source, not(hasKey("status")));
|
||||
}
|
||||
|
||||
public void testGetNotFoundOnNonExistingIndex() throws Exception {
|
||||
Exception e = expectThrows(Exception.class, () -> watcherClient().getWatch(new GetWatchRequest("_name")).get());
|
||||
assertThat(e.getMessage(), containsString("no such index"));
|
||||
}
|
||||
|
||||
public void testGetNotFound() throws Exception {
|
||||
assertAcked(client().admin().indices().prepareCreate(Watch.INDEX));
|
||||
GetWatchResponse getResponse = watcherClient().getWatch(new GetWatchRequest("_name")).get();
|
||||
assertThat(getResponse, notNullValue());
|
||||
assertThat(getResponse.getId(), is("_name"));
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.common.util.concurrent.EsExecutors;
|
|||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.LatchScriptEngine;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||
import org.elasticsearch.xpack.watcher.WatcherState;
|
||||
import org.elasticsearch.xpack.watcher.actions.ActionBuilders;
|
||||
import org.elasticsearch.xpack.watcher.condition.ScriptCondition;
|
||||
|
@ -47,6 +48,11 @@ public class WatchStatsTests extends AbstractWatcherIntegrationTestCase {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean enableSecurity() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<? extends Plugin>> nodePlugins() {
|
||||
List<Class<? extends Plugin>> plugins = new ArrayList<>(super.nodePlugins());
|
||||
|
@ -73,6 +79,7 @@ public class WatchStatsTests extends AbstractWatcherIntegrationTestCase {
|
|||
getLatchScriptEngine().finishScriptExecution();
|
||||
}
|
||||
|
||||
@TestLogging("org.elasticsearch.xpack.watcher.trigger.schedule.engine:TRACE,org.elasticsearch.xpack.scheduler:TRACE")
|
||||
public void testCurrentWatches() throws Exception {
|
||||
watcherClient().preparePutWatch("_id").setSource(watchBuilder()
|
||||
.trigger(schedule(interval("1s")))
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.daily;
|
||||
import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.interval;
|
||||
|
@ -184,17 +185,42 @@ public abstract class BaseTriggerEngineTestCase extends ESTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testAddSameJobSeveralTimes() {
|
||||
public void testAddSameJobSeveralTimesAndExecutedOnce() throws InterruptedException {
|
||||
engine.start(Collections.emptySet());
|
||||
engine.register(events -> logger.info("triggered job"));
|
||||
|
||||
final CountDownLatch firstLatch = new CountDownLatch(1);
|
||||
final CountDownLatch secondLatch = new CountDownLatch(1);
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
engine.register(events -> {
|
||||
events.forEach(event -> {
|
||||
if (counter.getAndIncrement() == 0) {
|
||||
firstLatch.countDown();
|
||||
} else {
|
||||
secondLatch.countDown();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
int times = scaledRandomIntBetween(3, 30);
|
||||
for (int i = 0; i < times; i++) {
|
||||
engine.add(new SimpleJob("_id", interval("10s")));
|
||||
}
|
||||
engine.add(new SimpleJob("_id", interval("1s")));
|
||||
}
|
||||
|
||||
static class SimpleJob implements TriggerEngine.Job {
|
||||
advanceClockIfNeeded(new DateTime(clock.millis(), UTC).plusMillis(1100));
|
||||
if (!firstLatch.await(3, TimeUnit.SECONDS)) {
|
||||
fail("waiting too long for all watches to be triggered");
|
||||
}
|
||||
|
||||
advanceClockIfNeeded(new DateTime(clock.millis(), UTC).plusMillis(1100));
|
||||
if (!secondLatch.await(3, TimeUnit.SECONDS)) {
|
||||
fail("waiting too long for all watches to be triggered");
|
||||
}
|
||||
|
||||
// ensure job was only called twice independent from its name
|
||||
assertThat(counter.get(), is(2));
|
||||
}
|
||||
|
||||
public static class SimpleJob implements TriggerEngine.Job {
|
||||
|
||||
private final String name;
|
||||
private final ScheduleTrigger trigger;
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.watcher.watch;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.watcher.actions.ActionStatus;
|
||||
import org.elasticsearch.xpack.watcher.actions.ActionStatus.AckStatus.State;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.joda.time.DateTime.now;
|
||||
|
||||
public class WatchStatusTests extends ESTestCase {
|
||||
|
||||
public void testThatWatchStatusDirtyOnConditionCheck() throws Exception {
|
||||
// no actions, met condition
|
||||
WatchStatus status = new WatchStatus(now(), new HashMap<>());
|
||||
status.onCheck(true, now());
|
||||
assertThat(status.dirty(), is(true));
|
||||
|
||||
// no actions, unmet condition
|
||||
status = new WatchStatus(now(), new HashMap<>());
|
||||
status.onCheck(false, now());
|
||||
assertThat(status.dirty(), is(true));
|
||||
|
||||
// actions, no action with reset ack status, unmet condition
|
||||
Map<String, ActionStatus > actions = new HashMap<>();
|
||||
actions.put(randomAsciiOfLength(10), new ActionStatus(now()));
|
||||
status = new WatchStatus(now(), actions);
|
||||
status.onCheck(false, now());
|
||||
assertThat(status.dirty(), is(true));
|
||||
|
||||
// actions, one action with state other than AWAITS_SUCCESSFUL_EXECUTION, unmet condition
|
||||
actions.clear();
|
||||
ActionStatus.AckStatus ackStatus = new ActionStatus.AckStatus(now(), randomFrom(State.ACKED, State.ACKABLE));
|
||||
actions.put(randomAsciiOfLength(10), new ActionStatus(ackStatus, null, null, null));
|
||||
actions.put(randomAsciiOfLength(11), new ActionStatus(now()));
|
||||
status = new WatchStatus(now(), actions);
|
||||
status.onCheck(false, now());
|
||||
assertThat(status.dirty(), is(true));
|
||||
|
||||
status.resetDirty();
|
||||
assertThat(status.dirty(), is(false));
|
||||
}
|
||||
}
|
|
@ -1,528 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.watcher.watch;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
|
||||
import org.elasticsearch.action.search.ClearScrollResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.routing.IndexRoutingTable;
|
||||
import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
|
||||
import org.elasticsearch.cluster.routing.RoutingTable;
|
||||
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
||||
import org.elasticsearch.cluster.routing.TestShardRouting;
|
||||
import org.elasticsearch.cluster.routing.UnassignedInfo;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.text.Text;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.shard.ShardId;
|
||||
import org.elasticsearch.search.SearchHitField;
|
||||
import org.elasticsearch.search.internal.InternalSearchHit;
|
||||
import org.elasticsearch.search.internal.InternalSearchHits;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.watcher.actions.ActionWrapper;
|
||||
import org.elasticsearch.xpack.watcher.actions.ExecutableAction;
|
||||
import org.elasticsearch.xpack.watcher.condition.AlwaysCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.NeverCondition;
|
||||
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.input.none.ExecutableNoneInput;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
|
||||
import org.elasticsearch.xpack.watcher.transform.ExecutableTransform;
|
||||
import org.elasticsearch.xpack.watcher.transform.Transform;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.Schedule;
|
||||
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class WatchStoreTests extends ESTestCase {
|
||||
private WatchStore watchStore;
|
||||
private WatcherClientProxy clientProxy;
|
||||
private Watch.Parser parser;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
clientProxy = mock(WatcherClientProxy.class);
|
||||
parser = mock(Watch.Parser.class);
|
||||
watchStore = new WatchStore(Settings.EMPTY, clientProxy, parser);
|
||||
}
|
||||
|
||||
public void testStartNoPreviousWatchesIndex() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
MetaData.Builder metaDataBuilder = MetaData.builder();
|
||||
csBuilder.metaData(metaDataBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
assertThat(watchStore.validate(cs), is(true));
|
||||
watchStore.start(cs);
|
||||
assertThat(watchStore.started(), is(true));
|
||||
assertThat(watchStore.watches().size(), equalTo(0));
|
||||
verifyZeroInteractions(clientProxy);
|
||||
|
||||
watchStore.start(cs);
|
||||
verifyZeroInteractions(clientProxy);
|
||||
}
|
||||
|
||||
public void testStartPrimaryShardNotReady() {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
MetaData.Builder metaDataBuilder = MetaData.builder();
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
Settings settings = settings(Version.CURRENT)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDataBuilder.put(IndexMetaData.builder(WatchStore.INDEX).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
final Index index = metaDataBuilder.get(WatchStore.INDEX).getIndex();
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(index);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(index, 0))
|
||||
.addShard(TestShardRouting.newShardRouting(WatchStore.INDEX, 0, "_node_id", null, true,
|
||||
ShardRoutingState.UNASSIGNED, new UnassignedInfo(UnassignedInfo.Reason.INDEX_CREATED, "")))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDataBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder.build());
|
||||
|
||||
ClusterState cs = csBuilder.build();
|
||||
assertThat(watchStore.validate(cs), is(false));
|
||||
verifyZeroInteractions(clientProxy);
|
||||
}
|
||||
|
||||
public void testStartRefreshFailed() {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
createWatchIndexMetaData(csBuilder);
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 0);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
assertThat(watchStore.validate(cs), is(true));
|
||||
try {
|
||||
watchStore.start(cs);
|
||||
} catch (Exception e) {
|
||||
assertThat(e.getMessage(), equalTo("not all required shards have been refreshed"));
|
||||
}
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
verify(clientProxy, never()).search(any(SearchRequest.class), any(TimeValue.class));
|
||||
verify(clientProxy, never()).clearScroll(anyString());
|
||||
}
|
||||
|
||||
public void testStartSearchFailed() {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
createWatchIndexMetaData(csBuilder);
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
SearchResponse searchResponse = mockSearchResponse(1, 0, 0);
|
||||
when(clientProxy.search(any(SearchRequest.class), any(TimeValue.class))).thenReturn(searchResponse);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 0));
|
||||
|
||||
ClusterState cs = csBuilder.build();
|
||||
assertThat(watchStore.validate(cs), is(true));
|
||||
try {
|
||||
watchStore.start(cs);
|
||||
} catch (Exception e) {
|
||||
assertThat(e.getMessage(), equalTo("Partial response while loading watches"));
|
||||
}
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
verify(clientProxy, times(1)).search(any(SearchRequest.class), any(TimeValue.class));
|
||||
verify(clientProxy, times(1)).clearScroll(anyString());
|
||||
}
|
||||
|
||||
public void testStartNoWatchStored() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
createWatchIndexMetaData(csBuilder);
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
SearchResponse searchResponse = mockSearchResponse(1, 1, 0);
|
||||
when(clientProxy.search(any(SearchRequest.class), any(TimeValue.class))).thenReturn(searchResponse);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 0));
|
||||
|
||||
ClusterState cs = csBuilder.build();
|
||||
assertThat(watchStore.validate(cs), is(true));
|
||||
watchStore.start(cs);
|
||||
assertThat(watchStore.started(), is(true));
|
||||
assertThat(watchStore.watches().size(), equalTo(0));
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
verify(clientProxy, times(1)).search(any(SearchRequest.class), any(TimeValue.class));
|
||||
verify(clientProxy, times(1)).clearScroll(anyString());
|
||||
}
|
||||
|
||||
public void testStartWatchStored() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
createWatchIndexMetaData(csBuilder);
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
BytesReference source = new BytesArray("{}");
|
||||
InternalSearchHit hit1 = new InternalSearchHit(0, "_id1", new Text("type"), Collections.<String, SearchHitField>emptyMap());
|
||||
hit1.sourceRef(source);
|
||||
InternalSearchHit hit2 = new InternalSearchHit(1, "_id2", new Text("type"), Collections.<String, SearchHitField>emptyMap());
|
||||
hit2.sourceRef(source);
|
||||
SearchResponse searchResponse1 = mockSearchResponse(1, 1, 2, hit1, hit2);
|
||||
|
||||
when(clientProxy.search(any(SearchRequest.class), any(TimeValue.class))).thenReturn(searchResponse1);
|
||||
|
||||
InternalSearchHit hit3 = new InternalSearchHit(2, "_id3", new Text("type"), Collections.<String, SearchHitField>emptyMap());
|
||||
hit3.sourceRef(source);
|
||||
InternalSearchHit hit4 = new InternalSearchHit(3, "_id4", new Text("type"), Collections.<String, SearchHitField>emptyMap());
|
||||
hit4.sourceRef(source);
|
||||
SearchResponse searchResponse2 = mockSearchResponse(1, 1, 2, hit3, hit4);
|
||||
SearchResponse searchResponse3 = mockSearchResponse(1, 1, 2);
|
||||
when(clientProxy.searchScroll(anyString(), any(TimeValue.class))).thenReturn(searchResponse2, searchResponse3);
|
||||
|
||||
Watch watch1 = mock(Watch.class);
|
||||
WatchStatus status = mock(WatchStatus.class);
|
||||
when(watch1.status()).thenReturn(status);
|
||||
Watch watch2 = mock(Watch.class);
|
||||
when(watch2.status()).thenReturn(status);
|
||||
Watch watch3 = mock(Watch.class);
|
||||
when(watch3.status()).thenReturn(status);
|
||||
Watch watch4 = mock(Watch.class);
|
||||
when(watch4.status()).thenReturn(status);
|
||||
when(parser.parse("_id1", true, source)).thenReturn(watch1);
|
||||
when(parser.parse("_id2", true, source)).thenReturn(watch2);
|
||||
when(parser.parse("_id3", true, source)).thenReturn(watch3);
|
||||
when(parser.parse("_id4", true, source)).thenReturn(watch4);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 0));
|
||||
|
||||
ClusterState cs = csBuilder.build();
|
||||
assertThat(watchStore.validate(cs), is(true));
|
||||
watchStore.start(cs);
|
||||
assertThat(watchStore.started(), is(true));
|
||||
assertThat(watchStore.watches().size(), equalTo(4));
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
verify(clientProxy, times(1)).search(any(SearchRequest.class), any(TimeValue.class));
|
||||
verify(clientProxy, times(2)).searchScroll(anyString(), any(TimeValue.class));
|
||||
verify(clientProxy, times(1)).clearScroll(anyString());
|
||||
}
|
||||
|
||||
public void testUsageStats() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
createWatchIndexMetaData(csBuilder);
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
BytesReference source = new BytesArray("{}");
|
||||
int hitCount = randomIntBetween(50, 100);
|
||||
int activeHitCount = 0;
|
||||
|
||||
List<InternalSearchHit> hits = new ArrayList<>();
|
||||
for (int i = 0; i < hitCount; i++) {
|
||||
InternalSearchHit hit = new InternalSearchHit(0, "_id" + i, new Text("type"), Collections.<String, SearchHitField>emptyMap());
|
||||
hits.add(hit.sourceRef(source));
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
WatchStatus status = mock(WatchStatus.class);
|
||||
when(watch.status()).thenReturn(status);
|
||||
|
||||
boolean isActive = usually();
|
||||
WatchStatus.State state = mock(WatchStatus.State.class);
|
||||
when(state.isActive()).thenReturn(isActive);
|
||||
when(status.state()).thenReturn(state);
|
||||
if (isActive) {
|
||||
activeHitCount++;
|
||||
}
|
||||
|
||||
// random schedule
|
||||
ScheduleTrigger mockTricker = mock(ScheduleTrigger.class);
|
||||
when(watch.trigger()).thenReturn(mockTricker);
|
||||
when(mockTricker.type()).thenReturn("schedule");
|
||||
String scheduleType = randomFrom("a", "b", "c");
|
||||
Schedule mockSchedule = mock(Schedule.class);
|
||||
when(mockSchedule.type()).thenReturn(scheduleType);
|
||||
when(mockTricker.getSchedule()).thenReturn(mockSchedule);
|
||||
|
||||
// either a none input, or null
|
||||
when(watch.input()).thenReturn(randomFrom(new ExecutableNoneInput(logger), null));
|
||||
|
||||
// random conditions
|
||||
when(watch.condition()).thenReturn(randomFrom(AlwaysCondition.INSTANCE, null,
|
||||
NeverCondition.INSTANCE));
|
||||
|
||||
// random actions
|
||||
ActionWrapper actionWrapper = mock(ActionWrapper.class);
|
||||
ExecutableAction action = mock(ExecutableAction.class);
|
||||
when(actionWrapper.action()).thenReturn(action);
|
||||
when(action.type()).thenReturn(randomFrom("a", "b", "c"));
|
||||
when(watch.actions()).thenReturn(Arrays.asList(actionWrapper));
|
||||
|
||||
// random transform, not always set
|
||||
Transform mockTransform = mock(Transform.class);
|
||||
when(mockTransform.type()).thenReturn("TYPE");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ExecutableTransform testTransform = new ExecutableTransform(mockTransform, logger) {
|
||||
@Override
|
||||
public Transform.Result execute(WatchExecutionContext ctx, Payload payload) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
when(watch.transform()).thenReturn(randomFrom(testTransform, null));
|
||||
|
||||
when(parser.parse("_id" + i, true, source)).thenReturn(watch);
|
||||
}
|
||||
|
||||
SearchResponse searchResponse = mockSearchResponse(1, 1, hitCount, hits.toArray(new InternalSearchHit[] {}));
|
||||
when(clientProxy.search(any(SearchRequest.class), any(TimeValue.class))).thenReturn(searchResponse);
|
||||
SearchResponse noHitsResponse = mockSearchResponse(1, 1, 2);
|
||||
when(clientProxy.searchScroll(anyString(), any(TimeValue.class))).thenReturn(noHitsResponse);
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 0));
|
||||
|
||||
ClusterState cs = csBuilder.build();
|
||||
watchStore.start(cs);
|
||||
|
||||
XContentSource stats = new XContentSource(jsonBuilder().map(watchStore.usageStats()));
|
||||
|
||||
assertThat(stats.getValue("count.total"), is(hitCount));
|
||||
assertThat(stats.getValue("count.active"), is(activeHitCount));
|
||||
|
||||
// schedule count
|
||||
int scheduleCountA = stats.getValue("watch.trigger.schedule.a.active");
|
||||
int scheduleCountB = stats.getValue("watch.trigger.schedule.b.active");
|
||||
int scheduleCountC = stats.getValue("watch.trigger.schedule.c.active");
|
||||
assertThat(scheduleCountA + scheduleCountB + scheduleCountC, is(activeHitCount));
|
||||
|
||||
// input count
|
||||
assertThat(stats.getValue("watch.input.none.active"), is(greaterThan(0)));
|
||||
assertThat(stats.getValue("watch.input.none.total"), is(greaterThan(0)));
|
||||
assertThat(stats.getValue("watch.input.none.total"), is(lessThan(activeHitCount)));
|
||||
|
||||
// condition count
|
||||
assertThat(stats.getValue("watch.condition.never.active"), is(greaterThan(0)));
|
||||
assertThat(stats.getValue("watch.condition.always.active"), is(greaterThan(0)));
|
||||
|
||||
// action count
|
||||
int actionCountA = stats.getValue("watch.action.a.active");
|
||||
int actionCountB = stats.getValue("watch.action.b.active");
|
||||
int actionCountC = stats.getValue("watch.action.c.active");
|
||||
assertThat(actionCountA + actionCountB + actionCountC, is(activeHitCount));
|
||||
|
||||
// transform count
|
||||
assertThat(stats.getValue("watch.transform.TYPE.active"), is(greaterThan(0)));
|
||||
}
|
||||
|
||||
public void testThatCleaningWatchesWorks() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
createWatchIndexMetaData(csBuilder);
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
BytesReference source = new BytesArray("{}");
|
||||
InternalSearchHit hit = new InternalSearchHit(0, "_id1", new Text("type"), Collections.emptyMap());
|
||||
hit.sourceRef(source);
|
||||
|
||||
SearchResponse searchResponse = mockSearchResponse(1, 1, 1, hit);
|
||||
when(clientProxy.search(any(SearchRequest.class), any(TimeValue.class))).thenReturn(searchResponse);
|
||||
|
||||
SearchResponse finalSearchResponse = mockSearchResponse(1, 1, 0);
|
||||
when(clientProxy.searchScroll(anyString(), any(TimeValue.class))).thenReturn(finalSearchResponse);
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
WatchStatus status = mock(WatchStatus.class);
|
||||
when(watch.status()).thenReturn(status);
|
||||
when(parser.parse("_id1", true, source)).thenReturn(watch);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 0));
|
||||
|
||||
ClusterState cs = csBuilder.build();
|
||||
assertThat(watchStore.validate(cs), is(true));
|
||||
watchStore.start(cs);
|
||||
assertThat(watchStore.started(), is(true));
|
||||
assertThat(watchStore.watches(), hasSize(1));
|
||||
|
||||
watchStore.clearWatchesInMemory();
|
||||
assertThat(watchStore.started(), is(true));
|
||||
assertThat(watchStore.watches(), hasSize(0));
|
||||
assertThat(watchStore.activeWatches(), hasSize(0));
|
||||
}
|
||||
|
||||
// the elasticsearch migration helper is doing reindex using aliases, so we have to
|
||||
// make sure that the watch store supports a single alias pointing to the watch index
|
||||
public void testThatStartingWithWatchesIndexAsAliasWorks() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
|
||||
MetaData.Builder metaDataBuilder = MetaData.builder();
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
Settings settings = settings(Version.CURRENT)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDataBuilder.put(IndexMetaData.builder("watches-alias").settings(settings).numberOfShards(1).numberOfReplicas(1)
|
||||
.putAlias(new AliasMetaData.Builder(WatchStore.INDEX).build()));
|
||||
|
||||
final Index index = metaDataBuilder.get("watches-alias").getIndex();
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(index);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(index, 0))
|
||||
.addShard(TestShardRouting.newShardRouting("watches-alias", 0, "_node_id", null, true, ShardRoutingState.STARTED))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDataBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder.build());
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
BytesReference source = new BytesArray("{}");
|
||||
InternalSearchHit hit1 = new InternalSearchHit(0, "_id1", new Text("type"), Collections.emptyMap());
|
||||
hit1.sourceRef(source);
|
||||
InternalSearchHit hit2 = new InternalSearchHit(1, "_id2", new Text("type"), Collections.emptyMap());
|
||||
hit2.sourceRef(source);
|
||||
SearchResponse searchResponse1 = mockSearchResponse(1, 1, 2, hit1, hit2);
|
||||
|
||||
when(clientProxy.search(any(SearchRequest.class), any(TimeValue.class))).thenReturn(searchResponse1);
|
||||
|
||||
InternalSearchHit hit3 = new InternalSearchHit(2, "_id3", new Text("type"), Collections.emptyMap());
|
||||
hit3.sourceRef(source);
|
||||
InternalSearchHit hit4 = new InternalSearchHit(3, "_id4", new Text("type"), Collections.emptyMap());
|
||||
hit4.sourceRef(source);
|
||||
SearchResponse searchResponse2 = mockSearchResponse(1, 1, 2, hit3, hit4);
|
||||
SearchResponse searchResponse3 = mockSearchResponse(1, 1, 2);
|
||||
when(clientProxy.searchScroll(anyString(), any(TimeValue.class))).thenReturn(searchResponse2, searchResponse3);
|
||||
|
||||
Watch watch1 = mock(Watch.class);
|
||||
WatchStatus status = mock(WatchStatus.class);
|
||||
when(watch1.status()).thenReturn(status);
|
||||
Watch watch2 = mock(Watch.class);
|
||||
when(watch2.status()).thenReturn(status);
|
||||
Watch watch3 = mock(Watch.class);
|
||||
when(watch3.status()).thenReturn(status);
|
||||
Watch watch4 = mock(Watch.class);
|
||||
when(watch4.status()).thenReturn(status);
|
||||
when(parser.parse("_id1", true, source)).thenReturn(watch1);
|
||||
when(parser.parse("_id2", true, source)).thenReturn(watch2);
|
||||
when(parser.parse("_id3", true, source)).thenReturn(watch3);
|
||||
when(parser.parse("_id4", true, source)).thenReturn(watch4);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 0));
|
||||
|
||||
ClusterState cs = csBuilder.build();
|
||||
assertThat(watchStore.validate(cs), is(true));
|
||||
watchStore.start(cs);
|
||||
assertThat(watchStore.started(), is(true));
|
||||
assertThat(watchStore.watches().size(), equalTo(4));
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
verify(clientProxy, times(1)).search(any(SearchRequest.class), any(TimeValue.class));
|
||||
verify(clientProxy, times(1)).clearScroll(anyString());
|
||||
}
|
||||
|
||||
// the elasticsearch migration helper is doing reindex using aliases, so we have to
|
||||
// make sure that the watch store supports only a single index in an alias
|
||||
public void testThatWatchesIndexWithTwoAliasesFails() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
|
||||
MetaData.Builder metaDataBuilder = MetaData.builder();
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
Settings settings = settings(Version.CURRENT)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDataBuilder.put(IndexMetaData.builder("watches-alias").settings(settings).numberOfShards(1).numberOfReplicas(1)
|
||||
.putAlias(new AliasMetaData.Builder(WatchStore.INDEX).build()));
|
||||
metaDataBuilder.put(IndexMetaData.builder("whatever").settings(settings).numberOfShards(1).numberOfReplicas(1)
|
||||
.putAlias(new AliasMetaData.Builder(WatchStore.INDEX).build()));
|
||||
|
||||
final Index index = metaDataBuilder.get("watches-alias").getIndex();
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(index);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(index, 0))
|
||||
.addShard(TestShardRouting.newShardRouting("watches-alias", 0, "_node_id", null, true, ShardRoutingState.STARTED))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
final Index otherIndex = metaDataBuilder.get("whatever").getIndex();
|
||||
IndexRoutingTable.Builder otherIndexRoutingTableBuilder = IndexRoutingTable.builder(otherIndex);
|
||||
otherIndexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(index, 0))
|
||||
.addShard(TestShardRouting.newShardRouting("whatever", 0, "_node_id", null, true, ShardRoutingState.STARTED))
|
||||
.build());
|
||||
otherIndexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(otherIndexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDataBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder.build());
|
||||
|
||||
ClusterState cs = csBuilder.build();
|
||||
assertThat(watchStore.validate(cs), is(false));
|
||||
IllegalStateException exception = expectThrows(IllegalStateException.class, () -> watchStore.start(cs));
|
||||
assertThat(exception.getMessage(), is("Alias [.watches] points to more than one index"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates the standard cluster state metadata for the watches index
|
||||
* with shards/replicas being marked as started
|
||||
*/
|
||||
private void createWatchIndexMetaData(ClusterState.Builder builder) {
|
||||
MetaData.Builder metaDataBuilder = MetaData.builder();
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
Settings settings = settings(Version.CURRENT)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDataBuilder.put(IndexMetaData.builder(WatchStore.INDEX).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
final Index index = metaDataBuilder.get(WatchStore.INDEX).getIndex();
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(index);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(index, 0))
|
||||
.addShard(TestShardRouting.newShardRouting(WatchStore.INDEX, 0, "_node_id", null, true, ShardRoutingState.STARTED))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
builder.metaData(metaDataBuilder);
|
||||
builder.routingTable(routingTableBuilder.build());
|
||||
}
|
||||
|
||||
private RefreshResponse mockRefreshResponse(int total, int successful) {
|
||||
RefreshResponse refreshResponse = mock(RefreshResponse.class);
|
||||
when(refreshResponse.getTotalShards()).thenReturn(total);
|
||||
when(refreshResponse.getSuccessfulShards()).thenReturn(successful);
|
||||
return refreshResponse;
|
||||
}
|
||||
|
||||
private SearchResponse mockSearchResponse(int total, int successful, int totalHits, InternalSearchHit... hits) {
|
||||
InternalSearchHits internalSearchHits = new InternalSearchHits(hits, totalHits, 1f);
|
||||
SearchResponse searchResponse = mock(SearchResponse.class);
|
||||
when(searchResponse.getTotalShards()).thenReturn(total);
|
||||
when(searchResponse.getSuccessfulShards()).thenReturn(successful);
|
||||
when(searchResponse.getHits()).thenReturn(internalSearchHits);
|
||||
return searchResponse;
|
||||
}
|
||||
}
|
|
@ -35,10 +35,6 @@
|
|||
|
||||
- match: { _id: "my_watch" }
|
||||
|
||||
- do:
|
||||
cluster.health:
|
||||
wait_for_status: yellow
|
||||
|
||||
- do:
|
||||
xpack.watcher.get_watch:
|
||||
id: "my_watch"
|
||||
|
@ -51,7 +47,7 @@
|
|||
xpack.watcher.deactivate_watch:
|
||||
watch_id: "my_watch"
|
||||
|
||||
- match: { "_status.state.active" : false }
|
||||
- match: { _status.state.active : false }
|
||||
|
||||
- do:
|
||||
xpack.watcher.get_watch:
|
||||
|
@ -64,7 +60,7 @@
|
|||
xpack.watcher.activate_watch:
|
||||
watch_id: "my_watch"
|
||||
|
||||
- match: { "_status.state.active" : true }
|
||||
- match: { _status.state.active : true }
|
||||
|
||||
- do:
|
||||
xpack.watcher.get_watch:
|
||||
|
|
|
@ -52,5 +52,5 @@ teardown:
|
|||
id: "my_watch"
|
||||
- match: { found : true}
|
||||
- match: { _id: "my_watch" }
|
||||
- is_true: _status.version
|
||||
- is_true: watch
|
||||
- is_false: watch.status
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
cluster.health:
|
||||
wait_for_status: yellow
|
||||
|
||||
- do:
|
||||
indices.create:
|
||||
index: .watches
|
||||
|
||||
- do:
|
||||
catch: missing
|
||||
|
|
|
@ -93,8 +93,6 @@
|
|||
- is_true: graph.available
|
||||
- is_true: monitoring.enabled
|
||||
- is_true: monitoring.available
|
||||
- gte: { watcher.count.total: 0 }
|
||||
- gte: { watcher.count.active: 0 }
|
||||
|
||||
- do:
|
||||
xpack.info:
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
cluster.health:
|
||||
wait_for_status: yellow
|
||||
|
||||
|
||||
- do: {xpack.watcher.stats:{}}
|
||||
- match: { "watcher_state": "started" }
|
||||
- match: { "watch_count": 0 }
|
||||
|
||||
- do:
|
||||
xpack.watcher.put_watch:
|
||||
id: "test_watch"
|
||||
|
@ -47,9 +42,6 @@
|
|||
- match: { _id: "test_watch" }
|
||||
- match: { created: true }
|
||||
|
||||
- do: {xpack.watcher.stats:{}}
|
||||
- match: { "watch_count": 1 }
|
||||
|
||||
# Simulate a Thread.sleep()
|
||||
- do:
|
||||
catch: request_timeout
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
cluster.health:
|
||||
wait_for_status: yellow
|
||||
|
||||
|
||||
- do: {xpack.watcher.stats:{}}
|
||||
- match: { "watcher_state": "started" }
|
||||
- match: { "watch_count": 0 }
|
||||
|
||||
- do:
|
||||
xpack.watcher.put_watch:
|
||||
id: "test_watch"
|
||||
|
@ -45,9 +40,6 @@
|
|||
- match: { _id: "test_watch" }
|
||||
- match: { created: true }
|
||||
|
||||
- do: {xpack.watcher.stats:{}}
|
||||
- match: { "watch_count": 1 }
|
||||
|
||||
# Simulate a Thread.sleep()
|
||||
- do:
|
||||
catch: request_timeout
|
||||
|
|
|
@ -10,7 +10,7 @@ watcher_manager:
|
|||
cluster:
|
||||
- manage
|
||||
indices:
|
||||
- names: '.watcher-history-*'
|
||||
- names: '.watch*'
|
||||
privileges:
|
||||
- all
|
||||
run_as:
|
||||
|
|
|
@ -64,6 +64,10 @@ teardown:
|
|||
- match: { _id: "cluster_health_watch" }
|
||||
- match: { created: true }
|
||||
|
||||
- do:
|
||||
indices.refresh:
|
||||
index: .watches
|
||||
|
||||
- do:
|
||||
xpack.watcher.stats: {}
|
||||
- match: { "watch_count": 1 }
|
||||
|
@ -113,6 +117,9 @@ teardown:
|
|||
id: "cluster_health_watch"
|
||||
- match: { found: true }
|
||||
|
||||
- do:
|
||||
indices.refresh:
|
||||
index: .watches
|
||||
|
||||
- do:
|
||||
xpack.watcher.stats: {}
|
||||
|
|
Loading…
Reference in New Issue