Skip to content

Commit e5ebed0

Browse files
committed
Move SQLite datastore to its own package/folder
This layout is closer to the PostrgeSQL datastore layout in claircore and easier to navigate.
1 parent be6fb4e commit e5ebed0

File tree

16 files changed

+1349
-1277
lines changed

16 files changed

+1349
-1277
lines changed

cmd/cvetool/scan.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import (
2424
"github.com/urfave/cli/v2"
2525

2626
datastore "github.com/ComplianceAsCode/cvetool/datastore"
27+
ds_sqlite "github.com/ComplianceAsCode/cvetool/datastore/sqlite"
2728
image "github.com/ComplianceAsCode/cvetool/image"
2829
output "github.com/ComplianceAsCode/cvetool/output"
30+
"github.com/ComplianceAsCode/cvetool/util"
2931
)
3032

3133
type EnumValue struct {
@@ -159,7 +161,7 @@ func scan(c *cli.Context) error {
159161
case dbPath != "":
160162
case dbURL != "":
161163
var err error
162-
err = datastore.DownloadDB(ctx, dbURL, "")
164+
err = util.DownloadDB(ctx, dbURL, "")
163165
if err != nil {
164166
return fmt.Errorf("could not download database: %v", err)
165167
}
@@ -175,7 +177,7 @@ func scan(c *cli.Context) error {
175177
return fmt.Errorf("unable to get database path")
176178
}
177179

178-
matcherStore, err := datastore.NewSQLiteMatcherStore(dbPath, true)
180+
matcherStore, err := ds_sqlite.NewSQLiteMatcherStore(dbPath, true)
179181
if err != nil {
180182
return fmt.Errorf("error creating sqlite backend: %v", err)
181183
}

cmd/cvetool/update.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"path/filepath"
88
"time"
99

10-
"github.com/ComplianceAsCode/cvetool/datastore"
10+
ds_sqlite "github.com/ComplianceAsCode/cvetool/datastore/sqlite"
1111
"github.com/quay/claircore/libvuln"
1212
_ "github.com/quay/claircore/updater/defaults"
1313
"github.com/urfave/cli/v2"
@@ -44,7 +44,7 @@ func update(c *cli.Context) error {
4444
}
4545
}
4646
}
47-
matcherStore, err := datastore.NewSQLiteMatcherStore(dbPath, true)
47+
matcherStore, err := ds_sqlite.NewSQLiteMatcherStore(dbPath, true)
4848
if err != nil {
4949
return fmt.Errorf("error creating sqlite backend: %v", err)
5050
}

datastore/indexer_store.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package datastore
1+
package sqlite
22

