@rancher/shell 0.3.16 → 0.3.18
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 +75 -16
- package/assets/translations/zh-hans.yaml +151 -15
- 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/components/AsyncButton.vue +1 -1
- package/components/CommunityLinks.vue +1 -0
- package/components/FileDiff.vue +92 -85
- package/components/Inactivity.vue +10 -0
- package/components/LazyImage.vue +2 -2
- package/components/PromptRestore.vue +7 -5
- package/components/ResourceDetail/Masthead.vue +1 -1
- package/components/ResourceDetail/index.vue +8 -14
- package/components/ResourceList/index.vue +1 -1
- package/components/ResourceTable.vue +50 -2
- package/components/YamlEditor.vue +1 -0
- package/components/__tests__/PromptRestore.test.ts +72 -0
- package/components/auth/AzureWarning.vue +1 -1
- package/components/auth/RoleDetailEdit.vue +1 -0
- package/components/fleet/FleetResources.vue +3 -64
- package/components/form/FileImageSelector.vue +9 -0
- package/components/form/FileSelector.vue +2 -1
- package/components/form/MatchExpressions.vue +1 -3
- package/components/form/NameNsDescription.vue +28 -12
- package/components/form/NodeAffinity.vue +2 -2
- package/components/form/PodAffinity.vue +2 -2
- package/components/form/ResourceTabs/index.vue +8 -2
- package/components/form/Select.vue +16 -0
- package/components/form/__tests__/FileImageSelector.test.ts +42 -0
- package/components/form/__tests__/FileSelector.test.ts +76 -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/ClusterProvider.vue +3 -1
- 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/formatter/__tests__/ClusterProvider.test.ts +24 -0
- package/components/nav/Header.vue +2 -2
- package/components/nav/WindowManager/ContainerShell.vue +60 -36
- package/components/nav/WindowManager/__tests__/ContainerShell.test.ts +561 -0
- package/config/__test__/home-links.test.ts +62 -0
- package/config/home-links.js +15 -3
- package/config/labels-annotations.js +7 -2
- package/config/persistentVolume.ts +108 -0
- package/config/product/manager.js +5 -1
- package/config/router.js +0 -4
- package/config/settings.ts +4 -0
- package/config/table-headers.js +6 -5
- package/config/types.js +2 -0
- package/config/uiplugins.js +50 -5
- package/core/plugin-helpers.js +39 -15
- package/core/plugin.ts +9 -0
- package/core/plugins.js +1 -1
- package/core/types-provisioning.ts +253 -0
- package/core/types.ts +21 -3
- package/detail/autoscaling.horizontalpodautoscaler/index.vue +50 -1
- package/detail/fleet.cattle.io.gitrepo.vue +10 -2
- package/detail/node.vue +6 -6
- package/detail/pod.vue +38 -9
- package/detail/provisioning.cattle.io.cluster.vue +46 -7
- package/detail/workload/index.vue +49 -18
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +62 -0
- package/edit/__tests__/ui.cattle.io.navlink.test.ts +110 -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.clustergroup.vue +14 -3
- package/edit/fleet.cattle.io.gitrepo.vue +18 -1
- package/edit/namespace.vue +9 -1
- package/edit/networking.k8s.io.ingress/RulePath.vue +0 -2
- package/edit/persistentvolume/__tests__/persistentvolume.test.ts +82 -0
- package/edit/persistentvolume/index.vue +2 -1
- package/edit/persistentvolume/plugins/csi.vue +3 -1
- package/edit/persistentvolume/plugins/longhorn.vue +12 -12
- package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +1 -30
- package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +15 -11
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +79 -1
- package/edit/provisioning.cattle.io.cluster/index.vue +53 -1
- package/edit/provisioning.cattle.io.cluster/rke2.vue +335 -151
- package/edit/storage.k8s.io.storageclass/index.vue +1 -2
- package/edit/ui.cattle.io.navlink.vue +213 -186
- package/initialize/App.js +3 -13
- package/initialize/layouts.ts +26 -0
- package/layouts/default.vue +1 -1
- package/list/group.principal.vue +1 -1
- package/list/provisioning.cattle.io.cluster.vue +8 -1
- package/middleware/authenticated.js +101 -5
- package/mixins/brand.js +39 -3
- package/mixins/child-hook.js +2 -2
- package/mixins/create-edit-view/impl.js +4 -4
- package/models/chart.js +1 -1
- package/models/fleet.cattle.io.cluster.js +33 -4
- package/models/fleet.cattle.io.gitrepo.js +113 -38
- package/models/management.cattle.io.kontainerdriver.js +14 -0
- package/models/persistentvolume.js +2 -111
- package/models/pod.js +30 -0
- package/models/provisioning.cattle.io.cluster.js +9 -1
- package/models/rke.cattle.io.etcdsnapshot.js +10 -7
- package/package.json +2 -2
- package/pages/about.vue +8 -2
- package/pages/auth/login.vue +1 -1
- package/pages/auth/logout.vue +11 -3
- 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/group.principal/assign-edit.vue +1 -1
- package/pages/c/_cluster/auth/roles/index.vue +1 -1
- package/pages/c/_cluster/explorer/index.vue +2 -11
- package/pages/c/_cluster/manager/cloudCredential/_id.vue +0 -1
- package/pages/c/_cluster/manager/cloudCredential/create.vue +0 -1
- package/pages/c/_cluster/settings/brand.vue +11 -8
- 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 +160 -44
- package/pages/docs/_doc.vue +9 -3
- package/pages/home.vue +6 -6
- 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/__tests__/actions.spec.ts +165 -0
- package/plugins/dashboard-store/__tests__/getters.spec.ts +100 -0
- package/plugins/dashboard-store/__tests__/{mutations.spec.js → mutations.spec.ts} +2 -2
- package/plugins/dashboard-store/actions.js +1 -1
- package/plugins/dashboard-store/resource-class.js +39 -2
- package/plugins/plugin.js +9 -1
- package/plugins/steve/__tests__/getters.spec.ts +93 -0
- package/plugins/steve/getters.js +21 -1
- package/plugins/steve/subscribe.js +1 -3
- 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.test.ts +37 -0
- 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.test.ts +31 -0
- 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 +44 -26
- package/scripts/extension/publish +2 -2
- package/scripts/typegen.sh +11 -2
- package/server/server-middleware.js +4 -12
- package/store/index.js +14 -3
- package/store/prefs.js +0 -3
- package/store/store-types.js +2 -0
- package/store/type-map.js +17 -29
- package/types/api.d.ts +1 -0
- package/types/fleet.d.ts +1 -0
- package/types/shell/index.d.ts +931 -85
- package/types/userPreferences.d.ts +1 -1
- package/utils/__mocks__/socket.js +21 -0
- package/utils/grafana.js +23 -11
- package/utils/kube.js +9 -0
- package/utils/object.js +27 -0
- package/utils/selector.js +2 -1
- package/utils/settings.ts +2 -2
- package/utils/validators/formRules/index.ts +3 -3
- package/vue.config.js +3 -2
- package/components/.DS_Store +0 -0
- package/components/__tests__/.DS_Store +0 -0
- package/creators/pkg/package-lock.json +0 -37
- package/pages/safeMode.vue +0 -17
- package/plugins/steve/urloptions.js +0 -47
- package/yarn-error.log +0 -196
package/components/FileDiff.vue
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import {
|
|
2
|
+
import { Diff2HtmlUI } from 'diff2html/lib/ui/js/diff2html-ui-slim.js';
|
|
3
|
+
|
|
3
4
|
import { createPatch } from 'diff';
|
|
4
5
|
|
|
5
6
|
export default {
|
|
@@ -38,36 +39,40 @@ export default {
|
|
|
38
39
|
}
|
|
39
40
|
},
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
mounted() {
|
|
43
|
+
this.draw();
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
watch: {
|
|
47
|
+
sideBySide() {
|
|
48
|
+
this.draw();
|
|
49
|
+
}
|
|
50
|
+
},
|
|
46
51
|
|
|
52
|
+
methods: {
|
|
53
|
+
draw() {
|
|
54
|
+
const targetElement = document.getElementById('diffElement');
|
|
47
55
|
const patch = createPatch(
|
|
48
56
|
this.filename,
|
|
49
57
|
this.orig,
|
|
50
58
|
this.neu
|
|
51
59
|
);
|
|
60
|
+
const configuration = {
|
|
61
|
+
// UI
|
|
62
|
+
synchronisedScroll: true,
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
matching,
|
|
65
|
-
synchronizedScroll: true,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
},
|
|
64
|
+
// Base
|
|
65
|
+
outputFormat: this.sideBySide ? 'side-by-side' : 'line-by-line',
|
|
66
|
+
drawFileList: false,
|
|
67
|
+
matching: 'words',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const diff2htmlUi = new Diff2HtmlUI(targetElement, patch, configuration);
|
|
71
|
+
|
|
72
|
+
diff2htmlUi.draw();
|
|
73
|
+
this.fit();
|
|
74
|
+
},
|
|
69
75
|
|
|
70
|
-
methods: {
|
|
71
76
|
fit() {
|
|
72
77
|
if ( !this.autoResize ) {
|
|
73
78
|
return;
|
|
@@ -97,8 +102,8 @@ export default {
|
|
|
97
102
|
<div>
|
|
98
103
|
<resize-observer @notify="fit" />
|
|
99
104
|
<div
|
|
105
|
+
id="diffElement"
|
|
100
106
|
ref="root"
|
|
101
|
-
v-clean-html="html"
|
|
102
107
|
class="root"
|
|
103
108
|
/>
|
|
104
109
|
</div>
|
|
@@ -112,66 +117,68 @@ export default {
|
|
|
112
117
|
}
|
|
113
118
|
</style>
|
|
114
119
|
|
|
115
|
-
<style lang="scss">
|
|
116
|
-
@import 'node_modules/diff2html/
|
|
117
|
-
|
|
118
|
-
.d2h-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
.d2h-code-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
120
|
+
<style scoped lang="scss">
|
|
121
|
+
@import 'node_modules/diff2html/bundles/css/diff2html.min.css';
|
|
122
|
+
|
|
123
|
+
::v-deep .d2h-wrapper {
|
|
124
|
+
.d2h-file-header {
|
|
125
|
+
display: none;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.d2h-file-wrapper {
|
|
129
|
+
border-color: var(--diff-border);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.d2h-diff-table {
|
|
133
|
+
font-family: Menlo,Consolas,monospace;
|
|
134
|
+
font-size: 13px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.d2h-emptyplaceholder, .d2h-code-side-emptyplaceholder {
|
|
138
|
+
border-color: var(--diff-linenum-border);
|
|
139
|
+
background-color: var(--diff-empty-placeholder);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.d2h-code-linenumber,
|
|
143
|
+
.d2h-code-side-linenumber {
|
|
144
|
+
background-color: var(--diff-linenum-bg);
|
|
145
|
+
color: var(--diff-linenum);
|
|
146
|
+
border-color: var(--diff-linenum-border);
|
|
147
|
+
border-left: 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.d2h-code-line del,.d2h-code-side-line del {
|
|
151
|
+
background-color: var(--diff-line-del-bg);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.d2h-code-line ins,.d2h-code-side-line ins {
|
|
155
|
+
background-color: var(--diff-line-ins-bg);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.d2h-del {
|
|
159
|
+
background-color: var(--diff-del-bg);
|
|
160
|
+
border-color: var(--diff-del-border);
|
|
161
|
+
color: var(--body-text);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.d2h-ins {
|
|
165
|
+
background-color: var(--diff-ins-bg);
|
|
166
|
+
border-color: var(--diff-ins-border);
|
|
167
|
+
color: var(--body-text);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.d2h-info {
|
|
171
|
+
background-color: var(--diff-header-bg);
|
|
172
|
+
color: var(--diff-header);
|
|
173
|
+
border-color: var(--diff-header-border);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.d2h-file-diff .d2h-del.d2h-change {
|
|
177
|
+
background-color: var(--diff-chg-del);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.d2h-file-diff .d2h-ins.d2h-change {
|
|
181
|
+
background-color: var(--diff-chg-ins);
|
|
182
|
+
}
|
|
176
183
|
}
|
|
177
184
|
</style>
|
|
@@ -6,6 +6,8 @@ import throttle from 'lodash/throttle';
|
|
|
6
6
|
import { MANAGEMENT } from '@shell/config/types';
|
|
7
7
|
import { DEFAULT_PERF_SETTING, SETTING } from '@shell/config/settings';
|
|
8
8
|
|
|
9
|
+
let globalId;
|
|
10
|
+
|
|
9
11
|
export default {
|
|
10
12
|
name: 'Inactivity',
|
|
11
13
|
components: {
|
|
@@ -22,6 +24,7 @@ export default {
|
|
|
22
24
|
courtesyTimerId: null,
|
|
23
25
|
courtesyCountdown: null,
|
|
24
26
|
trackInactivity: throttle(this._trackInactivity, 1000),
|
|
27
|
+
id: null,
|
|
25
28
|
};
|
|
26
29
|
},
|
|
27
30
|
async mounted() {
|
|
@@ -72,9 +75,16 @@ export default {
|
|
|
72
75
|
this.clearAllTimeouts();
|
|
73
76
|
const endTime = Date.now() + this.showModalAfter * 1000;
|
|
74
77
|
|
|
78
|
+
this.id = endTime;
|
|
79
|
+
globalId = endTime;
|
|
80
|
+
|
|
75
81
|
const checkInactivityTimer = () => {
|
|
76
82
|
const now = Date.now();
|
|
77
83
|
|
|
84
|
+
if (this.id !== globalId) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
78
88
|
if (now >= endTime) {
|
|
79
89
|
this.isOpen = true;
|
|
80
90
|
this.startCountdown();
|
package/components/LazyImage.vue
CHANGED
|
@@ -3,12 +3,12 @@ export default {
|
|
|
3
3
|
props: {
|
|
4
4
|
initialSrc: {
|
|
5
5
|
type: String,
|
|
6
|
-
default: require('
|
|
6
|
+
default: require('@shell/assets/images/generic-catalog.svg'),
|
|
7
7
|
},
|
|
8
8
|
|
|
9
9
|
errorSrc: {
|
|
10
10
|
type: String,
|
|
11
|
-
default: require('
|
|
11
|
+
default: require('@shell/assets/images/generic-catalog.svg'),
|
|
12
12
|
},
|
|
13
13
|
|
|
14
14
|
src: {
|
|
@@ -14,6 +14,7 @@ import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
|
|
|
14
14
|
import { escapeHtml } from '@shell/utils/string';
|
|
15
15
|
import day from 'dayjs';
|
|
16
16
|
import { sortBy } from '@shell/utils/sort';
|
|
17
|
+
import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
|
|
17
18
|
|
|
18
19
|
export default {
|
|
19
20
|
components: {
|
|
@@ -51,7 +52,7 @@ export default {
|
|
|
51
52
|
// Was the dialog opened to restore a specific snapshot, or opened on a cluster to choose
|
|
52
53
|
isCluster() {
|
|
53
54
|
const isSnapshot = this.toRestore[0]?.type.toLowerCase() === NORMAN.ETCD_BACKUP ||
|
|
54
|
-
|
|
55
|
+
this.toRestore[0]?.type.toLowerCase() === SNAPSHOT;
|
|
55
56
|
|
|
56
57
|
return !isSnapshot;
|
|
57
58
|
},
|
|
@@ -113,7 +114,7 @@ export default {
|
|
|
113
114
|
const cluster = this.toRestore?.[0];
|
|
114
115
|
let promise;
|
|
115
116
|
|
|
116
|
-
if (!cluster
|
|
117
|
+
if (!cluster?.isRke2) {
|
|
117
118
|
promise = this.$store.dispatch('rancher/findAll', { type: NORMAN.ETCD_BACKUP }).then((snapshots) => {
|
|
118
119
|
return snapshots.filter((s) => s.clusterId === cluster.metadata.name);
|
|
119
120
|
});
|
|
@@ -121,12 +122,13 @@ export default {
|
|
|
121
122
|
promise = this.$store.dispatch('management/findAll', { type: SNAPSHOT }).then((snapshots) => {
|
|
122
123
|
const toRestoreClusterName = cluster?.clusterName || cluster?.metadata?.name;
|
|
123
124
|
|
|
124
|
-
return snapshots.filter((s) => s.clusterName === toRestoreClusterName
|
|
125
|
+
return snapshots.filter((s) => s?.snapshotFile?.status === STATES_ENUM.SUCCESSFUL && s.clusterName === toRestoreClusterName
|
|
126
|
+
);
|
|
125
127
|
});
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
// Map of snapshots by name
|
|
129
|
-
const
|
|
131
|
+
const allSnapshots = await promise.then((snapshots) => {
|
|
130
132
|
return snapshots.reduce((v, s) => {
|
|
131
133
|
v[s.name] = s;
|
|
132
134
|
|
|
@@ -136,7 +138,7 @@ export default {
|
|
|
136
138
|
this.errors = exceptionToErrorsArray(err);
|
|
137
139
|
});
|
|
138
140
|
|
|
139
|
-
this.allSnapshots =
|
|
141
|
+
this.allSnapshots = allSnapshots;
|
|
140
142
|
this.sortedSnapshots = sortBy(Object.values(this.allSnapshots), ['snapshotFile.createdAt', 'created', 'metadata.creationTimestamp'], true);
|
|
141
143
|
},
|
|
142
144
|
|
|
@@ -216,7 +216,7 @@ export default {
|
|
|
216
216
|
},
|
|
217
217
|
|
|
218
218
|
parent() {
|
|
219
|
-
const displayName = this.value
|
|
219
|
+
const displayName = this.value?.parentNameOverride || this.$store.getters['type-map/labelFor'](this.schema);
|
|
220
220
|
const product = this.$store.getters['currentProduct'].name;
|
|
221
221
|
|
|
222
222
|
const defaultLocation = {
|
|
@@ -83,16 +83,18 @@ export default {
|
|
|
83
83
|
default: 'resource-details'
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
|
+
|
|
86
87
|
async fetch() {
|
|
87
88
|
const store = this.$store;
|
|
88
89
|
const route = this.$route;
|
|
89
90
|
const params = route.params;
|
|
90
|
-
|
|
91
|
+
let resource = this.resourceOverride || params.resource;
|
|
92
|
+
|
|
93
|
+
const inStore = this.storeOverride || store.getters['currentStore'](resource);
|
|
91
94
|
const realMode = this.realMode;
|
|
92
95
|
|
|
93
96
|
// eslint-disable-next-line prefer-const
|
|
94
97
|
let { namespace, id } = params;
|
|
95
|
-
let resource = this.resourceOverride || params.resource;
|
|
96
98
|
|
|
97
99
|
// There are 6 "real" modes that can be put into the query string
|
|
98
100
|
// These are mapped down to the 3 regular page "mode"s that create-edit-view components
|
|
@@ -193,6 +195,9 @@ export default {
|
|
|
193
195
|
opt: { watch: true }
|
|
194
196
|
});
|
|
195
197
|
} catch (e) {
|
|
198
|
+
if (e.status === 404 || e.status === 403) {
|
|
199
|
+
store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceIdNotFound', { resource, fqid }, true)));
|
|
200
|
+
}
|
|
196
201
|
liveModel = {};
|
|
197
202
|
notFound = fqid;
|
|
198
203
|
}
|
|
@@ -367,18 +372,7 @@ export default {
|
|
|
367
372
|
</script>
|
|
368
373
|
|
|
369
374
|
<template>
|
|
370
|
-
<Loading v-if="$fetchState.pending" />
|
|
371
|
-
<div v-else-if="notFound">
|
|
372
|
-
<IconMessage icon="icon-warning">
|
|
373
|
-
<template v-slot:message>
|
|
374
|
-
{{ t('generic.notFound') }}
|
|
375
|
-
<div>
|
|
376
|
-
<div>{{ t('generic.type') }}: {{ resource }}</div>
|
|
377
|
-
<div>{{ t('generic.id') }}: {{ notFound }}</div>
|
|
378
|
-
</div>
|
|
379
|
-
</template>
|
|
380
|
-
</IconMessage>
|
|
381
|
-
</div>
|
|
375
|
+
<Loading v-if="$fetchState.pending || notFound" />
|
|
382
376
|
<div v-else>
|
|
383
377
|
<Masthead
|
|
384
378
|
v-if="showMasthead"
|
|
@@ -70,7 +70,7 @@ export default {
|
|
|
70
70
|
|
|
71
71
|
if ( !this.hasFetch ) {
|
|
72
72
|
if ( !schema ) {
|
|
73
|
-
store.dispatch('loadingError', new Error(
|
|
73
|
+
store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceListNotFound', { resource }, true)));
|
|
74
74
|
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
@@ -4,8 +4,9 @@ import { get } from '@shell/utils/object';
|
|
|
4
4
|
import { mapPref, GROUP_RESOURCES } from '@shell/store/prefs';
|
|
5
5
|
import ButtonGroup from '@shell/components/ButtonGroup';
|
|
6
6
|
import SortableTable from '@shell/components/SortableTable';
|
|
7
|
-
import { NAMESPACE } from '@shell/config/table-headers';
|
|
7
|
+
import { NAMESPACE, AGE } from '@shell/config/table-headers';
|
|
8
8
|
import { findBy } from '@shell/utils/array';
|
|
9
|
+
import { ExtensionPoint, TableColumnLocation } from '@shell/core/types';
|
|
9
10
|
|
|
10
11
|
// Default group-by in the case the group stored in the preference does not apply
|
|
11
12
|
const DEFAULT_GROUP = 'namespace';
|
|
@@ -57,6 +58,12 @@ export default {
|
|
|
57
58
|
required: false
|
|
58
59
|
},
|
|
59
60
|
|
|
61
|
+
keyField: {
|
|
62
|
+
// Field that is unique for each row.
|
|
63
|
+
type: String,
|
|
64
|
+
default: '_key',
|
|
65
|
+
},
|
|
66
|
+
|
|
60
67
|
headers: {
|
|
61
68
|
type: Array,
|
|
62
69
|
default: null,
|
|
@@ -216,6 +223,7 @@ export default {
|
|
|
216
223
|
_headers() {
|
|
217
224
|
let headers;
|
|
218
225
|
const showNamespace = this.showNamespaceColumn;
|
|
226
|
+
const type = this.schema?.id || this.$route?.params?.resource || undefined;
|
|
219
227
|
|
|
220
228
|
if ( this.headers ) {
|
|
221
229
|
headers = this.headers.slice();
|
|
@@ -223,6 +231,46 @@ export default {
|
|
|
223
231
|
headers = this.$store.getters['type-map/headersFor'](this.schema);
|
|
224
232
|
}
|
|
225
233
|
|
|
234
|
+
// add custom table columns provided by the extensions ExtensionPoint.TABLE_COL hook
|
|
235
|
+
// gate it so that we prevent errors on older versions of dashboard
|
|
236
|
+
if (this.$store.$plugin?.getUIConfig) {
|
|
237
|
+
const extensionCols = this.$store.$plugin.getUIConfig(ExtensionPoint.TABLE_COL, TableColumnLocation.RESOURCE);
|
|
238
|
+
|
|
239
|
+
// Try and insert the columns before the Age column
|
|
240
|
+
let insertPosition = headers.length;
|
|
241
|
+
|
|
242
|
+
if (headers.length > 0) {
|
|
243
|
+
const ageColIndex = headers.findIndex((h) => h.name === AGE.name);
|
|
244
|
+
|
|
245
|
+
if (ageColIndex >= 0) {
|
|
246
|
+
insertPosition = ageColIndex;
|
|
247
|
+
} else {
|
|
248
|
+
// we've found some labels with ' ', which isn't necessarily empty (explore action/button)
|
|
249
|
+
// if we are to add cols, let's push them before these so that the UI doesn't look weird
|
|
250
|
+
const lastViableColIndex = headers.findIndex((h) => (!h.label || !h.label?.trim()) && (!h.labelKey || !h.labelKey?.trim()));
|
|
251
|
+
|
|
252
|
+
if (lastViableColIndex >= 0) {
|
|
253
|
+
insertPosition = lastViableColIndex;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// adding extension defined cols to the correct header config
|
|
259
|
+
extensionCols.forEach((col) => {
|
|
260
|
+
if (col.locationConfig.resource) {
|
|
261
|
+
col.locationConfig.resource.forEach((resource) => {
|
|
262
|
+
if (resource && type === resource) {
|
|
263
|
+
// we need the 'value' prop to be populated in order for the rows to show the values
|
|
264
|
+
if (!col.value && col.getValue) {
|
|
265
|
+
col.value = col.getValue;
|
|
266
|
+
}
|
|
267
|
+
headers.splice(insertPosition, 0, col);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
226
274
|
// If only one namespace is selected, hide the namespace column
|
|
227
275
|
if ( !showNamespace ) {
|
|
228
276
|
const idx = headers.findIndex((header) => header.name === NAMESPACE.name);
|
|
@@ -447,7 +495,7 @@ export default {
|
|
|
447
495
|
:has-advanced-filtering="hasAdvancedFiltering"
|
|
448
496
|
:adv-filter-hide-labels-as-cols="advFilterHideLabelsAsCols"
|
|
449
497
|
:adv-filter-prevent-filtering-labels="advFilterPreventFilteringLabels"
|
|
450
|
-
key-field="
|
|
498
|
+
:key-field="keyField"
|
|
451
499
|
:sort-generation-fn="safeSortGenerationFn"
|
|
452
500
|
:use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
|
|
453
501
|
:force-update-live-and-delayed="forceUpdateLiveAndDelayed"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
|
2
|
+
import PromptRestore from '@shell/components/PromptRestore.vue';
|
|
3
|
+
import Vuex from 'vuex';
|
|
4
|
+
import { ExtendedVue, Vue } from 'vue/types/vue';
|
|
5
|
+
import { DefaultProps } from 'vue/types/options';
|
|
6
|
+
import { CAPI } from '@shell/config/types';
|
|
7
|
+
import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
|
|
8
|
+
|
|
9
|
+
const SUCCESSFUL_SNAPSHOT_1 = {
|
|
10
|
+
isRke2: true,
|
|
11
|
+
type: CAPI.RANCHER_CLUSTER,
|
|
12
|
+
created: 'Thu Jul 20 2023 11:11:39',
|
|
13
|
+
snapshotFile: { status: STATES_ENUM.SUCCESSFUL },
|
|
14
|
+
id: 'id',
|
|
15
|
+
name: 'name'
|
|
16
|
+
};
|
|
17
|
+
const SUCCESSFUL_SNAPSHOT_2 = {
|
|
18
|
+
isRke2: true,
|
|
19
|
+
type: CAPI.RANCHER_CLUSTER,
|
|
20
|
+
created: 'Thu Jul 20 2022 11:11:39',
|
|
21
|
+
snapshotFile: { status: STATES_ENUM.SUCCESSFUL },
|
|
22
|
+
id: 'id2',
|
|
23
|
+
name: 'name2'
|
|
24
|
+
};
|
|
25
|
+
const FAILED_SNAPSHOT = {
|
|
26
|
+
isRke2: true,
|
|
27
|
+
type: CAPI.RANCHER_CLUSTER,
|
|
28
|
+
created: 'Thu Jul 20 2021 11:11:39',
|
|
29
|
+
snapshotFile: { status: STATES_ENUM.FAILED },
|
|
30
|
+
id: 'id_3',
|
|
31
|
+
name: 'name_3'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
describe('component: GrowlManager', () => {
|
|
35
|
+
const localVue = createLocalVue();
|
|
36
|
+
|
|
37
|
+
localVue.use(Vuex);
|
|
38
|
+
|
|
39
|
+
const testCases = [
|
|
40
|
+
[[], 0],
|
|
41
|
+
[[FAILED_SNAPSHOT], 0],
|
|
42
|
+
[[SUCCESSFUL_SNAPSHOT_1], 1],
|
|
43
|
+
[[SUCCESSFUL_SNAPSHOT_1, SUCCESSFUL_SNAPSHOT_2], 2],
|
|
44
|
+
[[FAILED_SNAPSHOT, SUCCESSFUL_SNAPSHOT_1, SUCCESSFUL_SNAPSHOT_2], 2]
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
it.each(testCases)('should list RKE2 snapshots properly', async(snapShots, expected) => {
|
|
48
|
+
const store = new Vuex.Store({
|
|
49
|
+
modules: {
|
|
50
|
+
'action-menu': {
|
|
51
|
+
namespaced: true,
|
|
52
|
+
state: {
|
|
53
|
+
showPromptRestore: true,
|
|
54
|
+
toRestore: snapShots
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
getters: { 'i18n/t': () => jest.fn(), 'prefs/get': () => jest.fn() },
|
|
59
|
+
actions: { 'management/findAll': jest.fn().mockResolvedValue(snapShots), 'rancher/findAll': jest.fn().mockResolvedValue([]) }
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const wrapper = shallowMount(PromptRestore as unknown as ExtendedVue<Vue, {}, {}, {}, DefaultProps>, {
|
|
63
|
+
store,
|
|
64
|
+
localVue
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await wrapper.vm.fetchSnapshots();
|
|
68
|
+
await wrapper.vm.$nextTick();
|
|
69
|
+
|
|
70
|
+
expect(wrapper.vm.clusterSnapshots).toHaveLength(expected);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { NORMAN, MANAGEMENT } from '@shell/config/types';
|
|
4
4
|
import { get } from '@shell/utils/object';
|
|
5
5
|
import { AZURE_MIGRATED } from '@shell/config/labels-annotations';
|
|
6
|
-
import { BLANK_CLUSTER } from '@shell/store';
|
|
6
|
+
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
7
7
|
|
|
8
8
|
export default {
|
|
9
9
|
async fetch() {
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { colorForState, stateDisplay, stateSort, STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
|
|
3
2
|
import SortableTable from '@shell/components/SortableTable';
|
|
4
|
-
import {
|
|
5
|
-
import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
|
|
6
|
-
import { randomStr } from '@shell/utils/string';
|
|
3
|
+
import { AGE } from '@shell/config/table-headers';
|
|
7
4
|
|
|
8
5
|
export default {
|
|
9
6
|
name: 'FleetResources',
|
|
@@ -19,66 +16,7 @@ export default {
|
|
|
19
16
|
|
|
20
17
|
computed: {
|
|
21
18
|
computedResources() {
|
|
22
|
-
|
|
23
|
-
const resources = this.value.status?.resources || [];
|
|
24
|
-
const out = [];
|
|
25
|
-
|
|
26
|
-
for ( const r of resources ) {
|
|
27
|
-
let namespacedName = r.name;
|
|
28
|
-
|
|
29
|
-
if ( r.namespace ) {
|
|
30
|
-
namespacedName = `${ r.namespace }:${ r.name }`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
for ( const c of clusters ) {
|
|
34
|
-
let state = r.state;
|
|
35
|
-
const perEntry = r.perClusterState?.find((x) => x.clusterId === c.id );
|
|
36
|
-
const tooMany = r.perClusterState?.length >= 10 || false;
|
|
37
|
-
|
|
38
|
-
if ( perEntry ) {
|
|
39
|
-
state = perEntry.state;
|
|
40
|
-
} else if ( tooMany ) {
|
|
41
|
-
state = STATES_ENUM.UNKNOWN;
|
|
42
|
-
} else {
|
|
43
|
-
state = STATES_ENUM.READY;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const color = colorForState(state).replace('text-', 'bg-');
|
|
47
|
-
const display = stateDisplay(state);
|
|
48
|
-
|
|
49
|
-
const detailLocation = {
|
|
50
|
-
name: `c-cluster-product-resource${ r.namespace ? '-namespace' : '' }-id`,
|
|
51
|
-
params: {
|
|
52
|
-
product: EXPLORER,
|
|
53
|
-
cluster: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME],
|
|
54
|
-
resource: r.type,
|
|
55
|
-
namespace: r.namespace,
|
|
56
|
-
id: r.name,
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
out.push({
|
|
61
|
-
key: `${ r.id }-${ c.id }-${ r.type }-${ r.namespace }-${ r.name }`,
|
|
62
|
-
tableKey: `${ r.id }-${ c.id }-${ r.type }-${ r.namespace }-${ r.name }-${ randomStr(8) }`,
|
|
63
|
-
kind: r.kind,
|
|
64
|
-
apiVersion: r.apiVersion,
|
|
65
|
-
type: r.type,
|
|
66
|
-
id: r.id,
|
|
67
|
-
namespace: r.namespace,
|
|
68
|
-
name: r.name,
|
|
69
|
-
clusterId: c.id,
|
|
70
|
-
clusterName: c.nameDisplay,
|
|
71
|
-
state,
|
|
72
|
-
stateBackground: color,
|
|
73
|
-
stateDisplay: display,
|
|
74
|
-
stateSort: stateSort(color, display),
|
|
75
|
-
namespacedName,
|
|
76
|
-
detailLocation,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return out;
|
|
19
|
+
return this.value.resourcesStatuses;
|
|
82
20
|
},
|
|
83
21
|
|
|
84
22
|
resourceHeaders() {
|
|
@@ -122,6 +60,7 @@ export default {
|
|
|
122
60
|
sort: 'namespace',
|
|
123
61
|
label: 'Namespace',
|
|
124
62
|
},
|
|
63
|
+
{ ...AGE }
|
|
125
64
|
];
|
|
126
65
|
},
|
|
127
66
|
}
|