@rancher/shell 0.3.26 → 0.3.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/translations/en-us.yaml +8 -23
- package/assets/translations/zh-hans.yaml +2 -26
- package/chart/gatekeeper.vue +2 -11
- package/chart/istio.vue +1 -10
- package/chart/logging/index.vue +2 -11
- package/chart/monitoring/index.vue +1 -9
- package/chart/rancher-backup/index.vue +1 -9
- package/components/AlertTable.vue +8 -6
- package/components/Carousel.vue +2 -1
- package/components/EmberPage.vue +2 -2
- package/components/EtcdInfoBanner.vue +12 -2
- package/components/GlobalRoleBindings.vue +10 -0
- package/components/GrafanaDashboard.vue +8 -3
- package/components/Wizard.vue +17 -1
- package/components/auth/RoleDetailEdit.vue +17 -1
- package/components/form/ArrayList.vue +20 -11
- package/components/form/__tests__/ArrayList.test.ts +44 -0
- package/components/formatter/ClusterProvider.vue +1 -18
- package/components/nav/Header.vue +5 -4
- package/components/nav/TopLevelMenu.vue +38 -15
- package/components/nav/WindowManager/ContainerLogs.vue +22 -19
- package/components/nav/__tests__/TopLevelMenu.test.ts +120 -0
- package/components/nav/__tests__/Type.test.ts +139 -0
- package/config/private-label.js +1 -1
- package/config/product/manager.js +0 -13
- package/config/settings.ts +0 -2
- package/config/types.js +0 -4
- package/core/types.ts +11 -4
- package/edit/management.cattle.io.project.vue +1 -52
- package/edit/management.cattle.io.setting.vue +31 -2
- package/edit/provisioning.cattle.io.cluster/Basics.vue +19 -107
- package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -3
- package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -128
- package/edit/workload/mixins/workload.js +14 -4
- package/middleware/authenticated.js +4 -2
- package/models/__tests__/management.cattle.io.cluster.test.ts +19 -0
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +90 -0
- package/models/cluster.x-k8s.io.machine.js +1 -1
- package/models/fleet.cattle.io.cluster.js +11 -1
- package/models/management.cattle.io.cluster.js +4 -0
- package/models/management.cattle.io.project.js +0 -36
- package/models/management.cattle.io.setting.js +11 -7
- package/models/provisioning.cattle.io.cluster.js +16 -4
- package/package.json +1 -1
- package/pages/auth/setup.vue +38 -1
- package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +2 -17
- package/pages/c/_cluster/apps/charts/index.vue +0 -15
- package/pages/c/_cluster/apps/charts/install.helpers.js +2 -13
- package/pages/c/_cluster/apps/charts/install.vue +1 -1
- package/pages/c/_cluster/auth/roles/index.vue +11 -1
- package/pages/c/_cluster/explorer/index.vue +7 -49
- package/pages/c/_cluster/manager/pages/_page.vue +4 -5
- package/pages/c/_cluster/monitoring/index.vue +26 -39
- package/pages/support/index.vue +1 -8
- package/promptRemove/management.cattle.io.project.vue +6 -9
- package/rancher-components/BadgeState/BadgeState.vue +1 -5
- package/rancher-components/Banner/Banner.test.ts +1 -51
- package/rancher-components/Banner/Banner.vue +53 -134
- package/rancher-components/Card/Card.vue +7 -24
- package/rancher-components/Form/Checkbox/Checkbox.test.ts +29 -20
- package/rancher-components/Form/Checkbox/Checkbox.vue +20 -45
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +8 -2
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +10 -22
- package/rancher-components/Form/Radio/RadioButton.vue +13 -30
- package/rancher-components/Form/Radio/RadioGroup.vue +7 -26
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +38 -25
- package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +11 -23
- package/rancher-components/LabeledTooltip/LabeledTooltip.vue +5 -19
- package/rancher-components/StringList/StringList.test.ts +49 -453
- package/rancher-components/StringList/StringList.vue +58 -92
- package/rancher-components/components/Form/Radio/RadioGroup.test.ts +30 -0
- package/rancher-components/components/Form/Radio/RadioGroup.vue +4 -0
- package/rancher-components/components/StringList/StringList.test.ts +270 -0
- package/rancher-components/components/StringList/StringList.vue +57 -18
- package/store/features.js +1 -0
- package/store/prefs.js +0 -3
- package/types/shell/index.d.ts +26 -17
- package/utils/__tests__/object.test.ts +67 -1
- package/utils/__tests__/version.test.ts +13 -23
- package/utils/cluster.js +1 -1
- package/utils/custom-validators.js +0 -2
- package/utils/error.js +16 -1
- package/utils/grafana.js +1 -2
- package/utils/monitoring.js +25 -1
- package/utils/object.js +4 -3
- package/utils/sort.js +1 -1
- package/utils/validators/formRules/__tests__/index.test.ts +49 -4
- package/utils/validators/formRules/index.ts +13 -10
- package/utils/validators/role-template.js +1 -1
- package/utils/validators/setting.js +6 -10
- package/utils/version.js +0 -13
- package/components/ChartPsp.vue +0 -76
- package/components/__tests__/ChartPsp.test.ts +0 -75
- package/components/formatter/__tests__/ClusterProvider.test.ts +0 -28
- package/rancher-components/Card/Card.test.ts +0 -37
- package/rancher-components/Form/Radio/RadioButton.test.ts +0 -31
- package/yarn-error.log +0 -200
package/pages/auth/setup.vue
CHANGED
|
@@ -18,6 +18,10 @@ import Password from '@shell/components/form/Password';
|
|
|
18
18
|
import { applyProducts } from '@shell/store/type-map';
|
|
19
19
|
import BrandImage from '@shell/components/BrandImage';
|
|
20
20
|
import { waitFor } from '@shell/utils/async';
|
|
21
|
+
import { Banner } from '@components/Banner';
|
|
22
|
+
import FormValidation from '@shell/mixins/form-validation';
|
|
23
|
+
import isUrl from 'is-url';
|
|
24
|
+
import { isLocalhost } from '@shell/utils/validators/setting';
|
|
21
25
|
|
|
22
26
|
const calcIsFirstLogin = (store) => {
|
|
23
27
|
const firstLoginSetting = store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.FIRST_LOGIN);
|
|
@@ -36,11 +40,18 @@ const calcMustChangePassword = async(store) => {
|
|
|
36
40
|
export default {
|
|
37
41
|
layout: 'unauthenticated',
|
|
38
42
|
|
|
43
|
+
mixins: [FormValidation],
|
|
44
|
+
|
|
39
45
|
data() {
|
|
40
46
|
return {
|
|
41
47
|
passwordOptions: [
|
|
42
48
|
{ label: this.t('setup.useRandom'), value: true },
|
|
43
49
|
{ label: this.t('setup.useManual'), value: false }],
|
|
50
|
+
fvFormRuleSets: [{
|
|
51
|
+
path: 'serverUrl',
|
|
52
|
+
rootObject: this,
|
|
53
|
+
rules: ['required', 'https', 'url', 'trailingForwardSlash']
|
|
54
|
+
}]
|
|
44
55
|
};
|
|
45
56
|
},
|
|
46
57
|
|
|
@@ -78,7 +89,7 @@ export default {
|
|
|
78
89
|
},
|
|
79
90
|
|
|
80
91
|
components: {
|
|
81
|
-
AsyncButton, LabeledInput, CopyToClipboard, Checkbox, RadioGroup, Password, BrandImage
|
|
92
|
+
AsyncButton, LabeledInput, CopyToClipboard, Checkbox, RadioGroup, Password, BrandImage, Banner
|
|
82
93
|
},
|
|
83
94
|
|
|
84
95
|
async asyncData({ route, req, store }) {
|
|
@@ -191,6 +202,10 @@ export default {
|
|
|
191
202
|
}
|
|
192
203
|
}
|
|
193
204
|
|
|
205
|
+
if (!isUrl(this.serverUrl) || this.fvGetPathErrors(['serverUrl']).length > 0) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
194
209
|
return true;
|
|
195
210
|
},
|
|
196
211
|
|
|
@@ -198,6 +213,10 @@ export default {
|
|
|
198
213
|
const out = findBy(this.principals, 'me', true);
|
|
199
214
|
|
|
200
215
|
return out;
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
showLocalhostWarning() {
|
|
219
|
+
return isLocalhost(this.serverUrl);
|
|
201
220
|
}
|
|
202
221
|
},
|
|
203
222
|
|
|
@@ -265,6 +284,10 @@ export default {
|
|
|
265
284
|
done() {
|
|
266
285
|
this.$router.replace('/');
|
|
267
286
|
},
|
|
287
|
+
|
|
288
|
+
onServerUrlChange(value) {
|
|
289
|
+
this.serverUrl = value.trim();
|
|
290
|
+
},
|
|
268
291
|
},
|
|
269
292
|
};
|
|
270
293
|
</script>
|
|
@@ -368,10 +391,24 @@ export default {
|
|
|
368
391
|
/>
|
|
369
392
|
</p>
|
|
370
393
|
<div class="mt-20">
|
|
394
|
+
<Banner
|
|
395
|
+
v-if="showLocalhostWarning"
|
|
396
|
+
color="warning"
|
|
397
|
+
:label="t('validation.setting.serverUrl.localhost')"
|
|
398
|
+
/>
|
|
399
|
+
<Banner
|
|
400
|
+
v-for="(err, i) in fvGetPathErrors(['serverUrl'])"
|
|
401
|
+
:key="i"
|
|
402
|
+
color="error"
|
|
403
|
+
:label="err"
|
|
404
|
+
/>
|
|
371
405
|
<LabeledInput
|
|
372
406
|
v-model="serverUrl"
|
|
373
407
|
:label="t('setup.serverUrl.label')"
|
|
374
408
|
data-testid="setup-server-url"
|
|
409
|
+
:rules="fvGetAndReportPathRules('serverUrl')"
|
|
410
|
+
:required="true"
|
|
411
|
+
@input="onServerUrlChange"
|
|
375
412
|
/>
|
|
376
413
|
</div>
|
|
377
414
|
</template>
|
|
@@ -2,30 +2,15 @@ import { ignoreVariables } from '@shell/pages/c/_cluster/apps/charts/install.hel
|
|
|
2
2
|
|
|
3
3
|
describe('fX: ignoreVariables', () => {
|
|
4
4
|
describe.each([['epinio', 'global.rbac.pspEnabled']])('given chart %p with path %p', (name, path) => {
|
|
5
|
-
it.each([
|
|
6
|
-
['v1.24.11+rke2r1'],
|
|
7
|
-
])('should not return variable path list if cluster has k8s version %p', (version) => {
|
|
8
|
-
const cluster = { kubernetesVersion: version };
|
|
9
|
-
const data = {
|
|
10
|
-
chart: { name },
|
|
11
|
-
values: { global: { rbac: { pspEnabled: undefined } } }
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const paths = ignoreVariables(cluster, data);
|
|
15
|
-
|
|
16
|
-
expect(paths).toStrictEqual([]);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
5
|
it.each([
|
|
20
6
|
['v1.25.11+rke2r1'],
|
|
21
|
-
])('should return questions
|
|
22
|
-
const cluster = { kubernetesVersion: version };
|
|
7
|
+
])('should return questions', () => {
|
|
23
8
|
const data = {
|
|
24
9
|
chart: { name },
|
|
25
10
|
values: { global: { rbac: { pspEnabled: undefined } } }
|
|
26
11
|
};
|
|
27
12
|
|
|
28
|
-
const paths = ignoreVariables(
|
|
13
|
+
const paths = ignoreVariables(data);
|
|
29
14
|
|
|
30
15
|
expect(paths).toStrictEqual([path]);
|
|
31
16
|
});
|
|
@@ -54,7 +54,6 @@ export default {
|
|
|
54
54
|
searchQuery: null,
|
|
55
55
|
showDeprecated: null,
|
|
56
56
|
showHidden: null,
|
|
57
|
-
isPspLegacy: false,
|
|
58
57
|
chartOptions: [
|
|
59
58
|
{
|
|
60
59
|
label: 'Browse',
|
|
@@ -241,14 +240,6 @@ export default {
|
|
|
241
240
|
}
|
|
242
241
|
},
|
|
243
242
|
|
|
244
|
-
created() {
|
|
245
|
-
const release = this.currentCluster?.status?.version?.gitVersion || '';
|
|
246
|
-
const isRKE2 = release.includes('rke2');
|
|
247
|
-
const version = release.match(/\d+/g);
|
|
248
|
-
|
|
249
|
-
this.isPspLegacy = version?.length ? isRKE2 && (+version[0] === 1 && +version[1] < 25) : false;
|
|
250
|
-
},
|
|
251
|
-
|
|
252
243
|
methods: {
|
|
253
244
|
colorForChart(chart) {
|
|
254
245
|
const repos = this.repoOptions;
|
|
@@ -377,12 +368,6 @@ export default {
|
|
|
377
368
|
/>
|
|
378
369
|
</div>
|
|
379
370
|
|
|
380
|
-
<Banner
|
|
381
|
-
v-if="isPspLegacy"
|
|
382
|
-
color="warning"
|
|
383
|
-
:label="t('catalog.chart.banner.legacy')"
|
|
384
|
-
/>
|
|
385
|
-
|
|
386
371
|
<TypeDescription resource="chart" />
|
|
387
372
|
<div class="left-right-split">
|
|
388
373
|
<Select
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Return list of variables to filter chart questions
|
|
3
3
|
*/
|
|
4
|
-
export const ignoreVariables = (
|
|
4
|
+
export const ignoreVariables = (data) => {
|
|
5
5
|
const pspChartMap = {
|
|
6
6
|
epinio: 'global.rbac.pspEnabled',
|
|
7
7
|
longhorn: 'enablePSP',
|
|
@@ -11,16 +11,5 @@ export const ignoreVariables = (cluster, data) => {
|
|
|
11
11
|
};
|
|
12
12
|
const path = pspChartMap[data.chart.name];
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
const clusterVersion = cluster?.kubernetesVersion || '';
|
|
16
|
-
const version = clusterVersion.match(/\d+/g);
|
|
17
|
-
const isRequiredVersion = version?.length ? +version[0] === 1 && +version[1] < 25 : false;
|
|
18
|
-
|
|
19
|
-
// Provide path as question variable to be ignored
|
|
20
|
-
if (!isRequiredVersion) {
|
|
21
|
-
return [path];
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return [];
|
|
14
|
+
return [path];
|
|
26
15
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import { mapGetters } from 'vuex';
|
|
2
3
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
3
4
|
import Tabbed from '@shell/components/Tabbed';
|
|
4
5
|
import { MANAGEMENT } from '@shell/config/types';
|
|
@@ -7,6 +8,7 @@ import Loading from '@shell/components/Loading';
|
|
|
7
8
|
import { SUBTYPE_MAPPING, CREATE_VERBS } from '@shell/models/management.cattle.io.roletemplate';
|
|
8
9
|
import { NAME } from '@shell/config/product/auth';
|
|
9
10
|
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
11
|
+
import { Banner } from '@components/Banner';
|
|
10
12
|
|
|
11
13
|
const GLOBAL = SUBTYPE_MAPPING.GLOBAL.key;
|
|
12
14
|
const CLUSTER = SUBTYPE_MAPPING.CLUSTER.key;
|
|
@@ -31,7 +33,7 @@ export default {
|
|
|
31
33
|
name: 'Roles',
|
|
32
34
|
|
|
33
35
|
components: {
|
|
34
|
-
Tab, Tabbed, ResourceTable, Loading
|
|
36
|
+
Tab, Tabbed, ResourceTable, Loading, Banner
|
|
35
37
|
},
|
|
36
38
|
|
|
37
39
|
async asyncData({ store }) {
|
|
@@ -100,6 +102,8 @@ export default {
|
|
|
100
102
|
},
|
|
101
103
|
|
|
102
104
|
computed: {
|
|
105
|
+
...mapGetters(['releaseNotesUrl']),
|
|
106
|
+
|
|
103
107
|
globalResources() {
|
|
104
108
|
return this.globalRoles;
|
|
105
109
|
},
|
|
@@ -181,6 +185,12 @@ export default {
|
|
|
181
185
|
:weight="tabs[GLOBAL].weight"
|
|
182
186
|
:label-key="tabs[GLOBAL].labelKey"
|
|
183
187
|
>
|
|
188
|
+
<Banner
|
|
189
|
+
color="warning"
|
|
190
|
+
class="mb-20"
|
|
191
|
+
>
|
|
192
|
+
<span v-clean-html="t('rbac.globalRoles.role.restricted-admin.deprecation', { releaseNotesUrl }, true)" />
|
|
193
|
+
</Banner>
|
|
184
194
|
<ResourceTable
|
|
185
195
|
:schema="tabs[GLOBAL].schema"
|
|
186
196
|
:rows="globalResources"
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
WORKLOAD_TYPES,
|
|
16
16
|
COUNT,
|
|
17
17
|
CATALOG,
|
|
18
|
-
PSP,
|
|
19
18
|
} from '@shell/config/types';
|
|
20
19
|
import { setPromiseResult } from '@shell/utils/promise';
|
|
21
20
|
import AlertTable from '@shell/components/AlertTable';
|
|
@@ -27,8 +26,7 @@ import {
|
|
|
27
26
|
STATE,
|
|
28
27
|
} from '@shell/config/table-headers';
|
|
29
28
|
|
|
30
|
-
import {
|
|
31
|
-
import { haveV1Monitoring, monitoringStatus } from '@shell/utils/monitoring';
|
|
29
|
+
import { haveV1Monitoring, monitoringStatus, canViewGrafanaLink } from '@shell/utils/monitoring';
|
|
32
30
|
import Tabbed from '@shell/components/Tabbed';
|
|
33
31
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
34
32
|
import { allDashboardsExist } from '@shell/utils/grafana';
|
|
@@ -103,6 +101,10 @@ export default {
|
|
|
103
101
|
`Determine etcd metrics`
|
|
104
102
|
);
|
|
105
103
|
|
|
104
|
+
// It's not enough to check that the grafana links are working for the current user; embedded cluster-level dashboards should only be shown if the user can view the grafana endpoint
|
|
105
|
+
// https://github.com/rancher/dashboard/issues/9792
|
|
106
|
+
setPromiseResult(canViewGrafanaLink(this.$store), this, 'canViewMetrics', 'Determine Grafana Permission');
|
|
107
|
+
|
|
106
108
|
if (this.currentCluster.isLocal && this.$store.getters['management/schemaFor'](MANAGEMENT.NODE)) {
|
|
107
109
|
this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE });
|
|
108
110
|
}
|
|
@@ -125,6 +127,7 @@ export default {
|
|
|
125
127
|
showClusterMetrics: false,
|
|
126
128
|
showK8sMetrics: false,
|
|
127
129
|
showEtcdMetrics: false,
|
|
130
|
+
canViewMetrics: false,
|
|
128
131
|
CLUSTER_METRICS_DETAIL_URL,
|
|
129
132
|
CLUSTER_METRICS_SUMMARY_URL,
|
|
130
133
|
K8S_METRICS_DETAIL_URL,
|
|
@@ -151,20 +154,6 @@ export default {
|
|
|
151
154
|
...mapGetters(['currentCluster']),
|
|
152
155
|
...monitoringStatus(),
|
|
153
156
|
|
|
154
|
-
displayPspDeprecationBanner() {
|
|
155
|
-
const cluster = this.currentCluster;
|
|
156
|
-
const major = cluster.status?.version?.major ? parseInt(cluster.status?.version?.major) : 0;
|
|
157
|
-
const minor = cluster.status?.version?.minor ? parseInt(cluster.status?.version?.minor) : 0;
|
|
158
|
-
|
|
159
|
-
if (major === 1 && minor >= 21 && minor < 25) {
|
|
160
|
-
const clusterCounts = this.$store.getters[`cluster/all`](COUNT)?.[0]?.counts;
|
|
161
|
-
|
|
162
|
-
return !!clusterCounts?.[PSP]?.summary?.count;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return false;
|
|
166
|
-
},
|
|
167
|
-
|
|
168
157
|
nodes() {
|
|
169
158
|
return this.$store.getters['cluster/all'](NODE);
|
|
170
159
|
},
|
|
@@ -173,8 +162,6 @@ export default {
|
|
|
173
162
|
return this.$store.getters['management/all'](MANAGEMENT.CLUSTER);
|
|
174
163
|
},
|
|
175
164
|
|
|
176
|
-
hidePspDeprecationBanner: mapPref(PSP_DEPRECATION_BANNER),
|
|
177
|
-
|
|
178
165
|
hasV1Monitoring() {
|
|
179
166
|
return haveV1Monitoring(this.$store.getters);
|
|
180
167
|
},
|
|
@@ -346,7 +333,7 @@ export default {
|
|
|
346
333
|
},
|
|
347
334
|
|
|
348
335
|
hasMetricsTabs() {
|
|
349
|
-
return this.showClusterMetrics || this.showK8sMetrics || this.showEtcdMetrics;
|
|
336
|
+
return this.canViewMetrics && ( this.showClusterMetrics || this.showK8sMetrics || this.showEtcdMetrics);
|
|
350
337
|
},
|
|
351
338
|
|
|
352
339
|
hasBadge() {
|
|
@@ -429,17 +416,6 @@ export default {
|
|
|
429
416
|
</div>
|
|
430
417
|
</div>
|
|
431
418
|
</header>
|
|
432
|
-
<Banner
|
|
433
|
-
v-if="displayPspDeprecationBanner && !hidePspDeprecationBanner"
|
|
434
|
-
:closable="true"
|
|
435
|
-
color="warning"
|
|
436
|
-
@close="hidePspDeprecationBanner = true"
|
|
437
|
-
>
|
|
438
|
-
<t
|
|
439
|
-
k="landing.deprecatedPsp"
|
|
440
|
-
:raw="true"
|
|
441
|
-
/>
|
|
442
|
-
</Banner>
|
|
443
419
|
<div
|
|
444
420
|
class="cluster-dashboard-glance"
|
|
445
421
|
>
|
|
@@ -473,14 +449,6 @@ export default {
|
|
|
473
449
|
:show-tooltip="true"
|
|
474
450
|
/></span>
|
|
475
451
|
</div>
|
|
476
|
-
<p
|
|
477
|
-
v-if="displayPspDeprecationBanner && hidePspDeprecationBanner"
|
|
478
|
-
v-clean-tooltip="t('landing.deprecatedPsp')"
|
|
479
|
-
class="alt-psp-deprecation-info"
|
|
480
|
-
>
|
|
481
|
-
<span>{{ t('landing.psps') }}</span>
|
|
482
|
-
<i class="icon icon-warning" />
|
|
483
|
-
</p>
|
|
484
452
|
<div :style="{'flex':1}" />
|
|
485
453
|
<div v-if="!monitoringStatus.v2 && !monitoringStatus.v1">
|
|
486
454
|
<n-link
|
|
@@ -729,16 +697,6 @@ export default {
|
|
|
729
697
|
margin-top: 0;
|
|
730
698
|
}
|
|
731
699
|
|
|
732
|
-
.alt-psp-deprecation-info {
|
|
733
|
-
display: flex;
|
|
734
|
-
align-items: center;
|
|
735
|
-
color: var(--warning);
|
|
736
|
-
|
|
737
|
-
span {
|
|
738
|
-
margin-right: 4px;
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
700
|
.monitoring-install {
|
|
743
701
|
display: flex;
|
|
744
702
|
margin-left: 10px;
|
|
@@ -7,11 +7,10 @@ export default {
|
|
|
7
7
|
data() {
|
|
8
8
|
return {
|
|
9
9
|
PAGES: {
|
|
10
|
-
'rke-drivers':
|
|
11
|
-
'rke-templates':
|
|
12
|
-
'cloud-credentials':
|
|
13
|
-
'node-templates':
|
|
14
|
-
'pod-security-policies': '/g/security/policies',
|
|
10
|
+
'rke-drivers': '/n/drivers/cluster',
|
|
11
|
+
'rke-templates': '/g/rke-templates/index',
|
|
12
|
+
'cloud-credentials': '/g/security/cloud-credentials',
|
|
13
|
+
'node-templates': '/n/node-templates',
|
|
15
14
|
}
|
|
16
15
|
};
|
|
17
16
|
}
|
|
@@ -4,16 +4,14 @@ import isEmpty from 'lodash/isEmpty';
|
|
|
4
4
|
import InstallRedirect from '@shell/utils/install-redirect';
|
|
5
5
|
import AlertTable from '@shell/components/AlertTable';
|
|
6
6
|
import { NAME, CHART_NAME } from '@shell/config/product/monitoring';
|
|
7
|
-
import { CATALOG,
|
|
7
|
+
import { CATALOG, MONITORING } from '@shell/config/types';
|
|
8
8
|
import { allHash } from '@shell/utils/promise';
|
|
9
9
|
import { findBy } from '@shell/utils/array';
|
|
10
10
|
import { getClusterPrefix } from '@shell/utils/grafana';
|
|
11
11
|
import { Banner } from '@components/Banner';
|
|
12
12
|
import LazyImage from '@shell/components/LazyImage';
|
|
13
13
|
import SimpleBox from '@shell/components/SimpleBox';
|
|
14
|
-
import { haveV1MonitoringWorkloads } from '@shell/utils/monitoring';
|
|
15
|
-
|
|
16
|
-
const CATTLE_MONITORING_NAMESPACE = 'cattle-monitoring-system';
|
|
14
|
+
import { haveV1MonitoringWorkloads, canViewAlertManagerLink, canViewGrafanaLink, canViewPrometheusLink } from '@shell/utils/monitoring';
|
|
17
15
|
|
|
18
16
|
export default {
|
|
19
17
|
components: {
|
|
@@ -96,52 +94,41 @@ export default {
|
|
|
96
94
|
const { $store, externalLinks } = this;
|
|
97
95
|
|
|
98
96
|
this.v1Installed = await haveV1MonitoringWorkloads($store);
|
|
99
|
-
const hash =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
const hash = {};
|
|
98
|
+
|
|
99
|
+
if ($store.getters['cluster/canList'](CATALOG.APP)) {
|
|
100
|
+
hash.apps = $store.dispatch('cluster/findAll', { type: CATALOG.APP });
|
|
101
|
+
}
|
|
102
|
+
const res = await allHash(hash);
|
|
103
|
+
|
|
104
|
+
const canViewAlertManager = await canViewAlertManagerLink(this.$store);
|
|
105
|
+
const canViewGrafana = await canViewGrafanaLink(this.$store);
|
|
106
|
+
const canViewPrometheus = await canViewPrometheusLink(this.$store);
|
|
103
107
|
|
|
104
|
-
if (
|
|
108
|
+
if (canViewAlertManager) {
|
|
105
109
|
const amMatch = findBy(externalLinks, 'group', 'alertmanager');
|
|
106
|
-
const grafanaMatch = findBy(externalLinks, 'group', 'grafana');
|
|
107
|
-
const promeMatch = externalLinks.filter(
|
|
108
|
-
(el) => el.group === 'prometheus'
|
|
109
|
-
);
|
|
110
110
|
|
|
111
|
+
amMatch.enabled = true;
|
|
112
|
+
}
|
|
113
|
+
if (canViewGrafana) {
|
|
114
|
+
const grafanaMatch = findBy(externalLinks, 'group', 'grafana');
|
|
111
115
|
// Generate Grafana link
|
|
112
116
|
const currentCluster = this.$store.getters['currentCluster'];
|
|
113
|
-
const rancherMonitoring = !isEmpty(
|
|
117
|
+
const rancherMonitoring = !isEmpty(res.apps) ? findBy(res.apps, 'id', 'cattle-monitoring-system/rancher-monitoring') : '';
|
|
114
118
|
const clusterPrefix = getClusterPrefix(rancherMonitoring?.currentVersion || '', currentCluster.id);
|
|
115
119
|
|
|
116
120
|
grafanaMatch.link = `${ clusterPrefix }/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-grafana:80/proxy/`;
|
|
121
|
+
grafanaMatch.enabled = true;
|
|
122
|
+
}
|
|
117
123
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
'
|
|
121
|
-
`${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-alertmanager`
|
|
122
|
-
);
|
|
123
|
-
const grafana = findBy(
|
|
124
|
-
hash.endpoints,
|
|
125
|
-
'id',
|
|
126
|
-
`${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-grafana`
|
|
127
|
-
);
|
|
128
|
-
const prometheus = findBy(
|
|
129
|
-
hash.endpoints,
|
|
130
|
-
'id',
|
|
131
|
-
`${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-prometheus`
|
|
124
|
+
if (canViewPrometheus) {
|
|
125
|
+
const promeMatch = externalLinks.filter(
|
|
126
|
+
(el) => el.group === 'prometheus'
|
|
132
127
|
);
|
|
133
128
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
if (!isEmpty(grafana) && !isEmpty(grafana.subsets)) {
|
|
138
|
-
grafanaMatch.enabled = true;
|
|
139
|
-
}
|
|
140
|
-
if (!isEmpty(prometheus) && !isEmpty(prometheus.subsets)) {
|
|
141
|
-
promeMatch.forEach((match) => {
|
|
142
|
-
match.enabled = true;
|
|
143
|
-
});
|
|
144
|
-
}
|
|
129
|
+
promeMatch.forEach((match) => {
|
|
130
|
+
match.enabled = true;
|
|
131
|
+
});
|
|
145
132
|
}
|
|
146
133
|
},
|
|
147
134
|
},
|
package/pages/support/index.vue
CHANGED
|
@@ -8,7 +8,6 @@ import { SETTING } from '@shell/config/settings';
|
|
|
8
8
|
import { addParam } from '@shell/utils/url';
|
|
9
9
|
import { isRancherPrime } from '@shell/config/version';
|
|
10
10
|
import { hasCspAdapter } from 'mixins/brand';
|
|
11
|
-
import { generateSupportLink } from '@shell/utils/version';
|
|
12
11
|
|
|
13
12
|
export default {
|
|
14
13
|
layout: 'home',
|
|
@@ -112,12 +111,6 @@ export default {
|
|
|
112
111
|
|
|
113
112
|
sccLink() {
|
|
114
113
|
return this.hasAWSSupport ? addParam('https://scc.suse.com', 'from_marketplace', '1') : 'https://scc.suse.com';
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
supportLink() {
|
|
118
|
-
const version = this.settings?.find((s) => s.id === SETTING.VERSION_RANCHER)?.value;
|
|
119
|
-
|
|
120
|
-
return generateSupportLink(version);
|
|
121
114
|
}
|
|
122
115
|
},
|
|
123
116
|
|
|
@@ -139,7 +132,7 @@ export default {
|
|
|
139
132
|
<div class="support-link">
|
|
140
133
|
<a
|
|
141
134
|
class="support-link"
|
|
142
|
-
|
|
135
|
+
href="https://www.rancher.com/support"
|
|
143
136
|
target="_blank"
|
|
144
137
|
rel="noopener noreferrer nofollow"
|
|
145
138
|
>{{ t('support.community.learnMore') }}</a>
|
|
@@ -71,12 +71,9 @@ export default {
|
|
|
71
71
|
names() {
|
|
72
72
|
return this.filteredNamespaces.map((obj) => obj.nameDisplay).slice(0, 5);
|
|
73
73
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// BUT cluster members can also manage projects and namespaces and may want to not delete the namespaces associated with the project
|
|
78
|
-
// as per https://github.com/rancher/dashboard/issues/9517 despite the namespaces cannot be seen afterwards (projectless)
|
|
79
|
-
return this.currentCluster.canUpdate || (this.currentProject.canDelete && this.filteredNamespaces.length && this.filteredNamespaces[0]?.canDelete);
|
|
74
|
+
// Only admins and cluster owners can see namespaces outside of projects
|
|
75
|
+
canSeeProjectlessNamespaces() {
|
|
76
|
+
return this.currentCluster.canUpdate;
|
|
80
77
|
}
|
|
81
78
|
},
|
|
82
79
|
methods: {
|
|
@@ -84,7 +81,7 @@ export default {
|
|
|
84
81
|
remove() {
|
|
85
82
|
// Delete all of thre namespaces and return false - this tells the prompt remove dialog to continue and delete the project
|
|
86
83
|
// Delete all namespaces if the user wouldn't be able to see them after deleting the project
|
|
87
|
-
if (this.deleteProjectNamespaces || !this.
|
|
84
|
+
if (this.deleteProjectNamespaces || !this.canSeeProjectlessNamespaces) {
|
|
88
85
|
return Promise.all(this.filteredNamespaces.map((n) => n.remove())).then(() => false);
|
|
89
86
|
}
|
|
90
87
|
|
|
@@ -100,7 +97,7 @@ export default {
|
|
|
100
97
|
<div>
|
|
101
98
|
<div class="mb-10">
|
|
102
99
|
{{ t('promptRemove.attemptingToRemove', { type }) }} <span class="display-name">{{ `${displayName}.` }}</span>
|
|
103
|
-
<template v-if="!
|
|
100
|
+
<template v-if="!canSeeProjectlessNamespaces">
|
|
104
101
|
<span class="delete-warning"> {{ t('promptRemove.willDeleteAssociatedNamespaces') }}</span> <br>
|
|
105
102
|
<div
|
|
106
103
|
v-clean-html="resourceNames(names, plusMore, t)"
|
|
@@ -109,7 +106,7 @@ export default {
|
|
|
109
106
|
</template>
|
|
110
107
|
</div>
|
|
111
108
|
<div
|
|
112
|
-
v-if="filteredNamespaces.length > 0 &&
|
|
109
|
+
v-if="filteredNamespaces.length > 0 && canSeeProjectlessNamespaces"
|
|
113
110
|
class="mt-20 remove-project-dialog"
|
|
114
111
|
>
|
|
115
112
|
<Checkbox
|
|
@@ -60,11 +60,7 @@ export default Vue.extend({
|
|
|
60
60
|
|
|
61
61
|
<template>
|
|
62
62
|
<span :class="{'badge-state': true, [bg]: true}">
|
|
63
|
-
<i
|
|
64
|
-
v-if="icon"
|
|
65
|
-
class="icon"
|
|
66
|
-
:class="{[icon]: true, 'mr-5': !!msg}"
|
|
67
|
-
/>{{ msg }}
|
|
63
|
+
<i v-if="icon" class="icon" :class="{[icon]: true, 'mr-5': !!msg}" />{{ msg }}
|
|
68
64
|
</span>
|
|
69
65
|
</template>
|
|
70
66
|
|
|
@@ -1,63 +1,13 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import { Banner } from './index';
|
|
3
|
-
import { cleanHtmlDirective } from '@shell/plugins/clean-html-directive';
|
|
4
3
|
|
|
5
4
|
describe('component: Banner', () => {
|
|
6
5
|
it('should display text based on label', () => {
|
|
7
6
|
const label = 'test';
|
|
8
|
-
const wrapper = mount(
|
|
9
|
-
Banner,
|
|
10
|
-
{
|
|
11
|
-
directives: { cleanHtmlDirective },
|
|
12
|
-
propsData: { label }
|
|
13
|
-
});
|
|
7
|
+
const wrapper = mount(Banner, { propsData: { label } });
|
|
14
8
|
|
|
15
9
|
const element = wrapper.find('span').element;
|
|
16
10
|
|
|
17
11
|
expect(element.textContent).toBe(label);
|
|
18
12
|
});
|
|
19
|
-
|
|
20
|
-
it('should display an icon', () => {
|
|
21
|
-
const icon = 'my-icon';
|
|
22
|
-
const wrapper = mount(Banner, { propsData: { icon } });
|
|
23
|
-
|
|
24
|
-
const element = wrapper.find(`.${ icon }`).element;
|
|
25
|
-
|
|
26
|
-
expect(element.classList).toContain(icon);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should not display an icon', () => {
|
|
30
|
-
const wrapper = mount(Banner);
|
|
31
|
-
|
|
32
|
-
const element = wrapper.find(`[data-testid="banner-icon"]`).element;
|
|
33
|
-
|
|
34
|
-
expect(element).not.toBeDefined();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should emit close event', () => {
|
|
38
|
-
const wrapper = mount(Banner, { propsData: { closable: true } });
|
|
39
|
-
const element = wrapper.find(`[data-testid="banner-close"]`).element;
|
|
40
|
-
|
|
41
|
-
element.click();
|
|
42
|
-
|
|
43
|
-
expect(wrapper.emitted('close')).toHaveLength(1);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should add the right color', () => {
|
|
47
|
-
const color = 'red';
|
|
48
|
-
const wrapper = mount(Banner, { propsData: { color } });
|
|
49
|
-
|
|
50
|
-
const element = wrapper.element;
|
|
51
|
-
|
|
52
|
-
expect(element.classList).toContain(color);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should stack the banner messages', () => {
|
|
56
|
-
const stacked = true;
|
|
57
|
-
const wrapper = mount(Banner, { propsData: { stacked } });
|
|
58
|
-
|
|
59
|
-
const element = wrapper.find(`[data-testid="banner-content"]`).element;
|
|
60
|
-
|
|
61
|
-
expect(element.classList).toContain('stacked');
|
|
62
|
-
});
|
|
63
13
|
});
|