33
import (
44
"bytes"
@@ -21,6 +21,7 @@ type localStore struct {
2121
lock *sync.RWMutex
2222
}
2323

24+
// compile check datastore.IndexerStore implementation
2425
var _ indexer.Indexer = (*LocalIndexerStore)(nil)
2526

2627
// LocalIndexerStore is an implementation of indexer.Indexer.

datastore/sqlite/gc.go

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
package sqlite
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"encoding/json"
7+
"fmt"
8+
9+
"github.com/google/uuid"
10+
"github.com/quay/claircore/libvuln/driver"
11+
"github.com/quay/zlog"
12+
)
13+
14+
// GC will delete any update operations for an updater which exceeds the provided keep
15+
// value.
16+
//
17+
// Implementations may throttle the GC process for datastore efficiency reasons.
18+
//
19+
// The returned int64 value indicates the remaining number of update operations needing GC.
20+
// Running this method till the returned value is 0 accomplishes a full GC of the vulnstore.
21+
func (ms *sqliteMatcherStore) GC(ctx context.Context, count int) (int64, error) {
22+
ctx = zlog.ContextWithValues(ctx, "component", "datastore/sqlite/GC")
23+
24+
// obtain update operations which need deletin'
25+
ops, totalOps, err := eligibleUpdateOpts(ctx, ms.conn, count)
26+
if err != nil {
27+
return 0, err
28+
}
29+
30+
deletedOps, err := ms.DeleteUpdateOperations(ctx, ops...)
31+
if err != nil {
32+
zlog.Error(ctx).Err(err).Msg("DeleteUpdateOperations")
33+
return totalOps - deletedOps, err
34+
}
35+
36+
// get all updaters we know about.
37+
updaters, err := distinctUpdaters(ctx, ms.conn)
38+
if err != nil {
39+
return totalOps - deletedOps, err
40+
}
41+
42+
for kind, us := range updaters {
43+
var cleanup cleanupFunc
44+
switch kind {
45+
case driver.VulnerabilityKind:
46+
cleanup = vulnCleanup
47+
case driver.EnrichmentKind:
48+
cleanup = enrichmentCleanup
49+
default:
50+
zlog.Error(ctx).Str("kind", string(kind)).Msg("unknown updater kind; skipping cleanup")
51+
continue
52+
}
53+
for _, u := range us {
54+
err := cleanup(ctx, ms.conn, u)
55+
if err != nil {
56+
return totalOps - deletedOps, err
57+
}
58+
}
59+
}
60+
61+
return totalOps - deletedOps, nil
62+
}
63+
64+
// distinctUpdaters returns all updaters which have registered an update
65+
// operation.
66+
func distinctUpdaters(ctx context.Context, conn *sql.DB) (map[driver.UpdateKind][]string, error) {
67+
const (
68+
// will always contain at least two update operations
69+
selectUpdaters = `SELECT DISTINCT(updater), kind FROM update_operation;`
70+
)
71+
rows, err := conn.QueryContext(ctx, selectUpdaters)
72+
if err != nil {
73+
return nil, fmt.Errorf("error selecting distinct updaters: %v", err)
74+
}
75+
defer rows.Close()
76+
77+
updaters := make(map[driver.UpdateKind][]string)
78+
for rows.Next() {
79+
var (
80+
updater string
81+
kind driver.UpdateKind
82+
)
83+
err := rows.Scan(&updater, &kind)
84+
switch err {
85+
case nil:
86+
// hop out
87+
default:
88+
return nil, fmt.Errorf("error scanning updater: %v", err)
89+
}
90+
updaters[kind] = append(updaters[kind], updater)
91+
}
92+
if rows.Err() != nil {
93+
return nil, rows.Err()
94+
}
95+
96+
return updaters, nil
97+
}
98+
99+
// eligibleUpdateOpts returns a list of update operation refs which exceed the specified
100+
// keep value.
101+
func eligibleUpdateOpts(ctx context.Context, conn *sql.DB, keep int) ([]uuid.UUID, int64, error) {
102+
const (
103+
// this query will return rows of UUID arrays.
104+
updateOps = `SELECT updater, json_group_array(ref ORDER BY date desc) FROM update_operation GROUP BY updater;`
105+
)
106+
107+
// gather any update operations exceeding our keep value.
108+
// keep+1 is used because PG's array slicing is inclusive,
109+
// we want to grab all items once after our keep value.
110+
m := []uuid.UUID{}
111+
112+
rows, err := conn.QueryContext(ctx, updateOps)
113+
switch err {
114+
case nil:
115+
default:
116+
return nil, 0, fmt.Errorf("error querying for update operations: %v", err)
117+
}
118+
119+
defer rows.Close()
120+
for rows.Next() {
121+
var uuids_json string
122+
var updater string
123+
err := rows.Scan(&updater, &uuids_json)
124+
if err != nil {
125+
return nil, 0, fmt.Errorf("error scanning update operations: %w", err)
126+
}
127+
var uuids []uuid.UUID
128+
json.Unmarshal([]byte(uuids_json), &uuids)
129+
m = append(m, uuids[keep:]...)
130+
}
131+
if rows.Err() != nil {
132+
return nil, 0, rows.Err()
133+
}
134+
135+
return m, int64(len(m)), nil
136+
}
137+
138+
type cleanupFunc func(context.Context, *sql.DB, string) error
139+
140+
func vulnCleanup(ctx context.Context, conn *sql.DB, updater string) error {
141+
const (
142+
deleteOrphanedVulns = `
143+
DELETE FROM vuln
144+
WHERE id IN (
145+
SELECT v2.id
146+
FROM vuln v2
147+
LEFT JOIN uo_vuln uvl
148+
ON v2.id = uvl.vuln
149+
WHERE uvl.vuln IS NULL
150+
AND v2.updater = $1
151+
);
152+
`
153+
)
154+
155+
ctx = zlog.ContextWithValues(ctx, "updater", updater)
156+
zlog.Debug(ctx).
157+
Msg("starting vuln clean up")
158+
res, err := conn.ExecContext(ctx, deleteOrphanedVulns, updater)
159+
if err != nil {
160+
return fmt.Errorf("failed while exec'ing vuln delete: %w", err)
161+
}
162+
rows, err := res.RowsAffected()
163+
if err != nil {
164+
return fmt.Errorf("failed while exec'ing vuln delete (affected): %w", err)
165+
}
166+
zlog.Debug(ctx).Int64("rows affected", rows).Msg("vulns deleted")
167+
168+
return nil
169+
}
170+
171+
func enrichmentCleanup(ctx context.Context, conn *sql.DB, updater string) error {
172+
const (
173+
deleteOrphanedEnrichments = `
174+
DELETE FROM enrichment
175+
WHERE id IN (
176+
SELECT e2.id
177+
FROM enrichment e2
178+
LEFT JOIN uo_enrich uen
179+
ON e2.id = uen.enrich
180+
WHERE uen.enrich IS NULL
181+
AND e2.updater = $1
182+
);
183+
`
184+
)
185+
186+
ctx = zlog.ContextWithValues(ctx, "updater", updater)
187+
zlog.Debug(ctx).
188+
Msg("starting enrichment clean up")
189+
res, err := conn.ExecContext(ctx, deleteOrphanedEnrichments, updater)
190+
if err != nil {
191+
return fmt.Errorf("failed while exec'ing enrichment delete: %w", err)
192+
}
193+
rows, err := res.RowsAffected()
194+
if err != nil {
195+
return fmt.Errorf("failed while exec'ing enrichment delete (affected): %w", err)
196+
}
197+
zlog.Debug(ctx).Int64("rows affected", rows).Msg("enrichments deleted")
198+
199+
return nil
200+
}

0 commit comments

Comments
 (0)