@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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import KeyValue from '@shell/components/form/KeyValue';
|
|
3
3
|
import Select from '@shell/components/form/Select';
|
|
4
4
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
5
|
+
import { HARVESTER_NAME as VIRTUAL } from '@shell/config/features';
|
|
5
6
|
|
|
6
7
|
export default {
|
|
7
8
|
emits: ['remove'],
|
|
@@ -39,6 +40,12 @@ export default {
|
|
|
39
40
|
}
|
|
40
41
|
},
|
|
41
42
|
|
|
43
|
+
computed: {
|
|
44
|
+
isHarvester() {
|
|
45
|
+
return this.$store.getters['currentProduct'].inStore === VIRTUAL;
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
|
|
42
49
|
methods: {
|
|
43
50
|
update() {},
|
|
44
51
|
|
|
@@ -51,19 +58,22 @@ export default {
|
|
|
51
58
|
|
|
52
59
|
<template>
|
|
53
60
|
<div>
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
<template v-if="!isHarvester">
|
|
62
|
+
<KeyValue
|
|
63
|
+
v-model:value="value.labels"
|
|
64
|
+
:title="value.select ? t('logging.flow.matches.pods.title.include') : t('logging.flow.matches.pods.title.exclude')"
|
|
65
|
+
:mode="mode"
|
|
66
|
+
:initial-empty-row="true"
|
|
67
|
+
:read-allowed="false"
|
|
68
|
+
:title-add="true"
|
|
69
|
+
protip=""
|
|
70
|
+
:key-label="t('logging.flow.matches.pods.keyLabel')"
|
|
71
|
+
:value-label="t('logging.flow.matches.pods.valueLabel')"
|
|
72
|
+
:add-label="t('logging.flow.matches.pods.addLabel')"
|
|
73
|
+
/>
|
|
74
|
+
<div class="spacer" />
|
|
75
|
+
</template>
|
|
76
|
+
|
|
67
77
|
<h3>
|
|
68
78
|
{{ value.select ? t('logging.flow.matches.nodes.title.include') : t('logging.flow.matches.nodes.title.exclude') }}
|
|
69
79
|
</h3>
|
|
@@ -83,48 +93,71 @@ export default {
|
|
|
83
93
|
/>
|
|
84
94
|
</div>
|
|
85
95
|
</div>
|
|
86
|
-
<div
|
|
87
|
-
<h3>
|
|
88
|
-
{{ value.select ? t('logging.flow.matches.containerNames.title.include') : t('logging.flow.matches.containerNames.title.exclude') }}
|
|
89
|
-
</h3>
|
|
90
|
-
<div class="row">
|
|
91
|
-
<div class="col span-12">
|
|
92
|
-
<LabeledSelect
|
|
93
|
-
v-model:value="value.container_names"
|
|
94
|
-
:mode="mode"
|
|
95
|
-
:options="[]"
|
|
96
|
-
:disabled="false"
|
|
97
|
-
:placeholder="t('logging.flow.matches.containerNames.placeholder')"
|
|
98
|
-
:multiple="true"
|
|
99
|
-
:taggable="true"
|
|
100
|
-
:clearable="true"
|
|
101
|
-
:searchable="true"
|
|
102
|
-
:close-on-select="false"
|
|
103
|
-
no-options-label-key="logging.flow.matches.containerNames.enter"
|
|
104
|
-
placement="top"
|
|
105
|
-
/>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
<div v-if="isClusterFlow">
|
|
96
|
+
<div v-if="!isHarvester">
|
|
109
97
|
<div class="spacer" />
|
|
110
98
|
<h3>
|
|
111
|
-
{{ value.select ? t('logging.flow.matches.
|
|
99
|
+
{{ value.select ? t('logging.flow.matches.containerNames.title.include') : t('logging.flow.matches.containerNames.title.exclude') }}
|
|
112
100
|
</h3>
|
|
113
101
|
<div class="row">
|
|
114
102
|
<div class="col span-12">
|
|
115
|
-
<
|
|
116
|
-
v-model:value="value.
|
|
117
|
-
|
|
118
|
-
:options="
|
|
119
|
-
:
|
|
103
|
+
<LabeledSelect
|
|
104
|
+
v-model:value="value.container_names"
|
|
105
|
+
:mode="mode"
|
|
106
|
+
:options="[]"
|
|
107
|
+
:disabled="false"
|
|
108
|
+
:placeholder="t('logging.flow.matches.containerNames.placeholder')"
|
|
120
109
|
:multiple="true"
|
|
121
110
|
:taggable="true"
|
|
122
111
|
:clearable="true"
|
|
112
|
+
:searchable="true"
|
|
123
113
|
:close-on-select="false"
|
|
114
|
+
no-options-label-key="logging.flow.matches.containerNames.enter"
|
|
124
115
|
placement="top"
|
|
125
116
|
/>
|
|
126
117
|
</div>
|
|
127
118
|
</div>
|
|
119
|
+
<div v-if="isClusterFlow">
|
|
120
|
+
<div class="spacer" />
|
|
121
|
+
<h3>
|
|
122
|
+
{{ value.select ? t('logging.flow.matches.containerNames.title.include') : t('logging.flow.matches.containerNames.title.exclude') }}
|
|
123
|
+
</h3>
|
|
124
|
+
<div class="row">
|
|
125
|
+
<div class="col span-12">
|
|
126
|
+
<Select
|
|
127
|
+
v-model:value="value.namespaces"
|
|
128
|
+
class="lg"
|
|
129
|
+
:options="namespaces"
|
|
130
|
+
:placeholder="t('logging.flow.matches.namespaces.placeholder')"
|
|
131
|
+
:multiple="true"
|
|
132
|
+
:taggable="true"
|
|
133
|
+
:clearable="true"
|
|
134
|
+
:searchable="true"
|
|
135
|
+
:close-on-select="false"
|
|
136
|
+
no-options-label-key="logging.flow.matches.containerNames.enter"
|
|
137
|
+
placement="top"
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
<div class="spacer" />
|
|
142
|
+
<h3>
|
|
143
|
+
{{ value.select ? t('logging.flow.matches.namespaces.title.include') : t('logging.flow.matches.namespaces.title.exclude') }}
|
|
144
|
+
</h3>
|
|
145
|
+
<div class="row">
|
|
146
|
+
<div class="col span-12">
|
|
147
|
+
<Select
|
|
148
|
+
v-model="value.namespaces"
|
|
149
|
+
class="lg"
|
|
150
|
+
:options="namespaces"
|
|
151
|
+
:placeholder="t('logging.flow.matches.namespaces.placeholder')"
|
|
152
|
+
:multiple="true"
|
|
153
|
+
:taggable="true"
|
|
154
|
+
:clearable="true"
|
|
155
|
+
:close-on-select="false"
|
|
156
|
+
placement="top"
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
128
161
|
</div>
|
|
129
162
|
</div>
|
|
130
163
|
</template>
|
|
@@ -6,20 +6,28 @@ import Loading from '@shell/components/Loading';
|
|
|
6
6
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
7
7
|
import Tabbed from '@shell/components/Tabbed';
|
|
8
8
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
LOGGING, NAMESPACE, NODE, POD, SCHEMA
|
|
11
|
+
} from '@shell/config/types';
|
|
10
12
|
import jsyaml from 'js-yaml';
|
|
11
13
|
import { createYaml } from '@shell/utils/create-yaml';
|
|
12
14
|
import YamlEditor, { EDITOR_MODES } from '@shell/components/YamlEditor';
|
|
13
15
|
import { allHash } from '@shell/utils/promise';
|
|
14
|
-
import { isArray } from '@shell/utils/array';
|
|
16
|
+
import { isArray, uniq } from '@shell/utils/array';
|
|
15
17
|
import { matchRuleIsPopulated } from '@shell/models/logging.banzaicloud.io.flow';
|
|
16
18
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
17
19
|
import { clone } from '@shell/utils/object';
|
|
18
20
|
import isEmpty from 'lodash/isEmpty';
|
|
19
21
|
import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
|
|
20
22
|
import { exceptionToErrorsArray } from '@shell/utils/error';
|
|
23
|
+
import { HARVESTER_NAME as VIRTUAL } from '@shell/config/features';
|
|
21
24
|
import Match from './Match';
|
|
22
25
|
|
|
26
|
+
const FLOW_LOGGING = 'Logging';
|
|
27
|
+
const FLOW_AUDIT = 'Audit';
|
|
28
|
+
const FLOW_EVENT = 'Event';
|
|
29
|
+
const FLOW_TYPE = [FLOW_LOGGING, FLOW_AUDIT, FLOW_EVENT];
|
|
30
|
+
|
|
23
31
|
function emptyMatch(include = true) {
|
|
24
32
|
const rule = {
|
|
25
33
|
select: !!include,
|
|
@@ -53,14 +61,17 @@ export default {
|
|
|
53
61
|
inheritAttrs: false,
|
|
54
62
|
|
|
55
63
|
async fetch() {
|
|
56
|
-
const
|
|
57
|
-
const
|
|
64
|
+
const currentCluster = this.$store.getters['currentCluster'];
|
|
65
|
+
const inStore = currentCluster.isHarvester ? VIRTUAL : 'cluster';
|
|
66
|
+
const hasAccessToClusterOutputs = this.$store.getters[`${ inStore }/schemaFor`](LOGGING.CLUSTER_OUTPUT);
|
|
67
|
+
const hasAccessToOutputs = this.$store.getters[`${ inStore }/schemaFor`](LOGGING.OUTPUT);
|
|
58
68
|
const hasAccessToNamespaces = this.$store.getters[`cluster/schemaFor`](NAMESPACE);
|
|
59
|
-
const hasAccessToNodes = this.$store.getters[
|
|
69
|
+
const hasAccessToNodes = this.$store.getters[`${ inStore }/schemaFor`](NODE);
|
|
70
|
+
const hasAccessToPods = this.$store.getters[`${ inStore }/schemaFor`](POD);
|
|
60
71
|
const isFlow = this.value.type === LOGGING.FLOW;
|
|
61
72
|
|
|
62
73
|
const getAllOrDefault = (type, hasAccess) => {
|
|
63
|
-
return hasAccess ? this.$store.dispatch(
|
|
74
|
+
return hasAccess ? this.$store.dispatch(`${ inStore }/findAll`, { type }) : Promise.resolve([]);
|
|
64
75
|
};
|
|
65
76
|
|
|
66
77
|
const hash = await allHash({
|
|
@@ -68,6 +79,7 @@ export default {
|
|
|
68
79
|
allClusterOutputs: getAllOrDefault(LOGGING.CLUSTER_OUTPUT, hasAccessToClusterOutputs),
|
|
69
80
|
allNamespaces: getAllOrDefault(NAMESPACE, hasAccessToNamespaces),
|
|
70
81
|
allNodes: getAllOrDefault(NODE, hasAccessToNodes),
|
|
82
|
+
allPods: getAllOrDefault(POD, hasAccessToPods),
|
|
71
83
|
});
|
|
72
84
|
|
|
73
85
|
for ( const k of Object.keys(hash) ) {
|
|
@@ -76,7 +88,9 @@ export default {
|
|
|
76
88
|
},
|
|
77
89
|
|
|
78
90
|
data() {
|
|
79
|
-
const
|
|
91
|
+
const currentCluster = this.$store.getters['currentCluster'];
|
|
92
|
+
const inStore = currentCluster.isHarvester ? VIRTUAL : 'cluster';
|
|
93
|
+
const schemas = this.$store.getters[`${ inStore }/all`](SCHEMA);
|
|
80
94
|
let filtersYaml;
|
|
81
95
|
|
|
82
96
|
this.value.spec = this.value.spec || {};
|
|
@@ -124,7 +138,8 @@ export default {
|
|
|
124
138
|
filtersYaml,
|
|
125
139
|
initialFiltersYaml: filtersYaml,
|
|
126
140
|
globalOutputRefs,
|
|
127
|
-
localOutputRefs
|
|
141
|
+
localOutputRefs,
|
|
142
|
+
loggingType: clone(this.value.loggingType || FLOW_LOGGING)
|
|
128
143
|
};
|
|
129
144
|
},
|
|
130
145
|
|
|
@@ -150,7 +165,17 @@ export default {
|
|
|
150
165
|
return true;
|
|
151
166
|
}
|
|
152
167
|
|
|
153
|
-
|
|
168
|
+
const isEqualNs = output.namespace === this.value.namespace;
|
|
169
|
+
|
|
170
|
+
if (!this.isHarvester) {
|
|
171
|
+
return isEqualNs;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (this.loggingType === FLOW_AUDIT) {
|
|
175
|
+
return output.loggingType === FLOW_AUDIT && isEqualNs;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return output.loggingType !== FLOW_AUDIT && isEqualNs;
|
|
154
179
|
}).map((x) => {
|
|
155
180
|
return { label: x.metadata.name, value: x.metadata.name };
|
|
156
181
|
});
|
|
@@ -165,7 +190,17 @@ export default {
|
|
|
165
190
|
|
|
166
191
|
return this.allClusterOutputs
|
|
167
192
|
.filter((clusterOutput) => {
|
|
168
|
-
|
|
193
|
+
const isEqualNs = clusterOutput.namespace === 'cattle-logging-system';
|
|
194
|
+
|
|
195
|
+
if (!this.isHarvester) {
|
|
196
|
+
return isEqualNs;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (this.loggingType === FLOW_AUDIT) {
|
|
200
|
+
return clusterOutput.loggingType === FLOW_AUDIT && isEqualNs;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return clusterOutput.loggingType !== FLOW_AUDIT && isEqualNs;
|
|
169
204
|
})
|
|
170
205
|
.map((clusterOutput) => {
|
|
171
206
|
return { label: clusterOutput.metadata.name, value: clusterOutput.metadata.name };
|
|
@@ -204,6 +239,25 @@ export default {
|
|
|
204
239
|
return out;
|
|
205
240
|
},
|
|
206
241
|
|
|
242
|
+
containerChoices() {
|
|
243
|
+
const out = [];
|
|
244
|
+
|
|
245
|
+
for ( const pod of this.allPods ) {
|
|
246
|
+
for ( const c of (pod.spec?.containers || []) ) {
|
|
247
|
+
out.push(c.name);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return uniq(out).sort();
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
isHarvester() {
|
|
255
|
+
return this.$store.getters['currentProduct'].inStore === VIRTUAL;
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
flowTypeOptions() {
|
|
259
|
+
return FLOW_TYPE;
|
|
260
|
+
},
|
|
207
261
|
},
|
|
208
262
|
|
|
209
263
|
watch: {
|
|
@@ -312,6 +366,20 @@ export default {
|
|
|
312
366
|
if (this.value.spec.match && this.isMatchEmpty(this.value.spec.match)) {
|
|
313
367
|
delete this.value.spec['match'];
|
|
314
368
|
}
|
|
369
|
+
|
|
370
|
+
if (this.loggingType === FLOW_AUDIT) {
|
|
371
|
+
this.value.spec['loggingRef'] = 'harvester-kube-audit-log-ref';
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (this.loggingType === FLOW_EVENT) {
|
|
375
|
+
const eventSelector = { select: { labels: { 'app.kubernetes.io/name': 'event-tailer' } } };
|
|
376
|
+
|
|
377
|
+
if (!this.value.spec.match) {
|
|
378
|
+
this.value.spec['match'] = [eventSelector];
|
|
379
|
+
} else {
|
|
380
|
+
this.value.spec.match.push(eventSelector);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
315
383
|
},
|
|
316
384
|
onYamlEditorReady(cm) {
|
|
317
385
|
cm.getMode().fold = 'yamlcomments';
|
|
@@ -359,10 +427,21 @@ export default {
|
|
|
359
427
|
:weight="3"
|
|
360
428
|
>
|
|
361
429
|
<Banner
|
|
430
|
+
v-if="!isHarvester"
|
|
362
431
|
color="info"
|
|
363
432
|
class="mt-0"
|
|
364
433
|
:label="t('logging.flow.matches.banner')"
|
|
365
434
|
/>
|
|
435
|
+
<div v-if="isHarvester">
|
|
436
|
+
<LabeledSelect
|
|
437
|
+
v-model:value="loggingType"
|
|
438
|
+
class="mb-20"
|
|
439
|
+
:options="flowTypeOptions"
|
|
440
|
+
:mode="mode"
|
|
441
|
+
:disabled="!isCreate"
|
|
442
|
+
:label="t('generic.type')"
|
|
443
|
+
/>
|
|
444
|
+
</div>
|
|
366
445
|
<ArrayListGrouped
|
|
367
446
|
v-model:value="matches"
|
|
368
447
|
:add-label="t('ingress.rules.addRule')"
|
|
@@ -199,13 +199,13 @@ export default {
|
|
|
199
199
|
v-if="hasMultipleProvidersSelected"
|
|
200
200
|
color="info"
|
|
201
201
|
>
|
|
202
|
-
|
|
202
|
+
{{ t('logging.output.tips.singleProvider') }}
|
|
203
203
|
</Banner>
|
|
204
204
|
<Banner
|
|
205
205
|
v-else-if="!value.allProvidersSupported"
|
|
206
206
|
color="info"
|
|
207
207
|
>
|
|
208
|
-
|
|
208
|
+
{{ t('logging.output.tips.multipleProviders') }}
|
|
209
209
|
</Banner>
|
|
210
210
|
<Tabbed
|
|
211
211
|
v-else
|
|
@@ -52,7 +52,7 @@ export default {
|
|
|
52
52
|
};
|
|
53
53
|
},
|
|
54
54
|
computed: {
|
|
55
|
-
...mapGetters(['currentCluster']),
|
|
55
|
+
...mapGetters(['currentCluster', 'isStandaloneHarvester']),
|
|
56
56
|
|
|
57
57
|
canViewMembers() {
|
|
58
58
|
return canViewProjectMembershipEditor(this.$store);
|
|
@@ -222,7 +222,7 @@ export default {
|
|
|
222
222
|
<ResourceQuota
|
|
223
223
|
:value="value"
|
|
224
224
|
:mode="canEditTabElements"
|
|
225
|
-
:types="
|
|
225
|
+
:types="isStandaloneHarvester ? HARVESTER_TYPES : RANCHER_TYPES"
|
|
226
226
|
@remove="removeQuota"
|
|
227
227
|
/>
|
|
228
228
|
</Tab>
|
package/edit/namespace.vue
CHANGED
|
@@ -15,9 +15,9 @@ import MoveModal from '@shell/components/MoveModal';
|
|
|
15
15
|
import ResourceQuota from '@shell/components/form/ResourceQuota/Namespace';
|
|
16
16
|
import Loading from '@shell/components/Loading';
|
|
17
17
|
import { HARVESTER_TYPES, RANCHER_TYPES } from '@shell/components/form/ResourceQuota/shared';
|
|
18
|
-
import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
|
|
19
18
|
import Labels from '@shell/components/form/Labels';
|
|
20
19
|
import { randomStr } from '@shell/utils/string';
|
|
20
|
+
import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
|
|
21
21
|
|
|
22
22
|
export default {
|
|
23
23
|
emits: ['input'],
|
|
@@ -211,8 +211,9 @@ export default {
|
|
|
211
211
|
|
|
212
212
|
emberLink() {
|
|
213
213
|
if (this.value) {
|
|
214
|
+
// set subtype if editing EKS/GKE/AKS cluster -- this ensures that the component provided by extension is loaded instead of iframing old ember ui
|
|
214
215
|
if (this.value.provisioner) {
|
|
215
|
-
const matchingSubtype = this.subTypes.find((st) =>
|
|
216
|
+
const matchingSubtype = this.subTypes.find((st) => DRIVER_TO_IMPORT[st.id.toLowerCase()] === this.value.provisioner.toLowerCase());
|
|
216
217
|
|
|
217
218
|
if (matchingSubtype) {
|
|
218
219
|
this.selectType(matchingSubtype.id, false);
|
|
@@ -65,14 +65,12 @@ import Advanced from '@shell/edit/provisioning.cattle.io.cluster/tabs/Advanced';
|
|
|
65
65
|
import { DEFAULT_COMMON_BASE_PATH, DEFAULT_SUBDIRS } from '@shell/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig';
|
|
66
66
|
import ClusterAppearance from '@shell/components/form/ClusterAppearance';
|
|
67
67
|
import AddOnAdditionalManifest from '@shell/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest';
|
|
68
|
-
import VsphereUtils from '@shell/utils/v-sphere';
|
|
68
|
+
import VsphereUtils, { VMWARE_VSPHERE } from '@shell/utils/v-sphere';
|
|
69
69
|
|
|
70
70
|
const HARVESTER = 'harvester';
|
|
71
71
|
const HARVESTER_CLOUD_PROVIDER = 'harvester-cloud-provider';
|
|
72
72
|
const NETBIOS_TRUNCATION_LENGTH = 15;
|
|
73
73
|
|
|
74
|
-
const VMWARE_VSPHERE = 'vmwarevsphere';
|
|
75
|
-
|
|
76
74
|
/**
|
|
77
75
|
* Classes to be adopted by the node badges in Machine pools
|
|
78
76
|
*/
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import BrandImage from '@shell/components/BrandImage';
|
|
3
|
+
import TypeDescription from '@shell/components/TypeDescription';
|
|
4
|
+
import ResourceTable from '@shell/components/ResourceTable';
|
|
5
|
+
import Masthead from '@shell/components/ResourceList/Masthead';
|
|
6
|
+
import Loading from '@shell/components/Loading';
|
|
7
|
+
import { HARVESTER_NAME as VIRTUAL } from '@shell/config/features';
|
|
8
|
+
import { CAPI, HCI, MANAGEMENT } from '@shell/config/types';
|
|
9
|
+
import { isHarvesterCluster } from '@shell/utils/cluster';
|
|
10
|
+
import { allHash } from '@shell/utils/promise';
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
components: {
|
|
14
|
+
BrandImage,
|
|
15
|
+
ResourceTable,
|
|
16
|
+
Masthead,
|
|
17
|
+
TypeDescription,
|
|
18
|
+
Loading
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
props: {
|
|
22
|
+
schema: {
|
|
23
|
+
type: Object,
|
|
24
|
+
required: true,
|
|
25
|
+
},
|
|
26
|
+
useQueryParamsForSimpleFiltering: {
|
|
27
|
+
type: Boolean,
|
|
28
|
+
default: false
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async fetch() {
|
|
33
|
+
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
34
|
+
|
|
35
|
+
const hash = await allHash({
|
|
36
|
+
hciClusters: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.CLUSTER }),
|
|
37
|
+
mgmtClusters: this.$store.dispatch(`${ inStore }/findAll`, { type: MANAGEMENT.CLUSTER })
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
this.hciClusters = hash.hciClusters;
|
|
41
|
+
this.mgmtClusters = hash.mgmtClusters;
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
data() {
|
|
45
|
+
const resource = CAPI.RANCHER_CLUSTER;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
navigating: false,
|
|
49
|
+
VIRTUAL,
|
|
50
|
+
hciDashboard: HCI.DASHBOARD,
|
|
51
|
+
resource,
|
|
52
|
+
hResource: HCI.CLUSTER,
|
|
53
|
+
hciClusters: [],
|
|
54
|
+
mgmtClusters: []
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
computed: {
|
|
59
|
+
realSchema() {
|
|
60
|
+
return this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
importLocation() {
|
|
64
|
+
return {
|
|
65
|
+
name: 'c-cluster-product-resource-create',
|
|
66
|
+
params: {
|
|
67
|
+
product: this.$store.getters['currentProduct'].name,
|
|
68
|
+
resource: this.schema.id,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
canCreateCluster() {
|
|
74
|
+
const schema = this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER);
|
|
75
|
+
|
|
76
|
+
return !!schema?.collectionMethods.find((x) => x.toLowerCase() === 'post');
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
rows() {
|
|
80
|
+
return this.hciClusters.filter((c) => {
|
|
81
|
+
const cluster = this.mgmtClusters.find((cluster) => cluster?.metadata?.name === c?.status?.clusterName);
|
|
82
|
+
|
|
83
|
+
return isHarvesterCluster(cluster);
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
typeDisplay() {
|
|
88
|
+
return this.t(`typeLabel."${ HCI.CLUSTER }"`, { count: this.row?.length || 0 });
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
methods: {
|
|
93
|
+
async goToCluster(row) {
|
|
94
|
+
const timeout = setTimeout(() => {
|
|
95
|
+
// Don't show loading indicator for quickly fetched plugins
|
|
96
|
+
this.navigating = row.id;
|
|
97
|
+
}, 1000);
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await row.goToCluster();
|
|
101
|
+
|
|
102
|
+
clearTimeout(timeout);
|
|
103
|
+
this.navigating = false;
|
|
104
|
+
} catch {
|
|
105
|
+
// The error handling is carried out within goToCluster, but just in case something happens before the promise chain can catch it...
|
|
106
|
+
clearTimeout(timeout);
|
|
107
|
+
this.navigating = false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<template>
|
|
115
|
+
<Loading v-if="$fetchState.pending" />
|
|
116
|
+
<div v-else>
|
|
117
|
+
<Masthead
|
|
118
|
+
:schema="realSchema"
|
|
119
|
+
:resource="resource"
|
|
120
|
+
:is-creatable="false"
|
|
121
|
+
:type-display="typeDisplay"
|
|
122
|
+
>
|
|
123
|
+
<template #typeDescription>
|
|
124
|
+
<TypeDescription :resource="hResource" />
|
|
125
|
+
</template>
|
|
126
|
+
|
|
127
|
+
<template
|
|
128
|
+
v-if="canCreateCluster"
|
|
129
|
+
slot="extraActions"
|
|
130
|
+
>
|
|
131
|
+
<n-link
|
|
132
|
+
:to="importLocation"
|
|
133
|
+
class="btn role-primary"
|
|
134
|
+
>
|
|
135
|
+
{{ t('cluster.importAction') }}
|
|
136
|
+
</n-link>
|
|
137
|
+
</template>
|
|
138
|
+
</Masthead>
|
|
139
|
+
|
|
140
|
+
<ResourceTable
|
|
141
|
+
v-if="rows && rows.length"
|
|
142
|
+
:schema="schema"
|
|
143
|
+
:rows="rows"
|
|
144
|
+
:is-creatable="true"
|
|
145
|
+
:namespaced="false"
|
|
146
|
+
:use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
|
|
147
|
+
>
|
|
148
|
+
<template #col:name="{row}">
|
|
149
|
+
<td>
|
|
150
|
+
<span class="cluster-link">
|
|
151
|
+
<a
|
|
152
|
+
v-if="row.isReady"
|
|
153
|
+
class="link"
|
|
154
|
+
:disabled="navigating"
|
|
155
|
+
@click="goToCluster(row)"
|
|
156
|
+
>{{ row.nameDisplay }}</a>
|
|
157
|
+
<span v-else>
|
|
158
|
+
{{ row.nameDisplay }}
|
|
159
|
+
</span>
|
|
160
|
+
<i
|
|
161
|
+
class="icon icon-spinner icon-spin ml-5"
|
|
162
|
+
:class="{'navigating': navigating === row.id}"
|
|
163
|
+
/>
|
|
164
|
+
</span>
|
|
165
|
+
</td>
|
|
166
|
+
</template>
|
|
167
|
+
|
|
168
|
+
<template #cell:harvester="{row}">
|
|
169
|
+
<n-link
|
|
170
|
+
class="btn btn-sm role-primary"
|
|
171
|
+
:to="row.detailLocation"
|
|
172
|
+
>
|
|
173
|
+
{{ t('harvesterManager.manage') }}
|
|
174
|
+
</n-link>
|
|
175
|
+
</template>
|
|
176
|
+
</ResourceTable>
|
|
177
|
+
<div v-else>
|
|
178
|
+
<div class="no-clusters">
|
|
179
|
+
{{ t('harvesterManager.cluster.none') }}
|
|
180
|
+
</div>
|
|
181
|
+
<hr class="info-section">
|
|
182
|
+
<div class="logo">
|
|
183
|
+
<BrandImage
|
|
184
|
+
file-name="harvester.png"
|
|
185
|
+
height="64"
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
<div class="tagline">
|
|
189
|
+
<div>{{ t('harvesterManager.cluster.description') }}</div>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="tagline sub-tagline">
|
|
192
|
+
<div v-clean-html="t('harvesterManager.cluster.learnMore', {}, true)" />
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</template>
|
|
197
|
+
|
|
198
|
+
<style lang="scss" scoped>
|
|
199
|
+
.cluster-link {
|
|
200
|
+
display: flex;
|
|
201
|
+
align-items: center;
|
|
202
|
+
|
|
203
|
+
.icon {
|
|
204
|
+
// Use visibility to avoid the columns re-adjusting when the icon is shown
|
|
205
|
+
visibility: hidden;
|
|
206
|
+
|
|
207
|
+
&.navigating {
|
|
208
|
+
visibility: visible;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
.no-clusters {
|
|
214
|
+
text-align: center;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.info-section {
|
|
218
|
+
margin-top: 60px;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.logo {
|
|
222
|
+
display: flex;
|
|
223
|
+
justify-content: center;
|
|
224
|
+
margin: 60px 0 40px 0;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.tagline {
|
|
228
|
+
display: flex;
|
|
229
|
+
justify-content: center;
|
|
230
|
+
margin-top: 30px;
|
|
231
|
+
|
|
232
|
+
> div {
|
|
233
|
+
font-size: 16px;
|
|
234
|
+
line-height: 22px;
|
|
235
|
+
max-width: 80%;
|
|
236
|
+
text-align: center;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.link {
|
|
241
|
+
cursor: pointer;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
</style>
|