Introduce Maintenance Mode to ILM (#31164)

This PR introduces a concept of a maintenance mode for the
lifecycle service. During maintenance mode, no policies are
executed.

To be placed into maintenance mode, users must first issue a
request to be placed in maintenance mode. Once the service
is assured that no policies are in actions that are not to be
interrupted (like ShrinkAction), the service will place itself
in maintenance mode.

APIs to-be introduced:

- POST _xpack/index_lifecycle/maintenance/_request
   - issues a request to be placed into maintenenance mode.
     This is not immediate, since we must first verify that
     it is safe to go from REQUESTED -> IN maintenance mode.
- POST _xpack/index_lifecycle/maintenance/_stop
   - issues a request to be taken out (this is immediate)
- GET _xpack/index_lifecycle/maintenance
   - get back the current mode our lifecycle management is in
This commit is contained in:
Tal Levy 2018-06-15 11:38:53 -07:00 committed by GitHub
parent c54937731e
commit 01939794fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 409 additions and 241 deletions

View File

@ -33,24 +33,29 @@ import java.util.stream.Collectors;
public class IndexLifecycleMetadata implements XPackMetaDataCustom { public class IndexLifecycleMetadata implements XPackMetaDataCustom {
public static final String TYPE = "index_lifecycle"; public static final String TYPE = "index_lifecycle";
public static final ParseField MAINTENANCE_MODE_FIELD = new ParseField("maintenance_mode");
public static final ParseField POLICIES_FIELD = new ParseField("policies"); public static final ParseField POLICIES_FIELD = new ParseField("policies");
public static final IndexLifecycleMetadata EMPTY = new IndexLifecycleMetadata(Collections.emptySortedMap()); public static final IndexLifecycleMetadata EMPTY = new IndexLifecycleMetadata(Collections.emptySortedMap(), OperationMode.NORMAL);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static final ConstructingObjectParser<IndexLifecycleMetadata, Void> PARSER = new ConstructingObjectParser<>( public static final ConstructingObjectParser<IndexLifecycleMetadata, Void> PARSER = new ConstructingObjectParser<>(
TYPE, a -> new IndexLifecycleMetadata( TYPE, a -> new IndexLifecycleMetadata(
ObjectParserUtils.convertListToMapValues(LifecyclePolicyMetadata::getName, (List<LifecyclePolicyMetadata>) a[0]))); ObjectParserUtils.convertListToMapValues(LifecyclePolicyMetadata::getName, (List<LifecyclePolicyMetadata>) a[0]),
OperationMode.valueOf((String) a[1])));
static { static {
PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(), (p, c, n) -> LifecyclePolicyMetadata.parse(p, n), PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(), (p, c, n) -> LifecyclePolicyMetadata.parse(p, n),
v -> { v -> {
throw new IllegalArgumentException("ordered " + POLICIES_FIELD.getPreferredName() + " are not supported"); throw new IllegalArgumentException("ordered " + POLICIES_FIELD.getPreferredName() + " are not supported");
}, POLICIES_FIELD); }, POLICIES_FIELD);
PARSER.declareString(ConstructingObjectParser.constructorArg(), MAINTENANCE_MODE_FIELD);
} }
private final Map<String, LifecyclePolicyMetadata> policyMetadatas; private final Map<String, LifecyclePolicyMetadata> policyMetadatas;
private final OperationMode maintenanceMode;
public IndexLifecycleMetadata(Map<String, LifecyclePolicyMetadata> policies) { public IndexLifecycleMetadata(Map<String, LifecyclePolicyMetadata> policies, OperationMode maintenanceMode) {
this.policyMetadatas = Collections.unmodifiableMap(policies); this.policyMetadatas = Collections.unmodifiableMap(policies);
this.maintenanceMode = maintenanceMode;
} }
public IndexLifecycleMetadata(StreamInput in) throws IOException { public IndexLifecycleMetadata(StreamInput in) throws IOException {
@ -60,6 +65,7 @@ public class IndexLifecycleMetadata implements XPackMetaDataCustom {
policies.put(in.readString(), new LifecyclePolicyMetadata(in)); policies.put(in.readString(), new LifecyclePolicyMetadata(in));
} }
this.policyMetadatas = policies; this.policyMetadatas = policies;
this.maintenanceMode = in.readEnum(OperationMode.class);
} }
@Override @Override
@ -69,12 +75,17 @@ public class IndexLifecycleMetadata implements XPackMetaDataCustom {
out.writeString(entry.getKey()); out.writeString(entry.getKey());
entry.getValue().writeTo(out); entry.getValue().writeTo(out);
} }
out.writeEnum(maintenanceMode);
} }
public Map<String, LifecyclePolicyMetadata> getPolicyMetadatas() { public Map<String, LifecyclePolicyMetadata> getPolicyMetadatas() {
return policyMetadatas; return policyMetadatas;
} }
public OperationMode getMaintenanceMode() {
return maintenanceMode;
}
public Map<String, LifecyclePolicy> getPolicies() { public Map<String, LifecyclePolicy> getPolicies() {
return policyMetadatas.values().stream().map(LifecyclePolicyMetadata::getPolicy) return policyMetadatas.values().stream().map(LifecyclePolicyMetadata::getPolicy)
.collect(Collectors.toMap(LifecyclePolicy::getName, Function.identity())); .collect(Collectors.toMap(LifecyclePolicy::getName, Function.identity()));
@ -88,6 +99,7 @@ public class IndexLifecycleMetadata implements XPackMetaDataCustom {
@Override @Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field(POLICIES_FIELD.getPreferredName(), policyMetadatas); builder.field(POLICIES_FIELD.getPreferredName(), policyMetadatas);
builder.field(MAINTENANCE_MODE_FIELD.getPreferredName(), maintenanceMode);
return builder; return builder;
} }
@ -108,7 +120,7 @@ public class IndexLifecycleMetadata implements XPackMetaDataCustom {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(policyMetadatas); return Objects.hash(policyMetadatas, maintenanceMode);
} }
@Override @Override
@ -120,7 +132,8 @@ public class IndexLifecycleMetadata implements XPackMetaDataCustom {
return false; return false;
} }
IndexLifecycleMetadata other = (IndexLifecycleMetadata) obj; IndexLifecycleMetadata other = (IndexLifecycleMetadata) obj;
return Objects.equals(policyMetadatas, other.policyMetadatas); return Objects.equals(policyMetadatas, other.policyMetadatas)
&& Objects.equals(maintenanceMode, other.maintenanceMode);
} }
@Override @Override
@ -131,26 +144,30 @@ public class IndexLifecycleMetadata implements XPackMetaDataCustom {
public static class IndexLifecycleMetadataDiff implements NamedDiff<MetaData.Custom> { public static class IndexLifecycleMetadataDiff implements NamedDiff<MetaData.Custom> {
final Diff<Map<String, LifecyclePolicyMetadata>> policies; final Diff<Map<String, LifecyclePolicyMetadata>> policies;
final OperationMode maintenanceMode;
IndexLifecycleMetadataDiff(IndexLifecycleMetadata before, IndexLifecycleMetadata after) { IndexLifecycleMetadataDiff(IndexLifecycleMetadata before, IndexLifecycleMetadata after) {
this.policies = DiffableUtils.diff(before.policyMetadatas, after.policyMetadatas, DiffableUtils.getStringKeySerializer()); this.policies = DiffableUtils.diff(before.policyMetadatas, after.policyMetadatas, DiffableUtils.getStringKeySerializer());
this.maintenanceMode = after.maintenanceMode;
} }
public IndexLifecycleMetadataDiff(StreamInput in) throws IOException { public IndexLifecycleMetadataDiff(StreamInput in) throws IOException {
this.policies = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), LifecyclePolicyMetadata::new, this.policies = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), LifecyclePolicyMetadata::new,
IndexLifecycleMetadataDiff::readLifecyclePolicyDiffFrom); IndexLifecycleMetadataDiff::readLifecyclePolicyDiffFrom);
this.maintenanceMode = in.readEnum(OperationMode.class);
} }
@Override @Override
public MetaData.Custom apply(MetaData.Custom part) { public MetaData.Custom apply(MetaData.Custom part) {
TreeMap<String, LifecyclePolicyMetadata> newPolicies = new TreeMap<>( TreeMap<String, LifecyclePolicyMetadata> newPolicies = new TreeMap<>(
policies.apply(((IndexLifecycleMetadata) part).policyMetadatas)); policies.apply(((IndexLifecycleMetadata) part).policyMetadatas));
return new IndexLifecycleMetadata(newPolicies); return new IndexLifecycleMetadata(newPolicies, this.maintenanceMode);
} }
@Override @Override
public void writeTo(StreamOutput out) throws IOException { public void writeTo(StreamOutput out) throws IOException {
policies.writeTo(out); policies.writeTo(out);
out.writeEnum(maintenanceMode);
} }
@Override @Override

