From a881310f48051f4a2f5f4f55f834e4dd3fdfc1dd Mon Sep 17 00:00:00 2001 From: ansgarz Date: Mon, 16 May 2022 22:09:35 +0200 Subject: [PATCH] fix configureVenv for idempotence, add createSymlink --- .../provs/desktop/infrastructure/Python.kt | 4 +- .../ubuntu/filesystem/base/Filesystem.kt | 39 +++++++- .../desktop/infrastructure/BashKtTest.kt | 2 - .../desktop/infrastructure/PythonKtTest.kt | 2 +- .../filesystem/base/FilesystemKtTest.kt | 97 ++++++++++++++++--- 5 files changed, 123 insertions(+), 21 deletions(-) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/Python.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/Python.kt index 08d5793..2f2f674 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/Python.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/Python.kt @@ -2,7 +2,9 @@ package org.domaindrivenarchitecture.provs.desktop.infrastructure import org.domaindrivenarchitecture.provs.framework.core.Prov import org.domaindrivenarchitecture.provs.framework.core.ProvResult +import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createSymlink import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.aptInstall +import java.io.File fun Prov.provisionPython() = task { @@ -21,7 +23,7 @@ fun Prov.configureVenv(): ProvResult = task { val venvHome = "~/.venv/meissa" cmd("python3 -m venv " + venvHome) cmd("source " + venvHome + "/bin/activate") - cmd("ln -s " + venvHome + "/bin/activate ~/.bashrc.d/venv.sh") + createSymlink(File(venvHome + "/bin/activate"), File("~/.bashrc.d/venv.sh")) cmd("pip3 install pip --upgrade") } diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/filesystem/base/Filesystem.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/filesystem/base/Filesystem.kt index 6024256..db1b769 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/filesystem/base/Filesystem.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/filesystem/base/Filesystem.kt @@ -94,10 +94,9 @@ fun Prov.createFile( val maxBlockSize = 50000 val withSudo = if (sudo) "sudo " else "" val file = File(fullyQualifiedFilename) - val dir = file.parent?.toString() - if (dir != null && dir != "" && createDirIfMissing && !checkDir(dir, sudo = sudo)) { - createDirs(dir, sudo = sudo) + if (createDirIfMissing) { + createParentDir(file, sudo) } posixFilePermission?.let { @@ -336,6 +335,29 @@ fun Prov.deleteDir(dir: String, path: String, sudo: Boolean = false): ProvResult } +// ============================= link operations ========================== + +/** + * Creates and validates a symlink. + */ +fun Prov.createSymlink( + source: File, + target: File, + sudo: Boolean = false, + overwriteIfExisting: Boolean = true, + createTargetDirIfMissing: Boolean = true, +): ProvResult = task { + if (createTargetDirIfMissing) { + createParentDir(target, sudo) + } + val overwriteFlag = if (overwriteIfExisting) "f" else "" + cmd("ln -s$overwriteFlag $source $target", sudo = sudo) + // ensure link works + taskWithResult("validate link") { + ProvResult(checkFile(target.toString(), sudo = sudo)) + } +} + // --------------------- various functions ---------------------- fun Prov.userHome(): String { val user = cmd("whoami").out?.trim() @@ -351,6 +373,17 @@ fun Prov.userHome(): String { } +/** + * Creates the parent-dir (parent path) of the specified file if parent-dir id not yet existing + */ +internal fun Prov.createParentDir(file: File, sudo: Boolean = false) = task { + val dir = file.parent?.toString() + + if (dir != null && dir != "" && !checkDir(dir, sudo = sudo)) { + createDirs(dir, sudo = sudo) + } +} + /** * Returns number of bytes of a file or null if size could not be determined */ diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/BashKtTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/BashKtTest.kt index 424102d..21a9317 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/BashKtTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/BashKtTest.kt @@ -7,11 +7,9 @@ import org.domaindrivenarchitecture.provs.test.defaultTestContainer import org.domaindrivenarchitecture.provs.test.tags.ContainerTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test internal class BashKtTest { - @Test @ContainerTest fun configureBashForUser() { // when diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/PythonKtTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/PythonKtTest.kt index 939ce31..8ba40b2 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/PythonKtTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/PythonKtTest.kt @@ -7,7 +7,7 @@ import org.junit.jupiter.api.Assertions.assertTrue internal class PythonKtTest { @ExtensiveContainerTest - fun installPython() { + fun test_provisionPython() { // when val res = defaultTestContainer().provisionPython() diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/filesystem/base/FilesystemKtTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/filesystem/base/FilesystemKtTest.kt index 107eaa1..ae9ac38 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/filesystem/base/FilesystemKtTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/filesystem/base/FilesystemKtTest.kt @@ -33,7 +33,6 @@ internal class FilesystemKtTest { assertEquals(testtext, textFromFile) } - @Test @ContainerTest fun createFile_successfully() { // given @@ -51,7 +50,6 @@ internal class FilesystemKtTest { assertEquals(testtext, prov.fileContent("sudo$filename")) } - @Test @ContainerTest fun createFile_with_dir_successfully() { // given @@ -77,7 +75,6 @@ internal class FilesystemKtTest { assertEquals(testtext, prov.fileContent("sudo$filename")) } - @Test @ContainerTest fun createFile_with_dir_fails_if_createDirIfMissing_is_false() { // given @@ -95,7 +92,6 @@ internal class FilesystemKtTest { assertFalse(prov.checkDir("sudodirDoesNotExist", sudo = true)) } - @Test @ContainerTest fun create_large_file_in_container() { // given @@ -113,7 +109,6 @@ internal class FilesystemKtTest { // assertEquals(testtext, prov.fileContent(filename)) } - @Test @ContainerTest fun create_and_delete_file() { // given @@ -141,7 +136,6 @@ internal class FilesystemKtTest { } - @Test @ContainerTest fun create_and_delete_file_with_sudo() { // given @@ -174,7 +168,6 @@ internal class FilesystemKtTest { } - @Test @ContainerTest fun create_and_delete_dir() { // given @@ -204,7 +197,6 @@ internal class FilesystemKtTest { } - @Test @ContainerTest fun create_and_delete_dir_with_sudo() { // given @@ -240,7 +232,6 @@ internal class FilesystemKtTest { } - @Test @ContainerTest fun replaceTextInFile() { // given @@ -261,7 +252,6 @@ internal class FilesystemKtTest { } - @Test @ContainerTest fun replaceTextInFileRegex() { // given @@ -282,7 +272,6 @@ internal class FilesystemKtTest { } - @Test @ContainerTest fun insertTextInFile() { // given @@ -302,7 +291,6 @@ internal class FilesystemKtTest { assertTrue(res4.success) } - @Test @ContainerTest fun copyFileFromLocal_successfully() { // given @@ -316,7 +304,6 @@ internal class FilesystemKtTest { assertEquals("resource text\n", content) } - @Test @ContainerTest fun fileContainsText() { // given @@ -346,7 +333,6 @@ internal class FilesystemKtTest { assertTrue(res10) } - @Test @ContainerTest fun fileContainsText_with_sudo() { // given @@ -404,4 +390,87 @@ internal class FilesystemKtTest { assertEquals(content, actualContent) assertEquals(1400000, size) } + + @ContainerTest + fun test_createParentDir() { + // given + val prov = defaultTestContainer() + val filename = "parent_dir/test/file" + + // when + val res = prov.createParentDir(File(filename)) + val dirExists = prov.checkDir("parent_dir/test") + val res2 = prov.createParentDir(File(filename)) // test idempotence + val dirExists2 = prov.checkDir("parent_dir/test") + + // then + assertTrue(res.success) + assertTrue(dirExists) + assertTrue(res2.success) + assertTrue(dirExists2) + + } + + @ContainerTest + fun test_createLink_without_dir() { + // given + val prov = defaultTestContainer() + val source = File("testlinksource") + val target = File("testlinktarget") + prov.createFile(source.toString(), "textinlinkfile") + + // when + val res = prov.createSymlink(source, target) + val res2 = prov.createSymlink(source, target) // test idempotence + val linkExists = prov.checkFile(target.name) + val content = prov.fileContent(target.name) + + // then + assertTrue(res.success) + assertTrue(res2.success) + assertTrue(linkExists) + assertEquals("textinlinkfile", content) + } + + @ContainerTest + fun test_createLink_with_dirs() { + // given + val prov = defaultTestContainer() + val source = File("~/linksourcedir/testlinksource2") + val target = File("linkdir1/linkdir2/testlinktarget2") + prov.createFile(source.toString(), "textinlinkfile2") + + // when + val res = prov.createSymlink(source, target) + val res2 = prov.createSymlink(source, target) // test idempotence + val linkExists = prov.checkFile(target.toString()) + val content = prov.fileContent(target.toString()) + + // then + assertTrue(res.success) + assertTrue(res2.success) + assertTrue(linkExists) + assertEquals("textinlinkfile2", content) + } + + @ContainerTest + fun test_createLink_with_dirs_and_sudo() { + // given + val prov = defaultTestContainer() + val source = File("/linksourcedirsudo/linksourcefilesudo") + val target = File("/linkdir1sudo/linkdir2sudo/linksudo") + prov.createFile(source.toString(), "textinlinkfilesudo", sudo = true) + + // when + val res = prov.createSymlink(source, target, sudo = true) + val res2 = prov.createSymlink(source, target, sudo = true) // test idempotence + val linkExists = prov.checkFile(target.toString(), sudo = true) + val content = prov.fileContent(target.toString(), sudo = true) + + // then + assertTrue(res.success) + assertTrue(res2.success) + assertTrue(linkExists) + assertEquals("textinlinkfilesudo", content) + } }