From 855436efd290a7319b4c4551552009de33475326 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Mon, 4 Jul 2022 14:27:48 +0300 Subject: [PATCH] lib/promauth: refactor NewConfig in order to improve maintainability 1. Split NewConfig into smaller functions 2. Introduce Options struct for simplifying construction of the Config with various options This commit is based on https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2684 --- app/vmagent/remotewrite/client.go | 10 +- lib/promauth/config.go | 499 +++++++++++++-------- lib/promauth/config_test.go | 57 ++- lib/promscrape/config_test.go | 10 +- lib/promscrape/discovery/kubernetes/api.go | 20 +- lib/promscrape/discovery/openstack/api.go | 6 +- 6 files changed, 369 insertions(+), 233 deletions(-) diff --git a/app/vmagent/remotewrite/client.go b/app/vmagent/remotewrite/client.go index c466feea4..bd1be5d76 100644 --- a/app/vmagent/remotewrite/client.go +++ b/app/vmagent/remotewrite/client.go @@ -212,7 +212,15 @@ func getAuthConfig(argIdx int) (*promauth.Config, error) { InsecureSkipVerify: tlsInsecureSkipVerify.GetOptionalArg(argIdx), } - authCfg, err := promauth.NewConfig(".", nil, basicAuthCfg, token, tokenFile, oauth2Cfg, tlsCfg, hdrs) + opts := &promauth.Options{ + BasicAuth: basicAuthCfg, + BearerToken: token, + BearerTokenFile: tokenFile, + OAuth2: oauth2Cfg, + TLSConfig: tlsCfg, + Headers: hdrs, + } + authCfg, err := opts.NewConfig() if err != nil { return nil, fmt.Errorf("cannot populate OAuth2 config for remoteWrite idx: %d, err: %w", argIdx, err) } diff --git a/lib/promauth/config.go b/lib/promauth/config.go index 62a0efaf2..f5f5e8baa 100644 --- a/lib/promauth/config.go +++ b/lib/promauth/config.go @@ -81,16 +81,16 @@ type TLSConfig struct { MinVersion string `yaml:"min_version,omitempty"` } -// String returns human-readable representation of tlsConfig -func (tlsConfig *TLSConfig) String() string { - if tlsConfig == nil { +// String returns human-readable representation of tc +func (tc *TLSConfig) String() string { + if tc == nil { return "" } - caHash := xxhash.Sum64(tlsConfig.CA) - certHash := xxhash.Sum64(tlsConfig.Cert) - keyHash := xxhash.Sum64(tlsConfig.Key) + caHash := xxhash.Sum64(tc.CA) + certHash := xxhash.Sum64(tc.Cert) + keyHash := xxhash.Sum64(tc.Key) return fmt.Sprintf("hash(ca)=%d, ca_file=%q, hash(cert)=%d, cert_file=%q, hash(key)=%d, key_file=%q, server_name=%q, insecure_skip_verify=%v, min_version=%q", - caHash, tlsConfig.CAFile, certHash, tlsConfig.CertFile, keyHash, tlsConfig.KeyFile, tlsConfig.ServerName, tlsConfig.InsecureSkipVerify, tlsConfig.MinVersion) + caHash, tc.CAFile, certHash, tc.CertFile, keyHash, tc.KeyFile, tc.ServerName, tc.InsecureSkipVerify, tc.MinVersion) } // Authorization represents generic authorization config. @@ -198,7 +198,11 @@ func newOAuth2ConfigInternal(baseDir string, o *OAuth2Config) (*oauth2ConfigInte } oi.cfg.ClientSecret = secret } - ac, err := o.NewConfig(baseDir) + opts := &Options{ + BaseDir: baseDir, + TLSConfig: o.TLSConfig, + } + ac, err := opts.NewConfig() if err != nil { return nil, fmt.Errorf("cannot initialize TLS config for OAuth2: %w", err) } @@ -400,220 +404,325 @@ func (ac *Config) NewTLSConfig() *tls.Config { // NewConfig creates auth config for the given hcc. func (hcc *HTTPClientConfig) NewConfig(baseDir string) (*Config, error) { - return NewConfig(baseDir, hcc.Authorization, hcc.BasicAuth, hcc.BearerToken.String(), hcc.BearerTokenFile, hcc.OAuth2, hcc.TLSConfig, hcc.Headers) + opts := &Options{ + BaseDir: baseDir, + Authorization: hcc.Authorization, + BasicAuth: hcc.BasicAuth, + BearerToken: hcc.BearerToken.String(), + BearerTokenFile: hcc.BearerTokenFile, + OAuth2: hcc.OAuth2, + TLSConfig: hcc.TLSConfig, + Headers: hcc.Headers, + } + return opts.NewConfig() } // NewConfig creates auth config for the given pcc. func (pcc *ProxyClientConfig) NewConfig(baseDir string) (*Config, error) { - return NewConfig(baseDir, pcc.Authorization, pcc.BasicAuth, pcc.BearerToken.String(), pcc.BearerTokenFile, pcc.OAuth2, pcc.TLSConfig, pcc.Headers) -} - -// NewConfig creates auth config for the given o. -func (o *OAuth2Config) NewConfig(baseDir string) (*Config, error) { - return NewConfig(baseDir, nil, nil, "", "", nil, o.TLSConfig, nil) + opts := &Options{ + BaseDir: baseDir, + Authorization: pcc.Authorization, + BasicAuth: pcc.BasicAuth, + BearerToken: pcc.BearerToken.String(), + BearerTokenFile: pcc.BearerTokenFile, + OAuth2: pcc.OAuth2, + TLSConfig: pcc.TLSConfig, + Headers: pcc.Headers, + } + return opts.NewConfig() } // NewConfig creates auth config for the given ba. func (ba *BasicAuthConfig) NewConfig(baseDir string) (*Config, error) { - return NewConfig(baseDir, nil, ba, "", "", nil, nil, nil) + opts := &Options{ + BaseDir: baseDir, + BasicAuth: ba, + } + return opts.NewConfig() } -// NewConfig creates auth config from the given args. -// -// headers must be in the form 'HeaderName: header value' -func NewConfig(baseDir string, az *Authorization, basicAuth *BasicAuthConfig, bearerToken, bearerTokenFile string, o *OAuth2Config, tlsConfig *TLSConfig, headers []string) (*Config, error) { - var getAuthHeader func() string - authDigest := "" - if az != nil { - azType := "Bearer" - if az.Type != "" { - azType = az.Type - } - if az.CredentialsFile != "" { - if az.Credentials != nil { - return nil, fmt.Errorf("both `credentials`=%q and `credentials_file`=%q are set", az.Credentials, az.CredentialsFile) - } - filePath := fs.GetFilepath(baseDir, az.CredentialsFile) - getAuthHeader = func() string { - token, err := readPasswordFromFile(filePath) - if err != nil { - logger.Errorf("cannot read credentials from `credentials_file`=%q: %s", az.CredentialsFile, err) - return "" - } - return azType + " " + token - } - authDigest = fmt.Sprintf("custom(type=%q, credsFile=%q)", az.Type, filePath) - } else { - getAuthHeader = func() string { - return azType + " " + az.Credentials.String() - } - authDigest = fmt.Sprintf("custom(type=%q, creds=%q)", az.Type, az.Credentials) - } +// Options contain options, which must be passed to NewConfig. +type Options struct { + // BaseDir is an optional path to a base directory for resolving + // relative filepaths in various config options. + // + // It is set to the current directory by default. + BaseDir string + + // Authorization contains optional Authorization. + Authorization *Authorization + + // BasicAuth contains optional BasicAuthConfig. + BasicAuth *BasicAuthConfig + + // BearerToken contains optional bearer token. + BearerToken string + + // BearerTokenFile contains optional path to a file with bearer token. + BearerTokenFile string + + // OAuth2 contains optional OAuth2Config. + OAuth2 *OAuth2Config + + // TLSconfig contains optional TLSConfig. + TLSConfig *TLSConfig + + // Headers contains optional http request headers in the form 'Foo: bar'. + Headers []string +} + +// NewConfig creates auth config from the given opts. +func (opts *Options) NewConfig() (*Config, error) { + baseDir := opts.BaseDir + if baseDir == "" { + baseDir = "." } - if basicAuth != nil { - if getAuthHeader != nil { - return nil, fmt.Errorf("cannot use both `authorization` and `basic_auth`") - } - if basicAuth.Username == "" { - return nil, fmt.Errorf("missing `username` in `basic_auth` section") - } - if basicAuth.PasswordFile != "" { - if basicAuth.Password != nil { - return nil, fmt.Errorf("both `password`=%q and `password_file`=%q are set in `basic_auth` section", basicAuth.Password, basicAuth.PasswordFile) - } - filePath := fs.GetFilepath(baseDir, basicAuth.PasswordFile) - getAuthHeader = func() string { - password, err := readPasswordFromFile(filePath) - if err != nil { - logger.Errorf("cannot read password from `password_file`=%q set in `basic_auth` section: %s", basicAuth.PasswordFile, err) - return "" - } - // See https://en.wikipedia.org/wiki/Basic_access_authentication - token := basicAuth.Username + ":" + password - token64 := base64.StdEncoding.EncodeToString([]byte(token)) - return "Basic " + token64 - } - authDigest = fmt.Sprintf("basic(username=%q, passwordFile=%q)", basicAuth.Username, filePath) - } else { - getAuthHeader = func() string { - // See https://en.wikipedia.org/wiki/Basic_access_authentication - token := basicAuth.Username + ":" + basicAuth.Password.String() - token64 := base64.StdEncoding.EncodeToString([]byte(token)) - return "Basic " + token64 - } - authDigest = fmt.Sprintf("basic(username=%q, password=%q)", basicAuth.Username, basicAuth.Password) - } - } - if bearerTokenFile != "" { - if getAuthHeader != nil { - return nil, fmt.Errorf("cannot simultaneously use `authorization`, `basic_auth` and `bearer_token_file`") - } - if bearerToken != "" { - return nil, fmt.Errorf("both `bearer_token`=%q and `bearer_token_file`=%q are set", bearerToken, bearerTokenFile) - } - filePath := fs.GetFilepath(baseDir, bearerTokenFile) - getAuthHeader = func() string { - token, err := readPasswordFromFile(filePath) - if err != nil { - logger.Errorf("cannot read bearer token from `bearer_token_file`=%q: %s", bearerTokenFile, err) - return "" - } - return "Bearer " + token - } - authDigest = fmt.Sprintf("bearer(tokenFile=%q)", filePath) - } - if bearerToken != "" { - if getAuthHeader != nil { - return nil, fmt.Errorf("cannot simultaneously use `authorization`, `basic_auth` and `bearer_token`") - } - getAuthHeader = func() string { - return "Bearer " + bearerToken - } - authDigest = fmt.Sprintf("bearer(token=%q)", bearerToken) - } - if o != nil { - if getAuthHeader != nil { - return nil, fmt.Errorf("cannot simultaneously use `authorization`, `basic_auth, `bearer_token` and `ouath2`") - } - oi, err := newOAuth2ConfigInternal(baseDir, o) - if err != nil { + var actx authContext + if opts.Authorization != nil { + if err := actx.initFromAuthorization(baseDir, opts.Authorization); err != nil { return nil, err } - getAuthHeader = func() string { - ts, err := oi.getTokenSource() - if err != nil { - logger.Errorf("cannot get OAuth2 tokenSource: %s", err) - return "" - } - t, err := ts.Token() - if err != nil { - logger.Errorf("cannot get OAuth2 token: %s", err) - return "" - } - return t.Type() + " " + t.AccessToken - } - authDigest = fmt.Sprintf("oauth2(%s)", o.String()) } - var tlsRootCA *x509.CertPool - var getTLSCert func(*tls.CertificateRequestInfo) (*tls.Certificate, error) - tlsCertDigest := "" - tlsServerName := "" - tlsInsecureSkipVerify := false - tlsMinVersion := uint16(0) - if tlsConfig != nil { - tlsServerName = tlsConfig.ServerName - tlsInsecureSkipVerify = tlsConfig.InsecureSkipVerify - if len(tlsConfig.Key) != 0 || len(tlsConfig.Cert) != 0 { - cert, err := tls.X509KeyPair(tlsConfig.Cert, tlsConfig.Key) - if err != nil { - return nil, fmt.Errorf("cannot load TLS certificate from the provided `cert` and `key` values: %w", err) - } - getTLSCert = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { - return &cert, nil - } - h := xxhash.Sum64(tlsConfig.Key) ^ xxhash.Sum64(tlsConfig.Cert) - tlsCertDigest = fmt.Sprintf("digest(key+cert)=%d", h) - } else if tlsConfig.CertFile != "" || tlsConfig.KeyFile != "" { - getTLSCert = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { - // Re-read TLS certificate from disk. This is needed for https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1420 - certPath := fs.GetFilepath(baseDir, tlsConfig.CertFile) - keyPath := fs.GetFilepath(baseDir, tlsConfig.KeyFile) - cert, err := tls.LoadX509KeyPair(certPath, keyPath) - if err != nil { - return nil, fmt.Errorf("cannot load TLS certificate from `cert_file`=%q, `key_file`=%q: %w", tlsConfig.CertFile, tlsConfig.KeyFile, err) - } - return &cert, nil - } - // Check whether the configured TLS cert can be loaded. - if _, err := getTLSCert(nil); err != nil { - return nil, err - } - tlsCertDigest = fmt.Sprintf("certFile=%q, keyFile=%q", tlsConfig.CertFile, tlsConfig.KeyFile) + if opts.BasicAuth != nil { + if actx.getAuthHeader != nil { + return nil, fmt.Errorf("cannot use both `authorization` and `basic_auth`") } - if len(tlsConfig.CA) != 0 { - tlsRootCA = x509.NewCertPool() - if !tlsRootCA.AppendCertsFromPEM(tlsConfig.CA) { - return nil, fmt.Errorf("cannot parse data from `ca` value") - } - } else if tlsConfig.CAFile != "" { - path := fs.GetFilepath(baseDir, tlsConfig.CAFile) - data, err := fs.ReadFileOrHTTP(path) - if err != nil { - return nil, fmt.Errorf("cannot read `ca_file` %q: %w", tlsConfig.CAFile, err) - } - tlsRootCA = x509.NewCertPool() - if !tlsRootCA.AppendCertsFromPEM(data) { - return nil, fmt.Errorf("cannot parse data from `ca_file` %q", tlsConfig.CAFile) - } - } - if tlsConfig.MinVersion != "" { - v, err := parseTLSVersion(tlsConfig.MinVersion) - if err != nil { - return nil, fmt.Errorf("cannot parse `min_version`: %w", err) - } - tlsMinVersion = v + if err := actx.initFromBasicAuthConfig(baseDir, opts.BasicAuth); err != nil { + return nil, err } } - parsedHeaders, err := parseHeaders(headers) + if opts.BearerTokenFile != "" { + if actx.getAuthHeader != nil { + return nil, fmt.Errorf("cannot simultaneously use `authorization`, `basic_auth` and `bearer_token_file`") + } + if opts.BearerToken != "" { + return nil, fmt.Errorf("both `bearer_token`=%q and `bearer_token_file`=%q are set", opts.BearerToken, opts.BearerTokenFile) + } + if err := actx.initFromBearerTokenFile(baseDir, opts.BearerTokenFile); err != nil { + return nil, err + } + } + if opts.BearerToken != "" { + if actx.getAuthHeader != nil { + return nil, fmt.Errorf("cannot simultaneously use `authorization`, `basic_auth` and `bearer_token`") + } + if err := actx.initFromBearerToken(opts.BearerToken); err != nil { + return nil, err + } + } + if opts.OAuth2 != nil { + if actx.getAuthHeader != nil { + return nil, fmt.Errorf("cannot simultaneously use `authorization`, `basic_auth, `bearer_token` and `ouath2`") + } + if err := actx.initFromOAuth2Config(baseDir, opts.OAuth2); err != nil { + return nil, err + } + } + var tctx tlsContext + if opts.TLSConfig != nil { + if err := tctx.initFromTLSConfig(baseDir, opts.TLSConfig); err != nil { + return nil, err + } + } + headers, err := parseHeaders(opts.Headers) if err != nil { return nil, err } ac := &Config{ - TLSRootCA: tlsRootCA, - TLSServerName: tlsServerName, - TLSInsecureSkipVerify: tlsInsecureSkipVerify, - TLSMinVersion: tlsMinVersion, + TLSRootCA: tctx.rootCA, + TLSServerName: tctx.serverName, + TLSInsecureSkipVerify: tctx.insecureSkipVerify, + TLSMinVersion: tctx.minVersion, - getTLSCert: getTLSCert, - tlsCertDigest: tlsCertDigest, + getTLSCert: tctx.getTLSCert, + tlsCertDigest: tctx.tlsCertDigest, - getAuthHeader: getAuthHeader, - headers: parsedHeaders, - authDigest: authDigest, + getAuthHeader: actx.getAuthHeader, + headers: headers, + authDigest: actx.authDigest, } return ac, nil } +type authContext struct { + // getAuthHeader must return for 'Authorization: ' http request header + getAuthHeader func() string + + // authDigest must contain the digest for the used authorization + // The digest must be changed whenever the original config changes. + authDigest string +} + +func (actx *authContext) initFromAuthorization(baseDir string, az *Authorization) error { + azType := "Bearer" + if az.Type != "" { + azType = az.Type + } + if az.CredentialsFile == "" { + actx.getAuthHeader = func() string { + return azType + " " + az.Credentials.String() + } + actx.authDigest = fmt.Sprintf("custom(type=%q, creds=%q)", az.Type, az.Credentials) + return nil + } + if az.Credentials != nil { + return fmt.Errorf("both `credentials`=%q and `credentials_file`=%q are set", az.Credentials, az.CredentialsFile) + } + filePath := fs.GetFilepath(baseDir, az.CredentialsFile) + actx.getAuthHeader = func() string { + token, err := readPasswordFromFile(filePath) + if err != nil { + logger.Errorf("cannot read credentials from `credentials_file`=%q: %s", az.CredentialsFile, err) + return "" + } + return azType + " " + token + } + actx.authDigest = fmt.Sprintf("custom(type=%q, credsFile=%q)", az.Type, filePath) + return nil +} + +func (actx *authContext) initFromBasicAuthConfig(baseDir string, ba *BasicAuthConfig) error { + if ba.Username == "" { + return fmt.Errorf("missing `username` in `basic_auth` section") + } + if ba.PasswordFile == "" { + actx.getAuthHeader = func() string { + // See https://en.wikipedia.org/wiki/Basic_access_authentication + token := ba.Username + ":" + ba.Password.String() + token64 := base64.StdEncoding.EncodeToString([]byte(token)) + return "Basic " + token64 + } + actx.authDigest = fmt.Sprintf("basic(username=%q, password=%q)", ba.Username, ba.Password) + return nil + } + if ba.Password != nil { + return fmt.Errorf("both `password`=%q and `password_file`=%q are set in `basic_auth` section", ba.Password, ba.PasswordFile) + } + filePath := fs.GetFilepath(baseDir, ba.PasswordFile) + actx.getAuthHeader = func() string { + password, err := readPasswordFromFile(filePath) + if err != nil { + logger.Errorf("cannot read password from `password_file`=%q set in `basic_auth` section: %s", ba.PasswordFile, err) + return "" + } + // See https://en.wikipedia.org/wiki/Basic_access_authentication + token := ba.Username + ":" + password + token64 := base64.StdEncoding.EncodeToString([]byte(token)) + return "Basic " + token64 + } + actx.authDigest = fmt.Sprintf("basic(username=%q, passwordFile=%q)", ba.Username, filePath) + return nil +} + +func (actx *authContext) initFromBearerTokenFile(baseDir string, bearerTokenFile string) error { + filePath := fs.GetFilepath(baseDir, bearerTokenFile) + actx.getAuthHeader = func() string { + token, err := readPasswordFromFile(filePath) + if err != nil { + logger.Errorf("cannot read bearer token from `bearer_token_file`=%q: %s", bearerTokenFile, err) + return "" + } + return "Bearer " + token + } + actx.authDigest = fmt.Sprintf("bearer(tokenFile=%q)", filePath) + return nil +} + +func (actx *authContext) initFromBearerToken(bearerToken string) error { + actx.getAuthHeader = func() string { + return "Bearer " + bearerToken + } + actx.authDigest = fmt.Sprintf("bearer(token=%q)", bearerToken) + return nil +} + +func (actx *authContext) initFromOAuth2Config(baseDir string, o *OAuth2Config) error { + oi, err := newOAuth2ConfigInternal(baseDir, o) + if err != nil { + return err + } + actx.getAuthHeader = func() string { + ts, err := oi.getTokenSource() + if err != nil { + logger.Errorf("cannot get OAuth2 tokenSource: %s", err) + return "" + } + t, err := ts.Token() + if err != nil { + logger.Errorf("cannot get OAuth2 token: %s", err) + return "" + } + return t.Type() + " " + t.AccessToken + } + actx.authDigest = fmt.Sprintf("oauth2(%s)", o.String()) + return nil +} + +type tlsContext struct { + getTLSCert func(*tls.CertificateRequestInfo) (*tls.Certificate, error) + tlsCertDigest string + rootCA *x509.CertPool + serverName string + insecureSkipVerify bool + minVersion uint16 +} + +func (tctx *tlsContext) initFromTLSConfig(baseDir string, tc *TLSConfig) error { + tctx.serverName = tc.ServerName + tctx.insecureSkipVerify = tc.InsecureSkipVerify + if len(tc.Key) != 0 || len(tc.Cert) != 0 { + cert, err := tls.X509KeyPair(tc.Cert, tc.Key) + if err != nil { + return fmt.Errorf("cannot load TLS certificate from the provided `cert` and `key` values: %w", err) + } + tctx.getTLSCert = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + return &cert, nil + } + h := xxhash.Sum64(tc.Key) ^ xxhash.Sum64(tc.Cert) + tctx.tlsCertDigest = fmt.Sprintf("digest(key+cert)=%d", h) + } else if tc.CertFile != "" || tc.KeyFile != "" { + tctx.getTLSCert = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + // Re-read TLS certificate from disk. This is needed for https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1420 + certPath := fs.GetFilepath(baseDir, tc.CertFile) + keyPath := fs.GetFilepath(baseDir, tc.KeyFile) + cert, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + return nil, fmt.Errorf("cannot load TLS certificate from `cert_file`=%q, `key_file`=%q: %w", tc.CertFile, tc.KeyFile, err) + } + return &cert, nil + } + // Check whether the configured TLS cert can be loaded. + if _, err := tctx.getTLSCert(nil); err != nil { + return err + } + tctx.tlsCertDigest = fmt.Sprintf("certFile=%q, keyFile=%q", tc.CertFile, tc.KeyFile) + } + if len(tc.CA) != 0 { + tctx.rootCA = x509.NewCertPool() + if !tctx.rootCA.AppendCertsFromPEM(tc.CA) { + return fmt.Errorf("cannot parse data from `ca` value") + } + } else if tc.CAFile != "" { + path := fs.GetFilepath(baseDir, tc.CAFile) + data, err := fs.ReadFileOrHTTP(path) + if err != nil { + return fmt.Errorf("cannot read `ca_file` %q: %w", tc.CAFile, err) + } + tctx.rootCA = x509.NewCertPool() + if !tctx.rootCA.AppendCertsFromPEM(data) { + return fmt.Errorf("cannot parse data from `ca_file` %q", tc.CAFile) + } + } + if tc.MinVersion != "" { + v, err := parseTLSVersion(tc.MinVersion) + if err != nil { + return fmt.Errorf("cannot parse `min_version`: %w", err) + } + tctx.minVersion = v + } + return nil +} + func parseTLSVersion(s string) (uint16, error) { switch strings.ToUpper(s) { case "TLS13": diff --git a/lib/promauth/config_test.go b/lib/promauth/config_test.go index dcbbd2a30..def949f9a 100644 --- a/lib/promauth/config_test.go +++ b/lib/promauth/config_test.go @@ -9,25 +9,16 @@ import ( ) func TestNewConfig(t *testing.T) { - type args struct { - baseDir string - az *Authorization - basicAuth *BasicAuthConfig - bearerToken string - bearerTokenFile string - oauth *OAuth2Config - tlsConfig *TLSConfig - } tests := []struct { name string - args args + opts Options wantErr bool expectHeader string }{ { name: "OAuth2 config", - args: args{ - oauth: &OAuth2Config{ + opts: Options{ + OAuth2: &OAuth2Config{ ClientID: "some-id", ClientSecret: NewSecret("some-secret"), TokenURL: "http://localhost:8511", @@ -37,8 +28,8 @@ func TestNewConfig(t *testing.T) { }, { name: "OAuth2 config with file", - args: args{ - oauth: &OAuth2Config{ + opts: Options{ + OAuth2: &OAuth2Config{ ClientID: "some-id", ClientSecretFile: "testdata/test_secretfile.txt", TokenURL: "http://localhost:8511", @@ -48,8 +39,8 @@ func TestNewConfig(t *testing.T) { }, { name: "OAuth2 want err", - args: args{ - oauth: &OAuth2Config{ + opts: Options{ + OAuth2: &OAuth2Config{ ClientID: "some-id", ClientSecret: NewSecret("some-secret"), ClientSecretFile: "testdata/test_secretfile.txt", @@ -60,8 +51,8 @@ func TestNewConfig(t *testing.T) { }, { name: "basic Auth config", - args: args{ - basicAuth: &BasicAuthConfig{ + opts: Options{ + BasicAuth: &BasicAuthConfig{ Username: "user", Password: NewSecret("password"), }, @@ -70,8 +61,8 @@ func TestNewConfig(t *testing.T) { }, { name: "basic Auth config with file", - args: args{ - basicAuth: &BasicAuthConfig{ + opts: Options{ + BasicAuth: &BasicAuthConfig{ Username: "user", PasswordFile: "testdata/test_secretfile.txt", }, @@ -80,8 +71,8 @@ func TestNewConfig(t *testing.T) { }, { name: "want Authorization", - args: args{ - az: &Authorization{ + opts: Options{ + Authorization: &Authorization{ Type: "Bearer", Credentials: NewSecret("Value"), }, @@ -90,16 +81,16 @@ func TestNewConfig(t *testing.T) { }, { name: "token file", - args: args{ - bearerTokenFile: "testdata/test_secretfile.txt", + opts: Options{ + BearerTokenFile: "testdata/test_secretfile.txt", }, expectHeader: "Bearer secret-content", }, { name: "token with tls", - args: args{ - bearerToken: "some-token", - tlsConfig: &TLSConfig{ + opts: Options{ + BearerToken: "some-token", + TLSConfig: &TLSConfig{ InsecureSkipVerify: true, }, }, @@ -108,7 +99,7 @@ func TestNewConfig(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if tt.args.oauth != nil { + if tt.opts.OAuth2 != nil { r := http.NewServeMux() r.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -116,9 +107,9 @@ func TestNewConfig(t *testing.T) { }) mock := httptest.NewServer(r) - tt.args.oauth.TokenURL = mock.URL + tt.opts.OAuth2.TokenURL = mock.URL } - got, err := NewConfig(tt.args.baseDir, tt.args.az, tt.args.basicAuth, tt.args.bearerToken, tt.args.bearerTokenFile, tt.args.oauth, tt.args.tlsConfig, nil) + got, err := tt.opts.NewConfig() if (err != nil) != tt.wantErr { t.Errorf("NewConfig() error = %v, wantErr %v", err, tt.wantErr) return @@ -140,7 +131,6 @@ func TestNewConfig(t *testing.T) { t.Fatalf("unexpected auth header from fasthttp request; got %q; want %q", ahb, tt.expectHeader) } } - }) } } @@ -186,7 +176,10 @@ func TestConfigHeaders(t *testing.T) { if err != nil { t.Fatalf("cannot parse headers: %s", err) } - c, err := NewConfig("", nil, nil, "", "", nil, nil, headers) + opts := Options{ + Headers: headers, + } + c, err := opts.NewConfig() if err != nil { t.Fatalf("cannot create config: %s", err) } diff --git a/lib/promscrape/config_test.go b/lib/promscrape/config_test.go index bd6ba5277..9455d97aa 100644 --- a/lib/promscrape/config_test.go +++ b/lib/promscrape/config_test.go @@ -1656,11 +1656,17 @@ scrape_configs: }, }) - ac, err := promauth.NewConfig(".", nil, nil, "", "", nil, nil, []string{"My-Auth: foo-Bar"}) + opts := &promauth.Options{ + Headers: []string{"My-Auth: foo-Bar"}, + } + ac, err := opts.NewConfig() if err != nil { t.Fatalf("unexpected error when creating promauth.Config: %s", err) } - proxyAC, err := promauth.NewConfig(".", nil, nil, "", "", nil, nil, []string{"Foo:bar"}) + opts = &promauth.Options{ + Headers: []string{"Foo:bar"}, + } + proxyAC, err := opts.NewConfig() if err != nil { t.Fatalf("unexpected error when creating promauth.Config for proxy: %s", err) } diff --git a/lib/promscrape/discovery/kubernetes/api.go b/lib/promscrape/discovery/kubernetes/api.go index 45cb7a06f..79615b320 100644 --- a/lib/promscrape/discovery/kubernetes/api.go +++ b/lib/promscrape/discovery/kubernetes/api.go @@ -31,7 +31,16 @@ func newAPIConfig(sdc *SDConfig, baseDir string, swcFunc ScrapeWorkConstructorFu if err != nil { return nil, fmt.Errorf("cannot build kube config from the specified `kubeconfig_file` config option: %w", err) } - acNew, err := promauth.NewConfig(".", nil, kc.basicAuth, kc.token, kc.tokenFile, cc.OAuth2, kc.tlsConfig, cc.Headers) + opts := &promauth.Options{ + BaseDir: baseDir, + BasicAuth: kc.basicAuth, + BearerToken: kc.token, + BearerTokenFile: kc.tokenFile, + OAuth2: cc.OAuth2, + TLSConfig: kc.tlsConfig, + Headers: cc.Headers, + } + acNew, err := opts.NewConfig() if err != nil { return nil, fmt.Errorf("cannot initialize auth config from `kubeconfig_file: %q`: %w", sdc.KubeConfigFile, err) } @@ -58,7 +67,14 @@ func newAPIConfig(sdc *SDConfig, baseDir string, swcFunc ScrapeWorkConstructorFu tlsConfig := promauth.TLSConfig{ CAFile: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt", } - acNew, err := promauth.NewConfig(".", nil, nil, "", "/var/run/secrets/kubernetes.io/serviceaccount/token", cc.OAuth2, &tlsConfig, cc.Headers) + opts := &promauth.Options{ + BaseDir: baseDir, + BearerTokenFile: "/var/run/secrets/kubernetes.io/serviceaccount/token", + OAuth2: cc.OAuth2, + TLSConfig: &tlsConfig, + Headers: cc.Headers, + } + acNew, err := opts.NewConfig() if err != nil { return nil, fmt.Errorf("cannot initialize service account auth: %w; probably, `kubernetes_sd_config->api_server` is missing in Prometheus configs?", err) } diff --git a/lib/promscrape/discovery/openstack/api.go b/lib/promscrape/discovery/openstack/api.go index 010973c67..eaa6a0e90 100644 --- a/lib/promscrape/discovery/openstack/api.go +++ b/lib/promscrape/discovery/openstack/api.go @@ -81,7 +81,11 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { port: sdc.Port, } if sdc.TLSConfig != nil { - ac, err := promauth.NewConfig(baseDir, nil, nil, "", "", nil, sdc.TLSConfig, nil) + opts := &promauth.Options{ + BaseDir: baseDir, + TLSConfig: sdc.TLSConfig, + } + ac, err := opts.NewConfig() if err != nil { return nil, err }