Merge pull request #2889 from rhafer/reva-655

Update tests for opencloud-eu/reva#655
This commit is contained in:
Ralf Haferkamp
2026-06-25 16:55:18 +02:00
committed by GitHub
25 changed files with 485 additions and 66 deletions

6
go.mod
View File

@@ -59,7 +59,7 @@ require (
github.com/nats-io/nats.go v1.52.0
github.com/olekukonko/tablewriter v1.1.4
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.28.3
github.com/onsi/ginkgo/v2 v2.31.0
github.com/onsi/gomega v1.42.1
github.com/open-policy-agent/opa v1.17.1
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
@@ -163,7 +163,7 @@ require (
github.com/bombsimon/logrusr/v3 v3.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/ceph/go-ceph v0.39.0 // indirect
github.com/ceph/go-ceph v0.40.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 // indirect
github.com/clipperhouse/displaywidth v0.10.0 // indirect
@@ -412,3 +412,5 @@ replace go-micro.dev/v4 => github.com/butonic/go-micro/v4 v4.11.1-0.202411151126
exclude github.com/mattn/go-sqlite3 v2.0.3+incompatible
replace github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a
replace github.com/opencloud-eu/reva/v2 => github.com/michaelstingl/opencloud-eu-reva/v2 v2.46.1-0.20260625115217-4e671c0ed52f

14
go.sum
View File

@@ -209,8 +209,8 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1x
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/ceph/go-ceph v0.39.0 h1:fzINuBItJqhmTtnC4/iiTY+ONtsOqV7W+B/3xTS/DsY=
github.com/ceph/go-ceph v0.39.0/go.mod h1:UId58dqtDKTwnv3OY8rdpC+Ulz/AVpcvZqjXDICcd5c=
github.com/ceph/go-ceph v0.40.0 h1:Wz9WOX6i73Hz74mpwhTO9S6IyX3eFPv88VUc7FRMPRk=
github.com/ceph/go-ceph v0.40.0/go.mod h1:1oFtT/x/4y+teLsNiogdd/Kj81Gmrw6JM5w1bWnZoc4=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -832,6 +832,8 @@ github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103 h1:Z/i1e+gTZrmcGeZy
github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103/go.mod h1:o9YPB5aGP8ob35Vy6+vyq3P3bWe7NQWzf+JLiXCiMaE=
github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE=
github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A=
github.com/michaelstingl/opencloud-eu-reva/v2 v2.46.1-0.20260625115217-4e671c0ed52f h1:e/gFWEUXTKf5v4SAxqa3IEAXm+ikjT+3iKUlH2FZ2uA=
github.com/michaelstingl/opencloud-eu-reva/v2 v2.46.1-0.20260625115217-4e671c0ed52f/go.mod h1:l4CG5u8tdWZj6IyavXO9xDdnNTV85oaFudGwEDPvsnk=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
@@ -934,8 +936,8 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.28.3 h1:4JvMdwtFU0imd8fHx25OJXoDMRexnf8v5NHKYSTTji4=
github.com/onsi/ginkgo/v2 v2.28.3/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44=
github.com/onsi/ginkgo/v2 v2.31.0 h1:GtuJos5DFUV9EerYJo8RhYxosYNGvOdDE5haKq6Grfs=
github.com/onsi/ginkgo/v2 v2.31.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
@@ -949,8 +951,6 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d h1:JcqGDiyrcaQwVyV861TUyQgO7uEmsjkhfm7aQd84dOw=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
github.com/opencloud-eu/reva/v2 v2.46.4-0.20260618143111-1bf72cb76394 h1:A8SlnvYAKap8edTFnxsnvt7TPfA2WtWUFupPN4WBRnU=
github.com/opencloud-eu/reva/v2 v2.46.4-0.20260618143111-1bf72cb76394/go.mod h1:z3cZwP9nHkvyyZgrG56MvxSUkqTF7DTlcGJzJfkyNlI=
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI=
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -987,6 +987,8 @@ github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJ
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/xxHash v0.1.5 h1:n/jBpwTHiER4xYvK3/CdPVnLDPchj8eTJFFLUb4QHBo=
github.com/pierrec/xxHash v0.1.5/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I=
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

View File

@@ -154,28 +154,28 @@ Feature: upload file
| Upload-Length | 100 |
| Upload-Metadata | filename <metadata> |
| Tus-Resumable | 1.0.0 |
Then the HTTP status code should be "412"
Then the HTTP status code should be "<http-status-code>"
And the following headers should not be set
| header |
| Location |
And as "Alice" file <file-name> should not exist
Examples:
| dav-path-version | file-name | metadata |
| old | " " | IA== |
| old | "filewithLF-and-CR\r\n" | ZmlsZXdpdGhMRi1hbmQtQ1INCgo= |
| old | "folder/file" | Zm9sZGVyL2ZpbGU= |
| old | "my\\file" | bXkMaWxl |
| old | ".." | Li4= |
| new | " " | IA== |
| new | "filewithLF-and-CR\r\n" | ZmlsZXdpdGhMRi1hbmQtQ1INCgo= |
| new | "folder/file" | Zm9sZGVyL2ZpbGU= |
| new | "my\\file" | bXkMaWxl |
| new | ".." | Li4= |
| spaces | " " | IA== |
| spaces | "filewithLF-and-CR\r\n" | ZmlsZXdpdGhMRi1hbmQtQ1INCgo= |
| spaces | "folder/file" | Zm9sZGVyL2ZpbGU= |
| spaces | "my\\file" | bXkMaWxl |
| spaces | ".." | Li4= |
| dav-path-version | file-name | metadata | http-status-code |
| old | " " | IA== | 400 |
| old | "filewithLF-and-CR\r\n" | ZmlsZXdpdGhMRi1hbmQtQ1INCgo= | 400 |
| old | "folder/file" | Zm9sZGVyL2ZpbGU= | 412 |
| old | "my\\file" | bXkMaWxl | 400 |
| old | ".." | Li4= | 400 |
| new | " " | IA== | 400 |
| new | "filewithLF-and-CR\r\n" | ZmlsZXdpdGhMRi1hbmQtQ1INCgo= | 400 |
| new | "folder/file" | Zm9sZGVyL2ZpbGU= | 412 |
| new | "my\\file" | bXkMaWxl | 400 |
| new | ".." | Li4= | 400 |
| spaces | " " | IA== | 400 |
| spaces | "filewithLF-and-CR\r\n" | ZmlsZXdpdGhMRi1hbmQtQ1INCgo= | 400 |
| spaces | "folder/file" | Zm9sZGVyL2ZpbGU= | 412 |
| spaces | "my\\file" | bXkMaWxl | 400 |
| spaces | ".." | Li4= | 400 |
@issue-10346
Scenario Outline: upload a zero-byte file

View File

@@ -0,0 +1,50 @@
//go:build ceph_preview
package admin
// SubVolumeGroupInfo reports various informational values about a subvolume group.
type SubVolumeGroupInfo struct {
Uid int `json:"uid"`
Gid int `json:"gid"`
Mode int `json:"mode"`
BytesPercent string `json:"bytes_pcent"`
BytesUsed ByteCount `json:"bytes_used"`
BytesQuota QuotaSize `json:"-"`
DataPool string `json:"data_pool"`
Atime TimeStamp `json:"atime"`
Mtime TimeStamp `json:"mtime"`
Ctime TimeStamp `json:"ctime"`
CreatedAt TimeStamp `json:"created_at"`
MonAddrs []string `json:"mon_addrs"`
}
type subVolumeGroupInfoWrapper struct {
SubVolumeGroupInfo
VBytesQuota *quotaSizePlaceholder `json:"bytes_quota"`
}
func parseSubVolumeGroupInfo(res response) (*SubVolumeGroupInfo, error) {
var info subVolumeGroupInfoWrapper
if err := res.NoStatus().Unmarshal(&info).End(); err != nil {
return nil, err
}
if info.VBytesQuota != nil {
info.BytesQuota = info.VBytesQuota.Value
}
return &info.SubVolumeGroupInfo, nil
}
// SubVolumeGroupInfo returns information about the specified subvolume group.
//
// Similar To:
//
// ceph fs subvolumegroup info <volume> <group_name>
func (fsa *FSAdmin) SubVolumeGroupInfo(volume, name string) (*SubVolumeGroupInfo, error) {
res := fsa.marshalMgrCommand(map[string]string{
"prefix": "fs subvolumegroup info",
"vol_name": volume,
"group_name": name,
"format": "json",
})
return parseSubVolumeGroupInfo(res)
}

View File

@@ -1,5 +1,3 @@
//go:build ceph_preview
package cephfs
/*

View File

@@ -147,13 +147,22 @@ func OpenSnapDiff(config SnapDiffConfig) (*SnapDiffInfo, error) {
rawCephSnapDiffInfo := &C._ceph_snapdiff_info{}
cRootPath := C.CString(config.RootPath)
defer C.free(unsafe.Pointer(cRootPath))
cRelPath := C.CString(config.RelPath)
defer C.free(unsafe.Pointer(cRelPath))
cSnap1 := C.CString(config.Snap1)
defer C.free(unsafe.Pointer(cSnap1))
cSnap2 := C.CString(config.Snap2)
defer C.free(unsafe.Pointer(cSnap2))
ret := C.open_snapdiff_dlsym(
cephOpenSnapDiff,
config.CMount.mount,
C.CString(config.RootPath),
C.CString(config.RelPath),
C.CString(config.Snap1),
C.CString(config.Snap2),
cRootPath,
cRelPath,
cSnap1,
cSnap2,
rawCephSnapDiffInfo)
if ret != 0 {

86
vendor/github.com/ceph/go-ceph/rados/ioctx_checksum.go generated vendored Normal file
View File

@@ -0,0 +1,86 @@
//go:build ceph_preview
package rados
/*
#cgo LDFLAGS: -lrados
#include <stdlib.h>
#include <rados/librados.h>
*/
import "C"
import "unsafe"
// Checksum calculates the checksum of the given object data, using one of the supported checksum algorithms.
//
// Implements:
//
// int rados_checksum(rados_ioctx_t io,
// const char *oid,
// rados_checksum_type_t type,
// const char *init_value,
// size_t init_value_len,
// size_t len,
// uint64_t off,
// size_t chunk_size,
// char *pchecksum,
// size_t checksum_len);
func (ioctx *IOContext) Checksum(oid string, checksumType ChecksumType, dst []byte, opts *ChecksumOptions) error {
// apply defaults
if opts == nil {
opts = &ChecksumOptions{}
}
if opts.InitValue == nil {
initLen := 4
if checksumType == ChecksumTypeXXHash64 {
initLen = 8
}
opts.InitValue = make([]byte, initLen)
}
// call library
coid := C.CString(oid)
defer C.free(unsafe.Pointer(coid))
return getError(C.rados_checksum(
ioctx.ioctx,
coid,
C.rados_checksum_type_t(checksumType),
(*C.char)(unsafe.Pointer(&opts.InitValue[0])),
C.size_t(len(opts.InitValue)),
C.size_t(opts.Len),
C.uint64_t(opts.Off),
C.size_t(opts.ChunkSize),
(*C.char)(unsafe.Pointer(&dst[0])),
C.size_t(len(dst)),
))
}
// ChecksumType indicates checksum algorithm types supported by the IOContext.Checksum method.
// Equivalent to the rados_checksum_type_t enum.
type ChecksumType uint32
const (
// ChecksumTypeXXHash32 produces an encoded le32 checksum of the given object.
ChecksumTypeXXHash32 = ChecksumType(C.LIBRADOS_CHECKSUM_TYPE_XXHASH32)
// ChecksumTypeXXHash64 produces an encoded le64 checksum of the given object.
ChecksumTypeXXHash64 = ChecksumType(C.LIBRADOS_CHECKSUM_TYPE_XXHASH64)
// ChecksumTypeCRC32C produces an encoded le32 checksum of the given object.
ChecksumTypeCRC32C = ChecksumType(C.LIBRADOS_CHECKSUM_TYPE_CRC32C)
)
// ChecksumOptions exposes non-required parameters for the Checksum method.
type ChecksumOptions struct {
// Off sets the object offset to start checksumming in the object.
// By default, the entire object will be checksummed.
Off uint64
// Len sets the the number of bytes to checksum in the object.
// By default, the entire object will be checksummed.
Len uint64
// ChunkSize sets the length-aligned chunk size for the checksum calculation.
// By default, the entire object will be checksummed as a single chunk.
ChunkSize uint64
// InitValue sets the initial value for the checksum calculation.
// By default, the initial value will be a zeroed-out byte slice.
InitValue []byte
}

View File

@@ -1,6 +1,7 @@
.DS_Store
TODO
tmp/**/*
integration/tmp_*/
*.coverprofile
.vscode
.idea/

View File

@@ -1,3 +1,29 @@
## 2.31.0
Add a bunch of Claude Skills via the marketplace:
```
/plugin marketplace add onsi/ginkgo
/plugin install ginkgo@ginkgo
```
## 2.30.0
### Features
Ginkgo now allows `extentions/global.Reset` to support running multiple suites from within a single process. This may take some massaging on your part (see [1672](https://github.com/onsi/ginkgo/issues/1672)) but can dramatically speed up codebases with O(hundreds) of test suites.
Thanks @lawrencejones !
### Fixes
- Fix nested --github-output group for progress report nested inside timeline [4f62d7a]
## 2.29.0
`GinkgoHelperGo` makes it easier to write test helpers that need to run in goroutines. Specifically, it makes managing the failure state and capturing failure panics correctly straightforward.
`ginkgo outline` now includes entries defined in `DescribeTableSubtree`
## 2.28.3
### Maintenance

View File

@@ -106,6 +106,19 @@ And that's just Ginkgo! [Gomega](https://onsi.github.io/gomega/) brings a rich,
Happy Testing!
## Using Ginkgo with Claude Code
Ginkgo ships a set of [Claude Code](https://claude.com/claude-code) skills as a plugin, with this repo doubling as the marketplace, so an agent writing specs in *your* project has Ginkgo's idioms, decorators, and gotchas on hand. From inside Claude Code:
```
/plugin marketplace add onsi/ginkgo
/plugin install ginkgo@ginkgo
```
(or non-interactively: `claude plugin marketplace add onsi/ginkgo` then `claude plugin install ginkgo@ginkgo`)
This installs a family of `ginkgo:*` skills that activate automatically while you write and run specs. Start with `ginkgo:overview`; see the [plugin README](plugins/ginkgo/README.md) for the full list.
## License
Ginkgo is MIT-Licensed

View File

@@ -39,7 +39,6 @@ var flagSet types.GinkgoFlagSet
var deprecationTracker = types.NewDeprecationTracker()
var suiteConfig = types.NewDefaultSuiteConfig()
var reporterConfig = types.NewDefaultReporterConfig()
var suiteDidRun = false
var outputInterceptor internal.OutputInterceptor
var client parallel_support.Client
@@ -259,10 +258,10 @@ for more on how specs are parallelized in Ginkgo.
You can also pass suite-level Label() decorators to RunSpecs. The passed-in labels will apply to all specs in the suite.
*/
func RunSpecs(t GinkgoTestingT, description string, args ...any) bool {
if suiteDidRun {
if global.SuiteDidRun {
exitIfErr(types.GinkgoErrors.RerunningSuite())
}
suiteDidRun = true
global.SuiteDidRun = true
err := global.PushClone()
if err != nil {
exitIfErr(err)

View File

@@ -163,17 +163,17 @@ func ginkgoNodeFromCallExpr(fset *token.FileSet, ce *ast.CallExpr, ginkgoPackage
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
n.Labels = labelFromCallExpr(ce)
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
case "Context", "Describe", "When", "DescribeTable":
case "Context", "Describe", "When", "DescribeTable", "DescribeTableSubtree":
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
n.Labels = labelFromCallExpr(ce)
n.Pending = pendingFromCallExpr(ce)
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
case "FContext", "FDescribe", "FWhen", "FDescribeTable":
case "FContext", "FDescribe", "FWhen", "FDescribeTable", "FDescribeTableSubtree":
n.Focused = true
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
n.Labels = labelFromCallExpr(ce)
return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
case "PContext", "PDescribe", "PWhen", "XContext", "XDescribe", "XWhen", "PDescribeTable", "XDescribeTable":
case "PContext", "PDescribe", "PWhen", "XContext", "XDescribe", "XWhen", "PDescribeTable", "XDescribeTable", "PDescribeTableSubtree", "XDescribeTableSubtree":
n.Pending = true
n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
n.Labels = labelFromCallExpr(ce)

View File

@@ -54,6 +54,7 @@ func FromASTFile(fset *token.FileSet, src *ast.File) (*outline, error) {
// Node is not a Ginkgo spec or container, so it was not pushed onto the stack, continue
return true
}
expandSubtree(lastVisitedGinkgoNode)
stack = stack[0 : len(stack)-1]
return true
})
@@ -128,3 +129,29 @@ func (o *outline) StringIndent(width int) string {
return b.String()
}
// expandSubtree restructures a DescribeTableSubtree node so that each Entry
// child gets a copy of the subtree's spec nodes as its children. This mirrors
// the runtime behavior where each Entry generates a container with the specs
// defined in the DescribeTableSubtree body.
func expandSubtree(gn *ginkgoNode) {
if !strings.Contains(gn.Name, "DescribeTableSubtree") {
return
}
subNodes, entries := splitSubtreeSubnodes(gn.Nodes)
gn.Nodes = entries
for _, entry := range entries {
entry.Nodes = subNodes
}
}
// splitSubtreeSubnodes splits the child nodes of a DescribeTableSubtree into
// spec/container nodes (defined in the body) and Entry nodes.
func splitSubtreeSubnodes(nodes []*ginkgoNode) ([]*ginkgoNode, []*ginkgoNode) {
for i, node := range nodes {
if strings.Contains(node.Name, "Entry") {
return nodes[:i], nodes[i:]
}
}
return nodes, nil
}

143
vendor/github.com/onsi/ginkgo/v2/helpergo_dsl.go generated vendored Normal file
View File

@@ -0,0 +1,143 @@
package ginkgo
import (
"github.com/onsi/ginkgo/v2/internal/global"
ginkgotypes "github.com/onsi/ginkgo/v2/types"
)
// GinkgoHelperGo synchronously calls the specified “helper” function in a new
// go routine and with a “defer GinkgoRecover()” already in place, passing the
// function a “helper Fail”. GinkgoHelperGo is typically called from custom test
// helpers that in turn need to synchronously execute caller-supplied custom
// test code in a new Go routine while waiting for this new Go routine to
// terminate (either successfully or failing).
//
// GinkgoHelperGo hides the non-trivial details of correctly unblocking the
// caller's waiting go routine as well as reporting the correct call sites,
// depending on whether the test helper failed, or the caller-supplied function
// had its assertions failing or panicked.
//
// Let's take the following example of a test helper named “EnsureSprockets”
// that runs a set of caller-supplied assertions synchronously on a new Go
// routine and waits for the outcome before returning to the caller of the test
// helper. This is just using Ginkgo:
//
// func EnsureSprockets(sprockets int, assertions func()) {
// GinkgoHelper()
// GinkgoHelperGo(func(helperFail func(string, ...int)) {
// if sprockets == 0 {
// helperFail("sprockets must not be zero")
// }
// assertions()
// })
// }
//
// And now for an example that additionally uses Gomega assertions.
//
// func EnsureSprockets(sprockets int, assertions func()) {
// GinkgoHelper()
// GinkgoHelperGo(func(helperFail func(string, ...int)) {
// g := gomega.NewGomega(helperFail)
// g.Expect(sprockets).Not(BeZero())
// assertions()
// })
// }
//
// The called helper function should make any custom helper-related assertions
// using the passed “helper Fail”. Gomega users will want to create a new Gomega
// wired into this helper Fail. It is expected for the helper function at some
// point to call into a user-supplied function that might contain its own
// assertions. In the example above, that would be the function passed as
// assertions.
//
// Any failing assertion using the helper Gomega in the helper function will be
// reported as a fail at the call site of GinkgoHelperGo. Preferably, only
// custom test helpers call GinkgoHelperGo and thus mark themselves as
// [GinkgoHelper] also: in this case, the fail will be shown at the call site of
// the custom test helper.
//
// Any other failing assertions inside the caller-supplied custom test code and
// thus inside the helper function will instead be reported at the location of
// the failed assertion.
//
// If the caller-supplied custom test code panics, GinkgoHelperGo will fail at
// its call site, or at the call site of the custom test helper if it uses
// GinkgoHelper, reporting the usual stack trace for the panic, as a plain
// GinkgoRecover would also do.
//
// Important: the Gomega passed to the called function must only be used in
// assertions belonging to the test helper, but not any user test code called
// from the test helper. Thus, do not pass the Gomega passed to the helper
// function further on to any user test code functions.
func GinkgoHelperGo(fn func(fail func(message string, callerSkip ...int))) {
// userPanicked signals that the called user code panicked, such as due to a
// failed Gomega assertion.
type userPanicked struct{}
// helperPanicked signals that some helper code assertion panicked in the
// separate Go routine and we are expected to Fail the current test with that
// reason, but on the caller's Go routine.
type helperPanicked string
GinkgoHelper()
// possible types of values sent over the result channel:
// - nil (untyped): no problem at all, proceed.
// - helperPanicked: the message with which to (re)fail in the caller's
// go routine.
// - userPanicked: indication to (also) fail on the caller's go routine;
// the message doesn't matter as the user code fail takes precedence.
ch := make(chan any)
go func() {
isHelperPanic := false
helperFail := func(message string, callerSkip ...int) {
isHelperPanic = true
Fail(message, callerSkip...)
}
// Please note that we cannot simply recover a helper panic before
// GinkgoRecover kicks in as then GinkgoRecover would always report the
// stack trace only from the place of rethrown panic ... and that's
// pretty useless, because it would just consist of the panic rethrow.
defer func() {
// We need to unblock and immediately fail the waiting caller's
// go routine either for a reason, or just "because" when
// GinkgoRecover has already failed the current test on the
// separate go routine.
if global.Failer.GetState() != ginkgotypes.SpecStatePassed {
if isHelperPanic {
_, failure := global.Failer.Drain()
ch <- helperPanicked(failure.Message)
} else {
// keep the panic failure already recorded by GinkgoRecover.
ch <- userPanicked{}
}
}
close(ch) // causes a nil in case there were no panics anywhere.
}()
// Nota bene: GinkgoRecover always eats any user panic and channel the
// panic value into Ginkgo's Failer.Panic(). We can peek at the last
// failure recorded, which should be nil if GinkgoRecover didn't swallow
// a user code panic. The "problem" with GinkgoRecover is that it turns
// any panic value into a string message, so we loose any specific
// typing.
defer GinkgoRecover()
fn(helperFail)
}()
// Did we run into trouble?
switch v := (<-ch).(type) {
case userPanicked:
// The message actually is irrelevant, as it comes only second to
// the already registered user panic message. We just need Fail to
// panic on the caller's go routine in order to unblock the test.
Fail("fn panicked", 1)
case helperPanicked:
// Report the failure on the new go routine instead on the caller's go
// routine.
Fail(string(v), 1)
default:
// It's all fine!
}
}

View File

@@ -8,6 +8,12 @@ var Suite *internal.Suite
var Failer *internal.Failer
var backupSuite *internal.Suite
// SuiteDidRun tracks whether RunSpecs has already been invoked for the current global
// suite. It lives here (rather than in package ginkgo) so that InitializeGlobals can
// clear it, allowing extensions/globals.Reset to support running multiple suites
// sequentially in a single process.
var SuiteDidRun bool
func init() {
InitializeGlobals()
}
@@ -15,6 +21,7 @@ func init() {
func InitializeGlobals() {
Failer = internal.NewFailer()
Suite = internal.NewSuite()
SuiteDidRun = false
}
func PushClone() error {

View File

@@ -423,7 +423,7 @@ func (r *DefaultReporter) emitTimeline(indent uint, report types.SpecReport, tim
case types.ReportEntry:
r.emitReportEntry(indent, x)
case types.ProgressReport:
r.emitProgressReport(indent, false, isVeryVerbose, x)
r.emitProgressReport(indent, isVeryVerbose, false, x)
case types.SpecEvent:
if isVeryVerbose || !x.IsOnlyVisibleAtVeryVerbose() || r.conf.ShowNodeEvents {
r.emitSpecEvent(indent, x, isVeryVerbose)
@@ -533,6 +533,7 @@ func (r *DefaultReporter) emitProgressReport(indent uint, emitGinkgoWriterOutput
indent -= 1
}
// Emit only top-level groups because github logging cannot handle nested groups correctly.
if r.conf.GithubOutput && emitGroup {
r.emitBlock(r.fi(indent, "::group::Progress Report"))
}

View File

@@ -1,3 +1,3 @@
package types
const VERSION = "2.28.3"
const VERSION = "2.31.0"

View File

@@ -107,7 +107,9 @@ func (s *svc) handleTusPost(ctx context.Context, w http.ResponseWriter, r *http.
return
}
if err := ValidateName(filename(meta["filename"]), s.nameValidators); err != nil {
w.WriteHeader(http.StatusPreconditionFailed)
w.WriteHeader(http.StatusBadRequest)
b, err := errors.Marshal(http.StatusBadRequest, err.Error(), "", "")
errors.HandleWebdavError(&log, w, b, err)
return
}

View File

@@ -504,6 +504,11 @@ func (b HybridBackend) LockfilePath(n MetadataNode) string {
// Lock locks the metadata for the given path
func (b HybridBackend) Lock(n MetadataNode) (UnlockFunc, error) {
f, _, err := b.LockAndRead(n)
return f, err
}
func (b HybridBackend) LockAndRead(n MetadataNode) (UnlockFunc, io.Reader, error) {
metaLockPath := b.LockfilePath(n)
mlock, err := lockedfile.OpenFile(metaLockPath, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
@@ -511,20 +516,20 @@ func (b HybridBackend) Lock(n MetadataNode) (UnlockFunc, error) {
// create the parent directory
err = os.MkdirAll(filepath.Dir(metaLockPath), 0700)
if err != nil {
return nil, err
return nil, nil, err
}
mlock, err = lockedfile.OpenFile(metaLockPath, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
return nil, err
return nil, nil, err
}
} else {
return nil, err
return nil, nil, err
}
}
return func() error {
// Warning: do not remove the lockfile or we may lock the same file more than once, https://github.com/opencloud-eu/opencloud/issues/1793
return mlock.Close()
}, nil
}, mlock, nil
}
func (b HybridBackend) cacheKey(n MetadataNode) string {

View File

@@ -325,6 +325,20 @@ func (b MessagePackBackend) Lock(n MetadataNode) (UnlockFunc, error) {
}, nil
}
func (b MessagePackBackend) LockAndRead(n MetadataNode) (UnlockFunc, io.Reader, error) {
unlock, err := b.Lock(n)
if err != nil {
return nil, nil, err
}
f, err := os.Open(b.MetadataPath(n))
if err != nil {
_ = unlock()
return nil, nil, err
}
return unlock, f, nil
}
func (b MessagePackBackend) cacheKey(n MetadataNode) string {
return n.GetSpaceID() + "/" + n.GetID()
}

View File

@@ -59,6 +59,7 @@ type Backend interface {
Remove(ctx context.Context, n MetadataNode, key string, acquireLock bool) error
Lock(n MetadataNode) (UnlockFunc, error)
LockAndRead(n MetadataNode) (UnlockFunc, io.Reader, error)
Purge(ctx context.Context, n MetadataNode) error
Rename(oldNode, newNode MetadataNode) error
MetadataPath(n MetadataNode) string
@@ -113,6 +114,11 @@ func (NullBackend) Lock(n MetadataNode) (UnlockFunc, error) {
return nil, nil
}
// LockAndRead locks the metadata for reading
func (NullBackend) LockAndRead(n MetadataNode) (UnlockFunc, io.Reader, error) {
return nil, nil, nil
}
// IsMetaFile returns whether the given path represents a meta file
func (NullBackend) IsMetaFile(path string) bool { return false }

View File

@@ -286,6 +286,21 @@ func (b XattrsBackend) Lock(n MetadataNode) (UnlockFunc, error) {
}, nil
}
// LockAndRead locks the metadata for reading
func (b XattrsBackend) LockAndRead(n MetadataNode) (UnlockFunc, io.Reader, error) {
unlock, err := b.Lock(n)
if err != nil {
return nil, nil, err
}
f, err := os.Open(b.MetadataPath(n))
if err != nil {
_ = unlock()
return nil, nil, err
}
return unlock, f, nil
}
// AllWithLockedSource reads all extended attributes from the given reader.
// The path argument is used for storing the data in the cache
func (b XattrsBackend) AllWithLockedSource(ctx context.Context, n MetadataNode, _ io.Reader) (map[string][]byte, error) {

View File

@@ -357,13 +357,13 @@ func LockAndReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID, intern
_, subspan := tracer.Start(ctx, "lockedfile.OpenFile")
bn := NewBaseNode(spaceID, nodeID, lu)
unlock, err := lu.MetadataBackend().Lock(bn)
unlock, r, err := lu.MetadataBackend().LockAndRead(bn)
subspan.End()
if err != nil {
return nil, nil, err
}
n, err := ReadNode(ctx, lu, spaceID, nodeID, internalPath, canListDisabledSpace, spaceRoot, skipParentCheck)
n, err := readNode(ctx, lu, spaceID, nodeID, internalPath, canListDisabledSpace, spaceRoot, skipParentCheck, r)
if err != nil {
_ = unlock()
return nil, nil, err
@@ -378,6 +378,12 @@ func LockAndReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID, intern
// ReadNode creates a new instance from an id and checks if it exists
func ReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID, internalPath string, canListDisabledSpace bool, spaceRoot *Node, skipParentCheck bool) (*Node, error) {
return readNode(ctx, lu, spaceID, nodeID, internalPath, canListDisabledSpace, spaceRoot, skipParentCheck, nil)
}
// readNode reads a node by its id. If a reader is provided, it will be passed to the metadata backend to read the metadata.
// This is useful when the caller already holds a lock to prevent deadlocks when reading the metadata.
func readNode(ctx context.Context, lu PathLookup, spaceID, nodeID, internalPath string, canListDisabledSpace bool, spaceRoot *Node, skipParentCheck bool, r io.Reader) (*Node, error) {
ctx, span := tracer.Start(ctx, "ReadNode")
defer span.End()
var err error
@@ -392,6 +398,20 @@ func ReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID, internalPath
},
}
spaceRoot.SpaceRoot = spaceRoot
// If we hold the lock on the space root itself, prime its attribute cache
// through the no-lock path so the owner/name/disabled reads below do not try
// to re-acquire the already-held lock and self-deadlock.
if r != nil && nodeID == spaceID {
_, err = spaceRoot.XattrsWithReader(ctx, r)
switch {
case metadata.IsNotExist(err):
return spaceRoot, nil // swallow not found, the node defaults to exists = false
case err != nil:
return nil, err
}
}
spaceRoot.owner, err = spaceRoot.readOwner(ctx)
switch {
case metadata.IsNotExist(err):
@@ -456,7 +476,8 @@ func ReadNode(ctx context.Context, lu PathLookup, spaceID, nodeID, internalPath
}
}()
attrs, err := n.Xattrs(ctx)
var attrs Attributes
attrs, err = n.XattrsWithReader(ctx, r)
switch {
case metadata.IsNotExist(err):
return n, nil // swallow not found, the node defaults to exists = false

View File

@@ -285,29 +285,20 @@ func (p AsyncPropagator) propagate(ctx context.Context, pn PropagationNode, reca
attrs := node.Attributes{}
// lock parent before reading treesize or tree time
_, subspan = tracer.Start(ctx, "lockedfile.OpenFile")
unlock, err := p.lookup.MetadataBackend().Lock(pn)
_, subspan = tracer.Start(ctx, "node.LockAndReadNode")
n, unlock, err := node.LockAndReadNode(ctx, p.lookup, pn.GetSpaceID(), pn.GetID(), "", false, nil, false)
subspan.End()
if err != nil {
log.Error().Err(err).
Str("lock filepath", p.lookup.MetadataBackend().LockfilePath(pn)).
Msg("Propagation failed. Could not open metadata for node with lock.")
if n != nil && !n.Exists {
log.Debug().Str("attr", prefixes.PropagationAttr).Msg("node does not exist anymore, not propagating")
} else {
log.Error().Err(err).Msg("Propagation failed. Could not read node with lock.")
}
cleanup()
return
}
defer func() { _ = unlock() }()
_, subspan = tracer.Start(ctx, "node.ReadNode")
n, err := node.ReadNode(ctx, p.lookup, pn.GetSpaceID(), pn.GetID(), "", false, nil, false)
if err != nil {
log.Error().Err(err).
Msg("Propagation failed. Could not read node.")
cleanup()
return
}
subspan.End()
if !n.Exists {
log.Debug().Str("attr", prefixes.PropagationAttr).Msg("node does not exist anymore, not propagating")
cleanup()

7
vendor/modules.txt vendored
View File

@@ -239,7 +239,7 @@ github.com/cenkalti/backoff/v4
# github.com/cenkalti/backoff/v5 v5.0.3
## explicit; go 1.23
github.com/cenkalti/backoff/v5
# github.com/ceph/go-ceph v0.39.0
# github.com/ceph/go-ceph v0.40.0
## explicit; go 1.25.0
github.com/ceph/go-ceph/cephfs
github.com/ceph/go-ceph/cephfs/admin
@@ -1241,7 +1241,7 @@ github.com/onsi/ginkgo/reporters/stenographer
github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable
github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty
github.com/onsi/ginkgo/types
# github.com/onsi/ginkgo/v2 v2.28.3
# github.com/onsi/ginkgo/v2 v2.31.0
## explicit; go 1.25.0
github.com/onsi/ginkgo/v2
github.com/onsi/ginkgo/v2/config
@@ -1364,7 +1364,7 @@ github.com/opencloud-eu/icap-client
# github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d
## explicit; go 1.18
github.com/opencloud-eu/libre-graph-api-go
# github.com/opencloud-eu/reva/v2 v2.46.4-0.20260618143111-1bf72cb76394
# github.com/opencloud-eu/reva/v2 v2.46.4-0.20260618143111-1bf72cb76394 => github.com/michaelstingl/opencloud-eu-reva/v2 v2.46.1-0.20260625115217-4e671c0ed52f
## explicit; go 1.25.0
github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace
github.com/opencloud-eu/reva/v2/cmd/revad/runtime
@@ -2738,3 +2738,4 @@ stash.kopano.io/kgol/rndm
# github.com/unrolled/secure => github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4
# go-micro.dev/v4 => github.com/butonic/go-micro/v4 v4.11.1-0.20241115112658-b5d4de5ed9b3
# github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a
# github.com/opencloud-eu/reva/v2 => github.com/michaelstingl/opencloud-eu-reva/v2 v2.46.1-0.20260625115217-4e671c0ed52f