JUnit4 - Как проверить каталог на чтение / защищенный от записи, если запустить с помощью докера - PullRequest
0 голосов
/ 17 октября 2019

У нас есть настройка интеграционного теста для тестирования поведения отсутствующих, но необходимых свойств конфигурации. Одним из этих свойств является каталог, в который следует записывать неудачные загрузки для последующих попыток. Общее поведение для этого свойства должно состоять в том, что приложение даже не запускается и сразу выходит из строя при нарушении определенных ограничений.

Свойства управляются Spring через определенные ConfigurationProperties, среди которых у нас есть простой S3MessageUploadSettings class

@Getter
@Setter
@ConfigurationProperties(prefix = "s3")
@Validated
public class S3MessageUploadSettings {
    @NotNull
    private String bucketName;
    @NotNull
    private String uploadErrorPath;
    ...
}

В соответствующей конфигурации Spring мы теперь выполняем определенные проверки правильности, такие как, существует ли путь, доступен ли для записи и каталог, и генерируем соответствующие RuntimeException s, когда определенные утверждения не выполнены:

@Slf4j
@Import({ S3Config.class })
@Configuration
@EnableConfigurationProperties(S3MessageUploadSettings.class)
public class S3MessageUploadSpringConfig {

    @Resource
    private S3MessageUploadSettings settings;

    ...

