@rancher/shell 3.0.2-rc.5 → 3.0.2

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 (219) hide show
  1. package/assets/images/providers/nutanix.svg +12 -1
  2. package/assets/styles/base/_basic.scss +2 -1
  3. package/assets/styles/base/_helpers.scss +4 -0
  4. package/assets/styles/base/_variables.scss +2 -0
  5. package/assets/styles/global/_labeled-input.scss +5 -13
  6. package/assets/styles/global/_layout.scss +4 -1
  7. package/assets/styles/global/_select.scss +5 -0
  8. package/assets/styles/themes/_dark.scss +1 -3
  9. package/assets/styles/themes/_light.scss +5 -1
  10. package/assets/translations/en-us.yaml +130 -23
  11. package/assets/translations/zh-hans.yaml +0 -3
  12. package/cloud-credential/azure.vue +1 -1
  13. package/components/ActionMenuShell.vue +105 -0
  14. package/components/AppModal.vue +2 -2
  15. package/components/AsyncButton.vue +2 -0
  16. package/components/ButtonGroup.vue +9 -2
  17. package/components/ClusterBadge.vue +1 -0
  18. package/components/ClusterIconMenu.vue +3 -0
  19. package/components/ClusterProviderIcon.vue +14 -1
  20. package/components/CodeMirror.vue +96 -5
  21. package/components/Collapse.vue +16 -3
  22. package/components/CruResource.vue +9 -0
  23. package/components/CruResourceFooter.vue +1 -1
  24. package/components/ExplorerMembers.vue +2 -1
  25. package/components/FixedBanner.vue +19 -12
  26. package/components/Import.vue +14 -1
  27. package/components/LandingPagePreference.vue +4 -2
  28. package/components/PodSecurityAdmission.vue +8 -6
  29. package/components/PromptChangePassword.vue +1 -0
  30. package/components/PromptRemove.vue +23 -21
  31. package/components/ResourceDetail/Masthead.vue +30 -11
  32. package/components/ResourceDetail/__tests__/Masthead.test.ts +61 -0
  33. package/components/ResourceDetail/index.vue +6 -0
  34. package/components/ResourceTable.vue +6 -1
  35. package/components/ResourceYaml.vue +1 -0
  36. package/components/Setting.vue +115 -0
  37. package/components/SortableTable/THead.vue +2 -0
  38. package/components/SortableTable/index.vue +7 -12
  39. package/components/StatusBadge.vue +71 -0
  40. package/components/Tabbed/index.vue +16 -15
  41. package/components/Wizard.vue +108 -104
  42. package/components/YamlEditor.vue +12 -2
  43. package/components/__tests__/Collapse.test.ts +2 -2
  44. package/components/__tests__/FixedBanner.test.ts +3 -3
  45. package/components/auth/Principal.vue +29 -17
  46. package/components/auth/__tests__/Principal.test.ts +40 -0
  47. package/components/auth/login/ldap.vue +7 -0
  48. package/components/fleet/FleetBundles.vue +1 -1
  49. package/components/fleet/FleetRepos.vue +1 -1
  50. package/components/fleet/FleetResources.vue +0 -2
  51. package/components/fleet/FleetSummary.vue +60 -65
  52. package/components/fleet/ForceDirectedTreeChart/index.vue +5 -1
  53. package/components/fleet/__tests__/FleetSummary.test.ts +49 -9
  54. package/components/form/ArrayList.vue +6 -2
  55. package/components/form/ColorInput.vue +1 -0
  56. package/components/form/KeyValue.vue +11 -12
  57. package/components/form/LabeledSelect.vue +15 -3
  58. package/components/form/Labels.vue +8 -1
  59. package/components/form/Members/MembershipEditor.vue +230 -222
  60. package/components/form/Members/__tests__/MembershipEditor.test.ts +62 -0
  61. package/components/form/Password.vue +3 -0
  62. package/components/form/ProjectMemberEditor.vue +6 -3
  63. package/components/form/ResourceTabs/index.vue +15 -13
  64. package/components/form/SSHKnownHosts/KnownHostsEditDialog.vue +5 -4
  65. package/components/form/SchedulingCustomization.vue +85 -0
  66. package/components/form/Select.vue +3 -2
  67. package/components/form/SelectOrCreateAuthSecret.vue +2 -1
  68. package/components/form/UnitInput.vue +3 -4
  69. package/components/form/__tests__/ArrayList.test.ts +9 -6
  70. package/components/form/__tests__/LabeledSelect.test.ts +37 -0
  71. package/components/form/__tests__/SelectOrCreateAuthSecret.test.ts +34 -0
  72. package/components/form/__tests__/UnitInput.test.ts +4 -5
  73. package/components/formatter/LiveDate.vue +3 -1
  74. package/components/formatter/ServiceType.vue +12 -4
  75. package/components/formatter/WorkloadHealthScale.vue +2 -1
  76. package/components/nav/Header.vue +35 -2
  77. package/components/nav/HeaderPageActionMenu.vue +11 -40
  78. package/components/nav/Jump.vue +8 -2
  79. package/components/nav/NamespaceFilter.vue +5 -4
  80. package/components/nav/Pinned.vue +1 -1
  81. package/components/nav/TopLevelMenu.helper.ts +5 -5
  82. package/components/nav/TopLevelMenu.vue +1 -12
  83. package/components/nav/WindowManager/ContainerLogs.vue +96 -58
  84. package/components/nav/WindowManager/ContainerShell.vue +99 -18
  85. package/components/nav/WindowManager/index.vue +74 -6
  86. package/components/nav/__tests__/TopLevelMenu.test.ts +0 -40
  87. package/components/templates/default.vue +2 -47
  88. package/config/features.js +1 -0
  89. package/config/labels-annotations.js +11 -1
  90. package/config/router/navigation-guards/index.js +2 -1
  91. package/config/router/navigation-guards/record-last-route.js +24 -0
  92. package/config/settings.ts +66 -98
  93. package/config/version.js +1 -1
  94. package/core/types-provisioning.ts +7 -0
  95. package/detail/fleet.cattle.io.bundle.vue +7 -0
  96. package/detail/fleet.cattle.io.cluster.vue +0 -3
  97. package/detail/fleet.cattle.io.gitrepo.vue +8 -15
  98. package/detail/provisioning.cattle.io.cluster.vue +8 -2
  99. package/dialog/DeactivateDriverDialog.vue +5 -5
  100. package/dialog/GitRepoForceUpdateDialog.vue +132 -0
  101. package/directives/strip-html-aria-label.js +19 -0
  102. package/edit/__tests__/cis.cattle.io.clusterscan.test.ts +87 -0
  103. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +217 -37
  104. package/edit/auth/__tests__/oidc.test.ts +60 -12
  105. package/edit/auth/ldap/__tests__/config.test.ts +40 -0
  106. package/edit/auth/ldap/config.vue +67 -89
  107. package/edit/auth/oidc.vue +16 -2
  108. package/edit/catalog.cattle.io.clusterrepo.vue +12 -8
  109. package/edit/cis.cattle.io.clusterscan.vue +13 -1
  110. package/edit/fleet.cattle.io.gitrepo.vue +198 -72
  111. package/edit/logging-flow/Match.vue +0 -21
  112. package/edit/management.cattle.io.project.vue +1 -1
  113. package/edit/monitoring.coreos.com.prometheusrule/AlertingRule.vue +10 -3
  114. package/edit/monitoring.coreos.com.prometheusrule/RecordingRule.vue +5 -1
  115. package/edit/monitoring.coreos.com.prometheusrule/index.vue +5 -2
  116. package/edit/provisioning.cattle.io.cluster/CustomCommand.vue +8 -1
  117. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +2 -0
  118. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +0 -2
  119. package/edit/provisioning.cattle.io.cluster/__tests__/CustomCommand.test.ts +55 -15
  120. package/edit/provisioning.cattle.io.cluster/index.vue +28 -30
  121. package/edit/provisioning.cattle.io.cluster/rke2.vue +64 -13
  122. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +37 -2
  123. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +3 -2
  124. package/edit/resources.cattle.io.backup.vue +150 -15
  125. package/edit/secret/__tests__/ssh.test.ts +79 -0
  126. package/edit/secret/ssh.vue +7 -1
  127. package/edit/service.vue +0 -3
  128. package/edit/workload/Job.vue +8 -8
  129. package/edit/workload/__tests__/Job.test.ts +0 -1
  130. package/edit/workload/index.vue +3 -1
  131. package/initialize/install-directives.js +2 -0
  132. package/initialize/install-plugins.js +6 -1
  133. package/list/catalog.cattle.io.app.vue +21 -4
  134. package/list/fleet.cattle.io.bundle.vue +1 -1
  135. package/list/management.cattle.io.setting.vue +34 -132
  136. package/list/provisioning.cattle.io.cluster.vue +11 -3
  137. package/machine-config/vmwarevsphere.vue +15 -8
  138. package/mixins/__tests__/auth-config.test.ts +74 -0
  139. package/mixins/__tests__/chart.test.ts +5 -4
  140. package/mixins/__tests__/create-edit-view.test.ts +38 -0
  141. package/mixins/auth-config.js +8 -0
  142. package/mixins/chart.js +2 -2
  143. package/mixins/create-edit-view/impl.js +4 -1
  144. package/mixins/vue-select-overrides.js +10 -0
  145. package/models/__tests__/catalog.cattle.io.app.test.ts +148 -0
  146. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +157 -0
  147. package/models/__tests__/secret.test.ts +56 -13
  148. package/models/catalog.cattle.io.app.js +112 -37
  149. package/models/cluster.js +11 -0
  150. package/models/fleet.cattle.io.bundle.js +40 -2
  151. package/models/fleet.cattle.io.gitrepo.js +169 -109
  152. package/models/management.cattle.io.fleetworkspace.js +4 -0
  153. package/models/management.cattle.io.kontainerdriver.js +7 -0
  154. package/models/nodedriver.js +4 -1
  155. package/models/provisioning.cattle.io.cluster.js +24 -0
  156. package/models/secret.js +1 -1
  157. package/package.json +5 -5
  158. package/pages/auth/login.vue +5 -11
  159. package/pages/auth/verify.vue +11 -1
  160. package/pages/c/_cluster/apps/charts/index.vue +6 -4
  161. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  162. package/pages/c/_cluster/explorer/ConfigBadge.vue +3 -5
  163. package/pages/c/_cluster/explorer/EventsTable.vue +3 -2
  164. package/pages/c/_cluster/explorer/__tests__/index.test.ts +9 -9
  165. package/pages/c/_cluster/explorer/index.vue +33 -35
  166. package/pages/c/_cluster/explorer/tools/index.vue +3 -3
  167. package/pages/c/_cluster/fleet/index.vue +0 -5
  168. package/pages/c/_cluster/legacy/project/index.vue +1 -1
  169. package/pages/c/_cluster/settings/performance.vue +52 -53
  170. package/pages/c/_cluster/uiplugins/index.vue +19 -22
  171. package/pages/home.vue +17 -12
  172. package/pages/prefs.vue +5 -1
  173. package/plugins/shortkey.js +10 -1
  174. package/plugins/steve/steve-pagination-utils.ts +58 -8
  175. package/promptRemove/management.cattle.io.fleetworkspace.vue +98 -0
  176. package/promptRemove/management.cattle.io.globalrole.vue +1 -1
  177. package/promptRemove/management.cattle.io.project.vue +2 -8
  178. package/promptRemove/management.cattle.io.roletemplate.vue +1 -1
  179. package/promptRemove/mixin/roleDeletionCheck.js +1 -7
  180. package/promptRemove/pod.vue +7 -28
  181. package/rancher-components/Card/Card.vue +9 -1
  182. package/rancher-components/Form/Checkbox/Checkbox.vue +42 -6
  183. package/rancher-components/Form/LabeledInput/LabeledInput.vue +30 -3
  184. package/rancher-components/Form/Radio/RadioButton.vue +18 -3
  185. package/rancher-components/Form/Radio/RadioGroup.vue +39 -5
  186. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +13 -1
  187. package/rancher-components/RcButton/RcButton.test.ts +97 -0
  188. package/rancher-components/RcButton/RcButton.vue +14 -9
  189. package/rancher-components/RcDropdown/RcDropdown.vue +3 -1
  190. package/rancher-components/RcDropdown/RcDropdownItem.vue +8 -2
  191. package/rancher-components/RcDropdown/RcDropdownMenu.vue +66 -0
  192. package/rancher-components/RcDropdown/index.ts +1 -0
  193. package/rancher-components/RcDropdown/types.ts +27 -0
  194. package/rancher-components/RcDropdown/useDropdownContext.ts +5 -2
  195. package/scripts/extension/helm/charts/ui-plugin-server/templates/_helpers.tpl +2 -2
  196. package/scripts/typegen.sh +1 -0
  197. package/store/__tests__/auth.test.ts +120 -0
  198. package/store/action-menu.js +13 -3
  199. package/store/auth.js +14 -9
  200. package/store/aws.js +9 -2
  201. package/store/catalog.js +14 -7
  202. package/store/features.js +1 -0
  203. package/store/prefs.js +9 -28
  204. package/store/type-map.utils.ts +4 -0
  205. package/types/resources/settings.d.ts +27 -20
  206. package/types/shell/index.d.ts +18 -12
  207. package/utils/__tests__/array.test.ts +13 -1
  208. package/utils/__tests__/string.test.ts +80 -1
  209. package/utils/array.ts +13 -0
  210. package/utils/auth.js +4 -0
  211. package/utils/banners.js +0 -45
  212. package/utils/cluster.js +1 -1
  213. package/{edit/monitoring.coreos.com.prometheusrule → utils}/duration.js +5 -3
  214. package/utils/object.js +0 -3
  215. package/utils/pagination-utils.ts +15 -2
  216. package/utils/string.js +31 -7
  217. package/utils/validators/formRules/__tests__/index.test.ts +27 -0
  218. package/utils/validators/formRules/index.ts +16 -0
  219. package/edit/provisioning.cattle.io.cluster/import.vue +0 -198
