|
| 1 | +// ***************************************************************************** |
| 2 | +// Copyright (C) 2024 Typefox and others. |
| 3 | +// |
| 4 | +// This program and the accompanying materials are made available under the |
| 5 | +// terms of the Eclipse Public License v. 2.0 which is available at |
| 6 | +// http://www.eclipse.org/legal/epl-2.0. |
| 7 | +// |
| 8 | +// This Source Code may also be made available under the following Secondary |
| 9 | +// Licenses when the conditions for such availability set forth in the Eclipse |
| 10 | +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 |
| 11 | +// with the GNU Classpath Exception which is available at |
| 12 | +// https://www.gnu.org/software/classpath/license.html. |
| 13 | +// |
| 14 | +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 |
| 15 | +// ***************************************************************************** |
| 16 | +import * as Docker from 'dockerode'; |
| 17 | +import { injectable, interfaces } from '@theia/core/shared/inversify'; |
| 18 | +import { ContainerCreationContribution } from '../docker-container-service'; |
| 19 | +import { DevContainerConfiguration, ImageContainer } from '../devcontainer-file'; |
| 20 | + |
| 21 | +export function registerContainerCreationContributions(bind: interfaces.Bind): void { |
| 22 | + bind(ContainerCreationContribution).to(ImageFileContribution).inSingletonScope(); |
| 23 | + bind(ContainerCreationContribution).to(ForwardPortsContribution).inSingletonScope(); |
| 24 | + bind(ContainerCreationContribution).to(MountsContribution).inSingletonScope(); |
| 25 | +} |
| 26 | + |
| 27 | +@injectable() |
| 28 | +export class ImageFileContribution implements ContainerCreationContribution { |
| 29 | + async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: ImageContainer, api: Docker): Promise<void> { |
| 30 | + // check if image container |
| 31 | + if (containerConfig.image) { |
| 32 | + await api.pull(containerConfig.image); |
| 33 | + createOptions.Image = containerConfig.image; |
| 34 | + } |
| 35 | + } |
| 36 | +} |
| 37 | + |
| 38 | +@injectable() |
| 39 | +export class ForwardPortsContribution implements ContainerCreationContribution { |
| 40 | + async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration, api: Docker): Promise<void> { |
| 41 | + if (!containerConfig.forwardPorts) { |
| 42 | + return; |
| 43 | + } |
| 44 | + |
| 45 | + for (const port of containerConfig.forwardPorts) { |
| 46 | + let portKey: string; |
| 47 | + let hostPort: string; |
| 48 | + if (typeof port === 'string') { |
| 49 | + const parts = port.split(':'); |
| 50 | + portKey = isNaN(+parts[0]) ? parts[0] : `${parts[0]}/tcp`; |
| 51 | + hostPort = parts[1] ?? parts[0]; |
| 52 | + } else { |
| 53 | + portKey = `${port}/tcp`; |
| 54 | + hostPort = port.toString(); |
| 55 | + } |
| 56 | + createOptions.ExposedPorts![portKey] = {}; |
| 57 | + createOptions.HostConfig!.PortBindings[portKey] = [{ HostPort: hostPort }]; |
| 58 | + } |
| 59 | + |
| 60 | + } |
| 61 | + |
| 62 | +} |
| 63 | + |
| 64 | +@injectable() |
| 65 | +export class MountsContribution implements ContainerCreationContribution { |
| 66 | + async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration, api: Docker): Promise<void> { |
| 67 | + if (!containerConfig.mounts) { |
| 68 | + return; |
| 69 | + } |
| 70 | + |
| 71 | + createOptions.HostConfig!.Mounts!.push(...containerConfig.mounts |
| 72 | + .map(mount => typeof mount === 'string' ? |
| 73 | + this.parseMountString(mount) : |
| 74 | + { Source: mount.source, Target: mount.target, Type: mount.type ?? 'bind' })); |
| 75 | + } |
| 76 | + |
| 77 | + parseMountString(mount: string): Docker.MountSettings { |
| 78 | + const parts = mount.split(','); |
| 79 | + return { |
| 80 | + Source: parts.find(part => part.startsWith('source=') || part.startsWith('src='))?.split('=')[1]!, |
| 81 | + Target: parts.find(part => part.startsWith('target=') || part.startsWith('dst='))?.split('=')[1]!, |
| 82 | + Type: (parts.find(part => part.startsWith('type='))?.split('=')[1] ?? 'bind') as Docker.MountType |
| 83 | + }; |
| 84 | + } |
| 85 | +} |
0 commit comments