@rancher/shell 0.3.4 → 0.3.6

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 (289) hide show
  1. package/assets/images/providers/outscale.svg +19 -0
  2. package/assets/styles/app.scss +1 -1
  3. package/assets/styles/base/_basic.scss +18 -0
  4. package/assets/styles/base/_mixins.scss +0 -11
  5. package/assets/styles/base/_variables.scss +2 -4
  6. package/assets/styles/fonts/_fontstack.scss +11 -11
  7. package/assets/styles/global/_button.scss +12 -2
  8. package/assets/styles/vendor/vue-js-modal.scss +3 -3
  9. package/assets/translations/en-us.yaml +113 -22
  10. package/assets/translations/zh-hans.yaml +113 -24
  11. package/babel.config.js +13 -0
  12. package/chart/gatekeeper.vue +78 -0
  13. package/chart/istio.vue +135 -112
  14. package/chart/logging/index.vue +13 -4
  15. package/chart/monitoring/index.vue +15 -5
  16. package/chart/monitoring/steps/uninstall-v1.vue +2 -2
  17. package/chart/rancher-backup/index.vue +10 -3
  18. package/cloud-credential/aws.vue +1 -1
  19. package/cloud-credential/digitalocean.vue +1 -1
  20. package/cloud-credential/gcp.vue +1 -1
  21. package/cloud-credential/generic.vue +2 -2
  22. package/cloud-credential/linode.vue +1 -1
  23. package/cloud-credential/pnap.vue +1 -1
  24. package/components/ActionMenu.vue +3 -4
  25. package/components/AssignTo.vue +1 -1
  26. package/components/AsyncButton.vue +1 -1
  27. package/components/BannerGraphic.vue +1 -1
  28. package/components/BrandImage.vue +1 -4
  29. package/components/ButtonDropdown.vue +2 -3
  30. package/components/Carousel.vue +85 -37
  31. package/components/ChartPsp.vue +76 -0
  32. package/components/CruResource.vue +6 -2
  33. package/components/DashboardMetrics.vue +12 -10
  34. package/components/DetailText.vue +1 -1
  35. package/components/DisableAuthProviderModal.vue +1 -1
  36. package/components/EmberPage.vue +1 -1
  37. package/components/EtcdInfoBanner.vue +12 -7
  38. package/components/ExplorerMembers.vue +101 -6
  39. package/components/ExplorerProjectsNamespaces.vue +46 -3
  40. package/components/FileDiff.vue +6 -7
  41. package/components/GrafanaDashboard.vue +27 -23
  42. package/components/LazyImage.vue +10 -12
  43. package/components/LogItem.vue +1 -1
  44. package/components/Markdown.vue +1 -1
  45. package/components/PromptRemove.vue +2 -2
  46. package/components/PromptRestore.vue +1 -1
  47. package/components/ResourceDetail/Masthead.vue +16 -0
  48. package/components/ResourceDetail/index.vue +21 -4
  49. package/components/ResourceList/index.vue +1 -1
  50. package/components/ResourceTable.vue +4 -1
  51. package/components/SingleClusterInfo.vue +2 -2
  52. package/components/SortableTable/THead.vue +1 -1
  53. package/components/SortableTable/index.vue +28 -13
  54. package/components/SortableTable/selection.js +58 -50
  55. package/components/Wizard.vue +4 -2
  56. package/components/__tests__/AsyncButton.test.ts +3 -1
  57. package/components/__tests__/ChartPsp.test.ts +75 -0
  58. package/components/__tests__/CruResource.test.ts +3 -1
  59. package/components/auth/Principal.vue +1 -1
  60. package/components/auth/RoleDetailEdit.vue +2 -2
  61. package/components/fleet/FleetBundles.vue +3 -1
  62. package/components/fleet/FleetClusters.vue +1 -2
  63. package/components/fleet/FleetIntro.vue +9 -1
  64. package/components/fleet/FleetNoWorkspaces.vue +62 -0
  65. package/components/fleet/FleetSummary.vue +7 -1
  66. package/components/form/HookOption.vue +14 -10
  67. package/components/form/LabeledSelect.vue +14 -11
  68. package/components/form/Labels.vue +32 -27
  69. package/components/form/MatchExpressions.vue +19 -4
  70. package/components/form/Members/ClusterPermissionsEditor.vue +32 -7
  71. package/components/form/NameNsDescription.vue +32 -46
  72. package/components/form/ProjectMemberEditor.vue +46 -21
  73. package/components/form/ResourceSelector.vue +1 -1
  74. package/components/form/SecretSelector.vue +5 -1
  75. package/components/form/ServiceNameSelect.vue +1 -1
  76. package/components/form/SimpleSecretSelector.vue +9 -9
  77. package/components/form/Tolerations.vue +4 -1
  78. package/components/form/ValueFromResource.vue +14 -9
  79. package/components/form/WorkloadPorts.vue +2 -2
  80. package/components/form/__tests__/LabeledSelect.test.ts +138 -0
  81. package/components/form/__tests__/NameNsDescription.ts +59 -0
  82. package/components/formatter/InternalExternalIP.vue +6 -0
  83. package/components/formatter/InvolvedObjectLink.vue +54 -0
  84. package/components/formatter/Link.vue +20 -4
  85. package/components/formatter/LinkName.vue +6 -1
  86. package/components/formatter/ServiceTargets.vue +1 -1
  87. package/components/formatter/WorkloadHealthScale.vue +8 -2
  88. package/components/nav/Group.vue +2 -2
  89. package/components/nav/NamespaceFilter.vue +23 -11
  90. package/components/nav/TopLevelMenu.vue +2 -4
  91. package/components/nav/Type.vue +1 -1
  92. package/components/nav/WorkspaceSwitcher.vue +46 -5
  93. package/components/nuxt/nuxt-build-indicator.vue +143 -0
  94. package/components/nuxt/nuxt-child.js +122 -0
  95. package/components/nuxt/nuxt-error.vue +98 -0
  96. package/components/nuxt/nuxt-link.client.js +98 -0
  97. package/components/nuxt/nuxt-link.server.js +16 -0
  98. package/components/nuxt/nuxt-loading.vue +154 -0
  99. package/components/nuxt/nuxt.js +101 -0
  100. package/config/labels-annotations.js +17 -0
  101. package/config/middleware.js +12 -0
  102. package/config/product/auth.js +3 -2
  103. package/config/product/explorer.js +34 -6
  104. package/config/product/fleet.js +2 -0
  105. package/config/query-params.js +1 -0
  106. package/config/router.js +414 -0
  107. package/config/store.js +181 -0
  108. package/config/table-headers.js +54 -12
  109. package/config/types.js +18 -8
  110. package/config/uiplugins.js +30 -0
  111. package/content/docs/en-us/whats-new.md +10 -0
  112. package/content/docs/zh-hans/whats-new.md +11 -1
  113. package/core/plugin-routes.ts +23 -0
  114. package/core/plugin.ts +4 -2
  115. package/core/types.ts +258 -1
  116. package/creators/app/app.package.json +2 -1
  117. package/creators/app/files/.eslintrc.js +1 -1
  118. package/creators/app/files/babel.config.js +1 -18
  119. package/creators/app/files/tsconfig.json +0 -1
  120. package/creators/app/files/vue.config.js +6 -0
  121. package/creators/app/init +5 -5
  122. package/creators/pkg/files/.github/workflows/build-extension.yml +110 -0
  123. package/creators/pkg/files/tsconfig.json +0 -1
  124. package/creators/pkg/init +35 -4
  125. package/creators/pkg/pkg.package.json +3 -3
  126. package/creators/update/init +1 -1
  127. package/detail/constraints.gatekeeper.sh.constraint.vue +34 -17
  128. package/detail/fleet.cattle.io.clustergroup.vue +7 -1
  129. package/detail/fleet.cattle.io.gitrepo.vue +19 -11
  130. package/detail/harvesterhci.io.management.cluster.vue +3 -3
  131. package/detail/provisioning.cattle.io.cluster.vue +54 -12
  132. package/detail/workload/index.vue +3 -3
  133. package/dialog/AddClusterMemberDialog.vue +1 -1
  134. package/dialog/AddProjectMemberDialog.vue +2 -2
  135. package/dialog/AddonConfigConfirmationDialog.vue +27 -15
  136. package/dialog/DiagnosticTimingsDialog.vue +1 -1
  137. package/dialog/ForceMachineRemoveDialog.vue +1 -1
  138. package/dialog/GenericPrompt.vue +18 -6
  139. package/dialog/RotateEncryptionKeyDialog.vue +1 -1
  140. package/dialog/SaveAsRKETemplateDialog.vue +1 -1
  141. package/dialog/ScaleMachineDownDialog.vue +1 -1
  142. package/edit/auth/github.vue +8 -8
  143. package/edit/auth/googleoauth.vue +5 -5
  144. package/edit/auth/ldap/index.vue +1 -1
  145. package/edit/auth/oidc.vue +1 -1
  146. package/edit/auth/saml.vue +1 -1
  147. package/edit/cis.cattle.io.clusterscan.vue +1 -1
  148. package/edit/fleet.cattle.io.clustergroup.vue +6 -4
  149. package/edit/fleet.cattle.io.gitrepo.vue +32 -4
  150. package/edit/helm.cattle.io.projecthelmchart.vue +5 -1
  151. package/edit/logging.banzaicloud.io.output/index.vue +18 -5
  152. package/edit/logging.banzaicloud.io.output/providers/loki.vue +1 -0
  153. package/edit/management.cattle.io.fleetworkspace.vue +141 -6
  154. package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +4 -1
  155. package/edit/management.cattle.io.setting.vue +1 -1
  156. package/edit/monitoring.coreos.com.alertmanagerconfig/types/webhook.vue +2 -2
  157. package/edit/monitoring.coreos.com.receiver/tls.vue +18 -18
  158. package/edit/monitoring.coreos.com.receiver/types/webhook.banner.vue +4 -4
  159. package/edit/monitoring.coreos.com.receiver/types/webhook.vue +1 -1
  160. package/edit/namespace.vue +14 -10
  161. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +126 -45
  162. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
  163. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +21 -4
  164. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -0
  165. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +202 -2
  166. package/edit/provisioning.cattle.io.cluster/import.vue +23 -25
  167. package/edit/provisioning.cattle.io.cluster/rke2.vue +344 -102
  168. package/edit/resources.cattle.io.backup.vue +1 -1
  169. package/edit/service.vue +1 -1
  170. package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +2 -2
  171. package/edit/workload/__tests__/Job.test.ts +3 -1
  172. package/edit/workload/index.vue +8 -3
  173. package/edit/workload/mixins/workload.js +22 -7
  174. package/edit/workload/storage/Mount.vue +3 -3
  175. package/initialize/App.js +206 -0
  176. package/initialize/client.js +863 -0
  177. package/initialize/index.js +364 -0
  178. package/layouts/default.vue +7 -3
  179. package/layouts/standalone.vue +13 -0
  180. package/list/catalog.cattle.io.clusterrepo.vue +1 -0
  181. package/list/fleet.cattle.io.bundle.vue +6 -3
  182. package/list/fleet.cattle.io.clusterregistrationtoken.vue +3 -1
  183. package/list/fleet.cattle.io.gitrepo.vue +44 -5
  184. package/list/management.cattle.io.fleetworkspace.vue +45 -0
  185. package/list/node.vue +69 -16
  186. package/list/provisioning.cattle.io.cluster.vue +30 -1
  187. package/list/rbac.authorization.k8s.io.clusterrolebinding.vue +48 -0
  188. package/list/workload.vue +6 -4
  189. package/machine-config/azure.vue +97 -38
  190. package/middleware/authenticated.js +34 -0
  191. package/mixins/chart.js +101 -2
  192. package/mixins/fetch.client.js +95 -0
  193. package/mixins/fetch.server.js +73 -0
  194. package/mixins/labeled-form-element.ts +2 -2
  195. package/mixins/resource-fetch.js +2 -2
  196. package/models/apps.statefulset.js +28 -0
  197. package/models/cluster/node.js +23 -2
  198. package/models/cluster.x-k8s.io.machine.js +4 -2
  199. package/models/clusterroletemplatebinding.js +7 -0
  200. package/models/constraints.gatekeeper.sh.constraint.js +46 -0
  201. package/models/fleet.cattle.io.cluster.js +19 -10
  202. package/models/fleet.cattle.io.gitrepo.js +7 -2
  203. package/models/management.cattle.io.cluster.js +1 -1
  204. package/models/management.cattle.io.fleetworkspace.js +12 -0
  205. package/models/management.cattle.io.gitreporestriction.js +5 -0
  206. package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.js +3 -0
  207. package/models/pod.js +4 -0
  208. package/models/provisioning.cattle.io.cluster.js +7 -5
  209. package/models/rbac.authorization.k8s.io.clusterrolebinding.js +16 -0
  210. package/models/rbac.authorization.k8s.io.rolebinding.js +16 -0
  211. package/package.json +13 -21
  212. package/pages/auth/setup.vue +2 -2
  213. package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +33 -0
  214. package/pages/c/_cluster/apps/charts/chart.vue +4 -4
  215. package/pages/c/_cluster/apps/charts/install.helpers.js +26 -0
  216. package/pages/c/_cluster/apps/charts/install.vue +98 -102
  217. package/pages/c/_cluster/explorer/EventsTable.vue +5 -19
  218. package/pages/c/_cluster/explorer/index.vue +29 -25
  219. package/pages/c/_cluster/explorer/tools/index.vue +8 -8
  220. package/pages/c/_cluster/fleet/index.vue +95 -34
  221. package/pages/c/_cluster/gatekeeper/index.vue +1 -1
  222. package/pages/c/_cluster/istio/index.vue +5 -5
  223. package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
  224. package/pages/c/_cluster/monitoring/index.vue +7 -0
  225. package/pages/c/_cluster/uiplugins/InstallDialog.vue +8 -8
  226. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +20 -7
  227. package/pages/c/_cluster/uiplugins/index.vue +49 -17
  228. package/pages/diagnostic.vue +32 -25
  229. package/pages/home.vue +9 -4
  230. package/pages/index.vue +10 -1
  231. package/pages/rio/mesh.vue +1 -2
  232. package/pkg/tsconfig.json +0 -1
  233. package/plugins/clean-html-directive.js +34 -0
  234. package/plugins/dashboard-store/actions.js +32 -9
  235. package/plugins/dashboard-store/index.js +1 -1
  236. package/plugins/dashboard-store/mutations.js +5 -2
  237. package/plugins/dashboard-store/resource-class.js +8 -1
  238. package/plugins/plugin.js +0 -14
  239. package/plugins/portal-vue.js +4 -0
  240. package/plugins/steve/mutations.js +3 -2
  241. package/plugins/steve/steve-description-class.js +5 -1
  242. package/plugins/steve/subscribe.js +63 -54
  243. package/plugins/steve-create-worker.js +14 -0
  244. package/promptRemove/management.cattle.io.globalrole.vue +2 -2
  245. package/promptRemove/management.cattle.io.project.vue +2 -2
  246. package/promptRemove/management.cattle.io.roletemplate.vue +2 -2
  247. package/promptRemove/pod.vue +1 -1
  248. package/public/index.html +65 -0
  249. package/rancher-components/components/Banner/Banner.test.ts +7 -1
  250. package/rancher-components/components/Banner/Banner.vue +2 -1
  251. package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -0
  252. package/rancher-components/components/Form/Radio/RadioButton.test.ts +31 -0
  253. package/rancher-components/components/Form/Radio/RadioButton.vue +14 -3
  254. package/scripts/build-pkg.sh +1 -0
  255. package/scripts/clean +6 -0
  256. package/scripts/extension/bundle +58 -0
  257. package/scripts/extension/helmpatch +89 -0
  258. package/scripts/extension/publish +333 -0
  259. package/scripts/serve-pkgs +6 -2
  260. package/scripts/test-plugins-build.sh +4 -0
  261. package/store/__tests__/index.test.ts +110 -0
  262. package/store/index.js +145 -58
  263. package/store/type-map.js +6 -2
  264. package/tsconfig.default.json +36 -0
  265. package/tsconfig.json +23 -0
  266. package/types/rancher/index.d.ts +2 -0
  267. package/types/shell/index.d.ts +466 -320
  268. package/utils/__tests__/grafana.test.ts +44 -0
  269. package/utils/__tests__/string.test.ts +12 -0
  270. package/utils/auth.js +65 -0
  271. package/utils/axios.js +190 -0
  272. package/utils/cookie-universal-nuxt.js +10 -0
  273. package/utils/dom.js +15 -0
  274. package/utils/grafana.js +35 -16
  275. package/utils/monitoring.js +2 -1
  276. package/utils/nuxt.js +659 -0
  277. package/utils/position.js +5 -8
  278. package/utils/router.scrollBehavior.js +80 -0
  279. package/utils/select.js +1 -3
  280. package/utils/socket.js +1 -0
  281. package/utils/string.js +13 -0
  282. package/utils/time.js +9 -0
  283. package/vue.config.js +690 -0
  284. package/chart/rancher-alerting-drivers.vue +0 -53
  285. package/chart/rancher-gatekeeper.vue +0 -37
  286. package/creators/app/files/nuxt.config.js +0 -6
  287. package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +0 -4
  288. package/nuxt.config.js +0 -798
  289. package/plugins/dashboard-store/extensions.js +0 -22
