@rancher/shell 3.0.4 → 3.0.5-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/assets/styles/base/_basic.scss +6 -0
  2. package/assets/styles/global/_button.scss +1 -0
  3. package/assets/translations/en-us.yaml +37 -3
  4. package/cloud-credential/aws.vue +2 -0
  5. package/components/AssignTo.vue +25 -11
  6. package/components/AsyncButton.vue +24 -7
  7. package/components/BannerGraphic.vue +1 -0
  8. package/components/CommunityLinks.vue +3 -3
  9. package/components/CopyToClipboardText.vue +2 -1
  10. package/components/DetailText.vue +5 -0
  11. package/components/DisableAuthProviderModal.vue +1 -0
  12. package/components/ExplorerMembers.vue +1 -1
  13. package/components/ExplorerProjectsNamespaces.vue +56 -14
  14. package/components/LandingPagePreference.vue +5 -3
  15. package/components/LocaleSelector.vue +38 -94
  16. package/components/ModalWithCard.vue +1 -0
  17. package/components/MoveModal.vue +1 -0
  18. package/components/PromptRemove.vue +1 -0
  19. package/components/PromptRestore.vue +1 -0
  20. package/components/ResourceCancelModal.vue +1 -0
  21. package/components/SortableTable/index.vue +10 -11
  22. package/components/__tests__/AsyncButton.test.ts +2 -2
  23. package/components/auth/__tests__/RoleDetailEdit.test.ts +3 -2
  24. package/components/form/ArrayList.vue +66 -54
  25. package/components/form/Command.vue +6 -15
  26. package/components/form/EnvVars.vue +15 -8
  27. package/components/form/HealthCheck.vue +3 -3
  28. package/components/form/HookOption.vue +11 -16
  29. package/components/form/LabeledSelect.vue +2 -1
  30. package/components/form/LifecycleHooks.vue +3 -3
  31. package/components/form/MatchExpressions.vue +10 -7
  32. package/components/form/NameNsDescription.vue +123 -103
  33. package/components/form/Networking.vue +20 -12
  34. package/components/form/NodeAffinity.vue +31 -23
  35. package/components/form/NodeScheduling.vue +13 -3
  36. package/components/form/PodAffinity.vue +43 -43
  37. package/components/form/Probe.vue +67 -66
  38. package/components/form/ResourceQuota/Project.vue +5 -1
  39. package/components/form/ResourceSelector.vue +7 -9
  40. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +6 -3
  41. package/components/form/SSHKnownHosts/__tests__/KnownHostsEditDialog.test.ts +12 -1
  42. package/components/form/SSHKnownHosts/index.vue +16 -2
  43. package/components/form/Security.vue +54 -56
  44. package/components/form/Select.vue +31 -6
  45. package/components/form/ShellInput.vue +5 -1
  46. package/components/form/Tolerations.vue +5 -1
  47. package/components/form/ValueFromResource.vue +134 -121
  48. package/components/form/WorkloadPorts.vue +18 -18
  49. package/components/form/__tests__/ArrayList.test.ts +3 -0
  50. package/components/form/__tests__/MatchExpressions.test.ts +12 -12
  51. package/components/form/__tests__/NameNsDescription.test.ts +115 -14
  52. package/components/form/__tests__/Probe.test.ts +12 -8
  53. package/components/form/__tests__/SSHKnownHosts.test.ts +11 -0
  54. package/components/form/__tests__/Select.test.ts +37 -0
  55. package/components/formatter/InternalExternalIP.vue +2 -0
  56. package/components/formatter/SecretData.vue +20 -7
  57. package/components/nav/Group.vue +15 -1
  58. package/components/nav/Header.vue +1 -0
  59. package/components/nav/Type.vue +12 -1
  60. package/components/templates/blank.vue +4 -1
  61. package/components/templates/default.vue +2 -0
  62. package/components/templates/home.vue +4 -1
  63. package/components/templates/plain.vue +4 -1
  64. package/composables/useRuntimeFlag.ts +29 -0
  65. package/config/router/routes.js +20 -13
  66. package/core/types.ts +5 -0
  67. package/dialog/AddCustomBadgeDialog.vue +1 -0
  68. package/dialog/DeactivateDriverDialog.vue +1 -0
  69. package/dialog/ForceMachineRemoveDialog.vue +4 -1
  70. package/edit/__tests__/monitoring.coreos.com.prometheusrule.test.ts +16 -3
  71. package/edit/auth/__tests__/oidc.test.ts +152 -109
  72. package/edit/auth/azuread.vue +1 -0
  73. package/edit/auth/googleoauth.vue +4 -0
  74. package/edit/auth/oidc.vue +37 -4
  75. package/edit/cloudcredential.vue +1 -0
  76. package/edit/logging.banzaicloud.io.output/__tests__/logging.banzaicloud.io.output.test.ts +40 -9
  77. package/edit/networking.k8s.io.ingress/IngressClass.vue +7 -3
  78. package/edit/networking.k8s.io.ingress/__tests__/IngressClass.test.ts +58 -0
  79. package/edit/persistentvolume/__tests__/persistentvolume.test.ts +14 -2
  80. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +1 -0
  81. package/edit/provisioning.cattle.io.cluster/rke2.vue +25 -34
  82. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +6 -1
  83. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +29 -1
  84. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +2 -2
  85. package/edit/token.vue +2 -0
  86. package/edit/workload/index.vue +1 -0
  87. package/edit/workload/mixins/workload.js +0 -2
  88. package/list/management.cattle.io.feature.vue +1 -0
  89. package/list/provisioning.cattle.io.cluster.vue +20 -12
  90. package/models/__tests__/namespace.test.ts +25 -1
  91. package/models/cloudcredential.js +5 -0
  92. package/models/kontainerdriver.js +6 -3
  93. package/models/management.cattle.io.node.js +3 -3
  94. package/models/namespace.js +4 -5
  95. package/models/nodedriver.js +6 -3
  96. package/models/workload.js +4 -1
  97. package/package.json +3 -3
  98. package/pages/account/index.vue +4 -1
  99. package/pages/auth/login.vue +11 -3
  100. package/pages/auth/logout.vue +4 -1
  101. package/pages/auth/setup.vue +1 -0
  102. package/pages/auth/verify.vue +4 -1
  103. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  104. package/pages/diagnostic.vue +47 -2
  105. package/pages/fail-whale.vue +6 -3
  106. package/pages/home.vue +24 -18
  107. package/pages/support/index.vue +4 -1
  108. package/rancher-components/Form/Radio/RadioGroup.vue +25 -23
  109. package/rancher-components/RcDropdown/RcDropdown.vue +3 -2
  110. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -0
  111. package/rancher-components/RcDropdown/useDropdownCollection.ts +8 -0
  112. package/rancher-components/RcDropdown/useDropdownContext.ts +9 -3
  113. package/scripts/extension/publish +1 -0
  114. package/server/har-file.js +25 -3
  115. package/store/features.js +2 -1
  116. package/store/type-map.js +4 -0
  117. package/types/shell/index.d.ts +8 -1
  118. package/utils/cluster.js +35 -0
  119. package/utils/validators/machine-pool.ts +20 -0
  120. package/components/formatter/ExtensionCache.vue +0 -74
  121. package/components/formatter/Port.vue +0 -24
  122. package/components/formatter/SecretType.vue +0 -41
