@rancher/shell 0.3.8 → 0.3.10

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 (145) hide show
  1. package/assets/translations/en-us.yaml +47 -26
  2. package/assets/translations/zh-hans.yaml +82 -16
  3. package/babel.config.js +17 -4
  4. package/chart/istio.vue +11 -11
  5. package/chart/rancher-backup/S3.vue +1 -1
  6. package/components/AsyncButton.vue +2 -2
  7. package/components/ButtonGroup.vue +1 -1
  8. package/components/CodeMirror.vue +146 -14
  9. package/components/CompoundStatusBadge.vue +1 -1
  10. package/components/ContainerResourceLimit.vue +14 -1
  11. package/components/CopyCode.vue +1 -1
  12. package/components/CruResource.vue +21 -5
  13. package/components/DetailTop.vue +1 -1
  14. package/components/ExplorerProjectsNamespaces.vue +8 -4
  15. package/components/GlobalRoleBindings.vue +1 -1
  16. package/components/GroupPanel.vue +57 -0
  17. package/components/HarvesterServiceAddOnConfig.vue +2 -117
  18. package/components/ResourceDetail/Masthead.vue +1 -1
  19. package/components/ResourceList/Masthead.vue +0 -6
  20. package/components/ResourceList/ResourceLoadingIndicator.vue +1 -9
  21. package/components/ResourceList/index.vue +7 -6
  22. package/components/ResourceTable.vue +13 -3
  23. package/components/SortableTable/THead.vue +3 -3
  24. package/components/SortableTable/index.vue +3 -3
  25. package/components/Tabbed/Tab.vue +1 -1
  26. package/components/Tabbed/index.vue +1 -1
  27. package/components/Wizard.vue +9 -6
  28. package/components/YamlEditor.vue +2 -2
  29. package/components/__tests__/NamespaceFilter.test.ts +26 -7
  30. package/components/auth/RoleDetailEdit.vue +1 -1
  31. package/components/auth/SelectPrincipal.vue +1 -1
  32. package/components/fleet/FleetRepos.vue +1 -1
  33. package/components/form/ArrayList.vue +2 -2
  34. package/components/form/KeyValue.vue +37 -3
  35. package/components/form/Labels.vue +34 -14
  36. package/components/form/MatchExpressions.vue +120 -21
  37. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  38. package/components/form/NameNsDescription.vue +1 -1
  39. package/components/form/NodeAffinity.vue +54 -4
  40. package/components/form/PlusMinus.vue +2 -2
  41. package/components/form/PodAffinity.vue +160 -47
  42. package/components/form/Probe.vue +1 -1
  43. package/components/form/ProjectMemberEditor.vue +8 -4
  44. package/components/form/ResourceQuota/NamespaceRow.vue +1 -1
  45. package/components/form/ServicePorts.vue +2 -2
  46. package/components/form/Tolerations.vue +70 -7
  47. package/components/form/WorkloadPorts.vue +2 -1
  48. package/components/form/__tests__/ArrayList.test.ts +3 -3
  49. package/components/form/__tests__/KeyValue.test.ts +17 -0
  50. package/components/form/__tests__/MatchExpressions.test.ts +1 -1
  51. package/components/formatter/ClusterLink.vue +3 -3
  52. package/components/formatter/LiveDate.vue +1 -1
  53. package/components/formatter/PodImages.vue +1 -1
  54. package/components/formatter/RKETemplateName.vue +1 -1
  55. package/components/formatter/Shortened.vue +1 -1
  56. package/components/nav/Header.vue +9 -7
  57. package/components/nav/NamespaceFilter.vue +103 -54
  58. package/config/labels-annotations.js +8 -5
  59. package/config/settings.ts +8 -6
  60. package/config/types.js +6 -4
  61. package/core/plugin-routes.ts +26 -7
  62. package/detail/provisioning.cattle.io.cluster.vue +4 -4
  63. package/edit/cis.cattle.io.clusterscan.vue +1 -1
  64. package/edit/configmap.vue +33 -6
  65. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +19 -149
  66. package/edit/logging-flow/index.vue +2 -2
  67. package/edit/logging.banzaicloud.io.output/providers/elasticsearch.vue +12 -0
  68. package/edit/logging.banzaicloud.io.output/providers/opensearch.vue +12 -0
  69. package/edit/management.cattle.io.project.vue +7 -0
  70. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
  71. package/edit/monitoring.coreos.com.alertmanagerconfig/routeConfig.vue +2 -2
  72. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +11 -8
  73. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +2 -2
  74. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +12 -4
  75. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +140 -0
  76. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +158 -0
  77. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/selectors.ts +45 -0
  78. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
  79. package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +326 -0
  80. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +1 -1
  81. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -1
  82. package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +2 -2
  83. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +143 -169
  84. package/edit/provisioning.cattle.io.cluster/index.vue +1 -0
  85. package/edit/provisioning.cattle.io.cluster/rke2.vue +75 -6
  86. package/edit/resources.cattle.io.restore.vue +2 -2
  87. package/edit/service.vue +22 -3
  88. package/edit/storage.k8s.io.storageclass/index.vue +1 -1
  89. package/edit/workload/Job.vue +2 -2
  90. package/edit/workload/index.vue +1 -1
  91. package/edit/workload/mixins/workload.js +7 -1
  92. package/edit/workload/storage/__tests__/Storage.test.ts +84 -5
  93. package/initialize/index.js +1 -0
  94. package/layouts/default.vue +1 -1
  95. package/mixins/chart.js +1 -1
  96. package/mixins/resource-fetch-namespaced.js +19 -27
  97. package/mixins/resource-fetch.js +0 -5
  98. package/models/__tests__/namespace.test.ts +125 -0
  99. package/models/batch.cronjob.js +18 -3
  100. package/models/management.cattle.io.project.js +6 -1
  101. package/models/persistentvolume.js +1 -1
  102. package/models/workload.js +1 -1
  103. package/models/workload.service.js +22 -7
  104. package/package.json +17 -6
  105. package/pages/auth/login.vue +47 -49
  106. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  107. package/pages/c/_cluster/apps/charts/install.vue +42 -51
  108. package/pages/c/_cluster/explorer/index.vue +1 -1
  109. package/pages/c/_cluster/monitoring/index.vue +1 -1
  110. package/pages/c/_cluster/settings/performance.vue +53 -18
  111. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  112. package/pages/c/_cluster/uiplugins/index.vue +16 -5
  113. package/pages/home.vue +1 -1
  114. package/pages/prefs.vue +18 -2
  115. package/plugins/clean-html-directive.js +1 -1
  116. package/plugins/clean-tooltip-directive.js +33 -0
  117. package/plugins/codemirror.js +158 -0
  118. package/plugins/dashboard-store/actions.js +4 -2
  119. package/plugins/dashboard-store/getters.js +6 -0
  120. package/plugins/dashboard-store/mutations.js +2 -2
  121. package/plugins/plugin.js +6 -1
  122. package/plugins/steve/actions.js +1 -1
  123. package/plugins/steve/getters.js +14 -3
  124. package/plugins/steve/resourceWatcher.js +36 -62
  125. package/plugins/steve/subscribe.js +137 -21
  126. package/plugins/steve/worker/index.js +7 -1
  127. package/plugins/steve/worker/web-worker.advanced.js +26 -8
  128. package/plugins/steve/worker/web-worker.basic.js +23 -4
  129. package/public/index.html +1 -1
  130. package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -2
  131. package/rancher-components/components/Form/Radio/RadioGroup.vue +2 -2
  132. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +1 -1
  133. package/store/index.js +16 -61
  134. package/store/store-types.js +5 -0
  135. package/store/type-map.js +1 -1
  136. package/types/shell/index.d.ts +42 -7
  137. package/utils/__tests__/create-yaml.test.ts +63 -0
  138. package/utils/array.ts +4 -0
  139. package/utils/create-yaml.js +105 -8
  140. package/utils/namespace-filter.js +17 -5
  141. package/utils/projectAndNamespaceFiltering.utils.ts +62 -0
  142. package/utils/selector.js +6 -5
  143. package/utils/settings.ts +17 -7
  144. package/vue.config.js +2 -2
  145. package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +0 -93
