Merge pull request #9379 from Lyndon-Li/repo-static-info-provider
Some checks failed
Run the E2E test on kind / get-go-version (push) Failing after 1m6s
Run the E2E test on kind / build (push) Has been skipped
Run the E2E test on kind / setup-test-matrix (push) Successful in 2s
Run the E2E test on kind / run-e2e-test (push) Has been skipped
Main CI / get-go-version (push) Failing after 10s
Main CI / Build (push) Has been skipped
Close stale issues and PRs / stale (push) Successful in 13s
Trivy Nightly Scan / Trivy nightly scan (velero, main) (push) Failing after 7m58s
Trivy Nightly Scan / Trivy nightly scan (velero-plugin-for-aws, main) (push) Failing after 1m41s
Trivy Nightly Scan / Trivy nightly scan (velero-plugin-for-gcp, main) (push) Failing after 1m13s
Trivy Nightly Scan / Trivy nightly scan (velero-plugin-for-microsoft-azure, main) (push) Failing after 1m0s

Repo provider interface refactor for repo static configuration
This commit is contained in:
lyndon-li
2025-11-06 15:11:43 +08:00
committed by GitHub
8 changed files with 249 additions and 18 deletions

View File

@@ -0,0 +1 @@
Refactor repo provider interface for static configuration

View File

