@rancher/shell 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/assets/styles/app.scss +1 -1
  2. package/assets/styles/fonts/_fontstack.scss +11 -11
  3. package/assets/styles/vendor/vue-js-modal.scss +3 -3
  4. package/assets/translations/en-us.yaml +92 -22
  5. package/assets/translations/zh-hans.yaml +84 -15
  6. package/babel.config.js +13 -0
  7. package/chart/gatekeeper.vue +77 -0
  8. package/chart/istio.vue +108 -111
  9. package/chart/logging/index.vue +13 -4
  10. package/chart/monitoring/index.vue +15 -5
  11. package/chart/monitoring/steps/uninstall-v1.vue +2 -2
  12. package/chart/rancher-backup/index.vue +10 -3
  13. package/cloud-credential/aws.vue +1 -1
  14. package/cloud-credential/digitalocean.vue +1 -1
  15. package/cloud-credential/gcp.vue +1 -1
  16. package/cloud-credential/generic.vue +2 -2
  17. package/cloud-credential/linode.vue +1 -1
  18. package/cloud-credential/pnap.vue +1 -1
  19. package/components/ActionMenu.vue +3 -4
  20. package/components/AssignTo.vue +1 -1
  21. package/components/AsyncButton.vue +1 -1
  22. package/components/BannerGraphic.vue +1 -1
  23. package/components/ButtonDropdown.vue +2 -3
  24. package/components/ChartPsp.vue +76 -0
  25. package/components/CruResource.vue +6 -2
  26. package/components/DashboardMetrics.vue +12 -10
  27. package/components/DetailText.vue +1 -1
  28. package/components/DisableAuthProviderModal.vue +1 -1
  29. package/components/EmberPage.vue +1 -1
  30. package/components/EtcdInfoBanner.vue +5 -4
  31. package/components/ExplorerMembers.vue +1 -1
  32. package/components/ExplorerProjectsNamespaces.vue +14 -1
  33. package/components/FileDiff.vue +6 -7
  34. package/components/GrafanaDashboard.vue +18 -21
  35. package/components/LazyImage.vue +10 -12
  36. package/components/LogItem.vue +1 -1
  37. package/components/Markdown.vue +1 -1
  38. package/components/PromptRemove.vue +2 -2
  39. package/components/PromptRestore.vue +1 -1
  40. package/components/ResourceDetail/Masthead.vue +16 -0
  41. package/components/ResourceDetail/index.vue +21 -4
  42. package/components/ResourceList/index.vue +1 -1
  43. package/components/ResourceTable.vue +4 -1
  44. package/components/SingleClusterInfo.vue +2 -2
  45. package/components/SortableTable/THead.vue +1 -1
  46. package/components/SortableTable/index.vue +5 -2
  47. package/components/__tests__/AsyncButton.test.ts +3 -1
  48. package/components/__tests__/ChartPsp.test.ts +75 -0
  49. package/components/__tests__/CruResource.test.ts +3 -1
  50. package/components/auth/Principal.vue +1 -1
  51. package/components/fleet/FleetBundles.vue +3 -1
  52. package/components/fleet/FleetClusters.vue +1 -2
  53. package/components/fleet/FleetIntro.vue +9 -1
  54. package/components/fleet/FleetNoWorkspaces.vue +62 -0
  55. package/components/fleet/FleetSummary.vue +7 -1
  56. package/components/form/LabeledSelect.vue +14 -11
  57. package/components/form/MatchExpressions.vue +17 -2
  58. package/components/form/NameNsDescription.vue +31 -45
  59. package/components/form/ResourceSelector.vue +1 -1
  60. package/components/form/SecretSelector.vue +5 -1
  61. package/components/form/ServiceNameSelect.vue +1 -1
  62. package/components/form/SimpleSecretSelector.vue +9 -9
  63. package/components/form/__tests__/LabeledSelect.test.ts +138 -0
  64. package/components/form/__tests__/NameNsDescription.ts +32 -0
  65. package/components/formatter/InternalExternalIP.vue +6 -0
  66. package/components/formatter/InvolvedObjectLink.vue +54 -0
  67. package/components/formatter/Link.vue +20 -4
  68. package/components/formatter/LinkName.vue +6 -1
  69. package/components/formatter/ServiceTargets.vue +1 -1
  70. package/components/nav/Group.vue +2 -2
  71. package/components/nav/NamespaceFilter.vue +15 -11
  72. package/components/nav/TopLevelMenu.vue +2 -4
  73. package/components/nav/Type.vue +1 -1
  74. package/components/nav/WorkspaceSwitcher.vue +46 -5
  75. package/config/labels-annotations.js +17 -0
  76. package/config/product/auth.js +3 -2
  77. package/config/product/explorer.js +11 -4
  78. package/config/product/fleet.js +2 -0
  79. package/config/router.js +414 -0
  80. package/config/table-headers.js +10 -2
  81. package/config/types.js +11 -8
  82. package/config/uiplugins.js +30 -0
  83. package/content/docs/en-us/whats-new.md +10 -0
  84. package/content/docs/zh-hans/whats-new.md +11 -1
  85. package/core/plugin-routes.ts +23 -0
  86. package/creators/app/app.package.json +2 -1
  87. package/creators/app/files/.eslintrc.js +1 -1
  88. package/creators/app/files/babel.config.js +1 -18
  89. package/creators/app/files/vue.config.js +7 -0
  90. package/creators/app/init +5 -5
  91. package/creators/pkg/files/.github/workflows/build-extension.yml +111 -0
  92. package/creators/pkg/init +35 -4
  93. package/creators/update/init +1 -1
  94. package/detail/constraints.gatekeeper.sh.constraint.vue +20 -10
  95. package/detail/fleet.cattle.io.gitrepo.vue +19 -11
  96. package/detail/harvesterhci.io.management.cluster.vue +3 -3
  97. package/detail/provisioning.cattle.io.cluster.vue +54 -12
  98. package/detail/workload/index.vue +3 -3
  99. package/dialog/AddClusterMemberDialog.vue +1 -1
  100. package/dialog/AddProjectMemberDialog.vue +2 -2
  101. package/dialog/AddonConfigConfirmationDialog.vue +27 -15
  102. package/dialog/DiagnosticTimingsDialog.vue +1 -1
  103. package/dialog/ForceMachineRemoveDialog.vue +1 -1
  104. package/dialog/GenericPrompt.vue +18 -6
  105. package/dialog/RotateEncryptionKeyDialog.vue +1 -1
  106. package/dialog/SaveAsRKETemplateDialog.vue +1 -1
  107. package/dialog/ScaleMachineDownDialog.vue +1 -1
  108. package/edit/auth/github.vue +8 -8
  109. package/edit/auth/googleoauth.vue +5 -5
  110. package/edit/auth/ldap/index.vue +1 -1
  111. package/edit/auth/oidc.vue +1 -1
  112. package/edit/auth/saml.vue +1 -1
  113. package/edit/cis.cattle.io.clusterscan.vue +1 -1
  114. package/edit/fleet.cattle.io.clustergroup.vue +6 -4
  115. package/edit/fleet.cattle.io.gitrepo.vue +16 -3
  116. package/edit/helm.cattle.io.projecthelmchart.vue +5 -1
  117. package/edit/management.cattle.io.fleetworkspace.vue +141 -6
  118. package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +4 -1
  119. package/edit/management.cattle.io.setting.vue +1 -1
  120. package/edit/monitoring.coreos.com.alertmanagerconfig/types/webhook.vue +2 -2
  121. package/edit/monitoring.coreos.com.receiver/tls.vue +18 -18
  122. package/edit/monitoring.coreos.com.receiver/types/webhook.banner.vue +4 -4
  123. package/edit/monitoring.coreos.com.receiver/types/webhook.vue +1 -1
  124. package/edit/namespace.vue +2 -2
  125. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +126 -45
  126. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
  127. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +10 -0
  128. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -0
  129. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +202 -2
  130. package/edit/provisioning.cattle.io.cluster/rke2.vue +248 -84
  131. package/edit/resources.cattle.io.backup.vue +1 -1
  132. package/edit/service.vue +1 -1
  133. package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +2 -2
  134. package/edit/workload/__tests__/Job.test.ts +3 -1
  135. package/edit/workload/index.vue +8 -3
  136. package/edit/workload/mixins/workload.js +16 -0
  137. package/layouts/default.vue +7 -3
  138. package/list/fleet.cattle.io.bundle.vue +6 -3
  139. package/list/fleet.cattle.io.clusterregistrationtoken.vue +3 -1
  140. package/list/fleet.cattle.io.gitrepo.vue +44 -5
  141. package/list/management.cattle.io.fleetworkspace.vue +45 -0
  142. package/list/node.vue +69 -16
  143. package/list/provisioning.cattle.io.cluster.vue +30 -1
  144. package/machine-config/azure.vue +97 -38
  145. package/middleware/authenticated.js +34 -0
  146. package/mixins/chart.js +73 -2
  147. package/mixins/resource-fetch.js +2 -2
  148. package/models/apps.statefulset.js +28 -0
  149. package/models/cluster/node.js +23 -2
  150. package/models/cluster.x-k8s.io.machine.js +4 -2
  151. package/models/clusterroletemplatebinding.js +7 -0
  152. package/models/constraints.gatekeeper.sh.constraint.js +9 -0
  153. package/models/fleet.cattle.io.cluster.js +19 -10
  154. package/models/fleet.cattle.io.gitrepo.js +7 -2
  155. package/models/management.cattle.io.cluster.js +1 -1
  156. package/models/management.cattle.io.fleetworkspace.js +12 -0
  157. package/models/management.cattle.io.gitreporestriction.js +5 -0
  158. package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.js +3 -0
  159. package/models/provisioning.cattle.io.cluster.js +7 -5
  160. package/nuxt/App.js +210 -0
  161. package/nuxt/axios.js +186 -0
  162. package/nuxt/client.js +817 -0
  163. package/nuxt/components/nuxt-build-indicator.vue +143 -0
  164. package/nuxt/components/nuxt-child.js +122 -0
  165. package/nuxt/components/nuxt-error.vue +98 -0
  166. package/nuxt/components/nuxt-link.client.js +98 -0
  167. package/nuxt/components/nuxt-link.server.js +16 -0
  168. package/nuxt/components/nuxt-loading.vue +154 -0
  169. package/nuxt/components/nuxt.js +101 -0
  170. package/nuxt/cookie-universal-nuxt.js +9 -0
  171. package/nuxt/empty.js +1 -0
  172. package/nuxt/index.js +365 -0
  173. package/nuxt/jsonp.js +82 -0
  174. package/nuxt/loading.html +39 -0
  175. package/nuxt/middleware.js +12 -0
  176. package/nuxt/mixins/fetch.client.js +90 -0
  177. package/nuxt/mixins/fetch.server.js +69 -0
  178. package/nuxt/portal-vue.js +4 -0
  179. package/nuxt/server.js +312 -0
  180. package/nuxt/store.js +178 -0
  181. package/nuxt/utils.js +630 -0
  182. package/nuxt/views/app.template.html +9 -0
  183. package/nuxt/views/error.html +23 -0
  184. package/package.json +5 -9
  185. package/pages/auth/setup.vue +2 -2
  186. package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +33 -0
  187. package/pages/c/_cluster/apps/charts/chart.vue +4 -4
  188. package/pages/c/_cluster/apps/charts/install.helpers.js +26 -0
  189. package/pages/c/_cluster/apps/charts/install.vue +40 -66
  190. package/pages/c/_cluster/explorer/EventsTable.vue +5 -19
  191. package/pages/c/_cluster/explorer/index.vue +29 -25
  192. package/pages/c/_cluster/explorer/tools/index.vue +8 -8
  193. package/pages/c/_cluster/fleet/index.vue +95 -34
  194. package/pages/c/_cluster/gatekeeper/index.vue +1 -1
  195. package/pages/c/_cluster/istio/index.vue +5 -5
  196. package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
  197. package/pages/c/_cluster/monitoring/index.vue +7 -0
  198. package/pages/c/_cluster/uiplugins/InstallDialog.vue +8 -8
  199. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +20 -7
  200. package/pages/c/_cluster/uiplugins/index.vue +49 -17
  201. package/pages/home.vue +9 -4
  202. package/pages/index.vue +10 -1
  203. package/plugins/clean-html-directive.js +31 -0
  204. package/plugins/dashboard-store/actions.js +32 -9
  205. package/plugins/dashboard-store/mutations.js +5 -2
  206. package/plugins/dashboard-store/resource-class.js +8 -1
  207. package/plugins/steve/mutations.js +3 -2
  208. package/plugins/steve/steve-description-class.js +5 -1
  209. package/plugins/steve/subscribe.js +63 -54
  210. package/plugins/steve-create-worker.js +14 -0
  211. package/promptRemove/management.cattle.io.globalrole.vue +2 -2
  212. package/promptRemove/management.cattle.io.project.vue +2 -2
  213. package/promptRemove/management.cattle.io.roletemplate.vue +2 -2
  214. package/promptRemove/pod.vue +1 -1
  215. package/public/index.html +65 -0
  216. package/rancher-components/components/Banner/Banner.test.ts +9 -1
  217. package/rancher-components/components/Banner/Banner.vue +1 -1
  218. package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -0
  219. package/rancher-components/components/Form/Radio/RadioButton.vue +1 -1
  220. package/scripts/build-pkg.sh +1 -0
  221. package/scripts/clean +6 -0
  222. package/scripts/extension/bundle +58 -0
  223. package/scripts/extension/helmpatch +89 -0
  224. package/scripts/extension/publish +314 -0
  225. package/scripts/test-plugins-build.sh +4 -0
  226. package/store/__tests__/index.test.ts +110 -0
  227. package/store/index.js +145 -58
  228. package/store/type-map.js +5 -1
  229. package/tsconfig.default.json +36 -0
  230. package/tsconfig.json +24 -0
  231. package/types/shell/index.d.ts +420 -343
  232. package/utils/__tests__/string.test.ts +12 -0
  233. package/utils/auth.js +65 -0
  234. package/utils/monitoring.js +2 -1
  235. package/utils/position.js +5 -8
  236. package/utils/router.scrollBehavior.js +80 -0
  237. package/utils/select.js +1 -3
  238. package/utils/socket.js +1 -0
  239. package/utils/string.js +13 -0
  240. package/utils/time.js +9 -0
  241. package/vue.config.js +679 -0
  242. package/chart/rancher-alerting-drivers.vue +0 -53
  243. package/chart/rancher-gatekeeper.vue +0 -37
  244. package/creators/app/files/nuxt.config.js +0 -6
  245. package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +0 -4
  246. package/nuxt.config.js +0 -798
