change cli to new format
This commit is contained in:
parent
4c6d3ba3c3
commit
478f058fd7
11 changed files with 110 additions and 135 deletions
|
@ -6,49 +6,24 @@ import kotlinx.cli.default
|
|||
import org.domaindrivenarchitecture.provs.configuration.domain.TargetCliCommand
|
||||
|
||||
open class CliTargetParser(name: String) : ArgParser(name) {
|
||||
val remoteHost by option(
|
||||
ArgType.String, shortName =
|
||||
"r", description = "provision to remote host - either localHost or remoteHost must be specified"
|
||||
)
|
||||
val localHost by option(
|
||||
ArgType.Boolean, shortName =
|
||||
"l", description = "provision to local machine - either localHost or remoteHost must be specified"
|
||||
)
|
||||
val userName by option(
|
||||
val target by argument(
|
||||
ArgType.String,
|
||||
shortName = "u",
|
||||
description = "user for remote provisioning."
|
||||
description = "target: either 'local' or remote with 'user[:password]@host' (e.g. 'username@somehost.com' without password for ssh-key authorization)",
|
||||
)
|
||||
val sshWithGopassPath by option(
|
||||
ArgType.String,
|
||||
shortName = "p",
|
||||
description = "password stored at gopass path"
|
||||
)
|
||||
val sshWithPasswordPrompt by option(
|
||||
val passwordInteractive by option(
|
||||
ArgType.Boolean,
|
||||
shortName = "i",
|
||||
description = "prompt for password interactive"
|
||||
).default(false)
|
||||
val sshWithKey by option(
|
||||
ArgType.Boolean,
|
||||
shortName = "k",
|
||||
description = "provision over ssh using user & ssh key"
|
||||
"password-interactive",
|
||||
"p",
|
||||
"prompt for password",
|
||||
).default(false)
|
||||
}
|
||||
|
||||
fun parseTarget(
|
||||
programName: String = "java -jar provs.jar",
|
||||
programName: String = "provs",
|
||||
args: Array<String>
|
||||
): TargetCliCommand {
|
||||
val parser = CliTargetParser(programName)
|
||||
parser.parse(args)
|
||||
|
||||
return TargetCliCommand(
|
||||
parser.localHost,
|
||||
parser.remoteHost,
|
||||
parser.userName,
|
||||
parser.sshWithPasswordPrompt,
|
||||
parser.sshWithGopassPath,
|
||||
parser.sshWithKey
|
||||
)
|
||||
return TargetCliCommand(parser.target, parser.passwordInteractive)
|
||||
}
|
|
@ -1,29 +1,64 @@
|
|||
package org.domaindrivenarchitecture.provs.configuration.domain
|
||||
|
||||
import org.domaindrivenarchitecture.provs.framework.core.Secret
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.secret.secretSources.PlainSecretSource
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.secret.secretSources.PromptSecretSource
|
||||
|
||||
|
||||
private const val USER_HOST_DELIMITER = "@"
|
||||
private const val USER_PW_DELIMITER = ":"
|
||||
|
||||
|
||||
class TargetCliCommand(
|
||||
val localHost: Boolean?,
|
||||
val remoteHost: String?,
|
||||
val userName: String?,
|
||||
val sshWithPasswordPrompt: Boolean,
|
||||
val sshWithGopassPath: String?,
|
||||
val sshWithKey: Boolean
|
||||
val target: String,
|
||||
val passwordInteractive: Boolean = false
|
||||
) {
|
||||
fun isValidLocalhost(): Boolean {
|
||||
return (localHost ?: false) && remoteHost == null && userName == null && sshWithGopassPath == null &&
|
||||
!sshWithPasswordPrompt && !sshWithKey
|
||||
private var remoteTarget: RemoteTarget? = null
|
||||
|
||||
init {
|
||||
remoteTarget = parseRemoteTarget()
|
||||
}
|
||||
|
||||
fun hasValidPasswordOption(): Boolean {
|
||||
return (sshWithGopassPath != null) xor sshWithPasswordPrompt xor sshWithKey
|
||||
fun isValidLocalhost(): Boolean {
|
||||
return target == "local"
|
||||
}
|
||||
|
||||
fun isValidRemote(): Boolean {
|
||||
return remoteHost != null && userName != null && hasValidPasswordOption()
|
||||
return (remoteTarget != null)
|
||||
}
|
||||
|
||||
fun isValid(): Boolean {
|
||||
return (isValidLocalhost() || isValidRemote())
|
||||
}
|
||||
|
||||
private fun parseRemoteTarget(): RemoteTarget? {
|
||||
val user: String?
|
||||
val host: String?
|
||||
var password: Secret? = null
|
||||
|
||||
if (!target.contains(USER_HOST_DELIMITER)) {
|
||||
return null
|
||||
}
|
||||
|
||||
host = target.substringAfter(USER_HOST_DELIMITER)
|
||||
|
||||
val userPw = target.substringBefore(USER_HOST_DELIMITER)
|
||||
if (!userPw.contains(USER_PW_DELIMITER)) {
|
||||
user = userPw
|
||||
} else {
|
||||
user = userPw.substringBefore(USER_PW_DELIMITER)
|
||||
password = PlainSecretSource(userPw.substringAfter(USER_PW_DELIMITER)).secret()
|
||||
}
|
||||
if (passwordInteractive) {
|
||||
password = PromptSecretSource("Password for $user on $host").secretNullable()
|
||||
}
|
||||
return RemoteTarget(user, host, password)
|
||||
}
|
||||
|
||||
fun remoteTarget(): RemoteTarget? {
|
||||
return remoteTarget
|
||||
}
|
||||
|
||||
class RemoteTarget(val user: String, val host: String, val password: Secret?)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,12 +25,8 @@ open class CliArgumentsParser(name: String) : CliTargetParser(name) {
|
|||
return DesktopCliCommand(
|
||||
DesktopType.valueOf(module.name.uppercase()),
|
||||
TargetCliCommand(
|
||||
localHost,
|
||||
remoteHost,
|
||||
userName,
|
||||
sshWithPasswordPrompt,
|
||||
sshWithGopassPath,
|
||||
sshWithKey
|
||||
target,
|
||||
passwordInteractive
|
||||
),
|
||||
module.configFileName
|
||||
)
|
||||
|
|
|
@ -5,7 +5,6 @@ import org.domaindrivenarchitecture.provs.framework.core.Prov
|
|||
import org.domaindrivenarchitecture.provs.framework.core.Secret
|
||||
import org.domaindrivenarchitecture.provs.framework.core.local
|
||||
import org.domaindrivenarchitecture.provs.framework.core.remote
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.secret.secretSources.GopassSecretSource
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.secret.secretSources.PromptSecretSource
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.currentUserCanSudo
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.makeUserSudoerWithNoSudoPasswordRequired
|
||||
|
@ -26,15 +25,16 @@ fun createProvInstance(
|
|||
remoteHostSetSudoWithoutPasswordRequired: Boolean = false
|
||||
): Prov {
|
||||
if (targetCommand.isValid()) {
|
||||
val password: Secret? = if (targetCommand.isValidRemote()) retrievePassword(targetCommand) else null
|
||||
val password: Secret? = targetCommand.remoteTarget()?.password
|
||||
|
||||
val remoteTarget = targetCommand.remoteTarget()
|
||||
if (targetCommand.isValidLocalhost()) {
|
||||
return local()
|
||||
} else if (targetCommand.isValidRemote()) {
|
||||
} else if (targetCommand.isValidRemote() && remoteTarget != null) {
|
||||
return createProvInstanceRemote(
|
||||
targetCommand.remoteHost!!,
|
||||
targetCommand.userName!!,
|
||||
targetCommand.sshWithKey,
|
||||
remoteTarget.host,
|
||||
remoteTarget.user,
|
||||
remoteTarget.password == null,
|
||||
password,
|
||||
remoteHostSetSudoWithoutPasswordRequired
|
||||
)
|
||||
|
@ -81,15 +81,13 @@ private fun createProvInstanceRemote(
|
|||
}
|
||||
|
||||
|
||||
// todo: consider removal as password can be retrieved by PromptSecretSource
|
||||
internal fun retrievePassword(cliCommand: TargetCliCommand): Secret? {
|
||||
var password: Secret? = null
|
||||
if (cliCommand.isValidRemote()) {
|
||||
if (cliCommand.sshWithPasswordPrompt) {
|
||||
password =
|
||||
PromptSecretSource("Password for user $cliCommand.userName!! on $cliCommand.remoteHost!!").secret()
|
||||
} else if (cliCommand.sshWithGopassPath != null) {
|
||||
password = GopassSecretSource(cliCommand.sshWithGopassPath).secret()
|
||||
}
|
||||
if (cliCommand.isValidRemote() && cliCommand.passwordInteractive) {
|
||||
password =
|
||||
PromptSecretSource("Password for user $cliCommand.userName!! on $cliCommand.remoteHost!!").secret()
|
||||
|
||||
}
|
||||
return password
|
||||
}
|
||||
|
|
|
@ -24,12 +24,8 @@ class CliArgumentsParser(name: String) : CliTargetParser(name) {
|
|||
return ServerCliCommand(
|
||||
ServerType.valueOf(module.name.uppercase()),
|
||||
TargetCliCommand(
|
||||
localHost,
|
||||
remoteHost,
|
||||
userName,
|
||||
sshWithPasswordPrompt,
|
||||
sshWithGopassPath,
|
||||
sshWithKey
|
||||
target,
|
||||
passwordInteractive
|
||||
),
|
||||
module.configFileName
|
||||
)
|
||||
|
@ -41,14 +37,15 @@ class CliArgumentsParser(name: String) : CliTargetParser(name) {
|
|||
}
|
||||
|
||||
class K3s : ServerSubcommand("k3s", "the k3s module") {
|
||||
val cliConfigFileName by argument(
|
||||
val cliConfigFileName by option(
|
||||
ArgType.String,
|
||||
"configFilename",
|
||||
"config-file",
|
||||
"c",
|
||||
"the filename containing the yaml config for k3s"
|
||||
)
|
||||
|
||||
override fun execute() {
|
||||
super.configFileName = ConfigFileName(cliConfigFileName)
|
||||
super.configFileName = cliConfigFileName?.let { ConfigFileName(it) }
|
||||
super.parsed = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,64 +1,47 @@
|
|||
package org.domaindrivenarchitecture.provs.configuration.application
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class CliTargetParserTest {
|
||||
|
||||
@Test
|
||||
fun parse_localhost_with_default() {
|
||||
val parseCli = parseTarget(args = emptyArray())
|
||||
|
||||
assertFalse(parseCli.isValidLocalhost())
|
||||
assertFalse(parseCli.isValidRemote())
|
||||
assertFalse(parseCli.isValid())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_localhost() {
|
||||
val parseCli = parseTarget(args = arrayOf("-l"))
|
||||
assertTrue(parseCli.isValidLocalhost())
|
||||
assertFalse(parseCli.isValidRemote())
|
||||
assertTrue(parseCli.isValid())
|
||||
val cliCommand = parseTarget(args = arrayOf("local"))
|
||||
assertTrue(cliCommand.isValidLocalhost())
|
||||
assertFalse(cliCommand.isValidRemote())
|
||||
assertTrue(cliCommand.isValid())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_remote_with_missing_passwordoption() {
|
||||
val parseCli = parseTarget(args = arrayOf("-r", "1.2.3.4", "-u", "user"))
|
||||
fun parse_remote_with_given_pasword() {
|
||||
val cliCommand = parseTarget(args = arrayOf("user:mypassword@1.2.3.4"))
|
||||
|
||||
assertFalse(parseCli.isValidLocalhost())
|
||||
assertEquals("1.2.3.4", parseCli.remoteHost)
|
||||
assertEquals("user", parseCli.userName)
|
||||
assertFalse(parseCli.isValidRemote())
|
||||
assertFalse(parseCli.isValid())
|
||||
assertFalse(cliCommand.isValidLocalhost())
|
||||
assertEquals("1.2.3.4", cliCommand.remoteTarget()?.host)
|
||||
assertEquals("user", cliCommand.remoteTarget()?.user)
|
||||
assertEquals("mypassword", cliCommand.remoteTarget()?.password?.plain())
|
||||
assertTrue(cliCommand.isValid())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_remote_with_remote_key() {
|
||||
val parseCli = parseTarget(args = arrayOf("-r", "1.2.3.4", "-u", "user", "-k"))
|
||||
fun parse_remote_with_ssh_key() {
|
||||
val cliCommand = parseTarget(args = arrayOf("user@1.2.3.4"))
|
||||
|
||||
assertFalse(parseCli.isValidLocalhost())
|
||||
assertEquals("1.2.3.4", parseCli.remoteHost)
|
||||
assertEquals("user", parseCli.userName)
|
||||
assertTrue(parseCli.isValid())
|
||||
assertFalse(cliCommand.isValidLocalhost())
|
||||
assertEquals("1.2.3.4", cliCommand.remoteTarget()?.host)
|
||||
assertEquals("user", cliCommand.remoteTarget()?.user)
|
||||
assertTrue(cliCommand.isValid())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_remote_with_remote_password_prompt() {
|
||||
val parseCli = parseTarget(args = arrayOf("-r", "1.2.3.4", "-u", "user", "-i"))
|
||||
@Disabled // enable to enter manually the password when prompted
|
||||
fun parse_remote_with_password_prompt() {
|
||||
val cliCommand = parseTarget(args = arrayOf("user@1.2.3.4", "-p"))
|
||||
|
||||
assertEquals("1.2.3.4", parseCli.remoteHost)
|
||||
assertEquals("user", parseCli.userName)
|
||||
assertTrue(parseCli.isValid())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_remote_with_remote_password_gopass_path() {
|
||||
val parseCli = parseTarget(args = arrayOf("-r", "1.2.3.4", "-u", "user", "-p", "gopass/path"))
|
||||
|
||||
assertEquals("1.2.3.4", parseCli.remoteHost)
|
||||
assertEquals("user", parseCli.userName)
|
||||
assertEquals("gopass/path", parseCli.sshWithGopassPath)
|
||||
assertTrue(parseCli.isValid())
|
||||
assertEquals("1.2.3.4", cliCommand.remoteTarget()?.host)
|
||||
assertEquals("user", cliCommand.remoteTarget()?.user)
|
||||
assertTrue(cliCommand.isValid())
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ internal class CliTargetCommandKtTest {
|
|||
@Test
|
||||
fun createProvInstance_local() {
|
||||
// given
|
||||
val cliCommand = TargetCliCommand(true, null, null, false, null, false)
|
||||
val cliCommand = TargetCliCommand("local", false)
|
||||
|
||||
// when
|
||||
createProvInstance(cliCommand)
|
||||
|
@ -53,7 +53,7 @@ internal class CliTargetCommandKtTest {
|
|||
@Test
|
||||
fun createProvInstance_remote_with_sshKey() {
|
||||
// given
|
||||
val cliCommand = TargetCliCommand(false, "host123", "user123", false, null, true)
|
||||
val cliCommand = TargetCliCommand("user123@host123", false)
|
||||
|
||||
// when
|
||||
createProvInstance(cliCommand)
|
||||
|
@ -65,7 +65,7 @@ internal class CliTargetCommandKtTest {
|
|||
@Test
|
||||
fun createProvInstance_remote_with_interactive_password_retrieval() {
|
||||
// given
|
||||
val cliCommand = TargetCliCommand(false, "host123", "user123", true, null, false)
|
||||
val cliCommand = TargetCliCommand("user123:sec@host123", false)
|
||||
|
||||
// when
|
||||
createProvInstance(cliCommand)
|
||||
|
|
|
@ -8,10 +8,10 @@ internal class CliArgumentsParserTest {
|
|||
|
||||
@Test
|
||||
fun parse_cliCommand_with_module_and_local_target() {
|
||||
val cli = CliArgumentsParser("test").parseCommand(args = arrayOf("basic", "-l"))
|
||||
val cli = CliArgumentsParser("test").parseCommand(args = arrayOf("basic", "local"))
|
||||
|
||||
assertTrue(cli.isValid())
|
||||
assertEquals(null, cli.configFile)
|
||||
assertEquals(true, cli.target.localHost)
|
||||
assertEquals(true, cli.target.isValidLocalhost())
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ internal class CliWorkplaceKtTest {
|
|||
val testConfig = DesktopConfig(gitUserName = "gittestuser", gitEmail = "git@test.mail")
|
||||
val cmd = DesktopCliCommand(
|
||||
DesktopType.BASIC,
|
||||
TargetCliCommand(null, null, null, false, null, false),
|
||||
TargetCliCommand("user@host", false),
|
||||
ConfigFileName("bla")
|
||||
)
|
||||
|
||||
|
@ -74,7 +74,7 @@ internal class CliWorkplaceKtTest {
|
|||
fun provision_workplace_remotely() {
|
||||
|
||||
// when
|
||||
main(arrayOf("basic", "-i", "-r", "host123.xyz", "-u", "user123", "-c", "testconfig.yaml"))
|
||||
main(arrayOf("basic", "user123:sec@host123.xyz", "-c", "testconfig.yaml"))
|
||||
|
||||
// then
|
||||
verify { remote("host123.xyz", "user123", Secret("sec"), any()) }
|
||||
|
@ -103,7 +103,7 @@ internal class CliWorkplaceKtTest {
|
|||
System.setErr(PrintStream(errContent))
|
||||
|
||||
// when
|
||||
main(arrayOf("basic", "-c", "idontexist.yaml", "-r", "remotehost", "-u", "someuser", "-k"))
|
||||
main(arrayOf("basic", "someuser@remotehost", "-c", "idontexist.yaml"))
|
||||
|
||||
// then
|
||||
System.setOut(originalOut)
|
||||
|
@ -130,7 +130,7 @@ internal class CliWorkplaceKtTest {
|
|||
System.setErr(PrintStream(errContent))
|
||||
|
||||
// when
|
||||
main(arrayOf("basic", "-c", "src/test/resources/InvalidWorkplaceConfig.yaml", "-r", "remotehost", "-u", "someuser", "-k"))
|
||||
main(arrayOf("basic", "someuser@remotehost", "-c", "src/test/resources/InvalidWorkplaceConfig.yaml"))
|
||||
|
||||
// then
|
||||
System.setOut(originalOut)
|
||||
|
|
|
@ -1,24 +1,15 @@
|
|||
package org.domaindrivenarchitecture.provs.framework.extensions.workplace
|
||||
|
||||
import org.domaindrivenarchitecture.provs.configuration.domain.ConfigFileName
|
||||
import org.domaindrivenarchitecture.provs.configuration.domain.TargetCliCommand
|
||||
import org.domaindrivenarchitecture.provs.desktop.domain.DesktopCliCommand
|
||||
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
||||
import org.domaindrivenarchitecture.provs.test.tags.ContainerTest
|
||||
import org.domaindrivenarchitecture.provs.desktop.domain.DesktopType
|
||||
import org.domaindrivenarchitecture.provs.desktop.domain.provisionWorkplace
|
||||
import org.domaindrivenarchitecture.provs.desktop.infrastructure.getConfig
|
||||
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
||||
import org.domaindrivenarchitecture.provs.test.tags.ContainerTest
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class ProvisionWorkplaceKtTest {
|
||||
|
||||
val cmd = DesktopCliCommand(
|
||||
DesktopType.BASIC,
|
||||
TargetCliCommand(null, null, null, false, null, false),
|
||||
ConfigFileName("bla")
|
||||
)
|
||||
|
||||
@Test
|
||||
@ContainerTest
|
||||
fun provisionWorkplace() {
|
||||
|
|
|
@ -12,7 +12,7 @@ internal class CliArgumentParserTest {
|
|||
val parser = CliArgumentsParser("test")
|
||||
|
||||
// when
|
||||
val result = parser.parseCommand(args = arrayOf("k3s", "-l", "config.yaml"))
|
||||
val result = parser.parseCommand(args = arrayOf("k3s", "local", "-c", "config.yaml"))
|
||||
|
||||
// then
|
||||
assertTrue(result.isValid())
|
||||
|
|
Loading…
Reference in a new issue