Add buttons to quicky filter the current view by a specific namespace (#6677)

* Add quick namespace filtering in pods view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to HorizontalPodAutoscalers view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to Leases view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to LimitRanges view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to ConfigMaps view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove last usage of legacy global

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to PodDisruptionBudgets view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to ResourceQuotas view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to Secrets view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove last usage of legacy global secretsStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to CustomResources view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to Events view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to HelmReleases view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to Endpoints view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove last usage of legacy global endpointsStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to Ingresses view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to NetworkPolicies view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to PortForwards view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to Services view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to PersistentVolumeClaims view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to RoleBindings view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to Roles view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to ServiceAccounts view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to CronJobs view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to DaemonSets view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to Deployments view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to Jobs view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to StatefulSets view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add quick namespace filtering to ReplicaSets view

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Remove last usage of legacy global replicaSetStore

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix up missing styles

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton authored and GitHub committed 2022-12-01 09:20:56 -05:00
1 parent ab4a99bd93
commit a546f3b8ae
79 files changed
+1184 -433

No files matched your search

@@ -1,12 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import serviceStoreInjectable from "./store.injectable";
/**
* @deprecated use `di.inject(serviceStoreInjectable)` instead
*/
export const serviceStore = asLegacyGlobalForExtensionApi(serviceStoreInjectable);
@@ -7,31 +7,33 @@ import { observer } from "mobx-react";
import React from "react";
import { Table, TableHead, TableCell, TableRow } from "../table";
import { prevDefault } from "../../utils";
import { endpointsStore } from "../+network-endpoints/legacy-store";
import { Spinner } from "../spinner";
import { showDetails } from "../kube-detail-params";
import logger from "../../../common/logger";
import type { Logger } from "../../../common/logger";
import { Endpoints } from "../../../common/k8s-api/endpoints";
import type { ShowDetails } from "../kube-detail-params/show-details.injectable";
import { withInjectables } from "@ogre-tools/injectable-react";
import loggerInjectable from "../../../common/logger.injectable";
import showDetailsInjectable from "../kube-detail-params/show-details.injectable";
export interface ServiceDetailsEndpointProps {
endpoints: Endpoints;
}
interface Dependencies {
logger: Logger;
showDetails: ShowDetails;
}
@observer
export class ServiceDetailsEndpoint extends React.Component<ServiceDetailsEndpointProps> {
class NonInjectedServiceDetailsEndpoint extends React.Component<ServiceDetailsEndpointProps & Dependencies> {
render() {
const { endpoints } = this.props;
if (!endpoints && !endpointsStore.isLoaded) return (
<div className="PodDetailsList flex justify-center"><Spinner/></div>
);
if (!endpoints) {
return null;
}
if (!(endpoints instanceof Endpoints)) {
logger.error("[ServiceDetailsEndpoint]: passed object that is not an instanceof Endpoints", endpoints);
this.props.logger.error("[ServiceDetailsEndpoint]: passed object that is not an instanceof Endpoints", endpoints);
return null;
}
@@ -51,7 +53,7 @@ export class ServiceDetailsEndpoint extends React.Component<ServiceDetailsEndpoi
<TableRow
key={endpoints.getId()}
nowrap
onClick={prevDefault(() => showDetails(endpoints.selfLink, false))}
onClick={prevDefault(() => this.props.showDetails(endpoints.selfLink, false))}
>
<TableCell className="name">{endpoints.getName()}</TableCell>
<TableCell className="endpoints">{ endpoints.toString()}</TableCell>
@@ -61,3 +63,11 @@ export class ServiceDetailsEndpoint extends React.Component<ServiceDetailsEndpoi
);
}
}
export const ServiceDetailsEndpoint = withInjectables<Dependencies, ServiceDetailsEndpointProps>(NonInjectedServiceDetailsEndpoint, {
getProps: (di, props) => ({
...props,
logger: di.inject(loggerInjectable),
showDetails: di.inject(showDetailsInjectable),
}),
});
@@ -25,5 +25,9 @@
@include service-status-colors;
flex: 0.6;
}
a.filterNamespace {
border-bottom: unset;
}
}
}
@@ -9,10 +9,16 @@ import React from "react";
import { observer } from "mobx-react";
import { KubeObjectListLayout } from "../kube-object-list-layout";
import { Badge } from "../badge";
import { serviceStore } from "./legacy-store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import { KubeObjectAge } from "../kube-object/age";
import { prevDefault } from "../../utils";
import type { ServiceStore } from "./store";
import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
import type { Service } from "../../../common/k8s-api/endpoints";
import { withInjectables } from "@ogre-tools/injectable-react";
import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable";
import serviceStoreInjectable from "./store.injectable";
enum columnId {
name = "name",
@@ -26,8 +32,27 @@ enum columnId {
status = "status",
}
const formatExternalIps = (service: Service) => {
const externalIps = service.getExternalIps();
if (externalIps.length > 0) {
return externalIps.join(", ");
}
if (service.spec?.externalName) {
return service.spec.externalName;
}
return "-";
};
interface Dependencies {
serviceStore: ServiceStore;
filterByNamespace: FilterByNamespace;
}
@observer
export class Services extends React.Component {
class NonInjectedServices extends React.Component<Dependencies> {
render() {
return (
<SiblingsInTabLayout>
@@ -35,7 +60,7 @@ export class Services extends React.Component {
isConfigurable
tableId="network_services"
className="Services"
store={serviceStore}
store={this.props.serviceStore}
sortingCallbacks={{
[columnId.name]: service => service.getName(),
[columnId.namespace]: service => service.getNs(),
@@ -64,28 +89,34 @@ export class Services extends React.Component {
{ title: "Age", className: "age", sortBy: columnId.age, id: columnId.age },
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
]}
renderTableContents={service => {
const externalIps = service.getExternalIps();
if (externalIps.length === 0 && service.spec?.externalName) {
externalIps.push(service.spec.externalName);
}
return [
service.getName(),
<KubeObjectStatusIcon key="icon" object={service} />,
service.getNs(),
service.getType(),
service.getClusterIp(),
service.getPorts().join(", "),
externalIps.join(", ") || "-",
service.getSelector().map(label => <Badge key={label} label={label} />),
<KubeObjectAge key="age" object={service} />,
{ title: service.getStatus(), className: service.getStatus().toLowerCase() },
];
}}
renderTableContents={service => [
service.getName(),
<KubeObjectStatusIcon key="icon" object={ service } />,
<a
key="namespace"
className="filterNamespace"
onClick={ prevDefault(() => this.props.filterByNamespace(service.getNs())) }
>
{ service.getNs() }
</a>,
service.getType(),
service.getClusterIp(),
service.getPorts().join(", "),
formatExternalIps(service),
service.getSelector().map(label => <Badge key={ label } label={ label } />),
<KubeObjectAge key="age" object={ service } />,
{ title: service.getStatus(), className: service.getStatus().toLowerCase() },
]}
/>
</SiblingsInTabLayout>
);
}
}
export const Services = withInjectables<Dependencies>(NonInjectedServices, {
getProps: (di, props) => ({
...props,
filterByNamespace: di.inject(filterByNamespaceInjectable),
serviceStore: di.inject(serviceStoreInjectable),
}),
});