Skip to content

Commit 9b9a15c

Browse files
authored
fix: add strict rule to kind and docker extensions (podman-desktop#7425)
* fix: add strict rule to kind and docker extensions Signed-off-by: lstocchi <[email protected]> * fix: fix test Signed-off-by: lstocchi <[email protected]> * fix: typecheck Signed-off-by: lstocchi <[email protected]> --------- Signed-off-by: lstocchi <[email protected]>
1 parent e6ac0e2 commit 9b9a15c

14 files changed

+76
-54
lines changed

extensions/docker/src/docker-cli.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,5 @@ test('should return docker version', async () => {
5454
const installedDocker = await getDockerInstallation();
5555

5656
expect(installedDocker).toBeDefined();
57-
expect(installedDocker.version).toBe('25.0.2');
57+
expect(installedDocker?.version).toBe('25.0.2');
5858
});

extensions/docker/src/docker-cli.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import * as extensionApi from '@podman-desktop/api';
1919

2020
const macosExtraPath = '/usr/local/bin:/opt/homebrew/bin:/opt/local/bin';
2121

22-
export function getInstallationPath(): string {
22+
export function getInstallationPath(): string | undefined {
2323
const env = process.env;
2424
if (extensionApi.env.isMac) {
2525
if (!env.PATH) {

extensions/docker/src/extension.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ async function monitorDaemon(extensionContext: extensionApi.ExtensionContext): P
103103
if (err instanceof Error) {
104104
extensionApi.env.createTelemetryLogger().logError(err);
105105
} else {
106-
extensionApi.env.createTelemetryLogger().logError(err.toString());
106+
extensionApi.env.createTelemetryLogger().logError(String(err));
107107
}
108108
});
109109
}
@@ -173,7 +173,7 @@ export async function activate(extensionContext: extensionApi.ExtensionContext):
173173
if (err instanceof Error) {
174174
extensionApi.env.createTelemetryLogger().logError(err);
175175
} else {
176-
extensionApi.env.createTelemetryLogger().logError(err.toString());
176+
extensionApi.env.createTelemetryLogger().logError(String(err));
177177
}
178178
});
179179
}

