Skip to content

Commit

Permalink
chore: remove custom utility functions (#29)
Browse files Browse the repository at this point in the history
* chore: remove custom utility functions

make usage of Podman Desktop API instead

Signed-off-by: Florent Benoit <[email protected]>
  • Loading branch information
benoitf authored Jan 26, 2024
1 parent dd0a488 commit 7cea44b
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 31 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
},
"devDependencies": {
"7zip-min": "^1.4.4",
"@podman-desktop/api": "^1.0.1",
"@podman-desktop/api": "^1.6.4",
"@typescript-eslint/eslint-plugin": "^5.59.7",
"@typescript-eslint/parser": "^5.59.7",
"@vitest/coverage-c8": "^0.31.4",
Expand Down
5 changes: 4 additions & 1 deletion src/minikube-installer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (C) 2023 Red Hat, Inc.
* Copyright (C) 2023-2024 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.
Expand Down Expand Up @@ -32,6 +32,9 @@ vi.mock('@podman-desktop/api', async () => {
withProgress: vi.fn(),
showNotification: vi.fn(),
},
env: {
isWindows: vi.fn(),
},
ProgressLocation: {
APP_ICON: 1,
},
Expand Down
6 changes: 3 additions & 3 deletions src/minikube-installer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (C) 2023 Red Hat, Inc.
* Copyright (C) 2023-2024 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.
Expand All @@ -21,7 +21,7 @@ import * as os from 'node:os';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { Octokit } from '@octokit/rest';
import { isWindows, installBinaryToSystem } from './util';
import { installBinaryToSystem } from './util';
import type { components } from '@octokit/openapi-types';

const githubOrganization = 'kubernetes';
Expand Down Expand Up @@ -133,7 +133,7 @@ export class MinikubeInstaller {
fs.mkdirSync(this.storagePath);
}
fs.appendFileSync(destFile, Buffer.from(asset.data as unknown as ArrayBuffer));
if (!isWindows()) {
if (!extensionApi.env.isWindows) {
const stat = fs.statSync(destFile);
fs.chmodSync(destFile, stat.mode | fs.constants.S_IXUSR);
}
Expand Down
197 changes: 197 additions & 0 deletions src/util.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/**********************************************************************
* Copyright (C) 2024 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
***********************************************************************/

/* eslint-disable @typescript-eslint/no-explicit-any */

import * as extensionApi from '@podman-desktop/api';
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
import { detectMinikube, getMinikubePath, runCliCommand } from './util';
import * as childProcess from 'node:child_process';
import type { MinikubeInstaller } from './minikube-installer';

vi.mock('node:child_process');

vi.mock('@podman-desktop/api', async () => {
return {
window: {
showInformationMessage: vi.fn().mockReturnValue(Promise.resolve('Yes')),
showErrorMessage: vi.fn(),
withProgress: vi.fn(),
showNotification: vi.fn(),
},
env: {
isMac: vi.fn(),
isWindows: vi.fn(),
isLinux: vi.fn(),
},
ProgressLocation: {
APP_ICON: 1,
},
Disposable: {
from: vi.fn(),
},
};
});

const originalProcessEnv = process.env;
beforeEach(() => {
vi.clearAllMocks();
process.env = {};
});

afterEach(() => {
process.env = originalProcessEnv;
});

test('getMinikubePath on macOS', async () => {
vi.mocked(extensionApi.env).isMac = true;

const computedPath = getMinikubePath();
expect(computedPath).toEqual('/usr/local/bin:/opt/homebrew/bin:/opt/local/bin:/opt/podman/bin');
});

test('getMinikubePath on macOS with existing PATH', async () => {
const existingPATH = '/my-existing-path';
process.env.PATH = existingPATH;
vi.mocked(extensionApi.env).isMac = true;

const computedPath = getMinikubePath();
expect(computedPath).toEqual(`${existingPATH}:/usr/local/bin:/opt/homebrew/bin:/opt/local/bin:/opt/podman/bin`);
});

test.each([
['macOS', true, false],
['windows', false, true],
])('detectMinikube on %s', async (operatingSystem, isMac, isWindows) => {
vi.mocked(extensionApi.env).isMac = isMac;
vi.mocked(extensionApi.env).isWindows = isWindows;

// spy on runCliCommand
const spawnSpy = vi.spyOn(childProcess, 'spawn');

const onEventMock = vi.fn();

onEventMock.mockImplementation((event: string, callback: (data: string) => void) => {
// delay execution
if (event === 'close') {
setTimeout(() => {
callback(0 as unknown as string);
}, 500);
}
});

spawnSpy.mockReturnValue({
on: onEventMock,
stdout: { setEncoding: vi.fn(), on: vi.fn() },
stderr: { setEncoding: vi.fn(), on: vi.fn() },
} as unknown as childProcess.ChildProcessWithoutNullStreams);

const fakeMinikubeInstaller = {
getAssetInfo: vi.fn(),
} as unknown as MinikubeInstaller;

const result = await detectMinikube('', fakeMinikubeInstaller);
expect(result).toEqual('minikube');

// expect not called getAssetInfo
expect(fakeMinikubeInstaller.getAssetInfo).not.toBeCalled();

expect(spawnSpy).toBeCalled();
// expect right parameters
if (isMac) {
expect(spawnSpy.mock.calls[0][0]).toEqual('minikube');
} else if (isWindows) {
expect(spawnSpy.mock.calls[0][0]).toEqual('"minikube"');
}
expect(spawnSpy.mock.calls[0][1]).toEqual(['version']);
});

test('runCliCommand/killProcess on macOS', async () => {
vi.mocked(extensionApi.env).isMac = true;
vi.mocked(extensionApi.env).isWindows = false;

// spy on runCliCommand
const spawnSpy = vi.spyOn(childProcess, 'spawn');

const killMock = vi.fn();

spawnSpy.mockReturnValue({
kill: killMock,
on: vi.fn(),
stdout: { setEncoding: vi.fn(), on: vi.fn() },
stderr: { setEncoding: vi.fn(), on: vi.fn() },
} as unknown as childProcess.ChildProcessWithoutNullStreams);

const fakeToken = {
onCancellationRequested: vi.fn(),
} as unknown as extensionApi.CancellationToken;

vi.mocked(fakeToken.onCancellationRequested).mockImplementation((callback: any): extensionApi.Disposable => {
// abort execution after 500ms
setTimeout(() => {
callback();
}, 500);

return extensionApi.Disposable.from({ dispose: vi.fn() });
});

await expect(runCliCommand('fooCommand', [], undefined, fakeToken)).rejects.toThrow('Execution cancelled');

expect(spawnSpy.mock.calls[0][0]).toEqual('fooCommand');

expect(killMock).toBeCalled();
});

test('runCliCommand/killProcess on Windows', async () => {
vi.mocked(extensionApi.env).isMac = false;
vi.mocked(extensionApi.env).isWindows = true;

// spy on runCliCommand
const spawnSpy = vi.spyOn(childProcess, 'spawn');

const killMock = vi.fn();

spawnSpy.mockReturnValue({
kill: killMock,
pid: 'pid123',
on: vi.fn(),
stdout: { setEncoding: vi.fn(), on: vi.fn() },
stderr: { setEncoding: vi.fn(), on: vi.fn() },
} as unknown as childProcess.ChildProcessWithoutNullStreams);

const fakeToken = {
onCancellationRequested: vi.fn(),
} as unknown as extensionApi.CancellationToken;

vi.mocked(fakeToken.onCancellationRequested).mockImplementation((callback: any): extensionApi.Disposable => {
// abort execution after 500ms
setTimeout(() => {
callback();
}, 500);

return extensionApi.Disposable.from({ dispose: vi.fn() });
});

await expect(runCliCommand('fooCommand', [], undefined, fakeToken)).rejects.toThrow('Execution cancelled');

expect(spawnSpy.mock.calls[0][0]).toEqual('"fooCommand"');
// on windows we don't use killProcess but run taskkill
expect(killMock).not.toBeCalled();

expect(spawnSpy.mock.calls[1]).toEqual(['taskkill', ['/pid', 'pid123', '/f', '/t']]);
});
32 changes: 10 additions & 22 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (C) 2023 Red Hat, Inc.
* Copyright (C) 2023-2024 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.
Expand All @@ -21,22 +21,9 @@ import * as path from 'node:path';
import type { ChildProcess } from 'node:child_process';
import { spawn } from 'node:child_process';
import * as sudo from 'sudo-prompt';
import type * as extensionApi from '@podman-desktop/api';
import * as extensionApi from '@podman-desktop/api';
import type { MinikubeInstaller } from './minikube-installer';

const windows = os.platform() === 'win32';
export function isWindows(): boolean {
return windows;
}
const mac = os.platform() === 'darwin';
export function isMac(): boolean {
return mac;
}
const linux = os.platform() === 'linux';
export function isLinux(): boolean {
return linux;
}

export interface SpawnResult {
stdOut: string;
stdErr: string;
Expand All @@ -52,7 +39,7 @@ const macosExtraPath = '/usr/local/bin:/opt/homebrew/bin:/opt/local/bin:/opt/pod

export function getMinikubePath(): string {
const env = process.env;
if (isMac()) {
if (extensionApi.env.isMac) {
if (!env.PATH) {
return macosExtraPath;
} else {
Expand All @@ -78,7 +65,9 @@ export async function detectMinikube(pathAddition: string, installer: MinikubeIn
await runCliCommand(assetInfo.name, ['version'], {
env: { PATH: getMinikubePath().concat(path.delimiter).concat(pathAddition) },
});
return pathAddition.concat(path.sep).concat(isWindows() ? assetInfo.name + '.exe' : assetInfo.name);
return pathAddition
.concat(path.sep)
.concat(extensionApi.env.isWindows ? assetInfo.name + '.exe' : assetInfo.name);
} catch (e) {
console.error(e);
}
Expand All @@ -99,9 +88,9 @@ export function runCliCommand(
let env = Object.assign({}, process.env); // clone original env object

// In production mode, applications don't have access to the 'user' path like brew
if (isMac() || isWindows()) {
if (extensionApi.env.isMac || extensionApi.env.isWindows) {
env.PATH = getMinikubePath();
if (isWindows()) {
if (extensionApi.env.isWindows) {
// Escape any whitespaces in command
command = `"${command}"`;
}
Expand All @@ -115,8 +104,7 @@ export function runCliCommand(
env = Object.assign(env, options.env);
}

const spawnProcess = spawn(command, args, { shell: isWindows(), env });

const spawnProcess = spawn(command, args, { shell: extensionApi.env.isWindows, env });
// if the token is cancelled, kill the process and reject the promise
token?.onCancellationRequested(() => {
killProcess(spawnProcess);
Expand Down Expand Up @@ -232,7 +220,7 @@ export async function installBinaryToSystem(binaryPath: string, binaryName: stri
}

function killProcess(spawnProcess: ChildProcess) {
if (isWindows()) {
if (extensionApi.env.isWindows) {
spawn('taskkill', ['/pid', spawnProcess.pid?.toString(), '/f', '/t']);
} else {
spawnProcess.kill();
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,10 @@
picocolors "^1.0.0"
tslib "^2.5.0"

"@podman-desktop/api@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@podman-desktop/api/-/api-1.0.1.tgz#4d3caae333726db1d919e44568b2966d6e33f85d"
integrity sha512-iI4/U+AhaGz4WgcPNT4PjzhL3aHsGLXsgBSnS+BV9hitjNbcVKicCO3kHmPkmjATn0tBDCDhPkUXkI7aCHi01A==
"@podman-desktop/api@^1.6.4":
version "1.6.4"
resolved "https://registry.yarnpkg.com/@podman-desktop/api/-/api-1.6.4.tgz#f6da8228523e787f408d366f1d99d12a7b9e6924"
integrity sha512-8sxPcFvepxVM0iANq9h+QbnxAPAEE03KhrDTUp8AEzMPHZhascBSi11xhaLaDUujXVaKHUysEt0hh+0ccL479w==

"@types/chai-subset@^1.3.3":
version "1.3.3"
Expand Down

0 comments on commit 7cea44b

Please sign in to comment.