View File

@ -13,6 +13,7 @@ import org.elasticsearch.common.unit.TimeValue;
*/ */
public class LifecycleSettings { public class LifecycleSettings {
public static final String LIFECYCLE_POLL_INTERVAL = "indices.lifecycle.poll_interval"; public static final String LIFECYCLE_POLL_INTERVAL = "indices.lifecycle.poll_interval";
public static final String LIFECYCLE_MAINTENANCE_MODE = "indices.lifecycle.maintenance";
public static final String LIFECYCLE_NAME = "index.lifecycle.name"; public static final String LIFECYCLE_NAME = "index.lifecycle.name";
public static final String LIFECYCLE_PHASE = "index.lifecycle.phase"; public static final String LIFECYCLE_PHASE = "index.lifecycle.phase";
public static final String LIFECYCLE_ACTION = "index.lifecycle.action"; public static final String LIFECYCLE_ACTION = "index.lifecycle.action";

View File

@ -0,0 +1,44 @@
/*
* 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.core.indexlifecycle;
/**
* Enum representing the different modes that Index Lifecycle Service can operate in.
*/
public enum OperationMode {
/**
* This represents a state where no policies are executed
*/
MAINTENANCE {
@Override
public boolean isValidChange(OperationMode nextMode) {
return nextMode == NORMAL;
}
},
/**
* this representes a state where only sensitive actions (like {@link ShrinkAction}) will be executed
* until they finish, at which point the operation mode will move to maintenance mode.
*/
MAINTENANCE_REQUESTED {
@Override
public boolean isValidChange(OperationMode nextMode) {
return nextMode == NORMAL || nextMode == MAINTENANCE;
}
},
/**
* Normal operation where all policies are executed as normal.
*/
NORMAL {
@Override
public boolean isValidChange(OperationMode nextMode) {
return nextMode == MAINTENANCE_REQUESTED;
}
};
public abstract boolean isValidChange(OperationMode nextMode);
}

View File

@ -57,6 +57,10 @@ public class IndexLifecycleRunner {
return; return;
} }
Step currentStep = getCurrentStep(stepRegistry, policy, indexSettings); Step currentStep = getCurrentStep(stepRegistry, policy, indexSettings);
if (currentStep == null) {
throw new IllegalStateException(
"current step for index [" + indexMetaData.getIndex().getName() + "] with policy [" + policy + "] is not recognized");
}
logger.debug("running policy with current-step[" + currentStep.getKey() + "]"); logger.debug("running policy with current-step[" + currentStep.getKey() + "]");
if (currentStep instanceof TerminalPolicyStep) { if (currentStep instanceof TerminalPolicyStep) {
logger.debug("policy [" + policy + "] for index [" + indexMetaData.getIndex().getName() + "] complete, skipping execution"); logger.debug("policy [" + policy + "] for index [" + indexMetaData.getIndex().getName() + "] complete, skipping execution");

View File

@ -5,6 +5,7 @@
*/ */
package org.elasticsearch.xpack.indexlifecycle; package org.elasticsearch.xpack.indexlifecycle;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.SetOnce; import org.apache.lucene.util.SetOnce;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
@ -12,6 +13,7 @@ import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateApplier; import org.elasticsearch.cluster.ClusterStateApplier;
import org.elasticsearch.cluster.ClusterStateListener; import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.AbstractComponent;
@ -21,11 +23,15 @@ import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata; import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
import org.elasticsearch.xpack.core.indexlifecycle.OperationMode;
import org.elasticsearch.xpack.core.indexlifecycle.ShrinkAction;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey; import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
import org.elasticsearch.xpack.core.scheduler.SchedulerEngine; import org.elasticsearch.xpack.core.scheduler.SchedulerEngine;
import java.io.Closeable; import java.io.Closeable;
import java.time.Clock; import java.time.Clock;
import java.util.Collections;
import java.util.Set;
import java.util.function.LongSupplier; import java.util.function.LongSupplier;
/** /**
@ -34,6 +40,7 @@ import java.util.function.LongSupplier;
public class IndexLifecycleService extends AbstractComponent public class IndexLifecycleService extends AbstractComponent
implements ClusterStateListener, ClusterStateApplier, SchedulerEngine.Listener, Closeable { implements ClusterStateListener, ClusterStateApplier, SchedulerEngine.Listener, Closeable {
private static final Logger logger = ESLoggerFactory.getLogger(IndexLifecycleService.class); private static final Logger logger = ESLoggerFactory.getLogger(IndexLifecycleService.class);
private static final Set<String> IGNORE_ACTIONS_MAINTENANCE_REQUESTED = Collections.singleton(ShrinkAction.NAME);
private final SetOnce<SchedulerEngine> scheduler = new SetOnce<>(); private final SetOnce<SchedulerEngine> scheduler = new SetOnce<>();
private final Clock clock; private final Clock clock;
@ -93,7 +100,6 @@ public class IndexLifecycleService extends AbstractComponent
boolean pollIntervalSettingChanged = !pollInterval.equals(previousPollInterval); boolean pollIntervalSettingChanged = !pollInterval.equals(previousPollInterval);
if (scheduler.get() == null) { // metadata installed and scheduler should be kicked off. start your engines. if (scheduler.get() == null) { // metadata installed and scheduler should be kicked off. start your engines.
scheduler.set(new SchedulerEngine(clock)); scheduler.set(new SchedulerEngine(clock));
scheduler.get().register(this); scheduler.get().register(this);
@ -142,16 +148,51 @@ public class IndexLifecycleService extends AbstractComponent
} }
} }
/**
* executes the policy execution on the appropriate indices by running cluster-state tasks per index.
*
* If maintenance-mode was requested, and it is safe to move into maintenance-mode, this will also be done here
* when possible after no policies are executed.
*
* @param clusterState the current cluster state
* @param fromClusterStateChange whether things are triggered from the cluster-state-listener or the scheduler
*/
void triggerPolicies(ClusterState clusterState, boolean fromClusterStateChange) { void triggerPolicies(ClusterState clusterState, boolean fromClusterStateChange) {
IndexLifecycleMetadata currentMetadata = clusterState.metaData().custom(IndexLifecycleMetadata.TYPE);
if (currentMetadata == null) {
return;
}
OperationMode currentMode = currentMetadata.getMaintenanceMode();
if (OperationMode.MAINTENANCE.equals(currentMode)) {
return;
}
boolean safeToEnterMaintenanceMode = true; // true until proven false by a run policy
// loop through all indices in cluster state and filter for ones that are // loop through all indices in cluster state and filter for ones that are
// managed by the Index Lifecycle Service they have a index.lifecycle.name setting // managed by the Index Lifecycle Service they have a index.lifecycle.name setting
// associated to a policy // associated to a policy
clusterState.metaData().indices().valuesIt().forEachRemaining((idxMeta) -> { for (ObjectCursor<IndexMetaData> cursor : clusterState.metaData().indices().values()) {
IndexMetaData idxMeta = cursor.value;
String policyName = LifecycleSettings.LIFECYCLE_NAME_SETTING.get(idxMeta.getSettings()); String policyName = LifecycleSettings.LIFECYCLE_NAME_SETTING.get(idxMeta.getSettings());
if (Strings.isNullOrEmpty(policyName) == false) { if (Strings.isNullOrEmpty(policyName) == false) {
lifecycleRunner.runPolicy(policyName, idxMeta, clusterState, fromClusterStateChange); StepKey stepKey = IndexLifecycleRunner.getCurrentStepKey(idxMeta.getSettings());
if (OperationMode.MAINTENANCE_REQUESTED == currentMode && stepKey != null
&& IGNORE_ACTIONS_MAINTENANCE_REQUESTED.contains(stepKey.getAction()) == false) {
logger.info("skipping policy [" + policyName + "] for index [" + idxMeta.getIndex().getName()
+ "]. maintenance mode requested");
continue;
}
lifecycleRunner.runPolicy(policyName, idxMeta, clusterState, fromClusterStateChange);
safeToEnterMaintenanceMode = false; // proven false!
}
}
if (safeToEnterMaintenanceMode && OperationMode.MAINTENANCE_REQUESTED == currentMode) {
submitMaintenanceModeUpdate(OperationMode.MAINTENANCE);
} }
});
} }
@Override @Override
@ -161,4 +202,9 @@ public class IndexLifecycleService extends AbstractComponent
engine.stop(); engine.stop();
} }
} }
public void submitMaintenanceModeUpdate(OperationMode mode) {
clusterService.submitStateUpdateTask("ilm_maintenance_update",
new MaintenanceModeUpdateTask(mode));
}
} }

