Browse Source

Remove quota-aware-fs plugin (#76352)

The quota aware filesystem was added as a means of allowing
Elasticsearch to track the used space of the underlying filesystem in
virtualized environments. However, the need for it was due to a bug in a
much earlier version of Elasticsearch that always found the underlying
mount and checked it directely for usage. That bug has already been
fixed, so the there is no longer a need for this plugin. This commit
removes the plugin. We should consider separately whether there is still
a need for bootstrap plugins.

closes #70309
Ryan Ernst 4 năm trước cách đây
mục cha
commit
96627dfa14

+ 1 - 14
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/DistroTestPlugin.java

@@ -77,9 +77,6 @@ public class DistroTestPlugin implements Plugin<Project> {
     private static final String BWC_DISTRIBUTION_SYSPROP = "tests.bwc-distribution";
     private static final String EXAMPLE_PLUGIN_SYSPROP = "tests.example-plugin";
 
-    private static final String QUOTA_AWARE_FS_PLUGIN_CONFIGURATION = "quotaAwareFsPlugin";
-    private static final String QUOTA_AWARE_FS_PLUGIN_SYSPROP = "tests.quota-aware-fs-plugin";
-
     @Override
     public void apply(Project project) {
         project.getRootProject().getPluginManager().apply(DockerSupportPlugin.class);
@@ -102,7 +99,6 @@ public class DistroTestPlugin implements Plugin<Project> {
         TaskProvider<Task> destructiveDistroTest = project.getTasks().register("destructiveDistroTest");
 
         Configuration examplePlugin = configureExamplePlugin(project);
-        Configuration quotaAwareFsPlugin = configureQuotaAwareFsPlugin(project);
 
         List<TaskProvider<Test>> windowsTestTasks = new ArrayList<>();
         Map<ElasticsearchDistributionType, List<TaskProvider<Test>>> linuxTestTasks = new HashMap<>();
@@ -113,13 +109,12 @@ public class DistroTestPlugin implements Plugin<Project> {
             String taskname = destructiveDistroTestTaskName(distribution);
             TaskProvider<?> depsTask = project.getTasks().register(taskname + "#deps");
             // explicitly depend on the archive not on the implicit extracted distribution
-            depsTask.configure(t -> t.dependsOn(distribution.getArchiveDependencies(), examplePlugin, quotaAwareFsPlugin));
+            depsTask.configure(t -> t.dependsOn(distribution.getArchiveDependencies(), examplePlugin));
             depsTasks.put(taskname, depsTask);
             TaskProvider<Test> destructiveTask = configureTestTask(project, taskname, distribution, t -> {
                 t.onlyIf(t2 -> distribution.isDocker() == false || dockerSupport.get().getDockerAvailability().isAvailable);
                 addDistributionSysprop(t, DISTRIBUTION_SYSPROP, distribution::getFilepath);
                 addDistributionSysprop(t, EXAMPLE_PLUGIN_SYSPROP, () -> examplePlugin.getSingleFile().toString());
-                addDistributionSysprop(t, QUOTA_AWARE_FS_PLUGIN_SYSPROP, () -> quotaAwareFsPlugin.getSingleFile().toString());
                 t.exclude("**/PackageUpgradeTests.class");
             }, depsTask);
 
@@ -317,14 +312,6 @@ public class DistroTestPlugin implements Plugin<Project> {
         return examplePlugin;
     }
 
-    private static Configuration configureQuotaAwareFsPlugin(Project project) {
-        Configuration examplePlugin = project.getConfigurations().create(QUOTA_AWARE_FS_PLUGIN_CONFIGURATION);
-        DependencyHandler deps = project.getDependencies();
-        Map<String, String> quotaAwareFsPluginProject = Map.of("path", ":x-pack:quota-aware-fs", "configuration", "zip");
-        deps.add(QUOTA_AWARE_FS_PLUGIN_CONFIGURATION, deps.project(quotaAwareFsPluginProject));
-        return examplePlugin;
-    }
-
     private static void configureVMWrapperTasks(
         Project project,
         List<TaskProvider<Test>> destructiveTasks,

+ 1 - 2
build-tools/src/main/java/org/elasticsearch/gradle/util/GradleUtils.java

@@ -205,8 +205,7 @@ public abstract class GradleUtils {
 
     public static boolean isModuleProject(String projectPath) {
         return projectPath.contains("modules:")
-            || projectPath.startsWith(":x-pack:plugin")
-            || projectPath.startsWith(":x-pack:quota-aware-fs");
+            || projectPath.startsWith(":x-pack:plugin");
     }
 
     public static void disableTransitiveDependencies(Configuration config) {

+ 0 - 16
docs/plugins/filesystem.asciidoc

@@ -1,16 +0,0 @@
-[[filesystem]]
-== Filesystem Plugins
-
-Filesystem plugins modify how {es} interacts with the host filesystem.
-
-[discrete]
-=== Core filesystem plugins
-
-The core filesystem plugins are:
-
-<<quota-aware-fs,Quota-aware Filesystem>>::
-
-The Quota-aware Filesystem plugin adds an interface for telling Elasticsearch the disk-quota limits under which it is operating.
-
-include::quota-aware-fs.asciidoc[]
-

+ 0 - 2
docs/plugins/index.asciidoc

@@ -44,8 +44,6 @@ include::analysis.asciidoc[]
 
 include::discovery.asciidoc[]
 
-include::filesystem.asciidoc[]
-
 include::ingest.asciidoc[]
 
 include::mapper.asciidoc[]

+ 0 - 44
docs/plugins/quota-aware-fs.asciidoc

@@ -1,44 +0,0 @@
-[[quota-aware-fs]]
-=== Quota-aware Filesystem Plugin
-
-NOTE: {cloud-only}
-
-The Quota-aware Filesystem plugin adds an interface for telling
-Elasticsearch the disk-quota limits under which it is operating.
-
-:plugin_name: quota-aware-fs
-include::install_remove.asciidoc[]
-
-[[quota-aware-fs-usage]]
-==== Passing disk quota information to Elasticsearch
-
-{es} considers the available disk space on a node before deciding whether
-to allocate new shards to that node or to actively relocate shards away from that node.
-However, while the JVM has support for reporting a filesystem's total space and available
-space, it has no knowledge of any quota limits imposed on the user under which {es} is
-running. Consequently, the {es} mechanisms for handling low disk space situations cannot
-function. To work around this situation, this plugin provides a mechanism for supplying quota-ware
-total and available amounts of disk space.
-
-To use the plugin, install it on all nodes and restart them. You must configure the plugin
-by supplying the `es.fs.quota.file` {ref}/jvm-options.html[JVM system property] on startup. This
-property specifies a URI to a properties file, which contains the total and available
-amounts.
-
-NOTE: {es} will not start successfully if you install the `quota-aware-fs` plugin,
-but you do not supply the `es.fs.quota.file` system property at startup.
-
-[source,text]
-----
--Des.fs.quota.file=file:///path/to/some.properties
-----
-
-The properties file must contain the keys `total` and `remaining`, both of which contain the respective
-number in bytes. You are responsible for writing this file with the correct values, and keeping the
-values up-to-date. {es} will poll this file regularly to pick up any changes.
-
-[source,properties]
-----
-total=976490576
-remaining=376785728
-----

+ 0 - 213
qa/os/src/test/java/org/elasticsearch/packaging/test/QuotaAwareFsTests.java

@@ -1,213 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-package org.elasticsearch.packaging.test;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import org.apache.http.client.fluent.Request;
-import org.elasticsearch.core.CheckedRunnable;
-import org.elasticsearch.packaging.util.ServerUtils;
-import org.elasticsearch.packaging.util.Shell;
-import org.junit.After;
-import org.junit.BeforeClass;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.Locale;
-import java.util.stream.Collectors;
-
-import static org.elasticsearch.packaging.util.Distribution.Platform.WINDOWS;
-import static org.hamcrest.Matchers.arrayContaining;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.emptyString;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-/**
- * Check that the quota-aware filesystem plugin can be installed, and that it operates as expected.
- */
-public class QuotaAwareFsTests extends PackagingTestCase {
-
-    // private static final String QUOTA_AWARE_FS_PLUGIN_NAME = "quota-aware-fs";
-    private static final Path QUOTA_AWARE_FS_PLUGIN;
-    static {
-        // Re-read before each test so the plugin path can be manipulated within tests.
-        // Corresponds to DistroTestPlugin#QUOTA_AWARE_FS_PLUGIN_SYSPROP
-        QUOTA_AWARE_FS_PLUGIN = Paths.get(System.getProperty("tests.quota-aware-fs-plugin"));
-    }
-
-    @BeforeClass
-    public static void filterDistros() {
-        assumeTrue("only archives", distribution.isArchive());
-        assumeFalse("not on windows", distribution.platform == WINDOWS);
-    }
-
-    @After
-    public void teardown() throws Exception {
-        super.teardown();
-        cleanup();
-    }
-
-    /**
-     * Check that when the plugin is installed but the system property for passing the location of the related
-     * properties file is omitted, then Elasticsearch exits with the expected error message.
-     */
-    public void test10ElasticsearchRequiresSystemPropertyToBeSet() throws Exception {
-        install();
-
-        installation.executables().pluginTool.run("install --batch \"" + QUOTA_AWARE_FS_PLUGIN.toUri() + "\"");
-
-        // Without setting the `es.fs.quota.file` property, ES should exit with a failure code.
-        final Shell.Result result = runElasticsearchStartCommand(null, false, false);
-
-        assertThat("Elasticsearch should have terminated unsuccessfully", result.isSuccess(), equalTo(false));
-        assertThat(
-            result.stderr,
-            containsString("Property es.fs.quota.file must be set to a URI in order to use the quota filesystem provider")
-        );
-    }
-
-    /**
-     * Check that when the plugin is installed but the system property for passing the location of the related
-     * properties file contains a non-existent URI, then Elasticsearch exits with the expected error message.
-     */
-    public void test20ElasticsearchRejectsNonExistentPropertiesLocation() throws Exception {
-        install();
-
-        installation.executables().pluginTool.run("install --batch \"" + QUOTA_AWARE_FS_PLUGIN.toUri() + "\"");
-
-        sh.getEnv().put("ES_JAVA_OPTS", "-Des.fs.quota.file=file:///this/does/not/exist.properties");
-
-        final Shell.Result result = runElasticsearchStartCommand(null, false, false);
-
-        // Generate a Path for this location so that the platform-specific line-endings will be used.
-        final String platformPath = Path.of("/this/does/not/exist.properties").toString();
-
-        assertThat("Elasticsearch should have terminated unsuccessfully", result.isSuccess(), equalTo(false));
-        assertThat(result.stderr, containsString("NoSuchFileException: " + platformPath));
-    }
-
-    /**
-     * Check that Elasticsearch can load the plugin and apply the quota limits in the properties file. Also check that
-     * Elasticsearch polls the file for changes.
-     */
-    public void test30ElasticsearchStartsWhenSystemPropertySet() throws Exception {
-        install();
-
-        int total = 20 * 1024 * 1024;
-        int available = 10 * 1024 * 1024;
-
-        installation.executables().pluginTool.run("install --batch \"" + QUOTA_AWARE_FS_PLUGIN.toUri() + "\"");
-
-        final Path quotaPath = getRootTempDir().resolve("quota.properties");
-        Files.writeString(quotaPath, String.format(Locale.ROOT, "total=%d\nremaining=%d\n", total, available));
-
-        sh.getEnv().put("ES_JAVA_OPTS", "-Des.fs.quota.file=" + quotaPath.toUri());
-
-        startElasticsearchAndThen(() -> {
-            final Totals actualTotals = fetchFilesystemTotals();
-
-            assertThat(actualTotals.totalInBytes, equalTo(total));
-            assertThat(actualTotals.availableInBytes, equalTo(available));
-
-            int updatedTotal = total * 3;
-            int updatedAvailable = available * 3;
-
-            // Check that ES is polling the properties file for changes by modifying the properties file
-            // and waiting for ES to pick up the changes.
-            Files.writeString(quotaPath, String.format(Locale.ROOT, "total=%d\nremaining=%d\n", updatedTotal, updatedAvailable));
-
-            // The check interval is 1000ms, but give ourselves some leeway.
-            Thread.sleep(2000);
-
-            final Totals updatedActualTotals = fetchFilesystemTotals();
-
-            assertThat(updatedActualTotals.totalInBytes, equalTo(updatedTotal));
-            assertThat(updatedActualTotals.availableInBytes, equalTo(updatedAvailable));
-        });
-    }
-
-    /**
-     * Check that the _cat API can list the plugin correctly.
-     */
-    public void test40CatApiFiltersPlugin() throws Exception {
-        install();
-
-        int total = 20 * 1024 * 1024;
-        int available = 10 * 1024 * 1024;
-
-        installation.executables().pluginTool.run("install --batch \"" + QUOTA_AWARE_FS_PLUGIN.toUri() + "\"");
-
-        final Path quotaPath = getRootTempDir().resolve("quota.properties");
-        Files.writeString(quotaPath, String.format(Locale.ROOT, "total=%d\nremaining=%d\n", total, available));
-
-        sh.getEnv().put("ES_JAVA_OPTS", "-Des.fs.quota.file=" + quotaPath.toUri());
-
-        startElasticsearchAndThen(() -> {
-            final String uri = "http://localhost:9200/_cat/plugins?include_bootstrap=true&h=component,type";
-            String response = ServerUtils.makeRequest(Request.Get(uri)).trim();
-            assertThat(response, not(emptyString()));
-
-            List<String> lines = response.lines().collect(Collectors.toList());
-            assertThat(lines, hasSize(1));
-
-            final String[] fields = lines.get(0).split(" ");
-            assertThat(fields, arrayContaining("quota-aware-fs", "bootstrap"));
-        });
-    }
-
-    private void startElasticsearchAndThen(CheckedRunnable<Exception> runnable) throws Exception {
-        boolean started = false;
-        try {
-            startElasticsearch();
-            started = true;
-
-            runnable.run();
-        } finally {
-            if (started) {
-                stopElasticsearch();
-            }
-        }
-    }
-
-    private static class Totals {
-        int totalInBytes;
-        int availableInBytes;
-
-        Totals(int totalInBytes, int availableInBytes) {
-            this.totalInBytes = totalInBytes;
-            this.availableInBytes = availableInBytes;
-        }
-    }
-
-    private Totals fetchFilesystemTotals() {
-        try {
-            final String response = ServerUtils.makeRequest(Request.Get("http://localhost:9200/_nodes/stats"));
-
-            final ObjectMapper mapper = new ObjectMapper();
-            final JsonNode rootNode = mapper.readTree(response);
-
-            assertThat("Some nodes failed", rootNode.at("/_nodes/failed").intValue(), equalTo(0));
-
-            final String nodeId = rootNode.get("nodes").fieldNames().next();
-
-            final JsonNode fsNode = rootNode.at("/nodes/" + nodeId + "/fs/total");
-
-            return new Totals(fsNode.get("total_in_bytes").intValue(), fsNode.get("available_in_bytes").intValue());
-        } catch (Exception e) {
-            throw new RuntimeException("Failed to fetch filesystem totals: " + e.getMessage(), e);
-        }
-    }
-}

+ 1 - 4
x-pack/qa/smoke-test-plugins/build.gradle

@@ -14,10 +14,7 @@ def pluginPaths = project(':plugins').getChildProjects().findAll { pluginName, p
   // Do not attempt to install ingest-attachment in FIPS 140 as it is not supported (it depends on non-FIPS BouncyCastle)
   def ignoreIngestAttachment = BuildParams.inFipsJvm && pluginName == "ingest-attachment"
 
-  // This plugin has to be configured to work via system properties
-  def ignoreQuotaWareFs = pluginName == 'quota-aware-fs'
-
-  return ignoreIngestAttachment == false && ignoreQuotaWareFs == false
+  return ignoreIngestAttachment == false
 }.collect {pluginName, pluginProject -> pluginProject.path }
 
 ext.expansions = [

+ 0 - 8
x-pack/quota-aware-fs/build.gradle

@@ -1,8 +0,0 @@
-apply plugin: 'elasticsearch.internal-es-plugin'
-
-esplugin {
-  description 'A bootstrap plugin that adds support for interfacing with filesystem that enforce user quotas.'
-  type = 'bootstrap'
-  javaOpts = '-Djava.nio.file.spi.DefaultFileSystemProvider=org.elasticsearch.fs.quotaaware.QuotaAwareFileSystemProvider'
-  licensed true
-}

+ 0 - 87
x-pack/quota-aware-fs/src/main/java/org/elasticsearch/fs/quotaaware/QuotaAwareFileStore.java

@@ -1,87 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-package org.elasticsearch.fs.quotaaware;
-
-import java.io.IOException;
-import java.nio.file.FileStore;
-import java.nio.file.attribute.FileAttributeView;
-import java.nio.file.attribute.FileStoreAttributeView;
-
-/**
- * An implementation of {@link FileStore} that relies on
- * {@link QuotaAwareFileSystemProvider} for usage reporting. Other methods are
- * delegated to the backing instance of {@link FileStore}
- */
-public final class QuotaAwareFileStore extends FileStore {
-
-    private final FileStore backingFS;
-    private final QuotaAwareFileSystemProvider provider;
-
-    QuotaAwareFileStore(QuotaAwareFileSystemProvider provider, FileStore backingFS) {
-        this.provider = provider;
-        this.backingFS = backingFS;
-    }
-
-    @Override
-    public String name() {
-        return backingFS.name();
-    }
-
-    @Override
-    public String type() {
-        return backingFS.type();
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return backingFS.isReadOnly();
-    }
-
-    @Override
-    public long getTotalSpace() throws IOException {
-        return Math.min(provider.getTotal(), backingFS.getTotalSpace());
-    }
-
-    @Override
-    public long getUsableSpace() throws IOException {
-        return Math.min(provider.getRemaining(), backingFS.getUsableSpace());
-    }
-
-    @Override
-    public long getUnallocatedSpace() throws IOException {
-        // There is no point in telling users that the underlying
-        // host has more capacity than what they're allowed to use so we limit
-        // this one with remaining as well.
-        return Math.min(provider.getRemaining(), backingFS.getUnallocatedSpace());
-    }
-
-    @Override
-    public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
-        return backingFS.supportsFileAttributeView(type);
-    }
-
-    @Override
-    public boolean supportsFileAttributeView(String name) {
-        return backingFS.supportsFileAttributeView(name);
-    }
-
-    @Override
-    public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
-        return backingFS.getFileStoreAttributeView(type);
-    }
-
-    @Override
-    public Object getAttribute(String attribute) throws IOException {
-        return backingFS.getAttribute(attribute);
-    }
-
-    @Override
-    public String toString() {
-        return "QuotaAwareFileStore(" + backingFS.toString() + ")";
-    }
-}

+ 0 - 132
x-pack/quota-aware-fs/src/main/java/org/elasticsearch/fs/quotaaware/QuotaAwareFileSystem.java

@@ -1,132 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-package org.elasticsearch.fs.quotaaware;
-
-import org.elasticsearch.core.SuppressForbidden;
-
-import java.io.IOException;
-import java.nio.file.FileStore;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Path;
-import java.nio.file.PathMatcher;
-import java.nio.file.WatchService;
-import java.nio.file.attribute.UserPrincipalLookupService;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.StreamSupport;
-
-/**
- * An implementation of {@link FileSystem} that returns the given
- * {@link QuotaAwareFileSystemProvider} provider {@link #provider()}.
- *
- * Other methods are delegated to given instance of {@link FileSystem} and
- * wrapped where result types are either @link {@link QuotaAwareFileSystem}
- * or @link {@link QuotaAwareFileStore}.
- *
- */
-public final class QuotaAwareFileSystem extends FileSystem {
-    private final FileSystem delegate;
-    private final QuotaAwareFileSystemProvider provider;
-
-    QuotaAwareFileSystem(QuotaAwareFileSystemProvider provider, FileSystem delegate) {
-        this.provider = Objects.requireNonNull(provider, "Provider is required");
-        this.delegate = Objects.requireNonNull(delegate, "FileSystem is required");
-    }
-
-    @Override
-    public QuotaAwareFileSystemProvider provider() {
-        return provider;
-    }
-
-    @Override
-    @SuppressForbidden(reason = "accesses the default filesystem by design")
-    public void close() throws IOException {
-        if (this == FileSystems.getDefault()) {
-            throw new UnsupportedOperationException("The default file system cannot be closed");
-        } else if (delegate != FileSystems.getDefault()) {
-            delegate.close();
-        }
-        provider.purge(delegate);
-    }
-
-    @Override
-    public boolean isOpen() {
-        return delegate.isOpen();
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return delegate.isReadOnly();
-    }
-
-    @Override
-    public String getSeparator() {
-        return delegate.getSeparator();
-    }
-
-    @Override
-    public Iterable<Path> getRootDirectories() {
-        return StreamSupport.stream(delegate.getRootDirectories().spliterator(), false).map((Function<Path, Path>) this::wrap)::iterator;
-    }
-
-    @Override
-    public Iterable<FileStore> getFileStores() {
-        return StreamSupport.stream(delegate.getFileStores().spliterator(), false)
-            .map((Function<FileStore, FileStore>) provider::getFileStore)::iterator;
-    }
-
-    @Override
-    public Set<String> supportedFileAttributeViews() {
-        return delegate.supportedFileAttributeViews();
-    }
-
-    @Override
-    public Path getPath(String first, String... more) {
-        return wrap(delegate.getPath(first, more));
-    }
-
-    private QuotaAwarePath wrap(Path delegatePath) {
-        if (delegatePath == null) return null;
-        else return new QuotaAwarePath(this, delegatePath);
-    }
-
-    @Override
-    public PathMatcher getPathMatcher(String syntaxAndPattern) {
-        PathMatcher matcher = delegate.getPathMatcher(syntaxAndPattern);
-        return (path) -> matcher.matches(QuotaAwarePath.unwrap(path));
-    }
-
-    @Override
-    public UserPrincipalLookupService getUserPrincipalLookupService() {
-        return delegate.getUserPrincipalLookupService();
-    }
-
-    @Override
-    public WatchService newWatchService() throws IOException {
-        return delegate.newWatchService();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        QuotaAwareFileSystem other = (QuotaAwareFileSystem) obj;
-        if (delegate.equals(other.delegate) == false) return false;
-        if (provider.equals(other.provider) == false) return false;
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return delegate.hashCode();
-    }
-
-}

+ 0 - 423
x-pack/quota-aware-fs/src/main/java/org/elasticsearch/fs/quotaaware/QuotaAwareFileSystemProvider.java

@@ -1,423 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-package org.elasticsearch.fs.quotaaware;
-
-import org.elasticsearch.core.internal.io.IOUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.nio.channels.AsynchronousFileChannel;
-import java.nio.channels.FileChannel;
-import java.nio.channels.SeekableByteChannel;
-import java.nio.file.AccessMode;
-import java.nio.file.CopyOption;
-import java.nio.file.DirectoryStream;
-import java.nio.file.DirectoryStream.Filter;
-import java.nio.file.FileStore;
-import java.nio.file.FileSystem;
-import java.nio.file.LinkOption;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.ProviderMismatchException;
-import java.nio.file.StandardOpenOption;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.attribute.FileAttribute;
-import java.nio.file.attribute.FileAttributeView;
-import java.nio.file.spi.FileSystemProvider;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.StreamSupport;
-
-/**
- * QuotaAwareFileSystemProvider is intended to be used as a wrapper around the
- * default file system provider of the JVM.
- *
- * It augments the default file system by allowing to report total size and
- * remaining capacity according to some externally defined quota that is not
- * readily available to the JVM. Essentially it is a workaround for containers
- * that see the size of the host volume and not the effective quota available to
- * them. It will however never report larger capacity than what the underlying
- * file system sees.
- *
- * In application usage:
- * <ol>
- * <li>Include this project in the class path</li>
- * <li>Specify this argument at JVM boot:
- * <code>-Djava.nio.file.spi.DefaultFileSystemProvider=co.elastic.cloud.quotaawarefs.QuotaAwareFileSystemProvider</code>
- * </li>
- * <li>Have some external system check the quota usage and
- * write it to the path specified by the system property {@value #QUOTA_PATH_KEY}.</li>
- * </ol>
- *
- * In any case the quota file must be a {@link Properties} file with the
- * properties <code>total</code> and <code>remaining</code>. Both properties are
- * {@link Long} values, parsed according to {@link Long#parseLong(String)} and
- * assumed to be in bytes.
- *
- * Sample format:
- *
- * <pre>
- * {@code
- * total=5000
- * remaining=200
- * }
- * </pre>
- */
-public class QuotaAwareFileSystemProvider extends FileSystemProvider implements AutoCloseable {
-
-    private static final int CHECK_PERIOD = 1000;
-
-    private final class RefreshLimitsTask extends TimerTask {
-        @Override
-        public void run() {
-            try {
-                assert timerThread == Thread.currentThread() : "QuotaAwareFileSystemProvider doesn't support multithreaded timer.";
-                refreshLimits();
-            } catch (Exception e) {
-                // Canceling from the timer Thread guarantees last execution,
-                // so no need to check for duplicate error
-                error.set(e);
-                timer.cancel();
-            }
-        }
-
-    }
-
-    static final String QUOTA_PATH_KEY = "es.fs.quota.file";
-
-    private final FileSystemProvider delegate;
-    private final Path configPath;
-
-    private volatile long total = Long.MAX_VALUE;
-    private volatile long remaining = Long.MAX_VALUE;
-
-    private final Timer timer;
-
-    private final ConcurrentHashMap<FileSystem, QuotaAwareFileSystem> systemsCache = new ConcurrentHashMap<>();
-    private final ConcurrentHashMap<FileStore, QuotaAwareFileStore> storesCache = new ConcurrentHashMap<>();
-    private final AtomicBoolean closed = new AtomicBoolean(false);
-    private final AtomicReference<Throwable> error = new AtomicReference<>();
-
-    private static final AtomicInteger NEXT_SERIAL = new AtomicInteger(0);
-    private final String timerThreadName = "QuotaAwareFSTimer-" + NEXT_SERIAL.getAndIncrement();
-    private final Thread timerThread;
-
-    public QuotaAwareFileSystemProvider(FileSystemProvider delegate) throws Exception {
-        this(delegate, URI.create(getUri()));
-    }
-
-    private static String getUri() {
-        final String property = System.getProperty(QUOTA_PATH_KEY);
-        if (property == null) {
-            throw new IllegalArgumentException(
-                "Property "
-                    + QUOTA_PATH_KEY
-                    + " must be set to a URI in order to use the quota filesystem provider, e.g. using -D"
-                    + QUOTA_PATH_KEY
-                    + "=file://path/to/fsquota.properties"
-            );
-        }
-
-        return property;
-    }
-
-    public QuotaAwareFileSystemProvider(FileSystemProvider delegate, URI config) throws Exception {
-        if (delegate instanceof QuotaAwareFileSystemProvider) {
-            throw new IllegalArgumentException("Delegate provider cannot be an instance of QuotaAwareFileSystemProvider");
-        }
-        this.delegate = delegate;
-        configPath = delegate.getPath(config);
-        refreshLimits(); // Ensures that a parseable file exists before
-                         // timer is created
-        timer = new Timer(timerThreadName, true);
-        try {
-            timerThread = getThreadFromTimer(timer).get();
-            timer.schedule(new RefreshLimitsTask(), CHECK_PERIOD, CHECK_PERIOD);
-        } catch (Exception e1) {
-            if (e1 instanceof InterruptedException) {
-                Thread.currentThread().interrupt(); // Restore interrupted flag
-            }
-            try {
-                // Avoid thread leak if this failed to start
-                timer.cancel();
-            } catch (Exception e2) {
-                e1.addSuppressed(e2);
-            }
-            throw e1;
-        }
-    }
-
-    /**
-     * Extracts the {@link Thread} used by a {@link Timer}.
-     *
-     * Ideally {@link Timer} would provide necessary health checks or error
-     * handling support that this would not be required.
-     *
-     * @param timer
-     *            The {@link Timer} instance to extract thread from
-     * @return the Thread used by the given timer
-     * @throws IllegalStateException
-     *             if Timer is cancelled.
-     */
-    private static Future<Thread> getThreadFromTimer(Timer timer) {
-        FutureTask<Thread> timerThreadFuture = new FutureTask<>(() -> Thread.currentThread());
-        timer.schedule(new TimerTask() {
-            @Override
-            public void run() {
-                timerThreadFuture.run();
-            }
-        }, 0);
-        return timerThreadFuture;
-    }
-
-    /**
-     * Performs a single attempt at reading the required files.
-     *
-     * @throws PrivilegedActionException if something goes wrong
-     * @throws IOException if something goes wrong
-     *
-     * @throws IllegalStateException
-     *             if security manager denies reading the property file
-     * @throws IllegalStateException
-     *             if the property file cannot be parsed
-     */
-    private void refreshLimits() throws IOException, PrivilegedActionException {
-        try (InputStream newInputStream = AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() {
-            @Override
-            public InputStream run() throws Exception {
-                return delegate.newInputStream(configPath, StandardOpenOption.READ);
-            }
-        })) {
-            Properties properties = new Properties();
-            properties.load(newInputStream);
-            total = Long.parseLong(properties.getProperty("total"));
-            remaining = Long.parseLong(properties.getProperty("remaining"));
-        }
-    }
-
-    @Override
-    public String getScheme() {
-        return "file";
-    }
-
-    @Override
-    public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
-        // Delegate handles throwing exception if filesystem already exists
-        return getFS(delegate.newFileSystem(uri, env));
-
-    }
-
-    private QuotaAwareFileSystem getFS(FileSystem fileSystem) {
-        return systemsCache.computeIfAbsent(fileSystem, (delegate) -> new QuotaAwareFileSystem(this, delegate));
-    }
-
-    @Override
-    public FileSystem getFileSystem(URI uri) {
-        // Delegate handles throwing exception if filesystem doesn't exist, but
-        // delegate may also have precreated filesystems, like the default file
-        // system.
-        return getFS(delegate.getFileSystem(uri));
-    }
-
-    @Override
-    public Path getPath(URI uri) {
-        return ensureWrapped(delegate.getPath(uri));
-    }
-
-    private Path ensureWrapped(Path path) {
-        if (path instanceof QuotaAwarePath) {
-            assert path.getFileSystem().provider() == this;
-            // Delegate may use this instance to create the path when this
-            // instance is installed as the default provider.
-            // This is safe because nested QuotaAwareProviders are prohibited,
-            // otherwise this would require unwrapping.
-            return path;
-        } else {
-            return new QuotaAwarePath(getFS(path.getFileSystem()), path);
-        }
-    }
-
-    @Override
-    public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
-        return delegate.newByteChannel(QuotaAwarePath.unwrap(path), options, attrs);
-    }
-
-    @Override
-    public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException {
-        return new DirectoryStream<Path>() {
-            DirectoryStream<Path> stream = delegate.newDirectoryStream(QuotaAwarePath.unwrap(dir), filter);
-
-            @Override
-            public void close() throws IOException {
-                stream.close();
-            }
-
-            @Override
-            public Iterator<Path> iterator() {
-                return StreamSupport.stream(stream.spliterator(), false).map(QuotaAwareFileSystemProvider.this::ensureWrapped).iterator();
-            }
-        };
-    }
-
-    @Override
-    public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
-        delegate.createDirectory(QuotaAwarePath.unwrap(dir), attrs);
-    }
-
-    @Override
-    public void delete(Path path) throws IOException {
-        delegate.delete(QuotaAwarePath.unwrap(path));
-    }
-
-    @Override
-    public void copy(Path source, Path target, CopyOption... options) throws IOException {
-        delegate.copy(QuotaAwarePath.unwrap(source), QuotaAwarePath.unwrap(target), options);
-    }
-
-    @Override
-    public void move(Path source, Path target, CopyOption... options) throws IOException {
-        delegate.move(QuotaAwarePath.unwrap(source), QuotaAwarePath.unwrap(target), options);
-    }
-
-    @Override
-    public boolean isSameFile(Path path, Path path2) throws IOException {
-        return delegate.isSameFile(QuotaAwarePath.unwrap(path), QuotaAwarePath.unwrap(path2));
-    }
-
-    @Override
-    public boolean isHidden(Path path) throws IOException {
-        return delegate.isHidden(QuotaAwarePath.unwrap(path));
-    }
-
-    @Override
-    public QuotaAwareFileStore getFileStore(Path path) throws IOException {
-        return getFileStore(delegate.getFileStore(QuotaAwarePath.unwrap(path)));
-    }
-
-    QuotaAwareFileStore getFileStore(FileStore store) {
-        return storesCache.computeIfAbsent(store, (fs) -> new QuotaAwareFileStore(QuotaAwareFileSystemProvider.this, fs));
-    }
-
-    @Override
-    public void checkAccess(Path path, AccessMode... modes) throws IOException {
-        delegate.checkAccess(QuotaAwarePath.unwrap(path), modes);
-    }
-
-    @Override
-    public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
-        return delegate.getFileAttributeView(QuotaAwarePath.unwrap(path), type, options);
-    }
-
-    @Override
-    public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException {
-        try {
-            return delegate.readAttributes(QuotaAwarePath.unwrap(path), type, options);
-        } catch (ProviderMismatchException e) {
-            throw new IllegalArgumentException("Failed to read attributes for path: [" + path + "]", e);
-        }
-    }
-
-    @Override
-    public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
-        return delegate.readAttributes(QuotaAwarePath.unwrap(path), attributes, options);
-    }
-
-    @Override
-    public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
-        delegate.setAttribute(QuotaAwarePath.unwrap(path), attribute, value, options);
-    }
-
-    long getTotal() {
-        ensureHealth();
-        return total;
-    }
-
-    void ensureHealth() throws AssertionError {
-        boolean timerIsAlive = timerThread.isAlive();
-        Throwable cause = error.get();
-        if (cause != null || timerIsAlive == false) {
-            throw new AssertionError("The quota aware filesystem has failed", cause);
-        }
-    }
-
-    long getRemaining() {
-        ensureHealth();
-        return remaining;
-    }
-
-    @Override
-    public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
-        return delegate.newFileChannel(QuotaAwarePath.unwrap(path), options, attrs);
-    }
-
-    @Override
-    public AsynchronousFileChannel newAsynchronousFileChannel(
-        Path path,
-        Set<? extends OpenOption> options,
-        ExecutorService executor,
-        FileAttribute<?>... attrs
-    ) throws IOException {
-        return delegate.newAsynchronousFileChannel(QuotaAwarePath.unwrap(path), options, executor, attrs);
-    }
-
-    /**
-     * Normally only used in testing. Avoids thread leak when life cycle of this
-     * object doesn't follow that of the JVM.
-     *
-     * @throws IOException if something goes wrong
-     */
-    @Override
-    public void close() throws IOException {
-        if (closed.compareAndSet(false, true)) {
-            timer.cancel();
-
-            // If there was a currently executing task wait for it, to
-            // avoid false positives on file handle leak.
-            try {
-                timerThread.join();
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt(); // Restore interrupted flag
-            }
-
-            try {
-                IOUtils.close(systemsCache.values());
-            } finally {
-                storesCache.clear();
-            }
-        }
-    }
-
-    @Override
-    public void createLink(Path link, Path existing) throws IOException {
-        delegate.createLink(QuotaAwarePath.unwrap(link), QuotaAwarePath.unwrap(existing));
-    }
-
-    @Override
-    public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
-        delegate.createSymbolicLink(QuotaAwarePath.unwrap(link), QuotaAwarePath.unwrap(target), attrs);
-    }
-
-    void purge(FileSystem delegateFileSystem) {
-        systemsCache.remove(delegateFileSystem);
-    }
-}

