55 */
66package org .elasticsearch .cluster .coordination ;
77
8+ import org .elasticsearch .Version ;
9+ import org .elasticsearch .action .admin .cluster .repositories .verify .VerifyRepositoryResponse ;
10+ import org .elasticsearch .action .admin .cluster .snapshots .create .CreateSnapshotResponse ;
11+ import org .elasticsearch .action .admin .cluster .snapshots .restore .RestoreSnapshotResponse ;
12+ import org .elasticsearch .client .Client ;
13+ import org .elasticsearch .cluster .metadata .RepositoryMetadata ;
14+ import org .elasticsearch .cluster .node .DiscoveryNode ;
15+ import org .elasticsearch .cluster .node .DiscoveryNodeRole ;
16+ import org .elasticsearch .cluster .service .ClusterService ;
817import org .elasticsearch .common .Priority ;
18+ import org .elasticsearch .common .blobstore .BlobStore ;
919import org .elasticsearch .common .settings .Settings ;
20+ import org .elasticsearch .common .xcontent .NamedXContentRegistry ;
1021import org .elasticsearch .discovery .MasterNotDiscoveredException ;
22+ import org .elasticsearch .env .Environment ;
23+ import org .elasticsearch .indices .recovery .RecoverySettings ;
1124import org .elasticsearch .node .Node ;
1225import org .elasticsearch .plugins .Plugin ;
26+ import org .elasticsearch .plugins .RepositoryPlugin ;
27+ import org .elasticsearch .repositories .Repository ;
28+ import org .elasticsearch .repositories .fs .FsRepository ;
29+ import org .elasticsearch .snapshots .SnapshotInfo ;
30+ import org .elasticsearch .snapshots .SnapshotState ;
1331import org .elasticsearch .test .ESIntegTestCase ;
1432import org .elasticsearch .test .ESIntegTestCase .Scope ;
33+ import org .hamcrest .Matchers ;
1534
35+ import java .util .Arrays ;
1636import java .util .Collection ;
1737import java .util .Collections ;
38+ import java .util .List ;
39+ import java .util .Map ;
1840import java .util .Set ;
1941
2042import static org .elasticsearch .test .NodeRoles .addRoles ;
2143import static org .elasticsearch .test .NodeRoles .onlyRole ;
44+ import static org .elasticsearch .test .NodeRoles .onlyRoles ;
45+ import static org .elasticsearch .test .hamcrest .ElasticsearchAssertions .assertAcked ;
2246import static org .hamcrest .CoreMatchers .containsString ;
2347import static org .hamcrest .CoreMatchers .equalTo ;
48+ import static org .hamcrest .Matchers .greaterThan ;
2449import static org .hamcrest .Matchers .hasSize ;
2550import static org .hamcrest .Matchers .nullValue ;
2651
@@ -29,7 +54,7 @@ public class VotingOnlyNodePluginTests extends ESIntegTestCase {
2954
3055 @ Override
3156 protected Collection <Class <? extends Plugin >> nodePlugins () {
32- return Collections . singleton (LocalStateVotingOnlyNodePlugin .class );
57+ return Arrays . asList (LocalStateVotingOnlyNodePlugin . class , RepositoryVerifyAccessPlugin .class );
3358 }
3459
3560 public void testRequireVotingOnlyNodeToBeMasterEligible () {
@@ -115,4 +140,86 @@ public void testVotingOnlyNodesCannotBeMasterWithoutFullMasterNodes() throws Exc
115140 final String newMasterId = client ().admin ().cluster ().prepareState ().get ().getState ().nodes ().getMasterNodeId ();
116141 assertNotEquals (oldMasterId , newMasterId );
117142 }
143+
144+ public void testBasicSnapshotRestoreWorkFlow () {
145+ internalCluster ().setBootstrapMasterNodeIndex (0 );
146+ internalCluster ().startNodes (2 );
147+ // dedicated voting-only master node
148+ final String dedicatedVotingOnlyNode = internalCluster ().startNode (
149+ onlyRoles (Set .of (DiscoveryNodeRole .MASTER_ROLE , VotingOnlyNodePlugin .VOTING_ONLY_NODE_ROLE )));
150+ // voting-only master node that also has data
151+ final String nonDedicatedVotingOnlyNode = internalCluster ().startNode (
152+ addRoles (Set .of (VotingOnlyNodePlugin .VOTING_ONLY_NODE_ROLE )));
153+
154+ assertAcked (client ().admin ().cluster ().preparePutRepository ("test-repo" )
155+ .setType ("verifyaccess-fs" ).setSettings (Settings .builder ().put ("location" , randomRepoPath ())
156+ .put ("compress" , randomBoolean ())));
157+ createIndex ("test-idx-1" );
158+ createIndex ("test-idx-2" );
159+ createIndex ("test-idx-3" );
160+ ensureGreen ();
161+
162+ VerifyRepositoryResponse verifyResponse = client ().admin ().cluster ().prepareVerifyRepository ("test-repo" ).get ();
163+ // only the da
164+ assertEquals (3 , verifyResponse .getNodes ().size ());
165+ assertTrue (verifyResponse .getNodes ().stream ().noneMatch (nw -> nw .getName ().equals (dedicatedVotingOnlyNode )));
166+ assertTrue (verifyResponse .getNodes ().stream ().anyMatch (nw -> nw .getName ().equals (nonDedicatedVotingOnlyNode )));
167+
168+ final String [] indicesToSnapshot = {"test-idx-*" , "-test-idx-3" };
169+
170+ logger .info ("--> snapshot" );
171+ Client client = client ();
172+ CreateSnapshotResponse createSnapshotResponse = client .admin ().cluster ().prepareCreateSnapshot ("test-repo" , "test-snap" )
173+ .setWaitForCompletion (true ).setIndices (indicesToSnapshot ).get ();
174+ assertThat (createSnapshotResponse .getSnapshotInfo ().successfulShards (), greaterThan (0 ));
175+ assertThat (createSnapshotResponse .getSnapshotInfo ().successfulShards (),
176+ Matchers .equalTo (createSnapshotResponse .getSnapshotInfo ().totalShards ()));
177+
178+ List <SnapshotInfo > snapshotInfos = client .admin ().cluster ().prepareGetSnapshots ("test-repo" )
179+ .setSnapshots (randomFrom ("test-snap" , "_all" , "*" , "*-snap" , "test*" )).get ().getSnapshots ("test-repo" );
180+ assertThat (snapshotInfos .size (), Matchers .equalTo (1 ));
181+ SnapshotInfo snapshotInfo = snapshotInfos .get (0 );
182+ assertThat (snapshotInfo .state (), Matchers .equalTo (SnapshotState .SUCCESS ));
183+ assertThat (snapshotInfo .version (), Matchers .equalTo (Version .CURRENT ));
184+
185+ logger .info ("--> close indices" );
186+ client .admin ().indices ().prepareClose ("test-idx-1" , "test-idx-2" ).get ();
187+
188+ logger .info ("--> restore all indices from the snapshot" );
189+ RestoreSnapshotResponse restoreSnapshotResponse = client .admin ().cluster ().prepareRestoreSnapshot ("test-repo" , "test-snap" )
190+ .setWaitForCompletion (true ).execute ().actionGet ();
191+ assertThat (restoreSnapshotResponse .getRestoreInfo ().totalShards (), greaterThan (0 ));
192+
193+ ensureGreen ();
194+ }
195+
196+ public static class RepositoryVerifyAccessPlugin extends org .elasticsearch .plugins .Plugin implements RepositoryPlugin {
197+
198+ @ Override
199+ public Map <String , Repository .Factory > getRepositories (Environment env , NamedXContentRegistry namedXContentRegistry ,
200+ ClusterService clusterService , RecoverySettings recoverySettings ) {
201+ return Collections .singletonMap ("verifyaccess-fs" , (metadata ) ->
202+ new AccessVerifyingRepo (metadata , env , namedXContentRegistry , clusterService , recoverySettings ));
203+ }
204+
205+ private static class AccessVerifyingRepo extends FsRepository {
206+
207+ private final ClusterService clusterService ;
208+
209+ private AccessVerifyingRepo (RepositoryMetadata metadata , Environment environment , NamedXContentRegistry namedXContentRegistry ,
210+ ClusterService clusterService , RecoverySettings recoverySettings ) {
211+ super (metadata , environment , namedXContentRegistry , clusterService , recoverySettings );
212+ this .clusterService = clusterService ;
213+ }
214+
215+ @ Override
216+ protected BlobStore createBlobStore () throws Exception {
217+ final DiscoveryNode localNode = clusterService .state ().nodes ().getLocalNode ();
218+ if (localNode .getRoles ().contains (VotingOnlyNodePlugin .VOTING_ONLY_NODE_ROLE )) {
219+ assertTrue (localNode .isDataNode ());
220+ }
221+ return super .createBlobStore ();
222+ }
223+ }
224+ }
118225}
0 commit comments