@rancher/shell 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/assets/styles/app.scss +1 -1
  2. package/assets/styles/fonts/_fontstack.scss +11 -11
  3. package/assets/styles/vendor/vue-js-modal.scss +3 -3
  4. package/assets/translations/en-us.yaml +92 -22
  5. package/assets/translations/zh-hans.yaml +84 -15
  6. package/babel.config.js +13 -0
  7. package/chart/gatekeeper.vue +77 -0
  8. package/chart/istio.vue +108 -111
  9. package/chart/logging/index.vue +13 -4
  10. package/chart/monitoring/index.vue +15 -5
  11. package/chart/monitoring/steps/uninstall-v1.vue +2 -2
  12. package/chart/rancher-backup/index.vue +10 -3
  13. package/cloud-credential/aws.vue +1 -1
  14. package/cloud-credential/digitalocean.vue +1 -1
  15. package/cloud-credential/gcp.vue +1 -1
  16. package/cloud-credential/generic.vue +2 -2
  17. package/cloud-credential/linode.vue +1 -1
  18. package/cloud-credential/pnap.vue +1 -1
  19. package/components/ActionMenu.vue +3 -4
  20. package/components/AssignTo.vue +1 -1
  21. package/components/AsyncButton.vue +1 -1
  22. package/components/BannerGraphic.vue +1 -1
  23. package/components/ButtonDropdown.vue +2 -3
  24. package/components/ChartPsp.vue +76 -0
  25. package/components/CruResource.vue +6 -2
  26. package/components/DashboardMetrics.vue +12 -10
  27. package/components/DetailText.vue +1 -1
  28. package/components/DisableAuthProviderModal.vue +1 -1
  29. package/components/EmberPage.vue +1 -1
  30. package/components/EtcdInfoBanner.vue +5 -4
  31. package/components/ExplorerMembers.vue +1 -1
  32. package/components/ExplorerProjectsNamespaces.vue +14 -1
  33. package/components/FileDiff.vue +6 -7
  34. package/components/GrafanaDashboard.vue +18 -21
  35. package/components/LazyImage.vue +10 -12
  36. package/components/LogItem.vue +1 -1
  37. package/components/Markdown.vue +1 -1
  38. package/components/PromptRemove.vue +2 -2
  39. package/components/PromptRestore.vue +1 -1
  40. package/components/ResourceDetail/Masthead.vue +16 -0
  41. package/components/ResourceDetail/index.vue +21 -4
  42. package/components/ResourceList/index.vue +1 -1
  43. package/components/ResourceTable.vue +4 -1
  44. package/components/SingleClusterInfo.vue +2 -2
  45. package/components/SortableTable/THead.vue +1 -1
  46. package/components/SortableTable/index.vue +5 -2
  47. package/components/__tests__/AsyncButton.test.ts +3 -1
  48. package/components/__tests__/ChartPsp.test.ts +75 -0
  49. package/components/__tests__/CruResource.test.ts +3 -1
  50. package/components/auth/Principal.vue +1 -1
  51. package/components/fleet/FleetBundles.vue +3 -1
  52. package/components/fleet/FleetClusters.vue +1 -2
  53. package/components/fleet/FleetIntro.vue +9 -1
  54. package/components/fleet/FleetNoWorkspaces.vue +62 -0
  55. package/components/fleet/FleetSummary.vue +7 -1
  56. package/components/form/LabeledSelect.vue +14 -11
  57. package/components/form/MatchExpressions.vue +17 -2
  58. package/components/form/NameNsDescription.vue +31 -45
  59. package/components/form/ResourceSelector.vue +1 -1
  60. package/components/form/SecretSelector.vue +5 -1
  61. package/components/form/ServiceNameSelect.vue +1 -1
  62. package/components/form/SimpleSecretSelector.vue +9 -9
  63. package/components/form/__tests__/LabeledSelect.test.ts +138 -0
  64. package/components/form/__tests__/NameNsDescription.ts +32 -0
  65. package/components/formatter/InternalExternalIP.vue +6 -0
  66. package/components/formatter/InvolvedObjectLink.vue +54 -0
  67. package/components/formatter/Link.vue +20 -4
  68. package/components/formatter/LinkName.vue +6 -1
  69. package/components/formatter/ServiceTargets.vue +1 -1
  70. package/components/nav/Group.vue +2 -2
  71. package/components/nav/NamespaceFilter.vue +15 -11
  72. package/components/nav/TopLevelMenu.vue +2 -4
  73. package/components/nav/Type.vue +1 -1
  74. package/components/nav/WorkspaceSwitcher.vue +46 -5
  75. package/config/labels-annotations.js +17 -0
  76. package/config/product/auth.js +3 -2
  77. package/config/product/explorer.js +11 -4
  78. package/config/product/fleet.js +2 -0
  79. package/config/router.js +414 -0
  80. package/config/table-headers.js +10 -2
  81. package/config/types.js +11 -8
  82. package/config/uiplugins.js +30 -0
  83. package/content/docs/en-us/whats-new.md +10 -0
  84. package/content/docs/zh-hans/whats-new.md +11 -1
  85. package/core/plugin-routes.ts +23 -0
  86. package/creators/app/app.package.json +2 -1
  87. package/creators/app/files/.eslintrc.js +1 -1
  88. package/creators/app/files/babel.config.js +1 -18
  89. package/creators/app/files/vue.config.js +7 -0
  90. package/creators/app/init +5 -5
  91. package/creators/pkg/files/.github/workflows/build-extension.yml +111 -0
  92. package/creators/pkg/init +35 -4
  93. package/creators/update/init +1 -1
  94. package/detail/constraints.gatekeeper.sh.constraint.vue +20 -10
  95. package/detail/fleet.cattle.io.gitrepo.vue +19 -11
  96. package/detail/harvesterhci.io.management.cluster.vue +3 -3
  97. package/detail/provisioning.cattle.io.cluster.vue +54 -12
  98. package/detail/workload/index.vue +3 -3
  99. package/dialog/AddClusterMemberDialog.vue +1 -1
  100. package/dialog/AddProjectMemberDialog.vue +2 -2
  101. package/dialog/AddonConfigConfirmationDialog.vue +27 -15
  102. package/dialog/DiagnosticTimingsDialog.vue +1 -1
  103. package/dialog/ForceMachineRemoveDialog.vue +1 -1
  104. package/dialog/GenericPrompt.vue +18 -6
  105. package/dialog/RotateEncryptionKeyDialog.vue +1 -1
  106. package/dialog/SaveAsRKETemplateDialog.vue +1 -1
  107. package/dialog/ScaleMachineDownDialog.vue +1 -1
  108. package/edit/auth/github.vue +8 -8
  109. package/edit/auth/googleoauth.vue +5 -5
  110. package/edit/auth/ldap/index.vue +1 -1
  111. package/edit/auth/oidc.vue +1 -1
  112. package/edit/auth/saml.vue +1 -1
  113. package/edit/cis.cattle.io.clusterscan.vue +1 -1
  114. package/edit/fleet.cattle.io.clustergroup.vue +6 -4
  115. package/edit/fleet.cattle.io.gitrepo.vue +16 -3
  116. package/edit/helm.cattle.io.projecthelmchart.vue +5 -1
  117. package/edit/management.cattle.io.fleetworkspace.vue +141 -6
  118. package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +4 -1
  119. package/edit/management.cattle.io.setting.vue +1 -1
  120. package/edit/monitoring.coreos.com.alertmanagerconfig/types/webhook.vue +2 -2
  121. package/edit/monitoring.coreos.com.receiver/tls.vue +18 -18
  122. package/edit/monitoring.coreos.com.receiver/types/webhook.banner.vue +4 -4
  123. package/edit/monitoring.coreos.com.receiver/types/webhook.vue +1 -1
  124. package/edit/namespace.vue +2 -2
  125. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +126 -45
  126. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -1
  127. package/edit/provisioning.cattle.io.cluster/MachinePool.vue +10 -0
  128. package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +1 -0
  129. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +202 -2
  130. package/edit/provisioning.cattle.io.cluster/rke2.vue +248 -84
  131. package/edit/resources.cattle.io.backup.vue +1 -1
  132. package/edit/service.vue +1 -1
  133. package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +2 -2
  134. package/edit/workload/__tests__/Job.test.ts +3 -1
  135. package/edit/workload/index.vue +8 -3
  136. package/edit/workload/mixins/workload.js +16 -0
  137. package/layouts/default.vue +7 -3
  138. package/list/fleet.cattle.io.bundle.vue +6 -3
  139. package/list/fleet.cattle.io.clusterregistrationtoken.vue +3 -1
  140. package/list/fleet.cattle.io.gitrepo.vue +44 -5
  141. package/list/management.cattle.io.fleetworkspace.vue +45 -0
  142. package/list/node.vue +69 -16
  143. package/list/provisioning.cattle.io.cluster.vue +30 -1
  144. package/machine-config/azure.vue +97 -38
  145. package/middleware/authenticated.js +34 -0
  146. package/mixins/chart.js +73 -2
  147. package/mixins/resource-fetch.js +2 -2
  148. package/models/apps.statefulset.js +28 -0
  149. package/models/cluster/node.js +23 -2
  150. package/models/cluster.x-k8s.io.machine.js +4 -2
  151. package/models/clusterroletemplatebinding.js +7 -0
  152. package/models/constraints.gatekeeper.sh.constraint.js +9 -0
  153. package/models/fleet.cattle.io.cluster.js +19 -10
  154. package/models/fleet.cattle.io.gitrepo.js +7 -2
  155. package/models/management.cattle.io.cluster.js +1 -1
  156. package/models/management.cattle.io.fleetworkspace.js +12 -0
  157. package/models/management.cattle.io.gitreporestriction.js +5 -0
  158. package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.js +3 -0
  159. package/models/provisioning.cattle.io.cluster.js +7 -5
  160. package/nuxt/App.js +210 -0
  161. package/nuxt/axios.js +186 -0
  162. package/nuxt/client.js +817 -0
  163. package/nuxt/components/nuxt-build-indicator.vue +143 -0
  164. package/nuxt/components/nuxt-child.js +122 -0
  165. package/nuxt/components/nuxt-error.vue +98 -0
  166. package/nuxt/components/nuxt-link.client.js +98 -0
  167. package/nuxt/components/nuxt-link.server.js +16 -0
  168. package/nuxt/components/nuxt-loading.vue +154 -0
  169. package/nuxt/components/nuxt.js +101 -0
  170. package/nuxt/cookie-universal-nuxt.js +9 -0
  171. package/nuxt/empty.js +1 -0
  172. package/nuxt/index.js +365 -0
  173. package/nuxt/jsonp.js +82 -0
  174. package/nuxt/loading.html +39 -0
  175. package/nuxt/middleware.js +12 -0
  176. package/nuxt/mixins/fetch.client.js +90 -0
  177. package/nuxt/mixins/fetch.server.js +69 -0
  178. package/nuxt/portal-vue.js +4 -0
  179. package/nuxt/server.js +312 -0
  180. package/nuxt/store.js +178 -0
  181. package/nuxt/utils.js +630 -0
  182. package/nuxt/views/app.template.html +9 -0
  183. package/nuxt/views/error.html +23 -0
  184. package/package.json +5 -9
  185. package/pages/auth/setup.vue +2 -2
  186. package/pages/c/_cluster/apps/charts/__tests__/install.helper.test.ts +33 -0
  187. package/pages/c/_cluster/apps/charts/chart.vue +4 -4
  188. package/pages/c/_cluster/apps/charts/install.helpers.js +26 -0
  189. package/pages/c/_cluster/apps/charts/install.vue +40 -66
  190. package/pages/c/_cluster/explorer/EventsTable.vue +5 -19
  191. package/pages/c/_cluster/explorer/index.vue +29 -25
  192. package/pages/c/_cluster/explorer/tools/index.vue +8 -8
  193. package/pages/c/_cluster/fleet/index.vue +95 -34
  194. package/pages/c/_cluster/gatekeeper/index.vue +1 -1
  195. package/pages/c/_cluster/istio/index.vue +5 -5
  196. package/pages/c/_cluster/manager/cloudCredential/index.vue +1 -1
  197. package/pages/c/_cluster/monitoring/index.vue +7 -0
  198. package/pages/c/_cluster/uiplugins/InstallDialog.vue +8 -8
  199. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +20 -7
  200. package/pages/c/_cluster/uiplugins/index.vue +49 -17
  201. package/pages/home.vue +9 -4
  202. package/pages/index.vue +10 -1
  203. package/plugins/clean-html-directive.js +31 -0
  204. package/plugins/dashboard-store/actions.js +32 -9
  205. package/plugins/dashboard-store/mutations.js +5 -2
  206. package/plugins/dashboard-store/resource-class.js +8 -1
  207. package/plugins/steve/mutations.js +3 -2
  208. package/plugins/steve/steve-description-class.js +5 -1
  209. package/plugins/steve/subscribe.js +63 -54
  210. package/plugins/steve-create-worker.js +14 -0
  211. package/promptRemove/management.cattle.io.globalrole.vue +2 -2
  212. package/promptRemove/management.cattle.io.project.vue +2 -2
  213. package/promptRemove/management.cattle.io.roletemplate.vue +2 -2
  214. package/promptRemove/pod.vue +1 -1
  215. package/public/index.html +65 -0
  216. package/rancher-components/components/Banner/Banner.test.ts +9 -1
  217. package/rancher-components/components/Banner/Banner.vue +1 -1
  218. package/rancher-components/components/Form/Checkbox/Checkbox.vue +2 -0
  219. package/rancher-components/components/Form/Radio/RadioButton.vue +1 -1
  220. package/scripts/build-pkg.sh +1 -0
  221. package/scripts/clean +6 -0
  222. package/scripts/extension/bundle +58 -0
  223. package/scripts/extension/helmpatch +89 -0
  224. package/scripts/extension/publish +314 -0
  225. package/scripts/test-plugins-build.sh +4 -0
  226. package/store/__tests__/index.test.ts +110 -0
  227. package/store/index.js +145 -58
  228. package/store/type-map.js +5 -1
  229. package/tsconfig.default.json +36 -0
  230. package/tsconfig.json +24 -0
  231. package/types/shell/index.d.ts +420 -343
  232. package/utils/__tests__/string.test.ts +12 -0
  233. package/utils/auth.js +65 -0
  234. package/utils/monitoring.js +2 -1
  235. package/utils/position.js +5 -8
  236. package/utils/router.scrollBehavior.js +80 -0
  237. package/utils/select.js +1 -3
  238. package/utils/socket.js +1 -0
  239. package/utils/string.js +13 -0
  240. package/utils/time.js +9 -0
  241. package/vue.config.js +679 -0
  242. package/chart/rancher-alerting-drivers.vue +0 -53
  243. package/chart/rancher-gatekeeper.vue +0 -37
  244. package/creators/app/files/nuxt.config.js +0 -6
  245. package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +0 -4
  246. package/nuxt.config.js +0 -798
