import { ActivatedRouteStub } from '../../../../testing/activated-route-stub';
import { configureTestBed } from '../../../../testing/unit-test-helper';
+import { PoolService } from '../../../shared/api/pool.service';
import { RbdService } from '../../../shared/api/rbd.service';
import { ImageSpec } from '../../../shared/models/image-spec';
import { SharedModule } from '../../../shared/shared.module';
+import { Pool } from '../../pool/pool';
import { RbdConfigurationFormComponent } from '../rbd-configuration-form/rbd-configuration-form.component';
import { RbdImageFeature } from './rbd-feature.interface';
import { RbdFormMode } from './rbd-form-mode.enum';
+import { RbdFormResponseModel } from './rbd-form-response.model';
import { RbdFormComponent } from './rbd-form.component';
describe('RbdFormComponent', () => {
+ const urlPrefix = {
+ create: '/block/rbd/create',
+ edit: '/block/rbd/edit',
+ clone: '/block/rbd/clone',
+ copy: '/block/rbd/copy'
+ };
let component: RbdFormComponent;
let fixture: ComponentFixture<RbdFormComponent>;
let activatedRoute: ActivatedRouteStub;
+ const mock: { rbd: RbdFormResponseModel; pools: Pool[]; defaultFeatures: string[] } = {
+ rbd: {} as RbdFormResponseModel,
+ pools: [],
+ defaultFeatures: []
+ };
+
+ const setRouterUrl = (
+ action: 'create' | 'edit' | 'clone' | 'copy',
+ poolName?: string,
+ imageName?: string
+ ) => {
+ component['routerUrl'] = [urlPrefix[action], poolName, imageName].filter((x) => x).join('/');
+ };
const queryNativeElement = (cssSelector: string) =>
fixture.debugElement.query(By.css(cssSelector)).nativeElement;
const DELAY = 100;
+ const getPool = (
+ pool_name: string,
+ type: 'replicated' | 'erasure',
+ flags_names: string,
+ application_metadata: string[]
+ ): Pool =>
+ ({
+ pool_name,
+ flags_names,
+ application_metadata,
+ type
+ } as Pool);
+
beforeEach(() => {
createAction = spyOn(component, 'createAction').and.returnValue(of(null));
editAction = spyOn(component, 'editAction');
copyAction = spyOn(component, 'copyAction').and.returnValue(of(null));
spyOn(component, 'setResponse').and.stub();
routerNavigate = spyOn(TestBed.inject(Router), 'navigate').and.stub();
+ mock.pools = [
+ getPool('one', 'replicated', '', []),
+ getPool('two', 'replicated', '', ['rbd']),
+ getPool('three', 'replicated', '', ['rbd']),
+ getPool('four', 'erasure', '', ['rbd']),
+ getPool('four', 'erasure', 'ec_overwrites', ['rbd'])
+ ];
+ spyOn(TestBed.inject(PoolService), 'list').and.callFake(() => of(mock.pools));
rbdServiceGetSpy = spyOn(TestBed.inject(RbdService), 'get');
- rbdServiceGetSpy.and.returnValue(of({ pool_name: 'foo', pool_image: 'bar' }));
+ mock.rbd = ({ pool_name: 'foo', pool_image: 'bar' } as any) as RbdFormResponseModel;
+ rbdServiceGetSpy.and.returnValue(of(mock.rbd));
component.mode = undefined;
});
});
it('should unsubscribe right after image data is received', () => {
- component.mode = RbdFormMode.editing;
- rbdServiceGetSpy.and.returnValue(of({ pool_name: 'foo', pool_image: 'bar' }));
+ setRouterUrl('edit', 'foo', 'bar');
+ rbdServiceGetSpy.and.returnValue(of(mock.rbd));
editAction.and.returnValue(NEVER);
- component.ngOnInit();
- component.submit();
-
expect(component['rbdImage'].observers.length).toEqual(0);
+ component.ngOnInit(); // Subscribes to image once during init
+ component.submit();
+ expect(component['rbdImage'].observers.length).toEqual(1);
expect(createAction).toHaveBeenCalledTimes(0);
expect(editAction).toHaveBeenCalledTimes(1);
expect(cloneAction).toHaveBeenCalledTimes(0);
});
it('should not edit image if no image data is received', fakeAsync(() => {
- component.mode = RbdFormMode.editing;
- rbdServiceGetSpy.and.returnValue(
- of({ pool_name: 'foo', pool_image: 'bar' }).pipe(delay(DELAY))
- );
+ setRouterUrl('edit', 'foo', 'bar');
+ rbdServiceGetSpy.and.returnValue(of(mock.rbd).pipe(delay(DELAY)));
component.ngOnInit();
component.submit();
});
it('should edit image after image data is received', () => {
- component.mode = RbdFormMode.editing;
+ setRouterUrl('edit', 'foo', 'bar');
component.ngOnInit();
component.submit();
});
it('should not clone image if no image data is received', fakeAsync(() => {
- component.mode = RbdFormMode.cloning;
- rbdServiceGetSpy.and.returnValue(
- of({ pool_name: 'foo', pool_image: 'bar' }).pipe(delay(DELAY))
- );
+ setRouterUrl('clone', 'foo', 'bar');
+ rbdServiceGetSpy.and.returnValue(of(mock.rbd).pipe(delay(DELAY)));
component.ngOnInit();
component.submit();
}));
it('should clone image after image data is received', () => {
- component.mode = RbdFormMode.cloning;
+ setRouterUrl('clone', 'foo', 'bar');
component.ngOnInit();
component.submit();
});
it('should not copy image if no image data is received', fakeAsync(() => {
- component.mode = RbdFormMode.copying;
- rbdServiceGetSpy.and.returnValue(
- of({ pool_name: 'foo', pool_image: 'bar' }).pipe(delay(DELAY))
- );
+ setRouterUrl('copy', 'foo', 'bar');
+ rbdServiceGetSpy.and.returnValue(of(mock.rbd).pipe(delay(DELAY)));
component.ngOnInit();
component.submit();
}));
it('should copy image after image data is received', () => {
- component.mode = RbdFormMode.copying;
+ setRouterUrl('copy', 'foo', 'bar');
component.ngOnInit();
component.submit();
})
);
spyOn(rbdService, 'defaultFeatures').and.returnValue(of(defaultFeatures));
- component.router = { url: `/block/rbd/edit/${pool}/${image}` } as Router;
+ setRouterUrl('edit', pool, image);
fixture.detectChanges();
[
deepFlatten,
beforeEach(() => {
const rbdService = TestBed.inject(RbdService);
spyOn(rbdService, 'defaultFeatures').and.returnValue(of(defaultFeatures));
- component.router = { url: '/block/rbd/create' } as Router;
+ setRouterUrl('create');
fixture.detectChanges();
[
deepFlatten,
action: string;
resource: string;
private rbdImage = new ReplaySubject(1);
+ private routerUrl: string;
icons = Icons;
private taskWrapper: TaskWrapperService,
private dimlessBinaryPipe: DimlessBinaryPipe,
public actionLabels: ActionLabelsI18n,
- public router: Router
+ private router: Router
) {
super();
+ this.routerUrl = this.router.url;
this.poolPermission = this.authStorageService.getPermissions().pool;
this.resource = $localize`RBD`;
this.features = {
}
private prepareFormForAction() {
- const url = this.router.url;
+ const url = this.routerUrl;
if (url.startsWith('/block/rbd/edit')) {
this.mode = this.rbdFormMode.editing;
this.action = this.actionLabels.EDIT;
}
onPoolChange(selectedPoolName: string) {
- const newDataPools = this.allDataPools
+ const dataPoolControl = this.rbdForm.get('dataPool');
+ if (dataPoolControl.value === selectedPoolName) {
+ dataPoolControl.setValue(null);
+ }
+ this.dataPools = this.allDataPools
? this.allDataPools.filter((dataPool: any) => {
return dataPool.pool_name !== selectedPoolName;
})
: [];
- if (this.rbdForm.getValue('dataPool') === selectedPoolName) {
- this.rbdForm.get('dataPool').setValue(null);
- }
- this.dataPools = newDataPools;
this.namespaces = null;
if (selectedPoolName in this.namespacesByPoolCache) {
this.namespaces = this.namespacesByPoolCache[selectedPoolName];
};
}
- protected getDependendChildFeatures(featureKey: string) {
- return _.filter(this.features, (f) => f.requires === featureKey) || [];
- }
-
deepBoxCheck(key: string, checked: boolean) {
- const childFeatures = this.getDependendChildFeatures(key);
+ const childFeatures = this.getDependentChildFeatures(key);
childFeatures.forEach((feature) => {
const featureControl = this.rbdForm.get(feature.key);
if (checked) {
});
}
+ protected getDependentChildFeatures(featureKey: string) {
+ return _.filter(this.features, (f) => f.requires === featureKey) || [];
+ }
+
interlockCheck(key: string, checked: boolean) {
// Adds a compatibility layer for Ceph cluster where the feature interlock of features hasn't
// been implemented yet. It disables the feature interlock for images which only have one of
.get('stripingUnit')
.setValue(this.dimlessBinaryPipe.transform(response.stripe_unit));
this.rbdForm.get('stripingCount').setValue(response.stripe_count);
-
/* Configuration */
this.initializeConfigData.emit({
initialData: this.response.configuration,
request.namespace = this.rbdForm.getValue('namespace');
request.name = this.rbdForm.getValue('name');
request.size = this.formatter.toBytes(this.rbdForm.getValue('size'));
+ this.addObjectSizeAndStripingToRequest(request);
+ request.configuration = this.getDirtyConfigurationValues();
+ return request;
+ }
+
+ private addObjectSizeAndStripingToRequest(
+ request: RbdFormCreateRequestModel | RbdFormCloneRequestModel | RbdFormCopyRequestModel
+ ) {
request.obj_size = this.formatter.toBytes(this.rbdForm.getValue('obj_size'));
_.forIn(this.features, (feature) => {
if (this.rbdForm.getValue(feature.key)) {
request.stripe_unit = this.formatter.toBytes(this.rbdForm.getValue('stripingUnit'));
request.stripe_count = this.rbdForm.getValue('stripingCount');
request.data_pool = this.rbdForm.getValue('dataPool');
-
- /* Configuration */
- request.configuration = this.getDirtyConfigurationValues();
-
- return request;
}
createAction(): Observable<any> {
request.features.push(feature.key);
}
});
-
request.configuration = this.getDirtyConfigurationValues();
-
return request;
}
request.child_pool_name = this.rbdForm.getValue('pool');
request.child_namespace = this.rbdForm.getValue('namespace');
request.child_image_name = this.rbdForm.getValue('name');
- request.obj_size = this.formatter.toBytes(this.rbdForm.getValue('obj_size'));
- _.forIn(this.features, (feature) => {
- if (this.rbdForm.getValue(feature.key)) {
- request.features.push(feature.key);
- }
- });
-
- /* Striping */
- request.stripe_unit = this.formatter.toBytes(this.rbdForm.getValue('stripingUnit'));
- request.stripe_count = this.rbdForm.getValue('stripingCount');
- request.data_pool = this.rbdForm.getValue('dataPool');
-
- /* Configuration */
+ this.addObjectSizeAndStripingToRequest(request);
request.configuration = this.getDirtyConfigurationValues(
true,
RbdConfigurationSourceField.image
);
-
return request;
}
request.dest_pool_name = this.rbdForm.getValue('pool');
request.dest_namespace = this.rbdForm.getValue('namespace');
request.dest_image_name = this.rbdForm.getValue('name');
- request.obj_size = this.formatter.toBytes(this.rbdForm.getValue('obj_size'));
- _.forIn(this.features, (feature) => {
- if (this.rbdForm.getValue(feature.key)) {
- request.features.push(feature.key);
- }
- });
-
- /* Striping */
- request.stripe_unit = this.formatter.toBytes(this.rbdForm.getValue('stripingUnit'));
- request.stripe_count = this.rbdForm.getValue('stripingCount');
- request.data_pool = this.rbdForm.getValue('dataPool');
-
- /* Configuration */
+ this.addObjectSizeAndStripingToRequest(request);
request.configuration = this.getDirtyConfigurationValues(
true,
RbdConfigurationSourceField.image
);
-
return request;
}