-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
wip: rpm: consult dnf database for repository information
tk See-also: #809 Signed-off-by: Hank Donnay <hdonnay@redhat.com>
- Loading branch information
Showing
2 changed files
with
160 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package rpm | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
_ "embed" // embed query | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/fs" | ||
"os" | ||
|
||
"github.com/quay/zlog" | ||
_ "modernc.org/sqlite" // register the sqlite driver | ||
) | ||
|
||
// BUG(hank) The dnf mapping function is currently useless because there's no | ||
// way to turn the "repoid" that it reports into something with meaning outside | ||
// of the Red Hat build system's builder's context. | ||
|
||
// RepoMap reports the latest nevra → repoid mapping, as extracted from the dnf | ||
// or dnf5 database. | ||
func repoMap(ctx context.Context, sys fs.FS) (map[string]string, error) { | ||
var toOpen string | ||
var isdnf5 bool | ||
Look: | ||
for i, p := range []string{ | ||
`usr/lib/sysimage/libdnf5/transaction_history.sqlite`, | ||
`var/lib/dnf/history.sqlite`, | ||
} { | ||
switch _, err := fs.Stat(sys, p); { | ||
case errors.Is(err, nil): | ||
toOpen = p | ||
isdnf5 = i == 0 | ||
break Look | ||
case errors.Is(err, fs.ErrNotExist): // OK | ||
default: | ||
return nil, fmt.Errorf("rpm: unexpected error opening dnf history: %w", err) | ||
} | ||
} | ||
if toOpen == "" { | ||
// Nothing found. | ||
return nil, nil | ||
} | ||
|
||
zlog.Debug(ctx). | ||
Str("path", toOpen). | ||
Bool("is-5", isdnf5). | ||
Msg("found dnf history database") | ||
r, err := sys.Open(toOpen) | ||
switch { | ||
case errors.Is(err, nil): | ||
case errors.Is(err, fs.ErrNotExist): | ||
return nil, nil | ||
default: | ||
return nil, fmt.Errorf("rpm: unexpected error opening dnf history: %w", err) | ||
} | ||
defer func() { | ||
if err := r.Close(); err != nil { | ||
zlog.Warn(ctx).Err(err).Msg("unable to close tarfs sqlite db") | ||
} | ||
}() | ||
|
||
// Currently needs to be linked into the filesystem. | ||
// See also: quay/claircore#720 | ||
f, err := os.CreateTemp(os.TempDir(), `dnf.sqlite.*`) | ||
if err != nil { | ||
return nil, fmt.Errorf("rpm: error reading sqlite db: %w", err) | ||
} | ||
defer func() { | ||
if err := os.Remove(f.Name()); err != nil { | ||
zlog.Error(ctx).Err(err).Msg("unable to unlink sqlite db") | ||
} | ||
if err := f.Close(); err != nil { | ||
zlog.Warn(ctx).Err(err).Msg("unable to close sqlite db") | ||
} | ||
}() | ||
zlog.Debug(ctx).Str("file", f.Name()).Msg("copying sqlite db out of tar") | ||
if _, err := io.Copy(f, r); err != nil { | ||
return nil, fmt.Errorf("rpm: error reading sqlite db: %w", err) | ||
} | ||
if err := f.Sync(); err != nil { | ||
return nil, fmt.Errorf("rpm: error reading sqlite db: %w", err) | ||
} | ||
|
||
db, err := sql.Open("sqlite", f.Name()) | ||
if err != nil { | ||
return nil, fmt.Errorf("rpm: error reading sqlite db: %w", err) | ||
} | ||
defer db.Close() | ||
rows, err := db.QueryContext(ctx, queryFinalState, removedEnum(isdnf5)) | ||
if err != nil { | ||
return nil, fmt.Errorf("rpm: error querying dnf database: %w", err) | ||
} | ||
defer func() { | ||
if err := rows.Close(); err != nil { | ||
zlog.Warn(ctx).Err(err).Msg("error closing rows object") | ||
} | ||
}() | ||
|
||
ret := make(map[string]string) | ||
var k, v string | ||
for rows.Next() { | ||
if err := rows.Scan(&k, &v); err != nil { | ||
return nil, fmt.Errorf("rpm: error reading dnf database: %w", err) | ||
} | ||
ret[k] = v | ||
} | ||
if err := rows.Err(); err != nil { | ||
return nil, fmt.Errorf("rpm: error reading dnf database: %w", err) | ||
} | ||
|
||
return ret, nil | ||
} | ||
|
||
// RemovedEnum reports the enum for a "removed" action for the indicated | ||
// database version. | ||
func removedEnum(is5 bool) int { | ||
// Defined here: | ||
// https://github.com/rpm-software-management/dnf5/blob/13886935418e28482de7b675169482b85303845d/include/libdnf/transaction/transaction_item_action.hpp#L35 | ||
if is5 { | ||
return 5 | ||
} | ||
// Defined here: | ||
// https://github.com/rpm-software-management/libdnf/blob/93759bc5cac262906e52b6a173d7b157914ec29e/libdnf/transaction/Types.hpp#L45 | ||
return 8 | ||
} | ||
|
||
// QueryFinalState returns (nerva, repoid) rows and takes a single argument, the | ||
// "removed" enum to disregard. | ||
// | ||
//go:embed dnf_finalstate.sql | ||
var queryFinalState string |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
SELECT | ||
name ||'-'|| | ||
CASE | ||
WHEN epoch = 0 THEN '' | ||
ELSE epoch || ':' | ||
END || | ||
version ||'-'|| | ||
release ||'.'|| | ||
arch | ||
AS nerva, | ||
repoid | ||
FROM | ||
trans_item | ||
JOIN ( | ||
SELECT | ||
max(id) AS uniq | ||
FROM | ||
trans_item | ||
WHERE | ||
action <> ? | ||
GROUP BY | ||
item_id | ||
) ON (uniq = trans_item.id) | ||
JOIN | ||
repo ON (repo.id = repo_id) | ||
JOIN | ||
rpm USING (item_id); |