@rancher/shell 3.0.5-rc.8 → 3.0.5-rc.9

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 (171) 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 +136 -14
  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/PromptModal.vue +1 -1
  16. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +1 -0
  17. package/components/Resource/Detail/CopyToClipboard.vue +78 -0
  18. package/components/Resource/Detail/FetchLoader/__tests__/composables.test.ts +69 -0
  19. package/components/Resource/Detail/FetchLoader/composables.ts +27 -0
  20. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
  21. package/components/Resource/Detail/Metadata/Annotations/index.vue +1 -1
  22. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +13 -61
  23. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +33 -6
  24. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +24 -38
  25. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +25 -5
  26. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -23
  27. package/components/Resource/Detail/Metadata/KeyValueRow.vue +144 -0
  28. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -0
  29. package/components/Resource/Detail/Metadata/Labels/index.vue +1 -0
  30. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +30 -32
  31. package/components/Resource/Detail/Metadata/__tests__/KeyValueRow.test.ts +108 -0
  32. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +0 -3
  33. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +12 -5
  34. package/components/Resource/Detail/Metadata/composables.ts +1 -4
  35. package/components/Resource/Detail/Metadata/index.vue +1 -0
  36. package/components/Resource/Detail/Preview/Content.vue +63 -0
  37. package/components/Resource/Detail/Preview/Preview.vue +128 -0
  38. package/components/Resource/Detail/Preview/__tests__/Content.spec.ts +71 -0
  39. package/components/Resource/Detail/Preview/__tests__/Preview.spec.ts +121 -0
  40. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +141 -0
  41. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +136 -0
  42. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +245 -0
  43. package/components/Resource/Detail/ResourcePopover/index.vue +226 -0
  44. package/components/Resource/Detail/SpacedRow.vue +1 -0
  45. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +0 -5
  46. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
  47. package/components/Resource/Detail/TitleBar/composables.ts +1 -3
  48. package/components/Resource/Detail/TitleBar/index.vue +2 -29
  49. package/components/Resource/Detail/ViewOptions/composable.ts +9 -0
  50. package/components/Resource/Detail/ViewOptions/index.vue +41 -0
  51. package/components/Resource/Detail/__tests__/CopyToClipboard.spec.ts +82 -0
  52. package/components/ResourceDetail/Masthead/legacy.vue +0 -19
  53. package/components/ResourceDetail/index.vue +1 -26
  54. package/components/ResourceTable.vue +24 -0
  55. package/components/SortableTable/index.vue +7 -1
  56. package/components/SortableTable/paging.js +3 -0
  57. package/components/Tabbed/Tab.vue +43 -1
  58. package/components/Tabbed/index.vue +3 -1
  59. package/components/__tests__/Cron/CronExpressionEditor.test.ts +151 -0
  60. package/components/__tests__/Cron/CronExpressionEditorModal.test.ts +81 -0
  61. package/components/auth/login/saml.vue +86 -0
  62. package/components/form/LabeledSelect.vue +8 -8
  63. package/components/form/ResourceTabs/composable.ts +54 -0
  64. package/components/form/ResourceTabs/index.vue +10 -7
  65. package/components/form/Select.vue +13 -10
  66. package/components/form/__tests__/LabeledSelect.test.ts +133 -0
  67. package/components/form/__tests__/Select.test.ts +134 -0
  68. package/composables/useExtensionManager.ts +17 -0
  69. package/config/home-links.js +12 -0
  70. package/config/labels-annotations.js +0 -1
  71. package/config/page-actions.js +0 -1
  72. package/config/product/explorer.js +3 -1
  73. package/config/product/fleet.js +2 -7
  74. package/config/product/manager.js +0 -5
  75. package/config/query-params.js +1 -0
  76. package/config/router/navigation-guards/clusters.js +2 -1
  77. package/config/router/navigation-guards/products.js +1 -1
  78. package/core/extension-manager-impl.js +518 -0
  79. package/core/plugins.js +35 -468
  80. package/core/types.ts +8 -2
  81. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +1 -0
  82. package/detail/catalog.cattle.io.app.vue +7 -4
  83. package/detail/fleet.cattle.io.bundle.vue +1 -5
  84. package/detail/fleet.cattle.io.cluster.vue +3 -2
  85. package/detail/fleet.cattle.io.gitrepo.vue +76 -49
  86. package/detail/fleet.cattle.io.helmop.vue +78 -49
  87. package/dialog/AddonConfigConfirmationDialog.vue +1 -1
  88. package/dialog/GenericPrompt.vue +1 -1
  89. package/dialog/ImportDialog.vue +9 -2
  90. package/dialog/InstallExtensionDialog.vue +18 -10
  91. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +2 -1
  92. package/edit/__tests__/resources.cattle.io.restore.test.ts +106 -0
  93. package/edit/cloudcredential.vue +31 -17
  94. package/edit/constraints.gatekeeper.sh.constraint/index.vue +10 -2
  95. package/edit/fleet.cattle.io.cluster.vue +19 -0
  96. package/edit/fleet.cattle.io.gitrepo.vue +23 -16
  97. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +12 -11
  98. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +11 -1
  99. package/edit/provisioning.cattle.io.cluster/index.vue +14 -19
  100. package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -3
  101. package/edit/resources.cattle.io.restore.vue +5 -8
  102. package/list/__tests__/workload.test.ts +1 -0
  103. package/list/workload.vue +8 -1
  104. package/machine-config/components/GCEImage.vue +6 -5
  105. package/machine-config/google.vue +11 -6
  106. package/mixins/__tests__/chart.test.ts +139 -1
  107. package/mixins/chart.js +58 -18
  108. package/models/__tests__/namespace.test.ts +69 -0
  109. package/models/apps.statefulset.js +8 -10
  110. package/models/chart.js +5 -1
  111. package/models/fleet-application.js +16 -46
  112. package/models/fleet.cattle.io.bundle.js +1 -38
  113. package/models/fleet.cattle.io.gitrepo.js +4 -0
  114. package/models/fleet.cattle.io.helmop.js +4 -0
  115. package/models/management.cattle.io.project.js +12 -0
  116. package/models/namespace.js +30 -0
  117. package/models/workload.js +3 -0
  118. package/package.json +10 -10
  119. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +26 -11
  120. package/pages/c/_cluster/apps/charts/chart.vue +29 -20
  121. package/pages/c/_cluster/apps/charts/index.vue +1 -0
  122. package/pages/c/_cluster/apps/charts/install.vue +6 -5
  123. package/pages/c/_cluster/explorer/tools/__tests__/index.test.ts +102 -12
  124. package/pages/c/_cluster/explorer/tools/index.vue +145 -254
  125. package/pages/c/_cluster/manager/cloudCredential/index.vue +18 -1
  126. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +12 -2
  127. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  128. package/pages/c/_cluster/uiplugins/__tests__/index.spec.ts +318 -0
  129. package/pages/c/_cluster/uiplugins/index.vue +221 -363
  130. package/pages/home.vue +1 -9
  131. package/plugins/dashboard-store/resource-class.js +49 -0
  132. package/public/index.html +2 -1
  133. package/rancher-components/Card/Card.vue +1 -1
  134. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  135. package/rancher-components/Form/Radio/RadioButton.vue +1 -1
  136. package/rancher-components/Form/Radio/RadioGroup.vue +1 -1
  137. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -11
  138. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.test.ts +53 -0
  139. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +65 -0
  140. package/rancher-components/Pill/RcCounterBadge/index.ts +1 -0
  141. package/rancher-components/Pill/RcCounterBadge/types.ts +7 -0
  142. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +1 -1
  143. package/rancher-components/Pill/RcStatusBadge/index.ts +1 -1
  144. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -3
  145. package/rancher-components/Pill/RcStatusIndicator/types.ts +1 -1
  146. package/rancher-components/Pill/RcTag/RcTag.test.ts +64 -0
  147. package/rancher-components/Pill/RcTag/RcTag.vue +94 -0
  148. package/rancher-components/Pill/RcTag/index.ts +1 -0
  149. package/rancher-components/Pill/RcTag/types.ts +9 -0
  150. package/rancher-components/Pill/types.ts +1 -0
  151. package/rancher-components/RcItemCard/RcItemCard.vue +1 -0
  152. package/rancher-components/RcItemCard/RcItemCardAction.vue +12 -0
  153. package/store/__tests__/catalog.test.ts +63 -0
  154. package/store/catalog.js +2 -2
  155. package/store/type-map.js +3 -15
  156. package/types/extension-manager.ts +26 -0
  157. package/types/shell/index.d.ts +121 -16
  158. package/utils/__tests__/product.test.ts +129 -0
  159. package/utils/__tests__/resource.test.ts +87 -0
  160. package/utils/alertmanagerconfig.js +2 -2
  161. package/utils/auth.js +3 -76
  162. package/utils/product.ts +39 -0
  163. package/utils/resource.ts +35 -0
  164. package/utils/select.js +0 -24
  165. package/utils/validators/formRules/__tests__/index.test.ts +3 -0
  166. package/utils/validators/formRules/index.ts +2 -1
  167. package/vue.config.js +1 -1
  168. package/components/Resource/Detail/Metadata/Rectangle.vue +0 -34
  169. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +0 -24
  170. package/components/ResourceDetail/Masthead/__tests__/legacy.test.ts +0 -65
  171. /package/components/{ForceDirectedTreeChart.vue → ForceDirectedTreeChart/index.vue} +0 -0
