diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/Gopass.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/Gopass.kt index e210e21..cd89ea5 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/Gopass.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/Gopass.kt @@ -12,13 +12,13 @@ fun Prov.installGopass( version: String = "1.12.7", enforceVersion: Boolean = false, sha256sum: String = "0824d5110ff1e68bff1ba10c1be63acb67cb1ad8e3bccddd6b6fc989608beca8" // checksum for sha256sum version 8.30 (e.g. ubuntu 20.04) -) = task { +) = taskWithResult { if (isPackageInstalled("gopass") && !enforceVersion) { - return@task ProvResult(true) + return@taskWithResult ProvResult(true) } if (checkGopassVersion(version)) { - return@task ProvResult(true, out = "Version $version of gopass is already installed.") + return@taskWithResult ProvResult(true, out = "Version $version of gopass is already installed.") } val path = "tmp" @@ -41,17 +41,17 @@ fun Prov.installGopass( } -fun Prov.configureGopass(gopassRootFolder: String? = null) = task { +fun Prov.configureGopass(gopassRootFolder: String? = null) = taskWithResult() { val configFile = ".config/gopass/config.yml" val defaultRootFolder = userHome() + ".password-store" val rootFolder = gopassRootFolder ?: defaultRootFolder if (checkFile(configFile)) { - return@task ProvResult(true, out = "Gopass already configured in file $configFile") + return@taskWithResult ProvResult(true, out = "Gopass already configured in file $configFile") } if ((gopassRootFolder != null) && (!gopassRootFolder.startsWith("/"))) { - return@task ProvResult(false, err = "Gopass cannot be initialized with a relative path or path starting with ~") + return@taskWithResult ProvResult(false, err = "Gopass cannot be initialized with a relative path or path starting with ~") } // use default createDir(rootFolder) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/VirtualBoxGuest.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/VirtualBoxGuest.kt index 63db5c4..c53ff40 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/VirtualBoxGuest.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/VirtualBoxGuest.kt @@ -5,14 +5,14 @@ import org.domaindrivenarchitecture.provs.framework.core.ProvResult import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.aptInstall import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.whoami -fun Prov.installVirtualBoxGuestAdditions() = task { +fun Prov.installVirtualBoxGuestAdditions() = taskWithResult { // if running in a VirtualBox vm if (!chk("lspci | grep VirtualBox")) { - return@task ProvResult(true, "Not running in a VirtualBox") + return@taskWithResult ProvResult(true, "Not running in a VirtualBox") } if (chk("VBoxService --version")) { - return@task ProvResult(true, "VBoxService already installed") + return@taskWithResult ProvResult(true, "VBoxService already installed") } // install guest additions diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Prov.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Prov.kt index da95cad..22e3775 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Prov.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Prov.kt @@ -67,11 +67,19 @@ open class Prov protected constructor( private var runInContainerWithName: String? = null /** - * Defines a task with a custom name instead of the name of the calling function. - * Returns success if all subtasks finished with success (same as requireAll). + * A task is the base execution unit in provs. In the results overview it is represented by one line resp. result (of either success or failure). + * Returns success if no sub-tasks are called or if all subtasks finish with success. */ - fun task(name: String? = null, a: Prov.() -> ProvResult): ProvResult { - return handle(ResultMode.ALL, name) { a() } + fun task(name: String? = null, taskLambda: Prov.() -> Unit): ProvResult { + return handle(ResultMode.ALL, name) { taskLambda(); ProvResult(true) } + } + + /** + * Same as task but the provided lambda is explicitly required to provide a ProvResult to be returned. + * The returned result is included in the evaluation. + */ + fun taskWithResult(name: String? = null, taskLambda: Prov.() -> ProvResult): ProvResult { + return handle(ResultMode.ALL, name) { taskLambda() } } /** @@ -84,7 +92,7 @@ open class Prov protected constructor( } /** - * defines a task, which returns success if the the last subtasks or last value returns success + * defines a task, which returns the returned result, the results of sub-tasks are not considered */ fun requireLast(a: Prov.() -> ProvResult): ProvResult { return handle(ResultMode.LAST) { a() } @@ -100,7 +108,7 @@ open class Prov protected constructor( /** * defines a task, which returns success if all subtasks finished with success */ - @Suppress("unused") + @Deprecated("Use function task instead", replaceWith = ReplaceWith("task()")) fun requireAll(a: Prov.() -> ProvResult): ProvResult { return handle(ResultMode.ALL) { a() } } @@ -218,7 +226,7 @@ open class Prov protected constructor( * Adds a ProvResult to the overall success evaluation. * Intended for use in methods which do not automatically add results. */ - fun addResultToEval(result: ProvResult) = task { + fun addResultToEval(result: ProvResult) = taskWithResult { result } @@ -227,7 +235,7 @@ open class Prov protected constructor( * Multi-line commands within the script are not supported. * Empty lines and comments (all text behind # in a line) are supported, i.e. they are ignored. */ - fun sh(script: String, dir: String? = null, sudo: Boolean = false) = task { + fun sh(script: String, dir: String? = null, sudo: Boolean = false) = taskWithResult { val lines = script.trimIndent().replace("\\\n", "").replace("\r\n", "\n").split("\n") val linesWithoutComments = lines.stream().map { it.split("#")[0] } val linesNonEmpty = linesWithoutComments.filter { it.trim().isNotEmpty() } diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Utils.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Utils.kt index 55e54e9..fba1281 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Utils.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/Utils.kt @@ -18,7 +18,7 @@ import java.net.InetAddress */ internal fun getCallingMethodName(): String? { val offsetVal = 1 - val exclude = arrayOf("task", "def", "record", "invoke", "invoke0", "handle", "task\$default", "def\$default", "addResultToEval", "handle\$default") + val exclude = arrayOf("task", "task\$default", "taskWithResult\$default", "taskWithResult", "def", "def\$default", "record", "invoke", "invoke0", "handle", "handle\$default", ) // suffixes are also ignored as method names but will be added as suffix in the evaluation results val suffixes = arrayOf("optional", "requireAll", "requireLast", "inContainer") diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/platforms/UbuntuProv.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/platforms/UbuntuProv.kt index ff3e0e0..9c6995e 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/platforms/UbuntuProv.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/platforms/UbuntuProv.kt @@ -23,7 +23,7 @@ class UbuntuProv internal constructor( } } - override fun cmd(cmd: String, dir: String?, sudo: Boolean): ProvResult = task { + override fun cmd(cmd: String, dir: String?, sudo: Boolean): ProvResult = taskWithResult { exec(SHELL, "-c", commandWithDirAndSudo(cmd, dir, sudo)) } 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 2152d97..162df2b 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 @@ -90,7 +90,7 @@ fun Prov.createFile( posixFilePermission: String? = null, sudo: Boolean = false, overwriteIfExisting: Boolean = true -): ProvResult = task { +): ProvResult = taskWithResult { val maxBlockSize = 50000 val withSudo = if (sudo) "sudo " else "" @@ -98,7 +98,7 @@ fun Prov.createFile( ensureValidPosixFilePermission(posixFilePermission) } if (!overwriteIfExisting && checkFile(fullyQualifiedFilename, sudo)) { - return@task ProvResult(true, "File $fullyQualifiedFilename already existing.") + return@taskWithResult ProvResult(true, "File $fullyQualifiedFilename already existing.") } val modeOption = posixFilePermission?.let { "-m $it" } ?: "" @@ -220,10 +220,10 @@ fun Prov.addTextToFile( doNotAddIfExisting: Boolean = true, sudo: Boolean = false ): ProvResult = - task { + taskWithResult { val fileContainsText = fileContainsText(file.path, text, sudo = sudo) if (fileContainsText && doNotAddIfExisting) { - return@task ProvResult(true, out = "Text already in file") + return@taskWithResult ProvResult(true, out = "Text already in file") } cmd( "printf '%s' " + text diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/Git.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/Git.kt index 6f412c7..2e670f5 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/Git.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/Git.kt @@ -17,11 +17,11 @@ fun Prov.gitClone( targetPath: String = "", pullIfExisting: Boolean = true, targetFolderName: String? = null -): ProvResult = task { +): ProvResult = taskWithResult { // if specified, use targetFolderName as basename or otherwise retrieve basename from repoSource val basename = targetFolderName ?: cmdNoEval("basename $repoSource .git").out?.trim() // return err if basename could not be retrieved from repoSource - ?: return@task ProvResult(false, err = "$repoSource is not a valid git repository source path.") + ?: return@taskWithResult ProvResult(false, err = "$repoSource is not a valid git repository source path.") val pathWithBasename = targetPath.normalizePath() + basename if (checkDir(pathWithBasename + "/.git/")) { diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/install/base/Install.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/install/base/Install.kt index a37c457..b1fb8cb 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/install/base/Install.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/install/base/Install.kt @@ -13,7 +13,7 @@ private var aptInit = false * @param ignoreAlreadyInstalled if true, then for an already installed package no action will be taken, * if "ignoreAlreadyInstalled" is false, then installation is always attempted, which normally results in an upgrade if package wa already installed */ -fun Prov.aptInstall(packages: String, ignoreAlreadyInstalled: Boolean = true): ProvResult = task { +fun Prov.aptInstall(packages: String, ignoreAlreadyInstalled: Boolean = true): ProvResult = taskWithResult { val packageList = packages.split(" ") val allInstalled: Boolean = packageList.map { isPackageInstalled(it) }.fold(true, { a, b -> a && b }) if (!allInstalled) { diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/Ssh.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/Ssh.kt index 8745a7b..b506d64 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/Ssh.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/Ssh.kt @@ -38,9 +38,9 @@ fun Prov.isHostKnown(hostOrIp: String) : Boolean { * Either add the specified rsaFingerprints or - if null - add automatically retrieved keys. * Note: adding keys automatically is vulnerable to a man-in-the-middle attack, thus considered insecure and not recommended. */ -fun Prov.trustHost(host: String, fingerprintsOfKeysToBeAdded: Set?) = task { +fun Prov.trustHost(host: String, fingerprintsOfKeysToBeAdded: Set?) = taskWithResult { if (isHostKnown(host)) { - return@task ProvResult(true, out = "Host already known") + return@taskWithResult ProvResult(true, out = "Host already known") } if (!checkFile(KNOWN_HOSTS_FILE)) { createDir(".ssh") @@ -53,7 +53,7 @@ fun Prov.trustHost(host: String, fingerprintsOfKeysToBeAdded: Set?) = ta // logic based on https://serverfault.com/questions/447028/non-interactive-git-clone-ssh-fingerprint-prompt val actualKeys = findSshKeys(host) if (actualKeys == null || actualKeys.size == 0) { - return@task ProvResult(false, out = "No valid keys found for host: $host") + return@taskWithResult ProvResult(false, out = "No valid keys found for host: $host") } val actualFingerprints = getFingerprintsForKeys(actualKeys) for (fingerprintToBeAdded in fingerprintsOfKeysToBeAdded) { @@ -67,7 +67,7 @@ fun Prov.trustHost(host: String, fingerprintsOfKeysToBeAdded: Set?) = ta } } if (indexOfKeyFound == -1) { - return@task ProvResult( + return@taskWithResult ProvResult( false, err = "Fingerprint ($fingerprintToBeAdded) could not be found in actual fingerprints: $actualFingerprints" ) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/web/base/Web.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/web/base/Web.kt index 63d10b5..0ae428b 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/web/base/Web.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/web/base/Web.kt @@ -22,13 +22,13 @@ fun Prov.downloadFromURL( followRedirect: Boolean = true, sha256sum: String? = null, overwrite: Boolean = false -): ProvResult = task { +): ProvResult = taskWithResult { val finalFilename: String = filename ?: url.substringAfterLast("/") val fqFilename: String = (path?.normalizePath() ?: "") + finalFilename if (!overwrite && checkFile(fqFilename, sudo = sudo)) { - return@task ProvResult(true, out = "File $fqFilename already exists.") + return@taskWithResult ProvResult(true, out = "File $fqFilename already exists.") } aptInstall("curl") diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/server/infrastructure/K3s.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/server/infrastructure/K3s.kt index d11140f..4622fd7 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/server/infrastructure/K3s.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/server/infrastructure/K3s.kt @@ -51,9 +51,9 @@ fun Prov.deprovisionK3sInfra() = task { } -fun Prov.installK3s(k3sConfig: K3sConfig) = task { +fun Prov.installK3s(k3sConfig: K3sConfig) = taskWithResult { if (testConfigExists()) { - return@task ProvResult(true, out = "K3s config is already in place, so skip (re)provisioning.") + return@taskWithResult ProvResult(true, out = "K3s config is already in place, so skip (re)provisioning.") } createDirs(k8sCredentialsDir, sudo = true) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/syspec/domain/SyspecService.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/syspec/domain/SyspecService.kt index f394dae..24c537f 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/syspec/domain/SyspecService.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/syspec/domain/SyspecService.kt @@ -8,17 +8,17 @@ import org.domaindrivenarchitecture.provs.syspec.infrastructure.findSpecConfigFr import org.domaindrivenarchitecture.provs.syspec.infrastructure.verifySpecConfig -fun Prov.verifySpec(configFile: ConfigFileName? = null) = task { +fun Prov.verifySpec(configFile: ConfigFileName? = null) = taskWithResult { val result = findSpecConfigFromFile(configFile) - val spec = result.getOrElse { return@task ProvResult(false, "Could not read file: ${configFile?.fileName} due to: ${result.exceptionOrNull()?.message}") } + val spec = result.getOrElse { return@taskWithResult ProvResult(false, "Could not read file: ${configFile?.fileName} due to: ${result.exceptionOrNull()?.message}") } verifySpecConfig(spec) } @Suppress("unused") // Api -fun Prov.verifySpecFromResource(resourceName: String) = task { +fun Prov.verifySpecFromResource(resourceName: String) = taskWithResult { val result = findSpecConfigFromResource(resourceName) - val spec = result.getOrElse { return@task ProvResult(false, "Could not read resource: $resourceName due to: ${result.exceptionOrNull()?.message}") } + val spec = result.getOrElse { return@taskWithResult ProvResult(false, "Could not read resource: $resourceName due to: ${result.exceptionOrNull()?.message}") } verifySpecConfig(spec) } diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/syspec/infrastructure/Verification.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/syspec/infrastructure/Verification.kt index 93d3d10..eecba8e 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/syspec/infrastructure/Verification.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/syspec/infrastructure/Verification.kt @@ -149,7 +149,7 @@ private fun Prov.verify(success: Boolean, message: String, expected: T? = nu val actualText = expected?.let { " | Actual value [$actual]" } ?: "" val msg = ": $message $expectedText$actualText" - return task("Verification") { + return taskWithResult("Verification") { ProvResult( success, cmd = if (success) msg else null, diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvTest.kt index a622a24..445b73b 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/ProvTest.kt @@ -489,7 +489,7 @@ internal class ProvTest { addResultToEval(ProvResult(true)) } - fun Prov.outer() = task { + fun Prov.outer() = taskWithResult { inner() ProvResult(false) } @@ -504,11 +504,11 @@ internal class ProvTest { @Test fun task_with_failing_subtask_and_successful_result_fails() { // given - fun Prov.inner() = task { + fun Prov.inner() = taskWithResult { ProvResult(false) } - fun Prov.outer() = task { + fun Prov.outer() = taskWithResult { inner() ProvResult(true) } @@ -527,7 +527,7 @@ internal class ProvTest { addResultToEval(ProvResult(false)) } - fun Prov.outer() = task { + fun Prov.outer() = taskWithResult { inner() ProvResult(true) } diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/TaskFunctionsKtTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/TaskFunctionsKtTest.kt index 33930eb..59b82f2 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/TaskFunctionsKtTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/core/TaskFunctionsKtTest.kt @@ -29,7 +29,7 @@ internal class TaskFunctionsKtTest { } var count2 = 3 - fun Prov.thirdTimeSuccess() = task { + fun Prov.thirdTimeSuccess() = taskWithResult { if (count2 <= 1) { count2 = 3 ProvResult(true, out = "1")