add config option check logging (#3722)

This commit is contained in:
Denis Mishin 2022-11-05 00:25:09 -04:00 committed by GitHub
parent 02df20f10a
commit 74a7daed4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 15 deletions

View file

@ -11,11 +11,11 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp"
"strings" "strings"
"time" "time"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"github.com/rs/zerolog"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/volatiletech/null/v9" "github.com/volatiletech/null/v9"
@ -374,7 +374,9 @@ func optionsFromViper(configFile string) (*Options, error) {
if err := v.Unmarshal(o, ViperPolicyHooks, func(c *mapstructure.DecoderConfig) { c.Metadata = &metadata }); err != nil { if err := v.Unmarshal(o, ViperPolicyHooks, func(c *mapstructure.DecoderConfig) { c.Metadata = &metadata }); err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %w", err) return nil, fmt.Errorf("failed to unmarshal config: %w", err)
} }
checkUnusedConfigFields(configFile, metadata.Unused) if err := checkConfigKeysErrors(configFile, metadata.Unused); err != nil {
return nil, err
}
// This is necessary because v.Unmarshal will overwrite .viper field. // This is necessary because v.Unmarshal will overwrite .viper field.
o.viper = v o.viper = v
@ -385,22 +387,28 @@ func optionsFromViper(configFile string) (*Options, error) {
return o, nil return o, nil
} }
var ( func checkConfigKeysErrors(configFile string, unused []string) error {
// policy's embedded protobuf structs are decoded by separate hook and are unknown to mapstructure checks := CheckUnknownConfigFields(unused)
routesEmbeddedFieldsRe = regexp.MustCompile(`(routes|policy)\[\.*`) ctx := context.Background()
) errInvalidConfigKeys := errors.New("some configuration options are no longer supported, please check logs for details")
var err error
func checkUnusedConfigFields(configFile string, unused []string) { for _, check := range checks {
keys := make([]string, 0, len(unused)) var evt *zerolog.Event
for _, k := range unused { switch check.KeyAction {
if !routesEmbeddedFieldsRe.MatchString(k) { case KeyActionError:
keys = append(keys, k) evt = log.Error(ctx)
err = errInvalidConfigKeys
default:
evt = log.Warn(ctx)
} }
evt.Str("config_file", configFile).Str("key", check.Key)
if check.DocsURL != "" {
evt = evt.Str("help", check.DocsURL)
} }
if len(keys) == 0 { evt.Msg(string(check.FieldCheckMsg))
return
} }
log.Warn(context.Background()).Str("config_file", configFile).Strs("keys", keys).Msg("config contained unknown keys that were ignored") return err
} }
// parsePolicy initializes policy to the options from either base64 environmental // parsePolicy initializes policy to the options from either base64 environmental

82
config/options_check.go Normal file
View file

@ -0,0 +1,82 @@
package config
import (
"regexp"
)
// KeyAction defines the Pomerium behavior when it encounters a deprecated config field
type KeyAction string
// FieldCheckMsg is a log message to print for a config option
type FieldCheckMsg string
const (
// KeyActionWarn would result in warning to log
KeyActionWarn = KeyAction("warn")
// KeyActionError would result in error in log and possibly program stop
KeyActionError = KeyAction("error")
// UnknownFieldAction default behavior when observing an unknown field is to warn
UnknownFieldAction = KeyActionWarn
// FieldCheckMsgRemoved log message when field was removed
FieldCheckMsgRemoved = FieldCheckMsg("config option was removed")
// FieldCheckMsgUnknown log message for unrecognized / unhandled config option
FieldCheckMsgUnknown = FieldCheckMsg("unknown config option")
)
var reKeyPath = regexp.MustCompile(`\[\d+\]`)
var (
// options that were deprecated in the config
removedConfigFields = map[string]string{
"idp_service_account": "https://docs.pomerium.com/docs/overview/upgrading#idp-directory-sync",
"idp_refresh_directory_timeout": "https://docs.pomerium.com/docs/overview/upgrading#idp-directory-sync",
"idp_refresh_directory_interval": "https://docs.pomerium.com/docs/overview/upgrading#idp-directory-sync",
"idp_qps": "https://docs.pomerium.com/docs/overview/upgrading#idp-directory-sync",
"routes.allowed_groups": "https://docs.pomerium.com/docs/overview/upgrading#idp-groups-policy",
}
// mapstructure has issues with embedded protobuf structs that we should ignore
ignoreConfigFields = map[string]struct{}{
"routes.outlier_detection": {},
"routes.health_checks": {},
}
)
// FieldMsg returns information
type FieldMsg struct {
Key string
DocsURL string
FieldCheckMsg
KeyAction
}
// CheckUnknownConfigFields returns list of messages to be emitted about unrecognized fields
func CheckUnknownConfigFields(fields []string) []FieldMsg {
out := make([]FieldMsg, 0, len(fields))
for _, key := range fields {
path := reKeyPath.ReplaceAllString(key, "")
if docsURL, ok := removedConfigFields[path]; ok {
out = append(out, FieldMsg{
Key: path,
DocsURL: docsURL,
KeyAction: KeyActionError,
FieldCheckMsg: FieldCheckMsgRemoved,
})
continue
}
if _, ok := ignoreConfigFields[path]; ok {
continue
}
out = append(out, FieldMsg{
Key: path,
KeyAction: KeyActionWarn,
FieldCheckMsg: FieldCheckMsgUnknown,
})
}
return out
}