Skip to content

Add uptime value in Camel App Detail tab #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions locales/en/plugin__camel-openshift-console-plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
{
"C": "C",
"{{count}} day": "{{count}} day",
"{{count}} day_plural": "{{count}} day",
"{{count}} hour": "{{count}} hour",
"{{count}} hour_plural": "{{count}} hour",
"{{count}} minute": "{{count}} minute",
"{{count}} minute_plural": "{{count}} minute",
"Camel": "Camel",
"Camel App Details": "Camel App Details",
"Camel Details": "Camel Details",
"Camel Application": "Camel Application",
"Camel Applications": "Camel Applications",
"Camel Version": "Camel Version",
"Details": "Details",
"Endpoints": "Endpoints",
Expand All @@ -11,6 +17,7 @@
"Health": "Health",
"Image": "Image",
"Internal IP": "Internal IP",
"Just now": "Just now",
"Kind": "Kind",
"Location:": "Location:",
"Metrics": "Metrics",
Expand All @@ -30,5 +37,7 @@
"succeed": "succeed",
"total": "total",
"unknown host": "unknown host",
"Uptime": "Uptime",
"View Hawtio": "View Hawtio",
"View Logs": "View Logs"
}
8 changes: 8 additions & 0 deletions src/components/camel-app-details/CamelAppStatusPod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { podGVK } from '../../const';
import { ResourceLink, ResourceStatus } from '@openshift-console/dynamic-plugin-sdk';
import { useTranslation } from 'react-i18next';
import Status from '@openshift-console/dynamic-plugin-sdk/lib/app/components/status/Status';
import { formatDuration } from '../../date-utils';

