@rancher/shell 3.0.5-rc.3 → 3.0.5-rc.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 (200) hide show
  1. package/assets/images/icons/document.svg +3 -0
  2. package/assets/images/vendor/cognito.svg +1 -0
  3. package/assets/styles/app.scss +1 -0
  4. package/assets/styles/base/_basic.scss +10 -0
  5. package/assets/styles/base/_spacing.scss +29 -0
  6. package/assets/styles/global/_layout.scss +1 -1
  7. package/assets/styles/themes/_dark.scss +25 -0
  8. package/assets/styles/themes/_light.scss +65 -0
  9. package/assets/translations/en-us.yaml +322 -24
  10. package/assets/translations/zh-hans.yaml +8 -5
  11. package/components/Certificates.vue +5 -0
  12. package/components/FilterPanel.vue +156 -0
  13. package/components/{fleet/ForceDirectedTreeChart/index.vue → ForceDirectedTreeChart.vue} +47 -41
  14. package/components/IconOrSvg.vue +14 -35
  15. package/components/PromptRemove.vue +5 -1
  16. package/components/Resource/Detail/Card/PodsCard/Bubble.vue +13 -0
  17. package/components/Resource/Detail/Card/PodsCard/composable.ts +30 -0
  18. package/components/Resource/Detail/Card/PodsCard/index.vue +118 -0
  19. package/components/Resource/Detail/Card/ResourceUsageCard/composable.ts +51 -0
  20. package/components/Resource/Detail/Card/ResourceUsageCard/index.vue +79 -0
  21. package/components/Resource/Detail/Card/Scaler.vue +89 -0
  22. package/components/Resource/Detail/Card/StateCard/composables.ts +112 -0
  23. package/components/Resource/Detail/Card/StateCard/index.vue +39 -0
  24. package/components/Resource/Detail/Card/VerticalGap.vue +11 -0
  25. package/components/Resource/Detail/Card/__tests__/Card.test.ts +36 -0
  26. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +84 -0
  27. package/components/Resource/Detail/Card/__tests__/ResourceUsageCard.test.ts +72 -0
  28. package/components/Resource/Detail/Card/__tests__/Scaler.test.ts +87 -0
  29. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +53 -0
  30. package/components/Resource/Detail/Card/__tests__/VerticalGap.test.ts +14 -0
  31. package/components/Resource/Detail/Card/__tests__/index.test.ts +36 -0
  32. package/components/Resource/Detail/Card/index.vue +56 -0
  33. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +19 -0
  34. package/components/Resource/Detail/Metadata/Annotations/composable.ts +12 -0
  35. package/components/Resource/Detail/Metadata/Annotations/index.vue +26 -0
  36. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +103 -0
  37. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +281 -0
  38. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +111 -0
  39. package/components/Resource/Detail/Metadata/KeyValue.vue +130 -0
  40. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +18 -0
  41. package/components/Resource/Detail/Metadata/Labels/composable.ts +12 -0
  42. package/components/Resource/Detail/Metadata/Labels/index.vue +27 -0
  43. package/components/Resource/Detail/Metadata/Rectangle.vue +32 -0
  44. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +107 -0
  45. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +24 -0
  46. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +91 -0
  47. package/components/Resource/Detail/Metadata/composables.ts +29 -0
  48. package/components/Resource/Detail/Metadata/index.vue +66 -0
  49. package/components/Resource/Detail/Page.vue +22 -0
  50. package/components/Resource/Detail/PercentageBar.vue +40 -0
  51. package/components/Resource/Detail/ResourceRow.vue +119 -0
  52. package/components/Resource/Detail/SpacedRow.vue +14 -0
  53. package/components/Resource/Detail/StatusBar.vue +59 -0
  54. package/components/Resource/Detail/StatusRow.vue +61 -0
  55. package/components/Resource/Detail/TitleBar/Title.vue +13 -0
  56. package/components/Resource/Detail/TitleBar/Top.vue +14 -0
  57. package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +17 -0
  58. package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +17 -0
  59. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +142 -0
  60. package/components/Resource/Detail/TitleBar/composable.ts +31 -0
  61. package/components/Resource/Detail/TitleBar/index.vue +124 -0
  62. package/components/Resource/Detail/Top/index.vue +34 -0
  63. package/components/Resource/Detail/__tests__/Page.test.ts +32 -0
  64. package/components/ResourceDetail/__tests__/index.test.ts +114 -0
  65. package/components/ResourceDetail/index.vue +64 -562
  66. package/components/ResourceDetail/legacy.vue +545 -0
  67. package/components/ResourceTable.vue +41 -7
  68. package/components/SlideInPanelManager.vue +76 -8
  69. package/components/SortableTable/index.vue +13 -2
  70. package/components/SortableTable/selection.js +21 -8
  71. package/components/StatusBadge.vue +6 -4
  72. package/components/SubtleLink.vue +25 -0
  73. package/components/Wizard.vue +12 -1
  74. package/components/YamlEditor.vue +1 -1
  75. package/components/__tests__/FilterPanel.test.ts +81 -0
  76. package/components/auth/AuthBanner.vue +2 -3
  77. package/components/auth/RoleDetailEdit.vue +45 -3
  78. package/components/auth/login/oidc.vue +6 -1
  79. package/components/fleet/FleetApplications.vue +181 -0
  80. package/components/fleet/FleetHelmOps.vue +115 -0
  81. package/components/fleet/FleetIntro.vue +58 -28
  82. package/components/fleet/FleetNoWorkspaces.vue +5 -1
  83. package/components/fleet/FleetOCIStorageSecret.vue +171 -0
  84. package/components/fleet/FleetRepos.vue +38 -76
  85. package/components/fleet/FleetResources.vue +50 -22
  86. package/components/fleet/FleetSummary.vue +26 -51
  87. package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +213 -0
  88. package/components/fleet/__tests__/FleetSummary.test.ts +39 -39
  89. package/components/fleet/dashboard/Empty.vue +73 -0
  90. package/components/fleet/dashboard/ResourceCard.vue +183 -0
  91. package/components/fleet/dashboard/ResourceCardSummary.vue +199 -0
  92. package/components/fleet/dashboard/ResourceDetails.vue +196 -0
  93. package/components/fleet/dashboard/ResourcePanel.vue +376 -0
  94. package/components/form/ArrayList.vue +6 -0
  95. package/components/form/SimpleSecretSelector.vue +8 -2
  96. package/components/form/ValueFromResource.vue +31 -19
  97. package/components/formatter/FleetApplicationClustersReady.vue +77 -0
  98. package/components/formatter/FleetApplicationSource.vue +71 -0
  99. package/components/formatter/FleetSummaryGraph.vue +7 -0
  100. package/components/nav/Header.vue +8 -7
  101. package/components/nav/TopLevelMenu.helper.ts +55 -34
  102. package/components/nav/TopLevelMenu.vue +11 -0
  103. package/components/nav/Type.vue +4 -1
  104. package/composables/useI18n.ts +12 -11
  105. package/config/labels-annotations.js +14 -11
  106. package/config/product/auth.js +1 -0
  107. package/config/product/fleet.js +70 -17
  108. package/config/query-params.js +3 -1
  109. package/config/roles.ts +1 -0
  110. package/config/router/routes.js +20 -2
  111. package/config/secret.ts +15 -0
  112. package/config/settings.ts +3 -2
  113. package/config/table-headers.js +52 -22
  114. package/config/types.js +2 -0
  115. package/core/plugin-helpers.ts +3 -2
  116. package/detail/fleet.cattle.io.cluster.vue +28 -15
  117. package/detail/fleet.cattle.io.gitrepo.vue +10 -1
  118. package/detail/fleet.cattle.io.helmop.vue +157 -0
  119. package/dialog/HelmOpForceUpdateDialog.vue +132 -0
  120. package/dialog/RedeployWorkloadDialog.vue +164 -0
  121. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +56 -67
  122. package/edit/auth/oidc.vue +159 -93
  123. package/edit/fleet.cattle.io.gitrepo.vue +26 -33
  124. package/edit/fleet.cattle.io.helmop.vue +997 -0
  125. package/edit/management.cattle.io.fleetworkspace.vue +43 -10
  126. package/list/fleet.cattle.io.gitrepo.vue +1 -1
  127. package/list/fleet.cattle.io.helmop.vue +108 -0
  128. package/list/namespace.vue +5 -2
  129. package/mixins/auth-config.js +8 -1
  130. package/mixins/preset.js +100 -0
  131. package/mixins/resource-fetch-api-pagination.js +2 -0
  132. package/mixins/resource-fetch.js +1 -1
  133. package/mixins/resource-table-watch.js +45 -0
  134. package/models/__tests__/chart.test.ts +273 -0
  135. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
  136. package/models/chart.js +144 -2
  137. package/models/fleet-application.js +385 -0
  138. package/models/fleet.cattle.io.bundle.js +9 -8
  139. package/models/fleet.cattle.io.gitrepo.js +41 -365
  140. package/models/fleet.cattle.io.helmop.js +228 -0
  141. package/models/management.cattle.io.authconfig.js +1 -0
  142. package/models/management.cattle.io.fleetworkspace.js +12 -0
  143. package/models/workload.js +14 -18
  144. package/package.json +2 -1
  145. package/pages/auth/verify.vue +13 -1
  146. package/pages/c/_cluster/apps/charts/AddRepoLink.vue +37 -0
  147. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +80 -0
  148. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +54 -0
  149. package/pages/c/_cluster/apps/charts/StatusLabel.vue +33 -0
  150. package/pages/c/_cluster/apps/charts/index.vue +302 -484
  151. package/pages/c/_cluster/explorer/EventsTable.vue +1 -1
  152. package/pages/c/_cluster/fleet/__tests__/index.test.ts +426 -0
  153. package/pages/c/_cluster/fleet/application/_resource/_id.vue +14 -0
  154. package/pages/c/_cluster/fleet/application/_resource/create.vue +14 -0
  155. package/pages/c/_cluster/fleet/application/create.vue +340 -0
  156. package/pages/c/_cluster/fleet/application/index.vue +139 -0
  157. package/pages/c/_cluster/fleet/graph/config.js +277 -0
  158. package/pages/c/_cluster/fleet/index.vue +772 -330
  159. package/pages/explorer/resource/detail/configmap.vue +19 -0
  160. package/plugins/dashboard-store/actions.js +31 -9
  161. package/plugins/dashboard-store/getters.js +34 -21
  162. package/plugins/dashboard-store/mutations.js +51 -7
  163. package/plugins/dashboard-store/resource-class.js +14 -2
  164. package/plugins/steve/__tests__/subscribe.spec.ts +66 -1
  165. package/plugins/steve/actions.js +3 -0
  166. package/plugins/steve/steve-pagination-utils.ts +14 -13
  167. package/plugins/steve/subscribe.js +229 -42
  168. package/rancher-components/BadgeState/BadgeState.vue +3 -1
  169. package/rancher-components/Form/Checkbox/Checkbox.vue +2 -2
  170. package/rancher-components/RcItemCard/RcItemCard.test.ts +189 -0
  171. package/rancher-components/RcItemCard/RcItemCard.vue +425 -0
  172. package/rancher-components/RcItemCard/RcItemCardAction.vue +24 -0
  173. package/rancher-components/RcItemCard/index.ts +2 -0
  174. package/store/auth.js +1 -0
  175. package/store/catalog.js +62 -24
  176. package/store/index.js +33 -14
  177. package/store/slideInPanel.ts +6 -0
  178. package/store/type-map.js +1 -0
  179. package/types/fleet.d.ts +35 -0
  180. package/types/resources/settings.d.ts +19 -1
  181. package/types/shell/index.d.ts +339 -272
  182. package/types/store/dashboard-store.types.ts +17 -3
  183. package/types/store/pagination.types.ts +6 -1
  184. package/types/store/subscribe.types.ts +50 -0
  185. package/utils/auth.js +32 -3
  186. package/utils/fleet-types.ts +0 -0
  187. package/utils/fleet.ts +200 -1
  188. package/utils/pagination-utils.ts +26 -1
  189. package/utils/pagination-wrapper.ts +132 -50
  190. package/utils/settings.ts +4 -1
  191. package/utils/style.ts +39 -0
  192. package/utils/validators/formRules/__tests__/index.test.ts +36 -3
  193. package/utils/validators/formRules/index.ts +10 -3
  194. package/utils/window.js +11 -7
  195. package/components/__tests__/ApplicationCard.test.ts +0 -27
  196. package/components/cards/ApplicationCard.vue +0 -145
  197. package/components/fleet/ForceDirectedTreeChart/chartIcons.js +0 -17
  198. package/config/secret.js +0 -14
  199. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +0 -249
  200. /package/{components/form/SSHKnownHosts → dialog}/__tests__/KnownHostsEditDialog.test.ts +0 -0
