Задача Добавить функцию скачивания докер-образов
Добавить функцию скачивания докер-образов
Сейчас есть запрос на создание контейнера
mutation createContainer {
createContainer(
image: "node:22-alpine"
cmd: ["tail", "-f", "/dev/null"]
labels: { test: "SDfsdf" }
) {
...DockerContainer_
}
}
Получаем ошибку
{
"errors": [
{
"message": "(HTTP code 404) no such container - No such image: node:22-alpine ",
"locations": [
{
"line": 16,
"column": 3
}
],
"path": [
"createContainer"
],
Это потому что образ еще не скачан. Надо: 1. Добавить отдельный резолвер для скачивания образов 2. Добавить в резолвер создания контейнера проверку есть ли уже образ, и если нету, то скачивать его, и только потом создавать контейнер.
Ворклоги
Сделано.
Хелперы:
Скачивание образа
import Docker from 'dockerode'
import { DockerImage } from '../DockerImage/types'
export async function pullDockerImage(
docker: Docker,
image: string,
): Promise<DockerImage> {
return new Promise((resolve, reject) => {
docker.pull(image, (err: Error | null, stream: NodeJS.ReadableStream) => {
if (err) {
reject(err)
return
}
docker.modem.followProgress(
stream,
async (err: Error | null) => {
if (err) {
reject(err)
return
}
try {
const imageInfo = docker.getImage(image)
const inspectData = await imageInfo.inspect()
resolve({
id: inspectData.Id,
repoTags: inspectData.RepoTags,
repoDigests: inspectData.RepoDigests,
created: new Date(inspectData.Created),
size: inspectData.Size,
virtualSize: inspectData.VirtualSize,
labels: inspectData.Config?.Labels ?? null,
})
} catch (inspectErr) {
reject(inspectErr)
}
},
(event: { status: string }) => {
if (process.env.NODE_ENV === 'development') {
// eslint-disable-next-line no-console
console.log('pullDockerImage progress:', event.status)
}
},
)
})
})
}
Проверка наличия образа и скачивание
import Docker from 'dockerode'
import { pullDockerImage } from './pullDockerImage'
export async function ensureDockerImage(
docker: Docker,
image: string,
): Promise<void> {
try {
const imageInfo = docker.getImage(image)
await imageInfo.inspect()
} catch (err) {
if ((err as { statusCode?: number }).statusCode === 404) {
await pullDockerImage(docker, image)
} else {
throw err
}
}
}
Обновленный резолвер создания контейнера с проверкой и скачиванием образа
import { Container } from 'dockerode'
import { builder } from 'server/schema/builder'
import { ensureDockerImage } from '../../helpers/ensureDockerImage'
import { DockerContainer } from '../types'
export const createDockerContainerResolver = builder.mutationField(
'createDockerContainer',
(t) =>
t.field({
type: DockerContainer,
nullable: true,
args: {
image: t.arg.string({ required: true }),
labels: t.arg({ type: 'Json' }),
cmd: t.arg.stringList(),
},
async resolve(_, { image, labels, cmd }, ctx) {
const { dockerClient } = ctx
const docker = dockerClient.getDockerClient()
await ensureDockerImage(docker, image)
let auxContainer: Container | undefined
return docker
.createContainer({
Image: image,
AttachStdin: false,
AttachStdout: true,
AttachStderr: true,
Tty: true,
Cmd: cmd ?? undefined,
OpenStdin: false,
StdinOnce: false,
Labels:
typeof labels === 'object'
? (labels as Record<string, string>)
: undefined,
})
.then(async function (container) {
auxContainer = container
return auxContainer.start()
})
.then(function () {
return auxContainer?.inspect()
})
.then(function (containerInfo): DockerContainer | null {
return containerInfo
? {
id: containerInfo.Id,
names: [containerInfo.Name],
command: containerInfo.Config.Cmd,
created: new Date(containerInfo.Created),
image: containerInfo.Config.Image,
imageID: containerInfo.Image,
state: containerInfo.State.Status,
status: containerInfo.State.Status,
labels: containerInfo.Config.Labels,
}
: null
})
},
}),
)