@ -0,0 +1,38 @@
|
||||
# ADR: We implement domain services static
|
||||
|
||||
Domain services can be implemented either as object (and composed like done in spring / example1 ) or with extension
|
||||
function and composed static (see example2).
|
||||
|
||||
## example1
|
||||
```kotlin
|
||||
class DesktopServie(val aptApi: AptApi, val prov: Prov) {
|
||||
fun provisionIdeDesktop(onlyModules: List<String>? = null) {
|
||||
prov.task {
|
||||
if (onlyModules == null) {
|
||||
aptApi.aptInstall(OPEN_VPM)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## example2
|
||||
```kotlin
|
||||
fun Prov.provisionIdeDesktop(onlyModules: List<String>? = null) {
|
||||
if (onlyModules == null) {
|
||||
aptInstall(OPEN_VPM)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Decission
|
||||
|
||||
We use extension function and composed static.
|
||||
|
||||
## Reason
|
||||
|
||||
1. Similar to composed objects we can easily mock `aptInstall` in tests. Both solutions are equivalent.
|
||||
2. Inheritance in case of composed objects we can solve by static composition.
|
||||
3. Object composition we can solve by static composition.
|
||||
|
||||
There is no reason left to change the current implementd pattern.
|
@ -0,0 +1,77 @@
|
||||
|
||||
## Initialization
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor user
|
||||
participant app as Application
|
||||
participant ds as DesktopService
|
||||
participant gtr as KnownHost
|
||||
participant pa as CliArgumentsParser
|
||||
participant cr as DesktopConfigRepository
|
||||
participant ut as CliUtils
|
||||
participant su as ProvsWithSudo
|
||||
|
||||
user ->> app: main
|
||||
activate app
|
||||
app ->> pa: parseCommands
|
||||
app ->> cr: getConfig(configFileName)
|
||||
app ->> ut: createProvInstance(cmd.target)
|
||||
app ->> su: ensureSudoWithoutPassword(cmd.target.remoteTarget()?.password)
|
||||
app ->> ds: provisionDesktopCommand(cmd, config)
|
||||
activate ds
|
||||
ds ->> gtr: values()
|
||||
gtr -->> ds: List(KnownHost)
|
||||
deactivate ds
|
||||
deactivate app
|
||||
```
|
||||
|
||||
## Domain
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
|
||||
namespace configuration {
|
||||
|
||||
class TargetCliCommand {
|
||||
val target: String,
|
||||
val passwordInteractive: Boolean = false
|
||||
}
|
||||
|
||||
class ConfigFileName {
|
||||
fileName: String
|
||||
}
|
||||
}
|
||||
|
||||
namespace desktop {
|
||||
|
||||
class DesktopCliCommand {
|
||||
}
|
||||
|
||||
class DesktopConfig {
|
||||
val ssh: SshKeyPairSource? = null,
|
||||
val gpg: KeyPairSource? = null,
|
||||
val gitUserName: String? = null,
|
||||
val gitEmail: String? = null,
|
||||
}
|
||||
|
||||
class DesktopType {
|
||||
val name: String
|
||||
}
|
||||
class DesktopOnlyModule {
|
||||
<<enum>>
|
||||
FIREFOX, VERIFY
|
||||
}
|
||||
|
||||
class KnownHost {
|
||||
hostName: String,
|
||||
hostKeys: List<HostKey>
|
||||
}
|
||||
}
|
||||
|
||||
DesktopCliCommand "1" *-- "1" DesktopType: type
|
||||
DesktopCliCommand "1" *-- "1" TargetCliCommand: target
|
||||
DesktopCliCommand "1" *-- "1" ConfigFileName: configFile
|
||||
DesktopCliCommand "1" *-- "..n" DesktopOnlyModule: onlyModules
|
||||
|
||||
```
|
@ -0,0 +1,35 @@
|
||||
package org.domaindrivenarchitecture.provs.desktop.domain
|
||||
|
||||
/**
|
||||
* A HostKey should contain space-separated: keytype, key and (optionally) a comment
|
||||
*
|
||||
* See: https://man7.org/linux/man-pages/man8/sshd.8.html#SSH_KNOWN_HOSTS_FILE_FORMAT
|
||||
*/
|
||||
typealias HostKey = String
|
||||
|
||||
open class KnownHost protected constructor(val hostName: String, val hostKeys: List<HostKey>) {
|
||||
companion object {
|
||||
val GITHUB = KnownHost(
|
||||
"github.com", listOf(
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl",
|
||||
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=",
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=",
|
||||
)
|
||||
)
|
||||
val GITLAB = KnownHost(
|
||||
"gitlab.com", listOf(
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf",
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9",
|
||||
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=",
|
||||
)
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
protected val values = listOf(GITHUB, GITLAB)
|
||||
|
||||
fun values(): List<KnownHost> {
|
||||
return values
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package org.domaindrivenarchitecture.provs.desktop.domain
|
||||
|
||||
import org.domaindrivenarchitecture.provs.framework.core.Prov
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base.addKnownHost
|
||||
|
||||
|
||||
fun Prov.addKnownHosts(knownHosts: List<KnownHost> = KnownHost.values()) = task {
|
||||
for (knownHost in knownHosts) {
|
||||
with(knownHost) {
|
||||
addKnownHost(hostName, hostKeys, verifyKeys = true)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package org.domaindrivenarchitecture.provs.desktop.domain
|
||||
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.deleteFile
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.aptInstall
|
||||
import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base.KNOWN_HOSTS_FILE
|
||||
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
|
||||
import org.domaindrivenarchitecture.provs.test.tags.ContainerTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
|
||||
class KnownHostTest {
|
||||
|
||||
@ContainerTest
|
||||
fun defaultKnownHosts() {
|
||||
// given
|
||||
val prov = defaultTestContainer()
|
||||
prov.task {
|
||||
aptInstall("ssh")
|
||||
deleteFile(KNOWN_HOSTS_FILE)
|
||||
}
|
||||
|
||||
// when
|
||||
val res = prov.addKnownHosts()
|
||||
|
||||
// then
|
||||
assertTrue(res.success)
|
||||
}
|
||||
|
||||
|
||||
// Subclass of KnownHost for test knownHostSubclass_includes_additional_host
|
||||
class KnownHostsSubclass(hostName: String, hostKeys: List<HostKey>): KnownHost(hostName, hostKeys) {
|
||||
|
||||
companion object {
|
||||
val ANOTHER_HOST = KnownHostsSubclass("anotherhost.com", listOf("key1"))
|
||||
|
||||
fun values(): List<KnownHost> {
|
||||
return values + ANOTHER_HOST
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun knownHostSubclass_includes_additional_host() {
|
||||
// when
|
||||
val hosts = KnownHostsSubclass.values()
|
||||
|
||||
// then
|
||||
assertTrue(hosts.size > 1)
|
||||
assertEquals("key1", hosts.last().hostKeys[0])
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue