Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Validate Collection memory config through adding conflictsWith validation #806

Merged
merged 5 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions config.md
Original file line number Diff line number Diff line change
Expand Up @@ -742,8 +742,7 @@ The number of traces in the cache should be many multiples (100x to 1000x) of th
AvailableMemory is the amount of system memory available to the Refinery process.

This value will typically be set through an environment variable controlled by the container or deploy script.
If this value is zero or not set, then `MaxMemory` cannot be used to calculate the maximum allocation and `MaxAlloc` will be used instead.
If set, then this must be a memory size.
If set, then this must be a memory size and `Collections.maxAlloc` must not be defined to avoid unclear configuration.
64-bit values are supported.
Sizes with standard unit suffixes (`MB`, `GiB`, etc) and Kubernetes units (`M`, `Gi`, etc) are also supported.

Expand All @@ -761,7 +760,6 @@ If nonzero, then it must be an integer value between 1 and 100, representing the
If set to a non-zero value, then once per tick (see `SendTicker`) the collector will compare total allocated bytes to this calculated value.
If allocation is too high, then traces will be ejected from the cache early to reduce memory.
Useful values for this setting are generally in the range of 70-90.
If this value is `0`, then `MaxAlloc` will be used.

- Eligible for live reload.
- Type: `percentage`
Expand All @@ -772,7 +770,7 @@ If this value is `0`, then `MaxAlloc` will be used.

MaxAlloc is the maximum number of bytes that should be allocated by the Collector.

If set, then this must be a memory size.
If set, then this must be a memory size and `Collections.AvailableMemory` must not be defined to avoid unclear configuration.
64-bit values are supported.
Sizes with standard unit suffixes (`MB`, `GiB`, etc) and Kubernetes units (`M`, `Gi`, etc) are also supported.
See `MaxMemory` for more details.
Expand Down
11 changes: 7 additions & 4 deletions config/metadata/configMeta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,7 @@ groups:
accepted. Events arriving with API keys not in the `ReceiveKeys` list
will be rejected with an HTTP `401` error.

If `false`, then all traffic is accepted and `ReceiveKeys` is
ignored. Must be specified if `ReceiveKeys` is specified.
If `false`, then all traffic is accepted and `ReceiveKeys` is ignored.

- name: RefineryTelemetry
title: "Refinery Telemetry"
Expand Down Expand Up @@ -935,7 +934,8 @@ groups:
allocation and `MaxAlloc` will be used instead. If set, then this must
be a memory size. 64-bit values are supported. Sizes with standard
unit suffixes (`MB`, `GiB`, etc.) and Kubernetes units (`M`, `Gi`,
etc.) are also supported.
etc.) are also supported. If set, `Collections.MaxAlloc` must not be
defined.

- name: MaxMemoryPercentage
type: percentage
Expand All @@ -957,7 +957,6 @@ groups:
allocated bytes to this calculated value. If allocation is too high,
then traces will be ejected from the cache early to reduce memory.
Useful values for this setting are generally in the range of 70-90.
If this value is `0`, then `MaxAlloc` will be used.

- name: MaxAlloc
v1group: InMemCollector
Expand All @@ -966,11 +965,15 @@ groups:
valuetype: memorysize
default: 0
reload: true
validatons:
- type: conflictsWith
arg: AvailableMemory
summary: is the maximum number of bytes that should be allocated by the Collector.
description: >
If set, then this must be a memory size. 64-bit values are supported.
Sizes with standard unit suffixes (`MB`, `GiB`, etc) and Kubernetes units
(`M`, `Gi`, etc) are also supported. See `MaxMemory` for more details.
If set, `Collections.AvailableMemory` must not be defined.

- name: BufferSizes
title: "Buffer Sizes"
Expand Down
13 changes: 11 additions & 2 deletions config/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ func (m *Metadata) Validate(data map[string]any) []string {
}
}
}
case "required", "requiredInGroup", "requiredWith":
case "required", "requiredInGroup", "requiredWith", "conflictsWith":
// these are handled below
default:
panic("unknown validation type: " + validation.Type)
Expand Down Expand Up @@ -368,10 +368,19 @@ func (m *Metadata) Validate(data map[string]any) []string {
// if the named key is specified then this one must be also
otherName := validation.Arg.(string)
if _, ok := flatdata[group.Name+"."+otherName]; ok {
if _, ok := flatdata[group.Name+"."+field.Name]; !ok {
if _, ok := flatdata[group.Name+"."+field.Name]; !ok && m.GetField(group.Name+"."+field.Name).Default == nil {
errors = append(errors, fmt.Sprintf("the group %s includes %s, which also requires %s", group.Name, otherName, field.Name))
}
}
case "conflictsWith":
// if both values are defined then report an error. Default values can also lead to conflicts and should not be
// used with this validation.
otherName := validation.Arg.(string)
if _, ok := flatdata[group.Name+"."+otherName]; ok {
if _, ok := flatdata[group.Name+"."+field.Name]; ok {
errors = append(errors, fmt.Sprintf("the group %s includes %s, which conflicts with %s", group.Name, otherName, field.Name))
}
}
}
}
}
Expand Down
19 changes: 18 additions & 1 deletion config/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,22 @@ groups:
validations:
- type: requiredWith
arg: FieldB
- name: FieldE
type: int
validations:
- type: requiredWith
arg: FieldA
default: 100

- name: ConflictTest
fields:
- name: FieldA
type: int
- name: FieldB
type: int
validations:
- type: conflictsWith
arg: FieldA

- name: Traces
fields:
Expand Down Expand Up @@ -276,8 +291,10 @@ func Test_validate(t *testing.T) {
{"good require A", mm("RequireTest.FieldA", 1, "RequireTest.FieldC", 3), ""},
{"good require B", mm("RequireTest.FieldB", 2, "RequireTest.FieldC", 3, "RequireTest.FieldD", 4), ""},
{"good require C", mm("RequireTest.FieldC", 3), ""},
{"good require E with default", mm("RequireTest.FieldA", 2, "RequireTest.FieldC", 3), ""},
{"bad require", mm("RequireTest.FieldA", 1), "the group RequireTest is missing its required field FieldC"},
{"bad require", mm("RequireTest.FieldA", 1, "RequireTest.FieldB", 2), "the group RequireTest includes FieldB, which also requires FieldD"},
{"bad conflicts with A", mm("ConflictTest.FieldA", 2, "ConflictTest.FieldB", 3), "the group ConflictTest includes FieldA, which conflicts with FieldB"},
{"good conflicts with A", mm("ConflictTest.FieldA", 2), ""},
{"good slice elementType", mm("Traces.AStringArray", []any{"0.0.0.0:8080", "192.168.1.1:8080"}), ""},
{"bad slice elementType", mm("Traces.AStringArray", []any{"0.0.0.0"}), "field Traces.AStringArray[0] (0.0.0.0) must be a hostport: address 0.0.0.0: missing port in address"},
{"good map elementType", mm("Traces.AStringMap", map[string]any{"k": "v"}), ""},
Expand Down