chore: use podman v5 installer by default

no more v4/v5 installation path, only v5

Also updates the airgapped archive to include the v5 one and not the v4

fixes https://github.com/containers/podman-desktop/issues/6360
Signed-off-by: Florent Benoit <fbenoit@redhat.com>
This commit is contained in:
Florent Benoit 2024-04-08 10:44:00 +02:00 committed by Florent BENOIT
parent 3c015df03e
commit 0b77cf2d6b
10 changed files with 80 additions and 464 deletions

View file

@ -101,10 +101,10 @@ const config = {
context.packager.config.extraResources.push('extensions/podman/assets/podman-*.exe');
}
if (context.arch === Arch.x64 && context.electronPlatformName === 'win32') {
context.packager.config.extraResources.push('extensions/podman/assets/podman-image-x64.tar.xz');
context.packager.config.extraResources.push('extensions/podman/assets/podman-image-x64.tar.zst');
}
if (context.arch === Arch.arm64 && context.electronPlatformName === 'win32') {
context.packager.config.extraResources.push('extensions/podman/assets/podman-image-arm64.tar.xz');
context.packager.config.extraResources.push('extensions/podman/assets/podman-image-arm64.tar.zst');
}
},
afterPack: async context => {

View file

@ -51,11 +51,6 @@
"default": "",
"description": "Custom path to Podman binary (Default is blank)"
},
"podman.experimental.install.v5": {
"type": "boolean",
"default": false,
"description": "Enable podman v5 installation support (experimental)"
},
"podman.machine.cpus": {
"type": "number",
"format": "cpu",

View file

@ -19,11 +19,7 @@
import { PodmanDownload } from './podman-download';
import * as podman4JSON from '../src/podman4.json';
import * as podman5JSON from '../src/podman5.json';
const podman4Download = new PodmanDownload(podman4JSON, true);
await podman4Download.downloadBinaries();
const podman5Download = new PodmanDownload(podman5JSON, true);
await podman5Download.downloadBinaries();

View file

@ -20,40 +20,36 @@ import { afterEach } from 'node:test';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import {
DownloadAndCheck,
Podman5DownloadFedoraImage,
Podman5DownloadMachineOS,
PodmanDownload,
PodmanDownloadFcosImage,
PodmanDownloadFedoraImage,
ShaCheck,
} from './podman-download';
import * as podman4JSON from '../src/podman4.json';
import * as podman5JSON from '../src/podman5.json';
import nock from 'nock';
import { WriteStream, appendFileSync, createWriteStream, existsSync, mkdirSync } from 'node:fs';
import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
import { Octokit } from 'octokit';
import { PassThrough, Readable, Writable } from 'node:stream';
import { WritableStream, WritableStreamDefaultWriter } from 'stream/web';
import path from 'node:path';
import { tmpdir } from 'node:os';
import exp from 'node:constants';
import { Readable, Writable } from 'node:stream';
import { WritableStream } from 'stream/web';
const mockedPodman4 = {
version: '4.5.0',
const mockedPodman5 = {
version: '5.0.0',
platform: {
win32: {
version: 'v4.5.0',
fileName: 'podman-4.5.0-setup.exe',
version: 'v5.0.0',
fileName: 'podman-5.0.0-setup.exe',
},
darwin: {
version: 'v4.5.0',
version: 'v5.0.0',
arch: {
x64: {
fileName: 'podman-installer-macos-amd64-v4.5.0.pkg',
fileName: 'podman-installer-macos-amd64-v5.0.0.pkg',
},
arm64: {
fileName: 'podman-installer-macos-aarch64-v4.5.0.pkg',
fileName: 'podman-installer-macos-aarch64-v5.0.0.pkg',
},
universal: {
fileName: 'podman-installer-macos-universal-v4.5.0.pkg',
fileName: 'podman-installer-macos-universal-v5.0.0.pkg',
},
},
},
@ -61,12 +57,12 @@ const mockedPodman4 = {
};
class TestPodmanDownload extends PodmanDownload {
public getPodmanDownloadFcosImage(): PodmanDownloadFcosImage {
return super.getPodmanDownloadFcosImage();
public getPodman5DownloadFedoraImage(): Podman5DownloadFedoraImage {
return super.getPodman5DownloadFedoraImage();
}
public getPodmanDownloadFedoraImage(): PodmanDownloadFedoraImage {
return super.getPodmanDownloadFedoraImage();
public getPodman5DownloadMachineOS(): Podman5DownloadMachineOS {
return super.getPodman5DownloadMachineOS();
}
public getShaCheck(): ShaCheck {
@ -100,17 +96,17 @@ describe('macOS platform', () => {
});
test('PodmanDownload with real json', async () => {
const podmanDownload = new TestPodmanDownload(podman4JSON, true);
const podmanDownload = new TestPodmanDownload(podman5JSON, true);
// mock downloadAndCheckSha
const downloadCheck = podmanDownload.getDownloadAndCheck();
const downloadAndCheckShaSpy = vi.spyOn(downloadCheck, 'downloadAndCheckSha');
downloadAndCheckShaSpy.mockResolvedValue();
// mock podmanDownloadFcosImage
const podmanDownloadFcosImage = podmanDownload.getPodmanDownloadFcosImage();
const podmanDownloadFcosImageSpy = vi.spyOn(podmanDownloadFcosImage, 'download');
podmanDownloadFcosImageSpy.mockResolvedValue();
// mock podman5DownloadMachineOS
const podman5DownloadMachineOS = podmanDownload.getPodman5DownloadMachineOS();
const podman5DownloadMachineOSSpy = vi.spyOn(podman5DownloadMachineOS, 'download');
podman5DownloadMachineOSSpy.mockResolvedValue();
// add env file
process.env.AIRGAP_DOWNLOAD = 'yes';
@ -118,28 +114,26 @@ describe('macOS platform', () => {
await podmanDownload.downloadBinaries();
// check called 2 times
expect(downloadAndCheckShaSpy).toHaveBeenCalledTimes(2);
expect(downloadAndCheckShaSpy).toHaveBeenCalledTimes(3);
// check called with the correct parameters
expect(downloadAndCheckShaSpy).toHaveBeenCalledWith(
expect.stringContaining('v4.'),
expect.stringContaining('v5.'),
expect.stringContaining('podman-installer-macos-amd64'),
'podman-installer-macos-amd64.pkg',
);
expect(downloadAndCheckShaSpy).toHaveBeenCalledWith(
expect.stringContaining('v4.'),
expect.stringContaining('v5.'),
expect.stringContaining('podman-installer-macos-aarch64'),
'podman-installer-macos-arm64.pkg',
);
// check airgap download
expect(podmanDownloadFcosImageSpy).toHaveBeenCalled();
expect(podmanDownloadFcosImageSpy).toHaveBeenCalledWith('arm64');
expect(podmanDownloadFcosImageSpy).toHaveBeenCalledWith('x64');
expect(podman5DownloadMachineOSSpy).toHaveBeenCalled();
});
test('PodmanDownload with mocked json', async () => {
const podmanDownload = new TestPodmanDownload(mockedPodman4, false);
const podmanDownload = new TestPodmanDownload(mockedPodman5, false);
// mock downloadAndCheckSha
const downloadCheck = podmanDownload.getDownloadAndCheck();
@ -154,20 +148,20 @@ describe('macOS platform', () => {
// check called with the correct parameters
expect(downloadAndCheckShaSpy).toHaveBeenNthCalledWith(
1,
'v4.5.0',
'podman-installer-macos-amd64-v4.5.0.pkg',
'v5.0.0',
'podman-installer-macos-amd64-v5.0.0.pkg',
'podman-installer-macos-amd64.pkg',
);
expect(downloadAndCheckShaSpy).toHaveBeenNthCalledWith(
2,
'v4.5.0',
'podman-installer-macos-aarch64-v4.5.0.pkg',
'v5.0.0',
'podman-installer-macos-aarch64-v5.0.0.pkg',
'podman-installer-macos-arm64.pkg',
);
expect(downloadAndCheckShaSpy).toHaveBeenNthCalledWith(
3,
'v4.5.0',
'podman-installer-macos-universal-v4.5.0.pkg',
'v5.0.0',
'podman-installer-macos-universal-v5.0.0.pkg',
'podman-installer-macos-universal.pkg',
);
});
@ -192,7 +186,7 @@ describe('windows platform', () => {
});
test('PodmanDownload with real data', async () => {
const podmanDownload = new TestPodmanDownload(podman4JSON, true);
const podmanDownload = new TestPodmanDownload(podman5JSON, true);
// mock downloadAndCheckSha
const downloadCheck = podmanDownload.getDownloadAndCheck();
@ -203,9 +197,9 @@ describe('windows platform', () => {
process.env.AIRGAP_DOWNLOAD = 'yes';
// mock podmanDownloadFedoraImage
const podmanDownloadFedoraImage = podmanDownload.getPodmanDownloadFedoraImage();
const podmanDownloadFedoraImageSpy = vi.spyOn(podmanDownloadFedoraImage, 'download');
podmanDownloadFedoraImageSpy.mockResolvedValue();
const podman5DownloadFedoraImage = podmanDownload.getPodman5DownloadFedoraImage();
const podman5DownloadFedoraImageSpy = vi.spyOn(podman5DownloadFedoraImage, 'download');
podman5DownloadFedoraImageSpy.mockResolvedValue();
await podmanDownload.downloadBinaries();
@ -214,27 +208,27 @@ describe('windows platform', () => {
// check called with the correct parameters
expect(downloadAndCheckShaSpy).toHaveBeenCalledWith(
expect.stringContaining('v4.'),
expect.stringContaining('v5.'),
expect.stringContaining('-setup.exe'),
expect.stringContaining('-setup.exe'),
);
// check airgap download
expect(podmanDownloadFedoraImageSpy).toHaveBeenCalled();
expect(podmanDownloadFedoraImageSpy).toHaveBeenCalledWith('podman-wsl-fedora', 'x64');
expect(podmanDownloadFedoraImageSpy).toHaveBeenCalledWith('podman-wsl-fedora-arm', 'arm64');
expect(podman5DownloadFedoraImageSpy).toHaveBeenCalled();
expect(podman5DownloadFedoraImageSpy).toHaveBeenCalledWith('x64');
expect(podman5DownloadFedoraImageSpy).toHaveBeenCalledWith('arm64');
});
test('PodmanDownload with mocked json', async () => {
const podmanDownload = new TestPodmanDownload(mockedPodman4, false);
const podmanDownload = new TestPodmanDownload(mockedPodman5, false);
// mock downloadAndCheckSha
const downloadCheck = podmanDownload.getDownloadAndCheck();
const downloadAndCheckShaSpy = vi.spyOn(downloadCheck, 'downloadAndCheckSha');
downloadAndCheckShaSpy.mockResolvedValue();
const podmanDownloadFedoraImage = podmanDownload.getPodmanDownloadFedoraImage();
const podmanDownloadFedoraImageSpy = vi.spyOn(podmanDownloadFedoraImage, 'download');
const podman5DownloadFedoraImage = podmanDownload.getPodman5DownloadFedoraImage();
const podmanDownloadFedoraImageSpy = vi.spyOn(podman5DownloadFedoraImage, 'download');
await podmanDownload.downloadBinaries();
@ -244,9 +238,9 @@ describe('windows platform', () => {
// check called with the correct parameters
expect(downloadAndCheckShaSpy).toHaveBeenNthCalledWith(
1,
'v4.5.0',
'podman-4.5.0-setup.exe',
'podman-4.5.0-setup.exe',
'v5.0.0',
'podman-5.0.0-setup.exe',
'podman-5.0.0-setup.exe',
);
// check no airgap download

View file

@ -30,15 +30,11 @@ export class PodmanDownload {
#podmanVersion: string;
#downloadAndCheck: DownloadAndCheck;
#podmanDownloadFcosImage: PodmanDownloadFcosImage;
#podmanDownloadFedoraImage: PodmanDownloadFedoraImage;
#podman5DownloadFedoraImage: Podman5DownloadFedoraImage | undefined;
#podman5DownloadMachineOS: Podman5DownloadMachineOS | undefined;
#podman5DownloadFedoraImage: Podman5DownloadFedoraImage;
#podman5DownloadMachineOS: Podman5DownloadMachineOS;
#shaCheck: ShaCheck;
#httpsDownloader: HttpsDownloader;
#octokit: Octokit;
#platform: string;
#assetsFolder: string;
@ -108,46 +104,34 @@ export class PodmanDownload {
}
}
this.#shaCheck = new ShaCheck();
this.#httpsDownloader = new HttpsDownloader();
this.#podmanDownloadFcosImage = new PodmanDownloadFcosImage(
this.#httpsDownloader,
this.#shaCheck,
this.#assetsFolder,
);
this.#podmanDownloadFedoraImage = new PodmanDownloadFedoraImage(
this.#octokit,
this.#httpsDownloader,
this.#assetsFolder,
);
this.#downloadAndCheck = new DownloadAndCheck(this.#octokit, this.#shaCheck, this.#assetsFolder);
if (!fs.existsSync(this.#assetsFolder)) {
fs.mkdirSync(this.#assetsFolder);
}
if (podmanJSON.version.startsWith('5.')) {
// grab only first 2 digits from the version
const majorMinorVersion = podmanJSON.version.split('.').slice(0, 2).join('.');
// grab only first 2 digits from the version
const majorMinorVersion = podmanJSON.version.split('.').slice(0, 2).join('.');
this.#podman5DownloadFedoraImage = new Podman5DownloadFedoraImage(
majorMinorVersion,
this.#octokit,
this.#downloadAndCheck,
);
this.#podman5DownloadFedoraImage = new Podman5DownloadFedoraImage(
majorMinorVersion,
this.#octokit,
this.#downloadAndCheck,
);
this.#podman5DownloadMachineOS = new Podman5DownloadMachineOS(
majorMinorVersion,
this.#shaCheck,
this.#assetsFolder,
);
}
this.#podman5DownloadMachineOS = new Podman5DownloadMachineOS(
majorMinorVersion,
this.#shaCheck,
this.#assetsFolder,
);
}
protected getPodmanDownloadFcosImage(): PodmanDownloadFcosImage {
return this.#podmanDownloadFcosImage;
protected getPodman5DownloadFedoraImage(): Podman5DownloadFedoraImage {
return this.#podman5DownloadFedoraImage;
}
protected getPodmanDownloadFedoraImage(): PodmanDownloadFedoraImage {
return this.#podmanDownloadFedoraImage;
protected getPodman5DownloadMachineOS(): Podman5DownloadMachineOS {
return this.#podman5DownloadMachineOS;
}
protected getShaCheck(): ShaCheck {
@ -178,14 +162,7 @@ export class PodmanDownload {
await this.#podman5DownloadFedoraImage?.download('x64');
await this.#podman5DownloadFedoraImage?.download('arm64');
await this.#podmanDownloadFedoraImage.download('podman-wsl-fedora', 'x64');
await this.#podmanDownloadFedoraImage.download('podman-wsl-fedora-arm', 'arm64');
} else if (this.#platform === 'darwin') {
// download the fedora core os images for both arches
await this.#podmanDownloadFcosImage.download('x64');
await this.#podmanDownloadFcosImage.download('arm64');
// download the podman 5 machines OS
await this.#podman5DownloadMachineOS?.download();
}
@ -199,180 +176,6 @@ export class ShaCheck {
}
}
export class HttpsDownloader {
// grab the JSON from the given URL
async downloadJson(url): Promise<unknown> {
return new Promise((resolve, reject) => {
https.get(url, res => {
let body = '';
res.on('data', chunk => {
body += chunk;
});
res.on('end', () => {
try {
const json = JSON.parse(body);
resolve(json);
} catch (error) {
reject(error.message);
}
});
res.on('error', error => {
reject(error.message);
});
});
});
}
// download the file from the given URL and store the content in destFile
async downloadFile(url, destFile): Promise<void> {
return new Promise((resolve, reject) => {
const request = https.get(url, async res => {
// handle url redirect
if (res.statusCode === 302) {
await this.downloadFile(res.headers.location, destFile);
resolve();
}
res.on('data', data => {
fs.appendFileSync(destFile, data);
});
res.on('end', () => {
try {
resolve();
} catch (error) {
reject(error.message);
}
});
res.on('error', error => {
reject(error.message);
});
});
request.end();
});
}
}
export class PodmanDownloadFcosImage {
#downloadAttempt = 0;
#httpsDownloader: HttpsDownloader;
#shaCheck: ShaCheck;
#assetsFolder: string;
readonly MAX_DOWNLOAD_ATTEMPT = 3;
constructor(
readonly httpsDownloader: HttpsDownloader,
readonly shaCheck: ShaCheck,
readonly assetsFolder: string,
) {
this.#httpsDownloader = httpsDownloader;
this.#shaCheck = shaCheck;
this.#assetsFolder = assetsFolder;
}
// For macOS, grab the qemu image from Fedora CoreOS
async download(arch: string): Promise<void> {
if (this.#downloadAttempt >= this.MAX_DOWNLOAD_ATTEMPT) {
console.error('Max download attempt reached, exiting...');
process.exit(1);
}
// download the JSON of testing Fedora CoreOS images at https://builds.coreos.fedoraproject.org/streams/testing.json
const data = await this.#httpsDownloader.downloadJson(
'https://builds.coreos.fedoraproject.org/streams/testing.json',
);
// get the file to download
const qemuArch = arch === 'x64' ? 'x86_64' : 'aarch64';
const qemuData = data?.['architectures'][qemuArch]['artifacts']['qemu'];
// get disk object
const disk = qemuData.formats['qcow2.xz'].disk;
// get the disk location
const diskLocation = disk.location;
// get the sha2556
const sha256 = disk.sha256;
const filename = `podman-image-${arch}.qcow2.xz`;
const destFile = path.resolve(this.#assetsFolder, filename);
if (!fs.existsSync(destFile)) {
// download the file from diskLocation
console.log(`⚡️ Downloading Podman package from ${diskLocation}`);
await this.httpsDownloader.downloadFile(diskLocation, destFile);
console.log(`📔 Downloaded to ${destFile}`);
} else {
console.log(`⏭️ Skipping podman image (already downloaded to ${filename})`);
}
if (!(await this.#shaCheck.checkFile(destFile, sha256))) {
console.warn(`❌ Invalid checksum for downloaded ${destFile} is not matching, downloading again...`);
fs.rmSync(destFile);
this.#downloadAttempt++;
// call the loop again
await this.download(arch);
} else {
console.log(`✅ Valid checksum for ${filename}`);
}
}
}
export class PodmanDownloadFedoraImage {
readonly MAX_DOWNLOAD_ATTEMPT = 3;
#downloadAttempt = 0;
#octokit: Octokit;
#assetsFolder: string;
#httpsDownloader: HttpsDownloader;
constructor(
readonly octokit: Octokit,
readonly httpsDownloader: HttpsDownloader,
readonly assetsFolder: string,
) {
this.#octokit = octokit;
this.#httpsDownloader = httpsDownloader;
this.#assetsFolder = assetsFolder;
}
// For Windows binaries, grab the latest release from GitHub repository
async download(repo: string, arch: string): Promise<void> {
if (this.#downloadAttempt >= this.MAX_DOWNLOAD_ATTEMPT) {
console.error('Max download attempt reached, exiting...');
process.exit(1);
}
// now, grab the files
const release = await this.#octokit.request('GET /repos/{owner}/{repo}/releases/latest', {
owner: 'containers',
repo,
});
const artifactRelease = release.data.assets.find(asset => asset.name === 'rootfs.tar.xz');
if (!artifactRelease) {
throw new Error(`Can't find assets to download and verify for podman image from repository ${repo}`);
}
const filename = `podman-image-${arch}.tar.xz`;
const destFile = path.resolve(this.#assetsFolder, filename);
if (!fs.existsSync(destFile)) {
// download the file from diskLocation
console.log(`⚡️ Downloading Podman package from ${artifactRelease.browser_download_url}`);
await this.#httpsDownloader.downloadFile(artifactRelease.browser_download_url, destFile);
console.log(`📔 Downloaded to ${destFile}`);
} else {
console.log(`⏭️ Skipping Windows podman image for ${arch} (already downloaded to ${filename})`);
}
}
}
export class DownloadAndCheck {
readonly MAX_DOWNLOAD_ATTEMPT = 3;
#downloadAttempt = 0;
@ -680,7 +483,7 @@ export class Podman5DownloadMachineOS {
);
await this.downloadZstdFromManifest(
`${manifestUrl} for amd64`,
'podman-image-amd64.zst',
'podman-image-x64.zst',
amd64ZstdManifest.layers[0],
);
}

View file

@ -404,12 +404,12 @@ test('verify error contains name, message and stderr if creation fails', async (
).rejects.toThrowError('name\ndescription\nerror\n');
});
test('verify create command called with embedded image if using podman v4', async () => {
test('verify create command called with embedded image if using podman v5', async () => {
vi.mocked(isMac).mockReturnValue(true);
vi.mocked(getAssetsFolder).mockReturnValue('fake');
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
stdout: 'podman version 4.0.0',
stdout: 'podman version 5.0.0',
} as extensionApi.RunResult);
await extension.createMachine(
@ -434,42 +434,7 @@ test('verify create command called with embedded image if using podman v4', asyn
expect(vi.mocked(extensionApi.process.exec)).toHaveBeenNthCalledWith(
2,
getPodmanCli(),
expect.arrayContaining([expect.stringContaining('.qcow2.xz')]),
expect.anything(),
);
});
test('verify create command not called with embedded image if using podman v4', async () => {
vi.mocked(isMac).mockReturnValue(true);
vi.mocked(getAssetsFolder).mockReturnValue('fake');
vi.mocked(fs.existsSync).mockReturnValue(true);
vi.mocked(extensionApi.process.exec).mockResolvedValueOnce({
stdout: 'podman version 5.0.0',
} as extensionApi.RunResult);
await extension.createMachine(
{
'podman.factory.machine.cpus': '2',
'podman.factory.machine.memory': '1048000000',
'podman.factory.machine.diskSize': '250000000000',
'podman.factory.machine.now': true,
},
undefined,
undefined,
);
// check telemetry is called with telemetryRecords.imagePath
await vi.waitFor(() => {
expect(telemetryLogger.logUsage).toBeCalledWith(
'podman.machine.init',
expect.not.objectContaining({ imagePath: 'embedded' }),
);
});
expect(vi.mocked(extensionApi.process.exec)).toHaveBeenNthCalledWith(
2,
getPodmanCli(),
expect.not.arrayContaining([expect.stringContaining('.qcow2.xz')]),
expect.arrayContaining([expect.stringContaining('.zst')]),
expect.anything(),
);
});

View file

@ -35,7 +35,7 @@ import type { InstalledPodman } from './podman-cli';
import { getPodmanCli, getPodmanInstallation } from './podman-cli';
import { PodmanConfiguration } from './podman-configuration';
import { PodmanInfoHelper } from './podman-info-helper';
import { PODMAN5_EXPERIMENTAL_MODE_CONFIG_FULLKEY, PodmanInstall } from './podman-install';
import { PodmanInstall } from './podman-install';
import { QemuHelper } from './qemu-helper';
import { RegistrySetup } from './registry-setup';
import { appConfigDir, appHomeDir, getAssetsFolder, isLinux, isMac, isWindows, LoggerDelegator } from './util';
@ -907,13 +907,6 @@ export async function initCheckAndRegisterUpdate(
};
await checkForUpdate();
// if configuration key change, check for update again
extensionApi.configuration.onDidChangeConfiguration(async e => {
if (e.affectsConfiguration(PODMAN5_EXPERIMENTAL_MODE_CONFIG_FULLKEY)) {
await checkForUpdate();
}
});
// register onDidUpdateVersion
provider.onDidUpdateVersion(async () => {
await checkForUpdate();
@ -1727,14 +1720,16 @@ export async function createMachine(
if (isWindows()) {
suffix = `-${process.arch}.tar.xz`;
} else if (isMac()) {
suffix = `-${process.arch}.qcow2.xz`;
suffix = `-${process.arch}.zst`;
}
const assetImagePath = path.resolve(getAssetsFolder(), `podman-image${suffix}`);
const podmanInstallation = await getPodmanInstallation();
// check if the file exists and if it does, use it
if (fs.existsSync(assetImagePath) && podmanInstallation.version.startsWith('4.')) {
// Use embedded image only for Podman 5 and onwards
if (fs.existsSync(assetImagePath) && podmanInstallation.version.startsWith('5.')) {
console.log('using embedded image path');
parameters.push('--image-path');
parameters.push(assetImagePath);
telemetryRecords.imagePath = 'embedded';

View file

@ -666,89 +666,10 @@ test('expect winWSL2 command to be registered as disposable', async () => {
});
describe('getBundledPodmanVersion', () => {
test('should return the podman 4 version if flag is not enabled', async () => {
test('should return the podman 5 version', async () => {
const version = getBundledPodmanVersion();
expect(version.startsWith('4')).toBeTruthy();
expect(version.startsWith('5')).toBeFalsy();
});
test('should return the podman 5 version if experimental podman 5 flag is enabled', async () => {
vi.mocked(extensionApi.configuration.getConfiguration).mockReset();
const getMock = vi.fn();
getMock.mockReturnValue(true);
vi.mocked(extensionApi.configuration.getConfiguration).mockReturnValue({
get: getMock,
has: vi.fn(),
update: vi.fn(),
});
const version = getBundledPodmanVersion();
expect(version.startsWith('4')).toBeFalsy();
expect(version.startsWith('5')).toBeTruthy();
// check first argument of the call to getMock
expect(getMock).toHaveBeenCalled();
// should have called the get with the property for experimental install
expect(getMock).toHaveBeenCalledWith('experimental.install.v5');
});
test('should return the podman 5 version if experimental podman 5 flag is not enabled but first install', async () => {
vi.mocked(extensionApi.configuration.getConfiguration).mockReset();
const getMock = vi.fn();
getMock.mockReturnValue(false);
// exist sync is false
vi.mock('node:fs');
vi.mocked(fs.existsSync).mockReturnValue(false);
vi.mocked(extensionApi.configuration.getConfiguration).mockReturnValue({
get: getMock,
has: vi.fn(),
update: vi.fn(),
});
const version = getBundledPodmanVersion();
expect(version.startsWith('4')).toBeFalsy();
expect(version.startsWith('5')).toBeTruthy();
// check existSync has been called
expect(vi.mocked(fs.existsSync)).toHaveBeenCalled();
// check first argument of the call to getMock
expect(getMock).toHaveBeenCalled();
// should have called the get with the property for experimental install
expect(getMock).toHaveBeenCalledWith('experimental.install.v5');
});
test('should return the podman 4 version if there is some airgapped images (fresh install)', async () => {
vi.mocked(extensionApi.configuration.getConfiguration).mockReset();
const getMock = vi.fn();
getMock.mockReturnValue(false);
// exist sync is false
vi.mock('node:fs');
// no qemu folders
vi.mocked(fs.existsSync).mockReturnValueOnce(false);
vi.mocked(fs.existsSync).mockReturnValueOnce(false);
// but we have some images
vi.mocked(fs.existsSync).mockReturnValueOnce(true);
vi.mocked(fs.readdirSync).mockReturnValue(['podman-image-foo.tar.gz'] as unknown as fs.Dirent[]);
vi.mocked(extensionApi.configuration.getConfiguration).mockReturnValue({
get: getMock,
has: vi.fn(),
update: vi.fn(),
});
const version = getBundledPodmanVersion();
expect(version.startsWith('4')).toBeTruthy();
expect(version.startsWith('5')).toBeFalsy();
// check existSync has been called
expect(vi.mocked(fs.existsSync)).toHaveBeenCalled();
// check first argument of the call to getMock
expect(getMock).toHaveBeenCalled();
// should have called the get with the property for experimental install
expect(getMock).toHaveBeenCalledWith('experimental.install.v5');
});
});

View file

@ -40,7 +40,6 @@ import { PodmanCleanupMacOS } from './podman-cleanup-macos';
import { PodmanCleanupWindows } from './podman-cleanup-windows';
import type { InstalledPodman } from './podman-cli';
import { getPodmanCli, getPodmanInstallation } from './podman-cli';
import * as podman4JSON from './podman4.json';
import * as podman5JSON from './podman5.json';
import { getAssetsFolder, normalizeWSLOutput } from './util';
import { WslHelper } from './wsl-helper';
@ -48,40 +47,8 @@ import { WslHelper } from './wsl-helper';
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
const PODMAN5_EXPERIMENTAL_MODE_CONFIG_KEY = 'experimental.install.v5';
export const PODMAN5_EXPERIMENTAL_MODE_CONFIG_FULLKEY = `podman.${PODMAN5_EXPERIMENTAL_MODE_CONFIG_KEY}`;
export function getBundledPodmanVersion(): string {
const podman5ExperimentalModeEnabled = extensionApi.configuration
.getConfiguration('podman')
.get<boolean>(PODMAN5_EXPERIMENTAL_MODE_CONFIG_KEY);
// also check if the user has never installed podman (for example there is no configuration folder for Podman)
// no ~/.config/containers/podman and ~/.local/share/containers/podman folders on Windows or macOS
let noPreviousPodmanInstallation = false;
if (extensionApi.env.isWindows || extensionApi.env.isMac) {
noPreviousPodmanInstallation =
!fs.existsSync(path.resolve(os.homedir(), '.config/containers/podman')) &&
!fs.existsSync(path.resolve(os.homedir(), '.local/share/containers/podman'));
}
// air gapped mode check.
const assetImagePath = path.resolve(getAssetsFolder());
let airGappedMode = false;
// check if we have some podman-image files
if (fs.existsSync(assetImagePath)) {
const podmanImageFiles = fs.readdirSync(assetImagePath).filter(file => file.startsWith('podman-image'));
if (podmanImageFiles.length > 0) {
airGappedMode = true;
}
}
// default to v5 if experimental mode is enabled or if there is no previous installation (and not using airgapped mode)
if (podman5ExperimentalModeEnabled || (noPreviousPodmanInstallation && !airGappedMode)) {
return podman5JSON.version;
}
return podman4JSON.version;
return podman5JSON.version;
}
export interface PodmanInfo {

View file

@ -1,20 +0,0 @@
{
"version": "4.9.4",
"platform": {
"win32": {
"version": "v4.9.4",
"fileName": "podman-4.9.4-setup.exe"
},
"darwin": {
"version": "v4.9.4",
"arch": {
"x64": {
"fileName": "podman-installer-macos-amd64-v4.9.4.pkg"
},
"arm64": {
"fileName": "podman-installer-macos-aarch64-v4.9.4.pkg"
}
}
}
}
}