lib/flagutil: add defaultValue arg to NewArray{Int,Bytes,Duration} functions

The defaultValue is printed in the flag description when passing -help to the app.

This is a follow-up for aef31f201a

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4776
This commit is contained in:
Aliaksandr Valialkin 2023-08-12 04:17:55 -07:00
parent e8bcb17c8a
commit d7067c46d0
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
4 changed files with 203 additions and 149 deletions

View file

@ -26,12 +26,11 @@ var (
forceVMProto = flagutil.NewArrayBool("remoteWrite.forceVMProto", "Whether to force VictoriaMetrics remote write protocol for sending data "+ 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") "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 "+ "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") "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 "+ sendTimeout = flagutil.NewArrayDuration("remoteWrite.sendTimeout", time.Minute, "Timeout for sending a single block of data to the corresponding -remoteWrite.url")
defaultSendTimeout.String()+")") proxyURL = flagutil.NewArrayString("remoteWrite.proxyURL", "Optional proxy URL for writing 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") "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") 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") 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 { type client struct {
sanitizedURL string sanitizedURL string
remoteWriteURL string remoteWriteURL string
@ -137,7 +134,7 @@ func newHTTPClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persiste
} }
hc := &http.Client{ hc := &http.Client{
Transport: tr, Transport: tr,
Timeout: sendTimeout.GetOptionalArgOrDefault(argIdx, defaultSendTimeout), Timeout: sendTimeout.GetOptionalArg(argIdx),
} }
c := &client{ c := &client{
sanitizedURL: sanitizedURL, sanitizedURL: sanitizedURL,
@ -172,7 +169,7 @@ func newHTTPClient(argIdx int, remoteWriteURL, sanitizedURL string, fq *persiste
} }
func (c *client) init(argIdx, concurrency int, sanitizedURL string) { 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) logger.Infof("applying %d bytes per second rate limit for -remoteWrite.url=%q", bytesPerSec, sanitizedURL)
c.rl.perSecondLimit = int64(bytesPerSec) 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.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.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 { 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.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)) c.requestsOKCount = metrics.GetOrCreateCounter(fmt.Sprintf(`vmagent_remotewrite_requests_total{url=%q, status_code="2XX"}`, c.sanitizedURL))

View file

