diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index be4e41de..edbd10ab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,7 @@ ktx-serialization = "1.3.1" ktor = "2.0.0-beta-1" junixsocket = "2.4.0" kotest = "5.0.3" +ktx-datetime = "0.3.1" [libraries.ktx-coroutines-core] module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" @@ -78,6 +79,10 @@ version.ref = "kotest" module = "io.kotest:kotest-runner-junit5-jvm" version.ref = "kotest" +[libraries.ktx-datetime] +module = "org.jetbrains.kotlinx:kotlinx-datetime" +version.ref = "ktx-datetime" + [bundles] ktor = ["ktor-client-core", "ktor-client-okhttp", "ktor-client-serialization", "ktor-client-json", "ktor-network", "ktor-client-content-negotiation", "ktor-serialization-kotlinx-json"] ktx = ["ktx-coroutines-core", "ktx-serialization-core", "ktx-serialization-json"] diff --git a/yoki-engine-docker/README.md b/yoki-engine-docker/README.md index 5c021662..ce56faef 100644 --- a/yoki-engine-docker/README.md +++ b/yoki-engine-docker/README.md @@ -28,11 +28,11 @@ * [ ] Delete unused networks - POST **/networks/prune** ### Volumes -* [ ] List volumes - GET **/volumes** -* [ ] Create a volume - POST **/volumes/create** -* [ ] Inspect a volume - GET **/volumes/:name** -* [ ] Remove a volume - DELETE **/volumes/:name** -* [ ] Delete unused volumes - POST **/volumes/prune** +* [x] List volumes - GET **/volumes** +* [x] Create a volume - POST **/volumes/create** +* [x] Inspect a volume - GET **/volumes/:name** +* [x] Remove a volume - DELETE **/volumes/:name** +* [x] Delete unused volumes - POST **/volumes/prune** ### Exec * [ ] Start an exec instance - POST **/exec/:id/start** diff --git a/yoki-engine-docker/build.gradle.kts b/yoki-engine-docker/build.gradle.kts index 86faacf5..636be403 100644 --- a/yoki-engine-docker/build.gradle.kts +++ b/yoki-engine-docker/build.gradle.kts @@ -42,6 +42,7 @@ kotlin { implementation(project(":yoki-protocol")) implementation(libs.bundles.ktor) implementation(libs.bundles.ktx) + implementation(libs.ktx.datetime) } } diff --git a/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/model/DockerContainer.kt b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/model/Container.kt similarity index 91% rename from yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/model/DockerContainer.kt rename to yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/model/Container.kt index 323d4011..1a24db4b 100644 --- a/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/model/DockerContainer.kt +++ b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/model/Container.kt @@ -1,4 +1,4 @@ -package org.katan.yoki.engine.docker.model +package org.katan.yoki.engine.docker.network.model import kotlinx.serialization.Serializable @@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable * @see Container */ @Serializable -public data class DockerContainer( +public data class Container( val image: String, val name: String? = null, ) diff --git a/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/model/volume/Volume.kt b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/model/volume/Volume.kt new file mode 100644 index 00000000..73b13a86 --- /dev/null +++ b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/model/volume/Volume.kt @@ -0,0 +1,46 @@ +package org.katan.yoki.engine.docker.network.model.volume + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +public data class Volume( + @SerialName("Name") public val name: String, + @SerialName("Driver") public val driver: String, + @SerialName("Scope") public val scope: String, + @SerialName("Mountpoint") public val mountPoint: String, + @SerialName("CreatedAt") public val createdAt: String?, + @SerialName("Labels") public val labels: Map, + @SerialName("Options") public val options: Map +) + +/** + * Filters used in List, Inspect and Prune requests. + * + * @see org.katan.yoki.engine.docker.resource.volume.VolumeResource.list + * @see org.katan.yoki.engine.docker.resource.volume.VolumeResource.inspect + * @see org.katan.yoki.engine.docker.resource.volume.VolumeResource.prune + */ +public const val VolumeFilters: String = "filters" + +/** + * Volume definition used in Create request. + * + * @see org.katan.yoki.engine.docker.resource.volume.VolumeResource.create + */ +public const val VolumeDefinition: String = "definition" + +/** + * Volume's name or ID used in Inspect and Remove requests. + * + * @see org.katan.yoki.engine.docker.resource.volume.VolumeResource.inspect + * @see org.katan.yoki.engine.docker.resource.volume.VolumeResource.remove + */ +public const val VolumeName: String = "name" + +/** + * Parameter used to set if a volume should be force deleted in Remove request + * + * @see org.katan.yoki.engine.docker.resource.volume.VolumeResource.remove + */ +public const val VolumeForceDelete: String = "forceDelete" \ No newline at end of file diff --git a/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/response/volume/VolumeListResponse.kt b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/response/volume/VolumeListResponse.kt new file mode 100644 index 00000000..8c6249ff --- /dev/null +++ b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/response/volume/VolumeListResponse.kt @@ -0,0 +1,11 @@ +package org.katan.yoki.engine.docker.network.response.volume + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.katan.yoki.engine.docker.network.model.volume.Volume + +@Serializable +public data class VolumeListResponse( + @SerialName("Volumes") public val volumes: Collection, + @SerialName("Warnings") public val warnings: Collection +) \ No newline at end of file diff --git a/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/response/volume/VolumePruneResponse.kt b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/response/volume/VolumePruneResponse.kt new file mode 100644 index 00000000..62dc13a4 --- /dev/null +++ b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/network/response/volume/VolumePruneResponse.kt @@ -0,0 +1,11 @@ +package org.katan.yoki.engine.docker.network.response.volume + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.katan.yoki.engine.docker.network.model.volume.Volume + +@Serializable +public data class VolumePruneResponse( + @SerialName("VolumesDeleted") public val volumesDeleted: Collection, + @SerialName("SpaceReclaimed") public val spaceReclaimed: Long +) \ No newline at end of file diff --git a/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/resource/container/ContainerResource.kt b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/resource/container/ContainerResource.kt index 46f18e71..fdc41aa7 100644 --- a/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/resource/container/ContainerResource.kt +++ b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/resource/container/ContainerResource.kt @@ -4,7 +4,7 @@ import io.ktor.client.call.* import io.ktor.client.request.* import io.ktor.http.* import org.katan.yoki.engine.docker.* -import org.katan.yoki.engine.docker.model.* +import org.katan.yoki.engine.docker.network.model.ContainerImage /** * @see ContainerResource diff --git a/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/resource/volume/VolumeResource.kt b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/resource/volume/VolumeResource.kt new file mode 100644 index 00000000..e1cee4df --- /dev/null +++ b/yoki-engine-docker/src/commonMain/kotlin/org/katan/yoki/engine/docker/resource/volume/VolumeResource.kt @@ -0,0 +1,98 @@ +package org.katan.yoki.engine.docker.resource.volume + +import io.ktor.client.call.* +import io.ktor.client.request.* +import io.ktor.http.* +import org.katan.yoki.engine.docker.DockerEngine +import org.katan.yoki.engine.docker.network.model.volume.* +import org.katan.yoki.engine.docker.network.response.volume.VolumeListResponse +import org.katan.yoki.engine.docker.network.response.volume.VolumePruneResponse + +/** + * @see VolumeResource + * @author João Victor Gomides Cruz (devwckd) + */ +public class VolumeResource( + private val engine: DockerEngine +) { + + /** + * List volumes + * + * @param options see VolumeList for options. + * @return VolumeListResponse + * + * @see VolumeList + */ + public suspend fun list(options: Map): VolumeListResponse { + return engine.httpClient.get("/volumes") { + options["filter"]?.let { + parameter(VolumeFilters, options["filter"]) + } + }.body() + } + + /** + * Create a volume + * + * @param options see VolumeCreate for options. + * @return Volume + * + * @see VolumeCreate + */ + public suspend fun create(options: Map): Volume { + require(VolumeDefinition in options) { "Volume Definition is required" } + + return engine.httpClient.post("/volumes/create") { + setBody(options[VolumeDefinition]) + }.body() + } + + /** + * Inspect a volume + * + * @param options see VolumeInspect for options. + * @return Volume + * + * @see VolumeInspect + */ + public suspend fun inspect(options: Map): Volume { + require(VolumeName in options) { "Volume Name is required" } + + return engine.httpClient.get("/volumes/${options[VolumeName]}").body() + } + + /** + * Remove a volume + * + * @param options see VolumeDelete for options. + * + * @see VolumeDelete + */ + public suspend fun remove(options: Map) { + require(VolumeName in options) { "Volume Name is required" } + + engine.httpClient.delete("/volumes/${options[VolumeName]}") { + options[VolumeForceDelete]?.let { + parameter(VolumeForceDelete, it) + } + } + } + + /** + * Delete unused volumes + * + * @param options see VolumePrune for options. + * @return VolumePruneResponse + * + * @see VolumePrune + */ + public suspend fun prune(options: Map): VolumePruneResponse { + return engine.httpClient.delete("/volumes/${options[VolumeName]}") { + options[VolumeFilters]?.let { + parameter(VolumeFilters, options["filter"]) + } + }.body() + } + +} \ No newline at end of file