Support fetching observability from ctx. (#2610)

* add option to load observability config from ctx

Signed-off-by: Kenny Leung <kleung@chainguard.dev>

* comments and tests

Signed-off-by: Kenny Leung <kleung@chainguard.dev>

* undo

Signed-off-by: Kenny Leung <kleung@chainguard.dev>

* move observability setup logic into function to match logger

Signed-off-by: Kenny Leung <kleung@chainguard.dev>

* fix arg

Signed-off-by: Kenny Leung <kleung@chainguard.dev>

Signed-off-by: Kenny Leung <kleung@chainguard.dev>
This commit is contained in:
Kenny Leung 2022-10-14 09:45:53 -07:00 committed by GitHub
parent 714b7630a8
commit b812affa38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 141 additions and 0 deletions

View File

@ -104,6 +104,25 @@ func GetLeaderElectionConfig(ctx context.Context) (*leaderelection.Config, error
return leaderelection.NewConfigFromConfigMap(leaderElectionConfigMap)
}
// GetObservabilityConfig gets the observability config from the (in order):
// 1. provided context,
// 2. reading from the API server,
// 3. defaults (if not found).
func GetObservabilityConfig(ctx context.Context) (*metrics.ObservabilityConfig, error) {
if cfg := metrics.GetObservabilityConfig(ctx); cfg != nil {
return cfg, nil
}
observabilityConfigMap, err := kubeclient.Get(ctx).CoreV1().ConfigMaps(system.Namespace()).Get(ctx, metrics.ConfigMapName(), metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return metrics.NewObservabilityConfigFromConfigMap(nil)
}
if err != nil {
return nil, err
}
return metrics.NewObservabilityConfigFromConfigMap(observabilityConfigMap)
}
// EnableInjectionOrDie enables Knative Injection and starts the informers.
// Both Context and Config are optional.
// Deprecated: use injection.EnableInjectionOrDie
@ -249,6 +268,8 @@ func MainWithConfig(ctx context.Context, component string, cfg *rest.Config, cto
leaderElectionConfig.GetComponentConfig(component))
}
SetupObservabilityOrDie(ctx, component, logger, profilingHandler)
controllers, webhooks := ControllersAndWebhooksFromCtors(ctx, cmw, ctors...)
WatchLoggingConfigOrDie(ctx, cmw, logger, atomicLevel, component)
WatchObservabilityConfigOrDie(ctx, cmw, profilingHandler, logger, component)
@ -327,6 +348,18 @@ func SetupLoggerOrDie(ctx context.Context, component string) (*zap.SugaredLogger
return l, level
}
// SetupObservabilityOrDie sets up the observability using the config from the given context
// or dies by calling log.Fatalf.
func SetupObservabilityOrDie(ctx context.Context, component string, logger *zap.SugaredLogger, profilingHandler *profiling.Handler) {
observabilityConfig, err := GetObservabilityConfig(ctx)
if err != nil {
logger.Fatal("Error loading observability configuration: ", err)
}
observabilityConfigMap := observabilityConfig.GetConfigMap()
metrics.ConfigMapWatcher(ctx, component, SecretFetcher(ctx), logger)(&observabilityConfigMap)
profilingHandler.UpdateFromConfigMap(&observabilityConfigMap)
}
// CheckK8sClientMinimumVersionOrDie checks that the hosting Kubernetes cluster
// is at least the minimum allowable version or dies by calling log.Fatalw.
func CheckK8sClientMinimumVersionOrDie(ctx context.Context, logger *zap.SugaredLogger) {

View File

@ -26,6 +26,7 @@ import (
"knative.dev/pkg/injection"
"knative.dev/pkg/leaderelection"
"knative.dev/pkg/logging"
"knative.dev/pkg/metrics"
)
func TestEnabledControllers(t *testing.T) {
@ -103,3 +104,25 @@ func TestWithLeaderElectionConfig(t *testing.T) {
t.Errorf("(-got, +want) = %s", diff)
}
}
func TextWithObservabilityConfig(t *testing.T) {
want := &metrics.ObservabilityConfig{
EnableVarLogCollection: false,
LoggingURLTemplate: "url-template",
RequestLogTemplate: "log-template",
EnableProbeRequestLog: true,
RequestMetricsBackend: "prometheus",
EnableProfiling: true,
EnableRequestLog: false,
MetricsCollectorAddress: "localhost:9090",
}
ctx := metrics.WithConfig(context.Background(), want)
got, err := GetObservabilityConfig(ctx)
if err != nil {
t.Fatalf("GetObservabilityConfig() = %v", err)
}
if diff := cmp.Diff(got, want); diff != "" {
t.Errorf("(-got, +want) = %s", diff)
}
}

View File

@ -17,8 +17,10 @@ limitations under the License.
package metrics
import (
"context"
"fmt"
"os"
"strconv"
texttemplate "text/template"
corev1 "k8s.io/api/core/v1"
@ -81,6 +83,22 @@ type ObservabilityConfig struct {
MetricsCollectorAddress string
}
type ocfg struct{}
// WithConfig associates a observability configuration with the context.
func WithConfig(ctx context.Context, cfg *ObservabilityConfig) context.Context {
return context.WithValue(ctx, ocfg{}, cfg)
}
// GetObservability gets the observability config from the provided context.
func GetObservabilityConfig(ctx context.Context) *ObservabilityConfig {
untyped := ctx.Value(ocfg{})
if untyped == nil {
return nil
}
return untyped.(*ObservabilityConfig)
}
func defaultConfig() *ObservabilityConfig {
return &ObservabilityConfig{
LoggingURLTemplate: DefaultLogURLTemplate,
@ -120,6 +138,21 @@ func NewObservabilityConfigFromConfigMap(configMap *corev1.ConfigMap) (*Observab
return oc, nil
}
func (oc *ObservabilityConfig) GetConfigMap() corev1.ConfigMap {
return corev1.ConfigMap{
Data: map[string]string{
"logging.enable-var-log-collection": strconv.FormatBool(oc.EnableVarLogCollection),
"logging.revision-url-template": oc.LoggingURLTemplate,
ReqLogTemplateKey: oc.RequestLogTemplate,
EnableReqLogKey: strconv.FormatBool(oc.EnableRequestLog),
EnableProbeReqLogKey: strconv.FormatBool(oc.EnableProbeRequestLog),
"metrics.request-metrics-backend-destination": oc.RequestMetricsBackend,
"profiling.enable": strconv.FormatBool(oc.EnableProfiling),
"metrics.opencensus-address": oc.MetricsCollectorAddress,
},
}
}
// ConfigMapName gets the name of the metrics ConfigMap
func ConfigMapName() string {
if cm := os.Getenv(configMapNameEnv); cm != "" {

View File

@ -154,3 +154,55 @@ func TestConfigMapName(t *testing.T) {
t.Errorf("ConfigMapName = %q, want: %q", got, want)
}
}
func TestObservabilityConfigMap(t *testing.T) {
observabilityConfigMapTests := []struct {
name string
config *ObservabilityConfig
wantConfigMapData map[string]string
}{{
name: "observability configuration with all inputs",
config: &ObservabilityConfig{
EnableProbeRequestLog: true,
EnableProfiling: true,
EnableVarLogCollection: true,
EnableRequestLog: true,
LoggingURLTemplate: "https://logging.io",
RequestLogTemplate: `{"requestMethod": "{{.Request.Method}}"}`,
RequestMetricsBackend: "opencensus",
},
wantConfigMapData: map[string]string{
EnableProbeReqLogKey: "true",
"logging.enable-var-log-collection": "true",
ReqLogTemplateKey: `{"requestMethod": "{{.Request.Method}}"}`,
"logging.revision-url-template": "https://logging.io",
EnableReqLogKey: "true",
"metrics.request-metrics-backend-destination": "opencensus",
"profiling.enable": "true",
"metrics.opencensus-address": "",
},
}, {
name: "observability configuration default config",
config: defaultConfig(),
wantConfigMapData: map[string]string{
EnableProbeReqLogKey: "false",
"logging.enable-var-log-collection": "false",
ReqLogTemplateKey: `{"httpRequest": {"requestMethod": "{{.Request.Method}}", "requestUrl": "{{js .Request.RequestURI}}", "requestSize": "{{.Request.ContentLength}}", "status": {{.Response.Code}}, "responseSize": "{{.Response.Size}}", "userAgent": "{{js .Request.UserAgent}}", "remoteIp": "{{js .Request.RemoteAddr}}", "serverIp": "{{.Revision.PodIP}}", "referer": "{{js .Request.Referer}}", "latency": "{{.Response.Latency}}s", "protocol": "{{.Request.Proto}}"}, "traceId": "{{index .Request.Header "X-B3-Traceid"}}"}`,
"logging.revision-url-template": "",
EnableReqLogKey: "false",
"metrics.request-metrics-backend-destination": "prometheus",
"profiling.enable": "false",
"metrics.opencensus-address": "",
},
}}
for _, tt := range observabilityConfigMapTests {
t.Run(tt.name, func(t *testing.T) {
cm := tt.config.GetConfigMap()
if got, want := cm.Data, tt.wantConfigMapData; !cmp.Equal(got, want) {
t.Errorf("Got = %v, want: %v, diff(-want,+got)\n%s", got, want, cmp.Diff(want, got))
}
})
}
}