@rancher/shell 2.0.1 → 2.0.2-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/assets/translations/en-us.yaml +51 -26
  2. package/assets/translations/zh-hans.yaml +1 -0
  3. package/components/AssignTo.vue +2 -0
  4. package/components/Questions/index.vue +2 -2
  5. package/components/auth/RoleDetailEdit.vue +5 -4
  6. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  7. package/components/form/ProjectMemberEditor.vue +1 -1
  8. package/components/form/ResourceLabeledSelect.vue +11 -3
  9. package/components/form/labeled-select-utils/labeled-select.utils.ts +1 -1
  10. package/config/pagination-table-headers.js +5 -4
  11. package/config/roles.ts +34 -19
  12. package/config/router/navigation-guards/attempt-first-login.js +1 -1
  13. package/config/router/navigation-guards/authentication.js +1 -1
  14. package/config/router/navigation-guards/i18n.js +1 -1
  15. package/config/router/navigation-guards/index.js +2 -1
  16. package/config/router/navigation-guards/load-initial-settings.js +1 -1
  17. package/config/router/navigation-guards/runtime-extension-route.js +31 -0
  18. package/config/router/routes.js +10 -1
  19. package/config/uiplugins.js +130 -61
  20. package/core/plugin.ts +5 -0
  21. package/core/plugins.js +7 -1
  22. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +86 -13
  23. package/edit/provisioning.cattle.io.cluster/__tests__/DirectoryConfig.test.ts +3 -134
  24. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +209 -0
  25. package/edit/provisioning.cattle.io.cluster/index.vue +8 -4
  26. package/edit/provisioning.cattle.io.cluster/rke2.vue +115 -17
  27. package/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest.vue +50 -0
  28. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +29 -64
  29. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +42 -3
  30. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +22 -86
  31. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +8 -2
  32. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +61 -0
  33. package/initialize/entry-helpers.js +4 -21
  34. package/mixins/__tests__/chart.test.ts +4 -1
  35. package/mixins/chart.js +30 -14
  36. package/models/__tests__/apps.deployment.test.ts +93 -0
  37. package/models/apps.deployment.js +18 -4
  38. package/models/management.cattle.io.cluster.js +2 -2
  39. package/models/management.cattle.io.user.js +3 -3
  40. package/models/nodedriver.js +5 -0
  41. package/models/provisioning.cattle.io.cluster.js +4 -0
  42. package/package.json +1 -1
  43. package/pages/404.vue +15 -0
  44. package/pages/auth/login.vue +4 -1
  45. package/pages/auth/setup.vue +4 -1
  46. package/pages/c/_cluster/explorer/index.vue +5 -0
  47. package/pages/c/_cluster/manager/jwt.authentication/index.vue +10 -4
  48. package/pages/c/_cluster/settings/performance.vue +2 -2
  49. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +7 -10
  50. package/pages/c/_cluster/uiplugins/index.vue +24 -16
  51. package/pages/home.vue +1 -13
  52. package/plugins/dashboard-store/actions.js +1 -1
  53. package/plugins/dashboard-store/getters.js +1 -1
  54. package/plugins/steve/__tests__/getters.test.ts +5 -5
  55. package/plugins/steve/getters.js +6 -4
  56. package/plugins/steve/hybrid-class.js +1 -5
  57. package/scripts/extension/helm/charts/ui-plugin-server/Chart.yaml +1 -1
  58. package/scripts/publish-shell.sh +53 -55
  59. package/scripts/test-plugins-build.sh +45 -39
  60. package/shell/types/shell/index.d.ts +2 -0
  61. package/store/type-map.js +4 -2
  62. package/types/store/pagination.types.ts +1 -1
  63. package/utils/cluster.js +9 -0
  64. package/utils/settings.ts +3 -1
  65. package/creators/app/app.package.json +0 -14
  66. package/creators/app/files/.eslintignore +0 -16
  67. package/creators/app/files/.eslintrc.js +0 -173
  68. package/creators/app/files/.gitignore +0 -70
  69. package/creators/app/files/.gitlab-ci.yml +0 -14
  70. package/creators/app/files/.vscode/settings.json +0 -21
  71. package/creators/app/files/babel.config.js +0 -1
  72. package/creators/app/files/tsconfig.json +0 -42
  73. package/creators/app/files/vue.config.js +0 -6
  74. package/creators/app/init +0 -120
  75. package/creators/app/package.json +0 -25
  76. package/creators/pkg/files/.github/workflows/build-extension-catalog.yml +0 -24
  77. package/creators/pkg/files/.github/workflows/build-extension-charts.yml +0 -22
  78. package/creators/pkg/files/babel.config.js +0 -1
  79. package/creators/pkg/files/index.ts +0 -14
  80. package/creators/pkg/files/tsconfig.json +0 -53
  81. package/creators/pkg/files/vue.config.js +0 -1
  82. package/creators/pkg/init +0 -286
  83. package/creators/pkg/package.json +0 -19
  84. package/creators/pkg/pkg.package.json +0 -21
  85. package/creators/pkg/vue-shim.ts +0 -4
  86. package/creators/update/init +0 -56
  87. package/creators/update/package.json +0 -20
  88. package/creators/update/upgrade +0 -56
  89. package/types/shell/index.d.ts +0 -4585
