diff --git a/pkg/harvester-manager/l10n/en-us.yaml b/pkg/harvester-manager/l10n/en-us.yaml index ae122e17609..a8b915ee264 100644 --- a/pkg/harvester-manager/l10n/en-us.yaml +++ b/pkg/harvester-manager/l10n/en-us.yaml @@ -35,7 +35,7 @@ harvesterManager: prompt-standard-user: Please contact your system administrator to install the latest Harvester UI Extension, if any missingVersion: warning: "Could not find a compatible version" - prompt: "Please update Rancher to get the latest compatible version of the Harvester UI extension" + prompt: "Please update Rancher to get the latest compatible version of the Harvester UI extension or try to install it manually" prompt-standard-user: Please contact your system administrator error: warning: "Warning, Harvester UI extension automatic installation failed" diff --git a/pkg/harvester-manager/list/harvesterhci.io.management.cluster.vue b/pkg/harvester-manager/list/harvesterhci.io.management.cluster.vue index c25490cf9be..0faf44c3bb0 100644 --- a/pkg/harvester-manager/list/harvesterhci.io.management.cluster.vue +++ b/pkg/harvester-manager/list/harvesterhci.io.management.cluster.vue @@ -14,11 +14,12 @@ import { allHash } from '@shell/utils/promise'; import { NAME as APP_PRODUCT } from '@shell/config/product/apps'; import { BLANK_CLUSTER } from '@shell/store/store-types.js'; import { UI_PLUGIN_NAMESPACE } from '@shell/config/uiplugins'; -import { HARVESTER_CHART, HARVESTER_COMMUNITY_REPO, HARVESTER_RANCHER_REPO } from '../types'; +import { HARVESTER_CHART, HARVESTER_COMMUNITY_REPO, HARVESTER_RANCHER_REPO, communityRepoRegexes } from '../types'; import { getLatestExtensionVersion, - getHelmRepository, - ensureHelmRepository, + getHelmRepositoryExact, + getHelmRepositoryMatch, + createHelmRepository, refreshHelmRepository, installHelmChart, waitForUIExtension, @@ -102,9 +103,9 @@ export default { }, watch: { - async harvesterRepository(value) { - if (value) { - await refreshHelmRepository(this.$store, HARVESTER_REPO.spec.gitRepo, HARVESTER_REPO.spec.gitBranch); + async harvesterRepository(neu) { + if (neu) { + await refreshHelmRepository(this.$store, neu.spec.gitRepo || neu.spec.url); if (this.harvester.extension) { await this.setHarvesterUpdateVersion(); @@ -212,7 +213,11 @@ export default { methods: { async getHarvesterRepository() { try { - return await getHelmRepository(this.$store, HARVESTER_REPO.spec.gitRepo, HARVESTER_REPO.spec.gitBranch); + if (isRancherPrime()) { + return await getHelmRepositoryExact(this.$store, HARVESTER_REPO.gitRepo); + } else { + return await getHelmRepositoryMatch(this.$store, communityRepoRegexes); + } } catch (error) { this.harvesterRepositoryError = true; } @@ -234,13 +239,15 @@ export default { let installed = false; try { - const harvesterRepo = await ensureHelmRepository(this.$store, HARVESTER_REPO.spec.gitRepo, HARVESTER_REPO.metadata.name, HARVESTER_REPO.spec.gitBranch); + if (!this.harvesterRepository) { + this.harvesterRepository = await createHelmRepository(this.$store, HARVESTER_REPO.metadata.name, HARVESTER_REPO.gitRepo, HARVESTER_REPO.gitBranch); + } /** * Server issue * It needs to refresh the HelmRepository because the server can have a previous one in the cache. */ - await refreshHelmRepository(this.$store, HARVESTER_REPO.spec.gitRepo, HARVESTER_REPO.spec.gitBranch); + await refreshHelmRepository(this.$store, this.harvesterRepository.spec.gitRepo || this.harvesterRepository.spec.url); this.harvesterInstallVersion = await getLatestExtensionVersion(this.$store, HARVESTER_CHART.name, this.rancherVersion, this.kubeVersion); @@ -250,7 +257,16 @@ export default { return; } - await installHelmChart(harvesterRepo, { ...HARVESTER_CHART, version: this.harvesterInstallVersion }, {}, UI_PLUGIN_NAMESPACE, 'install'); + await installHelmChart( + this.harvesterRepository, + { + ...HARVESTER_CHART, + version: this.harvesterInstallVersion + }, + {}, + UI_PLUGIN_NAMESPACE, + 'install' + ); const extension = await waitForUIExtension(this.$store, HARVESTER_CHART.name); @@ -272,7 +288,7 @@ export default { try { if (this.harvester.missingRepository) { - this.harvesterRepository = await ensureHelmRepository(this.$store, HARVESTER_REPO.spec.gitRepo, HARVESTER_REPO.metadata.name, HARVESTER_REPO.spec.gitBranch); + this.harvesterRepository = await createHelmRepository(this.$store, HARVESTER_REPO.metadata.name, HARVESTER_REPO.gitRepo, HARVESTER_REPO.gitBranch); await this.setHarvesterUpdateVersion(); } @@ -283,7 +299,16 @@ export default { return; } - await installHelmChart(this.harvesterRepository, { ...HARVESTER_CHART, version: this.harvesterUpdateVersion }, {}, UI_PLUGIN_NAMESPACE, 'upgrade'); + await installHelmChart( + this.harvesterRepository, + { + ...HARVESTER_CHART, + version: this.harvesterUpdateVersion + }, + {}, + UI_PLUGIN_NAMESPACE, + 'upgrade' + ); const extension = await waitForUIExtension(this.$store, HARVESTER_CHART.name); diff --git a/pkg/harvester-manager/types.js b/pkg/harvester-manager/types.js index a7845391565..4fe4b8bac27 100644 --- a/pkg/harvester-manager/types.js +++ b/pkg/harvester-manager/types.js @@ -1,5 +1,10 @@ import { UI_PLUGINS_REPOS } from '@shell/config/uiplugins'; +export const communityRepoRegexes = [ + /^https:\/\/github\.com\/.*\/harvester-ui-extension+/g, + /^https:\/\/.*\.github\.io\/harvester-ui-extension+/g, +]; + export const HARVESTER_CHART = { name: 'harvester', version: '', @@ -8,21 +13,13 @@ export const HARVESTER_CHART = { }; export const HARVESTER_COMMUNITY_REPO = { - type: 'catalog.cattle.io.clusterrepo', - metadata: { name: 'harvester' }, - spec: { - clientSecret: null, - gitRepo: 'https://github.com/harvester/harvester-ui-extension', - gitBranch: 'gh-pages' - } + metadata: { name: 'harvester' }, + gitRepo: 'https://github.com/harvester/harvester-ui-extension', + gitBranch: 'gh-pages', }; export const HARVESTER_RANCHER_REPO = { - type: 'catalog.cattle.io.clusterrepo', - metadata: { name: 'rancher' }, - spec: { - clientSecret: null, - gitRepo: UI_PLUGINS_REPOS.OFFICIAL.URL, - gitBranch: UI_PLUGINS_REPOS.OFFICIAL.BRANCH, - } + metadata: { name: 'rancher' }, + gitRepo: UI_PLUGINS_REPOS.OFFICIAL.URL, + gitBranch: UI_PLUGINS_REPOS.OFFICIAL.BRANCH, }; diff --git a/shell/utils/uiplugins.ts b/shell/utils/uiplugins.ts index c748e2260bd..f27fe7e02de 100644 --- a/shell/utils/uiplugins.ts +++ b/shell/utils/uiplugins.ts @@ -1,3 +1,4 @@ +import { matchesSomeRegex } from '@shell/utils/string'; import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations'; import { CATALOG } from '@shell/config/types'; import { UI_PLUGIN_BASE_URL, isSupportedChartVersion } from '@shell/config/uiplugins'; @@ -158,37 +159,56 @@ export async function installHelmChart(repo: any, chart: any, values: any = {}, } /** - * Get the Helm repository object * * @param store Vue store - * @param url The url of the Helm repository - * @param branch The branch of the Helm repository + * @param url Repository Url * @returns HelmRepository */ -export async function getHelmRepository(store: any, url: string, branch?: string): Promise { +export async function getHelmRepositoryExact(store: any, url: string): Promise { + return getHelmRepository(store, (repository: any) => { + const target = repository.spec?.gitRepo || repository.spec?.url; + + return target === url; + }); +} + +/** + * + * @param store Vue store + * @param urlRegexes Regex to match a community repository + * @returns HelmRepository + */ +export async function getHelmRepositoryMatch(store: any, urlRegexes: string[]): Promise { + return getHelmRepository(store, (repository: any) => { + const target = repository.spec?.gitBranch ? repository.spec?.gitRepo : repository.spec?.url; + + return matchesSomeRegex(target, urlRegexes); + }); +} + +/** + * + * @param store Vue store + * @param matchFn Match function for repository's urls + * @returns HelmRepository + */ +async function getHelmRepository(store: any, matchFn: (repository: any) => boolean): Promise { if (store.getters['management/schemaFor'](CATALOG.CLUSTER_REPO)) { const repos = await store.dispatch('management/findAll', { type: CATALOG.CLUSTER_REPO, opt: { force: true, watch: false } }); - return repos.find((r: any) => { - const target = branch ? r.spec?.gitRepo : r.spec?.url ; - - return target === url; - }); + return repos.find(matchFn); } else { throw new Error('No permissions'); } } /** - * Refresh the Helm repository - * Ensures that we find the latest extension versions * * @param store Vue store - * @param gitRepo Extension Repository url - * @param gitBranch Extension Repository branch + * @param url Repository Url */ -export async function refreshHelmRepository(store: any, gitRepo: string, gitBranch: string): Promise { - const repository = await getHelmRepository(store, gitRepo, gitBranch); +export async function refreshHelmRepository(store: any, url: string): Promise { + const repository = await getHelmRepositoryExact(store, url); const now = (new Date()).toISOString().replace(/\.\d+Z$/, 'Z'); @@ -202,40 +222,32 @@ export async function refreshHelmRepository(store: any, gitRepo: string, gitBran } /** - * Ensure the required Helm Repository exits, if it does not, add it with the specified name - * - * Wait until the newly added repository has been downloaded * * @param store Vue store - * @param url The url of the Helm repository - * @param name The name of the cluster repository - * @param branch The branch of the Helm repository - * @returns HelmRepository object + * @param name Repository name + * @param url Repository Url + * @param branch Repository Branch + * @returns HelmRepository */ -export async function ensureHelmRepository(store: any, url: string, name: string, branch?: string): Promise { - let helmRepo = await getHelmRepository(store, url, branch); - - // Add the Helm repository, if it is not there - if (!helmRepo) { - const data = { - type: CATALOG.CLUSTER_REPO, - metadata: { name }, - spec: {} as any - }; - - if (branch) { - data.spec.gitBranch = branch; - data.spec.gitRepo = url; - } else { - data.spec.url = url; - } - - // Create a model for the new repository and save it - const repo = await store.dispatch('management/create', data); +export async function createHelmRepository(store: any, name: string, url: string, branch?: string): Promise { + const data = { + type: CATALOG.CLUSTER_REPO, + metadata: { name }, + spec: {} as any + }; - helmRepo = await repo.save(); + if (branch) { + data.spec.gitBranch = branch; + data.spec.gitRepo = url; + } else { + data.spec.url = url; } + // Create a model for the new repository and save it + const repo = await store.dispatch('management/create', data); + + const helmRepo = await repo.save(); + // Poll the repository until it says it has been downloaded let fetched = false; let tries = 0; @@ -278,7 +290,7 @@ export async function ensureHelmRepository(store: any, url: string, name: string * Get the given Helm Chart from the specified Helm Repository * * @param store Vue store - * @param repository Helm Repository + * @param repository Repository Url * @param chartName Helm Chart name * @returns Helm Chart */