@rancher/shell 3.0.9-rc.4 → 3.0.9-rc.5
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/brand/suse/metadata.json +2 -1
- package/assets/translations/en-us.yaml +91 -3
- package/components/ActionMenuShell.vue +1 -1
- package/components/Inactivity.vue +2 -2
- package/components/Resource/Detail/Card/ExtrasCard.vue +49 -15
- package/components/Resource/Detail/Card/__tests__/ExtrasCard.test.ts +111 -0
- package/components/Resource/Detail/Masthead/__tests__/index.test.ts +0 -17
- package/components/Resource/Detail/Masthead/index.vue +11 -4
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +3 -1
- package/components/Resource/Detail/Metadata/index.vue +1 -1
- package/components/Resource/Detail/ResourceRow.vue +1 -1
- package/components/ResourceDetail/Masthead/latest.vue +12 -2
- package/components/ResourceList/index.vue +9 -0
- package/components/ResourceTable.vue +38 -4
- package/components/Tabbed/Tab.vue +4 -0
- package/components/Tabbed/index.vue +4 -1
- package/components/__tests__/ProjectRow.test.ts +60 -0
- package/components/form/ChangePassword.vue +41 -35
- package/components/form/ResourceQuota/Project.vue +42 -1
- package/components/form/ResourceQuota/ProjectRow.vue +71 -4
- package/components/form/ResourceQuota/__tests__/Project.test.ts +63 -0
- package/components/form/SelectOrCreateAuthSecret.vue +6 -1
- package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +35 -0
- package/components/formatter/MachineSummaryGraph.vue +10 -2
- package/components/nav/TopLevelMenu.helper.ts +50 -2
- package/components/nav/TopLevelMenu.vue +14 -0
- package/components/nav/Type.vue +5 -0
- package/components/nav/__tests__/TopLevelMenu.test.ts +3 -3
- package/components/nav/__tests__/Type.test.ts +6 -4
- package/config/product/explorer.js +4 -3
- package/config/product/manager.js +18 -1
- package/config/router/navigation-guards/authentication.js +8 -9
- package/config/types.js +10 -2
- package/detail/auditlog.cattle.io.auditpolicy.vue +19 -0
- package/detail/management.cattle.io.user.vue +1 -2
- package/detail/node.vue +0 -1
- package/detail/provisioning.cattle.io.cluster.vue +2 -1
- package/dialog/ChangePasswordDialog.vue +8 -0
- package/dialog/GenericPrompt.vue +20 -3
- package/dialog/ScaleMachineDownDialog.vue +65 -15
- package/dialog/SearchDialog.vue +10 -2
- package/dialog/__tests__/ScaleMachineDownDialog.test.ts +184 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +89 -0
- package/edit/__tests__/management.cattle.io.project.test.js +56 -1
- package/edit/auditlog.cattle.io.auditpolicy/AdditionalRedactions.vue +114 -0
- package/edit/auditlog.cattle.io.auditpolicy/Filters.vue +119 -0
- package/edit/auditlog.cattle.io.auditpolicy/General.vue +180 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/AdditionalRedactions.test.ts +327 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/Filters.test.ts +449 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/General.test.ts +472 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/AdditionalRedactions.test.ts.snap +27 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/Filters.test.ts.snap +39 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +174 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +29 -0
- package/edit/auditlog.cattle.io.auditpolicy/__tests__/index.test.ts +215 -0
- package/edit/auditlog.cattle.io.auditpolicy/index.vue +104 -0
- package/edit/auditlog.cattle.io.auditpolicy/types.ts +28 -0
- package/edit/fleet.cattle.io.gitrepo.vue +16 -1
- package/edit/management.cattle.io.project.vue +8 -2
- package/edit/management.cattle.io.user.vue +29 -34
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +178 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +21 -2
- package/edit/provisioning.cattle.io.cluster/shared.ts +4 -0
- package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +1 -0
- package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +1 -1
- package/list/auditlog.cattle.io.auditpolicy.vue +63 -0
- package/list/group.principal.vue +11 -15
- package/list/management.cattle.io.user.vue +11 -21
- package/machine-config/azure.vue +14 -0
- package/mixins/browser-tab-visibility.js +5 -4
- package/mixins/fetch.client.js +6 -0
- package/models/__tests__/auditlog.cattle.io.auditpolicy.test.ts +117 -0
- package/models/__tests__/workload.test.ts +49 -6
- package/models/auditlog.cattle.io.auditpolicy.js +46 -0
- package/models/cluster.x-k8s.io.machine.js +1 -1
- package/models/cluster.x-k8s.io.machinedeployment.js +5 -5
- package/models/event.js +5 -0
- package/models/ext.cattle.io.groupmembershiprefreshrequest.js +15 -0
- package/models/ext.cattle.io.passwordchangerequest.js +15 -0
- package/models/ext.cattle.io.selfuser.js +15 -0
- package/models/fleet-application.js +17 -7
- package/models/management.cattle.io.user.js +28 -31
- package/models/schema.js +18 -0
- package/models/secret.js +27 -24
- package/models/steve-schema.ts +39 -2
- package/models/workload.js +3 -2
- package/package.json +1 -1
- package/pages/account/index.vue +23 -16
- package/pages/auth/login.vue +15 -8
- package/pages/auth/setup.vue +52 -15
- package/pages/home.vue +9 -3
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +1 -3
- package/plugins/dashboard-store/actions.js +7 -0
- package/plugins/dashboard-store/getters.js +23 -1
- package/plugins/dashboard-store/index.js +3 -2
- package/plugins/dashboard-store/mutations.js +4 -0
- package/plugins/dashboard-store/resource-class.js +12 -5
- package/plugins/steve/__tests__/steve-class.test.ts +167 -0
- package/plugins/steve/schema.d.ts +5 -0
- package/plugins/steve/steve-class.js +19 -0
- package/plugins/steve/steve-pagination-utils.ts +2 -1
- package/store/auth.js +57 -19
- package/store/notifications.ts +1 -1
- package/store/type-map.js +12 -1
- package/types/shell/index.d.ts +10 -14
- package/types/store/dashboard-store.types.ts +7 -0
- package/utils/pagination-wrapper.ts +11 -3
- package/vue.config.js +26 -13
- package/edit/provisioning.cattle.io.cluster/defaults.ts +0 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import AuditPolicy from '@shell/models/auditlog.cattle.io.auditpolicy';
|
|
2
|
+
|
|
3
|
+
describe('auditPolicy Model', () => {
|
|
4
|
+
let mockDispatch: jest.Mock;
|
|
5
|
+
let mockT: jest.Mock;
|
|
6
|
+
let auditPolicy: any;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
mockDispatch = jest.fn();
|
|
10
|
+
mockT = jest.fn();
|
|
11
|
+
|
|
12
|
+
const mockResource = {
|
|
13
|
+
id: 'test-policy',
|
|
14
|
+
spec: { enabled: false },
|
|
15
|
+
metadata: { name: 'test-policy' }
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
auditPolicy = new AuditPolicy(mockResource, {
|
|
19
|
+
dispatch: mockDispatch,
|
|
20
|
+
rootGetters: { 'i18n/t': mockT },
|
|
21
|
+
getters: { schemaFor: () => ({ linkFor: jest.fn() }) }
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('enable method', () => {
|
|
26
|
+
it('should call enableOrDisable with "enable"', () => {
|
|
27
|
+
const spy = jest.spyOn(auditPolicy, 'enableOrDisable').mockImplementation();
|
|
28
|
+
|
|
29
|
+
auditPolicy.enable();
|
|
30
|
+
|
|
31
|
+
expect(spy).toHaveBeenCalledWith('enable');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('disable method', () => {
|
|
36
|
+
it('should call enableOrDisable with "disable"', () => {
|
|
37
|
+
const spy = jest.spyOn(auditPolicy, 'enableOrDisable').mockImplementation();
|
|
38
|
+
|
|
39
|
+
auditPolicy.disable();
|
|
40
|
+
|
|
41
|
+
expect(spy).toHaveBeenCalledWith('disable');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('enableOrDisable method', () => {
|
|
46
|
+
let mockClone: any;
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
mockClone = {
|
|
50
|
+
spec: { enabled: false },
|
|
51
|
+
save: jest.fn()
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
mockDispatch.mockImplementation((action: string) => {
|
|
55
|
+
if (action === 'rancher/clone') {
|
|
56
|
+
return Promise.resolve(mockClone);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return Promise.resolve();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should enable policy when flag is "enable"', async() => {
|
|
64
|
+
mockClone.save.mockResolvedValue({});
|
|
65
|
+
|
|
66
|
+
await auditPolicy.enableOrDisable('enable');
|
|
67
|
+
|
|
68
|
+
expect(mockClone.spec.enabled).toBe(true);
|
|
69
|
+
expect(mockClone.save).toHaveBeenCalledWith();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should disable policy when flag is "disable"', async() => {
|
|
73
|
+
mockClone.save.mockResolvedValue({});
|
|
74
|
+
|
|
75
|
+
await auditPolicy.enableOrDisable('disable');
|
|
76
|
+
|
|
77
|
+
expect(mockClone.spec.enabled).toBe(false);
|
|
78
|
+
expect(mockClone.save).toHaveBeenCalledWith();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should handle save errors and show growl notification', async() => {
|
|
82
|
+
const saveError = new Error('Save failed');
|
|
83
|
+
|
|
84
|
+
mockClone.save.mockRejectedValue(saveError);
|
|
85
|
+
mockT.mockReturnValue('Error when enabling - test-policy');
|
|
86
|
+
|
|
87
|
+
await auditPolicy.enableOrDisable('enable');
|
|
88
|
+
|
|
89
|
+
expect(mockDispatch).toHaveBeenCalledWith('growl/fromError', {
|
|
90
|
+
title: 'Error when enabling - test-policy',
|
|
91
|
+
err: saveError,
|
|
92
|
+
timeout: 5000
|
|
93
|
+
}, { root: true });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should call translation with correct parameters', async() => {
|
|
97
|
+
const saveError = new Error('Save failed');
|
|
98
|
+
|
|
99
|
+
mockClone.save.mockRejectedValue(saveError);
|
|
100
|
+
|
|
101
|
+
await auditPolicy.enableOrDisable('enable');
|
|
102
|
+
|
|
103
|
+
expect(mockT).toHaveBeenCalledWith('auditPolicy.error.enableOrDisable', {
|
|
104
|
+
flag: 'enable',
|
|
105
|
+
id: 'test-policy'
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should dispatch rancher/clone with correct parameters', async() => {
|
|
110
|
+
mockClone.save.mockResolvedValue({});
|
|
111
|
+
|
|
112
|
+
await auditPolicy.enableOrDisable('enable');
|
|
113
|
+
|
|
114
|
+
expect(mockDispatch).toHaveBeenCalledWith('rancher/clone', { resource: auditPolicy }, { root: true });
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -253,6 +253,8 @@ describe('class: Workload', () => {
|
|
|
253
253
|
});
|
|
254
254
|
|
|
255
255
|
describe('getter: podsCard', () => {
|
|
256
|
+
const mockPod = { metadata: { name: 'pod-1', namespace: 'default' } };
|
|
257
|
+
|
|
256
258
|
it('should return card for Deployment type', () => {
|
|
257
259
|
const workload = new Workload({
|
|
258
260
|
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
@@ -264,7 +266,7 @@ describe('class: Workload', () => {
|
|
|
264
266
|
rootGetters: { 'i18n/t': (key: string) => key },
|
|
265
267
|
});
|
|
266
268
|
|
|
267
|
-
Object.defineProperty(workload, 'pods', { get: () => [] });
|
|
269
|
+
Object.defineProperty(workload, 'pods', { get: () => [mockPod] });
|
|
268
270
|
Object.defineProperty(workload, 'canUpdate', { get: () => true });
|
|
269
271
|
|
|
270
272
|
const card = workload.podsCard;
|
|
@@ -285,7 +287,7 @@ describe('class: Workload', () => {
|
|
|
285
287
|
rootGetters: { 'i18n/t': (key: string) => key },
|
|
286
288
|
});
|
|
287
289
|
|
|
288
|
-
Object.defineProperty(workload, 'pods', { get: () => [] });
|
|
290
|
+
Object.defineProperty(workload, 'pods', { get: () => [mockPod] });
|
|
289
291
|
Object.defineProperty(workload, 'canUpdate', { get: () => true });
|
|
290
292
|
|
|
291
293
|
const card = workload.podsCard;
|
|
@@ -310,7 +312,7 @@ describe('class: Workload', () => {
|
|
|
310
312
|
expect(card).toBeNull();
|
|
311
313
|
});
|
|
312
314
|
|
|
313
|
-
it('should
|
|
315
|
+
it('should return null when pods array is empty', () => {
|
|
314
316
|
const workload = new Workload({
|
|
315
317
|
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
316
318
|
metadata: { name: 'test', namespace: 'default' },
|
|
@@ -322,6 +324,24 @@ describe('class: Workload', () => {
|
|
|
322
324
|
});
|
|
323
325
|
|
|
324
326
|
Object.defineProperty(workload, 'pods', { get: () => [] });
|
|
327
|
+
|
|
328
|
+
const card = workload.podsCard;
|
|
329
|
+
|
|
330
|
+
expect(card).toBeNull();
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('should hide scaling when canUpdate is false', () => {
|
|
334
|
+
const workload = new Workload({
|
|
335
|
+
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
336
|
+
metadata: { name: 'test', namespace: 'default' },
|
|
337
|
+
spec: {}
|
|
338
|
+
}, {
|
|
339
|
+
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
340
|
+
dispatch: jest.fn(),
|
|
341
|
+
rootGetters: { 'i18n/t': (key: string) => key },
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
Object.defineProperty(workload, 'pods', { get: () => [mockPod] });
|
|
325
345
|
Object.defineProperty(workload, 'canUpdate', { get: () => false });
|
|
326
346
|
|
|
327
347
|
const card = workload.podsCard;
|
|
@@ -331,6 +351,8 @@ describe('class: Workload', () => {
|
|
|
331
351
|
});
|
|
332
352
|
|
|
333
353
|
describe('getter: jobsCard', () => {
|
|
354
|
+
const mockJob = { metadata: { name: 'job-1', namespace: 'default' } };
|
|
355
|
+
|
|
334
356
|
it('should return card for CronJob type', () => {
|
|
335
357
|
const workload = new Workload({
|
|
336
358
|
type: WORKLOAD_TYPES.CRON_JOB,
|
|
@@ -342,7 +364,7 @@ describe('class: Workload', () => {
|
|
|
342
364
|
rootGetters: { 'i18n/t': (key: string) => key },
|
|
343
365
|
});
|
|
344
366
|
|
|
345
|
-
Object.defineProperty(workload, 'jobs', { get: () => [] });
|
|
367
|
+
Object.defineProperty(workload, 'jobs', { get: () => [mockJob] });
|
|
346
368
|
|
|
347
369
|
const card = workload.jobsCard;
|
|
348
370
|
|
|
@@ -366,9 +388,30 @@ describe('class: Workload', () => {
|
|
|
366
388
|
|
|
367
389
|
expect(card).toBeNull();
|
|
368
390
|
});
|
|
391
|
+
|
|
392
|
+
it('should return null when jobs array is empty', () => {
|
|
393
|
+
const workload = new Workload({
|
|
394
|
+
type: WORKLOAD_TYPES.CRON_JOB,
|
|
395
|
+
metadata: { name: 'test', namespace: 'default' },
|
|
396
|
+
spec: {}
|
|
397
|
+
}, {
|
|
398
|
+
getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
|
|
399
|
+
dispatch: jest.fn(),
|
|
400
|
+
rootGetters: { 'i18n/t': (key: string) => key },
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
Object.defineProperty(workload, 'jobs', { get: () => [] });
|
|
404
|
+
|
|
405
|
+
const card = workload.jobsCard;
|
|
406
|
+
|
|
407
|
+
expect(card).toBeNull();
|
|
408
|
+
});
|
|
369
409
|
});
|
|
370
410
|
|
|
371
411
|
describe('getter: cards', () => {
|
|
412
|
+
const mockPod = { metadata: { name: 'pod-1', namespace: 'default' } };
|
|
413
|
+
const mockJob = { metadata: { name: 'job-1', namespace: 'default' } };
|
|
414
|
+
|
|
372
415
|
it('should include podsCard for Deployment', () => {
|
|
373
416
|
const workload = new Workload({
|
|
374
417
|
type: WORKLOAD_TYPES.DEPLOYMENT,
|
|
@@ -384,7 +427,7 @@ describe('class: Workload', () => {
|
|
|
384
427
|
},
|
|
385
428
|
});
|
|
386
429
|
|
|
387
|
-
Object.defineProperty(workload, 'pods', { get: () => [] });
|
|
430
|
+
Object.defineProperty(workload, 'pods', { get: () => [mockPod] });
|
|
388
431
|
Object.defineProperty(workload, 'canUpdate', { get: () => true });
|
|
389
432
|
|
|
390
433
|
const cards = workload.cards;
|
|
@@ -411,7 +454,7 @@ describe('class: Workload', () => {
|
|
|
411
454
|
},
|
|
412
455
|
});
|
|
413
456
|
|
|
414
|
-
Object.defineProperty(workload, 'jobs', { get: () => [] });
|
|
457
|
+
Object.defineProperty(workload, 'jobs', { get: () => [mockJob] });
|
|
415
458
|
|
|
416
459
|
const cards = workload.cards;
|
|
417
460
|
const nonNullCards = cards.filter((c: any) => c !== null);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { insertAt } from '@shell/utils/array';
|
|
2
|
+
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
3
|
+
|
|
4
|
+
export default class AuditPolicy extends SteveModel {
|
|
5
|
+
get _availableActions() {
|
|
6
|
+
const out = super._availableActions;
|
|
7
|
+
|
|
8
|
+
insertAt(out, 0, {
|
|
9
|
+
action: 'enable',
|
|
10
|
+
label: this.t('action.enable'),
|
|
11
|
+
icon: 'icon icon-play',
|
|
12
|
+
enabled: (this.canEdit || this.canEditYaml) && !this.spec.enabled,
|
|
13
|
+
bulkable: true,
|
|
14
|
+
weight: 2,
|
|
15
|
+
});
|
|
16
|
+
insertAt(out, 0, {
|
|
17
|
+
action: 'disable',
|
|
18
|
+
label: this.t('action.disable'),
|
|
19
|
+
icon: 'icon icon-pause',
|
|
20
|
+
enabled: (this.canEdit || this.canEditYaml) && this.spec.enabled,
|
|
21
|
+
bulkable: true,
|
|
22
|
+
weight: 1,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
enable() {
|
|
29
|
+
this.enableOrDisable('enable');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
disable() {
|
|
33
|
+
this.enableOrDisable('disable');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async enableOrDisable(flag) {
|
|
37
|
+
const clone = await this.$dispatch('rancher/clone', { resource: this }, { root: true });
|
|
38
|
+
|
|
39
|
+
clone.spec.enabled = flag === 'enable';
|
|
40
|
+
await clone.save().catch((err) => {
|
|
41
|
+
this.$dispatch('growl/fromError', {
|
|
42
|
+
title: this.t('auditPolicy.error.enableOrDisable', { flag, id: this.id }), err, timeout: 5000
|
|
43
|
+
}, { root: true });
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -138,7 +138,7 @@ export default class CapiMachine extends SteveModel {
|
|
|
138
138
|
|
|
139
139
|
async machineRef() {
|
|
140
140
|
const ref = this.spec.infrastructureRef;
|
|
141
|
-
const id = `${
|
|
141
|
+
const id = `${ this.metadata.namespace }/${ ref.name }`;
|
|
142
142
|
const kind = `rke-machine.cattle.io.${ ref.kind.toLowerCase() }`;
|
|
143
143
|
|
|
144
144
|
return await this.$dispatch('find', { type: kind, id });
|
|
@@ -41,12 +41,12 @@ export default class CapiMachineDeployment extends SteveModel {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
get templateType() {
|
|
44
|
-
return this.
|
|
44
|
+
return this.infrastructureRefKind ? `rke-machine.cattle.io.${ this.infrastructureRefKind.toLowerCase() }` : null;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
get template() {
|
|
48
48
|
const ref = this.spec.template.spec.infrastructureRef;
|
|
49
|
-
const id = `${
|
|
49
|
+
const id = `${ this.metadata.namespace }/${ ref.name }`;
|
|
50
50
|
const template = this.$rootGetters['management/byId'](this.templateType, id);
|
|
51
51
|
|
|
52
52
|
return template;
|
|
@@ -92,15 +92,15 @@ export default class CapiMachineDeployment extends SteveModel {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
get outdated() {
|
|
95
|
-
return Math.max(0, (this.status?.replicas || 0) - (this.status?.
|
|
95
|
+
return Math.max(0, (this.status?.replicas || 0) - (this.status?.upToDateReplicas || 0));
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
get ready() {
|
|
99
|
-
return
|
|
99
|
+
return this.status?.availableReplicas || 0;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
get unavailable() {
|
|
103
|
-
return this.status?.
|
|
103
|
+
return Math.max(0, (this.status?.replicas || 0) - (this.status?.availableReplicas || 0));
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
get isControlPlane() {
|
package/models/event.js
CHANGED
|
@@ -38,4 +38,9 @@ export default class K8sEvent extends SteveModel {
|
|
|
38
38
|
|
|
39
39
|
return schema && rowValueGetter ? rowValueGetter(schema, 'Last Seen')(this) : null;
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
// Because we're using eventType which is a non-standard state we don't have a reliable way to provide a state color anymore and have therefore disabled the color.
|
|
43
|
+
get insightsColor() {
|
|
44
|
+
return 'disabled';
|
|
45
|
+
}
|
|
41
46
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
2
|
+
|
|
3
|
+
export default class GroupMembershipRefreshRequest extends SteveModel {
|
|
4
|
+
get canRefreshMemberships() {
|
|
5
|
+
return this.schema?.collectionMethods.find((x) => x.toLowerCase() === 'post');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
cleanForSave(data) {
|
|
9
|
+
const val = super.cleanForSave(data);
|
|
10
|
+
|
|
11
|
+
delete val.type;
|
|
12
|
+
|
|
13
|
+
return val;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
2
|
+
|
|
3
|
+
export default class PasswordChangeRequest extends SteveModel {
|
|
4
|
+
get canChangePassword() {
|
|
5
|
+
return this.schema?.collectionMethods.find((x) => x.toLowerCase() === 'post');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
cleanForSave(data) {
|
|
9
|
+
const val = super.cleanForSave(data);
|
|
10
|
+
|
|
11
|
+
delete val.type;
|
|
12
|
+
|
|
13
|
+
return val;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
2
|
+
|
|
3
|
+
export default class SelfUser extends SteveModel {
|
|
4
|
+
get canGetUser() {
|
|
5
|
+
return this.schema?.collectionMethods.find((x) => x.toLowerCase() === 'post');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
cleanForSave(data) {
|
|
9
|
+
const val = super.cleanForSave(data);
|
|
10
|
+
|
|
11
|
+
delete val.type;
|
|
12
|
+
|
|
13
|
+
return val;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { matching, convertSelectorObj } from '@shell/utils/selector';
|
|
2
2
|
import isEmpty from 'lodash/isEmpty';
|
|
3
3
|
import { escapeHtml } from '@shell/utils/string';
|
|
4
|
-
import { FLEET } from '@shell/config/types';
|
|
4
|
+
import { FLEET, MANAGEMENT } from '@shell/config/types';
|
|
5
5
|
import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
6
6
|
import { addObject, addObjects, findBy } from '@shell/utils/array';
|
|
7
7
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
@@ -30,18 +30,28 @@ function normalizeStateCounts(data) {
|
|
|
30
30
|
|
|
31
31
|
export default class FleetApplication extends SteveModel {
|
|
32
32
|
async getCurrentUser() {
|
|
33
|
-
const user = this.$rootGetters['auth/
|
|
33
|
+
const user = this.$rootGetters['auth/user'];
|
|
34
34
|
|
|
35
35
|
if (user?.id) {
|
|
36
36
|
return user;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const
|
|
40
|
-
url: '/v3/users?me=true',
|
|
41
|
-
method: 'get',
|
|
42
|
-
}, { root: true });
|
|
39
|
+
const selfUser = await this.$dispatch('auth/getSelfUser');
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
if (selfUser?.canGetUser && selfUser.status?.userID) {
|
|
42
|
+
const user = await this.$dispatch('management/find', {
|
|
43
|
+
type: MANAGEMENT.USER,
|
|
44
|
+
id: selfUser.status?.userID
|
|
45
|
+
}, { root: true });
|
|
46
|
+
|
|
47
|
+
if (user) {
|
|
48
|
+
this.$dispatch('auth/gotUser', user, { root: true });
|
|
49
|
+
|
|
50
|
+
return user;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {};
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
pause() {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { NORMAN } from '@shell/config/types';
|
|
2
|
-
import
|
|
1
|
+
import { NORMAN, EXT } from '@shell/config/types';
|
|
2
|
+
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
3
3
|
import day from 'dayjs';
|
|
4
4
|
|
|
5
|
-
export default class User extends
|
|
5
|
+
export default class User extends SteveModel {
|
|
6
6
|
// Preserve description
|
|
7
7
|
constructor(data, ctx, rehydrateNamespace = null, setClone = false) {
|
|
8
8
|
const _description = data.description;
|
|
@@ -11,16 +11,6 @@ export default class User extends HybridModel {
|
|
|
11
11
|
this.description = _description;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// Clean the Norman properties, but keep description
|
|
15
|
-
cleanResource(data) {
|
|
16
|
-
const desc = data.description;
|
|
17
|
-
const clean = cleanHybridResources(data);
|
|
18
|
-
|
|
19
|
-
clean._description = desc;
|
|
20
|
-
|
|
21
|
-
return clean;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
14
|
get isSystem() {
|
|
25
15
|
for ( const p of this.principalIds || [] ) {
|
|
26
16
|
if ( p.startsWith('system://') ) {
|
|
@@ -175,13 +165,13 @@ export default class User extends HybridModel {
|
|
|
175
165
|
const clone = await this.$dispatch('clone', { resource: this });
|
|
176
166
|
|
|
177
167
|
// Remove local properties
|
|
178
|
-
delete clone.
|
|
168
|
+
delete clone.canRefreshMemberships;
|
|
179
169
|
|
|
180
170
|
return clone._save(opt);
|
|
181
171
|
}
|
|
182
172
|
|
|
183
173
|
async setEnabled(enabled) {
|
|
184
|
-
const clone = await this.$dispatch('
|
|
174
|
+
const clone = await this.$dispatch('clone', { resource: this });
|
|
185
175
|
|
|
186
176
|
clone.enabled = enabled;
|
|
187
177
|
await clone.save();
|
|
@@ -204,12 +194,21 @@ export default class User extends HybridModel {
|
|
|
204
194
|
}
|
|
205
195
|
|
|
206
196
|
async refreshGroupMembership() {
|
|
207
|
-
const
|
|
208
|
-
type: NORMAN.USER,
|
|
209
|
-
id: this.id,
|
|
210
|
-
}, { root: true });
|
|
197
|
+
const membershipRefreshRequests = await this.$dispatch('create', { type: EXT.GROUP_MEMBERSHIP_REFRESH_REQUESTS });
|
|
211
198
|
|
|
212
|
-
|
|
199
|
+
// userId specifies the user ID. Use '*' for all users. Check the schemaDefinition for more details.
|
|
200
|
+
membershipRefreshRequests.spec = { userId: this.id };
|
|
201
|
+
await membershipRefreshRequests.save();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
get canRefreshMemberships() {
|
|
205
|
+
const schema = this.$getters[`schemaFor`](EXT.GROUP_MEMBERSHIP_REFRESH_REQUESTS);
|
|
206
|
+
|
|
207
|
+
if (!schema) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return schema?.collectionMethods.find((x) => x.toLowerCase() === 'post');
|
|
213
212
|
}
|
|
214
213
|
|
|
215
214
|
canActivate(state) {
|
|
@@ -243,7 +242,7 @@ export default class User extends HybridModel {
|
|
|
243
242
|
action: 'refreshGroupMembership',
|
|
244
243
|
label: this.t('authGroups.actions.refresh'),
|
|
245
244
|
icon: 'icon icon-refresh',
|
|
246
|
-
enabled: this.
|
|
245
|
+
enabled: this.canRefreshMemberships
|
|
247
246
|
},
|
|
248
247
|
{ divider: true },
|
|
249
248
|
...super._availableActions,
|
|
@@ -284,19 +283,17 @@ export default class User extends HybridModel {
|
|
|
284
283
|
return true;
|
|
285
284
|
}
|
|
286
285
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
286
|
+
cleanForSave(data) {
|
|
287
|
+
const val = super.cleanForSave(data);
|
|
290
288
|
|
|
291
|
-
|
|
292
|
-
return this.norman?.hasLink('remove') && !this.isCurrentUser;
|
|
293
|
-
}
|
|
289
|
+
delete val.type;
|
|
294
290
|
|
|
295
|
-
|
|
296
|
-
return this.norman?.hasLink('update');
|
|
291
|
+
return val;
|
|
297
292
|
}
|
|
298
293
|
|
|
299
|
-
|
|
300
|
-
|
|
294
|
+
get norman() {
|
|
295
|
+
console.warn('Norman "user" is deprecated. Use Steve "management.cattle.io.user" user instead.'); // eslint-disable-line no-console
|
|
296
|
+
|
|
297
|
+
return this.$rootGetters['rancher/byId'](NORMAN.USER, this.id);
|
|
301
298
|
}
|
|
302
299
|
}
|
package/models/schema.js
CHANGED
|
@@ -7,6 +7,24 @@ export default class Schema extends Resource {
|
|
|
7
7
|
get groupName() {
|
|
8
8
|
return this.attributes.namespaced ? 'ns' : 'cluster';
|
|
9
9
|
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Legacy check to determine if the user can GET a specific resource of this type.
|
|
13
|
+
*
|
|
14
|
+
* This supports things like spoof or norman schemas.
|
|
15
|
+
*/
|
|
16
|
+
get canGet() {
|
|
17
|
+
return this.hasLink('collection');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Legacy check to determine if the user can LIST a resource of this type.
|
|
22
|
+
*
|
|
23
|
+
* This supports things like spoof or norman schemas.
|
|
24
|
+
*/
|
|
25
|
+
get canList() {
|
|
26
|
+
return this.hasLink('collection');
|
|
27
|
+
}
|
|
10
28
|
}
|
|
11
29
|
|
|
12
30
|
/**
|
package/models/secret.js
CHANGED
|
@@ -3,6 +3,7 @@ import { CERTMANAGER, KUBERNETES, UI_PROJECT_SECRET, UI_PROJECT_SECRET_COPY } fr
|
|
|
3
3
|
import { base64Decode, base64Encode } from '@shell/utils/crypto';
|
|
4
4
|
import { removeObjects } from '@shell/utils/array';
|
|
5
5
|
import { MANAGEMENT, SERVICE_ACCOUNT, VIRTUAL_TYPES } from '@shell/config/types';
|
|
6
|
+
import { SECRET_SCOPE, SECRET_QUERY_PARAMS } from '@shell/config/query-params';
|
|
6
7
|
import { set } from '@shell/utils/object';
|
|
7
8
|
import { NAME as MANAGER } from '@shell/config/product/manager';
|
|
8
9
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
|
@@ -491,18 +492,16 @@ export default class Secret extends SteveModel {
|
|
|
491
492
|
return steveCleanForDownload(yaml, { rootKeys: ['id', 'links', 'actions'] });
|
|
492
493
|
}
|
|
493
494
|
|
|
494
|
-
/**
|
|
495
|
-
* is this a project scoped secret .... or also a cloned project scoped secret
|
|
496
|
-
*/
|
|
497
|
-
get isProjectScopedRelated() {
|
|
498
|
-
return !!this.metadata.labels?.[UI_PROJECT_SECRET];
|
|
499
|
-
}
|
|
500
|
-
|
|
501
495
|
/**
|
|
502
496
|
* is this a project scoped secret
|
|
503
497
|
*/
|
|
504
498
|
get isProjectScoped() {
|
|
505
|
-
|
|
499
|
+
/**
|
|
500
|
+
* is this a project scoped secret .... or also a cloned project scoped secret
|
|
501
|
+
*/
|
|
502
|
+
const isProjectScopedRelated = !!this.metadata.labels?.[UI_PROJECT_SECRET];
|
|
503
|
+
|
|
504
|
+
return isProjectScopedRelated && !this.isProjectSecretCopy && this.$rootGetters['isRancher'];
|
|
506
505
|
}
|
|
507
506
|
|
|
508
507
|
get projectScopedClusterId() {
|
|
@@ -588,34 +587,38 @@ export default class Secret extends SteveModel {
|
|
|
588
587
|
}
|
|
589
588
|
|
|
590
589
|
get listLocation() {
|
|
591
|
-
if (
|
|
592
|
-
return
|
|
590
|
+
if (this.hasProjectScopedUrlQueryParam || this.isProjectScoped) {
|
|
591
|
+
return {
|
|
592
|
+
name: 'c-cluster-product-resource',
|
|
593
|
+
params: {
|
|
594
|
+
product: this.$rootGetters['productId'],
|
|
595
|
+
cluster: this.$rootGetters['clusterId'],
|
|
596
|
+
resource: VIRTUAL_TYPES.PROJECT_SECRETS,
|
|
597
|
+
}
|
|
598
|
+
};
|
|
593
599
|
}
|
|
594
600
|
|
|
595
|
-
return
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
resource: VIRTUAL_TYPES.PROJECT_SECRETS,
|
|
601
|
-
}
|
|
602
|
-
};
|
|
601
|
+
return super.listLocation;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
get hasProjectScopedUrlQueryParam() {
|
|
605
|
+
return this.currentRoute()?.query?.[SECRET_SCOPE] === SECRET_QUERY_PARAMS.PROJECT_SCOPED;
|
|
603
606
|
}
|
|
604
607
|
|
|
605
608
|
get parentNameOverride() {
|
|
606
|
-
if (
|
|
607
|
-
return
|
|
609
|
+
if (this.hasProjectScopedUrlQueryParam || this.isProjectScoped) {
|
|
610
|
+
return this.$rootGetters['i18n/t'](`typeLabel."${ VIRTUAL_TYPES.PROJECT_SECRETS }"`, { count: 1 })?.trim();
|
|
608
611
|
}
|
|
609
612
|
|
|
610
|
-
return
|
|
613
|
+
return super.parentNameOverride;
|
|
611
614
|
}
|
|
612
615
|
|
|
613
616
|
get parentLocationOverride() {
|
|
614
|
-
if (
|
|
615
|
-
return
|
|
617
|
+
if (this.hasProjectScopedUrlQueryParam || this.isProjectScoped) {
|
|
618
|
+
return this.listLocation;
|
|
616
619
|
}
|
|
617
620
|
|
|
618
|
-
return
|
|
621
|
+
return super.parentLocationOverride;
|
|
619
622
|
}
|
|
620
623
|
|
|
621
624
|
get groupByProject() {
|