Skip to content

Commit

Permalink
clair: add configuration for introspection
Browse files Browse the repository at this point in the history
  • Loading branch information
hdonnay committed Mar 5, 2020
1 parent d9db7c1 commit a003aa4
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 22 deletions.
158 changes: 141 additions & 17 deletions cmd/clair/introspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ package main
import (
"context"
"fmt"
"net"
"net/http"
"net/http/pprof"
"strings"

"github.com/rs/zerolog"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/exporter/metric/dogstatsd"
"go.opentelemetry.io/otel/exporter/metric/prometheus"
"go.opentelemetry.io/otel/exporter/trace/jaeger"
"go.opentelemetry.io/otel/exporter/trace/stdout"
sdktrace "go.opentelemetry.io/otel/sdk/trace"

Expand All @@ -22,32 +26,151 @@ const (
func introspection(ctx context.Context, cfg *config.Config, healthCheck func() bool) (*http.Server, error) {
mux := http.NewServeMux()
srv := http.Server{
Addr: cfg.IntrospectionAddr,
Handler: mux,
BaseContext: func(_ net.Listener) context.Context { return ctx },
Addr: cfg.IntrospectionAddr,
}
log := zerolog.Ctx(ctx).With().Logger()

// TODO Make configurable.
pipeline, hf, err := prometheus.InstallNewPipeline(prometheus.Config{})
if err != nil {
return nil, err
// Metrics config
if n := cfg.Metrics.Name; n != "" {
log.Info().Str("sink", n).Msg("configuring metrics sink")
}
switch cfg.Metrics.Name {
case "":
log.Info().Str("sink", "default").Msg("configuring metrics sink")
fallthrough
case "prometheus":
endpoint := "/metrics"
if cfg.Metrics.Prometheus.Endpoint != nil {
endpoint = *cfg.Metrics.Prometheus.Endpoint
}
log.Info().
Str("endpoint", endpoint).
Msg("configuring prometheus")
promlog := log.With().
Str("component", "metrics-exporter").
Logger()
pipeline, hf, err := prometheus.InstallNewPipeline(prometheus.Config{
OnError: func(err error) {
promlog.Error().Err(err).Msg("prometheus error")
},
})
if err != nil {
return nil, err
}
srv.RegisterOnShutdown(pipeline.Stop)
mux.HandleFunc(endpoint, hf)
case "dogstatsd":
log.Info().
Str("endpoint", cfg.Metrics.Dogstatsd.URL).
Msg("configuring dogstatsd")
pipeline, err := dogstatsd.InstallNewPipeline(dogstatsd.Config{
URL: cfg.Metrics.Dogstatsd.URL,
})
if err != nil {
return nil, err
}
srv.RegisterOnShutdown(pipeline.Stop)
default:
}
srv.RegisterOnShutdown(pipeline.Stop)

// TODO Make configurable.
exporter, err := stdout.NewExporter(stdout.Options{PrettyPrint: true})
if err != nil {
return nil, err
// Trace config
traceCfg := sdktrace.Config{
// By default, assume an ingress is tagging incoming requests for
// tracing.
DefaultSampler: sdktrace.NeverSample(),
}
if cfg.Mode == config.DevMode {
// Unless we're in dev mode, then go ham.
traceCfg.DefaultSampler = sdktrace.AlwaysSample()
}
if p := cfg.Trace.Probability; p != nil {
traceCfg.DefaultSampler = sdktrace.ProbabilitySampler(*p)
}
traceOpts := []sdktrace.ProviderOption{
sdktrace.WithConfig(traceCfg),
}
if n := cfg.Trace.Name; n != "" {
log.Info().Str("sink", n).Msg("configuring trace sink")
}
Trace:
switch cfg.Trace.Name {
case "stdout":
exporter, err := stdout.NewExporter(stdout.Options{})
if err != nil {
return nil, err
}
traceOpts = append(traceOpts, sdktrace.WithSyncer(exporter))
case "jaeger":
jcfg := &cfg.Trace.Jaeger
var e jaeger.EndpointOption
lev := log.Info()
switch {
case jcfg.Agent.Endpoint == "":
jcfg.Agent.Endpoint = "localhost:6831"
fallthrough
case jcfg.Agent.Endpoint != "":
lev = lev.Str("agent", jcfg.Agent.Endpoint)
e = jaeger.WithAgentEndpoint(jcfg.Agent.Endpoint)
case jcfg.Collector.Endpoint != "":
var opt []jaeger.CollectorEndpointOption
u, p := jcfg.Collector.Username, jcfg.Collector.Password
if u != nil {
lev = lev.Str("username", *u)
opt = append(opt, jaeger.WithUsername(*u))
}
if p != nil {
lev = lev.Str("password", strings.Repeat("*", len(*p)))
opt = append(opt, jaeger.WithPassword(*p))
}
e = jaeger.WithCollectorEndpoint(jcfg.Collector.Endpoint, opt...)
default:
lev.Msg("neither jaeger collector nor agent specified")
break Trace
}

jaegerlog := log.With().
Str("component", "trace-exporter").
Logger()
opts := []jaeger.Option{
jaeger.WithOnError(func(err error) {
jaegerlog.Error().Err(err).Msg("jaeger error")
}),
}
if jcfg.BufferMax != 0 {
lev = lev.Int("buffer_max", jcfg.BufferMax)
opts = append(opts, jaeger.WithBufferMaxCount(jcfg.BufferMax))
}
if jcfg.ServiceName == "" {
jcfg.ServiceName = "clairv4/" + cfg.Mode
}
p := jaeger.Process{
ServiceName: jcfg.ServiceName,
}
if len(jcfg.Tags) != 0 {
d := zerolog.Dict()
for k, v := range jcfg.Tags {
d.Str(k, v)
p.Tags = append(p.Tags, key.String(k, v))
}
lev = lev.Dict("tags", d)
}
opts = append(opts, jaeger.WithProcess(p))
lev.Str("service_name", jcfg.ServiceName).
Msg("configuring jaeger")
exporter, err := jaeger.NewExporter(e, opts...)
if err != nil {
return nil, err
}
traceOpts = append(traceOpts, sdktrace.WithSyncer(exporter))
srv.RegisterOnShutdown(exporter.Flush)
default:
}
tp, err := sdktrace.NewProvider(
sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.NeverSample()}),
sdktrace.WithSyncer(exporter))
tp, err := sdktrace.NewProvider(traceOpts...)
if err != nil {
return nil, err
}
global.SetTraceProvider(tp)

mux.HandleFunc("/metrics", hf)
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
Expand All @@ -62,5 +185,6 @@ func introspection(ctx context.Context, cfg *config.Config, healthCheck func() b
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
srv.Handler = mux
return &srv, nil
}
34 changes: 33 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type Config struct {
Auth Auth `yaml:"auth"`
// Tracing config
Trace Trace `yaml:"trace"`
// Metrics config
Metrics Metrics `yaml:"metrics"`
}

type Auth struct {
Expand Down Expand Up @@ -70,7 +72,37 @@ type Matcher struct {
}

type Trace struct {
Probability *float64 `yaml:probability`
Name string `yaml:"name"`
Probability *float64 `yaml:"probability"`
Jaeger Jaeger `yaml:",inline"`
}

type Jaeger struct {
Agent struct {
Endpoint string `yaml:"agent_endpoint"`
} `yaml:",inline"`
Collector struct {
Endpoint string `yaml:"collector_endpoint"`
Username *string `yaml:"username"`
Password *string `yaml:"password"`
} `yaml:",inline"`
ServiceName string `yaml:"service_name"`
Tags map[string]string `yaml:"tags"`
BufferMax int `yaml:"buffer_max"`
}

type Metrics struct {
Name string `yaml:"name"`
Prometheus Prometheus `yaml:",inline"`
Dogstatsd Dogstatsd `yaml:",inline"`
}

type Prometheus struct {
Endpoint *string `yaml:"endpoint"`
}

type Dogstatsd struct {
URL string `yaml:"url"`
}

func Validate(conf Config) error {
Expand Down
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ module github.com/quay/clair/v4
go 1.13

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
github.com/klauspost/compress v1.9.4
github.com/mattn/go-sqlite3 v1.11.0 // indirect
github.com/quay/claircore v0.0.16
github.com/prometheus/client_golang v0.9.4 // indirect
github.com/prometheus/procfs v0.0.8 // indirect
github.com/rs/zerolog v1.16.0
go.opentelemetry.io/otel v0.2.1
go.opentelemetry.io/otel/exporter/metric/prometheus v0.2.1
go.opentelemetry.io/otel/exporter/metric/prometheus v0.2.2-0.20200111012159-d85178b63b15
go.opentelemetry.io/otel/exporter/trace/jaeger v0.2.1
golang.org/x/tools v0.0.0-20191210200704-1bcf67c9cb49 // indirect
gopkg.in/square/go-jose.v2 v2.4.1
gopkg.in/yaml.v2 v2.2.5
Expand Down

0 comments on commit a003aa4

Please sign in to comment.