Skip to content

Commit

Permalink
Merge pull request #13212 from torchiaf/backport-13206
Browse files Browse the repository at this point in the history
[backport 2.10] Match Harvester community repo check via regex
  • Loading branch information
torchiaf authored Jan 27, 2025
2 parents 125e7a6 + be3ee30 commit da5c6f2
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 71 deletions.
2 changes: 1 addition & 1 deletion pkg/harvester-manager/l10n/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
51 changes: 39 additions & 12 deletions pkg/harvester-manager/list/harvesterhci.io.management.cluster.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
Expand All @@ -234,13 +239,17 @@ export default {
let installed = false;
try {
const harvesterRepo = await ensureHelmRepository(this.$store, HARVESTER_REPO.spec.gitRepo, HARVESTER_REPO.metadata.name, HARVESTER_REPO.spec.gitBranch);
let harvesterRepository = this.harvesterRepository;
if (!harvesterRepository) {
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, harvesterRepository.spec.gitRepo || harvesterRepository.spec.url);
this.harvesterInstallVersion = await getLatestExtensionVersion(this.$store, HARVESTER_CHART.name, this.rancherVersion, this.kubeVersion);
Expand All @@ -250,7 +259,16 @@ export default {
return;
}
await installHelmChart(harvesterRepo, { ...HARVESTER_CHART, version: this.harvesterInstallVersion }, {}, UI_PLUGIN_NAMESPACE, 'install');
await installHelmChart(
harvesterRepository,
{
...HARVESTER_CHART,
version: this.harvesterInstallVersion
},
{},
UI_PLUGIN_NAMESPACE,
'install'
);
const extension = await waitForUIExtension(this.$store, HARVESTER_CHART.name);
Expand All @@ -272,7 +290,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();
}
Expand All @@ -283,7 +301,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);
Expand Down
25 changes: 11 additions & 14 deletions pkg/harvester-manager/types.js
Original file line number Diff line number Diff line change
@@ -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: '',
Expand All @@ -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,
};
100 changes: 56 additions & 44 deletions shell/utils/uiplugins.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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<HelmRepository> {
export async function getHelmRepositoryExact(store: any, url: string): Promise<HelmRepository> {
return await 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<HelmRepository> {
return await 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<HelmRepository> {
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<void> {
const repository = await getHelmRepository(store, gitRepo, gitBranch);
export async function refreshHelmRepository(store: any, url: string): Promise<void> {
const repository = await getHelmRepositoryExact(store, url);

const now = (new Date()).toISOString().replace(/\.\d+Z$/, 'Z');

Expand All @@ -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<HelmRepository> {
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<HelmRepository> {
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;
Expand Down Expand Up @@ -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
*/
Expand Down

0 comments on commit da5c6f2

Please sign in to comment.