Skip to content

Commit 4b2aa2d

Browse files
committed
wip: rpm: consult dnf database for repository information
tk See-also: #809 Signed-off-by: Hank Donnay <[email protected]>
1 parent aa0b994 commit 4b2aa2d

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

rpm/dnf.go

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package rpm
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
_ "embed" // embed query
7+
"errors"
8+
"fmt"
9+
"io"
10+
"io/fs"
11+
"os"
12+
13+
"github.com/quay/zlog"
14+
_ "modernc.org/sqlite" // register the sqlite driver
15+
)
16+
17+
// BUG(hank) The dnf mapping function is currently useless because there's no
18+
// way to turn the "repoid" that it reports into something with meaning outside
19+
// of the Red Hat build system's builder's context.
20+
21+
// RepoMap reports the latest nevra → repoid mapping, as extracted from the dnf
22+
// or dnf5 database.
23+
func repoMap(ctx context.Context, sys fs.FS) (map[string]string, error) {
24+
var toOpen string
25+
var isdnf5 bool
26+
Look:
27+
for i, p := range []string{
28+
`usr/lib/sysimage/libdnf5/transaction_history.sqlite`,
29+
`var/lib/dnf/history.sqlite`,
30+
} {
31+
switch _, err := fs.Stat(sys, p); {
32+
case errors.Is(err, nil):
33+
toOpen = p
34+
isdnf5 = i == 0
35+
break Look
36+
case errors.Is(err, fs.ErrNotExist): // OK
37+
default:
38+
return nil, fmt.Errorf("rpm: unexpected error opening dnf history: %w", err)
39+
}
40+
}
41+
if toOpen == "" {
42+
// Nothing found.
43+
return nil, nil
44+
}
45+
46+
zlog.Debug(ctx).
47+
Str("path", toOpen).
48+
Bool("is-5", isdnf5).
49+
Msg("found dnf history database")
50+
r, err := sys.Open(toOpen)
51+
switch {
52+
case errors.Is(err, nil):
53+
case errors.Is(err, fs.ErrNotExist):
54+
return nil, nil
55+
default:
56+
return nil, fmt.Errorf("rpm: unexpected error opening dnf history: %w", err)
57+
}
58+
defer func() {
59+
if err := r.Close(); err != nil {
60+
zlog.Warn(ctx).Err(err).Msg("unable to close tarfs sqlite db")
61+
}
62+
}()
63+
64+
// Currently needs to be linked into the filesystem.
65+
// See also: quay/claircore#720
66+
f, err := os.CreateTemp(os.TempDir(), `dnf.sqlite.*`)
67+
if err != nil {
68+
return nil, fmt.Errorf("rpm: error reading sqlite db: %w", err)
69+
}
70+
defer func() {
71+
if err := os.Remove(f.Name()); err != nil {
72+
zlog.Error(ctx).Err(err).Msg("unable to unlink sqlite db")
73+
}
74+
if err := f.Close(); err != nil {
75+
zlog.Warn(ctx).Err(err).Msg("unable to close sqlite db")
76+
}
77+
}()
78+
zlog.Debug(ctx).Str("file", f.Name()).Msg("copying sqlite db out of tar")
79+
if _, err := io.Copy(f, r); err != nil {
80+
return nil, fmt.Errorf("rpm: error reading sqlite db: %w", err)
81+
}
82+
if err := f.Sync(); err != nil {
83+
return nil, fmt.Errorf("rpm: error reading sqlite db: %w", err)
84+
}
85+
86+
db, err := sql.Open("sqlite", f.Name())
87+
if err != nil {
88+
return nil, fmt.Errorf("rpm: error reading sqlite db: %w", err)
89+
}
90+
defer db.Close()
91+
rows, err := db.QueryContext(ctx, queryFinalState, removedEnum(isdnf5))
92+
if err != nil {
93+
return nil, fmt.Errorf("rpm: error querying dnf database: %w", err)
94+
}
95+
defer func() {
96+
if err := rows.Close(); err != nil {
97+
zlog.Warn(ctx).Err(err).Msg("error closing rows object")
98+
}
99+
}()
100+
101+
ret := make(map[string]string)
102+
var k, v string
103+
for rows.Next() {
104+
if err := rows.Scan(&k, &v); err != nil {
105+
return nil, fmt.Errorf("rpm: error reading dnf database: %w", err)
106+
}
107+
ret[k] = v
108+
}
109+
if err := rows.Err(); err != nil {
110+
return nil, fmt.Errorf("rpm: error reading dnf database: %w", err)
111+
}
112+
113+
return ret, nil
114+
}
115+
116+
// RemovedEnum reports the enum for a "removed" action for the indicated
117+
// database version.
118+
func removedEnum(is5 bool) int {
119+
// Defined here:
120+
// https://github.com/rpm-software-management/dnf5/blob/13886935418e28482de7b675169482b85303845d/include/libdnf/transaction/transaction_item_action.hpp#L35
121+
if is5 {
122+
return 5
123+
}
124+
// Defined here:
125+
// https://github.com/rpm-software-management/libdnf/blob/93759bc5cac262906e52b6a173d7b157914ec29e/libdnf/transaction/Types.hpp#L45
126+
return 8
127+
}
128+
129+
// QueryFinalState returns (nerva, repoid) rows and takes a single argument, the
130+
// "removed" enum to disregard.
131+
//
132+
//go:embed dnf_finalstate.sql
133+
var queryFinalState string

rpm/dnf_finalstate.sql

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
SELECT
2+
name ||'-'||
3+
CASE
4+
WHEN epoch = 0 THEN ''
5+
ELSE epoch || ':'
6+
END ||
7+
version ||'-'||
8+
release ||'.'||
9+
arch
10+
AS nerva,
11+
repoid
12+
FROM
13+
trans_item
14+
JOIN (
15+
SELECT
16+
max(id) AS uniq
17+
FROM
18+
trans_item
19+
WHERE
20+
action <> ?
21+
GROUP BY
22+
item_id
23+
) ON (uniq = trans_item.id)
24+
JOIN
25+
repo ON (repo.id = repo_id)
26+
JOIN
27+
rpm USING (item_id);

0 commit comments

Comments
 (0)