@rancher/shell 3.0.8-rc.1 → 3.0.8-rc.12

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 (345) hide show
  1. package/assets/brand/suse/banner.svg +1 -0
  2. package/assets/brand/suse/dark/banner.svg +1 -0
  3. package/assets/brand/suse/dark/login-landscape.svg +1 -0
  4. package/assets/brand/suse/dark/rancher-logo.svg +1 -1
  5. package/assets/brand/suse/favicon.png +0 -0
  6. package/assets/brand/suse/login-landscape.svg +1 -0
  7. package/assets/brand/suse/metadata.json +11 -1
  8. package/assets/brand/suse/rancher-logo.svg +1 -1
  9. package/assets/fonts/suse/suse-v2-latin-300.woff +0 -0
  10. package/assets/fonts/suse/suse-v2-latin-300.woff2 +0 -0
  11. package/assets/fonts/suse/suse-v2-latin-600.woff +0 -0
  12. package/assets/fonts/suse/suse-v2-latin-600.woff2 +0 -0
  13. package/assets/fonts/suse/suse-v2-latin-700.woff +0 -0
  14. package/assets/fonts/suse/suse-v2-latin-700.woff2 +0 -0
  15. package/assets/fonts/suse/suse-v2-latin-800.woff +0 -0
  16. package/assets/fonts/suse/suse-v2-latin-800.woff2 +0 -0
  17. package/assets/fonts/suse/suse-v2-latin-regular.woff +0 -0
  18. package/assets/fonts/suse/suse-v2-latin-regular.woff2 +0 -0
  19. package/assets/images/content/README.md +5 -0
  20. package/assets/images/content/cloud-native.svg +84 -0
  21. package/assets/images/content/dark/cloud-native.svg +21 -0
  22. package/assets/images/content/dark/shield.svg +59 -0
  23. package/assets/images/content/dark/suse.svg +10 -0
  24. package/assets/images/content/shield.svg +59 -0
  25. package/assets/images/content/suse.svg +10 -0
  26. package/assets/styles/base/_typography.scss +1 -0
  27. package/assets/styles/fonts/_fontstack.scss +53 -1
  28. package/assets/styles/global/_cards.scss +0 -3
  29. package/assets/styles/global/_layout.scss +21 -35
  30. package/assets/styles/themes/_dark.scss +1 -1
  31. package/assets/styles/themes/_light.scss +1 -1
  32. package/assets/styles/themes/_modern.scss +11 -3
  33. package/assets/styles/themes/_suse.scss +116 -24
  34. package/assets/translations/en-us.yaml +94 -10
  35. package/components/AutoscalerCard.vue +113 -0
  36. package/components/AutoscalerTab.vue +94 -0
  37. package/components/BackLink.vue +8 -0
  38. package/components/BannerGraphic.vue +36 -21
  39. package/components/BrandImage.vue +17 -6
  40. package/components/ClusterIconMenu.vue +1 -1
  41. package/components/ClusterProviderIcon.vue +1 -1
  42. package/components/Cron/CronExpressionEditor.vue +1 -1
  43. package/components/Cron/CronExpressionEditorModal.vue +1 -1
  44. package/components/Drawer/Chrome.vue +2 -6
  45. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +4 -9
  46. package/components/Drawer/ResourceDetailDrawer/YamlTab.vue +3 -8
  47. package/components/Drawer/ResourceDetailDrawer/composables.ts +3 -4
  48. package/components/Drawer/ResourceDetailDrawer/index.vue +4 -9
  49. package/components/Drawer/ResourceDetailDrawer/types.ts +17 -0
  50. package/components/Drawer/types.ts +3 -0
  51. package/components/DynamicContent/DynamicContentBanner.vue +102 -0
  52. package/components/DynamicContent/DynamicContentCloseButton.vue +42 -0
  53. package/components/DynamicContent/DynamicContentIcon.vue +132 -0
  54. package/components/DynamicContent/DynamicContentPanel.vue +112 -0
  55. package/components/DynamicContent/content.ts +78 -0
  56. package/components/EmberPage.vue +1 -1
  57. package/components/IconOrSvg.vue +2 -2
  58. package/components/PaginatedResourceTable.vue +2 -6
  59. package/components/PopoverCard.vue +192 -0
  60. package/components/Questions/__tests__/index.test.ts +159 -0
  61. package/components/Resource/Detail/CopyToClipboard.vue +4 -1
  62. package/components/Resource/Detail/FetchLoader/composables.ts +18 -4
  63. package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
  64. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/identifying-fields.test.ts +1 -1
  65. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +4 -0
  66. package/components/Resource/Detail/Metadata/KeyValueRow.vue +1 -1
  67. package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
  68. package/components/Resource/Detail/Metadata/composables.ts +9 -9
  69. package/components/Resource/Detail/Metadata/index.vue +3 -3
  70. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -19
  71. package/components/Resource/Detail/ResourcePopover/__tests__/ResourcePopoverCard.test.ts +0 -29
  72. package/components/Resource/Detail/ResourcePopover/__tests__/index.test.ts +132 -150
  73. package/components/Resource/Detail/ResourcePopover/index.vue +54 -159
  74. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +0 -2
  75. package/components/Resource/Detail/TitleBar/composables.ts +2 -1
  76. package/components/Resource/Detail/TitleBar/index.vue +10 -6
  77. package/components/Resource/Detail/composables.ts +12 -0
  78. package/components/ResourceDetail/Masthead/latest.vue +29 -0
  79. package/components/ResourceDetail/index.vue +4 -1
  80. package/components/ResourceList/Masthead.vue +1 -1
  81. package/components/ResourceTable.vue +1 -1
  82. package/components/SortableTable/index.vue +2 -1
  83. package/components/Tabbed/__tests__/index.test.ts +86 -0
  84. package/components/{nav/WindowManager → Window}/ContainerLogs.vue +1 -1
  85. package/components/{nav/WindowManager → Window}/ContainerLogsActions.vue +1 -0
  86. package/components/{nav/WindowManager → Window}/__tests__/ContainerLogs.test.ts +1 -1
  87. package/components/{nav/WindowManager → Window}/__tests__/ContainerShell.test.ts +2 -2
  88. package/components/__tests__/AutoscalerCard.test.ts +154 -0
  89. package/components/__tests__/AutoscalerTab.test.ts +125 -0
  90. package/components/__tests__/PopoverCard.test.ts +204 -0
  91. package/components/auth/SelectPrincipal.vue +24 -6
  92. package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
  93. package/components/auth/login/ldap.vue +3 -3
  94. package/components/form/NodeScheduling.vue +2 -2
  95. package/components/formatter/Autoscaler.vue +97 -0
  96. package/components/formatter/InternalExternalIP.vue +198 -24
  97. package/components/formatter/__tests__/Autoscaler.test.ts +156 -0
  98. package/components/formatter/__tests__/InternalExternalIP.test.ts +133 -0
  99. package/components/google/util/__tests__/formatter.test.ts +47 -0
  100. package/components/google/util/formatter.ts +5 -2
  101. package/components/nav/Group.vue +21 -5
  102. package/components/nav/Header.vue +37 -17
  103. package/components/nav/NamespaceFilter.vue +13 -1
  104. package/components/nav/NotificationCenter/index.vue +2 -1
  105. package/components/nav/TopLevelMenu.helper.ts +16 -6
  106. package/components/nav/TopLevelMenu.vue +4 -2
  107. package/components/nav/Type.vue +8 -3
  108. package/components/{DraggableZone.vue → nav/WindowManager/PinArea.vue} +47 -80
  109. package/components/nav/WindowManager/composables/useComponentsMount.ts +70 -0
  110. package/components/nav/WindowManager/composables/useDimensionsHandler.ts +105 -0
  111. package/components/nav/WindowManager/composables/useDragHandler.ts +99 -0
  112. package/components/nav/WindowManager/composables/usePanelHandler.ts +72 -0
  113. package/components/nav/WindowManager/composables/usePanelsHandler.ts +14 -0
  114. package/components/nav/WindowManager/composables/useResizeHandler.ts +167 -0
  115. package/components/nav/WindowManager/composables/useTabsHandler.ts +51 -0
  116. package/components/nav/WindowManager/constants.ts +23 -0
  117. package/components/nav/WindowManager/index.vue +61 -575
  118. package/components/nav/WindowManager/panels/HorizontalPanel.vue +265 -0
  119. package/components/nav/WindowManager/panels/TabBodyContainer.vue +39 -0
  120. package/components/nav/WindowManager/panels/VerticalPanel.vue +308 -0
  121. package/components/nav/__tests__/Type.test.ts +59 -0
  122. package/components/templates/default.vue +4 -40
  123. package/components/templates/home.vue +31 -5
  124. package/components/templates/plain.vue +30 -4
  125. package/components/templates/standalone.vue +1 -1
  126. package/composables/useI18n.ts +10 -1
  127. package/composables/useInterval.ts +15 -0
  128. package/config/__test__/uiplugins.test.ts +309 -0
  129. package/config/labels-annotations.js +9 -1
  130. package/config/product/explorer.js +3 -1
  131. package/config/product/manager.js +20 -9
  132. package/config/router/navigation-guards/clusters.js +3 -3
  133. package/config/router/navigation-guards/products.js +1 -1
  134. package/config/router/routes.js +10 -2
  135. package/config/settings.ts +2 -1
  136. package/config/store.js +4 -2
  137. package/config/table-headers.js +8 -0
  138. package/config/types.js +9 -0
  139. package/config/uiplugins.js +46 -2
  140. package/config/version.js +1 -1
  141. package/core/__test__/extension-manager-impl.test.js +236 -0
  142. package/core/extension-manager-impl.js +21 -4
  143. package/core/plugin-helpers.ts +4 -2
  144. package/core/plugins-loader.js +2 -2
  145. package/core/types-provisioning.ts +8 -1
  146. package/detail/pod.vue +1 -0
  147. package/detail/provisioning.cattle.io.cluster.vue +19 -7
  148. package/dialog/DeveloperLoadExtensionDialog.vue +13 -4
  149. package/dialog/RollbackWorkloadDialog.vue +2 -5
  150. package/dialog/SearchDialog.vue +1 -0
  151. package/directives/ui-context.ts +103 -0
  152. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +2 -2
  153. package/edit/auth/__tests__/oidc.test.ts +26 -0
  154. package/edit/auth/github.vue +5 -0
  155. package/edit/auth/oidc.vue +5 -1
  156. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
  157. package/edit/cloudcredential.vue +1 -1
  158. package/edit/configmap.vue +1 -0
  159. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  160. package/edit/fleet.cattle.io.gitrepo.vue +0 -10
  161. package/edit/fleet.cattle.io.helmop.vue +6 -6
  162. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  163. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  164. package/edit/logging-flow/index.vue +1 -0
  165. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  166. package/edit/management.cattle.io.fleetworkspace.vue +1 -1
  167. package/edit/management.cattle.io.project.vue +1 -0
  168. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
  169. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
  170. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  171. package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
  172. package/edit/monitoring.coreos.com.route.vue +1 -1
  173. package/edit/namespace.vue +1 -0
  174. package/edit/networking.istio.io.destinationrule/index.vue +1 -0
  175. package/edit/networking.k8s.io.ingress/index.vue +1 -0
  176. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
  177. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
  178. package/edit/node.vue +1 -0
  179. package/edit/persistentvolume/index.vue +27 -22
  180. package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
  181. package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
  182. package/edit/persistentvolume/plugins/azureFile.vue +15 -14
  183. package/edit/persistentvolume/plugins/cephfs.vue +15 -14
  184. package/edit/persistentvolume/plugins/cinder.vue +15 -14
  185. package/edit/persistentvolume/plugins/csi.vue +18 -16
  186. package/edit/persistentvolume/plugins/fc.vue +13 -14
  187. package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
  188. package/edit/persistentvolume/plugins/flocker.vue +1 -3
  189. package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
  190. package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
  191. package/edit/persistentvolume/plugins/hostPath.vue +40 -39
  192. package/edit/persistentvolume/plugins/iscsi.vue +13 -14
  193. package/edit/persistentvolume/plugins/local.vue +1 -3
  194. package/edit/persistentvolume/plugins/longhorn.vue +23 -22
  195. package/edit/persistentvolume/plugins/nfs.vue +15 -14
  196. package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
  197. package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
  198. package/edit/persistentvolume/plugins/quobyte.vue +15 -14
  199. package/edit/persistentvolume/plugins/rbd.vue +15 -14
  200. package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
  201. package/edit/persistentvolume/plugins/storageos.vue +15 -14
  202. package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
  203. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +32 -5
  204. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +35 -0
  205. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +155 -0
  206. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +21 -21
  207. package/edit/provisioning.cattle.io.cluster/index.vue +28 -18
  208. package/edit/provisioning.cattle.io.cluster/rke2.vue +50 -16
  209. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +107 -5
  210. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +92 -4
  211. package/edit/secret/index.vue +1 -1
  212. package/edit/service.vue +9 -4
  213. package/edit/serviceaccount.vue +1 -0
  214. package/edit/storage.k8s.io.storageclass/index.vue +1 -0
  215. package/edit/workload/index.vue +2 -1
  216. package/edit/workload/mixins/workload.js +1 -1
  217. package/initialize/App.vue +4 -4
  218. package/initialize/install-directives.js +2 -0
  219. package/initialize/install-plugins.js +19 -2
  220. package/list/provisioning.cattle.io.cluster.vue +15 -2
  221. package/machine-config/amazonec2.vue +42 -135
  222. package/machine-config/components/EC2Networking.vue +490 -0
  223. package/machine-config/components/__tests__/EC2Networking.test.ts +148 -0
  224. package/machine-config/components/__tests__/utils/vpcSubnetMockData.js +294 -0
  225. package/machine-config/digitalocean.vue +11 -0
  226. package/machine-config/google.vue +1 -1
  227. package/mixins/__tests__/brand.spec.ts +2 -2
  228. package/mixins/__tests__/chart.test.ts +21 -0
  229. package/mixins/brand.js +1 -7
  230. package/mixins/chart.js +7 -1
  231. package/mixins/create-edit-view/index.js +5 -0
  232. package/models/__tests__/chart.test.ts +33 -4
  233. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +112 -5
  234. package/models/chart.js +25 -13
  235. package/models/cluster/node.js +13 -6
  236. package/models/cluster.x-k8s.io.machine.js +10 -20
  237. package/models/cluster.x-k8s.io.machinedeployment.js +5 -1
  238. package/models/management.cattle.io.cluster.js +21 -3
  239. package/models/management.cattle.io.kontainerdriver.js +1 -0
  240. package/models/provisioning.cattle.io.cluster.js +249 -33
  241. package/package.json +8 -7
  242. package/pages/auth/login.vue +41 -5
  243. package/pages/auth/setup.vue +1 -1
  244. package/pages/auth/verify.vue +3 -3
  245. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
  246. package/pages/c/_cluster/apps/charts/chart.vue +33 -15
  247. package/pages/c/_cluster/apps/charts/index.vue +11 -13
  248. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  249. package/pages/c/_cluster/explorer/index.vue +8 -6
  250. package/pages/c/_cluster/manager/hostedprovider/index.vue +220 -0
  251. package/pages/c/_cluster/settings/brand.vue +1 -1
  252. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
  253. package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
  254. package/pages/c/_cluster/uiplugins/index.vue +126 -184
  255. package/pages/home.vue +14 -4
  256. package/pkg/auto-import.js +3 -3
  257. package/pkg/dynamic-importer.lib.js +5 -1
  258. package/pkg/import.js +1 -1
  259. package/plugins/dashboard-client-init.js +3 -0
  260. package/plugins/dashboard-store/getters.js +19 -2
  261. package/plugins/dashboard-store/model-loader.js +1 -1
  262. package/plugins/dashboard-store/resource-class.js +10 -6
  263. package/plugins/dynamic-content.js +13 -0
  264. package/plugins/i18n.js +8 -0
  265. package/plugins/plugin.js +2 -2
  266. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +333 -0
  267. package/plugins/steve/steve-class.js +1 -1
  268. package/plugins/steve/steve-pagination-utils.ts +39 -20
  269. package/plugins/steve/subscribe.js +17 -9
  270. package/plugins/subscribe-events.ts +4 -2
  271. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  272. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -34
  273. package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
  274. package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
  275. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
  276. package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
  277. package/rancher-components/Pill/types.ts +0 -1
  278. package/rancher-components/RcDropdown/RcDropdownItem.vue +1 -0
  279. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +5 -1
  280. package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
  281. package/rancher-components/RcIcon/RcIcon.vue +46 -0
  282. package/rancher-components/RcIcon/index.ts +1 -0
  283. package/rancher-components/RcIcon/types.ts +160 -0
  284. package/rancher-components/utils/status.test.ts +67 -0
  285. package/rancher-components/utils/status.ts +77 -0
  286. package/scripts/typegen.sh +1 -0
  287. package/store/__tests__/catalog.test.ts +1 -1
  288. package/store/action-menu.js +8 -0
  289. package/store/auth.js +4 -4
  290. package/store/catalog.js +6 -0
  291. package/store/features.js +1 -0
  292. package/store/i18n.js +3 -3
  293. package/store/index.js +40 -19
  294. package/store/notifications.ts +51 -4
  295. package/store/plugins.js +7 -3
  296. package/store/prefs.js +6 -6
  297. package/store/type-map.js +7 -7
  298. package/store/ui-context.ts +86 -0
  299. package/store/wm.ts +244 -0
  300. package/types/notifications/index.ts +27 -3
  301. package/types/shell/index.d.ts +80 -4
  302. package/types/store/__tests__/pagination.types.spec.ts +137 -0
  303. package/types/store/pagination.types.ts +157 -9
  304. package/types/store/subscribe-events.types.ts +8 -1
  305. package/types/store/subscribe.types.ts +1 -0
  306. package/types/window-manager.ts +24 -0
  307. package/utils/__tests__/object.test.ts +19 -0
  308. package/utils/__tests__/provider.test.ts +98 -0
  309. package/utils/__tests__/selector-typed.test.ts +263 -0
  310. package/utils/__tests__/version.test.ts +19 -1
  311. package/utils/autoscaler-utils.ts +7 -0
  312. package/utils/back-off.ts +3 -3
  313. package/utils/brand.ts +29 -0
  314. package/utils/chart.js +18 -0
  315. package/utils/color.js +1 -1
  316. package/utils/dynamic-content/__tests__/announcement.test.ts +498 -0
  317. package/utils/dynamic-content/__tests__/info.test.ts +21 -9
  318. package/utils/dynamic-content/announcement.ts +142 -0
  319. package/utils/dynamic-content/example.json +40 -0
  320. package/utils/dynamic-content/index.ts +6 -2
  321. package/utils/dynamic-content/info.ts +44 -2
  322. package/utils/dynamic-content/new-release.ts +1 -1
  323. package/utils/dynamic-content/notification-handler.ts +48 -0
  324. package/utils/dynamic-content/types.d.ts +53 -1
  325. package/utils/dynamic-importer.js +2 -2
  326. package/utils/favicon.js +4 -4
  327. package/utils/object.js +20 -2
  328. package/utils/pagination-utils.ts +2 -2
  329. package/utils/pagination-wrapper.ts +13 -9
  330. package/utils/provider.ts +14 -0
  331. package/utils/scroll.js +7 -0
  332. package/utils/selector-typed.ts +6 -2
  333. package/utils/settings.ts +15 -0
  334. package/utils/unit-tests/pagination-utils.spec.ts +8 -8
  335. package/utils/validators/machine-pool.ts +13 -3
  336. package/utils/version.js +15 -0
  337. package/vue.config.js +3 -3
  338. package/assets/images/icons/document.svg +0 -3
  339. package/plugins/nuxt-client-init.js +0 -3
  340. package/store/wm.js +0 -95
  341. /package/components/{nav/WindowManager → Window}/ChartReadme.vue +0 -0
  342. /package/components/{nav/WindowManager → Window}/ContainerShell.vue +0 -0
  343. /package/components/{nav/WindowManager → Window}/KubectlShell.vue +0 -0
  344. /package/components/{nav/WindowManager → Window}/MachineSsh.vue +0 -0
  345. /package/components/{nav/WindowManager → Window}/Window.vue +0 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ *
3
+ * The code in this file is responsible for adding 'announcement 'notifications driven off of the dynamic content metadata
4
+ *
5
+ * Announcements will be able to be shown in different places in the UI
6
+ *
7
+ */
8
+
9
+ import semver from 'semver';
10
+ import { NotificationLevel, Notification } from '@shell/types/notifications';
11
+ import { READ_ANNOUNCEMENTS } from '@shell/store/prefs';
12
+ import { Context, VersionInfo, Announcement } from './types';
13
+ import { DynamicContentAnnouncementHandlerName } from './notification-handler';
14
+
15
+ // Prefixes used in the notifications IDs created here
16
+ export const ANNOUNCEMENT_PREFIX = 'announcement-';
17
+
18
+ const TARGET_NOTIFICATION_CENTER = 'notification';
19
+ const TARGET_HOME_PAGE = 'homepage';
20
+ const ALLOWED_TARGETS = [TARGET_NOTIFICATION_CENTER, TARGET_HOME_PAGE];
21
+
22
+ const ALLOWED_NOTIFICATIONS: Record<string, NotificationLevel> = {
23
+ announcement: NotificationLevel.Announcement,
24
+ info: NotificationLevel.Info,
25
+ warning: NotificationLevel.Warning,
26
+ homepage: NotificationLevel.Hidden,
27
+ };
28
+
29
+ /**
30
+ * Main exported function that will process the announcements
31
+ *
32
+ * @param context Context helper providing access to config, logger, store
33
+ * @param announcements Announcement information
34
+ * @param versionInfo Version information
35
+ */
36
+ export async function processAnnouncements(context: Context, announcements: Announcement[] | undefined, versionInfo: VersionInfo): Promise<void> {
37
+ if (!announcements || !announcements.length || !versionInfo?.version) {
38
+ return;
39
+ }
40
+
41
+ const { dispatch, getters, logger } = context;
42
+
43
+ // Process each announcement
44
+ await Promise.all(announcements.map(async(announcement: Announcement) => {
45
+ // Check version
46
+ if (announcement.version && !semver.satisfies(versionInfo.version, announcement.version)) {
47
+ return;
48
+ }
49
+
50
+ // Check audience (currently only admin or all, but may add more in the future)
51
+ if (announcement.audience === 'admin' && !context.isAdmin) {
52
+ return;
53
+ }
54
+
55
+ if (!announcement.id) {
56
+ logger.error(`No ID For announcement - not going to add a notification for the announcement`);
57
+
58
+ return;
59
+ }
60
+
61
+ // Check type
62
+ const targetSplit = announcement.target.split('/');
63
+ const target = targetSplit[0];
64
+
65
+ // Make sure that the target is supported
66
+ if (ALLOWED_TARGETS.includes(target)) {
67
+ let level = NotificationLevel.Announcement;
68
+ let data: any = {};
69
+
70
+ if (target === TARGET_NOTIFICATION_CENTER) {
71
+ // Show a notification
72
+ const subType = targetSplit.length === 2 ? targetSplit[1] : 'announcement';
73
+
74
+ if (!(subType in ALLOWED_NOTIFICATIONS)) {
75
+ logger.error(`Announcement notification type ${ subType } is not supported`);
76
+
77
+ return;
78
+ }
79
+
80
+ level = ALLOWED_NOTIFICATIONS[subType];
81
+ } else if (target === TARGET_HOME_PAGE) {
82
+ level = NotificationLevel.Hidden;
83
+ data = {
84
+ icon: announcement.icon,
85
+ location: targetSplit.length === 2 ? targetSplit[1] : 'banner',
86
+ };
87
+
88
+ if (announcement.style) {
89
+ data.style = announcement.style;
90
+ }
91
+ }
92
+
93
+ logger.info(`Going to add a notification for announcement ${ announcement.target }`);
94
+
95
+ // We should check if the notification already exists
96
+ const id = `${ ANNOUNCEMENT_PREFIX }${ announcement.id }`;
97
+ const existing = getters['notifications/item'](id);
98
+
99
+ // Check if the pref for 'read announcements' has the id
100
+ const pref = getters['prefs/get'](READ_ANNOUNCEMENTS) || '';
101
+ const prefExists = pref.split(',').includes(announcement.id);
102
+
103
+ if (existing || prefExists) {
104
+ logger.info(`Not adding announcement with ID ${ id } as it already exists or has been read previously (title: ${ announcement.title })`);
105
+
106
+ return;
107
+ }
108
+
109
+ const notification: Notification = {
110
+ id,
111
+ level,
112
+ title: announcement.title,
113
+ message: announcement.message,
114
+ handlerName: DynamicContentAnnouncementHandlerName,
115
+ };
116
+
117
+ if (data && Object.keys(data).length > 0) {
118
+ notification.data = data;
119
+ }
120
+
121
+ if (announcement.cta?.primary) {
122
+ notification.primaryAction = {
123
+ label: announcement.cta.primary.action,
124
+ target: announcement.cta.primary.link,
125
+ };
126
+ }
127
+
128
+ if (announcement.cta?.secondary) {
129
+ notification.secondaryAction = {
130
+ label: announcement.cta.secondary.action,
131
+ target: announcement.cta.secondary.link,
132
+ };
133
+ }
134
+
135
+ logger.info(`Adding announcement with ID ${ id } (title: ${ announcement.title }, target: ${ announcement.target })`);
136
+
137
+ await dispatch('notifications/add', notification);
138
+ } else {
139
+ logger.error(`Announcement type ${ announcement.target } is not supported`);
140
+ }
141
+ }));
142
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "version": 1,
3
+ "releases": [
4
+ {
5
+ "name": "2.12.2"
6
+ },
7
+ {
8
+ "name": "2.11.3"
9
+ },
10
+ {
11
+ "name": "2.10.3"
12
+ }
13
+ ],
14
+ "announcements": [
15
+ {
16
+ "id": "security-update",
17
+ "target": "notification/announcement",
18
+ "title": "Important Security Update",
19
+ "message": "A critical security vulnerability has been discovered in version 2.10.1. Users are strongly advised to update to version 2.12.2 immediately to ensure their systems remain secure.",
20
+ "cta": {
21
+ "primary" : {
22
+ "action": "Update Now",
23
+ "link": "https://www.suse.com/"
24
+ }
25
+ }
26
+ },
27
+ {
28
+ "id": "suse-rocks",
29
+ "target": "homepage/banner",
30
+ "title": "Important Security Update",
31
+ "message": "A critical security vulnerability has been discovered in version 2.10.1. Users are strongly advised to update to version 2.12.2 immediately to ensure their systems remain secure.",
32
+ "cta": {
33
+ "primary" : {
34
+ "action": "Update Now",
35
+ "link": "https://www.suse.com/"
36
+ }
37
+ }
38
+ }
39
+ ]
40
+ }
@@ -15,6 +15,7 @@ import { Context, DynamicContent, VersionInfo } from './types';
15
15
  import { createLogger, LOCAL_STORAGE_CONTENT_DEBUG_LOG } from './util';
