Skip to content

Commit

Permalink
feat: Add Minikube extension
Browse files Browse the repository at this point in the history
  • Loading branch information
afbjorklund committed May 23, 2023
1 parent 0ab9ffd commit c44598b
Show file tree
Hide file tree
Showing 23 changed files with 1,586 additions and 2 deletions.
1 change: 1 addition & 0 deletions extensions/minikube/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Binary file added extensions/minikube/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added extensions/minikube/logo-dark.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added extensions/minikube/logo-light.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added extensions/minikube/minikube
Empty file.
73 changes: 73 additions & 0 deletions extensions/minikube/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"name": "minikube",
"displayName": "Minikube",
"description": "Integration for minikube: a tool that makes it easy to run Kubernetes locally",
"version": "0.0.1",
"icon": "icon.png",
"publisher": "podman-desktop",
"license": "Apache-2.0",
"engines": {
"podman-desktop": "^0.0.1"
},
"main": "./dist/extension.js",
"contributes": {
"configuration": {
"title": "Minikube",
"properties": {
"minikube.cluster.creation.name": {
"type": "string",
"default": "minikube",
"scope": "KubernetesProviderConnectionFactory",
"description": "Name"
},
"minikube.cluster.creation.driver": {
"type": "string",
"default": "podman",
"enum": [
"podman",
"docker"
],
"scope": "KubernetesProviderConnectionFactory",
"description": "Driver"
},
"minikube.cluster.creation.runtime": {
"type": "string",
"default": "cri-o",
"enum": [
"cri-o",
"containerd",
"docker"
],
"scope": "KubernetesProviderConnectionFactory",
"description": "Container Runtime"
}
}
},
"menus": {
"dashboard/image": [
{
"command": "minikube.image.move",
"title": "Push image to minikube cluster"
}
]
}
},
"scripts": {
"build": "npx ts-node ./scripts/download.ts && vite build && node ./scripts/build.js",
"test": "vitest run --coverage",
"test:watch": "vitest watch --coverage",
"watch": "vite build -w"
},
"dependencies": {
"@octokit/rest": "^19.0.11",
"@podman-desktop/api": "^0.0.1",
"mustache": "^4.2.0",
"sudo-prompt": "^9.2.1"
},
"devDependencies": {
"7zip-min": "^1.4.4",
"mkdirp": "^2.1.6",
"vite": "^4.3.8",
"zip-local": "^0.3.5"
}
}
42 changes: 42 additions & 0 deletions extensions/minikube/scripts/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env node
/**********************************************************************
* Copyright (C) 2022-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
***********************************************************************/

const zipper = require('zip-local');
const path = require('path');
const packageJson = require('../package.json');
const fs = require('fs');
const { mkdirp } = require('mkdirp');

const destFile = path.resolve(__dirname, `../${packageJson.name}.cdix`);
const builtinDirectory = path.resolve(__dirname, '../builtin');
const unzippedDirectory = path.resolve(builtinDirectory, `${packageJson.name}.cdix`);
// remove the .cdix file before zipping
if (fs.existsSync(destFile)) {
fs.rmSync(destFile);
}
// remove the builtin folder before zipping
if (fs.existsSync(builtinDirectory)) {
fs.rmSync(builtinDirectory, { recursive: true, force: true });
}

zipper.sync.zip(path.resolve(__dirname, '../')).compress().save(destFile);

