@rancher/shell 3.0.5-rc.2 → 3.0.5-rc.3

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 (113) hide show
  1. package/assets/data/aws-regions.json +2 -0
  2. package/assets/styles/global/_layout.scss +0 -1
  3. package/assets/translations/en-us.yaml +61 -19
  4. package/assets/translations/zh-hans.yaml +0 -10
  5. package/chart/monitoring/index.vue +1 -1
  6. package/components/AsyncButton.vue +2 -0
  7. package/components/CodeMirror.vue +3 -3
  8. package/components/CruResource.vue +103 -15
  9. package/components/ExplorerProjectsNamespaces.vue +7 -2
  10. package/components/FixedBanner.vue +19 -5
  11. package/components/PaginatedResourceTable.vue +7 -0
  12. package/components/ResourceDetail/Masthead.vue +0 -1
  13. package/components/ResourceList/index.vue +2 -1
  14. package/components/SlideInPanelManager.vue +1 -2
  15. package/components/SortableTable/selection.js +1 -1
  16. package/components/Tabbed/index.vue +6 -0
  17. package/components/__tests__/AsyncButton.test.ts +39 -0
  18. package/components/__tests__/CruResource.test.ts +63 -0
  19. package/components/__tests__/PromptModal.test.ts +0 -2
  20. package/components/form/ArrayList.vue +134 -118
  21. package/components/form/BannerSettings.vue +145 -96
  22. package/components/form/KeyValue.vue +10 -7
  23. package/components/form/LabeledSelect.vue +9 -2
  24. package/components/form/MatchExpressions.vue +5 -1
  25. package/components/form/NameNsDescription.vue +1 -1
  26. package/components/form/ResourceSelector.vue +26 -23
  27. package/components/form/ResourceTabs/index.vue +2 -1
  28. package/components/form/Select.vue +9 -2
  29. package/components/form/UnitInput.vue +13 -0
  30. package/components/form/__tests__/ArrayList.test.ts +32 -0
  31. package/components/form/__tests__/KeyValue.test.ts +36 -0
  32. package/components/form/__tests__/LabeledSelect.test.ts +33 -0
  33. package/components/form/__tests__/Select.test.ts +34 -1
  34. package/components/form/__tests__/UnitInput.test.ts +23 -1
  35. package/components/formatter/ClusterLink.vue +5 -8
  36. package/components/formatter/Description.vue +30 -0
  37. package/components/formatter/__tests__/ClusterLink.test.ts +2 -32
  38. package/components/nav/NamespaceFilter.vue +1 -1
  39. package/components/nav/WindowManager/index.vue +1 -0
  40. package/config/product/explorer.js +16 -13
  41. package/config/product/manager.js +1 -28
  42. package/config/settings.ts +11 -13
  43. package/config/table-headers.js +7 -5
  44. package/detail/catalog.cattle.io.app.vue +0 -1
  45. package/detail/provisioning.cattle.io.cluster.vue +13 -3
  46. package/detail/service.vue +0 -1
  47. package/detail/workload/index.vue +21 -34
  48. package/dialog/ExtensionCatalogUninstallDialog.vue +14 -8
  49. package/edit/__tests__/service.test.ts +2 -1
  50. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +3 -14
  51. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +57 -62
  52. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +3 -14
  53. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.test.ts +72 -41
  54. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +17 -1
  55. package/edit/networking.k8s.io.networkpolicy/index.vue +18 -30
  56. package/edit/provisioning.cattle.io.cluster/index.vue +21 -73
  57. package/edit/service.vue +13 -28
  58. package/list/workload.vue +6 -1
  59. package/mixins/resource-fetch-api-pagination.js +55 -43
  60. package/mixins/resource-fetch.js +14 -5
  61. package/models/__tests__/workload.test.ts +1 -0
  62. package/models/cluster/node.js +1 -0
  63. package/models/cluster.js +32 -2
  64. package/models/management.cattle.io.cluster.js +0 -20
  65. package/models/management.cattle.io.node.js +7 -22
  66. package/models/management.cattle.io.nodepool.js +12 -0
  67. package/models/namespace.js +5 -0
  68. package/models/provisioning.cattle.io.cluster.js +18 -64
  69. package/models/service.js +24 -9
  70. package/models/workload.js +70 -31
  71. package/package.json +1 -1
  72. package/pages/c/_cluster/apps/charts/install.vue +0 -1
  73. package/pages/c/_cluster/explorer/index.vue +11 -0
  74. package/pages/c/_cluster/longhorn/index.vue +2 -2
  75. package/pages/c/_cluster/settings/banners.vue +56 -2
  76. package/pages/c/_cluster/settings/performance.vue +7 -26
  77. package/pages/home.vue +11 -52
  78. package/plugins/clean-html.js +2 -0
  79. package/plugins/dashboard-store/__tests__/actions.test.ts +4 -1
  80. package/plugins/dashboard-store/actions.js +122 -21
  81. package/plugins/dashboard-store/getters.js +74 -3
  82. package/plugins/dashboard-store/mutations.js +10 -5
  83. package/plugins/dashboard-store/resource-class.js +23 -3
  84. package/plugins/steve/__tests__/getters.test.ts +18 -11
  85. package/plugins/steve/__tests__/steve-class.test.ts +1 -0
  86. package/plugins/steve/actions.js +34 -12
  87. package/plugins/steve/getters.js +39 -10
  88. package/plugins/steve/steve-class.js +5 -0
  89. package/plugins/steve/steve-pagination-utils.ts +199 -37
  90. package/plugins/steve/worker/web-worker.advanced.js +3 -1
  91. package/rancher-components/Banner/Banner.test.ts +51 -3
  92. package/rancher-components/Banner/Banner.vue +28 -6
  93. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +5 -1
  94. package/rancher-components/Form/ToggleSwitch/ToggleSwitch.vue +21 -1
  95. package/store/features.js +0 -1
  96. package/store/type-map.utils.ts +45 -2
  97. package/types/fleet.d.ts +1 -1
  98. package/types/kube/kube-api.ts +22 -0
  99. package/types/resources/settings.d.ts +0 -4
  100. package/types/shell/index.d.ts +346 -289
  101. package/types/store/dashboard-store.types.ts +24 -1
  102. package/types/store/pagination.types.ts +19 -2
  103. package/utils/cluster.js +24 -20
  104. package/utils/grafana.js +1 -0
  105. package/utils/object.js +0 -12
  106. package/utils/pagination-utils.ts +6 -2
  107. package/utils/perf-setting.utils.ts +28 -0
  108. package/utils/selector-typed.ts +205 -0
  109. package/utils/selector.js +29 -6
  110. package/utils/uiplugins.ts +10 -6
  111. package/utils/v-sphere.ts +5 -1
  112. package/components/formatter/RKETemplateName.vue +0 -37
  113. package/dialog/SaveAsRKETemplateDialog.vue +0 -139