+ 0 - 218
x-pack/quota-aware-fs/src/main/java/org/elasticsearch/fs/quotaaware/QuotaAwarePath.java

@@ -1,218 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-package org.elasticsearch.fs.quotaaware;
-
-import java.io.IOException;
-import java.net.URI;
-import java.nio.file.FileSystem;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.ProviderMismatchException;
-import java.nio.file.WatchEvent.Kind;
-import java.nio.file.WatchEvent.Modifier;
-import java.nio.file.WatchKey;
-import java.nio.file.WatchService;
-import java.util.Iterator;
-import java.util.Objects;
-import java.util.stream.StreamSupport;
-
-/**
- * A wrapper implementation of {@link Path} that ensures the given
- * {@link QuotaAwareFileSystem} is returned in {@link #getFileSystem()} from
- * this instance and any other {@link Path} returned from this.
- *
- */
-public final class QuotaAwarePath implements Path {
-
-    private final QuotaAwareFileSystem fileSystem;
-    private final Path delegate;
-
-    QuotaAwarePath(QuotaAwareFileSystem fileSystem, Path delegate) {
-        if (delegate instanceof QuotaAwarePath) {
-            throw new IllegalArgumentException("Nested quota wrappers are not supported.");
-        }
-        this.fileSystem = Objects.requireNonNull(fileSystem, "A filesystem is required");
-        this.delegate = Objects.requireNonNull(delegate, "A delegate path is required");
-    }
-
-    @Override
-    public FileSystem getFileSystem() {
-        return fileSystem;
-    }
-
-    @Override
-    public boolean isAbsolute() {
-        return delegate.isAbsolute();
-    }
-
-    @Override
-    public Path getRoot() {
-        return wrap(delegate.getRoot());
-    }
-
-    @Override
-    public Path getFileName() {
-        return wrap(delegate.getFileName());
-    }
-
-    private Path wrap(Path delegate) {
-        if (delegate == null) {
-            return null;
-        } else {
-            return new QuotaAwarePath(fileSystem, delegate);
-        }
-    }
-
-    static Path unwrap(Path path) {
-        if (path instanceof QuotaAwarePath) {
-            return ((QuotaAwarePath) path).delegate;
-        } else {
-            return path;
-        }
-    }
-
-    @Override
-    public Path getParent() {
-        return wrap(delegate.getParent());
-    }
-
-    @Override
-    public int getNameCount() {
-        return delegate.getNameCount();
-    }
-
-    @Override
-    public Path getName(int index) {
-        return wrap(delegate.getName(index));
-    }
-
-    @Override
-    public Path subpath(int beginIndex, int endIndex) {
-        return wrap(delegate.subpath(beginIndex, endIndex));
-    }
-
-    @Override
-    public boolean startsWith(Path other) {
-        return delegate.startsWith(unwrap(other));
-    }
-
-    @Override
-    public boolean startsWith(String other) {
-        return delegate.startsWith(other);
-    }
-
-    @Override
-    public boolean endsWith(Path other) {
-        return delegate.endsWith(unwrap(other));
-    }
-
-    @Override
-    public boolean endsWith(String other) {
-        return delegate.endsWith(other);
-    }
-
-    @Override
-    public Path normalize() {
-        return wrap(delegate.normalize());
-    }
-
-    @Override
-    public Path resolve(Path other) {
-        return wrap(delegate.resolve(unwrap(other)));
-    }
-
-    @Override
-    public Path resolve(String other) {
-        return wrap(delegate.resolve(other));
-    }
-
-    @Override
-    public Path resolveSibling(Path other) {
-        return wrap(delegate.resolveSibling(unwrap(other)));
-    }
-
-    @Override
-    public Path resolveSibling(String other) {
-        return wrap(delegate.resolveSibling(other));
-    }
-
-    @Override
-    public Path relativize(Path other) {
-        return wrap(delegate.relativize(unwrap(other)));
-    }
-
-    @Override
-    public URI toUri() {
-        return delegate.toUri();
-    }
-
-    @Override
-    public Path toAbsolutePath() {
-        return wrap(delegate.toAbsolutePath());
-    }
-
-    @Override
-    public Path toRealPath(LinkOption... options) throws IOException {
-        return wrap(delegate.toRealPath(options));
-    }
-
-    @Override
-    public WatchKey register(WatchService watcher, Kind<?>[] events, Modifier... modifiers) throws IOException {
-        return delegate.register(watcher, events, modifiers);
-    }
-
-    @Override
-    public WatchKey register(WatchService watcher, Kind<?>... events) throws IOException {
-        return delegate.register(watcher, events);
-    }
-
-    @Override
-    public Iterator<Path> iterator() {
-        return StreamSupport.stream(delegate.spliterator(), false).map(this::wrap).iterator();
-    }
-
-    @Override
-    public int compareTo(Path other) {
-        return delegate.compareTo(toDelegate(other));
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        QuotaAwarePath other = (QuotaAwarePath) obj;
-        if (delegate.equals(other.delegate) == false) return false;
-        if (fileSystem.equals(other.fileSystem) == false) return false;
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return delegate.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return delegate.toString();
-    }
-
-    private Path toDelegate(Path path) {
-        if (path instanceof QuotaAwarePath) {
-            QuotaAwarePath qaPath = (QuotaAwarePath) path;
-            if (qaPath.fileSystem != fileSystem) {
-                throw new ProviderMismatchException(
-                    "mismatch, expected: " + fileSystem.provider().getClass() + ", got: " + qaPath.fileSystem.provider().getClass()
-                );
-            }
-            return qaPath.delegate;
-        } else {
-            throw new ProviderMismatchException("mismatch, expected: QuotaAwarePath, got: " + path.getClass());
-        }
-    }
-}

+ 0 - 135
x-pack/quota-aware-fs/src/test/java/org/elasticsearch/fs/quotaaware/DelegatingProvider.java

@@ -1,135 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-package org.elasticsearch.fs.quotaaware;
-
-import java.io.IOException;
-import java.net.URI;
-import java.nio.channels.SeekableByteChannel;
-import java.nio.file.AccessMode;
-import java.nio.file.CopyOption;
-import java.nio.file.DirectoryStream;
-import java.nio.file.FileStore;
-import java.nio.file.FileSystem;
-import java.nio.file.LinkOption;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.attribute.FileAttribute;
-import java.nio.file.attribute.FileAttributeView;
-import java.nio.file.spi.FileSystemProvider;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A simple purely delegating provider, allows tests to only override the
- * methods they need custom behaviour on.
- */
-class DelegatingProvider extends FileSystemProvider {
-    private final FileSystemProvider provider;
-
-    /**
-     * An optional field for subclasses that need to test cyclic references
-     */
-    protected FileSystemProvider cyclicReference;
-
-    DelegatingProvider(FileSystemProvider provider) {
-        this.provider = provider;
-        this.cyclicReference = provider;
-    }
-
-    @Override
-    public String getScheme() {
-        return provider.getScheme();
-    }
-
-    @Override
-    public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
-        return provider.newFileSystem(uri, env);
-    }
-
-    @Override
-    public FileSystem getFileSystem(URI uri) {
-        return provider.getFileSystem(uri);
-    }
-
-    @Override
-    public Path getPath(URI uri) {
-        return provider.getPath(uri);
-    }
-
-    @Override
-    public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
-        return provider.newByteChannel(path, options, attrs);
-    }
-
-    @Override
-    public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
-        return provider.newDirectoryStream(dir, filter);
-    }
-
-    @Override
-    public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
-        provider.createDirectory(dir, attrs);
-    }
-
-    @Override
-    public void delete(Path path) throws IOException {
-        provider.delete(path);
-    }
-
-    @Override
-    public void copy(Path source, Path target, CopyOption... options) throws IOException {
-        provider.copy(source, target, options);
-    }
-
-    @Override
-    public void move(Path source, Path target, CopyOption... options) throws IOException {
-        provider.move(source, target, options);
-    }
-
-    @Override
-    public boolean isSameFile(Path path, Path path2) throws IOException {
-        return provider.isSameFile(path, path2);
-    }
-
-    @Override
-    public boolean isHidden(Path path) throws IOException {
-        return provider.isHidden(path);
-    }
-
-    @Override
-    public FileStore getFileStore(Path path) throws IOException {
-        return provider.getFileStore(path);
-    }
-
-    @Override
-    public void checkAccess(Path path, AccessMode... modes) throws IOException {
-        provider.checkAccess(path, modes);
-    }
-
-    @Override
-    public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
-        return provider.getFileAttributeView(path, type, options);
-    }
-
-    @Override
-    public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException {
-        return provider.readAttributes(path, type, options);
-    }
-
-    @Override
-    public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
-        return provider.readAttributes(path, attributes, options);
-    }
-
-    @Override
-    public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
-        provider.setAttribute(path, attribute, value, options);
-    }
-
-}

