Skip to content

Commit 79cec6a

Browse files
authored
🔥 add k8s role resource (#789)
1 parent aa1464c commit 79cec6a

File tree

2 files changed

+163
-0
lines changed

2 files changed

+163
-0
lines changed

cyclops-ui/src/components/k8s-resources/ResourceList/ResourceList.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import StatefulSet from "../StatefulSet";
2727
import Pod from "../Pod";
2828
import Service from "../Service";
2929
import ClusterRole from "../ClusterRole";
30+
import Role from "../Role";
3031
import ConfigMap from "../ConfigMap";
3132
import PersistentVolumeClaim from "../PersistentVolumeClaim";
3233
import Secret from "../Secret";
@@ -368,6 +369,13 @@ const ResourceList = ({
368369
resource.kind === "ClusterRole":
369370
resourceDetails = <ClusterRole name={resource.name} />;
370371
break;
372+
case resource.group === "rbac.authorization.k8s.io" &&
373+
resource.version === "v1" &&
374+
resource.kind === "Role":
375+
resourceDetails = (
376+
<Role name={resource.name} namespace={resource.namespace} />
377+
);
378+
break;
371379
case resource.group === "networking.k8s.io" &&
372380
resource.version === "v1" &&
373381
resource.kind === "NetworkPolicy":
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import { Col, Divider, Row, Alert, Table, Tag, Spin } from "antd";
2+
import React, { useCallback, useEffect, useState } from "react";
3+
import { mapResponseError } from "../../utils/api/errors";
4+
import { useResourceListActions } from "./ResourceList/ResourceListActionsContext";
5+
6+
interface Props {
7+
name: string;
8+
namespace: string;
9+
}
10+
11+
interface Rule {
12+
verbs?: string[];
13+
apiGroups?: string[];
14+
resources?: string[];
15+
nonResourceURLs?: string[];
16+
}
17+
18+
interface RoleData {
19+
rules: Rule[];
20+
}
21+
22+
const Role = ({ name, namespace }: Props) => {
23+
const [loading, setLoading] = useState(true);
24+
const { fetchResource } = useResourceListActions();
25+
26+
const [role, setRole] = useState<RoleData>({
27+
rules: [],
28+
});
29+
30+
const [error, setError] = useState({
31+
message: "",
32+
description: "",
33+
});
34+
35+
const fetchRole = useCallback(() => {
36+
fetchResource("rbac.authorization.k8s.io", "v1", "Role", namespace, name)()
37+
.then((res) => {
38+
setRole({
39+
rules: res.rules || [],
40+
});
41+
setLoading(false);
42+
})
43+
.catch((error) => {
44+
setError(mapResponseError(error));
45+
setLoading(false);
46+
});
47+
}, [name, namespace, fetchResource]);
48+
49+
useEffect(() => {
50+
fetchRole();
51+
52+
const interval = setInterval(() => fetchRole(), 15000);
53+
return () => {
54+
clearInterval(interval);
55+
};
56+
}, [fetchRole]);
57+
58+
const columns = [
59+
{
60+
title: "Verbs",
61+
dataIndex: "verbs",
62+
key: "verbs",
63+
render: (verbs?: string[]) => (
64+
<>
65+
{verbs?.map((verb) => (
66+
<Tag key={verb} color="orange">
67+
{verb}
68+
</Tag>
69+
))}
70+
</>
71+
),
72+
},
73+
{
74+
title: "API Groups",
75+
dataIndex: "apiGroups",
76+
key: "apiGroups",
77+
render: (apiGroups?: string[]) => (
78+
<>
79+
{apiGroups?.map((group) => (
80+
<Tag key={group} color="blue">
81+
{group || "*"}
82+
</Tag>
83+
))}
84+
</>
85+
),
86+
},
87+
{
88+
title: "Resources",
89+
dataIndex: "resources",
90+
key: "resources",
91+
render: (resources?: string[]) => (
92+
<>
93+
{resources?.map((resource) => (
94+
<Tag key={resource} color="green">
95+
{resource}
96+
</Tag>
97+
))}
98+
</>
99+
),
100+
},
101+
];
102+
103+
if (role.rules.some((rule) => rule.nonResourceURLs)) {
104+
columns.push({
105+
title: "Non resource URLs",
106+
dataIndex: "nonResourceURLs",
107+
key: "nonResourceURLs",
108+
render: (nonResourceURLs?: string[]) => (
109+
<>
110+
{nonResourceURLs?.map((url) => (
111+
<Tag key={url} color="purple">
112+
{url}
113+
</Tag>
114+
))}
115+
</>
116+
),
117+
});
118+
}
119+
120+
if (loading) return <Spin size="large" style={{ marginTop: "20px" }} />;
121+
122+
return (
123+
<div>
124+
{error.message.length !== 0 && (
125+
<Alert
126+
message={error.message}
127+
description={error.description}
128+
type="error"
129+
closable
130+
afterClose={() => {
131+
setError({
132+
message: "",
133+
description: "",
134+
});
135+
}}
136+
style={{ marginBottom: "20px" }}
137+
/>
138+
)}
139+
<Divider
140+
style={{ fontSize: "120%" }}
141+
orientationMargin="0"
142+
orientation={"left"}
143+
>
144+
Rules
145+
</Divider>
146+
<Row>
147+
<Col span={24} style={{ overflowX: "auto" }}>
148+
<Table dataSource={role.rules} columns={columns} />
149+
</Col>
150+
</Row>
151+
</div>
152+
);
153+
};
154+
155+
export default Role;

0 commit comments

Comments
 (0)