@@ -5,6 +5,7 @@ import { mapPref, PLUGIN_DEVELOPER } from '@shell/store/prefs';
5
5
  import { sortBy } from '@shell/utils/sort';
6
6
  import { allHash } from '@shell/utils/promise';
7
7
  import { CATALOG, UI_PLUGIN, SERVICE } from '@shell/config/types';
8
+ import { getVersionData } from '@shell/config/version';
8
9
  import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
9
10
  import { NAME as APP_PRODUCT } from '@shell/config/product/apps';
10
11
  import ActionMenu from '@shell/components/ActionMenu';
@@ -23,6 +24,8 @@ import {
23
24
  uiPluginAnnotation,
24
25
  uiPluginHasAnnotation,
25
26
  isSupportedChartVersion,
27
+ isChartVersionAvailableForInstall,
28
+ isChartVersionHigher,
26
29
  UI_PLUGIN_NAMESPACE,
27
30
  UI_PLUGIN_CHART_ANNOTATIONS
28
31
  } from '@shell/config/uiplugins';
@@ -59,6 +62,7 @@ export default {
59
62
  hasService: false,
60
63
  defaultIcon: require('~shell/assets/images/generic-plugin.svg'),
61
64
  reloadRequired: false,
65
+ rancherVersion: getVersionData()?.Version
62
66
  };