@@ -60,7 +60,19 @@ type Manager interface {
BatchForget(context.Context, *velerov1api.BackupRepository, []string) []error
// DefaultMaintenanceFrequency returns the default maintenance frequency from the specific repo
DefaultMaintenanceFrequency(repo *velerov1api.BackupRepository) (time.Duration, error)
DefaultMaintenanceFrequency(*velerov1api.BackupRepository) (time.Duration, error)
// ClientSideCacheLimit returns the max cache size required on client side
ClientSideCacheLimit(*velerov1api.BackupRepository) (int64, error)
}
// ConfigProvider defines the methods to get configurations of a backup repository
type ConfigManager interface {
// DefaultMaintenanceFrequency returns the default maintenance frequency from the specific repo
DefaultMaintenanceFrequency(string) (time.Duration, error)
// ClientSideCacheLimit returns the max cache size required on client side
ClientSideCacheLimit(string, map[string]string) (int64, error)
}
type manager struct {
@@ -74,6 +86,11 @@ type manager struct {
log logrus.FieldLogger
}
type configManager struct {
providers map[string]provider.ConfigProvider
log logrus.FieldLogger
}
// NewManager create a new repository manager.
func NewManager(
namespace string,
@@ -101,6 +118,20 @@ func NewManager(
return mgr
}
// NewConfigManager create a new repository config manager.
func NewConfigManager(
log logrus.FieldLogger,
) ConfigManager {
mgr := &configManager{
providers: map[string]provider.ConfigProvider{},
log: log,
}
mgr.providers[velerov1api.BackupRepositoryTypeKopia] = provider.NewUnifiedRepoConfigProvider(velerov1api.BackupRepositoryTypeKopia, mgr.log)
return mgr
}
func (m *manager) InitRepo(repo *velerov1api.BackupRepository) error {
m.repoLocker.LockExclusive(repo.Name)
defer m.repoLocker.UnlockExclusive(repo.Name)
@@ -227,12 +258,16 @@ func (m *manager) DefaultMaintenanceFrequency(repo *velerov1api.BackupRepository
return 0, errors.WithStack(err)
}
param, err := m.assembleRepoParam(repo)
return prd.DefaultMaintenanceFrequency(), nil
}
func (m *manager) ClientSideCacheLimit(repo *velerov1api.BackupRepository) (int64, error) {
prd, err := m.getRepositoryProvider(repo)
if err != nil {
return 0, errors.WithStack(err)
}
return prd.DefaultMaintenanceFrequency(context.Background(), param), nil
return prd.ClientSideCacheLimit(repo.Spec.RepositoryConfig), nil
}
func (m *manager) getRepositoryProvider(repo *velerov1api.BackupRepository) (provider.Provider, error) {
@@ -256,3 +291,30 @@ func (m *manager) assembleRepoParam(repo *velerov1api.BackupRepository) (provide
BackupRepo: repo,
}, nil
}
func (cm *configManager) DefaultMaintenanceFrequency(repoType string) (time.Duration, error) {
prd, err := cm.getRepositoryProvider(repoType)
if err != nil {
return 0, errors.WithStack(err)
}
return prd.DefaultMaintenanceFrequency(), nil
}
func (cm *configManager) ClientSideCacheLimit(repoType string, repoOption map[string]string) (int64, error) {
prd, err := cm.getRepositoryProvider(repoType)
if err != nil {
return 0, errors.WithStack(err)
}
return prd.ClientSideCacheLimit(repoOption), nil
}
func (cm *configManager) getRepositoryProvider(repoType string) (provider.ConfigProvider, error) {
switch repoType {
case velerov1api.BackupRepositoryTypeKopia:
return cm.providers[velerov1api.BackupRepositoryTypeKopia], nil
default:
return nil, fmt.Errorf("failed to get provider for repository %s", repoType)
}
}

View File

@@ -47,3 +47,20 @@ func TestGetRepositoryProvider(t *testing.T) {
_, err = mgr.getRepositoryProvider(repo)
require.Error(t, err)
}
func TestGetRepositoryConfigProvider(t *testing.T) {
mgr := NewConfigManager(nil).(*configManager)
// empty repository type
_, err := mgr.getRepositoryProvider("")
require.Error(t, err)
// valid repository type
provider, err := mgr.getRepositoryProvider(velerov1.BackupRepositoryTypeKopia)
require.NoError(t, err)
assert.NotNil(t, provider)
// invalid repository type
_, err = mgr.getRepositoryProvider(velerov1.BackupRepositoryTypeRestic)
require.Error(t, err)
}

View File

@@ -0,0 +1,84 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
package mocks
import (
mock "github.com/stretchr/testify/mock"
time "time"
)
// ConfigManager is an autogenerated mock type for the ConfigManager type
type ConfigManager struct {
mock.Mock
}
// ClientSideCacheLimit provides a mock function with given fields: repoType, repoOption
func (_m *ConfigManager) ClientSideCacheLimit(repoType string, repoOption map[string]string) (int64, error) {
ret := _m.Called(repoType, repoOption)
if len(ret) == 0 {
panic("no return value specified for ClientSideCacheLimit")
}
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(string, map[string]string) (int64, error)); ok {
return rf(repoType, repoOption)
}
if rf, ok := ret.Get(0).(func(string, map[string]string) int64); ok {
r0 = rf(repoType, repoOption)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(string, map[string]string) error); ok {
r1 = rf(repoType, repoOption)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DefaultMaintenanceFrequency provides a mock function with given fields: repoType
func (_m *ConfigManager) DefaultMaintenanceFrequency(repoType string) (time.Duration, error) {
ret := _m.Called(repoType)
if len(ret) == 0 {
panic("no return value specified for DefaultMaintenanceFrequency")
}
var r0 time.Duration
var r1 error
if rf, ok := ret.Get(0).(func(string) (time.Duration, error)); ok {
return rf(repoType)
}
if rf, ok := ret.Get(0).(func(string) time.Duration); ok {
r0 = rf(repoType)
} else {
r0 = ret.Get(0).(time.Duration)
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(repoType)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewConfigManager creates a new instance of ConfigManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewConfigManager(t interface {
mock.TestingT
Cleanup(func())
}) *ConfigManager {
mock := &ConfigManager{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.39.1. DO NOT EDIT.
// Code generated by mockery v2.53.2. DO NOT EDIT.
package mocks
@@ -37,6 +37,34 @@ func (_m *Manager) BatchForget(_a0 context.Context, _a1 *v1.BackupRepository, _a
return r0
}
// ClientSideCacheLimit provides a mock function with given fields: _a0
func (_m *Manager) ClientSideCacheLimit(_a0 *v1.BackupRepository) (int64, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for ClientSideCacheLimit")
}
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(*v1.BackupRepository) (int64, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(*v1.BackupRepository) int64); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(*v1.BackupRepository) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ConnectToRepo provides a mock function with given fields: repo
func (_m *Manager) ConnectToRepo(repo *v1.BackupRepository) error {
ret := _m.Called(repo)
@@ -55,9 +83,9 @@ func (_m *Manager) ConnectToRepo(repo *v1.BackupRepository) error {
return r0
}
// DefaultMaintenanceFrequency provides a mock function with given fields: repo
func (_m *Manager) DefaultMaintenanceFrequency(repo *v1.BackupRepository) (time.Duration, error) {
ret := _m.Called(repo)
// DefaultMaintenanceFrequency provides a mock function with given fields: _a0
func (_m *Manager) DefaultMaintenanceFrequency(_a0 *v1.BackupRepository) (time.Duration, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for DefaultMaintenanceFrequency")
@@ -66,16 +94,16 @@ func (_m *Manager) DefaultMaintenanceFrequency(repo *v1.BackupRepository) (time.
var r0 time.Duration
var r1 error
if rf, ok := ret.Get(0).(func(*v1.BackupRepository) (time.Duration, error)); ok {
return rf(repo)
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(*v1.BackupRepository) time.Duration); ok {
r0 = rf(repo)
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(time.Duration)
}
if rf, ok := ret.Get(1).(func(*v1.BackupRepository) error); ok {
r1 = rf(repo)
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}

View File

@@ -32,6 +32,8 @@ type RepoParam struct {
// Provider defines the methods to manipulate a backup repository
type Provider interface {
ConfigProvider
// InitRepo is to initialize a repository from a new storage place
InitRepo(ctx context.Context, param RepoParam) error
@@ -60,7 +62,13 @@ type Provider interface {
// BatchForget is to delete a list of snapshots from the repository
BatchForget(ctx context.Context, snapshotIDs []string, param RepoParam) []error
// DefaultMaintenanceFrequency returns the default frequency to run maintenance
DefaultMaintenanceFrequency(ctx context.Context, param RepoParam) time.Duration
}
// ConfigProvider defines the methods to get configurations of a backup repository
type ConfigProvider interface {
// DefaultMaintenanceFrequency returns the default frequency to run maintenance
DefaultMaintenanceFrequency() time.Duration
// ClientSideCacheLimit returns the max cache size required on client side
ClientSideCacheLimit(repoOption map[string]string) int64
}

View File

@@ -90,6 +90,10 @@ func (r *resticRepositoryProvider) BatchForget(ctx context.Context, snapshotIDs
return errs
}
func (r *resticRepositoryProvider) DefaultMaintenanceFrequency(ctx context.Context, param RepoParam) time.Duration {
func (r *resticRepositoryProvider) DefaultMaintenanceFrequency() time.Duration {
return r.svc.DefaultMaintenanceFrequency()
}
func (r *resticRepositoryProvider) ClientSideCacheLimit(repoOption map[string]string) int64 {
return 0
}

View File

@@ -46,6 +46,12 @@ type unifiedRepoProvider struct {
log logrus.FieldLogger
}
type unifiedRepoConfigProvider struct {
repoService udmrepo.BackupRepoService
repoBackend string
log logrus.FieldLogger
}
// this func is assigned to a package-level variable so it can be
// replaced when unit-testing
var getS3Credentials = repoconfig.GetS3Credentials
@@ -86,9 +92,18 @@ func NewUnifiedRepoProvider(
return &repo
}
func GetUnifiedRepoClientSideCacheLimit(repoOption map[string]string, repoBackend string, log logrus.FieldLogger) int64 {
repoService := createRepoService(repoBackend, log)
return repoService.ClientSideCacheLimit(repoOption)
func NewUnifiedRepoConfigProvider(
repoBackend string,
log logrus.FieldLogger,
) ConfigProvider {
repo := unifiedRepoConfigProvider{
repoBackend: repoBackend,
log: log,
}
repo.repoService = createRepoService(repoBackend, log)
return &repo
}
func (urp *unifiedRepoProvider) InitRepo(ctx context.Context, param RepoParam) error {
@@ -375,10 +390,14 @@ func (urp *unifiedRepoProvider) BatchForget(ctx context.Context, snapshotIDs []s
return errs
}
func (urp *unifiedRepoProvider) DefaultMaintenanceFrequency(ctx context.Context, param RepoParam) time.Duration {
func (urp *unifiedRepoProvider) DefaultMaintenanceFrequency() time.Duration {
return urp.repoService.DefaultMaintenanceFrequency()
}
func (urp *unifiedRepoProvider) ClientSideCacheLimit(repoOption map[string]string) int64 {
return urp.repoService.ClientSideCacheLimit(repoOption)
}
func (urp *unifiedRepoProvider) GetPassword(param any) (string, error) {
_, ok := param.(RepoParam)
if !ok {
@@ -429,6 +448,14 @@ func (urp *unifiedRepoProvider) GetStoreOptions(param any) (map[string]string, e
return storeOptions, nil
}
func (urcp *unifiedRepoConfigProvider) DefaultMaintenanceFrequency() time.Duration {
return urcp.repoService.DefaultMaintenanceFrequency()
}
func (urcp *unifiedRepoConfigProvider) ClientSideCacheLimit(repoOption map[string]string) int64 {
return urcp.repoService.ClientSideCacheLimit(repoOption)
}
func getRepoPassword(secretStore credentials.SecretStore) (string, error) {
if secretStore == nil {
return "", errors.New("invalid credentials interface")