add prov installation for desktop

This commit is contained in:
jem 2022-02-03 21:11:36 +01:00
parent 89c0fba39e
commit 83d590f1c9
12 changed files with 150 additions and 80 deletions

View file

@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="provs-desktop" type="JetRunConfigurationType">
<option name="MAIN_CLASS_NAME" value="org.domaindrivenarchitecture.provs.desktop.application.ApplicationKt" />
<module name="provs.main" />
<option name="PROGRAM_PARAMETERS" value="-s provs -l myIdeConfig.yaml" />
<shortenClasspath name="NONE" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View file

@ -2,7 +2,7 @@
<configuration default="false" name="provs-server statistics.prod" type="JetRunConfigurationType"> <configuration default="false" name="provs-server statistics.prod" type="JetRunConfigurationType">
<option name="MAIN_CLASS_NAME" value="org.domaindrivenarchitecture.provs.server.application.ApplicationKt" /> <option name="MAIN_CLASS_NAME" value="org.domaindrivenarchitecture.provs.server.application.ApplicationKt" />
<module name="provs.main" /> <module name="provs.main" />
<option name="PROGRAM_PARAMETERS" value="k3s -r statistics.prod.meissa-gmbh.de -u root -k myServerKonfig.yaml" /> <option name="PROGRAM_PARAMETERS" value="k3s -r statistics.prod.meissa-gmbh.de -u root -k myK3sServerConfig.yaml" />
<shortenClasspath name="NONE" /> <shortenClasspath name="NONE" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />

View file