+ 0 - 369
x-pack/quota-aware-fs/src/test/java/org/elasticsearch/fs/quotaaware/QuotaAwareFileSystemProviderTests.java

@@ -1,369 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-package org.elasticsearch.fs.quotaaware;
-
-import org.apache.lucene.util.LuceneTestCase;
-import org.apache.lucene.util.TestRuleLimitSysouts.Limit;
-import org.elasticsearch.core.SuppressForbidden;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.FileStore;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileAttribute;
-import java.nio.file.spi.FileSystemProvider;
-import java.security.PrivilegedActionException;
-import java.util.Properties;
-import java.util.Random;
-
-import static java.nio.file.StandardOpenOption.CREATE;
-import static java.nio.file.StandardOpenOption.CREATE_NEW;
-import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
-import static java.nio.file.StandardOpenOption.WRITE;
-import static org.elasticsearch.fs.quotaaware.QuotaAwareFileSystemProvider.QUOTA_PATH_KEY;
-import static org.hamcrest.Matchers.startsWith;
-
-@Limit(bytes = 10000)
-@SuppressForbidden(reason = "accesses the default filesystem by design")
-public class QuotaAwareFileSystemProviderTests extends LuceneTestCase {
-
-    public void testSystemPropertyShouldBeSet() {
-        FileSystemProvider systemProvider = FileSystems.getDefault().provider();
-        System.clearProperty(QUOTA_PATH_KEY);
-
-        final IllegalArgumentException exception = expectThrows(
-            IllegalArgumentException.class,
-            () -> new QuotaAwareFileSystemProvider(systemProvider)
-        );
-
-        assertThat(exception.getMessage(), startsWith("Property " + QUOTA_PATH_KEY + " must be set to a URI"));
-    }
-
-    public void testInitiallyNoQuotaFile() throws Exception {
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = FileSystems.getDefault().provider();
-        Throwable cause = null;
-
-        try (QuotaAwareFileSystemProvider ignored = new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri())) {
-            fail(); //
-        } catch (PrivilegedActionException e) {
-            cause = e.getCause();
-        }
-        assertTrue("Should be FileNotFoundException", cause instanceof NoSuchFileException);
-    }
-
-    public void testBasicQuotaFile() throws Exception {
-        doValidFileTest(500, 200);
-    }
-
-    public void testUpdateQuotaFile() throws Exception {
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        writeQuota(500L, 200L, systemProvider, quotaFile);
-
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri())) {
-            assertEquals(500, provider.getTotal());
-            assertEquals(200, provider.getRemaining());
-            writeQuota(450, 150, systemProvider, quotaFile);
-            withRetry(2000, 500, () -> {
-                assertEquals(450, provider.getTotal());
-                assertEquals(150, provider.getRemaining());
-            });
-        }
-    }
-
-    @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/70680")
-    public void testRepeatedUpdate() throws Exception {
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        writeQuota(500L, 200L, systemProvider, quotaFile);
-        Random random = new Random();
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri())) {
-            for (int i = 0; i < 10; i++) {
-                long expectedTotal = Math.abs(random.nextLong());
-                long expectedRemaining = Math.abs(random.nextLong());
-                writeQuota(expectedTotal, expectedRemaining, systemProvider, quotaFile);
-                withRetry(2000, 100, () -> {
-                    assertEquals(expectedTotal, provider.getTotal());
-                    assertEquals(expectedRemaining, provider.getRemaining());
-                });
-            }
-        }
-    }
-
-    public void testEventuallyMissingQuotaFile() throws Exception {
-
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        writeQuota(500L, 200L, systemProvider, quotaFile);
-
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri())) {
-            assertEquals(500, provider.getTotal());
-            assertEquals(200, provider.getRemaining());
-
-            systemProvider.delete(quotaFile);
-
-            withRetry(2000, 500, () -> {
-                boolean gotError = false;
-                try {
-                    provider.getTotal();
-                } catch (AssertionError e) {
-                    gotError = true;
-                }
-                assertTrue(gotError);
-            });
-        }
-    }
-
-    public void testEventuallyMalformedQuotaFile() throws Exception {
-
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        writeQuota(500L, 200L, systemProvider, quotaFile);
-
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri())) {
-            assertEquals(500, provider.getTotal());
-            assertEquals(200, provider.getRemaining());
-
-            try (
-                OutputStream stream = systemProvider.newOutputStream(quotaFile, WRITE, TRUNCATE_EXISTING);
-                OutputStreamWriter streamWriter = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
-                PrintWriter printWriter = new PrintWriter(streamWriter)
-            ) {
-                printWriter.write("This is not valid properties file syntax");
-            }
-
-            withRetry(2000, 500, () -> {
-                boolean gotError = false;
-                try {
-                    provider.getTotal();
-                } catch (AssertionError e) {
-                    gotError = true;
-                }
-                assertTrue(gotError);
-            });
-        }
-    }
-
-    public void testHighQuotaFile() throws Exception {
-        doValidFileTest(Long.MAX_VALUE - 1L, Long.MAX_VALUE - 2L);
-    }
-
-    public void testMalformedNumberInQuotaFile() throws Exception {
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        Properties quota = new Properties();
-        quota.setProperty("total", "ThisNotANumber");
-        quota.setProperty("remaining", "1");
-        try (OutputStream stream = systemProvider.newOutputStream(quotaFile, WRITE, CREATE_NEW)) {
-            quota.store(stream, "QuotaFile for: QuotaAwareFileSystemProviderTest#malformedNumberInQuotaFile");
-        }
-
-        expectThrows(NumberFormatException.class, () -> new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri()));
-    }
-
-    public void testMalformedQuotaFile() throws Exception {
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-
-        try (
-            OutputStream stream = systemProvider.newOutputStream(quotaFile, WRITE, CREATE_NEW);
-            OutputStreamWriter streamWriter = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
-            PrintWriter printWriter = new PrintWriter(streamWriter)
-        ) {
-            printWriter.write("This is not valid properties file syntax");
-        }
-
-        expectThrows(Exception.class, () -> new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri()));
-    }
-
-    public void testFileStoreLimited() throws Exception {
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        Properties quota = new Properties();
-        long expectedTotal = 500;
-        long expectedRemaining = 200;
-        quota.setProperty("total", Long.toString(expectedTotal));
-        quota.setProperty("remaining", Long.toString(expectedRemaining));
-        try (OutputStream stream = systemProvider.newOutputStream(quotaFile, WRITE, CREATE_NEW)) {
-            quota.store(stream, "QuotaFile for: QuotaAwareFileSystemProviderTest#fileStoreLimited");
-        }
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri())) {
-            Path path = createTempFile();
-            FileStore fileStore = provider.getFileStore(path);
-            assertEquals(expectedTotal, fileStore.getTotalSpace());
-            assertEquals(expectedRemaining, fileStore.getUsableSpace());
-            assertEquals(expectedRemaining, fileStore.getUnallocatedSpace());
-        }
-    }
-
-    public void testFileStoreNotLimited() throws Exception {
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        DelegatingProvider snapshotProvider = new SnapshotFilesystemProvider(quotaFile.getFileSystem().provider());
-
-        Properties quota = new Properties();
-        quota.setProperty("total", Long.toString(Long.MAX_VALUE));
-        quota.setProperty("remaining", Long.toString(Long.MAX_VALUE));
-
-        try (OutputStream stream = snapshotProvider.newOutputStream(quotaFile, WRITE, CREATE_NEW)) {
-            quota.store(stream, "QuotaFile for: QuotaAwareFileSystemProviderTest#fileStoreNotLimited");
-        }
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(snapshotProvider, quotaFile.toUri())) {
-            Path path = createTempFile();
-            FileStore fileStore = provider.getFileStore(path);
-            FileStore unLimitedStore = snapshotProvider.getFileStore(path);
-            assertEquals(unLimitedStore.getTotalSpace(), fileStore.getTotalSpace());
-            assertEquals(unLimitedStore.getUsableSpace(), fileStore.getUsableSpace());
-            assertEquals(unLimitedStore.getUnallocatedSpace(), fileStore.getUnallocatedSpace());
-        }
-    }
-
-    public void testDefaultFilesystemIsPreinitialized() throws Exception {
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        Properties quota = new Properties();
-        quota.setProperty("total", Long.toString(Long.MAX_VALUE));
-        quota.setProperty("remaining", Long.toString(Long.MAX_VALUE));
-        try (OutputStream stream = systemProvider.newOutputStream(quotaFile, WRITE, CREATE_NEW)) {
-            quota.store(stream, "QuotaFile for: QuotaAwareFileSystemProviderTest#defaultFilesystemIsPreinitialized");
-        }
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri())) {
-            assertNotNull(provider.getFileSystem(new URI("file:///")));
-        }
-    }
-
-    /**
-     * Mimics a cyclic reference that may happen when
-     * {@link QuotaAwareFileSystemProvider} is installed as the default provider
-     * in the JVM and the delegate provider references
-     * {@link FileSystems#getDefault()} by for instance relying on
-     * {@link File#toPath()}
-     */
-    public void testHandleReflexiveDelegate() throws Exception {
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        DelegatingProvider cyclicProvider = new DelegatingProvider(systemProvider) {
-            @Override
-            @SuppressForbidden(reason = "Uses new File() to work around test issue on Windows")
-            public Path getPath(URI uri) {
-                try {
-                    // This convoluted line is necessary in order to get a valid path on Windows.
-                    final String uriPath = new File(uri.getPath()).toPath().toString();
-                    return cyclicReference.getFileSystem(new URI("file:///")).getPath(uriPath);
-                } catch (URISyntaxException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        };
-
-        Properties quota = new Properties();
-        quota.setProperty("total", Long.toString(Long.MAX_VALUE));
-        quota.setProperty("remaining", Long.toString(Long.MAX_VALUE));
-        try (OutputStream stream = systemProvider.newOutputStream(quotaFile, WRITE, CREATE_NEW)) {
-            quota.store(stream, "QuotaFile for: QuotaAwareFileSystemProviderTest#testHandleReflexiveDelegate");
-        }
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(cyclicProvider, quotaFile.toUri())) {
-            cyclicProvider.cyclicReference = provider;
-            assertNotNull(provider.getPath(new URI("file:///")));
-        }
-    }
-
-    /**
-     * If the implementation of {@link QuotaAwareFileSystemProvider#createLink(Path, Path)}
-     * doesn't unwrap its {@link Path} arguments, it causes a runtime exception, so exercise
-     * this method to check that it unwraps correctly.
-     */
-    public void testCreateLinkUnwrapsPaths() throws Exception {
-        final Path tempDir = createTempDir();
-        Path quotaFile = tempDir.resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        writeQuota(500, 200, systemProvider, quotaFile);
-
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri())) {
-            final Path quotaFilePath = provider.getPath(tempDir.resolve("path1.txt").toUri());
-            final Path quotaLinkPath = provider.getPath(tempDir.resolve("path2.txt").toUri());
-
-            Files.writeString(quotaFilePath, "some text");
-
-            provider.createLink(quotaLinkPath, quotaFilePath);
-        }
-    }
-
-    /**
-     * If the implementation of {@link QuotaAwareFileSystemProvider#createSymbolicLink(Path, Path, FileAttribute[])}
-     * doesn't unwrap its {@link Path} arguments, it causes a runtime exception, so exercise
-     * this method to check that it unwraps correctly.
-     */
-    public void testCreateSymbolicLinkUnwrapsPaths() throws Exception {
-        final Path tempDir = createTempDir();
-        Path quotaFile = tempDir.resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        writeQuota(500, 200, systemProvider, quotaFile);
-
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri())) {
-            final Path quotaFilePath = provider.getPath(tempDir.resolve("path1.txt").toUri());
-            final Path quotaLinkPath = provider.getPath(tempDir.resolve("path2.txt").toUri());
-
-            Files.writeString(quotaFilePath, "some text");
-
-            provider.createSymbolicLink(quotaLinkPath, quotaFilePath);
-        }
-    }
-
-    private void doValidFileTest(long expectedTotal, long expectedRemaining) throws Exception {
-        Path quotaFile = createTempDir().resolve("quota.properties");
-        FileSystemProvider systemProvider = quotaFile.getFileSystem().provider();
-        writeQuota(expectedTotal, expectedRemaining, systemProvider, quotaFile);
-
-        try (QuotaAwareFileSystemProvider provider = new QuotaAwareFileSystemProvider(systemProvider, quotaFile.toUri())) {
-            assertEquals(expectedTotal, provider.getTotal());
-            assertEquals(expectedRemaining, provider.getRemaining());
-        }
-    }
-
-    private void writeQuota(long expectedTotal, long expectedRemaining, FileSystemProvider systemProvider, Path quotaFile)
-        throws IOException {
-        Properties quota = new Properties();
-        quota.setProperty("total", Long.toString(expectedTotal));
-        quota.setProperty("remaining", Long.toString(expectedRemaining));
-        try (OutputStream outputStream = systemProvider.newOutputStream(quotaFile, WRITE, CREATE, TRUNCATE_EXISTING)) {
-            // Ideally this would use atomic write and the scala allocator does,
-            // but the parsing logic should be able to deal with it in either
-            // case.
-            quota.store(outputStream, "QuotaFile for: QuotaAwareFileSystemProviderTest#doValidFileTest");
-        }
-    }
-
-    public static void withRetry(int maxMillis, int interval, Runnable func) throws Exception {
-        long endBy = System.currentTimeMillis() + maxMillis;
-
-        while (true) {
-            try {
-                func.run();
-                break;
-            } catch (AssertionError | Exception e) {
-                if (System.currentTimeMillis() + interval < endBy) {
-                    Thread.sleep(interval);
-                    continue;
-                }
-                throw new IllegalStateException("Retry timed out after [" + maxMillis + "]ms", e);
-            }
-        }
-
-    }
-
-}

