Skip to content

Commit

Permalink
suse: dynamic distribution discovery
Browse files Browse the repository at this point in the history
Currently Suse distributions are predefined in the code, this change
adds dynamic support for two Suse distro flavors:
suse.linux.enterprise.server and opensuse.leap.

Signed-off-by: crozzy <[email protected]>
  • Loading branch information
crozzy committed Nov 4, 2024
1 parent 73859ed commit aae1a36
Show file tree
Hide file tree
Showing 13 changed files with 386 additions and 295 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ require (
go.opentelemetry.io/otel/trace v1.31.0
go.uber.org/mock v0.5.0
golang.org/x/crypto v0.28.0
golang.org/x/net v0.30.0
golang.org/x/sync v0.8.0
golang.org/x/sys v0.26.0
golang.org/x/text v0.19.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
Expand Down
18 changes: 10 additions & 8 deletions pkg/ovalutil/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,20 @@ import (
)

var (
gzipPool sync.Pool
zstdPool sync.Pool
gzipPool = sync.Pool{
New: func() any {
return new(gzip.Reader)
},
}
zstdPool = sync.Pool{
New: func() any {
return new(zstd.Decoder)
},
}
)

func getGzip(r io.Reader) (*gzip.Reader, error) {
z := gzipPool.Get().(*gzip.Reader)
if z == nil {
return gzip.NewReader(r)
}
if err := z.Reset(r); err != nil {
return nil, err
}
Expand All @@ -30,9 +35,6 @@ func putGzip(z *gzip.Reader) {

func getZstd(r io.Reader) (*zstd.Decoder, error) {
z := zstdPool.Get().(*zstd.Decoder)
if z == nil {
return zstd.NewReader(r)
}
if err := z.Reset(r); err != nil {
return nil, err
}
Expand Down
93 changes: 55 additions & 38 deletions suse/distributionscanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package suse
import (
"bytes"
"context"
"regexp"
"fmt"
"runtime/trace"

"github.com/Masterminds/semver"
"github.com/quay/zlog"

"github.com/quay/claircore"
"github.com/quay/claircore/indexer"
"github.com/quay/claircore/osrelease"
"github.com/quay/claircore/pkg/cpe"
)

// Suse Enterprise Server has service pack releases however their security database files are bundled together
Expand All @@ -29,38 +32,12 @@ const (
suseReleasePath = `etc/SuSE-release`
)

type suseRegex struct {
release Release
regexp *regexp.Regexp
}
type suseType string

var suseRegexes = []suseRegex{
{
release: EnterpriseServer15,
// regex for /etc/issue
regexp: regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 15`),
},
{
release: EnterpriseServer12,
regexp: regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 12`),
},
{
release: EnterpriseServer11,
regexp: regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 11`),
},
{
release: Leap151,
regexp: regexp.MustCompile(`(?i)openSUSE Leap 15.1`),
},
{
release: Leap150,
regexp: regexp.MustCompile(`(?i)openSUSE Leap 15.0`),
},
{
release: Leap423,
regexp: regexp.MustCompile(`(?i)openSUSE Leap 42.3`),
},
}
var (
SLES suseType = "sles"
LEAP suseType = "leap"
)

var (
_ indexer.DistributionScanner = (*DistributionScanner)(nil)
Expand Down Expand Up @@ -99,7 +76,7 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]
return nil, nil
}
for _, buff := range files {
dist := ds.parse(buff)
dist := ds.parse(ctx, buff)
if dist != nil {
return []*claircore.Distribution{dist}, nil
}
Expand All @@ -111,11 +88,51 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]
// distribution if it exists.
//
// separated into its own method to aid testing.
func (ds *DistributionScanner) parse(buff *bytes.Buffer) *claircore.Distribution {
for _, ur := range suseRegexes {
if ur.regexp.Match(buff.Bytes()) {
return releaseToDist(ur.release)
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")
}
cpeName, cpeOK := kv["CPE_NAME"]
if !cpeOK {
return nil
}
// 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")
return nil
}
ver, verOK := kv["VERSION_ID"]
if !verOK {
return nil
}

d, err := cpeToDist(c, ver)
if err != nil {
zlog.Warn(ctx).Err(err).Msg("error converting cpe to distribution")
return nil
}

return d
}

func cpeToDist(r cpe.WFN, ver string) (*claircore.Distribution, error) {
if vendor, err := cpe.NewValue("opensuse"); err == nil && r.Attr[cpe.Vendor] == vendor {
if prod, err := cpe.NewValue("leap"); err == nil && r.Attr[cpe.Product] == prod {
return mkLeapDist(r.String(), ver), nil
}
}
if vendor, err := cpe.NewValue("suse"); err == nil && r.Attr[cpe.Vendor] == vendor {
if prod, err := cpe.NewValue("sles"); err == nil && r.Attr[cpe.Product] == prod {
// Canonicalize the version to the major.
v, err := semver.NewVersion(ver)
if err != nil {
return nil, err
}

return mkELDist(r.String(), fmt.Sprint(v.Major())), nil
}
}
return nil
return nil, nil
}
90 changes: 44 additions & 46 deletions suse/distributionscanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package suse

import (
"bytes"
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/quay/claircore"
)

var enterpriseServer15OSRelease []byte = []byte(`NAME="SLES"
Expand All @@ -16,33 +18,14 @@ ID_LIKE="suse"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:suse:sles:15:sp1"`)

var enterpriseServer12OSRelase []byte = []byte(`NAME="SLES"
var enterpriseServer12OSRelease []byte = []byte(`NAME="SLES"
VERSION="12-SP5"
VERSION_ID="12.5"
PRETTY_NAME="SUSE Linux Enterprise Server 12 SP5"
ID="sles"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:suse:sles:12:sp5"`)

var enterpriseServer11OSRelease []byte = []byte(`NAME="SLES"
VERSION="11-SP5"
VERSION_ID="11.2"
PRETTY_NAME="SUSE Linux Enterprise Server 11 SP5"
ID="sles"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:suse:sles:12:sp5"`)

var leap151OSRelease []byte = []byte(`NAME="openSUSE Leap"
VERSION="15.1"
ID="opensuse-leap"
ID_LIKE="suse opensuse"
VERSION_ID="15.1"
PRETTY_NAME="openSUSE Leap 15.1"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:opensuse:leap:15.1"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"`)

var leap15OSRelease []byte = []byte(`NAME="openSUSE Leap"
VERSION="15.0"
ID="opensuse-leap"
Expand All @@ -54,60 +37,75 @@ CPE_NAME="cpe:/o:opensuse:leap:15.0"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"`)

var leap423OSRelease []byte = []byte(`NAME="openSUSE Leap"
VERSION="42.3"
ID=opensuse
ID_LIKE="suse"
VERSION_ID="42.3"
PRETTY_NAME="openSUSE Leap 42.3"
var leap151OSRelease []byte = []byte(`NAME="openSUSE Leap"
VERSION="15.1"
ID="opensuse-leap"
ID_LIKE="suse opensuse"
VERSION_ID="15.1"
PRETTY_NAME="openSUSE Leap 15.1"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:opensuse:leap:42.3"
CPE_NAME="cpe:/o:opensuse:leap:15.1"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"`)

func TestDistributionScanner(t *testing.T) {
ctx := context.Background()
table := []struct {
name string
release Release
dist *claircore.Distribution
osRelease []byte
}{
{
name: "enterprise server 15",
release: EnterpriseServer15,
osRelease: enterpriseServer15OSRelease,
dist: &claircore.Distribution{
DID: "sles",
Name: "SLES",
Version: "15",
VersionID: "15",
PrettyName: "SUSE Linux Enterprise Server 15",
},
},
{
name: "enterprise server 12",
release: EnterpriseServer12,
osRelease: enterpriseServer12OSRelase,
},
{
name: "enterprise server 11",
release: EnterpriseServer11,
osRelease: enterpriseServer11OSRelease,
osRelease: enterpriseServer12OSRelease,
dist: &claircore.Distribution{
DID: "sles",
Name: "SLES",
Version: "12",
VersionID: "12",
PrettyName: "SUSE Linux Enterprise Server 12",
},
},
{
name: "leap 15.0",
release: Leap150,
osRelease: leap15OSRelease,
dist: &claircore.Distribution{
DID: "opensuse-leap",
Name: "openSUSE Leap",
Version: "15.0",
VersionID: "15.0",
PrettyName: "openSUSE Leap 15.0",
},
},
{
name: "leap 15.1",
release: Leap151,
osRelease: leap151OSRelease,
},
{
name: "leap 42.3",
release: Leap423,
osRelease: leap423OSRelease,
dist: &claircore.Distribution{
DID: "opensuse-leap",
Name: "openSUSE Leap",
Version: "15.1",
VersionID: "15.1",
PrettyName: "openSUSE Leap 15.1",
},
},
}
for _, tt := range table {
t.Run(tt.name, func(t *testing.T) {
scanner := DistributionScanner{}
dist := scanner.parse(bytes.NewBuffer(tt.osRelease))
if !cmp.Equal(dist, releaseToDist(tt.release)) {
t.Fatalf("%v", cmp.Diff(dist, releaseToDist(tt.release)))
dist := scanner.parse(ctx, bytes.NewBuffer(tt.osRelease))
if !cmp.Equal(dist, tt.dist) {
t.Fatalf("%v", cmp.Diff(dist, tt.dist))
}
})
}
Expand Down
Loading

0 comments on commit aae1a36

Please sign in to comment.