Skip to content

Add rustfs#65

Open
kwonkwonn wants to merge 3 commits into
easy-cloud-Knet:stagingfrom
kwonkwonn:object_storage_intergration
Open

Add rustfs#65
kwonkwonn wants to merge 3 commits into
easy-cloud-Knet:stagingfrom
kwonkwonn:object_storage_intergration

Conversation

@kwonkwonn

@kwonkwonn kwonkwonn commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #64 — Story 1: rustfsClient 구현 및 테스트 환경 구성

client/rustfs.go: aws-sdk-go-v2 기반 RustFS 클라이언트 구현

  • NewRustFSClient() — endpoint/accessKey/secretKey 환경변수 기반 초기화 (RUSTFS_ENDPOINT, RUSTFS_ACCESS_KEY, RUSTFS_SECRET_KEY)
  • ListBuckets(), CreateBucket() — 버킷 관리
  • PresignGetObject(), PresignPutObject() — Presigned URL 발급 (업로드/다운로드는 Core가 직접 수행)
  • S3 Path-style 강제 설정 (UsePathStyle: true) — RustFS 호환을 위해 필수
  • tests/docker-compose.test.yml: rustfs-test 서비스 추가 (포트 19000/19001, healthcheck 포함)
  • SDK 결정: 이슈에서 검토된 MinIO SDK 대신 aws-sdk-go-v2 채택 — RustFS가 S3 API 호환을 공식 지원하므로 AWS 표준 SDK로 충분하며, 이미 AWS 생태계를 사용하는 경우 의존성을 통일할 수 있음

Test plan

docker compose -f docker-compose.test.yml up --build 실행 후 kws-test-rustfs healthy 확인
NewRustFSClient() 호출 시 버킷 생성 및 ListBuckets() 반환값 로그 확인
Presigned URL로 Core에서 추가 인증 없이 업로드/다운로드 가능 여부 확인

Out of scope (후속 태스크)

Service 레이어 연동 (Story 2)
Repository / DB 스키마 (별도 태스크)
동기/비동기 처리 방식 확정 (Story 3)

notes

go.mod 및 dockerfile 에 변화가 있었습니다.
aws-sdk import 중에 발생했는데 특정 버전에 sdk 를 맞추는것도 좋지 않아보여서 업데이트했습니다.
혹시 관련하여 우려사항 있다면 알려주세용

Type of Change

  • Bug fix
  • New feature
  • Refactoring
  • Docs / Config
  • CI/CD

Testing

  • Tested locally
  • No regression in existing functionality

Checklist

  • Reviewers assigned
  • Related issue linked

Summary by CodeRabbit

  • New Features

    • S3-compatible storage client with presigned PUT/GET URLs for uploads/downloads
    • Snapshot API: take, list, and delete VM snapshots (endpoints and service)
    • VM creation accepts optional presigned image URL for remote image uploads
  • Tests

    • Test compose and end-to-end tests include local storage service and snapshot scenarios
  • Chores

    • Go toolchain and base image updated; minor ignore-file addition

@kwonkwonn kwonkwonn force-pushed the object_storage_intergration branch 3 times, most recently from a664e76 to 9b1ec88 Compare May 25, 2026 08:27
coderabbitai[bot]

This comment was marked as outdated.

@kwonkwonn kwonkwonn force-pushed the object_storage_intergration branch from 9b1ec88 to 2923f85 Compare May 25, 2026 08:28
@kwonkwonn kwonkwonn force-pushed the object_storage_intergration branch from 2923f85 to 1198a35 Compare May 25, 2026 08:30

@kwonkwonn kwonkwonn left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공식 문서상 aws-s3-sdk 와 완전히 compatible 하다고 합니다.
그래서 aws-s3-sdk 를 가져다 쓸 예정이고, list 및 bucket 생성은 컨트롤에서
업로드 및 다운로드는 preasigned link 를 코어에 넘기는 방식으로 할 예정입니다

Comment thread go.mod
module github.com/easy-cloud-Knet/KWS_Control