@ -48,14 +48,15 @@ var (
"isn't enough for sending high volume of collected data to remote storage. Default value is 2 * numberOfAvailableCPUs") "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. "+ 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") "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. "+ "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. "+ "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") "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. "+ "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") "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. "+ "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. "+ "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") "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 "+ 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 "+ "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") "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") "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 = "" pqURL.Fragment = ""
h := xxhash.Sum64([]byte(pqURL.String())) h := xxhash.Sum64([]byte(pqURL.String()))
queuePath := filepath.Join(*tmpDataPath, persistentQueueDirname, fmt.Sprintf("%d_%016X", argIdx+1, h)) 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 { if maxPendingBytes != 0 && maxPendingBytes < persistentqueue.DefaultChunkFileSize {
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4195 // See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4195
logger.Warnf("rounding the -remoteWrite.maxDiskUsagePerURL=%d to the minimum supported value: %d", maxPendingBytes, persistentqueue.DefaultChunkFileSize) 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) c.init(argIdx, *queues, sanitizedURL)
// Initialize pss // Initialize pss
sf := significantFigures.GetOptionalArgOrDefault(argIdx, 0) sf := significantFigures.GetOptionalArg(argIdx)
rd := roundDigits.GetOptionalArgOrDefault(argIdx, 100) rd := roundDigits.GetOptionalArg(argIdx)
pssLen := *queues pssLen := *queues
if n := cgroup.AvailableCPUs(); pssLen > n { if n := cgroup.AvailableCPUs(); pssLen > n {
// There is no sense in running more than availableCPUs concurrent pendingSeries, // 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 // Initialize sas
sasFile := streamAggrConfig.GetOptionalArg(argIdx) sasFile := streamAggrConfig.GetOptionalArg(argIdx)
if sasFile != "" { if sasFile != "" {
dedupInterval := streamAggrDedupInterval.GetOptionalArgOrDefault(argIdx, 0) dedupInterval := streamAggrDedupInterval.GetOptionalArg(argIdx)
sas, err := streamaggr.LoadFromFile(sasFile, rwctx.pushInternal, dedupInterval) sas, err := streamaggr.LoadFromFile(sasFile, rwctx.pushInternal, dedupInterval)
if err != nil { if err != nil {
logger.Fatalf("cannot initialize stream aggregators from -remoteWrite.streamAggr.config=%q: %s", sasFile, err) 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) sasFile := streamAggrConfig.GetOptionalArg(rwctx.idx)
logger.Infof("reloading stream aggregation configs pointed by -remoteWrite.streamAggr.config=%q", sasFile) 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() 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) sasNew, err := streamaggr.LoadFromFile(sasFile, rwctx.pushInternal, dedupInterval)
if err != nil { if err != nil {
metrics.GetOrCreateCounter(fmt.Sprintf(`vmagent_streamaggr_config_reloads_errors_total{path=%q}`, sasFile)).Inc() metrics.GetOrCreateCounter(fmt.Sprintf(`vmagent_streamaggr_config_reloads_errors_total{path=%q}`, sasFile)).Inc()
@ -775,7 +776,7 @@ func CheckStreamAggrConfigs() error {
if sasFile == "" { if sasFile == "" {
continue continue
} }
dedupInterval := streamAggrDedupInterval.GetOptionalArgOrDefault(idx, 0) dedupInterval := streamAggrDedupInterval.GetOptionalArg(idx)
sas, err := streamaggr.LoadFromFile(sasFile, pushNoop, dedupInterval) sas, err := streamaggr.LoadFromFile(sasFile, pushNoop, dedupInterval)
if err != nil { if err != nil {
return fmt.Errorf("cannot load -remoteWrite.streamAggr.config=%q: %w", sasFile, err) return fmt.Errorf("cannot load -remoteWrite.streamAggr.config=%q: %w", sasFile, err)

View file

@ -16,12 +16,15 @@ func NewArrayString(name, description string) *ArrayString {
return &a return &a
} }
// NewArrayDuration returns new ArrayDuration with the given name and description. // NewArrayDuration returns new ArrayDuration with the given name, defaultValue and description.
func NewArrayDuration(name, description string) *ArrayDuration { 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." description += "\nSupports `array` of values separated by comma or specified via multiple flags."
var a ArrayDuration a := &ArrayDuration{
flag.Var(&a, name, description) defaultValue: defaultValue,
return &a }
flag.Var(a, name, description)
return a
} }
// NewArrayBool returns new ArrayBool with the given name and description. // NewArrayBool returns new ArrayBool with the given name and description.
@ -32,21 +35,27 @@ func NewArrayBool(name, description string) *ArrayBool {
return &a return &a
} }
// NewArrayInt returns new ArrayInt with the given name and description. // NewArrayInt returns new ArrayInt with the given name, defaultValue and description.
func NewArrayInt(name, description string) *ArrayInt { 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." description += "\nSupports `array` of values separated by comma or specified via multiple flags."
var a ArrayInt a := &ArrayInt{
flag.Var(&a, name, description) defaultValue: defaultValue,
return &a }
flag.Var(a, name, description)
return a
} }
// NewArrayBytes returns new ArrayBytes with the given name and description. // NewArrayBytes returns new ArrayBytes with the given name, defaultValue and description.
func NewArrayBytes(name, description string) *ArrayBytes { 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 += "\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." description += "\nSupports `array` of values separated by comma or specified via multiple flags."
var a ArrayBytes a := &ArrayBytes{
flag.Var(&a, name, description) defaultValue: defaultValue,
return &a }
flag.Var(a, name, description)
return a
} }
// ArrayString is a flag that holds an array of strings. // 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 // String implements flag.Value interface
func (a *ArrayBool) String() string { func (a *ArrayBool) String() string {
formattedBools := make([]string, len(*a)) formattedResults := make([]string, len(*a))
for i, v := range *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 // 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. // ArrayDuration is a flag that holds an array of time.Duration values.
// //
// Has the same api as ArrayString. // Has the same api as ArrayString.
type ArrayDuration []time.Duration type ArrayDuration struct {
defaultValue time.Duration
a []time.Duration
}
// String implements flag.Value interface // String implements flag.Value interface
func (a *ArrayDuration) String() string { func (a *ArrayDuration) String() string {
formattedBools := make([]string, len(*a)) x := a.a
for i, v := range *a { formattedResults := make([]string, len(x))
formattedBools[i] = v.String() for i, v := range x {
formattedResults[i] = v.String()
} }
return strings.Join(formattedBools, ",") return strings.Join(formattedResults, ",")
} }
// Set implements flag.Value interface // Set implements flag.Value interface
@ -274,20 +287,19 @@ func (a *ArrayDuration) Set(value string) error {
if err != nil { if err != nil {
return err return err
} }
*a = append(*a, b) a.a = append(a.a, b)
} }
return nil return nil
} }
// GetOptionalArgOrDefault returns optional arg under the given argIdx, // GetOptionalArg returns optional arg under the given argIdx, or default value, if argIdx not found.
// or default value, if argIdx not found. func (a *ArrayDuration) GetOptionalArg(argIdx int) time.Duration {
func (a *ArrayDuration) GetOptionalArgOrDefault(argIdx int, defaultValue time.Duration) time.Duration { x := a.a
x := *a
if argIdx >= len(x) { if argIdx >= len(x) {
if len(x) == 1 { if len(x) == 1 {
return x[0] return x[0]
} }
return defaultValue return a.defaultValue
} }
return x[argIdx] 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. // ArrayInt is flag that holds an array of ints.
// //
// Has the same api as ArrayString. // Has the same api as ArrayString.
type ArrayInt []int type ArrayInt struct {
defaultValue int
a []int
}
// String implements flag.Value interface // String implements flag.Value interface
func (a *ArrayInt) String() string { func (a *ArrayInt) String() string {
x := *a x := a.a
formattedInts := make([]string, len(x)) formattedInts := make([]string, len(x))
for i, v := range x { for i, v := range x {
formattedInts[i] = strconv.Itoa(v) formattedInts[i] = strconv.Itoa(v)
@ -315,31 +330,34 @@ func (a *ArrayInt) Set(value string) error {
if err != nil { if err != nil {
return err return err
} }
*a = append(*a, n) a.a = append(a.a, n)
} }
return nil return nil
} }
// GetOptionalArgOrDefault returns optional arg under the given argIdx. // GetOptionalArg returns optional arg under the given argIdx or default value.
func (a *ArrayInt) GetOptionalArgOrDefault(argIdx, defaultValue int) int { func (a *ArrayInt) GetOptionalArg(argIdx int) int {
x := *a x := a.a
if argIdx < len(x) { if argIdx < len(x) {
return x[argIdx] return x[argIdx]
} }
if len(x) == 1 { if len(x) == 1 {
return x[0] return x[0]
} }
return defaultValue return a.defaultValue
} }
// ArrayBytes is flag that holds an array of Bytes. // ArrayBytes is flag that holds an array of Bytes.
// //
// Has the same api as ArrayString. // Has the same api as ArrayString.
type ArrayBytes []*Bytes type ArrayBytes struct {
defaultValue int64
a []*Bytes
}
// String implements flag.Value interface // String implements flag.Value interface
func (a *ArrayBytes) String() string { func (a *ArrayBytes) String() string {
x := *a x := a.a
formattedBytes := make([]string, len(x)) formattedBytes := make([]string, len(x))
for i, v := range x { for i, v := range x {
formattedBytes[i] = v.String() formattedBytes[i] = v.String()
@ -355,19 +373,19 @@ func (a *ArrayBytes) Set(value string) error {
if err := b.Set(v); err != nil { if err := b.Set(v); err != nil {
return err return err
} }
*a = append(*a, &b) a.a = append(a.a, &b)
} }
return nil return nil
} }
// GetOptionalArgOrDefault returns optional arg under the given argIdx. // GetOptionalArg returns optional arg under the given argIdx, or default value
func (a *ArrayBytes) GetOptionalArgOrDefault(argIdx int, defaultValue int64) int64 { func (a *ArrayBytes) GetOptionalArg(argIdx int) int64 {
x := *a x := a.a
if argIdx < len(x) { if argIdx < len(x) {
return x[argIdx].N return x[argIdx].N
} }
if len(x) == 1 { if len(x) == 1 {
return x[0].N return x[0].N
} }
return defaultValue return a.defaultValue
} }

View file

@ -45,69 +45,74 @@ func TestArrayString(t *testing.T) {
} }
func TestArrayString_Set(t *testing.T) { func TestArrayString_Set(t *testing.T) {
f := func(s string, expectedValues []string) { f := func(s, expectedResult string) {
t.Helper() t.Helper()
var a ArrayString var a ArrayString
_ = a.Set(s) if err := a.Set(s); err != nil {
if !reflect.DeepEqual([]string(a), expectedValues) { t.Fatalf("unexpected error: %s", err)
t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", a, expectedValues) }
result := a.String()
if result != expectedResult {
t.Fatalf("unexpected values parsed;\ngot\n%s\nwant\n%s", result, expectedResult)
} }
} }
// Zero args // Zero args
f("", nil) f("", "")
// Single arg // Single arg
f(`foo`, []string{`foo`}) f(`foo`, `foo`)
f(`fo"o`, []string{`fo"o`}) f(`fo"o`, `"fo\"o"`)
f(`fo'o`, []string{`fo'o`}) f(`fo'o`, `"fo'o"`)
f(`fo{o`, []string{`fo{o`}) f(`fo{o`, `"fo{o"`)
f(`fo[o`, []string{`fo[o`}) f(`fo[o`, `"fo[o"`)
f(`fo(o`, []string{`fo(o`}) f(`fo(o`, `"fo(o"`)
// Single arg with Prometheus label filters // Single arg with Prometheus label filters
f(`foo{bar="baz",x="y"}`, []string{`foo{bar="baz",x="y"}`}) f(`foo{bar="baz",x="y"}`, `"foo{bar=\"baz\",x=\"y\"}"`)
f(`foo{bar="ba}z",x="y"}`, []string{`foo{bar="ba}z",x="y"}`}) f(`foo{bar="ba}z",x="y"}`, `"foo{bar=\"ba}z\",x=\"y\"}"`)
f(`foo{bar='baz',x="y"}`, []string{`foo{bar='baz',x="y"}`}) f(`foo{bar='baz',x="y"}`, `"foo{bar='baz',x=\"y\"}"`)
f(`foo{bar='baz',x='y'}`, []string{`foo{bar='baz',x='y'}`}) f(`foo{bar='baz',x='y'}`, `"foo{bar='baz',x='y'}"`)
f(`foo{bar='ba}z',x='y'}`, []string{`foo{bar='ba}z',x='y'}`}) f(`foo{bar='ba}z',x='y'}`, `"foo{bar='ba}z',x='y'}"`)
f(`{foo="ba[r",baz='a'}`, []string{`{foo="ba[r",baz='a'}`}) f(`{foo="ba[r",baz='a'}`, `"{foo=\"ba[r\",baz='a'}"`)
// Single arg with JSON // Single arg with JSON
f(`[1,2,3]`, []string{`[1,2,3]`}) f(`[1,2,3]`, `"[1,2,3]"`)
f(`{"foo":"ba,r",baz:x}`, []string{`{"foo":"ba,r",baz:x}`}) f(`{"foo":"ba,r",baz:x}`, `"{\"foo\":\"ba,r\",baz:x}"`)
// Single quoted arg // Single quoted arg
f(`"foo"`, []string{`foo`}) f(`"foo"`, `foo`)
f(`"fo,'o"`, []string{`fo,'o`}) f(`"fo,'o"`, `"fo,'o"`)
f(`"f\\o,\'\"o"`, []string{`f\o,\'"o`}) f(`"f\\o,\'\"o"`, `"f\\o,\\'\"o"`)
f(`"foo{bar='baz',x='y'}"`, []string{`foo{bar='baz',x='y'}`}) f(`"foo{bar='baz',x='y'}"`, `"foo{bar='baz',x='y'}"`)
f(`'foo'`, []string{`foo`}) f(`'foo'`, `foo`)
f(`'fo,"o'`, []string{`fo,"o`}) f(`'fo,"o'`, `"fo,\"o"`)
f(`'f\\o,\'\"o'`, []string{`f\o,'\"o`}) f(`'f\\o,\'\"o'`, `"f\\o,'\\\"o"`)
f(`'foo{bar="baz",x="y"}'`, []string{`foo{bar="baz",x="y"}`}) f(`'foo{bar="baz",x="y"}'`, `"foo{bar=\"baz\",x=\"y\"}"`)
// Multiple args // Multiple args
f(`foo,bar,baz`, []string{`foo`, `bar`, `baz`}) f(`foo,bar,baz`, `foo,bar,baz`)
f(`"foo",'bar',{[(ba'",z"`, []string{`foo`, `bar`, `{[(ba'",z"`}) f(`"foo",'bar',{[(ba'",z"`, `foo,bar,"{[(ba'\",z\""`)
f(`foo,b"'ar,"baz,d`, []string{`foo`, `b"'ar,"baz`, `d`}) f(`foo,b"'ar,"baz,d`, `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="b,ar"},baz{x="y",z="d"}`, `"{foo=\"b,ar\"}","baz{x=\"y\",z=\"d\"}"`)
// Empty args // Empty args
f(`""`, []string{``}) f(`""`, ``)
f(`''`, []string{``}) f(`''`, ``)
f(`,`, []string{``, ``}) f(`,`, `,`)
f(`,foo,,ba"r,`, []string{``, `foo`, ``, `ba"r`, ``}) f(`,foo,,ba"r,`, `,foo,,"ba\"r",`)
// Special chars inside double quotes // Special chars inside double quotes
f(`"foo,b\nar"`, []string{"foo,b\nar"}) f(`"foo,b\nar"`, `"foo,b\nar"`)
f(`"foo\x23bar"`, []string{"foo\x23bar"}) f(`"foo\x23bar"`, "foo\x23bar")
} }
func TestArrayString_GetOptionalArg(t *testing.T) { func TestArrayString_GetOptionalArg(t *testing.T) {
f := func(s string, argIdx int, expectedValue string) { f := func(s string, argIdx int, expectedValue string) {
t.Helper() t.Helper()
var a ArrayString var a ArrayString
_ = a.Set(s) if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err)
}
v := a.GetOptionalArg(argIdx) v := a.GetOptionalArg(argIdx)
if v != expectedValue { if v != expectedValue {
t.Fatalf("unexpected value; got %q; want %q", 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) { f := func(s string) {
t.Helper() t.Helper()
var a ArrayString var a ArrayString
_ = a.Set(s) if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err)
}
result := a.String() result := a.String()
if result != s { if result != s {
t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", 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) { func TestArrayDuration(t *testing.T) {
expected := ArrayDuration{ expected := ArrayDuration{
time.Second * 10, a: []time.Duration{
time.Minute * 5, time.Second * 10,
time.Minute * 5,
},
} }
if !reflect.DeepEqual(expected, fooFlagDuration) { 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) { func TestArrayDuration_Set(t *testing.T) {
f := func(s string, expectedValues []time.Duration) { f := func(s, expectedResult string) {
t.Helper() t.Helper()
var a ArrayDuration var a ArrayDuration
_ = a.Set(s) if err := a.Set(s); err != nil {
if !reflect.DeepEqual([]time.Duration(a), expectedValues) { t.Fatalf("unexpected error: %s", err)
t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", a, expectedValues) }
result := a.String()
if result != expectedResult {
t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", result, expectedResult)
} }
} }
f("", nil) f("", "")
f(`1m`, []time.Duration{time.Minute}) f(`1m`, `1m0s`)
f(`5m,1s,1h`, []time.Duration{time.Minute * 5, time.Second, time.Hour}) f(`5m,1s,1h`, `5m0s,1s,1h0m0s`)
} }
func TestArrayDuration_GetOptionalArg(t *testing.T) { func TestArrayDuration_GetOptionalArg(t *testing.T) {
f := func(s string, argIdx int, defaultValue, expectedValue time.Duration) { f := func(s string, argIdx int, defaultValue, expectedValue time.Duration) {
t.Helper() t.Helper()
var a ArrayDuration var a ArrayDuration
_ = a.Set(s) a.defaultValue = defaultValue
v := a.GetOptionalArgOrDefault(argIdx, defaultValue) if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err)
}
v := a.GetOptionalArg(argIdx)
if v != expectedValue { if v != expectedValue {
t.Fatalf("unexpected value; got %q; want %q", 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) { f := func(s string) {
t.Helper() t.Helper()
var a ArrayDuration var a ArrayDuration
_ = a.Set(s) if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err)
}
result := a.String() result := a.String()
if result != s { if result != s {
t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", 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) { func TestArrayBool_Set(t *testing.T) {
f := func(s string, expectedValues []bool) { f := func(s, expectedResult string) {
t.Helper() t.Helper()
var a ArrayBool var a ArrayBool
_ = a.Set(s) if err := a.Set(s); err != nil {
if !reflect.DeepEqual([]bool(a), expectedValues) { t.Fatalf("unexpected error: %s", err)
t.Fatalf("unexpected values parsed;\ngot\n%v\nwant\n%v", a, expectedValues) }
result := a.String()
if result != expectedResult {
t.Fatalf("unexpected values parsed;\ngot\n%v\nwant\n%v", result, expectedResult)
} }
} }
f("", nil) f("", "")
f(`true`, []bool{true}) f(`true`, `true`)
f(`false,True,False`, []bool{false, true, false}) f(`false,True,False`, `false,true,false`)
} }
func TestArrayBool_GetOptionalArg(t *testing.T) { func TestArrayBool_GetOptionalArg(t *testing.T) {
f := func(s string, argIdx int, expectedValue bool) { f := func(s string, argIdx int, expectedValue bool) {
t.Helper() t.Helper()
var a ArrayBool var a ArrayBool
_ = a.Set(s) if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err)
}
v := a.GetOptionalArg(argIdx) v := a.GetOptionalArg(argIdx)
if v != expectedValue { if v != expectedValue {
t.Fatalf("unexpected value; got %v; want %v", 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) { f := func(s string) {
t.Helper() t.Helper()
var a ArrayBool var a ArrayBool
_ = a.Set(s) if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err)
}
result := a.String() result := a.String()
if result != s { if result != s {
t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", 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) { func TestArrayInt(t *testing.T) {
expected := ArrayInt{1, 2, 3} expected := ArrayInt{
a: []int{1, 2, 3},
}
if !reflect.DeepEqual(expected, fooFlagInt) { if !reflect.DeepEqual(expected, fooFlagInt) {
t.Fatalf("unexpected flag values; got\n%d\nwant\n%d", fooFlagInt, expected) t.Fatalf("unexpected flag values; got\n%d\nwant\n%d", fooFlagInt, expected)
} }
} }
func TestArrayInt_Set(t *testing.T) { func TestArrayInt_Set(t *testing.T) {
f := func(s string, expectedValues []int) { f := func(s, expectedResult string) {
t.Helper() t.Helper()
var a ArrayInt var a ArrayInt
_ = a.Set(s) if err := a.Set(s); err != nil {
if !reflect.DeepEqual([]int(a), expectedValues) { t.Fatalf("unexpected error: %s", err)
t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", a, expectedValues) }
result := a.String()
if result != expectedResult {
t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", result, expectedResult)
} }
} }
f("", nil) f("", "")
f(`1`, []int{1}) f(`1`, `1`)
f(`-2,3,-64`, []int{-2, 3, -64}) f(`-2,3,-64`, `-2,3,-64`)
} }
func TestArrayInt_GetOptionalArg(t *testing.T) { func TestArrayInt_GetOptionalArg(t *testing.T) {
f := func(s string, argIdx, defaultValue, expectedValue int) { f := func(s string, argIdx, defaultValue, expectedValue int) {
t.Helper() t.Helper()
var a ArrayInt var a ArrayInt
_ = a.Set(s) a.defaultValue = defaultValue
v := a.GetOptionalArgOrDefault(argIdx, defaultValue) if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err)
}
v := a.GetOptionalArg(argIdx)
if v != expectedValue { if v != expectedValue {
t.Fatalf("unexpected value; got %d; want %d", 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) { f := func(s string) {
t.Helper() t.Helper()
var a ArrayInt var a ArrayInt
_ = a.Set(s) if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err)
}
result := a.String() result := a.String()
if result != s { if result != s {
t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", 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) { func TestArrayBytes(t *testing.T) {
expected := []int64{10000000, 23, 10240} expected := []int64{10000000, 23, 10240}
result := make([]int64, len(fooFlagBytes)) result := make([]int64, len(fooFlagBytes.a))
for i, b := range fooFlagBytes { for i, b := range fooFlagBytes.a {
result[i] = b.N result[i] = b.N
} }
if !reflect.DeepEqual(expected, result) { if !reflect.DeepEqual(expected, result) {
@ -316,29 +350,31 @@ func TestArrayBytes(t *testing.T) {
} }
func TestArrayBytes_Set(t *testing.T) { func TestArrayBytes_Set(t *testing.T) {
f := func(s string, expectedValues []int64) { f := func(s, expectedResult string) {
t.Helper() t.Helper()
var a ArrayBytes var a ArrayBytes
_ = a.Set(s) if err := a.Set(s); err != nil {
values := make([]int64, len(a)) t.Fatalf("unexpected error: %s", err)
for i, v := range a {
values[i] = v.N
} }
if !reflect.DeepEqual(values, expectedValues) { result := a.String()
t.Fatalf("unexpected values parsed;\ngot\n%d\nwant\n%d", values, expectedValues) if result != expectedResult {
t.Fatalf("unexpected values parsed;\ngot\n%s\nwant\n%s", result, expectedResult)
} }
} }
f("", []int64{}) f("", "")
f(`1`, []int64{1}) f(`1`, `1`)
f(`-2,3,10kb`, []int64{-2, 3, 10000}) f(`-2,3,10kb`, `-2,3,10KB`)
} }
func TestArrayBytes_GetOptionalArg(t *testing.T) { func TestArrayBytes_GetOptionalArg(t *testing.T) {
f := func(s string, argIdx int, defaultValue, expectedValue int64) { f := func(s string, argIdx int, defaultValue, expectedValue int64) {
t.Helper() t.Helper()
var a ArrayBytes var a ArrayBytes
_ = a.Set(s) a.defaultValue = defaultValue
v := a.GetOptionalArgOrDefault(argIdx, defaultValue) if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err)
}
v := a.GetOptionalArg(argIdx)
if v != expectedValue { if v != expectedValue {
t.Fatalf("unexpected value; got %d; want %d", 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) { f := func(s string) {
t.Helper() t.Helper()
var a ArrayBytes var a ArrayBytes
_ = a.Set(s) if err := a.Set(s); err != nil {
t.Fatalf("unexpected error: %s", err)
}
result := a.String() result := a.String()
if result != s { if result != s {
t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s) t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s)