From 130a9b478628d2ede7681c13b0328bbce5685c26 Mon Sep 17 00:00:00 2001 From: az Date: Thu, 2 Dec 2021 10:59:21 +0100 Subject: [PATCH] refactor createProvInstance & add tests --- build.gradle | 1 + .../provs/core/Secret.kt | 8 +++ .../provs/core/cli/CliCommand.kt | 57 +++++++++-------- .../provs/core/cli/CliCommandKtTest.kt | 61 +++++++++++++++++++ 4 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 src/test/kotlin/org/domaindrivenarchitecture/provs/core/cli/CliCommandKtTest.kt diff --git a/build.gradle b/build.gradle index f4a596d..23d2f97 100644 --- a/build.gradle +++ b/build.gradle @@ -79,6 +79,7 @@ dependencies { api "ch.qos.logback:logback-core:1.2.5" testFixturesApi group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.2' + testImplementation("io.mockk:mockk:1.12.0") testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2' } diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/core/Secret.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/core/Secret.kt index 5772373..e024ebd 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/core/Secret.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/core/Secret.kt @@ -1,5 +1,7 @@ package org.domaindrivenarchitecture.provs.core +import java.util.* + open class Secret(private val value: String) { override fun toString(): String { @@ -8,6 +10,12 @@ open class Secret(private val value: String) { fun plain() : String { return value } + override fun equals(other: Any?): Boolean { + return (this === other) || ((other is Secret) && (this.value == other.value)) + } + override fun hashCode(): Int { + return Objects.hash(value) + } } diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/core/cli/CliCommand.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/core/cli/CliCommand.kt index 5813328..0e1e8b2 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/core/cli/CliCommand.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/core/cli/CliCommand.kt @@ -93,30 +93,7 @@ internal fun createProvInstance( if (cliCommand.isValidLocalhost()) { return local() } else if (cliCommand.isValidRemote()) { - val host = cliCommand.remoteHost!! - val remoteUser = cliCommand.userName!! - - val prov = - if (cliCommand.sshWithKey) { - remote(host, remoteUser) - } else { - require( - password != null, - { "No password available for provisioning without ssh keys. Either specify provisioning by ssh-keys or provide password." }) - remote(host, remoteUser, password) - } - - if (!prov.currentUserCanSudo()) { - if (remoteHostSetSudoWithoutPasswordRequired) { - require( - password != null, - { "User ${prov.whoami()} not able to sudo on remote machine without password and no password available for the user." }) - prov.makeUserSudoerWithNoSudoPasswordRequired(password) - } else { - throw IllegalStateException("User ${prov.whoami()} not able to sudo on remote machine without password and option not set to enable user to sudo without password.") - } - } - return prov + return createProvInstanceRemote(cliCommand.remoteHost!!, cliCommand.userName!!, cliCommand.sshWithKey, password, remoteHostSetSudoWithoutPasswordRequired) } else { throw IllegalArgumentException("Error: neither a valid localHost nor a valid remoteHost was specified! Use option -h for help.") } @@ -126,8 +103,38 @@ internal fun createProvInstance( } } +private fun createProvInstanceRemote( + host: String, + remoteUser: String, + sshWithKey: Boolean, + password: Secret?, + remoteHostSetSudoWithoutPasswordRequired: Boolean +): Prov { + val prov = + if (sshWithKey) { + remote(host, remoteUser) + } else { + require( + password != null, + { "No password available for provisioning without ssh keys. Either specify provisioning by ssh-keys or provide password." }) + remote(host, remoteUser, password) + } + + if (!prov.currentUserCanSudo()) { + if (remoteHostSetSudoWithoutPasswordRequired) { + require( + password != null, + { "User ${prov.whoami()} not able to sudo on remote machine without password and no password available for the user." }) + prov.makeUserSudoerWithNoSudoPasswordRequired(password) + } else { + throw IllegalStateException("User ${prov.whoami()} not able to sudo on remote machine without password and option not set to enable user to sudo without password.") + } + } + return prov +} + -private fun retrievePassword(cliCommand: CliCommand): Secret? { +internal fun retrievePassword(cliCommand: CliCommand): Secret? { var password: Secret? = null if (cliCommand.isValidRemote()) { if (cliCommand.sshWithPasswordPrompt) { diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/core/cli/CliCommandKtTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/core/cli/CliCommandKtTest.kt new file mode 100644 index 0000000..32f6614 --- /dev/null +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/core/cli/CliCommandKtTest.kt @@ -0,0 +1,61 @@ +package org.domaindrivenarchitecture.provs.core.cli + +import io.mockk.every +import io.mockk.mockkStatic +import io.mockk.verify +import org.domaindrivenarchitecture.provs.core.Prov +import org.domaindrivenarchitecture.provs.core.Secret +import org.domaindrivenarchitecture.provs.core.local +import org.domaindrivenarchitecture.provs.core.processors.PrintOnlyProcessor +import org.domaindrivenarchitecture.provs.core.remote +import org.junit.jupiter.api.Test + +internal class CliCommandKtTest { + + @Test + fun createProvInstance_local() { + mockkStatic(::local) + + // given + val cliCommand = CliCommand(true, null, null, false, null, false) + + // when + createProvInstance(cliCommand) + + // then + verify { local() } + } + + @Test + fun createProvInstance_remote_with_sshKey() { + mockkStatic(::remote) + every { remote(any(), any(), any(), any()) } returns Prov.newInstance(PrintOnlyProcessor()) + + // given + val cliCommand = CliCommand(false, "host123", "user123", false, null, true) + + // when + createProvInstance(cliCommand) + + // then + verify { remote("host123", "user123", null, any()) } + } + + @Test + fun createProvInstance_remote_with_interactive_password_retrieval() { + mockkStatic(::remote) + every { remote(any(), any(), any(), any()) } returns Prov.newInstance(PrintOnlyProcessor()) + + mockkStatic(::retrievePassword) + every { retrievePassword(any()) } returns Secret("sec") + + // given + val cliCommand = CliCommand(false, "host123", "user123", true, null, false) + + // when + createProvInstance(cliCommand) + + // then + verify { remote("host123", "user123", Secret("sec"), any()) } + } +} \ No newline at end of file