@@ -0,0 +1,157 @@
1
+ import GitRepo from '@shell/models/fleet.cattle.io.gitrepo.js';
2
+
3
+ const status = {
4
+ commit: 'foo',
5
+ conditions: [
6
+ {
7
+ error: true,
8
+ lastUpdateTime: '2025-02-28T15:39:52Z',
9
+ message: 'Modified(1) [Cluster fleet-local/local]; configmap.v1 lots-a/test-config-one missing',
10
+ status: 'False',
11
+ transitioning: true,
12
+ type: 'Ready'
13
+ },
14
+ {
15
+ error: false,
16
+ lastUpdateTime: '2025-02-28T15:36:25Z',
17
+ status: 'True',
18
+ transitioning: false,
19
+ type: 'GitPolling'
20
+ },
21
+ {
22
+ error: false,
23
+ lastUpdateTime: '2025-02-28T15:36:25Z',
24
+ status: 'False',
25
+ transitioning: false,
26
+ type: 'Reconciling'
27
+ },
28
+ {
29
+ error: false,
30
+ lastUpdateTime: '2025-02-28T15:36:25Z',
31
+ status: 'False',
32
+ transitioning: false,
33
+ type: 'Stalled'
34
+ },
35
+ {
36
+ error: false,
37
+ lastUpdateTime: '2025-02-28T15:36:25Z',
38
+ status: 'True',
39
+ transitioning: false,
40
+ type: 'Accepted'
41
+ }
42
+ ],
43
+ desiredReadyClusters: 1,
44
+ display: {
45
+ readyBundleDeployments: '1/2',
46
+ state: 'Modified'
47
+ },
48
+ gitJobStatus: 'Current',
49
+ lastPollingTriggered: '2025-02-28T16:08:39Z',
50
+ observedGeneration: 1,
51
+ perClusterResourceCounts: {
52
+ 'fleet-local/local': {
53
+ desiredReady: 2,
54
+ missing: 1,
55
+ modified: 0,
56
+ notReady: 0,
57
+ orphaned: 0,
58
+ ready: 2,
59
+ unknown: 0,
60
+ waitApplied: 0
61
+ }
62
+ },
63
+ readyClusters: 0,
64
+ resourceCounts: {
65
+ desiredReady: 2,
66
+ missing: 1,
67
+ modified: 0,
68
+ notReady: 0,
69
+ orphaned: 0,
70
+ ready: 2,
71
+ unknown: 0,
72
+ waitApplied: 0
73
+ },
74
+ resources: [
75
+ {
76
+ apiVersion: 'v1',
77
+ id: 'lots-a/test-config-one',
78
+ kind: 'ConfigMap',
79
+ name: 'test-config-one',
80
+ namespace: 'lots-a',
81
+ perClusterState: {
82
+ missing: [
83
+ 'fleet-local/local'
84
+ ]
85
+ },
86
+ state: 'Missing',
87
+ type: 'configmap'
88
+ },
89
+ {
90
+ apiVersion: 'v1',
91
+ id: 'lots-a/test-config-two',
92
+ kind: 'ConfigMap',
93
+ name: 'test-config-two',
94
+ namespace: 'lots-a',
95
+ perClusterState: {
96
+ ready: [
97
+ 'fleet-local/local'
98
+ ]
99
+ },
100
+ state: 'Ready',
101
+ type: 'configmap'
102
+ },
103
+ ],
104
+ summary: {
105
+ desiredReady: 2,
106
+ modified: 1,
107
+ nonReadyResources: [
108
+ {
109
+ bundleState: 'Modified',
110
+ modifiedStatus: [
111
+ {
112
+ apiVersion: 'v1',
113
+ kind: 'ConfigMap',
114
+ missing: true,
115
+ name: 'test-config-one',
116
+ namespace: 'lots-a'
117
+ }
118
+ ],
119
+ name: 'lots-a-scale-lotsofbundles-one'
120
+ }
121
+ ],
122
+ ready: 1
123
+ }
124
+ };
125
+
126
+ describe('class GitRepo', () => {
127
+ describe('resourcesStatuses', () => {
128
+ it.each([
129
+ []
130
+ ])('foobat', () => {
131
+ jest.spyOn(GitRepo.prototype, '$getters', 'get').mockReturnValue({ byId: jest.fn() });
132
+
133
+ jest.spyOn(GitRepo.prototype, 'targetClusters', 'get').mockReturnValue([{
134
+ id: 'fleet-local/local',
135
+ metadata: { labels: {} }
136
+ }]);
137
+
138
+ const gitRepo = new GitRepo({
139
+ metadata: { namespace: 'fleet-local' },
140
+ spec: {},
141
+ status
142
+ });
143
+
144
+ const resourcesStatuses = gitRepo.resourcesStatuses;
145
+
146
+ const resource1 = resourcesStatuses.find((el: any) => el.id === 'lots-a/test-config-one');
147
+
148
+ expect(resource1.state).toStrictEqual('missing');
149
+ expect(resource1.detailLocation).toBeUndefined();
150
+
151
+ const resource2 = resourcesStatuses.find((el: any) => el.id === 'lots-a/test-config-two');
152
+
153
+ expect(resource2.state).toStrictEqual('ready');
154
+ expect(resource2.detailLocation).toBeDefined();
155
+ });
156
+ });
157
+ });
@@ -1,37 +1,80 @@
1
1
  import Secret from '@shell/models/secret';
