Skip to content

Commit ae6bd09

Browse files
committed
better container creation extension features
Signed-off-by: Jonah Iden <[email protected]>
1 parent 369147f commit ae6bd09

File tree

4 files changed

+499
-16
lines changed

4 files changed

+499
-16
lines changed

packages/dev-container/src/electron-node/dev-container-backend-module.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ import { ContainerModule } from '@theia/core/shared/inversify';
1818
import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module';
1919
import { DevContainerConnectionProvider } from './remote-container-connection-provider';
2020
import { RemoteContainerConnectionProvider, RemoteContainerConnectionProviderPath } from '../electron-common/remote-container-connection-provider';
21-
import { DockerContainerService } from './docker-container-service';
21+
import { ContainerCreationContribution, DockerContainerService } from './docker-container-service';
22+
import { bindContributionProvider } from '@theia/core';
23+
import { registerContainerCreationContributions } from './devcontainer-contributions/main-container-creation-contributions';
2224

2325
export const remoteConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => {
26+
bindContributionProvider(bind, ContainerCreationContribution);
27+
registerContainerCreationContributions(bind);
28+
2429
bind(DevContainerConnectionProvider).toSelf().inSingletonScope();
2530
bind(RemoteContainerConnectionProvider).toService(DevContainerConnectionProvider);
2631
bindBackendService(RemoteContainerConnectionProviderPath, RemoteContainerConnectionProvider);
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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

Comments
 (0)