From f9b3bf0d11904021629a9dc11012b6a4e4cba96e Mon Sep 17 00:00:00 2001 From: Guzman Date: Mon, 29 Dec 2025 12:32:44 +0100 Subject: [PATCH 01/77] rhel: fix error swallowing in PackageScanner.Scan Fix bug where filesystem walk errors from rpm.FindDBs were silently swallowed. The defer attempted to modify a local 'err' variable but couldn't affect the returned error value due to unnamed return params. Changed function signature to use named return parameters matching the pattern in rpm.Scanner. This allows the defer to modify the returned error value. Using a bare return statement makes it clear that the defer will set the final error value. The bug caused PackageScanner to return ([], nil) when encountering filesystem errors (e.g., from zombie processes in /proc), instead of properly propagating the error. Signed-off-by: Guzman --- rhel/packagescanner.go | 5 ++--- rhel/packagescanner_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/rhel/packagescanner.go b/rhel/packagescanner.go index bc0bfae24..8ccf02a94 100644 --- a/rhel/packagescanner.go +++ b/rhel/packagescanner.go @@ -34,7 +34,7 @@ func (p PackageScanner) Version() string { return "1" } // // This implementation stores additional information needed to correlate with // [claircore.Repository] values in the "RepositoryHint" field. -func (p PackageScanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircore.Package, error) { +func (p PackageScanner) Scan(ctx context.Context, layer *claircore.Layer) (out []*claircore.Package, err error) { if err := ctx.Err(); err != nil { return nil, err } @@ -59,7 +59,6 @@ func (p PackageScanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*cl } doDNFWrap = cm == nil || cm.FromDNFHint - var out []*claircore.Package found, errFunc := rpm.FindDBs(ctx, sys) defer func() { err = errors.Join(err, errFunc()) @@ -99,5 +98,5 @@ func (p PackageScanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*cl } } - return out, nil + return } diff --git a/rhel/packagescanner_test.go b/rhel/packagescanner_test.go index b0d0eb32b..ea8506476 100644 --- a/rhel/packagescanner_test.go +++ b/rhel/packagescanner_test.go @@ -2,6 +2,8 @@ package rhel import ( "context" + "errors" + "io/fs" "net/url" "os" "path/filepath" @@ -148,3 +150,38 @@ func TestPackageScannerDNFWrapLogic(t *testing.T) { }) } } + +// TestPackageScannerPropagatesFindDBErrors ensures filesystem walk errors from FindDBs +// are surfaced to the caller instead of being swallowed. +func TestPackageScannerPropagatesFindDBErrors(t *testing.T) { + t.Parallel() + ctx := zlog.Test(context.Background(), t) + + // Point the layer at a non-existent directory to force WalkDir to fail immediately. + missingRoot := filepath.Join(t.TempDir(), "missing-root") + + var l claircore.Layer + desc := claircore.LayerDescription{ + Digest: `sha256:` + "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", + URI: `file://` + missingRoot, + MediaType: `application/vnd.claircore.filesystem`, + Headers: make(map[string][]string), + } + if err := l.Init(ctx, &desc, nil); err != nil { + t.Fatalf("init layer: %v", err) + } + t.Cleanup(func() { + if err := l.Close(); err != nil { + t.Error(err) + } + }) + + scanner := &PackageScanner{} + _, err := scanner.Scan(ctx, &l) + if err == nil { + t.Fatalf("expected error from PackageScanner.Scan") + } + if !errors.Is(err, fs.ErrNotExist) { + t.Fatalf("expected error to wrap fs.ErrNotExist, got %v", err) + } +} From c3a1e6519fbcd73581590d46144f2d2d1e7d7728 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jan 2026 17:18:33 +0000 Subject: [PATCH 02/77] chore(deps): bump github.com/jackc/pgx/v5 from 5.7.6 to 5.8.0 Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.7.6 to 5.8.0. - [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md) - [Commits](https://github.com/jackc/pgx/compare/v5.7.6...v5.8.0) --- updated-dependencies: - dependency-name: github.com/jackc/pgx/v5 dependency-version: 5.8.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e502087ce..744be44a7 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 github.com/jackc/pgconn v1.14.3 - github.com/jackc/pgx/v5 v5.7.6 + github.com/jackc/pgx/v5 v5.8.0 github.com/klauspost/compress v1.18.2 github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d diff --git a/go.sum b/go.sum index 44b319b6a..793bdbd26 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUO github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk= -github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= From 1301e01baa1c80fa9e3f12e3cb9f4b9a68edc902 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jan 2026 19:52:35 +0000 Subject: [PATCH 03/77] chore(deps): bump modernc.org/sqlite from 1.40.1 to 1.42.2 Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.40.1 to 1.42.2. - [Commits](https://gitlab.com/cznic/sqlite/compare/v1.40.1...v1.42.2) --- updated-dependencies: - dependency-name: modernc.org/sqlite dependency-version: 1.42.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 744be44a7..20f10995c 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( golang.org/x/text v0.32.0 golang.org/x/time v0.14.0 golang.org/x/tools v0.40.0 - modernc.org/sqlite v1.40.1 + modernc.org/sqlite v1.42.2 ) require ( diff --git a/go.sum b/go.sum index 793bdbd26..7710a55a4 100644 --- a/go.sum +++ b/go.sum @@ -211,8 +211,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY= -modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= +modernc.org/sqlite v1.42.2 h1:7hkZUNJvJFN2PgfUdjni9Kbvd4ef4mNLOu0B9FGxM74= +modernc.org/sqlite v1.42.2/go.mod h1:+VkC6v3pLOAE0A0uVucQEcbVW0I5nHCeDaBf+DpsQT8= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= From db82e75ba1240856458b2e86fa03e8e7ea0084cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jan 2026 21:49:42 +0000 Subject: [PATCH 04/77] chore(deps): bump github.com/spdx/tools-golang from 0.5.5 to 0.5.6 Bumps [github.com/spdx/tools-golang](https://github.com/spdx/tools-golang) from 0.5.5 to 0.5.6. - [Release notes](https://github.com/spdx/tools-golang/releases) - [Changelog](https://github.com/spdx/tools-golang/blob/main/RELEASE-NOTES.md) - [Commits](https://github.com/spdx/tools-golang/compare/v0.5.5...v0.5.6) --- updated-dependencies: - dependency-name: github.com/spdx/tools-golang dependency-version: 0.5.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 21 +++++---------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 20f10995c..6c2216b25 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/quay/zlog v1.1.9 github.com/remind101/migrate v0.0.0-20170729031349-52c1edff7319 github.com/rs/zerolog v1.32.0 - github.com/spdx/tools-golang v0.5.5 + github.com/spdx/tools-golang v0.5.6 github.com/ulikunitz/xz v0.5.15 go.opentelemetry.io/otel v1.39.0 go.opentelemetry.io/otel/trace v1.39.0 @@ -39,7 +39,7 @@ require ( ) require ( - github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect + github.com/anchore/go-struct-converter v0.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect diff --git a/go.sum b/go.sum index 7710a55a4..5e87f1eab 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFD github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= -github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= +github.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30= +github.com/anchore/go-struct-converter v0.1.0/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -25,8 +25,6 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= @@ -109,22 +107,14 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= -github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk= -github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE= +github.com/spdx/tools-golang v0.5.6 h1:HUwSJWxyAR7vBstEeZ1+guD6Jcl4TVsKyTBUwT0RjTQ= +github.com/spdx/tools-golang v0.5.6/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= @@ -217,4 +207,3 @@ modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= From 63d8b58dc9cdc25b31cf2135dc47d641a533f597 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Wed, 1 Oct 2025 14:52:45 -0500 Subject: [PATCH 05/77] test: add slog shim Signed-off-by: Hank Donnay --- test/logging.go | 150 ++++++++++++++++++++++++++++++++++++++++++ test/logging_go124.go | 36 ++++++++++ test/logging_go125.go | 12 ++++ 3 files changed, 198 insertions(+) create mode 100644 test/logging.go create mode 100644 test/logging_go124.go create mode 100644 test/logging_go125.go diff --git a/test/logging.go b/test/logging.go new file mode 100644 index 000000000..670a33223 --- /dev/null +++ b/test/logging.go @@ -0,0 +1,150 @@ +package test + +import ( + "context" + "fmt" + "log/slog" + "os" + "path/filepath" + "runtime/debug" + "strings" + "sync" + "testing" + "time" + + "github.com/quay/claircore/toolkit/log" + "github.com/quay/zlog" +) + +var ( + // Setup installs the test log handler exactly once. + setup = sync.OnceFunc(func() { + slog.SetDefault(slog.New(new(handler))) + }) + + // Getwd caches [os.Getwd], since it may be called for every [slog.Record]. + getwd = sync.OnceValue(func() string { + dir, err := os.Getwd() + if err != nil { + panic(err) + } + return dir + }) + + // Modname caches the main module name, since it may be needed for every + // [slog.Record]. + modname = sync.OnceValue(func() string { + if info, ok := debug.ReadBuildInfo(); ok { + return info.Main.Path + "/" + } + return "" + }) +) + +type ctxKey struct{} + +var logHandler ctxKey + +var _ slog.Handler = (handler)(nil) + +// DeferredOp is a closure used with [handler] that allows +// [slog.Handler.WithAttrs] and [slog.Handler.WithGroup] to work by deferring +// the call until the concrete [slog.Handler] implementation can be retrieved +// from a [context.Context]. +// +// When a Handler isn't in the Context passed to the Enabled or Handle methods, +// the call is a no-op. +type deferredOp func(slog.Handler) slog.Handler + +// Handler implements [slog.Handler] by extracting a "real" [slog.Handler] from +// the a [context.Context]. +type handler []deferredOp + +// Enabled implements [slog.Handler]. +func (h handler) Enabled(ctx context.Context, l slog.Level) bool { + v := ctx.Value(logHandler) + if v == nil { + return false // ??? + } + lh, ok := v.(slog.Handler) + if !ok { + return false // ??? + } + return lh.Enabled(ctx, l) +} + +// Handle implements [slog.Handler]. +func (h handler) Handle(ctx context.Context, r slog.Record) error { + v := ctx.Value(logHandler) + if v == nil { + return nil // ??? + } + lh, ok := v.(slog.Handler) + if !ok { + return nil // ??? + } + for _, op := range h { + lh = op(lh) + } + if v, ok := ctx.Value(log.AttrsKey).(slog.Value); ok { + r.AddAttrs(v.Group()...) + } + return lh.Handle(ctx, r) +} + +// WithAttrs implements [slog.Handler]. +func (h handler) WithAttrs(attrs []slog.Attr) slog.Handler { + return append(h, func(h slog.Handler) slog.Handler { + return h.WithAttrs(attrs) + }) +} + +// WithGroup implements [slog.Handler]. +func (h handler) WithGroup(name string) slog.Handler { + return append(h, func(h slog.Handler) slog.Handler { + return h.WithGroup(name) + }) +} + +// Logging returns a [context.Context] that's set up to make the default +// [slog.Logger] defer output to the provided [testing.TB] output. +func Logging(t testing.TB, parent ...context.Context) context.Context { + setup() + var ctx context.Context + if len(parent) > 0 { + ctx = parent[0] + } else { + // Don't use the test Context: users should pass that in if that's what + // they want. + ctx = context.Background() + } + ctx = zlog.Test(ctx, t) + start := time.Now() + w := logOutput(t) + h := slog.NewTextHandler(w, &slog.HandlerOptions{ + AddSource: true, + Level: slog.LevelDebug, + ReplaceAttr: func(g []string, a slog.Attr) slog.Attr { + if g != nil { + return a + } + switch a.Key { + case "time": + dur := time.Since(start) + return slog.String("time", "+"+dur.String()) + case "source": + src := a.Value.Any().(*slog.Source) + if src.Function != "" { + return slog.String("source", strings.TrimPrefix(src.Function, modname())) + } + f := src.File + if r, err := filepath.Rel(getwd(), f); err == nil && r != "" { + f = r + } + return slog.String("source", fmt.Sprintf("%s:%d", f, src.Line)) + } + return a + }, + }) + return context.WithValue(ctx, logHandler, h) +} diff --git a/test/logging_go124.go b/test/logging_go124.go new file mode 100644 index 000000000..ceb1dab26 --- /dev/null +++ b/test/logging_go124.go @@ -0,0 +1,36 @@ +//go:build !go1.25 + +package test + +import ( + "bufio" + "io" + "os" + "path/filepath" + "testing" +) + +func logOutput(t testing.TB) io.Writer { + d := t.TempDir() + n := filepath.Join(d, "output") + t.Cleanup(func() { + f, err := os.Open(n) + if err != nil { + t.Error(err) + return + } + defer f.Close() + s := bufio.NewScanner(f) + for s.Scan() { + t.Log(s.Text()) + } + if err := s.Err(); err != nil { + t.Error(err) + } + }) + w, err := os.Create(n) + if err != nil { + t.Fatal(err) + } + return w +} diff --git a/test/logging_go125.go b/test/logging_go125.go new file mode 100644 index 000000000..a90ea43d9 --- /dev/null +++ b/test/logging_go125.go @@ -0,0 +1,12 @@ +//go:build go1.25 + +package test + +import ( + "io" + "testing" +) + +func logOutput(t testing.TB) io.Writer { + return t.Output() +} From 8b628a03181dc0194cf42bdf239f6be031f981a0 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 10:55:31 -0500 Subject: [PATCH 06/77] test: migrate to slog Signed-off-by: Hank Donnay --- test/matcher.go | 6 +----- test/packagescanner.go | 5 ++--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/test/matcher.go b/test/matcher.go index 38777c2e4..fb7f0fbbd 100644 --- a/test/matcher.go +++ b/test/matcher.go @@ -17,7 +17,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "go.uber.org/mock/gomock" "golang.org/x/tools/txtar" @@ -58,9 +57,6 @@ type MatcherGetCall struct { // [Jq]: https://jqlang.github.io/jq/ func RunMatcherTests(ctx context.Context, t *testing.T, dir string, matchers ...driver.Matcher) { t.Helper() - if ctx == nil { - ctx = zlog.Test(context.Background(), t) - } ms, err := filepath.Glob(filepath.Join(dir, "*.txtar")) if err != nil { @@ -73,7 +69,7 @@ func RunMatcherTests(ctx context.Context, t *testing.T, dir string, matchers ... n := filepath.Base(m) n = strings.TrimSuffix(n, filepath.Ext(n)) t.Run(n, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := Logging(t, ctx) t.Logf("loading txtar: %#q", m) ar, err := txtar.ParseFile(m) diff --git a/test/packagescanner.go b/test/packagescanner.go index 88dc64dc1..23bfc6c2c 100644 --- a/test/packagescanner.go +++ b/test/packagescanner.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -46,7 +45,7 @@ func (tc ScannerTestcase) Digest() claircore.Digest { func (tc ScannerTestcase) Run(ctx context.Context) func(*testing.T) { sort.Slice(tc.Want, pkgSort(tc.Want)) return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := Logging(t, ctx) l := tc.getLayer(ctx, t) got, err := tc.Scanner.Scan(ctx, l) @@ -69,7 +68,7 @@ func (tc ScannerTestcase) Run(ctx context.Context) func(*testing.T) { func (tc ScannerTestcase) RunSubset(ctx context.Context, n int) func(*testing.T) { sort.Slice(tc.Want, pkgSort(tc.Want)) return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := Logging(t, ctx) l := tc.getLayer(ctx, t) got, err := tc.Scanner.Scan(ctx, l) From f8284e4175363f7d6677146786ed2c00f750d9cf Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Tue, 25 Mar 2025 14:18:01 -0500 Subject: [PATCH 07/77] alpine: migrate to slog Signed-off-by: Hank Donnay --- alpine/distributionscanner.go | 27 ++++++++---------- alpine/distributionscanner_test.go | 6 ++-- alpine/fetcher.go | 35 +++++++++++------------ alpine/fetcher_test.go | 7 +++-- alpine/parser.go | 7 ++--- alpine/parser_test.go | 9 +++--- alpine/secdb_test.go | 2 +- alpine/updater.go | 45 ++++++++++++++---------------- 8 files changed, 65 insertions(+), 73 deletions(-) diff --git a/alpine/distributionscanner.go b/alpine/distributionscanner.go index 1f95bc020..b9179c085 100644 --- a/alpine/distributionscanner.go +++ b/alpine/distributionscanner.go @@ -6,12 +6,11 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "regexp" "runtime/trace" "strings" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/osrelease" @@ -61,12 +60,10 @@ func (*DistributionScanner) Kind() string { return scannerKind } // If the files are found but all regexp fail to match an empty slice is returned. func (s *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() - ctx = zlog.ContextWithValues(ctx, - "component", "alpine/DistributionScanner.Scan", + slog.DebugContext(ctx, "start", "version", s.Version(), "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + defer slog.DebugContext(ctx, "done") sys, err := l.FS() if err != nil { return nil, fmt.Errorf("alpine: unable to open layer: %w", err) @@ -102,13 +99,13 @@ func readOSRelease(ctx context.Context, sys fs.FS) (*claircore.Distribution, err return nil, err } if id := m[`ID`]; id != `alpine` { - zlog.Debug(ctx).Str("id", id).Msg("seemingly not alpine") + slog.DebugContext(ctx, "seemingly not alpine", "id", id) break } vid := m[`VERSION_ID`] idx := strings.LastIndexByte(vid, '.') if idx == -1 { - zlog.Debug(ctx).Str("val", vid).Msg("martian VERSION_ID") + slog.DebugContext(ctx, "martian VERSION_ID", "val", vid) break } v := vid[:idx] @@ -126,9 +123,9 @@ func readOSRelease(ctx context.Context, sys fs.FS) (*claircore.Distribution, err PrettyName: m[`PRETTY_NAME`], }, nil case errors.Is(err, fs.ErrNotExist): - zlog.Debug(ctx). - Str("path", osrelease.Path). - Msg("file doesn't exist") + slog.DebugContext(ctx, + "file doesn't exist", + "path", osrelease.Path) default: return nil, err } @@ -153,7 +150,7 @@ func readIssue(ctx context.Context, sys fs.FS) (*claircore.Distribution, error) ms := issueRegexp.FindSubmatch(b) if ms == nil { - zlog.Debug(ctx).Msg("seemingly not alpine") + slog.DebugContext(ctx, "seemingly not alpine") break } v := string(ms[1]) @@ -164,9 +161,9 @@ func readIssue(ctx context.Context, sys fs.FS) (*claircore.Distribution, error) PrettyName: fmt.Sprintf(`Alpine Linux v%s`, v), }, nil case errors.Is(err, fs.ErrNotExist): - zlog.Debug(ctx). - Str("path", issuePath). - Msg("file doesn't exist") + slog.DebugContext(ctx, + "file doesn't exist", + "path", issuePath) default: return nil, err } diff --git a/alpine/distributionscanner_test.go b/alpine/distributionscanner_test.go index b21adc1e3..50a8fb20a 100644 --- a/alpine/distributionscanner_test.go +++ b/alpine/distributionscanner_test.go @@ -8,14 +8,13 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" + "github.com/quay/claircore/test" ) func TestScanFs(t *testing.T) { - ctx := context.Background() - ctx = zlog.Test(ctx, t) + ctx := test.Logging(t) td := os.DirFS("testdata") ms, err := fs.Glob(td, "3.*") @@ -46,7 +45,6 @@ func scanFsTestcase(ctx context.Context, sys fs.FS) func(*testing.T) { if err != nil { t.Fatal(err) } - ctx := zlog.Test(ctx, t) got, err := s.scanFs(ctx, sys) if err != nil { t.Error(err) diff --git a/alpine/fetcher.go b/alpine/fetcher.go index f496ab94a..717172352 100644 --- a/alpine/fetcher.go +++ b/alpine/fetcher.go @@ -4,27 +4,24 @@ import ( "context" "fmt" "io" + "log/slog" "net/http" - "github.com/quay/zlog" - "github.com/quay/claircore/libvuln/driver" "github.com/quay/claircore/pkg/tmp" ) func (u *updater) Fetch(ctx context.Context, hint driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { - ctx = zlog.ContextWithValues(ctx, "component", "alpine/Updater.Fetch") - - zlog.Info(ctx).Str("database", u.url).Msg("starting fetch") + slog.InfoContext(ctx, "starting fetch", "database", u.url) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.url, nil) if err != nil { return nil, hint, fmt.Errorf("alpine: unable to construct request: %w", err) } if hint != "" { - zlog.Debug(ctx). - Str("hint", string(hint)). - Msg("using hint") + slog.DebugContext(ctx, + "using hint", + "hint", string(hint)) req.Header.Set("if-none-match", string(hint)) } @@ -41,25 +38,27 @@ func (u *updater) Fetch(ctx context.Context, hint driver.Fingerprint) (io.ReadCl } fallthrough case http.StatusNotModified: - zlog.Info(ctx).Msg("database unchanged since last fetch") + slog.InfoContext(ctx, "database unchanged since last fetch") return nil, hint, driver.Unchanged default: return nil, hint, fmt.Errorf("alpine: http response error: %s %d", res.Status, res.StatusCode) } - zlog.Debug(ctx).Msg("successfully requested database") + slog.DebugContext(ctx, "successfully requested database") tf, err := tmp.NewFile("", u.Name()+".") if err != nil { return nil, hint, fmt.Errorf("alpine: unable to open tempfile: %w", err) } - zlog.Debug(ctx). - Str("name", tf.Name()). - Msg("created tempfile") + slog.DebugContext(ctx, + "created tempfile", + "name", tf.Name()) var success bool defer func() { if !success { if err := tf.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close spool") + slog.WarnContext(ctx, + "unable to close spool", + "reason", err) } } }() @@ -71,13 +70,13 @@ func (u *updater) Fetch(ctx context.Context, hint driver.Fingerprint) (io.ReadCl if n, err := tf.Seek(0, io.SeekStart); err != nil || n != 0 { return nil, hint, fmt.Errorf("alpine: unable to seek database to start: at %d, %v", n, err) } - zlog.Debug(ctx).Msg("decompressed and buffered database") + slog.DebugContext(ctx, "decompressed and buffered database") success = true hint = driver.Fingerprint(res.Header.Get("etag")) - zlog.Debug(ctx). - Str("hint", string(hint)). - Msg("using new hint") + slog.DebugContext(ctx, + "using new hint", + "hint", string(hint)) return tf, hint, nil } diff --git a/alpine/fetcher_test.go b/alpine/fetcher_test.go index aa2b93e45..5388fc669 100644 --- a/alpine/fetcher_test.go +++ b/alpine/fetcher_test.go @@ -1,13 +1,13 @@ package alpine import ( - "context" "net/http" "net/http/httptest" "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" + + "github.com/quay/claircore/test" ) func serveSecDB(t *testing.T) (string, *http.Client) { @@ -17,7 +17,8 @@ func serveSecDB(t *testing.T) (string, *http.Client) { } func TestFactory(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) + root, c := serveSecDB(t) fac := &Factory{} err := fac.Configure(ctx, func(v any) error { diff --git a/alpine/parser.go b/alpine/parser.go index 015faae33..b0d6bf400 100644 --- a/alpine/parser.go +++ b/alpine/parser.go @@ -4,8 +4,7 @@ import ( "context" "encoding/json" "io" - - "github.com/quay/zlog" + "log/slog" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" @@ -18,8 +17,8 @@ const ( var _ driver.Parser = (*updater)(nil) func (u *updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, "component", "alpine/Updater.Parse") - zlog.Info(ctx).Msg("starting parse") + slog.InfoContext(ctx, "parse start") + defer slog.InfoContext(ctx, "parse done") defer r.Close() var db SecurityDB diff --git a/alpine/parser_test.go b/alpine/parser_test.go index 2ecc05df7..4de7777d7 100644 --- a/alpine/parser_test.go +++ b/alpine/parser_test.go @@ -8,9 +8,9 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" + "github.com/quay/claircore/test" ) var dist310 = stableRelease{3, 10}.Distribution() @@ -128,8 +128,8 @@ var v3_10CommunityTruncatedVulns = []*claircore.Vulnerability{ func TestParser(t *testing.T) { t.Parallel() - ctx := context.Background() - var table = []struct { + ctx := test.Logging(t) + table := []struct { release release repo string testFile string @@ -145,7 +145,8 @@ func TestParser(t *testing.T) { for _, test := range table { t.Run(test.testFile, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx, done := context.WithCancel(ctx) + t.Cleanup(done) path := fmt.Sprintf("testdata/%s", test.testFile) f, err := os.Open(path) diff --git a/alpine/secdb_test.go b/alpine/secdb_test.go index f0e3cb749..51ba73c95 100644 --- a/alpine/secdb_test.go +++ b/alpine/secdb_test.go @@ -48,7 +48,7 @@ var v3_10CommunityTruncatedSecDB = SecurityDB{ } func TestSecDBParse(t *testing.T) { - var table = []struct { + table := []struct { testFile string expected SecurityDB }{ diff --git a/alpine/updater.go b/alpine/updater.go index d8c7c4371..8e444da7d 100644 --- a/alpine/updater.go +++ b/alpine/updater.go @@ -4,14 +4,13 @@ import ( "bytes" "context" "fmt" + "log/slog" "net/http" "net/url" "path" "strings" "sync" - "github.com/quay/zlog" - "github.com/quay/claircore/libvuln/driver" ) @@ -60,11 +59,10 @@ func NewFactory(_ context.Context) (*Factory, error) { // UpdaterSet implements driver.UpdaterSetFactory. func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { - ctx = zlog.ContextWithValues(ctx, "component", "alpine/Factory.UpdaterSet") s := driver.NewUpdaterSet() if f.c == nil { - zlog.Info(ctx). - Msg("unconfigured") + slog.InfoContext(ctx, + "unconfigured") return s, nil } @@ -81,9 +79,9 @@ func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { if f.etag != "" { req.Header.Set(`if-none-match`, f.etag) } - zlog.Debug(ctx). - Stringer("url", u). - Msg("making request") + slog.DebugContext(ctx, + "making request", + "url", u) res, err := f.c.Do(req) if err != nil { return s, fmt.Errorf("alpine: error requesting %q: %w", u.String(), err) @@ -91,9 +89,9 @@ func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { defer res.Body.Close() switch res.StatusCode { case http.StatusNotModified: - zlog.Debug(ctx). - Stringer("url", u). - Msg("not modified") + slog.DebugContext(ctx, + "not modified", + "url", u) return f.cur, nil case http.StatusOK: default: @@ -127,12 +125,12 @@ Major: if err != nil { return s, fmt.Errorf("alpine: unable to construct request: %w", err) } - ctx := zlog.ContextWithValues(ctx, "url", u.String(), "release", r.String()) + l := slog.With("url", u, "release", r) req, err := http.NewRequestWithContext(ctx, http.MethodHead, u.String(), nil) if err != nil { return s, fmt.Errorf("alpine: unable to construct request: %w", err) } - zlog.Debug(ctx).Msg("checking release") + l.DebugContext(ctx, "checking release") res, err := f.c.Do(req) if err != nil { return s, fmt.Errorf("alpine: error requesting %q: %w", u.String(), err) @@ -143,13 +141,13 @@ Major: foundLower = true todo = append(todo, r) case http.StatusNotFound: - zlog.Debug(ctx).Msg("not found") + l.DebugContext(ctx, "not found") if foundLower { break Minor } break Major default: - zlog.Info(ctx).Str("status", res.Status).Msg("unexpected status reported") + l.InfoContext(ctx, "unexpected status reported", "status", res.Status) } } } @@ -159,12 +157,12 @@ Major: if err != nil { return s, fmt.Errorf("alpine: unable to construct request: %w", err) } - ctx := zlog.ContextWithValues(ctx, "url", u.String(), "release", r.String(), "repo", n) + l := slog.With("url", u, "release", r, "repo", n) req, err := http.NewRequestWithContext(ctx, http.MethodHead, u.String(), nil) if err != nil { return s, fmt.Errorf("alpine: unable to construct request: %w", err) } - zlog.Debug(ctx).Msg("checking repository") + l.DebugContext(ctx, "checking repository") res, err := f.c.Do(req) if err != nil { return s, fmt.Errorf("alpine: error requesting %q: %w", u.String(), err) @@ -172,12 +170,12 @@ Major: res.Body.Close() switch res.StatusCode { case http.StatusOK: - zlog.Debug(ctx).Msg("found") + l.DebugContext(ctx, "found") case http.StatusNotFound: - zlog.Debug(ctx).Msg("not found") + l.DebugContext(ctx, "not found") continue default: - zlog.Info(ctx).Str("status", res.Status).Msg("unexpected status reported") + l.InfoContext(ctx, "unexpected status reported", "status", res.Status) continue } s.Add(&updater{ @@ -247,10 +245,9 @@ func (u *updater) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c * } if cfg.URL != "" { u.url = cfg.URL - zlog.Info(ctx). - Str("component", "alpine/Updater.Configure"). - Str("updater", u.Name()). - Msg("configured url") + slog.InfoContext(ctx, + "configured url", + "updater", u.Name()) } u.client = c return nil From 7b05cbd1cc4eb10e7655bdeab4d693710f470aec Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Wed, 1 Oct 2025 14:53:25 -0500 Subject: [PATCH 08/77] apk: migrate to slog Signed-off-by: Hank Donnay --- apk/scanner.go | 14 ++++++-------- apk/scanner_test.go | 4 +--- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/apk/scanner.go b/apk/scanner.go index 519e2000d..3ad6e2d01 100644 --- a/apk/scanner.go +++ b/apk/scanner.go @@ -6,10 +6,9 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "runtime/trace" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) @@ -51,13 +50,12 @@ func (*Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircore. } defer trace.StartRegion(ctx, "Scanner.Scan").End() trace.Log(ctx, "layer", layer.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "apk/Scanner.Scan", + log := slog.With( "version", version, "layer", layer.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + log.DebugContext(ctx, "start") + defer log.DebugContext(ctx, "done") sys, err := layer.FS() if err != nil { @@ -71,7 +69,7 @@ func (*Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircore. default: return nil, err } - zlog.Debug(ctx).Msg("found database") + log.DebugContext(ctx, "found database") pkgs := []*claircore.Package{} srcs := make(map[string]*claircore.Package) @@ -118,7 +116,7 @@ func (*Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircore. } pkgs = append(pkgs, &p) } - zlog.Debug(ctx).Int("count", len(pkgs)).Msg("found packages") + log.DebugContext(ctx, "found packages", "count", len(pkgs)) return pkgs, nil } diff --git a/apk/scanner_test.go b/apk/scanner_test.go index 21009b995..7fc0c8df8 100644 --- a/apk/scanner_test.go +++ b/apk/scanner_test.go @@ -1,11 +1,9 @@ package apk import ( - "context" "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/test" @@ -13,7 +11,7 @@ import ( func TestScan(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) // TODO(hank) Turn into a fixture. want := []*claircore.Package{ { From 5ccb674f4750f6f26e7ec9498e4347352e836ac1 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 10:07:16 -0500 Subject: [PATCH 09/77] aws: migrate to slog Signed-off-by: Hank Donnay --- aws/client.go | 59 +++++++++++++++++--------------------- aws/client_test.go | 9 ++++-- aws/distributionscanner.go | 16 ++++------- aws/updater.go | 3 -- 4 files changed, 38 insertions(+), 49 deletions(-) diff --git a/aws/client.go b/aws/client.go index 7e1de3575..511c33464 100644 --- a/aws/client.go +++ b/aws/client.go @@ -7,14 +7,13 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "net/url" "path" "strings" "time" - "github.com/quay/zlog" - "github.com/quay/claircore/aws/internal/alas" "github.com/quay/claircore/internal/xmlutil" "github.com/quay/claircore/pkg/tmp" @@ -29,16 +28,17 @@ const ( // Client is an http for accessing ALAS mirrors. type Client struct { c *http.Client + log *slog.Logger mirrors []*url.URL } func NewClient(ctx context.Context, hc *http.Client, release Release) (*Client, error) { - ctx = zlog.ContextWithValues(ctx, "release", string(release)) if hc == nil { return nil, errors.New("http.Client not provided") } client := &Client{ c: hc, + log: slog.With("release", string(release)), mirrors: []*url.URL{}, } tctx, cancel := context.WithTimeout(ctx, defaultOpTimeout) @@ -49,22 +49,22 @@ func NewClient(ctx context.Context, hc *http.Client, release Release) (*Client, // RepoMD returns a alas.RepoMD containing sha256 information of a repositories contents func (c *Client) RepoMD(ctx context.Context) (alas.RepoMD, error) { - ctx = zlog.ContextWithValues(ctx, "component", "aws/Client.RepoMD") + log := c.log for _, mirror := range c.mirrors { m := *mirror m.Path = path.Join(m.Path, repoDataPath) - ctx := zlog.ContextWithValues(ctx, "mirror", m.String()) + log := log.With("mirror", m.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, m.String(), nil) if err != nil { - zlog.Error(ctx).Err(err).Msg("failed to make request object") + log.ErrorContext(ctx, "failed to make request object", "reason", err) continue } - zlog.Debug(ctx).Msg("attempting repomd download") + log.DebugContext(ctx, "attempting repomd download") resp, err := c.c.Do(req) if err != nil { - zlog.Error(ctx).Err(err).Msg("failed to retrieve repomd") + log.ErrorContext(ctx, "failed to retrieve repomd", "reason", err) continue } defer resp.Body.Close() @@ -73,10 +73,9 @@ func (c *Client) RepoMD(ctx context.Context) (alas.RepoMD, error) { case http.StatusOK: // break default: - zlog.Error(ctx). - Int("code", resp.StatusCode). - Str("status", resp.Status). - Msg("unexpected HTTP response") + log.ErrorContext(ctx, "unexpected HTTP response", + "code", resp.StatusCode, + "status", resp.Status) continue } @@ -84,51 +83,49 @@ func (c *Client) RepoMD(ctx context.Context) (alas.RepoMD, error) { dec := xml.NewDecoder(resp.Body) dec.CharsetReader = xmlutil.CharsetReader if err := dec.Decode(&repoMD); err != nil { - zlog.Error(ctx). - Err(err). - Msg("failed xml unmarshal") + log.ErrorContext(ctx, "failed xml unmarshal", "reason", err) continue } - zlog.Debug(ctx).Msg("success") + log.DebugContext(ctx, "success") return repoMD, nil } - zlog.Error(ctx).Msg("exhausted all mirrors") + log.ErrorContext(ctx, "exhausted all mirrors") return alas.RepoMD{}, fmt.Errorf("all mirrors failed to retrieve repo metadata") } // Updates returns the *http.Response of the first mirror to establish a connection func (c *Client) Updates(ctx context.Context) (io.ReadCloser, error) { - ctx = zlog.ContextWithValues(ctx, "component", "aws/Client.Updates") + log := c.log for _, mirror := range c.mirrors { m := *mirror m.Path = path.Join(m.Path, updatesPath) - ctx := zlog.ContextWithValues(ctx, "mirror", m.String()) + log := log.With("mirror", m.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, m.String(), nil) if err != nil { - zlog.Error(ctx).Err(err).Msg("failed to make request object") + log.ErrorContext(ctx, "failed to make request object", "reason", err) continue } tf, err := tmp.NewFile("", "") if err != nil { - zlog.Error(ctx).Err(err).Msg("failed to open temp file") + log.ErrorContext(ctx, "failed to open temp file", "reason", err) continue } var success bool defer func() { if !success { if err := tf.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close spool") + log.WarnContext(ctx, "unable to close spool", "reason", err) } } }() resp, err := c.c.Do(req) if err != nil { - zlog.Error(ctx).Err(err).Msg("failed to retrieve updates") + log.ErrorContext(ctx, "failed to retrieve updates", "reason", err) continue } defer resp.Body.Close() @@ -137,10 +134,9 @@ func (c *Client) Updates(ctx context.Context) (io.ReadCloser, error) { case http.StatusOK: // break default: - zlog.Error(ctx). - Int("code", resp.StatusCode). - Str("status", resp.Status). - Msg("unexpected HTTP response") + log.ErrorContext(ctx, "unexpected HTTP response", + "code", resp.StatusCode, + "status", resp.Status) continue } @@ -155,7 +151,7 @@ func (c *Client) Updates(ctx context.Context) (io.ReadCloser, error) { return nil, fmt.Errorf("failed to create gzip reader: %v", err) } - zlog.Debug(ctx).Msg("success") + log.DebugContext(ctx, "success") success = true return &gzippedFile{ Reader: gz, @@ -163,7 +159,7 @@ func (c *Client) Updates(ctx context.Context) (io.ReadCloser, error) { }, nil } - zlog.Error(ctx).Msg("exhausted all mirrors") + log.ErrorContext(ctx, "exhausted all mirrors") return nil, fmt.Errorf("all update_info mirrors failed to return a response") } @@ -176,7 +172,7 @@ type gzippedFile struct { } func (c *Client) getMirrors(ctx context.Context, list string) error { - ctx = zlog.ContextWithValues(ctx, "component", "aws/Client.getMirrors") + log := c.log req, err := http.NewRequestWithContext(ctx, http.MethodGet, list, nil) if err != nil { @@ -215,7 +211,6 @@ func (c *Client) getMirrors(ctx context.Context, list string) error { c.mirrors = append(c.mirrors, uu) } - zlog.Debug(ctx). - Msg("successfully got list of mirrors") + log.DebugContext(ctx, "successfully got list of mirrors") return nil } diff --git a/aws/client_test.go b/aws/client_test.go index b5f83a4a4..d1106ca31 100644 --- a/aws/client_test.go +++ b/aws/client_test.go @@ -2,13 +2,14 @@ package aws import ( "context" + "log/slog" "net/http" "net/http/httptest" "net/url" "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" + "github.com/quay/claircore/test" ) type clientTestcase struct { @@ -31,11 +32,12 @@ func (tc *clientTestcase) Run(ctx context.Context) func(*testing.T) { })) client := Client{ c: srv.Client(), + log: slog.Default(), mirrors: make([]*url.URL, 0), } return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) t.Cleanup(srv.Close) err := client.getMirrors(ctx, srv.URL) @@ -48,8 +50,9 @@ func (tc *clientTestcase) Run(ctx context.Context) func(*testing.T) { } } } + func TestClientGetMirrors(t *testing.T) { - ctx := context.Background() + ctx := test.Logging(t) tests := []clientTestcase{ { Release: AmazonLinux1, diff --git a/aws/distributionscanner.go b/aws/distributionscanner.go index ca4ac7fef..bd3fd0b19 100644 --- a/aws/distributionscanner.go +++ b/aws/distributionscanner.go @@ -3,11 +3,10 @@ package aws import ( "bytes" "context" + "log/slog" "regexp" "runtime/trace" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) @@ -71,17 +70,12 @@ func (*DistributionScanner) Kind() string { return scannerKind } // If the files are found but all regexp fail to match an empty slice is returned. func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() - ctx = zlog.ContextWithValues(ctx, - "component", "aws_dist_scanner", - "name", ds.Name(), - "version", ds.Version(), - "kind", ds.Kind(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + log := slog.With("layer", l.Hash.String(), "version", ds.Version()) + log.DebugContext(ctx, "start") + defer log.DebugContext(ctx, "done") files, err := l.Files(osReleasePath) if err != nil { - zlog.Debug(ctx).Msg("didn't find an os-release") + log.DebugContext(ctx, "didn't find an os-release") return nil, nil } for _, buff := range files { diff --git a/aws/updater.go b/aws/updater.go index 16a550ab1..7a3c895a9 100644 --- a/aws/updater.go +++ b/aws/updater.go @@ -9,8 +9,6 @@ import ( "strings" "time" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/aws/internal/alas" "github.com/quay/claircore/internal/xmlutil" @@ -43,7 +41,6 @@ func (u *Updater) Configure(ctx context.Context, _ driver.ConfigUnmarshaler, c * } func (u *Updater) Fetch(ctx context.Context, fingerprint driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { - ctx = zlog.ContextWithValues(ctx, "component", "aws/Updater.Fetch") client, err := NewClient(ctx, u.c, u.release) if err != nil { return nil, "", fmt.Errorf("failed to create client: %v", err) From 97aaeb86f54512c627df4bfce66a1da7597044e8 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 10:28:44 -0500 Subject: [PATCH 10/77] debian: migrate to slog Signed-off-by: Hank Donnay --- debian/distributionscanner.go | 27 ++++++-------- debian/distributionscanner_test.go | 8 ++--- debian/matcher_integration_test.go | 6 ++-- debian/parser.go | 6 ++-- debian/updater.go | 58 +++++++++++++----------------- 5 files changed, 43 insertions(+), 62 deletions(-) diff --git a/debian/distributionscanner.go b/debian/distributionscanner.go index 72f4bbb57..f2f643947 100644 --- a/debian/distributionscanner.go +++ b/debian/distributionscanner.go @@ -5,14 +5,13 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "regexp" "runtime/trace" "strconv" "strings" "unicode" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/osrelease" @@ -39,18 +38,15 @@ func (*DistributionScanner) Kind() string { return "distribution" } // Scan implements [indexer.DistributionScanner]. func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() - ctx = zlog.ContextWithValues(ctx, - "component", "debian/DistributionScanner.Scan", - "version", ds.Version(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + log := slog.With("version", ds.Version(), "layer", l.Hash.String()) + log.DebugContext(ctx, "start") + defer log.DebugContext(ctx, "done") sys, err := l.FS() if err != nil { return nil, fmt.Errorf("debian: unable to open layer: %w", err) } - d, err := findDist(ctx, sys) + d, err := findDist(ctx, log, sys) if err != nil { return nil, err } @@ -60,20 +56,19 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([] return []*claircore.Distribution{d}, nil } -func findDist(ctx context.Context, sys fs.FS) (*claircore.Distribution, error) { +func findDist(ctx context.Context, log *slog.Logger, sys fs.FS) (*claircore.Distribution, error) { f, err := sys.Open(osrelease.Path) switch { case errors.Is(err, nil): case errors.Is(err, fs.ErrNotExist): - zlog.Debug(ctx).Msg("no os-release file") + log.DebugContext(ctx, "no os-release file") return nil, nil default: return nil, fmt.Errorf("debian: unexpected error: %w", err) } kv, err := osrelease.Parse(ctx, f) if err != nil { - zlog.Info(ctx). - Err(err).Msg("malformed os-release file") + log.InfoContext(ctx, "malformed os-release file", "reason", err) return nil, nil } if kv[`ID`] != `debian` { @@ -89,14 +84,12 @@ func findDist(ctx context.Context, sys fs.FS) (*claircore.Distribution, error) { name = strings.TrimFunc(ver.FindString(kv[`VERSION`]), func(r rune) bool { return !unicode.IsLetter(r) }) } if name == "" || idstr == "" { - zlog.Info(ctx). - Err(err).Msg("malformed os-release file") + log.InfoContext(ctx, "malformed os-release file", "reason", err) return nil, nil } id, err := strconv.ParseInt(idstr, 10, 32) if err != nil { - zlog.Info(ctx). - Err(err).Msg("malformed os-release file") + log.InfoContext(ctx, "malformed os-release file", "reason", err) return nil, nil } return mkDist(name, int(id)), nil diff --git a/debian/distributionscanner_test.go b/debian/distributionscanner_test.go index 5a2241da4..afcdde75e 100644 --- a/debian/distributionscanner_test.go +++ b/debian/distributionscanner_test.go @@ -1,19 +1,18 @@ package debian import ( - "context" "io/fs" + "log/slog" "os" "path/filepath" "regexp" "testing" - "github.com/quay/zlog" + "github.com/quay/claircore/test" ) func TestDistributionScanner(t *testing.T) { ver := regexp.MustCompile(`^\d+ \(\w+\)$`) - ctx := zlog.Test(context.Background(), t) ents, err := os.ReadDir(`testdata/dist`) if err != nil { t.Fatal(err) @@ -31,8 +30,9 @@ func TestDistributionScanner(t *testing.T) { for tcDir, tcEnts := range testCase { for _, e := range tcEnts { t.Run(e.Name(), func(t *testing.T) { + ctx := test.Logging(t) sys := os.DirFS(filepath.Join(tcDir, e.Name())) - d, err := findDist(ctx, sys) + d, err := findDist(ctx, slog.Default(), sys) if err != nil { t.Error(err) } diff --git a/debian/matcher_integration_test.go b/debian/matcher_integration_test.go index fdcae16d1..7b157cb04 100644 --- a/debian/matcher_integration_test.go +++ b/debian/matcher_integration_test.go @@ -1,7 +1,6 @@ package debian import ( - "context" "encoding/json" "fmt" "net/http" @@ -12,14 +11,13 @@ import ( "path/filepath" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/datastore/postgres" "github.com/quay/claircore/internal/matcher" "github.com/quay/claircore/libvuln/driver" "github.com/quay/claircore/libvuln/updates" "github.com/quay/claircore/pkg/ctxlock/v2" + "github.com/quay/claircore/test" "github.com/quay/claircore/test/integration" pgtest "github.com/quay/claircore/test/postgres" ) @@ -37,7 +35,7 @@ func TestMain(m *testing.M) { // CVE data func TestMatcherIntegration(t *testing.T) { integration.NeedDB(t) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) pool := pgtest.TestMatcherDB(ctx, t) store := postgres.NewMatcherStore(pool) srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/debian/parser.go b/debian/parser.go index e9f6f0952..f1e6b03db 100644 --- a/debian/parser.go +++ b/debian/parser.go @@ -5,8 +5,7 @@ import ( "encoding/json" "fmt" "io" - - "github.com/quay/zlog" + "log/slog" "github.com/quay/claircore" ) @@ -34,8 +33,7 @@ type ReleaseData struct { // Parse implements [driver.Parser]. func (u *updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, "component", "debian/Updater.Parse") - zlog.Info(ctx).Msg("starting parse") + slog.InfoContext(ctx, "starting parse") defer r.Close() var vulnsJSON JSONData diff --git a/debian/updater.go b/debian/updater.go index 7522c3515..81ea48a39 100644 --- a/debian/updater.go +++ b/debian/updater.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "net/textproto" "net/url" @@ -15,8 +16,6 @@ import ( "strconv" "strings" - "github.com/quay/zlog" - "github.com/quay/claircore/libvuln/driver" "github.com/quay/claircore/pkg/tmp" ) @@ -184,28 +183,25 @@ Listing: dist = strings.Trim(dist, "/") rf, err := dir.Parse(path.Join(dist, `Release`)) if err != nil { - zlog.Info(ctx). - Err(err). - Stringer("context", dir). - Str("target", path.Join(dist, `Release`)). - Msg("unable to construct URL") + slog.InfoContext(ctx, "unable to construct URL", + "reason", err, + "context", dir, + "target", path.Join(dist, `Release`)) continue } req, err := http.NewRequestWithContext(ctx, http.MethodGet, rf.String(), nil) if err != nil { - zlog.Info(ctx). - Err(err). - Stringer("url", rf). - Msg("unable to construct request") + slog.InfoContext(ctx, "unable to construct request", + "reason", err, + "url", rf) continue } req.Header.Set("range", "bytes=0-512") res, err := f.c.Do(req) if err != nil { - zlog.Info(ctx). - Err(err). - Stringer("url", rf). - Msg("unable to do request") + slog.InfoContext(ctx, "unable to do request", + "reason", err, + "url", rf) continue } buf.Reset() @@ -216,31 +212,31 @@ Listing: case http.StatusNotFound: // Probably extremely old, it's fine. continue default: - zlog.Info(ctx). - Str("status", res.Status). - Stringer("url", rf). - Msg("unexpected response") + slog.InfoContext(ctx, "unexpected response", + "status", res.Status, + "url", rf) continue } tp := textproto.NewReader(bufio.NewReader(io.MultiReader(&buf, bytes.NewReader([]byte("\r\n\r\n"))))) h, err := tp.ReadMIMEHeader() if err != nil { - zlog.Info(ctx).Err(err).Msg("unable to read MIME-ish headers") + slog.InfoContext(ctx, "unable to read MIME-ish headers", + "reason", err) continue } sv := h.Get("Version") if sv == "" { - zlog.Debug(ctx).Str("dist", dist).Msg("no version assigned, skipping") + slog.DebugContext(ctx, "no version assigned, skipping", "dist", dist) continue } vs := strings.Split(sv, ".") if len(vs) == 1 { - zlog.Debug(ctx).Str("dist", dist).Msg("no version assigned, skipping") + slog.DebugContext(ctx, "no version assigned, skipping", "dist", dist) continue } ver, err := strconv.ParseInt(vs[0], 10, 32) if err != nil { - zlog.Info(ctx).Err(err).Msg("unable to parse version") + slog.InfoContext(ctx, "unable to parse version", "reason", err) continue } @@ -275,7 +271,6 @@ func (u *updater) Name() string { // Configure implements [driver.Configurable]. func (u *updater) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, "component", "debian/Updater.Configure") u.c = c var cfg UpdaterConfig if err := f(&cfg); err != nil { @@ -283,14 +278,13 @@ func (u *updater) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c * } if cfg.DistsURL != "" || cfg.OVALURL != "" { - zlog.Error(ctx).Msg("configured with deprecated URLs") + slog.ErrorContext(ctx, "configured with deprecated URLs") return fmt.Errorf("debian: neither url nor dists_url should be used anymore; use json_url and dists_urls instead") } if cfg.JSONURL != "" { u.jsonURL = cfg.JSONURL - zlog.Info(ctx). - Msg("configured JSON database URL") + slog.InfoContext(ctx, "configured JSON database URL") } return nil @@ -298,9 +292,7 @@ func (u *updater) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c * // Fetch implements [driver.Fetcher]. func (u *updater) Fetch(ctx context.Context, fingerprint driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "debian/Updater.Fetch", - "database", u.jsonURL) + log := slog.With("database", u.jsonURL) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.jsonURL, nil) if err != nil { @@ -324,7 +316,7 @@ func (u *updater) Fetch(ctx context.Context, fingerprint driver.Fingerprint) (io switch resp.StatusCode { case http.StatusOK: if fingerprint == "" || fp != string(fingerprint) { - zlog.Info(ctx).Msg("fetching latest JSON database") + log.InfoContext(ctx, "fetching latest JSON database") break } fallthrough @@ -343,7 +335,7 @@ func (u *updater) Fetch(ctx context.Context, fingerprint driver.Fingerprint) (io defer func() { if !success { if err := f.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close spool") + log.WarnContext(ctx, "unable to close spool", "reason", err) } } }() @@ -353,7 +345,7 @@ func (u *updater) Fetch(ctx context.Context, fingerprint driver.Fingerprint) (io if _, err := f.Seek(0, io.SeekStart); err != nil { return nil, "", fmt.Errorf("failed to seek body: %w", err) } - zlog.Info(ctx).Msg("fetched latest json database successfully") + log.InfoContext(ctx, "fetched latest json database successfully") success = true return f, driver.Fingerprint(fp), err From d7ef73bb4fc7a75b656d8815af66dd2d719bd92f Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 10:33:43 -0500 Subject: [PATCH 11/77] test/rhel: migrate to slog Signed-off-by: Hank Donnay --- test/rhel/rhcc_matcher_integration_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/rhel/rhcc_matcher_integration_test.go b/test/rhel/rhcc_matcher_integration_test.go index acd64a224..5cafa6bf0 100644 --- a/test/rhel/rhcc_matcher_integration_test.go +++ b/test/rhel/rhcc_matcher_integration_test.go @@ -1,7 +1,6 @@ package rhel import ( - "context" "encoding/json" "fmt" "io" @@ -9,8 +8,6 @@ import ( "path/filepath" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/datastore/postgres" match_engine "github.com/quay/claircore/internal/matcher" @@ -19,6 +16,7 @@ import ( "github.com/quay/claircore/pkg/ctxlock/v2" "github.com/quay/claircore/rhel/rhcc" "github.com/quay/claircore/rhel/vex" + "github.com/quay/claircore/test" "github.com/quay/claircore/test/integration" testpostgres "github.com/quay/claircore/test/postgres" ) @@ -67,7 +65,7 @@ func TestMatcherIntegration(t *testing.T) { } integration.NeedDB(t) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) pool := testpostgres.TestMatcherDB(ctx, t) store := postgres.NewMatcherStore(pool) locks, err := ctxlock.New(ctx, pool) From 4f297259c124c25302d7f8f71302b9b3716dd498 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 10:39:31 -0500 Subject: [PATCH 12/77] test/periodic: migrate to slog Signed-off-by: Hank Donnay --- test/periodic/enricher_test.go | 5 ++-- test/periodic/rpm_test.go | 3 +-- test/periodic/updater_test.go | 43 +++++++++++++++++----------------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/test/periodic/enricher_test.go b/test/periodic/enricher_test.go index b1f470161..69b4b19db 100644 --- a/test/periodic/enricher_test.go +++ b/test/periodic/enricher_test.go @@ -6,14 +6,13 @@ import ( "testing" "time" - "github.com/quay/zlog" - "github.com/quay/claircore/enricher/cvss" "github.com/quay/claircore/libvuln/driver" + "github.com/quay/claircore/test" ) func TestCVSS(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) e := &cvss.Enricher{} err := e.Configure(ctx, func(any) error { return nil }, pkgClient) if err != nil { diff --git a/test/periodic/rpm_test.go b/test/periodic/rpm_test.go index 8f1e12254..ed1be28c3 100644 --- a/test/periodic/rpm_test.go +++ b/test/periodic/rpm_test.go @@ -12,7 +12,6 @@ import ( "time" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/rhel" @@ -133,7 +132,7 @@ func (doc hydraDoc) Run(dir string) func(*testing.T) { } return func(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) try := 1 Retry: diff --git a/test/periodic/updater_test.go b/test/periodic/updater_test.go index fb6b64849..f74019644 100644 --- a/test/periodic/updater_test.go +++ b/test/periodic/updater_test.go @@ -7,8 +7,6 @@ import ( "time" "github.com/quay/claircore" - "github.com/quay/zlog" - "github.com/quay/claircore/alpine" "github.com/quay/claircore/aws" "github.com/quay/claircore/debian" @@ -17,6 +15,7 @@ import ( "github.com/quay/claircore/photon" "github.com/quay/claircore/rhel/vex" "github.com/quay/claircore/suse" + "github.com/quay/claircore/test" "github.com/quay/claircore/ubuntu" "github.com/quay/claircore/updater/osv" ) @@ -27,16 +26,16 @@ func noopConfigure(_ any) error { } func TestAWS(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) set, err := aws.UpdaterSet(ctx) if err != nil { t.Fatal() } - runUpdaterSet(ctx, t, set) + runUpdaterSet(t, set) } func TestAlpine(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) fac, err := alpine.NewFactory(ctx) if err != nil { t.Fatal() @@ -49,11 +48,11 @@ func TestAlpine(t *testing.T) { if err != nil { t.Fatal(err) } - runUpdaterSet(ctx, t, set) + runUpdaterSet(t, set) } func TestDebian(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) fac, err := debian.NewFactory(ctx) if err != nil { t.Fatal() @@ -66,29 +65,29 @@ func TestDebian(t *testing.T) { if err != nil { t.Fatal(err) } - runUpdaterSet(ctx, t, set) + runUpdaterSet(t, set) } func TestOracle(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) set, err := oracle.UpdaterSet(ctx) if err != nil { t.Fatal(err) } - runUpdaterSet(ctx, t, set) + runUpdaterSet(t, set) } func TestPhoton(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) set, err := photon.UpdaterSet(ctx) if err != nil { t.Fatal(err) } - runUpdaterSet(ctx, t, set) + runUpdaterSet(t, set) } func TestRHELVEX(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) fac := new(vex.Factory) err := fac.Configure(ctx, noopConfigure, pkgClient) if err != nil { @@ -98,11 +97,11 @@ func TestRHELVEX(t *testing.T) { if err != nil { t.Fatal(err) } - runUpdaterSet(ctx, t, set) + runUpdaterSet(t, set) } func TestSUSE(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) fac := new(suse.Factory) err := fac.Configure(ctx, noopConfigure, pkgClient) if err != nil { @@ -113,11 +112,11 @@ func TestSUSE(t *testing.T) { if err != nil { t.Fatal() } - runUpdaterSet(ctx, t, set) + runUpdaterSet(t, set) } func TestUbuntu(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) fac, err := ubuntu.NewFactory(ctx) if err != nil { t.Fatal(err) @@ -130,11 +129,11 @@ func TestUbuntu(t *testing.T) { if err != nil { t.Fatal(err) } - runUpdaterSet(ctx, t, set) + runUpdaterSet(t, set) } func TestOSV(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) fac := &osv.Factory{} if err := fac.Configure(ctx, noopConfigure, pkgClient); err != nil { t.Fatal(err) @@ -143,14 +142,14 @@ func TestOSV(t *testing.T) { if err != nil { t.Fatal(err) } - runUpdaterSet(ctx, t, us) + runUpdaterSet(t, us) } -func runUpdaterSet(ctx context.Context, t *testing.T, set driver.UpdaterSet) { +func runUpdaterSet(t *testing.T, set driver.UpdaterSet) { t.Helper() for _, u := range set.Updaters() { t.Run(u.Name(), func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) if cfg, ok := u.(driver.Configurable); ok { err := cfg.Configure(ctx, noopConfigure, pkgClient) if err != nil { From 0d556cd80865d502bc4cfa0d72635d3f958389b4 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 10:43:19 -0500 Subject: [PATCH 13/77] test/bisect: migrate to slog Signed-off-by: Hank Donnay --- test/bisect/main_test.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/bisect/main_test.go b/test/bisect/main_test.go index ff054675b..dbe0f1c96 100644 --- a/test/bisect/main_test.go +++ b/test/bisect/main_test.go @@ -7,6 +7,7 @@ import ( "flag" "fmt" "hash/fnv" + "log/slog" "net/http" "net/http/httptest" "os" @@ -21,8 +22,6 @@ import ( "text/template" "time" - "github.com/quay/zlog" - "github.com/rs/zerolog" "golang.org/x/sync/errgroup" "github.com/quay/claircore" @@ -217,11 +216,11 @@ func escapeImage(i string) string { // Setup does a grip of test setup work, returning a context that will cancel on // interrupt and a server set up to serve files from "dir". func setup(ctx context.Context, t *testing.T, dir string) (context.Context, *httptest.Server) { - l := zerolog.Nop() + h := slog.DiscardHandler if stderr { - l = zerolog.New(zerolog.NewConsoleWriter()) + h = slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{}) } - zlog.Set(&l) + slog.SetDefault(slog.New(h)) for _, v := range []struct { Tmpl **template.Template @@ -243,7 +242,7 @@ func setup(ctx context.Context, t *testing.T, dir string) (context.Context, *htt ctx, done := signal.NotifyContext(ctx, os.Interrupt) t.Cleanup(done) - if err := os.MkdirAll(dir, 0755); err != nil { + if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } fsrv := http.FileServer(http.Dir(dir)) From d204fa9a93298f553f29f0c7968566a10a863939 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 11:03:37 -0500 Subject: [PATCH 14/77] internal/dnf: migrate to slog Signed-off-by: Hank Donnay --- internal/dnf/dnf.go | 31 +++++++++++++++---------------- internal/dnf/dnf_test.go | 10 +++++----- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/internal/dnf/dnf.go b/internal/dnf/dnf.go index 7c6ac4962..f6c36370e 100644 --- a/internal/dnf/dnf.go +++ b/internal/dnf/dnf.go @@ -13,12 +13,12 @@ import ( "io" "io/fs" "iter" + "log/slog" "net/url" "os" "runtime" "slices" - "github.com/quay/zlog" _ "modernc.org/sqlite" // register the sqlite driver "github.com/quay/claircore" @@ -85,7 +85,7 @@ func FindRepoids(ctx context.Context, sys fs.FS) ([]string, error) { } defer func() { if err := rows.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("error closing returned rows") + slog.WarnContext(ctx, "error closing returned rows", "reason", err) } }() @@ -145,7 +145,7 @@ Stat: } defer func() { if err := f.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close fs.FS db file") + slog.WarnContext(ctx, "unable to close fs.FS db file", "reason", err) } }() @@ -157,17 +157,17 @@ Stat: } defer func() { if err := spool.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close sqlite db file") + slog.WarnContext(ctx, "unable to close sqlite db file", "reason", err) } // If in an error return, make sure to clean up the spool file. if h == nil { if err := os.Remove(spool.Name()); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to unlink sqlite db file") + slog.WarnContext(ctx, "unable to unlink sqlite db file", "reason", err) } } }() - zlog.Debug(ctx).Str("file", spool.Name()).Msg("copying sqlite db out of tar") + slog.DebugContext(ctx, "copying sqlite db out of tar", "file", spool.Name()) if _, err := io.Copy(spool, f); err != nil { return nil, fmt.Errorf("internal/dnf: error spooling sqlite db: %w", err) } @@ -181,7 +181,7 @@ Stat: } if err := db.PingContext(ctx); err != nil { if err := db.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close sqlite db") + slog.WarnContext(ctx, "unable to close sqlite db", "reason", err) } return nil, fmt.Errorf("internal/dnf: error pinging sqlite db: %w", err) } @@ -210,7 +210,7 @@ func pickQueries(ctx context.Context, db *sql.DB) (q queries, err error) { if err != nil { return q, err } - zlog.Debug(ctx).Strs("tables", names).Msg("found tables in database") + slog.DebugContext(ctx, "found tables in database", "tables", names) switch { case !slices.Contains(names, `config`): @@ -260,17 +260,16 @@ func (h *historyDB) AddRepoid(ctx context.Context, pkg *claircore.Package) error v := fmt.Sprintf("%s-%s.%s", pkg.Name, pkg.Version, pkg.Arch) // "Version" contains the EVR. ver, err := rpmver.Parse(v) if err != nil { - zlog.Warn(ctx). - Err(err). - Str("version", v). - Msg("unable to re-parse rpm version") + slog.WarnContext(ctx, "unable to re-parse rpm version", + "version", v, + "reason", err, + ) return nil } if ver.Architecture == nil { - zlog.Debug(ctx). - Err(err). - Str("version", v). - Msg("unable to parse architecture") + slog.DebugContext(ctx, "unable to parse architecture", + "reason", err, + "version", v) return nil } diff --git a/internal/dnf/dnf_test.go b/internal/dnf/dnf_test.go index 6e8a550a4..adc9d80d3 100644 --- a/internal/dnf/dnf_test.go +++ b/internal/dnf/dnf_test.go @@ -10,9 +10,9 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" + "github.com/quay/claircore/test" ) type testCase struct { @@ -24,7 +24,7 @@ type testCase struct { func (tc *testCase) Run(ctx context.Context, t *testing.T) { t.Run(tc.Name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) seq := func(yield func(claircore.Package, error) bool) { yield(tc.Package, nil) } @@ -43,7 +43,7 @@ func (tc *testCase) Run(ctx context.Context, t *testing.T) { func TestWrap(t *testing.T) { t.Run("dnf4", func(t *testing.T) { - ctx := zlog.Test(t.Context(), t) + ctx := test.Logging(t) sys := os.DirFS(filepath.Join("testdata", path.Base(t.Name()))) tcs := []testCase{ @@ -77,7 +77,7 @@ func TestWrap(t *testing.T) { } }) t.Run("dnf5", func(t *testing.T) { - ctx := zlog.Test(t.Context(), t) + ctx := test.Logging(t) sys := os.DirFS(filepath.Join("testdata", path.Base(t.Name()))) tcs := []testCase{ @@ -129,7 +129,7 @@ func TestFindRepoids(t *testing.T) { for _, tc := range tcs { t.Run(tc.Name, func(t *testing.T) { - ctx := zlog.Test(t.Context(), t) + ctx := test.Logging(t) sys := os.DirFS(filepath.Join("testdata", tc.Name)) got, err := FindRepoids(ctx, sys) From 13c3c09f450d7017d450cdbf624a230ab5d6beaf Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 11:21:55 -0500 Subject: [PATCH 15/77] internal/matcher: migrate to slog Signed-off-by: Hank Donnay --- internal/matcher/controller.go | 31 +++++++++++-------------------- internal/matcher/match.go | 10 +++------- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/internal/matcher/controller.go b/internal/matcher/controller.go index c67774506..a5e17e363 100644 --- a/internal/matcher/controller.go +++ b/internal/matcher/controller.go @@ -2,10 +2,9 @@ package matcher import ( "context" + "log/slog" "time" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/datastore" "github.com/quay/claircore/libvuln/driver" @@ -30,15 +29,12 @@ func NewController(m driver.Matcher, store datastore.Vulnerability) *Controller // Match is the entrypoint for [Controller]. func (mc *Controller) Match(ctx context.Context, records []*claircore.IndexRecord) (map[string][]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "internal/matcher/Controller.Match", - "matcher", mc.m.Name()) + log := slog.With("matcher", mc.m.Name()) // find the packages the matcher is interested in. interested := mc.findInterested(records) - zlog.Debug(ctx). - Int("interested", len(interested)). - Int("records", len(records)). - Msg("interest") + log.DebugContext(ctx, "interest", + "interested", len(interested), + "records", len(records)) // early return; do not call db at all if len(interested) == 0 { @@ -48,26 +44,23 @@ func (mc *Controller) Match(ctx context.Context, records []*claircore.IndexRecor remoteMatcher, matchedVulns, err := mc.queryRemoteMatcher(ctx, interested) if remoteMatcher { if err != nil { - zlog.Error(ctx).Err(err).Msg("remote matcher error, returning empty results") + log.ErrorContext(ctx, "remote matcher error, returning empty results", "reason", err) return map[string][]*claircore.Vulnerability{}, nil } return matchedVulns, nil } dbSide, authoritative := mc.dbFilter() - zlog.Debug(ctx). - Bool("opt-in", dbSide). - Bool("authoritative", authoritative). - Msg("version filter compatible?") + log.DebugContext(ctx, "version filter compatible?", + "opt-in", dbSide, + "authoritative", authoritative) // query the vulnstore vulns, err := mc.query(ctx, interested, dbSide) if err != nil { return nil, err } - zlog.Debug(ctx). - Int("vulnerabilities", len(vulns)). - Msg("query") + log.DebugContext(ctx, "query", "count", len(vulns)) if authoritative { return vulns, nil @@ -77,9 +70,7 @@ func (mc *Controller) Match(ctx context.Context, records []*claircore.IndexRecor if err != nil { return nil, err } - zlog.Debug(ctx). - Int("filtered", len(filteredVulns)). - Msg("filtered") + log.DebugContext(ctx, "filtered", "count", len(filteredVulns)) return filteredVulns, nil } diff --git a/internal/matcher/match.go b/internal/matcher/match.go index aa67f814a..3ba8eb372 100644 --- a/internal/matcher/match.go +++ b/internal/matcher/match.go @@ -5,11 +5,11 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "runtime" "sync" "sync/atomic" - "github.com/quay/zlog" "golang.org/x/sync/errgroup" "github.com/quay/claircore" @@ -202,15 +202,11 @@ func EnrichedMatch(ctx context.Context, ir *claircore.IndexReport, ms []driver.M for e = range eCh { kind, msg, err := e.Enrich(ectx, getter(s, e.Name()), vr) if err != nil { - zlog.Error(ctx). - Err(err). - Msg("enrichment error") + slog.ErrorContext(ctx, "enrichment error", "reason", err) continue } if len(msg) == 0 { - zlog.Debug(ctx). - Str("name", e.Name()). - Msg("enricher reported nothing, skipping") + slog.DebugContext(ctx, "enricher reported nothing, skipping", "name", e.Name()) continue } res := entry{ From 5a45d00f20ce585c31a6b5ab991b29f0ae2d362a Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 11:39:11 -0500 Subject: [PATCH 16/77] internal/rpm/bdb: migrate to slog Signed-off-by: Hank Donnay --- internal/rpm/bdb/bdb.go | 10 ++++------ internal/rpm/bdb/bdb_test.go | 8 ++------ internal/rpm/bdb/doc.go | 5 ++--- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/internal/rpm/bdb/bdb.go b/internal/rpm/bdb/bdb.go index caab00fb3..63242da0b 100644 --- a/internal/rpm/bdb/bdb.go +++ b/internal/rpm/bdb/bdb.go @@ -7,8 +7,7 @@ import ( "fmt" "io" "iter" - - "github.com/quay/zlog" + "log/slog" ) // PackageDB is the "pkgdb" a.k.a. "Packages", the raw package data. @@ -258,10 +257,9 @@ func (db *PackageDB) Headers(ctx context.Context) iter.Seq2[io.ReaderAt, error] } // Double-check we'll read the intended amount. if got, want := r.Size(), int64(offpg.Length); got != want { - zlog.Info(ctx). - Int64("got", got). - Int64("want", want). - Msg("bdb: expected data length botch") + slog.InfoContext(ctx, "expected data length botch", + "got", got, + "want", want) } if !yield(&r, nil) { diff --git a/internal/rpm/bdb/bdb_test.go b/internal/rpm/bdb/bdb_test.go index a45d06e50..e9713550b 100644 --- a/internal/rpm/bdb/bdb_test.go +++ b/internal/rpm/bdb/bdb_test.go @@ -2,19 +2,15 @@ package bdb import ( "bytes" - "context" "io/fs" "os" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore/internal/rpm/rpmdb" + "github.com/quay/claircore/test" ) func TestLoadPackage(t *testing.T) { - ctx := context.Background() - dir := os.DirFS("testdata") ms, err := fs.Glob(dir, "*Packages") if err != nil || len(ms) == 0 { @@ -22,7 +18,7 @@ func TestLoadPackage(t *testing.T) { } for _, n := range ms { t.Run(n, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) b, err := fs.ReadFile(dir, n) if err != nil { t.Fatal(err) diff --git a/internal/rpm/bdb/doc.go b/internal/rpm/bdb/doc.go index 093126a25..a44db4c9c 100644 --- a/internal/rpm/bdb/doc.go +++ b/internal/rpm/bdb/doc.go @@ -6,8 +6,7 @@ import ( "context" "encoding/binary" "io" - - "github.com/quay/zlog" + "log/slog" ) // CheckMagic looks at bit of the provided Reader to see if it looks like a @@ -28,7 +27,7 @@ func CheckMagic(ctx context.Context, r io.Reader) bool { b := make([]byte, 16) if _, err := io.ReadFull(r, b); err != nil { - zlog.Warn(ctx).Err(err).Msg("unexpected error checking magic") + slog.WarnContext(ctx, "unexpected error checking magic", "reason", err) return false } // Look at position 12 for a magic number. From a0f3ae8465ac08e249a0d2d69120ec6a8dcc9b24 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 11:43:53 -0500 Subject: [PATCH 17/77] internal/rpm/ndb: migrate to slog Signed-off-by: Hank Donnay --- internal/rpm/ndb/package_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/rpm/ndb/package_test.go b/internal/rpm/ndb/package_test.go index 3014725bd..455c94c1c 100644 --- a/internal/rpm/ndb/package_test.go +++ b/internal/rpm/ndb/package_test.go @@ -2,19 +2,14 @@ package ndb import ( "bytes" - "context" "io/fs" "os" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore/internal/rpm/rpmdb" ) func TestLoadPackage(t *testing.T) { - ctx := context.Background() - dir := os.DirFS("testdata") ms, err := fs.Glob(dir, "Packages*.db") if err != nil || len(ms) == 0 { @@ -22,7 +17,7 @@ func TestLoadPackage(t *testing.T) { } for _, n := range ms { t.Run(n, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := t.Context() b, err := fs.ReadFile(dir, n) if err != nil { t.Fatal(err) From 1bde3ae880a02460a6abcea7d10ebb92424dcbe6 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 11:45:09 -0500 Subject: [PATCH 18/77] internal/rpm/rpmdb: migrate to slog Signed-off-by: Hank Donnay --- internal/rpm/rpmdb/header_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/rpm/rpmdb/header_test.go b/internal/rpm/rpmdb/header_test.go index 6f1331dd5..e4b5f5806 100644 --- a/internal/rpm/rpmdb/header_test.go +++ b/internal/rpm/rpmdb/header_test.go @@ -1,15 +1,12 @@ package rpmdb import ( - "context" "os" "testing" - - "github.com/quay/zlog" ) func TestParseHeader(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := t.Context() f, err := os.Open(`testdata/package.header`) if err != nil { t.Fatal(err) From 6ee1f920cee8db506e179075c741b8f198512308 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 11:47:36 -0500 Subject: [PATCH 19/77] internal/rpm/sqlite: migrate to slog Signed-off-by: Hank Donnay --- internal/rpm/sqlite/sqlite_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/rpm/sqlite/sqlite_test.go b/internal/rpm/sqlite/sqlite_test.go index e22ed6c75..92a994d9f 100644 --- a/internal/rpm/sqlite/sqlite_test.go +++ b/internal/rpm/sqlite/sqlite_test.go @@ -1,7 +1,6 @@ package sqlite import ( - "context" "encoding/json" "hash/crc64" "io" @@ -10,11 +9,10 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" ) func TestPackages(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := t.Context() h := crc64.New(crc64.MakeTable(crc64.ISO)) // Copying to a tempdir is needed if the tests are being run from a prepared @@ -87,7 +85,7 @@ func TestPackages(t *testing.T) { } func TestValidate(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := t.Context() dbfile := filepath.Join(t.TempDir(), `no_packages.sqlite`) dst, err := os.Create(dbfile) From 426a8aeb51fe8f432f0ab028fabbf5a295d19d2e Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 12:05:54 -0500 Subject: [PATCH 20/77] internal/rpm: migrate to slog Signed-off-by: Hank Donnay --- internal/rpm/database.go | 34 ++++++++++------------------------ internal/rpm/files.go | 18 +++++++++++------- internal/rpm/files_test.go | 8 +++----- internal/rpm/find.go | 21 +++++++++------------ 4 files changed, 33 insertions(+), 48 deletions(-) diff --git a/internal/rpm/database.go b/internal/rpm/database.go index c8e8bb4a0..49615d9f7 100644 --- a/internal/rpm/database.go +++ b/internal/rpm/database.go @@ -5,12 +5,11 @@ import ( "fmt" "io" "iter" + "log/slog" "runtime/trace" "strings" "sync" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/internal/rpm/rpmdb" "github.com/quay/claircore/internal/rpmver" @@ -45,9 +44,7 @@ func (db *Database) Packages(ctx context.Context) iter.Seq2[claircore.Package, e var ok bool ct := 0 defer func() { - zlog.Debug(ctx). - Int("packages", ct). - Msg("processed rpm db") + slog.DebugContext(ctx, "processed rpm db", "packages", ct) }() retErr := func(err error) (cont bool) { trace.WithRegion(ctx, "internal/rpm.Database.PackagesYield", func() { cont = yield(claircore.Package{}, err) }) @@ -87,14 +84,11 @@ func (db *Database) Packages(ctx context.Context) iter.Seq2[claircore.Package, e for !ok { v, err := rpmver.Parse(srcRPM) if err != nil { - zlog.Info(ctx). - Err(err). - Msg("unable to parse SOURCERPM tag, skipping") + slog.InfoContext(ctx, "unable to parse SOURCERPM tag, skipping", "reason", err) break } if v.Name == nil { - zlog.Info(ctx). - Msg("no name parse out of SOURCERPM tag, skipping") + slog.InfoContext(ctx, "no name parse out of SOURCERPM tag, skipping") break } src := claircore.Package{ @@ -135,39 +129,31 @@ var sourceVersionWarning sync.Once func printSourceVersionWarning(ctx context.Context) { sourceVersionWarning.Do(func() { - zlog.Warn(ctx). - Strs("see-also", []string{ + slog.WarnContext(ctx, "rpm source packages always record 0 epoch; this may cause incorrect matching", + "see-also", strings.Join([]string{ `https://github.com/rpm-software-management/rpm/issues/2796`, `https://github.com/rpm-software-management/rpm/discussions/3703`, `https://github.com/rpm-software-management/rpm/pull/3755`, - }). - Msg("rpm source packages always record 0 epoch; this may cause incorrect matching") + }, " ")) }) } // PopulatePathSet adds relevant paths from the RPM database to the provided // [PathSet]. -func (db *Database) populatePathSet(ctx context.Context, s *PathSet) error { +func (db *Database) populatePathSet(ctx context.Context, s *PathSet) (int, error) { ctx, task := trace.NewTask(ctx, "internal/rpm.Database.populatePathSet") defer task.End() seq := loadPackageInfo(ctx, db.headers.Headers(ctx)) ct := 0 - defer func() { - zlog.Debug(ctx). - Int("packages", ct). - Int("files", s.len()). - Msg("processed rpm db") - }() - for info, err := range seq { if err != nil { - return err + return 0, err } ct++ info.InsertIntoSet(s) } - return nil + return ct, nil } func (db *Database) Close() error { diff --git a/internal/rpm/files.go b/internal/rpm/files.go index b5998122f..6d41b8e8e 100644 --- a/internal/rpm/files.go +++ b/internal/rpm/files.go @@ -4,13 +4,13 @@ import ( "context" "errors" "fmt" + "log/slog" "runtime" "strings" "sync" "unique" "weak" - "github.com/quay/zlog" "golang.org/x/sync/singleflight" "github.com/quay/claircore" @@ -69,18 +69,22 @@ func (c *fileCache) GetPathSet(ctx context.Context, layer *claircore.Layer) (*Pa }() for found := range seq { - zlog.Debug(ctx). - Stringer("db", found). - Msg("found possible database") + slog.DebugContext(ctx, "found possible database", "db", found) err = func() error { db, err := OpenDB(ctx, sys, found) if err != nil { return err } defer db.Close() - ctx := zlog.ContextWithValues(ctx, "db", db.String()) - zlog.Debug(ctx).Msg("examining database") - return db.populatePathSet(ctx, set) + log := slog.With("db", db) + log.DebugContext(ctx, "examining database") + ct, err := db.populatePathSet(ctx, set) + if err == nil { + log.DebugContext(ctx, "processed rpm db", + "packages", ct, + "files", set.len()) + } + return err }() if err != nil { return nil, err diff --git a/internal/rpm/files_test.go b/internal/rpm/files_test.go index a0939314c..25b3052e8 100644 --- a/internal/rpm/files_test.go +++ b/internal/rpm/files_test.go @@ -7,8 +7,6 @@ import ( "unique" "weak" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/test" ) @@ -27,7 +25,7 @@ type FileCheckerTestcase struct { func (tc *FileCheckerTestcase) Run(ctx context.Context, t *testing.T, a *test.CachedArena) { t.Run(tc.Name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) a.LoadLayerFromRegistry(ctx, t, tc.Layer) r := a.Realizer(ctx).(*test.CachedRealizer) t.Cleanup(func() { @@ -56,7 +54,7 @@ func (tc *FileCheckerTestcase) Run(ctx context.Context, t *testing.T, a *test.Ca for _, tc := range tc.Testcases { t.Run(tc.Name, func(t *testing.T) { t.Parallel() - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -74,7 +72,7 @@ func (tc *FileCheckerTestcase) Run(ctx context.Context, t *testing.T, a *test.Ca } func TestIsRPMFile(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) a := test.NewCachedArena(t) t.Cleanup(func() { diff --git a/internal/rpm/find.go b/internal/rpm/find.go index 96be11e96..b31d4cbc4 100644 --- a/internal/rpm/find.go +++ b/internal/rpm/find.go @@ -7,10 +7,10 @@ import ( "io" "io/fs" "iter" + "log/slog" "os" "path" - "github.com/quay/zlog" "golang.org/x/sync/errgroup" "github.com/quay/claircore/internal/rpm/bdb" @@ -166,7 +166,7 @@ func OpenDB(ctx context.Context, sys fs.FS, found FoundDB) (*Database, error) { } defer func() { if err := f.Close(); err != nil { - zlog.Warn(ctx).Stringer("kind", found.kind).Err(err).Msg("unable to close db") + slog.WarnContext(ctx, "unable to close db", "kind", found, "reason", err) } }() @@ -180,10 +180,9 @@ func OpenDB(ctx context.Context, sys fs.FS, found FoundDB) (*Database, error) { if err != nil { return nil, fmt.Errorf("internal/rpm: error spooling db: %w", err) } - ctx = zlog.ContextWithValues(ctx, "file", spool.Name()) + log := slog.With("file", spool.Name()) cleanup.spool = spool - zlog.Debug(ctx). - Msg("copying db out of fs.FS") + log.DebugContext(ctx, "copying db out of fs.FS") // Need to have the file linked into the filesystem for the sqlite package. // @@ -206,12 +205,12 @@ func OpenDB(ctx context.Context, sys fs.FS, found FoundDB) (*Database, error) { // [this post]: https://sqlite.org/forum/info/57aaaf20cf703d301fed5aeaef59e70723f1d9454fb3a4e6383b2bfac6897e5a if _, err := io.Copy(spool, f); err != nil { if err := spool.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close spool") + log.WarnContext(ctx, "unable to close spool", "reason", err) } return nil, fmt.Errorf("internal/rpm: error spooling db: %w", err) } if err := spool.Sync(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to sync spool; results may be Weird") + log.WarnContext(ctx, "unable to sync spool; results may be Weird", "reason", err) } switch found.kind { @@ -219,10 +218,10 @@ func OpenDB(ctx context.Context, sys fs.FS, found FoundDB) (*Database, error) { sdb, err := sqlite.Open(spool.Name()) if err != nil { if err := spool.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close spool") + log.WarnContext(ctx, "unable to close spool", "reason", err) } if err := os.Remove(spool.Name()); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to remove spool") + log.WarnContext(ctx, "unable to remove spool", "reason", err) } return nil, fmt.Errorf("internal/rpm: unable to open sqlite db: %w", err) } @@ -243,9 +242,7 @@ func OpenDB(ctx context.Context, sys fs.FS, found FoundDB) (*Database, error) { default: panic("unreachable") } - zlog.Debug(ctx). - Stringer("db", found). - Msg("opened database") + log.DebugContext(ctx, "opened database", "db", found) if v, ok := db.headers.(validator); ok { if err := v.Validate(ctx); err != nil { From c0a042d6c4cc7ecb4fb4f8285ccdc1d35bcf0660 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 14:11:04 -0500 Subject: [PATCH 21/77] datastore/postgres: migrate to slog Signed-off-by: Hank Donnay --- .../postgres/affected_manifests_e2e_test.go | 10 +++---- datastore/postgres/affectedmanifest.go | 14 ++++----- datastore/postgres/connect.go | 4 +-- datastore/postgres/deletemanifests.go | 25 ++++++---------- datastore/postgres/deletemanifests_test.go | 20 ++++++------- datastore/postgres/enrichment.go | 21 +++++--------- datastore/postgres/gc.go | 18 ++++-------- datastore/postgres/gc_test.go | 4 +-- datastore/postgres/get.go | 10 +++---- .../postgres/getupdateoperationdiff_test.go | 5 ++-- datastore/postgres/getupdateoperations.go | 13 ++------- datastore/postgres/index_e2e_test.go | 21 +++++++------- datastore/postgres/indexmanifest.go | 5 ++-- datastore/postgres/indexpackage.go | 18 +++++------- .../postgres/indexpackage_benchmark_test.go | 7 +---- datastore/postgres/matcher_store.go | 2 -- .../packagesbylayer_benchmark_test.go | 6 +--- datastore/postgres/querybuilder_test.go | 6 ++-- datastore/postgres/recordupdatetime.go | 29 +++++-------------- datastore/postgres/setlayerscanned.go | 2 -- datastore/postgres/update_e2e_test.go | 15 +++++----- datastore/postgres/updatevulnerabilities.go | 20 +++++-------- .../postgres/updatevulnerabilities_test.go | 6 ++-- 23 files changed, 104 insertions(+), 177 deletions(-) diff --git a/datastore/postgres/affected_manifests_e2e_test.go b/datastore/postgres/affected_manifests_e2e_test.go index f56aa4180..72c347721 100644 --- a/datastore/postgres/affected_manifests_e2e_test.go +++ b/datastore/postgres/affected_manifests_e2e_test.go @@ -8,11 +8,11 @@ import ( "testing" "github.com/jackc/pgx/v5/pgxpool" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/pkg/omnimatcher" + "github.com/quay/claircore/test" "github.com/quay/claircore/test/integration" pgtest "github.com/quay/claircore/test/postgres" ) @@ -27,7 +27,7 @@ type affectedE2E struct { func TestAffectedE2E(t *testing.T) { integration.NeedDB(t) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) pool := pgtest.TestIndexerDB(ctx, t) store := NewIndexerStore(pool) @@ -161,7 +161,7 @@ func (e *affectedE2E) Run(t *testing.T) { // this is required so foreign key constraints do not // fail in later tests. func (e *affectedE2E) IndexArtifacts(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) const ( insertManifest = ` INSERT INTO manifest @@ -255,7 +255,7 @@ func (e *affectedE2E) IndexArtifacts(t *testing.T) { // IndexManifest confirms the contents of a manifest // can be written to the manifest index table. func (e *affectedE2E) IndexManifest(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) err := e.store.IndexManifest(ctx, &e.ir) if err != nil { t.Fatalf("failed to index manifest: %v", err) @@ -266,7 +266,7 @@ func (e *affectedE2E) IndexManifest(t *testing.T) { // in the vulnereability report reports the associated // manifest is affected. func (e *affectedE2E) AffectedManifests(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) om := omnimatcher.New(nil) for _, vuln := range e.vr.Vulnerabilities { hashes, err := e.store.AffectedManifests(ctx, *vuln, om.Vulnerable) diff --git a/datastore/postgres/affectedmanifest.go b/datastore/postgres/affectedmanifest.go index 16448f63b..2bc51152e 100644 --- a/datastore/postgres/affectedmanifest.go +++ b/datastore/postgres/affectedmanifest.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "strconv" "time" @@ -12,7 +13,6 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "github.com/quay/claircore" ) @@ -107,7 +107,6 @@ WHERE ); ` ) - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/affectedManifests") // confirm the incoming vuln can be // resolved into a prototype index record @@ -160,7 +159,7 @@ WHERE pkg.ID = idStr pkgsToFilter = append(pkgsToFilter, pkg) } - zlog.Debug(ctx).Int("count", len(pkgsToFilter)).Msg("packages to filter") + slog.DebugContext(ctx, "packages to filter", "count", len(pkgsToFilter)) if err := rows.Err(); err != nil { return nil, fmt.Errorf("error scanning packages: %w", err) } @@ -183,7 +182,7 @@ WHERE }) } } - zlog.Debug(ctx).Int("count", len(filteredRecords)).Msg("vulnerable index records") + slog.DebugContext(ctx, "vulnerable index records", "count", len(filteredRecords)) // Query the manifest index for manifests containing the vulnerable // IndexRecords and create a set containing each unique manifest. @@ -232,7 +231,7 @@ WHERE return nil, err } } - zlog.Debug(ctx).Int("count", len(out)).Msg("affected manifests") + slog.DebugContext(ctx, "affected manifests", "count", len(out)) return out, nil } @@ -262,7 +261,6 @@ func protoRecord(ctx context.Context, pool *pgxpool.Pool, v claircore.Vulnerabil ` timeout = 5 * time.Second ) - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/protoRecord") protoRecord := claircore.IndexRecord{} // fill dist into prototype index record if exists @@ -302,7 +300,7 @@ func protoRecord(ctx context.Context, pool *pgxpool.Pool, v claircore.Vulnerabil VersionCodeName: v.Dist.VersionCodeName, VersionID: v.Dist.VersionID, } - zlog.Debug(ctx).Str("id", id).Msg("discovered distribution id") + slog.DebugContext(ctx, "discovered distribution id", "id", id) } } @@ -332,7 +330,7 @@ func protoRecord(ctx context.Context, pool *pgxpool.Pool, v claircore.Vulnerabil Name: v.Repo.Name, URI: v.Repo.URI, } - zlog.Debug(ctx).Str("id", id).Msg("discovered repo id") + slog.DebugContext(ctx, "discovered repo id", "id", id) } } diff --git a/datastore/postgres/connect.go b/datastore/postgres/connect.go index e9f0938c5..5a586664d 100644 --- a/datastore/postgres/connect.go +++ b/datastore/postgres/connect.go @@ -3,10 +3,10 @@ package postgres import ( "context" "fmt" + "log/slog" "github.com/jackc/pgx/v5/pgxpool" "github.com/prometheus/client_golang/prometheus" - "github.com/quay/zlog" "github.com/quay/claircore/datastore/postgres/types" "github.com/quay/claircore/pkg/poolstats" @@ -33,7 +33,7 @@ func Connect(ctx context.Context, connString string, applicationName string) (*p } if err := prometheus.Register(poolstats.NewCollector(pool, applicationName)); err != nil { - zlog.Info(ctx).Msg("pool metrics already registered") + slog.InfoContext(ctx, "pool metrics already registered") } return pool, nil diff --git a/datastore/postgres/deletemanifests.go b/datastore/postgres/deletemanifests.go index fd10616a5..1545f7b29 100644 --- a/datastore/postgres/deletemanifests.go +++ b/datastore/postgres/deletemanifests.go @@ -4,11 +4,11 @@ import ( "context" "errors" "fmt" + "log/slog" "github.com/jackc/pgx/v5" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/internal/dblock" @@ -43,7 +43,6 @@ var errLockFail = errors.New("lock failed") // The returned slice indicates the successfully deleted manifests. An error is // reported if none were able to be deleted. func (s *IndexerStore) DeleteManifests(ctx context.Context, ds ...claircore.Digest) ([]claircore.Digest, error) { - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/DeleteManifests") const ( getManifestID = `SELECT id FROM manifest WHERE hash = $1` getLayers = `SELECT layer_id FROM manifest_layer WHERE manifest_id = $1;` @@ -135,10 +134,9 @@ func (s *IndexerStore) DeleteManifests(ctx context.Context, ds ...claircore.Dige return fmt.Errorf("unable check layer usage: %w", err) } ra := tag.RowsAffected() - zlog.Debug(ctx). - Int64("count", ra). - Str("manifest", d.String()). - Msg("deleted layers for manifest") + slog.DebugContext(ctx, "deleted layers for manifest", + "count", ra, + "manifest", d) } deletedManifests = append(deletedManifests, d) return nil @@ -146,25 +144,20 @@ func (s *IndexerStore) DeleteManifests(ctx context.Context, ds ...claircore.Dige switch { case attemptErr == nil: case errors.Is(attemptErr, errLockFail): - zlog.Debug(ctx). - Stringer("manifest", d). - Msg("unable to obtain lock") + slog.DebugContext(ctx, "unable to obtain lock", "manifest", d) default: errs = append(errs, fmt.Errorf("%s: %w", d, attemptErr)) } } - zlog.Debug(ctx). - Int("count", len(deletedManifests)). - Int("nonexistant", len(ds)-len(deletedManifests)). - Msg("deleted manifests") + slog.DebugContext(ctx, "deleted manifests", + "count", len(deletedManifests), + "nonexistant", len(ds)-len(deletedManifests)) if len(deletedManifests) == 0 { err = errors.Join(errs...) return nil, err } if len(errs) > 0 { - zlog.Warn(ctx). - Errs("reason", errs). - Msg("unexpected errors") + slog.WarnContext(ctx, "unexpected errors", "reason", errs) } return deletedManifests, nil } diff --git a/datastore/postgres/deletemanifests_test.go b/datastore/postgres/deletemanifests_test.go index 51d29be92..f01a7083f 100644 --- a/datastore/postgres/deletemanifests_test.go +++ b/datastore/postgres/deletemanifests_test.go @@ -1,11 +1,9 @@ package postgres import ( - "context" "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -17,13 +15,13 @@ import ( func TestDeleteManifests(t *testing.T) { integration.NeedDB(t) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) pool := pgtest.TestIndexerDB(ctx, t) store := NewIndexerStore(pool) defer store.Close(ctx) t.Run("Nonexistent", func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) in := []claircore.Digest{ test.RandomSHA256Digest(t), } @@ -36,7 +34,7 @@ func TestDeleteManifests(t *testing.T) { } }) t.Run("NonexistentMulti", func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) in := []claircore.Digest{ test.RandomSHA256Digest(t), test.RandomSHA256Digest(t), @@ -54,7 +52,7 @@ func TestDeleteManifests(t *testing.T) { }) const insertManifest = `INSERT INTO manifest (hash) SELECT unnest($1::TEXT[]);` t.Run("One", func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) want := []claircore.Digest{ test.RandomSHA256Digest(t), } @@ -70,7 +68,7 @@ func TestDeleteManifests(t *testing.T) { } }) t.Run("Locked", func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) want := []claircore.Digest{ test.RandomSHA256Digest(t), test.RandomSHA256Digest(t), @@ -107,7 +105,7 @@ func TestDeleteManifests(t *testing.T) { } }) t.Run("Subset", func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) in := make([]claircore.Digest, 8) for i := range in { in[i] = test.RandomSHA256Digest(t) @@ -139,7 +137,7 @@ INSERT INTO manifest_layer (i, manifest_id, layer_id) nManifests = 8 layersPer = 4 ) - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) ms := make([]claircore.Digest, nManifests) for i := range ms { ms[i] = test.RandomSHA256Digest(t) @@ -207,7 +205,7 @@ INSERT INTO manifest_layer (i, manifest_id, layer_id) nManifests = 8 nonBaseLayersPer = 3 ) - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) ms := make([]claircore.Digest, nManifests) for i := range ms { ms[i] = test.RandomSHA256Digest(t) @@ -289,7 +287,7 @@ INSERT INTO manifest_layer (i, manifest_id, layer_id) packageN = 10 ) s := NewIndexerStore(pool) - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) toDelete := make([]claircore.Digest, manifestsN) for i := range manifestsN { ir := &claircore.IndexReport{} diff --git a/datastore/postgres/enrichment.go b/datastore/postgres/enrichment.go index b3b6dd7d7..0d7edb6ef 100644 --- a/datastore/postgres/enrichment.go +++ b/datastore/postgres/enrichment.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "log/slog" "sort" "strconv" "time" @@ -15,7 +16,6 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "github.com/quay/claircore/datastore" "github.com/quay/claircore/libvuln/driver" @@ -70,7 +70,6 @@ var ( ) func (s *MatcherStore) UpdateEnrichmentsIter(ctx context.Context, updater string, fp driver.Fingerprint, it datastore.EnrichmentIter) (uuid.UUID, error) { - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/MatcherStore.UpdateEnrichmentsIter") return s.updateEnrichments(ctx, updater, fp, it) } @@ -78,7 +77,6 @@ func (s *MatcherStore) UpdateEnrichmentsIter(ctx context.Context, updater string // EnrichmentRecord(s), and ensures enrichments from previous updates are not // queried by clients. func (s *MatcherStore) UpdateEnrichments(ctx context.Context, updater string, fp driver.Fingerprint, es []driver.EnrichmentRecord) (uuid.UUID, error) { - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/MatcherStore.UpdateEnrichments") enIter := func(yield func(record *driver.EnrichmentRecord, err error) bool) { for i := range es { if !yield(&es[i], nil) { @@ -133,7 +131,6 @@ DO NOTHING;` refreshView = `REFRESH MATERIALIZED VIEW CONCURRENTLY latest_update_operations;` ) - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/UpdateEnrichments") var id uint64 var ref uuid.UUID @@ -146,9 +143,7 @@ DO } updateEnrichmentsCounter.WithLabelValues("create").Add(1) updateEnrichmentsDuration.WithLabelValues("create").Observe(time.Since(start).Seconds()) - zlog.Debug(ctx). - Str("ref", ref.String()). - Msg("update_operation created") + slog.DebugContext(ctx, "update_operation created", "ref", ref) return nil }) if err != nil { @@ -180,7 +175,9 @@ DO // Periodically refresh planner statistics during bulk inserts to avoid stalls if enCt%analyzeEveryNRecords == 0 { if _, analyzeErr := s.pool.Exec(ctx, "ANALYZE enrichment"); analyzeErr != nil { - zlog.Warn(ctx).Err(analyzeErr).Int("record_count", enCt).Msg("failed to analyze enrichment table during processing") + slog.WarnContext(ctx, "failed to analyze enrichment table during processing", + "record_count", enCt, + "reason", analyzeErr) } } @@ -213,10 +210,9 @@ DO if _, err = s.pool.Exec(ctx, refreshView); err != nil { return uuid.Nil, fmt.Errorf("could not refresh latest_update_operations: %w", err) } - zlog.Debug(ctx). - Stringer("ref", ref). - Int("inserted", enCt). - Msg("update_operation committed") + slog.DebugContext(ctx, "update_operation committed", + "ref", ref, + "inserted", enCt) return ref, nil } @@ -256,7 +252,6 @@ where and l.kind = 'enrichment' limit 1;` - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/GetEnrichment") timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) { getEnrichmentsDuration.WithLabelValues("query", strconv.FormatBool(errors.Is(err, nil))).Observe(v) })) diff --git a/datastore/postgres/gc.go b/datastore/postgres/gc.go index 756203ffd..3bdd9f50d 100644 --- a/datastore/postgres/gc.go +++ b/datastore/postgres/gc.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "runtime" "strings" "time" @@ -12,7 +13,6 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "golang.org/x/sync/semaphore" "github.com/quay/claircore/libvuln/driver" @@ -55,8 +55,6 @@ const ( // If a full GC is required run this method until the returned int64 value // is 0. func (s *MatcherStore) GC(ctx context.Context, keep int) (int64, error) { - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/GC") - // obtain update operations which need deletin' ops, totalOps, err := eligibleUpdateOpts(ctx, s.pool, keep) if err != nil { @@ -94,7 +92,7 @@ func (s *MatcherStore) GC(ctx context.Context, keep int) (int64, error) { case driver.EnrichmentKind: cleanup = enrichmentCleanup default: - zlog.Error(ctx).Str("kind", string(kind)).Msg("unknown updater kind; skipping cleanup") + slog.ErrorContext(ctx, "unknown updater kind; skipping cleanup", "kind", string(kind)) continue } for _, u := range us { @@ -229,15 +227,13 @@ AND v1.id = v2.id; ) start := time.Now() - ctx = zlog.ContextWithValues(ctx, "updater", updater) - zlog.Debug(ctx). - Msg("starting vuln clean up") + slog.DebugContext(ctx, "starting vuln clean up") res, err := pool.Exec(ctx, deleteOrphanedVulns, updater) if err != nil { gcCounter.WithLabelValues("deleteVulns", "false").Inc() return fmt.Errorf("failed while exec'ing vuln delete: %w", err) } - zlog.Debug(ctx).Int64("rows affected", res.RowsAffected()).Msg("vulns deleted") + slog.DebugContext(ctx, "vulns deleted", "rows affected", res.RowsAffected()) gcCounter.WithLabelValues("deleteVulns", "true").Inc() gcDuration.WithLabelValues("deleteVulns").Observe(time.Since(start).Seconds()) @@ -258,15 +254,13 @@ DELETE FROM enrichment e1 USING ) start := time.Now() - ctx = zlog.ContextWithValues(ctx, "updater", updater) - zlog.Debug(ctx). - Msg("starting enrichment clean up") + slog.DebugContext(ctx, "starting enrichment clean up") res, err := pool.Exec(ctx, deleteOrphanedEnrichments, updater) if err != nil { gcCounter.WithLabelValues("deleteEnrichments", "false").Inc() return fmt.Errorf("failed while exec'ing enrichment delete: %w", err) } - zlog.Debug(ctx).Int64("rows affected", res.RowsAffected()).Msg("enrichments deleted") + slog.DebugContext(ctx, "enrichments deleted", "rows affected", res.RowsAffected()) gcCounter.WithLabelValues("deleteEnrichments", "true").Inc() gcDuration.WithLabelValues("deleteEnrichments").Observe(time.Since(start).Seconds()) diff --git a/datastore/postgres/gc_test.go b/datastore/postgres/gc_test.go index b95538688..d00013cb0 100644 --- a/datastore/postgres/gc_test.go +++ b/datastore/postgres/gc_test.go @@ -9,8 +9,6 @@ import ( "net/http" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" "github.com/quay/claircore/libvuln/updates" @@ -146,7 +144,7 @@ func TestGC(t *testing.T) { for _, tt := range table { t.Run(tt.name, func(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) pool := pgtest.TestMatcherDB(ctx, t) store := NewMatcherStore(pool) locks, err := ctxlock.New(ctx, pool) diff --git a/datastore/postgres/get.go b/datastore/postgres/get.go index 0d732c10f..5ca8c7eaf 100644 --- a/datastore/postgres/get.go +++ b/datastore/postgres/get.go @@ -3,13 +3,13 @@ package postgres import ( "context" "fmt" + "log/slog" "strconv" "time" "github.com/jackc/pgx/v5" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/datastore" @@ -43,7 +43,6 @@ type recordQuery struct { // Get implements vulnstore.Vulnerability. func (s *MatcherStore) Get(ctx context.Context, records []*claircore.IndexRecord, opts datastore.GetOpts) (map[string][]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, "component", "internal/vulnstore/postgres/Get") tx, err := s.pool.Begin(ctx) if err != nil { return nil, err @@ -57,10 +56,9 @@ func (s *MatcherStore) Get(ctx context.Context, records []*claircore.IndexRecord query, err := buildGetQuery(record, &opts) if err != nil { // if we cannot build a query for an individual record continue to the next - zlog.Debug(ctx). - Err(err). - Str("record", fmt.Sprintf("%+v", record)). - Msg("could not build query for record") + slog.DebugContext(ctx, "could not build query for record", + "reason", err, + "record", record) continue } rqs = append(rqs, &recordQuery{query: query, record: record}) diff --git a/datastore/postgres/getupdateoperationdiff_test.go b/datastore/postgres/getupdateoperationdiff_test.go index f377875e4..51f914461 100644 --- a/datastore/postgres/getupdateoperationdiff_test.go +++ b/datastore/postgres/getupdateoperationdiff_test.go @@ -1,15 +1,14 @@ package postgres import ( - "context" "fmt" "testing" "github.com/google/uuid" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" + "github.com/quay/claircore/test" "github.com/quay/claircore/test/integration" pgtest "github.com/quay/claircore/test/postgres" ) @@ -26,7 +25,7 @@ type diffTestCase struct { // are captured there, e.g. if there's no difference between the two operations. func TestGetUpdateDiff(t *testing.T) { integration.NeedDB(t) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) cases := []diffTestCase{ // second op adds two new vulns diff --git a/datastore/postgres/getupdateoperations.go b/datastore/postgres/getupdateoperations.go index a808661e3..a71b9444d 100644 --- a/datastore/postgres/getupdateoperations.go +++ b/datastore/postgres/getupdateoperations.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "time" "github.com/google/uuid" @@ -11,7 +12,6 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "github.com/quay/claircore/libvuln/driver" ) @@ -80,7 +80,6 @@ func (s *MatcherStore) GetLatestUpdateRef(ctx context.Context, kind driver.Updat queryEnrichment = `SELECT ref FROM update_operation WHERE kind = 'enrichment' ORDER BY id USING > LIMIT 1;` queryVulnerability = `SELECT ref FROM update_operation WHERE kind = 'vulnerability' ORDER BY id USING > LIMIT 1;` ) - ctx = zlog.ContextWithValues(ctx, "component", "internal/vulnstore/postgres/getLatestRef") var q string var label string @@ -156,15 +155,12 @@ func (s *MatcherStore) GetLatestUpdateRefs(ctx context.Context, kind driver.Upda } ret[uo.Updater] = ops } - zlog.Debug(ctx). - Int("count", len(ret)). - Msg("found updaters") + slog.DebugContext(ctx, "found updaters", "count", len(ret)) return ret, nil } func getLatestRefs(ctx context.Context, pool *pgxpool.Pool) (map[string][]driver.UpdateOperation, error) { const query = `SELECT DISTINCT ON (updater) updater, ref, fingerprint, date FROM update_operation ORDER BY updater, id USING >;` - ctx = zlog.ContextWithValues(ctx, "component", "internal/vulnstore/postgres/getLatestRefs") start := time.Now() @@ -195,9 +191,7 @@ func getLatestRefs(ctx context.Context, pool *pgxpool.Pool) (map[string][]driver } ret[uo.Updater] = ops } - zlog.Debug(ctx). - Int("count", len(ret)). - Msg("found updaters") + slog.DebugContext(ctx, "found updaters", "count", len(ret)) return ret, nil } @@ -208,7 +202,6 @@ func (s *MatcherStore) GetUpdateOperations(ctx context.Context, kind driver.Upda queryEnrichment = `SELECT ref, updater, fingerprint, date FROM update_operation WHERE updater = ANY($1) AND kind = 'enrichment' ORDER BY id DESC;` getUpdaters = `SELECT DISTINCT(updater) FROM update_operation;` ) - ctx = zlog.ContextWithValues(ctx, "component", "internal/vulnstore/postgres/getUpdateOperations") tx, err := s.pool.Begin(ctx) if err != nil { diff --git a/datastore/postgres/index_e2e_test.go b/datastore/postgres/index_e2e_test.go index 62e267805..d85f2cc6c 100644 --- a/datastore/postgres/index_e2e_test.go +++ b/datastore/postgres/index_e2e_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -26,9 +25,11 @@ type mockScnr struct { func (m mockScnr) Name() string { return m.name } + func (m mockScnr) Kind() string { return m.kind } + func (m mockScnr) Version() string { return m.version } @@ -220,7 +221,7 @@ func (e *indexE2e) Run(t *testing.T) { // Manifest and Layer identities so layer code // foreign key references do not fail. func (e *indexE2e) PersistManifest(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) err := e.store.PersistManifest(ctx, e.manifest) if err != nil { t.Fatalf("failed to persist manifest: %v", err) @@ -230,7 +231,7 @@ func (e *indexE2e) PersistManifest(t *testing.T) { // RegisterScanner confirms a scanner can be registered // and provides this scanner for other subtests to use func (e *indexE2e) RegisterScanner(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) err := e.store.RegisterScanners(ctx, e.scnrs) if err != nil { t.Fatalf("failed to register scnr: %v", err) @@ -241,7 +242,7 @@ func (e *indexE2e) RegisterScanner(t *testing.T) { // selecting packages associated with a layer works // correctly. func (e *indexE2e) IndexAndRetrievePackages(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) A := test.GenUniquePackages(e.packageGen) for _, scnr := range e.scnrs { @@ -265,7 +266,7 @@ func (e *indexE2e) IndexAndRetrievePackages(t *testing.T) { // selecting distributions associated with a layer works // correctly. func (e *indexE2e) IndexAndRetrieveDistributions(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) A := test.GenUniqueDistributions(e.distGen) for _, scnr := range e.scnrs { @@ -289,7 +290,7 @@ func (e *indexE2e) IndexAndRetrieveDistributions(t *testing.T) { // selecting repositories associated with a layer works // correctly. func (e *indexE2e) IndexAndRetrieveRepos(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) A := test.GenUniqueRepositories(e.repoGen) for _, scnr := range e.scnrs { @@ -312,7 +313,7 @@ func (e *indexE2e) IndexAndRetrieveRepos(t *testing.T) { // LayerScanned confirms the book keeping involved in marking a layer // scanned works correctly. func (e *indexE2e) LayerScanned(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) for _, scnr := range e.scnrs { err := e.store.SetLayerScanned(ctx, e.manifest.Layers[0].Hash, scnr) if err != nil { @@ -332,7 +333,7 @@ func (e *indexE2e) LayerScanned(t *testing.T) { // LayerScannedNotExists confirms an error is returned when attempting // to obtain if a layer was scanned by a non-existent scanner. func (e *indexE2e) LayerScannedNotExists(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) scnr := mockScnr{ name: "invalid", kind: "invalid", @@ -348,7 +349,7 @@ func (e *indexE2e) LayerScannedNotExists(t *testing.T) { // LayerScannedFalse confirms a false boolean is returned when attempting // to obtain if a non-existent layer was scanned by a valid scanner func (e *indexE2e) LayerScannedFalse(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) // create a layer that has not been persisted to the store layer := &claircore.Layer{ @@ -367,7 +368,7 @@ func (e *indexE2e) LayerScannedFalse(t *testing.T) { // IndexReport confirms the book keeping around index reports works // correctly. func (e *indexE2e) IndexReport(t *testing.T) { - ctx := zlog.Test(e.ctx, t) + ctx := test.Logging(t, e.ctx) A := &claircore.IndexReport{ Hash: e.manifest.Hash, diff --git a/datastore/postgres/indexmanifest.go b/datastore/postgres/indexmanifest.go index 014cd4c4b..cc905576a 100644 --- a/datastore/postgres/indexmanifest.go +++ b/datastore/postgres/indexmanifest.go @@ -3,13 +3,13 @@ package postgres import ( "context" "fmt" + "log/slog" "strconv" "time" "github.com/jackc/pgx/v5" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "github.com/quay/claircore" ) @@ -50,7 +50,6 @@ func (s *IndexerStore) IndexManifest(ctx context.Context, ir *claircore.IndexRep ON CONFLICT DO NOTHING; ` ) - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/IndexerStore.IndexManifest") if ir.Hash.String() == "" { return fmt.Errorf("received empty hash; cannot associate contents with a manifest hash") @@ -59,7 +58,7 @@ func (s *IndexerStore) IndexManifest(ctx context.Context, ir *claircore.IndexRep records := ir.IndexRecords() if len(records) == 0 { - zlog.Warn(ctx).Msg("manifest being indexed has 0 index records") + slog.WarnContext(ctx, "manifest being indexed has 0 index records") return nil } diff --git a/datastore/postgres/indexpackage.go b/datastore/postgres/indexpackage.go index eef3fe9ae..ee970fc01 100644 --- a/datastore/postgres/indexpackage.go +++ b/datastore/postgres/indexpackage.go @@ -4,12 +4,12 @@ import ( "cmp" "context" "fmt" + "log/slog" "time" "github.com/jackc/pgx/v5" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -99,8 +99,6 @@ func (s *IndexerStore) IndexPackages(ctx context.Context, pkgs []*claircore.Pack ` ) - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/IndexerStore.IndexPackages") - var batch, assocBatch pgx.Batch skipCt := 0 queueInsert := func(pkg *claircore.Package) { @@ -155,19 +153,17 @@ func (s *IndexerStore) IndexPackages(ctx context.Context, pkgs []*claircore.Pack if err != nil { return err } - zlog.Debug(ctx). - Int("skipped", skipCt). - Int("inserted", len(pkgs)-skipCt). - Msg("packages inserted") + slog.DebugContext(ctx, "packages inserted", + "skipped", skipCt, + "inserted", len(pkgs)-skipCt) start = time.Now() err = tx.SendBatch(ctx, &assocBatch).Close() indexPackageCounter.WithLabelValues("insertWith_batch").Add(1) indexPackageDuration.WithLabelValues("insertWith_batch").Observe(time.Since(start).Seconds()) - zlog.Debug(ctx). - Int("skipped", skipCt). - Int("inserted", len(pkgs)-skipCt). - Msg("scanartifacts inserted") + slog.DebugContext(ctx, "scanartifacts inserted", + "skipped", skipCt, + "inserted", len(pkgs)-skipCt) if err != nil { return err } diff --git a/datastore/postgres/indexpackage_benchmark_test.go b/datastore/postgres/indexpackage_benchmark_test.go index 19bbdd57a..8578a8619 100644 --- a/datastore/postgres/indexpackage_benchmark_test.go +++ b/datastore/postgres/indexpackage_benchmark_test.go @@ -1,11 +1,8 @@ package postgres import ( - "context" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/test" "github.com/quay/claircore/test/integration" @@ -14,7 +11,6 @@ import ( func Benchmark_IndexPackages(b *testing.B) { integration.NeedDB(b) - ctx := context.Background() benchmarks := []struct { // the name of this benchmark name string @@ -179,7 +175,7 @@ func Benchmark_IndexPackages(b *testing.B) { for _, bench := range benchmarks { b.Run(bench.name, func(b *testing.B) { - ctx := zlog.Test(ctx, b) + ctx := test.Logging(b) pool := pgtest.TestIndexerDB(ctx, b) store := NewIndexerStore(pool) @@ -215,5 +211,4 @@ func Benchmark_IndexPackages(b *testing.B) { } }) } - } diff --git a/datastore/postgres/matcher_store.go b/datastore/postgres/matcher_store.go index 2f980cf43..218b01049 100644 --- a/datastore/postgres/matcher_store.go +++ b/datastore/postgres/matcher_store.go @@ -8,7 +8,6 @@ import ( "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/stdlib" - "github.com/quay/zlog" "github.com/remind101/migrate" "github.com/quay/claircore/datastore" @@ -56,7 +55,6 @@ var ( // DeleteUpdateOperations implements vulnstore.Updater. func (s *MatcherStore) DeleteUpdateOperations(ctx context.Context, id ...uuid.UUID) (int64, error) { const query = `DELETE FROM update_operation WHERE ref = ANY($1::uuid[]);` - ctx = zlog.ContextWithValues(ctx, "component", "internal/vulnstore/postgres/deleteUpdateOperations") if len(id) == 0 { return 0, nil } diff --git a/datastore/postgres/packagesbylayer_benchmark_test.go b/datastore/postgres/packagesbylayer_benchmark_test.go index 2818aae30..1fa9d75de 100644 --- a/datastore/postgres/packagesbylayer_benchmark_test.go +++ b/datastore/postgres/packagesbylayer_benchmark_test.go @@ -1,11 +1,8 @@ package postgres import ( - "context" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/test" "github.com/quay/claircore/test/integration" @@ -14,7 +11,6 @@ import ( func Benchmark_PackagesByLayer(b *testing.B) { integration.NeedDB(b) - ctx := context.Background() benchmarks := []struct { name string hash claircore.Digest @@ -91,7 +87,7 @@ func Benchmark_PackagesByLayer(b *testing.B) { for _, bench := range benchmarks { b.Run(bench.name, func(b *testing.B) { - ctx := zlog.Test(ctx, b) + ctx := test.Logging(b) pool := pgtest.TestIndexerDB(ctx, b) store := NewIndexerStore(pool) diff --git a/datastore/postgres/querybuilder_test.go b/datastore/postgres/querybuilder_test.go index fefee94f2..fd2d4036e 100644 --- a/datastore/postgres/querybuilder_test.go +++ b/datastore/postgres/querybuilder_test.go @@ -1,14 +1,12 @@ package postgres import ( - "context" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/uuid" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/datastore" @@ -33,7 +31,7 @@ func TestGetQueryBuilderDeterministicArgs(t *testing.T) { both = `(((("package_name" = 'package-0') AND ("package_kind" = 'binary')) OR (("package_name" = 'source-package-0') AND ("package_kind" = 'source'))) AND ` noSource = `((("package_name" = 'package-0') AND ("package_kind" = 'binary')) AND ` ) - var table = []struct { + table := []struct { // name of test name string // the expected query string returned @@ -253,7 +251,7 @@ type testCase struct { // considered when querying for vulns func TestLatestVulns(t *testing.T) { integration.NeedDB(t) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) cases := []testCase{ { diff --git a/datastore/postgres/recordupdatetime.go b/datastore/postgres/recordupdatetime.go index fff4a298b..71e7d4aa5 100644 --- a/datastore/postgres/recordupdatetime.go +++ b/datastore/postgres/recordupdatetime.go @@ -3,10 +3,10 @@ package postgres import ( "context" "fmt" + "log/slog" "time" "github.com/jackc/pgx/v5/pgxpool" - "github.com/quay/zlog" "github.com/quay/claircore/libvuln/driver" ) @@ -57,9 +57,7 @@ func recordUpdaterStatus(ctx context.Context, pool *pgxpool.Pool, updaterName st last_error = $4 RETURNING updater_name;` ) - - ctx = zlog.ContextWithValues(ctx, - "component", "internal/vulnstore/postgres/recordUpdaterStatus") + log := slog.With("updater", updaterName) tx, err := pool.Begin(ctx) if err != nil { @@ -70,17 +68,13 @@ func recordUpdaterStatus(ctx context.Context, pool *pgxpool.Pool, updaterName st var returnedUpdaterName string if updaterError == nil { - zlog.Debug(ctx). - Str("updater", updaterName). - Msg("recording successful update") + log.DebugContext(ctx, "recording successful update") _, err := tx.Exec(ctx, upsertSuccessfulUpdate, updaterName, updateTime, fingerprint) if err != nil { return fmt.Errorf("failed to upsert successful updater status: %w", err) } } else { - zlog.Debug(ctx). - Str("updater", updaterName). - Msg("recording failed update") + log.DebugContext(ctx, "recording failed update") if err := tx.QueryRow(ctx, upsertFailedUpdate, updaterName, updateTime, fingerprint, updaterError.Error()).Scan(&returnedUpdaterName); err != nil { return fmt.Errorf("failed to upsert failed updater status: %w", err) } @@ -89,10 +83,7 @@ func recordUpdaterStatus(ctx context.Context, pool *pgxpool.Pool, updaterName st if err := tx.Commit(ctx); err != nil { return fmt.Errorf("failed to commit transaction: %w", err) } - - zlog.Debug(ctx). - Str("updater", updaterName). - Msg("updater status stored in database") + log.DebugContext(ctx, "updater status stored in database") return nil } @@ -109,9 +100,6 @@ func recordUpdaterSetStatus(ctx context.Context, pool *pgxpool.Pool, updaterSet WHERE updater_name like $2 || '%';` ) - ctx = zlog.ContextWithValues(ctx, - "component", "internal/vulnstore/postgres/recordUpdaterSetStatus") - tx, err := pool.Begin(ctx) if err != nil { return fmt.Errorf("unable to start transaction: %w", err) @@ -127,10 +115,9 @@ func recordUpdaterSetStatus(ctx context.Context, pool *pgxpool.Pool, updaterSet return fmt.Errorf("failed to commit transaction: %w", err) } - zlog.Debug(ctx). - Str("factory", updaterSet). - Int64("rowsAffected", tag.RowsAffected()). - Msg("status updated for factory updaters") + slog.DebugContext(ctx, "status updated for factory updaters", + "factory", updaterSet, + "rowsAffected", tag.RowsAffected()) return nil } diff --git a/datastore/postgres/setlayerscanned.go b/datastore/postgres/setlayerscanned.go index ec97d59ae..39f8842aa 100644 --- a/datastore/postgres/setlayerscanned.go +++ b/datastore/postgres/setlayerscanned.go @@ -7,7 +7,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -36,7 +35,6 @@ var ( ) func (s *IndexerStore) SetLayerScanned(ctx context.Context, hash claircore.Digest, vs indexer.VersionedScanner) error { - ctx = zlog.ContextWithValues(ctx, "scanner", vs.Name()) const query = ` WITH scanner diff --git a/datastore/postgres/update_e2e_test.go b/datastore/postgres/update_e2e_test.go index 0d05562b2..367afa8d9 100644 --- a/datastore/postgres/update_e2e_test.go +++ b/datastore/postgres/update_e2e_test.go @@ -14,7 +14,6 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/datastore" @@ -27,7 +26,7 @@ import ( // TestUpdateE2E performs an end to end test of update operations and diffing func TestUpdateE2E(t *testing.T) { integration.NeedDB(t) - ctx := zlog.Test(context.Background(), t) + ctx := t.Context() cases := []updateE2e{ { @@ -84,7 +83,7 @@ func (e *updateE2e) Run(ctx context.Context) func(*testing.T) { {"DeleteUpdateOperations", e.DeleteUpdateOperations}, } return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) pool := pgtest.TestMatcherDB(ctx, t) e.pool = pool e.s = NewMatcherStore(pool) @@ -118,7 +117,7 @@ var updateOpCmp = cmpopts.IgnoreFields(driver.UpdateOperation{}, "Date") func (e *updateE2e) Update(ctx context.Context) func(*testing.T) { fp := driver.Fingerprint(uuid.New().String()) return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) e.updateOps = make([]driver.UpdateOperation, 0, e.Updates) for _, vs := range e.vulns() { ref, err := e.s.UpdateVulnerabilities(ctx, e.updater, fp, vs) @@ -144,7 +143,7 @@ func (e *updateE2e) Update(ctx context.Context) func(*testing.T) { // operation returns the expected results. func (e *updateE2e) GetUpdateOperations(ctx context.Context) func(*testing.T) { return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) out, err := e.s.GetUpdateOperations(ctx, driver.VulnerabilityKind, e.updater) if err != nil { t.Fatalf("failed to get UpdateOperations: %v", err) @@ -179,7 +178,7 @@ type update struct { // and then an update to an whole updater set func (e *updateE2e) recordUpdaterStatus(ctx context.Context) func(*testing.T) { return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) errorText := "test error" firstUpdateDate := time.Date(2020, time.Month(1), 22, 2, 10, 30, 0, time.UTC) secondUpdateDate := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) @@ -256,7 +255,7 @@ func orNoIndex(a int) string { // independently calculated diffs. func (e *updateE2e) Diff(ctx context.Context) func(t *testing.T) { return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) for n := range e.vulns() { // This does a bunch of checks so that the first operation is // compared appropriately. @@ -344,7 +343,7 @@ func (e *updateE2e) DeleteUpdateOperations(ctx context.Context) func(*testing.T) assocExists = `SELECT EXISTS(SELECT 1 FROM uo_vuln JOIN update_operation uo ON (uo_vuln.uo = uo.id) WHERE uo.ref = $1::uuid);` ) var exists bool - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) for _, op := range e.updateOps { _, err := e.s.DeleteUpdateOperations(ctx, op.Ref) if err != nil { diff --git a/datastore/postgres/updatevulnerabilities.go b/datastore/postgres/updatevulnerabilities.go index 5b58f9383..de8d148b2 100644 --- a/datastore/postgres/updatevulnerabilities.go +++ b/datastore/postgres/updatevulnerabilities.go @@ -5,6 +5,7 @@ import ( "context" "crypto/md5" "fmt" + "log/slog" "strconv" "strings" "time" @@ -13,7 +14,6 @@ import ( "github.com/jackc/pgx/v5" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/datastore" @@ -48,7 +48,6 @@ var ( // UpdateVulnerabilitiesIter implements vulnstore.Updater. func (s *MatcherStore) UpdateVulnerabilitiesIter(ctx context.Context, updater string, fp driver.Fingerprint, it datastore.VulnerabilityIter) (uuid.UUID, error) { - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/MatcherStore.UpdateVulnerabilitiesIter") return s.updateVulnerabilities(ctx, updater, fp, it, nil) } @@ -58,7 +57,6 @@ func (s *MatcherStore) UpdateVulnerabilitiesIter(ctx context.Context, updater st // provided vulnerabilities and computes a diff comprising the removed // and added vulnerabilities for this UpdateOperation. func (s *MatcherStore) UpdateVulnerabilities(ctx context.Context, updater string, fp driver.Fingerprint, vulns []*claircore.Vulnerability) (uuid.UUID, error) { - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/MatcherStore.UpdateVulnerabilities") iterVulns := func(yield func(*claircore.Vulnerability, error) bool) { for i := range vulns { if !yield(vulns[i], nil) { @@ -81,7 +79,6 @@ func (s *MatcherStore) UpdateVulnerabilities(ctx context.Context, updater string // - Insert the new vulnerabilities // - Associate new vulnerabilities with new updateOperation func (s *MatcherStore) DeltaUpdateVulnerabilities(ctx context.Context, updater string, fingerprint driver.Fingerprint, vulns []*claircore.Vulnerability, deletedVulns []string) (uuid.UUID, error) { - ctx = zlog.ContextWithValues(ctx, "component", "datastore/postgres/MatcherStore.DeltaUpdateVulnerabilities") iterVulns := func(yield func(*claircore.Vulnerability, error) bool) { for i := range vulns { if !yield(vulns[i], nil) { @@ -171,12 +168,11 @@ func (s *MatcherStore) updateVulnerabilities(ctx context.Context, updater string updateVulnerabilitiesCounter.WithLabelValues("create", strconv.FormatBool(delta)).Add(1) updateVulnerabilitiesDuration.WithLabelValues("create", strconv.FormatBool(delta)).Observe(time.Since(start).Seconds()) - zlog.Debug(ctx). - Str("ref", ref.String()). - Msg("update_operation created") + log := slog.With("ref", ref) + log.DebugContext(ctx, "update_operation created") if delta { - ctx = zlog.ContextWithValues(ctx, "mode", "delta") + log = log.With("mode", "delta") // Get existing vulns // The reason this still works even though the new update_operation // is already created is because the latest_update_operation view isn't updated until @@ -312,11 +308,9 @@ func (s *MatcherStore) updateVulnerabilities(ctx context.Context, updater string return uuid.Nil, fmt.Errorf("could not refresh latest_update_operations: %w", err) } - zlog.Debug(ctx). - Str("ref", ref.String()). - Int("skipped", skipCt). - Int("inserted", vulnCt-skipCt). - Msg("update_operation committed") + log.DebugContext(ctx, "update_operation committed", + "skipped", skipCt, + "inserted", vulnCt-skipCt) return ref, nil } diff --git a/datastore/postgres/updatevulnerabilities_test.go b/datastore/postgres/updatevulnerabilities_test.go index 69f218b90..09b23906d 100644 --- a/datastore/postgres/updatevulnerabilities_test.go +++ b/datastore/postgres/updatevulnerabilities_test.go @@ -1,15 +1,14 @@ package postgres import ( - "context" "testing" "github.com/google/uuid" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/datastore" "github.com/quay/claircore/libvuln/driver" + "github.com/quay/claircore/test" "github.com/quay/claircore/test/integration" pgtest "github.com/quay/claircore/test/postgres" ) @@ -29,7 +28,7 @@ type op struct { func TestGetLatestVulnerabilities(t *testing.T) { integration.NeedDB(t) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) cases := []latestVulnTestCase{ { @@ -297,6 +296,7 @@ func TestGetLatestVulnerabilities(t *testing.T) { // run test cases for _, tc := range cases { t.Run(tc.TestName, func(t *testing.T) { + ctx := test.Logging(t, ctx) _, err := store.DeltaUpdateVulnerabilities(ctx, tc.Updater, driver.Fingerprint(uuid.New().String()), tc.FirstOp.vulns, tc.FirstOp.deletedVulns) if err != nil { t.Fatalf("failed to perform update for first op: %v", err) From 9f762ae8c9a50f16d1fce30c8e7c7dde0fcd7d15 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 14:35:25 -0500 Subject: [PATCH 22/77] libvuln/updates: migrate to slog Signed-off-by: Hank Donnay --- libvuln/updates/manager.go | 104 ++++++++++++-------------------- libvuln/updates/manager_test.go | 10 +-- 2 files changed, 46 insertions(+), 68 deletions(-) diff --git a/libvuln/updates/manager.go b/libvuln/updates/manager.go index e54ad2edc..770077180 100644 --- a/libvuln/updates/manager.go +++ b/libvuln/updates/manager.go @@ -5,13 +5,13 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "runtime" "strings" "time" "github.com/google/uuid" - "github.com/quay/zlog" "golang.org/x/sync/semaphore" "github.com/quay/claircore" @@ -62,8 +62,6 @@ type Manager struct { // NewManager will return a manager ready to have its Start or Run methods called. func NewManager(ctx context.Context, store datastore.Updater, locks LockSource, client *http.Client, opts ...ManagerOption) (*Manager, error) { - ctx = zlog.ContextWithValues(ctx, "component", "libvuln/updates/NewManager") - // the default Manager m := &Manager{ store: store, @@ -98,21 +96,19 @@ func NewManager(ctx context.Context, store datastore.Updater, locks LockSource, // // Start must only be called once between context cancellations. func (m *Manager) Start(ctx context.Context) error { - ctx = zlog.ContextWithValues(ctx, "component", "libvuln/updates/Manager.Start") - if m.interval == 0 { return fmt.Errorf("manager must be configured with an interval to start") } // perform the initial run - zlog.Info(ctx).Msg("starting initial updates") + slog.InfoContext(ctx, "starting initial updates") err := m.Run(ctx) if err != nil { - zlog.Error(ctx).Err(err).Msg("errors encountered during updater run") + slog.ErrorContext(ctx, "errors encountered during updater run", "reason", err) } // perform run on every tick - zlog.Info(ctx).Str("interval", m.interval.String()).Msg("starting background updates") + slog.InfoContext(ctx, "starting background updates", "interval", m.interval) t := time.NewTicker(m.interval) defer t.Stop() for { @@ -122,7 +118,7 @@ func (m *Manager) Start(ctx context.Context) error { case <-t.C: err := m.Run(ctx) if err != nil { - zlog.Error(ctx).Err(err).Msg("errors encountered during updater run") + slog.ErrorContext(ctx, "errors encountered during updater run", "reason", err) } } } @@ -134,8 +130,6 @@ func (m *Manager) Start(ctx context.Context) error { // Run is safe to call at anytime, regardless of whether background updaters // are running. func (m *Manager) Run(ctx context.Context) error { - ctx = zlog.ContextWithValues(ctx, "component", "libvuln/updates/Manager.Run") - updaters := []driver.Updater{} // Constructing updater sets may require network access // depending on the factory. @@ -145,23 +139,21 @@ func (m *Manager) Run(ctx context.Context) error { updateTime := time.Now() set, err := factory.UpdaterSet(ctx) if err != nil { - zlog.Error(ctx).Err(err).Msg("failed constructing factory, excluding from run") + slog.ErrorContext(ctx, "failed constructing factory, excluding from run", "reason", err) continue } if stubUpdaterInSet(set) { updaterSetName, err := getFactoryNameFromStubUpdater(set) if err != nil { - zlog.Error(ctx). - Err(err). - Msg("error getting updater set name") + slog.ErrorContext(ctx, "error getting updater set name", + "reason", err) } err = m.store.RecordUpdaterSetStatus(ctx, updaterSetName, updateTime) if err != nil { - zlog.Error(ctx). - Err(err). - Str("updaterSetName", updaterSetName). - Time("updateTime", updateTime). - Msg("error while recording update success for all updaters in updater set") + slog.ErrorContext(ctx, "error while recording update success for all updaters in updater set", + "updaterSetName", updaterSetName, + "updateTime", updateTime, + "reason", err) } continue } @@ -178,29 +170,25 @@ func (m *Manager) Run(ctx context.Context) error { cfg = noopConfig } if err := f.Configure(ctx, cfg, m.client); err != nil { - zlog.Warn(ctx). - Err(err). - Str("updater", name). - Msg("failed configuring updater, excluding from current run") + slog.WarnContext(ctx, "failed configuring updater, excluding from current run", + "updater", name, + "reason", err) continue } } toRun = append(toRun, u) } - zlog.Info(ctx). - Int("total", len(toRun)). - Int("batchSize", m.batchSize). - Msg("running updaters") + slog.InfoContext(ctx, "running updaters", + "total", len(toRun), + "batchSize", m.batchSize) sem := semaphore.NewWeighted(int64(m.batchSize)) errChan := make(chan error, len(toRun)+1) // +1 for a potential ctx error for i := range toRun { err := sem.Acquire(ctx, 1) if err != nil { - zlog.Error(ctx). - Err(err). - Msg("sem acquire failed, ending updater run") + slog.ErrorContext(ctx, "sem acquire failed, ending updater run", "reason", err) break } @@ -210,10 +198,9 @@ func (m *Manager) Run(ctx context.Context) error { ctx, done := m.locks.TryLock(ctx, u.Name()) defer done() if err := ctx.Err(); err != nil { - zlog.Debug(ctx). - Err(err). - Str("updater", u.Name()). - Msg("lock context canceled, excluding from run") + slog.DebugContext(ctx, "lock context canceled, excluding from run", + "reason", err, + "updater", u.Name()) return } @@ -232,19 +219,16 @@ func (m *Manager) Run(ctx context.Context) error { if m.updateRetention != 0 { ctx, done := m.locks.TryLock(ctx, "garbage-collection") if err := ctx.Err(); err != nil { - zlog.Debug(ctx). - Err(err). - Msg("lock context canceled, garbage collection already running") + slog.DebugContext(ctx, "lock context canceled, garbage collection already running", "reason", err) } else { - zlog.Info(ctx).Int("retention", m.updateRetention).Msg("GC started") + slog.InfoContext(ctx, "GC started", "retention", m.updateRetention) i, err := m.store.GC(ctx, m.updateRetention) if err != nil { - zlog.Error(ctx).Err(err).Msg("error while performing GC") + slog.ErrorContext(ctx, "error while performing GC", "reason", err) } else { - zlog.Info(ctx). - Int64("remaining_ops", i). - Int("retention", m.updateRetention). - Msg("GC completed") + slog.InfoContext(ctx, "GC completed", + "remaining_ops", i, + "retention", m.updateRetention) } } done() @@ -286,36 +270,30 @@ func getFactoryNameFromStubUpdater(set driver.UpdaterSet) (string, error) { func (m *Manager) driveUpdater(ctx context.Context, u driver.Updater) (err error) { var newFP driver.Fingerprint updateTime := time.Now() + name := u.Name() + log := slog.With("updater", name) defer func() { - deferErr := m.store.RecordUpdaterStatus(ctx, u.Name(), updateTime, newFP, err) + deferErr := m.store.RecordUpdaterStatus(ctx, name, updateTime, newFP, err) if deferErr != nil { - zlog.Error(ctx). - Err(deferErr). - Str("updater", u.Name()). - Time("updateTime", updateTime). - Msg("error while recording updater status") + log.ErrorContext(ctx, "error while recording updater status", + "reason", deferErr, + "updateTime", updateTime) } }() - name := u.Name() - ctx = zlog.ContextWithValues(ctx, - "component", "libvuln/updates/Manager.driveUpdater", - "updater", name) - zlog.Info(ctx).Msg("starting update") - defer zlog.Info(ctx).Msg("finished update") + log.InfoContext(ctx, "starting update") + defer log.InfoContext(ctx, "finished update") uoKind := driver.VulnerabilityKind // Do some assertions eu, euOK := u.(driver.EnrichmentUpdater) if euOK { - zlog.Info(ctx). - Msg("found EnrichmentUpdater") + log.InfoContext(ctx, "found EnrichmentUpdater") uoKind = driver.EnrichmentKind } du, duOK := u.(driver.DeltaUpdater) if duOK { - zlog.Info(ctx). - Msg("found DeltaUpdater") + slog.InfoContext(ctx, "found DeltaUpdater") } var prevFP driver.Fingerprint @@ -341,7 +319,7 @@ func (m *Manager) driveUpdater(ctx context.Context, u driver.Updater) (err error switch { case err == nil: case errors.Is(err, driver.Unchanged): - zlog.Info(ctx).Msg("vulnerability database unchanged") + log.InfoContext(ctx, "vulnerability database unchanged") err = nil return default: @@ -386,9 +364,7 @@ func (m *Manager) driveUpdater(ctx context.Context, u driver.Updater) (err error err = fmt.Errorf("failed to update: %v", err) return } - zlog.Info(ctx). - Str("ref", ref.String()). - Msg("successful update") + log.InfoContext(ctx, "successful update", "ref", ref) return nil } diff --git a/libvuln/updates/manager_test.go b/libvuln/updates/manager_test.go index 70e1aa5c5..502b30139 100644 --- a/libvuln/updates/manager_test.go +++ b/libvuln/updates/manager_test.go @@ -12,20 +12,20 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/jackc/pgx/v5/pgxpool" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/datastore/postgres" "github.com/quay/claircore/libvuln/driver" "github.com/quay/claircore/pkg/ctxlock/v2" + "github.com/quay/claircore/test" "github.com/quay/claircore/test/integration" pgtest "github.com/quay/claircore/test/postgres" ) func TestDeltaUpdates(t *testing.T) { integration.NeedDB(t) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) facs := map[string]driver.UpdaterSetFactory{ "delta": &Factory{ vulnGetter: &vulnGetter{ @@ -81,8 +81,10 @@ func TestDeltaUpdates(t *testing.T) { } } -var _ driver.DeltaUpdater = (*testUpdater)(nil) -var _ driver.Updater = (*testUpdater)(nil) +var ( + _ driver.DeltaUpdater = (*testUpdater)(nil) + _ driver.Updater = (*testUpdater)(nil) +) type testUpdater struct { vulnGetter *vulnGetter From 7cdf1fa17e3fd2b07c5c2cc44409bbf6d6f29cc8 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 15:08:22 -0500 Subject: [PATCH 23/77] libvuln: migrate to slog Signed-off-by: Hank Donnay --- libvuln/libvuln.go | 23 ++++++++++----------- libvuln/libvuln_test.go | 46 ----------------------------------------- libvuln/updates.go | 19 +++++++---------- 3 files changed, 18 insertions(+), 70 deletions(-) delete mode 100644 libvuln/libvuln_test.go diff --git a/libvuln/libvuln.go b/libvuln/libvuln.go index e7119297d..65f07ffae 100644 --- a/libvuln/libvuln.go +++ b/libvuln/libvuln.go @@ -3,13 +3,12 @@ package libvuln import ( "context" "fmt" + "log/slog" "math/rand" "reflect" "time" "github.com/google/uuid" - "github.com/quay/zlog" - "github.com/rs/zerolog" "github.com/quay/claircore" "github.com/quay/claircore/datastore" @@ -33,7 +32,6 @@ type Libvuln struct { updaters *updates.Manager } -// TODO (crozzy): Find a home for this and stop redefining it. // LockSource abstracts over how locks are implemented. // // An online system needs distributed locks, offline use cases can use @@ -44,10 +42,10 @@ type LockSource interface { Close(context.Context) error } +// TODO (crozzy): Find a home for this and stop redefining it. + // New creates a new instance of the Libvuln library func New(ctx context.Context, opts *Options) (*Libvuln, error) { - ctx = zlog.ContextWithValues(ctx, "component", "libvuln/New") - // required if opts.Store == nil { return nil, fmt.Errorf("libvuln: must provide a Store") @@ -97,7 +95,7 @@ func New(ctx context.Context, opts *Options) (*Libvuln, error) { return nil, err } - zlog.Info(ctx).Array("matchers", matcherLog(l.matchers)).Msg("matchers created") + slog.InfoContext(ctx, "matchers created", "matcher", matcherLog(l.matchers)) l.updaters, err = updates.NewManager(ctx, l.store, @@ -119,7 +117,7 @@ func New(ctx context.Context, opts *Options) (*Libvuln, error) { go l.updaters.Start(ctx) } - zlog.Info(ctx).Msg("libvuln initialized") + slog.InfoContext(ctx, "libvuln initialized") return l, nil } @@ -224,11 +222,12 @@ func (l *Libvuln) Initialized(ctx context.Context) (bool, error) { // generated documentation URL. type matcherLog []driver.Matcher -func (l matcherLog) MarshalZerologArray(a *zerolog.Array) { - for _, m := range l { +func (l matcherLog) LogValue() slog.Value { + as := make([]slog.Attr, len(l)) + for i, m := range l { t := reflect.ValueOf(m).Elem().Type() - a.Dict(zerolog.Dict(). - Str("name", m.Name()). - Str("docs", `https://pkg.go.dev/`+t.PkgPath())) + as[i] = slog.String(m.Name(), `https://pkg.go.dev/`+t.PkgPath()) } + + return slog.GroupValue(as...) } diff --git a/libvuln/libvuln_test.go b/libvuln/libvuln_test.go deleted file mode 100644 index a19cde3c9..000000000 --- a/libvuln/libvuln_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package libvuln - -import ( - "bytes" - "context" - "testing" - - "github.com/rs/zerolog" - - "github.com/quay/claircore" - "github.com/quay/claircore/libvuln/driver" -) - -func TestMatcherLog(t *testing.T) { - const want = `{"matchers":[{"name":"test-matcher","docs":"https://pkg.go.dev/github.com/quay/claircore/libvuln"}]}` + "\n" - var buf bytes.Buffer - log := zerolog.New(&buf) - log.Log().Array("matchers", matcherLog([]driver.Matcher{&TestMatcher{}})).Send() - - got := buf.String() - t.Logf("got: %+#q", got) - if got != want { - t.Errorf("want: %+#q", want) - } -} - -// Helper for above test -type TestMatcher struct{} - -var _ driver.Matcher = (*TestMatcher)(nil) - -func (*TestMatcher) Name() string { - return "test-matcher" -} - -func (*TestMatcher) Filter(*claircore.IndexRecord) bool { - return false -} - -func (*TestMatcher) Query() []driver.MatchConstraint { - return nil -} - -func (*TestMatcher) Vulnerable(context.Context, *claircore.IndexRecord, *claircore.Vulnerability) (bool, error) { - return false, nil -} diff --git a/libvuln/updates.go b/libvuln/updates.go index 57e5a0c3c..94c94533e 100644 --- a/libvuln/updates.go +++ b/libvuln/updates.go @@ -4,10 +4,10 @@ import ( "context" "fmt" "io" + "log/slog" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" - "github.com/quay/zlog" "github.com/quay/claircore/datastore/postgres" "github.com/quay/claircore/libvuln/driver" @@ -23,8 +23,6 @@ func OfflineImport(ctx context.Context, pool *pgxpool.Pool, in io.Reader) error // BUG(hank) The OfflineImport function is a wart, needed to work around // some package namespacing issues. It should get refactored if claircore // gets merged into clair. - ctx = zlog.ContextWithValues(ctx, "component", "libvuln/OfflineImporter") - s := postgres.NewMatcherStore(pool) l, err := jsonblob.Load(ctx, in) if err != nil { @@ -39,13 +37,12 @@ func OfflineImport(ctx context.Context, pool *pgxpool.Pool, in io.Reader) error Update: for l.Next() { e := l.Entry() + log := slog.With("updater", e.Updater) for _, op := range ops[e.Updater] { // This only helps if updaters don't keep something that // changes in the fingerprint. if op.Fingerprint == e.Fingerprint { - zlog.Info(ctx). - Str("updater", e.Updater). - Msg("fingerprint match, skipping") + log.InfoContext(ctx, "fingerprint match, skipping") continue Update } } @@ -60,12 +57,10 @@ Update: return fmt.Errorf("updating vulnerabilities: %w", err) } } - zlog.Info(ctx). - Str("updater", e.Updater). - Str("ref", ref.String()). - Int("vuln_count", len(e.Vuln)). - Int("enrichment_count", len(e.Enrichment)). - Msg("update imported") + log.InfoContext(ctx, "update imported", + "ref", ref, + "vuln_count", len(e.Vuln), + "enrichment_count", len(e.Enrichment)) } if err := l.Err(); err != nil { return err From 4cd41336c765589e87d18b9fa01c48d9b0a6c4a8 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 15:41:49 -0500 Subject: [PATCH 24/77] indexer/controller: migrate to slog Signed-off-by: Hank Donnay --- indexer/controller/checkmanifest.go | 6 +++--- indexer/controller/checkmanifest_test.go | 6 ++---- indexer/controller/coalesce_test.go | 9 +++------ indexer/controller/controller.go | 19 +++++++------------ indexer/controller/controller_test.go | 9 +++------ indexer/controller/fetchlayers.go | 17 ++++++----------- indexer/controller/indexfinished.go | 7 +++---- indexer/controller/indexfinished_test.go | 6 ++---- indexer/controller/indexmanifest.go | 5 ++--- indexer/controller/indexmanifest_test.go | 6 ++---- indexer/controller/reduce.go | 8 ++------ indexer/controller/scanlayers.go | 9 ++++----- indexer/controller/scanlayers_test.go | 6 ++---- 13 files changed, 41 insertions(+), 72 deletions(-) diff --git a/indexer/controller/checkmanifest.go b/indexer/controller/checkmanifest.go index 839cd0e4a..2cd510e7b 100644 --- a/indexer/controller/checkmanifest.go +++ b/indexer/controller/checkmanifest.go @@ -3,11 +3,11 @@ package controller import ( "context" "fmt" + "log/slog" "strconv" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/quay/zlog" "github.com/quay/claircore/indexer" ) @@ -35,7 +35,7 @@ func checkManifest(ctx context.Context, s *Controller) (State, error) { // if we haven't seen this manifest, determine which scanners to use, persist it // and transition to FetchLayer state. if !ok { - zlog.Info(ctx).Msg("manifest to be scanned") + slog.InfoContext(ctx, "manifest to be scanned") // if a manifest was analyzed by a particular scanner we can // omit it from this index, as all its comprising layers were analyzed @@ -61,7 +61,7 @@ func checkManifest(ctx context.Context, s *Controller) (State, error) { // we have seen this manifest before and it's been been processed with the desired scanners // retrieve the existing one and transition to Terminal. - zlog.Info(ctx).Msg("manifest already scanned") + slog.InfoContext(ctx, "manifest already scanned") sr, ok, err := s.Store.IndexReport(ctx, s.manifest.Hash) if err != nil { return Terminal, fmt.Errorf("failed to retrieve manifest: %w", err) diff --git a/indexer/controller/checkmanifest_test.go b/indexer/controller/checkmanifest_test.go index e67547bfe..911755e10 100644 --- a/indexer/controller/checkmanifest_test.go +++ b/indexer/controller/checkmanifest_test.go @@ -1,20 +1,18 @@ package controller import ( - "context" "testing" - "github.com/quay/zlog" "go.uber.org/mock/gomock" "github.com/quay/claircore" + "github.com/quay/claircore/test" indexer "github.com/quay/claircore/test/mock/indexer" ) // confirm checkManfest statefunc acts appropriately // when manifest has been seen func TestCheckManifest(t *testing.T) { - ctx := zlog.Test(context.Background(), t) tt := []struct { // the name of this test name string @@ -49,7 +47,7 @@ func TestCheckManifest(t *testing.T) { for _, table := range tt { t.Run(table.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) // get mock m := table.mock(t) diff --git a/indexer/controller/coalesce_test.go b/indexer/controller/coalesce_test.go index 6828848f8..a71eae3cb 100644 --- a/indexer/controller/coalesce_test.go +++ b/indexer/controller/coalesce_test.go @@ -1,12 +1,10 @@ package controller import ( - "context" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore/indexer" + "github.com/quay/claircore/test" ) // TestCoalesce confirms when no error is encountered @@ -16,8 +14,7 @@ import ( // This test simply provides no Ecosystems to the index // controller and does no work. func TestCoalesce(t *testing.T) { - ctx := context.Background() - var tt = []struct { + tt := []struct { name string expectedState State }{ @@ -29,7 +26,7 @@ func TestCoalesce(t *testing.T) { for _, table := range tt { t.Run(table.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) indexer := New(&indexer.Options{}) state, err := coalesce(ctx, indexer) diff --git a/indexer/controller/controller.go b/indexer/controller/controller.go index df6504201..e8b4aa814 100644 --- a/indexer/controller/controller.go +++ b/indexer/controller/controller.go @@ -4,10 +4,11 @@ import ( "context" "errors" "fmt" + "log/slog" "math/rand" "time" - "github.com/quay/zlog" + "github.com/quay/claircore/toolkit/log" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -64,12 +65,10 @@ func (s *Controller) Index(ctx context.Context, manifest *claircore.Manifest) (* // set manifest info on controller s.manifest = manifest s.report.Hash = manifest.Hash - ctx = zlog.ContextWithValues(ctx, - "component", "indexer/controller/Controller.Index", - "manifest", s.manifest.Hash.String()) + ctx = log.With(ctx, "manifest", s.manifest.Hash) s.Realizer = s.FetchArena.Realizer(ctx) defer s.Realizer.Close() - zlog.Info(ctx).Msg("starting scan") + slog.InfoContext(ctx, "starting scan") return s.report, s.run(ctx) } @@ -83,7 +82,7 @@ func (s *Controller) run(ctx context.Context) (err error) { // As long as there's not an error and the current state isn't Terminal, run // the corresponding function. for err == nil && s.currentState != Terminal { - ctx := zlog.ContextWithValues(ctx, "state", s.currentState.String()) + ctx := log.With(ctx, "state", s.currentState) next, err = stateToStateFunc[s.currentState](ctx, s) switch { case errors.Is(err, nil) && !errors.Is(ctx.Err(), nil): @@ -107,16 +106,12 @@ func (s *Controller) run(ctx context.Context) (err error) { continue default: s.setState(IndexError) - zlog.Error(ctx). - Err(err). - Msg("error during scan") + slog.ErrorContext(ctx, "error during scan", "reason", err) s.report.Success = false s.report.Err = err.Error() } if setReportErr := s.Store.SetIndexReport(ctx, s.report); !errors.Is(setReportErr, nil) { - zlog.Info(ctx). - Err(setReportErr). - Msg("failed persisting index report") + slog.InfoContext(ctx, "failed persisting index report", "reason", setReportErr) s.setState(IndexError) s.report.Err = fmt.Sprintf("failed persisting index report: %s", setReportErr.Error()) err = setReportErr diff --git a/indexer/controller/controller_test.go b/indexer/controller/controller_test.go index 99499a275..60eb340dc 100644 --- a/indexer/controller/controller_test.go +++ b/indexer/controller/controller_test.go @@ -1,16 +1,15 @@ package controller import ( - "context" "errors" "fmt" "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "go.uber.org/mock/gomock" "github.com/quay/claircore" + "github.com/quay/claircore/test" indexer "github.com/quay/claircore/test/mock/indexer" ) @@ -21,7 +20,6 @@ import ( // call to s.Store.ManifestScanned forcing checkManifest to return an error and // evaluate our scanner's state afterwards. func TestControllerIndexerError(t *testing.T) { - ctx := context.Background() tt := []struct { mock func(t *testing.T) (indexer.Store, indexer.FetchArena) name string @@ -69,7 +67,7 @@ func TestControllerIndexerError(t *testing.T) { for _, table := range tt { t.Run(table.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) store, fa := table.mock(t) c := New(&indexer.Options{ Store: store, @@ -96,7 +94,6 @@ func TestControllerIndexerError(t *testing.T) { // TestControllerIndexFinished tests that our state machine does the correct // thing when it reaches ScanFinished terminal state. func TestControllerIndexFinished(t *testing.T) { - ctx := context.Background() tt := []struct { mock func(t *testing.T) (indexer.Store, indexer.FetchArena) name string @@ -126,7 +123,7 @@ func TestControllerIndexFinished(t *testing.T) { for _, table := range tt { t.Run(table.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) store, fa := table.mock(t) c := New(&indexer.Options{ Store: store, diff --git a/indexer/controller/fetchlayers.go b/indexer/controller/fetchlayers.go index 24688711f..742d4c455 100644 --- a/indexer/controller/fetchlayers.go +++ b/indexer/controller/fetchlayers.go @@ -3,26 +3,21 @@ package controller import ( "context" "fmt" - - "github.com/quay/zlog" + "log/slog" "github.com/quay/claircore/internal/wart" ) func fetchLayers(ctx context.Context, s *Controller) (State, error) { - zlog.Info(ctx).Msg("layers fetch start") - defer zlog.Info(ctx).Msg("layers fetch done") + slog.InfoContext(ctx, "layers fetch start") + defer slog.InfoContext(ctx, "layers fetch done") toFetch, err := reduce(ctx, s.Store, s.Vscnrs, s.manifest.Layers) if err != nil { return Terminal, fmt.Errorf("failed to determine layers to fetch: %w", err) } - zlog.Debug(ctx). - Int("count", len(toFetch)). - Msg("fetching layers") + slog.DebugContext(ctx, "fetching layers", "count", len(toFetch)) if err := s.Realizer.Realize(ctx, toFetch); err != nil { - zlog.Warn(ctx). - Err(err). - Msg("layers fetch failure") + slog.WarnContext(ctx, "layers fetch failure", "reason", err) return Terminal, fmt.Errorf("failed to fetch layers: %w", err) } // With the addition of the LayerDescription abstraction, it's possible that @@ -30,6 +25,6 @@ func fetchLayers(ctx context.Context, s *Controller) (State, error) { // LayerDescription type is plumbed through the Indexer, this can be // removed. wart.CopyLayerPointers(s.manifest.Layers, toFetch) - zlog.Info(ctx).Msg("layers fetch success") + slog.InfoContext(ctx, "layers fetch success") return ScanLayers, nil } diff --git a/indexer/controller/indexfinished.go b/indexer/controller/indexfinished.go index bd5e47fc2..d96525f16 100644 --- a/indexer/controller/indexfinished.go +++ b/indexer/controller/indexfinished.go @@ -3,8 +3,7 @@ package controller import ( "context" "fmt" - - "github.com/quay/zlog" + "log/slog" ) // indexFinished is the terminal stateFunc. once it transitions the @@ -12,13 +11,13 @@ import ( // and return an IndexReport to the caller func indexFinished(ctx context.Context, s *Controller) (State, error) { s.report.Success = true - zlog.Info(ctx).Msg("finishing scan") + slog.InfoContext(ctx, "finishing scan") err := s.Store.SetIndexFinished(ctx, s.report, s.Vscnrs) if err != nil { return Terminal, fmt.Errorf("failed finish scan: %w", err) } - zlog.Info(ctx).Msg("manifest successfully scanned") + slog.InfoContext(ctx, "manifest successfully scanned") return Terminal, nil } diff --git a/indexer/controller/indexfinished_test.go b/indexer/controller/indexfinished_test.go index 83da52819..49c4c8b5f 100644 --- a/indexer/controller/indexfinished_test.go +++ b/indexer/controller/indexfinished_test.go @@ -1,17 +1,15 @@ package controller import ( - "context" "testing" - "github.com/quay/zlog" "go.uber.org/mock/gomock" + "github.com/quay/claircore/test" indexer "github.com/quay/claircore/test/mock/indexer" ) func TestIndexFinished(t *testing.T) { - ctx := context.Background() tt := []struct { name string expectedState State @@ -33,7 +31,7 @@ func TestIndexFinished(t *testing.T) { for _, table := range tt { t.Run(table.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) store := table.mock(t) scnr := New(&indexer.Options{ Store: store, diff --git a/indexer/controller/indexmanifest.go b/indexer/controller/indexmanifest.go index 272807c2a..25f919703 100644 --- a/indexer/controller/indexmanifest.go +++ b/indexer/controller/indexmanifest.go @@ -3,12 +3,11 @@ package controller import ( "context" "fmt" - - "github.com/quay/zlog" + "log/slog" ) func indexManifest(ctx context.Context, c *Controller) (State, error) { - zlog.Info(ctx).Msg("starting index manifest") + slog.InfoContext(ctx, "starting index manifest") if c.report == nil { return Terminal, fmt.Errorf("reached IndexManifest state with a nil report field. cannot continue") diff --git a/indexer/controller/indexmanifest_test.go b/indexer/controller/indexmanifest_test.go index 1bd40b577..25e911bca 100644 --- a/indexer/controller/indexmanifest_test.go +++ b/indexer/controller/indexmanifest_test.go @@ -1,18 +1,16 @@ package controller import ( - "context" "fmt" "testing" - "github.com/quay/zlog" "go.uber.org/mock/gomock" + "github.com/quay/claircore/test" indexer "github.com/quay/claircore/test/mock/indexer" ) func TestIndexManifest(t *testing.T) { - ctx := context.Background() tt := []struct { name string expectedState State @@ -44,7 +42,7 @@ func TestIndexManifest(t *testing.T) { for _, table := range tt { t.Run(table.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) s := table.mock(t) indexer := New(&indexer.Options{ Store: s, diff --git a/indexer/controller/reduce.go b/indexer/controller/reduce.go index d8cbb8d30..6890b44ca 100644 --- a/indexer/controller/reduce.go +++ b/indexer/controller/reduce.go @@ -2,8 +2,7 @@ package controller import ( "context" - - "github.com/quay/zlog" + "log/slog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -16,10 +15,7 @@ func reduce(ctx context.Context, store indexer.Store, scnrs indexer.VersionedSca for _, scnr := range scnrs { ok, err := store.LayerScanned(ctx, l.Hash, scnr) if err != nil { - zlog.Debug(ctx). - Stringer("layer", l.Hash). - Err(err). - Msg("unable to lookup layer") + slog.DebugContext(ctx, "unable to lookup layer", "layer", l.Hash, "reason", err) return nil, err } if !ok { diff --git a/indexer/controller/scanlayers.go b/indexer/controller/scanlayers.go index aeda09061..21c612635 100644 --- a/indexer/controller/scanlayers.go +++ b/indexer/controller/scanlayers.go @@ -3,19 +3,18 @@ package controller import ( "context" "fmt" - - "github.com/quay/zlog" + "log/slog" ) // scanLayers will run all scanner types against all layers if deemed necessary // to scan func scanLayers(ctx context.Context, c *Controller) (State, error) { - zlog.Info(ctx).Msg("layers scan start") - defer zlog.Info(ctx).Msg("layers scan done") + slog.InfoContext(ctx, "layers scan start") + defer slog.InfoContext(ctx, "layers scan done") err := c.LayerScanner.Scan(ctx, c.manifest.Hash, c.manifest.Layers) if err != nil { return Terminal, fmt.Errorf("failed to scan all layer contents: %w", err) } - zlog.Debug(ctx).Msg("layers scan ok") + slog.DebugContext(ctx, "layers scan ok") return Coalesce, nil } diff --git a/indexer/controller/scanlayers_test.go b/indexer/controller/scanlayers_test.go index a359b5365..b710bb0de 100644 --- a/indexer/controller/scanlayers_test.go +++ b/indexer/controller/scanlayers_test.go @@ -6,7 +6,6 @@ import ( "fmt" "testing" - "github.com/quay/zlog" "go.uber.org/mock/gomock" "github.com/quay/claircore" @@ -17,7 +16,6 @@ import ( ) func TestScanLayers(t *testing.T) { - ctx := context.Background() tt := []struct { mock func(t *testing.T) indexer.Store name string @@ -38,7 +36,7 @@ func TestScanLayers(t *testing.T) { for _, table := range tt { t.Run(table.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) s := table.mock(t) opts := &indexer.Options{ Store: s, @@ -62,7 +60,7 @@ func TestScanLayers(t *testing.T) { } func TestScanNoErrors(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) ctrl := gomock.NewController(t) store := indexer_mock.NewMockStore(ctrl) From 6cedab70eaaa17c345fe730fc11d693df44f487d Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Fri, 3 Oct 2025 12:54:52 -0500 Subject: [PATCH 25/77] pkg/ctxlock/v2: migrate to slog Signed-off-by: Hank Donnay --- pkg/ctxlock/v2/ctxlock.go | 67 +++++++++------------------------- pkg/ctxlock/v2/ctxlock_test.go | 4 -- pkg/ctxlock/v2/main_test.go | 4 +- 3 files changed, 20 insertions(+), 55 deletions(-) diff --git a/pkg/ctxlock/v2/ctxlock.go b/pkg/ctxlock/v2/ctxlock.go index 503d6116a..3b56b494e 100644 --- a/pkg/ctxlock/v2/ctxlock.go +++ b/pkg/ctxlock/v2/ctxlock.go @@ -8,16 +8,15 @@ import ( "context" "errors" "fmt" + "log/slog" "runtime" "runtime/pprof" "runtime/trace" - "strconv" "sync" "time" "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgxpool" - "github.com/quay/zlog" "github.com/quay/claircore/internal/dblock" ) @@ -93,26 +92,20 @@ var ( func (l *Locker) run(ctx context.Context) { ctx = pprof.WithLabels(ctx, pprof.Labels(tracelabel, `run`)) pprof.SetGoroutineLabels(ctx) - ctx = zlog.ContextWithValues(ctx, "component", "internal/ctxlock/Locker.run") for { tctx, done := context.WithTimeout(ctx, 5*time.Second) err := l.p.AcquireFunc(tctx, l.reconnect(ctx)) done() switch { case errors.Is(err, errExiting): - zlog.Debug(ctx). - Msg("ctxlocker exiting") + slog.DebugContext(ctx, "ctxlocker exiting") return case errors.Is(err, nil): return case errors.Is(err, context.DeadlineExceeded): - zlog.Info(ctx). - Err(err). - Msg("retrying immediately") + slog.InfoContext(ctx, "retrying immediately", "reason", err) default: - zlog.Warn(ctx). - Err(err). - Msg("unexpected error; retrying immediately") + slog.WarnContext(ctx, "unexpected error; retrying immediately", "reason", err) } } } @@ -133,7 +126,6 @@ func (l *Locker) Close(_ context.Context) (_ error) { // itself until awoken. All other methods should strobe the Cond to wake up this // loop and check if the connection has died. func (l *Locker) reconnect(ctx context.Context) func(*pgxpool.Conn) error { - ctx = zlog.ContextWithValues(ctx, "component", "internal/ctxlock/Locker.reconnect") return func(c *pgxpool.Conn) error { l.rc.L.Lock() defer l.rc.L.Unlock() @@ -141,15 +133,15 @@ func (l *Locker) reconnect(ctx context.Context) func(*pgxpool.Conn) error { l.gone = make(chan struct{}) l.cur = make(map[string]struct{}, 100) // Guess at a good capacity. l.gen++ - ctx = zlog.ContextWithValues(ctx, "gen", strconv.Itoa(l.gen)) + log := slog.With(slog.Int("gen", l.gen)) defer func() { close(l.gone) l.gone = nil l.conn = nil l.cur = nil - zlog.Debug(ctx).Msg("torn down") + log.DebugContext(ctx, "torn down") }() - zlog.Debug(ctx).Msg("set up") + log.DebugContext(ctx, "set up") l.rc.Broadcast() for l.gen > 0 { @@ -157,9 +149,7 @@ func (l *Locker) reconnect(ctx context.Context) func(*pgxpool.Conn) error { err := c.Ping(ctx) done() if err != nil { - zlog.Warn(ctx). - Err(err). - Msg("liveness check failed") + log.WarnContext(ctx, "liveness check failed", "reason", err) return err } l.rc.Wait() @@ -183,15 +173,6 @@ func (l *Locker) ping(ctx context.Context) { } } -/* -The TryLock and Lock methods do not add logging baggage, because the additional -allocations around the context.Context really stack up. Ensure that any -additional information is added to the zlog calls directly. - -I've left some Debug logs commented out because zlog needs to gain a level knob -for tests. Currently, the logs always happen and throw off benchmarks. -*/ - // TryLock attempts to lock on the provided key. // // If unsuccessful, an already-canceled Context will be returned. @@ -199,7 +180,8 @@ for tests. Currently, the logs always happen and throw off benchmarks. // If successful, the returned Context will be parented to the passed-in Context // and also to the underlying connection used for the lock. func (l *Locker) TryLock(parent context.Context, key string) (context.Context, context.CancelFunc) { - // zlog.Debug(parent).Str("key", key).Msg("trying lock") + a := slog.String("key", key) + slog.DebugContext(parent, "trying lock", a) defer trace.StartRegion(parent, pkgname+".TryLock").End() child, done := context.WithCancel(parent) w, err := l.try(parent, key, done) @@ -209,14 +191,9 @@ func (l *Locker) TryLock(parent context.Context, key string) (context.Context, c case errors.Is(err, errConnGone) || errors.Is(err, errLockFail) || errors.Is(err, errDoubleLock): - zlog.Debug(parent). - Err(err). - Str("key", key). - Msg("lock failed") + slog.DebugContext(parent, "lock failed", a, "reason", err) default: - zlog.Info(parent). - Err(err). - Msg("checking lock liveness") + slog.InfoContext(parent, "checking lock liveness", a, "reason", err) l.rc.Broadcast() } done() @@ -226,7 +203,8 @@ func (l *Locker) TryLock(parent context.Context, key string) (context.Context, c // Lock attempts to obtain the named lock until it succeeds or the passed // Context is canceled. func (l *Locker) Lock(parent context.Context, key string) (context.Context, context.CancelFunc) { - // zlog.Debug(parent).Str("key", key).Msg("locking") + a := slog.String("key", key) + slog.DebugContext(parent, "locking", a) defer trace.StartRegion(parent, pkgname+".Lock").End() child, done := context.WithCancel(parent) for wait := time.Duration(500 * time.Millisecond); ; backoff(&wait) { @@ -237,14 +215,9 @@ func (l *Locker) Lock(parent context.Context, key string) (context.Context, cont case errors.Is(err, errConnGone) || errors.Is(err, errLockFail) || errors.Is(err, errDoubleLock): - zlog.Debug(parent). - Err(err). - Str("key", key). - Msg("lock failed") + slog.DebugContext(parent, "lock failed", a, "reason", err) default: - zlog.Info(parent). - Err(err). - Msg("checking lock liveness") + slog.InfoContext(parent, "checking lock liveness", a, "reason", err) l.rc.Broadcast() } @@ -343,18 +316,14 @@ func (l *Locker) unlock(ctx context.Context, key string, kb []byte, gen int, nex [][]byte{kb}, nil, []int16{1}, nil).Close() if err != nil { - zlog.Debug(ctx). - Err(err). - Msg("error during unlock") + slog.DebugContext(ctx, "error during unlock", "reason", err) // Since we're in a different call path now, we need to signal on // error here, as well. l.rc.Broadcast() return } if _, ok := l.cur[key]; !ok || tag.RowsAffected() == 0 { - zlog.Error(ctx). - Str("key", key). - Msg("lock protocol botch") + slog.ErrorContext(ctx, "lock protocol botch", "key", key) } delete(l.cur, key) } diff --git a/pkg/ctxlock/v2/ctxlock_test.go b/pkg/ctxlock/v2/ctxlock_test.go index 32ce37c03..6e1e4d8c8 100644 --- a/pkg/ctxlock/v2/ctxlock_test.go +++ b/pkg/ctxlock/v2/ctxlock_test.go @@ -1,12 +1,10 @@ package ctxlock import ( - "strconv" "sync" "testing" "github.com/google/uuid" - "github.com/quay/zlog" ) func TestUncontested(t *testing.T) { @@ -32,7 +30,6 @@ func TestUncontested(t *testing.T) { for i := range wi { go func(i int) { defer wg.Done() - ctx := zlog.ContextWithValues(ctx, "worker", strconv.Itoa(i)) <-start t.Logf("worker %d: start", i) for _, id := range wi[i] { @@ -68,7 +65,6 @@ func TestContested(t *testing.T) { for i := range w { go func(i int) { defer wg.Done() - ctx := zlog.ContextWithValues(ctx, "worker", strconv.Itoa(i)) <-start t.Logf("worker %d: start", i) for _, id := range ids { diff --git a/pkg/ctxlock/v2/main_test.go b/pkg/ctxlock/v2/main_test.go index a8f476deb..f0e846306 100644 --- a/pkg/ctxlock/v2/main_test.go +++ b/pkg/ctxlock/v2/main_test.go @@ -7,15 +7,15 @@ import ( "github.com/jackc/pgx/v5/log/testingadapter" "github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/tracelog" - "github.com/quay/zlog" + "github.com/quay/claircore/test" "github.com/quay/claircore/test/integration" ) func basicSetup(t testing.TB) (context.Context, *Locker) { t.Helper() integration.NeedDB(t) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) // Setup the Database. db, err := integration.NewDB(ctx, t) From f02a1389d7a7f7b50f167e4e5cfbff497b1303c3 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Fri, 3 Oct 2025 13:29:25 -0500 Subject: [PATCH 26/77] indexer: migrate to slog Signed-off-by: Hank Donnay --- indexer/ecosystem.go | 5 +--- indexer/layerscanner.go | 58 ++++++++++++++--------------------------- 2 files changed, 21 insertions(+), 42 deletions(-) diff --git a/indexer/ecosystem.go b/indexer/ecosystem.go index 5f2ebc0e3..5e49267a0 100644 --- a/indexer/ecosystem.go +++ b/indexer/ecosystem.go @@ -2,11 +2,9 @@ package indexer import ( "context" - - "github.com/quay/zlog" ) -// Ecosystems group together scanners and a Coalescer which are commonly used +// An Ecosystem groups together scanners and a Coalescer which are commonly used // together. // // A typical ecosystem is "dpkg" which will use the "dpkg" package indexer, the @@ -26,7 +24,6 @@ type Ecosystem struct { // EcosystemsToScanners extracts and dedupes multiple ecosystems and returns // their discrete scanners. func EcosystemsToScanners(ctx context.Context, ecosystems []*Ecosystem) ([]PackageScanner, []DistributionScanner, []RepositoryScanner, []FileScanner, error) { - ctx = zlog.ContextWithValues(ctx, "component", "indexer/EcosystemsToScanners") ps := []PackageScanner{} ds := []DistributionScanner{} rs := []RepositoryScanner{} diff --git a/indexer/layerscanner.go b/indexer/layerscanner.go index 1f91b7e19..d93de7865 100644 --- a/indexer/layerscanner.go +++ b/indexer/layerscanner.go @@ -4,10 +4,11 @@ import ( "context" "errors" "fmt" + "log/slog" "net" "runtime" - "github.com/quay/zlog" + "github.com/quay/claircore/toolkit/log" "golang.org/x/sync/errgroup" "github.com/quay/claircore" @@ -30,13 +31,10 @@ type LayerScanner struct { // // The provided Context is only used for the duration of the call. func NewLayerScanner(ctx context.Context, concurrent int, opts *Options) (*LayerScanner, error) { - ctx = zlog.ContextWithValues(ctx, "component", "indexer.NewLayerScanner") - zlog.Info(ctx).Msg("NewLayerScanner: constructing a new layer-scanner") + slog.InfoContext(ctx, "constructing") switch { case concurrent < 1: - zlog.Warn(ctx). - Int("value", concurrent). - Msg("rectifying nonsense 'concurrent' argument") + slog.WarnContext(ctx, "rectifying nonsense 'concurrent' argument", "value", concurrent) fallthrough case concurrent == 0: concurrent = runtime.GOMAXPROCS(0) @@ -61,6 +59,7 @@ func configAndFilter[S VersionedScanner](ctx context.Context, opts *Options, ss i := 0 for _, s := range ss { n := s.Name() + log := slog.With("scanner", n) var cfgMap map[string]func(any) error switch k := s.Kind(); k { case "package": @@ -72,10 +71,7 @@ func configAndFilter[S VersionedScanner](ctx context.Context, opts *Options, ss case "file": cfgMap = opts.ScannerConfig.File default: - zlog.Warn(ctx). - Str("kind", k). - Str("scanner", n). - Msg("unknown scanner kind") + log.WarnContext(ctx, "unknown scanner kind", "kind", k) continue } @@ -87,25 +83,17 @@ func configAndFilter[S VersionedScanner](ctx context.Context, opts *Options, ss rs, rsOK := any(s).(RPCScanner) switch { case haveCfg && !csOK && !rsOK: - zlog.Warn(ctx). - Str("scanner", n). - Msg("configuration present for an unconfigurable scanner, skipping") + log.WarnContext(ctx, "configuration present for an unconfigurable scanner, skipping") case csOK && rsOK: fallthrough case !csOK && rsOK: if err := rs.Configure(ctx, f, opts.Client); err != nil { - zlog.Error(ctx). - Str("scanner", n). - Err(err). - Msg("configuration failed") + log.ErrorContext(ctx, "configuration failed", "reason", err) continue } case csOK && !rsOK: if err := cs.Configure(ctx, f); err != nil { - zlog.Error(ctx). - Str("scanner", n). - Err(err). - Msg("configuration failed") + log.ErrorContext(ctx, "configuration failed", "reason", err) continue } } @@ -125,9 +113,7 @@ func configAndFilter[S VersionedScanner](ctx context.Context, opts *Options, ss // The provided Context controls cancellation for all scanners. The first error // reported halts all work and is returned from Scan. func (ls *LayerScanner) Scan(ctx context.Context, manifest claircore.Digest, layers []*claircore.Layer) error { - ctx = zlog.ContextWithValues(ctx, - "component", "indexer/LayerScanner.Scan", - "manifest", manifest.String()) + ctx = log.With(ctx, "manifest", manifest) g, ctx := errgroup.WithContext(ctx) // Using the goroutine's built-in limit is worst-case the same as using an @@ -183,20 +169,16 @@ Layers: // ScanLayer (along with the result type) handles an individual (scanner, layer) // pair. func (ls *LayerScanner) scanLayer(ctx context.Context, l *claircore.Layer, s VersionedScanner) error { - ctx = zlog.ContextWithValues(ctx, - "component", "indexer/LayerScanner.scanLayer", - "scanner", s.Name(), - "kind", s.Kind(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("scan start") - defer zlog.Debug(ctx).Msg("scan done") + ctx = log.With(ctx, "scanner", s.Name(), "kind", s.Kind(), "layer", l.Hash) + slog.DebugContext(ctx, "scan start") + defer slog.DebugContext(ctx, "scan done") ok, err := ls.store.LayerScanned(ctx, l.Hash, s) if err != nil { return err } if ok { - zlog.Debug(ctx).Msg("layer already scanned") + slog.DebugContext(ctx, "layer already scanned") return nil } @@ -251,10 +233,10 @@ func (r *result) Do(ctx context.Context, s VersionedScanner, l *claircore.Layer) switch { case errors.Is(err, nil): case errors.As(err, &addrErr): - zlog.Warn(ctx).Str("scanner", s.Name()).Err(err).Msg("scanner not able to access resources") + slog.WarnContext(ctx, "scanner not able to access resources", "scanner", s.Name(), "reason", err) return nil default: - zlog.Info(ctx).Err(err).Send() + slog.InfoContext(ctx, "unexpected error", "reason", err) } return err @@ -264,25 +246,25 @@ func (r *result) Do(ctx context.Context, s VersionedScanner, l *claircore.Layer) // the result. func (r *result) Store(ctx context.Context, store Store, s VersionedScanner, l *claircore.Layer) error { if r.pkgs != nil { - zlog.Debug(ctx).Int("count", len(r.pkgs)).Msg("scan returned packages") + slog.DebugContext(ctx, "scan returned packages", "count", len(r.pkgs)) if err := store.IndexPackages(ctx, r.pkgs, l, s); err != nil { return err } } if r.dists != nil { - zlog.Debug(ctx).Int("count", len(r.dists)).Msg("scan returned dists") + slog.DebugContext(ctx, "scan returned distributions", "count", len(r.dists)) if err := store.IndexDistributions(ctx, r.dists, l, s); err != nil { return err } } if r.repos != nil { - zlog.Debug(ctx).Int("count", len(r.repos)).Msg("scan returned repos") + slog.DebugContext(ctx, "scan returned repositories", "count", len(r.repos)) if err := store.IndexRepositories(ctx, r.repos, l, s); err != nil { return err } } if r.files != nil { - zlog.Debug(ctx).Int("count", len(r.files)).Msg("scan returned files") + slog.DebugContext(ctx, "scan returned files", "count", len(r.files)) if err := store.IndexFiles(ctx, r.files, l, s); err != nil { return err } From d302572076b397b6c45e7b6204b69898a30374d5 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Fri, 3 Oct 2025 13:51:26 -0500 Subject: [PATCH 27/77] rhel/internal/common: migrate to slog Signed-off-by: Hank Donnay --- rhel/internal/common/updater.go | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/rhel/internal/common/updater.go b/rhel/internal/common/updater.go index 41c0fad5b..6fb9828a7 100644 --- a/rhel/internal/common/updater.go +++ b/rhel/internal/common/updater.go @@ -4,13 +4,13 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "reflect" "sync" "sync/atomic" "time" - "github.com/quay/zlog" "golang.org/x/time/rate" ) @@ -44,16 +44,12 @@ func NewUpdater(url string, init any) *Updater { // Get returns a pointer to the current copy of the value. The Get call may be // hijacked to update the value from the configured endpoint. func (u *Updater) Get(ctx context.Context, c *http.Client) (any, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "rhel/internal/common/Updater.Get") var err error if u.url != "" && u.reqRate.Allow() { - zlog.Debug(ctx).Msg("got unlucky, updating mapping file") + slog.DebugContext(ctx, "got unlucky, updating mapping file") err = u.Fetch(ctx, c) if err != nil { - zlog.Error(ctx). - Err(err). - Msg("error updating mapping file") + slog.ErrorContext(ctx, "error updating mapping file", "reason", err) } } @@ -64,10 +60,8 @@ func (u *Updater) Get(ctx context.Context, c *http.Client) (any, error) { // // Fetch is safe to call concurrently. func (u *Updater) Fetch(ctx context.Context, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, - "component", "rhel/internal/common/Updater.Fetch", - "url", u.url) - zlog.Debug(ctx).Msg("attempting fetch of mapping file") + log := slog.With("url", u.url) + log.DebugContext(ctx, "attempting fetch of mapping file") req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.url, nil) if err != nil { @@ -87,9 +81,7 @@ func (u *Updater) Fetch(ctx context.Context, c *http.Client) error { switch resp.StatusCode { case http.StatusOK: case http.StatusNotModified: - zlog.Debug(ctx). - Str("since", u.lastModified). - Msg("response not modified; no update necessary") + log.DebugContext(ctx, "response not modified; no update necessary", "since", u.lastModified) return nil default: return fmt.Errorf("received status code %d querying mapping url", resp.StatusCode) @@ -105,6 +97,6 @@ func (u *Updater) Fetch(ctx context.Context, c *http.Client) error { u.mu.Unlock() // atomic store of mapping file u.value.Store(v) - zlog.Debug(ctx).Msg("atomic update of local mapping file complete") + log.DebugContext(ctx, "atomic update of local mapping file complete") return nil } From 860efaad71e3ba686c27cf5f4794a80dc3194229 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Fri, 3 Oct 2025 14:33:06 -0500 Subject: [PATCH 28/77] rhel/vex: migrate to slog Signed-off-by: Hank Donnay --- rhel/vex/fetcher.go | 30 +++++------ rhel/vex/fetcher_test.go | 5 +- rhel/vex/parser.go | 110 ++++++++++++--------------------------- rhel/vex/parser_test.go | 9 ++-- rhel/vex/updater.go | 9 +--- 5 files changed, 53 insertions(+), 110 deletions(-) diff --git a/rhel/vex/fetcher.go b/rhel/vex/fetcher.go index 0ab3b5615..552ec50ac 100644 --- a/rhel/vex/fetcher.go +++ b/rhel/vex/fetcher.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "net/url" "path" @@ -18,7 +19,6 @@ import ( "time" "github.com/klauspost/compress/snappy" - "github.com/quay/zlog" "github.com/quay/claircore/internal/httputil" "github.com/quay/claircore/internal/zreader" @@ -36,14 +36,13 @@ var ( // - Make a request to discover the latest archive endpoint. // - Make a HEAD request to archive endpoint to get the last-modified header. // - Save the last-modified time in the fingerprint's requestTime. -// 2. Process the changes.csv file, requesting and appending the entries that changed since the finderprint's requestTime. -// 3. Process the deletions.csv file, processing the entries that changed since the finderprint's requestTime. +// 2. Process the changes.csv file, requesting and appending the entries that changed since the fingerprint's requestTime. +// 3. Process the deletions.csv file, processing the entries that changed since the fingerprint's requestTime. // 4. If we need to process entire archive, request the archive data and append the entries that have not been changed or deleted. // // This helps to ensure that we only persist one copy of an advisory in the worst possible case. In most cases, // after the initial load, the number of processed files should be very small. func (u *Updater) Fetch(ctx context.Context, hint driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { - ctx = zlog.ContextWithValues(ctx, "component", "rhel/vex/Updater.Fetch") fp, err := parseFingerprint(hint) if err != nil { return nil, hint, err @@ -59,16 +58,14 @@ func (u *Updater) Fetch(ctx context.Context, hint driver.Fingerprint) (io.ReadCl var success bool defer func() { if err := cw.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close snappy writer") + slog.WarnContext(ctx, "unable to close snappy writer", "reason", err) } if _, err := f.Seek(0, io.SeekStart); err != nil { - zlog.Warn(ctx). - Err(err). - Msg("unable to seek file back to start") + slog.WarnContext(ctx, "unable to seek file back to start", "reason", err) } if !success { if err := f.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close spool") + slog.WarnContext(ctx, "unable to close spool", "reason", err) } } }() @@ -84,9 +81,7 @@ func (u *Updater) Fetch(ctx context.Context, hint driver.Fingerprint) (io.ReadCl if err != nil { return nil, hint, fmt.Errorf("could not get compressed file URL: %w", err) } - zlog.Debug(ctx). - Str("url", compressedURL.String()). - Msg("got compressed URL") + slog.DebugContext(ctx, "got compressed URL", "url", compressedURL) fp.requestTime, err = u.getLastModified(ctx, compressedURL) if err != nil { @@ -178,10 +173,9 @@ func (u *Updater) Fetch(ctx context.Context, hint driver.Fingerprint) (io.ReadCl return nil, hint, fmt.Errorf("error reading tar contents: %w", err) } - zlog.Debug(ctx). - Str("updater", u.Name()). - Int("entries written", entriesWritten). - Msg("finished writing compressed data to spool") + slog.DebugContext(ctx, "finished writing compressed data to spool", + "updater", u.Name(), + "entries written", entriesWritten) } fp.version = updaterVersion @@ -353,7 +347,7 @@ func (u *Updater) processChanges(ctx context.Context, w io.Writer, fp *fingerpri if err != nil { return fmt.Errorf("error reading from buffer: %w", err) } - zlog.Debug(ctx).Str("url", advisoryURI.String()).Msg("copying body to file") + slog.DebugContext(ctx, "copying body to file", "url", advisoryURI) err = json.Compact(&bc, buf.Bytes()) if err != nil { return fmt.Errorf("error compressing JSON: %w", err) @@ -434,7 +428,7 @@ func (u *Updater) processDeletions(ctx context.Context, w io.Writer, fp *fingerp deletedJSON, err := createDeletedJSON(cvePath) if err != nil { - zlog.Warn(ctx).Err(err).Msg("error creating JSON object denoting deletion") + slog.WarnContext(ctx, "error creating JSON object denoting deletion", "reason", err) } bc.Write(deletedJSON) bc.WriteByte('\n') diff --git a/rhel/vex/fetcher_test.go b/rhel/vex/fetcher_test.go index 588a0ec70..ac5026e70 100644 --- a/rhel/vex/fetcher_test.go +++ b/rhel/vex/fetcher_test.go @@ -3,18 +3,17 @@ package vex import ( "bufio" "bytes" - "context" "testing" "github.com/klauspost/compress/snappy" - "github.com/quay/zlog" "github.com/quay/claircore/libvuln/driver" + "github.com/quay/claircore/test" "github.com/quay/claircore/toolkit/types/csaf" ) func TestFactory(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) root, c := ServeSecDB(t, "testdata/server.txtar") fac := &Factory{} err := fac.Configure(ctx, func(v any) error { diff --git a/rhel/vex/parser.go b/rhel/vex/parser.go index 39a0b803c..d624728c6 100644 --- a/rhel/vex/parser.go +++ b/rhel/vex/parser.go @@ -7,13 +7,13 @@ import ( "errors" "fmt" "io" + "log/slog" "math" "strings" "unique" "github.com/klauspost/compress/snappy" "github.com/package-url/packageurl-go" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/pkg/rhctag" @@ -32,7 +32,6 @@ func (u *Updater) Parse(ctx context.Context, contents io.ReadCloser) ([]*clairco // DeltaParse implements [driver.DeltaUpdater]. func (u *Updater) DeltaParse(ctx context.Context, contents io.ReadCloser) ([]*claircore.Vulnerability, []string, error) { - ctx = zlog.ContextWithValues(ctx, "component", "rhel/vex/Updater.DeltaParse") // This map is needed for deduplication purposes, the compressed CSAF data will include // entries that have been subsequently updated in the changes. out := map[string][]*claircore.Vulnerability{} @@ -59,7 +58,6 @@ func (u *Updater) DeltaParse(ctx context.Context, contents io.ReadCloser) ([]*cl selfLink = r.URL } } - ctx = zlog.ContextWithValues(ctx, "link", selfLink) creator := newCreator(name, selfLink, c, pc, rc) for _, v := range c.Vulnerabilities { // Create vuln here, there should always be one vulnerability @@ -252,7 +250,8 @@ func extractProductNames(prodRef, relatesToProdRef string, comps []string, c *cs func (c *creator) knownAffectedVulnerabilities(ctx context.Context, v csaf.Vulnerability, protoVulnFunc func() *claircore.Vulnerability) ([]*claircore.Vulnerability, error) { ranger := newRanger() unrelatedProductIDs := []string{} - debugEnabled := zlog.Debug(ctx).Enabled() + log := slog.With("link", c.vulnLink) + debugEnabled := log.Enabled(ctx, slog.LevelDebug) out := []*claircore.Vulnerability{} for _, pc := range v.ProductStatus["known_affected"] { pkgName, modName, repoName, err := walkRelationships(pc, c.c) @@ -272,16 +271,13 @@ func (c *creator) knownAffectedVulnerabilities(ctx context.Context, v csaf.Vulne repoProd := c.pc.Get(repoName, c.c) if repoProd == nil { - zlog.Warn(ctx). - Str("prod", repoName). - Msg("could not find product in product tree") + // Should never get here, error in data + log.WarnContext(ctx, "could not find product in product tree", "prod", repoName) continue } cpeHelper, ok := repoProd.IdentificationHelper["cpe"] if !ok { - zlog.Warn(ctx). - Str("prod", repoName). - Msg("could not find cpe helper type in product") + log.WarnContext(ctx, "could not find cpe helper type in product", "prod", repoName) continue } @@ -291,10 +287,7 @@ func (c *creator) knownAffectedVulnerabilities(ctx context.Context, v csaf.Vulne modProd := c.pc.Get(modName, c.c) modName, err = createPackageModule(modProd) if err != nil { - zlog.Warn(ctx). - Str("module", modName). - Err(err). - Msg("could not create package module") + log.WarnContext(ctx, "could not create package module", "module", modName, "reason", err) } } @@ -302,9 +295,7 @@ func (c *creator) knownAffectedVulnerabilities(ctx context.Context, v csaf.Vulne compProd := c.pc.Get(pkgName, c.c) if compProd == nil { // Should never get here, error in data - zlog.Warn(ctx). - Str("pkg", pkgName). - Msg("could not find package in product tree") + log.WarnContext(ctx, "could not find package in product tree", "pkg", pkgName) continue } vuln := protoVulnFunc() @@ -321,29 +312,22 @@ func (c *creator) knownAffectedVulnerabilities(ctx context.Context, v csaf.Vulne if ok { purl, err := packageurl.FromString(purlHelper) if err != nil { - zlog.Warn(ctx). - Str("purlHelper", purlHelper). - Err(err). - Msg("could not parse PURL") + log.WarnContext(ctx, "could not parse pURL", "reason", err, "purl", purlHelper) continue } if !checkPURL(purl) { continue } if pn, err := extractPackageName(purl); err != nil { - zlog.Warn(ctx). - Str("purl", purl.String()). - Err(err). - Msg("could not extract package name from pURL") + log.WarnContext(ctx, "error extracting package name from pURL", + "reason", err, "purl", purl) } else { pkgName = pn } if modName, err = componentPURLToModuleName(purl); err != nil { - zlog.Warn(ctx). - Str("purl", purl.String()). - Err(err). - Msg("invalid rpmmod in component pURL") + log.WarnContext(ctx, "invalid rpmmod in component pURL", + "reason", err, "purl", purl) continue } @@ -351,10 +335,8 @@ func (c *creator) knownAffectedVulnerabilities(ctx context.Context, v csaf.Vulne vuln.Repo = c.rc.Get(wfn, rhcc.RepositoryKey) vuln.Range, err = ranger.add(pkgName, vuln.FixedInVersion) if err != nil { - zlog.Warn(ctx). - Str("FixedInVersion", vuln.FixedInVersion). - Err(err). - Msg("could not parse version into range") + log.WarnContext(ctx, "could not parse version into range", + "reason", err, "FixedInVersion", vuln.FixedInVersion) continue } } @@ -387,9 +369,7 @@ func (c *creator) knownAffectedVulnerabilities(ctx context.Context, v csaf.Vulne } ranger.resetLowest() if len(unrelatedProductIDs) > 0 { - zlog.Debug(ctx). - Strs("product_ids", unrelatedProductIDs). - Msg("skipped unrelatable product_ids") + log.DebugContext(ctx, "skipped unrelatable product_ids", "product_ids", unrelatedProductIDs) } return out, nil @@ -424,7 +404,7 @@ func newRanger() *ranger { // to the fixedInVersion. Add also saves the lowest range per packageName that it's seen. // This allows resetLowest() to zero out the lowest values we saw per packageName. func (r *ranger) add(packageName, fixedInVersion string) (*claircore.Range, error) { - var rng = &claircore.Range{} + rng := &claircore.Range{} if fixedInVersion == "" { zeroVer := &rhctag.Version{} @@ -463,7 +443,8 @@ func (r *ranger) resetLowest() { func (c *creator) fixedVulnerabilities(ctx context.Context, v csaf.Vulnerability, protoVulnFunc func() *claircore.Vulnerability) ([]*claircore.Vulnerability, error) { ranger := newRanger() unrelatedProductIDs := []string{} - debugEnabled := zlog.Debug(ctx).Enabled() + log := slog.With("link", c.vulnLink) + debugEnabled := log.Enabled(ctx, slog.LevelDebug) for _, pc := range v.ProductStatus["fixed"] { pkgName, modName, repoName, err := walkRelationships(pc, c.c) if err != nil { @@ -478,16 +459,12 @@ func (c *creator) fixedVulnerabilities(ctx context.Context, v csaf.Vulnerability repoProd := c.pc.Get(repoName, c.c) if repoProd == nil { // Should never get here, error in data - zlog.Warn(ctx). - Str("prod", repoName). - Msg("could not find product in product tree") + log.WarnContext(ctx, "could not find product in product tree", "prod", repoName) continue } cpeHelper, ok := repoProd.IdentificationHelper["cpe"] if !ok { - zlog.Warn(ctx). - Str("prod", repoName). - Msg("could not find cpe helper type in product") + log.WarnContext(ctx, "could not find cpe helper type in product", "prod", repoName) continue } @@ -497,33 +474,24 @@ func (c *creator) fixedVulnerabilities(ctx context.Context, v csaf.Vulnerability modProd := c.pc.Get(modName, c.c) modName, err = createPackageModule(modProd) if err != nil { - zlog.Warn(ctx). - Str("module", modName). - Err(err). - Msg("could not create package module") + log.WarnContext(ctx, "could not create package module", "module", modName, "reason", err) } } compProd := c.pc.Get(pkgName, c.c) if compProd == nil { // Should never get here, error in data - zlog.Warn(ctx). - Str("pkg", pkgName). - Msg("could not find package in product tree") + log.WarnContext(ctx, "could not find package in product tree", "pkg", pkgName) continue } purlHelper, ok := compProd.IdentificationHelper["purl"] if !ok { - zlog.Warn(ctx). - Str("pkg", pkgName). - Msg("could not find purl helper type in product") + log.WarnContext(ctx, "could not find purl helper type in product", "pkg", pkgName) continue } purl, err := packageurl.FromString(purlHelper) if err != nil { - zlog.Warn(ctx). - Str("purl", purlHelper). - Msg("could not parse PURL") + log.WarnContext(ctx, "could not parse pURL", "reason", err, "purl", purlHelper) continue } if !checkPURL(purl) { @@ -531,25 +499,19 @@ func (c *creator) fixedVulnerabilities(ctx context.Context, v csaf.Vulnerability } fixedIn, err := extractFixedInVersion(purl) if err != nil { - zlog.Warn(ctx). - Str("purl", purl.String()). - Err(err). - Msg("error extracting fixed_in version from pURL") + log.WarnContext(ctx, "error extracting fixed_in version from pURL", + "reason", err, "purl", purl) continue } packageName, err := extractPackageName(purl) if err != nil { - zlog.Warn(ctx). - Str("purl", purl.String()). - Err(err). - Msg("error extracting package name from pURL") + log.WarnContext(ctx, "error extracting package name from pURL", + "reason", err, "purl", purl) continue } if modName, err = componentPURLToModuleName(purl); err != nil { - zlog.Warn(ctx). - Str("purl", purl.String()). - Err(err). - Msg("invalid rpmmod in component pURL") + log.WarnContext(ctx, "invalid rpmmod in component pURL", + "reason", err, "purl", purl) continue } @@ -583,10 +545,8 @@ func (c *creator) fixedVulnerabilities(ctx context.Context, v csaf.Vulnerability vuln.Repo = c.rc.Get(wfn, rhcc.RepositoryKey) vuln.Range, err = ranger.add(packageName, vuln.FixedInVersion) if err != nil { - zlog.Warn(ctx). - Str("FixedInVersion", vuln.FixedInVersion). - Err(err). - Msg("could not parse version into range") + log.WarnContext(ctx, "could not parse version into range", + "reason", err, "FixedInVersion", vuln.FixedInVersion) continue } default: @@ -617,9 +577,7 @@ func (c *creator) fixedVulnerabilities(ctx context.Context, v csaf.Vulnerability } ranger.resetLowest() if len(unrelatedProductIDs) > 0 { - zlog.Debug(ctx). - Strs("product_ids", unrelatedProductIDs). - Msg("skipped unrelatable product_ids") + log.DebugContext(ctx, "skipped unrelatable product_ids", "product_ids", unrelatedProductIDs) } out := make([]*claircore.Vulnerability, len(c.fixedVulns)) diff --git a/rhel/vex/parser_test.go b/rhel/vex/parser_test.go index af86f26c5..11311779e 100644 --- a/rhel/vex/parser_test.go +++ b/rhel/vex/parser_test.go @@ -2,7 +2,6 @@ package vex import ( "bytes" - "context" "encoding/json" "errors" "io" @@ -15,10 +14,10 @@ import ( "github.com/google/go-cmp/cmp" "github.com/klauspost/compress/snappy" "github.com/package-url/packageurl-go" - "github.com/quay/zlog" "github.com/quay/claircore" + "github.com/quay/claircore/test" "github.com/quay/claircore/toolkit/types/cpe" "github.com/quay/claircore/toolkit/types/csaf" ) @@ -373,7 +372,6 @@ func TestEscapeCPE(t *testing.T) { func TestParseCompare(t *testing.T) { t.Parallel() - ctx := context.Background() url, err := url.Parse(BaseURL) if err != nil { t.Error(err) @@ -415,7 +413,7 @@ func TestParseCompare(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) f, err := os.Open(tc.filename) if err != nil { t.Fatalf("failed to open test data file %s: %v", tc.filename, err) @@ -458,7 +456,6 @@ func TestParseCompare(t *testing.T) { func TestParse(t *testing.T) { t.Parallel() - ctx := context.Background() url, err := url.Parse(BaseURL) if err != nil { t.Error(err) @@ -520,7 +517,7 @@ func TestParse(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) // Build a snappy stream consisting of compacted JSON blobs from each file, // newline-separated to simulate JSONL input across multiple files. diff --git a/rhel/vex/updater.go b/rhel/vex/updater.go index 3db969136..36285164a 100644 --- a/rhel/vex/updater.go +++ b/rhel/vex/updater.go @@ -4,13 +4,12 @@ import ( "context" "errors" "fmt" + "log/slog" "net/http" "net/url" "strings" "time" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" ) @@ -163,8 +162,6 @@ type UpdaterConfig struct { // Configure implements driver.Configurable. func (u *Updater) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, "component", "rhel/vex/Updater.Configure") - var cfg UpdaterConfig if err := f(&cfg); err != nil { return err @@ -178,9 +175,7 @@ func (u *Updater) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c * return err } u.url = url - zlog.Info(ctx). - Str("updater", u.Name()). - Msg("configured url") + slog.InfoContext(ctx, "configured url", "updater", u.Name()) u.client = c return nil From 39aa522c5ed6574ce10fbdd3040a0a9237a19ac4 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Fri, 3 Oct 2025 14:40:46 -0500 Subject: [PATCH 29/77] rhel/rhcc: migrate to slog Signed-off-by: Hank Donnay --- rhel/rhcc/coalescer_test.go | 5 +---- rhel/rhcc/detector.go | 10 +++------- rhel/rhcc/detector_test.go | 10 ++++------ rhel/rhcc/matcher.go | 12 +++--------- rhel/rhcc/scanner.go | 22 ++++++---------------- rhel/rhcc/scanner_test.go | 6 ++---- 6 files changed, 19 insertions(+), 46 deletions(-) diff --git a/rhel/rhcc/coalescer_test.go b/rhel/rhcc/coalescer_test.go index fa5a281fc..edd571d46 100644 --- a/rhel/rhcc/coalescer_test.go +++ b/rhel/rhcc/coalescer_test.go @@ -1,12 +1,9 @@ package rhcc import ( - "context" "encoding/json" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/test" @@ -14,7 +11,7 @@ import ( func TestCoalescer(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) coalescer := &coalescer{} repo := []*claircore.Repository{&GoldRepo} repo[0].ID = "1" // Assign it an ID and check it later. diff --git a/rhel/rhcc/detector.go b/rhel/rhcc/detector.go index 675e54e3f..c82868379 100644 --- a/rhel/rhcc/detector.go +++ b/rhel/rhcc/detector.go @@ -6,11 +6,10 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "strconv" "time" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/pkg/rhctag" @@ -43,7 +42,6 @@ func (s *detector) Kind() string { return "package" } // It performs a package scan on the given layer and returns all the RHEL // container identifying metadata by using the labels.json file. func (s *detector) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Package, error) { - ctx = zlog.ContextWithValues(ctx, "component", "rhel/rhcc/detector.Scan") if err := ctx.Err(); err != nil { return nil, err } @@ -62,7 +60,7 @@ func (s *detector) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.P // Check if required fields are present if labels.Name == "" || labels.Architecture == "" || labels.Created.IsZero() { - zlog.Warn(ctx).Msg("required labels not found in labels.json") + slog.WarnContext(ctx, "required labels not found in labels.json") return nil, nil } @@ -70,7 +68,7 @@ func (s *detector) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.P rhctagVersion, err := rhctag.Parse(vr) if err != nil { // A unix timestamp should always parse, so this is unexpected. - zlog.Warn(ctx).Err(err).Str("version", vr).Msg("failed to parse rhctag version") + slog.WarnContext(ctx, "failed to parse rhctag version", "reason", err, "version", vr) return nil, nil } @@ -146,7 +144,6 @@ func (s *repoDetector) Kind() string { return "repository" } // It performs a repository scan on the given layer and returns all the RHEL // container identifying metadata by using the labels.json file. func (s *repoDetector) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Repository, error) { - ctx = zlog.ContextWithValues(ctx, "component", "rhel/rhcc/repoDetector.Scan") if err := ctx.Err(); err != nil { return nil, err } @@ -176,5 +173,4 @@ func (s *repoDetector) Scan(ctx context.Context, l *claircore.Layer) ([]*clairco Key: RepositoryKey, }, }, nil - } diff --git a/rhel/rhcc/detector_test.go b/rhel/rhcc/detector_test.go index 1c197e352..c2010c628 100644 --- a/rhel/rhcc/detector_test.go +++ b/rhel/rhcc/detector_test.go @@ -2,14 +2,12 @@ package rhcc import ( "archive/tar" - "context" "io" "os" "strings" "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/test" @@ -18,7 +16,7 @@ import ( func TestPackageDetector(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) type testcase struct { Name string @@ -130,7 +128,7 @@ func TestPackageDetector(t *testing.T) { }) for _, tt := range table { t.Run(tt.Name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) mod := test.Modtime(t, tt.LabelsFile) a.GenerateLayer(t, tt.Name, mod, genLayerFunc(tt.LabelsFile, tt.LabelsPath)) @@ -164,7 +162,7 @@ func TestPackageDetector(t *testing.T) { func TestRepositoryDetector(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) type testcase struct { Name string @@ -218,7 +216,7 @@ func TestRepositoryDetector(t *testing.T) { }) for _, tt := range table { t.Run(tt.Name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) mod := test.Modtime(t, tt.LabelsFile) a.GenerateLayer(t, tt.Name, mod, genLayerFunc(tt.LabelsFile, tt.LabelsPath)) diff --git a/rhel/rhcc/matcher.go b/rhel/rhcc/matcher.go index fcc7c52d5..1b156b61b 100644 --- a/rhel/rhcc/matcher.go +++ b/rhel/rhcc/matcher.go @@ -2,9 +2,9 @@ package rhcc import ( "context" + "log/slog" rpmVersion "github.com/knqyf263/go-rpm-version" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" @@ -43,10 +43,7 @@ func (*matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v // This is not a gold repo record, so we need to check if the CPE matches. vuln.Repo.CPE, err = cpe.Unbind(vuln.Repo.Name) if err != nil { - zlog.Warn(ctx). - Str("vulnerability name", vuln.Name). - Err(err). - Msg("unable to unbind repo CPE") + slog.WarnContext(ctx, "unable to unbind repo CPE", "reason", err, "vulnerability name", vuln.Name) return false, nil } if !cpe.Compare(vuln.Repo.CPE, record.Repository.CPE).IsSuperset() && !rhel.IsCPESubstringMatch(record.Repository.CPE, vuln.Repo.CPE) { @@ -54,10 +51,7 @@ func (*matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v } } pkgVer, fixedInVer := rpmVersion.NewVersion(record.Package.Version), rpmVersion.NewVersion(vuln.FixedInVersion) - zlog.Debug(ctx). - Str("record", record.Package.Version). - Str("vulnerability", vuln.FixedInVersion). - Msg("comparing versions") + slog.DebugContext(ctx, "comparing versions", "record", record.Package.Version, "vulnerability", vuln.FixedInVersion) return pkgVer.LessThan(fixedInVer), nil } diff --git a/rhel/rhcc/scanner.go b/rhel/rhcc/scanner.go index 39e2f27de..bb5a9b105 100644 --- a/rhel/rhcc/scanner.go +++ b/rhel/rhcc/scanner.go @@ -6,13 +6,12 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "net/http" "os" "strings" "time" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/internal/zreader" @@ -62,7 +61,6 @@ const DefaultName2ReposMappingURL = "https://security.access.redhat.com/data/met // Configure implements [indexer.RPCScanner]. func (s *scanner) Configure(ctx context.Context, f indexer.ConfigDeserializer, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, "component", "rhel/rhcc/scanner.Configure") s.client = c if err := f(&s.cfg); err != nil { return err @@ -117,7 +115,6 @@ func (s *scanner) Kind() string { return "package" } // Scan implements [indexer.PackageScanner]. func (s *scanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Package, error) { - ctx = zlog.ContextWithValues(ctx, "component", "rhel/rhcc/scanner.Scan") const ( compLabel = `com.redhat.component` nameLabel = `name` @@ -160,7 +157,7 @@ func (s *scanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Pa var ok bool (*chk.Found), ok = labels[chk.Want] if !ok { - zlog.Info(ctx).Str("label", chk.Want).Msg("expected label not found in dockerfile") + slog.InfoContext(ctx, "expected label not found in dockerfile", "label", chk.Want) return nil, nil } } @@ -189,8 +186,7 @@ func (s *scanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Pa } repos, ok := v.Data[name] if ok { - zlog.Debug(ctx).Str("name", name). - Msg("name present in mapping file") + slog.DebugContext(ctx, "name present in mapping file", "name", name) } else { // Didn't find external_repos in mapping, use name label as package // name. @@ -229,9 +225,7 @@ func findLabels(ctx context.Context, sys fs.FS) (map[string]string, string, erro if len(ms) == 0 { return nil, "", errNotFound } - zlog.Debug(ctx). - Strs("paths", ms). - Msg("found possible buildinfo Dockerfile(s)") + slog.DebugContext(ctx, "found possible buildinfo Dockerfile(s)", "paths", ms) var p string for _, m := range ms { if strings.Count(m, "-") > 1 { @@ -242,9 +236,7 @@ func findLabels(ctx context.Context, sys fs.FS) (map[string]string, string, erro if p == "" { return nil, "", errNotFound } - zlog.Info(ctx). - Str("path", p). - Msg("found buildinfo Dockerfile") + slog.InfoContext(ctx, "found buildinfo Dockerfile", "path", p) f, err := sys.Open(p) if err != nil { return nil, "", err @@ -287,7 +279,6 @@ func (s *reposcanner) Kind() string { return "repository" } // Scan implements [indexer.RepositoryScanner]. func (s *reposcanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Repository, error) { - ctx = zlog.ContextWithValues(ctx, "component", "rhel/rhcc/reposcanner.Scan") sys, err := l.FS() if err != nil { return nil, fmt.Errorf("rhcc: unable to open layer: %w", err) @@ -299,7 +290,6 @@ func (s *reposcanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircor if len(ms) == 0 { return nil, nil } - zlog.Debug(ctx). - Msg("found buildinfo Dockerfile") + slog.DebugContext(ctx, "found buildinfo Dockerfile") return []*claircore.Repository{&GoldRepo}, nil } diff --git a/rhel/rhcc/scanner_test.go b/rhel/rhcc/scanner_test.go index 30e03199c..f38fd9af8 100644 --- a/rhel/rhcc/scanner_test.go +++ b/rhel/rhcc/scanner_test.go @@ -2,7 +2,6 @@ package rhcc import ( "archive/tar" - "context" "encoding/json" "io" "net/http" @@ -13,7 +12,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/test" @@ -21,7 +19,7 @@ import ( func TestContainerScanner(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) clairSourceContainer := &claircore.Package{ Name: "quay-clair-container", Version: "v3.5.5-4", @@ -191,7 +189,7 @@ func TestContainerScanner(t *testing.T) { }) for _, tt := range table { t.Run(tt.Name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) mod := test.Modtime(t, tt.Dockerfile) a.GenerateLayer(t, tt.Name, mod, func(t testing.TB, w *os.File) { dockerfile, err := os.Open(tt.Dockerfile) From b940c7073859857685b88275632e4295e334e0be Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Fri, 3 Oct 2025 15:16:22 -0500 Subject: [PATCH 30/77] rhel: migrate to slog Signed-off-by: Hank Donnay --- rhel/coalescer.go | 7 ++----- rhel/coalescer_test.go | 5 ++--- rhel/distributionscanner.go | 13 ++++--------- rhel/matcher.go | 7 ++----- rhel/matcher_test.go | 7 ++----- rhel/matcherfactory.go | 12 ++++-------- rhel/packagescanner.go | 16 ++++++---------- rhel/packagescanner_test.go | 10 +++------- rhel/repositoryscanner.go | 31 ++++++++++--------------------- rhel/repositoryscanner_test.go | 7 ++----- rhel/rhel.go | 14 +++++--------- rhel/rhel_test.go | 5 +---- 12 files changed, 43 insertions(+), 91 deletions(-) diff --git a/rhel/coalescer.go b/rhel/coalescer.go index 0d017354a..faa99b323 100644 --- a/rhel/coalescer.go +++ b/rhel/coalescer.go @@ -2,11 +2,11 @@ package rhel import ( "context" + "log/slog" "net/url" "github.com/quay/claircore" "github.com/quay/claircore/indexer" - "github.com/quay/zlog" ) // Coalescer takes individual layer artifacts and coalesces them into a full @@ -128,10 +128,7 @@ func (*Coalescer) Coalesce(ctx context.Context, artifacts []*indexer.LayerArtifa for _, repo := range layerArtifacts.Repos { uri, err := url.ParseQuery(repo.URI) if err != nil { - zlog.Warn(ctx). - Err(err). - Str("repository", repo.URI). - Msg("unable to parse repository URI") + slog.WarnContext(ctx, "unable to parse repository URI", "repository", repo.URI) continue } repoRepoid := uri.Get("repoid") diff --git a/rhel/coalescer_test.go b/rhel/coalescer_test.go index 9aec1eb61..395f6b806 100644 --- a/rhel/coalescer_test.go +++ b/rhel/coalescer_test.go @@ -9,7 +9,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/quay/claircore/toolkit/types/cpe" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -23,7 +22,7 @@ type CoalescerTestcase struct { func (tc CoalescerTestcase) Run(ctx context.Context, t *testing.T) { t.Run(tc.Name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) input, want := tc.Fixture(t) got, err := new(Coalescer).Coalesce(ctx, input) if err != nil { @@ -38,7 +37,7 @@ func (tc CoalescerTestcase) Run(ctx context.Context, t *testing.T) { func TestCoalescer(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) tcs := []CoalescerTestcase{ { diff --git a/rhel/distributionscanner.go b/rhel/distributionscanner.go index c68516a66..588783d75 100644 --- a/rhel/distributionscanner.go +++ b/rhel/distributionscanner.go @@ -5,12 +5,11 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "regexp" "runtime/trace" "strconv" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) @@ -40,12 +39,8 @@ func (*DistributionScanner) Kind() string { return "distribution" } // Scan implements [indexer.DistributionScanner]. func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() - ctx = zlog.ContextWithValues(ctx, - "component", "rhel/DistributionScanner.Scan", - "version", ds.Version(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := l.FS() if err != nil { return nil, fmt.Errorf("rhel: unable to open layer: %w", err) @@ -55,7 +50,7 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([] return nil, fmt.Errorf("rhel: unexpected error reading files: %w", err) } if d == nil { - zlog.Debug(ctx).Msg("didn't find an os-release or redhat-release file") + slog.DebugContext(ctx, "didn't find an os-release or redhat-release file") return nil, nil } return []*claircore.Distribution{d}, nil diff --git a/rhel/matcher.go b/rhel/matcher.go index d197973dd..e5dfd3687 100644 --- a/rhel/matcher.go +++ b/rhel/matcher.go @@ -2,10 +2,10 @@ package rhel import ( "context" + "log/slog" "strings" version "github.com/knqyf263/go-rpm-version" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" @@ -72,10 +72,7 @@ func (m *Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, // support the claircore.Vulnerability.Repo.CPE field. vuln.Repo.CPE, err = cpe.Unbind(vuln.Repo.Name) if err != nil { - zlog.Warn(ctx). - Str("vulnerability name", vuln.Name). - Err(err). - Msg("unable to unbind repo CPE") + slog.WarnContext(ctx, "unable to unbind repo CPE", "reason", err, "vulnerability name", vuln.Name) return false, nil } if !cpe.Compare(vuln.Repo.CPE, record.Repository.CPE).IsSuperset() && !IsCPESubstringMatch(record.Repository.CPE, vuln.Repo.CPE) { diff --git a/rhel/matcher_test.go b/rhel/matcher_test.go index 8caa589ef..9b701afbe 100644 --- a/rhel/matcher_test.go +++ b/rhel/matcher_test.go @@ -1,12 +1,10 @@ package rhel import ( - "context" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" + "github.com/quay/claircore/test" "github.com/quay/claircore/toolkit/types/cpe" ) @@ -155,8 +153,7 @@ func TestVulnerable(t *testing.T) { } m := &Matcher{} - ctx := context.Background() - ctx = zlog.Test(ctx, t) + ctx := test.Logging(t) for _, tc := range testCases { got, err := m.Vulnerable(ctx, tc.ir, tc.v) if err != nil { diff --git a/rhel/matcherfactory.go b/rhel/matcherfactory.go index 135eded2c..d16a9d390 100644 --- a/rhel/matcherfactory.go +++ b/rhel/matcherfactory.go @@ -2,10 +2,9 @@ package rhel import ( "context" + "log/slog" "net/http" - "github.com/quay/zlog" - "github.com/quay/claircore/libvuln/driver" ) @@ -18,7 +17,7 @@ type MatcherFactory struct { ignoreUnpatched bool } -// MatcherFactory implements [driver.MatcherFactory] +// Matcher implements [driver.MatcherFactory] func (f *MatcherFactory) Matcher(ctx context.Context) ([]driver.Matcher, error) { m := &Matcher{ ignoreUnpatched: f.ignoreUnpatched, @@ -30,16 +29,13 @@ type MatcherFactoryConfig struct { IgnoreUnpatched bool `json:"ignore_unpatched" yaml:"ignore_unpatched"` } -// MatcherFactory implements driver.MatcherConfigurable. +// Configure implements [driver.MatcherConfigurable]. func (f *MatcherFactory) Configure(ctx context.Context, cfg driver.MatcherConfigUnmarshaler, _ *http.Client) error { var fc MatcherFactoryConfig if err := cfg(&fc); err != nil { return err } f.ignoreUnpatched = fc.IgnoreUnpatched - zlog.Info(ctx). - Str("component", "rhel/MatcherFactory.Configure"). - Bool("ignore_unpatched", f.ignoreUnpatched). - Msg("configured") + slog.InfoContext(ctx, "configured", "ignore_unpatched", f.ignoreUnpatched) return nil } diff --git a/rhel/packagescanner.go b/rhel/packagescanner.go index 8ccf02a94..5285803cf 100644 --- a/rhel/packagescanner.go +++ b/rhel/packagescanner.go @@ -5,14 +5,14 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "runtime/trace" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/internal/dnf" "github.com/quay/claircore/internal/rpm" + "github.com/quay/claircore/toolkit/log" ) var _ indexer.PackageScanner = PackageScanner{} @@ -40,12 +40,8 @@ func (p PackageScanner) Scan(ctx context.Context, layer *claircore.Layer) (out [ } defer trace.StartRegion(ctx, "PackageScanner.Scan").End() trace.Log(ctx, "layer", layer.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "rhel/PackageScanner.Scan", - "version", p.Version(), - "layer", layer.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := layer.FS() if err != nil { @@ -65,8 +61,8 @@ func (p PackageScanner) Scan(ctx context.Context, layer *claircore.Layer) (out [ }() for found := range found { err = func() error { - ctx := zlog.ContextWithValues(ctx, "db", found.String()) - zlog.Debug(ctx).Msg("examining database") + ctx := log.With(ctx, "db", found) + slog.DebugContext(ctx, "examining database") db, err := rpm.OpenDB(ctx, sys, found) switch { case err == nil: diff --git a/rhel/packagescanner_test.go b/rhel/packagescanner_test.go index ea8506476..99141ac02 100644 --- a/rhel/packagescanner_test.go +++ b/rhel/packagescanner_test.go @@ -1,7 +1,6 @@ package rhel import ( - "context" "errors" "io/fs" "net/url" @@ -10,7 +9,6 @@ import ( "testing" "github.com/quay/claircore" - "github.com/quay/zlog" "github.com/quay/claircore/test" "github.com/quay/claircore/test/rpmtest" @@ -23,7 +21,7 @@ import ( func TestPackageDetection(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) ms, err := filepath.Glob("testdata/package/*.txtar") if err != nil { panic("programmer error") // static glob @@ -57,7 +55,6 @@ func TestPackageDetection(t *testing.T) { // get repoid information. func TestPackageScannerDNFWrapLogic(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) tests := []struct { name string @@ -88,7 +85,7 @@ func TestPackageScannerDNFWrapLogic(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) // Create layer from tar file f, err := os.Open(tt.layerPath) @@ -145,7 +142,6 @@ func TestPackageScannerDNFWrapLogic(t *testing.T) { if !tt.expectRepoidHint && packagesWithRepoid > 0 { t.Errorf("Expected no packages to have repoid hints, but found %d", packagesWithRepoid) - } }) } @@ -155,7 +151,7 @@ func TestPackageScannerDNFWrapLogic(t *testing.T) { // are surfaced to the caller instead of being swallowed. func TestPackageScannerPropagatesFindDBErrors(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) // Point the layer at a non-existent directory to force WalkDir to fail immediately. missingRoot := filepath.Join(t.TempDir(), "missing-root") diff --git a/rhel/repositoryscanner.go b/rhel/repositoryscanner.go index a55bbefd2..b4e463c8d 100644 --- a/rhel/repositoryscanner.go +++ b/rhel/repositoryscanner.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "net/http" "net/url" "os" @@ -16,8 +17,6 @@ import ( "strings" "time" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/internal/dnf" @@ -108,9 +107,6 @@ func (*RepositoryScanner) Kind() string { return "repository" } // Configure implements [indexer.RPCScanner]. func (r *RepositoryScanner) Configure(ctx context.Context, f indexer.ConfigDeserializer, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, - "component", "rhel/RepositoryScanner.Configure", - "version", r.Version()) r.client = c if err := f(&r.cfg); err != nil { return err @@ -157,12 +153,8 @@ func (r *RepositoryScanner) Configure(ctx context.Context, f indexer.ConfigDeser // stored in the [claircore.Repository]'s "Name" and "CPE" fields, respectively. func (r *RepositoryScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Repository, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() - ctx = zlog.ContextWithValues(ctx, - "component", "rhel/RepositoryScanner.Scan", - "version", r.Version(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := l.FS() if err != nil { @@ -220,11 +212,10 @@ func (r *RepositoryScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*cl for repoid, cpeID := range pairs { c, err := cpe.Unbind(cpeID) if err != nil { - zlog.Warn(ctx). - Err(err). - Str("url", bugURL(cpeID, err)). - Str("cpeID", cpeID). - Msg("invalid CPE, please report a bug upstream") + slog.WarnContext(ctx, "invalid CPE, please report a bug upstream", + "reason", err, + "cpeID", cpeID, + "url", bugURL(cpeID, err)) continue } @@ -257,7 +248,7 @@ func (r *RepositoryScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*cl } // BugURL constructs a link directly to the Red Hat Jira instance. -func bugURL(id string, err error) string { +func bugURL(id string, err error) *url.URL { const desc = "A Clair instance noticed an invalid CPE:{code}%s{code}\nThe reported error was:{code}%v{code}" v := url.Values{ "pid": {"12330022"}, // ID for the Red Hat Jira "SECDATA" project. @@ -271,7 +262,7 @@ func bugURL(id string, err error) string { Path: "/secure/CreateIssueDetails!init.jspa", RawQuery: v.Encode(), } - return u.String() + return &u } // RepoidsFromContentSets returns a slice of repoids, as discovered by examining @@ -304,9 +295,7 @@ func (m *mappingFile) GetOne(ctx context.Context, repoid string) (cpes []string, if repo, ok := m.Data[repoid]; ok { return repo.CPEs, true } - zlog.Debug(ctx). - Str("repository", repoid). - Msg("repository not present in a mapping file") + slog.DebugContext(ctx, "repository not present in a mapping file", "repository", repoid) return nil, false } diff --git a/rhel/repositoryscanner_test.go b/rhel/repositoryscanner_test.go index 9692172c8..ce390145a 100644 --- a/rhel/repositoryscanner_test.go +++ b/rhel/repositoryscanner_test.go @@ -2,7 +2,6 @@ package rhel import ( "bytes" - "context" "encoding/json" "errors" "io" @@ -16,7 +15,6 @@ import ( "github.com/quay/claircore/pkg/tmp" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/test" @@ -25,7 +23,6 @@ import ( func TestRepositoryScanner(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) repoData := ` { @@ -320,7 +317,7 @@ func TestRepositoryScanner(t *testing.T) { for _, tt := range table { t.Run(tt.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) f, err := os.Open(tt.layerPath) if err != nil { t.Fatal(err) @@ -388,7 +385,7 @@ func TestBugURL(t *testing.T) { if err == nil { t.Error("expected error") } - got := bugURL(in, err) + got := bugURL(in, err).String() t.Logf("\ngot: %s\nwant: %s", got, want) if got != want { diff --git a/rhel/rhel.go b/rhel/rhel.go index 7ae0a0cfd..2f442e616 100644 --- a/rhel/rhel.go +++ b/rhel/rhel.go @@ -13,8 +13,7 @@ import ( "errors" "fmt" "io/fs" - - "github.com/quay/zlog" + "log/slog" ) // GetContentManifest reads and parses the content manifest file from the layer. @@ -35,9 +34,7 @@ func getContentManifest(ctx context.Context, sys fs.FS) (*contentManifest, error return nil, nil } p := ms[0] - zlog.Debug(ctx). - Str("manifest-path", p). - Msg("found content manifest file") + slog.DebugContext(ctx, "found content manifest file", "manifest-path", p) b, err := fs.ReadFile(sys, p) if err != nil { return nil, fmt.Errorf("rhel: unable to read %q: %w", p, err) @@ -48,10 +45,9 @@ func getContentManifest(ctx context.Context, sys fs.FS) (*contentManifest, error switch { case errors.Is(err, nil): case errors.As(err, &syntaxErr): - zlog.Warn(ctx). - Str("manifest-path", p). - Err(err). - Msg("could not unmarshal content_manifests file") + slog.WarnContext(ctx, "could not unmarshal content_manifests file", + "manifest-path", p, + "reason", err) return nil, nil default: return nil, err diff --git a/rhel/rhel_test.go b/rhel/rhel_test.go index f61c55127..b76b92811 100644 --- a/rhel/rhel_test.go +++ b/rhel/rhel_test.go @@ -1,7 +1,6 @@ package rhel import ( - "context" "os" "strings" "testing" @@ -9,12 +8,10 @@ import ( "github.com/google/go-cmp/cmp" "github.com/quay/claircore" "github.com/quay/claircore/test" - "github.com/quay/zlog" ) func TestGetContentManifest(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) tests := []struct { name string @@ -53,7 +50,7 @@ func TestGetContentManifest(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) // Create a layer from the test data f, err := os.Open(tt.tarPath) From b0c3bfbe0f30dc678223e7ece99f206748a4ed48 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 2 Oct 2025 10:30:59 -0500 Subject: [PATCH 31/77] test/rpmtest: migrate to slog Signed-off-by: Hank Donnay --- test/rpmtest/archive.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/rpmtest/archive.go b/test/rpmtest/archive.go index 85b442e9f..03ed6ced8 100644 --- a/test/rpmtest/archive.go +++ b/test/rpmtest/archive.go @@ -16,7 +16,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "golang.org/x/tools/txtar" "github.com/quay/claircore" @@ -209,7 +208,7 @@ func (a *Archive) Tests( arches[img.Archtecture] = struct{}{} t.Run(img.Archtecture, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) m, err := a.Manifest(img.ID) if err != nil { t.Fatal(err) From e5aee1506837cb6c3f923c5493594c4a9a2b0b4c Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Fri, 3 Oct 2025 15:34:35 -0500 Subject: [PATCH 32/77] java/jar: migrate to slog Signed-off-by: Hank Donnay --- java/jar/jar.go | 75 ++++++++++++------------------------- java/jar/jar_test.go | 18 +++------ java/jar/manifest_test.go | 7 ++-- java/jar/name_test.go | 5 +-- java/jar/properties_test.go | 5 +-- 5 files changed, 37 insertions(+), 73 deletions(-) diff --git a/java/jar/jar.go b/java/jar/jar.go index cadc50596..0eba71234 100644 --- a/java/jar/jar.go +++ b/java/jar/jar.go @@ -36,13 +36,14 @@ import ( "fmt" "io" "io/fs" + "log/slog" "net/textproto" "path" "path/filepath" "regexp" "strings" - "github.com/quay/zlog" + "github.com/quay/claircore/toolkit/log" ) // MinSize is the absolute minimum size for a jar. @@ -60,10 +61,7 @@ const MinSize = 22 // The provided name is expected to be the full path within the layer to the jar // file being provided as "z". func Parse(ctx context.Context, name string, z *zip.Reader) ([]Info, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "java/jar/Parse", - "jar", name) - return parse(ctx, srcPath{name}, z) + return parse(log.With(ctx, "jar", name), srcPath{name}, z) } // SrcPath is a helper for tracking where an archive member is. @@ -90,9 +88,7 @@ func (p *srcPath) Pop() string { // Parse is the inner function that uses a srcPath to keep track of recursions. func parse(ctx context.Context, name srcPath, z *zip.Reader) ([]Info, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "java/jar/Parse", - "name", name.String()) + ctx = log.With(ctx, "name", name.String()) // This uses an admittedly non-idiomatic, C-like goto construction. We want // to attempt a few heuristics and keep the results of the first one that @@ -108,8 +104,7 @@ func parse(ctx context.Context, name srcPath, z *zip.Reader) ([]Info, error) { ret, err = extractProperties(ctx, name, z) switch { case errors.Is(err, nil): - zlog.Debug(ctx). - Msg("using discovered properties file(s)") + slog.DebugContext(ctx, "using discovered properties file(s)") goto Finish case errors.Is(err, errUnpopulated): case strings.HasPrefix(base, "javax") && errors.Is(err, ErrNotAJar): @@ -120,8 +115,7 @@ func parse(ctx context.Context, name srcPath, z *zip.Reader) ([]Info, error) { i, err = extractManifest(ctx, name, z) switch { case errors.Is(err, nil): - zlog.Debug(ctx). - Msg("using discovered manifest") + slog.DebugContext(ctx, "using discovered manifest") ret = append(ret, i) goto Finish case errors.Is(err, errUnpopulated) || errors.Is(err, errInsaneManifest): @@ -133,8 +127,7 @@ func parse(ctx context.Context, name srcPath, z *zip.Reader) ([]Info, error) { i, err = checkName(ctx, name.Cur()) switch { case errors.Is(err, nil): - zlog.Debug(ctx). - Msg("using name mangling") + slog.DebugContext(ctx, "using name mangling") ret = append(ret, i) goto Finish case errors.Is(err, errUnpopulated): @@ -154,9 +147,7 @@ Finish: return nil, archiveErr(name, err) } if ct := len(inner); ct != 0 { - zlog.Debug(ctx). - Int("count", ct). - Msg("found embedded jars") + slog.DebugContext(ctx, "found embedded jars", "count", ct) } ret = append(ret, inner...) @@ -210,14 +201,12 @@ func extractProperties(ctx context.Context, name srcPath, z *zip.Reader) ([]Info // encoded in the file names. p := normName(f.Name) if path.Base(p) == filename { - zlog.Debug(ctx). - Str("path", p). - Msg("found properties file") + slog.DebugContext(ctx, "found properties file", "path", p) pf = append(pf, p) } } if len(pf) == 0 { - zlog.Debug(ctx).Msg("properties not found") + slog.DebugContext(ctx, "properties not found") return nil, errUnpopulated } ret := make([]Info, len(pf)) @@ -244,7 +233,7 @@ func extractProperties(ctx context.Context, name srcPath, z *zip.Reader) ([]Info // ExtractInner recurses into anything that looks like a jar in "z". func extractInner(ctx context.Context, p srcPath, z *zip.Reader) ([]Info, error) { - ctx = zlog.ContextWithValues(ctx, "parent", p.String()) + ctx = log.With(ctx, "parent", p.String()) var ret []Info // Zips need random access, so allocate a buffer for any we find. var buf bytes.Buffer @@ -258,7 +247,7 @@ func extractInner(ctx context.Context, p srcPath, z *zip.Reader) ([]Info, error) fi := f.FileInfo() // Check size. if fi.Size() < MinSize { - zlog.Debug(ctx).Str("member", name).Msg("not actually a jar: too small") + slog.DebugContext(ctx, "not actually a jar: too small", "member", name) return nil } rc, err := f.Open() @@ -286,10 +275,7 @@ func extractInner(ctx context.Context, p srcPath, z *zip.Reader) ([]Info, error) // opening malformed zips. fallthrough case errors.Is(err, zip.ErrFormat): - zlog.Debug(ctx). - Str("member", name). - Err(err). - Msg("not actually a jar: invalid zip") + slog.DebugContext(ctx, "not actually a jar: invalid zip", "member", name) return nil default: return mkErr("failed opening inner zip", err) @@ -302,10 +288,7 @@ func extractInner(ctx context.Context, p srcPath, z *zip.Reader) ([]Info, error) case errors.Is(err, nil): case errors.Is(err, ErrNotAJar) || errors.Is(err, errInsaneManifest): - zlog.Debug(ctx). - Str("member", name). - Err(err). - Msg("not actually a jar") + slog.DebugContext(ctx, "not actually a jar", "reason", err, "member", name) return nil default: return mkErr("parse error", err) @@ -326,8 +309,7 @@ func extractInner(ctx context.Context, p srcPath, z *zip.Reader) ([]Info, error) } if len(ret) == 0 { - zlog.Debug(ctx). - Msg("found no bundled jars") + slog.DebugContext(ctx, "found no bundled jars") } return ret, nil } @@ -347,8 +329,7 @@ var nameRegexp = regexp.MustCompile(`([[:graph:]]+)-([[:digit:]][\-.[:alnum:]]*( func checkName(ctx context.Context, name string) (Info, error) { m := nameRegexp.FindStringSubmatch(filepath.Base(name)) if m == nil { - zlog.Debug(ctx). - Msg("name not useful") + slog.DebugContext(ctx, "name not useful") return Info{}, errUnpopulated } return Info{ @@ -414,26 +395,20 @@ func (i *Info) parseManifest(ctx context.Context, r io.Reader) error { tp := textproto.NewReader(bufio.NewReader(newMainSectionReader(r))) hdr, err := tp.ReadMIMEHeader() if err != nil { - zlog.Debug(ctx). - Err(err). - Msg("unable to read manifest") + slog.DebugContext(ctx, "unable to read manifest", "reason", err) return errInsaneManifest } // Sanity checks: switch { case len(hdr) == 0: - zlog.Debug(ctx). - Msg("no headers found") + slog.DebugContext(ctx, "no headers found") return errInsaneManifest case !manifestVer.MatchString(hdr.Get("Manifest-Version")): v := hdr.Get("Manifest-Version") - zlog.Debug(ctx). - Str("manifest_version", v). - Msg("invalid manifest version") + slog.DebugContext(ctx, "invalid manifest version", "manifest_version", v) return errInsaneManifest case hdr.Get("Name") != "": - zlog.Debug(ctx). - Msg("martian manifest") + slog.DebugContext(ctx, "martian manifest") // This shouldn't be happening in the Main section. return errInsaneManifest } @@ -494,9 +469,8 @@ func (i *Info) parseManifest(ctx context.Context, r io.Reader) error { } if name == "" || version == "" { - zlog.Debug(ctx). - Strs("attrs", []string{name, version}). - Msg("manifest not useful") + slog.DebugContext(ctx, "manifest not useful", + slog.Group("attrs", "name", name, "version", version)) return errUnpopulated } i.Name = name @@ -640,9 +614,8 @@ func (i *Info) parseProperties(ctx context.Context, r io.Reader) error { return mkErr("properties scanner", err) } if group == "" || artifact == "" || version == "" { - zlog.Debug(ctx). - Strs("attrs", []string{group, artifact, version}). - Msg("properties not useful") + slog.DebugContext(ctx, "properties not useful", + slog.Group("attrs", "group", group, "artifact", artifact, "version", version)) return errUnpopulated } diff --git a/java/jar/jar_test.go b/java/jar/jar_test.go index 89e5eb8b2..47e43ebd9 100644 --- a/java/jar/jar_test.go +++ b/java/jar/jar_test.go @@ -5,7 +5,6 @@ import ( "archive/zip" "bytes" "compress/gzip" - "context" "crypto/sha256" "encoding/binary" "encoding/hex" @@ -21,7 +20,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/quay/zlog" "github.com/quay/claircore/test" "github.com/quay/claircore/test/integration" @@ -31,7 +29,6 @@ import ( func TestParse(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) const url = `https://repo1.maven.org/maven2/com/orientechnologies/orientdb-community/3.2.37/orientdb-community-3.2.37.tar.gz` const sha = `101d93340ae17cfdc622ef37e30c8c3993874ab199fd5ff3fc76d466740fefe3` name := fetch(t, url, sha) @@ -55,7 +52,7 @@ func TestParse(t *testing.T) { } t.Log("found jar:", h.Name) t.Run(filepath.Base(h.Name), func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) buf.Reset() buf.Grow(int(h.Size)) n, err := io.Copy(&buf, tr) @@ -89,7 +86,7 @@ func TestParse(t *testing.T) { func TestWAR(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) const url = `https://get.jenkins.io/war/2.311/jenkins.war` const sha = `fe21501800c769279699ecf511fd9b495b1cb3ebd226452e01553ff06820910a` name := fetch(t, url, sha) @@ -174,7 +171,6 @@ func fetch(t testing.TB, u string, ck string) (name string) { } func TestJAR(t *testing.T) { - ctx := context.Background() td := os.DirFS("testdata/jar") ls, err := fs.ReadDir(td, ".") if err != nil { @@ -194,7 +190,7 @@ func TestJAR(t *testing.T) { continue } t.Run(n, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) f, err := td.Open(ent.Name()) if err != nil { t.Error(err) @@ -232,7 +228,6 @@ func TestJAR(t *testing.T) { } func TestJARBadManifest(t *testing.T) { - ctx := context.Background() path := "testdata/malformed-manifests" d := os.DirFS(path) ls, err := fs.ReadDir(d, ".") @@ -246,7 +241,7 @@ func TestJARBadManifest(t *testing.T) { for _, n := range ls { t.Log(n) t.Run(n.Name(), func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) f, err := os.Open(filepath.Join(path, n.Name())) if err != nil { t.Fatal(err) @@ -265,7 +260,6 @@ func TestJARBadManifest(t *testing.T) { // them gracefully. func TestMalformed(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) t.Run("BadOffset", func(t *testing.T) { const ( @@ -327,6 +321,7 @@ func TestMalformed(t *testing.T) { if err != nil { t.Fatal(err) } + ctx := test.Logging(t) infos, err := Parse(ctx, jarName, z) t.Logf("returned error: %v", err) switch { @@ -426,7 +421,6 @@ func TestMalformed(t *testing.T) { t.Fail() } }) - } func TestManifestSectionReader(t *testing.T) { @@ -487,7 +481,7 @@ func TestInnerJar(t *testing.T) { } }) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) got, err := Parse(ctx, name, &rc.Reader) if err != nil { t.Fatal(err) diff --git a/java/jar/manifest_test.go b/java/jar/manifest_test.go index 45affe801..c7f1446c9 100644 --- a/java/jar/manifest_test.go +++ b/java/jar/manifest_test.go @@ -2,7 +2,6 @@ package jar import ( "bytes" - "context" "errors" "io" "io/fs" @@ -11,12 +10,12 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" + "github.com/quay/claircore/test" ) func TestParseManifest(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) md := os.DirFS("testdata/manifest") fs, err := fs.ReadDir(md, ".") @@ -49,7 +48,7 @@ func TestParseManifest(t *testing.T) { func TestParseManifest_JenkinsPlugins(t *testing.T) { var i Info - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) for _, tc := range jenkinsPlugins { t.Run(tc.Name, func(t *testing.T) { err := i.parseManifest(ctx, strings.NewReader(tc.Contents)) diff --git a/java/jar/name_test.go b/java/jar/name_test.go index e3ef3f504..33d1fb484 100644 --- a/java/jar/name_test.go +++ b/java/jar/name_test.go @@ -1,18 +1,17 @@ package jar import ( - "context" "errors" "io/fs" "os" "testing" - "github.com/quay/zlog" + "github.com/quay/claircore/test" ) func TestCheckName(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) ms, err := fs.Glob(os.DirFS("testdata"), "*/*.jar") if err != nil { diff --git a/java/jar/properties_test.go b/java/jar/properties_test.go index db0ed88f7..a7b2d19e7 100644 --- a/java/jar/properties_test.go +++ b/java/jar/properties_test.go @@ -2,18 +2,17 @@ package jar import ( "bytes" - "context" "io" "io/fs" "os" "testing" - "github.com/quay/zlog" + "github.com/quay/claircore/test" ) func TestParseProperties(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) md := os.DirFS("testdata/properties") fs, err := fs.ReadDir(md, ".") From 9d036dba23db7d63eee2b08eeb738e2c3fed1ee2 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Fri, 3 Oct 2025 15:48:57 -0500 Subject: [PATCH 33/77] java: migrate to slog Signed-off-by: Hank Donnay --- java/matcher.go | 24 +++++++--------- java/matcher_test.go | 3 +- java/packagescanner.go | 55 +++++++++++-------------------------- java/packagescanner_test.go | 4 +-- 4 files changed, 28 insertions(+), 58 deletions(-) diff --git a/java/matcher.go b/java/matcher.go index fedfdf308..9b96f45c1 100644 --- a/java/matcher.go +++ b/java/matcher.go @@ -2,10 +2,9 @@ package java import ( "context" + "log/slog" "net/url" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/internal/maven" "github.com/quay/claircore/libvuln/driver" @@ -53,10 +52,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v if introduced != "" { iv, err := maven.ParseVersion(introduced) if err != nil { - zlog.Warn(ctx). - Str("package", vuln.Package.Name). - Str("version", introduced). - Msg("unable to parse maven introduced version") + slog.WarnContext(ctx, "unable to parse maven introduced version", + "package", vuln.Package.Name, + "version", introduced) return false, err } // If the package's version is less than the introduced version, it's not vulnerable. @@ -71,10 +69,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v case fixedVersion != "": fv, err := maven.ParseVersion(fixedVersion) if err != nil { - zlog.Warn(ctx). - Str("package", vuln.Package.Name). - Str("version", fixedVersion). - Msg("unable to parse maven fixed version") + slog.WarnContext(ctx, "unable to parse maven fixed version", + "package", vuln.Package.Name, + "version", fixedVersion) return false, err } // The package is affected if its version is less than the fixed version. @@ -82,10 +79,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v case lastAffected != "": la, err := maven.ParseVersion(lastAffected) if err != nil { - zlog.Warn(ctx). - Str("package", vuln.Package.Name). - Str("version", lastAffected). - Msg("unable to parse maven last_affected version") + slog.WarnContext(ctx, "unable to parse maven last_affected version", + "package", vuln.Package.Name, + "version", lastAffected) return false, err } // The package is affected if its version is less than or equal to the last affected version. diff --git a/java/matcher_test.go b/java/matcher_test.go index 026138c8a..05859fb2f 100644 --- a/java/matcher_test.go +++ b/java/matcher_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/test" @@ -204,5 +203,5 @@ func TestVulnerable(t *testing.T) { } func TestMatcher(t *testing.T) { - test.RunMatcherTests(zlog.Test(context.Background(), t), t, "testdata/matcher", new(Matcher)) + test.RunMatcherTests(test.Logging(t), t, "testdata/matcher", new(Matcher)) } diff --git a/java/packagescanner.go b/java/packagescanner.go index 457cc1cbd..eeba37458 100644 --- a/java/packagescanner.go +++ b/java/packagescanner.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "net/url" "runtime/trace" @@ -19,8 +20,6 @@ import ( "strings" "time" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/java/jar" @@ -82,9 +81,6 @@ func (*Scanner) Kind() string { return "package" } // Configure implements indexer.RPCScanner. func (s *Scanner) Configure(ctx context.Context, f indexer.ConfigDeserializer, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, - "component", "java/Scanner.Configure", - "version", s.Version()) var cfg ScannerConfig s.client = c if err := f(&cfg); err != nil { @@ -92,7 +88,7 @@ func (s *Scanner) Configure(ctx context.Context, f indexer.ConfigDeserializer, c } if cfg.DisableAPI { - zlog.Debug(ctx).Msg("search API disabled") + slog.DebugContext(ctx, "search API disabled") } else { api := DefaultSearchAPI if cfg.API != "" { @@ -103,10 +99,9 @@ func (s *Scanner) Configure(ctx context.Context, f indexer.ConfigDeserializer, c requestTimeout = cfg.APIRequestTimeout } s.rootRequestTimeout = requestTimeout - zlog.Debug(ctx). - Str("api", api). - Float64("requestTimeout", requestTimeout.Seconds()). - Msg("configured search API URL") + slog.DebugContext(ctx, "configured search API URL", + "api", api, + "requestTimeout", requestTimeout) u, err := url.Parse(api) if err != nil { return err @@ -124,12 +119,8 @@ func (s *Scanner) Configure(ctx context.Context, f indexer.ConfigDeserializer, c func (s *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircore.Package, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() trace.Log(ctx, "layer", layer.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "java/Scanner.Scan", - "version", s.Version(), - "layer", layer.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") if err := ctx.Err(); err != nil { return nil, err } @@ -154,11 +145,9 @@ func (s *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircor return nil, fmt.Errorf("java: unable to check RPM db: %w", err) } for _, n := range ars { - ctx := zlog.ContextWithValues(ctx, "file", n) + log := slog.With("path", n) if set.Contains(n) { - zlog.Debug(ctx). - Str("path", n). - Msg("file path determined to be of RPM origin") + log.DebugContext(ctx, "file path determined to be of RPM origin") continue } @@ -192,9 +181,7 @@ func (s *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircor // opening malformed zips. fallthrough case errors.Is(err, zip.ErrFormat): - zlog.Info(ctx). - Err(err). - Msg("not actually a jar: invalid zip") + log.InfoContext(ctx, "not actually a jar: invalid zip", "reason", err) continue default: return nil, err @@ -205,9 +192,7 @@ func (s *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircor case err == nil: case errors.Is(err, jar.ErrNotAJar): // Could not prove this is really a jar. Skip it and move on. - zlog.Info(ctx). - AnErr("reason", err). - Msg("skipping jar") + log.InfoContext(ctx, "skipping jar", "reason", err) continue default: return nil, err @@ -301,9 +286,7 @@ func (s *Scanner) search(ctx context.Context, i *jar.Info, ck []byte) error { defer done() req, err := http.NewRequestWithContext(tctx, http.MethodGet, s.root.String(), nil) if err != nil { - zlog.Warn(ctx). - Err(err). - Msg("unable to construct request") + slog.WarnContext(ctx, "unable to construct request", "reason", err) return errRPC } v := req.URL.Query() @@ -314,30 +297,24 @@ func (s *Scanner) search(ctx context.Context, i *jar.Info, ck []byte) error { req.URL.RawQuery = v.Encode() res, err := s.client.Do(req) if err != nil { - zlog.Warn(ctx). - Err(err). - Msg("error making request") + slog.WarnContext(ctx, "error making request", "reason", err) return errRPC } if res.StatusCode != http.StatusOK { res.Body.Close() - zlog.Warn(ctx). - Str("status", res.Status). - Msg("unexpected response status") + slog.WarnContext(ctx, "unexpected response status", "status", res.Status) return errRPC } var sr searchResponse err = json.NewDecoder(res.Body).Decode(&sr) res.Body.Close() if err != nil { - zlog.Warn(ctx). - Err(err). - Msg("error decoding json") + slog.WarnContext(ctx, "error decoding json", "reason", err) return errRPC } success = true if len(sr.Response.Doc) == 0 { - zlog.Debug(ctx).Msg("no matching artifacts found") + slog.DebugContext(ctx, "no matching artifacts found") return nil } // Sort and then take the first one, because apparently the same diff --git a/java/packagescanner_test.go b/java/packagescanner_test.go index 4456a9790..04067c315 100644 --- a/java/packagescanner_test.go +++ b/java/packagescanner_test.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/json" - "github.com/quay/zlog" "net/http" "net/http/httptest" "path" @@ -22,8 +21,7 @@ func TestDisableAPI(t *testing.T) { t.Fatal("external http request invoked when none was expected") })) - ctx := context.Background() - ctx = zlog.Test(ctx, t) + ctx := test.Logging(t) scanner := &java.Scanner{} var buf bytes.Buffer cfg := &java.ScannerConfig{ From 963162bb796f3068be0e82f81ad0fba6323e90f2 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 12:58:05 -0500 Subject: [PATCH 34/77] docs: migrate to slog Signed-off-by: Hank Donnay --- docs/contributor/logging.md | 41 +++++++++++++++++++++---------------- docs/logger_test.go | 27 ++++++++++++++++-------- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/docs/contributor/logging.md b/docs/contributor/logging.md index d43495175..91c21e772 100644 --- a/docs/contributor/logging.md +++ b/docs/contributor/logging.md @@ -1,7 +1,5 @@ # Logging -All the logging in claircore is done with [zerolog][doc] via `context.Context` -values. The `zlog` package takes OpenTelemetry labels and attaches them to -`zerolog` events. +All the logging in claircore is done with [`log/slog`][doc]. This allows for claircore's logging to be used consistently throughout all the packages without having unintended prints to stderr. @@ -9,20 +7,28 @@ packages without having unintended prints to stderr. ## How to Log ### Adding Context -In a function, use `zlog` to add key-value pairs of any relevant context: + +Within a function, create a `*slog.Logger` to add key-value pairs of any +relevant context: ```go {{#include ../logger_test.go:kvs}} ``` -Alternatively, the `go.opentelemetry.io/otel/baggage` package can be used for -more explicit control around the baggage values. +Alternatively, the `github.com/quay/claircore/toolkit/log` package has a helper +for associating multiple `slog.Attr` values with a `context.Context`. +```go +{{#include ../logger_test.go:ctx}} +``` + +Programs using claircore should configure their `slog.Handler` to extract the +values using the key in `github.com/quay/claircore/toolkit/log` or use the +`Wrap` helper. ### Logging style #### Constant Messages -Zerolog emits lines when the `Msg` or `Msgf` methods are called. Project style -is to _not_ use `Msgf`. Any variable data should be set as key-value pairs on -the Event object. +Project style is to use string constants for all logging calls. Any variable +data should be passed as additional arguments to the `slog` methods. For example, don't do this: ```go @@ -66,14 +72,6 @@ times in the logs. Claircore attempts to have consistent leveled logging. The rules for figuring out what level to use is: -* Panic - - There's some occurrence that means the process won't work correctly. - -* Fatal - - Unused, because it prevents defers from running. - * Error Something unexpected occurred and the process can continue, but a @@ -96,4 +94,11 @@ out what level to use is: and exiting functions, stepping through a function, or specific file paths used while work is being done. -[doc]: https://pkg.go.dev/github.com/rs/zerolog@v1.26.0 +In exceptional situations, levels of `slog.LevelDebug-4` or `slog.LevelError+4` +may be used for "Trace" and "Emergency" logs. There are no predefined constants +for these. + +#### Contexts +All logging calls should use the "Context" variant whenever possible. + +[doc]: https://pkg.go.dev/log/slog diff --git a/docs/logger_test.go b/docs/logger_test.go index 47abe5f01..5dc34c3e3 100644 --- a/docs/logger_test.go +++ b/docs/logger_test.go @@ -2,26 +2,35 @@ package docs_test import ( "context" + "fmt" + "log/slog" "time" - "github.com/quay/zlog" + "github.com/quay/claircore/toolkit/log" ) // Example_logger is an example annotated for inclusion in the prose // documentation. func Example_logger() { ctx := context.Background() - // ANCHOR: kvs - ctx = zlog.ContextWithValues(ctx, - "component", "Example.Logger") - // ANCHOR_END: kvs + { + // ANCHOR: kvs + log := slog.With("contextual", "value") + log.DebugContext(ctx, "message") + // ANCHOR_END: kvs + } + + { + // ANCHOR: ctx + ctx := log.With(ctx, "contextual", "value") + slog.DebugContext(ctx, "message") + // ANCHOR_END: ctx + } // ANCHOR: bad_example - zlog.Info(ctx).Msgf("done at: %v", time.Now()) + slog.InfoContext(ctx, fmt.Sprintf("done at: %v", time.Now())) // ANCHOR_END: bad_example // ANCHOR: good_example - zlog.Info(ctx). - Time("time", time.Now()). - Msgf("done") + slog.InfoContext(ctx, "done", "time", time.Now()) // ANCHOR_END: good_example } From 679412cdda690e998d2270a56aa950ff8b566996 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 13:18:56 -0500 Subject: [PATCH 35/77] dpkg: migrate to slog Signed-off-by: Hank Donnay --- dpkg/distroless_scanner.go | 25 ++++++++-------------- dpkg/distroless_scanner_test.go | 4 +--- dpkg/scanner.go | 38 +++++++++++---------------------- dpkg/scanner_test.go | 10 ++++----- 4 files changed, 27 insertions(+), 50 deletions(-) diff --git a/dpkg/distroless_scanner.go b/dpkg/distroless_scanner.go index 3abfa4063..261692781 100644 --- a/dpkg/distroless_scanner.go +++ b/dpkg/distroless_scanner.go @@ -7,12 +7,11 @@ import ( "fmt" "io" "io/fs" + "log/slog" "net/textproto" "path/filepath" "runtime/trace" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) @@ -55,12 +54,8 @@ func (ps *DistrolessScanner) Kind() string { return distrolessKind } func (ps *DistrolessScanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircore.Package, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() trace.Log(ctx, "layer", layer.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "dpkg/DistrolessScanner.Scan", - "version", ps.Version(), - "layer", layer.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := layer.FS() if err != nil { @@ -73,7 +68,7 @@ func (ps *DistrolessScanner) Scan(ctx context.Context, layer *claircore.Layer) ( return err } if d.Name() == "status.d" && d.IsDir() { - zlog.Debug(ctx).Str("path", p).Msg("found potential distroless dpkg db directory") + slog.DebugContext(ctx, "found potential distroless dpkg db directory", "path", p) dbFiles, err := fs.ReadDir(sys, p) if err != nil { return fmt.Errorf("error reading DB directory: %w", err) @@ -81,8 +76,8 @@ func (ps *DistrolessScanner) Scan(ctx context.Context, layer *claircore.Layer) ( for _, f := range dbFiles { pkgCt := 0 fn := filepath.Join(p, f.Name()) - ctx = zlog.ContextWithValues(ctx, "database-file", fn) - zlog.Debug(ctx).Msg("examining package database") + log := slog.With("database-file", fn) + log.DebugContext(ctx, "examining package database") db, err := sys.Open(fn) if err != nil { return fmt.Errorf("reading database files from layer failed: %w", err) @@ -124,14 +119,12 @@ func (ps *DistrolessScanner) Scan(ctx context.Context, layer *claircore.Layer) ( case errors.Is(err, io.EOF): default: if _, ok := err.(textproto.ProtocolError); ok { - zlog.Warn(ctx).Err(err).Msg("unable to read DB entry") + log.WarnContext(ctx, "unable to read DB entry", "reason", err) goto Restart } - zlog.Warn(ctx).Err(err).Msg("error reading DB file") + log.WarnContext(ctx, "error reading DB file", "reason", err) } - zlog.Debug(ctx). - Int("count", pkgCt). - Msg("found packages") + slog.DebugContext(ctx, "found packages", "count", pkgCt) } } return nil diff --git a/dpkg/distroless_scanner_test.go b/dpkg/distroless_scanner_test.go index 37451c06c..c6ad7e00e 100644 --- a/dpkg/distroless_scanner_test.go +++ b/dpkg/distroless_scanner_test.go @@ -1,18 +1,16 @@ package dpkg import ( - "context" "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/test" ) func TestDistrolessLayer(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) want := []*claircore.Package{ { Name: "base-files", diff --git a/dpkg/scanner.go b/dpkg/scanner.go index 505e2057b..99f5b5020 100644 --- a/dpkg/scanner.go +++ b/dpkg/scanner.go @@ -10,14 +10,13 @@ import ( "fmt" "io" "io/fs" + "log/slog" "net/textproto" "path/filepath" "runtime/trace" "slices" "strings" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) @@ -60,12 +59,8 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco // Preamble defer trace.StartRegion(ctx, "Scanner.Scan").End() trace.Log(ctx, "layer", layer.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "dpkg/Scanner.Scan", - "version", ps.Version(), - "layer", layer.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := layer.FS() if err != nil { @@ -91,7 +86,7 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco if err := fs.WalkDir(sys, ".", walk); err != nil { return nil, err } - zlog.Debug(ctx).Msg("scanned for possible databases") + slog.DebugContext(ctx, "scanned for possible databases") // If we didn't find anything, this loop is completely skipped. var pkgs []*claircore.Package @@ -109,7 +104,7 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco switch { case err == nil: case errors.Is(err, errNotDpkgDB): - zlog.Info(ctx).AnErr("reason", err).Msg("skipping possible database") + slog.InfoContext(ctx, "skipping possible database", "reason", err) default: return nil, err } @@ -158,7 +153,7 @@ func sortpkg(a, b *claircore.Package) int { // // "Found"is used for scratch space and results are appended to the slice pointed to by "out". func loadDatabase(ctx context.Context, sys fs.FS, dir string, found *packages, out *[]*claircore.Package) error { - zlog.Debug(ctx).Msg("examining package database") + slog.DebugContext(ctx, "examining package database") // We want the "status" file. fn := filepath.Join(dir, "status") @@ -166,9 +161,7 @@ func loadDatabase(ctx context.Context, sys fs.FS, dir string, found *packages, o switch { case errors.Is(err, nil): case errors.Is(err, fs.ErrNotExist): - zlog.Debug(ctx). - Str("filename", fn). - Msg("false positive") + slog.DebugContext(ctx, "false positive", "filename", fn) return err default: return fmt.Errorf("reading status file from layer failed: %w", err) @@ -195,9 +188,7 @@ func loadDatabase(ctx context.Context, sys fs.FS, dir string, found *packages, o } p, ok := found.bin[k] if !ok { - zlog.Debug(ctx). - Str("package", k). - Msg("extra metadata found, ignoring") + slog.DebugContext(ctx, "extra metadata found, ignoring", "package", k) continue } f, err := sys.Open(n) @@ -208,17 +199,14 @@ func loadDatabase(ctx context.Context, sys fs.FS, dir string, found *packages, o _, err = io.Copy(hash, f) f.Close() if err != nil { - zlog.Warn(ctx). - Err(err). - Str("package", n). - Msg("unable to read package metadata") + slog.WarnContext(ctx, "unable to read package metadata", + "package", n, + "reason", err) continue } p.RepositoryHint = hex.EncodeToString(hash.Sum(nil)) } - zlog.Debug(ctx). - Int("count", len(found.bin)). - Msg("found packages") + slog.DebugContext(ctx, "found packages", "count", len(found.bin)) for _, pkg := range found.bin { *out = append(*out, pkg) @@ -322,7 +310,7 @@ Restart: switch { case errors.Is(err, io.EOF): default: - zlog.Warn(ctx).Err(err).Msg("unable to read entry") + slog.WarnContext(ctx, "unable to read entry", "reason", err) goto Restart } return nil diff --git a/dpkg/scanner_test.go b/dpkg/scanner_test.go index 7af0768b3..ec55e8418 100644 --- a/dpkg/scanner_test.go +++ b/dpkg/scanner_test.go @@ -3,7 +3,6 @@ package dpkg import ( "archive/tar" "bufio" - "context" "errors" "io" "net/textproto" @@ -12,7 +11,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/test" @@ -825,7 +823,7 @@ func TestScanner(t *testing.T) { }, } slices.SortFunc(want, sortpkg) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) l := test.RealizeLayer(ctx, t, test.LayerRef{ Registry: "docker.io", Name: "library/ubuntu", @@ -846,7 +844,7 @@ func TestExtraMetadata(t *testing.T) { t.Parallel() mod := test.Modtime(t, "scanner_test.go") layerfile := test.GenerateFixture(t, `extrametadata.layer`, mod, extraMetadataSetup) - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) var l claircore.Layer var s Scanner @@ -964,7 +962,7 @@ func TestKeyringPackage(t *testing.T) { func TestParsedSource(t *testing.T) { t.Parallel() const filename = `testdata/postgresql.status` - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) db, err := os.Open(filename) if err != nil { @@ -998,7 +996,7 @@ func TestParsedSource(t *testing.T) { func TestNotDB(t *testing.T) { t.Parallel() const filename = `testdata/vcpkg.status` - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) db, err := os.Open(filename) if err != nil { From 2713795ba31c2cada1bae7f4c588bb4ce09f6f0f Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 13:48:56 -0500 Subject: [PATCH 36/77] gobin: migrate to slog Signed-off-by: Hank Donnay --- gobin/exe.go | 38 +++++++++++++++++--------------------- gobin/exe_test.go | 5 ++--- gobin/gobin.go | 25 ++++++++----------------- gobin/gobin_test.go | 7 ++----- 4 files changed, 29 insertions(+), 46 deletions(-) diff --git a/gobin/exe.go b/gobin/exe.go index 2b522846f..c746712b6 100644 --- a/gobin/exe.go +++ b/gobin/exe.go @@ -6,13 +6,12 @@ import ( "errors" "fmt" "io" + "log/slog" "regexp" "strconv" "strings" _ "unsafe" // for error linkname tricks - "github.com/quay/zlog" - "github.com/quay/claircore" ) @@ -31,21 +30,17 @@ func toPackages(ctx context.Context, out *[]*claircore.Package, p string, r io.R case errors.Is(err, errNotGoExe): return nil default: - zlog.Debug(ctx). - Err(err). - Msg("unable to open executable") + slog.DebugContext(ctx, "unable to open executable", "reason", err) return nil } - ctx = zlog.ContextWithValues(ctx, "exe", p) + log := slog.With("exe", p) pkgdb := "go:" + p badVers := make(map[string]string) defer func() { if len(badVers) == 0 { return } - zlog.Warn(ctx). - Interface("module_versions", badVers). - Msg("invalid semantic versions found in binary") + log.WarnContext(ctx, "invalid semantic versions found in binary", "module_versions", badVers) }() // TODO(hank) This package could use canonical versions, but the @@ -80,9 +75,10 @@ func toPackages(ctx context.Context, out *[]*claircore.Package, p string, r io.R NormalizedVersion: runtimeVer, }) - ev := zlog.Debug(ctx) - vs := map[string]string{ - "stdlib": bi.GoVersion, + debug := log.Enabled(ctx, slog.LevelDebug) + var attrs []slog.Attr + if debug { + attrs = append(attrs, slog.String("stdlib", bi.GoVersion)) } // The go main module version is reported differently depending on the go @@ -147,8 +143,8 @@ func toPackages(ctx context.Context, out *[]*claircore.Package, p string, r io.R NormalizedVersion: mainVer, }) - if ev.Enabled() { - vs[bi.Main.Path] = bi.Main.Version + if debug && bi.Main.Path != "" { + attrs = append(attrs, slog.String(bi.Main.Path, bi.Main.Version)) } for _, d := range bi.Deps { // Replacements are only evaluated for the main module and seem to only @@ -174,18 +170,18 @@ func toPackages(ctx context.Context, out *[]*claircore.Package, p string, r io.R NormalizedVersion: nv, }) - if ev.Enabled() { - vs[d.Path] = d.Version + if debug { + attrs = append(attrs, slog.String(d.Path, d.Version)) } } - ev. - Interface("versions", vs). - Msg("analyzed exe") + log.DebugContext(ctx, "analyzed exe", "versions", slog.GroupValue(attrs...)) return nil } -var versionRegex = regexp.MustCompile(`^v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?$`) -var ErrInvalidSemVer = errors.New("invalid semantic version") +var ( + versionRegex = regexp.MustCompile(`^v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?$`) + ErrInvalidSemVer = errors.New("invalid semantic version") +) // ParseVersion will return a claircore.Version of type semver given // a valid semantic version. If the string is not a valid semver it diff --git a/gobin/exe_test.go b/gobin/exe_test.go index 6ca7e7d1b..75a53956e 100644 --- a/gobin/exe_test.go +++ b/gobin/exe_test.go @@ -1,16 +1,15 @@ package gobin import ( - "context" "os" "path/filepath" "testing" "github.com/Masterminds/semver" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" + "github.com/quay/claircore/test" ) func TestBin(t *testing.T) { @@ -21,7 +20,7 @@ func TestBin(t *testing.T) { for _, n := range ms { name := filepath.Base(n) t.Run(name, func(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) f, err := os.Open(n) if err != nil { t.Fatal(err) diff --git a/gobin/gobin.go b/gobin/gobin.go index 91aad4456..763a35fe4 100644 --- a/gobin/gobin.go +++ b/gobin/gobin.go @@ -18,12 +18,11 @@ import ( "fmt" "io" "io/fs" + "log/slog" "os" "runtime/trace" "sync" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/rpm" @@ -65,12 +64,8 @@ func (Detector) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Pack } defer trace.StartRegion(ctx, "Scanner.Scan").End() trace.Log(ctx, "layer", l.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "gobin/Detector.Scan", - "version", detectorVersion, - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := l.FS() if err != nil { @@ -90,7 +85,7 @@ func (Detector) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Pack } walk := func(p string, d fs.DirEntry, err error) error { - ctx := zlog.ContextWithValues(ctx, "filename", d.Name()) + log := slog.With("path", p) switch { case err != nil: @@ -114,9 +109,7 @@ func (Detector) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Pack } if set.Contains(p) { - zlog.Debug(ctx). - Str("path", p). - Msg("file path determined to be of RPM origin") + log.DebugContext(ctx, "file path determined to be of RPM origin") return nil } @@ -124,7 +117,7 @@ func (Detector) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Pack if err != nil { // TODO(crozzy): Remove log line once controller is in a // position to log all the context when receiving an error. - zlog.Warn(ctx).Msg("unable to open file") + log.WarnContext(ctx, "unable to open file") return fmt.Errorf("gobin: unable to open %q: %w", p, err) } defer f.Close() @@ -138,7 +131,7 @@ func (Detector) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Pack default: // TODO(crozzy): Remove log line once controller is in a // position to log all the context when receiving an error. - zlog.Warn(ctx).Msg("unable to read file") + log.WarnContext(ctx, "unable to read file") return fmt.Errorf("gobin: unable to read %q: %w", p, err) } @@ -158,9 +151,7 @@ func (Detector) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Pack case 2: // big-endian typ = binary.BigEndian.Uint16(peek[0x10:]) default: - zlog.Warn(ctx). - Uint8("endianness", e). - Msg("martian ELF") + log.WarnContext(ctx, "martian ELF", "endianness", e) } if typ != 0x02 && typ != 0x03 { // AKA [debug/elf.ET_EXEC] and [debug/elf.ET_DYN] -- not imported in this file by convention. diff --git a/gobin/gobin_test.go b/gobin/gobin_test.go index 83cf657ee..1f671ff25 100644 --- a/gobin/gobin_test.go +++ b/gobin/gobin_test.go @@ -2,7 +2,6 @@ package gobin import ( "archive/tar" - "context" "io" "os" "os/exec" @@ -10,14 +9,12 @@ import ( "strings" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/test" ) func TestEmptyFile(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) mod := test.Modtime(t, "gobin_test.go") // Needs to be the name of this file. p := test.GenerateFixture(t, "nothing.tar", mod, func(t testing.TB, tf *os.File) { @@ -74,7 +71,7 @@ func TestEmptyFile(t *testing.T) { } func TestScanner(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) mod := test.Modtime(t, "gobin_test.go") // Needs to be the name of this file. p := test.GenerateFixture(t, t.Name()+".tar", mod, func(t testing.TB, tf *os.File) { From 851d14598d86881d9bb1db318fe46d79c8d9e1b6 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 14:04:03 -0500 Subject: [PATCH 37/77] libindex: migrate to slog Signed-off-by: Hank Donnay --- libindex/fetcher.go | 34 +++++++-------------------- libindex/fetcher_test.go | 17 ++++---------- libindex/libindex.go | 20 +++++++--------- libindex/libindex_integration_test.go | 3 +-- libindex/libindex_test.go | 10 ++++---- 5 files changed, 27 insertions(+), 57 deletions(-) diff --git a/libindex/fetcher.go b/libindex/fetcher.go index d641a03f9..d8c180146 100644 --- a/libindex/fetcher.go +++ b/libindex/fetcher.go @@ -8,13 +8,13 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "net/url" "os" "runtime" "strings" - "github.com/quay/zlog" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" @@ -154,15 +154,11 @@ func (f closeFunc) Close() error { // Because we know we're the only concurrent call that's dealing with this blob, // we can be a bit more lax. func (a *RemoteFetchArena) fetchFileForCache(ctx context.Context, desc *claircore.LayerDescription) (*os.File, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "libindex/RemoteFetchArena.fetchFileForCache", - "arena", a.root.Name(), - "layer", desc.Digest, - "uri", desc.URI) - ctx, span := tracer.Start(ctx, "RemoteFetchArena.fetchFileForCache") + log := slog.With("arena", a.root, "layer", desc.Digest, "uri", desc.URI) + ctx, span := tracer.Start(ctx, "RemoteFetchArena.fetchUnlinkedFile") defer span.End() span.SetStatus(codes.Error, "") - zlog.Debug(ctx).Msg("layer fetch start") + log.DebugContext(ctx, "layer fetch start") // Validate the layer input. if desc.URI == "" { @@ -215,9 +211,7 @@ func (a *RemoteFetchArena) fetchFileForCache(ctx context.Context, desc *claircor defer zr.Close() // Look at the content-type and optionally fix it up. ct := resp.Header.Get("content-type") - zlog.Debug(ctx). - Str("content-type", ct). - Msg("reported content-type") + log.DebugContext(ctx, "reported content-type", "content-type", ct) span.SetAttributes(attribute.String("payload.content-type", ct), attribute.Stringer("payload.compression.detected", kind)) if ct == "" || ct == "text/plain" || ct == "binary/octet-stream" || ct == "application/octet-stream" { switch kind { @@ -230,9 +224,7 @@ func (a *RemoteFetchArena) fetchFileForCache(ctx context.Context, desc *claircor default: return nil, fmt.Errorf("fetcher: disallowed compression kind: %q", kind.String()) } - zlog.Debug(ctx). - Str("content-type", ct). - Msg("fixed content-type") + log.DebugContext(ctx, "fixed content-type", "content-type", ct) span.SetAttributes(attribute.String("payload.content-type.detected", ct)) } @@ -275,18 +267,12 @@ func (a *RemoteFetchArena) fetchFileForCache(ctx context.Context, desc *claircor return } if err := f.Close(); err != nil { - zlog.Warn(ctx). - Err(err). - Msg("error closing spoolfile in error return") + log.WarnContext(ctx, "error closing spoolfile in error return", "reason", err) } }() buf := bufio.NewWriter(f) n, err := io.Copy(buf, zr) - zlog.Debug(ctx). - Int64("size", n). - Bool("big", n >= bigLayerSize). - AnErr("copy_error", err). - Msg("wrote file") + log.DebugContext(ctx, "wrote file", "size", n, "big", n >= bigLayerSize, "copy_error", err) // TODO(hank) Add a metric for "big files" and a histogram for size. if err != nil { return nil, err @@ -301,7 +287,7 @@ func (a *RemoteFetchArena) fetchFileForCache(ctx context.Context, desc *claircor return nil, err } - zlog.Debug(ctx).Msg("layer fetch ok") + log.DebugContext(ctx, "layer fetch ok") span.SetStatus(codes.Ok, "") fileOK = true return f, nil @@ -352,8 +338,6 @@ func (p *FetchProxy) Realize(ctx context.Context, ls []*claircore.Layer) error { // RealizeDescriptions returns [claircore.Layer] structs populated according to // the passed slice of [claircore.LayerDescription]. func (p *FetchProxy) RealizeDescriptions(ctx context.Context, descs []claircore.LayerDescription) ([]claircore.Layer, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "libindex/FetchProxy.RealizeDescriptions") ctx, span := tracer.Start(ctx, "RealizeDescriptions") defer span.End() g, ctx := errgroup.WithContext(ctx) diff --git a/libindex/fetcher_test.go b/libindex/fetcher_test.go index 03375ccd6..00e763ea3 100644 --- a/libindex/fetcher_test.go +++ b/libindex/fetcher_test.go @@ -16,8 +16,6 @@ import ( "sync/atomic" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/internal/wart" "github.com/quay/claircore/test" @@ -29,7 +27,7 @@ type fetchTestcase struct { func (tc fetchTestcase) Run(ctx context.Context) func(*testing.T) { return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) c, descs := test.ServeLayers(t, tc.N) for _, l := range descs { t.Logf("%+v", l) @@ -65,7 +63,6 @@ func TestFetchSimple(t *testing.T) { func TestFetchInvalid(t *testing.T) { // TODO(hank) Rewrite this into unified testcases. - ctx := context.Background() tt := []struct { name string layer []*claircore.Layer @@ -91,7 +88,7 @@ func TestFetchInvalid(t *testing.T) { tmp := t.TempDir() for _, table := range tt { t.Run(table.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) a := NewRemoteFetchArena(http.DefaultClient, tmp) fetcher := a.Realizer(ctx) if err := fetcher.Realize(ctx, table.layer); err == nil { @@ -103,7 +100,7 @@ func TestFetchInvalid(t *testing.T) { func TestFetchConcurrent(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) descs, h := commonLayerServer(t, 25) srv := httptest.NewUnstartedServer(h) srv.Start() @@ -119,8 +116,6 @@ func TestFetchConcurrent(t *testing.T) { }) t.Run("OldInterface", func(t *testing.T) { - ctx := zlog.Test(ctx, t) - t.Run("Thread", func(t *testing.T) { run := func(a *RemoteFetchArena, ls []claircore.LayerDescription) func(*testing.T) { ps := wart.DescriptionsToLayers(ls) @@ -132,7 +127,7 @@ func TestFetchConcurrent(t *testing.T) { }) return func(t *testing.T) { t.Parallel() - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) f := a.Realizer(ctx) t.Cleanup(func() { if err := f.Close(); err != nil { @@ -151,7 +146,6 @@ func TestFetchConcurrent(t *testing.T) { }) t.Run("NewInterface", func(t *testing.T) { - ctx := zlog.Test(ctx, t) t.Run("Thread", func(t *testing.T) { run := func(a *RemoteFetchArena, descs []claircore.LayerDescription) func(*testing.T) { ds := make([]claircore.LayerDescription, len(descs)) @@ -164,7 +158,7 @@ func TestFetchConcurrent(t *testing.T) { }) return func(t *testing.T) { t.Parallel() - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) f := a.Realizer(ctx).(*FetchProxy) defer func() { if err := f.Close(); err != nil { @@ -239,7 +233,6 @@ func commonLayerServer(t testing.TB, ct int) ([]claircore.LayerDescription, http case total < max: t.Logf("prevented %[3]d fetches: %[1]d < %d", total, max, max-total) } - }) inner := http.FileServer(http.Dir(dir)) return descs, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/libindex/libindex.go b/libindex/libindex.go index 117d06d4f..ffd65364a 100644 --- a/libindex/libindex.go +++ b/libindex/libindex.go @@ -7,11 +7,11 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "sort" "time" - "github.com/quay/zlog" "golang.org/x/sync/errgroup" "github.com/quay/claircore" @@ -27,6 +27,7 @@ import ( "github.com/quay/claircore/rhel/rhcc" "github.com/quay/claircore/rpm" "github.com/quay/claircore/ruby" + "github.com/quay/claircore/toolkit/log" "github.com/quay/claircore/whiteout" ) @@ -70,7 +71,6 @@ type Libindex struct { // The passed http.Client will be used for fetching layers and any HTTP requests // made by scanners. func New(ctx context.Context, opts *Options, cl *http.Client) (*Libindex, error) { - ctx = zlog.ContextWithValues(ctx, "component", "libindex/New") // required if opts.Locker == nil { return nil, fmt.Errorf("field Locker cannot be nil") @@ -142,7 +142,7 @@ func New(ctx context.Context, opts *Options, cl *http.Client) (*Libindex, error) return nil, fmt.Errorf("failed to set the indexer state: %v", err) } - zlog.Info(ctx).Msg("registered configured scanners") + slog.InfoContext(ctx, "registered configured scanners") l.vscnrs = vscnrs // create indexer.Options @@ -176,13 +176,11 @@ func (l *Libindex) Close(ctx context.Context) error { // If the index operation cannot start an error will be returned. // If an error occurs during scan the error will be propagated inside the IndexReport. func (l *Libindex) Index(ctx context.Context, manifest *claircore.Manifest) (*claircore.IndexReport, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "libindex/Libindex.Index", - "manifest", manifest.Hash.String()) - zlog.Info(ctx).Msg("index request start") - defer zlog.Info(ctx).Msg("index request done") + ctx = log.With(ctx, "manifest", manifest.Hash) + slog.InfoContext(ctx, "index request start") + defer slog.InfoContext(ctx, "index request done") - zlog.Debug(ctx).Msg("locking attempt") + slog.DebugContext(ctx, "locking attempt") lc, done := l.locker.Lock(ctx, manifest.Hash.String()) defer done() // The process may have waited on the lock, so check that the context is @@ -190,7 +188,7 @@ func (l *Libindex) Index(ctx context.Context, manifest *claircore.Manifest) (*cl if err := lc.Err(); !errors.Is(err, nil) { return nil, err } - zlog.Debug(ctx).Msg("locking OK") + slog.DebugContext(ctx, "locking OK") c := l.ControllerFactory(l.indexerOptions) return c.Index(lc, manifest) } @@ -242,7 +240,6 @@ func (l *Libindex) IndexReport(ctx context.Context, hash claircore.Digest) (*cla // AffectedManifests retrieves a list of affected manifests when provided a list of vulnerabilities. func (l *Libindex) AffectedManifests(ctx context.Context, vulns []claircore.Vulnerability) (*claircore.AffectedManifests, error) { - ctx = zlog.ContextWithValues(ctx, "component", "libindex/Libindex.AffectedManifests") om := omnimatcher.New(nil) affected := claircore.NewAffectedManifests() @@ -285,6 +282,5 @@ V: // // Providing an unknown digest is not an error. func (l *Libindex) DeleteManifests(ctx context.Context, d ...claircore.Digest) ([]claircore.Digest, error) { - ctx = zlog.ContextWithValues(ctx, "component", "libindex/Libindex.DeleteManifests") return l.store.DeleteManifests(ctx, d...) } diff --git a/libindex/libindex_integration_test.go b/libindex/libindex_integration_test.go index 3441d4906..99230be17 100644 --- a/libindex/libindex_integration_test.go +++ b/libindex/libindex_integration_test.go @@ -12,7 +12,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/jackc/pgx/v5/pgxpool" - "github.com/quay/zlog" "go.uber.org/mock/gomock" "github.com/quay/claircore" @@ -157,7 +156,7 @@ func (tc testcase) Run(ctx context.Context, check checkFunc) func(*testing.T) { return func(t *testing.T) { t.Parallel() integration.NeedDB(t) - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) pool := pgtest.TestIndexerDB(ctx, t) tc.RunInner(ctx, t, pool, check) } diff --git a/libindex/libindex_test.go b/libindex/libindex_test.go index 35ff67dce..b1fa99571 100644 --- a/libindex/libindex_test.go +++ b/libindex/libindex_test.go @@ -8,10 +8,10 @@ import ( "strconv" "testing" - "github.com/quay/zlog" "go.uber.org/mock/gomock" "github.com/quay/claircore" + "github.com/quay/claircore/test" indexer "github.com/quay/claircore/test/mock/indexer" ) @@ -45,7 +45,6 @@ func digest(inp string) claircore.Digest { } func TestAffectedManifests(t *testing.T) { - ctx := context.Background() tt := []struct { name string inputVulns []claircore.Vulnerability @@ -94,7 +93,7 @@ func TestAffectedManifests(t *testing.T) { for _, table := range tt { t.Run(table.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) s := table.mockStore(t) li := &Libindex{store: s} @@ -115,7 +114,7 @@ func TestAffectedManifests(t *testing.T) { t.Run("CancelledContext", func(t *testing.T) { for _, table := range tt { t.Run(table.name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) ctx, cancel := context.WithCancelCause(ctx) want := errors.New("early cancel") cancel(want) @@ -138,7 +137,6 @@ func TestAffectedManifests(t *testing.T) { } func BenchmarkAffectedManifests(b *testing.B) { - ctx := context.Background() // create store ctrl := gomock.NewController(b) s := indexer.NewMockStore(ctrl) @@ -151,7 +149,7 @@ func BenchmarkAffectedManifests(b *testing.B) { nil, ).MaxTimes(100 * b.N) - ctx = zlog.Test(ctx, b) + ctx := test.Logging(b) li := &Libindex{store: s} for b.Loop() { From 575f98e917a3c85739a830f6011e8e23e27bb733 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 14:11:15 -0500 Subject: [PATCH 38/77] linux: migrate to slog Signed-off-by: Hank Donnay --- linux/coalescer_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/linux/coalescer_test.go b/linux/coalescer_test.go index 4a9d84fce..fda4f45e9 100644 --- a/linux/coalescer_test.go +++ b/linux/coalescer_test.go @@ -1,19 +1,16 @@ package linux import ( - "context" "strconv" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/test" ) func TestCoalescer(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) coalescer := &Coalescer{ ir: &claircore.IndexReport{ Environments: map[string][]*claircore.Environment{}, From fb1ce003729c9c22c920456a056e7556a875a89b Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 14:12:08 -0500 Subject: [PATCH 39/77] matchers: migrate to slog Signed-off-by: Hank Donnay --- matchers/matchers.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/matchers/matchers.go b/matchers/matchers.go index 14e9724d7..333c644e7 100644 --- a/matchers/matchers.go +++ b/matchers/matchers.go @@ -4,10 +4,9 @@ import ( "context" "errors" "fmt" + "log/slog" "net/http" - "github.com/quay/zlog" - "github.com/quay/claircore/libvuln/driver" _ "github.com/quay/claircore/matchers/defaults" "github.com/quay/claircore/matchers/registry" @@ -30,7 +29,6 @@ type MatchersOption func(m *Matchers) // NewMatchers will return a slice of Matcher created based on the provided // MatchersOption. func NewMatchers(ctx context.Context, client *http.Client, opts ...MatchersOption) ([]driver.Matcher, error) { - ctx = zlog.ContextWithValues(ctx, "component", "libvuln/matchers/NewMatchers") if client == nil { return nil, errors.New("invalid *http.Client") } @@ -57,7 +55,7 @@ func NewMatchers(ctx context.Context, client *http.Client, opts ...MatchersOptio for _, factory := range m.factories { matcher, err := factory.Matcher(ctx) if err != nil { - zlog.Error(ctx).Err(err).Msg("failed constructing factory, excluding from run") + slog.ErrorContext(ctx, "failed constructing factory, excluding from run", "reason", err) continue } matchers = append(matchers, matcher...) From 90521c14cacb91ba405b1337a7aecfcc87bee8e1 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 14:20:29 -0500 Subject: [PATCH 40/77] nodejs: migrate to slog Signed-off-by: Hank Donnay --- nodejs/coalescer_test.go | 5 +---- nodejs/matcher_test.go | 6 ++---- nodejs/packagescanner.go | 37 +++++++++++------------------------ nodejs/packagescanner_test.go | 5 ++--- 4 files changed, 16 insertions(+), 37 deletions(-) diff --git a/nodejs/coalescer_test.go b/nodejs/coalescer_test.go index dba365f03..d8a55a364 100644 --- a/nodejs/coalescer_test.go +++ b/nodejs/coalescer_test.go @@ -1,12 +1,9 @@ package nodejs import ( - "context" "strconv" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/test" @@ -14,7 +11,7 @@ import ( func TestCoalescer(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) coalescer := &coalescer{} pkgs := test.GenUniquePackages(6) repo := []*claircore.Repository{&Repository} diff --git a/nodejs/matcher_test.go b/nodejs/matcher_test.go index 329360d3a..0cd1bd753 100644 --- a/nodejs/matcher_test.go +++ b/nodejs/matcher_test.go @@ -1,15 +1,13 @@ package nodejs import ( - "context" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore/test" ) func TestMatcher(t *testing.T) { t.Parallel() - test.RunMatcherTests(zlog.Test(context.Background(), t), t, "testdata/matcher", new(Matcher)) + ctx := test.Logging(t) + test.RunMatcherTests(ctx, t, "testdata/matcher", new(Matcher)) } diff --git a/nodejs/packagescanner.go b/nodejs/packagescanner.go index 9c56be593..1e0e5af90 100644 --- a/nodejs/packagescanner.go +++ b/nodejs/packagescanner.go @@ -8,12 +8,12 @@ import ( "encoding/json" "fmt" "io/fs" + "log/slog" "path/filepath" "runtime/trace" "strings" "github.com/Masterminds/semver" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -67,12 +67,8 @@ type packageJSON struct { func (s *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircore.Package, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() trace.Log(ctx, "layer", layer.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "nodejs/Scanner.Scan", - "version", s.Version(), - "layer", layer.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") if err := ctx.Err(); err != nil { return nil, err } @@ -99,9 +95,7 @@ func (s *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircor for _, p := range pkgs { if set.Contains(p) { - zlog.Debug(ctx). - Str("path", p). - Msg("file path determined to be of RPM origin") + slog.DebugContext(ctx, "file path determined to be of RPM origin", "path", p) continue } @@ -128,17 +122,16 @@ func (s *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircor if sv, err := semver.NewVersion(pkgJSON.Version); err == nil { pkg.NormalizedVersion = claircore.FromSemver(sv) } else { - zlog.Info(ctx). - Str("package", pkg.Name). - Str("version", pkg.Version). - Msg("invalid semantic version") + slog.InfoContext(ctx, "invalid semantic version", + "package", pkg.Name, + "version", pkg.Version) } ret = append(ret, pkg) } if len(invalidPkgs) > 0 { - zlog.Debug(ctx).Strs("paths", invalidPkgs).Msg("unable to decode package.json, skipping") + slog.DebugContext(ctx, "unable to decode package.json, skipping", "paths", invalidPkgs) } return ret, nil @@ -146,14 +139,7 @@ func (s *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircor func packages(ctx context.Context, sys fs.FS) (out []string, err error) { return out, fs.WalkDir(sys, ".", func(p string, d fs.DirEntry, err error) error { - ev := zlog.Debug(ctx). - Str("file", p) - var success bool - defer func() { - if !success { - ev.Discard().Send() - } - }() + attrs := []slog.Attr{slog.String("file", p)} switch { case err != nil: return err @@ -168,12 +154,11 @@ func packages(ctx context.Context, sys fs.FS) (out []string, err error) { // for more information. return nil case strings.HasSuffix(p, "/package.json"): - ev = ev.Str("kind", "package.json") + attrs = append(attrs, slog.String("kind", "package.json")) default: return nil } - ev.Msg("found package") - success = true + slog.LogAttrs(ctx, slog.LevelDebug, "found package", attrs...) out = append(out, p) return nil }) diff --git a/nodejs/packagescanner_test.go b/nodejs/packagescanner_test.go index d8aa08ae3..54818cf61 100644 --- a/nodejs/packagescanner_test.go +++ b/nodejs/packagescanner_test.go @@ -7,14 +7,13 @@ import ( "github.com/quay/claircore" "github.com/quay/claircore/nodejs" + "github.com/quay/claircore/test" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" ) func TestScanLocal(t *testing.T) { t.Parallel() - ctx := t.Context() table := []struct { name string @@ -52,7 +51,7 @@ func TestScanLocal(t *testing.T) { } defer file.Close() - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) scanner := &nodejs.Scanner{} var l claircore.Layer err = l.Init(ctx, &claircore.LayerDescription{ From 676e9a784df5d564dd18648c7513898aaf28d571 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 14:25:06 -0500 Subject: [PATCH 41/77] oracle: migrate to slog Signed-off-by: Hank Donnay --- oracle/distributionscanner.go | 13 ++++--------- oracle/parser.go | 26 +++++++++++--------------- oracle/parser_test.go | 5 ++--- oracle/updater_test.go | 6 ++---- 4 files changed, 19 insertions(+), 31 deletions(-) diff --git a/oracle/distributionscanner.go b/oracle/distributionscanner.go index fc5446b6b..5c002e620 100644 --- a/oracle/distributionscanner.go +++ b/oracle/distributionscanner.go @@ -3,11 +3,10 @@ package oracle import ( "bytes" "context" + "log/slog" "regexp" "runtime/trace" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) @@ -83,15 +82,11 @@ func (*DistributionScanner) Kind() string { return scannerKind } // If the files are found but all regexp fail to match an empty slice is returned. func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() - ctx = zlog.ContextWithValues(ctx, - "component", "oracle/DistributionScanner.Scan", - "version", ds.Version(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") files, err := l.Files(osReleasePath, issuePath) if err != nil { - zlog.Debug(ctx).Msg("didn't find an os-release or issues file") + slog.DebugContext(ctx, "didn't find an os-release or issues file") return nil, nil } for _, buff := range files { diff --git a/oracle/parser.go b/oracle/parser.go index 386be1b33..e1b4c2e06 100644 --- a/oracle/parser.go +++ b/oracle/parser.go @@ -5,9 +5,9 @@ import ( "encoding/xml" "fmt" "io" + "log/slog" "github.com/quay/goval-parser/oval" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/internal/xmlutil" @@ -36,8 +36,7 @@ var platformToDist = map[string]*claircore.Distribution{ var _ driver.Parser = (*Updater)(nil) func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, "component", "oracle/Updater.Parse") - zlog.Info(ctx).Msg("starting parse") + slog.InfoContext(ctx, "starting parse") defer r.Close() root := oval.Root{} dec := xml.NewDecoder(r) @@ -45,7 +44,7 @@ func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln if err := dec.Decode(&root); err != nil { return nil, fmt.Errorf("oracle: unable to decode OVAL document: %w", err) } - zlog.Debug(ctx).Msg("xml decoded") + slog.DebugContext(ctx, "xml decoded") protoVulns := func(def oval.Definition) ([]*claircore.Vulnerability, error) { // In all oracle databases tested a single // and correct platform string can be found inside a definition @@ -87,10 +86,9 @@ func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln if err != nil { // Found a CPE but could not parse it. Log a warning and return // successfully. - zlog.Warn(ctx). - Str("def_title", def.Title). - Str("cpe", affected). - Msg("could not parse CPE: there may be a false positive match with a userspace_ksplice package") + slog.WarnContext(ctx, "could not parse CPE: there may be a false positive match with a userspace_ksplice package", + "def_title", def.Title, + "cpe", affected) return vs, nil } if wfn.Attr[cpe.Edition].V == "userspace_ksplice" { @@ -102,16 +100,14 @@ func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln case kspliceCPEs == 0: // Continue if there are no ksplice CPEs. case cpes == 0: - zlog.Warn(ctx). - Str("def_title", def.Title). - Msg("potential false positives: couldn't find CPEs to check for ksplice packages") + slog.WarnContext(ctx, "potential false positives: couldn't find CPEs to check for ksplice packages", + "def_title", def.Title) case diff == 0: - zlog.Debug(ctx).Msg("skipping userspace_ksplice vulnerabilities") + slog.DebugContext(ctx, "skipping userspace_ksplice vulnerabilities") return nil, nil case diff > 0: - zlog.Warn(ctx). - Str("def_title", def.Title). - Msg("potential false positives: OVAL may have a userspace_ksplice CPE which could not be skipped") + slog.WarnContext(ctx, "potential false positives: OVAL may have a userspace_ksplice CPE which could not be skipped", + "def_title", def.Title) default: panic("programmer error") } diff --git a/oracle/parser_test.go b/oracle/parser_test.go index b1d5a3e05..34ef0e756 100644 --- a/oracle/parser_test.go +++ b/oracle/parser_test.go @@ -1,16 +1,15 @@ package oracle import ( - "context" "os" "testing" - "github.com/quay/zlog" + "github.com/quay/claircore/test" ) func TestParse(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) u, err := NewUpdater(-1) if err != nil { t.Fatal(err) diff --git a/oracle/updater_test.go b/oracle/updater_test.go index d6cccf304..62a29bc8c 100644 --- a/oracle/updater_test.go +++ b/oracle/updater_test.go @@ -1,18 +1,16 @@ package oracle import ( - "context" "net/http" "net/http/httptest" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore/libvuln/driver" + "github.com/quay/claircore/test" ) func TestFetch(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "testdata/com.oracle.elsa-2018.xml") })) From ec95a895400832588740aa2c6e274d53e1f5f74b Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 14:30:29 -0500 Subject: [PATCH 42/77] osrelease: migrate to slog Signed-off-by: Hank Donnay --- osrelease/scanner.go | 39 +++++++++++---------------------------- osrelease/scanner_test.go | 7 ++----- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/osrelease/scanner.go b/osrelease/scanner.go index c3116511d..041d51752 100644 --- a/osrelease/scanner.go +++ b/osrelease/scanner.go @@ -7,12 +7,12 @@ import ( "context" "fmt" "io" + "log/slog" "runtime/trace" "sort" "strings" "github.com/quay/claircore/toolkit/types/cpe" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -57,12 +57,8 @@ func (*Scanner) Kind() string { return scannerKind } // present in the layer. func (s *Scanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() - ctx = zlog.ContextWithValues(ctx, - "component", "osrelease/Scanner.Scan", - "version", s.Version(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := l.FS() if err != nil { @@ -75,10 +71,7 @@ func (s *Scanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Di for _, n := range []string{Path, FallbackPath} { f, err := sys.Open(n) if err != nil { - zlog.Debug(ctx). - Str("name", n). - Err(err). - Msg("unable to open file") + slog.DebugContext(ctx, "unable to open file", "name", n, "reason", err) continue } defer f.Close() @@ -86,7 +79,7 @@ func (s *Scanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Di break } if rd == nil { - zlog.Debug(ctx).Msg("didn't find an os-release file") + slog.DebugContext(ctx, "didn't find an os-release file") return nil, nil } d, err := toDist(ctx, rd) @@ -98,8 +91,6 @@ func (s *Scanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Di // ToDist returns the distribution information from the file contents provided on r. func toDist(ctx context.Context, r io.Reader) (*claircore.Distribution, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "osrelease/parse") defer trace.StartRegion(ctx, "parse").End() m, err := Parse(ctx, r) if err != nil { @@ -115,48 +106,41 @@ func toDist(ctx context.Context, r io.Reader) (*claircore.Distribution, error) { } sort.Strings(ks) for _, key := range ks { + slog.DebugContext(ctx, "found key", "key", key) value := m[key] switch key { case "ID": - zlog.Debug(ctx).Msg("found ID") d.DID = value case "VERSION_ID": - zlog.Debug(ctx).Msg("found VERSION_ID") d.VersionID = value case "BUILD_ID": case "VARIANT_ID": case "CPE_NAME": - zlog.Debug(ctx).Msg("found CPE_NAME") wfn, err := cpe.Unbind(value) if err != nil { - zlog.Warn(ctx). - Err(err). - Str("value", value). - Msg("failed to unbind the cpe") + slog.WarnContext(ctx, "failed to unbind the cpe", "reason", err, "value", value) break } d.CPE = wfn case "NAME": - zlog.Debug(ctx).Msg("found NAME") d.Name = value case "VERSION": - zlog.Debug(ctx).Msg("found VERSION") d.Version = value case "ID_LIKE": case "VERSION_CODENAME": - zlog.Debug(ctx).Msg("found VERISON_CODENAME") d.VersionCodeName = value case "PRETTY_NAME": - zlog.Debug(ctx).Msg("found PRETTY_NAME") d.PrettyName = value case "REDHAT_BUGZILLA_PRODUCT": - zlog.Debug(ctx).Msg("using RHEL hack") + slog.DebugContext(ctx, "using RHEL hack") // This is a dirty hack because the Red Hat OVAL database and the // CPE contained in the os-release file don't agree. d.PrettyName = value + default: + continue } } - zlog.Debug(ctx).Str("name", d.Name).Msg("found dist") + slog.DebugContext(ctx, "found dist", "name", d.Name) return &d, nil } @@ -165,7 +149,6 @@ func toDist(ctx context.Context, r io.Reader) (*claircore.Distribution, error) { // // See comments in the source for edge cases. func Parse(ctx context.Context, r io.Reader) (map[string]string, error) { - ctx = zlog.ContextWithValues(ctx, "component", "osrelease/Parse") defer trace.StartRegion(ctx, "Parse").End() m := make(map[string]string) s := bufio.NewScanner(r) diff --git a/osrelease/scanner_test.go b/osrelease/scanner_test.go index 91e01a18d..5b1c26a8c 100644 --- a/osrelease/scanner_test.go +++ b/osrelease/scanner_test.go @@ -2,14 +2,12 @@ package osrelease import ( "bytes" - "context" "encoding/json" "path/filepath" "strings" "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "golang.org/x/tools/txtar" "github.com/quay/claircore" @@ -18,14 +16,13 @@ import ( func TestParse(t *testing.T) { t.Parallel() - ctx := context.Background() ms, _ := filepath.Glob("testdata/*.txtar") for i := range ms { m := ms[i] name := strings.TrimSuffix(filepath.Base(m), ".txtar") t.Run(name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) ar, err := txtar.ParseFile(m) if err != nil { t.Fatal(err) @@ -99,7 +96,7 @@ type layerspec struct { func (lc layercase) Test(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) l := test.RealizeLayer(ctx, t, lc.Layer) var s Scanner From f3a0335fcf280cb317cabf66af993c9e0e9fa93a Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 14:45:55 -0500 Subject: [PATCH 43/77] photon: migrate to slog Signed-off-by: Hank Donnay --- photon/distributionscanner.go | 13 ++++--------- photon/parser.go | 9 ++++----- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/photon/distributionscanner.go b/photon/distributionscanner.go index 3d2bb0d27..b6bceae60 100644 --- a/photon/distributionscanner.go +++ b/photon/distributionscanner.go @@ -3,11 +3,10 @@ package photon import ( "bytes" "context" + "log/slog" "regexp" "runtime/trace" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) @@ -74,15 +73,11 @@ func (*DistributionScanner) Kind() string { return scannerKind } // If the files are found but all regexp fail to match an empty slice is returned. func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() - ctx = zlog.ContextWithValues(ctx, - "component", "photon/DistributionScanner.Scan", - "version", ds.Version(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") files, err := l.Files(osReleasePath, photonReleasePath) if err != nil { - zlog.Debug(ctx).Msg("didn't find an os-release or photon-release") + slog.DebugContext(ctx, "didn't find an os-release or photon-release") return nil, nil } for _, buff := range files { diff --git a/photon/parser.go b/photon/parser.go index 7467b1462..47018fb48 100644 --- a/photon/parser.go +++ b/photon/parser.go @@ -5,9 +5,9 @@ import ( "encoding/xml" "fmt" "io" + "log/slog" "github.com/quay/goval-parser/oval" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/internal/xmlutil" @@ -18,8 +18,7 @@ import ( var _ driver.Parser = (*Updater)(nil) func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, "component", "photon/Updater.Parse") - zlog.Info(ctx).Msg("starting parse") + slog.InfoContext(ctx, "starting parse") defer r.Close() root := oval.Root{} dec := xml.NewDecoder(r) @@ -27,11 +26,11 @@ func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln if err := dec.Decode(&root); err != nil { return nil, fmt.Errorf("photon: unable to decode OVAL document: %w", err) } - zlog.Debug(ctx).Msg("xml decoded") + slog.DebugContext(ctx, "xml decoded") protoVulns := func(def oval.Definition) ([]*claircore.Vulnerability, error) { return []*claircore.Vulnerability{ - &claircore.Vulnerability{ + { Updater: u.Name(), Name: def.Title, Description: def.Description, From 82ff52969af4e63007ec3693655f3fa084733d1f Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 15:02:05 -0500 Subject: [PATCH 44/77] python: migrate to slog Signed-off-by: Hank Donnay --- python/matcher.go | 31 +++++++++----------- python/matcher_test.go | 10 +++---- python/packagescanner.go | 53 +++++++++++------------------------ python/packagescanner_test.go | 4 +-- 4 files changed, 35 insertions(+), 63 deletions(-) diff --git a/python/matcher.go b/python/matcher.go index 737c5004f..dec42af2f 100644 --- a/python/matcher.go +++ b/python/matcher.go @@ -2,10 +2,9 @@ package python import ( "context" + "log/slog" "net/url" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" "github.com/quay/claircore/pkg/pep440" @@ -42,10 +41,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v // Parse the package first. If it cannot be parsed, it cannot properly be analyzed for vulnerabilities. rv, err := pep440.Parse(record.Package.Version) if err != nil { - zlog.Warn(ctx). - Str("package", record.Package.Name). - Stringer("version", &rv). - Msg("unable to parse python package version") + slog.WarnContext(ctx, "unable to parse python package version", + "package", record.Package.Name, + "version", &rv) return false, err } @@ -59,10 +57,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v if introduced != "" { iv, err := pep440.Parse(introduced) if err != nil { - zlog.Warn(ctx). - Str("package", vuln.Package.Name). - Str("version", introduced). - Msg("unable to parse python introduced version") + slog.WarnContext(ctx, "unable to parse python introduced version", + "package", vuln.Package.Name, + "version", introduced) return false, err } // If the package's version is less than the introduced version, it's not vulnerable. @@ -77,10 +74,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v case fixedVersion != "": uv, err := pep440.Parse(fixedVersion) if err != nil { - zlog.Warn(ctx). - Str("package", vuln.Package.Name). - Str("version", fixedVersion). - Msg("unable to parse python fixed version") + slog.WarnContext(ctx, "unable to parse python fixed version", + "package", vuln.Package.Name, + "version", fixedVersion) return false, err } // The package is affected if its version is less than the fixed version. @@ -88,10 +84,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v case lastAffected != "": la, err := pep440.Parse(lastAffected) if err != nil { - zlog.Warn(ctx). - Str("package", vuln.Package.Name). - Str("version", lastAffected). - Msg("unable to parse python last_affected version") + slog.WarnContext(ctx, "unable to parse python last_affected version", + "package", vuln.Package.Name, + "version", lastAffected) return false, err } // The package is affected if its version is less than or equal to the last affected version. diff --git a/python/matcher_test.go b/python/matcher_test.go index 6b9ee5101..186d50286 100644 --- a/python/matcher_test.go +++ b/python/matcher_test.go @@ -1,11 +1,9 @@ package python_test import ( - "context" "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" @@ -23,7 +21,7 @@ type matcherTestcase struct { func (tc matcherTestcase) Run(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) got, err := tc.Matcher.Vulnerable(ctx, &tc.R, &tc.V) if err != nil { t.Error(err) @@ -238,7 +236,8 @@ func TestMatcher(t *testing.T) { for _, testcase := range testcases { t.Run(testcase.Name, func(t *testing.T) { - got, err := testcase.Matcher.Vulnerable(context.Background(), &testcase.R, &testcase.V) + ctx := test.Logging(t) + got, err := testcase.Matcher.Vulnerable(ctx, &testcase.R, &testcase.V) if err != nil { t.Fatal(err) } @@ -248,5 +247,6 @@ func TestMatcher(t *testing.T) { }) } - test.RunMatcherTests(zlog.Test(context.Background(), t), t, "testdata/matcher", new(python.Matcher)) + ctx := test.Logging(t) + test.RunMatcherTests(ctx, t, "testdata/matcher", new(python.Matcher)) } diff --git a/python/packagescanner.go b/python/packagescanner.go index 6f6eb8d96..df990411a 100644 --- a/python/packagescanner.go +++ b/python/packagescanner.go @@ -8,14 +8,13 @@ import ( "context" "fmt" "io/fs" + "log/slog" "net/textproto" "path" "path/filepath" "runtime/trace" "strings" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/pkg/pep440" @@ -59,12 +58,8 @@ func (*Scanner) Kind() string { return "package" } func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircore.Package, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() trace.Log(ctx, "layer", layer.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "python/Scanner.Scan", - "version", ps.Version(), - "layer", layer.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") if err := ctx.Err(); err != nil { return nil, err } @@ -85,9 +80,7 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco } for _, n := range ms { if set.Contains(n) { - zlog.Debug(ctx). - Str("path", n). - Msg("file path determined to be of RPM origin") + slog.DebugContext(ctx, "file path determined to be of RPM origin", "path", n) continue } b, err := fs.ReadFile(sys, n) @@ -99,18 +92,12 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco rd := textproto.NewReader(bufio.NewReader(bytes.NewReader(b))) hdr, err := rd.ReadMIMEHeader() if err != nil && hdr == nil { - zlog.Warn(ctx). - Err(err). - Str("path", n). - Msg("unable to read metadata, skipping") + slog.WarnContext(ctx, "unable to read metadata, skipping", "reason", err, "path", n) continue } v, err := pep440.Parse(hdr.Get("Version")) if err != nil { - zlog.Warn(ctx). - Err(err). - Str("path", n). - Msg("couldn't parse the version, skipping") + slog.WarnContext(ctx, "couldn't parse the version, skipping", "reason", err, "path", n) continue } pkgDB := filepath.Join(n, "..", "..") @@ -172,14 +159,7 @@ func findDeliciousEgg(ctx context.Context, sys fs.FS) (out []string, err error) } return out, fs.WalkDir(sys, ".", func(p string, d fs.DirEntry, err error) error { - ev := zlog.Debug(ctx). - Str("file", p) - var success bool - defer func() { - if !success { - ev.Discard().Send() - } - }() + attrs := []slog.Attr{slog.String("file", p)} switch { case err != nil: return err @@ -190,15 +170,15 @@ func findDeliciousEgg(ctx context.Context, sys fs.FS) (out []string, err error) switch { case isRPM: pat = `usr/lib*/python[23].*` - ev = ev.Bool("rpm_dir", true) + attrs = append(attrs, slog.Bool("rpm_dir", true)) case dpkg: pat = `usr/lib*/python[23]` - ev = ev.Bool("dpkg_dir", true) + attrs = append(attrs, slog.Bool("dpkg_dir", true)) default: panic("programmer error: unreachable") } if m, _ := path.Match(pat, p); m { - ev.Msg("skipping directory") + slog.LogAttrs(ctx, slog.LevelDebug, "skipping directory", attrs...) return fs.SkipDir } fallthrough @@ -208,29 +188,28 @@ func findDeliciousEgg(ctx context.Context, sys fs.FS) (out []string, err error) case strings.HasPrefix(filepath.Base(p), ".wh."): return nil case strings.HasSuffix(p, `.egg/EGG-INFO/PKG-INFO`): - ev = ev.Str("kind", ".egg") + attrs = append(attrs, slog.String("kind", ".egg")) case strings.HasSuffix(p, `.egg-info`): fallthrough case strings.HasSuffix(p, `.egg-info/PKG-INFO`): - ev = ev.Str("kind", ".egg-info") + attrs = append(attrs, slog.String("kind", ".egg-info")) case strings.HasSuffix(p, `.dist-info/METADATA`): - ev = ev.Str("kind", "wheel") + attrs = append(attrs, slog.String("kind", "wheel")) // See if we can discern the installer. var installer string ip := path.Join(path.Dir(p), `INSTALLER`) if ic, err := fs.ReadFile(sys, ip); err == nil { installer = string(bytes.TrimSpace(ic)) - ev = ev.Str("installer", installer) + attrs = append(attrs, slog.String("installer", installer)) } if _, ok := blocklist[installer]; ok { - ev.Msg("skipping package") + slog.LogAttrs(ctx, slog.LevelDebug, "skipping package", attrs...) return nil } default: return nil } - ev.Msg("found package") - success = true + slog.LogAttrs(ctx, slog.LevelDebug, "found package", attrs...) out = append(out, p) return nil }) diff --git a/python/packagescanner_test.go b/python/packagescanner_test.go index fe6d502b8..9306cf4b5 100644 --- a/python/packagescanner_test.go +++ b/python/packagescanner_test.go @@ -13,7 +13,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/python" @@ -80,7 +79,6 @@ var scanTable = []test.ScannerTestcase{ func TestScanLocal(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) type testcase struct { Name string @@ -97,7 +95,7 @@ func TestScanLocal(t *testing.T) { for _, tt := range table { t.Run(tt.Name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) f, err := os.Open(tt.Path) if err != nil { t.Fatal(err) From 5c2b8850b464d56ac6b452992d77c71eed76be08 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 15:11:37 -0500 Subject: [PATCH 45/77] rpm: migrate to slog Signed-off-by: Hank Donnay --- rpm/packagescanner.go | 14 ++++---------- rpm/packagescanner_test.go | 13 +++++-------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/rpm/packagescanner.go b/rpm/packagescanner.go index 0bd2e0304..6fd7d381d 100644 --- a/rpm/packagescanner.go +++ b/rpm/packagescanner.go @@ -6,10 +6,9 @@ import ( "errors" "fmt" "io/fs" + "log/slog" "runtime/trace" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/internal/rpm" @@ -56,12 +55,8 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) (out []*cla } defer trace.StartRegion(ctx, "Scanner.Scan").End() trace.Log(ctx, "layer", layer.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "rpm/Scanner.Scan", - "version", ps.Version(), - "layer", layer.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := layer.FS() if err != nil { @@ -74,8 +69,7 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) (out []*cla }() for db := range dbs { err = func() error { - ctx := zlog.ContextWithValues(ctx, "db", db.String()) - zlog.Debug(ctx).Msg("examining database") + slog.DebugContext(ctx, "examining database", "db", db) db, dbErr := rpm.OpenDB(ctx, sys, db) switch { case dbErr == nil: diff --git a/rpm/packagescanner_test.go b/rpm/packagescanner_test.go index a2a464b3d..4f14237a9 100644 --- a/rpm/packagescanner_test.go +++ b/rpm/packagescanner_test.go @@ -1,7 +1,6 @@ package rpm import ( - "context" "os" "path/filepath" "slices" @@ -9,7 +8,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/internal/wart" @@ -25,7 +23,7 @@ import ( ////go:generate fetch -o testdata/package/ubi9_nodejs.txtar ubi9/nodejs-20 func TestPackageDetection(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) ms, err := filepath.Glob("testdata/package/*.txtar") if err != nil { panic("programmer error") // static glob @@ -66,7 +64,7 @@ func TestPackageDetection(t *testing.T) { arches[img.Archtecture] = struct{}{} t.Run(img.Archtecture, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) m, err := ar.Manifest(img.ID) if err != nil { t.Fatal(err) @@ -119,7 +117,7 @@ func TestPackageDetection(t *testing.T) { } func TestDanglingSymlink(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) desc := claircore.LayerDescription{ Digest: test.RandomSHA256Digest(t).String(), URI: "file:///dev/null", @@ -154,7 +152,6 @@ func TestDanglingSymlink(t *testing.T) { } func TestLayer(t *testing.T) { - ctx := context.Background() ents, err := os.ReadDir(`testdata/layers`) if err != nil { t.Fatal(err) @@ -173,7 +170,7 @@ func TestLayer(t *testing.T) { continue } t.Run(n, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) f, err := os.Open(filepath.Join(`testdata/layers`, n)) if err != nil { t.Fatal(err) @@ -203,7 +200,7 @@ func TestLayer(t *testing.T) { } func TestLayerDoesNotExist(t *testing.T) { - ctx := zlog.Test(t.Context(), t) + ctx := test.Logging(t) desc := &claircore.LayerDescription{ Digest: test.RandomSHA256Digest(t).String(), From 69b9048bb4033f5dda7339a5ab05a11913b23804 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 15:23:00 -0500 Subject: [PATCH 46/77] ruby: migrate to slog Signed-off-by: Hank Donnay --- ruby/matcher.go | 31 ++++++++++++---------------- ruby/matcher_test.go | 4 ++-- ruby/packagescanner.go | 40 ++++++++++--------------------------- ruby/packagescanner_test.go | 6 +----- 4 files changed, 27 insertions(+), 54 deletions(-) diff --git a/ruby/matcher.go b/ruby/matcher.go index 8f42df9ba..c1b18d772 100644 --- a/ruby/matcher.go +++ b/ruby/matcher.go @@ -2,10 +2,9 @@ package ruby import ( "context" + "log/slog" "net/url" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" ) @@ -41,10 +40,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v // Parse the package first. If it cannot be parsed, it cannot properly be analyzed for vulnerabilities. rv, err := NewVersion(record.Package.Version) if err != nil { - zlog.Warn(ctx). - Str("package", record.Package.Name). - Str("version", record.Package.Version). - Msg("unable to parse ruby gem package version") + slog.WarnContext(ctx, "unable to parse ruby gem package version", + "package", record.Package.Name, + "version", record.Package.Version) return false, err } @@ -58,10 +56,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v if introduced != "" { iv, err := NewVersion(introduced) if err != nil { - zlog.Warn(ctx). - Str("package", vuln.Package.Name). - Str("version", introduced). - Msg("unable to parse ruby gem introduced version") + slog.WarnContext(ctx, "unable to parse ruby gem introduced version", + "package", vuln.Package.Name, + "version", introduced) return false, err } // If the package's version is less than the introduced version, it's not vulnerable. @@ -76,10 +73,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v case fixedVersion != "": fv, err := NewVersion(fixedVersion) if err != nil { - zlog.Warn(ctx). - Str("package", vuln.Package.Name). - Str("version", fixedVersion). - Msg("unable to parse ruby gem fixed version") + slog.WarnContext(ctx, "unable to parse ruby gem fixed version", + "package", vuln.Package.Name, + "version", fixedVersion) return false, err } // The package is affected if its version is less than the fixed version. @@ -87,10 +83,9 @@ func (*Matcher) Vulnerable(ctx context.Context, record *claircore.IndexRecord, v case lastAffected != "": la, err := NewVersion(lastAffected) if err != nil { - zlog.Warn(ctx). - Str("package", vuln.Package.Name). - Str("version", lastAffected). - Msg("unable to parse ruby gem last_affected version") + slog.WarnContext(ctx, "unable to parse ruby gem last_affected version", + "package", vuln.Package.Name, + "version", lastAffected) return false, err } // The package is affected if its version is less than or equal to the last affected version. diff --git a/ruby/matcher_test.go b/ruby/matcher_test.go index a23a2ffe4..a4b9222f2 100644 --- a/ruby/matcher_test.go +++ b/ruby/matcher_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/test" @@ -183,5 +182,6 @@ func TestVulnerable(t *testing.T) { } func TestMatcher(t *testing.T) { - test.RunMatcherTests(zlog.Test(context.Background(), t), t, "testdata/matcher", new(Matcher)) + ctx := test.Logging(t) + test.RunMatcherTests(ctx, t, "testdata/matcher", new(Matcher)) } diff --git a/ruby/packagescanner.go b/ruby/packagescanner.go index 93100ef0e..c7da5fa17 100644 --- a/ruby/packagescanner.go +++ b/ruby/packagescanner.go @@ -7,13 +7,12 @@ import ( "context" "fmt" "io/fs" + "log/slog" "path/filepath" "regexp" "runtime/trace" "strings" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" "github.com/quay/claircore/rpm" @@ -82,12 +81,8 @@ func (*Scanner) Kind() string { return "package" } func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*claircore.Package, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() trace.Log(ctx, "layer", layer.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "ruby/Scanner.Scan", - "version", ps.Version(), - "layer", layer.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") if err := ctx.Err(); err != nil { return nil, err } @@ -109,9 +104,7 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco } for _, g := range gs { if set.Contains(g) { - zlog.Debug(ctx). - Str("path", g). - Msg("file path determined to be of RPM origin") + slog.DebugContext(ctx, "file path determined to be of RPM origin", "path", g) continue } f, err := sys.Open(g) @@ -132,17 +125,14 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco } } if err := scanner.Err(); err != nil { - zlog.Warn(ctx). - Err(err). - Str("path", g). - Msg("unable to read metadata, skipping") + slog.WarnContext(ctx, "unable to read metadata, skipping", + "reason", err, + "path", g) continue } if name == "" || version == "" { - zlog.Warn(ctx). - Str("path", g). - Msg("couldn't parse name or version, skipping") + slog.WarnContext(ctx, "couldn't parse name or version, skipping", "path", g) continue } @@ -172,14 +162,7 @@ func trim(s string) string { func gems(ctx context.Context, sys fs.FS) (out []string, err error) { return out, fs.WalkDir(sys, ".", func(p string, d fs.DirEntry, err error) error { - ev := zlog.Debug(ctx). - Str("file", p) - var success bool - defer func() { - if !success { - ev.Discard().Send() - } - }() + attrs := []slog.Attr{slog.String("file", p)} switch { case err != nil: return err @@ -189,12 +172,11 @@ func gems(ctx context.Context, sys fs.FS) (out []string, err error) { case strings.HasPrefix(filepath.Base(p), ".wh."): return nil case gemspecPath.MatchString(p): - ev = ev.Str("kind", "gem") + attrs = append(attrs, slog.String("kind", "gem")) default: return nil } - ev.Msg("found package") - success = true + slog.LogAttrs(ctx, slog.LevelDebug, "found package", attrs...) out = append(out, p) return nil }) diff --git a/ruby/packagescanner_test.go b/ruby/packagescanner_test.go index fc5bb422c..9704f7183 100644 --- a/ruby/packagescanner_test.go +++ b/ruby/packagescanner_test.go @@ -1,11 +1,9 @@ package ruby_test import ( - "context" "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/ruby" @@ -13,8 +11,6 @@ import ( ) func TestScanRemote(t *testing.T) { - ctx := zlog.Test(context.Background(), t) - type testcase struct { Name string Ref test.LayerRef @@ -138,7 +134,7 @@ func TestScanRemote(t *testing.T) { for _, tc := range table { t.Run(tc.Name, func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) l := test.RealizeLayer(ctx, t, tc.Ref) var scanner ruby.Scanner From 8bc9c8bd315d931fc39f688cc122392934d2e0c8 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 15:28:26 -0500 Subject: [PATCH 47/77] suse: migrate to slog Signed-off-by: Hank Donnay --- suse/distributionscanner.go | 18 +++++++----------- suse/factory.go | 10 +++------- suse/factory_test.go | 7 ++++--- suse/parser.go | 8 +++----- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/suse/distributionscanner.go b/suse/distributionscanner.go index cae924edb..05a1c34e0 100644 --- a/suse/distributionscanner.go +++ b/suse/distributionscanner.go @@ -4,10 +4,10 @@ import ( "bytes" "context" "fmt" + "log/slog" "runtime/trace" "github.com/Masterminds/semver" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" @@ -64,15 +64,11 @@ func (*DistributionScanner) Kind() string { return scannerKind } // If the files are found but all regexp fail to match an empty slice is returned. func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() - ctx = zlog.ContextWithValues(ctx, - "component", "suse/DistributionScanner.Scan", - "version", ds.Version(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") files, err := l.Files(osReleasePath, suseReleasePath) if err != nil { - zlog.Debug(ctx).Msg("didn't find an os-release or SuSE-release") + slog.DebugContext(ctx, "didn't find an os-release or SuSE-release") return nil, nil } for _, buff := range files { @@ -91,7 +87,7 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([] func (ds *DistributionScanner) parse(ctx context.Context, buff *bytes.Buffer) *claircore.Distribution { kv, err := osrelease.Parse(ctx, buff) if err != nil { - zlog.Warn(ctx).Err(err).Msg("malformed os-release file") + slog.WarnContext(ctx, "malformed os-release file", "reason", err) return nil } cpeName, cpeOK := kv["CPE_NAME"] @@ -101,13 +97,13 @@ func (ds *DistributionScanner) parse(ctx context.Context, buff *bytes.Buffer) *c // Instead of regexing through, we can grab the CPE. c, err := cpe.Unbind(cpeName) if err != nil { - zlog.Warn(ctx).Err(err).Msg("could not unbind CPE") + slog.WarnContext(ctx, "could not unbind CPE", "reason", err) return nil } d, err := cpeToDist(c) if err != nil { - zlog.Warn(ctx).Err(err).Msg("error converting cpe to distribution") + slog.WarnContext(ctx, "error converting cpe to distribution", "reason", err) return nil } diff --git a/suse/factory.go b/suse/factory.go index 8f70875c7..62737aad0 100644 --- a/suse/factory.go +++ b/suse/factory.go @@ -3,6 +3,7 @@ package suse import ( "context" "fmt" + "log/slog" "net/http" "net/url" "regexp" @@ -10,7 +11,6 @@ import ( "sync" "github.com/Masterminds/semver" - "github.com/quay/zlog" "golang.org/x/net/html" "github.com/quay/claircore" @@ -37,20 +37,16 @@ type Factory struct { // UpdaterSet implements [driver.UpdaterSetFactory]. func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { - ctx = zlog.ContextWithValues(ctx, "component", "suse/Factory.UpdaterSet") us := driver.NewUpdaterSet() if f.c == nil { - zlog.Info(ctx). - Msg("unconfigured") + slog.InfoContext(ctx, "unconfigured") return us, nil } req, err := http.NewRequestWithContext(ctx, http.MethodGet, f.base.String(), nil) if err != nil { return us, fmt.Errorf("suse: unable to construct request: %w", err) } - zlog.Debug(ctx). - Stringer("url", f.base). - Msg("making request") + slog.DebugContext(ctx, "making request", "url", f.base) res, err := f.c.Do(req) if err != nil { return us, fmt.Errorf("suse: error requesting %q: %w", f.base.String(), err) diff --git a/suse/factory_test.go b/suse/factory_test.go index 22aca61b5..7ab76c838 100644 --- a/suse/factory_test.go +++ b/suse/factory_test.go @@ -1,16 +1,17 @@ package suse import ( - "context" "net/http" "net/url" "testing" - "github.com/quay/zlog" + "github.com/quay/claircore/test" + "github.com/quay/claircore/test/integration" ) func TestFactory(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + integration.Skip(t) + ctx := test.Logging(t) u, _ := url.Parse(base) f := &Factory{ c: http.DefaultClient, diff --git a/suse/parser.go b/suse/parser.go index e53cde0e0..eca750efb 100644 --- a/suse/parser.go +++ b/suse/parser.go @@ -5,9 +5,9 @@ import ( "encoding/xml" "fmt" "io" + "log/slog" "github.com/quay/goval-parser/oval" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/internal/xmlutil" @@ -18,9 +18,7 @@ import ( var _ driver.Parser = (*Updater)(nil) func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "suse/Updater.Parse") - zlog.Info(ctx).Msg("starting parse") + slog.InfoContext(ctx, "starting parse") defer r.Close() root := oval.Root{} dec := xml.NewDecoder(r) @@ -28,7 +26,7 @@ func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln if err := dec.Decode(&root); err != nil { return nil, fmt.Errorf("suse: unable to decode OVAL document: %w", err) } - zlog.Debug(ctx).Msg("xml decoded") + slog.DebugContext(ctx, "xml decoded") protoVulns := func(def oval.Definition) ([]*claircore.Vulnerability, error) { return []*claircore.Vulnerability{ { From 50ff13dd90e2d1598020dbcc29dcab2ad55bbfbe Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 6 Oct 2025 15:36:27 -0500 Subject: [PATCH 48/77] ubuntu: migrate to slog Signed-off-by: Hank Donnay --- ubuntu/distributionscanner.go | 11 +++-------- ubuntu/updater.go | 26 +++++++++----------------- ubuntu/updaterset.go | 25 +++++++++---------------- 3 files changed, 21 insertions(+), 41 deletions(-) diff --git a/ubuntu/distributionscanner.go b/ubuntu/distributionscanner.go index 95ec100f1..4dc513017 100644 --- a/ubuntu/distributionscanner.go +++ b/ubuntu/distributionscanner.go @@ -7,11 +7,10 @@ import ( "fmt" "io" "io/fs" + "log/slog" "runtime/trace" "strings" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) @@ -45,12 +44,8 @@ func (*DistributionScanner) Kind() string { return scannerKind } // Scan implements [indexer.DistributionScanner]. func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]*claircore.Distribution, error) { defer trace.StartRegion(ctx, "Scanner.Scan").End() - ctx = zlog.ContextWithValues(ctx, - "component", "ubuntu/DistributionScanner.Scan", - "version", ds.Version(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := l.FS() if err != nil { return nil, fmt.Errorf("ubuntu: unable to open layer: %w", err) diff --git a/ubuntu/updater.go b/ubuntu/updater.go index 8060ecf33..726c9cea6 100644 --- a/ubuntu/updater.go +++ b/ubuntu/updater.go @@ -6,11 +6,11 @@ import ( "encoding/xml" "fmt" "io" + "log/slog" "net/http" "net/url" "github.com/quay/goval-parser/oval" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/internal/xmlutil" @@ -43,9 +43,6 @@ func (u *updater) Name() string { // Configure implements [driver.Configurable]. func (u *updater) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, - "component", "ubuntu/Updater.Configure", - "updater", u.Name()) u.c = c var cfg UpdaterConfig @@ -58,8 +55,7 @@ func (u *updater) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c * return err } u.url = cfg.URL - zlog.Info(ctx). - Msg("configured database URL") + slog.InfoContext(ctx, "configured database URL") } if cfg.UseBzip2 != nil { u.useBzip2 = *cfg.UseBzip2 @@ -79,9 +75,7 @@ type UpdaterConfig struct { // Fetch implements [driver.Updater]. func (u *updater) Fetch(ctx context.Context, fingerprint driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "ubuntu/Updater.Fetch", - "database", u.url) + log := slog.With("database", u.url) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.url, nil) if err != nil { @@ -101,7 +95,7 @@ func (u *updater) Fetch(ctx context.Context, fingerprint driver.Fingerprint) (io switch resp.StatusCode { case http.StatusOK: if fp := string(fingerprint); fp == "" || fp != resp.Header.Get("etag") { - zlog.Info(ctx).Msg("fetching latest oval database") + log.InfoContext(ctx, "fetching latest oval database") break } fallthrough @@ -120,7 +114,7 @@ func (u *updater) Fetch(ctx context.Context, fingerprint driver.Fingerprint) (io defer func() { if !success { if err := f.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close spool") + log.WarnContext(ctx, "unable to close spool", "reason", err) } } }() @@ -136,15 +130,13 @@ func (u *updater) Fetch(ctx context.Context, fingerprint driver.Fingerprint) (io } success = true - zlog.Info(ctx).Msg("fetched latest oval database successfully") + log.InfoContext(ctx, "fetched latest oval database successfully") return f, driver.Fingerprint(fp), err } // Parse implements [driver.Updater]. func (u *updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "ubuntu/Updater.Parse") - zlog.Info(ctx).Msg("starting parse") + slog.InfoContext(ctx, "starting parse") defer r.Close() root := oval.Root{} dec := xml.NewDecoder(r) @@ -152,7 +144,7 @@ func (u *updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln if err := dec.Decode(&root); err != nil { return nil, fmt.Errorf("ubuntu: unable to decode OVAL document: %w", err) } - zlog.Debug(ctx).Msg("xml decoded") + slog.DebugContext(ctx, "xml decoded") nameLookupFunc := func(def oval.Definition, name *oval.DpkgName) []string { // if the dpkginfo_object>name field has a var_ref it indicates @@ -167,7 +159,7 @@ func (u *updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln } _, i, err := root.Variables.Lookup(name.Ref) if err != nil { - zlog.Error(ctx).Err(err).Msg("could not lookup variable id") + slog.WarnContext(ctx, "could not lookup variable id", "reason", err) return ns } consts := root.Variables.ConstantVariables[i] diff --git a/ubuntu/updaterset.go b/ubuntu/updaterset.go index 42ffd4ed5..da9e59282 100644 --- a/ubuntu/updaterset.go +++ b/ubuntu/updaterset.go @@ -4,13 +4,13 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "net/url" "path" "strings" "sync" - "github.com/quay/zlog" "golang.org/x/sync/errgroup" "github.com/quay/claircore" @@ -67,8 +67,6 @@ type FactoryConfig struct { // Configure implements [driver.Configurable]. func (f *Factory) Configure(ctx context.Context, cf driver.ConfigUnmarshaler, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, - "component", "ubuntu/Factory.Configure") var cfg FactoryConfig if err := cf(&cfg); err != nil { return err @@ -84,8 +82,7 @@ func (f *Factory) Configure(ctx context.Context, cf driver.ConfigUnmarshaler, c if err != nil { return fmt.Errorf("ubuntu: unable to parse provided URL: %w", err) } - zlog.Info(ctx). - Msg("configured URL") + slog.InfoContext(ctx, "configured URL") } n := defaultName if cfg.Name != "" { @@ -106,9 +103,6 @@ func (f *Factory) Configure(ctx context.Context, cf driver.ConfigUnmarshaler, c // UpdaterSet implements [driver.UpdaterSetFactory] func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "ubuntu/Factory.UpdaterSet") - set := driver.NewUpdaterSet() req, err := http.NewRequestWithContext(ctx, http.MethodGet, f.api, nil) if err != nil { @@ -146,7 +140,7 @@ func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { e := &series.Entries[i] mkDist(e.Version, e.Name) if !e.Active { - zlog.Debug(ctx).Str("release", e.Name).Msg("release not active") + slog.DebugContext(ctx, "release not active", "release", e.Name) continue } select { @@ -175,10 +169,9 @@ func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { switch res.StatusCode { case http.StatusOK: case http.StatusNotFound: - zlog.Debug(ctx). - Str("name", e.Name). - Str("version", e.Version). - Msg("OVAL database missing, skipping") + slog.DebugContext(ctx, "OVAL database missing, skipping", + "name", e.Name, + "version", e.Version) continue default: return fmt.Errorf("ubuntu: unexpected status requesting %q: %q", url, res.Status) @@ -210,7 +203,7 @@ func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { } if len(f.force) != 0 { - zlog.Info(ctx).Msg("configuring manually specified updaters") + slog.InfoContext(ctx, "configuring manually specified updaters") ns := make([]string, 0, len(f.force)) for _, p := range f.force { u := &updater{ @@ -221,12 +214,12 @@ func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { } if err := set.Add(u); err != nil { // Already exists, skip. - zlog.Debug(ctx).Err(err).Msg("skipping updater") + slog.DebugContext(ctx, "skipping updater", "reason", err) continue } ns = append(ns, u.Name()) } - zlog.Info(ctx).Strs("updaters", ns).Msg("added specified updaters") + slog.InfoContext(ctx, "added specified updaters", "updaters", ns) } return set, nil From 85724f0eb446b4349482a0b7b749af8a0c2faa83 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Wed, 8 Oct 2025 11:26:14 -0500 Subject: [PATCH 49/77] updater/osv: migrate to slog Signed-off-by: Hank Donnay --- updater/osv/osv.go | 117 ++++++++++++++-------------------------- updater/osv/osv_test.go | 26 ++++----- 2 files changed, 53 insertions(+), 90 deletions(-) diff --git a/updater/osv/osv.go b/updater/osv/osv.go index 1d7333c75..d6f4bf4aa 100644 --- a/updater/osv/osv.go +++ b/updater/osv/osv.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "io/fs" + "log/slog" "net/http" "net/http/httputil" "net/url" @@ -20,7 +21,6 @@ import ( "github.com/Masterminds/semver" "github.com/quay/claircore/toolkit/types/cvss" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" @@ -74,7 +74,6 @@ type FactoryConfig struct { // Configure implements driver.Configurable. func (f *Factory) Configure(ctx context.Context, cf driver.ConfigUnmarshaler, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, "component", "updater/osv/factory.Configure") var err error f.c = c @@ -100,12 +99,11 @@ func (f *Factory) Configure(ctx context.Context, cf driver.ConfigUnmarshaler, c } } - zlog.Debug(ctx).Msg("loaded incoming config") + slog.DebugContext(ctx, "loaded incoming config") return nil } func (f *Factory) UpdaterSet(ctx context.Context) (s driver.UpdaterSet, err error) { - ctx = zlog.ContextWithValues(ctx, "component", "updater/osv/factory.UpdaterSet") s = driver.NewUpdaterSet() if f.root == nil || f.c == nil { return s, errors.New("osv: factory not configured") // Purposely return an unhandleable error. @@ -117,10 +115,9 @@ func (f *Factory) UpdaterSet(ctx context.Context) (s driver.UpdaterSet, err erro defer func() { // This is an info print so operators can compare their allow list, // if need be. - zlog.Info(ctx). - Strs("ecosystems", stats.ecosystems). - Strs("skipped", stats.skipped). - Msg("ecosystems stats") + slog.InfoContext(ctx, "ecosystems stats", + "ecosystems", stats.ecosystems, + "skipped", stats.skipped) }() uri := *f.root @@ -156,9 +153,7 @@ func (f *Factory) UpdaterSet(ctx context.Context) (s driver.UpdaterSet, err erro } seen[e] = struct{}{} if _, ok := ignore[e]; ok { - zlog.Debug(ctx). - Str("ecosystem", e). - Msg("ignoring ecosystem") + slog.DebugContext(ctx, "ignoring ecosystem", "ecosystem", e) continue } stats.ecosystems = append(stats.ecosystems, e) @@ -170,10 +165,9 @@ func (f *Factory) UpdaterSet(ctx context.Context) (s driver.UpdaterSet, err erro uri := (*f.root).JoinPath(k, "all.zip") up := &updater{name: name, ecosystem: e, c: f.c, uri: uri} if err = s.Add(up); err != nil { - zlog.Error(ctx). - Str("ecosystem", e). - Err(err). - Msg("Failed to add updater to updaterset") + slog.WarnContext(ctx, "failed to add updater to updaterset", + "reason", err, + "ecosystem", e) continue } } @@ -188,9 +182,7 @@ func (f *Factory) UpdaterSet(ctx context.Context) (s driver.UpdaterSet, err erro err = fmt.Errorf("osv: unexpected response from %q: %v (request: %q) (body: %q)", res.Request.URL, res.Status, b, buf) } if err := res.Body.Close(); err != nil { - zlog.Info(ctx). - Err(err). - Msg("error closing ecosystems.txt response body") + slog.InfoContext(ctx, "error closing ecosystems.txt response body", "reason", err) } if err != nil { return s, err @@ -236,7 +228,6 @@ type UpdaterConfig struct { // Configure implements driver.Configurable. func (u *updater) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, "component", "updater/osv/updater.Configure") var err error u.c = c @@ -250,28 +241,22 @@ func (u *updater) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c * return err } } - zlog.Debug(ctx).Msg("loaded incoming config") + slog.DebugContext(ctx, "loaded incoming config") return nil } // Fetcher implements driver.Updater. func (u *updater) Fetch(ctx context.Context, fp driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { - ctx = zlog.ContextWithValues(ctx, "component", "updater/osv/updater.Fetch") - out, err := tmp.NewFile("", "osv.fetch.*") if err != nil { return nil, fp, err } defer func() { if _, err := out.Seek(0, io.SeekStart); err != nil { - zlog.Warn(ctx). - Err(err). - Msg("unable to seek file back to start") + slog.WarnContext(ctx, "unable to seek file back to start", "reason", err) } }() - zlog.Debug(ctx). - Str("filename", out.Name()). - Msg("opened temporary file for output") + slog.DebugContext(ctx, "opened temporary file for output", "filename", out.Name()) w := zip.NewWriter(out) defer w.Close() var ct int @@ -282,9 +267,7 @@ func (u *updater) Fetch(ctx context.Context, fp driver.Fingerprint) (io.ReadClos } req.Header.Set(`accept`, `application/zip`) if fp != "" { - zlog.Debug(ctx). - Str("hint", string(fp)). - Msg("using hint") + slog.DebugContext(ctx, "using hint", "hint", string(fp)) req.Header.Set("if-none-match", string(fp)) } @@ -304,26 +287,20 @@ func (u *updater) Fetch(ctx context.Context, fp driver.Fingerprint) (io.ReadClos if err != nil { break } - zlog.Debug(ctx). - Str("name", n). - Msg("wrote zip") + slog.DebugContext(ctx, "wrote zip", "name", n) ct++ case http.StatusNotModified: default: err = fmt.Errorf("osv: unexpected response from %q: %v", res.Request.URL.String(), res.Status) } if err := res.Body.Close(); err != nil { - zlog.Info(ctx). - Err(err). - Msg("error closing advisory zip response body") + slog.InfoContext(ctx, "error closing advisory zip response body", "reason", err) } if err != nil { return nil, fp, err } newEtag := res.Header.Get(`etag`) - zlog.Info(ctx). - Int("count", ct). - Msg("found updates") + slog.InfoContext(ctx, "found updates", "count", ct) if ct == 0 { return nil, fp, driver.Unchanged } @@ -333,11 +310,9 @@ func (u *updater) Fetch(ctx context.Context, fp driver.Fingerprint) (io.ReadClos // Fetcher implements driver.Updater. func (u *updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, "component", "updater/osv/updater.Parse") ra, ok := r.(io.ReaderAt) if !ok { - zlog.Info(ctx). - Msg("spooling to disk") + slog.InfoContext(ctx, "spooling to disk") tf, err := tmp.NewFile("", `osv.parse.spool.*`) if err != nil { return nil, err @@ -388,9 +363,8 @@ func (u *updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln now := time.Now() ecs := newECS(u.Name()) for _, zf := range z.File { - ctx := zlog.ContextWithValues(ctx, "dumpfile", zf.Name) - zlog.Debug(ctx). - Msg("found file") + log := slog.With("dumpfile", zf.Name) + log.DebugContext(ctx, "found file") r, err := zf.Open() if err != nil { return nil, err @@ -411,7 +385,6 @@ func (u *updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln var skipped stats var ct int for _, zf := range z.File { - ctx := zlog.ContextWithValues(ctx, "advisory", strings.TrimSuffix(path.Base(zf.Name), ".json")) ct++ var a advisory rc, err := zf.Open() @@ -434,22 +407,18 @@ func (u *updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln default: } - if err := ecs.Insert(ctx, &skipped, name, &a); err != nil { + log := log.With("advisory", strings.TrimSuffix(path.Base(zf.Name), ".json")) + if err := ecs.Insert(ctx, log, &skipped, name, &a); err != nil { return nil, err } } - zlog.Debug(ctx). - Int("count", ct). - Msg("processed advisories") - zlog.Debug(ctx). - Strs("withdrawn", skipped.Withdrawn). - Strs("unaffected", skipped.Unaffected). - Strs("ignored", skipped.Ignored). - Msg("skipped advisories") - } - zlog.Info(ctx). - Int("count", ecs.Len()). - Msg("found vulnerabilities") + log.DebugContext(ctx, "processed advisories", "count", ct) + log.DebugContext(ctx, "skipped advisories", + "withdrawn", skipped.Withdrawn, + "unaffected", skipped.Unaffected, + "ignored", skipped.Ignored) + } + slog.InfoContext(ctx, "found vulnerabilities", "count", ecs.Len()) return ecs.Finalize(), nil } @@ -502,7 +471,7 @@ type rangeVer struct { ecosystemRange url.Values } -func (e *ecs) Insert(ctx context.Context, skipped *stats, name string, a *advisory) (err error) { +func (e *ecs) Insert(ctx context.Context, log *slog.Logger, skipped *stats, name string, a *advisory) (err error) { if a.GitOnly() { return nil } @@ -537,9 +506,7 @@ func (e *ecs) Insert(ctx context.Context, skipped *stats, name string, a *adviso continue } if err != nil { - zlog.Info(ctx). - Err(err). - Msg("odd cvss mangling result") + log.InfoContext(ctx, "odd cvss mangling result", "reason", err) continue } switch q { @@ -593,9 +560,7 @@ func (e *ecs) Insert(ctx context.Context, skipped *stats, name string, a *adviso // ignore, not going to fetch source. continue default: - zlog.Debug(ctx). - Str("type", r.Type). - Msg("odd range type") + log.DebugContext(ctx, "odd range type", "type", r.Type) } // This does some heavy assumptions about valid inputs. vs := &rangeVer{semverRange: &claircore.Range{}, ecosystemRange: url.Values{}} @@ -635,11 +600,10 @@ func (e *ecs) Insert(ctx context.Context, skipped *stats, name string, a *adviso } case ev.LastAffected != "" && len(af.Versions) != 0: // less than equal to // TODO(hank) Should be able to convert this to a "less than." - zlog.Info(ctx). - Str("which", "last_affected"). - Str("event", ev.LastAffected). - Strs("versions", af.Versions). - Msg("unsure how to interpret event") + log.InfoContext(ctx, "unsure how to interpret event", + "which", "last_affected", + "event", ev.LastAffected, + "versions", af.Versions) case ev.LastAffected != "": // less than equal to // This is semver, so we should be able to calculate the // "next" version: @@ -652,10 +616,9 @@ func (e *ecs) Insert(ctx context.Context, skipped *stats, name string, a *adviso vs.semverRange.Upper.Kind = `semver` vs.semverRange.Upper.V[0] = 65535 case ev.Limit != "": // Something arbitrary - zlog.Info(ctx). - Str("which", "limit"). - Str("event", ev.Limit). - Msg("unsure how to interpret event") + log.InfoContext(ctx, "unsure how to interpret event", + "which", "limit", + "event", ev.Limit) } case `ECOSYSTEM`: switch af.Package.Ecosystem { @@ -696,7 +659,7 @@ func (e *ecs) Insert(ctx context.Context, skipped *stats, name string, a *adviso } } if err != nil { - zlog.Warn(ctx).Err(err).Msg("event version error") + log.WarnContext(ctx, "event version error", "reason", err) } vers = append(vers, vs) } diff --git a/updater/osv/osv_test.go b/updater/osv/osv_test.go index bda1597bd..6b42f1bf5 100644 --- a/updater/osv/osv_test.go +++ b/updater/osv/osv_test.go @@ -4,12 +4,12 @@ import ( "archive/zip" "bufio" "bytes" - "context" "encoding/json" "errors" "fmt" "io" "io/fs" + "log/slog" "net/http" "net/http/httptest" "os" @@ -21,16 +21,16 @@ import ( "github.com/Masterminds/semver" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" + "github.com/quay/claircore/test" ) func TestFetch(t *testing.T) { srv := httptest.NewServer(&apiStub{t, ""}) defer srv.Close() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) f := Factory{} cfgFunc := func(v any) error { @@ -110,7 +110,7 @@ func (a *apiStub) ServeHTTP(w http.ResponseWriter, r *http.Request) { func TestParse(t *testing.T) { srv := httptest.NewServer(&apiStub{t, ""}) defer srv.Close() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) f := Factory{} cfgFunc := func(v any) error { @@ -615,13 +615,13 @@ var cmpIgnore = cmpopts.IgnoreFields( claircore.Vulnerability{}, "ID", "Updater", "Description", "Severity", "NormalizedSeverity", "Package", "Repo") func TestInsert(t *testing.T) { - ctx := zlog.Test(context.Background(), t) - for _, tt := range insertTestCases { t.Run(tt.name, func(t *testing.T) { + ctx := test.Logging(t) ecs := newECS("test") - err := ecs.Insert(ctx, nil, "", tt.ad) + log := slog.Default() + err := ecs.Insert(ctx, log, nil, "", tt.ad) if err != nil { t.Error("got error Inserting advisory", err) } @@ -773,13 +773,13 @@ var severityTestCases = []struct { } func TestSeverityParsing(t *testing.T) { - ctx := zlog.Test(context.Background(), t) - for _, tt := range severityTestCases { t.Run(tt.name, func(t *testing.T) { + ctx := test.Logging(t) ecs := newECS("test") - err := ecs.Insert(ctx, nil, "", tt.a) + log := slog.Default() + err := ecs.Insert(ctx, log, nil, "", tt.a) if err != nil { t.Error("got error Inserting advisory", err) } @@ -793,7 +793,6 @@ func TestSeverityParsing(t *testing.T) { if v.Severity != tt.expectedSeverity { t.Errorf("expected severity %q but got %q", tt.expectedSeverity, v.Severity) } - }) } } @@ -875,12 +874,13 @@ func TestInsertLinksAliases(t *testing.T) { }, } - ctx := zlog.Test(context.Background(), t) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + ctx := test.Logging(t) + log := slog.Default() e := newECS("osv") var st stats - if err := (&e).Insert(ctx, &st, "pkg", &tt.adv); err != nil { + if err := (&e).Insert(ctx, log, &st, "pkg", &tt.adv); err != nil { t.Fatalf("Insert() error: %v", err) } if len(e.Vulnerability) == 0 { From 03773c7955be8746d11f4b8e379eefde7d575f14 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Wed, 8 Oct 2025 15:17:48 -0700 Subject: [PATCH 50/77] pkg/ovalutil: migrate to slog Signed-off-by: Hank Donnay --- pkg/ovalutil/dpkg.go | 28 ++++++++++------------------ pkg/ovalutil/fetcher.go | 38 ++++++++++++++------------------------ pkg/ovalutil/rpm.go | 26 +++++++++++--------------- 3 files changed, 35 insertions(+), 57 deletions(-) diff --git a/pkg/ovalutil/dpkg.go b/pkg/ovalutil/dpkg.go index 8475d34ec..1434066de 100644 --- a/pkg/ovalutil/dpkg.go +++ b/pkg/ovalutil/dpkg.go @@ -4,12 +4,11 @@ import ( "context" "errors" "fmt" + "log/slog" "regexp" "strings" "github.com/quay/goval-parser/oval" - "github.com/quay/zlog" - "github.com/rs/zerolog" "github.com/quay/claircore" ) @@ -23,7 +22,6 @@ type PackageExpansionFunc func(def oval.Definition, name *oval.DpkgName) []strin // // Each Criterion encountered with an EVR string will be translated into a claircore.Vulnerability func DpkgDefsToVulns(ctx context.Context, root *oval.Root, protoVulns ProtoVulnsFunc, expansionFunc PackageExpansionFunc) ([]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, "component", "ovalutil/DpkgDefsToVulns") vulns := make([]*claircore.Vulnerability, 0, 10000) pkgcache := map[string]*claircore.Package{} cris := []*oval.Criterion{} @@ -36,10 +34,7 @@ func DpkgDefsToVulns(ctx context.Context, root *oval.Root, protoVulns ProtoVulns // create our prototype vulnerability protoVulns, err := protoVulns(def) if err != nil { - zlog.Debug(ctx). - Err(err). - Str("def_id", def.ID). - Msg("could not create prototype vulnerabilities") + slog.DebugContext(ctx, "could not create prototype vulnerabilities", "reason", err, "def_id", def.ID) continue } // recursively collect criterions for this definition @@ -131,19 +126,16 @@ func DpkgDefsToVulns(ctx context.Context, root *oval.Root, protoVulns ProtoVulns } } } - zlog.Debug(ctx). - Int("test", stats.Test). - Int("object", stats.Obj). - Int("state", stats.State). - Msg("ref lookup failures") - if ev := zlog.Debug(ctx); ev.Enabled() { - d := zerolog.Dict() + slog.DebugContext(ctx, "ref lookup failures", + "test", stats.Test, + "object", stats.Obj, + "state", stats.State) + if slog.Default().Enabled(ctx, slog.LevelDebug) { + var bad []slog.Attr for k, v := range badvers { - d.Str(k, v) + bad = append(bad, slog.String(k, v)) } - ev. - Dict("package-version", d). - Msg("bogus versions") + slog.DebugContext(ctx, "bogus versions", "package-version", slog.GroupValue(bad...)) } return vulns, nil } diff --git a/pkg/ovalutil/fetcher.go b/pkg/ovalutil/fetcher.go index d0a0af646..49cdf95d6 100644 --- a/pkg/ovalutil/fetcher.go +++ b/pkg/ovalutil/fetcher.go @@ -6,13 +6,12 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "mime" "net/http" "net/url" "path" - "github.com/quay/zlog" - "github.com/quay/claircore/libvuln/driver" "github.com/quay/claircore/pkg/tmp" ) @@ -66,7 +65,6 @@ type Fetcher struct { // For users that embed a Fetcher, this provides a configuration hook by // default. func (f *Fetcher) Configure(ctx context.Context, cf driver.ConfigUnmarshaler, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, "component", "pkg/ovalutil/Fetcher.Configure") var cfg FetcherConfig if err := cf(&cfg); err != nil { return err @@ -77,8 +75,7 @@ func (f *Fetcher) Configure(ctx context.Context, cf driver.ConfigUnmarshaler, c return err } f.URL = uri - zlog.Info(ctx). - Msg("configured database URL") + slog.InfoContext(ctx, "configured database URL") } if cfg.Compression != "" { c, err := ParseCompressor(cfg.Compression) @@ -86,8 +83,7 @@ func (f *Fetcher) Configure(ctx context.Context, cf driver.ConfigUnmarshaler, c return err } f.Compression = c - zlog.Info(ctx). - Msg("configured database compression") + slog.InfoContext(ctx, "configured database compression") } f.Client = c @@ -112,8 +108,7 @@ type FetcherConfig struct { // // Tmp.File is used to return a ReadCloser that outlives the passed-in context. func (f *Fetcher) Fetch(ctx context.Context, hint driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { - ctx = zlog.ContextWithValues(ctx, "component", "pkg/ovalutil/Fetcher.Fetch") - zlog.Info(ctx).Str("database", f.URL.String()).Msg("starting fetch") + slog.InfoContext(ctx, "starting fetch", "database", f.URL) req := http.Request{ Method: http.MethodGet, Header: http.Header{ @@ -150,7 +145,7 @@ func (f *Fetcher) Fetch(ctx context.Context, hint driver.Fingerprint) (io.ReadCl default: return nil, hint, fmt.Errorf("ovalutil: fetcher got unexpected HTTP response: %d (%s)", res.StatusCode, res.Status) } - zlog.Debug(ctx).Msg("request ok") + slog.DebugContext(ctx, "request ok") var r io.Reader cmp := f.Compression @@ -186,11 +181,10 @@ Compression: default: panic("unreachable") } - zlog.Debug(ctx). - Err(err). - Str("header", h). - Str("value", v). - Msg("failed to parse incoming HTTP header") + slog.DebugContext(ctx, "failed to parse incoming HTTP header", + "reason", err, + "header", h, + "value", v) } kind = mime.TypeByExtension(path.Ext(res.Request.URL.Path)) Found: @@ -227,23 +221,19 @@ Compression: default: panic(fmt.Sprintf("ovalutil: programmer error: unknown compression scheme: %v", f.Compression)) } - zlog.Debug(ctx). - Stringer("compression", cmp). - Msg("found compression scheme") + slog.DebugContext(ctx, "found compression scheme", "compression", cmp) tf, err := tmp.NewFile("", "fetcher.") if err != nil { return nil, hint, err } - zlog.Debug(ctx). - Str("path", tf.Name()). - Msg("using tempfile") + slog.DebugContext(ctx, "using tempfile", "path", tf.Name()) success := false defer func() { if !success { - zlog.Debug(ctx).Msg("unsuccessful, cleaning up tempfile") + slog.DebugContext(ctx, "unsuccessful, cleaning up tempfile") if err := tf.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("failed to close tempfile") + slog.WarnContext(ctx, "failed to close tempfile", "reason", err) } } }() @@ -254,7 +244,7 @@ Compression: if o, err := tf.Seek(0, io.SeekStart); err != nil || o != 0 { return nil, hint, err } - zlog.Debug(ctx).Msg("decompressed and buffered database") + slog.DebugContext(ctx, "decompressed and buffered database") fp.From(res.Header) hint = fp.Fingerprint() diff --git a/pkg/ovalutil/rpm.go b/pkg/ovalutil/rpm.go index f3cd9430b..ce6cc27f2 100644 --- a/pkg/ovalutil/rpm.go +++ b/pkg/ovalutil/rpm.go @@ -4,10 +4,10 @@ import ( "context" "errors" "fmt" + "log/slog" "regexp" "github.com/quay/goval-parser/oval" - "github.com/quay/zlog" "github.com/quay/claircore" ) @@ -41,17 +41,15 @@ type ProtoVulnsFunc func(def oval.Definition) ([]*claircore.Vulnerability, error // // Each Criterion encountered with an EVR string will be translated into a claircore.Vulnerability func RPMDefsToVulns(ctx context.Context, root *oval.Root, protoVulns ProtoVulnsFunc) ([]*claircore.Vulnerability, error) { - ctx = zlog.ContextWithValues(ctx, "component", "ovalutil/RPMDefsToVulns") vulns := make([]*claircore.Vulnerability, 0, 10000) cris := []*oval.Criterion{} for _, def := range root.Definitions.Definitions { // create our prototype vulnerability protoVulns, err := protoVulns(def) if err != nil { - zlog.Debug(ctx). - Err(err). - Str("def_id", def.ID). - Msg("could not create prototype vulnerabilities") + slog.DebugContext(ctx, "could not create prototype vulnerabilities", + "reason", err, + "def_id", def.ID) continue } // recursively collect criterions for this definition @@ -77,7 +75,7 @@ func RPMDefsToVulns(ctx context.Context, root *oval.Root, protoVulns ProtoVulnsF case errors.Is(err, errTestSkip): continue default: - zlog.Debug(ctx).Str("test_ref", criterion.TestRef).Msg("test ref lookup failure. moving to next criterion") + slog.DebugContext(ctx, "test ref lookup failure, moving to next criterion", "test_ref", criterion.TestRef) continue } @@ -98,10 +96,9 @@ func RPMDefsToVulns(ctx context.Context, root *oval.Root, protoVulns ProtoVulnsF // We only handle rpminfo_objects. continue default: - zlog.Debug(ctx). - Err(err). - Str("object_ref", objRef). - Msg("failed object lookup. moving to next criterion") + slog.DebugContext(ctx, "failed object lookup, moving to next criterion", + "reason", err, + "object_ref", objRef) continue } @@ -113,10 +110,9 @@ func RPMDefsToVulns(ctx context.Context, root *oval.Root, protoVulns ProtoVulnsF stateRef := stateRefs[0].StateRef state, err = rpmStateLookup(root, stateRef) if err != nil { - zlog.Debug(ctx). - Err(err). - Str("state_ref", stateRef). - Msg("failed state lookup. moving to next criterion") + slog.DebugContext(ctx, "failed state lookup, moving to next criterion", + "reason", err, + "state_ref", stateRef) continue } // if we find a state, but this state does not contain an EVR, From fc5cb6b7e5f41b5ad16cf20ea24167454f2ace74 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Wed, 8 Oct 2025 16:54:42 -0700 Subject: [PATCH 51/77] scanner/pkgconfig: migrate to slog Signed-off-by: Hank Donnay --- scanner/pkgconfig/scanner.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/scanner/pkgconfig/scanner.go b/scanner/pkgconfig/scanner.go index 0a1ee5ed2..53e8ea655 100644 --- a/scanner/pkgconfig/scanner.go +++ b/scanner/pkgconfig/scanner.go @@ -12,12 +12,11 @@ import ( "fmt" "io" "io/fs" + "log/slog" "os" "path/filepath" "runtime/trace" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) @@ -49,12 +48,8 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco } defer trace.StartRegion(ctx, "Scanner.Scan").End() trace.Log(ctx, "layer", layer.Hash.String()) - ctx = zlog.ContextWithValues(ctx, - "component", "scanner/pkgconfig/Scanner.Scan", - "version", ps.Version(), - "layer", layer.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := layer.FS() if err != nil { @@ -71,9 +66,8 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco case filepath.Ext(d.Name()) != ".pc": return nil } - zlog.Debug(ctx). - Str("path", p). - Msg("found possible pkg-config file") + log := slog.With("path", p) + log.DebugContext(ctx, "found possible pkg-config file") f, err := sys.Open(p) if err != nil { return err @@ -84,9 +78,7 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco switch { case errors.Is(nil, err): case errors.Is(errInvalid, err): // skip - zlog.Info(ctx). - Str("path", p). - Msg("invalid pkg-config file") + slog.InfoContext(ctx, "invalid pkg-config file") return nil default: return err From 727cd8ea6d1218116eb4b45d5691b79365216795 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Wed, 8 Oct 2025 16:58:15 -0700 Subject: [PATCH 52/77] whiteout: migrate to slog Signed-off-by: Hank Donnay --- whiteout/resolver.go | 16 ++++++---------- whiteout/resolver_test.go | 7 ++----- whiteout/scanner.go | 11 +++-------- whiteout/scanner_test.go | 5 +---- 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/whiteout/resolver.go b/whiteout/resolver.go index d98cac5f1..fa913f60e 100644 --- a/whiteout/resolver.go +++ b/whiteout/resolver.go @@ -2,18 +2,15 @@ package whiteout import ( "context" + "log/slog" "path/filepath" "strings" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) -var ( - _ indexer.Resolver = (*Resolver)(nil) -) +var _ indexer.Resolver = (*Resolver)(nil) type Resolver struct{} @@ -40,11 +37,10 @@ func (r *Resolver) Resolve(ctx context.Context, ir *claircore.IndexReport, layer // layer. if f.Kind == claircore.FileKindWhiteout && ls.isChildOf(fileLayer, packageLayer) && fileIsDeleted(pkg.Filepath, f.Path) { packageDeleted = true - zlog.Debug(ctx). - Str("package name", pkg.Name). - Str("package file", pkg.Filepath). - Str("whiteout file", f.Path). - Msg("package determined to be deleted") + slog.DebugContext(ctx, "package determined to be deleted", + "package name", pkg.Name, + "package file", pkg.Filepath, + "whiteout file", f.Path) } } if !packageDeleted { diff --git a/whiteout/resolver_test.go b/whiteout/resolver_test.go index 0212435ad..ccf9cfa62 100644 --- a/whiteout/resolver_test.go +++ b/whiteout/resolver_test.go @@ -1,14 +1,12 @@ package whiteout import ( - "context" "crypto/sha256" "io" "testing" "github.com/quay/claircore" - - "github.com/quay/zlog" + "github.com/quay/claircore/test" ) func Digest(name string) claircore.Digest { @@ -260,7 +258,7 @@ func TestResolver(t *testing.T) { r := &Resolver{} for _, tc := range tests { t.Run(tc.Name, func(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) report := r.Resolve(ctx, tc.Report, tc.Layers) if tc.LenPackage != len(report.Packages) { t.Fatalf("wrong number of packages: expected: %d got: %d", tc.LenPackage, len(report.Packages)) @@ -269,7 +267,6 @@ func TestResolver(t *testing.T) { t.Fatalf("wrong number of environments: expected: %d got: %d", tc.LenEnvs, len(report.Environments)) } }) - } } diff --git a/whiteout/scanner.go b/whiteout/scanner.go index a81a70091..bd5dce4a7 100644 --- a/whiteout/scanner.go +++ b/whiteout/scanner.go @@ -4,10 +4,9 @@ import ( "context" "fmt" "io/fs" + "log/slog" "strings" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/indexer" ) @@ -32,12 +31,8 @@ func (*Scanner) Version() string { return scannerVersion } func (*Scanner) Kind() string { return scannerKind } func (s *Scanner) Scan(ctx context.Context, l *claircore.Layer) ([]claircore.File, error) { - ctx = zlog.ContextWithValues(ctx, - "component", "whiteout/Scanner.Scan", - "version", s.Version(), - "layer", l.Hash.String()) - zlog.Debug(ctx).Msg("start") - defer zlog.Debug(ctx).Msg("done") + slog.DebugContext(ctx, "start") + defer slog.DebugContext(ctx, "done") sys, err := l.FS() if err != nil { return nil, fmt.Errorf("whiteout: unable to create fs: %w", err) diff --git a/whiteout/scanner_test.go b/whiteout/scanner_test.go index 754b8bab8..77a69f009 100644 --- a/whiteout/scanner_test.go +++ b/whiteout/scanner_test.go @@ -1,12 +1,9 @@ package whiteout import ( - "context" "os" "testing" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/test" ) @@ -17,7 +14,7 @@ var testScanner = &Scanner{} // layer in "name". func checkCount(t testing.TB, name string, ct int) { t.Helper() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) lf, err := os.Open(name) if err != nil { t.Fatal(err) From 32d0dff91d4db1606d06fda7de3ceb9453901a2a Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 9 Oct 2025 09:30:03 -0700 Subject: [PATCH 53/77] updater: migrate to slog Signed-off-by: Hank Donnay --- updater/offline_test.go | 10 +++--- updater/offline_v1.go | 49 +++++++++++--------------- updater/registry.go | 11 +++--- updater/updater.go | 76 +++++++++++++++++++---------------------- updater/updater_test.go | 9 +++-- 5 files changed, 68 insertions(+), 87 deletions(-) diff --git a/updater/offline_test.go b/updater/offline_test.go index 0d1ed83ee..effb69d20 100644 --- a/updater/offline_test.go +++ b/updater/offline_test.go @@ -3,7 +3,6 @@ package updater import ( "archive/zip" "bytes" - "context" "encoding/json" "io" "net/http" @@ -13,16 +12,15 @@ import ( "path/filepath" "testing" - "github.com/quay/zlog" "go.uber.org/mock/gomock" + "github.com/quay/claircore/test" mock_updater "github.com/quay/claircore/test/mock/updater" mock_driver "github.com/quay/claircore/test/mock/updater/driver/v1" driver "github.com/quay/claircore/updater/driver/v1" ) func TestOffline(t *testing.T) { - ctx := context.Background() vs := &driver.ParsedVulnerabilities{ Updater: t.Name(), Vulnerability: []driver.Vulnerability{{}}, @@ -40,7 +38,7 @@ func TestOffline(t *testing.T) { defer spool.Close() t.Run("Fetch", func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) ctl := gomock.NewController(t) n := path.Dir(t.Name()) @@ -76,7 +74,7 @@ func TestOffline(t *testing.T) { }) t.Run("Parse", func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) ctl := gomock.NewController(t) n := path.Dir(t.Name()) @@ -124,7 +122,7 @@ func TestOffline(t *testing.T) { }) t.Run("Prev", func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) ctl := gomock.NewController(t) n := path.Dir(t.Name()) if _, err := spool.Seek(0, io.SeekStart); err != nil { diff --git a/updater/offline_v1.go b/updater/offline_v1.go index 7f8d03a40..1305336bd 100644 --- a/updater/offline_v1.go +++ b/updater/offline_v1.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "io/fs" + "log/slog" "os" "path" "runtime" @@ -15,7 +16,6 @@ import ( "time" "github.com/google/uuid" - "github.com/quay/zlog" "golang.org/x/sync/errgroup" "github.com/quay/claircore/updater/driver/v1" @@ -41,7 +41,7 @@ func (u *Updater) importV1(ctx context.Context, sys fs.FS) error { } defer func() { if err := f.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("error closing config.json") + slog.WarnContext(ctx, "error closing config.json", "reason", err) } }() if err := json.NewDecoder(f).Decode(&cfg); err != nil { @@ -52,9 +52,7 @@ func (u *Updater) importV1(ctx context.Context, sys fs.FS) error { if err != nil { return err } - zlog.Info(ctx). - Int("ct", len(us)). - Msg("got updaters") + slog.InfoContext(ctx, "got updaters", "count", len(us)) spool, err := os.CreateTemp(tmpDir, tmpPattern) if err != nil { @@ -63,22 +61,20 @@ func (u *Updater) importV1(ctx context.Context, sys fs.FS) error { defer func() { spoolname := spool.Name() if err := os.Remove(spoolname); err != nil { - zlog.Warn(ctx).Str("filename", spoolname).Err(err).Msg("unable to remove spool file") + slog.WarnContext(ctx, "unable to remove spool file", "filename", spoolname, "reason", err) } if err := spool.Close(); err != nil { - zlog.Warn(ctx).Str("filename", spoolname).Err(err).Msg("error closing spool file") + slog.WarnContext(ctx, "error closing spool file", "filename", spoolname, "reason", err) } }() for _, upd := range us { name := upd.Name - ctx := zlog.ContextWithValues(ctx, "updater", name) fi, err := fs.Stat(sys, name) switch { case errors.Is(err, nil): case errors.Is(err, fs.ErrNotExist): - zlog.Info(ctx). - Msg("no import, skipping") + slog.InfoContext(ctx, "no import, skipping", "updater", name) continue default: return err @@ -128,13 +124,13 @@ func (u *Updater) importV1(ctx context.Context, sys fs.FS) error { if err := u.store.UpdateVulnerabilities(ctx, ref, name, fp, res.Vulnerabilities); err != nil { return err } - zlog.Info(ctx).Stringer("ref", ref).Msg("updated vulnerabilites") + slog.InfoContext(ctx, "updated vulnerabilites", "ref", ref) } if len(res.Enrichments) != 0 { if err := u.store.UpdateEnrichments(ctx, ref, name, fp, res.Enrichments); err != nil { return err } - zlog.Info(ctx).Stringer("ref", ref).Msg("updated enrichments") + slog.InfoContext(ctx, "updated enrichments", "ref", ref) } } @@ -161,9 +157,7 @@ func (u *Updater) exportV1(ctx context.Context, z *zip.Writer, prev fs.FS) error if err != nil { return err } - zlog.Info(ctx). - Int("ct", len(us)). - Msg("got updaters") + slog.InfoContext(ctx, "got updaters", "count", len(us)) // WaitGroup for the worker goroutines. var wg sync.WaitGroup @@ -218,7 +212,7 @@ func (u *Updater) exportV1(ctx context.Context, z *zip.Writer, prev fs.FS) error defer wg.Done() for upd := range feed { name := upd.Name - ctx := zlog.ContextWithValues(ctx, "updater", name) + log := slog.With("updater", name) // Zips have to be written serially, so we spool the fetcher // output to disk, then seek back to the start so it's ready to // read. @@ -229,29 +223,29 @@ func (u *Updater) exportV1(ctx context.Context, z *zip.Writer, prev fs.FS) error // can't do much to recover from. spool, err := os.CreateTemp(tmpDir, tmpPattern) if err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to create spool file") + log.WarnContext(ctx, "unable to create spool file", "reason", err) continue } spoolname := spool.Name() fp, err := u.fetchOne(ctx, upd, pfps[name], spool) if err != nil { if err := os.Remove(spoolname); err != nil { - zlog.Warn(ctx).Str("filename", spoolname).Err(err).Msg("unable to remove spool file") + log.WarnContext(ctx, "unable to remove spool file", "filename", spoolname, "reason", err) } if err := spool.Close(); err != nil { - zlog.Warn(ctx).Str("filename", spoolname).Err(err).Msg("error closing spool file") + log.WarnContext(ctx, "error closing spool file", "filename", spoolname, "reason", err) } - zlog.Info(ctx).Err(err).Msg("updater error") + log.InfoContext(ctx, "updater error", "reason", err) continue } if _, err := spool.Seek(0, io.SeekStart); err != nil { if err := os.Remove(spoolname); err != nil { - zlog.Warn(ctx).Str("filename", spoolname).Err(err).Msg("unable to remove spool file") + log.WarnContext(ctx, "unable to remove spool file", "filename", spoolname, "reason", err) } if err := spool.Close(); err != nil { - zlog.Warn(ctx).Str("filename", spoolname).Err(err).Msg("error closing spool file") + log.WarnContext(ctx, "error closing spool file", "filename", spoolname, "reason", err) } - zlog.Warn(ctx).Str("filename", spoolname).Err(err).Msg("unable to seek to start") + log.WarnContext(ctx, "unable to seek to start", "filename", spoolname, "reason", err) continue } res <- &result{ @@ -274,10 +268,10 @@ func addUpdater(ctx context.Context, z *zip.Writer, now time.Time, r *result) er defer func() { fn := r.spool.Name() if err := os.Remove(fn); err != nil { - zlog.Warn(ctx).Err(err).Str("file", fn).Msg("unable to remove fetch spool") + slog.WarnContext(ctx, "unable to remove fetch spool", "reason", err, "file", fn) } if err := r.spool.Close(); err != nil { - zlog.Warn(ctx).Err(err).Str("file", fn).Msg("unable to close fetch spool") + slog.WarnContext(ctx, "unable to close fetch spool", "reason", err, "file", fn) } }() n := r.name @@ -327,10 +321,7 @@ func addUpdater(ctx context.Context, z *zip.Writer, now time.Time, r *result) er if _, err := io.Copy(w, r.spool); err != nil { return err } - zlog.Debug(ctx). - Stringer("ref", ref). - Str("name", n). - Msg("wrote out fetch results") + slog.DebugContext(ctx, "wrote out fetch results", "ref", ref, "name", n) return nil } diff --git a/updater/registry.go b/updater/registry.go index 9578f0d29..dda500c29 100644 --- a/updater/registry.go +++ b/updater/registry.go @@ -3,13 +3,12 @@ package updater import ( "context" "errors" + "log/slog" "maps" "net/http" "strings" "sync" - "github.com/quay/zlog" - "github.com/quay/claircore/libvuln/driver" ) @@ -58,11 +57,10 @@ func Configure(ctx context.Context, fs map[string]driver.UpdaterSetFactory, cfg b.WriteString("updater: errors configuring factories:") for name, fac := range fs { - ev := zlog.Debug(ctx). - Str("factory", name) + msg := "factory unconfigurable" f, ok := fac.(driver.Configurable) if ok { - ev.Msg("configuring factory") + msg = "configuring factory" cf := cfg[name] if cf == nil { cf = noopConfig @@ -72,9 +70,8 @@ func Configure(ctx context.Context, fs map[string]driver.UpdaterSetFactory, cfg b.WriteString("\n\t") b.WriteString(err.Error()) } - } else { - ev.Msg("factory unconfigurable") } + slog.DebugContext(ctx, msg, "factory", name) } if errd { diff --git a/updater/updater.go b/updater/updater.go index 869156dc5..97d7722ef 100644 --- a/updater/updater.go +++ b/updater/updater.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "io/fs" + "log/slog" "net/http" "os" "runtime" @@ -16,9 +17,9 @@ import ( "sync" "github.com/google/uuid" - "github.com/quay/zlog" "golang.org/x/sync/errgroup" + "github.com/quay/claircore/toolkit/log" driver "github.com/quay/claircore/updater/driver/v1" ) @@ -54,15 +55,15 @@ func New(ctx context.Context, opts *Options) (*Updater, error) { } if opts.Locker == nil { - zlog.Warn(ctx).Msg("no locker passed, using process-local locking") + slog.WarnContext(ctx, "no locker passed, using process-local locking") u.locker = newLocalLocker() } if opts.Configs == nil { - zlog.Info(ctx).Msg("no updater configuration passed") + slog.InfoContext(ctx, "no updater configuration passed") u.configs = make(driver.Configs) } if opts.Factories == nil { - zlog.Warn(ctx).Msg("no updater factories provided, this may be a misconfiguration") + slog.WarnContext(ctx, "no updater factories provided, this may be a misconfiguration") } _, file, line, _ := runtime.Caller(1) @@ -171,16 +172,16 @@ func (u *Updater) Run(ctx context.Context, strict bool) error { defer wg.Done() spool, err := os.CreateTemp(tmpDir, tmpPattern) if err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to create spool file") + slog.WarnContext(ctx, "unable to create spool file", "reason", err) return err } spoolname := spool.Name() defer func() { if err := os.Remove(spoolname); err != nil { - zlog.Warn(ctx).Str("filename", spoolname).Err(err).Msg("unable to remove spool file") + slog.WarnContext(ctx, "unable to remove spool file", "filename", spoolname, "reason", err) } if err := spool.Close(); err != nil { - zlog.Warn(ctx).Str("filename", spoolname).Err(err).Msg("error closing spool file") + slog.WarnContext(ctx, "error closing spool file", "filename", spoolname, "reason", err) } }() var updErr *updaterError @@ -189,7 +190,7 @@ func (u *Updater) Run(ctx context.Context, strict bool) error { switch { case errors.Is(err, nil): case errors.As(err, &updErr): - zlog.Debug(ctx).Err(updErr).Msg("updater error") + slog.DebugContext(ctx, "updater error", "reason", updErr) errCh <- updErr.Unwrap() default: return err @@ -206,7 +207,7 @@ func (u *Updater) Run(ctx context.Context, strict bool) error { if strict { return errors.Join(errs...) } - zlog.Info(ctx).Errs("errors", errs).Msg("updater errors") + slog.InfoContext(ctx, "updater errors", "errors", errs) } return nil } @@ -229,7 +230,7 @@ func (u *Updater) updaters(ctx context.Context, cfg driver.Configs) ([]taggedUpd set, err = fac.Create(ctx, cfg[key]) }) if err != nil { - zlog.Info(ctx).Err(err).Msg("factory errored") + slog.InfoContext(ctx, "factory errored", "reason", err) continue } for _, upd := range set { @@ -238,11 +239,11 @@ func (u *Updater) updaters(ctx context.Context, cfg driver.Configs) ([]taggedUpd name = upd.Name() }) if strings.Contains(name, "/") { - zlog.Info(ctx).Str("updater", name).Msg("name contains invalid character: /") + slog.InfoContext(ctx, "name contains invalid character: /", "updater", name) continue } if _, ok := dedup[name]; ok { - zlog.Info(ctx).Str("updater", name).Msg("updater already exists") + slog.InfoContext(ctx, "updater already exists", "updater", name) continue } dedup[name] = struct{}{} @@ -258,17 +259,16 @@ func (u *Updater) updaters(ctx context.Context, cfg driver.Configs) ([]taggedUpd func (u *Updater) fetchOne(ctx context.Context, tu taggedUpdater, pfp driver.Fingerprint, out io.Writer) (fp driver.Fingerprint, err error) { name := tu.Name - ctx = zlog.ContextWithValues(ctx, "updater", name) - zlog.Info(ctx).Msg("fetch start") - defer zlog.Info(ctx).Msg("fetch done") + slog.InfoContext(ctx, "fetch start") + defer slog.InfoContext(ctx, "fetch done") lctx, done := u.locker.TryLock(ctx, name) defer done() if err := lctx.Err(); err != nil { if pErr := ctx.Err(); pErr != nil { - zlog.Debug(ctx).Err(err).Msg("parent context canceled") + slog.DebugContext(ctx, "parent context canceled", "reason", err) return fp, nil } - zlog.Info(ctx).Err(err).Msg("lock acquisition failed, skipping") + slog.InfoContext(ctx, "lock acquisition failed, skipping", "reason", err) return fp, err } ctx = lctx @@ -276,11 +276,11 @@ func (u *Updater) fetchOne(ctx context.Context, tu taggedUpdater, pfp driver.Fin zw := zip.NewWriter(out) defer func() { if err := zw.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close zip writer") + slog.WarnContext(ctx, "unable to close zip writer", "reason", err) } }() if len(pfp) != 0 { - zlog.Debug(ctx).Str("fingerprint", string(pfp)).Msg("found previous fingerprint") + slog.DebugContext(ctx, "found previous fingerprint", "fingerprint", string(pfp)) } pprof.Do(ctx, pprof.Labels("task", "updater_fetch", "updater", name), func(ctx context.Context) { fp, err = tu.Updater.Fetch(ctx, zw, pfp, u.client) @@ -295,36 +295,33 @@ func (u *Updater) parseOne(ctx context.Context, tu taggedUpdater, in fs.FS) (*pa err error ) name := tu.Name - ctx = zlog.ContextWithValues(ctx, "updater", name) - zlog.Info(ctx).Msg("parse start") - defer zlog.Info(ctx).Msg("parse done") + ctx = log.With(ctx, "updater", name) + slog.InfoContext(ctx, "parse start") + defer slog.InfoContext(ctx, "parse done") pprof.Do(ctx, pprof.Labels("task", "updater_parse", "updater", name), func(ctx context.Context) { - ctx = zlog.ContextWithValues(ctx, "updater", name) upd := tu.Updater if p, ok := upd.(driver.VulnerabilityParser); ok { - zlog.Debug(ctx).Msg("implements VulnerabilityParser") + slog.DebugContext(ctx, "implements VulnerabilityParser") any = true res.Vulnerabilities, err = p.ParseVulnerability(ctx, in) if err != nil { return } - zlog.Debug(ctx). - Err(err). - Int("ct", len(res.Vulnerabilities.Vulnerability)). - Msg("found vulnerabilities") + slog.DebugContext(ctx, "found vulnerabilities", + "reason", err, + "ct", len(res.Vulnerabilities.Vulnerability)) } if p, ok := upd.(driver.EnrichmentParser); ok { - zlog.Debug(ctx).Msg("implements EnrichmentParser") + slog.DebugContext(ctx, "implements EnrichmentParser") any = true res.Enrichments, err = p.ParseEnrichment(ctx, in) if err != nil { return } - zlog.Debug(ctx). - Err(err). - Int("ct", len(res.Enrichments)). - Msg("found enrichments") + slog.DebugContext(ctx, "found enrichments", + "reason", err, + "ct", len(res.Enrichments)) } }) if !any { @@ -341,28 +338,27 @@ type parseResult struct { func (u *Updater) fetchAndParse(ctx context.Context, spool *os.File, pfps map[string]driver.Fingerprint, tu taggedUpdater) error { spoolname := spool.Name() name := tu.Name - ctx = zlog.ContextWithValues(ctx, "updater", name) if _, err := spool.Seek(0, io.SeekStart); err != nil { - zlog.Error(ctx).Str("filename", spoolname).Err(err).Msg("unable to seek to start") + slog.ErrorContext(ctx, "unable to seek to start", "filename", spoolname, "reason", err) return err } fp, err := u.fetchOne(ctx, tu, pfps[name], spool) switch { case errors.Is(err, nil): case errors.Is(err, driver.ErrUnchanged): - zlog.Debug(ctx).Msg("unchanged") + slog.DebugContext(ctx, "unchanged") return nil default: return updaterErr(err) } sz, err := spool.Seek(0, io.SeekCurrent) if err != nil { - zlog.Error(ctx).Str("filename", spoolname).Err(err).Msg("unable to seek spoolfile") + slog.ErrorContext(ctx, "unable to seek spoolfile", "filename", spoolname, "reason", err) return err } z, err := zip.NewReader(spool, sz) if err != nil { - zlog.Error(ctx).Str("filename", spoolname).Err(err).Msg("unable to create zip reader") + slog.ErrorContext(ctx, "unable to create zip reader", "filename", spoolname, "reason", err) return err } res, err := u.parseOne(ctx, tu, z) @@ -377,14 +373,14 @@ func (u *Updater) fetchAndParse(ctx context.Context, spool *os.File, pfps map[st if err != nil { return } - zlog.Info(ctx).Stringer("ref", ref).Msg("updated vulnerabilites") + slog.InfoContext(ctx, "updated vulnerabilites", "ref", ref) } if len(res.Enrichments) != 0 { err = u.store.UpdateEnrichments(ctx, ref, name, fp, res.Enrichments) if err != nil { return } - zlog.Info(ctx).Stringer("ref", ref).Msg("updated enrichments") + slog.InfoContext(ctx, "updated enrichments", "ref", ref) } }) if err != nil { diff --git a/updater/updater_test.go b/updater/updater_test.go index 44e4a57cb..028a055c6 100644 --- a/updater/updater_test.go +++ b/updater/updater_test.go @@ -15,18 +15,17 @@ import ( "testing" "github.com/google/uuid" - "github.com/quay/zlog" "go.uber.org/mock/gomock" + "github.com/quay/claircore/test" mock_updater "github.com/quay/claircore/test/mock/updater" mock_driver "github.com/quay/claircore/test/mock/updater/driver/v1" "github.com/quay/claircore/updater/driver/v1" ) func TestNew(t *testing.T) { - ctx := zlog.Test(context.Background(), t) t.Run("MissingStore", func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) u, err := New(ctx, &Options{ Client: &http.Client{}, }) @@ -39,7 +38,7 @@ func TestNew(t *testing.T) { } }) t.Run("MissingClient", func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) u, err := New(ctx, &Options{ Store: mock_updater.NewMockStore(nil), }) @@ -132,7 +131,7 @@ func parseEnrich(_ context.Context, in fs.FS) (r []driver.EnrichmentRecord, err } func TestRun(t *testing.T) { - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) ctl := gomock.NewController(t) n := path.Base(t.Name()) vs := &driver.ParsedVulnerabilities{ From c5365b5a5cb0a7b3b02f40a4cbc731da8b8fc98a Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 9 Oct 2025 09:51:27 -0700 Subject: [PATCH 54/77] enricher/cvss: migrate to slog Signed-off-by: Hank Donnay --- enricher/cvss/cvss.go | 48 +++++++++++--------------------------- enricher/cvss/cvss_test.go | 16 ++++++------- enricher/cvss/feed.go | 13 ++++------- enricher/cvss/feed_test.go | 4 ++-- 4 files changed, 28 insertions(+), 53 deletions(-) diff --git a/enricher/cvss/cvss.go b/enricher/cvss/cvss.go index 5f032f1e6..d273c0d90 100644 --- a/enricher/cvss/cvss.go +++ b/enricher/cvss/cvss.go @@ -9,14 +9,13 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "net/url" "sort" "strings" "time" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/enricher" "github.com/quay/claircore/libvuln/driver" @@ -110,8 +109,6 @@ func (*Enricher) Name() string { return name } // FetchEnrichment implements driver.EnrichmentUpdater. func (e *Enricher) FetchEnrichment(ctx context.Context, hint driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/cvss/Enricher/FetchEnrichment") - // year → sha256 prev := make(map[int]string) if err := json.Unmarshal([]byte(hint), &prev); err != nil && hint != "" { @@ -126,10 +123,7 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, hint driver.Fingerprint) if err != nil { return nil, hint, err } - zlog.Debug(ctx). - Int("year", y). - Stringer("url", u). - Msg("fetching meta file") + slog.DebugContext(ctx, "fetching meta file", "year", y, "url", u) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) if err != nil { return nil, hint, err @@ -148,20 +142,17 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, hint driver.Fingerprint) if err := mf.Parse(&buf); err != nil { return nil, hint, err } - zlog.Debug(ctx). - Int("year", y). - Stringer("url", u). - Time("mod", mf.LastModified). - Msg("parsed meta file") + slog.DebugContext(ctx, "parsed meta file", + "year", y, + "url", u, + "mod", mf.LastModified) cur[y] = strings.ToUpper(mf.SHA256) } doFetch := false for _, y := range yrs { if prev[y] != cur[y] { - zlog.Info(ctx). - Int("year", y). - Msg("change detected") + slog.InfoContext(ctx, "change detected", "year", y) doFetch = true break } @@ -178,7 +169,7 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, hint driver.Fingerprint) defer func() { if !success { if err := out.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close spool") + slog.WarnContext(ctx, "unable to close spool", "reason", err) } } }() @@ -195,10 +186,7 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, hint driver.Fingerprint) if err != nil { return nil, hint, fmt.Errorf("unable to create request: %w", err) } - zlog.Debug(ctx). - Int("year", y). - Stringer("url", u). - Msg("requesting json") + slog.DebugContext(ctx, "requesting json", "year", y, "url", u) res, err := e.c.Do(req) if err != nil { return nil, hint, fmt.Errorf("unable to do request: %w", err) @@ -232,7 +220,6 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, hint driver.Fingerprint) // ParseEnrichment implements driver.EnrichmentUpdater. func (e *Enricher) ParseEnrichment(ctx context.Context, rc io.ReadCloser) ([]driver.EnrichmentRecord, error) { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/cvss/Enricher/ParseEnrichment") // Our Fetch method actually has all the smarts w/r/t to constructing the // records, so this is just decoding in a loop. defer rc.Close() @@ -244,9 +231,7 @@ func (e *Enricher) ParseEnrichment(ctx context.Context, rc io.ReadCloser) ([]dri ret = append(ret, driver.EnrichmentRecord{}) err = dec.Decode(&ret[len(ret)-1]) } - zlog.Debug(ctx). - Int("count", len(ret)). - Msg("decoded enrichments") + slog.DebugContext(ctx, "decoded enrichments", "count", len(ret)) if err != nil && !errors.Is(err, io.EOF) { return nil, err } @@ -255,8 +240,6 @@ func (e *Enricher) ParseEnrichment(ctx context.Context, rc io.ReadCloser) ([]dri // Enrich implements driver.Enricher. func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *claircore.VulnerabilityReport) (string, []json.RawMessage, error) { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/cvss/Enricher/Enrich") - // We return any CVSS blobs for CVEs mentioned in the free-form parts of the // vulnerability. m := make(map[string][]json.RawMessage) @@ -264,8 +247,7 @@ func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *cla erCache := make(map[string][]driver.EnrichmentRecord) for id, v := range r.Vulnerabilities { t := make(map[string]struct{}) - ctx := zlog.ContextWithValues(ctx, - "vuln", v.Name) + log := slog.With("vuln", v.Name) for _, elem := range []string{ v.Name, v.Links, @@ -281,9 +263,7 @@ func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *cla for m := range t { ts = append(ts, m) } - zlog.Debug(ctx). - Strs("cve", ts). - Msg("found CVEs") + log.DebugContext(ctx, "found CVEs", "cve", ts) sort.Strings(ts) cveKey := strings.Join(ts, "_") @@ -296,9 +276,7 @@ func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *cla } erCache[cveKey] = rec } - zlog.Debug(ctx). - Int("count", len(rec)). - Msg("found records") + log.DebugContext(ctx, "found records", "count", len(rec)) for _, r := range rec { m[id] = append(m[id], r.Enrichment) } diff --git a/enricher/cvss/cvss_test.go b/enricher/cvss/cvss_test.go index 47d2d8b10..e22bcb7fd 100644 --- a/enricher/cvss/cvss_test.go +++ b/enricher/cvss/cvss_test.go @@ -17,15 +17,15 @@ import ( "time" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" + "github.com/quay/claircore/test" ) func TestConfigure(t *testing.T) { t.Parallel() - ctx := zlog.Test(t.Context(), t) + ctx := test.Logging(t) tt := []configTestcase{ { Name: "None", @@ -91,7 +91,7 @@ type configTestcase struct { func (tc configTestcase) Run(ctx context.Context) func(*testing.T) { e := &Enricher{} return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) f := tc.Config if f == nil { f = noopConfig @@ -111,7 +111,7 @@ func noopConfig(_ any) error { return nil } func TestFetch(t *testing.T) { t.Parallel() - ctx := zlog.Test(t.Context(), t) + ctx := test.Logging(t) srv := mockServer(t) tt := []fetchTestcase{ { @@ -175,7 +175,7 @@ type fetchTestcase struct { func (tc fetchTestcase) Run(ctx context.Context, srv *httptest.Server) func(*testing.T) { e := &Enricher{} return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) f := func(i any) error { cfg, ok := i.(*Config) if !ok { @@ -234,7 +234,7 @@ func mockServer(t *testing.T) *httptest.Server { func TestParse(t *testing.T) { t.Parallel() - ctx := zlog.Test(t.Context(), t) + ctx := test.Logging(t) srv := mockServer(t) tt := []parseTestcase{ { @@ -254,7 +254,7 @@ type parseTestcase struct { func (tc parseTestcase) Run(ctx context.Context, srv *httptest.Server) func(*testing.T) { e := &Enricher{} return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) f := func(i any) error { cfg, ok := i.(*Config) if !ok { @@ -285,7 +285,7 @@ func (tc parseTestcase) Run(ctx context.Context, srv *httptest.Server) func(*tes func TestEnrich(t *testing.T) { t.Parallel() - ctx := zlog.Test(t.Context(), t) + ctx := test.Logging(t) feedIn, err := os.Open("testdata/feed.json") if err != nil { t.Fatal(err) diff --git a/enricher/cvss/feed.go b/enricher/cvss/feed.go index 5f61c870a..5a959fdbf 100644 --- a/enricher/cvss/feed.go +++ b/enricher/cvss/feed.go @@ -4,10 +4,9 @@ import ( "context" "encoding/json" "io" + "log/slog" "strings" - "github.com/quay/zlog" - "github.com/quay/claircore/libvuln/driver" ) @@ -59,7 +58,6 @@ func newItemFeed(year int, r io.Reader) (*itemFeed, error) { // entire serialization in memory. func (f *itemFeed) WriteCVSS(ctx context.Context, w io.Writer) error { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/cvss/itemFeed/WriteCVSS") // Use records directly because our parse step doesn't actually parse // anything -- the Fetch step rips out the relevant JSON. var skip, wrote uint @@ -104,10 +102,9 @@ func (f *itemFeed) WriteCVSS(ctx context.Context, w io.Writer) error { } wrote++ } - zlog.Debug(ctx). - Int("year", f.year). - Uint("skip", skip). - Uint("wrote", wrote). - Msg("wrote cvss items") + slog.DebugContext(ctx, "wrote cvss items", + "year", f.year, + "skip", skip, + "wrote", wrote) return nil } diff --git a/enricher/cvss/feed_test.go b/enricher/cvss/feed_test.go index cea9bc562..824c5b971 100644 --- a/enricher/cvss/feed_test.go +++ b/enricher/cvss/feed_test.go @@ -5,11 +5,11 @@ import ( "os" "testing" - "github.com/quay/zlog" + "github.com/quay/claircore/test" ) func TestFeedIngest(t *testing.T) { - ctx := zlog.Test(t.Context(), t) + ctx := test.Logging(t) in, err := os.Open("testdata/feed.json") if err != nil { t.Fatal(err) From 71986cc82213a9e718f40be6fc347e813f075fda Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 9 Oct 2025 09:59:44 -0700 Subject: [PATCH 55/77] enricher/epss: migrate to slog Signed-off-by: Hank Donnay --- enricher/epss/epss.go | 35 +++++++++++++---------------------- enricher/epss/epss_test.go | 16 ++++++++-------- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/enricher/epss/epss.go b/enricher/epss/epss.go index 8bbabd23c..6e32c84f5 100644 --- a/enricher/epss/epss.go +++ b/enricher/epss/epss.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "net/url" "path" @@ -17,8 +18,6 @@ import ( "strings" "time" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/enricher" "github.com/quay/claircore/internal/httputil" @@ -76,7 +75,6 @@ func NewFactory() driver.UpdaterSetFactory { } func (e *Enricher) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c *http.Client) error { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/epss/Enricher/Configure") var cfg Config e.c = c e.feedPath = currentFeedURL() @@ -94,7 +92,7 @@ func (e *Enricher) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c // only .gz file is supported if strings.HasSuffix(*cfg.URL, ".gz") { - //overwrite feedPath is cfg provides another baseURL path + // overwrite feedPath is cfg provides another baseURL path e.feedPath = *cfg.URL } else { return fmt.Errorf("invalid baseURL root: expected a '.gz' file, but got '%q'", *cfg.URL) @@ -106,8 +104,6 @@ func (e *Enricher) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c // FetchEnrichment implements driver.EnrichmentUpdater. func (e *Enricher) FetchEnrichment(ctx context.Context, prevFingerprint driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/epss/Enricher/FetchEnrichment") - out, err := tmp.NewFile("", "epss.") if err != nil { return nil, "", err @@ -116,7 +112,7 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, prevFingerprint driver.F defer func() { if !success { if err := out.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close spool") + slog.WarnContext(ctx, "unable to close spool", "reason", err) } } }() @@ -140,7 +136,7 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, prevFingerprint driver.F if etag := resp.Header.Get("etag"); etag != "" { newFingerprint = driver.Fingerprint(etag) if prevFingerprint == newFingerprint { - zlog.Info(ctx).Str("fingerprint", string(newFingerprint)).Msg("file unchanged; skipping processing") + slog.InfoContext(ctx, "file unchanged; skipping processing", "fingerprint", string(newFingerprint)) return nil, prevFingerprint, nil } newFingerprint = driver.Fingerprint(etag) @@ -207,7 +203,7 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, prevFingerprint driver.F r, err := newItemFeed(record, modelVersion, date) if err != nil { - zlog.Warn(ctx).Err(err).Msg("skipping invalid record") + slog.WarnContext(ctx, "skipping invalid record", "reason", err) continue } @@ -217,7 +213,7 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, prevFingerprint driver.F totalCVEs++ } - zlog.Info(ctx).Int("totalCVEs", totalCVEs).Msg("processed CVEs") + slog.InfoContext(ctx, "processed CVEs", "totalCVEs", totalCVEs) if _, err := out.Seek(0, io.SeekStart); err != nil { return nil, newFingerprint, fmt.Errorf("unable to reset file pointer: %w", err) } @@ -228,8 +224,6 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, prevFingerprint driver.F // ParseEnrichment implements driver.EnrichmentUpdater. func (e *Enricher) ParseEnrichment(ctx context.Context, rc io.ReadCloser) ([]driver.EnrichmentRecord, error) { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/epss/Enricher/ParseEnrichment") - defer rc.Close() dec := json.NewDecoder(rc) @@ -244,9 +238,7 @@ func (e *Enricher) ParseEnrichment(ctx context.Context, rc io.ReadCloser) ([]dri ret = append(ret, record) } - zlog.Debug(ctx). - Int("count", len(ret)). - Msg("decoded enrichments") + slog.DebugContext(ctx, "decoded enrichments", "count", len(ret)) if !errors.Is(err, io.EOF) { return nil, fmt.Errorf("error decoding enrichment records: %w", err) @@ -274,13 +266,12 @@ func currentFeedURL() string { } func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *claircore.VulnerabilityReport) (string, []json.RawMessage, error) { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/epss/Enricher/Enrich") m := make(map[string][]json.RawMessage) erCache := make(map[string][]driver.EnrichmentRecord) for id, v := range r.Vulnerabilities { t := make(map[string]struct{}) - ctx := zlog.ContextWithValues(ctx, "vuln", v.Name) + log := slog.With("vuln", v.Name) for _, elem := range []string{ v.Name, @@ -288,13 +279,13 @@ func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *cla } { // Check if the element is non-empty before running the regex if elem == "" { - zlog.Debug(ctx).Str("element", elem).Msg("skipping empty element") + log.DebugContext(ctx, "skipping empty element", "element", elem) continue } matches := enricher.CVERegexp.FindAllString(elem, -1) if len(matches) == 0 { - zlog.Debug(ctx).Str("element", elem).Msg("no CVEs found in element") + log.DebugContext(ctx, "no CVEs found in element", "element", elem) continue } for _, m := range matches { @@ -304,7 +295,7 @@ func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *cla // Skip if no CVEs were found if len(t) == 0 { - zlog.Debug(ctx).Msg("no CVEs found in vulnerability metadata") + log.DebugContext(ctx, "no CVEs found in vulnerability metadata") continue } @@ -326,11 +317,11 @@ func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *cla erCache[cveKey] = rec } - zlog.Debug(ctx).Int("count", len(rec)).Msg("found records") + log.DebugContext(ctx, "found records", "count", len(rec)) // Skip if no enrichment records are found if len(rec) == 0 { - zlog.Debug(ctx).Strs("cve", ts).Msg("no enrichment records found for CVEs") + log.DebugContext(ctx, "no enrichment records found for CVEs", "cve", ts) continue } diff --git a/enricher/epss/epss_test.go b/enricher/epss/epss_test.go index d10810e58..d5d279827 100644 --- a/enricher/epss/epss_test.go +++ b/enricher/epss/epss_test.go @@ -16,15 +16,15 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" + "github.com/quay/claircore/test" ) func TestConfigure(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) tt := []configTestcase{ { Name: "None", // No configuration provided, should use default @@ -96,7 +96,7 @@ func TestConfigure(t *testing.T) { func (tc configTestcase) Run(ctx context.Context) func(*testing.T) { e := &Enricher{} return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) f := tc.Config if f == nil { f = noopConfig @@ -114,7 +114,6 @@ func (tc configTestcase) Run(ctx context.Context) func(*testing.T) { func TestFetch(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) srv := mockServer(t) tt := []fetchTestcase{ @@ -143,6 +142,7 @@ func TestFetch(t *testing.T) { }, } + ctx := context.Background() for _, tc := range tt { t.Run(tc.Name, tc.Run(ctx, srv)) } @@ -201,7 +201,7 @@ func mockServer(t *testing.T) *httptest.Server { func (tc fetchTestcase) Run(ctx context.Context, srv *httptest.Server) func(*testing.T) { return func(t *testing.T) { e := &Enricher{} - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) configFunc := func(i any) error { cfg, ok := i.(*Config) if !ok { @@ -233,7 +233,7 @@ func (tc fetchTestcase) Run(ctx context.Context, srv *httptest.Server) func(*tes func TestParse(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) srv := mockServer(t) tt := []parseTestcase{ { @@ -253,7 +253,7 @@ type parseTestcase struct { func (tc parseTestcase) Run(ctx context.Context, srv *httptest.Server) func(*testing.T) { e := &Enricher{} return func(t *testing.T) { - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t, ctx) f := func(i any) error { cfg, ok := i.(*Config) if !ok { @@ -302,7 +302,7 @@ func (g *fakeGetter) GetEnrichment(ctx context.Context, cves []string) ([]driver func TestEnrich(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) srv := mockServer(t) e := &Enricher{} f := func(i any) error { From 616f8b4b2512c1bfbaf0efc4002b830cdc447d0b Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 9 Oct 2025 10:09:36 -0700 Subject: [PATCH 56/77] enricher/kev: migrate to slog Signed-off-by: Hank Donnay --- enricher/kev/kev.go | 31 +++++++++++-------------------- enricher/kev/kev_test.go | 12 +++++------- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/enricher/kev/kev.go b/enricher/kev/kev.go index d7d4bce74..e8682b180 100644 --- a/enricher/kev/kev.go +++ b/enricher/kev/kev.go @@ -7,13 +7,12 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "net/url" "slices" "strings" - "github.com/quay/zlog" - "github.com/quay/claircore" "github.com/quay/claircore/enricher" "github.com/quay/claircore/libvuln/driver" @@ -91,8 +90,6 @@ func (*Enricher) Name() string { return name } // FetchEnrichment implements driver.EnrichmentUpdater. func (e *Enricher) FetchEnrichment(ctx context.Context, hint driver.Fingerprint) (io.ReadCloser, driver.Fingerprint, error) { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/kev/Enricher.FetchEnrichment") - req, err := http.NewRequestWithContext(ctx, http.MethodGet, e.feed.String(), nil) if err != nil { return nil, hint, fmt.Errorf("kev: unable to create request: %w", err) @@ -116,12 +113,12 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, hint driver.Fingerprint) } fallthrough case http.StatusNotModified: - zlog.Info(ctx).Msg("database unchanged since last fetch") + slog.InfoContext(ctx, "database unchanged since last fetch") return nil, hint, driver.Unchanged default: return nil, hint, fmt.Errorf("http response error: %s %d", res.Status, res.StatusCode) } - zlog.Debug(ctx).Msg("successfully requested database") + slog.DebugContext(ctx, "successfully requested database") out, err := tmp.NewFile("", "kev.") if err != nil { @@ -131,7 +128,7 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, hint driver.Fingerprint) defer func() { if !success { if err := out.Close(); err != nil { - zlog.Warn(ctx).Err(err).Msg("unable to close spool") + slog.WarnContext(ctx, "unable to close spool", "reason", err) } } }() @@ -149,17 +146,13 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, hint driver.Fingerprint) success = true hint = driver.Fingerprint(res.Header.Get("Last-Modified")) - zlog.Debug(ctx). - Str("hint", string(hint)). - Msg("using new hint") + slog.DebugContext(ctx, "using new hint", "hint", string(hint)) return out, hint, nil } // ParseEnrichment implements driver.EnrichmentUpdater. func (e *Enricher) ParseEnrichment(ctx context.Context, rc io.ReadCloser) ([]driver.EnrichmentRecord, error) { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/kev/Enricher.ParseEnrichment") - var root Root buf := bufio.NewReader(rc) if err := json.NewDecoder(buf).Decode(&root); err != nil { @@ -196,14 +189,12 @@ func (e *Enricher) ParseEnrichment(ctx context.Context, rc io.ReadCloser) ([]dri // Enrich implements driver.Enricher. func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *claircore.VulnerabilityReport) (string, []json.RawMessage, error) { - ctx = zlog.ContextWithValues(ctx, "component", "enricher/kev/Enricher.Enrich") - m := make(map[string][]json.RawMessage) erCache := make(map[string][]driver.EnrichmentRecord) for id, v := range r.Vulnerabilities { t := make(map[string]struct{}) - ctx := zlog.ContextWithValues(ctx, "vuln", v.Name) + log := slog.With("vuln", v.Name) for _, elem := range []string{ v.Name, @@ -211,13 +202,13 @@ func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *cla } { // Check if the element is non-empty before running the regex if elem == "" { - zlog.Debug(ctx).Str("element", elem).Msg("skipping empty element") + log.DebugContext(ctx, "skipping empty element", "element", elem) continue } matches := enricher.CVERegexp.FindAllString(elem, -1) if len(matches) == 0 { - zlog.Debug(ctx).Str("element", elem).Msg("no CVEs found in element") + log.DebugContext(ctx, "no CVEs found in element", "element", elem) continue } for _, m := range matches { @@ -227,7 +218,7 @@ func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *cla // Skip if no CVEs were found if len(t) == 0 { - zlog.Debug(ctx).Msg("no CVEs found in vulnerability metadata") + log.DebugContext(ctx, "no CVEs found in vulnerability metadata") continue } @@ -249,11 +240,11 @@ func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *cla erCache[cveKey] = rec } - zlog.Debug(ctx).Int("count", len(rec)).Msg("found records") + log.DebugContext(ctx, "found records", "count", len(rec)) // Skip if no enrichment records are found if len(rec) == 0 { - zlog.Debug(ctx).Strs("cve", ts).Msg("no enrichment records found for CVEs") + log.DebugContext(ctx, "no enrichment records found for CVEs", "cve", ts) continue } diff --git a/enricher/kev/kev_test.go b/enricher/kev/kev_test.go index 7b3f5a5a9..9ae6e30b1 100644 --- a/enricher/kev/kev_test.go +++ b/enricher/kev/kev_test.go @@ -15,10 +15,10 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" + "github.com/quay/claircore/test" ) // Define a static Last-Modified for testing purposes @@ -28,7 +28,6 @@ func noopConfig(_ any) error { return nil } func TestConfigure(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) type configTestcase struct { Config func(any) error @@ -87,7 +86,7 @@ func TestConfigure(t *testing.T) { for _, tc := range tt { t.Run(tc.Name, func(t *testing.T) { e := &Enricher{} - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) f := tc.Config if f == nil { f = noopConfig @@ -156,7 +155,6 @@ func mockServer(t *testing.T) *httptest.Server { func TestFetch(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) srv := mockServer(t) type fetchTestcase struct { @@ -218,7 +216,7 @@ func TestFetch(t *testing.T) { for _, tc := range tt { t.Run(tc.Name, func(t *testing.T) { e := &Enricher{} - ctx := zlog.Test(ctx, t) + ctx := test.Logging(t) configFunc := func(i any) error { cfg, ok := i.(*Config) if !ok { @@ -251,7 +249,7 @@ func TestFetch(t *testing.T) { func TestParse(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) srv := mockServer(t) e := &Enricher{} @@ -336,7 +334,7 @@ func (g fakeGetter) GetEnrichment(_ context.Context, cves []string) ([]driver.En func TestEnrich(t *testing.T) { t.Parallel() - ctx := zlog.Test(context.Background(), t) + ctx := test.Logging(t) srv := mockServer(t) e := &Enricher{} From abae1b450474224e6bdde6c3fb0e55fc1928fb51 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 5 Jan 2026 11:47:53 -0600 Subject: [PATCH 57/77] sbom/spdx: migrate to slog Signed-off-by: Hank Donnay --- sbom/spdx/encoder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sbom/spdx/encoder.go b/sbom/spdx/encoder.go index a5134ac76..e2ba77ebf 100644 --- a/sbom/spdx/encoder.go +++ b/sbom/spdx/encoder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "log/slog" "runtime/debug" "slices" "strconv" @@ -18,7 +19,6 @@ import ( "github.com/quay/claircore" "github.com/quay/claircore/purl" "github.com/quay/claircore/sbom" - "github.com/quay/zlog" ) // Version describes the SPDX version to target. @@ -209,7 +209,7 @@ func (e *Encoder) parseIndexReport(ctx context.Context, ir *claircore.IndexRepor purl, err := e.PURLConverter.Generate(ctx, r) switch { case err != nil: - zlog.Warn(ctx).Err(err).Msg("failed to generate PURL") + slog.WarnContext(ctx, "failed to generate PURL", "reason", err) default: pkg.PackageExternalReferences = append(pkg.PackageExternalReferences, &v2_3.PackageExternalReference{ Category: "PACKAGE_MANAGER", From 28be6dc26ff5ef0ece18af6a0083980ff72e2240 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Thu, 9 Oct 2025 15:54:38 -0700 Subject: [PATCH 58/77] test: remove zlog Signed-off-by: Hank Donnay --- test/logging.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/logging.go b/test/logging.go index 670a33223..50c8bacbe 100644 --- a/test/logging.go +++ b/test/logging.go @@ -13,7 +13,6 @@ import ( "time" "github.com/quay/claircore/toolkit/log" - "github.com/quay/zlog" ) var ( @@ -118,7 +117,6 @@ func Logging(t testing.TB, parent ...context.Context) context.Context { // they want. ctx = context.Background() } - ctx = zlog.Test(ctx, t) start := time.Now() w := logOutput(t) h := slog.NewTextHandler(w, &slog.HandlerOptions{ From 47d1ecea962f1223e416b8e9582ba1084abaa23d Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Fri, 31 Oct 2025 09:51:45 -0500 Subject: [PATCH 59/77] go.mod: remove zlog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🫡 Signed-off-by: Hank Donnay --- go.mod | 3 --- go.sum | 14 -------------- 2 files changed, 17 deletions(-) diff --git a/go.mod b/go.mod index 6c2216b25..95b6258ea 100644 --- a/go.mod +++ b/go.mod @@ -20,9 +20,7 @@ require ( github.com/quay/claircore/toolkit v1.3.0 github.com/quay/claircore/updater/driver v1.0.0 github.com/quay/goval-parser v0.8.8 - github.com/quay/zlog v1.1.9 github.com/remind101/migrate v0.0.0-20170729031349-52c1edff7319 - github.com/rs/zerolog v1.32.0 github.com/spdx/tools-golang v0.5.6 github.com/ulikunitz/xz v0.5.15 go.opentelemetry.io/otel v1.39.0 @@ -53,7 +51,6 @@ require ( github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lib/pq v1.10.2 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect diff --git a/go.sum b/go.sum index 5e87f1eab..3498f4ec4 100644 --- a/go.sum +++ b/go.sum @@ -8,7 +8,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -22,7 +21,6 @@ github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -67,10 +65,6 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= @@ -81,7 +75,6 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/package-url/packageurl-go v0.1.3 h1:4juMED3hHiz0set3Vq3KeQ75KD1avthoXLtmE3I0PLs= github.com/package-url/packageurl-go v0.1.3/go.mod h1:nKAWB8E6uk1MHqiS/lQb9pYBGH2+mdJ2PJc2s50dQY0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= @@ -96,17 +89,12 @@ github.com/quay/claircore/toolkit v1.3.0 h1:QncygaArnuSKbkPESD2zMDz5xJLWlJxAGPZ6 github.com/quay/claircore/toolkit v1.3.0/go.mod h1:REVn3WdU+9yMurWa+h9mfuiPWYzKqSaKIGWZ4Xanj5g= github.com/quay/goval-parser v0.8.8 h1:Uf+f9iF2GIR5GPUY2pGoa9il2+4cdES44ZlM0mWm4cA= github.com/quay/goval-parser v0.8.8/go.mod h1:Y0NTNfPYOC7yxsYKzJOrscTWUPq1+QbtHw4XpPXWPMc= -github.com/quay/zlog v1.1.9 h1:O9/vfCUcGGUs0ukqQ77xPCJBPqpZBJ00jt7IUMsRco8= -github.com/quay/zlog v1.1.9/go.mod h1:O7OCW9oe7igrswhjKlyOvTo2+6FPsV7L0/owWEX6JHE= github.com/remind101/migrate v0.0.0-20170729031349-52c1edff7319 h1:ukjThsA2ou7AmovpwtMVkNQSuoN/v5U16+JomTz3c7o= github.com/remind101/migrate v0.0.0-20170729031349-52c1edff7319/go.mod h1:rhSvwcijY9wfmrBYrfCvapX8/xOTV46NAUjBRgUyJqc= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= -github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/spdx/tools-golang v0.5.6 h1:HUwSJWxyAR7vBstEeZ1+guD6Jcl4TVsKyTBUwT0RjTQ= github.com/spdx/tools-golang v0.5.6/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= @@ -153,9 +141,7 @@ golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From f992becbec3b24bd3a62fe4cb63df51089119b5f Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Mon, 5 Jan 2026 14:03:27 -0600 Subject: [PATCH 60/77] toolkit/events: log/slog API This adds a different API to the `toolkit/events` package that forwards to a `slog.Handler` extracted from a `context.Context`. This deprecates the (unused) `Group`/`Sink`/`Event` architecture in favor of a much simpler facade. The end goal of this addition is to allow claircore components to emit events into a separate per-request (whatever that means) event stream. Signed-off-by: Hank Donnay --- toolkit/events/doc.go | 2 + toolkit/events/events.go | 96 ++++++++++------------------- toolkit/events/log.go | 60 ------------------ toolkit/events/log_test.go | 117 ------------------------------------ toolkit/events/slog.go | 43 +++++++++++++ toolkit/events/slog_test.go | 34 +++++++++++ toolkit/go.mod | 2 +- 7 files changed, 113 insertions(+), 241 deletions(-) create mode 100644 toolkit/events/doc.go delete mode 100644 toolkit/events/log.go delete mode 100644 toolkit/events/log_test.go create mode 100644 toolkit/events/slog.go create mode 100644 toolkit/events/slog_test.go diff --git a/toolkit/events/doc.go b/toolkit/events/doc.go new file mode 100644 index 000000000..c57f6c8d7 --- /dev/null +++ b/toolkit/events/doc.go @@ -0,0 +1,2 @@ +// Package events is a small event logging system. +package events diff --git a/toolkit/events/events.go b/toolkit/events/events.go index df4b7182f..3f6ab9fbc 100644 --- a/toolkit/events/events.go +++ b/toolkit/events/events.go @@ -1,68 +1,36 @@ -// Package events is a small event logging system. package events import ( "context" "errors" "fmt" - "runtime" - "sync" ) // Group is a grouping of log events going to a common Sink. -type Group struct { - mu sync.Mutex // Protects errs - sink Sink - name string - errs []error -} +// +// Deprecated: This was never used. +type Group struct{} // NewGroup creates a new Group "name" writing to Sink "sink". // // The passed Context is only used for the duration of the NewGroup call. -func NewGroup(ctx context.Context, sink Sink, name string) (*Group, error) { - if err := sink.StartGroup(ctx, name); err != nil { - return nil, err - } - g := &Group{ - sink: sink, - name: name, - } - _, file, line, _ := runtime.Caller(1) - runtime.SetFinalizer(g, func(g *Group) { - panic(fmt.Sprintf("%s:%d: Group not finished", file, line)) - }) - return g, nil +// +// Deprecated: This was never used. +func NewGroup(_ context.Context, _ Sink, _ string) (*Group, error) { + return nil, fmt.Errorf(`events.Group API: NewGroup: %w`, errors.ErrUnsupported) } // Finish signals to the underlying sink that this group is done and reports any // errors accumulated by derived Log objects. -func (g *Group) Finish(ctx context.Context) error { - runtime.SetFinalizer(g, nil) - if err := g.sink.FinishGroup(ctx, g.name); err != nil { - g.pushErr(err) - } - g.mu.Lock() - defer g.mu.Unlock() - if err := errors.Join(g.errs...); err != nil { - return fmt.Errorf("events: error(s) while emitting logs:\n%w", err) - } - return nil -} - -func (g *Group) event(topic string, ev Event) { - if err := g.sink.Event(g.name, topic, ev); err != nil { - g.pushErr(err) - } -} - -func (g *Group) pushErr(err error) { - g.mu.Lock() - g.errs = append(g.errs, err) - g.mu.Unlock() +// +// Deprecated: This was never used. +func (g *Group) Finish(_ context.Context) error { + return fmt.Errorf(`events.Group API: Group.Finish: %w`, errors.ErrUnsupported) } // Log is the facade that "user" code should expect. +// +// Deprecated: This was never used. type Log interface { Printf(format string, v ...interface{}) Errorf(format string, v ...interface{}) @@ -70,16 +38,15 @@ type Log interface { Finish() } -type opaque struct{} - -var ctxKey = (*opaque)(nil) - // WithGroup returns a Context with the provided Group embedded. // // Functions further down the call stack can derive Log interfaces with // FromContext. -func WithGroup(ctx context.Context, g *Group) context.Context { - return context.WithValue(ctx, ctxKey, g) +// +// Deprecated: This was never used, and will not return a child +// [context.Context]. +func WithGroup(ctx context.Context, _ *Group) context.Context { + return ctx } // FromContext returns a Log implementation grouping messages under the provided @@ -87,21 +54,22 @@ func WithGroup(ctx context.Context, g *Group) context.Context { // // The returned implementation may be all no-op methods, so callers should avoid // logging "expensive" data. -func FromContext(ctx context.Context, topic string) Log { - g := ctx.Value(ctxKey).(*Group) - if g == nil { - return noop{} - } - l := log{ - g: g, - topic: topic, - } - g.sink.Topic(topic) - l.buf.Grow(4 * 1024) // Guess - return &l +// +// Deprecated: This was never used, and will return a no-op implementation. +func FromContext(_ context.Context, _ string) Log { + return noop{} } +// Noop is an implementer of Log that does nothing. +type noop struct{} + +func (noop) Printf(_ string, _ ...interface{}) {} +func (noop) Errorf(_ string, _ ...interface{}) {} +func (noop) Finish() {} + // Sink is the interface that event sinks must implement. +// +// Deprecated: This was never used. type Sink interface { // StartGroup is called when a new group is created. The Context should only // be used for the duration of the StartGroup call. @@ -118,6 +86,8 @@ type Sink interface { } // Event is the event information delivered to a Sink. +// +// Deprecated: This was never used. type Event struct { _key struct{} Message string diff --git a/toolkit/events/log.go b/toolkit/events/log.go deleted file mode 100644 index 41bcb2e71..000000000 --- a/toolkit/events/log.go +++ /dev/null @@ -1,60 +0,0 @@ -package events - -import ( - "bytes" - "fmt" -) - -// Log implements Log by writing prints to a strings.Builder. -// -// Using a Builder should prevent extra copying. The log "lines" are written -// with invalid UTF-8 sequences between them, which lets the resulting giant -// string be split easily with very little chance of mangling inputs. -type log struct { - g *Group - buf bytes.Buffer - topic string -} - -func (l *log) Printf(format string, v ...interface{}) { - if format == "" { - return - } - l.msg(fmt.Sprintf(format, v...), false) -} - -func (l *log) Errorf(format string, v ...interface{}) { - if format == "" { - return - } - l.msg(fmt.Sprintf(format, v...), true) -} - -func (l *log) msg(msg string, err bool) { - if err { - l.buf.WriteByte('\xFF') - } - l.buf.WriteString(msg) - l.buf.WriteByte('\x00') -} - -func (l *log) Finish() { - b := l.buf.Bytes() - for len(b) != 0 { - i := bytes.IndexByte(b, '\x00') - var m []byte - m, b = b[:i], b[i+1:] - err := m[0] == '\xFF' - if err { - m = m[1:] - } - l.g.event(l.topic, Event{Error: err, Message: string(m)}) - } -} - -// Noop is an implementer of Log that does nothing. -type noop struct{} - -func (noop) Printf(_ string, _ ...interface{}) {} -func (noop) Errorf(_ string, _ ...interface{}) {} -func (noop) Finish() {} diff --git a/toolkit/events/log_test.go b/toolkit/events/log_test.go deleted file mode 100644 index 64a0ec9bf..000000000 --- a/toolkit/events/log_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package events - -import ( - "context" - "errors" - "io" - "testing" -) - -type testSink struct{ *testing.T } - -var _ Sink = (*testSink)(nil) - -func (s *testSink) StartGroup(ctx context.Context, group string) error { - s.Logf("group start: %s", group) - return nil -} - -func (s *testSink) Topic(t string) { - s.Logf("topic: %s", t) -} - -func (s *testSink) Event(group, topic string, ev Event) error { - s.Logf("%s/%s: err?%v %s", group, topic, ev.Error, ev.Message) - return nil -} - -func (s *testSink) FinishGroup(ctx context.Context, group string) error { - s.Logf("group finish: %s", group) - return nil -} - -func TestLog(t *testing.T) { - ctx := context.Background() - sink := &testSink{T: t} - g, err := NewGroup(ctx, sink, t.Name()) - if err != nil { - t.Fatal(err) - } - defer func() { - if err := g.Finish(ctx); err != nil { - t.Error(err) - } - }() - - ctx = WithGroup(ctx, g) - l := FromContext(ctx, "topic") - defer l.Finish() - l.Printf("") - l.Errorf("") - for i := 0; i < 10; i++ { - l.Printf("hello, %d", i) - l.Errorf("hello, %d", i) - } -} - -type errSink struct { - start error - event error - finish error -} - -var _ Sink = (*errSink)(nil) - -func (s *errSink) StartGroup(ctx context.Context, group string) (err error) { - err, s.start = s.start, nil - return err -} - -func (s *errSink) Topic(t string) {} - -func (s *errSink) Event(group, topic string, ev Event) (err error) { - err, s.event = s.event, nil - return err -} - -func (s *errSink) FinishGroup(ctx context.Context, group string) (err error) { - err, s.finish = s.finish, nil - return err -} - -func TestLogError(t *testing.T) { - startErr := errors.New("start") - eventErr := errors.New("event") - finishErr := errors.New("finish") - ctx := context.Background() - sink := &errSink{ - start: startErr, - event: eventErr, - finish: finishErr, - } - _, err := NewGroup(ctx, sink, t.Name()) - if !errors.Is(err, startErr) { - t.Errorf("got: %v, want: %v", err, startErr) - } - g, err := NewGroup(ctx, sink, t.Name()) - if err != nil { - t.Fatal(err) - } - - ctx = WithGroup(ctx, g) - l := FromContext(ctx, "topic") - l.Printf("hello, test") - l.Finish() - - err = g.Finish(ctx) - if got, want := err, eventErr; !errors.Is(got, want) { - t.Errorf("got: %v, want: %v", got, want) - } - if got, want := err, finishErr; !errors.Is(got, want) { - t.Errorf("got: %v, want: %v", got, want) - } - if got, want := err, io.EOF; errors.Is(got, want) { - t.Errorf("got: %v, want: %v", got, want) - } - t.Log(err) -} diff --git a/toolkit/events/slog.go b/toolkit/events/slog.go new file mode 100644 index 000000000..cb125d365 --- /dev/null +++ b/toolkit/events/slog.go @@ -0,0 +1,43 @@ +package events + +import ( + "context" + "log/slog" + "sync" +) + +// Ctxkey is a Context key type. +// +// This is unexported so that other packages cannot construct these values. +type ctxkey int + +const ( + _ ctxkey = iota + + // HandlerKey is the value used in [WithHandler]/[Logger]. + handlerKey +) + +// WithHandler sets the provided [slog.Handler] to be used when calls to +// [Logger] are made with the returned [context.Context]. +func WithHandler(ctx context.Context, h slog.Handler) context.Context { + return context.WithValue(ctx, handlerKey, h) +} + +// DiscardLogger returns a [slog.Logger] configured to discard all records. +var discardLogger = sync.OnceValue(func() *slog.Logger { + return slog.New(slog.DiscardHandler) +}) + +// Logger returns a [slog.Logger] for recording current "request" (whatever that +// means in context) events. +// +// The returned Logger may simply ignore all events. See [WithHandler] for +// setting the backing [slog.Handler]. +func Logger(ctx context.Context) *slog.Logger { + v := ctx.Value(handlerKey) + if v == nil { + return discardLogger() + } + return slog.New(v.(slog.Handler)) +} diff --git a/toolkit/events/slog_test.go b/toolkit/events/slog_test.go new file mode 100644 index 000000000..a299c4f97 --- /dev/null +++ b/toolkit/events/slog_test.go @@ -0,0 +1,34 @@ +package events + +import ( + "bytes" + "context" + "log/slog" + "testing" +) + +func TestSlog(t *testing.T) { + t.Run("Handler", func(t *testing.T) { + var buf bytes.Buffer + want := slog.NewTextHandler(&buf, nil) + + ctx := WithHandler(context.Background(), want) + h := Logger(ctx).Handler() + got, ok := h.(*slog.TextHandler) + if !ok { + t.Errorf("expected a *slog.TextHandler, got: %T", h) + } + if got != want { + t.Errorf("unexpected Hander: got: %p, want: %p", got, want) + } + }) + + t.Run("Noop", func(t *testing.T) { + want := discardLogger() + ctx := context.Background() + got := Logger(ctx) + if got != want { + t.Errorf("unexpected Logger: got: %p, want: %p", got, want) + } + }) +} diff --git a/toolkit/go.mod b/toolkit/go.mod index e5cc1f27f..d0b8f2ee2 100644 --- a/toolkit/go.mod +++ b/toolkit/go.mod @@ -1,5 +1,5 @@ module github.com/quay/claircore/toolkit -go 1.22 +go 1.24 require github.com/google/go-cmp v0.7.0 From db75b36765bd84359e77b4a662705cb993fcb110 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 03:29:31 +0000 Subject: [PATCH 61/77] chore(deps): bump github.com/quay/claircore/toolkit from 1.3.0 to 1.4.0 Bumps [github.com/quay/claircore/toolkit](https://github.com/quay/claircore) from 1.3.0 to 1.4.0. - [Release notes](https://github.com/quay/claircore/releases) - [Changelog](https://github.com/quay/claircore/blob/main/CHANGELOG.md) - [Commits](https://github.com/quay/claircore/compare/v1.3.0...v1.4.0) --- updated-dependencies: - dependency-name: github.com/quay/claircore/toolkit dependency-version: 1.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 95b6258ea..48ac68090 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 github.com/package-url/packageurl-go v0.1.3 github.com/prometheus/client_golang v1.23.2 - github.com/quay/claircore/toolkit v1.3.0 + github.com/quay/claircore/toolkit v1.4.0 github.com/quay/claircore/updater/driver v1.0.0 github.com/quay/goval-parser v0.8.8 github.com/remind101/migrate v0.0.0-20170729031349-52c1edff7319 diff --git a/go.sum b/go.sum index 3498f4ec4..2178c2fbb 100644 --- a/go.sum +++ b/go.sum @@ -85,8 +85,8 @@ github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9Z github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/quay/claircore/toolkit v1.3.0 h1:QncygaArnuSKbkPESD2zMDz5xJLWlJxAGPZ69XCYA5o= -github.com/quay/claircore/toolkit v1.3.0/go.mod h1:REVn3WdU+9yMurWa+h9mfuiPWYzKqSaKIGWZ4Xanj5g= +github.com/quay/claircore/toolkit v1.4.0 h1:ygHG1pLAOTSk7r2Wmo/UbIz9WHJb+K3hhQnNIvLrBSQ= +github.com/quay/claircore/toolkit v1.4.0/go.mod h1:0cQXEt/BIYSxo/Wq6ItyAJOJzdvD6ty1wPZJ9xR3b6E= github.com/quay/goval-parser v0.8.8 h1:Uf+f9iF2GIR5GPUY2pGoa9il2+4cdES44ZlM0mWm4cA= github.com/quay/goval-parser v0.8.8/go.mod h1:Y0NTNfPYOC7yxsYKzJOrscTWUPq1+QbtHw4XpPXWPMc= github.com/remind101/migrate v0.0.0-20170729031349-52c1edff7319 h1:ukjThsA2ou7AmovpwtMVkNQSuoN/v5U16+JomTz3c7o= From 831f201ccfce558b34c4e22bc5cf1202b3570bae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:28:28 +0000 Subject: [PATCH 62/77] chore(deps): bump the golang-x group across 1 directory with 5 updates Bumps the golang-x group with 3 updates in the / directory: [golang.org/x/crypto](https://github.com/golang/crypto), [golang.org/x/net](https://github.com/golang/net) and [golang.org/x/tools](https://github.com/golang/tools). Updates `golang.org/x/crypto` from 0.46.0 to 0.47.0 - [Commits](https://github.com/golang/crypto/compare/v0.46.0...v0.47.0) Updates `golang.org/x/net` from 0.48.0 to 0.49.0 - [Commits](https://github.com/golang/net/compare/v0.48.0...v0.49.0) Updates `golang.org/x/sys` from 0.39.0 to 0.40.0 - [Commits](https://github.com/golang/sys/compare/v0.39.0...v0.40.0) Updates `golang.org/x/text` from 0.32.0 to 0.33.0 - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.32.0...v0.33.0) Updates `golang.org/x/tools` from 0.40.0 to 0.41.0 - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.40.0...v0.41.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.47.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x - dependency-name: golang.org/x/net dependency-version: 0.49.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x - dependency-name: golang.org/x/sys dependency-version: 0.40.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x - dependency-name: golang.org/x/text dependency-version: 0.33.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x - dependency-name: golang.org/x/tools dependency-version: 0.41.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-x ... Signed-off-by: dependabot[bot] --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 48ac68090..579493606 100644 --- a/go.mod +++ b/go.mod @@ -26,13 +26,13 @@ require ( go.opentelemetry.io/otel v1.39.0 go.opentelemetry.io/otel/trace v1.39.0 go.uber.org/mock v0.6.0 - golang.org/x/crypto v0.46.0 - golang.org/x/net v0.48.0 + golang.org/x/crypto v0.47.0 + golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 - golang.org/x/sys v0.39.0 - golang.org/x/text v0.32.0 + golang.org/x/sys v0.40.0 + golang.org/x/text v0.33.0 golang.org/x/time v0.14.0 - golang.org/x/tools v0.40.0 + golang.org/x/tools v0.41.0 modernc.org/sqlite v1.42.2 ) @@ -62,7 +62,7 @@ require ( go.opentelemetry.io/otel/metric v1.39.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/mod v0.31.0 // indirect + golang.org/x/mod v0.32.0 // indirect google.golang.org/protobuf v1.36.8 // indirect modernc.org/libc v1.66.10 // indirect modernc.org/mathutil v1.7.1 // indirect diff --git a/go.sum b/go.sum index 2178c2fbb..fd0689b50 100644 --- a/go.sum +++ b/go.sum @@ -123,18 +123,18 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= -golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= -golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= @@ -142,19 +142,19 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= -golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190924052046-3ac2a5bbd98a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= -golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= From e7b6109f5e4e8e9204e1e286b9b9ca0179679a67 Mon Sep 17 00:00:00 2001 From: crozzy Date: Mon, 12 Jan 2026 16:26:29 -0800 Subject: [PATCH 63/77] rpm: check more paths for rpm-installed files The function that checks if files were installed via RPMs gates the paths it considers with a regex, this regex was missing some binary paths that (when evaluated) don't contain a preceding slash. This also adds a path where the golang binaries are installed in some RH images. Signed-off-by: crozzy --- internal/rpm/info.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/rpm/info.go b/internal/rpm/info.go index c05414df7..a7893b34e 100644 --- a/internal/rpm/info.go +++ b/internal/rpm/info.go @@ -223,15 +223,16 @@ var ( // FilePatterns is a regular expression for *any* file that may need to be // recorded alongside a package. // - // The tested strings are absolute paths. + // The tested strings are paths relative to the rpm root (almost always "/"). filePatterns = sync.OnceValue(func() *regexp.Regexp { pat := []string{ `^.*/[^/]+\.[ejw]ar$`, // Jar files `^.*/site-packages/[^/]+\.egg-info/PKG-INFO$`, // Python packages `^.*/package.json$`, // npm packages `^.*/[^/]+\.gemspec$`, // ruby gems - `^/usr/s?bin/[^/]+$`, // any executable - `^/usr/libexec/[^/]+/[^/]+$`, // sometimes the executables are here too + `^usr/s?bin/[^/]+$`, // any executable + `^usr/libexec/[^/]+/[^/]+$`, // sometimes the executables are here too + `^usr/lib/golang/(?:[^/]+/)+[^/]+$`, // golang executables are here for some RH layers } return regexp.MustCompile(strings.Join(pat, `|`)) }) From 583a45689475c3c2c43adc72428a92a71c339370 Mon Sep 17 00:00:00 2001 From: Hank Donnay Date: Wed, 14 Jan 2026 11:03:03 -0600 Subject: [PATCH 64/77] libindex: fix `os.Remove` calls While thinking about #1728, I noticed that these are wrong and will probably subtly break things. Signed-off-by: Hank Donnay --- libindex/fetcher.go | 2 +- libindex/tempfile_linux.go | 2 +- libindex/tempfile_unix.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libindex/fetcher.go b/libindex/fetcher.go index d8c180146..41ca4dc06 100644 --- a/libindex/fetcher.go +++ b/libindex/fetcher.go @@ -154,7 +154,7 @@ func (f closeFunc) Close() error { // Because we know we're the only concurrent call that's dealing with this blob, // we can be a bit more lax. func (a *RemoteFetchArena) fetchFileForCache(ctx context.Context, desc *claircore.LayerDescription) (*os.File, error) { - log := slog.With("arena", a.root, "layer", desc.Digest, "uri", desc.URI) + log := slog.With("arena", a.root.Name(), "layer", desc.Digest, "uri", desc.URI) ctx, span := tracer.Start(ctx, "RemoteFetchArena.fetchUnlinkedFile") defer span.End() span.SetStatus(codes.Error, "") diff --git a/libindex/tempfile_linux.go b/libindex/tempfile_linux.go index 16efa55b4..fab396bfe 100644 --- a/libindex/tempfile_linux.go +++ b/libindex/tempfile_linux.go @@ -64,7 +64,7 @@ Loop: if !tmpOK && err == nil { // This is just a best-effort action to keep files from accumulating. // The correct way is to use the kernel feature for this: the O_TMPFILE flag. - _ = os.Remove(f.Name()) + _ = dir.Remove(f.Name()) } if err != nil { diff --git a/libindex/tempfile_unix.go b/libindex/tempfile_unix.go index 4627273fd..f3b75d733 100644 --- a/libindex/tempfile_unix.go +++ b/libindex/tempfile_unix.go @@ -23,6 +23,6 @@ func openTemp(dir *os.Root) (f *os.File, err error) { if err != nil { return nil, err } - runtime.AddCleanup(f, func(n string) { os.Remove(n) }, f.Name()) + runtime.AddCleanup(f, func(n string) { dir.Remove(n) }, f.Name()) return f, nil } From f66a4452e495ac33a489b6a2983ebf830f1fa03f Mon Sep 17 00:00:00 2001 From: crozzy Date: Wed, 5 Nov 2025 14:59:29 -0800 Subject: [PATCH 65/77] alpine: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. Signed-off-by: crozzy --- alpine/purl.go | 82 ++++++++++++++++++++++++++++++++++++++++++ alpine/purl_test.go | 86 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 alpine/purl.go create mode 100644 alpine/purl_test.go diff --git a/alpine/purl.go b/alpine/purl.go new file mode 100644 index 000000000..527a57221 --- /dev/null +++ b/alpine/purl.go @@ -0,0 +1,82 @@ +package alpine + +import ( + "context" + "strconv" + "strings" + + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +const ( + // PURLType is the type of package URL for Alpine APKs. + PURLType = "apk" + // PURLNamespace is the namespace of Alpine APKs. + PURLNamespace = "alpine" + // PURLDistroQualifier is the qualifier key for the distribution. + PURLDistroQualifier = "distro" +) + +// GeneratePURL generates a PURL for an Alpine APK package in the format: +// pkg:apk/alpine/@?arch=&distro=- +func GeneratePURL(ctx context.Context, r *claircore.IndexRecord) (packageurl.PackageURL, error) { + var distro string + if r.Distribution != nil { + distro = r.Distribution.Name + "-" + r.Distribution.Version + } + return packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: r.Package.Name, + Version: r.Package.Version, + Qualifiers: packageurl.QualifiersFromMap(map[string]string{ + "arch": r.Package.Arch, + PURLDistroQualifier: distro, + }), + }, nil +} + +// ParsePURL parses a PURL for an Alpine APK package into a list of IndexRecords. +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + d := distoToDistribution(purl.Qualifiers.Map()[PURLDistroQualifier]) + return []*claircore.IndexRecord{ + { + Package: &claircore.Package{ + Name: purl.Name, + Version: purl.Version, + Kind: claircore.BINARY, + Arch: purl.Qualifiers.Map()["arch"], + }, + Distribution: d, + }, + }, nil +} + +// DistributionFromPURL converts a PURL string to a *claircore.Distribution. +// distro strings are expected to be in the form "alpine-". +// The distro format is discussed here: https://github.com/package-url/purl-spec/issues/423 +func distoToDistribution(distro string) *claircore.Distribution { + // split the distro string into name and version + d := strings.Split(distro, "-") + if d[1] == edgeDist.VersionID { + return edgeDist + } + v := strings.Split(d[1], ".") + if len(v) < 2 { + // There are some cases where the version is 3 parts but the patch doesn't + // influence addressability so we can ignore it. + return nil + } + maj, err := strconv.Atoi(v[0]) + if err != nil { + return nil + } + min, err := strconv.Atoi(v[1]) + if err != nil { + return nil + } + dist := stableRelease{maj, min}.Distribution() + return dist +} diff --git a/alpine/purl_test.go b/alpine/purl_test.go new file mode 100644 index 000000000..c3d5cbcef --- /dev/null +++ b/alpine/purl_test.go @@ -0,0 +1,86 @@ +package alpine + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/quay/claircore" +) + +func TestRoundTripIndexRecordAlpine(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + }{ + { + name: "basic", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "busybox", + Version: "1.36.1-r0", + Arch: "x86_64", + Kind: claircore.BINARY, + PackageDB: "apk:/busybox", + Filepath: "/bin/busybox", + }, + Distribution: &claircore.Distribution{ + Name: "Alpine Linux", + PrettyName: "Alpine Linux v3.18", + Version: "3.18", + DID: "alpine", + }, + }, + }, + { + name: "edge", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "busybox", + Version: "1.36.1-r0", + Arch: "x86_64", + Kind: claircore.BINARY, + PackageDB: "apk:/busybox", + Filepath: "/bin/busybox", + }, + Distribution: &claircore.Distribution{ + Name: "Alpine Linux", + PrettyName: "Alpine Linux edge", + Version: "edge", + DID: "alpine", + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + got, err := ParsePURL(ctx, p) + if err != nil { + t.Fatalf("ParsePURL: %v", err) + } + if diff := cmp.Diff([]*claircore.IndexRecord{tc.ir}, got, purlCmp); diff != "" { + t.Fatalf("round-trip mismatch (-want +got):\n%s", diff) + } + }) + } +} + +var purlCmp = cmp.Options{ + // Ignore PackageDB and Filepath as they are not currently used in the matching. + cmpopts.IgnoreFields(claircore.Package{}, "PackageDB", "Filepath"), + // The version is what we save when indexing, the versionID is what we parse + // from the vulnerability database. Neither are used in the matching, the DID, + // DistributionName, and DistributionPrettyName are used. + cmpopts.IgnoreFields(claircore.Distribution{}, "VersionID", "Version"), +} From 7fbd6d3b1a92fee9b781cd55b577f10d459a9727 Mon Sep 17 00:00:00 2001 From: crozzy Date: Thu, 6 Nov 2025 12:01:39 -0800 Subject: [PATCH 66/77] debian: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. This patch minimally changes the matching logic to ensure the version code name isn't needed. Signed-off-by: crozzy --- debian/matcher.go | 2 +- debian/purl.go | 69 +++++++++++++++++++++++++++++++++++++++++++++ debian/purl_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 debian/purl.go create mode 100644 debian/purl_test.go diff --git a/debian/matcher.go b/debian/matcher.go index b13754086..28f590adf 100644 --- a/debian/matcher.go +++ b/debian/matcher.go @@ -40,7 +40,7 @@ func (*Matcher) Query() []driver.MatchConstraint { return []driver.MatchConstraint{ driver.DistributionDID, driver.DistributionName, - driver.DistributionVersion, + driver.DistributionVersionID, } } diff --git a/debian/purl.go b/debian/purl.go new file mode 100644 index 000000000..9cf612fcc --- /dev/null +++ b/debian/purl.go @@ -0,0 +1,69 @@ +package debian + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +const ( + // PURLType is the type of package URL for Debian packages. + PURLType = "deb" + // PURLNamespace is the namespace of Debian packages. + PURLNamespace = "debian" + // PURLDistroQualifier is the qualifier key for the distribution. + PURLDistroQualifier = "distro" +) + +// GeneratePURL generates a PURL for a Debian package in the format: +// pkg:deb/debian/@?arch=&distro=debian- +func GeneratePURL(ctx context.Context, r *claircore.IndexRecord) (packageurl.PackageURL, error) { + var distro string + if r.Distribution != nil { + // This completely ignores the version code name e.g. "debian-13". + distro = "debian-" + r.Distribution.VersionID + } + return packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: r.Package.Name, + Version: r.Package.Version, + Qualifiers: packageurl.QualifiersFromMap(map[string]string{ + "arch": r.Package.Arch, + PURLDistroQualifier: distro, + }), + }, nil +} + +// ParsePURL parses a PURL for a Debian package into a list of IndexRecords. +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + dq := purl.Qualifiers.Map()[PURLDistroQualifier] + distroParts := strings.SplitN(dq, "-", 2) + if len(distroParts) != 2 { + return nil, fmt.Errorf("invalid distro PURL: %s", dq) + } + _, err := strconv.Atoi(distroParts[1]) + if err != nil { + return nil, fmt.Errorf("invalid distro version: %s", distroParts[1]) + } + return []*claircore.IndexRecord{ + { + Package: &claircore.Package{ + Name: purl.Name, + Version: purl.Version, + Arch: purl.Qualifiers.Map()["arch"], + Kind: claircore.BINARY, + }, + Distribution: &claircore.Distribution{ + Name: "Debian GNU/Linux", + VersionID: distroParts[1], + DID: "debian", + }, + }, + }, nil +} diff --git a/debian/purl_test.go b/debian/purl_test.go new file mode 100644 index 000000000..c342a68e2 --- /dev/null +++ b/debian/purl_test.go @@ -0,0 +1,68 @@ +package debian + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/quay/claircore" +) + +func TestRoundTripIndexRecordDebian(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + }{ + { + name: "basic", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "bash", + Version: "5.1.8-6", + Arch: "x86_64", + Kind: claircore.BINARY, + PackageDB: "deb:/var/lib/dpkg/status", + }, + Distribution: &claircore.Distribution{ + Name: "Debian GNU/Linux", + VersionID: "11", + VersionCodeName: "bullseye", + DID: "debian", + Version: "11 (bullseye)", + PrettyName: "Debian GNU/Linux 11 (bullseye)", + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + fmt.Println(p.String()) + got, err := ParsePURL(ctx, p) + if err != nil { + t.Fatalf("ParsePURL: %v", err) + } + if diff := cmp.Diff([]*claircore.IndexRecord{tc.ir}, got, purlCmp); diff != "" { + t.Fatalf("round-trip mismatch (-want +got):\n%s", diff) + } + }) + } +} + +var purlCmp = cmp.Options{ + // Ignore PackageDB and Filepath as they are not currently used in the matching. + cmpopts.IgnoreFields(claircore.Package{}, "PackageDB", "Filepath"), + // The distribution that we can decipher from the PURL only has the fields that + // are used in the matching; DID, Name and VersionID. + cmpopts.IgnoreFields(claircore.Distribution{}, "VersionCodeName", "Version", "PrettyName"), +} From a53f956f891f236e8987da911e6cfdc92974c56f Mon Sep 17 00:00:00 2001 From: crozzy Date: Thu, 6 Nov 2025 15:09:41 -0800 Subject: [PATCH 67/77] ubuntu: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. This patch minimally changes the matching logic to ensure the version code name isn't needed. Signed-off-by: crozzy --- ubuntu/matcher.go | 2 +- ubuntu/purl.go | 65 +++++++++++++++++++++++++++++++++++++++++++ ubuntu/purl_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 ubuntu/purl.go create mode 100644 ubuntu/purl_test.go diff --git a/ubuntu/matcher.go b/ubuntu/matcher.go index ed6793ad0..ee0f3dece 100644 --- a/ubuntu/matcher.go +++ b/ubuntu/matcher.go @@ -40,7 +40,7 @@ func (*Matcher) Query() []driver.MatchConstraint { return []driver.MatchConstraint{ driver.DistributionDID, driver.DistributionName, - driver.DistributionVersion, + driver.DistributionVersionID, } } diff --git a/ubuntu/purl.go b/ubuntu/purl.go new file mode 100644 index 000000000..f88c343c5 --- /dev/null +++ b/ubuntu/purl.go @@ -0,0 +1,65 @@ +package ubuntu + +import ( + "context" + "fmt" + "strings" + + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +const ( + // PURLType is the type of package URL for Ubuntu packages. + PURLType = "deb" + // PURLNamespace is the namespace of Ubuntu packages. + PURLNamespace = "ubuntu" + // PURLDistroQualifier is the qualifier key for the distribution. + PURLDistroQualifier = "distro" +) + +// GeneratePURL generates a PURL for a Ubuntu package in the format: +// pkg:deb/ubuntu/@?arch=&distro=ubuntu- +func GeneratePURL(ctx context.Context, r *claircore.IndexRecord) (packageurl.PackageURL, error) { + var distro string + if r.Distribution != nil { + // This completely ignores the version code name e.g. "ubuntu-24.04". + distro = "ubuntu-" + r.Distribution.VersionID + } + return packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: r.Package.Name, + Version: r.Package.Version, + Qualifiers: packageurl.QualifiersFromMap(map[string]string{ + "arch": r.Package.Arch, + PURLDistroQualifier: distro, + }), + }, nil +} + +// ParsePURL parses a PURL for a Ubuntu package into a list of IndexRecords. +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + dq := purl.Qualifiers.Map()[PURLDistroQualifier] + distroParts := strings.SplitN(dq, "-", 2) + if len(distroParts) != 2 { + return nil, fmt.Errorf("invalid distro PURL: %s", dq) + } + return []*claircore.IndexRecord{ + { + Package: &claircore.Package{ + Name: purl.Name, + Version: purl.Version, + Arch: purl.Qualifiers.Map()["arch"], + Kind: claircore.BINARY, + }, + Distribution: &claircore.Distribution{ + Name: "Ubuntu", + DID: "ubuntu", + VersionID: distroParts[1], + PrettyName: "Ubuntu " + distroParts[1], + }, + }, + }, nil +} diff --git a/ubuntu/purl_test.go b/ubuntu/purl_test.go new file mode 100644 index 000000000..e926b5127 --- /dev/null +++ b/ubuntu/purl_test.go @@ -0,0 +1,68 @@ +package ubuntu + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/quay/claircore" +) + +func TestRoundTripIndexRecordDebian(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + }{ + { + name: "basic", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "bash", + Version: "5.1.8-6", + Arch: "x86_64", + Kind: claircore.BINARY, + PackageDB: "deb:/var/lib/dpkg/status", + }, + Distribution: &claircore.Distribution{ + Name: "Ubuntu", + DID: "ubuntu", + VersionID: "24.04", + PrettyName: "Ubuntu 24.04", + VersionCodeName: "noble", + Version: "24.04 (Noble)", + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + fmt.Println(p.String()) + got, err := ParsePURL(ctx, p) + if err != nil { + t.Fatalf("ParsePURL: %v", err) + } + if diff := cmp.Diff([]*claircore.IndexRecord{tc.ir}, got, purlCmp); diff != "" { + t.Fatalf("round-trip mismatch (-want +got):\n%s", diff) + } + }) + } +} + +var purlCmp = cmp.Options{ + // Ignore PackageDB and Filepath as they are not currently used in the matching. + cmpopts.IgnoreFields(claircore.Package{}, "PackageDB", "Filepath"), + // The distribution that we can decipher from the PURL only has the fields that + // are used in the matching; DID, Name and VersionID. + cmpopts.IgnoreFields(claircore.Distribution{}, "VersionCodeName", "Version", "PrettyName"), +} From 89bf592980b8b6efb5ff7ce48dc2b8f619c427af Mon Sep 17 00:00:00 2001 From: crozzy Date: Mon, 10 Nov 2025 13:24:52 -0800 Subject: [PATCH 68/77] java: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to Maven PURLs and back. The group ID is extracted from the Package.Name and used as the PURL Namespace. Signed-off-by: crozzy --- java/purl.go | 47 ++++++++++++++++++++ java/purl_test.go | 106 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 java/purl.go create mode 100644 java/purl_test.go diff --git a/java/purl.go b/java/purl.go new file mode 100644 index 000000000..10b520a65 --- /dev/null +++ b/java/purl.go @@ -0,0 +1,47 @@ +package java + +import ( + "context" + "fmt" + "strings" + + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +const ( + // PURLType is the type of package URL for Java packages. + PURLType = "maven" +) + +// GeneratePURL generates a Maven PURL for a given [claircore.IndexRecord]. +func GeneratePURL(ctx context.Context, ir *claircore.IndexRecord) (packageurl.PackageURL, error) { + // The PURL examples in the spec show that the group ID is used + // as the namespace for Maven PURLs, so split the package name on the colon. + // https://github.com/package-url/purl-spec?tab=readme-ov-file#some-purl-examples + parts := strings.SplitN(ir.Package.Name, ":", 2) + if len(parts) != 2 { + return packageurl.PackageURL{}, fmt.Errorf("invalid package name: %s", ir.Package.Name) + } + return packageurl.PackageURL{ + Type: PURLType, + Namespace: parts[0], + Name: parts[1], + Version: ir.Package.Version, + }, nil +} + +// ParsePURL parses a Maven PURL into a list of [claircore.IndexRecord]s. +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + return []*claircore.IndexRecord{ + { + Package: &claircore.Package{ + Name: purl.Namespace + ":" + purl.Name, + Version: purl.Version, + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, nil +} diff --git a/java/purl_test.go b/java/purl_test.go new file mode 100644 index 000000000..d3fb08484 --- /dev/null +++ b/java/purl_test.go @@ -0,0 +1,106 @@ +package java + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +func TestRoundTripIndexRecordJava(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + }{ + { + name: "basic", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "org.apache.commons:commons-lang3", + Version: "3.12.0", + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, + { + name: "different-version", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "com.fasterxml.jackson.core:jackson-databind", + Version: "2.17.1", + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + got, err := ParsePURL(ctx, p) + if err != nil { + t.Fatalf("ParsePURL: %v", err) + } + if diff := cmp.Diff(got, []*claircore.IndexRecord{tc.ir}, purlCmp); diff != "" { + t.Fatalf("round-trip mismatch (-got +want):\n%s", diff) + } + }) + } +} + +func TestGeneratePURL(t *testing.T) { + t.Parallel() + ctx := context.Background() + tests := []struct { + name string + ir *claircore.IndexRecord + want packageurl.PackageURL + }{ + { + name: "basic", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "org.slf4j:slf4j-api", + Version: "2.0.12", + }, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Namespace: "org.slf4j", + Name: "slf4j-api", + Version: "2.0.12", + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", got.String()) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Fatalf("purl mismatch (-got +want):\n%s", diff) + } + }) + } +} + +var purlCmp = cmp.Options{ + // Ignore fields that are not part of the PURL round-trip for Java. + cmpopts.IgnoreFields(claircore.Package{}, "PackageDB", "Filepath"), +} From a00961b57585fe8b0a0d170f46010b5864883ac2 Mon Sep 17 00:00:00 2001 From: crozzy Date: Mon, 10 Nov 2025 14:20:18 -0800 Subject: [PATCH 69/77] python: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. Signed-off-by: crozzy --- python/purl.go | 46 +++++++++++++++ python/purl_test.go | 137 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 python/purl.go create mode 100644 python/purl_test.go diff --git a/python/purl.go b/python/purl.go new file mode 100644 index 000000000..82270068f --- /dev/null +++ b/python/purl.go @@ -0,0 +1,46 @@ +package python + +import ( + "context" + "fmt" + + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" + "github.com/quay/claircore/pkg/pep440" +) + +const ( + // PURLType is the type of package URL for Python packages. + PURLType = "pypi" +) + +// GeneratePURL generates a PyPI PURL for a given [claircore.IndexRecord]. +// Example: pkg:pypi/django@1.11.1 +func GeneratePURL(ctx context.Context, ir *claircore.IndexRecord) (packageurl.PackageURL, error) { + return packageurl.PackageURL{ + Type: PURLType, + Name: ir.Package.Name, + Version: ir.Package.Version, + }, nil +} + +// ParsePURL parses a PyPI PURL into a list of [claircore.IndexRecord]s. +// The matcher needs the NormalizedVersion to be set, and it to be pep440. +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + v, err := pep440.Parse(purl.Version) + if err != nil { + return nil, fmt.Errorf("python: unable to parse version: %w", err) + } + return []*claircore.IndexRecord{ + { + Package: &claircore.Package{ + Name: purl.Name, + Version: v.String(), + NormalizedVersion: v.Version(), + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, nil +} diff --git a/python/purl_test.go b/python/purl_test.go new file mode 100644 index 000000000..b70eb59f6 --- /dev/null +++ b/python/purl_test.go @@ -0,0 +1,137 @@ +package python + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" + "github.com/quay/claircore/pkg/pep440" +) + +func TestRoundTripIndexRecordPython(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + wantErr bool + }{ + { + name: "urllib3", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "urllib3", + Version: "2.2.1", + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, + { + name: "django", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "django", + Version: "1.11.1", + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, + { + name: "bad-version", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "django", + Version: "something-invalid", + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + wantErr: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Align expected NormalizedVersion with GeneratePURL/ParsePURL behaviour. + // This helps the testcases stay simple. + if v, err := pep440.Parse(tc.ir.Package.Version); err == nil { + tc.ir.Package.NormalizedVersion = v.Version() + } + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", p.String()) + + got, err := ParsePURL(ctx, p) + if tc.wantErr { + if err == nil { + t.Fatalf("expected an error, got nil") + } + return + } + if err != nil { + t.Fatalf("ParsePURL unexpected error: %v", err) + } + if diff := cmp.Diff(got, []*claircore.IndexRecord{tc.ir}, purlCmp); diff != "" { + t.Fatalf("round-trip mismatch (-got +want):\n%s", diff) + } + }) + } +} + +func TestGeneratePURL(t *testing.T) { + t.Parallel() + ctx := context.Background() + tests := []struct { + name string + ir *claircore.IndexRecord + want packageurl.PackageURL + }{ + { + name: "basic", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "requests", + Version: "2.31.0", + }, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Name: "requests", + Version: "2.31.0", + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Ensure NormalizedVersion is set since GeneratePURL uses it. + if v, err := pep440.Parse(tc.ir.Package.Version); err == nil { + tc.ir.Package.NormalizedVersion = v.Version() + } + got, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", got.String()) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Fatalf("purl mismatch (-got +want):\n%s", diff) + } + }) + } +} + +var purlCmp = cmp.Options{ + // Ignore fields not relevant to PURL round-trip for Python. + cmpopts.IgnoreFields(claircore.Package{}, "PackageDB", "Filepath"), +} From e2a1399a881a431c8f7da9fe44182422da7a09d6 Mon Sep 17 00:00:00 2001 From: crozzy Date: Tue, 11 Nov 2025 09:57:34 -0800 Subject: [PATCH 70/77] nodejs: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. Signed-off-by: crozzy --- nodejs/purl.go | 46 +++++++++++++++ nodejs/purl_test.go | 136 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 nodejs/purl.go create mode 100644 nodejs/purl_test.go diff --git a/nodejs/purl.go b/nodejs/purl.go new file mode 100644 index 000000000..9d3ad9b63 --- /dev/null +++ b/nodejs/purl.go @@ -0,0 +1,46 @@ +package nodejs + +import ( + "context" + "fmt" + + "github.com/Masterminds/semver" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +const ( + // PURLType is the type of package URL for Node.js packages. + PURLType = "npm" +) + +// GeneratePURL generates a Node.js PURL for a given [claircore.IndexRecord]. +// Example: pkg:npm/express@4.18.2 +func GeneratePURL(ctx context.Context, ir *claircore.IndexRecord) (packageurl.PackageURL, error) { + return packageurl.PackageURL{ + Type: PURLType, + Name: ir.Package.Name, + Version: ir.Package.Version, + }, nil +} + +// ParsePURL parses a Node.js PURL into a list of [claircore.IndexRecord]s. +// The matcher needs the NormalizedVersion to be set. +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + v, err := semver.NewVersion(purl.Version) + if err != nil { + return nil, fmt.Errorf("nodejs: unable to parse version: %w", err) + } + return []*claircore.IndexRecord{ + { + Package: &claircore.Package{ + Name: purl.Name, + Kind: claircore.BINARY, + Version: v.String(), + NormalizedVersion: claircore.FromSemver(v), + }, + Repository: &Repository, + }, + }, nil +} diff --git a/nodejs/purl_test.go b/nodejs/purl_test.go new file mode 100644 index 000000000..6a250e896 --- /dev/null +++ b/nodejs/purl_test.go @@ -0,0 +1,136 @@ +package nodejs + +import ( + "context" + "testing" + + "github.com/Masterminds/semver" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +func TestRoundTripIndexRecordNodeJS(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + wantErr bool + }{ + { + name: "express", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "express", + Version: "4.18.2", + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, + { + name: "lodash", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "lodash", + Version: "4.17.21", + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, + { + name: "bad-version", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "left-pad", + Version: "not-a-version", + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + wantErr: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Align expected NormalizedVersion with GeneratePURL/ParsePURL behaviour. + if v, err := semver.NewVersion(tc.ir.Package.Version); err == nil { + tc.ir.Package.NormalizedVersion = claircore.FromSemver(v) + } + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", p.String()) + + got, err := ParsePURL(ctx, p) + if tc.wantErr { + if err == nil { + t.Fatalf("expected an error, got nil") + } + return + } + if err != nil { + t.Fatalf("ParsePURL unexpected error: %v", err) + } + if diff := cmp.Diff(got, []*claircore.IndexRecord{tc.ir}, purlCmp); diff != "" { + t.Fatalf("round-trip mismatch (-got +want):\n%s", diff) + } + }) + } +} + +func TestGeneratePURL(t *testing.T) { + t.Parallel() + ctx := context.Background() + tests := []struct { + name string + ir *claircore.IndexRecord + want packageurl.PackageURL + }{ + { + name: "basic", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "express", + Version: "4.18.2", + }, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Name: "express", + Version: "4.18.2", + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Ensure NormalizedVersion is set since GeneratePURL uses it. + if v, err := semver.NewVersion(tc.ir.Package.Version); err == nil { + tc.ir.Package.NormalizedVersion = claircore.FromSemver(v) + } + got, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", got.String()) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Fatalf("purl mismatch (-got +want):\n%s", diff) + } + }) + } +} + +var purlCmp = cmp.Options{ + // Ignore fields not relevant to PURL round-trip for Node.js. + cmpopts.IgnoreFields(claircore.Package{}, "PackageDB", "Filepath"), +} From 1144172111092ac97023355b39113ff678657ada Mon Sep 17 00:00:00 2001 From: crozzy Date: Tue, 11 Nov 2025 13:27:12 -0800 Subject: [PATCH 71/77] ruby: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. Signed-off-by: crozzy --- ruby/purl.go | 39 +++++++++++++++++ ruby/purl_test.go | 106 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 ruby/purl.go create mode 100644 ruby/purl_test.go diff --git a/ruby/purl.go b/ruby/purl.go new file mode 100644 index 000000000..7dc49a1f6 --- /dev/null +++ b/ruby/purl.go @@ -0,0 +1,39 @@ +package ruby + +import ( + "context" + + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +const ( + // PURLType is the type of package URL for Ruby packages. + PURLType = "gem" +) + +// GeneratePURL generates a Ruby PURL for a given [claircore.IndexRecord]. +// Example: pkg:gem/rails@6.1.0 +func GeneratePURL(ctx context.Context, ir *claircore.IndexRecord) (packageurl.PackageURL, error) { + return packageurl.PackageURL{ + Type: PURLType, + Name: ir.Package.Name, + Version: ir.Package.Version, + }, nil +} + +// ParsePURL parses a Ruby PURL into a list of [claircore.IndexRecord]s. +// The matcher needs the NormalizedVersion to be set. +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + return []*claircore.IndexRecord{ + { + Package: &claircore.Package{ + Name: purl.Name, + Version: purl.Version, + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, nil +} diff --git a/ruby/purl_test.go b/ruby/purl_test.go new file mode 100644 index 000000000..4190149a1 --- /dev/null +++ b/ruby/purl_test.go @@ -0,0 +1,106 @@ +package ruby + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +func TestRoundTripIndexRecordRuby(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + }{ + { + name: "rails", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "rails", + Version: "6.1.0", + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, + { + name: "rack", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "rack", + Version: "2.2.8", + Kind: claircore.BINARY, + }, + Repository: &Repository, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", p.String()) + got, err := ParsePURL(ctx, p) + if err != nil { + t.Fatalf("ParsePURL: %v", err) + } + if diff := cmp.Diff(got, []*claircore.IndexRecord{tc.ir}, purlCmp); diff != "" { + t.Fatalf("round-trip mismatch (-got +want):\n%s", diff) + } + }) + } +} + +func TestGeneratePURL(t *testing.T) { + t.Parallel() + ctx := context.Background() + tests := []struct { + name string + ir *claircore.IndexRecord + want packageurl.PackageURL + }{ + { + name: "basic", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "rails", + Version: "6.1.0", + }, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Name: "rails", + Version: "6.1.0", + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", got.String()) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Fatalf("purl mismatch (-got +want):\n%s", diff) + } + }) + } +} + +var purlCmp = cmp.Options{ + // Ignore fields not relevant to PURL round-trip for Ruby. + cmpopts.IgnoreFields(claircore.Package{}, "PackageDB", "Filepath"), +} From 63272f048fb52cfe4462438c6b71fd588c08e467 Mon Sep 17 00:00:00 2001 From: crozzy Date: Wed, 12 Nov 2025 08:31:21 -0800 Subject: [PATCH 72/77] rhcc: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. Signed-off-by: crozzy --- rhel/rhcc/purl.go | 80 +++++++++++++++++++++ rhel/rhcc/purl_test.go | 153 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 rhel/rhcc/purl.go create mode 100644 rhel/rhcc/purl_test.go diff --git a/rhel/rhcc/purl.go b/rhel/rhcc/purl.go new file mode 100644 index 000000000..b6a8ba8af --- /dev/null +++ b/rhel/rhcc/purl.go @@ -0,0 +1,80 @@ +package rhcc + +import ( + "context" + "fmt" + + "github.com/Masterminds/semver" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" + "github.com/quay/claircore/toolkit/types/cpe" +) + +const ( + // PURLType is the type of package URL for Red Hat Container Catalog packages. + PURLType = "oci" +) + +// GenerateOCIPURL generates an OCI PURL for a given [claircore.IndexRecord]. +// Example: +// pkg:oci/ubi@sha256:dbc1e98d14a022542e45b5f22e0206d3f86b5bdf237b58ee7170c9ddd1b3a283?repository_url=registry.access.redhat.com/ubi9/ubi +func GenerateOCIPURL(ctx context.Context, ir *claircore.IndexRecord) (packageurl.PackageURL, error) { + purl := packageurl.PackageURL{ + Type: PURLType, + Name: ir.Package.Name, + // This tends to be represented as a digest but we don't persist that information + // or use it for matching so we use the version as it is the most applicable. + Version: ir.Package.Version, + Qualifiers: packageurl.QualifiersFromMap(map[string]string{ + "arch": ir.Package.Arch, + "tag": ir.Package.Version, + }), + } + if ir.Repository != nil && ir.Repository.Name != GoldRepo.Name { + purl.Qualifiers = append( + purl.Qualifiers, + packageurl.Qualifier{Key: "container_cpe", Value: ir.Repository.CPE.String()}, + ) + } + return purl, nil +} + +// ParseOCIPURL parses an OCI PURL into a list of [claircore.IndexRecord]s. +// The matcher needs the NormalizedVersion to be set. +func ParseOCIPURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + ir := &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: purl.Name, + Version: purl.Version, + Arch: purl.Qualifiers.Map()["arch"], + RepositoryHint: "rhcc", + }, + } + + ir.Repository = &GoldRepo + if containerCPE, ok := purl.Qualifiers.Map()["container_cpe"]; ok { + cpe, err := cpe.Unbind(containerCPE) + if err != nil { + return nil, err + } + ir.Repository = &claircore.Repository{ + CPE: cpe, + Name: cpe.String(), + Key: RepositoryKey, + } + } + + // Deal with the version. + ir.Package.Version = purl.Version + if tag := purl.Qualifiers.Map()["tag"]; tag != "" { + // Prefer the tag over the version if it is present. + ir.Package.Version = tag + } + sv, err := semver.NewVersion(ir.Package.Version) + if err != nil { + return nil, fmt.Errorf("error parsing version: %w", err) + } + ir.Package.NormalizedVersion = claircore.FromSemver(sv) + return []*claircore.IndexRecord{ir}, nil +} diff --git a/rhel/rhcc/purl_test.go b/rhel/rhcc/purl_test.go new file mode 100644 index 000000000..28e3126c9 --- /dev/null +++ b/rhel/rhcc/purl_test.go @@ -0,0 +1,153 @@ +package rhcc + +import ( + "context" + "testing" + + "github.com/Masterminds/semver" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" + "github.com/quay/claircore/toolkit/types/cpe" +) + +func TestRoundTripIndexRecordOCI(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + }{ + { + name: "goldrepo-no-container-cpe", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "ubi", + Version: "v9.3.1", + NormalizedVersion: claircore.FromSemver(semver.MustParse("v9.3.1")), + Arch: "x86_64", + RepositoryHint: "rhcc", + }, + Repository: &GoldRepo, + }, + }, + { + name: "with-container-cpe", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "ubi-micro", + Version: "v9.3.1", + NormalizedVersion: claircore.FromSemver(semver.MustParse("v9.3.1")), + Arch: "x86_64", + RepositoryHint: "rhcc", + }, + Repository: &claircore.Repository{ + CPE: cpe.MustUnbind("cpe:/o:redhat:enterprise_linux:9::baseos"), + Name: "cpe:2.3:o:redhat:enterprise_linux:9:*:baseos:*:*:*:*:*", + Key: RepositoryKey, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + p, err := GenerateOCIPURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GenerateOCIPURL: %v", err) + } + t.Logf("generated PURL: %s", p.String()) + got, err := ParseOCIPURL(ctx, p) + if err != nil { + t.Fatalf("ParseOCIPURL: %v", err) + } + if diff := cmp.Diff(got, []*claircore.IndexRecord{tc.ir}, purlCmp); diff != "" { + t.Fatalf("round-trip mismatch (-got +want):\n%s", diff) + } + }) + } +} + +func TestGenerateOCIPURL(t *testing.T) { + t.Parallel() + ctx := context.Background() + tests := []struct { + name string + ir *claircore.IndexRecord + want packageurl.PackageURL + }{ + { + name: "basic-goldrepo", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "ubi", + Version: "v9.3.1", + Arch: "amd64", + }, + Repository: &GoldRepo, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Name: "ubi", + Version: "v9.3.1", + Qualifiers: packageurl.Qualifiers{ + {Key: "arch", Value: "amd64"}, + {Key: "tag", Value: "v9.3.1"}, + }, + }, + }, + { + name: "with-container-cpe", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "ubi", + Version: "v9.3.1", + Arch: "amd64", + }, + Repository: &claircore.Repository{ + CPE: cpe.MustUnbind("cpe:/o:redhat:enterprise_linux:9::baseos"), + Name: "cpe:2.3:o:redhat:enterprise_linux:9:*:baseos:*:*:*:*:*", + Key: RepositoryKey, + }, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Name: "ubi", + Version: "v9.3.1", + Qualifiers: packageurl.Qualifiers{ + {Key: "arch", Value: "amd64"}, + {Key: "tag", Value: "v9.3.1"}, + {Key: "container_cpe", Value: "cpe:2.3:o:redhat:enterprise_linux:9:*:baseos:*:*:*:*:*"}, + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := GenerateOCIPURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GenerateOCIPURL: %v", err) + } + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Fatalf("purl mismatch (-got +want):\n%s", diff) + } + }) + } +} + +var purlCmp = cmp.Options{ + // Ignore Distribution field as there isn't currently a serialized format + // and it is not currently used in the matching. + cmpopts.IgnoreFields(claircore.IndexRecord{}, "Distribution"), + cmpCPE, +} + +var cmpCPE = cmp.FilterPath( + func(p cmp.Path) bool { return p.Last().String() == ".CPE" }, + cmp.Comparer(func(a, b cpe.WFN) bool { return a.String() == b.String() }), +) From c1a2e0c0cc28106823b4762971f526d23b6237c8 Mon Sep 17 00:00:00 2001 From: crozzy Date: Thu, 13 Nov 2025 15:36:52 -0800 Subject: [PATCH 73/77] suse: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. Uses distro qualifier to pass DID-VERSION but also supports distro_cpe as SUSE is a distro that includes a CPE in their os-release file. Signed-off-by: crozzy --- suse/factory.go | 24 ++++--- suse/purl.go | 93 +++++++++++++++++++++++++++ suse/purl_test.go | 160 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+), 8 deletions(-) create mode 100644 suse/purl.go create mode 100644 suse/purl_test.go diff --git a/suse/factory.go b/suse/factory.go index 62737aad0..c73c61644 100644 --- a/suse/factory.go +++ b/suse/factory.go @@ -124,26 +124,34 @@ var releases sync.Map func mkELDist(oURL, ver string) *claircore.Distribution { name := strings.TrimSuffix(oURL, ".xml.gz") - v, _ := releases.LoadOrStore(name, &claircore.Distribution{ + v, _ := releases.LoadOrStore(name, ELDist(ver)) + return v.(*claircore.Distribution) +} + +func mkLeapDist(oURL, ver string) *claircore.Distribution { + name := strings.TrimSuffix(oURL, ".xml.gz") + v, _ := releases.LoadOrStore(name, leapDist(ver)) + return v.(*claircore.Distribution) +} + +func ELDist(ver string) *claircore.Distribution { + return &claircore.Distribution{ Name: "SLES", DID: "sles", Version: ver, VersionID: ver, PrettyName: "SUSE Linux Enterprise Server " + ver, - }) - return v.(*claircore.Distribution) + } } -func mkLeapDist(oURL, ver string) *claircore.Distribution { - name := strings.TrimSuffix(oURL, ".xml.gz") - v, _ := releases.LoadOrStore(name, &claircore.Distribution{ +func leapDist(ver string) *claircore.Distribution { + return &claircore.Distribution{ Name: "openSUSE Leap", DID: "opensuse-leap", Version: ver, VersionID: ver, PrettyName: "openSUSE Leap " + ver, - }) - return v.(*claircore.Distribution) + } } // FactoryConfig is the configuration accepted by the Factory. diff --git a/suse/purl.go b/suse/purl.go new file mode 100644 index 000000000..c9e98b382 --- /dev/null +++ b/suse/purl.go @@ -0,0 +1,93 @@ +package suse + +import ( + "context" + "fmt" + "strings" + + "github.com/package-url/packageurl-go" + "github.com/quay/claircore" + "github.com/quay/claircore/toolkit/types/cpe" +) + +const ( + // PURLType is the type of package URL for RPM packages. + PURLType = "rpm" + // PURLNamespace is the namespace of SUSE RPMs. + PURLNamespace = "opensuse" +) + +// GeneratePURL generates an RPM PURL for a given [claircore.IndexRecord]. +func GeneratePURL(ctx context.Context, ir *claircore.IndexRecord) (packageurl.PackageURL, error) { + p := packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: ir.Package.Name, + Version: ir.Package.Version, + Qualifiers: packageurl.QualifiersFromMap(map[string]string{ + "arch": ir.Package.Arch, + }), + } + if ir.Distribution != nil { + // We don't persist the CPE in the Distribution but try it first in case it's available. + if c := ir.Distribution.CPE.String(); c != "" { + p.Qualifiers = append(p.Qualifiers, packageurl.Qualifier{ + Key: "distro_cpe", + Value: c, + }) + } + + if ir.Distribution.DID != "" && ir.Distribution.VersionID != "" { + p.Qualifiers = append(p.Qualifiers, packageurl.Qualifier{ + Key: "distro", + Value: ir.Distribution.DID + "-" + ir.Distribution.VersionID, + }) + } + } + return p, nil +} + +// ParsePURL parses an RPM PURL into a list of [claircore.IndexRecord]s. +// Preference order for distribution: +// 1. distro_cpe qualifier (converted to a [claircore.Distribution]) +// 2. fallback to "distro" qualifier in the form "-" converted to a [claircore.Distribution] +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + var dist *claircore.Distribution + // First try and parse a distro CPE. + if dc := purl.Qualifiers.Map()["distro_cpe"]; dc != "" { + if wf, err := cpe.Unbind(dc); err == nil { + if d, err := cpeToDist(wf); err == nil && d != nil { + dist = d + } + } + } + // Fallback to legacy "distro" qualifier. + if dist == nil { + distroQualifier := purl.Qualifiers.Map()["distro"] + parts := strings.SplitN(distroQualifier, "-", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid distro qualifier: %s", distroQualifier) + } + switch parts[0] { + case "sles": + dist = ELDist(parts[1]) + case "opensuse-leap": + dist = leapDist(parts[1]) + default: + return nil, fmt.Errorf("invalid distro name: %s", parts[0]) + } + + } + + return []*claircore.IndexRecord{ + { + Package: &claircore.Package{ + Name: purl.Name, + Version: purl.Version, + Arch: purl.Qualifiers.Map()["arch"], + Kind: claircore.BINARY, + }, + Distribution: dist, + }, + }, nil +} diff --git a/suse/purl_test.go b/suse/purl_test.go new file mode 100644 index 000000000..d35dcfaec --- /dev/null +++ b/suse/purl_test.go @@ -0,0 +1,160 @@ +package suse + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" + "github.com/quay/claircore/toolkit/types/cpe" +) + +func TestRoundTripIndexRecord(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + }{ + { + name: "opensuse-leap-with-cpe", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "zlib", + Version: "1.2.11-150500.59.68.1", + Arch: "x86_64", + Kind: claircore.BINARY, + }, + Distribution: &claircore.Distribution{ + CPE: cpe.MustUnbind("cpe:2.3:o:opensuse:leap:15.5"), + Name: "openSUSE Leap", + VersionID: "15.5", + Version: "15.5", + DID: "opensuse-leap", + PrettyName: "openSUSE Leap 15.5", + }, + }, + }, + { + name: "sles-fallback-distro", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "bash", + Version: "5.1-150300.51.1", + Arch: "aarch64", + Kind: claircore.BINARY, + }, + Distribution: &claircore.Distribution{ + Name: "SLES", + VersionID: "12", + Version: "12", + DID: "sles", + PrettyName: "SUSE Linux Enterprise Server 12", + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GenerateRPMPURL: %v", err) + } + t.Logf("generated PURL: %s", p.String()) + got, err := ParsePURL(ctx, p) + if err != nil { + t.Fatalf("ParseRPMPURL: %v", err) + } + if diff := cmp.Diff(got, []*claircore.IndexRecord{tc.ir}, purlCmp); diff != "" { + t.Fatalf("round-trip mismatch (-got +want):\n%s", diff) + } + }) + } +} + +func TestGeneratePURL(t *testing.T) { + t.Parallel() + ctx := context.Background() + tests := []struct { + name string + ir *claircore.IndexRecord + want packageurl.PackageURL + }{ + { + name: "with-distro-cpe", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "zlib", + Version: "1.2.11-150500.59.68.1", + Arch: "x86_64", + }, + Distribution: &claircore.Distribution{ + CPE: cpe.MustUnbind("cpe:2.3:o:opensuse:leap:15.5"), + Name: "openSUSE Leap", + VersionID: "15.5", + DID: "opensuse", + }, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: "zlib", + Version: "1.2.11-150500.59.68.1", + Qualifiers: packageurl.Qualifiers{ + {Key: "arch", Value: "x86_64"}, + {Key: "distro_cpe", Value: "cpe:2.3:o:opensuse:leap:15.5:*:*:*:*:*:*:*"}, + {Key: "distro", Value: "opensuse-15.5"}, + }, + }, + }, + { + name: "fallback-distro-qualifier", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "bash", + Version: "5.1-150300.51.1", + Arch: "aarch64", + }, + Distribution: &claircore.Distribution{ + Name: "SUSE Linux Enterprise Server", + VersionID: "12", + DID: "suse", + }, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: "bash", + Version: "5.1-150300.51.1", + Qualifiers: packageurl.Qualifiers{ + {Key: "arch", Value: "aarch64"}, + {Key: "distro", Value: "suse-12"}, + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GenerateRPMPURL: %v", err) + } + t.Logf("generated PURL: %s", got.String()) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Fatalf("purl mismatch (-got +want):\n%s", diff) + } + }) + } +} + +var purlCmp = cmp.Options{ + // Ignore Distribution field differences for round-trip; only assert package fields. + cmpopts.IgnoreFields(claircore.Distribution{}, "PrettyName", "CPE"), +} From d9615cae83447d9ef2c702c8c39e1c9a3ef969eb Mon Sep 17 00:00:00 2001 From: crozzy Date: Fri, 14 Nov 2025 11:40:07 -0800 Subject: [PATCH 74/77] aws: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. Uses distro qualifier to pass DID-VERSION but also supports distro_cpe as amazon linux is a distro that includes a CPE in their os-release file. Signed-off-by: crozzy --- aws/distributionscanner.go | 6 +- aws/distributionscanner_test.go | 45 ++++++----- aws/purl.go | 89 ++++++++++++++++++++++ aws/purl_test.go | 129 ++++++++++++++++++++++++++++++++ aws/releases.go | 28 +++++-- 5 files changed, 265 insertions(+), 32 deletions(-) create mode 100644 aws/purl.go create mode 100644 aws/purl_test.go diff --git a/aws/distributionscanner.go b/aws/distributionscanner.go index bd3fd0b19..b237cfd16 100644 --- a/aws/distributionscanner.go +++ b/aws/distributionscanner.go @@ -79,7 +79,7 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([] return nil, nil } for _, buff := range files { - dist := ds.parse(buff) + dist := parse(buff) if dist != nil { return []*claircore.Distribution{dist}, nil } @@ -87,11 +87,11 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([] return []*claircore.Distribution{}, nil } -// parse attempts to match all AWS release regexp and returns the associated +// Parse attempts to match all AWS release regexp and returns the associated // distribution if it exists. // // separated into its own method to aid testing. -func (ds *DistributionScanner) parse(buff *bytes.Buffer) *claircore.Distribution { +func parse(buff *bytes.Buffer) *claircore.Distribution { for _, ur := range awsRegexes { if ur.regexp.Match(buff.Bytes()) { dist := releaseToDist(ur.release) diff --git a/aws/distributionscanner_test.go b/aws/distributionscanner_test.go index ec251ba75..161238ae2 100644 --- a/aws/distributionscanner_test.go +++ b/aws/distributionscanner_test.go @@ -72,52 +72,51 @@ SUPPORT_END="2028-03-01"`) func TestDistributionScanner(t *testing.T) { table := []struct { - name string - release Release - osRelease []byte + name string + release Release + osRelease []byte prettyDistName string }{ { - name: "AL1", - release: AmazonLinux1, - osRelease: AL1v201609OSRelease, + name: "AL1", + release: AmazonLinux1, + osRelease: AL1v201609OSRelease, prettyDistName: "Amazon Linux AMI 2018.03", }, { - name: "AL1", - release: AmazonLinux1, - osRelease: AL1v201703OSRelease, + name: "AL1", + release: AmazonLinux1, + osRelease: AL1v201703OSRelease, prettyDistName: "Amazon Linux AMI 2018.03", }, { - name: "AL1", - release: AmazonLinux1, - osRelease: AL1v201709OSRelease, + name: "AL1", + release: AmazonLinux1, + osRelease: AL1v201709OSRelease, prettyDistName: "Amazon Linux AMI 2018.03", }, { - name: "AL1", - release: AmazonLinux1, - osRelease: AL1v201803OSRelease, + name: "AL1", + release: AmazonLinux1, + osRelease: AL1v201803OSRelease, prettyDistName: "Amazon Linux AMI 2018.03", }, { - name: "AL2", - release: AmazonLinux2, - osRelease: AL2OSRelease, + name: "AL2", + release: AmazonLinux2, + osRelease: AL2OSRelease, prettyDistName: "Amazon Linux 2", }, { - name: "AL2023", - release: AmazonLinux2023, - osRelease: AL2023OSRelease, + name: "AL2023", + release: AmazonLinux2023, + osRelease: AL2023OSRelease, prettyDistName: "Amazon Linux 2023", }, } for _, tt := range table { t.Run(tt.name, func(t *testing.T) { - scanner := DistributionScanner{} - dist := scanner.parse(bytes.NewBuffer(tt.osRelease)) + dist := parse(bytes.NewBuffer(tt.osRelease)) cmpDist := releaseToDist(tt.release) if !cmp.Equal(dist, cmpDist) { t.Fatalf("%v", cmp.Diff(dist, cmpDist)) diff --git a/aws/purl.go b/aws/purl.go new file mode 100644 index 000000000..b8abc8644 --- /dev/null +++ b/aws/purl.go @@ -0,0 +1,89 @@ +package aws + +import ( + "context" + "fmt" + "strings" + + "github.com/package-url/packageurl-go" + "github.com/quay/claircore" + "github.com/quay/claircore/toolkit/types/cpe" +) + +const ( + // PURLType is the type of package URL for RPM packages. + PURLType = "rpm" + // PURLNamespace is the namespace of AWS RPMs. + PURLNamespace = "aws" +) + +// GeneratePURL generates an RPM PURL for a given [claircore.IndexRecord]. +func GeneratePURL(ctx context.Context, ir *claircore.IndexRecord) (packageurl.PackageURL, error) { + p := packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: ir.Package.Name, + Version: ir.Package.Version, + Qualifiers: packageurl.QualifiersFromMap(map[string]string{ + "arch": ir.Package.Arch, + }), + } + if ir.Distribution != nil { + // We don't persist the CPE in the Distribution but try it first in case it's available. + if c := ir.Distribution.CPE.String(); c != "" { + p.Qualifiers = append(p.Qualifiers, packageurl.Qualifier{ + Key: "distro_cpe", + Value: c, + }) + } + + if ir.Distribution.DID != "" && ir.Distribution.VersionID != "" { + p.Qualifiers = append(p.Qualifiers, packageurl.Qualifier{ + Key: "distro", + Value: "amzn-" + ir.Distribution.VersionID, + }) + } + } + return p, nil +} + +// ParsePURL parses an RPM PURL into a list of [claircore.IndexRecord]s. +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + ir := &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: purl.Name, + Version: purl.Version, + Arch: purl.Qualifiers.Map()["arch"], + Kind: claircore.BINARY, + }, + Distribution: &claircore.Distribution{}, + } + // Prefer a distro CPE if provided. + if dc := purl.Qualifiers.Map()["distro_cpe"]; dc != "" { + if wf, err := cpe.Unbind(dc); err == nil { + ir.Distribution = cpeToDistribution(wf) + return []*claircore.IndexRecord{ir}, nil + } + } + + // Fallback to legacy distro qualifier parsing: "Name-VersionID". + distroQualifier := purl.Qualifiers.Map()["distro"] + distroParts := strings.SplitN(distroQualifier, "-", 2) + if len(distroParts) != 2 { + return nil, fmt.Errorf("invalid distro PURL: %s", distroQualifier) + } + ver := distroParts[1] + if ver == AL1Dist.Version { + ir.Distribution = AL1Dist + } else { + ir.Distribution = &claircore.Distribution{ + Name: "Amazon Linux", + DID: ID, + Version: ver, + VersionID: ver, + PrettyName: "Amazon Linux " + ver, + CPE: cpe.MustUnbind("cpe:o:amazon:amazon_linux:" + ver), + } + } + return []*claircore.IndexRecord{ir}, nil +} diff --git a/aws/purl_test.go b/aws/purl_test.go new file mode 100644 index 000000000..6e3bf8818 --- /dev/null +++ b/aws/purl_test.go @@ -0,0 +1,129 @@ +package aws + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" + "github.com/quay/claircore/toolkit/types/cpe" +) + +func TestRoundTripIndexRecordAWS(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + }{ + { + name: "amzn2", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "curl", + Version: "7.79.1-2.amzn2.0.2", + Arch: "x86_64", + Kind: claircore.BINARY, + }, + Distribution: &claircore.Distribution{ + Name: "Amazon Linux AMI", + VersionID: "2018.03", + DID: "amzn", + Version: "2018.03", + PrettyName: "Amazon Linux AMI 2018.03", + CPE: cpe.MustUnbind("cpe:/o:amazon:linux:2018.03:ga"), + }, + }, + }, + { + name: "amzn2023", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "bash", + Version: "5.1.16-6.amzn2023.0.4", + Arch: "aarch64", + Kind: claircore.BINARY, + }, + Distribution: &claircore.Distribution{ + Name: "Amazon Linux", + VersionID: "2023", + DID: "amzn", + Version: "2023", + PrettyName: "Amazon Linux 2023", + CPE: cpe.MustUnbind("cpe:2.3:o:amazon:amazon_linux:2023:*:*:*:*:*:*:*"), + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GenerateRPMPURL: %v", err) + } + t.Logf("generated PURL: %s", p.String()) + got, err := ParsePURL(ctx, p) + if err != nil { + t.Fatalf("ParseRPMPURL: %v", err) + } + if diff := cmp.Diff(got, []*claircore.IndexRecord{tc.ir}); diff != "" { + t.Fatalf("round-trip mismatch (-got +want):\n%s", diff) + } + }) + } +} + +func TestGeneratePURL(t *testing.T) { + t.Parallel() + ctx := context.Background() + tests := []struct { + name string + ir *claircore.IndexRecord + want packageurl.PackageURL + }{ + { + name: "amzn2-basic", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "curl", + Version: "7.79.1-2.amzn2.0.2", + Arch: "x86_64", + }, + Distribution: &claircore.Distribution{ + Name: "Amazon Linux", + VersionID: "2023", + Version: "2023", + DID: "amzn", + }, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: "curl", + Version: "7.79.1-2.amzn2.0.2", + Qualifiers: packageurl.Qualifiers{ + {Key: "arch", Value: "x86_64"}, + {Key: "distro", Value: "amzn-2023"}, + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GenerateRPMPURL: %v", err) + } + t.Logf("generated PURL: %s", got.String()) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Fatalf("purl mismatch (-got +want):\n%s", diff) + } + }) + } +} diff --git a/aws/releases.go b/aws/releases.go index 055105584..456d12241 100644 --- a/aws/releases.go +++ b/aws/releases.go @@ -4,14 +4,14 @@ import ( "fmt" "github.com/quay/claircore" - "github.com/quay/claircore/pkg/cpe" + "github.com/quay/claircore/toolkit/types/cpe" ) type Release string const ( - AmazonLinux1 Release = "AL1" - AmazonLinux2 Release = "AL2" + AmazonLinux1 Release = "AL1" + AmazonLinux2 Release = "AL2" AmazonLinux2023 Release = "AL2023" // os-release name ID field consistently available on official amazon linux images ID = "amzn" @@ -20,8 +20,8 @@ const ( func (r Release) mirrorlist() string { //doc:url updater const ( - al1 = "http://repo.us-west-2.amazonaws.com/2018.03/updates/x86_64/mirror.list" - al2 = "https://cdn.amazonlinux.com/2/core/latest/x86_64/mirror.list" + al1 = "http://repo.us-west-2.amazonaws.com/2018.03/updates/x86_64/mirror.list" + al2 = "https://cdn.amazonlinux.com/2/core/latest/x86_64/mirror.list" al2023 = "https://cdn.amazonlinux.com/al2023/core/mirrors/latest/x86_64/mirror.list" ) switch r { @@ -62,7 +62,6 @@ var AL2023Dist = &claircore.Distribution{ CPE: cpe.MustUnbind("cpe:2.3:o:amazon:amazon_linux:2023"), } - func releaseToDist(release Release) *claircore.Distribution { switch release { case AmazonLinux1: @@ -76,3 +75,20 @@ func releaseToDist(release Release) *claircore.Distribution { return &claircore.Distribution{} } } + +// cpeToDistribution constructs a Distribution from a [cpe.WFN]. +func cpeToDistribution(wf cpe.WFN) *claircore.Distribution { + ver := wf.Attr[cpe.Version].String() + if ver == AL1Dist.Version { + return AL1Dist + } else { + return &claircore.Distribution{ + Name: "Amazon Linux", + DID: ID, + Version: ver, + VersionID: ver, + PrettyName: "Amazon Linux " + ver, + CPE: wf, + } + } +} From 4b9baa71d8a10281bbacabd70088d0c980cbab0b Mon Sep 17 00:00:00 2001 From: crozzy Date: Fri, 14 Nov 2025 12:07:38 -0800 Subject: [PATCH 75/77] oracle: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. Signed-off-by: crozzy --- oracle/purl.go | 60 ++++++++++++++++++++ oracle/purl_test.go | 130 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 oracle/purl.go create mode 100644 oracle/purl_test.go diff --git a/oracle/purl.go b/oracle/purl.go new file mode 100644 index 000000000..24e3c1336 --- /dev/null +++ b/oracle/purl.go @@ -0,0 +1,60 @@ +package oracle + +import ( + "context" + "fmt" + "strings" + + "github.com/package-url/packageurl-go" + "github.com/quay/claircore" +) + +const ( + // PURLType is the type of package URL for RPM packages. + PURLType = "rpm" + // PURLNamespace is the namespace of Oracle RPMs. + PURLNamespace = "oracle" +) + +// GeneratePURL generates an RPM PURL for a given [claircore.IndexRecord]. +func GeneratePURL(ctx context.Context, ir *claircore.IndexRecord) (packageurl.PackageURL, error) { + p := packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: ir.Package.Name, + Version: ir.Package.Version, + Qualifiers: packageurl.QualifiersFromMap(map[string]string{ + "arch": ir.Package.Arch, + }), + } + if ir.Distribution != nil { + if ir.Distribution.DID != "" && ir.Distribution.VersionID != "" { + p.Qualifiers = append(p.Qualifiers, packageurl.Qualifier{ + Key: "distro", + Value: "oracle-" + ir.Distribution.VersionID, + }) + } + } + return p, nil +} + +// ParsePURL parses an RPM PURL into a list of [claircore.IndexRecord]s. +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + ir := &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: purl.Name, + Version: purl.Version, + Arch: purl.Qualifiers.Map()["arch"], + Kind: claircore.BINARY, + }, + Distribution: &claircore.Distribution{}, + } + distroQualifier := purl.Qualifiers.Map()["distro"] + distroParts := strings.SplitN(distroQualifier, "-", 2) + if len(distroParts) != 2 { + return nil, fmt.Errorf("invalid distro PURL: %s", distroQualifier) + } + release := Release(distroParts[1]) + ir.Distribution = releaseToDist(release) + return []*claircore.IndexRecord{ir}, nil +} diff --git a/oracle/purl_test.go b/oracle/purl_test.go new file mode 100644 index 000000000..bbe2735f6 --- /dev/null +++ b/oracle/purl_test.go @@ -0,0 +1,130 @@ +package oracle + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +func TestRoundTripIndexRecordOracle(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + }{ + { + name: "ol9", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "bash", + Version: "5.1.8-6.el9", + Arch: "x86_64", + Kind: claircore.BINARY, + }, + Distribution: nineDist, + }, + }, + { + name: "ol7", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "coreutils", + Version: "8.22-24.oe1", + Arch: "aarch64", + Kind: claircore.BINARY, + }, + Distribution: sevenDist, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", p.String()) + got, err := ParsePURL(ctx, p) + if err != nil { + t.Fatalf("ParsePURL: %v", err) + } + if diff := cmp.Diff(got, []*claircore.IndexRecord{tc.ir}); diff != "" { + t.Fatalf("round-trip mismatch (-got +want):\n%s", diff) + } + }) + } +} + +func TestGeneratePURL(t *testing.T) { + t.Parallel() + ctx := context.Background() + tests := []struct { + name string + ir *claircore.IndexRecord + want packageurl.PackageURL + }{ + { + name: "basic-ol9", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "bash", + Version: "5.1.8-6.el9", + Arch: "x86_64", + }, + Distribution: releaseToDist(Nine), + }, + want: packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: "bash", + Version: "5.1.8-6.el9", + Qualifiers: packageurl.Qualifiers{ + {Key: "arch", Value: "x86_64"}, + {Key: "distro", Value: "oracle-9"}, + }, + }, + }, + { + name: "basic-ol7", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "coreutils", + Version: "8.22-24.oe1", + Arch: "aarch64", + }, + Distribution: releaseToDist(Seven), + }, + want: packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: "coreutils", + Version: "8.22-24.oe1", + Qualifiers: packageurl.Qualifiers{ + {Key: "arch", Value: "aarch64"}, + {Key: "distro", Value: "oracle-7"}, + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", got.String()) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Fatalf("purl mismatch (-got +want):\n%s", diff) + } + }) + } +} From 6491e6ab3859bef09575d3e69bede72a663e9c9b Mon Sep 17 00:00:00 2001 From: crozzy Date: Fri, 14 Nov 2025 13:06:38 -0800 Subject: [PATCH 76/77] photon: add purl generator and parser Add GeneratePURL and ParsePURL to translate from IndexRecord to PURL and back. Signed-off-by: crozzy --- photon/purl.go | 59 ++++++++++++++++++++ photon/purl_test.go | 130 ++++++++++++++++++++++++++++++++++++++++++++ photon/releases.go | 13 +++++ 3 files changed, 202 insertions(+) create mode 100644 photon/purl.go create mode 100644 photon/purl_test.go diff --git a/photon/purl.go b/photon/purl.go new file mode 100644 index 000000000..b6c5e1a00 --- /dev/null +++ b/photon/purl.go @@ -0,0 +1,59 @@ +package photon + +import ( + "context" + "fmt" + "strings" + + "github.com/package-url/packageurl-go" + "github.com/quay/claircore" +) + +const ( + // PURLType is the type of package URL for RPM packages. + PURLType = "rpm" + // PURLNamespace is the namespace of photon RPMs. + PURLNamespace = "photon" +) + +// GeneratePURL generates an RPM PURL for a given [claircore.IndexRecord]. +func GeneratePURL(ctx context.Context, ir *claircore.IndexRecord) (packageurl.PackageURL, error) { + p := packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: ir.Package.Name, + Version: ir.Package.Version, + Qualifiers: packageurl.QualifiersFromMap(map[string]string{ + "arch": ir.Package.Arch, + }), + } + if ir.Distribution != nil { + if ir.Distribution.DID != "" && ir.Distribution.VersionID != "" { + p.Qualifiers = append(p.Qualifiers, packageurl.Qualifier{ + Key: "distro", + Value: "photon-" + ir.Distribution.VersionID, + }) + } + } + return p, nil +} + +// ParsePURL parses an RPM PURL into a list of [claircore.IndexRecord]s. +func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.IndexRecord, error) { + ir := &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: purl.Name, + Version: purl.Version, + Arch: purl.Qualifiers.Map()["arch"], + Kind: claircore.BINARY, + }, + Distribution: &claircore.Distribution{}, + } + distroQualifier := purl.Qualifiers.Map()["distro"] + distroParts := strings.SplitN(distroQualifier, "-", 2) + if len(distroParts) != 2 { + return nil, fmt.Errorf("invalid distro PURL: %s", distroQualifier) + } + ir.Distribution = versionToDist(distroParts[1]) + return []*claircore.IndexRecord{ir}, nil +} diff --git a/photon/purl_test.go b/photon/purl_test.go new file mode 100644 index 000000000..a193c4111 --- /dev/null +++ b/photon/purl_test.go @@ -0,0 +1,130 @@ +package photon + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/package-url/packageurl-go" + + "github.com/quay/claircore" +) + +func TestRoundTripIndexRecordPhoton(t *testing.T) { + t.Parallel() + ctx := context.Background() + + tests := []struct { + name string + ir *claircore.IndexRecord + }{ + { + name: "photon 1.0", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "bash", + Version: "5.1.8-6.el9", + Arch: "x86_64", + Kind: claircore.BINARY, + }, + Distribution: photon1Dist, + }, + }, + { + name: "photon 2.0", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "coreutils", + Version: "8.22-24.oe1", + Arch: "aarch64", + Kind: claircore.BINARY, + }, + Distribution: photon2Dist, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + p, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", p.String()) + got, err := ParsePURL(ctx, p) + if err != nil { + t.Fatalf("ParsePURL: %v", err) + } + if diff := cmp.Diff(got, []*claircore.IndexRecord{tc.ir}); diff != "" { + t.Fatalf("round-trip mismatch (-got +want):\n%s", diff) + } + }) + } +} + +func TestGeneratePURL(t *testing.T) { + t.Parallel() + ctx := context.Background() + tests := []struct { + name string + ir *claircore.IndexRecord + want packageurl.PackageURL + }{ + { + name: "basic-ol9", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "bash", + Version: "5.1.8-6.el9", + Arch: "x86_64", + }, + Distribution: photon1Dist, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: "bash", + Version: "5.1.8-6.el9", + Qualifiers: packageurl.Qualifiers{ + {Key: "arch", Value: "x86_64"}, + {Key: "distro", Value: "photon-1.0"}, + }, + }, + }, + { + name: "basic-ol7", + ir: &claircore.IndexRecord{ + Package: &claircore.Package{ + Name: "coreutils", + Version: "8.22-24.oe1", + Arch: "aarch64", + }, + Distribution: photon2Dist, + }, + want: packageurl.PackageURL{ + Type: PURLType, + Namespace: PURLNamespace, + Name: "coreutils", + Version: "8.22-24.oe1", + Qualifiers: packageurl.Qualifiers{ + {Key: "arch", Value: "aarch64"}, + {Key: "distro", Value: "photon-2.0"}, + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := GeneratePURL(ctx, tc.ir) + if err != nil { + t.Fatalf("GeneratePURL: %v", err) + } + t.Logf("generated PURL: %s", got.String()) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Fatalf("purl mismatch (-got +want):\n%s", diff) + } + }) + } +} diff --git a/photon/releases.go b/photon/releases.go index 8468aa6c5..ed2f8bb92 100644 --- a/photon/releases.go +++ b/photon/releases.go @@ -49,3 +49,16 @@ func releaseToDist(r Release) *claircore.Distribution { return &claircore.Distribution{} } } + +func versionToDist(v string) *claircore.Distribution { + switch v { + case "1.0": + return photon1Dist + case "2.0": + return photon2Dist + case "3.0": + return photon3Dist + default: + return nil + } +} From 99b48ffc33162e1114a3964621f06680d4aaaddd Mon Sep 17 00:00:00 2001 From: crozzy Date: Fri, 14 Nov 2025 14:06:25 -0800 Subject: [PATCH 77/77] photon: add distro versions 4 and 5 There are now versions 4 and 5 that should be referenced in the code because this is not an updater that supports dynamic distribution discovery (yet). Signed-off-by: crozzy --- photon/distributionscanner.go | 10 ++++++++ photon/distributionscanner_test.go | 28 ++++++++++++++++++++ photon/purl.go | 2 +- photon/releases.go | 41 ++++++++++++++++++------------ 4 files changed, 64 insertions(+), 17 deletions(-) diff --git a/photon/distributionscanner.go b/photon/distributionscanner.go index b6bceae60..af0116611 100644 --- a/photon/distributionscanner.go +++ b/photon/distributionscanner.go @@ -46,6 +46,16 @@ var photonRegexes = []photonRegex{ // regex for /etc/os-release regexp: regexp.MustCompile(`^.*"VMware Photon OS"\sVERSION="3.0"`), }, + { + release: Photon4, + // regex for /etc/os-release + regexp: regexp.MustCompile(`^.*"VMware Photon OS"\sVERSION="4.0"`), + }, + { + release: Photon5, + // regex for /etc/os-release + regexp: regexp.MustCompile(`^.*"VMware Photon OS"\sVERSION="5.0"`), + }, } var ( diff --git a/photon/distributionscanner_test.go b/photon/distributionscanner_test.go index 6d2fc9d67..1598439da 100644 --- a/photon/distributionscanner_test.go +++ b/photon/distributionscanner_test.go @@ -34,6 +34,24 @@ ANSI_COLOR="1;34" HOME_URL="https://vmware.github.io/photon/" BUG_REPORT_URL="https://github.com/vmware/photon/issues"`) +var photon4OSRelease []byte = []byte(`NAME="VMware Photon OS" +VERSION="4.0" +ID=photon +VERSION_ID="4.0" +PRETTY_NAME="VMware Photon OS/Linux" +ANSI_COLOR="1;34" +HOME_URL="https://vmware.github.io/photon/" +BUG_REPORT_URL="https://github.com/vmware/photon/issues"`) + +var photon5OSRelease []byte = []byte(`NAME="VMware Photon OS" +VERSION="5.0" +ID=photon +VERSION_ID="5.0" +PRETTY_NAME="VMware Photon OS/Linux" +ANSI_COLOR="1;34" +HOME_URL="https://vmware.github.io/photon/" +BUG_REPORT_URL="https://github.com/vmware/photon/issues"`) + func TestDistributionScanner(t *testing.T) { table := []struct { name string @@ -55,6 +73,16 @@ func TestDistributionScanner(t *testing.T) { release: Photon3, osRelease: photon3OSRelease, }, + { + name: "photon 4.0", + release: Photon4, + osRelease: photon4OSRelease, + }, + { + name: "photon 5.0", + release: Photon5, + osRelease: photon5OSRelease, + }, } for _, tt := range table { t.Run(tt.name, func(t *testing.T) { diff --git a/photon/purl.go b/photon/purl.go index b6c5e1a00..b8ecd5192 100644 --- a/photon/purl.go +++ b/photon/purl.go @@ -54,6 +54,6 @@ func ParsePURL(ctx context.Context, purl packageurl.PackageURL) ([]*claircore.In if len(distroParts) != 2 { return nil, fmt.Errorf("invalid distro PURL: %s", distroQualifier) } - ir.Distribution = versionToDist(distroParts[1]) + ir.Distribution = releaseToDist(Release(distroParts[1])) return []*claircore.IndexRecord{ir}, nil } diff --git a/photon/releases.go b/photon/releases.go index ed2f8bb92..51bebf8a8 100644 --- a/photon/releases.go +++ b/photon/releases.go @@ -7,9 +7,11 @@ type Release string // These are some known Releases. const ( - Photon1 Release = `photon1` - Photon2 Release = `photon2` - Photon3 Release = `photon3` + Photon1 Release = `1.0` + Photon2 Release = `2.0` + Photon3 Release = `3.0` + Photon4 Release = `4.0` + Photon5 Release = `5.0` ) var photon1Dist = &claircore.Distribution{ @@ -36,6 +38,22 @@ var photon3Dist = &claircore.Distribution{ DID: "photon", } +var photon4Dist = &claircore.Distribution{ + Name: "VMware Photon OS", + Version: "4.0", + VersionID: "4.0", + PrettyName: "VMware Photon OS/Linux", + DID: "photon", +} + +var photon5Dist = &claircore.Distribution{ + Name: "VMware Photon OS", + Version: "5.0", + VersionID: "5.0", + PrettyName: "VMware Photon OS/Linux", + DID: "photon", +} + func releaseToDist(r Release) *claircore.Distribution { switch r { case Photon1: @@ -44,21 +62,12 @@ func releaseToDist(r Release) *claircore.Distribution { return photon2Dist case Photon3: return photon3Dist + case Photon4: + return photon4Dist + case Photon5: + return photon5Dist default: // return empty dist return &claircore.Distribution{} } } - -func versionToDist(v string) *claircore.Distribution { - switch v { - case "1.0": - return photon1Dist - case "2.0": - return photon2Dist - case "3.0": - return photon3Dist - default: - return nil - } -}