@rancher/shell 3.0.5-rc.8 → 3.0.5

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 (199) hide show
  1. package/assets/styles/base/_color.scss +4 -1
  2. package/assets/styles/global/_tooltip.scss +7 -4
  3. package/assets/styles/themes/_dark.scss +11 -0
  4. package/assets/styles/themes/_light.scss +13 -1
  5. package/assets/styles/themes/_modern.scss +22 -0
  6. package/assets/translations/en-us.yaml +147 -19
  7. package/assets/translations/zh-hans.yaml +0 -1
  8. package/chart/monitoring/grafana/index.vue +8 -2
  9. package/components/ActionMenuShell.vue +3 -1
  10. package/components/Cron/CronExpressionEditor.vue +299 -0
  11. package/components/Cron/CronExpressionEditorModal.vue +247 -0
  12. package/components/Cron/CronTooltip.vue +87 -0
  13. package/components/Cron/types.ts +13 -0
  14. package/components/ForceDirectedTreeChart/composable.ts +11 -0
  15. package/components/PodSecurityAdmission.vue +2 -0
  16. package/components/PromptModal.vue +1 -1
  17. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +1 -0
  18. package/components/Resource/Detail/CopyToClipboard.vue +78 -0
  19. package/components/Resource/Detail/FetchLoader/__tests__/composables.test.ts +69 -0
  20. package/components/Resource/Detail/FetchLoader/composables.ts +27 -0
  21. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
  22. package/components/Resource/Detail/Metadata/Annotations/index.vue +1 -1
  23. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +13 -61
  24. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +33 -6
  25. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +24 -38
  26. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +25 -5
  27. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -23
  28. package/components/Resource/Detail/Metadata/KeyValueRow.vue +144 -0
  29. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -0
  30. package/components/Resource/Detail/Metadata/Labels/index.vue +1 -0
  31. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +30 -32
  32. package/components/Resource/Detail/Metadata/__tests__/KeyValueRow.test.ts +108 -0
  33. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +0 -3
  34. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +12 -5
  35. package/components/Resource/Detail/Metadata/composables.ts +1 -4
  36. package/components/Resource/Detail/Metadata/index.vue +1 -0
  37. package/components/Resource/Detail/Preview/Content.vue +63 -0
  38. package/components/Resource/Detail/Preview/Preview.vue +128 -0
  39. package/components/Resource/Detail/Preview/__tests__/Content.spec.ts +71 -0
  40. package/components/Resource/Detail/Preview/__tests__/Preview.spec.ts +121 -0
  41. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +141 -0
  42. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +136 -0
  43. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +245 -0
  44. package/components/Resource/Detail/ResourcePopover/index.vue +226 -0
  45. package/components/Resource/Detail/SpacedRow.vue +1 -0
  46. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +0 -5
  47. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
  48. package/components/Resource/Detail/TitleBar/composables.ts +1 -3
  49. package/components/Resource/Detail/TitleBar/index.vue +2 -29
  50. package/components/Resource/Detail/ViewOptions/composable.ts +9 -0
  51. package/components/Resource/Detail/ViewOptions/index.vue +41 -0
  52. package/components/Resource/Detail/__tests__/CopyToClipboard.spec.ts +82 -0
  53. package/components/ResourceDetail/Masthead/legacy.vue +0 -19
  54. package/components/ResourceDetail/index.vue +1 -26
  55. package/components/ResourceTable.vue +24 -0
  56. package/components/SortableTable/index.vue +7 -1
  57. package/components/SortableTable/paging.js +3 -0
  58. package/components/Tabbed/Tab.vue +43 -1
  59. package/components/Tabbed/index.vue +3 -1
  60. package/components/__tests__/Cron/CronExpressionEditor.test.ts +151 -0
  61. package/components/__tests__/Cron/CronExpressionEditorModal.test.ts +81 -0
  62. package/components/auth/login/saml.vue +86 -0
  63. package/components/form/LabeledSelect.vue +8 -8
  64. package/components/form/ProjectMemberEditor.vue +2 -0
  65. package/components/form/ResourceTabs/composable.ts +54 -0
  66. package/components/form/ResourceTabs/index.vue +10 -7
  67. package/components/form/Select.vue +13 -10
  68. package/components/form/__tests__/LabeledSelect.test.ts +133 -0
  69. package/components/form/__tests__/Select.test.ts +134 -0
  70. package/components/nav/Header.vue +6 -5
  71. package/composables/useExtensionManager.ts +17 -0
  72. package/config/home-links.js +12 -0
  73. package/config/labels-annotations.js +0 -1
  74. package/config/page-actions.js +0 -1
  75. package/config/product/explorer.js +3 -1
  76. package/config/product/fleet.js +2 -7
  77. package/config/product/manager.js +0 -5
  78. package/config/query-params.js +1 -0
  79. package/config/router/navigation-guards/clusters.js +2 -1
  80. package/config/router/navigation-guards/products.js +1 -1
  81. package/config/store.js +2 -0
  82. package/core/extension-manager-impl.js +518 -0
  83. package/core/plugins.js +35 -468
  84. package/core/types.ts +8 -2
  85. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +1 -0
  86. package/detail/catalog.cattle.io.app.vue +7 -4
  87. package/detail/fleet.cattle.io.bundle.vue +1 -5
  88. package/detail/fleet.cattle.io.cluster.vue +3 -2
  89. package/detail/fleet.cattle.io.gitrepo.vue +76 -49
  90. package/detail/fleet.cattle.io.helmop.vue +78 -49
  91. package/dialog/AddonConfigConfirmationDialog.vue +1 -1
  92. package/dialog/GenericPrompt.vue +1 -1
  93. package/dialog/ImportDialog.vue +9 -2
  94. package/dialog/InstallExtensionDialog.vue +18 -10
  95. package/dialog/SloDialog.vue +1 -1
  96. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +2 -1
  97. package/edit/__tests__/resources.cattle.io.restore.test.ts +106 -0
  98. package/edit/auth/oidc.vue +106 -6
  99. package/edit/auth/saml.vue +5 -5
  100. package/edit/cloudcredential.vue +31 -17
  101. package/edit/constraints.gatekeeper.sh.constraint/index.vue +10 -2
  102. package/edit/fleet.cattle.io.cluster.vue +19 -0
  103. package/edit/fleet.cattle.io.gitrepo.vue +23 -16
  104. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +12 -11
  105. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +11 -1
  106. package/edit/provisioning.cattle.io.cluster/index.vue +14 -19
  107. package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -3
  108. package/edit/provisioning.cattle.io.cluster/tabs/AddOnAdditionalManifest.vue +1 -0
  109. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +1 -0
  110. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +1 -0
  111. package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +1 -0
  112. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -0
  113. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +6 -0
  114. package/edit/resources.cattle.io.restore.vue +5 -8
  115. package/initialize/install-plugins.js +1 -3
  116. package/list/__tests__/workload.test.ts +1 -0
  117. package/list/workload.vue +8 -1
  118. package/machine-config/components/GCEImage.vue +6 -5
  119. package/machine-config/google.vue +11 -6
  120. package/mixins/__tests__/auth-config.test.ts +4 -6
  121. package/mixins/__tests__/chart.test.ts +139 -1
  122. package/mixins/auth-config.js +33 -10
  123. package/mixins/chart.js +58 -18
  124. package/models/__tests__/namespace.test.ts +69 -0
  125. package/models/apps.statefulset.js +8 -10
  126. package/models/chart.js +5 -1
  127. package/models/fleet-application.js +16 -46
  128. package/models/fleet.cattle.io.bundle.js +1 -38
  129. package/models/fleet.cattle.io.gitrepo.js +4 -0
  130. package/models/fleet.cattle.io.helmop.js +4 -0
  131. package/models/management.cattle.io.cluster.js +1 -1
  132. package/models/management.cattle.io.project.js +12 -0
  133. package/models/namespace.js +30 -0
  134. package/models/workload.js +4 -1
  135. package/package.json +10 -10
  136. package/pages/auth/login.vue +8 -3
  137. package/pages/auth/logout.vue +6 -5
  138. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +26 -11
  139. package/pages/c/_cluster/apps/charts/chart.vue +29 -20
  140. package/pages/c/_cluster/apps/charts/index.vue +1 -0
  141. package/pages/c/_cluster/apps/charts/install.vue +6 -5
  142. package/pages/c/_cluster/explorer/tools/__tests__/index.test.ts +102 -12
  143. package/pages/c/_cluster/explorer/tools/index.vue +145 -254
  144. package/pages/c/_cluster/manager/cloudCredential/index.vue +18 -1
  145. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +12 -2
  146. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  147. package/pages/c/_cluster/uiplugins/__tests__/index.spec.ts +318 -0
  148. package/pages/c/_cluster/uiplugins/index.vue +221 -363
  149. package/pages/home.vue +1 -9
  150. package/plugins/axios.js +3 -2
  151. package/plugins/dashboard-store/resource-class.js +49 -0
  152. package/plugins/ember-cookie.js +7 -3
  153. package/plugins/steve/subscribe.js +4 -2
  154. package/public/index.html +2 -1
  155. package/rancher-components/Card/Card.vue +1 -1
  156. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  157. package/rancher-components/Form/Radio/RadioButton.vue +1 -1
  158. package/rancher-components/Form/Radio/RadioGroup.vue +1 -1
  159. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -11
  160. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.test.ts +53 -0
  161. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +65 -0
  162. package/rancher-components/Pill/RcCounterBadge/index.ts +1 -0
  163. package/rancher-components/Pill/RcCounterBadge/types.ts +7 -0
  164. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +1 -1
  165. package/rancher-components/Pill/RcStatusBadge/index.ts +1 -1
  166. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -3
  167. package/rancher-components/Pill/RcStatusIndicator/types.ts +1 -1
  168. package/rancher-components/Pill/RcTag/RcTag.test.ts +64 -0
  169. package/rancher-components/Pill/RcTag/RcTag.vue +94 -0
  170. package/rancher-components/Pill/RcTag/index.ts +1 -0
  171. package/rancher-components/Pill/RcTag/types.ts +9 -0
  172. package/rancher-components/Pill/types.ts +1 -0
  173. package/rancher-components/RcItemCard/RcItemCard.vue +1 -0
  174. package/rancher-components/RcItemCard/RcItemCardAction.vue +12 -0
  175. package/scripts/test-plugins-build.sh +0 -1
  176. package/store/__tests__/catalog.test.ts +63 -0
  177. package/store/__tests__/cookies.test.ts +72 -0
  178. package/store/auth.js +33 -10
  179. package/store/catalog.js +2 -2
  180. package/store/cookies.ts +30 -0
  181. package/store/prefs.js +10 -5
  182. package/store/type-map.js +3 -15
  183. package/types/extension-manager.ts +26 -0
  184. package/types/shell/index.d.ts +123 -27
  185. package/utils/__tests__/product.test.ts +129 -0
  186. package/utils/__tests__/resource.test.ts +87 -0
  187. package/utils/alertmanagerconfig.js +2 -2
  188. package/utils/auth.js +4 -77
  189. package/utils/product.ts +39 -0
  190. package/utils/resource.ts +35 -0
  191. package/utils/select.js +0 -24
  192. package/utils/validators/formRules/__tests__/index.test.ts +3 -0
  193. package/utils/validators/formRules/index.ts +2 -1
  194. package/vue.config.js +1 -1
  195. package/components/Resource/Detail/Metadata/Rectangle.vue +0 -34
  196. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +0 -24
  197. package/components/ResourceDetail/Masthead/__tests__/legacy.test.ts +0 -65
  198. package/utils/cookie-universal.js +0 -10
  199. /package/components/{ForceDirectedTreeChart.vue → ForceDirectedTreeChart/index.vue} +0 -0
