@rancher/shell 3.0.12-rc.4 → 3.0.12-rc.6

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 (159) hide show
  1. package/apis/intf/resources-api/resource-base.ts +42 -2
  2. package/apis/intf/resources-api/resource-instance.ts +101 -0
  3. package/apis/intf/resources-api/resources-api.ts +177 -23
  4. package/apis/intf/resources.ts +8 -6
  5. package/apis/resources/__tests__/resources-api-class.test.ts +309 -28
  6. package/apis/resources/resources-api-class.ts +232 -43
  7. package/assets/images/providers/traefik.png +0 -0
  8. package/assets/styles/global/_button.scss +1 -1
  9. package/assets/translations/en-us.yaml +49 -14
  10. package/chart/__tests__/rancher-monitoring-dashboards-index.test.ts +269 -0
  11. package/chart/rancher-monitoring-dashboards/index.vue +155 -0
  12. package/cloud-credential/__tests__/generic.test.ts +132 -0
  13. package/cloud-credential/generic.vue +33 -3
  14. package/components/ActionDropdownShell.vue +5 -3
  15. package/components/ButtonGroup.vue +26 -1
  16. package/components/CruResource.vue +51 -2
  17. package/components/EtcdInfoBanner.vue +2 -12
  18. package/components/GrafanaDashboard.vue +2 -8
  19. package/components/PromptRestore.vue +93 -32
  20. package/components/Questions/index.vue +1 -0
  21. package/components/Resource/Detail/Masthead/__tests__/index.test.ts +68 -14
  22. package/components/Resource/Detail/Masthead/index.vue +8 -3
  23. package/components/ResourceTable.vue +1 -0
  24. package/components/SortableTable/index.vue +4 -3
  25. package/components/Wizard.vue +14 -1
  26. package/components/__tests__/ButtonGroup.test.ts +56 -0
  27. package/components/__tests__/PromptRestore.test.ts +169 -19
  28. package/components/fleet/GitRepoAdvancedTab.vue +1 -0
  29. package/components/fleet/GitRepoMetadataTab.vue +5 -0
  30. package/components/fleet/HelmOpAppCoConfigTab.vue +4 -0
  31. package/components/fleet/HelmOpMetadataTab.vue +5 -0
  32. package/components/form/FileSelector.vue +39 -1
  33. package/components/form/PrivateRegistry.constants.ts +7 -0
  34. package/components/form/PrivateRegistry.vue +253 -18
  35. package/components/form/Security.vue +1 -1
  36. package/components/form/SelectOrCreateAuthSecret.vue +140 -17
  37. package/components/form/__tests__/FileSelector.test.ts +23 -0
  38. package/components/form/__tests__/PrivateRegistry.test.ts +463 -73
  39. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +122 -0
  40. package/components/formatter/EtcdSnapshotName.vue +73 -0
  41. package/components/formatter/Translate.vue +22 -1
  42. package/components/nav/Group.vue +5 -10
  43. package/components/nav/Header.vue +8 -1
  44. package/components/nav/TopLevelMenu.vue +1 -30
  45. package/components/nav/__tests__/Group.test.ts +61 -0
  46. package/components/templates/default.vue +7 -0
  47. package/config/features.js +1 -0
  48. package/config/labels-annotations.js +3 -0
  49. package/config/product/manager.js +6 -0
  50. package/config/secret.ts +10 -0
  51. package/config/settings.ts +6 -2
  52. package/config/table-headers.js +5 -2
  53. package/config/types.js +7 -0
  54. package/core/__tests__/plugin-products-apply.test.ts +787 -0
  55. package/core/__tests__/plugin-products-extend.test.ts +189 -0
  56. package/core/__tests__/plugin-products-helpers.test.ts +81 -78
  57. package/core/__tests__/plugin-products-new.test.ts +389 -0
  58. package/core/__tests__/plugin-products-scenarios.test.ts +980 -0
  59. package/core/__tests__/plugin-products-side-menu.test.ts +1935 -0
  60. package/core/plugin-products-base.ts +201 -93
  61. package/core/plugin-products-extending.ts +1 -1
  62. package/core/plugin-products-external.ts +380 -0
  63. package/core/plugin-products-helpers.ts +28 -24
  64. package/core/plugin-products-internal.ts +207 -0
  65. package/core/plugin-products-top-level.ts +10 -5
  66. package/core/plugin-products-type-guards.ts +17 -6
  67. package/core/plugin-products.ts +5 -7
  68. package/core/plugin-types.ts +1 -389
  69. package/core/plugin.ts +10 -11
  70. package/core/types.ts +6 -202
  71. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +52 -0
  72. package/detail/pod.vue +1 -1
  73. package/detail/provisioning.cattle.io.cluster.vue +79 -5
  74. package/dialog/RotateEncryptionKeyDialog.vue +33 -9
  75. package/dialog/__tests__/RotateEncryptionKeyDialog.test.ts +78 -0
  76. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +92 -0
  77. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +101 -0
  78. package/edit/__tests__/management.cattle.io.setting.test.ts +2 -1
  79. package/edit/auth/AuthProviderWarningBanners.vue +12 -0
  80. package/edit/auth/__tests__/AuthProviderWarningBanners.test.ts +10 -1
  81. package/edit/auth/__tests__/azuread.test.ts +1 -0
  82. package/edit/auth/__tests__/github.test.ts +1 -0
  83. package/edit/auth/__tests__/oidc.test.ts +1 -0
  84. package/edit/auth/__tests__/saml.test.ts +1 -0
  85. package/edit/auth/ldap/__tests__/index.test.ts +1 -0
  86. package/edit/compliance.cattle.io.clusterscanprofile.vue +39 -41
  87. package/edit/fleet.cattle.io.gitrepo.vue +70 -16
  88. package/edit/fleet.cattle.io.helmop.vue +51 -5
  89. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  90. package/edit/{management.cattle.io.setting.vue → management.cattle.io.setting/index.vue} +32 -9
  91. package/edit/management.cattle.io.setting/system-default-registry-pull-secrets.vue +81 -0
  92. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -12
  93. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +18 -0
  94. package/edit/provisioning.cattle.io.cluster/index.vue +1 -1
  95. package/edit/provisioning.cattle.io.cluster/rke2.vue +6 -2
  96. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +0 -1
  97. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +2 -2
  98. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +14 -55
  99. package/list/management.cattle.io.user.vue +24 -5
  100. package/list/utils/management.cattle.io.cluster.utils.ts +1 -1
  101. package/machine-config/__tests__/generic.test.ts +163 -0
  102. package/machine-config/components/EC2Networking.vue +31 -7
  103. package/machine-config/components/__tests__/EC2Networking.test.ts +38 -15
  104. package/machine-config/generic.vue +32 -5
  105. package/mixins/resource-fetch.js +1 -1
  106. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +156 -0
  107. package/models/__tests__/rke-machine.cattle.io.vmwarevspheremachinetemplate.test.ts +22 -0
  108. package/models/__tests__/secret.test.ts +68 -1
  109. package/models/management.cattle.io.cluster.js +21 -3
  110. package/models/pod.js +25 -12
  111. package/models/provisioning.cattle.io.cluster.js +59 -9
  112. package/models/rke-machine.cattle.io.vmwarevspheremachinetemplate.js +9 -0
  113. package/models/rke.cattle.io.etcdsnapshot.js +17 -9
  114. package/models/secret.js +19 -0
  115. package/models/workload.js +12 -7
  116. package/package.json +1 -1
  117. package/pages/auth/login.vue +1 -1
  118. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +569 -81
  119. package/pages/c/_cluster/apps/charts/install.vue +143 -28
  120. package/pages/c/_cluster/auth/config/index.vue +11 -4
  121. package/pages/c/_cluster/monitoring/index.vue +6 -1
  122. package/pkg/require-asset.lib.js +25 -0
  123. package/pkg/vue.config.js +7 -0
  124. package/plugins/dashboard-store/__tests__/resource-class.test.ts +84 -0
  125. package/plugins/dashboard-store/getters.js +0 -1
  126. package/plugins/dashboard-store/resource-class.js +62 -15
  127. package/plugins/steve/__tests__/steve-class-resource-api.test.ts +147 -0
  128. package/plugins/steve/steve-class.js +43 -0
  129. package/plugins/steve/subscribe.js +11 -0
  130. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +115 -0
  131. package/rancher-components/Form/LabeledInput/LabeledInput.vue +83 -3
  132. package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +30 -0
  133. package/rancher-components/Form/TextArea/__tests__/TextAreaAutoGrow.test.ts +95 -0
  134. package/rancher-components/RcButton/index.ts +1 -1
  135. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +6 -1
  136. package/store/__tests__/auth.test.ts +18 -0
  137. package/store/__tests__/features.test.ts +131 -0
  138. package/store/__tests__/growl.test.ts +374 -0
  139. package/store/__tests__/modal.test.ts +131 -0
  140. package/store/__tests__/notifications.test.ts +466 -1
  141. package/store/__tests__/slideInPanel.test.ts +88 -0
  142. package/store/__tests__/type-map.utils.test.ts +433 -0
  143. package/store/__tests__/ui-context.test.ts +255 -0
  144. package/store/auth.js +11 -1
  145. package/store/features.js +5 -0
  146. package/store/plugins.js +6 -0
  147. package/types/rancher/steve.api.ts +2 -2
  148. package/types/shell/index.d.ts +120 -0
  149. package/types/store/dashboard-store.types.ts +2 -2
  150. package/types/store/type-map.ts +262 -1
  151. package/utils/__tests__/operation-cr.test.ts +34 -0
  152. package/utils/async.ts +2 -0
  153. package/utils/grafana.js +2 -17
  154. package/utils/monitoring.js +38 -1
  155. package/utils/operation-cr.js +19 -0
  156. package/utils/require-asset.ts +7 -0
  157. package/utils/validators/__tests__/private-registry.test.ts +27 -15
  158. package/utils/validators/private-registry.ts +15 -4
  159. package/core/__tests__/plugin-products.test.ts +0 -4694