go 1.23.0
go 1.24

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1.23.0 <-> aws sdk 와 호환성 문제가 있어서 업데이트 했는데 혹시 관련해서 문제 있을까요

Comment thread client/rustfs.go
endpoint := os.Getenv("RUSTFS_ENDPOINT")
if endpoint == "" {
endpoint = "http://localhost:9001"
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minioadmin<-- docker-compose.test.yaml 파일의 인자를 일단 덮어쓰도록 했습니다.
control 단에서 accesskey 넘어가는건 마지막 단계에서 할 거 같아요

@easy-cloud-Knet easy-cloud-Knet deleted a comment from coderabbitai Bot May 31, 2026
@kwonkwonn

Copy link
Copy Markdown
Contributor Author

feat: integrate RustFS as external snapshot storage

Summary

  • RustFS(S3-compatible) 를 external snapshot 저장소로 연동
  • CreateVMRequestpresignedImageUrl 필드 추가 (omitempty, 하위호환)
  • 스냅샷 API 3종 추가: POST/GET/DELETE /vm/snapshot

Changes

client/rustfs.go

  • HeadObject, ListObjects, DeleteObject 추가
  • sync.Once 싱글톤(GetRustFSClient) — config.LoadDefaultConfig 반복 호출 방지

client/model/snapshot.go (신규)

  • Core 스냅샷 API contract 정의 (TakeSnapshotRequest/Response)
  • omitempty로 Core 미구현 상태에서도 하위호환 유지

service/external_snap.go (신규)

  • ListSnapshots: VM UUID 버킷 기준 RustFS 조회
  • DeleteSnapshot: 버킷/키 직접 삭제
  • TakeSnapshot: presigned PUT URL 생성까지 구현, Core 호출은 TODO (Core API 미구현)

api/snapshot.go (신규)

  • 스냅샷 핸들러 3종, 기존 핸들러 패턴 동일하게 적용

Notes

  • 다른 클라이언트 New* 함수에도 sync.Once 도입?
  • CreateVM 내 이미지 presign 단계(HeadObject → presign → Core 전달)는 Core API 준비 후 별도 PR

@kwonkwonn kwonkwonn changed the title Add Test dockerscript for rustfs Add rustfs May 31, 2026
@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a RustFSClient (S3 wrapper) with presign and object APIs, snapshot service functions that use the client, HTTP snapshot endpoints and models, test Docker Compose wiring for RustFS, end-to-end snapshot tests, and Go/tooling dependency updates.

Changes

RustFS Client and Snapshot Flow

Layer / File(s) Summary
Client initialization and structure
client/rustfs.go
RustFSClient type, singleton accessor, and NewRustFSClient reading RUSTFS_ENDPOINT, RUSTFS_ACCESS_KEY, RUSTFS_SECRET_KEY, configuring AWS SDK for us-east-1 with static creds and custom endpoint/path-style addressing.
Bucket and object operations
client/rustfs.go
Methods: ListBuckets, CreateBucket, PresignPutObject, PresignGetObject, HeadObject, ListObjects, DeleteObject implemented as thin S3 wrappers with contextual error messages.
Service: external snapshot operations
service/external_snap.go
Adds ListSnapshots, DeleteSnapshot, and TakeSnapshot. TakeSnapshot finds the VM core, generates a presigned PUT URL (15m TTL) for snapshot upload, and currently leaves core upload as TODO.
API handlers and client models
api/snapshot.go, api/handlers.go, client/model/*
Adds POST /vm/snapshot, GET /vm/snapshot, DELETE /vm/snapshot handlers; request/response structs (ApiTakeSnapshotRequest, ApiDeleteSnapshotRequest, TakeSnapshotRequest/Response); extends CreateVMRequest with PresignedImageURL.
Test environment orchestration
tests/docker-compose.test.yml
Adds rustfs-test service (rustfs/rustfs image) with credentials, ports, volume and TCP healthcheck; updates control-test to set RUSTFS_ENDPOINT and depend on rustfs-test being healthy.
End-to-end snapshot tests
tests/blackbox_io_test.sh
Adds Section 7 snapshot E2E tests: validation (400), non-existent VM behavior (500), and happy-path create/list/delete/presign flows using mc inside test network.
Build and config
go.mod, Dockerfile, .gitignore
Bumps Go to 1.24 and updates AWS SDK v2 pins; Dockerfile base image updated to golang:1.24; adds CLAUDE.md to .gitignore.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #66: Overlaps TakeSnapshot and RustFS client changes; this PR implements presign flows and snapshot service hooks referenced by that issue.
  • #63: Matches the objective to add a RustFS S3-compatible client and object/presign APIs.

Poem

🐰 I dug a tunnel to the S3 field,
I wrapped a client with a tiny shield,
Presigned links to carry the night,
Tests and Docker keep things right,
Hopping home — snapshots tucked tight.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes snapshot API implementation (handlers, service layer, models) that extends beyond the linked issue #64 scope, which focused solely on RustFS client initialization. Clarify whether snapshot API integration (api/snapshot.go, service/external_snap.go, client/model/snapshot.go) should be included in this PR or deferred to a separate issue covering service layer integration.
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Add rustfs' is vague and generic, using minimal specificity about what RustFS functionality is being added. Consider a more descriptive title like 'Add RustFS S3 client with presigned URL support' to clarify the main implementation details.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed All coding-related requirements from issue #64 are met: RustFS client initialization with environment variable configuration, bucket management operations, and presigned URL generation for upload/download are fully implemented.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/snapshot.go`:
- Around line 36-40: The handler is returning raw err.Error() to clients (e.g.,
after service.TakeSnapshot in the snapshot handler) which leaks internal
details; instead keep the existing log.Error call to record full details but
change the HTTP response body passed to util.RespondError to a generic message
(e.g., "internal server error" or "failed to take snapshot") that does not
include error internals; apply the same change for the other occurrences
referenced (the error branches around lines 54-59 and 78-82) so all calls to
util.RespondError use non-sensitive, user-friendly messages while preserving the
detailed server-side logs.

In `@client/rustfs.go`:
- Around line 132-147: The ListObjects implementation in RustFSClient currently
issues a single ListObjectsV2 call and truncates results beyond 1000 keys;
replace that single-call logic with the AWS SDK v2 paginator
(s3.NewListObjectsV2Paginator) to iterate all pages, accumulating
aws.ToString(obj.Key) for each out.Contents until the paginator is done,
propagate any page error as before, and ensure you still return an empty slice
(not nil) when no objects are found; update the ListObjects function to use the
paginator and preserve the existing error message formatting.
- Around line 40-42: The default RUSTFS endpoint fallback currently sets
endpoint = "http://localhost:9001" which points to the web console; change the
fallback to the S3 API port by setting the default to "http://localhost:9000" in
client/rustfs.go where the variable endpoint is assigned so S3-compatible calls
go to the correct port.

In `@service/external_snap.go`:
- Around line 14-24: ListSnapshots currently returns an error when
rustfs.ListObjects fails for a VM that has no per-UUID bucket; change
ListSnapshots (and the ListObjects error handling) to treat a NoSuchBucket (or
equivalent "bucket not found") error as "no snapshots" by returning an empty
[]string and nil error instead of propagating a 500; use the same rustfs client
from client.GetRustFSClient and detect the missing-bucket condition via the
rustfs/minio/S3 error sentinel or by matching the NoSuchBucket error string,
then return []string{} , nil.
- Around line 39-53: TakeSnapshot currently presigns a PUT against string(uuid)
without ensuring the per-VM bucket exists, causing NoSuchBucket on upload;
modify TakeSnapshot (before calling rustfs.PresignPutObject) to ensure the
bucket exists by calling rustfs.CreateBucket (or a new helper EnsureBucket) for
bucket string(uuid) and treat BucketAlreadyOwnedByYou/BucketAlreadyExists as
non-fatal (swallow those specific errors), returning other errors; keep the
existing rustfs.PresignPutObject call after the bucket existence check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8f119b54-f2ff-44ca-94ff-d730db692a7a

📥 Commits

Reviewing files that changed from the base of the PR and between a00027c and 926942d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (10)
  • .gitignore
  • Dockerfile
  • api/handlers.go
  • api/snapshot.go
  • client/model/snapshot.go
  • client/model/vm.go
  • client/rustfs.go
  • go.mod
  • service/external_snap.go
  • tests/docker-compose.test.yml
✅ Files skipped from review due to trivial changes (1)
  • Dockerfile
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/docker-compose.test.yml

Comment thread api/snapshot.go
Comment thread client/rustfs.go
Comment thread client/rustfs.go
Comment thread service/external_snap.go
Comment thread service/external_snap.go
@kwonkwonn kwonkwonn force-pushed the object_storage_intergration branch from 78662e2 to 43238cf Compare May 31, 2026 10:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/blackbox_io_test.sh (1)

455-461: ⚡ Quick win

set -e makes a silent mc_exec failure abort the suite without a reported assertion.

mc_exec on Line 455 runs as a bare command under set -euo pipefail. If mc mb/mc cp returns non-zero (e.g., alias setup failed, bucket name collision, RRustFS not yet ready), the script exits immediately—before the Line 457/461 assertions can surface a FAIL. Because all output is routed to /dev/null, the run terminates with no diagnostic about why. Consider gating the seed step on success (or asserting it) so the failure is reported through the harness.

♻️ Proposed guard for the seed step
-mc_exec "mc mb r/${SNAP_UUID} && echo seed > /tmp/f && mc cp /tmp/f r/${SNAP_UUID}/snap-seed"
+if mc_exec "mc mb r/${SNAP_UUID} && echo seed > /tmp/f && mc cp /tmp/f r/${SNAP_UUID}/snap-seed"; then
+    pass "Seeded snapshot bucket/key via mc"
+else
+    fail "Failed to seed snapshot bucket/key via mc" "mc seed ok" "mc seed failed"
+fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/blackbox_io_test.sh` around lines 455 - 461, The seed step uses mc_exec
as a bare command under set -euo pipefail so any silent failure aborts the suite
without a test assertion; change the seed step (the mc_exec invocation that runs
"mc mb ... && mc cp ...") to be guarded and asserted: run mc_exec and capture
its exit status and output, or wrap it in an assertion helper (e.g., use
assert_status/assert_exit or a custom assert_command) so failures produce a
controlled test failure and include the mc_exec stdout/stderr (don’t redirect
all output to /dev/null) before proceeding to the GET /vm/snapshot checks
(references: mc_exec invocation, assert_status, assert_body_contains in the
test).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/blackbox_io_test.sh`:
- Around line 455-461: The seed step uses mc_exec as a bare command under set
-euo pipefail so any silent failure aborts the suite without a test assertion;
change the seed step (the mc_exec invocation that runs "mc mb ... && mc cp ...")
to be guarded and asserted: run mc_exec and capture its exit status and output,
or wrap it in an assertion helper (e.g., use assert_status/assert_exit or a
custom assert_command) so failures produce a controlled test failure and include
the mc_exec stdout/stderr (don’t redirect all output to /dev/null) before
proceeding to the GET /vm/snapshot checks (references: mc_exec invocation,
assert_status, assert_body_contains in the test).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0129e5f0-099e-447d-9fcd-2d9900a8d1d0

📥 Commits

Reviewing files that changed from the base of the PR and between 926942d and 43238cf.

📒 Files selected for processing (8)
  • .gitignore
  • api/handlers.go
  • api/snapshot.go
  • client/model/snapshot.go
  • client/model/vm.go
  • client/rustfs.go
  • service/external_snap.go
  • tests/blackbox_io_test.sh
✅ Files skipped from review due to trivial changes (2)
  • client/model/vm.go
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (5)
  • client/model/snapshot.go
  • api/handlers.go
  • service/external_snap.go
  • client/rustfs.go
  • api/snapshot.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RustFS client initialization

1 participant