confgenerator/experimental.go (51 lines of code) (raw):
// Copyright 2023 Google LLC
//
// 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 confgenerator
import (
"context"
"fmt"
"os"
"strings"
"github.com/go-playground/validator/v10"
)
// requiredFeatureForType maps a component type to a feature that must
// be enabled (via EXPERIMENTAL_FEATURES) in order to use that component
// in an Ops Agent configuration.
// For example, the following would require the user to define the
// "otlp_receiver" feature flag inside EXPERIMENTAL_FEATURES in order to
// be able to use the "otlp" combined receiver:
//
// "otlp": "otlp_receiver"
//
// N.B. There are no enforced feature flags today, so this map is
// intentionally left empty.
var requiredFeatureForType = map[string]string{}
var enabledExperimentalFeatures map[string]bool
func ParseExperimentalFeatures(features string) map[string]bool {
out := map[string]bool{}
for _, f := range strings.Split(features, ",") {
out[strings.TrimSpace(f)] = true
}
return out
}
func init() {
enabledExperimentalFeatures = ParseExperimentalFeatures(os.Getenv("EXPERIMENTAL_FEATURES"))
}
type experimentsKeyType struct{}
var experimentsKey = experimentsKeyType{}
func ContextWithExperiments(ctx context.Context, experiments map[string]bool) context.Context {
return context.WithValue(ctx, experimentsKey, experiments)
}
func experimentsFromContext(ctx context.Context) map[string]bool {
if features := ctx.Value(experimentsKey); features != nil {
return features.(map[string]bool)
}
return enabledExperimentalFeatures
}
func registerExperimentalValidations(v *validator.Validate) {
v.RegisterValidationCtx("experimental", func(ctx context.Context, fl validator.FieldLevel) bool {
return fl.Field().IsZero() || experimentsFromContext(ctx)[fl.Param()]
})
v.RegisterStructValidationCtx(componentValidator, ConfigComponent{})
}
func componentValidator(ctx context.Context, sl validator.StructLevel) {
comp, ok := sl.Current().Interface().(ConfigComponent)
if !ok {
return
}
feature, ok := requiredFeatureForType[comp.Type]
if !ok || experimentsFromContext(ctx)[feature] {
return
}
sl.ReportError(comp, "type", "Type", "experimental", comp.Type)
}
func experimentalValidationErrorString(ve validationError) string {
return fmt.Sprintf("Experimental feature %q cannot be used with the current version of the Ops Agent", ve.Param())
}