Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #259 from wilhelmguo/feature/add_kubernetes_secret
Add kubernetes secret resource
  • Loading branch information
wilhelmguo committed Feb 12, 2019
2 parents 6a4228a + 57f8b2b commit 8beded3
Show file tree
Hide file tree
Showing 13 changed files with 340 additions and 5 deletions.
9 changes: 7 additions & 2 deletions src/backend/resources/common/common.go
Expand Up @@ -4,13 +4,18 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// TODO convert runtime.Object to real type
// for common kubernetes Object
type Object struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// for endpoint only
Subsets interface{} `json:"subsets,omitempty"`
Spec interface{} `json:"spec,omitempty"`
Status interface{} `json:"status,omitempty"`
// for secret and configmap
Type interface{} `json:"type,omitempty"`
Data interface{} `json:"data,omitempty"`

Spec interface{} `json:"spec,omitempty"`
Status interface{} `json:"status,omitempty"`
}
3 changes: 3 additions & 0 deletions src/frontend/src/app/admin/admin-routing.module.ts
Expand Up @@ -72,6 +72,7 @@ import { KubePodComponent } from './kubernetes/pod/kube-pod.component';
import { KubeServiceComponent } from './kubernetes/service/kube-service.component';
import { KubeEndpointComponent } from './kubernetes/endpoint/kube-endpoint.component';
import { KubeConfigmapComponent } from './kubernetes/configmap/kube-configmap.component';
import { KubeSecretComponent } from './kubernetes/secret/kube-secret.component';


const routes: Routes = [
Expand Down Expand Up @@ -160,6 +161,8 @@ const routes: Routes = [
{path: 'kubernetes/endpoint/:cluster', component: KubeEndpointComponent},
{path: 'kubernetes/configmap', component: KubeConfigmapComponent},
{path: 'kubernetes/configmap/:cluster', component: KubeConfigmapComponent},
{path: 'kubernetes/secret', component: KubeSecretComponent},
{path: 'kubernetes/secret/:cluster', component: KubeSecretComponent},
...ADMINROUTES
]
}
Expand Down
4 changes: 3 additions & 1 deletion src/frontend/src/app/admin/admin.module.ts
Expand Up @@ -52,6 +52,7 @@ import { KubePodModule } from './kubernetes/pod/kube-pod.module';
import { KubeServiceModule } from './kubernetes/service/kube-service.module';
import { KubeEndpointModule } from './kubernetes/endpoint/kube-endpoint.module';
import { KubeConfigmapModule } from './kubernetes/configmap/kube-configmap.module';
import { KubeSecretModule } from './kubernetes/secret/kube-secret.module';

