@rancher/shell 0.3.27 → 0.3.28
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/translations/en-us.yaml +4 -20
- package/assets/translations/zh-hans.yaml +0 -23
- package/chart/gatekeeper.vue +2 -11
- package/chart/istio.vue +1 -10
- package/chart/logging/index.vue +2 -11
- package/chart/monitoring/index.vue +1 -9
- package/chart/rancher-backup/index.vue +1 -9
- package/components/Carousel.vue +2 -1
- package/components/formatter/ClusterProvider.vue +1 -18
- package/components/nav/WindowManager/ContainerLogs.vue +22 -19
- package/config/product/manager.js +0 -13
- package/config/types.js +0 -4
- package/edit/management.cattle.io.project.vue +1 -52
- package/edit/management.cattle.io.setting.vue +31 -2
- package/edit/provisioning.cattle.io.cluster/Basics.vue +6 -107
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -3
- package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -128
- package/middleware/authenticated.js +4 -2
- package/models/__tests__/management.cattle.io.cluster.test.ts +19 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +90 -0
- package/models/cluster.x-k8s.io.machine.js +1 -1
- package/models/management.cattle.io.cluster.js +4 -0
- package/models/management.cattle.io.project.js +0 -36
- package/models/management.cattle.io.setting.js +11 -7
- package/models/provisioning.cattle.io.cluster.js +16 -4
- package/package.json +1 -1
- package/pages/auth/setup.vue +38 -1
- package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +2 -17
- package/pages/c/_cluster/apps/charts/index.vue +0 -15
- package/pages/c/_cluster/apps/charts/install.helpers.js +2 -13
- package/pages/c/_cluster/apps/charts/install.vue +1 -1
- package/pages/c/_cluster/explorer/index.vue +0 -47
- package/pages/c/_cluster/manager/pages/_page.vue +4 -5
- package/rancher-components/BadgeState/BadgeState.vue +1 -5
- package/rancher-components/Banner/Banner.test.ts +1 -51
- package/rancher-components/Banner/Banner.vue +53 -134
- package/rancher-components/Card/Card.vue +7 -24
- package/rancher-components/Form/Checkbox/Checkbox.test.ts +29 -20
- package/rancher-components/Form/Checkbox/Checkbox.vue +20 -45
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +8 -2
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +10 -22
- package/rancher-components/Form/Radio/RadioButton.vue +13 -30
- package/rancher-components/Form/Radio/RadioGroup.vue +7 -26
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +38 -25
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +11 -23
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +5 -19
- package/rancher-components/StringList/StringList.test.ts +49 -453
- package/rancher-components/StringList/StringList.vue +58 -92
- package/rancher-components/components/StringList/StringList.test.ts +270 -0
- package/rancher-components/components/StringList/StringList.vue +57 -18
- package/store/prefs.js +0 -3
- package/types/shell/index.d.ts +22 -16
- package/utils/custom-validators.js +0 -2
- package/utils/error.js +16 -1
- package/utils/validators/formRules/__tests__/index.test.ts +49 -4
- package/utils/validators/formRules/index.ts +12 -9
- package/utils/validators/setting.js +6 -10
- package/.DS_Store +0 -0
- package/components/ChartPsp.vue +0 -76
- package/components/__tests__/ChartPsp.test.ts +0 -75
- package/components/formatter/__tests__/ClusterProvider.test.ts +0 -28
- package/rancher-components/Card/Card.test.ts +0 -37
- package/rancher-components/Form/Radio/RadioButton.test.ts +0 -31
- package/yarn-error.log +0 -200
|
@@ -82,6 +82,13 @@ export default Vue.extend({
|
|
|
82
82
|
return {} as ErrorMessages;
|
|
83
83
|
},
|
|
84
84
|
},
|
|
85
|
+
/**
|
|
86
|
+
* Enables bulk addition and defines the delimiter to split the input string.
|
|
87
|
+
*/
|
|
88
|
+
bulkAdditionDelimiter: {
|
|
89
|
+
type: RegExp,
|
|
90
|
+
default: null,
|
|
91
|
+
}
|
|
85
92
|
},
|
|
86
93
|
data() {
|
|
87
94
|
return {
|
|
@@ -125,13 +132,9 @@ export default Vue.extend({
|
|
|
125
132
|
},
|
|
126
133
|
|
|
127
134
|
methods: {
|
|
128
|
-
onChange(value: string) {
|
|
135
|
+
onChange(value: string, index?: number) {
|
|
129
136
|
this.value = value;
|
|
130
|
-
|
|
131
|
-
const items = [
|
|
132
|
-
...this.items,
|
|
133
|
-
this.value
|
|
134
|
-
];
|
|
137
|
+
const items = this.addValueToItems(this.items, value, index);
|
|
135
138
|
|
|
136
139
|
this.toggleError(
|
|
137
140
|
'duplicate',
|
|
@@ -321,10 +324,7 @@ export default Vue.extend({
|
|
|
321
324
|
const value = this.value?.trim();
|
|
322
325
|
|
|
323
326
|
if (value) {
|
|
324
|
-
const items =
|
|
325
|
-
...this.items,
|
|
326
|
-
value,
|
|
327
|
-
];
|
|
327
|
+
const items = this.addValueToItems(this.items, value);
|
|
328
328
|
|
|
329
329
|
if (!hasDuplicatedStrings(items, this.caseSensitive)) {
|
|
330
330
|
this.updateItems(items);
|
|
@@ -343,12 +343,8 @@ export default Vue.extend({
|
|
|
343
343
|
const value = this.value?.trim();
|
|
344
344
|
|
|
345
345
|
if (value) {
|
|
346
|
-
const
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
if (index !== -1) {
|
|
350
|
-
items[index] = value;
|
|
351
|
-
}
|
|
346
|
+
const index = findStringIndex(this.items, item, false);
|
|
347
|
+
const items = index !== -1 ? this.addValueToItems(this.items, value, index) : this.items;
|
|
352
348
|
|
|
353
349
|
if (!hasDuplicatedStrings(items, this.caseSensitive)) {
|
|
354
350
|
this.updateItems(items);
|
|
@@ -360,6 +356,49 @@ export default Vue.extend({
|
|
|
360
356
|
}
|
|
361
357
|
},
|
|
362
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Add a new or update an exiting item in the items list.
|
|
361
|
+
*
|
|
362
|
+
* @param items The current items list.
|
|
363
|
+
* @param value The new value to be added.
|
|
364
|
+
* @param index The list index of the item to be updated (optional).
|
|
365
|
+
* @returns Updated items list.
|
|
366
|
+
*/
|
|
367
|
+
addValueToItems(items: string[], value: string, index?: number): string[] {
|
|
368
|
+
const updatedItems = [...items];
|
|
369
|
+
|
|
370
|
+
// Add new item
|
|
371
|
+
if (index === undefined) {
|
|
372
|
+
if (this.bulkAdditionDelimiter && value.search(this.bulkAdditionDelimiter) >= 0) {
|
|
373
|
+
updatedItems.push(...this.splitBulkValue(value));
|
|
374
|
+
} else {
|
|
375
|
+
updatedItems.push(value);
|
|
376
|
+
}
|
|
377
|
+
} else { // Update existing item
|
|
378
|
+
if (this.bulkAdditionDelimiter && value.search(this.bulkAdditionDelimiter) >= 0) {
|
|
379
|
+
updatedItems.splice(index, 1, ...this.splitBulkValue(value));
|
|
380
|
+
} else {
|
|
381
|
+
updatedItems[index] = value;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return updatedItems;
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Split the value by the defined delimiter and remove empty strings.
|
|
390
|
+
*
|
|
391
|
+
* @param value The value to be split.
|
|
392
|
+
* @returns Array containing split values.
|
|
393
|
+
*/
|
|
394
|
+
splitBulkValue(value: string): string[] {
|
|
395
|
+
return value
|
|
396
|
+
.split(this.bulkAdditionDelimiter)
|
|
397
|
+
.filter((item) => {
|
|
398
|
+
return item.trim().length > 0;
|
|
399
|
+
});
|
|
400
|
+
},
|
|
401
|
+
|
|
363
402
|
/**
|
|
364
403
|
* Remove an item from items list
|
|
365
404
|
*/
|
|
@@ -393,7 +432,7 @@ export default Vue.extend({
|
|
|
393
432
|
@dblclick="onClickEmptyBody()"
|
|
394
433
|
>
|
|
395
434
|
<div
|
|
396
|
-
v-for="item in items"
|
|
435
|
+
v-for="(item, index) in items"
|
|
397
436
|
:key="item"
|
|
398
437
|
:ref="item"
|
|
399
438
|
:class="{
|
|
@@ -421,7 +460,7 @@ export default Vue.extend({
|
|
|
421
460
|
:data-testid="`item-edit-${item}`"
|
|
422
461
|
class="edit-input static"
|
|
423
462
|
:value="value != null ? value : item"
|
|
424
|
-
@input="onChange($event)"
|
|
463
|
+
@input="onChange($event, index)"
|
|
425
464
|
@blur.prevent="updateItem(item)"
|
|
426
465
|
@keydown.native.enter="updateItem(item, !errors.duplicate)"
|
|
427
466
|
/>
|
package/store/prefs.js
CHANGED
|
@@ -112,9 +112,6 @@ export const _RKE1 = 'rke1';
|
|
|
112
112
|
export const _RKE2 = 'rke2';
|
|
113
113
|
export const PROVISIONER = create('provisioner', _RKE2, { options: [_RKE1, _RKE2] });
|
|
114
114
|
|
|
115
|
-
// Promo for Pod Security Policies (PSPs) being deprecated on kube version 1.25 on Cluster Dashboard page
|
|
116
|
-
export const PSP_DEPRECATION_BANNER = create('hide-psp-deprecation-banner', false, { parseJSON });
|
|
117
|
-
|
|
118
115
|
// Maximum number of clusters to show in the slide-in menu
|
|
119
116
|
export const MENU_MAX_CLUSTERS = 10;
|
|
120
117
|
// Prompt for confirm when scaling down node pool in GUI and save the pref
|
package/types/shell/index.d.ts
CHANGED
|
@@ -1765,8 +1765,6 @@ export const NODE: "node";
|
|
|
1765
1765
|
export const NETWORK_POLICY: "networking.k8s.io.networkpolicy";
|
|
1766
1766
|
export const POD: "pod";
|
|
1767
1767
|
export const POD_DISRUPTION_BUDGET: "policy.poddisruptionbudget";
|
|
1768
|
-
export const PSP: "policy.podsecuritypolicy";
|
|
1769
|
-
export const PSPS: "policy.podsecuritypolicies";
|
|
1770
1768
|
export const PV: "persistentvolume";
|
|
1771
1769
|
export const PVC: "persistentvolumeclaim";
|
|
1772
1770
|
export const RESOURCE_QUOTA: "resourcequota";
|
|
@@ -1884,8 +1882,6 @@ export namespace MANAGEMENT {
|
|
|
1884
1882
|
export { GLOBAL_ROLE_1 as GLOBAL_ROLE };
|
|
1885
1883
|
const GLOBAL_ROLE_BINDING_1: string;
|
|
1886
1884
|
export { GLOBAL_ROLE_BINDING_1 as GLOBAL_ROLE_BINDING };
|
|
1887
|
-
export const POD_SECURITY_POLICY_TEMPLATE: string;
|
|
1888
|
-
export const PSP_TEMPLATE_BINDING: string;
|
|
1889
1885
|
export const PSA: string;
|
|
1890
1886
|
export const MANAGED_CHART: string;
|
|
1891
1887
|
export const USER_NOTIFICATION: string;
|
|
@@ -2869,7 +2865,6 @@ export const PLUGIN_DEVELOPER: any;
|
|
|
2869
2865
|
export const _RKE1: "rke1";
|
|
2870
2866
|
export const _RKE2: "rke2";
|
|
2871
2867
|
export const PROVISIONER: any;
|
|
2872
|
-
export const PSP_DEPRECATION_BANNER: any;
|
|
2873
2868
|
export const MENU_MAX_CLUSTERS: 10;
|
|
2874
2869
|
export const SCALE_POOL_PROMPT: any;
|
|
2875
2870
|
export function state(): {
|
|
@@ -3301,7 +3296,6 @@ declare namespace _default {
|
|
|
3301
3296
|
export { cronSchedule };
|
|
3302
3297
|
export { podAffinity };
|
|
3303
3298
|
export { roleTemplateRules };
|
|
3304
|
-
export { isHttps };
|
|
3305
3299
|
}
|
|
3306
3300
|
export default _default;
|
|
3307
3301
|
}
|
|
@@ -3361,8 +3355,17 @@ declare module '@shell/utils/error' {
|
|
|
3361
3355
|
export function stringify(err: any): any;
|
|
3362
3356
|
export function exceptionToErrorsArray(err: any): any;
|
|
3363
3357
|
export class ClusterNotFoundError extends Error {
|
|
3358
|
+
static name: string;
|
|
3364
3359
|
constructor(message: any);
|
|
3365
3360
|
}
|
|
3361
|
+
/**
|
|
3362
|
+
* An error occurred and the user should be redirected to a certain location (where this is handled)
|
|
3363
|
+
*/
|
|
3364
|
+
export class RedirectToError extends Error {
|
|
3365
|
+
static name: string;
|
|
3366
|
+
constructor(message: any, url: any);
|
|
3367
|
+
url: any;
|
|
3368
|
+
}
|
|
3366
3369
|
export class ApiError extends Error {
|
|
3367
3370
|
constructor(res: any);
|
|
3368
3371
|
status: any;
|
|
@@ -3618,35 +3621,35 @@ export namespace KEY {
|
|
|
3618
3621
|
}
|
|
3619
3622
|
}
|
|
3620
3623
|
|
|
3621
|
-
// @shell/utils/poller
|
|
3624
|
+
// @shell/utils/poller
|
|
3622
3625
|
|
|
3623
|
-
declare module '@shell/utils/poller
|
|
3624
|
-
export default class
|
|
3626
|
+
declare module '@shell/utils/poller' {
|
|
3627
|
+
export default class Poller {
|
|
3625
3628
|
constructor(fn: any, pollRateMs: any, maxRetries?: number);
|
|
3626
3629
|
fn: any;
|
|
3627
3630
|
pollRateMs: any;
|
|
3628
3631
|
maxRetries: number;
|
|
3629
|
-
|
|
3632
|
+
intervalId: any;
|
|
3630
3633
|
tryCount: number;
|
|
3631
3634
|
start(): void;
|
|
3632
3635
|
stop(): void;
|
|
3633
|
-
_poll(): void;
|
|
3634
3636
|
_intervalMethod(): Promise<void>;
|
|
3635
3637
|
}
|
|
3636
3638
|
}
|
|
3637
3639
|
|
|
3638
|
-
// @shell/utils/poller
|
|
3640
|
+
// @shell/utils/poller-sequential
|
|
3639
3641
|
|
|
3640
|
-
declare module '@shell/utils/poller' {
|
|
3641
|
-
export default class
|
|
3642
|
+
declare module '@shell/utils/poller-sequential' {
|
|
3643
|
+
export default class PollerSequential {
|
|
3642
3644
|
constructor(fn: any, pollRateMs: any, maxRetries?: number);
|
|
3643
3645
|
fn: any;
|
|
3644
3646
|
pollRateMs: any;
|
|
3645
3647
|
maxRetries: number;
|
|
3646
|
-
|
|
3648
|
+
timeoutId: any;
|
|
3647
3649
|
tryCount: number;
|
|
3648
3650
|
start(): void;
|
|
3649
3651
|
stop(): void;
|
|
3652
|
+
_poll(): void;
|
|
3650
3653
|
_intervalMethod(): Promise<void>;
|
|
3651
3654
|
}
|
|
3652
3655
|
}
|
|
@@ -4173,7 +4176,10 @@ export function externalName(spec: any, getters: any, errors: any, validatorArgs
|
|
|
4173
4176
|
// @shell/utils/validators/setting
|
|
4174
4177
|
|
|
4175
4178
|
declare module '@shell/utils/validators/setting' {
|
|
4176
|
-
export function
|
|
4179
|
+
export function isServerUrl(value: any): boolean;
|
|
4180
|
+
export function isHttps(value: any): any;
|
|
4181
|
+
export function isLocalhost(value: any): boolean;
|
|
4182
|
+
export function hasTrailingForwardSlash(value: any): any;
|
|
4177
4183
|
}
|
|
4178
4184
|
|
|
4179
4185
|
// @shell/utils/version
|
|
@@ -8,7 +8,6 @@ import { cronSchedule } from '@shell/utils/validators/cron-schedule';
|
|
|
8
8
|
import { podAffinity } from '@shell/utils/validators/pod-affinity';
|
|
9
9
|
import { roleTemplateRules } from '@shell/utils/validators/role-template';
|
|
10
10
|
import { clusterName } from '@shell/utils/validators/cluster-name';
|
|
11
|
-
import { isHttps } from '@shell/utils/validators/setting';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Custom validation functions beyond normal scalr types
|
|
@@ -30,5 +29,4 @@ export default {
|
|
|
30
29
|
cronSchedule,
|
|
31
30
|
podAffinity,
|
|
32
31
|
roleTemplateRules,
|
|
33
|
-
isHttps,
|
|
34
32
|
};
|
package/utils/error.js
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
import { isArray } from '@shell/utils/array';
|
|
2
2
|
|
|
3
3
|
export class ClusterNotFoundError extends Error {
|
|
4
|
+
static name = 'ClusterNotFoundError'
|
|
5
|
+
|
|
4
6
|
constructor(message) {
|
|
5
7
|
super(message);
|
|
6
|
-
this.name =
|
|
8
|
+
this.name = ClusterNotFoundError.name;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* An error occurred and the user should be redirected to a certain location (where this is handled)
|
|
14
|
+
*/
|
|
15
|
+
export class RedirectToError extends Error {
|
|
16
|
+
static name = 'RedirectToError'
|
|
17
|
+
|
|
18
|
+
constructor(message, url) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.url = url;
|
|
21
|
+
this.name = RedirectToError.name;
|
|
7
22
|
}
|
|
8
23
|
}
|
|
9
24
|
|
|
@@ -43,21 +43,66 @@ describe('formRules', () => {
|
|
|
43
43
|
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
it('"
|
|
46
|
+
it('"https" : returns undefined when valid https url value is supplied', () => {
|
|
47
47
|
const testValue = 'https://url.com';
|
|
48
|
-
const formRuleResult = formRules.
|
|
48
|
+
const formRuleResult = formRules.https(testValue);
|
|
49
49
|
|
|
50
50
|
expect(formRuleResult).toBeUndefined();
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
it('"
|
|
53
|
+
it('"https" : returns correct message when http url value is supplied', () => {
|
|
54
54
|
const testValue = 'http://url.com';
|
|
55
|
-
const formRuleResult = formRules.
|
|
55
|
+
const formRuleResult = formRules.https(testValue);
|
|
56
56
|
const expectedResult = JSON.stringify({ message: 'validation.setting.serverUrl.https' });
|
|
57
57
|
|
|
58
58
|
expect(formRuleResult).toStrictEqual(expectedResult);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
describe('localhost', () => {
|
|
62
|
+
const message = JSON.stringify({ message: 'validation.setting.serverUrl.localhost' });
|
|
63
|
+
const testCases = [
|
|
64
|
+
['http://LOCALhosT:8005', message],
|
|
65
|
+
['http://localhost:8005', message],
|
|
66
|
+
['https://localhost:8005', message],
|
|
67
|
+
['localhost', message],
|
|
68
|
+
['http://127.0.0.1', message],
|
|
69
|
+
['https://127.0.0.1', message],
|
|
70
|
+
['127.0.0.1', message],
|
|
71
|
+
['https://test.com', undefined],
|
|
72
|
+
['https://test.com/localhost', undefined],
|
|
73
|
+
[undefined, undefined]
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
it.each(testCases)(
|
|
77
|
+
'should return undefined or correct message based on the provided url',
|
|
78
|
+
(url, expected) => {
|
|
79
|
+
const formRuleResult = formRules.localhost(url);
|
|
80
|
+
|
|
81
|
+
expect(formRuleResult).toStrictEqual(expected);
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('trailingForwardSlash', () => {
|
|
87
|
+
const message = JSON.stringify({ message: 'validation.setting.serverUrl.trailingForwardSlash' });
|
|
88
|
+
const testCases = [
|
|
89
|
+
['https://test.com', undefined],
|
|
90
|
+
['https://test.com/', message],
|
|
91
|
+
['https://', undefined],
|
|
92
|
+
['/', undefined],
|
|
93
|
+
[undefined, undefined]
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
it.each(testCases)(
|
|
97
|
+
'should return undefined or correct message based on the provided url',
|
|
98
|
+
(url, expected) => {
|
|
99
|
+
const formRuleResult = formRules.trailingForwardSlash(url);
|
|
100
|
+
|
|
101
|
+
expect(formRuleResult).toStrictEqual(expected);
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
61
106
|
it('"interval" : returns undefined when valid hour interval value is supplied', () => {
|
|
62
107
|
const testValue = '5h';
|
|
63
108
|
const formRuleResult = formRules.interval(testValue);
|
|
@@ -2,9 +2,11 @@ import { RBAC } from '@shell/config/types';
|
|
|
2
2
|
import { HCI } from '@shell/config/labels-annotations';
|
|
3
3
|
import isEmpty from 'lodash/isEmpty';
|
|
4
4
|
import has from 'lodash/has';
|
|
5
|
+
import isUrl from 'is-url';
|
|
5
6
|
// import uniq from 'lodash/uniq';
|
|
6
7
|
import cronstrue from 'cronstrue';
|
|
7
8
|
import { Translation } from '@shell/types/t';
|
|
9
|
+
import { isHttps, isLocalhost, hasTrailingForwardSlash } from '@shell/utils/validators/setting';
|
|
8
10
|
|
|
9
11
|
// import uniq from 'lodash/uniq';
|
|
10
12
|
export type Validator<T = undefined | string> = (val: any, arg?: any) => T;
|
|
@@ -34,10 +36,6 @@ export class Port {
|
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
const httpsKeys = [
|
|
38
|
-
'server-url'
|
|
39
|
-
];
|
|
40
|
-
|
|
41
39
|
const runValidators = (val: any, validators: Validator[]) => {
|
|
42
40
|
for (const validator of validators) {
|
|
43
41
|
const message = validator(val);
|
|
@@ -139,11 +137,13 @@ export default function(t: Translation, { key = 'Value' }: ValidationOptions): {
|
|
|
139
137
|
}
|
|
140
138
|
};
|
|
141
139
|
|
|
142
|
-
const
|
|
143
|
-
const isHttps: Validator = (val: string) => httpsKeys.includes(key) && !val.toLowerCase().startsWith('https://') ? t('validation.setting.serverUrl.https') : undefined;
|
|
140
|
+
const https: Validator = (val: string) => val && !isHttps(val) ? t('validation.setting.serverUrl.https') : undefined;
|
|
144
141
|
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
const localhost: Validator = (val: string) => isLocalhost(val) ? t('validation.setting.serverUrl.localhost') : undefined;
|
|
143
|
+
|
|
144
|
+
const trailingForwardSlash: Validator = (val: string) => hasTrailingForwardSlash(val) ? t('validation.setting.serverUrl.trailingForwardSlash') : undefined;
|
|
145
|
+
|
|
146
|
+
const url: Validator = (val: string) => val && !isUrl(val) ? t('validation.setting.serverUrl.url') : undefined;
|
|
147
147
|
|
|
148
148
|
const interval: Validator = (val: string) => !/^\d+[hms]$/.test(val) ? t('validation.monitoring.route.interval', { key }) : undefined;
|
|
149
149
|
|
|
@@ -475,7 +475,10 @@ export default function(t: Translation, { key = 'Value' }: ValidationOptions): {
|
|
|
475
475
|
hostname,
|
|
476
476
|
imageUrl,
|
|
477
477
|
interval,
|
|
478
|
-
|
|
478
|
+
https,
|
|
479
|
+
localhost,
|
|
480
|
+
trailingForwardSlash,
|
|
481
|
+
url,
|
|
479
482
|
matching,
|
|
480
483
|
maxLength,
|
|
481
484
|
maxValue,
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
'server-url'
|
|
3
|
-
];
|
|
1
|
+
import isUrl from 'is-url';
|
|
4
2
|
|
|
5
|
-
export
|
|
6
|
-
const key = validatorArgs[0];
|
|
3
|
+
export const isServerUrl = (value) => value === 'server-url';
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
errors.push(getters['i18n/t']('validation.setting.serverUrl.https'));
|
|
10
|
-
}
|
|
5
|
+
export const isHttps = (value) => value.toLowerCase().startsWith('https://');
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
export const isLocalhost = (value) => (/^(?:https?:\/\/)?(?:localhost|127\.0\.0\.1)/i).test(value);
|
|
8
|
+
|
|
9
|
+
export const hasTrailingForwardSlash = (value) => isUrl(value) && value?.toLowerCase().endsWith('/');
|
package/.DS_Store
DELETED
|
Binary file
|
package/components/ChartPsp.vue
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { Checkbox } from '@components/Form/Checkbox';
|
|
3
|
-
import { mapGetters } from 'vuex';
|
|
4
|
-
|
|
5
|
-
export default {
|
|
6
|
-
components: { Checkbox },
|
|
7
|
-
props: {
|
|
8
|
-
value: {
|
|
9
|
-
type: Object,
|
|
10
|
-
default: () => {
|
|
11
|
-
return {};
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
mode: {
|
|
15
|
-
type: String,
|
|
16
|
-
default: 'edit'
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Optional title section prior checkbox
|
|
21
|
-
*/
|
|
22
|
-
title: {
|
|
23
|
-
type: String,
|
|
24
|
-
default: null
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Cluster information
|
|
29
|
-
*/
|
|
30
|
-
cluster: {
|
|
31
|
-
type: Object,
|
|
32
|
-
default: null
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
created() {
|
|
36
|
-
if (!this.value.global.cattle) {
|
|
37
|
-
this.$set(this.value.global, 'cattle', { psp: { enabled: false } });
|
|
38
|
-
}
|
|
39
|
-
if (!this.value.global.cattle.psp) {
|
|
40
|
-
this.$set(this.value.global.cattle, 'psp', { enabled: false });
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
computed: {
|
|
44
|
-
...mapGetters({ t: 'i18n/t' }),
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Display checkbox only if contains PSP or K8S version is less than 1.25
|
|
48
|
-
*/
|
|
49
|
-
hasCheckbox() {
|
|
50
|
-
const clusterVersion = this.cluster?.kubernetesVersion || '';
|
|
51
|
-
const version = clusterVersion.match(/\d+/g);
|
|
52
|
-
const isRequiredVersion = version?.length ? +version[0] === 1 && +version[1] < 25 : false;
|
|
53
|
-
|
|
54
|
-
return isRequiredVersion;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
</script>
|
|
59
|
-
|
|
60
|
-
<template>
|
|
61
|
-
<div
|
|
62
|
-
v-if="hasCheckbox"
|
|
63
|
-
class="mt-10 mb-10"
|
|
64
|
-
>
|
|
65
|
-
<h3 v-if="title">
|
|
66
|
-
{{ title }}
|
|
67
|
-
</h3>
|
|
68
|
-
|
|
69
|
-
<Checkbox
|
|
70
|
-
v-model="value.global.cattle.psp.enabled"
|
|
71
|
-
data-testid="psp-checkbox"
|
|
72
|
-
:mode="mode"
|
|
73
|
-
:label="t('catalog.chart.enablePSP')"
|
|
74
|
-
/>
|
|
75
|
-
</div>
|
|
76
|
-
</template>
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { shallowMount, mount } from '@vue/test-utils';
|
|
2
|
-
import ChartPsp from '@shell/components/ChartPsp.vue';
|
|
3
|
-
|
|
4
|
-
describe('component: ChartPsp', () => {
|
|
5
|
-
it.each([
|
|
6
|
-
true, false
|
|
7
|
-
])('should render checkbox referencing value.global.cattle.psp.enabled as %p', (value) => {
|
|
8
|
-
const version = 'v1.24.11+rke2r1';
|
|
9
|
-
const wrapper = shallowMount(ChartPsp, {
|
|
10
|
-
propsData: {
|
|
11
|
-
value: { global: { cattle: { psp: { enabled: value } } } },
|
|
12
|
-
cluster: { kubernetesVersion: version }
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
expect(wrapper.findComponent({ name: 'Checkbox' }).props().value).toBe(value);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it.each([
|
|
20
|
-
['v1.24.11+rke2r1'],
|
|
21
|
-
])('should display the checkbox for cluster with k8s version %p', (version) => {
|
|
22
|
-
const wrapper = shallowMount(ChartPsp, {
|
|
23
|
-
propsData: {
|
|
24
|
-
value: { global: { cattle: { psp: { enabled: false } } } },
|
|
25
|
-
cluster: { kubernetesVersion: version }
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const input = wrapper.find(`[data-testid="psp-checkbox"]`).element as HTMLInputElement;
|
|
30
|
-
|
|
31
|
-
expect(input).toBeDefined();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it.each([
|
|
35
|
-
['v1.25.11+rke2r1'],
|
|
36
|
-
])('should not display the checkbox for cluster with k8s version %p', (version) => {
|
|
37
|
-
const wrapper = shallowMount(ChartPsp, {
|
|
38
|
-
propsData: {
|
|
39
|
-
value: { global: { cattle: { psp: { enabled: false } } } },
|
|
40
|
-
cluster: { kubernetesVersion: version }
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const input = wrapper.find(`[data-testid="psp-checkbox"]`).element as HTMLInputElement;
|
|
45
|
-
|
|
46
|
-
expect(input).toBeUndefined();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should update value.global.cattle.psp.enabled when checkbox is toggled', async() => {
|
|
50
|
-
const chartValues = { global: {} } as any;
|
|
51
|
-
const version = 'v1.24.11+rke2r1';
|
|
52
|
-
const wrapper = mount(ChartPsp, {
|
|
53
|
-
propsData: {
|
|
54
|
-
value: chartValues,
|
|
55
|
-
cluster: { kubernetesVersion: version }
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
await wrapper.find('.checkbox-container').trigger('click');
|
|
60
|
-
|
|
61
|
-
expect(chartValues.global.cattle.psp.enabled).toBe(true);
|
|
62
|
-
|
|
63
|
-
await wrapper.find('.checkbox-container').trigger('click');
|
|
64
|
-
expect(chartValues.global.cattle.psp.enabled).toBe(false);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it.each([
|
|
68
|
-
{ global: {} } as any,
|
|
69
|
-
{ global: { cattle: {} } } as any,
|
|
70
|
-
])('should define cattle.psp.enabled and set to false', (chartValues) => {
|
|
71
|
-
shallowMount(ChartPsp, { propsData: { value: chartValues } });
|
|
72
|
-
|
|
73
|
-
expect(chartValues.global.cattle.psp.enabled).toBe(false);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { mount } from '@vue/test-utils';
|
|
2
|
-
import ClusterProvider from '@shell/components/formatter/ClusterProvider.vue';
|
|
3
|
-
|
|
4
|
-
describe('component: ClusterProvider', () => {
|
|
5
|
-
const importedGkeClusterInfo = { mgmt: { spec: { gkeConfig: { imported: true } } } };
|
|
6
|
-
const importedAksClusterInfo = { mgmt: { spec: { aksConfig: { imported: true } } } };
|
|
7
|
-
const importedEksClusterInfo = { mgmt: { spec: { eksConfig: { imported: true } } } };
|
|
8
|
-
const notImportedGkeClusterInfo = { mgmt: { spec: { gkeConfig: { imported: false } } } };
|
|
9
|
-
const importedClusterInfoWithProviderForEmberParam = { mgmt: { providerForEmberParam: 'import' } };
|
|
10
|
-
|
|
11
|
-
describe('isImported', () => {
|
|
12
|
-
const testCases = [
|
|
13
|
-
[importedGkeClusterInfo, true],
|
|
14
|
-
[importedAksClusterInfo, true],
|
|
15
|
-
[importedEksClusterInfo, true],
|
|
16
|
-
[notImportedGkeClusterInfo, false],
|
|
17
|
-
[importedClusterInfoWithProviderForEmberParam, true],
|
|
18
|
-
[{}, false],
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
it.each(testCases)('should return the isImported value properly based on the props data', (row, expected) => {
|
|
22
|
-
const wrapper = mount(ClusterProvider, { propsData: { row } });
|
|
23
|
-
|
|
24
|
-
expect(wrapper.vm.$data.isImported).toBe(expected);
|
|
25
|
-
}
|
|
26
|
-
);
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { mount } from '@vue/test-utils';
|
|
2
|
-
import { Card } from './index';
|
|
3
|
-
|
|
4
|
-
describe('component: Card', () => {
|
|
5
|
-
const title = 'Card title';
|
|
6
|
-
const body = 'Card body';
|
|
7
|
-
|
|
8
|
-
it('should have a card title', () => {
|
|
9
|
-
const wrapper = mount(Card, {
|
|
10
|
-
propsData: { title },
|
|
11
|
-
slots: { title: '<div>Card title</div>' }
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const element = wrapper.find('[data-testid="card-title-slot"]');
|
|
15
|
-
|
|
16
|
-
expect(element.exists()).toBe(true);
|
|
17
|
-
expect(element.text()).toBe(title);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should have a card body', () => {
|
|
21
|
-
const wrapper = mount(Card, {
|
|
22
|
-
propsData: { body },
|
|
23
|
-
slots: { body: '<div>Card body</div>' }
|
|
24
|
-
});
|
|
25
|
-
const element = wrapper.find('[data-testid="card-body-slot"]');
|
|
26
|
-
|
|
27
|
-
expect(element.exists()).toBe(true);
|
|
28
|
-
expect(element.text()).toBe(body);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('should display the default card actions', () => {
|
|
32
|
-
const wrapper = mount(Card);
|
|
33
|
-
const element = wrapper.find('[data-testid="card-actions-slot"]');
|
|
34
|
-
|
|
35
|
-
expect(element.exists()).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { shallowMount } from '@vue/test-utils';
|
|
2
|
-
import { RadioButton } from './index';
|
|
3
|
-
import { cleanHtmlDirective } from '@shell/plugins/clean-html-directive';
|
|
4
|
-
|
|
5
|
-
describe('radioButton.vue', () => {
|
|
6
|
-
it('renders label slot contents', () => {
|
|
7
|
-
const wrapper = shallowMount(RadioButton, { slots: { label: 'Test Label' } });
|
|
8
|
-
|
|
9
|
-
expect(wrapper.find('.radio-label').text()).toBe('Test Label');
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('renders label prop contents', () => {
|
|
13
|
-
const wrapper = shallowMount(
|
|
14
|
-
RadioButton,
|
|
15
|
-
{
|
|
16
|
-
directives: { cleanHtmlDirective },
|
|
17
|
-
propsData: { label: 'Test Label' }
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
expect(wrapper.find('.radio-label').text()).toBe('Test Label');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('renders slot contents when both slot and label prop are provided', () => {
|
|
24
|
-
const wrapper = shallowMount(RadioButton, {
|
|
25
|
-
slots: { label: 'Test Label - Slot' },
|
|
26
|
-
propsData: { label: 'Test Label - Props' },
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
expect(wrapper.find('.radio-label').text()).toBe('Test Label - Slot');
|
|
30
|
-
});
|
|
31
|
-
});
|