add listsnapshot method

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
This commit is contained in:
Lyndon-Li
2026-05-12 09:51:47 +08:00
parent 6ca73a00b6
commit 6257282117
4 changed files with 214 additions and 0 deletions

View File

@@ -527,6 +527,39 @@ func (kr *kopiaRepository) DeleteSnapshot(ctx context.Context, id udmrepo.ID) er
return kr.DeleteManifest(ctx, id)
}
func (kr *kopiaRepository) ListSnapshot(ctx context.Context, source string) ([]udmrepo.Snapshot, error) {
mani, err := snapshot.ListSnapshots(ctx, kr.rawRepo, snapshot.SourceInfo{
Host: udmrepo.GetRepoDomain(),
UserName: udmrepo.GetRepoUser(),
Path: source,
})
if err != nil {
return nil, errors.Wrapf(err, "error listing snapshot manifest for source %s", source)
}
snapshots := []udmrepo.Snapshot{}
for _, snap := range mani {
snapshots = append(snapshots, udmrepo.Snapshot{
Source: snap.Source.Path,
Description: snap.Description,
StartTime: snap.StartTime.ToTime(),
EndTime: snap.EndTime.ToTime(),
Tags: snap.Tags,
RootObject: udmrepo.ObjectMetadata{
ID: udmrepo.ID(snap.RootEntry.ObjectID.String()),
Type: udmrepo.ObjectDataTypeMetadata,
Size: snap.RootEntry.FileSize,
ModTime: snap.RootEntry.ModTime.ToTime(),
Permissions: int(snap.RootEntry.Permissions),
UserID: snap.RootEntry.UserID,
GroupID: snap.RootEntry.GroupID,
},
})
}
return snapshots, nil
}
func (kr *kopiaRepository) Flush(ctx context.Context) error {
if kr.rawWriter == nil {
return errors.New("repo writer is closed or not open")

View File

@@ -1555,3 +1555,113 @@ func TestDeleteSnapshot(t *testing.T) {
}
}
func TestListSnapshot(t *testing.T) {
expectedTime := time.Now()
rawObjID, _ := object.ParseID("I123456")
mockMani := &snapshot.Manifest{
Source: snapshot.SourceInfo{Path: "fake-source"},
Description: "fake-desc",
StartTime: fs.UTCTimestampFromTime(expectedTime),
EndTime: fs.UTCTimestampFromTime(expectedTime.Add(time.Minute)),
RootEntry: &snapshot.DirEntry{
ObjectID: rawObjID,
FileSize: 100,
ModTime: fs.UTCTimestampFromTime(expectedTime),
Permissions: 0o644,
UserID: 1000,
GroupID: 1000,
},
Tags: map[string]string{"tag1": "val1"},
}
testCases := []struct {
name string
rawRepo *repomocks.MockRepository
source string
findRetErr error
setRepoMock bool
expectedErr string
expectedSnaps []udmrepo.Snapshot
}{
{
name: "find manifest fail",
rawRepo: repomocks.NewMockRepository(t),
source: "fake-source",
findRetErr: errors.New("fake-find-error"),
setRepoMock: true,
expectedErr: "error listing snapshot manifest for source fake-source: unable to find manifest entries: fake-find-error",
},
{
name: "succeed",
rawRepo: repomocks.NewMockRepository(t),
source: "fake-source",
setRepoMock: true,
expectedSnaps: []udmrepo.Snapshot{
{
Source: "fake-source",
Description: "fake-desc",
StartTime: mockMani.StartTime.ToTime(),
EndTime: mockMani.EndTime.ToTime(),
RootObject: udmrepo.ObjectMetadata{
ID: udmrepo.ID("I123456"),
Type: udmrepo.ObjectDataTypeMetadata,
Size: mockMani.RootEntry.FileSize,
ModTime: mockMani.RootEntry.ModTime.ToTime(),
Permissions: int(mockMani.RootEntry.Permissions),
UserID: mockMani.RootEntry.UserID,
GroupID: mockMani.RootEntry.GroupID,
},
Tags: map[string]string{"tag1": "val1"},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
kr := &kopiaRepository{}
if tc.rawRepo != nil {
if tc.setRepoMock {
tc.rawRepo.On("FindManifests", mock.Anything, mock.Anything).Return([]*manifest.EntryMetadata{
{
ID: "fake-id",
Labels: map[string]string{
manifest.TypeLabelKey: snapshot.ManifestType,
"hostname": udmrepo.GetRepoDomain(),
"username": udmrepo.GetRepoUser(),
"path": tc.source,
},
},
}, tc.findRetErr)
tc.rawRepo.On("GetManifest", mock.Anything, mock.Anything, mock.Anything).Return(&manifest.EntryMetadata{
Labels: map[string]string{
manifest.TypeLabelKey: snapshot.ManifestType,
},
}, nil).Run(func(args mock.Arguments) {
payload := args.Get(2)
if ptr, ok := payload.(*snapshot.Manifest); ok {
*ptr = *mockMani
} else {
b, _ := json.Marshal(mockMani)
json.Unmarshal(b, payload)
}
}).Maybe()
}
kr.rawRepo = tc.rawRepo
}
snaps, err := kr.ListSnapshot(t.Context(), tc.source)
if tc.expectedErr == "" {
require.NoError(t, err)
assert.Equal(t, tc.expectedSnaps, snaps)
} else {
assert.EqualError(t, err, tc.expectedErr)
}
})
}
}

View File

@@ -562,6 +562,74 @@ func (_c *BackupRepo_GetSnapshot_Call) RunAndReturn(run func(ctx context.Context
return _c
}
// ListSnapshot provides a mock function for the type BackupRepo
func (_mock *BackupRepo) ListSnapshot(ctx context.Context, source string) ([]udmrepo.Snapshot, error) {
ret := _mock.Called(ctx, source)
if len(ret) == 0 {
panic("no return value specified for ListSnapshot")
}
var r0 []udmrepo.Snapshot
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, string) ([]udmrepo.Snapshot, error)); ok {
return returnFunc(ctx, source)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, string) []udmrepo.Snapshot); ok {
r0 = returnFunc(ctx, source)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]udmrepo.Snapshot)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = returnFunc(ctx, source)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BackupRepo_ListSnapshot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListSnapshot'
type BackupRepo_ListSnapshot_Call struct {
*mock.Call
}
// ListSnapshot is a helper method to define mock.On call
// - ctx context.Context
// - source string
func (_e *BackupRepo_Expecter) ListSnapshot(ctx interface{}, source interface{}) *BackupRepo_ListSnapshot_Call {
return &BackupRepo_ListSnapshot_Call{Call: _e.mock.On("ListSnapshot", ctx, source)}
}
func (_c *BackupRepo_ListSnapshot_Call) Run(run func(ctx context.Context, source string)) *BackupRepo_ListSnapshot_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 string
if args[1] != nil {
arg1 = args[1].(string)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *BackupRepo_ListSnapshot_Call) Return(snapshots []udmrepo.Snapshot, err error) *BackupRepo_ListSnapshot_Call {
_c.Call.Return(snapshots, err)
return _c
}
func (_c *BackupRepo_ListSnapshot_Call) RunAndReturn(run func(ctx context.Context, source string) ([]udmrepo.Snapshot, error)) *BackupRepo_ListSnapshot_Call {
_c.Call.Return(run)
return _c
}
// NewObjectWriter provides a mock function for the type BackupRepo
func (_mock *BackupRepo) NewObjectWriter(ctx context.Context, opt udmrepo.ObjectWriteOptions) (udmrepo.ObjectWriter, error) {
ret := _mock.Called(ctx, opt)

View File

@@ -180,6 +180,9 @@ type BackupRepo interface {
// DeleteSnapshot deletes a repo snapshot
DeleteSnapshot(ctx context.Context, id ID) error
// ListSnapshot lists all snapshots in repo for the given source
ListSnapshot(ctx context.Context, source string) ([]Snapshot, error)
// Close closes the backup repository
Close(ctx context.Context) error
}