diff --git a/shell/components/fleet/FleetRepos.vue b/shell/components/fleet/FleetRepos.vue
index 4fed19ad5f8..8ab7dd209df 100644
--- a/shell/components/fleet/FleetRepos.vue
+++ b/shell/components/fleet/FleetRepos.vue
@@ -6,17 +6,15 @@ import FleetIntro from '@shell/components/fleet/FleetIntro';
import {
AGE,
- STATE,
- NAME,
- FLEET_SUMMARY,
FLEET_REPO,
- FLEET_REPO_TARGET,
- FLEET_REPO_CLUSTERS_READY,
FLEET_REPO_CLUSTER_SUMMARY,
- FLEET_REPO_PER_CLUSTER_STATE
-
+ FLEET_REPO_CLUSTERS_READY,
+ FLEET_REPO_PER_CLUSTER_STATE,
+ FLEET_REPO_TARGET,
+ FLEET_SUMMARY,
+ NAME,
+ STATE,
} from '@shell/config/table-headers';
-import { STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
// i18n-ignore repoDisplay
export default {
@@ -77,31 +75,18 @@ export default {
headers() {
// Cluster summary is only shown in the cluster view
- const fleetClusterSummary = {
+ const summary = this.isClusterView ? [{
...FLEET_REPO_CLUSTER_SUMMARY,
- formatterOpts: {
- // Fleet uses labels to identify clusters
- clusterLabel: this.clusterId
- },
- };
+ formatterOpts: { clusterId: this.clusterId },
+ }] : [FLEET_REPO_CLUSTERS_READY, FLEET_SUMMARY];
// if hasPerClusterState then use the repo state
- const fleetPerClusterState = {
+ const state = this.isClusterView ? {
...FLEET_REPO_PER_CLUSTER_STATE,
- value: (row) => {
- const statePerCluster = row.clusterResourceStatus?.find((c) => {
- return c.clusterLabel === this.clusterId;
- });
-
- return statePerCluster ? statePerCluster?.status?.displayStatus : STATES_ENUM.ACTIVE;
- },
- };
+ value: (repo) => repo.clusterState(this.clusterId),
+ } : STATE;
- const summary = this.isClusterView ? [fleetClusterSummary] : [FLEET_REPO_CLUSTERS_READY, FLEET_SUMMARY];
-
- const state = this.isClusterView ? fleetPerClusterState : STATE;
-
- const out = [
+ return [
state,
NAME,
FLEET_REPO,
@@ -109,8 +94,6 @@ export default {
...summary,
AGE
];
-
- return out;
},
},
methods: {
diff --git a/shell/components/formatter/FleetClusterSummaryGraph.vue b/shell/components/formatter/FleetClusterSummaryGraph.vue
index 7fc03cbbd2c..ba5ea1a654f 100644
--- a/shell/components/formatter/FleetClusterSummaryGraph.vue
+++ b/shell/components/formatter/FleetClusterSummaryGraph.vue
@@ -11,7 +11,7 @@ export default {
required: true
},
- clusterLabel: {
+ clusterId: {
type: String,
required: true
}
@@ -22,6 +22,6 @@ export default {
diff --git a/shell/components/formatter/FleetSummaryGraph.vue b/shell/components/formatter/FleetSummaryGraph.vue
index 58d3b571d2b..f6dd595b935 100644
--- a/shell/components/formatter/FleetSummaryGraph.vue
+++ b/shell/components/formatter/FleetSummaryGraph.vue
@@ -14,7 +14,7 @@ export default {
required: true
},
- clusterLabel: {
+ clusterId: {
type: String,
required: false,
default: null,
@@ -23,10 +23,8 @@ export default {
computed: {
summary() {
- if (this.clusterLabel) {
- return this.row.clusterResourceStatus.find((x) => {
- return x.clusterLabel === this.clusterLabel;
- })?.status.resourceCounts || {};
+ if (this.clusterId) {
+ return this.row.statusResourceCountsForCluster(this.clusterId);
}
return this.row.status?.resourceCounts || {};
@@ -37,7 +35,8 @@ export default {
},
stateParts() {
- const keys = Object.keys(this.summary).filter((x) => !x.startsWith('desired'));
+ const summary = this.summary;
+ const keys = Object.keys(summary).filter((x) => !x.startsWith('desired'));
const out = keys.map((key) => {
const textColor = colorForState(key);
@@ -46,7 +45,7 @@ export default {
label: ucFirst(key),
color: textColor.replace(/text-/, 'bg-'),
textColor,
- value: this.summary[key],
+ value: summary[key],
sort: stateSort(textColor, key),
};
}).filter((x) => x.value > 0);
diff --git a/shell/detail/fleet.cattle.io.cluster.vue b/shell/detail/fleet.cattle.io.cluster.vue
index 46693240622..cca17db8ac2 100644
--- a/shell/detail/fleet.cattle.io.cluster.vue
+++ b/shell/detail/fleet.cattle.io.cluster.vue
@@ -29,11 +29,11 @@ export default {
},
async fetch() {
- const clusterId = this.value?.metadata?.labels[FLEET_LABELS.CLUSTER_NAME];
+ const managementClusterId = this.value?.metadata?.labels[FLEET_LABELS.CLUSTER_NAME];
const hash = await allHash({
rancherCluster: this.$store.dispatch('management/find', {
type: MANAGEMENT.CLUSTER,
- id: clusterId
+ id: managementClusterId
}),
repos: this.$store.dispatch('management/findAll', { type: FLEET.GIT_REPO }),
workspaces: this.$store.dispatch('management/findAll', { type: FLEET.WORKSPACE }),
@@ -53,7 +53,7 @@ export default {
return this.value.bundleDeployments;
},
clusterId() {
- return this.value?.metadata?.labels[FLEET_LABELS.CLUSTER_NAME];
+ return this.value.id;
},
repos() {
diff --git a/shell/models/fleet.cattle.io.bundle.js b/shell/models/fleet.cattle.io.bundle.js
index 9e3443e67f9..f2609e73740 100644
--- a/shell/models/fleet.cattle.io.bundle.js
+++ b/shell/models/fleet.cattle.io.bundle.js
@@ -6,17 +6,6 @@ import { FLEET } from '@shell/config/types';
import { convertSelectorObj, matching } from '@shell/utils/selector';
export default class FleetBundle extends SteveModel {
- get deploymentInfo() {
- const ready = this.status?.summary?.ready || 0;
- const total = this.status?.summary?.desiredReady || 0;
-
- return {
- ready,
- unready: total - ready,
- total
- };
- }
-
get lastUpdateTime() {
return this.status?.conditions?.[0].lastUpdateTime;
}
diff --git a/shell/models/fleet.cattle.io.gitrepo.js b/shell/models/fleet.cattle.io.gitrepo.js
index 247f36293af..02b34e3b6ba 100644
--- a/shell/models/fleet.cattle.io.gitrepo.js
+++ b/shell/models/fleet.cattle.io.gitrepo.js
@@ -7,7 +7,7 @@ import { addObject, addObjects, findBy, insertAt } from '@shell/utils/array';
import { set } from '@shell/utils/object';
import SteveModel from '@shell/plugins/steve/steve-class';
import {
- colorForState, mapStateToEnum, primaryDisplayStatusFromCount, stateDisplay, stateSort
+ colorForState, mapStateToEnum, primaryDisplayStatusFromCount, stateDisplay, STATES_ENUM, stateSort,
} from '@shell/plugins/dashboard-store/resource-class';
import { NAME } from '@shell/config/product/explorer';
import FleetUtils from '@shell/utils/fleet';
@@ -20,6 +20,21 @@ function quacksLikeAHash(str) {
return false;
}
+function normalizeStateCounts(data) {
+ if (!data || data === {}) {
+ return {
+ total: 0,
+ states: {},
+ };
+ }
+ const { desiredReady, ...rest } = data ;
+
+ return {
+ total: desiredReady,
+ states: rest,
+ };
+}
+
export default class GitRepo extends SteveModel {
applyDefaults() {
const spec = this.spec || {};
@@ -305,18 +320,7 @@ export default class GitRepo extends SteveModel {
}
get bundles() {
- const all = this.$getters['all'](FLEET.BUNDLE);
-
- return all.filter((bundle) => bundle.repoName === this.name &&
- bundle.namespace === this.namespace &&
- bundle.namespacedName.startsWith(`${ this.namespace }:${ this.name }`));
- }
-
- /**
- * Bundles with state of active
- */
- get bundlesReady() {
- return this.bundles?.filter((bundle) => bundle.state === 'active');
+ return this.$getters['matching'](FLEET.BUNDLE, { 'fleet.cattle.io/repo-name': this.name }, this.namespace);
}
get bundleDeployments() {
@@ -325,6 +329,37 @@ export default class GitRepo extends SteveModel {
return bds.filter((bd) => bd.metadata?.labels?.['fleet.cattle.io/repo-name'] === this.name);
}
+ get allBundlesStatuses() {
+ const { nonReadyResources, ...bundlesSummary } = this.status?.summary || {};
+
+ return normalizeStateCounts(bundlesSummary);
+ }
+
+ get allResourceStatuses() {
+ return normalizeStateCounts(this.status?.resourceCounts || {});
+ }
+
+ statusResourceCountsForCluster(clusterId) {
+ if (!this.targetClusters.some((c) => c.id === clusterId)) {
+ return {};
+ }
+
+ return this.bundleDeployments
+ .filter((bd) => FleetUtils.clusterIdFromBundleDeploymentLabels(bd.metadata?.labels) === clusterId)
+ .map((bd) => FleetUtils.resourcesFromBundleDeploymentStatus(bd.status))
+ .flat()
+ .map((r) => r.state)
+ .reduce((prev, state) => {
+ if (!prev[state]) {
+ prev[state] = 0;
+ }
+ prev[state]++;
+ prev.desiredReady++;
+
+ return prev;
+ }, { desiredReady: 0 });
+ }
+
get resourcesStatuses() {
const bundleDeployments = this.bundleDeployments || [];
const clusters = (this.targetClusters || []).reduce((res, c) => {
@@ -357,7 +392,7 @@ export default class GitRepo extends SteveModel {
name: `c-cluster-product-resource${ r.namespace ? '-namespace' : '' }-id`,
params: {
product: NAME,
- cluster: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME],
+ cluster: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME], // explorer uses the "management" Cluster name, which differs from the Fleet Cluster name
resource: type,
namespace: r.namespace,
id: r.name,
@@ -385,7 +420,6 @@ export default class GitRepo extends SteveModel {
creationTimestamp: r.createdAt,
// other properties
- clusterLabel: c.metadata.labels[FLEET_ANNOTATIONS.CLUSTER_NAME],
stateBackground: color,
stateDisplay: display,
stateSort: stateSort(color, display),
@@ -408,42 +442,10 @@ export default class GitRepo extends SteveModel {
};
}
- get clusterResourceStatus() {
- const clusterStatuses = this.resourcesStatuses.reduce((prev, curr) => {
- const { clusterId, clusterLabel, state } = curr;
-
- if (!prev[clusterId]) {
- prev[clusterId] = {
- clusterLabel,
- resourceCounts: { [state]: 0, desiredReady: 0 }
-
- };
- }
-
- if (!prev[clusterId].resourceCounts[state]) {
- prev[clusterId].resourceCounts[state] = 0;
- }
-
- prev[clusterId].resourceCounts[state] += 1;
- prev[clusterId].resourceCounts.desiredReady += 1;
-
- return prev;
- }, {});
-
- const values = Object.keys(clusterStatuses).map((key) => {
- const { clusterLabel, resourceCounts } = clusterStatuses[key];
-
- return {
- clusterId: key,
- clusterLabel, // FLEET LABEL
- status: {
- displayStatus: primaryDisplayStatusFromCount(resourceCounts),
- resourceCounts: { ...resourceCounts }
- }
- };
- });
+ clusterState(clusterId) {
+ const resourceCounts = this.statusResourceCountsForCluster(clusterId);
- return values;
+ return primaryDisplayStatusFromCount(resourceCounts) || STATES_ENUM.ACTIVE;
}
get clustersList() {
diff --git a/shell/pages/c/_cluster/fleet/index.vue b/shell/pages/c/_cluster/fleet/index.vue
index 87b26971672..2d176c6beaa 100644
--- a/shell/pages/c/_cluster/fleet/index.vue
+++ b/shell/pages/c/_cluster/fleet/index.vue
@@ -2,7 +2,12 @@
import { mapState } from 'vuex';
import { FLEET } from '@shell/config/types';
import { WORKSPACE } from '@shell/store/prefs';
-import { STATES_ENUM, STATES, getStateLabel } from '@shell/plugins/dashboard-store/resource-class';
+import {
+ getStateLabel,
+ primaryDisplayStatusFromCount,
+ STATES,
+ STATES_ENUM,
+} from '@shell/plugins/dashboard-store/resource-class';
import Loading from '@shell/components/Loading';
import CollapsibleCard from '@shell/components/CollapsibleCard.vue';
import ResourceTable from '@shell/components/ResourceTable';
@@ -41,6 +46,7 @@ export default {
inStoreType: 'management',
type: FLEET.BUNDLE,
opt: { excludeFields: ['metadata.managedFields', 'spec.resources'] },
+ skipWait: true,
},
gitRepos: {
inStoreType: 'management',
@@ -98,7 +104,6 @@ export default {
}
],
schema: {},
- allBundles: [],
gitRepos: [],
fleetWorkspacesData: [],
isCollapsed: {},
@@ -138,14 +143,24 @@ export default {
});
},
workspacesData() {
- return this.fleetWorkspaces.filter((ws) => ws.repos && ws.repos.length);
+ return this.fleetWorkspaces.filter((ws) => ws.counts.gitRepos > 0);
},
emptyWorkspaces() {
- return this.fleetWorkspaces.filter((ws) => !ws.repos || !ws.repos.length);
+ return this.fleetWorkspaces.filter((ws) => ws.counts.gitRepos === 0);
},
areAllCardsExpanded() {
return Object.keys(this.isCollapsed).every((key) => !this.isCollapsed[key]);
- }
+ },
+ gitReposCounts() {
+ return this.gitRepos.reduce((prev, gitRepo) => {
+ prev[gitRepo.id] = {
+ bundles: gitRepo.allBundlesStatuses,
+ resources: gitRepo.allResourceStatuses,
+ };
+
+ return prev;
+ }, {});
+ },
},
methods: {
setWorkspaceFilterAndLinkToGitRepo(value) {
@@ -170,96 +185,59 @@ export default {
return this.getBadgeClassAndIcon(area, row) || defaultStatusInfo;
},
getBadgeClassAndIcon(area, row) {
- let group;
-
if (!this.admissableAreas.includes(area)) {
return false;
}
+ let group;
+
if (area === 'clusters') {
- if (row.clusterInfo?.ready === row.clusterInfo?.total && row.clusterInfo?.ready) {
- return {
- badgeClass: STATES[STATES_ENUM.ACTIVE].color,
- icon: STATES[STATES_ENUM.ACTIVE].compoundIcon
- };
- }
- } else if (area === 'bundles') {
- group = row.bundles;
- } else if (area === 'resources') {
- group = row.status?.resources;
- }
+ const clusterInfo = row.clusterInfo;
+ const state = clusterInfo.ready === clusterInfo.total ? STATES_ENUM.ACTIVE : STATES_ENUM.NOT_READY;
- if (group?.length && group?.every((item) => item.state?.toLowerCase() === STATES_ENUM.ACTIVE)) {
return {
- badgeClass: STATES[STATES_ENUM.ACTIVE].color ? STATES[STATES_ENUM.ACTIVE].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
- icon: STATES[STATES_ENUM.ACTIVE].compoundIcon ? STATES[STATES_ENUM.ACTIVE].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
- };
- }
- if (group?.length && group?.some((item) => item.state?.toLowerCase() === STATES_ENUM.ERR_APPLIED)) {
- return {
- badgeClass: STATES[STATES_ENUM.ERR_APPLIED].color ? STATES[STATES_ENUM.ERR_APPLIED].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
- icon: STATES[STATES_ENUM.ERR_APPLIED].compoundIcon ? STATES[STATES_ENUM.ERR_APPLIED].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
+ badgeClass: `${ STATES[state].color } badge-class-area-${ area }`,
+ icon: STATES[state].compoundIcon
};
+ } else if (area === 'clusters' || area === 'bundles') {
+ group = row.allBundlesStatuses;
+ } else if (area === 'resources') {
+ group = row.allResourceStatuses;
+ } else {
+ // unreachable
+ return false;
}
- if (group?.length && group?.some((item) => item.state?.toLowerCase() === STATES_ENUM.NOT_READY)) {
+
+ if (group.total === group.states.ready) {
return {
- badgeClass: STATES[STATES_ENUM.NOT_READY].color ? STATES[STATES_ENUM.NOT_READY].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
- icon: STATES[STATES_ENUM.NOT_READY].compoundIcon ? STATES[STATES_ENUM.NOT_READY].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
+ badgeClass: STATES[STATES_ENUM.ACTIVE].color,
+ icon: STATES[STATES_ENUM.ACTIVE].compoundIcon,
};
}
-
- if (area === 'resources') {
- if (row.status?.resourceCounts?.desiredReady === row.status?.resourceCounts?.ready && row.status?.resourceCounts?.desiredReady) {
- return {
- badgeClass: STATES[STATES_ENUM.ACTIVE].color,
- icon: STATES[STATES_ENUM.ACTIVE].compoundIcon
- };
- }
- }
+ const state = primaryDisplayStatusFromCount(group.states);
return {
- badgeClass: `${ STATES[STATES_ENUM.NOT_READY].color } badge-class-area-${ area }`,
- icon: STATES[STATES_ENUM.NOT_READY].compoundIcon
+ badgeClass: STATES[state].color ? STATES[state].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
+ icon: STATES[state].compoundIcon ? STATES[state].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
};
},
- getTooltipInfo(area, row) {
- let group;
-
+ getTooltipInfo(area, row, rowCounts) {
if (!this.admissableAreas.includes(area)) {
return {};
}
- if (area === 'clusters') {
- group = '';
- } else if (area === 'bundles') {
- group = row.bundles;
+ if (area === 'bundles') {
+ return this.generateTooltipData(rowCounts[row.id].bundles.states);
} else if (area === 'resources') {
- group = row.status?.resources;
- }
-
- if (group?.length) {
- return this.generateTooltipData(group);
+ return this.generateTooltipData(rowCounts[row.id].resources.states);
}
return '';
},
- generateTooltipData(data) {
- const infoObj = {};
- let tooltipData = '';
-
- data.forEach((item) => {
- if (!infoObj[item.state]) {
- infoObj[item.state] = 0;
- }
-
- infoObj[item.state]++;
- });
-
- Object.keys(infoObj).forEach((key) => {
- tooltipData += `${ getStateLabel(key) }: ${ infoObj[key] }
`;
- });
-
- return tooltipData;
+ generateTooltipData(infoObj) {
+ return Object.keys(infoObj)
+ .filter((key) => infoObj[key] > 0) // filter zero values
+ .map((key) => `${ getStateLabel(key) }: ${ infoObj[key] }
`).join('');
},
getBadgeValue(area, row) {
let value;
@@ -269,11 +247,15 @@ export default {
}
if (area === 'clusters') {
- return `${ row.clusterInfo.ready }/${ row.clusterInfo.total }`;
+ value = `${ row.clusterInfo.ready }/${ row.clusterInfo.total }`;
} else if (area === 'bundles') {
- value = xOfy(row.bundlesReady?.length, row.bundles?.length);
+ const bundles = row.allBundlesStatuses;
+
+ value = xOfy(bundles.states.ready || 0, bundles.total);
} else if (area === 'resources') {
- value = xOfy(row.status?.resourceCounts?.ready, row.status?.resourceCounts?.desiredReady);
+ const resources = row.allResourceStatuses;
+
+ value = xOfy(resources.states.ready || 0, resources.total);
}
return value;
@@ -413,7 +395,7 @@ export default {
{
const validSchema = value.schemaValidator ? value.schemaValidator(schema) : !!schema;
if (validSchema) {
- hash[key] = store.dispatch(`${ value.inStoreType }/findAll`, { type: value.type, opt: value.opt } );
+ const res = store.dispatch(`${ value.inStoreType }/findAll`, { type: value.type, opt: value.opt } );
+
+ if (!value.skipWait) {
+ hash[key] = res;
+ }
}
}