    @PostConstruct
    public void checkConstraints() {
        String sPath = settings.getUploadErrorPath();
        Path path = Paths.get(sPath);
        ...
        log.debug("Probing path '{}' for existence', path);
        if (!Files.exists(path)) {
            throw new RuntimeException("Required error upload directory '" + path + "' does not exist");
        }

        log.debug("Probig path '{}' for being a directory", path);
        if (!Files.isDirectory(path)) {
            throw new RuntimeException("Upload directory '" + path + "' is not a directoy");
        }

        log.debug("Probing path '{}' for write permissions", path);
        if (!Files.isWritable(path)) {
            throw new RuntimeException("Error upload path '" + path +"' is not writable);
        }
    }
}

Теперь наша тестовая установка выглядит следующим образом:

public class StartupTest {

    @ClassRule
    public static TemporaryFolder testFolder = new TemporaryFolder();

    private static File BASE_FOLDER;
    private static File ACCESSIBLE;
    private static File WRITE_PROTECTED;
    private static File NON_DIRECTORY;

    @BeforeClass
    public static void initFolderSetup() throws IOException {
        BASE_FOLDER = testFolder.getRoot();
        ACCESSIBLE = testFolder.newFolder("accessible");
        WRITE_PROTECTED = testFolder.newFolder("writeProtected");
        if (!WRITE_PROTECTED.setReadOnly()) {
            fail("Could not change directory permissions to readonly")
        }
        if (!WRITE_PROTECTED.setWritable(false)) {
            fail("Could not change directory permissions to writable(false)");
        }
        NON_DIRECTORY = testFolder.newFile("nonDirectory");
    }

    @Configuration
    @Import({
        S3MessageUploadSpringConfig.class,
        S3MockConfig.class,
        ...
    })
    static class BaseContextConfig {
        // common bean definitions
        ...
    }

    @Configuration
    @Import(BaseContextConfig.class)
    @PropertySource("classpath:ci.properties")
    static class NotExistingPathContextConfig {

        @Resource
        private S3MessageUploadSettings settings;

        @PostConstruct
        public void updateSettings() {
            settings.setUploadErrorPath(BASE_FOLDER.getPath() + "/foo/bar");
        }
    }

    @Configuration
    @Import(BaseContextConfig.class)
    @PropertySource("classpath:ci.properties")
    static class NotWritablePathContextConfig {

        @Resource
        private S3MessageUploadSettings settings;

        @PostConstruct
        public void updateSettings() {
            settings.setUploadErrorPath(WRITE_PROTECTED.getPath());
        }
    }

    ...

    @Configuration
    @Import(BaseContextConfig.class)
    @PropertySource("classpath:ci.properties")
    static class StartableContextConfig {

        @Resource
        private S3MessageUploadSettings settings;

        @PostConstruct
        public void updateSettings() {
            settings.setUploadErrorPath(ACCESSIBLE.getPath());
        }
    }

    @Test
    public void shouldFailStartupDueToNonExistingErrorPathDirectory() {
        ApplicationContext context = null;
        try {
            context = new AnnotationConfigApplicationContext(StartupTest.NotExistingPathContextConfig.class);
            fail("Should not have started the context");
        } catch (Exception e) {
            e.printStackTrace();
            assertThat(e, instanceOf(BeanCreationException.class));
            assertThat(e.getMessage(), containsString("Required error upload directory '" + BASE_FOLDER + "/foo/bar' does not exist"));
        } finally {
            closeContext(context);
        }
    }

    @Test
    public void shouldFailStartupDueToNonWritablePathDirectory() {
        ApplicationContext context = null;
        try {
            context = new AnnotationConfigApplicationContext(StartupTest.NotWritablePathContextConfig.class);
            fail("Should not have started the context");
        } catch (Exception e) {
            assertThat(e, instanceOf(BeanCreationException.class));
            assertThat(e.getMessage(), containsString("Error upload path '" + WRITE_PROTECTED + "' is not writable"));
        } finally {
            closeContext(context);
        } 
    }

    ...

    @Test
    public void shouldStartUpSuccessfully() {
        ApplicationContext context = null;
        try {
            context = new AnnotationConfigApplicationContext(StartableContextConfig.class);
        } catch (Exception e) {
            e.printStackTrace();
            fail("Should not have thrown an exception of type " + e.getClass().getSimpleName() + " with message " + e.getMessage());
        } finally {
            closeContext(context);
        }
    }

    private void closeContext(ApplicationContext context) {
        if (context != null) {
            // check and close any running S3 mock as this may have negative impact on the startup of a further context
            closeS3Mock(context);
            // stop a running Spring context manually as this might interfere with a starting context of an other test
            ((ConfigurableApplicationContext) context).stop();
        }
    }

    private void closeS3Mock(ApplicationContext context) {
        S3Mock s3Mock = null;
        try {
            if (context != null) {
                s3Mock = context.getBean("s3Mock", S3Mock.class);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != s3Mock) {
                s3Mock.stop();
            }
        }
    }
}

При локальном запуске все выглядит хорошо и все тесты проходят. Хотя наш CI выполняет эти тесты в док-контейнере и по какой-то причине изменение прав доступа к файлу, по-видимому, приводит к тому, что NOOP возвращает true при вызове метода, но ничего не меняет в отношении самого разрешения файла.

Neiter File.setReadOnly(), File.setWritable(false) и Files.setPosixFilePermissions(Path, Set<PosixFilePermission>), похоже, влияют на фактические права доступа к файлам в контейнере Docker.

Я также пытался изменить каталоги на реальныекаталоги, то есть /root или /dev/pts, которые защищены от записи, хотя, когда CI выполняет тесты как root, эти каталоги доступны для записи приложению, и тест снова не проходит.

Я также рассмотрел использованиефайловая система в памяти (такая как JimFS), хотя здесь я не уверен, как убедить тест использовать пользовательскую файловую систему. AFAIK JimFS не поддерживает конструктор, необходимый для объявления его в качестве файловой системы по умолчанию.

Какие другие возможности существуют в Java для изменения разрешения каталогов на чтение / защита от записи при запуске внутри контейнера Docker или успешном тестировании для такогокаталог?

1 Ответ

0 голосов
/ 17 октября 2019

Я предполагаю, что это связано с разрешениями и политиками JVM, и вы ничего не можете сделать из своего кода, если ОС заблокировала некоторые разрешения для вашей JVM.

Вы можете попытаться редактировать java.policy file и установите соответствующий файл permissions .

Возможно, это будут некоторые заданные файлы, для которых будут установлены привилегии записи, например:

grant {
    permission java.io.FilePermission "/dev/pts/*", "read,write,delete";
};

Больше примеров в документации: https://docs.oracle.com/javase/8/docs/technotes/guides/security/spec/security-spec.doc3.html.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...