Skip to content

Commit

Permalink
add role bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
maciaszczykm committed Mar 29, 2024
1 parent 92d4146 commit 019abc1
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 7 deletions.
2 changes: 0 additions & 2 deletions assets/src/components/kubernetes/access/Role.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import { NAMESPACE_PARAM } from '../Kubernetes'
import LoadingIndicator from '../../utils/LoadingIndicator'
import ResourceDetails, { TabEntry } from '../ResourceDetails'

import { SubTitle } from '../../cluster/nodes/SubTitle'

import PolicyRules from '../common/PolicyRules'

import { FullHeightTableWrap } from '../../utils/layout/FullHeightTableWrap'
Expand Down
107 changes: 105 additions & 2 deletions assets/src/components/kubernetes/access/RoleBinding.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,108 @@
import { ReactElement } from 'react'
import { ReactElement, useMemo } from 'react'
import { Link, Outlet, useOutletContext, useParams } from 'react-router-dom'
import { SidecarItem, useSetBreadcrumbs } from '@pluralsh/design-system'
import { A } from 'honorable'

import {
RoleBindingQueryVariables,
Rolebinding_RoleBindingDetail as RoleBindingT,
useRoleBindingQuery,
} from '../../../generated/graphql-kubernetes'
import { KubernetesClient } from '../../../helpers/kubernetes.client'
import { MetadataSidecar, useKubernetesCluster } from '../utils'
import ResourceDetails, { TabEntry } from '../ResourceDetails'
import {
ROLES_REL_PATH,
getAccessAbsPath,
getResourceDetailsAbsPath,
} from '../../../routes/kubernetesRoutesConsts'
import { NAMESPACE_PARAM } from '../Kubernetes'
import LoadingIndicator from '../../utils/LoadingIndicator'
import { FullHeightTableWrap } from '../../utils/layout/FullHeightTableWrap'
import Subjects from '../common/Subjects'

import { getBreadcrumbs } from './Roles'

const directory: Array<TabEntry> = [
{ path: '', label: 'Subjects' },
{ path: 'raw', label: 'Raw' },
] as const

export default function RoleBinding(): ReactElement {
return <div>RoleBinding details</div>
const cluster = useKubernetesCluster()
const { clusterId, name = '', namespace = '' } = useParams()
const { data, loading } = useRoleBindingQuery({
client: KubernetesClient(clusterId ?? ''),
skip: !clusterId,
pollInterval: 30_000,
variables: {
name,
namespace,
} as RoleBindingQueryVariables,
})

const rb = data?.handleGetRoleBindingDetail

useSetBreadcrumbs(
useMemo(
() => [
...getBreadcrumbs(cluster),
{
label: namespace ?? '',
url: `${getAccessAbsPath(
cluster?.id
)}/${ROLES_REL_PATH}?${NAMESPACE_PARAM}=${namespace}`,
},
{
label: name ?? '',
url: getResourceDetailsAbsPath(
clusterId,
'rolebinding',
name,
namespace
),
},
],
[cluster, clusterId, name, namespace]
)
)

if (loading) return <LoadingIndicator />

return (
<ResourceDetails
tabs={directory}
sidecar={
<MetadataSidecar objectMeta={rb?.objectMeta}>
<SidecarItem heading="Role">
<A
as={Link}
to={getResourceDetailsAbsPath(
clusterId,
'role',
rb?.roleRef.name ?? '',
namespace
)}
inline
>
{rb?.roleRef.name}
</A>
</SidecarItem>
</MetadataSidecar>
}
>
<Outlet context={rb} />
</ResourceDetails>
)
}

// TODO: Add links.
export function RoleBindingSubjects(): ReactElement {
const rb = useOutletContext() as RoleBindingT

return (
<FullHeightTableWrap>
<Subjects subjects={rb?.subjects} />
</FullHeightTableWrap>
)
}
50 changes: 50 additions & 0 deletions assets/src/components/kubernetes/common/Subjects.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ReactElement } from 'react'
import { Table } from '@pluralsh/design-system'
import { createColumnHelper } from '@tanstack/react-table'

import {
Maybe,
V1_Subject as SubjectT,
} from '../../../generated/graphql-kubernetes'

const columnHelper = createColumnHelper<SubjectT>()

const columns = [
columnHelper.accessor((subject) => subject?.name, {
id: 'name',
header: 'Name',
cell: ({ getValue }) => getValue(),
}),
columnHelper.accessor((subject) => subject?.namespace, {
id: 'namespace',
header: 'Namespace',
cell: ({ getValue }) => getValue(),
}),
columnHelper.accessor((subject) => subject?.kind, {
id: 'kind',
header: 'Kind',
cell: ({ getValue }) => getValue(),
}),
columnHelper.accessor((subject) => subject?.apiGroup, {
id: 'apiGroup',
header: 'API group',
cell: ({ getValue }) => getValue(),
}),
]

