Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 3 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 { APP_UPGRADE_STATUS, isRancherRepo, getPermittedOSs } from '@shell/store/catalog';
import { clone } from '@shell/utils/object';
import { merge } from 'lodash';

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

const permittedSystems = (version?.annotations?.[CATALOG_ANNOTATIONS.PERMITTED_OS] || LINUX).split(',');
const isRancher = isRancherRepo(this.repo, this.chart);
const permittedSystems = getPermittedOSs(version?.annotations, isRancher);

if (permittedSystems.length > 0 && difference(OSs, permittedSystems).length > 0) {
nue.disabled = true;
Expand Down
17 changes: 10 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, getPermittedOSs } 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 @@ -766,14 +766,17 @@ 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);
const permittedSystems = getPermittedOSs(this.versionInfo?.chart?.annotations, isRancher);
const incompatibleVersion = permittedSystems.length > 0 && !permittedSystems.includes('windows');

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
116 changes: 115 additions & 1 deletion shell/store/__tests__/catalog.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CATALOG } from '@shell/config/types';
import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
import {
state, getters, actions, mutations, filterAndArrangeCharts
state, getters, actions, mutations, filterAndArrangeCharts, isRancherRepo, compatibleVersionsFor, getPermittedOSs
} from '../catalog';
import { createStore } from 'vuex';

Expand Down Expand Up @@ -509,4 +509,118 @@ 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 false if repo is not a rancher source', () => {
const repo = { isRancherSource: false } 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 chart = { isRancherRepo: true, versions: [{ version: '1.0.0' }] } as any;
const versions = compatibleVersionsFor(chart, 'windows');

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

it('should not fallback to LINUX for non-rancher repos and allow windows nodes', () => {
const chart = { isRancherRepo: false, versions: [{ version: '1.0.0' }] } as any;
const versions = compatibleVersionsFor(chart, 'windows');

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

describe('getPermittedOSs', () => {
it('should return explicitly permitted OSs when the annotation is present on a Rancher repo', () => {
const annotations = { [CATALOG_ANNOTATIONS.PERMITTED_OS]: 'linux,windows' };
const result = getPermittedOSs(annotations, true);

expect(result).toHaveLength(2);
expect(result).toContain('linux');
expect(result).toContain('windows');
});

it('should return explicitly permitted OSs when the annotation is present on a non-Rancher repo', () => {
const annotations = { [CATALOG_ANNOTATIONS.PERMITTED_OS]: 'linux' };
const result = getPermittedOSs(annotations, false);

expect(result).toHaveLength(1);
expect(result).toContain('linux');
});

it('should fallback to linux if the annotation is missing on a Rancher repo', () => {
const annotations = {};
const result = getPermittedOSs(annotations, true);

expect(result).toHaveLength(1);
expect(result).toContain('linux');
});

it('should return an empty array (no restrictions) if the annotation is missing on a non-Rancher repo', () => {
const annotations = {};
const result = getPermittedOSs(annotations, false);

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

it('should handle undefined annotations safely for Rancher repos', () => {
const result = getPermittedOSs(undefined, true);

expect(result).toHaveLength(1);
expect(result).toContain('linux');
});

it('should handle undefined annotations safely for non-Rancher repos', () => {
const result = getPermittedOSs(undefined, false);

expect(result).toHaveLength(0);
});
});
});
30 changes: 27 additions & 3 deletions shell/store/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,10 @@ 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 permittedSystems = getPermittedOSs(chart.annotations, isRancherRepoFlag);
const windowsIncompatible = permittedSystems.length > 0 && !permittedSystems.includes('windows');
const deploysOnWindows = (chart.annotations?.[CATALOG_ANNOTATIONS.DEPLOYED_OS] || '').includes('windows');
const tags = [];

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

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

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

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 @@ -787,3 +791,23 @@ export function filterAndArrangeCharts(charts, {

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

/**
* Detects if a repository is a Rancher repository.
*/
export function isRancherRepo(repo, chart) {
Comment thread
richard-cox marked this conversation as resolved.
return !!(chart?.isRancherRepo || repo?.isRancherSource);
}

/**
* Returns an array of permitted operating systems for a given chart or version.
* If the chart explicitly defines permitted OSs via annotation, those are returned.
* Otherwise, if the chart is from a Rancher repository, it defaults to Linux.
* External charts with no annotations have no OS restrictions (returns empty array).
*/
export function getPermittedOSs(annotations, isRancher) {
const permittedOs = annotations?.[CATALOG_ANNOTATIONS.PERMITTED_OS];
const fallbackOs = isRancher ? LINUX : '';

return (permittedOs || fallbackOs).split(',').filter(Boolean);
Comment thread
richard-cox marked this conversation as resolved.
}
Loading