@ -1,9 +1,12 @@
package org.domaindrivenarchitecture.provs.desktop.application package org.domaindrivenarchitecture.provs.desktop.application
import kotlinx.cli.ArgType import kotlinx.cli.ArgType
import kotlinx.cli.default
import kotlinx.cli.multiple
import org.domaindrivenarchitecture.provs.configuration.application.CliTargetParser import org.domaindrivenarchitecture.provs.configuration.application.CliTargetParser
import org.domaindrivenarchitecture.provs.configuration.domain.ConfigFileName import org.domaindrivenarchitecture.provs.configuration.domain.ConfigFileName
import org.domaindrivenarchitecture.provs.configuration.domain.TargetCliCommand import org.domaindrivenarchitecture.provs.configuration.domain.TargetCliCommand
import org.domaindrivenarchitecture.provs.desktop.domain.Scope
open class CliArgumentsParser(name: String) : CliTargetParser(name) { open class CliArgumentsParser(name: String) : CliTargetParser(name) {
@ -14,12 +17,19 @@ open class CliArgumentsParser(name: String) : CliTargetParser(name) {
"the filename containing the yaml config for the desktop" "the filename containing the yaml config for the desktop"
) )
val scopes by option (
type = ArgType.Choice<Scope>(),
shortName = "s",
fullName = "scope",
description = "only provision component in scope."
).multiple()
fun parseWorkplaceArguments(args: Array<String>): DesktopCliCommand { fun parseWorkplaceArguments(args: Array<String>): DesktopCliCommand {
super.parse(args) super.parse(args)
return DesktopCliCommand( return DesktopCliCommand(
ConfigFileName(configFileName), ConfigFileName(configFileName),
scopes,
TargetCliCommand( TargetCliCommand(
localHost, localHost,
remoteHost, remoteHost,

View file

@ -2,12 +2,20 @@ package org.domaindrivenarchitecture.provs.desktop.application
import org.domaindrivenarchitecture.provs.configuration.domain.ConfigFileName import org.domaindrivenarchitecture.provs.configuration.domain.ConfigFileName
import org.domaindrivenarchitecture.provs.configuration.domain.TargetCliCommand import org.domaindrivenarchitecture.provs.configuration.domain.TargetCliCommand
import org.domaindrivenarchitecture.provs.desktop.domain.Scope
class DesktopCliCommand(val configFile: ConfigFileName, val target: TargetCliCommand) { class DesktopCliCommand(
val configFile: ConfigFileName,
val scopes: List<Scope>,
val target: TargetCliCommand, ) {
fun isValid(): Boolean { fun isValid(): Boolean {
return configFile.fileName.isNotEmpty() && target.isValid() return configFile.fileName.isNotEmpty() && target.isValid()
} }
fun haScope(): Boolean {
return scopes.isNotEmpty()
}
} }

View file

@ -14,12 +14,13 @@ import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base.gpgFingerpr
import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.provisionKeys import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.provisionKeys
import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.currentUserCanSudo import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.currentUserCanSudo
import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.whoami import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.whoami
import org.domaindrivenarchitecture.provs.framework.ubuntu.web.base.downloadFromURL
fun provisionDesktop(prov: Prov, cmd: DesktopCliCommand) { fun provisionDesktop(prov: Prov, cmd: DesktopCliCommand) {
// retrieve config // retrieve config
val conf = getConfig(cmd.configFile.fileName) val conf = getConfig(cmd.configFile.fileName)
with(conf) { with(conf) {
prov.provisionWorkplace(type, ssh?.keyPair(), gpg?.keyPair(), gitUserName, gitEmail) prov.provisionWorkplace(type, ssh?.keyPair(), gpg?.keyPair(), gitUserName, gitEmail, cmd)
} }
} }
@ -36,13 +37,35 @@ fun Prov.provisionWorkplace(
ssh: KeyPair? = null, ssh: KeyPair? = null,
gpg: KeyPair? = null, gpg: KeyPair? = null,
gitUserName: String? = null, gitUserName: String? = null,
gitEmail: String? = null gitEmail: String? = null,
cmd: DesktopCliCommand
) = requireAll { ) = requireAll {
if (!currentUserCanSudo()) { if (!currentUserCanSudo()) {
throw Exception("Current user ${whoami()} cannot execute sudo without entering a password! This is necessary to execute provisionWorkplace") throw Exception("Current user ${whoami()} cannot execute sudo without entering a password! This is necessary to execute provisionWorkplace")
} }
if (cmd.haScope()) {
if (cmd.scopes.contains(Scope.PROVS)) {
downloadFromURL(
url="https://gitlab.com/domaindrivenarchitecture/provs/-/jobs/2046149473/artifacts/file/build/libs/provs-server.jar",
filename = "provs-server.jar",
path = "/usr/local/bin/",
sha256sum = "cec1c8762ce310694bacef587ad26b3bb7b8482a8548330ccaf9c9d3eb052409",
sudo = true
)
downloadFromURL(
url="https://gitlab.com/domaindrivenarchitecture/provs/-/jobs/2046149473/artifacts/file/build/libs/provs-desktop.jar",
filename = "provs-desktop.jar",
path = "/usr/local/bin/",
sha256sum = "61bad1380809325aca95bfbcb7cf27928ee070ed886c5de7e300797961d1fa58",
sudo = true
)
cmd("chmod 755 /usr/local/bin/provs-server.jar" , sudo = true)
cmd("chmod 755 /usr/local/bin/provs-desktop.jar", sudo = true)
}
ProvResult(true)
} else {
aptInstall(KEY_MANAGEMENT) aptInstall(KEY_MANAGEMENT)
aptInstall(VERSION_MANAGEMENT) aptInstall(VERSION_MANAGEMENT)
aptInstall(NETWORK_TOOLS) aptInstall(NETWORK_TOOLS)
@ -108,6 +131,6 @@ fun Prov.provisionWorkplace(
installPython() installPython()
} }
ProvResult(true)
ProvResult(true) // dummy }
} }

View file

@ -0,0 +1,5 @@
package org.domaindrivenarchitecture.provs.desktop.domain
enum class Scope {
PROVS
}

View file

@ -13,12 +13,15 @@ import org.domaindrivenarchitecture.provs.server.infrastructure.k3s.getK3sConfig
fun Prov.provisionK3s(configFileName: ConfigFileName?) = task { fun Prov.provisionK3s(configFileName: ConfigFileName?) = task {
val k3sConfig: K3sConfig = getK3sConfig(configFileName!!) val k3sConfig: K3sConfig = getK3sConfig(configFileName!!)
provisionNetwork(loopbackIpv4 = k3sConfig.loopback.ipv4, loopbackIpv6 = k3sConfig.loopback.ipv6!!) provisionNetwork(loopbackIpv4 = k3sConfig.loopback.ipv4, loopbackIpv6 = k3sConfig.loopback.ipv6)
if (k3sConfig.reprovision && testConfigExists()) { if (k3sConfig.reprovision && testConfigExists()) {
deprovisionK3sInfra() deprovisionK3sInfra()
} }
provisionK3sInfra(tlsName = k3sConfig.fqdn, nodeIpv4 = k3sConfig.node.ipv4, nodeIpv6 = k3sConfig.node.ipv6, provisionK3sInfra(
loopbackIpv4 = k3sConfig.loopback.ipv4, loopbackIpv6 = k3sConfig.loopback.ipv6) tlsName = k3sConfig.fqdn, nodeIpv4 = k3sConfig.node.ipv4, nodeIpv6 = k3sConfig.node.ipv6,
loopbackIpv4 = k3sConfig.loopback.ipv4, loopbackIpv6 = k3sConfig.loopback.ipv6
)
provisionK3sCertManager(k3sConfig.letsencryptEndpoint) provisionK3sCertManager(k3sConfig.letsencryptEndpoint)
provisionK3sApple(k3sConfig.fqdn, k3sConfig.letsencryptEndpoint) provisionK3sApple(k3sConfig.fqdn, k3sConfig.letsencryptEndpoint)
} }

View file

@ -36,18 +36,19 @@ fun Prov.deprovisionK3sInfra() = task {
* If docker is true, then docker will be installed (may conflict if docker is already existing) and k3s will be installed with docker option. * If docker is true, then docker will be installed (may conflict if docker is already existing) and k3s will be installed with docker option.
* If tlsHost is specified, then tls (if configured) also applies to the specified host. * If tlsHost is specified, then tls (if configured) also applies to the specified host.
*/ */
fun Prov.provisionK3sInfra(tlsName: String, nodeIpv4: String, loopbackIpv4: String, loopbackIpv6: String, fun Prov.provisionK3sInfra(tlsName: String, nodeIpv4: String, loopbackIpv4: String, loopbackIpv6: String?,
nodeIpv6: String? = null) = task { nodeIpv6: String? = null) = task {
val isDualStack = nodeIpv6?.isNotEmpty() ?: false val isDualStack = nodeIpv6 != null && loopbackIpv6 != null
if (!testConfigExists()) { if (!testConfigExists()) {
createDirs(k3sAutomatedManifestsDir, sudo = true) createDirs(k3sAutomatedManifestsDir, sudo = true)
createDirs(k3sManualManifestsDir, sudo = true) createDirs(k3sManualManifestsDir, sudo = true)
var k3sConfigFileName = "config" var k3sConfigFileName = "config"
var k3sConfigMap: Map<String, String> = mapOf("loopback_ipv4" to loopbackIpv4, "loopback_ipv6" to loopbackIpv6, var k3sConfigMap: Map<String, String> = mapOf("loopback_ipv4" to loopbackIpv4,
"node_ipv4" to nodeIpv4, "tls_name" to tlsName) "node_ipv4" to nodeIpv4, "tls_name" to tlsName)
if (isDualStack) { if (isDualStack) {
k3sConfigFileName += ".dual.template.yaml" k3sConfigFileName += ".dual.template.yaml"
k3sConfigMap = k3sConfigMap.plus("node_ipv6" to nodeIpv6!!) k3sConfigMap = k3sConfigMap.plus("node_ipv6" to nodeIpv6!!)
.plus("loopback_ipv6" to loopbackIpv6!!)
} else { } else {
k3sConfigFileName += ".ipv4.template.yaml" k3sConfigFileName += ".ipv4.template.yaml"
} }

View file

@ -12,16 +12,21 @@ fun Prov.testNetworkExists(): Boolean {
return fileExists(loopbackFile) return fileExists(loopbackFile)
} }
fun Prov.provisionNetwork(loopbackIpv4: String, loopbackIpv6: String) = task { fun Prov.provisionNetwork(loopbackIpv4: String, loopbackIpv6: String?) = task {
val isDualStack = loopbackIpv6?.isNotEmpty() ?: false
if(!testNetworkExists()) { if(!testNetworkExists()) {
if(isDualStack) {
createFileFromResourceTemplate( createFileFromResourceTemplate(
loopbackFile, loopbackFile,
"99-loopback.yaml.template", "99-loopback.yaml.template",
resourcePath, resourcePath,
mapOf("loopback_ipv4" to loopbackIpv4, "loopback_ipv6" to loopbackIpv6), mapOf("loopback_ipv4" to loopbackIpv4, "loopback_ipv6" to loopbackIpv6!!),
"644", "644",
sudo = true sudo = true
) )
} else {
}
cmd("netplan apply", sudo = true) cmd("netplan apply", sudo = true)
} else { } else {
ProvResult(true) ProvResult(true)

View file

@ -40,7 +40,7 @@ internal class CliWorkplaceKtTest {
every { getConfig("testconfig.yaml") } returns testConfig every { getConfig("testconfig.yaml") } returns testConfig
mockkStatic(Prov::provisionWorkplace) mockkStatic(Prov::provisionWorkplace)
every { any<Prov>().provisionWorkplace(any(), any(), any(), any(), any()) } returns ProvResult( every { any<Prov>().provisionWorkplace(any(), any(), any(), any(), any(), cmd) } returns ProvResult(
true, true,
cmd = "mocked command" cmd = "mocked command"
) )
@ -72,7 +72,8 @@ internal class CliWorkplaceKtTest {
null, null,
null, null,
testConfig.gitUserName, testConfig.gitUserName,
testConfig.gitEmail testConfig.gitEmail,
cmd
) )
} }
} }
@ -91,7 +92,8 @@ internal class CliWorkplaceKtTest {
null, null,
null, null,
testConfig.gitUserName, testConfig.gitUserName,
testConfig.gitEmail testConfig.gitEmail,
cmd
) )
} }
} }
@ -119,7 +121,7 @@ internal class CliWorkplaceKtTest {
val expectedOutput = "Error: File\u001B[31m ConfigFileName(fileName=idontexist.yaml) \u001B[0m was not found.Pls copy file \u001B[31m WorkplaceConfigExample.yaml \u001B[0m to file \u001B[31m ConfigFileName(fileName=idontexist.yaml) \u001B[0m and change the content according to your needs." val expectedOutput = "Error: File\u001B[31m ConfigFileName(fileName=idontexist.yaml) \u001B[0m was not found.Pls copy file \u001B[31m WorkplaceConfigExample.yaml \u001B[0m to file \u001B[31m ConfigFileName(fileName=idontexist.yaml) \u001B[0m and change the content according to your needs."
assertEquals(expectedOutput, outContent.toString().replace("\r", "").replace("\n", "")) assertEquals(expectedOutput, outContent.toString().replace("\r", "").replace("\n", ""))
verify(exactly = 0) { any<Prov>().provisionWorkplace(any()) } verify(exactly = 0) { any<Prov>().provisionWorkplace(any(), cmd = cmd) }
} }
@Test @Test
@ -145,6 +147,6 @@ internal class CliWorkplaceKtTest {
val expectedOutput = "Error: File \"ConfigFileName(fileName=src/test/resources/InvalidWorkplaceConfig.yaml)\" has an invalid format and or invalid data." val expectedOutput = "Error: File \"ConfigFileName(fileName=src/test/resources/InvalidWorkplaceConfig.yaml)\" has an invalid format and or invalid data."
assertEquals(expectedOutput, outContent.toString().replace("\r", "").replace("\n", "")) assertEquals(expectedOutput, outContent.toString().replace("\r", "").replace("\n", ""))
verify(exactly = 0) { any<Prov>().provisionWorkplace(any()) } verify(exactly = 0) { any<Prov>().provisionWorkplace(any(), cmd = cmd) }
} }
} }

View file

@ -21,7 +21,8 @@ internal class ProvisionWorkplaceKtTest {
val res = a.provisionWorkplace( val res = a.provisionWorkplace(
WorkplaceType.MINIMAL, WorkplaceType.MINIMAL,
gitUserName = "testuser", gitUserName = "testuser",
gitEmail = "testuser@test.org" gitEmail = "testuser@test.org",
cmd = cmd
) )
// then // then
@ -44,6 +45,7 @@ internal class ProvisionWorkplaceKtTest {
config.gpg?.keyPair(), config.gpg?.keyPair(),
config.gitUserName, config.gitUserName,
config.gitEmail, config.gitEmail,
cmd,
) )
// then // then