Merge pull request #52493 from rhcs-dashboard/cluster-upgrade-ui

mgr/dashboard: cluster upgrade start UI


Reviewed-by: Pegonzal <pegonzal@redhat.com>
Reviewed-by: cloudbehl <NOT@FOUND>
Reviewed-by: Nizamudeen A <nia@redhat.com>
This commit is contained in:
Pedro Gonzalez Gomez 2023-08-01 10:46:51 +02:00 committed by GitHub
commit 172133f18b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 182 additions and 4 deletions

View File

@ -58,6 +58,7 @@ import { ServiceFormComponent } from './services/service-form/service-form.compo
import { ServicesComponent } from './services/services.component';
import { TelemetryComponent } from './telemetry/telemetry.component';
import { UpgradeComponent } from './upgrade/upgrade.component';
import { UpgradeStartModalComponent } from './upgrade/upgrade-form/upgrade-start-modal.component';
@NgModule({
imports: [
@ -118,7 +119,8 @@ import { UpgradeComponent } from './upgrade/upgrade.component';
PlacementPipe,
CreateClusterComponent,
CreateClusterReviewComponent,
UpgradeComponent
UpgradeComponent,
UpgradeStartModalComponent
],
providers: [NgbActiveModal]
})

View File

@ -0,0 +1,49 @@
<cd-modal [modalRef]="activeModal">
<ng-container class="modal-title">
<ng-container i18n>Upgrade Cluster</ng-container>&nbsp;
</ng-container>
<ng-container class="modal-content">
<form name="upgradeForm"
class="form"
#formDir="ngForm"
[formGroup]="upgradeForm"
novalidate>
<div class="modal-body">
<div class="form-group row">
<label class="cd-col-form-label required"
for="availableVersions"
i18n>New Version</label>
<div class="cd-col-form-input">
<select id="availableVersions"
name="availableVersions"
class="form-select"
formControlName="availableVersions">
<option *ngIf="versions === null"
ngValue="null"
i18n>Loading...</option>
<option *ngIf="versions !== null && versions.length === 0"
[ngValue]="null"
i18n>-- No version available --</option>
<option *ngIf="versions !== null && versions.length > 0"
[ngValue]="null"
i18n>-- Select a version --</option>
<option *ngFor="let version of versions"
[value]="version">{{ version }}</option>
</select>
<span class="invalid-feedback"
*ngIf="upgradeForm.showError('availableVersions', formDir, 'required')"
i18n>This field is required!</span>
</div>
</div>
</div>
<div class="modal-footer">
<cd-form-button-panel *ngIf="versions"
(submitActionEvent)="startUpgrade()"
[form]="upgradeForm"
[submitText]="actionLabels.START_UPGRADE"></cd-form-button-panel>
</div>
</form>
</ng-container>
</cd-modal>

View File

