Allow extensions to register kube-object menus + details (#1108)

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen authored and GitHub committed 2020-10-22 21:41:20 +03:00
1 parent 91bef77997
commit 99db7aca19
101 files changed
+7858 -1604

No files matched your search

@@ -7,14 +7,14 @@ import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge";
import { KubeObjectDetailsProps } from "../kube-object";
import { cssNames } from "../../utils";
import { HorizontalPodAutoscaler, hpaApi, HpaMetricType, IHpaMetric } from "../../api/endpoints/hpa.api";
import { HorizontalPodAutoscaler, HpaMetricType, IHpaMetric } from "../../api/endpoints/hpa.api";
import { KubeEventDetails } from "../+events/kube-event-details";
import { Trans } from "@lingui/macro";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { getDetailsUrl } from "../../navigation";
import { lookupApiLink } from "../../api/kube-api";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<HorizontalPodAutoscaler> {
}
@@ -128,6 +128,10 @@ export class HpaDetails extends React.Component<Props> {
}
}
apiManager.registerViews(hpaApi, {
Details: HpaDetails,
});
kubeObjectDetailRegistry.add({
kind: "HorizontalPodAutoscaler",
apiVersions: ["autoscaling/v1"],
components: {
Details: (props) => <HpaDetails {...props} />
}
})
@@ -80,20 +80,8 @@ export class HorizontalPodAutoscalers extends React.Component<Props> {
)
})
]}
renderItemMenu={(item: HorizontalPodAutoscaler) => {
return <HpaMenu object={item}/>
}}
/>
);
}
}
export function HpaMenu(props: KubeObjectMenuProps<HorizontalPodAutoscaler>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(hpaApi, {
Menu: HpaMenu,
})
@@ -11,9 +11,9 @@ import { Button } from "../button";
import { KubeEventDetails } from "../+events/kube-event-details";
import { configMapsStore } from "./config-maps.store";
import { KubeObjectDetailsProps } from "../kube-object";
import { ConfigMap, configMapApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
import { ConfigMap } from "../../api/endpoints";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<ConfigMap> {
}
@@ -94,6 +94,10 @@ export class ConfigMapDetails extends React.Component<Props> {
}
}
apiManager.registerViews(configMapApi, {
Details: ConfigMapDetails
})
kubeObjectDetailRegistry.add({
kind: "ConfigMap",
apiVersions: ["v1"],
components: {
Details: (props) => <ConfigMapDetails {...props} />
}
})
@@ -50,20 +50,8 @@ export class ConfigMaps extends React.Component<Props> {
configMap.getKeys().join(", "),
configMap.getAge(),
]}
renderItemMenu={(item: ConfigMap) => {
return <ConfigMapMenu object={item}/>
}}
/>
);
}
}
export function ConfigMapMenu(props: KubeObjectMenuProps<ConfigMap>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(configMapApi, {
Menu: ConfigMapMenu,
})
@@ -3,14 +3,12 @@ import "./pod-disruption-budgets-details.scss";
import React from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { DrawerItem, DrawerTitle } from "../drawer";
import { DrawerItem } from "../drawer";
import { Badge } from "../badge";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { KubeObjectDetailsProps } from "../kube-object";
import { PodDisruptionBudget, pdbApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
import { KubeObjectStore } from "../../kube-object.store";
import { PodDisruptionBudget } from "../../api/endpoints";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<PodDisruptionBudget> {
}
@@ -56,6 +54,10 @@ export class PodDisruptionBudgetDetails extends React.Component<Props> {
}
}
apiManager.registerViews(pdbApi, {
Details: PodDisruptionBudgetDetails,
});
kubeObjectDetailRegistry.add({
kind: "PodDisruptionBudget",
apiVersions: ["policy/v1beta1"],
components: {
Details: (props) => <PodDisruptionBudgetDetails {...props} />
}
})
@@ -64,20 +64,7 @@ export class PodDisruptionBudgets extends React.Component<Props> {
pdb.getAge(),
]
}}
renderItemMenu={(pdb: PodDisruptionBudget) => {
return <PodDisruptionBudgetsMenu object={pdb}/>
}}
/>
);
}
}
export function PodDisruptionBudgetsMenu(props: KubeObjectMenuProps<PodDisruptionBudget>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(pdbApi, {
Menu: PodDisruptionBudgetsMenu,
})
@@ -6,11 +6,12 @@ import { Trans } from "@lingui/macro";
import { DrawerItem, DrawerTitle } from "../drawer";
import { cpuUnitsToNumber, cssNames, unitsToBytes, metricUnitsToNumber } from "../../utils";
import { KubeObjectDetailsProps } from "../kube-object";
import { ResourceQuota, resourceQuotaApi } from "../../api/endpoints/resource-quota.api";
import { ResourceQuota } from "../../api/endpoints/resource-quota.api";
import { LineProgress } from "../line-progress";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
import { ReplicaSetDetails } from "../+workloads-replicasets";
interface Props extends KubeObjectDetailsProps<ResourceQuota> {
}
@@ -97,6 +98,10 @@ export class ResourceQuotaDetails extends React.Component<Props> {
}
}
apiManager.registerViews(resourceQuotaApi, {
Details: ResourceQuotaDetails
})
kubeObjectDetailRegistry.add({
kind: "ResourceQuota",
apiVersions: ["v1"],
components: {
Details: (props) => <ReplicaSetDetails {...props} />
}
})
@@ -48,9 +48,6 @@ export class ResourceQuotas extends React.Component<Props> {
resourceQuota.getNs(),
resourceQuota.getAge(),
]}
renderItemMenu={(item: ResourceQuota) => {
return <ResourceQuotaMenu object={item}/>
}}
addRemoveButtons={{
onAdd: () => AddQuotaDialog.open(),
addTooltip: <Trans>Create new ResourceQuota</Trans>
@@ -61,13 +58,3 @@ export class ResourceQuotas extends React.Component<Props> {
);
}
}
export function ResourceQuotaMenu(props: KubeObjectMenuProps<ResourceQuota>) {
return (
<KubeObjectMenu {...props}/>
);
}
apiManager.registerViews(resourceQuotaApi, {
Menu: ResourceQuotaMenu,
})
@@ -13,10 +13,10 @@ import { base64 } from "../../utils";
import { Icon } from "../icon";
import { secretsStore } from "./secrets.store";
import { KubeObjectDetailsProps } from "../kube-object";
import { Secret, secretsApi } from "../../api/endpoints";
import { Secret } from "../../api/endpoints";
import { _i18n } from "../../i18n";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<Secret> {
}
@@ -113,6 +113,10 @@ export class SecretDetails extends React.Component<Props> {
}
}
apiManager.registerViews(secretsApi, {
Details: SecretDetails,
kubeObjectDetailRegistry.add({
kind: "Secret",
apiVersions: ["v1"],
components: {
Details: (props) => <SecretDetails {...props} />
}
})
@@ -61,9 +61,6 @@ export class Secrets extends React.Component<Props> {
secret.type,
secret.getAge(),
]}
renderItemMenu={(item: Secret) => {
return <SecretMenu object={item}/>
}}
addRemoveButtons={{
onAdd: () => AddSecretDialog.open(),
addTooltip: <Trans>Create new Secret</Trans>
@@ -74,13 +71,3 @@ export class Secrets extends React.Component<Props> {
);
}
}
export function SecretMenu(props: KubeObjectMenuProps<Secret>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(secretsApi, {
Menu: SecretMenu,
})
@@ -1,12 +0,0 @@
$cert-status-colors: (
ready: $colorOk,
);
@mixin cert-status-bgc {
@each $status, $color in $cert-status-colors {
&.#{$status} {
background: $color;
color: white;
}
}
}
@@ -1,7 +0,0 @@
@import "cert-manager.mixins";
.CertificateDetails {
.Badge {
@include cert-status-bgc;
}
}
@@ -1,142 +0,0 @@
import "./certificate-details.scss"
import React from "react";
import moment from "moment"
import { observer } from "mobx-react";
import { Link } from "react-router-dom";
import { Trans } from "@lingui/macro";
import { DrawerItem, DrawerTitle } from "../../drawer";
import { Badge } from "../../badge";
import { KubeEventDetails } from "../../+events/kube-event-details";
import { KubeObjectDetailsProps } from "../../kube-object";
import { Certificate, certificatesApi } from "../../../api/endpoints/cert-manager.api";
import { cssNames } from "../../../utils";
import { apiManager } from "../../../api/api-manager";
import { KubeObjectMeta } from "../../kube-object/kube-object-meta";
interface Props extends KubeObjectDetailsProps<Certificate> {
}
@observer
export class CertificateDetails extends React.Component<Props> {
render() {
const { object: cert, className } = this.props;
if (!cert) return;
const { spec, status } = cert;
const { acme, isCA, commonName, secretName, dnsNames, duration, ipAddresses, keyAlgorithm, keySize, organization, renewBefore } = spec;
const { lastFailureTime, notAfter } = status;
return (
<div className={cssNames("CertificateDetails", className)}>
<KubeObjectMeta object={cert}/>
<DrawerItem name={<Trans>Issuer</Trans>}>
<Link to={cert.getIssuerDetailsUrl()}>
{cert.getIssuerName()}
</Link>
</DrawerItem>
<DrawerItem name={<Trans>Secret Name</Trans>}>
<Link to={cert.getSecretDetailsUrl()}>
{secretName}
</Link>
</DrawerItem>
<DrawerItem name="CA">
{isCA ? <Trans>Yes</Trans> : <Trans>No</Trans>}
</DrawerItem>
{commonName && (
<DrawerItem name={<Trans>Common Name</Trans>}>
{commonName}
</DrawerItem>
)}
{dnsNames && (
<DrawerItem name={<Trans>DNS names</Trans>} labelsOnly>
{dnsNames.map(name => <Badge key={name} label={name}/>)}
</DrawerItem>
)}
{ipAddresses && (
<DrawerItem name={<Trans>IP addresses</Trans>}>
{ipAddresses.join(", ")}
</DrawerItem>
)}
{organization && (
<DrawerItem name={<Trans>Organization</Trans>}>
{organization.join(", ")}
</DrawerItem>
)}
{duration && (
<DrawerItem name={<Trans>Duration</Trans>}>
{duration}
</DrawerItem>
)}
{renewBefore && (
<DrawerItem name={<Trans>Renew Before</Trans>}>
{renewBefore}
</DrawerItem>
)}
{keySize && (
<DrawerItem name={<Trans>Key Size</Trans>}>
{keySize}
</DrawerItem>
)}
{keyAlgorithm && (
<DrawerItem name={<Trans>Key Algorithm</Trans>}>
{keyAlgorithm}
</DrawerItem>
)}
<DrawerItem name={<Trans>Not After</Trans>}>
{moment(notAfter).format("LLL")}
</DrawerItem>
{lastFailureTime && (
<DrawerItem name={<Trans>Last Failure Time</Trans>}>
{lastFailureTime}
</DrawerItem>
)}
<DrawerItem name={<Trans>Status</Trans>} labelsOnly>
{cert.getConditions().map(({ type, tooltip, isReady }) => {
return (
<Badge
key={type}
label={type}
tooltip={tooltip}
className={cssNames({ [type.toLowerCase()]: isReady })}
/>
)
})}
</DrawerItem>
{acme && (
<>
<DrawerTitle title="ACME"/>
{acme.config.map(({ domains, http01, dns01 }, index) => {
return (
<div key={index} className="acme-config">
<DrawerItem name={<Trans>Domains</Trans>} labelsOnly>
{domains.map(domain => <Badge key={domain} label={domain}/>)}
</DrawerItem>
<DrawerItem name={<Trans>Http01</Trans>}>
{Object.entries(http01).map(([key, val]) => `${key}: ${val}`)[0]}
</DrawerItem>
{dns01 && (
<DrawerItem name={<Trans>DNS Provider</Trans>} labelsOnly>
{dns01.provider}
</DrawerItem>
)}
</div>
)
})}
</>
)}
<KubeEventDetails object={cert}/>
</div>
);
}
}
apiManager.registerViews(certificatesApi, {
Details: CertificateDetails
})
@@ -1,26 +0,0 @@
@import "cert-manager.mixins";
.Certificates {
.TableCell {
&.name {
flex: 1.2;
}
&.type {
flex: .5;
}
&.status {
flex: .5;
@include table-cell-labels-offsets;
.Badge {
@include cert-status-bgc;
}
}
&.age {
flex: .5;
}
}
}
@@ -1,105 +0,0 @@
import "./certificates.scss"
import React from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { KubeObjectMenu, KubeObjectMenuProps } from "../../kube-object/kube-object-menu";
import { KubeObjectListLayout, KubeObjectListLayoutProps } from "../../kube-object";
import { Certificate, certificatesApi } from "../../../api/endpoints/cert-manager.api";
import { cssNames, stopPropagation } from "../../../utils";
import { Link } from "react-router-dom";
import { Badge } from "../../badge";
import { apiManager } from "../../../api/api-manager";
import { Spinner } from "../../spinner";
enum sortBy {
name = "name",
namespace = "namespace",
age = "age",
commonName = "common-name",
secretName = "secret",
issuer = "issuer",
type = "type",
}
@observer
export class Certificates extends React.Component<KubeObjectListLayoutProps> {
render() {
const { store = apiManager.getStore(certificatesApi), ...layoutProps } = this.props;
if (!store) {
return <Spinner center/>
}
return (
<KubeObjectListLayout
{...layoutProps}
store={store}
className="Certificates"
sortingCallbacks={{
[sortBy.name]: (item: Certificate) => item.getName(),
[sortBy.namespace]: (item: Certificate) => item.getNs(),
[sortBy.secretName]: (item: Certificate) => item.getSecretName(),
[sortBy.commonName]: (item: Certificate) => item.getCommonName(),
[sortBy.issuer]: (item: Certificate) => item.getIssuerName(),
[sortBy.type]: (item: Certificate) => item.getType(),
}}
searchFilters={[
(item: Certificate) => item.getSearchFields(),
(item: Certificate) => item.getSecretName(),
(item: Certificate) => item.getCommonName(),
(item: Certificate) => item.getIssuerName(),
(item: Certificate) => item.getType(),
]}
renderHeaderTitle={<Trans>Certificates</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Common Name</Trans>, className: "common-name", sortBy: sortBy.type },
{ title: <Trans>Type</Trans>, className: "type", sortBy: sortBy.type },
{ title: <Trans>Issuer</Trans>, className: "issuer", sortBy: sortBy.issuer },
{ title: <Trans>Secret</Trans>, className: "secret", sortBy: sortBy.secretName },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
{ title: <Trans>Status</Trans>, className: "status" },
]}
renderTableContents={(cert: Certificate) => {
return [
cert.getName(),
cert.getNs(),
cert.getCommonName(),
cert.getType(),
<Link to={cert.getIssuerDetailsUrl()} onClick={stopPropagation}>
{cert.getIssuerName()}
</Link>,
<Link to={cert.getSecretDetailsUrl()} onClick={stopPropagation}>
{cert.getSecretName()}
</Link>,
cert.getAge(),
cert.getConditions().map(({ type, tooltip, isReady }) => {
return (
<Badge
key={type}
label={type}
tooltip={tooltip}
className={cssNames({ [type.toLowerCase()]: isReady })}
/>
)
})
]
}}
renderItemMenu={(item: Certificate) => {
return <CertificateMenu object={item}/>
}}
/>
);
}
}
export function CertificateMenu(props: KubeObjectMenuProps<Certificate>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(certificatesApi, {
List: Certificates,
Menu: CertificateMenu
})
@@ -1,4 +0,0 @@
export * from "./certificates"
export * from "./certificate-details"
export * from "./issuers"
export * from "./issuer-details"
@@ -1,7 +0,0 @@
@import "cert-manager.mixins";
.IssuerDetails {
.Badge {
@include cert-status-bgc;
}
}
@@ -1,175 +0,0 @@
import "./issuer-details.scss"
import React from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { Link } from "react-router-dom";
import { DrawerItem, DrawerTitle } from "../../drawer";
import { Badge } from "../../badge";
import { KubeEventDetails } from "../../+events/kube-event-details";
import { KubeObjectDetailsProps } from "../../kube-object";
import { clusterIssuersApi, Issuer, issuersApi } from "../../../api/endpoints/cert-manager.api";
import { autobind, cssNames } from "../../../utils";
import { getDetailsUrl } from "../../../navigation";
import { secretsApi } from "../../../api/endpoints";
import { apiManager } from "../../../api/api-manager";
import { KubeObjectMeta } from "../../kube-object/kube-object-meta";
interface Props extends KubeObjectDetailsProps<Issuer> {
}
@observer
export class IssuerDetails extends React.Component<Props> {
@autobind()
renderSecretLink(secretName: string) {
const namespace = this.props.object.getNs();
if (!namespace) {
return secretName;
}
const secretDetailsUrl = getDetailsUrl(secretsApi.getUrl({
namespace: namespace,
name: secretName,
}));
return (
<Link to={secretDetailsUrl}>
{secretName}
</Link>
)
}
render() {
const { object: issuer, className } = this.props;
if (!issuer) return;
const { renderSecretLink } = this;
const { spec: { acme, ca, vault, venafi }, status } = issuer;
return (
<div className={cssNames("IssuerDetails", className)}>
<KubeObjectMeta object={issuer}/>
<DrawerItem name={<Trans>Type</Trans>}>
{issuer.getType()}
</DrawerItem>
<DrawerItem name={<Trans>Status</Trans>} labelsOnly>
{issuer.getConditions().map(({ type, tooltip, isReady }) => {
return (
<Badge
key={type}
label={type}
tooltip={tooltip}
className={cssNames({ [type.toLowerCase()]: isReady })}
/>
)
})}
</DrawerItem>
{acme && (() => {
const { email, server, skipTLSVerify, privateKeySecretRef, solvers } = acme;
return (
<>
<DrawerTitle title="ACME"/>
<DrawerItem name={<Trans>E-mail</Trans>}>
{email}
</DrawerItem>
<DrawerItem name={<Trans>Server</Trans>}>
{server}
</DrawerItem>
{status.acme && (
<DrawerItem name={<Trans>Status URI</Trans>}>
{status.acme.uri}
</DrawerItem>
)}
<DrawerItem name={<Trans>Private Key Secret</Trans>}>
{renderSecretLink(privateKeySecretRef.name)}
</DrawerItem>
<DrawerItem name={<Trans>Skip TLS Verify</Trans>}>
{skipTLSVerify ? <Trans>Yes</Trans> : <Trans>No</Trans>}
</DrawerItem>
</>
)
})()}
{ca && (() => {
const { secretName } = ca;
return (
<>
<DrawerTitle title="CA"/>
<DrawerItem name={<Trans>Secret Name</Trans>}>
{renderSecretLink(secretName)}
</DrawerItem>
</>
)
})()}
{vault && (() => {
const { auth, caBundle, path, server } = vault;
const { path: authPath, roleId, secretRef } = auth.appRole;
return (
<>
<DrawerTitle title="Vault"/>
<DrawerItem name={<Trans>Server</Trans>}>
{server}
</DrawerItem>
<DrawerItem name={<Trans>Path</Trans>}>
{path}
</DrawerItem>
<DrawerItem name={<Trans>CA Bundle</Trans>} labelsOnly>
<Badge label={caBundle}/>
</DrawerItem>
<DrawerTitle title={<Trans>Auth App Role</Trans>}/>
<DrawerItem name={<Trans>Path</Trans>}>
{authPath}
</DrawerItem>
<DrawerItem name={<Trans>Role ID</Trans>}>
{roleId}
</DrawerItem>
{secretRef && (
<DrawerItem name={<Trans>Secret</Trans>}>
{renderSecretLink(secretRef.name)}
</DrawerItem>
)}
</>
)
})()}
{venafi && (() => {
const { zone, cloud, tpp } = venafi;
return (
<>
<DrawerTitle title="CA"/>
<DrawerItem name={<Trans>Zone</Trans>}>
{zone}
</DrawerItem>
{cloud && (
<DrawerItem name={<Trans>Cloud API Token Secret</Trans>}>
{renderSecretLink(cloud.apiTokenSecretRef.name)}
</DrawerItem>
)}
{tpp && (
<>
<DrawerTitle title="TPP"/>
<DrawerItem name={<Trans>URL</Trans>}>
{tpp.url}
</DrawerItem>
<DrawerItem name={<Trans>CA Bundle</Trans>} labelsOnly>
<Badge label={tpp.caBundle}/>
</DrawerItem>
<DrawerItem name={<Trans>Credentials Ref</Trans>}>
{renderSecretLink(tpp.credentialsRef.name)}
</DrawerItem>
</>
)}
</>
)
})()}
<KubeEventDetails object={issuer}/>
</div>
);
}
}
apiManager.registerViews([issuersApi, clusterIssuersApi], {
Details: IssuerDetails
})
@@ -1,27 +0,0 @@
@import "cert-manager.mixins";
.Issuers {
.TableCell {
&.name {
flex: 1.2;
}
&.labels {
flex: 2;
@include table-cell-labels-offsets;
}
&.status {
flex: .5;
@include table-cell-labels-offsets;
.Badge {
@include cert-status-bgc;
}
}
&.age {
flex: .5;
}
}
}
@@ -1,103 +0,0 @@
import "./issuers.scss"
import React from "react";
import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { KubeObjectMenu, KubeObjectMenuProps } from "../../kube-object/kube-object-menu";
import { KubeObjectListLayout, KubeObjectListLayoutProps } from "../../kube-object";
import { clusterIssuersApi, Issuer, issuersApi } from "../../../api/endpoints/cert-manager.api";
import { cssNames } from "../../../utils";
import { Badge } from "../../badge";
import { Spinner } from "../../spinner";
import { apiManager } from "../../../api/api-manager";
enum sortBy {
name = "name",
namespace = "namespace",
type = "type",
labels = "labels",
age = "age",
}
@observer
export class ClusterIssuers extends React.Component<KubeObjectListLayoutProps> {
render() {
const store = apiManager.getStore(clusterIssuersApi);
return (
<Issuers
{...this.props}
isClusterScoped={true}
store={store}
renderHeaderTitle={<Trans>Cluster Issuers</Trans>}
/>
)
}
}
@observer
export class Issuers extends React.Component<KubeObjectListLayoutProps> {
render() {
const { store = apiManager.getStore(issuersApi), ...layoutProps } = this.props;
if (!store) {
return <Spinner center/>
}
return (
<KubeObjectListLayout
store={store}
renderHeaderTitle={<Trans>Issuers</Trans>}
{...layoutProps}
className="Issuers"
sortingCallbacks={{
[sortBy.name]: (item: Issuer) => item.getName(),
[sortBy.namespace]: (item: Issuer) => item.getNs(),
[sortBy.type]: (item: Issuer) => item.getType(),
[sortBy.labels]: (item: Issuer) => item.getLabels(),
[sortBy.age]: (item: Issuer) => item.metadata.creationTimestamp,
}}
searchFilters={[
(item: Issuer) => item.getSearchFields(),
(item: Issuer) => item.getType(),
]}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Labels</Trans>, className: "labels", sortBy: sortBy.labels },
{ title: <Trans>Type</Trans>, className: "type", sortBy: sortBy.type },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
{ title: <Trans>Status</Trans>, className: "status" },
]}
renderTableContents={(issuer: Issuer) => [
issuer.getName(),
issuer.getNs(),
issuer.getLabels().map(label => <Badge key={label} label={label} title={label}/>),
issuer.getType(),
issuer.getAge(),
issuer.getConditions().map(({ type, tooltip, isReady }) => {
return (
<Badge
key={type}
label={type}
tooltip={tooltip}
className={cssNames({ [type.toLowerCase()]: isReady })}
/>
)
})
]}
renderItemMenu={(item: Issuer) => {
return <IssuerMenu object={item}/>
}}
/>
);
}
}
export function IssuerMenu(props: KubeObjectMenuProps<Issuer>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews([issuersApi, clusterIssuersApi], {
List: Issuers,
Menu: IssuerMenu,
})
@@ -14,6 +14,7 @@ import { KubeObjectDetailsProps } from "../kube-object";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { Input } from "../input";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<CustomResourceDefinition> {
}
@@ -133,6 +134,10 @@ export class CRDDetails extends React.Component<Props> {
}
}
apiManager.registerViews(crdApi, {
Details: CRDDetails
})
kubeObjectDetailRegistry.add({
kind: "CustomResourceDefinition",
apiVersions: ["apiextensions.k8s.io/v1", "apiextensions.k8s.io/v1beta1"],
components: {
Details: (props) => <CRDDetails {...props} />
}
})
@@ -8,9 +8,7 @@ import { Link } from "react-router-dom";
import { stopPropagation } from "../../utils";
import { KubeObjectListLayout } from "../kube-object";
import { crdStore } from "./crd.store";
import { apiManager } from "../../api/api-manager";
import { crdApi, CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { Select, SelectOption } from "../select";
import { navigation, setQueryParams } from "../../navigation";
import { Icon } from "../icon";
@@ -103,20 +101,8 @@ export class CrdList extends React.Component {
crd.getAge(),
]
}}
renderItemMenu={(item: CustomResourceDefinition) => {
return <CRDMenu object={item}/>
}}
/>
)
}
}
export function CRDMenu(props: KubeObjectMenuProps<CustomResourceDefinition>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(crdApi, {
Menu: CRDMenu,
});
@@ -9,7 +9,6 @@ import { cssNames } from "../../utils";
import { Badge } from "../badge";
import { DrawerItem } from "../drawer";
import { KubeObjectDetailsProps } from "../kube-object";
import { apiManager } from "../../api/api-manager";
import { crdStore } from "./crd.store";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { Input } from "../input";
@@ -39,20 +38,13 @@ export class CrdResourceDetails extends React.Component<Props> {
return crdStore.getByObject(this.props.object);
}
@computed get CustomDetailsViews() {
return apiManager.getViews(this.props.object.selfLink).Details;
}
render() {
const { object } = this.props;
const { crd, CustomDetailsViews } = this;
const { crd } = this;
if (!object || !crd) return null;
const className = cssNames("CrdResourceDetails", crd.getResourceKind());
const extraColumns = crd.getPrinterColumns();
const showStatus = !extraColumns.find(column => column.name == "Status") && object.status?.conditions;
if (CustomDetailsViews) {
return <CustomDetailsViews className={className} object={object}/>
}
return (
<div className={className}>
<KubeObjectMeta object={object}/>
@@ -7,7 +7,6 @@ import { Trans } from "@lingui/macro";
import { RouteComponentProps } from "react-router";
import { KubeObjectListLayout } from "../kube-object";
import { KubeObject } from "../../api/kube-object";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { ICRDRouteParams } from "./crd.route";
import { autorun, computed } from "mobx";
import { crdStore } from "./crd.store";
@@ -59,9 +58,7 @@ export class CrdResources extends React.Component<Props> {
extraColumns.forEach(column => {
sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.query(item, column.jsonPath.slice(1))
})
// todo: merge extra columns and other params to predefined view
const { List } = apiManager.getViews(crd.getResourceApiBase());
const ListView = List || KubeObjectListLayout;
const ListView = KubeObjectListLayout;
return (
<ListView
className="CrdResources"
@@ -93,20 +90,7 @@ export class CrdResources extends React.Component<Props> {
}),
crdInstance.getAge(),
]}
renderItemMenu={(item: KubeObject) => {
return <CrdResourceMenu object={item}/>
}}
/>
)
}
}
export function CrdResourceMenu(props: KubeObjectMenuProps<KubeObject>) {
const { Menu } = apiManager.getViews(props.object.selfLink);
if (Menu) {
return <Menu {...props}/>
}
return (
<KubeObjectMenu {...props}/>
)
}
@@ -3,6 +3,3 @@ export * from "./crd-list";
export * from "./crd-details";
export * from "./crd-resources";
export * from "./crd-resource-details";
// customized crd-s
export * from "./certmanager.k8s.io"
@@ -7,12 +7,12 @@ import { DrawerItem, DrawerTitle } from "../drawer";
import { Link } from "react-router-dom";
import { observer } from "mobx-react";
import { KubeObjectDetailsProps } from "../kube-object";
import { eventApi, KubeEvent } from "../../api/endpoints/events.api";
import { apiManager } from "../../api/api-manager";
import { KubeEvent } from "../../api/endpoints/events.api";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { getDetailsUrl } from "../../navigation";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { lookupApiLink } from "../../api/kube-api";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<KubeEvent> {
}
@@ -74,6 +74,10 @@ export class EventDetails extends React.Component<Props> {
}
}
apiManager.registerViews(eventApi, {
Details: EventDetails,
});
kubeObjectDetailRegistry.add({
kind: "Event",
apiVersions: ["v1"],
components: {
Details: (props) => <EventDetails {...props}/>
}
})
@@ -6,14 +6,14 @@ import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { DrawerItem } from "../drawer";
import { cssNames } from "../../utils";
import { Namespace, namespacesApi } from "../../api/endpoints";
import { Namespace } from "../../api/endpoints";
import { KubeObjectDetailsProps } from "../kube-object";
import { Link } from "react-router-dom";
import { getDetailsUrl } from "../../navigation";
import { Spinner } from "../spinner";
import { resourceQuotaStore } from "../+config-resource-quotas/resource-quotas.store";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<Namespace> {
}
@@ -56,6 +56,10 @@ export class NamespaceDetails extends React.Component<Props> {
}
}
apiManager.registerViews(namespacesApi, {
Details: NamespaceDetails
});
kubeObjectDetailRegistry.add({
kind: "Namespace",
apiVersions: ["v1"],
components: {
Details: (props) => <NamespaceDetails {...props} />
}
})
@@ -53,9 +53,6 @@ export class Namespaces extends React.Component<Props> {
item.getAge(),
{ title: item.getStatus(), className: item.getStatus().toLowerCase() },
]}
renderItemMenu={(item: Namespace) => {
return <NamespaceMenu object={item}/>
}}
addRemoveButtons={{
addTooltip: <Trans>Add Namespace</Trans>,
onAdd: () => AddNamespaceDialog.open(),
@@ -69,13 +66,3 @@ export class Namespaces extends React.Component<Props> {
)
}
}
export function NamespaceMenu(props: KubeObjectMenuProps<Namespace>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(namespacesApi, {
Menu: NamespaceMenu,
});
@@ -2,16 +2,15 @@ import "./endpoint-details.scss"
import React from "react";
import { observer } from "mobx-react";
import { t, Trans } from "@lingui/macro";
import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge";
import { Trans } from "@lingui/macro";
import { DrawerTitle } from "../drawer";
import { KubeEventDetails } from "../+events/kube-event-details";
import { KubeObjectDetailsProps } from "../kube-object";
import { Endpoint, endpointApi } from "../../api/endpoints";
import { Endpoint } from "../../api/endpoints";
import { _i18n } from "../../i18n";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { EndpointSubsetList } from "./endpoint-subset-list";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<Endpoint> {
}
@@ -39,6 +38,10 @@ export class EndpointDetails extends React.Component<Props> {
}
}
apiManager.registerViews(endpointApi, {
Details: EndpointDetails,
kubeObjectDetailRegistry.add({
kind: "Endpoints",
apiVersions: ["v1"],
components: {
Details: (props) => <EndpointDetails {...props} />
}
})
@@ -47,9 +47,6 @@ export class Endpoints extends React.Component<Props> {
endpoint.toString(),
endpoint.getAge(),
]}
renderItemMenu={(item: Endpoint) => {
return <EndpointMenu object={item}/>
}}
tableProps={{
customRowHeights: (item: Endpoint, lineHeight, paddings) => {
const lines = item.getEndpointSubsets().length || 1;
@@ -60,13 +57,3 @@ export class Endpoints extends React.Component<Props> {
)
}
}
export function EndpointMenu(props: KubeObjectMenuProps<Endpoint>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(endpointApi, {
Menu: EndpointMenu
})
@@ -5,15 +5,15 @@ import { disposeOnUnmount, observer } from "mobx-react";
import { reaction } from "mobx";
import { Trans } from "@lingui/macro";
import { DrawerItem, DrawerTitle } from "../drawer";
import { Ingress, ILoadBalancerIngress, ingressApi } from "../../api/endpoints";
import { Ingress, ILoadBalancerIngress } from "../../api/endpoints";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { KubeEventDetails } from "../+events/kube-event-details";
import { ingressStore } from "./ingress.store";
import { ResourceMetrics } from "../resource-metrics";
import { KubeObjectDetailsProps } from "../kube-object";
import { IngressCharts } from "./ingress-charts";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<Ingress> {
}
@@ -134,6 +134,10 @@ export class IngressDetails extends React.Component<Props> {
}
}
apiManager.registerViews(ingressApi, {
Details: IngressDetails,
})
kubeObjectDetailRegistry.add({
kind: "Ingress",
apiVersions: ["extensions/v1beta1"],
components: {
Details: (props) => <IngressDetails {...props} />
}
})
@@ -48,9 +48,6 @@ export class Ingresses extends React.Component<Props> {
ingress.getRoutes().map(route => <p key={route}>{route}</p>),
ingress.getAge(),
]}
renderItemMenu={(item: Ingress) => {
return <IngressMenu object={item}/>
}}
tableProps={{
customRowHeights: (item: Ingress, lineHeight, paddings) => {
const lines = item.getRoutes().length || 1;
@@ -61,13 +58,3 @@ export class Ingresses extends React.Component<Props> {
)
}
}
export function IngressMenu(props: KubeObjectMenuProps<Ingress>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(ingressApi, {
Menu: IngressMenu
})
@@ -47,20 +47,7 @@ export class NetworkPolicies extends React.Component<Props> {
item.getTypes().join(", "),
item.getAge(),
]}
renderItemMenu={(item: NetworkPolicy) => {
return <NetworkPolicyMenu object={item}/>
}}
/>
)
}
}
export function NetworkPolicyMenu(props: KubeObjectMenuProps<NetworkPolicy>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(networkPolicyApi, {
Menu: NetworkPolicyMenu,
})
@@ -4,15 +4,15 @@ import get from "lodash/get";
import React, { Fragment } from "react";
import { t, Trans } from "@lingui/macro";
import { DrawerItem, DrawerTitle } from "../drawer";
import { IPolicyEgress, IPolicyIngress, IPolicyIpBlock, IPolicySelector, NetworkPolicy, networkPolicyApi } from "../../api/endpoints/network-policy.api";
import { IPolicyEgress, IPolicyIngress, IPolicyIpBlock, IPolicySelector, NetworkPolicy } from "../../api/endpoints/network-policy.api";
import { Badge } from "../badge";
import { SubTitle } from "../layout/sub-title";
import { KubeEventDetails } from "../+events/kube-event-details";
import { observer } from "mobx-react";
import { KubeObjectDetailsProps } from "../kube-object";
import { _i18n } from "../../i18n";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<NetworkPolicy> {
}
@@ -144,6 +144,10 @@ export class NetworkPolicyDetails extends React.Component<Props> {
}
}
apiManager.registerViews(networkPolicyApi, {
Details: NetworkPolicyDetails
})
kubeObjectDetailRegistry.add({
kind: "NetworkPolicy",
apiVersions: ["networking.k8s.io/v1"],
components: {
Details: (props) => <NetworkPolicyDetails {...props} />
}
})
@@ -7,13 +7,13 @@ import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge";
import { KubeEventDetails } from "../+events/kube-event-details";
import { KubeObjectDetailsProps } from "../kube-object";
import { Service, serviceApi, endpointApi } from "../../api/endpoints";
import { Service, endpointApi } from "../../api/endpoints";
import { _i18n } from "../../i18n";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { ServicePortComponent } from "./service-port-component";
import { endpointStore } from "../+network-endpoints/endpoints.store";
import { ServiceDetailsEndpoint } from "./service-details-endpoint";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<Service> {
}
@@ -85,6 +85,10 @@ export class ServiceDetails extends React.Component<Props> {
}
}
apiManager.registerViews(serviceApi, {
Details: ServiceDetails,
kubeObjectDetailRegistry.add({
kind: "Service",
apiVersions: ["v1"],
components: {
Details: (props) => <ServiceDetails {...props} />
}
})
@@ -70,20 +70,7 @@ export class Services extends React.Component<Props> {
service.getAge(),
{ title: service.getStatus(), className: service.getStatus().toLowerCase() },
]}
renderItemMenu={(item: Service) => {
return <ServiceMenu object={item}/>
}}
/>
)
}
}
export function ServiceMenu(props: KubeObjectMenuProps<Service>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(serviceApi, {
Menu: ServiceMenu
})
+1 -2
View File
@@ -1,4 +1,3 @@
export * from "./nodes"
export * from "./nodes.route"
export * from "./node-menu"
export * from "./node-details"
export * from "./node-details"
@@ -11,13 +11,13 @@ import { nodesStore } from "./nodes.store";
import { ResourceMetrics } from "../resource-metrics";
import { podsStore } from "../+workloads-pods/pods.store";
import { KubeObjectDetailsProps } from "../kube-object";
import { Node, nodesApi } from "../../api/endpoints";
import { Node } from "../../api/endpoints";
import { NodeCharts } from "./node-charts";
import { reaction } from "mobx";
import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { KubeEventDetails } from "../+events/kube-event-details";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<Node> {
}
@@ -155,6 +155,10 @@ export class NodeDetails extends React.Component<Props> {
}
}
apiManager.registerViews(nodesApi, {
Details: NodeDetails,
});
kubeObjectDetailRegistry.add({
kind: "Node",
apiVersions: ["v1"],
components: {
Details: (props) => <NodeDetails {...props} />
}
})
@@ -1,78 +0,0 @@
import React from "react";
import { t, Trans } from "@lingui/macro";
import { Node } from "../../api/endpoints/nodes.api";
import { createTerminalTab, terminalStore } from "../dock/terminal.store";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { ConfirmDialog } from "../confirm-dialog";
import { MenuItem } from "../menu";
import { Icon } from "../icon";
import { _i18n } from "../../i18n";
import { hideDetails } from "../../navigation";
export function NodeMenu(props: KubeObjectMenuProps<Node>) {
const { object: node, toolbar } = props;
if (!node) return null;
const nodeName = node.getName();
const sendToTerminal = (command: string) => {
terminalStore.sendCommand(command, {
enter: true,
newTab: true,
});
hideDetails();
}
const shell = () => {
createTerminalTab({
title: _i18n._(t`Node`) + `: ${nodeName}`,
node: nodeName,
});
hideDetails();
}
const cordon = () => {
sendToTerminal(`kubectl cordon ${nodeName}`);
}
const unCordon = () => {
sendToTerminal(`kubectl uncordon ${nodeName}`)
}
const drain = () => {
const command = `kubectl drain ${nodeName} --delete-local-data --ignore-daemonsets --force`;
ConfirmDialog.open({
ok: () => sendToTerminal(command),
labelOk: _i18n._(t`Drain Node`),
message: (
<p>
<Trans>Are you sure you want to drain <b>{nodeName}</b>?</Trans>
</p>
),
})
}
return (
<KubeObjectMenu {...props}>
<MenuItem onClick={shell}>
<Icon svg="ssh" interactive={toolbar} title={_i18n._(t`Node shell`)}/>
<span className="title"><Trans>Shell</Trans></span>
</MenuItem>
{!node.isUnschedulable() && (
<MenuItem onClick={cordon}>
<Icon material="pause_circle_filled" title={_i18n._(t`Cordon`)} interactive={toolbar}/>
<span className="title"><Trans>Cordon</Trans></span>
</MenuItem>
)}
{node.isUnschedulable() && (
<MenuItem onClick={unCordon}>
<Icon material="play_circle_filled" title={_i18n._(t`Uncordon`)} interactive={toolbar}/>
<span className="title"><Trans>Uncordon</Trans></span>
</MenuItem>
)}
<MenuItem onClick={drain}>
<Icon material="delete_sweep" title={_i18n._(t`Drain`)} interactive={toolbar}/>
<span className="title"><Trans>Drain</Trans></span>
</MenuItem>
</KubeObjectMenu>
);
}
+1 -10
View File
@@ -9,15 +9,13 @@ import { nodesStore } from "./nodes.store";
import { podsStore } from "../+workloads-pods/pods.store";
import { KubeObjectListLayout } from "../kube-object";
import { INodesRouteParams } from "./nodes.route";
import { Node, nodesApi } from "../../api/endpoints/nodes.api";
import { NodeMenu } from "./node-menu";
import { Node } from "../../api/endpoints/nodes.api";
import { LineProgress } from "../line-progress";
import { _i18n } from "../../i18n";
import { bytesToUnits } from "../../utils/convertMemory";
import { Tooltip, TooltipPosition } from "../tooltip";
import kebabCase from "lodash/kebabCase";
import upperFirst from "lodash/upperFirst";
import { apiManager } from "../../api/api-manager";
enum sortBy {
name = "name",
@@ -178,15 +176,8 @@ export class Nodes extends React.Component<Props> {
this.renderConditions(node),
]
}}
renderItemMenu={(item: Node) => {
return <NodeMenu object={item}/>
}}
/>
</TabLayout>
)
}
}
apiManager.registerViews(nodesApi, {
Menu: NodeMenu,
});
@@ -50,20 +50,7 @@ export class PodSecurityPolicies extends React.Component {
item.getAge(),
]
}}
renderItemMenu={(item: PodSecurityPolicy) => {
return <PodSecurityPolicyMenu object={item}/>
}}
/>
)
}
}
export function PodSecurityPolicyMenu(props: KubeObjectMenuProps<PodSecurityPolicy>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(pspApi, {
Menu: PodSecurityPolicyMenu,
})
@@ -5,11 +5,11 @@ import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { DrawerItem, DrawerTitle } from "../drawer";
import { KubeObjectDetailsProps } from "../kube-object";
import { PodSecurityPolicy, pspApi } from "../../api/endpoints";
import { PodSecurityPolicy } from "../../api/endpoints";
import { Badge } from "../badge";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<PodSecurityPolicy> {
}
@@ -209,6 +209,10 @@ export class PodSecurityPolicyDetails extends React.Component<Props> {
}
}
apiManager.registerViews(pspApi, {
Details: PodSecurityPolicyDetails,
});
kubeObjectDetailRegistry.add({
kind: "PodSecurityPolicy",
apiVersions: ["policy/v1beta1"],
components: {
Details: (props) => <PodSecurityPolicyDetails {...props}/>
}
})
@@ -8,10 +8,10 @@ import { Badge } from "../badge";
import { KubeEventDetails } from "../+events/kube-event-details";
import { observer } from "mobx-react";
import { KubeObjectDetailsProps } from "../kube-object";
import { StorageClass, storageClassApi } from "../../api/endpoints";
import { StorageClass } from "../../api/endpoints";
import { _i18n } from "../../i18n";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<StorageClass> {
}
@@ -62,6 +62,10 @@ export class StorageClassDetails extends React.Component<Props> {
}
}
apiManager.registerViews(storageClassApi, {
Details: StorageClassDetails
})
kubeObjectDetailRegistry.add({
kind: "StorageClass",
apiVersions: ["storage.k8s.io/v1"],
components: {
Details: (props) => <StorageClassDetails {...props} />
}
})
@@ -53,20 +53,7 @@ export class StorageClasses extends React.Component<Props> {
storageClass.isDefault() ? <Trans>Yes</Trans> : null,
storageClass.getAge(),
]}
renderItemMenu={(item: StorageClass) => {
return <StorageClassMenu object={item}/>
}}
/>
)
}
}
export function StorageClassMenu(props: KubeObjectMenuProps<StorageClass>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(storageClassApi, {
Menu: StorageClassMenu,
})
@@ -14,10 +14,10 @@ import { getDetailsUrl } from "../../navigation";
import { ResourceMetrics } from "../resource-metrics";
import { VolumeClaimDiskChart } from "./volume-claim-disk-chart";
import { KubeObjectDetailsProps } from "../kube-object";
import { PersistentVolumeClaim, pvcApi } from "../../api/endpoints";
import { PersistentVolumeClaim } from "../../api/endpoints";
import { _i18n } from "../../i18n";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<PersistentVolumeClaim> {
}
@@ -95,6 +95,10 @@ export class PersistentVolumeClaimDetails extends React.Component<Props> {
}
}
apiManager.registerViews(pvcApi, {
Details: PersistentVolumeClaimDetails,
})
kubeObjectDetailRegistry.add({
kind: "PersistentVolumeClaim",
apiVersions: ["v1"],
components: {
Details: (props) => <PersistentVolumeClaimDetails {...props} />
}
})
@@ -82,20 +82,7 @@ export class PersistentVolumeClaims extends React.Component<Props> {
{ title: pvc.getStatus(), className: pvc.getStatus().toLowerCase() },
]
}}
renderItemMenu={(item: PersistentVolumeClaim) => {
return <PersistentVolumeClaimMenu object={item}/>
}}
/>
)
}
}
export function PersistentVolumeClaimMenu(props: KubeObjectMenuProps<PersistentVolumeClaim>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(pvcApi, {
Menu: PersistentVolumeClaimMenu,
})
@@ -9,10 +9,10 @@ import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge";
import { KubeEventDetails } from "../+events/kube-event-details";
import { getDetailsUrl } from "../../navigation";
import { PersistentVolume, persistentVolumeApi, pvcApi } from "../../api/endpoints";
import { PersistentVolume, pvcApi } from "../../api/endpoints";
import { KubeObjectDetailsProps } from "../kube-object";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<PersistentVolume> {
}
@@ -103,6 +103,10 @@ export class PersistentVolumeDetails extends React.Component<Props> {
}
}
apiManager.registerViews(persistentVolumeApi, {
Details: PersistentVolumeDetails
})
kubeObjectDetailRegistry.add({
kind: "PersistentVolume",
apiVersions: ["v1"],
components: {
Details: (props) => <PersistentVolumeDetails {...props} />
}
})
@@ -72,20 +72,7 @@ export class PersistentVolumes extends React.Component<Props> {
{ title: volume.getStatus(), className: volume.getStatus().toLowerCase() }
]
}}
renderItemMenu={(item: PersistentVolume) => {
return <PersistentVolumeMenu object={item}/>
}}
/>
)
}
}
export function PersistentVolumeMenu(props: KubeObjectMenuProps<PersistentVolume>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(persistentVolumeApi, {
Menu: PersistentVolumeMenu,
})
@@ -3,7 +3,7 @@ import "./role-binding-details.scss"
import React from "react";
import { t, Trans } from "@lingui/macro";
import { AddRemoveButtons } from "../add-remove-buttons";
import { clusterRoleBindingApi, IRoleBindingSubject, RoleBinding, roleBindingApi } from "../../api/endpoints";
import { IRoleBindingSubject, RoleBinding } from "../../api/endpoints";
import { autobind, prevDefault } from "../../utils";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { ConfirmDialog } from "../confirm-dialog";
@@ -15,8 +15,8 @@ import { roleBindingsStore } from "./role-bindings.store";
import { AddRoleBindingDialog } from "./add-role-binding-dialog";
import { KubeObjectDetailsProps } from "../kube-object";
import { _i18n } from "../../i18n";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<RoleBinding> {
}
@@ -125,6 +125,17 @@ export class RoleBindingDetails extends React.Component<Props> {
}
}
apiManager.registerViews([roleBindingApi, clusterRoleBindingApi], {
Details: RoleBindingDetails,
});
kubeObjectDetailRegistry.add({
kind: "RoleBinding",
apiVersions: ["rbac.authorization.k8s.io/v1"],
components: {
Details: (props) => <RoleBindingDetails {...props} />
}
})
kubeObjectDetailRegistry.add({
kind: "ClusterRoleBinding",
apiVersions: ["rbac.authorization.k8s.io/v1"],
components: {
Details: (props) => <RoleBindingDetails {...props} />
}
})
@@ -54,9 +54,6 @@ export class RoleBindings extends React.Component<Props> {
binding.getNs() || "-",
binding.getAge(),
]}
renderItemMenu={(item: RoleBinding) => {
return <RoleBindingMenu object={item}/>
}}
addRemoveButtons={{
onAdd: () => AddRoleBindingDialog.open(),
addTooltip: <Trans>Create new RoleBinding</Trans>,
@@ -65,13 +62,3 @@ export class RoleBindings extends React.Component<Props> {
)
}
}
export function RoleBindingMenu(props: KubeObjectMenuProps<RoleBinding>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews([roleBindingApi, clusterRoleBindingApi], {
Menu: RoleBindingMenu,
})
@@ -6,9 +6,9 @@ import { DrawerTitle } from "../drawer";
import { KubeEventDetails } from "../+events/kube-event-details";
import { observer } from "mobx-react";
import { KubeObjectDetailsProps } from "../kube-object";
import { clusterRoleApi, Role, roleApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
import { Role } from "../../api/endpoints";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<Role> {
}
@@ -66,6 +66,18 @@ export class RoleDetails extends React.Component<Props> {
}
}
apiManager.registerViews([roleApi, clusterRoleApi], {
Details: RoleDetails,
})
kubeObjectDetailRegistry.add({
kind: "Role",
apiVersions: ["rbac.authorization.k8s.io/v1"],
components: {
Details: (props) => <RoleDetails {...props}/>
}
})
kubeObjectDetailRegistry.add({
kind: "ClusterRole",
apiVersions: ["rbac.authorization.k8s.io/v1"],
components: {
Details: (props) => <RoleDetails {...props}/>
}
})
@@ -5,12 +5,10 @@ import { observer } from "mobx-react";
import { Trans } from "@lingui/macro";
import { RouteComponentProps } from "react-router";
import { IRolesRouteParams } from "../+user-management/user-management.route";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { rolesStore } from "./roles.store";
import { clusterRoleApi, Role, roleApi } from "../../api/endpoints";
import { Role } from "../../api/endpoints";
import { KubeObjectListLayout } from "../kube-object";
import { AddRoleDialog } from "./add-role-dialog";
import { apiManager } from "../../api/api-manager";
enum sortBy {
name = "name",
@@ -48,9 +46,6 @@ export class Roles extends React.Component<Props> {
role.getNs() || "-",
role.getAge(),
]}
renderItemMenu={(item: Role) => {
return <RoleMenu object={item}/>
}}
addRemoveButtons={{
onAdd: () => AddRoleDialog.open(),
addTooltip: <Trans>Create new Role</Trans>,
@@ -61,13 +56,3 @@ export class Roles extends React.Component<Props> {
)
}
}
export function RoleMenu(props: KubeObjectMenuProps<Role>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews([roleApi, clusterRoleApi], {
Menu: RoleMenu,
});
@@ -9,13 +9,13 @@ import { DrawerItem, DrawerTitle } from "../drawer";
import { disposeOnUnmount, observer } from "mobx-react";
import { secretsStore } from "../+config-secrets/secrets.store";
import { Link } from "react-router-dom";
import { Secret, ServiceAccount, serviceAccountsApi } from "../../api/endpoints";
import { Secret, ServiceAccount } from "../../api/endpoints";
import { KubeEventDetails } from "../+events/kube-event-details";
import { getDetailsUrl } from "../../navigation";
import { KubeObjectDetailsProps } from "../kube-object";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { Icon } from "../icon";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<ServiceAccount> {
}
@@ -132,6 +132,10 @@ export class ServiceAccountsDetails extends React.Component<Props> {
}
}
apiManager.registerViews(serviceAccountsApi, {
Details: ServiceAccountsDetails
})
kubeObjectDetailRegistry.add({
kind: "ServiceAccount",
apiVersions: ["v1"],
components: {
Details: (props) => <ServiceAccountsDetails {...props} />
}
})
@@ -13,7 +13,7 @@ import { KubeObjectListLayout } from "../kube-object";
import { IServiceAccountsRouteParams } from "../+user-management";
import { serviceAccountsStore } from "./service-accounts.store";
import { CreateServiceAccountDialog } from "./create-service-account-dialog";
import { apiManager } from "../../api/api-manager";
import { kubeObjectMenuRegistry } from "../../api/kube-object-menu-registry";
enum sortBy {
name = "name",
@@ -64,18 +64,20 @@ export class ServiceAccounts extends React.Component<Props> {
}
}
export function ServiceAccountMenu(props: KubeObjectMenuProps<ServiceAccount>) {
function ServiceAccountMenu(props: KubeObjectMenuProps<ServiceAccount>) {
const { object, toolbar } = props;
return (
<KubeObjectMenu {...props}>
<MenuItem onClick={() => openServiceAccountKubeConfig(object)}>
<Icon material="insert_drive_file" title="Kubeconfig File" interactive={toolbar}/>
<span className="title"><Trans>Kubeconfig</Trans></span>
</MenuItem>
</KubeObjectMenu>
<MenuItem onClick={() => openServiceAccountKubeConfig(object)}>
<Icon material="insert_drive_file" title="Kubeconfig File" interactive={toolbar}/>
<span className="title"><Trans>Kubeconfig</Trans></span>
</MenuItem>
)
}
apiManager.registerViews(serviceAccountsApi, {
Menu: ServiceAccountMenu,
kubeObjectMenuRegistry.add({
kind: "ServiceAccount",
apiVersions: ["v1"],
components: {
MenuItem: ServiceAccountMenu
}
})
@@ -12,9 +12,9 @@ import { KubeEventDetails } from "../+events/kube-event-details";
import { cronJobStore } from "./cronjob.store";
import { getDetailsUrl } from "../../navigation";
import { KubeObjectDetailsProps } from "../kube-object";
import { CronJob, cronJobApi, Job } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
import { CronJob, Job } from "../../api/endpoints";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<CronJob> {
}
@@ -87,6 +87,10 @@ export class CronJobDetails extends React.Component<Props> {
}
}
apiManager.registerViews(cronJobApi, {
Details: CronJobDetails,
})
kubeObjectDetailRegistry.add({
kind: "CronJob",
apiVersions: ["batch/v1"],
components: {
Details: (props) => <CronJobDetails {...props} />
}
})
@@ -15,8 +15,8 @@ import { ICronJobsRouteParams } from "../+workloads";
import { KubeObjectListLayout } from "../kube-object";
import { KubeEventIcon } from "../+events/kube-event-icon";
import { _i18n } from "../../i18n";
import { apiManager } from "../../api/api-manager";
import { CronJobTriggerDialog } from "./cronjob-trigger-dialog";
import { kubeObjectMenuRegistry } from "../../api/kube-object-menu-registry";
enum sortBy {
name = "name",
@@ -85,15 +85,17 @@ export class CronJobs extends React.Component<Props> {
export function CronJobMenu(props: KubeObjectMenuProps<CronJob>) {
const { object, toolbar } = props;
return (
<KubeObjectMenu {...props}>
<MenuItem onClick={() => CronJobTriggerDialog.open(object)}>
<Icon material="play_circle_filled" title={_i18n._(t`Trigger`)} interactive={toolbar}/>
<span className="title"><Trans>Trigger</Trans></span>
</MenuItem>
</KubeObjectMenu>
<MenuItem onClick={() => CronJobTriggerDialog.open(object)}>
<Icon material="play_circle_filled" title={_i18n._(t`Trigger`)} interactive={toolbar}/>
<span className="title"><Trans>Trigger</Trans></span>
</MenuItem>
)
}
apiManager.registerViews(cronJobApi, {
Menu: CronJobMenu,
kubeObjectMenuRegistry.add({
kind: "CronJob",
apiVersions: ["batch/v1beta1"],
components: {
MenuItem: CronJobMenu
}
})
@@ -12,13 +12,13 @@ import { KubeEventDetails } from "../+events/kube-event-details";
import { daemonSetStore } from "./daemonsets.store";
import { podsStore } from "../+workloads-pods/pods.store";
import { KubeObjectDetailsProps } from "../kube-object";
import { DaemonSet, daemonSetApi } from "../../api/endpoints";
import { DaemonSet } from "../../api/endpoints";
import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics";
import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts";
import { reaction } from "mobx";
import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<DaemonSet> {
}
@@ -97,6 +97,10 @@ export class DaemonSetDetails extends React.Component<Props> {
}
}
apiManager.registerViews(daemonSetApi, {
Details: DaemonSetDetails,
})
kubeObjectDetailRegistry.add({
kind: "DaemonSet",
apiVersions: ["apps/v1"],
components: {
Details: (props: any) => <DaemonSetDetails {...props} />
}
})
@@ -70,20 +70,8 @@ export class DaemonSets extends React.Component<Props> {
this.renderNodeSelector(daemonSet),
daemonSet.getAge(),
]}
renderItemMenu={(item: DaemonSet) => {
return <DaemonSetMenu object={item}/>
}}
/>
)
}
}
export function DaemonSetMenu(props: KubeObjectMenuProps<DaemonSet>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(daemonSetApi, {
Menu: DaemonSetMenu,
})
@@ -21,8 +21,8 @@ import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts";
import { reaction } from "mobx";
import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { ReplicaSets } from "../+workloads-replicasets";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<Deployment> {
}
@@ -122,6 +122,10 @@ export class DeploymentDetails extends React.Component<Props> {
}
}
apiManager.registerViews(deploymentApi, {
Details: DeploymentDetails
})
kubeObjectDetailRegistry.add({
kind: "Deployment",
apiVersions: ["apps/v1"],
components: {
Details: (props: any) => <DeploymentDetails {...props} />
}
})
@@ -21,7 +21,8 @@ import { cssNames } from "../../utils";
import kebabCase from "lodash/kebabCase";
import orderBy from "lodash/orderBy";
import { KubeEventIcon } from "../+events/kube-event-icon";
import { apiManager } from "../../api/api-manager";
import { kubeObjectMenuRegistry } from "../../api/kube-object-menu-registry";
import { DeploymentDetails } from "./deployment-details";
enum sortBy {
name = "name",
@@ -96,15 +97,18 @@ export class Deployments extends React.Component<Props> {
export function DeploymentMenu(props: KubeObjectMenuProps<Deployment>) {
const { object, toolbar } = props;
return (
<KubeObjectMenu {...props}>
<MenuItem onClick={() => DeploymentScaleDialog.open(object)}>
<Icon material="open_with" title={_i18n._(t`Scale`)} interactive={toolbar}/>
<span className="title"><Trans>Scale</Trans></span>
</MenuItem>
</KubeObjectMenu>
<MenuItem onClick={() => DeploymentScaleDialog.open(object)}>
<Icon material="open_with" title={_i18n._(t`Scale`)} interactive={toolbar}/>
<span className="title"><Trans>Scale</Trans></span>
</MenuItem>
)
}
apiManager.registerViews(deploymentApi, {
Menu: DeploymentMenu,
});
kubeObjectMenuRegistry.add({
kind: "Deployment",
apiVersions: ["apps/v1"],
components: {
MenuItem: DeploymentMenu
}
})
@@ -15,11 +15,11 @@ import { podsStore } from "../+workloads-pods/pods.store";
import { jobStore } from "./job.store";
import { getDetailsUrl } from "../../navigation";
import { KubeObjectDetailsProps } from "../kube-object";
import { Job, jobApi } from "../../api/endpoints";
import { Job } from "../../api/endpoints";
import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { lookupApiLink } from "../../api/kube-api";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<Job> {
}
@@ -107,6 +107,10 @@ export class JobDetails extends React.Component<Props> {
}
}
apiManager.registerViews(jobApi, {
Details: JobDetails
});
kubeObjectDetailRegistry.add({
kind: "Job",
apiVersions: ["batch/v1"],
components: {
Details: (props: any) => <JobDetails {...props}/>
}
})
@@ -64,20 +64,7 @@ export class Jobs extends React.Component<Props> {
}
]
}}
renderItemMenu={(item: Job) => {
return <JobMenu object={item}/>
}}
/>
)
}
}
export function JobMenu(props: KubeObjectMenuProps<Job>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(jobApi, {
Menu: JobMenu,
})
@@ -6,7 +6,7 @@ import { disposeOnUnmount, observer } from "mobx-react";
import { Link } from "react-router-dom";
import { autorun, observable, reaction, toJS } from "mobx";
import { Trans } from "@lingui/macro";
import { IPodMetrics, nodesApi, Pod, podsApi, pvcApi, configMapApi } from "../../api/endpoints";
import { IPodMetrics, nodesApi, Pod, pvcApi, configMapApi } from "../../api/endpoints";
import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge";
import { autobind, cssNames, interval } from "../../utils";
@@ -22,9 +22,8 @@ import { getDetailsUrl } from "../../navigation";
import { KubeObjectDetailsProps } from "../kube-object";
import { getItemMetrics } from "../../api/endpoints/metrics.api";
import { PodCharts, podMetricTabs } from "./pod-charts";
import { lookupApiLink } from "../../api/kube-api";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<Pod> {
}
@@ -223,6 +222,10 @@ export class PodDetails extends React.Component<Props> {
}
}
apiManager.registerViews(podsApi, {
Details: PodDetails
kubeObjectDetailRegistry.add({
kind: "Pod",
apiVersions: ["v1"],
components: {
Details: (props: any) => <PodDetails {...props} />
}
})
@@ -1,10 +0,0 @@
.PodMenu {
.StatusBrick {
margin-right: $margin;
@include pod-status-bgs;
&.running:not(.ready) {
background-color: $pod-status-pending-color;
}
}
}
@@ -1,134 +0,0 @@
import "./pod-menu.scss";
import React from "react";
import { t, Trans } from "@lingui/macro";
import { MenuItem, SubMenu } from "../menu";
import { IPodContainer, Pod } from "../../api/endpoints";
import { Icon } from "../icon";
import { StatusBrick } from "../status-brick";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { cssNames, prevDefault } from "../../utils";
import { terminalStore, createTerminalTab } from "../dock/terminal.store";
import { _i18n } from "../../i18n";
import { hideDetails } from "../../navigation";
import { createPodLogsTab } from "../dock/pod-logs.store";
interface Props extends KubeObjectMenuProps<Pod> {
}
export class PodMenu extends React.Component<Props> {
async execShell(container?: string) {
hideDetails();
const { object: pod } = this.props
const containerParam = container ? `-c ${container}` : ""
let command = `kubectl exec -i -t -n ${pod.getNs()} ${pod.getName()} ${containerParam} "--"`
if (window.navigator.platform !== "Win32") {
command = `exec ${command}`
}
if (pod.getSelectedNodeOs() === "windows") {
command = `${command} powershell`
} else {
command = `${command} sh -c "clear; (bash || ash || sh)"`
}
const shell = createTerminalTab({
title: _i18n._(t`Pod`) + `: ${pod.getName()} (namespace: ${pod.getNs()})`
});
terminalStore.sendCommand(command, {
enter: true,
tabId: shell.id
});
}
showLogs(container: IPodContainer) {
hideDetails();
const pod = this.props.object;
createPodLogsTab({
pod,
containers: pod.getContainers(),
initContainers: pod.getInitContainers(),
selectedContainer: container,
showTimestamps: false,
previous: false,
tailLines: 1000
});
}
renderShellMenu() {
const { object: pod, toolbar } = this.props
const containers = pod.getRunningContainers();
if (!containers.length) return;
return (
<MenuItem onClick={prevDefault(() => this.execShell(containers[0].name))}>
<Icon svg="ssh" interactive={toolbar} title={_i18n._(t`Pod shell`)}/>
<span className="title"><Trans>Shell</Trans></span>
{containers.length > 1 && (
<>
<Icon className="arrow" material="keyboard_arrow_right"/>
<SubMenu>
{
containers.map(container => {
const { name } = container;
return (
<MenuItem key={name} onClick={prevDefault(() => this.execShell(name))} className="flex align-center">
<StatusBrick/>
{name}
</MenuItem>
)
})
}
</SubMenu>
</>
)}
</MenuItem>
)
}
renderLogsMenu() {
const { object: pod, toolbar } = this.props
const containers = pod.getAllContainers();
const statuses = pod.getContainerStatuses();
if (!containers.length) return;
return (
<MenuItem onClick={prevDefault(() => this.showLogs(containers[0]))}>
<Icon material="subject" title={_i18n._(t`Logs`)} interactive={toolbar}/>
<span className="title"><Trans>Logs</Trans></span>
{containers.length > 1 && (
<>
<Icon className="arrow" material="keyboard_arrow_right"/>
<SubMenu>
{
containers.map(container => {
const { name } = container
const status = statuses.find(status => status.name === name);
const brick = status ? (
<StatusBrick
className={cssNames(Object.keys(status.state)[0], { ready: status.ready })}
/>
) : null
return (
<MenuItem key={name} onClick={prevDefault(() => this.showLogs(container))} className="flex align-center">
{brick}
{name}
</MenuItem>
)
})
}
</SubMenu>
</>
)}
</MenuItem>
)
}
render() {
const { ...menuProps } = this.props;
return (
<KubeObjectMenu {...menuProps} className="PodMenu">
{this.renderShellMenu()}
{this.renderLogsMenu()}
</KubeObjectMenu>
)
}
}
@@ -10,8 +10,7 @@ import { volumeClaimStore } from "../+storage-volume-claims/volume-claim.store";
import { IPodsRouteParams } from "../+workloads";
import { eventStore } from "../+events/event.store";
import { KubeObjectListLayout } from "../kube-object";
import { Pod, podsApi } from "../../api/endpoints";
import { PodMenu } from "./pod-menu";
import { Pod } from "../../api/endpoints";
import { StatusBrick } from "../status-brick";
import { cssNames, stopPropagation } from "../../utils";
import { KubeEventIcon } from "../+events/kube-event-icon";
@@ -20,7 +19,6 @@ import toPairs from "lodash/toPairs";
import startCase from "lodash/startCase";
import kebabCase from "lodash/kebabCase";
import { lookupApiLink } from "../../api/kube-api";
import { apiManager } from "../../api/api-manager";
enum sortBy {
name = "name",
@@ -119,14 +117,7 @@ export class Pods extends React.Component<Props> {
pod.getAge(),
{ title: pod.getStatusMessage(), className: kebabCase(pod.getStatusMessage()) }
]}
renderItemMenu={(item: Pod) => {
return <PodMenu object={item}/>
}}
/>
)
}
}
apiManager.registerViews(podsApi, {
Menu: PodMenu,
})
@@ -12,12 +12,12 @@ import { KubeEventDetails } from "../+events/kube-event-details";
import { disposeOnUnmount, observer } from "mobx-react";
import { podsStore } from "../+workloads-pods/pods.store";
import { KubeObjectDetailsProps } from "../kube-object";
import { ReplicaSet, replicaSetApi } from "../../api/endpoints";
import { ReplicaSet } from "../../api/endpoints";
import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics";
import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts";
import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<ReplicaSet> {
}
@@ -97,6 +97,10 @@ export class ReplicaSetDetails extends React.Component<Props> {
}
}
apiManager.registerViews(replicaSetApi, {
Details: ReplicaSetDetails,
})
kubeObjectDetailRegistry.add({
kind: "ReplicaSet",
apiVersions: ["apps/v1"],
components: {
Details: (props: any) => <ReplicaSetDetails {...props} />
}
})
@@ -92,7 +92,3 @@ export function ReplicaSetMenu(props: KubeObjectMenuProps<ReplicaSet>) {
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(replicaSetApi, {
Menu: ReplicaSetMenu,
});
@@ -13,12 +13,12 @@ import { KubeEventDetails } from "../+events/kube-event-details";
import { podsStore } from "../+workloads-pods/pods.store";
import { statefulSetStore } from "./statefulset.store";
import { KubeObjectDetailsProps } from "../kube-object";
import { StatefulSet, statefulSetApi } from "../../api/endpoints";
import { StatefulSet } from "../../api/endpoints";
import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics";
import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts";
import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
interface Props extends KubeObjectDetailsProps<StatefulSet> {
}
@@ -95,6 +95,11 @@ export class StatefulSetDetails extends React.Component<Props> {
}
}
apiManager.registerViews(statefulSetApi, {
Details: StatefulSetDetails
})
kubeObjectDetailRegistry.add({
kind: "StatefulSet",
apiVersions: ["apps/v1"],
components: {
Details: (props: any) => <StatefulSetDetails {...props} />
}
})
@@ -60,20 +60,7 @@ export class StatefulSets extends React.Component<Props> {
<KubeEventIcon object={statefulSet}/>,
statefulSet.getAge(),
]}
renderItemMenu={(item: StatefulSet) => {
return <StatefulSetMenu object={item}/>
}}
/>
)
}
}
export function StatefulSetMenu(props: KubeObjectMenuProps<StatefulSet>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(statefulSetApi, {
Menu: StatefulSetMenu,
})
@@ -8,9 +8,11 @@ import { getDetails, hideDetails } from "../../navigation";
import { Drawer } from "../drawer";
import { KubeObject } from "../../api/kube-object";
import { Spinner } from "../spinner";
import { apiManager, ApiComponents } from "../../api/api-manager";
import { apiManager } from "../../api/api-manager";
import { crdStore } from "../+custom-resources/crd.store";
import { CrdResourceDetails, CrdResourceMenu } from "../+custom-resources";
import { CrdResourceDetails } from "../+custom-resources";
import { KubeObjectMenu } from "./kube-object-menu"
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
export interface KubeObjectDetailsProps<T = KubeObject> {
className?: string;
@@ -64,14 +66,15 @@ export class KubeObjectDetails extends React.Component {
const { object, isLoading, loadingError, isCrdInstance } = this;
const isOpen = !!(object || isLoading || loadingError);
let title = "";
let apiComponents: ApiComponents;
let details: JSX.Element[];
if (object) {
const { kind, getName, selfLink } = object;
const { kind, getName } = object;
title = `${kind}: ${getName()}`;
apiComponents = apiManager.getViews(selfLink);
if (isCrdInstance && !apiComponents.Details) {
apiComponents.Details = CrdResourceDetails
apiComponents.Menu = CrdResourceMenu
details = kubeObjectDetailRegistry.getItemsForKind(object.kind, object.apiVersion).map((item, index) => {
return <item.components.Details object={object} key={`object-details-${index}`}/>
})
if (isCrdInstance && details.length === 0) {
details.push(<CrdResourceDetails object={object} />)
}
}
return (
@@ -79,12 +82,12 @@ export class KubeObjectDetails extends React.Component {
className="KubeObjectDetails flex column"
open={isOpen}
title={title}
toolbar={apiComponents && apiComponents.Menu && <apiComponents.Menu object={object} toolbar/>}
toolbar={<KubeObjectMenu object={object} toolbar={true} />}
onClose={hideDetails}
>
{isLoading && <Spinner center/>}
{loadingError && <div className="box center">{loadingError}</div>}
{apiComponents && apiComponents.Details && <apiComponents.Details object={object}/>}
{details}
</Drawer>
)
}
@@ -6,6 +6,7 @@ import { KubeObject } from "../../api/kube-object";
import { getSelectedDetails, showDetails } from "../../navigation";
import { ItemListLayout, ItemListLayoutProps } from "../item-object-list/item-list-layout";
import { KubeObjectStore } from "../../kube-object.store";
import { KubeObjectMenu } from "./kube-object-menu";
export interface KubeObjectListLayoutProps extends ItemListLayoutProps {
store: KubeObjectStore;
@@ -34,6 +35,9 @@ export class KubeObjectListLayout extends React.Component<KubeObjectListLayoutPr
className={cssNames("KubeObjectListLayout", className)}
detailsItem={this.selectedItem}
onDetails={this.onDetails}
renderItemMenu={(item) => {
return <KubeObjectMenu object={item}/>
}}
/>
);
}
@@ -6,6 +6,7 @@ import { editResourceTab } from "../dock/edit-resource.store";
import { MenuActions, MenuActionsProps } from "../menu/menu-actions";
import { hideDetails } from "../../navigation";
import { apiManager } from "../../api/api-manager";
import { kubeObjectMenuRegistry } from "../../api/kube-object-menu-registry";
export interface KubeObjectMenuProps<T extends KubeObject = any> extends MenuActionsProps {
object: T;
@@ -57,6 +58,11 @@ export class KubeObjectMenu extends React.Component<KubeObjectMenuProps> {
render() {
const { remove, update, renderRemoveMessage, isEditable, isRemovable } = this;
const { className, object, editable, removable, ...menuProps } = this.props;
if (!object) return null;
const menuItems = kubeObjectMenuRegistry.getItemsForKind(object.kind, object.apiVersion).map((item, index) => {
return <item.components.MenuItem object={object} key={`menu-item-${index}`} />
})
return (
<MenuActions
className={cssNames("KubeObjectMenu", className)}
@@ -64,7 +70,9 @@ export class KubeObjectMenu extends React.Component<KubeObjectMenuProps> {
removeAction={isRemovable ? remove : undefined}
removeConfirmationMessage={renderRemoveMessage}
{...menuProps}
/>
>
{menuItems}
</MenuActions>
)
}
}