Skip to content

Commit ada0982

Browse files
committed
suse: dynamic distribution discovery
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]>
1 parent 73859ed commit ada0982

13 files changed

+377
-295
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ require (
2727
go.opentelemetry.io/otel/trace v1.31.0
2828
go.uber.org/mock v0.5.0
2929
golang.org/x/crypto v0.28.0
30+
golang.org/x/net v0.30.0
3031
golang.org/x/sync v0.8.0
3132
golang.org/x/sys v0.26.0
3233
golang.org/x/text v0.19.0

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
229229
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
230230
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
231231
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
232+
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
233+
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
232234
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
233235
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
234236
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=

pkg/ovalutil/pool.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,20 @@ import (
99
)
1010

1111
var (
12-
gzipPool sync.Pool
13-
zstdPool sync.Pool
12+
gzipPool = sync.Pool{
13+
New: func() any {
14+
return new(gzip.Reader)
15+
},
16+
}
17+
zstdPool = sync.Pool{
18+
New: func() any {
19+
return new(zstd.Decoder)
20+
},
21+
}
1422
)
1523

1624
func getGzip(r io.Reader) (*gzip.Reader, error) {
1725
z := gzipPool.Get().(*gzip.Reader)
18-
if z == nil {
19-
return gzip.NewReader(r)
20-
}
2126
if err := z.Reset(r); err != nil {
2227
return nil, err
2328
}
@@ -30,9 +35,6 @@ func putGzip(z *gzip.Reader) {
3035

3136
func getZstd(r io.Reader) (*zstd.Decoder, error) {
3237
z := zstdPool.Get().(*zstd.Decoder)
33-
if z == nil {
34-
return zstd.NewReader(r)
35-
}
3638
if err := z.Reset(r); err != nil {
3739
return nil, err
3840
}

suse/distributionscanner.go

+51-38
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ package suse
33
import (
44
"bytes"
55
"context"
6-
"regexp"
6+
"fmt"
77
"runtime/trace"
88

9+
"github.com/Masterminds/semver"
910
"github.com/quay/zlog"
1011

1112
"github.com/quay/claircore"
1213
"github.com/quay/claircore/indexer"
14+
"github.com/quay/claircore/osrelease"
15+
"github.com/quay/claircore/pkg/cpe"
1316
)
1417

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

32-
type suseRegex struct {
33-
release Release
34-
regexp *regexp.Regexp
35-
}
35+
type suseType string
3636

37-
var suseRegexes = []suseRegex{
38-
{
39-
release: EnterpriseServer15,
40-
// regex for /etc/issue
41-
regexp: regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 15`),
42-
},
43-
{
44-
release: EnterpriseServer12,
45-
regexp: regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 12`),
46-
},
47-
{
48-
release: EnterpriseServer11,
49-
regexp: regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 11`),
50-
},
51-
{
52-
release: Leap151,
53-
regexp: regexp.MustCompile(`(?i)openSUSE Leap 15.1`),
54-
},
55-
{
56-
release: Leap150,
57-
regexp: regexp.MustCompile(`(?i)openSUSE Leap 15.0`),
58-
},
59-
{
60-
release: Leap423,
61-
regexp: regexp.MustCompile(`(?i)openSUSE Leap 42.3`),
62-
},
63-
}
37+
var (
38+
SLES suseType = "sles"
39+
LEAP suseType = "leap"
40+
)
6441

6542
var (
6643
_ indexer.DistributionScanner = (*DistributionScanner)(nil)
@@ -99,7 +76,7 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]
9976
return nil, nil
10077
}
10178
for _, buff := range files {
102-
dist := ds.parse(buff)
79+
dist := ds.parse(ctx, buff)
10380
if dist != nil {
10481
return []*claircore.Distribution{dist}, nil
10582
}
@@ -111,11 +88,47 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([]
11188
// distribution if it exists.
11289
//
11390
// separated into its own method to aid testing.
114-
func (ds *DistributionScanner) parse(buff *bytes.Buffer) *claircore.Distribution {
115-
for _, ur := range suseRegexes {
116-
if ur.regexp.Match(buff.Bytes()) {
117-
return releaseToDist(ur.release)
91+
func (ds *DistributionScanner) parse(ctx context.Context, buff *bytes.Buffer) *claircore.Distribution {
92+
kv, err := osrelease.Parse(ctx, buff)
93+
if err != nil {
94+
zlog.Warn(ctx).Err(err).Msg("malformed os-release file")
95+
return nil
96+
}
97+
cpeName, cpeOK := kv["CPE_NAME"]
98+
if !cpeOK {
99+
return nil
100+
}
101+
// Instead of regexing through, we can grab the CPE.
102+
c, err := cpe.Unbind(cpeName)
103+
if err != nil {
104+
zlog.Warn(ctx).Err(err).Msg("could not unbind CPE")
105+
return nil
106+
}
107+
108+
d, err := cpeToDist(c)
109+
if err != nil {
110+
zlog.Warn(ctx).Err(err).Msg("error converting cpe to distribution")
111+
return nil
112+
}
113+
114+
return d
115+
}
116+
117+
func cpeToDist(r cpe.WFN) (*claircore.Distribution, error) {
118+
if vendor, err := cpe.NewValue("opensuse"); err == nil && r.Attr[cpe.Vendor] == vendor {
119+
if prod, err := cpe.NewValue("leap"); err == nil && r.Attr[cpe.Product] == prod {
120+
return mkLeapDist(r.String(), r.Attr[cpe.Version].String()), nil
121+
}
122+
}
123+
if vendor, err := cpe.NewValue("suse"); err == nil && r.Attr[cpe.Vendor] == vendor {
124+
if prod, err := cpe.NewValue("sles"); err == nil && r.Attr[cpe.Product] == prod {
125+
// Canonicalize the version to the major.
126+
v, err := semver.NewVersion(r.Attr[cpe.Version].String())
127+
if err != nil {
128+
return nil, err
129+
}
130+
return mkELDist(r.String(), fmt.Sprint(v.Major())), nil
118131
}
119132
}
120-
return nil
133+
return nil, nil
121134
}

suse/distributionscanner_test.go

+44-46
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package suse
22

33
import (
44
"bytes"
5+
"context"
56
"testing"
67

78
"github.com/google/go-cmp/cmp"
9+
"github.com/quay/claircore"
810
)
911

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

19-
var enterpriseServer12OSRelase []byte = []byte(`NAME="SLES"
21+
var enterpriseServer12OSRelease []byte = []byte(`NAME="SLES"
2022
VERSION="12-SP5"
2123
VERSION_ID="12.5"
2224
PRETTY_NAME="SUSE Linux Enterprise Server 12 SP5"
2325
ID="sles"
2426
ANSI_COLOR="0;32"
2527
CPE_NAME="cpe:/o:suse:sles:12:sp5"`)
2628

27-
var enterpriseServer11OSRelease []byte = []byte(`NAME="SLES"
28-
VERSION="11-SP5"
29-
VERSION_ID="11.2"
30-
PRETTY_NAME="SUSE Linux Enterprise Server 11 SP5"
31-
ID="sles"
32-
ANSI_COLOR="0;32"
33-
CPE_NAME="cpe:/o:suse:sles:12:sp5"`)
34-
35-
var leap151OSRelease []byte = []byte(`NAME="openSUSE Leap"
36-
VERSION="15.1"
37-
ID="opensuse-leap"
38-
ID_LIKE="suse opensuse"
39-
VERSION_ID="15.1"
40-
PRETTY_NAME="openSUSE Leap 15.1"
41-
ANSI_COLOR="0;32"
42-
CPE_NAME="cpe:/o:opensuse:leap:15.1"
43-
BUG_REPORT_URL="https://bugs.opensuse.org"
44-
HOME_URL="https://www.opensuse.org/"`)
45-
4629
var leap15OSRelease []byte = []byte(`NAME="openSUSE Leap"
4730
VERSION="15.0"
4831
ID="opensuse-leap"
@@ -54,60 +37,75 @@ CPE_NAME="cpe:/o:opensuse:leap:15.0"
5437
BUG_REPORT_URL="https://bugs.opensuse.org"
5538
HOME_URL="https://www.opensuse.org/"`)
5639

57-
var leap423OSRelease []byte = []byte(`NAME="openSUSE Leap"
58-
VERSION="42.3"
59-
ID=opensuse
60-
ID_LIKE="suse"
61-
VERSION_ID="42.3"
62-
PRETTY_NAME="openSUSE Leap 42.3"
40+
var leap151OSRelease []byte = []byte(`NAME="openSUSE Leap"
41+
VERSION="15.1"
42+
ID="opensuse-leap"
43+
ID_LIKE="suse opensuse"
44+
VERSION_ID="15.1"
45+
PRETTY_NAME="openSUSE Leap 15.1"
6346
ANSI_COLOR="0;32"
64-
CPE_NAME="cpe:/o:opensuse:leap:42.3"
47+
CPE_NAME="cpe:/o:opensuse:leap:15.1"
6548
BUG_REPORT_URL="https://bugs.opensuse.org"
6649
HOME_URL="https://www.opensuse.org/"`)
6750

6851
func TestDistributionScanner(t *testing.T) {
52+
ctx := context.Background()
6953
table := []struct {
7054
name string
71-
release Release
55+
dist *claircore.Distribution
7256
osRelease []byte
7357
}{
7458
{
7559
name: "enterprise server 15",
76-
release: EnterpriseServer15,
7760
osRelease: enterpriseServer15OSRelease,
61+
dist: &claircore.Distribution{
62+
DID: "sles",
63+
Name: "SLES",
64+
Version: "15",
65+
VersionID: "15",
66+
PrettyName: "SUSE Linux Enterprise Server 15",
67+
},
7868
},
7969
{
8070
name: "enterprise server 12",
81-
release: EnterpriseServer12,
82-
osRelease: enterpriseServer12OSRelase,
83-
},
84-
{
85-
name: "enterprise server 11",
86-
release: EnterpriseServer11,
87-
osRelease: enterpriseServer11OSRelease,
71+
osRelease: enterpriseServer12OSRelease,
72+
dist: &claircore.Distribution{
73+
DID: "sles",
74+
Name: "SLES",
75+
Version: "12",
76+
VersionID: "12",
77+
PrettyName: "SUSE Linux Enterprise Server 12",
78+
},
8879
},
8980
{
9081
name: "leap 15.0",
91-
release: Leap150,
9282
osRelease: leap15OSRelease,
83+
dist: &claircore.Distribution{
84+
DID: "opensuse-leap",
85+
Name: "openSUSE Leap",
86+
Version: "15.0",
87+
VersionID: "15.0",
88+
PrettyName: "openSUSE Leap 15.0",
89+
},
9390
},
9491
{
9592
name: "leap 15.1",
96-
release: Leap151,
9793
osRelease: leap151OSRelease,
98-
},
99-
{
100-
name: "leap 42.3",
101-
release: Leap423,
102-
osRelease: leap423OSRelease,
94+
dist: &claircore.Distribution{
95+
DID: "opensuse-leap",
96+
Name: "openSUSE Leap",
97+
Version: "15.1",
98+
VersionID: "15.1",
99+
PrettyName: "openSUSE Leap 15.1",
100+
},
103101
},
104102
}
105103
for _, tt := range table {
106104
t.Run(tt.name, func(t *testing.T) {
107105
scanner := DistributionScanner{}
108-
dist := scanner.parse(bytes.NewBuffer(tt.osRelease))
109-
if !cmp.Equal(dist, releaseToDist(tt.release)) {
110-
t.Fatalf("%v", cmp.Diff(dist, releaseToDist(tt.release)))
106+
dist := scanner.parse(ctx, bytes.NewBuffer(tt.osRelease))
107+
if !cmp.Equal(dist, tt.dist) {
108+
t.Fatalf("%v", cmp.Diff(dist, tt.dist))
111109
}
112110
})
113111
}

0 commit comments

Comments
 (0)