Skip to content

Commit

Permalink
clair: Implement worker detector support
Browse files Browse the repository at this point in the history
The worker is changed to accommodate the new database model and API.
Worker is refactored to move the database query helper functions to pkg.
  • Loading branch information
KeyboardNerd committed Oct 8, 2018
1 parent 48427e9 commit 0283240
Show file tree
Hide file tree
Showing 7 changed files with 770 additions and 678 deletions.
44 changes: 19 additions & 25 deletions cmd/clair/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,27 +102,13 @@ func stopCPUProfiling(f *os.File) {
}

func configClairVersion(config *Config) {
listers := featurefmt.ListListers()
detectors := featurens.ListDetectors()
updaters := vulnsrc.ListUpdaters()
clair.EnabledDetectors = append(featurefmt.ListListers(), featurens.ListDetectors()...)
clair.EnabledUpdaters = strutil.Intersect(config.Updater.EnabledUpdaters, vulnsrc.ListUpdaters())

log.WithFields(log.Fields{
"Listers": strings.Join(listers, ","),
"Detectors": strings.Join(detectors, ","),
"Updaters": strings.Join(updaters, ","),
}).Info("Clair registered components")

unregUpdaters := strutil.CompareStringLists(config.Updater.EnabledUpdaters, updaters)
if len(unregUpdaters) != 0 {
log.WithFields(log.Fields{
"Unknown Updaters": strings.Join(unregUpdaters, ","),
"Available Updaters": strings.Join(vulnsrc.ListUpdaters(), ","),
}).Fatal("Unknown or unregistered components are configured")
}

// All listers and detectors are enabled.
clair.Processors = database.Processors{Detectors: detectors, Listers: listers}
clair.EnabledUpdaters = strutil.CompareStringListsInBoth(config.Updater.EnabledUpdaters, updaters)
"Detectors": database.SerializeDetectors(clair.EnabledDetectors),
"Updaters": clair.EnabledUpdaters,
}).Info("enabled Clair extensions")
}

