diff --git a/src/crc-cli.ts b/src/crc-cli.ts index c1f6baa..e0560db 100644 --- a/src/crc-cli.ts +++ b/src/crc-cli.ts @@ -168,12 +168,3 @@ export function daemonStop() { daemonProcess.kill(); } } - -export async function needSetup(): Promise { - try { - await execPromise(getCrcCli(), ['setup', '--check-only']); - return false; - } catch (e) { - return true; - } -} diff --git a/src/crc-setup.ts b/src/crc-setup.ts index 7937929..cb5534b 100644 --- a/src/crc-setup.ts +++ b/src/crc-setup.ts @@ -25,6 +25,19 @@ interface PresetQuickPickItem extends extensionApi.QuickPickItem { data: Preset; } +export let isNeedSetup = false; + +export async function needSetup(): Promise { + try { + await execPromise(getCrcCli(), ['setup', '--check-only']); + isNeedSetup = false; + return false; + } catch (e) { + isNeedSetup = true; + return true; + } +} + // eslint-disable-next-line @typescript-eslint/no-unused-vars export async function setUpCrc(logger: extensionApi.Logger, askForPreset = false): Promise { if (askForPreset) { diff --git a/src/crc-start.ts b/src/crc-start.ts new file mode 100644 index 0000000..c35178a --- /dev/null +++ b/src/crc-start.ts @@ -0,0 +1,103 @@ +/********************************************************************** + * 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 extensionApi from '@podman-desktop/api'; +import { isNeedSetup, needSetup, setUpCrc } from './crc-setup'; +import { crcStatus } from './crc-status'; +import { commander } from './daemon-commander'; +import { crcLogProvider } from './log-provider'; + +interface ImagePullSecret { + auths: { auth: string; credsStore: string }[]; +} + +const missingPullSecret = 'Failed to ask for pull secret'; + +export async function startCrc(logger: extensionApi.Logger): Promise { + try { + // call crc setup to prepare bundle, before start + if (isNeedSetup) { + try { + crcStatus.setSetupRunning(true); + await setUpCrc(logger); + await needSetup(); + } catch (error) { + logger.error(error); + return; + } finally { + crcStatus.setSetupRunning(false); + } + } + crcLogProvider.startSendingLogs(logger); + const result = await commander.start(); + console.error('StartResult:' + JSON.stringify(result)); + } catch (err) { + if (typeof err.message === 'string') { + // check that crc missing pull secret + if (err.message.startsWith(missingPullSecret)) { + // ask user to provide pull secret + if (await askAndStorePullSecret(logger)) { + // if pull secret provided try to start again + return startCrc(logger); + } + return; + } else if (err.name === 'RequestError' && err.code === 'ECONNRESET') { + // look like crc start normally, but we receive empty response from socket, so 'got' generate an error + return; + } + } + + console.error(err); + } +} + +async function askAndStorePullSecret(logger: extensionApi.Logger): Promise { + const pullSecret = await extensionApi.window.showInputBox({ + prompt: 'Provide a pull secret', + // prompt: 'To pull container images from the registry, a pull secret is necessary.', + ignoreFocusOut: true, + }); + + if (!pullSecret) { + return false; + } + try { + const s: ImagePullSecret = JSON.parse(pullSecret); + if (s.auths && s.auths.length > 0) { + for (const a of s.auths) { + if (!a.auth && !a.credsStore) { + throw `${JSON.stringify(s)} JSON-object requires either 'auth' or 'credsStore' field`; + } + } + } else { + throw 'missing "auths" JSON-object field'; + } + } catch (err) { + // not valid json + extensionApi.window.showErrorMessage(`Pull secret is not valid:\n '${err}',\n please try again`); + return false; + } + try { + await commander.pullSecretStore(pullSecret); + return true; + } catch (error) { + console.error(error); + logger.error(error); + } + return false; +} diff --git a/src/crc-status.ts b/src/crc-status.ts index dd3ea39..40e69d0 100644 --- a/src/crc-status.ts +++ b/src/crc-status.ts @@ -58,7 +58,7 @@ export class CrcStatus { } get status(): Status { - return this.status; + return this._status; } setErrorStatus(): void { diff --git a/src/daemon-commander.ts b/src/daemon-commander.ts index 9c18c18..781863d 100644 --- a/src/daemon-commander.ts +++ b/src/daemon-commander.ts @@ -139,14 +139,15 @@ export class DaemonCommander { async pullSecretStore(value: unknown): Promise { const url = this.apiPath + '/pull-secret'; - await got.post(url, { - json: value, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const response = await (got as any).post(url, { + body: value, throwHttpErrors: false, retry: { limit: 0, }, }); - return 'OK'; + return response.body; } async pullSecretAvailable() { diff --git a/src/extension.ts b/src/extension.ts index 467a60d..937e325 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,17 +22,15 @@ import * as os from 'node:os'; import * as fs from 'node:fs'; import type { Status } from './daemon-commander'; import { commander } from './daemon-commander'; -import { LogProvider } from './log-provider'; +import { crcLogProvider } from './log-provider'; import { isWindows, productName } from './util'; -import { daemonStart, daemonStop, getCrcVersion, needSetup } from './crc-cli'; +import { daemonStart, daemonStop, getCrcVersion } from './crc-cli'; import { getCrcDetectionChecks } from './detection-checks'; import { CrcInstall } from './install/crc-install'; -import { setUpCrc } from './crc-setup'; -import { crcStatus } from './crc-status'; - -const crcLogProvider = new LogProvider(commander); -let isNeedSetup = false; +import { crcStatus } from './crc-status'; +import { startCrc } from './crc-start'; +import { isNeedSetup, needSetup } from './crc-setup'; export async function activate(extensionContext: extensionApi.ExtensionContext): Promise { const crcInstaller = new CrcInstall(); @@ -44,13 +42,11 @@ export async function activate(extensionContext: extensionApi.ExtensionContext): if (crcVersion) { status = 'installed'; - } - - isNeedSetup = await needSetup(); - if (crcVersion) { connectToCrc(); } + await needSetup(); + detectionChecks.push(...getCrcDetectionChecks(crcVersion)); // create CRC provider @@ -202,28 +198,6 @@ function presetChanged(provider: extensionApi.Provider, extensionContext: extens } } -async function startCrc(logger: extensionApi.Logger): Promise { - try { - // call crc setup to prepare bundle, before start - if (isNeedSetup) { - try { - crcStatus.setSetupRunning(true); - await setUpCrc(logger); - isNeedSetup = false; - } catch (error) { - logger.error(error); - return; - } finally { - crcStatus.setSetupRunning(false); - } - } - crcLogProvider.startSendingLogs(logger); - await commander.start(); - } catch (err) { - console.error(err); - } -} - async function stopCrc(): Promise { console.log('extension:crc: receive the call stop'); try { diff --git a/src/install/crc-install.ts b/src/install/crc-install.ts index a8b275b..bff9e7d 100644 --- a/src/install/crc-install.ts +++ b/src/install/crc-install.ts @@ -23,10 +23,10 @@ import * as os from 'node:os'; import type { CrcReleaseInfo, Installer } from './base-install'; import { WinInstall } from './win-install'; -import { getCrcVersion, needSetup } from '../crc-cli'; +import { getCrcVersion } from '../crc-cli'; import { getCrcDetectionChecks } from '../detection-checks'; import { MacOsInstall } from './mac-install'; -import { setUpCrc } from '../crc-setup'; +import { needSetup, setUpCrc } from '../crc-setup'; const crcLatestReleaseUrl = 'https://developers.redhat.com/content-gateway/rest/mirror/pub/openshift-v4/clients/crc/latest/release-info.json'; diff --git a/src/log-provider.ts b/src/log-provider.ts index d7d17e9..cccb72d 100644 --- a/src/log-provider.ts +++ b/src/log-provider.ts @@ -18,6 +18,7 @@ import type { Logger } from '@podman-desktop/api'; import type { DaemonCommander } from './daemon-commander'; +import { commander } from './daemon-commander'; export class LogProvider { private timeout: NodeJS.Timeout; @@ -41,3 +42,5 @@ export class LogProvider { clearInterval(this.timeout); } } + +export const crcLogProvider = new LogProvider(commander);