Skip to content

Commit

Permalink
0.2.3 CRD improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
sebt3 committed Mar 15, 2024
1 parent adaf9b1 commit 135b1c7
Show file tree
Hide file tree
Showing 81 changed files with 1,562 additions and 291 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
!/front/libs/i18n/*
!/front/libs/core/*
/front/pages/**/*.vue
!/front/pages/core/*.vue
!/front/pages/install/vynil/DistribView.vue
!/front/pages/install/vynil/InstallView.vue
!/front/pages/install/vynil/InstallEdit.vue
Expand Down
122 changes: 122 additions & 0 deletions back/resolvers/core/CrdObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import k8s from '@kubernetes/client-node';
import rfc6902 from 'rfc6902';
import {kc, getMeta, getMetaNS } from '../k8slibs.js';
const k8sApi = kc.makeApiClient(k8s.CustomObjectsApi);
export const mutations = {
clusteredCrdObjectCreate: async (_parent, args: object) => {
const payload = {
apiVersion: `${args['group']}/${args['version']}`,
kind: args['kind'],
metadata: getMeta(args),
"spec": args['spec'],
}
try {
const res = await k8sApi.createClusterCustomObject(args['group'],args['version'],args['plural'], payload)
return res.body
} catch (err) {
if (typeof err === 'object' && (err as object)['body'] !=undefined && (err as object)['statusCode'] !=undefined) {
if ((err as object)['statusCode'] != 404 && (err as object)['body']['reason']!='Forbidden') {
console.error('error', (err as object)['body']);
}
} else {console.error('error', err)}
}
return null
},
clusteredCrdObjectDelete: async (_parent, args: object) => {
try {
const res = await k8sApi.deleteClusterCustomObject(args['group'],args['version'],args['plural'], args['name'])
return res.body
} catch (err) {
if (typeof err === 'object' && (err as object)['body'] !=undefined && (err as object)['statusCode'] !=undefined) {
if ((err as object)['statusCode'] != 404 && (err as object)['body']['reason']!='Forbidden') {
console.error('error', (err as object)['body']);
}
} else {console.error('error', err)}
}
return null
},
clusteredCrdObjectPatch: async (_parent, args: object) => {
const request = {
apiVersion: `${args['group']}/${args['version']}`,
kind: args['kind'],
metadata: getMeta(args),
}
if (args['spec'] != undefined && args['spec'] != null)
request["spec"] = args['spec'];
try {
const resGet = await k8sApi.getClusterCustomObject(args['group'],args['version'],args['plural'], args['metadata']['name'])
const payload = rfc6902.createPatch(resGet.body,request).filter(rule => !rule.path.startsWith('/status') && !['/metadata/creationTimestamp', '/metadata/finalizers', '/metadata/generation', '/metadata/managedFields', '/metadata/resourceVersion','/metadata/uid'].includes(rule.path) )
const options = { "headers": { "Content-type": k8s.PatchUtils.PATCH_FORMAT_JSON_PATCH}};
const res = await k8sApi.patchClusterCustomObject(args['group'],args['version'],args['plural'], args['metadata']['name'], payload, undefined, undefined, undefined, options)
return res.body
} catch (err) {
if (typeof err === 'object' && (err as object)['body'] !=undefined && (err as object)['statusCode'] !=undefined) {
if ((err as object)['statusCode'] != 404 && (err as object)['body']['reason']!='Forbidden') {
console.error('error', (err as object)['body']);
}
} else {console.error('error', err)}
}
return null
},
namespacedCrdObjectCreate: async (_parent, args: object) => {
const payload = {
apiVersion: `${args['group']}/${args['version']}`,
kind: args['kind'],
metadata: getMetaNS(args),
"spec": args['spec'],
}
try {
const res = await k8sApi.createNamespacedCustomObject(args['group'],args['version'],args['metadata']['namespace'],args['plural'], payload)
return res.body
} catch (err) {
if (typeof err === 'object' && (err as object)['body'] !=undefined && (err as object)['statusCode'] !=undefined) {
if ((err as object)['statusCode'] != 404 && (err as object)['body']['reason']!='Forbidden') {
console.error('error', (err as object)['body']);
}
} else {console.error('error', err)}
}
return null
},
namespacedCrdObjectDelete: async (_parent, args: object) => {
try {
const res = await k8sApi.deleteNamespacedCustomObject(args['group'],args['version'],args['namespace'],args['plural'], args['name'])
return res.body
} catch (err) {
if (typeof err === 'object' && (err as object)['body'] !=undefined && (err as object)['statusCode'] !=undefined) {
if ((err as object)['statusCode'] != 404 && (err as object)['body']['reason']!='Forbidden') {
console.error('error', (err as object)['body']);
}
} else {console.error('error', err)}
}
return null
},
namespacedCrdObjectPatch: async (_parent, args: object) => {
const request = {
apiVersion: `${args['group']}/${args['version']}`,
kind: args['kind'],
metadata: getMetaNS(args),
}
if (args['spec'] != undefined && args['spec'] != null)
request["spec"] = args['spec'];
try {
const resGet = await k8sApi.getNamespacedCustomObject(args['group'],args['version'],args['metadata']['namespace'],args['plural'], args['metadata']['name'])
const payload = rfc6902.createPatch(resGet.body,request).filter(rule => !rule.path.startsWith('/status') && !['/metadata/creationTimestamp', '/metadata/finalizers', '/metadata/generation', '/metadata/managedFields', '/metadata/resourceVersion','/metadata/uid'].includes(rule.path) )
const options = { "headers": { "Content-type": k8s.PatchUtils.PATCH_FORMAT_JSON_PATCH}};
const res = await k8sApi.patchNamespacedCustomObject(args['group'],args['version'],args['metadata']['namespace'],args['plural'], args['metadata']['name'], payload, undefined, undefined, undefined, options)
return res.body
} catch (err) {
if (typeof err === 'object' && (err as object)['body'] !=undefined && (err as object)['statusCode'] !=undefined) {
if ((err as object)['statusCode'] != 404 && (err as object)['body']['reason']!='Forbidden') {
console.error('error', (err as object)['body']);
}
} else {console.error('error', err)}
}
return null
},
};
export const lists = {
};
export const queries = {
};
export const resolvers = {
};
4 changes: 4 additions & 0 deletions back/resolvers/core/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@ 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';
import { queries as crdObjectQueries, resolvers as crdObjectResolvers, mutations as crdObjectMutations } from './CrdObject.js';

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

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

export const mutations = {
...containerMutations,
...eventMutations,
...crdObjectMutations,
};
13 changes: 13 additions & 0 deletions back/schema/core.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ type coreUrl {
host: String
path: String
}
type coreCrdObject {
metadata: metadata
spec: JSONObject
status: JSONObject
}
type coreEvent {
metadata: metadata
source: JSONObject
Expand Down Expand Up @@ -152,4 +157,12 @@ type Query {
coreEvent(params: queryParameters): [coreEvent]
vynilCategory(params: queryParameters): [vynilCategory]
vynilPackage(params: queryParameters): [vynilPackage]
}
type Mutation {
namespacedCrdObjectCreate(group: String!, version: String!, plural: String!, kind: String!, metadata: metadataInput!, spec: JSONObject): coreCrdObject
namespacedCrdObjectPatch(group: String!, version: String!, plural: String!, kind: String!, metadata: metadataInput!, spec: JSONObject): coreCrdObject
namespacedCrdObjectDelete(group: String!, version: String!, plural: String!, namespace: String!, name: String!): coreCrdObject
clusteredCrdObjectCreate(group: String!, version: String!, plural: String!, kind: String!, metadata: metadataInput!, spec: JSONObject): coreCrdObject
clusteredCrdObjectPatch(group: String!, version: String!, plural: String!, kind: String!, metadata: metadataInput!, spec: JSONObject): coreCrdObject
clusteredCrdObjectDelete(group: String!, version: String!, plural: String!, name: String!): coreCrdObject
}
2 changes: 1 addition & 1 deletion front/components/charts/stackedBarChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const svgRoot = ref(null);
const series = d3.stack().keys(d3.union(props.datum.map(props.axisColor))).value(([, D], key) => props.getVal(D.get(key)))(d3.index(props.datum, props.axisX, props.axisColor));
const x = d3.scaleBand().domain(d3.groupSort(props.datum, D => -d3.sum(D, props.getVal), props.axisX)).range([marginLeft, options.value.width - marginRight]).padding(0.1);
const y = d3.scaleLinear().domain([0, d3.max(series, d => d3.max(d, d => d[1]))]).rangeRound([options.value.height - marginBottom, marginTop]);
const color = props.datum.length>1?d3.scaleOrdinal().domain(series.map(d => d.key)).range(d3.schemeSpectral[series.length>4?series.length:4]).unknown("#ccc"):()=>d3.schemeSpectral[4][1];
const color = props.datum.length>1?d3.scaleOrdinal().domain(series.map(d => d.key)).range(d3.schemeSpectral[series.length>4&&series.length<11?series.length:4]).unknown("#ccc"):()=>d3.schemeSpectral[4][1];
const rotation = options.value.width/(props.datum.map(props.axisX).filter(onlyUnique).length+1)<80?-10:0
const formatValue = x => isNaN(x) ? "N/A" : x.toLocaleString("en")
onMounted(() => {
Expand Down
2 changes: 1 addition & 1 deletion front/components/core/AdviceList.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
defineEmits(['refresh','on-delete']);
const props=withDefaults(defineProps<{model: object[], useAction?:boolean, useRefresh?: boolean, showNamespace?:boolean, showKind?:boolean}>(),{
const props=withDefaults(defineProps<{model: object[], parent?:object, useAction?:boolean, useRefresh?: boolean, showNamespace?:boolean, showKind?:boolean}>(),{
useAction: false,
useRefresh: true,
showNamespace: false,
Expand Down
12 changes: 6 additions & 6 deletions front/components/core/AdviceOverview.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue'
const pieChart = defineAsyncComponent(() => import( '@/components/charts/pieChart.vue'));
const stackedBarChart = defineAsyncComponent(() => import( '@/components/charts/stackedBarChart.vue'));
import { useQuasar } from 'quasar'
const $q = useQuasar()
import {onlyUnique} from "@/components/charts/commonTools.js"
Expand All @@ -13,14 +11,16 @@ const props=withDefaults(defineProps<{model: object[], short: string, to: (strin
});
const advicelist = props.model.map(o=>o.getcoreadvice.map(p=>{ return {namespace: o.metadata.namespace, source: p.source}})).flat()
const advices = ref(advicelist.map(p=>p.namespace).filter(onlyUnique).map(namespace=>advicelist.map(p=>p.source).filter(onlyUnique).map(source=>{return {namespace, source, value: advicelist.filter(p=>p.namespace==namespace&&p.source==source).length}})).flat().filter(p=>p.value>0))
const pieChart = defineAsyncComponent(() => import( '@/components/charts/pieChart.vue'));
const stackedBarChart = defineAsyncComponent(() => import( '@/components/charts/stackedBarChart.vue'));
</script>
<template>
<q-card bordered class="q-ma-sm">
<q-card-section class="text-center text-white bg-warning">
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="advices.map(p=>p.namespace).filter(onlyUnique).length>1 && advices.map(p=>p.source).filter(onlyUnique).length>1">{{ short }} advices distribution per namespaces and sources</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="advices.map(p=>p.namespace).filter(onlyUnique).length<2 && advices.map(p=>p.source).filter(onlyUnique).length>1">{{ short }} advices per sources</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="advices.map(p=>p.namespace).filter(onlyUnique).length>1 && advices.map(p=>p.source).filter(onlyUnique).length<2">{{ short }} advices per namespace</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="advices.map(p=>p.namespace).filter(onlyUnique).length<2 && advices.map(p=>p.source).filter(onlyUnique).length<2">{{ short }} advices</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="advices.map(p=>p.namespace).filter(onlyUnique).length>1 && advices.map(p=>p.source).filter(onlyUnique).length>1">{{ $t('dashboard.advicePerNamespaceSource', {short}) }}</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="advices.map(p=>p.namespace).filter(onlyUnique).length<2 && advices.map(p=>p.source).filter(onlyUnique).length>1">{{ $t('dashboard.advicePerSource', {short}) }}</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="advices.map(p=>p.namespace).filter(onlyUnique).length>1 && advices.map(p=>p.source).filter(onlyUnique).length<2">{{ $t('dashboard.advicePerNamespace', {short}) }}</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="advices.map(p=>p.namespace).filter(onlyUnique).length<2 && advices.map(p=>p.source).filter(onlyUnique).length<2">{{ $t('dashboard.adviceShort', {short}) }}</div>
</q-card-section>
<q-card-section :class="`text-center bg-amber-${$q.dark.isActive?'10':'2'}`">
<stackedBarChart v-if="advices.map(p=>p.namespace).filter(onlyUnique).length>1 && advices.map(p=>p.source).filter(onlyUnique).length>1"
Expand Down
21 changes: 10 additions & 11 deletions front/components/core/AllProblemOverview.vue
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue'
import {chartSizeOptions,chartMarginOptions} from "../../libs/core"
const pieChart = defineAsyncComponent(() => import( '@/components/charts/pieChart.vue'));
const stackedBarChart = defineAsyncComponent(() => import( '@/components/charts/stackedBarChart.vue'));
import { useQuasar } from 'quasar'
const $q = useQuasar()
import {onlyUnique} from "@/components/charts/commonTools.js"
const props=withDefaults(defineProps<{model: {short:string,source:string}[], short: string, useAction?:boolean, useRefresh?: boolean, showNamespace?:boolean, showKind?:boolean, options?: chartSizeOptions&chartMarginOptions}>(),{
useAction: false,
useRefresh: true,
showNamespace: false,
showKind: true,
});
import {defineAsyncComponent, ref, chartSizeOptions,chartMarginOptions} from "../../libs/core"
import {onlyUnique} from "@/components/charts/commonTools.js"
import { useQuasar } from 'quasar'
const $q = useQuasar()
const problems = ref(props.model.map(p=>p.short).filter(onlyUnique).map(short=>props.model.map(p=>p.source).filter(onlyUnique).map(source=>{return {short, source, value: props.model.filter(p=>p.short==short&&p.source==source).length}})).flat().filter(p=>p.value>0))
const pieChart = defineAsyncComponent(() => import( '@/components/charts/pieChart.vue'));
const stackedBarChart = defineAsyncComponent(() => import( '@/components/charts/stackedBarChart.vue'));
</script>
<template>
<q-card bordered class="q-ma-sm">
<q-card-section class="text-center text-white bg-negative">
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="problems.map(p=>p.short).filter(onlyUnique).length>1 && problems.map(p=>p.source).filter(onlyUnique).length>1">{{ short }} problems distribution per object type and sources</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="problems.map(p=>p.short).filter(onlyUnique).length<2 && problems.map(p=>p.source).filter(onlyUnique).length>1">{{ short }} problems per sources</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="problems.map(p=>p.short).filter(onlyUnique).length>1 && problems.map(p=>p.source).filter(onlyUnique).length<2">{{ short }} problems per object type</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="problems.map(p=>p.short).filter(onlyUnique).length<2 && problems.map(p=>p.source).filter(onlyUnique).length<2">{{ short }} problems</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="problems.map(p=>p.short).filter(onlyUnique).length>1 && problems.map(p=>p.source).filter(onlyUnique).length>1">{{ $t('dashboard.problemPerNamespaceSource', {short}) }}</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="problems.map(p=>p.short).filter(onlyUnique).length<2 && problems.map(p=>p.source).filter(onlyUnique).length>1">{{ $t('dashboard.problemPerSource', {short}) }}</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="problems.map(p=>p.short).filter(onlyUnique).length>1 && problems.map(p=>p.source).filter(onlyUnique).length<2">{{ $t('dashboard.problemPerNamespace', {short}) }}</div>
<div class="text-subtitle q-mt-none q-mb-none q-pt-none q-pb-none" v-if="problems.map(p=>p.short).filter(onlyUnique).length<2 && problems.map(p=>p.source).filter(onlyUnique).length<2">{{ $t('dashboard.problemShort', {short}) }}</div>
</q-card-section>
<q-card-section :class="`text-center bg-red-${$q.dark.isActive?'10':'2'}`">
<stackedBarChart v-if="problems.map(p=>p.short).filter(onlyUnique).length>1 && problems.map(p=>p.source).filter(onlyUnique).length>1" :options="options"
Expand Down
Loading

0 comments on commit 135b1c7

Please sign in to comment.