Skip to content

Commit

Permalink
feat: re-worked navigation to show available resources + basic suppor…
Browse files Browse the repository at this point in the history
…t for non-implemented object views
  • Loading branch information
Jeroen Nijhuis committed Jan 16, 2024
1 parent 3136819 commit 4a1fc13
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 1 deletion.
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"js-yaml": "^4.1.0",
"monaco-editor": "^0.45.0",
"monaco-languageclient": "^7.3.0",
"pluralize": "^8.0.0",
"radix-vue": "^1.2.7",
"tailwind-merge": "^2.1.0",
"tailwindcss-animate": "^1.0.7",
Expand Down
55 changes: 55 additions & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use either::Either;
use istio_api_rs::networking::v1beta1::virtual_service::VirtualService;
use k8s_openapi::api::batch::v1::{CronJob, Job};
use k8s_openapi::api::networking::v1::Ingress;
use k8s_openapi::apimachinery::pkg::apis::meta::v1::{APIGroup, APIResource};
use tauri::Manager;

use k8s_openapi::api::apps::v1::Deployment;
Expand Down Expand Up @@ -509,6 +510,56 @@ async fn replace_persistentvolumeclaim(
.map_err(|err| SerializableKubeError::from(err));
}

#[tauri::command]
async fn get_core_api_versions(context: &str) -> Result<Vec<String>, SerializableKubeError> {
let client = client_with_context(context).await?;

return client
.list_core_api_versions()
.await
.map(|api_versions| api_versions.versions)
.map_err(|err| SerializableKubeError::from(err));
}

#[tauri::command]
async fn get_core_api_resources(
context: &str,
core_api_version: &str,
) -> Result<Vec<APIResource>, SerializableKubeError> {
let client = client_with_context(context).await?;

return client
.list_core_api_resources(core_api_version)
.await
.map(|api_resources| api_resources.resources)
.map_err(|err| SerializableKubeError::from(err));
}

#[tauri::command]
async fn get_api_groups(context: &str) -> Result<Vec<APIGroup>, SerializableKubeError> {
let client = client_with_context(context).await?;

return client
.list_api_groups()
.await
.map(|api_groups| api_groups.groups)
.map_err(|err| SerializableKubeError::from(err));
}

#[tauri::command]
async fn get_api_group_resources(
context: &str,
api_group_version: &str,
) -> Result<Vec<APIResource>, SerializableKubeError> {
let client = client_with_context(context).await?;

return client
.list_api_group_resources(api_group_version)
.await
.map(|api_resources| api_resources.resources)
.map_err(|err| SerializableKubeError::from(err));
}

struct TerminalSession {
writer: Arc<Mutex<Box<dyn Write + Send>>>,
}
Expand Down Expand Up @@ -618,6 +669,10 @@ fn main() {
list_contexts,
get_current_context,
list_namespaces,
get_core_api_versions,
get_core_api_resources,
get_api_groups,
get_api_group_resources,
list_pods,
get_pod,
delete_pod,
Expand Down
137 changes: 136 additions & 1 deletion src/components/Navigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,114 @@ import ContextSwitcher from "./ContextSwitcher.vue";
import NavigationGroup from "./NavigationGroup.vue";
import NavigationItem from "./NavigationItem.vue";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Kubernetes } from "@/services/Kubernetes";
import { KubeContextStateKey } from "@/providers/KubeContextProvider";
import { injectStrict } from "@/lib/utils";
import { V1APIResource } from "@kubernetes/client-node";
import pluralize from "pluralize";
const { context } = injectStrict(KubeContextStateKey);
interface NavigationGroup {
title: string;
coreResources: string[];
apiGroupResources: string[];
}
const navigationGroups: NavigationGroup[] = [
{
title: "Workloads",
coreResources: ["pods"],
apiGroupResources: ["apps", "batch"],
},
{
title: "Config",
coreResources: ["configmaps", "resourcequotas", "secrets"],
apiGroupResources: [""],
},
{
title: "Network",
coreResources: ["endpoints", "services", "endpointslices"],
apiGroupResources: ["networking.k8s.io"],
},
{
title: "Storage",
coreResources: ["persistentvolumeclaims"],
apiGroupResources: ["storage.k8s.io"],
},
{
title: "Scaling",
coreResources: [],
apiGroupResources: ["autoscaling"],
},
{
title: "Policies",
coreResources: ["poddisruptionbudgets", "limitranges"],
apiGroupResources: [],
},
{
title: "Access Control",
coreResources: ["serviceaccounts"],
apiGroupResources: ["rbac.authorization.k8s.io"],
},
];
const clusterResources = ref<Map<string, V1APIResource[]>>(new Map());
const getCoreResourcesForGroup = (group: NavigationGroup) => {
return Array.from(clusterResources.value.values())
.flat()
.filter((resource) => group.coreResources.includes(resource.name));
};
const getApiResourcesForGroup = (group: NavigationGroup) => {
return Array.from(clusterResources.value.keys())
.filter((key) => group.apiGroupResources.includes(key))
.map((key) => clusterResources.value.get(key)!)
.flat()
.filter((resource) => resource.singularName !== "");
};
const formatResourceKind = (kind: string) => {
return pluralize(kind);
};
onMounted(() => {
Kubernetes.getCoreApiVersions(context.value).then((results) => {
results.forEach((version) => {
Kubernetes.getCoreApiResources(context.value, version).then(
(resources) => {
clusterResources.value.set(
version,
resources.filter((r) => r.namespaced)
);
}
);
});
});
Kubernetes.getApiGroups(context.value)
.then((results) => {
results.forEach((group) => {
Kubernetes.getApiGroupResources(
context.value,
group.preferredVersion?.groupVersion ?? ""
)
.then((resources) => {
clusterResources.value.set(
group.name,
resources.filter((r) => r.namespaced)
);
})
.catch((error) => {
console.error(error);
});
});
})
.catch((error) => {
console.error(error);
});
});
</script>

