From 160cc9debdbd95f055fbdfde9605545b2182f7d1 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Wed, 20 Dec 2023 21:35:16 +0200 Subject: [PATCH] app/{vmagent,vmalert}: add the ability to set OAuth2 endpoint params via the corresponding *.oauth2.endpointParams command-line flags This is a follow-up for 5ebd5a0d7b12ac8c8ba6282ac5be16a193d5239d Updates https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5427 --- app/vmagent/remotewrite/client.go | 14 +++++--- app/vmalert/datasource/init.go | 18 ++++++---- app/vmalert/notifier/alertmanager.go | 2 +- app/vmalert/notifier/init.go | 8 +++++ app/vmalert/remoteread/init.go | 12 +++++-- app/vmalert/remotewrite/init.go | 18 ++++++---- app/vmalert/utils/auth.go | 3 +- docs/CHANGELOG.md | 7 +++- docs/vmagent.md | 4 +-- docs/vmalert.md | 21 ++++++++---- lib/flagutil/dict.go | 14 ++++++++ lib/flagutil/dict_test.go | 47 ++++++++++++++++++++++++++ lib/flagutil/map.go | 49 ---------------------------- 13 files changed, 138 insertions(+), 79 deletions(-) delete mode 100644 lib/flagutil/map.go diff --git a/app/vmagent/remotewrite/client.go b/app/vmagent/remotewrite/client.go index af7fe4d39e..370fa853ad 100644 --- a/app/vmagent/remotewrite/client.go +++ b/app/vmagent/remotewrite/client.go @@ -58,9 +58,10 @@ var ( 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") - oauth2EndpointParams = flagutil.NewMapString("remoteWrite.oauth2.endpointParams", "Optional OAuth2 endpoint parameters 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 ';'") + oauth2EndpointParams = flagutil.NewArrayString("remoteWrite.oauth2.endpointParams", "Optional OAuth2 endpoint parameters to use for the corresponding -remoteWrite.url . "+ + `The endpoint parameters must be set in JSON format: {"param1":"value1",...,"paramN":"valueN"}`) + 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") @@ -235,11 +236,16 @@ func getAuthConfig(argIdx int) (*promauth.Config, error) { clientSecret := oauth2ClientSecret.GetOptionalArg(argIdx) clientSecretFile := oauth2ClientSecretFile.GetOptionalArg(argIdx) if clientSecretFile != "" || clientSecret != "" { + endpointParamsJSON := oauth2EndpointParams.GetOptionalArg(argIdx) + endpointParams, err := flagutil.ParseJSONMap(endpointParamsJSON) + if err != nil { + return nil, fmt.Errorf("cannot parse JSON for -remoteWrite.oauth2.endpointParams=%s: %w", endpointParamsJSON, err) + } oauth2Cfg = &promauth.OAuth2Config{ ClientID: oauth2ClientID.GetOptionalArg(argIdx), ClientSecret: promauth.NewSecret(clientSecret), ClientSecretFile: clientSecretFile, - EndpointParams: *oauth2EndpointParams, + EndpointParams: endpointParams, TokenURL: oauth2TokenURL.GetOptionalArg(argIdx), Scopes: strings.Split(oauth2Scopes.GetOptionalArg(argIdx), ";"), } diff --git a/app/vmalert/datasource/init.go b/app/vmalert/datasource/init.go index faa015013b..9738999b3f 100644 --- a/app/vmalert/datasource/init.go +++ b/app/vmalert/datasource/init.go @@ -37,11 +37,13 @@ var ( tlsCAFile = flag.String("datasource.tlsCAFile", "", `Optional path to TLS CA file to use for verifying connections to -datasource.url. By default, system CA is used`) tlsServerName = flag.String("datasource.tlsServerName", "", `Optional TLS server name to use for connections to -datasource.url. By default, the server name from -datasource.url is used`) - oauth2ClientID = flag.String("datasource.oauth2.clientID", "", "Optional OAuth2 clientID to use for -datasource.url. ") - oauth2ClientSecret = flag.String("datasource.oauth2.clientSecret", "", "Optional OAuth2 clientSecret to use for -datasource.url.") - oauth2ClientSecretFile = flag.String("datasource.oauth2.clientSecretFile", "", "Optional OAuth2 clientSecretFile to use for -datasource.url. ") - oauth2TokenURL = flag.String("datasource.oauth2.tokenUrl", "", "Optional OAuth2 tokenURL to use for -datasource.url.") - oauth2Scopes = flag.String("datasource.oauth2.scopes", "", "Optional OAuth2 scopes to use for -datasource.url. Scopes must be delimited by ';'") + oauth2ClientID = flag.String("datasource.oauth2.clientID", "", "Optional OAuth2 clientID to use for -datasource.url") + oauth2ClientSecret = flag.String("datasource.oauth2.clientSecret", "", "Optional OAuth2 clientSecret to use for -datasource.url") + oauth2ClientSecretFile = flag.String("datasource.oauth2.clientSecretFile", "", "Optional OAuth2 clientSecretFile to use for -datasource.url") + oauth2EndpointParams = flag.String("datasource.oauth2.endpointParams", "", "Optional OAuth2 endpoint parameters to use for -datasource.url . "+ + `The endpoint parameters must be set in JSON format: {"param1":"value1",...,"paramN":"valueN"}`) + oauth2TokenURL = flag.String("datasource.oauth2.tokenUrl", "", "Optional OAuth2 tokenURL to use for -datasource.url") + oauth2Scopes = flag.String("datasource.oauth2.scopes", "", "Optional OAuth2 scopes to use for -datasource.url. Scopes must be delimited by ';'") lookBack = flag.Duration("datasource.lookback", 0, `Will be deprecated soon, please adjust "-search.latencyOffset" at datasource side `+ `or specify "latency_offset" in rule group's params. Lookback defines how far into the past to look when evaluating queries. `+ @@ -108,10 +110,14 @@ func Init(extraParams url.Values) (QuerierBuilder, error) { extraParams.Set("round_digits", fmt.Sprintf("%d", *roundDigits)) } + endpointParams, err := flagutil.ParseJSONMap(*oauth2EndpointParams) + if err != nil { + return nil, fmt.Errorf("cannot parse JSON for -datasource.oauth2.endpointParams=%s: %w", *oauth2EndpointParams, err) + } authCfg, err := utils.AuthConfig( utils.WithBasicAuth(*basicAuthUsername, *basicAuthPassword, *basicAuthPasswordFile), utils.WithBearer(*bearerToken, *bearerTokenFile), - utils.WithOAuth(*oauth2ClientID, *oauth2ClientSecret, *oauth2ClientSecretFile, *oauth2TokenURL, *oauth2Scopes), + utils.WithOAuth(*oauth2ClientID, *oauth2ClientSecret, *oauth2ClientSecretFile, *oauth2TokenURL, *oauth2Scopes, endpointParams), utils.WithHeaders(*headers)) if err != nil { return nil, fmt.Errorf("failed to configure auth: %w", err) diff --git a/app/vmalert/notifier/alertmanager.go b/app/vmalert/notifier/alertmanager.go index 3352bda25f..35764be3cb 100644 --- a/app/vmalert/notifier/alertmanager.go +++ b/app/vmalert/notifier/alertmanager.go @@ -144,7 +144,7 @@ func NewAlertManager(alertManagerURL string, fn AlertURLGenerator, authCfg proma aCfg, err := utils.AuthConfig( utils.WithBasicAuth(ba.Username, ba.Password.String(), ba.PasswordFile), utils.WithBearer(authCfg.BearerToken.String(), authCfg.BearerTokenFile), - utils.WithOAuth(oauth.ClientID, oauth.ClientSecretFile, oauth.ClientSecretFile, oauth.TokenURL, strings.Join(oauth.Scopes, ";"))) + utils.WithOAuth(oauth.ClientID, oauth.ClientSecretFile, oauth.ClientSecretFile, oauth.TokenURL, strings.Join(oauth.Scopes, ";"), oauth.EndpointParams)) if err != nil { return nil, fmt.Errorf("failed to configure auth: %w", err) } diff --git a/app/vmalert/notifier/init.go b/app/vmalert/notifier/init.go index 6972effb9c..6d29ef2381 100644 --- a/app/vmalert/notifier/init.go +++ b/app/vmalert/notifier/init.go @@ -46,6 +46,8 @@ var ( "If multiple args are set, then they are applied independently for the corresponding -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") + oauth2EndpointParams = flagutil.NewArrayString("notifier.oauth2.endpointParams", "Optional OAuth2 endpoint parameters to use for the corresponding -notifier.url . "+ + `The endpoint parameters must be set in JSON format: {"param1":"value1",...,"paramN":"valueN"}`) 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.NewArrayString("notifier.oauth2.scopes", "Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'. "+ @@ -141,6 +143,11 @@ func InitSecretFlags() { func notifiersFromFlags(gen AlertURLGenerator) ([]Notifier, error) { var notifiers []Notifier for i, addr := range *addrs { + endpointParamsJSON := oauth2EndpointParams.GetOptionalArg(i) + endpointParams, err := flagutil.ParseJSONMap(endpointParamsJSON) + if err != nil { + return nil, fmt.Errorf("cannot parse JSON for -notifier.oauth2.endpointParams=%s: %w", endpointParamsJSON, err) + } authCfg := promauth.HTTPClientConfig{ TLSConfig: &promauth.TLSConfig{ CAFile: tlsCAFile.GetOptionalArg(i), @@ -160,6 +167,7 @@ func notifiersFromFlags(gen AlertURLGenerator) ([]Notifier, error) { ClientID: oauth2ClientID.GetOptionalArg(i), ClientSecret: promauth.NewSecret(oauth2ClientSecret.GetOptionalArg(i)), ClientSecretFile: oauth2ClientSecretFile.GetOptionalArg(i), + EndpointParams: endpointParams, Scopes: strings.Split(oauth2Scopes.GetOptionalArg(i), ";"), TokenURL: oauth2TokenURL.GetOptionalArg(i), }, diff --git a/app/vmalert/remoteread/init.go b/app/vmalert/remoteread/init.go index 417462b7cb..10f0bcd1c5 100644 --- a/app/vmalert/remoteread/init.go +++ b/app/vmalert/remoteread/init.go @@ -41,8 +41,10 @@ var ( oauth2ClientID = flag.String("remoteRead.oauth2.clientID", "", "Optional OAuth2 clientID to use for -remoteRead.url.") oauth2ClientSecret = flag.String("remoteRead.oauth2.clientSecret", "", "Optional OAuth2 clientSecret to use for -remoteRead.url.") oauth2ClientSecretFile = flag.String("remoteRead.oauth2.clientSecretFile", "", "Optional OAuth2 clientSecretFile to use for -remoteRead.url.") - oauth2TokenURL = flag.String("remoteRead.oauth2.tokenUrl", "", "Optional OAuth2 tokenURL to use for -remoteRead.url. ") - oauth2Scopes = flag.String("remoteRead.oauth2.scopes", "", "Optional OAuth2 scopes to use for -remoteRead.url. Scopes must be delimited by ';'.") + oauth2EndpointParams = flag.String("remoteRead.oauth2.endpointParams", "", "Optional OAuth2 endpoint parameters to use for -remoteRead.url . "+ + `The endpoint parameters must be set in JSON format: {"param1":"value1",...,"paramN":"valueN"}`) + oauth2TokenURL = flag.String("remoteRead.oauth2.tokenUrl", "", "Optional OAuth2 tokenURL to use for -remoteRead.url. ") + oauth2Scopes = flag.String("remoteRead.oauth2.scopes", "", "Optional OAuth2 scopes to use for -remoteRead.url. Scopes must be delimited by ';'.") ) // InitSecretFlags must be called after flag.Parse and before any logging @@ -63,10 +65,14 @@ func Init() (datasource.QuerierBuilder, error) { return nil, fmt.Errorf("failed to create transport: %w", err) } + endpointParams, err := flagutil.ParseJSONMap(*oauth2EndpointParams) + if err != nil { + return nil, fmt.Errorf("cannot parse JSON for -remoteRead.oauth2.endpointParams=%s: %w", *oauth2EndpointParams, err) + } authCfg, err := utils.AuthConfig( utils.WithBasicAuth(*basicAuthUsername, *basicAuthPassword, *basicAuthPasswordFile), utils.WithBearer(*bearerToken, *bearerTokenFile), - utils.WithOAuth(*oauth2ClientID, *oauth2ClientSecret, *oauth2ClientSecretFile, *oauth2TokenURL, *oauth2Scopes), + utils.WithOAuth(*oauth2ClientID, *oauth2ClientSecret, *oauth2ClientSecretFile, *oauth2TokenURL, *oauth2Scopes, endpointParams), utils.WithHeaders(*headers)) if err != nil { return nil, fmt.Errorf("failed to configure auth: %w", err) diff --git a/app/vmalert/remotewrite/init.go b/app/vmalert/remotewrite/init.go index 7bc1f90d60..998d076e84 100644 --- a/app/vmalert/remotewrite/init.go +++ b/app/vmalert/remotewrite/init.go @@ -41,11 +41,13 @@ var ( tlsServerName = flag.String("remoteWrite.tlsServerName", "", "Optional TLS server name to use for connections to -remoteWrite.url. "+ "By default, the server name from -remoteWrite.url is used") - oauth2ClientID = flag.String("remoteWrite.oauth2.clientID", "", "Optional OAuth2 clientID to use for -remoteWrite.url.") - oauth2ClientSecret = flag.String("remoteWrite.oauth2.clientSecret", "", "Optional OAuth2 clientSecret to use for -remoteWrite.url.") - oauth2ClientSecretFile = flag.String("remoteWrite.oauth2.clientSecretFile", "", "Optional OAuth2 clientSecretFile to use for -remoteWrite.url.") - oauth2TokenURL = flag.String("remoteWrite.oauth2.tokenUrl", "", "Optional OAuth2 tokenURL to use for -notifier.url.") - oauth2Scopes = flag.String("remoteWrite.oauth2.scopes", "", "Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'.") + oauth2ClientID = flag.String("remoteWrite.oauth2.clientID", "", "Optional OAuth2 clientID to use for -remoteWrite.url") + oauth2ClientSecret = flag.String("remoteWrite.oauth2.clientSecret", "", "Optional OAuth2 clientSecret to use for -remoteWrite.url") + oauth2ClientSecretFile = flag.String("remoteWrite.oauth2.clientSecretFile", "", "Optional OAuth2 clientSecretFile to use for -remoteWrite.url") + oauth2EndpointParams = flag.String("remoteWrite.oauth2.endpointParams", "", "Optional OAuth2 endpoint parameters to use for -remoteWrite.url . "+ + `The endpoint parameters must be set in JSON format: {"param1":"value1",...,"paramN":"valueN"}`) + oauth2TokenURL = flag.String("remoteWrite.oauth2.tokenUrl", "", "Optional OAuth2 tokenURL to use for -notifier.url.") + oauth2Scopes = flag.String("remoteWrite.oauth2.scopes", "", "Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'.") ) // InitSecretFlags must be called after flag.Parse and before any logging @@ -67,10 +69,14 @@ func Init(ctx context.Context) (*Client, error) { return nil, fmt.Errorf("failed to create transport: %w", err) } + endpointParams, err := flagutil.ParseJSONMap(*oauth2EndpointParams) + if err != nil { + return nil, fmt.Errorf("cannot parse JSON for -remoteWrite.oauth2.endpointParams=%s: %w", *oauth2EndpointParams, err) + } authCfg, err := utils.AuthConfig( utils.WithBasicAuth(*basicAuthUsername, *basicAuthPassword, *basicAuthPasswordFile), utils.WithBearer(*bearerToken, *bearerTokenFile), - utils.WithOAuth(*oauth2ClientID, *oauth2ClientSecret, *oauth2ClientSecretFile, *oauth2TokenURL, *oauth2Scopes), + utils.WithOAuth(*oauth2ClientID, *oauth2ClientSecret, *oauth2ClientSecretFile, *oauth2TokenURL, *oauth2Scopes, endpointParams), utils.WithHeaders(*headers)) if err != nil { return nil, fmt.Errorf("failed to configure auth: %w", err) diff --git a/app/vmalert/utils/auth.go b/app/vmalert/utils/auth.go index 35db61fb31..ae30031e34 100644 --- a/app/vmalert/utils/auth.go +++ b/app/vmalert/utils/auth.go @@ -45,13 +45,14 @@ func WithBearer(token, tokenFile string) AuthConfigOptions { } // WithOAuth returns AuthConfigOptions and set OAuth params based on given params -func WithOAuth(clientID, clientSecret, clientSecretFile, tokenURL, scopes string) AuthConfigOptions { +func WithOAuth(clientID, clientSecret, clientSecretFile, tokenURL, scopes string, endpointParams map[string]string) AuthConfigOptions { return func(config *promauth.HTTPClientConfig) { if clientSecretFile != "" || clientSecret != "" { config.OAuth2 = &promauth.OAuth2Config{ ClientID: clientID, ClientSecret: promauth.NewSecret(clientSecret), ClientSecretFile: clientSecretFile, + EndpointParams: endpointParams, TokenURL: tokenURL, Scopes: strings.Split(scopes, ";"), } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ae4d4d6481..95d865a7cd 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -28,7 +28,12 @@ The sandbox cluster installation is running under the constant load generated by ## tip -* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): expose ability to set additional endpoint parameters when requesting an OAuth2 token via a the flag `remoteWrite.oauth2.endpointParams`. See [these docs](https://docs.victoriametrics.com/vmagent.html#advanced-usage). +* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): expose ability to set OAuth2 endpoint parameters per each `-remoteWrite.url` via the command-line flag `-remoteWrite.oauth2.endpointParams`. See [these docs](https://docs.victoriametrics.com/vmagent.html#advanced-usage). Thanks to @mhill-holoplot for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5427). +* FEATURE: [vmalert](https://docs.victoriametrics.com/vmagent.html): expose ability to set OAuth2 endpoint parameters via the following command-line flags: + - `-datasource.oauth2.endpointParams` for `-datasource.url` + - `-notifier.oauth2.endpointParams` for `-notifier.url` + - `-remoteRead.oauth2.endpointParams` for `-remoteRead.url` + - `-remoteWrite.oauth2.endpointParams` for `-remoteWrite.url` * FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth.html): add ability to proxy incoming requests to different backends based on the requested host via `src_hosts` option at `url_map`. See [these docs](https://docs.victoriametrics.com/vmauth.html#generic-http-proxy-for-different-backends). * FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): rename cmd-line flag `vm-native-disable-retries` to `vm-native-disable-per-metric-migration` to better reflect its meaning. * FEATURE: all VictoriaMetrics components: add ability to specify arbitrary HTTP headers to send with every request to `-pushmetrics.url`. See [`push metrics` docs](https://docs.victoriametrics.com/#push-metrics). diff --git a/docs/vmagent.md b/docs/vmagent.md index d7f3cc5316..d54e490992 100644 --- a/docs/vmagent.md +++ b/docs/vmagent.md @@ -1922,8 +1922,8 @@ See the docs at https://docs.victoriametrics.com/vmagent.html . Optional OAuth2 clientSecretFile to use for the corresponding -remoteWrite.url Supports an array of values separated by comma or specified via multiple flags. -remoteWrite.oauth2.endpointParams array - Optional OAuth2 endpointParams to use for the corresponding -remoteWrite.url. Keys and values must be seperated by ':'. - Supports and array of key:value pairs seperated by comma or specified via multiple flags. + Optional OAuth2 endpoint parameters to use for the corresponding -remoteWrite.url . The endpoint parameters must be set in JSON format: {"param1":"value1",...,"paramN":"valueN"} + Supports an array of values separated by comma or specified via multiple flags. -remoteWrite.oauth2.scopes array Optional OAuth2 scopes to use for the corresponding -remoteWrite.url. Scopes must be delimited by ';' Supports an array of values separated by comma or specified via multiple flags. diff --git a/docs/vmalert.md b/docs/vmalert.md index 6089218848..a427e6a0f3 100644 --- a/docs/vmalert.md +++ b/docs/vmalert.md @@ -1003,11 +1003,13 @@ The shortlist of configuration flags is the following: -datasource.maxIdleConnections int Defines the number of idle (keep-alive connections) to each configured datasource. Consider setting this value equal to the value: groups_total * group.concurrency. Too low a value may result in a high number of sockets in TIME_WAIT state. (default 100) -datasource.oauth2.clientID string - Optional OAuth2 clientID to use for -datasource.url. + Optional OAuth2 clientID to use for -datasource.url -datasource.oauth2.clientSecret string - Optional OAuth2 clientSecret to use for -datasource.url. + Optional OAuth2 clientSecret to use for -datasource.url -datasource.oauth2.clientSecretFile string - Optional OAuth2 clientSecretFile to use for -datasource.url. + Optional OAuth2 clientSecretFile to use for -datasource.url + -datasource.oauth2.endpointParams string + Optional OAuth2 endpoint parameters to use for -datasource.url . The endpoint parameters must be set in JSON format: {"param1":"value1",...,"paramN":"valueN"} -datasource.oauth2.scopes string Optional OAuth2 scopes to use for -datasource.url. Scopes must be delimited by ';' -datasource.oauth2.tokenUrl string @@ -1156,6 +1158,9 @@ The shortlist of configuration flags is the following: -notifier.oauth2.clientSecretFile array Optional OAuth2 clientSecretFile to use for -notifier.url. If multiple args are set, then they are applied independently for the corresponding -notifier.url Supports an array of values separated by comma or specified via multiple flags. + -notifier.oauth2.endpointParams array + Optional OAuth2 endpoint parameters to use for the corresponding -notifier.url . The endpoint parameters must be set in JSON format: {"param1":"value1",...,"paramN":"valueN"} + Supports an array of values separated by comma or specified via multiple flags. -notifier.oauth2.scopes array 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 Supports an array of values separated by comma or specified via multiple flags. @@ -1233,6 +1238,8 @@ The shortlist of configuration flags is the following: Optional OAuth2 clientSecret to use for -remoteRead.url. -remoteRead.oauth2.clientSecretFile string Optional OAuth2 clientSecretFile to use for -remoteRead.url. + -remoteRead.oauth2.endpointParams string + Optional OAuth2 endpoint parameters to use for -remoteRead.url . The endpoint parameters must be set in JSON format: {"param1":"value1",...,"paramN":"valueN"} -remoteRead.oauth2.scopes string Optional OAuth2 scopes to use for -remoteRead.url. Scopes must be delimited by ';'. -remoteRead.oauth2.tokenUrl string @@ -1274,11 +1281,13 @@ The shortlist of configuration flags is the following: -remoteWrite.maxQueueSize int Defines the max number of pending datapoints to remote write endpoint (default 100000) -remoteWrite.oauth2.clientID string - Optional OAuth2 clientID to use for -remoteWrite.url. + Optional OAuth2 clientID to use for -remoteWrite.url -remoteWrite.oauth2.clientSecret string - Optional OAuth2 clientSecret to use for -remoteWrite.url. + Optional OAuth2 clientSecret to use for -remoteWrite.url -remoteWrite.oauth2.clientSecretFile string - Optional OAuth2 clientSecretFile to use for -remoteWrite.url. + Optional OAuth2 clientSecretFile to use for -remoteWrite.url + -remoteWrite.oauth2.endpointParams string + Optional OAuth2 endpoint parameters to use for -remoteWrite.url . The endpoint parameters must be set in JSON format: {"param1":"value1",...,"paramN":"valueN"} -remoteWrite.oauth2.scopes string Optional OAuth2 scopes to use for -notifier.url. Scopes must be delimited by ';'. -remoteWrite.oauth2.tokenUrl string diff --git a/lib/flagutil/dict.go b/lib/flagutil/dict.go index eceade66d9..a7ae573939 100644 --- a/lib/flagutil/dict.go +++ b/lib/flagutil/dict.go @@ -1,6 +1,7 @@ package flagutil import ( + "encoding/json" "flag" "fmt" "strconv" @@ -98,3 +99,16 @@ func (di *DictInt) Get(key string) int { } return di.defaultValue } + +// ParseJSONMap parses s, which must contain JSON map of {"k1":"v1",...,"kN":"vN"} +func ParseJSONMap(s string) (map[string]string, error) { + if s == "" { + // Special case + return nil, nil + } + var m map[string]string + if err := json.Unmarshal([]byte(s), &m); err != nil { + return nil, err + } + return m, nil +} diff --git a/lib/flagutil/dict_test.go b/lib/flagutil/dict_test.go index 31e326ab43..ef66e5ae13 100644 --- a/lib/flagutil/dict_test.go +++ b/lib/flagutil/dict_test.go @@ -1,9 +1,56 @@ package flagutil import ( + "encoding/json" "testing" ) +func TestParseJSONMapSuccess(t *testing.T) { + f := func(s string) { + t.Helper() + m, err := ParseJSONMap(s) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if s == "" && m == nil { + return + } + data, err := json.Marshal(m) + if err != nil { + t.Fatalf("cannot marshal m: %s", err) + } + if s != string(data) { + t.Fatalf("unexpected result; got %s; want %s", data, s) + } + } + + f("") + f("{}") + f(`{"foo":"bar"}`) + f(`{"a":"b","c":"d"}`) +} + +func TestParseJSONMapFailure(t *testing.T) { + f := func(s string) { + t.Helper() + m, err := ParseJSONMap(s) + if err == nil { + t.Fatalf("expecting non-nil error") + } + if m != nil { + t.Fatalf("expecting nil m") + } + } + + f("foo") + f("123") + f("{") + f(`{foo:bar}`) + f(`{"foo":1}`) + f(`[]`) + f(`{"foo":"bar","a":[123]}`) +} + func TestDictIntSetSuccess(t *testing.T) { f := func(s string) { t.Helper() diff --git a/lib/flagutil/map.go b/lib/flagutil/map.go deleted file mode 100644 index 2ba97f88dc..0000000000 --- a/lib/flagutil/map.go +++ /dev/null @@ -1,49 +0,0 @@ -package flagutil - -import ( - "flag" - "fmt" - "strings" -) - -type MapString map[string]string - -// String returns a string representation of the map. -func (m *MapString) String() string { - if m == nil { - return "" - } - return fmt.Sprintf("%v", *m) -} - -// Set parses the given value into a map. -func (m *MapString) Set(value string) error { - if *m == nil { - *m = make(map[string]string) - } - for _, pair := range parseArrayValues(value) { - key, value, err := parseMapValue(pair) - if err != nil { - return err - } - (*m)[key] = value - } - return nil -} - -func parseMapValue(s string) (string, string, error) { - kv := strings.SplitN(s, ":", 2) - if len(kv) != 2 { - return "", "", fmt.Errorf("invalid map value '%s' values must be 'key:value'", s) - } - - return kv[0], kv[1], nil -} - -// NewMapString returns a new MapString with the given name and description. -func NewMapString(name, description string) *MapString { - description += fmt.Sprintf("\nSupports multiple flags with the following syntax: -%s=key:value", name) - var m MapString - flag.Var(&m, name, description) - return &m -}