From: Naman Munet Date: Thu, 10 Apr 2025 11:40:02 +0000 (+0530) Subject: mgr/dashboard: fix bucket rate limit API on owner change X-Git-Tag: v20.3.0~94^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=36a313ff95e0e9ccc6c5c747dce16cf038cfac1c;p=ceph.git mgr/dashboard: fix bucket rate limit API on owner change Fixes: https://tracker.ceph.com/issues/70874 PR covers & fixes below scenarios: Whenever we change the owner of bucket from non-tenanted to tenanted and vice-versa with the rate-limit changes, there was issue in sending bucket name Scenario 1: Changing the bucket owner from tenanted to non-tenanted Scenario 2: Changing the bucket owner from non-tenanted to tenanted Scenario 3: Keeping the owner(tenanted) same and changing only rate limit Signed-off-by: Naman Munet --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.spec.ts index 0de5bbdef563..f72573e12a43 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.spec.ts @@ -21,6 +21,8 @@ import { RgwBucketFormComponent } from './rgw-bucket-form.component'; import { RgwRateLimitComponent } from '../rgw-rate-limit/rgw-rate-limit.component'; import { CheckboxModule, SelectModule } from 'carbon-components-angular'; import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { LoadingStatus } from '~/app/shared/forms/cd-form'; describe('RgwBucketFormComponent', () => { let component: RgwBucketFormComponent; @@ -30,6 +32,7 @@ describe('RgwBucketFormComponent', () => { let rgwBucketServiceGetSpy: jasmine.Spy; let enumerateSpy: jasmine.Spy; let formHelper: FormHelper; + let childComponent: RgwRateLimitComponent; configureTestBed({ declarations: [RgwBucketFormComponent, RgwRateLimitComponent], @@ -338,4 +341,90 @@ describe('RgwBucketFormComponent', () => { component.deleteTag(0); expect(updateValidationSpy).toHaveBeenCalled(); }); + + describe('should call bucket ratelimit API with correct bucket name', () => { + beforeEach(() => { + component.loading = LoadingStatus.Ready; + fixture.detectChanges(); + childComponent = fixture.debugElement.query(By.directive(RgwRateLimitComponent)) + .componentInstance; + }); + it('Scenario 1: tenanted owner with tenanted bucket name', () => { + const rateLimitSpy = spyOn(rgwBucketService, 'updateBucketRateLimit').and.returnValue( + observableOf([]) + ); + component.editing = true; + formHelper.setMultipleValues({ + bid: 'tenant/bucket1', + owner: 'tenant$user1' + }); + childComponent.form.patchValue({ + rate_limit_enabled: true, + rate_limit_max_readOps: 100, + rate_limit_max_writeOps: 200, + rate_limit_max_readBytes: '10MB', + rate_limit_max_writeBytes: '20MB', + rate_limit_max_readOps_unlimited: true, // Unlimited + rate_limit_max_writeOps_unlimited: true, // Unlimited + rate_limit_max_readBytes_unlimited: true, // Unlimited + rate_limit_max_writeBytes_unlimited: true // Unlimited + }); + childComponent.form.get('rate_limit_enabled').markAsDirty(); + const rateLimitConfig = childComponent.getRateLimitFormValue(); + component.updateBucketRateLimit(); + expect(rateLimitSpy).toHaveBeenCalledWith('tenant/bucket1', rateLimitConfig); + }); + + it('Scenario 2: non tenanted owner with tenanted bucket name', () => { + const rateLimitSpy = spyOn(rgwBucketService, 'updateBucketRateLimit').and.returnValue( + observableOf([]) + ); + component.editing = true; + formHelper.setMultipleValues({ + bid: 'tenant/bucket1', + owner: 'non_tenanted_user' + }); + childComponent.form.patchValue({ + rate_limit_enabled: true, + rate_limit_max_readOps: 100, + rate_limit_max_writeOps: 200, + rate_limit_max_readBytes: '10MB', + rate_limit_max_writeBytes: '20MB', + rate_limit_max_readOps_unlimited: true, // Unlimited + rate_limit_max_writeOps_unlimited: true, // Unlimited + rate_limit_max_readBytes_unlimited: true, // Unlimited + rate_limit_max_writeBytes_unlimited: true // Unlimited + }); + childComponent.form.get('rate_limit_enabled').markAsDirty(); + const rateLimitConfig = childComponent.getRateLimitFormValue(); + component.updateBucketRateLimit(); + expect(rateLimitSpy).toHaveBeenCalledWith('bucket1', rateLimitConfig); + }); + + it('Scenario 3: tenanted owner and with non-tenanted bucket name', () => { + const rateLimitSpy = spyOn(rgwBucketService, 'updateBucketRateLimit').and.returnValue( + observableOf([]) + ); + component.editing = true; + formHelper.setMultipleValues({ + bid: 'bucket1', + owner: 'tenant$user1' + }); + childComponent.form.patchValue({ + rate_limit_enabled: true, + rate_limit_max_readOps: 100, + rate_limit_max_writeOps: 200, + rate_limit_max_readBytes: '10MB', + rate_limit_max_writeBytes: '20MB', + rate_limit_max_readOps_unlimited: true, // Unlimited + rate_limit_max_writeOps_unlimited: true, // Unlimited + rate_limit_max_readBytes_unlimited: true, // Unlimited + rate_limit_max_writeBytes_unlimited: true // Unlimited + }); + childComponent.form.get('rate_limit_enabled').markAsDirty(); + const rateLimitConfig = childComponent.getRateLimitFormValue(); + component.updateBucketRateLimit(); + expect(rateLimitSpy).toHaveBeenCalledWith('tenant/bucket1', rateLimitConfig); + }); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts index c9c6954d2302..468718214432 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts @@ -412,17 +412,42 @@ export class RgwBucketFormComponent extends CdForm implements OnInit, AfterViewC } updateBucketRateLimit() { + /** + * Whenever we change the owner of bucket from non-tenanted to tenanted + * and vice-versa with the rate-limit changes there was issue in sending + * bucket name, hence the below logic caters to it. + * + * Scenario 1: Changing the bucket owner from tenanted to non-tenanted + * Scenario 2: Changing the bucket owner from non-tenanted to tenanted + * Scenario 3: Keeping the owner(tenanted) same and changing only rate limit + */ + const owner = this.bucketForm.getValue('owner'); + const bidInput = this.bucketForm.getValue('bid'); + + let bid: string; + + const hasOwnerWithDollar = owner.includes('$'); + const bidHasSlash = bidInput.includes('/'); + + if (bidHasSlash && hasOwnerWithDollar) { + bid = bidInput; + } else if (hasOwnerWithDollar) { + const ownerPrefix = owner.split('$')[0]; + bid = `${ownerPrefix}/${bidInput}`; + } else if (bidHasSlash) { + bid = bidInput.split('/')[1]; + } else { + bid = bidInput; + } // Check if bucket ratelimit has been modified. const rateLimitConfig: RgwRateLimitConfig = this.rateLimitComponent.getRateLimitFormValue(); if (!!rateLimitConfig) { - this.rgwBucketService - .updateBucketRateLimit(this.bucketForm.getValue('bid'), rateLimitConfig) - .subscribe( - () => {}, - (error: any) => { - this.notificationService.show(NotificationType.error, error); - } - ); + this.rgwBucketService.updateBucketRateLimit(bid, rateLimitConfig).subscribe( + () => {}, + (error: any) => { + this.notificationService.show(NotificationType.error, error); + } + ); } }