@@ -53,8 +53,9 @@ export async function loadSchemas(ctx, watch = true) {
53
53
 
54
54
  commit('loadAll', {
55
55
  ctx,
56
- type: SCHEMA,
57
- data: res.data
56
+ type: SCHEMA,
57
+ data: res.data,
58
+ revision: res.revision
58
59
  });
59
60
 
60
61
  if ( watch !== false ) {
@@ -148,11 +149,14 @@ export default {
148
149
  commit('registerType', type);
149
150
  }
150
151
 
152
+ // No need to request the resources if we have them already
151
153
  if ( opt.force !== true && (getters['haveAll'](type) || getters['haveAllNamespace'](type, opt.namespaced))) {
152
154
  const args = {
153
155
  type,
154
156
  revision: '',
155
- namespace: opt.watchNamespace
157
+ // watchNamespace - used sometimes when we haven't fetched the results of a single namespace
158
+ // namespaced - used when we have fetched the result of a single namespace (see https://github.com/rancher/dashboard/pull/7329/files)
159
+ namespace: opt.watchNamespace || opt.namespaced
156
160
  };
157
161
 
158
162
  if (opt.watch !== false ) {
@@ -304,17 +308,21 @@ export default {
304
308
  ctx,
305
309
  type,
306
310
  data: out.data,
311
+ revision: out.revision,
307
312
  skipHaveAll,
308
313
  namespace: opt.namespaced,
309
314
  });
310
315
  }
311
316
  }
312
317
 
318
+ // ToDo: SM if we start a "bigger" watch (such as watch without a namespace vs a watch with a namespace), we should stop the stop the "smaller" watch so we don't have duplicate events coming back
313
319
  if ( opt.watch !== false ) {
314
320
  dispatch('watch', {
315
321
  type,
316
322
  revision: out.revision,
317
- namespace: opt.watchNamespace
323
+ namespace: opt.watchNamespace || opt.namespaced, // it could be either apparently
324
+ // ToDo: SM namespaced is sometimes a boolean and sometimes a string, I don't see it as especially broken but we should refactor that in the future
325
+ force: opt.forceWatch === true,
318
326
  });
319
327
  }
320
328
 
@@ -369,15 +377,17 @@ export default {
369
377
  commit('loadSelector', {
370
378
  ctx,
371
379
  type,
372
- entries: res.data,
373
- selector
380
+ entries: res.data,
381
+ selector,
382
+ revision: res.revision,
374
383
  });
375
384
 
376
385
  if ( opt.watch !== false ) {
377
386
  dispatch('watch', {
378
387
  type,
379
388
  selector,
380
- revision: res.revision
389
+ revision: res.revision,
390
+ force: opt.forceWatch === true,
381
391
  });
382
392
  }
383
393
 
@@ -394,6 +404,12 @@ export default {
394
404
  // url: Use this specific URL instead of looking up the URL for the type/id. This should only be used for bootstrapping schemas on startup.
395
405
  // @TODO depaginate: If the response is paginated, retrieve all the pages. (default: true)
396
406
  async find(ctx, { type, id, opt }) {
407
+ if (!id) {
408
+ console.error('Attempting to find a resource with no id', type, id); // eslint-disable-line no-console
409
+
410
+ return;
411
+ }
412
+
397
413
  const { getters, dispatch } = ctx;
398
414
 
399
415
  opt = opt || {};
@@ -459,6 +475,11 @@ export default {
459
475
  commit('registerType', type);
460
476
  }
461
477
  }
478
+ // Inject special fields for indexing schemas
479
+ if ( type === SCHEMA ) {
480
+ addSchemaIndexFields(data);
481
+ }
482
+
462
483
  const keyField = getters.keyFieldForType(type);
463
484
  const id = data?.[keyField] || existing?.[keyField];
464
485
 
@@ -531,8 +552,10 @@ export default {
531
552
 
532
553
  // Forget a type in the store
533
554
  // Remove all entries for that type and stop watching it
534
- forgetType({ commit, getters, dispatch }, type) {
535
- dispatch('unwatch', type);
555
+ forgetType({ commit, dispatch, state }, type) {
556
+ state.started
557
+ .filter(entry => entry.type === type)
558
+ .forEach(entry => dispatch('unwatch', entry));
536
559
 
537
560
  commit('forgetType', type);
538
561
  },
@@ -261,7 +261,8 @@ export function loadAll(state, {
261
261
  data,
262
262
  ctx,
263
263
  skipHaveAll,
264
- namespace
264
+ namespace,
265
+ revision
265
266
  }) {
266
267
  const { getters } = ctx;
267
268
 
@@ -283,6 +284,7 @@ export function loadAll(state, {
283
284
 
284
285
  clear(cache.list);
285
286
  cache.map.clear();
287
+ cache.revision = revision || 0;
286
288
  cache.generation++;
287
289
 
288
290
  addObjects(cache.list, proxies);
@@ -320,7 +322,7 @@ export default {
320
322
  },
321
323
 
322
324
  loadSelector(state, {
323
- type, entries, ctx, selector
325
+ type, entries, ctx, selector, revision
324
326
  }) {
325
327
  const cache = registerType(state, type);
326
328
 
@@ -329,6 +331,7 @@ export default {
329
331
  }
330
332
 
331
333
  cache.haveSelector[selector] = true;
334
+ cache.revision = revision || 0;
332
335
  },
333
336
 
334
337
  loadAll,
@@ -1110,7 +1110,7 @@ export default class Resource {
1110
1110
  }
1111
1111
 
1112
1112
  // @TODO remove this once the API maps steve _type <-> k8s type in both directions
1113
- opt.data = { ...this };
1113
+ opt.data = this.toSave() || { ...this };
1114
1114
 
1115
1115
  if (opt?.data._type) {
1116
1116
  opt.data.type = opt.data._type;
@@ -1884,6 +1884,13 @@ export default class Resource {
1884
1884
  return out;
1885
1885
  }
1886
1886
 
1887
+ /**
1888
+ * Allow models to override the object that is sent when saving this resource
1889
+ */
1890
+ toSave() {
1891
+ return undefined;
1892
+ }
1893
+
1887
1894
  get creationTimestamp() {
1888
1895
  return this.metadata?.creationTimestamp;
1889
1896
  }
@@ -121,7 +121,8 @@ export default {
121
121
  data,
122
122
  ctx,
123
123
  skipHaveAll,
124
- namespace
124
+ namespace,
125
+ revision
125
126
  }) {
126
127
  // Performance testing in dev and when env var is set
127
128
  if (process.env.dev && !!process.env.perfTest) {
@@ -129,7 +130,7 @@ export default {
129
130
  }
130
131
 
131
132
  const proxies = loadAll(state, {
132
- type, data, ctx, skipHaveAll, namespace
133
+ type, data, ctx, skipHaveAll, namespace, revision
133
134
  });
134
135
 
135
136
  // If we loaded a set of pods, then update the podsByNamespace cache
@@ -20,7 +20,7 @@ export default class SteveDescriptionModel extends SteveModel {
20
20
  this._description = value;
21
21
  }
22
22
 
23
- // Ensure when we clone that we preserve the desription
23
+ // Ensure when we clone that we preserve the description
24
24
  toJSON() {
25
25
  const data = super.toJSON();
26
26
 
@@ -29,4 +29,8 @@ export default class SteveDescriptionModel extends SteveModel {
29
29
 
30
30
  return data;
31
31
  }
32
+
33
+ toSave() {
34
+ return this.toJSON();
35
+ }
32
36
  }
@@ -21,6 +21,7 @@ import Socket, {
21
21
  EVENT_DISCONNECT_ERROR,
22
22
  NO_WATCH,
23
23
  NO_SCHEMA,
24
+ REVISION_TOO_OLD
24
25
  } from '@shell/utils/socket';
25
26
  import { normalizeType } from '@shell/plugins/dashboard-store/normalize';
26
27
  import day from 'dayjs';
@@ -29,8 +30,6 @@ import { escapeHtml } from '@shell/utils/string';
29
30
  import { keyForSubscribe } from '@shell/plugins/steve/resourceWatcher';
30
31
  import { waitFor } from '@shell/utils/async';
31
32
 
32
- // eslint-disable-next-line
33
- import storeWorker from './worker/index.js';
34
33
  import { BLANK_CLUSTER } from '@shell/store/index.js';
35
34
 
36
35
  // minimum length of time a disconnect notification is shown
@@ -97,7 +96,7 @@ export async function createWorker(store, ctx) {
97
96
 
98
97
  if (!store.$workers[storeName]) {
99
98
  const workerMode = advancedWorker ? 'advanced' : 'basic';
100
- const worker = storeWorker(workerMode);
99
+ const worker = store.steveCreateWorker(workerMode);
101
100
 
102
101
  store.$workers[storeName] = worker;
103
102
 
@@ -307,8 +306,13 @@ const sharedActions = {
307
306
  }
308
307
 
309
308
  // If socket is in error don't try to watch.... unless we `force` it
310
- if ( !stop && !force && !getters.canWatch(params) ) {
311
- console.error(`Cannot Watch [${ getters.storeName }]`, JSON.stringify(params)); // eslint-disable-line no-console
309
+ const inError = getters.inError(params);
310
+
311
+ if ( !stop && !force && inError ) {
312
+ // REVISION_TOO_OLD is a temporary state and will be handled when `resyncWatch` completes
313
+ if (inError !== REVISION_TOO_OLD) {
314
+ console.error(`Aborting Watch Request [${ getters.storeName }]. Watcher in error (${ inError })`, JSON.stringify(params)); // eslint-disable-line no-console
315
+ }
312
316
 
313
317
  return;
314
318
  }
@@ -316,7 +320,10 @@ const sharedActions = {
316
320
  if ( !stop && getters.watchStarted({
317
321
  type, id, selector, namespace
318
322
  }) ) {
319
- state.debugSocket && console.debug(`Already Watching [${ getters.storeName }]`, JSON.stringify(params)); // eslint-disable-line no-console
323
+ // eslint-disable-next-line no-console
324
+ state.debugSocket && console.debug(`Already Watching [${ getters.storeName }]`, {
325
+ type, id, selector, namespace
326
+ });
320
327
 
321
328
  return;
322
329
  }
@@ -347,7 +354,7 @@ const sharedActions = {
347
354
  msg.selector = selector;
348
355
  }
349
356
 
350
- const worker = this.$workers[getters.storeName] || {};
357
+ const worker = this.$workers?.[getters.storeName] || {};
351
358
 
352
359
  if (worker.mode === 'advanced') {
353
360
  if ( force ) {
@@ -362,12 +369,17 @@ const sharedActions = {
362
369
  return dispatch('send', msg);
363
370
  },
364
371
 
365
- unwatch(ctx, type) {
372
+ unwatch(ctx, {
373
+ type, id, namespace, selector
374
+ }) {
366
375
  const { commit, getters, dispatch } = ctx;
367
376
 
368
377
  if (getters['schemaFor'](type)) {
369
378
  const obj = {
370
379
  type,
380
+ id,
381
+ namespace,
382
+ selector,
371
383
  stop: true, // Stops the watch on a type
372
384
  };
373
385
 
@@ -675,14 +687,30 @@ const defaultActions = {
675
687
  /**
676
688
  * Steve only event
677
689
  */
678
- 'ws.resource.start'({ state, getters, commit }, msg) {
690
+ 'ws.resource.start'({
691
+ state, getters, commit, dispatch
692
+ }, msg) {
679
693
  state.debugSocket && console.info(`Resource start: [${ getters.storeName }]`, msg); // eslint-disable-line no-console
680
- commit('setWatchStarted', {
694
+
695
+ const newWatch = {
681
696
  type: msg.resourceType,
682
697
  namespace: msg.namespace,
683
698
  id: msg.id,
684
699
  selector: msg.selector
700
+ };
701
+
702
+ state.started.filter((entry) => {
703
+ if (
704
+ entry.type === newWatch.type &&
705
+ entry.namespace !== newWatch.namespace
706
+ ) {
707
+ return true;
708
+ }
709
+ }).forEach((entry) => {
710
+ dispatch('unwatch', entry);
685
711
  });
712
+
713
+ commit('setWatchStarted', newWatch);
686
714
  },
687
715
 
688
716
  'ws.resource.error'({ getters, commit, dispatch }, msg) {
@@ -695,6 +723,10 @@ const defaultActions = {
695
723
  } else if ( err.includes('failed to find schema') ) {
696
724
  commit('setInError', { type: msg.resourceType, reason: NO_SCHEMA });
697
725
  } else if ( err.includes('too old') ) {
726
+ // Set an error for (all) subs of this type. This..
727
+ // 1) blocks attempts by resource.stop to resub (as type is in error)
728
+ // 2) will be cleared when resyncWatch --> watch (with force) --> resource.start completes
729
+ commit('setInError', { type: msg.resourceType, reason: REVISION_TOO_OLD });
698
730
  dispatch('resyncWatch', msg);
699
731
  }
700
732
  },
@@ -702,12 +734,15 @@ const defaultActions = {
702
734
  /**
703
735
  * Steve only event
704
736
  *
705
- * Steve only seems to send out `resource.stop` messages for two reasons
706
- * - We have requested that the resource watch should be stopped and we receive this event as confirmation
707
- * - Steve tells us that the resource is no longer watched
708
- *
737
+ * Steve has stopped watching this resource. This happens for a couple of reasons
738
+ * - We have requested that the resource watch should be stopped (and we receive this event as confirmation)
739
+ * - Steve tells us that the resource watch has been stopped. Possible reasons
740
+ * - The rancher <--> k8s socket closed (happens every ~30 mins on mgmt socket)
741
+ * - Permissions has changed for the subscribed resource, so rancher closes socket
709
742
  */
710
- 'ws.resource.stop'({ getters, commit, dispatch }, msg) {
743
+ 'ws.resource.stop'({
744
+ state, getters, commit, dispatch
745
+ }, msg) {
711
746
  const type = msg.resourceType;
712
747
  const obj = {
713
748
  type,
@@ -716,51 +751,23 @@ const defaultActions = {
716
751
  selector: msg.selector
717
752
  };
718
753
 
719
- // console.warn(`Resource stop: [${ getters.storeName }]`, msg); // eslint-disable-line no-console
754
+ state.debugSocket && console.info(`Resource Stop [${ getters.storeName }]`, type, msg); // eslint-disable-line no-console
755
+
756
+ if (!type) {
757
+ console.error(`Resource Stop [${ getters.storeName }]. Received resource.stop with an empty resourceType, aborting`, msg); // eslint-disable-line no-console
758
+
759
+ return;
760
+ }
720
761
 
721
762
  // If we're trying to watch this event, attempt to re-watch
722
763
  if ( getters['schemaFor'](type) && getters['watchStarted'](obj) ) {
723
764
  commit('setWatchStopped', obj);
724
-
725
- // In summary, we need to re-watch but with a reliable `revision` (to avoid `too old` message kicking off a full re-fetch of all
726
- // resources). To get a reliable `revision` go out and fetch the latest for that resource type, in theory our local cache should be
727
- // up to date with that revision.
728
-
729
- const revisionExisting = getters.nextResourceVersion(type, obj.id);
730
-
731
- let revisionLatest;
732
-
733
- if (revisionExisting) {
734
- // Attempt to fetch the latest revision at the time the resource watch was stopped, in theory our local cache should be up to
735
- // date with this
736
- // Ideally we shouldn't need to fetch here and supply `0`, `-1` or `null` to start watching from the latest revision, however steve
737
- // will send the current state of each resource via a `resource.created` event.
738
- const opt = { limit: 1 };
739
-
740
- opt.url = getters.urlFor(type, null, opt);
741
- revisionLatest = dispatch('request', { opt, type } )
742
- .then(res => res.revision)
743
- .catch((err) => {
744
- // For some reason we can't fetch a reasonable revision, so force a re-fetch
745
- console.warn(`Resource error retrieving resourceVersion, forcing re-fetch`, type, ':', err); // eslint-disable-line no-console
746
- dispatch('resyncWatch', msg);
747
- throw err;
748
- });
749
- } else {
750
- // Some v1 resource types don't have revisions (either at the collection or resource level), so we avoided making an API request
751
- // for them
752
- revisionLatest = Promise.resolve(null); // Null to ensure we don't go through `nextResourceVersion` again
753
- }
754
-
755
- setTimeout(() => {
756
- // Delay a bit so that immediate start/error/stop causes
757
- // only a slow infinite loop instead of a tight one.
758
- revisionLatest.then(revision => dispatch('watch', { ...obj, revision }));
759
- }, 5000);
765
+ dispatch('watch', obj);
760
766
  }
761
767
  },
762
768
 
763
769
  'ws.resource.create'(ctx, msg) {
770
+ ctx.state.debugSocket && console.info(`Resource Create [${ ctx.getters.storeName }]`, msg.resourceType, msg); // eslint-disable-line no-console
764
771
  queueChange(ctx, msg, true, 'Create');
765
772
  },
766
773
 
@@ -811,6 +818,8 @@ const defaultActions = {
811
818
  const data = msg.data;
812
819
  const type = data.type;
813
820
 
821
+ ctx.state.debugSocket && console.info(`Resource Remove [${ ctx.getters.storeName }]`, type, msg); // eslint-disable-line no-console
822
+
814
823
  if (type === SCHEMA) {
815
824
  const worker = (this.$workers || {})[ctx.getters.storeName];
816
825
 
@@ -914,8 +923,8 @@ const defaultMutations = {
914
923
  * Getters that cover cases 1 & 2 (see file description)
915
924
  */
916
925
  const defaultGetters = {
917
- canWatch: state => (obj) => {
918
- return !state.inError[keyForSubscribe(obj)];
926
+ inError: state => (obj) => {
927
+ return state.inError[keyForSubscribe(obj)];
919
928
  },
920
929
 
921
930
  watchStarted: state => (obj) => {
@@ -0,0 +1,14 @@
1
+ import createWorker from './steve/worker/index.js';
2
+
3
+ /**
4
+ * Link the steve worker creator to the store
5
+ *
6
+ * This is done to allow disassociate (no import chain) access to the steve worker file.
7
+ * Without this third party plugins (in given scenarios / harvester) will fail to build
8
+ * due to missing web worker specific build config
9
+ *
10
+ * Note - When rancher/steve store is spun out in to a extension this also needs to move out
11
+ */
12
+ export default function({ store }) {
13
+ store.steveCreateWorker = createWorker;
14
+ }
@@ -23,14 +23,14 @@ export default {
23
23
  <div>
24
24
  <template>
25
25
  {{ t('promptRemove.attemptingToRemove', { type }) }} <span
26
- v-html="resourceNames(names, plusMore, t)"
26
+ v-clean-html="resourceNames(names, plusMore, t)"
27
27
  />
28
28
  </template>
29
29
  <div
30
30
  v-if="info"
31
31
  class="text info mb-10 mt-20"
32
32
  >
33
- <span v-html="info" />
33
+ <span v-clean-html="info" />
34
34
  </div>
35
35
  <div
36
36
  v-if="warning"
@@ -100,8 +100,8 @@ export default {
100
100
  <template v-if="!canSeeProjectlessNamespaces">
101
101
  <span class="delete-warning"> {{ t('promptRemove.willDeleteAssociatedNamespaces') }}</span> <br>
102
102
  <div
103
+ v-clean-html="resourceNames(names, plusMore, t)"
103
104
  class="mt-10"
104
- v-html="resourceNames(names, plusMore, t)"
105
105
  />
106
106
  </template>
107
107
  </div>
@@ -114,7 +114,7 @@ export default {
114
114
  :label="t('promptRemove.deleteAssociatedNamespaces')"
115
115
  />
116
116
  <div class="mt-10 ml-20">
117
- <span v-html="resourceNames(names, plusMore, t)" />
117
+ <span v-clean-html="resourceNames(names, plusMore, t)" />
118
118
  </div>
119
119
  </div>
120
120
  </div>
@@ -23,14 +23,14 @@ export default {
23
23
  <div>
24
24
  <template>
25
25
  {{ t('promptRemove.attemptingToRemove', { type }) }} <span
26
- v-html="resourceNames(names, plusMore, t)"
26
+ v-clean-html="resourceNames(names, plusMore, t)"
27
27
  />
28
28
  </template>
29
29
  <div
30
30
  v-if="info"
31
31
  class="text info mb-10 mt-20"
32
32
  >
33
- <span v-html="info" />
33
+ <span v-clean-html="info" />
34
34
  </div>
35
35
  <div
36
36
  v-if="warning"
@@ -108,8 +108,8 @@ export default {
108
108
  <div class="mt-10">
109
109
  <div class="mb-30">
110
110
  {{ t('promptRemove.attemptingToRemove', { type }) }} <span
111
+ v-clean-html="podNames"
111
112
  class="machine-name"
112
- v-html="podNames"
113
113
  />
114
114
  </div>
115
115
  <div class="mb-30">
@@ -0,0 +1,65 @@
1
+ <!DOCTYPE html>
2
+ <html lang="">
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
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">
9
+ <title>Rancher</title>
10
+ </head>
11
+
12
+ <body>
13
+ <div id="app">
14
+ <script>
15
+ (() => {
16
+ const isDark = document.cookie.includes('R_PCS=dark');
17
+ const color = isDark ? '#1b1c21' : '#FFF';
18
+ const style = document.createElement('style');
19
+ style.innerHTML = ':root { --loading-bg-color: ' + color + ';}';
20
+ document.getElementsByTagName('head')[0].prepend(style);
21
+ })();
22
+ </script>
23
+ <style>
24
+ .initial-load-spinner-container {
25
+ align-items: center;
26
+ background-color: var(--loading-bg-color);
27
+ display: flex;
28
+ justify-content: center;
29
+ height: 100vh;
30
+ left: 0;
31
+ position: absolute;
32
+ top: 0;
33
+ width: 100vw;
34
+ }
35
+
36
+ .initial-load-spinner {
37
+ animation: initial-load-animate 1s infinite linear;
38
+ background-color: var(--loading-bg-color);
39
+ box-sizing: border-box;
40
+ border: 5px solid #008ACF;
41
+ border-radius: 50%;
42
+ border-top-color: #00B2E2;
43
+ display: inline-block;
44
+ height: 80px;
45
+ margin: 0 auto;
46
+ width: 80px;
47
+ }
48
+
49
+ @keyframes initial-load-animate {
50
+ 0% {
51
+ transform: rotate(0deg);
52
+ }
53
+
54
+ 100% {
55
+ transform: rotate(359deg);
56
+ }
57
+ }
58
+ </style>
59
+ <div class="initial-load-spinner-container">
60
+ <i class="initial-load-spinner"></i>
61
+ </div>
62
+ </div>
63
+ </body>
64
+
65
+ </html>
@@ -1,10 +1,18 @@
1
1
  import { mount } from '@vue/test-utils';
2
2
  import { Banner } from './index';
3
+ import { cleanHtmlDirective } from '@shell/plugins/clean-html-directive'
3
4
 
4
5
  describe('component: Banner', () => {
5
6
  it('should display text based on label', () => {
6
7
  const label = 'test';
7
- const wrapper = mount(Banner, { propsData: { label } });
8
+ const wrapper = mount(
9
+ Banner,
10
+ {
11
+ directives: {
12
+ cleanHtmlDirective
13
+ },
14
+ propsData: { label }
15
+ });
8
16
 
9
17
  const element = wrapper.find('span').element;
10
18
 
@@ -95,7 +95,7 @@ export default Vue.extend({
95
95
  <span v-else-if="messageLabel">{{ messageLabel }}</span>
96
96
  <span
97
97
  v-else
98
- v-html="nlToBr(label)"
98
+ v-clean-html="nlToBr(label)"
99
99
  />
100
100
  </slot>
101
101
  <div
@@ -5,6 +5,8 @@ import { addObject, removeObject } from '@shell/utils/array';
5
5
  import cloneDeep from 'lodash/cloneDeep';
6
6
 
7
7
  export default Vue.extend({
8
+ name: 'Checkbox',
9
+
8
10
  props: {
9
11
  /**
10
12
  * The checkbox value.
@@ -152,7 +152,7 @@ export default Vue.extend({
152
152
  v-if="label"
153
153
  :class="[ muteLabel ? 'text-muted' : '', 'radio-label', 'm-0']"
154
154
  :for="name"
155
- v-html="label"
155
+ v-clean-html="label"
156
156
  >
157
157
  <slot name="label">{{ label }}</slot>
158
158
  </label>
@@ -40,6 +40,7 @@ if [ -d "${BASE_DIR}/pkg/${1}" ]; then
40
40
  echo " Package name: ${NAME}"
41
41
  echo " Package version: ${VERSION}"
42
42
  echo " Output formats: ${FORMATS}"
43
+ echo " Output directory: ${PKG_DIST}"
43
44
  rm -rf ${PKG_DIST}
44
45
  mkdir -p ${PKG_DIST}
45
46
 
package/scripts/clean ADDED
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ cd $(dirname $0)/..
5
+
6
+ rm -rf dist .nuxt* node_modules/.cache