Skip to content

Commit 7fba22c

Browse files
authored
fix: list images api (#5)
- time parse err - d.Walk now contains folders
1 parent 1786e4b commit 7fba22c

4 files changed

Lines changed: 185 additions & 28 deletions

File tree

registry/storage/driver/cos/cos.go

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@ import (
55
"context"
66
"errors"
77
"fmt"
8-
"github.com/distribution/distribution/v3/internal/dcontext"
9-
storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
10-
"github.com/distribution/distribution/v3/registry/storage/driver/base"
11-
"github.com/distribution/distribution/v3/registry/storage/driver/factory"
12-
"github.com/tencentyun/cos-go-sdk-v5"
138
"io"
149
"net/http"
1510
"net/url"
@@ -18,6 +13,12 @@ import (
1813
"strings"
1914
"sync"
2015
"time"
16+
17+
"github.com/distribution/distribution/v3/internal/dcontext"
18+
storagedriver "github.com/distribution/distribution/v3/registry/storage/driver"
19+
"github.com/distribution/distribution/v3/registry/storage/driver/base"
20+
"github.com/distribution/distribution/v3/registry/storage/driver/factory"
21+
"github.com/tencentyun/cos-go-sdk-v5"
2122
)
2223

2324
const (
@@ -286,7 +287,10 @@ func (d *driver) Stat(ctx context.Context, path string) (storagedriver.FileInfo,
286287
}
287288
// file info
288289
size, err := strconv.ParseInt(res.Header.Get("Content-Length"), 10, 64)
289-
modTime, err := time.Parse(time.RFC1123, res.Header.Get("Last-Modified"))
290+
if err != nil {
291+
return nil, err
292+
}
293+
modTime, err := ParseTime(res.Header.Get("Last-Modified"))
290294
if err != nil {
291295
return nil, err
292296
}
@@ -374,7 +378,7 @@ func (d *driver) copy(ctx context.Context, sourcePath, destPath string) error {
374378
return nil
375379
}
376380
// file size is larger than 32MB
377-
v, _, err := d.cosClient.Object.InitiateMultipartUpload(ctx, key, &cos.InitiateMultipartUploadOptions{
381+
v, _, _ := d.cosClient.Object.InitiateMultipartUpload(ctx, key, &cos.InitiateMultipartUploadOptions{
378382
ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{
379383
ContentType: contentType,
380384
},
@@ -524,6 +528,8 @@ func (d *driver) doWalk(parentCtx context.Context, objectCount *int64, from, sta
524528
var retError error
525529
// the most recent skip directory to avoid walking over undesirable files
526530
var prevSkipDir string
531+
// the most recent directory walked for de-duplication
532+
var prevDir string
527533

528534
key := d.pathToKey(from)
529535
opt := &cos.BucketGetOptions{
@@ -532,7 +538,7 @@ func (d *driver) doWalk(parentCtx context.Context, objectCount *int64, from, sta
532538
Marker: d.pathToKey(startAfter),
533539
}
534540
ctx, done := dcontext.WithTrace(parentCtx)
535-
defer done("cos.Bucket.Get(%s)", opt)
541+
defer done("cos.Bucket.Get(%+v)", opt)
536542
isTruncated := true
537543
for isTruncated {
538544
// list all the objects
@@ -541,30 +547,45 @@ func (d *driver) doWalk(parentCtx context.Context, objectCount *int64, from, sta
541547
return err
542548
}
543549
walkInfos := make([]storagedriver.FileInfoInternal, 0, len(res.Contents))
550+
prevDir = from
544551
for _, content := range res.Contents {
545-
if strings.HasSuffix(content.Key, "/") { // directory
552+
// COS returns only objects, without directories
553+
path := d.keyToPath(content.Key)
554+
555+
// get a list of all directories between the previous
556+
dirs := DirectoryDiff(prevDir, path)
557+
for _, dir := range dirs {
546558
walkInfos = append(walkInfos, storagedriver.FileInfoInternal{
547559
FileInfoFields: storagedriver.FileInfoFields{
548560
IsDir: true,
549-
Path: strings.TrimRight(content.Key, "/"),
550-
},
551-
})
552-
} else { // file object
553-
// last modification time
554-
modTime, err := time.Parse(time.RFC1123, content.LastModified)
555-
if err != nil {
556-
return err
557-
}
558-
walkInfos = append(walkInfos, storagedriver.FileInfoInternal{
559-
FileInfoFields: storagedriver.FileInfoFields{
560-
IsDir: false,
561-
Size: content.Size,
562-
ModTime: modTime,
563-
Path: content.Key,
561+
Path: dir,
564562
},
565563
})
564+
prevDir = dir
565+
}
566+
567+
// corner case
568+
if strings.HasSuffix(path, "/") {
569+
continue
566570
}
571+
572+
modTime, err := ParseTime(content.LastModified)
573+
if err != nil {
574+
return err
575+
}
576+
577+
walkInfos = append(walkInfos, storagedriver.FileInfoInternal{
578+
FileInfoFields: storagedriver.FileInfoFields{
579+
IsDir: false,
580+
Size: content.Size,
581+
ModTime: modTime,
582+
Path: path,
583+
},
584+
})
567585
}
586+
587+
// according to the files, append directories
588+
568589
isTruncated = res.IsTruncated
569590
opt.Marker = res.NextMarker
570591
// iterative
@@ -600,14 +621,12 @@ func (d *driver) doWalk(parentCtx context.Context, objectCount *int64, from, sta
600621

601622
// add the prefix
602623
func (d *driver) pathToKey(path string) string {
603-
// Important! delete the root prefix
604-
newPath := strings.TrimPrefix(path, d.rootDirectory)
605-
return strings.TrimLeft(strings.TrimRight(d.rootDirectory, "/")+newPath, "/")
624+
return PathToKey(d.rootDirectory, path)
606625
}
607626

608627
// remove the prefix
609628
func (d *driver) keyToPath(key string) string {
610-
return "/" + strings.TrimRight(strings.TrimPrefix(key, d.rootDirectory), "/")
629+
return KeyToPath(d.rootDirectory, key)
611630
}
612631

613632
var _ storagedriver.FileWriter = &writer{}

registry/storage/driver/cos/parser.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cos
22

33
import (
44
"errors"
5+
56
"github.com/mitchellh/mapstructure"
67
)
78

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package cos
2+
3+
import (
4+
"path/filepath"
5+
"slices"
6+
"strings"
7+
"time"
8+
)
9+
10+
func ParseTime(timeStr string) (time.Time, error) {
11+
var timeObj time.Time
12+
timeObj, err := time.Parse(time.RFC3339, timeStr)
13+
if err != nil {
14+
timeObj, err = time.Parse(time.RFC1123, timeStr)
15+
if err != nil {
16+
return time.Time{}, err
17+
}
18+
}
19+
return timeObj, nil
20+
}
21+
22+
// DirectoryDiff finds all directories that are not in common between
23+
// the previous and current paths in sorted order.
24+
//
25+
// # Examples
26+
//
27+
// DirectoryDiff("/path/to/folder", "/path/to/folder/folder/file")
28+
// // => [ "/path/to/folder/folder" ]
29+
//
30+
// DirectoryDiff("/path/to/folder/folder1", "/path/to/folder/folder2/file")
31+
// // => [ "/path/to/folder/folder2" ]
32+
//
33+
// DirectoryDiff("/path/to/folder/folder1/file", "/path/to/folder/folder2/file")
34+
// // => [ "/path/to/folder/folder2" ]
35+
//
36+
// DirectoryDiff("/path/to/folder/folder1/file", "/path/to/folder/folder2/folder1/file")
37+
// // => [ "/path/to/folder/folder2", "/path/to/folder/folder2/folder1" ]
38+
//
39+
// DirectoryDiff("/", "/path/to/folder/folder/file")
40+
// // => [ "/path", "/path/to", "/path/to/folder", "/path/to/folder/folder" ]
41+
func DirectoryDiff(prev, current string) []string {
42+
var paths []string
43+
44+
if prev == "" || current == "" {
45+
return paths
46+
}
47+
48+
parent := current
49+
for {
50+
parent = filepath.Dir(parent)
51+
if parent == "/" || parent == prev || strings.HasPrefix(prev+"/", parent+"/") {
52+
break
53+
}
54+
paths = append(paths, parent)
55+
}
56+
slices.Reverse(paths)
57+
return paths
58+
}
59+
60+
// PathToKey 将路径转换为存储键
61+
func PathToKey(rootDir, path string) string {
62+
rootDir = strings.Trim(rootDir, "/")
63+
path = strings.Trim(path, "/")
64+
// Important! delete the root prefix if existed
65+
path = strings.TrimLeft(strings.TrimPrefix(path, rootDir), "/")
66+
if rootDir == "" {
67+
return path
68+
}
69+
if path == "" {
70+
return rootDir
71+
}
72+
return rootDir + "/" + path
73+
}
74+
75+
// KeyToPath 将存储键转换为路径
76+
func KeyToPath(rootDir, key string) string {
77+
rootDir = strings.Trim(rootDir, "/")
78+
key = strings.Trim(key, "/")
79+
if rootDir == "" {
80+
return "/" + key
81+
}
82+
if key == "" {
83+
return ""
84+
}
85+
return "/" + strings.TrimLeft(strings.TrimPrefix(key, rootDir), "/")
86+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package cos
2+
3+
import "testing"
4+
5+
func TestPathToKey(t *testing.T) {
6+
cases := []struct {
7+
root string
8+
path string
9+
key string
10+
}{
11+
{root: "", path: "/a/b/c.txt", key: "a/b/c.txt"},
12+
{root: "", path: "a/b/c.txt", key: "a/b/c.txt"},
13+
{root: "", path: "/", key: ""},
14+
{root: "rootdir", path: "/a/b/c.txt", key: "rootdir/a/b/c.txt"},
15+
{root: "rootdir", path: "a/b/c.txt", key: "rootdir/a/b/c.txt"},
16+
{root: "rootdir", path: "/", key: "rootdir"},
17+
{root: "dup-rootdir", path: "/dup-rootdir/a/b/c.txt", key: "dup-rootdir/a/b/c.txt"},
18+
{root: "dup-rootdir", path: "dup-rootdir/a/b/c.txt", key: "dup-rootdir/a/b/c.txt"},
19+
}
20+
21+
for _, c := range cases {
22+
key := PathToKey(c.root, c.path)
23+
if key != c.key {
24+
t.Errorf("PathToKey(%q, %q) = %q; want %q", c.root, c.path, key, c.key)
25+
}
26+
}
27+
}
28+
29+
func TestKeyToPath(t *testing.T) {
30+
cases := []struct {
31+
root string
32+
key string
33+
path string
34+
}{
35+
{root: "", key: "a/b/c.txt", path: "/a/b/c.txt"},
36+
{root: "", key: "a/b/c.txt", path: "/a/b/c.txt"},
37+
{root: "", key: "/", path: "/"},
38+
{root: "rootdir", key: "rootdir/a/b/c.txt", path: "/a/b/c.txt"},
39+
{root: "rootdir", key: "rootdir/a/b/c.txt", path: "/a/b/c.txt"},
40+
{root: "rootdir", key: "rootdir", path: "/"},
41+
{root: "dup-rootdir", key: "dup-rootdir/a/b/c.txt", path: "/a/b/c.txt"},
42+
{root: "dup-rootdir", key: "dup-rootdir/a/b/c.txt", path: "/a/b/c.txt"},
43+
}
44+
45+
for _, c := range cases {
46+
path := KeyToPath(c.root, c.key)
47+
if path != c.path {
48+
t.Errorf("KeyToPath(%q, %q) = %q; want %q", c.root, c.key, path, c.path)
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)