@@ -9,6 +9,7 @@ import { classify } from '@shell/plugins/dashboard-store/classify';
9
9
  import { NAMESPACE } from '@shell/config/types';
10
10
  import { handleKubeApiHeaderWarnings } from '@shell/plugins/steve/header-warnings';
11
11
  import { steveCleanForDownload } from '@shell/plugins/steve/resource-utils';
12
+ import paginationUtils from '@shell/utils/pagination-utils';
12
13
 
13
14
  export default {
14
15
 
@@ -17,7 +18,9 @@ export default {
17
18
  return await loadSchemas(ctx, watch);
18
19
  },
19
20
 
20
- async request({ state, dispatch, rootGetters }, pOpt ) {
21
+ async request({
22
+ state, dispatch, rootGetters, getters
23
+ }, pOpt ) {
21
24
  const opt = pOpt.opt || pOpt;
22
25
  const spoofedRes = await handleSpoofedRequest(rootGetters, 'cluster', opt);
23
26
 
@@ -82,6 +85,7 @@ export default {
82
85
  }
83
86
 
84
87
  let paginatedResult;
88
+ const isSteveCacheUrl = getters.isSteveCacheUrl(opt.url);
85
89
 
86
90
  while (true) {
87
91
  try {
@@ -92,24 +96,42 @@ export default {
92
96
  }
93
97
 
94
98
  if (!paginatedResult) {
95
- // First result, so store it
96
- paginatedResult = out;
99
+ const pageByNumber = isSteveCacheUrl && opt.url.includes(`pagesize=${ paginationUtils.defaultPageSize }`) ? {
100
+ total: out.count,
101
+ page: 1,
102
+ url: opt.url,
103
+ } : null;
104
+ const pageByLimit = !pageByNumber ? { } : null;
105
+
106
+ paginatedResult = {
107
+ // initialise some settings
108
+ pageByLimit,
109
+ pageByNumber,
110
+ // First result, so store it
111
+ out
112
+ };
97
113
  } else {
98
114
  // Subsequent request, so add to it
99
- paginatedResult.data = paginatedResult.data.concat(out.data);
115
+ paginatedResult.out.data = paginatedResult.out.data.concat(out.data);
100
116
  }
101
117
 
102
- if (out?.pagination?.next) {
103
- // More results to come, update options
104
- opt.url = out.pagination.next;
118
+ const { total, page, url } = paginatedResult.pageByNumber || {};
119
+
120
+ if (paginatedResult.pageByLimit && out?.pagination?.next) {
121
+ opt.url = out?.pagination?.next;
122
+ } else if (paginatedResult.pageByNumber && (total > paginationUtils.defaultPageSize * page)) {
123
+ paginatedResult.pageByNumber.page += 1;
124
+
125
+ opt.url = addParam(url, 'page', `${ paginatedResult.pageByNumber.page }`);
105
126
  } else {
106
127
  // No more results, so clear out the pagination section (which will be stale from the first request)
107
- delete paginatedResult.pagination?.first;
108
- delete paginatedResult.pagination?.last;
109
- delete paginatedResult.pagination?.next;
110
- delete paginatedResult.pagination?.partial;
128
+ delete paginatedResult.out.pagination?.first;
129
+ delete paginatedResult.out.pagination?.last;
130
+ delete paginatedResult.out.pagination?.next;
131
+ delete paginatedResult.out.pagination?.partial;
132
+ delete paginatedResult.out.continue;
111
133
 
112
- return paginatedResult;
134
+ return paginatedResult.out;
113
135
  }
114
136
  } catch (err) {
115
137
  return onError(err);
@@ -15,9 +15,10 @@ import { splitObjectPath } from '@shell/utils/string';
15
15
  import { parseType } from '@shell/models/schema';
16
16
  import {
17
17
  STEVE_AGE_COL,
18
- STEVE_ID_COL, STEVE_LIST_GROUPS, STEVE_NAMESPACE_COL, STEVE_STATE_COL
18
+ STEVE_ID_COL, STEVE_LIST_GROUPS, STEVE_NAME_COL, STEVE_NAMESPACE_COL, STEVE_STATE_COL
19
19
  } from '@shell/config/pagination-table-headers';
20
20
  import { createHeaders } from '@shell/store/type-map.utils';
21
+ import paginationUtils from '@shell/utils/pagination-utils';
21
22
 
22
23
  export const STEVE_MODEL_TYPES = {
23
24
  NORMAN: 'norman',
@@ -36,16 +37,34 @@ const GC_IGNORE_TYPES = {
36
37
  const steveRegEx = new RegExp('(/v1)|(\/k8s\/clusters\/[a-z0-9-]+\/v1)');
37
38
 
38
39
  export default {
39
- urlOptions: () => (url, opt, schema) => {
40
+ /**
41
+ * Is the url path a rancher steve one?
42
+ *
43
+ * Can be used to change behaviour given steve api
44
+ */
45
+ isSteveUrl: () => (urlPath) => steveRegEx.test(urlPath),
46
+ /**
47
+ * Is the url path a rancher steve one AND the steve cache is enabled?
48
+ *
49
+ * Can be used to change behaviour given steve cache api functionality
50
+ */
51
+ isSteveCacheUrl: (state, getters, rootState, rootGetters) => (urlPath) => getters.isSteveUrl(urlPath) && paginationUtils.isSteveCacheEnabled({ rootGetters }),
52
+
53
+ /**
54
+ * opt: ActionFindPageArgs
55
+ */
56
+ urlOptions: (state, getters) => (url, opt, schema) => {
40
57
  opt = opt || {};
41
- const parsedUrl = parse(url);
42
- const isSteve = steveRegEx.test(parsedUrl.path);
58
+ const parsedUrl = parse(url || '');
43
59
 
44
- const stevePagination = stevePaginationUtils.createParamsForPagination(schema, opt);
60
+ const isSteveUrl = getters.isSteveUrl(parsedUrl.path);
61
+ const stevePagination = stevePaginationUtils.createParamsForPagination({ schema, opt });
45
62
 
46
63
  if (stevePagination) {
47
64
  url += `${ (url.includes('?') ? '&' : '?') + stevePagination }`;
48
65
  } else {
66
+ const isSteveCacheUrl = getters.isSteveCacheUrl(parsedUrl.path);
67
+
49
68
  // labelSelector
50
69
  if ( opt.labelSelector ) {
51
70
  url += `${ url.includes('?') ? '&' : '?' }labelSelector=${ opt.labelSelector }`;
@@ -54,6 +73,7 @@ export default {
54
73
 
55
74
  // Filter
56
75
  if ( opt.filter ) {
76
+ // When ui-sql-cache is always on we should look to replace the usages of this with findPage (basically using the new filter definitions)
57
77
  url += `${ (url.includes('?') ? '&' : '?') }`;
58
78
  const keys = Object.keys(opt.filter);
59
79
 
@@ -64,13 +84,12 @@ export default {
64
84
  vals = [vals];
65
85
  }
66
86
 
67
- // Steve's filter options now support more complex filtering not yet implemented here #9341
68
- if (isSteve) {
87
+ if (isSteveUrl) {
69
88
  url += `${ (url.includes('filter=') ? '&' : 'filter=') }`;
70
89
  }
71
90
 
72
91
  const filterStrings = vals.map((val) => {
73
- return `${ encodeURI(key) }=${ encodeURI(val) }`;
92
+ return `${ encodeURI(key) }${ isSteveCacheUrl ? '~' : '=' }${ encodeURI(val) }`;
74
93
  });
75
94
  const urlEnding = url.charAt(url.length - 1);
76
95
  const nextStringConnector = ['&', '?', '='].includes(urlEnding) ? '' : '&';
@@ -97,13 +116,22 @@ export default {
97
116
  }
98
117
  // End: Limit
99
118
 
119
+ // Page Size
120
+ if (isSteveCacheUrl && opt.isCollection) {
121
+ // This is a steve url and the new cache is being used.
122
+ // Pre-cache there was always a max page size (given kube proxy). With cache there's not.
123
+ // So ensure we don't go backwards (and fetch crazy high resource counts) by adding a default
124
+ url += `${ url.includes('?') ? '&' : '?' }pagesize=${ paginationUtils.defaultPageSize }`;
125
+ }
126
+ // End: Page Size
127
+
100
128
  // Sort
101
129
  // Steve's sort options supports multi-column sorting and column specific sort orders, not implemented yet #9341
102
130
  const sortBy = opt.sortBy;
103
131
  const orderBy = opt.sortOrder;
104
132
 
105
133
  if ( sortBy ) {
106
- if (isSteve) {
134
+ if (isSteveUrl) {
107
135
  url += `${ url.includes('?') ? '&' : '?' }sort=${ (orderBy === 'desc' ? '-' : '') + encodeURI(sortBy) }`;
108
136
  } else {
109
137
  url += `${ url.includes('?') ? '&' : '?' }sort=${ encodeURI(sortBy) }`;
@@ -118,7 +146,7 @@ export default {
118
146
  // Exclude
119
147
  // excludeFields should be an array of strings representing the paths of the fields to exclude
120
148
  // only works on Steve but is ignored without error by Norman
121
- if (isSteve) {
149
+ if (isSteveUrl) {
122
150
  if (!Array.isArray(opt?.excludeFields)) {
123
151
  const excludeFields = ['metadata.managedFields'];
124
152
 
@@ -312,6 +340,7 @@ export default {
312
340
  typeOptions: typeMapGetters['optionsFor'](schema, true),
313
341
  schema,
314
342
  columns: {
343
+ name: STEVE_NAME_COL,
315
344
  state: STEVE_STATE_COL,
316
345
  namespace: STEVE_NAMESPACE_COL,
317
346
  age: STEVE_AGE_COL,
@@ -11,6 +11,7 @@ const STEVE_ADD = [
11
11
  'metadata.clusterName',
12
12
  'metadata.deletionGracePeriodSeconds',
13
13
  'metadata.generateName',
14
+ 'metadata.ownerReferences',
14
15
  ];
15
16
  const STEVE_NEVER_SAVE = NEVER_ADD.filter((na) => !STEVE_ADD.includes(na));
16
17
 
@@ -58,4 +59,8 @@ export default class SteveModel extends HybridModel {
58
59
 
59
60
  return val;
60
61
  }
62
+
63
+ paginationEnabled() {
64
+ return this.$getters['paginationEnabled'](this.type);
65
+ }
61
66
  }
@@ -15,9 +15,11 @@ import {
15
15
  HPA,
16
16
  SECRET
17
17
  } from '@shell/config/types';
18
- import { CAPI as CAPI_LAB_AND_ANO, CATTLE_PUBLIC_ENDPOINTS } from '@shell/config/labels-annotations';
18
+ import { CAPI as CAPI_LAB_AND_ANO, CATTLE_PUBLIC_ENDPOINTS, STORAGE } from '@shell/config/labels-annotations';
19
19
  import { Schema } from '@shell/plugins/steve/schema';
20
20
  import { PaginationSettingsStore } from '@shell/types/resources/settings';
21
+ import paginationUtils from '@shell/utils/pagination-utils';
22
+ import { KubeLabelSelector, KubeLabelSelectorExpression } from '@shell/types/kube/kube-api';
21
23
 
22
24
  /**
23
25
  * This is a workaround for a ts build issue found in check-plugins-build.
@@ -25,19 +27,30 @@ import { PaginationSettingsStore } from '@shell/types/resources/settings';
25
27
  * The build would error on <ns>.name, it somehow doesn't know about the steve model's properties (they are included in typegen)
26
28
  */
27
29
  interface Namespace extends ModelNamespace {
30
+ id: string;
28
31
  name: string;
32
+ metadata: {
33
+ name: string
34
+ }
29
35
  }
30
36
 
31
37
  class NamespaceProjectFilters {
32
38
  /**
33
39
  * User needs all resources.... except if there's some settings which should remove resources in specific circumstances
34
40
  */
35
- protected handlePrefAndSettingFilter(allNamespaces: Namespace[], showDynamicRancherNamespaces: boolean, productHidesSystemNamespaces: boolean): PaginationParamFilter[] {
41
+ protected handlePrefAndSettingFilter(args: {
42
+ allNamespaces: Namespace[],
43
+ showReservedRancherNamespaces: boolean,
44
+ productHidesSystemNamespaces: boolean,
45
+ }): PaginationParamFilter[] {
46
+ const { allNamespaces, showReservedRancherNamespaces, productHidesSystemNamespaces } = args;
47
+
36
48
  // These are AND'd together
37
49
  // Not ns 1 AND ns 2
38
50
  return allNamespaces.reduce((res, ns) => {
39
51
  // Links to ns.isObscure and covers things like `c-`, `user-`, etc (see OBSCURE_NAMESPACE_PREFIX)
40
- const hideObscure = showDynamicRancherNamespaces ? false : ns.isObscure;
52
+ const hideObscure = showReservedRancherNamespaces ? false : ns.isObscure;
53
+
41
54
  // Links to ns.isSystem and covers things like ns with system annotation, hardcoded list, etc
42
55
  const hideSystem = productHidesSystemNamespaces ? ns.isSystem : false;
43
56
 
@@ -58,7 +71,12 @@ class NamespaceProjectFilters {
58
71
  *
59
72
  * Users resources are those not in system namespaces
60
73
  */
61
- protected handleSystemOrUserFilter(allNamespaces: Namespace[], isAllSystem: boolean, isAllUser: boolean) {
74
+ protected handleSystemOrUserFilter(args: {
75
+ allNamespaces: Namespace[],
76
+ isAllSystem: boolean,
77
+ isAllUser: boolean,
78
+ }) {
79
+ const { allNamespaces, isAllSystem } = args;
62
80
  const allSystem = allNamespaces.filter((ns) => ns.isSystem);
63
81
 
64
82
  // > Neither of these use projectsOrNamespaces to avoid scenarios where the local cluster provides a namespace which has
@@ -118,6 +136,14 @@ class NamespaceProjectFilters {
118
136
  * Helper functions for steve pagination
119
137
  */
120
138
  class StevePaginationUtils extends NamespaceProjectFilters {
139
+ /**
140
+ * Match
141
+ * - a-z (case insensitive)
142
+ * - 0-9
143
+ * - `-`, `_`, `.`
144
+ */
145
+ static VALID_FIELD_VALUE_REGEX = /^[\w\-.]+$/;
146
+
121
147
  /**
122
148
  * Filtering with the vai cache supports specific fields
123
149
  * 1) Those listed here
@@ -168,7 +194,7 @@ class StevePaginationUtils extends NamespaceProjectFilters {
168
194
  { field: '_type' },
169
195
  { field: 'reason' },
170
196
  { field: 'involvedObject.kind' },
171
- // { field: 'involvedObject.uid' }, // Pending API Support - https://github.com/rancher/rancher/issues/48603
197
+ { field: 'involvedObject.uid' },
172
198
  { field: 'message' },
173
199
  ],
174
200
  [CATALOG.CLUSTER_REPO]: [
@@ -189,17 +215,17 @@ class StevePaginationUtils extends NamespaceProjectFilters {
189
215
  ],
190
216
  [SERVICE]: [
191
217
  { field: 'spec.type' },
192
- // { field: 'spec.clusterIP' }, // Pending API support (blocked https://github.com/rancher/rancher/issues/48473 (index fields)
218
+ { field: 'spec.clusterIP' },
193
219
  ],
194
220
  [INGRESS]: [
195
- // { field: 'spec.rules.host' }, // Pending API support (blocked https://github.com/rancher/rancher/issues/48473 (index fields)
196
- // { field: 'spec.ingressClassName' }, // Pending API support (blocked https://github.com/rancher/rancher/issues/48473 (index fields)
221
+ { field: 'spec.rules.host' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50526
222
+ { field: 'spec.ingressClassName' },
197
223
  ],
198
224
  [HPA]: [
199
- // { field: 'spec.scaleTargetRef.name' }, // Pending API support https://github.com/rancher/rancher/issues/48473 (hpa filtering fix)
200
- // { field: 'spec.minReplicas' }, // Pending API support https://github.com/rancher/rancher/issues/48473 (hpa filtering fix)
201
- // { field: 'spec.maxReplicas' }, // Pending API support https://github.com/rancher/rancher/issues/48473 (hpa filtering fix)
202
- // { field: 'spec.currentReplicas' }, // Pending API support https://github.com/rancher/rancher/issues/48473 (hpa filtering fix)
225
+ { field: 'spec.scaleTargetRef.name' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50527
226
+ { field: 'spec.minReplicas' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50527
227
+ { field: 'spec.maxReplicas' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50527
228
+ { field: 'spec.currentReplicas' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50527
203
229
  ],
204
230
  [PVC]: [
205
231
  { field: 'spec.volumeName' },
@@ -210,27 +236,37 @@ class StevePaginationUtils extends NamespaceProjectFilters {
210
236
  ],
211
237
  [STORAGE_CLASS]: [
212
238
  { field: 'provisioner' },
213
- // { field: `metadata.annotations[STORAGE.DEFAULT_STORAGE_CLASS]` }, // Pending API Support - https://github.com/rancher/rancher/issues/48453
239
+ { field: `metadata.annotations[${ STORAGE.DEFAULT_STORAGE_CLASS }]` },
214
240
  ],
215
241
  [CATALOG.APP]: [
216
242
  { field: 'spec.chart.metadata.name' }
217
243
  ],
218
244
  [WORKLOAD_TYPES.CRON_JOB]: [
219
- { field: `metadata.annotations[${ CATTLE_PUBLIC_ENDPOINTS }]` }
245
+ { field: `metadata.annotations[${ CATTLE_PUBLIC_ENDPOINTS }]` },
246
+ { field: 'spec.template.spec.containers.image' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50526
220
247
  ],
221
248
  [WORKLOAD_TYPES.DAEMON_SET]: [
222
- { field: `metadata.annotations[${ CATTLE_PUBLIC_ENDPOINTS }]` }
249
+ { field: `metadata.annotations[${ CATTLE_PUBLIC_ENDPOINTS }]` },
250
+ { field: 'spec.template.spec.containers.image' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50526
223
251
  ],
224
252
  [WORKLOAD_TYPES.DEPLOYMENT]: [
225
- { field: `metadata.annotations[${ CATTLE_PUBLIC_ENDPOINTS }]` }
253
+ { field: `metadata.annotations[${ CATTLE_PUBLIC_ENDPOINTS }]` },
254
+ { field: 'spec.template.spec.containers.image' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50526
226
255
  ],
227
256
  [WORKLOAD_TYPES.JOB]: [
228
- { field: `metadata.annotations[${ CATTLE_PUBLIC_ENDPOINTS }]` }
257
+ { field: `metadata.annotations[${ CATTLE_PUBLIC_ENDPOINTS }]` },
258
+ { field: 'spec.template.spec.containers.image' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50526
229
259
  ],
230
260
  [WORKLOAD_TYPES.STATEFUL_SET]: [
231
- { field: `metadata.annotations[${ CATTLE_PUBLIC_ENDPOINTS }]` }
232
- ]
233
-
261
+ { field: `metadata.annotations[${ CATTLE_PUBLIC_ENDPOINTS }]` },
262
+ { field: 'spec.template.spec.containers.image' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50526
263
+ ],
264
+ [WORKLOAD_TYPES.REPLICA_SET]: [
265
+ { field: 'spec.template.spec.containers.image' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50526
266
+ ],
267
+ [WORKLOAD_TYPES.REPLICATION_CONTROLLER]: [
268
+ { field: 'spec.template.spec.containers.image' }, // Pending API Support - BUG - https://github.com/rancher/rancher/issues/50526
269
+ ],
234
270
  }
235
271
 
236
272
  private convertArrayPath(path: string): string {
@@ -253,7 +289,7 @@ class StevePaginationUtils extends NamespaceProjectFilters {
253
289
  selection,
254
290
  isAllNamespaces,
255
291
  isLocalCluster,
256
- showDynamicRancherNamespaces,
292
+ showReservedRancherNamespaces,
257
293
  productHidesSystemNamespaces,
258
294
  }: {
259
295
  allNamespaces: Namespace[],
@@ -263,14 +299,18 @@ class StevePaginationUtils extends NamespaceProjectFilters {
263
299
  */
264
300
  isAllNamespaces: boolean,
265
301
  /**
266
- * Weird things be happening if the target cluster is local / upstream. Uses this to check what cluster we're in
302
+ * Weird things be happening if the target cluster is local / upstream. Use this to check what cluster we're in
267
303
  */
268
304
  isLocalCluster: boolean,
269
305
  /**
306
+ * User preference states we should show reserved rancher namespaces. Preference description "Show dynamic Namespaces managed by Rancher (not intended for editing or deletion)"
307
+ *
270
308
  * Links to ns.isObscure and covers things like `c-`, `user-`, etc (see OBSCURE_NAMESPACE_PREFIX)
271
309
  */
272
- showDynamicRancherNamespaces: boolean,
310
+ showReservedRancherNamespaces: boolean,
273
311
  /**
312
+ * Product config states that system namespaces should be hidden
313
+ *
274
314
  * Links to ns.isSystem and covers things like ns with system annotation, hardcoded list, etc
275
315
  */
276
316
  productHidesSystemNamespaces: boolean,
@@ -288,7 +328,7 @@ class StevePaginationUtils extends NamespaceProjectFilters {
288
328
  // - Only System Namespaces - Gimme resources in the system namespaces (which shouldn't be many namespaces)
289
329
  // - Only User Namespaces - Gimme resources NOT in system namespaces
290
330
  // - User selection - Gimme resources in specific Projects or Namespaces
291
- if (isAllNamespaces && (showDynamicRancherNamespaces && !productHidesSystemNamespaces)) {
331
+ if (isAllNamespaces && (showReservedRancherNamespaces && !productHidesSystemNamespaces)) {
292
332
  // No-op. Everything is returned
293
333
  return {
294
334
  projectsOrNamespaces: [],
@@ -303,9 +343,11 @@ class StevePaginationUtils extends NamespaceProjectFilters {
303
343
  // &filter=metadata.namespace=abc
304
344
  let filters: PaginationParamFilter[] = [];
305
345
 
306
- if (!showDynamicRancherNamespaces || productHidesSystemNamespaces) {
307
- // We need to hide dynamic namespaces ('c-', 'p-', etc) OR system namespaces
308
- filters = this.handlePrefAndSettingFilter(allNamespaces, showDynamicRancherNamespaces, productHidesSystemNamespaces);
346
+ if (!showReservedRancherNamespaces || productHidesSystemNamespaces) {
347
+ // We need to hide reserved namespaces ('c-', 'user-', etc) OR system namespaces
348
+ filters = this.handlePrefAndSettingFilter({
349
+ allNamespaces, showReservedRancherNamespaces, productHidesSystemNamespaces
350
+ });
309
351
  }
310
352
 
311
353
  const isAllSystem = selection[0] === NAMESPACE_FILTER_ALL_SYSTEM;
@@ -313,7 +355,9 @@ class StevePaginationUtils extends NamespaceProjectFilters {
313
355
 
314
356
  if (selection.length === 1 && (isAllSystem || isAllUser)) {
315
357
  // Filter by resources either in or not in system namespaces
316
- filters.push(...this.handleSystemOrUserFilter(allNamespaces, isAllSystem, isAllUser ));
358
+ filters.push(...this.handleSystemOrUserFilter({
359
+ allNamespaces, isAllSystem, isAllUser
360
+ }));
317
361
  } else {
318
362
  // User has one or more projects or namespaces
319
363
  const res = this.handleSelectionFilter(selection, isLocalCluster);
@@ -328,13 +372,13 @@ class StevePaginationUtils extends NamespaceProjectFilters {
328
372
  };
329
373
  }
330
374
 
331
- public createParamsForPagination(schema: Schema, opt: ActionFindPageArgs): string | undefined {
375
+ public createParamsForPagination({ schema, opt }: {schema?: Schema, opt: ActionFindPageArgs}): string | undefined {
332
376
  if (!opt.pagination) {
333
377
  return;
334
378
  }
335
379
 
336
380
  const params: string[] = [];
337
- const namespaceParam = this.convertPaginationParams(schema, opt.pagination.projectsOrNamespaces);
381
+ const namespaceParam = this.convertPaginationParams({ schema, filters: opt.pagination.projectsOrNamespaces });
338
382
 
339
383
  if (namespaceParam) {
340
384
  params.push(namespaceParam);
@@ -344,8 +388,11 @@ class StevePaginationUtils extends NamespaceProjectFilters {
344
388
  params.push(`page=${ opt.pagination.page }`);
345
389
  }
346
390
 
347
- if (opt.pagination.pageSize) {
391
+ if (!!opt.pagination.pageSize || opt.pagination.pageSize === 0) {
348
392
  params.push(`pagesize=${ opt.pagination.pageSize }`);
393
+ } else {
394
+ // Prevent unlimited resources in response
395
+ params.push(`pagesize=${ paginationUtils.defaultPageSize }`);
349
396
  }
350
397
 
351
398
  if (opt.pagination.sort?.length) {
@@ -365,12 +412,20 @@ class StevePaginationUtils extends NamespaceProjectFilters {
365
412
  params.push(`sort=${ joined }`);
366
413
 
367
414
  if (validateFields.invalid.length) {
368
- console.warn(`Pagination API does not support sorting '${ schema.id }' by the requested fields: ${ uniq(validateFields.invalid).join(', ') }`); // eslint-disable-line no-console
415
+ console.warn(`Pagination API does not support sorting '${ schema?.id || opt.url }' by the requested fields: ${ uniq(validateFields.invalid).join(', ') }`); // eslint-disable-line no-console
369
416
  }
370
417
  }
371
418
 
372
419
  if (opt.pagination.filters?.length) {
373
- const filters = this.convertPaginationParams(schema, opt.pagination.filters);
420
+ const filters = this.convertPaginationParams({ schema, filters: opt.pagination.filters });
421
+
422
+ if (filters) {
423
+ params.push(filters);
424
+ }
425
+ }
426
+
427
+ if (opt.pagination.labelSelector) {
428
+ const filters = this.convertLabelSelectorPaginationParams({ labelSelector: opt.pagination.labelSelector });
374
429
 
375
430
  if (filters) {
376
431
  params.push(filters);
@@ -386,7 +441,7 @@ class StevePaginationUtils extends NamespaceProjectFilters {
386
441
  /**
387
442
  * Check if the API supports filtering by this field
388
443
  */
389
- private validateField(state: { checked: string[], invalid: string[]}, schema: Schema, field?: string) {
444
+ private validateField(state: { checked: string[], invalid: string[]}, schema?: Schema, field?: string) {
390
445
  if (!field) {
391
446
  return; // no field, so not invalid
392
447
  }
@@ -400,6 +455,7 @@ class StevePaginationUtils extends NamespaceProjectFilters {
400
455
  // First check in our hardcoded list of supported filters
401
456
  if (
402
457
  process.env.NODE_ENV === 'dev' &&
458
+ !!schema &&
403
459
  [
404
460
  StevePaginationUtils.VALID_FIELDS[''], // Global
405
461
  StevePaginationUtils.VALID_FIELDS[schema.id], // Type specific
@@ -430,7 +486,7 @@ class StevePaginationUtils extends NamespaceProjectFilters {
430
486
  /**
431
487
  * Convert our {@link PaginationParam} definition of params to a set of url params
432
488
  */
433
- private convertPaginationParams(schema: Schema, filters: PaginationParam[] = []): string {
489
+ private convertPaginationParams({ schema, filters = [] }: {schema?: Schema, filters: PaginationParam[]}): string {
434
490
  const validateFields = {
435
491
  checked: new Array<string>(),
436
492
  invalid: new Array<string>(),
@@ -451,8 +507,9 @@ class StevePaginationUtils extends NamespaceProjectFilters {
451
507
  // != not exact match (!equals + exact)
452
508
  // !~ not partial match (!equals + !exact)
453
509
  const operator = `${ field.equals ? '' : '!' }${ field.exact ? '=' : '~' }`;
510
+ const quotedValue = StevePaginationUtils.VALID_FIELD_VALUE_REGEX.test(value) ? value : `"${ value }"`;
454
511
 
455
- return `${ this.convertArrayPath(field.field) }${ operator }${ value }`;
512
+ return `${ this.convertArrayPath(field.field) }${ operator }${ quotedValue }`;
456
513
  }
457
514
 
458
515
  return field.value;
@@ -470,11 +527,116 @@ class StevePaginationUtils extends NamespaceProjectFilters {
470
527
  const res = Object.keys(unique).join('&'); // This means AND
471
528
 
472
529
  if (validateFields.invalid.length) {
473
- console.warn(`Pagination API does not support filtering '${ schema.id }' by the requested fields: ${ uniq(validateFields.invalid).join(', ') }`); // eslint-disable-line no-console
530
+ console.warn(`Pagination API does not support filtering '${ schema?.id || 'unknown' }' by the requested fields: ${ uniq(validateFields.invalid).join(', ') }`); // eslint-disable-line no-console
474
531
  }
475
532
 
476
533
  return res;
477
534
  }
535
+
536
+ /**
537
+ * Convert kube labelSelector object into steve filter params
538
+ *
539
+ * A lot of the requirements and details are taken directly from
540
+ * https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
541
+ */
542
+ private convertLabelSelectorPaginationParams({ labelSelector }: { labelSelector: KubeLabelSelector}): string {
543
+ // Get a list of matchExpressions
544
+ const expressions: KubeLabelSelectorExpression[] = labelSelector.matchExpressions ? [...labelSelector.matchExpressions] : [];
545
+
546
+ // matchLabels are just simpler versions of matchExpressions, for ease convert them
547
+ if (labelSelector.matchLabels) {
548
+ Object.entries(labelSelector.matchLabels).forEach(([key, value]) => {
549
+ const expression: KubeLabelSelectorExpression = {
550
+ key,
551
+ values: [value],
552
+ operator: 'In'
553
+ };
554
+
555
+ expressions.push(expression);
556
+ });
557
+ }
558
+
559
+ // concert all matchExpressions into string params
560
+ const filters: string[] = expressions.reduce((res, exp) => {
561
+ const labelKey = `metadata.labels[${ exp.key }]`;
562
+
563
+ switch (exp.operator) {
564
+ case 'In':
565
+ if (!exp.values?.length) {
566
+ console.error(`Skipping labelSelector to API filter param conversion for ${ exp.key }(IN) as no value was supplied`); // eslint-disable-line no-console
567
+
568
+ return res;
569
+ }
570
+
571
+ // foo IN [bar] => ?filter=foo+IN+(bar)
572
+ // foo IN [bar, baz2] => ?filter=foo+IN+(bar,baz2)
573
+ res.push(`filter=${ labelKey } IN (${ exp.values.join(',') })`);
574
+ break;
575
+ case 'NotIn':
576
+
577
+ if (!exp.values?.length) {
578
+ console.error(`Skipping labelSelector to API filter param conversion for ${ exp.key }(NOTIN) as no value was supplied`); // eslint-disable-line no-console
579
+
580
+ return res;
581
+ }
582
+
583
+ // aaa NotIn [bar, baz2]=> ?filter=foo+NOTIN+(bar,baz2)
584
+ res.push(`filter=${ labelKey } NOTIN (${ exp.values.join(',') })`);
585
+ break;
586
+ case 'Exists':
587
+
588
+ if (exp.values?.length) {
589
+ console.error(`Skipping labelSelector to API filter param conversion for ${ exp.key }(Exists) as no value was supplied`); // eslint-disable-line no-console
590
+
591
+ return res;
592
+ }
593
+
594
+ // bbb Exists=> ?filter=bbb
595
+ res.push(`filter=${ labelKey }`);
596
+ break;
597
+ case 'DoesNotExist':
598
+ if (exp.values?.length) {
599
+ console.error(`Skipping labelSelector to API filter param conversion for ${ exp.key }(DoesNotExist) as no value was supplied`); // eslint-disable-line no-console
600
+
601
+ return res;
602
+ }
603
+
604
+ // ccc DoesNotExist ?filter=!bbb. # or %21bbb
605
+ res.push(`filter=!${ labelKey }`);
606
+ break;
607
+ case 'Gt':
608
+ // Currently broken - see https://github.com/rancher/rancher/issues/50057
609
+ // Only applicable to node affinity (atm) - https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#operators
610
+
611
+ if (typeof exp.values !== 'string') {
612
+ console.error(`Skipping labelSelector to API filter param conversion for ${ exp.key }(Gt) as no value was supplied`); // eslint-disable-line no-console
613
+
614
+ return res;
615
+ }
616
+
617
+ // ddd Gt 1=> ?filter=ddd+>+1
618
+ res.push(`filter=${ labelKey } > (${ exp.values })`);
619
+ break;
620
+ case 'Lt':
621
+ // Currently broken - see https://github.com/rancher/rancher/issues/50057
622
+ // Only applicable to node affinity (atm) - https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#operators
623
+ if (typeof exp.values !== 'string') {
624
+ console.error(`Skipping labelSelector to API filter param conversion for ${ exp.key }(Lt) as no value was supplied`); // eslint-disable-line no-console
625
+
626
+ return res;
627
+ }
628
+
629
+ // eee Lt 2=> ?filter=eee+<+2
630
+ res.push(`filter=${ labelKey } < (${ exp.values })`);
631
+ break;
632
+ }
633
+
634
+ return res;
635
+ }, [] as string[]);
636
+
637
+ // "All of the requirements, from both matchLabels and matchExpressions are ANDed together -- they must all be satisfied in order to match"
638
+ return filters.join('&');
639
+ }
478
640
  }
479
641
 
480
642
  export const PAGINATION_SETTINGS_STORE_DEFAULTS: PaginationSettingsStore = {
@@ -314,7 +314,9 @@ self.onmessage = (e) => {
314
314
  if (workerActions[action]) {
315
315
  workerActions[action](e?.data[action]);
316
316
  } else {
317
- console.warn('no associated action for:', action); // eslint-disable-line no-console
317
+ // This catches any window sendMessage event. We're hitting this on hot-reload of code where somehow this file is loaded
318
+ // Could be related to extensions, which have their own version of this
319
+ console.debug('no associated action for:', action); // eslint-disable-line no-console
318
320
  }
319
321
  });
320
322
  }; // bind everything to the worker's onmessage handler via the workerActions