refactor(db): change logic to detect wrong DB (#8864)

This commit is contained in:
DmitriyLewen
2025-05-19 11:01:50 +06:00
committed by GitHub
parent 35e88890c3
commit 6aff7b0c4f
3 changed files with 71 additions and 26 deletions

View File

@@ -60,9 +60,10 @@ func initDB(t *testing.T) string {
defer dbtest.Close()
err = metadata.NewClient(db.Dir(cacheDir)).Update(metadata.Metadata{
Version: db.SchemaVersion,
NextUpdate: time.Now().Add(24 * time.Hour),
UpdatedAt: time.Now(),
Version: db.SchemaVersion,
NextUpdate: time.Now().Add(24 * time.Hour),
UpdatedAt: time.Now(),
DownloadedAt: time.Now(),
})
require.NoError(t, err)

View File

@@ -110,9 +110,18 @@ func (c *Client) NeedsUpdate(ctx context.Context, cliVersion string, skip bool)
meta = metadata.Metadata{Version: db.SchemaVersion}
}
// There are 2 cases when DownloadAt field is zero:
// - trivy-db was downloaded with `oras`. In this case user can use `--skip-db-update` (like for air-gapped) or re-download trivy-db.
// - trivy-db was corrupted while copying from tmp directory to cache directory. We should update this trivy-db.
// We can't detect these cases, so we will show warning for users who use oras + air-gapped.
if meta.DownloadedAt.IsZero() && !skip {
log.WarnContext(ctx, "Trivy DB may be corrupted and will be re-downloaded. If you manually downloaded DB - use the `--skip-db-update` flag to skip updating DB.")
return true, nil
}
if skip && noRequiredFiles {
log.ErrorContext(ctx, "The first run cannot skip downloading DB")
return false, xerrors.New("--skip-update cannot be specified on the first run")
return false, xerrors.New("--skip-db-update cannot be specified on the first run")
}
if db.SchemaVersion < meta.Version {
@@ -141,7 +150,7 @@ func (c *Client) NeedsUpdate(ctx context.Context, cliVersion string, skip bool)
func (c *Client) validate(meta metadata.Metadata) error {
if db.SchemaVersion != meta.Version {
log.Error("The local DB has an old schema version which is not supported by the current version of Trivy CLI. DB needs to be updated.")
return xerrors.Errorf("--skip-update cannot be specified with the old DB schema. Local DB: %d, Expected: %d",
return xerrors.Errorf("--skip-db-update cannot be specified with the old DB schema. Local DB: %d, Expected: %d",
meta.Version, db.SchemaVersion)
}
return nil
@@ -163,11 +172,6 @@ func (c *Client) isNewDB(ctx context.Context, meta metadata.Metadata) bool {
// Download downloads the DB file
func (c *Client) Download(ctx context.Context, dst string, opt types.RegistryOptions) error {
// Remove the metadata file under the cache directory before downloading DB
if err := c.metadata.Delete(); err != nil {
log.DebugContext(ctx, "No metadata file")
}
if err := c.downloadDB(ctx, opt, dst); err != nil {
return xerrors.Errorf("OCI artifact error: %w", err)
}

View File

@@ -19,6 +19,8 @@ func TestClient_NeedsUpdate(t *testing.T) {
timeNextUpdateDay1 := time.Date(2019, 9, 1, 0, 0, 0, 0, time.UTC)
timeNextUpdateDay2 := time.Date(2019, 10, 2, 0, 0, 0, 0, time.UTC)
timeDownloadAt := time.Date(2019, 9, 30, 22, 30, 0, 0, time.UTC)
tests := []struct {
name string
skip bool
@@ -57,11 +59,12 @@ func TestClient_NeedsUpdate(t *testing.T) {
want: true,
},
{
name: "happy path with --skip-update",
name: "happy path with --skip-db-update",
dbFileExists: true,
metadata: metadata.Metadata{
Version: db.SchemaVersion,
NextUpdate: timeNextUpdateDay1,
Version: db.SchemaVersion,
NextUpdate: timeNextUpdateDay1,
DownloadedAt: timeDownloadAt,
},
skip: true,
want: false,
@@ -70,8 +73,9 @@ func TestClient_NeedsUpdate(t *testing.T) {
name: "skip downloading DB",
dbFileExists: true,
metadata: metadata.Metadata{
Version: db.SchemaVersion,
NextUpdate: timeNextUpdateDay2,
Version: db.SchemaVersion,
NextUpdate: timeNextUpdateDay2,
DownloadedAt: timeDownloadAt,
},
want: false,
},
@@ -79,34 +83,36 @@ func TestClient_NeedsUpdate(t *testing.T) {
name: "newer schema version",
dbFileExists: true,
metadata: metadata.Metadata{
Version: db.SchemaVersion + 1,
NextUpdate: timeNextUpdateDay2,
Version: db.SchemaVersion + 1,
NextUpdate: timeNextUpdateDay2,
DownloadedAt: timeDownloadAt,
},
wantErr: fmt.Sprintf("the version of DB schema doesn't match. Local DB: %d, Expected: %d",
db.SchemaVersion+1, db.SchemaVersion),
},
{
name: "--skip-update without trivy.db on the first run",
name: "--skip-db-update without trivy.db on the first run",
dbFileExists: false,
skip: true,
wantErr: "--skip-update cannot be specified on the first run",
wantErr: "--skip-db-update cannot be specified on the first run",
},
{
name: "--skip-update without metadata.json on the first run",
name: "--skip-db-update without metadata.json on the first run",
dbFileExists: true,
metadata: metadata.Metadata{},
skip: true,
wantErr: "--skip-update cannot be specified on the first run",
wantErr: "--skip-db-update cannot be specified on the first run",
},
{
name: "--skip-update with different schema version",
name: "--skip-db-update with different schema version",
dbFileExists: true,
metadata: metadata.Metadata{
Version: 0,
NextUpdate: timeNextUpdateDay1,
Version: 0,
NextUpdate: timeNextUpdateDay1,
DownloadedAt: timeDownloadAt,
},
skip: true,
wantErr: fmt.Sprintf("--skip-update cannot be specified with the old DB schema. Local DB: %d, Expected: %d",
wantErr: fmt.Sprintf("--skip-db-update cannot be specified with the old DB schema. Local DB: %d, Expected: %d",
0, db.SchemaVersion),
},
{
@@ -115,7 +121,7 @@ func TestClient_NeedsUpdate(t *testing.T) {
metadata: metadata.Metadata{
Version: db.SchemaVersion,
NextUpdate: timeNextUpdateDay1,
DownloadedAt: time.Date(2019, 9, 30, 22, 30, 0, 0, time.UTC),
DownloadedAt: timeDownloadAt,
},
want: true,
},
@@ -129,6 +135,40 @@ func TestClient_NeedsUpdate(t *testing.T) {
},
want: false,
},
{
name: "DownloadedAt is zero, skip is false",
dbFileExists: true,
skip: false,
metadata: metadata.Metadata{
Version: db.SchemaVersion,
DownloadedAt: time.Time{}, // zero time
NextUpdate: timeNextUpdateDay1,
},
want: true,
},
{
name: "DownloadedAt is zero, skip is true",
dbFileExists: true,
skip: true,
metadata: metadata.Metadata{
Version: db.SchemaVersion,
DownloadedAt: time.Time{}, // zero time
NextUpdate: timeNextUpdateDay1,
},
want: false,
},
{
name: "DownloadedAt is zero, skip is true, old schema version",
dbFileExists: true,
skip: true,
metadata: metadata.Metadata{
Version: 0,
DownloadedAt: time.Time{}, // zero time
NextUpdate: timeNextUpdateDay1,
},
wantErr: "--skip-db-update cannot be specified with the old DB schema. Local DB: 0, Expected: 2",
want: false,
},
}
for _, tt := range tests {