Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config: add OTLP configuration types #1870

Merged
merged 1 commit into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions config/doc.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Package config is the configuration package for Clair's binaries. See the
// Config type for the main entry point.
// [Config] type for the main entry point.
//
// It's currently meant for reading configs and tested against YAML and JSON.
// It's currently meant for reading configurations and tested against YAML and
// JSON.
//
// # Version Scheme
//
Expand All @@ -16,7 +17,7 @@
// changes on a program importing the module.
package config

// This pakcage can't use "omitempty" tags on slices because "not present" and
// This package can't use "omitempty" tags on slices because "not present" and
// "empty" aren't distinguished. This would be much easier if code didn't
// serialize our config struct. It's impossible to implement custom YAML
// marshalling without importing the yaml.v3 package.
29 changes: 21 additions & 8 deletions config/introspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@ import "fmt"
// Trace specifies how to configure Clair's tracing support.
//
// The "Name" key must match the provider to use.
//
// Currently, only "jaeger" is supported.
type Trace struct {
Name string `yaml:"name" json:"name"`
Probability *float64 `yaml:"probability,omitempty" json:"probability,omitempty"`
Jaeger Jaeger `yaml:"jaeger,omitempty" json:"jaeger,omitempty"`
Name string `yaml:"name" json:"name"`
Probability *float64 `yaml:"probability,omitempty" json:"probability,omitempty"`
Jaeger Jaeger `yaml:"jaeger,omitempty" json:"jaeger,omitempty"`
OTLP TraceOTLP `yaml:"otlp,omitempty" json:"otlp,omitempty"`
}

