From c1fa9828b3de1a0fb9fd0e5797a6975b8cdba648 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Sat, 1 Oct 2022 18:26:05 +0300 Subject: [PATCH] lib/flagutil: rename Array to ArrayString This makes the ArrayString more consistent with other Array* types. While at it, add ArrayBytes type, which will be used for https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3071 --- app/vmagent/remotewrite/client.go | 46 +++---- app/vmagent/remotewrite/relabel.go | 4 +- app/vmagent/remotewrite/remotewrite.go | 4 +- app/vmalert/main.go | 6 +- app/vmalert/notifier/init.go | 30 ++--- lib/flagutil/array.go | 77 ++++++++++-- lib/flagutil/array_test.go | 162 ++++++++++++++++--------- lib/httpserver/httpserver.go | 2 +- lib/influxutils/influxutils.go | 2 +- lib/pushmetrics/pushmetrics.go | 4 +- 10 files changed, 217 insertions(+), 120 deletions(-) diff --git a/app/vmagent/remotewrite/client.go b/app/vmagent/remotewrite/client.go index 06f4f324d6..2d25edbad7 100644 --- a/app/vmagent/remotewrite/client.go +++ b/app/vmagent/remotewrite/client.go @@ -24,46 +24,46 @@ var ( "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") - proxyURL = flagutil.NewArray("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") tlsInsecureSkipVerify = flagutil.NewArrayBool("remoteWrite.tlsInsecureSkipVerify", "Whether to skip tls verification when connecting to the corresponding -remoteWrite.url") - tlsCertFile = flagutil.NewArray("remoteWrite.tlsCertFile", "Optional path to client-side TLS certificate file to use when connecting "+ + tlsCertFile = flagutil.NewArrayString("remoteWrite.tlsCertFile", "Optional path to client-side TLS certificate file to use when connecting "+ "to the corresponding -remoteWrite.url") - tlsKeyFile = flagutil.NewArray("remoteWrite.tlsKeyFile", "Optional path to client-side TLS certificate key to use when connecting to the corresponding -remoteWrite.url") - tlsCAFile = flagutil.NewArray("remoteWrite.tlsCAFile", "Optional path to TLS CA file to use for verifying connections to the corresponding -remoteWrite.url. "+ + tlsKeyFile = flagutil.NewArrayString("remoteWrite.tlsKeyFile", "Optional path to client-side TLS certificate key to use when connecting to the corresponding -remoteWrite.url") + tlsCAFile = flagutil.NewArrayString("remoteWrite.tlsCAFile", "Optional path to TLS CA file to use for verifying connections to the corresponding -remoteWrite.url. "+ "By default system CA is used") - tlsServerName = flagutil.NewArray("remoteWrite.tlsServerName", "Optional TLS server name to use for connections to the corresponding -remoteWrite.url. "+ + tlsServerName = flagutil.NewArrayString("remoteWrite.tlsServerName", "Optional TLS server name to use for connections to the corresponding -remoteWrite.url. "+ "By default the server name from -remoteWrite.url is used") - headers = flagutil.NewArray("remoteWrite.headers", "Optional HTTP headers to send with each request to the corresponding -remoteWrite.url. "+ + headers = flagutil.NewArrayString("remoteWrite.headers", "Optional HTTP headers to send with each request to the corresponding -remoteWrite.url. "+ "For example, -remoteWrite.headers='My-Auth:foobar' would send 'My-Auth: foobar' HTTP header with every request to the corresponding -remoteWrite.url. "+ "Multiple headers must be delimited by '^^': -remoteWrite.headers='header1:value1^^header2:value2'") - basicAuthUsername = flagutil.NewArray("remoteWrite.basicAuth.username", "Optional basic auth username to use for the corresponding -remoteWrite.url") - basicAuthPassword = flagutil.NewArray("remoteWrite.basicAuth.password", "Optional basic auth password to use for the corresponding -remoteWrite.url") - basicAuthPasswordFile = flagutil.NewArray("remoteWrite.basicAuth.passwordFile", "Optional path to basic auth password to use for the corresponding -remoteWrite.url. "+ + basicAuthUsername = flagutil.NewArrayString("remoteWrite.basicAuth.username", "Optional basic auth username to use for the corresponding -remoteWrite.url") + basicAuthPassword = flagutil.NewArrayString("remoteWrite.basicAuth.password", "Optional basic auth password to use for the corresponding -remoteWrite.url") + basicAuthPasswordFile = flagutil.NewArrayString("remoteWrite.basicAuth.passwordFile", "Optional path to basic auth password to use for the corresponding -remoteWrite.url. "+ "The file is re-read every second") - bearerToken = flagutil.NewArray("remoteWrite.bearerToken", "Optional bearer auth token to use for the corresponding -remoteWrite.url") - bearerTokenFile = flagutil.NewArray("remoteWrite.bearerTokenFile", "Optional path to bearer token file to use for the corresponding -remoteWrite.url. "+ + bearerToken = flagutil.NewArrayString("remoteWrite.bearerToken", "Optional bearer auth token to use for the corresponding -remoteWrite.url") + bearerTokenFile = flagutil.NewArrayString("remoteWrite.bearerTokenFile", "Optional path to bearer token file to use for the corresponding -remoteWrite.url. "+ "The token is re-read from the file every second") - oauth2ClientID = flagutil.NewArray("remoteWrite.oauth2.clientID", "Optional OAuth2 clientID to use for the corresponding -remoteWrite.url") - oauth2ClientSecret = flagutil.NewArray("remoteWrite.oauth2.clientSecret", "Optional OAuth2 clientSecret to use for the corresponding -remoteWrite.url") - oauth2ClientSecretFile = flagutil.NewArray("remoteWrite.oauth2.clientSecretFile", "Optional OAuth2 clientSecretFile to use for the corresponding -remoteWrite.url") - oauth2TokenURL = flagutil.NewArray("remoteWrite.oauth2.tokenUrl", "Optional OAuth2 tokenURL to use for the corresponding -remoteWrite.url") - oauth2Scopes = flagutil.NewArray("remoteWrite.oauth2.scopes", "Optional OAuth2 scopes to use for the corresponding -remoteWrite.url. Scopes must be delimited by ';'") + oauth2ClientID = flagutil.NewArrayString("remoteWrite.oauth2.clientID", "Optional OAuth2 clientID to use for the corresponding -remoteWrite.url") + oauth2ClientSecret = flagutil.NewArrayString("remoteWrite.oauth2.clientSecret", "Optional OAuth2 clientSecret to use for the corresponding -remoteWrite.url") + oauth2ClientSecretFile = flagutil.NewArrayString("remoteWrite.oauth2.clientSecretFile", "Optional OAuth2 clientSecretFile to use for the corresponding -remoteWrite.url") + oauth2TokenURL = flagutil.NewArrayString("remoteWrite.oauth2.tokenUrl", "Optional OAuth2 tokenURL to use for the corresponding -remoteWrite.url") + oauth2Scopes = flagutil.NewArrayString("remoteWrite.oauth2.scopes", "Optional OAuth2 scopes to use for the corresponding -remoteWrite.url. Scopes must be delimited by ';'") awsUseSigv4 = flagutil.NewArrayBool("remoteWrite.aws.useSigv4", "Enables SigV4 request signing for the corresponding -remoteWrite.url. "+ "It is expected that other -remoteWrite.aws.* command-line flags are set if sigv4 request signing is enabled") - awsEC2Endpoint = flagutil.NewArray("remoteWrite.aws.ec2Endpoint", "Optional AWS EC2 API endpoint to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") - awsSTSEndpoint = flagutil.NewArray("remoteWrite.aws.stsEndpoint", "Optional AWS STS API endpoint to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") - awsRegion = flagutil.NewArray("remoteWrite.aws.region", "Optional AWS region to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") - awsRoleARN = flagutil.NewArray("remoteWrite.aws.roleARN", "Optional AWS roleARN to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") - awsAccessKey = flagutil.NewArray("remoteWrite.aws.accessKey", "Optional AWS AccessKey to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") - awsService = flagutil.NewArray("remoteWrite.aws.service", "Optional AWS Service to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set. "+ + awsEC2Endpoint = flagutil.NewArrayString("remoteWrite.aws.ec2Endpoint", "Optional AWS EC2 API endpoint to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") + awsSTSEndpoint = flagutil.NewArrayString("remoteWrite.aws.stsEndpoint", "Optional AWS STS API endpoint to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") + awsRegion = flagutil.NewArrayString("remoteWrite.aws.region", "Optional AWS region to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") + awsRoleARN = flagutil.NewArrayString("remoteWrite.aws.roleARN", "Optional AWS roleARN to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") + awsAccessKey = flagutil.NewArrayString("remoteWrite.aws.accessKey", "Optional AWS AccessKey to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set") + awsService = flagutil.NewArrayString("remoteWrite.aws.service", "Optional AWS Service to use for the corresponding -remoteWrite.url if -remoteWrite.aws.useSigv4 is set. "+ "Defaults to \"aps\"") - awsSecretKey = flagutil.NewArray("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") ) type client struct { diff --git a/app/vmagent/remotewrite/relabel.go b/app/vmagent/remotewrite/relabel.go index 4374ff2bcf..2eb4f24213 100644 --- a/app/vmagent/remotewrite/relabel.go +++ b/app/vmagent/remotewrite/relabel.go @@ -13,14 +13,14 @@ import ( ) var ( - unparsedLabelsGlobal = flagutil.NewArray("remoteWrite.label", "Optional label in the form 'name=value' to add to all the metrics before sending them to -remoteWrite.url. "+ + unparsedLabelsGlobal = flagutil.NewArrayString("remoteWrite.label", "Optional label in the form 'name=value' to add to all the metrics before sending them to -remoteWrite.url. "+ "Pass multiple -remoteWrite.label flags in order to add multiple labels to metrics before sending them to remote storage") relabelConfigPathGlobal = flag.String("remoteWrite.relabelConfig", "", "Optional path to file with relabel_config entries. "+ "The path can point either to local file or to http url. These entries are applied to all the metrics "+ "before sending them to -remoteWrite.url. See https://docs.victoriametrics.com/vmagent.html#relabeling for details") relabelDebugGlobal = flag.Bool("remoteWrite.relabelDebug", false, "Whether to log metrics before and after relabeling with -remoteWrite.relabelConfig. "+ "If the -remoteWrite.relabelDebug is enabled, then the metrics aren't sent to remote storage. This is useful for debugging the relabeling configs") - relabelConfigPaths = flagutil.NewArray("remoteWrite.urlRelabelConfig", "Optional path to relabel config for the corresponding -remoteWrite.url. "+ + relabelConfigPaths = flagutil.NewArrayString("remoteWrite.urlRelabelConfig", "Optional path to relabel config for the corresponding -remoteWrite.url. "+ "The path can point either to local file or to http url") relabelDebug = flagutil.NewArrayBool("remoteWrite.urlRelabelDebug", "Whether to log metrics before and after relabeling with -remoteWrite.urlRelabelConfig. "+ "If the -remoteWrite.urlRelabelDebug is enabled, then the metrics aren't sent to the corresponding -remoteWrite.url. "+ diff --git a/app/vmagent/remotewrite/remotewrite.go b/app/vmagent/remotewrite/remotewrite.go index c863765aa1..995139502d 100644 --- a/app/vmagent/remotewrite/remotewrite.go +++ b/app/vmagent/remotewrite/remotewrite.go @@ -26,10 +26,10 @@ import ( ) var ( - remoteWriteURLs = flagutil.NewArray("remoteWrite.url", "Remote storage URL to write data to. It must support Prometheus remote_write API. "+ + remoteWriteURLs = flagutil.NewArrayString("remoteWrite.url", "Remote storage URL to write data to. It must support Prometheus remote_write API. "+ "It is recommended using VictoriaMetrics as remote storage. Example url: http://:8428/api/v1/write . "+ "Pass multiple -remoteWrite.url flags in order to replicate data to multiple remote storage systems. See also -remoteWrite.multitenantURL") - remoteWriteMultitenantURLs = flagutil.NewArray("remoteWrite.multitenantURL", "Base path for multitenant remote storage URL to write data to. "+ + remoteWriteMultitenantURLs = flagutil.NewArrayString("remoteWrite.multitenantURL", "Base path for multitenant remote storage URL to write data to. "+ "See https://docs.victoriametrics.com/vmagent.html#multitenancy for details. Example url: http://:8480 . "+ "Pass multiple -remoteWrite.multitenantURL flags in order to replicate data to multiple remote storage systems. See also -remoteWrite.url") tmpDataPath = flag.String("remoteWrite.tmpDataPath", "vmagent-remotewrite-data", "Path to directory where temporary data for remote write component is stored. "+ diff --git a/app/vmalert/main.go b/app/vmalert/main.go index 01adb9972a..46d846f394 100644 --- a/app/vmalert/main.go +++ b/app/vmalert/main.go @@ -28,7 +28,7 @@ import ( ) var ( - rulePath = flagutil.NewArray("rule", `Path to the file with alert rules. + rulePath = flagutil.NewArrayString("rule", `Path to the file with alert rules. Supports patterns. Flag can be specified multiple times. Examples: -rule="/path/to/file". Path to a single file with alerting rules @@ -36,7 +36,7 @@ Examples: absolute path to all .yaml files in root. Rule files may contain %{ENV_VAR} placeholders, which are substituted by the corresponding env vars.`) - ruleTemplatesPath = flagutil.NewArray("rule.templates", `Path or glob pattern to location with go template definitions + ruleTemplatesPath = flagutil.NewArrayString("rule.templates", `Path or glob pattern to location with go template definitions for rules annotations templating. Flag can be specified multiple times. Examples: -rule.templates="/path/to/file". Path to a single file with go templates @@ -62,7 +62,7 @@ absolute path to all .tpl files in root.`) externalAlertSource = flag.String("external.alert.source", "", `External Alert Source allows to override the Source link for alerts sent to AlertManager for cases where you want to build a custom link to Grafana, Prometheus or any other service. Supports templating. For example, link to Grafana: 'explore?orgId=1&left=[\"now-1h\",\"now\",\"VictoriaMetrics\",{\"expr\": \"{{$expr|quotesEscape|crlfEscape|queryEscape}}\"},{\"mode\":\"Metrics\"},{\"ui\":[true,true,true,\"none\"]}]'. If empty 'vmalert/alert?group_id={{.GroupID}}&alert_id={{.AlertID}}' is used.`) - externalLabels = flagutil.NewArray("external.label", "Optional label in the form 'Name=value' to add to all generated recording rules and alerts. "+ + externalLabels = flagutil.NewArrayString("external.label", "Optional label in the form 'Name=value' to add to all generated recording rules and alerts. "+ "Pass multiple -label flags in order to add multiple label sets.") remoteReadLookBack = flag.Duration("remoteRead.lookback", time.Hour, "Lookback defines how far to look into past for alerts timeseries."+ diff --git a/app/vmalert/notifier/init.go b/app/vmalert/notifier/init.go index ad20f4d812..716b1ebd68 100644 --- a/app/vmalert/notifier/init.go +++ b/app/vmalert/notifier/init.go @@ -17,32 +17,32 @@ var ( configPath = flag.String("notifier.config", "", "Path to configuration file for notifiers") suppressDuplicateTargetErrors = flag.Bool("notifier.suppressDuplicateTargetErrors", false, "Whether to suppress 'duplicate target' errors during discovery") - addrs = flagutil.NewArray("notifier.url", "Prometheus alertmanager URL, e.g. http://127.0.0.1:9093") + addrs = flagutil.NewArrayString("notifier.url", "Prometheus alertmanager URL, e.g. http://127.0.0.1:9093") - basicAuthUsername = flagutil.NewArray("notifier.basicAuth.username", "Optional basic auth username for -notifier.url") - basicAuthPassword = flagutil.NewArray("notifier.basicAuth.password", "Optional basic auth password for -notifier.url") - basicAuthPasswordFile = flagutil.NewArray("notifier.basicAuth.passwordFile", "Optional path to basic auth password file for -notifier.url") + basicAuthUsername = flagutil.NewArrayString("notifier.basicAuth.username", "Optional basic auth username for -notifier.url") + basicAuthPassword = flagutil.NewArrayString("notifier.basicAuth.password", "Optional basic auth password for -notifier.url") + basicAuthPasswordFile = flagutil.NewArrayString("notifier.basicAuth.passwordFile", "Optional path to basic auth password file for -notifier.url") - bearerToken = flagutil.NewArray("notifier.bearerToken", "Optional bearer token for -notifier.url") - bearerTokenFile = flagutil.NewArray("notifier.bearerTokenFile", "Optional path to bearer token file for -notifier.url") + bearerToken = flagutil.NewArrayString("notifier.bearerToken", "Optional bearer token for -notifier.url") + bearerTokenFile = flagutil.NewArrayString("notifier.bearerTokenFile", "Optional path to bearer token file for -notifier.url") tlsInsecureSkipVerify = flagutil.NewArrayBool("notifier.tlsInsecureSkipVerify", "Whether to skip tls verification when connecting to -notifier.url") - tlsCertFile = flagutil.NewArray("notifier.tlsCertFile", "Optional path to client-side TLS certificate file to use when connecting to -notifier.url") - tlsKeyFile = flagutil.NewArray("notifier.tlsKeyFile", "Optional path to client-side TLS certificate key to use when connecting to -notifier.url") - tlsCAFile = flagutil.NewArray("notifier.tlsCAFile", "Optional path to TLS CA file to use for verifying connections to -notifier.url. "+ + tlsCertFile = flagutil.NewArrayString("notifier.tlsCertFile", "Optional path to client-side TLS certificate file to use when connecting to -notifier.url") + tlsKeyFile = flagutil.NewArrayString("notifier.tlsKeyFile", "Optional path to client-side TLS certificate key to use when connecting to -notifier.url") + tlsCAFile = flagutil.NewArrayString("notifier.tlsCAFile", "Optional path to TLS CA file to use for verifying connections to -notifier.url. "+ "By default system CA is used") - tlsServerName = flagutil.NewArray("notifier.tlsServerName", "Optional TLS server name to use for connections to -notifier.url. "+ + tlsServerName = flagutil.NewArrayString("notifier.tlsServerName", "Optional TLS server name to use for connections to -notifier.url. "+ "By default the server name from -notifier.url is used") - oauth2ClientID = flagutil.NewArray("notifier.oauth2.clientID", "Optional OAuth2 clientID to use for -notifier.url. "+ + oauth2ClientID = flagutil.NewArrayString("notifier.oauth2.clientID", "Optional OAuth2 clientID to use for -notifier.url. "+ "If multiple args are set, then they are applied independently for the corresponding -notifier.url") - oauth2ClientSecret = flagutil.NewArray("notifier.oauth2.clientSecret", "Optional OAuth2 clientSecret to use for -notifier.url. "+ + oauth2ClientSecret = flagutil.NewArrayString("notifier.oauth2.clientSecret", "Optional OAuth2 clientSecret to use for -notifier.url. "+ "If multiple args are set, then they are applied independently for the corresponding -notifier.url") - oauth2ClientSecretFile = flagutil.NewArray("notifier.oauth2.clientSecretFile", "Optional OAuth2 clientSecretFile to use for -notifier.url. "+ + oauth2ClientSecretFile = flagutil.NewArrayString("notifier.oauth2.clientSecretFile", "Optional OAuth2 clientSecretFile to use for -notifier.url. "+ "If multiple args are set, then they are applied independently for the corresponding -notifier.url") - oauth2TokenURL = flagutil.NewArray("notifier.oauth2.tokenUrl", "Optional OAuth2 tokenURL to use for -notifier.url. "+ + oauth2TokenURL = flagutil.NewArrayString("notifier.oauth2.tokenUrl", "Optional OAuth2 tokenURL to use for -notifier.url. "+ "If multiple args are set, then they are applied independently for the corresponding -notifier.url") - oauth2Scopes = flagutil.NewArray("notifier.oauth2.scopes", "Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'. "+ + oauth2Scopes = flagutil.NewArrayString("notifier.oauth2.scopes", "Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'. "+ "If multiple args are set, then they are applied independently for the corresponding -notifier.url") ) diff --git a/lib/flagutil/array.go b/lib/flagutil/array.go index 245301b508..4dc4d4dc9e 100644 --- a/lib/flagutil/array.go +++ b/lib/flagutil/array.go @@ -8,11 +8,11 @@ import ( "time" ) -// NewArray returns new Array with the given name and description. -func NewArray(name, description string) *Array { +// NewArrayString returns new ArrayString with the given name and description. +func NewArrayString(name, description string) *ArrayString { description += "\nSupports an `array` of values separated by comma" + " or specified via multiple flags." - var a Array + var a ArrayString flag.Var(&a, name, description) return &a } @@ -36,7 +36,7 @@ func NewArrayBool(name, description string) *ArrayBool { } // NewArrayInt returns new ArrayInt with the given name and description. -func NewArrayInt(name string, description string) *ArrayInt { +func NewArrayInt(name, description string) *ArrayInt { description += "\nSupports `array` of values separated by comma" + " or specified via multiple flags." var a ArrayInt @@ -44,7 +44,16 @@ func NewArrayInt(name string, description string) *ArrayInt { return &a } -// Array is a flag that holds an array of values. +// NewArrayBytes returns new ArrayBytes with the given name and description. +func NewArrayBytes(name, description string) *ArrayBytes { + description += "\nSupports `array` of values separated by comma" + + " or specified via multiple flags." + var a ArrayBytes + flag.Var(&a, name, description) + return &a +} + +// ArrayString is a flag that holds an array of strings. // // It may be set either by specifying multiple flags with the given name // passed to NewArray or by joining flag values by comma. @@ -57,10 +66,10 @@ func NewArrayInt(name string, description string) *ArrayInt { // Flag values may be quoted. For instance, the following arg creates an array of ("a", "b, c") items: // // -foo='a,"b, c"' -type Array []string +type ArrayString []string // String implements flag.Value interface -func (a *Array) String() string { +func (a *ArrayString) String() string { aEscaped := make([]string, len(*a)) for i, v := range *a { if strings.ContainsAny(v, `", `+"\n") { @@ -72,7 +81,7 @@ func (a *Array) String() string { } // Set implements flag.Value interface -func (a *Array) Set(value string) error { +func (a *ArrayString) Set(value string) error { values := parseArrayValues(value) *a = append(*a, values...) return nil @@ -141,7 +150,7 @@ func getNextArrayValue(s string) (string, string) { } // GetOptionalArg returns optional arg under the given argIdx. -func (a *Array) GetOptionalArg(argIdx int) string { +func (a *ArrayString) GetOptionalArg(argIdx int) string { x := *a if argIdx >= len(x) { if len(x) == 1 { @@ -153,7 +162,8 @@ func (a *Array) GetOptionalArg(argIdx int) string { } // ArrayBool is a flag that holds an array of booleans values. -// have the same api as Array. +// +// Has the same api as ArrayString. type ArrayBool []bool // IsBoolFlag implements flag.IsBoolFlag interface @@ -194,7 +204,8 @@ func (a *ArrayBool) GetOptionalArg(argIdx int) bool { } // ArrayDuration is a flag that holds an array of time.Duration values. -// have the same api as Array. +// +// Has the same api as ArrayString. type ArrayDuration []time.Duration // String implements flag.Value interface @@ -233,6 +244,8 @@ 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 // String implements flag.Value interface @@ -259,7 +272,7 @@ func (a *ArrayInt) Set(value string) error { } // GetOptionalArgOrDefault returns optional arg under the given argIdx. -func (a *ArrayInt) GetOptionalArgOrDefault(argIdx int, defaultValue int) int { +func (a *ArrayInt) GetOptionalArgOrDefault(argIdx, defaultValue int) int { x := *a if argIdx < len(x) { return x[argIdx] @@ -269,3 +282,43 @@ func (a *ArrayInt) GetOptionalArgOrDefault(argIdx int, defaultValue int) int { } return defaultValue } + +// ArrayBytes is flag that holds an array of Bytes. +// +// Has the same api as ArrayString. +type ArrayBytes []*Bytes + +// String implements flag.Value interface +func (a *ArrayBytes) String() string { + x := *a + formattedBytes := make([]string, len(x)) + for i, v := range x { + formattedBytes[i] = v.String() + } + return strings.Join(formattedBytes, ",") +} + +// Set implemented flag.Value interface +func (a *ArrayBytes) Set(value string) error { + values := parseArrayValues(value) + for _, v := range values { + var b Bytes + if err := b.Set(v); err != nil { + return err + } + *a = append(*a, &b) + } + return nil +} + +// GetOptionalArgOrDefault returns optional arg under the given argIdx. +func (a *ArrayBytes) GetOptionalArgOrDefault(argIdx, defaultValue int) int { + x := *a + if argIdx < len(x) { + return x[argIdx].N + } + if len(x) == 1 { + return x[0].N + } + return defaultValue +} diff --git a/lib/flagutil/array_test.go b/lib/flagutil/array_test.go index b6675ebd8b..a4e8dad03c 100644 --- a/lib/flagutil/array_test.go +++ b/lib/flagutil/array_test.go @@ -9,21 +9,24 @@ import ( ) var ( - fooFlag Array + fooFlagString ArrayString fooFlagDuration ArrayDuration fooFlagBool ArrayBool fooFlagInt ArrayInt + fooFlagBytes ArrayBytes ) func init() { - os.Args = append(os.Args, "--fooFlag=foo", "--fooFlag=bar") - os.Args = append(os.Args, "--fooFlagDuration=10s", "--fooFlagDuration=5m") - os.Args = append(os.Args, "--fooFlagBool=true", "--fooFlagBool=false,true", "--fooFlagBool") - os.Args = append(os.Args, "--fooFlagInt=1", "--fooFlagInt=2,3") - flag.Var(&fooFlag, "fooFlag", "test") + os.Args = append(os.Args, "-fooFlagString=foo", "-fooFlagString=bar") + os.Args = append(os.Args, "-fooFlagDuration=10s", "-fooFlagDuration=5m") + os.Args = append(os.Args, "-fooFlagBool=true", "-fooFlagBool=false,true", "-fooFlagBool") + os.Args = append(os.Args, "-fooFlagInt=1", "-fooFlagInt=2,3") + os.Args = append(os.Args, "-fooFlagBytes=10MB", "-fooFlagBytes=23,10kib") + flag.Var(&fooFlagString, "fooFlagString", "test") flag.Var(&fooFlagDuration, "fooFlagDuration", "test") flag.Var(&fooFlagBool, "fooFlagBool", "test") flag.Var(&fooFlagInt, "fooFlagInt", "test") + flag.Var(&fooFlagBytes, "fooFlagBytes", "test") } func TestMain(m *testing.M) { @@ -31,25 +34,20 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestArray(t *testing.T) { - expected := []string{ +func TestArrayString(t *testing.T) { + expected := ArrayString{ "foo", "bar", } - if len(expected) != len(fooFlag) { - t.Errorf("len array flag (%d) is not equal to %d", len(fooFlag), len(expected)) - } - for i, v := range fooFlag { - if v != expected[i] { - t.Errorf("unexpected item in array %q", v) - } + if !reflect.DeepEqual(expected, fooFlagString) { + t.Fatalf("unexpected flag values; got\n%q\nwant\n%q", fooFlagString, expected) } } -func TestArraySet(t *testing.T) { +func TestArrayString_Set(t *testing.T) { f := func(s string, expectedValues []string) { t.Helper() - var a Array + 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) @@ -66,10 +64,10 @@ func TestArraySet(t *testing.T) { f(`,fo,"\"b, a'\\",,r,`, []string{``, `fo`, `"b, a'\`, ``, `r`, ``}) } -func TestArrayGetOptionalArg(t *testing.T) { +func TestArrayString_GetOptionalArg(t *testing.T) { f := func(s string, argIdx int, expectedValue string) { t.Helper() - var a Array + var a ArrayString _ = a.Set(s) v := a.GetOptionalArg(argIdx) if v != expectedValue { @@ -85,10 +83,10 @@ func TestArrayGetOptionalArg(t *testing.T) { f("foo,bar", 2, "") } -func TestArrayString(t *testing.T) { +func TestArrayString_String(t *testing.T) { f := func(s string) { t.Helper() - var a Array + var a ArrayString _ = a.Set(s) result := a.String() if result != s { @@ -105,21 +103,16 @@ func TestArrayString(t *testing.T) { } func TestArrayDuration(t *testing.T) { - expected := []time.Duration{ + expected := ArrayDuration{ time.Second * 10, time.Minute * 5, } - if len(expected) != len(fooFlagDuration) { - t.Errorf("len array flag (%d) is not equal to %d", len(fooFlag), len(expected)) - } - for i, v := range fooFlagDuration { - if v != expected[i] { - t.Errorf("unexpected item in array %s", v) - } + if !reflect.DeepEqual(expected, fooFlagDuration) { + t.Fatalf("unexpected flag values; got\n%s\nwant\n%s", fooFlagDuration, expected) } } -func TestArrayDurationSet(t *testing.T) { +func TestArrayDuration_Set(t *testing.T) { f := func(s string, expectedValues []time.Duration) { t.Helper() var a ArrayDuration @@ -133,8 +126,8 @@ func TestArrayDurationSet(t *testing.T) { f(`5m,1s,1h`, []time.Duration{time.Minute * 5, time.Second, time.Hour}) } -func TestArrayDurationGetOptionalArg(t *testing.T) { - f := func(s string, argIdx int, expectedValue, defaultValue time.Duration) { +func TestArrayDuration_GetOptionalArg(t *testing.T) { + f := func(s string, argIdx int, defaultValue, expectedValue time.Duration) { t.Helper() var a ArrayDuration _ = a.Set(s) @@ -146,10 +139,10 @@ func TestArrayDurationGetOptionalArg(t *testing.T) { f("", 0, time.Second, time.Second) f("", 1, time.Minute, time.Minute) f("10s,1m", 1, time.Minute, time.Minute) - f("10s", 3, time.Second*10, time.Minute) + f("10s", 3, time.Minute, time.Second*10) } -func TestArrayDurationString(t *testing.T) { +func TestArrayDuration_String(t *testing.T) { f := func(s string) { t.Helper() var a ArrayDuration @@ -165,20 +158,15 @@ func TestArrayDurationString(t *testing.T) { } func TestArrayBool(t *testing.T) { - expected := []bool{ + expected := ArrayBool{ true, false, true, true, } - if len(expected) != len(fooFlagBool) { - t.Errorf("len array flag (%d) is not equal to %d", len(fooFlag), len(expected)) - } - for i, v := range fooFlagBool { - if v != expected[i] { - t.Errorf("unexpected item in array index=%v,value=%v,want=%v", i, v, expected[i]) - } + if !reflect.DeepEqual(expected, fooFlagBool) { + t.Fatalf("unexpected flag values; got\n%v\nwant\n%v", fooFlagBool, expected) } } -func TestArrayBoolSet(t *testing.T) { +func TestArrayBool_Set(t *testing.T) { f := func(s string, expectedValues []bool) { t.Helper() var a ArrayBool @@ -192,7 +180,7 @@ func TestArrayBoolSet(t *testing.T) { f(`false,True,False`, []bool{false, true, false}) } -func TestArrayBoolGetOptionalArg(t *testing.T) { +func TestArrayBool_GetOptionalArg(t *testing.T) { f := func(s string, argIdx int, expectedValue bool) { t.Helper() var a ArrayBool @@ -208,7 +196,7 @@ func TestArrayBoolGetOptionalArg(t *testing.T) { f("true", 2, true) } -func TestArrayBoolString(t *testing.T) { +func TestArrayBool_String(t *testing.T) { f := func(s string) { t.Helper() var a ArrayBool @@ -225,18 +213,13 @@ func TestArrayBoolString(t *testing.T) { } func TestArrayInt(t *testing.T) { - expected := []int{1, 2, 3} - if len(expected) != len(fooFlagInt) { - t.Errorf("len array flag (%d) is not equal to %d", len(fooFlag), len(expected)) - } - for i, n := range fooFlagInt { - if n != expected[i] { - t.Errorf("unexpected item in array %d", n) - } + expected := ArrayInt{1, 2, 3} + if !reflect.DeepEqual(expected, fooFlagInt) { + t.Fatalf("unexpected flag values; got\n%d\nwant\n%d", fooFlagInt, expected) } } -func TestArrayIntSet(t *testing.T) { +func TestArrayInt_Set(t *testing.T) { f := func(s string, expectedValues []int) { t.Helper() var a ArrayInt @@ -250,8 +233,8 @@ func TestArrayIntSet(t *testing.T) { f(`-2,3,-64`, []int{-2, 3, -64}) } -func TestArrayIntGetOptionalArg(t *testing.T) { - f := func(s string, argIdx int, expectedValue, defaultValue int) { +func TestArrayInt_GetOptionalArg(t *testing.T) { + f := func(s string, argIdx, defaultValue, expectedValue int) { t.Helper() var a ArrayInt _ = a.Set(s) @@ -262,11 +245,11 @@ func TestArrayIntGetOptionalArg(t *testing.T) { } f("", 0, 123, 123) f("", 1, -34, -34) - f("10,1", 1, 1, 234) - f("10", 3, 10, -34) + f("10,1", 1, 234, 1) + f("10", 3, -34, 10) } -func TestArrayIntString(t *testing.T) { +func TestArrayInt_String(t *testing.T) { f := func(s string) { t.Helper() var a ArrayInt @@ -280,3 +263,64 @@ func TestArrayIntString(t *testing.T) { f("10,1") f("-5,1,123") } + +func TestArrayBytes(t *testing.T) { + expected := []int{10000000, 23, 10240} + result := make([]int, len(fooFlagBytes)) + for i, b := range fooFlagBytes { + result[i] = b.N + } + if !reflect.DeepEqual(expected, result) { + t.Fatalf("unexpected flag values; got\n%d\nwant\n%d", result, expected) + } +} + +func TestArrayBytes_Set(t *testing.T) { + f := func(s string, expectedValues []int) { + t.Helper() + var a ArrayBytes + _ = a.Set(s) + values := make([]int, len(a)) + for i, v := range a { + values[i] = v.N + } + if !reflect.DeepEqual(values, expectedValues) { + t.Fatalf("unexpected values parsed;\ngot\n%d\nwant\n%d", values, expectedValues) + } + } + f("", []int{}) + f(`1`, []int{1}) + f(`-2,3,10kb`, []int{-2, 3, 10000}) +} + +func TestArrayBytes_GetOptionalArg(t *testing.T) { + f := func(s string, argIdx, defaultValue, expectedValue int) { + t.Helper() + var a ArrayBytes + _ = a.Set(s) + v := a.GetOptionalArgOrDefault(argIdx, defaultValue) + if v != expectedValue { + t.Fatalf("unexpected value; got %d; want %d", v, expectedValue) + } + } + f("", 0, 123, 123) + f("", 1, -34, -34) + f("10,1", 1, 234, 1) + f("10,1", 3, 234, 234) + f("10Kb", 3, -34, 10000) +} + +func TestArrayBytes_String(t *testing.T) { + f := func(s string) { + t.Helper() + var a ArrayBytes + _ = a.Set(s) + result := a.String() + if result != s { + t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s) + } + } + f("") + f("10.5KiB,1") + f("-5,1,123MB") +} diff --git a/lib/httpserver/httpserver.go b/lib/httpserver/httpserver.go index 14ad40ba4d..32dd2d4573 100644 --- a/lib/httpserver/httpserver.go +++ b/lib/httpserver/httpserver.go @@ -34,7 +34,7 @@ var ( tlsEnable = flag.Bool("tls", false, "Whether to enable TLS for incoming HTTP requests at -httpListenAddr (aka https). -tlsCertFile and -tlsKeyFile must be set if -tls is set") tlsCertFile = flag.String("tlsCertFile", "", "Path to file with TLS certificate if -tls is set. Prefer ECDSA certs instead of RSA certs as RSA certs are slower. The provided certificate file is automatically re-read every second, so it can be dynamically updated") tlsKeyFile = flag.String("tlsKeyFile", "", "Path to file with TLS key if -tls is set. The provided key file is automatically re-read every second, so it can be dynamically updated") - tlsCipherSuites = flagutil.NewArray("tlsCipherSuites", "Optional list of TLS cipher suites for incoming requests over HTTPS if -tls is set. See the list of supported cipher suites at https://pkg.go.dev/crypto/tls#pkg-constants") + tlsCipherSuites = flagutil.NewArrayString("tlsCipherSuites", "Optional list of TLS cipher suites for incoming requests over HTTPS if -tls is set. See the list of supported cipher suites at https://pkg.go.dev/crypto/tls#pkg-constants") tlsMinVersion = flag.String("tlsMinVersion", "", "Optional minimum TLS version to use for incoming requests over HTTPS if -tls is set. "+ "Supported values: TLS10, TLS11, TLS12, TLS13") diff --git a/lib/influxutils/influxutils.go b/lib/influxutils/influxutils.go index 0350b01028..dc89e6560d 100644 --- a/lib/influxutils/influxutils.go +++ b/lib/influxutils/influxutils.go @@ -8,7 +8,7 @@ import ( "github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil" ) -var influxDatabaseNames = flagutil.NewArray("influx.databaseNames", "Comma-separated list of database names to return from /query and /influx/query API. "+ +var influxDatabaseNames = flagutil.NewArrayString("influx.databaseNames", "Comma-separated list of database names to return from /query and /influx/query API. "+ "This can be needed for accepting data from Telegraf plugins such as https://github.com/fangli/fluent-plugin-influxdb") // WriteDatabaseNames writes influxDatabaseNames to w. diff --git a/lib/pushmetrics/pushmetrics.go b/lib/pushmetrics/pushmetrics.go index 8b533db84d..c23bc62486 100644 --- a/lib/pushmetrics/pushmetrics.go +++ b/lib/pushmetrics/pushmetrics.go @@ -12,10 +12,10 @@ import ( ) var ( - pushURL = flagutil.NewArray("pushmetrics.url", "Optional URL to push metrics exposed at /metrics page. See https://docs.victoriametrics.com/#push-metrics . "+ + pushURL = flagutil.NewArrayString("pushmetrics.url", "Optional URL to push metrics exposed at /metrics page. See https://docs.victoriametrics.com/#push-metrics . "+ "By default metrics exposed at /metrics page aren't pushed to any remote storage") pushInterval = flag.Duration("pushmetrics.interval", 10*time.Second, "Interval for pushing metrics to -pushmetrics.url") - pushExtraLabel = flagutil.NewArray("pushmetrics.extraLabel", "Optional labels to add to metrics pushed to -pushmetrics.url . "+ + pushExtraLabel = flagutil.NewArrayString("pushmetrics.extraLabel", "Optional labels to add to metrics pushed to -pushmetrics.url . "+ `For example, -pushmetrics.extraLabel='instance="foo"' adds instance="foo" label to all the metrics pushed to -pushmetrics.url`) )