@@ -196,7 +196,7 @@ export default {
196
196
  label: this.t('fleet.helmOp.add.steps.metadata.label'),
197
197
  subtext: this.t('fleet.helmOp.add.steps.metadata.subtext'),
198
198
  descriptionKey: 'fleet.helmOp.add.steps.metadata.description',
199
- ready: this.isView || !!this.value.metadata.name,
199
+ ready: this.isView || (!!this.value.metadata.name && this.stepPathErrors('basics').length === 0),
200
200
  weight: 1
201
201
  },
202
202
  {
@@ -443,6 +443,15 @@ export default {
443
443
  this.done();
444
444
  },
445
445
 
446
+ stepPathErrors(stepName) {
447
+ // Helper is used to check which validations is for each step
448
+ const paths = this.fvFormRuleSets
449
+ .filter((rule) => rule.step === stepName)
450
+ .map((rule) => rule.path);
451
+
452
+ return this.fvGetPathErrors(paths);
453
+ },
454
+
446
455
  onSourceTypeSelect(type) {
447
456
  if (this.isSuseAppCollection) {
448
457
  return;
@@ -659,35 +668,49 @@ export default {
659
668
  },
660
669
 
661
670
  updateValidationRules(sourceType) {
671
+ const nameRule = {
672
+ step: 'basics',
673
+ path: 'metadata.name',
674
+ rules: ['subDomain'],
675
+ translationKey: 'nameNsDescription.name.label'
676
+ };
677
+
662
678
  switch (sourceType) {
663
679
  case SOURCE_TYPE.REPO:
664
- this.fvFormRuleSets = [{
680
+ this.fvFormRuleSets = [nameRule, {
681
+ step: 'chart',
665
682
  path: 'spec.helm.repo',
666
683
  rules: ['urlRepository'],
667
684
  }, {
685
+ step: 'chart',
668
686
  path: 'spec.helm.chart',
669
687
  rules: ['required'],
670
688
  }, {
689
+ step: 'chart',
671
690
  path: 'spec.helm.version',
672
691
  rules: ['semanticVersion'],
673
692
  }];
674
693
  break;
675
694
  case SOURCE_TYPE.OCI:
676
- this.fvFormRuleSets = [{
695
+ this.fvFormRuleSets = [nameRule, {
696
+ step: 'chart',
677
697
  path: 'spec.helm.repo',
678
698
  rules: ['ociRegistry'],
679
699
  },
680
700
  ...(this.isSuseAppCollection ? [{
701
+ step: 'chart',
681
702
  path: 'spec.helm.chart',
682
703
  rules: ['required'],
683
704
  }] : []),
684
705
  {
706
+ step: 'chart',
685
707
  path: 'spec.helm.version',
686
708
  rules: this.isSuseAppCollection ? ['required', 'semanticVersion'] : ['semanticVersion'],
687
709
  }];
688
710
  break;
689
711
  case SOURCE_TYPE.TARBALL:
690
- this.fvFormRuleSets = [{
712
+ this.fvFormRuleSets = [nameRule, {
713
+ step: 'chart',
691
714
  path: 'spec.helm.chart',
692
715
  rules: ['urlRepository'],
693
716
  }];
@@ -739,6 +762,26 @@ export default {
739
762
  break;
740
763
  }
741
764
  },
765
+
766
+ async beforeNext(activeStep) {
767
+ if (activeStep.name !== 'basics' || !this.isCreate) {
768
+ return;
769
+ }
770
+
771
+ await this.value.dryRunCreate({
772
+ type: this.value.type,
773
+ metadata: {
774
+ name: this.value.metadata.name,
775
+ namespace: this.value.metadata.namespace,
776
+ },
777
+ spec: {
778
+ helm: {
779
+ chart: 'placeholder',
780
+ repo: 'https://example.com',
781
+ },
782
+ }
783
+ });
784
+ },
742
785
  },
743
786
  };
744
787
  </script>
@@ -753,11 +796,12 @@ export default {
753
796
  :mode="mode"
754
797
  :resource="value"
755
798
  :subtypes="[]"
756
- :validation-passed="true"
799
+ :validation-passed="fvFormIsValid"
757
800
  :errors="errors"
758
801
  :steps="!isView ? steps : undefined"
759
802
  :finish-mode="'finish'"
760
803
  :cancel-event="true"
804
+ :before-next="beforeNext"
761
805
  class="wizard"
762
806
  data-testid="helmop-cru-resource"
763
807
  @cancel="onCancel"
@@ -773,6 +817,7 @@ export default {
773
817
  :mode="mode"
774
818
  :is-view="isView"
775
819
  data-testid="helmop-metadata-tab"
820
+ :name-rules="fvGetAndReportPathRules('metadata.name')"
776
821
  @update:value="$emit('input', $event)"
777
822
  />
778
823
  </template>
@@ -1049,6 +1094,7 @@ export default {
1049
1094
  >
1050
1095
  <HelmOpAppCoConfigTab
1051
1096
  v-bind="appCoConfigProps"
1097
+ :name-rules="fvGetAndReportPathRules('metadata.name')"
1052
1098
  data-testid="helmop-appco-edit-config-tab"
1053
1099
  v-on="appCoConfigListeners"
1054
1100
  />
@@ -150,6 +150,7 @@ export default {
150
150
  tabbed="multiple"
151
151
  :target-namespace="value.metadata.namespace"
152
152
  :source="selectedNamespaceQuestions"
153
+ :mode="mode"
153
154
  />
154
155
  </Tabbed>
155
156
  </div>
@@ -32,13 +32,14 @@ export default {
32
32
  const t = this.$store.getters['i18n/t'];
33
33
 
34
34
  return {
35
- setting: ALLOWED_SETTINGS[this.value.id],
36
- description: t(`advancedSettings.descriptions.${ this.value.id }`),
37
- editHelp: t(`advancedSettings.editHelp.${ this.value.id }`),
38
- enumOptions: [],
39
- canReset: false,
40
- errors: [],
41
- fvFormRuleSets: [],
35
+ setting: ALLOWED_SETTINGS[this.value.id],
36
+ description: t(`advancedSettings.descriptions.${ this.value.id }`),
37
+ editHelp: t(`advancedSettings.editHelp.${ this.value.id }`),
38
+ enumOptions: [],
39
+ canReset: false,
40
+ errors: [],
41
+ fvFormRuleSets: [],
42
+ customEditComponent: null
42
43
  };
43
44
  },
44
45
 
@@ -60,6 +61,14 @@ export default {
60
61
  if (isServerUrl(this.value.id) && !this.value.default) {
61
62
  this.canReset = false;
62
63
  }
64
+
65
+ let component;
66
+
67
+ try {
68
+ component = require(`@shell/edit/management.cattle.io.setting/${ this.value.id }.vue`).default;
69
+ } catch {}
70
+
71
+ this.customEditComponent = component;
63
72
  },
64
73
 
65
74
  computed: {
@@ -195,8 +204,22 @@ export default {
195
204
  :label="err"
196
205
  data-testid="setting-error-banner"
197
206
  />
198
-
199
- <div class="mt-20">
207
+ <div
208
+ v-if="customEditComponent"
209
+ class="mt-20"
210
+ >
211
+ <component
212
+ :is="customEditComponent"
213
+ :setting-value="value.value"
214
+ :default-value="value.default"
215
+ :rules="fvGetAndReportPathRules('value')"
216
+ @update:setting-value="value.value=$event"
217
+ />
218
+ </div>
219
+ <div
220
+ v-else
221
+ class="mt-20"
222
+ >
200
223
  <div v-if="setting.kind === 'enum'">
201
224
  <LabeledSelect
202
225
  v-model:value="value.value"
@@ -0,0 +1,81 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, onMounted, toRef } from 'vue';
3
+ import { useStore } from 'vuex';
4
+ import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
5
+ import { SECRET } from '@shell/config/types';
6
+ import { SECRET_TYPES } from '@shell/config/secret';
7
+
8
+ const AUTH_TYPES = [
9
+ SECRET_TYPES.DOCKER_JSON,
10
+ SECRET_TYPES.BASIC,
11
+ SECRET_TYPES.SSH,
12
+ SECRET_TYPES.RKE_AUTH_CONFIG
13
+ ];
14
+
15
+ const props = defineProps<{
16
+ settingValue: Record<string, any>;
17
+ defaultValue?: Record<string, any>;
18
+ rules?: Array<(value: any) => string | undefined>;
19
+ }>();
20
+
21
+ const emit = defineEmits(['update:settingValue']);
22
+
23
+ const store = useStore();
24
+ const settingValue = toRef(props, 'settingValue');
25
+ const defaultValue = toRef(props, 'defaultValue');
26
+ const secrets = ref<any[]>([]);
27
+
28
+ const secretOptions = computed(() => secrets.value
29
+ .filter((s) => AUTH_TYPES.includes(s._type))
30
+ .map((s) => {
31
+ const { dataPreview, subTypeDisplay, metadata } = s;
32
+
33
+ const label = subTypeDisplay && dataPreview ? `${ metadata.name } (${ subTypeDisplay }: ${ dataPreview })` : `${ metadata.name } (${ subTypeDisplay })`;
34
+
35
+ return {
36
+ label,
37
+ value: s.metadata?.name
38
+ };
39
+ })
40
+ );
41
+
42
+ const selectedSecrets = computed({
43
+ get: () => {
44
+ const sValue = settingValue.value || defaultValue.value || '';
45
+
46
+ return sValue.split(',').reduce((all: string[], secret: string) => {
47
+ if (!!secret.trim()) {
48
+ all.push(secret.trim());
49
+ }
50
+
51
+ return all;
52
+ }, []);
53
+ },
54
+ set: (neu: string[]) => {
55
+ const newSettingValue = neu?.length ? neu.join(',') : null;
56
+
57
+ emit('update:settingValue', newSettingValue);
58
+ },
59
+ });
60
+
61
+ onMounted(async() => {
62
+ const res = await store.dispatch('management/findAll', {
63
+ type: SECRET,
64
+ opt: { namespaced: 'cattle-system' },
65
+ });
66
+
67
+ secrets.value = Array.isArray(res) ? res : [];
68
+ });
69
+
70
+ </script>
71
+
72
+ <template>
73
+ <LabeledSelect
74
+ id="pull-secrets"
75
+ v-model:value="selectedSecrets"
76
+ label="Image Pull Secrets"
77
+ :options="secretOptions"
78
+ :rules="rules"
79
+ :multiple="true"
80
+ />
81
+ </template>
@@ -1,7 +1,7 @@
1
1
  <script>
2
2
  import Loading from '@shell/components/Loading';
3
3
  import LabeledSelect from '@shell/components/form/LabeledSelect';
4
- import { NORMAN, DEFAULT_WORKSPACE } from '@shell/config/types';
4
+ import { NORMAN } from '@shell/config/types';
5
5
  import CreateEditView from '@shell/mixins/create-edit-view';
6
6
  import CruResource from '@shell/components/CruResource';
7
7
  import NameNsDescription from '@shell/components/form/NameNsDescription';
@@ -50,11 +50,8 @@ export default {
50
50
  const field = this.$store.getters['plugins/credentialFieldForDriver'](this.driverName);
51
51
 
52
52
  this.newCredential = await this.$store.dispatch('rancher/create', {
53
- type: NORMAN.CLOUD_CREDENTIAL,
54
- metadata: {
55
- namespace: DEFAULT_WORKSPACE,
56
- annotations: { [CAPI.CREDENTIAL_DRIVER]: this.driverName }
57
- },
53
+ type: NORMAN.CLOUD_CREDENTIAL,
54
+ annotations: { [CAPI.CREDENTIAL_DRIVER]: this.driverName },
58
55
  [`${ field }credentialConfig`]: {}
59
56
  });
60
57
 
@@ -216,12 +213,6 @@ export default {
216
213
  }
217
214
  }
218
215
 
219
- if ( this.newCredential.metadata.name ) {
220
- delete this.newCredential.metadata.generateName;
221
- } else {
222
- this.newCredential.metadata.generateName = 'cloud-credential-';
223
- }
224
-
225
216
  try {
226
217
  const res = await this.newCredential.save();
227
218
 
@@ -806,4 +806,22 @@ describe('component: rke2', () => {
806
806
  expect(wrapper.vm.value.spec.rkeConfig.machineGlobalConfig[INGRESS_CONTROLLER]).toBe(INGRESS_NGINX);
807
807
  });
808
808
  });
809
+
810
+ describe('computed: canEditAsYaml', () => {
811
+ it('should return false when isUpstreamCAPIProvider is true', () => {
812
+ const vm = { isUpstreamCAPIProvider: true } as any;
813
+
814
+ const canEditAsYaml = rke2.computed!.canEditAsYaml.call(vm);
815
+
816
+ expect(canEditAsYaml).toBe(false);
817
+ });
818
+
819
+ it('should return true when isUpstreamCAPIProvider is false', () => {
820
+ const vm = { isUpstreamCAPIProvider: false } as any;
821
+
822
+ const canEditAsYaml = rke2.computed!.canEditAsYaml.call(vm);
823
+
824
+ expect(canEditAsYaml).toBe(true);
825
+ });
826
+ });
809
827
  });
@@ -87,7 +87,7 @@ export default {
87
87
  await this.$store.dispatch('management/watch', { type: MANAGEMENT.CLUSTER, registerType: true });
88
88
  await this.$store.dispatch('management/watch', { type: CAPI.RANCHER_CLUSTER, registerType: true });
89
89
  } else {
90
- hash.mgmtClusters = this.value.waitForMgmt();
90
+ hash.mgmtCluster = this.value.waitForMgmt();
91
91
  }
92
92
 
93
93
  // No need to fetch charts when editing an RKE1 cluster
@@ -919,6 +919,10 @@ export default {
919
919
  return this.needCredential && !this.credentialId;
920
920
  },
921
921
 
922
+ canEditAsYaml() {
923
+ return !(this.isUpstreamCAPIProvider);
924
+ },
925
+
922
926
  overallFormValidationPassed() {
923
927
  return this.validationPassed &&
924
928
  this.fvFormIsValid &&
@@ -1118,7 +1122,6 @@ export default {
1118
1122
 
1119
1123
  this.rkeConfig.etcd.disableSnapshots = disableSnapshots;
1120
1124
  }
1121
-
1122
1125
  // Namespaces if required - this is mainly for custom provisioners via extensions that want
1123
1126
  // to allow creating their resources in a different namespace
1124
1127
  if (this.needsNamespace) {
@@ -1626,7 +1629,7 @@ export default {
1626
1629
  const isIpv6 = this.hasOnlyIpv6Pools;
1627
1630
 
1628
1631
  const flannelMasqInvalid = isIpv6 && isK3s && !flannelMasqEnabled;
1629
- const stackPrefInvalid = (isIpv6 && stackPreference !== STACK_PREFS.IPV6) || (isDualStack && ![STACK_PREFS.IPV6, STACK_PREFS.DUAL].includes(stackPreference));
1632
+ const stackPrefInvalid = (isIpv6 && stackPreference !== STACK_PREFS.IPV6) || (isDualStack && stackPreference !== STACK_PREFS.DUAL);
1630
1633
 
1631
1634
  const clusterCIDRInvalid = (isIpv6 || isDualStack) && !clusterCIDR.includes(':');
1632
1635
  const serviceCIDRInvalid = (isIpv6 || isDualStack) && !serviceCIDR.includes(':');
@@ -2459,6 +2462,7 @@ export default {
2459
2462
  :done-route="doneRoute"
2460
2463
  :apply-hooks="applyHooks"
2461
2464
  :generate-yaml="generateYaml"
2465
+ :can-yaml="canEditAsYaml"
2462
2466
  class="rke2"
2463
2467
  component-testid="rke2-custom-create"
2464
2468
  @done="done"
@@ -40,7 +40,6 @@ export default {
40
40
  type: Function,
41
41
  required: true,
42
42
  },
43
-
44
43
  },
45
44
 
46
45
  computed: {
@@ -120,7 +120,7 @@ export default {
120
120
 
121
121
  stackPreference: {
122
122
  get() {
123
- return this.localValue.spec?.networking?.stackPreference || STACK_PREFS.IPV4;
123
+ return this.localValue.spec?.networking?.stackPreference || STACK_PREFS.DUAL;
124
124
  },
125
125
  set(neu) {
126
126
  if (!this.localValue.spec.networking) {
@@ -295,7 +295,7 @@ export default {
295
295
  <div class="col span-3">
296
296
  <LabeledSelect
297
297
  :key="hasSomeIpv6Pools"
298
- :value="localValue?.spec?.rkeConfig?.networking?.stackPreference || STACK_PREFS.IPV4"
298
+ :value="localValue?.spec?.rkeConfig?.networking?.stackPreference || STACK_PREFS.DUAL"
299
299
  :mode="mode"
300
300
  :options="stackPreferenceOptions"
301
301
  data-testid="network-tab-stackpreferences"
@@ -1,8 +1,6 @@
1
1
  <script>
2
- import { LabeledInput } from '@components/Form/LabeledInput';
3
2
  import { Banner } from '@components/Banner';
4
- import { Checkbox } from '@components/Form/Checkbox';
5
- import SelectOrCreateAuthSecret from '@shell/components/form/SelectOrCreateAuthSecret';
3
+ import PrivateRegistry from '@shell/components/form/PrivateRegistry.vue';
6
4
  import AdvancedSection from '@shell/components/AdvancedSection.vue';
7
5
  import RegistryConfigs from '@shell/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs';
8
6
  import RegistryMirrors from '@shell/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors';
@@ -10,10 +8,8 @@ import RegistryMirrors from '@shell/edit/provisioning.cattle.io.cluster/tabs/reg
10
8
  export default {
11
9
  emits: ['custom-registry-changed', 'registry-host-changed', 'registry-secret-changed', 'input', 'update-configs-changed', 'registry-validation-changed'],
12
10
  components: {
13
- LabeledInput,
14
11
  Banner,
15
- Checkbox,
16
- SelectOrCreateAuthSecret,
12
+ PrivateRegistry,
17
13
  AdvancedSection,
18
14
  RegistryConfigs,
19
15
  RegistryMirrors
@@ -65,55 +61,18 @@ export default {
65
61
  <div class="row">
66
62
  <h3>{{ t('cluster.privateRegistry.label') }}</h3>
67
63
  </div>
68
- <div class="row">
69
- <div class="col span-12">
70
- <Banner
71
- :closable="false"
72
- class="cluster-tools-tip"
73
- color="info"
74
- label-key="cluster.privateRegistry.description"
75
- />
76
- </div>
77
- </div>
78
- <div class="row">
79
- <Checkbox
80
- :value="showCustomRegistryInput"
81
- :mode="mode"
82
- :label="t('cluster.privateRegistry.label')"
83
- data-testid="registries-enable-checkbox"
84
- @update:value="$emit('custom-registry-changed', $event)"
85
- />
86
- </div>
87
- <div
88
- v-if="showCustomRegistryInput"
89
- class="row mt-20"
90
- >
91
- <div class="col span-6">
92
- <LabeledInput
93
- :value="registryHost"
94
- :mode="mode"
95
- label-key="catalog.chart.registry.custom.inputLabel"
96
- placeholder-key="catalog.chart.registry.custom.placeholder"
97
- :min-height="30"
98
- data-testid="registry-host-input"
99
- @update:value="$emit('registry-host-changed', $event)"
100
- />
101
- <SelectOrCreateAuthSecret
102
- :value="registrySecret"
103
- :register-before-hook="registerBeforeHook"
104
- :hook-priority="1"
105
- :mode="mode"
106
- in-store="management"
107
- :allow-ssh="false"
108
- :allow-rke="true"
109
- :vertical="true"
110
- :namespace="value.metadata.namespace"
111
- generate-name="registryconfig-auth-"
112
- :cache-secrets="true"
113
- @update:value="$emit('registry-secret-changed', $event)"
114
- />
115
- </div>
116
- </div>
64
+ <PrivateRegistry
65
+ :value="registryHost"
66
+ :enabled="showCustomRegistryInput"
67
+ :mode="mode"
68
+ :pull-secret="registrySecret"
69
+ :register-before-hook="registerBeforeHook"
70
+ checkbox-test-id="registries-enable-checkbox"
71
+ input-test-id="registry-host-input"
72
+ @update:value="$emit('registry-host-changed', $event)"
73
+ @update:enabled="$emit('custom-registry-changed', $event)"
74
+ @update:pull-secret="$emit('registry-secret-changed', $event)"
75
+ />
117
76
  <div
118
77
  class="row"
119
78
  >
@@ -8,6 +8,8 @@ import ResourceFetch from '@shell/mixins/resource-fetch';
8
8
  import { isAdminUser } from '@shell/store/type-map';
9
9
  import TableDataUserIcon from '@shell/components/TableDataUserIcon';
10
10
  import { RcButton } from '@components/RcButton';
11
+ import { allHash } from '@shell/utils/promise';
12
+ import Loading from '@shell/components/Loading.vue';
11
13
 
12
14
  export default {
13
15
  components: {
@@ -15,7 +17,8 @@ export default {
15
17
  ResourceTable,
16
18
  Masthead,
17
19
  TableDataUserIcon,
18
- RcButton
20
+ RcButton,
21
+ Loading
19
22
  },
20
23
  mixins: [ResourceFetch],
21
24
  props: {
@@ -40,9 +43,16 @@ export default {
40
43
  }
41
44
  },
42
45
  async fetch() {
43
- await this.$fetchType(this.resource);
46
+ const promises = {
47
+ resources: this.$fetchType(this.resource),
48
+ localProviderEnabled: this.$store.dispatch('auth/getLocalProviderEnabled'),
49
+ membershipRefreshRequests: this.$store.dispatch('management/create', { type: EXT.GROUP_MEMBERSHIP_REFRESH_REQUESTS }),
50
+ };
51
+
52
+ const res = await allHash(promises);
44
53
 
45
- this.membershipRefreshRequests = await this.$store.dispatch('management/create', { type: EXT.GROUP_MEMBERSHIP_REFRESH_REQUESTS });
54
+ this.localProviderEnabled = res.localProviderEnabled;
55
+ this.membershipRefreshRequests = res.membershipRefreshRequests;
46
56
  this.canRefreshMemberships = !!this.membershipRefreshRequests?.canRefreshMemberships;
47
57
  },
48
58
 
@@ -87,7 +97,14 @@ export default {
87
97
 
88
98
  isAdmin() {
89
99
  return isAdminUser(this.$store.getters);
90
- }
100
+ },
101
+
102
+ canCreateUsers() {
103
+ const userCan = !!this.schema?.collectionMethods?.find((x) => x.toLowerCase() === 'post');
104
+ const systemCan = this.localProviderEnabled;
105
+
106
+ return userCan && systemCan;
107
+ },
91
108
  },
92
109
 
93
110
  methods: {
@@ -107,13 +124,15 @@ export default {
107
124
  </script>
108
125
 
109
126
  <template>
110
- <div>
127
+ <Loading v-if="$fetchState.pending" />
128
+ <div v-else>
111
129
  <Masthead
112
130
  :schema="schema"
113
131
  :resource="resource"
114
132
  :show-incremental-loading-indicator="incrementalLoadingIndicator"
115
133
  :load-resources="loadResources"
116
134
  :load-indeterminate="loadIndeterminate"
135
+ :is-creatable="canCreateUsers"
117
136
  >
118
137
  <template #extraActions>
119
138
  <AsyncButton
@@ -31,7 +31,7 @@ class ManagementClusterUtils {
31
31
  }
32
32
 
33
33
  const existingFilters = pagination.filters;
34
- const requiredFilters = paginationFilterClusters($store, false);
34
+ const requiredFilters = paginationFilterClusters($store);
35
35
 
36
36
  for (let i = 0; i < requiredFilters.length; i++) {
37
37
  let found = false;