move config to main / decentralize config
This puts config in its relevant location and moves functions around loading config files into the main package. As a side effect of removing cyclic imports for the API config, the context library is no longer used.
This commit is contained in:
parent
889615276a
commit
6a569fd945
38
api/api.go
38
api/api.go
@ -23,29 +23,37 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/pkg/capnslog"
|
||||||
"github.com/tylerb/graceful"
|
"github.com/tylerb/graceful"
|
||||||
|
|
||||||
"github.com/coreos/clair/api/context"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
"github.com/coreos/clair/pkg/stopper"
|
"github.com/coreos/clair/pkg/stopper"
|
||||||
"github.com/coreos/pkg/capnslog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const timeoutResponse = `{"Error":{"Message":"Clair failed to respond within the configured timeout window.","Type":"Timeout"}}`
|
const timeoutResponse = `{"Error":{"Message":"Clair failed to respond within the configured timeout window.","Type":"Timeout"}}`
|
||||||
|
|
||||||
var log = capnslog.NewPackageLogger("github.com/coreos/clair", "api")
|
var log = capnslog.NewPackageLogger("github.com/coreos/clair", "api")
|
||||||
|
|
||||||
func Run(config *config.APIConfig, ctx *context.RouteContext, st *stopper.Stopper) {
|
// Config is the configuration for the API service.
|
||||||
|
type Config struct {
|
||||||
|
Port int
|
||||||
|
HealthPort int
|
||||||
|
Timeout time.Duration
|
||||||
|
PaginationKey string
|
||||||
|
CertFile, KeyFile, CAFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(cfg *Config, store database.Datastore, st *stopper.Stopper) {
|
||||||
defer st.End()
|
defer st.End()
|
||||||
|
|
||||||
// Do not run the API service if there is no config.
|
// Do not run the API service if there is no config.
|
||||||
if config == nil {
|
if cfg == nil {
|
||||||
log.Infof("main API service is disabled.")
|
log.Infof("main API service is disabled.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Infof("starting main API on port %d.", config.Port)
|
log.Infof("starting main API on port %d.", cfg.Port)
|
||||||
|
|
||||||
tlsConfig, err := tlsClientConfig(config.CAFile)
|
tlsConfig, err := tlsClientConfig(cfg.CAFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("could not initialize client cert authentication: %s\n", err)
|
log.Fatalf("could not initialize client cert authentication: %s\n", err)
|
||||||
}
|
}
|
||||||
@ -57,33 +65,33 @@ func Run(config *config.APIConfig, ctx *context.RouteContext, st *stopper.Stoppe
|
|||||||
Timeout: 0, // Already handled by our TimeOut middleware
|
Timeout: 0, // Already handled by our TimeOut middleware
|
||||||
NoSignalHandling: true, // We want to use our own Stopper
|
NoSignalHandling: true, // We want to use our own Stopper
|
||||||
Server: &http.Server{
|
Server: &http.Server{
|
||||||
Addr: ":" + strconv.Itoa(config.Port),
|
Addr: ":" + strconv.Itoa(cfg.Port),
|
||||||
TLSConfig: tlsConfig,
|
TLSConfig: tlsConfig,
|
||||||
Handler: http.TimeoutHandler(newAPIHandler(ctx), config.Timeout, timeoutResponse),
|
Handler: http.TimeoutHandler(newAPIHandler(cfg, store), cfg.Timeout, timeoutResponse),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
listenAndServeWithStopper(srv, st, config.CertFile, config.KeyFile)
|
listenAndServeWithStopper(srv, st, cfg.CertFile, cfg.KeyFile)
|
||||||
|
|
||||||
log.Info("main API stopped")
|
log.Info("main API stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunHealth(config *config.APIConfig, ctx *context.RouteContext, st *stopper.Stopper) {
|
func RunHealth(cfg *Config, store database.Datastore, st *stopper.Stopper) {
|
||||||
defer st.End()
|
defer st.End()
|
||||||
|
|
||||||
// Do not run the API service if there is no config.
|
// Do not run the API service if there is no config.
|
||||||
if config == nil {
|
if cfg == nil {
|
||||||
log.Infof("health API service is disabled.")
|
log.Infof("health API service is disabled.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Infof("starting health API on port %d.", config.HealthPort)
|
log.Infof("starting health API on port %d.", cfg.HealthPort)
|
||||||
|
|
||||||
srv := &graceful.Server{
|
srv := &graceful.Server{
|
||||||
Timeout: 10 * time.Second, // Interrupt health checks when stopping
|
Timeout: 10 * time.Second, // Interrupt health checks when stopping
|
||||||
NoSignalHandling: true, // We want to use our own Stopper
|
NoSignalHandling: true, // We want to use our own Stopper
|
||||||
Server: &http.Server{
|
Server: &http.Server{
|
||||||
Addr: ":" + strconv.Itoa(config.HealthPort),
|
Addr: ":" + strconv.Itoa(cfg.HealthPort),
|
||||||
Handler: http.TimeoutHandler(newHealthHandler(ctx), config.Timeout, timeoutResponse),
|
Handler: http.TimeoutHandler(newHealthHandler(store), cfg.Timeout, timeoutResponse),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
// 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 context
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
|
||||||
"github.com/julienschmidt/httprouter"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
|
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
"github.com/coreos/clair/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
log = capnslog.NewPackageLogger("github.com/coreos/clair", "api")
|
|
||||||
|
|
||||||
promResponseDurationMilliseconds = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
|
||||||
Name: "clair_api_response_duration_milliseconds",
|
|
||||||
Help: "The duration of time it takes to receieve and write a response to an API request",
|
|
||||||
Buckets: prometheus.ExponentialBuckets(9.375, 2, 10),
|
|
||||||
}, []string{"route", "code"})
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
prometheus.MustRegister(promResponseDurationMilliseconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Handler func(http.ResponseWriter, *http.Request, httprouter.Params, *RouteContext) (route string, status int)
|
|
||||||
|
|
||||||
func HTTPHandler(handler Handler, ctx *RouteContext) httprouter.Handle {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
||||||
start := time.Now()
|
|
||||||
route, status := handler(w, r, p, ctx)
|
|
||||||
statusStr := strconv.Itoa(status)
|
|
||||||
if status == 0 {
|
|
||||||
statusStr = "???"
|
|
||||||
}
|
|
||||||
|
|
||||||
promResponseDurationMilliseconds.
|
|
||||||
WithLabelValues(route, statusStr).
|
|
||||||
Observe(float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond))
|
|
||||||
|
|
||||||
log.Infof("%s \"%s %s\" %s (%s)", r.RemoteAddr, r.Method, r.RequestURI, statusStr, time.Since(start))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type RouteContext struct {
|
|
||||||
Store database.Datastore
|
|
||||||
Config *config.APIConfig
|
|
||||||
}
|
|
@ -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.
|
||||||
@ -20,8 +20,8 @@ import (
|
|||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
|
||||||
"github.com/coreos/clair/api/context"
|
|
||||||
"github.com/coreos/clair/api/v1"
|
"github.com/coreos/clair/api/v1"
|
||||||
|
"github.com/coreos/clair/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// router is an HTTP router that forwards requests to the appropriate sub-router
|
// router is an HTTP router that forwards requests to the appropriate sub-router
|
||||||
@ -31,9 +31,9 @@ type router map[string]*httprouter.Router
|
|||||||
// Let's hope we never have more than 99 API versions.
|
// Let's hope we never have more than 99 API versions.
|
||||||
const apiVersionLength = len("v99")
|
const apiVersionLength = len("v99")
|
||||||
|
|
||||||
func newAPIHandler(ctx *context.RouteContext) http.Handler {
|
func newAPIHandler(cfg *Config, store database.Datastore) http.Handler {
|
||||||
router := make(router)
|
router := make(router)
|
||||||
router["/v1"] = v1.NewRouter(ctx)
|
router["/v1"] = v1.NewRouter(store, cfg.PaginationKey)
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,21 +56,22 @@ func (rtr router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHealthHandler(ctx *context.RouteContext) http.Handler {
|
func newHealthHandler(store database.Datastore) http.Handler {
|
||||||
router := httprouter.New()
|
router := httprouter.New()
|
||||||
router.GET("/health", context.HTTPHandler(getHealth, ctx))
|
router.GET("/health", healthHandler(store))
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHealth(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func healthHandler(store database.Datastore) httprouter.Handle {
|
||||||
header := w.Header()
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
header.Set("Server", "clair")
|
header := w.Header()
|
||||||
|
header.Set("Server", "clair")
|
||||||
|
|
||||||
status := http.StatusInternalServerError
|
status := http.StatusInternalServerError
|
||||||
if ctx.Store.Ping() {
|
if store.Ping() {
|
||||||
status = http.StatusOK
|
status = http.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(status)
|
|
||||||
return "health", status
|
|
||||||
}
|
}
|
||||||
|
@ -16,41 +16,83 @@
|
|||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/julienschmidt/httprouter"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/clair/api/context"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
"github.com/coreos/clair/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
promResponseDurationMilliseconds = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||||
|
Name: "clair_api_response_duration_milliseconds",
|
||||||
|
Help: "The duration of time it takes to receieve and write a response to an API request",
|
||||||
|
Buckets: prometheus.ExponentialBuckets(9.375, 2, 10),
|
||||||
|
}, []string{"route", "code"})
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
prometheus.MustRegister(promResponseDurationMilliseconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler func(http.ResponseWriter, *http.Request, httprouter.Params, *context) (route string, status int)
|
||||||
|
|
||||||
|
func httpHandler(h handler, ctx *context) httprouter.Handle {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
|
start := time.Now()
|
||||||
|
route, status := h(w, r, p, ctx)
|
||||||
|
statusStr := strconv.Itoa(status)
|
||||||
|
if status == 0 {
|
||||||
|
statusStr = "???"
|
||||||
|
}
|
||||||
|
|
||||||
|
promResponseDurationMilliseconds.
|
||||||
|
WithLabelValues(route, statusStr).
|
||||||
|
Observe(float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond))
|
||||||
|
|
||||||
|
log.Infof("%s \"%s %s\" %s (%s)", r.RemoteAddr, r.Method, r.RequestURI, statusStr, time.Since(start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type context struct {
|
||||||
|
Store database.Datastore
|
||||||
|
PaginationKey string
|
||||||
|
}
|
||||||
|
|
||||||
// NewRouter creates an HTTP router for version 1 of the Clair API.
|
// NewRouter creates an HTTP router for version 1 of the Clair API.
|
||||||
func NewRouter(ctx *context.RouteContext) *httprouter.Router {
|
func NewRouter(store database.Datastore, paginationKey string) *httprouter.Router {
|
||||||
router := httprouter.New()
|
router := httprouter.New()
|
||||||
|
ctx := &context{store, paginationKey}
|
||||||
|
|
||||||
// Layers
|
// Layers
|
||||||
router.POST("/layers", context.HTTPHandler(postLayer, ctx))
|
router.POST("/layers", httpHandler(postLayer, ctx))
|
||||||
router.GET("/layers/:layerName", context.HTTPHandler(getLayer, ctx))
|
router.GET("/layers/:layerName", httpHandler(getLayer, ctx))
|
||||||
router.DELETE("/layers/:layerName", context.HTTPHandler(deleteLayer, ctx))
|
router.DELETE("/layers/:layerName", httpHandler(deleteLayer, ctx))
|
||||||
|
|
||||||
// Namespaces
|
// Namespaces
|
||||||
router.GET("/namespaces", context.HTTPHandler(getNamespaces, ctx))
|
router.GET("/namespaces", httpHandler(getNamespaces, ctx))
|
||||||
|
|
||||||
// Vulnerabilities
|
// Vulnerabilities
|
||||||
router.GET("/namespaces/:namespaceName/vulnerabilities", context.HTTPHandler(getVulnerabilities, ctx))
|
router.GET("/namespaces/:namespaceName/vulnerabilities", httpHandler(getVulnerabilities, ctx))
|
||||||
router.POST("/namespaces/:namespaceName/vulnerabilities", context.HTTPHandler(postVulnerability, ctx))
|
router.POST("/namespaces/:namespaceName/vulnerabilities", httpHandler(postVulnerability, ctx))
|
||||||
router.GET("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", context.HTTPHandler(getVulnerability, ctx))
|
router.GET("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", httpHandler(getVulnerability, ctx))
|
||||||
router.PUT("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", context.HTTPHandler(putVulnerability, ctx))
|
router.PUT("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", httpHandler(putVulnerability, ctx))
|
||||||
router.DELETE("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", context.HTTPHandler(deleteVulnerability, ctx))
|
router.DELETE("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", httpHandler(deleteVulnerability, ctx))
|
||||||
|
|
||||||
// Fixes
|
// Fixes
|
||||||
router.GET("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes", context.HTTPHandler(getFixes, ctx))
|
router.GET("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes", httpHandler(getFixes, ctx))
|
||||||
router.PUT("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes/:fixName", context.HTTPHandler(putFix, ctx))
|
router.PUT("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes/:fixName", httpHandler(putFix, ctx))
|
||||||
router.DELETE("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes/:fixName", context.HTTPHandler(deleteFix, ctx))
|
router.DELETE("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes/:fixName", httpHandler(deleteFix, ctx))
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
router.GET("/notifications/:notificationName", context.HTTPHandler(getNotification, ctx))
|
router.GET("/notifications/:notificationName", httpHandler(getNotification, ctx))
|
||||||
router.DELETE("/notifications/:notificationName", context.HTTPHandler(deleteNotification, ctx))
|
router.DELETE("/notifications/:notificationName", httpHandler(deleteNotification, ctx))
|
||||||
|
|
||||||
// Metrics
|
// Metrics
|
||||||
router.GET("/metrics", context.HTTPHandler(getMetrics, ctx))
|
router.GET("/metrics", httpHandler(getMetrics, ctx))
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
"github.com/coreos/clair"
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/api/context"
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
"github.com/coreos/clair/pkg/tarutil"
|
"github.com/coreos/clair/pkg/tarutil"
|
||||||
@ -96,7 +95,7 @@ func writeResponse(w http.ResponseWriter, r *http.Request, status int, resp inte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func postLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func postLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
request := LayerEnvelope{}
|
request := LayerEnvelope{}
|
||||||
err := decodeJSON(r, &request)
|
err := decodeJSON(r, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -138,7 +137,7 @@ func postLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx
|
|||||||
return postLayerRoute, http.StatusCreated
|
return postLayerRoute, http.StatusCreated
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func getLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
_, withFeatures := r.URL.Query()["features"]
|
_, withFeatures := r.URL.Query()["features"]
|
||||||
_, withVulnerabilities := r.URL.Query()["vulnerabilities"]
|
_, withVulnerabilities := r.URL.Query()["vulnerabilities"]
|
||||||
|
|
||||||
@ -157,7 +156,7 @@ func getLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *
|
|||||||
return getLayerRoute, http.StatusOK
|
return getLayerRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func deleteLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
err := ctx.Store.DeleteLayer(p.ByName("layerName"))
|
err := ctx.Store.DeleteLayer(p.ByName("layerName"))
|
||||||
if err == commonerr.ErrNotFound {
|
if err == commonerr.ErrNotFound {
|
||||||
writeResponse(w, r, http.StatusNotFound, LayerEnvelope{Error: &Error{err.Error()}})
|
writeResponse(w, r, http.StatusNotFound, LayerEnvelope{Error: &Error{err.Error()}})
|
||||||
@ -171,7 +170,7 @@ func deleteLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ct
|
|||||||
return deleteLayerRoute, http.StatusOK
|
return deleteLayerRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNamespaces(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func getNamespaces(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
dbNamespaces, err := ctx.Store.ListNamespaces()
|
dbNamespaces, err := ctx.Store.ListNamespaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeResponse(w, r, http.StatusInternalServerError, NamespaceEnvelope{Error: &Error{err.Error()}})
|
writeResponse(w, r, http.StatusInternalServerError, NamespaceEnvelope{Error: &Error{err.Error()}})
|
||||||
@ -189,7 +188,7 @@ func getNamespaces(w http.ResponseWriter, r *http.Request, p httprouter.Params,
|
|||||||
return getNamespacesRoute, http.StatusOK
|
return getNamespacesRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func getVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
query := r.URL.Query()
|
query := r.URL.Query()
|
||||||
|
|
||||||
limitStrs, limitExists := query["limit"]
|
limitStrs, limitExists := query["limit"]
|
||||||
@ -209,7 +208,7 @@ func getVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Par
|
|||||||
page := 0
|
page := 0
|
||||||
pageStrs, pageExists := query["page"]
|
pageStrs, pageExists := query["page"]
|
||||||
if pageExists {
|
if pageExists {
|
||||||
err = tokenUnmarshal(pageStrs[0], ctx.Config.PaginationKey, &page)
|
err = tokenUnmarshal(pageStrs[0], ctx.PaginationKey, &page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"invalid page format: " + err.Error()}})
|
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"invalid page format: " + err.Error()}})
|
||||||
return getNotificationRoute, http.StatusBadRequest
|
return getNotificationRoute, http.StatusBadRequest
|
||||||
@ -239,7 +238,7 @@ func getVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Par
|
|||||||
|
|
||||||
var nextPageStr string
|
var nextPageStr string
|
||||||
if nextPage != -1 {
|
if nextPage != -1 {
|
||||||
nextPageBytes, err := tokenMarshal(nextPage, ctx.Config.PaginationKey)
|
nextPageBytes, err := tokenMarshal(nextPage, ctx.PaginationKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"failed to marshal token: " + err.Error()}})
|
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"failed to marshal token: " + err.Error()}})
|
||||||
return getNotificationRoute, http.StatusBadRequest
|
return getNotificationRoute, http.StatusBadRequest
|
||||||
@ -251,7 +250,7 @@ func getVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Par
|
|||||||
return getVulnerabilitiesRoute, http.StatusOK
|
return getVulnerabilitiesRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func postVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func postVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
request := VulnerabilityEnvelope{}
|
request := VulnerabilityEnvelope{}
|
||||||
err := decodeJSON(r, &request)
|
err := decodeJSON(r, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -286,7 +285,7 @@ func postVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Para
|
|||||||
return postVulnerabilityRoute, http.StatusCreated
|
return postVulnerabilityRoute, http.StatusCreated
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func getVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
_, withFixedIn := r.URL.Query()["fixedIn"]
|
_, withFixedIn := r.URL.Query()["fixedIn"]
|
||||||
|
|
||||||
dbVuln, err := ctx.Store.FindVulnerability(p.ByName("namespaceName"), p.ByName("vulnerabilityName"))
|
dbVuln, err := ctx.Store.FindVulnerability(p.ByName("namespaceName"), p.ByName("vulnerabilityName"))
|
||||||
@ -304,7 +303,7 @@ func getVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Param
|
|||||||
return getVulnerabilityRoute, http.StatusOK
|
return getVulnerabilityRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func putVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func putVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
request := VulnerabilityEnvelope{}
|
request := VulnerabilityEnvelope{}
|
||||||
err := decodeJSON(r, &request)
|
err := decodeJSON(r, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -347,7 +346,7 @@ func putVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Param
|
|||||||
return putVulnerabilityRoute, http.StatusOK
|
return putVulnerabilityRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func deleteVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
err := ctx.Store.DeleteVulnerability(p.ByName("namespaceName"), p.ByName("vulnerabilityName"))
|
err := ctx.Store.DeleteVulnerability(p.ByName("namespaceName"), p.ByName("vulnerabilityName"))
|
||||||
if err == commonerr.ErrNotFound {
|
if err == commonerr.ErrNotFound {
|
||||||
writeResponse(w, r, http.StatusNotFound, VulnerabilityEnvelope{Error: &Error{err.Error()}})
|
writeResponse(w, r, http.StatusNotFound, VulnerabilityEnvelope{Error: &Error{err.Error()}})
|
||||||
@ -361,7 +360,7 @@ func deleteVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Pa
|
|||||||
return deleteVulnerabilityRoute, http.StatusOK
|
return deleteVulnerabilityRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFixes(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func getFixes(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
dbVuln, err := ctx.Store.FindVulnerability(p.ByName("namespaceName"), p.ByName("vulnerabilityName"))
|
dbVuln, err := ctx.Store.FindVulnerability(p.ByName("namespaceName"), p.ByName("vulnerabilityName"))
|
||||||
if err == commonerr.ErrNotFound {
|
if err == commonerr.ErrNotFound {
|
||||||
writeResponse(w, r, http.StatusNotFound, FeatureEnvelope{Error: &Error{err.Error()}})
|
writeResponse(w, r, http.StatusNotFound, FeatureEnvelope{Error: &Error{err.Error()}})
|
||||||
@ -376,7 +375,7 @@ func getFixes(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *
|
|||||||
return getFixesRoute, http.StatusOK
|
return getFixesRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func putFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func putFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
request := FeatureEnvelope{}
|
request := FeatureEnvelope{}
|
||||||
err := decodeJSON(r, &request)
|
err := decodeJSON(r, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -420,7 +419,7 @@ func putFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *co
|
|||||||
return putFixRoute, http.StatusOK
|
return putFixRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func deleteFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
err := ctx.Store.DeleteVulnerabilityFix(p.ByName("vulnerabilityNamespace"), p.ByName("vulnerabilityName"), p.ByName("fixName"))
|
err := ctx.Store.DeleteVulnerabilityFix(p.ByName("vulnerabilityNamespace"), p.ByName("vulnerabilityName"), p.ByName("fixName"))
|
||||||
if err == commonerr.ErrNotFound {
|
if err == commonerr.ErrNotFound {
|
||||||
writeResponse(w, r, http.StatusNotFound, FeatureEnvelope{Error: &Error{err.Error()}})
|
writeResponse(w, r, http.StatusNotFound, FeatureEnvelope{Error: &Error{err.Error()}})
|
||||||
@ -434,7 +433,7 @@ func deleteFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx
|
|||||||
return deleteFixRoute, http.StatusOK
|
return deleteFixRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNotification(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func getNotification(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
query := r.URL.Query()
|
query := r.URL.Query()
|
||||||
|
|
||||||
limitStrs, limitExists := query["limit"]
|
limitStrs, limitExists := query["limit"]
|
||||||
@ -452,14 +451,14 @@ func getNotification(w http.ResponseWriter, r *http.Request, p httprouter.Params
|
|||||||
page := database.VulnerabilityNotificationFirstPage
|
page := database.VulnerabilityNotificationFirstPage
|
||||||
pageStrs, pageExists := query["page"]
|
pageStrs, pageExists := query["page"]
|
||||||
if pageExists {
|
if pageExists {
|
||||||
err := tokenUnmarshal(pageStrs[0], ctx.Config.PaginationKey, &page)
|
err := tokenUnmarshal(pageStrs[0], ctx.PaginationKey, &page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeResponse(w, r, http.StatusBadRequest, NotificationEnvelope{Error: &Error{"invalid page format: " + err.Error()}})
|
writeResponse(w, r, http.StatusBadRequest, NotificationEnvelope{Error: &Error{"invalid page format: " + err.Error()}})
|
||||||
return getNotificationRoute, http.StatusBadRequest
|
return getNotificationRoute, http.StatusBadRequest
|
||||||
}
|
}
|
||||||
pageToken = pageStrs[0]
|
pageToken = pageStrs[0]
|
||||||
} else {
|
} else {
|
||||||
pageTokenBytes, err := tokenMarshal(page, ctx.Config.PaginationKey)
|
pageTokenBytes, err := tokenMarshal(page, ctx.PaginationKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeResponse(w, r, http.StatusBadRequest, NotificationEnvelope{Error: &Error{"failed to marshal token: " + err.Error()}})
|
writeResponse(w, r, http.StatusBadRequest, NotificationEnvelope{Error: &Error{"failed to marshal token: " + err.Error()}})
|
||||||
return getNotificationRoute, http.StatusBadRequest
|
return getNotificationRoute, http.StatusBadRequest
|
||||||
@ -476,13 +475,13 @@ func getNotification(w http.ResponseWriter, r *http.Request, p httprouter.Params
|
|||||||
return getNotificationRoute, http.StatusInternalServerError
|
return getNotificationRoute, http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
notification := NotificationFromDatabaseModel(dbNotification, limit, pageToken, nextPage, ctx.Config.PaginationKey)
|
notification := NotificationFromDatabaseModel(dbNotification, limit, pageToken, nextPage, ctx.PaginationKey)
|
||||||
|
|
||||||
writeResponse(w, r, http.StatusOK, NotificationEnvelope{Notification: ¬ification})
|
writeResponse(w, r, http.StatusOK, NotificationEnvelope{Notification: ¬ification})
|
||||||
return getNotificationRoute, http.StatusOK
|
return getNotificationRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteNotification(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func deleteNotification(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
err := ctx.Store.DeleteNotification(p.ByName("notificationName"))
|
err := ctx.Store.DeleteNotification(p.ByName("notificationName"))
|
||||||
if err == commonerr.ErrNotFound {
|
if err == commonerr.ErrNotFound {
|
||||||
writeResponse(w, r, http.StatusNotFound, NotificationEnvelope{Error: &Error{err.Error()}})
|
writeResponse(w, r, http.StatusNotFound, NotificationEnvelope{Error: &Error{err.Error()}})
|
||||||
@ -496,7 +495,7 @@ func deleteNotification(w http.ResponseWriter, r *http.Request, p httprouter.Par
|
|||||||
return deleteNotificationRoute, http.StatusOK
|
return deleteNotificationRoute, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMetrics(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
func getMetrics(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context) (string, int) {
|
||||||
prometheus.Handler().ServeHTTP(w, r)
|
prometheus.Handler().ServeHTTP(w, r)
|
||||||
return getMetricsRoute, 0
|
return getMetricsRoute, 0
|
||||||
}
|
}
|
||||||
|
@ -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,7 +12,7 @@
|
|||||||
// 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 config
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -20,21 +20,19 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fernet/fernet-go"
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
|
"github.com/coreos/clair/api"
|
||||||
|
"github.com/coreos/clair/database"
|
||||||
|
"github.com/coreos/clair/ext/notification"
|
||||||
|
"github.com/fernet/fernet-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrDatasourceNotLoaded is returned when the datasource variable in the configuration file is not loaded properly
|
// ErrDatasourceNotLoaded is returned when the datasource variable in the
|
||||||
|
// configuration file is not loaded properly
|
||||||
var ErrDatasourceNotLoaded = errors.New("could not load configuration: no database source specified")
|
var ErrDatasourceNotLoaded = errors.New("could not load configuration: no database source specified")
|
||||||
|
|
||||||
// RegistrableComponentConfig is a configuration block that can be used to
|
|
||||||
// determine which registrable component should be initialized and pass
|
|
||||||
// custom configuration to it.
|
|
||||||
type RegistrableComponentConfig struct {
|
|
||||||
Type string
|
|
||||||
Options map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// File represents a YAML configuration file that namespaces all Clair
|
// File represents a YAML configuration file that namespaces all Clair
|
||||||
// configuration under the top-level "clair" key.
|
// configuration under the top-level "clair" key.
|
||||||
type File struct {
|
type File struct {
|
||||||
@ -43,57 +41,37 @@ type File struct {
|
|||||||
|
|
||||||
// Config is the global configuration for an instance of Clair.
|
// Config is the global configuration for an instance of Clair.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Database RegistrableComponentConfig
|
Database database.RegistrableComponentConfig
|
||||||
Updater *UpdaterConfig
|
Updater *clair.UpdaterConfig
|
||||||
Notifier *NotifierConfig
|
Notifier *notification.Config
|
||||||
API *APIConfig
|
API *api.Config
|
||||||
}
|
|
||||||
|
|
||||||
// UpdaterConfig is the configuration for the Updater service.
|
|
||||||
type UpdaterConfig struct {
|
|
||||||
Interval time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotifierConfig is the configuration for the Notifier service and its registered notifiers.
|
|
||||||
type NotifierConfig struct {
|
|
||||||
Attempts int
|
|
||||||
RenotifyInterval time.Duration
|
|
||||||
Params map[string]interface{} `yaml:",inline"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIConfig is the configuration for the API service.
|
|
||||||
type APIConfig struct {
|
|
||||||
Port int
|
|
||||||
HealthPort int
|
|
||||||
Timeout time.Duration
|
|
||||||
PaginationKey string
|
|
||||||
CertFile, KeyFile, CAFile string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultConfig is a configuration that can be used as a fallback value.
|
// DefaultConfig is a configuration that can be used as a fallback value.
|
||||||
func DefaultConfig() Config {
|
func DefaultConfig() Config {
|
||||||
return Config{
|
return Config{
|
||||||
Database: RegistrableComponentConfig{
|
Database: database.RegistrableComponentConfig{
|
||||||
Type: "pgsql",
|
Type: "pgsql",
|
||||||
},
|
},
|
||||||
Updater: &UpdaterConfig{
|
Updater: &clair.UpdaterConfig{
|
||||||
Interval: 1 * time.Hour,
|
Interval: 1 * time.Hour,
|
||||||
},
|
},
|
||||||
API: &APIConfig{
|
API: &api.Config{
|
||||||
Port: 6060,
|
Port: 6060,
|
||||||
HealthPort: 6061,
|
HealthPort: 6061,
|
||||||
Timeout: 900 * time.Second,
|
Timeout: 900 * time.Second,
|
||||||
},
|
},
|
||||||
Notifier: &NotifierConfig{
|
Notifier: ¬ification.Config{
|
||||||
Attempts: 5,
|
Attempts: 5,
|
||||||
RenotifyInterval: 2 * time.Hour,
|
RenotifyInterval: 2 * time.Hour,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load is a shortcut to open a file, read it, and generate a Config.
|
// LoadConfig is a shortcut to open a file, read it, and generate a Config.
|
||||||
|
//
|
||||||
// It supports relative and absolute paths. Given "", it returns DefaultConfig.
|
// It supports relative and absolute paths. Given "", it returns DefaultConfig.
|
||||||
func Load(path string) (config *Config, err error) {
|
func LoadConfig(path string) (config *Config, err error) {
|
||||||
var cfgFile File
|
var cfgFile File
|
||||||
cfgFile.Clair = DefaultConfig()
|
cfgFile.Clair = DefaultConfig()
|
||||||
if path == "" {
|
if path == "" {
|
@ -29,8 +29,6 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/clair"
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/api"
|
"github.com/coreos/clair/api"
|
||||||
"github.com/coreos/clair/api/context"
|
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/pkg/stopper"
|
"github.com/coreos/clair/pkg/stopper"
|
||||||
|
|
||||||
@ -88,7 +86,7 @@ func stopCPUProfiling(f *os.File) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Boot starts Clair instance with the provided config.
|
// Boot starts Clair instance with the provided config.
|
||||||
func Boot(config *config.Config) {
|
func Boot(config *Config) {
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
st := stopper.NewStopper()
|
st := stopper.NewStopper()
|
||||||
|
|
||||||
@ -105,9 +103,9 @@ func Boot(config *config.Config) {
|
|||||||
|
|
||||||
// Start API
|
// Start API
|
||||||
st.Begin()
|
st.Begin()
|
||||||
go api.Run(config.API, &context.RouteContext{db, config.API}, st)
|
go api.Run(config.API, db, st)
|
||||||
st.Begin()
|
st.Begin()
|
||||||
go api.RunHealth(config.API, &context.RouteContext{db, config.API}, st)
|
go api.RunHealth(config.API, db, st)
|
||||||
|
|
||||||
// Start updater
|
// Start updater
|
||||||
st.Begin()
|
st.Begin()
|
||||||
@ -136,7 +134,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load configuration
|
// Load configuration
|
||||||
config, err := config.Load(*flagConfigPath)
|
config, err := LoadConfig(*flagConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to load configuration: %s", err)
|
log.Fatalf("failed to load configuration: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -35,11 +33,19 @@ var (
|
|||||||
ErrInconsistent = errors.New("database: inconsistent database")
|
ErrInconsistent = errors.New("database: inconsistent database")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RegistrableComponentConfig is a configuration block that can be used to
|
||||||
|
// determine which registrable component should be initialized and pass custom
|
||||||
|
// configuration to it.
|
||||||
|
type RegistrableComponentConfig struct {
|
||||||
|
Type string
|
||||||
|
Options map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
var drivers = make(map[string]Driver)
|
var drivers = make(map[string]Driver)
|
||||||
|
|
||||||
// Driver is a function that opens a Datastore specified by its database driver type and specific
|
// Driver is a function that opens a Datastore specified by its database driver type and specific
|
||||||
// configuration.
|
// configuration.
|
||||||
type Driver func(config.RegistrableComponentConfig) (Datastore, error)
|
type Driver func(RegistrableComponentConfig) (Datastore, error)
|
||||||
|
|
||||||
// Register makes a Constructor available by the provided name.
|
// Register makes a Constructor available by the provided name.
|
||||||
//
|
//
|
||||||
@ -56,7 +62,7 @@ func Register(name string, driver Driver) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open opens a Datastore specified by a configuration.
|
// Open opens a Datastore specified by a configuration.
|
||||||
func Open(cfg config.RegistrableComponentConfig) (Datastore, error) {
|
func Open(cfg RegistrableComponentConfig) (Datastore, error) {
|
||||||
driver, ok := drivers[cfg.Type]
|
driver, ok := drivers[cfg.Type]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("database: unknown Driver %q (forgotten configuration or import?)", cfg.Type)
|
return nil, fmt.Errorf("database: unknown Driver %q (forgotten configuration or import?)", cfg.Type)
|
||||||
|
@ -31,7 +31,6 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/remind101/migrate"
|
"github.com/remind101/migrate"
|
||||||
|
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/database/pgsql/migrations"
|
"github.com/coreos/clair/database/pgsql/migrations"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
@ -114,11 +113,13 @@ type Config struct {
|
|||||||
FixturePath string
|
FixturePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// openDatabase opens a PostgresSQL-backed Datastore using the given configuration.
|
// openDatabase opens a PostgresSQL-backed Datastore using the given
|
||||||
// It immediately every necessary migrations. If ManageDatabaseLifecycle is specified,
|
// configuration.
|
||||||
// the database will be created first. If FixturePath is specified, every SQL queries that are
|
//
|
||||||
// present insides will be executed.
|
// It immediately runs all necessary migrations. If ManageDatabaseLifecycle is
|
||||||
func openDatabase(registrableComponentConfig config.RegistrableComponentConfig) (database.Datastore, error) {
|
// specified, the database will be created first. If FixturePath is specified,
|
||||||
|
// every SQL queries that are present insides will be executed.
|
||||||
|
func openDatabase(registrableComponentConfig database.RegistrableComponentConfig) (database.Datastore, error) {
|
||||||
var pg pgSQL
|
var pg pgSQL
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -21,8 +21,9 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
"github.com/pborman/uuid"
|
"github.com/pborman/uuid"
|
||||||
|
|
||||||
|
"github.com/coreos/clair/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
func openDatabaseForTest(testName string, loadFixture bool) (*pgSQL, error) {
|
func openDatabaseForTest(testName string, loadFixture bool) (*pgSQL, error) {
|
||||||
@ -34,7 +35,7 @@ func openDatabaseForTest(testName string, loadFixture bool) (*pgSQL, error) {
|
|||||||
return datastore, nil
|
return datastore, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateTestConfig(testName string, loadFixture bool) config.RegistrableComponentConfig {
|
func generateTestConfig(testName string, loadFixture bool) database.RegistrableComponentConfig {
|
||||||
dbName := "test_" + strings.ToLower(testName) + "_" + strings.Replace(uuid.New(), "-", "_", -1)
|
dbName := "test_" + strings.ToLower(testName) + "_" + strings.Replace(uuid.New(), "-", "_", -1)
|
||||||
|
|
||||||
var fixturePath string
|
var fixturePath string
|
||||||
@ -48,7 +49,7 @@ func generateTestConfig(testName string, loadFixture bool) config.RegistrableCom
|
|||||||
source = fmt.Sprintf(sourceEnv, dbName)
|
source = fmt.Sprintf(sourceEnv, dbName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return config.RegistrableComponentConfig{
|
return database.RegistrableComponentConfig{
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"source": source,
|
"source": source,
|
||||||
"cachesize": 0,
|
"cachesize": 0,
|
||||||
|
@ -22,10 +22,10 @@ package notification
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,11 +36,19 @@ var (
|
|||||||
senders = make(map[string]Sender)
|
senders = make(map[string]Sender)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Config is the configuration for the Notifier service and its registered
|
||||||
|
// notifiers.
|
||||||
|
type Config struct {
|
||||||
|
Attempts int
|
||||||
|
RenotifyInterval time.Duration
|
||||||
|
Params map[string]interface{} `yaml:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
// Sender represents anything that can transmit notifications.
|
// Sender represents anything that can transmit notifications.
|
||||||
type Sender interface {
|
type Sender interface {
|
||||||
// Configure attempts to initialize the notifier with the provided configuration.
|
// Configure attempts to initialize the notifier with the provided configuration.
|
||||||
// It returns whether the notifier is enabled or not.
|
// It returns whether the notifier is enabled or not.
|
||||||
Configure(*config.NotifierConfig) (bool, error)
|
Configure(*Config) (bool, error)
|
||||||
|
|
||||||
// Send informs the existence of the specified notification.
|
// Send informs the existence of the specified notification.
|
||||||
Send(notification database.VulnerabilityNotification) error
|
Send(notification database.VulnerabilityNotification) error
|
||||||
|
@ -29,7 +29,6 @@ import (
|
|||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/notification"
|
"github.com/coreos/clair/ext/notification"
|
||||||
)
|
)
|
||||||
@ -55,7 +54,7 @@ func init() {
|
|||||||
notification.RegisterSender("webhook", &sender{})
|
notification.RegisterSender("webhook", &sender{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sender) Configure(config *config.NotifierConfig) (bool, error) {
|
func (s *sender) Configure(config *notification.Config) (bool, error) {
|
||||||
// Get configuration
|
// Get configuration
|
||||||
var httpConfig Config
|
var httpConfig Config
|
||||||
if config == nil {
|
if config == nil {
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"github.com/pborman/uuid"
|
"github.com/pborman/uuid"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/notification"
|
"github.com/coreos/clair/ext/notification"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
@ -54,7 +53,7 @@ func init() {
|
|||||||
|
|
||||||
// RunNotifier begins a process that checks for new notifications that should
|
// RunNotifier begins a process that checks for new notifications that should
|
||||||
// be sent out to third parties.
|
// be sent out to third parties.
|
||||||
func RunNotifier(config *config.NotifierConfig, datastore database.Datastore, stopper *stopper.Stopper) {
|
func RunNotifier(config *notification.Config, datastore database.Datastore, stopper *stopper.Stopper) {
|
||||||
defer stopper.End()
|
defer stopper.End()
|
||||||
|
|
||||||
// Configure registered notifiers.
|
// Configure registered notifiers.
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
"github.com/pborman/uuid"
|
"github.com/pborman/uuid"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/vulnmdsrc"
|
"github.com/coreos/clair/ext/vulnmdsrc"
|
||||||
"github.com/coreos/clair/ext/vulnsrc"
|
"github.com/coreos/clair/ext/vulnsrc"
|
||||||
@ -63,9 +62,14 @@ func init() {
|
|||||||
prometheus.MustRegister(promUpdaterNotesTotal)
|
prometheus.MustRegister(promUpdaterNotesTotal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdaterConfig is the configuration for the Updater service.
|
||||||
|
type UpdaterConfig struct {
|
||||||
|
Interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
// RunUpdater begins a process that updates the vulnerability database at
|
// RunUpdater begins a process that updates the vulnerability database at
|
||||||
// regular intervals.
|
// regular intervals.
|
||||||
func RunUpdater(config *config.UpdaterConfig, datastore database.Datastore, st *stopper.Stopper) {
|
func RunUpdater(config *UpdaterConfig, datastore database.Datastore, st *stopper.Stopper) {
|
||||||
defer st.End()
|
defer st.End()
|
||||||
|
|
||||||
// Do not run the updater if there is no config or if the interval is 0.
|
// Do not run the updater if there is no config or if the interval is 0.
|
||||||
|
@ -78,9 +78,9 @@ func TestProcessWithDistUpgrade(t *testing.T) {
|
|||||||
// wheezy.tar: FROM debian:wheezy
|
// wheezy.tar: FROM debian:wheezy
|
||||||
// jessie.tar: RUN sed -i "s/precise/trusty/" /etc/apt/sources.list && apt-get update &&
|
// jessie.tar: RUN sed -i "s/precise/trusty/" /etc/apt/sources.list && apt-get update &&
|
||||||
// apt-get -y dist-upgrade
|
// apt-get -y dist-upgrade
|
||||||
assert.Nil(t, Process(datastore, "Docker", "blank", "", testDataPath+"blank.tar.gz", nil))
|
assert.Nil(t, ProcessLayer(datastore, "Docker", "blank", "", testDataPath+"blank.tar.gz", nil))
|
||||||
assert.Nil(t, Process(datastore, "Docker", "wheezy", "blank", testDataPath+"wheezy.tar.gz", nil))
|
assert.Nil(t, ProcessLayer(datastore, "Docker", "wheezy", "blank", testDataPath+"wheezy.tar.gz", nil))
|
||||||
assert.Nil(t, Process(datastore, "Docker", "jessie", "wheezy", testDataPath+"jessie.tar.gz", nil))
|
assert.Nil(t, ProcessLayer(datastore, "Docker", "jessie", "wheezy", testDataPath+"jessie.tar.gz", nil))
|
||||||
|
|
||||||
// Ensure that the 'wheezy' layer has the expected namespace and features.
|
// Ensure that the 'wheezy' layer has the expected namespace and features.
|
||||||
wheezy, ok := datastore.layers["wheezy"]
|
wheezy, ok := datastore.layers["wheezy"]
|
||||||
|
Loading…
Reference in New Issue
Block a user