Do not store additional metadata in .metadata.json in the bucket itself
This commit is contained in:
parent
67a0d8ecbc
commit
2089c40001
3 changed files with 42 additions and 106 deletions
|
@ -22,7 +22,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ctrox/csi-s3/pkg/mounter"
|
"github.com/ctrox/csi-s3/pkg/mounter"
|
||||||
|
@ -43,24 +42,9 @@ type controllerServer struct {
|
||||||
func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
|
func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
|
||||||
params := req.GetParameters()
|
params := req.GetParameters()
|
||||||
capacityBytes := int64(req.GetCapacityRange().GetRequiredBytes())
|
capacityBytes := int64(req.GetCapacityRange().GetRequiredBytes())
|
||||||
mounterType := params[mounter.TypeKey]
|
|
||||||
volumeID := sanitizeVolumeID(req.GetName())
|
volumeID := sanitizeVolumeID(req.GetName())
|
||||||
bucketName := volumeID
|
bucketName := volumeID
|
||||||
prefix := ""
|
prefix := ""
|
||||||
mountOptions := make([]string, 0)
|
|
||||||
mountOptStr := params[mounter.OptionsKey]
|
|
||||||
if mountOptStr != "" {
|
|
||||||
re, _ := regexp.Compile(`([^\s"]+|"([^"\\]+|\\")*")+`)
|
|
||||||
re2, _ := regexp.Compile(`"([^"\\]+|\\")*"`)
|
|
||||||
re3, _ := regexp.Compile(`\\(.)`)
|
|
||||||
for _, opt := range re.FindAll([]byte(mountOptStr), -1) {
|
|
||||||
// Unquote options
|
|
||||||
opt = re2.ReplaceAllFunc(opt, func(q []byte) []byte {
|
|
||||||
return re3.ReplaceAll(q[1 : len(q)-1], []byte("$1"))
|
|
||||||
})
|
|
||||||
mountOptions = append(mountOptions, string(opt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if bucket name is overridden
|
// check if bucket name is overridden
|
||||||
if nameOverride, ok := params[mounter.BucketKey]; ok {
|
if nameOverride, ok := params[mounter.BucketKey]; ok {
|
||||||
|
@ -84,14 +68,6 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
||||||
|
|
||||||
glog.V(4).Infof("Got a request to create volume %s", volumeID)
|
glog.V(4).Infof("Got a request to create volume %s", volumeID)
|
||||||
|
|
||||||
meta := &s3.FSMeta{
|
|
||||||
BucketName: bucketName,
|
|
||||||
Prefix: prefix,
|
|
||||||
Mounter: mounterType,
|
|
||||||
MountOptions: mountOptions,
|
|
||||||
CapacityBytes: capacityBytes,
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := s3.NewClientFromSecret(req.GetSecrets())
|
client, err := s3.NewClientFromSecret(req.GetSecrets())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize S3 client: %s", err)
|
return nil, fmt.Errorf("failed to initialize S3 client: %s", err)
|
||||||
|
@ -102,18 +78,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
||||||
return nil, fmt.Errorf("failed to check if bucket %s exists: %v", volumeID, err)
|
return nil, fmt.Errorf("failed to check if bucket %s exists: %v", volumeID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if exists {
|
if !exists {
|
||||||
// get meta, ignore errors as it could just mean meta does not exist yet
|
|
||||||
m, err := client.GetFSMeta(bucketName, prefix)
|
|
||||||
if err == nil {
|
|
||||||
// Check if volume capacity requested is bigger than the already existing capacity
|
|
||||||
if capacityBytes > m.CapacityBytes {
|
|
||||||
return nil, status.Error(
|
|
||||||
codes.AlreadyExists, fmt.Sprintf("Volume with the same name: %s but with smaller size already exist", volumeID),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err = client.CreateBucket(bucketName); err != nil {
|
if err = client.CreateBucket(bucketName); err != nil {
|
||||||
return nil, fmt.Errorf("failed to create bucket %s: %v", bucketName, err)
|
return nil, fmt.Errorf("failed to create bucket %s: %v", bucketName, err)
|
||||||
}
|
}
|
||||||
|
@ -123,16 +88,19 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
||||||
return nil, fmt.Errorf("failed to create prefix %s: %v", prefix, err)
|
return nil, fmt.Errorf("failed to create prefix %s: %v", prefix, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := client.SetFSMeta(meta); err != nil {
|
|
||||||
return nil, fmt.Errorf("error setting bucket metadata: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.V(4).Infof("create volume %s", volumeID)
|
glog.V(4).Infof("create volume %s", volumeID)
|
||||||
|
// DeleteVolume lacks VolumeContext, but publish&unpublish requests have it,
|
||||||
|
// so we don't need to store additional metadata anywhere
|
||||||
|
context := make(map[string]string)
|
||||||
|
for k, v := range params {
|
||||||
|
context[k] = v
|
||||||
|
}
|
||||||
|
context["capacity"] = fmt.Sprintf("%v", capacityBytes)
|
||||||
return &csi.CreateVolumeResponse{
|
return &csi.CreateVolumeResponse{
|
||||||
Volume: &csi.Volume{
|
Volume: &csi.Volume{
|
||||||
VolumeId: volumeID,
|
VolumeId: volumeID,
|
||||||
CapacityBytes: capacityBytes,
|
CapacityBytes: capacityBytes,
|
||||||
VolumeContext: req.GetParameters(),
|
VolumeContext: context,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -140,7 +108,6 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
||||||
func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
|
func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) {
|
||||||
volumeID := req.GetVolumeId()
|
volumeID := req.GetVolumeId()
|
||||||
bucketName, prefix := volumeIDToBucketPrefix(volumeID)
|
bucketName, prefix := volumeIDToBucketPrefix(volumeID)
|
||||||
var meta *s3.FSMeta
|
|
||||||
|
|
||||||
// Check arguments
|
// Check arguments
|
||||||
if len(volumeID) == 0 {
|
if len(volumeID) == 0 {
|
||||||
|
@ -158,11 +125,6 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
|
||||||
return nil, fmt.Errorf("failed to initialize S3 client: %s", err)
|
return nil, fmt.Errorf("failed to initialize S3 client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if meta, err = client.GetFSMeta(bucketName, prefix); err != nil {
|
|
||||||
glog.V(5).Infof("FSMeta of volume %s does not exist, ignoring delete request", volumeID)
|
|
||||||
return &csi.DeleteVolumeResponse{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var deleteErr error
|
var deleteErr error
|
||||||
if prefix == "" {
|
if prefix == "" {
|
||||||
// prefix is empty, we delete the whole bucket
|
// prefix is empty, we delete the whole bucket
|
||||||
|
@ -178,10 +140,6 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
|
||||||
}
|
}
|
||||||
|
|
||||||
if deleteErr != nil {
|
if deleteErr != nil {
|
||||||
glog.Warning("remove volume failed, will ensure fsmeta exists to avoid losing control over volume")
|
|
||||||
if err := client.SetFSMeta(meta); err != nil {
|
|
||||||
glog.Error(err)
|
|
||||||
}
|
|
||||||
return nil, deleteErr
|
return nil, deleteErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +154,7 @@ func (cs *controllerServer) ValidateVolumeCapabilities(ctx context.Context, req
|
||||||
if req.GetVolumeCapabilities() == nil {
|
if req.GetVolumeCapabilities() == nil {
|
||||||
return nil, status.Error(codes.InvalidArgument, "Volume capabilities missing in request")
|
return nil, status.Error(codes.InvalidArgument, "Volume capabilities missing in request")
|
||||||
}
|
}
|
||||||
bucketName, prefix := volumeIDToBucketPrefix(req.GetVolumeId())
|
bucketName, _ := volumeIDToBucketPrefix(req.GetVolumeId())
|
||||||
|
|
||||||
client, err := s3.NewClientFromSecret(req.GetSecrets())
|
client, err := s3.NewClientFromSecret(req.GetSecrets())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -212,11 +170,6 @@ func (cs *controllerServer) ValidateVolumeCapabilities(ctx context.Context, req
|
||||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("bucket of volume with id %s does not exist", req.GetVolumeId()))
|
return nil, status.Error(codes.NotFound, fmt.Sprintf("bucket of volume with id %s does not exist", req.GetVolumeId()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := client.GetFSMeta(bucketName, prefix); err != nil {
|
|
||||||
// return an error if the fsmeta of the requested volume does not exist
|
|
||||||
return nil, status.Error(codes.NotFound, fmt.Sprintf("fsmeta of volume with id %s does not exist", req.GetVolumeId()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// We currently only support RWO
|
// We currently only support RWO
|
||||||
supportedAccessMode := &csi.VolumeCapability_AccessMode{
|
supportedAccessMode := &csi.VolumeCapability_AccessMode{
|
||||||
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
|
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
|
||||||
|
|
|
@ -19,6 +19,8 @@ package driver
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/ctrox/csi-s3/pkg/mounter"
|
"github.com/ctrox/csi-s3/pkg/mounter"
|
||||||
"github.com/ctrox/csi-s3/pkg/s3"
|
"github.com/ctrox/csi-s3/pkg/s3"
|
||||||
|
@ -37,6 +39,31 @@ type nodeServer struct {
|
||||||
*csicommon.DefaultNodeServer
|
*csicommon.DefaultNodeServer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMeta(bucketName, prefix string, context map[string]string) *s3.FSMeta {
|
||||||
|
mountOptions := make([]string, 0)
|
||||||
|
mountOptStr := context[mounter.OptionsKey]
|
||||||
|
if mountOptStr != "" {
|
||||||
|
re, _ := regexp.Compile(`([^\s"]+|"([^"\\]+|\\")*")+`)
|
||||||
|
re2, _ := regexp.Compile(`"([^"\\]+|\\")*"`)
|
||||||
|
re3, _ := regexp.Compile(`\\(.)`)
|
||||||
|
for _, opt := range re.FindAll([]byte(mountOptStr), -1) {
|
||||||
|
// Unquote options
|
||||||
|
opt = re2.ReplaceAllFunc(opt, func(q []byte) []byte {
|
||||||
|
return re3.ReplaceAll(q[1 : len(q)-1], []byte("$1"))
|
||||||
|
})
|
||||||
|
mountOptions = append(mountOptions, string(opt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
capacity, _ := strconv.ParseInt(context["capacity"], 10, 64)
|
||||||
|
return &s3.FSMeta{
|
||||||
|
BucketName: bucketName,
|
||||||
|
Prefix: prefix,
|
||||||
|
Mounter: context[mounter.TypeKey],
|
||||||
|
MountOptions: mountOptions,
|
||||||
|
CapacityBytes: capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
|
func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
|
||||||
volumeID := req.GetVolumeId()
|
volumeID := req.GetVolumeId()
|
||||||
targetPath := req.GetTargetPath()
|
targetPath := req.GetTargetPath()
|
||||||
|
@ -65,29 +92,21 @@ func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
|
||||||
return &csi.NodePublishVolumeResponse{}, nil
|
return &csi.NodePublishVolumeResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceID := ""
|
|
||||||
if req.GetPublishContext() != nil {
|
|
||||||
deviceID = req.GetPublishContext()[deviceID]
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement readOnly & mountFlags
|
// TODO: Implement readOnly & mountFlags
|
||||||
readOnly := req.GetReadonly()
|
readOnly := req.GetReadonly()
|
||||||
// TODO: check if attrib is correct with context.
|
// TODO: check if attrib is correct with context.
|
||||||
attrib := req.GetVolumeContext()
|
attrib := req.GetVolumeContext()
|
||||||
mountFlags := req.GetVolumeCapability().GetMount().GetMountFlags()
|
mountFlags := req.GetVolumeCapability().GetMount().GetMountFlags()
|
||||||
|
|
||||||
glog.V(4).Infof("target %v\ndevice %v\nreadonly %v\nvolumeId %v\nattributes %v\nmountflags %v\n",
|
glog.V(4).Infof("target %v\nreadonly %v\nvolumeId %v\nattributes %v\nmountflags %v\n",
|
||||||
targetPath, deviceID, readOnly, volumeID, attrib, mountFlags)
|
targetPath, readOnly, volumeID, attrib, mountFlags)
|
||||||
|
|
||||||
s3, err := s3.NewClientFromSecret(req.GetSecrets())
|
s3, err := s3.NewClientFromSecret(req.GetSecrets())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize S3 client: %s", err)
|
return nil, fmt.Errorf("failed to initialize S3 client: %s", err)
|
||||||
}
|
}
|
||||||
meta, err := s3.GetFSMeta(bucketName, prefix)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
meta := getMeta(bucketName, prefix, req.VolumeContext)
|
||||||
mounter, err := mounter.New(meta, s3.Config)
|
mounter, err := mounter.New(meta, s3.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -150,10 +169,8 @@ func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize S3 client: %s", err)
|
return nil, fmt.Errorf("failed to initialize S3 client: %s", err)
|
||||||
}
|
}
|
||||||
meta, err := client.GetFSMeta(bucketName, prefix)
|
|
||||||
if err != nil {
|
meta := getMeta(bucketName, prefix, req.VolumeContext)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
mounter, err := mounter.New(meta, client.Config)
|
mounter, err := mounter.New(meta, client.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -3,11 +3,8 @@ package s3
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/minio/minio-go/v7"
|
"github.com/minio/minio-go/v7"
|
||||||
|
@ -221,34 +218,3 @@ func (client *s3Client) removeObjectsOneByOne(bucketName, prefix string) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *s3Client) SetFSMeta(meta *FSMeta) error {
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
json.NewEncoder(b).Encode(meta)
|
|
||||||
opts := minio.PutObjectOptions{ContentType: "application/json"}
|
|
||||||
_, err := client.minio.PutObject(
|
|
||||||
client.ctx, meta.BucketName, path.Join(meta.Prefix, metadataName), b, int64(b.Len()), opts,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *s3Client) GetFSMeta(bucketName, prefix string) (*FSMeta, error) {
|
|
||||||
opts := minio.GetObjectOptions{}
|
|
||||||
obj, err := client.minio.GetObject(client.ctx, bucketName, path.Join(prefix, metadataName), opts)
|
|
||||||
if err != nil {
|
|
||||||
return &FSMeta{}, err
|
|
||||||
}
|
|
||||||
objInfo, err := obj.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return &FSMeta{}, err
|
|
||||||
}
|
|
||||||
b := make([]byte, objInfo.Size)
|
|
||||||
_, err = obj.Read(b)
|
|
||||||
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return &FSMeta{}, err
|
|
||||||
}
|
|
||||||
var meta FSMeta
|
|
||||||
err = json.Unmarshal(b, &meta)
|
|
||||||
return &meta, err
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue