Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat & fix: Event, Ingress and Secret Overview & Errors in using next build #4374

Merged
merged 9 commits into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-basic-auth
type: kubernetes.io/basic-auth
stringData:
username: admin
password: t0p-Secret
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const PieChart = ({ title, data, color }: PieChartProps) => {
<div className="font-medium text-base text-black">{title}</div>
<div className="font-medium text-4xl text-black">{sum(data.map((d) => d.value))}</div>
</div>
);
) as unknown as string;
}
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
interface SectionProps {
children: React.ReactNode;
}

/**
* Renders a section component with the provided children.
*
* @param {SectionProps} children - The children to render within the section.
* @return {JSX.Element} The rendered section component.
*/
export function Section({ children }: SectionProps) {
return (
<section className="w-full bg-white px-10 py-8 flex flex-col flex-wrap gap-3">
{children}
</section>
);
}
22 changes: 22 additions & 0 deletions frontend/plugins/kubepanel/src/components/common/title/title.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
interface PageTitleProps {
children: string;
type: 'primary' | 'secondary' | 'table';
}

const titleClassName: Record<PageTitleProps['type'], string> = {
primary: 'text-2xl font-medium',
secondary: 'text-xl font-medium',
table: 'text-xs font-medium text-gray-500'
};

/**
* Renders a title component with the specified type and children.
*
* @param {PageTitleProps} props - The properties of the title component.
* @param {ReactNode} props.children - The content of the title component.
* @param {string} props.type - The type of the title component.
* @return {JSX.Element} The rendered title component.
*/
export default function Title({ children, type }: PageTitleProps) {
return <span className={titleClassName[type]}>{children}</span>;
}
12 changes: 6 additions & 6 deletions frontend/plugins/kubepanel/src/components/kube/kube-badge.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from 'react';

