diff --git a/app/vmagent/remotewrite/client.go b/app/vmagent/remotewrite/client.go index 5b2826f51..05162e22d 100644 --- a/app/vmagent/remotewrite/client.go +++ b/app/vmagent/remotewrite/client.go @@ -26,12 +26,11 @@ var ( forceVMProto = flagutil.NewArrayBool("remoteWrite.forceVMProto", "Whether to force VictoriaMetrics remote write protocol for sending data "+ "to the corresponding -remoteWrite.url . See https://docs.victoriametrics.com/vmagent.html#victoriametrics-remote-write-protocol") - rateLimit = flagutil.NewArrayInt("remoteWrite.rateLimit", "Optional rate limit in bytes per second for data sent to the corresponding -remoteWrite.url. "+ + rateLimit = flagutil.NewArrayInt("remoteWrite.rateLimit", 0, "Optional rate limit in bytes per second for data sent to the corresponding -remoteWrite.url. "+ "By default, the rate limit is disabled. It can be useful for limiting load on remote storage when big amounts of buffered data "+ "is sent after temporary unavailability of the remote storage") - sendTimeout = flagutil.NewArrayDuration("remoteWrite.sendTimeout", "Timeout for sending a single block of data to the corresponding -remoteWrite.url (default "+ - defaultSendTimeout.String()+")") - proxyURL = flagutil.NewArrayString("remoteWrite.proxyURL", "Optional proxy URL for writing data to the corresponding -remoteWrite.url. "+ + sendTimeout = flagutil.NewArrayDuration("remoteWrite.sendTimeout", time.Minute, "Timeout for sending a single block of data to the corresponding -remoteWrite.url") + proxyURL = flagutil.NewArrayString("remoteWrite.proxyURL", "Optional proxy URL for writing data to the corresponding -remoteWrite.url. "+ "Supported proxies: http, https, socks5. Example: -remoteWrite.proxyURL=socks5://proxy:1234") tlsInsecureSkipVerify = flagutil.NewArrayBool("remoteWrite.tlsInsecureSkipVerify", "Whether to skip tls verification when connecting to the corresponding -remoteWrite.url") @@ -73,8 +72,6 @@ var ( awsSecretKey = flagutil.NewArrayString("remoteWrite.aws.secretKey", "Optional AWS SecretKey to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") ) -const defaultSendTimeout = time.Minute - type client struct { sanitizedURL string remoteWriteURL string @@ -137,7 +134,7 @@ func newHTTPClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persiste } hc := &http.Client{ Transport: tr, - Timeout: sendTimeout.GetOptionalArgOrDefault(argIdx, defaultSendTimeout), + Timeout: sendTimeout.GetOptionalArg(argIdx), } c := &client{ sanitizedURL: sanitizedURL, @@ -172,7 +169,7 @@ func newHTTPClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persiste } func (c *client) init(argIdx, concurrency int, sanitizedURL string) { - if bytesPerSec := rateLimit.GetOptionalArgOrDefault(argIdx, 0); bytesPerSec > 0 { + if bytesPerSec := rateLimit.GetOptionalArg(argIdx); bytesPerSec > 0 { logger.Infof("applying %d bytes per second rate limit for -remoteWrite.url=%q", bytesPerSec, sanitizedURL) c.rl.perSecondLimit = int64(bytesPerSec) } @@ -181,7 +178,7 @@ func (c *client) init(argIdx, concurrency int, sanitizedURL string) { c.bytesSent = metrics.GetOrCreateCounter(fmt.Sprintf(`vmagent_remotewrite_bytes_sent_total{url=%q}`, c.sanitizedURL)) c.blocksSent = metrics.GetOrCreateCounter(fmt.Sprintf(`vmagent_remotewrite_blocks_sent_total{url=%q}`, c.sanitizedURL)) c.rateLimit = metrics.GetOrCreateGauge(fmt.Sprintf(`vmagent_remotewrite_rate_limit{url=%q}`, c.sanitizedURL), func() float64 { - return float64(rateLimit.GetOptionalArgOrDefault(argIdx, 0)) + return float64(rateLimit.GetOptionalArg(argIdx)) }) c.requestDuration = metrics.GetOrCreateHistogram(fmt.Sprintf(`vmagent_remotewrite_duration_seconds{url=%q}`, c.sanitizedURL)) c.requestsOKCount = metrics.GetOrCreateCounter(fmt.Sprintf(`vmagent_remotewrite_requests_total{url=%q, status_code="2XX"}`, c.sanitizedURL)) diff --git a/app/vmagent/remotewrite/remotewrite.go b/app/vmagent/remotewrite/remotewrite.go index 616a173cc..29099e114 100644 --- a/app/vmagent/remotewrite/remotewrite.go +++ b/app/vmagent/remotewrite/remotewrite.go @@ -48,14 +48,15 @@ var ( "isn't enough for sending high volume of collected data to remote storage. Default value is 2 * numberOfAvailableCPUs") showRemoteWriteURL = flag.Bool("remoteWrite.showURL", false, "Whether to show -remoteWrite.url in the exported metrics. "+ "It is hidden by default, since it can contain sensitive info such as auth key") - maxPendingBytesPerURL = flagutil.NewArrayBytes("remoteWrite.maxDiskUsagePerURL", "The maximum file-based buffer size in bytes at -remoteWrite.tmpDataPath "+ + maxPendingBytesPerURL = flagutil.NewArrayBytes("remoteWrite.maxDiskUsagePerURL", 0, "The maximum file-based buffer size in bytes at -remoteWrite.tmpDataPath "+ "for each -remoteWrite.url. When buffer size reaches the configured maximum, then old data is dropped when adding new data to the buffer. "+ "Buffered data is stored in ~500MB chunks. It is recommended to set the value for this flag to a multiple of the block size 500MB. "+ "Disk usage is unlimited if the value is set to 0") - significantFigures = flagutil.NewArrayInt("remoteWrite.significantFigures", "The number of significant figures to leave in metric values before writing them "+ + significantFigures = flagutil.NewArrayInt("remoteWrite.significantFigures", 0, "The number of significant figures to leave in metric values before writing them "+ "to remote storage. See https://en.wikipedia.org/wiki/Significant_figures . Zero value saves all the significant figures. "+ "This option may be used for improving data compression for the stored metrics. See also -remoteWrite.roundDigits") - roundDigits = flagutil.NewArrayInt("remoteWrite.roundDigits", "Round metric values to this number of decimal digits after the point before writing them to remote storage. "+ + roundDigits = flagutil.NewArrayInt("remoteWrite.roundDigits", 100, "Round metric values to this number of decimal digits after the point before "+ + "writing them to remote storage. "+ "Examples: -remoteWrite.roundDigits=2 would round 1.236 to 1.24, while -remoteWrite.roundDigits=-1 would round 126.78 to 130. "+ "By default, digits rounding is disabled. Set it to 100 for disabling it for a particular remote storage. "+ "This option may be used for improving data compression for the stored metrics") @@ -77,7 +78,7 @@ var ( streamAggrDropInput = flagutil.NewArrayBool("remoteWrite.streamAggr.dropInput", "Whether to drop all the input samples after the aggregation "+ "with -remoteWrite.streamAggr.config. By default, only aggregates samples are dropped, while the remaining samples "+ "are written to the corresponding -remoteWrite.url . See also -remoteWrite.streamAggr.keepInput and https://docs.victoriametrics.com/stream-aggregation.html") - streamAggrDedupInterval = flagutil.NewArrayDuration("remoteWrite.streamAggr.dedupInterval", "Input samples are de-duplicated with this interval before being aggregated. "+ + streamAggrDedupInterval = flagutil.NewArrayDuration("remoteWrite.streamAggr.dedupInterval", 0, "Input samples are de-duplicated with this interval before being aggregated. "+ "Only the last sample per each time series per each interval is aggregated if the interval is greater than zero") ) @@ -565,7 +566,7 @@ func newRemoteWriteCtx(argIdx int, at *auth.Token, remoteWriteURL *url.URL, maxI pqURL.Fragment = "" h := xxhash.Sum64([]byte(pqURL.String())) queuePath := filepath.Join(*tmpDataPath, persistentQueueDirname, fmt.Sprintf("%d_%016X", argIdx+1, h)) - maxPendingBytes := maxPendingBytesPerURL.GetOptionalArgOrDefault(argIdx, 0) + maxPendingBytes := maxPendingBytesPerURL.GetOptionalArg(argIdx) if maxPendingBytes != 0 && maxPendingBytes < persistentqueue.DefaultChunkFileSize { // See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4195 logger.Warnf("rounding the -remoteWrite.maxDiskUsagePerURL=%d to the minimum supported value: %d", maxPendingBytes, persistentqueue.DefaultChunkFileSize) @@ -589,8 +590,8 @@ func newRemoteWriteCtx(argIdx int, at *auth.Token, remoteWriteURL *url.URL, maxI c.init(argIdx, *queues, sanitizedURL) // Initialize pss - sf := significantFigures.GetOptionalArgOrDefault(argIdx, 0) - rd := roundDigits.GetOptionalArgOrDefault(argIdx, 100) + sf := significantFigures.GetOptionalArg(argIdx) + rd := roundDigits.GetOptionalArg(argIdx) pssLen := *queues if n := cgroup.AvailableCPUs(); pssLen > n { // There is no sense in running more than availableCPUs concurrent pendingSeries, @@ -615,7 +616,7 @@ func newRemoteWriteCtx(argIdx int, at *auth.Token, remoteWriteURL *url.URL, maxI // Initialize sas sasFile := streamAggrConfig.GetOptionalArg(argIdx) if sasFile != "" { - dedupInterval := streamAggrDedupInterval.GetOptionalArgOrDefault(argIdx, 0) + dedupInterval := streamAggrDedupInterval.GetOptionalArg(argIdx) sas, err := streamaggr.LoadFromFile(sasFile, rwctx.pushInternal, dedupInterval) if err != nil { logger.Fatalf("cannot initialize stream aggregators from -remoteWrite.streamAggr.config=%q: %s", sasFile, err) @@ -733,7 +734,7 @@ func (rwctx *remoteWriteCtx) reinitStreamAggr() { sasFile := streamAggrConfig.GetOptionalArg(rwctx.idx) logger.Infof("reloading stream aggregation configs pointed by -remoteWrite.streamAggr.config=%q", sasFile) metrics.GetOrCreateCounter(fmt.Sprintf(`vmagent_streamaggr_config_reloads_total{path=%q}`, sasFile)).Inc() - dedupInterval := streamAggrDedupInterval.GetOptionalArgOrDefault(rwctx.idx, 0) + dedupInterval := streamAggrDedupInterval.GetOptionalArg(rwctx.idx) sasNew, err := streamaggr.LoadFromFile(sasFile, rwctx.pushInternal, dedupInterval) if err != nil { metrics.GetOrCreateCounter(fmt.Sprintf(`vmagent_streamaggr_config_reloads_errors_total{path=%q}`, sasFile)).Inc() @@ -775,7 +776,7 @@ func CheckStreamAggrConfigs() error { if sasFile == "" { continue } - dedupInterval := streamAggrDedupInterval.GetOptionalArgOrDefault(idx, 0) + dedupInterval := streamAggrDedupInterval.GetOptionalArg(idx) sas, err := streamaggr.LoadFromFile(sasFile, pushNoop, dedupInterval) if err != nil { return fmt.Errorf("cannot load -remoteWrite.streamAggr.config=%q: %w", sasFile, err) diff --git a/lib/flagutil/array.go b/lib/flagutil/array.go index 9db5b6db8..d90bfa680 100644 --- a/lib/flagutil/array.go +++ b/lib/flagutil/array.go @@ -16,12 +16,15 @@ func NewArrayString(name, description string) *ArrayString { return &a } -// NewArrayDuration returns new ArrayDuration with the given name and description. -func NewArrayDuration(name, description string) *ArrayDuration { +// NewArrayDuration returns new ArrayDuration with the given name, defaultValue and description. +func NewArrayDuration(name string, defaultValue time.Duration, description string) *ArrayDuration { + description += fmt.Sprintf(" (default %s)", defaultValue) description += "\nSupports `array` of values separated by comma or specified via multiple flags." - var a ArrayDuration - flag.Var(&a, name, description) - return &a + a := &ArrayDuration{ + defaultValue: defaultValue, + } + flag.Var(a, name, description) + return a } // NewArrayBool returns new ArrayBool with the given name and description. @@ -32,21 +35,27 @@ func NewArrayBool(name, description string) *ArrayBool { return &a } -// NewArrayInt returns new ArrayInt with the given name and description. -func NewArrayInt(name, description string) *ArrayInt { +// NewArrayInt returns new ArrayInt with the given name, defaultValue and description. +func NewArrayInt(name string, defaultValue int, description string) *ArrayInt { + description += fmt.Sprintf(" (default %d)", defaultValue) description += "\nSupports `array` of values separated by comma or specified via multiple flags." - var a ArrayInt - flag.Var(&a, name, description) - return &a + a := &ArrayInt{ + defaultValue: defaultValue, + } + flag.Var(a, name, description) + return a } -// NewArrayBytes returns new ArrayBytes with the given name and description. -func NewArrayBytes(name, description string) *ArrayBytes { +// NewArrayBytes returns new ArrayBytes with the given name, defaultValue and description. +func NewArrayBytes(name string, defaultValue int64, description string) *ArrayBytes { description += "\nSupports the following optional suffixes for size values: KB, MB, GB, TB, KiB, MiB, GiB, TiB." + description += fmt.Sprintf(" (default %d)", defaultValue) description += "\nSupports `array` of values separated by comma or specified via multiple flags." - var a ArrayBytes - flag.Var(&a, name, description) - return &a + a := &ArrayBytes{ + defaultValue: defaultValue, + } + flag.Var(a, name, description) + return a } // ArrayString is a flag that holds an array of strings. @@ -220,11 +229,11 @@ func (a *ArrayBool) IsBoolFlag() bool { return true } // String implements flag.Value interface func (a *ArrayBool) String() string { - formattedBools := make([]string, len(*a)) + formattedResults := make([]string, len(*a)) for i, v := range *a { - formattedBools[i] = strconv.FormatBool(v) + formattedResults[i] = strconv.FormatBool(v) } - return strings.Join(formattedBools, ",") + return strings.Join(formattedResults, ",") } // Set implements flag.Value interface @@ -255,15 +264,19 @@ func (a *ArrayBool) GetOptionalArg(argIdx int) bool { // ArrayDuration is a flag that holds an array of time.Duration values. // // Has the same api as ArrayString. -type ArrayDuration []time.Duration +type ArrayDuration struct { + defaultValue time.Duration + a []time.Duration +} // String implements flag.Value interface func (a *ArrayDuration) String() string { - formattedBools := make([]string, len(*a)) - for i, v := range *a { - formattedBools[i] = v.String() + x := a.a + formattedResults := make([]string, len(x)) + for i, v := range x { + formattedResults[i] = v.String() } - return strings.Join(formattedBools, ",") + return strings.Join(formattedResults, ",") } // Set implements flag.Value interface @@ -274,20 +287,19 @@ func (a *ArrayDuration) Set(value string) error { if err != nil { return err } - *a = append(*a, b) + a.a = append(a.a, b) } return nil } -// GetOptionalArgOrDefault returns optional arg under the given argIdx, -// or default value, if argIdx not found. -func (a *ArrayDuration) GetOptionalArgOrDefault(argIdx int, defaultValue time.Duration) time.Duration { - x := *a +// GetOptionalArg returns optional arg under the given argIdx, or default value, if argIdx not found. +func (a *ArrayDuration) GetOptionalArg(argIdx int) time.Duration { + x := a.a if argIdx >= len(x) { if len(x) == 1 { return x[0] } - return defaultValue + return a.defaultValue } return x[argIdx] } @@ -295,11 +307,14 @@ func (a *ArrayDuration) GetOptionalArgOrDefault(argIdx int, defaultValue time.Du // ArrayInt is flag that holds an array of ints. // // Has the same api as ArrayString. -type ArrayInt []int +type ArrayInt struct { + defaultValue int + a []int +} // String implements flag.Value interface func (a *ArrayInt) String() string { - x := *a + x := a.a formattedInts := make([]string, len(x)) for i, v := range x { formattedInts[i] = strconv.Itoa(v) @@ -315,31 +330,34 @@ func (a *ArrayInt) Set(value string) error { if err != nil { return err } - *a = append(*a, n) + a.a = append(a.a, n) } return nil } -// GetOptionalArgOrDefault returns optional arg under the given argIdx. -func (a *ArrayInt) GetOptionalArgOrDefault(argIdx, defaultValue int) int { - x := *a +// GetOptionalArg returns optional arg under the given argIdx or default value. +func (a *ArrayInt) GetOptionalArg(argIdx int) int { + x := a.a if argIdx < len(x) { return x[argIdx] } if len(x) == 1 { return x[0] } - return defaultValue + return a.defaultValue } // ArrayBytes is flag that holds an array of Bytes. // // Has the same api as ArrayString. -type ArrayBytes []*Bytes +type ArrayBytes struct { + defaultValue int64 + a []*Bytes +} // String implements flag.Value interface func (a *ArrayBytes) String() string { - x := *a + x := a.a formattedBytes := make([]string, len(x)) for i, v := range x { formattedBytes[i] = v.String() @@ -355,19 +373,19 @@ func (a *ArrayBytes) Set(value string) error { if err := b.Set(v); err != nil { return err } - *a = append(*a, &b) + a.a = append(a.a, &b) } return nil } -// GetOptionalArgOrDefault returns optional arg under the given argIdx. -func (a *ArrayBytes) GetOptionalArgOrDefault(argIdx int, defaultValue int64) int64 { - x := *a +// GetOptionalArg returns optional arg under the given argIdx, or default value +func (a *ArrayBytes) GetOptionalArg(argIdx int) int64 { + x := a.a if argIdx < len(x) { return x[argIdx].N } if len(x) == 1 { return x[0].N } - return defaultValue + return a.defaultValue } diff --git a/lib/flagutil/array_test.go b/lib/flagutil/array_test.go index f64321120..848fe1b54 100644 --- a/lib/flagutil/array_test.go +++ b/lib/flagutil/array_test.go @@ -45,69 +45,74 @@ func TestArrayString(t *testing.T) { } func TestArrayString_Set(t *testing.T) { - f := func(s string, expectedValues []string) { + f := func(s, expectedResult string) { t.Helper() var a ArrayString - _ = a.Set(s) - if !reflect.DeepEqual([]string(a), expectedValues) { - t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", a, expectedValues) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } + result := a.String() + if result != expectedResult { + t.Fatalf("unexpected values parsed;\ngot\n%s\nwant\n%s", result, expectedResult) } } // Zero args - f("", nil) + f("", "") // Single arg - f(`foo`, []string{`foo`}) - f(`fo"o`, []string{`fo"o`}) - f(`fo'o`, []string{`fo'o`}) - f(`fo{o`, []string{`fo{o`}) - f(`fo[o`, []string{`fo[o`}) - f(`fo(o`, []string{`fo(o`}) + f(`foo`, `foo`) + f(`fo"o`, `"fo\"o"`) + f(`fo'o`, `"fo'o"`) + f(`fo{o`, `"fo{o"`) + f(`fo[o`, `"fo[o"`) + f(`fo(o`, `"fo(o"`) // Single arg with Prometheus label filters - f(`foo{bar="baz",x="y"}`, []string{`foo{bar="baz",x="y"}`}) - f(`foo{bar="ba}z",x="y"}`, []string{`foo{bar="ba}z",x="y"}`}) - f(`foo{bar='baz',x="y"}`, []string{`foo{bar='baz',x="y"}`}) - f(`foo{bar='baz',x='y'}`, []string{`foo{bar='baz',x='y'}`}) - f(`foo{bar='ba}z',x='y'}`, []string{`foo{bar='ba}z',x='y'}`}) - f(`{foo="ba[r",baz='a'}`, []string{`{foo="ba[r",baz='a'}`}) + f(`foo{bar="baz",x="y"}`, `"foo{bar=\"baz\",x=\"y\"}"`) + f(`foo{bar="ba}z",x="y"}`, `"foo{bar=\"ba}z\",x=\"y\"}"`) + f(`foo{bar='baz',x="y"}`, `"foo{bar='baz',x=\"y\"}"`) + f(`foo{bar='baz',x='y'}`, `"foo{bar='baz',x='y'}"`) + f(`foo{bar='ba}z',x='y'}`, `"foo{bar='ba}z',x='y'}"`) + f(`{foo="ba[r",baz='a'}`, `"{foo=\"ba[r\",baz='a'}"`) // Single arg with JSON - f(`[1,2,3]`, []string{`[1,2,3]`}) - f(`{"foo":"ba,r",baz:x}`, []string{`{"foo":"ba,r",baz:x}`}) + f(`[1,2,3]`, `"[1,2,3]"`) + f(`{"foo":"ba,r",baz:x}`, `"{\"foo\":\"ba,r\",baz:x}"`) // Single quoted arg - f(`"foo"`, []string{`foo`}) - f(`"fo,'o"`, []string{`fo,'o`}) - f(`"f\\o,\'\"o"`, []string{`f\o,\'"o`}) - f(`"foo{bar='baz',x='y'}"`, []string{`foo{bar='baz',x='y'}`}) - f(`'foo'`, []string{`foo`}) - f(`'fo,"o'`, []string{`fo,"o`}) - f(`'f\\o,\'\"o'`, []string{`f\o,'\"o`}) - f(`'foo{bar="baz",x="y"}'`, []string{`foo{bar="baz",x="y"}`}) + f(`"foo"`, `foo`) + f(`"fo,'o"`, `"fo,'o"`) + f(`"f\\o,\'\"o"`, `"f\\o,\\'\"o"`) + f(`"foo{bar='baz',x='y'}"`, `"foo{bar='baz',x='y'}"`) + f(`'foo'`, `foo`) + f(`'fo,"o'`, `"fo,\"o"`) + f(`'f\\o,\'\"o'`, `"f\\o,'\\\"o"`) + f(`'foo{bar="baz",x="y"}'`, `"foo{bar=\"baz\",x=\"y\"}"`) // Multiple args - f(`foo,bar,baz`, []string{`foo`, `bar`, `baz`}) - f(`"foo",'bar',{[(ba'",z"`, []string{`foo`, `bar`, `{[(ba'",z"`}) - f(`foo,b"'ar,"baz,d`, []string{`foo`, `b"'ar,"baz`, `d`}) - f(`{foo="b,ar"},baz{x="y",z="d"}`, []string{`{foo="b,ar"}`, `baz{x="y",z="d"}`}) + f(`foo,bar,baz`, `foo,bar,baz`) + f(`"foo",'bar',{[(ba'",z"`, `foo,bar,"{[(ba'\",z\""`) + f(`foo,b"'ar,"baz,d`, `foo,"b\"'ar,\"baz",d`) + f(`{foo="b,ar"},baz{x="y",z="d"}`, `"{foo=\"b,ar\"}","baz{x=\"y\",z=\"d\"}"`) // Empty args - f(`""`, []string{``}) - f(`''`, []string{``}) - f(`,`, []string{``, ``}) - f(`,foo,,ba"r,`, []string{``, `foo`, ``, `ba"r`, ``}) + f(`""`, ``) + f(`''`, ``) + f(`,`, `,`) + f(`,foo,,ba"r,`, `,foo,,"ba\"r",`) // Special chars inside double quotes - f(`"foo,b\nar"`, []string{"foo,b\nar"}) - f(`"foo\x23bar"`, []string{"foo\x23bar"}) + f(`"foo,b\nar"`, `"foo,b\nar"`) + f(`"foo\x23bar"`, "foo\x23bar") } func TestArrayString_GetOptionalArg(t *testing.T) { f := func(s string, argIdx int, expectedValue string) { t.Helper() var a ArrayString - _ = a.Set(s) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } v := a.GetOptionalArg(argIdx) if v != expectedValue { t.Fatalf("unexpected value; got %q; want %q", v, expectedValue) @@ -126,7 +131,9 @@ func TestArrayString_String(t *testing.T) { f := func(s string) { t.Helper() var a ArrayString - _ = a.Set(s) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } result := a.String() if result != s { t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s) @@ -144,34 +151,42 @@ func TestArrayString_String(t *testing.T) { func TestArrayDuration(t *testing.T) { expected := ArrayDuration{ - time.Second * 10, - time.Minute * 5, + a: []time.Duration{ + time.Second * 10, + time.Minute * 5, + }, } if !reflect.DeepEqual(expected, fooFlagDuration) { - t.Fatalf("unexpected flag values; got\n%s\nwant\n%s", fooFlagDuration, expected) + t.Fatalf("unexpected flag values; got\n%s\nwant\n%s", &fooFlagDuration, &expected) } } func TestArrayDuration_Set(t *testing.T) { - f := func(s string, expectedValues []time.Duration) { + f := func(s, expectedResult string) { t.Helper() var a ArrayDuration - _ = a.Set(s) - if !reflect.DeepEqual([]time.Duration(a), expectedValues) { - t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", a, expectedValues) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } + result := a.String() + if result != expectedResult { + t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", result, expectedResult) } } - f("", nil) - f(`1m`, []time.Duration{time.Minute}) - f(`5m,1s,1h`, []time.Duration{time.Minute * 5, time.Second, time.Hour}) + f("", "") + f(`1m`, `1m0s`) + f(`5m,1s,1h`, `5m0s,1s,1h0m0s`) } func TestArrayDuration_GetOptionalArg(t *testing.T) { f := func(s string, argIdx int, defaultValue, expectedValue time.Duration) { t.Helper() var a ArrayDuration - _ = a.Set(s) - v := a.GetOptionalArgOrDefault(argIdx, defaultValue) + a.defaultValue = defaultValue + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } + v := a.GetOptionalArg(argIdx) if v != expectedValue { t.Fatalf("unexpected value; got %q; want %q", v, expectedValue) } @@ -186,7 +201,9 @@ func TestArrayDuration_String(t *testing.T) { f := func(s string) { t.Helper() var a ArrayDuration - _ = a.Set(s) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } result := a.String() if result != s { t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s) @@ -207,24 +224,29 @@ func TestArrayBool(t *testing.T) { } func TestArrayBool_Set(t *testing.T) { - f := func(s string, expectedValues []bool) { + f := func(s, expectedResult string) { t.Helper() var a ArrayBool - _ = a.Set(s) - if !reflect.DeepEqual([]bool(a), expectedValues) { - t.Fatalf("unexpected values parsed;\ngot\n%v\nwant\n%v", a, expectedValues) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } + result := a.String() + if result != expectedResult { + t.Fatalf("unexpected values parsed;\ngot\n%v\nwant\n%v", result, expectedResult) } } - f("", nil) - f(`true`, []bool{true}) - f(`false,True,False`, []bool{false, true, false}) + f("", "") + f(`true`, `true`) + f(`false,True,False`, `false,true,false`) } func TestArrayBool_GetOptionalArg(t *testing.T) { f := func(s string, argIdx int, expectedValue bool) { t.Helper() var a ArrayBool - _ = a.Set(s) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } v := a.GetOptionalArg(argIdx) if v != expectedValue { t.Fatalf("unexpected value; got %v; want %v", v, expectedValue) @@ -240,7 +262,9 @@ func TestArrayBool_String(t *testing.T) { f := func(s string) { t.Helper() var a ArrayBool - _ = a.Set(s) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } result := a.String() if result != s { t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s) @@ -253,32 +277,40 @@ func TestArrayBool_String(t *testing.T) { } func TestArrayInt(t *testing.T) { - expected := ArrayInt{1, 2, 3} + expected := ArrayInt{ + a: []int{1, 2, 3}, + } if !reflect.DeepEqual(expected, fooFlagInt) { t.Fatalf("unexpected flag values; got\n%d\nwant\n%d", fooFlagInt, expected) } } func TestArrayInt_Set(t *testing.T) { - f := func(s string, expectedValues []int) { + f := func(s, expectedResult string) { t.Helper() var a ArrayInt - _ = a.Set(s) - if !reflect.DeepEqual([]int(a), expectedValues) { - t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", a, expectedValues) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } + result := a.String() + if result != expectedResult { + t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", result, expectedResult) } } - f("", nil) - f(`1`, []int{1}) - f(`-2,3,-64`, []int{-2, 3, -64}) + f("", "") + f(`1`, `1`) + f(`-2,3,-64`, `-2,3,-64`) } func TestArrayInt_GetOptionalArg(t *testing.T) { f := func(s string, argIdx, defaultValue, expectedValue int) { t.Helper() var a ArrayInt - _ = a.Set(s) - v := a.GetOptionalArgOrDefault(argIdx, defaultValue) + a.defaultValue = defaultValue + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } + v := a.GetOptionalArg(argIdx) if v != expectedValue { t.Fatalf("unexpected value; got %d; want %d", v, expectedValue) } @@ -293,7 +325,9 @@ func TestArrayInt_String(t *testing.T) { f := func(s string) { t.Helper() var a ArrayInt - _ = a.Set(s) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } result := a.String() if result != s { t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s) @@ -306,8 +340,8 @@ func TestArrayInt_String(t *testing.T) { func TestArrayBytes(t *testing.T) { expected := []int64{10000000, 23, 10240} - result := make([]int64, len(fooFlagBytes)) - for i, b := range fooFlagBytes { + result := make([]int64, len(fooFlagBytes.a)) + for i, b := range fooFlagBytes.a { result[i] = b.N } if !reflect.DeepEqual(expected, result) { @@ -316,29 +350,31 @@ func TestArrayBytes(t *testing.T) { } func TestArrayBytes_Set(t *testing.T) { - f := func(s string, expectedValues []int64) { + f := func(s, expectedResult string) { t.Helper() var a ArrayBytes - _ = a.Set(s) - values := make([]int64, len(a)) - for i, v := range a { - values[i] = v.N + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) } - if !reflect.DeepEqual(values, expectedValues) { - t.Fatalf("unexpected values parsed;\ngot\n%d\nwant\n%d", values, expectedValues) + result := a.String() + if result != expectedResult { + t.Fatalf("unexpected values parsed;\ngot\n%s\nwant\n%s", result, expectedResult) } } - f("", []int64{}) - f(`1`, []int64{1}) - f(`-2,3,10kb`, []int64{-2, 3, 10000}) + f("", "") + f(`1`, `1`) + f(`-2,3,10kb`, `-2,3,10KB`) } func TestArrayBytes_GetOptionalArg(t *testing.T) { f := func(s string, argIdx int, defaultValue, expectedValue int64) { t.Helper() var a ArrayBytes - _ = a.Set(s) - v := a.GetOptionalArgOrDefault(argIdx, defaultValue) + a.defaultValue = defaultValue + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } + v := a.GetOptionalArg(argIdx) if v != expectedValue { t.Fatalf("unexpected value; got %d; want %d", v, expectedValue) } @@ -354,7 +390,9 @@ func TestArrayBytes_String(t *testing.T) { f := func(s string) { t.Helper() var a ArrayBytes - _ = a.Set(s) + if err := a.Set(s); err != nil { + t.Fatalf("unexpected error: %s", err) + } result := a.String() if result != s { t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s)