@@ -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"
@@ -159,19 +159,16 @@ export default {
159
159
  if (neu === 'useDefault') {
160
160
  delete this.value.spec.storageLocation;
161
161
  delete this.value.spec.backupFilename;
162
- } else if (!this.value.spec.storageLocation && neu === 'configureS3') {
163
- this.value.spec['storageLocation'] = { s3: {} };
164
- this.s3 = this.value.spec.storageLocation.s3;
165
- }
166
- if (neu === 'useBackup') {
162
+ this.s3 = {};
163
+ } else if (neu === 'configureS3') {
164
+ this.s3 = this.value.spec.storageLocation?.s3 || {};
165
+ this.value.spec.storageLocation = { s3: this.s3 };
166
+ } else if (neu === 'useBackup') {
167
167
  delete this.value.spec.storageLocation;
168
168
 
169
169
  if (this.availableBackups.length === 1) {
170
170
  this.updateTargetBackup(this.availableBackups[0]);
171
171
  }
172
- } else {
173
- delete this.value.spec.backupFilename;
174
- this.value.spec.storageLocation = { s3: this.s3 };
175
172
  }
176
173
  },
177
174
 
@@ -37,6 +37,7 @@ describe('component: workload', () => {
37
37
  'prefs/get': () => resource,
38
38
  'cluster/schemaFor': () => {},
39
39
  'cluster/all': () => [{}],
40
+ 'features/get': () => false,
40
41
  }
41
42
  },