func (t *Trace) lint() ([]Warning, error) {
switch t.Name {
case "":
case "otlp":
case "jaeger":
return []Warning{{
path: ".name",
msg: `trace provider "jaeger" is deprecated; migrate to "otlp"`,
}}, nil
default:
return []Warning{{
path: ".name",
Expand All @@ -27,6 +31,11 @@ func (t *Trace) lint() ([]Warning, error) {
}

// Jaeger specific distributed tracing configuration.
//
// Deprecated: The Jaeger project recommends using their OTLP ingestion support
// and the OpenTelemetry exporter for Jaeger has since been removed. Users
// should migrate to OTLP. Clair may refuse to start when configured to emit
// Jaeger traces.
type Jaeger struct {
Tags map[string]string `yaml:"tags,omitempty" json:"tags,omitempty"`
Agent struct {
Expand All @@ -44,16 +53,20 @@ type Jaeger struct {
// Metrics specifies how to configure Clair's metrics exporting.
//
// The "Name" key must match the provider to use.
//
// Currently, only "prometheus" is supported.
type Metrics struct {
Prometheus Prometheus `yaml:"prometheus,omitempty" json:"prometheus,omitempty"`
Name string `yaml:"name" json:"name"`
Prometheus Prometheus `yaml:"prometheus,omitempty" json:"prometheus,omitempty"`
OTLP MetricOTLP `yaml:"otlp,omitempty" json:"otlp,omitempty"`
}

func (m *Metrics) lint() ([]Warning, error) {
switch m.Name {
case "":
case "otlp":
return []Warning{{
path: ".name",
msg: `please consult the documentation for the status of metrics via "otlp"`,
}}, nil
case "prometheus":
default:
return []Warning{{
Expand Down
180 changes: 180 additions & 0 deletions config/otlp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package config

import (
"fmt"
"path"
"strings"
)

// OTLPCommon is common configuration options for an OTLP client.
type OTLPCommon struct {
// Compression configures payload compression.
//
// Only "gzip" is guaranteed to exist for both HTTP and gRPC.
Compression OTLPCompressor `yaml:"compression,omitempty" json:"compression,omitempty"`
// Endpoint is the host and port pair that the client should connect to.
// This is not a URL and must not have a scheme or trailing slashes.
//
// The default is "localhost:4317" for gRPC and "localhost:4318" for HTTP.
Endpoint string `yaml:"endpoint,omitempty" json:"endpoint,omitempty"`
// Headers adds additional headers to requests.
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
// Insecure allows using an unsecured connection to the collector.
//
// For gRPC, this means certificate validation is not done.
// For HTTP, this means HTTP is used instead of HTTPS.
Insecure bool `yaml:"insecure,omitempty" json:"insecure,omitempty"`
// Timeout is the maximum amount of time for a submission.
//
// The default is 10 seconds.
Timeout *Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`
// ClientTLS configures client TLS certificates, meaning a user should
// ignore the "RootCA" member and look only at the "Cert" and "Key" members.
//
// See the documentation for the TLS struct for recommendations on
// configuring certificate authorities.
ClientTLS *TLS `yaml:"client_tls,omitempty" json:"client_tls,omitempty"`
}

// Lint implements [linter].
func (c *OTLPCommon) lint() (ws []Warning, _ error) {
if c.Timeout != nil && *c.Timeout == 0 {
ws = append(ws, Warning{
path: ".timeout",
msg: "timeout of 0 is almost certainly wrong",
})
}
return ws, nil
}

// Validate implements [validator].
func (c *OTLPCommon) validate(_ Mode) (ws []Warning, _ error) {
return c.lint()
}

// OTLPHTTPCommon is common configuration options for an OTLP HTTP client.
type OTLPHTTPCommon struct {
OTLPCommon
// URLPath overrides the URL path for sending traces. If unset, the default
// is "/v1/traces".
URLPath string `yaml:"url_path,omitempty" json:"url_path,omitempty"`
}

// Lint implements [linter].
func (c *OTLPHTTPCommon) lint() (ws []Warning, _ error) {
if c.URLPath != "" && strings.HasSuffix(c.URLPath, "/") {
ws = append(ws, Warning{
path: ".URLPath",
msg: fmt.Sprintf("path %q has a trailing slash; this is probably incorrect", c.URLPath),
})
}
return ws, nil
}

// Validate implements [validator].
func (c *OTLPHTTPCommon) validate(_ Mode) (ws []Warning, err error) {
ws, err = c.lint()
if err != nil {
return ws, err
}
if c.URLPath != "" {
c.URLPath = path.Clean(c.URLPath)
if !path.IsAbs(c.URLPath) {
return ws, &Warning{
path: ".URLPath",
msg: fmt.Sprintf("path %q must be absolute", c.URLPath),
}
}
}
return ws, nil
}

// OTLPgRPCCommon is common configuration options for an OTLP gRPC client.
type OTLPgRPCCommon struct {
OTLPCommon
// Reconnect sets the minimum amount of time between connection attempts.
Reconnect *Duration `yaml:"reconnect,omitempty" json:"reconnect,omitempty"`
// ServiceConfig specifies a gRPC service config as a string containing JSON.
// See the [doc] for the format and possibilities.
//
// [doc]: https://github.com/grpc/grpc/blob/master/doc/service_config.md
ServiceConfig string `yaml:"service_config,omitempty" json:"service_config,omitempty"`
}

// TraceOTLP is the configuration for an OTLP traces client.
//
// See the [OpenTelemetry docs] for more information on traces.
// See the Clair docs for the current status of of the instrumentation.
//
// [OpenTelemetry docs]: https://opentelemetry.io/docs/concepts/signals/traces/
type TraceOTLP struct {
// HTTP configures OTLP via HTTP.
HTTP *TraceOTLPHTTP `yaml:"http,omitempty" json:"http,omitempty"`
// GRPC configures OTLP via gRPC.
GRPC *TraceOTLPgRPC `yaml:"grpc,omitempty" json:"grpc,omitempty"`
}

// Lint implements [linter].
func (t *TraceOTLP) lint() (ws []Warning, _ error) {
if t.HTTP != nil && t.GRPC != nil {
ws = append(ws, Warning{
msg: `both "http" and "grpc" are configured, this may cause duplicate submissions`,
})
}
return ws, nil
}

// TraceOTLPHTTP is the configuration for an OTLP traces HTTP client.
type TraceOTLPHTTP struct {
OTLPHTTPCommon
}

// TraceOTLPgRPC is the configuration for an OTLP traces gRPC client.
type TraceOTLPgRPC struct {
OTLPgRPCCommon
}

// MetricOTLP is the configuration for an OTLP metrics client.
//
// See the [OpenTelemetry docs] for more information on metrics.
// See the Clair docs for the current status of of the instrumentation.
//
// [OpenTelemetry docs]: https://opentelemetry.io/docs/concepts/signals/metrics/
type MetricOTLP struct {
// HTTP configures OTLP via HTTP.
HTTP *MetricOTLPHTTP `yaml:"http,omitempty" json:"http,omitempty"`
// GRPC configures OTLP via gRPC.
GRPC *MetricOTLPgRPC `yaml:"grpc,omitempty" json:"grpc,omitempty"`
}

// Lint implements [linter].
func (m *MetricOTLP) lint() (ws []Warning, _ error) {
if m.HTTP != nil && m.GRPC != nil {
ws = append(ws, Warning{
msg: `both "http" and "grpc" are configured, this may cause duplicate submissions`,
})
}
return ws, nil
}

// MetricOTLPHTTP is the configuration for an OTLP metrics HTTP client.
type MetricOTLPHTTP struct {
OTLPHTTPCommon
}

// MetricOTLPgRPC is the configuration for an OTLP metrics gRPC client.
type MetricOTLPgRPC struct {
OTLPgRPCCommon
}

//go:generate go run golang.org/x/tools/cmd/stringer@latest -type OTLPCompressor -linecomment

// OTLPCompressor is the valid options for compressing OTLP payloads.
type OTLPCompressor int

// OTLPCompressor values
const (
OTLPCompressUnset OTLPCompressor = iota //
OTLPCompressNone // none
OTLPCompressGzip // gzip
)
25 changes: 25 additions & 0 deletions config/otlpcompressor_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions config/tags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,20 @@ var wanttags = []string{`json`, `yaml`}
func typecheck(t *testing.T, typ reflect.Type) {
for i, lim := 0, typ.NumField(); i < lim; i++ {
f := typ.Field(i)
if f.PkgPath != "" {
// TODO(hank) Use the IsExported method once 1.16 support is
// dropped.
if !f.IsExported() {
continue
}
// track the number of names for this field
vals := make(map[string]struct{})
// track which tag has which name
tagval := make(map[string]string)
// If embedded, there shouldn't be any tags.
if f.Anonymous {
if f.Tag != "" {
t.Errorf("%s.%s: unexpected tag %q", typ.Name(), f.Name, f.Tag)
}
goto Recurse
}
for _, n := range wanttags {
if v, ok := f.Tag.Lookup(n); !ok {
t.Errorf("%s.%s: missing %q tag", typ.Name(), f.Name, n)
Expand All @@ -38,6 +43,7 @@ func typecheck(t *testing.T, typ reflect.Type) {
if len(vals) != 1 {
t.Errorf("different names for %q: %v", f.Name, tagval)
}
Recurse:
// Recurse on structs and pointers-to-structs.
switch nt := f.Type; nt.Kind() {
case reflect.Ptr:
Expand Down
13 changes: 8 additions & 5 deletions config/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,22 @@ import (

// TLS describes some TLS settings.
//
// These are currently only used in the Notifier. Using the environment
// variables "SSL_CERT_DIR" or "SSL_CERT_FILE" or modifying the system's trust
// store are the ways to modify root CAs for all outgoing TLS connections.
// Some uses of this type ignore the RootCA member; see the documentation at the
// use site to determine if that's the case.
//
// Using the environment variables "SSL_CERT_DIR" or "SSL_CERT_FILE" or
// modifying the system's trust store are the ways to modify root CAs for all
// outgoing TLS connections.
type TLS struct {
// The filesystem path where a root CA can be read.
//
// This can also be controlled by the SSL_CERT_FILE and SSL_CERT_DIR
// environment variables, or adding the relevant certs to the system trust
// store.
RootCA string `yaml:"root_ca" json:"root_ca"`
// The filesystem path where a tls certificate can be read.
// The filesystem path where a TLS certificate can be read.
Cert string `yaml:"cert" json:"cert"`
// The filesystem path where a tls private key can be read.
// The filesystem path where a TLS private key can be read.
Key string `yaml:"key" json:"key"`
}

Expand Down
Loading