Files
zot/pkg/test/mocks/repo_db_mock.go
Jacob McSwain 273b15364b metadb: add optional fast restart path that skips storage walk when (version + commit + storage config) matches metaDB stamp (#4026)
* chore(metadb): add writer version to interface

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* chore(metadb): add writer version to db mock

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* chore(metadb): implement writer version for bolt, redis, and dynamodb

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* feat(metadb): add optional fast restart path that skips storage walk when binary identity matches metaDB stamp

binary identity is determined by the current release tag/commit and stored in metaDB after a successful storage parse. When fast restart is enabled, the next startup will skip the parse if the stored identity matches the current binary

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* chore(cli): serve: add a way to force reparse storage

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* refactor(meta): version: split to avoid global state mutation in tests

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* fix(meta): version: include commit in writerVersion to distinguish retags

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* chore(config): add IsFastRestartEnabled() test

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* fix(meta): skip writer-version stamp when storage parse is incomplete

ParseStorage returns nil even when individual repos fail to parse or are only partially parsed (a missing manifest blob), so MaybeParseStorage would stamp a partially-populated metaDB as good. On the next restart fastRestart trusts the stamp, skips the storage walk, and never recovers.

Track per-repo outcomes via parseStats and stamp only when the walk fully populated the metaDB, otherwise log and continue so the next restart reparses

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* fix(docs): readme: remove trailing comma from JSON config

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* fix(meta): dynamodb: use context.Background instead of context.TODO

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* fix(meta): invalidate fast restart on storage config changes

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* chore(meta): dynamodb: use context.Background() instead of context.TODO()

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* docs(meta): dynamodb: add comment about nil AttributeValue handling in GetWriterVersion

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* chore: rename writer-version stamp to fast-restart stamp

also replaces the version/commit tracking to use BinaryVersion instead of WriterVersion

This should make things more clear

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* fix(config): ensure FastRestart is on GlobalStorageConfig

This is not a per-subpath setting

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

* fix(metadb): redis: tests: ensure clients are closed

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>

---------

Signed-off-by: Jacob McSwain <jacob@mcswain.dev>
2026-06-09 10:47:20 -07:00

504 lines
13 KiB
Go

package mocks
import (
"context"
"time"
godigest "github.com/opencontainers/go-digest"
mTypes "zotregistry.dev/zot/v2/pkg/meta/types"
)
type MetaDBMock struct {
DeleteRepoMetaFn func(repo string) error
GetRepoLastUpdatedFn func(repo string) time.Time
GetStarredReposFn func(ctx context.Context) ([]string, error)
GetBookmarkedReposFn func(ctx context.Context) ([]string, error)
ToggleStarRepoFn func(ctx context.Context, repo string) (mTypes.ToggleState, error)
ToggleBookmarkRepoFn func(ctx context.Context, repo string) (mTypes.ToggleState, error)
GetUserDataFn func(ctx context.Context) (mTypes.UserData, error)
SetUserDataFn func(ctx context.Context, userProfile mTypes.UserData) error
SetUserGroupsFn func(ctx context.Context, groups []string) error
GetUserGroupsFn func(ctx context.Context) ([]string, error)
DeleteUserDataFn func(ctx context.Context) error
GetUserAPIKeyInfoFn func(hashedKey string) (string, error)
IsAPIKeyExpiredFn func(ctx context.Context, hashedKey string) (bool, error)
GetUserAPIKeysFn func(ctx context.Context) ([]mTypes.APIKeyDetails, error)
AddUserAPIKeyFn func(ctx context.Context, hashedKey string, apiKeyDetails *mTypes.APIKeyDetails) error
UpdateUserAPIKeyLastUsedFn func(ctx context.Context, hashedKey string) error
DeleteUserAPIKeyFn func(ctx context.Context, id string) error
PatchDBFn func() error
GetFastRestartStampFn func() (string, error)
SetFastRestartStampFn func(stamp string) error
ImageTrustStoreFn func() mTypes.ImageTrustStore
SetImageTrustStoreFn func(mTypes.ImageTrustStore)
SetRepoReferenceFn func(ctx context.Context, repo string, reference string, imageMeta mTypes.ImageMeta) error
SearchReposFn func(ctx context.Context, searchText string,
) ([]mTypes.RepoMeta, error)
SearchTagsFn func(ctx context.Context, searchText string) ([]mTypes.FullImageMeta, error)
GetImageMetaFn func(digest godigest.Digest) (mTypes.ImageMeta, error)
GetMultipleRepoMetaFn func(ctx context.Context, filter func(repoMeta mTypes.RepoMeta) bool,
) ([]mTypes.RepoMeta, error)
FilterReposFn func(ctx context.Context, rankName mTypes.FilterRepoNameFunc,
filterFunc mTypes.FilterFullRepoFunc) ([]mTypes.RepoMeta, error)
IncrementRepoStarsFn func(repo string) error
DecrementRepoStarsFn func(repo string) error
SetRepoMetaFn func(repo string, repoMeta mTypes.RepoMeta) error
DeleteReferrerFn func(repo string, referredDigest godigest.Digest, referrerDigest godigest.Digest) error
GetReferrersInfoFn func(repo string, referredDigest godigest.Digest, artifactTypes []string,
) ([]mTypes.ReferrerInfo, error)
UpdateStatsOnDownloadFn func(repo string, reference string) error
UpdateSignaturesValidityFn func(ctx context.Context, crepo string, manifestDigest godigest.Digest) error
AddManifestSignatureFn func(repo string, signedManifestDigest godigest.Digest, sygMeta mTypes.SignatureMetadata,
) error
DeleteSignatureFn func(repo string, signedManifestDigest godigest.Digest, sigMeta mTypes.SignatureMetadata) error
SetImageMetaFn func(digest godigest.Digest, imageMeta mTypes.ImageMeta) error
FilterTagsFn func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc,
filterFunc mTypes.FilterFunc) ([]mTypes.FullImageMeta, error)
GetRepoMetaFn func(ctx context.Context, repo string) (mTypes.RepoMeta, error)
FilterImageMetaFn func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error)
RemoveRepoReferenceFn func(repo, reference string, manifestDigest godigest.Digest) error
GetFullImageMetaFn func(ctx context.Context, repo string, tag string) (mTypes.FullImageMeta, error)
ResetRepoReferencesFn func(repo string, tagsToKeep map[string]bool) error
GetAllRepoNamesFn func() ([]string, error)
CountReposFn func(ctx context.Context) (int, error)
ResetDBFn func() error
CloseFn func() error
}
func (sdm MetaDBMock) DeleteRepoMeta(repo string) error {
if sdm.DeleteRepoMetaFn != nil {
return sdm.DeleteRepoMetaFn(repo)
}
return nil
}
func (sdm MetaDBMock) GetAllRepoNames() ([]string, error) {
if sdm.GetAllRepoNamesFn != nil {
return sdm.GetAllRepoNamesFn()
}
return []string{}, nil
}
func (sdm MetaDBMock) CountRepos(ctx context.Context) (int, error) {
if sdm.CountReposFn != nil {
return sdm.CountReposFn(ctx)
}
return 0, nil
}
func (sdm MetaDBMock) GetRepoLastUpdated(repo string) time.Time {
if sdm.GetRepoLastUpdatedFn != nil {
return sdm.GetRepoLastUpdatedFn(repo)
}
return time.Time{}
}
func (sdm MetaDBMock) ResetDB() error {
if sdm.ResetDBFn != nil {
return sdm.ResetDBFn()
}
return nil
}
func (sdm MetaDBMock) ImageTrustStore() mTypes.ImageTrustStore {
if sdm.ImageTrustStoreFn != nil {
return sdm.ImageTrustStoreFn()
}
return nil
}
func (sdm MetaDBMock) SetImageTrustStore(imgTrustStore mTypes.ImageTrustStore) {
if sdm.SetImageTrustStoreFn != nil {
sdm.SetImageTrustStoreFn(imgTrustStore)
}
}
func (sdm MetaDBMock) PatchDB() error {
if sdm.PatchDBFn != nil {
return sdm.PatchDBFn()
}
return nil
}
func (sdm MetaDBMock) GetFastRestartStamp() (string, error) {
if sdm.GetFastRestartStampFn != nil {
return sdm.GetFastRestartStampFn()
}
return "", nil
}
func (sdm MetaDBMock) SetFastRestartStamp(stamp string) error {
if sdm.SetFastRestartStampFn != nil {
return sdm.SetFastRestartStampFn(stamp)
}
return nil
}
func (sdm MetaDBMock) GetStarredRepos(ctx context.Context) ([]string, error) {
if sdm.GetStarredReposFn != nil {
return sdm.GetStarredReposFn(ctx)
}
return []string{}, nil
}
func (sdm MetaDBMock) GetBookmarkedRepos(ctx context.Context) ([]string, error) {
if sdm.GetBookmarkedReposFn != nil {
return sdm.GetBookmarkedReposFn(ctx)
}
return []string{}, nil
}
func (sdm MetaDBMock) ToggleStarRepo(ctx context.Context, repo string) (mTypes.ToggleState, error) {
if sdm.ToggleStarRepoFn != nil {
return sdm.ToggleStarRepoFn(ctx, repo)
}
return mTypes.NotChanged, nil
}
func (sdm MetaDBMock) ToggleBookmarkRepo(ctx context.Context, repo string) (mTypes.ToggleState, error) {
if sdm.ToggleBookmarkRepoFn != nil {
return sdm.ToggleBookmarkRepoFn(ctx, repo)
}
return mTypes.NotChanged, nil
}
func (sdm MetaDBMock) GetUserData(ctx context.Context) (mTypes.UserData, error) {
if sdm.GetUserDataFn != nil {
return sdm.GetUserDataFn(ctx)
}
return mTypes.UserData{}, nil
}
func (sdm MetaDBMock) SetUserData(ctx context.Context, userProfile mTypes.UserData) error {
if sdm.SetUserDataFn != nil {
return sdm.SetUserDataFn(ctx, userProfile)
}
return nil
}
func (sdm MetaDBMock) SetUserGroups(ctx context.Context, groups []string) error {
if sdm.SetUserGroupsFn != nil {
return sdm.SetUserGroupsFn(ctx, groups)
}
return nil
}
func (sdm MetaDBMock) GetUserGroups(ctx context.Context) ([]string, error) {
if sdm.GetUserGroupsFn != nil {
return sdm.GetUserGroupsFn(ctx)
}
return []string{}, nil
}
func (sdm MetaDBMock) DeleteUserData(ctx context.Context) error {
if sdm.DeleteUserDataFn != nil {
return sdm.DeleteUserDataFn(ctx)
}
return nil
}
func (sdm MetaDBMock) GetUserAPIKeyInfo(hashedKey string) (string, error) {
if sdm.GetUserAPIKeyInfoFn != nil {
return sdm.GetUserAPIKeyInfoFn(hashedKey)
}
return "", nil
}
func (sdm MetaDBMock) IsAPIKeyExpired(ctx context.Context, hashedKey string) (bool, error) {
if sdm.IsAPIKeyExpiredFn != nil {
return sdm.IsAPIKeyExpiredFn(ctx, hashedKey)
}
return false, nil
}
func (sdm MetaDBMock) GetUserAPIKeys(ctx context.Context) ([]mTypes.APIKeyDetails, error) {
if sdm.GetUserAPIKeysFn != nil {
return sdm.GetUserAPIKeysFn(ctx)
}
return nil, nil //nolint:nilnil
}
func (sdm MetaDBMock) AddUserAPIKey(ctx context.Context, hashedKey string, apiKeyDetails *mTypes.APIKeyDetails) error {
if sdm.AddUserAPIKeyFn != nil {
return sdm.AddUserAPIKeyFn(ctx, hashedKey, apiKeyDetails)
}
return nil
}
func (sdm MetaDBMock) UpdateUserAPIKeyLastUsed(ctx context.Context, hashedKey string) error {
if sdm.UpdateUserAPIKeyLastUsedFn != nil {
return sdm.UpdateUserAPIKeyLastUsedFn(ctx, hashedKey)
}
return nil
}
func (sdm MetaDBMock) DeleteUserAPIKey(ctx context.Context, id string) error {
if sdm.DeleteUserAPIKeyFn != nil {
return sdm.DeleteUserAPIKeyFn(ctx, id)
}
return nil
}
func (sdm MetaDBMock) SetImageMeta(digest godigest.Digest, imageMeta mTypes.ImageMeta) error {
if sdm.SetImageMetaFn != nil {
return sdm.SetImageMetaFn(digest, imageMeta)
}
return nil
}
func (sdm MetaDBMock) SetRepoReference(ctx context.Context, repo string, reference string,
imageMeta mTypes.ImageMeta,
) error {
if sdm.SetRepoReferenceFn != nil {
return sdm.SetRepoReferenceFn(ctx, repo, reference, imageMeta)
}
return nil
}
func (sdm MetaDBMock) SearchRepos(ctx context.Context, searchText string) ([]mTypes.RepoMeta, error) {
if sdm.SearchReposFn != nil {
return sdm.SearchReposFn(ctx, searchText)
}
return []mTypes.RepoMeta{}, nil
}
func (sdm MetaDBMock) SearchTags(ctx context.Context, searchText string) ([]mTypes.FullImageMeta, error) {
if sdm.SearchTagsFn != nil {
return sdm.SearchTagsFn(ctx, searchText)
}
return []mTypes.FullImageMeta{}, nil
}
func (sdm MetaDBMock) FilterTags(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc,
filterFunc mTypes.FilterFunc,
) ([]mTypes.FullImageMeta, error) {
if sdm.FilterTagsFn != nil {
return sdm.FilterTagsFn(ctx, filterRepoTag, filterFunc)
}
return []mTypes.FullImageMeta{}, nil
}
func (sdm MetaDBMock) GetRepoMeta(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
if sdm.GetRepoMetaFn != nil {
return sdm.GetRepoMetaFn(ctx, repo)
}
return mTypes.RepoMeta{}, nil
}
func (sdm MetaDBMock) GetImageMeta(digest godigest.Digest) (mTypes.ImageMeta, error) {
if sdm.GetImageMetaFn != nil {
return sdm.GetImageMetaFn(digest)
}
return mTypes.ImageMeta{}, nil
}
func (sdm MetaDBMock) GetMultipleRepoMeta(ctx context.Context, filter func(repoMeta mTypes.RepoMeta) bool,
) ([]mTypes.RepoMeta, error) {
if sdm.GetMultipleRepoMetaFn != nil {
return sdm.GetMultipleRepoMetaFn(ctx, filter)
}
return []mTypes.RepoMeta{}, nil
}
func (sdm MetaDBMock) FilterRepos(ctx context.Context, rankName mTypes.FilterRepoNameFunc,
filterFunc mTypes.FilterFullRepoFunc,
) ([]mTypes.RepoMeta, error) {
if sdm.FilterReposFn != nil {
return sdm.FilterReposFn(ctx, rankName, filterFunc)
}
return []mTypes.RepoMeta{}, nil
}
func (sdm MetaDBMock) IncrementRepoStars(repo string) error {
if sdm.IncrementRepoStarsFn != nil {
return sdm.IncrementRepoStarsFn(repo)
}
return nil
}
func (sdm MetaDBMock) DecrementRepoStars(repo string) error {
if sdm.DecrementRepoStarsFn != nil {
return sdm.DecrementRepoStarsFn(repo)
}
return nil
}
func (sdm MetaDBMock) SetRepoMeta(repo string, repoMeta mTypes.RepoMeta) error {
if sdm.SetRepoMetaFn != nil {
return sdm.SetRepoMetaFn(repo, repoMeta)
}
return nil
}
func (sdm MetaDBMock) GetReferrersInfo(repo string, referredDigest godigest.Digest,
artifactTypes []string,
) ([]mTypes.ReferrerInfo, error) {
if sdm.GetReferrersInfoFn != nil {
return sdm.GetReferrersInfoFn(repo, referredDigest, artifactTypes)
}
return []mTypes.ReferrerInfo{}, nil
}
func (sdm MetaDBMock) UpdateStatsOnDownload(repo string, reference string) error {
if sdm.UpdateStatsOnDownloadFn != nil {
return sdm.UpdateStatsOnDownloadFn(repo, reference)
}
return nil
}
func (sdm MetaDBMock) UpdateSignaturesValidity(ctx context.Context, repo string, manifestDigest godigest.Digest) error {
if sdm.UpdateSignaturesValidityFn != nil {
return sdm.UpdateSignaturesValidityFn(ctx, repo, manifestDigest)
}
return nil
}
func (sdm MetaDBMock) AddManifestSignature(repo string, signedManifestDigest godigest.Digest,
sygMeta mTypes.SignatureMetadata,
) error {
if sdm.AddManifestSignatureFn != nil {
return sdm.AddManifestSignatureFn(repo, signedManifestDigest, sygMeta)
}
return nil
}
func (sdm MetaDBMock) DeleteSignature(repo string, signedManifestDigest godigest.Digest,
sigMeta mTypes.SignatureMetadata,
) error {
if sdm.DeleteSignatureFn != nil {
return sdm.DeleteSignatureFn(repo, signedManifestDigest, sigMeta)
}
return nil
}
func (sdm MetaDBMock) FilterImageMeta(ctx context.Context, digests []string,
) (map[string]mTypes.ImageMeta, error) {
if sdm.FilterImageMetaFn != nil {
return sdm.FilterImageMetaFn(ctx, digests)
}
return map[string]mTypes.ImageMeta{}, nil
}
func (sdm MetaDBMock) RemoveRepoReference(repo, reference string, manifestDigest godigest.Digest) error {
if sdm.RemoveRepoReferenceFn != nil {
return sdm.RemoveRepoReferenceFn(repo, reference, manifestDigest)
}
return nil
}
func (sdm MetaDBMock) GetFullImageMeta(ctx context.Context, repo string, tag string,
) (mTypes.FullImageMeta, error) {
if sdm.GetFullImageMetaFn != nil {
return sdm.GetFullImageMetaFn(ctx, repo, tag)
}
return mTypes.FullImageMeta{}, nil
}
func (sdm MetaDBMock) ResetRepoReferences(repo string, tagsToKeep map[string]bool) error {
if sdm.ResetRepoReferencesFn != nil {
return sdm.ResetRepoReferencesFn(repo, tagsToKeep)
}
return nil
}
func (sdm MetaDBMock) Close() error {
if sdm.CloseFn != nil {
return sdm.CloseFn()
}
return nil
}