View File

@ -0,0 +1,49 @@
/*
* 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.indexlifecycle;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateUpdateTask;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.OperationMode;
public class MaintenanceModeUpdateTask extends ClusterStateUpdateTask {
private static final Logger logger = ESLoggerFactory.getLogger(MaintenanceModeUpdateTask.class);
private final OperationMode mode;
public MaintenanceModeUpdateTask(OperationMode mode) {
this.mode = mode;
}
OperationMode getOperationMode() {
return mode;
}
@Override
public ClusterState execute(ClusterState currentState) {
IndexLifecycleMetadata currentMetadata = currentState.metaData().custom(IndexLifecycleMetadata.TYPE);
if (currentMetadata.getMaintenanceMode().isValidChange(mode) == false) {
return currentState;
}
ClusterState.Builder builder = new ClusterState.Builder(currentState);
MetaData.Builder metadataBuilder = MetaData.builder(currentState.metaData());
metadataBuilder.putCustom(IndexLifecycleMetadata.TYPE,
new IndexLifecycleMetadata(currentMetadata.getPolicyMetadatas(), mode));
builder.metaData(metadataBuilder.build());
return builder.build();
}
@Override
public void onFailure(String source, Exception e) {
logger.error("unable to update lifecycle metadata with new mode [" + mode + "]", e);
}
}

View File

@ -81,7 +81,7 @@ public class TransportDeleteLifecycleAction extends TransportMasterNodeAction<Re
} }
SortedMap<String, LifecyclePolicyMetadata> newPolicies = new TreeMap<>(currentMetadata.getPolicyMetadatas()); SortedMap<String, LifecyclePolicyMetadata> newPolicies = new TreeMap<>(currentMetadata.getPolicyMetadatas());
newPolicies.remove(request.getPolicyName()); newPolicies.remove(request.getPolicyName());
IndexLifecycleMetadata newMetadata = new IndexLifecycleMetadata(newPolicies); IndexLifecycleMetadata newMetadata = new IndexLifecycleMetadata(newPolicies, currentMetadata.getMaintenanceMode());
newState.metaData(MetaData.builder(currentState.getMetaData()) newState.metaData(MetaData.builder(currentState.getMetaData())
.putCustom(IndexLifecycleMetadata.TYPE, newMetadata).build()); .putCustom(IndexLifecycleMetadata.TYPE, newMetadata).build());
return newState.build(); return newState.build();

View File

@ -23,6 +23,7 @@ import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.core.ClientHelper;
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata; import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.OperationMode;
import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction.Request; import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction.Request;
import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction.Response; import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction.Response;
@ -82,7 +83,7 @@ public class TransportPutLifecycleAction extends TransportMasterNodeAction<Reque
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
LifecyclePolicyMetadata lifecyclePolicyMetadata = new LifecyclePolicyMetadata(request.getPolicy(), filteredHeaders); LifecyclePolicyMetadata lifecyclePolicyMetadata = new LifecyclePolicyMetadata(request.getPolicy(), filteredHeaders);
newPolicies.put(lifecyclePolicyMetadata.getName(), lifecyclePolicyMetadata); newPolicies.put(lifecyclePolicyMetadata.getName(), lifecyclePolicyMetadata);
IndexLifecycleMetadata newMetadata = new IndexLifecycleMetadata(newPolicies); IndexLifecycleMetadata newMetadata = new IndexLifecycleMetadata(newPolicies, OperationMode.NORMAL);
newState.metaData(MetaData.builder(currentState.getMetaData()) newState.metaData(MetaData.builder(currentState.getMetaData())
.putCustom(IndexLifecycleMetadata.TYPE, newMetadata).build()); .putCustom(IndexLifecycleMetadata.TYPE, newMetadata).build());
return newState.build(); return newState.build();

View File

@ -24,6 +24,7 @@ import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
import org.elasticsearch.xpack.core.indexlifecycle.OperationMode;
import org.elasticsearch.xpack.core.indexlifecycle.MockAction; import org.elasticsearch.xpack.core.indexlifecycle.MockAction;
import org.elasticsearch.xpack.core.indexlifecycle.MockStep; import org.elasticsearch.xpack.core.indexlifecycle.MockStep;
import org.elasticsearch.xpack.core.indexlifecycle.Phase; import org.elasticsearch.xpack.core.indexlifecycle.Phase;
@ -98,7 +99,7 @@ public class ExecuteStepsUpdateTaskTests extends ESTestCase {
MetaData metaData = MetaData.builder() MetaData metaData = MetaData.builder()
.persistentSettings(settings(Version.CURRENT).build()) .persistentSettings(settings(Version.CURRENT).build())
.putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap)) .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap, OperationMode.NORMAL))
.put(IndexMetaData.builder(indexMetadata)) .put(IndexMetaData.builder(indexMetadata))
.build(); .build();
String nodeId = randomAlphaOfLength(10); String nodeId = randomAlphaOfLength(10);

View File

@ -24,6 +24,7 @@ import org.elasticsearch.xpack.core.indexlifecycle.LifecycleAction;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleType; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleType;
import org.elasticsearch.xpack.core.indexlifecycle.OperationMode;
import org.elasticsearch.xpack.core.indexlifecycle.Phase; import org.elasticsearch.xpack.core.indexlifecycle.Phase;
import org.elasticsearch.xpack.core.indexlifecycle.TestLifecycleType; import org.elasticsearch.xpack.core.indexlifecycle.TestLifecycleType;
@ -59,7 +60,7 @@ public class IndexLifecycleMetadataTests extends AbstractDiffableSerializationTe
policies.put(policyName, new LifecyclePolicyMetadata(new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, phases), policies.put(policyName, new LifecyclePolicyMetadata(new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, phases),
Collections.emptyMap())); Collections.emptyMap()));
} }
return new IndexLifecycleMetadata(policies); return new IndexLifecycleMetadata(policies, randomFrom(OperationMode.values()));
} }
@Override @Override
@ -93,10 +94,15 @@ public class IndexLifecycleMetadataTests extends AbstractDiffableSerializationTe
IndexLifecycleMetadata metadata = (IndexLifecycleMetadata) instance; IndexLifecycleMetadata metadata = (IndexLifecycleMetadata) instance;
Map<String, LifecyclePolicyMetadata> policies = metadata.getPolicyMetadatas(); Map<String, LifecyclePolicyMetadata> policies = metadata.getPolicyMetadatas();
policies = new TreeMap<>(policies); policies = new TreeMap<>(policies);
OperationMode mode = metadata.getMaintenanceMode();
if (randomBoolean()) {
String policyName = randomAlphaOfLength(10); String policyName = randomAlphaOfLength(10);
policies.put(policyName, new LifecyclePolicyMetadata( policies.put(policyName, new LifecyclePolicyMetadata(
new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, Collections.emptyMap()), Collections.emptyMap())); new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, Collections.emptyMap()), Collections.emptyMap()));
return new IndexLifecycleMetadata(policies); } else {
mode = randomValueOtherThan(metadata.getMaintenanceMode(), () -> randomFrom(OperationMode.values()));
}
return new IndexLifecycleMetadata(policies, mode);
} }
@Override @Override

View File

@ -321,6 +321,22 @@ public class IndexLifecycleRunnerTests extends ESTestCase {
Mockito.verifyZeroInteractions(clusterService); Mockito.verifyZeroInteractions(clusterService);
} }
public void testRunPolicyWithNoStepsInRegistry() {
String policyName = "cluster_state_action_policy";
StepKey stepKey = new StepKey("phase", "action", "cluster_state_action_step");
ClusterService clusterService = mock(ClusterService.class);
IndexLifecycleRunner runner = new IndexLifecycleRunner(new PolicyStepsRegistry(), clusterService, () -> 0L);
IndexMetaData indexMetaData = IndexMetaData.builder("my_index").settings(settings(Version.CURRENT))
.numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
IllegalStateException exception = expectThrows(IllegalStateException.class,
() -> runner.runPolicy(policyName, indexMetaData, null, randomBoolean()));
assertEquals("current step for index [my_index] with policy [cluster_state_action_policy] is not recognized",
exception.getMessage());
Mockito.verifyZeroInteractions(clusterService);
}
public void testRunPolicyUnknownStepType() { public void testRunPolicyUnknownStepType() {
String policyName = "cluster_state_action_policy"; String policyName = "cluster_state_action_policy";
StepKey stepKey = new StepKey("phase", "action", "cluster_state_action_step"); StepKey stepKey = new StepKey("phase", "action", "cluster_state_action_step");

View File

@ -5,6 +5,7 @@
*/ */
package org.elasticsearch.xpack.indexlifecycle; package org.elasticsearch.xpack.indexlifecycle;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.client.AdminClient; import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
@ -13,28 +14,42 @@ import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.ClusterStateUpdateTask;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.Index;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata; import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleSettings;
import org.elasticsearch.xpack.core.indexlifecycle.MockAction;
import org.elasticsearch.xpack.core.indexlifecycle.OperationMode;
import org.elasticsearch.xpack.core.indexlifecycle.Phase;
import org.elasticsearch.xpack.core.indexlifecycle.ShrinkAction;
import org.elasticsearch.xpack.core.indexlifecycle.Step;
import org.elasticsearch.xpack.core.indexlifecycle.TestLifecycleType;
import org.elasticsearch.xpack.core.scheduler.SchedulerEngine; import org.elasticsearch.xpack.core.scheduler.SchedulerEngine;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.mockito.Mockito; import org.mockito.Mockito;
import java.io.IOException;
import java.time.Clock; import java.time.Clock;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Collections;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import static org.elasticsearch.node.Node.NODE_MASTER_SETTING; import static org.elasticsearch.node.Node.NODE_MASTER_SETTING;
import static org.elasticsearch.xpack.core.indexlifecycle.AbstractStepTestCase.randomStepKey;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
@ -77,12 +92,18 @@ public class IndexLifecycleServiceTests extends ESTestCase {
indicesClient = mock(IndicesAdminClient.class); indicesClient = mock(IndicesAdminClient.class);
when(client.admin()).thenReturn(adminClient); when(client.admin()).thenReturn(adminClient);
when(adminClient.indices()).thenReturn(indicesClient); when(adminClient.indices()).thenReturn(indicesClient);
when(client.settings()).thenReturn(Settings.EMPTY);
indexLifecycleService = new IndexLifecycleService(Settings.EMPTY, client, clusterService, clock, () -> now); indexLifecycleService = new IndexLifecycleService(Settings.EMPTY, client, clusterService, clock, () -> now);
Mockito.verify(clusterService).addListener(indexLifecycleService); Mockito.verify(clusterService).addListener(indexLifecycleService);
Mockito.verify(clusterService).addStateApplier(indexLifecycleService); Mockito.verify(clusterService).addStateApplier(indexLifecycleService);
} }
@After
public void cleanup() {
indexLifecycleService.close();
}
public void testOnlyChangesStateOnMasterAndMetadataExists() { public void testOnlyChangesStateOnMasterAndMetadataExists() {
boolean isMaster = randomBoolean(); boolean isMaster = randomBoolean();
String localNodeId = isMaster ? nodeId : nodeId + "not_master"; String localNodeId = isMaster ? nodeId : nodeId + "not_master";
@ -176,11 +197,6 @@ public class IndexLifecycleServiceTests extends ESTestCase {
assertNotNull(indexLifecycleService.getScheduledJob()); assertNotNull(indexLifecycleService.getScheduledJob());
} }
@After
public void cleanup() throws IOException {
indexLifecycleService.close();
}
public void testSchedulerInitializationAndUpdate() { public void testSchedulerInitializationAndUpdate() {
TimeValue pollInterval = TimeValue.timeValueSeconds(randomIntBetween(1, 59)); TimeValue pollInterval = TimeValue.timeValueSeconds(randomIntBetween(1, 59));
MetaData metaData = MetaData.builder() MetaData metaData = MetaData.builder()
@ -221,222 +237,126 @@ public class IndexLifecycleServiceTests extends ESTestCase {
Mockito.verifyNoMoreInteractions(clusterService); Mockito.verifyNoMoreInteractions(clusterService);
} }
// /** public void testMaintenanceModeSkip() {
// * Checks that a new index does the following successfully: String policyName = randomAlphaOfLengthBetween(1, 20);
// * IndexLifecycleRunnerTests.MockInitializePolicyContextStep mockStep =
// * 1. setting index.lifecycle.date new IndexLifecycleRunnerTests.MockInitializePolicyContextStep(randomStepKey(), randomStepKey());
// * 2. sets phase MockAction mockAction = new MockAction(Collections.singletonList(mockStep));
// * 3. sets action Phase phase = new Phase("phase", TimeValue.ZERO, Collections.singletonMap("action", mockAction));
// * 4. executes action LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName,
// */ Collections.singletonMap(phase.getName(), phase));
// @SuppressWarnings("unchecked") SortedMap<String, LifecyclePolicyMetadata> policyMap = new TreeMap<>();
// public void testTriggeredWithMatchingPolicy() { policyMap.put(policyName, new LifecyclePolicyMetadata(policy, Collections.emptyMap()));
// String policyName = randomAlphaOfLengthBetween(1, 20); Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
// MockAction mockAction = new MockAction(Collections.emptyList()); IndexMetaData indexMetadata = IndexMetaData.builder(index.getName())
// Phase phase = new Phase("phase", TimeValue.ZERO, Collections.singletonMap("action", mockAction)); .settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME_SETTING.getKey(), policyName))
// LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
// Collections.singletonMap(phase.getName(), phase)); ImmutableOpenMap.Builder<String, IndexMetaData> indices = ImmutableOpenMap.<String, IndexMetaData> builder()
// SortedMap<String, LifecyclePolicy> policyMap = new TreeMap<>(); .fPut(index.getName(), indexMetadata);
// policyMap.put(policyName, policy); MetaData metaData = MetaData.builder()
// Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap, OperationMode.MAINTENANCE))
// IndexMetaData indexMetadata = IndexMetaData.builder(index.getName()) .indices(indices.build())
// .settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME_SETTING.getKey(), policyName)) .persistentSettings(settings(Version.CURRENT).build())
// .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); .build();
// ImmutableOpenMap.Builder<String, IndexMetaData> indices = ImmutableOpenMap.<String, IndexMetaData> builder() ClusterState currentState = ClusterState.builder(ClusterName.DEFAULT)
// .fPut(index.getName(), indexMetadata); .metaData(metaData)
// MetaData metaData = MetaData.builder() .nodes(DiscoveryNodes.builder().localNodeId(nodeId).masterNodeId(nodeId).add(masterNode).build())
// .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap)) .build();
// .indices(indices.build()) indexLifecycleService.triggerPolicies(currentState, randomBoolean());
// .persistentSettings(settings(Version.CURRENT).build()) assertThat(mockStep.getExecuteCount(), equalTo(0L));
// .build(); }
// ClusterState currentState = ClusterState.builder(ClusterName.DEFAULT)
// .metaData(metaData)
// .nodes(DiscoveryNodes.builder().localNodeId(nodeId).masterNodeId(nodeId).add(masterNode).build())
// .build();
//
// SchedulerEngine.Event schedulerEvent = new SchedulerEngine.Event(IndexLifecycle.NAME, randomLong(), randomLong());
//
// when(clusterService.state()).thenReturn(currentState);
//
// SetOnce<Boolean> dateUpdated = new SetOnce<>();
// SetOnce<Boolean> phaseUpdated = new SetOnce<>();
// SetOnce<Boolean> actionUpdated = new SetOnce<>();
// doAnswer(invocationOnMock -> {
// UpdateSettingsRequest request = (UpdateSettingsRequest) invocationOnMock.getArguments()[0];
// ActionListener<UpdateSettingsResponse> listener = (ActionListener<UpdateSettingsResponse>) invocationOnMock.getArguments()[1];
// UpdateSettingsTestHelper.assertSettingsRequest(request, Settings.builder()
// .put(LifecycleSettings.LIFECYCLE_INDEX_CREATION_DATE_SETTING.getKey(),
// indexMetadata.getCreationDate()).build(), index.getName());
// dateUpdated.set(true);
// listener.onResponse(UpdateSettingsTestHelper.createMockResponse(true));
// return null;
// }).doAnswer(invocationOnMock -> {
// UpdateSettingsRequest request = (UpdateSettingsRequest) invocationOnMock.getArguments()[0];
// ActionListener<UpdateSettingsResponse> listener = (ActionListener<UpdateSettingsResponse>) invocationOnMock.getArguments()[1];
// UpdateSettingsTestHelper.assertSettingsRequest(request, Settings.builder()
// .put(LifecycleSettings.LIFECYCLE_ACTION, "")
// .put(LifecycleSettings.LIFECYCLE_ACTION_TIME, -1L)
// .put(LifecycleSettings.LIFECYCLE_PHASE_TIME, now)
// .put(LifecycleSettings.LIFECYCLE_PHASE, "phase").build(), index.getName());
// phaseUpdated.set(true);
// listener.onResponse(UpdateSettingsTestHelper.createMockResponse(true));
// return null;
// }).doAnswer(invocationOnMock -> {
// UpdateSettingsRequest request = (UpdateSettingsRequest) invocationOnMock.getArguments()[0];
// ActionListener<UpdateSettingsResponse> listener = (ActionListener<UpdateSettingsResponse>) invocationOnMock.getArguments()[1];
// UpdateSettingsTestHelper.assertSettingsRequest(request, Settings.builder()
// .put(LifecycleSettings.LIFECYCLE_ACTION, MockAction.NAME)
// .put(LifecycleSettings.LIFECYCLE_ACTION_TIME, now).build(), index.getName());
// actionUpdated.set(true);
// listener.onResponse(UpdateSettingsTestHelper.createMockResponse(true));
// return null;
// }).when(indicesClient).updateSettings(any(), any());
//
// indexLifecycleService.triggered(schedulerEvent);
//
// assertThat(dateUpdated.get(), equalTo(true));
// assertThat(phaseUpdated.get(), equalTo(true));
// assertThat(actionUpdated.get(), equalTo(true));
// }
// /** public void testRequestedMaintenanceOnShrink() {
// * Check that a policy is executed without first setting the `index.lifecycle.date` setting Step.StepKey mockShrinkStep = new Step.StepKey(randomAlphaOfLength(4), ShrinkAction.NAME, randomAlphaOfLength(5));
// */ String policyName = randomAlphaOfLengthBetween(1, 20);
// @SuppressWarnings("unchecked") IndexLifecycleRunnerTests.MockInitializePolicyContextStep mockStep =
// public void testTriggeredWithDateSettingAlreadyPresent() { new IndexLifecycleRunnerTests.MockInitializePolicyContextStep(mockShrinkStep, randomStepKey());
// String policyName = randomAlphaOfLengthBetween(1, 20); MockAction mockAction = new MockAction(Collections.singletonList(mockStep));
// MockAction mockAction = new MockAction(Collections.emptyList()); Phase phase = new Phase("phase", TimeValue.ZERO, Collections.singletonMap("action", mockAction));
// Phase phase = new Phase("phase", TimeValue.ZERO, Collections.singletonMap("action", mockAction)); LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName,
// LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, Collections.singletonMap(phase.getName(), phase));
// Collections.singletonMap(phase.getName(), phase)); SortedMap<String, LifecyclePolicyMetadata> policyMap = new TreeMap<>();
// SortedMap<String, LifecyclePolicy> policyMap = new TreeMap<>(); policyMap.put(policyName, new LifecyclePolicyMetadata(policy, Collections.emptyMap()));
// policyMap.put(policyName, policy); Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
// Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); IndexMetaData indexMetadata = IndexMetaData.builder(index.getName())
// long creationDate = randomLongBetween(0, now - 1); .settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME_SETTING.getKey(), policyName)
// IndexMetaData indexMetadata = IndexMetaData.builder(index.getName()) .put(LifecycleSettings.LIFECYCLE_PHASE, mockShrinkStep.getPhase())
// .settings(settings(Version.CURRENT) .put(LifecycleSettings.LIFECYCLE_ACTION, mockShrinkStep.getAction())
// .put(LifecycleSettings.LIFECYCLE_NAME_SETTING.getKey(), policyName) .put(LifecycleSettings.LIFECYCLE_STEP, mockShrinkStep.getName()))
// .put(LifecycleSettings.LIFECYCLE_INDEX_CREATION_DATE_SETTING.getKey(), creationDate)) .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
// .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).creationDate(creationDate).build(); ImmutableOpenMap.Builder<String, IndexMetaData> indices = ImmutableOpenMap.<String, IndexMetaData> builder()
// ImmutableOpenMap.Builder<String, IndexMetaData> indices = ImmutableOpenMap.<String, IndexMetaData> builder() .fPut(index.getName(), indexMetadata);
// .fPut(index.getName(), indexMetadata); MetaData metaData = MetaData.builder()
// MetaData metaData = MetaData.builder() .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap, OperationMode.MAINTENANCE_REQUESTED))
// .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap)) .indices(indices.build())
// .indices(indices.build()) .persistentSettings(settings(Version.CURRENT).build())
// .persistentSettings(settings(Version.CURRENT).build()) .build();
// .build(); ClusterState currentState = ClusterState.builder(ClusterName.DEFAULT)
// ClusterState currentState = ClusterState.builder(ClusterName.DEFAULT) .metaData(metaData)
// .metaData(metaData) .nodes(DiscoveryNodes.builder().localNodeId(nodeId).masterNodeId(nodeId).add(masterNode).build())
// .nodes(DiscoveryNodes.builder().localNodeId(nodeId).masterNodeId(nodeId).add(masterNode).build()) .build();
// .build();
//
// SchedulerEngine.Event schedulerEvent = new SchedulerEngine.Event(IndexLifecycle.NAME, randomLong(), randomLong());
//
// when(clusterService.state()).thenReturn(currentState);
//
// SetOnce<Boolean> dateUpdated = new SetOnce<>();
// doAnswer(invocationOnMock -> {
// UpdateSettingsRequest request = (UpdateSettingsRequest) invocationOnMock.getArguments()[0];
// ActionListener<UpdateSettingsResponse> listener = (ActionListener<UpdateSettingsResponse>) invocationOnMock.getArguments()[1];
// try {
// UpdateSettingsTestHelper.assertSettingsRequest(request, Settings.builder()
// .put(LifecycleSettings.LIFECYCLE_INDEX_CREATION_DATE_SETTING.getKey(),
// indexMetadata.getCreationDate()).build(), index.getName());
// dateUpdated.set(true);
// } catch (AssertionError e) {
// // noop: here because we are either updating the phase or action prior to executing MockAction
// }
// listener.onResponse(UpdateSettingsTestHelper.createMockResponse(true));
// return null;
// }).when(indicesClient).updateSettings(any(), any());
//
// indexLifecycleService.triggered(schedulerEvent);
//
// assertNull(dateUpdated.get());
// }
// /** ClusterChangedEvent event = new ClusterChangedEvent("_source", currentState, ClusterState.EMPTY_STATE);
// * Check that if an index has an unknown lifecycle policy set it does not SetOnce<Boolean> executedShrink = new SetOnce<>();
// * execute any policy but does process other indexes. doAnswer(invocationOnMock -> {
// */ executedShrink.set(true);
// public void testTriggeredUnknownPolicyNameSet() { return null;
// String policyName = randomAlphaOfLengthBetween(1, 20); }).when(clusterService).submitStateUpdateTask(anyString(), any(ExecuteStepsUpdateTask.class));
// MockAction mockAction = new MockAction(Collections.emptyList()); indexLifecycleService.applyClusterState(new ClusterChangedEvent("change", currentState, ClusterState.EMPTY_STATE));
// Phase phase = new Phase("phase", TimeValue.ZERO, Collections.singletonMap("action", mockAction)); indexLifecycleService.clusterChanged(event);
// LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, assertTrue(executedShrink.get());
// Collections.singletonMap(phase.getName(), phase)); }
// SortedMap<String, LifecyclePolicy> policyMap = new TreeMap<>();
// policyMap.put(policyName, policy);
// Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
// IndexMetaData indexMetadata = IndexMetaData.builder(index.getName())
// .settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME_SETTING.getKey(), "foo"))
// .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
// Index index2 = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
// IndexMetaData indexMetadata2 = IndexMetaData.builder(index2.getName())
// .settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME_SETTING.getKey(), policyName))
// .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
// ImmutableOpenMap.Builder<String, IndexMetaData> indices = ImmutableOpenMap.<String, IndexMetaData> builder().fPut(index.getName(),
// indexMetadata).fPut(index2.getName(), indexMetadata2);
// MetaData metaData = MetaData.builder().putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap))
// .indices(indices.build()).persistentSettings(settings(Version.CURRENT).build()).build();
// ClusterState currentState = ClusterState.builder(ClusterName.DEFAULT).metaData(metaData)
// .nodes(DiscoveryNodes.builder().localNodeId(nodeId).masterNodeId(nodeId).add(masterNode).build()).build();
//
// SchedulerEngine.Event schedulerEvent = new SchedulerEngine.Event(IndexLifecycle.NAME, randomLong(), randomLong());
//
// when(clusterService.state()).thenReturn(currentState);
//
// doAnswer(invocationOnMock -> {
// @SuppressWarnings("unchecked")
// ActionListener<UpdateSettingsResponse> listener = (ActionListener<UpdateSettingsResponse>) invocationOnMock.getArguments()[1];
// listener.onResponse(UpdateSettingsTestHelper.createMockResponse(true));
// return null;
//
// }).when(indicesClient).updateSettings(any(), any());
//
// indexLifecycleService.triggered(schedulerEvent);
// }
// /** public void testRequestedMaintenanceOnSafeAction() {
// * Check that if an index has no lifecycle policy set it does not execute String policyName = randomAlphaOfLengthBetween(1, 20);
// * any policy but does process other indexes. Step.StepKey currentStepKey = randomStepKey();
// */ IndexLifecycleRunnerTests.MockInitializePolicyContextStep mockStep =
// public void testTriggeredNoPolicyNameSet() { new IndexLifecycleRunnerTests.MockInitializePolicyContextStep(currentStepKey, randomStepKey());
// String policyName = randomAlphaOfLengthBetween(1, 20); MockAction mockAction = new MockAction(Collections.singletonList(mockStep));
// MockAction mockAction = new MockAction(Collections.emptyList()); Phase phase = new Phase("phase", TimeValue.ZERO, Collections.singletonMap("action", mockAction));
// Phase phase = new Phase("phase", TimeValue.ZERO, Collections.singletonMap("action", mockAction)); LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName,
// LifecyclePolicy policy = new LifecyclePolicy(TestLifecycleType.INSTANCE, policyName, Collections.singletonMap(phase.getName(), phase));
// Collections.singletonMap(phase.getName(), phase)); SortedMap<String, LifecyclePolicyMetadata> policyMap = new TreeMap<>();
// SortedMap<String, LifecyclePolicy> policyMap = new TreeMap<>(); policyMap.put(policyName, new LifecyclePolicyMetadata(policy, Collections.emptyMap()));
// policyMap.put(policyName, policy); Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20));
// Index index = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); IndexMetaData indexMetadata = IndexMetaData.builder(index.getName())
// IndexMetaData indexMetadata = IndexMetaData.builder(index.getName()).settings(settings(Version.CURRENT)) .settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME_SETTING.getKey(), policyName)
// .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); .put(LifecycleSettings.LIFECYCLE_PHASE, currentStepKey.getPhase())
// Index index2 = new Index(randomAlphaOfLengthBetween(1, 20), randomAlphaOfLengthBetween(1, 20)); .put(LifecycleSettings.LIFECYCLE_ACTION, currentStepKey.getAction())
// IndexMetaData indexMetadata2 = IndexMetaData.builder(index2.getName()) .put(LifecycleSettings.LIFECYCLE_STEP, currentStepKey.getName()))
// .settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME_SETTING.getKey(), policyName)) .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
// .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build(); ImmutableOpenMap.Builder<String, IndexMetaData> indices = ImmutableOpenMap.<String, IndexMetaData> builder()
// ImmutableOpenMap.Builder<String, IndexMetaData> indices = ImmutableOpenMap.<String, IndexMetaData> builder().fPut(index.getName(), .fPut(index.getName(), indexMetadata);
// indexMetadata).fPut(index2.getName(), indexMetadata2); MetaData metaData = MetaData.builder()
// MetaData metaData = MetaData.builder().putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap)) .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap, OperationMode.MAINTENANCE_REQUESTED))
// .indices(indices.build()).persistentSettings(settings(Version.CURRENT).build()).build(); .indices(indices.build())
// ClusterState currentState = ClusterState.builder(ClusterName.DEFAULT).metaData(metaData) .persistentSettings(settings(Version.CURRENT).build())
// .nodes(DiscoveryNodes.builder().localNodeId(nodeId).masterNodeId(nodeId).add(masterNode).build()).build(); .build();
// ClusterState currentState = ClusterState.builder(ClusterName.DEFAULT)
// SchedulerEngine.Event schedulerEvent = new SchedulerEngine.Event(IndexLifecycle.NAME, randomLong(), randomLong()); .metaData(metaData)
// .nodes(DiscoveryNodes.builder().localNodeId(nodeId).masterNodeId(nodeId).add(masterNode).build())
// when(clusterService.state()).thenReturn(currentState); .build();
//
// doAnswer(invocationOnMock -> { ClusterChangedEvent event = new ClusterChangedEvent("_source", currentState, ClusterState.EMPTY_STATE);
// @SuppressWarnings("unchecked")
// ActionListener<UpdateSettingsResponse> listener = (ActionListener<UpdateSettingsResponse>) invocationOnMock.getArguments()[1]; SetOnce<Boolean> ranPolicy = new SetOnce<>();
// listener.onResponse(UpdateSettingsTestHelper.createMockResponse(true)); SetOnce<Boolean> moveToMaintenance = new SetOnce<>();
// return null; doAnswer(invocationOnMock -> {
// ranPolicy.set(true);
// }).when(indicesClient).updateSettings(any(), any()); throw new AssertionError("invalid invocation");
// }).when(clusterService).submitStateUpdateTask(anyString(), any(ExecuteStepsUpdateTask.class));
// indexLifecycleService.triggered(schedulerEvent);
// } doAnswer(invocationOnMock -> {
MaintenanceModeUpdateTask task = (MaintenanceModeUpdateTask) invocationOnMock.getArguments()[1];
assertThat(task.getOperationMode(), equalTo(OperationMode.MAINTENANCE));
moveToMaintenance.set(true);
return null;
}).when(clusterService).submitStateUpdateTask(anyString(), any(MaintenanceModeUpdateTask.class));
indexLifecycleService.clusterChanged(event);
assertNull(ranPolicy.get());
assertTrue(moveToMaintenance.get());
}
public void testTriggeredDifferentJob() { public void testTriggeredDifferentJob() {
SchedulerEngine.Event schedulerEvent = new SchedulerEngine.Event("foo", randomLong(), randomLong()); SchedulerEngine.Event schedulerEvent = new SchedulerEngine.Event("foo", randomLong(), randomLong());

View File

@ -0,0 +1,61 @@
/*
* 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.indexlifecycle;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.OperationMode;
import java.util.Collections;
import static org.hamcrest.Matchers.equalTo;
public class MaintenanceModeUpdateTaskTests extends ESTestCase {
public void testExecute() {
assertMove(OperationMode.NORMAL, randomFrom(OperationMode.MAINTENANCE_REQUESTED));
assertMove(OperationMode.MAINTENANCE_REQUESTED, randomFrom(OperationMode.NORMAL, OperationMode.MAINTENANCE));
assertMove(OperationMode.MAINTENANCE, randomFrom(OperationMode.NORMAL));
OperationMode mode = randomFrom(OperationMode.values());
assertNoMove(mode, mode);
assertNoMove(OperationMode.MAINTENANCE, randomFrom(OperationMode.MAINTENANCE_REQUESTED));
assertNoMove(OperationMode.NORMAL, randomFrom(OperationMode.MAINTENANCE));
}
private void assertMove(OperationMode currentMode, OperationMode requestedMode) {
OperationMode newMode = executeUpdate(currentMode, requestedMode, false);
assertThat(newMode, equalTo(requestedMode));
}
private void assertNoMove(OperationMode currentMode, OperationMode requestedMode) {
OperationMode newMode = executeUpdate(currentMode, requestedMode, true);
assertThat(newMode, equalTo(currentMode));
}
private OperationMode executeUpdate(OperationMode currentMode, OperationMode requestMode, boolean assertSameClusterState) {
IndexLifecycleMetadata indexLifecycleMetadata = new IndexLifecycleMetadata(Collections.emptyMap(), currentMode);
ImmutableOpenMap.Builder<String, MetaData.Custom> customsMapBuilder = ImmutableOpenMap.builder();
MetaData metaData = MetaData.builder()
.customs(customsMapBuilder.fPut(IndexLifecycleMetadata.TYPE, indexLifecycleMetadata).build())
.persistentSettings(settings(Version.CURRENT).build())
.build();
ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metaData(metaData).build();
MaintenanceModeUpdateTask task = new MaintenanceModeUpdateTask(requestMode);
ClusterState newState = task.execute(state);
if (assertSameClusterState) {
assertSame(state, newState);
}
IndexLifecycleMetadata newMetaData = newState.metaData().custom(IndexLifecycleMetadata.TYPE);
assertThat(newMetaData.getPolicyMetadatas(), equalTo(indexLifecycleMetadata.getPolicyMetadatas()));
return newMetaData.getMaintenanceMode();
}
}

View File

@ -21,6 +21,7 @@ import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyTests; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyTests;
import org.elasticsearch.xpack.core.indexlifecycle.OperationMode;
import org.elasticsearch.xpack.core.indexlifecycle.MockStep; import org.elasticsearch.xpack.core.indexlifecycle.MockStep;
import org.elasticsearch.xpack.core.indexlifecycle.Step; import org.elasticsearch.xpack.core.indexlifecycle.Step;
import org.mockito.Mockito; import org.mockito.Mockito;
@ -108,7 +109,7 @@ public class PolicyStepsRegistryTests extends ESTestCase {
new LifecyclePolicyMetadata(newPolicy, headers)); new LifecyclePolicyMetadata(newPolicy, headers));
MetaData metaData = MetaData.builder() MetaData metaData = MetaData.builder()
.persistentSettings(settings(Version.CURRENT).build()) .persistentSettings(settings(Version.CURRENT).build())
.putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap)) .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap, OperationMode.NORMAL))
.build(); .build();
String nodeId = randomAlphaOfLength(10); String nodeId = randomAlphaOfLength(10);
DiscoveryNode masterNode = DiscoveryNode.createLocal(settings(Version.CURRENT) DiscoveryNode masterNode = DiscoveryNode.createLocal(settings(Version.CURRENT)
@ -150,7 +151,8 @@ public class PolicyStepsRegistryTests extends ESTestCase {
currentState = ClusterState.builder(currentState) currentState = ClusterState.builder(currentState)
.metaData( .metaData(
MetaData.builder(metaData) MetaData.builder(metaData)
.putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(Collections.emptyMap()))).build(); .putCustom(IndexLifecycleMetadata.TYPE,
new IndexLifecycleMetadata(Collections.emptyMap(), OperationMode.NORMAL))).build();
registry.update(currentState, client, () -> 0L); registry.update(currentState, client, () -> 0L);
assertTrue(registry.getLifecyclePolicyMap().isEmpty()); assertTrue(registry.getLifecyclePolicyMap().isEmpty());
assertTrue(registry.getFirstStepMap().isEmpty()); assertTrue(registry.getFirstStepMap().isEmpty());
@ -171,7 +173,7 @@ public class PolicyStepsRegistryTests extends ESTestCase {
new LifecyclePolicyMetadata(newPolicy, headers)); new LifecyclePolicyMetadata(newPolicy, headers));
MetaData metaData = MetaData.builder() MetaData metaData = MetaData.builder()
.persistentSettings(settings(Version.CURRENT).build()) .persistentSettings(settings(Version.CURRENT).build())
.putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap)) .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(policyMap, OperationMode.NORMAL))
.build(); .build();
String nodeId = randomAlphaOfLength(10); String nodeId = randomAlphaOfLength(10);
DiscoveryNode masterNode = DiscoveryNode.createLocal(settings(Version.CURRENT) DiscoveryNode masterNode = DiscoveryNode.createLocal(settings(Version.CURRENT)
@ -192,7 +194,7 @@ public class PolicyStepsRegistryTests extends ESTestCase {
MetaData.builder(metaData) MetaData.builder(metaData)
.putCustom(IndexLifecycleMetadata.TYPE, .putCustom(IndexLifecycleMetadata.TYPE,
new IndexLifecycleMetadata(Collections.singletonMap(policyName, new IndexLifecycleMetadata(Collections.singletonMap(policyName,
new LifecyclePolicyMetadata(newPolicy, Collections.emptyMap()))))) new LifecyclePolicyMetadata(newPolicy, Collections.emptyMap())), OperationMode.NORMAL)))
.build(); .build();
registry.update(currentState, client, () -> 0L); registry.update(currentState, client, () -> 0L);
// TODO(talevy): assert changes... right now we do not support updates to policies. will require internal cleanup // TODO(talevy): assert changes... right now we do not support updates to policies. will require internal cleanup