Skip to content

Commit

Permalink
feat: allow image push to lima kubernetes cluster
Browse files Browse the repository at this point in the history
Signed-off-by: Anders F Björklund <anders.f.bjorklund@gmail.com>
  • Loading branch information
afbjorklund committed Oct 24, 2023
1 parent 797fe20 commit e12640e
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 2 deletions.
17 changes: 16 additions & 1 deletion extensions/lima/package.json
Expand Up @@ -36,7 +36,21 @@
"description": "Instance name (default is same name as type)"
}
}
}
},
"menus": {
"dashboard/image": [
{
"command": "lima.image.move",
"title": "Push image to Lima cluster"
}
]
},
"commands": [
{
"command": "lima.image.move",
"title": "Lima: Move image to cluster..."
}
]
},
"scripts": {
"build": "tsc && node ./scripts/build.js",
Expand All @@ -50,6 +64,7 @@
"devDependencies": {
"7zip-min": "^1.4.4",
"mkdirp": "^3.0.1",
"tmp-promise": "^3.0.3",
"zip-local": "^0.3.5"
}
}
21 changes: 20 additions & 1 deletion extensions/lima/src/extension.ts
Expand Up @@ -21,17 +21,24 @@ import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs';

import { configuration } from '@podman-desktop/api';
import { configuration, ProgressLocation } from '@podman-desktop/api';
import { getLimactl } from './limactl';
import { ImageHandler } from './image-handler';

type limaProviderType = 'docker' | 'podman' | 'kubernetes';

const LIMA_MOVE_IMAGE_COMMAND = 'lima.image.move';

const imageHandler = new ImageHandler();

function registerProvider(
extensionContext: extensionApi.ExtensionContext,
provider: extensionApi.Provider,
providerPath: string,
): void {
let providerState: extensionApi.ProviderConnectionStatus = 'unknown';
const providerType: limaProviderType = configuration.getConfiguration('lima').get('type');
const instanceName: string = configuration.getConfiguration('lima').get('name') || providerType;
if (providerType === 'podman' || providerType === 'docker') {
const connection: extensionApi.ContainerProviderConnection = {
name: 'Lima',
Expand All @@ -57,6 +64,18 @@ function registerProvider(
const disposable = provider.registerKubernetesProviderConnection(connection);
provider.updateStatus('started');
extensionContext.subscriptions.push(disposable);
extensionContext.subscriptions.push(
extensionApi.commands.registerCommand(LIMA_MOVE_IMAGE_COMMAND, async image => {
return extensionApi.window.withProgress(
{ location: ProgressLocation.TASK_WIDGET, title: `Loading ${image.name} to lima.` },
async progress => {
await imageHandler.moveImage(image, instanceName, getLimactl());
// Mark the task as completed
progress.report({ increment: -1 });
},
);
}),
);
}
console.log('Lima extension is active');
}
Expand Down
83 changes: 83 additions & 0 deletions extensions/lima/src/image-handler.ts
@@ -0,0 +1,83 @@
/**********************************************************************
* Copyright (C) 2023 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import * as extensionApi from '@podman-desktop/api';
import { tmpName } from 'tmp-promise';
import { getInstallationPath } from './limactl';
import * as fs from 'node:fs';

type ImageInfo = { engineId: string; name?: string; tag?: string };

// Handle the image move command when moving from Podman or Docker to Lima
export class ImageHandler {
// Move image from Podman or Docker to Lima
async moveImage(image: ImageInfo, instanceName: string, limactl: string): Promise<void> {
// If there's no image name passed in, we can't do anything
if (!image.name) {
throw new Error('Image selection not supported yet');
}

// Only proceed if instance was given
if (instanceName) {
let name = image.name;
let filename: string;
const env = Object.assign({}, process.env);

// Create a name:tag string for the image
if (image.tag) {
name = name + ':' + image.tag;
}

env.PATH = getInstallationPath();
try {
// Create a temporary file to store the image
filename = await tmpName();

// Save the image to the temporary file
await extensionApi.containerEngine.saveImage(image.engineId, name, filename);

// Run the Lima commands to push the image to the cluster
const { stdout: tempname } = await extensionApi.process.exec(limactl, ['shell', instanceName, 'mktemp'], {
env: env,
});
await extensionApi.process.exec(limactl, ['copy', filename, instanceName + ':' + tempname], { env: env });
const loadCommand = ['sudo', 'ctr', '-n=k8s.io', 'images', 'import']; // or "sudo nerdctl -n k8s.io load -i"
await extensionApi.process.exec(limactl, ['shell', instanceName, ...loadCommand, tempname], { env: env });
await extensionApi.process.exec(limactl, ['shell', instanceName, 'rm', tempname], { env: env });

// Show a dialog to the user that the image was pushed
// TODO: Change this to taskbar notification when implemented
await extensionApi.window.showInformationMessage(
`Image ${image.name} pushed to Lima instance: ${instanceName}`,
);
} catch (err) {
// Show a dialog error to the user that the image was not pushed
await extensionApi.window.showErrorMessage(
`Unable to push image ${image.name} to Lima instance: ${instanceName}. Error: ${err}`,
);

// Throw the errors to the console aswell
throw new Error(`Unable to push image to Lima instance: ${err}`);
} finally {
// Remove the temporary file if one was created
if (filename !== undefined) {
await fs.promises.rm(filename);
}
}
}
}
}

0 comments on commit e12640e

Please sign in to comment.