@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.
Files changed (99) hide show
  1. package/assets/translations/en-us.yaml +8 -23
  2. package/assets/translations/zh-hans.yaml +2 -26
  3. package/chart/gatekeeper.vue +2 -11
  4. package/chart/istio.vue +1 -10
  5. package/chart/logging/index.vue +2 -11
  6. package/chart/monitoring/index.vue +1 -9
  7. package/chart/rancher-backup/index.vue +1 -9
  8. package/components/AlertTable.vue +8 -6
  9. package/components/Carousel.vue +2 -1
  10. package/components/EmberPage.vue +2 -2
  11. package/components/EtcdInfoBanner.vue +12 -2
  12. package/components/GlobalRoleBindings.vue +10 -0
  13. package/components/GrafanaDashboard.vue +8 -3
  14. package/components/Wizard.vue +17 -1
  15. package/components/auth/RoleDetailEdit.vue +17 -1
  16. package/components/form/ArrayList.vue +20 -11
  17. package/components/form/__tests__/ArrayList.test.ts +44 -0
  18. package/components/formatter/ClusterProvider.vue +1 -18
  19. package/components/nav/Header.vue +5 -4
  20. package/components/nav/TopLevelMenu.vue +38 -15
  21. package/components/nav/WindowManager/ContainerLogs.vue +22 -19
  22. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -0
  23. package/components/nav/__tests__/Type.test.ts +139 -0
  24. package/config/private-label.js +1 -1
  25. package/config/product/manager.js +0 -13
  26. package/config/settings.ts +0 -2
  27. package/config/types.js +0 -4
  28. package/core/types.ts +11 -4
  29. package/edit/management.cattle.io.project.vue +1 -52
  30. package/edit/management.cattle.io.setting.vue +31 -2
  31. package/edit/provisioning.cattle.io.cluster/Basics.vue +19 -107
  32. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +1 -1
  33. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -3
  34. package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -128
  35. package/edit/workload/mixins/workload.js +14 -4
  36. package/middleware/authenticated.js +4 -2
  37. package/models/__tests__/management.cattle.io.cluster.test.ts +19 -0
  38. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +90 -0
  39. package/models/cluster.x-k8s.io.machine.js +1 -1
  40. package/models/fleet.cattle.io.cluster.js +11 -1
  41. package/models/management.cattle.io.cluster.js +4 -0
  42. package/models/management.cattle.io.project.js +0 -36
  43. package/models/management.cattle.io.setting.js +11 -7
  44. package/models/provisioning.cattle.io.cluster.js +16 -4
  45. package/package.json +1 -1
  46. package/pages/auth/setup.vue +38 -1
  47. package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +2 -17
  48. package/pages/c/_cluster/apps/charts/index.vue +0 -15
  49. package/pages/c/_cluster/apps/charts/install.helpers.js +2 -13
  50. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  51. package/pages/c/_cluster/auth/roles/index.vue +11 -1
  52. package/pages/c/_cluster/explorer/index.vue +7 -49
  53. package/pages/c/_cluster/manager/pages/_page.vue +4 -5
  54. package/pages/c/_cluster/monitoring/index.vue +26 -39
  55. package/pages/support/index.vue +1 -8
  56. package/promptRemove/management.cattle.io.project.vue +6 -9
  57. package/rancher-components/BadgeState/BadgeState.vue +1 -5
  58. package/rancher-components/Banner/Banner.test.ts +1 -51
  59. package/rancher-components/Banner/Banner.vue +53 -134
  60. package/rancher-components/Card/Card.vue +7 -24
  61. package/rancher-components/Form/Checkbox/Checkbox.test.ts +29 -20
  62. package/rancher-components/Form/Checkbox/Checkbox.vue +20 -45
  63. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +8 -2
  64. package/rancher-components/Form/LabeledInput/LabeledInput.vue +10 -22
  65. package/rancher-components/Form/Radio/RadioButton.vue +13 -30
  66. package/rancher-components/Form/Radio/RadioGroup.vue +7 -26
  67. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +6 -7
  68. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.test.ts +38 -25
  69. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +11 -23
  70. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +5 -19
  71. package/rancher-components/StringList/StringList.test.ts +49 -453
  72. package/rancher-components/StringList/StringList.vue +58 -92
  73. package/rancher-components/components/Form/Radio/RadioGroup.test.ts +30 -0
  74. package/rancher-components/components/Form/Radio/RadioGroup.vue +4 -0
  75. package/rancher-components/components/StringList/StringList.test.ts +270 -0
  76. package/rancher-components/components/StringList/StringList.vue +57 -18
  77. package/store/features.js +1 -0
  78. package/store/prefs.js +0 -3
  79. package/types/shell/index.d.ts +26 -17
  80. package/utils/__tests__/object.test.ts +67 -1
  81. package/utils/__tests__/version.test.ts +13 -23
  82. package/utils/cluster.js +1 -1
  83. package/utils/custom-validators.js +0 -2
  84. package/utils/error.js +16 -1
  85. package/utils/grafana.js +1 -2
  86. package/utils/monitoring.js +25 -1
  87. package/utils/object.js +4 -3
  88. package/utils/sort.js +1 -1
  89. package/utils/validators/formRules/__tests__/index.test.ts +49 -4
  90. package/utils/validators/formRules/index.ts +13 -10
  91. package/utils/validators/role-template.js +1 -1
  92. package/utils/validators/setting.js +6 -10
  93. package/utils/version.js +0 -13
  94. package/components/ChartPsp.vue +0 -76
  95. package/components/__tests__/ChartPsp.test.ts +0 -75
  96. package/components/formatter/__tests__/ClusterProvider.test.ts +0 -28
  97. package/rancher-components/Card/Card.test.ts +0 -37
  98. package/rancher-components/Form/Radio/RadioButton.test.ts +0 -31
  99. package/yarn-error.log +0 -200
@@ -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 if cluster has k8s version %p', (version) => {
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(cluster, data);
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 = (cluster, data) => {
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
- if (path) {
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
  };
@@ -473,7 +473,7 @@ export default {
473
473
  * Return list of variables to filter chart questions
474
474
  */
475
475
  ignoreVariables() {
476
- return ignoreVariables(this.currentCluster, this.versionInfo);
476
+ return ignoreVariables(this.versionInfo);
477
477
  },
478
478
 
479
479
  namespaceIsNew() {
@@ -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 { mapPref, PSP_DEPRECATION_BANNER } from '@shell/store/prefs';
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': '/n/drivers/cluster',
11
- 'rke-templates': '/g/rke-templates/index',
12
- 'cloud-credentials': '/g/security/cloud-credentials',
13
- 'node-templates': '/n/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, ENDPOINTS, MONITORING } from '@shell/config/types';
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 = await allHash({
100
- apps: $store.dispatch('cluster/findAll', { type: CATALOG.APP }),
101
- endpoints: $store.dispatch('cluster/findAll', { type: ENDPOINTS }),
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 (!isEmpty(hash.endpoints)) {
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(hash.apps) ? findBy(hash.apps, 'id', 'cattle-monitoring-system/rancher-monitoring') : '';
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
- const alertmanager = findBy(
119
- hash.endpoints,
120
- 'id',
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
- if (!isEmpty(alertmanager) && !isEmpty(alertmanager.subsets)) {
135
- amMatch.enabled = true;
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
  },
@@ -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
- :href="supportLink"
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
- canManageNamespaces() {
76
- // Only admins and cluster owners can see namespaces outside of projects
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.canManageNamespaces) {
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="!canManageNamespaces">
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 && canManageNamespaces"
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
  });