Skip to content

Commit

Permalink
0.1.5
Browse files Browse the repository at this point in the history
  • Loading branch information
sebt3 committed Feb 19, 2024
1 parent 90e7aef commit 445aacd
Show file tree
Hide file tree
Showing 67 changed files with 867 additions and 466 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
!/front/queries/vynil/InstallPrepare.graphql
/front/routes/index.ts
/front/routes/*/*
/front/components/**/*.ts
/front/components/**/*.vue
!/front/components/core/*
!/front/components/navigation/*
Expand All @@ -37,5 +36,3 @@
!/front/pages/install/vynil/InstallEdit.vue
!/front/pages/install/vynil/InstallNew.vue
!/front/pages/install/vynil/installDashboard.vue
!/front/pages/config/k8s/SecretView.vue
!/front/pages/config/k8s/SecretEdit.vue
50 changes: 50 additions & 0 deletions back/resolvers/core/Container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import k8s from '@kubernetes/client-node';
import { lists as listNamespace } from '../k8s/Namespace.js';
import {kc, cache, applyFilter, applyFieldSelection, getByPath, getMeta } from '../k8slibs.js';
import { knowledge } from '../knowledge.js'
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
const short2plural = (short:string) => short.toLowerCase()+'s'
export const mutations = {
};
export const lists = {
};
export const queries = {
};
export const resolvers = {
coreContainer: {
getcoreLog: async (cont, args: object) => {
let lst:object|undefined|null = cache.get(`getcoreLog-${cont['namespace']}-${cont['pod_name']}-${cont['name']}`)
const perms = (cache.get(`permissions`)) as { apiGroup: string, resource: string, verb: string }[];
if (perms!=undefined && lst==undefined) {
if(!perms.reduce((res,perm) => res||((perm.verb=='*'||perm.verb=='get')&&(
['*','pods/log'].includes(perm.resource) && ['*',''].includes(perm.apiGroup)
)), false)) {
cache.set(`getcoreLog-${cont['namespace']}-${cont['pod_name']}-${cont['name']}`, undefined, 60);
return []
}
}
if (lst==undefined && typeof cont['namespace']==='string' && typeof cont['pod_name']==='string' && typeof cont['name']==='string') {
try {
const res = (await k8sApi.readNamespacedPodLog(cont['pod_name'],cont['namespace'],cont['name'],false,undefined,undefined,undefined,undefined,undefined,typeof args['maxLines']== 'number'?args['maxLines']:500)).body
if (typeof res==='string') {
lst = {lines: (res as string).split('\n')}
cache.set(`getcoreLog-${cont['namespace']}-${cont['pod_name']}-${cont['name']}`, lst, 2);
} else {
lst = {lines: []}
cache.set(`getcoreLog-${cont['namespace']}-${cont['pod_name']}-${cont['name']}`, lst, 2);
}
} catch (err) {
if (typeof err === 'object' && (err as object)['body'] !=undefined) {
if ((err as object)['body']['reason']!='Forbidden') {
console.error('error', (err as object)['body']);
} else {
cache.set(`getcoreLog-${cont['namespace']}-${cont['pod_name']}-${cont['name']}`, undefined, 2);
}
} else {console.error('error', err)}
return null
}
}
return lst
},
}
};
70 changes: 70 additions & 0 deletions back/resolvers/core/Event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import k8s from '@kubernetes/client-node';
import { lists as listNamespace } from '../k8s/Namespace.js';
import {kc, cache, applyFilter, applyFieldSelection} from '../k8slibs.js';
import { knowledge } from '../knowledge.js'
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
const short2plural = (short:string) => short.toLowerCase()+'s'
export const mutations = {
};
export const lists = {
coreEvent: async (parent, args: object) => {
let lst:Array<object>|undefined = cache.get('coreEvent')
const perms = (cache.get(`permissions`)) as { apiGroup: string, resource: string, verb: string }[];
if (perms!=undefined && lst==undefined) {
if(!perms.reduce((res,perm) => res||((perm.verb=='*'||perm.verb=='list')&&(
(perm.resource=='*' && perm.apiGroup=='*') ||
(perm.resource=='*' && knowledge.filter(o=>o.group=='core'&&o.short=='Event'&&perm.apiGroup==o.apiGroup).length>0) ||
(knowledge.filter(o=>o.group=='k8s'&&o.short=='APIService'&&(perm.apiGroup=='*'||perm.apiGroup==o.apiGroup)).map(o=>short2plural(o.short)).includes(perm.resource))
)), false)) {
cache.set('coreEvent', [], 60);
return []
}
}
if (lst==undefined) {
try {
const res = await k8sApi.listEventForAllNamespaces()
lst = ((res as object)['body']['items'] as Array<object>)
cache.set('coreEvent', lst, 2);
} catch (err) {
if (typeof err === 'object' && (err as object)['body'] !=undefined) {
if ((err as object)['body']['reason']!='Forbidden') {
console.error('error', (err as object)['body']);
} else {
try {
const nss = await listNamespace.k8sNamespace(parent, args)
const lst = (await Promise.all(nss.map(n=>n['metadata']['name']).map(async (ns)=>{
return (await k8sApi.listNamespacedEvent(ns)).body.items
}))).flat().filter((v)=>v!=null)
cache.set('coreEvent', lst, 2);
return lst.filter(o=>typeof parent != 'object' || parent==null || (
o['involvedObject']!=undefined && o['involvedObject']['name'] == parent['metadata']['name'] && o['involvedObject']['namespace'] == parent['metadata']['namespace']
)).filter(o=> typeof args['namespace'] != 'string' || (
o['involvedObject']!=undefined && o['involvedObject']['namespace']==args['namespace']
)).filter(o=>applyFilter(o,args)).map(o=>applyFieldSelection(o,args))
} catch (err) {
if (typeof err === 'object' && (err as object)['body'] !=undefined) {
if ((err as object)['body']['reason']!='Forbidden') {
console.error('error', (err as object)['body']);
} else {
cache.set('coreEvent', [], 2);
}
} else {console.error('error', err)}
return []
}
}
} else {console.error('error', err)}
return []
}
}
return lst.filter(o=>typeof parent != 'object' || parent==null || (
o['involvedObject']!=undefined && o['involvedObject']['name'] == parent['metadata']['name'] && o['involvedObject']['namespace'] == parent['metadata']['namespace']
)).filter(o=> typeof args['namespace'] != 'string' || (
o['involvedObject']!=undefined && o['involvedObject']['namespace']==args['namespace']
)).filter(o=>applyFilter(o,args)).map(o=>applyFieldSelection(o,args))
}
};
export const queries = {
coreEvent: lists.coreEvent,
};
export const resolvers = {
};
10 changes: 9 additions & 1 deletion back/resolvers/core/custom.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
// noGramoGenerator
import GraphQLJSON, { GraphQLJSONObject } from 'graphql-type-json';
import { gramoConfig } from '../../config.js'
import { queries as containerQueries, resolvers as containerResolvers, mutations as containerMutations } from './Container.js';
import { queries as eventQueries, resolvers as eventResolvers, mutations as eventMutations } from './Event.js';

export const queries = {
gramoConfig: () => gramoConfig,
...containerQueries,
...eventQueries,
};

export const resolvers = {
JSON: GraphQLJSON,
JSONObject: GraphQLJSONObject
JSONObject: GraphQLJSONObject,
...containerResolvers,
...eventResolvers,
};

export const mutations = {
...containerMutations,
...eventMutations,
};
36 changes: 31 additions & 5 deletions back/schema/core.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ input ExcludeInclude {
input queryParameters {
filters: [Filter!]
excludes: [ExcludeInclude!]
maxLines: Int
}
input metadataMore {
labels: [Label!]
Expand Down Expand Up @@ -80,11 +81,6 @@ type vynilCategory {
name: String!
providevynilPackage(params: queryParameters): [vynilPackage!]
}
type coreUrl {
proto: String
host: String
path: String
}
type vynilPackage {
name: String!
commit_id: ID!
Expand All @@ -93,8 +89,38 @@ type vynilPackage {
consumevynilDistrib: vynilDistrib!
options: JSONObject!
}
type coreUrl {
proto: String
host: String
path: String
}
type coreEvent {
metadata: metadata
source: JSONObject
involvedObject: JSONObject
type: String
action: String
count: Int
message: String
reason: String
reportingComponent: String
reportingInstance: String
}
type coreLog {
lines: [String]!
}
type coreContainer {
namespace: String!
pod_name: String!
name: String!
init: Boolean
spec: JSONObject
status: JSONObject
getcoreLog(params: queryParameters): coreLog
}
type Query {
gramoConfig: GramoConfig
coreEvent(params: queryParameters): [coreEvent]
vynilCategory(params: queryParameters): [vynilCategory]
vynilPackage(params: queryParameters): [vynilPackage]
}
79 changes: 79 additions & 0 deletions front/components/core/ContainerList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<script setup lang="ts">
import { onMounted } from "vue";
import { elude, getColor, getConditionColor} from "../../libs/core/"
import { usePod, getProperties, PodDefinition } from '../../libs/k8s/Pod.js'
import { colorPod, iconPod } from '../../libs/k8s/custom.js'
import { QTableColumn } from 'quasar'
import { ref, useCore, tableColumnAlign } from '../../libs/core'
const { can, writeProperties, onlyReadProperties, toParentView, viewer, viewerUpdate, toEdit, actionDelete, setNamespacedItemFromRoute } = usePod();setNamespacedItemFromRoute();
defineEmits(['refresh','on-delete']);
const props=withDefaults(defineProps<{model: object[], useAction?:boolean, useRefresh?: boolean, showNamespace?:boolean}>(),{
useAction: false,
useRefresh: true,
showNamespace: false,
});
console.log('ContainerList',props.model)
const tab=ref('list')
const container=ref(props.model.map(c=>c['name'])[0])
const { pagination } = useCore();
const ContainerColumns:Array<QTableColumn> = ((props.showNamespace?[{name: 'Namespace', label: 'Namespace', field: row => row.namespace, sortable: true, align: tableColumnAlign.left}]:[]) as QTableColumn[]).concat([
{name: 'Init', label: 'Init?', field: row => row.init?'init':'main', sortable: true, align: tableColumnAlign.left},
{name: 'Name', label: 'Name', field: row => row.name, sortable: true, align: tableColumnAlign.left},
{name: 'Image', label: 'Image', field: row => row.spec.image, sortable: true, align: tableColumnAlign.left},
{name: 'Status', label: 'Status', field: row => null, sortable: true, align: tableColumnAlign.left},
{name: 'Action', label: 'Action', field: row => null, sortable: true, align: tableColumnAlign.left},
] as QTableColumn[]);
const filter = ref('');
onMounted(() => {
viewerUpdate(onlyReadProperties(props.model));
})
function switchToLog(obj) {
container.value=obj['name']
tab.value='logs'
}
</script>
<template>
<q-card bordered class="q-ma-sm">
<q-tabs v-model="tab" :class="`bg-${ colorPod } text-grey-2`" active-color="white" align="justify">
<q-avatar :icon="iconPod" />
<q-tab label="Containers" name="list" />
<q-tab label="Logs" name="logs" />
</q-tabs>
<q-tab-panels v-model="tab" animated>
<q-tab-panel name="list" :class="`bg-${ colorPod }-1`">
<q-table :rows="model" :class="`no-shadow bg-${ colorPod }-1`" :columns="ContainerColumns" v-model:pagination="pagination" :filter="filter" hide-bottom>
<template v-slot:body-cell-Status="props">
<q-td :props="props">
<q-chip class="float-right text-white text-capitalize" v-if="typeof props.row.status.state.terminated==='object'"
:label="props.row.status.state.terminated.reason" :color="props.row.status.state.terminated.exitCode==0?'positive':'negative'">
<q-tooltip>exit code :{{ props.row.status.state.terminated.exitCode }}</q-tooltip>
</q-chip>
<q-chip class="float-right text-white text-capitalize" v-if="typeof props.row.status.state.running==='object'"
label="Running" color="positive">
<q-tooltip>Started at:{{ props.row.status.state.running.startedAt }}</q-tooltip>
</q-chip>
</q-td>
</template>
<template v-slot:body-cell-Action="props">
<q-td :props="props">
<q-btn icon="output" size="sm" class="q-ml-sm" flat dense @click="switchToLog(props.row)">
<q-tooltip>View {{ props.row.name }} logs</q-tooltip>
</q-btn>
</q-td>
</template>
</q-table>
</q-tab-panel>
<q-tab-panel name="logs" :class="`bg-${ colorPod }-1`">
<q-toolbar :class="`bg-${ colorPod } text-white q-my-md shadow-2`">
<q-space />
<q-select v-model="container" :options="model.map(o=>o['name'])" label="Container" label-color="white" style="min-width: 350px">
</q-select>
<q-space />
</q-toolbar>
<pre v-if="model.filter(c=>c['name']==container).length>0" class="bg-black text-white">
{{ model.filter(c=>c['name']==container)[0]['getcoreLog']['lines'].join('\n') }}
</pre>
</q-tab-panel>
</q-tab-panels>
</q-card>
</template>
41 changes: 41 additions & 0 deletions front/components/core/EventList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script setup lang="ts">
defineEmits(['refresh','on-delete']);
const props=withDefaults(defineProps<{model: object[], useAction?:boolean, useRefresh?: boolean, showNamespace?:boolean}>(),{
useAction: false,
useRefresh: true,
showNamespace: false,
});
console.log('EventList',props.model)
</script>
<template>
<q-timeline dense>
<q-timeline-entry v-for="(item, key) in model" v-bind:key="key"
:title="item['reason']"
:subtitle="item['metadata']['creationTimestamp']"
:color="item['type']=='Warning'?'warning':'secondary'"
:icon="item['type']=='Warning'?'warning_amber':undefined"
:body="item['message']"
/>

<q-timeline-entry
title="Event Title"
subtitle="February 21, 1986"
icon="delete"
>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</q-timeline-entry>

<q-timeline-entry
title="Event Title"
subtitle="February 22, 1986"
color="orange"
icon="done_all"
>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</q-timeline-entry>
</q-timeline>
</template>
2 changes: 2 additions & 0 deletions front/components/navigation/MainMenu.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import MainMenuLinks from './MainMenuLinks.vue';
import RefreshRateSelector from './RefreshRateSelector.vue';
import { ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useNavigationStoreRef } from '../../stores'
Expand Down Expand Up @@ -54,6 +55,7 @@ function toggleLeftDrawer() {
<q-icon name="dashboard" />
</template>
</q-select>
<RefreshRateSelector />
</q-toolbar>
</q-header>

Expand Down
2 changes: 1 addition & 1 deletion front/components/navigation/MainMenuLinks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const toNS = props.targetNS!=undefined?{ name: props.targetNS }:props.target!=un
<q-item-label v-if="caption != ''" caption>{{elude(caption, maxCaptionLength)}}</q-item-label>
</q-item-section>
</template>
<MainMenuLinks v-for="child in children" :key="`${child.title}`" v-bind="child" />
<MainMenuLinks v-for="child in children" :key="`${child.title}`" v-bind="child" :level="level+1" />
</q-expansion-item>
</div>
<div v-else>
Expand Down
28 changes: 28 additions & 0 deletions front/components/navigation/RefreshRateSelector.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useNavigationStore } from '../../stores'
const navigation = useNavigationStore()
const options = ref([
{label: 'off', value: undefined},
{label: '2s', value: 2000},
{label: '10s', value: 10000},
{label: '1m', value: 60000},
{label: '2m', value: 120000},
{label: '5m', value: 60000},
]);
const model = ref(options.value[0])
function onRefreshChange() {
navigation.setPoolInterval(model.value.value)
}
navigation.setPoolInterval(model.value.value)
</script>
<template>
<q-select
v-model="model"
@update:model-value="onRefreshChange"
:options="options" standout>
<template v-slot:prepend>
<q-icon name="refresh" />
</template>
</q-select>
</template>
Loading

0 comments on commit 445aacd

Please sign in to comment.