export interface KubeBadgeProps {
interface KubeBadgeProps {
label: React.ReactNode;
disabled?: boolean;
expandable?: boolean;
Expand Down Expand Up @@ -32,14 +32,14 @@ export const KubeBadge = ({
};

const { textColor, backgroundColor } = color ?? {};
const disabledClass = disabled ? 'opacity-50 cursor-not-allowed' : 'opacity-100 cursor-pointer';
const disabledClass = disabled && 'opacity-50 cursor-not-allowed';
const expandedClass = isExpanded ? 'break-words' : 'truncate';
const expandableClass = isExpandable && 'cursor-pointer';
const bgColorClass = backgroundColor ? `bg-${backgroundColor}` : 'bg-[#EFF0F1]';

return (
<div
className={`inline-block py-2 px-2 mr-1 my-1 max-w-full rounded-[4px] ${disabledClass}
${backgroundColor ? `bg-${backgroundColor}` : 'bg-[#EFF0F1]'} ${
isExpanded ? 'break-words' : 'truncate'
} `}
className={`inline-block py-1 px-1.5 mr-1 mb-1 max-w-full rounded-[4px] ${disabledClass} ${expandableClass} ${expandedClass} ${bgColorClass}`}
ref={elem}
onClick={onClick}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DrawerItem } from '../../../../pages/kubepanel/components/drawer/drawer-item';
import DrawerItem from '../../../../pages/kubepanel/components/drawer/drawer-item';
import { KubeObjectAge } from '../kube-object-age';
import { LocaleDate } from '../../local-date';
import moment from 'moment-timezone';
Expand Down
18 changes: 18 additions & 0 deletions frontend/plugins/kubepanel/src/constants/kube-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,23 @@ export const ApiBaseParamsMap: Record<ResourceKey, UrlParams> = {
apiGroup: undefined,
apiVersion: 'v1',
resource: Resources.PersistentVolumeClaims
},
secrets: {
apiPrefix: 'api',
apiGroup: undefined,
apiVersion: 'v1',
resource: Resources.Secrets
},
ingresses: {
apiPrefix: 'apis',
apiGroup: 'networking.k8s.io',
apiVersion: 'v1',
resource: Resources.Ingresses
},
events: {
apiPrefix: 'api',
apiGroup: undefined,
apiVersion: 'v1',
resource: Resources.Events
}
};
18 changes: 15 additions & 3 deletions frontend/plugins/kubepanel/src/constants/kube-object.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import {
ConfigMap,
Deployment,
Ingress,
KubeEvent,
PersistentVolumeClaim,
Pod,
Secret,
StatefulSet
} from '@/k8slens/kube-object';
import { StringKeyOf } from 'type-fest';
Expand All @@ -14,21 +17,30 @@ export enum Resources {
Deployments = 'deployments',
StatefulSets = 'statefulsets',
ConfigMaps = 'configmaps',
PersistentVolumeClaims = 'persistentvolumeclaims'
PersistentVolumeClaims = 'persistentvolumeclaims',
Secrets = 'secrets',
Ingresses = 'ingresses',
Events = 'events'
}

export const KubeObjectConstructorMap: { [key in Resources]: any } = {
[Resources.Pods]: Pod,
[Resources.Deployments]: Deployment,
[Resources.StatefulSets]: StatefulSet,
[Resources.ConfigMaps]: ConfigMap,
[Resources.PersistentVolumeClaims]: PersistentVolumeClaim
[Resources.PersistentVolumeClaims]: PersistentVolumeClaim,
[Resources.Secrets]: Secret,
[Resources.Ingresses]: Ingress,
[Resources.Events]: KubeEvent
};

export const KindMap: { [key in Resources]: any } = {
[Resources.Pods]: 'Pod',
[Resources.Deployments]: 'Deployment',
[Resources.StatefulSets]: 'StatefulSet',
[Resources.ConfigMaps]: 'ConfigMap',
[Resources.PersistentVolumeClaims]: 'PersistentVolumeClaim'
[Resources.PersistentVolumeClaims]: 'PersistentVolumeClaim',
[Resources.Secrets]: 'Secret',
[Resources.Ingresses]: 'Ingress',
[Resources.Events]: 'Event'
};
4 changes: 4 additions & 0 deletions frontend/plugins/kubepanel/src/constants/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export const theme: ThemeConfig = {
},
Collapse: {
headerPadding: 0
},
Table: {
headerBorderRadius: 0,
headerBg: '#F6F8F9'
}
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { editor } from 'monaco-editor';
import { useEffect, useRef, useState } from 'react';

interface Props<K extends KubeObject> {
obj: K;
obj?: K;
open: boolean;
onUpdate: (data: string) => Promise<ApiResp>;
onCancel: () => void;
Expand All @@ -21,6 +21,8 @@ const UpdateEditorModal = <K extends KubeObject = KubeObject>({
onCancel,
onOk
}: Props<K>) => {
if (!obj) return null;

const [clickedUpdate, setClickedUpdate] = useState(false);
const [msgApi, contextHolder] = message.useMessage();
const editorRef = useRef<editor.IStandaloneCodeEditor>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DrawerItem } from '@/pages/kubepanel/components/drawer/drawer-item';
import DrawerItem from '@/pages/kubepanel/components/drawer/drawer-item';
import { Collapse } from 'antd';

interface Props {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,18 @@ interface DrawerItemProps {
padding?: boolean;
}

export const DrawerItem = ({
name,
value,
hidden = false,
}: DrawerItemProps) => {
const DrawerItem = ({ name, value, hidden = false }: DrawerItemProps) => {
if (hidden) return null;

return (
<div
className={`grid last:border-b-0`}
style={{ gridTemplateColumns: 'minmax(30%, min-content) auto' }}
>
<span className="overflow-hidden text-ellipsis text-[#5A646E] font-semibold">
{name}
</span>
<span className="max-w-full min-w-0 text-[#24282C]">
{value}
</span>
<span className="overflow-hidden text-ellipsis text-[#5A646E] font-semibold">{name}</span>
<span className="max-w-full min-w-0 text-[#24282C]">{value}</span>
</div>
);
};

export default DrawerItem;
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ interface Props {
const Drawer = ({ title, children, open, onClose }: Props) => {
return (
<AntdDrawer
classNames={{ header: 'bg-[#24282C]' }}
styles={{
header: {
padding: '12px',
backgroundColor: '#24282C'
}
}}
open={open}
closeIcon={<CloseOutlined style={{ color: 'white', fontSize: '32px' }} />}
title={<span className="pl-2 text-white font-medium text-base">{title}</span>}
closeIcon={<CloseOutlined style={{ color: 'white', fontSize: '24px', padding: '4px' }}/>}
title={<span className="text-white font-medium text-base">{title}</span>}
width="550px"
onClose={onClose}
destroyOnClose
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { Secret } from '@/k8slens/kube-object';
import DrawerPanel from '../../../drawer/drawer-panel';
import { KubeObjectInfoList } from '@/components/kube/object/detail/kube-object-detail-info-list';
import Drawer from '../../../drawer/drawer';
import React, { Suspense } from 'react';
import { Button, Input, Space } from 'antd';
import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons';
import { DetailDrawerProps } from '@/types/detail';
import { entries } from 'lodash';
import { updateResource } from '@/api/update';
import { Resources } from '@/constants/kube-object';
import { dumpKubeObject } from '@/utils/yaml';
import useMessage from 'antd/lib/message/useMessage';

type RevealableInput = {
name: string;
initValue: string;
onChange?: (value: string) => void;
};

const SecretDataInput = ({ name, initValue, onChange }: RevealableInput) => {
const [revealed, setRevealed] = React.useState(false);
const [value, setValue] = React.useState(initValue);
return (
<div>
<div className="mb-1">{name}</div>
<Space>
<Input.TextArea
disabled={!revealed}
autoSize={{ maxRows: 20 }}
styles={{
textarea: {
fontFamily: 'monospace',
lineHeight: 1.2,
width: '478px'
}
}}
onChange={(e) => {
setValue(e.target.value);
onChange?.(e.target.value);
}}
value={revealed ? value : ''}
/>
<Button
icon={revealed ? <EyeInvisibleOutlined /> : <EyeOutlined />}
type="text"
onClick={() => setRevealed(!revealed)}
/>
</Space>
</div>
);
};

const SecretDataForm = ({ secret }: { secret: Secret }) => {
const [isSaving, setIsSaving] = React.useState(false);
const [msgApi, contextHolder] = useMessage({});
const dataMap = React.useRef<Partial<Record<string, string>>>(secret.data).current;

return (
<>
{contextHolder}
{entries(secret.data).map(([name, value]) => (
<SecretDataInput
key={name}
name={name}
initValue={Buffer.from(value || '', 'base64').toString()}
onChange={(value) => {
dataMap[name] = Buffer.from(value, 'ascii').toString('base64');
}}
/>
))}
<Suspense>
<Button
style={{ width: 'max-content' }}
loading={isSaving}
onClick={() => {
setIsSaving(true);
updateResource(
dumpKubeObject<Secret>({
...secret,
data: dataMap
}),
secret.getName(),
Resources.Secrets
)
.then((res) => {
if (res.code >= 300 || res.code < 200) msgApi.error(res.message);
else msgApi.success('Successfully Saved!');
})
.finally(() => {
setIsSaving(false);
});
}}
>
Save
</Button>
</Suspense>
</>
);
};

const SecretDetail = ({ obj, open, onClose }: DetailDrawerProps<Secret>) => {
if (!obj || !(obj instanceof Secret)) return null;

return (
<Drawer open={open} title={`Secret: ${obj.getName()}`} onClose={onClose}>
<DrawerPanel>
<KubeObjectInfoList obj={obj} />
</DrawerPanel>
<DrawerPanel title="Data">
<SecretDataForm secret={obj} />
</DrawerPanel>
</Drawer>
);
};

export default SecretDetail;
Loading