You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ge...@apache.org on 2024/05/28 18:12:06 UTC
(solr) branch main updated: SOLR-17302: Convert /cluster filestore APIs to JAX-RS (#2470)
This is an automated email from the ASF dual-hosted git repository.
gerlowskija pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 8c5ae1bb745 SOLR-17302: Convert /cluster filestore APIs to JAX-RS (#2470)
8c5ae1bb745 is described below
commit 8c5ae1bb745869ebd6fab5911b3dd0fd75ed9106
Author: Jason Gerlowski <ge...@apache.org>
AuthorDate: Tue May 28 14:12:00 2024 -0400
SOLR-17302: Convert /cluster filestore APIs to JAX-RS (#2470)
This commit moves some of Solr's "filestore" APIs (those located under
the `/api/cluster/files` path) to JAX-RS.
---
.../client/api/endpoint/ClusterFileStoreApis.java | 56 +++++
.../api/model/UploadToFileStoreResponse.java | 25 ++
.../java/org/apache/solr/core/CoreContainer.java | 20 +-
.../apache/solr/filestore/ClusterFileStore.java | 257 +++++++++++++++++++++
.../apache/solr/filestore/DistribFileStore.java | 26 ++-
.../java/org/apache/solr/filestore/FileStore.java | 2 +
.../org/apache/solr/filestore/FileStoreAPI.java | 226 +-----------------
.../org/apache/solr/filestore/FileStoreUtils.java | 145 ++++++++++++
.../apache/solr/packagemanager/PackageUtils.java | 3 +-
.../solr/packagemanager/RepositoryManager.java | 4 +-
.../src/java/org/apache/solr/pkg/PackageAPI.java | 18 +-
.../org/apache/solr/pkg/SolrPackageLoader.java | 6 +-
.../solr/filestore/TestDistribFileStore.java | 3 +-
.../apache/solr/handler/TestContainerPlugin.java | 4 +-
.../src/test/org/apache/solr/pkg/TestPackages.java | 10 +-
15 files changed, 541 insertions(+), 264 deletions(-)
diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/ClusterFileStoreApis.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/ClusterFileStoreApis.java
new file mode 100644
index 00000000000..d95a59ecf65
--- /dev/null
+++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/ClusterFileStoreApis.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.endpoint;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.QueryParam;
+import java.io.InputStream;
+import java.util.List;
+import org.apache.solr.client.api.model.SolrJerseyResponse;
+import org.apache.solr.client.api.model.UploadToFileStoreResponse;
+
+@Path("/cluster")
+public interface ClusterFileStoreApis {
+ // TODO Better understand the purpose of the 'sig' parameter and improve docs here.
+ @PUT
+ @Operation(
+ summary = "Upload a file to the filestore.",
+ tags = {"file-store"})
+ @Path("/files{filePath:.+}")
+ UploadToFileStoreResponse uploadFile(
+ @Parameter(description = "File store path") @PathParam("filePath") String filePath,
+ @Parameter(description = "Signature(s) for the file being uploaded") @QueryParam("sig")
+ List<String> sig,
+ @Parameter(description = "File content to be stored in the filestore") @RequestBody
+ InputStream requestBody);
+
+ @DELETE
+ @Operation(
+ summary = "Delete a file or directory from the filestore.",
+ tags = {"file-store"})
+ @Path("/files{path:.+}")
+ SolrJerseyResponse deleteFile(
+ @Parameter(description = "Path to a file or directory within the filestore")
+ @PathParam("path")
+ String path);
+}
diff --git a/solr/api/src/java/org/apache/solr/client/api/model/UploadToFileStoreResponse.java b/solr/api/src/java/org/apache/solr/client/api/model/UploadToFileStoreResponse.java
new file mode 100644
index 00000000000..4492dce7daa
--- /dev/null
+++ b/solr/api/src/java/org/apache/solr/client/api/model/UploadToFileStoreResponse.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.api.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class UploadToFileStoreResponse extends SolrJerseyResponse {
+
+ @JsonProperty public String file;
+ @JsonProperty public String message;
+}
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index d0978fcbba2..a05f3d5a3c7 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -104,6 +104,9 @@ import org.apache.solr.common.util.Utils;
import org.apache.solr.core.DirectoryFactory.DirContext;
import org.apache.solr.core.backup.repository.BackupRepository;
import org.apache.solr.core.backup.repository.BackupRepositoryFactory;
+import org.apache.solr.filestore.ClusterFileStore;
+import org.apache.solr.filestore.DistribFileStore;
+import org.apache.solr.filestore.FileStore;
import org.apache.solr.filestore.FileStoreAPI;
import org.apache.solr.handler.ClusterAPI;
import org.apache.solr.handler.RequestHandlerBase;
@@ -294,7 +297,9 @@ public class CoreContainer {
private volatile ClusterEventProducer clusterEventProducer;
private DelegatingPlacementPluginFactory placementPluginFactory;
+ private DistribFileStore fileStore;
private FileStoreAPI fileStoreAPI;
+ private ClusterFileStore clusterFileStoreAPI;
private SolrPackageLoader packageLoader;
private final Set<Path> allowPaths;
@@ -723,8 +728,8 @@ public class CoreContainer {
return packageLoader;
}
- public FileStoreAPI getFileStoreAPI() {
- return fileStoreAPI;
+ public FileStore getFileStore() {
+ return fileStore;
}
public SolrCache<?, ?> getCache(String name) {
@@ -856,9 +861,11 @@ public class CoreContainer {
(PublicKeyHandler) containerHandlers.get(PublicKeyHandler.PATH));
pkiAuthenticationSecurityBuilder.initializeMetrics(solrMetricsContext, "/authentication/pki");
+ fileStore = new DistribFileStore(this);
fileStoreAPI = new FileStoreAPI(this);
registerV2ApiIfEnabled(fileStoreAPI.readAPI);
registerV2ApiIfEnabled(fileStoreAPI.writeAPI);
+ registerV2ApiIfEnabled(ClusterFileStore.class);
packageLoader = new SolrPackageLoader(this);
registerV2ApiIfEnabled(packageLoader.getPackageAPI().editAPI);
@@ -1150,6 +1157,15 @@ public class CoreContainer {
.in(Singleton.class);
}
})
+ .register(
+ new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bindFactory(new InjectionFactories.SingletonFactory<>(fileStore))
+ .to(DistribFileStore.class)
+ .in(Singleton.class);
+ }
+ })
.register(
new AbstractBinder() {
@Override
diff --git a/solr/core/src/java/org/apache/solr/filestore/ClusterFileStore.java b/solr/core/src/java/org/apache/solr/filestore/ClusterFileStore.java
new file mode 100644
index 00000000000..a664e5699cb
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/filestore/ClusterFileStore.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.filestore;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import jakarta.inject.Inject;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.solr.api.JerseyResource;
+import org.apache.solr.client.api.endpoint.ClusterFileStoreApis;
+import org.apache.solr.client.api.model.SolrJerseyResponse;
+import org.apache.solr.client.api.model.UploadToFileStoreResponse;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.jersey.PermissionName;
+import org.apache.solr.pkg.PackageAPI;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.PermissionNameProvider;
+import org.apache.solr.util.CryptoKeys;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ClusterFileStore extends JerseyResource implements ClusterFileStoreApis {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ public static final String FILESTORE_DIRECTORY = "filestore";
+ public static final String TRUSTED_DIR = "_trusted_";
+ public static final String KEYS_DIR = "/_trusted_/keys";
+ static final String TMP_ZK_NODE = "/fileStoreWriteInProgress";
+
+ private final CoreContainer coreContainer;
+ private final SolrQueryRequest req;
+ private final SolrQueryResponse rsp;
+ private final FileStore fileStore;
+
+ @Inject
+ public ClusterFileStore(
+ CoreContainer coreContainer,
+ DistribFileStore fileStore,
+ SolrQueryRequest req,
+ SolrQueryResponse rsp) {
+ this.coreContainer = coreContainer;
+ this.req = req;
+ this.rsp = rsp;
+ this.fileStore = fileStore;
+ }
+
+ @Override
+ @PermissionName(PermissionNameProvider.Name.FILESTORE_WRITE_PERM)
+ public UploadToFileStoreResponse uploadFile(
+ String filePath, List<String> sig, InputStream requestBody) {
+ final var response = instantiateJerseyResponse(UploadToFileStoreResponse.class);
+ if (!coreContainer.getPackageLoader().getPackageAPI().isEnabled()) {
+ throw new RuntimeException(PackageAPI.ERR_MSG);
+ }
+ try {
+ coreContainer
+ .getZkController()
+ .getZkClient()
+ .create(TMP_ZK_NODE, "true".getBytes(UTF_8), CreateMode.EPHEMERAL, true);
+
+ if (requestBody == null)
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "no payload");
+ if (filePath == null) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No path");
+ }
+ validateName(filePath, true);
+ try {
+ byte[] buf = requestBody.readAllBytes();
+ List<String> signatures = readSignatures(sig, buf);
+ FileStoreAPI.MetaData meta = _createJsonMetaData(buf, signatures);
+ FileStore.FileType type = fileStore.getType(filePath, true);
+ if (type == FileStore.FileType.FILE) {
+ // a file already exist at the same path
+ fileStore.get(
+ filePath,
+ fileEntry -> {
+ if (meta.equals(fileEntry.meta)) {
+ // the file content is same too. this is an idempotent put
+ // do not throw an error
+ response.file = filePath;
+ response.message = "File with same metadata exists ";
+ }
+ },
+ true);
+ // 'message' only set in the "already exists w/ same content" case, so we're done!
+ if (response.message != null) {
+ return response;
+ }
+ } else if (type != FileStore.FileType.NOFILE) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST, "Path already exists " + filePath);
+ }
+
+ fileStore.put(new FileStore.FileEntry(ByteBuffer.wrap(buf), meta, filePath));
+ response.file = filePath;
+ } catch (IOException e) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
+ }
+ } catch (InterruptedException e) {
+ log.error("Unexpected error", e);
+ } catch (KeeperException.NodeExistsException e) {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR, "A write is already in process , try later");
+ } catch (KeeperException e) {
+ log.error("Unexpected error", e);
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e.getMessage());
+ } finally {
+ try {
+ coreContainer.getZkController().getZkClient().delete(TMP_ZK_NODE, -1, true);
+ } catch (Exception e) {
+ log.error("Unexpected error ", e);
+ }
+ }
+
+ return response;
+ }
+
+ @Override
+ @PermissionName(PermissionNameProvider.Name.FILESTORE_WRITE_PERM)
+ public SolrJerseyResponse deleteFile(String filePath) {
+ final var response = instantiateJerseyResponse(SolrJerseyResponse.class);
+ if (!coreContainer.getPackageLoader().getPackageAPI().isEnabled()) {
+ throw new RuntimeException(PackageAPI.ERR_MSG);
+ }
+
+ try {
+ coreContainer
+ .getZkController()
+ .getZkClient()
+ .create(TMP_ZK_NODE, "true".getBytes(UTF_8), CreateMode.EPHEMERAL, true);
+ validateName(filePath, true);
+ if (coreContainer.getPackageLoader().getPackageAPI().isJarInuse(filePath)) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "jar in use, can't delete");
+ }
+ FileStore.FileType type = fileStore.getType(filePath, true);
+ if (type == FileStore.FileType.NOFILE) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST, "Path does not exist: " + filePath);
+ }
+ fileStore.delete(filePath);
+ return response;
+ } catch (SolrException e) {
+ throw e;
+ } catch (Exception e) {
+ log.error("Unknown error", e);
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
+ } finally {
+ try {
+ coreContainer.getZkController().getZkClient().delete(TMP_ZK_NODE, -1, true);
+ } catch (Exception e) {
+ log.error("Unexpected error ", e);
+ }
+ }
+ }
+
+ private List<String> readSignatures(List<String> signatures, byte[] buf)
+ throws SolrException, IOException {
+ if (signatures == null || signatures.isEmpty()) return null;
+ fileStore.refresh(KEYS_DIR);
+ validate(signatures, buf);
+ return signatures;
+ }
+
+ private void validate(List<String> sigs, byte[] buf) throws SolrException, IOException {
+ Map<String, byte[]> keys = fileStore.getKeys();
+ if (keys == null || keys.isEmpty()) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST, "File store does not have any keys");
+ }
+ CryptoKeys cryptoKeys = null;
+ try {
+ cryptoKeys = new CryptoKeys(keys);
+ } catch (Exception e) {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR, "Error parsing public keys in file store");
+ }
+ for (String sig : sigs) {
+ if (cryptoKeys.verify(sig, ByteBuffer.wrap(buf)) == null) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "Signature does not match any public key : "
+ + sig
+ + " len: "
+ + buf.length
+ + " content sha512: "
+ + DigestUtils.sha512Hex(buf));
+ }
+ }
+ }
+
+ /**
+ * Creates a JSON string with the metadata.
+ *
+ * @lucene.internal
+ */
+ public static FileStoreAPI.MetaData _createJsonMetaData(byte[] buf, List<String> signatures)
+ throws IOException {
+ String sha512 = DigestUtils.sha512Hex(buf);
+ Map<String, Object> vals = new HashMap<>();
+ vals.put(FileStoreAPI.MetaData.SHA512, sha512);
+ if (signatures != null) {
+ vals.put("sig", signatures);
+ }
+ return new FileStoreAPI.MetaData(vals);
+ }
+
+ static final String INVALIDCHARS = " /\\#&*\n\t%@~`=+^$><?{}[]|:;!";
+
+ public static void validateName(String path, boolean failForTrusted) {
+ if (path == null) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "empty path");
+ }
+ List<String> parts = StrUtils.splitSmart(path, '/', true);
+ for (String part : parts) {
+ if (part.charAt(0) == '.') {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "cannot start with period");
+ }
+ for (int i = 0; i < part.length(); i++) {
+ for (int j = 0; j < INVALIDCHARS.length(); j++) {
+ if (part.charAt(i) == INVALIDCHARS.charAt(j))
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST, "Unsupported char in file name: " + part);
+ }
+ }
+ }
+ if (failForTrusted && TRUSTED_DIR.equals(parts.get(0))) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST, "trying to write into /_trusted_/ directory");
+ }
+ }
+}
diff --git a/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java b/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java
index 4c62c1ff20e..7eb73354e6e 100644
--- a/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java
+++ b/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java
@@ -216,8 +216,9 @@ public class DistribFileStore implements FileStore {
}
boolean fetchFromAnyNode() {
- ArrayList<String> l = coreContainer.getFileStoreAPI().shuffledNodes();
- for (String liveNode : l) {
+ ArrayList<String> nodesToAttemptFetchFrom =
+ FileStoreUtils.fetchAndShuffleRemoteLiveNodes(coreContainer);
+ for (String liveNode : nodesToAttemptFetchFrom) {
try {
String baseUrl =
coreContainer.getZkController().getZkStateReader().getBaseUrlV2ForNodeName(liveNode);
@@ -244,12 +245,6 @@ public class DistribFileStore implements FileStore {
return false;
}
- String getSimpleName() {
- int idx = path.lastIndexOf('/');
- if (idx == -1) return path;
- return path.substring(idx + 1);
- }
-
public Path realPath() {
return getRealpath(path);
}
@@ -306,6 +301,13 @@ public class DistribFileStore implements FileStore {
ew.put("timestamp", getTimeStamp());
if (metaData != null) metaData.writeMap(ew);
}
+
+ @Override
+ public String getSimpleName() {
+ int idx = path.lastIndexOf('/');
+ if (idx == -1) return path;
+ return path.substring(idx + 1);
+ }
};
}
@@ -353,7 +355,7 @@ public class DistribFileStore implements FileStore {
}
tmpFiles.put(info.path, info);
- List<String> nodes = coreContainer.getFileStoreAPI().shuffledNodes();
+ List<String> nodes = FileStoreUtils.fetchAndShuffleRemoteLiveNodes(coreContainer);
int i = 0;
int FETCHFROM_SRC = 50;
String myNodeName = coreContainer.getZkController().getNodeName();
@@ -496,7 +498,7 @@ public class DistribFileStore implements FileStore {
@Override
public void delete(String path) {
deleteLocal(path);
- List<String> nodes = coreContainer.getFileStoreAPI().shuffledNodes();
+ List<String> nodes = FileStoreUtils.fetchAndShuffleRemoteLiveNodes(coreContainer);
HttpClient client = coreContainer.getUpdateShardHandler().getDefaultHttpClient();
for (String node : nodes) {
String baseUrl =
@@ -583,7 +585,7 @@ public class DistribFileStore implements FileStore {
}
public static synchronized Path getFileStoreDirPath(Path solrHome) {
- var path = solrHome.resolve(FileStoreAPI.FILESTORE_DIRECTORY);
+ var path = solrHome.resolve(ClusterFileStore.FILESTORE_DIRECTORY);
if (!Files.exists(path)) {
try {
Files.createDirectories(path);
@@ -632,7 +634,7 @@ public class DistribFileStore implements FileStore {
// reads local keys file
private static Map<String, byte[]> _getKeys(Path solrHome) throws IOException {
Map<String, byte[]> result = new HashMap<>();
- Path keysDir = _getRealPath(FileStoreAPI.KEYS_DIR, solrHome);
+ Path keysDir = _getRealPath(ClusterFileStore.KEYS_DIR, solrHome);
File[] keyFiles = keysDir.toFile().listFiles();
if (keyFiles == null) return result;
diff --git a/solr/core/src/java/org/apache/solr/filestore/FileStore.java b/solr/core/src/java/org/apache/solr/filestore/FileStore.java
index fdff79e1fa5..65673757438 100644
--- a/solr/core/src/java/org/apache/solr/filestore/FileStore.java
+++ b/solr/core/src/java/org/apache/solr/filestore/FileStore.java
@@ -111,6 +111,8 @@ public interface FileStore {
interface FileDetails extends MapWriter {
+ String getSimpleName();
+
MetaData getMetaData();
Date getTimeStamp();
diff --git a/solr/core/src/java/org/apache/solr/filestore/FileStoreAPI.java b/solr/core/src/java/org/apache/solr/filestore/FileStoreAPI.java
index dc420d37b31..d54acc464f5 100644
--- a/solr/core/src/java/org/apache/solr/filestore/FileStoreAPI.java
+++ b/solr/core/src/java/org/apache/solr/filestore/FileStoreAPI.java
@@ -23,18 +23,11 @@ import static org.apache.solr.handler.ReplicationHandler.FILE_STREAM;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
-import java.util.function.Consumer;
import java.util.function.Supplier;
-import org.apache.commons.codec.digest.DigestUtils;
import org.apache.solr.api.EndPoint;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.common.MapWriter;
@@ -42,18 +35,14 @@ import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
-import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrCore;
-import org.apache.solr.pkg.PackageAPI;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.security.PermissionNameProvider;
import org.apache.solr.util.CryptoKeys;
-import org.apache.zookeeper.CreateMode;
-import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -70,101 +59,11 @@ public class FileStoreAPI {
public FileStoreAPI(CoreContainer coreContainer) {
this.coreContainer = coreContainer;
- fileStore = new DistribFileStore(coreContainer);
- }
-
- public FileStore getFileStore() {
- return fileStore;
- }
-
- /** get a list of nodes randomly shuffled * @lucene.internal */
- public ArrayList<String> shuffledNodes() {
- Set<String> liveNodes =
- coreContainer.getZkController().getZkStateReader().getClusterState().getLiveNodes();
- ArrayList<String> l = new ArrayList<>(liveNodes);
- l.remove(coreContainer.getZkController().getNodeName());
- Collections.shuffle(l, Utils.RANDOM);
- return l;
- }
-
- public void validateFiles(List<String> files, boolean validateSignatures, Consumer<String> errs) {
- for (String path : files) {
- try {
- FileStore.FileType type = fileStore.getType(path, true);
- if (type != FileStore.FileType.FILE) {
- errs.accept("No such file: " + path);
- continue;
- }
-
- fileStore.get(
- path,
- entry -> {
- if (entry.getMetaData().signatures == null
- || entry.getMetaData().signatures.isEmpty()) {
- errs.accept(path + " has no signature");
- return;
- }
- if (validateSignatures) {
- try {
- fileStore.refresh(KEYS_DIR);
- validate(entry.meta.signatures, entry, false);
- } catch (Exception e) {
- log.error("Error validating package artifact", e);
- errs.accept(e.getMessage());
- }
- }
- },
- false);
- } catch (Exception e) {
- log.error("Error reading file ", e);
- errs.accept("Error reading file " + path + " " + e.getMessage());
- }
- }
+ fileStore = coreContainer.getFileStore();
}
public class FSWrite {
- static final String TMP_ZK_NODE = "/fileStoreWriteInProgress";
-
- @EndPoint(
- path = "/cluster/files/*",
- method = SolrRequest.METHOD.DELETE,
- permission = PermissionNameProvider.Name.FILESTORE_WRITE_PERM)
- public void delete(SolrQueryRequest req, SolrQueryResponse rsp) {
- if (!coreContainer.getPackageLoader().getPackageAPI().isEnabled()) {
- throw new RuntimeException(PackageAPI.ERR_MSG);
- }
-
- try {
- coreContainer
- .getZkController()
- .getZkClient()
- .create(TMP_ZK_NODE, "true".getBytes(UTF_8), CreateMode.EPHEMERAL, true);
- String path = req.getPathTemplateValues().get("*");
- validateName(path, true);
- if (coreContainer.getPackageLoader().getPackageAPI().isJarInuse(path)) {
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "jar in use, can't delete");
- }
- FileStore.FileType type = fileStore.getType(path, true);
- if (type == FileStore.FileType.NOFILE) {
- throw new SolrException(
- SolrException.ErrorCode.BAD_REQUEST, "Path does not exist: " + path);
- }
- fileStore.delete(path);
- } catch (SolrException e) {
- throw e;
- } catch (Exception e) {
- log.error("Unknown error", e);
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
- } finally {
- try {
- coreContainer.getZkController().getZkClient().delete(TMP_ZK_NODE, -1, true);
- } catch (Exception e) {
- log.error("Unexpected error ", e);
- }
- }
- }
-
@EndPoint(
path = "/node/files/*",
method = SolrRequest.METHOD.DELETE,
@@ -174,129 +73,6 @@ public class FileStoreAPI {
validateName(path, true);
fileStore.deleteLocal(path);
}
-
- @EndPoint(
- path = "/cluster/files/*",
- method = SolrRequest.METHOD.PUT,
- permission = PermissionNameProvider.Name.FILESTORE_WRITE_PERM)
- public void upload(SolrQueryRequest req, SolrQueryResponse rsp) {
- if (!coreContainer.getPackageLoader().getPackageAPI().isEnabled()) {
- throw new RuntimeException(PackageAPI.ERR_MSG);
- }
- try {
- coreContainer
- .getZkController()
- .getZkClient()
- .create(TMP_ZK_NODE, "true".getBytes(UTF_8), CreateMode.EPHEMERAL, true);
-
- Iterable<ContentStream> streams = req.getContentStreams();
- if (streams == null)
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "no payload");
- String path = req.getPathTemplateValues().get("*");
- if (path == null) {
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No path");
- }
- validateName(path, true);
- ContentStream stream = streams.iterator().next();
- try {
- byte[] buf = stream.getStream().readAllBytes();
- List<String> signatures = readSignatures(req, buf);
- MetaData meta = _createJsonMetaData(buf, signatures);
- FileStore.FileType type = fileStore.getType(path, true);
- boolean[] returnAfter = new boolean[] {false};
- if (type == FileStore.FileType.FILE) {
- // a file already exist at the same path
- fileStore.get(
- path,
- fileEntry -> {
- if (meta.equals(fileEntry.meta)) {
- // the file content is same too. this is an idempotent put
- // do not throw an error
- returnAfter[0] = true;
- rsp.add(CommonParams.FILE, path);
- rsp.add("message", "File with same metadata exists ");
- }
- },
- true);
- }
- if (returnAfter[0]) return;
- if (type != FileStore.FileType.NOFILE) {
- throw new SolrException(
- SolrException.ErrorCode.BAD_REQUEST, "Path already exists " + path);
- }
- fileStore.put(new FileStore.FileEntry(ByteBuffer.wrap(buf), meta, path));
- rsp.add(CommonParams.FILE, path);
- } catch (IOException e) {
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
- }
- } catch (InterruptedException e) {
- log.error("Unexpected error", e);
- } catch (KeeperException.NodeExistsException e) {
- throw new SolrException(
- SolrException.ErrorCode.SERVER_ERROR, "A write is already in process , try later");
- } catch (KeeperException e) {
- log.error("Unexpected error", e);
- } finally {
- try {
- coreContainer.getZkController().getZkClient().delete(TMP_ZK_NODE, -1, true);
- } catch (Exception e) {
- log.error("Unexpected error ", e);
- }
- }
- }
-
- private List<String> readSignatures(SolrQueryRequest req, byte[] buf)
- throws SolrException, IOException {
- String[] signatures = req.getParams().getParams("sig");
- if (signatures == null || signatures.length == 0) return null;
- List<String> sigs = Arrays.asList(signatures);
- fileStore.refresh(KEYS_DIR);
- validate(sigs, buf);
- return sigs;
- }
-
- private void validate(List<String> sigs, byte[] buf) throws SolrException, IOException {
- Map<String, byte[]> keys = fileStore.getKeys();
- if (keys == null || keys.isEmpty()) {
- throw new SolrException(
- SolrException.ErrorCode.BAD_REQUEST, "File store does not have any keys");
- }
- CryptoKeys cryptoKeys = null;
- try {
- cryptoKeys = new CryptoKeys(keys);
- } catch (Exception e) {
- throw new SolrException(
- SolrException.ErrorCode.SERVER_ERROR, "Error parsing public keys in file store");
- }
- for (String sig : sigs) {
- if (cryptoKeys.verify(sig, ByteBuffer.wrap(buf)) == null) {
- throw new SolrException(
- SolrException.ErrorCode.BAD_REQUEST,
- "Signature does not match any public key : "
- + sig
- + " len: "
- + buf.length
- + " content sha512: "
- + DigestUtils.sha512Hex(buf));
- }
- }
- }
- }
-
- /**
- * Creates a JSON string with the metadata.
- *
- * @lucene.internal
- */
- public static MetaData _createJsonMetaData(byte[] buf, List<String> signatures)
- throws IOException {
- String sha512 = DigestUtils.sha512Hex(buf);
- Map<String, Object> vals = new HashMap<>();
- vals.put(MetaData.SHA512, sha512);
- if (signatures != null) {
- vals.put("sig", signatures);
- }
- return new MetaData(vals);
}
public class FSRead {
diff --git a/solr/core/src/java/org/apache/solr/filestore/FileStoreUtils.java b/solr/core/src/java/org/apache/solr/filestore/FileStoreUtils.java
new file mode 100644
index 00000000000..b9bd841e803
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/filestore/FileStoreUtils.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.filestore;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.util.CryptoKeys;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Common utilities used by filestore-related code. */
+public class FileStoreUtils {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ /** Returns a shuffled list of all live nodes except the current host */
+ public static ArrayList<String> fetchAndShuffleRemoteLiveNodes(CoreContainer coreContainer) {
+ Set<String> liveNodes =
+ coreContainer.getZkController().getZkStateReader().getClusterState().getLiveNodes();
+ ArrayList<String> l = new ArrayList<>(liveNodes);
+ l.remove(coreContainer.getZkController().getNodeName());
+ Collections.shuffle(l, Utils.RANDOM);
+ return l;
+ }
+
+ public static void validateFiles(
+ FileStore fileStore, List<String> files, boolean validateSignatures, Consumer<String> errs) {
+ for (String path : files) {
+ try {
+ FileStore.FileType type = fileStore.getType(path, true);
+ if (type != FileStore.FileType.FILE) {
+ errs.accept("No such file: " + path);
+ continue;
+ }
+
+ fileStore.get(
+ path,
+ entry -> {
+ if (entry.getMetaData().signatures == null
+ || entry.getMetaData().signatures.isEmpty()) {
+ errs.accept(path + " has no signature");
+ return;
+ }
+ if (validateSignatures) {
+ try {
+ fileStore.refresh(ClusterFileStore.KEYS_DIR);
+ validate(fileStore, entry.meta.signatures, entry, false);
+ } catch (Exception e) {
+ log.error("Error validating package artifact", e);
+ errs.accept(e.getMessage());
+ }
+ }
+ },
+ false);
+ } catch (Exception e) {
+ log.error("Error reading file ", e);
+ errs.accept("Error reading file " + path + " " + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Validate a file for signature
+ *
+ * @param sigs the signatures, at least one should succeed
+ * @param entry The file details
+ * @param isFirstAttempt If there is a failure
+ */
+ public static void validate(
+ FileStore fileStore, List<String> sigs, FileStore.FileEntry entry, boolean isFirstAttempt)
+ throws SolrException, IOException {
+ if (!isFirstAttempt) {
+ // we are retrying because last validation failed.
+ // get all keys again and try again
+ fileStore.refresh(ClusterFileStore.KEYS_DIR);
+ }
+
+ Map<String, byte[]> keys = fileStore.getKeys();
+ if (keys == null || keys.isEmpty()) {
+ if (isFirstAttempt) {
+ validate(fileStore, sigs, entry, false);
+ return;
+ }
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST, "Filestore does not have any public keys");
+ }
+ CryptoKeys cryptoKeys = null;
+ try {
+ cryptoKeys = new CryptoKeys(keys);
+ } catch (Exception e) {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR, "Error parsing public keys in ZooKeeper");
+ }
+ for (String sig : sigs) {
+ Supplier<String> errMsg =
+ () ->
+ "Signature does not match any public key : "
+ + sig
+ + "sha256 "
+ + entry.getMetaData().sha512;
+ if (entry.getBuffer() != null) {
+ if (cryptoKeys.verify(sig, entry.getBuffer()) == null) {
+ if (isFirstAttempt) {
+ validate(fileStore, sigs, entry, false);
+ return;
+ }
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, errMsg.get());
+ }
+ } else {
+ InputStream inputStream = entry.getInputStream();
+ if (cryptoKeys.verify(sig, inputStream) == null) {
+ if (isFirstAttempt) {
+ validate(fileStore, sigs, entry, false);
+ return;
+ }
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, errMsg.get());
+ }
+ }
+ }
+ }
+}
diff --git a/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java b/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
index c0d50dd85e2..d05c541243d 100644
--- a/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
+++ b/solr/core/src/java/org/apache/solr/packagemanager/PackageUtils.java
@@ -48,6 +48,7 @@ import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.Utils;
+import org.apache.solr.filestore.ClusterFileStore;
import org.apache.solr.filestore.DistribFileStore;
import org.apache.solr.filestore.FileStoreAPI;
import org.apache.solr.packagemanager.SolrPackage.Manifest;
@@ -301,7 +302,7 @@ public class PackageUtils {
}
public static void uploadKey(byte[] bytes, String path, Path home) throws IOException {
- FileStoreAPI.MetaData meta = FileStoreAPI._createJsonMetaData(bytes, null);
+ FileStoreAPI.MetaData meta = ClusterFileStore._createJsonMetaData(bytes, null);
DistribFileStore._persistToFile(
home, path, ByteBuffer.wrap(bytes), ByteBuffer.wrap(Utils.toJSON(meta)));
}
diff --git a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java
index d92d027d1c0..4e3be7b16c6 100644
--- a/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java
+++ b/solr/core/src/java/org/apache/solr/packagemanager/RepositoryManager.java
@@ -51,7 +51,7 @@ import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.Utils;
-import org.apache.solr.filestore.FileStoreAPI;
+import org.apache.solr.filestore.ClusterFileStore;
import org.apache.solr.packagemanager.SolrPackage.Artifact;
import org.apache.solr.packagemanager.SolrPackage.SolrPackageRelease;
import org.apache.solr.pkg.PackageAPI;
@@ -146,7 +146,7 @@ public class RepositoryManager {
String solrHome = (String) systemInfo.get("solr_home");
// put the public key into package store's trusted key store and request a sync.
- String path = FileStoreAPI.KEYS_DIR + "/" + destinationKeyFilename;
+ String path = ClusterFileStore.KEYS_DIR + "/" + destinationKeyFilename;
PackageUtils.uploadKey(key, path, Paths.get(solrHome));
PackageUtils.getJsonStringFromNonCollectionApi(
solrClient, "/api/node/files" + path, new ModifiableSolrParams().add("sync", "true"));
diff --git a/solr/core/src/java/org/apache/solr/pkg/PackageAPI.java b/solr/core/src/java/org/apache/solr/pkg/PackageAPI.java
index 7e2c5a6f73e..24339feefe8 100644
--- a/solr/core/src/java/org/apache/solr/pkg/PackageAPI.java
+++ b/solr/core/src/java/org/apache/solr/pkg/PackageAPI.java
@@ -45,7 +45,7 @@ import org.apache.solr.common.util.EnvUtils;
import org.apache.solr.common.util.ReflectMapWriter;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CoreContainer;
-import org.apache.solr.filestore.FileStoreAPI;
+import org.apache.solr.filestore.FileStoreUtils;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.util.SolrJacksonAnnotationInspector;
@@ -73,10 +73,6 @@ public class PackageAPI {
public final Read readAPI = new Read();
public PackageAPI(CoreContainer coreContainer, SolrPackageLoader loader) {
- if (coreContainer.getFileStoreAPI() == null) {
- throw new IllegalStateException("Must successfully load FileStoreAPI first");
- }
-
this.coreContainer = coreContainer;
this.packageLoader = loader;
pkgs = new Packages();
@@ -254,10 +250,10 @@ public class PackageAPI {
}
// first refresh my own
packageLoader.notifyListeners(p);
- for (String s : coreContainer.getFileStoreAPI().shuffledNodes()) {
+ for (String liveNode : FileStoreUtils.fetchAndShuffleRemoteLiveNodes(coreContainer)) {
Utils.executeGET(
coreContainer.getUpdateShardHandler().getDefaultHttpClient(),
- coreContainer.getZkController().zkStateReader.getBaseUrlV2ForNodeName(s)
+ coreContainer.getZkController().zkStateReader.getBaseUrlV2ForNodeName(liveNode)
+ "/cluster/package?wt=javabin&omitHeader=true&refreshPackage="
+ p,
Utils.JAVABINCONSUMER);
@@ -272,8 +268,8 @@ public class PackageAPI {
payload.addError("No files specified");
return;
}
- FileStoreAPI fileStoreAPI = coreContainer.getFileStoreAPI();
- fileStoreAPI.validateFiles(add.files, true, s -> payload.addError(s));
+ FileStoreUtils.validateFiles(
+ coreContainer.getFileStore(), add.files, true, s -> payload.addError(s));
if (payload.hasError()) return;
Packages[] finalState = new Packages[1];
try {
@@ -422,10 +418,10 @@ public class PackageAPI {
}
void notifyAllNodesToSync(int expected) {
- for (String s : coreContainer.getFileStoreAPI().shuffledNodes()) {
+ for (String liveNode : FileStoreUtils.fetchAndShuffleRemoteLiveNodes(coreContainer)) {
Utils.executeGET(
coreContainer.getUpdateShardHandler().getDefaultHttpClient(),
- coreContainer.getZkController().zkStateReader.getBaseUrlV2ForNodeName(s)
+ coreContainer.getZkController().zkStateReader.getBaseUrlV2ForNodeName(liveNode)
+ "/cluster/package?wt=javabin&omitHeader=true&expectedVersion"
+ expected,
Utils.JAVABINCONSUMER);
diff --git a/solr/core/src/java/org/apache/solr/pkg/SolrPackageLoader.java b/solr/core/src/java/org/apache/solr/pkg/SolrPackageLoader.java
index 5fe62e1f46b..0d5294f1ae6 100644
--- a/solr/core/src/java/org/apache/solr/pkg/SolrPackageLoader.java
+++ b/solr/core/src/java/org/apache/solr/pkg/SolrPackageLoader.java
@@ -42,6 +42,7 @@ import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrResourceLoader;
+import org.apache.solr.filestore.FileStoreUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -272,12 +273,13 @@ public class SolrPackageLoader implements Closeable {
List<Path> paths = new ArrayList<>();
List<String> errs = new ArrayList<>();
- coreContainer.getFileStoreAPI().validateFiles(version.files, true, s -> errs.add(s));
+ FileStoreUtils.validateFiles(
+ coreContainer.getFileStore(), version.files, true, s -> errs.add(s));
if (!errs.isEmpty()) {
throw new RuntimeException("Cannot load package: " + errs);
}
for (String file : version.files) {
- paths.add(coreContainer.getFileStoreAPI().getFileStore().getRealpath(file));
+ paths.add(coreContainer.getFileStore().getRealpath(file));
}
loader =
diff --git a/solr/core/src/test/org/apache/solr/filestore/TestDistribFileStore.java b/solr/core/src/test/org/apache/solr/filestore/TestDistribFileStore.java
index 62668789d22..f7d9144d812 100644
--- a/solr/core/src/test/org/apache/solr/filestore/TestDistribFileStore.java
+++ b/solr/core/src/test/org/apache/solr/filestore/TestDistribFileStore.java
@@ -83,9 +83,8 @@ public class TestDistribFileStore extends SolrCloudTestCase {
.addConfig("conf", configset("cloud-minimal"))
.configure();
try {
-
byte[] derFile = readFile("cryptokeys/pub_key512.der");
- uploadKey(derFile, FileStoreAPI.KEYS_DIR + "/pub_key512.der", cluster);
+ uploadKey(derFile, ClusterFileStore.KEYS_DIR + "/pub_key512.der", cluster);
try {
postFile(
diff --git a/solr/core/src/test/org/apache/solr/handler/TestContainerPlugin.java b/solr/core/src/test/org/apache/solr/handler/TestContainerPlugin.java
index bd19c67f935..3b657e9cf72 100644
--- a/solr/core/src/test/org/apache/solr/handler/TestContainerPlugin.java
+++ b/solr/core/src/test/org/apache/solr/handler/TestContainerPlugin.java
@@ -53,7 +53,7 @@ import org.apache.solr.common.util.ReflectMapWriter;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.embedded.JettySolrRunner;
-import org.apache.solr.filestore.FileStoreAPI;
+import org.apache.solr.filestore.ClusterFileStore;
import org.apache.solr.filestore.TestDistribFileStore;
import org.apache.solr.filestore.TestDistribFileStore.Fetcher;
import org.apache.solr.pkg.PackageAPI;
@@ -294,7 +294,7 @@ public class TestContainerPlugin extends SolrCloudTestCase {
int version = phaser.getPhase();
byte[] derFile = readFile("cryptokeys/pub_key512.der");
- uploadKey(derFile, FileStoreAPI.KEYS_DIR + "/pub_key512.der", cluster);
+ uploadKey(derFile, ClusterFileStore.KEYS_DIR + "/pub_key512.der", cluster);
TestPackages.postFileAndWait(
cluster,
"runtimecode/containerplugin.v.1.jar.bin",
diff --git a/solr/core/src/test/org/apache/solr/pkg/TestPackages.java b/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
index aae36019a65..04423132451 100644
--- a/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
+++ b/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
@@ -68,7 +68,7 @@ import org.apache.solr.common.util.ReflectMapWriter;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.SolrCore;
import org.apache.solr.embedded.JettySolrRunner;
-import org.apache.solr.filestore.FileStoreAPI;
+import org.apache.solr.filestore.ClusterFileStore;
import org.apache.solr.filestore.TestDistribFileStore;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
@@ -121,7 +121,7 @@ public class TestPackages extends SolrCloudTestCase {
String FILE1 = "/mypkg/runtimelibs.jar";
String COLLECTION_NAME = "testCoreReloadingPluginColl";
byte[] derFile = readFile("cryptokeys/pub_key512.der");
- uploadKey(derFile, FileStoreAPI.KEYS_DIR + "/pub_key512.der", cluster);
+ uploadKey(derFile, ClusterFileStore.KEYS_DIR + "/pub_key512.der", cluster);
postFileAndWait(
cluster,
"runtimecode/runtimelibs.jar.bin",
@@ -190,7 +190,7 @@ public class TestPackages extends SolrCloudTestCase {
String EXPR1 = "/mypkg/expressible.jar";
String COLLECTION_NAME = "testPluginLoadingColl";
byte[] derFile = readFile("cryptokeys/pub_key512.der");
- uploadKey(derFile, FileStoreAPI.KEYS_DIR + "/pub_key512.der", cluster);
+ uploadKey(derFile, ClusterFileStore.KEYS_DIR + "/pub_key512.der", cluster);
postFileAndWait(
cluster,
"runtimecode/runtimelibs.jar.bin",
@@ -609,7 +609,7 @@ public class TestPackages extends SolrCloudTestCase {
expectError(req, cluster.getSolrClient(), errPath, FILE1 + " has no signature");
// now we upload the keys
byte[] derFile = readFile("cryptokeys/pub_key512.der");
- uploadKey(derFile, FileStoreAPI.KEYS_DIR + "/pub_key512.der", cluster);
+ uploadKey(derFile, ClusterFileStore.KEYS_DIR + "/pub_key512.der", cluster);
// and upload the same file with a different name, but it has proper signature
postFileAndWait(
cluster,
@@ -755,7 +755,7 @@ public class TestPackages extends SolrCloudTestCase {
String FILE1 = "/schemapkg/schema-plugins.jar";
byte[] derFile = readFile("cryptokeys/pub_key512.der");
- uploadKey(derFile, FileStoreAPI.KEYS_DIR + "/pub_key512.der", cluster);
+ uploadKey(derFile, ClusterFileStore.KEYS_DIR + "/pub_key512.der", cluster);
postFileAndWait(
cluster,
"runtimecode/schema-plugins.jar.bin",