Skip to content

Commit c87fa70

Browse files
authored
feat(aurora-portal): implement create and delete opetations bucket (#806)
* feat(ceph): extend backend to return full container metadata Align Ceph container data structure with Swift ContainerSummary: - Add count, bytes, last_modified fields to containerSchema - Update list procedure to fetch metadata via ListObjectsV2Command - Add deleteAll procedure for emptying buckets - Remove getDetails procedure (replaced by enhanced list) This enables UI parity between Swift and Ceph containers view. * feat(ceph): add Swift-like UI with ListToolbar, virtualization, and bulk operations Frontend refactor to achieve UI parity with Swift containers: - Add ListToolbar with sorting (name, count, bytes, last_modified) and search - Implement URL-persisted state for sort/search (survives navigation) - Create ContainerTableView with @tanstack/react-virtual for performance - Add checkbox selection for bulk operations - Implement EmptyBucketModal and EmptyBucketsModal - Update toast notifications to Swift standard (title/description structure) - Support "Empty All" bulk operation with progress tracking The UI is now fully consistent with Swift containers view. * refactor(ceph): remove Ceph-specific routes, use shared $provider route Remove duplicate routes at /storage/ceph/* to consolidate all storage providers under the shared /storage/$provider/* route structure. This ensures Ceph uses the same routing as Swift with proper provider switching support. * refactor(ceph): improve region resolution and add JSDoc comments - Auto-resolve Ceph region from OpenStack service catalog - Add special case for qa-de-1 region (uses ec prefix for historical reasons) - Remove CEPH_REGION environment variable (no longer needed) - Remove debug console.log statements - Add comprehensive JSDoc comments to resolveS3Config and bucket operations - Extract magic strings to constants for better maintainability * refactor: code styles * refactor: ceph doc * refactor: ceph doc * test(ceph): add comprehensive unit test coverage for Ceph storage Add unit tests for previously untested Ceph storage files: - cephProcedure.ts: S3 config resolution, middleware, and procedures (15 tests) - s3Client.ts: Client factory with validation (16 tests) - s3ErrorMapper.ts: Error mapping from AWS SDK to TRPC (27 tests) Fix existing router tests by adding catalog structure to mock context: - containerRouter.test.ts: Add catalog to token data, update expected results - objectRouter.test.ts: Add catalog to token data All tests follow existing patterns from router tests using Vitest. Total: 58 new tests, all passing (4149/4153 tests pass). * refactor(test): replace 'any' with proper types in Ceph tests Replace all 'any' type assertions with proper TypeScript types: - s3Client.test.ts: Use 'as unknown as string' for null/undefined tests - cephProcedure.test.ts: Create MockS3Client type for S3Client mocks - s3ErrorMapper.test.ts: Use ReturnType<typeof vi.spyOn> for spy type All tests still passing (58/58). No eslint-disable comments needed. * test(ceph): add frontend tests for container toast notifications and credential prompt Add comprehensive test coverage for Ceph frontend components: - ContainerToastNotifications.test.tsx (97 tests): - getBucketCreatedToast - getBucketCreateErrorToast - getBucketEmptiedToast (plural, singular, zero cases) - getBucketEmptyErrorToast - getBucketDeletedToast - getBucketDeleteErrorToast - getBucketsEmptyCompleteToast (success/warning variants) - Toast configuration validation - CredentialPrompt.test.tsx (16 tests): - Rendering setup prompt with EC2 credential explanation - Button interaction with TRPC mutation - Success handling with callback and query invalidation - Error handling with toast display - Layout and styling validation All tests follow existing patterns from Swift component tests. Total Ceph test coverage: 187 tests (58 backend + 129 frontend). * test(ceph): add modal tests for CreateBucket and DeleteBucket Add comprehensive test coverage for Ceph bucket modals: - CreateBucketModal.test.tsx (48 tests): - UI elements rendering - Valid bucket name validation (lowercase, numbers, periods, hyphens) - Length validation (3-63 characters) - Character validation (no uppercase, special chars, invalid start/end) - IP address format rejection - Reserved prefix/suffix validation (xn--, sthree-, amzn-s3-demo-, -s3alias, --ol-s3, etc) - Real-time validation feedback - Bucket creation with TRPC mutation - Success/error handling with callbacks - DeleteBucketModal.test.tsx (35 tests): - Visibility logic (isOpen, bucket null checks) - UI elements for empty/non-empty buckets - Loading state with bucket contents check - Non-empty bucket error message (plural/singular forms) - Objects query error handling - Bucket name display and copy functionality - Confirmation name validation - Bucket deletion with TRPC mutation - Success/error handling with query invalidation Total Ceph test coverage: 212 tests (58 backend + 154 frontend). * test(ceph): add modal tests for EmptyBucket and EmptyBuckets Add comprehensive test coverage for bucket emptying modals: - EmptyBucketModal.test.tsx (33 tests): - Visibility logic (isOpen, bucket null checks) - Empty bucket state (info message, Got it! button) - Non-empty bucket UI with warning and object count (plural/singular) - Bucket name display and copy functionality - Confirmation name validation - Bucket emptying mutation with TRPC - Success/error handling with callbacks - Modal close behavior - EmptyBucketsModal.test.tsx (23 tests): - Visibility with empty buckets array - UI elements with bucket count (plural/singular) - Bucket list display (max 20 visible, hidden count) - Object count for each bucket (plural/singular) - Batch emptying process with sequential mutations - Success handling with emptiedCount and totalDeleted - Error handling (partial failures, all failures) - Query invalidation logic (only when some succeed) - Modal close and state reset Total Ceph test coverage: 268 tests (58 backend + 210 frontend). * test(ceph): add ContainerTableView component tests Add comprehensive test coverage for ContainerTableView component: - Empty state rendering - Table structure with headers (Bucket Name, Object Count, Last Modified, Total Size) - Footer with bucket count (singular/plural forms) - Selection functionality (select all checkbox, individual container checkboxes) - Row navigation (click and Enter key) - Action menu presence (PopupMenu for each container) - Modal visibility (CreateBucketModal, EmptyBucketModal, DeleteBucketModal) - Date formatting Total: 24 tests * test(ceph): add ContainerListView component tests Add comprehensive test coverage for ContainerListView component: - Loading state with spinner - Error handling (NO_CEPH_CREDENTIALS, other errors) - CredentialPrompt integration - Empty state rendering - Container list rendering (names, dates, delete buttons) - Create Bucket button and modal - Delete Bucket button and modal - Table structure Total: 21 tests * fix(ceph): correct last_modified comparator for stable sorting Fix comparator function to return 0 when both values are missing, preventing unstable/inconsistent sorting behavior. Previous logic: - Returned 1 when both last_modified were missing - Violated comparator contract (equal items should return 0) New logic: - Returns 0 when both are missing (equal) - Returns 1 when only first is missing (first goes last) - Returns -1 when only second is missing (first goes first) - Compares timestamps when both present This ensures stable sort order where items without last_modified consistently appear at the end of the sorted list. * fix(ceph): validate object keys before deletion in deleteAll Add validation to ensure all objects have valid Key field before attempting batch deletion. Previously, objects with undefined keys would be included in delete requests, causing S3 API errors. Changes: - Filter and validate object keys before deletion - Throw descriptive error if objects without keys are encountered - Fail fast with clear error message instead of sending invalid requests Tests added (5 new tests): - Delete all objects and return count - Handle paginated deletion with multiple batches - Return 0 when bucket is empty - Throw error when objects have undefined keys ✨ NEW - Throw NOT_FOUND when bucket does not exist This prevents invalid delete requests and provides better error messages when S3 API returns unexpected data. * perf(ceph): add optional metadata fetching with concurrency control Add includeMetadata parameter to containers.list endpoint to address performance issues when listing many buckets. Previously, every bucket triggered a ListObjectsV2 request in parallel, causing thundering herd problems and potential rate limit issues. Changes: - Add includeMetadata boolean parameter (default: false) to listContainersInputSchema - Fast path (includeMetadata=false): Returns basic bucket info only (name, creationDate) - Slow path (includeMetadata=true): Fetches full metadata with CONCURRENCY_LIMIT=5 - Batched concurrent requests prevent overwhelming S3 API Performance impact: - Without metadata: ~1 ListBuckets request (fast) - With metadata: 1 ListBuckets + N ListObjectsV2 requests (batched in groups of 5) This allows frontends to choose between fast listing (for initial load) and detailed metadata (when user explicitly requests it or for table views). Addresses Copilot feedback about thundering herd and rate limits. * feat(ceph): use includeMetadata flag in frontend components Update frontend components to use new includeMetadata parameter: - ContainerTableView (index.tsx): Set includeMetadata=true for full table with sorting by object count, size, and last modified date - ContainerListView: Set includeMetadata=false for fast basic list view that only shows bucket names without metadata This optimizes performance: - Basic list view loads instantly (1 API call) - Table view with metadata loads progressively (batched requests) * docs(ceph): document metadata estimation limitations Add explicit documentation that count, bytes, and last_modified are ESTIMATES when buckets contain >1000 objects, addressing Copilot feedback about potential data inaccuracy. Changes: - Updated containerSchema JSDoc to warn about estimation limitations - Added detailed comments in containerRouter about MaxKeys=1000 impact - Clarified that pagination would be needed for accurate data (expensive) Context: ListObjectsV2 with MaxKeys=1000 means: - count: Capped at 1000 (use KeyCount for actual count up to limit) - bytes: Only sums first 1000 objects - last_modified: May miss newer objects beyond first 1000 Trade-off: Performance vs accuracy. Current implementation prioritizes fast response times over perfect accuracy for large buckets. Alternative solutions considered: 1. Full pagination (accurate but very slow for large buckets) 2. S3 bucket metrics/inventory (requires additional setup) 3. Current approach: Fast estimates with clear documentation ✅ Addresses Copilot comment on lines +36 to +49. * refactor(ceph): remove misleading TODO about pagination Replace TODO with clear explanation that MaxKeys=1000 limitation is a deliberate design decision, not something that needs fixing. Rationale: - Fast response time for UI list view > perfect accuracy - Full pagination would be prohibitively expensive for large buckets - Current approach (estimates) is the correct trade-off for this use case - Misleading TODOs create false expectations of future "fixes" The limitation is already well-documented in comments and type definitions. * refactor(storage): organize routers into ceph/ and swift/ subdirectories Move storage routers into separate subdirectories to improve organization and avoid confusion between Ceph and Swift implementations. Changes: - Create routers/ceph/ and routers/swift/ subdirectories - Move containerRouter, objectRouter, ec2CredentialRouter to ceph/ - Move swiftRouter to swift/ - Add index.ts exports for each subdirectory - Update all import paths in routers and tests - Update main routers/index.ts to import from subdirectories All 4397 tests passing after restructure. * fix(i18n): remove duplicate translation message IDs Fix duplicate i18n message IDs identified by Copilot: - Change 'EmptyBucketsModal' to use existing '... and {hiddenCount} more' (with spaces) - Fix 'EmptyBucketModal' Trans block formatting to keep string on one line This ensures consistent UI copy across features and reduces translation burden. Regenerated translation files with lingui extract and compile. * refactor: code style * refactor: translations * refactor: code styles * refactor(storage): simplify Ceph service resolution in resolveS3Config Refactored resolveS3Config to use ctx.openstack.service() API directly instead of manually searching through the catalog, addressing PR feedback from @andypf. Changes: - Removed redundant token.tokenData.catalog.find() logic - Use service.availableEndpoints() instead of accessing catalog directly - Simplified error handling and reduced nesting - Updated all test mocks to include availableEndpoints() method Fixes code duplication where we were already calling ctx.openstack.service("ceph") but then manually searching the catalog again. * refactor(storage): extract S3 MaxKeys value to constant Extracted hardcoded MaxKeys=1000 value to S3_MAX_KEYS_PER_REQUEST constant, addressing PR feedback from @mark-karnaukh-extern-sap. Changes: - Created src/server/Storage/constants.ts with S3_MAX_KEYS_PER_REQUEST constant - Updated objectRouter.ts to use the constant - Updated containerRouter.ts to use the constant - Updated comment in types/ceph.ts to reference the constant name This improves maintainability - the value is now defined in one place with proper documentation. * fix(ceph): show creationDate when last_modified is missing for empty containers Empty containers don't have last_modified field, so we now fall back to creationDate which is always present from S3 CreationDate. Changes: - Updated ContainerTableView to use creationDate as fallback - Updated test to reflect the new behavior - formatDate handles empty string gracefully (returns N/A) * refactor: modals * refactor: data grid * fix(ceph): localize delete bucket button title in ContainerListView * fix(ceph): avoid dereferencing bucket in async mutation callbacks Capture bucket name before async operations to prevent accessing null bucket reference if modal closes before callbacks execute. - EmptyBucketModal: capture bucketName in handleSubmit, pass to inline onSuccess/onError - DeleteBucketModal: capture bucketName in handleSubmit, pass to inline onSuccess/onError - Update tests to expect second parameter (options) in mutate() calls - Move invalidate() to onSettled to ensure it runs in both success and error cases * refactor: env ceph region variable * test(ceph): update tests to use CEPH_REGION env variable * test(ceph): add CEPH_REGION env to containerRouter and objectRouter tests
1 parent f28aea4 commit c87fa70

45 files changed

Lines changed: 6878 additions & 456 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/aurora-portal/.env.example

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ IDENTITY_ENDPOINT="http://localhost:8080/identity/v3/"
22
DEFAULT_ENDPOINT_INTERFACE="public"
33
PORT="4001"
44
CEPH_S3_ENDPOINT="https://rgw.example.com"
5-
CEPH_REGION="default"
5+
# Ceph region identifier for AWS Signature V4 signing and LocationConstraint
6+
# SAP Converged Cloud examples:
7+
# - Standard regions: ceph-objectstore-st1 + {region}
8+
# For other Ceph deployments, use your custom region identifier
9+
CEPH_REGION="ceph-objectstore-ec-st1-qa-de-1"
610
IMAGE_METADATA_EXCLUDED_PROPERTIES="name,tags,visibility,protected,min_disk,min_ram,id,status,size,checksum,created_at,updated_at,created-at,updated-at,disk_format,container_format,file,schema,locations,self,direct_url,owner,virtual_size,kernel_id,ramdisk_id,os_hash_algo,os_hash_value,os-hash-algo,os-hash-value,stores,owner_specified.openstack.md5,owner_specified.openstack.sha256,owner_specified.openstack.object"
711
INSECURE_COOKIES=true
812

apps/aurora-portal/docs/009_ceph_s3_bff.md

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,32 @@ The `createS3Client` factory:
8585

8686
1. Validates that `access` and `secret` are non-empty
8787
2. Resolves the S3 endpoint:
88-
- **Primary:** Extract from Ceph service catalog (removes `/swift/v1/...` suffix)
89-
- **Fallback:** `CEPH_S3_ENDPOINT` environment variable
90-
3. Resolves the region: `CEPH_REGION` env var or `"default"`
88+
- Extract from Ceph service catalog (removes `/swift/v1/...` suffix)
89+
3. Resolves the region from OpenStack service catalog:
90+
- Extracts the OpenStack region from Ceph service endpoint (e.g., `qa-de-1`, `eu-de-2`)
91+
- Constructs Ceph-compatible region identifier:
92+
- Standard format: `ceph-objectstore-st1-{region}` (e.g., `ceph-objectstore-st1-eu-de-2`)
93+
- Special case: `qa-de-1` uses `ceph-objectstore-ec-st1-qa-de-1` (with "ec" prefix for historical reasons)
94+
- This identifier is used for:
95+
- AWS Signature V4 request signing (region field in Authorization header)
96+
- LocationConstraint in CreateBucket API calls
9197
4. Returns a configured AWS SDK v3 `S3Client` with:
9298
- `forcePathStyle: true` (required for Ceph RGW — it does not support virtual-hosted-style URLs)
9399
- Static credentials (access key ID + secret access key)
94100

101+
**Region Configuration:**
102+
103+
Region identifiers are automatically constructed from the OpenStack service catalog:
104+
105+
- Extracts region from Ceph service endpoint (e.g., `qa-de-1`, `eu-de-2`, `staging`)
106+
- Constructs Ceph-compatible identifier using the pattern from Go SDK / Terraform:
107+
- Standard: `ceph-objectstore-st1-{region}` (e.g., `ceph-objectstore-st1-eu-de-2`)
108+
- Exception: `qa-de-1``ceph-objectstore-ec-st1-qa-de-1` (uses "ec" prefix for historical reasons)
109+
- Used for AWS Signature V4 request signing and LocationConstraint in CreateBucket
110+
- No environment variable override needed — region is auto-detected
111+
112+
See: https://documentation.global.cloud.sap/docs/customer/storage/obj-v2-ceph/ceph-storage-options/
113+
95114
### 3. Middleware Layers
96115

97116
#### `cephCredentialMiddleware`
@@ -571,11 +590,10 @@ try {
571590

572591
### Environment Variables
573592

574-
#### Optional
593+
None required. All configuration is resolved from the OpenStack service catalog:
575594

576-
- **`CEPH_REGION`**
577-
- S3 region name (default: `"default"`)
578-
- Used for AWS SDK signature calculation
595+
- S3 endpoint: extracted from Ceph service endpoints
596+
- Region: auto-constructed from OpenStack region identifier
579597

580598
### Service Catalog Endpoint Resolution
581599

@@ -586,9 +604,13 @@ The BFF resolves the S3 endpoint from the OpenStack service catalog:
586604
3. If the URL contains `/swift/`, remove that suffix:
587605
- Swift: `https://rgw.example.com/swift/v1/AUTH_xxx`
588606
- S3: `https://rgw.example.com` (base URL)
589-
4. Return the base URL
607+
4. Extract the region from the Ceph service endpoint (e.g., `qa-de-1`)
608+
5. Construct the Ceph-compatible region identifier:
609+
- Standard: `ceph-objectstore-st1-{region}`
610+
- Exception: `qa-de-1``ceph-objectstore-ec-st1-qa-de-1`
611+
6. Return the base URL and region identifier
590612

591-
**Important:** The Ceph service **must** be registered in the OpenStack service catalog. Environment variable fallbacks (e.g., `CEPH_S3_ENDPOINT`) are **not supported** — all configuration comes from the service catalog to ensure consistency across deployments.
613+
**Important:** The Ceph service **must** be registered in the OpenStack service catalog. All configuration comes from the service catalog to ensure consistency across deployments.
592614

593615
---
594616

@@ -799,8 +821,8 @@ Both can coexist — Ceph RGW supports **both Swift and S3 APIs** on the same cl
799821
### Not Yet Implemented
800822

801823
1. **Bucket Management**
802-
- Create bucket (`CreateBucketCommand`)
803-
- Delete bucket (`DeleteBucketCommand`)
824+
- ~~Create bucket (`CreateBucketCommand`)~~ ✅ Implemented
825+
- ~~Delete bucket (`DeleteBucketCommand`)~~ ✅ Implemented
804826
- Configure bucket policies, CORS, lifecycle rules
805827

806828
2. **Object Upload/Download**

0 commit comments

Comments
 (0)