Browse Source

Adds ignoreUnavailable option to the snapshot status API (#20066)

Adds ignoreUnavailable to the snapshot status API to be consistent
with the get snapshots API which has a similar parameter. If
ignoreUnavailable is set to true, then the snapshot status request
will ignore any snapshots that were not found in the repository,
instead of throwing a SnapshotMissingException.

Closes #18522
Ali Beyad 9 years ago
parent
commit
1c9b64e09a

+ 25 - 0
core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequest.java

@@ -38,6 +38,8 @@ public class SnapshotsStatusRequest extends MasterNodeRequest<SnapshotsStatusReq
 
     private String[] snapshots = Strings.EMPTY_ARRAY;
 
+    private boolean ignoreUnavailable;
+
     public SnapshotsStatusRequest() {
     }
 
@@ -112,11 +114,33 @@ public class SnapshotsStatusRequest extends MasterNodeRequest<SnapshotsStatusReq
         return this;
     }
 
+    /**
+     * Set to <code>true</code> to ignore unavailable snapshots, instead of throwing an exception.
+     * Defaults to <code>false</code>, which means unavailable snapshots cause an exception to be thrown.
+     *
+     * @param ignoreUnavailable whether to ignore unavailable snapshots
+     * @return this request
+     */
+    public SnapshotsStatusRequest ignoreUnavailable(boolean ignoreUnavailable) {
+        this.ignoreUnavailable = ignoreUnavailable;
+        return this;
+    }
+
+    /**
+     * Returns whether the request permits unavailable snapshots to be ignored.
+     *
+     * @return true if the request will ignore unavailable snapshots, false if it will throw an exception on unavailable snapshots
+     */
+    public boolean ignoreUnavailable() {
+        return ignoreUnavailable;
+    }
+
     @Override
     public void readFrom(StreamInput in) throws IOException {
         super.readFrom(in);
         repository = in.readString();
         snapshots = in.readStringArray();
+        ignoreUnavailable = in.readBoolean();
     }
 
     @Override
@@ -124,5 +148,6 @@ public class SnapshotsStatusRequest extends MasterNodeRequest<SnapshotsStatusReq
         super.writeTo(out);
         out.writeString(repository);
         out.writeStringArray(snapshots);
+        out.writeBoolean(ignoreUnavailable);
     }
 }

+ 12 - 0
core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequestBuilder.java

@@ -74,4 +74,16 @@ public class SnapshotsStatusRequestBuilder extends MasterNodeOperationRequestBui
         request.snapshots(ArrayUtils.concat(request.snapshots(), snapshots));
         return this;
     }
+
+    /**
+     * Set to <code>true</code> to ignore unavailable snapshots, instead of throwing an exception.
+     * Defaults to <code>false</code>, which means unavailable snapshots cause an exception to be thrown.
+     *
+     * @param ignoreUnavailable whether to ignore unavailable snapshots.
+     * @return this builder
+     */
+    public SnapshotsStatusRequestBuilder setIgnoreUnavailable(boolean ignoreUnavailable) {
+        request.ignoreUnavailable(ignoreUnavailable);
+        return this;
+    }
 }

+ 8 - 1
core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java

@@ -214,7 +214,14 @@ public class TransportSnapshotsStatusAction extends TransportMasterNodeAction<Sn
                 SnapshotId snapshotId = matchedSnapshotIds.get(snapshotName);
                 if (snapshotId == null) {
                     // neither in the current snapshot entries nor found in the repository
-                    throw new SnapshotMissingException(repositoryName, snapshotName);
+                    if (request.ignoreUnavailable()) {
+                        // ignoring unavailable snapshots, so skip over
+                        logger.debug("snapshot status request ignoring snapshot [{}], not found in repository [{}]",
+                                     snapshotName, repositoryName);
+                        continue;
+                    } else {
+                        throw new SnapshotMissingException(repositoryName, snapshotName);
+                    }
                 }
                 SnapshotInfo snapshotInfo = snapshotsService.snapshot(repositoryName, snapshotId);
                 List<SnapshotIndexShardStatus> shardStatusBuilder = new ArrayList<>();

+ 4 - 4
core/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestSnapshotsStatusAction.java

