@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.
- package/assets/brand/harvester/favicon.png +0 -0
- package/assets/brand/harvester/metadata.json +3 -0
- package/assets/images/pl/harvester.svg +1 -0
- package/assets/translations/en-us.yaml +25 -4
- package/assets/translations/zh-hans.yaml +1 -1
- package/components/BrandImage.vue +5 -1
- package/components/CopyToClipboardText.vue +2 -0
- package/components/CruResourceFooter.vue +1 -0
- package/components/DetailTop.vue +1 -1
- package/components/ExplorerMembers.vue +5 -1
- package/components/ExplorerProjectsNamespaces.vue +39 -15
- package/components/HardwareResourceGauge.vue +12 -2
- package/components/InputOrDisplay.vue +6 -2
- package/components/LandingPagePreference.vue +2 -2
- package/components/MessageLink.vue +1 -1
- package/components/ResourceDetail/Masthead.vue +22 -1
- package/components/ResourceDetail/index.vue +2 -8
- package/components/ResourceTable.vue +14 -6
- package/components/ResourceYaml.vue +1 -1
- package/components/SideNav.vue +1 -1
- package/components/TableDataUserIcon.vue +1 -1
- package/components/fleet/FleetRepos.vue +0 -7
- package/components/form/ArrayList.vue +5 -1
- package/components/form/ArrayListSelect.vue +5 -1
- package/components/form/KeyValue.vue +1 -1
- package/components/form/LabeledSelect.vue +26 -6
- package/components/form/Password.vue +7 -1
- package/components/form/UnitInput.vue +10 -1
- package/components/form/__tests__/UnitInput.test.ts +1 -0
- package/components/formatter/ClusterProvider.vue +3 -3
- package/components/formatter/ImagePercentageBar.vue +1 -1
- package/components/formatter/Si.vue +5 -1
- package/components/formatter/Translate.vue +1 -1
- package/components/nav/Header.vue +29 -5
- package/components/nav/NamespaceFilter.vue +5 -8
- package/components/nav/TopLevelMenu.vue +11 -11
- package/config/labels-annotations.js +2 -0
- package/config/table-headers.js +15 -0
- package/config/types.js +3 -0
- package/detail/fleet.cattle.io.bundle.vue +5 -68
- package/detail/fleet.cattle.io.gitrepo.vue +2 -1
- package/directives/clean-tooltip.js +4 -4
- package/edit/logging-flow/Match.vue +75 -42
- package/edit/logging-flow/index.vue +89 -10
- package/edit/logging.banzaicloud.io.output/index.vue +2 -2
- package/edit/management.cattle.io.project.vue +2 -2
- package/edit/namespace.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/index.vue +2 -1
- package/edit/provisioning.cattle.io.cluster/rke2.vue +1 -3
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +1 -1
- package/list/harvesterhci.io.management.cluster.vue +244 -0
- package/list/namespace.vue +16 -4
- package/models/__tests__/management.cattle.io.cluster.test.ts +45 -3
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +0 -86
- package/models/fleet.cattle.io.bundle.js +3 -1
- package/models/fleet.cattle.io.gitrepo.js +43 -50
- package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +88 -0
- package/models/management.cattle.io.cluster.js +26 -5
- package/models/management.cattle.io.setting.js +25 -0
- package/models/provisioning.cattle.io.cluster.js +5 -14
- package/models/storage.k8s.io.storageclass.js +15 -4
- package/package.json +3 -3
- package/pages/auth/login.vue +3 -2
- package/pages/auth/setup.vue +1 -1
- package/pages/c/_cluster/fleet/index.vue +2 -4
- package/pages/c/_cluster/settings/brand.vue +4 -1
- package/pages/prefs.vue +22 -10
- package/plugins/dashboard-store/resource-class.js +11 -3
- package/plugins/steve/steve-pagination-utils.ts +1 -1
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -2
- package/store/features.js +1 -0
- package/store/i18n.js +5 -1
- package/store/prefs.js +8 -0
- package/types/resources/fleet.d.ts +40 -0
- package/types/shell/index.d.ts +428 -395
- package/utils/auth.js +1 -1
- package/utils/create-yaml.js +1 -1
- package/utils/favicon.js +2 -0
- package/utils/fleet.ts +159 -0
- package/utils/gc/gc.ts +1 -1
- package/utils/v-sphere.ts +31 -0
- package/utils/validators/cron-schedule.js +1 -1
- package/utils/validators/formRules/index.ts +1 -1
package/list/namespace.vue
CHANGED
|
@@ -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' }, '
|
|
11
|
-
[{ provider: 'k3s', driver: 'K3S' }, '
|
|
12
|
-
[{ provider: 'aks', driver: '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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
329
|
-
const
|
|
330
|
-
|
|
329
|
+
const bundleDeployments = this.bundleDeployments || [];
|
|
330
|
+
const clusters = (this.targetClusters || []).reduce((res, c) => {
|
|
331
|
+
res[c.id] = c;
|
|
331
332
|
|
|
332
|
-
|
|
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
|
-
|
|
343
|
-
namespacedName = `${ r.namespace }:${ r.name }`;
|
|
344
|
-
}
|
|
336
|
+
const out = [];
|
|
345
337
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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:
|
|
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
|
|
374
|
-
tableKey:
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
clusterName:
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
|
93
|
-
if (this.
|
|
94
|
-
return
|
|
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
|
-
|
|
140
|
+
|
|
141
|
+
// provisioner is status.driver
|
|
121
142
|
const provisioner = KONTAINER_TO_DRIVER[(this.provisioner || '').toLowerCase()] || this.provisioner;
|
|
122
143
|
|
|
123
|
-
if ( provisioner === 'rancherKubernetesEngine'
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
66
|
-
"cronstrue": "
|
|
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",
|
package/pages/auth/login.vue
CHANGED
|
@@ -64,7 +64,8 @@ export default {
|
|
|
64
64
|
},
|
|
65
65
|
|
|
66
66
|
computed: {
|
|
67
|
-
...mapGetters(
|
|
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
|
package/pages/auth/setup.vue
CHANGED
|
@@ -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
|
|