@rancher/shell 0.3.8 → 0.3.10

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 (145) hide show
  1. package/assets/translations/en-us.yaml +47 -26
  2. package/assets/translations/zh-hans.yaml +82 -16
  3. package/babel.config.js +17 -4
  4. package/chart/istio.vue +11 -11
  5. package/chart/rancher-backup/S3.vue +1 -1
  6. package/components/AsyncButton.vue +2 -2
  7. package/components/ButtonGroup.vue +1 -1
  8. package/components/CodeMirror.vue +146 -14
  9. package/components/CompoundStatusBadge.vue +1 -1
  10. package/components/ContainerResourceLimit.vue +14 -1
  11. package/components/CopyCode.vue +1 -1
  12. package/components/CruResource.vue +21 -5
  13. package/components/DetailTop.vue +1 -1
  14. package/components/ExplorerProjectsNamespaces.vue +8 -4
  15. package/components/GlobalRoleBindings.vue +1 -1
  16. package/components/GroupPanel.vue +57 -0
  17. package/components/HarvesterServiceAddOnConfig.vue +2 -117
  18. package/components/ResourceDetail/Masthead.vue +1 -1
  19. package/components/ResourceList/Masthead.vue +0 -6
  20. package/components/ResourceList/ResourceLoadingIndicator.vue +1 -9
  21. package/components/ResourceList/index.vue +7 -6
  22. package/components/ResourceTable.vue +13 -3
  23. package/components/SortableTable/THead.vue +3 -3
  24. package/components/SortableTable/index.vue +3 -3
  25. package/components/Tabbed/Tab.vue +1 -1
  26. package/components/Tabbed/index.vue +1 -1
  27. package/components/Wizard.vue +9 -6
  28. package/components/YamlEditor.vue +2 -2
  29. package/components/__tests__/NamespaceFilter.test.ts +26 -7
  30. package/components/auth/RoleDetailEdit.vue +1 -1
  31. package/components/auth/SelectPrincipal.vue +1 -1
  32. package/components/fleet/FleetRepos.vue +1 -1
  33. package/components/form/ArrayList.vue +2 -2
  34. package/components/form/KeyValue.vue +37 -3
  35. package/components/form/Labels.vue +34 -14
  36. package/components/form/MatchExpressions.vue +120 -21
  37. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  38. package/components/form/NameNsDescription.vue +1 -1
  39. package/components/form/NodeAffinity.vue +54 -4
  40. package/components/form/PlusMinus.vue +2 -2
  41. package/components/form/PodAffinity.vue +160 -47
  42. package/components/form/Probe.vue +1 -1
  43. package/components/form/ProjectMemberEditor.vue +8 -4
  44. package/components/form/ResourceQuota/NamespaceRow.vue +1 -1
  45. package/components/form/ServicePorts.vue +2 -2
  46. package/components/form/Tolerations.vue +70 -7
  47. package/components/form/WorkloadPorts.vue +2 -1
  48. package/components/form/__tests__/ArrayList.test.ts +3 -3
  49. package/components/form/__tests__/KeyValue.test.ts +17 -0
  50. package/components/form/__tests__/MatchExpressions.test.ts +1 -1
  51. package/components/formatter/ClusterLink.vue +3 -3
  52. package/components/formatter/LiveDate.vue +1 -1
  53. package/components/formatter/PodImages.vue +1 -1
  54. package/components/formatter/RKETemplateName.vue +1 -1
  55. package/components/formatter/Shortened.vue +1 -1
  56. package/components/nav/Header.vue +9 -7
  57. package/components/nav/NamespaceFilter.vue +103 -54
  58. package/config/labels-annotations.js +8 -5
  59. package/config/settings.ts +8 -6
  60. package/config/types.js +6 -4
  61. package/core/plugin-routes.ts +26 -7
  62. package/detail/provisioning.cattle.io.cluster.vue +4 -4
  63. package/edit/cis.cattle.io.clusterscan.vue +1 -1
  64. package/edit/configmap.vue +33 -6
  65. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +19 -149
  66. package/edit/logging-flow/index.vue +2 -2
  67. package/edit/logging.banzaicloud.io.output/providers/elasticsearch.vue +12 -0
  68. package/edit/logging.banzaicloud.io.output/providers/opensearch.vue +12 -0
  69. package/edit/management.cattle.io.project.vue +7 -0
  70. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +1 -1
  71. package/edit/monitoring.coreos.com.alertmanagerconfig/routeConfig.vue +2 -2
  72. package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +11 -8
  73. package/edit/networking.k8s.io.networkpolicy/PolicyRule.vue +2 -2
  74. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +12 -4
  75. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +140 -0
  76. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/mock.json +158 -0
  77. package/edit/networking.k8s.io.networkpolicy/__tests__/utils/selectors.ts +45 -0
  78. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
  79. package/edit/provisioning.cattle.io.cluster/AgentConfiguration.vue +326 -0
  80. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +1 -1
  81. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -1
  82. package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +2 -2
  83. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +143 -169
  84. package/edit/provisioning.cattle.io.cluster/index.vue +1 -0
  85. package/edit/provisioning.cattle.io.cluster/rke2.vue +75 -6
  86. package/edit/resources.cattle.io.restore.vue +2 -2
  87. package/edit/service.vue +22 -3
  88. package/edit/storage.k8s.io.storageclass/index.vue +1 -1
  89. package/edit/workload/Job.vue +2 -2
  90. package/edit/workload/index.vue +1 -1
  91. package/edit/workload/mixins/workload.js +7 -1
  92. package/edit/workload/storage/__tests__/Storage.test.ts +84 -5
  93. package/initialize/index.js +1 -0
  94. package/layouts/default.vue +1 -1
  95. package/mixins/chart.js +1 -1
  96. package/mixins/resource-fetch-namespaced.js +19 -27
  97. package/mixins/resource-fetch.js +0 -5
  98. package/models/__tests__/namespace.test.ts +125 -0
  99. package/models/batch.cronjob.js +18 -3
  100. package/models/management.cattle.io.project.js +6 -1
  101. package/models/persistentvolume.js +1 -1
  102. package/models/workload.js +1 -1
  103. package/models/workload.service.js +22 -7
  104. package/package.json +17 -6
  105. package/pages/auth/login.vue +47 -49
  106. package/pages/c/_cluster/apps/charts/chart.vue +1 -1
  107. package/pages/c/_cluster/apps/charts/install.vue +42 -51
  108. package/pages/c/_cluster/explorer/index.vue +1 -1
  109. package/pages/c/_cluster/monitoring/index.vue +1 -1
  110. package/pages/c/_cluster/settings/performance.vue +53 -18
  111. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  112. package/pages/c/_cluster/uiplugins/index.vue +16 -5
  113. package/pages/home.vue +1 -1
  114. package/pages/prefs.vue +18 -2
  115. package/plugins/clean-html-directive.js +1 -1
  116. package/plugins/clean-tooltip-directive.js +33 -0
  117. package/plugins/codemirror.js +158 -0
  118. package/plugins/dashboard-store/actions.js +4 -2
  119. package/plugins/dashboard-store/getters.js +6 -0
  120. package/plugins/dashboard-store/mutations.js +2 -2
  121. package/plugins/plugin.js +6 -1
  122. package/plugins/steve/actions.js +1 -1
  123. package/plugins/steve/getters.js +14 -3
  124. package/plugins/steve/resourceWatcher.js +36 -62
  125. package/plugins/steve/subscribe.js +137 -21
  126. package/plugins/steve/worker/index.js +7 -1
  127. package/plugins/steve/worker/web-worker.advanced.js +26 -8
  128. package/plugins/steve/worker/web-worker.basic.js +23 -4
  129. package/public/index.html +1 -1
  130. package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -2
  131. package/rancher-components/components/Form/Radio/RadioGroup.vue +2 -2
  132. package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +1 -1
  133. package/store/index.js +16 -61
  134. package/store/store-types.js +5 -0
  135. package/store/type-map.js +1 -1
  136. package/types/shell/index.d.ts +42 -7
  137. package/utils/__tests__/create-yaml.test.ts +63 -0
  138. package/utils/array.ts +4 -0
  139. package/utils/create-yaml.js +105 -8
  140. package/utils/namespace-filter.js +17 -5
  141. package/utils/projectAndNamespaceFiltering.utils.ts +62 -0
  142. package/utils/selector.js +6 -5
  143. package/utils/settings.ts +17 -7
  144. package/vue.config.js +2 -2
  145. package/models/k8s.cni.cncf.io.networkattachmentdefinition.js +0 -93
