Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions cmd/kw-fixture/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# kw-fixture

```bash
STORAGE_USERS_KITEWORKS_ENDPOINT=<url> \
STORAGE_USERS_KITEWORKS_API_TOKEN=<token> \
go test -mod=mod ./pkg/storage/fs/kiteworks/ -v --ginkgo.focus "smoke"
```

## Step 1

```bash
export STORAGE_USERS_KITEWORKS_ENDPOINT=<url>
export STORAGE_USERS_KITEWORKS_API_TOKEN=<token>

ROOT_ID=$(go run -mod=mod ./cmd/kw-fixture/ setup ocis-test-step1)
echo "created: $ROOT_ID"
go run -mod=mod ./cmd/kw-fixture/ teardown $ROOT_ID && echo "exit: 0"
```

```
created: <uuid>
exit: 0
```

## Step 2

```bash
ROOT_ID=$(go run -mod=mod ./cmd/kw-fixture/ setup ocis-smoke)
DIR_ID=$(go run -mod=mod ./cmd/kw-fixture/ mkdir $ROOT_ID a/b/c)
FILE_ID=$(go run -mod=mod ./cmd/kw-fixture/ upload $ROOT_ID hello.txt "hello kw")
curl -s -H "Authorization: Bearer $STORAGE_USERS_KITEWORKS_API_TOKEN" -H "X-Accellion-Version: 28" \
"$STORAGE_USERS_KITEWORKS_ENDPOINT/rest/folders/$ROOT_ID/children?deleted=false" | jq '[.data[]|{name,type}]'
go run -mod=mod ./cmd/kw-fixture/ teardown $ROOT_ID && echo "exit: 0"
```

```
[{"name":"a","type":"d"},{"name":"hello.txt","type":"f"}]
exit: 0
```

## Step 3 — Go smoke tests

```bash
go test -mod=mod ./pkg/storage/fs/kiteworks/ -v --ginkgo.focus "smoke"
```

```
It staged folder appears in ListStorageSpaces PASSED
It uploaded file content round-trips via Download PASSED
It nested mkdir leaf appears in ListFolder PASSED
```

## Step 4 — Behat kw-backed acceptance tests

Requires a running oCIS instance with `storage-users-kiteworks` using the same env vars.

```bash
cd tests/acceptance
vendor/bin/behat --suite=apiKiteworks --tags=@kw-backed
```

```
3 scenarios (3 passed)
```
87 changes: 87 additions & 0 deletions cmd/kw-fixture/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package main

import (
"fmt"
"os"
"strings"

"github.com/owncloud/reva/v2/pkg/storage/fs/kiteworks/fixture"
)

func env(key string) string {
v := os.Getenv(key)
if v == "" {
fmt.Fprintf(os.Stderr, "error: %s is not set\n", key)
os.Exit(1)
}
return v
}

func mgr() *fixture.Manager {
insecure := strings.ToLower(os.Getenv("STORAGE_USERS_KITEWORKS_INSECURE")) == "true"
return fixture.New(env("STORAGE_USERS_KITEWORKS_ENDPOINT"), env("STORAGE_USERS_KITEWORKS_API_TOKEN"), insecure)
}

func usage() {
fmt.Fprintln(os.Stderr, `usage: kw-fixture <command> [args]

commands:
setup <name> create top-level folder; print ID to stdout
teardown <id> delete folder by ID
mkdir <parent-id> <path> create nested path; print leaf ID to stdout
upload <parent-id> <name> <content> upload file; print file ID to stdout

env: STORAGE_USERS_KITEWORKS_ENDPOINT, STORAGE_USERS_KITEWORKS_API_TOKEN, STORAGE_USERS_KITEWORKS_INSECURE (optional, default false)`)
os.Exit(1)
}

