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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/assets/brand/classic/metadata.json +3 -0
  2. package/assets/styles/app.scss +1 -0
  3. package/assets/styles/base/_color.scss +19 -0
  4. package/assets/styles/base/_helpers.scss +10 -0
  5. package/assets/styles/base/_variables.scss +1 -1
  6. package/assets/styles/fonts/_icons.scss +1 -32
  7. package/assets/styles/global/_layout.scss +1 -1
  8. package/assets/styles/global/_tooltip.scss +7 -4
  9. package/assets/styles/themes/_dark.scss +272 -259
  10. package/assets/styles/themes/_light.scss +551 -516
  11. package/assets/styles/themes/_modern.scss +936 -0
  12. package/assets/translations/en-us.yaml +219 -38
  13. package/assets/translations/zh-hans.yaml +0 -1
  14. package/chart/__tests__/S3.test.ts +2 -1
  15. package/chart/monitoring/grafana/index.vue +8 -2
  16. package/cloud-credential/generic.vue +18 -10
  17. package/cloud-credential/harvester.vue +1 -9
  18. package/components/ActionMenuShell.vue +3 -1
  19. package/components/AdvancedSection.vue +8 -0
  20. package/components/ChartReadme.vue +17 -7
  21. package/components/Cron/CronExpressionEditor.vue +299 -0
  22. package/components/Cron/CronExpressionEditorModal.vue +247 -0
  23. package/components/Cron/CronTooltip.vue +87 -0
  24. package/components/Cron/types.ts +13 -0
  25. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +1 -26
  26. package/components/Drawer/ResourceDetailDrawer/composables.ts +0 -23
  27. package/components/Drawer/ResourceDetailDrawer/index.vue +17 -4
  28. package/components/ForceDirectedTreeChart/composable.ts +11 -0
  29. package/components/InstallHelmCharts.vue +656 -0
  30. package/components/LazyImage.vue +60 -4
  31. package/components/LocaleSelector.vue +7 -2
  32. package/components/Markdown.vue +4 -0
  33. package/components/PromptModal.vue +1 -1
  34. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +1 -0
  35. package/components/Resource/Detail/CopyToClipboard.vue +78 -0
  36. package/components/Resource/Detail/FetchLoader/__tests__/composables.test.ts +69 -0
  37. package/components/Resource/Detail/FetchLoader/composables.ts +27 -0
  38. package/components/Resource/Detail/Masthead/composable.ts +16 -0
  39. package/components/Resource/Detail/Masthead/index.vue +37 -0
  40. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +1 -1
  41. package/components/Resource/Detail/Metadata/Annotations/index.vue +1 -1
  42. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +13 -61
  43. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +33 -6
  44. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +29 -43
  45. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +25 -5
  46. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -23
  47. package/components/Resource/Detail/Metadata/KeyValueRow.vue +144 -0
  48. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +1 -0
  49. package/components/Resource/Detail/Metadata/Labels/index.vue +1 -0
  50. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +30 -32
  51. package/components/Resource/Detail/Metadata/__tests__/KeyValueRow.test.ts +108 -0
  52. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +10 -20
  53. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +12 -5
  54. package/components/Resource/Detail/Metadata/composables.ts +9 -10
  55. package/components/Resource/Detail/Metadata/index.vue +18 -2
  56. package/components/Resource/Detail/Page.vue +35 -21
  57. package/components/Resource/Detail/Preview/Content.vue +63 -0
  58. package/components/Resource/Detail/Preview/Preview.vue +128 -0
  59. package/components/Resource/Detail/Preview/__tests__/Content.spec.ts +71 -0
  60. package/components/Resource/Detail/Preview/__tests__/Preview.spec.ts +121 -0
  61. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +141 -0
  62. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +136 -0
  63. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +245 -0
  64. package/components/Resource/Detail/ResourcePopover/index.vue +226 -0
  65. package/components/Resource/Detail/SpacedRow.vue +1 -0
  66. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +8 -14
  67. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +1 -1
  68. package/components/Resource/Detail/TitleBar/composables.ts +3 -6
  69. package/components/Resource/Detail/TitleBar/index.vue +11 -29
  70. package/components/Resource/Detail/ViewOptions/composable.ts +9 -0
  71. package/components/Resource/Detail/ViewOptions/index.vue +41 -0
  72. package/components/Resource/Detail/__tests__/CopyToClipboard.spec.ts +82 -0
  73. package/components/ResourceDetail/Masthead/legacy.vue +0 -19
  74. package/components/ResourceDetail/index.vue +544 -74
  75. package/components/ResourceTable.vue +24 -0
  76. package/components/SlideInPanelManager.vue +10 -3
  77. package/components/SortableTable/index.vue +11 -5
  78. package/components/SortableTable/paging.js +3 -0
  79. package/components/Tabbed/Tab.vue +43 -1
  80. package/components/Tabbed/index.vue +32 -4
  81. package/components/__tests__/Cron/CronExpressionEditor.test.ts +151 -0
  82. package/components/__tests__/Cron/CronExpressionEditorModal.test.ts +81 -0
  83. package/components/__tests__/LazyImage.spec.ts +121 -0
  84. package/components/auth/login/saml.vue +86 -0
  85. package/components/fleet/FleetStatus.vue +4 -0
  86. package/components/form/ClusterAppearance.vue +5 -0
  87. package/components/form/LabeledSelect.vue +8 -8
  88. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  89. package/components/form/ProjectMemberEditor.vue +1 -1
  90. package/components/form/ResourceLabeledSelect.vue +19 -6
  91. package/components/form/ResourceTabs/composable.ts +54 -0
  92. package/components/form/ResourceTabs/index.vue +30 -7
  93. package/components/form/SecretSelector.vue +9 -0
  94. package/components/form/Select.vue +13 -10
  95. package/components/form/__tests__/LabeledSelect.test.ts +133 -0
  96. package/components/form/__tests__/Select.test.ts +134 -0
  97. package/components/form/labeled-select-utils/labeled-select-pagination.ts +3 -38
  98. package/components/formatter/FleetApplicationSource.vue +25 -17
  99. package/components/nav/Favorite.vue +4 -0
  100. package/components/nav/NotificationCenter/Notification.vue +1 -27
  101. package/components/nav/WindowManager/index.vue +3 -3
  102. package/composables/useExtensionManager.ts +17 -0
  103. package/config/home-links.js +12 -0
  104. package/config/labels-annotations.js +1 -3
  105. package/config/page-actions.js +0 -1
  106. package/config/product/explorer.js +3 -1
  107. package/config/product/fleet.js +2 -7
  108. package/config/product/manager.js +0 -5
  109. package/config/query-params.js +1 -0
  110. package/config/router/navigation-guards/clusters.js +2 -1
  111. package/config/router/navigation-guards/products.js +1 -1
  112. package/core/extension-manager-impl.js +518 -0
  113. package/core/plugins.js +35 -468
  114. package/core/types.ts +8 -2
  115. package/detail/__tests__/autoscaling.horizontalpodautoscaler.test.ts +1 -0
  116. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +11 -0
  117. package/detail/__tests__/workload.test.ts +164 -0
  118. package/detail/catalog.cattle.io.app.vue +7 -4
  119. package/detail/configmap.vue +33 -75
  120. package/detail/fleet.cattle.io.bundle.vue +1 -5
  121. package/detail/fleet.cattle.io.cluster.vue +3 -2
  122. package/detail/fleet.cattle.io.gitrepo.vue +76 -49
  123. package/detail/fleet.cattle.io.helmop.vue +78 -49
  124. package/detail/projectsecret.vue +11 -0
  125. package/detail/provisioning.cattle.io.cluster.vue +350 -324
  126. package/detail/secret.vue +49 -308
  127. package/detail/workload/index.vue +38 -21
  128. package/dialog/AddonConfigConfirmationDialog.vue +1 -1
  129. package/dialog/GenericPrompt.vue +1 -1
  130. package/dialog/ImportDialog.vue +9 -2
  131. package/dialog/InstallExtensionDialog.vue +26 -15
  132. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +2 -1
  133. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  134. package/edit/__tests__/resources.cattle.io.restore.test.ts +106 -0
  135. package/edit/cloudcredential.vue +31 -17
  136. package/edit/constraints.gatekeeper.sh.constraint/index.vue +10 -2
  137. package/edit/fleet.cattle.io.cluster.vue +19 -0
  138. package/edit/fleet.cattle.io.gitrepo.vue +28 -22
  139. package/edit/fleet.cattle.io.helmop.vue +78 -56
  140. package/edit/logging.banzaicloud.io.output/index.vue +1 -1
  141. package/edit/logging.banzaicloud.io.output/providers/awsElasticsearch.vue +5 -6
  142. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +12 -11
  143. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +11 -1
  144. package/edit/networking.k8s.io.ingress/Certificate.vue +9 -11
  145. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +8 -3
  146. package/edit/networking.k8s.io.ingress/Rule.vue +2 -5
  147. package/edit/networking.k8s.io.ingress/RulePath.vue +17 -11
  148. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +11 -10
  149. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -3
  150. package/edit/networking.k8s.io.networkpolicy/index.vue +17 -17
  151. package/edit/provisioning.cattle.io.cluster/index.vue +14 -19
  152. package/edit/provisioning.cattle.io.cluster/rke2.vue +31 -15
  153. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +9 -7
  154. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +10 -12
  155. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +39 -38
  156. package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +41 -19
  157. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +16 -3
  158. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +30 -31
  159. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +9 -10
  160. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +1 -3
  161. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +16 -9
  162. package/edit/resources.cattle.io.restore.vue +5 -8
  163. package/edit/workload/index.vue +5 -14
  164. package/list/__tests__/workload.test.ts +1 -0
  165. package/list/provisioning.cattle.io.cluster.vue +1 -69
  166. package/list/workload.vue +8 -1
  167. package/machine-config/__tests__/vmwarevsphere.test.ts +5 -7
  168. package/machine-config/components/GCEImage.vue +6 -5
  169. package/machine-config/google.vue +20 -7
  170. package/machine-config/vmwarevsphere.vue +7 -17
  171. package/mixins/__tests__/chart.test.ts +139 -1
  172. package/mixins/chart.js +58 -20
  173. package/mixins/resource-fetch-api-pagination.js +3 -4
  174. package/models/__tests__/chart.test.ts +111 -80
  175. package/models/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  176. package/models/__tests__/namespace.test.ts +69 -0
  177. package/models/__tests__/node.test.ts +7 -63
  178. package/models/apps.statefulset.js +8 -10
  179. package/models/catalog.cattle.io.app.js +1 -1
  180. package/models/catalog.cattle.io.operation.js +1 -1
  181. package/models/chart.js +41 -21
  182. package/models/cloudcredential.js +2 -163
  183. package/models/cluster/node.js +7 -7
  184. package/models/cluster.x-k8s.io.machine.js +3 -3
  185. package/models/compliance.cattle.io.clusterscan.js +2 -2
  186. package/models/configmap.js +4 -0
  187. package/models/constraints.gatekeeper.sh.constraint.js +1 -1
  188. package/models/fleet-application.js +16 -63
  189. package/models/fleet.cattle.io.bundle.js +1 -38
  190. package/models/fleet.cattle.io.gitrepo.js +19 -1
  191. package/models/fleet.cattle.io.helmop.js +30 -22
  192. package/models/management.cattle.io.project.js +12 -0
  193. package/models/management.cattle.io.setting.js +4 -0
  194. package/models/namespace.js +30 -0
  195. package/models/persistentvolumeclaim.js +1 -1
  196. package/models/pod.js +2 -2
  197. package/models/provisioning.cattle.io.cluster.js +16 -40
  198. package/models/rke.cattle.io.etcdsnapshot.js +1 -1
  199. package/models/secret.js +4 -0
  200. package/models/storage.k8s.io.storageclass.js +2 -2
  201. package/models/workload.js +6 -3
  202. package/package.json +19 -18
  203. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +26 -10
  204. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +4 -1
  205. package/pages/c/_cluster/apps/charts/__tests__/AppChartCardFooter.spec.js +41 -0
  206. package/pages/c/_cluster/apps/charts/chart.vue +440 -183
  207. package/pages/c/_cluster/apps/charts/index.vue +1 -0
  208. package/pages/c/_cluster/apps/charts/install.vue +7 -6
  209. package/pages/c/_cluster/explorer/projectsecret.vue +3 -13
  210. package/pages/c/_cluster/explorer/tools/__tests__/index.test.ts +102 -12
  211. package/pages/c/_cluster/explorer/tools/index.vue +145 -254
  212. package/pages/c/_cluster/fleet/__tests__/index.test.ts +608 -314
  213. package/pages/c/_cluster/fleet/index.vue +103 -44
  214. package/pages/c/_cluster/manager/cloudCredential/index.vue +20 -60
  215. package/pages/c/_cluster/manager/drivers/kontainerDriver/index.vue +12 -2
  216. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +11 -4
  217. package/pages/c/_cluster/uiplugins/__tests__/index.spec.ts +318 -0
  218. package/pages/c/_cluster/uiplugins/index.vue +256 -387
  219. package/pages/home.vue +1 -9
  220. package/plugins/dashboard-store/actions.js +42 -22
  221. package/plugins/dashboard-store/resource-class.js +80 -0
  222. package/plugins/steve/__tests__/getters.test.ts +1 -1
  223. package/plugins/steve/__tests__/subscribe.spec.ts +259 -1
  224. package/plugins/steve/getters.js +8 -2
  225. package/plugins/steve/resourceWatcher.js +10 -3
  226. package/plugins/steve/subscribe.js +192 -19
  227. package/plugins/steve/worker/web-worker.advanced.js +2 -0
  228. package/public/index.html +2 -1
  229. package/rancher-components/Card/Card.vue +1 -19
  230. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  231. package/rancher-components/Form/Radio/RadioButton.vue +1 -1
  232. package/rancher-components/Form/Radio/RadioGroup.vue +1 -1
  233. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -11
  234. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.test.ts +53 -0
  235. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +65 -0
  236. package/rancher-components/Pill/RcCounterBadge/index.ts +1 -0
  237. package/rancher-components/Pill/RcCounterBadge/types.ts +7 -0
  238. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.test.ts +15 -0
  239. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +65 -0
  240. package/rancher-components/Pill/RcStatusBadge/index.ts +2 -0
  241. package/rancher-components/Pill/RcStatusBadge/types.ts +5 -0
  242. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.test.ts +33 -0
  243. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +75 -0
  244. package/rancher-components/Pill/RcStatusIndicator/index.ts +2 -0
  245. package/rancher-components/Pill/RcStatusIndicator/types.ts +7 -0
  246. package/rancher-components/Pill/RcTag/RcTag.test.ts +64 -0
  247. package/rancher-components/Pill/RcTag/RcTag.vue +94 -0
  248. package/rancher-components/Pill/RcTag/index.ts +1 -0
  249. package/rancher-components/Pill/RcTag/types.ts +9 -0
  250. package/rancher-components/Pill/types.ts +3 -0
  251. package/rancher-components/RcButton/RcButton.vue +1 -1
  252. package/rancher-components/RcDropdown/RcDropdown.test.ts +98 -0
  253. package/rancher-components/RcDropdown/RcDropdown.vue +5 -0
  254. package/rancher-components/RcDropdown/RcDropdownItem.vue +7 -1
  255. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +2 -1
  256. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +2 -1
  257. package/rancher-components/RcDropdown/useDropdownContext.ts +21 -0
  258. package/rancher-components/RcDropdown/useDropdownItem.ts +30 -1
  259. package/rancher-components/RcItemCard/RcItemCard.test.ts +20 -0
  260. package/rancher-components/RcItemCard/RcItemCard.vue +41 -6
  261. package/rancher-components/RcItemCard/RcItemCardAction.vue +12 -0
  262. package/store/__tests__/catalog.test.ts +156 -1
  263. package/store/aws.js +19 -8
  264. package/store/catalog.js +10 -5
  265. package/store/type-map.js +3 -15
  266. package/types/extension-manager.ts +26 -0
  267. package/types/resources/settings.d.ts +1 -1
  268. package/types/shell/index.d.ts +149 -44
  269. package/types/uiplugins.ts +73 -0
  270. package/utils/__tests__/back-off.test.ts +354 -0
  271. package/utils/__tests__/kontainer.test.ts +19 -0
  272. package/utils/__tests__/product.test.ts +129 -0
  273. package/utils/__tests__/resource.test.ts +87 -0
  274. package/utils/__tests__/uiplugins.test.ts +84 -0
  275. package/utils/alertmanagerconfig.js +2 -2
  276. package/utils/auth.js +3 -76
  277. package/utils/back-off.ts +176 -0
  278. package/utils/dynamic-importer.js +8 -0
  279. package/utils/kontainer.ts +3 -5
  280. package/utils/product.ts +39 -0
  281. package/utils/resource.ts +35 -0
  282. package/utils/select.js +0 -24
  283. package/utils/style.ts +3 -0
  284. package/utils/uiplugins.ts +29 -2
  285. package/utils/validators/__tests__/setting.test.js +92 -0
  286. package/utils/validators/formRules/__tests__/index.test.ts +91 -7
  287. package/utils/validators/formRules/index.ts +84 -8
  288. package/utils/validators/setting.js +17 -0
  289. package/vue.config.js +1 -1
  290. package/cloud-credential/__tests__/harvester.test.ts +0 -18
  291. package/components/Resource/Detail/Metadata/Rectangle.vue +0 -34
  292. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +0 -24
  293. package/components/ResourceDetail/Masthead/__tests__/legacy.test.ts +0 -65
  294. package/components/ResourceDetail/__tests__/index.test.ts +0 -135
  295. package/components/ResourceDetail/legacy.vue +0 -562
  296. package/components/formatter/CloudCredExpired.vue +0 -69
  297. package/pages/explorer/resource/detail/configmap.vue +0 -42
  298. package/pages/explorer/resource/detail/projectsecret.vue +0 -9
  299. package/pages/explorer/resource/detail/secret.vue +0 -63
  300. package/utils/aws.js +0 -0
  301. /package/components/{ForceDirectedTreeChart.vue → ForceDirectedTreeChart/index.vue} +0 -0
package/pages/home.vue CHANGED
@@ -22,7 +22,7 @@ import { filterHiddenLocalCluster, filterOnlyKubernetesClusters, paginationFilte
22
22
  import TabTitle from '@shell/components/TabTitle.vue';
23
23
  import { ActionFindPageArgs } from '@shell/types/store/dashboard-store.types';
24
24
 
25
- import { RESET_CARDS_ACTION, SET_LOGIN_ACTION, SHOW_HIDE_BANNER_ACTION } from '@shell/config/page-actions';
25
+ import { SET_LOGIN_ACTION, SHOW_HIDE_BANNER_ACTION } from '@shell/config/page-actions';
26
26
  import { STEVE_NAME_COL, STEVE_STATE_COL } from '@shell/config/pagination-table-headers';
27
27
  import { PaginationParamFilter, FilterArgs, PaginationFilterField, PaginationArgs } from '@shell/types/store/pagination.types';
28
28
  import ProvCluster from '@shell/models/provisioning.cattle.io.cluster';
@@ -75,10 +75,6 @@ export default defineComponent({
75
75
  label: this.t('nav.header.showHideBanner'),
76
76
  action: SHOW_HIDE_BANNER_ACTION
77
77
  },
78
- {
79
- label: this.t('nav.header.restoreCards'),
80
- action: RESET_CARDS_ACTION
81
- },
82
78
  ],
83
79
  vendor: getVendor(),
84
80
 
@@ -368,10 +364,6 @@ export default defineComponent({
368
364
  */
369
365
  handlePageAction(action: any) {
370
366
  switch (action.action) {
371
- case RESET_CARDS_ACTION:
372
- this.resetCards();
373
- break;
374
-
375
367
  case SHOW_HIDE_BANNER_ACTION:
376
368
  this.toggleBanner();
377
369
  break;
@@ -79,6 +79,29 @@ const findAllGetter = (getters, type, opt) => {
79
79
  return opt.namespaced ? getters.matching(type, null, opt.namespaced, { skipSelector: true }) : getters.all(type);
80
80
  };
81
81
 
82
+ const createFindWatchArg = ({
83
+ type, id, opt, res
84
+ }) => {
85
+ const revision = typeof opt.revision !== 'undefined' ? opt.revision : res?.metadata?.resourceVersion;
86
+ const watchMsg = {
87
+ type,
88
+ id,
89
+ // Although not used by sockets, we need this for when resyncWatch calls find... which needs namespace to construct the url
90
+ namespace: opt.namespaced,
91
+ revision: revision || '',
92
+ force: opt.forceWatch === true,
93
+ };
94
+
95
+ const idx = id.indexOf('/');
96
+
97
+ if ( idx > 0 ) {
98
+ watchMsg.namespace = id.substr(0, idx);
99
+ watchMsg.id = id.substr(idx + 1);
100
+ }
101
+
102
+ return watchMsg;
103
+ };
104
+
82
105
  export default {
83
106
  request() {
84
107
  throw new Error('Not Implemented');
@@ -657,6 +680,12 @@ export default {
657
680
  out = getters.byId(type, id);
658
681
 
659
682
  if ( out ) {
683
+ if ( opt.watch !== false ) {
684
+ dispatch('watch', createFindWatchArg({
685
+ type, id, opt, res: undefined
686
+ }));
687
+ }
688
+
660
689
  return out;
661
690
  }
662
691
  }
@@ -669,26 +698,9 @@ export default {
669
698
  await dispatch('load', { data: res });
670
699
 
671
700
  if ( opt.watch !== false ) {
672
- const watchMsg = {
673
- type,
674
- id,
675
- // Although not used by sockets, we need this for when resyncWatch calls find... which needs namespace to construct the url
676
- namespace: opt.namespaced,
677
- // Override the revision. Used in cases where we need to avoid using the resource's own revision which would be `too old`.
678
- // For the above case opt.revision will be `null`. If left as `undefined` the subscribe mechanism will try to determine a revision
679
- // from resources in store (which would be this one, with the too old revision)
680
- revision: typeof opt.revision !== 'undefined' ? opt.revision : res?.metadata?.resourceVersion,
681
- force: opt.forceWatch === true,
682
- };
683
-
684
- const idx = id.indexOf('/');
685
-
686
- if ( idx > 0 ) {
687
- watchMsg.namespace = id.substr(0, idx);
688
- watchMsg.id = id.substr(idx + 1);
689
- }
690
-
691
- dispatch('watch', watchMsg);
701
+ dispatch('watch', createFindWatchArg({
702
+ type, id, opt, res
703
+ }));
692
704
  }
693
705
 
694
706
  out = getters.byId(type, id);
@@ -808,13 +820,21 @@ export default {
808
820
  return classify(ctx, resource.toJSON(), true);
809
821
  },
810
822
 
811
- // Forget a type in the store
812
- // Remove all entries for that type and stop watching it
823
+ /**
824
+ * Remove all cached entries for a resource and stop watches
825
+ */
813
826
  forgetType({ commit, dispatch, state }, type, compareWatches) {
827
+ // Stop all known watches
814
828
  state.started
815
829
  .filter((entry) => compareWatches ? compareWatches(entry) : entry.type === type)
816
830
  .forEach((entry) => dispatch('unwatch', entry));
817
831
 
832
+ // Stop all known back-off watch processes for this type
833
+ dispatch('resetWatchBackOff', {
834
+ type, compareWatches, resetStarted: false
835
+ });
836
+
837
+ // Remove entries from store
818
838
  commit('forgetType', type);
819
839
  },
820
840
 
@@ -36,6 +36,7 @@ import { handleConflict } from '@shell/plugins/dashboard-store/normalize';
36
36
  import { ExtensionPoint, ActionLocation } from '@shell/core/types';
37
37
  import { getApplicableExtensionEnhancements } from '@shell/core/plugin-helpers';
38
38
  import { parse } from '@shell/utils/selector';
39
+ import { importDrawer } from '@shell/utils/dynamic-importer';
39
40
 
40
41
  export const DNS_LIKE_TYPES = ['dnsLabel', 'dnsLabelRestricted', 'hostname'];
41
42
 
@@ -859,6 +860,10 @@ export default class Resource {
859
860
 
860
861
  // ------------------------------------------------------------------
861
862
 
863
+ get canEdit() {
864
+ return this.canUpdate && this.canCustomEdit;
865
+ }
866
+
862
867
  get availableActions() {
863
868
  const all = this._availableActions;
864
869
 
@@ -898,6 +903,26 @@ export default class Resource {
898
903
  return out;
899
904
  }
900
905
 
906
+ showConfiguration(returnFocusSelector) {
907
+ const onClose = () => this.$ctx.commit('slideInPanel/close', undefined, { root: true });
908
+
909
+ this.$ctx.commit('slideInPanel/open', {
910
+ component: importDrawer('ResourceDetailDrawer'),
911
+ componentProps: {
912
+ resource: this,
913
+ onClose,
914
+ width: '73%',
915
+ // We want this to be full viewport height top to bottom
916
+ height: '100vh',
917
+ top: '0',
918
+ 'z-index': 101, // We want this to be above the main side menu
919
+ closeOnRouteChange: ['name', 'params', 'query'], // We want to ignore hash changes, tables in extensions can trigger the drawer to close while opening
920
+ triggerFocusTrap: true,
921
+ returnFocusSelector
922
+ }
923
+ }, { root: true });
924
+ }
925
+
901
926
  // You can add custom actions by overriding your own availableActions (and probably reading super._availableActions)
902
927
  get _availableActions() {
903
928
  // get menu actions available by plugins configuration
@@ -905,6 +930,12 @@ export default class Resource {
905
930
  const extensionMenuActions = getApplicableExtensionEnhancements(this.$rootState, ExtensionPoint.ACTION, ActionLocation.TABLE, currentRoute, this);
906
931
 
907
932
  const all = [
933
+ {
934
+ action: 'showConfiguration',
935
+ label: this.t('action.showConfiguration'),
936
+ icon: 'icon icon-document',
937
+ enabled: this.disableResourceDetailDrawer !== true && (this.canCustomEdit || this.canYaml), // If the resource can't show an edit or a yaml we don't want to show the configuration drawer
938
+ },
908
939
  { divider: true },
909
940
  {
910
941
  action: this.canUpdate ? 'goToEdit' : 'goToViewConfig',
@@ -1816,6 +1847,55 @@ export default class Resource {
1816
1847
  return details;
1817
1848
  }
1818
1849
 
1850
+ get glance() {
1851
+ return this._glance;
1852
+ }
1853
+
1854
+ get _glance() {
1855
+ const type = this.parentNameOverride || this.$rootGetters['type-map/labelFor'](this.schema);
1856
+
1857
+ return [
1858
+ {
1859
+ name: 'state',
1860
+ label: this.t('component.resource.detail.glance.state'),
1861
+ formatter: 'BadgeStateFormatter',
1862
+ formatterOpts: { row: this },
1863
+ content: this.stateDisplay
1864
+ },
1865
+ {
1866
+ name: 'type',
1867
+ label: this.t('component.resource.detail.glance.type'),
1868
+ formatter: 'Link',
1869
+ formatterOpts: {
1870
+ to: this.listLocation, row: {}, options: { internal: true }
1871
+ },
1872
+ content: type
1873
+ },
1874
+ {
1875
+ name: 'namespace',
1876
+ label: this.t('component.resource.detail.glance.namespace'),
1877
+ formatter: 'Link',
1878
+ formatterOpts: {
1879
+ to: {
1880
+ name: `c-cluster-product-resource-id`,
1881
+ product: this.$rootGetters['currentProduct'].id,
1882
+ cluster: this.$rootGetters['currentCluster'].id,
1883
+ resource: this.type
1884
+ },
1885
+ row: {},
1886
+ options: { internal: true }
1887
+ },
1888
+ content: this.namespacedName
1889
+ },
1890
+ {
1891
+ name: 'age',
1892
+ label: this.t('component.resource.detail.glance.age'),
1893
+ formatter: 'LiveDate',
1894
+ content: this.creationTimestamp
1895
+ }
1896
+ ];
1897
+ }
1898
+
1819
1899
  get t() {
1820
1900
  return this.$rootGetters['i18n/t'];
1821
1901
  }
@@ -120,7 +120,7 @@ describe('steve: getters:', () => {
120
120
  expect(urlOptionsGetter('/v1/foo', { excludeFields: ['bar'] })).toBe('/v1/foo?exclude=bar');
121
121
  });
122
122
  it('returns a string without an exclude statement for "managedFields" if omitExcludeFields includes it and the url starts with "/v1/"', () => {
123
- expect(urlOptionsGetter('/v1/foo', { omitExcludeFields: ['metadata.managedFields'] })).toBe('/v1/foo?');
123
+ expect(urlOptionsGetter('/v1/foo', { omitExcludeFields: ['metadata.managedFields'] })).toBe('/v1/foo');
124
124
  });
125
125
  it('returns a string without an exclude statement if excludeFields is set but the url does not start with "/v1/"', () => {
126
126
  expect(urlOptionsGetter('foo', { excludeFields: ['bar'] })).toBe('foo');
@@ -1,4 +1,7 @@
1
- import { actions, getters } from '../subscribe';
1
+ import { actions, getters, mutations } from '../subscribe';
2
+ import { REVISION_TOO_OLD } from '../../../utils/socket';
3
+ import { STEVE_WATCH_EVENT } from '../../../types/store/subscribe.types';
4
+ import backOff from '../../../utils/back-off';
2
5
 
3
6
  describe('steve: subscribe', () => {
4
7
  describe('actions', () => {
@@ -171,4 +174,259 @@ describe('steve: subscribe', () => {
171
174
  });
172
175
  });
173
176
  });
177
+
178
+ describe('backoff', () => {
179
+ const waitForBackOff = async(advanceTimersByTime = 20000) => {
180
+ jest.advanceTimersByTime(advanceTimersByTime);
181
+ // jest.advanceTimersByTime(advanceTimersByTime);
182
+ await Promise.resolve();
183
+ await Promise.resolve();
184
+ await Promise.resolve();
185
+ };
186
+
187
+ describe('stale cache in replicate that handles watch', () => {
188
+ /**
189
+ 1. ui makes http request.
190
+ - it's handled by up-to-date replica A
191
+ - response contains an up-to-date revision X
192
+ 2. ui makes watch request referencing up-to-date revision X from A
193
+ - it's received by replica B with a stale cache which does not contain revision X.
194
+ - replicate B rejects watch with unknown revision message (i.e. 'too old')
195
+ 3. ui receives unknown revision and makes a new request
196
+ - this should backoff until eventually succeeding
197
+ */
198
+
199
+ const startWatch = ({
200
+ ctx,
201
+ obj, msg,
202
+ revision
203
+ }) => {
204
+ const {
205
+ state, dispatch, getters, rootGetters, commit
206
+ } = ctx;
207
+
208
+ // call watch
209
+ actions.watch({
210
+ state, dispatch, getters, rootGetters
211
+ }, {
212
+ ...obj,
213
+ revision,
214
+ mode: STEVE_WATCH_EVENT.CHANGES,
215
+ force: true,
216
+ });
217
+
218
+ expect(dispatch).toHaveBeenNthCalledWith(1, 'unwatchIncompatible', {
219
+ id: undefined, mode: STEVE_WATCH_EVENT.CHANGES, namespace: undefined, selector: undefined, type: obj.type
220
+ });
221
+
222
+ expect(dispatch).toHaveBeenNthCalledWith(2, 'send', {
223
+ debounceMs: 4000,
224
+ mode: 'resource.changes',
225
+ resourceType: obj.type,
226
+ resourceVersion: revision.toString(),
227
+ });
228
+
229
+ // Receive start from BE
230
+ actions['ws.resource.start']({
231
+ state, dispatch, getters, commit
232
+ }, { ...msg });
233
+
234
+ expect(dispatch).toHaveBeenCalledTimes(2);
235
+ dispatch.mockClear();
236
+ };
237
+
238
+ const errorWatch = ({
239
+ ctx,
240
+ obj, msg,
241
+ }) => {
242
+ const {
243
+ state, dispatch, getters, commit
244
+ } = ctx;
245
+
246
+ // Receive error from BE
247
+ actions['ws.resource.error']({
248
+ dispatch, getters, commit
249
+ }, {
250
+ ...msg,
251
+ data: { error: 'too old' }
252
+ });
253
+ expect(state.inError).toStrictEqual(
254
+ {
255
+ 'type=abc,namespace=,id=,selector=': {
256
+ obj: {
257
+ type: msg.resourceType,
258
+ mode: msg.mode,
259
+ },
260
+ reason: REVISION_TOO_OLD
261
+ }
262
+ }
263
+ );
264
+
265
+ // Receive stop from BE
266
+ actions['ws.resource.stop']({
267
+ state, dispatch, getters, commit
268
+ }, { ...msg });
269
+ // stop tries to watch again, however we're in error so will be ignored
270
+ expect(dispatch).toHaveBeenNthCalledWith(1, 'watch', {
271
+ id: undefined, mode: STEVE_WATCH_EVENT.CHANGES, namespace: undefined, selector: undefined, type: obj.type
272
+ });
273
+
274
+ dispatch.mockClear();
275
+ };
276
+
277
+ const cycleFail = async({
278
+ ctx,
279
+ obj, msg,
280
+ revision,
281
+ tooManyTries = false,
282
+ }) => {
283
+ const { dispatch } = ctx;
284
+
285
+ startWatch({
286
+ ctx, obj, msg, revision
287
+ });
288
+ errorWatch({
289
+ ctx, obj, msg
290
+ });
291
+
292
+ await waitForBackOff(50000);
293
+ await waitForBackOff(50000);
294
+
295
+ if (tooManyTries) {
296
+ expect(dispatch).toHaveBeenCalledTimes(0);
297
+ } else {
298
+ expect(dispatch).toHaveBeenCalledTimes(1);
299
+ expect(dispatch).toHaveBeenCalledWith('resyncWatch', {
300
+ ...msg,
301
+ data: { error: 'too old' }
302
+ });
303
+ }
304
+
305
+ await waitForBackOff();
306
+
307
+ if (tooManyTries) {
308
+ expect(dispatch).toHaveBeenCalledTimes(0);
309
+ } else {
310
+ expect(dispatch).toHaveBeenCalledTimes(1);
311
+ }
312
+
313
+ dispatch.mockClear();
314
+ };
315
+
316
+ const cycleSucceed = async({
317
+ ctx,
318
+ obj, msg,
319
+ revision
320
+ }) => {
321
+ const { dispatch } = ctx;
322
+
323
+ dispatch.mockImplementation(async(type: string) => {
324
+ if (type === 'resyncWatch') {
325
+ return Promise.resolve();
326
+ }
327
+ });
328
+
329
+ startWatch({
330
+ ctx, obj, msg, revision
331
+ });
332
+
333
+ await waitForBackOff();
334
+
335
+ expect(dispatch).toHaveBeenCalledTimes(0);
336
+
337
+ await waitForBackOff();
338
+
339
+ expect(dispatch).toHaveBeenCalledTimes(0);
340
+
341
+ dispatch.mockClear();
342
+ };
343
+
344
+ const dispatch = jest.fn();
345
+ const rootGetters = {
346
+ 'type-map/isSpoofed': () => false,
347
+ 'management/byId': () => ({ value: true })
348
+ };
349
+ const obj = { type: 'abc' };
350
+ const msg = {
351
+ resourceType: obj.type,
352
+ mode: STEVE_WATCH_EVENT.CHANGES,
353
+ };
354
+
355
+ const initStore = () => {
356
+ const state = { started: [], inError: {} };
357
+ const _getters = {
358
+ normalizeType: (type: string) => type,
359
+ schemaFor: () => ({}),
360
+ storeName: 'test',
361
+ inError: (...args) => getters.inError(state)(...args),
362
+ watchStarted: (...args) => getters.watchStarted(state)(...args),
363
+ backOffId: (...args) => getters.backOffId()(...args),
364
+ canBackoff: () => true,
365
+ };
366
+ const commit = (type, ...args) => mutations[type](state, ...args);
367
+
368
+ return {
369
+ state, dispatch, getters: _getters, rootGetters, commit
370
+ };
371
+ };
372
+
373
+ beforeAll(() => {
374
+ jest.useFakeTimers();
375
+ });
376
+
377
+ afterEach(() => {
378
+ backOff.resetAll();
379
+ dispatch.mockClear();
380
+ });
381
+
382
+ // eslint-disable-next-line jest/expect-expect
383
+ it('succeeds', async() => {
384
+ jest.useFakeTimers();
385
+
386
+ const ctx = initStore();
387
+
388
+ await cycleSucceed({
389
+ ctx, msg, obj, revision: 1
390
+ });
391
+ });
392
+
393
+ // eslint-disable-next-line jest/expect-expect
394
+ it('succeeds after a few failures', async() => {
395
+ jest.useFakeTimers();
396
+
397
+ const ctx = initStore();
398
+
399
+ await cycleFail({
400
+ ctx, msg, obj, revision: 1
401
+ });
402
+ await cycleFail({
403
+ ctx, msg, obj, revision: 1
404
+ });
405
+ await cycleFail({
406
+ ctx, msg, obj, revision: 1
407
+ });
408
+ await cycleFail({
409
+ ctx, msg, obj, revision: 1
410
+ });
411
+ await cycleSucceed({
412
+ ctx, msg, obj, revision: 1
413
+ });
414
+ });
415
+
416
+ // eslint-disable-next-line jest/expect-expect
417
+ it('never succeeds', async() => {
418
+ const ctx = initStore();
419
+
420
+ for (let i = 0; i < 10; i++) {
421
+ await cycleFail({
422
+ ctx, msg, obj, revision: 1
423
+ });
424
+ }
425
+
426
+ await cycleFail({
427
+ ctx, msg, obj, revision: 1, tooManyTries: true
428
+ });
429
+ });
430
+ });
431
+ });
174
432
  });
@@ -154,9 +154,15 @@ export default {
154
154
  opt.excludeFields = Array.isArray(opt?.omitExcludeFields) ? excludeFields.filter((f) => !f.includes(opt.omitExcludeFields)) : excludeFields;
155
155
  }
156
156
 
157
- const excludeParamsString = opt.excludeFields.map((field) => `exclude=${ field }`).join('&');
157
+ if (opt.excludeFields.length) {
158
+ const excludeParamsString = opt.excludeFields.map((field) => `exclude=${ field }`).join('&');
158
159
 
159
- url += `${ url.includes('?') ? '&' : '?' }${ excludeParamsString }`;
160
+ url += `${ url.includes('?') ? '&' : '?' }${ excludeParamsString }`;
161
+ }
162
+
163
+ if (opt.revision) {
164
+ url += `${ url.includes('?') ? '&' : '?' }${ `revision=${ opt.revision }` }`;
165
+ }
160
166
  }
161
167
  // End: Exclude
162
168
 
@@ -36,12 +36,19 @@ export const WATCH_STATUSES = {
36
36
  REMOVE_REQUESTED: 'removed_requested'
37
37
  };
38
38
 
39
+ /**
40
+ * Create a unique key for a specific resource watch's params
41
+ */
39
42
  export const keyForSubscribe = ({
40
43
  resourceType, type, namespace, id, selector
41
44
  } = {}) => {
42
- return [(resourceType || type), namespace, id, selector] // each watch param in an array
43
- .filter((param) => !!param) // filter out all the empty ones // the filter makes these keys neater
44
- .join('/'); // join into a string so we can use it as an object key
45
+ const keyMap = {
46
+ type: resourceType || type, namespace, id, selector
47
+ };
48
+
49
+ return Object.entries(keyMap)
50
+ .map(([prop, value]) => `${ prop }=${ value || '' }`)
51
+ .join(',');
45
52
  };
46
53
 
47
54
  export const watchKeyFromMessage = (msg) => {