mirror of https://github.com/apache/jclouds.git
Fix for FileSystem blob store clearContainer with options
This commit is contained in:
parent
f9cebd59f8
commit
a36c9dcef0
|
@ -27,6 +27,7 @@ import static java.nio.file.Files.getPosixFilePermissions;
|
||||||
import static java.nio.file.Files.probeContentType;
|
import static java.nio.file.Files.probeContentType;
|
||||||
import static java.nio.file.Files.readAttributes;
|
import static java.nio.file.Files.readAttributes;
|
||||||
import static java.nio.file.Files.setPosixFilePermissions;
|
import static java.nio.file.Files.setPosixFilePermissions;
|
||||||
|
import static java.nio.file.Files.newDirectoryStream;
|
||||||
import static org.jclouds.filesystem.util.Utils.delete;
|
import static org.jclouds.filesystem.util.Utils.delete;
|
||||||
import static org.jclouds.filesystem.util.Utils.isPrivate;
|
import static org.jclouds.filesystem.util.Utils.isPrivate;
|
||||||
import static org.jclouds.filesystem.util.Utils.isWindows;
|
import static org.jclouds.filesystem.util.Utils.isWindows;
|
||||||
|
@ -41,6 +42,7 @@ import java.io.InputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.AccessDeniedException;
|
import java.nio.file.AccessDeniedException;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
import java.nio.file.NoSuchFileException;
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
@ -58,6 +60,7 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import org.jclouds.blobstore.ContainerNotFoundException;
|
import org.jclouds.blobstore.ContainerNotFoundException;
|
||||||
import org.jclouds.blobstore.KeyNotFoundException;
|
import org.jclouds.blobstore.KeyNotFoundException;
|
||||||
import org.jclouds.blobstore.LocalStorageStrategy;
|
import org.jclouds.blobstore.LocalStorageStrategy;
|
||||||
|
@ -253,16 +256,45 @@ public class FilesystemStorageStrategyImpl implements LocalStorageStrategy {
|
||||||
@Override
|
@Override
|
||||||
public void clearContainer(String container, ListContainerOptions options) {
|
public void clearContainer(String container, ListContainerOptions options) {
|
||||||
filesystemContainerNameValidator.validate(container);
|
filesystemContainerNameValidator.validate(container);
|
||||||
// TODO: these require calling removeDirectoriesTreeOfBlobKey
|
checkArgument(options.getDir() == null || options.getPrefix() == null, "cannot specify both directory and prefix");
|
||||||
checkArgument(options.getDir() == null && options.getPrefix() == null, "cannot specify directory or prefix");
|
String optsPrefix = Strings.nullToEmpty(options.getDir() == null ? options.getPrefix() : options.getDir());
|
||||||
|
String normalizedOptsPath = normalize(optsPrefix);
|
||||||
|
String basePath = buildPathStartingFromBaseDir(container, normalizedOptsPath);
|
||||||
|
filesystemBlobKeyValidator.validate(basePath);
|
||||||
try {
|
try {
|
||||||
File containerFile = openFolder(container);
|
File object = new File(basePath);
|
||||||
File[] children = containerFile.listFiles();
|
if (object.isFile()) {
|
||||||
if (null != children) {
|
// To mimic the S3 type blobstores, a prefix for an object blob
|
||||||
for (File child : children)
|
// should also get deleted
|
||||||
if (options.isRecursive() || child.isFile()) {
|
delete(object);
|
||||||
Utils.deleteRecursively(child);
|
}
|
||||||
|
else if (object.isDirectory() && (optsPrefix.endsWith(File.separator) || isNullOrEmpty(optsPrefix))) {
|
||||||
|
// S3 blobstores will only match prefixes that end with a trailing slash/file separator
|
||||||
|
// For instance, if we have a blob at /path/1/2/a, a prefix of /path/1/2 will not list /path/1/2/a
|
||||||
|
// but a prefix of /path/1/2/ will
|
||||||
|
File containerFile = openFolder(container + File.separator + normalizedOptsPath);
|
||||||
|
File[] children = containerFile.listFiles();
|
||||||
|
if (null != children) {
|
||||||
|
for (File child : children) {
|
||||||
|
if (options.isRecursive()) {
|
||||||
|
Utils.deleteRecursively(child);
|
||||||
|
} else {
|
||||||
|
if (child.isFile()) {
|
||||||
|
Utils.delete(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty dirs in path if they don't have any objects
|
||||||
|
if (!optsPrefix.isEmpty()) {
|
||||||
|
if (options.isRecursive()) {
|
||||||
|
//first, remove the empty dir. It should be totally empty if it was a
|
||||||
|
// recursive delete
|
||||||
|
deleteDirectory(container, optsPrefix);
|
||||||
|
}
|
||||||
|
removeDirectoriesTreeOfBlobKey(container, optsPrefix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error(e, "An error occurred while clearing container %s", container);
|
logger.error(e, "An error occurred while clearing container %s", container);
|
||||||
|
@ -858,6 +890,18 @@ public class FilesystemStorageStrategyImpl implements LocalStorageStrategy {
|
||||||
return pathToBeCleaned.substring(beginIndex, endIndex);
|
return pathToBeCleaned.substring(beginIndex, endIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a directory is empty using a DirectoryStream iterator
|
||||||
|
*
|
||||||
|
* @param directoryPath
|
||||||
|
*/
|
||||||
|
private boolean isDirEmpty(String directoryPath) throws IOException {
|
||||||
|
Path path = new File(directoryPath).toPath();
|
||||||
|
try (DirectoryStream<Path> dirStream = newDirectoryStream(path)) {
|
||||||
|
return !dirStream.iterator().hasNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes recursively the directory structure of a complex blob key, only if the directory is
|
* Removes recursively the directory structure of a complex blob key, only if the directory is
|
||||||
* empty
|
* empty
|
||||||
|
@ -889,16 +933,21 @@ public class FilesystemStorageStrategyImpl implements LocalStorageStrategy {
|
||||||
logger.debug("Could not look for attributes from %s: %s", directory, e);
|
logger.debug("Could not look for attributes from %s: %s", directory, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] children = directory.list();
|
// Don't need to do a listing on the dir, which could be costly. The iterator should be more performant.
|
||||||
if (null == children || children.length == 0) {
|
try {
|
||||||
try {
|
if (isDirEmpty(directory.getPath())) {
|
||||||
delete(directory);
|
try {
|
||||||
} catch (IOException e) {
|
delete(directory);
|
||||||
logger.debug("Could not delete %s: %s", directory, e);
|
} catch (IOException e) {
|
||||||
return;
|
logger.debug("Could not delete %s: %s", directory, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// recursively call for removing other path
|
||||||
|
removeDirectoriesTreeOfBlobKey(container, parentPath);
|
||||||
}
|
}
|
||||||
// recursively call for removing other path
|
} catch (IOException e) {
|
||||||
removeDirectoriesTreeOfBlobKey(container, parentPath);
|
logger.debug("Could not locate directory %s", directory, e);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,8 +194,4 @@ public class FilesystemContainerIntegrationTest extends BaseContainerIntegration
|
||||||
throw new SkipException("filesystem does not support anonymous access");
|
throw new SkipException("filesystem does not support anonymous access");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void testClearWithOptions() throws InterruptedException {
|
|
||||||
throw new SkipException("filesystem does not support clear with options");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,6 +182,8 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
try {
|
try {
|
||||||
ListContainerOptions options;
|
ListContainerOptions options;
|
||||||
|
|
||||||
|
// Should wipe out all objects, as there are empty folders
|
||||||
|
// above
|
||||||
add5NestedBlobsToContainer(containerName);
|
add5NestedBlobsToContainer(containerName);
|
||||||
options = new ListContainerOptions();
|
options = new ListContainerOptions();
|
||||||
options.prefix("path/1/");
|
options.prefix("path/1/");
|
||||||
|
@ -195,7 +197,9 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
options.prefix("path/1/2/3");
|
options.prefix("path/1/2/3");
|
||||||
options.recursive();
|
options.recursive();
|
||||||
view.getBlobStore().clearContainer(containerName, options);
|
view.getBlobStore().clearContainer(containerName, options);
|
||||||
assertConsistencyAwareContainerSize(containerName, 2);
|
assertConsistencyAwareBlobExists(containerName, "path/1/a");
|
||||||
|
assertConsistencyAwareBlobExists(containerName, "path/1/2/b");
|
||||||
|
assertConsistencyAwareBlobDoesntExist(containerName, "path/1/2/3");
|
||||||
|
|
||||||
view.getBlobStore().clearContainer(containerName);
|
view.getBlobStore().clearContainer(containerName);
|
||||||
add5NestedBlobsToContainer(containerName);
|
add5NestedBlobsToContainer(containerName);
|
||||||
|
@ -203,7 +207,10 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
options.prefix("path/1/2/3/4/");
|
options.prefix("path/1/2/3/4/");
|
||||||
options.recursive();
|
options.recursive();
|
||||||
view.getBlobStore().clearContainer(containerName, options);
|
view.getBlobStore().clearContainer(containerName, options);
|
||||||
assertConsistencyAwareContainerSize(containerName, 4);
|
assertConsistencyAwareBlobExists(containerName, "path/1/a");
|
||||||
|
assertConsistencyAwareBlobExists(containerName, "path/1/2/b");
|
||||||
|
assertConsistencyAwareBlobExists(containerName, "path/1/2/3/5/e");
|
||||||
|
assertConsistencyAwareBlobDoesntExist(containerName, "path/1/2/3/4");
|
||||||
|
|
||||||
// non-recursive, should not clear anything, as prefix does not match
|
// non-recursive, should not clear anything, as prefix does not match
|
||||||
view.getBlobStore().clearContainer(containerName);
|
view.getBlobStore().clearContainer(containerName);
|
||||||
|
@ -211,7 +218,11 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
options = new ListContainerOptions();
|
options = new ListContainerOptions();
|
||||||
options.prefix("path/1/2/3");
|
options.prefix("path/1/2/3");
|
||||||
view.getBlobStore().clearContainer(containerName, options);
|
view.getBlobStore().clearContainer(containerName, options);
|
||||||
assertConsistencyAwareContainerSize(containerName, 5);
|
assertConsistencyAwareBlobExists(containerName, "path/1/a");
|
||||||
|
assertConsistencyAwareBlobExists(containerName, "path/1/2/b");
|
||||||
|
assertConsistencyAwareBlobExists(containerName, "path/1/2/3/c");
|
||||||
|
assertConsistencyAwareBlobExists(containerName, "path/1/2/3/5/e");
|
||||||
|
|
||||||
|
|
||||||
// non-recursive, should only clear path/1/2/3/c
|
// non-recursive, should only clear path/1/2/3/c
|
||||||
view.getBlobStore().clearContainer(containerName);
|
view.getBlobStore().clearContainer(containerName);
|
||||||
|
@ -219,7 +230,10 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
options = new ListContainerOptions();
|
options = new ListContainerOptions();
|
||||||
options.prefix("path/1/2/3/");
|
options.prefix("path/1/2/3/");
|
||||||
view.getBlobStore().clearContainer(containerName, options);
|
view.getBlobStore().clearContainer(containerName, options);
|
||||||
assertConsistencyAwareContainerSize(containerName, 4);
|
assertConsistencyAwareBlobExists(containerName, "path/1/a");
|
||||||
|
assertConsistencyAwareBlobExists(containerName, "path/1/2/b");
|
||||||
|
assertConsistencyAwareBlobExists(containerName, "path/1/2/3/4/d");
|
||||||
|
assertConsistencyAwareBlobDoesntExist(containerName, "path/1/2/3/c");
|
||||||
|
|
||||||
// non-recursive, should only clear path/1/2/3/c
|
// non-recursive, should only clear path/1/2/3/c
|
||||||
view.getBlobStore().clearContainer(containerName);
|
view.getBlobStore().clearContainer(containerName);
|
||||||
|
@ -227,7 +241,10 @@ public class BaseContainerIntegrationTest extends BaseBlobStoreIntegrationTest {
|
||||||
options = new ListContainerOptions();
|
options = new ListContainerOptions();
|
||||||
options.prefix("path/1/2/3/c");
|
options.prefix("path/1/2/3/c");
|
||||||
view.getBlobStore().clearContainer(containerName, options);
|
view.getBlobStore().clearContainer(containerName, options);
|
||||||
assertConsistencyAwareContainerSize(containerName, 4);
|
assertConsistencyAwareBlobExists(containerName, "path/1/a");
|
||||||
|
assertConsistencyAwareBlobExists(containerName, "path/1/2/b");
|
||||||
|
assertConsistencyAwareBlobExists(containerName, "path/1/2/3/4/d");
|
||||||
|
assertConsistencyAwareBlobDoesntExist(containerName, "path/1/2/3/c");
|
||||||
} finally {
|
} finally {
|
||||||
returnContainer(containerName);
|
returnContainer(containerName);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue