Skip to content

Commit

Permalink
Merge pull request #282 from wilhelmguo/feature/kubernetes_resource_s…
Browse files Browse the repository at this point in the history
…upport_statefulset

Add kubernetes statefulset resource
  • Loading branch information
wilhelmguo committed Feb 18, 2019
2 parents a05c23f + 64d0c9c commit 6d85b4a
Show file tree
Hide file tree
Showing 12 changed files with 351 additions and 2 deletions.
3 changes: 3 additions & 0 deletions src/frontend/src/app/admin/admin-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import { KubeEndpointComponent } from './kubernetes/endpoint/kube-endpoint.compo
import { KubeConfigmapComponent } from './kubernetes/configmap/kube-configmap.component';
import { KubeSecretComponent } from './kubernetes/secret/kube-secret.component';
import { KubeIngressComponent } from './kubernetes/ingress/kube-ingress.component';
import { KubeStatefulsetComponent } from './kubernetes/statefulset/kube-statefulset.component';


const routes: Routes = [
Expand Down Expand Up @@ -166,6 +167,8 @@ const routes: Routes = [
{path: 'kubernetes/secret/:cluster', component: KubeSecretComponent},
{path: 'kubernetes/ingress', component: KubeIngressComponent},
{path: 'kubernetes/ingress/:cluster', component: KubeIngressComponent},
{path: 'kubernetes/statefulset', component: KubeStatefulsetComponent},
{path: 'kubernetes/statefulset/:cluster', component: KubeStatefulsetComponent},
...ADMINROUTES
]
}
Expand Down
4 changes: 3 additions & 1 deletion src/frontend/src/app/admin/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { KubeEndpointModule } from './kubernetes/endpoint/kube-endpoint.module';
import { KubeConfigmapModule } from './kubernetes/configmap/kube-configmap.module';
import { KubeSecretModule } from './kubernetes/secret/kube-secret.module';
import { KubeIngressModule } from './kubernetes/ingress/kube-ingress.module';
import { KubeStatefulsetModule } from './kubernetes/statefulset/kube-statefulset.module';