extensions/docker/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"compilerOptions": {
3+
"strict": true,
34
"lib": ["ES2017", "webworker"],
45
"sourceMap": true,
56
"rootDir": "src",

extensions/kind/src/create-cluster.spec.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,18 @@ test('expect error is cli returns non zero exit code', async () => {
6363
const error = { exitCode: -1, message: 'error' } as extensionApi.RunError;
6464
try {
6565
(extensionApi.process.exec as Mock).mockRejectedValue(error);
66-
await createCluster({}, undefined, '', telemetryLoggerMock, undefined);
66+
await createCluster({}, '', telemetryLoggerMock, undefined, undefined);
6767
} catch (err) {
6868
expect(err).to.be.a('Error');
69-
expect(err.message).equal('Failed to create kind cluster. error');
69+
expect((err as Error).message).equal('Failed to create kind cluster. error');
7070
expect(telemetryLogUsageMock).toBeCalledWith('createCluster', expect.objectContaining({ error: error }));
7171
expect(telemetryLogErrorMock).not.toBeCalled();
7272
}
7373
});
7474

7575
test('expect cluster to be created', async () => {
7676
(extensionApi.process.exec as Mock).mockReturnValue({} as extensionApi.RunResult);
77-
await createCluster({}, undefined, '', telemetryLoggerMock, undefined);
77+
await createCluster({}, '', telemetryLoggerMock, undefined, undefined);
7878
expect(telemetryLogUsageMock).toHaveBeenNthCalledWith(
7979
1,
8080
'createCluster',
@@ -91,7 +91,7 @@ test('expect cluster to be created with ingress', async () => {
9191
error: vi.fn(),
9292
warn: vi.fn(),
9393
};
94-
await createCluster({ 'kind.cluster.creation.ingress': 'on' }, logger, '', telemetryLoggerMock, undefined);
94+
await createCluster({ 'kind.cluster.creation.ingress': 'on' }, '', telemetryLoggerMock, logger, undefined);
9595
expect(telemetryLogUsageMock).toHaveBeenNthCalledWith(
9696
1,
9797
'createCluster',
@@ -110,11 +110,11 @@ test('expect error if Kubernetes reports error', async () => {
110110
warn: vi.fn(),
111111
};
112112
(extensionApi.kubernetes.createResources as Mock).mockRejectedValue(error);
113-
await createCluster({ 'kind.cluster.creation.ingress': 'on' }, logger, '', telemetryLoggerMock, undefined);
113+
await createCluster({ 'kind.cluster.creation.ingress': 'on' }, '', telemetryLoggerMock, logger, undefined);
114114
} catch (err) {
115115
expect(extensionApi.kubernetes.createResources).toBeCalled();
116116
expect(err).to.be.a('Error');
117-
expect(err.message).equal('Failed to create kind cluster. Kubernetes error');
117+
expect((err as Error).message).equal('Failed to create kind cluster. Kubernetes error');
118118
expect(telemetryLogErrorMock).not.toBeCalled();
119119
expect(telemetryLogUsageMock).toBeCalledWith('createCluster', expect.objectContaining(error));
120120
}
@@ -129,12 +129,12 @@ test('check cluster configuration generation', async () => {
129129
});
130130

131131
test('check cluster configuration empty string image', async () => {
132-
const conf = getKindClusterConfig(undefined, undefined, undefined, '');
132+
const conf = getKindClusterConfig('cluster', 80, 80, '');
133133
expect(conf).to.not.contains('image:');
134134
});
135135

136136
test('check cluster configuration null string image', async () => {
137-
const conf = getKindClusterConfig(undefined, undefined, undefined, undefined);
137+
const conf = getKindClusterConfig('cluster', 80, 80, undefined);
138138
expect(conf).to.not.contains('image:');
139139
});
140140

extensions/kind/src/create-cluster.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ export async function connectionAuditor(provider: string, items: AuditRequestIte
102102
export async function createCluster(
103103
// eslint-disable-next-line @typescript-eslint/no-explicit-any
104104
params: { [key: string]: any },
105-
logger: extensionApi.Logger,
106105
kindCli: string,
107106
telemetryLogger: extensionApi.TelemetryLogger,
107+
logger?: extensionApi.Logger,
108108
token?: CancellationToken,
109109
): Promise<void> {
110110
let clusterName = 'kind';
@@ -118,7 +118,7 @@ export async function createCluster(
118118
provider = params['kind.cluster.creation.provider'];
119119
}
120120

121-
const env = Object.assign({}, process.env);
121+
const env = Object.assign({}, process.env) as { [key: string]: string };
122122
// add KIND_EXPERIMENTAL_PROVIDER env variable if needed
123123
if (provider === 'podman') {
124124
env['KIND_EXPERIMENTAL_PROVIDER'] = 'podman';
@@ -158,7 +158,7 @@ export async function createCluster(
158158
await fs.promises.writeFile(tmpFilePath, kindClusterConfig, 'utf8');
159159

160160
// update PATH to include kind
161-
env.PATH = getKindPath();
161+
env.PATH = getKindPath() ?? '';
162162

163163
// eslint-disable-next-line @typescript-eslint/no-explicit-any
164164
const telemetryOptions: Record<string, any> = {
@@ -173,15 +173,15 @@ export async function createCluster(
173173
try {
174174
await extensionApi.process.exec(kindCli, ['create', 'cluster', '--config', tmpFilePath], { env, logger, token });
175175
if (ingressController) {
176-
logger.log('Creating ingress controller resources');
176+
logger?.log('Creating ingress controller resources');
177177
await setupIngressController(clusterName);
178178
}
179179
} catch (error) {
180180
telemetryOptions.error = error;
181-
let errorMessage: string;
181+
let errorMessage: string = '';
182182

183-
if (typeof error === 'object' && 'message' in error) {
184-
errorMessage = error.message.toString();
183+
if (error && typeof error === 'object' && 'message' in error) {
184+
errorMessage = String(error.message);
185185
} else if (typeof error === 'string') {
186186
errorMessage = error;
187187
}

extensions/kind/src/extension.spec.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import type * as extensionApi from '@podman-desktop/api';
2222
import * as podmanDesktopApi from '@podman-desktop/api';
2323
import { beforeEach, expect, test, vi } from 'vitest';
2424

25-
import { createProvider, moveImage, refreshKindClustersOnProviderConnectionUpdate } from './extension';
25+
import { activate, moveImage, refreshKindClustersOnProviderConnectionUpdate } from './extension';
26+
import * as util from './util';
2627

2728
vi.mock('./image-handler', async () => {
2829
return {
@@ -59,6 +60,11 @@ vi.mock('@podman-desktop/api', async () => {
5960
context: {
6061
setValue: vi.fn(),
6162
},
63+
env: {
64+
createTelemetryLogger: vi.fn().mockReturnValue({
65+
logUsage: vi.fn(),
66+
} as unknown as extensionApi.TelemetryLogger),
67+
},
6268
};
6369
});
6470

@@ -113,15 +119,14 @@ test('Ensuring a progress task is created when calling kind.image.move command',
113119
const contextSetValueMock = vi.fn();
114120
(podmanDesktopApi.context as any).setValue = contextSetValueMock;
115121

116-
await createProvider(
122+
vi.spyOn(util, 'detectKind').mockResolvedValue('kind');
123+
124+
await activate(
117125
vi.mocked<extensionApi.ExtensionContext>({
118126
subscriptions: {
119127
push: vi.fn(),
120128
},
121129
} as unknown as extensionApi.ExtensionContext),
122-
vi.mocked<extensionApi.TelemetryLogger>({
123-
logUsage: vi.fn(),
124-
} as unknown as extensionApi.TelemetryLogger),
125130
);
126131

127132
// ensure the command has been registered

extensions/kind/src/extension.ts

+12-6
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,12 @@ async function registerProvider(
5858
const disposable = provider.setKubernetesProviderConnectionFactory(
5959
{
6060
// eslint-disable-next-line @typescript-eslint/no-explicit-any
61-
create: (params: { [key: string]: any }, logger?: Logger, token?: CancellationToken) =>
62-
createCluster(params, logger, kindCli, telemetryLogger, token),
61+
create: (params: { [key: string]: any }, logger?: Logger, token?: CancellationToken) => {
62+
if (kindCli) {
63+
return createCluster(params, kindCli, telemetryLogger, logger, token);
64+
}
65+
return Promise.reject(new Error('Unable to create kind cluster. No kind cli detected'));
66+
},
6367
creationDisplayName: 'Kind cluster',
6468
},
6569
{
@@ -160,12 +164,14 @@ async function updateClusters(
160164
await extensionApi.containerEngine.stopContainer(cluster.engineId, cluster.id);
161165
},
162166
delete: async (logger): Promise<void> => {
163-
const env = Object.assign({}, process.env);
167+
const env = Object.assign({}, process.env) as { [key: string]: string };
164168
if (cluster.engineType === 'podman') {
165169
env['KIND_EXPERIMENTAL_PROVIDER'] = 'podman';
166170
}
167-
env.PATH = getKindPath();
168-
await extensionApi.process.exec(kindCli, ['delete', 'cluster', '--name', cluster.name], { env, logger });
171+
env.PATH = getKindPath() ?? '';
172+
if (kindCli) {
173+
await extensionApi.process.exec(kindCli, ['delete', 'cluster', '--name', cluster.name], { env, logger });
174+
}
169175
},
170176
};
171177
// create a new connection
@@ -296,7 +302,7 @@ export async function moveImage(
296302
image: unknown,
297303
): Promise<void> {
298304
// as the command receive an "any" value we check that it contains an id and an engineId as they are mandatory
299-
if (!(typeof image === 'object' && 'id' in image && 'engineId' in image)) {
305+
if (!(kindCli && image && typeof image === 'object' && 'id' in image && 'engineId' in image)) {
300306
throw new Error('Image selection not supported yet');
301307
}
302308

extensions/kind/src/image-handler.spec.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,19 @@ beforeEach(() => {
5454

5555
test('expect error to be raised if no image is given', async () => {
5656
try {
57-
await imageHandler.moveImage({ engineId: 'dummy' }, [], undefined);
57+
await imageHandler.moveImage({ engineId: 'dummy' }, [], 'kind');
5858
} catch (err) {
5959
expect(err).to.be.a('Error');
60-
expect(err.message).equal('Image selection not supported yet');
60+
expect((err as Error).message).equal('Image selection not supported yet');
6161
}
6262
});
6363

6464
test('expect error to be raised if no clusters are given', async () => {
6565
try {
66-
await imageHandler.moveImage({ engineId: 'dummy', name: 'myimage' }, [], undefined);
66+
await imageHandler.moveImage({ engineId: 'dummy', name: 'myimage' }, [], 'kind');
6767
} catch (err) {
6868
expect(err).to.be.a('Error');
69-
expect(err.message).equal('No Kind clusters to push to');
69+
expect((err as Error).message).equal('No Kind clusters to push to');
7070
}
7171
});
7272

@@ -78,7 +78,7 @@ test('expect image name to be given', async () => {
7878
await imageHandler.moveImage(
7979
{ engineId: 'dummy', name: 'myimage' },
8080
[{ name: 'c1', engineType: 'podman', status: 'started', apiPort: 9443 }],
81-
undefined,
81+
'kind',
8282
);
8383
expect(extensionApi.containerEngine.saveImage).toBeCalledWith('dummy', 'myimage', expect.anything());
8484
});
@@ -91,7 +91,7 @@ test('expect getting showInformationMessage when image is pushed', async () => {
9191
await imageHandler.moveImage(
9292
{ engineId: 'dummy', name: 'myimage' },
9393
[{ name: 'c1', engineType: 'podman', status: 'started', apiPort: 9443 }],
94-
undefined,
94+
'kind',
9595
);
9696
expect(extensionApi.window.showInformationMessage).toBeCalledWith('Image myimage pushed to Kind cluster: c1');
9797
});
@@ -104,7 +104,7 @@ test('expect image name and tag to be given', async () => {
104104
await imageHandler.moveImage(
105105
{ engineId: 'dummy', name: 'myimage', tag: '1.0' },
106106
[{ name: 'c1', engineType: 'podman', status: 'started', apiPort: 9443 }],
107-
undefined,
107+
'kind',
108108
);
109109
expect(extensionApi.containerEngine.saveImage).toBeCalledWith('dummy', 'myimage:1.0', expect.anything());
110110
});
@@ -119,7 +119,7 @@ test('expect cli is called with right PATH', async () => {
119119
await imageHandler.moveImage(
120120
{ engineId: 'dummy', name: 'myimage' },
121121
[{ name: 'c1', engineType: 'podman', status: 'started', apiPort: 9443 }],
122-
undefined,
122+
'kind',
123123
);
124124
expect(getKindPath).toBeCalled();
125125

extensions/kind/src/image-handler.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export class ImageHandler {
3636

3737
// Retrieve all the Kind clusters available.
3838
const clusters = kindClusters.filter(cluster => cluster.status === 'started');
39-
let selectedCluster: { label: string; engineType: string };
39+
let selectedCluster: { label: string; engineType: string } | undefined;
4040

4141
// Throw an error if there is no clusters,
4242
// but if there are multiple ones, prompt the user to select one
@@ -56,8 +56,8 @@ export class ImageHandler {
5656
// Only proceed if a cluster was selected
5757
if (selectedCluster) {
5858
let name = image.name;
59-
let filename: string;
60-
const env = Object.assign({}, process.env);
59+
let filename: string | undefined;
60+
const env = Object.assign({}, process.env) as { [key: string]: string };
6161

6262
// Create a name:tag string for the image
6363
if (image.tag) {
@@ -71,7 +71,7 @@ export class ImageHandler {
7171
env['KIND_EXPERIMENTAL_PROVIDER'] = 'docker';
7272
}
7373

74-
env.PATH = getKindPath();
74+
env.PATH = getKindPath() ?? '';
7575
try {
7676
// Create a temporary file to store the image
7777
filename = await tmpName();

extensions/kind/src/kind-installer.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ vi.mock('sudo-prompt', () => {
5555
| { name?: string; icns?: string; env?: { [key: string]: string } },
5656
callback?: (error?: Error, stdout?: string | Buffer, stderr?: string | Buffer) => void,
5757
) => {
58-
callback(undefined);
58+
callback?.(undefined);
5959
},
6060
),
6161
};
@@ -124,7 +124,7 @@ test('expect showNotification to be called', async () => {
124124
report: (): void => {},
125125
};
126126
vi.spyOn(extensionApi.window, 'withProgress').mockImplementation((options, task) => {
127-
return task(progress, undefined);
127+
return task(progress, {} as extensionApi.CancellationToken);
128128
});
129129
vi.spyOn(installer, 'getAssetInfo').mockReturnValue(Promise.resolve({ id: 0, name: 'kind' }));
130130
// eslint-disable-next-line @typescript-eslint/no-empty-function

extensions/kind/src/kind-installer.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const MACOS_ARM64_ASSET_NAME = 'kind-darwin-arm64';
5959
export class KindInstaller {
6060
private assetNames = new Map<string, string>();
6161

62-
private assetPromise: Promise<AssetInfo>;
62+
private assetPromise: Promise<AssetInfo | undefined> | undefined;
6363

6464
constructor(
6565
private readonly storagePath: string,
@@ -72,7 +72,7 @@ export class KindInstaller {
7272
this.assetNames.set(MACOS_ARM64_PLATFORM, MACOS_ARM64_ASSET_NAME);
7373
}
7474

75-
findAssetInfo(data: GitHubRelease[], assetName: string): AssetInfo {
75+
findAssetInfo(data: GitHubRelease[], assetName: string): AssetInfo | undefined {
7676
for (const release of data) {
7777
for (const asset of release.assets) {
7878
if (asset.name === assetName) {
@@ -86,9 +86,12 @@ export class KindInstaller {
8686
return undefined;
8787
}
8888

89-
async getAssetInfo(): Promise<AssetInfo> {
89+
async getAssetInfo(): Promise<AssetInfo | undefined> {
9090
if (!(await this.assetPromise)) {
9191
const assetName = this.assetNames.get(os.platform().concat('-').concat(os.arch()));
92+
if (assetName === undefined) {
93+
return undefined;
94+
}
9295
const octokit = new Octokit();
9396
this.assetPromise = octokit.repos
9497
.listReleases({ owner: githubOrganization, repo: githubRepo })
@@ -169,6 +172,7 @@ export class KindInstaller {
169172
} finally {
170173
progress.report({ increment: -1 });
171174
}
175+
return false;
172176
},
173177
);
174178
} else {

0 commit comments

Comments
 (0)