Merge branch 'master' into feature/sql

Original commit: elastic/x-pack-elasticsearch@d24c740fe3
This commit is contained in:
Nik Everett 2017-08-03 15:19:43 -04:00
commit 8a7a724179
27 changed files with 721 additions and 680 deletions

View File

@ -29,6 +29,7 @@ import org.elasticsearch.xpack.ml.datafeed.DatafeedUpdate;
import org.elasticsearch.xpack.ml.job.config.Job;
import org.elasticsearch.xpack.ml.job.config.JobState;
import org.elasticsearch.xpack.ml.job.config.JobTaskStatus;
import org.elasticsearch.xpack.ml.job.groups.GroupOrJobLookup;
import org.elasticsearch.xpack.ml.job.messages.Messages;
import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.ml.utils.NameResolver;
@ -66,10 +67,12 @@ public class MlMetadata implements MetaData.Custom {
private final SortedMap<String, Job> jobs;
private final SortedMap<String, DatafeedConfig> datafeeds;
private final GroupOrJobLookup groupOrJobLookup;
private MlMetadata(SortedMap<String, Job> jobs, SortedMap<String, DatafeedConfig> datafeeds) {
this.jobs = Collections.unmodifiableSortedMap(jobs);
this.datafeeds = Collections.unmodifiableSortedMap(datafeeds);
this.groupOrJobLookup = new GroupOrJobLookup(jobs.values());
}
public Map<String, Job> getJobs() {
@ -77,8 +80,7 @@ public class MlMetadata implements MetaData.Custom {
}
public Set<String> expandJobIds(String expression, boolean allowNoJobs) {
return NameResolver.newUnaliased(jobs.keySet(), jobId -> ExceptionsHelper.missingJobException(jobId))
.expand(expression, allowNoJobs);
return groupOrJobLookup.expandJobIds(expression, allowNoJobs);
}
public boolean isJobDeleted(String jobId) {
@ -136,6 +138,8 @@ public class MlMetadata implements MetaData.Custom {
datafeeds.put(in.readString(), new DatafeedConfig(in));
}
this.datafeeds = datafeeds;
this.groupOrJobLookup = new GroupOrJobLookup(jobs.values());
}
@Override

View File

@ -48,5 +48,13 @@ public final class DatafeedJobValidator {
TimeValue.timeValueMillis(histogramIntervalMillis).getStringRep(),
TimeValue.timeValueMillis(bucketSpanMillis).getStringRep()));
}
if (bucketSpanMillis % histogramIntervalMillis != 0) {
throw ExceptionsHelper.badRequestException(Messages.getMessage(
Messages.DATAFEED_AGGREGATIONS_INTERVAL_MUST_BE_DIVISOR_OF_BUCKET_SPAN,
TimeValue.timeValueMillis(histogramIntervalMillis).getStringRep(),
TimeValue.timeValueMillis(bucketSpanMillis).getStringRep()));
}
}
}

View File