16
16
  import { getConfig } from './config';
17
17
  import { SystemInfoProvider } from './info';
18
+ import { processAnnouncements } from './announcement';
18
19
 
19
20
  const FETCH_DELAY = 3 * 1000; // Short delay to let UI settle before we fetch the updates document
20
21
  const FETCH_REQUEST_TIMEOUT = 15000; // Time out the request after 15 seconds
@@ -83,7 +84,7 @@ export async function fetchAndProcessDynamicContent(dispatch: Function, getters:
83
84
  }
84
85
 
85
86
  const versionInfo: VersionInfo = {
86
- version,
87
+ version: version as semver.SemVer, // Will be defined, can not be null here
87
88
  isPrime: config.prime,
88
89
  };
89
90
 
@@ -103,7 +104,7 @@ export async function fetchAndProcessDynamicContent(dispatch: Function, getters:
103
104
  // If the cached content has a debug version then use that as an override for the current version number
104
105
  // This is only for debug and testing purposes
105
106
  if (content.settings?.debugVersion) {
106
- versionInfo.version = semver.coerce(content.settings.debugVersion);
107
+ versionInfo.version = semver.coerce(content.settings.debugVersion) || version;
107
108
  logger.debug(`Overriding version number to ${ content.settings.debugVersion }`);
108
109
  }
109
110
 
@@ -117,6 +118,9 @@ export async function fetchAndProcessDynamicContent(dispatch: Function, getters:
117
118
  // EOM, EOL notifications
118
119
  processSupportNotices(context, content.support, versionInfo);
119
120
  }
121
+
122
+ // Announcements - processed for all users
123
+ processAnnouncements(context, content.announcements, versionInfo);
120
124
  } catch (e) {
121
125
  logger.error('Error reading or processing dynamic content', e);
122
126
  }