@ -0,0 +1,30 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UpgradeComponent } from '../upgrade.component';
import { configureTestBed } from '~/testing/unit-test-helper';
import { UpgradeService } from '~/app/shared/api/upgrade.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { SharedModule } from '~/app/shared/shared.module';
describe('UpgradeComponent', () => {
let component: UpgradeComponent;
let fixture: ComponentFixture<UpgradeComponent>;
configureTestBed({
imports: [HttpClientTestingModule, SharedModule],
schemas: [NO_ERRORS_SCHEMA],
declarations: [UpgradeComponent],
providers: [UpgradeService]
});
beforeEach(() => {
fixture = TestBed.createComponent(UpgradeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,66 @@
import { Component, OnInit } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
import { Observable } from 'rxjs';
import { Icons } from '~/app/shared/enum/icons.enum';
import { Permission } from '~/app/shared/models/permissions';
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { UpgradeService } from '~/app/shared/api/upgrade.service';
import { UpgradeInfoInterface } from '~/app/shared/models/upgrade.interface';
import { NotificationType } from '~/app/shared/enum/notification-type.enum';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { NotificationService } from '~/app/shared/services/notification.service';
@Component({
selector: 'cd-upgrade-start-modal.component',
templateUrl: './upgrade-start-modal.component.html',
styleUrls: ['./upgrade-start-modal.component.scss']
})
export class UpgradeStartModalComponent implements OnInit {
permission: Permission;
upgradeInfoError$: Observable<any>;
upgradeInfo$: Observable<UpgradeInfoInterface>;
upgradeForm: CdFormGroup;
icons = Icons;
versions: string[];
constructor(
public actionLabels: ActionLabelsI18n,
private authStorageService: AuthStorageService,
public activeModal: NgbActiveModal,
private upgradeService: UpgradeService,
private notificationService: NotificationService
) {
this.permission = this.authStorageService.getPermissions().configOpt;
}
ngOnInit() {
this.upgradeForm = new CdFormGroup({
availableVersions: new FormControl(null, [Validators.required])
});
}
startUpgrade() {
this.upgradeService.start(this.upgradeForm.getValue('availableVersions')).subscribe({
next: () => {
this.notificationService.show(
NotificationType.success,
$localize`Started upgrading the cluster`
);
},
error: (error) => {
this.upgradeForm.setErrors({ cdSubmitButton: true });
this.notificationService.show(
NotificationType.error,
$localize`Failed to start the upgrade`,
error
);
},
complete: () => {
this.activeModal.close();
}
});
}
}

View File

@ -40,6 +40,7 @@
id="upgrade"
aria-label="Upgrade now"
[disabled]="(healthData.mgr_map | mgrSummary).total <= 1"
(click)="startUpgradeModal()"
i18n>Upgrade now</button>
</div>
</ng-container>

View File

@ -56,7 +56,7 @@ describe('UpgradeComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(UpgradeComponent);
component = fixture.componentInstance;
upgradeInfoSpy = spyOn(TestBed.inject(UpgradeService), 'list');
upgradeInfoSpy = spyOn(TestBed.inject(UpgradeService), 'list').and.callFake(() => of(null));
getHealthSpy = spyOn(TestBed.inject(HealthService), 'getMinimalHealth');
getHealthSpy.and.returnValue(of(healthPayload));
fixture.detectChanges();

View File

@ -1,13 +1,16 @@
import { Component, OnInit } from '@angular/core';
import { Observable, of } from 'rxjs';
import { catchError, ignoreElements } from 'rxjs/operators';
import { catchError, ignoreElements, tap } from 'rxjs/operators';
import { HealthService } from '~/app/shared/api/health.service';
import { UpgradeService } from '~/app/shared/api/upgrade.service';
import { Icons } from '~/app/shared/enum/icons.enum';
import { Permission } from '~/app/shared/models/permissions';
import { UpgradeInfoInterface } from '~/app/shared/models/upgrade.interface';
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { SummaryService } from '~/app/shared/services/summary.service';
import { ModalService } from '~/app/shared/services/modal.service';
import { UpgradeStartModalComponent } from './upgrade-form/upgrade-start-modal.component';
@Component({
selector: 'cd-upgrade',
@ -21,10 +24,13 @@ export class UpgradeComponent implements OnInit {
permission: Permission;
healthData$: Observable<any>;
fsid$: Observable<any>;
modalRef: NgbModalRef;
upgradableVersions: string[];
icons = Icons;
constructor(
private modalService: ModalService,
private summaryService: SummaryService,
private upgradeService: UpgradeService,
private authStorageService: AuthStorageService,
@ -38,7 +44,11 @@ export class UpgradeComponent implements OnInit {
const version = summary.version.replace('ceph version ', '').split('-');
this.version = version[0];
});
this.upgradeInfo$ = this.upgradeService.list();
this.upgradeInfo$ = this.upgradeService
.list()
.pipe(
tap((upgradeInfo: UpgradeInfoInterface) => (this.upgradableVersions = upgradeInfo.versions))
);
this.upgradeInfoError$ = this.upgradeInfo$?.pipe(
ignoreElements(),
catchError((error) => of(error))
@ -46,4 +56,10 @@ export class UpgradeComponent implements OnInit {
this.healthData$ = this.healthService.getMinimalHealth();
this.fsid$ = this.healthService.getClusterFsid();
}
startUpgradeModal() {
this.modalRef = this.modalService.show(UpgradeStartModalComponent, {
versions: this.upgradableVersions.sort()
});
}
}

View File

@ -57,4 +57,11 @@ describe('UpgradeService', () => {
expectedVersions
);
});
it('should start the upgrade', () => {
service.start('18.1.0').subscribe();
const req = httpTesting.expectOne('api/cluster/upgrade/start');
expect(req.request.method).toBe('POST');
expect(req.request.body).toEqual({ version: '18.1.0' });
});
});

View File

@ -41,4 +41,8 @@ export class UpgradeService extends ApiClient {
upgradeInfo.versions = upgradableVersions;
return upgradeInfo;
}
start(version: string) {
return this.http.post(`${this.baseURL}/start`, { version: version });
}
}

View File

@ -140,6 +140,7 @@ export class ActionLabelsI18n {
EXPORT: string;
IMPORT: any;
MIGRATE: string;
START_UPGRADE: string;
constructor() {
/* Create a new item */
@ -215,6 +216,8 @@ export class ActionLabelsI18n {
this.REMOVE_SCHEDULING = $localize`Remove Scheduling`;
this.PROMOTE = $localize`Promote`;
this.DEMOTE = $localize`Demote`;
this.START_UPGRADE = $localize`Start Upgrade`;
}
}