Merge branch 'master' into MEIS-2538--make-sudo-in-application
This commit is contained in:
commit
b36f2f965a
16 changed files with 230 additions and 86 deletions
|
@ -18,7 +18,7 @@ apply plugin: "kotlinx-serialization"
|
||||||
|
|
||||||
|
|
||||||
group = "org.domaindrivenarchitecture.provs"
|
group = "org.domaindrivenarchitecture.provs"
|
||||||
version = "0.18.2-SNAPSHOT"
|
version = "0.18.4-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
|
@ -106,19 +106,19 @@ fun Prov.provisionMSDesktop(onlyModules: List<String>?) {
|
||||||
fun Prov.provisionOfficeDesktop(onlyModules: List<String>? = null) {
|
fun Prov.provisionOfficeDesktop(onlyModules: List<String>? = null) {
|
||||||
if (onlyModules == null) {
|
if (onlyModules == null) {
|
||||||
aptInstall(ZIP_UTILS)
|
aptInstall(ZIP_UTILS)
|
||||||
|
aptInstall(SPELLCHECKING_DE)
|
||||||
aptInstall(BROWSER)
|
aptInstall(BROWSER)
|
||||||
aptInstall(EMAIL_CLIENT)
|
aptInstall(EMAIL_CLIENT)
|
||||||
installDeltaChat()
|
installDeltaChat()
|
||||||
aptInstall(OFFICE_SUITE)
|
aptInstall(OFFICE_SUITE)
|
||||||
installZimWiki()
|
installZimWiki()
|
||||||
installNextcloudClient()
|
installNextcloudClient()
|
||||||
|
aptInstall(COMPARE_TOOLS)
|
||||||
|
|
||||||
// optional as installation of these tools often fail and they are not considered mandatory
|
// optional as installation of these tools often fail and they are not considered mandatory
|
||||||
optional {
|
optional {
|
||||||
aptInstall(DRAWING_TOOLS)
|
aptInstall(DRAWING_TOOLS)
|
||||||
}
|
}
|
||||||
|
|
||||||
aptInstall(SPELLCHECKING_DE)
|
|
||||||
} else if (onlyModules.contains(DesktopOnlyModule.VERIFY.name.lowercase())) {
|
} else if (onlyModules.contains(DesktopOnlyModule.VERIFY.name.lowercase())) {
|
||||||
verifyOfficeSetup()
|
verifyOfficeSetup()
|
||||||
} else if (onlyModules.contains(DesktopOnlyModule.FIREFOX.name.lowercase())) {
|
} else if (onlyModules.contains(DesktopOnlyModule.FIREFOX.name.lowercase())) {
|
||||||
|
|
|
@ -7,7 +7,9 @@ import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.aptInsta
|
||||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.web.base.downloadFromURL
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.web.base.downloadFromURL
|
||||||
|
|
||||||
|
|
||||||
private const val resourcePath = "org/domaindrivenarchitecture/provs/desktop/infrastructure"
|
private const val RESOURCE_PATH = "org/domaindrivenarchitecture/provs/desktop/infrastructure"
|
||||||
|
private const val KUBE_CONFIG_CONTEXT_SCRIPT = ".bashrc.d/kubectl.sh"
|
||||||
|
|
||||||
|
|
||||||
fun Prov.installDevOps() = task {
|
fun Prov.installDevOps() = task {
|
||||||
installTerraform()
|
installTerraform()
|
||||||
|
@ -41,20 +43,9 @@ fun Prov.installYq(
|
||||||
fun Prov.installKubectlAndTools(): ProvResult = task {
|
fun Prov.installKubectlAndTools(): ProvResult = task {
|
||||||
|
|
||||||
task("installKubectl") {
|
task("installKubectl") {
|
||||||
val kubeConfigFile = ".bashrc.d/kubectl.sh"
|
if (!checkFile(KUBE_CONFIG_CONTEXT_SCRIPT)) {
|
||||||
if (!checkFile(kubeConfigFile)) {
|
installKubectl()
|
||||||
// prerequisites -- see https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
|
configureKubectlBashCompletion()
|
||||||
cmd("sudo apt-get update")
|
|
||||||
aptInstall("apt-transport-https ca-certificates curl")
|
|
||||||
cmd("sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg")
|
|
||||||
cmd("echo \"deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main\" | sudo tee /etc/apt/sources.list.d/kubernetes.list")
|
|
||||||
|
|
||||||
// kubectl and bash completion
|
|
||||||
cmd("sudo apt update")
|
|
||||||
aptInstall("kubectl")
|
|
||||||
cmd("kubectl completion bash >> /etc/bash_completion.d/kubernetes", sudo = true)
|
|
||||||
createDir(".bashrc.d")
|
|
||||||
createFileFromResource(kubeConfigFile, "kubectl.sh", resourcePath)
|
|
||||||
} else {
|
} else {
|
||||||
ProvResult(true, out = "Kubectl already installed")
|
ProvResult(true, out = "Kubectl already installed")
|
||||||
}
|
}
|
||||||
|
@ -63,20 +54,45 @@ fun Prov.installKubectlAndTools(): ProvResult = task {
|
||||||
installDevopsScripts()
|
installDevopsScripts()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Prov.installKubectl(): ProvResult = task {
|
||||||
|
|
||||||
|
// see https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
|
||||||
|
val kubectlVersion = "1.23.0"
|
||||||
|
val tmpDir = "~/tmp"
|
||||||
|
|
||||||
|
// prerequisites -- see https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
|
||||||
|
cmd("sudo apt-get update")
|
||||||
|
aptInstall("apt-transport-https ca-certificates curl")
|
||||||
|
createDir(tmpDir)
|
||||||
|
downloadFromURL(
|
||||||
|
"https://dl.k8s.io/release/v$kubectlVersion/bin/linux/amd64/kubectl",
|
||||||
|
path = tmpDir,
|
||||||
|
// from https://dl.k8s.io/v1.23.0/bin/linux/amd64/kubectl.sha256
|
||||||
|
sha256sum = "2d0f5ba6faa787878b642c151ccb2c3390ce4c1e6c8e2b59568b3869ba407c4f"
|
||||||
|
)
|
||||||
|
cmd("sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl", dir = tmpDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Prov.configureKubectlBashCompletion(): ProvResult = task {
|
||||||
|
cmd("kubectl completion bash >> /etc/bash_completion.d/kubernetes", sudo = true)
|
||||||
|
createDir(".bashrc.d")
|
||||||
|
createFileFromResource(KUBE_CONFIG_CONTEXT_SCRIPT, "kubectl.sh", RESOURCE_PATH)
|
||||||
|
}
|
||||||
|
|
||||||
fun Prov.installDevopsScripts() {
|
fun Prov.installDevopsScripts() {
|
||||||
|
|
||||||
task("install ssh helper") {
|
task("install ssh helper") {
|
||||||
createFileFromResource(
|
createFileFromResource(
|
||||||
"/usr/local/bin/sshu.sh",
|
"/usr/local/bin/sshu.sh",
|
||||||
"sshu.sh",
|
"sshu.sh",
|
||||||
resourcePath,
|
RESOURCE_PATH,
|
||||||
"555",
|
"555",
|
||||||
sudo = true
|
sudo = true
|
||||||
)
|
)
|
||||||
createFileFromResource(
|
createFileFromResource(
|
||||||
"/usr/local/bin/ssht.sh",
|
"/usr/local/bin/ssht.sh",
|
||||||
"ssht.sh",
|
"ssht.sh",
|
||||||
resourcePath,
|
RESOURCE_PATH,
|
||||||
"555",
|
"555",
|
||||||
sudo = true
|
sudo = true
|
||||||
)
|
)
|
||||||
|
@ -87,7 +103,7 @@ fun Prov.installDevopsScripts() {
|
||||||
createFileFromResource(
|
createFileFromResource(
|
||||||
k3sContextFile,
|
k3sContextFile,
|
||||||
"k3s-create-context.sh",
|
"k3s-create-context.sh",
|
||||||
resourcePath,
|
RESOURCE_PATH,
|
||||||
"555",
|
"555",
|
||||||
sudo = true
|
sudo = true
|
||||||
)
|
)
|
||||||
|
@ -98,12 +114,13 @@ fun Prov.installDevopsScripts() {
|
||||||
createFileFromResource(
|
createFileFromResource(
|
||||||
k3sConnectFile,
|
k3sConnectFile,
|
||||||
"k3s-connect.sh",
|
"k3s-connect.sh",
|
||||||
resourcePath,
|
RESOURCE_PATH,
|
||||||
"555",
|
"555",
|
||||||
sudo = true
|
sudo = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Prov.installTerraform(): ProvResult = task {
|
fun Prov.installTerraform(): ProvResult = task {
|
||||||
val dir = "/usr/lib/tfenv/"
|
val dir = "/usr/lib/tfenv/"
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ val NETWORK_TOOLS = "curl wget net-tools"
|
||||||
|
|
||||||
val KEY_MANAGEMENT_GUI = "seahorse"
|
val KEY_MANAGEMENT_GUI = "seahorse"
|
||||||
|
|
||||||
val BROWSER = "firefox chromium-browser"
|
val BROWSER = "chromium-browser" // firefox can be installed by installFirefox
|
||||||
|
|
||||||
val EMAIL_CLIENT = "thunderbird"
|
val EMAIL_CLIENT = "thunderbird"
|
||||||
|
|
||||||
|
@ -39,3 +39,5 @@ val CLOJURE_TOOLS = "leiningen"
|
||||||
val PASSWORD_TOOLS = "pwgen"
|
val PASSWORD_TOOLS = "pwgen"
|
||||||
|
|
||||||
val SCREEN_TOOLS = "scrcpy"
|
val SCREEN_TOOLS = "scrcpy"
|
||||||
|
|
||||||
|
val COMPARE_TOOLS = "meld"
|
||||||
|
|
|
@ -28,8 +28,9 @@ fun Prov.configureVenv(): ProvResult = task {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Prov.installPybuilder(): ProvResult = task {
|
fun Prov.installPybuilder(): ProvResult = task {
|
||||||
cmd("pip3 install pybuilder ddadevops pypandoc mockito coverage unittest-xml-reporting deprecation python_terraform " +
|
cmd("pip3 install pybuilder ddadevops pypandoc mockito coverage unittest-xml-reporting deprecation" +
|
||||||
"boto3")
|
" python_terraform dda_python_terraform boto3 pyyaml ")
|
||||||
|
cmd("pip3 install --upgrade ddadevops")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Prov.installRestClient(): ProvResult = task {
|
fun Prov.installRestClient(): ProvResult = task {
|
||||||
|
|
|
@ -87,15 +87,15 @@ open class Prov protected constructor(
|
||||||
/**
|
/**
|
||||||
* defines a task, which returns the returned result, the results of sub-tasks are not considered
|
* defines a task, which returns the returned result, the results of sub-tasks are not considered
|
||||||
*/
|
*/
|
||||||
fun requireLast(a: Prov.() -> ProvResult): ProvResult {
|
fun requireLast(name: String? = null, a: Prov.() -> ProvResult): ProvResult {
|
||||||
return evaluate(ResultMode.LAST) { a() }
|
return evaluate(ResultMode.LAST, name) { a() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* defines a task, which always returns success
|
* defines a task, which always returns success
|
||||||
*/
|
*/
|
||||||
fun optional(a: Prov.() -> ProvResult): ProvResult {
|
fun optional(name: String? = null, a: Prov.() -> ProvResult): ProvResult {
|
||||||
return evaluate(ResultMode.OPTIONAL) { a() }
|
return evaluate(ResultMode.OPTIONAL, name) { a() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -312,6 +312,15 @@ open class Prov protected constructor(
|
||||||
|
|
||||||
internalResults[resultIndex].provResult = returnValue
|
internalResults[resultIndex].provResult = returnValue
|
||||||
|
|
||||||
|
// Add failure result to output if not yet included,
|
||||||
|
// which is the case if the result was not part of another subtask but created and returned by the lambda itself.
|
||||||
|
// Success results do not need to be added here as they don't change the overall success evaluation,
|
||||||
|
// whereas the failure results may have a useful error message, which should be in the output.
|
||||||
|
// Only direct result objects are added, but not result objects that were passed from a subtask as they are already handled in the subtask.
|
||||||
|
if (!resultOfTaskLambda.success && (resultIndex < internalResults.size - 1) && (resultOfTaskLambda != internalResults[resultIndex + 1].provResult)) {
|
||||||
|
internalResults.add(ResultLine(level + 1, "<<returned result>>", resultOfTaskLambda))
|
||||||
|
}
|
||||||
|
|
||||||
if (level == 0) {
|
if (level == 0) {
|
||||||
endProgress()
|
endProgress()
|
||||||
processor.close()
|
processor.close()
|
||||||
|
@ -322,8 +331,12 @@ open class Prov protected constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the task at the specified index has no subtasks.
|
||||||
|
* I.e. if the task is the last one or if level of the next task is the same or less (which means same level or "higher" in the tree)
|
||||||
|
*/
|
||||||
private fun internalResultIsLeaf(resultIndex: Int): Boolean {
|
private fun internalResultIsLeaf(resultIndex: Int): Boolean {
|
||||||
return !(resultIndex < internalResults.size - 1 && internalResults[resultIndex + 1].level > internalResults[resultIndex].level)
|
return (resultIndex >= internalResults.size - 1 || internalResults[resultIndex].level >= internalResults[resultIndex + 1].level)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ internal fun getCallingMethodName(): String? {
|
||||||
val offsetVal = 1
|
val offsetVal = 1
|
||||||
val exclude = arrayOf("task", "task\$default", "taskWithResult\$default", "taskWithResult", "def", "def\$default", "record", "invoke", "invoke0", "evaluate", "evaluate\$default", )
|
val exclude = arrayOf("task", "task\$default", "taskWithResult\$default", "taskWithResult", "def", "def\$default", "record", "invoke", "invoke0", "evaluate", "evaluate\$default", )
|
||||||
// suffixes are also ignored as method names but will be added as suffix in the evaluation results
|
// suffixes are also ignored as method names but will be added as suffix in the evaluation results
|
||||||
val suffixes = arrayOf("optional", "requireAll", "requireLast", "inContainer")
|
val suffixes = arrayOf("optional", "optional\$default", "requireAll", "requireLast", "requireLast\$default", "inContainer")
|
||||||
|
|
||||||
var suffix = ""
|
var suffix = ""
|
||||||
val callingFrame = Thread.currentThread().stackTrace
|
val callingFrame = Thread.currentThread().stackTrace
|
||||||
|
@ -30,7 +30,7 @@ internal fun getCallingMethodName(): String? {
|
||||||
var inc = 0
|
var inc = 0
|
||||||
while ((method in exclude) or (method in suffixes)) {
|
while ((method in exclude) or (method in suffixes)) {
|
||||||
if (method in suffixes && suffix == "") {
|
if (method in suffixes && suffix == "") {
|
||||||
suffix = method
|
suffix = method.split("$")[0]
|
||||||
}
|
}
|
||||||
inc++
|
inc++
|
||||||
method = callingFrame[i + offsetVal + inc].methodName
|
method = callingFrame[i + offsetVal + inc].methodName
|
||||||
|
|
|
@ -45,10 +45,9 @@ fun Prov.gitClone(
|
||||||
fun Prov.trustGithub() = task {
|
fun Prov.trustGithub() = task {
|
||||||
// current fingerprints from https://docs.github.com/en/github/authenticating-to-github/githubs-ssh-key-fingerprints
|
// current fingerprints from https://docs.github.com/en/github/authenticating-to-github/githubs-ssh-key-fingerprints
|
||||||
val fingerprints = setOf(
|
val fingerprints = setOf(
|
||||||
"SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8 github.com", // (RSA)
|
"SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s github.com", // (RSA)
|
||||||
// supported beginning September 14, 2021:
|
|
||||||
"SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM github.com", // (ECDSA)
|
"SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM github.com", // (ECDSA)
|
||||||
"SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU github.com" // (Ed25519)
|
"SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU github.com" // (Ed25519)
|
||||||
)
|
)
|
||||||
trustHost("github.com", fingerprints)
|
trustHost("github.com", fingerprints)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.domaindrivenarchitecture.provs.desktop.domain
|
package org.domaindrivenarchitecture.provs.desktop.domain
|
||||||
|
|
||||||
import org.domaindrivenarchitecture.provs.desktop.infrastructure.getConfig
|
|
||||||
import org.domaindrivenarchitecture.provs.framework.core.ProgressType
|
import org.domaindrivenarchitecture.provs.framework.core.ProgressType
|
||||||
import org.domaindrivenarchitecture.provs.framework.core.Prov
|
import org.domaindrivenarchitecture.provs.framework.core.Prov
|
||||||
import org.domaindrivenarchitecture.provs.framework.core.docker.provideContainer
|
import org.domaindrivenarchitecture.provs.framework.core.docker.provideContainer
|
||||||
|
@ -46,6 +45,7 @@ internal class DesktopServiceKtTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExtensiveContainerTest
|
@ExtensiveContainerTest
|
||||||
|
@Disabled("Takes very long, enable if you want to test a desktop setup")
|
||||||
fun provisionDesktop() {
|
fun provisionDesktop() {
|
||||||
// given
|
// given
|
||||||
val prov = defaultTestContainer()
|
val prov = defaultTestContainer()
|
||||||
|
@ -87,28 +87,6 @@ internal class DesktopServiceKtTest {
|
||||||
// then
|
// then
|
||||||
assertTrue(res.success)
|
assertTrue(res.success)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ExtensiveContainerTest
|
|
||||||
fun provisionDesktopFromConfigFile() {
|
|
||||||
// given
|
|
||||||
val prov = defaultTestContainer()
|
|
||||||
|
|
||||||
// when
|
|
||||||
// in order to test DesktopType.OFFICE: fix installing libreoffice for a fresh container as it hangs the first time but succeeds 2nd time
|
|
||||||
val config = getConfig("src/test/resources/desktop-config-example.json")
|
|
||||||
val res = prov.provisionDesktop(
|
|
||||||
DesktopType.BASIC,
|
|
||||||
config.ssh?.keyPair(),
|
|
||||||
config.gpg?.keyPair(),
|
|
||||||
config.gitUserName,
|
|
||||||
config.gitEmail,
|
|
||||||
onlyModules = null
|
|
||||||
)
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertTrue(res.success)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.fileC
|
||||||
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
||||||
import org.domaindrivenarchitecture.provs.test.tags.ExtensiveContainerTest
|
import org.domaindrivenarchitecture.provs.test.tags.ExtensiveContainerTest
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
import org.junit.jupiter.api.Disabled
|
||||||
|
|
||||||
internal class DevOpsKtTest {
|
internal class DevOpsKtTest {
|
||||||
|
|
||||||
|
@ -34,4 +35,17 @@ internal class DevOpsKtTest {
|
||||||
defaultTestContainer().checkFile("/etc/bash_completion.d/kubernetes", sudo = true)
|
defaultTestContainer().checkFile("/etc/bash_completion.d/kubernetes", sudo = true)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtensiveContainerTest
|
||||||
|
@Disabled("Part of test installKubectlAndTools, but can be tested separately by this test if required")
|
||||||
|
fun installKubectl() {
|
||||||
|
// given
|
||||||
|
val prov = defaultTestContainer()
|
||||||
|
|
||||||
|
// when
|
||||||
|
val res = prov.installKubectl()
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertTrue(res.success)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,9 @@ import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.isPackag
|
||||||
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
||||||
import org.domaindrivenarchitecture.provs.test.tags.ExtensiveContainerTest
|
import org.domaindrivenarchitecture.provs.test.tags.ExtensiveContainerTest
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
internal class FirefoxKtTest {
|
internal class FirefoxKtTest {
|
||||||
|
|
||||||
@Test
|
|
||||||
@ExtensiveContainerTest
|
@ExtensiveContainerTest
|
||||||
fun installFirefox() {
|
fun installFirefox() {
|
||||||
// when
|
// when
|
||||||
|
|
|
@ -254,12 +254,12 @@ internal class ProvTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// given
|
// additional methods to be used in the tests below
|
||||||
fun Prov.checkPrereq_evaluateToFailure() = requireLast {
|
fun Prov.checkPrereq_evaluateToFailure() = requireLast {
|
||||||
ProvResult(false, err = "This is a test error.")
|
ProvResult(false, err = "This is a test error.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Prov.methodThatProvidesSomeOutput() = requireLast {
|
fun Prov.testMethodForOutputTest_with_mode_requireLast() = requireLast {
|
||||||
|
|
||||||
if (!checkPrereq_evaluateToFailure().success) {
|
if (!checkPrereq_evaluateToFailure().success) {
|
||||||
sh(
|
sh(
|
||||||
|
@ -273,6 +273,17 @@ internal class ProvTest {
|
||||||
sh("echo -End test-")
|
sh("echo -End test-")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Prov.testMethodForOutputTest_nested_with_failure() = taskWithResult {
|
||||||
|
|
||||||
|
taskWithResult(name = "sub1") {
|
||||||
|
taskWithResult {
|
||||||
|
ProvResult(true)
|
||||||
|
}
|
||||||
|
ProvResult(false, err = "Iamanerrormessage")
|
||||||
|
}
|
||||||
|
cmd("echo -End test-")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@NonCi
|
@NonCi
|
||||||
fun prov_prints_correct_output_for_overall_success() {
|
fun prov_prints_correct_output_for_overall_success() {
|
||||||
|
@ -290,7 +301,7 @@ internal class ProvTest {
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Prov.newInstance(name = "test instance with no progress info", progressType = ProgressType.NONE)
|
Prov.newInstance(name = "test instance with no progress info", progressType = ProgressType.NONE)
|
||||||
.methodThatProvidesSomeOutput()
|
.testMethodForOutputTest_with_mode_requireLast()
|
||||||
|
|
||||||
// then
|
// then
|
||||||
System.setOut(originalOut)
|
System.setOut(originalOut)
|
||||||
|
@ -300,7 +311,7 @@ internal class ProvTest {
|
||||||
|
|
||||||
val expectedOutput =
|
val expectedOutput =
|
||||||
"============================================== SUMMARY (test instance with no progress info) =============================================\n" +
|
"============================================== SUMMARY (test instance with no progress info) =============================================\n" +
|
||||||
"> \u001B[92mSuccess\u001B[0m -- methodThatProvidesSomeOutput (requireLast) \n" +
|
"> \u001B[92mSuccess\u001B[0m -- testMethodForOutputTest_with_mode_requireLast (requireLast) \n" +
|
||||||
"---> \u001B[93mFAILED\u001B[0m -- checkPrereq_evaluateToFailure (requireLast) -- Error: This is a test error.\n" +
|
"---> \u001B[93mFAILED\u001B[0m -- checkPrereq_evaluateToFailure (requireLast) -- Error: This is a test error.\n" +
|
||||||
"---> \u001B[92mSuccess\u001B[0m -- sh \n" +
|
"---> \u001B[92mSuccess\u001B[0m -- sh \n" +
|
||||||
"------> \u001B[92mSuccess\u001B[0m -- cmd [/bin/bash, -c, echo -Start test-]\n" +
|
"------> \u001B[92mSuccess\u001B[0m -- cmd [/bin/bash, -c, echo -Start test-]\n" +
|
||||||
|
@ -317,7 +328,7 @@ internal class ProvTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@NonCi
|
@NonCi
|
||||||
fun prov_prints_correct_output_for_failure() {
|
fun prov_prints_correct_output_for_nested_calls_with_failure() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
setRootLoggingLevel(Level.OFF)
|
setRootLoggingLevel(Level.OFF)
|
||||||
|
@ -332,7 +343,7 @@ internal class ProvTest {
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Prov.newInstance(name = "test instance with no progress info", progressType = ProgressType.NONE)
|
Prov.newInstance(name = "test instance with no progress info", progressType = ProgressType.NONE)
|
||||||
.checkPrereq_evaluateToFailure()
|
.testMethodForOutputTest_nested_with_failure()
|
||||||
|
|
||||||
// then
|
// then
|
||||||
System.setOut(originalOut)
|
System.setOut(originalOut)
|
||||||
|
@ -342,7 +353,13 @@ internal class ProvTest {
|
||||||
|
|
||||||
val expectedOutput =
|
val expectedOutput =
|
||||||
"============================================== SUMMARY (test instance with no progress info) =============================================\n" +
|
"============================================== SUMMARY (test instance with no progress info) =============================================\n" +
|
||||||
"> \u001B[91mFAILED\u001B[0m -- checkPrereq_evaluateToFailure (requireLast) -- Error: This is a test error.\n" +
|
"> \u001B[91mFAILED\u001B[0m -- testMethodForOutputTest_nested_with_failure \n" +
|
||||||
|
"---> \u001B[91mFAILED\u001B[0m -- sub1 \n" +
|
||||||
|
"------> \u001B[92mSuccess\u001B[0m -- testMethodForOutputTest_nested_with_failure \n" +
|
||||||
|
"------> \u001B[91mFAILED\u001B[0m -- <<returned result>> -- Error: Iamanerrormessage\n" +
|
||||||
|
"---> \u001B[92mSuccess\u001B[0m -- cmd [/bin/bash, -c, echo -End test-]\n" +
|
||||||
|
"----------------------------------------------------------------------------------------------------\n" +
|
||||||
|
"Overall > \u001B[91mFAILED\u001B[0m \n" +
|
||||||
"============================================ SUMMARY END ===========================================\n" +
|
"============================================ SUMMARY END ===========================================\n" +
|
||||||
"\n"
|
"\n"
|
||||||
|
|
||||||
|
@ -603,7 +620,7 @@ internal class ProvTest {
|
||||||
|
|
||||||
val prov = Prov.newInstance(name = "test instance with no progress info", progressType = ProgressType.NONE)
|
val prov = Prov.newInstance(name = "test instance with no progress info", progressType = ProgressType.NONE)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
prov.task {
|
prov.task {
|
||||||
addInfoText("Text1")
|
addInfoText("Text1")
|
||||||
addInfoText("Text2\nwith newline")
|
addInfoText("Text2\nwith newline")
|
||||||
|
@ -629,5 +646,84 @@ internal class ProvTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// method to be used in the next test
|
||||||
|
fun Prov.testMethodForOutputTest_with_returned_results() = taskWithResult {
|
||||||
|
|
||||||
|
taskWithResult(name = "sub1") {
|
||||||
|
taskWithResult("sub2a") {
|
||||||
|
ProvResult(true)
|
||||||
|
}
|
||||||
|
taskWithResult("sub2b") {
|
||||||
|
ProvResult(false, err = "error msg A for sub2b should be shown as result of sub2b")
|
||||||
|
}
|
||||||
|
optional("sub2c-optional") {
|
||||||
|
taskWithResult("sub3a-taskWithResult") {
|
||||||
|
addResultToEval(ProvResult(false, err = "returned-result - error msg B should be once in output - in addResultToEval"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requireLast("sub2d-requireLast") {
|
||||||
|
taskWithResult("sub3b-taskWithResult without error message") {
|
||||||
|
ProvResult(false) // no error message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
task("sub2e-task") {
|
||||||
|
addResultToEval(ProvResult(true))
|
||||||
|
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 D should be once in output - at the end of sub1 ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@NonCi
|
||||||
|
fun prov_prints_correct_output_for_returned_results() {
|
||||||
|
|
||||||
|
// given
|
||||||
|
setRootLoggingLevel(Level.OFF)
|
||||||
|
|
||||||
|
val outContent = ByteArrayOutputStream()
|
||||||
|
val errContent = ByteArrayOutputStream()
|
||||||
|
val originalOut = System.out
|
||||||
|
val originalErr = System.err
|
||||||
|
|
||||||
|
System.setOut(PrintStream(outContent))
|
||||||
|
System.setErr(PrintStream(errContent))
|
||||||
|
|
||||||
|
// when
|
||||||
|
Prov.newInstance(name = "test instance with no progress info", progressType = ProgressType.NONE)
|
||||||
|
.testMethodForOutputTest_with_returned_results()
|
||||||
|
|
||||||
|
// then
|
||||||
|
System.setOut(originalOut)
|
||||||
|
System.setErr(originalErr)
|
||||||
|
|
||||||
|
println(outContent.toString())
|
||||||
|
|
||||||
|
val expectedOutput =
|
||||||
|
"============================================== SUMMARY (test instance with no progress info) =============================================\n" +
|
||||||
|
"> \u001B[91mFAILED\u001B[0m -- testMethodForOutputTest_with_returned_results \n" +
|
||||||
|
"---> \u001B[91mFAILED\u001B[0m -- sub1 \n" +
|
||||||
|
"------> \u001B[92mSuccess\u001B[0m -- sub2a \n" +
|
||||||
|
"------> \u001B[91mFAILED\u001B[0m -- sub2b -- Error: error msg A for sub2b should be shown as result of sub2b\n" +
|
||||||
|
"------> \u001B[92mSuccess\u001B[0m -- sub2c-optional \n" +
|
||||||
|
"---------> \u001B[93mFAILED\u001B[0m -- sub3a-taskWithResult \n" +
|
||||||
|
"------------> \u001B[93mFAILED\u001B[0m -- addResultToEval -- Error: returned-result - error msg B should be once in output - in addResultToEval\n" +
|
||||||
|
"------> \u001B[91mFAILED\u001B[0m -- sub2d-requireLast \n" +
|
||||||
|
"---------> \u001B[91mFAILED\u001B[0m -- sub3b-taskWithResult without error message \n" +
|
||||||
|
"------> \u001B[92mSuccess\u001B[0m -- sub2e-task \n" +
|
||||||
|
"---------> \u001B[92mSuccess\u001B[0m -- addResultToEval \n" +
|
||||||
|
"------> \u001B[91mFAILED\u001B[0m -- sub2f-taskWithResult -- Error: returned-result - error msg C should be once in output - at the end of sub3taskWithResult \n" +
|
||||||
|
"------> \u001B[91mFAILED\u001B[0m -- <<returned result>> -- Error: returned-result - error msg D should be once in output - at the end of sub1 \n" +
|
||||||
|
"----------------------------------------------------------------------------------------------------\n" +
|
||||||
|
"Overall > \u001B[91mFAILED\u001B[0m \n" +
|
||||||
|
"============================================ SUMMARY END ===========================================\n" +
|
||||||
|
"\n"
|
||||||
|
|
||||||
|
assertEquals(expectedOutput, outContent.toString().replace("\r", ""))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package org.domaindrivenarchitecture.provs.server.infrastructure
|
package org.domaindrivenarchitecture.provs.server.infrastructure
|
||||||
|
|
||||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.checkFile
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.checkFile
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.deleteFile
|
||||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.fileContainsText
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.fileContainsText
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.aptInstall
|
||||||
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
||||||
import org.domaindrivenarchitecture.provs.test.tags.ExtensiveContainerTest
|
import org.domaindrivenarchitecture.provs.test.tags.ExtensiveContainerTest
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
@ -12,15 +14,21 @@ class SshKtTest {
|
||||||
fun test_configureSsh() {
|
fun test_configureSsh() {
|
||||||
|
|
||||||
// given
|
// given
|
||||||
val p = defaultTestContainer()
|
val prov = defaultTestContainer()
|
||||||
|
prov.task {
|
||||||
|
aptInstall("openssh-server")
|
||||||
|
deleteFile(pathSshdHardeningConfig, sudo = true)
|
||||||
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val res = p.configureSsh()
|
prov.configureSsh()
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(res.success)
|
|
||||||
assertTrue(p.fileContainsText("/etc/ssh/ssh_config","PasswordAuthentication no", sudo=true))
|
// Note: result of method configureSsh might have status failure as restart ssh within docker is not possible,
|
||||||
assertTrue(p.fileContainsText("/etc/ssh/sshd_config","PasswordAuthentication no", sudo=true))
|
// but files should have expected content
|
||||||
assertTrue(p.checkFile("/etc/ssh/sshd_config.d/sshd_hardening.conf"))
|
assertTrue(prov.fileContainsText("/etc/ssh/ssh_config","PasswordAuthentication no", sudo=true))
|
||||||
|
assertTrue(prov.fileContainsText("/etc/ssh/sshd_config","PasswordAuthentication no", sudo=true))
|
||||||
|
assertTrue(prov.checkFile("/etc/ssh/sshd_config.d/sshd_hardening.conf"))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,10 +3,7 @@ package org.domaindrivenarchitecture.provs.syspec.infrastructure
|
||||||
import org.domaindrivenarchitecture.provs.framework.core.ProvResult
|
import org.domaindrivenarchitecture.provs.framework.core.ProvResult
|
||||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createDirs
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createDirs
|
||||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createFile
|
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createFile
|
||||||
import org.domaindrivenarchitecture.provs.syspec.domain.FileSpec
|
import org.domaindrivenarchitecture.provs.syspec.domain.*
|
||||||
import org.domaindrivenarchitecture.provs.syspec.domain.FolderSpec
|
|
||||||
import org.domaindrivenarchitecture.provs.syspec.domain.SocketSpec
|
|
||||||
import org.domaindrivenarchitecture.provs.syspec.domain.SyspecConfig
|
|
||||||
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
||||||
import org.domaindrivenarchitecture.provs.test.tags.ContainerTest
|
import org.domaindrivenarchitecture.provs.test.tags.ContainerTest
|
||||||
import org.domaindrivenarchitecture.provs.test.testLocal
|
import org.domaindrivenarchitecture.provs.test.testLocal
|
||||||
|
@ -131,4 +128,30 @@ internal class VerificationKtTest {
|
||||||
assertFalse(res3)
|
assertFalse(res3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ContainerTest
|
||||||
|
fun test_verifySpecConfig_succeeds() {
|
||||||
|
// given
|
||||||
|
val dir = "/home/testuser"
|
||||||
|
val prov = defaultTestContainer()
|
||||||
|
|
||||||
|
// when
|
||||||
|
val res = prov.verifySpecConfig(SyspecConfig(folder = listOf(FolderSpec(dir)), command = listOf(CommandSpec("echo bla"))))
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertTrue(res.success)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ContainerTest
|
||||||
|
fun test_verifySpecConfig_fails() {
|
||||||
|
// given
|
||||||
|
val dir = "/home/testuser"
|
||||||
|
val prov = defaultTestContainer()
|
||||||
|
|
||||||
|
// when
|
||||||
|
val res = prov.verifySpecConfig(SyspecConfig(command = listOf(CommandSpec("echoo bla"), CommandSpec("echo bla")), folder = listOf(FolderSpec(dir))))
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertFalse(res.success)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"ssh": null,
|
|
||||||
"gpg": null,
|
|
||||||
"gitUserName": "mygitusername",
|
|
||||||
"gitEmail": "my@git.email"
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package org.domaindrivenarchitecture.provs.test
|
||||||
|
|
||||||
import org.domaindrivenarchitecture.provs.framework.core.ProgressType
|
import org.domaindrivenarchitecture.provs.framework.core.ProgressType
|
||||||
import org.domaindrivenarchitecture.provs.framework.core.Prov
|
import org.domaindrivenarchitecture.provs.framework.core.Prov
|
||||||
|
import org.domaindrivenarchitecture.provs.framework.core.docker.containerRuns
|
||||||
import org.domaindrivenarchitecture.provs.framework.core.docker.dockerImageExists
|
import org.domaindrivenarchitecture.provs.framework.core.docker.dockerImageExists
|
||||||
import org.domaindrivenarchitecture.provs.framework.core.docker.dockerProvideImage
|
import org.domaindrivenarchitecture.provs.framework.core.docker.dockerProvideImage
|
||||||
import org.domaindrivenarchitecture.provs.framework.core.docker.dockerimages.UbuntuPlusUser
|
import org.domaindrivenarchitecture.provs.framework.core.docker.dockerimages.UbuntuPlusUser
|
||||||
|
@ -15,7 +16,7 @@ const val defaultTestContainerName = "provs_test"
|
||||||
private lateinit var prov: Prov
|
private lateinit var prov: Prov
|
||||||
|
|
||||||
fun defaultTestContainer(startMode: ContainerStartMode = ContainerStartMode.USE_RUNNING_ELSE_CREATE): Prov {
|
fun defaultTestContainer(startMode: ContainerStartMode = ContainerStartMode.USE_RUNNING_ELSE_CREATE): Prov {
|
||||||
if (!::prov.isInitialized) { prov = initDefaultTestContainer(startMode) }
|
if (!::prov.isInitialized || !testLocal().containerRuns(defaultTestContainerName)) { prov = initDefaultTestContainer(startMode) }
|
||||||
return prov
|
return prov
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue