Skip to content

Commit

Permalink
add sa list and details view
Browse files Browse the repository at this point in the history
  • Loading branch information
floreks committed Mar 29, 2024
1 parent 488fe1c commit 2f60516
Show file tree
Hide file tree
Showing 9 changed files with 369 additions and 4 deletions.
2 changes: 2 additions & 0 deletions assets/src/components/kubernetes/access/Access.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CLUSTER_ROLE_BINDINGS_REL_PATH,
ROLES_REL_PATH,
ROLE_BINDINGS_REL_PATH,
SERVICE_ACCOUNTS_REL_PATH,
getAccessAbsPath,
} from '../../../routes/kubernetesRoutesConsts'
import { ScrollablePage } from '../../utils/layout/ScrollablePage'
Expand All @@ -24,6 +25,7 @@ const directory = [
{ path: ROLE_BINDINGS_REL_PATH, label: 'Role bindings' },
{ path: CLUSTER_ROLES_REL_PATH, label: 'Cluster roles' },
{ path: CLUSTER_ROLE_BINDINGS_REL_PATH, label: 'Cluster role bindings' },
{ path: SERVICE_ACCOUNTS_REL_PATH, label: 'Service Accounts' },
] as const

export default function Access() {
Expand Down
73 changes: 73 additions & 0 deletions assets/src/components/kubernetes/access/ServiceAccount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { ReactElement, useMemo } from 'react'
import { Outlet, useParams } from 'react-router-dom'
import { useSetBreadcrumbs } from '@pluralsh/design-system'

import { MetadataSidecar, useKubernetesCluster } from '../utils'
import {
ServiceAccountQueryVariables,
useServiceAccountQuery,
} from '../../../generated/graphql-kubernetes'
import { KubernetesClient } from '../../../helpers/kubernetes.client'
import {
SERVICE_ACCOUNTS_REL_PATH,
getAccessAbsPath,
getResourceDetailsAbsPath,
} from '../../../routes/kubernetesRoutesConsts'
import { NAMESPACE_PARAM } from '../Kubernetes'
import LoadingIndicator from '../../utils/LoadingIndicator'
import ResourceDetails, { TabEntry } from '../ResourceDetails'

import { getBreadcrumbs } from './ServiceAccounts'

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

export default function ServiceAccount(): ReactElement {
const cluster = useKubernetesCluster()
const { clusterId, name = '', namespace = '' } = useParams()
const { data, loading } = useServiceAccountQuery({
client: KubernetesClient(clusterId ?? ''),
skip: !clusterId,
pollInterval: 30_000,
variables: {
name,
namespace,
} as ServiceAccountQueryVariables,
})

const serviceAccount = data?.handleGetServiceAccountDetail

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

if (loading) return <LoadingIndicator />

return (
<ResourceDetails
tabs={directory}
sidecar={<MetadataSidecar resource={serviceAccount} />}
>
<Outlet context={serviceAccount} />
</ResourceDetails>
)
}
62 changes: 62 additions & 0 deletions assets/src/components/kubernetes/access/ServiceAccounts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { createColumnHelper } from '@tanstack/react-table'
import { useMemo } from 'react'
import { useSetBreadcrumbs } from '@pluralsh/design-system'

import {
Maybe,
Serviceaccount_ServiceAccountList as ServiceAccountListT,
Serviceaccount_ServiceAccount as ServiceAccountT,
ServiceAccountsQuery,
ServiceAccountsQueryVariables,
useServiceAccountsQuery,
} from '../../../generated/graphql-kubernetes'
import { getBaseBreadcrumbs, useDefaultColumns } from '../utils'
import { ResourceList } from '../ResourceList'
import { ClusterTinyFragment } from '../../../generated/graphql'
import {
SERVICE_ACCOUNTS_REL_PATH,
getAccessAbsPath,
} from '../../../routes/kubernetesRoutesConsts'
import { useKubernetesContext } from '../Kubernetes'

export const getBreadcrumbs = (cluster?: Maybe<ClusterTinyFragment>) => [
...getBaseBreadcrumbs(cluster),
{
label: 'access',
url: getAccessAbsPath(cluster?.id),
},
{
label: 'service accounts',
url: `${getAccessAbsPath(cluster?.id)}/${SERVICE_ACCOUNTS_REL_PATH}`,
},
]

const columnHelper = createColumnHelper<ServiceAccountT>()

export default function ServiceAccounts() {
const { cluster } = useKubernetesContext()

useSetBreadcrumbs(useMemo(() => getBreadcrumbs(cluster), [cluster]))

const { colName, colNamespace, colLabels, colCreationTimestamp } =
useDefaultColumns(columnHelper)
const columns = useMemo(
() => [colName, colNamespace, colLabels, colCreationTimestamp],
[colName, colNamespace, colLabels, colCreationTimestamp]
)

return (
<ResourceList<
ServiceAccountListT,
ServiceAccountT,
ServiceAccountsQuery,
ServiceAccountsQueryVariables
>
namespaced
columns={columns}
query={useServiceAccountsQuery}
queryName="handleGetServiceAccountList"
itemsKey="items"
/>
)
}
19 changes: 18 additions & 1 deletion assets/src/components/kubernetes/common/ResourceOwner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ import moment from 'moment'

import { ChipList } from '@pluralsh/design-system'

import { Link } from 'react-router-dom'

import { Controller_ResourceOwner as ResourceOwnerT } from '../../../generated/graphql-kubernetes'

import { getResourceDetailsAbsPath } from '../../../routes/kubernetesRoutesConsts'

import { InlineLink } from '../../utils/typography/InlineLink'

import ResourceInfoCard, {
ResourceInfoCardEntry,
ResourceInfoCardSection,
Expand All @@ -14,16 +20,27 @@ import Annotations from './Annotations'

interface ResourceOwnerProps {
owner: Nullable<ResourceOwnerT>
clusterId: Nullable<string>
}

export default function ResourceOwner({
owner,
clusterId,
}: ResourceOwnerProps): ReactElement {
return (
<ResourceInfoCard title={`Controlled By ${owner?.typeMeta?.kind}`}>
<ResourceInfoCardSection>
<ResourceInfoCardEntry heading="Name">
{owner?.objectMeta?.name}
<Link
to={getResourceDetailsAbsPath(
clusterId,
owner?.typeMeta?.kind,
owner?.objectMeta?.name,
owner?.objectMeta?.namespace
)}
>
<InlineLink>{owner?.objectMeta?.name}</InlineLink>
</Link>
</ResourceInfoCardEntry>
<ResourceInfoCardEntry heading="Pods">
{owner?.pods?.running} / {owner?.pods?.desired}
Expand Down
5 changes: 4 additions & 1 deletion assets/src/components/kubernetes/workloads/Pod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ export function PodInfo(): ReactElement {
<>
<section>
<SubTitle>Owner</SubTitle>
<ResourceOwner owner={pod?.controller} />
<ResourceOwner
clusterId={cluster?.id}
owner={pod?.controller}
/>
</section>
<section>
<SubTitle>Conditions</SubTitle>
Expand Down
128 changes: 128 additions & 0 deletions assets/src/generated/graphql-kubernetes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4658,6 +4658,25 @@ export type RoleBindingQueryVariables = Exact<{

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 ServiceAccountsQueryVariables = Exact<{
namespace: Scalars['String']['input'];
filterBy?: InputMaybe<Scalars['String']['input']>;
sortBy?: InputMaybe<Scalars['String']['input']>;
itemsPerPage?: InputMaybe<Scalars['String']['input']>;
page?: InputMaybe<Scalars['String']['input']>;
}>;


export type ServiceAccountsQuery = { __typename?: 'Query', handleGetServiceAccountList?: { __typename?: 'serviceaccount_ServiceAccountList', listMeta: { __typename?: 'types_ListMeta', totalItems: number }, items: Array<{ __typename?: 'serviceaccount_ServiceAccount', 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 ServiceAccountQueryVariables = Exact<{
namespace: Scalars['String']['input'];
name: Scalars['String']['input'];
}>;


export type ServiceAccountQuery = { __typename?: 'Query', handleGetServiceAccountDetail?: { __typename?: 'serviceaccount_ServiceAccountDetail', 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 EventsQueryVariables = Exact<{
namespace: Scalars['String']['input'];
filterBy?: InputMaybe<Scalars['String']['input']>;
Expand Down Expand Up @@ -5754,6 +5773,115 @@ 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 ServiceAccountsDocument = gql`
query ServiceAccounts($namespace: String!, $filterBy: String, $sortBy: String, $itemsPerPage: String, $page: String) {
handleGetServiceAccountList(
namespace: $namespace
filterBy: $filterBy
sortBy: $sortBy
itemsPerPage: $itemsPerPage
page: $page
) @rest(type: "serviceaccount_ServiceAccountList", path: "serviceaccount/{args.namespace}?filterBy={args.filterBy}&sortBy={args.sortBy}&itemsPerPage={args.itemsPerPage}&page={args.page}") {
listMeta @type(name: "types_ListMeta") {
...ListMeta
}
items {
typeMeta @type(name: "types_TypeMeta") {
...TypeMeta
}
objectMeta @type(name: "types_ObjectMeta") {
...ObjectMeta
}
}
}
}
${ListMetaFragmentDoc}
${TypeMetaFragmentDoc}
${ObjectMetaFragmentDoc}`;

/**
* __useServiceAccountsQuery__
*
* To run a query within a React component, call `useServiceAccountsQuery` and pass it any options that fit your needs.
* When your component renders, `useServiceAccountsQuery` 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 } = useServiceAccountsQuery({
* variables: {
* namespace: // value for 'namespace'
* filterBy: // value for 'filterBy'
* sortBy: // value for 'sortBy'
* itemsPerPage: // value for 'itemsPerPage'
* page: // value for 'page'
* },
* });
*/
export function useServiceAccountsQuery(baseOptions: Apollo.QueryHookOptions<ServiceAccountsQuery, ServiceAccountsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ServiceAccountsQuery, ServiceAccountsQueryVariables>(ServiceAccountsDocument, options);
}
export function useServiceAccountsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ServiceAccountsQuery, ServiceAccountsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ServiceAccountsQuery, ServiceAccountsQueryVariables>(ServiceAccountsDocument, options);
}
export function useServiceAccountsSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions<ServiceAccountsQuery, ServiceAccountsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSuspenseQuery<ServiceAccountsQuery, ServiceAccountsQueryVariables>(ServiceAccountsDocument, options);
}
export type ServiceAccountsQueryHookResult = ReturnType<typeof useServiceAccountsQuery>;
export type ServiceAccountsLazyQueryHookResult = ReturnType<typeof useServiceAccountsLazyQuery>;
export type ServiceAccountsSuspenseQueryHookResult = ReturnType<typeof useServiceAccountsSuspenseQuery>;
export type ServiceAccountsQueryResult = Apollo.QueryResult<ServiceAccountsQuery, ServiceAccountsQueryVariables>;
export const ServiceAccountDocument = gql`
query ServiceAccount($namespace: String!, $name: String!) {
handleGetServiceAccountDetail(namespace: $namespace, serviceaccount: $name) @rest(type: "serviceaccount_ServiceAccountDetail", path: "serviceaccount/{args.namespace}/{args.serviceaccount}") {
typeMeta @type(name: "types_TypeMeta") {
...TypeMeta
}
objectMeta @type(name: "types_ObjectMeta") {
...ObjectMeta
}
}
}
${TypeMetaFragmentDoc}
${ObjectMetaFragmentDoc}`;

/**
* __useServiceAccountQuery__
*
* To run a query within a React component, call `useServiceAccountQuery` and pass it any options that fit your needs.
* When your component renders, `useServiceAccountQuery` 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 } = useServiceAccountQuery({
* variables: {
* namespace: // value for 'namespace'
* name: // value for 'name'
* },
* });
*/
export function useServiceAccountQuery(baseOptions: Apollo.QueryHookOptions<ServiceAccountQuery, ServiceAccountQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ServiceAccountQuery, ServiceAccountQueryVariables>(ServiceAccountDocument, options);
}
export function useServiceAccountLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ServiceAccountQuery, ServiceAccountQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ServiceAccountQuery, ServiceAccountQueryVariables>(ServiceAccountDocument, options);
}
export function useServiceAccountSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions<ServiceAccountQuery, ServiceAccountQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSuspenseQuery<ServiceAccountQuery, ServiceAccountQueryVariables>(ServiceAccountDocument, options);
}
export type ServiceAccountQueryHookResult = ReturnType<typeof useServiceAccountQuery>;
export type ServiceAccountLazyQueryHookResult = ReturnType<typeof useServiceAccountLazyQuery>;
export type ServiceAccountSuspenseQueryHookResult = ReturnType<typeof useServiceAccountSuspenseQuery>;
export type ServiceAccountQueryResult = Apollo.QueryResult<ServiceAccountQuery, ServiceAccountQueryVariables>;
export const EventsDocument = gql`
query Events($namespace: String!, $filterBy: String, $sortBy: String, $itemsPerPage: String, $page: String) {
handleGetEventList(
Expand Down
Loading

0 comments on commit 2f60516

Please sign in to comment.