diff --git a/docs/configuration/config-file-reference.md b/docs/configuration/config-file-reference.md index 9f7bd445aaf..8fb01dc4a71 100644 --- a/docs/configuration/config-file-reference.md +++ b/docs/configuration/config-file-reference.md @@ -2857,7 +2857,7 @@ The `limits_config` configures default and per-tenant limits imposed by Cortex s # List of metric relabel configurations. Note that in most situations, it is # more effective to use metrics relabeling directly in the Prometheus server, # e.g. remote_write.write_relabel_configs. -[metric_relabel_configs: | default = ] +[metric_relabel_configs: | default = []] # Enables support for exemplars in TSDB and sets the maximum number that will be # stored. less than zero means disabled. If the value is set to zero, cortex @@ -3099,7 +3099,7 @@ The `limits_config` configures default and per-tenant limits imposed by Cortex s [alertmanager_max_alerts_size_bytes: | default = 0] # list of rule groups to disable -[disabled_rule_groups: | default = ] +[disabled_rule_groups: | default = []] ``` ### `memberlist_config` @@ -3719,7 +3719,7 @@ The `ruler_config` configures the Cortex ruler. [external_url: | default = ] # Labels to add to all alerts. -[external_labels: | default = ] +[external_labels: | default = []] ruler_client: # gRPC client max receive message size (bytes). @@ -4932,3 +4932,21 @@ otel: # CLI flag: -tracing.otel.tls.tls-insecure-skip-verify [tls_insecure_skip_verify: | default = false] ``` + +### `DisabledRuleGroup` + +```yaml +# namespace in which the rule group belongs +[namespace: | default = ""] + +# name of the rule group +[name: | default = ""] +``` + +### `Label` + +```yaml +[name: | default = ""] + +[value: | default = ""] +``` diff --git a/pkg/util/validation/limits.go b/pkg/util/validation/limits.go index fe46c6b561a..d60b2ca8e65 100644 --- a/pkg/util/validation/limits.go +++ b/pkg/util/validation/limits.go @@ -39,9 +39,9 @@ func (e LimitError) Error() string { } type DisabledRuleGroup struct { - Namespace string `yaml:"namespace"` - Name string `yaml:"name"` - User string `yaml:"-"` + Namespace string `yaml:"namespace" doc:"nocli|description=namespace in which the rule group belongs"` + Name string `yaml:"name" doc:"nocli|description=name of the rule group"` + User string `yaml:"-" doc:"nocli"` } type DisabledRuleGroups []DisabledRuleGroup diff --git a/tools/doc-generator/main.go b/tools/doc-generator/main.go index 65ee0b8212a..1fc1cf4701c 100644 --- a/tools/doc-generator/main.go +++ b/tools/doc-generator/main.go @@ -46,6 +46,11 @@ const ( ) var ( + typesToIgnoreCLI = map[string]bool{ + "labels.Label": true, + "flagext.CIDR": true, + } + // Ordered list of root blocks. The order is the same order that will // follow the markdown generation. rootBlocks = []rootBlock{ diff --git a/tools/doc-generator/parser.go b/tools/doc-generator/parser.go index a2032593570..b1679f2e5ef 100644 --- a/tools/doc-generator/parser.go +++ b/tools/doc-generator/parser.go @@ -112,7 +112,7 @@ func parseConfig(block *configBlock, cfg interface{}, flags map[uintptr]*flag.Fl } // Skip field types which are non configurable - if field.Type.Kind() == reflect.Func { + if field.Type.Kind() == reflect.Func || field.Type.Kind() == reflect.Pointer { continue } @@ -123,7 +123,7 @@ func parseConfig(block *configBlock, cfg interface{}, flags map[uintptr]*flag.Fl } // Handle custom fields in vendored libs upon which we have no control. - fieldEntry, err := getCustomFieldEntry(field, fieldValue, flags) + fieldEntry, err := getCustomFieldEntry(t, field, fieldValue, flags) if err != nil { return nil, err } @@ -189,17 +189,45 @@ func parseConfig(block *configBlock, cfg interface{}, flags map[uintptr]*flag.Fl return nil, errors.Wrapf(err, "config=%s.%s", t.PkgPath(), t.Name()) } - fieldFlag, err := getFieldFlag(field, fieldValue, flags) + fieldDefault := "" + if field.Type.Kind() == reflect.Slice { + sliceElementType := field.Type.Elem() + if sliceElementType.Kind() == reflect.Struct { + rootBlocks = append(rootBlocks, rootBlock{ + name: field.Type.Elem().Name(), + structType: field.Type.Elem(), + }) + sliceElementBlock := &configBlock{ + name: field.Type.Elem().Name(), + desc: "", + } + sliceElementCfg := reflect.New(sliceElementType).Interface() + otherBlocks, err := parseConfig(sliceElementBlock, sliceElementCfg, flags) + + if err != nil { + return nil, err + } + if len(sliceElementBlock.entries) > 0 { + blocks = append(blocks, sliceElementBlock) + } + + blocks = append(blocks, otherBlocks...) + } + fieldDefault = "[]" + } + + fieldFlag, err := getFieldFlag(t, field, fieldValue, flags) if err != nil { return nil, errors.Wrapf(err, "config=%s.%s", t.PkgPath(), t.Name()) } if fieldFlag == nil { block.Add(&configEntry{ - kind: "field", - name: fieldName, - required: isFieldRequired(field), - fieldDesc: getFieldDescription(field, ""), - fieldType: fieldType, + kind: "field", + name: fieldName, + required: isFieldRequired(field), + fieldDesc: getFieldDescription(field, ""), + fieldType: fieldType, + fieldDefault: fieldDefault, }) continue } @@ -213,6 +241,7 @@ func parseConfig(block *configBlock, cfg interface{}, flags map[uintptr]*flag.Fl fieldType: fieldType, fieldDefault: fieldFlag.DefValue, }) + } return blocks, nil @@ -259,12 +288,12 @@ func getFieldType(t reflect.Type) (string, error) { return "relabel_config...", nil case "labels.Labels": return "map of string to string", nil - case "validation.DisabledRuleGroups": - return "list of rule groups to disable", nil } // Fallback to auto-detection of built-in data types switch t.Kind() { + case reflect.Struct: + return t.Name(), nil case reflect.Bool: return "boolean", nil @@ -314,8 +343,9 @@ func getFieldType(t reflect.Type) (string, error) { } } -func getFieldFlag(field reflect.StructField, fieldValue reflect.Value, flags map[uintptr]*flag.Flag) (*flag.Flag, error) { - if isAbsentInCLI(field) { +func getFieldFlag(parent reflect.Type, field reflect.StructField, fieldValue reflect.Value, flags map[uintptr]*flag.Flag) (*flag.Flag, error) { + + if isAbsentInCLI(field) || ignoreCLI(parent) { return nil, nil } fieldPtr := fieldValue.Addr().Pointer() @@ -327,9 +357,9 @@ func getFieldFlag(field reflect.StructField, fieldValue reflect.Value, flags map return fieldFlag, nil } -func getCustomFieldEntry(field reflect.StructField, fieldValue reflect.Value, flags map[uintptr]*flag.Flag) (*configEntry, error) { +func getCustomFieldEntry(parent reflect.Type, field reflect.StructField, fieldValue reflect.Value, flags map[uintptr]*flag.Flag) (*configEntry, error) { if field.Type == reflect.TypeOf(logging.Level{}) || field.Type == reflect.TypeOf(logging.Format{}) { - fieldFlag, err := getFieldFlag(field, fieldValue, flags) + fieldFlag, err := getFieldFlag(parent, field, fieldValue, flags) if err != nil { return nil, err } @@ -345,7 +375,7 @@ func getCustomFieldEntry(field reflect.StructField, fieldValue reflect.Value, fl }, nil } if field.Type == reflect.TypeOf(flagext.URLValue{}) { - fieldFlag, err := getFieldFlag(field, fieldValue, flags) + fieldFlag, err := getFieldFlag(parent, field, fieldValue, flags) if err != nil { return nil, err } @@ -361,7 +391,7 @@ func getCustomFieldEntry(field reflect.StructField, fieldValue reflect.Value, fl }, nil } if field.Type == reflect.TypeOf(flagext.Secret{}) { - fieldFlag, err := getFieldFlag(field, fieldValue, flags) + fieldFlag, err := getFieldFlag(parent, field, fieldValue, flags) if err != nil { return nil, err } @@ -377,7 +407,7 @@ func getCustomFieldEntry(field reflect.StructField, fieldValue reflect.Value, fl }, nil } if field.Type == reflect.TypeOf(model.Duration(0)) { - fieldFlag, err := getFieldFlag(field, fieldValue, flags) + fieldFlag, err := getFieldFlag(parent, field, fieldValue, flags) if err != nil { return nil, err } @@ -393,7 +423,7 @@ func getCustomFieldEntry(field reflect.StructField, fieldValue reflect.Value, fl }, nil } if field.Type == reflect.TypeOf(flagext.Time{}) { - fieldFlag, err := getFieldFlag(field, fieldValue, flags) + fieldFlag, err := getFieldFlag(parent, field, fieldValue, flags) if err != nil { return nil, err } @@ -420,6 +450,13 @@ func isAbsentInCLI(f reflect.StructField) bool { return getDocTagFlag(f, "nocli") } +func ignoreCLI(f reflect.Type) bool { + if ignore, OK := typesToIgnoreCLI[f.String()]; OK && ignore { + return true + } + return false +} + func isFieldRequired(f reflect.StructField) bool { return getDocTagFlag(f, "required") }