From 53f781b169dfdf7a32523a288437c003c0d4de21 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Wed, 28 Jan 2026 16:30:13 +0100 Subject: [PATCH] Implemented FileSystemFreeSpaceHealthCheck --- .../FileSystemFreeSpaceHealthCheck.java | 57 +++++++++++++ .../FileSystemFreeSpaceHealthCheckTest.java | 83 +++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 src/main/java/nl/knaw/dans/lib/util/healthcheck/FileSystemFreeSpaceHealthCheck.java create mode 100644 src/test/java/nl/knaw/dans/lib/util/healthcheck/FileSystemFreeSpaceHealthCheckTest.java diff --git a/src/main/java/nl/knaw/dans/lib/util/healthcheck/FileSystemFreeSpaceHealthCheck.java b/src/main/java/nl/knaw/dans/lib/util/healthcheck/FileSystemFreeSpaceHealthCheck.java new file mode 100644 index 0000000..54d9404 --- /dev/null +++ b/src/main/java/nl/knaw/dans/lib/util/healthcheck/FileSystemFreeSpaceHealthCheck.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2021 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed 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 nl.knaw.dans.lib.util.healthcheck; + +import com.codahale.metrics.health.HealthCheck; +import io.dropwizard.util.DataSize; +import lombok.AllArgsConstructor; + +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * A health check that verifies that the file system containing the given path has at least the given margin of free space. + */ +@AllArgsConstructor +public class FileSystemFreeSpaceHealthCheck extends HealthCheck { + private final Path path; + private final DataSize margin; + + @Override + protected Result check() throws Exception { + long freeSpace = Files.getFileStore(path).getUsableSpace(); + long requiredSpace = margin.toBytes(); + + if (freeSpace < requiredSpace) { + return Result.unhealthy(String.format( + "Free space on file system containing %s is %d bytes, which is less than the required margin of %d bytes.", + path, freeSpace, requiredSpace + )); + } + return Result.healthy(String.format( + "Free space on file system containing %s is %d bytes, which is above the required margin of %d bytes.", + path, freeSpace, requiredSpace + )); + } + + @Override + public String toString() { + return "FileSystemFreeSpaceHealthCheck{" + + "path=" + path + + ", margin=" + margin + + '}'; + } +} diff --git a/src/test/java/nl/knaw/dans/lib/util/healthcheck/FileSystemFreeSpaceHealthCheckTest.java b/src/test/java/nl/knaw/dans/lib/util/healthcheck/FileSystemFreeSpaceHealthCheckTest.java new file mode 100644 index 0000000..b28176a --- /dev/null +++ b/src/test/java/nl/knaw/dans/lib/util/healthcheck/FileSystemFreeSpaceHealthCheckTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed 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 nl.knaw.dans.lib.util.healthcheck; + +import com.codahale.metrics.health.HealthCheck; +import io.dropwizard.util.DataSize; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class FileSystemFreeSpaceHealthCheckTest { + + @Test + public void healthy_when_free_space_is_above_required_margin() throws Exception { + // Given + Path path = Path.of("/any/path"); + DataSize margin = DataSize.megabytes(100); // 100 MB + FileStore fileStore = mock(FileStore.class); + when(fileStore.getUsableSpace()).thenReturn(DataSize.gigabytes(1).toBytes()); // 1 GB free + + try (MockedStatic filesMock = Mockito.mockStatic(Files.class)) { + filesMock.when(() -> Files.getFileStore(path)).thenReturn(fileStore); + + FileSystemFreeSpaceHealthCheck healthCheck = new FileSystemFreeSpaceHealthCheck(path, margin); + + // When + HealthCheck.Result result = healthCheck.check(); + + // Then + assertThat(result.isHealthy()).isTrue(); + assertThat(result.getMessage()) + .contains("Free space on file system containing") + .contains("is ") + .contains("which is above the required margin"); + } + } + + @Test + public void unhealthy_when_free_space_is_below_required_margin() throws Exception { + // Given + Path path = Path.of("/any/path"); + DataSize margin = DataSize.gigabytes(2); // 2 GB required + FileStore fileStore = mock(FileStore.class); + when(fileStore.getUsableSpace()).thenReturn(DataSize.megabytes(500).toBytes()); // 500 MB free + + try (MockedStatic filesMock = Mockito.mockStatic(Files.class)) { + filesMock.when(() -> Files.getFileStore(path)).thenReturn(fileStore); + + FileSystemFreeSpaceHealthCheck healthCheck = new FileSystemFreeSpaceHealthCheck(path, margin); + + // When + HealthCheck.Result result = healthCheck.check(); + + // Then + assertThat(result.isHealthy()).isFalse(); + assertThat(result.getMessage()) + .contains("Free space on file system containing") + .contains("is ") + .contains("which is less than the required margin"); + } + } +}