@@ -10,6 +10,7 @@ import {
10
10
  import { SETTING } from '@shell/config/settings';
11
11
  import { getVersionData } from '@shell/config/version';
12
12
  import { SettingsInfo } from '@shell/utils/dynamic-content/types';
13
+ import { STEVE_CACHE } from '@shell/store/features';
13
14
 
14
15
  const QS_VERSION = 'v1'; // Include a version number in the query string in case we want to version the set of params we are sending
15
16
  const UNKNOWN = 'unknown';
@@ -26,6 +27,29 @@ const SUSE_EXTENSIONS = [
26
27
  'virtual-clusters'
27
28
  ];
28
29
 
30
+ type FeatureFlagInfos = {
31
+ [id: string]: {
32
+ /**
33
+ * Query param, in format `ff-<param>`
34
+ */
35
+ param: string,
36
+ /**
37
+ * The actual value used by the UI, roughly spec.value || status.default
38
+ */
39
+ value: string,
40
+ }
41
+ };
42
+
43
+ /**
44
+ * Explicit ff's to send
45
+ */
46
+ const ffs: FeatureFlagInfos = {
47
+ [STEVE_CACHE]: {
48
+ param: 'usc',
49
+ value: '',
50
+ }
51
+ };
52
+
29
53
  /**
30
54
  * System information that is collected and which can then be encoded into a query string in the dyanmic content request
31
55
  */
@@ -47,6 +71,7 @@ type SystemInfo = {
47
71
  browserSize: string;
48
72
  screenSize: string;
49
73
  language: string;
74
+ featureFlags: FeatureFlagInfos
50
75
  };
51
76
 
52
77
  /**
@@ -107,8 +132,7 @@ export class SystemInfoProvider {
107
132
  // High-level information from clusters
108
133
  const counts = this.getAll(getters, COUNT)?.[0]?.counts || {};
109
134
  const clusterCount = counts[MANAGEMENT.CLUSTER] || {};
110
- const all = this.getAll(getters, MANAGEMENT.CLUSTER);
111
- const localCluster = all ? all.find((c: any) => c.isLocal) : undefined;
135
+ const localCluster = getters['localCluster'];
112
136
 
113
137
  // Stats for installed extensions
114
138
  const uiExtensionList = getters['uiplugins/plugins'];
@@ -132,6 +156,19 @@ export class SystemInfoProvider {
132
156
  const screenSize = `${ window.screen?.width || '?' }x${ window.screen?.height || '?' }`;
133
157
  const browserSize = `${ window.innerWidth }x${ window.innerHeight }`;
134
158
 
159
+ const safeFfs = Object.entries(ffs).reduce((res, [id, ff]) => {
160
+ try {
161
+ res[id] = {
162
+ param: ff.param,
163
+ value: getters['features/get'](id),
164
+ };
165
+ } catch (e) {
166
+ console.debug(`Cannot include Feature Flag "${ id }" in dynamic feature request: `, e); // eslint-disable-line no-console
167
+ }
168
+
169
+ return res;
170
+ }, {} as FeatureFlagInfos);
171
+
135
172
  return {
136
173
  systemUUID,
137
174
  userHash,
@@ -147,6 +184,7 @@ export class SystemInfoProvider {
147
184
  screenSize,
148
185
  browserSize,
149
186
  language: window.navigator?.language,
187
+ featureFlags: safeFfs,
150
188
  };
151
189
  }
152
190
 
@@ -214,6 +252,10 @@ export class SystemInfoProvider {
214
252
  params.push(`ss=${ systemData.screenSize }`);
215
253
  }
216
254
 
255
+ Object.values(systemData.featureFlags).forEach((ff) => {
256
+ params.push(`ff-` + `${ ff.param }=${ ff.value }`);
257
+ });
258
+
217
259
  return params.join('&');
218
260
  }
219
261
  }
@@ -27,7 +27,7 @@ export async function processReleaseVersion(context: Context, releaseInfo: Relea
27
27
  const versions = releaseInfo.map((v: any) => semver.coerce(v.name));
28
28
 
29
29
  // Sort the versions, so that the newest is first in the list
30
- versions.sort((a: any, b: any) => semver.gt(b, a) ? 1 : -1);
30
+ versions.sort((a: any, b: any) => semver.rcompare(a, b));
31
31
 
32
32
  // Find first newer version
33
33
  const newer = versions.find((v: any) => semver.gt(v, version));
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Notification handler for dynamic content announcements
3
+ *
4
+ * This provides custom handling for read/unread state using a single user preference
5
+ */
6
+ import { Notification, NotificationHandler } from '@shell/types/notifications';
7
+ import { READ_ANNOUNCEMENTS } from '@shell/store/prefs';
8
+ import { ANNOUNCEMENT_PREFIX } from './announcement';
9
+
10
+ // Global name for this handler that can be used when creating notifications to associate them with this handler
11
+ export const DynamicContentAnnouncementHandlerName = 'dc-announcements';
12
+
13
+ /**
14
+ * Create the dynamic content notification handler
15
+ *
16
+ * This is for announcements, where we need to manage an array of IDs of announcements that have been read, which
17
+ * is taken care of by this custom handler.
18
+ *
19
+ * When a notification is read/unread that specifies this handler, we will add or remove its ID from the list of
20
+ * read IDs that we maintain in the user preference value.
21
+ *
22
+ * This allows us to use a single user preference to track read announcements
23
+ */
24
+ export function createHandler(store: any): NotificationHandler {
25
+ return {
26
+ async onReadUpdated(notification: Notification, read: boolean) {
27
+ if (!notification.id.startsWith(ANNOUNCEMENT_PREFIX)) {
28
+ return;
29
+ }
30
+
31
+ const id = notification.id.substring(ANNOUNCEMENT_PREFIX.length);
32
+ const announcements = store.getters['notifications/all'].filter((n: any) => n.id.startsWith(ANNOUNCEMENT_PREFIX));
33
+ const pref = store.getters['prefs/get'](READ_ANNOUNCEMENTS) || '';
34
+ const values = !pref.length ? [] : pref.split(',').filter((v: string) => !announcements.includes(v));
35
+ const valuesUnique = new Set(values);
36
+
37
+ if (read) {
38
+ valuesUnique.add(id);
39
+ } else {
40
+ valuesUnique.delete(id);
41
+ }
42
+
43
+ const newValues = Array.from(valuesUnique).sort();
44
+
45
+ await store.dispatch('prefs/set', { key: READ_ANNOUNCEMENTS, value: newValues.join(',') });
46
+ }
47
+ };
48
+ }
@@ -56,7 +56,7 @@ export type Context = {
56
56
  * Version information
57
57
  */
58
58
  export type VersionInfo = {
59
- version: SemVer | null;
59
+ version: SemVer;
60
60
  isPrime: boolean;
61
61
  };
62
62
 
@@ -90,6 +90,57 @@ export type SupportInfo = {
90
90
  }
91
91
  };
92
92
 
93
+ /**
94
+ * Call to action for an announcement
95
+ */
96
+ export type CallToAction = {
97
+ action: string;
98
+ link: string;
99
+ };
100
+
101
+ /**
102
+ * Announcements to be shown in the notification center or on the home page
103
+ *
104
+ * Target determines where the notification will be shown, supported values:
105
+ *
106
+ * - `notification/announcement` - Shown with `Announcement` level in the Notification Center
107
+ * - `notification/info` - Shown with `Info` level in the Notification Center
108
+ * - `notification/warning` - Shown with `Warning` level in the Notification Center
109
+ * - `homepage/banner` - Shown on the home page as a banner beneath the main banner
110
+ * - `homepage/rhs` - Shown on the home page as a panel beneath the right-hand side links panel
111
+ *
112
+ */
113
+ export type Announcement = {
114
+ id: string; // Unique id for this announcement
115
+ title: string; // Title to be shown
116
+ message: string; // Message/Body for the announcement
117
+ target: string; // Where the announcement should be shown
118
+ version?: string; // Version or semver expression for when to show this announcement
119
+ audience?: 'admin' | 'all'; // Audience - show for just Admins or for all users
120
+ icon?: string;
121
+ cta?: {
122
+ primary: CallToAction, // Must have a primary call to action, if we have a cta field
123
+ secondary?: CallToAction,
124
+ },
125
+ style?: string; // Styling information that will be interpreted by the rendering component
126
+ };
127
+
128
+ /**
129
+ * Icon information
130
+ */
131
+ export type AnnouncementNotificationIconData = {
132
+ light: string; // Light mode icon/image
133
+ dark?: string; // Light mode icon/image
134
+ };
135
+
136
+ /**
137
+ * Custom data for announcements stored with the notification
138
+ */
139
+ export type AnnouncementNotificationData = {
140
+ icon?: AnnouncementNotificationIconData; // Icon/Image to show
141
+ location: string; // Location of the announcement in the UI
142
+ };
143
+
93
144
  /**
94
145
  * Main type for the metadata that is retrieved from the dynamic content endpoint
95
146
  */
@@ -97,5 +148,6 @@ export type DynamicContent = {
97
148
  version: string;
98
149
  releases: ReleaseInfo[],
99
150
  support: SupportInfo,
151
+ announcements: Announcement[],
100
152
  settings?: Partial<SettingsInfo>,
101
153
  };
@@ -81,7 +81,7 @@ export function importWindowComponent(name) {
81
81
  throw new Error('Name required');
82
82
  }
83
83
 
84
- return defineAsyncComponent(() => import(/* webpackChunkName: "components/nav" */ `@shell/components/nav/WindowManager/${name}`));
84
+ return defineAsyncComponent(() => import(/* webpackChunkName: "components" */ `@shell/components/Window/${name}`));
85
85
  }
