Skip to content

Commit

Permalink
ext: Lister and Detector returns detector info with detected content
Browse files Browse the repository at this point in the history
1. Every Lister and Detector are versioned
2. detected content, are returned in a map with detector info as the key
  • Loading branch information
KeyboardNerd committed Oct 8, 2018
1 parent 34d0e51 commit 53bf19a
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 104 deletions.
2 changes: 1 addition & 1 deletion ext/featurefmt/apk/apk.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
)

func init() {
featurefmt.RegisterLister("apk", dpkg.ParserName, &lister{})
featurefmt.RegisterLister("apk", "1.0", &lister{})
}

type lister struct{}
Expand Down
2 changes: 1 addition & 1 deletion ext/featurefmt/dpkg/dpkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var (
type lister struct{}

func init() {
featurefmt.RegisterLister("dpkg", dpkg.ParserName, &lister{})
featurefmt.RegisterLister("dpkg", "1.0", &lister{})
}

func (l lister) ListFeatures(files tarutil.FilesMap) ([]database.Feature, error) {
Expand Down
71 changes: 45 additions & 26 deletions ext/featurefmt/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ import (
)

var (
listersM sync.RWMutex
listers = make(map[string]Lister)
versionfmtListerName = make(map[string][]string)
listersM sync.RWMutex
listers = make(map[string]lister)
)

// Lister represents an ability to list the features present in an image layer.
Expand All @@ -48,13 +47,19 @@ type Lister interface {
RequiredFilenames() []string
}

type lister struct {
Lister

info database.Detector
}

// RegisterLister makes a Lister available by the provided name.
//
// If called twice with the same name, the name is blank, or if the provided
// Lister is nil, this function panics.
func RegisterLister(name string, versionfmt string, l Lister) {
if name == "" {
panic("featurefmt: could not register a Lister with an empty name")
func RegisterLister(name string, version string, l Lister) {
if name == "" || version == "" {
panic("featurefmt: could not register a Lister with an empty name or version")
}
if l == nil {
panic("featurefmt: could not register a nil Lister")
Expand All @@ -67,51 +72,65 @@ func RegisterLister(name string, versionfmt string, l Lister) {
panic("featurefmt: RegisterLister called twice for " + name)
}

listers[name] = l
versionfmtListerName[versionfmt] = append(versionfmtListerName[versionfmt], name)
listers[name] = lister{l, database.NewFeatureDetector(name, version)}
}

// ListFeatures produces the list of Features in an image layer using
// every registered Lister.
func ListFeatures(files tarutil.FilesMap, listerNames []string) ([]database.Feature, error) {
func ListFeatures(files tarutil.FilesMap, toUse []database.Detector) ([]database.LayerFeature, error) {
listersM.RLock()
defer listersM.RUnlock()

var totalFeatures []database.Feature
features := []database.LayerFeature{}
for _, d := range toUse {
// Only use the detector with the same type
if d.DType != database.FeatureDetectorType {
continue
}

for _, name := range listerNames {
if lister, ok := listers[name]; ok {
features, err := lister.ListFeatures(files)
if lister, ok := listers[d.Name]; ok {
fs, err := lister.ListFeatures(files)
if err != nil {
return []database.Feature{}, err
return nil, err
}
totalFeatures = append(totalFeatures, features...)

for _, f := range fs {
features = append(features, database.LayerFeature{
Feature: f,
By: lister.info,
})
}

} else {
log.WithField("Name", name).Warn("Unknown Lister")
log.WithField("Name", d).Fatal("unknown feature detector")
}
}

return totalFeatures, nil
return features, nil
}

// RequiredFilenames returns the total list of files required for all
// registered Listers.
func RequiredFilenames(listerNames []string) (files []string) {
// RequiredFilenames returns all files required by the give extensions. Any
// extension metadata that has non feature-detector type will be skipped.
func RequiredFilenames(toUse []database.Detector) (files []string) {
listersM.RLock()
defer listersM.RUnlock()

for _, lister := range listers {
files = append(files, lister.RequiredFilenames()...)
for _, d := range toUse {
if d.DType != database.FeatureDetectorType {
continue
}

files = append(files, listers[d.Name].RequiredFilenames()...)
}

return
}

// ListListers returns the names of all the registered feature listers.
func ListListers() []string {
r := []string{}
for name := range listers {
r = append(r, name)
func ListListers() []database.Detector {
r := []database.Detector{}
for _, d := range listers {
r = append(r, d.info)
}
return r
}
Expand Down
2 changes: 1 addition & 1 deletion ext/featurefmt/rpm/rpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (
type lister struct{}

func init() {
featurefmt.RegisterLister("rpm", rpm.ParserName, &lister{})
featurefmt.RegisterLister("rpm", "1.0", &lister{})
}

func (l lister) ListFeatures(files tarutil.FilesMap) ([]database.Feature, error) {
Expand Down
2 changes: 1 addition & 1 deletion ext/featurens/alpinerelease/alpinerelease.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const (
var versionRegexp = regexp.MustCompile(`^(\d)+\.(\d)+\.(\d)+$`)

func init() {
featurens.RegisterDetector("alpine-release", &detector{})
featurens.RegisterDetector("alpine-release", "1.0", &detector{})
}

type detector struct{}
Expand Down
2 changes: 1 addition & 1 deletion ext/featurens/aptsources/aptsources.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (
type detector struct{}

func init() {
featurens.RegisterDetector("apt-sources", &detector{})
featurens.RegisterDetector("apt-sources", "1.0", &detector{})
}

func (d detector) Detect(files tarutil.FilesMap) (*database.Namespace, error) {
Expand Down
75 changes: 45 additions & 30 deletions ext/featurens/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (

var (
detectorsM sync.RWMutex
detectors = make(map[string]Detector)
detectors = make(map[string]detector)
)

// Detector represents an ability to detect a namespace used for organizing
Expand All @@ -46,13 +46,19 @@ type Detector interface {
RequiredFilenames() []string
}

type detector struct {
Detector

info database.Detector
}

// RegisterDetector makes a detector available by the provided name.
//
// If called twice with the same name, the name is blank, or if the provided
// Detector is nil, this function panics.
func RegisterDetector(name string, d Detector) {
if name == "" {
panic("namespace: could not register a Detector with an empty name")
func RegisterDetector(name string, version string, d Detector) {
if name == "" || version == "" {
panic("namespace: could not register a Detector with an empty name or version")
}
if d == nil {
panic("namespace: could not register a nil Detector")
Expand All @@ -61,60 +67,69 @@ func RegisterDetector(name string, d Detector) {
detectorsM.Lock()
defer detectorsM.Unlock()

if _, dup := detectors[name]; dup {
if _, ok := detectors[name]; ok {
panic("namespace: RegisterDetector called twice for " + name)
}

detectors[name] = d
detectors[name] = detector{d, database.NewNamespaceDetector(name, version)}
}

// Detect iterators through all registered Detectors and returns all non-nil detected namespaces
func Detect(files tarutil.FilesMap, detectorNames []string) ([]database.Namespace, error) {
// Detect uses detectors specified to retrieve the detect result.
func Detect(files tarutil.FilesMap, toUse []database.Detector) ([]database.LayerNamespace, error) {
detectorsM.RLock()
defer detectorsM.RUnlock()
namespaces := map[string]*database.Namespace{}
for _, name := range detectorNames {
if detector, ok := detectors[name]; ok {

namespaces := []database.LayerNamespace{}
for _, d := range toUse {
// Only use the detector with the same type
if d.DType != database.NamespaceDetectorType {
continue
}

if detector, ok := detectors[d.Name]; ok {
namespace, err := detector.Detect(files)
if err != nil {
log.WithError(err).WithField("name", name).Warning("failed while attempting to detect namespace")
log.WithError(err).WithField("detector", d).Warning("failed while attempting to detect namespace")
return nil, err
}

if namespace != nil {
log.WithFields(log.Fields{"name": name, "namespace": namespace.Name}).Debug("detected namespace")
namespaces[namespace.Name] = namespace
log.WithFields(log.Fields{"detector": d, "namespace": namespace.Name}).Debug("detected namespace")
namespaces = append(namespaces, database.LayerNamespace{
Namespace: *namespace,
By: detector.info,
})
}
} else {
log.WithField("Name", name).Warn("Unknown namespace detector")
log.WithField("detector", d).Fatal("unknown namespace detector")
}
}

nslist := []database.Namespace{}
for _, ns := range namespaces {
nslist = append(nslist, *ns)
}
return nslist, nil
return namespaces, nil
}

// RequiredFilenames returns the total list of files required for all
// registered Detectors.
func RequiredFilenames(detectorNames []string) (files []string) {
// RequiredFilenames returns all files required by the give extensions. Any
// extension metadata that has non namespace-detector type will be skipped.
func RequiredFilenames(toUse []database.Detector) (files []string) {
detectorsM.RLock()
defer detectorsM.RUnlock()

for _, detector := range detectors {
files = append(files, detector.RequiredFilenames()...)
for _, d := range toUse {
if d.DType != database.NamespaceDetectorType {
continue
}

files = append(files, detectors[d.Name].RequiredFilenames()...)
}

return
}

// ListDetectors returns the names of all registered namespace detectors.
func ListDetectors() []string {
r := []string{}
for name := range detectors {
r = append(r, name)
// ListDetectors returns the info of all registered namespace detectors.
func ListDetectors() []database.Detector {
r := make([]database.Detector, 0, len(detectors))
for _, d := range detectors {
r = append(r, d.info)
}
return r
}
Expand Down
63 changes: 26 additions & 37 deletions ext/featurens/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/coreos/clair/database"
"github.com/coreos/clair/ext/featurens"
"github.com/coreos/clair/pkg/tarutil"
"github.com/coreos/clair/pkg/testutil"

_ "github.com/coreos/clair/ext/featurens/alpinerelease"
_ "github.com/coreos/clair/ext/featurens/aptsources"
Expand All @@ -16,40 +17,14 @@ import (
_ "github.com/coreos/clair/ext/featurens/redhatrelease"
)

type MultipleNamespaceTestData struct {
Files tarutil.FilesMap
ExpectedNamespaces []database.Namespace
}

func assertnsNameEqual(t *testing.T, nslist_expected, nslist []database.Namespace) {
assert.Equal(t, len(nslist_expected), len(nslist))
expected := map[string]struct{}{}
input := map[string]struct{}{}
// compare the two sets
for i := range nslist_expected {
expected[nslist_expected[i].Name] = struct{}{}
input[nslist[i].Name] = struct{}{}
}
assert.Equal(t, expected, input)
}

func testMultipleNamespace(t *testing.T, testData []MultipleNamespaceTestData) {
for _, td := range testData {
nslist, err := featurens.Detect(td.Files, featurens.ListDetectors())
assert.Nil(t, err)
assertnsNameEqual(t, td.ExpectedNamespaces, nslist)
}
}

func TestMultipleNamespaceDetector(t *testing.T) {
testData := []MultipleNamespaceTestData{
{
ExpectedNamespaces: []database.Namespace{
{Name: "debian:8", VersionFormat: "dpkg"},
{Name: "alpine:v3.3", VersionFormat: "dpkg"},
},
Files: tarutil.FilesMap{
"etc/os-release": []byte(`
var namespaceDetectorTests = []struct {
in tarutil.FilesMap
out []database.LayerNamespace
err string
}{
{
in: tarutil.FilesMap{
"etc/os-release": []byte(`
PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
NAME="Debian GNU/Linux"
VERSION_ID="8"
Expand All @@ -58,9 +33,23 @@ ID=debian
HOME_URL="http://www.debian.org/"
SUPPORT_URL="http://www.debian.org/support/"
BUG_REPORT_URL="https://bugs.debian.org/"`),
"etc/alpine-release": []byte(`3.3.4`),
},
"etc/alpine-release": []byte(`3.3.4`),
},
out: []database.LayerNamespace{
{database.Namespace{"debian:8", "dpkg"}, database.NewNamespaceDetector("os-release", "1.0")},
{database.Namespace{"alpine:v3.3", "dpkg"}, database.NewNamespaceDetector("alpine-release", "1.0")},
},
},
}

func TestNamespaceDetector(t *testing.T) {
for _, test := range namespaceDetectorTests {
out, err := featurens.Detect(test.in, featurens.ListDetectors())
if test.err != "" {
assert.EqualError(t, err, test.err)
return
}

testutil.AssertLayerNamespacesEqual(t, test.out, out)
}
testMultipleNamespace(t, testData)
}
2 changes: 1 addition & 1 deletion ext/featurens/lsbrelease/lsbrelease.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var (
type detector struct{}

func init() {
featurens.RegisterDetector("lsb-release", &detector{})
featurens.RegisterDetector("lsb-release", "1.0", &detector{})
}

func (d detector) Detect(files tarutil.FilesMap) (*database.Namespace, error) {
Expand Down
2 changes: 1 addition & 1 deletion ext/featurens/osrelease/osrelease.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ var (
type detector struct{}

func init() {
featurens.RegisterDetector("os-release", &detector{})
featurens.RegisterDetector("os-release", "1.0", &detector{})
}

func (d detector) Detect(files tarutil.FilesMap) (*database.Namespace, error) {
Expand Down
Loading

0 comments on commit 53bf19a

Please sign in to comment.