pkg/metrics/config.go

124 lines
3.8 KiB
Go

/*
Copyright 2018 The Knative 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 metrics
import (
"errors"
"fmt"
"strings"
"go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
)
const (
backendDestinationKey = "metrics.backend-destination"
stackdriverProjectIDKey = "metrics.stackdriver-project-id"
)
type MetricsBackend string
const (
// The metrics backend is stackdriver
Stackdriver MetricsBackend = "stackdriver"
// The metrics backend is prometheus
Prometheus MetricsBackend = "prometheus"
)
type metricsConfig struct {
// The metrics domain. e.g. "serving.knative.dev" or "build.knative.dev".
domain string
// The component that emits the metrics. e.g. "activator", "autoscaler".
component string
// The metrics backend destination.
backendDestination MetricsBackend
// The stackdriver project ID where the stats data are uploaded to. This is
// not the GCP project ID.
stackdriverProjectID string
}
func getMetricsConfig(m map[string]string, domain string, component string, logger *zap.SugaredLogger) (*metricsConfig, error) {
var mc metricsConfig
backend, ok := m[backendDestinationKey]
if !ok {
return nil, errors.New("metrics.backend-destination key is missing")
}
lb := MetricsBackend(strings.ToLower(backend))
switch lb {
case Stackdriver, Prometheus:
mc.backendDestination = lb
default:
return nil, fmt.Errorf("Unsupported metrics backend value \"%s\"", backend)
}
if mc.backendDestination == Stackdriver {
sdProj, ok := m[stackdriverProjectIDKey]
if !ok || sdProj == "" {
return nil, errors.New("For backend stackdriver, metrics.stackdriver-project-id field must exist and cannot be empty")
}
mc.stackdriverProjectID = sdProj
}
if domain == "" {
return nil, errors.New("Metrics domain cannot be empty")
}
mc.domain = domain
if component == "" {
return nil, errors.New("Metrics component name cannot be empty")
}
mc.component = component
return &mc, nil
}
// UpdateExporterFromConfigMap returns a helper func that can be used to update the exporter
// when a config map is updated
func UpdateExporterFromConfigMap(domain string, component string, logger *zap.SugaredLogger) func(configMap *corev1.ConfigMap) {
return func(configMap *corev1.ConfigMap) {
newConfig, err := getMetricsConfig(configMap.Data, domain, component, logger)
if err != nil {
ce := getCurMetricsExporter()
if ce == nil {
// Fail the process if there doesn't exist an exporter.
logger.Fatal("Failed to get a valid metrics config")
} else {
logger.Error("Failed to get a valid metrics config; Skip updating the metrics exporter", zap.Error(err))
return
}
}
if isMetricsConfigChanged(newConfig) {
if err := newMetricsExporter(newConfig, logger); err != nil {
logger.Error("Failed to update a new metrics exporter based on metric config.", zap.Error(err))
return
}
}
}
}
// isMetricsConfigChanged compares the non-nil newConfig against curMetricsConfig. When backend changes,
// or stackdriver project ID changes for stackdriver backend, we need to update the metrics exporter.
func isMetricsConfigChanged(newConfig *metricsConfig) bool {
cc := getCurMetricsConfig()
if cc == nil || newConfig.backendDestination != cc.backendDestination {
return true
} else if newConfig.backendDestination == Stackdriver && newConfig.stackdriverProjectID != cc.stackdriverProjectID {
return true
}
return false
}