prometheus/discovery/scaleway/baremetal.go

202 lines
5.9 KiB
Go

// 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/scaleway/scaleway-sdk-go/api/baremetal/v1"
"github.com/scaleway/scaleway-sdk-go/scw"
"github.com/prometheus/prometheus/discovery/refresh"
"github.com/prometheus/prometheus/discovery/targetgroup"
)
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")
if err != nil {
return nil, err
}
if conf.SecretKeyFile != "" {
rt, err = newAuthTokenFileRoundTripper(conf.SecretKeyFile, rt)
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
}