@@ -3,11 +3,15 @@ import { mapState } from 'vuex';
3
3
  import { FLEET } from '@shell/config/types';
4
4
  import { WORKSPACE } from '@shell/store/prefs';
5
5
  import { STATES_ENUM, STATES, getStateLabel } from '@shell/plugins/dashboard-store/resource-class';
6
- import { allHash } from '@shell/utils/promise';
7
6
  import Loading from '@shell/components/Loading';
8
7
  import CollapsibleCard from '@shell/components/CollapsibleCard.vue';
9
8
  import ResourceTable from '@shell/components/ResourceTable';
10
9
  import CompoundStatusBadge from '@shell/components/CompoundStatusBadge';
10
+ import { checkPermissions, checkSchemasForFindAllHash } from '@shell/utils/auth';
11
+ import { WORKSPACE_ANNOTATION } from '@shell/config/labels-annotations';
12
+ import { filterBy } from '@shell/utils/array';
13
+ import FleetNoWorkspaces from '@shell/components/fleet/FleetNoWorkspaces.vue';
14
+ import { NAME } from '@shell/config/product/fleet';
11
15
 
12
16
  export default {
13
17
  name: 'ListGitRepo',
@@ -15,27 +19,38 @@ export default {
15
19
  Loading,
16
20
  ResourceTable,
17
21
  CollapsibleCard,
18
- CompoundStatusBadge
22
+ CompoundStatusBadge,
23
+ FleetNoWorkspaces
19
24
  },
20
25
 
21
26
  async fetch() {
22
- const hash = await allHash({
23
- allBundles: this.$store.dispatch('management/findAll', { type: FLEET.BUNDLE }),
24
- gitRepos: this.$store.dispatch('management/findAll', { type: FLEET.GIT_REPO }),
25
- fleetWorkspaces: this.$store.dispatch('management/findAll', { type: FLEET.WORKSPACE }),
26
- });
27
+ const hash = await checkSchemasForFindAllHash({
28
+ fleetWorkspaces: {
29
+ inStoreType: 'management',
30
+ type: FLEET.WORKSPACE,
31
+ schemaValidator: (schema) => {
32
+ return !!schema?.links?.collection;
33
+ }
34
+ },
35
+ allBundles: {
36
+ inStoreType: 'management',
37
+ type: FLEET.BUNDLE,
38
+ },
39
+ gitRepos: {
40
+ inStoreType: 'management',
41
+ type: FLEET.GIT_REPO,
42
+ }
43
+ }, this.$store);
27
44
 
28
- this.allBundles = hash.allBundles;
29
45
  this.gitRepos = hash.gitRepos;
30
- this.fleetWorkspaces = hash.fleetWorkspaces;
46
+ this.fleetWorkspacesData = hash.fleetWorkspaces || [];
31
47
 
32
- // init cards collapse flags
33
- const workspaces = this.fleetWorkspaces.filter(ws => ws.repos.length);
48
+ try {
49
+ const permissions = await checkPermissions({ workspaces: { type: FLEET.WORKSPACE }, gitRepos: { type: FLEET.GIT_REPO, schemaValidator: schema => schema.resourceMethods.includes('PUT') } }, this.$store.getters);
34
50
 
35
- if (workspaces.length) {
36
- workspaces.forEach((ws) => {
37
- this.$set(this.isCollapsed, ws.id, false);
38
- });
51
+ this.permissions = permissions;
52
+ } catch (e) {
53
+ console.error(e); // eslint-disable-line no-console
39
54
  }
40
55
  },
41
56
 
@@ -71,24 +86,49 @@ export default {
71
86
  sort: 'status.resourceCounts.ready',
72
87
  }
73
88
  ],
74
- schema: {},
75
- allBundles: null,
76
- gitRepos: null,
77
- fleetWorkspaces: null,
78
- isCollapsed: {},
79
- getStartedLink: {
89
+ schema: {},
90
+ allBundles: [],
91
+ gitRepos: [],
92
+ fleetWorkspacesData: [],
93
+ isCollapsed: {},
94
+ permissions: {},
95
+ getStartedLink: {
80
96
  name: 'c-cluster-product-resource-create',
81
97
  params: {
82
- product: 'fleet',
98
+ product: NAME,
83
99
  resource: FLEET.GIT_REPO
84
100
  },
85
101
  }
86
102
  };
87
103
  },