+ 0 - 93
x-pack/quota-aware-fs/src/test/java/org/elasticsearch/fs/quotaaware/QuotaAwareFileSystemTests.java

@@ -1,93 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-package org.elasticsearch.fs.quotaaware;
-
-import org.apache.lucene.mockfile.MockFileSystemTestCase;
-import org.elasticsearch.core.SuppressForbidden;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.DirectoryStream;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Properties;
-
-public class QuotaAwareFileSystemTests extends MockFileSystemTestCase {
-
-    private QuotaAwareFileSystemProvider quotaAwareFileSystemProvider;
-
-    @Override
-    @SuppressForbidden(reason = "accesses the default filesystem by design")
-    public void setUp() throws Exception {
-        super.setUp();
-        Path quotaPath = createTempDir().resolve("fsquota.properties");
-        Properties properties = new Properties();
-        properties.put("total", Long.toString(Long.MAX_VALUE));
-        properties.put("remaining", Long.toString(Long.MAX_VALUE));
-        try (OutputStream outputStream = Files.newOutputStream(quotaPath)) {
-            properties.store(outputStream, "");
-        }
-        quotaAwareFileSystemProvider = new QuotaAwareFileSystemProvider(FileSystems.getDefault().provider(), quotaPath.toUri());
-    }
-
-    @Override
-    protected Path wrap(Path path) {
-        return new QuotaAwarePath(new QuotaAwareFileSystem(quotaAwareFileSystemProvider, path.getFileSystem()), path);
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        quotaAwareFileSystemProvider.close();
-        super.tearDown();
-    }
-
-    /** Tests that newDirectoryStream with a filter works correctly */
-    @Override
-    public void testDirectoryStreamFiltered() throws IOException {
-        Path dir = wrap(createTempDir());
-
-        OutputStream file = Files.newOutputStream(dir.resolve("file1"));
-        file.write(5);
-        file.close();
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
-            int count = 0;
-            for (Path path : stream) {
-                assertTrue(path instanceof QuotaAwarePath);
-                if (path.getFileName().toString().startsWith("extra") == false) {
-                    count++;
-                }
-            }
-            assertEquals(1, count);
-        }
-        dir.getFileSystem().close();
-    }
-
-    /** Tests that newDirectoryStream with globbing works correctly */
-    @Override
-    public void testDirectoryStreamGlobFiltered() throws IOException {
-        Path dir = wrap(createTempDir());
-
-        OutputStream file = Files.newOutputStream(dir.resolve("foo"));
-        file.write(5);
-        file.close();
-        file = Files.newOutputStream(dir.resolve("bar"));
-        file.write(5);
-        file.close();
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "f*")) {
-            int count = 0;
-            for (Path path : stream) {
-                assertTrue(path instanceof QuotaAwarePath);
-                ++count;
-            }
-            assertEquals(1, count);
-        }
-        dir.getFileSystem().close();
-    }
-
-}