@NgModule({
imports: [
Expand Down Expand Up @@ -100,7 +101,8 @@ import { KubeIngressModule } from './kubernetes/ingress/kube-ingress.module';
KubeEndpointModule,
KubeConfigmapModule,
KubeSecretModule,
KubeIngressModule
KubeIngressModule,
KubeStatefulsetModule
],
providers: [
AdminAuthCheckGuard,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<div class="clr-row">
<div class="clr-col-lg-12 clr-col-md-12 clr-col-sm-12 clr-col-xs-12">
<div class="table-search">
<div class="table-search-left">
<button class="wayne-button normal" (click)="createResource()">
{{'ADMIN.KUBERNETES.STATEFULSET.CREATE' | translate}}
</button>
<button class="wayne-button normal" (click)="retrieveResource()">
{{'ADMIN.KUBERNETES.ACTION.REFRESH' | translate}}
</button>
<wayne-filter-box (confirm)="onConfirmEvent()" (cancel)="onCancelEvent()">
<wayne-checkbox-group [(ngModel)]="showList">
<wayne-checkbox value="name">{{'ADMIN.KUBERNETES.STATEFULSET.LIST.NAME' | translate}}</wayne-checkbox>
<wayne-checkbox value="label">{{'ADMIN.KUBERNETES.STATEFULSET.LIST.LABEL' | translate}}</wayne-checkbox>
<wayne-checkbox value="containers">{{'ADMIN.KUBERNETES.STATEFULSET.LIST.IMAGES' | translate}}</wayne-checkbox>
<wayne-checkbox value="status">{{'ADMIN.KUBERNETES.STATEFULSET.LIST.STATUS' | translate}}</wayne-checkbox>
<wayne-checkbox value="age">{{'ADMIN.KUBERNETES.STATEFULSET.LIST.AGE' | translate}}</wayne-checkbox>
</wayne-checkbox-group>
</wayne-filter-box>
<label for="namespace_name" class="clr-col-md-3">{{'ADMIN.KUBERNETES.LABEL.NAMESPACE' | translate}}</label>
<wayne-select [(ngModel)]="namespace" (change)="retrieveResource()"
searchable
name="namespace_name"
[placeholder]="'PLACEHOLDER.CHOOSE' | translate"
style="margin-left: 12px;">
<wayne-option *ngFor="let ns of namespaces" [value]="ns">{{ns}}</wayne-option>
</wayne-select>
</div>
</div>

<wayne-list-statefulset
[resources]="resources"
[showState]="showState"
(delete)="onDeleteResourceEvent($event)"
(edit)="onEditResourceEvent($event)"
[page]="pageState.page"
[cluster]="cluster"
(migration)="migration($event)"
(paginate)="retrieveResource($event)">
</wayne-list-statefulset>
</div>
</div>
<kube-migration></kube-migration>
<deletion-dialog (outputObj)="confirmDeleteEvent($event)"></deletion-dialog>
<wayne-ace-editor (createOutputObj)="onCreateResourceEvent($event)" (outputObj)="onSaveResourceEvent($event)"></wayne-ace-editor>
<wayne-float-window value="{{ cluster }}">
<wayne-float-window-item *ngFor="let cluster of clusters" [value]="cluster"
(click)="jumpToHref(cluster)"></wayne-float-window-item>
</wayne-float-window>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
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 { KubeResourceStatefulSet } 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 { ListStatefulsetComponent } from './list-statefulset/list-statefulset.component';

const showState = {
'name': {hidden: false},
'label': {hidden: false},
'containers': {hidden: false},
'status': {hidden: false},
'age': {hidden: false},
};

@Component({
selector: 'wayne-kube-statefulset',
templateUrl: './kube-statefulset.component.html'
})

export class KubeStatefulsetComponent extends KubernetesNamespacedResource implements OnInit, OnDestroy {
@ViewChild(ListStatefulsetComponent)
listResourceComponent: ListStatefulsetComponent;

@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('statefulset');
super.registKubeResource(KubeResourceStatefulSet);
super.registShowSate(showState);
}

ngOnInit() {
super.ngOnInit();
}

ngOnDestroy(): void {
super.ngOnDestroy();
}


migration(obj: any) {
this.migrationComponent.openModal(this.cluster, obj);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../../../shared/shared.module';
import { ReactiveFormsModule } from '@angular/forms';
import { KubeStatefulsetComponent } from './kube-statefulset.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 { ListStatefulsetComponent } from './list-statefulset/list-statefulset.component';

@NgModule({
imports: [
SharedModule,
ReactiveFormsModule,
DeletionDialogModule
],
providers: [
KubernetesClient
],
exports: [
KubeStatefulsetComponent,
ListStatefulsetComponent
],
declarations: [
KubeStatefulsetComponent,
ListStatefulsetComponent,
MigrationComponent
]
})

export class KubeStatefulsetModule {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<clr-datagrid (clrDgRefresh)="refresh($event)">
<clr-dg-column class="col-app-name" [clrDgField]="'name'">
<ng-container *clrDgHideableColumn="showState['name']">
{{'ADMIN.KUBERNETES.STATEFULSET.LIST.NAME' | translate}}
</ng-container>
</clr-dg-column>
<clr-dg-column>
<ng-container *clrDgHideableColumn="showState['label']">
{{'ADMIN.KUBERNETES.STATEFULSET.LIST.LABEL' | translate}}
</ng-container>
</clr-dg-column>
<clr-dg-column class="col-version">
<ng-container *clrDgHideableColumn="showState['containers']">
{{'ADMIN.KUBERNETES.STATEFULSET.LIST.IMAGES' | translate}}
</ng-container>
</clr-dg-column>
<clr-dg-column>
<ng-container *clrDgHideableColumn="showState['status']">
{{'ADMIN.KUBERNETES.STATEFULSET.LIST.STATUS' | translate}}
</ng-container>
</clr-dg-column>
<clr-dg-column [clrDgField]="'creationTimestamp'">
<ng-container *clrDgHideableColumn="showState['age']">
{{'ADMIN.KUBERNETES.STATEFULSET.LIST.AGE' | translate}}
</ng-container>
</clr-dg-column>


<clr-dg-row *ngFor="let obj of resources" [clrDgItem]="obj">
<clr-dg-action-overflow>
<button class="action-item" (click)="onEditEvent(obj)">{{'ADMIN.KUBERNETES.ACTION.EDIT' | translate}}</button>
<button class="action-item" (click)="onDeleteEvent(obj)">{{'ADMIN.KUBERNETES.ACTION.DELETE' | translate}}</button>
<button class="action-item" (click)="migrationResource(obj)">{{'ADMIN.KUBERNETES.ACTION.MIGRATION' | translate}}</button>
</clr-dg-action-overflow>
<clr-dg-cell class="col-app-name"> {{ obj.metadata.name }} </clr-dg-cell>
<clr-dg-cell>
<div *ngFor="let label of obj.metadata.labels | keyvalue" class="version-text">
<a href="javascript:" (click)="versionDetail(label.key + ': ' + label.value)">{{label.key}}: {{label.value}}</a>
</div>
</clr-dg-cell>
<clr-dg-cell class="col-version">
<div *ngFor="let container of obj.spec.template.spec.containers" class="version-text">
<a href="javascript:" (click)="versionDetail(container.image)">{{container.image}}</a>
</div>
</clr-dg-cell>
<clr-dg-cell>
{{obj.status.readyReplicas ? obj.status.readyReplicas : 0 }}/{{obj.spec.replicas}}
<clr-icon style="color: #FF3434" shape="exclamation-triangle" *ngIf="obj.status.readyReplicas !== obj.spec.replicas"
class="is-solid"></clr-icon>
<clr-icon style="color: green" shape="success-standard" *ngIf="obj.status.readyReplicas === obj.spec.replicas"
class="is-solid"></clr-icon>
</clr-dg-cell>
<clr-dg-cell> {{ obj.metadata.creationTimestamp | relativeTime}} </clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>
<wayne-paginate
[(currentPage)]="currentPage"
[total]="page.totalCount"
[pageSizes]="[10, 20, 50]"
(sizeChange)="pageSizeChange($event)">
</wayne-paginate>
</clr-dg-footer>
</clr-datagrid>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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';

@Component({
selector: 'wayne-list-statefulset',
templateUrl: './list-statefulset.component.html'
})

export class ListStatefulsetComponent extends KubernetesNamespacedListResource {
@Input() resources: any[];
@Input() showState: object;

@Output() migration = new EventEmitter<any>();

constructor(public tplDetailService: TplDetailService) {
super(tplDetailService);
}

migrationResource(obj: any) {
this.migration.emit(obj);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<clr-modal [(clrModalOpen)]="modalOpened" [clrModalSize]="'xl'" #modal>
<h3 class="modal-title">迁移资源
<wayne-modal-operate [modal]="modal" *ngIf="modalOpened"></wayne-modal-operate>
</h3>
<div class="modal-body">
<form #ngForm="ngForm">
<section class="form-block">
<div class="form-group" style="padding-left: 135px;">
<label for="app" class="clr-col-md-3 form-group-label-override required">应用</label>
<div class="select form-control">
<wayne-select name="app" [(ngModel)]="selectedApp"
searchable
[placeholder]="'PLACEHOLDER.CHOOSE' | translate">
<wayne-option *ngFor="let app of apps" [value]="app">{{app.name}}</wayne-option>
</wayne-select>
</div>
</div>
<div class="form-group" style="padding-left: 135px;">
<label class="clr-col-md-3 form-group-label-override">模版</label>
<wayne-ace-editor-box></wayne-ace-editor-box>

</div>
</section>
</form>
<div class="modal-footer">

<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
<button type="button" class="btn btn-primary" [disabled]="!isValid"
(click)="onSubmit()">{{'BUTTON.SUBMIT' | translate}}</button>
</div>
</div>
</clr-modal>
Original file line number Diff line number Diff line change
@@ -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 { KubeResourceStatefulSet } from '../../../../shared/shared.const';
import { StatefulsetService } from '../../../../shared/client/v1/statefulset.service';
import { StatefulsetTplService } from '../../../../shared/client/v1/statefulsettpl.service';
import { Statefulset } from '../../../../shared/model/v1/statefulset';
import { StatefulsetTemplate } from '../../../../shared/model/v1/statefulsettpl';

@Component({
selector: 'kube-migration',
templateUrl: 'migration.component.html'
})
export class MigrationComponent extends MigrationResource implements OnInit {

constructor(private statefulsetService: StatefulsetService,
private statefulsetTplService: StatefulsetTplService,
public appService: AppService,
public kubernetesClient: KubernetesClient,
public aceEditorService: AceEditorService,
public messageHandlerService: MessageHandlerService) {
super(kubernetesClient, appService, aceEditorService, messageHandlerService);
super.registKubeResource(KubeResourceStatefulSet);
}

ngOnInit(): void {
super.ngOnInit();
}

onSubmit() {
if (this.isSubmitOnGoing) {
return;
}
this.isSubmitOnGoing = true;
const resource = new Statefulset();
resource.name = this.obj.metadata.name;
resource.appId = this.selectedApp.id;
this.statefulsetService.create(resource).subscribe(
resp => {
const data = resp.data;
const tpl = new StatefulsetTemplate();
tpl.name = this.obj.metadata.name;
tpl.statefulsetId = data.id;
tpl.template = JSON.stringify(this.obj);
tpl.description = 'migration from kubernetes. ';
this.statefulsetTplService.create(tpl, this.selectedApp.id).subscribe(
() => {
this.messageHandlerService.showSuccess('资源创建成功!请前往前台手动发布到相应机房!');
},
error => {
this.messageHandlerService.handleError(error);
});
},
error => {
this.messageHandlerService.handleError(error);
}
);
this.modalOpened = false;
}

}
7 changes: 7 additions & 0 deletions src/frontend/src/app/admin/sidenav/sidenav.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@
<clr-icon clrVerticalNavIcon shape="event"></clr-icon>
Deployment
</a>
<a clrVerticalNavLink
routerLink="kubernetes/statefulset"
[routerLinkActiveOptions]="{exact:true}"
routerLinkActive="active">
<clr-icon clrVerticalNavIcon shape="blocks-group"></clr-icon>
StatefulSet
</a>
<a clrVerticalNavLink
routerLink="kubernetes/pod"
[routerLinkActiveOptions]="{exact:true}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export class KubernetesNamespacedResource implements OnInit, OnDestroy {
}

createResource() {
this.aceEditorModal.openModal({}, 'ADMIN.KUBERNETES.POD.CREATE', true, true);
this.aceEditorModal.openModal({}, `创建 ${this.resourceType}`, true, true);
}


Expand Down
Loading

0 comments on commit 6d85b4a

Please sign in to comment.