@ -115,13 +115,10 @@ class AggregationDataExtractor implements DataExtractor {
}
private SearchRequestBuilder buildSearchRequest() {
long histogramSearchStartTime = context.start;
if (context.aggs.getPipelineAggregatorFactories().isEmpty() == false) {
// For derivative aggregations the first bucket will always be null
// so query one extra histogram bucket back and hope there is data
// in that bucket
histogramSearchStartTime = Math.max(0, context.start - getHistogramInterval());
}
// For derivative aggregations the first bucket will always be null
// so query one extra histogram bucket back and hope there is data
// in that bucket
long histogramSearchStartTime = Math.max(0, context.start - getHistogramInterval());
SearchRequestBuilder searchRequestBuilder = SearchAction.INSTANCE.newRequestBuilder(client)
.setIndices(context.indices)

View File

@ -16,7 +16,6 @@ import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser.Token;
@ -29,6 +28,7 @@ import org.elasticsearch.xpack.ml.utils.time.TimeUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashSet;
@ -57,6 +57,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
public static final ParseField ID = new ParseField("job_id");
public static final ParseField JOB_TYPE = new ParseField("job_type");
public static final ParseField JOB_VERSION = new ParseField("job_version");
public static final ParseField GROUPS = new ParseField("groups");
public static final ParseField ANALYSIS_CONFIG = new ParseField("analysis_config");
public static final ParseField ANALYSIS_LIMITS = new ParseField("analysis_limits");
public static final ParseField CREATE_TIME = new ParseField("create_time");
@ -94,6 +95,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
parser.declareString(Builder::setId, ID);
parser.declareString(Builder::setJobType, JOB_TYPE);
parser.declareString(Builder::setJobVersion, JOB_VERSION);
parser.declareStringArray(Builder::setGroups, GROUPS);
parser.declareStringOrNull(Builder::setDescription, DESCRIPTION);
parser.declareField(Builder::setCreateTime, p -> {
if (p.currentToken() == Token.VALUE_NUMBER) {
@ -148,6 +150,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
@Nullable
private final Version jobVersion;
private final List<String> groups;
private final String description;
// TODO: Use java.time for the Dates here: x-pack-elasticsearch#829
private final Date createTime;
@ -166,16 +169,17 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
private final String resultsIndexName;
private final boolean deleted;
private Job(String jobId, String jobType, Version jobVersion, String description, Date createTime,
Date finishedTime, Date lastDataTime,
AnalysisConfig analysisConfig, AnalysisLimits analysisLimits, DataDescription dataDescription,
ModelPlotConfig modelPlotConfig, Long renormalizationWindowDays, TimeValue backgroundPersistInterval,
Long modelSnapshotRetentionDays, Long resultsRetentionDays, Map<String, Object> customSettings,
String modelSnapshotId, String resultsIndexName, boolean deleted) {
private Job(String jobId, String jobType, Version jobVersion, List<String> groups, String description, Date createTime,
Date finishedTime, Date lastDataTime,
AnalysisConfig analysisConfig, AnalysisLimits analysisLimits, DataDescription dataDescription,
ModelPlotConfig modelPlotConfig, Long renormalizationWindowDays, TimeValue backgroundPersistInterval,
Long modelSnapshotRetentionDays, Long resultsRetentionDays, Map<String, Object> customSettings,
String modelSnapshotId, String resultsIndexName, boolean deleted) {
this.jobId = jobId;
this.jobType = jobType;
this.jobVersion = jobVersion;
this.groups = groups;
this.description = description;
this.createTime = createTime;
this.finishedTime = finishedTime;
@ -202,6 +206,11 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
} else {
jobVersion = null;
}
if (in.getVersion().onOrAfter(Version.V_6_1_0)) {
groups = in.readList(StreamInput::readString);
} else {
groups = Collections.emptyList();
}
description = in.readOptionalString();
createTime = new Date(in.readVLong());
finishedTime = in.readBoolean() ? new Date(in.readVLong()) : null;
@ -237,6 +246,10 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
return jobVersion;
}
public List<String> getGroups() {
return groups;
}
/**
* The name of the index storing the job's results and state.
* This defaults to {@link #getId()} if a specific index name is not set.
@ -414,6 +427,9 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
out.writeBoolean(false);
}
}
if (out.getVersion().onOrAfter(Version.V_6_1_0)) {
out.writeStringList(groups);
}
out.writeOptionalString(description);
out.writeVLong(createTime.getTime());
if (finishedTime != null) {
@ -458,6 +474,9 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
if (jobVersion != null) {
builder.field(JOB_VERSION.getPreferredName(), jobVersion);
}
if (groups.isEmpty() == false) {
builder.field(GROUPS.getPreferredName(), groups);
}
if (description != null) {
builder.field(DESCRIPTION.getPreferredName(), description);
}
@ -519,6 +538,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
return Objects.equals(this.jobId, that.jobId)
&& Objects.equals(this.jobType, that.jobType)
&& Objects.equals(this.jobVersion, that.jobVersion)
&& Objects.equals(this.groups, that.groups)
&& Objects.equals(this.description, that.description)
&& Objects.equals(this.createTime, that.createTime)
&& Objects.equals(this.finishedTime, that.finishedTime)
@ -538,7 +558,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
@Override
public int hashCode() {
return Objects.hash(jobId, jobType, jobVersion, description, createTime, finishedTime, lastDataTime, analysisConfig,
return Objects.hash(jobId, jobType, jobVersion, groups, description, createTime, finishedTime, lastDataTime, analysisConfig,
analysisLimits, dataDescription, modelPlotConfig, renormalizationWindowDays,
backgroundPersistInterval, modelSnapshotRetentionDays, resultsRetentionDays, customSettings,
modelSnapshotId, resultsIndexName, deleted);
@ -574,6 +594,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
private String id;
private String jobType = ANOMALY_DETECTOR_JOB_TYPE;
private Version jobVersion;
private List<String> groups = Collections.emptyList();
private String description;
private AnalysisConfig analysisConfig;
private AnalysisLimits analysisLimits;
@ -602,6 +623,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
this.id = job.getId();
this.jobType = job.getJobType();
this.jobVersion = job.getJobVersion();
this.groups = job.getGroups();
this.description = job.getDescription();
this.analysisConfig = job.getAnalysisConfig();
this.analysisLimits = job.getAnalysisLimits();
@ -626,6 +648,11 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
if (in.getVersion().onOrAfter(Version.V_5_5_0)) {
jobVersion = in.readBoolean() ? Version.readVersion(in) : null;
}
if (in.getVersion().onOrAfter(Version.V_6_1_0)) {
groups = in.readList(StreamInput::readString);
} else {
groups = Collections.emptyList();
}
description = in.readOptionalString();
createTime = in.readBoolean() ? new Date(in.readVLong()) : null;
finishedTime = in.readBoolean() ? new Date(in.readVLong()) : null;
@ -665,6 +692,15 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
this.jobType = jobType;
}
public void setGroups(List<String> groups) {
this.groups = groups == null ? Collections.emptyList() : groups;
for (String group : this.groups) {
if (MlStrings.isValidId(group) == false) {
throw new IllegalArgumentException(Messages.getMessage(Messages.INVALID_GROUP, group));
}
}
}
public Date getCreateTime() {
return createTime;
}
@ -797,6 +833,9 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
out.writeBoolean(false);
}
}
if (out.getVersion().onOrAfter(Version.V_6_1_0)) {
out.writeStringList(groups);
}
out.writeOptionalString(description);
if (createTime != null) {
out.writeBoolean(true);
@ -1002,7 +1041,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
}
return new Job(
id, jobType, jobVersion, description, createTime, finishedTime, lastDataTime,
id, jobType, jobVersion, groups, description, createTime, finishedTime, lastDataTime,
analysisConfig, analysisLimits, dataDescription, modelPlotConfig, renormalizationWindowDays,
backgroundPersistInterval, modelSnapshotRetentionDays, resultsRetentionDays, customSettings,
modelSnapshotId, resultsIndexName, deleted);

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.xpack.ml.job.config;
import org.elasticsearch.Version;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
@ -19,6 +20,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -31,6 +34,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
static {
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), Job.ID);
PARSER.declareStringArray(Builder::setGroups, Job.GROUPS);
PARSER.declareStringOrNull(Builder::setDescription, Job.DESCRIPTION);
PARSER.declareObjectArray(Builder::setDetectorUpdates, DetectorUpdate.PARSER, DETECTORS);
PARSER.declareObject(Builder::setModelPlotConfig, ModelPlotConfig.CONFIG_PARSER, Job.MODEL_PLOT_CONFIG);
@ -46,6 +50,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
}
private final String jobId;
private final List<String> groups;
private final String description;
private final List<DetectorUpdate> detectorUpdates;
private final ModelPlotConfig modelPlotConfig;
@ -58,13 +63,14 @@ public class JobUpdate implements Writeable, ToXContentObject {
private final Map<String, Object> customSettings;
private final String modelSnapshotId;
private JobUpdate(String jobId, @Nullable String description, @Nullable List<DetectorUpdate> detectorUpdates,
@Nullable ModelPlotConfig modelPlotConfig, @Nullable AnalysisLimits analysisLimits,
@Nullable TimeValue backgroundPersistInterval, @Nullable Long renormalizationWindowDays,
@Nullable Long resultsRetentionDays, @Nullable Long modelSnapshotRetentionDays,
@Nullable List<String> categorisationFilters, @Nullable Map<String, Object> customSettings,
@Nullable String modelSnapshotId) {
private JobUpdate(String jobId, @Nullable List<String> groups, @Nullable String description,
@Nullable List<DetectorUpdate> detectorUpdates, @Nullable ModelPlotConfig modelPlotConfig,
@Nullable AnalysisLimits analysisLimits, @Nullable TimeValue backgroundPersistInterval,
@Nullable Long renormalizationWindowDays, @Nullable Long resultsRetentionDays,
@Nullable Long modelSnapshotRetentionDays, @Nullable List<String> categorisationFilters,
@Nullable Map<String, Object> customSettings, @Nullable String modelSnapshotId) {
this.jobId = jobId;
this.groups = groups;
this.description = description;
this.detectorUpdates = detectorUpdates;
this.modelPlotConfig = modelPlotConfig;
@ -80,6 +86,12 @@ public class JobUpdate implements Writeable, ToXContentObject {
public JobUpdate(StreamInput in) throws IOException {
jobId = in.readString();
if (in.getVersion().onOrAfter(Version.V_6_1_0)) {
String[] groupsArray = in.readOptionalStringArray();
groups = groupsArray == null ? null : Arrays.asList(groupsArray);
} else {
groups = null;
}
description = in.readOptionalString();
if (in.readBoolean()) {
detectorUpdates = in.readList(DetectorUpdate::new);
@ -100,9 +112,14 @@ public class JobUpdate implements Writeable, ToXContentObject {
customSettings = in.readMap();
modelSnapshotId = in.readOptionalString();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(jobId);
if (out.getVersion().onOrAfter(Version.V_6_1_0)) {
String[] groupsArray = groups == null ? null : groups.toArray(new String[groups.size()]);
out.writeOptionalStringArray(groupsArray);
}
out.writeOptionalString(description);
out.writeBoolean(detectorUpdates != null);
if (detectorUpdates != null) {
@ -126,6 +143,10 @@ public class JobUpdate implements Writeable, ToXContentObject {
return jobId;
}
public List<String> getGroups() {
return groups;
}
public String getDescription() {
return description;
}
@ -178,6 +199,9 @@ public class JobUpdate implements Writeable, ToXContentObject {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Job.ID.getPreferredName(), jobId);
if (groups != null) {
builder.field(Job.GROUPS.getPreferredName(), groups);
}
if (description != null) {
builder.field(Job.DESCRIPTION.getPreferredName(), description);
}
@ -223,6 +247,9 @@ public class JobUpdate implements Writeable, ToXContentObject {
*/
public Job mergeWithJob(Job source) {
Job.Builder builder = new Job.Builder(source);
if (groups != null) {
builder.setGroups(groups);
}
if (description != null) {
builder.setDescription(description);
}
@ -294,6 +321,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
JobUpdate that = (JobUpdate) other;
return Objects.equals(this.jobId, that.jobId)
&& Objects.equals(this.groups, that.groups)
&& Objects.equals(this.description, that.description)
&& Objects.equals(this.detectorUpdates, that.detectorUpdates)
&& Objects.equals(this.modelPlotConfig, that.modelPlotConfig)
@ -309,7 +337,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
@Override
public int hashCode() {
return Objects.hash(jobId, description, detectorUpdates, modelPlotConfig, analysisLimits, renormalizationWindowDays,
return Objects.hash(jobId, groups, description, detectorUpdates, modelPlotConfig, analysisLimits, renormalizationWindowDays,
backgroundPersistInterval, modelSnapshotRetentionDays, resultsRetentionDays, categorizationFilters, customSettings,
modelSnapshotId);
}
@ -410,6 +438,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
public static class Builder {
private String jobId;
private List<String> groups;
private String description;
private List<DetectorUpdate> detectorUpdates;
private ModelPlotConfig modelPlotConfig;
@ -431,6 +460,11 @@ public class JobUpdate implements Writeable, ToXContentObject {
return this;
}
public Builder setGroups(List<String> groups) {
this.groups = groups;
return this;
}
public Builder setDescription(String description) {
this.description = description;
return this;
@ -487,7 +521,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
}
public JobUpdate build() {
return new JobUpdate(jobId, description, detectorUpdates, modelPlotConfig, analysisLimits, backgroundPersistInterval,
return new JobUpdate(jobId, groups, description, detectorUpdates, modelPlotConfig, analysisLimits, backgroundPersistInterval,
renormalizationWindowDays, resultsRetentionDays, modelSnapshotRetentionDays, categorizationFilters, customSettings,
modelSnapshotId);
}

View File

@ -0,0 +1,59 @@
/*
* 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.ml.job.groups;
import org.elasticsearch.xpack.ml.job.config.Job;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* An interface to represent either a job or a group of jobs
*/
interface GroupOrJob {
boolean isGroup();
List<Job> jobs();
final class Group implements GroupOrJob {
private final List<Job> jobs;
Group(List<Job> jobs) {
this.jobs = Collections.unmodifiableList(jobs);
}
@Override
public boolean isGroup() {
return true;
}
@Override
public List<Job> jobs() {
return jobs;
}
}
final class SingleJob implements GroupOrJob {
private final Job job;
SingleJob(Job job) {
this.job = Objects.requireNonNull(job);
}
@Override
public boolean isGroup() {
return false;
}
@Override
public List<Job> jobs() {
return Collections.singletonList(job);
}
}
}

View File

@ -0,0 +1,87 @@
/*
* 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.ml.job.groups;
import org.elasticsearch.ResourceAlreadyExistsException;
import org.elasticsearch.xpack.ml.job.config.Job;
import org.elasticsearch.xpack.ml.job.messages.Messages;
import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;
import org.elasticsearch.xpack.ml.utils.NameResolver;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
/**
* A lookup that allows expanding expressions that may consist of job
* IDs, job group names, wildcard patterns or a comma separated combination
* of the aforementioned to the matching job IDs.
* The lookup is immutable.
*/
public class GroupOrJobLookup {
private final SortedMap<String, GroupOrJob> groupOrJobLookup;
public GroupOrJobLookup(Collection<Job> jobs) {
groupOrJobLookup = new TreeMap<>();
jobs.forEach(this::put);
}
private void put(Job job) {
if (groupOrJobLookup.containsKey(job.getId())) {
throw new ResourceAlreadyExistsException(Messages.getMessage(Messages.JOB_AND_GROUP_NAMES_MUST_BE_UNIQUE, job.getId()));
}
groupOrJobLookup.put(job.getId(), new GroupOrJob.SingleJob(job));
for (String groupName : job.getGroups()) {
GroupOrJob oldGroup = groupOrJobLookup.get(groupName);
if (oldGroup == null) {
groupOrJobLookup.put(groupName, new GroupOrJob.Group(Collections.singletonList(job)));
} else {
if (oldGroup.isGroup() == false) {
throw new ResourceAlreadyExistsException(Messages.getMessage(Messages.JOB_AND_GROUP_NAMES_MUST_BE_UNIQUE, groupName));
}
List<Job> groupJobs = new ArrayList<>(oldGroup.jobs());
groupJobs.add(job);
groupOrJobLookup.put(groupName, new GroupOrJob.Group(groupJobs));
}
}
}
public Set<String> expandJobIds(String expression, boolean allowNoJobs) {
return new GroupOrJobResolver().expand(expression, allowNoJobs);
}
private class GroupOrJobResolver extends NameResolver {
private GroupOrJobResolver() {
super(ExceptionsHelper::missingJobException);
}
@Override
protected Set<String> keys() {
return groupOrJobLookup.keySet();
}
@Override
protected Set<String> nameSet() {
return groupOrJobLookup.values().stream()
.filter(groupOrJob -> groupOrJob.isGroup() == false)
.map(groupOrJob -> groupOrJob.jobs().get(0).getId())
.collect(Collectors.toSet());
}
@Override
protected List<String> lookup(String key) {
GroupOrJob groupOrJob = groupOrJobLookup.get(key);
return groupOrJob == null ? Collections.emptyList() : groupOrJob.jobs().stream().map(Job::getId).collect(Collectors.toList());
}
}
}

View File

@ -30,6 +30,8 @@ public final class Messages {
"The date_histogram (or histogram) aggregation cannot have sibling aggregations";
public static final String DATAFEED_AGGREGATIONS_INTERVAL_MUST_BE_GREATER_THAN_ZERO =
"Aggregation interval must be greater than 0";
public static final String DATAFEED_AGGREGATIONS_INTERVAL_MUST_BE_DIVISOR_OF_BUCKET_SPAN =
"Aggregation interval [{0}] must be a divisor of the bucket_span [{1}]";
public static final String DATAFEED_AGGREGATIONS_INTERVAL_MUST_LESS_OR_EQUAL_TO_BUCKET_SPAN =
"Aggregation interval [{0}] must be less than or equal to the bucket_span [{1}]";
public static final String DATAFEED_DATA_HISTOGRAM_MUST_HAVE_NESTED_MAX_AGGREGATION =
@ -40,6 +42,8 @@ public final class Messages {
"Inconsistent {0}; ''{1}'' specified in the body differs from ''{2}'' specified as a URL argument";
public static final String INVALID_ID = "Invalid {0}; ''{1}'' can contain lowercase alphanumeric (a-z and 0-9), hyphens or " +
"underscores; must start and end with alphanumeric";
public static final String INVALID_GROUP = "Invalid group id ''{0}''; must be non-empty string and may contain lowercase alphanumeric" +
" (a-z and 0-9), hyphens or underscores; must start and end with alphanumeric";
public static final String JOB_AUDIR_DATAFEED_DATA_SEEN_AGAIN = "Datafeed has started retrieving data again";
public static final String JOB_AUDIT_CREATED = "Job created";
@ -149,6 +153,9 @@ public final class Messages {
public static final String JOB_CONFIG_TIME_FIELD_NOT_ALLOWED_IN_ANALYSIS_CONFIG =
"data_description.time_field may not be used in the analysis_config";
public static final String JOB_AND_GROUP_NAMES_MUST_BE_UNIQUE =
"job and group names must be unique but job [{0}] and group [{0}] have the same name";
public static final String JOB_UNKNOWN_ID = "No known job with id ''{0}''";
public static final String REST_CANNOT_DELETE_HIGHEST_PRIORITY =

View File

@ -69,7 +69,7 @@ public abstract class NameResolver {
result.addAll(expanded);
} else {
List<String> matchingNames = lookup(token);
if (matchingNames == null) {
if (matchingNames.isEmpty()) {
throw notFoundExceptionSupplier.apply(token);
}
result.addAll(matchingNames);
@ -97,7 +97,6 @@ public abstract class NameResolver {
* @param key the key to look up
* @return a list of the matching names or {@code null} when no matching names exist
*/
@Nullable
protected abstract List<String> lookup(String key);
/**
@ -120,7 +119,7 @@ public abstract class NameResolver {
@Override
protected List<String> lookup(String key) {
return nameSet.contains(key) ? Collections.singletonList(key) : null;
return nameSet.contains(key) ? Collections.singletonList(key) : Collections.emptyList();
}
};
}

View File

@ -1,87 +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.bench;
import org.elasticsearch.common.Randomness;
import com.carrotsearch.randomizedtesting.generators.RandomStrings;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.metrics.MeanMetric;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.security.authc.support.Hasher;
@SuppressForbidden(reason = "benchmark")
public class HasherBenchmark {
private static final int WARMING_ITERS = 1000;
private static final int BENCH_ITERS = 10000;
public static void main(String[] args) throws Exception {
test(Hasher.SSHA256).print();
test(Hasher.MD5).print();
test(Hasher.SHA1).print();
test(Hasher.BCRYPT4).print();
}
protected static Metrics test(Hasher hasher) {
Metrics metrics = new Metrics(hasher);
System.out.print("warming up [" + hasher.name() + "]...");
for (int i = 0; i < WARMING_ITERS; i++) {
SecureString str = new SecureString(RandomStrings.randomAsciiOfLength(Randomness.get(), 8).toCharArray());
char[] hash = hasher.hash(str);
hasher.verify(str, hash);
}
System.out.println("done!");
System.out.print("starting benchmark for [" + hasher.name() + "]...");
long start;
for (int i = 0; i < BENCH_ITERS; i++) {
SecureString str = new SecureString(RandomStrings.randomAsciiOfLength(Randomness.get(), 8).toCharArray());
start = System.nanoTime();
char[] hash = hasher.hash(str);
metrics.hash.inc(System.nanoTime() - start);
start = System.nanoTime();
hasher.verify(str, hash);
metrics.verify.inc(System.nanoTime() - start);
if (i % 1000000 == 0) {
System.out.println("finished " + i + " iterations");
}
}
System.out.println("done!");
return metrics;
}
@SuppressForbidden(reason = "benchmark")
private static class Metrics {
final String name;
final MeanMetric hash = new MeanMetric();
final MeanMetric verify = new MeanMetric();
Metrics(Hasher hasher) {
this.name = hasher.name();
}
void print() {
System.out.println(name);
System.out.println("\tHash (total): " + TimeValue.timeValueNanos(hash.sum()).format());
System.out.println("\tHash (avg): " + hash.mean() + " nanos");
System.out.println("\tVerify (total): " + TimeValue.timeValueNanos(verify.sum()).format());
System.out.println("\tVerify (avg): " + verify.mean() + " nanos");
}
}
}

View File

@ -123,6 +123,23 @@ public class DatafeedJobValidatorTests extends ESTestCase {
assertEquals("Aggregation interval [1800001ms] must be less than or equal to the bucket_span [1800000ms]", e.getMessage());
}
public void testVerify_HistogramIntervalIsDivisorOfBucketSpan() throws IOException {
Job.Builder builder = buildJobBuilder("foo");
AnalysisConfig.Builder ac = createAnalysisConfig();
ac.setSummaryCountFieldName("some_count");
ac.setBucketSpan(TimeValue.timeValueMinutes(5));
builder.setAnalysisConfig(ac);
Job job = builder.build(new Date());
DatafeedConfig datafeedConfig = createValidDatafeedConfigWithAggs(37 * 1000).build();
ElasticsearchStatusException e = ESTestCase.expectThrows(ElasticsearchStatusException.class,
() -> DatafeedJobValidator.validate(datafeedConfig, job));
assertEquals("Aggregation interval [37000ms] must be a divisor of the bucket_span [300000ms]", e.getMessage());
DatafeedConfig goodDatafeedConfig = createValidDatafeedConfigWithAggs(60 * 1000).build();
DatafeedJobValidator.validate(goodDatafeedConfig, job);
}
private static Job.Builder buildJobBuilder(String id) {
Job.Builder builder = new Job.Builder(id);
AnalysisConfig.Builder ac = createAnalysisConfig();

View File

@ -442,7 +442,10 @@ public class DatafeedJobsRestIT extends ESRestTestCase {
+ "\"aggs\": {\"timestamp\":{\"max\":{\"field\":\"timestamp\"}},"
+ "\"bytes-delta\":{\"derivative\":{\"buckets_path\":\"avg_bytes_out\"}},"
+ "\"avg_bytes_out\":{\"avg\":{\"field\":\"network_bytes_out\"}} }}}}}";
new DatafeedBuilder(datafeedId, jobId, "network-data", "doc").setAggregations(aggregations).build();
new DatafeedBuilder(datafeedId, jobId, "network-data", "doc")
.setAggregations(aggregations)
.setChunkingTimespan("300s")
.build();
openJob(client(), jobId);
@ -696,6 +699,7 @@ public class DatafeedJobsRestIT extends ESRestTestCase {
String scriptedFields;
String aggregations;
String authHeader = BASIC_AUTH_VALUE_SUPER_USER;
String chunkingTimespan;
DatafeedBuilder(String datafeedId, String jobId, String index, String type) {
this.datafeedId = datafeedId;
@ -724,12 +728,19 @@ public class DatafeedJobsRestIT extends ESRestTestCase {
return this;
}
DatafeedBuilder setChunkingTimespan(String timespan) {
chunkingTimespan = timespan;
return this;
}
Response build() throws IOException {
String datafeedConfig = "{"
+ "\"job_id\": \"" + jobId + "\",\"indexes\":[\"" + index + "\"],\"types\":[\"" + type + "\"]"
+ (source ? ",\"_source\":true" : "")
+ (scriptedFields == null ? "" : ",\"script_fields\":" + scriptedFields)
+ (aggregations == null ? "" : ",\"aggs\":" + aggregations)
+ (chunkingTimespan == null ? "" :
",\"chunking_config\":{\"mode\":\"MANUAL\",\"time_span\":\"" + chunkingTimespan + "\"}")
+ "}";
return client().performRequest("put", MachineLearning.BASE_PATH + "datafeeds/" + datafeedId, Collections.emptyMap(),
new StringEntity(datafeedConfig, ContentType.APPLICATION_JSON),

View File

@ -21,15 +21,18 @@ import org.elasticsearch.xpack.ml.job.messages.Messages;
import org.elasticsearch.xpack.ml.job.persistence.AnomalyDetectorsIndex;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
@ -465,6 +468,20 @@ public class JobTests extends AbstractSerializingTestCase<Job> {
assertEquals(expected, new HashSet<>(builder.invalidCreateTimeSettings()));
}
public void testEmptyGroup() {
Job.Builder builder = buildJobBuilder("foo");
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> builder.setGroups(Arrays.asList("foo-group", "")));
assertThat(e.getMessage(), containsString("Invalid group id ''"));
}
public void testInvalidGroup() {
Job.Builder builder = buildJobBuilder("foo");
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> builder.setGroups(Arrays.asList("foo-group", "$$$")));
assertThat(e.getMessage(), containsString("Invalid group id '$$$'"));
}
public static Job.Builder buildJobBuilder(String id, Date date) {
Job.Builder builder = new Job.Builder(id);
builder.setCreateTime(date);
@ -500,6 +517,14 @@ public class JobTests extends AbstractSerializingTestCase<Job> {
if (randomBoolean()) {
builder.setJobVersion(Version.CURRENT);
}
if (randomBoolean()) {
int groupsNum = randomIntBetween(0, 10);
List<String> groups = new ArrayList<>(groupsNum);
for (int i = 0; i < groupsNum; i++) {
groups.add(randomValidJobId());
}
builder.setGroups(groups);
}
builder.setCreateTime(new Date(randomNonNegativeLong()));
if (randomBoolean()) {
builder.setFinishedTime(new Date(randomNonNegativeLong()));

View File

@ -24,6 +24,14 @@ public class JobUpdateTests extends AbstractSerializingTestCase<JobUpdate> {
@Override
protected JobUpdate createTestInstance() {
JobUpdate.Builder update = new JobUpdate.Builder(randomAlphaOfLength(4));
if (randomBoolean()) {
int groupsNum = randomIntBetween(0, 10);
List<String> groups = new ArrayList<>(groupsNum);
for (int i = 0; i < groupsNum; i++) {
groups.add(JobTests.randomValidJobId());
}
update.setGroups(groups);
}
if (randomBoolean()) {
update.setDescription(randomAlphaOfLength(20));
}
@ -106,6 +114,7 @@ public class JobUpdateTests extends AbstractSerializingTestCase<JobUpdate> {
Map<String, Object> customSettings = Collections.singletonMap(randomAlphaOfLength(10), randomAlphaOfLength(10));
JobUpdate.Builder updateBuilder = new JobUpdate.Builder("foo");
updateBuilder.setGroups(Arrays.asList("group-1", "group-2"));
updateBuilder.setDescription("updated_description");
updateBuilder.setDetectorUpdates(detectorUpdates);
updateBuilder.setModelPlotConfig(modelPlotConfig);
@ -120,6 +129,7 @@ public class JobUpdateTests extends AbstractSerializingTestCase<JobUpdate> {
JobUpdate update = updateBuilder.build();
Job.Builder jobBuilder = new Job.Builder("foo");
jobBuilder.setGroups(Arrays.asList("group-1"));
Detector.Builder d1 = new Detector.Builder("info_content", "domain");
d1.setOverFieldName("mlcategory");
Detector.Builder d2 = new Detector.Builder("min", "field");
@ -132,6 +142,7 @@ public class JobUpdateTests extends AbstractSerializingTestCase<JobUpdate> {
Job updatedJob = update.mergeWithJob(jobBuilder.build());
assertEquals(update.getGroups(), updatedJob.getGroups());
assertEquals(update.getDescription(), updatedJob.getDescription());
assertEquals(update.getModelPlotConfig(), updatedJob.getModelPlotConfig());
assertEquals(update.getAnalysisLimits(), updatedJob.getAnalysisLimits());

View File

@ -0,0 +1,98 @@
/*
* 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.ml.job.groups;
import org.elasticsearch.ResourceAlreadyExistsException;
import org.elasticsearch.ResourceNotFoundException;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.ml.job.config.Job;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.contains;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class GroupOrJobLookupTests extends ESTestCase {
public void testEmptyLookup_GivenAllowNoJobs() {
GroupOrJobLookup lookup = new GroupOrJobLookup(Collections.emptyList());
assertThat(lookup.expandJobIds("_all", true).isEmpty(), is(true));
assertThat(lookup.expandJobIds("*", true).isEmpty(), is(true));
assertThat(lookup.expandJobIds("foo*", true).isEmpty(), is(true));
expectThrows(ResourceNotFoundException.class, () -> lookup.expandJobIds("foo", true));
}
public void testEmptyLookup_GivenNotAllowNoJobs() {
GroupOrJobLookup lookup = new GroupOrJobLookup(Collections.emptyList());
expectThrows(ResourceNotFoundException.class, () -> lookup.expandJobIds("_all", false));
expectThrows(ResourceNotFoundException.class, () -> lookup.expandJobIds("*", false));
expectThrows(ResourceNotFoundException.class, () -> lookup.expandJobIds("foo*", false));
expectThrows(ResourceNotFoundException.class, () -> lookup.expandJobIds("foo", true));
}
public void testAllIsNotExpandedInCommaSeparatedExpression() {
GroupOrJobLookup lookup = new GroupOrJobLookup(Collections.emptyList());
ResourceNotFoundException e = expectThrows(ResourceNotFoundException.class, () -> lookup.expandJobIds("foo-*,_all", true));
assertThat(e.getMessage(), equalTo("No known job with id '_all'"));
}
public void testConstructor_GivenJobWithSameIdAsPreviousGroupName() {
List<Job> jobs = new ArrayList<>();
jobs.add(mockJob("foo", Arrays.asList("foo-group")));
jobs.add(mockJob("foo-group", Collections.emptyList()));
ResourceAlreadyExistsException e = expectThrows(ResourceAlreadyExistsException.class, () -> new GroupOrJobLookup(jobs));
assertThat(e.getMessage(),
equalTo("job and group names must be unique but job [foo-group] and group [foo-group] have the same name"));
}
public void testConstructor_GivenGroupWithSameNameAsPreviousJobId() {
List<Job> jobs = new ArrayList<>();
jobs.add(mockJob("foo", Collections.emptyList()));
jobs.add(mockJob("foo-2", Arrays.asList("foo")));
ResourceAlreadyExistsException e = expectThrows(ResourceAlreadyExistsException.class, () -> new GroupOrJobLookup(jobs));
assertThat(e.getMessage(),
equalTo("job and group names must be unique but job [foo] and group [foo] have the same name"));
}
public void testLookup() {
List<Job> jobs = new ArrayList<>();
jobs.add(mockJob("foo-1", Arrays.asList("foo-group", "ones")));
jobs.add(mockJob("foo-2", Arrays.asList("foo-group", "twos")));
jobs.add(mockJob("bar-1", Arrays.asList("bar-group", "ones")));
jobs.add(mockJob("bar-2", Arrays.asList("bar-group", "twos")));
jobs.add(mockJob("nogroup", Collections.emptyList()));
GroupOrJobLookup groupOrJobLookup = new GroupOrJobLookup(jobs);
assertThat(groupOrJobLookup.expandJobIds("_all", false), contains("bar-1", "bar-2", "foo-1", "foo-2", "nogroup"));
assertThat(groupOrJobLookup.expandJobIds("*", false), contains("bar-1", "bar-2", "foo-1", "foo-2", "nogroup"));
assertThat(groupOrJobLookup.expandJobIds("bar-1", false), contains("bar-1"));
assertThat(groupOrJobLookup.expandJobIds("foo-1", false), contains("foo-1"));
assertThat(groupOrJobLookup.expandJobIds("foo-2, bar-1", false), contains("bar-1", "foo-2"));
assertThat(groupOrJobLookup.expandJobIds("foo-group", false), contains("foo-1", "foo-2"));
assertThat(groupOrJobLookup.expandJobIds("bar-group", false), contains("bar-1", "bar-2"));
assertThat(groupOrJobLookup.expandJobIds("ones", false), contains("bar-1", "foo-1"));
assertThat(groupOrJobLookup.expandJobIds("twos", false), contains("bar-2", "foo-2"));
assertThat(groupOrJobLookup.expandJobIds("foo-group, nogroup", false), contains("foo-1", "foo-2", "nogroup"));
assertThat(groupOrJobLookup.expandJobIds("*-group", false), contains("bar-1", "bar-2", "foo-1", "foo-2"));
assertThat(groupOrJobLookup.expandJobIds("foo-group,foo-1,foo-2", false), contains("foo-1", "foo-2"));
assertThat(groupOrJobLookup.expandJobIds("foo-group,*-2", false), contains("bar-2", "foo-1", "foo-2"));
}
private static Job mockJob(String jobId, List<String> groups) {
Job job = mock(Job.class);
when(job.getId()).thenReturn(jobId);
when(job.getGroups()).thenReturn(groups);
return job;
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.ml.job.groups;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.ml.job.config.Job;
import java.util.Arrays;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
public class GroupOrJobTests extends ESTestCase {
public void testSingleJob() {
Job job = mock(Job.class);
GroupOrJob groupOrJob = new GroupOrJob.SingleJob(job);
assertThat(groupOrJob.isGroup(), is(false));
assertThat(groupOrJob.jobs(), contains(job));
expectThrows(UnsupportedOperationException.class, () -> groupOrJob.jobs().add(mock(Job.class)));
}
public void testGroup() {
Job job1 = mock(Job.class);
Job job2 = mock(Job.class);
GroupOrJob groupOrJob = new GroupOrJob.Group(Arrays.asList(job1, job2));
assertThat(groupOrJob.isGroup(), is(true));
assertThat(groupOrJob.jobs(), contains(job1, job2));
expectThrows(UnsupportedOperationException.class, () -> groupOrJob.jobs().add(mock(Job.class)));
}
}

View File

@ -140,7 +140,7 @@ public class NameResolverTests extends ESTestCase {
@Override
protected List<String> lookup(String key) {
return lookup.get(key);
return lookup.containsKey(key) ? lookup.get(key) : Collections.emptyList();
}
}
}

View File

@ -1,59 +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.monitoring;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.node.MockNode;
import org.elasticsearch.node.Node;
import org.elasticsearch.xpack.XPackPlugin;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
/**
* Main class to easily run Monitoring from a IDE.
* <p>
* In order to run this class set configure the following:
* 1) Set `-Des.path.home=` to a directory containing an ES config directory
*
* It accepts collectors names as program arguments.
*/
public class MonitoringF {
public static void main(String[] args) throws Throwable {
Settings.Builder settings = Settings.builder();
settings.put("security.manager.enabled", "false");
settings.put("cluster.name", MonitoringF.class.getSimpleName());
settings.put("xpack.monitoring.collection.interval", "1s");
if (!CollectionUtils.isEmpty(args)) {
settings.putArray("xpack.monitoring.collection.collectors", args);
}
final CountDownLatch latch = new CountDownLatch(1);
final Node node = new MockNode(settings.build(),
Arrays.asList(XPackPlugin.class, XPackPlugin.class, XPackPlugin.class));
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
IOUtils.close(node);
} catch (IOException e) {
throw new ElasticsearchException(e);
} finally {
latch.countDown();
}
}
});
node.start();
latch.await();
}
}

View File

@ -1,123 +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.notification.email;
import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.cli.Terminal;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import java.util.Collections;
import java.util.Locale;
@AwaitsFix(bugUrl = "https://github.com/elastic/x-plugins/issues/379")
public class ManualPublicSmtpServersTester {
private static final Terminal terminal = Terminal.DEFAULT;
public static class Gmail {
public static void main(String[] args) throws Exception {
test("gmail", Profile.GMAIL, Settings.builder()
.put("xpack.notification.email.account.gmail.smtp.auth", true)
.put("xpack.notification.email.account.gmail.smtp.starttls.enable", true)
.put("xpack.notification.email.account.gmail.smtp.host", "smtp.gmail.com")
.put("xpack.notification.email.account.gmail.smtp.port", 587)
.put("xpack.notification.email.account.gmail.smtp.user", terminal.readText("username: "))
.put("xpack.notification.email.account.gmail.smtp.password", new String(terminal.readSecret("password: ")))
.put("xpack.notification.email.account.gmail.email_defaults.to", terminal.readText("to: "))
);
}
}
public static class OutlookDotCom {
public static void main(String[] args) throws Exception {
test("outlook", Profile.STANDARD, Settings.builder()
.put("xpack.notification.email.account.outlook.smtp.auth", true)
.put("xpack.notification.email.account.outlook.smtp.starttls.enable", true)
.put("xpack.notification.email.account.outlook.smtp.host", "smtp-mail.outlook.com")
.put("xpack.notification.email.account.outlook.smtp.port", 587)
.put("xpack.notification.email.account.outlook.smtp.user", "elastic.user@outlook.com")
.put("xpack.notification.email.account.outlook.smtp.password", "fantastic42")
.put("xpack.notification.email.account.outlook.email_defaults.to", "elastic.user@outlook.com")
.put()
);
}
}
public static class YahooMail {
public static void main(String[] args) throws Exception {
test("yahoo", Profile.STANDARD, Settings.builder()
.put("xpack.notification.email.account.yahoo.smtp.starttls.enable", true)
.put("xpack.notification.email.account.yahoo.smtp.auth", true)
.put("xpack.notification.email.account.yahoo.smtp.host", "smtp.mail.yahoo.com")
.put("xpack.notification.email.account.yahoo.smtp.port", 587)
.put("xpack.notification.email.account.yahoo.smtp.user", "elastic.user@yahoo.com")
.put("xpack.notification.email.account.yahoo.smtp.password", "fantastic42")
// note: from must be set to the same authenticated user account
.put("xpack.notification.email.account.yahoo.email_defaults.from", "elastic.user@yahoo.com")
.put("xpack.notification.email.account.yahoo.email_defaults.to", "elastic.user@yahoo.com")
);
}
}
// Amazon Simple Email Service
public static class SES {
public static void main(String[] args) throws Exception {
test("ses", Profile.STANDARD, Settings.builder()
.put("xpack.notification.email.account.ses.smtp.auth", true)
.put("xpack.notification.email.account.ses.smtp.starttls.enable", true)
.put("xpack.notification.email.account.ses.smtp.starttls.required", true)
.put("xpack.notification.email.account.ses.smtp.host", "email-smtp.us-east-1.amazonaws.com")
.put("xpack.notification.email.account.ses.smtp.port", 587)
.put("xpack.notification.email.account.ses.smtp.user", terminal.readText("user: "))
.put("xpack.notification.email.account.ses.email_defaults.from", "dummy.user@elasticsearch.com")
.put("xpack.notification.email.account.ses.email_defaults.to", terminal.readText("to: "))
.put("xpack.notification.email.account.ses.smtp.password",
new String(terminal.readSecret("password: ")))
);
}
}
static void test(String accountName, Profile profile, Settings.Builder settingsBuilder) throws Exception {
String path = "/org/elasticsearch/xpack/watcher/actions/email/service/logo.png";
if (EmailServiceTests.class.getResourceAsStream(path) == null) {
throw new ElasticsearchException("Could not find logo at path {}", path);
}
EmailService service = startEmailService(settingsBuilder);
ToXContent content = (xContentBuilder, params) -> xContentBuilder.startObject()
.field("key1", "value1")
.field("key2", "value2")
.field("key3", "value3")
.endObject();
Email email = Email.builder()
.id("_id")
.subject("_subject")
.textBody("_text_body")
.htmlBody("<b>html body</b><p/><p/><img src=\"cid:logo.png\"/>")
.attach(new Attachment.XContent.Yaml("test.yml", content))
.attach(new Attachment.Stream("logo.png", "logo.png", true,
() -> EmailServiceTests.class.getResourceAsStream(path)))
.build();
EmailService.EmailSent sent = service.send(email, null, profile, accountName);
terminal.println(String.format(Locale.ROOT, "email sent via account [%s]", sent.account()));
}
static EmailService startEmailService(Settings.Builder builder) {
Settings settings = builder.build();
return new EmailService(settings, null,
new ClusterSettings(settings, Collections.singleton(EmailService.EMAIL_ACCOUNT_SETTING)));
}
}

View File

@ -1,81 +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.security;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.node.MockNode;
import org.elasticsearch.node.Node;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.test.SecurityTestUtils;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.XPackPlugin;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import static org.elasticsearch.xpack.security.test.SecurityTestUtils.writeFile;
/**
* Main class to easily run X-Pack Security from a IDE.
*
* During startup an error will be printed that the config directory can't be found, to fix this:
* set `-Des.path.home=` to a location where there is a config directory on your machine.
*/
public class SecurityF {
public static void main(String[] args) throws Throwable {
Settings.Builder settings = Settings.builder();
settings.put("http.cors.enabled", "true");
settings.put("http.cors.allow-origin", "*");
settings.put("xpack.security.enabled", "true");
// Disable Monitoring to prevent cluster activity
settings.put("xpack.monitoring.enabled", "false");
settings.put("cluster.name", SecurityF.class.getSimpleName());
String homeDir = System.getProperty("es.path.home");
if (homeDir == null || Files.exists(PathUtils.get(homeDir)) == false) {
throw new IllegalStateException("es.path.home must be set and exist");
}
final Path config = PathUtils.get(homeDir).resolve("config");
SecurityTestUtils.createFolder(config);
final Path folder = config.resolve("x-pack");
SecurityTestUtils.createFolder(folder);
writeFile(folder, "users", SecuritySettingsSource.CONFIG_STANDARD_USER);
writeFile(folder, "users_roles", SecuritySettingsSource.CONFIG_STANDARD_USER_ROLES);
writeFile(folder, "roles.yml", SecuritySettingsSource.CONFIG_ROLE_ALLOW_ALL);
settings.put("xpack.security.authc.realms.file.type", FileRealm.TYPE);
settings.put("xpack.security.authc.realms.file.order", "0");
settings.put("xpack.security.authc.realms.esnative.type", NativeRealm.TYPE);
settings.put("xpack.security.authc.realms.esnative.order", "1");
final CountDownLatch latch = new CountDownLatch(1);
final Node node = new MockNode(settings.build(), Arrays.asList(XPackPlugin.class), config);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
IOUtils.close(node);
} catch (IOException ex) {
throw new ElasticsearchException(ex);
} finally {
latch.countDown();
}
}
});
node.start();
latch.await();
}
}

View File

@ -1,182 +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.apache.lucene.util.LuceneTestCase.AwaitsFix;
import org.elasticsearch.AbstractOldXPackIndicesBackwardsCompatibilityTestCase;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.watcher.actions.logging.LoggingAction;
import org.elasticsearch.xpack.watcher.client.WatchSourceBuilder;
import org.elasticsearch.xpack.watcher.client.WatcherClient;
import org.elasticsearch.xpack.watcher.condition.AlwaysCondition;
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchResponse;
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse;
import org.elasticsearch.xpack.watcher.transport.actions.stats.WatcherStatsResponse;
import org.elasticsearch.xpack.watcher.trigger.schedule.IntervalSchedule;
import org.elasticsearch.xpack.watcher.trigger.schedule.IntervalSchedule.Interval;
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
/**
* Tests for watcher indexes created before 5.0.
*/
// This will only work, when the upgrade API is in place!
@AwaitsFix(bugUrl = "https://github.com/elastic/x-pack-elasticsearch/issues/1303")
public class OldWatcherIndicesBackwardsCompatibilityTests extends AbstractOldXPackIndicesBackwardsCompatibilityTestCase {
@Override
public Settings nodeSettings(int ord) {
return Settings.builder()
.put(super.nodeSettings(ord))
.put(XPackSettings.WATCHER_ENABLED.getKey(), true)
.build();
}
@Override
protected void checkVersion(Version version) throws Exception {
// Wait for watcher to actually start....
WatcherClient watcherClient = new WatcherClient(client());
watcherClient.prepareWatchService().start();
assertBusy(() -> {
List<WatcherState> states = watcherClient.prepareWatcherStats().get().getNodes()
.stream().map(WatcherStatsResponse.Node::getWatcherState).collect(Collectors.toList());
assertThat(states, everyItem(is(WatcherState.STARTED)));
});
try {
assertOldTemplatesAreDeleted();
assertWatchIndexContentsWork(version);
assertBasicWatchInteractions();
} finally {
/* Shut down watcher after every test because watcher can be a bit finicky about shutting down when the node shuts down. This
* makes super sure it shuts down *and* causes the test to fail in a sensible spot if it doesn't shut down. */
watcherClient.prepareWatchService().stop().get();
assertBusy(() -> internalCluster().getInstances(WatcherService.class)
.forEach(watcherService -> assertThat(watcherService.state(), is(WatcherState.STOPPED))));
}
}
private void assertOldTemplatesAreDeleted() {
GetIndexTemplatesResponse response = client().admin().indices().prepareGetTemplates().get();
List<String> templateNames = response.getIndexTemplates().stream().map(IndexTemplateMetaData::getName).collect(Collectors.toList());
assertThat(templateNames, not(hasItems(is("watches"), startsWith("watch-history"), is("triggered_watches"))));
}
void assertWatchIndexContentsWork(Version version) throws Exception {
WatcherClient watcherClient = new WatcherClient(client());
// Fetch a basic watch
GetWatchResponse bwcWatch = watcherClient.prepareGetWatch("bwc_watch").get();
assertTrue(bwcWatch.isFound());
assertNotNull(bwcWatch.getSource());
Map<String, Object> source = bwcWatch.getSource().getAsMap();
assertEquals(1000, source.get("throttle_period_in_millis"));
Map<?, ?> input = (Map<?, ?>) source.get("input");
Map<?, ?> search = (Map<?, ?>) input.get("search");
// We asked for 100s but 2.x converted that to 1.6m which is actually 96s...
int timeout = (int) (version.onOrAfter(Version.V_5_0_0_alpha1) ? timeValueSeconds(100).millis() : timeValueSeconds(96).millis());
assertEquals(timeout, search.get("timeout_in_millis"));
Map<?, ?> actions = (Map<?, ?>) source.get("actions");
Map<?, ?> indexPayload = (Map<?, ?>) actions.get("index_payload");
Map<?, ?> transform = (Map<?, ?>) indexPayload.get("transform");
search = (Map<?, ?>) transform.get("search");
assertEquals(timeout, search.get("timeout_in_millis"));
Map<?, ?> index = (Map<?, ?>) indexPayload.get("index");
assertEquals("bwc_watch_index", index.get("index"));
assertEquals("bwc_watch_type", index.get("doc_type"));
assertEquals(timeout, index.get("timeout_in_millis"));
// Fetch a watch with "fun" throttle periods
bwcWatch = watcherClient.prepareGetWatch("bwc_throttle_period").get();
assertTrue(bwcWatch.isFound());
assertNotNull(bwcWatch.getSource());
source = bwcWatch.getSource().getAsMap();
assertEquals(timeout, source.get("throttle_period_in_millis"));
actions = (Map<?, ?>) source.get("actions");
indexPayload = (Map<?, ?>) actions.get("index_payload");
assertEquals(timeout, indexPayload.get("throttle_period_in_millis"));
/*
* Fetch a watch with a funny timeout to verify loading fractional time
* values. This watch is only built in >= 2.3 because email attachments
* aren't supported before that.
*/
bwcWatch = watcherClient.prepareGetWatch("bwc_funny_timeout").get();
assertTrue(bwcWatch.isFound());
assertNotNull(bwcWatch.getSource());
source = bwcWatch.getSource().getAsMap();
actions = (Map<?, ?>) source.get("actions");
Map<?, ?> work = (Map<?, ?>) actions.get("work");
Map<?, ?> email = (Map<?, ?>) work.get("email");
Map<?, ?> attachments = (Map<?, ?>) email.get("attachments");
Map<?, ?> attachment = (Map<?, ?>) attachments.get("test_report.pdf");
Map<?, ?> http = (Map<?, ?>) attachment.get("http");
Map<?, ?> request = (Map<?, ?>) http.get("request");
assertEquals(timeout, request.get("read_timeout_millis"));
assertEquals("https", request.get("scheme"));
assertEquals("example.com", request.get("host"));
assertEquals("{{ctx.metadata.report_url}}", request.get("path"));
assertEquals(8443, request.get("port"));
Map<?, ?> auth = (Map<?, ?>) request.get("auth");
Map<?, ?> basic = (Map<?, ?>) auth.get("basic");
assertThat(basic, hasEntry("username", "Aladdin"));
// password doesn't come back because it is hidden
assertThat(basic, not(hasKey("password")));
String watchHistoryPattern = version.onOrAfter(Version.V_5_0_0_alpha1) ? ".watcher-history*" : ".watch_history*";
SearchResponse history = client().prepareSearch(watchHistoryPattern).get();
assertThat(history.getHits().getTotalHits(), greaterThanOrEqualTo(10L));
}
void assertBasicWatchInteractions() throws Exception {
WatcherClient watcherClient = new WatcherClient(client());
PutWatchResponse put = watcherClient.preparePutWatch("new_watch").setSource(new WatchSourceBuilder()
.condition(AlwaysCondition.INSTANCE)
.trigger(ScheduleTrigger.builder(new IntervalSchedule(Interval.seconds(1))))
.addAction("awesome", LoggingAction.builder(new TextTemplate("test")))).get();
assertTrue(put.isCreated());
assertEquals(1, put.getVersion());
put = watcherClient.preparePutWatch("new_watch").setSource(new WatchSourceBuilder()
.condition(AlwaysCondition.INSTANCE)
.trigger(ScheduleTrigger.builder(new IntervalSchedule(Interval.seconds(1))))
.addAction("awesome", LoggingAction.builder(new TextTemplate("test")))).get();
assertFalse(put.isCreated());
assertEquals(2, put.getVersion());
GetWatchResponse get = watcherClient.prepareGetWatch(put.getId()).get();
assertTrue(get.isFound());
{
Map<?, ?> source = get.getSource().getAsMap();
Map<?, ?> actions = (Map<?, ?>) source.get("actions");
Map<?, ?> awesome = (Map<?, ?>) actions.get("awesome");
Map<?, ?> logging = (Map<?, ?>) awesome.get("logging");
assertEquals("info", logging.get("level"));
assertEquals("test", logging.get("text"));
}
}
}

View File

@ -1,77 +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.apache.lucene.util.IOUtils;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.node.MockNode;
import org.elasticsearch.node.Node;
import org.elasticsearch.xpack.XPackPlugin;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
/**
* Main class to easily run Watcher from a IDE.
* It sets all the options to run the Watcher plugin and access it from Sense, but doesn't run with security.
*
* In order to run this class set configure the following:
* 1) Set `-Des.path.home=` to a directory containing an ES config directory
*/
public class WatcherF {
@SuppressForbidden(reason = "not really code or a test")
public static void main(String[] args) throws Throwable {
Settings.Builder settings = Settings.builder();
settings.put("http.cors.enabled", "true");
settings.put("http.cors.allow-origin", "*");
settings.put("xpack.security.enabled", "false");
settings.put("security.manager.enabled", "false");
settings.put("cluster.name", WatcherF.class.getSimpleName());
// this is for the `test-watcher-integration` group level integration in HipChat
settings.put("xpack.notification.hipchat.account.integration.profile", "integration");
settings.put("xpack.notification.hipchat.account.integration.auth_token", "huuS9v7ccuOy3ZBWWWr1vt8Lqu3sQnLUE81nrLZU");
settings.put("xpack.notification.hipchat.account.integration.room", "test-watcher");
// this is for the Watcher Test account in HipChat
settings.put("xpack.notification.hipchat.account.user.profile", "user");
settings.put("xpack.notification.hipchat.account.user.auth_token", "12rNQUuQ0wObfRVeoVD8OeoAnosCT8tSTV5UjsII");
// this is for the `test-watcher-v1` notification token (hipchat)
settings.put("xpack.notification.hipchat.account.v1.profile", "v1");
settings.put("xpack.notification.hipchat.account.v1.auth_token", "a734baf62df618b96dda55b323fc30");
// this is for our test slack incoming webhook (under elasticsearch team)
System.setProperty("es.xpack.notification.slack.account.a1.url",
"https://hooks.slack.com/services/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");
System.setProperty("es.xpack.notification.pagerduty.account.service1.service_api_key",
"fc082467005d4072a914e0bb041882d0");
final CountDownLatch latch = new CountDownLatch(1);
final Node node = new MockNode(settings.build(), Arrays.asList(XPackPlugin.class, XPackPlugin.class));
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
IOUtils.close(node);
} catch (IOException e) {
throw new ElasticsearchException(e);
} finally {
latch.countDown();
}
}
});
node.start();
latch.await();
}
}

View File

@ -17,7 +17,9 @@ import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.xpack.watcher.actions.ActionBuilders.loggingAction;
@ -26,7 +28,7 @@ import static org.elasticsearch.xpack.watcher.input.InputBuilders.simpleInput;
import static org.elasticsearch.xpack.watcher.transform.TransformBuilders.scriptTransform;
import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule;
import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.interval;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
/**
@ -69,20 +71,7 @@ public class HistoryTemplateTransformMappingsTests extends AbstractWatcherIntegr
return true; // just to have better control over the triggers
}
@Override
protected boolean enableSecurity() {
return false; // remove security noise from this test
}
@AwaitsFix(bugUrl = "https://github.com/elastic/x-pack-elasticsearch/issues/1517")
public void testTransformFields() throws Exception {
String index = "the-index";
String type = "the-type";
createIndex(index);
index(index, type, "{}");
flush();
refresh();
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("_id1").setSource(watchBuilder()
.trigger(schedule(interval("5s")))
.input(simpleInput())
@ -121,12 +110,15 @@ public class HistoryTemplateTransformMappingsTests extends AbstractWatcherIntegr
.includeDefaults(true)
.get();
for (Map<String, Map<String, GetFieldMappingsResponse.FieldMappingMetaData>> map : response.mappings().values()) {
Map<String, GetFieldMappingsResponse.FieldMappingMetaData> watchRecord = map.get("doc");
assertThat(watchRecord, hasKey("result.actions.transform.payload"));
GetFieldMappingsResponse.FieldMappingMetaData fieldMappingMetaData = watchRecord.get("result.actions.transform.payload");
assertThat(fieldMappingMetaData.isNull(), is(true));
}
// time might have rolled over to a new day, thus we need to check that this field exists only in one of the history indices
List<Boolean> payloadNulls = response.mappings().values().stream()
.map(map -> map.get("doc"))
.map(map -> map.get("result.actions.transform.payload"))
.filter(Objects::nonNull)
.map(GetFieldMappingsResponse.FieldMappingMetaData::isNull)
.collect(Collectors.toList());
assertThat(payloadNulls, hasItem(true));
});
}
}

View File

@ -183,7 +183,6 @@ public class BootStrapTests extends AbstractWatcherIntegrationTestCase {
});
}
@AwaitsFix(bugUrl = "https://github.com/elastic/x-pack-elasticsearch/pull/1544")
public void testMixedTriggeredWatchLoading() throws Exception {
createIndex("output");
client().prepareIndex("my-index", "foo", "bar")

View File

@ -1,19 +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.trigger.schedule.tool;
import org.elasticsearch.cli.Terminal;
/**
* A small executable tool that can eval crons
*/
public class EvalCron {
public static void main(String[] args) throws Exception {
String expression = Terminal.DEFAULT.readText("cron: ");
CronEvalTool.main(new String[] { expression });
}
}

View File

@ -0,0 +1,245 @@
setup:
- do:
xpack.ml.put_job:
job_id: test-job-groups-foo-1
body: >
{
"groups": ["foo-group", "ones"],
"analysis_config" : {
"detectors" :[{"function":"count"}]
},
"data_description" : {}
}
- do:
xpack.ml.put_job:
job_id: test-job-groups-foo-2
body: >
{
"groups": ["foo-group", "twos"],
"analysis_config" : {
"detectors" :[{"function":"count"}]
},
"data_description" : {}
}
- do:
xpack.ml.put_job:
job_id: test-job-groups-bar-1
body: >
{
"groups": ["bar-group", "ones"],
"analysis_config" : {
"detectors" :[{"function":"count"}]
},
"data_description" : {}
}
- do:
xpack.ml.put_job:
job_id: test-job-groups-bar-2
body: >
{
"groups": ["bar-group", "twos"],
"analysis_config" : {
"detectors" :[{"function":"count"}]
},
"data_description" : {}
}
- do:
xpack.ml.put_job:
job_id: test-job-groups-nogroup
body: >
{
"analysis_config" : {
"detectors" :[{"function":"count"}]
},
"data_description" : {}
}
---
"Test put job with id that matches an existing group":
- do:
catch: /resource_already_exists_exception/
xpack.ml.put_job:
job_id: foo-group
body: >
{
"analysis_config" : {
"detectors" :[{"function":"count"}]
},
"data_description" : {}
}
---
"Test put job with group that matches an job id":
- do:
catch: /resource_already_exists_exception/
xpack.ml.put_job:
job_id: test-job-groups-job-with-group-matching-existing-job-id
body: >
{
"groups": ["test-job-groups-nogroup"],
"analysis_config" : {
"detectors" :[{"function":"count"}]
},
"data_description" : {}
}
---
"Test put job with group that matches its id":
- do:
catch: /resource_already_exists_exception/
xpack.ml.put_job:
job_id: test-job-groups-job-with-group-matching-its-id
body: >
{
"groups": ["test-job-groups-job-with-group-matching-its-id"],
"analysis_config" : {
"detectors" :[{"function":"count"}]
},
"data_description" : {}
}
---
"Test put job with empty group":
- do:
catch: /Invalid group id ''; must be non-empty string and may contain lowercase alphanumeric \(a-z and 0-9\), hyphens or underscores; must start and end with alphanumeric/
xpack.ml.put_job:
job_id: test-job-groups-job-with-empty-group
body: >
{
"groups": ["foo-group", ""],
"analysis_config" : {
"detectors" :[{"function":"count"}]
},
"data_description" : {}
}
---
"Test put job with invalid group":
- do:
catch: /Invalid group id '___'; must be non-empty string and may contain lowercase alphanumeric \(a-z and 0-9\), hyphens or underscores; must start and end with alphanumeric/
xpack.ml.put_job:
job_id: test-job-groups-job-with-invalid-group
body: >
{
"groups": ["foo", "___"],
"analysis_config" : {
"detectors" :[{"function":"count"}]
},
"data_description" : {}
}
---
"Test get job API":
- do:
xpack.ml.get_jobs:
job_id: "_all"
- match: { count: 5 }
- match: { jobs.0.job_id: "test-job-groups-bar-1"}
- match: { jobs.1.job_id: "test-job-groups-bar-2"}
- match: { jobs.2.job_id: "test-job-groups-foo-1"}
- match: { jobs.3.job_id: "test-job-groups-foo-2"}
- match: { jobs.4.job_id: "test-job-groups-nogroup"}
- do:
xpack.ml.get_jobs:
job_id: "test-job-groups-bar-1"
- match: { count: 1 }
- match: { jobs.0.job_id: "test-job-groups-bar-1"}
- do:
xpack.ml.get_jobs:
job_id: "foo-group"
- match: { count: 2 }
- match: { jobs.0.job_id: "test-job-groups-foo-1"}
- match: { jobs.1.job_id: "test-job-groups-foo-2"}
- do:
xpack.ml.get_jobs:
job_id: "bar-group"
- match: { count: 2 }
- match: { jobs.0.job_id: "test-job-groups-bar-1"}
- match: { jobs.1.job_id: "test-job-groups-bar-2"}
- do:
xpack.ml.get_jobs:
job_id: "ones"
- match: { count: 2 }
- match: { jobs.0.job_id: "test-job-groups-bar-1"}
- match: { jobs.1.job_id: "test-job-groups-foo-1"}
- do:
xpack.ml.get_jobs:
job_id: "twos"
- match: { count: 2 }
- match: { jobs.0.job_id: "test-job-groups-bar-2"}
- match: { jobs.1.job_id: "test-job-groups-foo-2"}
- do:
xpack.ml.get_jobs:
job_id: "*-group"
- match: { count: 4 }
- match: { jobs.0.job_id: "test-job-groups-bar-1"}
- match: { jobs.1.job_id: "test-job-groups-bar-2"}
- match: { jobs.2.job_id: "test-job-groups-foo-1"}
- match: { jobs.3.job_id: "test-job-groups-foo-2"}
- do:
xpack.ml.get_jobs:
job_id: "bar-group,test-job-groups-nogroup"
- match: { count: 3 }
- match: { jobs.0.job_id: "test-job-groups-bar-1"}
- match: { jobs.1.job_id: "test-job-groups-bar-2"}
- match: { jobs.2.job_id: "test-job-groups-nogroup"}
---
"Test get job stats API":
- do:
xpack.ml.get_job_stats:
job_id: "foo-group"
- match: { count: 2 }
- match: { jobs.0.job_id: "test-job-groups-foo-1"}
- match: { jobs.1.job_id: "test-job-groups-foo-2"}
---
"Test close job API":
- do:
xpack.ml.open_job:
job_id: "test-job-groups-foo-1"
- do:
xpack.ml.open_job:
job_id: "test-job-groups-bar-1"
- do:
xpack.ml.get_job_stats:
job_id: "ones"
- match: { count: 2 }
- match: { jobs.0.job_id: "test-job-groups-bar-1"}
- match: { jobs.0.state: opened}
- match: { jobs.1.job_id: "test-job-groups-foo-1"}
- match: { jobs.1.state: opened}
- do:
xpack.ml.close_job:
job_id: "ones"
- do:
xpack.ml.get_job_stats:
job_id: "ones"
- match: { count: 2 }
- match: { jobs.0.job_id: "test-job-groups-bar-1"}
- match: { jobs.0.state: closed}
- match: { jobs.1.job_id: "test-job-groups-foo-1"}
- match: { jobs.1.state: closed}

View File

@ -296,6 +296,7 @@
job_id: jobs-crud-update-job
body: >
{
"groups": ["group-1", "group-2"],
"description":"Post update description",
"detectors": [{"detector_index": 0, "rules": {"target_field_name": "airline",
"rule_conditions": [ { "condition_type": "numerical_actual",
@ -318,6 +319,7 @@
}
}
- match: { job_id: "jobs-crud-update-job" }
- match: { groups: ["group-1", "group-2"] }
- match: { description: "Post update description" }
- match: { model_plot_config.enabled: false }
- match: { model_plot_config.terms: "foobar" }