@@ -77,7 +77,7 @@ export default {
77
77
  <h3>
78
78
  {{ t('registryMirror.header') }}
79
79
  <i
80
- v-tooltip="t('registryMirror.toolTip')"
80
+ v-clean-tooltip="t('registryMirror.toolTip')"
81
81
  class="icon icon-info"
82
82
  />
83
83
  </h3>
@@ -116,7 +116,7 @@ export default {
116
116
  <h3>
117
117
  {{ t('registryMirrorRewrite.header') }}
118
118
  <i
119
- v-tooltip="t('registryMirrorRewrite.toolTip')"
119
+ v-clean-tooltip="t('registryMirrorRewrite.toolTip')"
120
120
  class="icon icon-info"
121
121
  />
122
122
  </h3>
@@ -1,8 +1,80 @@
1
1
  import { mount } from '@vue/test-utils';
2
2
  import rke2 from '@shell/edit/provisioning.cattle.io.cluster/rke2.vue';
3
3
 
4
+ /**
5
+ * DISCLAIMER ***************************************************************************************
6
+ * Declarations should not be done outside the tests!!
7
+ * This component is overwhelming for test and requires too much initialization.
8
+ * In this way the tests are more readable and we can avoid annoying repetitions.
9
+ ****************************************************************************************************
10
+ */
11
+ const defaultStubs = {
12
+ CruResource: { template: '<div><slot></slot></div>' }, // Required to render the slot content
13
+ Banner: true,
14
+ LabeledSelect: true,
15
+ ACE: true,
16
+ AgentEnv: true,
17
+ ArrayList: true,
18
+ ArrayListGrouped: true,
19
+ BadgeState: true,
20
+ Checkbox: true,
21
+ ClusterMembershipEditor: true,
22
+ DrainOptions: true,
23
+ LabeledInput: true,
24
+ Labels: true,
25
+ Loading: true,
26
+ MachinePool: true,
27
+ MatchExpressions: true,
28
+ NameNsDescription: true,
29
+ Questions: true,
30
+ RadioGroup: true,
31
+ RegistryConfigs: true,
32
+ RegistryMirrors: true,
33
+ S3Config: true,
34
+ SelectCredential: true,
35
+ SelectOrCreateAuthSecret: true,
36
+ Tab: true,
37
+ Tabbed: true,
38
+ UnitInput: true,
39
+ YamlEditor: true,
40
+ };
41
+
42
+ const defaultComputed = {
43
+ showForm() {
44
+ return true;
45
+ },
46
+ showk8s21LegacyWarning() {
47
+ return false;
48
+ },
49
+ };
50
+
51
+ const defaultGetters = {
52
+ currentStore: () => 'current_store',
53
+ 'management/schemaFor': jest.fn(),
54
+ 'current_store/all': jest.fn(),
55
+ 'i18n/t': jest.fn(),
56
+ 'i18n/withFallback': jest.fn(),
57
+ };
58
+
59
+ const defaultMocks = {
60
+ $fetchState: { pending: false },
61
+ $route: {
62
+ name: 'anything',
63
+ query: { AS: 'yaml' },
64
+ },
65
+ };
66
+
67
+ const defaultSpec = {
68
+ rkeConfig: { etcd: { disableSnapshots: false } },
69
+ chartValues: {},
70
+ };
71
+
4
72
  describe('component: rke2', () => {
5
- // Disable existing log to avoid pollution
73
+ /**
74
+ * DISCLAIMER ***************************************************************************************
75
+ * Logs are prevented to avoid polluting the test output.
76
+ ****************************************************************************************************
77
+ */
6
78
  // eslint-disable-next-line jest/no-hooks
7
79
  beforeEach(() => {
8
80
  jest.spyOn(console, 'log').mockImplementation(() => {});
@@ -23,76 +95,25 @@ describe('component: rke2', () => {
23
95
  mode: 'create',
24
96
  value: {
25
97
  spec: {
26
- rkeConfig: { etcd: { disableSnapshots: false } },
27
- chartValues: {},
98
+ ...defaultSpec,
28
99
  defaultPodSecurityAdmissionConfigurationTemplateName: label,
29
100
  kubernetesVersion: k8s
30
101
  }
31
102
  },
32
103
  provider: 'whatever',
33
- resource: {}
34
104
  },
35
- computed: {
36
- showForm() {
37
- return true;
38
- },
39
- hasMachinePools() {
40
- return false;
41
- },
42
- showk8s21LegacyWarning() {
43
- return false;
44
- },
45
- },
46
- mocks: {
47
- $fetchState: { pending: false },
48
- $route: {
49
- name: 'anything',
50
- query: { AS: 'yaml' },
51
- },
105
+ computed: defaultComputed,
106
+ mocks: {
107
+ ...defaultMocks,
52
108
  $store: {
53
- getters: {
54
- currentStore: () => 'current_store',
55
- 'management/schemaFor': jest.fn(),
56
- 'current_store/all': jest.fn(),
57
- 'i18n/t': jest.fn(),
58
- 'i18n/withFallback': jest.fn(),
59
- },
109
+ getters: defaultGetters,
60
110
  dispatch: {
61
111
  'management/find': jest.fn(),
62
112
  'management/findAll': () => ([option]),
63
113
  }
64
114
  },
65
115
  },
66
- stubs: {
67
- CruResource: { template: '<div><slot></slot></div>' }, // Required to render the slot content
68
- Banner: true,
69
- LabeledSelect: true,
70
- ACE: true,
71
- AgentEnv: true,
72
- ArrayList: true,
73
- ArrayListGrouped: true,
74
- BadgeState: true,
75
- Checkbox: true,
76
- ClusterMembershipEditor: true,
77
- DrainOptions: true,
78
- LabeledInput: true,
79
- Labels: true,
80
- Loading: true,
81
- MachinePool: true,
82
- MatchExpressions: true,
83
- NameNsDescription: true,
84
- Questions: true,
85
- RadioGroup: true,
86
- RegistryConfigs: true,
87
- RegistryMirrors: true,
88
- S3Config: true,
89
- SelectCredential: true,
90
- SelectOrCreateAuthSecret: true,
91
- Tab: true,
92
- Tabbed: true,
93
- UnitInput: true,
94
- YamlEditor: true,
95
- }
116
+ stubs: defaultStubs
96
117
  });
97
118
 
98
119
  const select = wrapper.find('[data-testid="rke2-custom-edit-psa"]');
@@ -115,76 +136,25 @@ describe('component: rke2', () => {
115
136
  mode: 'create',
116
137
  value: {
117
138
  spec: {
118
- rkeConfig: { etcd: { disableSnapshots: false } },
119
- chartValues: {},
139
+ ...defaultSpec,
120
140
  defaultPodSecurityAdmissionConfigurationTemplateName: label,
121
141
  kubernetesVersion: k8s
122
142
  }
123
143
  },
124
144
  provider: 'whatever',
125
- resource: {}
126
- },
127
- computed: {
128
- showForm() {
129
- return true;
130
- },
131
- hasMachinePools() {
132
- return false;
133
- },
134
- showk8s21LegacyWarning() {
135
- return false;
136
- },
137
145
  },
138
- mocks: {
139
- $fetchState: { pending: false },
140
- $route: {
141
- name: 'anything',
142
- query: { AS: 'yaml' },
143
- },
146
+ computed: defaultComputed,
147
+ mocks: {
148
+ ...defaultMocks,
144
149
  $store: {
145
- getters: {
146
- currentStore: () => 'current_store',
147
- 'management/schemaFor': jest.fn(),
148
- 'current_store/all': jest.fn(),
149
- 'i18n/t': jest.fn(),
150
- 'i18n/withFallback': jest.fn(),
151
- },
150
+ getters: defaultGetters,
152
151
  dispatch: {
153
152
  'management/find': jest.fn(),
154
153
  'management/findAll': () => ([option]),
155
154
  }
156
155
  },
157
156
  },
158
- stubs: {
159
- CruResource: { template: '<div><slot></slot></div>' }, // Required to render the slot content
160
- Banner: true,
161
- LabeledSelect: true,
162
- ACE: true,
163
- AgentEnv: true,
164
- ArrayList: true,
165
- ArrayListGrouped: true,
166
- BadgeState: true,
167
- Checkbox: true,
168
- ClusterMembershipEditor: true,
169
- DrainOptions: true,
170
- LabeledInput: true,
171
- Labels: true,
172
- Loading: true,
173
- MachinePool: true,
174
- MatchExpressions: true,
175
- NameNsDescription: true,
176
- Questions: true,
177
- RadioGroup: true,
178
- RegistryConfigs: true,
179
- RegistryMirrors: true,
180
- S3Config: true,
181
- SelectCredential: true,
182
- SelectOrCreateAuthSecret: true,
183
- Tab: true,
184
- Tabbed: true,
185
- UnitInput: true,
186
- YamlEditor: true,
187
- }
157
+ stubs: defaultStubs
188
158
  });
189
159
 
190
160
  const select = wrapper.find('[data-testid="rke2-custom-edit-psa"]');
@@ -206,25 +176,15 @@ describe('component: rke2', () => {
206
176
  value: {
207
177
  agentConfig: { profile: cis },
208
178
  spec: {
209
- rkeConfig: { etcd: { disableSnapshots: false } },
210
- chartValues: {},
179
+ ...defaultSpec,
211
180
  defaultPodSecurityAdmissionConfigurationTemplateName: label,
212
181
  kubernetesVersion: k8s
213
182
  }
214
183
  },
215
184
  provider: 'custom',
216
- resource: {}
217
185
  },
218
186
  computed: {
219
- showForm() {
220
- return true;
221
- },
222
- hasMachinePools() {
223
- return false;
224
- },
225
- showk8s21LegacyWarning() {
226
- return false;
227
- },
187
+ ...defaultComputed,
228
188
  agentArgs: () => ({ profile: { options: [cis] } }),
229
189
  versionOptions: () => [
230
190
  {
@@ -236,55 +196,16 @@ describe('component: rke2', () => {
236
196
  ]
237
197
  },
238
198
  mocks: {
239
- $fetchState: { pending: false },
240
- $route: {
241
- name: 'anything',
242
- query: { AS: 'yaml' },
243
- },
199
+ ...defaultMocks,
244
200
  $store: {
245
- getters: {
246
- currentStore: () => 'current_store',
247
- 'management/schemaFor': jest.fn(),
248
- 'current_store/all': jest.fn(),
249
- 'i18n/t': jest.fn(),
250
- 'i18n/withFallback': jest.fn(),
251
- },
201
+ getters: defaultGetters,
252
202
  dispatch: {
253
203
  'management/find': jest.fn(),
254
204
  'management/findAll': () => ([option]),
255
205
  }
256
206
  },
257
207
  },
258
- stubs: {
259
- CruResource: { template: '<div><slot></slot></div>' }, // Required to render the slot content
260
- Banner: true,
261
- LabeledSelect: true,
262
- ACE: true,
263
- AgentEnv: true,
264
- ArrayList: true,
265
- ArrayListGrouped: true,
266
- BadgeState: true,
267
- Checkbox: true,
268
- ClusterMembershipEditor: true,
269
- DrainOptions: true,
270
- LabeledInput: true,
271
- Labels: true,
272
- Loading: true,
273
- MachinePool: true,
274
- MatchExpressions: true,
275
- NameNsDescription: true,
276
- Questions: true,
277
- RadioGroup: true,
278
- RegistryConfigs: true,
279
- RegistryMirrors: true,
280
- S3Config: true,
281
- SelectCredential: true,
282
- SelectOrCreateAuthSecret: true,
283
- Tab: true,
284
- Tabbed: true,
285
- UnitInput: true,
286
- YamlEditor: true,
287
- }
208
+ stubs: defaultStubs
288
209
  });
289
210
 
290
211
  wrapper.setData({ cisOverride: override });
@@ -293,4 +214,57 @@ describe('component: rke2', () => {
293
214
 
294
215
  expect((select.vm as unknown as any).disabled).toBe(disabled);
295
216
  });
217
+
218
+ it.each([
219
+ ['custom', true],
220
+ ['anything else', false] // without proper data, machine pool is always not present
221
+ ])('should allow creation of RKE2 cluster with provider %p if pool machines are missing (%p)', (provider, result) => {
222
+ const k8s = 'v1.25.0+rke2r1';
223
+ const wrapper = mount(rke2, {
224
+ propsData: {
225
+ mode: 'create',
226
+ value: {
227
+ spec: {
228
+ ...defaultSpec,
229
+ kubernetesVersion: k8s,
230
+
231
+ }
232
+ },
233
+ provider,
234
+ },
235
+ computed: defaultComputed,
236
+ mocks: {
237
+ ...defaultMocks,
238
+ $store: { getters: defaultGetters },
239
+ },
240
+ stubs: defaultStubs
241
+ });
242
+
243
+ expect(wrapper.vm.validationPassed()).toBe(result);
244
+ });
245
+
246
+ it('should allow creation of K3 clusters if pool machines are missing', () => {
247
+ const k8s = 'v1.25.0+k3s1';
248
+ const wrapper = mount(rke2, {
249
+ propsData: {
250
+ mode: 'create',
251
+ value: {
252
+ spec: {
253
+ ...defaultSpec,
254
+ kubernetesVersion: k8s
255
+ }
256
+ },
257
+ provider: 'custom'
258
+ },
259
+ data: () => ({ credentialId: 'I am authenticated' }),
260
+ computed: defaultComputed,
261
+ mocks: {
262
+ ...defaultMocks,
263
+ $store: { getters: defaultGetters },
264
+ },
265
+ stubs: defaultStubs
266
+ });
267
+
268
+ expect(wrapper.vm.validationPassed()).toBe(true);
269
+ });
296
270
  });
@@ -477,6 +477,7 @@ export default {
477
477
  :errors="errors"
478
478
  :subtypes="subTypes"
479
479
  :cancel-event="true"
480
+ :prevent-enter-submit="true"
480
481
  class="create-cluster"
481
482
  @finish="save"
482
483
  @cancel="cancel"
@@ -69,6 +69,7 @@ import S3Config from './S3Config';
69
69
  import SelectCredential from './SelectCredential';
70
70
  import AdvancedSection from '@shell/components/AdvancedSection.vue';
71
71
  import { ELEMENTAL_SCHEMA_IDS, KIND, ELEMENTAL_CLUSTER_PROVIDER } from '../../config/elemental-types';
72
+ import AgentConfiguration, { cleanAgentConfiguration } from './AgentConfiguration';
72
73
 
73
74
  const PUBLIC = 'public';
74
75
  const PRIVATE = 'private';
@@ -96,6 +97,8 @@ const NODE_TOTAL = {
96
97
  icon: 'icon-checkmark'
97
98
  }
98
99
  };
100
+ const CLUSTER_AGENT_CUSTOMIZATION = 'clusterAgentDeploymentCustomization';
101
+ const FLEET_AGENT_CUSTOMIZATION = 'fleetAgentDeploymentCustomization';
99
102
 
100
103
  export default {
101
104
  components: {
@@ -107,6 +110,7 @@ export default {
107
110
  BadgeState,
108
111
  Banner,
109
112
  Checkbox,
113
+ AgentConfiguration,
110
114
  ClusterMembershipEditor,
111
115
  CruResource,
112
116
  DrainOptions,
@@ -274,6 +278,18 @@ export default {
274
278
 
275
279
  this.userChartValues[key] = value;
276
280
  });
281
+
282
+ // Ensure we have empty models for the two agent configurations
283
+
284
+ // Cluster Agent Configuration
285
+ if ( !this.value.spec[CLUSTER_AGENT_CUSTOMIZATION]) {
286
+ set(this.value.spec, CLUSTER_AGENT_CUSTOMIZATION, {});
287
+ }
288
+
289
+ // Fleet Agent Configuration
290
+ if ( !this.value.spec[FLEET_AGENT_CUSTOMIZATION] ) {
291
+ set(this.value.spec, FLEET_AGENT_CUSTOMIZATION, {});
292
+ }
277
293
  },
278
294
 
279
295
  data() {
@@ -702,7 +718,7 @@ export default {
702
718
  },
703
719
 
704
720
  unremovedMachinePools() {
705
- return this.machinePools.filter(x => !x.remove);
721
+ return (this.machinePools || []).filter(x => !x.remove);
706
722
  },
707
723
 
708
724
  machineConfigSchema() {
@@ -1081,6 +1097,7 @@ export default {
1081
1097
  created() {
1082
1098
  this.registerBeforeHook(this.saveMachinePools, 'save-machine-pools');
1083
1099
  this.registerBeforeHook(this.setRegistryConfig, 'set-registry-config');
1100
+ this.registerBeforeHook(this.agentConfigurationCleanup, 'cleanup-agent-config');
1084
1101
  this.registerAfterHook(this.cleanupMachinePools, 'cleanup-machine-pools');
1085
1102
  this.registerAfterHook(this.saveRoleBindings, 'save-role-bindings');
1086
1103
  },
@@ -1089,6 +1106,12 @@ export default {
1089
1106
  nlToBr,
1090
1107
  set,
1091
1108
 
1109
+ agentConfigurationCleanup() {
1110
+ // Clean agent configuration objects, so we only send values when the user has configured something
1111
+ cleanAgentConfiguration(this.value.spec, CLUSTER_AGENT_CUSTOMIZATION);
1112
+ cleanAgentConfiguration(this.value.spec, FLEET_AGENT_CUSTOMIZATION);
1113
+ },
1114
+
1092
1115
  /**
1093
1116
  * set instanceNameLimit to 15 to all pool machine if truncateHostnames checkbox is clicked
1094
1117
  */
@@ -1110,6 +1133,10 @@ export default {
1110
1133
  return isRequiredVersion;
1111
1134
  },
1112
1135
 
1136
+ /**
1137
+ * Get machine pools from the cluster configuration
1138
+ * this.value.spec.rkeConfig.machinePools
1139
+ */
1113
1140
  async initMachinePools(existing) {
1114
1141
  const out = [];
1115
1142
 
@@ -1467,7 +1494,20 @@ export default {
1467
1494
  delete this.value.spec.rkeConfig.machineGlobalConfig.profile;
1468
1495
  }
1469
1496
 
1497
+ // store the current data for fleet and cluster agent so that we can re-apply it later if the save fails
1498
+ // we also have a before hook (check created() hooks) where the cleanup of the data occurs
1499
+ const clusterAgentDeploymentCustomization = JSON.parse(JSON.stringify(this.value.spec[CLUSTER_AGENT_CUSTOMIZATION]));
1500
+ const fleetAgentDeploymentCustomization = JSON.parse(JSON.stringify(this.value.spec[FLEET_AGENT_CUSTOMIZATION]));
1501
+
1470
1502
  await this.save(btnCb);
1503
+
1504
+ // comes from createEditView mixin
1505
+ // if there are any errors saving, restore the agent config data
1506
+ if (this.errors?.length) {
1507
+ // Ensure the agent configuration is set back to the values before we changed (cleaned) it
1508
+ set(this.value.spec, CLUSTER_AGENT_CUSTOMIZATION, clusterAgentDeploymentCustomization);
1509
+ set(this.value.spec, FLEET_AGENT_CUSTOMIZATION, fleetAgentDeploymentCustomization);
1510
+ }
1471
1511
  },
1472
1512
  // create a secret to reference the harvester cluster kubeconfig in rkeConfig
1473
1513
  async createKubeconfigSecret(kubeconfig = '') {
@@ -1993,6 +2033,11 @@ export default {
1993
2033
 
1994
2034
  this.previousKubernetesVersion = value;
1995
2035
  }
2036
+
2037
+ // If Harvester driver, reset cloud provider if not compatible
2038
+ if (this.isHarvesterDriver && this.mode === _CREATE && this.isHarvesterIncompatible) {
2039
+ this.setHarvesterDefaultCloudProvider();
2040
+ }
1996
2041
  }
1997
2042
  },
1998
2043
 
@@ -2084,21 +2129,21 @@ export default {
2084
2129
  class="pull-right"
2085
2130
  >
2086
2131
  <BadgeState
2087
- v-tooltip="nodeTotals.tooltip.etcd"
2132
+ v-clean-tooltip="nodeTotals.tooltip.etcd"
2088
2133
  :color="nodeTotals.color.etcd"
2089
2134
  :icon="nodeTotals.icon.etcd"
2090
2135
  :label="nodeTotals.label.etcd"
2091
2136
  class="mr-10"
2092
2137
  />
2093
2138
  <BadgeState
2094
- v-tooltip="nodeTotals.tooltip.controlPlane"
2139
+ v-clean-tooltip="nodeTotals.tooltip.controlPlane"
2095
2140
  :color="nodeTotals.color.controlPlane"
2096
2141
  :icon="nodeTotals.icon.controlPlane"
2097
2142
  :label="nodeTotals.label.controlPlane"
2098
2143
  class="mr-10"
2099
2144
  />
2100
2145
  <BadgeState
2101
- v-tooltip="nodeTotals.tooltip.worker"
2146
+ v-clean-tooltip="nodeTotals.tooltip.worker"
2102
2147
  :color="nodeTotals.color.worker"
2103
2148
  :icon="nodeTotals.icon.worker"
2104
2149
  :label="nodeTotals.label.worker"
@@ -2515,7 +2560,7 @@ export default {
2515
2560
  <h3>
2516
2561
  {{ t('cluster.rke2.address.header') }}
2517
2562
  <i
2518
- v-tooltip="t('cluster.rke2.address.tooltip')"
2563
+ v-clean-tooltip="t('cluster.rke2.address.tooltip')"
2519
2564
  class="icon icon-info"
2520
2565
  />
2521
2566
  </h3>
@@ -2807,7 +2852,7 @@ export default {
2807
2852
  <h3>
2808
2853
  {{ t('cluster.addOns.additionalManifest.title') }}
2809
2854
  <i
2810
- v-tooltip="t('cluster.addOns.additionalManifest.tooltip')"
2855
+ v-clean-tooltip="t('cluster.addOns.additionalManifest.tooltip')"
2811
2856
  class="icon icon-info"
2812
2857
  />
2813
2858
  </h3>
@@ -2821,6 +2866,30 @@ export default {
2821
2866
  </div>
2822
2867
  </Tab>
2823
2868
 
2869
+ <!-- Cluster Agent Configuration -->
2870
+ <Tab
2871
+ name="clusteragentconfig"
2872
+ label-key="cluster.agentConfig.tabs.cluster"
2873
+ >
2874
+ <AgentConfiguration
2875
+ v-model="value.spec.clusterAgentDeploymentCustomization"
2876
+ type="cluster"
2877
+ :mode="mode"
2878
+ />
2879
+ </Tab>
2880
+
2881
+ <!-- Fleet Agent Configuration -->
2882
+ <Tab
2883
+ name="fleetagentconfig"
2884
+ label-key="cluster.agentConfig.tabs.fleet"
2885
+ >
2886
+ <AgentConfiguration
2887
+ v-model="value.spec.fleetAgentDeploymentCustomization"
2888
+ type="fleet"
2889
+ :mode="mode"
2890
+ />
2891
+ </Tab>
2892
+
2824
2893
  <!-- Advanced -->
2825
2894
  <Tab
2826
2895
  v-if="haveArgInfo || agentArgs['protect-kernel-defaults']"
@@ -274,7 +274,7 @@ export default {
274
274
  >
275
275
  <template #label>
276
276
  <span
277
- v-tooltip="t('backupRestoreOperator.prune.tip')"
277
+ v-clean-tooltip="t('backupRestoreOperator.prune.tip')"
278
278
  class="text-label"
279
279
  >
280
280
  {{ t('backupRestoreOperator.prune.label') }} <i class="icon icon-info" />
@@ -290,7 +290,7 @@ export default {
290
290
  >
291
291
  <template #label>
292
292
  <label
293
- v-tooltip="t('backupRestoreOperator.deleteTimeout.tip')"
293
+ v-clean-tooltip="t('backupRestoreOperator.deleteTimeout.tip')"
294
294
  class="has-tooltip"
295
295
  >
296
296
  {{ t('backupRestoreOperator.deleteTimeout.label') }} <i class="icon icon-info" />
package/edit/service.vue CHANGED
@@ -20,12 +20,13 @@ import { Banner } from '@components/Banner';
20
20
  import Labels from '@shell/components/form/Labels';
21
21
  import HarvesterServiceAddOnConfig from '@shell/components/HarvesterServiceAddOnConfig';
22
22
  import { clone } from '@shell/utils/object';
23
- import { POD, CAPI } from '@shell/config/types';
23
+ import { POD, CAPI, HCI } from '@shell/config/types';
24
24
  import { matching } from '@shell/utils/selector';
25
25
  import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
26
26
  import { allHash } from '@shell/utils/promise';
27
27
  import { isHarvesterSatisfiesVersion } from '@shell/utils/cluster';
28
28
  import { Port } from '@shell/utils/validators/formRules';
29
+ import { HCI as HCI_LABELS_ANNOTATIONS } from '@shell/config/labels-annotations';
29
30
 
30
31
  const SESSION_AFFINITY_ACTION_VALUES = {
31
32
  NONE: 'None',
@@ -268,8 +269,9 @@ export default {
268
269
  const inStore = this.$store.getters['currentStore'](POD);
269
270
 
270
271
  const hash = {
271
- provClusters: this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER }),
272
- pods: this.$store.dispatch(`${ inStore }/findAll`, { type: POD }),
272
+ provClusters: this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER }),
273
+ pods: this.$store.dispatch(`${ inStore }/findAll`, { type: POD }),
274
+ harvesterConfigs: this.$store.dispatch(`management/findAll`, { type: HCI.HARVESTER_CONFIG }),
273
275
  };
274
276
 
275
277
  const res = await allHash(hash);
@@ -313,6 +315,23 @@ export default {
313
315
  if (ports && ports.length > 0) {
314
316
  this.value.spec.ports = this.targetPortsStrOrInt(this.value.spec.ports);
315
317
  }
318
+
319
+ if (this.showHarvesterAddOnConfig) {
320
+ const clusters = this.$store.getters['management/all'](CAPI.RANCHER_CLUSTER);
321
+ const configs = this.$store.getters['management/all'](HCI.HARVESTER_CONFIG);
322
+ const cluster = clusters.find(c => c.status.clusterName === this.currentCluster.id);
323
+
324
+ const machinePools = cluster?.spec?.rkeConfig?.machinePools || [];
325
+ const machineConfigName = machinePools[0]?.machineConfigRef?.name;
326
+ const config = configs.find(c => c.id === `fleet-default/${ machineConfigName }`);
327
+
328
+ if (config) {
329
+ const { vmNamespace, networkName } = config;
330
+
331
+ this.value.metadata.annotations[HCI_LABELS_ANNOTATIONS.CLOUD_PROVIDER_NAMESPACE] = vmNamespace;
332
+ this.value.metadata.annotations[HCI_LABELS_ANNOTATIONS.CLOUD_PROVIDER_NETWORK] = networkName;
333
+ }
334
+ }
316
335
  },
317
336
  },
318
337
  };
@@ -125,7 +125,7 @@ export default {
125
125
  if (driver.metadata.name === LONGHORN_DRIVER || provisionerOptionsDrivers.includes(driver.metadata.name)) {
126
126
  return;
127
127
  }
128
- const fallback = `${ driver.metadata.name } ${ this.t('persistentVolume.csi.drivers.suffix') }`;
128
+ const fallback = `${ driver.metadata.name } ${ this.t('persistentVolume.csi.suffix') }`;
129
129
 
130
130
  dropdownOptions.push({
131
131
  value: driver.metadata.name,