export default function Subjects({
subjects,
}: {
subjects?: Maybe<Array<Maybe<SubjectT>>>
}): ReactElement {
return (
<Table
data={subjects || []}
columns={columns}
css={{
maxHeight: 'unset',
height: '100%',
}}
/>
)
}
75 changes: 75 additions & 0 deletions assets/src/generated/graphql-kubernetes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4643,6 +4643,14 @@ export type RoleBindingsQueryVariables = Exact<{

export type RoleBindingsQuery = { __typename?: 'Query', handleGetRoleBindingList?: { __typename?: 'rolebinding_RoleBindingList', listMeta: { __typename?: 'types_ListMeta', totalItems: number }, items: Array<{ __typename?: 'rolebinding_RoleBinding', typeMeta: { __typename?: 'types_TypeMeta', kind?: string | null, restartable?: boolean | null, scalable?: boolean | null }, objectMeta: { __typename?: 'types_ObjectMeta', uid?: string | null, name?: string | null, namespace?: string | null, labels?: any | null, annotations?: any | null, creationTimestamp?: string | null } } | null> } | null };

export type RoleBindingQueryVariables = Exact<{
name: Scalars['String']['input'];
namespace: Scalars['String']['input'];
}>;


export type RoleBindingQuery = { __typename?: 'Query', handleGetRoleBindingDetail?: { __typename?: 'rolebinding_RoleBindingDetail', errors: Array<any | null>, typeMeta: { __typename?: 'types_TypeMeta', kind?: string | null, restartable?: boolean | null, scalable?: boolean | null }, objectMeta: { __typename?: 'types_ObjectMeta', uid?: string | null, name?: string | null, namespace?: string | null, labels?: any | null, annotations?: any | null, creationTimestamp?: string | null }, subjects?: Array<{ __typename?: 'v1_Subject', apiGroup?: string | null, kind: string, name: string, namespace?: string | null } | null> | null, roleRef: { __typename?: 'v1_RoleRef', name: string, kind: string, apiGroup: string } } | null };

export type EventsQueryVariables = Exact<{
namespace: Scalars['String']['input'];
filterBy?: InputMaybe<Scalars['String']['input']>;
Expand Down Expand Up @@ -4800,6 +4808,8 @@ export type ProbeFragment = { __typename?: 'v1_Probe', failureThreshold?: number

export type PolicyRuleFragment = { __typename?: 'v1_PolicyRule', apiGroups?: Array<string | null> | null, nonResourceURLs?: Array<string | null> | null, resourceNames?: Array<string | null> | null, verbs: Array<string | null>, resources?: Array<string | null> | null };

export type SubjectFragment = { __typename?: 'v1_Subject', apiGroup?: string | null, kind: string, name: string, namespace?: string | null };

export type PodInfoFragment = { __typename?: 'common_PodInfo', current: number, desired?: number | null, failed: number, pending: number, running: number, succeeded: number, warnings: Array<{ __typename?: 'common_Event', objectName?: string | null, objectNamespace?: string | null, reason: string, type: string, message: string, sourceComponent: string, sourceHost: string, count: number, firstSeen: string, lastSeen: string, typeMeta: { __typename?: 'types_TypeMeta', kind?: string | null, restartable?: boolean | null, scalable?: boolean | null }, objectMeta: { __typename?: 'types_ObjectMeta', uid?: string | null, name?: string | null, namespace?: string | null, labels?: any | null, annotations?: any | null, creationTimestamp?: string | null } } | null> };

export type ResourceOwnerFragment = { __typename?: 'controller_ResourceOwner', containerImages: Array<string | null>, initContainerImages: Array<string | null>, typeMeta: { __typename?: 'types_TypeMeta', kind?: string | null, restartable?: boolean | null, scalable?: boolean | null }, objectMeta: { __typename?: 'types_ObjectMeta', uid?: string | null, name?: string | null, namespace?: string | null, labels?: any | null, annotations?: any | null, creationTimestamp?: string | null }, pods: { __typename?: 'common_PodInfo', current: number, desired?: number | null, failed: number, pending: number, running: number, succeeded: number, warnings: Array<{ __typename?: 'common_Event', objectName?: string | null, objectNamespace?: string | null, reason: string, type: string, message: string, sourceComponent: string, sourceHost: string, count: number, firstSeen: string, lastSeen: string, typeMeta: { __typename?: 'types_TypeMeta', kind?: string | null, restartable?: boolean | null, scalable?: boolean | null }, objectMeta: { __typename?: 'types_ObjectMeta', uid?: string | null, name?: string | null, namespace?: string | null, labels?: any | null, annotations?: any | null, creationTimestamp?: string | null } } | null> } };
Expand Down Expand Up @@ -5063,6 +5073,14 @@ export const PolicyRuleFragmentDoc = gql`
resources
}
`;
export const SubjectFragmentDoc = gql`
fragment Subject on v1_Subject {
apiGroup
kind
name
namespace
}
`;
export const PodInfoFragmentDoc = gql`
fragment PodInfo on common_PodInfo {
current
Expand Down Expand Up @@ -5616,6 +5634,63 @@ export type RoleBindingsQueryHookResult = ReturnType<typeof useRoleBindingsQuery
export type RoleBindingsLazyQueryHookResult = ReturnType<typeof useRoleBindingsLazyQuery>;
export type RoleBindingsSuspenseQueryHookResult = ReturnType<typeof useRoleBindingsSuspenseQuery>;
export type RoleBindingsQueryResult = Apollo.QueryResult<RoleBindingsQuery, RoleBindingsQueryVariables>;
export const RoleBindingDocument = gql`
query RoleBinding($name: String!, $namespace: String!) {
handleGetRoleBindingDetail(namespace: $namespace, name: $name) @rest(path: "rolebinding/{args.namespace}/{args.name}") {
typeMeta @type(name: "types_TypeMeta") {
...TypeMeta
}
objectMeta @type(name: "types_ObjectMeta") {
...ObjectMeta
}
subjects @type(name: "v1_Subject") {
...Subject
}
roleRef {
name
kind
apiGroup
}
errors
}
}
${TypeMetaFragmentDoc}
${ObjectMetaFragmentDoc}
${SubjectFragmentDoc}`;

/**
* __useRoleBindingQuery__
*
* To run a query within a React component, call `useRoleBindingQuery` and pass it any options that fit your needs.
* When your component renders, `useRoleBindingQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useRoleBindingQuery({
* variables: {
* name: // value for 'name'
* namespace: // value for 'namespace'
* },
* });
*/
export function useRoleBindingQuery(baseOptions: Apollo.QueryHookOptions<RoleBindingQuery, RoleBindingQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<RoleBindingQuery, RoleBindingQueryVariables>(RoleBindingDocument, options);
}
export function useRoleBindingLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<RoleBindingQuery, RoleBindingQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<RoleBindingQuery, RoleBindingQueryVariables>(RoleBindingDocument, options);
}
export function useRoleBindingSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions<RoleBindingQuery, RoleBindingQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSuspenseQuery<RoleBindingQuery, RoleBindingQueryVariables>(RoleBindingDocument, options);
}
export type RoleBindingQueryHookResult = ReturnType<typeof useRoleBindingQuery>;
export type RoleBindingLazyQueryHookResult = ReturnType<typeof useRoleBindingLazyQuery>;
export type RoleBindingSuspenseQueryHookResult = ReturnType<typeof useRoleBindingSuspenseQuery>;
export type RoleBindingQueryResult = Apollo.QueryResult<RoleBindingQuery, RoleBindingQueryVariables>;
export const EventsDocument = gql`
query Events($namespace: String!, $filterBy: String, $sortBy: String, $itemsPerPage: String, $page: String) {
handleGetEventList(
Expand Down
22 changes: 22 additions & 0 deletions assets/src/graph-kubernetes/access/rolebinding.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,25 @@ query RoleBindings(
}
}
}


query RoleBinding($name: String!, $namespace: String!) {
handleGetRoleBindingDetail(namespace: $namespace, name: $name)
@rest(path: "rolebinding/{args.namespace}/{args.name}") {
typeMeta @type(name: "types_TypeMeta") {
...TypeMeta
}
objectMeta @type(name: "types_ObjectMeta") {
...ObjectMeta
}
subjects @type(name: "v1_Subject") {
...Subject
}
roleRef {
name
kind
apiGroup
}
errors
}
}
7 changes: 7 additions & 0 deletions assets/src/graph-kubernetes/fragments.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ fragment PolicyRule on v1_PolicyRule {
resources
}

fragment Subject on v1_Subject {
apiGroup
kind
name
namespace
}

fragment PodInfo on common_PodInfo {
current
desired
Expand Down
17 changes: 14 additions & 3 deletions assets/src/routes/kubernetesRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import PersistentVolume, {
PersistentVolumeInfo,
} from 'components/kubernetes/storage/PersistentVolume'
import Secret, { SecretData } from 'components/kubernetes/configuration/Secret'
import RoleBinding from 'components/kubernetes/access/RoleBinding'
import RoleBinding, {
RoleBindingSubjects,
} from 'components/kubernetes/access/RoleBinding'
import Role, { RolePolicyRules } from 'components/kubernetes/access/Role'
import ClusterRole from 'components/kubernetes/access/ClusterRole'
import ClusterRoleBinding from 'components/kubernetes/access/ClusterRoleBinding'
Expand Down Expand Up @@ -481,10 +483,19 @@ export const kubernetesRoutes = [
/>
</Route>,
<Route
index
path={`${KUBERNETES_ABS_PATH}/${ROLE_BINDINGS_REL_PATH}/${NAMESPACED_RESOURCE_DETAILS_REL_PATH}`}
element={<RoleBinding />}
/>,
>
<Route
index
path=""
element={<RoleBindingSubjects />}
/>
<Route
path="raw"
element={<Raw />}
/>
</Route>,
<Route
path={`${KUBERNETES_ABS_PATH}/${CLUSTER_ROLES_REL_PATH}/${RESOURCE_DETAILS_REL_PATH}`}
element={<ClusterRole />}
Expand Down

0 comments on commit 019abc1

Please sign in to comment.