@@ -22,6 +22,7 @@ const defaultStubs = {
22
22
  BadgeState: true,
23
23
  Checkbox: true,
24
24
  ClusterMembershipEditor: true,
25
+ ClusterAppearance: true,
25
26
  DrainOptions: true,
26
27
  LabeledInput: true,
27
28
  Labels: true,
@@ -53,11 +54,23 @@ const defaultStubs = {
53
54
  const mockAgentArgs = { 'cloud-provider-name': { options: [], profile: { options: [{ anything: 'yes' }] } } };
54
55
 
55
56
  const defaultComputed = {
57
+ appsOSWarning() {
58
+ return false;
59
+ },
56
60
  showForm() {
57
61
  return true;
58
62
  },
59
63
  versionOptions() {
60
64
  return [
65
+ {
66
+ id: 'v1.31.0+rke2r1', value: 'v1.31.0+rke2r1', serverArgs: {}, agentArgs: mockAgentArgs, charts: {}
67
+ },
68
+ {
69
+ id: 'v1.30.0+rke2r1', value: 'v1.30.0+rke2r1', serverArgs: {}, agentArgs: mockAgentArgs, charts: {}
70
+ },
71
+ {
72
+ id: 'v1.29.1+rke2r1', value: 'v1.29.1+rke2r1', serverArgs: {}, agentArgs: mockAgentArgs, charts: {}
73
+ },
61
74
  {
62
75
  id: 'v1.25.0+rke2r1', value: 'v1.25.0+rke2r1', serverArgs: {}, agentArgs: mockAgentArgs, charts: {}
63
76
  },
@@ -332,4 +345,200 @@ describe('component: rke2', () => {
332
345
  expect(agent.element).toBeDefined();
333
346
  });
334
347
  });
348
+
349
+ it.each([
350
+ ['v1.25.0+k3s1', [{ value: 'aws' }, { value: 'azure' }], 'azure', true],
351
+ ['v1.31.0+k3s1', [{ value: 'aws' }, { value: 'azure' }], 'harvester', true],
352
+ ['v1.29.0+k3s1', [{ value: 'aws' }, { value: 'azure' }], 'harvester', false],
353
+ ['v1.31.0+k3s1', [{ value: 'aws' }], 'azure', false],
354
+ ])('should set isAzureProviderUnsupported', (k8s, providerOptions, cloudProvider, value) => {
355
+ const wrapper = mount(rke2, {
356
+ propsData: {
357
+ mode: _CREATE,
358
+ value: {
359
+ spec: {
360
+ ...defaultSpec,
361
+ kubernetesVersion: k8s
362
+ },
363
+ agentConfig: { 'cloud-provider-name': cloudProvider }
364
+ },
365
+ provider: 'custom'
366
+ },
367
+ data: () => ({}),
368
+ computed: {
369
+ ...defaultComputed,
370
+ cloudProviderOptions: () => providerOptions
371
+ },
372
+ mocks: {
373
+ ...defaultMocks,
374
+ $store: { dispatch: () => jest.fn(), getters: defaultGetters },
375
+ },
376
+ stubs: defaultStubs
377
+ });
378
+
379
+ expect((wrapper.vm as any).isAzureProviderUnsupported).toBe(value);
380
+ });
381
+
382
+ it.each([
383
+ ['edit', 'v1.31.0+k3s1', 'azure', false],
384
+ ['edit', 'v1.26.0+k3s1', 'azure', false],
385
+ ['edit', 'v1.28.0+k3s1', 'harvester', false],
386
+ ['edit', 'v1.28.0+k3s1', 'azure', true],
387
+ ['create', 'v1.28.0+k3s1', 'azure', false],
388
+ ['view', 'v1.28.0+k3s1', 'azure', false],
389
+ ])('should set canAzureMigrateOnEdit', (mode, k8s, liveCloudProvider, value) => {
390
+ const wrapper = mount(rke2, {
391
+ propsData: {
392
+ mode,
393
+ liveValue: {
394
+ spec: {
395
+ ...defaultSpec,
396
+ kubernetesVersion: k8s
397
+ },
398
+ agentConfig: { 'cloud-provider-name': liveCloudProvider }
399
+ },
400
+ value: {
401
+ spec: {
402
+ ...defaultSpec,
403
+ kubernetesVersion: k8s
404
+ },
405
+ agentConfig: { 'cloud-provider-name': liveCloudProvider }
406
+ },
407
+ provider: 'custom'
408
+ },
409
+ data: () => ({}),
410
+ computed: defaultComputed,
411
+ mocks: {
412
+ ...defaultMocks,
413
+ $store: { dispatch: () => jest.fn(), getters: defaultGetters },
414
+ },
415
+ stubs: defaultStubs
416
+ });
417
+
418
+ expect((wrapper.vm as any).canAzureMigrateOnEdit).toBe(value);
419
+ });
420
+
421
+ it.each([
422
+ ['', 'v1.32.0+rke2r1', 'amazon', 'v1.32.0+rke2r1'],
423
+ ['', 'v1.29.0+rke2r1', 'amazon', 'v1.29.0+rke2r1'],
424
+ ['', 'v1.29.0+rke2r1', 'azure', 'v1.29.0+rke2r1'],
425
+ ['not', 'v1.31.0+rke2r1', 'azure', undefined],
426
+ ])('should %p include version %p if Cloud Provider is %p', async(_, k8s, liveCloudProvider, value) => {
427
+ const wrapper = mount(rke2, {
428
+ propsData: {
429
+ mode: 'create',
430
+ value: {
431
+ spec: {
432
+ ...defaultSpec,
433
+ kubernetesVersion: k8s
434
+ },
435
+ agentConfig: { 'cloud-provider-name': liveCloudProvider }
436
+ },
437
+ provider: 'custom'
438
+ },
439
+ data: () => ({}),
440
+ computed: {
441
+ appsOSWarning: () => false,
442
+ showForm: () => false,
443
+ },
444
+ mocks: {
445
+ ...defaultMocks,
446
+ $store: { dispatch: () => jest.fn(), getters: defaultGetters },
447
+ },
448
+ stubs: defaultStubs
449
+ });
450
+
451
+ wrapper.setData({
452
+ rke2Versions: [{
453
+ id: k8s,
454
+ version: k8s,
455
+ serverArgs: true
456
+ }]
457
+ });
458
+
459
+ expect((wrapper.vm as any).versionOptions[0]?.value).toBe(value);
460
+ });
461
+
462
+ it.each([
463
+ ['enable', 'v1.28.0+rke2r1', false],
464
+ ['disable', 'v1.32.0+rke2r1', true],
465
+ ])('should %p Azure provider option if version is %p', async(_, k8s, value) => {
466
+ const wrapper = mount(rke2, {
467
+ propsData: {
468
+ mode: 'create',
469
+ value: {
470
+ spec: {
471
+ ...defaultSpec,
472
+ kubernetesVersion: k8s
473
+ },
474
+ agentConfig: { 'cloud-provider-name': 'azure' }
475
+ },
476
+ provider: 'custom'
477
+ },
478
+ data: () => ({
479
+ agentArgs: {
480
+ 'cloud-provider-name': {
481
+ options: [
482
+ 'azure',
483
+ 'amazon'
484
+ ]
485
+ }
486
+ }
487
+ }),
488
+ computed: defaultComputed,
489
+ mocks: {
490
+ ...defaultMocks,
491
+ $store: { dispatch: () => jest.fn(), getters: defaultGetters },
492
+ },
493
+ stubs: defaultStubs
494
+ });
495
+
496
+ const azureOption = (wrapper.vm as any).cloudProviderOptions.find((o: any) => o.value === 'azure');
497
+
498
+ expect(azureOption.disabled).toBe(value);
499
+ });
500
+
501
+ it.each([
502
+ ['enable', 'azure', 'v1.28.0+rke2r1', false], // azure provider / current
503
+ ['enable', 'external', 'v1.28.0+rke2r1', false], // external provider
504
+ ['enable', 'azure', 'v1.26.0+rke2r1', false], // version mismatch
505
+ ['disable', 'amazon', 'v1.26.0+rke2r1', true],
506
+ ['enable', '', 'v1.28.0+rke2r1', true], // default provider
507
+ ])('should %p provider option %p in edit mode if live provider is Azure and 1.27 <= k8s < 1.30', async(_, cloudProvider, k8s, value) => {
508
+ const wrapper = mount(rke2, {
509
+ propsData: {
510
+ mode: 'edit',
511
+ value: {
512
+ spec: {
513
+ ...defaultSpec,
514
+ kubernetesVersion: k8s
515
+ },
516
+ agentConfig: { 'cloud-provider-name': 'azure' }
517
+ },
518
+ provider: 'custom'
519
+ },
520
+ data: () => ({
521
+ canAzureMigrateOnEdit: true,
522
+ agentArgs: {
523
+ 'cloud-provider-name': {
524
+ options: [
525
+ 'azure',
526
+ 'amazon',
527
+ 'external'
528
+ ]
529
+ }
530
+ }
531
+ }),
532
+ computed: defaultComputed,
533
+ mocks: {
534
+ ...defaultMocks,
535
+ $store: { dispatch: () => jest.fn(), getters: defaultGetters },
536
+ },
537
+ stubs: defaultStubs
538
+ });
539
+
540
+ const azureOption = (wrapper.vm as any).cloudProviderOptions.find((o: any) => o.value === cloudProvider);
541
+
542
+ expect(azureOption.disabled).toBe(value);
543
+ });
335
544
  });
@@ -330,6 +330,14 @@ export default {
330
330
  });