42
43
  $fetchState: {
package/list/workload.vue CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  } from '@shell/config/types';
6
6
  import ResourceFetch from '@shell/mixins/resource-fetch';
7
7
  import PaginatedResourceTable from '@shell/components/PaginatedResourceTable';
8
+ import { STEVE_CACHE } from '@shell/store/features';
8
9
 
9
10
  const workloadSchema = {
10
11
  id: 'workload',
@@ -84,11 +85,17 @@ export default {
84
85
  },
85
86
 
86
87
  data() {
88
+ const allTypes = this.$route.params.resource === workloadSchema.id;
89
+
90
+ if (allTypes && this.$store.getters['features/get'](STEVE_CACHE)) {
91
+ this.$store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceListNotFound', { resource: workloadSchema.id }, true)));
92
+
93
+ return;
94
+ }
87
95
  // Ensure these are set on load (to determine if the NS filter is required) rather than too late on `fetch`
88
96
  const { loadResources, loadIndeterminate } = $loadingResources(this.$route, this.$store);
89
97
 
90
98
  const { params:{ resource: type } } = this.$route;
91
- const allTypes = this.$route.params.resource === workloadSchema.id;
92
99
  const schema = type !== workloadSchema.id ? this.$store.getters['cluster/schemaFor'](type) : workloadSchema;
93
100
  const paginationEnabled = !allTypes && this.$store.getters[`cluster/paginationEnabled`]?.({ id: type });
94
101
 
@@ -43,6 +43,10 @@ export default {
43
43
  type: String,
44
44
  default: _CREATE,
45
45
  },
46
+ poolCreateMode: {
47
+ type: Boolean,
48
+ default: true
49
+ },
46
50
  location: {
47
51
  type: Object,
48
52
  required: true
@@ -85,7 +89,7 @@ export default {
85
89
  },
86
90
  created() {
87
91
  this.debouncedLoadFamilies = debounce(this.getFamilies, 500);
88
- if (this.mode !== _CREATE) {
92
+ if (!this.poolCreateMode) {
89
93
  this.imageProjects = `${ this.getProjectFromImage() }`;
90
94
  }
91
95
  },
@@ -111,9 +115,6 @@ export default {
111
115
 
112
116
  };
113
117
  },
114
- isCreate() {
115
- return this.mode === _CREATE;
116
- },
117
118
  project() {
118
119
  return this.value.project;
119
120
  },
@@ -284,7 +285,7 @@ export default {
284
285
  async getImages(val) {
285
286
  this.loadingImages = true;
286
287
  try {
287
- const isOriginal = !this.isCreate && this.machineImage === this.getImageNameFromImage(this.originalMachineImage);
288
+ const isOriginal = !this.poolCreateMode && this.machineImage === this.getImageNameFromImage(this.originalMachineImage);
288
289
 
289
290
  this.machineImages = await this.getImagesInProject(val, this.showDeprecated);
290
291
  // If we had to reload list of images, we need to reset selected image if it is no longer in the list,
@@ -79,7 +79,11 @@ export default {
79
79
  disabled: {
80
80
  type: Boolean,
81
81
  default: false
82
- }
82
+ },
83
+ poolCreateMode: {
84
+ type: Boolean,
85
+ default: true
86
+ },
83
87
  },
84
88
 
85
89
  async fetch() {
@@ -123,7 +127,7 @@ export default {
123
127
  };
124
128
  },
125
129
  created() {
126
- if (this.mode === _CREATE) {
130
+ if (this.poolCreateMode) {
127
131
  this.$emit('validationChanged', false);
128
132
  this.value.project = this.projectId;
129
133
  for (const key in this.defaultConfig) {
@@ -157,7 +161,7 @@ export default {
157
161
  'value.setExternalFirewallRulePrefix'(neu) {
158
162
  if (!neu) {
159
163
  this.value.openPort = [];
160
- } else if (this.isCreate) {
164
+ } else if (this.poolCreateMode) {
161
165
  this.value.openPort.push('6443');
162
166
  } else {
163
167
  this.value.openPort = this.originalOpenPort.length > 0 ? this.originalOpenPort : ['6443'];
@@ -334,7 +338,7 @@ export default {
334
338
 
335
339
  if (!cur ) {
336
340
  // If default is not actually available, reset
337
- if (this.isCreate) {
341
+ if (this.poolCreateMode) {
338
342
  this.value.diskType = '';
339
343
  }
340
344
  } else {
@@ -446,6 +450,7 @@ export default {
446
450
  :credentialId="credentialId"
447
451
  :projectId="value.project"
448
452
  :originalMachineImage="originalMachineImage"
453
+ :pool-create-mode="poolCreateMode"
449
454
  :mode="mode"
450
455
  :location="location"
451
456
  :rules="{machineImage: fvGetAndReportPathRules('machineImage')}"
@@ -494,7 +499,7 @@ export default {
494
499
  label-key="cluster.machineConfig.gce.network.label"
495
500
  :mode="mode"
496
501
  :options="networkOptions"
497
- :disabled="!isCreate"
502
+ :disabled="!poolCreateMode"
498
503
  option-key="name"
499
504
  option-label="label"
500
505
  :loading="loadingNetworks"
@@ -508,7 +513,7 @@ export default {
508
513
  label-key="cluster.machineConfig.gce.subnetwork.label"
509
514
  :mode="mode"
510
515
  :options="subnetworkOptions"
511
- :disabled="!isCreate"
516
+ :disabled="!poolCreateMode"
512
517
  option-key="name"
513
518
  option-label="name"
514
519
  :loading="loadingNetworks"