[#174] Add slash clipping for FileName attribute
According to the FrostFS API specification, the FileName attribute cannot contain a slash at the beginning. Signed-off-by: Roman Loginov <r.loginov@yadro.com>
This commit is contained in:
parent
20319418cc
commit
47d74a5a77
3 changed files with 44 additions and 3 deletions
|
@ -6,6 +6,7 @@ This document outlines major changes between releases.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Add handling quota limit reached error (#187)
|
- Add handling quota limit reached error (#187)
|
||||||
|
- Add slash clipping for FileName attribute (#174)
|
||||||
|
|
||||||
## [0.32.2] - 2025-02-03
|
## [0.32.2] - 2025-02-03
|
||||||
|
|
||||||
|
|
|
@ -253,6 +253,10 @@ func (h *Handler) byAttribute(c *fasthttp.RequestCtx, handler func(context.Conte
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if key == attrFileName {
|
||||||
|
val = prepareFileName(val)
|
||||||
|
}
|
||||||
|
|
||||||
log = log.With(zap.String("cid", cidParam), zap.String("attr_key", key), zap.String("attr_val", val))
|
log = log.With(zap.String("cid", cidParam), zap.String("attr_key", key), zap.String("attr_val", val))
|
||||||
|
|
||||||
bktInfo, err := h.getBucketInfo(ctx, cidParam, log)
|
bktInfo, err := h.getBucketInfo(ctx, cidParam, log)
|
||||||
|
@ -294,7 +298,7 @@ func (h *Handler) findObjectByAttribute(ctx context.Context, log *zap.Logger, cn
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, io.EOF) && h.needSearchByFileName(attrKey, attrVal):
|
case errors.Is(err, io.EOF) && h.needSearchByFileName(attrKey, attrVal):
|
||||||
log.Debug(logs.ObjectNotFoundByFilePathTrySearchByFileName, logs.TagField(logs.TagExternalStorage))
|
log.Debug(logs.ObjectNotFoundByFilePathTrySearchByFileName, logs.TagField(logs.TagExternalStorage))
|
||||||
return h.findObjectByAttribute(ctx, log, cnrID, attrFileName, attrVal)
|
return h.findObjectByAttribute(ctx, log, cnrID, attrFileName, prepareFileName(attrVal))
|
||||||
case errors.Is(err, io.EOF):
|
case errors.Is(err, io.EOF):
|
||||||
log.Error(logs.ObjectNotFound, zap.Error(err), logs.TagField(logs.TagExternalStorage))
|
log.Error(logs.ObjectNotFound, zap.Error(err), logs.TagField(logs.TagExternalStorage))
|
||||||
return oid.ID{}, fmt.Errorf("object not found: %w", err)
|
return oid.ID{}, fmt.Errorf("object not found: %w", err)
|
||||||
|
@ -315,6 +319,14 @@ func (h *Handler) needSearchByFileName(key, val string) bool {
|
||||||
return strings.HasPrefix(val, "/") && strings.Count(val, "/") == 1 || !strings.Contains(val, "/")
|
return strings.HasPrefix(val, "/") && strings.Count(val, "/") == 1 || !strings.Contains(val, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prepareFileName(fileName string) string {
|
||||||
|
if strings.HasPrefix(fileName, "/") {
|
||||||
|
return fileName[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileName
|
||||||
|
}
|
||||||
|
|
||||||
// resolveContainer decode container id, if it's not a valid container id
|
// resolveContainer decode container id, if it's not a valid container id
|
||||||
// then trey to resolve name using provided resolver.
|
// then trey to resolve name using provided resolver.
|
||||||
func (h *Handler) resolveContainer(ctx context.Context, containerID string) (*cid.ID, error) {
|
func (h *Handler) resolveContainer(ctx context.Context, containerID string) (*cid.ID, error) {
|
||||||
|
|
|
@ -239,6 +239,10 @@ func TestBasic(t *testing.T) {
|
||||||
r = prepareGetByAttributeRequest(ctx, bktName, keyAttr, valAttr)
|
r = prepareGetByAttributeRequest(ctx, bktName, keyAttr, valAttr)
|
||||||
hc.Handler().DownloadByAttribute(r)
|
hc.Handler().DownloadByAttribute(r)
|
||||||
require.Equal(t, content, string(r.Response.Body()))
|
require.Equal(t, content, string(r.Response.Body()))
|
||||||
|
|
||||||
|
r = prepareGetByAttributeRequest(ctx, bktName, attrFileName, "/"+objFileName)
|
||||||
|
hc.Handler().DownloadByAttribute(r)
|
||||||
|
require.Equal(t, content, string(r.Response.Body()))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("head by attribute", func(t *testing.T) {
|
t.Run("head by attribute", func(t *testing.T) {
|
||||||
|
@ -246,6 +250,11 @@ func TestBasic(t *testing.T) {
|
||||||
hc.Handler().HeadByAttribute(r)
|
hc.Handler().HeadByAttribute(r)
|
||||||
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
|
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
|
||||||
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
|
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
|
||||||
|
|
||||||
|
r = prepareGetByAttributeRequest(ctx, bktName, attrFileName, "/"+objFileName)
|
||||||
|
hc.Handler().HeadByAttribute(r)
|
||||||
|
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
|
||||||
|
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("zip", func(t *testing.T) {
|
t.Run("zip", func(t *testing.T) {
|
||||||
|
@ -293,8 +302,8 @@ func TestFindObjectByAttribute(t *testing.T) {
|
||||||
err = json.Unmarshal(r.Response.Body(), &putRes)
|
err = json.Unmarshal(r.Response.Body(), &putRes)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testAttrVal1 := "test-attr-val1"
|
testAttrVal1 := "/folder/cat.jpg"
|
||||||
testAttrVal2 := "test-attr-val2"
|
testAttrVal2 := "cat.jpg"
|
||||||
testAttrVal3 := "test-attr-val3"
|
testAttrVal3 := "test-attr-val3"
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
|
@ -340,6 +349,14 @@ func TestFindObjectByAttribute(t *testing.T) {
|
||||||
err: "not found",
|
err: "not found",
|
||||||
additionalSearch: true,
|
additionalSearch: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "success search by FilePath with leading slash (with additional search)",
|
||||||
|
firstAttr: prepareObjectAttributes(attrFilePath, testAttrVal1),
|
||||||
|
secondAttr: prepareObjectAttributes(attrFileName, testAttrVal2),
|
||||||
|
reqAttrKey: attrFilePath,
|
||||||
|
reqAttrValue: "/cat.jpg",
|
||||||
|
additionalSearch: true,
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
obj := hc.frostfs.objects[putRes.ContainerID+"/"+putRes.ObjectID]
|
obj := hc.frostfs.objects[putRes.ContainerID+"/"+putRes.ObjectID]
|
||||||
|
@ -422,6 +439,17 @@ func TestNeedSearchByFileName(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrepareFileName(t *testing.T) {
|
||||||
|
fileName := "/cat.jpg"
|
||||||
|
expected := "cat.jpg"
|
||||||
|
actual := prepareFileName(fileName)
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
|
||||||
|
fileName = "cat.jpg"
|
||||||
|
actual = prepareFileName(fileName)
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
func prepareUploadRequest(ctx context.Context, bucket, content string) (*fasthttp.RequestCtx, error) {
|
func prepareUploadRequest(ctx context.Context, bucket, content string) (*fasthttp.RequestCtx, error) {
|
||||||
r := new(fasthttp.RequestCtx)
|
r := new(fasthttp.RequestCtx)
|
||||||
utils.SetContextToRequest(ctx, r)
|
utils.SetContextToRequest(ctx, r)
|
||||||
|
|
Loading…
Add table
Reference in a new issue