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: [
PlacementPipe,
CreateClusterComponent,
CreateClusterReviewComponent,
- UpgradeComponent
+ UpgradeComponent,
+ UpgradeStartModalComponent
],
providers: [NgbActiveModal]
})
--- /dev/null
+<cd-modal [modalRef]="activeModal">
+ <ng-container class="modal-title">
+ <ng-container i18n>Upgrade Cluster</ng-container>
+ </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>
--- /dev/null
+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();
+ });
+});
--- /dev/null
+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();
+ }
+ });
+ }
+}
id="upgrade"
aria-label="Upgrade now"
[disabled]="(healthData.mgr_map | mgrSummary).total <= 1"
+ (click)="startUpgradeModal()"
i18n>Upgrade now</button>
</div>
</ng-container>
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();
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',
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,
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))
this.healthData$ = this.healthService.getMinimalHealth();
this.fsid$ = this.healthService.getClusterFsid();
}
+
+ startUpgradeModal() {
+ this.modalRef = this.modalService.show(UpgradeStartModalComponent, {
+ versions: this.upgradableVersions.sort()
+ });
+ }
}
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' });
+ });
});
upgradeInfo.versions = upgradableVersions;
return upgradeInfo;
}
+
+ start(version: string) {
+ return this.http.post(`${this.baseURL}/start`, { version: version });
+ }
}
EXPORT: string;
IMPORT: any;
MIGRATE: string;
+ START_UPGRADE: string;
constructor() {
/* Create a new item */
this.REMOVE_SCHEDULING = $localize`Remove Scheduling`;
this.PROMOTE = $localize`Promote`;
this.DEMOTE = $localize`Demote`;
+
+ this.START_UPGRADE = $localize`Start Upgrade`;
}
}