-
Notifications
You must be signed in to change notification settings - Fork 0
Add bucket quota apis #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: improvement/CLDSRVCLT-7
Are you sure you want to change the base?
Changes from all commits
600ab90
49c13fe
3d0f040
cf580bb
3768de7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,66 +1,82 @@ | ||
| --- | ||
| name: release | ||
| run-name: release ${{ inputs.tag }} | ||
| name: Release | ||
| run-name: Release | ||
|
|
||
| on: | ||
| # Uncomment to test your work as a release before it's merged | ||
| # push: | ||
| # branches: | ||
| # - improvement/CLDSRVCLT-X | ||
| workflow_dispatch: | ||
| inputs: | ||
| tag: | ||
| description: 'Tag to be released (e.g., 1.0.0)' | ||
| required: true | ||
|
|
||
| jobs: | ||
| build-and-tag: | ||
| name: Build and tag | ||
| build: | ||
| name: Build | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
|
|
||
| outputs: | ||
| version: ${{ steps.package-version.outputs.version }} | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Get version from package.json | ||
| id: package-version | ||
| run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Setup and Build | ||
| uses: ./.github/actions/setup-and-build | ||
|
|
||
| - name: Create Tag with Build Artifacts | ||
| run: | | ||
| # Configure git user to the GitHub Actions bot | ||
| git config --global user.name "github-actions[bot]" | ||
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | ||
| - name: Upload build artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: build-artifacts | ||
| path: | | ||
| dist/ | ||
| build/ | ||
|
|
||
| # Force add the build folders (even if they are in .gitignore) | ||
| git add -f dist build | ||
| publish-npm: | ||
| name: Publish to npm registry | ||
| runs-on: ubuntu-latest | ||
| needs: build | ||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| # Determine tag name | ||
| TAG_NAME="${{ inputs.tag }}" | ||
| if [ -z "$TAG_NAME" ]; then | ||
| TAG_NAME="test-${{ github.sha }}" | ||
| fi | ||
| - name: Download build artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: build-artifacts | ||
|
|
||
| # Commit the build artifacts | ||
| git commit -m "Build artifacts for version $TAG_NAME" | ||
| - name: Setup Node.js for npm registry | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '24' | ||
| registry-url: 'https://registry.npmjs.org' | ||
|
|
||
| # Create the tag | ||
| git tag $TAG_NAME | ||
| - name: Publish to npm with provenance | ||
| run: npm publish --provenance --tag latest | ||
|
|
||
| # Push the tag to the repository | ||
| git push origin $TAG_NAME | ||
| create-release: | ||
| name: Create GitHub Release | ||
| runs-on: ubuntu-latest | ||
| needs: [build, publish-npm] | ||
| permissions: | ||
| contents: write | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| # Export tag name for next step | ||
| echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT | ||
| id: create_tag | ||
| - name: Create Git Tag | ||
| run: | | ||
| git config --global user.name "github-actions[bot]" | ||
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | ||
| git tag ${{ needs.build.outputs.version }} | ||
| git push origin ${{ needs.build.outputs.version }} | ||
|
|
||
| - name: Create GitHub Release | ||
| uses: softprops/action-gh-release@v2 | ||
| with: | ||
| tag_name: ${{ steps.create_tag.outputs.tag_name }} | ||
| name: Release ${{ steps.create_tag.outputs.tag_name }} | ||
| draft: false | ||
| prerelease: false | ||
| tag_name: ${{ needs.build.outputs.version }} | ||
| name: Release ${{ needs.build.outputs.version }} | ||
| generate_release_notes: true | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| $version: "2.0" | ||
|
|
||
| namespace cloudserver.bucketquota | ||
|
|
||
| @http(method: "DELETE", uri: "/{Bucket}?quota=true") | ||
| @idempotent | ||
| operation DeleteBucketQuota { | ||
| input := { | ||
| @required | ||
| @httpLabel | ||
| Bucket: String | ||
| } | ||
| output := {} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| $version: "2.0" | ||
|
|
||
| namespace cloudserver.bucketquota | ||
|
|
||
| @http(method: "GET", uri: "/{Bucket}?quota=true") | ||
| @readonly | ||
| operation GetBucketQuota { | ||
| input := { | ||
| @required | ||
| @httpLabel | ||
| Bucket: String | ||
| } | ||
| output := { | ||
| Name: String | ||
| Quota: Integer | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,20 @@ | ||||||
| $version: "2.0" | ||||||
|
|
||||||
| namespace cloudserver.bucketquota | ||||||
|
|
||||||
| @http(method: "PUT", uri: "/{Bucket}?quota=true") | ||||||
| @idempotent | ||||||
| operation UpdateBucketQuota { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. keep same conventions as AWS (and http/rest in general) ?
Suggested change
|
||||||
| input := { | ||||||
| @required | ||||||
| @httpLabel | ||||||
| Bucket: String | ||||||
|
|
||||||
| @httpPayload | ||||||
| quota: String | ||||||
| } | ||||||
| output := { | ||||||
| @httpPayload | ||||||
| body: String | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,38 @@ | ||
| { | ||
| "name": "@scality/cloudserverclient", | ||
| "version": "1.0.0", | ||
| "version": "1.0.1", | ||
| "engines": { | ||
| "node": ">=20" | ||
| }, | ||
| "description": "Smithy-generated TypeScript client for Cloudserver's internal APIs", | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "exports": { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will allow importing with subpath, for example import { BucketQuotaClient } from '@scality/cloudserverclient/quota' |
||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "default": "./dist/index.js" | ||
| }, | ||
| "./*": { | ||
| "types": "./dist/*.d.ts", | ||
| "default": "./dist/*.js" | ||
| } | ||
| }, | ||
| "files": [ | ||
| "dist", | ||
| "build/smithy/source/typescript-codegen" | ||
| "build/smithy/cloudserver/typescript-codegen", | ||
| "build/smithy/cloudserverBucketQuota/typescript-codegen" | ||
| ], | ||
| "publishConfig": { | ||
| "access": "public", | ||
| "registry": "https://registry.npmjs.org" | ||
| }, | ||
| "scripts": { | ||
| "clean:build": "rm -rf build dist", | ||
| "build:smithy": "smithy build", | ||
| "build:generated": "cd build/smithy/source/typescript-codegen && yarn install && yarn build", | ||
| "build:generated": "cd build/smithy/cloudserver/typescript-codegen && yarn install && yarn build", | ||
| "build:generated:bucketQuota": "cd build/smithy/cloudserverBucketQuota/typescript-codegen && yarn install && yarn build", | ||
| "build:wrapper": "tsc", | ||
| "build": "yarn install && yarn clean:build && yarn build:smithy && yarn build:generated && yarn build:wrapper", | ||
| "build": "yarn install && yarn clean:build && yarn build:smithy && yarn build:generated && yarn build:generated:bucketQuota && yarn build:wrapper", | ||
| "test": "jest", | ||
| "test:indexes": "jest tests/testIndexesApis.test.ts", | ||
| "test:error-handling": "jest tests/testErrorHandling.test.ts", | ||
|
|
@@ -25,14 +41,15 @@ | |
| "test:lifecycle": "jest tests/testLifecycleApis.test.ts", | ||
| "test:metadata": "jest tests/testMetadataApis.test.ts", | ||
| "test:raft": "jest tests/testRaftApis.test.ts", | ||
| "test:mongo-backend": "yarn test:indexes && yarn test:error-handling && yarn test:multiple-backend", | ||
| "test:bucketQuotas": "jest tests/testQuotaApis.test.ts", | ||
| "test:mongo-backend": "yarn test:indexes && yarn test:error-handling && yarn test:multiple-backend && yarn test:bucketQuotas", | ||
| "test:metadata-backend": "yarn test:api && yarn test:lifecycle && yarn test:metadata && yarn test:raft", | ||
| "lint": "eslint src tests", | ||
| "typecheck": "tsc --noEmit" | ||
| }, | ||
| "devDependencies": { | ||
| "@eslint/compat": "^2.0.0", | ||
| "@scality/eslint-config-scality": "scality/Guidelines#8.3.0", | ||
| "@scality/eslint-config-scality": "github:scality/Guidelines#8.3.0", | ||
| "@types/jest": "^30.0.0", | ||
| "@typescript-eslint/eslint-plugin": "^6.21.0", | ||
| "@typescript-eslint/parser": "^6.21.0", | ||
|
|
@@ -51,6 +68,6 @@ | |
| "license": "Apache-2.0", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/scality/cloudserverclient.git" | ||
| "url": "git+https://github.com/scality/cloudserverclient.git" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| $version: "2.0" | ||
|
|
||
| namespace cloudserver.bucketquota | ||
|
|
||
| use aws.protocols#restXml | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have to create a new separate service, as the other client is json, but the quota apis are xml. |
||
| use aws.auth#sigv4 | ||
| use aws.api#service | ||
|
|
||
| @restXml(noErrorWrapping: true) | ||
| @sigv4(name: "s3") | ||
| @service(sdkId: "cloudserverBucketQuota") | ||
| service CloudserverBucketQuota { | ||
| version: "2018-07-11", | ||
| operations: [ | ||
| GetBucketQuota, | ||
| UpdateBucketQuota, | ||
| DeleteBucketQuota, | ||
| ] | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can create a new folder
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had the same thought, but i thought it would look weird because the import path would end up with "client" twice. But still, I moved everything in a /clients folder it will be better |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| import { | ||
| CloudserverBucketQuota, | ||
| CloudserverBucketQuotaClientConfig, | ||
| GetBucketQuotaCommand, | ||
| GetBucketQuotaCommandOutput, | ||
| UpdateBucketQuotaCommand, | ||
| UpdateBucketQuotaCommandOutput, | ||
| DeleteBucketQuotaCommand, | ||
| DeleteBucketQuotaCommandOutput | ||
| } from '../../build/smithy/cloudserverBucketQuota/typescript-codegen'; | ||
| import { CloudserverClientConfig } from '../../build/smithy/cloudserver/typescript-codegen'; | ||
|
|
||
| export class BucketQuotaClient { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ended up creating a wrapper, it will be easier to use as you will see in the tests, and i was kinda forced to do it because of the update quota api. Basically we probably made a mistake when designing the update quota apis, which wants to receive a structure like this :
When using the first structure, we can't use integers, so I was forced to send a string like this, but since we want a convenient api, I don't want frontend colleagues to have to call
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there is one subtelty regarding passing quota as "number", related to javascript:
At production-scale, quotas are large : so we ended up having to "force" use of string at some places to ensure large quotas can be properly retrieved... |
||
| private client: CloudserverBucketQuota; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok to introduce a wrapper (c.f. previous point) ; however, this specific wrapper changes the overall "form" of the API, which does not look like AWS sdk v3 code : looks more like v2. I wonder if it would not be better to wrap the command objects ( (this could also make it more seamless to have combined "s3" + quotas client: just need to write our own client whose |
||
|
|
||
| constructor(config: CloudserverClientConfig | CloudserverBucketQuotaClientConfig) { | ||
| this.client = new CloudserverBucketQuota(config as CloudserverBucketQuotaClientConfig); | ||
| } | ||
|
|
||
| async getBucketQuota(bucketName: string): Promise<GetBucketQuotaCommandOutput> { | ||
| const command = new GetBucketQuotaCommand({ Bucket: bucketName }); | ||
| return this.client.send(command); | ||
| } | ||
|
|
||
| async updateBucketQuota(bucketName: string, quota: number): Promise<UpdateBucketQuotaCommandOutput> { | ||
| const command = new UpdateBucketQuotaCommand({ | ||
| Bucket: bucketName, | ||
| quota: `<quota>${quota}</quota>`, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems weird to handle the XML here : should have a different definition of quota in the protocol? i.e. something more complex instead of just:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: not sure if this is possible and/or the only way, but this so maybe: and would do it... (and we just need to verify there is a single entry in the list) |
||
| }); | ||
| return this.client.send(command); | ||
| } | ||
|
|
||
| async deleteBucketQuota(bucketName: string): Promise<DeleteBucketQuotaCommandOutput> { | ||
| const command = new DeleteBucketQuotaCommand({ Bucket: bucketName }); | ||
| return this.client.send(command); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so now moving to separate clients for each service? even if each client is available "separately", may be good to keep an "aggregated" cloudserver client:
|
||
| CloudserverClient as GeneratedCloudserverClient, | ||
| CloudserverClientConfig | ||
| } from '../../build/smithy/cloudserver/typescript-codegen'; | ||
| import { createCustomErrorMiddleware } from '../utils'; | ||
|
|
||
| export * from '../../build/smithy/cloudserver/typescript-codegen'; | ||
| export class CloudserverClient extends GeneratedCloudserverClient { | ||
| constructor(config: CloudserverClientConfig) { | ||
| super(config); | ||
|
|
||
| this.middlewareStack.add(createCustomErrorMiddleware(), { | ||
| step: 'deserialize', | ||
| name: 'cloudserverErrorHandler', | ||
| priority: 'normal', | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,3 @@ | ||
| import { | ||
| CloudserverClient as GeneratedCloudserverClient, | ||
| CloudserverClientConfig | ||
| } from '../build/smithy/source/typescript-codegen'; | ||
| import { createCustomErrorMiddleware } from './utils'; | ||
|
|
||
| export * from '../build/smithy/source/typescript-codegen'; | ||
| export * from './clients/cloudserver'; | ||
| export { BucketQuotaClient } from './clients/bucketQuota'; | ||
| export * from './utils'; | ||
|
|
||
| export class CloudserverClient extends GeneratedCloudserverClient { | ||
| constructor(config: CloudserverClientConfig) { | ||
| super(config); | ||
|
|
||
| this.middlewareStack.add(createCustomErrorMiddleware(), { | ||
| step: 'deserialize', | ||
| name: 'cloudserverErrorHandler', | ||
| priority: 'normal', | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not directly related with the pr, but this is nicer as we will only have one source of truth for smithy version.
I could definitely see someone updating smithy version here and forgetting about updating it in smithy-build.json