@rancher/shell 3.0.0 → 3.0.1-rc.1

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 (83) hide show
  1. package/assets/brand/harvester/favicon.png +0 -0
  2. package/assets/brand/harvester/metadata.json +3 -0
  3. package/assets/images/pl/harvester.svg +1 -0
  4. package/assets/translations/en-us.yaml +25 -4
  5. package/assets/translations/zh-hans.yaml +1 -1
  6. package/components/BrandImage.vue +5 -1
  7. package/components/CopyToClipboardText.vue +2 -0
  8. package/components/CruResourceFooter.vue +1 -0
  9. package/components/DetailTop.vue +1 -1
  10. package/components/ExplorerMembers.vue +5 -1
  11. package/components/ExplorerProjectsNamespaces.vue +39 -15
  12. package/components/HardwareResourceGauge.vue +12 -2
  13. package/components/InputOrDisplay.vue +6 -2
  14. package/components/LandingPagePreference.vue +2 -2
  15. package/components/MessageLink.vue +1 -1
  16. package/components/ResourceDetail/Masthead.vue +22 -1
  17. package/components/ResourceDetail/index.vue +2 -8
  18. package/components/ResourceTable.vue +14 -6
  19. package/components/ResourceYaml.vue +1 -1
  20. package/components/SideNav.vue +1 -1
  21. package/components/TableDataUserIcon.vue +1 -1
  22. package/components/fleet/FleetRepos.vue +0 -7
  23. package/components/form/ArrayList.vue +5 -1
  24. package/components/form/ArrayListSelect.vue +5 -1
  25. package/components/form/KeyValue.vue +1 -1
  26. package/components/form/LabeledSelect.vue +26 -6
  27. package/components/form/Password.vue +7 -1
  28. package/components/form/UnitInput.vue +10 -1
  29. package/components/form/__tests__/UnitInput.test.ts +1 -0
  30. package/components/formatter/ClusterProvider.vue +3 -3
  31. package/components/formatter/ImagePercentageBar.vue +1 -1
  32. package/components/formatter/Si.vue +5 -1
  33. package/components/formatter/Translate.vue +1 -1
  34. package/components/nav/Header.vue +29 -5
  35. package/components/nav/NamespaceFilter.vue +5 -8
  36. package/components/nav/TopLevelMenu.vue +11 -11
  37. package/config/labels-annotations.js +2 -0
  38. package/config/table-headers.js +15 -0
  39. package/config/types.js +3 -0
  40. package/detail/fleet.cattle.io.bundle.vue +5 -68
  41. package/detail/fleet.cattle.io.gitrepo.vue +2 -1
  42. package/directives/clean-tooltip.js +4 -4
  43. package/edit/logging-flow/Match.vue +75 -42
  44. package/edit/logging-flow/index.vue +89 -10
  45. package/edit/logging.banzaicloud.io.output/index.vue +2 -2
  46. package/edit/management.cattle.io.project.vue +2 -2
  47. package/edit/namespace.vue +1 -1
  48. package/edit/provisioning.cattle.io.cluster/index.vue +2 -1
  49. package/edit/provisioning.cattle.io.cluster/rke2.vue +1 -3
  50. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +1 -1
  51. package/list/harvesterhci.io.management.cluster.vue +244 -0
  52. package/list/namespace.vue +16 -4
  53. package/models/__tests__/management.cattle.io.cluster.test.ts +45 -3
  54. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +0 -86
  55. package/models/fleet.cattle.io.bundle.js +3 -1
  56. package/models/fleet.cattle.io.gitrepo.js +43 -50
  57. package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +88 -0
  58. package/models/management.cattle.io.cluster.js +26 -5
  59. package/models/management.cattle.io.setting.js +25 -0
  60. package/models/provisioning.cattle.io.cluster.js +5 -14
  61. package/models/storage.k8s.io.storageclass.js +15 -4
  62. package/package.json +3 -3
  63. package/pages/auth/login.vue +3 -2
  64. package/pages/auth/setup.vue +1 -1
  65. package/pages/c/_cluster/fleet/index.vue +2 -4
  66. package/pages/c/_cluster/settings/brand.vue +4 -1
  67. package/pages/prefs.vue +22 -10
  68. package/plugins/dashboard-store/resource-class.js +11 -3
  69. package/plugins/steve/steve-pagination-utils.ts +1 -1
  70. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -2
  71. package/store/features.js +1 -0
  72. package/store/i18n.js +5 -1
  73. package/store/prefs.js +8 -0
  74. package/types/resources/fleet.d.ts +40 -0
  75. package/types/shell/index.d.ts +428 -395
  76. package/utils/auth.js +1 -1
  77. package/utils/create-yaml.js +1 -1
  78. package/utils/favicon.js +2 -0
  79. package/utils/fleet.ts +159 -0
  80. package/utils/gc/gc.ts +1 -1
  81. package/utils/v-sphere.ts +31 -0
  82. package/utils/validators/cron-schedule.js +1 -1
  83. package/utils/validators/formRules/index.ts +1 -1
