diff --git a/src/frontend/src/app/admin/admin-routing.module.ts b/src/frontend/src/app/admin/admin-routing.module.ts index ba7e288e2..2ace03934 100644 --- a/src/frontend/src/app/admin/admin-routing.module.ts +++ b/src/frontend/src/app/admin/admin-routing.module.ts @@ -76,6 +76,8 @@ import { KubeSecretComponent } from './kubernetes/secret/kube-secret.component'; import { KubeIngressComponent } from './kubernetes/ingress/kube-ingress.component'; import { KubeStatefulsetComponent } from './kubernetes/statefulset/kube-statefulset.component'; import { KubeDaemonsetComponent } from './kubernetes/daemonset/kube-daemonset.component'; +import { KubeCronjobComponent } from './kubernetes/cronjob/kube-cronjob.component'; +import { KubeJobComponent } from './kubernetes/job/kube-job.component'; const routes: Routes = [ @@ -172,6 +174,10 @@ const routes: Routes = [ {path: 'kubernetes/statefulset/:cluster', component: KubeStatefulsetComponent}, {path: 'kubernetes/daemonset', component: KubeDaemonsetComponent}, {path: 'kubernetes/daemonset/:cluster', component: KubeDaemonsetComponent}, + {path: 'kubernetes/cronjob', component: KubeCronjobComponent}, + {path: 'kubernetes/cronjob/:cluster', component: KubeCronjobComponent}, + {path: 'kubernetes/job', component: KubeJobComponent}, + {path: 'kubernetes/job/:cluster', component: KubeJobComponent}, ...ADMINROUTES ] } diff --git a/src/frontend/src/app/admin/admin.module.ts b/src/frontend/src/app/admin/admin.module.ts index 9093638f5..72a61fc33 100644 --- a/src/frontend/src/app/admin/admin.module.ts +++ b/src/frontend/src/app/admin/admin.module.ts @@ -56,6 +56,8 @@ import { KubeSecretModule } from './kubernetes/secret/kube-secret.module'; import { KubeIngressModule } from './kubernetes/ingress/kube-ingress.module'; import { KubeStatefulsetModule } from './kubernetes/statefulset/kube-statefulset.module'; import { KubeDaemonsetModule } from './kubernetes/daemonset/kube-daemonset.module'; +import { KubeCronjobModule } from './kubernetes/cronjob/kube-cronjob.module'; +import { KubeJobModule } from './kubernetes/job/kube-job.module'; @NgModule({ imports: [ @@ -104,7 +106,9 @@ import { KubeDaemonsetModule } from './kubernetes/daemonset/kube-daemonset.modul KubeSecretModule, KubeIngressModule, KubeStatefulsetModule, - KubeDaemonsetModule + KubeDaemonsetModule, + KubeCronjobModule, + KubeJobModule ], providers: [ AdminAuthCheckGuard, diff --git a/src/frontend/src/app/admin/kubernetes/cronjob/kube-cronjob.component.html b/src/frontend/src/app/admin/kubernetes/cronjob/kube-cronjob.component.html new file mode 100644 index 000000000..e8078b21f --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/cronjob/kube-cronjob.component.html @@ -0,0 +1,51 @@ +
+
+ + + + +
+
+ + + + + + diff --git a/src/frontend/src/app/admin/kubernetes/cronjob/kube-cronjob.component.ts b/src/frontend/src/app/admin/kubernetes/cronjob/kube-cronjob.component.ts new file mode 100644 index 000000000..82f70d72d --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/cronjob/kube-cronjob.component.ts @@ -0,0 +1,67 @@ +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { MessageHandlerService } from '../../../shared/message-handler/message-handler.service'; +import { ClusterService } from '../../../shared/client/v1/cluster.service'; +import { AuthService } from '../../../shared/auth/auth.service'; +import { AceEditorComponent } from '../../../shared/ace-editor/ace-editor.component'; +import { KubernetesClient } from '../../../shared/client/v1/kubernetes/kubernetes'; +import { KubeResourceCronJob } from '../../../shared/shared.const'; +import { KubernetesNamespacedResource } from '../../../shared/base/kubernetes-namespaced/kubernetes-namespaced-resource'; +import { DeletionDialogComponent } from '../../../shared/deletion-dialog/deletion-dialog.component'; +import { MigrationComponent } from './migration/migration.component'; +import { ListCronjobComponent } from './list-cronjob/list-cronjob.component'; + +const showState = { + 'name': {hidden: false}, + 'label': {hidden: false}, + 'schedule': {hidden: false}, + 'suspend': {hidden: false}, + 'active': {hidden: false}, + 'lastSchedule': {hidden: false}, + 'age': {hidden: false}, +}; + +@Component({ + selector: 'wayne-kube-cronjob', + templateUrl: './kube-cronjob.component.html' +}) + +export class KubeCronjobComponent extends KubernetesNamespacedResource implements OnInit, OnDestroy { + @ViewChild(ListCronjobComponent) + listResourceComponent: ListCronjobComponent; + + @ViewChild(AceEditorComponent) + aceEditorModal: AceEditorComponent; + + @ViewChild(DeletionDialogComponent) + deletionDialogComponent: DeletionDialogComponent; + + @ViewChild(MigrationComponent) + migrationComponent: MigrationComponent; + + constructor(public kubernetesClient: KubernetesClient, + public route: ActivatedRoute, + public router: Router, + public clusterService: ClusterService, + public authService: AuthService, + public messageHandlerService: MessageHandlerService) { + super(kubernetesClient, route, router, clusterService, authService, messageHandlerService); + super.registResourceType('cronjob'); + super.registKubeResource(KubeResourceCronJob); + super.registShowSate(showState); + } + + ngOnInit() { + super.ngOnInit(); + } + + ngOnDestroy(): void { + super.ngOnDestroy(); + } + + + migration(obj: any) { + this.migrationComponent.openModal(this.cluster, obj); + } + +} diff --git a/src/frontend/src/app/admin/kubernetes/cronjob/kube-cronjob.module.ts b/src/frontend/src/app/admin/kubernetes/cronjob/kube-cronjob.module.ts new file mode 100644 index 000000000..f3e267a62 --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/cronjob/kube-cronjob.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../../../shared/shared.module'; +import { ReactiveFormsModule } from '@angular/forms'; +import { KubeCronjobComponent } from './kube-cronjob.component'; +import { KubernetesClient } from '../../../shared/client/v1/kubernetes/kubernetes'; +import { DeletionDialogModule } from '../../../shared/deletion-dialog/deletion-dialog.module'; +import { MigrationComponent } from './migration/migration.component'; +import { ListCronjobComponent } from './list-cronjob/list-cronjob.component'; + +@NgModule({ + imports: [ + SharedModule, + ReactiveFormsModule, + DeletionDialogModule + ], + providers: [ + KubernetesClient + ], + exports: [ + KubeCronjobComponent, + ListCronjobComponent + ], + declarations: [ + KubeCronjobComponent, + ListCronjobComponent, + MigrationComponent + ] +}) + +export class KubeCronjobModule { +} diff --git a/src/frontend/src/app/admin/kubernetes/cronjob/list-cronjob/list-cronjob.component.html b/src/frontend/src/app/admin/kubernetes/cronjob/list-cronjob/list-cronjob.component.html new file mode 100644 index 000000000..614d90d38 --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/cronjob/list-cronjob/list-cronjob.component.html @@ -0,0 +1,65 @@ + + + + {{'ADMIN.KUBERNETES.CRONJOB.LIST.NAME' | translate}} + + + + + {{'ADMIN.KUBERNETES.CRONJOB.LIST.LABEL' | translate}} + + + + + {{'ADMIN.KUBERNETES.CRONJOB.LIST.SCHEDULE' | translate}} + + + + + {{'ADMIN.KUBERNETES.CRONJOB.LIST.SUSPEND' | translate}} + + + + + {{'ADMIN.KUBERNETES.CRONJOB.LIST.ACTIVE' | translate}} + + + + + {{'ADMIN.KUBERNETES.CRONJOB.LIST.LAST_SCHEDULE' | translate}} + + + + + {{'ADMIN.KUBERNETES.CRONJOB.LIST.AGE' | translate}} + + + + + + + + + + + {{ obj.metadata.name }} + + + + {{ obj.spec.schedule }} + {{ obj.spec.suspend }} + {{ getActiveJobs(obj) }} + {{ obj.status.lastScheduleTime | relativeTime }} + {{ obj.metadata.creationTimestamp | relativeTime}} + + + + + + diff --git a/src/frontend/src/app/admin/kubernetes/cronjob/list-cronjob/list-cronjob.component.ts b/src/frontend/src/app/admin/kubernetes/cronjob/list-cronjob/list-cronjob.component.ts new file mode 100644 index 000000000..7eccf0f9a --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/cronjob/list-cronjob/list-cronjob.component.ts @@ -0,0 +1,28 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { KubernetesNamespacedListResource } from '../../../../shared/base/kubernetes-namespaced/kubernetes-namespaced-list-resource'; +import { TplDetailService } from '../../../../shared/tpl-detail/tpl-detail.service'; +import { KubeCronJob } from '../../../../shared/model/v1/kubernetes/cronjob'; + +@Component({ + selector: 'wayne-list-cronjob', + templateUrl: './list-cronjob.component.html' +}) + +export class ListCronjobComponent extends KubernetesNamespacedListResource { + @Input() resources: any[]; + @Input() showState: object; + + @Output() migration = new EventEmitter(); + + constructor(public tplDetailService: TplDetailService) { + super(tplDetailService); + } + + getActiveJobs(obj: KubeCronJob): number { + return obj.status.active ? obj.status.active.length : 0; + } + + migrationResource(obj: any) { + this.migration.emit(obj); + } +} diff --git a/src/frontend/src/app/admin/kubernetes/cronjob/migration/migration.component.html b/src/frontend/src/app/admin/kubernetes/cronjob/migration/migration.component.html new file mode 100644 index 000000000..488f54eea --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/cronjob/migration/migration.component.html @@ -0,0 +1,32 @@ + + + + diff --git a/src/frontend/src/app/admin/kubernetes/cronjob/migration/migration.component.ts b/src/frontend/src/app/admin/kubernetes/cronjob/migration/migration.component.ts new file mode 100644 index 000000000..4ac9cbffb --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/cronjob/migration/migration.component.ts @@ -0,0 +1,64 @@ +import { Component, OnInit } from '@angular/core'; +import { AppService } from '../../../../shared/client/v1/app.service'; +import { AceEditorService } from '../../../../shared/ace-editor/ace-editor.service'; +import { MessageHandlerService } from '../../../../shared/message-handler/message-handler.service'; +import { MigrationResource } from '../../../../shared/base/kubernetes-namespaced/migration-resource'; +import { KubernetesClient } from '../../../../shared/client/v1/kubernetes/kubernetes'; +import { KubeResourceCronJob } from '../../../../shared/shared.const'; +import { CronjobService } from '../../../../shared/client/v1/cronjob.service'; +import { CronjobTplService } from '../../../../shared/client/v1/cronjobtpl.service'; +import { Cronjob } from '../../../../shared/model/v1/cronjob'; +import { CronjobTpl } from '../../../../shared/model/v1/cronjobtpl'; + +@Component({ + selector: 'kube-migration', + templateUrl: 'migration.component.html' +}) +export class MigrationComponent extends MigrationResource implements OnInit { + + constructor(private cronjobService: CronjobService, + private cronjobTplService: CronjobTplService, + public appService: AppService, + public kubernetesClient: KubernetesClient, + public aceEditorService: AceEditorService, + public messageHandlerService: MessageHandlerService) { + super(kubernetesClient, appService, aceEditorService, messageHandlerService); + super.registKubeResource(KubeResourceCronJob); + } + + ngOnInit(): void { + super.ngOnInit(); + } + + onSubmit() { + if (this.isSubmitOnGoing) { + return; + } + this.isSubmitOnGoing = true; + const resource = new Cronjob(); + resource.name = this.obj.metadata.name; + resource.appId = this.selectedApp.id; + this.cronjobService.create(resource).subscribe( + resp => { + const data = resp.data; + const tpl = new CronjobTpl(); + tpl.name = this.obj.metadata.name; + tpl.cronjobId = data.id; + tpl.template = JSON.stringify(this.obj); + tpl.description = 'migration from kubernetes. '; + this.cronjobTplService.create(tpl, this.selectedApp.id).subscribe( + () => { + this.messageHandlerService.showSuccess('资源创建成功!请前往前台手动发布到相应机房!'); + }, + error => { + this.messageHandlerService.handleError(error); + }); + }, + error => { + this.messageHandlerService.handleError(error); + } + ); + this.modalOpened = false; + } + +} diff --git a/src/frontend/src/app/admin/kubernetes/job/kube-job.component.html b/src/frontend/src/app/admin/kubernetes/job/kube-job.component.html new file mode 100644 index 000000000..e6a2af7b5 --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/job/kube-job.component.html @@ -0,0 +1,45 @@ +
+
+ + + + +
+
+ + + + + diff --git a/src/frontend/src/app/admin/kubernetes/job/kube-job.component.ts b/src/frontend/src/app/admin/kubernetes/job/kube-job.component.ts new file mode 100644 index 000000000..d5cb18a4e --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/job/kube-job.component.ts @@ -0,0 +1,54 @@ +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { MessageHandlerService } from '../../../shared/message-handler/message-handler.service'; +import { ClusterService } from '../../../shared/client/v1/cluster.service'; +import { AuthService } from '../../../shared/auth/auth.service'; +import { AceEditorComponent } from '../../../shared/ace-editor/ace-editor.component'; +import { KubernetesClient } from '../../../shared/client/v1/kubernetes/kubernetes'; +import { KubeResourceJob } from '../../../shared/shared.const'; +import { KubernetesNamespacedResource } from '../../../shared/base/kubernetes-namespaced/kubernetes-namespaced-resource'; +import { DeletionDialogComponent } from '../../../shared/deletion-dialog/deletion-dialog.component'; +import { ListJobComponent } from './list-job/list-job.component'; + +const showState = { + 'name': {hidden: false}, + 'label': {hidden: false}, + 'age': {hidden: false}, +}; + +@Component({ + selector: 'wayne-kube-job', + templateUrl: './kube-job.component.html' +}) + +export class KubeJobComponent extends KubernetesNamespacedResource implements OnInit, OnDestroy { + @ViewChild(ListJobComponent) + listResourceComponent: ListJobComponent; + + @ViewChild(AceEditorComponent) + aceEditorModal: AceEditorComponent; + + @ViewChild(DeletionDialogComponent) + deletionDialogComponent: DeletionDialogComponent; + + constructor(public kubernetesClient: KubernetesClient, + public route: ActivatedRoute, + public router: Router, + public clusterService: ClusterService, + public authService: AuthService, + public messageHandlerService: MessageHandlerService) { + super(kubernetesClient, route, router, clusterService, authService, messageHandlerService); + super.registResourceType('job'); + super.registKubeResource(KubeResourceJob); + super.registShowSate(showState); + } + + ngOnInit() { + super.ngOnInit(); + } + + ngOnDestroy(): void { + super.ngOnDestroy(); + } + +} diff --git a/src/frontend/src/app/admin/kubernetes/job/kube-job.module.ts b/src/frontend/src/app/admin/kubernetes/job/kube-job.module.ts new file mode 100644 index 000000000..2e733921f --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/job/kube-job.module.ts @@ -0,0 +1,29 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../../../shared/shared.module'; +import { ReactiveFormsModule } from '@angular/forms'; +import { KubeJobComponent } from './kube-job.component'; +import { KubernetesClient } from '../../../shared/client/v1/kubernetes/kubernetes'; +import { DeletionDialogModule } from '../../../shared/deletion-dialog/deletion-dialog.module'; +import { ListJobComponent } from './list-job/list-job.component'; + +@NgModule({ + imports: [ + SharedModule, + ReactiveFormsModule, + DeletionDialogModule + ], + providers: [ + KubernetesClient + ], + exports: [ + KubeJobComponent, + ListJobComponent + ], + declarations: [ + KubeJobComponent, + ListJobComponent + ] +}) + +export class KubeJobModule { +} diff --git a/src/frontend/src/app/admin/kubernetes/job/list-job/list-job.component.html b/src/frontend/src/app/admin/kubernetes/job/list-job/list-job.component.html new file mode 100644 index 000000000..434073bf7 --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/job/list-job/list-job.component.html @@ -0,0 +1,62 @@ + + + + {{'ADMIN.KUBERNETES.JOB.LIST.NAME' | translate}} + + + + + {{'ADMIN.KUBERNETES.JOB.LIST.LABEL' | translate}} + + + + + {{'ADMIN.KUBERNETES.JOB.LIST.IMAGES' | translate}} + + + + + {{'ADMIN.KUBERNETES.JOB.LIST.STATUS' | translate}} + + + + + {{'ADMIN.KUBERNETES.JOB.LIST.AGE' | translate}} + + + + + + + + + + {{ obj.metadata.name }} + + + + + + + + {{obj.status.succeeded ? obj.status.succeeded : 0}}/{{obj.spec.completions }} + + + + {{ obj.metadata.creationTimestamp | relativeTime}} + + + + + + diff --git a/src/frontend/src/app/admin/kubernetes/job/list-job/list-job.component.ts b/src/frontend/src/app/admin/kubernetes/job/list-job/list-job.component.ts new file mode 100644 index 000000000..27a3af5f4 --- /dev/null +++ b/src/frontend/src/app/admin/kubernetes/job/list-job/list-job.component.ts @@ -0,0 +1,18 @@ +import { Component, Input } from '@angular/core'; +import { KubernetesNamespacedListResource } from '../../../../shared/base/kubernetes-namespaced/kubernetes-namespaced-list-resource'; +import { TplDetailService } from '../../../../shared/tpl-detail/tpl-detail.service'; + +@Component({ + selector: 'wayne-list-job', + templateUrl: './list-job.component.html' +}) + +export class ListJobComponent extends KubernetesNamespacedListResource { + @Input() resources: any[]; + @Input() showState: object; + + constructor(public tplDetailService: TplDetailService) { + super(tplDetailService); + } + +} diff --git a/src/frontend/src/app/admin/sidenav/sidenav.component.html b/src/frontend/src/app/admin/sidenav/sidenav.component.html index fa3dabe2e..444fce176 100644 --- a/src/frontend/src/app/admin/sidenav/sidenav.component.html +++ b/src/frontend/src/app/admin/sidenav/sidenav.component.html @@ -56,6 +56,20 @@ DaemonSet + + + CronJob + + + + Job +
-

{{'CRONJOB.LIST' | translate}}

+

{{'CRONJOB.JOB' | translate}}

{{job.kubeJob.status.completionTime | date: "yyyy-MM-dd HH:mm:ss"}} - - - {{pagesize}} - - {{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} of {{pagination.totalItems}} - - - + + diff --git a/src/frontend/src/app/portal/cronjob/list-job/list-job.component.ts b/src/frontend/src/app/portal/cronjob/list-job/list-job.component.ts index 2e7f98d5e..17953520a 100644 --- a/src/frontend/src/app/portal/cronjob/list-job/list-job.component.ts +++ b/src/frontend/src/app/portal/cronjob/list-job/list-job.component.ts @@ -34,6 +34,8 @@ export class ListJobComponent implements OnInit, OnDestroy { subscription: Subscription; componentName = '任务'; pageSizes: number[] = new Array(10, 20, 50); + currentPage = 1; + state: ClrDatagridStateInterface; constructor( private deletionDialogService: ConfirmationDialogService, @@ -55,6 +57,13 @@ export class ListJobComponent implements OnInit, OnDestroy { } } + pageSizeChange(pageSize: number) { + this.state.page.to = pageSize - 1; + this.state.page.size = pageSize; + this.currentPage = 1; + this.paginate.emit(this.state); + } + get pageSize() { return this._pageSize; } @@ -79,6 +88,7 @@ export class ListJobComponent implements OnInit, OnDestroy { } refresh(state?: ClrDatagridStateInterface) { + this.state = state; this.paginate.emit(state); } diff --git a/src/frontend/src/assets/i18n/zh-Hans.json b/src/frontend/src/assets/i18n/zh-Hans.json index b542f0d87..8e8cfcdc7 100644 --- a/src/frontend/src/assets/i18n/zh-Hans.json +++ b/src/frontend/src/assets/i18n/zh-Hans.json @@ -105,6 +105,7 @@ }, "CREATE": {}, "CRONJOB": { + "JOB": "任务列表", "CONFIG": "计划任务配置", "CONFIG_MESSAGE": "k8s 调度任务的时区是 UTC(+0),按小时定期执行的任务,请减去8小时调整为北京时区(+8)
例如想在北京时间每天11点(0 11 * * *)执行定时任务,则设置为(0 2 * * *)", "CREATE": "创建计划任务", @@ -691,6 +692,30 @@ "IMAGES": "镜像", "STATUS": "状态" } + }, + "CRONJOB": { + "CREATE": "创建 CronJob", + "LIST": { + "NAME": "名称", + "LABEL": "标签", + "AGE": "启动时间", + "IMAGES": "镜像", + "SCHEDULE": "计划", + "SUSPEND": "挂起", + "ACTIVE": "活跃中", + "LAST_SCHEDULE": "最近调度", + "STATUS": "状态" + } + }, + "JOB": { + "CREATE": "创建 Job", + "LIST": { + "NAME": "名称", + "LABEL": "标签", + "AGE": "启动时间", + "IMAGES": "镜像", + "STATUS": "状态" + } } } }