@rancher/shell 3.0.1-rc.2 → 3.0.1-rc.4

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 (56) hide show
  1. package/assets/styles/app.scss +0 -1
  2. package/assets/translations/en-us.yaml +14 -12
  3. package/assets/translations/zh-hans.yaml +0 -9
  4. package/components/GlobalRoleBindings.vue +0 -7
  5. package/components/ResourceDetail/Masthead.vue +1 -1
  6. package/components/ResourceDetail/index.vue +66 -11
  7. package/components/ResourceYaml.vue +0 -53
  8. package/components/SortableTable/THead.vue +1 -1
  9. package/components/auth/RoleDetailEdit.vue +0 -16
  10. package/components/form/UnitInput.vue +1 -1
  11. package/components/form/__tests__/UnitInput.test.ts +1 -1
  12. package/components/nav/TopLevelMenu.vue +1 -1
  13. package/components/nav/WindowManager/ContainerShell.vue +13 -4
  14. package/components/nav/WindowManager/__tests__/ContainerShell.test.ts +20 -18
  15. package/composables/useLabeledFormElement.ts +6 -2
  16. package/config/router/navigation-guards/index.js +1 -2
  17. package/config/settings.ts +2 -7
  18. package/edit/catalog.cattle.io.clusterrepo.vue +0 -9
  19. package/edit/provisioning.cattle.io.cluster/rke2.vue +3 -4
  20. package/edit/workload/index.vue +1 -1
  21. package/edit/workload/storage/csi/index.vue +29 -1
  22. package/edit/workload/storage/index.vue +1 -0
  23. package/initialize/App.vue +3 -10
  24. package/initialize/install-plugins.js +1 -2
  25. package/list/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +6 -2
  26. package/models/__tests__/management.cattle.io.cluster.test.ts +0 -42
  27. package/models/management.cattle.io.cluster.js +0 -24
  28. package/models/management.cattle.io.globalrole.js +1 -0
  29. package/models/nodedriver.js +2 -2
  30. package/models/provisioning.cattle.io.cluster.js +24 -2
  31. package/package.json +2 -3
  32. package/pages/auth/setup.vue +7 -28
  33. package/pages/c/_cluster/auth/roles/index.vue +1 -11
  34. package/pages/c/_cluster/explorer/__tests__/index.test.ts +71 -1
  35. package/pages/c/_cluster/explorer/index.vue +6 -2
  36. package/rancher-components/Banner/Banner.vue +1 -0
  37. package/rancher-components/Form/Checkbox/Checkbox.vue +2 -0
  38. package/rancher-components/Form/LabeledInput/LabeledInput.vue +2 -0
  39. package/rancher-components/Form/Radio/RadioButton.vue +2 -0
  40. package/rancher-components/Form/Radio/RadioGroup.vue +2 -0
  41. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +3 -1
  42. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +3 -0
  43. package/rancher-components/StringList/StringList.test.ts +15 -15
  44. package/rancher-components/StringList/StringList.vue +3 -0
  45. package/scripts/extension/helm/charts/ui-plugin-server/Chart.yaml +0 -2
  46. package/scripts/test-plugins-build.sh +3 -2
  47. package/types/shell/index.d.ts +20 -0
  48. package/utils/__tests__/object.test.ts +30 -1
  49. package/utils/object.js +28 -0
  50. package/utils/uiplugins.ts +293 -0
  51. package/vue.config.js +2 -1
  52. package/components/templates/error.vue +0 -131
  53. package/config/router/navigation-guards/history.js +0 -13
  54. package/machine-config/__tests__/vmwarevsphere-pool-config-merge.test.ts +0 -30
  55. package/machine-config/vmwarevsphere-pool-config-merge.ts +0 -25
  56. package/plugins/back-button.js +0 -3
@@ -4,7 +4,6 @@ import { install as installAuthentication } from '@shell/config/router/navigatio
4
4
  import { install as installRuntimeExtensionRoute } from '@shell/config/router/navigation-guards/runtime-extension-route';
5
5
  import { install as installI18N } from '@shell/config/router/navigation-guards/i18n';
6
6
  import { install as installProducts } from '@shell/config/router/navigation-guards/products';
7
- import { install as installHistory } from '@shell/config/router/navigation-guards/history';
8
7
  import { install as installClusters } from '@shell/config/router/navigation-guards/clusters';
9
8
  import { install as installHandleInstallRedirect } from '@shell/config/router/navigation-guards/install-redirect';
