diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/state/ClusterStateRequestBuilder.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/state/ClusterStateRequestBuilder.java index 979c81c3c34ca..6ec7763e31b25 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/state/ClusterStateRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/state/ClusterStateRequestBuilder.java @@ -68,6 +68,15 @@ public ClusterStateRequestBuilder setNodes(boolean filter) { return this; } + /** + * Should the cluster state result include the {@link org.elasticsearch.cluster.ClusterState.Custom}. Defaults + * to true. + */ + public ClusterStateRequestBuilder setCustoms(boolean filter) { + request.customs(filter); + return this; + } + /** * Should the cluster state result include the {@link org.elasticsearch.cluster.routing.RoutingTable}. Defaults * to true. diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java index 601c69b3189e3..9efdbfa47148b 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java @@ -126,7 +126,11 @@ protected void masterOperation(final ClusterStateRequest request, final ClusterS builder.metaData(mdBuilder); } if (request.customs()) { - builder.customs(currentState.customs()); + for (ObjectObjectCursor custom : currentState.customs()) { + if (custom.value.isPrivate() == false) { + builder.putCustom(custom.key, custom.value); + } + } } listener.onResponse(new ClusterStateResponse(currentState.getClusterName(), builder.build(), serializeFullClusterState(currentState, Version.CURRENT).length())); diff --git a/core/src/main/java/org/elasticsearch/cluster/ClusterState.java b/core/src/main/java/org/elasticsearch/cluster/ClusterState.java index 576fd760a08e2..3f6409cc5dfd0 100644 --- a/core/src/main/java/org/elasticsearch/cluster/ClusterState.java +++ b/core/src/main/java/org/elasticsearch/cluster/ClusterState.java @@ -92,6 +92,14 @@ public class ClusterState implements ToXContentFragment, Diffable public static final ClusterState EMPTY_STATE = builder(ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)).build(); public interface Custom extends NamedDiffable, ToXContent { + + /** + * Returns true iff this {@link Custom} is private to the cluster and should never be send to a client. + * The default is false; + */ + default boolean isPrivate() { + return false; + } } private static final NamedDiffableValueSerializer CUSTOM_VALUE_SERIALIZER = new NamedDiffableValueSerializer<>(Custom.class); diff --git a/core/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java b/core/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java index 89d33043e574f..3a4e414275b0a 100644 --- a/core/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java +++ b/core/src/test/java/org/elasticsearch/cluster/SimpleClusterStateIT.java @@ -28,19 +28,32 @@ import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.routing.RoutingTable; +import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Strings; import org.elasticsearch.common.UUIDs; import org.elasticsearch.common.collect.ImmutableOpenMap; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.plugins.ClusterPlugin; +import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.hamcrest.CollectionAssertions; import org.junit.Before; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertIndexTemplateExists; @@ -54,6 +67,11 @@ */ public class SimpleClusterStateIT extends ESIntegTestCase { + @Override + protected Collection> nodePlugins() { + return Collections.singletonList(PrivateCustomPlugin.class); + } + @Before public void indexData() throws Exception { index("foo", "bar", "1", XContentFactory.jsonBuilder().startObject().field("foo", "foo").endObject()); @@ -258,4 +276,67 @@ public void testIndicesIgnoreUnavailableFalse() throws Exception { assertThat(e.getMessage(), is("no such index")); } } + + public void testPrivateCustomsAreExcluded() { + ClusterStateResponse clusterStateResponse = client().admin().cluster().prepareState().setCustoms(true).get(); + assertFalse(clusterStateResponse.getState().customs().containsKey("test")); + // just to make sure there is something + assertTrue(clusterStateResponse.getState().customs().containsKey(SnapshotDeletionsInProgress.TYPE)); + ClusterState state = internalCluster().getInstance(ClusterService.class).state(); + assertTrue(state.customs().containsKey("test")); + } + + private static class TestCustom extends AbstractNamedDiffable implements ClusterState.Custom { + + private final int value; + + TestCustom(int value) { + this.value = value; + } + + TestCustom(StreamInput in) throws IOException { + this.value = in.readInt(); + } + @Override + public String getWriteableName() { + return "test"; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeInt(value); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder; + } + + static NamedDiff readDiffFrom(StreamInput in) throws IOException { + return readDiffFrom(ClusterState.Custom.class, "test", in); + } + + @Override + public boolean isPrivate() { + return true; + } + } + + public static class PrivateCustomPlugin extends Plugin implements ClusterPlugin { + + public PrivateCustomPlugin() {} + + @Override + public Map> getInitialClusterStateCustomSupplier() { + return Collections.singletonMap("test", () -> new TestCustom(1)); + } + + @Override + public List getNamedWriteables() { + List entries = new ArrayList<>(); + entries.add(new NamedWriteableRegistry.Entry(ClusterState.Custom.class, "test", TestCustom::new)); + entries.add(new NamedWriteableRegistry.Entry(NamedDiff.class, "test", TestCustom::readDiffFrom)); + return entries; + } + } }