Skip to content

Commit ec90e18

Browse files
authored
Merge pull request #131 from mirkoDeer/main
Add gzip upload & download of container images
2 parents d91d489 + f188017 commit ec90e18

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1594
-105
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.tar filter=lfs diff=lfs merge=lfs -text

.idea/inspectionProfiles/ktlint.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ instance of `RegistryClientException` for all errors. These errors are than divi
2323
2. network related errors (`NetworkError`) like HostNotFound or SSL related errors
2424
3. unexpected errors (`UnknownError`)
2525

26-
As authentication schema JWT auth and BasicAuth are supported. Currently, there are no plans to implement certificate
26+
As an authentication schema, JWT auth and BasicAuth are supported. Currently, there are no plans to implement certificate
2727
based authentication.
2828

2929
The Registry communication can be done using either `HTTP` or `HTTPS`. The library is also able to use a proxy for the

kirc-blocking/build.gradle.kts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ dependencies {
77
api(project(":kirc-core"))
88
api(project(":kirc-image"))
99
implementation(project(":kirc-suspending"))
10-
11-
implementation(platform(coroutines.bom))
12-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
10+
implementation(libs.coroutines)
11+
implementation(libs.kotlinx.io)
1312
}
1413

1514
tasks.jar {

kirc-blocking/src/main/kotlin/de/cmdjulian/kirc/client/BlockingContainerImageClientFactory.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ package de.cmdjulian.kirc.client
22

33
import de.cmdjulian.kirc.image.ContainerImageName
44
import kotlinx.coroutines.runBlocking
5+
import kotlinx.io.files.SystemTemporaryDirectory
56
import java.net.Proxy
67
import java.net.URI
8+
import java.nio.file.Path
79
import java.security.KeyStore
810
import java.time.Duration
11+
import kotlin.io.path.Path
912

1013
object BlockingContainerImageClientFactory {
1114

@@ -25,9 +28,12 @@ object BlockingContainerImageClientFactory {
2528
skipTlsVerify: Boolean = false,
2629
keystore: KeyStore? = null,
2730
timeout: Duration = Duration.ofSeconds(5),
28-
): BlockingContainerImageRegistryClient =
29-
SuspendingContainerImageClientFactory.create(url, credentials, proxy, skipTlsVerify, keystore, timeout)
31+
tmpPath: Path = Path(SystemTemporaryDirectory.toString()),
32+
): BlockingContainerImageRegistryClient {
33+
return SuspendingContainerImageClientFactory
34+
.create(url, credentials, proxy, skipTlsVerify, keystore, timeout, tmpPath)
3035
.toBlockingClient()
36+
}
3137

3238
@JvmStatic
3339
@JvmOverloads
@@ -39,9 +45,10 @@ object BlockingContainerImageClientFactory {
3945
skipTlsVerify: Boolean = false,
4046
keystore: KeyStore? = null,
4147
timeout: Duration = Duration.ofSeconds(5),
48+
tmpPath: Path = Path(SystemTemporaryDirectory.toString()),
4249
): BlockingContainerImageClient = runBlocking {
4350
SuspendingContainerImageClientFactory
44-
.create(image, credentials, proxy, insecure, skipTlsVerify, keystore, timeout)
51+
.create(image, credentials, proxy, insecure, skipTlsVerify, keystore, timeout, tmpPath)
4552
.toBlockingClient()
4653
}
4754
}

kirc-blocking/src/main/kotlin/de/cmdjulian/kirc/client/BlockingContainerImageRegistryClient.kt

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@ import de.cmdjulian.kirc.image.Repository
66
import de.cmdjulian.kirc.image.Tag
77
import de.cmdjulian.kirc.spec.image.ImageConfig
88
import de.cmdjulian.kirc.spec.manifest.Manifest
9+
import de.cmdjulian.kirc.spec.manifest.ManifestList
910
import de.cmdjulian.kirc.spec.manifest.ManifestSingle
1011
import kotlinx.coroutines.runBlocking
12+
import kotlinx.io.asInputStream
13+
import kotlinx.io.asSink
14+
import kotlinx.io.asSource
15+
import kotlinx.io.buffered
16+
import java.io.InputStream
17+
import java.io.OutputStream
1118

1219
interface BlockingContainerImageRegistryClient {
1320
/**
@@ -57,7 +64,7 @@ interface BlockingContainerImageRegistryClient {
5764
* If the [reference] points to a ManifestList, the behaviour is up to the registry. Usually the first entry of the
5865
* list is returned.
5966
*
60-
* To be safe, it's better to use [config] instead.
67+
* To be safe, it's better to use [Digest] or config([Repository], [ManifestSingle]) instead.
6168
*/
6269
fun config(repository: Repository, reference: Reference): ImageConfig
6370

@@ -74,6 +81,22 @@ interface BlockingContainerImageRegistryClient {
7481
reference: Reference,
7582
manifest: ManifestSingle? = null,
7683
): BlockingContainerImageClient
84+
85+
/**
86+
* Uploads [tar] image archive to container registry at [repository] with [reference]
87+
*
88+
* @return the digest of uploaded image
89+
*/
90+
fun upload(repository: Repository, reference: Reference, tar: InputStream): Digest
91+
92+
/**
93+
* Downloads a docker image for certain [reference].
94+
*
95+
* For [reference] we download everything to what [reference] directs to (either [ManifestSingle] or [ManifestList])
96+
*/
97+
fun download(repository: Repository, reference: Reference): InputStream
98+
99+
fun download(repository: Repository, reference: Reference, destination: OutputStream)
77100
}
78101

