Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions shell/components/SelectIconGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -131,16 +131,6 @@ export default {
class="side-label"
:class="{'indicator': true }"
/>
<div v-if="r.deploysOnWindows">
<label class="deploys-os-label">
{{ t('catalog.charts.deploysOnWindows') }}
</label>
</div>
<div v-if="r.windowsIncompatible">
<label class="os-incompatible-label">
{{ t('catalog.charts.windowsIncompatible') }}
</label>
</div>
Comment thread
nwmac marked this conversation as resolved.
<div
v-if="get(r, sideLabelField)"
class="side-label"
Expand Down
8 changes: 6 additions & 2 deletions shell/mixins/chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { CAPI, CATALOG } from '@shell/config/types';
import { isPrerelease } from '@shell/utils/version';
import { compareChartVersions } from '@shell/utils/chart';
import difference from 'lodash/difference';
import { LINUX, APP_UPGRADE_STATUS } from '@shell/store/catalog';
import { LINUX, APP_UPGRADE_STATUS, isRancherRepo } from '@shell/store/catalog';
import { clone } from '@shell/utils/object';
import { merge } from 'lodash';

Expand Down Expand Up @@ -73,7 +73,11 @@ export default {
keywords: version.keywords
};

const permittedSystems = (version?.annotations?.[CATALOG_ANNOTATIONS.PERMITTED_OS] || LINUX).split(',');
const permittedOs = version?.annotations?.[CATALOG_ANNOTATIONS.PERMITTED_OS];
Comment thread
richard-cox marked this conversation as resolved.
Outdated

const isRancher = isRancherRepo(this.repo, this.chart);
const fallbackOs = isRancher ? LINUX : '';
const permittedSystems = (permittedOs || fallbackOs).split(',').filter(Boolean);

if (permittedSystems.length > 0 && difference(OSs, permittedSystems).length > 0) {
nue.disabled = true;
Expand Down
23 changes: 16 additions & 7 deletions shell/pages/c/_cluster/apps/charts/install.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import {
import { ignoreVariables } from './install.helpers';
import { findBy, insertAt } from '@shell/utils/array';
import { saferDump } from '@shell/utils/create-yaml';
import { LINUX, WINDOWS } from '@shell/store/catalog';
import { WINDOWS, isRancherRepo } from '@shell/store/catalog';
import { SETTING } from '@shell/config/settings';
import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret.vue';
import { generateRandomAlphaString } from '@shell/utils/string';
Expand Down Expand Up @@ -763,14 +763,23 @@ export default {
},

windowsIncompatible() {
if (this.chart?.windowsIncompatible) {
return this.t('catalog.charts.windowsIncompatible');
}
if (this.versionInfo) {
const incompatibleVersion = !(this.versionInfo?.chart?.annotations?.[CATALOG_ANNOTATIONS.PERMITTED_OS] || LINUX).includes('windows');
const isRancher = isRancherRepo(this.repo, this.chart);
Comment thread
momesgin marked this conversation as resolved.
const permittedOs = this.versionInfo?.chart?.annotations?.[CATALOG_ANNOTATIONS.PERMITTED_OS];
let incompatibleVersion = false;

if (permittedOs) {
incompatibleVersion = !permittedOs.includes('windows');
} else if (isRancher) {
incompatibleVersion = true;
}

if (incompatibleVersion) {
if (!this.chart?.windowsIncompatible) {
return this.t('catalog.charts.versionWindowsIncompatible');
}

if (incompatibleVersion && !this.chart.windowsIncompatible) {
return this.t('catalog.charts.versionWindowsIncompatible');
return this.t('catalog.charts.windowsIncompatible');
}
}

Expand Down
97 changes: 96 additions & 1 deletion shell/store/__tests__/catalog.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CATALOG } from '@shell/config/types';
import {
state, getters, actions, mutations, filterAndArrangeCharts
state, getters, actions, mutations, filterAndArrangeCharts, isRancherRepo, compatibleVersionsFor
} from '../catalog';
import { createStore } from 'vuex';

Expand Down Expand Up @@ -455,4 +455,99 @@ describe('catalog', () => {
});
});
});

describe('isRancherRepo', () => {
it('should return true if chart.isRancherRepo is true', () => {
const chart = { isRancherRepo: true } as any;

expect(isRancherRepo(null, chart)).toBe(true);
});

it('should return true if repo.isRancherSource is true', () => {
const repo = { isRancherSource: true } as any;

expect(isRancherRepo(repo, null)).toBe(true);
});

it('should return true if repo is airgapped rancher-charts', () => {
const repo = { type: CATALOG.CLUSTER_REPO, name: 'rancher-charts' } as any;

expect(isRancherRepo(repo, null)).toBe(true);
});

it('should return true if repo is airgapped rancher-partner-charts', () => {
const repo = { type: CATALOG.CLUSTER_REPO, name: 'rancher-partner-charts' } as any;

expect(isRancherRepo(repo, null)).toBe(true);
});

it('should return false if repo is a namespace-scoped rancher-charts', () => {
const repo = { type: CATALOG.REPO, name: 'rancher-charts' } as any;

expect(isRancherRepo(repo, null)).toBe(false);
});

it('should return false for unrelated repo', () => {
const repo = { type: CATALOG.CLUSTER_REPO, name: 'other-repo' } as any;

expect(isRancherRepo(repo, null)).toBe(false);
});
});