88
104
  computed: {
89
- ...mapState(['workspace']),
105
+ ...mapState(['workspace', 'allNamespaces']),
106
+ fleetWorkspaces() {
107
+ if (this.fleetWorkspacesData?.length) {
108
+ return this.fleetWorkspacesData;
109
+ }
110
+
111
+ // When user doesn't have access to the workspaces fall back to namespaces
112
+
113
+ return this.allNamespaces.filter((item) => {
114
+ return item.metadata.annotations[WORKSPACE_ANNOTATION] === WORKSPACE;
115
+ }).map(( obj ) => {
116
+ const repos = filterBy(this.gitRepos, 'metadata.namespace', obj.id);
117
+
118
+ return {
119
+ ...obj,
120
+ counts: {
121
+ clusters: '-',
122
+ clusterGroups: '-',
123
+ gitRepos: repos.length
124
+ },
125
+ repos,
126
+ nameDisplay: obj.id
127
+ };
128
+ });
129
+ },
90
130
  workspacesData() {
91
- return this.fleetWorkspaces.filter(ws => ws.repos.length);
131
+ return this.fleetWorkspaces.filter(ws => ws.repos && ws.repos.length);
92
132
  },
93
133
  emptyWorkspaces() {
94
134
  return this.fleetWorkspaces.filter(ws => !ws.repos || !ws.repos.length);
@@ -105,7 +145,7 @@ export default {
105
145
  this.$router.push({
106
146
  name: 'c-cluster-product-resource',
107
147
  params: {
108
- product: 'fleet',
148
+ product: NAME,
109
149
  resource: FLEET.GIT_REPO
110
150
  },
111
151
  });
@@ -247,6 +287,14 @@ export default {
247
287
  });
248
288
  }
249
289
  },
