@rancher/shell 0.3.15 → 0.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/images/wechat-qr-code.jpg +0 -0
- package/assets/translations/en-us.yaml +70 -15
- package/assets/translations/zh-hans.yaml +155 -33
- package/chart/__tests__/S3.test.ts +50 -0
- package/chart/rancher-backup/S3.vue +21 -0
- package/chart/rancher-backup/index.vue +4 -0
- package/cloud-credential/generic.vue +1 -1
- package/components/BannerGraphic.vue +1 -0
- package/components/CommunityLinks.vue +1 -0
- package/components/CruResource.vue +1 -1
- package/components/EmberPage.vue +1 -0
- package/components/FileDiff.vue +92 -85
- package/components/GrafanaDashboard.vue +7 -1
- package/components/ResourceDetail/index.vue +4 -12
- package/components/ResourceList/index.vue +1 -1
- package/components/ResourceTable.vue +50 -2
- package/components/SimpleBox.vue +1 -0
- package/components/SortableTable/index.vue +5 -1
- package/components/YamlEditor.vue +1 -0
- package/components/auth/RoleDetailEdit.vue +1 -0
- package/components/form/GitPicker.vue +1 -1
- package/components/form/NameNsDescription.vue +28 -12
- package/components/form/NodeAffinity.vue +2 -2
- package/components/form/PodAffinity.vue +8 -3
- package/components/form/ResourceTabs/index.vue +8 -2
- package/components/form/Select.vue +16 -0
- package/components/form/__tests__/NodeAffinity.test.ts +38 -0
- package/components/form/__tests__/PodAffinity.test.ts +46 -0
- package/components/formatter/ClusterLink.vue +8 -4
- package/components/formatter/ImageName.vue +23 -0
- package/components/formatter/PodImages.vue +7 -1
- package/components/formatter/__tests__/ClusterLink.test.ts +101 -0
- package/components/nav/Header.vue +2 -2
- package/config/__test__/home-links.test.ts +62 -0
- package/config/home-links.js +15 -3
- package/config/labels-annotations.js +5 -1
- package/config/product/auth.js +1 -1
- package/config/router.js +0 -9
- package/config/settings.ts +4 -0
- package/config/table-headers.js +6 -5
- package/config/uiplugins.js +50 -5
- package/core/plugin-helpers.js +20 -12
- package/core/plugin.ts +9 -0
- package/core/plugins.js +1 -1
- package/core/types-provisioning.ts +253 -0
- package/core/types.ts +17 -3
- package/detail/autoscaling.horizontalpodautoscaler/index.vue +50 -1
- package/detail/catalog.cattle.io.clusterrepo.vue +8 -1
- package/detail/node.vue +6 -6
- package/detail/pod.vue +2 -6
- package/detail/provisioning.cattle.io.cluster.vue +46 -7
- package/detail/workload/index.vue +9 -9
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +62 -0
- package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +56 -0
- package/edit/auth/github.vue +1 -0
- package/edit/autoscaling.horizontalpodautoscaler/hpa-scaling-rule.vue +130 -0
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +79 -0
- package/edit/fleet.cattle.io.gitrepo.vue +18 -1
- package/edit/monitoring.coreos.com.prometheusrule/index.vue +8 -3
- package/edit/namespace.vue +9 -1
- package/edit/networking.k8s.io.ingress/RulePath.vue +0 -2
- package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +1 -30
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +79 -1
- package/edit/provisioning.cattle.io.cluster/index.vue +52 -0
- package/edit/provisioning.cattle.io.cluster/rke2.vue +330 -150
- package/edit/ui.cattle.io.navlink.vue +2 -1
- package/initialize/App.js +3 -13
- package/initialize/layouts.ts +26 -0
- package/list/provisioning.cattle.io.cluster.vue +8 -1
- package/middleware/authenticated.js +93 -5
- package/mixins/brand.js +39 -3
- package/mixins/child-hook.js +2 -2
- package/mixins/create-edit-view/impl.js +2 -2
- package/models/fleet.cattle.io.gitrepo.js +1 -0
- package/models/provisioning.cattle.io.cluster.js +9 -1
- package/package.json +3 -3
- package/pages/about.vue +8 -2
- package/pages/auth/login.vue +10 -0
- package/pages/auth/logout.vue +11 -3
- package/pages/auth/setup.vue +4 -0
- package/pages/c/_cluster/apps/charts/index.vue +5 -2
- package/pages/c/_cluster/apps/charts/install.vue +5 -0
- package/pages/c/_cluster/auth/roles/index.vue +1 -1
- package/pages/c/_cluster/explorer/index.vue +1 -10
- package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +177 -0
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +19 -3
- package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +90 -21
- package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +107 -37
- package/pages/c/_cluster/uiplugins/index.vue +155 -44
- package/pages/docs/_doc.vue +9 -3
- package/pages/home.vue +10 -5
- package/pages/support/index.vue +10 -4
- package/pkg/auto-import.js +1 -1
- package/plugins/clean-tooltip-directive.js +1 -1
- package/plugins/dashboard-store/resource-class.js +35 -2
- package/plugins/plugin.js +9 -1
- package/plugins/steve/actions.js +22 -0
- package/rancher-components/BadgeState/BadgeState.vue +5 -1
- package/rancher-components/Banner/Banner.test.ts +51 -1
- package/rancher-components/Banner/Banner.vue +134 -53
- package/rancher-components/Card/Card.vue +24 -7
- package/rancher-components/Form/Checkbox/Checkbox.test.ts +20 -29
- package/rancher-components/Form/Checkbox/Checkbox.vue +45 -20
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +2 -8
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +22 -10
- package/rancher-components/Form/Radio/RadioButton.vue +30 -13
- package/rancher-components/Form/Radio/RadioGroup.vue +26 -7
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +7 -6
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +25 -38
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +23 -11
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +19 -5
- package/rancher-components/StringList/StringList.test.ts +453 -49
- package/rancher-components/StringList/StringList.vue +92 -58
- package/scripts/extension/publish +2 -2
- package/scripts/typegen.sh +1 -0
- package/server/server-middleware.js +4 -12
- package/store/index.js +13 -0
- package/store/prefs.js +0 -3
- package/store/type-map.js +17 -29
- package/types/shell/index.d.ts +243 -90
- package/utils/kube.js +9 -0
- package/utils/object.js +27 -0
- package/utils/settings.ts +2 -2
- package/vue.config.js +3 -2
- package/pages/safeMode.vue +0 -17
- package/rancher-components/components/BadgeState/BadgeState.spec.ts +0 -12
- package/rancher-components/components/BadgeState/BadgeState.vue +0 -111
- package/rancher-components/components/BadgeState/index.ts +0 -1
- package/rancher-components/components/Banner/Banner.test.ts +0 -63
- package/rancher-components/components/Banner/Banner.vue +0 -244
- package/rancher-components/components/Banner/index.ts +0 -1
- package/rancher-components/components/Card/Card.vue +0 -167
- package/rancher-components/components/Card/index.ts +0 -1
- package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +0 -68
- package/rancher-components/components/Form/Checkbox/Checkbox.vue +0 -420
- package/rancher-components/components/Form/Checkbox/index.ts +0 -1
- package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +0 -23
- package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +0 -355
- package/rancher-components/components/Form/LabeledInput/index.ts +0 -1
- package/rancher-components/components/Form/Radio/RadioButton.vue +0 -287
- package/rancher-components/components/Form/Radio/RadioGroup.vue +0 -254
- package/rancher-components/components/Form/Radio/index.ts +0 -2
- package/rancher-components/components/Form/TextArea/TextAreaAutoGrow.vue +0 -170
- package/rancher-components/components/Form/TextArea/index.ts +0 -1
- package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +0 -94
- package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.vue +0 -149
- package/rancher-components/components/Form/ToggleSwitch/index.ts +0 -1
- package/rancher-components/components/Form/index.ts +0 -5
- package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -151
- package/rancher-components/components/LabeledTooltip/index.ts +0 -1
- package/rancher-components/components/StringList/StringList.test.ts +0 -484
- package/rancher-components/components/StringList/StringList.vue +0 -611
- package/rancher-components/components/StringList/index.ts +0 -1
- /package/rancher-components/{components/Card → Card}/Card.test.ts +0 -0
- /package/rancher-components/{components/Form → Form}/Radio/RadioButton.test.ts +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import NodeAffinity from '@shell/components/form/NodeAffinity.vue';
|
|
3
|
+
import { _CREATE } from '@shell/config/query-params';
|
|
4
|
+
|
|
5
|
+
describe('component: NodeAffinity', () => {
|
|
6
|
+
it('should display the weight input when the priority is preferred', () => {
|
|
7
|
+
const nodeAffinity = {
|
|
8
|
+
preferredDuringSchedulingIgnoredDuringExecution: [{
|
|
9
|
+
preference: { matchExpressions: [] },
|
|
10
|
+
weight: 1
|
|
11
|
+
}],
|
|
12
|
+
requiredDuringSchedulingIgnoredDuringExecution: { nodeSelectorTerms: [{ matchExpressions: [] }] }
|
|
13
|
+
};
|
|
14
|
+
const wrapper = mount(NodeAffinity, { propsData: { mode: _CREATE, value: nodeAffinity } });
|
|
15
|
+
|
|
16
|
+
expect(wrapper.find('[data-testid="node-affinity-weight-index0"]').exists()).toBeTruthy();
|
|
17
|
+
expect(wrapper.find('[data-testid="node-affinity-weight-index1"]').exists()).toBeFalsy();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should display the weight input when the value is cleared', async() => {
|
|
21
|
+
const nodeAffinity = {
|
|
22
|
+
preferredDuringSchedulingIgnoredDuringExecution: [{
|
|
23
|
+
preference: { matchExpressions: [] },
|
|
24
|
+
weight: 1
|
|
25
|
+
}],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const wrapper = mount(NodeAffinity, { propsData: { mode: _CREATE, value: nodeAffinity } });
|
|
29
|
+
|
|
30
|
+
const weightInput = wrapper.find('[data-testid="node-affinity-weight-index0"]');
|
|
31
|
+
|
|
32
|
+
weightInput.setValue('');
|
|
33
|
+
|
|
34
|
+
await wrapper.vm.$nextTick();
|
|
35
|
+
|
|
36
|
+
expect(wrapper.find('[data-testid="node-affinity-weight-index0"]').exists()).toBeTruthy();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import PodAffinity from '@shell/components/form/PodAffinity.vue';
|
|
3
|
+
import { _CREATE } from '@shell/config/query-params';
|
|
4
|
+
|
|
5
|
+
describe('component: PodAffinity', () => {
|
|
6
|
+
it('should display the weight input when the priority is preferred', () => {
|
|
7
|
+
const podAffinity = {
|
|
8
|
+
preferredDuringSchedulingIgnoredDuringExecution: [{
|
|
9
|
+
podAffinityTerm: { topologyKey: 'test topology key 1' },
|
|
10
|
+
weight: 1
|
|
11
|
+
}],
|
|
12
|
+
requiredDuringSchedulingIgnoredDuringExecution: [{ topologyKey: 'test topology key 2' }]
|
|
13
|
+
};
|
|
14
|
+
const wrapper = mount(PodAffinity, {
|
|
15
|
+
propsData: {
|
|
16
|
+
mode: _CREATE, field: 'overrideAffinity', value: { overrideAffinity: { podAffinity } }
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(wrapper.find('[data-testid="pod-affinity-weight-index0"]').exists()).toBeTruthy();
|
|
21
|
+
expect(wrapper.find('[data-testid="pod-affinity-weight-index1"]').exists()).toBeFalsy();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should display the weight input when the value is cleared', async() => {
|
|
25
|
+
const podAffinity = {
|
|
26
|
+
preferredDuringSchedulingIgnoredDuringExecution: [{
|
|
27
|
+
podAffinityTerm: { topologyKey: 'test topology key 1' },
|
|
28
|
+
weight: 1
|
|
29
|
+
}],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const wrapper = mount(PodAffinity, {
|
|
33
|
+
propsData: {
|
|
34
|
+
mode: _CREATE, field: 'overrideAffinity', value: { overrideAffinity: { podAffinity } }
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const weightInput = wrapper.find('[data-testid="pod-affinity-weight-index0"]');
|
|
39
|
+
|
|
40
|
+
weightInput.setValue('');
|
|
41
|
+
|
|
42
|
+
await wrapper.vm.$nextTick();
|
|
43
|
+
|
|
44
|
+
expect(wrapper.find('[data-testid="pod-affinity-weight-index0"]').exists()).toBeTruthy();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { get } from '@shell/utils/object';
|
|
3
|
+
import { isConditionReadyAndWaiting } from '@shell/plugins/dashboard-store/resource-class';
|
|
3
4
|
|
|
4
5
|
export default {
|
|
5
6
|
props: {
|
|
@@ -27,10 +28,10 @@ export default {
|
|
|
27
28
|
|
|
28
29
|
statusErrorConditions() {
|
|
29
30
|
if (this.row.hasError) {
|
|
30
|
-
return this.row?.status.conditions.filter((
|
|
31
|
+
return this.row?.status.conditions.filter((cond) => cond.error === true && !isConditionReadyAndWaiting(cond));
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
return
|
|
34
|
+
return [];
|
|
34
35
|
},
|
|
35
36
|
|
|
36
37
|
formattedConditions() {
|
|
@@ -46,7 +47,7 @@ export default {
|
|
|
46
47
|
return formattedTooltip.toString().replaceAll(',', '');
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
return
|
|
50
|
+
return '';
|
|
50
51
|
},
|
|
51
52
|
},
|
|
52
53
|
|
|
@@ -66,16 +67,19 @@ export default {
|
|
|
66
67
|
v-if="row.unavailableMachines"
|
|
67
68
|
v-clean-tooltip="row.unavailableMachines"
|
|
68
69
|
class="conditions-alert-icon icon-alert icon"
|
|
70
|
+
data-testid="unavailable-machines-alert-icon"
|
|
69
71
|
/>
|
|
70
72
|
<i
|
|
71
73
|
v-if="row.rkeTemplateUpgrade"
|
|
72
74
|
v-clean-tooltip="t('cluster.rkeTemplateUpgrade', { name: row.rkeTemplateUpgrade })"
|
|
73
75
|
class="template-upgrade-icon icon-alert icon"
|
|
76
|
+
data-testid="rke-template-upgrade-alert-icon"
|
|
74
77
|
/>
|
|
75
78
|
<i
|
|
76
|
-
v-if="row.hasError"
|
|
79
|
+
v-if="row.hasError && statusErrorConditions.length > 0"
|
|
77
80
|
v-clean-tooltip="{ content: `<div>${formattedConditions}</div>`, html: true }"
|
|
78
81
|
class="conditions-alert-icon icon-error icon-lg"
|
|
82
|
+
data-testid="conditions-has-error-icon"
|
|
79
83
|
/>
|
|
80
84
|
</span>
|
|
81
85
|
</template>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export default {
|
|
3
|
+
props:
|
|
4
|
+
{
|
|
5
|
+
value: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: ''
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<span class="formatter-image">
|
|
15
|
+
{{ value }}
|
|
16
|
+
</span>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<style scoped>
|
|
20
|
+
.formatter-image {
|
|
21
|
+
word-break: break-all;
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
@@ -55,7 +55,7 @@ export default {
|
|
|
55
55
|
</script>
|
|
56
56
|
|
|
57
57
|
<template>
|
|
58
|
-
<span>
|
|
58
|
+
<span class="formatter-pod-images">
|
|
59
59
|
<span>{{ mainImage }}</span><br>
|
|
60
60
|
<span
|
|
61
61
|
v-if="images.length-1>0"
|
|
@@ -64,3 +64,9 @@ export default {
|
|
|
64
64
|
>{{ t('generic.plusMore', {n:images.length-1}) }}</span>
|
|
65
65
|
</span>
|
|
66
66
|
</template>
|
|
67
|
+
|
|
68
|
+
<style scoped>
|
|
69
|
+
.formatter-pod-images {
|
|
70
|
+
word-break: break-all;
|
|
71
|
+
}
|
|
72
|
+
</style>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import ClusterLink from '@shell/components/formatter/ClusterLink.vue';
|
|
3
|
+
|
|
4
|
+
describe('component: ClusterLink', () => {
|
|
5
|
+
const UNAVAILABLE_MACHINES_ICON_SELECTOR = '[data-testid="unavailable-machines-alert-icon"]';
|
|
6
|
+
const TEMPLATE_UPGRADE_ICON_SELECTOR = '[data-testid="rke-template-upgrade-alert-icon"]';
|
|
7
|
+
const CONDITION_HAS_ERROR_ICON_SELECTOR = '[data-testid="conditions-has-error-icon"]';
|
|
8
|
+
|
|
9
|
+
describe('unavailable machines alert icon', () => {
|
|
10
|
+
const testCases = [
|
|
11
|
+
[undefined, false],
|
|
12
|
+
[0, false],
|
|
13
|
+
[1, true],
|
|
14
|
+
[5, true],
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
it.each(testCases)(
|
|
18
|
+
'should show/hide properly based on on the number of unavailable machines',
|
|
19
|
+
(unavailableMachines, expected) => {
|
|
20
|
+
const wrapper = mount(ClusterLink, {
|
|
21
|
+
propsData: {
|
|
22
|
+
row: {
|
|
23
|
+
hasError: false,
|
|
24
|
+
status: {},
|
|
25
|
+
unavailableMachines,
|
|
26
|
+
rkeTemplateUpgrade: undefined
|
|
27
|
+
},
|
|
28
|
+
reference: 'any',
|
|
29
|
+
value: 'any'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
const el = wrapper.find(UNAVAILABLE_MACHINES_ICON_SELECTOR);
|
|
33
|
+
|
|
34
|
+
expect(el.exists()).toBe(expected);
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('template upgrade alert icon', () => {
|
|
40
|
+
const testCases = [
|
|
41
|
+
[undefined, false],
|
|
42
|
+
['any', true],
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
it.each(testCases)(
|
|
46
|
+
'should show/hide properly based on rkeTemplateUpgrade',
|
|
47
|
+
(rkeTemplateUpgrade, expected) => {
|
|
48
|
+
const wrapper = mount(ClusterLink, {
|
|
49
|
+
propsData: {
|
|
50
|
+
row: {
|
|
51
|
+
hasError: false,
|
|
52
|
+
status: {},
|
|
53
|
+
unavailableMachines: 0,
|
|
54
|
+
rkeTemplateUpgrade
|
|
55
|
+
},
|
|
56
|
+
reference: 'any',
|
|
57
|
+
value: 'any'
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
const el = wrapper.find(TEMPLATE_UPGRADE_ICON_SELECTOR);
|
|
61
|
+
|
|
62
|
+
expect(el.exists()).toBe(expected);
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('conditions has error icon', () => {
|
|
68
|
+
const MOCKED_CONDITIONS_1 = [{
|
|
69
|
+
status: '', type: 'Ready', reason: 'Waiting', error: true // When the only existing error has a type "Ready" and reason "Waiting"
|
|
70
|
+
}];
|
|
71
|
+
const MOCKED_CONDITIONS_2 = [{
|
|
72
|
+
status: 'any', type: 'any', error: true
|
|
73
|
+
}];
|
|
74
|
+
|
|
75
|
+
const testCases = [
|
|
76
|
+
[[], false],
|
|
77
|
+
[MOCKED_CONDITIONS_1, false],
|
|
78
|
+
[MOCKED_CONDITIONS_2, true],
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
it.each(testCases)(
|
|
82
|
+
'should show/hide properly based on the status conditions',
|
|
83
|
+
(conditions, expected) => {
|
|
84
|
+
const wrapper = mount(ClusterLink, {
|
|
85
|
+
propsData: {
|
|
86
|
+
row: {
|
|
87
|
+
hasError: true,
|
|
88
|
+
status: { conditions },
|
|
89
|
+
unavailableMachines: 0
|
|
90
|
+
},
|
|
91
|
+
reference: 'any',
|
|
92
|
+
value: 'any'
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
const el = wrapper.find(CONDITION_HAS_ERROR_ICON_SELECTOR);
|
|
96
|
+
|
|
97
|
+
expect(el.exists()).toBe(expected);
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -181,6 +181,8 @@ export default {
|
|
|
181
181
|
$route(nue) {
|
|
182
182
|
if (nue) {
|
|
183
183
|
this.extensionHeaderActions = getApplicableExtensionEnhancements(this, ExtensionPoint.ACTION, ActionLocation.HEADER, nue);
|
|
184
|
+
|
|
185
|
+
this.navHeaderRight = this.$plugin?.getDynamic('component', 'NavHeaderRight');
|
|
184
186
|
}
|
|
185
187
|
}
|
|
186
188
|
},
|
|
@@ -191,8 +193,6 @@ export default {
|
|
|
191
193
|
window.addEventListener('resize', this.debouncedLayoutHeader);
|
|
192
194
|
|
|
193
195
|
this.$nextTick(() => this.layoutHeader(null, true));
|
|
194
|
-
|
|
195
|
-
this.navHeaderRight = this.$plugin?.getDynamic('component', 'NavHeaderRight');
|
|
196
196
|
},
|
|
197
197
|
|
|
198
198
|
beforeDestroy() {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import Vuex from 'vuex';
|
|
2
|
+
import { createLocalVue } from '@vue/test-utils';
|
|
3
|
+
import { ensureSupportLink } from '@shell/config/home-links.js';
|
|
4
|
+
import { getters, state, mutations } from '@shell/store/i18n.js';
|
|
5
|
+
|
|
6
|
+
jest.mock('@shell/assets/translations/en-us.yaml', () => ({
|
|
7
|
+
locale: {
|
|
8
|
+
'en-us': 'English',
|
|
9
|
+
'zh-hans': '简体中文',
|
|
10
|
+
none: '(None)',
|
|
11
|
+
}
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
describe('fx: ensureSupportLink', () => {
|
|
15
|
+
const localVue = createLocalVue();
|
|
16
|
+
|
|
17
|
+
localVue.use(Vuex);
|
|
18
|
+
|
|
19
|
+
const store = new Vuex.Store({
|
|
20
|
+
state,
|
|
21
|
+
getters: {
|
|
22
|
+
'i18n/selectedLocaleLabel': getters.selectedLocaleLabel,
|
|
23
|
+
'i18n/t': getters.t,
|
|
24
|
+
},
|
|
25
|
+
mutations,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
store.commit('loadTranslations', {
|
|
29
|
+
locale: 'zh-zhans',
|
|
30
|
+
translations: {
|
|
31
|
+
locale: {
|
|
32
|
+
'en-us': 'English',
|
|
33
|
+
'zh-hans': '简体中文',
|
|
34
|
+
none: '(None)',
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const testCases = [
|
|
40
|
+
['en-us', false],
|
|
41
|
+
['zh-hans', true],
|
|
42
|
+
['none', false],
|
|
43
|
+
[null, false],
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
it.each(testCases)('should return cn forum link if the language is zh-hans', (language:String, value) => {
|
|
47
|
+
store.commit('setSelected', language);
|
|
48
|
+
|
|
49
|
+
const links = { defaults: [], custom: [] };
|
|
50
|
+
const hasSupport = true;
|
|
51
|
+
const isSupportPage = true;
|
|
52
|
+
|
|
53
|
+
const localThis = {
|
|
54
|
+
$store: store,
|
|
55
|
+
t: store.getters['i18n/t'],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const result = ensureSupportLink(links, hasSupport, isSupportPage, localThis.t, store);
|
|
59
|
+
|
|
60
|
+
expect(!!result.defaults.find((link) => link.key === 'cnforums')).toBe(value);
|
|
61
|
+
});
|
|
62
|
+
});
|
package/config/home-links.js
CHANGED
|
@@ -38,6 +38,12 @@ const SUPPORT_LINK = {
|
|
|
38
38
|
readonly: true
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
const CN_FORUMS_LINK = {
|
|
42
|
+
key: 'cnforums',
|
|
43
|
+
value: 'https://forums.rancher.cn/',
|
|
44
|
+
enabled: true,
|
|
45
|
+
};
|
|
46
|
+
|
|
41
47
|
// We add a version attribute to the setting so we know what has been migrated and which version of the setting we have
|
|
42
48
|
export const CUSTOM_LINKS_VERSION = 'v1';
|
|
43
49
|
|
|
@@ -72,7 +78,7 @@ export async function fetchLinks(store, hasSupport, isSupportPage, t) {
|
|
|
72
78
|
uiLinks.defaults = defaults;
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
return ensureSupportLink(uiLinks, hasSupport, isSupportPage, t);
|
|
81
|
+
return ensureSupportLink(uiLinks, hasSupport, isSupportPage, t, store);
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
// No new setting, so return the required structure
|
|
@@ -117,11 +123,11 @@ export async function fetchLinks(store, hasSupport, isSupportPage, t) {
|
|
|
117
123
|
console.warn('Could not parse legacy link settings', e); // eslint-disable-line no-console
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
return ensureSupportLink(links, hasSupport, isSupportPage, t);
|
|
126
|
+
return ensureSupportLink(links, hasSupport, isSupportPage, t, store);
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
// Ensure the support link is added if needed
|
|
124
|
-
function ensureSupportLink(links, hasSupport, isSupportPage, t) {
|
|
130
|
+
export function ensureSupportLink(links, hasSupport, isSupportPage, t, store) {
|
|
125
131
|
if (!hasSupport && !isSupportPage) {
|
|
126
132
|
const supportLink = links.defaults?.find((link) => link.key === 'commercialSupport');
|
|
127
133
|
|
|
@@ -130,6 +136,12 @@ function ensureSupportLink(links, hasSupport, isSupportPage, t) {
|
|
|
130
136
|
}
|
|
131
137
|
}
|
|
132
138
|
|
|
139
|
+
const selectedLocaleLabel = store.getters['i18n/selectedLocaleLabel'];
|
|
140
|
+
|
|
141
|
+
if (selectedLocaleLabel === t('locale.zh-hans')) {
|
|
142
|
+
links.defaults.push(CN_FORUMS_LINK);
|
|
143
|
+
}
|
|
144
|
+
|
|
133
145
|
// Localise the default links
|
|
134
146
|
links.defaults = links.defaults.map((link) => {
|
|
135
147
|
return {
|
|
@@ -54,7 +54,11 @@ export const CAPI = {
|
|
|
54
54
|
DELETE_MACHINE: 'cluster.x-k8s.io/delete-machine',
|
|
55
55
|
PROVIDER: 'provider.cattle.io',
|
|
56
56
|
SECRET_AUTH: 'v2prov-secret-authorized-for-cluster',
|
|
57
|
-
SECRET_WILL_DELETE: 'v2prov-authorized-secret-deletes-on-cluster-removal'
|
|
57
|
+
SECRET_WILL_DELETE: 'v2prov-authorized-secret-deletes-on-cluster-removal',
|
|
58
|
+
/**
|
|
59
|
+
* Annotation for overriding the cluster provider,
|
|
60
|
+
*/
|
|
61
|
+
UI_CUSTOM_PROVIDER: 'ui.rancher/provider'
|
|
58
62
|
};
|
|
59
63
|
|
|
60
64
|
export const CATALOG = {
|
package/config/product/auth.js
CHANGED
|
@@ -144,7 +144,7 @@ export function init(store) {
|
|
|
144
144
|
weightType(NORMAN.SPOOFED.GROUP_PRINCIPAL, 101, true);
|
|
145
145
|
|
|
146
146
|
virtualType({
|
|
147
|
-
labelKey: '
|
|
147
|
+
labelKey: 'auth.roleTemplate',
|
|
148
148
|
icon: 'user',
|
|
149
149
|
namespaced: false,
|
|
150
150
|
name: ROLES_VIRTUAL_TYPE,
|
package/config/router.js
CHANGED
|
@@ -17,11 +17,6 @@ export const routerOptions = {
|
|
|
17
17
|
scrollBehavior,
|
|
18
18
|
|
|
19
19
|
routes: [{
|
|
20
|
-
path: '/verify-auth',
|
|
21
|
-
redirect: (to) => {
|
|
22
|
-
return 'auth/verify';
|
|
23
|
-
},
|
|
24
|
-
}, {
|
|
25
20
|
path: '/about',
|
|
26
21
|
component: () => interopDefault(import('../pages/about.vue' /* webpackChunkName: "pages/about" */)),
|
|
27
22
|
name: 'about'
|
|
@@ -53,10 +48,6 @@ export const routerOptions = {
|
|
|
53
48
|
path: '/prefs',
|
|
54
49
|
component: () => interopDefault(import('../pages/prefs.vue' /* webpackChunkName: "pages/prefs" */)),
|
|
55
50
|
name: 'prefs'
|
|
56
|
-
}, {
|
|
57
|
-
path: '/safeMode',
|
|
58
|
-
component: () => interopDefault(import('../pages/safeMode.vue' /* webpackChunkName: "pages/safeMode" */)),
|
|
59
|
-
name: 'safeMode'
|
|
60
51
|
}, {
|
|
61
52
|
path: '/support',
|
|
62
53
|
component: () => interopDefault(import('../pages/support/index.vue' /* webpackChunkName: "pages/support/index" */)),
|
package/config/settings.ts
CHANGED
|
@@ -89,6 +89,10 @@ export const SETTING = {
|
|
|
89
89
|
*/
|
|
90
90
|
CLUSTER_AGENT_DEFAULT_AFFINITY: 'cluster-agent-default-affinity',
|
|
91
91
|
FLEET_AGENT_DEFAULT_AFFINITY: 'fleet-agent-default-affinity',
|
|
92
|
+
/**
|
|
93
|
+
* manage rancher repositories in extensions (official, partners repos)
|
|
94
|
+
*/
|
|
95
|
+
ADD_EXTENSION_REPOS_BANNER_DISPLAY: 'display-add-extension-repos-banner'
|
|
92
96
|
};
|
|
93
97
|
|
|
94
98
|
// These are the settings that are allowed to be edited via the UI
|
package/config/table-headers.js
CHANGED
|
@@ -264,11 +264,12 @@ export const DURATION = {
|
|
|
264
264
|
formatter: 'LiveDuration',
|
|
265
265
|
};
|
|
266
266
|
|
|
267
|
-
export const
|
|
268
|
-
name:
|
|
269
|
-
labelKey:
|
|
270
|
-
value:
|
|
271
|
-
sort:
|
|
267
|
+
export const IMAGE_NAME = {
|
|
268
|
+
name: 'image',
|
|
269
|
+
labelKey: 'tableHeaders.image',
|
|
270
|
+
value: 'image',
|
|
271
|
+
sort: ['image', 'nameSort'],
|
|
272
|
+
formatter: 'ImageName',
|
|
272
273
|
};
|
|
273
274
|
|
|
274
275
|
export const POD_IMAGES = {
|
package/config/uiplugins.js
CHANGED
|
@@ -29,8 +29,21 @@ export const UI_PLUGINS_REPO_NAME = 'rancher-ui-plugins';
|
|
|
29
29
|
export const UI_PLUGINS_REPO_URL = 'https://github.com/rancher/ui-plugin-charts';
|
|
30
30
|
export const UI_PLUGINS_REPO_BRANCH = 'main';
|
|
31
31
|
|
|
32
|
+
// Info for the Helm Chart Repo for Partner Extensions
|
|
33
|
+
export const UI_PLUGINS_PARTNERS_REPO_NAME = 'partner-extensions';
|
|
34
|
+
|
|
35
|
+
export const UI_PLUGINS_PARTNERS_REPO_URL = 'https://github.com/rancher/partner-extensions';
|
|
36
|
+
export const UI_PLUGINS_PARTNERS_REPO_BRANCH = 'main';
|
|
37
|
+
|
|
38
|
+
// Info for the Helm Chart Repo for Community Extensions
|
|
39
|
+
export const UI_PLUGINS_COMMUNITY_REPO_NAME = 'community-extensions';
|
|
40
|
+
|
|
41
|
+
export const UI_PLUGINS_COMMUNITY_REPO_URL = 'https://github.com/rancher/community-extensions';
|
|
42
|
+
export const UI_PLUGINS_COMMUNITY_REPO_BRANCH = 'main';
|
|
43
|
+
|
|
32
44
|
// Chart annotations
|
|
33
45
|
export const UI_PLUGIN_CHART_ANNOTATIONS = {
|
|
46
|
+
KUBE_VERSION: 'catalog.cattle.io/kube-version',
|
|
34
47
|
RANCHER_VERSION: 'catalog.cattle.io/rancher-version',
|
|
35
48
|
EXTENSIONS_VERSION: 'catalog.cattle.io/ui-extensions-version',
|
|
36
49
|
UI_VERSION: 'catalog.cattle.io/ui-version',
|
|
@@ -121,16 +134,18 @@ export function shouldNotLoadPlugin(plugin, rancherVersion, loadedPlugins) {
|
|
|
121
134
|
}
|
|
122
135
|
|
|
123
136
|
// Can a chart version be used for this Rancher (based on the annotations on the chart)?
|
|
124
|
-
export function isSupportedChartVersion(
|
|
137
|
+
export function isSupportedChartVersion(versionsData) {
|
|
138
|
+
const { version, rancherVersion, kubeVersion } = versionsData;
|
|
139
|
+
|
|
125
140
|
// Plugin specified a required extension API version
|
|
126
|
-
const requiredAPI =
|
|
141
|
+
const requiredAPI = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_VERSION];
|
|
127
142
|
|
|
128
143
|
if (requiredAPI && !semver.satisfies(UI_PLUGIN_API_VERSION, requiredAPI)) {
|
|
129
144
|
return false;
|
|
130
145
|
}
|
|
131
146
|
|
|
132
147
|
// Host application
|
|
133
|
-
const requiredHost =
|
|
148
|
+
const requiredHost = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.EXTENSIONS_HOST];
|
|
134
149
|
|
|
135
150
|
if (requiredHost && requiredHost !== UI_PLUGIN_HOST_APP) {
|
|
136
151
|
return false;
|
|
@@ -138,24 +153,37 @@ export function isSupportedChartVersion(chartVersion, rancherVersion) {
|
|
|
138
153
|
|
|
139
154
|
// Rancher version
|
|
140
155
|
if (rancherVersion) {
|
|
141
|
-
const requiredRancherVersion =
|
|
156
|
+
const requiredRancherVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.RANCHER_VERSION];
|
|
142
157
|
|
|
143
158
|
if (requiredRancherVersion && !semver.satisfies(rancherVersion, requiredRancherVersion)) {
|
|
144
159
|
return false;
|
|
145
160
|
}
|
|
146
161
|
}
|
|
147
162
|
|
|
163
|
+
// Kube version
|
|
164
|
+
if (kubeVersion) {
|
|
165
|
+
const requiredKubeVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.KUBE_VERSION];
|
|
166
|
+
|
|
167
|
+
if (requiredKubeVersion && !semver.satisfies(kubeVersion, requiredKubeVersion)) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
148
172
|
return true;
|
|
149
173
|
}
|
|
150
174
|
|
|
151
|
-
export function isChartVersionAvailableForInstall(
|
|
175
|
+
export function isChartVersionAvailableForInstall(versionsData, returnObj = false) {
|
|
176
|
+
const { version, rancherVersion, kubeVersion } = versionsData;
|
|
177
|
+
|
|
152
178
|
const parsedRancherVersion = rancherVersion.split('-')?.[0];
|
|
153
179
|
const regexHashString = new RegExp('^[A-Za-z0-9]{9}$');
|
|
154
180
|
const isRancherVersionHashString = regexHashString.test(rancherVersion);
|
|
155
181
|
const requiredUiVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.UI_VERSION];
|
|
182
|
+
const requiredKubeVersion = version.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.KUBE_VERSION];
|
|
156
183
|
const versionObj = { ...version };
|
|
157
184
|
|
|
158
185
|
versionObj.isCompatibleWithUi = true;
|
|
186
|
+
versionObj.isCompatibleWithKubeVersion = true;
|
|
159
187
|
|
|
160
188
|
// if it's a head version of Rancher, then we skip the validation and enable them all
|
|
161
189
|
if (!isRancherVersionHashString && requiredUiVersion && !semver.satisfies(parsedRancherVersion, requiredUiVersion)) {
|
|
@@ -164,6 +192,23 @@ export function isChartVersionAvailableForInstall(version, rancherVersion, retur
|
|
|
164
192
|
}
|
|
165
193
|
versionObj.isCompatibleWithUi = false;
|
|
166
194
|
versionObj.requiredUiVersion = requiredUiVersion;
|
|
195
|
+
|
|
196
|
+
if (returnObj) {
|
|
197
|
+
return versionObj;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// check kube version
|
|
202
|
+
if (kubeVersion && requiredKubeVersion && !semver.satisfies(kubeVersion, requiredKubeVersion)) {
|
|
203
|
+
if (!returnObj) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
versionObj.isCompatibleWithKubeVersion = false;
|
|
207
|
+
versionObj.requiredKubeVersion = requiredKubeVersion;
|
|
208
|
+
|
|
209
|
+
if (returnObj) {
|
|
210
|
+
return versionObj;
|
|
211
|
+
}
|
|
167
212
|
}
|
|
168
213
|
|
|
169
214
|
if (returnObj) {
|