introduce method session &refactor Application.kt
This commit is contained in:
parent
c78cf8e3bf
commit
eba6037fcc
6 changed files with 108 additions and 29 deletions
|
@ -2,8 +2,11 @@ package org.domaindrivenarchitecture.provs.desktop.application
|
|||
|
||||
import kotlinx.serialization.SerializationException
|
||||
import org.domaindrivenarchitecture.provs.configuration.application.ensureSudoWithoutPassword
|
||||
import org.domaindrivenarchitecture.provs.desktop.domain.DesktopConfig
|
||||
import org.domaindrivenarchitecture.provs.desktop.domain.provisionDesktopCommand
|
||||
import org.domaindrivenarchitecture.provs.desktop.infrastructure.getConfig
|
||||
import org.domaindrivenarchitecture.provs.framework.core.cli.createProvInstance
|
||||
import org.domaindrivenarchitecture.provs.framework.core.cli.quit
|
||||
import java.io.FileNotFoundException
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
@ -18,22 +21,33 @@ fun main(args: Array<String>) {
|
|||
exitProcess(1)
|
||||
}
|
||||
|
||||
val config = if (cmd.configFile == null) DesktopConfig() else
|
||||
try {
|
||||
getConfig(cmd.configFile.fileName)
|
||||
} catch (e: SerializationException) {
|
||||
println(
|
||||
"Error: File \"${cmd.configFile.fileName}\" has an invalid format and or invalid data."
|
||||
)
|
||||
null
|
||||
} catch (e: FileNotFoundException) {
|
||||
println(
|
||||
"Error: File\u001b[31m ${cmd.configFile.fileName} \u001b[0m was not found.\n" +
|
||||
"Pls copy file \u001B[31m desktop-config-example.yaml \u001B[0m to file \u001B[31m ${cmd.configFile.fileName} \u001B[0m " +
|
||||
"and change the content according to your needs."
|
||||
)
|
||||
null
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
println("No suitable config found.")
|
||||
quit(-1)
|
||||
}
|
||||
|
||||
val prov = createProvInstance(cmd.target)
|
||||
|
||||
try {
|
||||
prov.task {
|
||||
ensureSudoWithoutPassword(cmd.target.remoteTarget()?.password)
|
||||
provisionDesktopCommand(cmd)
|
||||
}
|
||||
} catch (e: SerializationException) {
|
||||
println(
|
||||
"Error: File \"${cmd.configFile?.fileName}\" has an invalid format and or invalid data.\n"
|
||||
)
|
||||
} catch (e: FileNotFoundException) {
|
||||
println(
|
||||
"Error: File\u001b[31m ${cmd.configFile?.fileName} \u001b[0m was not found.\n" +
|
||||
"Pls copy file \u001B[31m desktop-config-example.yaml \u001B[0m to file \u001B[31m ${cmd.configFile?.fileName} \u001B[0m " +
|
||||
"and change the content according to your needs.\n"
|
||||
)
|
||||
prov.session {
|
||||
ensureSudoWithoutPassword(cmd.target.remoteTarget()?.password)
|
||||
provisionDesktopCommand(cmd, config)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,11 +12,7 @@ import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.provisionKeys
|
|||
import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.currentUserCanSudoWithoutPassword
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.whoami
|
||||
|
||||
internal fun Prov.provisionDesktopCommand(cmd: DesktopCliCommand) = task {
|
||||
|
||||
// retrieve config
|
||||
val conf = if (cmd.configFile != null) getConfig(cmd.configFile.fileName) else DesktopConfig()
|
||||
|
||||
internal fun Prov.provisionDesktopCommand(cmd: DesktopCliCommand, conf: DesktopConfig) = task {
|
||||
provisionDesktop(
|
||||
cmd.type,
|
||||
conf.ssh?.keyPair(),
|
||||
|
|
|
@ -68,6 +68,17 @@ open class Prov protected constructor(
|
|||
private val internalResults = arrayListOf<ResultLine>()
|
||||
private val infoTexts = arrayListOf<String>()
|
||||
|
||||
/**
|
||||
* A session is the top-level execution unit in provs. A session can contain tasks.
|
||||
* Returns success if no sub-tasks are called or if all subtasks finish with success.
|
||||
*/
|
||||
fun session(taskLambda: Prov.() -> Unit) = task("session") {
|
||||
if (level > 1) {
|
||||
throw IllegalStateException("A session can only be created on the top-level and may not be included in another session or task.")
|
||||
}
|
||||
taskLambda()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
|
@ -52,3 +52,11 @@ internal fun createRemoteProvInstance(
|
|||
internal fun getPasswordToConfigureSudoWithoutPassword(): Secret {
|
||||
return PromptSecretSource("password to configure sudo without password.").secret()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper for exitProcess, which allows e.g. mocking for test purposes
|
||||
*/
|
||||
fun quit(status: Int): Nothing {
|
||||
exitProcess(status)
|
||||
}
|
|
@ -4,16 +4,21 @@ import ch.qos.logback.classic.Level
|
|||
import io.mockk.*
|
||||
import org.domaindrivenarchitecture.provs.configuration.domain.ConfigFileName
|
||||
import org.domaindrivenarchitecture.provs.configuration.domain.TargetCliCommand
|
||||
import org.domaindrivenarchitecture.provs.desktop.domain.*
|
||||
import org.domaindrivenarchitecture.provs.desktop.domain.DesktopCliCommand
|
||||
import org.domaindrivenarchitecture.provs.desktop.domain.DesktopConfig
|
||||
import org.domaindrivenarchitecture.provs.desktop.domain.DesktopType
|
||||
import org.domaindrivenarchitecture.provs.desktop.domain.provisionDesktop
|
||||
import org.domaindrivenarchitecture.provs.desktop.infrastructure.getConfig
|
||||
import org.domaindrivenarchitecture.provs.framework.core.*
|
||||
import org.domaindrivenarchitecture.provs.framework.core.cli.getPasswordToConfigureSudoWithoutPassword
|
||||
import org.domaindrivenarchitecture.provs.framework.core.cli.quit
|
||||
import org.domaindrivenarchitecture.provs.framework.core.processors.DummyProcessor
|
||||
import org.domaindrivenarchitecture.provs.test.setRootLoggingLevel
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.PrintStream
|
||||
|
||||
|
@ -91,6 +96,9 @@ internal class ApplicationKtTest {
|
|||
|
||||
@Test
|
||||
fun prints_error_message_if_config_not_found() {
|
||||
mockkStatic(::quit)
|
||||
every { quit(any()) } throws RuntimeException("mockked")
|
||||
|
||||
// given
|
||||
setRootLoggingLevel(Level.OFF)
|
||||
|
||||
|
@ -103,21 +111,28 @@ internal class ApplicationKtTest {
|
|||
System.setErr(PrintStream(errContent))
|
||||
|
||||
// when
|
||||
main(arrayOf("basic", "someuser@remotehost", "-c", "idontexist.yaml"))
|
||||
assertThrows<RuntimeException> {
|
||||
main(arrayOf("basic", "someuser@remotehost", "-c", "idontexist.yaml"))
|
||||
}
|
||||
|
||||
// then
|
||||
System.setOut(originalOut)
|
||||
System.setErr(originalErr)
|
||||
|
||||
val expectedOutput =
|
||||
"Error: File\u001B[31m idontexist.yaml \u001B[0m was not found.Pls copy file \u001B[31m desktop-config-example.yaml \u001B[0m to file \u001B[31m idontexist.yaml \u001B[0m and change the content according to your needs."
|
||||
"Error: File\u001B[31m idontexist.yaml \u001B[0m was not found.Pls copy file \u001B[31m desktop-config-example.yaml \u001B[0m to file \u001B[31m idontexist.yaml \u001B[0m and change the content according to your needs.No suitable config found."
|
||||
assertEquals(expectedOutput, outContent.toString().replace("\r", "").replace("\n", ""))
|
||||
|
||||
verify(exactly = 0) { any<Prov>().provisionDesktop(any(), any(), any(), any(), any(), any()) }
|
||||
|
||||
unmockkStatic(::quit)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun prints_error_message_if_config_not_parsable() {
|
||||
mockkStatic(::quit)
|
||||
every { quit(any()) } throws RuntimeException("mockked")
|
||||
|
||||
// given
|
||||
setRootLoggingLevel(Level.OFF)
|
||||
|
||||
|
@ -130,16 +145,20 @@ internal class ApplicationKtTest {
|
|||
System.setErr(PrintStream(errContent))
|
||||
|
||||
// when
|
||||
main(arrayOf("basic", "someuser@remotehost", "-c", "src/test/resources/invalid-desktop-config.yaml"))
|
||||
assertThrows<RuntimeException> {
|
||||
main(arrayOf("basic", "someuser@remotehost", "-c", "src/test/resources/invalid-desktop-config.yaml"))
|
||||
}
|
||||
|
||||
// then
|
||||
System.setOut(originalOut)
|
||||
System.setErr(originalErr)
|
||||
|
||||
val expectedOutput =
|
||||
"Error: File \"src/test/resources/invalid-desktop-config.yaml\" has an invalid format and or invalid data."
|
||||
"Error: File \"src/test/resources/invalid-desktop-config.yaml\" has an invalid format and or invalid data.No suitable config found."
|
||||
assertEquals(expectedOutput, outContent.toString().replace("\r", "").replace("\n", ""))
|
||||
|
||||
verify(exactly = 0) { any<Prov>().provisionDesktop(any(), any(), any(), any(), any(), any()) }
|
||||
|
||||
unmockkStatic(::quit)
|
||||
}
|
||||
}
|
|
@ -658,7 +658,12 @@ internal class ProvTest {
|
|||
}
|
||||
optional("sub2c-optional") {
|
||||
taskWithResult("sub3a-taskWithResult") {
|
||||
addResultToEval(ProvResult(false, err = "returned-result - error msg B should be once in output - in addResultToEval"))
|
||||
addResultToEval(
|
||||
ProvResult(
|
||||
false,
|
||||
err = "returned-result - error msg B should be once in output - in addResultToEval"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
requireLast("sub2d-requireLast") {
|
||||
|
@ -668,10 +673,16 @@ internal class ProvTest {
|
|||
}
|
||||
task("sub2e-task") {
|
||||
addResultToEval(ProvResult(true))
|
||||
ProvResult(false, err = "error should NOT be in output as results of task (not taskWithResult) are ignored")
|
||||
ProvResult(
|
||||
false,
|
||||
err = "error should NOT be in output as results of task (not taskWithResult) are ignored"
|
||||
)
|
||||
}
|
||||
taskWithResult("sub2f-taskWithResult") {
|
||||
ProvResult(false, err = "returned-result - error msg C should be once in output - at the end of sub3taskWithResult ")
|
||||
ProvResult(
|
||||
false,
|
||||
err = "returned-result - error msg C should be once in output - at the end of sub3taskWithResult "
|
||||
)
|
||||
}
|
||||
ProvResult(false, err = "returned-result - error msg D should be once in output - at the end of sub1 ")
|
||||
}
|
||||
|
@ -725,5 +736,25 @@ internal class ProvTest {
|
|||
assertEquals(expectedOutput, outContent.toString().replace("\r", ""))
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
fun session_on_top_level_succeeds() {
|
||||
// when
|
||||
val result = Prov.newInstance().session { cmd("echo bla") }
|
||||
// then
|
||||
assertTrue(result.success)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun session_not_on_top_level_throws_an_exception() {
|
||||
// when
|
||||
val exception = org.junit.jupiter.api.assertThrows<RuntimeException> {
|
||||
local().session {
|
||||
session {
|
||||
cmd("echo bla")
|
||||
}
|
||||
}
|
||||
}
|
||||
// then
|
||||
assertEquals("A session can only be created on the top-level and may not be included in another session or task.", exception.message)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue