Skip to content

Commit

Permalink
feat: install crc on windows
Browse files Browse the repository at this point in the history
  • Loading branch information
evidolob committed Mar 9, 2023
1 parent 35288a7 commit 5e8817e
Show file tree
Hide file tree
Showing 13 changed files with 765 additions and 50 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ dist
**/coverage
yarn-error.log
builtin
assets
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,26 @@
]
},
"scripts": {
"build": "rollup --bundleConfigAsCjs --config rollup.config.js --compact --environment BUILD:production && node ./scripts/build.js",
"build": "rollup --bundleConfigAsCjs --config rollup.config.js --compact --environment BUILD:production && npx ts-node --esm ./scripts/download.mts && node ./scripts/build.js",
"watch": "rollup --bundleConfigAsCjs --config rollup.config.js -w",
"desk:build": "node ./scripts/run.mjs build",
"desk:prepare": "node ./scripts/run.mjs prepare",
"desk:run": "node ./scripts/run.mjs run",
"desk:clean": "node ./scripts/run.mjs clean"
},
"dependencies": {
"got": "^12.5.3"
},
"dependencies": {},
"devDependencies": {
"7zip-min": "^1.4.4",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-typescript": "^11.0.0",
"@types/node": "^18.14.6",
"got": "^12.5.3",
"hasha": "^5.2.2",
"mkdirp": "^2.1.3",
"rollup": "^3.18.0",
"ts-node": "^10.9.1",
"tslib": "^2.5.0",
"typescript": "^4.9.5",
"zip-local": "^0.3.5"
Expand Down
1 change: 0 additions & 1 deletion scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ if (fs.existsSync(builtinDirectory)) {
}

const zip = zipper.sync.zip(path.resolve(__dirname, '../'));
zip.lowLevel().remove('assets');
zip.lowLevel().remove('.git');
zip.lowLevel().remove('node_modules');
zip.compress().save(destFile);
Expand Down
161 changes: 161 additions & 0 deletions scripts/download.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#!/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 * as readline from 'node:readline';
import got from 'got';
import hasha from 'hasha';
import { fileURLToPath } from 'url';

import bundledCrc from '../src/crc.json' assert {type: 'json'};


const __filename = fileURLToPath(import.meta.url);

const __dirname = path.dirname(__filename);

const platform = process.platform;

const MAX_DOWNLOAD_ATTEMPT = 3;
let downloadAttempt = 0;

const platformToInstallFileName = {
'darwin': 'crc-macos-installer.pkg',
'win32': 'crc-windows-installer.zip',
'linux': 'crc-linux-amd64.tar.xz',
};


async function checkFileSha(filePath: string, shaSum: string): Promise<boolean> {
const sha256sum: string = await hasha.fromFile(filePath, { algorithm: 'sha256' });
return sha256sum === shaSum;
}

async function downloadAndCheckSha(downloadUrl: string, destFile: string, fileSha: string): Promise<void>{
if (downloadAttempt >= MAX_DOWNLOAD_ATTEMPT) {
console.error('Max download attempt reached, exiting...');
process.exit(1);
}

let lastProgressStr = '';

process.stdout.write('Downloading:\n');


const downloadResponse = await got(downloadUrl).on('downloadProgress', (progress) => {
const progressStr = progress.transferred + ' of ' + progress.total + ' ' + Math.round(progress.percent * 100) + '%';
if(lastProgressStr !== progressStr) {
readline.cursorTo(process.stdout, 0);
process.stdout.write(progressStr);
readline.clearLine(process.stdout, 1);
lastProgressStr = progressStr;
}
});
// add new line after download percentage
process.stdout.write('\n');

fs.appendFileSync(destFile, Buffer.from(downloadResponse.rawBody));
console.log(`Downloaded to ${destFile}`);

console.log(`Verifying ${destFile}...`);

if (!(await checkFileSha(destFile, fileSha))) {
console.warn(`Checksum for downloaded ${destFile} is not match, downloading again...`);
fs.rmSync(destFile);
downloadAttempt++;
downloadAndCheckSha(downloadUrl, destFile, fileSha);
} else {
console.log(`Checksum for ${destFile} is matched.`);
}

}

async function downloadCrc(): Promise<void> {

console.info(`Found CRC: ${bundledCrc.version.crcVersion}`);


let releaseUrl: string;

if(platform === 'win32'){
releaseUrl = bundledCrc.links.windows;
} else if(platform === 'darwin') {
releaseUrl = bundledCrc.links.darwin;
} else {
releaseUrl = bundledCrc.links.linux;
}

console.info('Download SHA sums...');
const shaSumUrl = releaseUrl.substring(0, releaseUrl.lastIndexOf('/')) + '/sha256sum.txt';
const shaSumContentResponse = await got.get(shaSumUrl);

const installerFileName = platformToInstallFileName[platform];
if(!installerFileName) {
console.error('Can\'t find installer file name. Exiting...');
process.exit(1);
}


const shasSumArr = shaSumContentResponse.body.split('\n');

let installerSha = '';
for(const shaLine of shasSumArr){
if(shaLine.trim().endsWith(installerFileName)) {
installerSha = shaLine.split(' ')[0];
break;
}
}

if(!installerSha) {
console.error(`Can't find SHA256 sum for ${installerFileName} in:\n${shaSumContentResponse.body}`);
process.exit(1);
}

const destDir = path.resolve(__dirname, '..', 'assets');
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir);
}
const destFile = path.resolve(destDir, installerFileName);

if (!fs.existsSync(destFile)) {
console.log(`Downloading Podman package from ${releaseUrl}`);
downloadAndCheckSha(releaseUrl, destFile, installerSha);
return;
} else {
console.log(`Podman package ${releaseUrl} already downloaded.`);
}

console.log(`Verifying ${installerFileName}...`);

if (!(await checkFileSha(destFile, installerSha))) {
console.warn(`Checksum for downloaded ${destFile} is not match, downloading again...`);
fs.rmSync(destFile);
downloadAttempt++;
downloadAndCheckSha(releaseUrl, destFile, installerSha);
} else {
console.log(`Checksum for ${installerFileName} is matched.`);
}

}


if(platform === 'win32' /*|| platform === 'darwin'*/){
downloadCrc();
}
6 changes: 5 additions & 1 deletion scripts/run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ async function exec(command, args, options) {
const proc = cp.spawn(command, args, options);
proc.stderr.pipe(process.stderr);
proc.stdout.pipe(process.stdout);
proc.on('close', () => {
proc.on('close', (code) => {
if(code !== 0){
reject(code);
return;
}
resolve();
});

Expand Down
13 changes: 13 additions & 0 deletions src/crc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": {
"crcVersion": "2.14.0",
"gitSha": "1a1ef27",
"openshiftVersion": "4.12.1",
"podmanVersion": "4.3.1"
},
"links": {
"linux": "https://developers.redhat.com/content-gateway/file/pub/openshift-v4/clients/crc/2.14.0/crc-linux-amd64.tar.xz",
"darwin": "https://developers.redhat.com/content-gateway/file/pub/openshift-v4/clients/crc/2.14.0/crc-macos-installer.pkg",
"windows": "https://developers.redhat.com/content-gateway/file/pub/openshift-v4/clients/crc/2.14.0/crc-windows-installer.zip"
}
}
81 changes: 54 additions & 27 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ import * as fs from 'node:fs';
import type { Status } from './daemon-commander';
import { DaemonCommander } from './daemon-commander';
import { LogProvider } from './log-provider';
import { isWindows } from './util';
import { getAssetsFolder, isWindows } from './util';
import { daemonStart, daemonStop, getCrcVersion } from './crc-cli';
import { getCrcDetectionChecks } from './detection-checks';
import { CrcInstall } from './install/crc-install';

const commander = new DaemonCommander();
let statusFetchTimer: NodeJS.Timer;
Expand All @@ -37,32 +38,16 @@ const crcLogProvider = new LogProvider(commander);
const defaultStatus = { CrcStatus: 'Unknown', Preset: 'Unknown' };

export async function activate(extensionContext: extensionApi.ExtensionContext): Promise<void> {
const crcInstaller = new CrcInstall();

const crcVersion = await getCrcVersion();

const detectionChecks: extensionApi.ProviderDetectionCheck[] = [];
let status: extensionApi.ProviderStatus = 'not-installed';
let preset = 'unknown';

if (crcVersion) {
status = 'installed';

const daemonStarted = await daemonStart();
if (!daemonStarted) {
//TODO handle this
return;
}

try {
// initial status
crcStatus = await commander.status();
} catch (err) {
console.error('error in CRC extension', err);
crcStatus = defaultStatus;
}

// detect preset of CRC
preset = readPreset(crcStatus);

startStatusUpdateTimer();
connectToCrc();
}

detectionChecks.push(...getCrcDetectionChecks(crcVersion));
Expand Down Expand Up @@ -109,12 +94,22 @@ export async function activate(extensionContext: extensionApi.ExtensionContext):
};

provider.registerLifecycle(providerLifecycle);
if (preset === 'Podman') {
// podman connection ?
registerPodmanConnection(provider, extensionContext);
} else if (preset === 'OpenShift') {
// OpenShift
registerOpenShiftLocalCluster(provider, extensionContext);
// initial preset check
presetChanged(provider, extensionContext);

if (crcInstaller.isAbleToInstall()) {
const installationDisposable = provider.registerInstallation({
preflightChecks: () => {
return crcInstaller.getInstallChecks();
},
install: (logger: extensionApi.Logger) => {
return crcInstaller.doInstallCrc(provider, logger, async () => {
await connectToCrc();
presetChanged(provider, extensionContext);
});
},
});
extensionContext.subscriptions.push(installationDisposable);
}
}

Expand Down Expand Up @@ -233,3 +228,35 @@ function readPreset(crcStatus: Status): 'Podman' | 'OpenShift' | 'unknown' {
return 'unknown';
}
}

async function connectToCrc(): Promise<void> {
const daemonStarted = await daemonStart();
if (!daemonStarted) {
//TODO handle this
return;
}

try {
// initial status
crcStatus = await commander.status();
} catch (err) {
console.error('error in CRC extension', err);
crcStatus = defaultStatus;
}

startStatusUpdateTimer();
}

function presetChanged(provider: extensionApi.Provider, extensionContext: extensionApi.ExtensionContext): void {
// TODO: handle situation if some cluster/connection was registered already

// detect preset of CRC
const preset = readPreset(crcStatus);
if (preset === 'Podman') {
// podman connection ?
registerPodmanConnection(provider, extensionContext);
} else if (preset === 'OpenShift') {
// OpenShift
registerOpenShiftLocalCluster(provider, extensionContext);
}
}
Loading

0 comments on commit 5e8817e

Please sign in to comment.