Skip to content

Commit

Permalink
#73 add ssh git container for authentication tests
Browse files Browse the repository at this point in the history
  • Loading branch information
simonhauck committed Jan 26, 2025
1 parent 244d0c8 commit b28a47f
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 36 deletions.
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ztExec = { module = "org.zeroturnaround:zt-exec", version = "1.12" }
junitApi = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junitVersion" }
junitEngine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junitVersion" }
assertJCore = { module = "org.assertj:assertj-core", version = "3.27.3" }
testContainers = { module = "org.testcontainers:testcontainers", version = "1.20.4" }

[plugins]
gradlePublish = { id = "com.gradle.plugin-publish", version = "1.3.0" }
Expand Down
1 change: 1 addition & 0 deletions release-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies {

testImplementation(gradleTestKit())
testImplementation(libs.bundles.junit)
testImplementation(libs.testContainers)
}

@Suppress("UnstableApiUsage")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import io.github.simonhauck.release.util.onLeft
import java.io.File
import org.gradle.api.logging.Logging

internal class GitCommandProcessWrapper(
internal open class GitCommandProcessWrapper(
private val rootGitDirectory: File?,
private val gitUser: GitUser?,
// TODO Simon.Hauck 2024-06-02 - How to test authentication
private val sshKeyFile: File?,
private val disableStrictHostKeyChecking: Boolean = false,
private val processWrapper: ProcessWrapper = ProcessWrapper(),
) : GitCommandApi {

Expand Down Expand Up @@ -158,12 +159,12 @@ internal class GitCommandProcessWrapper(
}
}

private fun gitVoidCommand(command: List<String>): Either<GitError, GitOk> {
protected fun gitVoidCommand(command: List<String>): Either<GitError, GitOk> {
val runCommand = gitCommand(command)
return runCommand.map { GitOk }
}

private fun gitCommand(command: List<String>): Either<GitError, ProcessSuccess> {
protected fun gitCommand(command: List<String>): Either<GitError, ProcessSuccess> {
val gitCommand = listOf("git").plus(command)
log.info("Running git command: '${gitCommand.joinToString(" ")}'")

Expand All @@ -185,9 +186,17 @@ internal class GitCommandProcessWrapper(

private fun sshKeyEnvironment(sshKeyFile: File?): Map<String, String> {
if (sshKeyFile == null) return emptyMap()

val strictHostKeySetting =
if (disableStrictHostKeyChecking)
"-o StrictHostKeyChecking=no -o \"UserKnownHostsFile=/dev/null\""
else ""

// Escape filename for windows
val escapedPath = sshKeyFile.absolutePath.replace("\\", "\\\\")
return mapOf(
"GIT_SSH_VARIANT" to "ssh",
"GIT_SSH_COMMAND" to "ssh -i ${sshKeyFile.absolutePath} -o IdentitiesOnly=yes",
"GIT_SSH_COMMAND" to "ssh -i $escapedPath -o IdentitiesOnly=yes $strictHostKeySetting",
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package io.github.simonhauck.release.plugin

import io.github.simonhauck.release.testdriver.GitTestCommandService
import io.github.simonhauck.release.testdriver.ReleasePluginTestDriver
import io.github.simonhauck.release.testdriver.assertIsOk
import io.github.simonhauck.release.testdriver.getTestResourceFile
import java.io.File
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.testcontainers.containers.BindMode
import org.testcontainers.containers.ExecConfig
import org.testcontainers.containers.GenericContainer
import org.testcontainers.utility.DockerImageName

internal class ReleasePluginAuthenticationTest {

@TempDir private lateinit var tmpDir: File

private val testDriver = ReleasePluginTestDriver()

@Test
fun `test Git server container`() {

val publicKeyContent = getTestResourceFile("ssh-key/id_rsa.pub").readText()
val privateKey = getTestResourceFile("ssh-key/id_rsa")

val serverWorkDir = tmpDir.resolve("server").apply { mkdirs() }
val authorizedKeyFile =
serverWorkDir.resolve("authorized_keys").apply { writeText(publicKeyContent) }

val gitRepoMount = serverWorkDir.resolve("mount").apply { mkdirs() }

val gitServer =
GenericContainer(DockerImageName.parse("rockstorm/git-server"))
.withExposedPorts(22)
.withFileSystemBind(
authorizedKeyFile.canonicalPath,
"/home/git/.ssh/authorized_keys",
BindMode.READ_WRITE,
)
.withFileSystemBind(gitRepoMount.canonicalPath, "/srv/git", BindMode.READ_WRITE)
.withEnv(mapOf("SSH_AUTH_METHODS" to "publickey"))
.withCommand("/usr/sbin/sshd", "-D", "-e")

gitServer.start()

gitServer
.execInContainer(
ExecConfig.builder()
.command(arrayOf("chmod", "600", "/home/git/.ssh/authorized_keys"))
.build()
)
.apply {
println(stdout)
println(stderr)
}

gitServer
.execInContainer(
ExecConfig.builder()
.command(arrayOf("git", "init", "--bare", "/srv/git/your-repo.git"))
.user("git")
.workDir("/home/git")
.build()
)
.apply {
println(stdout)
println(stderr)
}

val port = gitServer.firstMappedPort
val host = gitServer.host

println("Port: $port")
println("Host: $host")

println(privateKey.canonicalFile)
val api =
GitTestCommandService(
tmpDir,
privateKey.canonicalFile,
disableStrictHostKeyChecking = true,
)

try {
api.clone("ssh://git@${host}:${port}/srv/git/your-repo.git", "client2").assertIsOk()
} finally {
gitServer.stop()
}
// Add your test logic here

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.github.simonhauck.release.tasks

import io.github.simonhauck.release.testdriver.ReleasePluginTestDriver
import io.github.simonhauck.release.testdriver.assertIsOk
import io.github.simonhauck.release.testdriver.getTestResourceFile
import java.io.File
import org.assertj.core.api.Assertions.assertThat
import org.gradle.testkit.runner.TaskOutcome
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
package io.github.simonhauck.release.testdriver

import io.github.simonhauck.release.git.api.GitCommandApi
import io.github.simonhauck.release.git.api.GitError
import io.github.simonhauck.release.git.api.GitOk
import io.github.simonhauck.release.git.api.GitVoidResult
import io.github.simonhauck.release.git.internal.process.ProcessConfig
import io.github.simonhauck.release.git.internal.process.ProcessSuccess
import io.github.simonhauck.release.git.internal.process.ProcessWrapper
import io.github.simonhauck.release.git.internal.commands.GitCommandProcessWrapper
import io.github.simonhauck.release.util.Either
import io.github.simonhauck.release.util.flatten
import io.github.simonhauck.release.util.map
import io.github.simonhauck.release.util.mapLeft
import java.io.File

internal class GitTestCommandService(private val workDir: File) :
GitCommandApi by GitCommandApi.create(workDir) {

private val config = ProcessConfig(workingDir = workDir)
private val processWrapper = ProcessWrapper()
internal class GitTestCommandService(
workDir: File,
sshKeyFile: File? = null,
disableStrictHostKeyChecking: Boolean = false,
) : GitCommandProcessWrapper(workDir, null, sshKeyFile, disableStrictHostKeyChecking) {

fun initBareRepository(): GitVoidResult {
return gitVoidCommand(listOf("init", "--bare"))
Expand All @@ -27,6 +23,11 @@ internal class GitTestCommandService(private val workDir: File) :
return gitVoidCommand(listOf("clone", repositoryUrl, directoryName, "-b", branchName))
}

fun clone(repositoryUrl: String, directoryName: String): GitVoidResult {
return gitVoidCommand(listOf("clone", repositoryUrl, directoryName))
}


fun checkOutTag(tagName: String): GitVoidResult {
return gitVoidCommand(listOf("checkout", tagName))
}
Expand All @@ -36,14 +37,4 @@ internal class GitTestCommandService(private val workDir: File) :
.map { gitVoidCommand(listOf("config", "user.email", email)) }
.flatten()
}

private fun gitVoidCommand(command: List<String>): Either<GitError, GitOk> {
val runCommand = gitCommand(command)
return runCommand.map { GitOk }
}

private fun gitCommand(command: List<String>): Either<GitError, ProcessSuccess> {
val runCommand = processWrapper.runCommand(listOf("git").plus(command), config)
return runCommand.mapLeft { GitError(it.message, it.error) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import org.gradle.api.logging.Logging
import org.gradle.testkit.runner.GradleRunner
import org.testcontainers.containers.BindMode
import org.testcontainers.containers.GenericContainer
import org.testcontainers.utility.DockerImageName

internal class ReleasePluginTestDriver {
private val log = Logging.getLogger(ReleasePluginTestDriver::class.java)
Expand Down Expand Up @@ -82,10 +85,6 @@ internal class SemanticVersioningProjectBuilder(
}
}

fun getTestResourceFile(resource: String): File {
return Paths.get("src/test/resources").resolve(resource).toFile()
}

fun appendContentToBuildGradle(content: String) {
val buildGradlePath = Paths.get(client1WorkDir.absolutePath, "build.gradle.kts")
val buildGradleFile = buildGradlePath.toFile()
Expand All @@ -102,3 +101,4 @@ internal class SemanticVersioningProjectBuilder(
return resolve("version.properties").readText()
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.simonhauck.release.testdriver

import java.io.File
import java.nio.file.Paths


fun getTestResourceFile(resource: String): File {
return Paths.get("src/test/resources").resolve(resource).toFile()
}
7 changes: 0 additions & 7 deletions release-plugin/src/test/resources/ssh-key/id_ed25519

This file was deleted.

1 change: 0 additions & 1 deletion release-plugin/src/test/resources/ssh-key/id_ed25519.pub

This file was deleted.

38 changes: 38 additions & 0 deletions release-plugin/src/test/resources/ssh-key/id_rsa
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
-----BEGIN OPENSSH PRIVATE KEY-----
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
CfnmCJ6Oo2f5fJAAAAA2dpdAECAwQFBgc=
-----END OPENSSH PRIVATE KEY-----
1 change: 1 addition & 0 deletions release-plugin/src/test/resources/ssh-key/id_rsa.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDv4vFT38UNWuCfuC24FqhGRv6dTTopkgyo3wxDG1J+sMJZf5XjYZ0XjMnPX10Q3osSZ5VWxgGrSDkwGqC3cwyHVDXvi9tfr7lvjm1mxcNmyCMLG3Wm1DjK5gqxua2oCNA7qmt9lAIJI1bRR1mNgxLEPRKzDxe00PlgOd31/mYKrYnZ6HZB3kpbYH8L4E0yD92qgyJvpaTUdxCCwHOii2FrA6PbQ4TzZrnioVGs3s7LtrkEe+N2U2qLhqCJpkMkzN3xmd+Lnpi55UbOCSAFGQRkr5FjBDmPGeHL0sEHGKqKVhFdioAEksHI6tuHysrvPeeTNeTAV9ft6/E3KGdrgQ6NKAIjFFP5pFi0WBjGh2jAIfITtlR5lSsvfiFv0wrzwAAOydmKdQ6V3Mi6GoSzg+tXcBMRitYdQIt9lDFFVLyviiY0MMpUvT1sVhwzGdDRgEVIm7SR5gcZ19BjhMyU1R2+I0UjkfQlV+bx9ZzmiGhvlDFznU05U2ELwcECyjoYAqE= git

0 comments on commit b28a47f

Please sign in to comment.