mirror of
https://github.com/hanzoai/s3-csi.git
synced 2026-09-30 23:52:32 +00:00
Fork of seaweedfs/seaweedfs-csi-driver, renamed seaweedfs -> s3 throughout and ported off protobuf entirely. WHY a fork was unavoidable: hanzoai/s3 serves HanzoFiler over the native ZAP transport on the port still named "grpc" (18888) — s3/command/filer.go binds it with transport.ListenStream and says it "replaces the legacy gRPC HanzoFiler server: the whole filer (28 unary + 5 streaming RPCs) now answers over ZAP, no gRPC". RegisterHanzoFilerServer exists nowhere in that tree. So the upstream driver's gRPC client cannot connect at all; it fails as `Unimplemented: unknown service filer_pb.SeaweedFiler`, which LOOKS like a service-name mismatch but is a gRPC client hitting a non-gRPC port. Renaming the service in the client would not have helped. pkg/filer is the only place this driver touches storage, and it speaks ZAP: Mkdir -> CreateEntry (CreateVolume) Remove -> DeleteEntry (DeleteVolume) Exists -> LookupDirectoryEntry (ValidateVolumeCapabilities) One shared, concurrency-safe client multiplexes over a single ZAP connection, dials lazily so controller start does not depend on filer readiness, and redials exactly once on a transport fault (covers a filer restart closing an idle conn without becoming a retry storm against a struggling backend). pkg/driver/volume.go Quota() likewise moves to s3/wire/mount — the fork generates no mount_pb at all. Dead gRPC plumbing (WithFilerClient, AdjustedUrl, grpcDialOption) is deleted rather than left inert. The kubelet-facing side stays CSI gRPC: that is the Kubernetes contract between kubelet and any CSI driver, not a Hanzo choice. Zero protobuf call sites remain. Whole module compiles clean. Co-authored-by: Hanzo Dev <[email protected]>
104 lines
3.1 KiB
Go
104 lines
3.1 KiB
Go
package k8s
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/kubernetes/fake"
|
|
)
|
|
|
|
func TestGetVolumeCapacityByPersistentVolumeName(t *testing.T) {
|
|
client := fake.NewSimpleClientset(newPersistentVolume("pvc-legacy", "pvc-legacy", "5Gi"))
|
|
|
|
got, err := getVolumeCapacity(context.Background(), client, "s3-csi", "pvc-legacy")
|
|
if err != nil {
|
|
t.Fatalf("getVolumeCapacity: %v", err)
|
|
}
|
|
if want := int64(5 * 1024 * 1024 * 1024); got != want {
|
|
t.Fatalf("capacity = %d, want %d", got, want)
|
|
}
|
|
}
|
|
|
|
func TestGetVolumeCapacityByCSIVolumeHandle(t *testing.T) {
|
|
client := fake.NewSimpleClientset(newPersistentVolume(
|
|
"pvc-1234",
|
|
"/buckets/pvc-1234",
|
|
"10Gi",
|
|
))
|
|
|
|
got, err := getVolumeCapacity(context.Background(), client, "s3-csi", "/buckets/pvc-1234")
|
|
if err != nil {
|
|
t.Fatalf("getVolumeCapacity: %v", err)
|
|
}
|
|
if want := int64(10 * 1024 * 1024 * 1024); got != want {
|
|
t.Fatalf("capacity = %d, want %d", got, want)
|
|
}
|
|
}
|
|
|
|
func TestGetVolumeCapacityRejectsDuplicateHandles(t *testing.T) {
|
|
client := fake.NewSimpleClientset(
|
|
newPersistentVolume("pv-a", "/shared/path", "1Gi"),
|
|
newPersistentVolume("pv-b", "/shared/path", "2Gi"),
|
|
)
|
|
|
|
if _, err := getVolumeCapacity(context.Background(), client, "s3-csi", "/shared/path"); err == nil {
|
|
t.Fatal("expected duplicate volume handles to return an error")
|
|
}
|
|
}
|
|
|
|
func TestGetVolumeCapacityScopesHandleToDriver(t *testing.T) {
|
|
otherDriverVolume := newPersistentVolume("other-pv", "/shared/path", "1Gi")
|
|
otherDriverVolume.Spec.CSI.Driver = "other.csi.example"
|
|
client := fake.NewSimpleClientset(
|
|
otherDriverVolume,
|
|
newPersistentVolume("s3-pv", "/shared/path", "2Gi"),
|
|
)
|
|
|
|
got, err := getVolumeCapacity(context.Background(), client, "s3-csi", "/shared/path")
|
|
if err != nil {
|
|
t.Fatalf("getVolumeCapacity: %v", err)
|
|
}
|
|
if want := int64(2 * 1024 * 1024 * 1024); got != want {
|
|
t.Fatalf("capacity = %d, want %d", got, want)
|
|
}
|
|
}
|
|
|
|
func TestGetVolumeCapacityIgnoresNameCollisionOnFastPath(t *testing.T) {
|
|
// A PV named "data" exists but belongs to a different volume handle. The
|
|
// fast-path Get by basename ("data") must not match it; the handle lookup
|
|
// has to fall back to the List scan and find the correct volume.
|
|
decoy := newPersistentVolume("data", "/some/other/path", "1Gi")
|
|
client := fake.NewSimpleClientset(
|
|
decoy,
|
|
newPersistentVolume("real-pv", "/mnt/data", "7Gi"),
|
|
)
|
|
|
|
got, err := getVolumeCapacity(context.Background(), client, "s3-csi", "/mnt/data")
|
|
if err != nil {
|
|
t.Fatalf("getVolumeCapacity: %v", err)
|
|
}
|
|
if want := int64(7 * 1024 * 1024 * 1024); got != want {
|
|
t.Fatalf("capacity = %d, want %d", got, want)
|
|
}
|
|
}
|
|
|
|
func newPersistentVolume(name, handle, capacity string) *corev1.PersistentVolume {
|
|
return &corev1.PersistentVolume{
|
|
ObjectMeta: metav1.ObjectMeta{Name: name},
|
|
Spec: corev1.PersistentVolumeSpec{
|
|
Capacity: corev1.ResourceList{
|
|
corev1.ResourceStorage: resource.MustParse(capacity),
|
|
},
|
|
PersistentVolumeSource: corev1.PersistentVolumeSource{
|
|
CSI: &corev1.CSIPersistentVolumeSource{
|
|
Driver: "s3-csi",
|
|
VolumeHandle: handle,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|