@@ -1,7 +1,8 @@
1
1
  <script>
2
2
  import { mapGetters } from 'vuex';
3
+ import { NS_SNAPSHOT_QUOTA } from '../config/table-headers';
3
4
  import ResourceTable from '@shell/components/ResourceTable';
4
-
5
+ import { HCI } from '@shell/config/types';
5
6
  export default {
6
7
  name: 'ListNamespace',
7
8
  components: { ResourceTable },
@@ -27,13 +28,23 @@ export default {
27
28
  default: false
28
29
  }
29
30
  },
30
- data() {
31
- return { asddsa: true };
32
- },
33
31
 
34
32
  computed: {
35
33
  ...mapGetters(['currentProduct']),
34
+ hasHarvesterResourceQuotaSchema() {
35
+ const inStore = this.$store.getters['currentProduct'].inStore;
36
+
37
+ return !!this.$store.getters[`${ inStore }/schemaFor`](HCI.RESOURCE_QUOTA);
38
+ },
39
+ headers() {
40
+ const headersFromSchema = this.$store.getters['type-map/headersFor'](this.schema);
36
41
 
42
+ if (this.hasHarvesterResourceQuotaSchema) {
43
+ headersFromSchema.splice(2, 0, NS_SNAPSHOT_QUOTA);
44
+ }
45
+
46
+ return headersFromSchema;
47
+ },
37
48
  filterRow() {
38
49
  if (this.currentProduct.hideSystemResources) {
39
50
  return this.rows.filter( (N) => {
@@ -56,6 +67,7 @@ export default {
56
67
  v-bind="$attrs"
57
68
  :rows="filterRow"
58
69
  :groupable="false"
70
+ :headers="headers"
59
71
  :schema="schema"
60
72
  key-field="_key"
61
73
  :loading="loading"
@@ -4,12 +4,34 @@ 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
+
7
29
  describe('class MgmtCluster', () => {
8
30
  describe('provisioner', () => {
9
31
  const testCases = [
10
- [{ provider: 'rke', driver: 'imported' }, 'rke'],
11
- [{ provider: 'k3s', driver: 'K3S' }, 'k3s'],
12
- [{ provider: 'aks', driver: 'AKS' }, 'aks'],
32
+ [{ provider: 'rke', driver: 'imported' }, 'imported'],
33
+ [{ provider: 'k3s', driver: 'K3S' }, 'K3S'],
34
+ [{ provider: 'aks', driver: 'AKS' }, 'AKS'],
13
35
  [{}, 'imported'],
14
36
  ];
15
37
 
@@ -20,4 +42,24 @@ describe('class MgmtCluster', () => {
20
42
  }
21
43
  );
22
44
  });
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
+ });
23
65
  });
@@ -1,31 +1,6 @@
1
1
  import ProvCluster from '@shell/models/provisioning.cattle.io.cluster';
2
2
 
3
3
  describe('class ProvCluster', () => {
4
- const importedClusterInfo = {
5
- clusterName: 'test', provisioner: 'imported', mgmt: { spec: { gkeConfig: {} } }, spec: {}
6
- };
7
- const importedGkeClusterInfo = {
8
- clusterName: 'test', provisioner: 'rke2', mgmt: { spec: { gkeConfig: { imported: true } } }
9
- };
10
- const importedAksClusterInfo = {
11
- clusterName: 'test', provisioner: 'rke2', mgmt: { spec: { aksConfig: { imported: true } } }
12
- };
13
- const importedEksClusterInfo = {
14
- clusterName: 'test', provisioner: 'rke2', mgmt: { spec: { eksConfig: { imported: true } } }
15
- };
16
- const notImportedGkeClusterInfo = {
17
- clusterName: 'test', provisioner: 'rke2', mgmt: { spec: { gkeConfig: { imported: false } }, rkeConfig: {} }
18
- };
19
- const importedClusterInfoWithProviderForEmberParam = {
20
- clusterName: 'test', provisioner: 'rke2', mgmt: { providerForEmberParam: 'import' }
21
- };
22
- const localClusterInfo = {
23
- clusterName: 'test', provisioner: 'imported', mgmt: { isLocal: true, spec: { gkeConfig: {} } }, spec: {}
24
- };
25
- const doRke2Info = {
26
- clusterName: 'test', provisioner: 'rke2', mgmt: { isLocal: false, providerForEmberParam: 'import' }, spec: { rkeConfig: {} }
27
- };
28
-
29
4
  const gkeClusterWithPrivateEndpoint = {
30
5
  clusterName: 'test',
31
6
  provisioner: 'GKE',
@@ -76,67 +51,6 @@ describe('class ProvCluster', () => {
76
51
  });
77
52
  });
78
53
 
79
- describe('isImported', () => {
80
- const testCases = [
81
- [importedClusterInfo, true],
82
- [importedGkeClusterInfo, true],
83
- [importedAksClusterInfo, true],
84
- [importedEksClusterInfo, true],
85
- [notImportedGkeClusterInfo, false],
86
- [importedClusterInfoWithProviderForEmberParam, true],
87
- [localClusterInfo, false],
88
- [doRke2Info, false],
89
- [{}, false],
90
- ];
91
- const resetMocks = () => {
92
- // Clear all mock function calls:
93
- jest.clearAllMocks();
94
- };
95
-
96
- it.each(testCases)('should return the isImported value properly based on the props data', (clusterData: Object, expected: Boolean) => {
97
- const cluster = new ProvCluster({ spec: clusterData.spec });
98
-
99
- jest.spyOn(cluster, 'mgmt', 'get').mockReturnValue(
100
- clusterData.mgmt
101
- );
102
- jest.spyOn(cluster, 'provisioner', 'get').mockReturnValue(
103
- clusterData.provisioner
104
- );
105
-
106
- expect(cluster.isImported).toBe(expected);
107
- resetMocks();
108
- }
109
- );
110
- });
111
-
112
- describe('mgmt', () => {
113
- const testCases = [
114
- [importedClusterInfo, importedClusterInfo.mgmt],
115
- [importedGkeClusterInfo, importedGkeClusterInfo.mgmt],
116
- [importedAksClusterInfo, importedAksClusterInfo.mgmt],
117
- [importedEksClusterInfo, importedEksClusterInfo.mgmt],
118
- [notImportedGkeClusterInfo, notImportedGkeClusterInfo.mgmt],
119
- [importedClusterInfoWithProviderForEmberParam, importedClusterInfoWithProviderForEmberParam.mgmt],
120
- [localClusterInfo, localClusterInfo.mgmt],
121
- [doRke2Info, doRke2Info.mgmt],
122
- [{}, null],
123
- ];
124
-
125
- const resetMocks = () => {
126
- // Clear all mock function calls:
127
- jest.clearAllMocks();
128
- };
129
-
130
- it.each(testCases)('should return the isImported value properly based on the props data', (clusterData: Object, expected: Object) => {
131
- const clusterMock = jest.fn(() => clusterData.mgmt);
132
- const ctx = { rootGetters: { 'management/byId': clusterMock } };
133
- const cluster = new ProvCluster({ status: { clusterName: clusterData.clusterName } }, ctx);
134
-
135
- expect(cluster.mgmt).toBe(expected);
136
- resetMocks();
137
- });
138
- });
139
-
140
54
  describe('hasError', () => {
141
55
  const conditionsWithoutError = [
142
56
  {
@@ -30,7 +30,9 @@ export default class FleetBundle extends SteveModel {
30
30
  }
31
31
 
32
32
  get repoName() {
33
- return this.metadata.labels['fleet.cattle.io/repo-name'];
33
+ const labels = this.metadata?.labels || {};
34
+
35
+ return labels['fleet.cattle.io/repo-name'];
34
36
  }
35
37
 
36
38
  get targetClusters() {
@@ -1,15 +1,16 @@
1
1
  import { convert, matching, convertSelectorObj } from '@shell/utils/selector';
2
2
  import jsyaml from 'js-yaml';
3
- import { escapeHtml, randomStr } from '@shell/utils/string';
3
+ import { escapeHtml } from '@shell/utils/string';
4
4
  import { FLEET } from '@shell/config/types';
5
5
  import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
6
6
  import { addObject, addObjects, findBy, insertAt } from '@shell/utils/array';
7
7
  import { set } from '@shell/utils/object';
8
8
  import SteveModel from '@shell/plugins/steve/steve-class';
9
9
  import {
10
- STATES_ENUM, colorForState, mapStateToEnum, primaryDisplayStatusFromCount, stateDisplay, stateSort
10
+ colorForState, mapStateToEnum, primaryDisplayStatusFromCount, stateDisplay, stateSort
11
11
  } from '@shell/plugins/dashboard-store/resource-class';
12
12
  import { NAME } from '@shell/config/product/explorer';
13
+ import FleetUtils from '@shell/utils/fleet';
13
14
 
14
15
  function quacksLikeAHash(str) {
15
16
  if (str.match(/^[a-f0-9]{40,}$/i)) {
@@ -325,35 +326,24 @@ export default class GitRepo extends SteveModel {
325
326
  }
326
327
 
327
328
  get resourcesStatuses() {
328
- const clusters = this.targetClusters || [];
329
- const resources = this.status?.resources || [];
330
- const conditions = this.status?.conditions || [];
329
+ const bundleDeployments = this.bundleDeployments || [];
330
+ const clusters = (this.targetClusters || []).reduce((res, c) => {
331
+ res[c.id] = c;
331
332
 
332
- const out = [];
333
-
334
- for (const c of clusters) {
335
- const clusterBundleDeploymentResources = this.bundleDeployments
336
- .find((bd) => bd.metadata?.labels?.[FLEET_ANNOTATIONS.CLUSTER] === c.metadata.name)
337
- ?.status?.resources || [];
338
-
339
- resources.forEach((r, i) => {
340
- let namespacedName = r.name;
333
+ return res;
334
+ }, {});
341
335
 
342
- if (r.namespace) {
343
- namespacedName = `${ r.namespace }:${ r.name }`;
344
- }
336
+ const out = [];
345
337
 
346
- let state = r.state;
347
- const perEntry = r.perClusterState?.find((x) => x.clusterId === c.id);
348
- const tooMany = r.perClusterState?.length >= 10 || false;
338
+ for (const bd of bundleDeployments) {
339
+ const clusterId = FleetUtils.clusterIdFromBundleDeploymentLabels(bd.metadata?.labels);
340
+ const c = clusters[clusterId];
341
+ const resources = FleetUtils.resourcesFromBundleDeploymentStatus(bd.status);
349
342
 
350
- if (perEntry) {
351
- state = perEntry.state;
352
- } else if (tooMany) {
353
- state = STATES_ENUM.UNKNOWN;
354
- } else {
355
- state = STATES_ENUM.READY;
356
- }
343
+ resources.forEach((r) => {
344
+ const id = FleetUtils.resourceId(r);
345
+ const type = FleetUtils.resourceType(r);
346
+ const state = r.state;
357
347
 
358
348
  const color = colorForState(state).replace('text-', 'bg-');
359
349
  const display = stateDisplay(state);
@@ -363,33 +353,38 @@ export default class GitRepo extends SteveModel {
363
353
  params: {
364
354
  product: NAME,
365
355
  cluster: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME],
366
- resource: r.type,
356
+ resource: type,
367
357
  namespace: r.namespace,
368
358
  id: r.name,
369
359
  }
370
360
  };
371
361
 
362
+ const key = `${ c.id }-${ type }-${ r.namespace }-${ r.name }`;
363
+
372
364
  out.push({
373
- key: `${ r.id }-${ c.id }-${ r.type }-${ r.namespace }-${ r.name }`,
374
- tableKey: `${ r.id }-${ c.id }-${ r.type }-${ r.namespace }-${ r.name }-${ randomStr(8) }`,
375
- kind: r.kind,
376
- apiVersion: r.apiVersion,
377
- type: r.type,
378
- id: r.id,
379
- namespace: r.namespace,
380
- name: r.name,
381
- clusterId: c.id,
382
- clusterLabel: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME],
383
- clusterName: c.nameDisplay,
384
- state: mapStateToEnum(state),
385
- stateBackground: color,
386
- stateDisplay: display,
387
- stateSort: stateSort(color, display),
388
- namespacedName,
365
+ key,
366
+ tableKey: key,
367
+
368
+ // Needed?
369
+ id,
370
+ type,
371
+ clusterId: c.id,
372
+
373
+ // columns, see FleetResources.vue
374
+ state: mapStateToEnum(state),
375
+ clusterName: c.nameDisplay,
376
+ apiVersion: r.apiVersion,
377
+ kind: r.kind,
378
+ name: r.name,
379
+ namespace: r.namespace,
380
+ creationTimestamp: r.createdAt,
381
+
382
+ // other properties
383
+ clusterLabel: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME],
384
+ stateBackground: color,
385
+ stateDisplay: display,
386
+ stateSort: stateSort(color, display),
389
387
  detailLocation,
390
- conditions: conditions[i],
391
- bundleDeploymentStatus: clusterBundleDeploymentResources?.[i],
392
- creationTimestamp: clusterBundleDeploymentResources?.[i]?.createdAt
393
388
  });
394
389
  });
395
390
  }
@@ -410,9 +405,7 @@ export default class GitRepo extends SteveModel {
410
405
 
411
406
  get clusterResourceStatus() {
412
407
  const clusterStatuses = this.resourcesStatuses.reduce((prev, curr) => {
413
- const { clusterId, clusterLabel } = curr;
414
-
415
- const state = curr.state;
408
+ const { clusterId, clusterLabel, state } = curr;
416
409
 
417
410
  if (!prev[clusterId]) {
418
411
  prev[clusterId] = {
@@ -0,0 +1,88 @@
1
+ import SteveModel from '@shell/plugins/steve/steve-class';
2
+ import { HCI } from '@shell/config/labels-annotations';
3
+
4
+ export default class NetworkAttachmentDef extends SteveModel {
5
+ applyDefaults() {
6
+ const spec = this.spec || {
7
+ config: JSON.stringify({
8
+ cniVersion: '0.3.1',
9
+ name: '',
10
+ type: 'bridge',
11
+ bridge: '',
12
+ promiscMode: true,
13
+ vlan: '',
14
+ ipam: {}
15
+ })
16
+ };
17
+
18
+ this['spec'] = spec;
19
+ }
20
+
21
+ get parseConfig() {
22
+ try {
23
+ return JSON.parse(this.spec.config) || {};
24
+ } catch (err) {
25
+ return {};
26
+ }
27
+ }
28
+
29
+ get isIpamStatic() {
30
+ return this.parseConfig.ipam?.type === 'static';
31
+ }
32
+
33
+ get clusterNetwork() {
34
+ return this?.metadata?.labels?.[HCI.CLUSTER_NETWORK];
35
+ }
36
+
37
+ get vlanType() {
38
+ const labels = this.metadata?.labels || {};
39
+ const type = labels[HCI.NETWORK_TYPE];
40
+
41
+ return type;
42
+ }
43
+
44
+ get vlanId() {
45
+ return this.vlanType === 'UntaggedNetwork' ? 'N/A' : this.parseConfig.vlan;
46
+ }
47
+
48
+ get customValidationRules() {
49
+ const rules = [
50
+ {
51
+ nullable: false,
52
+ path: 'metadata.name',
53
+ required: true,
54
+ minLength: 1,
55
+ maxLength: 63,
56
+ translationKey: 'harvester.fields.name'
57
+ }
58
+ ];
59
+
60
+ return rules;
61
+ }
62
+
63
+ get connectivity() {
64
+ const annotations = this.metadata?.annotations || {};
65
+ const route = annotations[HCI.NETWORK_ROUTE];
66
+ let config = {};
67
+
68
+ if (this.vlanType === 'UntaggedNetwork') {
69
+ return 'N/A';
70
+ }
71
+
72
+ try {
73
+ config = JSON.parse(route || '{}');
74
+ } catch {
75
+ return 'invalid';
76
+ }
77
+
78
+ const connectivity = config.connectivity;
79
+
80
+ if (connectivity === 'false') {
81
+ return 'inactive';
82
+ } else if (connectivity === 'true') {
83
+ return 'active';
84
+ } else {
85
+ return connectivity;
86
+ }
87
+ }
88
+ }
@@ -89,11 +89,31 @@ export default class MgmtCluster extends SteveModel {
89
89
  return pools.filter((x) => x.spec?.clusterName === this.id);
90
90
  }
91
91
 
92
- get provisioner() {
93
- if (this.status?.provider ) {
94
- return this.status.provider;
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;
95
111
  }
96
112
 
113
+ return this.provisioner === 'imported';
114
+ }
115
+
116
+ get provisioner() {
97
117
  // For imported K3s clusters, this.status.driver is 'k3s.'
98
118
  return this.status?.driver ? this.status.driver : 'imported';
99
119
  }
@@ -117,10 +137,11 @@ export default class MgmtCluster extends SteveModel {
117
137
  get providerForEmberParam() {
118
138
  // Ember wants one word called provider to tell what component to show, but has much indirect mapping to figure out what it is.
119
139
  let provider;
120
- // Provisioner is the "<something>Config" in the model
140
+
141
+ // provisioner is status.driver
121
142
  const provisioner = KONTAINER_TO_DRIVER[(this.provisioner || '').toLowerCase()] || this.provisioner;
122
143
 
123
- if ( provisioner === 'rancherKubernetesEngine' || provisioner === 'rke') {
144
+ if ( provisioner === 'rancherKubernetesEngine') {
124
145
  // Look for a cloud provider in one of the node templates
125
146
  if ( this.machinePools?.[0] ) {
126
147
  provider = this.machinePools[0]?.nodeTemplate?.spec?.driver || null;
@@ -1,6 +1,13 @@
1
1
  import { ALLOWED_SETTINGS } from '@shell/config/settings';
2
2
  import HybridModel from '@shell/plugins/steve/hybrid-class';
3
3
  import { isServerUrl } from '@shell/utils/validators/setting';
4
+ import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
5
+ import {
6
+ _EDIT,
7
+ _UNFLAG,
8
+ AS,
9
+ MODE
10
+ } from '@shell/config/query-params';
4
11
 
5
12
  export default class Setting extends HybridModel {
6
13
  get fromEnv() {
@@ -43,4 +50,22 @@ export default class Setting extends HybridModel {
43
50
 
44
51
  return out;
45
52
  }
53
+
54
+ goToEdit(moreQuery = {}) {
55
+ if (this.$rootGetters['currentProduct'].inStore === HARVESTER) {
56
+ location.name = `${ HARVESTER }-c-cluster-brand`;
57
+ location.params = { cluster: this.$rootGetters['currentCluster'].id, product: HARVESTER };
58
+
59
+ location.query = {
60
+ ...location.query,
61
+ [MODE]: _EDIT,
62
+ [AS]: _UNFLAG,
63
+ ...moreQuery
64
+ };
65
+
66
+ this.currentRouter().push(location);
67
+ } else {
68
+ super.goToEdit();
69
+ }
70
+ }
46
71
  }
@@ -282,19 +282,7 @@ export default class ProvCluster extends SteveModel {
282
282
  }
283
283
 
284
284
  get isImported() {
285
- // As of Rancher v2.6.7, this returns false for imported K3s clusters,
286
- // in which this.provisioner is `k3s`.
287
-
288
- const isImportedProvisioner = this.provisioner === 'imported';
289
- const isImportedSpecialCases = this.mgmt?.providerForEmberParam === 'import' ||
290
- // when imported cluster is GKE
291
- !!this.mgmt?.spec?.gkeConfig?.imported ||
292
- // or AKS
293
- !!this.mgmt?.spec?.aksConfig?.imported ||
294
- // or EKS
295
- !!this.mgmt?.spec?.eksConfig?.imported;
296
-
297
- return !this.isLocal && (isImportedProvisioner || (!this.isRke2 && !this.mgmt?.machineProvider && isImportedSpecialCases));
285
+ return this.mgmt?.isImported;
298
286
  }
299
287
 
300
288
  get isCustom() {
@@ -330,7 +318,8 @@ export default class ProvCluster extends SteveModel {
330
318
  }
331
319
 
332
320
  get isRke1() {
333
- return !!this.mgmt?.spec?.rancherKubernetesEngineConfig || this.labels['provider.cattle.io'] === 'rke';
321
+ // rancherKubernetesEngineConfig is not defined on imported RKE1 clusters
322
+ return !!this.mgmt?.spec?.rancherKubernetesEngineConfig || this.mgmt?.labels['provider.cattle.io'] === 'rke';
334
323
  }
335
324
 
336
325
  get isHarvester() {
@@ -407,6 +396,8 @@ export default class ProvCluster extends SteveModel {
407
396
  provisioner = 'k3s';
408
397
  } else if ( this.isImportedRke2 ) {
409
398
  provisioner = 'rke2';
399
+ } else if ((this.isImported || this.isLocal) && this.isRke1) {
400
+ provisioner = 'rke';
410
401
  }
411
402
 
412
403
  return this.$rootGetters['i18n/withFallback'](`cluster.provider."${ provisioner }"`, null, ucFirst(provisioner));
@@ -112,16 +112,21 @@ export default class extends SteveModel {
112
112
  return this.patch(data, {}, true, true);
113
113
  }
114
114
 
115
- setDefault() {
116
- const allStorageClasses = this.$rootGetters['cluster/all'](STORAGE_CLASS) || [];
115
+ async setDefault() {
116
+ const inStore = this.$rootGetters['currentProduct'].inStore;
117
+ const allStorageClasses = this.$rootGetters[`${ inStore }/all`](STORAGE_CLASS) || [];
118
+
119
+ for (const storageClass of allStorageClasses) {
120
+ await storageClass.resetDefault();
121
+ }
117
122
 
118
123
  allStorageClasses.forEach((storageClass) => storageClass.resetDefault());
119
124
  this.updateDefault(true);
120
125
  }
121
126
 
122
- resetDefault() {
127
+ async resetDefault() {
123
128
  if (this.isDefault) {
124
- this.updateDefault(false);
129
+ await this.updateDefault(false);
125
130
  }
126
131
  }
127
132
 
@@ -146,4 +151,10 @@ export default class extends SteveModel {
146
151
 
147
152
  return out;
148
153
  }
154
+
155
+ cleanForNew() {
156
+ this.$dispatch(`cleanForNew`, this);
157
+
158
+ delete this?.metadata?.annotations?.[STORAGE.DEFAULT_STORAGE_CLASS];
159
+ }
149
160
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.0",
3
+ "version": "3.0.1-rc.1",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -62,8 +62,8 @@
62
62
  "cookie": "0.5.0",
63
63
  "cookie-universal": "2.2.2",
64
64
  "core-js": "3.25.3",
65
- "cron-validator": "1.2.0",
66
- "cronstrue": "1.95.0",
65
+ "cron-validator": "1.3.1",
66
+ "cronstrue": "2.50.0",
67
67
  "cross-env": "6.0.3",
68
68
  "css-loader": "6.7.3",
69
69
  "csv-loader": "3.0.3",
@@ -64,7 +64,8 @@ export default {
64
64
  },
65
65
 
66
66
  computed: {
67
- ...mapGetters({ t: 'i18n/t' }),
67
+ ...mapGetters(['isStandaloneHarvester']),
68
+ ...mapGetters({ t: 'i18n/t', hasMultipleLocales: 'i18n/hasMultipleLocales' }),
68
69
 
69
70
  loggedOutSuccessMsg() {
70
71
  if (this.isSlo) {
@@ -497,7 +498,7 @@ export default {
497
498
  </div>
498
499
  </template>
499
500
  <div
500
- v-if="showLocaleSelector"
501
+ v-if="showLocaleSelector && hasMultipleLocales && !isStandaloneHarvester"
501
502
  class="locale-selector"
502
503
  >
503
504
  <LocaleSelector
@@ -141,7 +141,7 @@ export default {
141
141
  type: MANAGEMENT.FEATURE, id: 'multi-cluster-management', opt: { url: `/v1/${ MANAGEMENT.FEATURE }/multi-cluster-management` }
142
142
  });
143
143
 
144
- const mcmEnabled = mcmFeature?.spec?.value || mcmFeature?.status?.default;
144
+ const mcmEnabled = (mcmFeature?.spec?.value || mcmFeature?.status?.default) && productName !== 'Harvester';
145
145
 
146
146
  let serverUrl;
147
147