@NgModule({
imports: [
Expand Down Expand Up @@ -96,7 +97,8 @@ import { KubeConfigmapModule } from './kubernetes/configmap/kube-configmap.modul
KubePodModule,
KubeServiceModule,
KubeEndpointModule,
KubeConfigmapModule
KubeConfigmapModule,
KubeSecretModule
],
providers: [
AdminAuthCheckGuard,
Expand Down
Expand Up @@ -22,8 +22,8 @@
<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> {{ obj.metadata.name }} </clr-dg-cell>
<clr-dg-cell class="col-version">
<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>
Expand Down
@@ -0,0 +1,48 @@
<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.SECRET.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.SECRET.LIST.NAME' | translate}}</wayne-checkbox>
<wayne-checkbox value="type">{{'ADMIN.KUBERNETES.SECRET.LIST.TYPE' | translate}}</wayne-checkbox>
<wayne-checkbox value="label">{{'ADMIN.KUBERNETES.SECRET.LIST.LABEL' | translate}}</wayne-checkbox>
<wayne-checkbox value="age">{{'ADMIN.KUBERNETES.SECRET.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-secret
[resources]="resources"
[showState]="showState"
(delete)="onDeleteResourceEvent($event)"
(edit)="onEditResourceEvent($event)"
[page]="pageState.page"
[cluster]="cluster"
(migration)="migration($event)"
(paginate)="retrieveResource($event)">
</wayne-list-secret>
</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>
@@ -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 { KubeResourceSecret } 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 { ListSecretComponent } from './list-secret/list-secret.component';

const showState = {
'name': {hidden: false},
'type': {hidden: false},
'label': {hidden: false},
'clusterIP': {hidden: false},
'port': {hidden: false},
'externalIP': {hidden: false},
'age': {hidden: false},
};

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

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

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

ngOnInit() {
super.ngOnInit();
}

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


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

}
27 changes: 27 additions & 0 deletions src/frontend/src/app/admin/kubernetes/secret/kube-secret.module.ts
@@ -0,0 +1,27 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../../../shared/shared.module';
import { ReactiveFormsModule } from '@angular/forms';
import { KubeSecretComponent } from './kube-secret.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 { ListSecretComponent } from './list-secret/list-secret.component';

@NgModule({
imports: [
SharedModule,
ReactiveFormsModule,
DeletionDialogModule
],
providers: [
KubernetesClient
],
declarations: [
KubeSecretComponent,
ListSecretComponent,
MigrationComponent
]
})

export class KubeSecretModule {
}
@@ -0,0 +1,47 @@
<clr-datagrid (clrDgRefresh)="refresh($event)">
<clr-dg-column class="col-app-name" [clrDgField]="'name'">
<ng-container *clrDgHideableColumn="showState['name']">
{{'ADMIN.KUBERNETES.SECRET.LIST.NAME' | translate}}
</ng-container>
</clr-dg-column>
<clr-dg-column class="col-version">
<ng-container *clrDgHideableColumn="showState['label']">
{{'ADMIN.KUBERNETES.SECRET.LIST.LABEL' | translate}}
</ng-container>
</clr-dg-column>
<clr-dg-column>
<ng-container *clrDgHideableColumn="showState['type']">
{{'ADMIN.KUBERNETES.SECRET.LIST.TYPE' | translate}}
</ng-container>
</clr-dg-column>
<clr-dg-column [clrDgField]="'creationTimestamp'">
<ng-container *clrDgHideableColumn="showState['age']">
{{'ADMIN.KUBERNETES.SECRET.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 class="col-version">
<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> {{ obj.type }}</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>
@@ -0,0 +1,24 @@
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-secret',
templateUrl: './list-secret.component.html'
})

export class ListSecretComponent 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);
}
}
@@ -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>
@@ -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 { KubeResourceSecret } from '../../../../shared/shared.const';
import { SecretService } from '../../../../shared/client/v1/secret.service';
import { SecretTplService } from '../../../../shared/client/v1/secrettpl.service';
import { Secret } from '../../../../shared/model/v1/secret';
import { SecretTpl } from '../../../../shared/model/v1/secrettpl';

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

constructor(private secretService: SecretService,
private secretTplService: SecretTplService,
public appService: AppService,
public kubernetesClient: KubernetesClient,
public aceEditorService: AceEditorService,
public messageHandlerService: MessageHandlerService) {
super(kubernetesClient, appService, aceEditorService, messageHandlerService);
super.registKubeResource(KubeResourceSecret);
}

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

onSubmit() {
if (this.isSubmitOnGoing) {
return;
}
this.isSubmitOnGoing = true;
const resource = new Secret();
resource.name = this.obj.metadata.name;
resource.appId = this.selectedApp.id;
this.secretService.create(resource).subscribe(
resp => {
const data = resp.data;
const tpl = new SecretTpl();
tpl.name = this.obj.metadata.name;
tpl.secretId = data.id;
tpl.template = JSON.stringify(this.obj);
tpl.description = 'migration from kubernetes. ';
this.secretTplService.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
Expand Up @@ -72,6 +72,13 @@
<clr-icon clrVerticalNavIcon shape="file-settings"></clr-icon>
ConfigMap
</a>
<a clrVerticalNavLink
routerLink="kubernetes/secret"
[routerLinkActiveOptions]="{exact:true}"
routerLinkActive="active">
<clr-icon clrVerticalNavIcon shape="certificate"></clr-icon>
Secret
</a>
<a clrVerticalNavLink
routerLink="kubernetes/persistentvolume"
[routerLinkActiveOptions]="{exact:true}"
Expand Down

0 comments on commit 8beded3

Please sign in to comment.