@@ -9,7 +9,8 @@
9
9
 
10
10
  import { addObject, clear, removeObject } from '@shell/utils/array';
11
11
  import { get } from '@shell/utils/object';
12
- import { SCHEMA } from '@shell/config/types';
12
+ import { SCHEMA, MANAGEMENT } from '@shell/config/types';
13
+ import { SETTING } from '@shell/config/settings';
13
14
  import { CSRF } from '@shell/config/cookies';
14
15
  import { getPerformanceSetting } from '@shell/utils/settings';
15
16
  import Socket, {
@@ -29,16 +30,25 @@ import { DATE_FORMAT, TIME_FORMAT } from '@shell/store/prefs';
29
30
  import { escapeHtml } from '@shell/utils/string';
30
31
  import { keyForSubscribe } from '@shell/plugins/steve/resourceWatcher';
31
32
  import { waitFor } from '@shell/utils/async';
33
+ import { WORKER_MODES } from './worker';
34
+ import pAndNFiltering from '@shell/utils/projectAndNamespaceFiltering.utils';
32
35
 
33
36
  import { BLANK_CLUSTER } from '@shell/store/index.js';
37
+ import { STORE } from '@shell/store/store-types';
34
38
 
35
39
  // minimum length of time a disconnect notification is shown
36
40
  const MINIMUM_TIME_NOTIFIED = 3000;
37
41
 
38
- const waitForManagement = (store) => {
39
- const managementReady = () => store.state?.managementReady;
42
+ const workerQueues = {};
40
43
 
41
- return waitFor(managementReady, 'Management');
44
+ const supportedStores = [STORE.CLUSTER, STORE.RANCHER, STORE.MANAGEMENT];
45
+
46
+ const waitForSettingsSchema = (store) => {
47
+ return waitFor(() => !!store.getters['management/byId'](SCHEMA, MANAGEMENT.SETTING));
48
+ };
49
+
50
+ const waitForSettings = (store) => {
51
+ return waitFor(() => !!store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.UI_PERFORMANCE));
42
52
  };
43
53
 
44
54
  const isAdvancedWorker = (ctx) => {
@@ -46,7 +56,7 @@ const isAdvancedWorker = (ctx) => {
46
56
  const storeName = getters.storeName;
47
57
  const clusterId = rootGetters.clusterId;
48
58
 
49
- if (storeName !== 'cluster' || clusterId === BLANK_CLUSTER) {
59
+ if (!supportedStores.includes(storeName) || (clusterId === BLANK_CLUSTER && storeName === STORE.CLUSTER)) {
50
60
  return false;
51
61
  }
52
62
 
@@ -55,19 +65,33 @@ const isAdvancedWorker = (ctx) => {
55
65
  return perfSetting?.advancedWorker.enabled;
56
66
  };
57
67
 
58
- // We only create a worker for the cluster store
59
68
  export async function createWorker(store, ctx) {
60
69
  const { getters, dispatch } = ctx;
61
70
  const storeName = getters.storeName;
62
71
 
63
72
  store.$workers = store.$workers || {};
64
73
 
65
- if (storeName !== 'cluster') {
74
+ if (!supportedStores.includes(storeName)) {
66
75
  return;
67
76
  }
68
77
 
69
- await waitForManagement(store);
70
- // getting perf setting in a separate constant here because it'll provide other values we'll want later.
78
+ if (!store.$workers[storeName]) {
79
+ // we know we need a worker at this point but we don't know which one so we're creating a mock interface
80
+ // it will simply queue up any messages for the real worker to process when it loads up
81
+ store.$workers[storeName] = {
82
+ postMessage: (msg) => {
83
+ if (workerQueues[storeName]) {
84
+ workerQueues[storeName].push(msg);
85
+ } else {
86
+ workerQueues[storeName] = [msg];
87
+ }
88
+ },
89
+ mode: WORKER_MODES.WAITING
90
+ };
91
+ }
92
+
93
+ await waitForSettingsSchema(store);
94
+ await waitForSettings(store);
71
95
  const advancedWorker = isAdvancedWorker(ctx);
72
96
 
73
97
  const workerActions = {
@@ -81,21 +105,30 @@ export async function createWorker(store, ctx) {
81
105
  }
82
106
  },
83
107
  batchChanges: (batch) => {
84
- dispatch('batchChanges', batch);
108
+ dispatch('batchChanges', namespaceHandler.validateBatchChange(ctx, batch));
85
109
  },
86
110
  dispatch: (msg) => {
87
111
  dispatch(`ws.${ msg.name }`, msg);
88
112
  },
113
+ redispatch: (msg) => {
114
+ /**
115
+ * because we had to queue up some messages prior to loading the worker:
116
+ * the basic worker will need to redispatch some of the queued messages back to the UI thread
117
+ */
118
+ Object.entries(msg).forEach(([action, params]) => {
119
+ dispatch(action, params);
120
+ });
121
+ },
89
122
  [EVENT_CONNECT_ERROR]: (e) => {
90
123
  dispatch('error', e );
91
124
  },
92
125
  [EVENT_DISCONNECT_ERROR]: (e) => {
93
126
  dispatch('error', e );
94
- }
127
+ },
95
128
  };
96
129
 
97
- if (!store.$workers[storeName]) {
98
- const workerMode = advancedWorker ? 'advanced' : 'basic';
130
+ if (!store.$workers[storeName] || store.$workers[storeName].mode === WORKER_MODES.WAITING) {
131
+ const workerMode = advancedWorker ? WORKER_MODES.ADVANCED : WORKER_MODES.BASIC;
99
132
  const worker = store.steveCreateWorker(workerMode);
100
133
 
101
134
  store.$workers[storeName] = worker;
@@ -115,6 +148,12 @@ export async function createWorker(store, ctx) {
115
148
  });
116
149
  };
117
150
  }
151
+
152
+ while (workerQueues[storeName]?.length) {
153
+ const message = workerQueues[storeName].shift();
154
+
155
+ store.$workers[storeName].postMessage(message);
156
+ }
118
157
  }
119
158
 
120
159
  export function equivalentWatch(a, b) {
@@ -140,7 +179,67 @@ export function equivalentWatch(a, b) {
140
179
  return true;
141
180
  }
142
181
 
143
- function queueChange({ getters, state }, { data, revision }, load, label) {
182
+ /**
183
+ * Sockets will not be able to subscribe to more than one namespace. If this is requested we pretend to handle it
184
+ * - Changes to all resources are monitored (no namespace provided in sub)
185
+ * - We ignore any events not from a required namespace (we have the conversion of project --> namespaces already)
186
+ */
187
+ const namespaceHandler = {
188
+ /**
189
+ * Note - namespace can be a list of projects or namespaces
190
+ */
191
+ subscribeNamespace: (namespace) => {
192
+ if (pAndNFiltering.isApplicable({ namespaced: namespace }) && namespace.length) {
193
+ return undefined; // AKA sub to everything
194
+ }
195
+
196
+ return namespace;
197
+ },
198
+
199
+ validChange: ({ getters, rootGetters }, type, data) => {
200
+ const haveNamespace = getters.haveNamespace(type);
201
+
202
+ if (haveNamespace?.length) {
203
+ const namespaces = rootGetters.activeNamespaceCache;
204
+
205
+ if (!namespaces[data.metadata.namespace]) {
206
+ return false;
207
+ }
208
+ }
209
+
210
+ return true;
211
+ },
212
+
213
+ validateBatchChange: ({ getters, rootGetters }, batch) => {
214
+ const namespaces = rootGetters.activeNamespaceCache;
215
+
216
+ Object.entries(batch).forEach(([type, entries]) => {
217
+ const haveNamespace = getters.haveNamespace(type);
218
+
219
+ if (!haveNamespace?.length) {
220
+ return;
221
+ }
222
+
223
+ const schema = getters.schemaFor(type);
224
+
225
+ if (!schema?.attributes?.namespaced) {
226
+ return;
227
+ }
228
+
229
+ Object.keys(entries).forEach((id) => {
230
+ const namespace = id.split('/')[0];
231
+
232
+ if (!namespace || !namespaces[namespace]) {
233
+ delete entries[id];
234
+ }
235
+ });
236
+ });
237
+
238
+ return batch;
239
+ }
240
+ };
241
+
242
+ function queueChange({ getters, state, rootGetters }, { data, revision }, load, label) {
144
243
  const type = getters.normalizeType(data.type);
145
244
 
146
245
  const entry = getters.typeEntry(type);
@@ -153,6 +252,10 @@ function queueChange({ getters, state }, { data, revision }, load, label) {
153
252
 
154
253
  // console.log(`${ label } Event [${ state.config.namespace }]`, data.type, data.id); // eslint-disable-line no-console
155
254
 
255
+ if (!namespaceHandler.validChange({ getters, rootGetters }, type, data)) {
256
+ return;
257
+ }
258
+
156
259
  if ( load ) {
157
260
  state.queue.push({
158
261
  action: 'dispatch',
@@ -212,7 +315,7 @@ const sharedActions = {
212
315
 
213
316
  const url = `${ state.config.baseUrl }/subscribe`;
214
317
  const maxTries = growlsDisabled(rootGetters) ? null : 3;
215
- const connectionMetadata = get(opt, 'metadata');
318
+ const metadata = get(opt, 'metadata');
216
319
 
217
320
  if (isAdvancedWorker(ctx)) {
218
321
  if (!this.$workers[getters.storeName]) {
@@ -222,7 +325,7 @@ const sharedActions = {
222
325
  // if the worker is in advanced mode then it'll contain it's own socket which it calls a 'watcher'
223
326
  this.$workers[getters.storeName].postMessage({
224
327
  createWatcher: {
225
- connectionMetadata,
328
+ metadata,
226
329
  url: `${ state.config.baseUrl }/subscribe`,
227
330
  csrf: this.$cookies.get(CSRF, { parseJSON: false }),
228
331
  maxTries
@@ -231,7 +334,7 @@ const sharedActions = {
231
334
  } else if ( socket ) {
232
335
  socket.setAutoReconnect(true);
233
336
  socket.setUrl(url);
234
- socket.connect(connectionMetadata);
337
+ socket.connect(metadata);
235
338
  } else {
236
339
  socket = new Socket(`${ state.config.baseUrl }/subscribe`, true, null, null, maxTries);
237
340
 
@@ -263,7 +366,7 @@ const sharedActions = {
263
366
  }
264
367
  }
265
368
  });
266
- socket.connect(connectionMetadata);
369
+ socket.connect(metadata);
267
370
  }
268
371
  },
269
372
 
@@ -297,6 +400,7 @@ const sharedActions = {
297
400
  type, selector, id, revision, namespace, stop, force
298
401
  } = params;
299
402
 
403
+ namespace = namespaceHandler.subscribeNamespace(namespace);
300
404
  type = getters.normalizeType(type);
301
405
 
302
406
  if (rootGetters['type-map/isSpoofed'](type)) {
@@ -356,7 +460,7 @@ const sharedActions = {
356
460
 
357
461
  const worker = this.$workers?.[getters.storeName] || {};
358
462
 
359
- if (worker.mode === 'advanced') {
463
+ if (worker.mode === WORKER_MODES.ADVANCED || worker.mode === WORKER_MODES.WAITING) {
360
464
  if ( force ) {
361
465
  msg.force = true;
362
466
  }
@@ -375,6 +479,8 @@ const sharedActions = {
375
479
  const { commit, getters, dispatch } = ctx;
376
480
 
377
481
  if (getters['schemaFor'](type)) {
482
+ namespace = namespaceHandler.subscribeNamespace(namespace);
483
+
378
484
  const obj = {
379
485
  type,
380
486
  id,
@@ -760,8 +866,18 @@ const defaultActions = {
760
866
  }
761
867
 
762
868
  // If we're trying to watch this event, attempt to re-watch
763
- if ( getters['schemaFor'](type) && getters['watchStarted'](obj) ) {
764
- commit('setWatchStopped', obj);
869
+ //
870
+ // To make life easier in the advanced worker `resource.stop` --> `watch` is handled here (basically for access to getters.nextResourceVersion)
871
+ // This means the concept of resource sub watch state needs massaging
872
+ const advancedWorker = msg.advancedWorker;
873
+ const localState = !advancedWorker;
874
+ const watchStarted = localState ? getters['watchStarted'](obj) : advancedWorker;
875
+
876
+ if ( getters['schemaFor'](type) && watchStarted) {
877
+ if (localState) {
878
+ commit('setWatchStopped', obj);
879
+ }
880
+
765
881
  dispatch('watch', obj);
766
882
  }
767
883
  },
@@ -3,10 +3,16 @@ import basicWorkerConstructor from '@shell/plugins/steve/worker/web-worker.basic
3
3
  // eslint-disable-next-line no-unused-vars
4
4
  import advancedWorkerConstructor from '@shell/plugins/steve/worker/web-worker.advanced.js';
5
5
 
6
+ export const WORKER_MODES = {
7
+ WAITING: 'waiting',
8
+ BASIC: 'basic',
9
+ ADVANCED: 'advanced'
10
+ };
11
+
6
12
  export default function storeWorker(mode, options = {}, closures = {}) {
7
13
  let worker;
8
14
 
9
- if (mode === 'advanced') {
15
+ if (mode === WORKER_MODES.ADVANCED) {
10
16
  worker = new advancedWorkerConstructor();
11
17
  } else {
12
18
  worker = new basicWorkerConstructor();
@@ -93,12 +93,12 @@ const workerActions = {
93
93
  }
94
94
  caches[SCHEMA].load(collection);
95
95
  },
96
- createWatcher: (metadata) => {
97
- trace('createWatcher', metadata);
96
+ createWatcher: (opt) => {
97
+ trace('createWatcher', opt);
98
98
 
99
99
  const {
100
- connectionMetadata, maxTries, url, csrf
101
- } = metadata;
100
+ metadata, maxTries, url, csrf
101
+ } = opt;
102
102
 
103
103
  if (!state.watcher) {
104
104
  state.watcher = new ResourceWatcher(url, true, null, null, maxTries, csrf);
@@ -119,6 +119,10 @@ const workerActions = {
119
119
  }
120
120
  });
121
121
 
122
+ state.watcher.addEventListener('resync', (e) => {
123
+ self.postMessage({ redispatch: { resyncWatch: e.detail.data } });
124
+ });
125
+
122
126
  state.watcher.addEventListener(EVENT_CONNECT_ERROR, (e) => {
123
127
  handleConnectionError(EVENT_CONNECT_ERROR, e, state.watcher);
124
128
  });
@@ -129,7 +133,7 @@ const workerActions = {
129
133
 
130
134
  state.watcher.setDebug(state.debugWorker);
131
135
 
132
- state.watcher.connect(connectionMetadata);
136
+ state.watcher.connect(metadata);
133
137
 
134
138
  // Flush the workerQueue
135
139
  while (state.workerQueue.length > 0) {
@@ -183,7 +187,8 @@ const workerActions = {
183
187
  resourceType,
184
188
  id,
185
189
  namespace,
186
- selector
190
+ selector,
191
+ force: msg.force,
187
192
  };
188
193
 
189
194
  state.watcher.watch(watchKey, resourceVersion, resourceVersionTime, watchObject, skipResourceVersion);
@@ -270,10 +275,23 @@ const resourceWatcherActions = {
270
275
  }
271
276
  },
272
277
  'resource.stop': (msg) => {
273
- // State is handled in the resourceWatcher, no need to bubble out to UI thread
278
+ trace('resource.stop', msg);
279
+
280
+ // State is handled in the resourceWatcher....
274
281
  const watchKey = watchKeyFromMessage(msg);
275
282
 
276
283
  removeFromWorkerQueue(watchKey);
284
+
285
+ // ... however we still want to bubble out to UI thread
286
+ // We'll save some hassle and ignore any resource.stop bubble if we're in error. the only thing that will clear that is a resync
287
+ if (!state.watcher?.watches[watchKey]?.error) {
288
+ // See comment in resourceWatcher 'resource.stop' handler, until we can resolve the resourceVersion within the resourceWatcher
289
+ // internally, we'll want to bubble this out to the UI thread. When that's resolved this won't be needed
290
+ resourceWatcherActions.dispatch({
291
+ ...msg,
292
+ advancedWorker: true,
293
+ });
294
+ }
277
295
  },
278
296
  'resource.error': (msg) => {
279
297
  // State is handled in the resourceWatcher, no need to bubble out to UI thread
@@ -287,7 +305,7 @@ const resourceWatcherActions = {
287
305
  /**
288
306
  * Covers message from UI Thread to Worker
289
307
  */
290
- onmessage = (e) => {
308
+ self.onmessage = (e) => {
291
309
  /* on the off chance there's more than key in the message, we handle them in the order that they "keys" method provides which is
292
310
  // good enough for now considering that we never send more than one message action at a time right now */
293
311
  const messageActions = Object.keys(e?.data);
@@ -6,7 +6,7 @@ const SCHEMA_FLUSH_TIMEOUT = 2500;
6
6
 
7
7
  const state = {
8
8
  store: '', // Store name
9
- flushTimer: undefined, // Timer to flush the schema chaneg queue
9
+ flushTimer: undefined, // Timer to flush the schema change queue
10
10
  queue: [], // Schema change queue
11
11
  schemas: {} // Map of schema id to hash to track when a schema actually changes
12
12
  };
@@ -42,6 +42,25 @@ function load(data) {
42
42
  self.postMessage({ load: data });
43
43
  }
44
44
 
45
+ // used for dispatching a function in the worker, primarily for redirecting messages intended for the advanced worker back to the UI thread
46
+ function redispatch(msg) {
47
+ self.postMessage({ redispatch: msg });
48
+ }
49
+
50
+ /**
51
+ * These actions aren't applicable to the basic worker, so bounce back to ui thread
52
+ *
53
+ * These are called when a queue of actions is flushed. Queue is populated from requests made before we know if worker is basic or advanced.
54
+ */
55
+ const advancedWorkerActions = {
56
+ watch: (msg) => {
57
+ redispatch({ send: msg });
58
+ },
59
+ createWatcher: (msg) => {
60
+ redispatch({ subscribe: msg });
61
+ }
62
+ };
63
+
45
64
  const workerActions = {
46
65
  onmessage: (e) => {
47
66
  /* on the off chance there's more than key in the message, we handle them in the order that they "keys" method provides which is
@@ -75,7 +94,6 @@ const workerActions = {
75
94
 
76
95
  state.schemas[schema.id] = hashObj(schema);
77
96
  });
78
- // console.log(JSON.parse(JSON.stringify(state.resources.schemas)));
79
97
  },
80
98
 
81
99
  // Called when schema is updated
@@ -91,7 +109,8 @@ const workerActions = {
91
109
 
92
110
  // Delete the schema from the map, so if it comes back we don't ignore it if the hash is the same
93
111
  delete state.schemas[id];
94
- }
112
+ },
113
+ ...advancedWorkerActions
95
114
  };
96
115
 
97
- onmessage = workerActions.onmessage; // bind everything to the worker's onmessage handler via the workerAction
116
+ self.onmessage = workerActions.onmessage; // bind everything to the worker's onmessage handler via the workerAction
package/public/index.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta charset="utf-8">
6
6
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
7
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
8
- <link rel="shortcut icon" type="image/x-icon" href="/public/favicon.png">
8
+ <link rel="shortcut icon" type="image/x-icon" href="/favicon.png">
9
9
  <title>Rancher</title>
10
10
  </head>
11
11
 
@@ -251,12 +251,12 @@ export default Vue.extend({
251
251
  <template v-else-if="label">{{ label }}</template>
252
252
  <i
253
253
  v-if="tooltipKey"
254
- v-tooltip="t(tooltipKey)"
254
+ v-clean-tooltip="t(tooltipKey)"
255
255
  class="checkbox-info icon icon-info icon-lg"
256
256
  />
257
257
  <i
258
258
  v-else-if="tooltip"
259
- v-tooltip="tooltip"
259
+ v-clean-tooltip="tooltip"
260
260
  class="checkbox-info icon icon-info icon-lg"
261
261
  />
262
262
  </slot>
@@ -184,12 +184,12 @@ export default Vue.extend({
184
184
  </template>
185
185
  <i
186
186
  v-if="tooltipKey"
187
- v-tooltip="t(tooltipKey)"
187
+ v-clean-tooltip="t(tooltipKey)"
188
188
  class="icon icon-info icon-lg"
189
189
  />
190
190
  <i
191
191
  v-else-if="tooltip"
192
- v-tooltip="tooltip"
192
+ v-clean-tooltip="tooltip"
193
193
  class="icon icon-info icon-lg"
194
194
  />
195
195
  </h3>
@@ -44,7 +44,7 @@ export default Vue.extend({
44
44
  >
45
45
  <template v-if="hover">
46
46
  <i
47
- v-tooltip="value.content ? { ...{content: value.content, classes: [`tooltip-${status}`]}, ...value } : value"
47
+ v-clean-tooltip="value.content ? { ...{content: value.content, classes: [`tooltip-${status}`]}, ...value } : value"
48
48
  :class="{'hover':!value, [iconClass]: true}"
49
49
  class="icon status-icon"
50
50
  />
package/store/index.js CHANGED
@@ -27,11 +27,13 @@ import {
27
27
  NAMESPACE_FILTER_NAMESPACED_PREFIX as NAMESPACED_PREFIX,
28
28
  NAMESPACE_FILTER_NAMESPACED_YES as NAMESPACED_YES,
29
29
  splitNamespaceFilterKey,
30
+ NAMESPACE_FILTER_NS_FULL_PREFIX,
30
31
  } from '@shell/utils/namespace-filter';
31
32
  import { allHash, allHashSettled } from '@shell/utils/promise';
32
33
  import { sortBy } from '@shell/utils/sort';
33
34
  import { addParam } from '@shell/utils/url';
34
35
  import semver from 'semver';
36
+ import { STORE } from '@shell/store/store-types';
35
37
 
36
38
  // Disables strict mode for all store instances to prevent warning about changing state outside of mutations
37
39
  // because it's more efficient to do that sometimes.
@@ -41,19 +43,19 @@ export const BLANK_CLUSTER = '_';
41
43
 
42
44
  export const plugins = [
43
45
  Steve({
44
- namespace: 'management',
46
+ namespace: STORE.MANAGEMENT,
45
47
  baseUrl: '/v1',
46
48
  modelBaseClass: BY_TYPE,
47
49
  supportsStream: false, // true, -- Disabled due to report that it's sometimes much slower in Chrome
48
50
  }),
49
51
  Steve({
50
- namespace: 'cluster',
52
+ namespace: STORE.CLUSTER,
51
53
  baseUrl: '', // URL is dynamically set for the selected cluster
52
54
  supportsStream: false, // true, -- Disabled due to report that it's sometimes much slower in Chrome
53
55
  supportsGc: true, // Enable garbage collection for this store only
54
56
  }),
55
57
  Steve({
56
- namespace: 'rancher',
58
+ namespace: STORE.RANCHER,
57
59
  baseUrl: '/v3',
58
60
  supportsStream: false, // The norman API doesn't support streaming
59
61
  modelBaseClass: STEVE_MODEL_TYPES.NORMAN,
@@ -225,7 +227,6 @@ export const state = () => {
225
227
  return {
226
228
  managementReady: false,
227
229
  clusterReady: false,
228
- isMultiCluster: false,
229
230
  isRancher: false,
230
231
  namespaceFilters: [],
231
232
  activeNamespaceCache: {}, // Used to efficiently check if a resource should be displayed
@@ -241,7 +242,6 @@ export const state = () => {
241
242
  serverVersion: null,
242
243
  systemNamespaces: [],
243
244
  isSingleProduct: undefined,
244
- namespaceFilterMode: null,
245
245
  };
246
246
  };
247
247
 
@@ -250,8 +250,14 @@ export const getters = {
250
250
  return state.clusterReady === true;
251
251
  },
252
252
 
253
- isMultiCluster(state) {
254
- return state.isMultiCluster === true;
253
+ isMultiCluster(state, getters) {
254
+ const clusters = getters['management/all'](MANAGEMENT.CLUSTER);
255
+
256
+ if (clusters.length === 1 && clusters[0].metadata?.name === 'local') {
257
+ return false;
258
+ } else {
259
+ return true;
260
+ }
255
261
  },
256
262
 
257
263
  isRancher(state) {
@@ -278,15 +284,6 @@ export const getters = {
278
284
  return state.systemNamespaces;
279
285
  },
280
286
 
281
- /**
282
- * Namespace Filter Mode supplies a resource type to the NamespaceFilter.
283
- *
284
- * Only one of the resource type is allowed to be selected
285
- */
286
- namespaceFilterMode(state) {
287
- return state.namespaceFilterMode;
288
- },
289
-
290
287
  currentCluster(state, getters) {
291
288
  return getters['management/byId'](MANAGEMENT.CLUSTER, state.clusterId);
292
289
  },
@@ -372,34 +369,6 @@ export const getters = {
372
369
  return state.namespaceFilters.filter(x => !`${ x }`.startsWith(NAMESPACED_PREFIX)).length === 0;
373
370
  },
374
371
 
375
- isSingleNamespace(state, getters) {
376
- const product = getters['currentProduct'];
377
-
378
- if ( !product ) {
379
- return false;
380
- }
381
-
382
- if ( product.showWorkspaceSwitcher ) {
383
- return false;
384
- }
385
-
386
- if ( getters.isAllNamespaces ) {
387
- return false;
388
- }
389
-
390
- const filters = state.namespaceFilters;
391
-
392
- if ( filters.length !== 1 ) {
393
- return false;
394
- }
395
-
396
- if (filters[0].startsWith('ns://')) {
397
- return filters[0];
398
- }
399
-
400
- return false;
401
- },
402
-
403
372
  isMultipleNamespaces(state, getters) {
404
373
  const product = getters['currentProduct'];
405
374
 
@@ -421,7 +390,7 @@ export const getters = {
421
390
  return true;
422
391
  }
423
392
 
424
- return !filters[0].startsWith('ns://');
393
+ return !filters[0].startsWith(NAMESPACE_FILTER_NS_FULL_PREFIX);
425
394
  },
426
395
 
427
396
  namespaceFilters(state) {
@@ -591,9 +560,8 @@ export const getters = {
591
560
  };
592
561
 
593
562
  export const mutations = {
594
- managementChanged(state, { ready, isMultiCluster, isRancher }) {
563
+ managementChanged(state, { ready, isRancher }) {
595
564
  state.managementReady = ready;
596
- state.isMultiCluster = isMultiCluster;
597
565
  state.isRancher = isRancher;
598
566
  },
599
567
  clusterReady(state, ready) {
@@ -621,10 +589,6 @@ export const mutations = {
621
589
  state.allNamespaces = namespace;
622
590
  },
623
591
 
624
- setNamespaceFilterMode(state, mode) {
625
- state.namespaceFilterMode = mode;
626
- },
627
-
628
592
  pageActions(state, pageActions) {
629
593
  state.pageActions = pageActions;
630
594
  },
@@ -747,11 +711,7 @@ export const actions = {
747
711
 
748
712
  res = await allHash(promises);
749
713
  dispatch('i18n/init');
750
- let isMultiCluster = true;
751
-
752
- if ( res.clusters.length === 1 && res.clusters[0].metadata?.name === 'local' ) {
753
- isMultiCluster = false;
754
- }
714
+ const isMultiCluster = getters['isMultiCluster'];
755
715
 
756
716
  const pl = res.settings?.find(x => x.id === 'ui-pl')?.value;
757
717
  const brand = res.settings?.find(x => x.id === SETTING.BRAND)?.value;
@@ -773,7 +733,6 @@ export const actions = {
773
733
 
774
734
  commit('managementChanged', {
775
735
  ready: true,
776
- isMultiCluster,
777
736
  isRancher,
778
737
  });
779
738
 
@@ -967,10 +926,6 @@ export const actions = {
967
926
  commit('updateNamespaces', { filters: ids });
968
927
  },
969
928
 
970
- setNamespaceFilterMode({ commit }, mode) {
971
- commit('setNamespaceFilterMode', mode);
972
- },
973
-
974
929
  async cleanNamespaces({ getters, dispatch }) {
975
930
  // Initialise / Remove any filters that the user no-longer has access to
976
931
  await dispatch('management/findAll', { type: MANAGEMENT.CLUSTER }); // So they can be got byId below
@@ -0,0 +1,5 @@
1
+ export const STORE = {
2
+ CLUSTER: 'cluster',
3
+ RANCHER: 'rancher',
4
+ MANAGEMENT: 'management',
5
+ };