@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.
- package/assets/images/icons/document.svg +3 -0
- package/assets/images/vendor/cognito.svg +1 -0
- package/assets/styles/app.scss +1 -0
- package/assets/styles/base/_basic.scss +10 -0
- package/assets/styles/base/_spacing.scss +29 -0
- package/assets/styles/global/_layout.scss +1 -1
- package/assets/styles/themes/_dark.scss +25 -0
- package/assets/styles/themes/_light.scss +65 -0
- package/assets/translations/en-us.yaml +322 -24
- package/assets/translations/zh-hans.yaml +8 -5
- package/components/Certificates.vue +5 -0
- package/components/FilterPanel.vue +156 -0
- package/components/{fleet/ForceDirectedTreeChart/index.vue → ForceDirectedTreeChart.vue} +47 -41
- package/components/IconOrSvg.vue +14 -35
- package/components/PromptRemove.vue +5 -1
- package/components/Resource/Detail/Card/PodsCard/Bubble.vue +13 -0
- package/components/Resource/Detail/Card/PodsCard/composable.ts +30 -0
- package/components/Resource/Detail/Card/PodsCard/index.vue +118 -0
- package/components/Resource/Detail/Card/ResourceUsageCard/composable.ts +51 -0
- package/components/Resource/Detail/Card/ResourceUsageCard/index.vue +79 -0
- package/components/Resource/Detail/Card/Scaler.vue +89 -0
- package/components/Resource/Detail/Card/StateCard/composables.ts +112 -0
- package/components/Resource/Detail/Card/StateCard/index.vue +39 -0
- package/components/Resource/Detail/Card/VerticalGap.vue +11 -0
- package/components/Resource/Detail/Card/__tests__/Card.test.ts +36 -0
- package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +84 -0
- package/components/Resource/Detail/Card/__tests__/ResourceUsageCard.test.ts +72 -0
- package/components/Resource/Detail/Card/__tests__/Scaler.test.ts +87 -0
- package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +53 -0
- package/components/Resource/Detail/Card/__tests__/VerticalGap.test.ts +14 -0
- package/components/Resource/Detail/Card/__tests__/index.test.ts +36 -0
- package/components/Resource/Detail/Card/index.vue +56 -0
- package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +19 -0
- package/components/Resource/Detail/Metadata/Annotations/composable.ts +12 -0
- package/components/Resource/Detail/Metadata/Annotations/index.vue +26 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +103 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +281 -0
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +111 -0
- package/components/Resource/Detail/Metadata/KeyValue.vue +130 -0
- package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +18 -0
- package/components/Resource/Detail/Metadata/Labels/composable.ts +12 -0
- package/components/Resource/Detail/Metadata/Labels/index.vue +27 -0
- package/components/Resource/Detail/Metadata/Rectangle.vue +32 -0
- package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +107 -0
- package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +24 -0
- package/components/Resource/Detail/Metadata/__tests__/index.test.ts +91 -0
- package/components/Resource/Detail/Metadata/composables.ts +29 -0
- package/components/Resource/Detail/Metadata/index.vue +66 -0
- package/components/Resource/Detail/Page.vue +22 -0
- package/components/Resource/Detail/PercentageBar.vue +40 -0
- package/components/Resource/Detail/ResourceRow.vue +119 -0
- package/components/Resource/Detail/SpacedRow.vue +14 -0
- package/components/Resource/Detail/StatusBar.vue +59 -0
- package/components/Resource/Detail/StatusRow.vue +61 -0
- package/components/Resource/Detail/TitleBar/Title.vue +13 -0
- package/components/Resource/Detail/TitleBar/Top.vue +14 -0
- package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +17 -0
- package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +17 -0
- package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +142 -0
- package/components/Resource/Detail/TitleBar/composable.ts +31 -0
- package/components/Resource/Detail/TitleBar/index.vue +124 -0
- package/components/Resource/Detail/Top/index.vue +34 -0
- package/components/Resource/Detail/__tests__/Page.test.ts +32 -0
- package/components/ResourceDetail/__tests__/index.test.ts +114 -0
- package/components/ResourceDetail/index.vue +64 -562
- package/components/ResourceDetail/legacy.vue +545 -0
- package/components/ResourceTable.vue +41 -7
- package/components/SlideInPanelManager.vue +76 -8
- package/components/SortableTable/index.vue +13 -2
- package/components/SortableTable/selection.js +21 -8
- package/components/StatusBadge.vue +6 -4
- package/components/SubtleLink.vue +25 -0
- package/components/Wizard.vue +12 -1
- package/components/YamlEditor.vue +1 -1
- package/components/__tests__/FilterPanel.test.ts +81 -0
- package/components/auth/AuthBanner.vue +2 -3
- package/components/auth/RoleDetailEdit.vue +45 -3
- package/components/auth/login/oidc.vue +6 -1
- package/components/fleet/FleetApplications.vue +181 -0
- package/components/fleet/FleetHelmOps.vue +115 -0
- package/components/fleet/FleetIntro.vue +58 -28
- package/components/fleet/FleetNoWorkspaces.vue +5 -1
- package/components/fleet/FleetOCIStorageSecret.vue +171 -0
- package/components/fleet/FleetRepos.vue +38 -76
- package/components/fleet/FleetResources.vue +50 -22
- package/components/fleet/FleetSummary.vue +26 -51
- package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +213 -0
- package/components/fleet/__tests__/FleetSummary.test.ts +39 -39
- package/components/fleet/dashboard/Empty.vue +73 -0
- package/components/fleet/dashboard/ResourceCard.vue +183 -0
- package/components/fleet/dashboard/ResourceCardSummary.vue +199 -0
- package/components/fleet/dashboard/ResourceDetails.vue +196 -0
- package/components/fleet/dashboard/ResourcePanel.vue +376 -0
- package/components/form/ArrayList.vue +6 -0
- package/components/form/SimpleSecretSelector.vue +8 -2
- package/components/form/ValueFromResource.vue +31 -19
- package/components/formatter/FleetApplicationClustersReady.vue +77 -0
- package/components/formatter/FleetApplicationSource.vue +71 -0
- package/components/formatter/FleetSummaryGraph.vue +7 -0
- package/components/nav/Header.vue +8 -7
- package/components/nav/TopLevelMenu.helper.ts +55 -34
- package/components/nav/TopLevelMenu.vue +11 -0
- package/components/nav/Type.vue +4 -1
- package/composables/useI18n.ts +12 -11
- package/config/labels-annotations.js +14 -11
- package/config/product/auth.js +1 -0
- package/config/product/fleet.js +70 -17
- package/config/query-params.js +3 -1
- package/config/roles.ts +1 -0
- package/config/router/routes.js +20 -2
- package/config/secret.ts +15 -0
- package/config/settings.ts +3 -2
- package/config/table-headers.js +52 -22
- package/config/types.js +2 -0
- package/core/plugin-helpers.ts +3 -2
- package/detail/fleet.cattle.io.cluster.vue +28 -15
- package/detail/fleet.cattle.io.gitrepo.vue +10 -1
- package/detail/fleet.cattle.io.helmop.vue +157 -0
- package/dialog/HelmOpForceUpdateDialog.vue +132 -0
- package/dialog/RedeployWorkloadDialog.vue +164 -0
- package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +56 -67
- package/edit/auth/oidc.vue +159 -93
- package/edit/fleet.cattle.io.gitrepo.vue +26 -33
- package/edit/fleet.cattle.io.helmop.vue +997 -0
- package/edit/management.cattle.io.fleetworkspace.vue +43 -10
- package/list/fleet.cattle.io.gitrepo.vue +1 -1
- package/list/fleet.cattle.io.helmop.vue +108 -0
- package/list/namespace.vue +5 -2
- package/mixins/auth-config.js +8 -1
- package/mixins/preset.js +100 -0
- package/mixins/resource-fetch-api-pagination.js +2 -0
- package/mixins/resource-fetch.js +1 -1
- package/mixins/resource-table-watch.js +45 -0
- package/models/__tests__/chart.test.ts +273 -0
- package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
- package/models/chart.js +144 -2
- package/models/fleet-application.js +385 -0
- package/models/fleet.cattle.io.bundle.js +9 -8
- package/models/fleet.cattle.io.gitrepo.js +41 -365
- package/models/fleet.cattle.io.helmop.js +228 -0
- package/models/management.cattle.io.authconfig.js +1 -0
- package/models/management.cattle.io.fleetworkspace.js +12 -0
- package/models/workload.js +14 -18
- package/package.json +2 -1
- package/pages/auth/verify.vue +13 -1
- package/pages/c/_cluster/apps/charts/AddRepoLink.vue +37 -0
- package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +80 -0
- package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +54 -0
- package/pages/c/_cluster/apps/charts/StatusLabel.vue +33 -0
- package/pages/c/_cluster/apps/charts/index.vue +302 -484
- package/pages/c/_cluster/explorer/EventsTable.vue +1 -1
- package/pages/c/_cluster/fleet/__tests__/index.test.ts +426 -0
- package/pages/c/_cluster/fleet/application/_resource/_id.vue +14 -0
- package/pages/c/_cluster/fleet/application/_resource/create.vue +14 -0
- package/pages/c/_cluster/fleet/application/create.vue +340 -0
- package/pages/c/_cluster/fleet/application/index.vue +139 -0
- package/pages/c/_cluster/fleet/graph/config.js +277 -0
- package/pages/c/_cluster/fleet/index.vue +772 -330
- package/pages/explorer/resource/detail/configmap.vue +19 -0
- package/plugins/dashboard-store/actions.js +31 -9
- package/plugins/dashboard-store/getters.js +34 -21
- package/plugins/dashboard-store/mutations.js +51 -7
- package/plugins/dashboard-store/resource-class.js +14 -2
- package/plugins/steve/__tests__/subscribe.spec.ts +66 -1
- package/plugins/steve/actions.js +3 -0
- package/plugins/steve/steve-pagination-utils.ts +14 -13
- package/plugins/steve/subscribe.js +229 -42
- package/rancher-components/BadgeState/BadgeState.vue +3 -1
- package/rancher-components/Form/Checkbox/Checkbox.vue +2 -2
- package/rancher-components/RcItemCard/RcItemCard.test.ts +189 -0
- package/rancher-components/RcItemCard/RcItemCard.vue +425 -0
- package/rancher-components/RcItemCard/RcItemCardAction.vue +24 -0
- package/rancher-components/RcItemCard/index.ts +2 -0
- package/store/auth.js +1 -0
- package/store/catalog.js +62 -24
- package/store/index.js +33 -14
- package/store/slideInPanel.ts +6 -0
- package/store/type-map.js +1 -0
- package/types/fleet.d.ts +35 -0
- package/types/resources/settings.d.ts +19 -1
- package/types/shell/index.d.ts +339 -272
- package/types/store/dashboard-store.types.ts +17 -3
- package/types/store/pagination.types.ts +6 -1
- package/types/store/subscribe.types.ts +50 -0
- package/utils/auth.js +32 -3
- package/utils/fleet-types.ts +0 -0
- package/utils/fleet.ts +200 -1
- package/utils/pagination-utils.ts +26 -1
- package/utils/pagination-wrapper.ts +132 -50
- package/utils/settings.ts +4 -1
- package/utils/style.ts +39 -0
- package/utils/validators/formRules/__tests__/index.test.ts +36 -3
- package/utils/validators/formRules/index.ts +10 -3
- package/utils/window.js +11 -7
- package/components/__tests__/ApplicationCard.test.ts +0 -27
- package/components/cards/ApplicationCard.vue +0 -145
- package/components/fleet/ForceDirectedTreeChart/chartIcons.js +0 -17
- package/config/secret.js +0 -14
- package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +0 -249
- /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
|
-
])('
|
|
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
|
|
15
|
-
|
|
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
|
|
19
|
+
get helmName() {
|
|
23
20
|
const labels = this.metadata?.labels || {};
|
|
24
21
|
|
|
25
|
-
return labels[FLEET_ANNOTATIONS.
|
|
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() {
|