290
+
291
+ watch: {
292
+ fleetWorkspaces(value) {
293
+ value?.filter(ws => ws.repos?.length).forEach((ws) => {
294
+ this.$set(this.isCollapsed, ws.id, false);
295
+ });
296
+ }
297
+ }
250
298
  };
251
299
  </script>
252
300
 
@@ -254,6 +302,10 @@ export default {
254
302
  <div class="fleet-dashboard">
255
303
  <Loading v-if="$fetchState.pending" />
256
304
  <!-- no git repos -->
305
+ <FleetNoWorkspaces
306
+ v-else-if="!fleetWorkspacesData.length"
307
+ :can-view="permissions.workspaces"
308
+ />
257
309
  <div
258
310
  v-else-if="!gitRepos.length"
259
311
  class="fleet-empty-dashboard"
@@ -270,15 +322,17 @@ export default {
270
322
  {{ t('fleet.dashboard.learnMore') }} <i class="icon icon-external-link" />
271
323
  </a>
272
324
  </p>
273
- <h3 class="mb-30">
274
- {{ t('fleet.dashboard.noRepo', null, true) }}
275
- </h3>
276
- <n-link
277
- :to="getStartedLink"
278
- class="btn role-secondary"
279
- >
280
- {{ t('fleet.dashboard.getStarted') }}
281
- </n-link>
325
+ <template v-if="permissions.gitRepos">
326
+ <h3 class="mb-30">
327
+ {{ t('fleet.dashboard.noRepo', null, true) }}
328
+ </h3>
329
+ <n-link
330
+ :to="getStartedLink"
331
+ class="btn role-secondary"
332
+ >
333
+ {{ t('fleet.dashboard.getStarted') }}
334
+ </n-link>
335
+ </template>
282
336
  </div>
283
337
  <!-- fleet dashboard with repos -->
284
338
  <div
@@ -354,7 +408,9 @@ export default {
354
408
  v-on="$listeners"
355
409
  >
356
410
  <template #cell:clustersReady="{row}">
411
+ <span v-if="ws.type === 'namespace'"> - </span>
357
412
  <CompoundStatusBadge
413
+ v-else
358
414
  :tooltip-text="getTooltipInfo('clusters', row)"
359
415
  :badge-class="getStatusInfo('clusters', row).badgeClass"
360
416
  :icon="getStatusInfo('clusters', row).icon"
@@ -362,7 +418,9 @@ export default {
362
418
  />
363
419
  </template>
364
420
  <template #cell:bundlesReady="{row}">
421
+ <span v-if="ws.type === 'namespace'"> - </span>
365
422
  <CompoundStatusBadge
423
+ v-else
366
424
  :tooltip-text="getTooltipInfo('bundles', row)"
367
425
  :badge-class="getStatusInfo('bundles', row).badgeClass"
368
426
  :icon="getStatusInfo('bundles', row).icon"
@@ -389,6 +447,9 @@ export default {
389
447
  </template>
390
448
 
391
449
  <style lang="scss" scoped>
450
+ .fleet-dashboard {
451
+ min-height: 100vh;
452
+ }
392
453
  .fleet-empty-dashboard {
393
454
  flex: 1;
394
455
  display: flex;
@@ -27,7 +27,7 @@ export default {
27
27
  }
28
28
  }
29
29
  },
30
- count: constraint.violations.length
30
+ count: constraint.totalViolations
31
31
  }));
32
32
  },
