docs/CHANGELOG.md: support empty command-line flag values in short array notation

For example, -fooDuration=',10s,' is now supported - it sets three command-line flag values:

- the first and the last one are set to the default value for `-fooDuration`
- the second one is set to 10s
This commit is contained in:
Aliaksandr Valialkin 2024-02-07 20:53:01 +02:00
parent 05f0b707d1
commit e78e5ccfaa
No known key found for this signature in database
GPG key ID: 52C003EE2BCDB9EB
7 changed files with 36 additions and 2 deletions

View file

@ -31,6 +31,7 @@ The sandbox cluster installation is running under the constant load generated by
* SECURITY: upgrade Go builder from Go1.21.6 to Go1.21.7. See [the list of issues addressed in Go1.21.7](https://github.com/golang/go/issues?q=milestone%3AGo1.21.7+label%3ACherryPickApproved). * SECURITY: upgrade Go builder from Go1.21.6 to Go1.21.7. See [the list of issues addressed in Go1.21.7](https://github.com/golang/go/issues?q=milestone%3AGo1.21.7+label%3ACherryPickApproved).
* FEATURE: all VictoriaMetrics components: add support for TLS client certificate verification at `-httpListenAddr` (aka [mTLS](https://en.wikipedia.org/wiki/Mutual_authentication)). See [these docs](https://docs.victoriametrics.com/#mtls-protection). See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5458). * FEATURE: all VictoriaMetrics components: add support for TLS client certificate verification at `-httpListenAddr` (aka [mTLS](https://en.wikipedia.org/wiki/Mutual_authentication)). See [these docs](https://docs.victoriametrics.com/#mtls-protection). See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5458).
* FEATURE: all VictoriaMetrics components: add support for empty command flag values in short array notation. For example, `-remoteWrite.sendTimeout=',20s,'` specifies three `-remoteWrite.sendTimeout` values - the first and the last ones are default values (`30s` in this case), while the second one is `20s`.
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html) and [single-node VictoriaMetrics](https://docs.victoriametrics.com): add support for data ingestion via [DataDog lambda extension](https://docs.datadoghq.com/serverless/libraries_integrations/extension/) aka `/api/beta/sketches` endpoint. See [these docs](https://docs.victoriametrics.com/#how-to-send-data-from-datadog-agent) and [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3091). Thanks to @AndrewChubatiuk for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5584). * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html) and [single-node VictoriaMetrics](https://docs.victoriametrics.com): add support for data ingestion via [DataDog lambda extension](https://docs.datadoghq.com/serverless/libraries_integrations/extension/) aka `/api/beta/sketches` endpoint. See [these docs](https://docs.victoriametrics.com/#how-to-send-data-from-datadog-agent) and [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3091). Thanks to @AndrewChubatiuk for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5584).
* FEATURE: [VictoriaMetrics cluster](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html): add `-disableReroutingOnUnavailable` command-line flag, which can be used for reducing resource usage spikes at `vmstorage` nodes during rolling restart. Thanks to @Muxa1L for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5713). * FEATURE: [VictoriaMetrics cluster](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html): add `-disableReroutingOnUnavailable` command-line flag, which can be used for reducing resource usage spikes at `vmstorage` nodes during rolling restart. Thanks to @Muxa1L for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5713).
* FEATURE: [dashboards/vmagent](https://grafana.com/grafana/dashboards/12683): add `Targets scraped/s` stat panel showing the number of targets scraped by the vmagent per-second. * FEATURE: [dashboards/vmagent](https://grafana.com/grafana/dashboards/12683): add `Targets scraped/s` stat panel showing the number of targets scraped by the vmagent per-second.

View file

@ -21,6 +21,7 @@ func NewArrayString(name, description string) *ArrayString {
func NewArrayDuration(name string, defaultValue time.Duration, description string) *ArrayDuration { func NewArrayDuration(name string, defaultValue time.Duration, description string) *ArrayDuration {
description += fmt.Sprintf(" (default %s)", defaultValue) description += fmt.Sprintf(" (default %s)", defaultValue)
description += "\nSupports `array` of values separated by comma or specified via multiple flags." description += "\nSupports `array` of values separated by comma or specified via multiple flags."
description += "\nEmpty values are set to default value."
a := &ArrayDuration{ a := &ArrayDuration{
defaultValue: defaultValue, defaultValue: defaultValue,
} }
@ -31,6 +32,7 @@ func NewArrayDuration(name string, defaultValue time.Duration, description strin
// NewArrayBool returns new ArrayBool with the given name and description. // NewArrayBool returns new ArrayBool with the given name and description.
func NewArrayBool(name, description string) *ArrayBool { func NewArrayBool(name, description string) *ArrayBool {
description += "\nSupports `array` of values separated by comma or specified via multiple flags." description += "\nSupports `array` of values separated by comma or specified via multiple flags."
description += "\nEmpty values are set to false."
var a ArrayBool var a ArrayBool
flag.Var(&a, name, description) flag.Var(&a, name, description)
return &a return &a
@ -40,6 +42,7 @@ func NewArrayBool(name, description string) *ArrayBool {
func NewArrayInt(name string, defaultValue int, description string) *ArrayInt { func NewArrayInt(name string, defaultValue int, description string) *ArrayInt {
description += fmt.Sprintf(" (default %d)", defaultValue) description += fmt.Sprintf(" (default %d)", defaultValue)
description += "\nSupports `array` of values separated by comma or specified via multiple flags." description += "\nSupports `array` of values separated by comma or specified via multiple flags."
description += "\nEmpty values are set to default value."
a := &ArrayInt{ a := &ArrayInt{
defaultValue: defaultValue, defaultValue: defaultValue,
} }
@ -52,6 +55,7 @@ func NewArrayBytes(name string, defaultValue int64, description string) *ArrayBy
description += "\nSupports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB." description += "\nSupports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB."
description += fmt.Sprintf(" (default %d)", defaultValue) description += fmt.Sprintf(" (default %d)", defaultValue)
description += "\nSupports `array` of values separated by comma or specified via multiple flags." description += "\nSupports `array` of values separated by comma or specified via multiple flags."
description += "\nEmpty values are set to default value."
a := &ArrayBytes{ a := &ArrayBytes{
defaultValue: defaultValue, defaultValue: defaultValue,
} }
@ -241,6 +245,9 @@ func (a *ArrayBool) String() string {
func (a *ArrayBool) Set(value string) error { func (a *ArrayBool) Set(value string) error {
values := parseArrayValues(value) values := parseArrayValues(value)
for _, v := range values { for _, v := range values {
if v == "" {
v = "false"
}
b, err := strconv.ParseBool(v) b, err := strconv.ParseBool(v)
if err != nil { if err != nil {
return err return err
@ -284,6 +291,9 @@ func (a *ArrayDuration) String() string {
func (a *ArrayDuration) Set(value string) error { func (a *ArrayDuration) Set(value string) error {
values := parseArrayValues(value) values := parseArrayValues(value)
for _, v := range values { for _, v := range values {
if v == "" {
v = a.defaultValue.String()
}
b, err := time.ParseDuration(v) b, err := time.ParseDuration(v)
if err != nil { if err != nil {
return err return err
@ -332,6 +342,9 @@ func (a *ArrayInt) String() string {
func (a *ArrayInt) Set(value string) error { func (a *ArrayInt) Set(value string) error {
values := parseArrayValues(value) values := parseArrayValues(value)
for _, v := range values { for _, v := range values {
if v == "" {
v = fmt.Sprintf("%d", a.defaultValue)
}
n, err := strconv.Atoi(v) n, err := strconv.Atoi(v)
if err != nil { if err != nil {
return err return err
@ -376,6 +389,9 @@ func (a *ArrayBytes) Set(value string) error {
values := parseArrayValues(value) values := parseArrayValues(value)
for _, v := range values { for _, v := range values {
var b Bytes var b Bytes
if v == "" {
v = fmt.Sprintf("%d", a.defaultValue)
}
if err := b.Set(v); err != nil { if err := b.Set(v); err != nil {
return err return err
} }

View file

@ -165,6 +165,7 @@ func TestArrayDuration_Set(t *testing.T) {
f := func(s, expectedResult string) { f := func(s, expectedResult string) {
t.Helper() t.Helper()
var a ArrayDuration var a ArrayDuration
a.defaultValue = 42 * time.Second
if err := a.Set(s); err != nil { if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
@ -176,6 +177,7 @@ func TestArrayDuration_Set(t *testing.T) {
f("", "") f("", "")
f(`1m`, `1m0s`) f(`1m`, `1m0s`)
f(`5m,1s,1h`, `5m0s,1s,1h0m0s`) f(`5m,1s,1h`, `5m0s,1s,1h0m0s`)
f(`5m,,1h`, `5m0s,42s,1h0m0s`)
} }
func TestArrayDuration_GetOptionalArg(t *testing.T) { func TestArrayDuration_GetOptionalArg(t *testing.T) {
@ -238,6 +240,7 @@ func TestArrayBool_Set(t *testing.T) {
f("", "") f("", "")
f(`true`, `true`) f(`true`, `true`)
f(`false,True,False`, `false,true,false`) f(`false,True,False`, `false,true,false`)
f(`1,,False`, `true,false,false`)
} }
func TestArrayBool_GetOptionalArg(t *testing.T) { func TestArrayBool_GetOptionalArg(t *testing.T) {
@ -289,6 +292,7 @@ func TestArrayInt_Set(t *testing.T) {
f := func(s, expectedResult string, expectedValues []int) { f := func(s, expectedResult string, expectedValues []int) {
t.Helper() t.Helper()
var a ArrayInt var a ArrayInt
a.defaultValue = 42
if err := a.Set(s); err != nil { if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
@ -304,6 +308,7 @@ func TestArrayInt_Set(t *testing.T) {
f("", "", nil) f("", "", nil)
f(`1`, `1`, []int{1}) f(`1`, `1`, []int{1})
f(`-2,3,-64`, `-2,3,-64`, []int{-2, 3, -64}) f(`-2,3,-64`, `-2,3,-64`, []int{-2, 3, -64})
f(`,,-64,`, `42,42,-64,42`, []int{42, 42, -64, 42})
} }
func TestArrayInt_GetOptionalArg(t *testing.T) { func TestArrayInt_GetOptionalArg(t *testing.T) {
@ -357,6 +362,7 @@ func TestArrayBytes_Set(t *testing.T) {
f := func(s, expectedResult string) { f := func(s, expectedResult string) {
t.Helper() t.Helper()
var a ArrayBytes var a ArrayBytes
a.defaultValue = 42
if err := a.Set(s); err != nil { if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
} }
@ -368,6 +374,7 @@ func TestArrayBytes_Set(t *testing.T) {
f("", "") f("", "")
f(`1`, `1`) f(`1`, `1`)
f(`-2,3,10kb`, `-2,3,10KB`) f(`-2,3,10kb`, `-2,3,10KB`)
f(`,,10kb`, `42,42,10KB`)
} }
func TestArrayBytes_GetOptionalArg(t *testing.T) { func TestArrayBytes_GetOptionalArg(t *testing.T) {

View file

@ -47,6 +47,11 @@ func (b *Bytes) String() string {
// Set implements flag.Value interface // Set implements flag.Value interface
func (b *Bytes) Set(value string) error { func (b *Bytes) Set(value string) error {
if value == "" {
b.N = 0
b.valueString = ""
return nil
}
value = normalizeBytesString(value) value = normalizeBytesString(value)
switch { switch {
case strings.HasSuffix(value, "KB"): case strings.HasSuffix(value, "KB"):

View file

@ -12,7 +12,6 @@ func TestBytesSetFailure(t *testing.T) {
t.Fatalf("expecting non-nil error in b.Set(%q)", value) t.Fatalf("expecting non-nil error in b.Set(%q)", value)
} }
} }
f("")
f("foobar") f("foobar")
f("5foobar") f("5foobar")
f("aKB") f("aKB")
@ -39,6 +38,7 @@ func TestBytesSetSuccess(t *testing.T) {
t.Fatalf("unexpected valueString; got %q; want %q", valueString, valueExpected) t.Fatalf("unexpected valueString; got %q; want %q", valueString, valueExpected)
} }
} }
f("", 0)
f("0", 0) f("0", 0)
f("1", 1) f("1", 1)
f("-1234", -1234) f("-1234", -1234)

View file

@ -49,6 +49,11 @@ func (d *Duration) String() string {
// Set implements flag.Value interface // Set implements flag.Value interface
func (d *Duration) Set(value string) error { func (d *Duration) Set(value string) error {
if value == "" {
d.msecs = 0
d.valueString = ""
return nil
}
// An attempt to parse value in months. // An attempt to parse value in months.
months, err := strconv.ParseFloat(value, 64) months, err := strconv.ParseFloat(value, 64)
if err == nil { if err == nil {

View file

@ -14,7 +14,6 @@ func TestDurationSetFailure(t *testing.T) {
t.Fatalf("expecting non-nil error in d.Set(%q)", value) t.Fatalf("expecting non-nil error in d.Set(%q)", value)
} }
} }
f("")
f("foobar") f("foobar")
f("5foobar") f("5foobar")
f("ah") f("ah")
@ -51,6 +50,7 @@ func TestDurationSetSuccess(t *testing.T) {
t.Fatalf("unexpected valueString; got %q; want %q", valueString, valueExpected) t.Fatalf("unexpected valueString; got %q; want %q", valueString, valueExpected)
} }
} }
f("", 0)
f("0", 0) f("0", 0)
f("1", msecsPer31Days) f("1", msecsPer31Days)
f("123.456", 123.456*msecsPer31Days) f("123.456", 123.456*msecsPer31Days)