@@ -127,7 +127,7 @@ describe('class GitRepo', () => {
127
127
  describe('resourcesStatuses', () => {
128
128
  it.each([
129
129
  []
130
- ])('foobat', () => {
130
+ ])('fn', () => {
131
131
  jest.spyOn(GitRepo.prototype, '$getters', 'get').mockReturnValue({ byId: jest.fn() });
132
132
 
133
133
  jest.spyOn(GitRepo.prototype, 'targetClusters', 'get').mockReturnValue([{
package/models/chart.js CHANGED
@@ -1,9 +1,12 @@
1
- import { compatibleVersionsFor } from '@shell/store/catalog';
1
+ import { compatibleVersionsFor, APP_UPGRADE_STATUS } from '@shell/store/catalog';
2
2
  import {
3
- REPO_TYPE, REPO, CHART, VERSION, _FLAGGED, HIDE_SIDE_NAV
3
+ REPO_TYPE, REPO, CHART, VERSION, _FLAGGED, HIDE_SIDE_NAV, CATEGORY, TAG
4
4
  } from '@shell/config/query-params';
5
5
  import { BLANK_CLUSTER } from '@shell/store/store-types.js';
6
6
  import SteveModel from '@shell/plugins/steve/steve-class';
7
+ import { CATALOG } from '@shell/config/types';
8
+ import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
9
+ import day from 'dayjs';
7
10
 
8
11
  export default class Chart extends SteveModel {
9
12
  queryParams(from, hideSideNav) {
@@ -47,4 +50,143 @@ export default class Chart extends SteveModel {
47
50
  query,
48
51
  });
49
52
  }
53
+
54
+ /**
55
+ * Returns the list of installed apps that match this chart by:
56
+ * - chart name
57
+ * - repository name
58
+ * - and either:
59
+ * - the home URL matches the latest version’s home URL
60
+ * - or version + home URL match any other known version
61
+ *
62
+ * Inverse logic of `matchingCharts` in `catalog.cattle.io.app.js`.
63
+ *
64
+ * @returns {Array<Object>} List of matching installed app objects.
65
+ */
66
+ get matchingInstalledApps() {
67
+ const latestVersion = this.versions?.[0] || [];
68
+ const appHome = latestVersion?.home;
69
+ const installedApps = this.$rootGetters['cluster/all'](CATALOG.APP);
70
+
71
+ return installedApps.filter((installedApp) => {
72
+ const metadata = installedApp?.spec?.chart?.metadata;
73
+ const name = metadata?.name;
74
+ const version = metadata?.version;
75
+ const home = metadata?.home;
76
+
77
+ const repoName = metadata?.annotations?.[CATALOG_ANNOTATIONS.SOURCE_REPO_NAME] ||
78
+ installedApp?.metadata?.labels?.[CATALOG_ANNOTATIONS.CLUSTER_REPO_NAME];
79
+
80
+ // name and repo should match
81
+ if (name !== this.chartName || !repoName || repoName !== this.repoName) {
82
+ return false;
83
+ }
84
+
85
+ // match by comparing home URL in the latest version vs home URL from the installed app
86
+ if (appHome && home === appHome) {
87
+ return true;
88
+ }
89
+
90
+ // match by version + home for the rest
91
+ return this.versions?.some((v) => v.version === version && home === appHome);
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Determines if the chart is installed by checking if exactly one matching installed app is found.
97
+ *
98
+ * @returns {boolean} `true` if the chart is currently installed.
99
+ */
100
+ get isInstalled() {
101
+ return this.matchingInstalledApps.length === 1;
102
+ }
103
+
104
+ /**
105
+ * Determines if the installed app has a single available upgrade.
106
+ * Requires the chart to be installed.
107
+ *
108
+ * @returns {boolean} `true` if the app is installed and has a single upgrade available.
109
+ */
110
+ get upgradeable() {
111
+ return this.isInstalled && this.matchingInstalledApps[0].upgradeAvailable === APP_UPGRADE_STATUS.SINGLE_UPGRADE;
112
+ }
113
+
114
+ /**
115
+ * Builds structured metadata for display in RcItemCard.vue:
116
+ * - Sub-header (version and last updated)
117
+ * - Footer (repository, categories, tags)
118
+ * - Status indicators (e.g. deprecated, upgradeable, installed)
119
+ *
120
+ * @returns {Object} Card content object with `subHeaderItems`, `footerItems`, and `statuses` arrays.
121
+ */
122
+ get cardContent() {
123
+ if (!this._cardContent) {
124
+ const subHeaderItems = [
125
+ {
126
+ icon: 'icon-version-alt',
127
+ iconTooltip: { key: 'tableHeaders.version' },
128
+ label: this.versions[0].version
129
+ },
130
+ {
131
+ icon: 'icon-refresh-alt',
132
+ iconTooltip: { key: 'tableHeaders.lastUpdated' },
133
+ label: day(this.versions[0].created).format('MMM D, YYYY')
134
+ }
135
+ ];
136
+ const footerItems = [
137
+ {
138
+ type: REPO,
139
+ icon: 'icon-repository-alt',
140
+ iconTooltip: { key: 'tableHeaders.repoName' },
141
+ labels: [this.repoNameDisplay]
142
+ }
143
+ ];
144
+
145
+ if (this.categories.length) {
146
+ footerItems.push( {
147
+ type: CATEGORY,
148
+ icon: 'icon-category-alt',
149
+ iconTooltip: { key: 'generic.category' },
150
+ labels: this.categories
151
+ });
152
+ }
153
+
154
+ if (this.tags.length) {
155
+ footerItems.push({
156
+ type: TAG,
157
+ icon: 'icon-tag-alt',
158
+ iconTooltip: { key: 'generic.tags' },
159
+ labels: this.tags
160
+ });
161
+ }
162
+
163
+ const statuses = [];
164
+
165
+ if (this.deprecated) {
166
+ statuses.push({
167
+ icon: 'icon-alert-alt', color: 'error', tooltip: { key: 'generic.deprecated' }
168
+ });
169
+ }
170
+
171
+ if (this.upgradeable) {
172
+ statuses.push({
173
+ icon: 'icon-upgrade-alt', color: 'info', tooltip: { key: 'generic.upgradeable' }
174
+ });
175
+ }
176
+
177
+ if (this.isInstalled) {
178
+ statuses.push({
179
+ icon: 'icon-confirmation-alt', color: 'success', tooltip: { key: 'generic.installed' }
180
+ });
181
+ }
182
+
183
+ this._cardContent = {
184
+ subHeaderItems,
185
+ footerItems,
186
+ statuses
187
+ };
188
+ }
189
+
190
+ return this._cardContent;
191
+ }
50
192
  }
@@ -0,0 +1,385 @@
1
+ import { convert, matching, convertSelectorObj } from '@shell/utils/selector';
2
+ import jsyaml from 'js-yaml';
3
+ import isEmpty from 'lodash/isEmpty';
4
+ import { escapeHtml } from '@shell/utils/string';
5
+ import { FLEET, MANAGEMENT } from '@shell/config/types';
6
+ import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
7
+ import { addObject, addObjects, findBy } from '@shell/utils/array';
8
+ import SteveModel from '@shell/plugins/steve/steve-class';
9
+ import { mapStateToEnum, primaryDisplayStatusFromCount, STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
10
+ import FleetUtils from '@shell/utils/fleet';
11
+
12
+ export const MINIMUM_POLLING_INTERVAL = 15;
13
+ export const DEFAULT_POLLING_INTERVAL = 60;
14
+
15
+ function normalizeStateCounts(data) {
16
+ if (isEmpty(data)) {
17
+ return {
18
+ total: 0,
19
+ states: {},
20
+ };
21
+ }
22
+ const { desiredReady, ...rest } = data ;
23
+ const states = Object.entries(rest).reduce((res, [key, value]) => {
24
+ res[mapStateToEnum(key)] = value;
25
+
26
+ return res;
27
+ }, {});
28
+
29
+ return {
30
+ total: desiredReady,
31
+ states,
32
+ };
33
+ }
34
+
35
+ export default class FleetApplication extends SteveModel {
36
+ get currentUser() {
37
+ return this.$rootGetters['auth/v3User'] || {};
38
+ }
39
+
40
+ pause() {
41
+ this.spec.paused = true;
42
+ this.save();
43
+ }
44
+
45
+ unpause() {
46
+ this.spec.paused = false;
47
+ this.save();
48
+ }
49
+
50
+ enablePollingAction() {
51
+ this.spec.disablePolling = false;
52
+ this.save();
53
+ }
54
+
55
+ disablePollingAction() {
56
+ this.spec.disablePolling = true;
57
+ this.save();
58
+ }
59
+
60
+ goToClone() {
61
+ if (this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_ID]) {
62
+ delete this.metadata.labels[FLEET_ANNOTATIONS.CREATED_BY_USER_ID];
63
+ }
64
+
65
+ if (this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_NAME]) {
66
+ delete this.metadata.labels[FLEET_ANNOTATIONS.CREATED_BY_USER_NAME];
67
+ }
68
+
69
+ super.goToClone();
70
+ }
71
+
72
+ get isPollingEnabled() {
73
+ return !this.spec.disablePolling;
74
+ }
75
+
76
+ get state() {
77
+ if (this.spec?.paused === true) {
78
+ return 'paused';
79
+ }
80
+
81
+ return this.metadata?.state?.name || 'unknown';
82
+ }
83
+
84
+ get targetClusters() {
85
+ const workspace = this.$getters['byId'](FLEET.WORKSPACE, this.metadata.namespace);
86
+ const clusters = workspace?.clusters || [];
87
+ const groups = workspace?.clusterGroups || [];
88
+
89
+ if (workspace?.id === 'fleet-local') {
90
+ // should we be getting the clusters from workspace.clusters instead of having to rely on the groups,
91
+ // which takes an additional request to be done on the Fleet dashboard screen?
92
+ const local = findBy(groups, 'id', 'fleet-local/default');
93
+
94
+ if (local) {
95
+ return local.targetClusters;
96
+ }
97
+
98
+ return [];
99
+ }
100
+
101
+ if (!this.spec.targets) {
102
+ return [];
103
+ }
104
+
105
+ const out = [];
106
+
107
+ for (const tgt of this.spec.targets) {
108
+ if (tgt.clusterName) {
109
+ const cluster = findBy(clusters, 'metadata.name', tgt.clusterName);
110
+
111
+ if (cluster) {
112
+ addObject(out, cluster);
113
+ }
114
+ } else if (tgt.clusterGroup) {
115
+ const group = findBy(groups, {
116
+ 'metadata.namespace': this.metadata.namespace,
117
+ 'metadata.name': tgt.clusterGroup,
118
+ });
119
+
120
+ if (group) {
121
+ addObjects(out, group.targetClusters);
122
+ }
123
+ } else if (tgt.clusterGroupSelector) {
124
+ const expressions = convertSelectorObj(tgt.clusterGroupSelector);
125
+ const matchingGroups = matching(groups, expressions);
126
+
127
+ for (const group of matchingGroups) {
128
+ addObjects(out, group.targetClusters);
129
+ }
130
+ } else if (tgt.clusterSelector) {
131
+ const expressions = convertSelectorObj(tgt.clusterSelector);
132
+ const matchingClusters = matching(clusters, expressions);
133
+
134
+ addObjects(out, matchingClusters);
135
+ }
136
+ }
137
+
138
+ return out;
139
+ }
140
+
141
+ get targetInfo() {
142
+ let mode = null;
143
+ let cluster = null;
144
+ let clusterGroup = null;
145
+ let advanced = null;
146
+
147
+ const targets = this.spec.targets || [];
148
+
149
+ advanced = jsyaml.dump(targets);
150
+
151
+ if (advanced === '[]\n') {
152
+ advanced = `# - name:
153
+ # clusterSelector:
154
+ # matchLabels:
155
+ # foo: bar
156
+ # matchExpressions:
157
+ # - key: foo
158
+ # op: In
159
+ # values: [bar, baz]
160
+ # clusterGroup: foo
161
+ # clusterGroupSelector:
162
+ # matchLabels:
163
+ # foo: bar
164
+ # matchExpressions:
165
+ # - key: foo
166
+ # op: In
167
+ # values: [bar, baz]
168
+ `;
169
+ }
170
+
171
+ if (this.metadata.namespace === 'fleet-local') {
172
+ mode = 'local';
173
+ } else if (!targets.length) {
174
+ mode = 'none';
175
+ } else if (targets.length === 1) {
176
+ const target = targets[0];
177
+
178
+ if (Object.keys(target).length > 1) {
179
+ // There are multiple properties in a single target, so use the 'advanced' mode
180
+ // (otherwise any existing content is nuked for what we provide)
181
+ mode = 'advanced';
182
+ } else if (target.clusterGroup) {
183
+ clusterGroup = target.clusterGroup;
184
+
185
+ if (!mode) {
186
+ mode = 'clusterGroup';
187
+ }
188
+ } else if (target.clusterName) {
189
+ mode = 'cluster';
190
+ cluster = target.clusterName;
191
+ } else if (target.clusterSelector) {
192
+ if (Object.keys(target.clusterSelector).length === 0) {
193
+ mode = 'all';
194
+ } else {
195
+ const expressions = convert(target.clusterSelector.matchLabels, target.clusterSelector.matchExpressions);
196
+
197
+ if (expressions.length === 1 &&
198
+ expressions[0].key === FLEET_ANNOTATIONS.CLUSTER_NAME &&
199
+ expressions[0].operator === 'In' &&
200
+ expressions[0].values.length === 1
201
+ ) {
202
+ cluster = expressions[0].values[0];
203
+ if (!mode) {
204
+ mode = 'cluster';
205
+ }
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+ if (!mode) {
212
+ mode = 'advanced';
213
+ }
214
+
215
+ return {
216
+ mode,
217
+ modeDisplay: this.t(`fleet.gitRepo.targetDisplay."${ mode }"`),
218
+ cluster,
219
+ clusterGroup,
220
+ advanced
221
+ };
222
+ }
223
+
224
+ get groupByLabel() {
225
+ const name = this.metadata.namespace;
226
+
227
+ if (name) {
228
+ return this.$rootGetters['i18n/t']('resourceTable.groupLabel.workspace', { name: escapeHtml(name) });
229
+ } else {
230
+ return this.$rootGetters['i18n/t']('resourceTable.groupLabel.notInAWorkspace');
231
+ }
232
+ }
233
+
234
+ get allResourceStatuses() {
235
+ return normalizeStateCounts(this.status?.resourceCounts || {});
236
+ }
237
+
238
+ statusResourceCountsForCluster(clusterId) {
239
+ if (!this.targetClusters.some((c) => c.id === clusterId)) {
240
+ return {};
241
+ }
242
+
243
+ return this.status?.perClusterResourceCounts[clusterId] || { desiredReady: 0 };
244
+ }
245
+
246
+ get resourcesStatuses() {
247
+ if (isEmpty(this.status?.resources)) {
248
+ return [];
249
+ }
250
+
251
+ const clusters = (this.targetClusters || []).reduce((res, c) => {
252
+ res[c.id] = c;
253
+
254
+ return res;
255
+ }, {});
256
+
257
+ const resources = this.status?.resources?.reduce((acc, resourceInfo) => {
258
+ const { perClusterState, ...resource } = resourceInfo;
259
+
260
+ if (Object.entries(perClusterState).length === 0) {
261
+ (this.targetClusters || []).forEach((cluster) => {
262
+ acc.push(Object.assign({}, resource, { clusterId: cluster.id, state: resource.state }));
263
+ });
264
+ } else {
265
+ Object.entries(perClusterState).forEach(([state, clusterIds]) => {
266
+ clusterIds.filter((id) => !!clusters[id]).forEach((clusterId) => {
267
+ acc.push(Object.assign({}, resource, { clusterId, state }));
268
+ });
269
+ });
270
+ }
271
+
272
+ return acc;
273
+ }, []);
274
+
275
+ return resources.map((r) => {
276
+ const { namespace, name, clusterId } = r;
277
+ const type = FleetUtils.resourceType(r);
278
+ const c = clusters[clusterId];
279
+
280
+ return {
281
+ key: `${ clusterId }-${ type }-${ namespace }-${ name }`,
282
+
283
+ // Needed?
284
+ id: FleetUtils.resourceId(r),
285
+ type,
286
+ clusterId,
287
+
288
+ // columns, see FleetResources.vue
289
+ state: mapStateToEnum(r.state),
290
+ clusterName: c.nameDisplay,
291
+ apiVersion: r.apiVersion,
292
+ kind: r.kind,
293
+ name,
294
+ namespace,
295
+
296
+ // other properties
297
+ detailLocation: FleetUtils.detailLocation(r, c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME]),
298
+ };
299
+ });
300
+ }
301
+
302
+ get clusterInfo() {
303
+ const ready = this.status?.readyClusters || 0;
304
+ const total = this.status?.desiredReadyClusters || 0;
305
+
306
+ return {
307
+ ready,
308
+ unready: total - ready,
309
+ total,
310
+ };
311
+ }
312
+
313
+ clusterState(clusterId) {
314
+ const resourceCounts = this.statusResourceCountsForCluster(clusterId);
315
+
316
+ return primaryDisplayStatusFromCount(resourceCounts) || STATES_ENUM.ACTIVE;
317
+ }
318
+
319
+ get authorId() {
320
+ return this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_ID];
321
+ }
322
+
323
+ get author() {
324
+ if (this.authorId) {
325
+ return this.$rootGetters['management/byId'](MANAGEMENT.USER, this.authorId);
326
+ }
327
+
328
+ return null;
329
+ }
330
+
331
+ get createdBy() {
332
+ const displayName = this.metadata?.labels?.[FLEET_ANNOTATIONS.CREATED_BY_USER_NAME];
333
+
334
+ if (!displayName) {
335
+ return null;
336
+ }
337
+
338
+ return {
339
+ displayName,
340
+ location: !this.author ? null : {
341
+ name: 'c-cluster-product-resource-id',
342
+ params: {
343
+ cluster: '_',
344
+ product: 'auth',
345
+ resource: MANAGEMENT.USER,
346
+ id: this.author.id,
347
+ }
348
+ }
349
+ };
350
+ }
351
+
352
+ get showCreatedBy() {
353
+ return !!this.createdBy;
354
+ }
355
+
356
+ get clustersList() {
357
+ return this.$getters['all'](FLEET.CLUSTER);
358
+ }
359
+
360
+ get readyClusters() {
361
+ return this.status?.readyClusters || 0;
362
+ }
363
+
364
+ get _detailLocation() {
365
+ return {
366
+ ...super._detailLocation,
367
+ name: 'c-cluster-fleet-application-resource-namespace-id'
368
+ };
369
+ }
370
+
371
+ get doneOverride() {
372
+ return {
373
+ ...super.listLocation,
374
+ name: 'c-cluster-fleet-application'
375
+ };
376
+ }
377
+
378
+ get doneRoute() {
379
+ return this.doneOverride?.name;
380
+ }
381
+
382
+ get parentNameOverride() {
383
+ return this.$rootGetters['i18n/t'](`typeLabel."${ FLEET.APPLICATION }"`, { count: 1 })?.trim();
384
+ }
385
+ }
@@ -1,6 +1,5 @@
1
1
  import { escapeHtml, ucFirst } from '@shell/utils/string';
2
2
  import SteveModel from '@shell/plugins/steve/steve-class';
3
- import typeHelper from '@shell/utils/type-helpers';
4
3
  import { addObject, addObjects, findBy } from '@shell/utils/array';
5
4
  import { FLEET, MANAGEMENT } from '@shell/config/types';
6
5
  import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
@@ -11,18 +10,20 @@ export default class FleetBundle extends SteveModel {
11
10
  return this.status?.conditions?.[0].lastUpdateTime;
12
11
  }
13
12
 
14
- get bundleType() {
15
- if (typeHelper.memberOfObject(this.spec, 'helm')) {
16
- return 'helm';
17
- }
13
+ get repoName() {
14
+ const labels = this.metadata?.labels || {};
18
15
 
19
- return '';
16
+ return labels[FLEET_ANNOTATIONS.REPO_NAME];
20
17
  }
21
18
 
22
- get repoName() {
19
+ get helmName() {
23
20
  const labels = this.metadata?.labels || {};
24
21
 
25
- return labels[FLEET_ANNOTATIONS.REPO_NAME];
22
+ return labels[FLEET_ANNOTATIONS.HELM_NAME];
23
+ }
24
+
25
+ get appSourceName() {
26
+ return this.helmName || this.repoName;
26
27
  }
27
28
 
28
29
  get targetClusters() {