describe('compatibleVersionsFor', () => {
it('should allow versions if no OS constraint is provided', () => {
const chart = { versions: [{ version: '1.0.0' }] } as any;
const versions = compatibleVersionsFor(chart, undefined);

expect(versions).toHaveLength(1);
});

it('should allow windows nodes if permitted-os includes windows', () => {
const chart = {
versions: [{
version: '1.0.0',
annotations: { 'catalog.cattle.io/permits-os': 'linux,windows' }
}]
} as any;
const versions = compatibleVersionsFor(chart, 'windows');

expect(versions).toHaveLength(1);
});

it('should block windows nodes if permitted-os does not include windows', () => {
const chart = {
versions: [{
version: '1.0.0',
annotations: { 'catalog.cattle.io/permits-os': 'linux' }
}]
} as any;
const versions = compatibleVersionsFor(chart, 'windows');

expect(versions).toHaveLength(0);
});

it('should fallback to LINUX for rancher repos and block windows nodes', () => {
const rancherRepo = { type: CATALOG.CLUSTER_REPO, name: 'rancher-charts' } as any;
const chart = { versions: [{ version: '1.0.0' }] } as any;

chart.isRancherRepo = (isRancherRepo as any)(rancherRepo, chart);

const versions = compatibleVersionsFor(chart, 'windows');

expect(chart.isRancherRepo).toBe(true);
expect(versions).toHaveLength(0);
});

it('should not fallback to LINUX for non-rancher repos and allow windows nodes', () => {
const nonRancherRepo = { type: CATALOG.REPO, name: 'partner-charts' } as any;
const chart = { versions: [{ version: '1.0.0' }] } as any;

chart.isRancherRepo = (isRancherRepo as any)(nonRancherRepo, chart);

const versions = compatibleVersionsFor(chart, 'windows');

expect(chart.isRancherRepo).toBe(false);
expect(versions).toHaveLength(1);
});
});
});
32 changes: 29 additions & 3 deletions shell/store/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,16 @@ function addChart(ctx, map, chart, repo) {

const primeOnly = chart.annotations?.[CATALOG_ANNOTATIONS.PRIME_ONLY] === 'true';
const experimental = !!chart.annotations?.[CATALOG_ANNOTATIONS.EXPERIMENTAL];
const windowsIncompatible = !(chart.annotations?.[CATALOG_ANNOTATIONS.PERMITTED_OS] || '').includes('windows');

const isRancherRepoFlag = isRancherRepo(repo, chart);
const permittedOs = chart.annotations?.[CATALOG_ANNOTATIONS.PERMITTED_OS];
let windowsIncompatible = false;

if (permittedOs) {
windowsIncompatible = !permittedOs.includes('windows');
} else if (isRancherRepoFlag) {
windowsIncompatible = true;
}
const deploysOnWindows = (chart.annotations?.[CATALOG_ANNOTATIONS.DEPLOYED_OS] || '').includes('windows');
const tags = [];

Expand Down Expand Up @@ -606,6 +615,7 @@ function addChart(ctx, map, chart, repo) {
provides: [],
windowsIncompatible,
deploysOnWindows,
isRancherRepo: isRancherRepoFlag,
tags
});

Expand Down Expand Up @@ -690,13 +700,15 @@ export function compatibleVersionsFor(chart, os, includePrerelease = true) {
}

return versions.filter((ver) => {
const osPermitted = (ver?.annotations?.[CATALOG_ANNOTATIONS.PERMITTED_OS] || LINUX).split(',');
const permittedOs = ver?.annotations?.[CATALOG_ANNOTATIONS.PERMITTED_OS];
const fallbackOs = chart?.isRancherRepo === false ? '' : LINUX;
const osPermitted = (permittedOs || fallbackOs).split(',').filter(Boolean);

if ( !includePrerelease && isPrerelease(ver.version) ) {
return false;
}

if ( !os || difference(os, osPermitted).length === 0) {
if ( !os || osPermitted.length === 0 || difference(os, osPermitted).length === 0) {
return true;
}

Expand Down Expand Up @@ -785,3 +797,17 @@ export function filterAndArrangeCharts(charts, {

return sortBy(out, ['certifiedSort', 'repoName', 'chartNameDisplay']);
}

/*
Detects if a repository is a Rancher repository.
Airgapped environments often use mirrored registries, meaning `isRancherSource`
will return false because the URL doesn't point to a *.rancher.io domain. We check the repo
names directly to ensure we still correctly identify these mirrored rancher repos.
*/
export function isRancherRepo(repo, chart) {
Comment thread
richard-cox marked this conversation as resolved.
if (chart?.isRancherRepo || repo?.isRancherSource) {
return true;
}

return repo?.type === CATALOG.CLUSTER_REPO && (repo?.name === 'rancher-charts' || repo?.name === 'rancher-partner-charts');
Comment thread
richard-cox marked this conversation as resolved.
Outdated
}
Loading