Skip to content

Commit

Permalink
Display status information for Deployments in Application Explorer tree
Browse files Browse the repository at this point in the history
#3823

Fixes: #3823

Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
  • Loading branch information
vrubezhny committed May 28, 2024
1 parent 3ff60c2 commit 8c48ded
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 9 deletions.
Binary file added images/context/component-node-green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/context/component-node-red.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/context/component-node-yellow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
127 changes: 118 additions & 9 deletions src/explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ export interface DeploymentPodObject extends KubernetesObject {
},
}

export interface DeploymentPodErrors {
inCrashLoopBackOff: boolean,
messages: string[]
}

type PackageJSON = {
version: string;
bugs: string;
Expand Down Expand Up @@ -270,24 +275,128 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
collapsibleState: TreeItemCollapsibleState.Collapsed
}
}
const routeURL = await Oc.Instance.getRouteURL(element.metadata.name);
return {
return this.getDeploymentItem(element);
}
return {
label: 'Unknown element'
}
}

private getDeploymentIconSuffix(pods: DeploymentPodObject[]): string {
// Find all not 'Running' pods
const notRunning = pods.filter((pod) => pod.status && pod.status.phase !== `Running`);

Check failure on line 287 in src/explorer.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest, 20)

Strings must use singlequote
if (notRunning === undefined) {
return '-green'; // All running - return 'green'
}
// Find any 'Failed' or 'Unknown' pod - if any return error ('red')
const failed = notRunning.find((pod) => pod.status &&
(pod.status.phase === 'Failed' || pod.status.phase === 'Unknown'));
if (failed) {
return '-red'; // At least one failed or unknown - return 'red'
}
// Find any 'Pending' pod - if any return pending ('yellow')
const pending = notRunning.find((pod) => pod.status && pod.status.phase === 'Pending');
return pending ? '-yellow' : '';
}

private collectDeploymentPodErrors(pods): DeploymentPodErrors {
let inCrashLoopBackOff = false;
let messages: string[] = [];

Check failure on line 304 in src/explorer.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest, 20)

'messages' is never reassigned. Use 'const' instead

// Search for 'CrashLoopBackOff' condition and errors in pods
pods.forEach((pod) => {
pod.status.conditions.filter((c) => c.status === 'False')
.forEach((c) => {
inCrashLoopBackOff = inCrashLoopBackOff || c.reason === 'CrashLoopBackOff';
const message = `${c.reason}: ${c.message}`;
// Skip duplicates and show not more than 10 errors
if (messages.length <= 10 && !(messages.find((m) => m === message))) {
messages.push(message);
}
});
});

return {
inCrashLoopBackOff,
messages
}
}

private collectDeploymentErrors(deployment): DeploymentPodErrors {
let inCrashLoopBackOff = false;
let messages: string[] = [];

Check failure on line 327 in src/explorer.ts

View workflow job for this annotation

GitHub Actions / build (macos-latest, 20)

'messages' is never reassigned. Use 'const' instead

// Search for 'CrashLoopBackOff' condition and errors in pods

deployment.status.conditions.filter((c) => c.status === 'False')
.forEach((c) => {
inCrashLoopBackOff = inCrashLoopBackOff || c.reason === 'CrashLoopBackOff';
const message = `${c.reason}: ${c.message}`;
// Skip duplicates and show not more than 10 errors
if (messages.length <= 10 && !(messages.find((m) => m === message))) {
messages.push(message);
}
});

return {
inCrashLoopBackOff,
messages
}
}

private async getDeploymentItem(element): Promise<TreeItem> {
const shouldHaveReplicas = element.spec.replicas > 0;
const availableReplicas = element.status.availableReplicas ? element.status.availableReplicas :
element.spec.replicas ? element.spec.replicas : 0;
const replicas = element.status.replicas ? element.status.replicas : 0;
let pods: DeploymentPodObject[] = [];
if (shouldHaveReplicas) {
try {
pods = await this.getPods(element);
} catch {
// ignore
}
}

// We get Deployment's 'CrashLoopBackOff` status and error messages
const deploymentErrors = this.collectDeploymentErrors(element);
// We get Pods' 'CrashLoopBackOff` status (not using error messages)
const podErrors = this.collectDeploymentPodErrors(pods);

let errorMessages = '';
deploymentErrors.messages.forEach((m) => errorMessages = errorMessages.concat(`\n\t${m}`));

// const inCrashLoopBackOff = element.status.conditions.find((condition) => condition.status === 'False' && condition.reason === 'CrashLoopBackOff');
let description = `${this.makeCaps(element.kind)}`;
let tooltip = description;
if (element.kind === 'Deployment') {
description = `${description} (${replicas}/${availableReplicas})`
tooltip = `${tooltip}: ${element.metadata.name}\n`.concat(
`Available replicas: ${availableReplicas}\n`,
`Replicas: ${replicas}\n`,
`CrashLoopBackOff detected: ${podErrors.inCrashLoopBackOff || deploymentErrors.inCrashLoopBackOff ? 'Yes' : 'No'}\n`,
errorMessages.length > 0 ? `Pod Fail Statuses:${errorMessages}` : ''
);
}
const iconSuffix = !shouldHaveReplicas ? '' : this.getDeploymentIconSuffix(pods);
const iconPath = element.kind === 'Deployment' || element.kind === 'DeploymentConfig' ?
path.resolve(__dirname, `../../images/context/component-node${iconSuffix}.png`)
: undefined;

const routeURL = await Oc.Instance.getRouteURL(element.metadata.name);
return {
contextValue: `openshift.k8sObject.${element.kind}${routeURL ? '.route' : ''}`,
label: element.metadata.name,
description: `${element.kind.substring(0, 1).toLocaleUpperCase()}${element.kind.substring(1)}`,
description,
tooltip,
collapsibleState: element.kind === 'Deployment' ? TreeItemCollapsibleState.Collapsed : TreeItemCollapsibleState.None,
iconPath: element.kind === 'Deployment' || element.kind === 'DeploymentConfig' ? path.resolve(__dirname, '../../images/context/component-node.png') : undefined,
iconPath,
command: {
title: 'Load',
command: 'openshift.resource.load',
arguments: [element]
}
};

}
return {
label: 'Unknown element'
}
}

private makeCaps(kind: string): string {
Expand Down

0 comments on commit 8c48ded

Please sign in to comment.