@rancher/shell 3.0.12-rc.3 → 3.0.12-rc.4

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 (258) hide show
  1. package/assets/styles/global/_layout.scss +4 -0
  2. package/assets/translations/en-us.yaml +144 -41
  3. package/assets/translations/zh-hans.yaml +1 -7
  4. package/chart/monitoring/ClusterSelector.vue +0 -21
  5. package/chart/monitoring/prometheus/index.vue +6 -3
  6. package/components/CruResource.vue +161 -14
  7. package/components/ExplorerMembers.vue +8 -4
  8. package/components/ExplorerProjectsNamespaces.vue +10 -6
  9. package/components/GrowlManager.vue +4 -0
  10. package/components/MgmtNodeList.vue +184 -0
  11. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  12. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  13. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  14. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  15. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +2 -0
  16. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  17. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  18. package/components/ResourceDetail/index.vue +1 -1
  19. package/components/ResourceList/Masthead.vue +7 -1
  20. package/components/ResourceList/index.vue +82 -1
  21. package/components/RichTranslation.vue +5 -2
  22. package/components/Setting.vue +1 -0
  23. package/components/SubtleLink.vue +31 -6
  24. package/components/Tabbed/Tab.vue +29 -3
  25. package/components/Tabbed/index.vue +25 -3
  26. package/components/TableOfContents/TableOfContents.vue +109 -0
  27. package/components/TableOfContents/composables.ts +258 -0
  28. package/components/Window/ContainerShell.vue +21 -11
  29. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  30. package/components/Wizard.vue +9 -4
  31. package/components/fleet/AppCoChartGrid.vue +401 -0
  32. package/components/fleet/AppCoEmptyState.vue +127 -0
  33. package/components/fleet/AppCoPageHeader.vue +119 -0
  34. package/components/fleet/AppCoVersionSelect.vue +70 -0
  35. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  36. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  37. package/components/fleet/FleetClusterTargets/index.vue +189 -146
  38. package/components/fleet/FleetIntro.vue +7 -3
  39. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  40. package/components/fleet/FleetSecretSelector.vue +5 -3
  41. package/components/fleet/FleetValuesFrom.vue +8 -2
  42. package/components/fleet/GitRepoTargetTab.vue +0 -2
  43. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  44. package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
  45. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  46. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  47. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  48. package/components/fleet/HelmOpTargetTab.vue +64 -60
  49. package/components/fleet/HelmOpValuesTab.vue +129 -105
  50. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  51. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  52. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  53. package/components/fleet/__tests__/FleetClusterTargets.test.ts +253 -0
  54. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  55. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  56. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  57. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  58. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  59. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  60. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  61. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  62. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  63. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  64. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  65. package/components/fleet/dashboard/Empty.vue +8 -4
  66. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  67. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  68. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  69. package/components/form/ArrayList.vue +61 -4
  70. package/components/form/KeyValue.vue +23 -2
  71. package/components/form/LabeledSelect.vue +39 -1
  72. package/components/form/Labels.vue +22 -3
  73. package/components/form/NameNsDescription.vue +13 -5
  74. package/components/form/ResourceTabs/index.vue +1 -0
  75. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  76. package/components/formatter/InternalExternalIP.vue +10 -4
  77. package/components/formatter/ServiceTargets.vue +26 -7
  78. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  79. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  80. package/components/nav/Header.vue +4 -0
  81. package/components/nav/TopLevelMenu.vue +7 -2
  82. package/components/nav/__tests__/Header.test.ts +15 -0
  83. package/components/nav/__tests__/TopLevelMenu.test.ts +120 -2
  84. package/components/templates/default.vue +9 -4
  85. package/components/templates/home.vue +9 -4
  86. package/components/templates/plain.vue +9 -4
  87. package/composables/useHelmOpResources.test.ts +56 -0
  88. package/composables/useHelmOpResources.ts +32 -0
  89. package/composables/useStateColor.test.ts +325 -0
  90. package/composables/useStateColor.ts +128 -0
  91. package/config/home-links.js +1 -1
  92. package/config/labels-annotations.js +1 -0
  93. package/config/product/explorer.js +17 -4
  94. package/config/product/manager.js +2 -0
  95. package/config/router/index.js +16 -0
  96. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  97. package/config/router/navigation-guards/authentication.js +10 -4
  98. package/config/router/routes.js +20 -6
  99. package/config/settings.ts +0 -2
  100. package/config/table-headers.js +3 -4
  101. package/config/types.js +9 -0
  102. package/core/plugin-products-base.ts +3 -3
  103. package/core/plugin-types.ts +83 -30
  104. package/core/plugin.ts +3 -0
  105. package/core/types-provisioning.ts +34 -1
  106. package/core/types.ts +15 -2
  107. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  108. package/detail/__tests__/workload.test.ts +3 -152
  109. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  110. package/detail/provisioning.cattle.io.cluster.vue +30 -4
  111. package/detail/workload/index.vue +12 -55
  112. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  113. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
  114. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  115. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  116. package/edit/auth/__tests__/azuread.test.ts +34 -9
  117. package/edit/auth/__tests__/github.test.ts +234 -0
  118. package/edit/auth/__tests__/oidc.test.ts +26 -6
  119. package/edit/auth/__tests__/saml.test.ts +196 -0
  120. package/edit/auth/azuread.vue +128 -95
  121. package/edit/auth/github.vue +72 -13
  122. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  123. package/edit/auth/ldap/config.vue +8 -0
  124. package/edit/auth/ldap/index.vue +75 -1
  125. package/edit/auth/oidc.vue +119 -73
  126. package/edit/auth/saml.vue +76 -12
  127. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  128. package/edit/fleet.cattle.io.helmop.vue +491 -136
  129. package/edit/management.cattle.io.user.vue +5 -2
  130. package/edit/provisioning.cattle.io.cluster/rke2.vue +84 -10
  131. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  132. package/list/group.principal.vue +5 -4
  133. package/list/harvesterhci.io.management.cluster.vue +8 -9
  134. package/list/management.cattle.io.user.vue +12 -9
  135. package/list/provisioning.cattle.io.cluster.vue +16 -10
  136. package/mixins/__tests__/auth-config.test.ts +90 -0
  137. package/mixins/__tests__/chart.test.ts +94 -0
  138. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  139. package/mixins/auth-config.js +7 -0
  140. package/mixins/chart.js +11 -2
  141. package/mixins/child-hook.js +12 -6
  142. package/mixins/create-edit-view/impl.js +5 -3
  143. package/mixins/resource-fetch-api-pagination.js +21 -1
  144. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  145. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  146. package/models/__tests__/fleet-application.test.ts +175 -0
  147. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  148. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  149. package/models/__tests__/management.cattle.io.node.ts +22 -0
  150. package/models/__tests__/namespace.test.ts +36 -0
  151. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +49 -0
  152. package/models/__tests__/workload.test.ts +401 -26
  153. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  154. package/models/compliance.cattle.io.clusterscan.js +39 -4
  155. package/models/fleet-application.js +4 -0
  156. package/models/fleet.cattle.io.helmop.js +20 -1
  157. package/models/management.cattle.io.cluster.js +18 -2
  158. package/models/management.cattle.io.node.js +44 -3
  159. package/models/namespace.js +1 -1
  160. package/models/pod.js +33 -1
  161. package/models/provisioning.cattle.io.cluster.js +5 -5
  162. package/models/workload.js +108 -13
  163. package/models/workload.service.js +5 -0
  164. package/package.json +14 -13
  165. package/pages/about.vue +5 -6
  166. package/pages/auth/login.vue +0 -35
  167. package/pages/auth/setup.vue +11 -0
  168. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  169. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  170. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  171. package/pages/c/_cluster/apps/charts/chart.vue +2 -1
  172. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  173. package/pages/c/_cluster/apps/charts/install.vue +122 -116
  174. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  175. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  176. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  177. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  178. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  179. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  180. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  181. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  182. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  183. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  184. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  185. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  186. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  187. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  188. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  189. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  190. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  191. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  192. package/pages/c/_cluster/fleet/index.vue +2 -2
  193. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +96 -0
  194. package/pages/c/_cluster/uiplugins/index.vue +15 -0
  195. package/pages/fail-whale.vue +16 -11
  196. package/pages/home.vue +16 -46
  197. package/plugins/clean-html.d.ts +9 -0
  198. package/plugins/dashboard-store/__tests__/resource-class.test.ts +93 -0
  199. package/plugins/dashboard-store/resource-class.js +62 -7
  200. package/plugins/steve/__tests__/actions.test.ts +212 -0
  201. package/plugins/steve/actions.js +96 -0
  202. package/plugins/steve/steve-pagination-utils.ts +1 -1
  203. package/rancher-components/Accordion/Accordion.vue +53 -9
  204. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  205. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  206. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  207. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  208. package/rancher-components/RcButton/RcButton.test.ts +103 -0
  209. package/rancher-components/RcButton/RcButton.vue +94 -15
  210. package/rancher-components/RcButton/types.ts +3 -0
  211. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  212. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  213. package/rancher-components/RcSection/RcSection.vue +28 -3
  214. package/scripts/extension/helm/package/Dockerfile +1 -1
  215. package/scripts/test-plugins-build.sh +2 -1
  216. package/store/__tests__/notifications.test.ts +434 -0
  217. package/store/catalog.js +57 -0
  218. package/store/plugins.js +7 -4
  219. package/types/components/buttonGroup.ts +5 -0
  220. package/types/shell/index.d.ts +104 -70
  221. package/utils/__tests__/auth.test.ts +273 -0
  222. package/utils/__tests__/computed.test.ts +193 -0
  223. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  224. package/utils/__tests__/dom.test.ts +81 -0
  225. package/utils/__tests__/duration.test.ts +37 -1
  226. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  227. package/utils/__tests__/fleet-appco.test.ts +312 -0
  228. package/utils/__tests__/monitoring.test.ts +130 -0
  229. package/utils/__tests__/object.test.ts +22 -0
  230. package/utils/__tests__/platform.test.ts +91 -0
  231. package/utils/__tests__/position.test.ts +237 -0
  232. package/utils/__tests__/provider.test.ts +51 -1
  233. package/utils/__tests__/queue.test.ts +232 -0
  234. package/utils/__tests__/release-notes.test.ts +221 -0
  235. package/utils/__tests__/router.test.js +254 -1
  236. package/utils/__tests__/select.test.ts +208 -0
  237. package/utils/__tests__/time.test.ts +265 -1
  238. package/utils/__tests__/title.test.ts +47 -0
  239. package/utils/__tests__/width.test.ts +53 -0
  240. package/utils/__tests__/window.test.ts +158 -0
  241. package/utils/__tests__/xccdf.test.ts +126 -6
  242. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  243. package/utils/crypto/__tests__/index.test.ts +144 -0
  244. package/utils/duration.ts +104 -0
  245. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  246. package/utils/dynamic-content/info.ts +2 -1
  247. package/utils/error.js +13 -0
  248. package/utils/fleet-appco.ts +323 -0
  249. package/utils/object.js +22 -2
  250. package/utils/provider.ts +12 -0
  251. package/utils/validators/__tests__/container-images.test.ts +104 -0
  252. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  253. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  254. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  255. package/utils/xccdf.ts +39 -42
  256. package/vue.config.js +1 -1
  257. package/pages/support/index.vue +0 -264
  258. package/utils/duration.js +0 -43