@@ -182,7 +182,10 @@ export default {
182
182
  </script>
183
183
 
184
184
  <template>
185
- <main class="main-layout">
185
+ <main
186
+ class="main-layout"
187
+ :aria-label="t('layouts.verify')"
188
+ >
186
189
  <h1 class="text-center mt-50">
187
190
  <span v-if="testing">
188
191
  Testing Configuration&hellip;
@@ -69,7 +69,7 @@ export default {
69
69
  return {
70
70
  id: m.name,
71
71
  text: m.name,
72
- url: `mailto:${ m.email }`
72
+ url: m.email ? `mailto:${ m.email }` : m.url
73
73
  };
74
74
  });
75
75
  },
@@ -17,6 +17,7 @@ export default {
17
17
  const finalCounts = [];
18
18
  const promises = [];
19
19
  const topFifteenForResponseTime = [];
20
+ const schemaPromises = [];
20
21
 
21
22
  clusterForCounts.forEach((cluster, i) => {
22
23
  // Necessary to retrieve the proper display name of the cluster
@@ -31,9 +32,11 @@ export default {
31
32
  isTableVisible: !!(i === 0 && clusterForCounts.length === 1)
32
33
  });
33
34
  promises.push(this.$store.dispatch('management/request', { url: `/k8s/clusters/${ cluster.mgmt?.id }/v1/counts` }));
35
+ schemaPromises.push(this.$store.dispatch('management/request', { url: `/k8s/clusters/${ cluster.mgmt?.id }/v1/schemas?exclude=metadata.managedFields` }));
34
36
  });