+ 0 - 104
x-pack/quota-aware-fs/src/test/java/org/elasticsearch/fs/quotaaware/SnapshotFilesystemProvider.java

@@ -1,104 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-package org.elasticsearch.fs.quotaaware;
-
-import java.io.IOException;
-import java.net.URI;
-import java.nio.file.FileStore;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileAttributeView;
-import java.nio.file.attribute.FileStoreAttributeView;
-import java.nio.file.spi.FileSystemProvider;
-
-/**
- * A {@link FileSystemProvider} implementation that delegates to another provider, but
- * takes a snapshot of the total, usable and unallocated space values upon creation.
- * This prevents tests failing due to activity on the underlying filesystem.
- */
-class SnapshotFilesystemProvider extends DelegatingProvider {
-    private final long totalSpace;
-    private final long usableSpace;
-    private final long unallocatedSpace;
-
-    SnapshotFilesystemProvider(FileSystemProvider provider) throws Exception {
-        super(provider);
-
-        final FileStore fileStore = provider.getFileStore(provider.getPath(new URI("file:///")));
-        this.totalSpace = fileStore.getTotalSpace();
-        this.usableSpace = fileStore.getUsableSpace();
-        this.unallocatedSpace = fileStore.getUnallocatedSpace();
-    }
-
-    @Override
-    public FileStore getFileStore(Path path) throws IOException {
-        return new SnapshotFileStore(super.getFileStore(path));
-    }
-
-    private class SnapshotFileStore extends FileStore {
-        private final FileStore delegate;
-
-        SnapshotFileStore(FileStore fileStore) {
-            this.delegate = fileStore;
-        }
-
-        @Override
-        public long getBlockSize() throws IOException {
-            return delegate.getBlockSize();
-        }
-
-        @Override
-        public String name() {
-            return delegate.name();
-        }
-
-        @Override
-        public String type() {
-            return delegate.type();
-        }
-
-        @Override
-        public boolean isReadOnly() {
-            return delegate.isReadOnly();
-        }
-
-        @Override
-        public long getTotalSpace() {
-            return totalSpace;
-        }
-
-        @Override
-        public long getUsableSpace() {
-            return usableSpace;
-        }
-
-        @Override
-        public long getUnallocatedSpace() {
-            return unallocatedSpace;
-        }
-
-        @Override
-        public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
-            return delegate.supportsFileAttributeView(type);
-        }
-
-        @Override
-        public boolean supportsFileAttributeView(String name) {
-            return delegate.supportsFileAttributeView(name);
-        }
-
-        @Override
-        public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
-            return delegate.getFileStoreAttributeView(type);
-        }
-
-        @Override
-        public Object getAttribute(String attribute) throws IOException {
-            return delegate.getAttribute(attribute);
-        }
-    }
-}