func main() {
if len(os.Args) < 2 {
usage()
}
switch os.Args[1] {
case "setup":
if len(os.Args) != 3 {
usage()
}
id, err := mgr().Setup(os.Args[2])
if err != nil {
fmt.Fprintf(os.Stderr, "setup: %v\n", err)
os.Exit(1)
}
fmt.Println(id)

case "teardown":
if len(os.Args) != 3 {
usage()
}
m := mgr()
m.Track(os.Args[2])
m.Teardown()

case "mkdir":
if len(os.Args) != 4 {
usage()
}
id, err := mgr().MkdirAll(os.Args[2], os.Args[3])
if err != nil {
fmt.Fprintf(os.Stderr, "mkdir: %v\n", err)
os.Exit(1)
}
fmt.Println(id)

case "upload":
if len(os.Args) != 5 {
usage()
}
id, err := mgr().UploadFile(os.Args[2], os.Args[3], []byte(os.Args[4]))
if err != nil {
fmt.Fprintf(os.Stderr, "upload: %v\n", err)
os.Exit(1)
}
fmt.Println(id)

default:
usage()
}
}
137 changes: 85 additions & 52 deletions internal/grpc/services/storageprovider/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,11 @@ func (s *Service) SetLock(ctx context.Context, req *provider.SetLockRequest) (*p
Status: status.NewPermissionDenied(ctx, nil, "no permission to lock the share"),
}, nil
}
res, err := s.Storage.SetLock(ctx, req.Ref, req.Lock)
if err != nil {
return &provider.SetLockResponse{
Status: status.NewStatusFromErrType(ctx, "set lock", err),
}, nil
// non-decomposedfs drivers may return nil result; set SpaceOwner only when present
lockResult, err := s.Storage.SetLock(ctx, req.Ref, req.Lock)
if lockResult != nil {
storagespace.ContextSetSpaceOwner(ctx, lockResult.SpaceOwner)
}
storagespace.ContextSetSpaceOwner(ctx, res.SpaceOwner)

return &provider.SetLockResponse{
Status: status.NewStatusFromErrType(ctx, "set lock", err),
Expand Down Expand Up @@ -285,13 +283,11 @@ func (s *Service) Unlock(ctx context.Context, req *provider.UnlockRequest) (*pro
}, nil
}

res, err := s.Storage.Unlock(ctx, req.Ref, req.Lock)
if err != nil {
return &provider.UnlockResponse{
Status: status.NewStatusFromErrType(ctx, "unlock", err),
}, nil
// non-decomposedfs drivers may return nil result; set SpaceOwner only when present
unlockResult, err := s.Storage.Unlock(ctx, req.Ref, req.Lock)
if unlockResult != nil {
storagespace.ContextSetSpaceOwner(ctx, unlockResult.SpaceOwner)
}
storagespace.ContextSetSpaceOwner(ctx, res.SpaceOwner)

return &provider.UnlockResponse{
Status: status.NewStatusFromErrType(ctx, "unlock", err),
Expand Down Expand Up @@ -625,7 +621,34 @@ func (s *Service) UpdateStorageSpace(ctx context.Context, req *provider.UpdateSt
}

func (s *Service) DeleteStorageSpace(ctx context.Context, req *provider.DeleteStorageSpaceRequest) (*provider.DeleteStorageSpaceResponse, error) {
result, err := s.Storage.DeleteStorageSpace(ctx, req)
// pre-fetch spacename+grants before deletion: non-decomposedfs drivers don't populate DeleteStorageSpaceResult
idraw, _ := storagespace.ParseID(req.Id.GetOpaqueId())
idraw.OpaqueId = idraw.GetSpaceId()
id := &provider.StorageSpaceId{OpaqueId: storagespace.FormatResourceID(&idraw)}

spaces, err := s.Storage.ListStorageSpaces(ctx, []*provider.ListStorageSpacesRequest_Filter{{Type: provider.ListStorageSpacesRequest_Filter_TYPE_ID, Term: &provider.ListStorageSpacesRequest_Filter_Id{Id: id}}}, true)
if err != nil {
var st *rpc.Status
switch err.(type) {
case errtypes.IsNotFound:
st = status.NewNotFound(ctx, "space not found")
case errtypes.PermissionDenied:
st = status.NewPermissionDenied(ctx, err, "permission denied")
case errtypes.BadRequest:
st = status.NewInvalid(ctx, err.Error())
default:
st = status.NewInternal(ctx, "error deleting space: "+req.Id.String())
}
return &provider.DeleteStorageSpaceResponse{
Status: st,
}, nil
} else if len(spaces) != 1 {
return &provider.DeleteStorageSpaceResponse{
Status: status.NewNotFound(ctx, "space not found"),
}, nil
}

deleteSpaceResult, err := s.Storage.DeleteStorageSpace(ctx, req)
if err != nil {
var st *rpc.Status
switch err.(type) {
Expand All @@ -648,14 +671,13 @@ func (s *Service) DeleteStorageSpace(ctx context.Context, req *provider.DeleteSt
Status: st,
}, nil
}

if result != nil {
storagespace.ContextSetDeleteStorageSpaceResult(ctx, result)
if deleteSpaceResult == nil {
// driver didn't populate the result; fill SpaceName from the pre-fetched space so SpaceDeleted event is not empty
deleteSpaceResult = &storage.DeleteStorageSpaceResult{SpaceName: spaces[0].GetName()}
}
storagespace.ContextSetDeleteStorageSpaceResult(ctx, deleteSpaceResult)

return &provider.DeleteStorageSpaceResponse{
Status: status.NewOK(ctx),
}, nil
return &provider.DeleteStorageSpaceResponse{Status: status.NewOK(ctx)}, nil
}

func (s *Service) CreateContainer(ctx context.Context, req *provider.CreateContainerRequest) (*provider.CreateContainerResponse, error) {
Expand All @@ -666,13 +688,10 @@ func (s *Service) CreateContainer(ctx context.Context, req *provider.CreateConta
}
}

res, err := s.Storage.CreateDir(ctx, req.Ref)
if err != nil {
return &provider.CreateContainerResponse{
Status: status.NewStatusFromErrType(ctx, "create container", err),
}, nil
createDirResult, err := s.Storage.CreateDir(ctx, req.Ref)
if createDirResult != nil {
storagespace.ContextSetSpaceOwner(ctx, createDirResult.SpaceOwner)
}
storagespace.ContextSetSpaceOwner(ctx, res.SpaceOwner)

return &provider.CreateContainerResponse{
Status: status.NewStatusFromErrType(ctx, "create container", err),
Expand All @@ -689,13 +708,10 @@ func (s *Service) TouchFile(ctx context.Context, req *provider.TouchFileRequest)
mtime = utils.ReadPlainFromOpaque(req.Opaque, "X-OC-Mtime")
}

res, err := s.Storage.TouchFile(ctx, req.Ref, utils.ExistsInOpaque(req.Opaque, "markprocessing"), mtime)
if err != nil {
return &provider.TouchFileResponse{
Status: status.NewStatusFromErrType(ctx, "touch file", err),
}, nil
touchResult, err := s.Storage.TouchFile(ctx, req.Ref, utils.ExistsInOpaque(req.Opaque, "markprocessing"), mtime)
if touchResult != nil {
storagespace.ContextSetSpaceOwner(ctx, touchResult.SpaceOwner)
}
storagespace.ContextSetSpaceOwner(ctx, res.SpaceOwner)

return &provider.TouchFileResponse{
Status: status.NewStatusFromErrType(ctx, "touch file", err),
Expand All @@ -720,24 +736,46 @@ func (s *Service) Delete(ctx context.Context, req *provider.DeleteRequest) (*pro
}
}

result, err := s.Storage.Delete(ctx, req.Ref)
md, err := s.Storage.GetMD(ctx, req.Ref, []string{}, []string{"id", "status"})
if err != nil {
return &provider.DeleteResponse{
Status: status.NewStatusFromErrType(ctx, "can't stat resource to delete", err),
}, nil
}

if err == nil && result != nil {
storagespace.ContextSetDeleteResult(ctx, result)
if utils.ReadPlainFromOpaque(md.GetOpaque(), "status") == "processing" {
return &provider.DeleteResponse{
Status: &rpc.Status{
Code: rpc.Code_CODE_TOO_EARLY,
Message: "file is processing",
},
Opaque: &typesv1beta1.Opaque{
Map: map[string]*typesv1beta1.OpaqueEntry{
"status": {Decoder: "plain", Value: []byte("processing")},
},
},
}, nil
}

deleteResult, err := s.Storage.Delete(ctx, req.Ref)
storagespace.ContextSetDeleteResult(ctx, deleteResult)

return &provider.DeleteResponse{
Status: status.NewStatusFromErrType(ctx, "delete", err),
Opaque: &typesv1beta1.Opaque{
Map: map[string]*typesv1beta1.OpaqueEntry{
"opaque_id": {Decoder: "plain", Value: []byte(md.Id.OpaqueId)},
},
},
}, nil
}

func (s *Service) Move(ctx context.Context, req *provider.MoveRequest) (*provider.MoveResponse, error) {
ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)

result, err := s.Storage.Move(ctx, req.Source, req.Destination)
if err == nil && result != nil {
storagespace.ContextSetMoveResult(ctx, result)
}
moveResult, err := s.Storage.Move(ctx, req.Source, req.Destination)
storagespace.ContextSetMoveResult(ctx, moveResult)

return &provider.MoveResponse{
Status: status.NewStatusFromErrType(ctx, "move", err),
}, nil
Expand Down Expand Up @@ -849,13 +887,10 @@ func (s *Service) ListFileVersions(ctx context.Context, req *provider.ListFileVe
func (s *Service) RestoreFileVersion(ctx context.Context, req *provider.RestoreFileVersionRequest) (*provider.RestoreFileVersionResponse, error) {
ctx = ctxpkg.ContextSetLockID(ctx, req.LockId)

res, err := s.Storage.RestoreRevision(ctx, req.Ref, req.Key)
if err != nil {
return &provider.RestoreFileVersionResponse{
Status: status.NewStatusFromErrType(ctx, "restore file version", err),
}, nil
restoreRevResult, err := s.Storage.RestoreRevision(ctx, req.Ref, req.Key)
if restoreRevResult != nil {
storagespace.ContextSetSpaceOwner(ctx, restoreRevResult.SpaceOwner)
}
storagespace.ContextSetSpaceOwner(ctx, res.SpaceOwner)

return &provider.RestoreFileVersionResponse{
Status: status.NewStatusFromErrType(ctx, "restore file version", err),
Expand Down Expand Up @@ -954,17 +989,15 @@ func (s *Service) RestoreRecycleItem(ctx context.Context, req *provider.RestoreR

// TODO(labkode): CRITICAL: fill recycle info with storage provider.
key, relativePath := splitKeyAndPath(req.GetKey())
writeRes, err := s.Storage.RestoreRecycleItem(ctx, req.Ref, key, relativePath, req.RestoreRef)
if err != nil {
return &provider.RestoreRecycleItemResponse{
Status: status.NewStatusFromErrType(ctx, "restore recycle item", err),
}, nil
restoreItemResult, err := s.Storage.RestoreRecycleItem(ctx, req.Ref, key, relativePath, req.RestoreRef)
if restoreItemResult != nil {
storagespace.ContextSetSpaceOwner(ctx, restoreItemResult.SpaceOwner)
}
storagespace.ContextSetSpaceOwner(ctx, writeRes.SpaceOwner)

return &provider.RestoreRecycleItemResponse{
res := &provider.RestoreRecycleItemResponse{
Status: status.NewStatusFromErrType(ctx, "restore recycle item", err),
}, nil
}
return res, nil
}

func (s *Service) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRequest) (*provider.PurgeRecycleResponse, error) {
Expand Down
Loading
Loading