diff --git a/app/kubernetes/components/datatables/events-datatable/eventsDatatable.html b/app/kubernetes/components/datatables/events-datatable/eventsDatatable.html
deleted file mode 100644
index e68ca35ec..000000000
--- a/app/kubernetes/components/datatables/events-datatable/eventsDatatable.html
+++ /dev/null
@@ -1,150 +0,0 @@
-
-
-
-
-
-
-
-
- |
-
-
- |
-
-
- |
-
-
- |
-
-
-
-
- {{ item.Date | getisodate }} |
- {{ item.Involved.kind }} |
- {{ item.Type }} |
- {{ item.Message }} |
-
-
- Loading... |
-
-
- No event available. |
-
-
-
-
-
-
diff --git a/app/kubernetes/components/datatables/events-datatable/eventsDatatable.js b/app/kubernetes/components/datatables/events-datatable/eventsDatatable.js
deleted file mode 100644
index 09a99976e..000000000
--- a/app/kubernetes/components/datatables/events-datatable/eventsDatatable.js
+++ /dev/null
@@ -1,13 +0,0 @@
-angular.module('portainer.kubernetes').component('kubernetesEventsDatatable', {
- templateUrl: './eventsDatatable.html',
- controller: 'GenericDatatableController',
- bindings: {
- titleText: '@',
- dataset: '<',
- tableKey: '@',
- orderBy: '@',
- reverseOrder: '<',
- loading: '<',
- refreshCallback: '<',
- },
-});
diff --git a/app/kubernetes/react/components/clusterManagement.ts b/app/kubernetes/react/components/clusterManagement.ts
index 72440fc7a..8132ea80e 100644
--- a/app/kubernetes/react/components/clusterManagement.ts
+++ b/app/kubernetes/react/components/clusterManagement.ts
@@ -4,6 +4,8 @@ import { r2a } from '@/react-tools/react2angular';
import { withUIRouter } from '@/react-tools/withUIRouter';
import { withCurrentUser } from '@/react-tools/withCurrentUser';
import { NodeApplicationsDatatable } from '@/react/kubernetes/cluster/NodeView/NodeApplicationsDatatable/NodeApplicationsDatatable';
+import { ResourceEventsDatatable } from '@/react/kubernetes/components/EventsDatatable/ResourceEventsDatatable';
+import { withReactQuery } from '@/react-tools/withReactQuery';
export const clusterManagementModule = angular
.module('portainer.kubernetes.react.components.clusterManagement', [])
@@ -14,4 +16,11 @@ export const clusterManagementModule = angular
'isLoading',
'onRefresh',
])
+ )
+ .component(
+ 'resourceEventsDatatable',
+ r2a(
+ withUIRouter(withReactQuery(withCurrentUser(ResourceEventsDatatable))),
+ ['resourceId', 'storageKey', 'namespace']
+ )
).name;
diff --git a/app/kubernetes/views/cluster/node/node.html b/app/kubernetes/views/cluster/node/node.html
index 7055f0d6b..6610aa8ae 100644
--- a/app/kubernetes/views/cluster/node/node.html
+++ b/app/kubernetes/views/cluster/node/node.html
@@ -251,16 +251,7 @@
{{ ctrl.state.eventWarningCount }} warning(s)
-
-
+
diff --git a/app/kubernetes/views/cluster/node/nodeController.js b/app/kubernetes/views/cluster/node/nodeController.js
index 2c4b3daa4..db392d282 100644
--- a/app/kubernetes/views/cluster/node/nodeController.js
+++ b/app/kubernetes/views/cluster/node/nodeController.js
@@ -320,7 +320,7 @@ class KubernetesNodeController {
try {
this.state.eventsLoading = true;
const events = await this.KubernetesEventService.get();
- this.events = events.filter((item) => item.Involved.kind === 'Node');
+ this.events = events.filter((item) => item.Involved.uid === this.node.Id);
this.state.eventWarningCount = KubernetesEventHelper.warningCount(this.events);
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve node events');
diff --git a/app/kubernetes/views/configurations/configmap/edit/configMap.html b/app/kubernetes/views/configurations/configmap/edit/configMap.html
index fdd9b1151..2fa61285d 100644
--- a/app/kubernetes/views/configurations/configmap/edit/configMap.html
+++ b/app/kubernetes/views/configurations/configmap/edit/configMap.html
@@ -55,17 +55,11 @@
{{ ctrl.state.eventWarningCount }} warning(s)
-
-
+
diff --git a/app/kubernetes/views/configurations/secret/edit/secret.html b/app/kubernetes/views/configurations/secret/edit/secret.html
index e0c0d43b5..856e59846 100644
--- a/app/kubernetes/views/configurations/secret/edit/secret.html
+++ b/app/kubernetes/views/configurations/secret/edit/secret.html
@@ -62,17 +62,11 @@
{{ ctrl.state.eventWarningCount }} warning(s)
-
-
+
diff --git a/app/kubernetes/views/resource-pools/edit/resourcePool.html b/app/kubernetes/views/resource-pools/edit/resourcePool.html
index f893c72ff..134f6d6e9 100644
--- a/app/kubernetes/views/resource-pools/edit/resourcePool.html
+++ b/app/kubernetes/views/resource-pools/edit/resourcePool.html
@@ -286,15 +286,7 @@
{{ ctrl.state.eventWarningCount }} warning(s)
-
+
YAML
diff --git a/app/kubernetes/views/volumes/edit/volume.html b/app/kubernetes/views/volumes/edit/volume.html
index 2da384fe6..7134016f4 100644
--- a/app/kubernetes/views/volumes/edit/volume.html
+++ b/app/kubernetes/views/volumes/edit/volume.html
@@ -156,17 +156,12 @@
{{ ctrl.state.eventWarningCount }} warning(s)
-
-
+
diff --git a/app/react/kubernetes/applications/DetailsView/ApplicationDetailsView.tsx b/app/react/kubernetes/applications/DetailsView/ApplicationDetailsView.tsx
index 918bf0091..5f343c29d 100644
--- a/app/react/kubernetes/applications/DetailsView/ApplicationDetailsView.tsx
+++ b/app/react/kubernetes/applications/DetailsView/ApplicationDetailsView.tsx
@@ -8,7 +8,7 @@ import { Tab, WidgetTabs, findSelectedTabIndex } from '@@/Widget/WidgetTabs';
import { Icon } from '@@/Icon';
import { Badge } from '@@/Badge';
-import { EventsDatatable } from '../../components/KubernetesEventsDatatable';
+import { EventsDatatable } from '../../components/EventsDatatable';
import {
PlacementsDatatable,
diff --git a/app/react/kubernetes/applications/DetailsView/ApplicationEventsDatatable.tsx b/app/react/kubernetes/applications/DetailsView/ApplicationEventsDatatable.tsx
index 2f36d7934..f2046d55c 100644
--- a/app/react/kubernetes/applications/DetailsView/ApplicationEventsDatatable.tsx
+++ b/app/react/kubernetes/applications/DetailsView/ApplicationEventsDatatable.tsx
@@ -2,6 +2,7 @@ import { useCurrentStateAndParams } from '@uirouter/react';
import { useMemo } from 'react';
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
+import { EnvironmentId } from '@/react/portainer/environments/types';
import { useTableState } from '@@/datatables/useTableState';
@@ -10,9 +11,9 @@ import {
useApplicationPods,
useApplicationServices,
} from '../application.queries';
-import { EventsDatatable } from '../../components/KubernetesEventsDatatable';
-
-import { useNamespaceEventsQuery } from './useNamespaceEventsQuery';
+import { EventsDatatable } from '../../components/EventsDatatable';
+import { useEvents } from '../../queries/useEvents';
+import { AppKind } from '../types';
const storageKey = 'k8sAppEventsDatatable';
const settingsStore = createStore(storageKey, { id: 'Date', desc: true });
@@ -23,41 +24,73 @@ export function ApplicationEventsDatatable() {
params: {
namespace,
name,
- 'resource-type': resourceType,
+ 'resource-type': appKind,
endpointId: environmentId,
},
} = useCurrentStateAndParams();
+ const { relatedEvents, isInitialLoading } = useApplicationEvents(
+ environmentId,
+ namespace,
+ name,
+ appKind,
+ {
+ autoRefreshRate: tableState.autoRefreshRate,
+ }
+ );
+
+ return (
+
+ );
+}
+
+export function useApplicationEvents(
+ environmentId: EnvironmentId,
+ namespace: string,
+ name: string,
+ appKind?: AppKind,
+ options?: { autoRefreshRate?: number; yaml?: boolean }
+) {
const { data: application, ...applicationQuery } = useApplication(
environmentId,
namespace,
name,
- resourceType
+ appKind
);
- const { data: services, ...servicesQuery } = useApplicationServices(
+ const servicesQuery = useApplicationServices(
environmentId,
namespace,
name,
application
);
- const { data: pods, ...podsQuery } = useApplicationPods(
+ const podsQuery = useApplicationPods(
environmentId,
namespace,
name,
application
);
- const { data: events, ...eventsQuery } = useNamespaceEventsQuery(
- environmentId,
+
+ const { data: events, ...eventsQuery } = useEvents(environmentId, {
namespace,
- {
- autoRefreshRate: tableState.autoRefreshRate * 1000,
- }
- );
+ queryOptions: {
+ autoRefreshRate: options?.autoRefreshRate
+ ? options.autoRefreshRate * 1000
+ : undefined,
+ },
+ });
// related events are events that have the application id, or the id of a service or pod from the application
const relatedEvents = useMemo(() => {
- const serviceIds = services?.map((service) => service?.metadata?.uid);
- const podIds = pods?.map((pod) => pod?.metadata?.uid);
+ const serviceIds = servicesQuery.data?.map(
+ (service) => service?.metadata?.uid
+ );
+ const podIds = podsQuery.data?.map((pod) => pod?.metadata?.uid);
return (
events?.filter(
(event) =>
@@ -66,20 +99,13 @@ export function ApplicationEventsDatatable() {
podIds?.includes(event.involvedObject.uid)
) || []
);
- }, [application?.metadata?.uid, events, pods, services]);
+ }, [application?.metadata?.uid, events, podsQuery.data, servicesQuery.data]);
- return (
-
- );
+ const isInitialLoading =
+ applicationQuery.isInitialLoading ||
+ servicesQuery.isInitialLoading ||
+ podsQuery.isInitialLoading ||
+ eventsQuery.isInitialLoading;
+
+ return { relatedEvents, isInitialLoading };
}
diff --git a/app/react/kubernetes/applications/DetailsView/useApplicationEventsTableData.tsx b/app/react/kubernetes/applications/DetailsView/useApplicationEventsTableData.tsx
index 1c2ab95e5..9ab709b5d 100644
--- a/app/react/kubernetes/applications/DetailsView/useApplicationEventsTableData.tsx
+++ b/app/react/kubernetes/applications/DetailsView/useApplicationEventsTableData.tsx
@@ -10,8 +10,7 @@ import {
useApplicationPods,
useApplicationServices,
} from '../application.queries';
-
-import { useNamespaceEventsQuery } from './useNamespaceEventsQuery';
+import { useEvents } from '../../queries/useEvents';
const storageKey = 'k8sAppEventsDatatable';
const settingsStore = createStore(storageKey, { id: 'Date', desc: true });
@@ -49,13 +48,12 @@ export function useApplicationEventsTableData() {
name,
application
);
- const { data: events, ...eventsQuery } = useNamespaceEventsQuery(
- environmentId,
+ const { data: events, ...eventsQuery } = useEvents(environmentId, {
namespace,
- {
+ queryOptions: {
autoRefreshRate: appEventsTableState.autoRefreshRate * 1000,
- }
- );
+ },
+ });
// related events are events that have the application id, or the id of a service or pod from the application
const appEventsData = useMemo(() => {
diff --git a/app/react/kubernetes/applications/DetailsView/useNamespaceEventsQuery.ts b/app/react/kubernetes/applications/DetailsView/useNamespaceEventsQuery.ts
deleted file mode 100644
index 56a711b77..000000000
--- a/app/react/kubernetes/applications/DetailsView/useNamespaceEventsQuery.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { EventList } from 'kubernetes-types/core/v1';
-import { useQuery } from '@tanstack/react-query';
-
-import { EnvironmentId } from '@/react/portainer/environments/types';
-import axios from '@/portainer/services/axios';
-import { withError } from '@/react-tools/react-query';
-
-import { parseKubernetesAxiosError } from '../../axiosError';
-
-async function getNamespaceEvents(
- environmentId: EnvironmentId,
- namespace: string,
- labelSelector?: string
-) {
- try {
- const { data } = await axios.get(
- `/endpoints/${environmentId}/kubernetes/api/v1/namespaces/${namespace}/events`,
- {
- params: {
- labelSelector,
- },
- }
- );
- return data.items;
- } catch (e) {
- throw parseKubernetesAxiosError(e, 'Unable to retrieve events');
- }
-}
-
-export function useNamespaceEventsQuery(
- environmentId: EnvironmentId,
- namespace: string,
- options?: { autoRefreshRate?: number },
- labelSelector?: string
-) {
- return useQuery(
- [
- 'environments',
- environmentId,
- 'kubernetes',
- 'events',
- namespace,
- labelSelector,
- ],
- () => getNamespaceEvents(environmentId, namespace, labelSelector),
- {
- ...withError('Unable to retrieve events'),
- refetchInterval() {
- return options?.autoRefreshRate ?? false;
- },
- }
- );
-}
diff --git a/app/react/kubernetes/components/KubernetesEventsDatatable/EventsDatatable.tsx b/app/react/kubernetes/components/EventsDatatable/EventsDatatable.tsx
similarity index 100%
rename from app/react/kubernetes/components/KubernetesEventsDatatable/EventsDatatable.tsx
rename to app/react/kubernetes/components/EventsDatatable/EventsDatatable.tsx
diff --git a/app/react/kubernetes/components/EventsDatatable/ResourceEventsDatatable.tsx b/app/react/kubernetes/components/EventsDatatable/ResourceEventsDatatable.tsx
new file mode 100644
index 000000000..d94d4a3b5
--- /dev/null
+++ b/app/react/kubernetes/components/EventsDatatable/ResourceEventsDatatable.tsx
@@ -0,0 +1,53 @@
+import { useCurrentStateAndParams } from '@uirouter/react';
+
+import { useKubeStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
+import { useEvents } from '@/react/kubernetes/queries/useEvents';
+import { EventsDatatable } from '@/react/kubernetes/components/EventsDatatable';
+
+type Props = {
+ storageKey: string;
+ /** if undefined, all resources for the namespace (or cluster are returned) */
+ resourceId?: string;
+ /** if undefined, events are fetched for the cluster */
+ namespace?: string;
+};
+
+/** ResourceEventsDatatable returns the EventsDatatable for all events that relate to a specific resource id */
+export function ResourceEventsDatatable({
+ storageKey,
+ resourceId,
+ namespace,
+}: Props) {
+ const tableState = useKubeStore(storageKey, {
+ id: 'Date',
+ desc: true,
+ });
+
+ const {
+ params: { endpointId },
+ } = useCurrentStateAndParams();
+
+ const params = resourceId
+ ? { fieldSelector: `involvedObject.uid=${resourceId}` }
+ : {};
+ const resourceEventsQuery = useEvents(endpointId, {
+ namespace,
+ params,
+ queryOptions: {
+ autoRefreshRate: tableState.autoRefreshRate
+ ? tableState.autoRefreshRate * 1000
+ : undefined,
+ },
+ });
+ const nodeEvents = resourceEventsQuery.data || [];
+
+ return (
+
+ );
+}
diff --git a/app/react/kubernetes/components/KubernetesEventsDatatable/columns/date.tsx b/app/react/kubernetes/components/EventsDatatable/columns/date.tsx
similarity index 100%
rename from app/react/kubernetes/components/KubernetesEventsDatatable/columns/date.tsx
rename to app/react/kubernetes/components/EventsDatatable/columns/date.tsx
diff --git a/app/react/kubernetes/components/KubernetesEventsDatatable/columns/eventType.tsx b/app/react/kubernetes/components/EventsDatatable/columns/eventType.tsx
similarity index 100%
rename from app/react/kubernetes/components/KubernetesEventsDatatable/columns/eventType.tsx
rename to app/react/kubernetes/components/EventsDatatable/columns/eventType.tsx
diff --git a/app/react/kubernetes/components/KubernetesEventsDatatable/columns/helper.ts b/app/react/kubernetes/components/EventsDatatable/columns/helper.ts
similarity index 100%
rename from app/react/kubernetes/components/KubernetesEventsDatatable/columns/helper.ts
rename to app/react/kubernetes/components/EventsDatatable/columns/helper.ts
diff --git a/app/react/kubernetes/components/KubernetesEventsDatatable/columns/index.ts b/app/react/kubernetes/components/EventsDatatable/columns/index.ts
similarity index 100%
rename from app/react/kubernetes/components/KubernetesEventsDatatable/columns/index.ts
rename to app/react/kubernetes/components/EventsDatatable/columns/index.ts
diff --git a/app/react/kubernetes/components/KubernetesEventsDatatable/columns/kind.tsx b/app/react/kubernetes/components/EventsDatatable/columns/kind.tsx
similarity index 100%
rename from app/react/kubernetes/components/KubernetesEventsDatatable/columns/kind.tsx
rename to app/react/kubernetes/components/EventsDatatable/columns/kind.tsx
diff --git a/app/react/kubernetes/components/KubernetesEventsDatatable/columns/message.tsx b/app/react/kubernetes/components/EventsDatatable/columns/message.tsx
similarity index 100%
rename from app/react/kubernetes/components/KubernetesEventsDatatable/columns/message.tsx
rename to app/react/kubernetes/components/EventsDatatable/columns/message.tsx
diff --git a/app/react/kubernetes/components/KubernetesEventsDatatable/index.ts b/app/react/kubernetes/components/EventsDatatable/index.ts
similarity index 100%
rename from app/react/kubernetes/components/KubernetesEventsDatatable/index.ts
rename to app/react/kubernetes/components/EventsDatatable/index.ts
diff --git a/app/react/kubernetes/queries/query-keys.ts b/app/react/kubernetes/queries/query-keys.ts
new file mode 100644
index 000000000..ff225ac3f
--- /dev/null
+++ b/app/react/kubernetes/queries/query-keys.ts
@@ -0,0 +1,5 @@
+/** Kubernetes environment base query keys */
+export const queryKeys = {
+ base: (environmentId: number) =>
+ ['environments', environmentId, 'kubernetes'] as const,
+};
diff --git a/app/react/kubernetes/queries/useEvents.ts b/app/react/kubernetes/queries/useEvents.ts
new file mode 100644
index 000000000..870a468ca
--- /dev/null
+++ b/app/react/kubernetes/queries/useEvents.ts
@@ -0,0 +1,86 @@
+import { EventList } from 'kubernetes-types/core/v1';
+import { useQuery } from '@tanstack/react-query';
+
+import { EnvironmentId } from '@/react/portainer/environments/types';
+import axios from '@/portainer/services/axios';
+import { withError } from '@/react-tools/react-query';
+
+import { parseKubernetesAxiosError } from '../axiosError';
+
+import { queryKeys as environmentQueryKeys } from './query-keys';
+
+type RequestOptions = {
+ /** if undefined, events are fetched at the cluster scope */
+ namespace?: string;
+ params?: {
+ /** https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors */
+ labelSelector?: string;
+ /** https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors */
+ fieldSelector?: string;
+ };
+};
+
+const queryKeys = {
+ base: (environmentId: number, { namespace, params }: RequestOptions) => {
+ if (namespace) {
+ return [
+ ...environmentQueryKeys.base(environmentId),
+ 'events',
+ namespace,
+ params,
+ ] as const;
+ }
+ return [
+ ...environmentQueryKeys.base(environmentId),
+ 'events',
+ params,
+ ] as const;
+ },
+};
+
+async function getEvents(
+ environmentId: EnvironmentId,
+ options?: RequestOptions
+) {
+ const { namespace, params } = options ?? {};
+ try {
+ const { data } = await axios.get(
+ buildUrl(environmentId, namespace),
+ {
+ params,
+ }
+ );
+ return data.items;
+ } catch (e) {
+ throw parseKubernetesAxiosError(e, 'Unable to retrieve events');
+ }
+}
+
+type QueryOptions = {
+ queryOptions?: {
+ autoRefreshRate?: number;
+ };
+} & RequestOptions;
+
+export function useEvents(
+ environmentId: EnvironmentId,
+ options?: QueryOptions
+) {
+ const { queryOptions, params, namespace } = options ?? {};
+ return useQuery(
+ queryKeys.base(environmentId, { params, namespace }),
+ () => getEvents(environmentId, { params, namespace }),
+ {
+ ...withError('Unable to retrieve events'),
+ refetchInterval() {
+ return queryOptions?.autoRefreshRate ?? false;
+ },
+ }
+ );
+}
+
+function buildUrl(environmentId: EnvironmentId, namespace?: string) {
+ return namespace
+ ? `/endpoints/${environmentId}/kubernetes/api/v1/namespaces/${namespace}/events`
+ : `/endpoints/${environmentId}/kubernetes/api/v1/events`;
+}