63
67
  },
64
68
 
@@ -183,27 +187,39 @@ export default {
183
187
  // Label can be overridden by chart annotation
184
188
  const label = uiPluginAnnotation(UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME) || chart.chartNameDisplay;
185
189
  const item = {
186
- name: chart.chartNameDisplay,
190
+ name: chart.chartNameDisplay,
187
191
  label,
188
- description: chart.chartDescription,
189
- id: chart.id,
190
- versions: [],
191
- displayVersion: chart.versions?.length > 0 ? chart.versions[0].version : '',
192
- installed: false,
193
- builtin: false,
194
- experimental: uiPluginHasAnnotation(chart, CATALOG_ANNOTATIONS.EXPERIMENTAL, 'true'),
195
- certified: uiPluginHasAnnotation(chart, CATALOG_ANNOTATIONS.CERTIFIED, CATALOG_ANNOTATIONS._RANCHER),
192
+ description: chart.chartDescription,
193
+ id: chart.id,
194
+ versions: [],
195
+ installed: false,
196
+ builtin: false,
197
+ experimental: uiPluginHasAnnotation(chart, CATALOG_ANNOTATIONS.EXPERIMENTAL, 'true'),
198
+ certified: uiPluginHasAnnotation(chart, CATALOG_ANNOTATIONS.CERTIFIED, CATALOG_ANNOTATIONS._RANCHER),
196
199
  };
197
200
 
198
- this.latest = chart.versions[0];
199
201
  item.versions = [...chart.versions];
200
202
  item.chart = chart;
201
203
 
202
- // Filter the versions, leaving only those that are compatible with this Rancher
203
- item.versions = item.versions.filter(version => isSupportedChartVersion(version));
204
+ // Filter the versions available to install (plugins-api version and current dashboard version)
205
+ item.installableVersions = item.versions.filter(version => isSupportedChartVersion(version) && isChartVersionAvailableForInstall(version, this.rancherVersion));
204
206
 
205
- if (this.latest) {
206
- item.icon = chart.icon || this.latest.annotations['catalog.cattle.io/ui-icon'];
207
+ // add prop to version object if version is compatible with the current dashboard version
208
+ item.versions = item.versions.map(version => isChartVersionAvailableForInstall(version, this.rancherVersion, true));
209
+
210
+ const latestCompatible = item.installableVersions?.[0];
211
+ const latestNotCompatible = item.versions.find(version => !version.isCompatibleWithUi);
212
+
213
+ if (latestCompatible) {
214
+ item.displayVersion = latestCompatible.version;
215
+ item.icon = latestCompatible.icon;
216
+ } else {
217
+ item.displayVersion = item.versions?.[0]?.version;
218
+ item.icon = chart.icon || latestCompatible.annotations['catalog.cattle.io/ui-icon'];
219
+ }
220
+
221
+ if (latestNotCompatible && item.installableVersions.length && isChartVersionHigher(latestNotCompatible.version, item.installableVersions?.[0].version)) {
222
+ item.incompatibleDisclaimer = this.t('plugins.incompatibleDisclaimer', { version: latestNotCompatible.version, rancherVersion: latestNotCompatible.requiredUiVersion }, true);
207
223
  }
208
224
 
209
225
  if (this.installing[item.name]) {
@@ -253,8 +269,8 @@ export default {
253
269
  chart.installing = this.installing[chart.name];
254
270
 
255
271
  // Check for upgrade
256
- if (chart.versions.length && p.version !== chart.versions[0].version) {
257
- chart.upgrade = chart.versions[0].version;
272
+ if (chart.installableVersions?.length && p.version !== chart.installableVersions?.[0]?.version) {
273
+ chart.upgrade = chart.installableVersions[0].version;
258
274
  }
259
275
  } else {
260
276
  // No chart, so add a card for the plugin based on its Custom resource being present
@@ -619,6 +635,7 @@ export default {
619
635
  :data-testid="`extension-card-${plugin.name}`"
620
636
  @click="showPluginDetail(plugin)"
621
637
  >
638
+ <!-- plugin icon -->
622
639
  <div
623
640
  class="plugin-icon"
624
641
  :class="applyDarkModeBg"
@@ -636,7 +653,9 @@ export default {
636
653
  class="icon plugin-icon-img"
637
654
  >
638
655
  </div>
656
+ <!-- plugin card -->
639
657
  <div class="plugin-metadata">
658
+ <!-- plugin basic info -->
640
659
  <div class="plugin-name">
641
660
  {{ plugin.label }}
642
661
  </div>
@@ -654,8 +673,13 @@ export default {
654
673
  v-if="plugin.upgrade"
655
674
  v-tooltip="t('plugins.upgradeAvailable')"
656
675
  > -> {{ plugin.upgrade }}</span>
676
+ <p
677
+ v-if="plugin.incompatibleDisclaimer"
678
+ class="incompatible"
679
+ >{{ plugin.incompatibleDisclaimer }}</p>
657
680
  </span>
658
681
  </div>
682
+ <!-- plugin badges -->
659
683
  <div
660
684
  v-if="plugin.builtin"
661
685
  class="plugin-badges"
@@ -682,6 +706,7 @@ export default {
682
706
  </div>
683
707
  </div>
684
708
  <div class="plugin-spacer" />
709
+ <!-- plugin badges -->
685
710
  <div class="plugin-actions">
686
711
  <template v-if="plugin.error">
687
712
  <div
@@ -691,6 +716,7 @@ export default {
691
716
  <i class="icon icon-warning" />
692
717
  </div>
693
718
  </template>
719
+ <!-- plugin status -->
694
720
  <div
695
721
  v-if="plugin.helmError"
696
722
  v-tooltip="t('plugins.helmError')"
@@ -713,6 +739,7 @@ export default {
713
739
  {{ t('plugins.labels.uninstalling') }}
714
740
  </div>
715
741
  </div>
742
+ <!-- plugin buttons -->
716
743
  <div
717
744
  v-else-if="plugin.installed"
718
745
  class="plugin-buttons"
@@ -734,7 +761,7 @@ export default {
734
761
  {{ t('plugins.update.label') }}
735
762
  </button>
736
763
  <button
737
- v-if="!plugin.upgrade && plugin.versions.length > 1"
764
+ v-if="!plugin.upgrade && plugin.installableVersions && plugin.installableVersions.length > 1"
738
765
  class="btn role-secondary"
739
766
  :data-testid="`extension-card-rollback-btn-${plugin.name}`"
740
767
  @click="showInstallDialog(plugin, 'rollback', $event)"
@@ -942,6 +969,11 @@ export default {
942
969
  height: 16px;
943
970
  width: 16px;
944
971
  }
972
+
973
+ .incompatible {
974
+ margin: 10px 0;
975
+ font-weight: bold;
976
+ }
945
977
  }
946
978
 
947
979
  .plugin-installing {
@@ -18,7 +18,7 @@ export default {
18
18
  const clusterForCounts = filterHiddenLocalCluster(filterOnlyKubernetesClusters(readyClusters), this.$store);
19
19
  const finalCounts = [];
20
20
  const promises = [];
21
- let topTenForResponseTime = [];
21
+ const topFifteenForResponseTime = [];
22
22
 
23
23
  clusterForCounts.forEach((cluster, i) => {
24
24
  // Necessary to retrieve the proper display name of the cluster
@@ -42,6 +42,7 @@ export default {
42
42
 
43
43
  if (counts) {
44
44
  const sanitizedCount = [];
45
+ let finalCount = [];
45
46
 
46
47
  Object.keys(counts).forEach((key) => {
47
48
  sanitizedCount[key] = counts[key].summary?.count;
@@ -53,20 +54,22 @@ export default {
53
54
 
54
55
  const sortedCount = sortBy(sanitizedCount, 'count:desc');
55
56
 
56
- topTenForResponseTime = topTenForResponseTime.concat(sortedCount);
57
- topTenForResponseTime = sortBy(topTenForResponseTime, 'count:desc');
58
- topTenForResponseTime = topTenForResponseTime.splice(0, 15);
57
+ finalCount = finalCount.concat(sortedCount);
58
+ finalCount = sortBy(finalCount, 'count:desc');
59
+ finalCount = finalCount.splice(0, 15);
59
60
 
60
- topTenForResponseTime.forEach((item, i) => {
61
- topTenForResponseTime[i].id = finalCounts[index].id;
62
- topTenForResponseTime[i].capiId = finalCounts[index].capiId;
61
+ finalCount.forEach((item, i) => {
62
+ finalCount[i].id = finalCounts[index].id;
63
+ finalCount[i].capiId = finalCounts[index].capiId;
63
64
  });
64
65
 
66
+ topFifteenForResponseTime.push(finalCount);
67
+
65
68
  finalCounts[index].counts = sortedCount;
66
69
  }
67
70
  });
68
71
 
69
- this.topTenForResponseTime = topTenForResponseTime;
72
+ this.topFifteenForResponseTime = topFifteenForResponseTime;
70
73
  this.finalCounts = finalCounts;
71
74
  },
72
75
 
@@ -122,12 +125,12 @@ export default {
122
125
 
123
126
  return {
124
127
  systemInformation,
125
- topTenForResponseTime: null,
126
- responseTimes: null,
127
- finalCounts: null,
128
- includeResponseTimes: true,
129
- storeMapping: this.$store?._modules?.root?.state,
130
- latestLogs: console.logs // eslint-disable-line no-console
128
+ topFifteenForResponseTime: null,
129
+ responseTimes: null,
130
+ finalCounts: null,
131
+ includeResponseTimes: true,
132
+ storeMapping: this.$store?._modules?.root?.state,
133
+ latestLogs: console.logs // eslint-disable-line no-console
131
134
  };
132
135
  },
133
136
 
@@ -205,17 +208,21 @@ export default {
205
208
  },
206
209
 
207
210
  async gatherResponseTimes(btnCb) {
208
- return await Promise.all(this.topTenForResponseTime.map((item) => {
209
- const t = Date.now();
210
-
211
- return this.$store.dispatch('management/request', { url: `/k8s/clusters/${ item.capiId }/v1/${ item.resource }` })
212
- .then(() => ({
213
- outcome: 'success', item, durationMs: Date.now() - t
214
- }))
215
- .catch(() => ({
216
- outcome: 'error', item, durationMs: Date.now() - t
217
- }));
218
- })).then((responseTimes) => {
211
+ const promises = this.topFifteenForResponseTime.flatMap((cluster) => {
212
+ return cluster.map((item) => {
213
+ const t = Date.now();
214
+
215
+ return this.$store.dispatch('management/request', { url: `/k8s/clusters/${ item.capiId }/v1/${ item.resource }` })
216
+ .then(() => ({
217
+ outcome: 'success', item, durationMs: Date.now() - t
218
+ }))
219
+ .catch(() => ({
220
+ outcome: 'error', item, durationMs: Date.now() - t
221
+ }));
222
+ });
223
+ });
224
+
225
+ return await Promise.all(promises).then((responseTimes) => {
219
226
  this.responseTimes = responseTimes;
220
227
  this.setResourceResponseTiming(responseTimes);
221
228
  btnCb(true);
package/pages/home.vue CHANGED
@@ -38,8 +38,13 @@ export default {
38
38
  mixins: [PageHeaderActions],
39
39
 
40
40
  fetch() {
41
- this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER });
42
- this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER });
41
+ if ( this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER) ) {
42
+ this.$store.dispatch('management/findAll', { type: CAPI.RANCHER_CLUSTER });
43
+ }
44
+
45
+ if ( this.$store.getters['management/schemaFor'](MANAGEMENT.CLUSTER) ) {
46
+ this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER });
47
+ }
43
48
 
44
49
  if ( this.$store.getters['management/canList'](CAPI.MACHINE) ) {
45
50
  this.$store.dispatch('management/findAll', { type: CAPI.MACHINE });
@@ -307,7 +312,7 @@ export default {
307
312
  <a
308
313
  class="hand"
309
314
  @click.prevent.stop="showWhatsNew"
310
- ><span v-html="t('landing.whatsNewLink')" /></a>
315
+ ><span v-clean-html="t('landing.whatsNewLink')" /></a>
311
316
  </Banner>
312
317
  </div>
313
318
  </div>
@@ -330,7 +335,7 @@ export default {
330
335
  <a
331
336
  class="hand mr-20"
332
337
  @click.prevent.stop="showUserPrefs"
333
- ><span v-html="t('landing.landingPrefs.userPrefs')" /></a>
338
+ ><span v-clean-html="t('landing.landingPrefs.userPrefs')" /></a>
334
339
  </Banner>
335
340
  </div>
336
341
  </div>
package/pages/index.vue CHANGED
@@ -7,7 +7,9 @@ const validRoute = (route, router) => {
7
7
  };
8
8
 
9
9
  export default {
10
- middleware({ redirect, store, app } ) {
10
+ middleware({
11
+ redirect, store, app, route
12
+ } ) {
11
13
  const seenWhatsNew = store.getters['prefs/get'](SEEN_WHATS_NEW);
12
14
  const versionInfo = getVersionInfo(store);
13
15
  const isSingleProduct = store.getters['isSingleProduct'];
@@ -19,6 +21,13 @@ export default {
19
21
  }
20
22
 
21
23
  const afterLoginRouteObject = store.getters['prefs/afterLoginRoute'];
24
+ const targetRoute = app.router.resolve(afterLoginRouteObject);
25
+
26
+ // If target route is /, then we will loop with endless redirect - so detect that here and
27
+ // redirect to /home, which is what we would do below, if there was no `afterLoginRouteObject`
28
+ if (targetRoute?.route?.fullPath === '/') {
29
+ return redirect(dashboardHome);
30
+ }
22
31
 
23
32
  // Confirm this is a valid route (it could have come from an uninstalled plugin)
24
33
  if (validRoute(afterLoginRouteObject, app.router)) {
@@ -1,5 +1,4 @@
1
1
  <script>
2
- import $ from 'jquery';
3
2
  import { escapeHtml } from '@shell/utils/string';
4
3
 
5
4
  const RADIUS = 5;
@@ -399,7 +398,7 @@ export default {
399
398
  },
400
399
 
401
400
  clicked(event) {
402
- const path = $(event.target).closest('.edgePath');
401
+ const path = event.target.closest('.edgePath');
403
402
 
404
403
  console.log(path); // eslint-disable-line no-console
405
404
  }
package/pkg/tsconfig.json CHANGED
@@ -18,7 +18,6 @@
18
18
  "webpack-env",
19
19
  "@types/node",
20
20
  "@types/jest",
21
- "@nuxt/types"
22
21
  ],
23
22
  "lib": [
24
23
  "esnext",
@@ -0,0 +1,34 @@
1
+ import Vue from 'vue';
2
+ import DOMPurify from 'dompurify';
3
+
4
+ const ALLOWED_TAGS = [
5
+ 'code',
6
+ 'li',
7
+ 'a',
8
+ 'p',
9
+ 'b',
10
+ 'br',
11
+ 'ul',
12
+ 'pre',
13
+ 'span',
14
+ 'div',
15
+ 'i',
16
+ 'em',
17
+ 'strong',
18
+ ];
19
+
20
+ const purifyHTML = value => DOMPurify.sanitize(value, { ALLOWED_TAGS });
21
+
22
+ export const cleanHtmlDirective = {
23
+ inserted(el, binding) {
24
+ el.innerHTML = purifyHTML(binding.value);
25
+ },
26
+ componentUpdated(el, binding) {
27
+ el.innerHTML = purifyHTML(binding.value);
28
+ },
29
+ unbind(el) {
30
+ el.innerHTML = '';
31
+ }
32
+ };
33
+
34
+ Vue.directive('clean-html', cleanHtmlDirective);
@@ -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
  },
@@ -38,7 +38,7 @@ export default (vuexModule, config, init) => {
38
38
  store.registerModule(namespace, vuexModule);
39
39
  store.commit(`${ namespace }/applyConfig`, config);
40
40
 
41
- if ( !process.client || !window.__NUXT__ ) {
41
+ if ( !process.client ) {
42
42
  return;
43
43
  }
44
44
 
@@ -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
  }
package/plugins/plugin.js CHANGED
@@ -2,23 +2,9 @@
2
2
  import { allHashSettled } from '@shell/utils/promise';
3
3
  import { shouldNotLoadPlugin, UI_PLUGIN_BASE_URL } from '@shell/config/uiplugins';
4
4
 
5
- const META_NAME_PREFIX = 'app-autoload-';
6
-
7
5
  export default async function(context) {
8
- // UI Plugins declared in the HTML head
9
- const meta = context.app?.head?.meta || [];
10
6
  const hash = {};
11
7
 
12
- meta.forEach((m) => {
13
- const metaName = m.name || '';
14
-
15
- if (metaName.indexOf(META_NAME_PREFIX) === 0) {
16
- const name = metaName.substr(META_NAME_PREFIX.length);
17
-
18
- hash[name] = context.$plugin.loadAsync(name, m.content);
19
- }
20
- });
21
-
22
8
  // Provide a mechanism to load the UI without the plugins loaded - in case there is a problem
23
9
  let loadPlugins = true;
24
10
 
@@ -0,0 +1,4 @@
1
+ import Vue from 'vue';
2
+ import PortalVue from 'portal-vue';
3
+
4
+ Vue.use(PortalVue);
@@ -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
  }