79102
fun SuspendingContainerImageRegistryClient.toBlockingClient() = object : BlockingContainerImageRegistryClient {
@@ -119,4 +142,14 @@ fun SuspendingContainerImageRegistryClient.toBlockingClient() = object : Blockin
119142

120143
return client.toBlockingClient()
121144
}
145+
146+
override fun upload(repository: Repository, reference: Reference, tar: InputStream): Digest =
147+
runBlocking { this@toBlockingClient.upload(repository, reference, tar.asSource().buffered()) }
148+
149+
override fun download(repository: Repository, reference: Reference): InputStream =
150+
runBlocking { this@toBlockingClient.download(repository, reference).asInputStream() }
151+
152+
override fun download(repository: Repository, reference: Reference, destination: OutputStream) = runBlocking {
153+
this@toBlockingClient.download(repository, reference, destination.asSink().buffered())
154+
}
122155
}

kirc-core/src/main/kotlin/de/cmdjulian/kirc/exception/RegistryClientException.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ sealed class RegistryClientException(
2222
cause: Throwable,
2323
) : RegistryClientException(url, repository, reference, message, cause) {
2424

25+
class BadRequestException(
26+
url: URL,
27+
repository: Repository?,
28+
reference: Reference?,
29+
error: ErrorResponse?,
30+
cause: Throwable,
31+
) : ClientException(url, repository, reference, "bad request", error, cause) {
32+
override fun toString(): String = "DistributionClientException.BadRequestError -> $message"
33+
}
34+
2535
class AuthenticationException(
2636
url: URL,
2737
repository: Repository?,
@@ -62,6 +72,26 @@ sealed class RegistryClientException(
6272
override fun toString() = "ClientException.MethodNotAllowed (is registry delete enabled?) -> $message"
6373
}
6474

75+
class RangeNotSatisfiable(
76+
url: URL,
77+
repository: Repository?,
78+
reference: Reference?,
79+
error: ErrorResponse?,
80+
cause: Throwable,
81+
) : ClientException(url, repository, reference, "range not satisfiable", error, cause) {
82+
override fun toString() = "ClientException.RangeNotSatisfiable -> $message"
83+
}
84+
85+
class TooManyRequests(
86+
url: URL,
87+
repository: Repository?,
88+
reference: Reference?,
89+
error: ErrorResponse?,
90+
cause: Throwable,
91+
) : ClientException(url, repository, reference, "too many requests", error, cause) {
92+
override fun toString() = "ClientException.TooManyRequests -> $message"
93+
}
94+
6595
class UnexpectedErrorException(
6696
url: URL,
6797
repository: Repository?,
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
package de.cmdjulian.kirc.spec
22

3+
import de.cmdjulian.kirc.image.Digest
34
import de.cmdjulian.kirc.spec.image.ImageConfig
45
import de.cmdjulian.kirc.spec.manifest.ManifestSingle
56
import io.goodforgod.graalvm.hint.annotation.ReflectionHint
67

8+
/**
9+
* Represents a Docker Container Image
10+
*/
711
@ReflectionHint
8-
data class ContainerImage(val manifest: ManifestSingle, val config: ImageConfig, val blobs: List<LayerBlob>)
12+
data class ContainerImage(
13+
val manifest: ManifestSingle,
14+
val digest: Digest,
15+
val config: ImageConfig,
16+
val blobs: List<LayerBlob>,
17+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package de.cmdjulian.kirc.spec
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
4+
import com.fasterxml.jackson.databind.PropertyNamingStrategies
5+
import com.fasterxml.jackson.databind.annotation.JsonNaming
6+
import de.cmdjulian.kirc.image.Digest
7+
import de.cmdjulian.kirc.spec.manifest.LayerReference
8+
import io.goodforgod.graalvm.hint.annotation.ReflectionHint
9+
10+
typealias ManifestJson = List<ManifestJsonEntry>
11+
12+
/**
13+
* Represents the `manifest.json` file inside a docker archive.
14+
*
15+
* NOT PART OF the OFFICIAL OCI STANDARD!
16+
*/
17+
@ReflectionHint
18+
@JsonIgnoreProperties(ignoreUnknown = true)
19+
@JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy::class)
20+
@ReflectionHint(types = [PropertyNamingStrategies.UpperCamelCaseStrategy::class])
21+
data class ManifestJsonEntry(
22+
val config: String,
23+
val repoTags: List<String>,
24+
val layers: List<String>,
25+
val layerSources: Map<Digest, LayerReference>,
26+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package de.cmdjulian.kirc.spec
2+
3+
import io.goodforgod.graalvm.hint.annotation.ReflectionHint
4+
5+
@ReflectionHint
6+
data class OciLayout(val imageLayoutVersion: String = "1.0.0")

0 commit comments

Comments
 (0)