@@ -0,0 +1,128 @@
1
+ import { reactive } from 'vue';
2
+ import { useStore } from 'vuex';
3
+ import { colorForState } from '@shell/plugins/dashboard-store/resource-class';
4
+ import type { StateColor } from '@shell/utils/style';
5
+ import stevePaginationUtils from '@shell/plugins/steve/steve-pagination-utils';
6
+ import { PaginationParamFilter } from '@shell/types/store/pagination.types';
7
+
8
+ export interface StateSummaryEntry {
9
+ type: string;
10
+ summary: { property: string; counts: Record<string, { total: number; namespace: Record<string, number> }> }[] | null;
11
+ }
12
+
13
+ const stateColorCache = reactive<Record<string, StateColor>>({});
14
+ const pendingResolves = new Set<string>();
15
+
16
+ /**
17
+ * Composable that maps Kubernetes resource state names to dashboard color tokens.
18
+ *
19
+ * This scenario is to forcefully resolve state colors based on properties like error and transitioning.
20
+ * It is to be used when you only have the state name and don't have the actual state object
21
+ *
22
+ * Using a request of only one resource per unknown state, it will fetch the data to retrieve the state object.
23
+ * For normal cases where the object is already available, this composable is not needed.
24
+ *
25
+ * @returns `toStateColor` — synchronous lookup that triggers background API
26
+ * resolution for unknown states when a resource type is provided.
27
+ */
28
+ export function useStateColor() {
29
+ const store = useStore();
30
+
31
+ /**
32
+ * Returns the cached {@link StateColor} for a given state name.
33
+ * Falls back to `colorForState` as a temporary color and triggers an async
34
+ * resolve via the Steve API when `type` is provided. Once resolved, the
35
+ * reactive cache updates and the UI re-renders with the correct color.
36
+ *
37
+ * @param state - The resource state name (e.g. `'active'`, `'error'`). Case-insensitive.
38
+ * @param type - The Steve resource type (e.g. `'apps.deployment'`). Used to
39
+ * fetch a resource via the API when the state is not cached.
40
+ * @returns The resolved color token.
41
+ */
42
+ function toStateColor(state: string, type: string): StateColor {
43
+ const stateLower = (state || '').toLowerCase();
44
+ const key = `${ type }:${ stateLower }`;
45
+
46
+ if (stateColorCache[key]) {
47
+ return stateColorCache[key];
48
+ }
49
+
50
+ if (!pendingResolves.has(key)) {
51
+ pendingResolves.add(key);
52
+ resolveStateColor(key, state, type);
53
+ }
54
+
55
+ const rawColor = colorForState(stateLower, false, false).replace('text-', '');
56
+
57
+ return (rawColor === 'darker' ? 'disabled' : rawColor) as StateColor;
58
+ }
59
+
60
+ async function resolveStateColor(key: string, originalName: string, type: string): Promise<void> {
61
+ try {
62
+ const schema = store.getters['cluster/schemaFor'](type);
63
+ const params = stevePaginationUtils.createParamsForPagination({
64
+ schema,
65
+ opt: {
66
+ pagination: {
67
+ page: 1,
68
+ pageSize: 1,
69
+ sort: [],
70
+ filters: [PaginationParamFilter.createSingleField({ field: 'metadata.state.name', value: originalName })],
71
+ projectsOrNamespaces: [],
72
+ }
73
+ }
74
+ });
75
+ const url = `${ store.getters['cluster/urlFor'](type) }?${ params }`;
76
+ const res = await store.dispatch('cluster/request', { url });
77
+ const resource = res?.data?.[0];
78
+
79
+ if (resource?.metadata?.state) {
80
+ const { error: isError, transitioning, name } = resource.metadata.state;
81
+ const rawColor = colorForState(name, isError, transitioning).replace('text-', '');
82
+
83
+ stateColorCache[key] = (rawColor === 'darker' ? 'disabled' : rawColor) as StateColor;
84
+ }
85
+ } catch {
86
+ // Keep the colorForState fallback
87
+ } finally {
88
+ pendingResolves.delete(key);
89
+ }
90
+ }
91
+
92
+ async function resolveStateColors(entries: StateSummaryEntry[]): Promise<void> {
93
+ const toResolve: Array<{ key: string; originalName: string; type: string }> = [];
94
+
95
+ for (const entry of entries) {
96
+ if (!entry.summary) {
97
+ continue;
98
+ }
99
+
100
+ for (const s of entry.summary) {
101
+ if (s.property === 'metadata.state.name') {
102
+ for (const stateName of Object.keys(s.counts)) {
103
+ const key = stateName.toLowerCase();
104
+
105
+ const cacheKey = `${ entry.type }:${ key }`;
106
+
107
+ if (!stateColorCache[cacheKey] && !pendingResolves.has(cacheKey)) {
108
+ pendingResolves.add(cacheKey);
109
+ toResolve.push({
110
+ key: cacheKey, originalName: stateName, type: entry.type
111
+ });
112
+ }
113
+ }
114
+ }
115
+ }
116
+ }
117
+
118
+ const concurrency = 10;
119
+
120
+ for (let i = 0; i < toResolve.length; i += concurrency) {
121
+ await Promise.all(
122
+ toResolve.slice(i, i + concurrency).map((r) => resolveStateColor(r.key, r.originalName, r.type))
123
+ );
124
+ }
125
+ }
126
+
127
+ return { toStateColor, resolveStateColors };
128
+ }
@@ -44,7 +44,7 @@ const APP_COLLECTION_LINK = {
44
44
 
45
45
  const SUPPORT_LINK = {
46
46
  key: 'commercialSupport',
47
- value: '/support',
47
+ value: 'https://www.suse.com/products/rancher/',
48
48
  enabled: true,
49
49
  readonly: true
50
50
  };
@@ -19,6 +19,7 @@ export const NODE_ARCHITECTURE = 'kubernetes.io/arch';
19
19
  export const IMPORTED_CLUSTER_VERSION_MANAGEMENT = 'rancher.io/imported-cluster-version-management';
20
20
  export const UI_PROJECT_SECRET = 'management.cattle.io/project-scoped-secret';
21
21
  export const UI_PROJECT_SECRET_COPY = 'management.cattle.io/project-scoped-secret-copy';
22
+ export const SERVICE_LINKS = 'ui.rancher/service-links';
22
23
 
23
24
  export const KUBERNETES = {
24
25
  SERVICE_ACCOUNT_UID: 'kubernetes.io/service-account.uid',
@@ -10,6 +10,7 @@ import {
10
10
  SNAPSHOT,
11
11
  VIRTUAL_TYPES,
12
12
  CAPI,
13
+ WORKLOAD_DASHBOARD,
13
14
  } from '@shell/config/types';
14
15
 
15
16
  import {
@@ -103,6 +104,7 @@ export function init(store) {
103
104
  CONFIG_MAP
104
105
  ], 'storage');
105
106
  basicType([
107
+ WORKLOAD_DASHBOARD,
106
108
  WORKLOAD,
107
109
  WORKLOAD_TYPES.DEPLOYMENT,
108
110
  WORKLOAD_TYPES.DAEMON_SET,
@@ -112,10 +114,6 @@ export function init(store) {
112
114
  POD,
113
115
  ], 'workload');
114
116
 
115
- setGroupDefaultType('workload', () => {
116
- return store.getters['features/get'](STEVE_CACHE) ? WORKLOAD_TYPES.DEPLOYMENT : undefined;
117
- });
118
-
119
117
  weightGroup('cluster', 99, true);
120
118
  weightGroup('workload', 98, true);
121
119
  weightGroup('serviceDiscovery', 96, true);
@@ -586,6 +584,21 @@ export function init(store) {
586
584
  overview: true,
587
585
  });
588
586
 
587
+ // Workload Dashboard - overview page using the Resource Summary API
588
+ virtualType({
589
+ label: store.getters['i18n/t'](`typeLabel.${ WORKLOAD }`, { count: 2 }),
590
+ group: 'Root',
591
+ namespaced: true,
592
+ name: WORKLOAD_DASHBOARD,
593
+ weight: 100,
594
+ icon: 'folder',
595
+ ifHaveSubTypes: Object.values(WORKLOAD_TYPES),
596
+ ifFeature: STEVE_CACHE,
597
+ route: { name: 'c-cluster-explorer-workload-dashboard' },
598
+ exact: true,
599
+ overview: true,
600
+ });
601
+
589
602
  virtualType({
590
603
  labelKey: 'members.clusterAndProject',
591
604
  group: 'cluster',
@@ -130,10 +130,12 @@ export function init(store) {
130
130
 
131
131
  basicType([
132
132
  HOSTED_PROVIDER,
133
+ CAPI.CAPI_PROVIDER,
133
134
  'rke-kontainer-providers',
134
135
  'rke-node-providers',
135
136
  ], 'providers');
136
137
 
138
+ weightType(CAPI.CAPI_PROVIDER, 4, true);
137
139
  weightType(CAPI.MACHINE_DEPLOYMENT, 4, true);
138
140
  weightType(CAPI.MACHINE_SET, 3, true);
139
141
  weightType(CAPI.MACHINE, 2, true);
@@ -9,6 +9,22 @@ export const routerOptions = {
9
9
  base: process.env.routerBase || '/',
10
10
  routes: Routes,
11
11
  fallback: false,
12
+ scrollBehavior(to, from, savedPosition) {
13
+ // Returning the savedPosition will result in a native-like behavior when
14
+ // navigating with back/forward buttons
15
+ if (savedPosition) {
16
+ return savedPosition;
17
+ }
18
+
19
+ // Handle the "skip to main content" link
20
+ if (to.hash === '#main-content') {
21
+ const el = document.getElementById('main-content');
22
+
23
+ el?.focus();
24
+
25
+ return { el: to.hash };
26
+ }
27
+ },
12
28
  };
13
29
 
14
30
  export function extendRouter(config, context) {
@@ -0,0 +1,130 @@
1
+ import { authenticate } from '@shell/config/router/navigation-guards/authentication';
2
+
3
+ jest.mock('@shell/utils/router', () => ({ routeRequiresAuthentication: () => true }));
4
+
5
+ const isLoggedInMock = jest.fn();
6
+ const findMeMock = jest.fn();
7
+ const notLoggedInMock = jest.fn();
8
+ const noAuthMock = jest.fn();
9
+
10
+ jest.mock('@shell/utils/auth', () => {
11
+ const actual = jest.requireActual('@shell/utils/auth');
12
+
13
+ return {
14
+ ...actual,
15
+ isLoggedIn: (...args: any[]) => isLoggedInMock(...args),
16
+ findMe: (...args: any[]) => findMeMock(...args),
17
+ notLoggedIn: (...args: any[]) => notLoggedInMock(...args),
18
+ noAuth: (...args: any[]) => noAuthMock(...args),
19
+ };
20
+ });
21
+
22
+ function makeStore({ user, fromHeader }: { user: any, fromHeader: string }) {
23
+ const getters: Record<string, any> = {
24
+ 'auth/enabled': true,
25
+ 'auth/loggedIn': false,
26
+ 'auth/user': user,
27
+ 'auth/fromHeader': fromHeader,
28
+ };
29
+
30
+ return {
31
+ dispatch: jest.fn().mockResolvedValue(undefined),
32
+ commit: jest.fn(),
33
+ getters,
34
+ };
35
+ }
36
+
37
+ const to = { name: 'c-cluster-explorer', query: {} };
38
+
39
+ describe('navigation-guards/authentication: mustChangePassword', () => {
40
+ beforeEach(() => {
41
+ isLoggedInMock.mockReset().mockResolvedValue(undefined);
42
+ findMeMock.mockReset();
43
+ notLoggedInMock.mockReset();
44
+ noAuthMock.mockReset();
45
+ });
46
+
47
+ it('redirects local users with mustChangePassword to auth-setup', async() => {
48
+ const store = makeStore({
49
+ user: { mustChangePassword: true, principalIds: ['local://user-1'] },
50
+ fromHeader: 'true',
51
+ });
52
+
53
+ findMeMock.mockResolvedValue({ id: 'local://user-1', provider: 'local' });
54
+
55
+ const next = jest.fn();
56
+
57
+ await authenticate(to as any, {} as any, next, { store } as any);
58
+
59
+ expect(next).toHaveBeenCalledWith({ name: 'auth-setup' });
60
+ expect(isLoggedInMock).not.toHaveBeenCalled();
61
+ });
62
+
63
+ it('lets SSO users with mustChangePassword reach the requested route', async() => {
64
+ const me = { id: 'oidc_user://user@example.com', provider: 'genericoidc' };
65
+ const store = makeStore({
66
+ user: { mustChangePassword: true, principalIds: ['local://user-1', me.id] },
67
+ fromHeader: 'true',
68
+ });
69
+
70
+ findMeMock.mockResolvedValue(me);
71
+
72
+ const next = jest.fn();
73
+
74
+ await authenticate(to as any, {} as any, next, { store } as any);
75
+
76
+ expect(next).toHaveBeenCalledWith();
77
+ expect(next).not.toHaveBeenCalledWith({ name: 'auth-setup' });
78
+ expect(isLoggedInMock).toHaveBeenCalledTimes(1);
79
+ });
80
+
81
+ it('does not redirect when mustChangePassword is false, regardless of provider', async() => {
82
+ const store = makeStore({
83
+ user: { mustChangePassword: false, principalIds: ['local://user-1'] },
84
+ fromHeader: 'true',
85
+ });
86
+
87
+ findMeMock.mockResolvedValue({ id: 'local://user-1', provider: 'local' });
88
+
89
+ const next = jest.fn();
90
+
91
+ await authenticate(to as any, {} as any, next, { store } as any);
92
+
93
+ expect(next).toHaveBeenCalledWith();
94
+ expect(isLoggedInMock).toHaveBeenCalledTimes(1);
95
+ });
96
+
97
+ it('redirects local users with mustChangePassword to auth-setup for older-style fromHeader', async() => {
98
+ const store = makeStore({
99
+ user: { mustChangePassword: true, principalIds: ['local://user-1'] },
100
+ fromHeader: 'unknown',
101
+ });
102
+
103
+ findMeMock.mockResolvedValue({ id: 'local://user-1', provider: 'local' });
104
+
105
+ const next = jest.fn();
106
+
107
+ await authenticate(to as any, {} as any, next, { store } as any);
108
+
109
+ expect(next).toHaveBeenCalledWith({ name: 'auth-setup' });
110
+ expect(isLoggedInMock).not.toHaveBeenCalled();
111
+ });
112
+
113
+ it('lets SSO users with mustChangePassword through for older-style fromHeader', async() => {
114
+ const me = { id: 'saml_user://user', provider: 'saml' };
115
+ const store = makeStore({
116
+ user: { mustChangePassword: true, principalIds: ['local://user-1', me.id] },
117
+ fromHeader: 'unknown',
118
+ });
119
+
120
+ findMeMock.mockResolvedValue(me);
121
+
122
+ const next = jest.fn();
123
+
124
+ await authenticate(to as any, {} as any, next, { store } as any);
125
+
126
+ expect(next).toHaveBeenCalledWith();
127
+ expect(next).not.toHaveBeenCalledWith({ name: 'auth-setup' });
128
+ expect(isLoggedInMock).toHaveBeenCalledTimes(1);
129
+ });
130
+ });
@@ -61,19 +61,21 @@ export async function authenticate(to, from, next, { store }) {
61
61
  await store.dispatch('auth/getUser');
62
62
  const user = store.getters['auth/user'] || {};
63
63
 
64
- if (user?.mustChangePassword) {
65
- return next({ name: 'auth-setup' });
66
- }
67
-
68
64
  // In newer versions the API calls return the auth state instead of having to make a new call all the time.
69
65
  const fromHeader = store.getters['auth/fromHeader'];
70
66
 
67
+ const mustChangePasswordFor = (me) => user?.mustChangePassword && me?.provider === 'local';
68
+
71
69
  if ( fromHeader === 'none' ) {
72
70
  noAuth(store);
73
71
  handleOidcRedirectToCallbackUrl();
74
72
  } else if ( fromHeader === 'true' ) {
75
73
  const me = await findMe(store);
76
74
 
75
+ if (mustChangePasswordFor(me)) {
76
+ return next({ name: 'auth-setup' });
77
+ }
78
+
77
79
  await isLoggedIn(store, getUserObject(user, me));
78
80
  handleOidcRedirectToCallbackUrl();
79
81
  } else if ( fromHeader === 'false' ) {
@@ -85,6 +87,10 @@ export async function authenticate(to, from, next, { store }) {
85
87
  try {
86
88
  const me = await findMe(store);
87
89
 
90
+ if (mustChangePasswordFor(me)) {
91
+ return next({ name: 'auth-setup' });
92
+ }
93
+
88
94
  await isLoggedIn(store, getUserObject(user, me));
89
95
  handleOidcRedirectToCallbackUrl();
90
96
  } catch (e) {
@@ -58,12 +58,7 @@ export default [
58
58
  path: '/home',
59
59
  component: () => interopDefault(import('@shell/pages/home.vue')),
60
60
  name: 'home'
61
- },
62
- {
63
- path: '/support',
64
- component: () => interopDefault(import('@shell/pages/support/index.vue')),
65
- name: 'support'
66
- },
61
+ }
67
62
  ]
68
63
  },
69
64
  {
@@ -238,6 +233,21 @@ export default [
238
233
  path: '/c/:cluster/fleet/application/create',
239
234
  component: () => interopDefault(import('@shell/pages/c/_cluster/fleet/application/create.vue')),
240
235
  name: 'c-cluster-fleet-application-create',
236
+ }, {
237
+ path: '/c/:cluster/fleet/application/suse-app-collection/credentials',
238
+ component: () => interopDefault(import('@shell/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue')),
239
+ name: 'c-cluster-fleet-application-appco-credentials',
240
+ meta: { disableWorkspaceSwitcher: true },
241
+ }, {
242
+ path: '/c/:cluster/fleet/application/suse-app-collection/charts',
243
+ component: () => interopDefault(import('@shell/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue')),
244
+ name: 'c-cluster-fleet-application-appco-charts',
245
+ meta: { disableWorkspaceSwitcher: true },
246
+ }, {
247
+ path: '/c/:cluster/fleet/application/suse-app-collection/chart',
248
+ component: () => interopDefault(import('@shell/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue')),
249
+ name: 'c-cluster-fleet-application-appco-chart',
250
+ meta: { disableWorkspaceSwitcher: true },
241
251
  }, {
242
252
  path: '/c/:cluster/fleet/application/:resource/create',
243
253
  component: () => interopDefault(import('@shell/pages/c/_cluster/fleet/application/_resource/create.vue')),
@@ -349,6 +359,10 @@ export default [
349
359
  path: '/c/:cluster/explorer/explorer-utils',
350
360
  component: () => interopDefault(import('@shell/pages/c/_cluster/explorer/explorer-utils.js')),
351
361
  name: 'c-cluster-explorer-explorer-utils'
362
+ }, {
363
+ path: '/c/:cluster/explorer/workload-dashboard',
364
+ component: () => interopDefault(import('@shell/pages/c/_cluster/explorer/workload-dashboard/index.vue')),
365
+ name: 'c-cluster-explorer-workload-dashboard'
352
366
  }, {
353
367
  path: '/c/:cluster/explorer/tools',
354
368
  component: () => interopDefault(import('@shell/pages/c/_cluster/explorer/tools/index.vue')),
@@ -63,7 +63,6 @@ export const SETTING = {
63
63
  AUTH_USER_INFO_RESYNC_CRON: 'auth-user-info-resync-cron',
64
64
  AUTH_LOCAL_VALIDATE_DESC: 'auth-password-requirements-description',
65
65
  PASSWORD_MIN_LENGTH: 'password-min-length', // CATTLE_PASSWORD_MIN_LENGTH
66
- UI_INDEX: 'ui-index',
67
66
  UI_DASHBOARD_INDEX: 'ui-dashboard-index',
68
67
  UI_DASHBOARD_HARVESTER_LEGACY_PLUGIN: 'ui-dashboard-harvester-legacy-plugin',
69
68
  UI_OFFLINE_PREFERRED: 'ui-offline-preferred',
@@ -164,7 +163,6 @@ export const ALLOWED_SETTINGS: GlobalSetting = {
164
163
  [SETTING.SERVER_URL]: { kind: 'url', canReset: true },
165
164
  [SETTING.RKE_METADATA_CONFIG]: { kind: 'json' },
166
165
  [SETTING.SYSTEM_DEFAULT_REGISTRY]: {},
167
- [SETTING.UI_INDEX]: {},
168
166
  [SETTING.UI_DASHBOARD_INDEX]: {},
169
167
  [SETTING.UI_OFFLINE_PREFERRED]: {
170
168
  kind: 'enum',
@@ -793,11 +793,10 @@ export const FLEET_SUMMARY = {
793
793
  export const FLEET_APPLICATION_TYPE = {
794
794
  name: 'applicationType',
795
795
  labelKey: 'fleet.tableHeaders.applicationType',
796
- value: 'kind',
797
- sort: 'kind',
796
+ value: 'applicationType',
797
+ sort: 'applicationType',
798
798
  search: false,
799
- align: 'center',
800
- width: 100,
799
+ width: 200,
801
800
  };
802
801
 
803
802
  export const FLEET_APPLICATION_SOURCE = {
package/config/types.js CHANGED
@@ -81,6 +81,7 @@ export const RBAC = {
81
81
  };
82
82
 
83
83
  export const WORKLOAD = 'workload';
84
+ export const WORKLOAD_DASHBOARD = 'workload-dashboard';
84
85
 
85
86
  /**
86
87
  * Rancher Workload types
@@ -279,6 +280,7 @@ export const CAPI = {
279
280
  MACHINE: 'cluster.x-k8s.io.machine',
280
281
  RANCHER_CLUSTER: 'provisioning.cattle.io.cluster',
281
282
  MACHINE_CONFIG_GROUP: 'rke-machine-config.cattle.io',
283
+ CAPI_PROVIDER: 'turtles-capi.cattle.io.capiprovider'
282
284
  };
283
285
 
284
286
  // --------------------------------------
@@ -295,6 +297,7 @@ export const FLEET = {
295
297
  DASHBOARD: 'fleet.cattle.io.dashboard',
296
298
  GIT_REPO: 'fleet.cattle.io.gitrepo',
297
299
  HELM_OP: 'fleet.cattle.io.helmop',
300
+ SUSE_APP_COLLECTION: 'suse-application-collection',
298
301
  WORKSPACE: 'management.cattle.io.fleetworkspace',
299
302
  TOKEN: 'fleet.cattle.io.clusterregistrationtoken',
300
303
  BUNDLE_NAMESPACE_MAPPING: 'fleet.cattle.io.bundlenamespacemapping',
@@ -420,6 +423,12 @@ export const CLUSTER_REPO_APPCO_AUTH_GENERATE_NAME = 'clusterrepo-appco-auth-';
420
423
  */
421
424
  export const CLUSTER_REPO_AUTH_GENERATE_NAME = 'clusterrepo-auth-';
422
425
 
426
+ /**
427
+ * The `generateName` prefix used when creating Helm Op authentication secrets
428
+ * for standard Helm sources.
429
+ */
430
+ export const AUTH_GENERATE_NAME = 'auth-';
431
+
423
432
  export const ZERO_TIME = '0001-01-01T00:00:00Z';
424
433
 
425
434
  export const DEFAULT_GRAFANA_STORAGE_SIZE = '10Gi';
@@ -1,7 +1,7 @@
1
1
  import { IExtension } from '@shell/core/types';
2
2
  import {
3
3
  ProductChild, ProductMetadata,
4
- ConfigureTypeConfiguration, VirtualTypeConfiguration,
4
+ ResourcePageConfiguration, CustomPageConfiguration,
5
5
  ProductChildCustomPage, VueRouteComponent,
6
6
  OverviewPageRoutingMetadata
7
7
  } from '@shell/core/plugin-types';
@@ -343,7 +343,7 @@ export abstract class BasePluginProduct {
343
343
  this.registeredPageNames.add(finalName);
344
344
  this.pageIdMap.set(item.name, finalName);
345
345
 
346
- const virtualTypeConfig: VirtualTypeConfiguration = {
346
+ const virtualTypeConfig: CustomPageConfiguration = {
347
347
  label: item.label,
348
348
  labelKey: item.labelKey,
349
349
  namespaced: false,
@@ -377,7 +377,7 @@ export abstract class BasePluginProduct {
377
377
 
378
378
  const route = pluginProductsHelpers.generateConfigureTypeRoute(parentName, item, { extendProduct: !this.isNewProduct });
379
379
 
380
- const configureTypeConfig: ConfigureTypeConfiguration = {
380
+ const configureTypeConfig: ResourcePageConfiguration = {
381
381
  isCreatable: true,
382
382
  isEditable: true,
383
383
  isRemovable: true,