@rancher/shell 3.0.0 → 3.0.1-rc.2

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 (92) 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 +26 -8
  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 +6 -4
  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 +16 -14
  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/constraints.gatekeeper.sh.constraint/index.vue +5 -2
  44. package/edit/logging-flow/Match.vue +75 -42
  45. package/edit/logging-flow/index.vue +89 -10
  46. package/edit/logging.banzaicloud.io.output/index.vue +2 -2
  47. package/edit/management.cattle.io.project.vue +2 -2
  48. package/edit/namespace.vue +1 -1
  49. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +17 -7
  50. package/edit/provisioning.cattle.io.cluster/index.vue +2 -1
  51. package/edit/provisioning.cattle.io.cluster/rke2.vue +1 -3
  52. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +20 -6
  53. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +1 -1
  54. package/list/harvesterhci.io.management.cluster.vue +244 -0
  55. package/list/namespace.vue +16 -4
  56. package/models/__tests__/management.cattle.io.cluster.test.ts +45 -3
  57. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +0 -86
  58. package/models/fleet.cattle.io.bundle.js +3 -1
  59. package/models/fleet.cattle.io.gitrepo.js +46 -48
  60. package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +88 -0
  61. package/models/management.cattle.io.cluster.js +26 -5
  62. package/models/management.cattle.io.setting.js +25 -0
  63. package/models/provisioning.cattle.io.cluster.js +5 -14
  64. package/models/storage.k8s.io.storageclass.js +15 -4
  65. package/package.json +8 -6
  66. package/pages/auth/login.vue +8 -2
  67. package/pages/auth/setup.vue +1 -1
  68. package/pages/c/_cluster/fleet/index.vue +2 -4
  69. package/pages/c/_cluster/settings/brand.vue +4 -1
  70. package/pages/prefs.vue +22 -10
  71. package/plugins/dashboard-store/resource-class.js +11 -3
  72. package/plugins/steve/steve-pagination-utils.ts +1 -1
  73. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -2
  74. package/scripts/extension/parse-tag-name +19 -12
  75. package/scripts/publish-shell.sh +10 -2
  76. package/scripts/test-plugins-build.sh +8 -9
  77. package/scripts/typegen.sh +2 -0
  78. package/store/features.js +1 -0
  79. package/store/i18n.js +5 -1
  80. package/store/prefs.js +8 -0
  81. package/types/resources/fleet.d.ts +40 -0
  82. package/types/shell/index.d.ts +524 -396
  83. package/utils/auth.js +1 -1
  84. package/utils/create-yaml.js +1 -1
  85. package/utils/favicon.js +2 -0
  86. package/utils/fleet.ts +159 -0
  87. package/utils/gc/gc.ts +1 -1
  88. package/utils/v-sphere.ts +31 -0
  89. package/utils/validators/cron-schedule.js +1 -1
  90. package/utils/validators/formRules/index.ts +1 -1
  91. package/utils/version.js +1 -1
  92. package/vue.config.js +2 -2
@@ -13,10 +13,10 @@ function purifyContent(value) {
13
13
  }
14
14
  }
15
15
 
16
- function bind(el, { value, oldValue, modifiers }) {
16
+ function beforeMount(el, { value, oldValue, modifiers }) {
17
17
  const purifiedValue = purifyContent(value);
18
18
 
19
- VTooltip.bind(
19
+ VTooltip.beforeMount(
20
20
  el,
21
21
  {
22
22
  value: purifiedValue, oldValue, modifiers
@@ -25,8 +25,8 @@ function bind(el, { value, oldValue, modifiers }) {
25
25
 
26
26
  const cleanTooltipDirective = {
27
27
  ...VTooltip,
28
- bind,
29
- update: bind,
28
+ beforeMount,
29
+ updated: beforeMount,
30
30
  };
31
31
 
32
32
  export default cleanTooltipDirective;
@@ -1,7 +1,7 @@
1
1
  <script>
2
2
  import merge from 'lodash/merge';
3
3
  import { ucFirst } from '@shell/utils/string';
4
- import { isSimpleKeyValue } from '@shell/utils/object';
4
+ import { isSimpleKeyValue, set } from '@shell/utils/object';
5
5
  import { _CREATE, _VIEW } from '@shell/config/query-params';
6
6
  import { SCHEMA, NAMESPACE } from '@shell/config/types';
7
7
  import CreateEditView from '@shell/mixins/create-edit-view';
@@ -222,6 +222,9 @@ export default {
222
222
  this.value.spec.match.namespaces = [];
223
223
  this.value.spec.match.excludedNamespaces = [];
224
224
  }
225
+ },
226
+ setParameters(e) {
227
+ return set(this.value.spec, 'parameters', e);
225
228
  }
226
229
  }
227
230
  };
@@ -356,7 +359,7 @@ export default {
356
359
  v-model:value="parametersYaml"
357
360
  class="yaml-editor"
358
361
  :editor-mode="editorMode"
359
- @newObject="$set(value.spec, 'parameters', $event)"
362
+ @newObject="setParameters"
360
363
  />
361
364
  </Tab>
362
365
  </Tabbed>
@@ -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
- <KeyValue
55
- v-model:value="value.labels"
56
- :title="value.select ? t('logging.flow.matches.pods.title.include') : t('logging.flow.matches.pods.title.exclude')"
57
- :mode="mode"
58
- :initial-empty-row="true"
59
- :read-allowed="false"
60
- :title-add="true"
61
- protip=""
62
- :key-label="t('logging.flow.matches.pods.keyLabel')"
63
- :value-label="t('logging.flow.matches.pods.valueLabel')"
64
- :add-label="t('logging.flow.matches.pods.addLabel')"
65
- />
66
- <div class="spacer" />
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 class="spacer" />
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.namespaces.title.include') : t('logging.flow.matches.namespaces.title.exclude') }}
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
- <Select
116
- v-model:value="value.namespaces"
117
- class="lg"
118
- :options="namespaces"
119
- :placeholder="t('logging.flow.matches.namespaces.placeholder')"
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 { LOGGING, NAMESPACE, NODE, SCHEMA } from '@shell/config/types';
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 hasAccessToClusterOutputs = this.$store.getters[`cluster/schemaFor`](LOGGING.CLUSTER_OUTPUT);
57
- const hasAccessToOutputs = this.$store.getters[`cluster/schemaFor`](LOGGING.OUTPUT);
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[`cluster/schemaFor`](NODE);
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('cluster/findAll', { type }) : Promise.resolve([]);
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 schemas = this.$store.getters['cluster/all'](SCHEMA);
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
- return output.namespace === this.value.namespace;
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
- return clusterOutput.namespace === 'cattle-logging-system';
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
- This output is configured with multiple providers. We currently only support a single provider per output. You can view or edit the YAML.
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
- This output is configured with providers we don't support yet. You can view or edit the YAML.
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="isHarvester ? HARVESTER_TYPES : RANCHER_TYPES"
225
+ :types="isStandaloneHarvester ? HARVESTER_TYPES : RANCHER_TYPES"
226
226
  @remove="removeQuota"
227
227
  />
228
228
  </Tab>
@@ -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'],
@@ -113,7 +113,7 @@ describe('component: DirectoryConfig', () => {
113
113
  expect(wrapper.vm.value.k8sDistro).toStrictEqual(k8sDistroValue);
114
114
  });
115
115
 
116
- it('should render the component with configuration being an empty object, without errors and radio be of value DATA_DIR_RADIO_OPTIONS.DEFAULT (edit scenario)', () => {
116
+ it('should render the component with configuration being an empty object, without errors and radio be of value DATA_DIR_RADIO_OPTIONS.CUSTOM (edit scenario)', () => {
117
117
  const newMountOptions = clone(mountOptions);
118
118
 
119
119
  newMountOptions.propsData.value = {};
@@ -131,17 +131,21 @@ describe('component: DirectoryConfig', () => {
131
131
  const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
132
132
 
133
133
  expect(title.exists()).toBe(true);
134
- expect(radioInput.exists()).toBe(true);
134
+ expect(radioInput.isVisible()).toBe(false);
135
135
 
136
- expect(wrapper.vm.dataConfigRadioValue).toBe(DATA_DIR_RADIO_OPTIONS.DEFAULT);
136
+ expect(wrapper.vm.dataConfigRadioValue).toBe(DATA_DIR_RADIO_OPTIONS.CUSTOM);
137
137
 
138
138
  // since we have all of the vars empty, then the inputs should not be there
139
- expect(systemAgentInput.exists()).toBe(false);
140
- expect(provisioningInput.exists()).toBe(false);
141
- expect(k8sDistroInput.exists()).toBe(false);
139
+ expect(systemAgentInput.exists()).toBe(true);
140
+ expect(provisioningInput.exists()).toBe(true);
141
+ expect(k8sDistroInput.exists()).toBe(true);
142
+
143
+ expect(systemAgentInput.attributes().disabled).toBeDefined();
144
+ expect(provisioningInput.attributes().disabled).toBeDefined();
145
+ expect(k8sDistroInput.attributes().disabled).toBeDefined();
142
146
  });
143
147
 
144
- it('radio input should be set to DATA_DIR_RADIO_OPTIONS.CUSTOM with all data dir values existing and different (edit scenario)', async() => {
148
+ it('radio input should be set to DATA_DIR_RADIO_OPTIONS.CUSTOM with all data dir values existing and different (edit scenario)', () => {
145
149
  const newMountOptions = clone(mountOptions);
146
150
  const inputPath = 'some-data-dir';
147
151
 
@@ -157,14 +161,20 @@ describe('component: DirectoryConfig', () => {
157
161
 
158
162
  expect(wrapper.vm.dataConfigRadioValue).toBe(DATA_DIR_RADIO_OPTIONS.CUSTOM);
159
163
 
164
+ const radioInput = wrapper.find('[data-testid="rke2-directory-config-radio-input"]');
160
165
  const systemAgentInput = wrapper.find('[data-testid="rke2-directory-config-systemAgent-data-dir"]');
161
166
  const provisioningInput = wrapper.find('[data-testid="rke2-directory-config-provisioning-data-dir"]');
162
167
  const k8sDistroInput = wrapper.find('[data-testid="rke2-directory-config-k8sDistro-data-dir"]');
163
168
 
169
+ expect(radioInput.isVisible()).toBe(false);
164
170
  expect(systemAgentInput.isVisible()).toBe(true);
165
171
  expect(provisioningInput.isVisible()).toBe(true);
166
172
  expect(k8sDistroInput.isVisible()).toBe(true);
167
173
 
174
+ expect(systemAgentInput.attributes().disabled).toBeDefined();
175
+ expect(provisioningInput.attributes().disabled).toBeDefined();
176
+ expect(k8sDistroInput.attributes().disabled).toBeDefined();
177
+
168
178
  expect(wrapper.vm.value.systemAgent).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.AGENT }`);
169
179
  expect(wrapper.vm.value.provisioning).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.PROVISIONING }`);
170
180
  expect(wrapper.vm.value.k8sDistro).toStrictEqual(`${ inputPath }/${ DEFAULT_SUBDIRS.K8S_DISTRO_K3S }`);
@@ -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) => st.id.toLowerCase() === this.value.provisioner.toLowerCase() || DRIVER_TO_IMPORT[st.id.toLowerCase()] === this.value.provisioner.toLowerCase());
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
  */
@@ -1,8 +1,9 @@
1
1
 
2
2
  <script>
3
3
  import { LabeledInput } from '@components/Form/LabeledInput';
4
- import { _CREATE } from '@shell/config/query-params';
4
+ import { _CREATE, _EDIT } from '@shell/config/query-params';
5
5
  import RadioGroup from '@components/Form/Radio/RadioGroup.vue';
6
+ import { Banner } from '@components/Banner';
6
7
 
7
8
  export const DATA_DIR_RADIO_OPTIONS = {
8
9
  DEFAULT: 'defaultDataDir',
@@ -23,7 +24,8 @@ export default {
23
24
  name: 'DirectoryConfig',
24
25
  components: {
25
26
  LabeledInput,
26
- RadioGroup
27
+ RadioGroup,
28
+ Banner
27
29
  },
28
30
  props: {
29
31
  mode: {
@@ -50,9 +52,7 @@ export default {
50
52
  }
51
53
 
52
54
  if (this.mode !== _CREATE) {
53
- if (this.value?.systemAgent?.length || this.value?.provisioning?.length || this.value?.k8sDistro?.length) {
54
- dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.CUSTOM;
55
- }
55
+ dataConfigRadioValue = DATA_DIR_RADIO_OPTIONS.CUSTOM;
56
56
  }
57
57
 
58
58
  return {
@@ -85,6 +85,9 @@ export default {
85
85
  }
86
86
  },
87
87
  computed: {
88
+ isDisabled() {
89
+ return this.mode === _EDIT;
90
+ },
88
91
  dataConfigRadioOptions() {
89
92
  const defaultDataDirOption = {
90
93
  value: DATA_DIR_RADIO_OPTIONS.DEFAULT,
@@ -148,10 +151,17 @@ export default {
148
151
  <template>
149
152
  <div class="row">
150
153
  <div class="col span-8">
151
- <h3 class="mb-20">
154
+ <h3>
152
155
  {{ t('cluster.directoryConfig.title') }}
153
156
  </h3>
157
+ <Banner
158
+ class="mb-20"
159
+ :closable="false"
160
+ color="info"
161
+ label-key="cluster.directoryConfig.banner"
162
+ />
154
163
  <RadioGroup
164
+ v-show="!isDisabled"
155
165
  :value="dataConfigRadioValue"
156
166
  class="mb-10"
157
167
  :mode="mode"
@@ -167,6 +177,7 @@ export default {
167
177
  :mode="mode"
168
178
  :label="t('cluster.directoryConfig.common.label')"
169
179
  :tooltip="t('cluster.directoryConfig.common.tooltip')"
180
+ :disabled="isDisabled"
170
181
  data-testid="rke2-directory-config-common-data-dir"
171
182
  />
172
183
  <div v-if="dataConfigRadioValue === DATA_DIR_RADIO_OPTIONS.CUSTOM">
@@ -176,6 +187,7 @@ export default {
176
187
  :mode="mode"
177
188
  :label="t('cluster.directoryConfig.systemAgent.label')"
178
189
  :tooltip="t('cluster.directoryConfig.systemAgent.tooltip')"
190
+ :disabled="isDisabled"
179
191
  data-testid="rke2-directory-config-systemAgent-data-dir"
180
192
  />
181
193
  <LabeledInput
@@ -184,6 +196,7 @@ export default {
184
196
  :mode="mode"
185
197
  :label="t('cluster.directoryConfig.provisioning.label')"
186
198
  :tooltip="t('cluster.directoryConfig.provisioning.tooltip')"
199
+ :disabled="isDisabled"
187
200
  data-testid="rke2-directory-config-provisioning-data-dir"
188
201
  />
189
202
  <LabeledInput
@@ -192,6 +205,7 @@ export default {
192
205
  :mode="mode"
193
206
  :label="t('cluster.directoryConfig.k8sDistro.label')"
194
207
  :tooltip="t('cluster.directoryConfig.k8sDistro.tooltip')"
208
+ :disabled="isDisabled"
195
209
  data-testid="rke2-directory-config-k8sDistro-data-dir"
196
210
  />
197
211
  </div>
@@ -90,7 +90,7 @@ export default {
90
90
  :default-add-value="defaultAddValue"
91
91
  :initial-empty-row="true"
92
92
  :mode="mode"
93
- @input="update"
93
+ @update:value="update"
94
94
  >
95
95
  <template #default="{row}">
96
96
  <div class="row">