331
331
  });
332
332
 
333
+ if (isElementalActive) {
334
+ // !this.subType means we are on the /create screen - we only want to show for rke2
335
+ // if a subType is selected, always add the ELEMENTAL_CLUSTER_PROVIDER type to cover edit scenarios
336
+ if ((!this.subType && !this.isRke1) || this.subType) {
337
+ addType(this.$plugin, ELEMENTAL_CLUSTER_PROVIDER, 'custom2', false);
338
+ }
339
+ }
340
+
333
341
  if (this.isRke1 ) {
334
342
  machineTypes.forEach((type) => {
335
343
  const id = type.spec.displayName || type.id;
@@ -346,10 +354,6 @@ export default {
346
354
  });
347
355
 
348
356
  addType(this.$plugin, 'custom', 'custom2', false);
349
-
350
- if (isElementalActive) {
351
- addType(this.$plugin, ELEMENTAL_CLUSTER_PROVIDER, 'custom2', false);
352
- }
353
357
  }
354
358
 
355
359
  // Add from extensions
@@ -29,7 +29,7 @@ import { sortBy } from '@shell/utils/sort';
29
29
  import { vspherePoolConfigMerge } from '@shell/machine-config/vmwarevsphere-pool-config-merge';
30
30
 
31
31
  import { compare, sortable } from '@shell/utils/version';
32
- import { isHarvesterSatisfiesVersion } from '@shell/utils/cluster';
32
+ import { isHarvesterSatisfiesVersion, labelForAddon } from '@shell/utils/cluster';
33
33
 
34
34
  import { BadgeState } from '@components/BadgeState';
35
35
  import { Banner } from '@components/Banner';
@@ -42,6 +42,7 @@ import Tabbed from '@shell/components/Tabbed';
42
42
  import { canViewClusterMembershipEditor } from '@shell/components/form/Members/ClusterMembershipEditor';
43
43
  import semver from 'semver';
44
44
 
45
+ import { CLOUD_CREDENTIAL_OVERRIDE } from '@shell/models/nodedriver';
45
46
  import { SETTING } from '@shell/config/settings';
46
47
  import { base64Encode } from '@shell/utils/crypto';
47
48
  import { CAPI as CAPI_ANNOTATIONS, CLUSTER_BADGE } from '@shell/config/labels-annotations';
@@ -62,6 +63,7 @@ import Registries from '@shell/edit/provisioning.cattle.io.cluster/tabs/registri
62
63
  import AddOnConfig from '@shell/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig';
63
64
  import Advanced from '@shell/edit/provisioning.cattle.io.cluster/tabs/Advanced';
64
65
  import ClusterAppearance from '@shell/components/form/ClusterAppearance';
66
+ import AddOnAdditionalManifest from '@shell/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest';
65
67
 
66
68
  const HARVESTER = 'harvester';
67
69
  const HARVESTER_CLOUD_PROVIDER = 'harvester-cloud-provider';
@@ -89,6 +91,8 @@ const NODE_TOTAL = {
89
91
  const CLUSTER_AGENT_CUSTOMIZATION = 'clusterAgentDeploymentCustomization';
90
92
  const FLEET_AGENT_CUSTOMIZATION = 'fleetAgentDeploymentCustomization';
91
93
 
94
+ const isAzureK8sUnsupported = (version) => semver.gte(version, '1.30.0');
95
+
92
96
  export default {
93
97
  components: {
94
98
  AgentEnv,
@@ -111,7 +115,8 @@ export default {
111
115
  Registries,
112
116
  AddOnConfig,
113
117
  Advanced,
114
- ClusterAppearance
118
+ ClusterAppearance,
119
+ AddOnAdditionalManifest
115
120
  },
116
121
 
117
122
  mixins: [CreateEditView, FormValidation],
@@ -171,6 +176,7 @@ export default {
171
176
  });
172
177
  }
173
178
 
179
+ // default for dataDirectories configuration obj
174
180
  if (!this.value.spec.rkeConfig.dataDirectories) {
175
181
  set(this.value.spec.rkeConfig, 'dataDirectories', {
176
182
  systemAgent: '',
@@ -179,6 +185,19 @@ export default {
179
185
  });
180
186
  }
181
187
 
188
+ // default for dataDirectories configuration systemAgent config
189
+ if (!this.value.spec.rkeConfig.dataDirectories.systemAgent) {
190
+ set(this.value.spec.rkeConfig.dataDirectories, 'systemAgent', '');
191
+ }
192
+ // default for dataDirectories configuration provisioning config
193
+ if (!this.value.spec.rkeConfig.dataDirectories.provisioning) {
194
+ set(this.value.spec.rkeConfig.dataDirectories, 'provisioning', '');
195
+ }
196
+ // default for dataDirectories configuration k8sDistro config
197
+ if (!this.value.spec.rkeConfig.dataDirectories.k8sDistro) {
198
+ set(this.value.spec.rkeConfig.dataDirectories, 'k8sDistro', '');
199
+ }
200
+
182
201
  if (!this.value.spec.rkeConfig.machineGlobalConfig) {
183
202
  set(this.value.spec, 'rkeConfig.machineGlobalConfig', {});
184
203
  }
@@ -230,6 +249,7 @@ export default {
230
249
  machinePoolErrors: {},
231
250
  allNamespaces: [],
232
251
  extensionTabs: getApplicableExtensionEnhancements(this, ExtensionPoint.TAB, TabLocation.CLUSTER_CREATE_RKE2, this.$route, this),
252
+ labelForAddon
233
253
  };
234
254
  },
235
255
 
@@ -297,6 +317,7 @@ export default {
297
317
  const cur = this.liveValue?.spec?.kubernetesVersion || '';
298
318
  const existingRke2 = this.mode === _EDIT && cur.includes('rke2');
299
319
  const existingK3s = this.mode === _EDIT && cur.includes('k3s');
320
+ const isAzure = this.agentConfig?.['cloud-provider-name'] === 'azure';
300
321
 
301
322
  let allValidRke2Versions = this.getAllOptionsAfterCurrentVersion(this.rke2Versions, (existingRke2 ? cur : null), this.defaultRke2);
302
323
  let allValidK3sVersions = this.getAllOptionsAfterCurrentVersion(this.k3sVersions, (existingK3s ? cur : null), this.defaultK3s);
@@ -309,6 +330,11 @@ export default {
309
330
  allValidK3sVersions = this.filterOutDeprecatedPatchVersions(allValidK3sVersions, cur);
310
331
  }
311
332
 
333
+ if (isAzure) {
334
+ allValidRke2Versions = allValidRke2Versions.filter((v) => !isAzureK8sUnsupported(v.value));
335
+ allValidK3sVersions = allValidK3sVersions.filter((v) => !isAzureK8sUnsupported(v.value));
336
+ }
337
+
312
338
  const showRke2 = allValidRke2Versions.length && !existingK3s;
313
339
  const showK3s = allValidK3sVersions.length && !existingRke2;
314
340
  const out = [];
@@ -356,7 +382,7 @@ export default {
356
382
  // https://github.com/rancher/dashboard/issues/10338
357
383
  // there's an update loop on refresh that might include 'none'
358
384
  // multiple times... Prevent that
359
- if (out.serverArgs?.cni?.options && !out.serverArgs?.cni?.options.includes('none')) {
385
+ if (out?.serverArgs?.cni?.options && !out.serverArgs?.cni?.options.includes('none')) {
360
386
  out.serverArgs.cni.options.push('none');
361
387
  }
362
388
 
@@ -385,11 +411,22 @@ export default {
385
411
  },
386
412
 
387
413
  needCredential() {
388
- if (this.provider === 'custom' || this.provider === 'import' || this.isElementalCluster || this.mode === _VIEW || (this.providerConfig?.spec?.builtin === false && this.providerConfig?.spec?.addCloudCredential === false)) {
414
+ // Check non-provider specific config
415
+ if (
416
+ this.provider === 'custom' ||
417
+ this.provider === 'import' ||
418
+ this.isElementalCluster || // Elemental cluster can make use of `cloud-credential`: false
419
+ this.mode === _VIEW
420
+ ) {
389
421
  return false;
390
422
  }
391
423
 
392
- if (this.customCredentialComponentRequired === false) {
424
+ // Check provider specific config
425
+ if (this.cloudCredentialsOverride === true || this.cloudCredentialsOverride === false) {
426
+ return this.cloudCredentialsOverride;
427
+ }
428
+
429
+ if (this.providerConfig?.spec?.builtin === false && this.providerConfig?.spec?.addCloudCredential === false) {
393
430
  return false;
394
431
  }
395
432
 
@@ -397,10 +434,21 @@ export default {
397
434
  },
398
435
 
399
436
  /**
400
- * Only for extensions - extension can register a 'false' cloud credential to indicate that a cloud credential is not needed
437
+ * Override the native way of determining if cloud credentials are required (builtin ++ node driver spec.addCloudCredentials)
438
+ *
439
+ * 1) Override via extensions
440
+ * - `true` or actual component - return true
441
+ * - `false` - return false
442
+ * 2) Override via hardcoded setting
401
443
  */
402
- customCredentialComponentRequired() {
403
- return this.$plugin.getDynamic('cloud-credential', this.provider);
444
+ cloudCredentialsOverride() {
445
+ const cloudCredential = this.$plugin.getDynamic('cloud-credential', this.provider);
446
+
447
+ if (cloudCredential === undefined) {
448
+ return CLOUD_CREDENTIAL_OVERRIDE[this.provider];
449
+ }
450
+
451
+ return !!cloudCredential;
404
452
  },
405
453
 
406
454
  hasMachinePools() {
@@ -580,8 +628,9 @@ export default {
580
628
 
581
629
  cloudProviderOptions() {
582
630
  const out = [{
583
- label: this.$store.getters['i18n/t']('cluster.rke2.cloudProvider.defaultValue.label'),
584
- value: '',
631
+ label: this.$store.getters['i18n/t']('cluster.rke2.cloudProvider.defaultValue.label'),
632
+ value: '',
633
+ disabled: this.canAzureMigrateOnEdit
585
634
  }];
586
635
 
587
636
  if (!!this.agentArgs['cloud-provider-name']?.options) {
@@ -593,12 +642,22 @@ export default {
593
642
  // If we have a preferred provider... only show default, preferred and external
594
643
  const isPreferred = opt === preferred;
595
644
  const isExternal = opt === 'external';
645
+ const isAzure = opt === 'azure';
646
+
596
647
  let disabled = false;
597
648
 
598
649
  if ((this.isHarvesterExternalCredential || this.isHarvesterIncompatible) && isPreferred) {
599
650
  disabled = true;
600
651
  }
601
652
 
653
+ if (isAzure && isAzureK8sUnsupported(this.value.spec.kubernetesVersion)) {
654
+ disabled = true;
655
+ }
656
+
657
+ if (!isAzure && !isExternal && this.canAzureMigrateOnEdit) {
658
+ disabled = true;
659
+ }
660
+
602
661
  if (showAllOptions || isPreferred || isExternal) {
603
662
  out.push({
604
663
  label: this.$store.getters['i18n/withFallback'](`cluster.cloudProvider."${ opt }".label`, null, opt),
@@ -618,6 +677,25 @@ export default {
618
677
  return out;
619
678
  },
620
679
 
680
+ isAzureProviderUnsupported() {
681
+ const isAzureAvailable = !!this.cloudProviderOptions.find((p) => p.value === 'azure');
682
+ const isAzureSelected = this.agentConfig['cloud-provider-name'] === 'azure';
683
+
684
+ return isAzureAvailable && (isAzureK8sUnsupported(this.value.spec.kubernetesVersion) || isAzureSelected);
685
+ },
686
+
687
+ canAzureMigrateOnEdit() {
688
+ if (!this.isEdit) {
689
+ return false;
690
+ }
691
+
692
+ const isAzureLiveProvider = this.liveValue.agentConfig['cloud-provider-name'] === 'azure';
693
+
694
+ return isAzureLiveProvider &&
695
+ semver.gte(this.liveValue?.spec?.kubernetesVersion, '1.27.0') &&
696
+ semver.lt(this.liveValue?.spec?.kubernetesVersion, '1.30.0');
697
+ },
698
+
621
699
  canManageMembers() {
622
700
  return canViewClusterMembershipEditor(this.$store);
623
701
  },
@@ -1508,7 +1586,9 @@ export default {
1508
1586
  refreshComponentWithYamls(key) {
1509
1587
  const component = this.$refs[key];
1510
1588
 
1511
- if (component) {
1589
+ if (Array.isArray(component) && component.length > 0) {
1590
+ this.refreshYamls(component[0].$refs);
1591
+ } else if (component) {
1512
1592
  this.refreshYamls(component.$refs);
1513
1593
  }
1514
1594
  },
@@ -2225,6 +2305,8 @@ export default {
2225
2305
  :show-cni="showCni"
2226
2306
  :show-cloud-provider="showCloudProvider"
2227
2307
  :cloud-provider-options="cloudProviderOptions"
2308
+ :is-azure-provider-unsupported="isAzureProviderUnsupported"
2309
+ :can-azure-migrate-on-edit="canAzureMigrateOnEdit"
2228
2310
  @cilium-values-changed="handleCiliumValuesChanged"
2229
2311
  @enabled-system-services-changed="handleEnabledSystemServicesChanged"
2230
2312
  @kubernetes-changed="handleKubernetesChange"
@@ -2309,23 +2391,39 @@ export default {
2309
2391
  />
2310
2392
  </Tab>
2311
2393
 
2312
- <!-- Add-on Config -->
2394
+ <!-- Add-on Configs -->
2313
2395
  <Tab
2314
- name="addons"
2315
- label-key="cluster.tabs.addons"
2316
- @active="showAddons('tab-addOnConfig')"
2396
+ v-for="v in addonVersions"
2397
+ :key="v.name"
2398
+ :name="v.name"
2399
+ :label="labelForAddon(v.name, false)"
2400
+ :weight="9"
2401
+ :showHeader="false"
2402
+ @active="showAddons(v.name)"
2317
2403
  >
2318
2404
  <AddOnConfig
2319
- ref="tab-addOnConfig"
2405
+ :ref="v.name"
2320
2406
  v-model="value"
2321
2407
  :mode="mode"
2322
2408
  :version-info="versionInfo"
2323
- :addon-versions="addonVersions"
2409
+ :addon-version="v"
2324
2410
  :addons-rev="addonsRev"
2325
2411
  :user-chart-values-temp="userChartValuesTemp"
2326
2412
  :init-yaml-editor="initYamlEditor"
2327
2413
  @update-questions="syncChartValues"
2328
2414
  @update-values="updateValues"
2415
+ />
2416
+ </Tab>
2417
+
2418
+ <!-- Add-on Additional Manifest -->
2419
+ <Tab
2420
+ name="additionalmanifest"
2421
+ label-key="cluster.tabs.addOnAdditionalManifest"
2422
+ :showHeader="false"
2423
+ >
2424
+ <AddOnAdditionalManifest
2425
+ :value="value"
2426
+ :mode="mode"
2329
2427
  @additional-manifest-changed="updateAdditionalManifest"
2330
2428
  />
2331
2429
  </Tab>
@@ -0,0 +1,50 @@
1
+ <script>
2
+ import YamlEditor from '@shell/components/YamlEditor';
3
+
4
+ export default {
5
+ components: { YamlEditor },
6
+
7
+ props: {
8
+ mode: {
9
+ type: String,
10
+ required: true,
11
+ },
12
+
13
+ value: {
14
+ type: Object,
15
+ required: true,
16
+ }
17
+
18
+ },
19
+
20
+ computed: {
21
+ additionalManifest: {
22
+ get() {
23
+ return this.value.spec.rkeConfig.additionalManifest;
24
+ },
25
+ set(neu) {
26
+ this.$emit('additional-manifest-changed', neu);
27
+ }
28
+ }
29
+ }
30
+ };
31
+ </script>
32
+
33
+ <template>
34
+ <div>
35
+ <h3>
36
+ {{ t('cluster.addOns.additionalManifest.title') }}
37
+ <i
38
+ v-clean-tooltip="t('cluster.addOns.additionalManifest.tooltip')"
39
+ class="icon icon-info"
40
+ />
41
+ </h3>
42
+ <YamlEditor
43
+ ref="yaml-additional"
44
+ v-model="additionalManifest"
45
+ :editor-mode="mode === 'view' ? 'VIEW_CODE' : 'EDIT_CODE'"
46
+ initial-yaml-values="# Additional Manifest YAML"
47
+ class="yaml-editor"
48
+ />
49
+ </div>
50
+ </template>