@lakutata/containerd 0.0.0-stage → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +409 -2
- package/THIRD_PARTY_NOTICES.md +310 -0
- package/dist/cjs/client/BuildKit.d.ts +176 -0
- package/dist/cjs/client/BuildKit.js +536 -0
- package/dist/cjs/client/BuildKitMessages.d.ts +35 -0
- package/dist/cjs/client/BuildKitMessages.js +123 -0
- package/dist/cjs/client/ContainerStore.d.ts +558 -0
- package/dist/cjs/client/ContainerStore.js +1384 -0
- package/dist/cjs/client/ContainerdClient.d.ts +244 -0
- package/dist/cjs/client/ContainerdClient.js +455 -0
- package/dist/cjs/client/DockerIgnore.d.ts +44 -0
- package/dist/cjs/client/DockerIgnore.js +186 -0
- package/dist/cjs/client/Fifo.d.ts +31 -0
- package/dist/cjs/client/Fifo.js +66 -0
- package/dist/cjs/client/ImageStore.d.ts +258 -0
- package/dist/cjs/client/ImageStore.js +636 -0
- package/dist/cjs/client/Messages.d.ts +103 -0
- package/dist/cjs/client/Messages.js +397 -0
- package/dist/cjs/client/Networks.d.ts +221 -0
- package/dist/cjs/client/Networks.js +630 -0
- package/dist/cjs/client/Platforms.d.ts +53 -0
- package/dist/cjs/client/Platforms.js +169 -0
- package/dist/cjs/client/Reference.d.ts +33 -0
- package/dist/cjs/client/Reference.js +70 -0
- package/dist/cjs/client/Registry.d.ts +145 -0
- package/dist/cjs/client/Registry.js +272 -0
- package/dist/cjs/client/Rootfs.d.ts +83 -0
- package/dist/cjs/client/Rootfs.js +245 -0
- package/dist/cjs/client/Seccomp.d.ts +36 -0
- package/dist/cjs/client/Seccomp.js +156 -0
- package/dist/cjs/client/Spec.d.ts +151 -0
- package/dist/cjs/client/Spec.js +227 -0
- package/dist/cjs/client/Volumes.d.ts +42 -0
- package/dist/cjs/client/Volumes.js +80 -0
- package/dist/cjs/containerd/Containerd.d.ts +442 -0
- package/dist/cjs/containerd/Containerd.js +929 -0
- package/dist/cjs/containerd/exceptions/ContainerdConnectionException.d.ts +9 -0
- package/dist/cjs/containerd/exceptions/ContainerdConnectionException.js +16 -0
- package/dist/cjs/containerd/exceptions/ContainerdContainerNameConflictException.d.ts +8 -0
- package/dist/cjs/containerd/exceptions/ContainerdContainerNameConflictException.js +15 -0
- package/dist/cjs/containerd/exceptions/ContainerdContainerNotFoundException.d.ts +9 -0
- package/dist/cjs/containerd/exceptions/ContainerdContainerNotFoundException.js +16 -0
- package/dist/cjs/containerd/exceptions/ContainerdContainerStateException.d.ts +10 -0
- package/dist/cjs/containerd/exceptions/ContainerdContainerStateException.js +17 -0
- package/dist/cjs/containerd/exceptions/ContainerdImageBuildException.d.ts +9 -0
- package/dist/cjs/containerd/exceptions/ContainerdImageBuildException.js +16 -0
- package/dist/cjs/containerd/exceptions/ContainerdImageExportException.d.ts +9 -0
- package/dist/cjs/containerd/exceptions/ContainerdImageExportException.js +16 -0
- package/dist/cjs/containerd/exceptions/ContainerdImageImportException.d.ts +9 -0
- package/dist/cjs/containerd/exceptions/ContainerdImageImportException.js +16 -0
- package/dist/cjs/containerd/exceptions/ContainerdImageNotFoundException.d.ts +9 -0
- package/dist/cjs/containerd/exceptions/ContainerdImageNotFoundException.js +16 -0
- package/dist/cjs/containerd/exceptions/ContainerdImagePullException.d.ts +9 -0
- package/dist/cjs/containerd/exceptions/ContainerdImagePullException.js +16 -0
- package/dist/cjs/containerd/exceptions/ContainerdImagePushException.d.ts +8 -0
- package/dist/cjs/containerd/exceptions/ContainerdImagePushException.js +15 -0
- package/dist/cjs/containerd/exceptions/ContainerdNetworkConflictException.d.ts +9 -0
- package/dist/cjs/containerd/exceptions/ContainerdNetworkConflictException.js +16 -0
- package/dist/cjs/containerd/exceptions/ContainerdNetworkNotFoundException.d.ts +9 -0
- package/dist/cjs/containerd/exceptions/ContainerdNetworkNotFoundException.js +16 -0
- package/dist/cjs/containerd/exceptions/ContainerdPathNotFoundException.d.ts +8 -0
- package/dist/cjs/containerd/exceptions/ContainerdPathNotFoundException.js +15 -0
- package/dist/cjs/containerd/exceptions/ContainerdPlatformException.d.ts +9 -0
- package/dist/cjs/containerd/exceptions/ContainerdPlatformException.js +16 -0
- package/dist/cjs/containerd/exceptions/ContainerdVolumeInUseException.d.ts +8 -0
- package/dist/cjs/containerd/exceptions/ContainerdVolumeInUseException.js +15 -0
- package/dist/cjs/containerd/exceptions/ContainerdVolumeNotFoundException.d.ts +8 -0
- package/dist/cjs/containerd/exceptions/ContainerdVolumeNotFoundException.js +15 -0
- package/dist/cjs/containerd/lib/ContainerdContainer.d.ts +292 -0
- package/dist/cjs/containerd/lib/ContainerdContainer.js +536 -0
- package/dist/cjs/containerd/lib/ContainerdContainerTTY.d.ts +116 -0
- package/dist/cjs/containerd/lib/ContainerdContainerTTY.js +178 -0
- package/dist/cjs/containerd/lib/ContainerdErrors.d.ts +6 -0
- package/dist/cjs/containerd/lib/ContainerdErrors.js +37 -0
- package/dist/cjs/containerd/lib/ContainerdImage.d.ts +149 -0
- package/dist/cjs/containerd/lib/ContainerdImage.js +222 -0
- package/dist/cjs/containerd/options/ContainerdPruneOptions.d.ts +44 -0
- package/dist/cjs/containerd/options/ContainerdPruneOptions.js +51 -0
- package/dist/cjs/containerd/options/ContainerdRegistryAuthOptions.d.ts +32 -0
- package/dist/cjs/containerd/options/ContainerdRegistryAuthOptions.js +45 -0
- package/dist/cjs/containerd/options/container/ContainerCommitOptions.d.ts +57 -0
- package/dist/cjs/containerd/options/container/ContainerCommitOptions.js +66 -0
- package/dist/cjs/containerd/options/container/ContainerCreateOptions.d.ts +273 -0
- package/dist/cjs/containerd/options/container/ContainerCreateOptions.js +239 -0
- package/dist/cjs/containerd/options/container/ContainerExecOptions.d.ts +53 -0
- package/dist/cjs/containerd/options/container/ContainerExecOptions.js +63 -0
- package/dist/cjs/containerd/options/container/ContainerExportDirectoryOptions.d.ts +32 -0
- package/dist/cjs/containerd/options/container/ContainerExportDirectoryOptions.js +45 -0
- package/dist/cjs/containerd/options/container/ContainerKillOptions.d.ts +30 -0
- package/dist/cjs/containerd/options/container/ContainerKillOptions.js +41 -0
- package/dist/cjs/containerd/options/container/ContainerLogsOptions.d.ts +34 -0
- package/dist/cjs/containerd/options/container/ContainerLogsOptions.js +44 -0
- package/dist/cjs/containerd/options/container/ContainerRemoveOptions.d.ts +22 -0
- package/dist/cjs/containerd/options/container/ContainerRemoveOptions.js +34 -0
- package/dist/cjs/containerd/options/container/ContainerResourceOptions.d.ts +63 -0
- package/dist/cjs/containerd/options/container/ContainerResourceOptions.js +67 -0
- package/dist/cjs/containerd/options/container/ContainerStopOptions.d.ts +30 -0
- package/dist/cjs/containerd/options/container/ContainerStopOptions.js +41 -0
- package/dist/cjs/containerd/options/container/ContainerTTYConsoleSizeOptions.d.ts +26 -0
- package/dist/cjs/containerd/options/container/ContainerTTYConsoleSizeOptions.js +40 -0
- package/dist/cjs/containerd/options/container/ContainerTTYOptions.d.ts +50 -0
- package/dist/cjs/containerd/options/container/ContainerTTYOptions.js +63 -0
- package/dist/cjs/containerd/options/image/ImageBuildOptions.d.ts +88 -0
- package/dist/cjs/containerd/options/image/ImageBuildOptions.js +95 -0
- package/dist/cjs/containerd/options/image/ImageExportOptions.d.ts +25 -0
- package/dist/cjs/containerd/options/image/ImageExportOptions.js +39 -0
- package/dist/cjs/containerd/options/image/ImageImportOptions.d.ts +38 -0
- package/dist/cjs/containerd/options/image/ImageImportOptions.js +47 -0
- package/dist/cjs/containerd/options/image/ImagePullOptions.d.ts +53 -0
- package/dist/cjs/containerd/options/image/ImagePullOptions.js +66 -0
- package/dist/cjs/containerd/options/image/ImagePushOptions.d.ts +44 -0
- package/dist/cjs/containerd/options/image/ImagePushOptions.js +58 -0
- package/dist/cjs/containerd/options/network/NetworkCreateOptions.d.ts +82 -0
- package/dist/cjs/containerd/options/network/NetworkCreateOptions.js +83 -0
- package/dist/cjs/containerd/options/volume/VolumeCreateOptions.d.ts +26 -0
- package/dist/cjs/containerd/options/volume/VolumeCreateOptions.js +40 -0
- package/dist/cjs/containerd/types/ContainerdBind.d.ts +28 -0
- package/dist/cjs/containerd/types/ContainerdBind.js +2 -0
- package/dist/cjs/containerd/types/ContainerdBuildOutputCallback.d.ts +6 -0
- package/dist/cjs/containerd/types/ContainerdBuildOutputCallback.js +2 -0
- package/dist/cjs/containerd/types/ContainerdContainerNetwork.d.ts +40 -0
- package/dist/cjs/containerd/types/ContainerdContainerNetwork.js +2 -0
- package/dist/cjs/containerd/types/ContainerdContainerPort.d.ts +22 -0
- package/dist/cjs/containerd/types/ContainerdContainerPort.js +2 -0
- package/dist/cjs/containerd/types/ContainerdContainerState.d.ts +42 -0
- package/dist/cjs/containerd/types/ContainerdContainerState.js +2 -0
- package/dist/cjs/containerd/types/ContainerdContainerStats.d.ts +107 -0
- package/dist/cjs/containerd/types/ContainerdContainerStats.js +2 -0
- package/dist/cjs/containerd/types/ContainerdDevice.d.ts +25 -0
- package/dist/cjs/containerd/types/ContainerdDevice.js +2 -0
- package/dist/cjs/containerd/types/ContainerdExecResult.d.ts +18 -0
- package/dist/cjs/containerd/types/ContainerdExecResult.js +2 -0
- package/dist/cjs/containerd/types/ContainerdImageConfig.d.ts +46 -0
- package/dist/cjs/containerd/types/ContainerdImageConfig.js +2 -0
- package/dist/cjs/containerd/types/ContainerdNetworkInfo.d.ts +49 -0
- package/dist/cjs/containerd/types/ContainerdNetworkInfo.js +2 -0
- package/dist/cjs/containerd/types/ContainerdProgressCallback.d.ts +29 -0
- package/dist/cjs/containerd/types/ContainerdProgressCallback.js +2 -0
- package/dist/cjs/containerd/types/ContainerdPruneResult.d.ts +21 -0
- package/dist/cjs/containerd/types/ContainerdPruneResult.js +2 -0
- package/dist/cjs/containerd/types/ContainerdRestartPolicy.d.ts +13 -0
- package/dist/cjs/containerd/types/ContainerdRestartPolicy.js +2 -0
- package/dist/cjs/containerd/types/ContainerdTmpfs.d.ts +20 -0
- package/dist/cjs/containerd/types/ContainerdTmpfs.js +2 -0
- package/dist/cjs/containerd/types/ContainerdVolume.d.ts +19 -0
- package/dist/cjs/containerd/types/ContainerdVolume.js +2 -0
- package/dist/cjs/containerd/types/ContainerdVolumeInfo.d.ts +26 -0
- package/dist/cjs/containerd/types/ContainerdVolumeInfo.js +2 -0
- package/dist/cjs/exports/Containerd.d.ts +69 -0
- package/dist/cjs/exports/Containerd.js +87 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/client/BuildKit.js +488 -0
- package/dist/esm/client/BuildKitMessages.js +120 -0
- package/dist/esm/client/ContainerStore.js +1339 -0
- package/dist/esm/client/ContainerdClient.js +416 -0
- package/dist/esm/client/DockerIgnore.js +179 -0
- package/dist/esm/client/Fifo.js +59 -0
- package/dist/esm/client/ImageStore.js +628 -0
- package/dist/esm/client/Messages.js +393 -0
- package/dist/esm/client/Networks.js +621 -0
- package/dist/esm/client/Platforms.js +156 -0
- package/dist/esm/client/Reference.js +66 -0
- package/dist/esm/client/Registry.js +263 -0
- package/dist/esm/client/Rootfs.js +234 -0
- package/dist/esm/client/Seccomp.js +152 -0
- package/dist/esm/client/Spec.js +220 -0
- package/dist/esm/client/Volumes.js +73 -0
- package/dist/esm/containerd/Containerd.js +923 -0
- package/dist/esm/containerd/exceptions/ContainerdConnectionException.js +12 -0
- package/dist/esm/containerd/exceptions/ContainerdContainerNameConflictException.js +11 -0
- package/dist/esm/containerd/exceptions/ContainerdContainerNotFoundException.js +12 -0
- package/dist/esm/containerd/exceptions/ContainerdContainerStateException.js +13 -0
- package/dist/esm/containerd/exceptions/ContainerdImageBuildException.js +12 -0
- package/dist/esm/containerd/exceptions/ContainerdImageExportException.js +12 -0
- package/dist/esm/containerd/exceptions/ContainerdImageImportException.js +12 -0
- package/dist/esm/containerd/exceptions/ContainerdImageNotFoundException.js +12 -0
- package/dist/esm/containerd/exceptions/ContainerdImagePullException.js +12 -0
- package/dist/esm/containerd/exceptions/ContainerdImagePushException.js +11 -0
- package/dist/esm/containerd/exceptions/ContainerdNetworkConflictException.js +12 -0
- package/dist/esm/containerd/exceptions/ContainerdNetworkNotFoundException.js +12 -0
- package/dist/esm/containerd/exceptions/ContainerdPathNotFoundException.js +11 -0
- package/dist/esm/containerd/exceptions/ContainerdPlatformException.js +12 -0
- package/dist/esm/containerd/exceptions/ContainerdVolumeInUseException.js +11 -0
- package/dist/esm/containerd/exceptions/ContainerdVolumeNotFoundException.js +11 -0
- package/dist/esm/containerd/lib/ContainerdContainer.js +533 -0
- package/dist/esm/containerd/lib/ContainerdContainerTTY.js +175 -0
- package/dist/esm/containerd/lib/ContainerdErrors.js +34 -0
- package/dist/esm/containerd/lib/ContainerdImage.js +219 -0
- package/dist/esm/containerd/options/ContainerdPruneOptions.js +47 -0
- package/dist/esm/containerd/options/ContainerdRegistryAuthOptions.js +41 -0
- package/dist/esm/containerd/options/container/ContainerCommitOptions.js +62 -0
- package/dist/esm/containerd/options/container/ContainerCreateOptions.js +235 -0
- package/dist/esm/containerd/options/container/ContainerExecOptions.js +59 -0
- package/dist/esm/containerd/options/container/ContainerExportDirectoryOptions.js +41 -0
- package/dist/esm/containerd/options/container/ContainerKillOptions.js +37 -0
- package/dist/esm/containerd/options/container/ContainerLogsOptions.js +40 -0
- package/dist/esm/containerd/options/container/ContainerRemoveOptions.js +30 -0
- package/dist/esm/containerd/options/container/ContainerResourceOptions.js +63 -0
- package/dist/esm/containerd/options/container/ContainerStopOptions.js +37 -0
- package/dist/esm/containerd/options/container/ContainerTTYConsoleSizeOptions.js +36 -0
- package/dist/esm/containerd/options/container/ContainerTTYOptions.js +59 -0
- package/dist/esm/containerd/options/image/ImageBuildOptions.js +91 -0
- package/dist/esm/containerd/options/image/ImageExportOptions.js +35 -0
- package/dist/esm/containerd/options/image/ImageImportOptions.js +43 -0
- package/dist/esm/containerd/options/image/ImagePullOptions.js +62 -0
- package/dist/esm/containerd/options/image/ImagePushOptions.js +54 -0
- package/dist/esm/containerd/options/network/NetworkCreateOptions.js +79 -0
- package/dist/esm/containerd/options/volume/VolumeCreateOptions.js +36 -0
- package/dist/esm/containerd/types/ContainerdBind.js +1 -0
- package/dist/esm/containerd/types/ContainerdBuildOutputCallback.js +1 -0
- package/dist/esm/containerd/types/ContainerdContainerNetwork.js +1 -0
- package/dist/esm/containerd/types/ContainerdContainerPort.js +1 -0
- package/dist/esm/containerd/types/ContainerdContainerState.js +1 -0
- package/dist/esm/containerd/types/ContainerdContainerStats.js +1 -0
- package/dist/esm/containerd/types/ContainerdDevice.js +1 -0
- package/dist/esm/containerd/types/ContainerdExecResult.js +1 -0
- package/dist/esm/containerd/types/ContainerdImageConfig.js +1 -0
- package/dist/esm/containerd/types/ContainerdNetworkInfo.js +1 -0
- package/dist/esm/containerd/types/ContainerdProgressCallback.js +1 -0
- package/dist/esm/containerd/types/ContainerdPruneResult.js +1 -0
- package/dist/esm/containerd/types/ContainerdRestartPolicy.js +1 -0
- package/dist/esm/containerd/types/ContainerdTmpfs.js +1 -0
- package/dist/esm/containerd/types/ContainerdVolume.js +1 -0
- package/dist/esm/containerd/types/ContainerdVolumeInfo.js +1 -0
- package/dist/esm/exports/Containerd.js +68 -0
- package/dist/types/client/BuildKit.d.ts +176 -0
- package/dist/types/client/BuildKitMessages.d.ts +35 -0
- package/dist/types/client/ContainerStore.d.ts +558 -0
- package/dist/types/client/ContainerdClient.d.ts +244 -0
- package/dist/types/client/DockerIgnore.d.ts +44 -0
- package/dist/types/client/Fifo.d.ts +31 -0
- package/dist/types/client/ImageStore.d.ts +258 -0
- package/dist/types/client/Messages.d.ts +103 -0
- package/dist/types/client/Networks.d.ts +221 -0
- package/dist/types/client/Platforms.d.ts +53 -0
- package/dist/types/client/Reference.d.ts +33 -0
- package/dist/types/client/Registry.d.ts +145 -0
- package/dist/types/client/Rootfs.d.ts +83 -0
- package/dist/types/client/Seccomp.d.ts +36 -0
- package/dist/types/client/Spec.d.ts +151 -0
- package/dist/types/client/Volumes.d.ts +42 -0
- package/dist/types/containerd/Containerd.d.ts +442 -0
- package/dist/types/containerd/exceptions/ContainerdConnectionException.d.ts +9 -0
- package/dist/types/containerd/exceptions/ContainerdContainerNameConflictException.d.ts +8 -0
- package/dist/types/containerd/exceptions/ContainerdContainerNotFoundException.d.ts +9 -0
- package/dist/types/containerd/exceptions/ContainerdContainerStateException.d.ts +10 -0
- package/dist/types/containerd/exceptions/ContainerdImageBuildException.d.ts +9 -0
- package/dist/types/containerd/exceptions/ContainerdImageExportException.d.ts +9 -0
- package/dist/types/containerd/exceptions/ContainerdImageImportException.d.ts +9 -0
- package/dist/types/containerd/exceptions/ContainerdImageNotFoundException.d.ts +9 -0
- package/dist/types/containerd/exceptions/ContainerdImagePullException.d.ts +9 -0
- package/dist/types/containerd/exceptions/ContainerdImagePushException.d.ts +8 -0
- package/dist/types/containerd/exceptions/ContainerdNetworkConflictException.d.ts +9 -0
- package/dist/types/containerd/exceptions/ContainerdNetworkNotFoundException.d.ts +9 -0
- package/dist/types/containerd/exceptions/ContainerdPathNotFoundException.d.ts +8 -0
- package/dist/types/containerd/exceptions/ContainerdPlatformException.d.ts +9 -0
- package/dist/types/containerd/exceptions/ContainerdVolumeInUseException.d.ts +8 -0
- package/dist/types/containerd/exceptions/ContainerdVolumeNotFoundException.d.ts +8 -0
- package/dist/types/containerd/lib/ContainerdContainer.d.ts +292 -0
- package/dist/types/containerd/lib/ContainerdContainerTTY.d.ts +116 -0
- package/dist/types/containerd/lib/ContainerdErrors.d.ts +6 -0
- package/dist/types/containerd/lib/ContainerdImage.d.ts +149 -0
- package/dist/types/containerd/options/ContainerdPruneOptions.d.ts +44 -0
- package/dist/types/containerd/options/ContainerdRegistryAuthOptions.d.ts +32 -0
- package/dist/types/containerd/options/container/ContainerCommitOptions.d.ts +57 -0
- package/dist/types/containerd/options/container/ContainerCreateOptions.d.ts +273 -0
- package/dist/types/containerd/options/container/ContainerExecOptions.d.ts +53 -0
- package/dist/types/containerd/options/container/ContainerExportDirectoryOptions.d.ts +32 -0
- package/dist/types/containerd/options/container/ContainerKillOptions.d.ts +30 -0
- package/dist/types/containerd/options/container/ContainerLogsOptions.d.ts +34 -0
- package/dist/types/containerd/options/container/ContainerRemoveOptions.d.ts +22 -0
- package/dist/types/containerd/options/container/ContainerResourceOptions.d.ts +63 -0
- package/dist/types/containerd/options/container/ContainerStopOptions.d.ts +30 -0
- package/dist/types/containerd/options/container/ContainerTTYConsoleSizeOptions.d.ts +26 -0
- package/dist/types/containerd/options/container/ContainerTTYOptions.d.ts +50 -0
- package/dist/types/containerd/options/image/ImageBuildOptions.d.ts +88 -0
- package/dist/types/containerd/options/image/ImageExportOptions.d.ts +25 -0
- package/dist/types/containerd/options/image/ImageImportOptions.d.ts +38 -0
- package/dist/types/containerd/options/image/ImagePullOptions.d.ts +53 -0
- package/dist/types/containerd/options/image/ImagePushOptions.d.ts +44 -0
- package/dist/types/containerd/options/network/NetworkCreateOptions.d.ts +82 -0
- package/dist/types/containerd/options/volume/VolumeCreateOptions.d.ts +26 -0
- package/dist/types/containerd/types/ContainerdBind.d.ts +28 -0
- package/dist/types/containerd/types/ContainerdBuildOutputCallback.d.ts +6 -0
- package/dist/types/containerd/types/ContainerdContainerNetwork.d.ts +40 -0
- package/dist/types/containerd/types/ContainerdContainerPort.d.ts +22 -0
- package/dist/types/containerd/types/ContainerdContainerState.d.ts +42 -0
- package/dist/types/containerd/types/ContainerdContainerStats.d.ts +107 -0
- package/dist/types/containerd/types/ContainerdDevice.d.ts +25 -0
- package/dist/types/containerd/types/ContainerdExecResult.d.ts +18 -0
- package/dist/types/containerd/types/ContainerdImageConfig.d.ts +46 -0
- package/dist/types/containerd/types/ContainerdNetworkInfo.d.ts +49 -0
- package/dist/types/containerd/types/ContainerdProgressCallback.d.ts +29 -0
- package/dist/types/containerd/types/ContainerdPruneResult.d.ts +21 -0
- package/dist/types/containerd/types/ContainerdRestartPolicy.d.ts +13 -0
- package/dist/types/containerd/types/ContainerdTmpfs.d.ts +20 -0
- package/dist/types/containerd/types/ContainerdVolume.d.ts +19 -0
- package/dist/types/containerd/types/ContainerdVolumeInfo.d.ts +26 -0
- package/dist/types/exports/Containerd.d.ts +69 -0
- package/package.json +51 -4
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
3
|
+
import { mkdir, mkdtemp, readFile, rm, stat } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { pipeline } from 'node:stream/promises';
|
|
7
|
+
import { extract as tarExtract, pack as tarPack } from 'tar-stream';
|
|
8
|
+
import { isGRPCError } from './ContainerdClient.js';
|
|
9
|
+
import { GRPCStatus } from '@lakutata/grpc';
|
|
10
|
+
import { formatPlatform, hostPlatform, matchPlatform, normalizePlatform, platformRank } from './Platforms.js';
|
|
11
|
+
import { imageName, parseReference } from './Reference.js';
|
|
12
|
+
import { digestOf, isIndex, isManifest, MediaTypes, RegistryClient } from './Registry.js';
|
|
13
|
+
/**
|
|
14
|
+
* The labels of containerd binding its content (its garbage collection keeps what an image references)
|
|
15
|
+
*/
|
|
16
|
+
const Labels = {
|
|
17
|
+
contentRef: (kind, index) => `containerd.io/gc.ref.content.${kind}${index === undefined ? '' : `.${index}`}`,
|
|
18
|
+
snapshotRef: (snapshotter) => `containerd.io/gc.ref.snapshot.${snapshotter}`,
|
|
19
|
+
uncompressed: 'containerd.io/uncompressed',
|
|
20
|
+
source: (registry) => `containerd.io/distribution.source.${registry}`
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The bytes of a stream, whole
|
|
24
|
+
* @param chunks
|
|
25
|
+
*/
|
|
26
|
+
async function collect(chunks) {
|
|
27
|
+
const all = [];
|
|
28
|
+
for await (const chunk of chunks)
|
|
29
|
+
all.push(Buffer.from(chunk));
|
|
30
|
+
return Buffer.concat(all);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The identifiers of the chain of layers (OCI): the ones of the snapshots of their unpacking
|
|
34
|
+
* @param diffIds
|
|
35
|
+
*/
|
|
36
|
+
export function chainIds(diffIds) {
|
|
37
|
+
const chain = [];
|
|
38
|
+
for (const diffId of diffIds) {
|
|
39
|
+
chain.push(chain.length ? `sha256:${createHash('sha256').update(`${chain[chain.length - 1]} ${diffId}`).digest('hex')}` : diffId);
|
|
40
|
+
}
|
|
41
|
+
return chain;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The platform of the configuration of an image
|
|
45
|
+
* @param config
|
|
46
|
+
*/
|
|
47
|
+
function configPlatform(config) {
|
|
48
|
+
return normalizePlatform({ os: config.os, architecture: config.architecture, variant: config.variant });
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The images of containerd: pulled from the registries, unpacked into the snapshots, pushed, imported and exported
|
|
52
|
+
* (OCI image layouts, the archives of docker save), as ctr and nerdctl do: their content bound by the labels of the
|
|
53
|
+
* garbage collection of containerd, under a lease until the image references it
|
|
54
|
+
*/
|
|
55
|
+
export class ImageStore {
|
|
56
|
+
/**
|
|
57
|
+
* @param client
|
|
58
|
+
* @param snapshotter
|
|
59
|
+
* @param temporary the directory of the archives extracted (a disk of the machine, not a small tmpfs)
|
|
60
|
+
* @param runnablePlatforms the platforms the machine runs, unpacked by the pulls and the imports (the one of the
|
|
61
|
+
* machine by default)
|
|
62
|
+
*/
|
|
63
|
+
constructor(client, snapshotter, temporary = os.tmpdir(), runnablePlatforms = () => [hostPlatform()]) {
|
|
64
|
+
this.client = client;
|
|
65
|
+
this.snapshotter = snapshotter;
|
|
66
|
+
this.temporary = temporary;
|
|
67
|
+
this.runnablePlatforms = runnablePlatforms;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The JSON of a content
|
|
71
|
+
* @param digest
|
|
72
|
+
*/
|
|
73
|
+
async readJson(digest) {
|
|
74
|
+
return JSON.parse((await this.client.readAll(digest)).toString('utf8'));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The best manifest of an index for a platform, among the ones the content store has (or all)
|
|
78
|
+
* @param index
|
|
79
|
+
* @param platform
|
|
80
|
+
* @param local
|
|
81
|
+
*/
|
|
82
|
+
async chooseManifest(index, platform, local) {
|
|
83
|
+
let best;
|
|
84
|
+
for (const descriptor of index.manifests) {
|
|
85
|
+
if (!isManifest(descriptor.mediaType) || !descriptor.platform)
|
|
86
|
+
continue;
|
|
87
|
+
const rank = platformRank(descriptor.platform, platform);
|
|
88
|
+
if (rank === undefined || (best && best.rank <= rank))
|
|
89
|
+
continue;
|
|
90
|
+
if (local && !(await this.client.contentInfo(descriptor.digest)))
|
|
91
|
+
continue;
|
|
92
|
+
best = { descriptor: descriptor, rank: rank };
|
|
93
|
+
}
|
|
94
|
+
return best?.descriptor;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The image of a name (the reference given or its normalized name), undefined when it does not exist
|
|
98
|
+
* @param name
|
|
99
|
+
*/
|
|
100
|
+
async get(name) {
|
|
101
|
+
return await this.client.getImage(imageName(name)) ?? (imageName(name) === name ? undefined : await this.client.getImage(name));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Resolve an image for a platform: its manifest and its configuration (the manifest of the platform of its index)
|
|
105
|
+
* @param name
|
|
106
|
+
* @param platform the one of the machine by default
|
|
107
|
+
*/
|
|
108
|
+
async resolve(name, platform = hostPlatform()) {
|
|
109
|
+
const image = await this.get(name);
|
|
110
|
+
if (!image)
|
|
111
|
+
throw Object.assign(new Error(`The image ${name} does not exist`), { code: 'NOT_FOUND' });
|
|
112
|
+
const target = image.target;
|
|
113
|
+
let manifestDescriptor = target;
|
|
114
|
+
if (isIndex(target.mediaType)) {
|
|
115
|
+
const chosen = await this.chooseManifest(await this.readJson(target.digest), platform, true);
|
|
116
|
+
if (!chosen)
|
|
117
|
+
throw Object.assign(new Error(`The image ${name} has no manifest of the platform ${formatPlatform(platform)}`), { code: 'PLATFORM' });
|
|
118
|
+
manifestDescriptor = chosen;
|
|
119
|
+
}
|
|
120
|
+
const manifest = await this.readJson(manifestDescriptor.digest);
|
|
121
|
+
const config = await this.readJson(manifest.config.digest);
|
|
122
|
+
return { image: image, name: image.name, target: target, manifestDescriptor: manifestDescriptor, manifest: manifest, config: config, platform: configPlatform(config) };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The platforms of an image the content store has (the ones pulled)
|
|
126
|
+
* @param name
|
|
127
|
+
*/
|
|
128
|
+
async platforms(name) {
|
|
129
|
+
const image = await this.get(name);
|
|
130
|
+
if (!image)
|
|
131
|
+
return [];
|
|
132
|
+
if (!isIndex(image.target.mediaType))
|
|
133
|
+
return [configPlatform(await this.readJson((await this.readJson(image.target.digest)).config.digest))];
|
|
134
|
+
const platforms = [];
|
|
135
|
+
for (const descriptor of (await this.readJson(image.target.digest)).manifests) {
|
|
136
|
+
if (descriptor.platform && isManifest(descriptor.mediaType) && await this.client.contentInfo(descriptor.digest))
|
|
137
|
+
platforms.push(normalizePlatform(descriptor.platform));
|
|
138
|
+
}
|
|
139
|
+
return platforms;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Unpack the layers of an image into the snapshots of the snapshotter (named by the chain of their identifiers), the
|
|
143
|
+
* configuration bound to the last one
|
|
144
|
+
* @param manifest
|
|
145
|
+
* @param lease
|
|
146
|
+
*/
|
|
147
|
+
async unpack(manifest, lease) {
|
|
148
|
+
const config = await this.readJson(manifest.config.digest);
|
|
149
|
+
const diffIds = config.rootfs?.diff_ids ?? [];
|
|
150
|
+
if (diffIds.length !== manifest.layers.length)
|
|
151
|
+
throw new Error(`The image ${manifest.config.digest} has ${manifest.layers.length} layers and ${diffIds.length} identifiers of them`);
|
|
152
|
+
const chain = chainIds(diffIds);
|
|
153
|
+
for (let index = 0; index < chain.length; index++) {
|
|
154
|
+
if (await this.client.statSnapshot(this.snapshotter, chain[index]))
|
|
155
|
+
continue;
|
|
156
|
+
const key = `extract-${randomBytes(6).toString('hex')}-${chain[index]}`;
|
|
157
|
+
const mounts = await this.client.prepareSnapshot(this.snapshotter, key, index ? chain[index - 1] : '', {}, lease);
|
|
158
|
+
try {
|
|
159
|
+
const applied = await this.client.applyLayer(manifest.layers[index], mounts, lease);
|
|
160
|
+
if (applied.digest !== diffIds[index])
|
|
161
|
+
throw new Error(`The layer ${manifest.layers[index].digest} unpacked is ${applied.digest}, not ${diffIds[index]}`);
|
|
162
|
+
await this.client.commitSnapshot(this.snapshotter, chain[index], key, {}, lease);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
await this.client.removeSnapshot(this.snapshotter, key);
|
|
166
|
+
//Unpacked meanwhile
|
|
167
|
+
if (isGRPCError(error, GRPCStatus.ALREADY_EXISTS))
|
|
168
|
+
continue;
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (chain.length)
|
|
173
|
+
await this.client.labelContent(manifest.config.digest, { [Labels.snapshotRef(this.snapshotter)]: chain[chain.length - 1] });
|
|
174
|
+
return chain[chain.length - 1] ?? '';
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Write a manifest, its configuration and its layers (the blobs given by a source), bound by the labels of the
|
|
178
|
+
* garbage collection
|
|
179
|
+
* @param manifest
|
|
180
|
+
* @param blob the bytes of a blob (its stream)
|
|
181
|
+
* @param lease
|
|
182
|
+
* @param labels the labels of all the content (its source)
|
|
183
|
+
* @param progress
|
|
184
|
+
*/
|
|
185
|
+
async writeImage(manifest, blob, lease, labels, progress) {
|
|
186
|
+
const configData = await blob(manifest.config);
|
|
187
|
+
const configBytes = configData instanceof Uint8Array ? Buffer.from(configData) : await collect(configData);
|
|
188
|
+
await this.client.writeContent(manifest.config, configBytes, lease, labels);
|
|
189
|
+
const config = JSON.parse(configBytes.toString('utf8'));
|
|
190
|
+
for (let index = 0; index < manifest.layers.length; index++) {
|
|
191
|
+
const layer = manifest.layers[index];
|
|
192
|
+
const uncompressed = config.rootfs?.diff_ids?.[index] ? { [Labels.uncompressed]: config.rootfs.diff_ids[index] } : {};
|
|
193
|
+
if (await this.client.contentInfo(layer.digest)) {
|
|
194
|
+
await this.client.labelContent(layer.digest, { ...labels, ...uncompressed });
|
|
195
|
+
progress?.({ digest: layer.digest, mediaType: layer.mediaType, current: layer.size, total: layer.size });
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const data = await blob(layer);
|
|
199
|
+
let current = 0;
|
|
200
|
+
const counted = async function* () {
|
|
201
|
+
for await (const chunk of data) {
|
|
202
|
+
current += chunk.length;
|
|
203
|
+
progress?.({ digest: layer.digest, mediaType: layer.mediaType, current: current, total: layer.size });
|
|
204
|
+
yield chunk;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
await this.client.writeContent(layer, data instanceof Uint8Array ? data : counted(), lease, { ...labels, ...uncompressed });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The labels of a manifest binding its configuration and its layers
|
|
212
|
+
* @param manifest
|
|
213
|
+
*/
|
|
214
|
+
manifestLabels(manifest) {
|
|
215
|
+
const labels = { [Labels.contentRef('config')]: manifest.config.digest };
|
|
216
|
+
manifest.layers.forEach((layer, index) => labels[Labels.contentRef('l', index)] = layer.digest);
|
|
217
|
+
return labels;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Pull an image from its registry: the manifests of the platforms asked (of its index), their configurations and
|
|
221
|
+
* their layers; the image named by its reference (docker.io/library/busybox:latest), unpacked for the machine
|
|
222
|
+
* @param reference
|
|
223
|
+
* @param options
|
|
224
|
+
*/
|
|
225
|
+
async pull(reference, options = {}) {
|
|
226
|
+
const parsed = parseReference(reference);
|
|
227
|
+
const registry = new RegistryClient(parsed, options.registry);
|
|
228
|
+
const platforms = options.platforms ?? [hostPlatform()];
|
|
229
|
+
const source = { [Labels.source(parsed.registry)]: parsed.repository };
|
|
230
|
+
const lease = await this.client.createLease();
|
|
231
|
+
try {
|
|
232
|
+
const root = await registry.resolve(parsed.digest ?? parsed.tag);
|
|
233
|
+
const manifests = [];
|
|
234
|
+
if (isIndex(root.descriptor.mediaType)) {
|
|
235
|
+
const index = JSON.parse(root.data.toString('utf8'));
|
|
236
|
+
const chosen = new Set();
|
|
237
|
+
if (platforms === 'all') {
|
|
238
|
+
index.manifests.filter((descriptor) => isManifest(descriptor.mediaType) && descriptor.platform?.os !== 'unknown').forEach((descriptor) => chosen.add(descriptor));
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
for (const platform of platforms) {
|
|
242
|
+
const descriptor = await this.chooseManifest(index, platform, false);
|
|
243
|
+
if (!descriptor) {
|
|
244
|
+
const available = index.manifests.filter((item) => !!item.platform && item.platform.os !== 'unknown').map((item) => formatPlatform(normalizePlatform(item.platform))).join(', ');
|
|
245
|
+
throw Object.assign(new Error(`The image ${parsed.name} has no manifest of the platform ${formatPlatform(platform)} (it has ${available})`), { code: 'PLATFORM' });
|
|
246
|
+
}
|
|
247
|
+
chosen.add(descriptor);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
for (const descriptor of chosen)
|
|
251
|
+
manifests.push({ descriptor: descriptor, data: await registry.fetchManifest(descriptor), index: index.manifests.indexOf(descriptor) });
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
manifests.push(root);
|
|
255
|
+
}
|
|
256
|
+
for (const { descriptor, data } of manifests) {
|
|
257
|
+
const manifest = JSON.parse(data.toString('utf8'));
|
|
258
|
+
await this.writeImage(manifest, (blob) => registry.fetchBlob(blob), lease, source, options.progress);
|
|
259
|
+
await this.client.writeContent(descriptor, data, lease, { ...source, ...this.manifestLabels(manifest) });
|
|
260
|
+
}
|
|
261
|
+
if (isIndex(root.descriptor.mediaType)) {
|
|
262
|
+
const labels = { ...source };
|
|
263
|
+
manifests.forEach(({ descriptor, index }) => labels[Labels.contentRef('m', index)] = descriptor.digest);
|
|
264
|
+
await this.client.writeContent(root.descriptor, root.data, lease, labels);
|
|
265
|
+
}
|
|
266
|
+
await this.client.putImage({ name: parsed.name, target: root.descriptor }, lease);
|
|
267
|
+
if (options.unpack ?? true)
|
|
268
|
+
await this.unpackFor(parsed.name, lease);
|
|
269
|
+
return parsed.name;
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
await this.client.deleteLease(lease);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Unpack an image for the platforms the machine runs, among the ones the content store has (its manifests pulled):
|
|
277
|
+
* not the other platforms, nor the attestations (unknown/unknown)
|
|
278
|
+
* @param name
|
|
279
|
+
* @param lease
|
|
280
|
+
*/
|
|
281
|
+
async unpackFor(name, lease) {
|
|
282
|
+
const image = await this.get(name);
|
|
283
|
+
if (!image)
|
|
284
|
+
return;
|
|
285
|
+
const manifests = isIndex(image.target.mediaType)
|
|
286
|
+
? (await this.readJson(image.target.digest)).manifests.filter((descriptor) => isManifest(descriptor.mediaType))
|
|
287
|
+
: [image.target];
|
|
288
|
+
const runnable = this.runnablePlatforms();
|
|
289
|
+
for (const descriptor of manifests) {
|
|
290
|
+
if (!(await this.client.contentInfo(descriptor.digest)))
|
|
291
|
+
continue;
|
|
292
|
+
const manifest = await this.readJson(descriptor.digest);
|
|
293
|
+
const platform = descriptor.platform ? normalizePlatform(descriptor.platform) : configPlatform(await this.readJson(manifest.config.digest));
|
|
294
|
+
if (!runnable.some((item) => matchPlatform(platform, item)))
|
|
295
|
+
continue;
|
|
296
|
+
await this.unpack(manifest, lease);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Push an image to a registry (as another reference): the manifest of the platform asked; without it, the manifests
|
|
301
|
+
* of all the platforms and the index when the content store has all of them, the manifest of the machine's platform
|
|
302
|
+
* otherwise
|
|
303
|
+
* @param name
|
|
304
|
+
* @param options
|
|
305
|
+
*/
|
|
306
|
+
async push(name, options = {}) {
|
|
307
|
+
const image = await this.get(name);
|
|
308
|
+
if (!image)
|
|
309
|
+
throw Object.assign(new Error(`The image ${name} does not exist`), { code: 'NOT_FOUND' });
|
|
310
|
+
const parsed = parseReference(options.reference ?? image.name);
|
|
311
|
+
const registry = new RegistryClient(parsed, options.registry);
|
|
312
|
+
const tag = parsed.tag ?? parsed.digest;
|
|
313
|
+
const pushManifest = async (descriptor, reference) => {
|
|
314
|
+
const data = await this.client.readAll(descriptor.digest);
|
|
315
|
+
const manifest = JSON.parse(data.toString('utf8'));
|
|
316
|
+
for (const blob of [manifest.config, ...manifest.layers]) {
|
|
317
|
+
await registry.pushBlob(blob, () => this.client.readContent(blob.digest));
|
|
318
|
+
options.progress?.({ digest: blob.digest, mediaType: blob.mediaType, current: blob.size, total: blob.size });
|
|
319
|
+
}
|
|
320
|
+
await registry.pushManifest(reference, descriptor.mediaType, data);
|
|
321
|
+
};
|
|
322
|
+
const target = image.target;
|
|
323
|
+
if (!isIndex(target.mediaType)) {
|
|
324
|
+
if (options.platform) {
|
|
325
|
+
const platform = configPlatform(await this.readJson((await this.readJson(target.digest)).config.digest));
|
|
326
|
+
if (!matchPlatform(platform, options.platform))
|
|
327
|
+
throw Object.assign(new Error(`The image ${name} has no manifest of the platform ${formatPlatform(options.platform)} to push (it is ${formatPlatform(platform)})`), { code: 'PLATFORM' });
|
|
328
|
+
}
|
|
329
|
+
return await pushManifest(target, tag);
|
|
330
|
+
}
|
|
331
|
+
const index = await this.readJson(target.digest);
|
|
332
|
+
const local = [];
|
|
333
|
+
for (const descriptor of index.manifests)
|
|
334
|
+
if (await this.client.contentInfo(descriptor.digest))
|
|
335
|
+
local.push(descriptor);
|
|
336
|
+
if (!options.platform && local.length === index.manifests.length) {
|
|
337
|
+
for (const descriptor of local)
|
|
338
|
+
await pushManifest(descriptor, descriptor.digest);
|
|
339
|
+
return await registry.pushManifest(tag, target.mediaType, await this.client.readAll(target.digest));
|
|
340
|
+
}
|
|
341
|
+
const chosen = await this.chooseManifest(index, options.platform ?? hostPlatform(), true);
|
|
342
|
+
if (!chosen)
|
|
343
|
+
throw Object.assign(new Error(`The image ${name} has no manifest of the platform ${formatPlatform(options.platform ?? hostPlatform())} to push`), { code: 'PLATFORM' });
|
|
344
|
+
await pushManifest(chosen, tag);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Commit the changes of a snapshot (the one of a container) on its image: a layer of their diff, the configuration
|
|
348
|
+
* of the image with its changes, a manifest of the platform, the image named (and unpacked)
|
|
349
|
+
* @param options
|
|
350
|
+
*/
|
|
351
|
+
async commit(options) {
|
|
352
|
+
const base = await this.resolve(options.image, options.platform);
|
|
353
|
+
const name = imageName(options.name);
|
|
354
|
+
const lease = await this.client.createLease();
|
|
355
|
+
const view = `commit-${randomBytes(6).toString('hex')}`;
|
|
356
|
+
try {
|
|
357
|
+
const chain = chainIds(base.config.rootfs?.diff_ids ?? []);
|
|
358
|
+
const left = chain.length ? await this.client.viewSnapshot(this.snapshotter, view, chain[chain.length - 1], lease) : [];
|
|
359
|
+
const right = await this.client.snapshotMounts(this.snapshotter, options.snapshotKey);
|
|
360
|
+
const layer = await this.client.diff(left, right, lease);
|
|
361
|
+
const diffId = (await this.client.contentInfo(layer.digest))?.labels?.[Labels.uncompressed];
|
|
362
|
+
if (!diffId)
|
|
363
|
+
throw new Error(`The layer ${layer.digest} of the commit has no uncompressed digest`);
|
|
364
|
+
const created = new Date().toISOString();
|
|
365
|
+
const changes = options.changes ?? {};
|
|
366
|
+
const config = {
|
|
367
|
+
...structuredClone(base.config),
|
|
368
|
+
created: created,
|
|
369
|
+
rootfs: { type: 'layers', diff_ids: [...base.config.rootfs?.diff_ids ?? [], diffId] }
|
|
370
|
+
};
|
|
371
|
+
const containerConfig = config.config ??= {};
|
|
372
|
+
if (changes.cmd)
|
|
373
|
+
containerConfig.Cmd = changes.cmd;
|
|
374
|
+
if (changes.entrypoint)
|
|
375
|
+
containerConfig.Entrypoint = changes.entrypoint;
|
|
376
|
+
if (changes.env) {
|
|
377
|
+
const env = new Map((containerConfig.Env ?? []).map((entry) => [entry.split('=')[0], entry]));
|
|
378
|
+
for (const entry of changes.env)
|
|
379
|
+
env.set(entry.split('=')[0], entry);
|
|
380
|
+
containerConfig.Env = [...env.values()];
|
|
381
|
+
}
|
|
382
|
+
if (changes.workingDir !== undefined)
|
|
383
|
+
containerConfig.WorkingDir = changes.workingDir;
|
|
384
|
+
if (changes.user !== undefined)
|
|
385
|
+
containerConfig.User = changes.user;
|
|
386
|
+
if (changes.labels)
|
|
387
|
+
containerConfig.Labels = { ...containerConfig.Labels ?? {}, ...changes.labels };
|
|
388
|
+
if (changes.exposedPorts)
|
|
389
|
+
containerConfig.ExposedPorts = { ...containerConfig.ExposedPorts ?? {}, ...Object.fromEntries(changes.exposedPorts.map((port) => [port.includes('/') ? port : `${port}/tcp`, {}])) };
|
|
390
|
+
if (options.author)
|
|
391
|
+
config.author = options.author;
|
|
392
|
+
config.history = [...config.history ?? [], { created: created, created_by: 'lakutata commit', ...options.comment ? { comment: options.comment } : {}, ...options.author ? { author: options.author } : {} }];
|
|
393
|
+
const configData = Buffer.from(JSON.stringify(config));
|
|
394
|
+
const configDescriptor = { mediaType: MediaTypes.OCI_CONFIG, digest: digestOf(configData), size: configData.length };
|
|
395
|
+
await this.client.writeContent(configDescriptor, configData, lease);
|
|
396
|
+
const manifest = {
|
|
397
|
+
schemaVersion: 2,
|
|
398
|
+
mediaType: MediaTypes.OCI_MANIFEST,
|
|
399
|
+
config: configDescriptor,
|
|
400
|
+
layers: [...base.manifest.layers.map((item) => ({ mediaType: item.mediaType, digest: item.digest, size: item.size })), { mediaType: layer.mediaType, digest: layer.digest, size: Number(layer.size) }]
|
|
401
|
+
};
|
|
402
|
+
const manifestData = Buffer.from(JSON.stringify(manifest));
|
|
403
|
+
const manifestDescriptor = { mediaType: MediaTypes.OCI_MANIFEST, digest: digestOf(manifestData), size: manifestData.length };
|
|
404
|
+
await this.client.writeContent(manifestDescriptor, manifestData, lease, this.manifestLabels(manifest));
|
|
405
|
+
await this.client.putImage({ name: name, target: manifestDescriptor }, lease);
|
|
406
|
+
await this.unpack(manifest, lease);
|
|
407
|
+
return name;
|
|
408
|
+
}
|
|
409
|
+
finally {
|
|
410
|
+
await this.client.removeSnapshot(this.snapshotter, view);
|
|
411
|
+
await this.client.deleteLease(lease);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Name an image again (a tag), its target the same
|
|
416
|
+
* @param name
|
|
417
|
+
* @param reference
|
|
418
|
+
*/
|
|
419
|
+
async tag(name, reference) {
|
|
420
|
+
const image = await this.get(name);
|
|
421
|
+
if (!image)
|
|
422
|
+
throw Object.assign(new Error(`The image ${name} does not exist`), { code: 'NOT_FOUND' });
|
|
423
|
+
const target = imageName(reference);
|
|
424
|
+
await this.client.putImage({ name: target, target: image.target, labels: image.labels });
|
|
425
|
+
return target;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Remove an image (its content and its snapshots are collected by containerd once nothing references them)
|
|
429
|
+
* @param name
|
|
430
|
+
*/
|
|
431
|
+
async remove(name) {
|
|
432
|
+
const image = await this.get(name);
|
|
433
|
+
if (!image)
|
|
434
|
+
throw Object.assign(new Error(`The image ${name} does not exist`), { code: 'NOT_FOUND' });
|
|
435
|
+
await this.client.deleteImage(image.name, true);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Import an archive of images: an OCI image layout (nerdctl save, docker save of Docker 25+), or an archive of
|
|
439
|
+
* docker save; the images named by the archive (or by a name given, to an archive of one image), unpacked for the
|
|
440
|
+
* platforms the machine runs
|
|
441
|
+
* @param source a file, or its stream
|
|
442
|
+
* @param options
|
|
443
|
+
*/
|
|
444
|
+
async import(source, options = {}) {
|
|
445
|
+
await mkdir(this.temporary, { recursive: true });
|
|
446
|
+
const directory = await mkdtemp(path.join(this.temporary, 'lakutata-containerd-import-'));
|
|
447
|
+
const lease = await this.client.createLease();
|
|
448
|
+
try {
|
|
449
|
+
await this.extract(typeof source === 'string' ? createReadStream(source) : source, directory);
|
|
450
|
+
const file = (name) => path.join(directory, ...name.split('/'));
|
|
451
|
+
const blobFile = (digest) => file(`blobs/${digest.replace(':', '/')}`);
|
|
452
|
+
const exists = async (name) => !!(await stat(name).catch(() => undefined));
|
|
453
|
+
const names = [];
|
|
454
|
+
const named = (name) => options.name ? imageName(options.name) : name ? imageName(name) : undefined;
|
|
455
|
+
const index = await exists(file('index.json')) ? JSON.parse(await readFile(file('index.json'), 'utf8')) : undefined;
|
|
456
|
+
const entries = await exists(file('manifest.json')) ? JSON.parse(await readFile(file('manifest.json'), 'utf8')) : [];
|
|
457
|
+
if (options.name) {
|
|
458
|
+
//One name for one image (its tags: its entries of the same digest), checked before any import
|
|
459
|
+
const images = new Set((index?.manifests ?? []).map((entry) => entry.digest)).size || new Set(entries.map((entry) => entry.Config)).size;
|
|
460
|
+
if (images > 1)
|
|
461
|
+
throw new Error(`The archive has ${images} images: the name ${options.name} can only be given to an archive of one image`);
|
|
462
|
+
}
|
|
463
|
+
if (index) {
|
|
464
|
+
const blob = async (descriptor) => createReadStream(blobFile(descriptor.digest));
|
|
465
|
+
for (const entry of index.manifests) {
|
|
466
|
+
const name = named(entry.annotations?.['io.containerd.image.name'] ?? entry.annotations?.['org.opencontainers.image.ref.name']);
|
|
467
|
+
await this.writeTree(entry, blob, lease);
|
|
468
|
+
if (!name)
|
|
469
|
+
continue;
|
|
470
|
+
await this.client.putImage({ name: name, target: { mediaType: entry.mediaType, digest: entry.digest, size: entry.size } }, lease);
|
|
471
|
+
if (!names.includes(name))
|
|
472
|
+
names.push(name);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (!names.length && entries.length) {
|
|
476
|
+
//docker save: uncompressed layers (or compressed ones), the configuration, the tags
|
|
477
|
+
for (const entry of entries) {
|
|
478
|
+
const configData = await readFile(file(entry.Config));
|
|
479
|
+
const layers = [];
|
|
480
|
+
const layerFiles = new Map();
|
|
481
|
+
for (const layer of entry.Layers) {
|
|
482
|
+
const data = await readFile(file(layer));
|
|
483
|
+
const digest = digestOf(data);
|
|
484
|
+
const magic = data.subarray(0, 4).toString('hex');
|
|
485
|
+
const mediaType = magic.startsWith('1f8b') ? MediaTypes.DOCKER_LAYER_GZIP : magic === '28b52ffd' ? 'application/vnd.oci.image.layer.v1.tar+zstd' : MediaTypes.DOCKER_LAYER;
|
|
486
|
+
layers.push({ mediaType: mediaType, digest: digest, size: data.length });
|
|
487
|
+
layerFiles.set(digest, file(layer));
|
|
488
|
+
}
|
|
489
|
+
const manifest = { schemaVersion: 2, mediaType: MediaTypes.DOCKER_MANIFEST, config: { mediaType: MediaTypes.DOCKER_CONFIG, digest: digestOf(configData), size: configData.length }, layers: layers };
|
|
490
|
+
const manifestData = Buffer.from(JSON.stringify(manifest));
|
|
491
|
+
const descriptor = { mediaType: MediaTypes.DOCKER_MANIFEST, digest: digestOf(manifestData), size: manifestData.length };
|
|
492
|
+
await this.writeImage(manifest, async (blob) => blob.digest === manifest.config.digest ? configData : createReadStream(layerFiles.get(blob.digest)), lease, {});
|
|
493
|
+
await this.client.writeContent(descriptor, manifestData, lease, this.manifestLabels(manifest));
|
|
494
|
+
const tags = options.name ? [options.name] : entry.RepoTags?.length ? entry.RepoTags : [`import-${Date.now()}@${descriptor.digest}`];
|
|
495
|
+
for (const tag of tags) {
|
|
496
|
+
const name = imageName(tag);
|
|
497
|
+
await this.client.putImage({ name: name, target: descriptor }, lease);
|
|
498
|
+
names.push(name);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
if (!names.length)
|
|
503
|
+
throw new Error('The archive has no image: an OCI image layout (index.json) or an archive of docker save (manifest.json)');
|
|
504
|
+
if (options.unpack ?? true)
|
|
505
|
+
for (const name of names)
|
|
506
|
+
await this.unpackFor(name, lease);
|
|
507
|
+
return names;
|
|
508
|
+
}
|
|
509
|
+
finally {
|
|
510
|
+
await this.client.deleteLease(lease);
|
|
511
|
+
await rm(directory, { recursive: true, force: true });
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Write a manifest or an index of an archive, and what it references (the manifests the archive has)
|
|
516
|
+
* @param descriptor
|
|
517
|
+
* @param blob
|
|
518
|
+
* @param lease
|
|
519
|
+
*/
|
|
520
|
+
async writeTree(descriptor, blob, lease) {
|
|
521
|
+
const data = await collect(await blob(descriptor));
|
|
522
|
+
if (isIndex(descriptor.mediaType)) {
|
|
523
|
+
const index = JSON.parse(data.toString('utf8'));
|
|
524
|
+
const labels = {};
|
|
525
|
+
for (let position = 0; position < index.manifests.length; position++) {
|
|
526
|
+
const child = index.manifests[position];
|
|
527
|
+
try {
|
|
528
|
+
await this.writeTree(child, blob, lease);
|
|
529
|
+
labels[Labels.contentRef('m', position)] = child.digest;
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
//The archives of a platform have the index of all of them
|
|
533
|
+
if (error.code !== 'ENOENT')
|
|
534
|
+
throw error;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return await this.client.writeContent(descriptor, data, lease, labels);
|
|
538
|
+
}
|
|
539
|
+
const manifest = JSON.parse(data.toString('utf8'));
|
|
540
|
+
await this.writeImage(manifest, blob, lease, {});
|
|
541
|
+
await this.client.writeContent(descriptor, data, lease, this.manifestLabels(manifest));
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Extract an archive into a directory
|
|
545
|
+
* @param source
|
|
546
|
+
* @param directory
|
|
547
|
+
*/
|
|
548
|
+
async extract(source, directory) {
|
|
549
|
+
const extractor = tarExtract();
|
|
550
|
+
extractor.on('entry', (header, entry, next) => {
|
|
551
|
+
const stream = entry;
|
|
552
|
+
const target = path.resolve(directory, header.name);
|
|
553
|
+
if (header.type !== 'file' || !target.startsWith(directory + path.sep)) {
|
|
554
|
+
stream.on('end', () => next()).resume();
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
mkdir(path.dirname(target), { recursive: true })
|
|
558
|
+
.then(() => pipeline(stream, createWriteStream(target)))
|
|
559
|
+
.then(() => next(), (error) => next(error));
|
|
560
|
+
});
|
|
561
|
+
await pipeline(source, extractor);
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Export images into an archive: an OCI image layout (named by io.containerd.image.name), with the manifest.json of
|
|
565
|
+
* docker load (the manifest of the platform of the machine, or the first one)
|
|
566
|
+
* @param names
|
|
567
|
+
* @param destination a file, or its stream
|
|
568
|
+
*/
|
|
569
|
+
async export(names, destination) {
|
|
570
|
+
const blobs = new Map();
|
|
571
|
+
const entries = [];
|
|
572
|
+
const dockerManifests = [];
|
|
573
|
+
const blobPath = (digest) => `blobs/${digest.replace(':', '/')}`;
|
|
574
|
+
for (const name of names) {
|
|
575
|
+
const image = await this.get(name);
|
|
576
|
+
if (!image)
|
|
577
|
+
throw Object.assign(new Error(`The image ${name} does not exist`), { code: 'NOT_FOUND' });
|
|
578
|
+
const target = { mediaType: image.target.mediaType, digest: image.target.digest, size: Number(image.target.size) };
|
|
579
|
+
const reference = (() => {
|
|
580
|
+
try {
|
|
581
|
+
return parseReference(image.name);
|
|
582
|
+
}
|
|
583
|
+
catch {
|
|
584
|
+
return undefined;
|
|
585
|
+
}
|
|
586
|
+
})();
|
|
587
|
+
entries.push({ ...target, annotations: { 'io.containerd.image.name': image.name, ...reference?.tag ? { 'org.opencontainers.image.ref.name': reference.tag } : {} } });
|
|
588
|
+
blobs.set(target.digest, target);
|
|
589
|
+
const manifests = isIndex(target.mediaType) ? (await this.readJson(target.digest)).manifests : [target];
|
|
590
|
+
let first;
|
|
591
|
+
for (const descriptor of manifests) {
|
|
592
|
+
if (!isManifest(descriptor.mediaType) || !(await this.client.contentInfo(descriptor.digest)))
|
|
593
|
+
continue;
|
|
594
|
+
const manifest = await this.readJson(descriptor.digest);
|
|
595
|
+
blobs.set(descriptor.digest, descriptor);
|
|
596
|
+
for (const blob of [manifest.config, ...manifest.layers])
|
|
597
|
+
blobs.set(blob.digest, blob);
|
|
598
|
+
const config = await this.readJson(manifest.config.digest);
|
|
599
|
+
if (!first || platformRank(configPlatform(config), hostPlatform()) !== undefined)
|
|
600
|
+
first = manifest;
|
|
601
|
+
}
|
|
602
|
+
if (first && reference) {
|
|
603
|
+
const short = reference.registry === 'docker.io' ? reference.repository.replace(/^library\//, '') : `${reference.registry}/${reference.repository}`;
|
|
604
|
+
dockerManifests.push({ Config: blobPath(first.config.digest), RepoTags: reference.tag ? [`${short}:${reference.tag}`] : [], Layers: first.layers.map((layer) => blobPath(layer.digest)) });
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
const archive = tarPack();
|
|
608
|
+
const output = pipeline(archive, typeof destination === 'string' ? createWriteStream(destination) : destination);
|
|
609
|
+
const entry = (name, data) => new Promise((resolve, reject) => void archive.entry({ name: name, size: data.length, mode: 0o644 }, data, (error) => error ? reject(error) : resolve()));
|
|
610
|
+
try {
|
|
611
|
+
await entry('oci-layout', Buffer.from(JSON.stringify({ imageLayoutVersion: '1.0.0' })));
|
|
612
|
+
for (const blob of blobs.values()) {
|
|
613
|
+
const size = Number((await this.client.contentInfo(blob.digest))?.size ?? blob.size);
|
|
614
|
+
await new Promise((resolve, reject) => {
|
|
615
|
+
const writer = archive.entry({ name: blobPath(blob.digest), size: size, mode: 0o644 }, (error) => error ? reject(error) : resolve());
|
|
616
|
+
pipeline(this.client.readContent(blob.digest), writer).catch(reject);
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
await entry('index.json', Buffer.from(JSON.stringify({ schemaVersion: 2, mediaType: MediaTypes.OCI_INDEX, manifests: entries })));
|
|
620
|
+
await entry('manifest.json', Buffer.from(JSON.stringify(dockerManifests)));
|
|
621
|
+
archive.finalize();
|
|
622
|
+
}
|
|
623
|
+
catch (error) {
|
|
624
|
+
archive.destroy(error);
|
|
625
|
+
}
|
|
626
|
+
await output;
|
|
627
|
+
}
|
|
628
|
+
}
|