10
9
  import { install as installPageTitle } from '@shell/config/router/navigation-guards/page-title';
@@ -17,7 +16,7 @@ export function installNavigationGuards(router, context) {
17
16
  // NOTE: the order of the installation matters.
18
17
  // Be intentional when adding, removing or modifying the guards that are installed.
19
18
 
20
- const navigationGuardInstallers = [installLoadInitialSettings, installAttemptFirstLogin, installAuthentication, installProducts, installHistory, installClusters, installRuntimeExtensionRoute, installI18N, installHandleInstallRedirect, installPageTitle, installServerUpgradeGrowl];
19
+ const navigationGuardInstallers = [installLoadInitialSettings, installAttemptFirstLogin, installAuthentication, installProducts, installClusters, installRuntimeExtensionRoute, installI18N, installHandleInstallRedirect, installPageTitle, installServerUpgradeGrowl];
21
20
 
22
21
  navigationGuardInstallers.forEach((installer) => installer(router, context));
23
22
  }
@@ -51,7 +51,6 @@ export const SETTING = {
51
51
  INGRESS_IP_DOMAIN: 'ingress-ip-domain',
52
52
  SERVER_URL: 'server-url',
53
53
  RKE_METADATA_CONFIG: 'rke-metadata-config',
54
- TELEMETRY: 'telemetry-opt',
55
54
  EULA_AGREED: 'eula-agreed',
56
55
  AUTH_USER_INFO_MAX_AGE_SECONDS: 'auth-user-info-max-age-seconds',
57
56
  AUTH_USER_SESSION_TTL_MINUTES: 'auth-user-session-ttl-minutes',
@@ -156,12 +155,8 @@ export const ALLOWED_SETTINGS: GlobalSetting = {
156
155
  },
157
156
  [SETTING.BRAND]: { canReset: true },
158
157
  [SETTING.CLUSTER_TEMPLATE_ENFORCEMENT]: { kind: 'boolean' },
159
- [SETTING.TELEMETRY]: {
160
- kind: 'enum',
161
- options: ['prompt', 'in', 'out']
162
- },
163
- [SETTING.HIDE_LOCAL_CLUSTER]: { kind: 'boolean' },
164
- [SETTING.AGENT_TLS_MODE]: {
158
+ [SETTING.HIDE_LOCAL_CLUSTER]: { kind: 'boolean' },
159
+ [SETTING.AGENT_TLS_MODE]: {
165
160
  kind: 'enum',
166
161
  options: ['strict', 'system-store'],
167
162
  warning: 'agent-tls-mode'
@@ -287,12 +287,3 @@ export default {
287
287
  />
288
288
  </form>
289
289
  </template>
290
-
291
- <style lang="scss">
292
- span.oci-experimental-badge {
293
- background-color: var(--warning);
294
- color: var(--darker-active-bg);
295
- font-size: 12px;
296
- padding: 2px 6px;
297
- }
298
- </style>
@@ -22,11 +22,10 @@ import { _CREATE, _EDIT, _VIEW } from '@shell/config/query-params';
22
22
  import { findBy, removeObject, clear } from '@shell/utils/array';
23
23
  import { createYaml } from '@shell/utils/create-yaml';
24
24
  import {
25
- clone, diff, set, get, isEmpty
25
+ clone, diff, set, get, isEmpty, mergeWithReplaceArrays
26
26
  } from '@shell/utils/object';
27
27
  import { allHash } from '@shell/utils/promise';
28
28
  import { sortBy } from '@shell/utils/sort';
29
- import { vspherePoolConfigMerge } from '@shell/machine-config/vmwarevsphere-pool-config-merge';
30
29
 
31
30
  import { compare, sortable } from '@shell/utils/version';
32
31
  import { isHarvesterSatisfiesVersion, labelForAddon } from '@shell/utils/cluster';
@@ -1260,7 +1259,7 @@ export default {
1260
1259
  delete clonedCurrentConfig.metadata;
1261
1260
 
1262
1261
  if (this.provider === VMWARE_VSPHERE) {
1263
- machinePool.config = vspherePoolConfigMerge(clonedLatestConfig, clonedCurrentConfig);
1262
+ machinePool.config = mergeWithReplaceArrays(clonedLatestConfig, clonedCurrentConfig);
1264
1263
  } else {
1265
1264
  machinePool.config = merge(clonedLatestConfig, clonedCurrentConfig);
1266
1265
  }
@@ -1644,7 +1643,7 @@ export default {
1644
1643
  const defaultChartValue = this.versionInfo[name];
1645
1644
  const key = this.chartVersionKey(name);
1646
1645
 
1647
- return merge({}, defaultChartValue?.values || {}, this.userChartValues[key] || {});
1646
+ return mergeWithReplaceArrays(defaultChartValue?.values, this.userChartValues[key]);
1648
1647
  },
1649
1648
 
1650
1649
  initServerAgentArgs() {
@@ -415,7 +415,7 @@ export default {
415
415
  >
416
416
  <Tab
417
417
  :label="t('workload.storage.title')"
418
- name="storage"
418
+ name="storage-pod"
419
419
  :weight="tabWeightMap['storage']"
420
420
  @active="$refs.storage.refresh()"
421
421
  >
@@ -44,6 +44,33 @@ export default {
44
44
 
45
45
  ...mapGetters({ t: 'i18n/t' })
46
46
  },
47
+
48
+ methods: {
49
+ /**
50
+ * Retrieves the label for a given option
51
+ * @param option The option for which to retrieve the label. option can be
52
+ * either a string or an object. If it is an object, is should have a `label`
53
+ * property associated with it.
54
+ */
55
+ getOptionLabel(option) {
56
+ if (typeof option === 'string') {
57
+ return this.getOptionLabelString(option);
58
+ }
59
+
60
+ const { label } = option;
61
+
62
+ return this.getOptionLabelString(label);
63
+ },
64
+ /**
65
+ * Translates a given key into a localized string.
66
+ * @param key The key to be translated.
67
+ */
68
+ getOptionLabelString(key) {
69
+ // Periods are replaced with `-` to prevent conflict with the default key
70
+ // separator.
71
+ return this.t(`workload.storage.csi.drivers.'${ key.replaceAll('.', '-') }'`);
72
+ }
73
+ }
47
74
  };
48
75
  </script>
49
76
 
@@ -71,10 +98,11 @@ export default {
71
98
  <div class="col span-6">
72
99
  <LabeledSelect
73
100
  v-model:value="value.csi.driver"
101
+ data-testid="workload-storage-driver"
74
102
  :mode="mode"
75
103
  :label="t('workload.storage.driver')"
76
104
  :options="driverOpts"
77
- :get-option-label="opt=>t(`workload.storage.csi.drivers.'${opt}'`)"
105
+ :get-option-label="getOptionLabel"
78
106
  :required="true"
79
107
  />
80
108
  </div>
@@ -301,6 +301,7 @@ export default {
301
301
  :button-label="t('workload.storage.addVolume')"
302
302
  :dropdown-options="volumeTypeOptions"
303
303
  size="sm"
304
+ data-testid="dropdown-button-storage-volume"
304
305
  @click-action="e=>addVolume(e.value)"
305
306
  />
306
307
  </template>
@@ -1,15 +1,10 @@
1
1
  <script>
2
- import NuxtError from '@shell/components/templates/error.vue';
3
2
  import NuxtLoading from '@shell/components/nav/GlobalLoading.vue';
4
3
 
5
4
  import '@shell/assets/styles/app.scss';
6
5
 
7
6
  export default {
8
- data: () => ({
9
- isOnline: true,
10
-
11
- showErrorPage: false,
12
- }),
7
+ data: () => ({ isOnline: true }),
13
8
 
14
9
  created() {
15
10
  // add to window so we can listen when ready
@@ -60,7 +55,7 @@ export default {
60
55
  },
61
56
  },
62
57
 
63
- components: { NuxtLoading, NuxtError }
58
+ components: { NuxtLoading }
64
59
  };
65
60
  </script>
66
61
  <template>
@@ -68,10 +63,8 @@ export default {
68
63
  <NuxtLoading ref="loading" />
69
64
  <div
70
65
  id="__layout"
71
- :key="showErrorPage"
72
66
  >
73
- <NuxtError v-if="showErrorPage" />
74
- <router-view v-else />
67
+ <router-view />
75
68
  </div>
76
69
  </div>
77
70
  </template>
@@ -15,7 +15,6 @@ import axios from '@shell/utils/axios';
15
15
  import cookieUniversal from '@shell/utils/cookie-universal';
16
16
  import config from '@shell/utils/config';
17
17
  import axiosShell from '@shell/plugins/axios';
18
- import backButton from '@shell/plugins/back-button';
19
18
  import codeMirror from '@shell/plugins/codemirror-loader';
20
19
  import { InstallCodeMirror } from 'codemirror-editor-vue3';
21
20
  import * as intNumber from '@shell/directives/int-number';
@@ -42,7 +41,7 @@ export async function installPlugins(vueApp) {
42
41
  }
43
42
 
44
43
  export async function installInjectedPlugins(app, vueApp) {
45
- const pluginDefinitions = [config, cookieUniversal, axios, plugins, pluginsLoader, axiosShell, intNumber, codeMirror, nuxtClientInit, replaceAll, backButton, plugin, steveCreateWorker, emberCookie];
44
+ const pluginDefinitions = [config, cookieUniversal, axios, plugins, pluginsLoader, axiosShell, intNumber, codeMirror, nuxtClientInit, replaceAll, plugin, steveCreateWorker, emberCookie];
46
45
 
47
46
  const installations = pluginDefinitions.map(async(pluginDefinition) => {
48
47
  if (typeof pluginDefinition === 'function') {
@@ -19,13 +19,17 @@ export default {
19
19
  required: true,
20
20
  },
21
21
  },
22
+ emits: ['error'],
22
23
 
23
24
  async fetch() {
24
25
  const inStore = this.$store.getters['currentStore']();
25
26
 
27
+ try {
26
28
  // Fetch storage classes so we can determine if a PVC can be expanded
27
- this.$store.dispatch(`${ inStore }/findAll`, { type: STORAGE_CLASS });
28
-
29
+ await this.$store.dispatch(`${ inStore }/findAll`, { type: STORAGE_CLASS });
30
+ } catch (e) {
31
+ this.$emit('error', e?.data || e);
32
+ }
29
33
  await this.$fetchType(this.resource);
30
34
  }
31
35
  };
@@ -4,28 +4,6 @@ jest.mock('@shell/utils/clipboard', () => {
4
4
  return { copyTextToClipboard: jest.fn(() => Promise.resolve({})) };
5
5
  });
6
6
 
7
- const importedRKE2ClusterInfo = { status: { driver: 'rke2', provider: 'rke2' } };
8
-
9
- const provisionedRKE2ClusterInfo = { status: { driver: 'rke2', provider: 'imported' } };
10
-
11
- const importedK3sClusterInfo = { status: { driver: 'k3s', provider: 'k3s' } };
12
-
13
- const provisionedK3sClusterInfo = { status: { driver: 'k3s', provider: 'imported' } };
14
-
15
- const importedAksClusterInfo = { spec: { aksConfig: { imported: true } }, status: { provider: 'aks', driver: 'AKS' } };
16
-
17
- const provisionedAksClusterInfo = { spec: { aksConfig: { imported: false } }, status: { provider: 'aks', driver: 'AKS' } };
18
-
19
- const importedRKE1ClusterInfo = { status: { provider: 'rke', driver: 'imported' } };
20
-
21
- const provisionedRKE1ClusterInfo = { status: { provider: 'rke', driver: 'rancherKubernetesEngine' } };
22
-
23
- const localRKE1ClusterInfo = { status: { provider: 'rke', driver: 'imported' } };
24
-
25
- const localRKE2ClusterInfo = { status: { provider: 'rke2', driver: 'rke2' } };
26
-
27
- const localEKSClusterInfo = { status: { provider: 'eks', driver: 'imported' } };
28
-
29
7
  describe('class MgmtCluster', () => {
30
8
  describe('provisioner', () => {
31
9
  const testCases = [
@@ -42,24 +20,4 @@ describe('class MgmtCluster', () => {
42
20
  }
43
21
  );
44
22
  });
45
-
46
- describe('isImported', () => {
47
- it.each([
48
- [importedRKE2ClusterInfo, true],
49
- [provisionedRKE2ClusterInfo, false],
50
- [importedK3sClusterInfo, true],
51
- [provisionedK3sClusterInfo, false],
52
- [importedAksClusterInfo, true],
53
- [provisionedAksClusterInfo, false],
54
- [importedRKE1ClusterInfo, true],
55
- [provisionedRKE1ClusterInfo, false],
56
- [localRKE1ClusterInfo, true],
57
- [localRKE2ClusterInfo, true],
58
- [localEKSClusterInfo, true]
59
- ])('should return isImported based on props data', (clusterData, expected) => {
60
- const cluster = new MgmtCluster(clusterData);
61
-
62
- expect(cluster.isImported).toBe(expected);
63
- });
64
- });
65
23
  });
@@ -89,30 +89,6 @@ export default class MgmtCluster extends SteveModel {
89
89
  return pools.filter((x) => x.spec?.clusterName === this.id);
90
90
  }
91
91
 
92
- get isImported() {
93
- if (this.isLocal) {
94
- return false;
95
- }
96
- // imported rke2 and k3s have status.driver === rke2 and k3s respectively
97
- // Provisioned rke2 and k3s have status.driver === imported
98
- if (this.status?.provider === 'k3s' || this.status?.provider === 'rke2') {
99
- return this.status?.driver === this.status?.provider;
100
- }
101
-
102
- // imported KEv2
103
- const kontainerConfigs = ['aksConfig', 'eksConfig', 'gkeConfig'];
104
-
105
- const isImportedKontainer = kontainerConfigs.filter((key) => {
106
- return this.spec?.[key]?.imported === true;
107
- }).length;
108
-
109
- if (isImportedKontainer) {
110
- return true;
111
- }
112
-
113
- return this.provisioner === 'imported';
114
- }
115
-
116
92
  get provisioner() {
117
93
  // For imported K3s clusters, this.status.driver is 'k3s.'
118
94
  return this.status?.driver ? this.status.driver : 'imported';
@@ -117,6 +117,7 @@ export default class GlobalRole extends SteveDescriptionModel {
117
117
  norman.id = this.id;
118
118
  norman.name = this.displayName;
119
119
  norman.description = this.description;
120
+ norman.inheritedClusterRoles = this.inheritedClusterRoles;
120
121
 
121
122
  return norman;
122
123
  })();
@@ -18,7 +18,7 @@ export default class NodeDriver extends Driver {
18
18
  icon: 'icon icon-play',
19
19
  bulkable: true,
20
20
  bulkAction: 'activateBulk',
21
- enabled: !!this.actions.activate && this.state === 'inactive',
21
+ enabled: !!this.actions?.activate && this.state === 'inactive',
22
22
  },
23
23
  {
24
24
  action: 'deactivate',
@@ -26,7 +26,7 @@ export default class NodeDriver extends Driver {
26
26
  icon: 'icon icon-pause',
27
27
  bulkable: true,
28
28
  bulkAction: 'deactivateBulk',
29
- enabled: !!this.actions.deactivate && this.state === 'active',
29
+ enabled: !!this.actions?.deactivate && this.state === 'active',
30
30
  weight: -1,
31
31
  },
32
32
  { divider: true },
@@ -245,7 +245,7 @@ export default class ProvCluster extends SteveModel {
245
245
  }
246
246
 
247
247
  get canDelete() {
248
- return super.canDelete && this.stateObj.name !== 'removing';
248
+ return super.canDelete && this.stateObj?.name !== 'removing';
249
249
  }
250
250
 
251
251
  get canEditYaml() {
@@ -282,7 +282,29 @@ export default class ProvCluster extends SteveModel {
282
282
  }
283
283
 
284
284
  get isImported() {
285
- return this.mgmt?.isImported;
285
+ if (this.isLocal) {
286
+ return false;
287
+ }
288
+
289
+ // imported rke2 and k3s have status.driver === rke2 and k3s respectively
290
+ // Provisioned rke2 and k3s have status.driver === imported
291
+ if (this.mgmt?.status?.provider === 'k3s' || this.mgmt?.status?.provider === 'rke2') {
292
+ return this.mgmt?.status?.driver === this.mgmt?.status?.provider;
293
+ }
294
+
295
+ // imported KEv2
296
+ // we can't rely on this.provisioner to determine imported-ness for these clusters, as it will return 'aks' 'eks' 'gke' for both provisioned and imported clusters
297
+ const kontainerConfigs = ['aksConfig', 'eksConfig', 'gkeConfig'];
298
+
299
+ const isImportedKontainer = kontainerConfigs.filter((key) => {
300
+ return this.mgmt?.spec?.[key]?.imported === true;
301
+ }).length;
302
+
303
+ if (isImportedKontainer) {
304
+ return true;
305
+ }
306
+
307
+ return this.provisioner === 'imported';
286
308
  }
287
309
 
288
310
  get isCustom() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.1-rc.2",
3
+ "version": "3.0.1-rc.4",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -131,7 +131,6 @@
131
131
  "vue-router": "4.4.3",
132
132
  "vue-select": "4.0.0-beta.6",
133
133
  "vue-server-renderer": "2.7.16",
134
- "vue-template-compiler": "2.7.16",
135
134
  "vue3-resize": "0.2.0",
136
135
  "vue3-virtual-scroll-list": "0.2.1",
137
136
  "vuedraggable": "4.1.0",
@@ -170,4 +169,4 @@
170
169
  ".vue"
171
170
  ]
172
171
  }
173
- }
172
+ }
@@ -11,7 +11,6 @@ import { getVendor, getProduct, setVendor } from '@shell/config/private-label';
11
11
  import { RadioGroup } from '@components/Form/Radio';
12
12
  import { setSetting } from '@shell/utils/settings';
13
13
  import { SETTING } from '@shell/config/settings';
14
- import { isDevBuild } from '@shell/utils/version';
15
14
  import { exceptionToErrorsArray } from '@shell/utils/error';
16
15
  import Password from '@shell/components/form/Password';
17
16
  import { applyProducts } from '@shell/store/type-map';
@@ -66,7 +65,6 @@ export default {
66
65
  v3User: null,
67
66
  serverUrl: null,
68
67
  mcmEnabled: null,
69
- telemetry: null,
70
68
  eula: false,
71
69
  principals: null,
72
70
  errors: []
@@ -101,16 +99,7 @@ export default {
101
99
  },
102
100
 
103
101
  async fetch() {
104
- const telemetrySetting = this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.TELEMETRY);
105
102
  const serverUrlSetting = this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.SERVER_URL);
106
- const rancherVersionSetting = this.$store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.VERSION_RANCHER);
107
- let telemetry = true;
108
-
109
- if (telemetrySetting?.value && telemetrySetting.value !== 'prompt') {
110
- telemetry = telemetrySetting.value !== 'out';
111
- } else if (!rancherVersionSetting?.value || isDevBuild(rancherVersionSetting?.value)) {
112
- telemetry = false;
113
- }
114
103
 
115
104
  let plSetting;
116
105
 
@@ -163,7 +152,6 @@ export default {
163
152
  this['v3User'] = v3User;
164
153
  this['serverUrl'] = serverUrl;
165
154
  this['mcmEnabled'] = mcmEnabled;
166
- this['telemetry'] = telemetry;
167
155
  this['principals'] = principals;
168
156
  },
169
157
 
@@ -244,7 +232,6 @@ export default {
244
232
 
245
233
  if (this.isFirstLogin) {
246
234
  promises.push( setSetting(this.$store, SETTING.EULA_AGREED, (new Date()).toISOString()) );
247
- promises.push( setSetting(this.$store, SETTING.TELEMETRY, this.telemetry ? 'in' : 'out') );
248
235
 
249
236
  if ( this.mcmEnabled && this.serverUrl ) {
250
237
  promises.push( setSetting(this.$store, SETTING.SERVER_URL, this.serverUrl) );
@@ -406,20 +393,6 @@ export default {
406
393
  </div>
407
394
  </template>
408
395
 
409
- <div class="checkbox mt-40">
410
- <Checkbox
411
- id="checkbox-telemetry"
412
- v-model:value="telemetry"
413
- >
414
- <template #label>
415
- <t
416
- k="setup.telemetry"
417
- :raw="true"
418
- :name="productName"
419
- />
420
- </template>
421
- </Checkbox>
422
- </div>
423
396
  <div class="checkbox pt-10 eula">
424
397
  <Checkbox
425
398
  id="checkbox-eula"
@@ -461,6 +434,9 @@ export default {
461
434
  </h4>
462
435
  </div>
463
436
  </div>
437
+ <div>
438
+ &nbsp;
439
+ </div>
464
440
  </div>
465
441
  <BrandImage
466
442
  class="col span-6 landscape"
@@ -517,11 +493,14 @@ export default {
517
493
  width: 51%;
518
494
 
519
495
  & > div:first-of-type {
520
- flex:3;
496
+ flex: 3;
521
497
  }
522
498
  & > div:nth-of-type(2) {
523
499
  flex: 9;
524
500
  }
501
+ & > div:nth-of-type(3) {
502
+ flex: 2;
503
+ }
525
504
  }
526
505
 
527
506
  .setup-title {
@@ -1,5 +1,4 @@
1
1
  <script>
2
- import { mapGetters } from 'vuex';
3
2
  import Tab from '@shell/components/Tabbed/Tab';
4
3
  import Tabbed from '@shell/components/Tabbed';
5
4
  import { MANAGEMENT } from '@shell/config/types';
@@ -8,7 +7,6 @@ import Loading from '@shell/components/Loading';
8
7
  import { SUBTYPE_MAPPING, CREATE_VERBS } from '@shell/models/management.cattle.io.roletemplate';
9
8
  import { NAME } from '@shell/config/product/auth';
10
9
  import { BLANK_CLUSTER } from '@shell/store/store-types.js';
11
- import { Banner } from '@components/Banner';
12
10
 
13
11
  const GLOBAL = SUBTYPE_MAPPING.GLOBAL.key;
14
12
  const CLUSTER = SUBTYPE_MAPPING.CLUSTER.key;
@@ -33,7 +31,7 @@ export default {
33
31
  name: 'Roles',
34
32
 
35
33
  components: {
36
- Tab, Tabbed, ResourceTable, Loading, Banner
34
+ Tab, Tabbed, ResourceTable, Loading
37
35
  },
38
36
 
39
37
  async fetch() {
@@ -100,8 +98,6 @@ export default {
100
98
  },
101
99
 
102
100
  computed: {
103
- ...mapGetters(['releaseNotesUrl']),
104
-
105
101
  globalResources() {
106
102
  return this.globalRoles;
107
103
  },
@@ -183,12 +179,6 @@ export default {
183
179
  :weight="tabs[GLOBAL].weight"
184
180
  :label-key="tabs[GLOBAL].labelKey"
185
181
  >
186
- <Banner
187
- color="warning"
188
- class="mb-20"
189
- >
190
- <span v-clean-html="t('rbac.globalRoles.role.restricted-admin.deprecation', { releaseNotesUrl }, true)" />
191
- </Banner>
192
182
  <ResourceTable
193
183
  :schema="tabs[GLOBAL].schema"
194
184
  :rows="globalResources"
@@ -3,6 +3,7 @@ import Dashboard from '@shell/pages/c/_cluster/explorer/index.vue';
3
3
  import { shallowMount } from '@vue/test-utils';
4
4
  import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
5
5
  import { NODE_ARCHITECTURE } from '@shell/config/labels-annotations';
6
+ import { WORKLOAD_TYPES } from '@shell/config/types';
6
7
 
7
8
  describe('page: cluster dashboard', () => {
8
9
  const mountOptions = {
@@ -97,12 +98,81 @@ describe('page: cluster dashboard', () => {
97
98
  [STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 1],
98
99
  [STATES_ENUM.HEALTHY, 'icon-checkmark', true, false, false, [{ status: 'True' }], 1, 0],
99
100
  ]]
100
- ])('%p cluster - %p agent health box', (_, agentId, isLocal, agentResources, statuses) => {
101
+ ])('%p cluster - %p agent health box :', (_, agentId, isLocal, agentResources, statuses) => {
102
+ it.each(statuses)('should NOT show %p status due to missing canList permissions', (status, iconClass, isLoaded, disconnected, error, conditions, readyReplicas, unavailableReplicas) => {
103
+ const options = clone(mountOptions);
104
+
105
+ options.global.mocks.$store.getters.currentCluster.isLocal = isLocal;
106
+
107
+ const resources = agentResources.reduce((acc, r) => {
108
+ const agent = {
109
+ metadata: { state: { error } },
110
+ spec: { replicas: 1 },
111
+ status: {
112
+ readyReplicas,
113
+ unavailableReplicas,
114
+ conditions
115
+ }
116
+ };
117
+
118
+ return isLoaded ? {
119
+ ...acc,
120
+ [r]: agent
121
+ } : 'loading';
122
+ }, {});
123
+
124
+ const wrapper = shallowMount(Dashboard, {
125
+ ...options,
126
+ data: () => ({
127
+ ...resources,
128
+ disconnected,
129
+ canViewAgents: true
130
+ })
131
+ });
132
+
133
+ const box = wrapper.find(`[data-testid="k8s-service-${ agentId }"]`);
134
+
135
+ expect(box.exists()).toBe(false);
136
+ });
137
+ });
138
+
139
+ describe.each([
140
+ ['local', 'fleet', true, ['fleetDeployment', 'fleetStatefulSet'], [
141
+ [STATES_ENUM.IN_PROGRESS, 'icon-spinner', false, false, false, '', 0, 0],
142
+ [STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, false, [{ status: 'False' }], 0, 0],
143
+ [STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, true, [{ status: 'True' }], 0, 0],
144
+ [STATES_ENUM.WARNING, 'icon-warning', true, true, false, [{ status: 'True' }], 0, 0],
145
+ [STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 0],
146
+ [STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 1],
147
+ [STATES_ENUM.HEALTHY, 'icon-checkmark', true, false, false, [{ status: 'True' }], 1, 0],
148
+ ]],
149
+ ['downstream RKE2', 'fleet', false, ['fleetStatefulSet'], [
150
+ [STATES_ENUM.IN_PROGRESS, 'icon-spinner', false, false, false, '', 0, 0],
151
+ [STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, false, [{ status: 'False' }], 0, 0],
152
+ [STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, true, [{ status: 'True' }], 0, 0],
153
+ [STATES_ENUM.WARNING, 'icon-warning', true, true, false, [{ status: 'True' }], 0, 0],
154
+ [STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 0],
155
+ [STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 1],
156
+ [STATES_ENUM.HEALTHY, 'icon-checkmark', true, false, false, [{ status: 'True' }], 1, 0],
157
+ ]],
158
+ ['downstream RKE2', 'cattle', false, ['cattleDeployment'], [
159
+ [STATES_ENUM.IN_PROGRESS, 'icon-spinner', false, false, false, '', 0, 0],
160
+ [STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, false, [{ status: 'False' }], 0, 0],
161
+ [STATES_ENUM.UNHEALTHY, 'icon-warning', true, true, false, [{ status: 'True' }], 0, 0],
162
+ [STATES_ENUM.UNHEALTHY, 'icon-warning', true, false, true, [{ status: 'True' }], 0, 0],
163
+ [STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 0],
164
+ [STATES_ENUM.WARNING, 'icon-warning', true, false, false, [{ status: 'True' }], 0, 1],
165
+ [STATES_ENUM.HEALTHY, 'icon-checkmark', true, false, false, [{ status: 'True' }], 1, 0],
166
+ ]]
167
+ ])('%p cluster - %p agent health box ::', (_, agentId, isLocal, agentResources, statuses) => {
101
168
  it.each(statuses)('should show %p status', (status, iconClass, isLoaded, disconnected, error, conditions, readyReplicas, unavailableReplicas) => {
102
169
  const options = clone(mountOptions);
103
170
 
104
171
  options.global.mocks.$store.getters.currentCluster.isLocal = isLocal;
105
172
 
173
+ // let's pass the canList now
174
+ options.global.mocks.$store.getters['cluster/canList'] = (type: string) => !!(type === WORKLOAD_TYPES.DEPLOYMENT) || !!(type === WORKLOAD_TYPES.STATEFUL_SET);
175
+
106
176
  const resources = agentResources.reduce((acc, r) => {
107
177
  const agent = {
108
178
  metadata: { state: { error } },
@@ -188,7 +188,11 @@ export default {
188
188
  },
189
189
 
190
190
  fleetAgentNamespace() {
191
- return this.$store.getters['cluster/byId'](NAMESPACE, 'cattle-fleet-system');
191
+ if (this.currentCluster.isLocal) {
192
+ return this.$store.getters['cluster/canList'](WORKLOAD_TYPES.DEPLOYMENT) && this.$store.getters['cluster/canList'](WORKLOAD_TYPES.STATEFUL_SET) && this.$store.getters['cluster/byId'](NAMESPACE, 'cattle-fleet-system');
193
+ }
194
+
195
+ return this.$store.getters['cluster/canList'](WORKLOAD_TYPES.STATEFUL_SET) && this.$store.getters['cluster/byId'](NAMESPACE, 'cattle-fleet-system');
192
196
  },
193
197
 
194
198
  cattleAgentNamespace() {
@@ -196,7 +200,7 @@ export default {
196
200
  return;
197
201
  }
198
202
 
199
- return this.$store.getters['cluster/byId'](NAMESPACE, 'cattle-system');
203
+ return this.$store.getters['cluster/canList'](WORKLOAD_TYPES.DEPLOYMENT) && this.$store.getters['cluster/byId'](NAMESPACE, 'cattle-system');
200
204
  },
201
205
 
202
206
  canViewAgents() {
@@ -49,6 +49,7 @@ export default defineComponent({
49
49
  default: false
50
50
  }
51
51
  },
52
+ emits: ['close'],
52
53
  computed: {
53
54
  /**
54
55
  * Return message text as label.