Skip to content

Commit 281ba30

Browse files
authored
Merge pull request #74 from kubero-dev/66-add-persistent-volumes
Feature/Add persistent volumes
2 parents d669b6e + ce8f7c8 commit 281ba30

File tree

11 files changed

+328
-30
lines changed

11 files changed

+328
-30
lines changed

client/src/components/apps/new.vue

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,89 @@
366366
</v-col>
367367
</v-row>
368368

369+
<v-divider class="ma-5"></v-divider>
370+
<!-- EXTRAVOLUMES -->
371+
<h4 class="text-uppercase">Volumes</h4>
372+
<div v-for="volume in extraVolumes" v-bind:key="volume.id">
373+
<v-row>
374+
<v-col
375+
cols="12"
376+
md="3"
377+
>
378+
<v-text-field
379+
v-model="volume.name"
380+
label="name"
381+
:readonly="app!='new'"
382+
></v-text-field>
383+
</v-col>
384+
<v-col
385+
cols="12"
386+
md="2"
387+
>
388+
<v-text-field
389+
v-model="volume.size"
390+
label="size"
391+
:readonly="app!='new'"
392+
></v-text-field>
393+
</v-col>
394+
<v-col
395+
cols="12"
396+
md="1"
397+
>
398+
<v-btn
399+
elevation="2"
400+
icon
401+
small
402+
@click="removeVolumeLine(volume.name)"
403+
>
404+
<v-icon dark >
405+
mdi-minus
406+
</v-icon>
407+
</v-btn>
408+
</v-col>
409+
</v-row>
410+
411+
<v-row>
412+
<v-col
413+
cols="12"
414+
md="3"
415+
>
416+
<v-select
417+
v-model="volume.storageClass"
418+
:items="storageclasses"
419+
label="Storage Class"
420+
:readonly="app!='new'"
421+
></v-select>
422+
</v-col>
423+
<v-col
424+
cols="12"
425+
md="3"
426+
>
427+
<v-text-field
428+
v-model="volume.mountPath"
429+
label="Mount Path"
430+
></v-text-field>
431+
</v-col>
432+
</v-row>
433+
</div>
434+
435+
<v-row>
436+
<v-col
437+
cols="12"
438+
>
439+
<v-btn
440+
elevation="2"
441+
icon
442+
small
443+
@click="addVolumeLine()"
444+
>
445+
<v-icon dark >
446+
mdi-plus
447+
</v-icon>
448+
</v-btn>
449+
</v-col>
450+
</v-row>
451+
369452
<v-divider class="ma-5"></v-divider>
370453
<!-- CRONJOBS -->
371454
<h4 class="text-uppercase">Cronjobs</h4>
@@ -615,6 +698,24 @@ export default {
615698
},
616699
*/
617700
],
701+
storageclasses : [
702+
/*
703+
'standard',
704+
'standard-fast',
705+
*/
706+
],
707+
extraVolumes: [
708+
/*
709+
{
710+
name: 'example-volume',
711+
emptyDir: false,
712+
storageClass: 'standard',
713+
size: '1Gi',
714+
accessMode: ['ReadWriteOnce']
715+
mountPath: '/example/path',
716+
},
717+
*/
718+
],
618719
addons: [
619720
/*
620721
{
@@ -660,6 +761,7 @@ export default {
660761
}
661762
},
662763
mounted() {
764+
this.loadStorageClasses();
663765
this.loadPipeline();
664766
this.loadPodsizeList();
665767
this.loadApp(); // this may lead into a race condition with the buildpacks loaded in loadPipeline
@@ -703,6 +805,13 @@ export default {
703805
704806
});
705807
},
808+
loadStorageClasses() {
809+
axios.get('/api/config/storageclasses').then(response => {
810+
for (let i = 0; i < response.data.length; i++) {
811+
this.storageclasses.push(response.data[i].name);
812+
}
813+
});
814+
},
706815
loadBranches() {
707816
708817
// encode string to base64 (for ssh url)
@@ -786,6 +895,7 @@ export default {
786895
this.autodeploy = response.data.spec.autodeploy;
787896
this.domain = response.data.spec.domain;
788897
this.envvars = response.data.spec.envVars;
898+
this.extraVolumes = response.data.spec.extraVolumes;
789899
this.containerPort = response.data.spec.image.containerPort;
790900
this.podsize = response.data.spec.podsize;
791901
this.autoscale = response.data.spec.autoscale;
@@ -838,6 +948,7 @@ export default {
838948
targetMemoryUtilizationPercentage : 80,
839949
},
840950
},
951+
extraVolumes: this.extraVolumes,
841952
cronjobs: this.cronjobFormat(this.cronjobs),
842953
addons: this.addons,
843954
@@ -899,6 +1010,7 @@ export default {
8991010
targetMemoryUtilizationPercentage : 80,
9001011
},
9011012
},
1013+
extraVolumes: this.extraVolumes,
9021014
cronjobs: this.cronjobFormat(this.cronjobs),
9031015
addons: this.addons,
9041016
})
@@ -924,6 +1036,25 @@ export default {
9241036
}
9251037
}
9261038
},
1039+
addVolumeLine() {
1040+
this.extraVolumes.push({
1041+
name: 'example-volume',
1042+
emptyDir: false,
1043+
storageClass: 'standard',
1044+
size: '1Gi',
1045+
accessModes: [
1046+
'ReadWriteOnce',
1047+
],
1048+
mountPath: '/example/path',
1049+
});
1050+
},
1051+
removeVolumeLine(index) {
1052+
for (let i = 0; i < this.extraVolumes.length; i++) {
1053+
if (this.extraVolumes[i].name === index) {
1054+
this.extraVolumes.splice(i, 1);
1055+
}
1056+
}
1057+
},
9271058
addCronjobLine() {
9281059
this.cronjobs.push({
9291060
name: 'hello world',

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "kubero",
3-
"version": "1.5.3",
3+
"version": "1.6.0",
44
"description": "Heroku clone on Kubernetes",
55
"main": "dist/index.js",
66
"author": "Gianni Carafa",

src/kubero.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ export class Kubero {
497497
podsize: this.config.podSizeList[0], //TODO select from podsizelist
498498
autoscale: false,
499499
envVars: [], //TODO use custom env vars,
500+
extraVolumes: [], //TODO Not sure how to handlle extra Volumes on PR Apps
500501
image: {
501502
containerPort: 8080, //TODO use custom containerport
502503
repository: pipeline.dockerimage, // FIXME: Maybe needs a lookup into buildpack
@@ -703,4 +704,8 @@ export class Kubero {
703704
public getNodeMetrics() {
704705
return this.kubectl.getNodeMetrics();
705706
}
707+
708+
public getStorageglasses() {
709+
return this.kubectl.getStorageglasses();
710+
}
706711
}

src/modules/application.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IApp, IKubectlMetadata, IKubectlApp, IGithubRepository, ICronjob, IPodSize} from '../types';
1+
import { IApp, IKubectlMetadata, IKubectlApp, IGithubRepository, ICronjob, IPodSize, IExtraVolume} from '../types';
22
import { IAddon } from './addons';
33

44
export class KubectlApp implements IKubectlApp{
@@ -34,6 +34,7 @@ export class App implements IApp{
3434
public autoscale: boolean
3535
//public envVars: {[key: string]: string} = {}
3636
public envVars: {}[] = []
37+
public extraVolumes: IExtraVolume[] = []
3738
public cronjobs: ICronjob[] = []
3839
public addons: IAddon[] = []
3940

@@ -156,6 +157,8 @@ export class App implements IApp{
156157

157158
this.envVars = app.envVars
158159

160+
this.extraVolumes = app.extraVolumes
161+
159162
this.cronjobs = app.cronjobs
160163

161164
this.addons = app.addons

src/modules/kubectl.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
PodMetric,
2222
PodMetricsList,
2323
NodeMetric,
24+
StorageV1Api
2425
} from '@kubernetes/client-node'
2526
import { IPipeline, IKubectlPipeline, IKubectlPipelineList, IKubectlAppList, IKuberoConfig} from '../types';
2627
import { App, KubectlApp } from './application';
@@ -34,6 +35,7 @@ export class Kubectl {
3435
private coreV1Api: CoreV1Api;
3536
private appsV1Api: AppsV1Api;
3637
private metricsApi: Metrics;
38+
private storageV1Api: StorageV1Api;
3739
private customObjectsApi: CustomObjectsApi;
3840
private kubeVersion: VersionInfo | void;
3941
private patchUtils: PatchUtils;
@@ -66,6 +68,7 @@ export class Kubectl {
6668
this.versionApi = this.kc.makeApiClient(VersionApi);
6769
this.coreV1Api = this.kc.makeApiClient(CoreV1Api);
6870
this.appsV1Api = this.kc.makeApiClient(AppsV1Api);
71+
this.storageV1Api = this.kc.makeApiClient(StorageV1Api);
6972
this.metricsApi = new Metrics(this.kc);
7073
this.patchUtils = new PatchUtils();
7174
this.customObjectsApi = this.kc.makeApiClient(CustomObjectsApi);
@@ -491,4 +494,22 @@ export class Kubectl {
491494
const metrics = await this.metricsApi.getNodeMetrics();
492495
return metrics.items;
493496
}
497+
498+
public async getStorageglasses(): Promise<Object[]> {
499+
const storageClasses = await this.storageV1Api.listStorageClass();
500+
let ret = [];
501+
for (let i = 0; i < storageClasses.body.items.length; i++) {
502+
const sc = storageClasses.body.items[i];
503+
const storageClass = {
504+
name: sc.metadata?.name,
505+
provisioner: sc.provisioner,
506+
reclaimPolicy: sc.reclaimPolicy,
507+
volumeBindingMode: sc.volumeBindingMode,
508+
//allowVolumeExpansion: sc.allowVolumeExpansion,
509+
//parameters: sc.parameters
510+
}
511+
ret.push(storageClass);
512+
}
513+
return ret;
514+
}
494515
}

src/routes/apps.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ function createApp(req: Request,) : IApp {
184184
podsize: req.body.podsize,
185185
autoscale: req.body.autoscale,
186186
envVars: req.body.envvars,
187+
extraVolumes: req.body.extraVolumes,
187188
image: {
188189
containerPort: req.body.image.containerport,
189190
repository: req.body.image.repository,

src/routes/config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,10 @@ Router.get('/config/repositories', authMiddleware, async function (req: Request,
9999
// #swagger.summary = 'Get the available repositories'
100100
res.send(await req.app.locals.kubero.getRepositories());
101101
});
102+
103+
104+
Router.get('/config/storageclasses', authMiddleware, async function (req: Request, res: Response) {
105+
// #swagger.tags = ['UI']
106+
// #swagger.summary = 'Get the available storageclasses'
107+
res.send(await req.app.locals.kubero.getStorageglasses());
108+
});

src/routes/pipelines.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ Router.put('/pipelines/:pipeline/:phase/:app', authMiddleware, async function (r
201201
domain: req.body.domain,
202202
podsize: req.body.podsize,
203203
autoscale: req.body.autoscale,
204+
extraVolumes: req.body.extraVolumes,
204205
envVars: req.body.envvars,
205206
image: {
206207
containerPort: req.body.image.containerport,

src/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export interface IApp {
7878
}
7979
}
8080

81+
extraVolumes: IExtraVolume[],
8182
cronjobs: ICronjob[]
8283
addons: IAddon[]
8384
/*
@@ -123,6 +124,15 @@ export interface IApp {
123124
*/
124125
}
125126

127+
export interface IExtraVolume {
128+
name: string,
129+
mountPath: string,
130+
emptyDir: boolean,
131+
size: string,
132+
storageClass: string,
133+
accessModes: string[],
134+
}
135+
126136
export interface ICronjob {
127137
name: string,
128138
schedule: string,

0 commit comments

Comments
 (0)