Skip to content

Commit

Permalink
refactor: move updaters and notifier into ext
Browse files Browse the repository at this point in the history
  • Loading branch information
jzelinskie committed Jan 23, 2017
1 parent f66103c commit 4a99037
Show file tree
Hide file tree
Showing 31 changed files with 487 additions and 392 deletions.
17 changes: 8 additions & 9 deletions cmd/clair/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ import (
"github.com/coreos/clair"
"github.com/coreos/clair/config"

// Register components
_ "github.com/coreos/clair/notifier/notifiers"

_ "github.com/coreos/clair/updater/fetchers/alpine"
_ "github.com/coreos/clair/updater/fetchers/debian"
_ "github.com/coreos/clair/updater/fetchers/oracle"
_ "github.com/coreos/clair/updater/fetchers/rhel"
_ "github.com/coreos/clair/updater/fetchers/ubuntu"
_ "github.com/coreos/clair/updater/metadata_fetchers/nvd"
// Register extensions.
_ "github.com/coreos/clair/ext/notification/webhook"
_ "github.com/coreos/clair/ext/vulnmdsrc/nvd"
_ "github.com/coreos/clair/ext/vulnsrc/alpine"
_ "github.com/coreos/clair/ext/vulnsrc/debian"
_ "github.com/coreos/clair/ext/vulnsrc/oracle"
_ "github.com/coreos/clair/ext/vulnsrc/rhel"
_ "github.com/coreos/clair/ext/vulnsrc/ubuntu"

_ "github.com/coreos/clair/worker/detectors/data/aci"
_ "github.com/coreos/clair/worker/detectors/data/docker"
Expand Down
65 changes: 65 additions & 0 deletions ext/notification/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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 notification fetches notifications from the database and informs the
// specified remote handler about their existences, inviting the third party to
// actively query the API about it.

// Package notification exposes functions to dynamically register methods to
// deliver notifications from the Clair database.
package notification

import (
"github.com/coreos/pkg/capnslog"

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

var (
log = capnslog.NewPackageLogger("github.com/coreos/clair", "ext/notification")

// Senders is the list of registered Senders.
Senders = make(map[string]Sender)
)

// Sender represents anything that can transmit notifications.
type Sender interface {
// Configure attempts to initialize the notifier with the provided configuration.
// It returns whether the notifier is enabled or not.
Configure(*config.NotifierConfig) (bool, error)

// Send informs the existence of the specified notification.
Send(notification database.VulnerabilityNotification) error
}

// RegisterSender makes a Sender available by the provided name.
//
// If RegisterSender is called twice with the same name, the name is blank, or
// if the provided Sender is nil, this function panics.
func RegisterSender(name string, s Sender) {
if name == "" {
panic("notification: could not register a Sender with an empty name")
}

if s == nil {
panic("notification: could not register a nil Sender")
}

if _, dup := Senders[name]; dup {
panic("notification: RegisterSender called twice for " + name)
}

Senders[name] = s
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2015 clair authors
// 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.
Expand All @@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package notifiers implements several kinds of notifier.Notifier
package notifiers
// Package webhook implements a notification sender for HTTP JSON webhooks.
package webhook

import (
"bytes"
Expand All @@ -31,19 +31,18 @@ import (

"github.com/coreos/clair/config"
"github.com/coreos/clair/database"
"github.com/coreos/clair/notifier"
"github.com/coreos/clair/ext/notification"
)

const timeout = 5 * time.Second

// A WebhookNotifier dispatches notifications to a webhook endpoint.
type WebhookNotifier struct {
type sender struct {
endpoint string
client *http.Client
}

// A WebhookNotifierConfiguration represents the configuration of a WebhookNotifier.
type WebhookNotifierConfiguration struct {
// Config represents the configuration of a Webhook Sender.
type Config struct {
Endpoint string
ServerName string
CertFile string
Expand All @@ -53,12 +52,12 @@ type WebhookNotifierConfiguration struct {
}

func init() {
notifier.RegisterNotifier("webhook", &WebhookNotifier{})
notification.RegisterSender("webhook", &sender{})
}

func (h *WebhookNotifier) Configure(config *config.NotifierConfig) (bool, error) {
func (s *sender) Configure(config *config.NotifierConfig) (bool, error) {
// Get configuration
var httpConfig WebhookNotifierConfiguration
var httpConfig Config
if config == nil {
return false, nil
}
Expand All @@ -81,11 +80,11 @@ func (h *WebhookNotifier) Configure(config *config.NotifierConfig) (bool, error)
if _, err := url.ParseRequestURI(httpConfig.Endpoint); err != nil {
return false, fmt.Errorf("could not parse endpoint URL: %s\n", err)
}
h.endpoint = httpConfig.Endpoint
s.endpoint = httpConfig.Endpoint

// Setup HTTP client.
transport := &http.Transport{}
h.client = &http.Client{
s.client = &http.Client{
Transport: transport,
Timeout: timeout,
}
Expand Down Expand Up @@ -114,15 +113,15 @@ type notificationEnvelope struct {
}
}

func (h *WebhookNotifier) Send(notification database.VulnerabilityNotification) error {
func (s *sender) Send(notification database.VulnerabilityNotification) error {
// Marshal notification.
jsonNotification, err := json.Marshal(notificationEnvelope{struct{ Name string }{notification.Name}})
if err != nil {
return fmt.Errorf("could not marshal: %s", err)
}

// Send notification via HTTP POST.
resp, err := h.client.Post(h.endpoint, "application/json", bytes.NewBuffer(jsonNotification))
resp, err := s.client.Post(s.endpoint, "application/json", bytes.NewBuffer(jsonNotification))
if err != nil || resp == nil || (resp.StatusCode != 200 && resp.StatusCode != 201) {
if resp != nil {
return fmt.Errorf("got status %d, expected 200/201", resp.StatusCode)
Expand All @@ -134,11 +133,11 @@ func (h *WebhookNotifier) Send(notification database.VulnerabilityNotification)
return nil
}

// loadTLSClientConfig initializes a *tls.Config using the given WebhookNotifierConfiguration.
// loadTLSClientConfig initializes a *tls.Config using the given Config.
//
// If no certificates are given, (nil, nil) is returned.
// The CA certificate is optional and falls back to the system default.
func loadTLSClientConfig(cfg *WebhookNotifierConfiguration) (*tls.Config, error) {
func loadTLSClientConfig(cfg *Config) (*tls.Config, error) {
if cfg.CertFile == "" || cfg.KeyFile == "" {
return nil, nil
}
Expand Down
68 changes: 68 additions & 0 deletions ext/vulnmdsrc/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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 vulnmdsrc exposes functions to dynamically register vulnerability
// metadata sources used to update a Clair database.
package vulnmdsrc

import (
"github.com/coreos/clair/database"
"github.com/coreos/clair/utils/types"
)

// Appenders is the list of registered Appenders.
var Appenders = make(map[string]Appender)

// AppendFunc is the type of a callback provided to an Appender.
type AppendFunc func(metadataKey string, metadata interface{}, severity types.Priority)

// Appender represents anything that can fetch vulnerability metadata and
// append it to a Vulnerability.
type Appender interface {
// BuildCache loads metadata into memory such that it can be quickly accessed
// for future calls to Append.
BuildCache(database.Datastore) error

// AddMetadata adds metadata to the given database.Vulnerability.
// It is expected that the fetcher uses .Lock.Lock() when manipulating the Metadata map.
// Append
Append(vulnName string, callback AppendFunc) error

// PurgeCache deallocates metadata from memory after all calls to Append are
// finished.
PurgeCache()

// Clean deletes any allocated resources.
// It is invoked when Clair stops.
Clean()
}

// RegisterAppender makes an Appender available by the provided name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
func RegisterAppender(name string, a Appender) {
if name == "" {
panic("updater: could not register an Appender with an empty name")
}

if a == nil {
panic("vulnmdsrc: could not register a nil Appender")
}

if _, dup := Appenders[name]; dup {
panic("vulnmdsrc: RegisterAppender called twice for " + name)
}

Appenders[name] = a
}
33 changes: 33 additions & 0 deletions ext/vulnmdsrc/nvd/nested_read_closer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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 nvd

import "io"

// NestedReadCloser wraps an io.Reader and implements io.ReadCloser by closing every embed
// io.ReadCloser.
// It allows chaining io.ReadCloser together and still keep the ability to close them all in a
// simple manner.
type NestedReadCloser struct {
io.Reader
NestedReadClosers []io.ReadCloser
}

// Close closes the gzip.Reader and the underlying io.ReadCloser.
func (nrc *NestedReadCloser) Close() {
for _, nestedReadCloser := range nrc.NestedReadClosers {
nestedReadCloser.Close()
}
}
Loading

0 comments on commit 4a99037

Please sign in to comment.