33
33
  data(ctx) {
@@ -72,7 +72,7 @@ export default {
72
72
  <template>
73
73
  <div>
74
74
  <h1>Overview</h1>
75
- <h4 v-html="t('istio.poweredBy', {}, true)" />
75
+ <h4 v-clean-html="t('istio.poweredBy', {}, true)" />
76
76
  <div class="links">
77
77
  <div
78
78
  :class="{'disabled':!kialiUrl}"
@@ -99,7 +99,7 @@ export default {
99
99
  </a>
100
100
  <hr>
101
101
  <div class="description">
102
- <span v-html="t('istio.links.kiali.description', {link: monitoringUrl}, true)" />
102
+ <span v-clean-html="t('istio.links.kiali.description', {link: monitoringUrl}, true)" />
103
103
  </div>
104
104
  </div>
105
105
  </span>
@@ -107,7 +107,7 @@ export default {
107
107
  v-if="!kialiUrl"
108
108
  class="disabled-msg"
109
109
  >
110
- <span v-html="t('istio.links.disabled', {app: 'Kiali'})" />
110
+ <span v-clean-html="t('istio.links.disabled', {app: 'Kiali'})" />
111
111
  </div>
112
112
  </div>
113
113
  <div
@@ -135,7 +135,7 @@ export default {
135
135
  </a>
136
136
  <hr>
137
137
  <div class="description">
138
- <span v-html="t('istio.links.jaeger.description', true)" />
138
+ <span v-clean-html="t('istio.links.jaeger.description', true)" />
139
139
  </div>
140
140
  </div>
141
141
  </span>
@@ -143,7 +143,7 @@ export default {
143
143
  v-if="!jaegerUrl"
144
144
  class="disabled-msg"
145
145
  >
146
- <span v-html="t('istio.links.disabled', {app: 'Jaeger'})" />
146
+ <span v-clean-html="t('istio.links.disabled', {app: 'Jaeger'})" />
147
147
  </div>
148
148
  </div>
149
149
  </div>
@@ -89,7 +89,7 @@ export default {
89
89
  <template #cell:apikey="{row}">
90
90
  <span
91
91
  v-if="row.publicData"
92
- v-html="row.publicData"
92
+ v-clean-html="row.publicData"
93
93
  />
94
94
  <span
95
95
  v-else
@@ -214,3 +214,10 @@ export default {
214
214
  </div>
215
215
  </section>
216
216
  </template>
217
+
218
+ <style lang="scss" scoped>
219
+ .create-resource-container .subtype-banner {
220
+ min-height: 80px;
221
+ padding: 10px;
222
+ }
223
+ </style>
@@ -36,7 +36,7 @@ export default {
36
36
 
37
37
  computed: {
38
38
  showVersionSelector() {
39
- return this.plugin?.versions.length > 1;
39
+ return this.versionOptions?.length > 1;
40
40
  },
41
41
 
42
42
  versionOptions() {
@@ -44,8 +44,8 @@ export default {
44
44
  return [];
45
45
  }
46
46
 
47
- // Don't allow update/rollback to curent version
48
- const versions = this.plugin.versions.filter((v) => {
47
+ // Don't allow update/rollback to current version
48
+ const versions = this.plugin?.installableVersions?.filter((v) => {
49
49
  if (this.currentVersion) {
50
50
  return v.version !== this.currentVersion;
51
51
  }
@@ -78,12 +78,12 @@ export default {
78
78
  this.currentVersion = plugin.displayVersion;
79
79
 
80
80
  // Update to latest version, so take the first version
81
- if (plugin.versions.length > 0) {
82
- this.version = plugin.versions[0].version;
81
+ if (plugin.installableVersions?.length > 0) {
82
+ this.version = plugin.installableVersions?.[0]?.version;
83
83
  }
84
84
  } else if (mode === 'rollback') {
85
85
  // Find the newest version once we remove the current version
86
- const versionNames = plugin.versions.filter(v => v.version !== plugin.displayVersion);
86
+ const versionNames = plugin.installableVersions.filter(v => v.version !== plugin.displayVersion);
87
87
 
88
88
  this.currentVersion = plugin.displayVersion;
89
89
 
@@ -93,10 +93,10 @@ export default {
93
93
  }
94
94
 
95
95
  // Make sure we have the version available
96
- const versionChart = plugin.versions?.find(v => v.version === this.version);
96
+ const versionChart = plugin.installableVersions?.find(v => v.version === this.version);
97
97
 
98
98
  if (!versionChart) {
99
- this.version = plugin.versions[0].version;
99
+ this.version = plugin.installableVersions?.[0]?.version;
100
100
  }
101
101
 
102
102
  this.busy = false;
@@ -63,17 +63,19 @@ export default {
63
63
  },
64
64
 
65
65
  async loadPluginVersionInfo(version) {
66
- this.versionError = false;
67
- this.versionInfo = undefined;
68
-
69
66
  const versionName = version || this.info.displayVersion;
70
67
 
71
- this.infoVersion = versionName;
68
+ const isVersionNotCompatibleWithUi = this.info.versions?.find(v => v.version === versionName && !v.isCompatibleWithUi);
72
69
 
73
- if (!this.info.chart) {
70
+ if (!this.info.chart || isVersionNotCompatibleWithUi) {
74
71
  return;
75
72
  }
76
73
 
74
+ this.infoVersion = versionName;
75
+
76
+ this.versionError = false;
77
+ this.versionInfo = undefined;
78
+
77
79
  try {
78
80
  this.versionInfo = await this.$store.dispatch('catalog/getVersionInfo', {
79
81
  repoType: this.info.chart.repoType,
@@ -204,8 +206,9 @@ export default {
204
206
  :key="v.version"
205
207
  >
206
208
  <a
209
+ v-tooltip="v.requiredUiVersion ? t('plugins.info.requiresVersion', { version: v.requiredUiVersion }) : ''"
207
210
  class="version-link"
208
- :class="{'version-active': v.version === infoVersion}"
211
+ :class="{'version-active': v.version === infoVersion, 'disabled': !v.isCompatibleWithUi}"
209
212
  @click="loadPluginVersionInfo(v.version)"
210
213
  >
211
214
  {{ v.version }}
@@ -337,6 +340,7 @@ export default {
337
340
 
338
341
  .plugin-versions {
339
342
  display: flex;
343
+ flex-wrap: wrap;
340
344
  }
341
345
 
342
346
  .plugin-description {
@@ -349,13 +353,22 @@ export default {
349
353
  padding: 2px 8px;
350
354
  border-radius: 5px;
351
355
  user-select: none;
352
- margin-right: 5px;
356
+ margin: 0 5px 5px 0;
357
+ display: block;
353
358
 
354
359
  &.version-active {
355
360
  color: var(--link-text);
356
361
  background: var(--link);
357
362
  }
358
363
 
364
+ &.disabled {
365
+ cursor: not-allowed;
366
+ color: var(--disabled-text) !important;
367
+ background-color: var(--disabled-bg) !important;
368
+ border-color: var(--disabled-bg) !important;
369
+ text-decoration: none !important;
370
+ }
371
+
359
372
  &.version-builtin {
360
373
  display: inline-block;
361
374
  }
@@ -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 {
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)) {
@@ -0,0 +1,31 @@
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
+ };
30
+
31
+ Vue.directive('clean-html', cleanHtmlDirective);