From f690b811c5b0342618fa9d28d4d04802444acd4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20L=C3=A9one?= Date: Wed, 10 Mar 2021 15:10:17 +0100 Subject: [PATCH] add support for scaleway service discovery (#8555) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Co-authored-by: Julien Pivotto Signed-off-by: Rémy Léone --- config/config_test.go | 39 +++- config/testdata/conf.good.yml | 11 + config/testdata/scaleway_role.bad.yml | 4 + discovery/install/install.go | 1 + discovery/scaleway/baremetal.go | 192 +++++++++++++++++ discovery/scaleway/instance.go | 155 ++++++++++++++ discovery/scaleway/scaleway.go | 200 ++++++++++++++++++ docs/configuration/configuration.md | 95 +++++++++ .../examples/prometheus-scaleway.yml | 41 ++++ go.mod | 1 + go.sum | 4 + 11 files changed, 742 insertions(+), 1 deletion(-) create mode 100644 config/testdata/scaleway_role.bad.yml create mode 100644 discovery/scaleway/baremetal.go create mode 100644 discovery/scaleway/instance.go create mode 100644 discovery/scaleway/scaleway.go create mode 100644 documentation/examples/prometheus-scaleway.yml diff --git a/config/config_test.go b/config/config_test.go index 9d494bb22..61ef13ac8 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -40,6 +40,7 @@ import ( "github.com/prometheus/prometheus/discovery/kubernetes" "github.com/prometheus/prometheus/discovery/marathon" "github.com/prometheus/prometheus/discovery/openstack" + "github.com/prometheus/prometheus/discovery/scaleway" "github.com/prometheus/prometheus/discovery/targetgroup" "github.com/prometheus/prometheus/discovery/triton" "github.com/prometheus/prometheus/discovery/zookeeper" @@ -742,6 +743,42 @@ var expectedConf = &Config{ }, }, }, + { + JobName: "scaleway", + + HonorTimestamps: true, + ScrapeInterval: model.Duration(15 * time.Second), + ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout, + HTTPClientConfig: config.HTTPClientConfig{FollowRedirects: true}, + + MetricsPath: DefaultScrapeConfig.MetricsPath, + Scheme: DefaultScrapeConfig.Scheme, + + ServiceDiscoveryConfigs: discovery.Configs{ + &scaleway.SDConfig{ + APIURL: "https://api.scaleway.com", + AccessKey: "SCWXXXXXXXXXXXXXXXXX", + HTTPClientConfig: config.HTTPClientConfig{FollowRedirects: true}, + Port: 80, + Project: "11111111-1111-1111-1111-111111111112", + RefreshInterval: model.Duration(60 * time.Second), + Role: "instance", + SecretKey: "11111111-1111-1111-1111-111111111111", + Zone: "fr-par-1", + }, + &scaleway.SDConfig{ + APIURL: "https://api.scaleway.com", + AccessKey: "SCWXXXXXXXXXXXXXXXXX", + HTTPClientConfig: config.HTTPClientConfig{FollowRedirects: true}, + Port: 80, + Project: "11111111-1111-1111-1111-111111111112", + RefreshInterval: model.Duration(60 * time.Second), + Role: "baremetal", + SecretKey: "11111111-1111-1111-1111-111111111111", + Zone: "fr-par-1", + }, + }, + }, }, AlertingConfig: AlertingConfig{ AlertmanagerConfigs: []*AlertmanagerConfig{ @@ -826,7 +863,7 @@ func TestElideSecrets(t *testing.T) { yamlConfig := string(config) matches := secretRe.FindAllStringIndex(yamlConfig, -1) - require.Equal(t, 10, len(matches), "wrong number of secret matches found") + require.Equal(t, 12, len(matches), "wrong number of secret matches found") require.NotContains(t, yamlConfig, "mysecret", "yaml marshal reveals authentication credentials.") } diff --git a/config/testdata/conf.good.yml b/config/testdata/conf.good.yml index 33548f477..d41efc2df 100644 --- a/config/testdata/conf.good.yml +++ b/config/testdata/conf.good.yml @@ -298,6 +298,17 @@ scrape_configs: eureka_sd_configs: - server: 'http://eureka.example.com:8761/eureka' +- job_name: scaleway + scaleway_sd_configs: + - role: instance + project_id: 11111111-1111-1111-1111-111111111112 + access_key: SCWXXXXXXXXXXXXXXXXX + secret_key: 11111111-1111-1111-1111-111111111111 + - role: baremetal + project_id: 11111111-1111-1111-1111-111111111112 + access_key: SCWXXXXXXXXXXXXXXXXX + secret_key: 11111111-1111-1111-1111-111111111111 + alerting: alertmanagers: - scheme: https diff --git a/config/testdata/scaleway_role.bad.yml b/config/testdata/scaleway_role.bad.yml new file mode 100644 index 000000000..97970e835 --- /dev/null +++ b/config/testdata/scaleway_role.bad.yml @@ -0,0 +1,4 @@ +scrape_configs: +- scaleway_sd_configs: + - role: invalid + diff --git a/discovery/install/install.go b/discovery/install/install.go index d9394f270..075a302e4 100644 --- a/discovery/install/install.go +++ b/discovery/install/install.go @@ -29,6 +29,7 @@ import ( _ "github.com/prometheus/prometheus/discovery/kubernetes" // register kubernetes _ "github.com/prometheus/prometheus/discovery/marathon" // register marathon _ "github.com/prometheus/prometheus/discovery/openstack" // register openstack + _ "github.com/prometheus/prometheus/discovery/scaleway" // register scaleway _ "github.com/prometheus/prometheus/discovery/triton" // register triton _ "github.com/prometheus/prometheus/discovery/zookeeper" // register zookeeper ) diff --git a/discovery/scaleway/baremetal.go b/discovery/scaleway/baremetal.go new file mode 100644 index 000000000..8eca93875 --- /dev/null +++ b/discovery/scaleway/baremetal.go @@ -0,0 +1,192 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package scaleway + +import ( + "context" + "fmt" + "net" + "net/http" + "strconv" + "strings" + "time" + + "github.com/prometheus/common/config" + "github.com/prometheus/common/model" + "github.com/prometheus/common/version" + "github.com/prometheus/prometheus/discovery/refresh" + "github.com/prometheus/prometheus/discovery/targetgroup" + "github.com/scaleway/scaleway-sdk-go/api/baremetal/v1" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +type baremetalDiscovery struct { + *refresh.Discovery + client *scw.Client + port int + zone string + project string + accessKey string + secretKey string + nameFilter string + tagsFilter []string +} + +const ( + baremetalLabelPrefix = metaLabelPrefix + "baremetal_" + + baremetalIDLabel = baremetalLabelPrefix + "id" + baremetalPublicIPv4Label = baremetalLabelPrefix + "public_ipv4" + baremetalPublicIPv6Label = baremetalLabelPrefix + "public_ipv6" + baremetalNameLabel = baremetalLabelPrefix + "name" + baremetalOSNameLabel = baremetalLabelPrefix + "os_name" + baremetalOSVersionLabel = baremetalLabelPrefix + "os_version" + baremetalProjectLabel = baremetalLabelPrefix + "project_id" + baremetalStatusLabel = baremetalLabelPrefix + "status" + baremetalTagsLabel = baremetalLabelPrefix + "tags" + baremetalTypeLabel = baremetalLabelPrefix + "type" + baremetalZoneLabel = baremetalLabelPrefix + "zone" +) + +func newBaremetalDiscovery(conf *SDConfig) (*baremetalDiscovery, error) { + d := &baremetalDiscovery{ + port: conf.Port, + zone: conf.Zone, + project: conf.Project, + accessKey: conf.AccessKey, + secretKey: string(conf.SecretKey), + nameFilter: conf.NameFilter, + tagsFilter: conf.TagsFilter, + } + + rt, err := config.NewRoundTripperFromConfig(conf.HTTPClientConfig, "scaleway_sd", false, false) + if err != nil { + return nil, err + } + + profile, err := loadProfile(conf) + if err != nil { + return nil, err + } + d.client, err = scw.NewClient( + scw.WithHTTPClient(&http.Client{ + Transport: rt, + Timeout: time.Duration(conf.RefreshInterval), + }), + scw.WithUserAgent(fmt.Sprintf("Prometheus/%s", version.Version)), + scw.WithProfile(profile), + ) + if err != nil { + return nil, fmt.Errorf("error setting up scaleway client: %w", err) + } + + return d, nil +} + +func (d *baremetalDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) { + api := baremetal.NewAPI(d.client) + + req := &baremetal.ListServersRequest{} + + if d.nameFilter != "" { + req.Name = scw.StringPtr(d.nameFilter) + } + + if d.tagsFilter != nil { + req.Tags = d.tagsFilter + } + + servers, err := api.ListServers(req, scw.WithAllPages(), scw.WithContext(ctx)) + if err != nil { + return nil, err + } + + offers, err := api.ListOffers(&baremetal.ListOffersRequest{}, scw.WithAllPages()) + if err != nil { + return nil, err + } + + osFullList, err := api.ListOS(&baremetal.ListOSRequest{}, scw.WithAllPages()) + if err != nil { + return nil, err + } + + var targets []model.LabelSet + for _, server := range servers.Servers { + labels := model.LabelSet{ + baremetalIDLabel: model.LabelValue(server.ID), + baremetalNameLabel: model.LabelValue(server.Name), + baremetalZoneLabel: model.LabelValue(server.Zone.String()), + baremetalStatusLabel: model.LabelValue(server.Status), + baremetalProjectLabel: model.LabelValue(server.ProjectID), + } + + for _, offer := range offers.Offers { + if server.OfferID == offer.ID { + labels[baremetalTypeLabel] = model.LabelValue(offer.Name) + break + } + } + + if server.Install != nil { + for _, os := range osFullList.Os { + if server.Install.OsID == os.ID { + labels[baremetalOSNameLabel] = model.LabelValue(os.Name) + labels[baremetalOSVersionLabel] = model.LabelValue(os.Version) + break + } + } + } + + if len(server.Tags) > 0 { + // We surround the separated list with the separator as well. This way regular expressions + // in relabeling rules don't have to consider tag positions. + tags := separator + strings.Join(server.Tags, separator) + separator + labels[baremetalTagsLabel] = model.LabelValue(tags) + } + + for _, ip := range server.IPs { + switch v := ip.Version.String(); v { + case "IPv4": + if _, ok := labels[baremetalPublicIPv4Label]; ok { + // If the server has multiple IPv4, we only take the first one. + // This should not happen. + continue + } + labels[baremetalPublicIPv4Label] = model.LabelValue(ip.Address.String()) + + // We always default the __address__ to IPv4. + addr := net.JoinHostPort(ip.Address.String(), strconv.FormatUint(uint64(d.port), 10)) + labels[model.AddressLabel] = model.LabelValue(addr) + case "IPv6": + if _, ok := labels[baremetalPublicIPv6Label]; ok { + // If the server has multiple IPv6, we only take the first one. + // This should not happen. + continue + } + labels[baremetalPublicIPv6Label] = model.LabelValue(ip.Address.String()) + if _, ok := labels[model.AddressLabel]; !ok { + // This server does not have an IPv4 or we have not parsed it + // yet. + addr := net.JoinHostPort(ip.Address.String(), strconv.FormatUint(uint64(d.port), 10)) + labels[model.AddressLabel] = model.LabelValue(addr) + } + default: + return nil, fmt.Errorf("unknown IP version: %s", v) + } + } + targets = append(targets, labels) + } + return []*targetgroup.Group{{Source: "scaleway", Targets: targets}}, nil +} diff --git a/discovery/scaleway/instance.go b/discovery/scaleway/instance.go new file mode 100644 index 000000000..6a62730ef --- /dev/null +++ b/discovery/scaleway/instance.go @@ -0,0 +1,155 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package scaleway + +import ( + "context" + "fmt" + "net" + "net/http" + "strconv" + "strings" + "time" + + "github.com/prometheus/common/config" + "github.com/prometheus/common/model" + "github.com/prometheus/common/version" + "github.com/prometheus/prometheus/discovery/refresh" + "github.com/prometheus/prometheus/discovery/targetgroup" + "github.com/scaleway/scaleway-sdk-go/api/instance/v1" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +const ( + instanceLabelPrefix = metaLabelPrefix + "instance_" + + instanceIDLabel = instanceLabelPrefix + "id" + instancePrivateIPv4 = instanceLabelPrefix + "private_ipv4" + instancePublicIPv4 = instanceLabelPrefix + "public_ipv4" + instancePublicIPv6 = instanceLabelPrefix + "public_ipv6" + instanceImageNameLabel = instanceLabelPrefix + "image_name" + instanceNameLabel = instanceLabelPrefix + "name" + instanceProjectLabel = instanceLabelPrefix + "project_id" + instanceStateLabel = instanceLabelPrefix + "status" + instanceTagsLabel = instanceLabelPrefix + "tags" + instanceTypeLabel = instanceLabelPrefix + "type" + instanceZoneLabel = instanceLabelPrefix + "zone" +) + +type instanceDiscovery struct { + *refresh.Discovery + client *scw.Client + port int + zone string + project string + accessKey string + secretKey string + nameFilter string + tagsFilter []string +} + +func newInstanceDiscovery(conf *SDConfig) (*instanceDiscovery, error) { + d := &instanceDiscovery{ + port: conf.Port, + zone: conf.Zone, + project: conf.Project, + accessKey: conf.AccessKey, + secretKey: string(conf.SecretKey), + nameFilter: conf.NameFilter, + tagsFilter: conf.TagsFilter, + } + + rt, err := config.NewRoundTripperFromConfig(conf.HTTPClientConfig, "scaleway_sd", false, false) + if err != nil { + return nil, err + } + + profile, err := loadProfile(conf) + if err != nil { + return nil, err + } + + d.client, err = scw.NewClient( + scw.WithHTTPClient(&http.Client{ + Transport: rt, + Timeout: time.Duration(conf.RefreshInterval), + }), + scw.WithUserAgent(fmt.Sprintf("Prometheus/%s", version.Version)), + scw.WithProfile(profile), + ) + if err != nil { + return nil, fmt.Errorf("error setting up scaleway client: %w", err) + } + + return d, nil +} + +func (d *instanceDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) { + api := instance.NewAPI(d.client) + + req := &instance.ListServersRequest{} + + if d.nameFilter != "" { + req.Name = scw.StringPtr(d.nameFilter) + } + + if d.tagsFilter != nil { + req.Tags = d.tagsFilter + } + + servers, err := api.ListServers(req, scw.WithAllPages(), scw.WithContext(ctx)) + if err != nil { + return nil, err + } + + var targets []model.LabelSet + for _, server := range servers.Servers { + labels := model.LabelSet{ + instanceIDLabel: model.LabelValue(server.ID), + instanceImageNameLabel: model.LabelValue(server.Image.Name), + instanceNameLabel: model.LabelValue(server.Name), + instanceProjectLabel: model.LabelValue(server.Project), + instanceStateLabel: model.LabelValue(server.State), + instanceTypeLabel: model.LabelValue(server.CommercialType), + instanceZoneLabel: model.LabelValue(server.Zone.String()), + } + + if len(server.Tags) > 0 { + // We surround the separated list with the separator as well. This way regular expressions + // in relabeling rules don't have to consider tag positions. + tags := separator + strings.Join(server.Tags, separator) + separator + labels[instanceTagsLabel] = model.LabelValue(tags) + } + + if server.IPv6 != nil { + labels[instancePublicIPv6] = model.LabelValue(server.IPv6.Address.String()) + } + + if server.PublicIP != nil { + labels[instancePublicIPv4] = model.LabelValue(server.PublicIP.Address.String()) + } + + if server.PrivateIP != nil { + labels[instancePrivateIPv4] = model.LabelValue(*server.PrivateIP) + + addr := net.JoinHostPort(*server.PrivateIP, strconv.FormatUint(uint64(d.port), 10)) + labels[model.AddressLabel] = model.LabelValue(addr) + + targets = append(targets, labels) + } + + } + + return []*targetgroup.Group{{Source: "scaleway", Targets: targets}}, nil +} diff --git a/discovery/scaleway/scaleway.go b/discovery/scaleway/scaleway.go new file mode 100644 index 000000000..fc1ace806 --- /dev/null +++ b/discovery/scaleway/scaleway.go @@ -0,0 +1,200 @@ +// Copyright 2021 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package scaleway + +import ( + "context" + "time" + + "github.com/go-kit/kit/log" + "github.com/pkg/errors" + "github.com/prometheus/common/config" + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/discovery" + "github.com/prometheus/prometheus/discovery/refresh" + "github.com/prometheus/prometheus/discovery/targetgroup" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// metaLabelPrefix is the meta prefix used for all meta labels. +// in this discovery. +const ( + metaLabelPrefix = model.MetaLabelPrefix + "scaleway_" + separator = "," +) + +// role is the role of the target within the Scaleway Ecosystem. +type role string + +// The valid options for role. +const ( + // Scaleway Elements Baremetal + // https://www.scaleway.com/en/bare-metal-servers/ + roleBaremetal role = "baremetal" + + // Scaleway Elements Instance + // https://www.scaleway.com/en/virtual-instances/ + roleInstance role = "instance" +) + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (c *role) UnmarshalYAML(unmarshal func(interface{}) error) error { + if err := unmarshal((*string)(c)); err != nil { + return err + } + switch *c { + case roleInstance, roleBaremetal: + return nil + default: + return errors.Errorf("unknown role %q", *c) + } +} + +// DefaultSDConfig is the default Scaleway Service Discovery configuration. +var DefaultSDConfig = SDConfig{ + Port: 80, + RefreshInterval: model.Duration(60 * time.Second), + HTTPClientConfig: config.DefaultHTTPClientConfig, + Zone: scw.ZoneFrPar1.String(), + APIURL: "https://api.scaleway.com", +} + +type SDConfig struct { + // Project: The Scaleway Project ID used to filter discovery on. + Project string `yaml:"project_id"` + + // APIURL: URL of the Scaleway API to use. + APIURL string `yaml:"api_url,omitempty"` + // Zone: The zone of the scrape targets. + // If you need to configure multiple zones use multiple scaleway_sd_configs + Zone string `yaml:"zone"` + // AccessKey used to authenticate on Scaleway APIs. + AccessKey string `yaml:"access_key"` + // SecretKey used to authenticate on Scaleway APIs. + SecretKey config.Secret `yaml:"secret_key"` + // NameFilter to filter on during the ListServers. + NameFilter string `yaml:"name_filter,omitempty"` + // TagsFilter to filter on during the ListServers. + TagsFilter []string `yaml:"tags_filter,omitempty"` + + HTTPClientConfig config.HTTPClientConfig `yaml:",inline"` + + RefreshInterval model.Duration `yaml:"refresh_interval"` + Port int `yaml:"port"` + // Role can be either instance or baremetal + Role role `yaml:"role"` +} + +func (c SDConfig) Name() string { + return "scaleway" +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + *c = DefaultSDConfig + type plain SDConfig + err := unmarshal((*plain)(c)) + if err != nil { + return err + } + + if c.Role == "" { + return errors.New("role missing (one of: instance, baremetal)") + } + + if c.Project == "" { + return errors.New("project_id is mandatory") + } + + if c.SecretKey == "" { + return errors.New("secret_key is mandatory") + } + + if c.AccessKey == "" { + return errors.New("access_key is mandatory") + } + + profile, err := loadProfile(c) + if err != nil { + return err + } + _, err = scw.NewClient( + scw.WithProfile(profile), + ) + if err != nil { + return err + } + + return c.HTTPClientConfig.Validate() +} + +func (c SDConfig) NewDiscoverer(options discovery.DiscovererOptions) (discovery.Discoverer, error) { + return NewDiscovery(&c, options.Logger) +} + +// SetDirectory joins any relative file paths with dir. +func (c *SDConfig) SetDirectory(dir string) { + c.HTTPClientConfig.SetDirectory(dir) +} + +func init() { + discovery.RegisterConfig(&SDConfig{}) +} + +// Discovery periodically performs Scaleway requests. It implements +// the Discoverer interface. +type Discovery struct { +} + +func NewDiscovery(conf *SDConfig, logger log.Logger) (*refresh.Discovery, error) { + r, err := newRefresher(conf) + if err != nil { + return nil, err + } + + return refresh.NewDiscovery( + logger, + "scaleway", + time.Duration(conf.RefreshInterval), + r.refresh, + ), nil +} + +type refresher interface { + refresh(context.Context) ([]*targetgroup.Group, error) +} + +func newRefresher(conf *SDConfig) (refresher, error) { + switch conf.Role { + case roleBaremetal: + return newBaremetalDiscovery(conf) + case roleInstance: + return newInstanceDiscovery(conf) + } + return nil, errors.New("unknown Scaleway discovery role") +} + +func loadProfile(sdConfig *SDConfig) (*scw.Profile, error) { + // Profile coming from Prometheus Configuration file + prometheusConfigProfile := &scw.Profile{ + DefaultZone: scw.StringPtr(sdConfig.Zone), + APIURL: scw.StringPtr(sdConfig.APIURL), + SecretKey: scw.StringPtr(string(sdConfig.SecretKey)), + AccessKey: scw.StringPtr(sdConfig.AccessKey), + DefaultProjectID: scw.StringPtr(sdConfig.Project), + SendTelemetry: scw.BoolPtr(false), + } + + return prometheusConfigProfile, nil +} diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index 8a7f02a42..8f5073d31 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -246,6 +246,10 @@ nerve_sd_configs: openstack_sd_configs: [ - ... ] +# List of Scaleway service discovery configurations. +scaleway_sd_configs: + [ - ... ] + # List of Zookeeper Serverset service discovery configurations. serverset_sd_configs: [ - ... ] @@ -1523,6 +1527,93 @@ See [the Prometheus eureka-sd configuration file](/documentation/examples/promet for a practical example on how to set up your Eureka app and your Prometheus configuration. +### `` + +Scaleway SD configurations allow retrieving scrape targets from [Scaleway instances](https://www.scaleway.com/en/virtual-instances/) and [baremetal services](https://www.scaleway.com/en/bare-metal-servers/). + +The following meta labels are available on targets during [relabeling](#relabel_config): + +#### Instance role + +* `__meta_scaleway_instance_id`: the id of the instance +* `__meta_scaleway_instance_private_ipv4`: the private ipv4 address of the instance +* `__meta_scaleway_instance_public_ipv4`: the public ipv4 address of the instance +* `__meta_scaleway_instance_public_ipv6`: the public ipv6 address of the instance +* `__meta_scaleway_instance_image_name`: name of the server image +* `__meta_scaleway_instance_name`: name of the server +* `__meta_scaleway_instance_project_id`: project id of the server +* `__meta_scaleway_instance_status`: status of the server +* `__meta_scaleway_instance_tags`: the list of tags of the target joined by the tag separator +* `__meta_scaleway_instance_type`: commercial type of the server +* `__meta_scaleway_instance_zone`: zone of the instance (ex: `fr-par-1`, complete list on ) + +This role uses the private IPv4 address by default. This can be +changed with relabelling, as demonstrated in [the Prometheus scaleway-sd +configuration file](/documentation/examples/prometheus-scaleway.yml). + +#### Baremetal role + +* `__meta_scaleway_baremetal_id`: the id of the server +* `__meta_scaleway_baremetal_public_ipv4`: the public ipv4 address of the server +* `__meta_scaleway_baremetal_public_ipv6`: the public ipv6 address of the server +* `__meta_scaleway_baremetal_ipaddress_order`: zero-based order of the address in the server +* `__meta_scaleway_baremetal_name`: name of the server +* `__meta_scaleway_baremetal_os_name`: name of the os used +* `__meta_scaleway_baremetal_os_version`: version of the os used +* `__meta_scaleway_baremetal_project_id`: project id of the server +* `__meta_scaleway_baremetal_status`: status of the server +* `__meta_scaleway_baremetal_tags`: tag list of the server +* `__meta_scaleway_baremetal_type`: commercial type of the server +* `__meta_scaleway_baremetal_zone`: zone of the server (ex: `fr-par-1`, complete list on ) + +This role uses the public IPv4 address by default. This can be +changed with relabelling, as demonstrated in [the Prometheus scaleway-sd +configuration file](/documentation/examples/prometheus-scaleway.yml). + +See below for the configuration options for Scaleway discovery: + +```yaml +# Access key to use. https://console.scaleway.com/project/credentials +access_key: + +# Secret key to use when listing targets. https://console.scaleway.com/project/credentials +secret_key: + +# Project ID of the targets. +project_id: + +# Role of the targets to retrieve. Must be `instance` or `baremetal`. +role: + +# The port to scrape metrics from. +[ port: | default = 80 ] + +# API URL to use when doing the server listing requests. +[ api_url: | default = "https://api.scaleway.com" ] + +# Zone is the availability zone of your targets (e.g. fr-par-1). +[ zone: | default = fr-par-1 ] + +# NameFilter specify a name filter (works as a LIKE) to apply on the server listing request. +[ name_filter: ] + +# TagsFilter specify a tag filter (a server needs to have all defined tags to be listed) to apply on the server listing request. +tags_filter: +[ - ] + +# Refresh interval to re-read the targets list. +[ refresh_interval: | default = 60s ] + +# Configure whether HTTP requests follow HTTP 3xx redirects. +[ follow_redirects: | default = true ] + +# Optional proxy URL. +[ proxy_url: ] + +# TLS configuration. +tls_config: + [ ] +``` ### `` @@ -1746,6 +1837,10 @@ nerve_sd_configs: openstack_sd_configs: [ - ... ] +# List of Scaleway service discovery configurations. +scaleway_sd_configs: + [ - ... ] + # List of Zookeeper Serverset service discovery configurations. serverset_sd_configs: [ - ... ] diff --git a/documentation/examples/prometheus-scaleway.yml b/documentation/examples/prometheus-scaleway.yml new file mode 100644 index 000000000..22b9aa1cb --- /dev/null +++ b/documentation/examples/prometheus-scaleway.yml @@ -0,0 +1,41 @@ +# A example scrape configuration for running Prometheus with Scaleway. +scrape_configs: + - job_name: 'prometheus' + scaleway_sd_configs: + - role: instance + # You can find you project ID here: https://console.scaleway.com/project/settings + project_id: 11111111-1111-1111-1111-111111111111 + # Replace with Scaleway Credentials: https://console.scaleway.com/project/credentials + access_key: SCWXXXXXXXXXXXXXXXXX + secret_key: 11111111-1111-1111-1111-111111111111 + relabel_configs: + # Only scrape targets that have a tag 'prometheus'. + - source_labels: [__meta_scaleway_instance_tags] + regex: '.*,prometheus,.*' + action: keep + # Use the public IPv6 address and port 9100 to scrape the target. + - source_labels: [__meta_scaleway_instance_public_ipv6] + target_label: __address__ + replacement: '[$1]:9090' + # Add the zone as label + - source_labels: [__meta_scaleway_instance_zone] + target_label: scw_zone + + - job_name: 'node' + scaleway_sd_configs: + - role: baremetal + # You can find you project ID here: https://console.scaleway.com/project/settings + project_id: 11111111-1111-1111-1111-111111111111 + zone: fr-par-2 + # Replace with Scaleway Credentials: https://console.scaleway.com/project/credentials + access_key: SCWXXXXXXXXXXXXXXXXX + secret_key: 11111111-1111-1111-1111-111111111111 + relabel_configs: + # Filter out servers that are not physically in the datacenter. + - source_labels: [__meta_scaleway_baremetal_status] + regex: '(delivering|out_of_stock)' + action: drop + # Use the public IPv6 address and port 9100 to scrape the target. + - source_labels: [__meta_scaleway_baremetal_public_ipv6] + target_label: __address__ + replacement: '[$1]:9100' diff --git a/go.mod b/go.mod index 3e357148c..b2490fe6c 100644 --- a/go.mod +++ b/go.mod @@ -50,6 +50,7 @@ require ( github.com/prometheus/client_model v0.2.0 github.com/prometheus/common v0.18.0 github.com/prometheus/exporter-toolkit v0.5.1 + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44 github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index fe35f8347..34f4a59bf 100644 --- a/go.sum +++ b/go.sum @@ -161,6 +161,8 @@ github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245 h1:9cOfvEwjQxdwKu github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/digitalocean/godo v1.57.0 h1:uCpe0sRIZ/sJWxWDsJyBPBjUfSvxop+WHkHiSf+tjjM= github.com/digitalocean/godo v1.57.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= +github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.3+incompatible h1:+HS4XO73J41FpA260ztGujJ+0WibrA2TPJEnWNSyGNE= @@ -739,6 +741,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44 h1:3egqo0Vut6daANFm7tOXdNAa8v5/uLU+sgCJrc88Meo= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=