mirror of https://github.com/prometheus/prometheus
Merge pull request #8643 from roidelapluie/scaleway-read-secret-from-file
Scaleway SD: Add the ability to read token from filepull/8656/head
commit
cceab6defa
|
@ -1136,6 +1136,18 @@ var expectedErrors = []struct {
|
||||||
filename: "eureka_invalid_server.bad.yml",
|
filename: "eureka_invalid_server.bad.yml",
|
||||||
errMsg: "invalid eureka server URL",
|
errMsg: "invalid eureka server URL",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
filename: "scaleway_role.bad.yml",
|
||||||
|
errMsg: `unknown role "invalid"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: "scaleway_no_secret.bad.yml",
|
||||||
|
errMsg: "one of secret_key & secret_key_file must be configured",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: "scaleway_two_secrets.bad.yml",
|
||||||
|
errMsg: "at most one of secret_key & secret_key_file must be configured",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBadConfigs(t *testing.T) {
|
func TestBadConfigs(t *testing.T) {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
scrape_configs:
|
||||||
|
- scaleway_sd_configs:
|
||||||
|
- role: instance
|
||||||
|
project_id: 11111111-1111-1111-1111-111111111112
|
||||||
|
access_key: SCWXXXXXXXXXXXXXXXXX
|
|
@ -1,4 +1,7 @@
|
||||||
scrape_configs:
|
scrape_configs:
|
||||||
- scaleway_sd_configs:
|
- scaleway_sd_configs:
|
||||||
- role: invalid
|
- role: invalid
|
||||||
|
project_id: 11111111-1111-1111-1111-111111111112
|
||||||
|
access_key: SCWXXXXXXXXXXXXXXXXX
|
||||||
|
secret_key_file: bar
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
scrape_configs:
|
||||||
|
- scaleway_sd_configs:
|
||||||
|
- role: instance
|
||||||
|
project_id: 11111111-1111-1111-1111-111111111112
|
||||||
|
access_key: SCWXXXXXXXXXXXXXXXXX
|
||||||
|
secret_key_file: bar
|
||||||
|
secret_key: 11111111-1111-1111-1111-111111111112
|
||||||
|
|
|
@ -75,10 +75,18 @@ func newBaremetalDiscovery(conf *SDConfig) (*baremetalDiscovery, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.SecretKeyFile != "" {
|
||||||
|
rt, err = newAuthTokenFileRoundTripper(conf.SecretKeyFile, rt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
profile, err := loadProfile(conf)
|
profile, err := loadProfile(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.client, err = scw.NewClient(
|
d.client, err = scw.NewClient(
|
||||||
scw.WithHTTPClient(&http.Client{
|
scw.WithHTTPClient(&http.Client{
|
||||||
Transport: rt,
|
Transport: rt,
|
||||||
|
|
|
@ -76,7 +76,7 @@ func newInstanceDiscovery(conf *SDConfig) (*instanceDiscovery, error) {
|
||||||
zone: conf.Zone,
|
zone: conf.Zone,
|
||||||
project: conf.Project,
|
project: conf.Project,
|
||||||
accessKey: conf.AccessKey,
|
accessKey: conf.AccessKey,
|
||||||
secretKey: string(conf.SecretKey),
|
secretKey: conf.secretKeyForConfig(),
|
||||||
nameFilter: conf.NameFilter,
|
nameFilter: conf.NameFilter,
|
||||||
tagsFilter: conf.TagsFilter,
|
tagsFilter: conf.TagsFilter,
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,13 @@ func newInstanceDiscovery(conf *SDConfig) (*instanceDiscovery, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.SecretKeyFile != "" {
|
||||||
|
rt, err = newAuthTokenFileRoundTripper(conf.SecretKeyFile, rt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
profile, err := loadProfile(conf)
|
profile, err := loadProfile(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -27,9 +27,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testProjectID = "8feda53f-15f0-447f-badf-ebe32dad2fc0"
|
testProjectID = "8feda53f-15f0-447f-badf-ebe32dad2fc0"
|
||||||
testSecretKey = "6d6579e5-a5b9-49fc-a35f-b4feb9b87301"
|
testSecretKeyFile = "testdata/secret_key"
|
||||||
testAccessKey = "SCW0W8NG6024YHRJ7723"
|
testSecretKey = "6d6579e5-a5b9-49fc-a35f-b4feb9b87301"
|
||||||
|
testAccessKey = "SCW0W8NG6024YHRJ7723"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestScalewayInstanceRefresh(t *testing.T) {
|
func TestScalewayInstanceRefresh(t *testing.T) {
|
||||||
|
@ -137,3 +138,28 @@ func mockScalewayInstance(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestScalewayInstanceAuthToken(t *testing.T) {
|
||||||
|
mock := httptest.NewServer(http.HandlerFunc(mockScalewayInstance))
|
||||||
|
defer mock.Close()
|
||||||
|
|
||||||
|
cfgString := fmt.Sprintf(`
|
||||||
|
---
|
||||||
|
role: instance
|
||||||
|
project_id: %s
|
||||||
|
secret_key_file: %s
|
||||||
|
access_key: %s
|
||||||
|
api_url: %s
|
||||||
|
`, testProjectID, testSecretKeyFile, testAccessKey, mock.URL)
|
||||||
|
var cfg SDConfig
|
||||||
|
require.NoError(t, yaml.UnmarshalStrict([]byte(cfgString), &cfg))
|
||||||
|
|
||||||
|
d, err := newRefresher(&cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
tgs, err := d.refresh(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, 1, len(tgs))
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@ package scaleway
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-kit/kit/log"
|
"github.com/go-kit/kit/log"
|
||||||
|
@ -83,6 +86,8 @@ type SDConfig struct {
|
||||||
AccessKey string `yaml:"access_key"`
|
AccessKey string `yaml:"access_key"`
|
||||||
// SecretKey used to authenticate on Scaleway APIs.
|
// SecretKey used to authenticate on Scaleway APIs.
|
||||||
SecretKey config.Secret `yaml:"secret_key"`
|
SecretKey config.Secret `yaml:"secret_key"`
|
||||||
|
// SecretKey used to authenticate on Scaleway APIs.
|
||||||
|
SecretKeyFile string `yaml:"secret_key_file"`
|
||||||
// NameFilter to filter on during the ListServers.
|
// NameFilter to filter on during the ListServers.
|
||||||
NameFilter string `yaml:"name_filter,omitempty"`
|
NameFilter string `yaml:"name_filter,omitempty"`
|
||||||
// TagsFilter to filter on during the ListServers.
|
// TagsFilter to filter on during the ListServers.
|
||||||
|
@ -100,6 +105,15 @@ func (c SDConfig) Name() string {
|
||||||
return "scaleway"
|
return "scaleway"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// secretKeyForConfig returns a secret key that looks like a UUID, even if we
|
||||||
|
// take the actuel secret from a file.
|
||||||
|
func (c SDConfig) secretKeyForConfig() string {
|
||||||
|
if c.SecretKeyFile != "" {
|
||||||
|
return "00000000-0000-0000-0000-000000000000"
|
||||||
|
}
|
||||||
|
return string(c.SecretKey)
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||||
func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
*c = DefaultSDConfig
|
*c = DefaultSDConfig
|
||||||
|
@ -117,8 +131,12 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
return errors.New("project_id is mandatory")
|
return errors.New("project_id is mandatory")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.SecretKey == "" {
|
if c.SecretKey == "" && c.SecretKeyFile == "" {
|
||||||
return errors.New("secret_key is mandatory")
|
return errors.New("one of secret_key & secret_key_file must be configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.SecretKey != "" && c.SecretKeyFile != "" {
|
||||||
|
return errors.New("at most one of secret_key & secret_key_file must be configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.AccessKey == "" {
|
if c.AccessKey == "" {
|
||||||
|
@ -145,6 +163,7 @@ func (c SDConfig) NewDiscoverer(options discovery.DiscovererOptions) (discovery.
|
||||||
|
|
||||||
// SetDirectory joins any relative file paths with dir.
|
// SetDirectory joins any relative file paths with dir.
|
||||||
func (c *SDConfig) SetDirectory(dir string) {
|
func (c *SDConfig) SetDirectory(dir string) {
|
||||||
|
c.SecretKeyFile = config.JoinDir(dir, c.SecretKeyFile)
|
||||||
c.HTTPClientConfig.SetDirectory(dir)
|
c.HTTPClientConfig.SetDirectory(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +209,7 @@ func loadProfile(sdConfig *SDConfig) (*scw.Profile, error) {
|
||||||
prometheusConfigProfile := &scw.Profile{
|
prometheusConfigProfile := &scw.Profile{
|
||||||
DefaultZone: scw.StringPtr(sdConfig.Zone),
|
DefaultZone: scw.StringPtr(sdConfig.Zone),
|
||||||
APIURL: scw.StringPtr(sdConfig.APIURL),
|
APIURL: scw.StringPtr(sdConfig.APIURL),
|
||||||
SecretKey: scw.StringPtr(string(sdConfig.SecretKey)),
|
SecretKey: scw.StringPtr(sdConfig.secretKeyForConfig()),
|
||||||
AccessKey: scw.StringPtr(sdConfig.AccessKey),
|
AccessKey: scw.StringPtr(sdConfig.AccessKey),
|
||||||
DefaultProjectID: scw.StringPtr(sdConfig.Project),
|
DefaultProjectID: scw.StringPtr(sdConfig.Project),
|
||||||
SendTelemetry: scw.BoolPtr(false),
|
SendTelemetry: scw.BoolPtr(false),
|
||||||
|
@ -198,3 +217,29 @@ func loadProfile(sdConfig *SDConfig) (*scw.Profile, error) {
|
||||||
|
|
||||||
return prometheusConfigProfile, nil
|
return prometheusConfigProfile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type authTokenFileRoundTripper struct {
|
||||||
|
authTokenFile string
|
||||||
|
rt http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAuthTokenFileRoundTripper adds the auth token read from the file to a request.
|
||||||
|
func newAuthTokenFileRoundTripper(tokenFile string, rt http.RoundTripper) (http.RoundTripper, error) {
|
||||||
|
// fail-fast if we can't read the file.
|
||||||
|
_, err := ioutil.ReadFile(tokenFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to read auth token file %s", tokenFile)
|
||||||
|
}
|
||||||
|
return &authTokenFileRoundTripper{tokenFile, rt}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *authTokenFileRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||||
|
b, err := ioutil.ReadFile(rt.authTokenFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to read auth token file %s", rt.authTokenFile)
|
||||||
|
}
|
||||||
|
authToken := strings.TrimSpace(string(b))
|
||||||
|
|
||||||
|
request.Header.Set("X-Auth-Token", authToken)
|
||||||
|
return rt.rt.RoundTrip(request)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
6d6579e5-a5b9-49fc-a35f-b4feb9b87301
|
|
@ -1589,7 +1589,12 @@ See below for the configuration options for Scaleway discovery:
|
||||||
access_key: <string>
|
access_key: <string>
|
||||||
|
|
||||||
# Secret key to use when listing targets. https://console.scaleway.com/project/credentials
|
# Secret key to use when listing targets. https://console.scaleway.com/project/credentials
|
||||||
secret_key: <secret>
|
# It is mutually exclusive with `secret_key_file`.
|
||||||
|
[ secret_key: <secret> ]
|
||||||
|
|
||||||
|
# Sets the secret key with the credentials read from the configured file.
|
||||||
|
# It is mutually exclusive with `secret_key`.
|
||||||
|
[ secret_key_file: <filename> ]
|
||||||
|
|
||||||
# Project ID of the targets.
|
# Project ID of the targets.
|
||||||
project_id: <string>
|
project_id: <string>
|
||||||
|
|
Loading…
Reference in New Issue