fix(storage): fix Azure Blob dump failing with file does not exist (#38814) (#38828)

backport #38814
This commit is contained in:
wxiaoguang
2026-08-07 23:08:46 +00:00
committed by GitHub
parent cca0c65a6c
commit fe252be0ae
4 changed files with 44 additions and 27 deletions
+6 -8
View File
@@ -304,12 +304,10 @@ func (a *AzureBlobStorage) ServeDirectURL(storePath, name, method string, reqPar
// IterateObjects iterates across the objects in the azureblobstorage // IterateObjects iterates across the objects in the azureblobstorage
func (a *AzureBlobStorage) IterateObjects(dirName string, fn func(path string, obj Object) error) error { func (a *AzureBlobStorage) IterateObjects(dirName string, fn func(path string, obj Object) error) error {
dirName = a.buildAzureBlobPath(dirName) basePrefix := buildObjectStorePathPrefix(a.cfg.BasePath, "")
if dirName != "" { dirPrefix := buildObjectStorePathPrefix(a.cfg.BasePath, dirName)
dirName += "/"
}
pager := a.client.NewListBlobsFlatPager(a.cfg.Container, &container.ListBlobsFlatOptions{ pager := a.client.NewListBlobsFlatPager(a.cfg.Container, &container.ListBlobsFlatOptions{
Prefix: &dirName, Prefix: &dirPrefix,
}) })
for pager.More() { for pager.More() {
resp, err := pager.NextPage(a.ctx) resp, err := pager.NextPage(a.ctx)
@@ -317,7 +315,8 @@ func (a *AzureBlobStorage) IterateObjects(dirName string, fn func(path string, o
return convertAzureBlobErr(err) return convertAzureBlobErr(err)
} }
for _, object := range resp.Segment.BlobItems { for _, object := range resp.Segment.BlobItems {
blobClient := a.getBlobClient(*object.Name) objPath := strings.TrimPrefix(*object.Name, basePrefix)
blobClient := a.getBlobClient(objPath)
object := &azureBlobObject{ object := &azureBlobObject{
Context: a.ctx, Context: a.ctx,
blobClient: blobClient, blobClient: blobClient,
@@ -327,7 +326,7 @@ func (a *AzureBlobStorage) IterateObjects(dirName string, fn func(path string, o
} }
if err := func(object *azureBlobObject, fn func(path string, obj Object) error) error { if err := func(object *azureBlobObject, fn func(path string, obj Object) error) error {
defer object.Close() defer object.Close()
return fn(strings.TrimPrefix(object.Name, a.cfg.BasePath), object) return fn(objPath, object)
}(object, fn); err != nil { }(object, fn); err != nil {
return convertAzureBlobErr(err) return convertAzureBlobErr(err)
} }
@@ -336,7 +335,6 @@ func (a *AzureBlobStorage) IterateObjects(dirName string, fn func(path string, o
return nil return nil
} }
// Delete delete a file
func (a *AzureBlobStorage) getBlobClient(path string) *blob.Client { func (a *AzureBlobStorage) getBlobClient(path string) *blob.Client {
return a.client.ServiceClient().NewContainerClient(a.cfg.Container).NewBlobClient(a.buildAzureBlobPath(path)) return a.client.ServiceClient().NewContainerClient(a.cfg.Container).NewBlobClient(a.buildAzureBlobPath(path))
} }
+10 -18
View File
@@ -27,24 +27,16 @@ func TestAzureBlobStorage(t *testing.T) {
Container: "test", Container: "test",
}, },
} }
table := []struct { t.Run("Iterator", func(t *testing.T) {
name string testStorageIterator(t, storageType, config)
test func(t *testing.T, typStr Type, cfg *setting.Storage) })
}{ t.Run("BlobStorageURLContentTypeAndDisposition", func(t *testing.T) {
{ testBlobStorageURLContentTypeAndDisposition(t, storageType, config)
name: "iterator", })
test: testStorageIterator, t.Run("IteratorWithBasePath", func(t *testing.T) {
}, config.AzureBlobConfig.BasePath = "test-base-path"
{ testStorageIterator(t, storageType, config)
name: "testBlobStorageURLContentTypeAndDisposition", })
test: testBlobStorageURLContentTypeAndDisposition,
},
}
for _, entry := range table {
t.Run(entry.name, func(t *testing.T) {
entry.test(t, storageType, config)
})
}
} }
func TestAzureBlobStoragePath(t *testing.T) { func TestAzureBlobStoragePath(t *testing.T) {
+19
View File
@@ -11,11 +11,13 @@ import (
"net/url" "net/url"
"os" "os"
"path" "path"
"strings"
"gitea.dev/modules/httplib" "gitea.dev/modules/httplib"
"gitea.dev/modules/log" "gitea.dev/modules/log"
"gitea.dev/modules/public" "gitea.dev/modules/public"
"gitea.dev/modules/setting" "gitea.dev/modules/setting"
"gitea.dev/modules/util"
) )
// ErrURLNotSupported represents url is not supported // ErrURLNotSupported represents url is not supported
@@ -139,6 +141,23 @@ func SaveFrom(objStorage ObjectStorage, path string, callback func(w io.Writer)
return err return err
} }
func buildObjectStorePath(base, p string) string {
p = strings.TrimPrefix(util.PathJoinRelX(base, p), "/") // object store doesn't use slash for root path
if p == "." {
p = "" // object store doesn't use dot as relative path
}
return p
}
func buildObjectStorePathPrefix(base, p string) string {
// ending slash is required for avoiding matching like "foo/" and "foobar/" with prefix "foo"
p = buildObjectStorePath(base, p) + "/"
if p == "/" {
p = "" // object store doesn't use slash for root path
}
return p
}
var ( var (
// Attachments represents attachments storage // Attachments represents attachments storage
Attachments ObjectStorage = uninitializedStorage Attachments ObjectStorage = uninitializedStorage
+9 -1
View File
@@ -4,6 +4,7 @@
package storage package storage
import ( import (
"io"
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
@@ -31,6 +32,11 @@ func testStorageIterator(t *testing.T, typStr Type, cfg *setting.Storage) {
_, err = l.Save(f[0], strings.NewReader(f[1]), -1) _, err = l.Save(f[0], strings.NewReader(f[1]), -1)
assert.NoError(t, err) assert.NoError(t, err)
} }
defer func() {
for _, f := range testFiles {
_ = l.Delete(f[0])
}
}()
expectedList := map[string][]string{ expectedList := map[string][]string{
"a": {"a/1.txt"}, "a": {"a/1.txt"},
@@ -43,7 +49,9 @@ func testStorageIterator(t *testing.T, typStr Type, cfg *setting.Storage) {
for dir, expected := range expectedList { for dir, expected := range expectedList {
count := 0 count := 0
err = l.IterateObjects(dir, func(path string, f Object) error { err = l.IterateObjects(dir, func(path string, f Object) error {
defer f.Close() content, err := io.ReadAll(f)
assert.NoError(t, err)
assert.NotEmpty(t, content)
assert.Contains(t, expected, path) assert.Contains(t, expected, path)
count++ count++
return nil return nil