add support for scaleway service discovery (#8555)
Co-authored-by: Patrik <patrik@ptrk.io> Co-authored-by: Julien Pivotto <roidelapluie@inuits.eu> Signed-off-by: Rémy Léone <rleone@scaleway.com>
This commit is contained in:
parent
ad5ed416ba
commit
f690b811c5
|
@ -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.")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
scrape_configs:
|
||||
- scaleway_sd_configs:
|
||||
- role: invalid
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -246,6 +246,10 @@ nerve_sd_configs:
|
|||
openstack_sd_configs:
|
||||
[ - <openstack_sd_config> ... ]
|
||||
|
||||
# List of Scaleway service discovery configurations.
|
||||
scaleway_sd_configs:
|
||||
[ - <scaleway_sd_config> ... ]
|
||||
|
||||
# List of Zookeeper Serverset service discovery configurations.
|
||||
serverset_sd_configs:
|
||||
[ - <serverset_sd_config> ... ]
|
||||
|
@ -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_config>`
|
||||
|
||||
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 <https://developers.scaleway.com/en/>)
|
||||
|
||||
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 <https://developers.scaleway.com/en/>)
|
||||
|
||||
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: <string>
|
||||
|
||||
# Secret key to use when listing targets. https://console.scaleway.com/project/credentials
|
||||
secret_key: <secret>
|
||||
|
||||
# Project ID of the targets.
|
||||
project_id: <string>
|
||||
|
||||
# Role of the targets to retrieve. Must be `instance` or `baremetal`.
|
||||
role: <string>
|
||||
|
||||
# The port to scrape metrics from.
|
||||
[ port: <int> | default = 80 ]
|
||||
|
||||
# API URL to use when doing the server listing requests.
|
||||
[ api_url: <string> | default = "https://api.scaleway.com" ]
|
||||
|
||||
# Zone is the availability zone of your targets (e.g. fr-par-1).
|
||||
[ zone: <string> | default = fr-par-1 ]
|
||||
|
||||
# NameFilter specify a name filter (works as a LIKE) to apply on the server listing request.
|
||||
[ name_filter: <string> ]
|
||||
|
||||
# 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:
|
||||
[ - <string> ]
|
||||
|
||||
# Refresh interval to re-read the targets list.
|
||||
[ refresh_interval: <duration> | default = 60s ]
|
||||
|
||||
# Configure whether HTTP requests follow HTTP 3xx redirects.
|
||||
[ follow_redirects: <bool> | default = true ]
|
||||
|
||||
# Optional proxy URL.
|
||||
[ proxy_url: <string> ]
|
||||
|
||||
# TLS configuration.
|
||||
tls_config:
|
||||
[ <tls_config> ]
|
||||
```
|
||||
|
||||
### `<static_config>`
|
||||
|
||||
|
@ -1746,6 +1837,10 @@ nerve_sd_configs:
|
|||
openstack_sd_configs:
|
||||
[ - <openstack_sd_config> ... ]
|
||||
|
||||
# List of Scaleway service discovery configurations.
|
||||
scaleway_sd_configs:
|
||||
[ - <scaleway_sd_config> ... ]
|
||||
|
||||
# List of Zookeeper Serverset service discovery configurations.
|
||||
serverset_sd_configs:
|
||||
[ - <serverset_sd_config> ... ]
|
||||
|
|
|
@ -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'
|
1
go.mod
1
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
|
||||
|
|
4
go.sum
4
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=
|
||||
|
|
Loading…
Reference in New Issue