feat(dd): add support for backend extensions of Docker Desktop (#3435)

fix https://github.com/containers/podman-desktop/issues/2397

Signed-off-by: Florent Benoit <fbenoit@redhat.com>
This commit is contained in:
Florent BENOIT 2023-08-09 21:25:20 +02:00 committed by GitHub
parent eaa8a87afd
commit 97239a78f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 19 deletions

View file

@ -36,9 +36,15 @@ let dockerDesktopInstallation: TestDockerDesktopInstallation;
const contributionManagerLoadMetadataMock = vi.fn();
const contributionManagerInitMock = vi.fn();
const contributionManagerFindComposeBinaryMock = vi.fn();
const contributionManagerEnhanceComposeFileMock = vi.fn();
const containerProviderStartVMMock = vi.fn();
const contributionManager = {
init: contributionManagerInitMock,
loadMetadata: contributionManagerLoadMetadataMock,
findComposeBinary: contributionManagerFindComposeBinaryMock,
enhanceComposeFile: contributionManagerEnhanceComposeFileMock,
startVM: containerProviderStartVMMock,
} as unknown as ContributionManager;
const containerProviderGetFirstRunningConnectionMock = vi.fn();
@ -318,8 +324,17 @@ test('Check handlePluginInstall', async () => {
// mock extractDockerDesktopFiles
vi.spyOn(dockerDesktopInstallation, 'extractDockerDesktopFiles').mockResolvedValue();
// mock findComposeBinary
contributionManagerFindComposeBinaryMock.mockResolvedValue('/my-compose-binary');
// mock enhanceComposeFile
contributionManagerEnhanceComposeFileMock.mockResolvedValue('/my-enhanced-compose-file.yaml');
contributionManagerLoadMetadataMock.mockResolvedValue({
name: 'My Extension',
vm: {
composefile: 'docker-compose.yaml',
},
});
const logCallbackId = 2503;
@ -336,4 +351,6 @@ test('Check handlePluginInstall', async () => {
// contribution manager is called
expect(contributionManagerInitMock).toBeCalled();
expect(containerProviderStartVMMock).toBeCalledWith('My Extension', '/my-enhanced-compose-file.yaml', true);
});

View file

@ -67,6 +67,10 @@ export class DockerDesktopInstallation {
});
}
if (metadata.vm?.composefile) {
files.push(metadata.vm.composefile);
}
// host binaries
const hostFiles: string[] = [];
if (metadata?.host?.binaries) {
@ -377,6 +381,7 @@ export class DockerDesktopInstallation {
await this.extractDockerDesktopFiles(tmpFolderPath, finalFolderPath, reportLog);
event.reply('docker-desktop-plugin:install-log', logCallbackId, 'Loading metadata...');
// check metadata. If name is missing, add the one from the image
const metadata = await this.contributionManager.loadMetadata(finalFolderPath);
if (!metadata.name) {
@ -385,8 +390,41 @@ export class DockerDesktopInstallation {
await this.contributionManager.saveMetadata(finalFolderPath, metadata);
}
// if there is a VM, need to generate the updated compose file
let enhancedComposeFile;
if (metadata.vm) {
// check compose presence
event.reply('docker-desktop-plugin:install-log', logCallbackId, 'Check compose being setup...');
const foundPath = await this.contributionManager.findComposeBinary();
if (!foundPath) {
event.reply('docker-desktop-plugin:install-error', logCallbackId, 'Compose binary not found.');
return;
} else {
event.reply('docker-desktop-plugin:install-log', logCallbackId, `Compose binary found at ${foundPath}.`);
}
event.reply('docker-desktop-plugin:install-log', logCallbackId, 'Enhance compose file...');
// need to update the compose file
try {
enhancedComposeFile = await this.contributionManager.enhanceComposeFile(finalFolderPath, imageName, metadata);
} catch (error) {
event.reply('docker-desktop-plugin:install-error', logCallbackId, error);
return;
}
// try to start the VM
event.reply('docker-desktop-plugin:install-log', logCallbackId, 'Starting compose project...');
await this.contributionManager.startVM(metadata.name, enhancedComposeFile, true);
}
event.reply('docker-desktop-plugin:install-end', logCallbackId, 'Extension Successfully installed.');
// refresh contributions
await this.contributionManager.init();
try {
await this.contributionManager.init();
} catch (error) {
event.reply('docker-desktop-plugin:install-error', logCallbackId, error);
return;
}
}
}

View file

@ -295,40 +295,44 @@ export class DockerExtensionPreload {
hostname,
};
const vmServicePort = urlParams.get('vmServicePort') || undefined;
// do we have a service being exposed ?
const doRequest = async (config: RequestConfig): Promise<unknown> => {
if (vmServicePort) {
return ipcRenderer.invoke('docker-desktop-adapter:extensionVMServiceRequest', vmServicePort, config);
} else {
throw new Error(`no service port defined for request ${config}`);
}
};
const extensionVMService: dockerDesktopAPI.HttpService = {
get: async (url: string): Promise<unknown> => {
console.error('extensionVMService.get not implemented', url);
return {} as any;
return doRequest({ url, method: 'GET', headers: {}, data: undefined });
},
post: async (url: string, data: any): Promise<unknown> => {
console.error('extensionVMService.post not implemented', url, data);
return {} as any;
return doRequest({ url, method: 'POST', headers: {}, data });
},
put: async (url: string, data: any): Promise<unknown> => {
console.error('extensionVMService.put not implemented', url, data);
return {} as any;
return doRequest({ url, method: 'PUT', headers: {}, data });
},
patch: async (url: string, data: any): Promise<unknown> => {
console.error('extensionVMService.patch not implemented', url, data);
return {} as any;
return doRequest({ url, method: 'PATCH', headers: {}, data });
},
delete: async (url: string): Promise<unknown> => {
console.error('extensionVMService.delete not implemented', url);
return {} as any;
return doRequest({ url, method: 'DELETE', headers: {}, data: undefined });
},
head: async (url: string): Promise<unknown> => {
console.error('extensionVMService.head not implemented', url);
return {} as any;
return doRequest({ url, method: 'HEAD', headers: {}, data: undefined });
},
request: async (config: RequestConfig): Promise<unknown> => {
console.error('extensionVMService.request not implemented', config);
return {} as any;
return doRequest(config);
},
};
const extensionCliVM: dockerDesktopAPI.ExtensionCli = {
//FIXME:
exec: {} as any,
// need to call exec inside a container for the VM service
exec: this.getExec('VM_SERVICE'),
};
const extensionVM: dockerDesktopAPI.ExtensionVM = {

View file

@ -1229,7 +1229,7 @@ function initExposure(): void {
});
contextBridge.exposeInMainWorld('ddExtensionDelete', async (extensionName: string): Promise<void> => {
return ipcRenderer.invoke('docker-desktop-plugin:delete', extensionName);
return ipcInvoke('docker-desktop-plugin:delete', extensionName);
});
contextBridge.exposeInMainWorld('getDDPreloadPath', async (): Promise<string> => {

View file

@ -41,7 +41,7 @@ window.events?.receive('dev-tools:open-extension', extensionId => {
<Route path="/*" breadcrumb="{name}">
<webview
id="dd-webview-{webviewId}"
src="{source}?extensionName={currentContrib.extensionId}&arch={arch}&hostname={hostname}&platform={platform}"
src="{source}?extensionName={currentContrib.extensionId}&arch={arch}&hostname={hostname}&platform={platform}&vmServicePort={currentContrib.vmServicePort}"
preload="{preloadPath}"
style="height: 100%; width: 100%"></webview>
</Route>