diff --git a/shell/assets/translations/en-us.yaml b/shell/assets/translations/en-us.yaml
index 23bae1d138a..b02660dd08e 100644
--- a/shell/assets/translations/en-us.yaml
+++ b/shell/assets/translations/en-us.yaml
@@ -888,6 +888,13 @@ catalog:
appReadmeMissing: This chart doesn't have any additional chart information.
appReadmeTitle: Chart Information (Helm README)
chart: Chart
+ warning:
+ managed:
+ Warning, Rancher manages deployment and upgrade of the {name} app. Upgrading this app is not supported.
+ {version, select,
+ null { }
+ other { Under most circumstances, no user intervention should be needed to ensure that the {version} version is compatible with the version of Rancher that you are running.}
+ }
error:
requiresFound: '{name} must be installed before you can install this chart.'
requiresMissing: 'This chart requires another chart that provides {name}, but none was was found.'
@@ -981,6 +988,9 @@ catalog:
current: '{ver} (Current)'
linux: '{ver} (Linux-only)'
windows: '{ver} (Windows-only)'
+ delete:
+ warning:
+ managed: Warning, Rancher manages deployment and upgrade of the {name} app. Deleting this app is not supported.
operation:
tableHeaders:
action: Action
diff --git a/shell/mixins/__tests__/chart.test.ts b/shell/mixins/__tests__/chart.test.ts
index 3699a2eae11..a9d8a8139e7 100644
--- a/shell/mixins/__tests__/chart.test.ts
+++ b/shell/mixins/__tests__/chart.test.ts
@@ -4,15 +4,27 @@ import ChartMixin from '@shell/mixins/chart';
import { OPA_GATE_KEEPER_ID } from '@shell/pages/c/_cluster/gatekeeper/index.vue';
describe('chartMixin', () => {
- const testCases = [[null, 0], [OPA_GATE_KEEPER_ID, 1], ['any_other_id', 0]];
+ const testCases = {
+ opa: [
+ [null, 0],
+ [OPA_GATE_KEEPER_ID, 1],
+ ['any_other_id', 0]
+ ],
+ managedApps: [
+ [false, false, 0],
+ [true, null, 0],
+ [true, true, 0],
+ [true, false, 1],
+ ],
+ };
- it.each(testCases)(
- 'should add OPA deprecation warning properly', (chartId, expected) => {
- const localVue = createLocalVue();
+ const localVue = createLocalVue();
- localVue.use(Vuex);
- localVue.mixin(ChartMixin);
+ localVue.use(Vuex);
+ localVue.mixin(ChartMixin);
+ it.each(testCases.opa)(
+ 'should add OPA deprecation warning properly', (chartId, expected) => {
const store = new Vuex.Store({
getters: {
currentCluster: () => {},
@@ -37,4 +49,34 @@ describe('chartMixin', () => {
expect(warnings).toHaveLength(expected);
}
);
+
+ it.each(testCases.managedApps)(
+ 'should add managed apps warning properly', (isEdit, upgradeAvailable, expected) => {
+ const id = 'cattle-fleet-local-system/fleet-agent-local';
+ const data = isEdit ? { existing: { id, upgradeAvailable } } : undefined;
+
+ const store = new Vuex.Store({
+ getters: {
+ currentCluster: () => {},
+ isRancher: () => true,
+ 'catalog/repo': () => {
+ return () => 'repo';
+ },
+ 'catalog/chart': () => {
+ return () => ({ id });
+ },
+ 'i18n/t': () => jest.fn()
+ }
+ });
+
+ const vm = localVue.extend({});
+ const instance = new vm({ store, data });
+
+ instance.$route = { query: { chart: 'chart_name' } };
+
+ const warnings = instance.warnings;
+
+ expect(warnings).toHaveLength(expected as number);
+ }
+ );
});
diff --git a/shell/mixins/chart.js b/shell/mixins/chart.js
index 78d9e19001a..9803cad350b 100644
--- a/shell/mixins/chart.js
+++ b/shell/mixins/chart.js
@@ -190,6 +190,13 @@ export default {
warnings.unshift(this.t('gatekeeperIndex.deprecated', {}, true));
}
+ if (this.existing && this.existing.upgradeAvailable === false) {
+ warnings.unshift(this.t('catalog.install.warning.managed', {
+ name: this.existing.name,
+ version: this.chart ? this.query.versionName : null
+ }, true));
+ }
+
return warnings;
},
diff --git a/shell/models/catalog.cattle.io.app.js b/shell/models/catalog.cattle.io.app.js
index cf423bba1af..233a1e6153b 100644
--- a/shell/models/catalog.cattle.io.app.js
+++ b/shell/models/catalog.cattle.io.app.js
@@ -39,6 +39,14 @@ export default class CatalogApp extends SteveModel {
return out;
}
+ get warnDeletionMessage() {
+ if (this.upgradeAvailable === false) {
+ return this.t('catalog.delete.warning.managed', { name: this.name });
+ }
+
+ return null;
+ }
+
matchingChart(includeHidden) {
const chart = this.spec?.chart;