Skip to content

Commit c7ef125

Browse files
Add view and download cluster
1 parent b39929c commit c7ef125

3 files changed

Lines changed: 165 additions & 9 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<template>
2+
<tr class="text-no-wrap py-2">
3+
<td class="text-subtitle-2 text-center">
4+
<span class="opacity-50">{{ deployment.cluster?.name }}</span>
5+
</td>
6+
7+
<td class="text-subtitle-2 text-center">
8+
<span class="opacity-50">{{ deployment.cluster?.nodes.length }}</span>
9+
</td>
10+
11+
<td class="text-subtitle-2 text-center">
12+
<span class="opacity-50">{{ createdAt }}</span>
13+
</td>
14+
15+
<td>
16+
<div class="d-flex justify-center align-center ga-4">
17+
<v-btn
18+
variant="plain"
19+
border
20+
prepend-icon="mdi-eye-outline"
21+
size="small"
22+
text="View"
23+
:to="ROUTES.Dashboard.Clusters(deployment.cluster?.name)"
24+
/>
25+
26+
<v-btn
27+
variant="text"
28+
border
29+
prepend-icon="mdi-download-outline"
30+
size="small"
31+
text="Download"
32+
:loading="downloading"
33+
:href="binary"
34+
:download="`${deployment.cluster?.name}-kubeconfig.yaml`"
35+
/>
36+
37+
<v-btn
38+
variant="text"
39+
border
40+
prepend-icon="mdi-plus"
41+
size="small"
42+
text="Add Node"
43+
color="primary"
44+
@click="console.log('add node')"
45+
/>
46+
47+
<v-btn
48+
variant="text"
49+
border
50+
prepend-icon="mdi-trash-can-outline"
51+
size="small"
52+
text="Delete"
53+
color="error"
54+
@click="console.log('delete cluster')"
55+
/>
56+
</div>
57+
</td>
58+
</tr>
59+
</template>
60+
61+
<script setup lang="ts">
62+
import type { ServicesClusterData } from "~/generated/api"
63+
64+
const props = defineProps<{ deployment: ServicesClusterData }>()
65+
defineEmits<{ (e: "view"): void }>()
66+
67+
const createdAt = useDateFormat(() => props.deployment.created_at, DATE_FORMAT)
68+
69+
const api = useApi()
70+
71+
const { state: kubeconfig, isLoading: downloading } = useAsyncState(
72+
() => api.helpers.getKubeconfig(props.deployment.cluster?.name ?? ""),
73+
"",
74+
)
75+
76+
const binary = computed(() => {
77+
const data = kubeconfig.value
78+
79+
if (!data) {
80+
return ""
81+
}
82+
83+
const blob = new Blob([data], { type: "application/json" })
84+
return URL.createObjectURL(blob)
85+
})
86+
</script>

frontend/kubecloud-v2/composables/api.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export const useApi = createGlobalState(() => {
102102

103103
class ApiHelpers {
104104
private readonly twinToAccountId: { [twinId: number]: string }
105+
private readonly clusterToKubeconfig: { [clusterId: string]: string }
105106

106107
constructor(
107108
private readonly admin: AdminApi,
@@ -114,6 +115,7 @@ class ApiHelpers {
114115
private readonly workflow: WorkflowApi,
115116
) {
116117
this.twinToAccountId = {}
118+
this.clusterToKubeconfig = {}
117119
}
118120

119121
public async awaitWorkflowCompletion(workflowId: string): Promise<boolean> {
@@ -144,4 +146,14 @@ class ApiHelpers {
144146
this.twinToAccountId[twinId] = accountId
145147
return accountId
146148
}
149+
150+
public async getKubeconfig(clusterName: string): Promise<string> {
151+
if (clusterName in this.clusterToKubeconfig) {
152+
return this.clusterToKubeconfig[clusterName]!
153+
}
154+
155+
const { data } = await this.deployments.deploymentsNameKubeconfigGet(clusterName)
156+
this.clusterToKubeconfig[clusterName] = data.data?.kubeconfig ?? ""
157+
return data.data?.kubeconfig ?? ""
158+
}
147159
}
Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,79 @@
11
<template>
22
<div>
3-
<h1>Dashboard Clusters {{ isLoading }}</h1>
3+
<div class="mb-8">
4+
<h1 class="text-h5 font-weight-bold">
5+
Kubernetes Clusters
6+
</h1>
7+
<p class="text-body-2 mt-1 text-accent">
8+
Manage your cloud-native infrastructure
9+
</p>
10+
</div>
411

5-
<VBtn :to="ROUTES.Dashboard.Clusters.Deploy()" text="Deploy Cluster" />
6-
<VBtn :to="ROUTES.Dashboard.Clusters('123')" text="Deploy Cluster" />
12+
<div class="d-flex justify-end ga-4 mb-8">
13+
<v-btn prepend-icon="mdi-plus" text="New Cluster" variant="tonal" color="primary" />
14+
<v-btn
15+
prepend-icon="mdi-trash-can-outline"
16+
color="error"
17+
variant="text"
18+
text="Delete All"
19+
border
20+
class="border-error"
21+
:style="{ '--v-border-opacity': '0.3' }"
22+
/>
23+
</div>
724

8-
<pre>
9-
{{ state }}
10-
</pre>
25+
<v-row class="mb-4">
26+
<v-col cols="9">
27+
<v-text-field
28+
label="Search by name"
29+
placeholder="Search by name"
30+
prepend-inner-icon="mdi-magnify"
31+
variant="outlined"
32+
density="compact"
33+
hide-details
34+
/>
35+
</v-col>
36+
37+
<v-col cols="3">
38+
<v-select
39+
label="Sort By"
40+
:items="sortByItems"
41+
variant="outlined"
42+
density="compact"
43+
hide-details
44+
clearable
45+
/>
46+
</v-col>
47+
</v-row>
48+
49+
<v-data-table
50+
:headers="[
51+
{ title: 'Name', key: 'name', align: 'center', sortable: false },
52+
{ title: 'Nodes', key: 'nodes', align: 'center', sortable: false },
53+
{ title: 'Created At', key: 'createdAt', align: 'center', sortable: false },
54+
{ title: 'Actions', key: 'actions', align: 'center', sortable: false },
55+
]"
56+
:items="deployments"
57+
:loading="isLoading"
58+
>
59+
<template #item="{ item }">
60+
<DeploymentRow :deployment="item" />
61+
</template>
62+
</v-data-table>
1163
</div>
1264
</template>
1365

1466
<script setup lang="ts">
1567
const api = useApi()
1668
17-
const { state, isLoading } = useAsyncState(async () => {
69+
const { state: deployments, isLoading } = useAsyncState(async () => {
1870
const { data } = await api.deployments.deploymentsGet()
19-
return data
20-
}, null)
71+
return data.data?.deployments ?? []
72+
}, [])
73+
74+
const sortByItems = [
75+
{ title: "Name", value: "name" },
76+
{ title: "Created At", value: "createdAt" },
77+
{ title: "Nodes", value: "nodes" },
78+
]
2179
</script>

0 commit comments

Comments
 (0)