Skip to content

Commit b01d321

Browse files
committed
Introduce transport API for cluster bootstrapping
- Introduces a transport API for bootstrapping a Zen2 cluster - Introduces a transport API for requesting the set of nodes that a master-eligible node has discovered and for waiting until this comprises the expected number of nodes. - Alters ESIntegTestCase to use these APIs when forming a cluster, rather than injecting the initial configuration directly.
1 parent 7a3cd10 commit b01d321

22 files changed

+1790
-70
lines changed

server/src/main/java/org/elasticsearch/action/ActionModule.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
import org.apache.logging.log4j.LogManager;
2424
import org.elasticsearch.action.admin.cluster.allocation.ClusterAllocationExplainAction;
2525
import org.elasticsearch.action.admin.cluster.allocation.TransportClusterAllocationExplainAction;
26+
import org.elasticsearch.action.admin.cluster.bootstrap.BootstrapClusterAction;
27+
import org.elasticsearch.action.admin.cluster.bootstrap.GetDiscoveredNodesAction;
28+
import org.elasticsearch.action.admin.cluster.bootstrap.TransportBootstrapClusterAction;
29+
import org.elasticsearch.action.admin.cluster.bootstrap.TransportGetDiscoveredNodesAction;
2630
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
2731
import org.elasticsearch.action.admin.cluster.health.TransportClusterHealthAction;
2832
import org.elasticsearch.action.admin.cluster.node.hotthreads.NodesHotThreadsAction;
@@ -422,6 +426,8 @@ public <Request extends ActionRequest, Response extends ActionResponse> void reg
422426
actions.register(GetTaskAction.INSTANCE, TransportGetTaskAction.class);
423427
actions.register(CancelTasksAction.INSTANCE, TransportCancelTasksAction.class);
424428