@@ -20,7 +20,6 @@
 package org.elasticsearch.rest.action.admin.cluster;
 
 import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
-import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
 import org.elasticsearch.client.node.NodeClient;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.inject.Inject;
@@ -54,9 +53,10 @@ public class RestSnapshotsStatusAction extends BaseRestHandler {
         if (snapshots.length == 1 && "_all".equalsIgnoreCase(snapshots[0])) {
             snapshots = Strings.EMPTY_ARRAY;
         }
-        SnapshotsStatusRequest snapshotsStatusResponse = snapshotsStatusRequest(repository).snapshots(snapshots);
+        SnapshotsStatusRequest snapshotsStatusRequest = snapshotsStatusRequest(repository).snapshots(snapshots);
+        snapshotsStatusRequest.ignoreUnavailable(request.paramAsBoolean("ignore_unavailable", snapshotsStatusRequest.ignoreUnavailable()));
 
-        snapshotsStatusResponse.masterNodeTimeout(request.paramAsTime("master_timeout", snapshotsStatusResponse.masterNodeTimeout()));
-        client.admin().cluster().snapshotsStatus(snapshotsStatusResponse, new RestToXContentListener<SnapshotsStatusResponse>(channel));
+        snapshotsStatusRequest.masterNodeTimeout(request.paramAsTime("master_timeout", snapshotsStatusRequest.masterNodeTimeout()));
+        client.admin().cluster().snapshotsStatus(snapshotsStatusRequest, new RestToXContentListener<>(channel));
     }
 }

+ 3 - 3
core/src/main/java/org/elasticsearch/snapshots/SnapshotMissingException.java

@@ -30,15 +30,15 @@ import java.io.IOException;
 public class SnapshotMissingException extends SnapshotException {
 
     public SnapshotMissingException(final String repositoryName, final SnapshotId snapshotId, final Throwable cause) {
-        super(repositoryName, snapshotId, "is missing", cause);
+        super(repositoryName, snapshotId, " is missing", cause);
     }
 
     public SnapshotMissingException(final String repositoryName, final SnapshotId snapshotId) {
-        super(repositoryName, snapshotId, "is missing");
+        super(repositoryName, snapshotId, " is missing");
     }
 
     public SnapshotMissingException(final String repositoryName, final String snapshotName) {
-        super(repositoryName, snapshotName, "is missing");
+        super(repositoryName, snapshotName, " is missing");
     }
 
     public SnapshotMissingException(StreamInput in) throws IOException {

+ 18 - 6
core/src/test/java/org/elasticsearch/snapshots/SharedClusterSnapshotRestoreIT.java

@@ -1504,12 +1504,24 @@ public class SharedClusterSnapshotRestoreIT extends AbstractSnapshotIntegTestCas
         logger.info("--> checking that _current no longer returns the snapshot");
         assertThat(client.admin().cluster().prepareGetSnapshots("test-repo").addSnapshots("_current").execute().actionGet().getSnapshots().isEmpty(), equalTo(true));
 
-        try {
-            client.admin().cluster().prepareSnapshotStatus("test-repo").addSnapshots("test-snap-doesnt-exist").execute().actionGet();
-            fail();
-        } catch (SnapshotMissingException ex) {
-            // Expected
-        }
+        // test that getting an unavailable snapshot status throws an exception if ignoreUnavailable is false on the request
+        SnapshotMissingException ex = expectThrows(SnapshotMissingException.class, () ->
+            client.admin().cluster().prepareSnapshotStatus("test-repo").addSnapshots("test-snap-doesnt-exist").get());
+        assertEquals("[test-repo:test-snap-doesnt-exist] is missing", ex.getMessage());
+        // test that getting an unavailable snapshot status does not throw an exception if ignoreUnavailable is true on the request
+        response = client.admin().cluster().prepareSnapshotStatus("test-repo")
+                       .addSnapshots("test-snap-doesnt-exist")
+                       .setIgnoreUnavailable(true)
+                       .get();
+        assertTrue(response.getSnapshots().isEmpty());
+        // test getting snapshot status for available and unavailable snapshots where ignoreUnavailable is true
+        // (available one should be returned)
+        response = client.admin().cluster().prepareSnapshotStatus("test-repo")
+                       .addSnapshots("test-snap", "test-snap-doesnt-exist")
+                       .setIgnoreUnavailable(true)
+                       .get();
+        assertEquals(1, response.getSnapshots().size());
+        assertEquals("test-snap", response.getSnapshots().get(0).getSnapshot().getSnapshotId().getName());
     }
 
     public void testSnapshotRelocatingPrimary() throws Exception {

+ 4 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get.json

@@ -21,6 +21,10 @@
         "master_timeout": {
           "type" : "time",
           "description" : "Explicit operation timeout for connection to master node"
+        },
+        "ignore_unavailable": {
+          "type": "boolean",
+          "description": "Whether to ignore unavailable snapshots, defaults to false which means a SnapshotMissingException is thrown"
         }
       }
     },