// Boot starts Clair instance with the provided config.
Expand All @@ -147,6 +133,7 @@ func Boot(config *Config) {

defer db.Close()

clair.InitWorker(db)
// Start notifier
st.Begin()
go clair.RunNotifier(config.Notifier, db, st)
Expand All @@ -167,6 +154,18 @@ func Boot(config *Config) {
st.Stop()
}

// Initialize logging system
func configureLogger(flagLogLevel *string) {
logLevel, err := log.ParseLevel(strings.ToUpper(*flagLogLevel))
if err != nil {
log.WithError(err).Error("failed to set logger parser level")
}

log.SetLevel(logLevel)
log.SetOutput(os.Stdout)
log.SetFormatter(&formatter.JSONExtendedFormatter{ShowLn: true})
}

func main() {
// Parse command-line arguments
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
Expand All @@ -176,6 +175,7 @@ func main() {
flagInsecureTLS := flag.Bool("insecure-tls", false, "Disable TLS server's certificate chain and hostname verification when pulling layers.")
flag.Parse()

configureLogger(flagLogLevel)
// Check for dependencies.
for _, bin := range BinaryDependencies {
_, err := exec.LookPath(bin)
Expand All @@ -184,12 +184,6 @@ func main() {
}
}

// Initialize logging system
logLevel, err := log.ParseLevel(strings.ToUpper(*flagLogLevel))
log.SetLevel(logLevel)
log.SetOutput(os.Stdout)
log.SetFormatter(&formatter.JSONExtendedFormatter{ShowLn: true})

config, err := LoadConfig(*flagConfigPath)
if err != nil {
log.WithError(err).Fatal("failed to load configuration")
Expand Down
288 changes: 288 additions & 0 deletions pkg/dbutil/dbutil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
// Copyright 2018 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 dbutil

import (
"github.com/deckarep/golang-set"

"github.com/coreos/clair/database"
)

// DeduplicateNamespaces deduplicates a list of namespaces.
func DeduplicateNamespaces(namespaces ...database.Namespace) []database.Namespace {
nsSet := mapset.NewSet()
for _, ns := range namespaces {
nsSet.Add(ns)
}

result := make([]database.Namespace, 0, nsSet.Cardinality())
for ns := range nsSet.Iter() {
result = append(result, ns.(database.Namespace))
}

return result
}

// DeduplicateFeatures deduplicates a list of list of features.
func DeduplicateFeatures(features ...database.Feature) []database.Feature {
fSet := mapset.NewSet()
for _, f := range features {
fSet.Add(f)
}

result := make([]database.Feature, 0, fSet.Cardinality())
for f := range fSet.Iter() {
result = append(result, f.(database.Feature))
}

return result
}

// PersistPartialLayer wraps session PersistLayer function with begin and
// commit.
func PersistPartialLayer(datastore database.Datastore, layer *database.Layer) error {
tx, err := datastore.Begin()
if err != nil {
return err
}
defer tx.Rollback()

if err := tx.PersistLayer(layer.Hash, layer.Features, layer.Namespaces, layer.By); err != nil {
return err
}

return tx.Commit()
}

// PersistFeatures wraps session PersistFeatures function with begin and commit.
func PersistFeatures(datastore database.Datastore, features []database.Feature) error {
tx, err := datastore.Begin()
if err != nil {
return err
}
defer tx.Rollback()

if err := tx.PersistFeatures(features); err != nil {
return err
}
return tx.Commit()
}

// PersistNamespaces wraps session PersistNamespaces function with begin and
// commit.
func PersistNamespaces(datastore database.Datastore, namespaces []database.Namespace) error {
tx, err := datastore.Begin()
if err != nil {
return err
}
defer tx.Rollback()

if err := tx.PersistNamespaces(namespaces); err != nil {
return err
}

return tx.Commit()
}

// FindAncestry wraps session FindAncestry function with begin and rollback.
func FindAncestry(datastore database.Datastore, name string) (database.Ancestry, bool, error) {
tx, err := datastore.Begin()
defer tx.Rollback()

if err != nil {
return database.Ancestry{}, false, err
}

return tx.FindAncestry(name)
}

// FindLayer wraps session FindLayer function with begin and rollback.
func FindLayer(datastore database.Datastore, hash string) (layer database.Layer, ok bool, err error) {
var tx database.Session
if tx, err = datastore.Begin(); err != nil {
return
}

defer tx.Rollback()
layer, ok, err = tx.FindLayer(hash)
return
}

// DeduplicateNamespacedFeatures returns a copy of all unique features in the
// input.
func DeduplicateNamespacedFeatures(features []database.NamespacedFeature) []database.NamespacedFeature {
nsSet := mapset.NewSet()
for _, ns := range features {
nsSet.Add(ns)
}

result := make([]database.NamespacedFeature, 0, nsSet.Cardinality())
for ns := range nsSet.Iter() {
result = append(result, ns.(database.NamespacedFeature))
}

return result
}

// GetAncestryFeatures returns a list of unique namespaced features in the
// ancestry.
func GetAncestryFeatures(ancestry database.Ancestry) []database.NamespacedFeature {
features := []database.NamespacedFeature{}
for _, layer := range ancestry.Layers {
features = append(features, layer.GetFeatures()...)
}

return DeduplicateNamespacedFeatures(features)
}

// UpsertAncestry wraps session UpsertAncestry function with begin and commit.
func UpsertAncestry(datastore database.Datastore, ancestry database.Ancestry) error {
tx, err := datastore.Begin()
if err != nil {
return err
}

if err = tx.UpsertAncestry(ancestry); err != nil {
tx.Rollback()
return err
}

if err = tx.Commit(); err != nil {
return err
}

return nil
}

// PersistNamespacedFeatures wraps session PersistNamespacedFeatures function
// with begin and commit.
func PersistNamespacedFeatures(datastore database.Datastore, features []database.NamespacedFeature) error {
tx, err := datastore.Begin()
if err != nil {
return err
}

if err := tx.PersistNamespacedFeatures(features); err != nil {
tx.Rollback()
return err
}

if err := tx.Commit(); err != nil {
return err
}

return nil
}

// CacheRelatedVulnerability wraps session CacheAffectedNamespacedFeatures
// function with begin and commit.
func CacheRelatedVulnerability(datastore database.Datastore, features []database.NamespacedFeature) error {
tx, err := datastore.Begin()
if err != nil {
return err
}

if err := tx.CacheAffectedNamespacedFeatures(features); err != nil {
tx.Rollback()
return err
}

return tx.Commit()
}

// IntersectDetectors returns the detectors in both d1 and d2.
func IntersectDetectors(d1 []database.Detector, d2 []database.Detector) []database.Detector {
d1Set := mapset.NewSet()
for _, d := range d1 {
d1Set.Add(d)
}

d2Set := mapset.NewSet()
for _, d := range d2 {
d2Set.Add(d)
}

inter := d1Set.Intersect(d2Set)
result := make([]database.Detector, 0, inter.Cardinality())
for d := range inter.Iter() {
result = append(result, d.(database.Detector))
}

return result
}

// DiffDetectors returns the detectors belongs to d1 but not d2
func DiffDetectors(d1 []database.Detector, d2 []database.Detector) []database.Detector {
d1Set := mapset.NewSet()
for _, d := range d1 {
d1Set.Add(d)
}

d2Set := mapset.NewSet()
for _, d := range d2 {
d2Set.Add(d)
}

diff := d1Set.Difference(d2Set)
result := make([]database.Detector, 0, diff.Cardinality())
for d := range diff.Iter() {
result = append(result, d.(database.Detector))
}

return result
}

// MergeLayers merges all content in new layer to l, where the content is
// updated.
func MergeLayers(l *database.Layer, new *database.Layer) *database.Layer {
featureSet := mapset.NewSet()
namespaceSet := mapset.NewSet()
bySet := mapset.NewSet()

for _, f := range l.Features {
featureSet.Add(f)
}

for _, ns := range l.Namespaces {
namespaceSet.Add(ns)
}

for _, d := range l.By {
bySet.Add(d)
}

for _, feature := range new.Features {
if !featureSet.Contains(feature) {
l.Features = append(l.Features, feature)
featureSet.Add(feature)
}
}

for _, namespace := range new.Namespaces {
if !namespaceSet.Contains(namespace) {
l.Namespaces = append(l.Namespaces, namespace)
namespaceSet.Add(namespace)
}
}

for _, detector := range new.By {
if !bySet.Contains(detector) {
l.By = append(l.By, detector)
bySet.Add(detector)
}
}

return l
}

0 comments on commit 0283240

Please sign in to comment.