<template>
Expand All @@ -12,7 +120,7 @@ import { ScrollArea } from "@/components/ui/scroll-area";
<ContextSwitcher class="mt-[30px]" />
<div class="flex w-full flex-grow flex-shrink overflow-hidden">
<ScrollArea class="w-full mt-0 mb-0">
<NavigationGroup title="Workloads">
<!-- <NavigationGroup title="Workloads">
<NavigationItem icon="pods" title="Pods" :to="{ name: 'Pods' }" />
<NavigationItem
icon="deployments"
Expand Down Expand Up @@ -62,6 +170,33 @@ import { ScrollArea } from "@/components/ui/scroll-area";
:to="{ name: 'PersistentVolumeClaims' }"
/>
</NavigationGroup>
<NavigationGroup title="Custom Resources"> </NavigationGroup> -->
<NavigationGroup
v-for="group in navigationGroups"
:key="group.title"
:title="group.title"
>
<NavigationItem
icon="pods"
:title="formatResourceKind(resource.kind)"
v-for="resource in getCoreResourcesForGroup(group)"
:key="resource.name"
:to="{
path: `/${resource.name}`,
query: { resource: resource.name },
}"
/>
<NavigationItem
icon="pods"
:title="formatResourceKind(resource.kind)"
v-for="resource in getApiResourcesForGroup(group)"
:key="resource.name"
:to="{
path: `/${resource.name}`,
query: { resource: resource.name },
}"
/>
</NavigationGroup>
</ScrollArea>
</div>
<div class="flex-shrink-0 border-t -ml-2 pl-2 pt-2 mb-0">
Expand Down
18 changes: 18 additions & 0 deletions src/components/tables/generic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { V1Pod } from "@kubernetes/client-node";
import { ColumnDef } from "@tanstack/vue-table";
import { formatDateTimeDifference } from "@/lib/utils";

export const columns: ColumnDef<any>[] = [
{
accessorKey: "metadata.name",
header: "Name",
},
{
header: "Age",
accessorFn: (row) =>
formatDateTimeDifference(
row.metadata?.creationTimestamp || new Date(),
new Date()
),
},
];
5 changes: 5 additions & 0 deletions src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ const routes: Array<RouteRecordRaw> = [
name: "PersistentVolumeClaims",
component: () => import("./views/PersistentVolumeClaims.vue"),
},
{
path: "/:pathMatch(.*)*",
name: "GenericResource",
component: () => import("./views/GenericResource.vue"),
},
];

const router = createRouter({
Expand Down
30 changes: 30 additions & 0 deletions src/services/Kubernetes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
V1APIGroup,
V1APIResource,
V1ConfigMap,
V1CronJob,
V1Deployment,
Expand Down Expand Up @@ -33,6 +35,34 @@ export class Kubernetes {
return invoke("list_namespaces", { context: context });
}

static async getCoreApiVersions(context: string): Promise<string[]> {
return invoke("get_core_api_versions", { context: context });
}

static async getCoreApiResources(
context: string,
core_api_version: string
): Promise<V1APIResource[]> {
return invoke("get_core_api_resources", {
context: context,
coreApiVersion: core_api_version,
});
}

static async getApiGroups(context: string): Promise<V1APIGroup[]> {
return invoke("get_api_groups", { context: context });
}

static async getApiGroupResources(
context: string,
api_group_version: string
): Promise<V1APIResource[]> {
return invoke("get_api_group_resources", {
context: context,
apiGroupVersion: api_group_version,
});
}

static async getPods(
context: string,
namespace: string,
Expand Down
Loading

0 comments on commit 4a1fc13

Please sign in to comment.