@@ -1,7 +1,7 @@
1
1
  <script>
2
2
  import Loading from '@shell/components/Loading';
3
3
  import CreateEditView from '@shell/mixins/create-edit-view';
4
- import AuthConfig from '@shell/mixins/auth-config';
4
+ import AuthConfig, { SLO_OPTION_VALUES } from '@shell/mixins/auth-config';
5
5
  import CruResource from '@shell/components/CruResource';
6
6
  import AllowedPrincipals from '@shell/components/auth/AllowedPrincipals';
7
7
  import FileSelector from '@shell/components/form/FileSelector';
@@ -15,6 +15,7 @@ import { RadioGroup } from '@components/Form/Radio';
15
15
  import { Checkbox } from '@components/Form/Checkbox';
16
16
  import { BASE_SCOPES } from '@shell/store/auth';
17
17
  import CopyToClipboardText from '@shell/components/CopyToClipboardText.vue';
18
+ import isUrl from 'is-url';
18
19
 
19
20
  export default {
20
21
  components: {
@@ -33,6 +34,8 @@ export default {
33
34
  CopyToClipboardText,
34
35
  },
35
36
 
37
+ emits: ['validationChanged'],
38
+
36
39
  mixins: [CreateEditView, AuthConfig],
37
40
 
38
41
  data() {
@@ -56,7 +59,8 @@ export default {
56
59
  userInfoEndpoint: null,
57
60
  },
58
61
  // TODO #13457: this is duplicated due wrong format
59
- oidcScope: []
62
+ oidcScope: [],
63
+ SLO_OPTION_VALUES
60
64
  };
61
65
  },
62
66
 
@@ -89,6 +93,11 @@ export default {
89
93
  return false;
90
94
  }
91
95
 
96
+ // make sure that if SLO options are enabled on radio group, field "endSessionEndpoint" is required
97
+ if (this.isLogoutAllSupported && this.sloEndSessionEndpointUiEnabled && (!this.model.endSessionEndpoint || !isUrl(this.model.endSessionEndpoint))) {
98
+ return false;
99
+ }
100
+
92
101
  if (this.isAmazonCognito) {
93
102
  const { issuer } = this.model;
94
103
 
@@ -129,10 +138,36 @@ export default {
129
138
 
130
139
  isAmazonCognito() {
131
140
  return this.model?.id === 'cognito';
141
+ },
142
+
143
+ isLogoutAllSupported() {
144
+ return this.model?.logoutAllSupported;
145
+ },
146
+
147
+ sloOptions() {
148
+ return [
149
+ { value: SLO_OPTION_VALUES.rancher, label: this.t('authConfig.slo.sloOptions.onlyRancher', { name: this.model?.nameDisplay }) },
150
+ { value: SLO_OPTION_VALUES.all, label: this.t('authConfig.slo.sloOptions.logoutAll', { name: this.model?.nameDisplay }) },
151
+ { value: SLO_OPTION_VALUES.both, label: this.t('authConfig.slo.sloOptions.choose') },
152
+ ];
153
+ },
154
+
155
+ sloTypeText() {
156
+ const sloOptionSelected = this.sloOptions.find((item) => item.value === this.sloType);
157
+
158
+ return sloOptionSelected?.label || '';
159
+ },
160
+
161
+ sloEndSessionEndpointUiEnabled() {
162
+ return this.sloType === SLO_OPTION_VALUES.all || this.sloType === SLO_OPTION_VALUES.both;
132
163
  }
133
164
  },
134
165
 
135
166
  watch: {
167
+ fvFormIsValid(newValue) {
168
+ this.$emit('validationChanged', !!newValue);
169
+ },
170
+
136
171
  'oidcUrls.url'() {
137
172
  this.updateEndpoints();
138
173
  },
@@ -166,6 +201,25 @@ export default {
166
201
  if (!old && neu) {
167
202
  this.customEndpoint.value = !this.oidcUrls.url && !!this.model.issuer;
168
203
  }
204
+ },
205
+
206
+ // sloType is defined on shell/mixins/auth-config.js
207
+ sloType(neu) {
208
+ switch (neu) {
209
+ case SLO_OPTION_VALUES.rancher:
210
+ this.model.logoutAllEnabled = false;
211
+ this.model.logoutAllForced = false;
212
+ this.model.endSessionEndpoint = '';
213
+ break;
214
+ case SLO_OPTION_VALUES.all:
215
+ this.model.logoutAllEnabled = true;
216
+ this.model.logoutAllForced = true;
217
+ break;
218
+ case SLO_OPTION_VALUES.both:
219
+ this.model.logoutAllEnabled = true;
220
+ this.model.logoutAllForced = false;
221
+ break;
222
+ }
169
223
  }
170
224
  },
171
225
 
@@ -224,11 +278,19 @@ export default {
224
278
  :edit="goToEdit"
225
279
  >
226
280
  <template #rows>
227
- <tr><td>{{ t(`authConfig.oidc.rancherUrl`) }}: </td><td>{{ model.rancherUrl }}</td></tr>
228
- <tr><td>{{ t(`authConfig.oidc.clientId`) }}: </td><td>{{ model.clientId }}</td></tr>
229
- <tr><td>{{ t(`authConfig.oidc.issuer`) }}: </td><td>{{ model.issuer }}</td></tr>
281
+ <tr><td>{{ t('authConfig.oidc.rancherUrl') }}: </td><td>{{ model.rancherUrl }}</td></tr>
282
+ <tr><td>{{ t('authConfig.oidc.clientId') }}: </td><td>{{ model.clientId }}</td></tr>
283
+ <tr><td>{{ t('authConfig.oidc.issuer') }}: </td><td>{{ model.issuer }}</td></tr>
230
284
  <tr v-if="model.authEndpoint">
231
- <td>{{ t(`authConfig.oidc.authEndpoint`) }}: </td><td>{{ model.authEndpoint }}</td>
285
+ <td>{{ t('authConfig.oidc.authEndpoint') }}: </td><td>{{ model.authEndpoint }}</td>
286
+ </tr>
287
+ <tr v-if="isLogoutAllSupported">
288
+ <td>{{ t('authConfig.slo.sloTitle') }}: </td><td>{{ sloTypeText }}</td>
289
+ </tr>
290
+ <tr v-if="isLogoutAllSupported && sloEndSessionEndpointUiEnabled">
291
+ <td>
292
+ {{ t('authConfig.oidc.endSessionEndpoint.title') }}:
293
+ </td><td>{{ model.endSessionEndpoint }}</td>
232
294
  </tr>
233
295
  </template>
234
296
  </AuthBanner>
@@ -494,6 +556,44 @@ export default {
494
556
  </div>
495
557
  </div>
496
558
  </template>
559
+
560
+ <!-- SLO logout -->
561
+ <div
562
+ v-if="isLogoutAllSupported"
563
+ class="mt-40 mb-20"
564
+ >
565
+ <div class="row">
566
+ <div class="col span-12">
567
+ <h3>{{ t('authConfig.slo.sloTitle') }}</h3>
568
+ </div>
569
+ </div>
570
+ <div class="row">
571
+ <div class="col span-4">
572
+ <RadioGroup
573
+ v-model:value="sloType"
574
+ :mode="mode"
575
+ :options="sloOptions"
576
+ :disabled="!model.logoutAllSupported"
577
+ name="sloTypeRadio"
578
+ />
579
+ </div>
580
+ </div>
581
+ <div
582
+ v-if="sloEndSessionEndpointUiEnabled"
583
+ class="row mt-20"
584
+ >
585
+ <div class="col span-6">
586
+ <LabeledInput
587
+ v-model:value="model.endSessionEndpoint"
588
+ :tooltip="t('authConfig.oidc.endSessionEndpoint.tooltip')"
589
+ :label="t('authConfig.oidc.endSessionEndpoint.title')"
590
+ :mode="mode"
591
+ required
592
+ data-testid="oidc-endSessionEndpoint"
593
+ />
594
+ </div>
595
+ </div>
596
+ </div>
497
597
  </template>
498
598
  </CruResource>
499
599
  </div>
@@ -73,9 +73,9 @@ export default {
73
73
 
74
74
  sloOptions() {
75
75
  return [
76
- { value: SLO_OPTION_VALUES.rancher, label: this.t('authConfig.saml.sloOptions.onlyRancher', { name: this.model?.nameDisplay }) },
77
- { value: SLO_OPTION_VALUES.all, label: this.t('authConfig.saml.sloOptions.logoutAll', { name: this.model?.nameDisplay }) },
78
- { value: SLO_OPTION_VALUES.both, label: this.t('authConfig.saml.sloOptions.choose') },
76
+ { value: SLO_OPTION_VALUES.rancher, label: this.t('authConfig.slo.sloOptions.onlyRancher', { name: this.model?.nameDisplay }) },
77
+ { value: SLO_OPTION_VALUES.all, label: this.t('authConfig.slo.sloOptions.logoutAll', { name: this.model?.nameDisplay }) },
78
+ { value: SLO_OPTION_VALUES.both, label: this.t('authConfig.slo.sloOptions.choose') },
79
79
  ];
80
80
  },
81
81
 
@@ -175,7 +175,7 @@ export default {
175
175
  <tr><td>{{ t(`authConfig.saml.api`) }}: </td><td>{{ model.rancherApiHost }}</td></tr>
176
176
  <tr><td>{{ t(`authConfig.saml.groups`) }}: </td><td>{{ model.groupsField }}</td></tr>
177
177
  <tr v-if="isLogoutAllSupported">
178
- <td>{{ t(`authConfig.saml.sloTitle`) }}: </td><td>{{ sloTypeText }}</td>
178
+ <td>{{ t(`authConfig.slo.sloTitle`) }}: </td><td>{{ sloTypeText }}</td>
179
179
  </tr>
180
180
  </template>
181
181
 
@@ -357,7 +357,7 @@ export default {
357
357
  >
358
358
  <div class="row">
359
359
  <div class="col span-12">
360
- <h3>{{ t('authConfig.saml.sloTitle') }}</h3>
360
+ <h3>{{ t('authConfig.slo.sloTitle') }}</h3>
361
361
  </div>
362
362
  </div>
363
363
  <div class="row">
@@ -14,8 +14,6 @@ import SelectIconGrid from '@shell/components/SelectIconGrid';
14
14
  import { sortBy } from '@shell/utils/sort';
15
15
  import { ucFirst } from '@shell/utils/string';
16
16
  import { set } from '@shell/utils/object';
17
- import { mapFeature, RKE2 as RKE2_FEATURE } from '@shell/store/features';
18
- import { rke1Supports } from '@shell/store/plugins';
19
17
 
20
18
  export default {
21
19
  name: 'CruCloudCredential',
@@ -35,7 +33,7 @@ export default {
35
33
  async fetch() {
36
34
  this.nodeDrivers = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_DRIVER });
37
35
  this.kontainerDrivers = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.KONTAINER_DRIVER });
38
-
36
+ this.extensions = this.getExtensions();
39
37
  // Force reload the cloud cred schema and any missing subtypes because there aren't change events sent when drivers come/go
40
38
  try {
41
39
  const schema = await this.$store.dispatch('rancher/find', {
@@ -87,7 +85,8 @@ export default {
87
85
  credCustomComponentValidation: false,
88
86
  nameRequiredValidation: false,
89
87
  nodeDrivers: null,
90
- kontainerDrivers: null
88
+ kontainerDrivers: null,
89
+ extensions: null
91
90
  };
92
91
  },
93
92
 
@@ -98,12 +97,9 @@ export default {
98
97
  },
99
98
 
100
99
  computed: {
101
- rke2Enabled: mapFeature(RKE2_FEATURE),
102
100
 
103
101
  hasCustomCloudCredentialComponent() {
104
- const driverName = this.driverName;
105
-
106
- return this.$store.getters['type-map/hasCustomCloudCredentialComponent'](driverName);
102
+ return this.getCustomCloudCredentialComponent(this.driverName);
107
103
  },
108
104
 
109
105
  cloudCredentialComponent() {
@@ -140,15 +136,14 @@ export default {
140
136
  secretSubTypes() {
141
137
  const out = [];
142
138
 
143
- const drivers = [...this.nodeDrivers, ...this.kontainerDrivers]
139
+ const fromDrivers = [...this.nodeDrivers, ...this.kontainerDrivers]
144
140
  .filter((x) => x.spec.active && x.id !== 'rancherkubernetesengine')
145
141
  .map((x) => x.spec.displayName || x.id);
146
142
 
147
- let types = uniq(drivers.map((x) => this.$store.getters['plugins/credentialDriverFor'](x)));
143
+ const fromExtensions = this.extensions?.filter((x) => !!this.getCustomCloudCredentialComponent(x?.id)).map((x) => x?.id) || [];
144
+ const providers = [...fromDrivers, ...fromExtensions];
148
145
 
149
- if ( !this.rke2Enabled ) {
150
- types = types.filter((x) => rke1Supports.includes(x));
151
- }
146
+ let types = uniq(providers.map((x) => this.$store.getters['plugins/credentialDriverFor'](x)));
152
147
 
153
148
  const schema = this.$store.getters['rancher/schemaFor'](NORMAN.CLOUD_CREDENTIAL);
154
149
 
@@ -178,7 +173,7 @@ export default {
178
173
  for ( const id of types ) {
179
174
  let bannerAbbrv;
180
175
 
181
- let bannerImage = this.$store.app.$plugin.getDynamic('image', `providers/${ id }.svg`);
176
+ let bannerImage = this.$store.app.$extension.getDynamic('image', `providers/${ id }.svg`);
182
177
 
183
178
  if (!bannerImage) {
184
179
  try {
@@ -191,7 +186,7 @@ export default {
191
186
 
192
187
  out.push({
193
188
  id,
194
- label: this.typeDisplay(CAPI.CREDENTIAL_DRIVER, id),
189
+ label: this.typeDisplay(id),
195
190
  bannerImage,
196
191
  bannerAbbrv
197
192
  });
@@ -214,6 +209,25 @@ export default {
214
209
  this.credCustomComponentValidation = passed;
215
210
  },
216
211
 
212
+ getExtensions() {
213
+ const context = {
214
+ dispatch: this.$store.dispatch,
215
+ getters: this.$store.getters,
216
+ axios: this.$store.$axios,
217
+ $extension: this.$store.app.$extension,
218
+ t: (...args) => this.t.apply(this, args),
219
+ isCreate: this.isCreate,
220
+ isEdit: this.isEdit,
221
+ isView: this.isView,
222
+ };
223
+
224
+ return this.$extension.getProviders(context);
225
+ },
226
+
227
+ getCustomCloudCredentialComponent(driverName) {
228
+ return this.$store.getters['type-map/hasCustomCloudCredentialComponent'](driverName);
229
+ },
230
+
217
231
  async saveCredential(btnCb) {
218
232
  if ( this.errors ) {
219
233
  clear(this.errors);
@@ -266,10 +280,10 @@ export default {
266
280
  }
267
281
 
268
282
  this.value['_type'] = type;
269
- this.$emit('set-subtype', this.typeDisplay(type, driver));
283
+ this.$emit('set-subtype', this.typeDisplay(driver));
270
284
  },
271
285
 
272
- typeDisplay(type, driver) {
286
+ typeDisplay(driver) {
273
287
  return this.$store.getters['i18n/withFallback'](`cluster.provider."${ driver }"`, null, driver);
274
288
  },
275
289
 
@@ -17,6 +17,7 @@ import { saferDump } from '@shell/utils/create-yaml';
17
17
  import NamespaceList, { NAMESPACE_FILTERS_HELPER } from './NamespaceList';
18
18
  import MatchKinds from './MatchKinds';
19
19
  import Scope, { SCOPE_OPTIONS } from './Scope';
20
+ import Banner from '@components/Banner/Banner';
20
21
 
21
22
  function findConstraintTypes(schemas) {
22
23
  return schemas
@@ -43,7 +44,8 @@ export default {
43
44
  Scope,
44
45
  Tab,
45
46
  Tabbed,
46
- YamlEditor
47
+ YamlEditor,
48
+ Banner
47
49
  },
48
50
 
49
51
  mixins: [CreateEditView],
@@ -230,11 +232,17 @@ export default {
230
232
  };
231
233
  </script>
232
234
  <template>
235
+ <Banner
236
+ v-if="templateSubtypes.length === 0"
237
+ color="warning"
238
+ >
239
+ {{ t('gatekeeperConstraint.templateRequired') }}
240
+ </Banner>
233
241
  <CruResource
242
+ v-else
234
243
  :done-route="doneRoute"
235
244
  :mode="mode"
236
245
  :resource="value"
237
- :selected-subtype="value.kind"
238
246
  :subtypes="templateSubtypes"
239
247
  :validation-passed="true"
240
248
  :errors="errors"
@@ -56,10 +56,14 @@ export default {
56
56
  async save(buttonDone) {
57
57
  try {
58
58
  this.errors = [];
59
+
59
60
  await this.value.save();
60
61
 
61
62
  await this.normanCluster.save();
62
63
 
64
+ // Changes (such as labels or annotations fields) to normanCluster are reflected in the fleet cluster via Rancher services, so wait for that to occur
65
+ await this.waitForFleetClusterLastRevision();
66
+
63
67
  this.done();
64
68
  buttonDone(true);
65
69
  } catch (e) {
@@ -67,6 +71,21 @@ export default {
67
71
  buttonDone(false);
68
72
  }
69
73
  },
74
+
75
+ async waitForFleetClusterLastRevision() {
76
+ const inStore = this.$store.getters['currentProduct'].inStore;
77
+
78
+ const currRev = this.value?.metadata?.resourceVersion;
79
+
80
+ try {
81
+ return await this.value.waitForTestFn(() => {
82
+ const rev = this.$store.getters[`${ inStore }/byId`](this.value.type, this.value.id)?.metadata?.resourceVersion;
83
+
84
+ return currRev && currRev !== rev;
85
+ }, `${ this.value.id } - wait for resourceVersion to change`, 1000, 200);
86
+ } catch (e) {
87
+ }
88
+ }
70
89
  },
71
90
  };
72
91
  </script>
@@ -58,25 +58,26 @@ export default {
58
58
  mixins: [CreateEditView, FormValidation],
59
59
 
60
60
  async fetch() {
61
- let tls = _VERIFY;
61
+ this.currentUser = await this.value.getCurrentUser();
62
+ },
63
+
64
+ data() {
65
+ let tlsMode = _VERIFY;
66
+ let caBundle = null;
62
67
 
63
68
  if ( this.value.spec.insecureSkipTLSVerify ) {
64
- tls = _SKIP;
69
+ tlsMode = _SKIP;
65
70
  } else if ( this.value.spec.caBundle ) {
66
71
  try {
67
- this.caBundle = base64Decode(this.value.spec.caBundle);
68
- tls = _SPECIFY;
72
+ caBundle = base64Decode(this.value.spec.caBundle);
73
+ tlsMode = _SPECIFY;
69
74
  } catch (e) {
70
75
  // Hmm...
71
76
  }
72
77
  }
73
78
 
74
- this.tlsMode = tls;
75
-
76
- this.correctDriftEnabled = this.value.spec?.correctDrift?.enabled || false;
77
- },
79
+ const correctDriftEnabled = this.value.spec?.correctDrift?.enabled || false;
78
80
 
79
- data() {
80
81
  let pollingInterval = toSeconds(this.value.spec.pollingInterval) || this.value.spec.pollingInterval;
81
82
 
82
83
  if (!pollingInterval) {
@@ -92,14 +93,15 @@ export default {
92
93
  const refValue = this.value.spec?.[ref] || '';
93
94
 
94
95
  return {
96
+ currentUser: {},
95
97
  tempCachedValues: {},
96
98
  username: null,
97
99
  password: null,
98
100
  publicKey: null,
99
101
  privateKey: null,
100
- tlsMode: null,
101
- caBundle: null,
102
- correctDriftEnabled: false,
102
+ caBundle,
103
+ tlsMode,
104
+ correctDriftEnabled,
103
105
  pollingInterval,
104
106
  ref,
105
107
  refValue,
@@ -179,8 +181,14 @@ export default {
179
181
  },
180
182
 
181
183
  watch: {
182
- tlsMode: 'updateTls',
183
- caBundle: 'updateTls',
184
+ tlsMode: {
185
+ handler: 'updateTls',
186
+ immediate: true
187
+ },
188
+ caBundle: {
189
+ handler: 'updateTls',
190
+ immediate: true
191
+ },
184
192
 
185
193
  workspace(neu) {
186
194
  if ( this.isCreate ) {
@@ -406,8 +414,7 @@ export default {
406
414
  this.value.spec['correctDrift'] = { enabled: this.correctDriftEnabled };
407
415
 
408
416
  if (this.mode === _CREATE) {
409
- this.value.metadata.labels[FLEET_LABELS.CREATED_BY_USER_ID] = this.value.currentUser.id;
410
- this.value.metadata.labels[FLEET_LABELS.CREATED_BY_USER_NAME] = this.value.currentUser.username;
417
+ this.value.metadata.labels[FLEET_LABELS.CREATED_BY_USER_ID] = this.currentUser.id;
411
418
  }
412
419
  },
413
420
 
@@ -34,23 +34,24 @@ export default {
34
34
 
35
35
  async fetch() {
36
36
  const inStore = this.$store.getters['currentProduct'].inStore;
37
- const alertmanagerConfigId = this.value.id;
38
-
39
- const { receiverSchema, routeSchema } = await fetchAlertManagerConfigSpecs(this.$store);
37
+ const { receiverSchema } = await fetchAlertManagerConfigSpecs(this.$store);
40
38
 
41
39
  this.receiverSchema = receiverSchema;
42
- this.routeSchema = routeSchema;
43
40
 
44
- const alertmanagerConfigResource = await this.$store.dispatch(`${ inStore }/find`, { type: MONITORING.ALERTMANAGERCONFIG, id: alertmanagerConfigId });
41
+ // The edit page is (mis)used as the detail page. When it's in create world none of the below is valid (there's no existing resource)
42
+ if (!this.isCreate) {
43
+ const alertmanagerConfigResource = await this.$store.dispatch(`${ inStore }/find`, { type: MONITORING.ALERTMANAGERCONFIG, id: this.value.id });
44
+
45
+ this.alertmanagerConfigId = this.value.id;
46
+ this.alertmanagerConfigResource = alertmanagerConfigResource;
45
47
 
46
- this.alertmanagerConfigId = alertmanagerConfigId;
47
- this.alertmanagerConfigResource = alertmanagerConfigResource;
48
- this.alertmanagerConfigDetailRoute = alertmanagerConfigResource?._detailLocation;
48
+ this.alertmanagerConfigDetailRoute = alertmanagerConfigResource._detailLocation;
49
49
 
50
- const alertmanagerConfigActions = alertmanagerConfigResource.availableActions;
51
- const receiverActions = alertmanagerConfigResource.getReceiverActions(alertmanagerConfigActions);
50
+ const alertmanagerConfigActions = alertmanagerConfigResource.availableActions;
51
+ const receiverActions = alertmanagerConfigResource.getReceiverActions(alertmanagerConfigActions);
52
52
 
53
- this.receiverActions = receiverActions;
53
+ this.receiverActions = receiverActions;
54
+ }
54
55
  },
55
56
 
56
57
  data() {
@@ -128,6 +128,16 @@ export default {
128
128
  const expectedFields = Object.keys(receiverSchema.resourceFields);
129
129
  const suffix = {};
130
130
 
131
+ // Values contains multiple receivers of two types
132
+ // 1. Known (expected, from schema) shown in their own tabs with explicity forms
133
+ // 2. unknown (not expected, missing in schema) shown as a yaml blob
134
+
135
+ // - expectedFields and suffixYaml are only used if the receiver is of type `custom`
136
+ // - expectedFields are the known types
137
+ // - suffixYaml are the unknown types
138
+ // - usages,
139
+ // - for custom, we need to know only the unknown / yaml blog. so suffixYaml is created by extracting all known (expectedFields) from value
140
+ // - on edit of custom we then combine the two again and save to value
131
141
  Object.keys(this.value).forEach((key) => {
132
142
  if (!expectedFields.includes(key)) {
133
143
  suffix[key] = this.value[key];
@@ -195,7 +205,7 @@ export default {
195
205
  return {
196
206
  duplicateName: () => {
197
207
  const receiversArray = this.alertmanagerConfigResource.spec.receivers;
198
- const receiverNamesArray = receiversArray.map((R) => R.name);
208
+ const receiverNamesArray = receiversArray?.map((R) => R.name) || [];
199
209
  const receiversSet = new Set(receiverNamesArray);
200
210
 
201
211
  if (receiversArray.length !== receiversSet.size) {
@@ -149,25 +149,20 @@ export default {
149
149
  });
150
150
 
151
151
  // Custom Providers from extensions - initialize each with the store and the i18n service
152
- // Wrap in try ... catch, to prevent errors in an extension breaking the page
153
- try {
154
- const extensionClasses = this.$plugin.listDynamic('provisioner').map((name) => this.$plugin.getDynamic('provisioner', name));
155
-
156
- // We can't pass in this.$store as this leads to a circular-reference that causes Vue to freeze,
157
- // so pass in specific services that the provisioner extension may need
158
- this.extensions = extensionClasses.map((c) => new c({
159
- dispatch: this.$store.dispatch,
160
- getters: this.$store.getters,
161
- axios: this.$store.$axios,
162
- $plugin: this.$store.app.$plugin,
163
- t: (...args) => this.t.apply(this, args),
164
- isCreate: this.isCreate,
165
- isEdit: this.isEdit,
166
- isView: this.isView,
167
- }));
168
- } catch (e) {
169
- console.error('Error loading provisioner(s) from extensions', e); // eslint-disable-line no-console
170
- }
152
+ // We can't pass in this.$store as this leads to a circular-reference that causes Vue to freeze,
153
+ // so pass in specific services that the provisioner extension may need
154
+ const context = {
155
+ dispatch: this.$store.dispatch,
156
+ getters: this.$store.getters,
157
+ axios: this.$store.$axios,
158
+ $extension: this.$store.app.$extension,
159
+ t: (...args) => this.t.apply(this, args),
160
+ isCreate: this.isCreate,
161
+ isEdit: this.isEdit,
162
+ isView: this.isView,
163
+ };
164
+
165
+ this.extensions = this.$extension.getProviders(context);
171
166
  },
172
167
 
173
168
  data() {
@@ -221,6 +221,8 @@ export default {
221
221
  },
222
222
 
223
223
  data() {
224
+ const isGoogle = this.provider === GOOGLE;
225
+
224
226
  return {
225
227
  loadedOnce: false,
226
228
  lastIdx: 0,
@@ -270,7 +272,8 @@ export default {
270
272
  clusterAgentDefaultPC: null,
271
273
  clusterAgentDefaultPDB: null,
272
274
  activeTab: null,
273
- isAuthenticated: this.provider !== GOOGLE || this.mode === _EDIT,
275
+ isGoogle,
276
+ isAuthenticated: !isGoogle || this.mode === _EDIT,
274
277
  projectId: null,
275
278
  REGISTRIES_TAB_NAME,
276
279
  labelForAddon,
@@ -861,7 +864,7 @@ export default {
861
864
  }
862
865
  },
863
866
  hideFooter() {
864
- return this.needCredential && !this.credential;
867
+ return this.needCredential && !this.credentialId;
865
868
  },
866
869
 
867
870
  overallFormValidationPassed() {
@@ -1029,6 +1032,9 @@ export default {
1029
1032
 
1030
1033
  if (!this.machinePools) {
1031
1034
  await this.initMachinePools(this.value.spec.rkeConfig.machinePools);
1035
+ if (this.isEdit && this.isGoogle && this.machinePools?.length > 0 && this.machinePools[0]?.config?.project) {
1036
+ this.projectId = this.machinePools[0]?.config?.project;
1037
+ }
1032
1038
  if (this.mode === _CREATE && !this.machinePools.length) {
1033
1039
  await this.addMachinePool();
1034
1040
  }
@@ -1437,6 +1443,8 @@ export default {
1437
1443
  entry.pool.machineConfigRef.name = neu.metadata.name;
1438
1444
  entry.create = false;
1439
1445
  entry.update = true;
1446
+
1447
+ this.initialMachinePoolsValues[entry.config.id] = clone(neu);
1440
1448
  } else if (entry.update) {
1441
1449
  entry.config = await entry.config.save();
1442
1450
  }
@@ -2219,7 +2227,7 @@ export default {
2219
2227
  </div>
2220
2228
  <AccountAccess
2221
2229
  v-if="!isAuthenticated"
2222
- v-model:credential="credential"
2230
+ v-model:credential="credentialId"
2223
2231
  v-model:project="projectId"
2224
2232
  v-model:is-authenticated="isAuthenticated"
2225
2233
  :mode="mode"
@@ -41,6 +41,7 @@ export default {
41
41
  <YamlEditor
42
42
  ref="yaml-additional"
43
43
  v-model:value="additionalManifest"
44
+ :mode="mode"
44
45
  :editor-mode="mode === 'view' ? 'VIEW_CODE' : 'EDIT_CODE'"
45
46
  initial-yaml-values="# Additional Manifest YAML"
46
47
  class="yaml-editor"
@@ -99,6 +99,7 @@ export default {
99
99
  ref="yaml-values"
100
100
  data-testid="addon-yaml-editor"
101
101
  :value="initYamlEditor(addonVersion.name)"
102
+ :mode="mode"
102
103
  :scrolling="true"
103
104
  :as-object="true"
104
105
  :editor-mode="mode === 'view' ? 'VIEW_CODE' : 'EDIT_CODE'"
@@ -493,6 +493,7 @@ export default {
493
493
  />
494
494
  <Checkbox
495
495
  :value="showDeprecatedPatchVersions"
496
+ :mode="mode"
496
497
  :label="t('cluster.kubernetesVersion.deprecatedPatches')"
497
498
  :tooltip="t('cluster.kubernetesVersion.deprecatedPatchWarning')"
498
499
  class="patch-version"