mkdirp(unzippedDirectory).then(() => {
zipper.sync.unzip(destFile).save(unzippedDirectory);
});
39 changes: 39 additions & 0 deletions extensions/minikube/scripts/download.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env node
/**********************************************************************
* 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 fs from 'node:fs';
import * as path from 'node:path';
import { Octokit } from 'octokit';
import type { OctokitOptions } from '@octokit/core/dist-types/types';

const octokitOptions: OctokitOptions = {};
if (process.env.GITHUB_TOKEN) {
octokitOptions.auth = process.env.GITHUB_TOKEN;
}
const octokit = new Octokit(octokitOptions);

// to make this file a module
export {};

async function download(tagVersion: string, repoPath: string, fileName: string): Promise<void> {
const destDir = path.resolve(__dirname, '..', 'src-generated');
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir);
}
}
95 changes: 95 additions & 0 deletions extensions/minikube/src/create-cluster.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**********************************************************************
* 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 { beforeEach, expect, test, vi } from 'vitest';
import type { Mock } from 'vitest';
import { createCluster } from './create-cluster';
import { runCliCommand } from './util';
import type { TelemetryLogger } from '@podman-desktop/api';
import * as extensionApi from '@podman-desktop/api';

vi.mock('@podman-desktop/api', async () => {
return {
Logger: {},
kubernetes: {
createResources: vi.fn(),
},
};
});

vi.mock('./util', async () => {
return {
runCliCommand: vi.fn(),
getMinikubePath: vi.fn(),
};
});

beforeEach(() => {
vi.clearAllMocks();
});

const telemetryLogUsageMock = vi.fn();
const telemetryLogErrorMock = vi.fn();
const telemetryLoggerMock = {
logUsage: telemetryLogUsageMock,
logError: telemetryLogErrorMock,
} as unknown as TelemetryLogger;

test('expect error is cli returns non zero exit code', async () => {
try {
(runCliCommand as Mock).mockReturnValue({ exitCode: -1, error: 'error' });
await createCluster({}, undefined, '', telemetryLoggerMock, undefined);
} catch (err) {
expect(err).to.be.a('Error');
expect(err.message).equal('Failed to create minikube cluster. error');
expect(telemetryLogErrorMock).toBeCalledWith('createCluster', expect.objectContaining({ error: 'error' }));
}
});

test('expect cluster to be created', async () => {
(runCliCommand as Mock).mockReturnValue({ exitCode: 0 });
await createCluster({}, undefined, '', telemetryLoggerMock, undefined);
expect(telemetryLogUsageMock).toHaveBeenNthCalledWith(
1,
'createCluster',
expect.objectContaining({ driver: 'docker' }),
);
expect(telemetryLogErrorMock).not.toBeCalled();
expect(extensionApi.kubernetes.createResources).not.toBeCalled();
});

test('expect error if Kubernetes reports error', async () => {
try {
(runCliCommand as Mock).mockReturnValue({ exitCode: 0 });
const logger = {
log: vi.fn(),
error: vi.fn(),
warn: vi.fn(),
};
(extensionApi.kubernetes.createResources as Mock).mockRejectedValue(new Error('Kubernetes error'));
await createCluster({}, logger, '', telemetryLoggerMock, undefined);
} catch (err) {
expect(extensionApi.kubernetes.createResources).toBeCalled();
expect(err).to.be.a('Error');
expect(err.message).equal('Failed to create minikube cluster. Kubernetes error');
expect(telemetryLogErrorMock).toBeCalledWith(
'createCluster',
expect.objectContaining({ error: 'Kubernetes error' }),
);
}
});
69 changes: 69 additions & 0 deletions extensions/minikube/src/create-cluster.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**********************************************************************
* 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 { getMinikubePath, runCliCommand } from './util';

import type { Logger, TelemetryLogger, CancellationToken } from '@podman-desktop/api';

export async function createCluster(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params: { [key: string]: any },
logger: Logger,
minikubeCli: string,
telemetryLogger: TelemetryLogger,
token?: CancellationToken,
): Promise<void> {
let clusterName = 'minikube';
if (params['minikube.cluster.creation.name']) {
clusterName = params['minikube.cluster.creation.name'];
}

let driver = 'docker';
if (params['minikube.cluster.creation.driver']) {
driver = params['minikube.cluster.creation.driver'];
}

let runtime = 'docker';
if (params['minikube.cluster.creation.runtime']) {
runtime = params['minikube.cluster.creation.runtime'];
}

const env = Object.assign({}, process.env);

// update PATH to include minikube
env.PATH = getMinikubePath();

// now execute the command to create the cluster
try {
await runCliCommand(
minikubeCli,
['start', '--profile', clusterName, '--driver', driver, '--container-runtime', runtime],
{ env, logger },
token,
);
telemetryLogger.logUsage('createCluster', { driver, runtime });
} catch (error) {
const errorMessage = error instanceof Error ? error.message : error;
telemetryLogger.logError('createCluster', {
driver,
runtime,
error: errorMessage,
stdErr: errorMessage,
});
throw new Error(`Failed to create minikube cluster. ${errorMessage}`);
}
}
59 changes: 59 additions & 0 deletions extensions/minikube/src/extension.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**********************************************************************
* 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
***********************************************************************/

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

import { beforeEach, expect, test, vi } from 'vitest';
import * as podmanDesktopApi from '@podman-desktop/api';
import { refreshMinikubeClustersOnProviderConnectionUpdate } from './extension';

vi.mock('@podman-desktop/api', async () => {
return {
provider: {
onDidUpdateContainerConnection: vi.fn(),
},

containerEngine: {
listContainers: vi.fn(),
},
};
});

beforeEach(() => {
vi.clearAllMocks();
});

test('check we received notifications ', async () => {
const onDidUpdateContainerConnectionMock = vi.fn();
(podmanDesktopApi.provider as any).onDidUpdateContainerConnection = onDidUpdateContainerConnectionMock;

const listContainersMock = vi.fn();
(podmanDesktopApi.containerEngine as any).listContainers = listContainersMock;
listContainersMock.mockResolvedValue([]);

let callbackCalled = false;
onDidUpdateContainerConnectionMock.mockImplementation((callback: any) => {
callback();
callbackCalled = true;
});

const fakeProvider = {} as unknown as podmanDesktopApi.Provider;
refreshMinikubeClustersOnProviderConnectionUpdate(fakeProvider);
expect(callbackCalled).toBeTruthy();
expect(listContainersMock).toBeCalledTimes(1);
});

0 comments on commit c44598b

Please sign in to comment.