diff --git a/pkg/rancher-desktop/components/UpdateStatus.vue b/pkg/rancher-desktop/components/UpdateStatus.vue index d4967856621..5ca2f2f811f 100644 --- a/pkg/rancher-desktop/components/UpdateStatus.vue +++ b/pkg/rancher-desktop/components/UpdateStatus.vue @@ -36,16 +36,15 @@ - + - + diff --git a/pkg/rancher-desktop/components/__tests__/UpdateStatus.spec.ts b/pkg/rancher-desktop/components/__tests__/UpdateStatus.spec.ts index 8e897cb59e8..ee573c1f047 100644 --- a/pkg/rancher-desktop/components/__tests__/UpdateStatus.spec.ts +++ b/pkg/rancher-desktop/components/__tests__/UpdateStatus.spec.ts @@ -98,12 +98,12 @@ describe('UpdateStatus.vue', () => { available: true, downloaded: false, info: { - version: 'v1.2.3', - files: [], - path: '', - sha512: '', - releaseDate: '', - nextUpdateTime: 12345, + version: 'v1.2.3', + files: [], + path: '', + sha512: '', + releaseDate: '', + nextUpdateTime: 12345, unsupportedUpgradeAvailable: false, }, progress: { diff --git a/pkg/rancher-desktop/main/update/LonghornProvider.ts b/pkg/rancher-desktop/main/update/LonghornProvider.ts index 7a0b66ea557..e066c4d3fc5 100644 --- a/pkg/rancher-desktop/main/update/LonghornProvider.ts +++ b/pkg/rancher-desktop/main/update/LonghornProvider.ts @@ -71,18 +71,26 @@ export interface LonghornUpdateInfo extends UpdateInfo { * responder service. */ interface LonghornUpgraderResponse { - versions: { - Name: string; - ReleaseDate: Date; - Supported: boolean | undefined; - Tags: string[]; - }[]; + versions: UpgradeResponderVersion[]; /** * The number of minutes before the next upgrade check should be performed. */ requestIntervalInMinutes: number; } +type UpgradeResponderVersion = { + Name: string; + ReleaseDate: Date; + Supported?: boolean; + Tags: string[]; +}; + +type UpgradeResponderQueryResult = { + latest: UpgradeResponderVersion; + requestIntervalInMinutes: number, + unsupportedUpgradeAvailable: boolean, +}; + export interface GithubReleaseAsset { url: string; @@ -199,9 +207,7 @@ export async function setHasQueuedUpdate(isQueued: boolean): Promise { } async function getPlatformVersion(): Promise { - const platform = os.platform(); - - switch (platform) { + switch (process.platform) { case 'win32': return os.release(); case 'darwin': { @@ -219,7 +225,58 @@ async function getPlatformVersion(): Promise { // should suffice. return 'unknown'; } - throw new Error(`Platform "${ platform }" is not supported`); + throw new Error(`Platform "${ process.platform }" is not supported`); +} + +/** + * Fetch info on available versions of Rancher Desktop, as well as other + * things, from the Upgrade Responder server. + */ +export async function queryUpgradeResponder(url: string, currentVersion: semver.SemVer): Promise { + const requestPayload = { + appVersion: currentVersion, + extraInfo: { + platform: `${ process.platform }-${ os.arch() }`, + platformVersion: await getPlatformVersion(), + }, + }; + // If we are using anything on `github.io` as the update server, we're + // trying to run a simplified test. In that case, break the protocol and do + // a HTTP GET instead of the HTTP POST with data we should do for actual + // longhorn upgrade-responder servers. + const requestOptions = /^https?:\/\/[^/]+\.github\.io\//.test(url) ? { method: 'GET' } : { + method: 'POST', + body: JSON.stringify(requestPayload), + }; + + console.debug(`Checking for upgrades from ${ url }`); + const responseRaw = await fetch(url, requestOptions); + const response = await responseRaw.json() as LonghornUpgraderResponse; + + console.debug(`Upgrade server response:`, util.inspect(response, true, null)); + + const allVersions = response.versions; + + // If the upgrade responder does not send the Supported field, + // assume that the version is supported. + for (const version of allVersions) { + version.Supported ??= true; + } + + allVersions.sort((version1, version2) => semver.rcompare(version1.Name, version2.Name)); + const supportedVersions = allVersions.filter(version => version.Supported); + + if (supportedVersions.length === 0) { + throw newError('Could not find latest version', 'ERR_UPDATER_LATEST_VERSION_NOT_FOUND'); + } + const latest = supportedVersions[0]; + const unsupportedUpgradeAvailable = allVersions[0].Name !== latest.Name; + + return { + latest, + requestIntervalInMinutes: response.requestIntervalInMinutes, + unsupportedUpgradeAvailable, + }; } /** @@ -257,43 +314,6 @@ export default class LonghornProvider extends Provider { return buffer.toString('base64'); } - /** - * Fetch info on available versions of Rancher Desktop from the Upgrade - * Responder server. - * @returns Array of available versions. - */ - async fetchUpgraderResponse(): Promise { - const requestPayload = { - appVersion: this.updater.currentVersion.format(), - extraInfo: { - platform: `${ os.platform() }-${ os.arch() }`, - platformVersion: await getPlatformVersion(), - }, - }; - // If we are using anything on `github.io` as the update server, we're - // trying to run a simplified test. In that case, break the protocol and do - // a HTTP GET instead of the HTTP POST with data we should do for actual - // longhorn upgrade-responder servers. - const requestOptions = /^https?:\/\/[^/]+\.github\.io\//.test(this.configuration.upgradeServer) ? { method: 'GET' } : { - method: 'POST', - body: JSON.stringify(requestPayload), - }; - - console.debug(`Checking for upgrades from ${ this.configuration.upgradeServer }`); - const responseRaw = await fetch(this.configuration.upgradeServer, requestOptions); - const response = await responseRaw.json() as LonghornUpgraderResponse; - - console.debug(`Upgrade server response:`, util.inspect(response, true, null)); - - // If the upgrade responder does not send the Supported field, - // assume that the version is supported. - for (const version of response.versions) { - version.Supported = version.Supported ?? true; - } - - return response; - } - /** * Check for updates, possibly returning the cached information if it is still * applicable. @@ -313,19 +333,9 @@ export default class LonghornProvider extends Provider { } } - const response = await this.fetchUpgraderResponse(); - const allVersions = response.versions; - - allVersions.sort((version1, version2) => semver.rcompare(version1.Name, version2.Name)); - const supportedVersions = allVersions.filter(version => version.Supported); - - if (supportedVersions.length === 0) { - throw newError('Could not find latest version', 'ERR_UPDATER_LATEST_VERSION_NOT_FOUND'); - } - const latest = supportedVersions[0]; - const unsupportedUpgradeAvailable = allVersions[0].Name !== latest.Name; - - const requestIntervalInMinutes = response.requestIntervalInMinutes || defaultUpdateIntervalInMinutes; + const queryResult = await queryUpgradeResponder(this.configuration.upgradeServer, this.updater.currentVersion); + const { latest, unsupportedUpgradeAvailable } = queryResult; + const requestIntervalInMinutes = queryResult.requestIntervalInMinutes || defaultUpdateIntervalInMinutes; const requestIntervalInMs = requestIntervalInMinutes * 1000 * 60; const nextRequestTime = Date.now() + requestIntervalInMs; diff --git a/pkg/rancher-desktop/main/update/__tests__/LonghornProvider.spec.ts b/pkg/rancher-desktop/main/update/__tests__/LonghornProvider.spec.ts new file mode 100644 index 00000000000..2fc66cd06a8 --- /dev/null +++ b/pkg/rancher-desktop/main/update/__tests__/LonghornProvider.spec.ts @@ -0,0 +1,109 @@ +import semver from 'semver'; + +import { queryUpgradeResponder } from '../LonghornProvider'; + +import fetch from '@pkg/utils/fetch'; + +jest.mock('@pkg/utils/fetch', () => { + return { + __esModule: true, + default: jest.fn(), + }; +}); + +describe('queryUpgradeResponder', () => { + it('should return the latest version', async() => { + (fetch as jest.Mock).mockReturnValueOnce({ + json: () => Promise.resolve({ + requestIntervalInMinutes: 100, + versions: [ + { + Name: 'v1.2.3', + ReleaseDate: 'testreleasedate', + Supported: true, + Tags: [], + }, + { + Name: 'v3.2.1', + ReleaseDate: 'testreleasedate', + Supported: true, + Tags: [], + }, + { + Name: 'v2.1.3', + ReleaseDate: 'testreleasedate', + Supported: true, + Tags: [], + }, + ], + }), + }); + const result = await queryUpgradeResponder('testurl', new semver.SemVer('v1.2.3')); + + expect(result.latest.Name).toEqual('v3.2.1'); + }); + + it('should set unsupportedUpgradeAvailable to true when a newer-than-latest version is unsupported', async() => { + (fetch as jest.Mock).mockReturnValueOnce({ + json: () => Promise.resolve({ + requestIntervalInMinutes: 100, + versions: [ + { + Name: 'v1.2.3', + ReleaseDate: 'testreleasedate', + Supported: true, + Tags: [], + }, + { + Name: 'v3.2.1', + ReleaseDate: 'testreleasedate', + Supported: false, + Tags: [], + }, + { + Name: 'v2.1.3', + ReleaseDate: 'testreleasedate', + Supported: true, + Tags: [], + }, + ], + }), + }); + const result = await queryUpgradeResponder('testurl', new semver.SemVer('v1.2.3')); + + expect(result.unsupportedUpgradeAvailable).toBe(true); + expect(result.latest.Name).toEqual('v2.1.3'); + }); + + it('should set unsupportedUpgradeAvailable to false when no newer-than-latest versions are unsupported', async() => { + (fetch as jest.Mock).mockReturnValueOnce({ + json: () => Promise.resolve({ + requestIntervalInMinutes: 100, + versions: [ + { + Name: 'v1.2.3', + ReleaseDate: 'testreleasedate', + Supported: true, + Tags: [], + }, + { + Name: 'v3.2.1', + ReleaseDate: 'testreleasedate', + Supported: true, + Tags: [], + }, + { + Name: 'v2.1.3', + ReleaseDate: 'testreleasedate', + Supported: false, + Tags: [], + }, + ], + }), + }); + const result = await queryUpgradeResponder('testurl', new semver.SemVer('v1.2.3')); + + expect(result.unsupportedUpgradeAvailable).toBe(false); + expect(result.latest.Name).toEqual('v3.2.1'); + }); +}); diff --git a/pkg/rancher-desktop/main/update/index.ts b/pkg/rancher-desktop/main/update/index.ts index a69777aa011..af0645de175 100644 --- a/pkg/rancher-desktop/main/update/index.ts +++ b/pkg/rancher-desktop/main/update/index.ts @@ -166,7 +166,7 @@ async function getUpdater(): Promise { }); updater.on('update-downloaded', (info) => { if (!isLonghornUpdateInfo(info)) { - throw new Error('updater: event update-not-available: info is not of type LonghornUpdateInfo'); + throw new Error('updater: event update-downloaded: info is not of type LonghornUpdateInfo'); } if (state === State.DOWNLOADING) { state = State.UPDATE_PENDING; diff --git a/pkg/rancher-desktop/utils/osVersion.ts b/pkg/rancher-desktop/utils/osVersion.ts index e37f2d32612..c62bf8bf4f1 100644 --- a/pkg/rancher-desktop/utils/osVersion.ts +++ b/pkg/rancher-desktop/utils/osVersion.ts @@ -5,7 +5,7 @@ import { Log } from '@pkg/utils/logging'; export async function getMacOsVersion(console: Log): Promise { const { stdout } = await spawnFile('/usr/bin/sw_vers', ['-productVersion'], { stdio: ['ignore', 'pipe', console] }); - const currentVersion = semver.coerce(stdout.trim()); + const currentVersion = semver.coerce(stdout); return currentVersion; }