35
37
 
36
38
  const countsPerCluster = await Promise.all(promises);
39
+ const schemasPerCluster = await Promise.all(schemaPromises);
37
40
 
38
41
  countsPerCluster.forEach((clusterCount, index) => {
39
42
  const counts = clusterCount.data?.[0]?.counts;
@@ -69,6 +72,14 @@ export default {
69
72
 
70
73
  this.topFifteenForResponseTime = topFifteenForResponseTime;
71
74
  this.finalCounts = finalCounts;
75
+
76
+ // Schemas
77
+ schemasPerCluster.forEach((schemas, index) => {
78
+ finalCounts[index].schemaCount = schemas?.data?.length || 0;
79
+ finalCounts[index].customSchemas = (schemas?.data?.filter((schema) => {
80
+ return schema.attributes?.group.includes('.') && !schema.attributes?.group.includes('.cattle.io') && !schema.attributes?.group.includes('.k8s.io');
81
+ }).map((schema) => schema.id) || []).sort();
82
+ });
72
83
  },
73
84
 
74
85
  data() {
@@ -148,7 +159,7 @@ export default {
148
159
  systemInformation: this.systemInformation,
149
160
  storeMapping: this.parseStoreData(this.storeMapping),
150
161
  resourceCounts: this.finalCounts,
151
- responseTimes: this.responseTimes
162
+ responseTimes: this.responseTimes,
152
163
  };
153
164
 
154
165
  downloadFile(fileName, JSON.stringify(data), 'application/json')
@@ -352,6 +363,7 @@ export default {
352
363
  <span>Cluster: <b>{{ cluster.name }}</b></span>
353
364
  <span>Namespace: <b>{{ cluster.namespace }}</b></span>
354
365
  <span>Total Resources: <b>{{ sumResourceCount(cluster.counts) }}</b></span>
366
+ <span>Total Schemas: <b>{{ cluster.schemaCount }}</b></span>
355
367
  <span>Nodes: <b>{{ nodeCount(cluster.counts) }}</b></span>
356
368
  <i
357
369
  class="icon"
@@ -365,6 +377,22 @@ export default {
365
377
  </tr>
366
378
  </thead>
367
379
  <tbody v-show="cluster.isTableVisible">
380
+ <tr>
381
+ <td colspan="3">
382
+ <div class="schema-title">
383
+ Custom Schemas
384
+ </div>
385
+ <div class="custom-schemas">
386
+ <div
387
+ v-for="name in cluster.customSchemas"
388
+ :key="name"
389
+ class="schema-name"
390
+ >
391
+ {{ name }}
392
+ </div>
393
+ </div>
394
+ </td>
395
+ </tr>
368
396
  <tr>
369
397
  <th>
370
398
  Resource
@@ -445,6 +473,23 @@ table {
445
473
  padding: 8px 5px;
446
474
  min-width: 150px;
447
475
  text-align: left;
476
+
477
+ .schema-title {
478
+ font-weight: bold;
479
+ margin-bottom: 4px;
480
+ }
481
+
482
+ .custom-schemas {
483
+ display: flex;
484
+ flex-wrap: wrap;
485
+
486
+ > .schema-name {
487
+ margin-right: 5px;
488
+ margin-bottom: 2px;
489
+ border: 1px solid var(--border);
490
+ padding: 2px 5px;
491
+ }
492
+ }
448
493
  }
449
494
 
450
495
  th {
@@ -511,7 +556,7 @@ table {
511
556
  .resources-count-container {
512
557
  .cluster-row {
513
558
  display: grid;
514
- grid-template-columns: repeat(4, 1fr) 20px;
559
+ grid-template-columns: repeat(5, 1fr) 20px;
515
560
  grid-template-rows: 1fr;
516
561
  align-content: center;
517
562
  font-weight: normal;
@@ -70,12 +70,15 @@ export default {
70
70
  :simple="true"
71
71
  />
72
72
 
73
- <main class="main-layout">
73
+ <main
74
+ class="main-layout"
75
+ aria-label="Fail whale layout"
76
+ >
74
77
  <div
75
78
  v-if="error"
76
79
  class="outlet"
77
80
  >
78
- <main class="main-layout error">
81
+ <div class="main-layout error">
79
82
  <div class="text-center">
80
83
  <BrandImage
81
84
  file-name="error-desert-landscape.svg"
@@ -115,7 +118,7 @@ export default {
115
118
  </a>
116
119
  </p>
117
120
  </div>
118
- </main>
121
+ </div>
119
122
  </div>
120
123
  </main>
121
124
  </div>
package/pages/home.vue CHANGED
@@ -49,6 +49,21 @@ export default defineComponent({
49
49
  mixins: [PageHeaderActions],
50
50
 
51
51
  data() {
52
+ const options = this.$store.getters[`type-map/optionsFor`](CAPI.RANCHER_CLUSTER)?.custom || {};
53
+ const params = {
54
+ product: MANAGER,
55
+ cluster: BLANK_CLUSTER,
56
+ resource: CAPI.RANCHER_CLUSTER
57
+ };
58
+ const defaultCreateLocation = {
59
+ name: 'c-cluster-product-resource-create',
60
+ params,
61
+ };
62
+ const defaultImportLocation = {
63
+ ...defaultCreateLocation,
64
+ query: { [MODE]: _IMPORT }
65
+ };
66
+
52
67
  return {
53
68
  HIDE_HOME_PAGE_CARDS,
54
69
  fullVersion: getVersionInfo(this.$store).fullVersion,
@@ -87,24 +102,9 @@ export default defineComponent({
87
102
  },
88
103
  },
89
104
 
90
- createLocation: {
91
- name: 'c-cluster-product-resource-create',
92
- params: {
93
- product: MANAGER,
94
- cluster: BLANK_CLUSTER,
95
- resource: CAPI.RANCHER_CLUSTER
96
- },
97
- },
105
+ createLocation: options.createLocation ? options.createLocation(params) : defaultCreateLocation,
98
106
 
99
- importLocation: {
100
- name: 'c-cluster-product-resource-create',
101
- params: {
102
- product: MANAGER,
103
- cluster: BLANK_CLUSTER,
104
- resource: CAPI.RANCHER_CLUSTER
105
- },
106
- query: { [MODE]: _IMPORT }
107
- },
107
+ importLocation: options.importLocation ? options.importLocation(params) : defaultImportLocation,
108
108
 
109
109
  headers: [
110
110
  STATE,
@@ -512,7 +512,7 @@ export default defineComponent({
512
512
  :show-child="false"
513
513
  :breadcrumb="false"
514
514
  >
515
- {{ vendor }}
515
+ {{ `${vendor} - ${t('landing.homepage')}` }}
516
516
  </TabTitle>
517
517
  <BannerGraphic
518
518
  :small="true"
@@ -805,6 +805,12 @@ export default defineComponent({
805
805
  .cluster-name {
806
806
  display: flex;
807
807
  align-items: center;
808
+
809
+ // Ensure long cluster names truncate with ellipsis
810
+ > A {
811
+ overflow: hidden;
812
+ text-overflow: ellipsis;
813
+ }
808
814
  }
809
815
 
810
816
  .cluster-description {
@@ -118,7 +118,10 @@ export default {
118
118
  </script>
119
119
  <template>
120
120
  <div>
121
- <BannerGraphic :title="t(title, {}, true)" />
121
+ <BannerGraphic
122
+ :title="t(title, {}, true)"
123
+ :alt="t('support.bannerImage')"
124
+ />
122
125
 
123
126
  <IndentedPanel>
124
127
  <div class="content mt-20">
@@ -199,33 +199,35 @@ export default defineComponent({
199
199
  </script>
200
200
 
201
201
  <template>
202
- <div>
202
+ <fieldset>
203
203
  <!-- Label -->
204
204
  <div
205
205
  v-if="label || labelKey || tooltip || tooltipKey || $slots.label"
206
206
  class="radio-group label"
207
207
  >
208
- <slot name="label">
209
- <h3>
210
- <t
211
- v-if="labelKey"
212
- :k="labelKey"
213
- />
214
- <template v-else-if="label">
215
- {{ label }}
216
- </template>
217
- <i
218
- v-if="tooltipKey"
219
- v-clean-tooltip="t(tooltipKey)"
220
- class="icon icon-info icon-lg"
221
- />
222
- <i
223
- v-else-if="tooltip"
224
- v-clean-tooltip="tooltip"
225
- class="icon icon-info icon-lg"
226
- />
227
- </h3>
228
- </slot>
208
+ <legend>
209
+ <slot name="label">
210
+ <h3>
211
+ <t
212
+ v-if="labelKey"
213
+ :k="labelKey"
214
+ />
215
+ <template v-else-if="label">
216
+ {{ label }}
217
+ </template>
218
+ <i
219
+ v-if="tooltipKey"
220
+ v-clean-tooltip="t(tooltipKey)"
221
+ class="icon icon-info icon-lg"
222
+ />
223
+ <i
224
+ v-else-if="tooltip"
225
+ v-clean-tooltip="tooltip"
226
+ class="icon icon-info icon-lg"
227
+ />
228
+ </h3>
229
+ </slot>
230
+ </legend>
229
231
  </div>
230
232
 
231
233
  <!-- Group -->
@@ -266,7 +268,7 @@ export default defineComponent({
266
268
  </slot>
267
269
  </div>
268
270
  </div>
269
- </div>
271
+ </fieldset>
270
272
  </template>
271
273
 
272
274
  <style lang='scss'>
@@ -49,7 +49,7 @@ useClickOutside(dropdownTarget, () => showMenu(false));
49
49
 
50
50
  const applyShow = () => {
51
51
  registerDropdownCollection(dropdownTarget.value);
52
- setFocus();
52
+ setFocus('down');
53
53
  };
54
54
 
55
55
  </script>
@@ -78,7 +78,8 @@ const applyShow = () => {
78
78
  dropdown-menu-collection
79
79
  :aria-label="ariaLabel || 'Dropdown Menu'"
80
80
  @keydown="handleKeydown"
81
- @keydown.down="setFocus()"
81
+ @keydown.down="setFocus('down')"
82
+ @keydown.up="setFocus('up')"
82
83
  >
83
84
  <slot name="dropdownCollection">
84
85
  <!--Empty slot content-->
@@ -35,8 +35,18 @@ defineExpose({ focus });
35
35
  @keydown.enter.space="handleKeydown"
36
36
  @click="showMenu(true)"
37
37
  >
38
+ <template #before>
39
+ <slot name="before">
40
+ <!-- Empty Content -->
41
+ </slot>
42
+ </template>
38
43
  <slot name="default">
39
44
  <!--Empty slot content-->
40
45
  </slot>
46
+ <template #after>
47
+ <slot name="after">
48
+ <!-- Empty Content -->
49
+ </slot>
50
+ </template>
41
51
  </RcButton>
42
52
  </template>
@@ -10,6 +10,7 @@ export const useDropdownCollection = () => {
10
10
  const dropdownItems = ref<Element[]>([]);
11
11
  const dropdownContainer = ref<HTMLElement | null>(null);
12
12
  const firstDropdownItem = ref<HTMLElement | null>(null);
13
+ const lastDropdownItem = ref<HTMLElement | null>(null);
13
14
 
14
15
  /**
15
16
  * Registers the dropdown container and initializes dropdown items.
@@ -22,6 +23,12 @@ export const useDropdownCollection = () => {
22
23
  if (dropdownItems.value[0] instanceof HTMLElement) {
23
24
  firstDropdownItem.value = dropdownItems.value[0];
24
25
  }
26
+
27
+ const lastItem = dropdownItems.value[dropdownItems.value.length - 1];
28
+
29
+ if (lastItem instanceof HTMLElement) {
30
+ lastDropdownItem.value = lastItem;
31
+ }
25
32
  }
26
33
  };
27
34
 
@@ -40,6 +47,7 @@ export const useDropdownCollection = () => {
40
47
  return {
41
48
  dropdownItems,
42
49
  firstDropdownItem,
50
+ lastDropdownItem,
43
51
  dropdownContainer,
44
52
  registerDropdownCollection,
45
53
  };
@@ -17,6 +17,7 @@ export const useDropdownContext = (emit: typeof rcDropdownEmits) => {
17
17
  const {
18
18
  dropdownItems,
19
19
  firstDropdownItem,
20
+ lastDropdownItem,
20
21
  dropdownContainer,
21
22
  registerDropdownCollection,
22
23
  } = useDropdownCollection();
@@ -70,7 +71,7 @@ export const useDropdownContext = (emit: typeof rcDropdownEmits) => {
70
71
  /**
71
72
  * Sets focus to the first dropdown item if a keydown event has occurred.
72
73
  */
73
- const setFocus = () => {
74
+ const setFocus = (direction: 'down' | 'up') => {
74
75
  nextTick(() => {
75
76
  if (!didKeydown.value) {
76
77
  dropdownContainer.value?.focus();
@@ -78,7 +79,12 @@ export const useDropdownContext = (emit: typeof rcDropdownEmits) => {
78
79
  return;
79
80
  }
80
81
 
81
- firstDropdownItem.value?.focus();
82
+ if (direction === 'down') {
83
+ firstDropdownItem.value?.focus();
84
+ } else if (direction === 'up') {
85
+ lastDropdownItem.value?.focus();
86
+ }
87
+
82
88
  didKeydown.value = false;
83
89
  });
84
90
  };
@@ -95,7 +101,7 @@ export const useDropdownContext = (emit: typeof rcDropdownEmits) => {
95
101
  dropdownItems,
96
102
  close: () => returnFocus(),
97
103
  focusFirstElement: () => {
98
- setFocus();
104
+ setFocus('down');
99
105
  },
100
106
  handleKeydown,
101
107
  });
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bash
2
+ set -eo pipefail
2
3
 
3
4
  SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
4
5
  BASE_DIR="$(pwd)"
@@ -1,11 +1,15 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const querystring = require('querystring');
3
4
 
4
5
  // When we receive a request to this URL we will reset the session to replay again from the HAR file
5
6
  // This allows the user to refresh the browser and replay the HAR file again
6
7
  const RESET_URL = '/api/v1/namespaces/cattle-ui-plugin-system/services/http:ui-plugin-operator:80/proxy/index.json';
7
8
 
8
- const EXCLUDES_QS = '?exclude=metadata.managedFields';
9
+ const EXCLUDE_QS = 'exclude';
10
+
11
+ const LEGACY_UI_PLUGIN_INDEX = '/api/v1/namespaces/cattle-ui-plugin-system/services/http:ui-plugin-operator:80/proxy/index.json';
12
+ const NEW_UI_PLUGIN_INDEX = '/v1/uiplugins';
9
13
 
10
14
  /**
11
15
  * Load the network requests/responses from the har file
@@ -113,10 +117,28 @@ function harProxy(responses, folder) {
113
117
  const url = decodeURIComponent(req.originalUrl);
114
118
  let playback = session[req.originalUrl];
115
119
 
120
+ // Handle case where HAR file was created with older UI Extension API that used the operator
121
+ if (!playback && req.originalUrl.includes(NEW_UI_PLUGIN_INDEX)) {
122
+ // Look for new URl for UI plugins
123
+ playback = session[LEGACY_UI_PLUGIN_INDEX];
124
+ }
125
+
116
126
  // If it did not match, try without the metadata excludes query string that was adding in 2.8.0
117
127
  // This might allow HAR captures with Rancher < 2.8.0 to be replayed on >= 2.8.0
118
- if (!playback && req.originalUrl.endsWith(EXCLUDES_QS)) {
119
- playback = session[req.originalUrl.slice(0, -EXCLUDES_QS.length)];
128
+ if (!playback && req.originalUrl.includes('?')) {
129
+ const urlParts = req.originalUrl.split('?');
130
+
131
+ if (urlParts.length > 1) {
132
+ const queryString = urlParts[1];
133
+ const qs = querystring.parse(queryString);
134
+
135
+ delete qs[EXCLUDE_QS];
136
+
137
+ const newQs = querystring.stringify(qs);
138
+ const newUrl = newQs.length ? `${ urlParts[0] }?${ newQs }` : urlParts[0];
139
+
140
+ playback = session[newUrl];
141
+ }
120
142
  }
121
143
 
122
144
  if (playback && playback[req.method] && playback[req.method].length) {
package/store/features.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { MANAGEMENT } from '@shell/config/types';
2
+ import { SCHEDULING_CUSTOMIZATION as SCHEDULING_CUSTOMIZATION_FEATURE } from '@shell/config/features';
2
3
 
3
4
  const definitions = {};
4
5
 
@@ -36,7 +37,7 @@ export const FLEET_WORKSPACE_BACK = create('provisioningv2-fleet-workspace-back-
36
37
  export const STEVE_CACHE = create('ui-sql-cache', false);
37
38
  export const UIEXTENSION = create('uiextension', true);
38
39
  export const PROVISIONING_PRE_BOOTSTRAP = create('provisioningprebootstrap', false);
39
- export const SCHEDULING_CUSTOMIZATION = create('cluster-agent-scheduling-customization', false);
40
+ export const SCHEDULING_CUSTOMIZATION = create(SCHEDULING_CUSTOMIZATION_FEATURE, false);
40
41
 
41
42
  // Not currently used.. no point defining ones we don't use
42
43
  // export const EMBEDDED_CLUSTER_API = create('embedded-cluster-api', true);
package/store/type-map.js CHANGED
@@ -107,6 +107,7 @@
107
107
  // graphConfig: undefined -- Use this to pass along the graph configuration
108
108
  // notFilterNamespace: undefined -- Define namespaces that do not need to be filtered
109
109
  // localOnly: False -- Hide this type from the nav/search bar on downstream clusters
110
+ // custom: any - Custom options for a given type
110
111
  // }
111
112
  // )
112
113
  // ignoreGroup(group): Never show group or any types in it
@@ -524,6 +525,7 @@ export const getters = {
524
525
  depaginate: false,
525
526
  customRoute: undefined,
526
527
  resourceEditMasthead: true,
528
+ custom: {},
527
529
  };
528
530
 
529
531
  return (schemaOrType, pagination) => {
@@ -1729,6 +1731,8 @@ export const mutations = {
1729
1731
  let obj = { ...options, match };
1730
1732
 
1731
1733
  if ( idx >= 0 ) {
1734
+ // Merge the custom data object - multiple configures will update existing rather than overwrite
1735
+ obj.custom = Object.assign(state.typeOptions[idx].custom || {}, obj.custom || {});
1732
1736
  obj = Object.assign(state.typeOptions[idx], obj);
1733
1737
  state.typeOptions.splice(idx, 1, obj);
1734
1738
  } else {
@@ -2475,7 +2475,7 @@ export default class Namespace {
2475
2475
  get isObscure(): any;
2476
2476
  get projectId(): any;
2477
2477
  get project(): any;
2478
- get groupByLabel(): any;
2478
+ get groupById(): any;
2479
2479
  get projectNameSort(): any;
2480
2480
  get istioInstalled(): boolean;
2481
2481
  get injectionEnabled(): boolean;
@@ -3498,6 +3498,13 @@ export function abbreviateClusterName(input: string): string;
3498
3498
  export function labelForAddon(store: any, name: any, configuration?: boolean): any;
3499
3499
  export function filterOutDeprecatedPatchVersions(allVersions: any, currentVersion: any): any;
3500
3500
  export function getAllOptionsAfterCurrentVersion(store: any, versions: any, currentVersion: any, defaultVersion: any): any;
3501
+ export function initSchedulingCustomization(value: any, features: any, store: any, mode: any): Promise<{
3502
+ clusterAgentDefaultPC: any;
3503
+ clusterAgentDefaultPDB: any;
3504
+ schedulingCustomizationFeatureEnabled: any;
3505
+ schedulingCustomizationOriginallyEnabled: boolean;
3506
+ errors: any[];
3507
+ }>;
3501
3508
  }
3502
3509
 
3503
3510
  // @shell/utils/color
package/utils/cluster.js CHANGED
@@ -6,6 +6,10 @@ import { SETTING } from '@shell/config/settings';
6
6
  import { PaginationFilterField, PaginationParamFilter } from '@shell/types/store/pagination.types';
7
7
  import { compare, sortable } from '@shell/utils/version';
8
8
  import { sortBy } from '@shell/utils/sort';
9
+ import { SCHEDULING_CUSTOMIZATION } from '@shell/store/features';
10
+ import { _CREATE, _EDIT } from '@shell/config/query-params';
11
+ import isEmpty from 'lodash/isEmpty';
12
+ import { set } from '@shell/utils/object';
9
13
 
10
14
  /**
11
15
  * Combination of paginationFilterHiddenLocalCluster and paginationFilterOnlyKubernetesClusters
@@ -296,3 +300,34 @@ export function getAllOptionsAfterCurrentVersion(store, versions, currentVersion
296
300
 
297
301
  return sortedWithDeprecatedLabel;
298
302
  }
303
+
304
+ export async function initSchedulingCustomization(value, features, store, mode) {
305
+ const schedulingCustomizationFeatureEnabled = features(SCHEDULING_CUSTOMIZATION);
306
+ let clusterAgentDefaultPC = null;
307
+ let clusterAgentDefaultPDB = null;
308
+ let schedulingCustomizationOriginallyEnabled = false;
309
+ const errors = [];
310
+
311
+ try {
312
+ clusterAgentDefaultPC = JSON.parse((await store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.CLUSTER_AGENT_DEFAULT_PRIORITY_CLASS })).value) || null;
313
+ } catch (e) {
314
+ errors.push(e);
315
+ }
316
+ try {
317
+ clusterAgentDefaultPDB = JSON.parse((await store.dispatch('management/find', { type: MANAGEMENT.SETTING, id: SETTING.CLUSTER_AGENT_DEFAULT_POD_DISTRIBUTION_BUDGET })).value) || null;
318
+ } catch (e) {
319
+ errors.push(e);
320
+ }
321
+
322
+ if (schedulingCustomizationFeatureEnabled && mode === _CREATE && isEmpty(value?.clusterAgentDeploymentCustomization?.schedulingCustomization)) {
323
+ set(value, 'clusterAgentDeploymentCustomization.schedulingCustomization', { priorityClass: clusterAgentDefaultPC, podDisruptionBudget: clusterAgentDefaultPDB });
324
+ }
325
+
326
+ if (mode === _EDIT && !!value?.clusterAgentDeploymentCustomization?.schedulingCustomization) {
327
+ schedulingCustomizationOriginallyEnabled = true;
328
+ }
329
+
330
+ return {
331
+ clusterAgentDefaultPC, clusterAgentDefaultPDB, schedulingCustomizationFeatureEnabled, schedulingCustomizationOriginallyEnabled, errors
332
+ };
333
+ }
@@ -0,0 +1,20 @@
1
+ const FIELDS = {
2
+ NAME: 'pool.name',
3
+ QUANTITY: 'pool.quantity'
4
+ };
5
+
6
+ const RULESETS = [
7
+ {
8
+ path: FIELDS.QUANTITY,
9
+ rules: ['requiredInt', 'isPositive'],
10
+ },
11
+ {
12
+ path: FIELDS.NAME,
13
+ rules: ['required'],
14
+ }
15
+ ];
16
+
17
+ export const MACHINE_POOL_VALIDATION = {
18
+ FIELDS,
19
+ RULESETS
20
+ };
@@ -1,74 +0,0 @@
1
- <script>
2
- import { BadgeState } from '@components/BadgeState';
3
- import { stateDisplay } from '@shell/plugins/dashboard-store/resource-class';
4
-
5
- export default {
6
- props: {
7
- value: {
8
- type: String,
9
- default: ''
10
- }
11
- },
12
-
13
- components: { BadgeState },
14
-
15
- data() {
16
- const STATES = {
17
- cached: {
18
- color: 'info', icon: 'dot-open', label: 'Cached', compoundIcon: 'checkmark'
19
- },
20
- pending: {
21
- color: 'warning', icon: 'tag', label: 'In Progress', compoundIcon: 'info'
22
- },
23
- disabled: {
24
- color: 'error', icon: 'dot-half', label: 'Cache Disabled', compoundIcon: 'info'
25
- },
26
- };
27
-
28
- return {
29
- STATES,
30
- stateDisplay: '',
31
- stateBackground: ''
32
- };
33
- },
34
-
35
- watch: {
36
- value: {
37
- handler() {
38
- const out = this.value || 'pending';
39
- const color = this.colorForState(out);
40
-
41
- this.stateDisplay = stateDisplay(out);
42
- this.stateBackground = color.replace('text-', 'bg-');
43
- },
44
- immediate: true
45
- }
46
- },
47
-
48
- methods: {
49
- colorForState(state) {
50
- const key = (state).toLowerCase();
51
- let color;
52
-
53
- if ( this.STATES[key] && this.STATES[key].color ) {
54
- color = this.STATES[key].color;
55
- }
56
-
57
- if ( !color ) {
58
- color = 'info';
59
- }
60
-
61
- return `text-${ color }`;
62
- }
63
- }
64
- };
65
- </script>
66
-
67
- <template>
68
- <div>
69
- <BadgeState
70
- :color="stateBackground"
71
- :label="stateDisplay"
72
- />
73
- </div>
74
- </template>