429+
actions.register(GetDiscoveredNodesAction.INSTANCE, TransportGetDiscoveredNodesAction.class);
430+
actions.register(BootstrapClusterAction.INSTANCE, TransportBootstrapClusterAction.class);
425431
actions.register(ClusterAllocationExplainAction.INSTANCE, TransportClusterAllocationExplainAction.class);
426432
actions.register(ClusterStatsAction.INSTANCE, TransportClusterStatsAction.class);
427433
actions.register(ClusterStateAction.INSTANCE, TransportClusterStateAction.class);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.action.admin.cluster.bootstrap;
20+
21+
import org.elasticsearch.action.Action;
22+
import org.elasticsearch.action.support.master.AcknowledgedResponse;
23+
24+
public class BootstrapClusterAction extends Action<AcknowledgedResponse> {
25+
public static final BootstrapClusterAction INSTANCE = new BootstrapClusterAction();
26+
public static final String NAME = "cluster:coordination/bootstrap_cluster";
27+
28+
private BootstrapClusterAction() {
29+
super(NAME);
30+
}
31+
32+
@Override
33+
public AcknowledgedResponse newResponse() {
34+
return new AcknowledgedResponse();
35+
}
36+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.action.admin.cluster.bootstrap;
20+
21+
import org.elasticsearch.action.ActionRequest;
22+
import org.elasticsearch.action.ActionRequestValidationException;
23+
import org.elasticsearch.common.io.stream.StreamInput;
24+
import org.elasticsearch.common.io.stream.StreamOutput;
25+
26+
import java.io.IOException;
27+
28+
public class BootstrapClusterRequest extends ActionRequest {
29+
private BootstrapConfiguration bootstrapConfiguration;
30+
31+
public BootstrapClusterRequest bootstrapConfiguration(BootstrapConfiguration bootstrapConfiguration) {
32+
this.bootstrapConfiguration = bootstrapConfiguration;
33+
return this;
34+
}
35+
36+
@Override
37+
public ActionRequestValidationException validate() {
38+
return null;
39+
}
40+
41+
public BootstrapConfiguration bootstrapConfiguration() {
42+
return bootstrapConfiguration;
43+
}
44+
45+
@Override
46+
public void readFrom(StreamInput in) throws IOException {
47+
super.readFrom(in);
48+
bootstrapConfiguration = new BootstrapConfiguration(in);
49+
}
50+
51+
@Override
52+
public void writeTo(StreamOutput out) throws IOException {
53+
super.writeTo(out);
54+
bootstrapConfiguration.writeTo(out);
55+
}
56+
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.action.admin.cluster.bootstrap;
20+
21+
import org.elasticsearch.ElasticsearchException;
22+
import org.elasticsearch.cluster.ClusterState.VotingConfiguration;
23+
import org.elasticsearch.cluster.node.DiscoveryNode;
24+
import org.elasticsearch.common.Nullable;
25+
import org.elasticsearch.common.io.stream.StreamInput;
26+
import org.elasticsearch.common.io.stream.StreamOutput;
27+
import org.elasticsearch.common.io.stream.Writeable;
28+
29+
import java.io.IOException;
30+
import java.util.HashSet;
31+
import java.util.List;
32+
import java.util.Objects;
33+
import java.util.Set;
34+
import java.util.stream.Collectors;
35+
36+
public class BootstrapConfiguration implements Writeable {
37+
38+
private final List<NodeDescription> nodeDescriptions;
39+
40+
public BootstrapConfiguration(List<NodeDescription> nodeDescriptions) {
41+
if (nodeDescriptions.isEmpty()) {
42+
throw new IllegalArgumentException("cannot create empty bootstrap configuration");
43+
}
44+
this.nodeDescriptions = nodeDescriptions;
45+
}
46+
47+
public BootstrapConfiguration(StreamInput in) throws IOException {
48+
nodeDescriptions = in.readList(NodeDescription::new);
49+
assert nodeDescriptions.isEmpty() == false;
50+
}
51+
52+
public List<NodeDescription> getNodeDescriptions() {
53+
return nodeDescriptions;
54+
}
55+
56+
public VotingConfiguration resolve(Iterable<DiscoveryNode> discoveredNodes) {
57+
final Set<DiscoveryNode> selectedNodes = new HashSet<>();
58+
for (final NodeDescription nodeDescription : nodeDescriptions) {
59+
final DiscoveryNode discoveredNode = nodeDescription.resolve(discoveredNodes);
60+
if (selectedNodes.add(discoveredNode) == false) {
61+
throw new ElasticsearchException("multiple nodes matching {} in {}", discoveredNode, this);
62+
}
63+
}
64+
65+
final Set<String> nodeIds = selectedNodes.stream().map(DiscoveryNode::getId).collect(Collectors.toSet());
66+
assert nodeIds.size() == selectedNodes.size() : selectedNodes + " does not contain distinct IDs";
67+
return new VotingConfiguration(nodeIds);
68+
}
69+
70+
@Override
71+
public void writeTo(StreamOutput out) throws IOException {
72+
out.writeList(nodeDescriptions);
73+
}
74+
75+
@Override
76+
public String toString() {
77+
return "BootstrapConfiguration{" +
78+
"nodeDescriptions=" + nodeDescriptions +
79+
'}';
80+
}
81+
82+
@Override
83+
public boolean equals(Object o) {
84+
if (this == o) return true;
85+
if (o == null || getClass() != o.getClass()) return false;
86+
BootstrapConfiguration that = (BootstrapConfiguration) o;
87+
return Objects.equals(nodeDescriptions, that.nodeDescriptions);
88+
}
89+
90+
@Override
91+
public int hashCode() {
92+
return Objects.hash(nodeDescriptions);
93+
}
94+
95+
public static class NodeDescription implements Writeable {
96+
97+
@Nullable
98+
private final String id;
99+
100+
private final String name;
101+
102+
@Nullable
103+
public String getId() {
104+
return id;
105+
}
106+
107+
public String getName() {
108+
return name;
109+
}
110+
111+
public NodeDescription(@Nullable String id, String name) {
112+
this.id = id;
113+
this.name = Objects.requireNonNull(name);
114+
}
115+
116+
public NodeDescription(DiscoveryNode discoveryNode) {
117+
this(discoveryNode.getId(), discoveryNode.getName());
118+
}
119+
120+
public NodeDescription(StreamInput in) throws IOException {
121+
this(in.readOptionalString(), in.readString());
122+
}
123+
124+
@Override
125+
public void writeTo(StreamOutput out) throws IOException {
126+
out.writeOptionalString(id);
127+
out.writeString(name);
128+
}
129+
130+
@Override
131+
public String toString() {
132+
return "NodeDescription{" +
133+
"id='" + id + '\'' +
134+
", name='" + name + '\'' +
135+
'}';
136+
}
137+
138+
public DiscoveryNode resolve(Iterable<DiscoveryNode> discoveredNodes) {
139+
DiscoveryNode selectedNode = null;
140+
for (final DiscoveryNode discoveredNode : discoveredNodes) {
141+
assert discoveredNode.isMasterNode() : discoveredNode;
142+
if (discoveredNode.getName().equals(name)) {
143+
if (id == null || id.equals(discoveredNode.getId())) {
144+
if (selectedNode != null) {
145+
throw new ElasticsearchException(
146+
"discovered multiple nodes matching {} in {}", this, discoveredNodes);
147+
}
148+
selectedNode = discoveredNode;
149+
} else {
150+
throw new ElasticsearchException("node id mismatch comparing {} to {}", this, discoveredNode);
151+
}
152+
} else if (id != null && id.equals(discoveredNode.getId())) {
153+
throw new ElasticsearchException("node name mismatch comparing {} to {}", this, discoveredNode);
154+
}
155+
}
156+
if (selectedNode == null) {
157+
throw new ElasticsearchException("no node matching {} found in {}", this, discoveredNodes);
158+
}
159+
160+
return selectedNode;
161+
}
162+
163+
@Override
164+
public boolean equals(Object o) {
165+
if (this == o) return true;
166+
if (o == null || getClass() != o.getClass()) return false;
167+
NodeDescription that = (NodeDescription) o;
168+
return Objects.equals(id, that.id) &&
169+
Objects.equals(name, that.name);
170+
}
171+
172+
@Override
173+
public int hashCode() {
174+
return Objects.hash(id, name);
175+
}
176+
}
177+
}
178+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.action.admin.cluster.bootstrap;
20+
21+
import org.elasticsearch.action.Action;
22+
23+
public class GetDiscoveredNodesAction extends Action<GetDiscoveredNodesResponse> {
24+
public static final GetDiscoveredNodesAction INSTANCE = new GetDiscoveredNodesAction();
25+
public static final String NAME = "cluster:monitor/discovered_nodes";
26+
27+
private GetDiscoveredNodesAction() {
28+
super(NAME);
29+
}
30+
31+
@Override
32+
public GetDiscoveredNodesResponse newResponse() {
33+
return new GetDiscoveredNodesResponse();
34+
}
35+
}

0 commit comments

Comments
 (0)