86
86
 
87
87
  export function loadProduct(name) {
@@ -130,7 +130,7 @@ export function resolveDetail(key) {
130
130
  }
131
131
 
132
132
  export function resolveWindowComponent(key) {
133
- return require.resolve(`@shell/components/nav/WindowManager/${ key }`);
133
+ return require.resolve(`@shell/components/Window/${ key }`);
134
134
  }
135
135
 
136
136
  export function resolveMachineConfigComponent(key) {
package/utils/favicon.js CHANGED
@@ -9,17 +9,17 @@ export function haveSetFavIcon() {
9
9
 
10
10
  export function setFavIcon(store) {
11
11
  const res = store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.FAVICON);
12
- const brandSetting = store.getters['management/byId'](MANAGEMENT.SETTING, SETTING.BRAND);
12
+ const brandSetting = store.getters['management/brand'];
13
13
  const link = findIconLink(document.head.getElementsByTagName('link'));
14
14
 
15
15
  if (link) {
16
16
  let brandImage;
17
17
 
18
- if (brandSetting?.value === 'suse') {
18
+ if (brandSetting === 'suse') {
19
19
  brandImage = require('~shell/assets/brand/suse/favicon.png');
20
- } else if (brandSetting?.value === 'csp') {
20
+ } else if (brandSetting === 'csp') {
21
21
  brandImage = require('~shell/assets/brand/csp/favicon.png');
22
- } else if (brandSetting?.value === 'harvester') {
22
+ } else if (brandSetting === 'harvester') {
23
23
  brandImage = require('~shell/assets/brand/harvester/favicon.png');
24
24
  }
25
25
 
package/utils/object.js CHANGED
@@ -205,7 +205,7 @@ export function definedKeys(obj) {
205
205
  return compact(flattenDeep(keys));
206
206
  }
207
207
 
208
- export function diff(from, to) {
208
+ export function diff(from, to, preventNull = false) {
209
209
  from = from || {};
210
210
  to = to || {};
211
211
 
@@ -234,7 +234,25 @@ export function diff(from, to) {
234
234
  const missing = difference(fromKeys, toKeys);
235
235
 
236
236
  for ( const k of missing ) {
237
- set(out, k, null);
237
+ // we need to gate this in order to prevent unforseen problems with addonConfig
238
+ if (preventNull) {
239
+ // keys that come from "definedKeys" method are strings with "" chars inside... We need to clean them up
240
+ // so that we can access the value of the obj property
241
+ let key = k;
242
+
243
+ if (!k.includes('.')) {
244
+ key = k.replaceAll('"', '');
245
+ }
246
+
247
+ // // if value exists in "from" but is missing in "to", let's add it, otherwise we just set it null as we did before
248
+ if (from[key] !== undefined && from[key] !== null) {
249
+ set(out, key, from[key]);
250
+ } else {
251
+ set(out, key, null);
252
+ }
253
+ } else {
254
+ set(out, k, null);
255
+ }
238
256
  }
239
257
 
240
258
  return out;
@@ -165,7 +165,7 @@ class PaginationUtils {
165
165
  /**
166
166
  * Is pagination enabled at a global level or for a specific resource
167
167
  */
168
- isEnabled({ rootGetters, $plugin }: any, enabledFor: PaginationResourceContext) {
168
+ isEnabled({ rootGetters, $extension }: any, enabledFor: PaginationResourceContext) {
169
169
  // Cache must be enabled to support pagination api
170
170
  if (!this.isSteveCacheEnabled({ rootGetters })) {
171
171
  return false;
@@ -184,7 +184,7 @@ class PaginationUtils {
184
184
  }
185
185
 
186
186
  // Does an extension say this type is enabled?
187
- const plugin = $plugin as ExtensionManager;
187
+ const plugin = $extension as ExtensionManager;
188
188
  const paginationExtensionPoints = plugin.getAll()[EXT_IDS.SERVER_SIDE_PAGINATION_RESOURCES];
189
189
 
190
190
  if (paginationExtensionPoints) {
@@ -19,7 +19,7 @@ interface Args {
19
19
  /**
20
20
  * Callback called when the resource is changed (notified by socket)
21
21
  */
22
- onChange?: () => void,
22
+ onChange?: STEVE_WATCH_EVENT_LISTENER_CALLBACK,
23
23
 
24
24
  formatResponse?: {
25
25
  /**
@@ -69,16 +69,16 @@ class PaginationWrapper<T extends object> {
69
69
  this.classify = formatResponse?.classify || false;
70
70
  this.reactive = formatResponse?.reactive || false;
71
71
 
72
- this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters, $plugin: this.$store.$plugin }, enabledFor);
72
+ this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters, $extension: this.$store.$extension }, enabledFor);
73
73
  }
74
74
 
75
- async request(args: {
76
- pagination: PaginationArgs,
75
+ async request({ pagination, forceWatch }: {
76
+ forceWatch?: boolean,
77
+ pagination: PaginationArgs,
77
78
  }): Promise<Result<T>> {
78
79
  if (!this.isEnabled) {
79
80
  throw new Error(`Wrapper for type '${ this.enabledFor.store }/${ this.enabledFor.resource?.id }' in context '${ this.enabledFor.resource?.context }' not supported`);
80
81
  }
81
- const { pagination } = args;
82
82
  const opt: ActionFindPageArgs = {
83
83
  watch: false,
84
84
  pagination,
@@ -89,14 +89,18 @@ class PaginationWrapper<T extends object> {
89
89
  const out: ActionFindPageTransientResult<T> = await this.$store.dispatch(`${ this.enabledFor.store }/findPage`, { opt, type: this.enabledFor.resource?.id });
90
90
 
91
91
  // Watch
92
- if (this.onChange && !this.steveWatchParams) {
92
+ const firstTime = !this.steveWatchParams;
93
+
94
+ if (this.onChange && (firstTime || forceWatch) ) { // && !this.steveWatchParams
93
95
  this.steveWatchParams = {
94
96
  event: STEVE_WATCH_EVENT_TYPES.CHANGES,
95
97
  id: this.id,
96
98
  params: {
97
- type: this.enabledFor.resource?.id as string,
98
- mode: STEVE_WATCH_MODE.RESOURCE_CHANGES,
99
- }
99
+ type: this.enabledFor.resource?.id as string,
100
+ mode: STEVE_WATCH_MODE.RESOURCE_CHANGES,
101
+ force: forceWatch,
102
+ },
103
+
100
104
  };
101
105
 
102
106
  this.watch();
@@ -0,0 +1,14 @@
1
+ import { IClusterProvisioner, ClusterProvisionerContext } from '@shell/core/types';
2
+
3
+ export function getHostedProviders(context: ClusterProvisionerContext) {
4
+ return context?.$extension?.getProviders(context)?.filter((p: IClusterProvisioner) => p.group === 'hosted') || [];
5
+ }
6
+
7
+ export function isHostedProvider(context: ClusterProvisionerContext, provisioner: string) {
8
+ if (!provisioner) {
9
+ return false;
10
+ }
11
+ const provisioners = new Set(getHostedProviders(context).map((p: IClusterProvisioner) => p.id.toLowerCase()));
12
+
13
+ return provisioners.has(provisioner.toLowerCase());
14
+ }
@@ -0,0 +1,7 @@
1
+ export function scrollToBottom() {
2
+ const scrollable = document.getElementsByTagName('main')[0];
3
+
4
+ if (scrollable) {
5
+ scrollable.scrollTop = scrollable.scrollHeight;
6
+ }
7
+ }
@@ -199,8 +199,12 @@ export function labelSelectorToSelector(labelSelector?: KubeLabelSelector): stri
199
199
  });
200
200
 
201
201
  (labelSelector?.matchExpressions || []).forEach((value: KubeLabelSelectorExpression) => {
202
- if (value.operator === 'In' && value.values?.length === 1) {
203
- res.push(`${ value.key }=${ value.values[0] }`);
202
+ if (value.operator === 'In' && value.values !== undefined) {
203
+ if (value.values?.length === 1) {
204
+ res.push(`${ value.key }=${ value.values[0] }`);
205
+ } else {
206
+ res.push(`${ value.key } in (${ value.values.join(',') })`);
207
+ }
204
208
  } else {
205
209
  throw new Error(`Unsupported matchExpression found when converting to selector string. ${ value }`);
206
210
  }
package/utils/settings.ts CHANGED
@@ -3,6 +3,7 @@ import { Store } from 'vuex';
3
3
  import { DEFAULT_PERF_SETTING, PerfSettings, SETTING } from '@shell/config/settings';
4
4
  import { pluralize } from '@shell/utils/string';
5
5
  import { _MULTI } from '@shell/plugins/dashboard-store/actions';
6
+ import { ClusterProvisionerContext } from '@shell/core/types';
6
7
 
7
8
  export const fetchOrCreateSetting = async(store: Store<any>, id: string, val: string, save = true): Promise<any> => {
8
9
  let setting;
@@ -109,3 +110,17 @@ export const getPerformanceSetting = (rootGetters: Record<string, (arg0: string,
109
110
 
110
111
  return Object.assign(safeDefaults, perfSetting || {});
111
112
  };
113
+
114
+ export const isProviderEnabled = (context: ClusterProvisionerContext, provider: string): boolean => {
115
+ const providerTypesJSON = context.getters['management/byId'](MANAGEMENT.SETTING, SETTING.KEV2_OPERATORS )?.value;
116
+ const providerTypes = providerTypesJSON ? JSON.parse(providerTypesJSON) : [];
117
+
118
+ for ( let i = 0; i < providerTypes.length; i++) {
119
+ if ( providerTypes[i].name === provider) {
120
+ return providerTypes[i].active;
121
+ }
122
+ }
123
+
124
+ // We want to have providers enabled by default unless they are turned off by a setting
125
+ return true;
126
+ };