type CamelAppStatusPodProps = {
obj: CamelAppKind;
Expand All @@ -23,6 +24,9 @@ type CamelAppStatusPodProps = {
const CamelAppStatusPod: React.FC<CamelAppStatusPodProps> = ({ obj: camelInt, pod: camelPod }) => {
const { t } = useTranslation('plugin__camel-openshift-console-plugin');

// Golang time.Time is in nanoseconds
const durationFull = formatDuration(Number(camelPod.uptime) / 1000000, { omitSuffix: false });

return (
<>
<Card>
Expand All @@ -48,6 +52,10 @@ const CamelAppStatusPod: React.FC<CamelAppStatusPodProps> = ({ obj: camelInt, po
<DescriptionListTerm>{t('Internal IP')}:</DescriptionListTerm>
<DescriptionListDescription>{camelPod.internalIp}</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>{t('Uptime')}:</DescriptionListTerm>
<DescriptionListDescription>{durationFull}</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>{t('Runtime')}:</DescriptionListTerm>
<DescriptionListDescription>
Expand Down
10 changes: 7 additions & 3 deletions src/components/camel-app-resources/CamelAppPods.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { K8sResourceKind, ResourceLink } from '@openshift-console/dynamic-plugin
import { podGVK } from '../../const';
import Status from '@openshift-console/dynamic-plugin-sdk/lib/app/components/status/Status';
import { useCamelAppPods } from './useCamelAppResources';
import { getPodStatus } from '../../utils';
import { getPodStatus } from './podStatus';
import { useTranslation } from 'react-i18next';
import { isHawtioEnabled, useHawtioConsolePlugin } from './useHawtio';

Expand Down Expand Up @@ -82,7 +82,9 @@ const CamelAppPods: React.FC<CamelAppPodsProps> = ({ obj: camelInt }) => {
<>
<span className="col-xs-2 text-right">
<TextContent>
<a href={`/k8s/ns/${camelInt.metadata.namespace}/pods/${resource.name}/logs`}>
<a
href={`/k8s/ns/${camelInt.metadata.namespace}/pods/${resource.name}/logs`}
>
{t('View Logs')}
</a>
</TextContent>
Expand All @@ -100,7 +102,9 @@ const CamelAppPods: React.FC<CamelAppPodsProps> = ({ obj: camelInt }) => {
) : (
<span className="col-xs-4 text-right">
<TextContent>
<a href={`/k8s/ns/${camelInt.metadata.namespace}/pods/${resource.name}/logs`}>
<a
href={`/k8s/ns/${camelInt.metadata.namespace}/pods/${resource.name}/logs`}
>
{t('View Logs')}
</a>
</TextContent>
Expand Down
80 changes: 80 additions & 0 deletions src/components/camel-app-resources/podStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as _ from 'lodash';

const isContainerFailedFilter = (containerStatus) => {
return containerStatus.state.terminated && containerStatus.state.terminated.exitCode !== 0;
};

export const isContainerLoopingFilter = (containerStatus) => {
return (
containerStatus.state.waiting && containerStatus.state.waiting.reason === 'CrashLoopBackOff'
);
};

const numContainersReadyFilter = (pod) => {
const {
status: { containerStatuses },
} = pod;
let numReady = 0;
_.forEach(containerStatuses, (status) => {
if (status.ready) {
numReady++;
}
});
return numReady;
};

const isReady = (pod) => {
const {
spec: { containers },
} = pod;
const numReady = numContainersReadyFilter(pod);
const total = _.size(containers);

return numReady === total;
};

const podWarnings = (pod) => {
const {
status: { phase, containerStatuses },
} = pod;
if (phase === 'Running' && containerStatuses) {
return _.map(containerStatuses, (containerStatus) => {
if (!containerStatus.state) {
return null;
}

if (isContainerFailedFilter(containerStatus)) {
if (_.has(pod, ['metadata', 'deletionTimestamp'])) {
return 'Failed';
}
return 'Warning';
}
if (isContainerLoopingFilter(containerStatus)) {
return 'CrashLoopBackOff';
}
return null;
}).filter((x) => x);
}
return null;
};

export const getPodStatus = (pod) => {
if (_.has(pod, ['metadata', 'deletionTimestamp'])) {
return 'Terminating';
}
const warnings = podWarnings(pod);
if (warnings !== null && warnings.length) {
if (warnings.includes('CrashLoopBackOff')) {
return 'CrashLoopBackOff';
}
if (warnings.includes('Failed')) {
return 'Failed';
}
return 'Warning';
}
const phase = _.get(pod, ['status', 'phase'], 'Unknown');
if (phase === 'Running' && !isReady(pod)) {
return 'NotReady';
}
return phase;
};
16 changes: 1 addition & 15 deletions src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,6 @@ import { K8sGroupVersionKind } from '@openshift-console/dynamic-plugin-sdk';

export const HAWTIO_CONSOLE_PLUGIN_NAME = 'hawtio-online-console-plugin';

export const METADATA_LABEL_SELECTOR_CAMEL_APP_KEY = 'camel/integration-runtime';
export const METADATA_LABEL_SELECTOR_CAMEL_APP_VALUE = 'camel';

export const METADATA_ANNOTATION_APP_VERSION = 'app.kubernetes.io/version';

export const METADATA_ANNOTATION_CAMEL_VERSION = 'camel/camel-core-version';

export const METADATA_ANNOTATION_CAMEL_QUARKUS_PLATFORM_VERSION = 'camel/quarkus-platform';
export const METADATA_ANNOTATION_CAMEL_CEQ_VERSION = 'camel/camel-quarkus';

export const METADATA_ANNOTATION_QUARKUS_BUILD_TIMESTAMP = 'app.quarkus.io/build-timestamp';

export const METADATA_ANNOTATION_CAMEL_SPRINGBOOT_VERSION = 'camel/spring-boot-version';
export const METADATA_ANNOTATION_CAMEL_CSB_VERSION = 'camel/camel-spring-boot-version';

export const camelAppGVK: K8sGroupVersionKind = {
group: 'camel.apache.org',
version: 'v1alpha1',
Expand Down Expand Up @@ -83,3 +68,4 @@ export const consolePluginGVK: K8sGroupVersionKind = {
};

export const ALL_NAMESPACES_KEY = '#ALL_NS#';
export const LAST_LANGUAGE_LOCAL_STORAGE_KEY = 'bridge/last-language';
89 changes: 89 additions & 0 deletions src/date-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useTranslation } from 'react-i18next';
import { getLastLanguage } from './utils';

export type Duration = {
days: number;
hours: number;
minutes: number;
seconds: number;
};

export const relativeTimeFormatter = (langArg: string) =>
Intl.RelativeTimeFormat ? new Intl.RelativeTimeFormat(langArg) : null;

export const dateTimeFormatter = (langArg: string) =>
new Intl.DateTimeFormat(langArg, {
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
year: 'numeric',
});

function getDuration(ms: number): Duration {
let seconds = Math.floor(ms / 1000);
let minutes = Math.floor(seconds / 60);
seconds %= 60;
let hours = Math.floor(minutes / 60);
minutes %= 60;
const days = Math.floor(hours / 24);
hours %= 24;
return { days, hours, minutes, seconds };
}

export const formatDuration = (ms: number, options?) => {
const { t } = useTranslation('plugin__camel-openshift-console-plugin');
const langArg = getLastLanguage();
const duration = getDuration(ms);

// Check for null. If dateTime is null, it returns incorrect date Jan 1 1970.
if (!duration) {
return '-';
}

const d = new Date(ms);
const justNow = t('Just now');

// If the event occurred less than one minute in the future, assume it's clock drift and show "Just now."
if (!options?.omitSuffix && ms < 60000 && ms > -60000) {
return justNow;
}

// Do not attempt to handle other dates in the future.
if (ms < 0) {
return '-';
}

const { days, hours, minutes } = getDuration(ms);

if (options?.omitSuffix) {
if (days) {
return t('{{count}} day', { count: days });
}
if (hours) {
return t('{{count}} hour', { count: hours });
}
return t('{{count}} minute', { count: minutes });
}

// Fallback to normal date/time formatting if Intl.RelativeTimeFormat is not
// available. This is the case for older Safari versions.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat#browser_compatibility
if (!relativeTimeFormatter(langArg)) {
return dateTimeFormatter(langArg).format(d);
}

if (!days && !hours && !minutes) {
return justNow;
}

if (days) {
return relativeTimeFormatter(langArg).format(-days, 'day');
}

if (hours) {
return relativeTimeFormatter(langArg).format(-hours, 'hour');
}

return relativeTimeFormatter(langArg).format(-minutes, 'minute');
};
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export type CamelAppKind = K8sResourceKind & {
export type CamelAppStatusPod = {
name: string;
internalIp: string;
/** Format: date-time - in nanoseconds */
uptime: string;
observe: CamelAppObservability;
ready: boolean;
runtime: CamelAppRuntime;
Expand Down
Loading
Loading