ext: namespace detector -> featurens
This commit is contained in:
parent
d9be34c3c4
commit
fb193e1fde
@ -26,6 +26,11 @@ import (
|
|||||||
"github.com/coreos/clair/config"
|
"github.com/coreos/clair/config"
|
||||||
|
|
||||||
// Register extensions.
|
// Register extensions.
|
||||||
|
_ "github.com/coreos/clair/ext/featurens/alpinerelease"
|
||||||
|
_ "github.com/coreos/clair/ext/featurens/aptsources"
|
||||||
|
_ "github.com/coreos/clair/ext/featurens/lsbrelease"
|
||||||
|
_ "github.com/coreos/clair/ext/featurens/osrelease"
|
||||||
|
_ "github.com/coreos/clair/ext/featurens/redhatrelease"
|
||||||
_ "github.com/coreos/clair/ext/imagefmt/aci"
|
_ "github.com/coreos/clair/ext/imagefmt/aci"
|
||||||
_ "github.com/coreos/clair/ext/imagefmt/docker"
|
_ "github.com/coreos/clair/ext/imagefmt/docker"
|
||||||
_ "github.com/coreos/clair/ext/notification/webhook"
|
_ "github.com/coreos/clair/ext/notification/webhook"
|
||||||
@ -40,12 +45,6 @@ import (
|
|||||||
_ "github.com/coreos/clair/worker/detectors/feature/dpkg"
|
_ "github.com/coreos/clair/worker/detectors/feature/dpkg"
|
||||||
_ "github.com/coreos/clair/worker/detectors/feature/rpm"
|
_ "github.com/coreos/clair/worker/detectors/feature/rpm"
|
||||||
|
|
||||||
_ "github.com/coreos/clair/worker/detectors/namespace/alpinerelease"
|
|
||||||
_ "github.com/coreos/clair/worker/detectors/namespace/aptsources"
|
|
||||||
_ "github.com/coreos/clair/worker/detectors/namespace/lsbrelease"
|
|
||||||
_ "github.com/coreos/clair/worker/detectors/namespace/osrelease"
|
|
||||||
_ "github.com/coreos/clair/worker/detectors/namespace/redhatrelease"
|
|
||||||
|
|
||||||
_ "github.com/coreos/clair/database/pgsql"
|
_ "github.com/coreos/clair/database/pgsql"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 clair authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -12,6 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package alpinerelease implements a featurens.Detector for Alpine Linux based
|
||||||
|
// container image layers.
|
||||||
package alpinerelease
|
package alpinerelease
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -21,8 +23,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
|
"github.com/coreos/clair/ext/featurens"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/worker/detectors"
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -33,15 +36,13 @@ const (
|
|||||||
var versionRegexp = regexp.MustCompile(`^(\d)+\.(\d)+\.(\d)+$`)
|
var versionRegexp = regexp.MustCompile(`^(\d)+\.(\d)+\.(\d)+$`)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
detectors.RegisterNamespaceDetector("alpine-release", &detector{})
|
featurens.RegisterDetector("alpine-release", &detector{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// detector implements NamespaceDetector by reading the current version of
|
|
||||||
// Alpine Linux from /etc/alpine-release.
|
|
||||||
type detector struct{}
|
type detector struct{}
|
||||||
|
|
||||||
func (d *detector) Detect(data map[string][]byte) *database.Namespace {
|
func (d detector) Detect(files tarutil.FilesMap) (*database.Namespace, error) {
|
||||||
file, exists := data[alpineReleasePath]
|
file, exists := files[alpineReleasePath]
|
||||||
if exists {
|
if exists {
|
||||||
scanner := bufio.NewScanner(bytes.NewBuffer(file))
|
scanner := bufio.NewScanner(bytes.NewBuffer(file))
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
@ -52,14 +53,14 @@ func (d *detector) Detect(data map[string][]byte) *database.Namespace {
|
|||||||
return &database.Namespace{
|
return &database.Namespace{
|
||||||
Name: osName + ":" + "v" + versionNumbers[0] + "." + versionNumbers[1],
|
Name: osName + ":" + "v" + versionNumbers[0] + "." + versionNumbers[1],
|
||||||
VersionFormat: dpkg.ParserName,
|
VersionFormat: dpkg.ParserName,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *detector) GetRequiredFiles() []string {
|
func (d detector) RequiredFilenames() []string {
|
||||||
return []string{alpineReleasePath}
|
return []string{alpineReleasePath}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 clair authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -18,34 +18,35 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/worker/detectors/namespace"
|
"github.com/coreos/clair/ext/featurens"
|
||||||
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAlpineReleaseNamespaceDetection(t *testing.T) {
|
func TestDetector(t *testing.T) {
|
||||||
testData := []namespace.TestData{
|
testData := []featurens.TestData{
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "alpine:v3.3"},
|
ExpectedNamespace: &database.Namespace{Name: "alpine:v3.3"},
|
||||||
Data: map[string][]byte{"etc/alpine-release": []byte(`3.3.4`)},
|
Files: tarutil.FilesMap{"etc/alpine-release": []byte(`3.3.4`)},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "alpine:v3.4"},
|
ExpectedNamespace: &database.Namespace{Name: "alpine:v3.4"},
|
||||||
Data: map[string][]byte{"etc/alpine-release": []byte(`3.4.0`)},
|
Files: tarutil.FilesMap{"etc/alpine-release": []byte(`3.4.0`)},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "alpine:v0.3"},
|
ExpectedNamespace: &database.Namespace{Name: "alpine:v0.3"},
|
||||||
Data: map[string][]byte{"etc/alpine-release": []byte(`0.3.4`)},
|
Files: tarutil.FilesMap{"etc/alpine-release": []byte(`0.3.4`)},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "alpine:v0.3"},
|
ExpectedNamespace: &database.Namespace{Name: "alpine:v0.3"},
|
||||||
Data: map[string][]byte{"etc/alpine-release": []byte(`
|
Files: tarutil.FilesMap{"etc/alpine-release": []byte(`
|
||||||
0.3.4
|
0.3.4
|
||||||
`)},
|
`)},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ExpectedNamespace: nil,
|
ExpectedNamespace: nil,
|
||||||
Data: map[string][]byte{},
|
Files: tarutil.FilesMap{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace.TestDetector(t, &detector{}, testData)
|
featurens.TestDetector(t, &detector{}, testData)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 clair authors
|
// Copyright 2017 clair authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -12,6 +12,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package aptsources implements a featurens.Detector for apt based container
|
||||||
|
// image layers.
|
||||||
|
//
|
||||||
|
// This detector is necessary to determine the precise Debian version when it
|
||||||
|
// is an unstable version for instance.
|
||||||
package aptsources
|
package aptsources
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -19,25 +24,21 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
|
"github.com/coreos/clair/ext/featurens"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/worker/detectors"
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AptSourcesNamespaceDetector implements NamespaceDetector and detects the Namespace from the
|
type detector struct{}
|
||||||
// /etc/apt/sources.list file.
|
|
||||||
//
|
|
||||||
// This detector is necessary to determine precise Debian version when it is
|
|
||||||
// an unstable version for instance.
|
|
||||||
type AptSourcesNamespaceDetector struct{}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
detectors.RegisterNamespaceDetector("apt-sources", &AptSourcesNamespaceDetector{})
|
featurens.RegisterDetector("apt-sources", &detector{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (detector *AptSourcesNamespaceDetector) Detect(data map[string][]byte) *database.Namespace {
|
func (d detector) Detect(files tarutil.FilesMap) (*database.Namespace, error) {
|
||||||
f, hasFile := data["etc/apt/sources.list"]
|
f, hasFile := files["etc/apt/sources.list"]
|
||||||
if !hasFile {
|
if !hasFile {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var OS, version string
|
var OS, version string
|
||||||
@ -79,11 +80,11 @@ func (detector *AptSourcesNamespaceDetector) Detect(data map[string][]byte) *dat
|
|||||||
return &database.Namespace{
|
return &database.Namespace{
|
||||||
Name: OS + ":" + version,
|
Name: OS + ":" + version,
|
||||||
VersionFormat: dpkg.ParserName,
|
VersionFormat: dpkg.ParserName,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (detector *AptSourcesNamespaceDetector) GetRequiredFiles() []string {
|
func (d detector) RequiredFilenames() []string {
|
||||||
return []string{"etc/apt/sources.list"}
|
return []string{"etc/apt/sources.list"}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 clair authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -18,14 +18,15 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/worker/detectors/namespace"
|
"github.com/coreos/clair/ext/featurens"
|
||||||
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAptSourcesNamespaceDetector(t *testing.T) {
|
func TestDetector(t *testing.T) {
|
||||||
testData := []namespace.TestData{
|
testData := []featurens.TestData{
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "debian:unstable"},
|
ExpectedNamespace: &database.Namespace{Name: "debian:unstable"},
|
||||||
Data: map[string][]byte{
|
Files: tarutil.FilesMap{
|
||||||
"etc/os-release": []byte(
|
"etc/os-release": []byte(
|
||||||
`PRETTY_NAME="Debian GNU/Linux stretch/sid"
|
`PRETTY_NAME="Debian GNU/Linux stretch/sid"
|
||||||
NAME="Debian GNU/Linux"
|
NAME="Debian GNU/Linux"
|
||||||
@ -38,5 +39,5 @@ BUG_REPORT_URL="https://bugs.debian.org/"`),
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace.TestDetector(t, &AptSourcesNamespaceDetector{}, testData)
|
featurens.TestDetector(t, &detector{}, testData)
|
||||||
}
|
}
|
124
ext/featurens/driver.go
Normal file
124
ext/featurens/driver.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// Copyright 2017 clair authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package featurens exposes functions to dynamically register methods for
|
||||||
|
// determining a namespace for features present in an image layer.
|
||||||
|
package featurens
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/coreos/clair/database"
|
||||||
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
log = capnslog.NewPackageLogger("github.com/coreos/clair", "ext/featurens")
|
||||||
|
|
||||||
|
detectorsM sync.RWMutex
|
||||||
|
detectors = make(map[string]Detector)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Detector represents an ability to detect a namespace used for organizing
|
||||||
|
// features present in an image layer.
|
||||||
|
type Detector interface {
|
||||||
|
// Detect attempts to determine a Namespace from a FilesMap of an image
|
||||||
|
// layer.
|
||||||
|
Detect(tarutil.FilesMap) (*database.Namespace, error)
|
||||||
|
|
||||||
|
// RequireFilenames returns the list of files required to be in the FilesMap
|
||||||
|
// provided to the Detect method.
|
||||||
|
// TODO(jzelinskie): strip "/" prefix
|
||||||
|
RequiredFilenames() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
if d == nil {
|
||||||
|
panic("namespace: could not register a nil Detector")
|
||||||
|
}
|
||||||
|
|
||||||
|
detectorsM.Lock()
|
||||||
|
defer detectorsM.Unlock()
|
||||||
|
|
||||||
|
if _, dup := detectors[name]; dup {
|
||||||
|
panic("namespace: RegisterDetector called twice for " + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
detectors[name] = d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect iterators through all registered Detectors and returns the first
|
||||||
|
// non-nil detected namespace.
|
||||||
|
func Detect(files tarutil.FilesMap) (*database.Namespace, error) {
|
||||||
|
detectorsM.RLock()
|
||||||
|
defer detectorsM.RUnlock()
|
||||||
|
|
||||||
|
for name, detector := range detectors {
|
||||||
|
namespace, err := detector.Detect(files)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed while attempting to detect namespace %s: %s", name, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if namespace != nil {
|
||||||
|
log.Debugf("detected namespace %s: %#v", name, namespace)
|
||||||
|
return namespace, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequiredFilenames returns the total list of files required for all
|
||||||
|
// registered Detectors.
|
||||||
|
func RequiredFilenames() (files []string) {
|
||||||
|
for _, detector := range detectors {
|
||||||
|
files = append(files, detector.RequiredFilenames()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestData represents the data used to test an implementation of
|
||||||
|
// NameSpaceDetector.
|
||||||
|
type TestData struct {
|
||||||
|
Files tarutil.FilesMap
|
||||||
|
ExpectedNamespace *database.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDetector runs a Detector on each provided instance of TestData and
|
||||||
|
// asserts the output to be equal to the expected output.
|
||||||
|
func TestDetector(t *testing.T, d Detector, testData []TestData) {
|
||||||
|
for _, td := range testData {
|
||||||
|
namespace, err := d.Detect(td.Files)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
if namespace == nil {
|
||||||
|
assert.Equal(t, td.ExpectedNamespace, namespace)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, td.ExpectedNamespace.Name, namespace.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 clair authors
|
// Copyright 2017 clair authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -12,6 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package lsbrelease implements a featurens.Detector for container image
|
||||||
|
// layers containing an lsb-release file.
|
||||||
|
//
|
||||||
|
// This detector is necessary for detecting Ubuntu Precise.
|
||||||
package lsbrelease
|
package lsbrelease
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -20,9 +24,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
|
"github.com/coreos/clair/ext/featurens"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/ext/versionfmt/rpm"
|
"github.com/coreos/clair/ext/versionfmt/rpm"
|
||||||
"github.com/coreos/clair/worker/detectors"
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -30,20 +35,16 @@ var (
|
|||||||
lsbReleaseVersionRegexp = regexp.MustCompile(`^DISTRIB_RELEASE=(.*)`)
|
lsbReleaseVersionRegexp = regexp.MustCompile(`^DISTRIB_RELEASE=(.*)`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// LsbReleaseNamespaceDetector implements NamespaceDetector and detects the
|
type detector struct{}
|
||||||
// Namespace from the /etc/lsb-release file.
|
|
||||||
//
|
|
||||||
// This detector is necessary for Ubuntu Precise.
|
|
||||||
type LsbReleaseNamespaceDetector struct{}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
detectors.RegisterNamespaceDetector("lsb-release", &LsbReleaseNamespaceDetector{})
|
featurens.RegisterDetector("lsb-release", &detector{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (detector *LsbReleaseNamespaceDetector) Detect(data map[string][]byte) *database.Namespace {
|
func (d detector) Detect(files tarutil.FilesMap) (*database.Namespace, error) {
|
||||||
f, hasFile := data["etc/lsb-release"]
|
f, hasFile := files["etc/lsb-release"]
|
||||||
if !hasFile {
|
if !hasFile {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var OS, version string
|
var OS, version string
|
||||||
@ -79,19 +80,19 @@ func (detector *LsbReleaseNamespaceDetector) Detect(data map[string][]byte) *dat
|
|||||||
case "centos", "rhel", "fedora", "amzn", "ol", "oracle":
|
case "centos", "rhel", "fedora", "amzn", "ol", "oracle":
|
||||||
versionFormat = rpm.ParserName
|
versionFormat = rpm.ParserName
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if OS != "" && version != "" {
|
if OS != "" && version != "" {
|
||||||
return &database.Namespace{
|
return &database.Namespace{
|
||||||
Name: OS + ":" + version,
|
Name: OS + ":" + version,
|
||||||
VersionFormat: versionFormat,
|
VersionFormat: versionFormat,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRequiredFiles returns the list of files that are required for Detect()
|
func (d *detector) RequiredFilenames() []string {
|
||||||
func (detector *LsbReleaseNamespaceDetector) GetRequiredFiles() []string {
|
|
||||||
return []string{"etc/lsb-release"}
|
return []string{"etc/lsb-release"}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 clair authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -18,14 +18,15 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/worker/detectors/namespace"
|
"github.com/coreos/clair/ext/featurens"
|
||||||
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLsbReleaseNamespaceDetector(t *testing.T) {
|
func TestDetector(t *testing.T) {
|
||||||
testData := []namespace.TestData{
|
testData := []featurens.TestData{
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "ubuntu:12.04"},
|
ExpectedNamespace: &database.Namespace{Name: "ubuntu:12.04"},
|
||||||
Data: map[string][]byte{
|
Files: tarutil.FilesMap{
|
||||||
"etc/lsb-release": []byte(
|
"etc/lsb-release": []byte(
|
||||||
`DISTRIB_ID=Ubuntu
|
`DISTRIB_ID=Ubuntu
|
||||||
DISTRIB_RELEASE=12.04
|
DISTRIB_RELEASE=12.04
|
||||||
@ -35,7 +36,7 @@ DISTRIB_DESCRIPTION="Ubuntu 12.04 LTS"`),
|
|||||||
},
|
},
|
||||||
{ // We don't care about the minor version of Debian
|
{ // We don't care about the minor version of Debian
|
||||||
ExpectedNamespace: &database.Namespace{Name: "debian:7"},
|
ExpectedNamespace: &database.Namespace{Name: "debian:7"},
|
||||||
Data: map[string][]byte{
|
Files: tarutil.FilesMap{
|
||||||
"etc/lsb-release": []byte(
|
"etc/lsb-release": []byte(
|
||||||
`DISTRIB_ID=Debian
|
`DISTRIB_ID=Debian
|
||||||
DISTRIB_RELEASE=7.1
|
DISTRIB_RELEASE=7.1
|
||||||
@ -45,5 +46,5 @@ DISTRIB_DESCRIPTION="Debian 7.1"`),
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace.TestDetector(t, &LsbReleaseNamespaceDetector{}, testData)
|
featurens.TestDetector(t, &detector{}, testData)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 clair authors
|
// Copyright 2017 clair authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -12,6 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package osrelease implements a featurens.Detector for container image
|
||||||
|
// layers containing an os-release file.
|
||||||
|
//
|
||||||
|
// This detector is typically useful for detecting Debian or Ubuntu.
|
||||||
package osrelease
|
package osrelease
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -20,40 +24,41 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
|
"github.com/coreos/clair/ext/featurens"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/ext/versionfmt/rpm"
|
"github.com/coreos/clair/ext/versionfmt/rpm"
|
||||||
"github.com/coreos/clair/worker/detectors"
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
//log = capnslog.NewPackageLogger("github.com/coreos/clair", "worker/detectors/namespace/osrelease")
|
|
||||||
|
|
||||||
osReleaseOSRegexp = regexp.MustCompile(`^ID=(.*)`)
|
osReleaseOSRegexp = regexp.MustCompile(`^ID=(.*)`)
|
||||||
osReleaseVersionRegexp = regexp.MustCompile(`^VERSION_ID=(.*)`)
|
osReleaseVersionRegexp = regexp.MustCompile(`^VERSION_ID=(.*)`)
|
||||||
|
|
||||||
|
// blacklistFilenames are files that should exclude this detector.
|
||||||
|
blacklistFilenames = []string{
|
||||||
|
"etc/oracle-release",
|
||||||
|
"etc/redhat-release",
|
||||||
|
"usr/lib/centos-release",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// OsReleaseNamespaceDetector implements NamespaceDetector and detects the OS from the
|
type detector struct{}
|
||||||
// /etc/os-release and usr/lib/os-release files.
|
|
||||||
type OsReleaseNamespaceDetector struct{}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
detectors.RegisterNamespaceDetector("os-release", &OsReleaseNamespaceDetector{})
|
featurens.RegisterDetector("os-release", &detector{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect tries to detect OS/Version using "/etc/os-release" and "/usr/lib/os-release"
|
func (d detector) Detect(files tarutil.FilesMap) (*database.Namespace, error) {
|
||||||
// Typically for Debian / Ubuntu
|
|
||||||
// /etc/debian_version can't be used, it does not make any difference between testing and unstable, it returns stretch/sid
|
|
||||||
func (detector *OsReleaseNamespaceDetector) Detect(data map[string][]byte) *database.Namespace {
|
|
||||||
var OS, version string
|
var OS, version string
|
||||||
|
|
||||||
for _, filePath := range detector.getExcludeFiles() {
|
for _, filePath := range blacklistFilenames {
|
||||||
if _, hasFile := data[filePath]; hasFile {
|
if _, hasFile := files[filePath]; hasFile {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, filePath := range detector.GetRequiredFiles() {
|
for _, filePath := range d.RequiredFilenames() {
|
||||||
f, hasFile := data[filePath]
|
f, hasFile := files[filePath]
|
||||||
if !hasFile {
|
if !hasFile {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -82,24 +87,18 @@ func (detector *OsReleaseNamespaceDetector) Detect(data map[string][]byte) *data
|
|||||||
case "centos", "rhel", "fedora", "amzn", "ol", "oracle":
|
case "centos", "rhel", "fedora", "amzn", "ol", "oracle":
|
||||||
versionFormat = rpm.ParserName
|
versionFormat = rpm.ParserName
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if OS != "" && version != "" {
|
if OS != "" && version != "" {
|
||||||
return &database.Namespace{
|
return &database.Namespace{
|
||||||
Name: OS + ":" + version,
|
Name: OS + ":" + version,
|
||||||
VersionFormat: versionFormat,
|
VersionFormat: versionFormat,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRequiredFiles returns the list of files that are required for Detect()
|
func (d detector) RequiredFilenames() []string {
|
||||||
func (detector *OsReleaseNamespaceDetector) GetRequiredFiles() []string {
|
|
||||||
return []string{"etc/os-release", "usr/lib/os-release"}
|
return []string{"etc/os-release", "usr/lib/os-release"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getExcludeFiles returns the list of files that are ought to exclude this detector from Detect()
|
|
||||||
func (detector *OsReleaseNamespaceDetector) getExcludeFiles() []string {
|
|
||||||
return []string{"etc/oracle-release", "etc/redhat-release", "usr/lib/centos-release"}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 clair authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -18,14 +18,15 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/worker/detectors/namespace"
|
"github.com/coreos/clair/ext/featurens"
|
||||||
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOsReleaseNamespaceDetector(t *testing.T) {
|
func TestDetector(t *testing.T) {
|
||||||
testData := []namespace.TestData{
|
testData := []featurens.TestData{
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "debian:8"},
|
ExpectedNamespace: &database.Namespace{Name: "debian:8"},
|
||||||
Data: map[string][]byte{
|
Files: tarutil.FilesMap{
|
||||||
"etc/os-release": []byte(
|
"etc/os-release": []byte(
|
||||||
`PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
|
`PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
|
||||||
NAME="Debian GNU/Linux"
|
NAME="Debian GNU/Linux"
|
||||||
@ -39,7 +40,7 @@ BUG_REPORT_URL="https://bugs.debian.org/"`),
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "ubuntu:15.10"},
|
ExpectedNamespace: &database.Namespace{Name: "ubuntu:15.10"},
|
||||||
Data: map[string][]byte{
|
Files: tarutil.FilesMap{
|
||||||
"etc/os-release": []byte(
|
"etc/os-release": []byte(
|
||||||
`NAME="Ubuntu"
|
`NAME="Ubuntu"
|
||||||
VERSION="15.10 (Wily Werewolf)"
|
VERSION="15.10 (Wily Werewolf)"
|
||||||
@ -54,7 +55,7 @@ BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`),
|
|||||||
},
|
},
|
||||||
{ // Doesn't have quotes around VERSION_ID
|
{ // Doesn't have quotes around VERSION_ID
|
||||||
ExpectedNamespace: &database.Namespace{Name: "fedora:20"},
|
ExpectedNamespace: &database.Namespace{Name: "fedora:20"},
|
||||||
Data: map[string][]byte{
|
Files: tarutil.FilesMap{
|
||||||
"etc/os-release": []byte(
|
"etc/os-release": []byte(
|
||||||
`NAME=Fedora
|
`NAME=Fedora
|
||||||
VERSION="20 (Heisenbug)"
|
VERSION="20 (Heisenbug)"
|
||||||
@ -73,5 +74,5 @@ REDHAT_SUPPORT_PRODUCT_VERSION=20`),
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace.TestDetector(t, &OsReleaseNamespaceDetector{}, testData)
|
featurens.TestDetector(t, &detector{}, testData)
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 clair authors
|
// Copyright 2017 clair authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -12,6 +12,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package redhatrelease implements a featurens.Detector for container image
|
||||||
|
// layers containing an redhat-release-like files.
|
||||||
|
//
|
||||||
|
// This detector is typically useful for detecting CentOS and Red-Hat like
|
||||||
|
// systems.
|
||||||
package redhatrelease
|
package redhatrelease
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -19,77 +24,64 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
|
"github.com/coreos/clair/ext/featurens"
|
||||||
"github.com/coreos/clair/ext/versionfmt/rpm"
|
"github.com/coreos/clair/ext/versionfmt/rpm"
|
||||||
"github.com/coreos/clair/worker/detectors"
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
"github.com/coreos/pkg/capnslog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
log = capnslog.NewPackageLogger("github.com/coreos/clair", "worker/detectors/namespace/redhatrelease")
|
|
||||||
|
|
||||||
oracleReleaseRegexp = regexp.MustCompile(`(?P<os>[^\s]*) (Linux Server release) (?P<version>[\d]+)`)
|
oracleReleaseRegexp = regexp.MustCompile(`(?P<os>[^\s]*) (Linux Server release) (?P<version>[\d]+)`)
|
||||||
centosReleaseRegexp = regexp.MustCompile(`(?P<os>[^\s]*) (Linux release|release) (?P<version>[\d]+)`)
|
centosReleaseRegexp = regexp.MustCompile(`(?P<os>[^\s]*) (Linux release|release) (?P<version>[\d]+)`)
|
||||||
redhatReleaseRegexp = regexp.MustCompile(`(?P<os>Red Hat Enterprise Linux) (Client release|Server release|Workstation release) (?P<version>[\d]+)`)
|
redhatReleaseRegexp = regexp.MustCompile(`(?P<os>Red Hat Enterprise Linux) (Client release|Server release|Workstation release) (?P<version>[\d]+)`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// RedhatReleaseNamespaceDetector implements NamespaceDetector and detects the OS from the
|
type detector struct{}
|
||||||
// /etc/oracle-release, /etc/centos-release, /etc/redhat-release and /etc/system-release files.
|
|
||||||
//
|
|
||||||
// Typically for CentOS and Red-Hat like systems
|
|
||||||
// eg. CentOS release 5.11 (Final)
|
|
||||||
// eg. CentOS release 6.6 (Final)
|
|
||||||
// eg. CentOS Linux release 7.1.1503 (Core)
|
|
||||||
// eg. Oracle Linux Server release 7.3
|
|
||||||
// eg. Red Hat Enterprise Linux Server release 7.2 (Maipo)
|
|
||||||
type RedhatReleaseNamespaceDetector struct{}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
detectors.RegisterNamespaceDetector("redhat-release", &RedhatReleaseNamespaceDetector{})
|
featurens.RegisterDetector("redhat-release", &detector{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (detector *RedhatReleaseNamespaceDetector) Detect(data map[string][]byte) *database.Namespace {
|
func (d detector) Detect(files tarutil.FilesMap) (*database.Namespace, error) {
|
||||||
for _, filePath := range detector.GetRequiredFiles() {
|
for _, filePath := range d.RequiredFilenames() {
|
||||||
f, hasFile := data[filePath]
|
f, hasFile := files[filePath]
|
||||||
if !hasFile {
|
if !hasFile {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var r []string
|
var r []string
|
||||||
|
|
||||||
// try for Oracle Linux
|
// Attempt to match Oracle Linux.
|
||||||
r = oracleReleaseRegexp.FindStringSubmatch(string(f))
|
r = oracleReleaseRegexp.FindStringSubmatch(string(f))
|
||||||
if len(r) == 4 {
|
if len(r) == 4 {
|
||||||
return &database.Namespace{
|
return &database.Namespace{
|
||||||
Name: strings.ToLower(r[1]) + ":" + r[3],
|
Name: strings.ToLower(r[1]) + ":" + r[3],
|
||||||
VersionFormat: rpm.ParserName,
|
VersionFormat: rpm.ParserName,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// try for RHEL
|
// Attempt to match RHEL.
|
||||||
r = redhatReleaseRegexp.FindStringSubmatch(string(f))
|
r = redhatReleaseRegexp.FindStringSubmatch(string(f))
|
||||||
if len(r) == 4 {
|
if len(r) == 4 {
|
||||||
// TODO(vbatts) this is a hack until https://github.com/coreos/clair/pull/193
|
// TODO(vbatts): this is a hack until https://github.com/coreos/clair/pull/193
|
||||||
return &database.Namespace{
|
return &database.Namespace{
|
||||||
Name: "centos" + ":" + r[3],
|
Name: "centos" + ":" + r[3],
|
||||||
VersionFormat: rpm.ParserName,
|
VersionFormat: rpm.ParserName,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// then try centos first
|
// Atempt to match CentOS.
|
||||||
r = centosReleaseRegexp.FindStringSubmatch(string(f))
|
r = centosReleaseRegexp.FindStringSubmatch(string(f))
|
||||||
if len(r) == 4 {
|
if len(r) == 4 {
|
||||||
return &database.Namespace{
|
return &database.Namespace{
|
||||||
Name: strings.ToLower(r[1]) + ":" + r[3],
|
Name: strings.ToLower(r[1]) + ":" + r[3],
|
||||||
VersionFormat: rpm.ParserName,
|
VersionFormat: rpm.ParserName,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRequiredFiles returns the list of files that are required for Detect()
|
func (d detector) RequiredFilenames() []string {
|
||||||
func (detector *RedhatReleaseNamespaceDetector) GetRequiredFiles() []string {
|
|
||||||
return []string{"etc/oracle-release", "etc/centos-release", "etc/redhat-release", "etc/system-release"}
|
return []string{"etc/oracle-release", "etc/centos-release", "etc/redhat-release", "etc/system-release"}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 clair authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -18,36 +18,37 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/worker/detectors/namespace"
|
"github.com/coreos/clair/ext/featurens"
|
||||||
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRedhatReleaseNamespaceDetector(t *testing.T) {
|
func TestDetector(t *testing.T) {
|
||||||
testData := []namespace.TestData{
|
testData := []featurens.TestData{
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "oracle:6"},
|
ExpectedNamespace: &database.Namespace{Name: "oracle:6"},
|
||||||
Data: map[string][]byte{
|
Files: tarutil.FilesMap{
|
||||||
"etc/oracle-release": []byte(`Oracle Linux Server release 6.8`),
|
"etc/oracle-release": []byte(`Oracle Linux Server release 6.8`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "oracle:7"},
|
ExpectedNamespace: &database.Namespace{Name: "oracle:7"},
|
||||||
Data: map[string][]byte{
|
Files: tarutil.FilesMap{
|
||||||
"etc/oracle-release": []byte(`Oracle Linux Server release 7.2`),
|
"etc/oracle-release": []byte(`Oracle Linux Server release 7.2`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "centos:6"},
|
ExpectedNamespace: &database.Namespace{Name: "centos:6"},
|
||||||
Data: map[string][]byte{
|
Files: tarutil.FilesMap{
|
||||||
"etc/centos-release": []byte(`CentOS release 6.6 (Final)`),
|
"etc/centos-release": []byte(`CentOS release 6.6 (Final)`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ExpectedNamespace: &database.Namespace{Name: "centos:7"},
|
ExpectedNamespace: &database.Namespace{Name: "centos:7"},
|
||||||
Data: map[string][]byte{
|
Files: tarutil.FilesMap{
|
||||||
"etc/system-release": []byte(`CentOS Linux release 7.1.1503 (Core)`),
|
"etc/system-release": []byte(`CentOS Linux release 7.1.1503 (Core)`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace.TestDetector(t, &RedhatReleaseNamespaceDetector{}, testData)
|
featurens.TestDetector(t, &detector{}, testData)
|
||||||
}
|
}
|
@ -1,86 +0,0 @@
|
|||||||
// Copyright 2015 clair authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package detectors exposes functions to register and use container
|
|
||||||
// information extractors.
|
|
||||||
package detectors
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The NamespaceDetector interface defines a way to detect a Namespace from input data.
|
|
||||||
// A namespace is usually made of an Operating System name and its version.
|
|
||||||
type NamespaceDetector interface {
|
|
||||||
// Detect detects a Namespace and its version from input data.
|
|
||||||
Detect(map[string][]byte) *database.Namespace
|
|
||||||
// GetRequiredFiles returns the list of files required for Detect, without
|
|
||||||
// leading /.
|
|
||||||
GetRequiredFiles() []string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
nlog = capnslog.NewPackageLogger("github.com/coreos/clair", "worker/detectors")
|
|
||||||
|
|
||||||
namespaceDetectorsLock sync.Mutex
|
|
||||||
namespaceDetectors = make(map[string]NamespaceDetector)
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterNamespaceDetector provides a way to dynamically register an implementation of a
|
|
||||||
// NamespaceDetector.
|
|
||||||
//
|
|
||||||
// If RegisterNamespaceDetector is called twice with the same name if NamespaceDetector is nil,
|
|
||||||
// or if the name is blank, it panics.
|
|
||||||
func RegisterNamespaceDetector(name string, f NamespaceDetector) {
|
|
||||||
if name == "" {
|
|
||||||
panic("Could not register a NamespaceDetector with an empty name")
|
|
||||||
}
|
|
||||||
if f == nil {
|
|
||||||
panic("Could not register a nil NamespaceDetector")
|
|
||||||
}
|
|
||||||
|
|
||||||
namespaceDetectorsLock.Lock()
|
|
||||||
defer namespaceDetectorsLock.Unlock()
|
|
||||||
|
|
||||||
if _, alreadyExists := namespaceDetectors[name]; alreadyExists {
|
|
||||||
panic(fmt.Sprintf("Detector '%s' is already registered", name))
|
|
||||||
}
|
|
||||||
namespaceDetectors[name] = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// DetectNamespace finds the OS of the layer by using every registered NamespaceDetector.
|
|
||||||
func DetectNamespace(data map[string][]byte) *database.Namespace {
|
|
||||||
for name, detector := range namespaceDetectors {
|
|
||||||
if namespace := detector.Detect(data); namespace != nil {
|
|
||||||
nlog.Debugf("detector: %q; namespace: %q\n", name, namespace.Name)
|
|
||||||
return namespace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRequiredFilesNamespace returns the list of files required for DetectNamespace for every
|
|
||||||
// registered NamespaceDetector, without leading /.
|
|
||||||
func GetRequiredFilesNamespace() (files []string) {
|
|
||||||
for _, detector := range namespaceDetectors {
|
|
||||||
files = append(files, detector.GetRequiredFiles()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
// Copyright 2016 clair authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package namespace implements utilities common to implementations of
|
|
||||||
// NamespaceDetector.
|
|
||||||
package namespace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
|
||||||
"github.com/coreos/clair/worker/detectors"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestData represents the data used to test an implementation of
|
|
||||||
// NameSpaceDetector.
|
|
||||||
type TestData struct {
|
|
||||||
Data map[string][]byte
|
|
||||||
ExpectedNamespace *database.Namespace
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDetector runs a detector on each provided instance of TestData and
|
|
||||||
// asserts the output to be equal to the expected output.
|
|
||||||
func TestDetector(t *testing.T, detector detectors.NamespaceDetector, testData []TestData) {
|
|
||||||
for _, td := range testData {
|
|
||||||
detectedNamespace := detector.Detect(td.Data)
|
|
||||||
if detectedNamespace == nil {
|
|
||||||
assert.Equal(t, td.ExpectedNamespace, detectedNamespace)
|
|
||||||
} else {
|
|
||||||
assert.Equal(t, td.ExpectedNamespace.Name, detectedNamespace.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,8 +20,10 @@ import (
|
|||||||
"github.com/coreos/pkg/capnslog"
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
|
"github.com/coreos/clair/ext/featurens"
|
||||||
"github.com/coreos/clair/ext/imagefmt"
|
"github.com/coreos/clair/ext/imagefmt"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
"github.com/coreos/clair/utils"
|
"github.com/coreos/clair/utils"
|
||||||
"github.com/coreos/clair/worker/detectors"
|
"github.com/coreos/clair/worker/detectors"
|
||||||
)
|
)
|
||||||
@ -110,20 +112,23 @@ func Process(datastore database.Datastore, imageFormat, name, parentName, path s
|
|||||||
return datastore.InsertLayer(layer)
|
return datastore.InsertLayer(layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// detectContent downloads a layer's archive and extracts its Namespace and Features.
|
// detectContent downloads a layer's archive and extracts its Namespace and
|
||||||
|
// Features.
|
||||||
func detectContent(imageFormat, name, path string, headers map[string]string, parent *database.Layer) (namespace *database.Namespace, featureVersions []database.FeatureVersion, err error) {
|
func detectContent(imageFormat, name, path string, headers map[string]string, parent *database.Layer) (namespace *database.Namespace, featureVersions []database.FeatureVersion, err error) {
|
||||||
files, err := imagefmt.Extract(imageFormat, path, headers, append(detectors.GetRequiredFilesFeatures(), detectors.GetRequiredFilesNamespace()...))
|
totalRequiredFiles := append(detectors.GetRequiredFilesFeatures(), featurens.RequiredFilenames()...)
|
||||||
|
files, err := imagefmt.Extract(imageFormat, path, headers, totalRequiredFiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("layer %s: failed to extract data from %s: %s", name, utils.CleanURL(path), err)
|
log.Errorf("layer %s: failed to extract data from %s: %s", name, utils.CleanURL(path), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data := map[string][]byte(files)
|
namespace, err = detectNamespace(name, files, parent)
|
||||||
|
if err != nil {
|
||||||
// Detect namespace.
|
return
|
||||||
namespace = detectNamespace(name, data, parent)
|
}
|
||||||
|
|
||||||
// Detect features.
|
// Detect features.
|
||||||
|
data := map[string][]byte(files)
|
||||||
featureVersions, err = detectFeatureVersions(name, data, namespace, parent)
|
featureVersions, err = detectFeatureVersions(name, data, namespace, parent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -135,15 +140,17 @@ func detectContent(imageFormat, name, path string, headers map[string]string, pa
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func detectNamespace(name string, data map[string][]byte, parent *database.Layer) (namespace *database.Namespace) {
|
func detectNamespace(name string, files tarutil.FilesMap, parent *database.Layer) (namespace *database.Namespace, err error) {
|
||||||
// Use registered detectors to get the Namespace.
|
namespace, err = featurens.Detect(files)
|
||||||
namespace = detectors.DetectNamespace(data)
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if namespace != nil {
|
if namespace != nil {
|
||||||
log.Debugf("layer %s: detected namespace %q", name, namespace.Name)
|
log.Debugf("layer %s: detected namespace %q", name, namespace.Name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the parent's Namespace.
|
// Fallback to the parent's namespace.
|
||||||
if parent != nil {
|
if parent != nil {
|
||||||
namespace = parent.Namespace
|
namespace = parent.Namespace
|
||||||
if namespace != nil {
|
if namespace != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user