| /* |
| * 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.cloud.api.collections; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.URI; |
| import org.apache.lucene.store.Directory; |
| import org.apache.lucene.store.IOContext; |
| import org.apache.lucene.store.IndexInput; |
| import org.apache.lucene.tests.util.LuceneTestCase; |
| import org.apache.solr.client.solrj.SolrServerException; |
| import org.apache.solr.client.solrj.impl.CloudSolrClient; |
| import org.apache.solr.client.solrj.request.CollectionAdminRequest; |
| import org.apache.solr.cloud.MiniSolrCloudCluster; |
| import org.apache.solr.cloud.SolrCloudTestCase; |
| import org.apache.solr.cloud.ZkConfigSetService; |
| import org.apache.solr.common.SolrException; |
| import org.apache.solr.common.SolrException.ErrorCode; |
| import org.apache.solr.core.backup.repository.LocalFileSystemRepository; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| |
| /** |
| * This class implements the tests for local file-system integration for Solr backup/restore |
| * capability. Note that the Solr backup/restore still requires a "shared" file-system. Its just |
| * that in this case such file-system would be exposed via local file-system API. |
| */ |
| // Backups do checksum validation against a footer value not present in 'SimpleText' |
| @LuceneTestCase.SuppressCodecs({"SimpleText"}) |
| @SolrCloudTestCase.NoPrs // disabled PRS for a while to enure if that is causing test failures |
| public class TestLocalFSCloudBackupRestore extends AbstractCloudBackupRestoreTestCase { |
| private static String backupLocation; |
| |
| @BeforeClass |
| public static void setupClass() throws Exception { |
| String solrXml = MiniSolrCloudCluster.DEFAULT_CLOUD_SOLR_XML; |
| String poisoned = |
| " <repository name=\"" |
| + TestLocalFSCloudBackupRestore.poisoned |
| + "\" default=\"true\" " |
| + "class=\"org.apache.solr.cloud.api.collections.TestLocalFSCloudBackupRestore$PoisonedRepository\"> \n" |
| + " </repository>\n"; |
| String local = |
| " <repository name=\"local\" " |
| + "class=\"org.apache.solr.core.backup.repository.LocalFileSystemRepository\"> \n" |
| + " </repository>\n"; |
| solrXml = |
| solrXml.replace( |
| "</solr>", |
| "<backup>" |
| + (random().nextBoolean() ? poisoned + local : local + poisoned) |
| + "</backup>" |
| + "</solr>"); |
| |
| configureCluster(NUM_SHARDS) // nodes |
| .addConfig( |
| "conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf")) |
| .addConfig( |
| "confFaulty", |
| TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf")) |
| .withSolrXml(solrXml) |
| .configure(); |
| cluster |
| .getZkClient() |
| .delete( |
| ZkConfigSetService.CONFIGS_ZKNODE + "/" + "confFaulty" + "/" + "solrconfig.xml", |
| -1, |
| true); |
| |
| boolean whitespacesInPath = random().nextBoolean(); |
| if (whitespacesInPath) { |
| backupLocation = createTempDir("my backup").toAbsolutePath().toString(); |
| } else { |
| backupLocation = createTempDir("mybackup").toAbsolutePath().toString(); |
| } |
| } |
| |
| @Override |
| public String getCollectionNamePrefix() { |
| return "backuprestore"; |
| } |
| |
| @Override |
| public String getBackupRepoName() { |
| return "local"; |
| } |
| |
| @Override |
| public String getBackupLocation() { |
| return backupLocation; |
| } |
| |
| @Override |
| @Test |
| public void test() throws Exception { |
| super.test(); |
| |
| CloudSolrClient solrClient = cluster.getSolrClient(); |
| |
| errorBackup(solrClient); |
| |
| errorRestore(solrClient); |
| } |
| |
| private void errorRestore(CloudSolrClient solrClient) throws SolrServerException, IOException { |
| String backupName = BACKUPNAME_PREFIX + testSuffix; |
| CollectionAdminRequest.Restore restore = |
| CollectionAdminRequest.restoreCollection(getCollectionName() + "boo", backupName) |
| .setLocation(backupLocation); |
| if (random().nextBoolean()) { |
| restore.setRepositoryName(poisoned); |
| } |
| try { |
| restore.process(solrClient); |
| fail("This request should have failed since omitting repo, picks up default poisoned."); |
| } catch (SolrException ex) { |
| assertEquals(ErrorCode.SERVER_ERROR.code, ex.code()); |
| assertTrue(ex.getMessage(), ex.getMessage().contains(poisoned)); |
| } |
| } |
| |
| private void errorBackup(CloudSolrClient solrClient) throws SolrServerException, IOException { |
| CollectionAdminRequest.Backup backup = |
| CollectionAdminRequest.backupCollection(getCollectionName(), "poisonedbackup") |
| .setLocation(getBackupLocation()); |
| if (random().nextBoolean()) { |
| backup.setRepositoryName(poisoned); |
| } // otherwise, we hit default |
| |
| try { |
| backup.process(solrClient); |
| fail("This request should have failed since omitting repo, picks up default poisoned."); |
| } catch (SolrException ex) { |
| assertEquals(ErrorCode.SERVER_ERROR.code, ex.code()); |
| } |
| } |
| |
| private static final String poisoned = "poisoned"; |
| |
| // let it go through collection handler, and break only when real thing is doing: |
| // Restore/BackupCore |
| public static class PoisonedRepository extends LocalFileSystemRepository { |
| |
| public PoisonedRepository() { |
| super(); |
| } |
| |
| @Override |
| public void copyFileFrom(Directory sourceDir, String fileName, URI dest) { |
| throw new UnsupportedOperationException(poisoned); |
| } |
| |
| @Override |
| public void copyFileTo(URI sourceDir, String fileName, Directory dest) { |
| throw new UnsupportedOperationException(poisoned); |
| } |
| |
| @Override |
| public IndexInput openInput(URI dirPath, String fileName, IOContext ctx) { |
| throw new UnsupportedOperationException(poisoned); |
| } |
| |
| @Override |
| public OutputStream createOutput(URI path) { |
| throw new UnsupportedOperationException(poisoned); |
| } |
| } |
| } |