@rancher/shell 3.0.7 → 3.0.8-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/images/vendor/githubapp.svg +13 -0
- package/assets/styles/base/_typography.scss +1 -1
- package/assets/styles/themes/_modern.scss +5 -5
- package/assets/translations/en-us.yaml +91 -11
- package/assets/translations/zh-hans.yaml +0 -4
- package/components/Inactivity.vue +222 -106
- package/components/InstallHelmCharts.vue +2 -2
- package/components/ResourceDetail/index.vue +1 -1
- package/components/SortableTable/index.vue +17 -2
- package/components/fleet/FleetConfigMapSelector.vue +117 -0
- package/components/fleet/FleetSecretSelector.vue +127 -0
- package/components/fleet/__tests__/FleetConfigMapSelector.test.ts +125 -0
- package/components/fleet/__tests__/FleetSecretSelector.test.ts +82 -0
- package/components/form/FileImageSelector.vue +13 -4
- package/components/form/FileSelector.vue +11 -2
- package/components/form/ResourceLabeledSelect.vue +1 -0
- package/components/form/__tests__/ResourceLabeledSelect.test.ts +90 -0
- package/components/nav/Header.vue +1 -0
- package/config/product/auth.js +1 -0
- package/config/query-params.js +1 -0
- package/config/settings.ts +8 -1
- package/config/types.js +2 -0
- package/dialog/AddonConfigConfirmationDialog.vue +45 -1
- package/edit/__tests__/fleet.cattle.io.helmop.test.ts +52 -11
- package/edit/auth/AuthProviderWarningBanners.vue +14 -1
- package/edit/auth/github-app-steps.vue +97 -0
- package/edit/auth/github-steps.vue +75 -0
- package/edit/auth/github.vue +94 -65
- package/edit/fleet.cattle.io.helmop.vue +51 -2
- package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +15 -5
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +11 -9
- package/edit/provisioning.cattle.io.cluster/rke2.vue +56 -9
- package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +28 -2
- package/list/projectsecret.vue +1 -1
- package/machine-config/azure.vue +1 -1
- package/mixins/chart.js +1 -1
- package/models/__tests__/chart.test.ts +17 -9
- package/models/__tests__/compliance.cattle.io.clusterscanprofile.spec.js +30 -0
- package/models/catalog.cattle.io.app.js +1 -1
- package/models/chart.js +3 -1
- package/models/compliance.cattle.io.clusterscanprofile.js +1 -1
- package/models/management.cattle.io.authconfig.js +1 -0
- package/package.json +2 -2
- package/pages/auth/login.vue +5 -2
- package/pages/auth/verify.vue +1 -1
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +3 -2
- package/pages/c/_cluster/apps/charts/chart.vue +2 -2
- package/pages/c/_cluster/explorer/EventsTable.vue +89 -3
- package/pages/c/_cluster/explorer/tools/index.vue +3 -3
- package/pages/c/_cluster/settings/performance.vue +12 -25
- package/pages/home.vue +313 -12
- package/plugins/axios.js +2 -1
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/resource-class.js +17 -2
- package/plugins/steve/steve-pagination-utils.ts +2 -2
- package/scripts/extension/publish +1 -1
- package/store/auth.js +8 -3
- package/store/aws.js +8 -6
- package/store/features.js +1 -0
- package/store/index.js +9 -3
- package/store/prefs.js +6 -0
- package/types/kube/kube-api.ts +2 -1
- package/types/rancher/index.d.ts +1 -0
- package/types/resources/settings.d.ts +29 -7
- package/types/shell/index.d.ts +59 -0
- package/utils/__tests__/cluster.test.ts +379 -1
- package/utils/cluster.js +157 -3
- package/utils/dynamic-content/__tests__/config.test.ts +187 -0
- package/utils/dynamic-content/__tests__/index.test.ts +390 -0
- package/utils/dynamic-content/__tests__/info.test.ts +263 -0
- package/utils/dynamic-content/__tests__/new-release.test.ts +216 -0
- package/utils/dynamic-content/__tests__/support-notice.test.ts +262 -0
- package/utils/dynamic-content/__tests__/util.test.ts +235 -0
- package/utils/dynamic-content/config.ts +55 -0
- package/utils/dynamic-content/index.ts +273 -0
- package/utils/dynamic-content/info.ts +219 -0
- package/utils/dynamic-content/new-release.ts +126 -0
- package/utils/dynamic-content/support-notice.ts +169 -0
- package/utils/dynamic-content/types.d.ts +101 -0
- package/utils/dynamic-content/util.ts +122 -0
- package/utils/inactivity.ts +104 -0
- package/utils/pagination-utils.ts +19 -4
- package/utils/release-notes.ts +1 -1
package/store/prefs.js
CHANGED
|
@@ -114,6 +114,12 @@ export const PROVISIONER = create('provisioner', _RKE2, { options: [_RKE1, _RKE2
|
|
|
114
114
|
export const MENU_MAX_CLUSTERS = 10;
|
|
115
115
|
// Prompt for confirm when scaling down node pool in GUI and save the pref
|
|
116
116
|
export const SCALE_POOL_PROMPT = create('scale-pool-prompt', null, { parseJSON });
|
|
117
|
+
|
|
118
|
+
// Dynamic content
|
|
119
|
+
export const READ_NEW_RELEASE = create('read-new-release', '', { parseJSON });
|
|
120
|
+
export const READ_SUPPORT_NOTICE = create('read-support-notice', '', { parseJSON });
|
|
121
|
+
export const READ_UPCOMING_SUPPORT_NOTICE = create('read-upcoming-support-notice', '', { parseJSON });
|
|
122
|
+
|
|
117
123
|
// --------------------
|
|
118
124
|
|
|
119
125
|
const cookiePrefix = 'R_';
|
package/types/kube/kube-api.ts
CHANGED
package/types/rancher/index.d.ts
CHANGED
|
@@ -20,14 +20,39 @@ export interface PaginationSettingsStore {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
/*
|
|
24
24
|
* Determine which resources can utilise server-side pagination
|
|
25
25
|
*/
|
|
26
26
|
export interface PaginationSettingsStores {
|
|
27
27
|
[store: string]: PaginationSettingsStore
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Names of pagination features used in pagination settings (not featureflags)
|
|
32
|
+
*/
|
|
33
|
+
export type PaginationFeatureName = 'listAutoRefreshToggle' | 'listManualRefresh' | 'homePageCluster'
|
|
34
|
+
|
|
35
|
+
export type PaginationFeatureHomePageClusterConfig = {
|
|
36
|
+
threshold: number,
|
|
37
|
+
results: number,
|
|
38
|
+
pagesPerRow: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Details of a specific pagination feature
|
|
43
|
+
*/
|
|
44
|
+
export type PaginationFeature<Config = any> = {
|
|
45
|
+
version: number,
|
|
46
|
+
enabled: boolean,
|
|
47
|
+
configuration?: Config,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* List of specific features that can be enabled / disabled
|
|
52
|
+
*/
|
|
53
|
+
export type PaginationSettingsFeatures = {
|
|
54
|
+
[key in PaginationFeatureName]?: PaginationFeature
|
|
55
|
+
}
|
|
31
56
|
|
|
32
57
|
/**
|
|
33
58
|
* Settings to handle server side pagination
|
|
@@ -45,11 +70,7 @@ export interface PaginationSettings {
|
|
|
45
70
|
/**
|
|
46
71
|
* List of specific features that can be enabled / disabled
|
|
47
72
|
*/
|
|
48
|
-
features?:
|
|
49
|
-
[key in PaginationFeature]: {
|
|
50
|
-
enabled: boolean,
|
|
51
|
-
}
|
|
52
|
-
},
|
|
73
|
+
features?: PaginationSettingsFeatures,
|
|
53
74
|
|
|
54
75
|
/**
|
|
55
76
|
* Debounce the amount of time between a resource changing and the backend sending a resource.changes message
|
|
@@ -82,6 +103,7 @@ type ManagedFields = {
|
|
|
82
103
|
time: string;
|
|
83
104
|
};
|
|
84
105
|
|
|
106
|
+
// Note - this should now be @RancherKubeMetadata
|
|
85
107
|
type Metadata = {
|
|
86
108
|
creationTimestamp: string;
|
|
87
109
|
fields: string[];
|
package/types/shell/index.d.ts
CHANGED
|
@@ -174,6 +174,7 @@ export const STEP: "step";
|
|
|
174
174
|
export const LOGGED_OUT: "logged-out";
|
|
175
175
|
export const IS_SSO: "is-sso";
|
|
176
176
|
export const IS_SLO: "is-slo";
|
|
177
|
+
export const IS_SESSION_IDLE: "is-session-idle";
|
|
177
178
|
export const UPGRADED: "upgraded";
|
|
178
179
|
export const TIMED_OUT: "timed-out";
|
|
179
180
|
export const AUTH_TEST: "test";
|
|
@@ -2190,6 +2191,9 @@ export namespace MANAGEMENT {
|
|
|
2190
2191
|
export let CLUSTER_PROXY_CONFIG: string;
|
|
2191
2192
|
export let OIDC_CLIENT: string;
|
|
2192
2193
|
}
|
|
2194
|
+
export namespace EXT {
|
|
2195
|
+
let USER_ACTIVITY: string;
|
|
2196
|
+
}
|
|
2193
2197
|
export namespace CAPI {
|
|
2194
2198
|
let CAPI_CLUSTER: string;
|
|
2195
2199
|
let MACHINE_DEPLOYMENT: string;
|
|
@@ -3671,6 +3675,7 @@ export const STEVE_CACHE: any;
|
|
|
3671
3675
|
export const UIEXTENSION: any;
|
|
3672
3676
|
export const PROVISIONING_PRE_BOOTSTRAP: any;
|
|
3673
3677
|
export const SCHEDULING_CUSTOMIZATION: any;
|
|
3678
|
+
export const SCC: any;
|
|
3674
3679
|
export namespace getters {
|
|
3675
3680
|
function get(state: any, getters: any, rootState: any, rootGetters: any): (name: any) => any;
|
|
3676
3681
|
}
|
|
@@ -3754,6 +3759,9 @@ export const _RKE2: "rke2";
|
|
|
3754
3759
|
export const PROVISIONER: any;
|
|
3755
3760
|
export const MENU_MAX_CLUSTERS: 10;
|
|
3756
3761
|
export const SCALE_POOL_PROMPT: any;
|
|
3762
|
+
export const READ_NEW_RELEASE: any;
|
|
3763
|
+
export const READ_SUPPORT_NOTICE: any;
|
|
3764
|
+
export const READ_UPCOMING_SUPPORT_NOTICE: any;
|
|
3757
3765
|
export function state(): {
|
|
3758
3766
|
cookiesLoaded: boolean;
|
|
3759
3767
|
data: {};
|
|
@@ -3980,6 +3988,57 @@ export function initSchedulingCustomization(value: any, features: any, store: an
|
|
|
3980
3988
|
schedulingCustomizationOriginallyEnabled: boolean;
|
|
3981
3989
|
errors: any[];
|
|
3982
3990
|
}>;
|
|
3991
|
+
/**
|
|
3992
|
+
* Recursively filters a `diffs` object to only include differences that are relevant to the user.
|
|
3993
|
+
* A difference is considered relevant if the user has provided a custom value for that specific field.
|
|
3994
|
+
*
|
|
3995
|
+
* @param {object} diffs - The object representing the differences between two chart versions' default values.
|
|
3996
|
+
* @param {object} userVals - The object containing the user's custom values.
|
|
3997
|
+
* @returns {object} A new object containing only the relevant differences.
|
|
3998
|
+
*/
|
|
3999
|
+
export function _addonConfigPreserveFilter(diffs: object, userVals: object): object;
|
|
4000
|
+
/**
|
|
4001
|
+
* @typedef {object} AddonPreserveContext
|
|
4002
|
+
* @property {object} addonConfigDiffs - An object that stores the diffs.
|
|
4003
|
+
* @property {string[]} addonNames - An array of addon names to check.
|
|
4004
|
+
* @property {object} $store - The Vuex store.
|
|
4005
|
+
* @property {object} userChartValues - The user's customized chart values.
|
|
4006
|
+
*
|
|
4007
|
+
* When the Kubernetes version is changed, this method is called to handle the add-on configurations
|
|
4008
|
+
* for all enabled addons. It checks if an addon's version has changed and, if so, determines if the
|
|
4009
|
+
* user's custom configurations should be preserved for the new version.
|
|
4010
|
+
*
|
|
4011
|
+
* The goal is to avoid showing a confirmation dialog for changes in default values that the user has not customized.
|
|
4012
|
+
*
|
|
4013
|
+
* @param {AddonPreserveContext} context The context object from the component.
|
|
4014
|
+
* @param {object} oldCharts The charts object from the K8s release object being changed from.
|
|
4015
|
+
* @param {object} newCharts The charts object from the K8s release object being changed to.
|
|
4016
|
+
*/
|
|
4017
|
+
export function addonConfigPreserve(context: AddonPreserveContext, oldCharts: object, newCharts: object): Promise<void>;
|
|
4018
|
+
export type AddonPreserveContext = {
|
|
4019
|
+
/**
|
|
4020
|
+
* - An object that stores the diffs.
|
|
4021
|
+
*/
|
|
4022
|
+
addonConfigDiffs: object;
|
|
4023
|
+
/**
|
|
4024
|
+
* - An array of addon names to check.
|
|
4025
|
+
*/
|
|
4026
|
+
addonNames: string[];
|
|
4027
|
+
/**
|
|
4028
|
+
* - The Vuex store.
|
|
4029
|
+
*/
|
|
4030
|
+
$store: object;
|
|
4031
|
+
/**
|
|
4032
|
+
* - The user's customized chart values.
|
|
4033
|
+
*
|
|
4034
|
+
* When the Kubernetes version is changed, this method is called to handle the add-on configurations
|
|
4035
|
+
* for all enabled addons. It checks if an addon's version has changed and, if so, determines if the
|
|
4036
|
+
* user's custom configurations should be preserved for the new version.
|
|
4037
|
+
*
|
|
4038
|
+
* The goal is to avoid showing a confirmation dialog for changes in default values that the user has not customized.
|
|
4039
|
+
*/
|
|
4040
|
+
userChartValues: object;
|
|
4041
|
+
};
|
|
3983
4042
|
}
|
|
3984
4043
|
|
|
3985
4044
|
// @shell/utils/color
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { abbreviateClusterName } from '@shell/utils/cluster';
|
|
1
|
+
import { abbreviateClusterName, _addonConfigPreserveFilter, addonConfigPreserve } from '@shell/utils/cluster';
|
|
2
|
+
import { diff } from '@shell/utils/object';
|
|
2
3
|
|
|
3
4
|
describe('fx: abbreviateClusterName', () => {
|
|
4
5
|
it.each([
|
|
@@ -55,3 +56,380 @@ describe('fx: abbreviateClusterName', () => {
|
|
|
55
56
|
expect(result).toStrictEqual(expected);
|
|
56
57
|
});
|
|
57
58
|
});
|
|
59
|
+
|
|
60
|
+
describe('fx: _addonConfigPreserveFilter', () => {
|
|
61
|
+
it('should return an empty object if there are no differences between default values', () => {
|
|
62
|
+
const oldDefaults = { replicaCount: 1 };
|
|
63
|
+
const newDefaults = { replicaCount: 1 };
|
|
64
|
+
const userVals = { replicaCount: 3 };
|
|
65
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
66
|
+
const expected = {};
|
|
67
|
+
|
|
68
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should return an empty object if no user values are provided', () => {
|
|
72
|
+
const oldDefaults = { replicaCount: 1 };
|
|
73
|
+
const newDefaults = { replicaCount: 2 };
|
|
74
|
+
const userVals = {};
|
|
75
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
76
|
+
const expected = {};
|
|
77
|
+
|
|
78
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should filter out diffs for fields not customized by the user', () => {
|
|
82
|
+
const oldDefaults = {
|
|
83
|
+
replicaCount: 1,
|
|
84
|
+
persistence: false
|
|
85
|
+
};
|
|
86
|
+
const newDefaults = {
|
|
87
|
+
replicaCount: 2,
|
|
88
|
+
persistence: true
|
|
89
|
+
};
|
|
90
|
+
const userVals = { replicaCount: 3 };
|
|
91
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
92
|
+
const expected = { replicaCount: 2 };
|
|
93
|
+
|
|
94
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should include diffs for fields customized by the user', () => {
|
|
98
|
+
const oldDefaults = { replicaCount: 1 };
|
|
99
|
+
const newDefaults = { replicaCount: 2 };
|
|
100
|
+
const userVals = { replicaCount: 3 };
|
|
101
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
102
|
+
const expected = { replicaCount: 2 };
|
|
103
|
+
|
|
104
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should handle nested objects: include diff if user customized nested property', () => {
|
|
108
|
+
const oldDefaults = { service: { port: 80 } };
|
|
109
|
+
const newDefaults = { service: { port: 8080 } };
|
|
110
|
+
const userVals = { service: { port: 9000 } };
|
|
111
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
112
|
+
const expected = { service: { port: 8080 } };
|
|
113
|
+
|
|
114
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should handle nested objects: exclude diff if user did not customize nested property', () => {
|
|
118
|
+
const oldDefaults = { service: { port: 80, type: 'ClusterIP' } };
|
|
119
|
+
const newDefaults = { service: { port: 8080, type: 'ClusterIP' } };
|
|
120
|
+
const userVals = { service: { type: 'NodePort' } };
|
|
121
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
122
|
+
const expected = {};
|
|
123
|
+
|
|
124
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should handle nested objects: include diff if user customized a removed nested object', () => {
|
|
128
|
+
const oldDefaults = { service: { port: 80 } };
|
|
129
|
+
const newDefaults = {};
|
|
130
|
+
const userVals = { service: { port: 9000 } };
|
|
131
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
132
|
+
const expected = { service: { port: null } };
|
|
133
|
+
|
|
134
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should handle nested objects: exclude diff if a new property is added to default nested object and user did not customize it', () => {
|
|
138
|
+
const oldDefaults = { service: { port: 80 } };
|
|
139
|
+
const newDefaults = { service: { port: 80, type: 'ClusterIP' } };
|
|
140
|
+
const userVals = { service: { port: 80 } };
|
|
141
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
142
|
+
const expected = {};
|
|
143
|
+
|
|
144
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should handle arrays of primitives: include diff if user customized array and default array changed', () => {
|
|
148
|
+
const oldDefaults = { ingress: { hosts: ['host1.com', 'host2.com'] } };
|
|
149
|
+
const newDefaults = { ingress: { hosts: ['host1.com', 'host3.com'] } };
|
|
150
|
+
const userVals = { ingress: { hosts: ['user.host.com'] } };
|
|
151
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
152
|
+
const expected = { ingress: { hosts: ['host1.com', 'host3.com'] } };
|
|
153
|
+
|
|
154
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should handle arrays of primitives: exclude diff if user did not customize array', () => {
|
|
158
|
+
const oldDefaults = { ingress: { hosts: ['host1.com', 'host2.com'] } };
|
|
159
|
+
const newDefaults = { ingress: { hosts: ['host1.com', 'host3.com'] } };
|
|
160
|
+
const userVals = { ingress: { enabled: true } };
|
|
161
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
162
|
+
const expected = {};
|
|
163
|
+
|
|
164
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should handle arrays of objects: include diff if user customized array and default array changed', () => {
|
|
168
|
+
const oldDefaults = { service: { ports: [{ name: 'http', port: 80 }] } };
|
|
169
|
+
const newDefaults = { service: { ports: [{ name: 'http', port: 80 }, { name: 'https', port: 443 }] } };
|
|
170
|
+
const userVals = { service: { ports: [{ name: 'http', port: 8080 }] } };
|
|
171
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
172
|
+
const expected = { service: { ports: [{ name: 'http', port: 80 }, { name: 'https', port: 443 }] } };
|
|
173
|
+
|
|
174
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should handle arrays of objects: exclude diff if user did not customize array', () => {
|
|
178
|
+
const oldDefaults = { service: { ports: [{ name: 'http', port: 80 }] } };
|
|
179
|
+
const newDefaults = { service: { ports: [{ name: 'http', port: 80 }, { name: 'https', port: 443 }] } };
|
|
180
|
+
const userVals = { service: { type: 'ClusterIP' } };
|
|
181
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
182
|
+
const expected = {};
|
|
183
|
+
|
|
184
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should handle properties added/removed: include diff if user customized a removed property', () => {
|
|
188
|
+
const oldDefaults = { oldProperty: 'defaultValue' };
|
|
189
|
+
const newDefaults = {};
|
|
190
|
+
const userVals = { oldProperty: 'customValue' };
|
|
191
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
192
|
+
const expected = { oldProperty: null };
|
|
193
|
+
|
|
194
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should handle properties added/removed: exclude diff if user did not customize an added property', () => {
|
|
198
|
+
const oldDefaults = {};
|
|
199
|
+
const newDefaults = { newProperty: 'defaultValue' };
|
|
200
|
+
const userVals = { otherProp: 'value' };
|
|
201
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
202
|
+
const expected = {};
|
|
203
|
+
|
|
204
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('should handle complex nested structures with multiple changes', () => {
|
|
208
|
+
const oldDefaults = {
|
|
209
|
+
replicaCount: 1,
|
|
210
|
+
image: {
|
|
211
|
+
repository: 'nginx',
|
|
212
|
+
tag: 'stable'
|
|
213
|
+
},
|
|
214
|
+
service: {
|
|
215
|
+
type: 'ClusterIP',
|
|
216
|
+
port: 80
|
|
217
|
+
},
|
|
218
|
+
ingress: {
|
|
219
|
+
enabled: false,
|
|
220
|
+
hosts: ['chart-example.local']
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
const newDefaults = {
|
|
224
|
+
replicaCount: 2,
|
|
225
|
+
image: {
|
|
226
|
+
repository: 'nginx',
|
|
227
|
+
tag: 'mainline'
|
|
228
|
+
},
|
|
229
|
+
service: {
|
|
230
|
+
type: 'ClusterIP',
|
|
231
|
+
port: 80
|
|
232
|
+
},
|
|
233
|
+
ingress: {
|
|
234
|
+
enabled: true,
|
|
235
|
+
hosts: ['chart-example.local', 'new.chart-example.local'],
|
|
236
|
+
tls: true
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
const userVals = {
|
|
240
|
+
replicaCount: 3,
|
|
241
|
+
ingress: { hosts: ['my.custom.host'] }
|
|
242
|
+
};
|
|
243
|
+
const diffs = diff(oldDefaults, newDefaults);
|
|
244
|
+
const expected = {
|
|
245
|
+
replicaCount: 2,
|
|
246
|
+
ingress: { hosts: ['chart-example.local', 'new.chart-example.local'] }
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
expect(_addonConfigPreserveFilter(diffs, userVals)).toStrictEqual(expected);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describe('fx: addonConfigPreserve', () => {
|
|
254
|
+
const ADDON_NAME = 'rke2-my-addon';
|
|
255
|
+
const mockOldVersionCharts = {
|
|
256
|
+
[ADDON_NAME]: {
|
|
257
|
+
repo: 'repo',
|
|
258
|
+
version: '1.0.0'
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const mockNewVersionCharts = {
|
|
263
|
+
[ADDON_NAME]: {
|
|
264
|
+
repo: 'repo',
|
|
265
|
+
version: '1.1.0'
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const mockOldVersionInfo = {
|
|
270
|
+
values: {
|
|
271
|
+
replicas: 1,
|
|
272
|
+
service: { type: 'ClusterIP' }
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const mockNewVersionInfo = {
|
|
277
|
+
values: {
|
|
278
|
+
replicas: 2, // changed
|
|
279
|
+
service: { type: 'ClusterIP' },
|
|
280
|
+
persistence: true // new
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
let mockStore: any;
|
|
285
|
+
let addonConfigDiffs: any;
|
|
286
|
+
let userChartValues: any;
|
|
287
|
+
let context: any;
|
|
288
|
+
|
|
289
|
+
beforeEach(() => {
|
|
290
|
+
mockStore = {
|
|
291
|
+
dispatch: jest.fn((action, payload) => {
|
|
292
|
+
if (action === 'catalog/getVersionInfo') {
|
|
293
|
+
if (payload.versionName === '1.0.0') {
|
|
294
|
+
return Promise.resolve(mockOldVersionInfo);
|
|
295
|
+
}
|
|
296
|
+
if (payload.versionName === '1.1.0') {
|
|
297
|
+
return Promise.resolve(mockNewVersionInfo);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return Promise.resolve({});
|
|
302
|
+
})
|
|
303
|
+
};
|
|
304
|
+
addonConfigDiffs = {};
|
|
305
|
+
userChartValues = {};
|
|
306
|
+
context = {
|
|
307
|
+
addonConfigDiffs,
|
|
308
|
+
addonNames: [ADDON_NAME],
|
|
309
|
+
$store: mockStore,
|
|
310
|
+
userChartValues,
|
|
311
|
+
};
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should identify no relevant changes if user has no custom values', async() => {
|
|
315
|
+
// No user overrides means no relevant differences.
|
|
316
|
+
context.userChartValues = {};
|
|
317
|
+
await addonConfigPreserve(context, mockOldVersionCharts, mockNewVersionCharts);
|
|
318
|
+
|
|
319
|
+
expect(context.addonConfigDiffs[ADDON_NAME]).toStrictEqual({});
|
|
320
|
+
expect(context.userChartValues[`${ ADDON_NAME }-1.1.0`]).toBeUndefined();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should identify no relevant changes if user custom values are for different fields', async() => {
|
|
324
|
+
// User overrides for non-differing fields should not be considered relevant.
|
|
325
|
+
context.userChartValues = { [`${ ADDON_NAME }-1.0.0`]: { service: { type: 'NodePort' } } };
|
|
326
|
+
await addonConfigPreserve(context, mockOldVersionCharts, mockNewVersionCharts);
|
|
327
|
+
|
|
328
|
+
expect(context.addonConfigDiffs[ADDON_NAME]).toStrictEqual({});
|
|
329
|
+
// Since diffs are empty, user values should be preserved
|
|
330
|
+
expect(context.userChartValues[`${ ADDON_NAME }-1.1.0`]).toStrictEqual({ service: { type: 'NodePort' } });
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('should identify relevant changes if user has customized a changed field', async() => {
|
|
334
|
+
// A user override on a changed default should be flagged as a relevant difference.
|
|
335
|
+
context.userChartValues = { [`${ ADDON_NAME }-1.0.0`]: { replicas: 3 } };
|
|
336
|
+
await addonConfigPreserve(context, mockOldVersionCharts, mockNewVersionCharts);
|
|
337
|
+
|
|
338
|
+
expect(context.addonConfigDiffs[ADDON_NAME]).toStrictEqual({ replicas: 2 });
|
|
339
|
+
// Since diffs are NOT empty, user values should NOT be preserved
|
|
340
|
+
expect(context.userChartValues[`${ ADDON_NAME }-1.1.0`]).toBeUndefined();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('should not identify relevant changes for new fields not customized by user', async() => {
|
|
344
|
+
// New fields in the addon default values are not relevant if not customized by the user.
|
|
345
|
+
context.userChartValues = { [`${ ADDON_NAME }-1.0.0`]: {} };
|
|
346
|
+
await addonConfigPreserve(context, mockOldVersionCharts, mockNewVersionCharts);
|
|
347
|
+
|
|
348
|
+
// The only diff is `replicas`, which user didn't touch. `persistence` is new but also not touched.
|
|
349
|
+
// So final diffs should be empty.
|
|
350
|
+
expect(context.addonConfigDiffs[ADDON_NAME]).toStrictEqual({});
|
|
351
|
+
expect(context.userChartValues[`${ ADDON_NAME }-1.1.0`]).toStrictEqual({});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should preserve user values if there are no relevant differences', async() => {
|
|
355
|
+
// User values should be carried over to the new version if no relevant diffs are found.
|
|
356
|
+
context.userChartValues = { [`${ ADDON_NAME }-1.0.0`]: { service: { type: 'NodePort' } } };
|
|
357
|
+
await addonConfigPreserve(context, mockOldVersionCharts, mockNewVersionCharts);
|
|
358
|
+
|
|
359
|
+
expect(context.addonConfigDiffs[ADDON_NAME]).toStrictEqual({});
|
|
360
|
+
expect(context.userChartValues[`${ ADDON_NAME }-1.1.0`]).toStrictEqual({ service: { type: 'NodePort' } });
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('should not preserve user values if there are relevant differences', async() => {
|
|
364
|
+
// User values should not be carried over if relevant diffs are found.
|
|
365
|
+
context.userChartValues = { [`${ ADDON_NAME }-1.0.0`]: { replicas: 3 } };
|
|
366
|
+
await addonConfigPreserve(context, mockOldVersionCharts, mockNewVersionCharts);
|
|
367
|
+
|
|
368
|
+
expect(context.addonConfigDiffs[ADDON_NAME]).toStrictEqual({ replicas: 2 });
|
|
369
|
+
expect(context.userChartValues[`${ ADDON_NAME }-1.1.0`]).toBeUndefined();
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('should handle catalog API errors gracefully', async() => {
|
|
373
|
+
// Errors from fetching chart info should be caught and handled.
|
|
374
|
+
const error = new Error('catalog fetch failed');
|
|
375
|
+
|
|
376
|
+
jest.spyOn(context.$store, 'dispatch').mockImplementation().mockRejectedValue(error);
|
|
377
|
+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
378
|
+
|
|
379
|
+
await addonConfigPreserve(context, mockOldVersionCharts, mockNewVersionCharts);
|
|
380
|
+
|
|
381
|
+
expect(context.addonConfigDiffs[ADDON_NAME]).toBeUndefined();
|
|
382
|
+
expect(errorSpy).toHaveBeenCalledWith(`Failed to get chart version info for diff for chart ${ ADDON_NAME }`, error);
|
|
383
|
+
errorSpy.mockRestore();
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('should do nothing if oldCharts is not provided', async() => {
|
|
387
|
+
await addonConfigPreserve(context, {}, mockNewVersionCharts);
|
|
388
|
+
expect(context.addonConfigDiffs).toStrictEqual({});
|
|
389
|
+
expect(mockStore.dispatch).not.toHaveBeenCalled();
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('should do nothing if newCharts is not provided', async() => {
|
|
393
|
+
await addonConfigPreserve(context, mockOldVersionCharts, {});
|
|
394
|
+
expect(context.addonConfigDiffs).toStrictEqual({});
|
|
395
|
+
expect(mockStore.dispatch).not.toHaveBeenCalled();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('should do nothing if addon version is the same', async() => {
|
|
399
|
+
const mockNewVersionSame = {
|
|
400
|
+
[ADDON_NAME]: {
|
|
401
|
+
repo: 'repo',
|
|
402
|
+
version: '1.0.0'
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
await addonConfigPreserve(context, mockOldVersionCharts, mockNewVersionSame);
|
|
407
|
+
expect(context.addonConfigDiffs[ADDON_NAME]).toBeUndefined();
|
|
408
|
+
expect(mockStore.dispatch).not.toHaveBeenCalled();
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('should handle multiple addons', async() => {
|
|
412
|
+
const ADDON2_NAME = 'rke2-my-addon2';
|
|
413
|
+
const ADDON3_NAME = 'rke2-my-addon3'; // same version
|
|
414
|
+
|
|
415
|
+
const oldCharts = {
|
|
416
|
+
...mockOldVersionCharts,
|
|
417
|
+
[ADDON2_NAME]: { repo: 'repo', version: '1.0.0' },
|
|
418
|
+
[ADDON3_NAME]: { repo: 'repo', version: '1.0.0' },
|
|
419
|
+
};
|
|
420
|
+
const newCharts = {
|
|
421
|
+
...mockNewVersionCharts,
|
|
422
|
+
[ADDON2_NAME]: { repo: 'repo', version: '1.1.0' }, // changed version, but no user values
|
|
423
|
+
[ADDON3_NAME]: { repo: 'repo', version: '1.0.0' }, // same version
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
context.addonNames = [ADDON_NAME, ADDON2_NAME, ADDON3_NAME];
|
|
427
|
+
context.userChartValues = { [`${ ADDON_NAME }-1.0.0`]: { replicas: 3 } };
|
|
428
|
+
|
|
429
|
+
await addonConfigPreserve(context, oldCharts, newCharts);
|
|
430
|
+
|
|
431
|
+
expect(context.addonConfigDiffs[ADDON_NAME]).toStrictEqual({ replicas: 2 });
|
|
432
|
+
expect(context.addonConfigDiffs[ADDON2_NAME]).toStrictEqual({});
|
|
433
|
+
expect(context.addonConfigDiffs[ADDON3_NAME]).toBeUndefined();
|
|
434
|
+
});
|
|
435
|
+
});
|