2
+ import { SECRET_TYPES as TYPES } from '@shell/config/secret';
2
3
 
3
4
  describe('class Secret', () => {
4
- it('should contains the type attribute if cleanForDownload', async() => {
5
- const secret = new Secret({});
6
- const yaml = `apiVersion: v1
5
+ describe('cleanForDownload', () => {
6
+ it('should contains the type attribute if cleanForDownload', async() => {
7
+ const secret = new Secret({});
8
+ const yaml = `apiVersion: v1
7
9
  kind: Secret
8
10
  metadata:
9
11
  name: my-secret
10
12
  type: Opaque
11
13
  `;
12
- const cleanYaml = await secret.cleanForDownload(yaml);
14
+ const cleanYaml = await secret.cleanForDownload(yaml);
13
15
 
14
- expect(cleanYaml).toBe(yaml);
15
- });
16
+ expect(cleanYaml).toBe(yaml);
17
+ });
16
18
 
17
- it('should remove id, links and actions keys if cleanForDownload', async() => {
18
- const secret = new Secret({});
19
- const expectedYamlStr = `apiVersion: v1
19
+ it('should remove id, links and actions keys if cleanForDownload', async() => {
20
+ const secret = new Secret({});
21
+ const expectedYamlStr = `apiVersion: v1
20
22
  kind: Secret
21
23
  metadata:
22
24
  name: my-secret
23
25
  namespace: default
24
26
  type: Opaque
25
27
  `;
26
- const part = `id: test_id
28
+ const part = `id: test_id
27
29
  links:
28
30
  view: https://example.com
29
31
  actions:
30
32
  remove: https://example.com`;
31
- const yaml = `${ expectedYamlStr }
33
+ const yaml = `${ expectedYamlStr }
32
34
  ${ part }`;
33
- const cleanYaml = await secret.cleanForDownload(yaml);
35
+ const cleanYaml = await secret.cleanForDownload(yaml);
36
+
37
+ expect(cleanYaml).toBe(expectedYamlStr);
38
+ });
39
+ });
40
+
41
+ describe('supportsSshKnownHosts', () => {
42
+ it.each([
43
+ [
44
+ false,
45
+ 'type is not SSH',
46
+ 'generic',
47
+ { known_hosts: 'S05PV05fSE9TVFM=' },
48
+ ],
49
+ [
50
+ false,
51
+ 'missing known_hosts',
52
+ TYPES.SSH,
53
+ {},
54
+ ],
55
+ [
56
+ false,
57
+ 'data is null',
58
+ TYPES.SSH,
59
+ null,
60
+ ],
61
+ [
62
+ true,
63
+ 'type is SSH key and known_hosts exists',
64
+ TYPES.SSH,
65
+ { known_hosts: 'S05PV05fSE9TVFM=' },
66
+ ],
67
+ ])('is %p if %p', (
68
+ supported,
69
+ descr,
70
+ _type,
71
+ data
72
+ ) => {
73
+ const secret = new Secret({ _type, data });
74
+
75
+ const result = secret.supportsSshKnownHosts;
34
76
 
35
- expect(cleanYaml).toBe(expectedYamlStr);
77
+ expect(result).toBe(supported);
78
+ });
36
79
  });
37
80
  });
@@ -9,7 +9,7 @@ import { SHOW_PRE_RELEASE } from '@shell/store/prefs';
9
9
  import { set } from '@shell/utils/object';
10
10
 
11
11
  import SteveModel from '@shell/plugins/steve/steve-class';
12
- import { compatibleVersionsFor } from '@shell/store/catalog';
12
+ import { compatibleVersionsFor, APP_UPGRADE_STATUS } from '@shell/store/catalog';
13
13
 
14
14
  export default class CatalogApp extends SteveModel {
15
15
  showMasthead(mode) {
@@ -22,6 +22,7 @@ export default class CatalogApp extends SteveModel {
22
22
  set(this, 'skipCRDs', false);
23
23
  set(this, 'timeout', 300);
24
24
  set(this, 'wait', true);
25
+ set(this, 'upgradeAvailableVersion', '');
25
26
  }
26
27
 
27
28
  get _availableActions() {
@@ -40,7 +41,7 @@ export default class CatalogApp extends SteveModel {
40
41
  }
41
42
 
42
43
  get warnDeletionMessage() {
43
- if (this.upgradeAvailable === false) {
44
+ if (this.upgradeAvailable === APP_UPGRADE_STATUS.NOT_APPLICABLE) {
44
45
  const manager = this.spec?.chart?.metadata?.annotations?.[CATALOG_ANNOTATIONS.MANAGED] || 'Rancher';
45
46
 
46
47
  return this.t('catalog.delete.warning.managed', { manager: manager === 'true' ? 'Rancher' : manager, name: this.name });
@@ -49,25 +50,56 @@ export default class CatalogApp extends SteveModel {
49
50
  return null;
50
51
  }
51
52
 
52
- matchingChart(includeHidden) {
53
+ /**
54
+ * Finds matching charts based on the current chart's name, repository, and other attributes.
55
+ * The function filters out charts that do not meet specific criteria, including version and home value matches.
56
+ *
57
+ * @param includeHidden - Whether to include hidden charts in the search.
58
+ * @returns An array of matching chart objects that meet the specified criteria.
59
+ */
60
+ matchingCharts(includeHidden) {
53
61
  const chart = this.spec?.chart;
54
62
 
55
63
  if ( !chart ) {
56
- return;
64
+ return [];
57
65
  }
58
66
 
59
67
  const chartName = chart.metadata?.name;
60
68
  const repoName = chart.metadata?.annotations?.[CATALOG_ANNOTATIONS.SOURCE_REPO_NAME] || this.metadata?.labels?.[CATALOG_ANNOTATIONS.CLUSTER_REPO_NAME];
61
- const preferRepoType = chart.metadata?.annotations?.[CATALOG_ANNOTATIONS.SOURCE_REPO_TYPE] || 'cluster';
62
69
 
63
- const match = this.$rootGetters['catalog/chart']({
70
+ const matchingCharts = this.$rootGetters['catalog/chart']({
64
71
  chartName,
65
72
  repoName,
66
- preferRepoType,
67
- includeHidden
73
+ includeHidden,
74
+ multiple: true
75
+ }) || [];
76
+
77
+ if (matchingCharts.length === 0) {
78
+ return [];
79
+ }
80
+
81
+ // Filtering matches by verifying if the current version is in the matched chart's available versions, and that the home value matches as well
82
+ const thisHome = chart?.metadata?.home;
83
+ const bestMatches = matchingCharts.filter(({ versions }) => {
84
+ // First checking if the latest version has the same home value
85
+ if (thisHome === versions[0]?.home) {
86
+ return true;
87
+ }
88
+
89
+ for (let i = 1; i < versions.length; i++) {
90
+ const { version, home } = versions[i];
91
+
92
+ // Finding the exact version, if the version is not there, then most likely it's not a match
93
+ // if the exact version is found, then we can compare the home value
94
+ if (version === this.currentVersion && (home === thisHome)) {
95
+ return true;
96
+ }
97
+ }
98
+
99
+ return false;
68
100
  });
69
101
 
70
- return match;
102
+ return bestMatches;
71
103
  }
72
104
 
73
105
  get currentVersion() {
@@ -75,28 +107,42 @@ export default class CatalogApp extends SteveModel {
75
107
  }
76
108
 
77
109
  get upgradeAvailable() {
78
- // false = does not apply (managed by fleet)
79
- // null = no upgrade found
80
- // object = version available to upgrade to
110
+ // one of the following statuses gets returned:
111
+ // NOT_APPLICABLE - managed by fleet
112
+ // NO_UPGRADE - no upgrade found
113
+ // SINGLE_UPGRADE - a version available to upgrade to
114
+ // MULTIPLE_UPGRADES - more than one match found
81
115
 
82
116
  if (
83
117
  this.spec?.chart?.metadata?.annotations?.[CATALOG_ANNOTATIONS.MANAGED] ||
84
118
  this.spec?.chart?.metadata?.annotations?.[FLEET.BUNDLE_ID]
85
119
  ) {
86
120
  // Things managed by fleet shouldn't show upgrade available even if there might be.
87
- return false;
121
+ return APP_UPGRADE_STATUS.NOT_APPLICABLE;
88
122
  }
89
- const chart = this.matchingChart(false);
90
123
 
91
- if ( !chart ) {
92
- return null;
124
+ const charts = this.matchingCharts(false);
125
+
126
+ if (charts.length === 0) {
127
+ return APP_UPGRADE_STATUS.NO_UPGRADE;
93
128
  }
94
129
 
95
- const workerOSs = this.$rootGetters['currentCluster'].workerOSs;
130
+ // Handle single chart logic
131
+ if (charts.length === 1) {
132
+ return this.evaluateUpgradeForChart(charts[0]);
133
+ }
96
134
 
135
+ // Handle multiple upgrade matches
136
+ return this.handleMultipleUpgradeMatches(charts);
137
+ }
138
+
139
+ /**
140
+ * Evaluates upgrade status for a single chart.
141
+ */
142
+ evaluateUpgradeForChart(chart) {
143
+ const workerOSs = this.$rootGetters['currentCluster'].workerOSs;
97
144
  const showPreRelease = this.$rootGetters['prefs/get'](SHOW_PRE_RELEASE);
98
145
 
99
- const thisVersion = this.spec?.chart?.metadata?.version;
100
146
  let versions = chart.versions;
101
147
 
102
148
  if (!showPreRelease) {
@@ -108,45 +154,75 @@ export default class CatalogApp extends SteveModel {
108
154
  const newestChart = versions?.[0];
109
155
  const newestVersion = newestChart?.version;
110
156
 
111
- if ( !thisVersion || !newestVersion ) {
112
- return null;
157
+ if (!this.currentVersion || !newestVersion) {
158
+ return APP_UPGRADE_STATUS.NO_UPGRADE;
113
159
  }
114
160
 
115
- if ( compare(thisVersion, newestVersion) < 0 ) {
116
- return cleanupVersion(newestVersion);
161
+ if (compare(this.currentVersion, newestVersion) < 0) {
162
+ // Set the available upgrade version to be used in other places
163
+ this.upgradeAvailableVersion = cleanupVersion(newestVersion);
164
+
165
+ return APP_UPGRADE_STATUS.SINGLE_UPGRADE;
117
166
  }
118
167
 
119
- return null;
168
+ return APP_UPGRADE_STATUS.NO_UPGRADE;
120
169
  }
121
170
 
122
- get upgradeAvailableSort() {
123
- const version = this.upgradeAvailable;
171
+ /**
172
+ * Handles the case where multiple upgrade matches are found.
173
+ * @param charts - Array of matching charts
174
+ */
175
+ handleMultipleUpgradeMatches(charts) {
176
+ const qualifiedCharts = [];
177
+
178
+ for (const chart of charts) {
179
+ const status = this.evaluateUpgradeForChart(chart);
124
180
 
125
- if ( !version ) {
126
- return '~'; // Tilde sorts after all numbers and letters
181
+ if (status === APP_UPGRADE_STATUS.SINGLE_UPGRADE) {
182
+ qualifiedCharts.push(chart);
183
+ }
184
+ }
185
+
186
+ if (qualifiedCharts.length > 1) {
187
+ return APP_UPGRADE_STATUS.MULTIPLE_UPGRADES;
188
+ }
189
+
190
+ if (qualifiedCharts.length === 1) {
191
+ const newestVersion = qualifiedCharts[0]?.versions?.[0]?.version;
192
+
193
+ this.upgradeAvailableVersion = cleanupVersion(newestVersion);
194
+
195
+ return APP_UPGRADE_STATUS.SINGLE_UPGRADE;
196
+ }
197
+
198
+ return APP_UPGRADE_STATUS.NO_UPGRADE;
199
+ }
200
+
201
+ get upgradeAvailableSort() {
202
+ if (this.upgradeAvailable === APP_UPGRADE_STATUS.SINGLE_UPGRADE) {
203
+ return sortable(this.upgradeAvailableVersion);
127
204
  }
128
205
 
129
- return sortable(version);
206
+ return '~'; // Tilde sorts after all numbers and letters
130
207
  }
131
208
 
132
209
  get currentVersionCompatible() {
133
210
  const workerOSs = this.$rootGetters['currentCluster'].workerOSs;
134
211
 
135
- const chart = this.matchingChart(false);
136
- const thisVersion = this.spec?.chart?.metadata?.version;
212
+ const chart = this.matchingCharts(false)[0];
137
213
 
138
214
  if (!chart) {
139
215
  return true;
140
216
  }
141
217
 
142
- const versionInChart = chart.versions.find((version) => version.version === thisVersion);
218
+ const versionInChart = chart.versions.find((version) => version.version === this.currentVersion);
143
219
 
144
220
  if (!versionInChart) {
145
221
  return true;
146
222
  }
147
223
  const compatibleVersions = compatibleVersionsFor(chart, workerOSs, true) || [];
148
224
 
149
- const thisVersionCompatible = !!compatibleVersions.find((version) => version.version === thisVersion);
225
+ const thisVersionCompatible = !!compatibleVersions.find((version) => version.version === this.currentVersion);
150
226
 
151
227
  return thisVersionCompatible;
152
228
  }
@@ -155,7 +231,7 @@ export default class CatalogApp extends SteveModel {
155
231
  if (this.currentVersionCompatible) {
156
232
  return null;
157
233
  }
158
- if (this.upgradeAvailable) {
234
+ if (this.upgradeAvailableVersion) {
159
235
  return this.t('catalog.os.versionIncompatible');
160
236
  }
161
237
 
@@ -163,12 +239,11 @@ export default class CatalogApp extends SteveModel {
163
239
  }
164
240
 
165
241
  goToUpgrade(forceVersion, fromTools) {
166
- const match = this.matchingChart(true);
167
- const versionName = this.spec?.chart?.metadata?.version;
242
+ const match = this.matchingCharts(true)[0];
168
243
  const query = {
169
244
  [NAMESPACE]: this.metadata.namespace,
170
245
  [NAME]: this.metadata.name,
171
- [VERSION]: forceVersion || versionName,
246
+ [VERSION]: forceVersion || this.currentVersion,
172
247
  };
173
248
 
174
249
  if ( match ) {
@@ -223,7 +298,7 @@ export default class CatalogApp extends SteveModel {
223
298
  }
224
299
 
225
300
  get versionDisplay() {
226
- return cleanupVersion(this.spec?.chart?.metadata?.version);
301
+ return cleanupVersion(this.currentVersion);
227
302
  }
228
303
 
229
304
  get versionSort() {
package/models/cluster.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import NormanModel from '@shell/plugins/steve/norman-class';
2
+ import { CAPI } from '@shell/config/types';
2
3
 
3
4
  export const LABEL_CONTAINS_PROTECTED = [
4
5
  'io.cattle.lifecycle',
@@ -13,6 +14,7 @@ export const ANNOTATIONS_CONTAINS_PROTECTED = [
13
14
  'k3s.io',
14
15
  'kubernetes.io',
15
16
  'k3s.io',
17
+ 'rancher.io'
16
18
  ];
17
19
  export default class NormanCluster extends NormanModel {
18
20
  get systemLabels() {
@@ -30,4 +32,13 @@ export default class NormanCluster extends NormanModel {
30
32
  get hasSystemAnnotations() {
31
33
  return !!(this.systemAnnotations || []).length;
32
34
  }
35
+
36
+ waitForProvisioning(timeout = 60000, interval) {
37
+ return this.waitForTestFn(() => {
38
+ const ns = this.annotations['objectset.rio.cattle.io/owner-namespace'] || 'fleet-default';
39
+ const id = `${ ns }/${ this.id }`;
40
+
41
+ return id && !!this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, id);
42
+ }, this.$rootGetters['i18n/t']('cluster.managementTimeout'), timeout, interval);
43
+ }
33
44
  }
@@ -2,7 +2,8 @@ import { escapeHtml, ucFirst } from '@shell/utils/string';
2
2
  import SteveModel from '@shell/plugins/steve/steve-class';
3
3
  import typeHelper from '@shell/utils/type-helpers';
4
4
  import { addObject, addObjects, findBy } from '@shell/utils/array';
5
- import { FLEET } from '@shell/config/types';
5
+ import { FLEET, MANAGEMENT } from '@shell/config/types';
6
+ import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
6
7
  import { convertSelectorObj, matching } from '@shell/utils/selector';
7
8
 
8
9
  export default class FleetBundle extends SteveModel {
@@ -21,7 +22,7 @@ export default class FleetBundle extends SteveModel {
21
22
  get repoName() {
22
23
  const labels = this.metadata?.labels || {};
23
24
 
24
- return labels['fleet.cattle.io/repo-name'];
25
+ return labels[FLEET_ANNOTATIONS.REPO_NAME];
25
26
  }
26
27
 
27
28
  get targetClusters() {
@@ -127,4 +128,41 @@ export default class FleetBundle extends SteveModel {
127
128
  );
128
129
  }
129
130
  }
131
+
132
+ get authorId() {
133
+ return this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_ID];
134
+ }
135
+
136
+ get author() {
137
+ if (this.authorId) {
138
+ return this.$rootGetters['management/byId'](MANAGEMENT.USER, this.authorId);
139
+ }
140
+
141
+ return null;
142
+ }
143
+
144
+ get createdBy() {
145
+ const displayName = this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_NAME];
146
+
147
+ if (!displayName) {
148
+ return null;
149
+ }
150
+
151
+ return {
152
+ displayName,
153
+ location: !this.author ? null : {
154
+ name: 'c-cluster-product-resource-id',
155
+ params: {
156
+ cluster: '_',
157
+ product: 'auth',
158
+ resource: MANAGEMENT.USER,
159
+ id: this.author.id,
160
+ }
161
+ }
162
+ };
163
+ }
164
+
165
+ get showCreatedBy() {
166
+ return !!this.createdBy;
167
+ }
130
168
  }