@rancher/shell 0.3.18 → 0.3.19
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/components/PromptRestore.vue +1 -1
- package/components/__tests__/PromptRestore.test.ts +91 -21
- package/components/auth/RoleDetailEdit.vue +2 -0
- package/components/nav/WindowManager/ContainerShell.vue +1 -1
- package/components/nav/WindowManager/__tests__/ContainerShell.test.ts +14 -14
- package/dialog/DiagnosticTimingsDialog.vue +1 -0
- package/edit/ui.cattle.io.navlink.vue +2 -1
- package/models/etcdbackup.js +2 -1
- package/models/management.cattle.io.cluster.js +13 -3
- package/package.json +1 -1
- package/pages/c/_cluster/apps/charts/install.vue +74 -25
- package/pages/diagnostic.vue +5 -3
- package/scripts/extension/publish +54 -14
- package/types/shell/index.d.ts +1 -0
- package/utils/socket.js +1 -0
|
@@ -116,7 +116,7 @@ export default {
|
|
|
116
116
|
|
|
117
117
|
if (!cluster?.isRke2) {
|
|
118
118
|
promise = this.$store.dispatch('rancher/findAll', { type: NORMAN.ETCD_BACKUP }).then((snapshots) => {
|
|
119
|
-
return snapshots.filter((s) => s.clusterId === cluster.metadata.name);
|
|
119
|
+
return snapshots.filter((s) => s.state === STATES_ENUM.ACTIVE && s.clusterId === cluster.metadata.name);
|
|
120
120
|
});
|
|
121
121
|
} else {
|
|
122
122
|
promise = this.$store.dispatch('management/findAll', { type: SNAPSHOT }).then((snapshots) => {
|
|
@@ -3,55 +3,87 @@ import PromptRestore from '@shell/components/PromptRestore.vue';
|
|
|
3
3
|
import Vuex from 'vuex';
|
|
4
4
|
import { ExtendedVue, Vue } from 'vue/types/vue';
|
|
5
5
|
import { DefaultProps } from 'vue/types/options';
|
|
6
|
-
import { CAPI } from '@shell/config/types';
|
|
6
|
+
import { CAPI, NORMAN } from '@shell/config/types';
|
|
7
7
|
import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const RKE2_CLUSTER_NAME = 'rke2_cluster_name';
|
|
10
|
+
const RKE2_SUCCESSFUL_SNAPSHOT_1 = {
|
|
11
|
+
clusterName: RKE2_CLUSTER_NAME,
|
|
11
12
|
type: CAPI.RANCHER_CLUSTER,
|
|
12
13
|
created: 'Thu Jul 20 2023 11:11:39',
|
|
13
14
|
snapshotFile: { status: STATES_ENUM.SUCCESSFUL },
|
|
14
|
-
id: '
|
|
15
|
-
name: '
|
|
15
|
+
id: 'rke2_id_1',
|
|
16
|
+
name: 'rke2_name_1'
|
|
16
17
|
};
|
|
17
|
-
const
|
|
18
|
-
|
|
18
|
+
const RKE2_SUCCESSFUL_SNAPSHOT_2 = {
|
|
19
|
+
clusterName: RKE2_CLUSTER_NAME,
|
|
19
20
|
type: CAPI.RANCHER_CLUSTER,
|
|
20
21
|
created: 'Thu Jul 20 2022 11:11:39',
|
|
21
22
|
snapshotFile: { status: STATES_ENUM.SUCCESSFUL },
|
|
22
|
-
id: '
|
|
23
|
-
name: '
|
|
23
|
+
id: 'rke2_id_2',
|
|
24
|
+
name: 'rke2_name_2'
|
|
24
25
|
};
|
|
25
|
-
const
|
|
26
|
-
|
|
26
|
+
const RKE2_FAILED_SNAPSHOT = {
|
|
27
|
+
clusterName: RKE2_CLUSTER_NAME,
|
|
27
28
|
type: CAPI.RANCHER_CLUSTER,
|
|
28
29
|
created: 'Thu Jul 20 2021 11:11:39',
|
|
29
30
|
snapshotFile: { status: STATES_ENUM.FAILED },
|
|
30
|
-
id: '
|
|
31
|
-
name: '
|
|
31
|
+
id: 'rke2_id_3',
|
|
32
|
+
name: 'rke2_name_3'
|
|
32
33
|
};
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
const RKE1_CLUSTER_NAME = 'rke1_cluster_name';
|
|
36
|
+
const RKE1_SUCCESSFUL_SNAPSHOT_1 = {
|
|
37
|
+
clusterId: RKE1_CLUSTER_NAME,
|
|
38
|
+
type: NORMAN.ETCD_BACKUP,
|
|
39
|
+
state: STATES_ENUM.ACTIVE,
|
|
40
|
+
created: 'Thu Jul 30 2023 11:11:39',
|
|
41
|
+
id: 'rke1_id_1',
|
|
42
|
+
name: 'rke1_name_1'
|
|
43
|
+
};
|
|
44
|
+
const RKE1_SUCCESSFUL_SNAPSHOT_2 = {
|
|
45
|
+
clusterId: RKE1_CLUSTER_NAME,
|
|
46
|
+
type: NORMAN.ETCD_BACKUP,
|
|
47
|
+
state: STATES_ENUM.ACTIVE,
|
|
48
|
+
created: 'Thu Jul 30 2022 11:11:39',
|
|
49
|
+
id: 'rke1_id_2',
|
|
50
|
+
name: 'rke1_name_2'
|
|
51
|
+
};
|
|
52
|
+
const RKE1_WITH_ERROR_SNAPSHOT = {
|
|
53
|
+
clusterId: RKE1_CLUSTER_NAME,
|
|
54
|
+
type: NORMAN.ETCD_BACKUP,
|
|
55
|
+
state: STATES_ENUM.ERROR,
|
|
56
|
+
created: 'Thu Jul 30 2021 11:11:39',
|
|
57
|
+
id: 'rke1_id_3',
|
|
58
|
+
name: 'rke1_name_3'
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
describe('component: PromptRestore', () => {
|
|
35
62
|
const localVue = createLocalVue();
|
|
36
63
|
|
|
37
64
|
localVue.use(Vuex);
|
|
38
65
|
|
|
39
|
-
const
|
|
66
|
+
const rke2TestCases = [
|
|
40
67
|
[[], 0],
|
|
41
|
-
[[
|
|
42
|
-
[[
|
|
43
|
-
[[
|
|
44
|
-
[[
|
|
68
|
+
[[RKE2_FAILED_SNAPSHOT], 0],
|
|
69
|
+
[[RKE2_SUCCESSFUL_SNAPSHOT_1], 1],
|
|
70
|
+
[[RKE2_SUCCESSFUL_SNAPSHOT_1, RKE2_SUCCESSFUL_SNAPSHOT_2], 2],
|
|
71
|
+
[[RKE2_FAILED_SNAPSHOT, RKE2_SUCCESSFUL_SNAPSHOT_1, RKE2_SUCCESSFUL_SNAPSHOT_2], 2]
|
|
45
72
|
];
|
|
46
73
|
|
|
47
|
-
it.each(
|
|
74
|
+
it.each(rke2TestCases)('should list RKE2 snapshots properly', async(snapShots, expected) => {
|
|
48
75
|
const store = new Vuex.Store({
|
|
49
76
|
modules: {
|
|
50
77
|
'action-menu': {
|
|
51
78
|
namespaced: true,
|
|
52
79
|
state: {
|
|
53
80
|
showPromptRestore: true,
|
|
54
|
-
toRestore:
|
|
81
|
+
toRestore: [{
|
|
82
|
+
isRke2: true,
|
|
83
|
+
type: CAPI.RANCHER_CLUSTER,
|
|
84
|
+
metadata: { name: RKE2_CLUSTER_NAME },
|
|
85
|
+
snapShots
|
|
86
|
+
}]
|
|
55
87
|
},
|
|
56
88
|
},
|
|
57
89
|
},
|
|
@@ -69,4 +101,42 @@ describe('component: GrowlManager', () => {
|
|
|
69
101
|
|
|
70
102
|
expect(wrapper.vm.clusterSnapshots).toHaveLength(expected);
|
|
71
103
|
});
|
|
104
|
+
|
|
105
|
+
const rke1TestCases = [
|
|
106
|
+
[[], 0],
|
|
107
|
+
[[RKE1_WITH_ERROR_SNAPSHOT], 0],
|
|
108
|
+
[[RKE1_SUCCESSFUL_SNAPSHOT_1], 1],
|
|
109
|
+
[[RKE1_SUCCESSFUL_SNAPSHOT_1, RKE1_SUCCESSFUL_SNAPSHOT_2], 2],
|
|
110
|
+
[[RKE1_WITH_ERROR_SNAPSHOT, RKE1_SUCCESSFUL_SNAPSHOT_1, RKE1_SUCCESSFUL_SNAPSHOT_2], 2]
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
it.each(rke1TestCases)('should list RKE1 snapshots properly', async(snapShots, expected) => {
|
|
114
|
+
const store = new Vuex.Store({
|
|
115
|
+
modules: {
|
|
116
|
+
'action-menu': {
|
|
117
|
+
namespaced: true,
|
|
118
|
+
state: {
|
|
119
|
+
showPromptRestore: true,
|
|
120
|
+
toRestore: [{
|
|
121
|
+
type: CAPI.RANCHER_CLUSTER,
|
|
122
|
+
metadata: { name: RKE1_CLUSTER_NAME },
|
|
123
|
+
snapShots
|
|
124
|
+
}]
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
getters: { 'i18n/t': () => jest.fn(), 'prefs/get': () => jest.fn() },
|
|
129
|
+
actions: { 'rancher/findAll': jest.fn().mockResolvedValue(snapShots) }
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const wrapper = shallowMount(PromptRestore as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
|
|
133
|
+
store,
|
|
134
|
+
localVue
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
await wrapper.vm.fetchSnapshots();
|
|
138
|
+
await wrapper.vm.$nextTick();
|
|
139
|
+
|
|
140
|
+
expect(wrapper.vm.clusterSnapshots).toHaveLength(expected);
|
|
141
|
+
});
|
|
72
142
|
});
|
|
@@ -579,6 +579,7 @@ export default {
|
|
|
579
579
|
name="storageSource"
|
|
580
580
|
:label="defaultLabel"
|
|
581
581
|
class="mb-10"
|
|
582
|
+
data-testid="roletemplate-creator-default-options"
|
|
582
583
|
:options="newUserDefaultOptions"
|
|
583
584
|
:mode="mode"
|
|
584
585
|
/>
|
|
@@ -592,6 +593,7 @@ export default {
|
|
|
592
593
|
name="storageSource"
|
|
593
594
|
:label="t('rbac.roletemplate.locked.label')"
|
|
594
595
|
class="mb-10"
|
|
596
|
+
data-testid="roletemplate-locked-options"
|
|
595
597
|
:options="lockedOptions"
|
|
596
598
|
:mode="mode"
|
|
597
599
|
/>
|
|
@@ -288,7 +288,7 @@ export default {
|
|
|
288
288
|
this.pod.os = undefined;
|
|
289
289
|
// the pod will still return an os if one's been defined in the node so we'll skip the backups if that's the case and rely on retry count to break the retry loop
|
|
290
290
|
if (!this.pod.os) {
|
|
291
|
-
this.os =
|
|
291
|
+
this.os = undefined;
|
|
292
292
|
}
|
|
293
293
|
this.connect();
|
|
294
294
|
} else {
|
|
@@ -322,11 +322,11 @@ describe('component: ContainerShell', () => {
|
|
|
322
322
|
expect(wrapper.vm.isOpening).toBe(false);
|
|
323
323
|
expect(wrapper.vm.errorMsg).toBe('eventMessageError');
|
|
324
324
|
// the backup shell that was leftover was windows so it became the new os in dataprops
|
|
325
|
-
expect(wrapper.vm.os).
|
|
325
|
+
expect(wrapper.vm.os).toBeUndefined();
|
|
326
326
|
// but we still didn't write it to the pod itself since we don't know if it worked
|
|
327
327
|
expect(defaultContainerShellParams.propsData.pod.os).toBeUndefined();
|
|
328
328
|
// we can see here that we removed that last backup shell because we're attempting to use it now
|
|
329
|
-
expect(wrapper.vm.backupShells).toHaveLength(
|
|
329
|
+
expect(wrapper.vm.backupShells).toHaveLength(1);
|
|
330
330
|
expect(connect.mock.calls).toHaveLength(2);
|
|
331
331
|
});
|
|
332
332
|
|
|
@@ -351,8 +351,8 @@ describe('component: ContainerShell', () => {
|
|
|
351
351
|
eventMessage({ detail: { data: `3${ linuxErrorMessage }` } });
|
|
352
352
|
eventDisconnected();
|
|
353
353
|
|
|
354
|
-
expect(wrapper.vm.backupShells).toHaveLength(
|
|
355
|
-
expect(wrapper.vm.os).
|
|
354
|
+
expect(wrapper.vm.backupShells).toHaveLength(1);
|
|
355
|
+
expect(wrapper.vm.os).toBeUndefined();
|
|
356
356
|
// the pod os was 'linux' but we cleared it out since that didn't work
|
|
357
357
|
expect(defaultContainerShellParams.propsData.pod.os).toBeUndefined();
|
|
358
358
|
expect(connect.mock.calls).toHaveLength(2);
|
|
@@ -367,11 +367,11 @@ describe('component: ContainerShell', () => {
|
|
|
367
367
|
expect(wrapper.vm.isOpen).toBe(false);
|
|
368
368
|
expect(wrapper.vm.isOpening).toBe(false);
|
|
369
369
|
expect(wrapper.vm.errorMsg).toBe(windowsErrorMessage);
|
|
370
|
-
expect(wrapper.vm.os).
|
|
370
|
+
expect(wrapper.vm.os).toBeUndefined();
|
|
371
371
|
// we never found a shell that worked so we're going to leave the pod os as undefined
|
|
372
372
|
expect(defaultContainerShellParams.propsData.pod.os).toBeUndefined();
|
|
373
373
|
// we're out of backupShells now so we're not going to retry after that second disconnect
|
|
374
|
-
expect(connect.mock.calls).toHaveLength(
|
|
374
|
+
expect(connect.mock.calls).toHaveLength(3);
|
|
375
375
|
|
|
376
376
|
resetMocks();
|
|
377
377
|
});
|
|
@@ -409,8 +409,8 @@ describe('component: ContainerShell', () => {
|
|
|
409
409
|
eventMessage({ detail: { data: `3${ linuxErrorMessage }` } });
|
|
410
410
|
eventDisconnected();
|
|
411
411
|
|
|
412
|
-
expect(wrapper.vm.backupShells).toHaveLength(
|
|
413
|
-
expect(wrapper.vm.os).
|
|
412
|
+
expect(wrapper.vm.backupShells).toHaveLength(1);
|
|
413
|
+
expect(wrapper.vm.os).toBeUndefined();
|
|
414
414
|
expect(testUndefinedOsParams.propsData.pod.os).toBeUndefined();
|
|
415
415
|
|
|
416
416
|
eventConnecting();
|
|
@@ -423,9 +423,9 @@ describe('component: ContainerShell', () => {
|
|
|
423
423
|
expect(wrapper.vm.isOpen).toBe(false);
|
|
424
424
|
expect(wrapper.vm.isOpening).toBe(false);
|
|
425
425
|
expect(wrapper.vm.errorMsg).toBe(windowsErrorMessage);
|
|
426
|
-
expect(wrapper.vm.os).
|
|
426
|
+
expect(wrapper.vm.os).toBeUndefined();
|
|
427
427
|
expect(testUndefinedOsParams.propsData.pod.os).toBeUndefined();
|
|
428
|
-
expect(connect.mock.calls).toHaveLength(
|
|
428
|
+
expect(connect.mock.calls).toHaveLength(3);
|
|
429
429
|
|
|
430
430
|
resetMocks();
|
|
431
431
|
});
|
|
@@ -461,8 +461,8 @@ describe('component: ContainerShell', () => {
|
|
|
461
461
|
eventMessage({ detail: { data: `3${ linuxErrorMessage }` } });
|
|
462
462
|
eventDisconnected();
|
|
463
463
|
|
|
464
|
-
expect(wrapper.vm.backupShells).toHaveLength(
|
|
465
|
-
expect(wrapper.vm.os).
|
|
464
|
+
expect(wrapper.vm.backupShells).toHaveLength(1);
|
|
465
|
+
expect(wrapper.vm.os).toBeUndefined();
|
|
466
466
|
expect(testUndefinedOsParams.propsData.pod.os).toBeUndefined();
|
|
467
467
|
expect(wrapper.vm.errorMsg).toBe(linuxErrorMessage);
|
|
468
468
|
|
|
@@ -475,9 +475,9 @@ describe('component: ContainerShell', () => {
|
|
|
475
475
|
expect(wrapper.vm.isOpen).toBe(true);
|
|
476
476
|
expect(wrapper.vm.isOpening).toBe(false);
|
|
477
477
|
expect(wrapper.vm.errorMsg).toBe('');
|
|
478
|
-
expect(wrapper.vm.os).
|
|
478
|
+
expect(wrapper.vm.os).toBeUndefined();
|
|
479
479
|
// the second shell worked so we're going to set it on the pod itself so if we need to connect again we'll just use the correct shell on the first attempt
|
|
480
|
-
expect(testUndefinedOsParams.propsData.pod.os).
|
|
480
|
+
expect(testUndefinedOsParams.propsData.pod.os).toBeUndefined();
|
|
481
481
|
expect(connect.mock.calls).toHaveLength(2);
|
|
482
482
|
|
|
483
483
|
resetMocks();
|
|
@@ -8,9 +8,10 @@ import { LabeledInput } from '@components/Form/LabeledInput';
|
|
|
8
8
|
import { RadioGroup } from '@components/Form/Radio';
|
|
9
9
|
import FileImageSelector from '@shell/components/form/FileImageSelector';
|
|
10
10
|
import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
|
|
11
|
-
import NameNsDescription
|
|
11
|
+
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
12
12
|
import { Banner } from '@components/Banner';
|
|
13
13
|
import FormValidation from '@shell/mixins/form-validation';
|
|
14
|
+
import { normalizeName } from '@shell/utils/kube';
|
|
14
15
|
|
|
15
16
|
const LINK_TYPE_URL = 'url';
|
|
16
17
|
const LINK_TYPE_SERVICE = 'service';
|
package/models/etcdbackup.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import NormanModel from '@shell/plugins/steve/norman-class';
|
|
2
|
+
import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
|
|
2
3
|
|
|
3
4
|
export default class Rke1EtcdBackup extends NormanModel {
|
|
4
5
|
get _availableActions() {
|
|
5
6
|
const restore = {
|
|
6
7
|
action: 'promptRestore',
|
|
7
|
-
enabled:
|
|
8
|
+
enabled: this.state === STATES_ENUM.ACTIVE,
|
|
8
9
|
icon: 'icon icon-fw icon-backup-restore',
|
|
9
10
|
label: 'Restore'
|
|
10
11
|
};
|
|
@@ -19,6 +19,13 @@ import { KONTAINER_TO_DRIVER } from './management.cattle.io.kontainerdriver';
|
|
|
19
19
|
// If the logo is not named with the provider name, add an override here
|
|
20
20
|
const PROVIDER_LOGO_OVERRIDE = {};
|
|
21
21
|
|
|
22
|
+
function findRelationship(verb, type, relationships = []) {
|
|
23
|
+
const from = `${ verb }Type`;
|
|
24
|
+
const id = `${ verb }Id`;
|
|
25
|
+
|
|
26
|
+
return relationships.find((r) => r[from] === type)?.[id];
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
export default class MgmtCluster extends HybridModel {
|
|
23
30
|
get details() {
|
|
24
31
|
const out = [
|
|
@@ -440,9 +447,12 @@ export default class MgmtCluster extends HybridModel {
|
|
|
440
447
|
// cluster has the less human readable management cluster ID in it: fleet-default/c-khk48
|
|
441
448
|
|
|
442
449
|
const verb = this.isLocal || isRKE1 || this.isHostedKubernetesProvider ? 'to' : 'from';
|
|
443
|
-
const
|
|
444
|
-
|
|
450
|
+
const res = findRelationship(verb, CAPI.RANCHER_CLUSTER, this.metadata?.relationships);
|
|
451
|
+
|
|
452
|
+
if (res) {
|
|
453
|
+
return res;
|
|
454
|
+
}
|
|
445
455
|
|
|
446
|
-
return
|
|
456
|
+
return findRelationship(verb === 'to' ? 'from' : 'to', CAPI.RANCHER_CLUSTER, this.metadata?.relationships);
|
|
447
457
|
}
|
|
448
458
|
}
|
package/package.json
CHANGED
|
@@ -81,6 +81,9 @@ export default {
|
|
|
81
81
|
],
|
|
82
82
|
|
|
83
83
|
async fetch() {
|
|
84
|
+
this.errors = [];
|
|
85
|
+
// IMPORTANT! Any exception thrown before this.value is set will result in an empty page
|
|
86
|
+
|
|
84
87
|
/*
|
|
85
88
|
fetchChart is defined in shell/mixins. It first checks the URL
|
|
86
89
|
query for an app name and namespace. It uses those values to check
|
|
@@ -91,23 +94,43 @@ export default {
|
|
|
91
94
|
it checks for target name and namespace values defined in the
|
|
92
95
|
Helm chart itself.
|
|
93
96
|
*/
|
|
94
|
-
|
|
97
|
+
try {
|
|
98
|
+
await this.fetchChart();
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.warn('Unable to fetch chart: ', e); // eslint-disable-line no-console
|
|
101
|
+
}
|
|
95
102
|
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
try {
|
|
104
|
+
await this.fetchAutoInstallInfo();
|
|
105
|
+
} catch (e) {
|
|
106
|
+
console.warn('Unable to determine if other charts require install: ', e); // eslint-disable-line no-console
|
|
107
|
+
}
|
|
98
108
|
|
|
99
109
|
// If the chart doesn't contain system `systemDefaultRegistry` properties there's no point applying them
|
|
100
110
|
if (this.showCustomRegistry) {
|
|
101
111
|
// Note: Cluster scoped registry is only supported for node driver clusters
|
|
102
|
-
|
|
103
|
-
|
|
112
|
+
try {
|
|
113
|
+
this.clusterRegistry = await this.getClusterRegistry();
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.warn('Unable to get cluster registry: ', e); // eslint-disable-line no-console
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
this.globalRegistry = await this.getGlobalRegistry();
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.warn('Unable to get global registry: ', e); // eslint-disable-line no-console
|
|
122
|
+
}
|
|
104
123
|
this.defaultRegistrySetting = this.clusterRegistry || this.globalRegistry;
|
|
105
124
|
}
|
|
106
125
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
126
|
+
try {
|
|
127
|
+
this.serverUrlSetting = await this.$store.dispatch('management/find', {
|
|
128
|
+
type: MANAGEMENT.SETTING,
|
|
129
|
+
id: 'server-url'
|
|
130
|
+
});
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.error('Unable to fetch `server-url` setting: ', e); // eslint-disable-line no-console
|
|
133
|
+
}
|
|
111
134
|
|
|
112
135
|
/*
|
|
113
136
|
Figure out the namespace where the chart is
|
|
@@ -137,20 +160,38 @@ export default {
|
|
|
137
160
|
}
|
|
138
161
|
|
|
139
162
|
/* Check if the app is deprecated. */
|
|
140
|
-
|
|
163
|
+
try {
|
|
164
|
+
this.legacyApp = this.existing ? await this.existing.deployedAsLegacy() : false;
|
|
165
|
+
} catch (e) {
|
|
166
|
+
this.legacyApp = false;
|
|
167
|
+
console.warn('Unable to determine if existing install is a legacy app: ', e); // eslint-disable-line no-console
|
|
168
|
+
}
|
|
141
169
|
|
|
142
170
|
/* Check if the app is a multicluster deprecated app.
|
|
143
171
|
(Multicluster apps were replaced by Fleet.) */
|
|
144
|
-
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
this.mcapp = this.existing ? await this.existing.deployedAsMultiCluster() : false;
|
|
175
|
+
} catch (e) {
|
|
176
|
+
this.mcapp = false;
|
|
177
|
+
console.warn('Unable to determine if existing install is a mc app: ', e); // eslint-disable-line no-console
|
|
178
|
+
}
|
|
145
179
|
|
|
146
180
|
/* The form state is intialized as a chartInstallAction resource. */
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
181
|
+
try {
|
|
182
|
+
this.value = await this.$store.dispatch('cluster/create', {
|
|
183
|
+
type: 'chartInstallAction',
|
|
184
|
+
metadata: {
|
|
185
|
+
namespace: this.forceNamespace || this.$store.getters['defaultNamespace'],
|
|
186
|
+
name: this.existing?.spec?.name || this.query.appName || '',
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
} catch (e) {
|
|
190
|
+
console.error('Unable to create object of type `chartInstallAction`: ', e); // eslint-disable-line no-console
|
|
191
|
+
|
|
192
|
+
// Nothing's going to work without a `value`. See https://github.com/rancher/dashboard/issues/9452 to handle this and other catches.
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
154
195
|
|
|
155
196
|
/* Logic for when the Helm chart is not already installed */
|
|
156
197
|
if ( !this.existing) {
|
|
@@ -803,12 +844,19 @@ export default {
|
|
|
803
844
|
|
|
804
845
|
if (hasPermissionToSeeProvCluster) {
|
|
805
846
|
const mgmCluster = this.$store.getters['currentCluster'];
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
847
|
+
const provClusterId = mgmCluster?.provClusterId;
|
|
848
|
+
let provCluster;
|
|
849
|
+
|
|
850
|
+
try {
|
|
851
|
+
provCluster = provClusterId ? await this.$store.dispatch('management/find', {
|
|
852
|
+
type: CAPI.RANCHER_CLUSTER,
|
|
853
|
+
id: provClusterId
|
|
854
|
+
}) : {};
|
|
855
|
+
} catch (e) {
|
|
856
|
+
console.error(`Unable to fetch prov cluster '${ provClusterId }': `, e); // eslint-disable-line no-console
|
|
857
|
+
}
|
|
810
858
|
|
|
811
|
-
if (provCluster
|
|
859
|
+
if (provCluster?.isRke2) { // isRke2 returns true for both RKE2 and K3s clusters.
|
|
812
860
|
const agentConfig = provCluster.spec?.rkeConfig?.machineSelectorConfig?.find((x) => !x.machineLabelSelector).config;
|
|
813
861
|
|
|
814
862
|
// If a cluster scoped registry exists,
|
|
@@ -819,10 +867,11 @@ export default {
|
|
|
819
867
|
return clusterRegistry;
|
|
820
868
|
}
|
|
821
869
|
}
|
|
822
|
-
|
|
870
|
+
|
|
871
|
+
if (provCluster?.isRke1) {
|
|
823
872
|
// For RKE1 clusters, the cluster scoped private registry is on the management
|
|
824
873
|
// cluster, not the provisioning cluster.
|
|
825
|
-
const rke1Registries = mgmCluster
|
|
874
|
+
const rke1Registries = mgmCluster?.spec?.rancherKubernetesEngineConfig?.privateRegistries;
|
|
826
875
|
|
|
827
876
|
if (rke1Registries?.length > 0) {
|
|
828
877
|
const defaultRegistry = rke1Registries.find((registry) => {
|
package/pages/diagnostic.vue
CHANGED
|
@@ -162,9 +162,8 @@ export default {
|
|
|
162
162
|
},
|
|
163
163
|
|
|
164
164
|
downloadData(btnCb) {
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
const fileName = `rancher-diagnostic-data-${ date }-${ time.replaceAll(':', '_') }.json`;
|
|
165
|
+
// simplify filename due to e2e tests
|
|
166
|
+
const fileName = 'rancher-diagnostic-data.json';
|
|
168
167
|
const data = {
|
|
169
168
|
systemInformation: this.systemInformation,
|
|
170
169
|
logs: this.latestLogs,
|
|
@@ -235,6 +234,7 @@ export default {
|
|
|
235
234
|
'aws',
|
|
236
235
|
'digitalocean',
|
|
237
236
|
'linode',
|
|
237
|
+
'targetRoute', // contains circular references, isn't useful (added later to store)
|
|
238
238
|
];
|
|
239
239
|
|
|
240
240
|
const clearListsKeys = [
|
|
@@ -316,10 +316,12 @@ export default {
|
|
|
316
316
|
<AsyncButton
|
|
317
317
|
mode="timing"
|
|
318
318
|
class="mr-20"
|
|
319
|
+
data-testid="diagnostics-generate-response-times"
|
|
319
320
|
@click="gatherResponseTimes"
|
|
320
321
|
/>
|
|
321
322
|
<AsyncButton
|
|
322
323
|
mode="diagnostic"
|
|
324
|
+
data-testid="diagnostics-download-diagnostic-package"
|
|
323
325
|
@click="promptDownload"
|
|
324
326
|
/>
|
|
325
327
|
</div>
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
|
4
4
|
BASE_DIR="$(pwd)"
|
|
5
|
-
BASE_EXT=$(basename ${BASE_DIR})
|
|
6
5
|
|
|
7
6
|
CYAN="\033[96m"
|
|
8
7
|
YELLOW="\033[93m"
|
|
@@ -17,6 +16,7 @@ REGISTRY_ORG=""
|
|
|
17
16
|
IMAGE_PREFIX="ui-extension-"
|
|
18
17
|
FORCE="false"
|
|
19
18
|
GITHUB_BUILD="true"
|
|
19
|
+
GITHUB_RELEASE_TAG=""
|
|
20
20
|
|
|
21
21
|
GITHUB_SOURCE=$(git config --get remote.origin.url | sed -e 's/^git@.*:\([[:graph:]]*\).git/\1/')
|
|
22
22
|
GITHUB_BRANCH="main"
|
|
@@ -24,18 +24,19 @@ GITHUB_BRANCH="main"
|
|
|
24
24
|
usage() {
|
|
25
25
|
echo "Usage: $0 [<options>] [plugins]"
|
|
26
26
|
echo " options:"
|
|
27
|
-
echo " -
|
|
27
|
+
echo " -s <repo> Specify destination GitHub repository (org/name) - defaults to the git origin"
|
|
28
|
+
echo " -b <branch> Specify destination GitHub branch"
|
|
29
|
+
echo " -t <tag> Specify the Github release tag to build when a release has been tagged and published"
|
|
28
30
|
echo " -f Force building the chart even if it already exists"
|
|
31
|
+
echo " -c Build as a container image rather than publishing to Github"
|
|
32
|
+
echo " -p Push container images on build"
|
|
29
33
|
echo " -r <name> Specify destination container registry for built images"
|
|
30
34
|
echo " -o <name> Specify destination container registry organization for built images"
|
|
31
35
|
echo " -i <prefix> Specify prefix for the built container image (default: 'ui-extension-')"
|
|
32
|
-
echo " -c Build as a container image rather than publishing to Github"
|
|
33
|
-
echo " -s <repo> Specify destination GitHub repository (org/name) - defaults to the git origin"
|
|
34
|
-
echo " -b <branch> Specify destination GitHub branch"
|
|
35
36
|
exit 1
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
while getopts "hvr:o:pi:fcb:s:" opt; do
|
|
39
|
+
while getopts "hvr:o:pi:fcb:t:s:" opt; do
|
|
39
40
|
case $opt in
|
|
40
41
|
h)
|
|
41
42
|
usage
|
|
@@ -66,6 +67,9 @@ while getopts "hvr:o:pi:fcb:s:" opt; do
|
|
|
66
67
|
GITHUB_BUILD="true"
|
|
67
68
|
GITHUB_BRANCH="${OPTARG}"
|
|
68
69
|
;;
|
|
70
|
+
t)
|
|
71
|
+
GITHUB_RELEASE_TAG="${OPTARG}"
|
|
72
|
+
;;
|
|
69
73
|
*)
|
|
70
74
|
usage
|
|
71
75
|
;;
|
|
@@ -90,6 +94,10 @@ if [ "${GITHUB_BUILD}" == "true" ]; then
|
|
|
90
94
|
|
|
91
95
|
echo -e "${CYAN}GitHub Repository: ${GITHUB_SOURCE}${RESET}"
|
|
92
96
|
echo -e "${CYAN}GitHub Branch : ${GITHUB_BRANCH}${RESET}"
|
|
97
|
+
|
|
98
|
+
if [[ -n "${GITHUB_RELEASE_TAG}" ]]; then
|
|
99
|
+
echo -e "${CYAN}GitHub Release : ${GITHUB_RELEASE_TAG}${RESET}"
|
|
100
|
+
fi
|
|
93
101
|
else
|
|
94
102
|
echo -e ${CYAN}"Image prefix: ${IMAGE_PREFIX}${RESET}"
|
|
95
103
|
fi
|
|
@@ -116,7 +124,11 @@ if ! [[ -d ${BASE_DIR}/node_modules ]]; then
|
|
|
116
124
|
exit 1
|
|
117
125
|
fi
|
|
118
126
|
|
|
119
|
-
COMMANDS=("node" "jq" "yq" "git" "
|
|
127
|
+
COMMANDS=("node" "jq" "yq" "git" "helm" "yarn")
|
|
128
|
+
if [ "${GITHUB_BUILD}" == "false" ]; then
|
|
129
|
+
COMMANDS+=("docker")
|
|
130
|
+
fi
|
|
131
|
+
|
|
120
132
|
HAVE_COMMANDS="true"
|
|
121
133
|
for CMD in "${COMMANDS[@]}"
|
|
122
134
|
do
|
|
@@ -132,7 +144,15 @@ fi
|
|
|
132
144
|
|
|
133
145
|
# --------------------------------------------------------------------------------
|
|
134
146
|
# Only do conatiner args checks if not GitHub publish
|
|
147
|
+
# --------------------------------------------------------------------------------
|
|
135
148
|
if [ "${GITHUB_BUILD}" == "false" ]; then
|
|
149
|
+
BASE_EXT=$(jq -r .name ${BASE_DIR}/package.json)
|
|
150
|
+
EXT_VERSION=$(jq -r .version ${BASE_DIR}/package.json)
|
|
151
|
+
|
|
152
|
+
if [ -z ${EXT_VERSION} ]; then
|
|
153
|
+
EXT_VERSION="0.0.0"
|
|
154
|
+
fi
|
|
155
|
+
|
|
136
156
|
if [[ -z ${REGISTRY_ORG} ]]; then
|
|
137
157
|
# Infer that the user has the same Docker registry org as their GitHub org
|
|
138
158
|
GITHUB_REPO=$(git config --get remote.origin.url | sed -e 's/^git@.*:\([[:graph:]]*\).git/\1/')
|
|
@@ -146,6 +166,12 @@ if [ "${GITHUB_BUILD}" == "false" ]; then
|
|
|
146
166
|
exit 1
|
|
147
167
|
fi
|
|
148
168
|
|
|
169
|
+
if [ -n "${GITHUB_RELEASE_TAG}" ] && [[ ${GITHUB_RELEASE_TAG} != "${BASE_EXT}-${EXT_VERSION}" ]]; then
|
|
170
|
+
echo -e "${YELLOW}Github tagged release name does not match ${RESET}${BOLD}${BASE_EXT}-${EXT_VERSION}${RESET}"
|
|
171
|
+
echo -e "${YELLOW}Stopping build${RESET}"
|
|
172
|
+
exit 1
|
|
173
|
+
fi
|
|
174
|
+
|
|
149
175
|
docker images > /dev/null
|
|
150
176
|
if [ $? -ne 0 ]; then
|
|
151
177
|
echo "docker is not running - this is required to build container images for the UI Plugins"
|
|
@@ -185,6 +211,13 @@ for d in pkg/*/ ; do
|
|
|
185
211
|
PKG_NAME="${pkg}-${PKG_VERSION}"
|
|
186
212
|
PKG_ASSET=${ASSETS}/${pkg}/${PKG_NAME}.tgz
|
|
187
213
|
|
|
214
|
+
# Skip the build for a package that does not match the tagged release name
|
|
215
|
+
if [[ -n "${GITHUB_RELEASE_TAG}" ]] && [ ${GITHUB_BUILD} == "true" ] && [[ ${GITHUB_RELEASE_TAG} != ${PKG_NAME} ]]; then
|
|
216
|
+
echo -e "${YELLOW}Github release tag ${RESET}${BOLD}${GITHUB_RELEASE_TAG}${RESET}${YELLOW} does not match asset name${RESET}"
|
|
217
|
+
echo -e "${YELLOW}Skipping ${RESET}${BOLD}${PKG_NAME}${RESET}"
|
|
218
|
+
continue
|
|
219
|
+
fi
|
|
220
|
+
|
|
188
221
|
echo -e "${CYAN}${BOLD}Building plugin: ${pkg} (${PKG_VERSION}) ${RESET}"
|
|
189
222
|
|
|
190
223
|
echo "Package version: ${PKG_VERSION}"
|
|
@@ -299,6 +332,15 @@ for d in pkg/*/ ; do
|
|
|
299
332
|
fi
|
|
300
333
|
done
|
|
301
334
|
|
|
335
|
+
if [ "${GITHUB_BUILD}" == "true" ] && [[ -n "${GITHUB_RELEASE_TAG}" ]] && [ "${BUILT}" == "false" ]; then
|
|
336
|
+
echo -e "${YELLOW}Github release tag ${RESET}${BOLD}${GITHUB_RELEASE_TAG}${RESET}${YELLOW} did not match any package name.${RESET}"
|
|
337
|
+
echo -e "${YELLOW}Stopping build${RESET}"
|
|
338
|
+
|
|
339
|
+
rm -rf ${CHART_TMP}
|
|
340
|
+
rm -rf ${TMP}
|
|
341
|
+
exit 1
|
|
342
|
+
fi
|
|
343
|
+
|
|
302
344
|
if [ -f ${ROOT_INDEX} ] && [ "${GITHUB_BUILD}" == "false" ]; then
|
|
303
345
|
UPDATE="--merge ${ROOT_INDEX}"
|
|
304
346
|
helm repo index ${ASSETS} ${UPDATE}
|
|
@@ -309,12 +351,6 @@ if [ -f ${ASSETS}/index.yaml ] && ! [ -e "${ROOT_INDEX}" ]; then
|
|
|
309
351
|
fi
|
|
310
352
|
|
|
311
353
|
if [ "${GITHUB_BUILD}" == "false" ]; then
|
|
312
|
-
EXT_VERSION=$(jq -r .version ${BASE_DIR}/package.json)
|
|
313
|
-
|
|
314
|
-
if [ -z ${EXT_VERSION} ]; then
|
|
315
|
-
EXT_VERSION="0.0.0"
|
|
316
|
-
fi
|
|
317
|
-
|
|
318
354
|
echo -e "${CYAN}Base extension: ${BASE_EXT}${RESET}"
|
|
319
355
|
echo -e "${CYAN}Extension version: ${EXT_VERSION}${RESET}"
|
|
320
356
|
|
|
@@ -325,7 +361,11 @@ else
|
|
|
325
361
|
fi
|
|
326
362
|
|
|
327
363
|
if [ "${GITHUB_BUILD}" == "true" ] && [ -f ${ROOT_INDEX} ]; then
|
|
328
|
-
|
|
364
|
+
if [[ -n "${GITHUB_RELEASE_TAG}" ]] && [ -f ${ASSETS}/index.yaml ]; then
|
|
365
|
+
cp ${ASSETS}/index.yaml tmp
|
|
366
|
+
else
|
|
367
|
+
cp ${ROOT_INDEX} tmp
|
|
368
|
+
fi
|
|
329
369
|
fi
|
|
330
370
|
|
|
331
371
|
if [ "${GITHUB_BUILD}" == "true" ] && [ "${BUILT}" == "true" ]; then
|
package/types/shell/index.d.ts
CHANGED
|
@@ -3727,6 +3727,7 @@ export function matching(ary: any, selector: any, labelKey: any): any;
|
|
|
3727
3727
|
// @shell/utils/socket
|
|
3728
3728
|
|
|
3729
3729
|
declare module '@shell/utils/socket' {
|
|
3730
|
+
export const addEventListener: any;
|
|
3730
3731
|
export const STATE_CONNECTING: "connecting";
|
|
3731
3732
|
export const STATE_CONNECTED: "connected";
|
|
3732
3733
|
export const EVENT_CONNECTING: "connecting";
|
package/utils/socket.js
CHANGED
|
@@ -11,6 +11,7 @@ const SECURE = 'wss://';
|
|
|
11
11
|
|
|
12
12
|
const STATE_DISCONNECTED = 'disconnected';
|
|
13
13
|
|
|
14
|
+
export const addEventListener = EventTarget.addEventListener;
|
|
14
15
|
export const STATE_CONNECTING = 'connecting';
|
|
15
16
|
export const STATE_CONNECTED = 'connected';
|
|
16
17
|
const STATE_CLOSING = 'closing';
|