+ 5 - 1
rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.status.json

@@ -19,9 +19,13 @@
         "master_timeout": {
           "type" : "time",
           "description" : "Explicit operation timeout for connection to master node"
+        },
+        "ignore_unavailable": {
+          "type": "boolean",
+          "description": "Whether to ignore unavailable snapshots, defaults to false which means a SnapshotMissingException is thrown"
         }
       }
     },
     "body": null
   }
-}
+}

+ 61 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.get/10_basic.yaml

@@ -0,0 +1,61 @@
+---
+setup:
+
+  - do:
+      snapshot.create_repository:
+        repository: test_repo_get_1
+        body:
+          type: fs
+          settings:
+            location: "test_repo_get_1_loc"
+
+---
+teardown:
+
+  - do:
+      snapshot.delete_repository:
+        repository: test_repo_get_1
+
+---
+"Get snapshot info":
+
+  - do:
+      indices.create:
+        index: test_index
+        body:
+          settings:
+            number_of_shards:   1
+            number_of_replicas: 0
+
+  - do:
+      snapshot.create:
+        repository: test_repo_get_1
+        snapshot: test_snapshot
+        wait_for_completion: true
+
+  - do:
+      snapshot.get:
+        repository: test_repo_get_1
+        snapshot: test_snapshot
+
+  - is_true: snapshots
+  
+---
+"Get missing snapshot info throws an exception":
+
+  - do:
+      catch: /snapshot_missing_exception.+ is missing/
+      snapshot.get:
+        repository: test_repo_get_1
+        snapshot: test_nonexistent_snapshot
+   
+---
+"Get missing snapshot info succeeds when ignoreUnavailable is true":
+
+  - do:
+      snapshot.get:
+        repository: test_repo_get_1
+        snapshot: test_nonexistent_snapshot
+        ignore_unavailable: true
+
+  - is_true: snapshots

+ 61 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/snapshot.status/10_basic.yaml

@@ -0,0 +1,61 @@
+---
+setup:
+
+  - do:
+      snapshot.create_repository:
+        repository: test_repo_status_1
+        body:
+          type: fs
+          settings:
+            location: "test_repo_status_1_loc"
+
+---
+teardown:
+
+  - do:
+      snapshot.delete_repository:
+        repository: test_repo_status_1
+
+---
+"Get snapshot status":
+
+  - do:
+      indices.create:
+        index: test_index
+        body:
+          settings:
+            number_of_shards:   1
+            number_of_replicas: 0
+
+  - do:
+      snapshot.create:
+        repository: test_repo_status_1
+        snapshot: test_snapshot
+        wait_for_completion: true
+
+  - do:
+      snapshot.status:
+        repository: test_repo_status_1
+        snapshot: test_snapshot
+
+  - is_true: snapshots
+  
+---
+"Get missing snapshot status throws an exception":
+
+  - do:
+      catch: /snapshot_missing_exception.+ is missing/
+      snapshot.status:
+        repository: test_repo_status_1
+        snapshot: test_nonexistent_snapshot
+   
+---
+"Get missing snapshot status succeeds when ignoreUnavailable is true":
+
+  - do:
+      snapshot.status:
+        repository: test_repo_status_1
+        snapshot: test_nonexistent_snapshot
+        ignore_unavailable: true
+
+  - is_true: snapshots